diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 142597cce2ad..012469279b3d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -158,3 +158,12 @@ jobs: token: ${{ secrets.AX_TRIGGER_SITE_TOKEN }} repository: axmolengine/axmol.dev event-type: forward-push + wasm64: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: 'recursive' + - name: Build + shell: pwsh + run: ./tools/cmdline/axmol -p wasm64 -xc '-DAX_ENABLE_EXT_EFFEKSEER=ON,-DAX_WASM_THREADS=8' -j2 -t 'cpp-tests,fairygui-tests,lua-tests' diff --git a/1k/build.profiles b/1k/build.profiles index aba2d82ca1e6..c75d8f117e50 100644 --- a/1k/build.profiles +++ b/1k/build.profiles @@ -46,7 +46,7 @@ cmdline-tools=12.0 # - https://developer.android.google.cn/about/versions/15/behavior-changes-all?hl=zh-cn#16-kb # In China Mainland, please download from https://pan.baidu.com/s/1neJydxOGTT7aCQvLLwbicw?pwd=qqiq # to $AX_ROOT/cache/, then run `setup.ps1 -p android` -ndk=r23d +ndk=r27c # The android target sdk version, @gradle # as latest as possible @@ -54,7 +54,7 @@ target_sdk=35 # The android min sdk version, @gradle # as min as possible -min_sdk=17 +min_sdk=21 # The gradle version, @setup.ps1 # as latest as possible diff --git a/1k/ios.cmake b/1k/ios.cmake index f8458630a66e..671399f2ce46 100644 --- a/1k/ios.cmake +++ b/1k/ios.cmake @@ -35,7 +35,7 @@ if(NOT DEFINED DEPLOYMENT_TARGET) if (XCODE_VERSION LESS "14.3.0") set(DEPLOYMENT_TARGET "11.0" CACHE STRING "" FORCE) else() # xcode 14.3+ require 12.0 for c++ std::get - set(DEPLOYMENT_TARGET "12.0" CACHE STRING "" FORCE) + set(DEPLOYMENT_TARGET "13.0" CACHE STRING "" FORCE) endif() endif() elseif (PLAT STREQUAL "tvOS") diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index 77d20d29ff26..8981c917ae23 100644 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -262,22 +262,6 @@ if(AX_ENABLE_PHYSICS) ax_add_3rd(box2d OPTIONS ${box2d_opts}) endif() -if(AX_ENABLE_PHYSICS) - ax_add_3rd(chipmunk OPTIONS - "CP_BUILD_SHARED OFF" - "CP_BUILD_STATIC ON" - "CP_BUILD_DEMOS OFF" - "CP_INSTALL_STATIC OFF" - ) - set_target_properties(chipmunk PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/chipmunk/include" - ) - - # !important axmol not use double precision - target_compile_definitions(chipmunk PUBLIC CP_USE_CGTYPES=0) - target_compile_definitions(chipmunk PUBLIC CP_USE_DOUBLES=0) -endif() - ax_add_3rd(freetype OPTIONS "DISABLE_FORCE_DEBUG_POSTFIX ON" "SKIP_INSTALL_ALL TRUE" @@ -394,11 +378,11 @@ if (AX_ENABLE_AUDIO) set(ALSOFT_CPPWINRT_VERSION ${AX_CPPWINRT_VERSION} CACHE STRING "" FORCE) endif() - ax_add_3rd(openal EXCLUDE_FROM_ALL TARGETS alcommon;OpenAL OPTIONS ${alsoft_opts}) + ax_add_3rd(openal EXCLUDE_FROM_ALL TARGETS OpenAL;alsoft.common OPTIONS ${alsoft_opts}) target_include_directories(3rdparty INTERFACE openal) target_compile_definitions(3rdparty INTERFACE AX_USE_ALSOFT=1) - set_target_properties(OpenAL alcommon PROPERTIES CXX_STANDARD ${_AX_CXX_STD}) + set_target_properties(OpenAL alsoft.common PROPERTIES CXX_STANDARD ${_AX_CXX_STD}) if (AX_USE_ALSOFT_STATIC) target_compile_definitions(3rdparty INTERFACE AL_LIBTYPE_STATIC=1) diff --git a/3rdparty/README.md b/3rdparty/README.md index 7e6624330ffd..536309c265f0 100644 --- a/3rdparty/README.md +++ b/3rdparty/README.md @@ -11,7 +11,7 @@ ## Box2D - [![Upstream](https://img.shields.io/github/v/release/erincatto/box2d?label=Upstream)](https://github.com/erincatto/box2d) -- Version: 2.4.2 +- Version: 3.0.0-df7373c - License: MIT ## Bullet @@ -25,11 +25,6 @@ - Version: 1.34.4 - License: MIT -## Chipmunk2D -- [![Upstream](https://img.shields.io/github/v/tag/slembcke/Chipmunk2D?label=Upstream)](https://github.com/slembcke/Chipmunk2D) -- Version: git 7.0.3-0cb05e7 {until Dec 16, 2021} -- License: MIT - ## Clipper2 - [![Upstream](https://img.shields.io/github/v/tag/AngusJohnson/Clipper2?label=Upstream)](https://github.com/AngusJohnson/Clipper2) - Version: 1.5.2 @@ -60,6 +55,11 @@ - Version: git 344eb40 (121) - License: MIT +## fast_float +- [![Upstream](https://img.shields.io/github/v/release/fastfloat/fast_float?label=Upstream)](https://github.com/fastfloat/fast_float) +- Version: 8.0.0 +- License: MIT + ## flatbuffers - [![Upstream](https://img.shields.io/github/v/release/google/flatbuffers?label=Upstream)](https://github.com/google/flatbuffers) - Version: 24.12.23 @@ -86,11 +86,6 @@ - Version: 3.4 with modified for create as win32 child window support - License: zlib -## GHC (iOS devices only) -- [![Upstream](https://img.shields.io/github/v/release/gulrak/filesystem?label=Upstream)](https://github.com/gulrak/filesystem) -- Version: 1.5.14 -- License: MIT - ## jni.hpp - [![Upstream](https://img.shields.io/github/v/release/mapbox/jni.hpp?label=Upstream)](https://github.com/mapbox/jni.hpp) - Version: v4.0.0 @@ -175,7 +170,11 @@ ## OpenAL Soft - [![Upstream](https://img.shields.io/github/v/tag/kcat/openal-soft?label=Upstream)](https://github.com/kcat/openal-soft) -- Version: 1.23.1-e714c8f (8659) +- Version: 1.24.2 +- Modifications: + - Remove `-Werror=undef` + - Linking `fmt::fmt` instead `alsoft::fmt` + - Exclude target `alsoft::excommon` - License: LGPL-2.1 ## OpenSSL @@ -219,7 +218,7 @@ ## simdjson - [![Upstream](https://img.shields.io/github/v/tag/simdjson/simdjson?label=Upstream)](https://github.com/simdjson/simdjson) -- Version: 3.10.1 +- Version: 3.12.0 - License: Apache-2.0 ## stb (stb_image) diff --git a/3rdparty/box2d/CMakeLists.txt b/3rdparty/box2d/CMakeLists.txt index 9a625fdb806f..8e1e066b851a 100644 --- a/3rdparty/box2d/CMakeLists.txt +++ b/3rdparty/box2d/CMakeLists.txt @@ -4,105 +4,23 @@ set(target_name ${lib_name}) project(${lib_name}) -set(BOX2D_SOURCE_FILES - src/collision/b2_broad_phase.cpp - src/collision/b2_chain_shape.cpp - src/collision/b2_circle_shape.cpp - src/collision/b2_collide_circle.cpp - src/collision/b2_collide_edge.cpp - src/collision/b2_collide_polygon.cpp - src/collision/b2_collision.cpp - src/collision/b2_distance.cpp - src/collision/b2_dynamic_tree.cpp - src/collision/b2_edge_shape.cpp - src/collision/b2_polygon_shape.cpp - src/collision/b2_time_of_impact.cpp - src/common/b2_block_allocator.cpp - src/common/b2_draw.cpp - src/common/b2_math.cpp - src/common/b2_settings.cpp - src/common/b2_stack_allocator.cpp - src/common/b2_timer.cpp - src/dynamics/b2_body.cpp - src/dynamics/b2_chain_circle_contact.cpp - src/dynamics/b2_chain_circle_contact.h - src/dynamics/b2_chain_polygon_contact.cpp - src/dynamics/b2_chain_polygon_contact.h - src/dynamics/b2_circle_contact.cpp - src/dynamics/b2_circle_contact.h - src/dynamics/b2_contact.cpp - src/dynamics/b2_contact_manager.cpp - src/dynamics/b2_contact_solver.cpp - src/dynamics/b2_contact_solver.h - src/dynamics/b2_distance_joint.cpp - src/dynamics/b2_edge_circle_contact.cpp - src/dynamics/b2_edge_circle_contact.h - src/dynamics/b2_edge_polygon_contact.cpp - src/dynamics/b2_edge_polygon_contact.h - src/dynamics/b2_fixture.cpp - src/dynamics/b2_friction_joint.cpp - src/dynamics/b2_gear_joint.cpp - src/dynamics/b2_island.cpp - src/dynamics/b2_island.h - src/dynamics/b2_joint.cpp - src/dynamics/b2_motor_joint.cpp - src/dynamics/b2_mouse_joint.cpp - src/dynamics/b2_polygon_circle_contact.cpp - src/dynamics/b2_polygon_circle_contact.h - src/dynamics/b2_polygon_contact.cpp - src/dynamics/b2_polygon_contact.h - src/dynamics/b2_prismatic_joint.cpp - src/dynamics/b2_pulley_joint.cpp - src/dynamics/b2_revolute_joint.cpp - src/dynamics/b2_weld_joint.cpp - src/dynamics/b2_wheel_joint.cpp - src/dynamics/b2_world.cpp - src/dynamics/b2_world_callbacks.cpp - src/rope/b2_rope.cpp) +file(GLOB_RECURSE box2d_sources *.h;*.c) -set(BOX2D_HEADER_FILES - include/box2d/b2_api.h - include/box2d/b2_block_allocator.h - include/box2d/b2_body.h - include/box2d/b2_broad_phase.h - include/box2d/b2_chain_shape.h - include/box2d/b2_circle_shape.h - include/box2d/b2_collision.h - include/box2d/b2_common.h - include/box2d/b2_contact.h - include/box2d/b2_contact_manager.h - include/box2d/b2_distance.h - include/box2d/b2_distance_joint.h - include/box2d/b2_draw.h - include/box2d/b2_dynamic_tree.h - include/box2d/b2_edge_shape.h - include/box2d/b2_fixture.h - include/box2d/b2_friction_joint.h - include/box2d/b2_gear_joint.h - include/box2d/b2_growable_stack.h - include/box2d/b2_joint.h - include/box2d/b2_math.h - include/box2d/b2_motor_joint.h - include/box2d/b2_mouse_joint.h - include/box2d/b2_polygon_shape.h - include/box2d/b2_prismatic_joint.h - include/box2d/b2_pulley_joint.h - include/box2d/b2_revolute_joint.h - include/box2d/b2_rope.h - include/box2d/b2_settings.h - include/box2d/b2_shape.h - include/box2d/b2_stack_allocator.h - include/box2d/b2_time_of_impact.h - include/box2d/b2_timer.h - include/box2d/b2_time_step.h - include/box2d/b2_types.h - include/box2d/b2_weld_joint.h - include/box2d/b2_wheel_joint.h - include/box2d/b2_world.h - include/box2d/b2_world_callbacks.h - include/box2d/box2d.h) +add_library(${target_name} ${box2d_sources}) -add_library(${target_name} STATIC ${BOX2D_SOURCE_FILES} ${BOX2D_HEADER_FILES}) +if (MSVC) + if (BUILD_SHARED_LIBS) + # this is needed by DLL users to import Box2D symbols + target_compile_definitions(box2d INTERFACE BOX2D_DLL) + endif() +endif() + +# Atomics are still considered experimental in Visual Studio 17.8 +if (FULL_MSVC) + target_compile_options(box2d PRIVATE /experimental:c11atomics) +endif() + +set_target_properties(${target_name} PROPERTIES C_STANDARD 17 C_STANDARD_REQUIRED TRUE) target_include_directories(${target_name} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" diff --git a/3rdparty/box2d/include/box2d/b2_api.h b/3rdparty/box2d/include/box2d/b2_api.h deleted file mode 100644 index 6730203c64b1..000000000000 --- a/3rdparty/box2d/include/box2d/b2_api.h +++ /dev/null @@ -1,52 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_API_H -#define B2_API_H - -#ifdef B2_SHARED - #if defined _WIN32 || defined __CYGWIN__ - #ifdef box2d_EXPORTS - #ifdef __GNUC__ - #define B2_API __attribute__ ((dllexport)) - #else - #define B2_API __declspec(dllexport) - #endif - #else - #ifdef __GNUC__ - #define B2_API __attribute__ ((dllimport)) - #else - #define B2_API __declspec(dllimport) - #endif - #endif - #else - #if __GNUC__ >= 4 - #define B2_API __attribute__ ((visibility ("default"))) - #else - #define B2_API - #endif - #endif -#else - #define B2_API -#endif - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_block_allocator.h b/3rdparty/box2d/include/box2d/b2_block_allocator.h deleted file mode 100644 index 95c12de89cd8..000000000000 --- a/3rdparty/box2d/include/box2d/b2_block_allocator.h +++ /dev/null @@ -1,60 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_BLOCK_ALLOCATOR_H -#define B2_BLOCK_ALLOCATOR_H - -#include "b2_api.h" -#include "b2_settings.h" - -const int32 b2_blockSizeCount = 14; - -struct b2Block; -struct b2Chunk; - -/// This is a small object allocator used for allocating small -/// objects that persist for more than one time step. -/// See: http://www.codeproject.com/useritems/Small_Block_Allocator.asp -class B2_API b2BlockAllocator -{ -public: - b2BlockAllocator(); - ~b2BlockAllocator(); - - /// Allocate memory. This will use b2Alloc if the size is larger than b2_maxBlockSize. - void* Allocate(int32 size); - - /// Free memory. This will use b2Free if the size is larger than b2_maxBlockSize. - void Free(void* p, int32 size); - - void Clear(); - -private: - - b2Chunk* m_chunks; - int32 m_chunkCount; - int32 m_chunkSpace; - - b2Block* m_freeLists[b2_blockSizeCount]; -}; - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_body.h b/3rdparty/box2d/include/box2d/b2_body.h deleted file mode 100644 index 16b2bb002c78..000000000000 --- a/3rdparty/box2d/include/box2d/b2_body.h +++ /dev/null @@ -1,885 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_BODY_H -#define B2_BODY_H - -#include "b2_api.h" -#include "b2_math.h" -#include "b2_shape.h" - -class b2Fixture; -class b2Joint; -class b2Contact; -class b2Controller; -class b2World; -struct b2FixtureDef; -struct b2JointEdge; -struct b2ContactEdge; - -/// The body type. -/// static: zero mass, zero velocity, may be manually moved -/// kinematic: zero mass, non-zero velocity set by user, moved by solver -/// dynamic: positive mass, non-zero velocity determined by forces, moved by solver -enum b2BodyType -{ - b2_staticBody = 0, - b2_kinematicBody, - b2_dynamicBody -}; - -/// A body definition holds all the data needed to construct a rigid body. -/// You can safely re-use body definitions. Shapes are added to a body after construction. -struct B2_API b2BodyDef -{ - /// This constructor sets the body definition default values. - b2BodyDef() - { - position.Set(0.0f, 0.0f); - angle = 0.0f; - linearVelocity.Set(0.0f, 0.0f); - angularVelocity = 0.0f; - linearDamping = 0.0f; - angularDamping = 0.0f; - allowSleep = true; - awake = true; - fixedRotation = false; - bullet = false; - type = b2_staticBody; - enabled = true; - gravityScale = 1.0f; - } - - /// The body type: static, kinematic, or dynamic. - /// Note: if a dynamic body would have zero mass, the mass is set to one. - b2BodyType type; - - /// The world position of the body. Avoid creating bodies at the origin - /// since this can lead to many overlapping shapes. - b2Vec2 position; - - /// The world angle of the body in radians. - float angle; - - /// The linear velocity of the body's origin in world co-ordinates. - b2Vec2 linearVelocity; - - /// The angular velocity of the body. - float angularVelocity; - - /// Linear damping is use to reduce the linear velocity. The damping parameter - /// can be larger than 1.0f but the damping effect becomes sensitive to the - /// time step when the damping parameter is large. - /// Units are 1/time - float linearDamping; - - /// Angular damping is use to reduce the angular velocity. The damping parameter - /// can be larger than 1.0f but the damping effect becomes sensitive to the - /// time step when the damping parameter is large. - /// Units are 1/time - float angularDamping; - - /// Set this flag to false if this body should never fall asleep. Note that - /// this increases CPU usage. - bool allowSleep; - - /// Is this body initially awake or sleeping? - bool awake; - - /// Should this body be prevented from rotating? Useful for characters. - bool fixedRotation; - - /// Is this a fast moving body that should be prevented from tunneling through - /// other moving bodies? Note that all bodies are prevented from tunneling through - /// kinematic and static bodies. This setting is only considered on dynamic bodies. - /// @warning You should use this flag sparingly since it increases processing time. - bool bullet; - - /// Does this body start out enabled? - bool enabled; - - /// Use this to store application specific body data. - b2BodyUserData userData; - - /// Scale the gravity applied to this body. - float gravityScale; -}; - -/// A rigid body. These are created via b2World::CreateBody. -class B2_API b2Body -{ -public: - /// Creates a fixture and attach it to this body. Use this function if you need - /// to set some fixture parameters, like friction. Otherwise you can create the - /// fixture directly from a shape. - /// If the density is non-zero, this function automatically updates the mass of the body. - /// Contacts are not created until the next time step. - /// @param def the fixture definition. - /// @warning This function is locked during callbacks. - b2Fixture* CreateFixture(const b2FixtureDef* def); - - /// Creates a fixture from a shape and attach it to this body. - /// This is a convenience function. Use b2FixtureDef if you need to set parameters - /// like friction, restitution, user data, or filtering. - /// If the density is non-zero, this function automatically updates the mass of the body. - /// @param shape the shape to be cloned. - /// @param density the shape density (set to zero for static bodies). - /// @warning This function is locked during callbacks. - b2Fixture* CreateFixture(const b2Shape* shape, float density); - - /// Destroy a fixture. This removes the fixture from the broad-phase and - /// destroys all contacts associated with this fixture. This will - /// automatically adjust the mass of the body if the body is dynamic and the - /// fixture has positive density. - /// All fixtures attached to a body are implicitly destroyed when the body is destroyed. - /// @param fixture the fixture to be removed. - /// @warning This function is locked during callbacks. - void DestroyFixture(b2Fixture* fixture); - - /// Set the position of the body's origin and rotation. - /// Manipulating a body's transform may cause non-physical behavior. - /// Note: contacts are updated on the next call to b2World::Step. - /// @param position the world position of the body's local origin. - /// @param angle the world rotation in radians. - void SetTransform(const b2Vec2& position, float angle); - - /// Get the body transform for the body's origin. - /// @return the world transform of the body's origin. - const b2Transform& GetTransform() const; - - /// Get the world body origin position. - /// @return the world position of the body's origin. - const b2Vec2& GetPosition() const; - - /// Get the angle in radians. - /// @return the current world rotation angle in radians. - float GetAngle() const; - - /// Get the world position of the center of mass. - const b2Vec2& GetWorldCenter() const; - - /// Get the local position of the center of mass. - const b2Vec2& GetLocalCenter() const; - - /// Set the linear velocity of the center of mass. - /// @param v the new linear velocity of the center of mass. - void SetLinearVelocity(const b2Vec2& v); - - /// Get the linear velocity of the center of mass. - /// @return the linear velocity of the center of mass. - const b2Vec2& GetLinearVelocity() const; - - /// Set the angular velocity. - /// @param omega the new angular velocity in radians/second. - void SetAngularVelocity(float omega); - - /// Get the angular velocity. - /// @return the angular velocity in radians/second. - float GetAngularVelocity() const; - - /// Apply a force at a world point. If the force is not - /// applied at the center of mass, it will generate a torque and - /// affect the angular velocity. This wakes up the body. - /// @param force the world force vector, usually in Newtons (N). - /// @param point the world position of the point of application. - /// @param wake also wake up the body - void ApplyForce(const b2Vec2& force, const b2Vec2& point, bool wake); - - /// Apply a force to the center of mass. This wakes up the body. - /// @param force the world force vector, usually in Newtons (N). - /// @param wake also wake up the body - void ApplyForceToCenter(const b2Vec2& force, bool wake); - - /// Apply a torque. This affects the angular velocity - /// without affecting the linear velocity of the center of mass. - /// @param torque about the z-axis (out of the screen), usually in N-m. - /// @param wake also wake up the body - void ApplyTorque(float torque, bool wake); - - /// Apply an impulse at a point. This immediately modifies the velocity. - /// It also modifies the angular velocity if the point of application - /// is not at the center of mass. This wakes up the body. - /// @param impulse the world impulse vector, usually in N-seconds or kg-m/s. - /// @param point the world position of the point of application. - /// @param wake also wake up the body - void ApplyLinearImpulse(const b2Vec2& impulse, const b2Vec2& point, bool wake); - - /// Apply an impulse to the center of mass. This immediately modifies the velocity. - /// @param impulse the world impulse vector, usually in N-seconds or kg-m/s. - /// @param wake also wake up the body - void ApplyLinearImpulseToCenter(const b2Vec2& impulse, bool wake); - - /// Apply an angular impulse. - /// @param impulse the angular impulse in units of kg*m*m/s - /// @param wake also wake up the body - void ApplyAngularImpulse(float impulse, bool wake); - - /// Get the total mass of the body. - /// @return the mass, usually in kilograms (kg). - float GetMass() const; - - /// Get the rotational inertia of the body about the local origin. - /// @return the rotational inertia, usually in kg-m^2. - float GetInertia() const; - - /// Get the mass data of the body. - /// @return a struct containing the mass, inertia and center of the body. - b2MassData GetMassData() const; - - /// Set the mass properties to override the mass properties of the fixtures. - /// Note that this changes the center of mass position. - /// Note that creating or destroying fixtures can also alter the mass. - /// This function has no effect if the body isn't dynamic. - /// @param data the mass properties. - void SetMassData(const b2MassData* data); - - /// This resets the mass properties to the sum of the mass properties of the fixtures. - /// This normally does not need to be called unless you called SetMassData to override - /// the mass and you later want to reset the mass. - void ResetMassData(); - - /// Get the world coordinates of a point given the local coordinates. - /// @param localPoint a point on the body measured relative the the body's origin. - /// @return the same point expressed in world coordinates. - b2Vec2 GetWorldPoint(const b2Vec2& localPoint) const; - - /// Get the world coordinates of a vector given the local coordinates. - /// @param localVector a vector fixed in the body. - /// @return the same vector expressed in world coordinates. - b2Vec2 GetWorldVector(const b2Vec2& localVector) const; - - /// Gets a local point relative to the body's origin given a world point. - /// @param worldPoint a point in world coordinates. - /// @return the corresponding local point relative to the body's origin. - b2Vec2 GetLocalPoint(const b2Vec2& worldPoint) const; - - /// Gets a local vector given a world vector. - /// @param worldVector a vector in world coordinates. - /// @return the corresponding local vector. - b2Vec2 GetLocalVector(const b2Vec2& worldVector) const; - - /// Get the world linear velocity of a world point attached to this body. - /// @param worldPoint a point in world coordinates. - /// @return the world velocity of a point. - b2Vec2 GetLinearVelocityFromWorldPoint(const b2Vec2& worldPoint) const; - - /// Get the world velocity of a local point. - /// @param localPoint a point in local coordinates. - /// @return the world velocity of a point. - b2Vec2 GetLinearVelocityFromLocalPoint(const b2Vec2& localPoint) const; - - /// Get the linear damping of the body. - float GetLinearDamping() const; - - /// Set the linear damping of the body. - void SetLinearDamping(float linearDamping); - - /// Get the angular damping of the body. - float GetAngularDamping() const; - - /// Set the angular damping of the body. - void SetAngularDamping(float angularDamping); - - /// Get the gravity scale of the body. - float GetGravityScale() const; - - /// Set the gravity scale of the body. - void SetGravityScale(float scale); - - /// Set the type of this body. This may alter the mass and velocity. - void SetType(b2BodyType type); - - /// Get the type of this body. - b2BodyType GetType() const; - - /// Should this body be treated like a bullet for continuous collision detection? - void SetBullet(bool flag); - - /// Is this body treated like a bullet for continuous collision detection? - bool IsBullet() const; - - /// You can disable sleeping on this body. If you disable sleeping, the - /// body will be woken. - void SetSleepingAllowed(bool flag); - - /// Is this body allowed to sleep - bool IsSleepingAllowed() const; - - /// Set the sleep state of the body. A sleeping body has very - /// low CPU cost. - /// @param flag set to true to wake the body, false to put it to sleep. - void SetAwake(bool flag); - - /// Get the sleeping state of this body. - /// @return true if the body is awake. - bool IsAwake() const; - - /// Allow a body to be disabled. A disabled body is not simulated and cannot - /// be collided with or woken up. - /// If you pass a flag of true, all fixtures will be added to the broad-phase. - /// If you pass a flag of false, all fixtures will be removed from the - /// broad-phase and all contacts will be destroyed. - /// Fixtures and joints are otherwise unaffected. You may continue - /// to create/destroy fixtures and joints on disabled bodies. - /// Fixtures on a disabled body are implicitly disabled and will - /// not participate in collisions, ray-casts, or queries. - /// Joints connected to a disabled body are implicitly disabled. - /// An diabled body is still owned by a b2World object and remains - /// in the body list. - void SetEnabled(bool flag); - - /// Get the active state of the body. - bool IsEnabled() const; - - /// Set this body to have fixed rotation. This causes the mass - /// to be reset. - void SetFixedRotation(bool flag); - - /// Does this body have fixed rotation? - bool IsFixedRotation() const; - - /// Get the list of all fixtures attached to this body. - b2Fixture* GetFixtureList(); - const b2Fixture* GetFixtureList() const; - - /// Get the list of all joints attached to this body. - b2JointEdge* GetJointList(); - const b2JointEdge* GetJointList() const; - - /// Get the list of all contacts attached to this body. - /// @warning this list changes during the time step and you may - /// miss some collisions if you don't use b2ContactListener. - b2ContactEdge* GetContactList(); - const b2ContactEdge* GetContactList() const; - - /// Get the next body in the world's body list. - b2Body* GetNext(); - const b2Body* GetNext() const; - - /// Get the user data pointer that was provided in the body definition. - b2BodyUserData& GetUserData(); - const b2BodyUserData& GetUserData() const; - - /// Get the parent world of this body. - b2World* GetWorld(); - const b2World* GetWorld() const; - - /// Dump this body to a file - void Dump(); - -private: - - friend class b2World; - friend class b2Island; - friend class b2ContactManager; - friend class b2ContactSolver; - friend class b2Contact; - - friend class b2DistanceJoint; - friend class b2FrictionJoint; - friend class b2GearJoint; - friend class b2MotorJoint; - friend class b2MouseJoint; - friend class b2PrismaticJoint; - friend class b2PulleyJoint; - friend class b2RevoluteJoint; - friend class b2WeldJoint; - friend class b2WheelJoint; - - // m_flags - enum - { - e_islandFlag = 0x0001, - e_awakeFlag = 0x0002, - e_autoSleepFlag = 0x0004, - e_bulletFlag = 0x0008, - e_fixedRotationFlag = 0x0010, - e_enabledFlag = 0x0020, - e_toiFlag = 0x0040 - }; - - b2Body(const b2BodyDef* bd, b2World* world); - ~b2Body(); - - void SynchronizeFixtures(); - void SynchronizeTransform(); - - // This is used to prevent connected bodies from colliding. - // It may lie, depending on the collideConnected flag. - bool ShouldCollide(const b2Body* other) const; - - void Advance(float t); - - b2BodyType m_type; - - uint16 m_flags; - - int32 m_islandIndex; - - b2Transform m_xf; // the body origin transform - b2Sweep m_sweep; // the swept motion for CCD - - b2Vec2 m_linearVelocity; - float m_angularVelocity; - - b2Vec2 m_force; - float m_torque; - - b2World* m_world; - b2Body* m_prev; - b2Body* m_next; - - b2Fixture* m_fixtureList; - int32 m_fixtureCount; - - b2JointEdge* m_jointList; - b2ContactEdge* m_contactList; - - float m_mass, m_invMass; - - // Rotational inertia about the center of mass. - float m_I, m_invI; - - float m_linearDamping; - float m_angularDamping; - float m_gravityScale; - - float m_sleepTime; - - b2BodyUserData m_userData; -}; - -inline b2BodyType b2Body::GetType() const -{ - return m_type; -} - -inline const b2Transform& b2Body::GetTransform() const -{ - return m_xf; -} - -inline const b2Vec2& b2Body::GetPosition() const -{ - return m_xf.p; -} - -inline float b2Body::GetAngle() const -{ - return m_sweep.a; -} - -inline const b2Vec2& b2Body::GetWorldCenter() const -{ - return m_sweep.c; -} - -inline const b2Vec2& b2Body::GetLocalCenter() const -{ - return m_sweep.localCenter; -} - -inline void b2Body::SetLinearVelocity(const b2Vec2& v) -{ - if (m_type == b2_staticBody) - { - return; - } - - if (b2Dot(v,v) > 0.0f) - { - SetAwake(true); - } - - m_linearVelocity = v; -} - -inline const b2Vec2& b2Body::GetLinearVelocity() const -{ - return m_linearVelocity; -} - -inline void b2Body::SetAngularVelocity(float w) -{ - if (m_type == b2_staticBody) - { - return; - } - - if (w * w > 0.0f) - { - SetAwake(true); - } - - m_angularVelocity = w; -} - -inline float b2Body::GetAngularVelocity() const -{ - return m_angularVelocity; -} - -inline float b2Body::GetMass() const -{ - return m_mass; -} - -inline float b2Body::GetInertia() const -{ - return m_I + m_mass * b2Dot(m_sweep.localCenter, m_sweep.localCenter); -} - -inline b2MassData b2Body::GetMassData() const -{ - b2MassData data; - data.mass = m_mass; - data.I = m_I + m_mass * b2Dot(m_sweep.localCenter, m_sweep.localCenter); - data.center = m_sweep.localCenter; - return data; -} - -inline b2Vec2 b2Body::GetWorldPoint(const b2Vec2& localPoint) const -{ - return b2Mul(m_xf, localPoint); -} - -inline b2Vec2 b2Body::GetWorldVector(const b2Vec2& localVector) const -{ - return b2Mul(m_xf.q, localVector); -} - -inline b2Vec2 b2Body::GetLocalPoint(const b2Vec2& worldPoint) const -{ - return b2MulT(m_xf, worldPoint); -} - -inline b2Vec2 b2Body::GetLocalVector(const b2Vec2& worldVector) const -{ - return b2MulT(m_xf.q, worldVector); -} - -inline b2Vec2 b2Body::GetLinearVelocityFromWorldPoint(const b2Vec2& worldPoint) const -{ - return m_linearVelocity + b2Cross(m_angularVelocity, worldPoint - m_sweep.c); -} - -inline b2Vec2 b2Body::GetLinearVelocityFromLocalPoint(const b2Vec2& localPoint) const -{ - return GetLinearVelocityFromWorldPoint(GetWorldPoint(localPoint)); -} - -inline float b2Body::GetLinearDamping() const -{ - return m_linearDamping; -} - -inline void b2Body::SetLinearDamping(float linearDamping) -{ - m_linearDamping = linearDamping; -} - -inline float b2Body::GetAngularDamping() const -{ - return m_angularDamping; -} - -inline void b2Body::SetAngularDamping(float angularDamping) -{ - m_angularDamping = angularDamping; -} - -inline float b2Body::GetGravityScale() const -{ - return m_gravityScale; -} - -inline void b2Body::SetGravityScale(float scale) -{ - m_gravityScale = scale; -} - -inline void b2Body::SetBullet(bool flag) -{ - if (flag) - { - m_flags |= e_bulletFlag; - } - else - { - m_flags &= ~e_bulletFlag; - } -} - -inline bool b2Body::IsBullet() const -{ - return (m_flags & e_bulletFlag) == e_bulletFlag; -} - -inline void b2Body::SetAwake(bool flag) -{ - if (m_type == b2_staticBody) - { - return; - } - - if (flag) - { - m_flags |= e_awakeFlag; - m_sleepTime = 0.0f; - } - else - { - m_flags &= ~e_awakeFlag; - m_sleepTime = 0.0f; - m_linearVelocity.SetZero(); - m_angularVelocity = 0.0f; - m_force.SetZero(); - m_torque = 0.0f; - } -} - -inline bool b2Body::IsAwake() const -{ - return (m_flags & e_awakeFlag) == e_awakeFlag; -} - -inline bool b2Body::IsEnabled() const -{ - return (m_flags & e_enabledFlag) == e_enabledFlag; -} - -inline bool b2Body::IsFixedRotation() const -{ - return (m_flags & e_fixedRotationFlag) == e_fixedRotationFlag; -} - -inline void b2Body::SetSleepingAllowed(bool flag) -{ - if (flag) - { - m_flags |= e_autoSleepFlag; - } - else - { - m_flags &= ~e_autoSleepFlag; - SetAwake(true); - } -} - -inline bool b2Body::IsSleepingAllowed() const -{ - return (m_flags & e_autoSleepFlag) == e_autoSleepFlag; -} - -inline b2Fixture* b2Body::GetFixtureList() -{ - return m_fixtureList; -} - -inline const b2Fixture* b2Body::GetFixtureList() const -{ - return m_fixtureList; -} - -inline b2JointEdge* b2Body::GetJointList() -{ - return m_jointList; -} - -inline const b2JointEdge* b2Body::GetJointList() const -{ - return m_jointList; -} - -inline b2ContactEdge* b2Body::GetContactList() -{ - return m_contactList; -} - -inline const b2ContactEdge* b2Body::GetContactList() const -{ - return m_contactList; -} - -inline b2Body* b2Body::GetNext() -{ - return m_next; -} - -inline const b2Body* b2Body::GetNext() const -{ - return m_next; -} - -inline b2BodyUserData& b2Body::GetUserData() -{ - return m_userData; -} - -inline const b2BodyUserData& b2Body::GetUserData() const -{ - return m_userData; -} - -inline void b2Body::ApplyForce(const b2Vec2& force, const b2Vec2& point, bool wake) -{ - if (m_type != b2_dynamicBody) - { - return; - } - - if (wake && (m_flags & e_awakeFlag) == 0) - { - SetAwake(true); - } - - // Don't accumulate a force if the body is sleeping. - if (m_flags & e_awakeFlag) - { - m_force += force; - m_torque += b2Cross(point - m_sweep.c, force); - } -} - -inline void b2Body::ApplyForceToCenter(const b2Vec2& force, bool wake) -{ - if (m_type != b2_dynamicBody) - { - return; - } - - if (wake && (m_flags & e_awakeFlag) == 0) - { - SetAwake(true); - } - - // Don't accumulate a force if the body is sleeping - if (m_flags & e_awakeFlag) - { - m_force += force; - } -} - -inline void b2Body::ApplyTorque(float torque, bool wake) -{ - if (m_type != b2_dynamicBody) - { - return; - } - - if (wake && (m_flags & e_awakeFlag) == 0) - { - SetAwake(true); - } - - // Don't accumulate a force if the body is sleeping - if (m_flags & e_awakeFlag) - { - m_torque += torque; - } -} - -inline void b2Body::ApplyLinearImpulse(const b2Vec2& impulse, const b2Vec2& point, bool wake) -{ - if (m_type != b2_dynamicBody) - { - return; - } - - if (wake && (m_flags & e_awakeFlag) == 0) - { - SetAwake(true); - } - - // Don't accumulate velocity if the body is sleeping - if (m_flags & e_awakeFlag) - { - m_linearVelocity += m_invMass * impulse; - m_angularVelocity += m_invI * b2Cross(point - m_sweep.c, impulse); - } -} - -inline void b2Body::ApplyLinearImpulseToCenter(const b2Vec2& impulse, bool wake) -{ - if (m_type != b2_dynamicBody) - { - return; - } - - if (wake && (m_flags & e_awakeFlag) == 0) - { - SetAwake(true); - } - - // Don't accumulate velocity if the body is sleeping - if (m_flags & e_awakeFlag) - { - m_linearVelocity += m_invMass * impulse; - } -} - -inline void b2Body::ApplyAngularImpulse(float impulse, bool wake) -{ - if (m_type != b2_dynamicBody) - { - return; - } - - if (wake && (m_flags & e_awakeFlag) == 0) - { - SetAwake(true); - } - - // Don't accumulate velocity if the body is sleeping - if (m_flags & e_awakeFlag) - { - m_angularVelocity += m_invI * impulse; - } -} - -inline void b2Body::SynchronizeTransform() -{ - m_xf.q.Set(m_sweep.a); - m_xf.p = m_sweep.c - b2Mul(m_xf.q, m_sweep.localCenter); -} - -inline void b2Body::Advance(float alpha) -{ - // Advance to the new safe time. This doesn't sync the broad-phase. - m_sweep.Advance(alpha); - m_sweep.c = m_sweep.c0; - m_sweep.a = m_sweep.a0; - m_xf.q.Set(m_sweep.a); - m_xf.p = m_sweep.c - b2Mul(m_xf.q, m_sweep.localCenter); -} - -inline b2World* b2Body::GetWorld() -{ - return m_world; -} - -inline const b2World* b2Body::GetWorld() const -{ - return m_world; -} - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_broad_phase.h b/3rdparty/box2d/include/box2d/b2_broad_phase.h deleted file mode 100644 index cc882ab477ce..000000000000 --- a/3rdparty/box2d/include/box2d/b2_broad_phase.h +++ /dev/null @@ -1,238 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_BROAD_PHASE_H -#define B2_BROAD_PHASE_H - -#include "b2_api.h" -#include "b2_settings.h" -#include "b2_collision.h" -#include "b2_dynamic_tree.h" - -struct B2_API b2Pair -{ - int32 proxyIdA; - int32 proxyIdB; -}; - -/// The broad-phase is used for computing pairs and performing volume queries and ray casts. -/// This broad-phase does not persist pairs. Instead, this reports potentially new pairs. -/// It is up to the client to consume the new pairs and to track subsequent overlap. -class B2_API b2BroadPhase -{ -public: - - enum - { - e_nullProxy = -1 - }; - - b2BroadPhase(); - ~b2BroadPhase(); - - /// Create a proxy with an initial AABB. Pairs are not reported until - /// UpdatePairs is called. - int32 CreateProxy(const b2AABB& aabb, void* userData); - - /// Destroy a proxy. It is up to the client to remove any pairs. - void DestroyProxy(int32 proxyId); - - /// Call MoveProxy as many times as you like, then when you are done - /// call UpdatePairs to finalized the proxy pairs (for your time step). - void MoveProxy(int32 proxyId, const b2AABB& aabb, const b2Vec2& displacement); - - /// Call to trigger a re-processing of it's pairs on the next call to UpdatePairs. - void TouchProxy(int32 proxyId); - - /// Get the fat AABB for a proxy. - const b2AABB& GetFatAABB(int32 proxyId) const; - - /// Get user data from a proxy. Returns nullptr if the id is invalid. - void* GetUserData(int32 proxyId) const; - - /// Test overlap of fat AABBs. - bool TestOverlap(int32 proxyIdA, int32 proxyIdB) const; - - /// Get the number of proxies. - int32 GetProxyCount() const; - - /// Update the pairs. This results in pair callbacks. This can only add pairs. - template - void UpdatePairs(T* callback); - - /// Query an AABB for overlapping proxies. The callback class - /// is called for each proxy that overlaps the supplied AABB. - template - void Query(T* callback, const b2AABB& aabb) const; - - /// Ray-cast against the proxies in the tree. This relies on the callback - /// to perform a exact ray-cast in the case were the proxy contains a shape. - /// The callback also performs the any collision filtering. This has performance - /// roughly equal to k * log(n), where k is the number of collisions and n is the - /// number of proxies in the tree. - /// @param input the ray-cast input data. The ray extends from p1 to p1 + maxFraction * (p2 - p1). - /// @param callback a callback class that is called for each proxy that is hit by the ray. - template - void RayCast(T* callback, const b2RayCastInput& input) const; - - /// Get the height of the embedded tree. - int32 GetTreeHeight() const; - - /// Get the balance of the embedded tree. - int32 GetTreeBalance() const; - - /// Get the quality metric of the embedded tree. - float GetTreeQuality() const; - - /// Shift the world origin. Useful for large worlds. - /// The shift formula is: position -= newOrigin - /// @param newOrigin the new origin with respect to the old origin - void ShiftOrigin(const b2Vec2& newOrigin); - -private: - - friend class b2DynamicTree; - - void BufferMove(int32 proxyId); - void UnBufferMove(int32 proxyId); - - bool QueryCallback(int32 proxyId); - - b2DynamicTree m_tree; - - int32 m_proxyCount; - - int32* m_moveBuffer; - int32 m_moveCapacity; - int32 m_moveCount; - - b2Pair* m_pairBuffer; - int32 m_pairCapacity; - int32 m_pairCount; - - int32 m_queryProxyId; -}; - -inline void* b2BroadPhase::GetUserData(int32 proxyId) const -{ - return m_tree.GetUserData(proxyId); -} - -inline bool b2BroadPhase::TestOverlap(int32 proxyIdA, int32 proxyIdB) const -{ - const b2AABB& aabbA = m_tree.GetFatAABB(proxyIdA); - const b2AABB& aabbB = m_tree.GetFatAABB(proxyIdB); - return b2TestOverlap(aabbA, aabbB); -} - -inline const b2AABB& b2BroadPhase::GetFatAABB(int32 proxyId) const -{ - return m_tree.GetFatAABB(proxyId); -} - -inline int32 b2BroadPhase::GetProxyCount() const -{ - return m_proxyCount; -} - -inline int32 b2BroadPhase::GetTreeHeight() const -{ - return m_tree.GetHeight(); -} - -inline int32 b2BroadPhase::GetTreeBalance() const -{ - return m_tree.GetMaxBalance(); -} - -inline float b2BroadPhase::GetTreeQuality() const -{ - return m_tree.GetAreaRatio(); -} - -template -void b2BroadPhase::UpdatePairs(T* callback) -{ - // Reset pair buffer - m_pairCount = 0; - - // Perform tree queries for all moving proxies. - for (int32 i = 0; i < m_moveCount; ++i) - { - m_queryProxyId = m_moveBuffer[i]; - if (m_queryProxyId == e_nullProxy) - { - continue; - } - - // We have to query the tree with the fat AABB so that - // we don't fail to create a pair that may touch later. - const b2AABB& fatAABB = m_tree.GetFatAABB(m_queryProxyId); - - // Query tree, create pairs and add them pair buffer. - m_tree.Query(this, fatAABB); - } - - // Send pairs to caller - for (int32 i = 0; i < m_pairCount; ++i) - { - b2Pair* primaryPair = m_pairBuffer + i; - void* userDataA = m_tree.GetUserData(primaryPair->proxyIdA); - void* userDataB = m_tree.GetUserData(primaryPair->proxyIdB); - - callback->AddPair(userDataA, userDataB); - } - - // Clear move flags - for (int32 i = 0; i < m_moveCount; ++i) - { - int32 proxyId = m_moveBuffer[i]; - if (proxyId == e_nullProxy) - { - continue; - } - - m_tree.ClearMoved(proxyId); - } - - // Reset move buffer - m_moveCount = 0; -} - -template -inline void b2BroadPhase::Query(T* callback, const b2AABB& aabb) const -{ - m_tree.Query(callback, aabb); -} - -template -inline void b2BroadPhase::RayCast(T* callback, const b2RayCastInput& input) const -{ - m_tree.RayCast(callback, input); -} - -inline void b2BroadPhase::ShiftOrigin(const b2Vec2& newOrigin) -{ - m_tree.ShiftOrigin(newOrigin); -} - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_chain_shape.h b/3rdparty/box2d/include/box2d/b2_chain_shape.h deleted file mode 100644 index da2605d6233b..000000000000 --- a/3rdparty/box2d/include/box2d/b2_chain_shape.h +++ /dev/null @@ -1,101 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_CHAIN_SHAPE_H -#define B2_CHAIN_SHAPE_H - -#include "b2_api.h" -#include "b2_shape.h" - -class b2EdgeShape; - -/// A chain shape is a free form sequence of line segments. -/// The chain has one-sided collision, with the surface normal pointing to the right of the edge. -/// This provides a counter-clockwise winding like the polygon shape. -/// Connectivity information is used to create smooth collisions. -/// @warning the chain will not collide properly if there are self-intersections. -class B2_API b2ChainShape : public b2Shape -{ -public: - b2ChainShape(); - - /// The destructor frees the vertices using b2Free. - ~b2ChainShape(); - - /// Clear all data. - void Clear(); - - /// Create a loop. This automatically adjusts connectivity. - /// @param vertices an array of vertices, these are copied - /// @param count the vertex count - void CreateLoop(const b2Vec2* vertices, int32 count); - - /// Create a chain with ghost vertices to connect multiple chains together. - /// @param vertices an array of vertices, these are copied - /// @param count the vertex count - /// @param prevVertex previous vertex from chain that connects to the start - /// @param nextVertex next vertex from chain that connects to the end - void CreateChain(const b2Vec2* vertices, int32 count, - const b2Vec2& prevVertex, const b2Vec2& nextVertex); - - /// Implement b2Shape. Vertices are cloned using b2Alloc. - b2Shape* Clone(b2BlockAllocator* allocator) const override; - - /// @see b2Shape::GetChildCount - int32 GetChildCount() const override; - - /// Get a child edge. - void GetChildEdge(b2EdgeShape* edge, int32 index) const; - - /// This always return false. - /// @see b2Shape::TestPoint - bool TestPoint(const b2Transform& transform, const b2Vec2& p) const override; - - /// Implement b2Shape. - bool RayCast(b2RayCastOutput* output, const b2RayCastInput& input, - const b2Transform& transform, int32 childIndex) const override; - - /// @see b2Shape::ComputeAABB - void ComputeAABB(b2AABB* aabb, const b2Transform& transform, int32 childIndex) const override; - - /// Chains have zero mass. - /// @see b2Shape::ComputeMass - void ComputeMass(b2MassData* massData, float density) const override; - - /// The vertices. Owned by this class. - b2Vec2* m_vertices; - - /// The vertex count. - int32 m_count; - - b2Vec2 m_prevVertex, m_nextVertex; -}; - -inline b2ChainShape::b2ChainShape() -{ - m_type = e_chain; - m_radius = b2_polygonRadius; - m_vertices = nullptr; - m_count = 0; -} - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_circle_shape.h b/3rdparty/box2d/include/box2d/b2_circle_shape.h deleted file mode 100644 index 5e330f5a7ea3..000000000000 --- a/3rdparty/box2d/include/box2d/b2_circle_shape.h +++ /dev/null @@ -1,67 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_CIRCLE_SHAPE_H -#define B2_CIRCLE_SHAPE_H - -#include "b2_api.h" -#include "b2_shape.h" - -/// A solid circle shape -class B2_API b2CircleShape : public b2Shape -{ -public: - b2CircleShape(); - - /// Implement b2Shape. - b2Shape* Clone(b2BlockAllocator* allocator) const override; - - /// @see b2Shape::GetChildCount - int32 GetChildCount() const override; - - /// Implement b2Shape. - bool TestPoint(const b2Transform& transform, const b2Vec2& p) const override; - - /// Implement b2Shape. - /// @note because the circle is solid, rays that start inside do not hit because the normal is - /// not defined. - bool RayCast(b2RayCastOutput* output, const b2RayCastInput& input, - const b2Transform& transform, int32 childIndex) const override; - - /// @see b2Shape::ComputeAABB - void ComputeAABB(b2AABB* aabb, const b2Transform& transform, int32 childIndex) const override; - - /// @see b2Shape::ComputeMass - void ComputeMass(b2MassData* massData, float density) const override; - - /// Position - b2Vec2 m_p; -}; - -inline b2CircleShape::b2CircleShape() -{ - m_type = e_circle; - m_radius = 0.0f; - m_p.SetZero(); -} - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_collision.h b/3rdparty/box2d/include/box2d/b2_collision.h deleted file mode 100644 index 055704e7c8d6..000000000000 --- a/3rdparty/box2d/include/box2d/b2_collision.h +++ /dev/null @@ -1,306 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_COLLISION_H -#define B2_COLLISION_H - -#include - -#include "b2_api.h" -#include "b2_math.h" - -/// @file -/// Structures and functions used for computing contact points, distance -/// queries, and TOI queries. - -class b2Shape; -class b2CircleShape; -class b2EdgeShape; -class b2PolygonShape; - -const uint8 b2_nullFeature = UCHAR_MAX; - -/// The features that intersect to form the contact point -/// This must be 4 bytes or less. -struct B2_API b2ContactFeature -{ - enum Type - { - e_vertex = 0, - e_face = 1 - }; - - uint8 indexA; ///< Feature index on shapeA - uint8 indexB; ///< Feature index on shapeB - uint8 typeA; ///< The feature type on shapeA - uint8 typeB; ///< The feature type on shapeB -}; - -/// Contact ids to facilitate warm starting. -union B2_API b2ContactID -{ - b2ContactFeature cf; - uint32 key; ///< Used to quickly compare contact ids. -}; - -/// A manifold point is a contact point belonging to a contact -/// manifold. It holds details related to the geometry and dynamics -/// of the contact points. -/// The local point usage depends on the manifold type: -/// -e_circles: the local center of circleB -/// -e_faceA: the local center of cirlceB or the clip point of polygonB -/// -e_faceB: the clip point of polygonA -/// This structure is stored across time steps, so we keep it small. -/// Note: the impulses are used for internal caching and may not -/// provide reliable contact forces, especially for high speed collisions. -struct B2_API b2ManifoldPoint -{ - b2Vec2 localPoint; ///< usage depends on manifold type - float normalImpulse; ///< the non-penetration impulse - float tangentImpulse; ///< the friction impulse - b2ContactID id; ///< uniquely identifies a contact point between two shapes -}; - -/// A manifold for two touching convex shapes. -/// Box2D supports multiple types of contact: -/// - clip point versus plane with radius -/// - point versus point with radius (circles) -/// The local point usage depends on the manifold type: -/// -e_circles: the local center of circleA -/// -e_faceA: the center of faceA -/// -e_faceB: the center of faceB -/// Similarly the local normal usage: -/// -e_circles: not used -/// -e_faceA: the normal on polygonA -/// -e_faceB: the normal on polygonB -/// We store contacts in this way so that position correction can -/// account for movement, which is critical for continuous physics. -/// All contact scenarios must be expressed in one of these types. -/// This structure is stored across time steps, so we keep it small. -struct B2_API b2Manifold -{ - enum Type - { - e_circles, - e_faceA, - e_faceB - }; - - b2ManifoldPoint points[b2_maxManifoldPoints]; ///< the points of contact - b2Vec2 localNormal; ///< not use for Type::e_points - b2Vec2 localPoint; ///< usage depends on manifold type - Type type; - int32 pointCount; ///< the number of manifold points -}; - -/// This is used to compute the current state of a contact manifold. -struct B2_API b2WorldManifold -{ - /// Evaluate the manifold with supplied transforms. This assumes - /// modest motion from the original state. This does not change the - /// point count, impulses, etc. The radii must come from the shapes - /// that generated the manifold. - void Initialize(const b2Manifold* manifold, - const b2Transform& xfA, float radiusA, - const b2Transform& xfB, float radiusB); - - b2Vec2 normal; ///< world vector pointing from A to B - b2Vec2 points[b2_maxManifoldPoints]; ///< world contact point (point of intersection) - float separations[b2_maxManifoldPoints]; ///< a negative value indicates overlap, in meters -}; - -/// This is used for determining the state of contact points. -enum b2PointState -{ - b2_nullState, ///< point does not exist - b2_addState, ///< point was added in the update - b2_persistState, ///< point persisted across the update - b2_removeState ///< point was removed in the update -}; - -/// Compute the point states given two manifolds. The states pertain to the transition from manifold1 -/// to manifold2. So state1 is either persist or remove while state2 is either add or persist. -B2_API void b2GetPointStates(b2PointState state1[b2_maxManifoldPoints], b2PointState state2[b2_maxManifoldPoints], - const b2Manifold* manifold1, const b2Manifold* manifold2); - -/// Used for computing contact manifolds. -struct B2_API b2ClipVertex -{ - b2Vec2 v; - b2ContactID id; -}; - -/// Ray-cast input data. The ray extends from p1 to p1 + maxFraction * (p2 - p1). -struct B2_API b2RayCastInput -{ - b2Vec2 p1, p2; - float maxFraction; -}; - -/// Ray-cast output data. The ray hits at p1 + fraction * (p2 - p1), where p1 and p2 -/// come from b2RayCastInput. -struct B2_API b2RayCastOutput -{ - b2Vec2 normal; - float fraction; -}; - -/// An axis aligned bounding box. -struct B2_API b2AABB -{ - /// Verify that the bounds are sorted. - bool IsValid() const; - - /// Get the center of the AABB. - b2Vec2 GetCenter() const - { - return 0.5f * (lowerBound + upperBound); - } - - /// Get the extents of the AABB (half-widths). - b2Vec2 GetExtents() const - { - return 0.5f * (upperBound - lowerBound); - } - - /// Get the perimeter length - float GetPerimeter() const - { - float wx = upperBound.x - lowerBound.x; - float wy = upperBound.y - lowerBound.y; - return 2.0f * (wx + wy); - } - - /// Combine an AABB into this one. - void Combine(const b2AABB& aabb) - { - lowerBound = b2Min(lowerBound, aabb.lowerBound); - upperBound = b2Max(upperBound, aabb.upperBound); - } - - /// Combine two AABBs into this one. - void Combine(const b2AABB& aabb1, const b2AABB& aabb2) - { - lowerBound = b2Min(aabb1.lowerBound, aabb2.lowerBound); - upperBound = b2Max(aabb1.upperBound, aabb2.upperBound); - } - - /// Does this aabb contain the provided AABB. - bool Contains(const b2AABB& aabb) const - { - bool result = true; - result = result && lowerBound.x <= aabb.lowerBound.x; - result = result && lowerBound.y <= aabb.lowerBound.y; - result = result && aabb.upperBound.x <= upperBound.x; - result = result && aabb.upperBound.y <= upperBound.y; - return result; - } - - bool RayCast(b2RayCastOutput* output, const b2RayCastInput& input) const; - - b2Vec2 lowerBound; ///< the lower vertex - b2Vec2 upperBound; ///< the upper vertex -}; - -/// Compute the collision manifold between two circles. -B2_API void b2CollideCircles(b2Manifold* manifold, - const b2CircleShape* circleA, const b2Transform& xfA, - const b2CircleShape* circleB, const b2Transform& xfB); - -/// Compute the collision manifold between a polygon and a circle. -B2_API void b2CollidePolygonAndCircle(b2Manifold* manifold, - const b2PolygonShape* polygonA, const b2Transform& xfA, - const b2CircleShape* circleB, const b2Transform& xfB); - -/// Compute the collision manifold between two polygons. -B2_API void b2CollidePolygons(b2Manifold* manifold, - const b2PolygonShape* polygonA, const b2Transform& xfA, - const b2PolygonShape* polygonB, const b2Transform& xfB); - -/// Compute the collision manifold between an edge and a circle. -B2_API void b2CollideEdgeAndCircle(b2Manifold* manifold, - const b2EdgeShape* polygonA, const b2Transform& xfA, - const b2CircleShape* circleB, const b2Transform& xfB); - -/// Compute the collision manifold between an edge and a polygon. -B2_API void b2CollideEdgeAndPolygon(b2Manifold* manifold, - const b2EdgeShape* edgeA, const b2Transform& xfA, - const b2PolygonShape* polygonB, const b2Transform& xfB); - -/// Clipping for contact manifolds. -B2_API int32 b2ClipSegmentToLine(b2ClipVertex vOut[2], const b2ClipVertex vIn[2], - const b2Vec2& normal, float offset, int32 vertexIndexA); - -/// Determine if two generic shapes overlap. -B2_API bool b2TestOverlap( const b2Shape* shapeA, int32 indexA, - const b2Shape* shapeB, int32 indexB, - const b2Transform& xfA, const b2Transform& xfB); - -/// Convex hull used for polygon collision -struct b2Hull -{ - b2Vec2 points[b2_maxPolygonVertices]; - int32 count; -}; - -/// Compute the convex hull of a set of points. Returns an empty hull if it fails. -/// Some failure cases: -/// - all points very close together -/// - all points on a line -/// - less than 3 points -/// - more than b2_maxPolygonVertices points -/// This welds close points and removes collinear points. -b2Hull b2ComputeHull(const b2Vec2* points, int32 count); - -/// This determines if a hull is valid. Checks for: -/// - convexity -/// - collinear points -/// This is expensive and should not be called at runtime. -bool b2ValidateHull(const b2Hull& hull); - - -// ---------------- Inline Functions ------------------------------------------ - -inline bool b2AABB::IsValid() const -{ - b2Vec2 d = upperBound - lowerBound; - bool valid = d.x >= 0.0f && d.y >= 0.0f; - valid = valid && lowerBound.IsValid() && upperBound.IsValid(); - return valid; -} - -inline bool b2TestOverlap(const b2AABB& a, const b2AABB& b) -{ - b2Vec2 d1, d2; - d1 = b.lowerBound - a.upperBound; - d2 = a.lowerBound - b.upperBound; - - if (d1.x > 0.0f || d1.y > 0.0f) - return false; - - if (d2.x > 0.0f || d2.y > 0.0f) - return false; - - return true; -} - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_common.h b/3rdparty/box2d/include/box2d/b2_common.h deleted file mode 100644 index dfca8af1bf9d..000000000000 --- a/3rdparty/box2d/include/box2d/b2_common.h +++ /dev/null @@ -1,138 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_COMMON_H -#define B2_COMMON_H - -#include "b2_settings.h" - -#include -#include -#include - -#if !defined(NDEBUG) - #define b2DEBUG -#endif - -#define B2_NOT_USED(x) ((void)(x)) -#define b2Assert(A) assert(A) - -#define b2_maxFloat FLT_MAX -#define b2_epsilon FLT_EPSILON -#define b2_pi 3.14159265359f - -/// @file -/// Global tuning constants based on meters-kilograms-seconds (MKS) units. -/// - -// Collision - -/// The maximum number of contact points between two convex shapes. Do -/// not change this value. -#define b2_maxManifoldPoints 2 - -/// This is used to fatten AABBs in the dynamic tree. This allows proxies -/// to move by a small amount without triggering a tree adjustment. -/// This is in meters. -#define b2_aabbExtension (0.1f * b2_lengthUnitsPerMeter) - -/// This is used to fatten AABBs in the dynamic tree. This is used to predict -/// the future position based on the current displacement. -/// This is a dimensionless multiplier. -#define b2_aabbMultiplier 4.0f - -/// A small length used as a collision and constraint tolerance. Usually it is -/// chosen to be numerically significant, but visually insignificant. In meters. -#define b2_linearSlop (0.005f * b2_lengthUnitsPerMeter) - -/// A small angle used as a collision and constraint tolerance. Usually it is -/// chosen to be numerically significant, but visually insignificant. -#define b2_angularSlop (2.0f / 180.0f * b2_pi) - -/// The radius of the polygon/edge shape skin. This should not be modified. Making -/// this smaller means polygons will have an insufficient buffer for continuous collision. -/// Making it larger may create artifacts for vertex collision. -#define b2_polygonRadius (2.0f * b2_linearSlop) - -/// Maximum number of sub-steps per contact in continuous physics simulation. -#define b2_maxSubSteps 8 - - -// Dynamics - -/// Maximum number of contacts to be handled to solve a TOI impact. -#define b2_maxTOIContacts 32 - -/// The maximum linear position correction used when solving constraints. This helps to -/// prevent overshoot. Meters. -#define b2_maxLinearCorrection (0.2f * b2_lengthUnitsPerMeter) - -/// The maximum angular position correction used when solving constraints. This helps to -/// prevent overshoot. -#define b2_maxAngularCorrection (8.0f / 180.0f * b2_pi) - -/// The maximum linear translation of a body per step. This limit is very large and is used -/// to prevent numerical problems. You shouldn't need to adjust this. Meters. -#define b2_maxTranslation (2.0f * b2_lengthUnitsPerMeter) -#define b2_maxTranslationSquared (b2_maxTranslation * b2_maxTranslation) - -/// The maximum angular velocity of a body. This limit is very large and is used -/// to prevent numerical problems. You shouldn't need to adjust this. -#define b2_maxRotation (0.5f * b2_pi) -#define b2_maxRotationSquared (b2_maxRotation * b2_maxRotation) - -/// This scale factor controls how fast overlap is resolved. Ideally this would be 1 so -/// that overlap is removed in one time step. However using values close to 1 often lead -/// to overshoot. -#define b2_baumgarte 0.2f -#define b2_toiBaumgarte 0.75f - - -// Sleep - -/// The time that a body must be still before it will go to sleep. -#define b2_timeToSleep 0.5f - -/// A body cannot sleep if its linear velocity is above this tolerance. -#define b2_linearSleepTolerance (0.01f * b2_lengthUnitsPerMeter) - -/// A body cannot sleep if its angular velocity is above this tolerance. -#define b2_angularSleepTolerance (2.0f / 180.0f * b2_pi) - -/// Dump to a file. Only one dump file allowed at a time. -void b2OpenDump(const char* fileName); -void b2Dump(const char* string, ...); -void b2CloseDump(); - -/// Version numbering scheme. -/// See http://en.wikipedia.org/wiki/Software_versioning -struct b2Version -{ - int32 major; ///< significant changes - int32 minor; ///< incremental changes - int32 revision; ///< bug fixes -}; - -/// Current version. -extern B2_API b2Version b2_version; - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_contact.h b/3rdparty/box2d/include/box2d/b2_contact.h deleted file mode 100644 index de7541f1350b..000000000000 --- a/3rdparty/box2d/include/box2d/b2_contact.h +++ /dev/null @@ -1,386 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_CONTACT_H -#define B2_CONTACT_H - -#include "b2_api.h" -#include "b2_collision.h" -#include "b2_fixture.h" -#include "b2_math.h" -#include "b2_shape.h" - -class b2Body; -class b2Contact; -class b2Fixture; -class b2World; -class b2BlockAllocator; -class b2StackAllocator; -class b2ContactListener; - -/// Friction mixing law. The idea is to allow either fixture to drive the friction to zero. -/// For example, anything slides on ice. -inline float b2MixFriction(float friction1, float friction2) -{ - return b2Sqrt(friction1 * friction2); -} - -/// Restitution mixing law. The idea is allow for anything to bounce off an inelastic surface. -/// For example, a superball bounces on anything. -inline float b2MixRestitution(float restitution1, float restitution2) -{ - return restitution1 > restitution2 ? restitution1 : restitution2; -} - -/// Restitution mixing law. This picks the lowest value. -inline float b2MixRestitutionThreshold(float threshold1, float threshold2) -{ - return threshold1 < threshold2 ? threshold1 : threshold2; -} - -typedef b2Contact* b2ContactCreateFcn( b2Fixture* fixtureA, int32 indexA, - b2Fixture* fixtureB, int32 indexB, - b2BlockAllocator* allocator); -typedef void b2ContactDestroyFcn(b2Contact* contact, b2BlockAllocator* allocator); - -struct B2_API b2ContactRegister -{ - b2ContactCreateFcn* createFcn; - b2ContactDestroyFcn* destroyFcn; - bool primary; -}; - -/// A contact edge is used to connect bodies and contacts together -/// in a contact graph where each body is a node and each contact -/// is an edge. A contact edge belongs to a doubly linked list -/// maintained in each attached body. Each contact has two contact -/// nodes, one for each attached body. -struct B2_API b2ContactEdge -{ - b2Body* other; ///< provides quick access to the other body attached. - b2Contact* contact; ///< the contact - b2ContactEdge* prev; ///< the previous contact edge in the body's contact list - b2ContactEdge* next; ///< the next contact edge in the body's contact list -}; - -/// The class manages contact between two shapes. A contact exists for each overlapping -/// AABB in the broad-phase (except if filtered). Therefore a contact object may exist -/// that has no contact points. -class B2_API b2Contact -{ -public: - - /// Get the contact manifold. Do not modify the manifold unless you understand the - /// internals of Box2D. - b2Manifold* GetManifold(); - const b2Manifold* GetManifold() const; - - /// Get the world manifold. - void GetWorldManifold(b2WorldManifold* worldManifold) const; - - /// Is this contact touching? - bool IsTouching() const; - - /// Enable/disable this contact. This can be used inside the pre-solve - /// contact listener. The contact is only disabled for the current - /// time step (or sub-step in continuous collisions). - void SetEnabled(bool flag); - - /// Has this contact been disabled? - bool IsEnabled() const; - - /// Get the next contact in the world's contact list. - b2Contact* GetNext(); - const b2Contact* GetNext() const; - - /// Get fixture A in this contact. - b2Fixture* GetFixtureA(); - const b2Fixture* GetFixtureA() const; - - /// Get the child primitive index for fixture A. - int32 GetChildIndexA() const; - - /// Get fixture B in this contact. - b2Fixture* GetFixtureB(); - const b2Fixture* GetFixtureB() const; - - /// Get the child primitive index for fixture B. - int32 GetChildIndexB() const; - - /// Override the default friction mixture. You can call this in b2ContactListener::PreSolve. - /// This value persists until set or reset. - void SetFriction(float friction); - - /// Get the friction. - float GetFriction() const; - - /// Reset the friction mixture to the default value. - void ResetFriction(); - - /// Override the default restitution mixture. You can call this in b2ContactListener::PreSolve. - /// The value persists until you set or reset. - void SetRestitution(float restitution); - - /// Get the restitution. - float GetRestitution() const; - - /// Reset the restitution to the default value. - void ResetRestitution(); - - /// Override the default restitution velocity threshold mixture. You can call this in b2ContactListener::PreSolve. - /// The value persists until you set or reset. - void SetRestitutionThreshold(float threshold); - - /// Get the restitution threshold. - float GetRestitutionThreshold() const; - - /// Reset the restitution threshold to the default value. - void ResetRestitutionThreshold(); - - /// Set the desired tangent speed for a conveyor belt behavior. In meters per second. - void SetTangentSpeed(float speed); - - /// Get the desired tangent speed. In meters per second. - float GetTangentSpeed() const; - - /// Evaluate this contact with your own manifold and transforms. - virtual void Evaluate(b2Manifold* manifold, const b2Transform& xfA, const b2Transform& xfB) = 0; - -protected: - friend class b2ContactManager; - friend class b2World; - friend class b2ContactSolver; - friend class b2Body; - friend class b2Fixture; - - // Flags stored in m_flags - enum - { - // Used when crawling contact graph when forming islands. - e_islandFlag = 0x0001, - - // Set when the shapes are touching. - e_touchingFlag = 0x0002, - - // This contact can be disabled (by user) - e_enabledFlag = 0x0004, - - // This contact needs filtering because a fixture filter was changed. - e_filterFlag = 0x0008, - - // This bullet contact had a TOI event - e_bulletHitFlag = 0x0010, - - // This contact has a valid TOI in m_toi - e_toiFlag = 0x0020 - }; - - /// Flag this contact for filtering. Filtering will occur the next time step. - void FlagForFiltering(); - - static void AddType(b2ContactCreateFcn* createFcn, b2ContactDestroyFcn* destroyFcn, - b2Shape::Type typeA, b2Shape::Type typeB); - static void InitializeRegisters(); - static b2Contact* Create(b2Fixture* fixtureA, int32 indexA, b2Fixture* fixtureB, int32 indexB, b2BlockAllocator* allocator); - static void Destroy(b2Contact* contact, b2Shape::Type typeA, b2Shape::Type typeB, b2BlockAllocator* allocator); - static void Destroy(b2Contact* contact, b2BlockAllocator* allocator); - - b2Contact() : m_fixtureA(nullptr), m_fixtureB(nullptr) {} - b2Contact(b2Fixture* fixtureA, int32 indexA, b2Fixture* fixtureB, int32 indexB); - virtual ~b2Contact() {} - - void Update(b2ContactListener* listener); - - static b2ContactRegister s_registers[b2Shape::e_typeCount][b2Shape::e_typeCount]; - static bool s_initialized; - - uint32 m_flags; - - // World pool and list pointers. - b2Contact* m_prev; - b2Contact* m_next; - - // Nodes for connecting bodies. - b2ContactEdge m_nodeA; - b2ContactEdge m_nodeB; - - b2Fixture* m_fixtureA; - b2Fixture* m_fixtureB; - - int32 m_indexA; - int32 m_indexB; - - b2Manifold m_manifold; - - int32 m_toiCount; - float m_toi; - - float m_friction; - float m_restitution; - float m_restitutionThreshold; - - float m_tangentSpeed; -}; - -inline b2Manifold* b2Contact::GetManifold() -{ - return &m_manifold; -} - -inline const b2Manifold* b2Contact::GetManifold() const -{ - return &m_manifold; -} - -inline void b2Contact::GetWorldManifold(b2WorldManifold* worldManifold) const -{ - const b2Body* bodyA = m_fixtureA->GetBody(); - const b2Body* bodyB = m_fixtureB->GetBody(); - const b2Shape* shapeA = m_fixtureA->GetShape(); - const b2Shape* shapeB = m_fixtureB->GetShape(); - - worldManifold->Initialize(&m_manifold, bodyA->GetTransform(), shapeA->m_radius, bodyB->GetTransform(), shapeB->m_radius); -} - -inline void b2Contact::SetEnabled(bool flag) -{ - if (flag) - { - m_flags |= e_enabledFlag; - } - else - { - m_flags &= ~e_enabledFlag; - } -} - -inline bool b2Contact::IsEnabled() const -{ - return (m_flags & e_enabledFlag) == e_enabledFlag; -} - -inline bool b2Contact::IsTouching() const -{ - return (m_flags & e_touchingFlag) == e_touchingFlag; -} - -inline b2Contact* b2Contact::GetNext() -{ - return m_next; -} - -inline const b2Contact* b2Contact::GetNext() const -{ - return m_next; -} - -inline b2Fixture* b2Contact::GetFixtureA() -{ - return m_fixtureA; -} - -inline const b2Fixture* b2Contact::GetFixtureA() const -{ - return m_fixtureA; -} - -inline b2Fixture* b2Contact::GetFixtureB() -{ - return m_fixtureB; -} - -inline int32 b2Contact::GetChildIndexA() const -{ - return m_indexA; -} - -inline const b2Fixture* b2Contact::GetFixtureB() const -{ - return m_fixtureB; -} - -inline int32 b2Contact::GetChildIndexB() const -{ - return m_indexB; -} - -inline void b2Contact::FlagForFiltering() -{ - m_flags |= e_filterFlag; -} - -inline void b2Contact::SetFriction(float friction) -{ - m_friction = friction; -} - -inline float b2Contact::GetFriction() const -{ - return m_friction; -} - -inline void b2Contact::ResetFriction() -{ - m_friction = b2MixFriction(m_fixtureA->m_friction, m_fixtureB->m_friction); -} - -inline void b2Contact::SetRestitution(float restitution) -{ - m_restitution = restitution; -} - -inline float b2Contact::GetRestitution() const -{ - return m_restitution; -} - -inline void b2Contact::ResetRestitution() -{ - m_restitution = b2MixRestitution(m_fixtureA->m_restitution, m_fixtureB->m_restitution); -} - -inline void b2Contact::SetRestitutionThreshold(float threshold) -{ - m_restitutionThreshold = threshold; -} - -inline float b2Contact::GetRestitutionThreshold() const -{ - return m_restitutionThreshold; -} - -inline void b2Contact::ResetRestitutionThreshold() -{ - m_restitutionThreshold = b2MixRestitutionThreshold(m_fixtureA->m_restitutionThreshold, m_fixtureB->m_restitutionThreshold); -} - -inline void b2Contact::SetTangentSpeed(float speed) -{ - m_tangentSpeed = speed; -} - -inline float b2Contact::GetTangentSpeed() const -{ - return m_tangentSpeed; -} - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_contact_manager.h b/3rdparty/box2d/include/box2d/b2_contact_manager.h deleted file mode 100644 index fbd3b4d401be..000000000000 --- a/3rdparty/box2d/include/box2d/b2_contact_manager.h +++ /dev/null @@ -1,57 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_CONTACT_MANAGER_H -#define B2_CONTACT_MANAGER_H - -#include "b2_api.h" -#include "b2_broad_phase.h" - -class b2Contact; -class b2ContactFilter; -class b2ContactListener; -class b2BlockAllocator; - -// Delegate of b2World. -class B2_API b2ContactManager -{ -public: - b2ContactManager(); - - // Broad-phase callback. - void AddPair(void* proxyUserDataA, void* proxyUserDataB); - - void FindNewContacts(); - - void Destroy(b2Contact* c); - - void Collide(); - - b2BroadPhase m_broadPhase; - b2Contact* m_contactList; - int32 m_contactCount; - b2ContactFilter* m_contactFilter; - b2ContactListener* m_contactListener; - b2BlockAllocator* m_allocator; -}; - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_distance.h b/3rdparty/box2d/include/box2d/b2_distance.h deleted file mode 100644 index 3e05773de339..000000000000 --- a/3rdparty/box2d/include/box2d/b2_distance.h +++ /dev/null @@ -1,171 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_DISTANCE_H -#define B2_DISTANCE_H - -#include "b2_api.h" -#include "b2_math.h" - -class b2Shape; - -/// A distance proxy is used by the GJK algorithm. -/// It encapsulates any shape. -struct B2_API b2DistanceProxy -{ - b2DistanceProxy() : m_vertices(nullptr), m_count(0), m_radius(0.0f) {} - - /// Initialize the proxy using the given shape. The shape - /// must remain in scope while the proxy is in use. - void Set(const b2Shape* shape, int32 index); - - /// Initialize the proxy using a vertex cloud and radius. The vertices - /// must remain in scope while the proxy is in use. - void Set(const b2Vec2* vertices, int32 count, float radius); - - /// Get the supporting vertex index in the given direction. - int32 GetSupport(const b2Vec2& d) const; - - /// Get the supporting vertex in the given direction. - const b2Vec2& GetSupportVertex(const b2Vec2& d) const; - - /// Get the vertex count. - int32 GetVertexCount() const; - - /// Get a vertex by index. Used by b2Distance. - const b2Vec2& GetVertex(int32 index) const; - - b2Vec2 m_buffer[2]; - const b2Vec2* m_vertices; - int32 m_count; - float m_radius; -}; - -/// Used to warm start b2Distance. -/// Set count to zero on first call. -struct B2_API b2SimplexCache -{ - float metric; ///< length or area - uint16 count; - uint8 indexA[3]; ///< vertices on shape A - uint8 indexB[3]; ///< vertices on shape B -}; - -/// Input for b2Distance. -/// You have to option to use the shape radii -/// in the computation. Even -struct B2_API b2DistanceInput -{ - b2DistanceProxy proxyA; - b2DistanceProxy proxyB; - b2Transform transformA; - b2Transform transformB; - bool useRadii; -}; - -/// Output for b2Distance. -struct B2_API b2DistanceOutput -{ - b2Vec2 pointA; ///< closest point on shapeA - b2Vec2 pointB; ///< closest point on shapeB - float distance; - int32 iterations; ///< number of GJK iterations used -}; - -/// Compute the closest points between two shapes. Supports any combination of: -/// b2CircleShape, b2PolygonShape, b2EdgeShape. The simplex cache is input/output. -/// On the first call set b2SimplexCache.count to zero. -B2_API void b2Distance(b2DistanceOutput* output, - b2SimplexCache* cache, - const b2DistanceInput* input); - -/// Input parameters for b2ShapeCast -struct B2_API b2ShapeCastInput -{ - b2DistanceProxy proxyA; - b2DistanceProxy proxyB; - b2Transform transformA; - b2Transform transformB; - b2Vec2 translationB; -}; - -/// Output results for b2ShapeCast -struct B2_API b2ShapeCastOutput -{ - b2Vec2 point; - b2Vec2 normal; - float lambda; - int32 iterations; -}; - -/// Perform a linear shape cast of shape B moving and shape A fixed. Determines the hit point, normal, and translation fraction. -/// @returns true if hit, false if there is no hit or an initial overlap -B2_API bool b2ShapeCast(b2ShapeCastOutput* output, const b2ShapeCastInput* input); - -////////////////////////////////////////////////////////////////////////// - -inline int32 b2DistanceProxy::GetVertexCount() const -{ - return m_count; -} - -inline const b2Vec2& b2DistanceProxy::GetVertex(int32 index) const -{ - b2Assert(0 <= index && index < m_count); - return m_vertices[index]; -} - -inline int32 b2DistanceProxy::GetSupport(const b2Vec2& d) const -{ - int32 bestIndex = 0; - float bestValue = b2Dot(m_vertices[0], d); - for (int32 i = 1; i < m_count; ++i) - { - float value = b2Dot(m_vertices[i], d); - if (value > bestValue) - { - bestIndex = i; - bestValue = value; - } - } - - return bestIndex; -} - -inline const b2Vec2& b2DistanceProxy::GetSupportVertex(const b2Vec2& d) const -{ - int32 bestIndex = 0; - float bestValue = b2Dot(m_vertices[0], d); - for (int32 i = 1; i < m_count; ++i) - { - float value = b2Dot(m_vertices[i], d); - if (value > bestValue) - { - bestIndex = i; - bestValue = value; - } - } - - return m_vertices[bestIndex]; -} - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_distance_joint.h b/3rdparty/box2d/include/box2d/b2_distance_joint.h deleted file mode 100644 index cfc75fefba4e..000000000000 --- a/3rdparty/box2d/include/box2d/b2_distance_joint.h +++ /dev/null @@ -1,176 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_DISTANCE_JOINT_H -#define B2_DISTANCE_JOINT_H - -#include "b2_api.h" -#include "b2_joint.h" - -/// Distance joint definition. This requires defining an anchor point on both -/// bodies and the non-zero distance of the distance joint. The definition uses -/// local anchor points so that the initial configuration can violate the -/// constraint slightly. This helps when saving and loading a game. -struct B2_API b2DistanceJointDef : public b2JointDef -{ - b2DistanceJointDef() - { - type = e_distanceJoint; - localAnchorA.Set(0.0f, 0.0f); - localAnchorB.Set(0.0f, 0.0f); - length = 1.0f; - minLength = 0.0f; - maxLength = FLT_MAX; - stiffness = 0.0f; - damping = 0.0f; - } - - /// Initialize the bodies, anchors, and rest length using world space anchors. - /// The minimum and maximum lengths are set to the rest length. - void Initialize(b2Body* bodyA, b2Body* bodyB, - const b2Vec2& anchorA, const b2Vec2& anchorB); - - /// The local anchor point relative to bodyA's origin. - b2Vec2 localAnchorA; - - /// The local anchor point relative to bodyB's origin. - b2Vec2 localAnchorB; - - /// The rest length of this joint. Clamped to a stable minimum value. - float length; - - /// Minimum length. Clamped to a stable minimum value. - float minLength; - - /// Maximum length. Must be greater than or equal to the minimum length. - float maxLength; - - /// The linear stiffness in N/m. - float stiffness; - - /// The linear damping in N*s/m. - float damping; -}; - -/// A distance joint constrains two points on two bodies to remain at a fixed -/// distance from each other. You can view this as a massless, rigid rod. -class B2_API b2DistanceJoint : public b2Joint -{ -public: - - b2Vec2 GetAnchorA() const override; - b2Vec2 GetAnchorB() const override; - - /// Get the reaction force given the inverse time step. - /// Unit is N. - b2Vec2 GetReactionForce(float inv_dt) const override; - - /// Get the reaction torque given the inverse time step. - /// Unit is N*m. This is always zero for a distance joint. - float GetReactionTorque(float inv_dt) const override; - - /// The local anchor point relative to bodyA's origin. - const b2Vec2& GetLocalAnchorA() const { return m_localAnchorA; } - - /// The local anchor point relative to bodyB's origin. - const b2Vec2& GetLocalAnchorB() const { return m_localAnchorB; } - - /// Get the rest length - float GetLength() const { return m_length; } - - /// Set the rest length - /// @returns clamped rest length - float SetLength(float length); - - /// Get the minimum length - float GetMinLength() const { return m_minLength; } - - /// Set the minimum length - /// @returns the clamped minimum length - float SetMinLength(float minLength); - - /// Get the maximum length - float GetMaxLength() const { return m_maxLength; } - - /// Set the maximum length - /// @returns the clamped maximum length - float SetMaxLength(float maxLength); - - /// Get the current length - float GetCurrentLength() const; - - /// Set/get the linear stiffness in N/m - void SetStiffness(float stiffness) { m_stiffness = stiffness; } - float GetStiffness() const { return m_stiffness; } - - /// Set/get linear damping in N*s/m - void SetDamping(float damping) { m_damping = damping; } - float GetDamping() const { return m_damping; } - - /// Dump joint to dmLog - void Dump() override; - - /// - void Draw(b2Draw* draw) const override; - -protected: - - friend class b2Joint; - b2DistanceJoint(const b2DistanceJointDef* data); - - void InitVelocityConstraints(const b2SolverData& data) override; - void SolveVelocityConstraints(const b2SolverData& data) override; - bool SolvePositionConstraints(const b2SolverData& data) override; - - float m_stiffness; - float m_damping; - float m_bias; - float m_length; - float m_minLength; - float m_maxLength; - - // Solver shared - b2Vec2 m_localAnchorA; - b2Vec2 m_localAnchorB; - float m_gamma; - float m_impulse; - float m_lowerImpulse; - float m_upperImpulse; - - // Solver temp - int32 m_indexA; - int32 m_indexB; - b2Vec2 m_u; - b2Vec2 m_rA; - b2Vec2 m_rB; - b2Vec2 m_localCenterA; - b2Vec2 m_localCenterB; - float m_currentLength; - float m_invMassA; - float m_invMassB; - float m_invIA; - float m_invIB; - float m_softMass; - float m_mass; -}; - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_draw.h b/3rdparty/box2d/include/box2d/b2_draw.h deleted file mode 100644 index a4d17118781b..000000000000 --- a/3rdparty/box2d/include/box2d/b2_draw.h +++ /dev/null @@ -1,102 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_DRAW_H -#define B2_DRAW_H - -#include "b2_api.h" -#include "b2_math.h" - -/// Color for debug drawing. Each value has the range [0,1]. -struct B2_API b2Color -{ - b2Color() {} - b2Color(float rIn, float gIn, float bIn, float aIn = 1.0f) - { - r = rIn; g = gIn; b = bIn; a = aIn; - } - - void Set(float rIn, float gIn, float bIn, float aIn = 1.0f) - { - r = rIn; g = gIn; b = bIn; a = aIn; - } - - float r, g, b, a; -}; - -/// Implement and register this class with a b2World to provide debug drawing of physics -/// entities in your game. -class B2_API b2Draw -{ -public: - b2Draw(); - - virtual ~b2Draw() {} - - enum - { - e_shapeBit = 0x0001, ///< draw shapes - e_jointBit = 0x0002, ///< draw joint connections - e_aabbBit = 0x0004, ///< draw axis aligned bounding boxes - e_pairBit = 0x0008, ///< draw broad-phase pairs - e_centerOfMassBit = 0x0010 ///< draw center of mass frame - }; - - /// Set the drawing flags. - void SetFlags(uint32 flags); - - /// Get the drawing flags. - uint32 GetFlags() const; - - /// Append flags to the current flags. - void AppendFlags(uint32 flags); - - /// Clear flags from the current flags. - void ClearFlags(uint32 flags); - - /// Draw a closed polygon provided in CCW order. - virtual void DrawPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) = 0; - - /// Draw a solid closed polygon provided in CCW order. - virtual void DrawSolidPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) = 0; - - /// Draw a circle. - virtual void DrawCircle(const b2Vec2& center, float radius, const b2Color& color) = 0; - - /// Draw a solid circle. - virtual void DrawSolidCircle(const b2Vec2& center, float radius, const b2Vec2& axis, const b2Color& color) = 0; - - /// Draw a line segment. - virtual void DrawSegment(const b2Vec2& p1, const b2Vec2& p2, const b2Color& color) = 0; - - /// Draw a transform. Choose your own length scale. - /// @param xf a transform. - virtual void DrawTransform(const b2Transform& xf) = 0; - - /// Draw a point. - virtual void DrawPoint(const b2Vec2& p, float size, const b2Color& color) = 0; - -protected: - uint32 m_drawFlags; -}; - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_dynamic_tree.h b/3rdparty/box2d/include/box2d/b2_dynamic_tree.h deleted file mode 100644 index b85491915d92..000000000000 --- a/3rdparty/box2d/include/box2d/b2_dynamic_tree.h +++ /dev/null @@ -1,308 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_DYNAMIC_TREE_H -#define B2_DYNAMIC_TREE_H - -#include "b2_api.h" -#include "b2_collision.h" -#include "b2_growable_stack.h" - -#define b2_nullNode (-1) - -/// A node in the dynamic tree. The client does not interact with this directly. -struct B2_API b2TreeNode -{ - bool IsLeaf() const - { - return child1 == b2_nullNode; - } - - /// Enlarged AABB - b2AABB aabb; - - void* userData; - - union - { - int32 parent; - int32 next; - }; - - int32 child1; - int32 child2; - - // leaf = 0, free node = -1 - int32 height; - - bool moved; -}; - -/// A dynamic AABB tree broad-phase, inspired by Nathanael Presson's btDbvt. -/// A dynamic tree arranges data in a binary tree to accelerate -/// queries such as volume queries and ray casts. Leafs are proxies -/// with an AABB. In the tree we expand the proxy AABB by b2_fatAABBFactor -/// so that the proxy AABB is bigger than the client object. This allows the client -/// object to move by small amounts without triggering a tree update. -/// -/// Nodes are pooled and relocatable, so we use node indices rather than pointers. -class B2_API b2DynamicTree -{ -public: - /// Constructing the tree initializes the node pool. - b2DynamicTree(); - - /// Destroy the tree, freeing the node pool. - ~b2DynamicTree(); - - /// Create a proxy. Provide a tight fitting AABB and a userData pointer. - int32 CreateProxy(const b2AABB& aabb, void* userData); - - /// Destroy a proxy. This asserts if the id is invalid. - void DestroyProxy(int32 proxyId); - - /// Move a proxy with a swepted AABB. If the proxy has moved outside of its fattened AABB, - /// then the proxy is removed from the tree and re-inserted. Otherwise - /// the function returns immediately. - /// @return true if the proxy was re-inserted. - bool MoveProxy(int32 proxyId, const b2AABB& aabb1, const b2Vec2& displacement); - - /// Get proxy user data. - /// @return the proxy user data or 0 if the id is invalid. - void* GetUserData(int32 proxyId) const; - - bool WasMoved(int32 proxyId) const; - void ClearMoved(int32 proxyId); - - /// Get the fat AABB for a proxy. - const b2AABB& GetFatAABB(int32 proxyId) const; - - /// Query an AABB for overlapping proxies. The callback class - /// is called for each proxy that overlaps the supplied AABB. - template - void Query(T* callback, const b2AABB& aabb) const; - - /// Ray-cast against the proxies in the tree. This relies on the callback - /// to perform a exact ray-cast in the case were the proxy contains a shape. - /// The callback also performs the any collision filtering. This has performance - /// roughly equal to k * log(n), where k is the number of collisions and n is the - /// number of proxies in the tree. - /// @param input the ray-cast input data. The ray extends from p1 to p1 + maxFraction * (p2 - p1). - /// @param callback a callback class that is called for each proxy that is hit by the ray. - template - void RayCast(T* callback, const b2RayCastInput& input) const; - - /// Validate this tree. For testing. - void Validate() const; - - /// Compute the height of the binary tree in O(N) time. Should not be - /// called often. - int32 GetHeight() const; - - /// Get the maximum balance of an node in the tree. The balance is the difference - /// in height of the two children of a node. - int32 GetMaxBalance() const; - - /// Get the ratio of the sum of the node areas to the root area. - float GetAreaRatio() const; - - /// Build an optimal tree. Very expensive. For testing. - void RebuildBottomUp(); - - /// Shift the world origin. Useful for large worlds. - /// The shift formula is: position -= newOrigin - /// @param newOrigin the new origin with respect to the old origin - void ShiftOrigin(const b2Vec2& newOrigin); - -private: - - int32 AllocateNode(); - void FreeNode(int32 node); - - void InsertLeaf(int32 node); - void RemoveLeaf(int32 node); - - int32 Balance(int32 index); - - int32 ComputeHeight() const; - int32 ComputeHeight(int32 nodeId) const; - - void ValidateStructure(int32 index) const; - void ValidateMetrics(int32 index) const; - - int32 m_root; - - b2TreeNode* m_nodes; - int32 m_nodeCount; - int32 m_nodeCapacity; - - int32 m_freeList; - - int32 m_insertionCount; -}; - -inline void* b2DynamicTree::GetUserData(int32 proxyId) const -{ - b2Assert(0 <= proxyId && proxyId < m_nodeCapacity); - return m_nodes[proxyId].userData; -} - -inline bool b2DynamicTree::WasMoved(int32 proxyId) const -{ - b2Assert(0 <= proxyId && proxyId < m_nodeCapacity); - return m_nodes[proxyId].moved; -} - -inline void b2DynamicTree::ClearMoved(int32 proxyId) -{ - b2Assert(0 <= proxyId && proxyId < m_nodeCapacity); - m_nodes[proxyId].moved = false; -} - -inline const b2AABB& b2DynamicTree::GetFatAABB(int32 proxyId) const -{ - b2Assert(0 <= proxyId && proxyId < m_nodeCapacity); - return m_nodes[proxyId].aabb; -} - -template -inline void b2DynamicTree::Query(T* callback, const b2AABB& aabb) const -{ - b2GrowableStack stack; - stack.Push(m_root); - - while (stack.GetCount() > 0) - { - int32 nodeId = stack.Pop(); - if (nodeId == b2_nullNode) - { - continue; - } - - const b2TreeNode* node = m_nodes + nodeId; - - if (b2TestOverlap(node->aabb, aabb)) - { - if (node->IsLeaf()) - { - bool proceed = callback->QueryCallback(nodeId); - if (proceed == false) - { - return; - } - } - else - { - stack.Push(node->child1); - stack.Push(node->child2); - } - } - } -} - -template -inline void b2DynamicTree::RayCast(T* callback, const b2RayCastInput& input) const -{ - b2Vec2 p1 = input.p1; - b2Vec2 p2 = input.p2; - b2Vec2 r = p2 - p1; - b2Assert(r.LengthSquared() > 0.0f); - r.Normalize(); - - // v is perpendicular to the segment. - b2Vec2 v = b2Cross(1.0f, r); - b2Vec2 abs_v = b2Abs(v); - - // Separating axis for segment (Gino, p80). - // |dot(v, p1 - c)| > dot(|v|, h) - - float maxFraction = input.maxFraction; - - // Build a bounding box for the segment. - b2AABB segmentAABB; - { - b2Vec2 t = p1 + maxFraction * (p2 - p1); - segmentAABB.lowerBound = b2Min(p1, t); - segmentAABB.upperBound = b2Max(p1, t); - } - - b2GrowableStack stack; - stack.Push(m_root); - - while (stack.GetCount() > 0) - { - int32 nodeId = stack.Pop(); - if (nodeId == b2_nullNode) - { - continue; - } - - const b2TreeNode* node = m_nodes + nodeId; - - if (b2TestOverlap(node->aabb, segmentAABB) == false) - { - continue; - } - - // Separating axis for segment (Gino, p80). - // |dot(v, p1 - c)| > dot(|v|, h) - b2Vec2 c = node->aabb.GetCenter(); - b2Vec2 h = node->aabb.GetExtents(); - float separation = b2Abs(b2Dot(v, p1 - c)) - b2Dot(abs_v, h); - if (separation > 0.0f) - { - continue; - } - - if (node->IsLeaf()) - { - b2RayCastInput subInput; - subInput.p1 = input.p1; - subInput.p2 = input.p2; - subInput.maxFraction = maxFraction; - - float value = callback->RayCastCallback(subInput, nodeId); - - if (value == 0.0f) - { - // The client has terminated the ray cast. - return; - } - - if (value > 0.0f) - { - // Update segment bounding box. - maxFraction = value; - b2Vec2 t = p1 + maxFraction * (p2 - p1); - segmentAABB.lowerBound = b2Min(p1, t); - segmentAABB.upperBound = b2Max(p1, t); - } - } - else - { - stack.Push(node->child1); - stack.Push(node->child2); - } - } -} - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_edge_shape.h b/3rdparty/box2d/include/box2d/b2_edge_shape.h deleted file mode 100644 index b930ee87281d..000000000000 --- a/3rdparty/box2d/include/box2d/b2_edge_shape.h +++ /dev/null @@ -1,86 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_EDGE_SHAPE_H -#define B2_EDGE_SHAPE_H - -#include "b2_api.h" -#include "b2_shape.h" - -/// A line segment (edge) shape. These can be connected in chains or loops -/// to other edge shapes. Edges created independently are two-sided and do -/// no provide smooth movement across junctions. -class B2_API b2EdgeShape : public b2Shape -{ -public: - b2EdgeShape(); - - /// Set this as a part of a sequence. Vertex v0 precedes the edge and vertex v3 - /// follows. These extra vertices are used to provide smooth movement - /// across junctions. This also makes the collision one-sided. The edge - /// normal points to the right looking from v1 to v2. - void SetOneSided(const b2Vec2& v0, const b2Vec2& v1,const b2Vec2& v2, const b2Vec2& v3); - - /// Set this as an isolated edge. Collision is two-sided. - void SetTwoSided(const b2Vec2& v1, const b2Vec2& v2); - - /// Implement b2Shape. - b2Shape* Clone(b2BlockAllocator* allocator) const override; - - /// @see b2Shape::GetChildCount - int32 GetChildCount() const override; - - /// @see b2Shape::TestPoint - bool TestPoint(const b2Transform& transform, const b2Vec2& p) const override; - - /// Implement b2Shape. - bool RayCast(b2RayCastOutput* output, const b2RayCastInput& input, - const b2Transform& transform, int32 childIndex) const override; - - /// @see b2Shape::ComputeAABB - void ComputeAABB(b2AABB* aabb, const b2Transform& transform, int32 childIndex) const override; - - /// @see b2Shape::ComputeMass - void ComputeMass(b2MassData* massData, float density) const override; - - /// These are the edge vertices - b2Vec2 m_vertex1, m_vertex2; - - /// Optional adjacent vertices. These are used for smooth collision. - b2Vec2 m_vertex0, m_vertex3; - - /// Uses m_vertex0 and m_vertex3 to create smooth collision. - bool m_oneSided; -}; - -inline b2EdgeShape::b2EdgeShape() -{ - m_type = e_edge; - m_radius = b2_polygonRadius; - m_vertex0.x = 0.0f; - m_vertex0.y = 0.0f; - m_vertex3.x = 0.0f; - m_vertex3.y = 0.0f; - m_oneSided = false; -} - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_fixture.h b/3rdparty/box2d/include/box2d/b2_fixture.h deleted file mode 100644 index 47d321df5ec1..000000000000 --- a/3rdparty/box2d/include/box2d/b2_fixture.h +++ /dev/null @@ -1,371 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_FIXTURE_H -#define B2_FIXTURE_H - -#include "b2_api.h" -#include "b2_body.h" -#include "b2_collision.h" -#include "b2_shape.h" - -class b2BlockAllocator; -class b2Body; -class b2BroadPhase; -class b2Fixture; - -/// This holds contact filtering data. -struct B2_API b2Filter -{ - b2Filter() - { - categoryBits = 0x0001; - maskBits = 0xFFFF; - groupIndex = 0; - } - - /// The collision category bits. Normally you would just set one bit. - uint16 categoryBits; - - /// The collision mask bits. This states the categories that this - /// shape would accept for collision. - uint16 maskBits; - - /// Collision groups allow a certain group of objects to never collide (negative) - /// or always collide (positive). Zero means no collision group. Non-zero group - /// filtering always wins against the mask bits. - int16 groupIndex; -}; - -/// A fixture definition is used to create a fixture. This class defines an -/// abstract fixture definition. You can reuse fixture definitions safely. -struct B2_API b2FixtureDef -{ - /// The constructor sets the default fixture definition values. - b2FixtureDef() - { - shape = nullptr; - friction = 0.2f; - restitution = 0.0f; - restitutionThreshold = 1.0f * b2_lengthUnitsPerMeter; - density = 0.0f; - isSensor = false; - } - - /// The shape, this must be set. The shape will be cloned, so you - /// can create the shape on the stack. - const b2Shape* shape; - - /// Use this to store application specific fixture data. - b2FixtureUserData userData; - - /// The friction coefficient, usually in the range [0,1]. - float friction; - - /// The restitution (elasticity) usually in the range [0,1]. - float restitution; - - /// Restitution velocity threshold, usually in m/s. Collisions above this - /// speed have restitution applied (will bounce). - float restitutionThreshold; - - /// The density, usually in kg/m^2. - float density; - - /// A sensor shape collects contact information but never generates a collision - /// response. - bool isSensor; - - /// Contact filtering data. - b2Filter filter; -}; - -/// This proxy is used internally to connect fixtures to the broad-phase. -struct B2_API b2FixtureProxy -{ - b2AABB aabb; - b2Fixture* fixture; - int32 childIndex; - int32 proxyId; -}; - -/// A fixture is used to attach a shape to a body for collision detection. A fixture -/// inherits its transform from its parent. Fixtures hold additional non-geometric data -/// such as friction, collision filters, etc. -/// Fixtures are created via b2Body::CreateFixture. -/// @warning you cannot reuse fixtures. -class B2_API b2Fixture -{ -public: - /// Get the type of the child shape. You can use this to down cast to the concrete shape. - /// @return the shape type. - b2Shape::Type GetType() const; - - /// Get the child shape. You can modify the child shape, however you should not change the - /// number of vertices because this will crash some collision caching mechanisms. - /// Manipulating the shape may lead to non-physical behavior. - b2Shape* GetShape(); - const b2Shape* GetShape() const; - - /// Set if this fixture is a sensor. - void SetSensor(bool sensor); - - /// Is this fixture a sensor (non-solid)? - /// @return the true if the shape is a sensor. - bool IsSensor() const; - - /// Set the contact filtering data. This will not update contacts until the next time - /// step when either parent body is active and awake. - /// This automatically calls Refilter. - void SetFilterData(const b2Filter& filter); - - /// Get the contact filtering data. - const b2Filter& GetFilterData() const; - - /// Call this if you want to establish collision that was previously disabled by b2ContactFilter::ShouldCollide. - void Refilter(); - - /// Get the parent body of this fixture. This is nullptr if the fixture is not attached. - /// @return the parent body. - b2Body* GetBody(); - const b2Body* GetBody() const; - - /// Get the next fixture in the parent body's fixture list. - /// @return the next shape. - b2Fixture* GetNext(); - const b2Fixture* GetNext() const; - - /// Get the user data that was assigned in the fixture definition. Use this to - /// store your application specific data. - b2FixtureUserData& GetUserData(); - const b2FixtureUserData& GetUserData() const; - - /// Test a point for containment in this fixture. - /// @param p a point in world coordinates. - bool TestPoint(const b2Vec2& p) const; - - /// Cast a ray against this shape. - /// @param output the ray-cast results. - /// @param input the ray-cast input parameters. - /// @param childIndex the child shape index (e.g. edge index) - bool RayCast(b2RayCastOutput* output, const b2RayCastInput& input, int32 childIndex) const; - - /// Get the mass data for this fixture. The mass data is based on the density and - /// the shape. The rotational inertia is about the shape's origin. This operation - /// may be expensive. - void GetMassData(b2MassData* massData) const; - - /// Set the density of this fixture. This will _not_ automatically adjust the mass - /// of the body. You must call b2Body::ResetMassData to update the body's mass. - void SetDensity(float density); - - /// Get the density of this fixture. - float GetDensity() const; - - /// Get the coefficient of friction. - float GetFriction() const; - - /// Set the coefficient of friction. This will _not_ change the friction of - /// existing contacts. - void SetFriction(float friction); - - /// Get the coefficient of restitution. - float GetRestitution() const; - - /// Set the coefficient of restitution. This will _not_ change the restitution of - /// existing contacts. - void SetRestitution(float restitution); - - /// Get the restitution velocity threshold. - float GetRestitutionThreshold() const; - - /// Set the restitution threshold. This will _not_ change the restitution threshold of - /// existing contacts. - void SetRestitutionThreshold(float threshold); - - /// Get the fixture's AABB. This AABB may be enlarge and/or stale. - /// If you need a more accurate AABB, compute it using the shape and - /// the body transform. - const b2AABB& GetAABB(int32 childIndex) const; - - /// Dump this fixture to the log file. - void Dump(int32 bodyIndex); - -protected: - - friend class b2Body; - friend class b2World; - friend class b2Contact; - friend class b2ContactManager; - - b2Fixture(); - - // We need separation create/destroy functions from the constructor/destructor because - // the destructor cannot access the allocator (no destructor arguments allowed by C++). - void Create(b2BlockAllocator* allocator, b2Body* body, const b2FixtureDef* def); - void Destroy(b2BlockAllocator* allocator); - - // These support body activation/deactivation. - void CreateProxies(b2BroadPhase* broadPhase, const b2Transform& xf); - void DestroyProxies(b2BroadPhase* broadPhase); - - void Synchronize(b2BroadPhase* broadPhase, const b2Transform& xf1, const b2Transform& xf2); - - float m_density; - - b2Fixture* m_next; - b2Body* m_body; - - b2Shape* m_shape; - - float m_friction; - float m_restitution; - float m_restitutionThreshold; - - b2FixtureProxy* m_proxies; - int32 m_proxyCount; - - b2Filter m_filter; - - bool m_isSensor; - - b2FixtureUserData m_userData; -}; - -inline b2Shape::Type b2Fixture::GetType() const -{ - return m_shape->GetType(); -} - -inline b2Shape* b2Fixture::GetShape() -{ - return m_shape; -} - -inline const b2Shape* b2Fixture::GetShape() const -{ - return m_shape; -} - -inline bool b2Fixture::IsSensor() const -{ - return m_isSensor; -} - -inline const b2Filter& b2Fixture::GetFilterData() const -{ - return m_filter; -} - -inline b2FixtureUserData& b2Fixture::GetUserData() -{ - return m_userData; -} - -inline const b2FixtureUserData& b2Fixture::GetUserData() const -{ - return m_userData; -} - -inline b2Body* b2Fixture::GetBody() -{ - return m_body; -} - -inline const b2Body* b2Fixture::GetBody() const -{ - return m_body; -} - -inline b2Fixture* b2Fixture::GetNext() -{ - return m_next; -} - -inline const b2Fixture* b2Fixture::GetNext() const -{ - return m_next; -} - -inline void b2Fixture::SetDensity(float density) -{ - b2Assert(b2IsValid(density) && density >= 0.0f); - m_density = density; -} - -inline float b2Fixture::GetDensity() const -{ - return m_density; -} - -inline float b2Fixture::GetFriction() const -{ - return m_friction; -} - -inline void b2Fixture::SetFriction(float friction) -{ - m_friction = friction; -} - -inline float b2Fixture::GetRestitution() const -{ - return m_restitution; -} - -inline void b2Fixture::SetRestitution(float restitution) -{ - m_restitution = restitution; -} - -inline float b2Fixture::GetRestitutionThreshold() const -{ - return m_restitutionThreshold; -} - -inline void b2Fixture::SetRestitutionThreshold(float threshold) -{ - m_restitutionThreshold = threshold; -} - -inline bool b2Fixture::TestPoint(const b2Vec2& p) const -{ - return m_shape->TestPoint(m_body->GetTransform(), p); -} - -inline bool b2Fixture::RayCast(b2RayCastOutput* output, const b2RayCastInput& input, int32 childIndex) const -{ - return m_shape->RayCast(output, input, m_body->GetTransform(), childIndex); -} - -inline void b2Fixture::GetMassData(b2MassData* massData) const -{ - m_shape->ComputeMass(massData, m_density); -} - -inline const b2AABB& b2Fixture::GetAABB(int32 childIndex) const -{ - b2Assert(0 <= childIndex && childIndex < m_proxyCount); - return m_proxies[childIndex].aabb; -} - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_friction_joint.h b/3rdparty/box2d/include/box2d/b2_friction_joint.h deleted file mode 100644 index 99922c3a375f..000000000000 --- a/3rdparty/box2d/include/box2d/b2_friction_joint.h +++ /dev/null @@ -1,124 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_FRICTION_JOINT_H -#define B2_FRICTION_JOINT_H - -#include "b2_api.h" -#include "b2_joint.h" - -/// Friction joint definition. -struct B2_API b2FrictionJointDef : public b2JointDef -{ - b2FrictionJointDef() - { - type = e_frictionJoint; - localAnchorA.SetZero(); - localAnchorB.SetZero(); - maxForce = 0.0f; - maxTorque = 0.0f; - } - - /// Initialize the bodies, anchors, axis, and reference angle using the world - /// anchor and world axis. - void Initialize(b2Body* bodyA, b2Body* bodyB, const b2Vec2& anchor); - - /// The local anchor point relative to bodyA's origin. - b2Vec2 localAnchorA; - - /// The local anchor point relative to bodyB's origin. - b2Vec2 localAnchorB; - - /// The maximum friction force in N. - float maxForce; - - /// The maximum friction torque in N-m. - float maxTorque; -}; - -/// Friction joint. This is used for top-down friction. -/// It provides 2D translational friction and angular friction. -class B2_API b2FrictionJoint : public b2Joint -{ -public: - b2Vec2 GetAnchorA() const override; - b2Vec2 GetAnchorB() const override; - - b2Vec2 GetReactionForce(float inv_dt) const override; - float GetReactionTorque(float inv_dt) const override; - - /// The local anchor point relative to bodyA's origin. - const b2Vec2& GetLocalAnchorA() const { return m_localAnchorA; } - - /// The local anchor point relative to bodyB's origin. - const b2Vec2& GetLocalAnchorB() const { return m_localAnchorB; } - - /// Set the maximum friction force in N. - void SetMaxForce(float force); - - /// Get the maximum friction force in N. - float GetMaxForce() const; - - /// Set the maximum friction torque in N*m. - void SetMaxTorque(float torque); - - /// Get the maximum friction torque in N*m. - float GetMaxTorque() const; - - /// Dump joint to dmLog - void Dump() override; - -protected: - - friend class b2Joint; - - b2FrictionJoint(const b2FrictionJointDef* def); - - void InitVelocityConstraints(const b2SolverData& data) override; - void SolveVelocityConstraints(const b2SolverData& data) override; - bool SolvePositionConstraints(const b2SolverData& data) override; - - b2Vec2 m_localAnchorA; - b2Vec2 m_localAnchorB; - - // Solver shared - b2Vec2 m_linearImpulse; - float m_angularImpulse; - float m_maxForce; - float m_maxTorque; - - // Solver temp - int32 m_indexA; - int32 m_indexB; - b2Vec2 m_rA; - b2Vec2 m_rB; - b2Vec2 m_localCenterA; - b2Vec2 m_localCenterB; - float m_invMassA; - float m_invMassB; - float m_invIA; - float m_invIB; - b2Mat22 m_linearMass; - float m_angularMass; -}; - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_gear_joint.h b/3rdparty/box2d/include/box2d/b2_gear_joint.h deleted file mode 100644 index a7e4fa5197d7..000000000000 --- a/3rdparty/box2d/include/box2d/b2_gear_joint.h +++ /dev/null @@ -1,131 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_GEAR_JOINT_H -#define B2_GEAR_JOINT_H - -#include "b2_joint.h" - -/// Gear joint definition. This definition requires two existing -/// revolute or prismatic joints (any combination will work). -/// @warning bodyB on the input joints must both be dynamic -struct B2_API b2GearJointDef : public b2JointDef -{ - b2GearJointDef() - { - type = e_gearJoint; - joint1 = nullptr; - joint2 = nullptr; - ratio = 1.0f; - } - - /// The first revolute/prismatic joint attached to the gear joint. - b2Joint* joint1; - - /// The second revolute/prismatic joint attached to the gear joint. - b2Joint* joint2; - - /// The gear ratio. - /// @see b2GearJoint for explanation. - float ratio; -}; - -/// A gear joint is used to connect two joints together. Either joint -/// can be a revolute or prismatic joint. You specify a gear ratio -/// to bind the motions together: -/// coordinate1 + ratio * coordinate2 = constant -/// The ratio can be negative or positive. If one joint is a revolute joint -/// and the other joint is a prismatic joint, then the ratio will have units -/// of length or units of 1/length. -/// @warning You have to manually destroy the gear joint if joint1 or joint2 -/// is destroyed. -class B2_API b2GearJoint : public b2Joint -{ -public: - b2Vec2 GetAnchorA() const override; - b2Vec2 GetAnchorB() const override; - - b2Vec2 GetReactionForce(float inv_dt) const override; - float GetReactionTorque(float inv_dt) const override; - - /// Get the first joint. - b2Joint* GetJoint1() { return m_joint1; } - - /// Get the second joint. - b2Joint* GetJoint2() { return m_joint2; } - - /// Set/Get the gear ratio. - void SetRatio(float ratio); - float GetRatio() const; - - /// Dump joint to dmLog - void Dump() override; - -protected: - - friend class b2Joint; - b2GearJoint(const b2GearJointDef* data); - - void InitVelocityConstraints(const b2SolverData& data) override; - void SolveVelocityConstraints(const b2SolverData& data) override; - bool SolvePositionConstraints(const b2SolverData& data) override; - - b2Joint* m_joint1; - b2Joint* m_joint2; - - b2JointType m_typeA; - b2JointType m_typeB; - - // Body A is connected to body C - // Body B is connected to body D - b2Body* m_bodyC; - b2Body* m_bodyD; - - // Solver shared - b2Vec2 m_localAnchorA; - b2Vec2 m_localAnchorB; - b2Vec2 m_localAnchorC; - b2Vec2 m_localAnchorD; - - b2Vec2 m_localAxisC; - b2Vec2 m_localAxisD; - - float m_referenceAngleA; - float m_referenceAngleB; - - float m_constant; - float m_ratio; - float m_tolerance; - - float m_impulse; - - // Solver temp - int32 m_indexA, m_indexB, m_indexC, m_indexD; - b2Vec2 m_lcA, m_lcB, m_lcC, m_lcD; - float m_mA, m_mB, m_mC, m_mD; - float m_iA, m_iB, m_iC, m_iD; - b2Vec2 m_JvAC, m_JvBD; - float m_JwA, m_JwB, m_JwC, m_JwD; - float m_mass; -}; - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_growable_stack.h b/3rdparty/box2d/include/box2d/b2_growable_stack.h deleted file mode 100644 index ec42e5e9e2d7..000000000000 --- a/3rdparty/box2d/include/box2d/b2_growable_stack.h +++ /dev/null @@ -1,91 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_GROWABLE_STACK_H -#define B2_GROWABLE_STACK_H - -#include - -#include "b2_settings.h" - -/// This is a growable LIFO stack with an initial capacity of N. -/// If the stack size exceeds the initial capacity, the heap is used -/// to increase the size of the stack. -template -class b2GrowableStack -{ -public: - b2GrowableStack() - { - m_stack = m_array; - m_count = 0; - m_capacity = N; - } - - ~b2GrowableStack() - { - if (m_stack != m_array) - { - b2Free(m_stack); - m_stack = nullptr; - } - } - - void Push(const T& element) - { - if (m_count == m_capacity) - { - T* old = m_stack; - m_capacity *= 2; - m_stack = (T*)b2Alloc(m_capacity * sizeof(T)); - memcpy(m_stack, old, m_count * sizeof(T)); - if (old != m_array) - { - b2Free(old); - } - } - - m_stack[m_count] = element; - ++m_count; - } - - T Pop() - { - b2Assert(m_count > 0); - --m_count; - return m_stack[m_count]; - } - - int32 GetCount() - { - return m_count; - } - -private: - T* m_stack; - T m_array[N]; - int32 m_count; - int32 m_capacity; -}; - - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_joint.h b/3rdparty/box2d/include/box2d/b2_joint.h deleted file mode 100644 index 586b13ad1866..000000000000 --- a/3rdparty/box2d/include/box2d/b2_joint.h +++ /dev/null @@ -1,233 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_JOINT_H -#define B2_JOINT_H - -#include "b2_api.h" -#include "b2_math.h" - -class b2Body; -class b2Draw; -class b2Joint; -struct b2SolverData; -class b2BlockAllocator; - -enum b2JointType -{ - e_unknownJoint, - e_revoluteJoint, - e_prismaticJoint, - e_distanceJoint, - e_pulleyJoint, - e_mouseJoint, - e_gearJoint, - e_wheelJoint, - e_weldJoint, - e_frictionJoint, - e_motorJoint -}; - -struct B2_API b2Jacobian -{ - b2Vec2 linear; - float angularA; - float angularB; -}; - -/// A joint edge is used to connect bodies and joints together -/// in a joint graph where each body is a node and each joint -/// is an edge. A joint edge belongs to a doubly linked list -/// maintained in each attached body. Each joint has two joint -/// nodes, one for each attached body. -struct B2_API b2JointEdge -{ - b2Body* other; ///< provides quick access to the other body attached. - b2Joint* joint; ///< the joint - b2JointEdge* prev; ///< the previous joint edge in the body's joint list - b2JointEdge* next; ///< the next joint edge in the body's joint list -}; - -/// Joint definitions are used to construct joints. -struct B2_API b2JointDef -{ - b2JointDef() - { - type = e_unknownJoint; - bodyA = nullptr; - bodyB = nullptr; - collideConnected = false; - } - - /// The joint type is set automatically for concrete joint types. - b2JointType type; - - /// Use this to attach application specific data to your joints. - b2JointUserData userData; - - /// The first attached body. - b2Body* bodyA; - - /// The second attached body. - b2Body* bodyB; - - /// Set this flag to true if the attached bodies should collide. - bool collideConnected; -}; - -/// Utility to compute linear stiffness values from frequency and damping ratio -B2_API void b2LinearStiffness(float& stiffness, float& damping, - float frequencyHertz, float dampingRatio, - const b2Body* bodyA, const b2Body* bodyB); - -/// Utility to compute rotational stiffness values frequency and damping ratio -B2_API void b2AngularStiffness(float& stiffness, float& damping, - float frequencyHertz, float dampingRatio, - const b2Body* bodyA, const b2Body* bodyB); - -/// The base joint class. Joints are used to constraint two bodies together in -/// various fashions. Some joints also feature limits and motors. -class B2_API b2Joint -{ -public: - - /// Get the type of the concrete joint. - b2JointType GetType() const; - - /// Get the first body attached to this joint. - b2Body* GetBodyA(); - - /// Get the second body attached to this joint. - b2Body* GetBodyB(); - - /// Get the anchor point on bodyA in world coordinates. - virtual b2Vec2 GetAnchorA() const = 0; - - /// Get the anchor point on bodyB in world coordinates. - virtual b2Vec2 GetAnchorB() const = 0; - - /// Get the reaction force on bodyB at the joint anchor in Newtons. - virtual b2Vec2 GetReactionForce(float inv_dt) const = 0; - - /// Get the reaction torque on bodyB in N*m. - virtual float GetReactionTorque(float inv_dt) const = 0; - - /// Get the next joint the world joint list. - b2Joint* GetNext(); - const b2Joint* GetNext() const; - - /// Get the user data pointer. - b2JointUserData& GetUserData(); - const b2JointUserData& GetUserData() const; - - /// Short-cut function to determine if either body is enabled. - bool IsEnabled() const; - - /// Get collide connected. - /// Note: modifying the collide connect flag won't work correctly because - /// the flag is only checked when fixture AABBs begin to overlap. - bool GetCollideConnected() const; - - /// Dump this joint to the log file. - virtual void Dump() { b2Dump("// Dump is not supported for this joint type.\n"); } - - /// Shift the origin for any points stored in world coordinates. - virtual void ShiftOrigin(const b2Vec2& newOrigin) { B2_NOT_USED(newOrigin); } - - /// Debug draw this joint - virtual void Draw(b2Draw* draw) const; - -protected: - friend class b2World; - friend class b2Body; - friend class b2Island; - friend class b2GearJoint; - - static b2Joint* Create(const b2JointDef* def, b2BlockAllocator* allocator); - static void Destroy(b2Joint* joint, b2BlockAllocator* allocator); - - b2Joint(const b2JointDef* def); - virtual ~b2Joint() {} - - virtual void InitVelocityConstraints(const b2SolverData& data) = 0; - virtual void SolveVelocityConstraints(const b2SolverData& data) = 0; - - // This returns true if the position errors are within tolerance. - virtual bool SolvePositionConstraints(const b2SolverData& data) = 0; - - b2JointType m_type; - b2Joint* m_prev; - b2Joint* m_next; - b2JointEdge m_edgeA; - b2JointEdge m_edgeB; - b2Body* m_bodyA; - b2Body* m_bodyB; - - int32 m_index; - - bool m_islandFlag; - bool m_collideConnected; - - b2JointUserData m_userData; -}; - -inline b2JointType b2Joint::GetType() const -{ - return m_type; -} - -inline b2Body* b2Joint::GetBodyA() -{ - return m_bodyA; -} - -inline b2Body* b2Joint::GetBodyB() -{ - return m_bodyB; -} - -inline b2Joint* b2Joint::GetNext() -{ - return m_next; -} - -inline const b2Joint* b2Joint::GetNext() const -{ - return m_next; -} - -inline b2JointUserData& b2Joint::GetUserData() -{ - return m_userData; -} - -inline const b2JointUserData& b2Joint::GetUserData() const -{ - return m_userData; -} - -inline bool b2Joint::GetCollideConnected() const -{ - return m_collideConnected; -} - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_math.h b/3rdparty/box2d/include/box2d/b2_math.h deleted file mode 100644 index c98cf35dfcb2..000000000000 --- a/3rdparty/box2d/include/box2d/b2_math.h +++ /dev/null @@ -1,717 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_MATH_H -#define B2_MATH_H - -#include - -#include "b2_api.h" -#include "b2_settings.h" - -/// This function is used to ensure that a floating point number is not a NaN or infinity. -inline bool b2IsValid(float x) -{ - return isfinite(x); -} - -#define b2Sqrt(x) sqrtf(x) -#define b2Atan2(y, x) atan2f(y, x) - -/// A 2D column vector. -struct B2_API b2Vec2 -{ - /// Default constructor does nothing (for performance). - b2Vec2() = default; - - /// Construct using coordinates. - b2Vec2(float xIn, float yIn) : x(xIn), y(yIn) {} - - /// Set this vector to all zeros. - void SetZero() { x = 0.0f; y = 0.0f; } - - /// Set this vector to some specified coordinates. - void Set(float x_, float y_) { x = x_; y = y_; } - - /// Negate this vector. - b2Vec2 operator -() const { b2Vec2 v; v.Set(-x, -y); return v; } - - /// Read from and indexed element. - float operator () (int32 i) const - { - return (&x)[i]; - } - - /// Write to an indexed element. - float& operator () (int32 i) - { - return (&x)[i]; - } - - /// Add a vector to this vector. - void operator += (const b2Vec2& v) - { - x += v.x; y += v.y; - } - - /// Subtract a vector from this vector. - void operator -= (const b2Vec2& v) - { - x -= v.x; y -= v.y; - } - - /// Multiply this vector by a scalar. - void operator *= (float a) - { - x *= a; y *= a; - } - - /// Get the length of this vector (the norm). - float Length() const - { - return b2Sqrt(x * x + y * y); - } - - /// Get the length squared. For performance, use this instead of - /// b2Vec2::Length (if possible). - float LengthSquared() const - { - return x * x + y * y; - } - - /// Convert this vector into a unit vector. Returns the length. - float Normalize() - { - float length = Length(); - if (length < b2_epsilon) - { - return 0.0f; - } - float invLength = 1.0f / length; - x *= invLength; - y *= invLength; - - return length; - } - - /// Does this vector contain finite coordinates? - bool IsValid() const - { - return b2IsValid(x) && b2IsValid(y); - } - - /// Get the skew vector such that dot(skew_vec, other) == cross(vec, other) - b2Vec2 Skew() const - { - return b2Vec2(-y, x); - } - - float x, y; -}; - -/// A 2D column vector with 3 elements. -struct B2_API b2Vec3 -{ - /// Default constructor does nothing (for performance). - b2Vec3() = default; - - /// Construct using coordinates. - b2Vec3(float xIn, float yIn, float zIn) : x(xIn), y(yIn), z(zIn) {} - - /// Set this vector to all zeros. - void SetZero() { x = 0.0f; y = 0.0f; z = 0.0f; } - - /// Set this vector to some specified coordinates. - void Set(float x_, float y_, float z_) { x = x_; y = y_; z = z_; } - - /// Negate this vector. - b2Vec3 operator -() const { b2Vec3 v; v.Set(-x, -y, -z); return v; } - - /// Add a vector to this vector. - void operator += (const b2Vec3& v) - { - x += v.x; y += v.y; z += v.z; - } - - /// Subtract a vector from this vector. - void operator -= (const b2Vec3& v) - { - x -= v.x; y -= v.y; z -= v.z; - } - - /// Multiply this vector by a scalar. - void operator *= (float s) - { - x *= s; y *= s; z *= s; - } - - float x, y, z; -}; - -/// A 2-by-2 matrix. Stored in column-major order. -struct B2_API b2Mat22 -{ - /// The default constructor does nothing (for performance). - b2Mat22() = default; - - /// Construct this matrix using columns. - b2Mat22(const b2Vec2& c1, const b2Vec2& c2) - { - ex = c1; - ey = c2; - } - - /// Construct this matrix using scalars. - b2Mat22(float a11, float a12, float a21, float a22) - { - ex.x = a11; ex.y = a21; - ey.x = a12; ey.y = a22; - } - - /// Initialize this matrix using columns. - void Set(const b2Vec2& c1, const b2Vec2& c2) - { - ex = c1; - ey = c2; - } - - /// Set this to the identity matrix. - void SetIdentity() - { - ex.x = 1.0f; ey.x = 0.0f; - ex.y = 0.0f; ey.y = 1.0f; - } - - /// Set this matrix to all zeros. - void SetZero() - { - ex.x = 0.0f; ey.x = 0.0f; - ex.y = 0.0f; ey.y = 0.0f; - } - - b2Mat22 GetInverse() const - { - float a = ex.x, b = ey.x, c = ex.y, d = ey.y; - b2Mat22 B; - float det = a * d - b * c; - if (det != 0.0f) - { - det = 1.0f / det; - } - B.ex.x = det * d; B.ey.x = -det * b; - B.ex.y = -det * c; B.ey.y = det * a; - return B; - } - - /// Solve A * x = b, where b is a column vector. This is more efficient - /// than computing the inverse in one-shot cases. - b2Vec2 Solve(const b2Vec2& b) const - { - float a11 = ex.x, a12 = ey.x, a21 = ex.y, a22 = ey.y; - float det = a11 * a22 - a12 * a21; - if (det != 0.0f) - { - det = 1.0f / det; - } - b2Vec2 x; - x.x = det * (a22 * b.x - a12 * b.y); - x.y = det * (a11 * b.y - a21 * b.x); - return x; - } - - b2Vec2 ex, ey; -}; - -/// A 3-by-3 matrix. Stored in column-major order. -struct B2_API b2Mat33 -{ - /// The default constructor does nothing (for performance). - b2Mat33() = default; - - /// Construct this matrix using columns. - b2Mat33(const b2Vec3& c1, const b2Vec3& c2, const b2Vec3& c3) - { - ex = c1; - ey = c2; - ez = c3; - } - - /// Set this matrix to all zeros. - void SetZero() - { - ex.SetZero(); - ey.SetZero(); - ez.SetZero(); - } - - /// Solve A * x = b, where b is a column vector. This is more efficient - /// than computing the inverse in one-shot cases. - b2Vec3 Solve33(const b2Vec3& b) const; - - /// Solve A * x = b, where b is a column vector. This is more efficient - /// than computing the inverse in one-shot cases. Solve only the upper - /// 2-by-2 matrix equation. - b2Vec2 Solve22(const b2Vec2& b) const; - - /// Get the inverse of this matrix as a 2-by-2. - /// Returns the zero matrix if singular. - void GetInverse22(b2Mat33* M) const; - - /// Get the symmetric inverse of this matrix as a 3-by-3. - /// Returns the zero matrix if singular. - void GetSymInverse33(b2Mat33* M) const; - - b2Vec3 ex, ey, ez; -}; - -/// Rotation -struct B2_API b2Rot -{ - b2Rot() = default; - - /// Initialize from an angle in radians - explicit b2Rot(float angle) - { - /// TODO_ERIN optimize - s = sinf(angle); - c = cosf(angle); - } - - /// Set using an angle in radians. - void Set(float angle) - { - /// TODO_ERIN optimize - s = sinf(angle); - c = cosf(angle); - } - - /// Set to the identity rotation - void SetIdentity() - { - s = 0.0f; - c = 1.0f; - } - - /// Get the angle in radians - float GetAngle() const - { - return b2Atan2(s, c); - } - - /// Get the x-axis - b2Vec2 GetXAxis() const - { - return b2Vec2(c, s); - } - - /// Get the u-axis - b2Vec2 GetYAxis() const - { - return b2Vec2(-s, c); - } - - /// Sine and cosine - float s, c; -}; - -/// A transform contains translation and rotation. It is used to represent -/// the position and orientation of rigid frames. -struct B2_API b2Transform -{ - /// The default constructor does nothing. - b2Transform() = default; - - /// Initialize using a position vector and a rotation. - b2Transform(const b2Vec2& position, const b2Rot& rotation) : p(position), q(rotation) {} - - /// Set this to the identity transform. - void SetIdentity() - { - p.SetZero(); - q.SetIdentity(); - } - - /// Set this based on the position and angle. - void Set(const b2Vec2& position, float angle) - { - p = position; - q.Set(angle); - } - - b2Vec2 p; - b2Rot q; -}; - -/// This describes the motion of a body/shape for TOI computation. -/// Shapes are defined with respect to the body origin, which may -/// no coincide with the center of mass. However, to support dynamics -/// we must interpolate the center of mass position. -struct B2_API b2Sweep -{ - b2Sweep() = default; - - /// Get the interpolated transform at a specific time. - /// @param transform the output transform - /// @param beta is a factor in [0,1], where 0 indicates alpha0. - void GetTransform(b2Transform* transform, float beta) const; - - /// Advance the sweep forward, yielding a new initial state. - /// @param alpha the new initial time. - void Advance(float alpha); - - /// Normalize the angles. - void Normalize(); - - b2Vec2 localCenter; ///< local center of mass position - b2Vec2 c0, c; ///< center world positions - float a0, a; ///< world angles - - /// Fraction of the current time step in the range [0,1] - /// c0 and a0 are the positions at alpha0. - float alpha0; -}; - -/// Useful constant -extern B2_API const b2Vec2 b2Vec2_zero; - -/// Perform the dot product on two vectors. -inline float b2Dot(const b2Vec2& a, const b2Vec2& b) -{ - return a.x * b.x + a.y * b.y; -} - -/// Perform the cross product on two vectors. In 2D this produces a scalar. -inline float b2Cross(const b2Vec2& a, const b2Vec2& b) -{ - return a.x * b.y - a.y * b.x; -} - -/// Perform the cross product on a vector and a scalar. In 2D this produces -/// a vector. -inline b2Vec2 b2Cross(const b2Vec2& a, float s) -{ - return b2Vec2(s * a.y, -s * a.x); -} - -/// Perform the cross product on a scalar and a vector. In 2D this produces -/// a vector. -inline b2Vec2 b2Cross(float s, const b2Vec2& a) -{ - return b2Vec2(-s * a.y, s * a.x); -} - -/// Multiply a matrix times a vector. If a rotation matrix is provided, -/// then this transforms the vector from one frame to another. -inline b2Vec2 b2Mul(const b2Mat22& A, const b2Vec2& v) -{ - return b2Vec2(A.ex.x * v.x + A.ey.x * v.y, A.ex.y * v.x + A.ey.y * v.y); -} - -/// Multiply a matrix transpose times a vector. If a rotation matrix is provided, -/// then this transforms the vector from one frame to another (inverse transform). -inline b2Vec2 b2MulT(const b2Mat22& A, const b2Vec2& v) -{ - return b2Vec2(b2Dot(v, A.ex), b2Dot(v, A.ey)); -} - -/// Add two vectors component-wise. -inline b2Vec2 operator + (const b2Vec2& a, const b2Vec2& b) -{ - return b2Vec2(a.x + b.x, a.y + b.y); -} - -/// Subtract two vectors component-wise. -inline b2Vec2 operator - (const b2Vec2& a, const b2Vec2& b) -{ - return b2Vec2(a.x - b.x, a.y - b.y); -} - -inline b2Vec2 operator * (float s, const b2Vec2& a) -{ - return b2Vec2(s * a.x, s * a.y); -} - -inline bool operator == (const b2Vec2& a, const b2Vec2& b) -{ - return a.x == b.x && a.y == b.y; -} - -inline bool operator != (const b2Vec2& a, const b2Vec2& b) -{ - return a.x != b.x || a.y != b.y; -} - -inline float b2Distance(const b2Vec2& a, const b2Vec2& b) -{ - b2Vec2 c = a - b; - return c.Length(); -} - -inline float b2DistanceSquared(const b2Vec2& a, const b2Vec2& b) -{ - b2Vec2 c = a - b; - return b2Dot(c, c); -} - -inline b2Vec3 operator * (float s, const b2Vec3& a) -{ - return b2Vec3(s * a.x, s * a.y, s * a.z); -} - -/// Add two vectors component-wise. -inline b2Vec3 operator + (const b2Vec3& a, const b2Vec3& b) -{ - return b2Vec3(a.x + b.x, a.y + b.y, a.z + b.z); -} - -/// Subtract two vectors component-wise. -inline b2Vec3 operator - (const b2Vec3& a, const b2Vec3& b) -{ - return b2Vec3(a.x - b.x, a.y - b.y, a.z - b.z); -} - -/// Perform the dot product on two vectors. -inline float b2Dot(const b2Vec3& a, const b2Vec3& b) -{ - return a.x * b.x + a.y * b.y + a.z * b.z; -} - -/// Perform the cross product on two vectors. -inline b2Vec3 b2Cross(const b2Vec3& a, const b2Vec3& b) -{ - return b2Vec3(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x); -} - -inline b2Mat22 operator + (const b2Mat22& A, const b2Mat22& B) -{ - return b2Mat22(A.ex + B.ex, A.ey + B.ey); -} - -// A * B -inline b2Mat22 b2Mul(const b2Mat22& A, const b2Mat22& B) -{ - return b2Mat22(b2Mul(A, B.ex), b2Mul(A, B.ey)); -} - -// A^T * B -inline b2Mat22 b2MulT(const b2Mat22& A, const b2Mat22& B) -{ - b2Vec2 c1(b2Dot(A.ex, B.ex), b2Dot(A.ey, B.ex)); - b2Vec2 c2(b2Dot(A.ex, B.ey), b2Dot(A.ey, B.ey)); - return b2Mat22(c1, c2); -} - -/// Multiply a matrix times a vector. -inline b2Vec3 b2Mul(const b2Mat33& A, const b2Vec3& v) -{ - return v.x * A.ex + v.y * A.ey + v.z * A.ez; -} - -/// Multiply a matrix times a vector. -inline b2Vec2 b2Mul22(const b2Mat33& A, const b2Vec2& v) -{ - return b2Vec2(A.ex.x * v.x + A.ey.x * v.y, A.ex.y * v.x + A.ey.y * v.y); -} - -/// Multiply two rotations: q * r -inline b2Rot b2Mul(const b2Rot& q, const b2Rot& r) -{ - // [qc -qs] * [rc -rs] = [qc*rc-qs*rs -qc*rs-qs*rc] - // [qs qc] [rs rc] [qs*rc+qc*rs -qs*rs+qc*rc] - // s = qs * rc + qc * rs - // c = qc * rc - qs * rs - b2Rot qr; - qr.s = q.s * r.c + q.c * r.s; - qr.c = q.c * r.c - q.s * r.s; - return qr; -} - -/// Transpose multiply two rotations: qT * r -inline b2Rot b2MulT(const b2Rot& q, const b2Rot& r) -{ - // [ qc qs] * [rc -rs] = [qc*rc+qs*rs -qc*rs+qs*rc] - // [-qs qc] [rs rc] [-qs*rc+qc*rs qs*rs+qc*rc] - // s = qc * rs - qs * rc - // c = qc * rc + qs * rs - b2Rot qr; - qr.s = q.c * r.s - q.s * r.c; - qr.c = q.c * r.c + q.s * r.s; - return qr; -} - -/// Rotate a vector -inline b2Vec2 b2Mul(const b2Rot& q, const b2Vec2& v) -{ - return b2Vec2(q.c * v.x - q.s * v.y, q.s * v.x + q.c * v.y); -} - -/// Inverse rotate a vector -inline b2Vec2 b2MulT(const b2Rot& q, const b2Vec2& v) -{ - return b2Vec2(q.c * v.x + q.s * v.y, -q.s * v.x + q.c * v.y); -} - -inline b2Vec2 b2Mul(const b2Transform& T, const b2Vec2& v) -{ - float x = (T.q.c * v.x - T.q.s * v.y) + T.p.x; - float y = (T.q.s * v.x + T.q.c * v.y) + T.p.y; - - return b2Vec2(x, y); -} - -inline b2Vec2 b2MulT(const b2Transform& T, const b2Vec2& v) -{ - float px = v.x - T.p.x; - float py = v.y - T.p.y; - float x = (T.q.c * px + T.q.s * py); - float y = (-T.q.s * px + T.q.c * py); - - return b2Vec2(x, y); -} - -// v2 = A.q.Rot(B.q.Rot(v1) + B.p) + A.p -// = (A.q * B.q).Rot(v1) + A.q.Rot(B.p) + A.p -inline b2Transform b2Mul(const b2Transform& A, const b2Transform& B) -{ - b2Transform C; - C.q = b2Mul(A.q, B.q); - C.p = b2Mul(A.q, B.p) + A.p; - return C; -} - -// v2 = A.q' * (B.q * v1 + B.p - A.p) -// = A.q' * B.q * v1 + A.q' * (B.p - A.p) -inline b2Transform b2MulT(const b2Transform& A, const b2Transform& B) -{ - b2Transform C; - C.q = b2MulT(A.q, B.q); - C.p = b2MulT(A.q, B.p - A.p); - return C; -} - -template -inline T b2Abs(T a) -{ - return a > T(0) ? a : -a; -} - -inline b2Vec2 b2Abs(const b2Vec2& a) -{ - return b2Vec2(b2Abs(a.x), b2Abs(a.y)); -} - -inline b2Mat22 b2Abs(const b2Mat22& A) -{ - return b2Mat22(b2Abs(A.ex), b2Abs(A.ey)); -} - -template -inline T b2Min(T a, T b) -{ - return a < b ? a : b; -} - -inline b2Vec2 b2Min(const b2Vec2& a, const b2Vec2& b) -{ - return b2Vec2(b2Min(a.x, b.x), b2Min(a.y, b.y)); -} - -template -inline T b2Max(T a, T b) -{ - return a > b ? a : b; -} - -inline b2Vec2 b2Max(const b2Vec2& a, const b2Vec2& b) -{ - return b2Vec2(b2Max(a.x, b.x), b2Max(a.y, b.y)); -} - -template -inline T b2Clamp(T a, T low, T high) -{ - return b2Max(low, b2Min(a, high)); -} - -inline b2Vec2 b2Clamp(const b2Vec2& a, const b2Vec2& low, const b2Vec2& high) -{ - return b2Max(low, b2Min(a, high)); -} - -template inline void b2Swap(T& a, T& b) -{ - T tmp = a; - a = b; - b = tmp; -} - -/// "Next Largest Power of 2 -/// Given a binary integer value x, the next largest power of 2 can be computed by a SWAR algorithm -/// that recursively "folds" the upper bits into the lower bits. This process yields a bit vector with -/// the same most significant 1 as x, but all 1's below it. Adding 1 to that value yields the next -/// largest power of 2. For a 32-bit value:" -inline uint32 b2NextPowerOfTwo(uint32 x) -{ - x |= (x >> 1); - x |= (x >> 2); - x |= (x >> 4); - x |= (x >> 8); - x |= (x >> 16); - return x + 1; -} - -inline bool b2IsPowerOfTwo(uint32 x) -{ - bool result = x > 0 && (x & (x - 1)) == 0; - return result; -} - -// https://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/ -inline void b2Sweep::GetTransform(b2Transform* xf, float beta) const -{ - xf->p = (1.0f - beta) * c0 + beta * c; - float angle = (1.0f - beta) * a0 + beta * a; - xf->q.Set(angle); - - // Shift to origin - xf->p -= b2Mul(xf->q, localCenter); -} - -inline void b2Sweep::Advance(float alpha) -{ - b2Assert(alpha0 < 1.0f); - float beta = (alpha - alpha0) / (1.0f - alpha0); - c0 += beta * (c - c0); - a0 += beta * (a - a0); - alpha0 = alpha; -} - -/// Normalize an angle in radians to be between -pi and pi -inline void b2Sweep::Normalize() -{ - float twoPi = 2.0f * b2_pi; - float d = twoPi * floorf(a0 / twoPi); - a0 -= d; - a -= d; -} - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_motor_joint.h b/3rdparty/box2d/include/box2d/b2_motor_joint.h deleted file mode 100644 index c88115f20b48..000000000000 --- a/3rdparty/box2d/include/box2d/b2_motor_joint.h +++ /dev/null @@ -1,138 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_MOTOR_JOINT_H -#define B2_MOTOR_JOINT_H - -#include "b2_api.h" -#include "b2_joint.h" - -/// Motor joint definition. -struct B2_API b2MotorJointDef : public b2JointDef -{ - b2MotorJointDef() - { - type = e_motorJoint; - linearOffset.SetZero(); - angularOffset = 0.0f; - maxForce = 1.0f; - maxTorque = 1.0f; - correctionFactor = 0.3f; - } - - /// Initialize the bodies and offsets using the current transforms. - void Initialize(b2Body* bodyA, b2Body* bodyB); - - /// Position of bodyB minus the position of bodyA, in bodyA's frame, in meters. - b2Vec2 linearOffset; - - /// The bodyB angle minus bodyA angle in radians. - float angularOffset; - - /// The maximum motor force in N. - float maxForce; - - /// The maximum motor torque in N-m. - float maxTorque; - - /// Position correction factor in the range [0,1]. - float correctionFactor; -}; - -/// A motor joint is used to control the relative motion -/// between two bodies. A typical usage is to control the movement -/// of a dynamic body with respect to the ground. -class B2_API b2MotorJoint : public b2Joint -{ -public: - b2Vec2 GetAnchorA() const override; - b2Vec2 GetAnchorB() const override; - - b2Vec2 GetReactionForce(float inv_dt) const override; - float GetReactionTorque(float inv_dt) const override; - - /// Set/get the target linear offset, in frame A, in meters. - void SetLinearOffset(const b2Vec2& linearOffset); - const b2Vec2& GetLinearOffset() const; - - /// Set/get the target angular offset, in radians. - void SetAngularOffset(float angularOffset); - float GetAngularOffset() const; - - /// Set the maximum friction force in N. - void SetMaxForce(float force); - - /// Get the maximum friction force in N. - float GetMaxForce() const; - - /// Set the maximum friction torque in N*m. - void SetMaxTorque(float torque); - - /// Get the maximum friction torque in N*m. - float GetMaxTorque() const; - - /// Set the position correction factor in the range [0,1]. - void SetCorrectionFactor(float factor); - - /// Get the position correction factor in the range [0,1]. - float GetCorrectionFactor() const; - - /// Dump to b2Log - void Dump() override; - -protected: - - friend class b2Joint; - - b2MotorJoint(const b2MotorJointDef* def); - - void InitVelocityConstraints(const b2SolverData& data) override; - void SolveVelocityConstraints(const b2SolverData& data) override; - bool SolvePositionConstraints(const b2SolverData& data) override; - - // Solver shared - b2Vec2 m_linearOffset; - float m_angularOffset; - b2Vec2 m_linearImpulse; - float m_angularImpulse; - float m_maxForce; - float m_maxTorque; - float m_correctionFactor; - - // Solver temp - int32 m_indexA; - int32 m_indexB; - b2Vec2 m_rA; - b2Vec2 m_rB; - b2Vec2 m_localCenterA; - b2Vec2 m_localCenterB; - b2Vec2 m_linearError; - float m_angularError; - float m_invMassA; - float m_invMassB; - float m_invIA; - float m_invIB; - b2Mat22 m_linearMass; - float m_angularMass; -}; - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_mouse_joint.h b/3rdparty/box2d/include/box2d/b2_mouse_joint.h deleted file mode 100644 index fcbc56a70ce3..000000000000 --- a/3rdparty/box2d/include/box2d/b2_mouse_joint.h +++ /dev/null @@ -1,134 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_MOUSE_JOINT_H -#define B2_MOUSE_JOINT_H - -#include "b2_api.h" -#include "b2_joint.h" - -/// Mouse joint definition. This requires a world target point, -/// tuning parameters, and the time step. -struct B2_API b2MouseJointDef : public b2JointDef -{ - b2MouseJointDef() - { - type = e_mouseJoint; - target.Set(0.0f, 0.0f); - maxForce = 0.0f; - stiffness = 0.0f; - damping = 0.0f; - } - - /// The initial world target point. This is assumed - /// to coincide with the body anchor initially. - b2Vec2 target; - - /// The maximum constraint force that can be exerted - /// to move the candidate body. Usually you will express - /// as some multiple of the weight (multiplier * mass * gravity). - float maxForce; - - /// The linear stiffness in N/m - float stiffness; - - /// The linear damping in N*s/m - float damping; -}; - -/// A mouse joint is used to make a point on a body track a -/// specified world point. This a soft constraint with a maximum -/// force. This allows the constraint to stretch and without -/// applying huge forces. -/// NOTE: this joint is not documented in the manual because it was -/// developed to be used in the testbed. If you want to learn how to -/// use the mouse joint, look at the testbed. -class B2_API b2MouseJoint : public b2Joint -{ -public: - - /// Implements b2Joint. - b2Vec2 GetAnchorA() const override; - - /// Implements b2Joint. - b2Vec2 GetAnchorB() const override; - - /// Implements b2Joint. - b2Vec2 GetReactionForce(float inv_dt) const override; - - /// Implements b2Joint. - float GetReactionTorque(float inv_dt) const override; - - /// Use this to update the target point. - void SetTarget(const b2Vec2& target); - const b2Vec2& GetTarget() const; - - /// Set/get the maximum force in Newtons. - void SetMaxForce(float force); - float GetMaxForce() const; - - /// Set/get the linear stiffness in N/m - void SetStiffness(float stiffness) { m_stiffness = stiffness; } - float GetStiffness() const { return m_stiffness; } - - /// Set/get linear damping in N*s/m - void SetDamping(float damping) { m_damping = damping; } - float GetDamping() const { return m_damping; } - - /// The mouse joint does not support dumping. - void Dump() override { b2Log("Mouse joint dumping is not supported.\n"); } - - /// Implement b2Joint::ShiftOrigin - void ShiftOrigin(const b2Vec2& newOrigin) override; - -protected: - friend class b2Joint; - - b2MouseJoint(const b2MouseJointDef* def); - - void InitVelocityConstraints(const b2SolverData& data) override; - void SolveVelocityConstraints(const b2SolverData& data) override; - bool SolvePositionConstraints(const b2SolverData& data) override; - - b2Vec2 m_localAnchorB; - b2Vec2 m_targetA; - float m_stiffness; - float m_damping; - float m_beta; - - // Solver shared - b2Vec2 m_impulse; - float m_maxForce; - float m_gamma; - - // Solver temp - int32 m_indexA; - int32 m_indexB; - b2Vec2 m_rB; - b2Vec2 m_localCenterB; - float m_invMassB; - float m_invIB; - b2Mat22 m_mass; - b2Vec2 m_C; -}; - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_polygon_shape.h b/3rdparty/box2d/include/box2d/b2_polygon_shape.h deleted file mode 100644 index 8a208b72b66d..000000000000 --- a/3rdparty/box2d/include/box2d/b2_polygon_shape.h +++ /dev/null @@ -1,93 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#ifndef B2_POLYGON_SHAPE_H -#define B2_POLYGON_SHAPE_H - -#include "b2_api.h" -#include "b2_shape.h" - -struct b2Hull; - -/// A solid convex polygon. It is assumed that the interior of the polygon is to -/// the left of each edge. -/// Polygons have a maximum number of vertices equal to b2_maxPolygonVertices. -/// In most cases you should not need many vertices for a convex polygon. -class B2_API b2PolygonShape : public b2Shape -{ -public: - b2PolygonShape(); - - /// Implement b2Shape. - b2Shape* Clone(b2BlockAllocator* allocator) const override; - - /// @see b2Shape::GetChildCount - int32 GetChildCount() const override; - - /// Create a convex hull from the given array of local points. - /// The count must be in the range [3, b2_maxPolygonVertices]. - /// @warning the points may be re-ordered, even if they form a convex polygon - /// @warning if this fails then the polygon is invalid - /// @returns true if valid - bool Set(const b2Vec2* points, int32 count); - - /// Create a polygon from a given convex hull (see b2ComputeHull). - /// @warning the hull must be valid or this will crash or have unexpected behavior - void Set(const b2Hull& hull); - - /// Build vertices to represent an axis-aligned box centered on the local origin. - /// @param hx the half-width. - /// @param hy the half-height. - void SetAsBox(float hx, float hy); - - /// Build vertices to represent an oriented box. - /// @param hx the half-width. - /// @param hy the half-height. - /// @param center the center of the box in local coordinates. - /// @param angle the rotation of the box in local coordinates. - void SetAsBox(float hx, float hy, const b2Vec2& center, float angle); - - /// @see b2Shape::TestPoint - bool TestPoint(const b2Transform& transform, const b2Vec2& p) const override; - - /// Implement b2Shape. - /// @note because the polygon is solid, rays that start inside do not hit because the normal is - /// not defined. - bool RayCast(b2RayCastOutput* output, const b2RayCastInput& input, - const b2Transform& transform, int32 childIndex) const override; - - /// @see b2Shape::ComputeAABB - void ComputeAABB(b2AABB* aabb, const b2Transform& transform, int32 childIndex) const override; - - /// @see b2Shape::ComputeMass - void ComputeMass(b2MassData* massData, float density) const override; - - /// Validate convexity. This is a very time consuming operation. - /// @returns true if valid - bool Validate() const; - - b2Vec2 m_centroid; - b2Vec2 m_vertices[b2_maxPolygonVertices]; - b2Vec2 m_normals[b2_maxPolygonVertices]; - int32 m_count; -}; - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_prismatic_joint.h b/3rdparty/box2d/include/box2d/b2_prismatic_joint.h deleted file mode 100644 index 9d12d2126b3d..000000000000 --- a/3rdparty/box2d/include/box2d/b2_prismatic_joint.h +++ /dev/null @@ -1,205 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_PRISMATIC_JOINT_H -#define B2_PRISMATIC_JOINT_H - -#include "b2_api.h" -#include "b2_joint.h" - -/// Prismatic joint definition. This requires defining a line of -/// motion using an axis and an anchor point. The definition uses local -/// anchor points and a local axis so that the initial configuration -/// can violate the constraint slightly. The joint translation is zero -/// when the local anchor points coincide in world space. Using local -/// anchors and a local axis helps when saving and loading a game. -struct B2_API b2PrismaticJointDef : public b2JointDef -{ - b2PrismaticJointDef() - { - type = e_prismaticJoint; - localAnchorA.SetZero(); - localAnchorB.SetZero(); - localAxisA.Set(1.0f, 0.0f); - referenceAngle = 0.0f; - enableLimit = false; - lowerTranslation = 0.0f; - upperTranslation = 0.0f; - enableMotor = false; - maxMotorForce = 0.0f; - motorSpeed = 0.0f; - } - - /// Initialize the bodies, anchors, axis, and reference angle using the world - /// anchor and unit world axis. - void Initialize(b2Body* bodyA, b2Body* bodyB, const b2Vec2& anchor, const b2Vec2& axis); - - /// The local anchor point relative to bodyA's origin. - b2Vec2 localAnchorA; - - /// The local anchor point relative to bodyB's origin. - b2Vec2 localAnchorB; - - /// The local translation unit axis in bodyA. - b2Vec2 localAxisA; - - /// The constrained angle between the bodies: bodyB_angle - bodyA_angle. - float referenceAngle; - - /// Enable/disable the joint limit. - bool enableLimit; - - /// The lower translation limit, usually in meters. - float lowerTranslation; - - /// The upper translation limit, usually in meters. - float upperTranslation; - - /// Enable/disable the joint motor. - bool enableMotor; - - /// The maximum motor torque, usually in N-m. - float maxMotorForce; - - /// The desired motor speed in radians per second. - float motorSpeed; -}; - -/// A prismatic joint. This joint provides one degree of freedom: translation -/// along an axis fixed in bodyA. Relative rotation is prevented. You can -/// use a joint limit to restrict the range of motion and a joint motor to -/// drive the motion or to model joint friction. -class B2_API b2PrismaticJoint : public b2Joint -{ -public: - b2Vec2 GetAnchorA() const override; - b2Vec2 GetAnchorB() const override; - - b2Vec2 GetReactionForce(float inv_dt) const override; - float GetReactionTorque(float inv_dt) const override; - - /// The local anchor point relative to bodyA's origin. - const b2Vec2& GetLocalAnchorA() const { return m_localAnchorA; } - - /// The local anchor point relative to bodyB's origin. - const b2Vec2& GetLocalAnchorB() const { return m_localAnchorB; } - - /// The local joint axis relative to bodyA. - const b2Vec2& GetLocalAxisA() const { return m_localXAxisA; } - - /// Get the reference angle. - float GetReferenceAngle() const { return m_referenceAngle; } - - /// Get the current joint translation, usually in meters. - float GetJointTranslation() const; - - /// Get the current joint translation speed, usually in meters per second. - float GetJointSpeed() const; - - /// Is the joint limit enabled? - bool IsLimitEnabled() const; - - /// Enable/disable the joint limit. - void EnableLimit(bool flag); - - /// Get the lower joint limit, usually in meters. - float GetLowerLimit() const; - - /// Get the upper joint limit, usually in meters. - float GetUpperLimit() const; - - /// Set the joint limits, usually in meters. - void SetLimits(float lower, float upper); - - /// Is the joint motor enabled? - bool IsMotorEnabled() const; - - /// Enable/disable the joint motor. - void EnableMotor(bool flag); - - /// Set the motor speed, usually in meters per second. - void SetMotorSpeed(float speed); - - /// Get the motor speed, usually in meters per second. - float GetMotorSpeed() const; - - /// Set the maximum motor force, usually in N. - void SetMaxMotorForce(float force); - float GetMaxMotorForce() const { return m_maxMotorForce; } - - /// Get the current motor force given the inverse time step, usually in N. - float GetMotorForce(float inv_dt) const; - - /// Dump to b2Log - void Dump() override; - - /// - void Draw(b2Draw* draw) const override; - -protected: - friend class b2Joint; - friend class b2GearJoint; - b2PrismaticJoint(const b2PrismaticJointDef* def); - - void InitVelocityConstraints(const b2SolverData& data) override; - void SolveVelocityConstraints(const b2SolverData& data) override; - bool SolvePositionConstraints(const b2SolverData& data) override; - - b2Vec2 m_localAnchorA; - b2Vec2 m_localAnchorB; - b2Vec2 m_localXAxisA; - b2Vec2 m_localYAxisA; - float m_referenceAngle; - b2Vec2 m_impulse; - float m_motorImpulse; - float m_lowerImpulse; - float m_upperImpulse; - float m_lowerTranslation; - float m_upperTranslation; - float m_maxMotorForce; - float m_motorSpeed; - bool m_enableLimit; - bool m_enableMotor; - - // Solver temp - int32 m_indexA; - int32 m_indexB; - b2Vec2 m_localCenterA; - b2Vec2 m_localCenterB; - float m_invMassA; - float m_invMassB; - float m_invIA; - float m_invIB; - b2Vec2 m_axis, m_perp; - float m_s1, m_s2; - float m_a1, m_a2; - b2Mat22 m_K; - float m_translation; - float m_axialMass; -}; - -inline float b2PrismaticJoint::GetMotorSpeed() const -{ - return m_motorSpeed; -} - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_pulley_joint.h b/3rdparty/box2d/include/box2d/b2_pulley_joint.h deleted file mode 100644 index 6b1445641045..000000000000 --- a/3rdparty/box2d/include/box2d/b2_pulley_joint.h +++ /dev/null @@ -1,157 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_PULLEY_JOINT_H -#define B2_PULLEY_JOINT_H - -#include "b2_api.h" -#include "b2_joint.h" - -const float b2_minPulleyLength = 2.0f; - -/// Pulley joint definition. This requires two ground anchors, -/// two dynamic body anchor points, and a pulley ratio. -struct B2_API b2PulleyJointDef : public b2JointDef -{ - b2PulleyJointDef() - { - type = e_pulleyJoint; - groundAnchorA.Set(-1.0f, 1.0f); - groundAnchorB.Set(1.0f, 1.0f); - localAnchorA.Set(-1.0f, 0.0f); - localAnchorB.Set(1.0f, 0.0f); - lengthA = 0.0f; - lengthB = 0.0f; - ratio = 1.0f; - collideConnected = true; - } - - /// Initialize the bodies, anchors, lengths, max lengths, and ratio using the world anchors. - void Initialize(b2Body* bodyA, b2Body* bodyB, - const b2Vec2& groundAnchorA, const b2Vec2& groundAnchorB, - const b2Vec2& anchorA, const b2Vec2& anchorB, - float ratio); - - /// The first ground anchor in world coordinates. This point never moves. - b2Vec2 groundAnchorA; - - /// The second ground anchor in world coordinates. This point never moves. - b2Vec2 groundAnchorB; - - /// The local anchor point relative to bodyA's origin. - b2Vec2 localAnchorA; - - /// The local anchor point relative to bodyB's origin. - b2Vec2 localAnchorB; - - /// The a reference length for the segment attached to bodyA. - float lengthA; - - /// The a reference length for the segment attached to bodyB. - float lengthB; - - /// The pulley ratio, used to simulate a block-and-tackle. - float ratio; -}; - -/// The pulley joint is connected to two bodies and two fixed ground points. -/// The pulley supports a ratio such that: -/// length1 + ratio * length2 <= constant -/// Yes, the force transmitted is scaled by the ratio. -/// Warning: the pulley joint can get a bit squirrelly by itself. They often -/// work better when combined with prismatic joints. You should also cover the -/// the anchor points with static shapes to prevent one side from going to -/// zero length. -class B2_API b2PulleyJoint : public b2Joint -{ -public: - b2Vec2 GetAnchorA() const override; - b2Vec2 GetAnchorB() const override; - - b2Vec2 GetReactionForce(float inv_dt) const override; - float GetReactionTorque(float inv_dt) const override; - - /// Get the first ground anchor. - b2Vec2 GetGroundAnchorA() const; - - /// Get the second ground anchor. - b2Vec2 GetGroundAnchorB() const; - - /// Get the current length of the segment attached to bodyA. - float GetLengthA() const; - - /// Get the current length of the segment attached to bodyB. - float GetLengthB() const; - - /// Get the pulley ratio. - float GetRatio() const; - - /// Get the current length of the segment attached to bodyA. - float GetCurrentLengthA() const; - - /// Get the current length of the segment attached to bodyB. - float GetCurrentLengthB() const; - - /// Dump joint to dmLog - void Dump() override; - - /// Implement b2Joint::ShiftOrigin - void ShiftOrigin(const b2Vec2& newOrigin) override; - -protected: - - friend class b2Joint; - b2PulleyJoint(const b2PulleyJointDef* data); - - void InitVelocityConstraints(const b2SolverData& data) override; - void SolveVelocityConstraints(const b2SolverData& data) override; - bool SolvePositionConstraints(const b2SolverData& data) override; - - b2Vec2 m_groundAnchorA; - b2Vec2 m_groundAnchorB; - float m_lengthA; - float m_lengthB; - - // Solver shared - b2Vec2 m_localAnchorA; - b2Vec2 m_localAnchorB; - float m_constant; - float m_ratio; - float m_impulse; - - // Solver temp - int32 m_indexA; - int32 m_indexB; - b2Vec2 m_uA; - b2Vec2 m_uB; - b2Vec2 m_rA; - b2Vec2 m_rB; - b2Vec2 m_localCenterA; - b2Vec2 m_localCenterB; - float m_invMassA; - float m_invMassB; - float m_invIA; - float m_invIB; - float m_mass; -}; - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_revolute_joint.h b/3rdparty/box2d/include/box2d/b2_revolute_joint.h deleted file mode 100644 index 36ad5314efac..000000000000 --- a/3rdparty/box2d/include/box2d/b2_revolute_joint.h +++ /dev/null @@ -1,211 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_REVOLUTE_JOINT_H -#define B2_REVOLUTE_JOINT_H - -#include "b2_api.h" -#include "b2_joint.h" - -/// Revolute joint definition. This requires defining an anchor point where the -/// bodies are joined. The definition uses local anchor points so that the -/// initial configuration can violate the constraint slightly. You also need to -/// specify the initial relative angle for joint limits. This helps when saving -/// and loading a game. -/// The local anchor points are measured from the body's origin -/// rather than the center of mass because: -/// 1. you might not know where the center of mass will be. -/// 2. if you add/remove shapes from a body and recompute the mass, -/// the joints will be broken. -struct B2_API b2RevoluteJointDef : public b2JointDef -{ - b2RevoluteJointDef() - { - type = e_revoluteJoint; - localAnchorA.Set(0.0f, 0.0f); - localAnchorB.Set(0.0f, 0.0f); - referenceAngle = 0.0f; - lowerAngle = 0.0f; - upperAngle = 0.0f; - maxMotorTorque = 0.0f; - motorSpeed = 0.0f; - enableLimit = false; - enableMotor = false; - } - - /// Initialize the bodies, anchors, and reference angle using a world - /// anchor point. - void Initialize(b2Body* bodyA, b2Body* bodyB, const b2Vec2& anchor); - - /// The local anchor point relative to bodyA's origin. - b2Vec2 localAnchorA; - - /// The local anchor point relative to bodyB's origin. - b2Vec2 localAnchorB; - - /// The bodyB angle minus bodyA angle in the reference state (radians). - float referenceAngle; - - /// A flag to enable joint limits. - bool enableLimit; - - /// The lower angle for the joint limit (radians). - float lowerAngle; - - /// The upper angle for the joint limit (radians). - float upperAngle; - - /// A flag to enable the joint motor. - bool enableMotor; - - /// The desired motor speed. Usually in radians per second. - float motorSpeed; - - /// The maximum motor torque used to achieve the desired motor speed. - /// Usually in N-m. - float maxMotorTorque; -}; - -/// A revolute joint constrains two bodies to share a common point while they -/// are free to rotate about the point. The relative rotation about the shared -/// point is the joint angle. You can limit the relative rotation with -/// a joint limit that specifies a lower and upper angle. You can use a motor -/// to drive the relative rotation about the shared point. A maximum motor torque -/// is provided so that infinite forces are not generated. -class B2_API b2RevoluteJoint : public b2Joint -{ -public: - b2Vec2 GetAnchorA() const override; - b2Vec2 GetAnchorB() const override; - - /// The local anchor point relative to bodyA's origin. - const b2Vec2& GetLocalAnchorA() const { return m_localAnchorA; } - - /// The local anchor point relative to bodyB's origin. - const b2Vec2& GetLocalAnchorB() const { return m_localAnchorB; } - - /// Get the reference angle. - float GetReferenceAngle() const { return m_referenceAngle; } - - /// Get the current joint angle in radians. - float GetJointAngle() const; - - /// Get the current joint angle speed in radians per second. - float GetJointSpeed() const; - - /// Is the joint limit enabled? - bool IsLimitEnabled() const; - - /// Enable/disable the joint limit. - void EnableLimit(bool flag); - - /// Get the lower joint limit in radians. - float GetLowerLimit() const; - - /// Get the upper joint limit in radians. - float GetUpperLimit() const; - - /// Set the joint limits in radians. - void SetLimits(float lower, float upper); - - /// Is the joint motor enabled? - bool IsMotorEnabled() const; - - /// Enable/disable the joint motor. - void EnableMotor(bool flag); - - /// Set the motor speed in radians per second. - void SetMotorSpeed(float speed); - - /// Get the motor speed in radians per second. - float GetMotorSpeed() const; - - /// Set the maximum motor torque, usually in N-m. - void SetMaxMotorTorque(float torque); - float GetMaxMotorTorque() const { return m_maxMotorTorque; } - - /// Get the reaction force given the inverse time step. - /// Unit is N. - b2Vec2 GetReactionForce(float inv_dt) const override; - - /// Get the reaction torque due to the joint limit given the inverse time step. - /// Unit is N*m. - float GetReactionTorque(float inv_dt) const override; - - /// Get the current motor torque given the inverse time step. - /// Unit is N*m. - float GetMotorTorque(float inv_dt) const; - - /// Dump to b2Log. - void Dump() override; - - /// - void Draw(b2Draw* draw) const override; - -protected: - - friend class b2Joint; - friend class b2GearJoint; - - b2RevoluteJoint(const b2RevoluteJointDef* def); - - void InitVelocityConstraints(const b2SolverData& data) override; - void SolveVelocityConstraints(const b2SolverData& data) override; - bool SolvePositionConstraints(const b2SolverData& data) override; - - // Solver shared - b2Vec2 m_localAnchorA; - b2Vec2 m_localAnchorB; - b2Vec2 m_impulse; - float m_motorImpulse; - float m_lowerImpulse; - float m_upperImpulse; - bool m_enableMotor; - float m_maxMotorTorque; - float m_motorSpeed; - bool m_enableLimit; - float m_referenceAngle; - float m_lowerAngle; - float m_upperAngle; - - // Solver temp - int32 m_indexA; - int32 m_indexB; - b2Vec2 m_rA; - b2Vec2 m_rB; - b2Vec2 m_localCenterA; - b2Vec2 m_localCenterB; - float m_invMassA; - float m_invMassB; - float m_invIA; - float m_invIB; - b2Mat22 m_K; - float m_angle; - float m_axialMass; -}; - -inline float b2RevoluteJoint::GetMotorSpeed() const -{ - return m_motorSpeed; -} - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_rope.h b/3rdparty/box2d/include/box2d/b2_rope.h deleted file mode 100644 index 0c4f819595e7..000000000000 --- a/3rdparty/box2d/include/box2d/b2_rope.h +++ /dev/null @@ -1,155 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_ROPE_H -#define B2_ROPE_H - -#include "b2_api.h" -#include "b2_math.h" - -class b2Draw; -struct b2RopeStretch; -struct b2RopeBend; - -enum b2StretchingModel -{ - b2_pbdStretchingModel, - b2_xpbdStretchingModel -}; - -enum b2BendingModel -{ - b2_springAngleBendingModel = 0, - b2_pbdAngleBendingModel, - b2_xpbdAngleBendingModel, - b2_pbdDistanceBendingModel, - b2_pbdHeightBendingModel, - b2_pbdTriangleBendingModel -}; - -/// -struct B2_API b2RopeTuning -{ - b2RopeTuning() - { - stretchingModel = b2_pbdStretchingModel; - bendingModel = b2_pbdAngleBendingModel; - damping = 0.0f; - stretchStiffness = 1.0f; - stretchHertz = 1.0f; - stretchDamping = 0.0f; - bendStiffness = 0.5f; - bendHertz = 1.0f; - bendDamping = 0.0f; - isometric = false; - fixedEffectiveMass = false; - warmStart = false; - } - - b2StretchingModel stretchingModel; - b2BendingModel bendingModel; - float damping; - float stretchStiffness; - float stretchHertz; - float stretchDamping; - float bendStiffness; - float bendHertz; - float bendDamping; - bool isometric; - bool fixedEffectiveMass; - bool warmStart; -}; - -/// -struct B2_API b2RopeDef -{ - b2RopeDef() - { - position.SetZero(); - vertices = nullptr; - count = 0; - masses = nullptr; - gravity.SetZero(); - } - - b2Vec2 position; - b2Vec2* vertices; - int32 count; - float* masses; - b2Vec2 gravity; - b2RopeTuning tuning; -}; - -/// -class B2_API b2Rope -{ -public: - b2Rope(); - ~b2Rope(); - - /// - void Create(const b2RopeDef& def); - - /// - void SetTuning(const b2RopeTuning& tuning); - - /// - void Step(float timeStep, int32 iterations, const b2Vec2& position); - - /// - void Reset(const b2Vec2& position); - - /// - void Draw(b2Draw* draw) const; - -private: - - void SolveStretch_PBD(); - void SolveStretch_XPBD(float dt); - void SolveBend_PBD_Angle(); - void SolveBend_XPBD_Angle(float dt); - void SolveBend_PBD_Distance(); - void SolveBend_PBD_Height(); - void SolveBend_PBD_Triangle(); - void ApplyBendForces(float dt); - - b2Vec2 m_position; - - int32 m_count; - int32 m_stretchCount; - int32 m_bendCount; - - b2RopeStretch* m_stretchConstraints; - b2RopeBend* m_bendConstraints; - - b2Vec2* m_bindPositions; - b2Vec2* m_ps; - b2Vec2* m_p0s; - b2Vec2* m_vs; - - float* m_invMasses; - b2Vec2 m_gravity; - - b2RopeTuning m_tuning; -}; - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_settings.h b/3rdparty/box2d/include/box2d/b2_settings.h deleted file mode 100644 index 48cd95dfe2a5..000000000000 --- a/3rdparty/box2d/include/box2d/b2_settings.h +++ /dev/null @@ -1,127 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_SETTINGS_H -#define B2_SETTINGS_H - -#include "b2_types.h" -#include "b2_api.h" - -/// @file -/// Settings that can be overriden for your application -/// - -/// Define this macro in your build if you want to override settings -#ifdef B2_USER_SETTINGS - -/// This is a user file that includes custom definitions of the macros, structs, and functions -/// defined below. -#include "b2_user_settings.h" - -#else - -#include -#include - -// Tunable Constants - -/// You can use this to change the length scale used by your game. -/// For example for inches you could use 39.4. -#define b2_lengthUnitsPerMeter 1.0f - -/// The maximum number of vertices on a convex polygon. You cannot increase -/// this too much because b2BlockAllocator has a maximum object size. -#define b2_maxPolygonVertices 8 - -// User data - -/// You can define this to inject whatever data you want in b2Body -struct B2_API b2BodyUserData -{ - b2BodyUserData() - { - pointer = 0; - } - - /// For legacy compatibility - uintptr_t pointer; -}; - -/// You can define this to inject whatever data you want in b2Fixture -struct B2_API b2FixtureUserData -{ - b2FixtureUserData() - { - pointer = 0; - } - - /// For legacy compatibility - uintptr_t pointer; -}; - -/// You can define this to inject whatever data you want in b2Joint -struct B2_API b2JointUserData -{ - b2JointUserData() - { - pointer = 0; - } - - /// For legacy compatibility - uintptr_t pointer; -}; - -// Memory Allocation - -/// Default allocation functions -B2_API void* b2Alloc_Default(int32 size); -B2_API void b2Free_Default(void* mem); - -/// Implement this function to use your own memory allocator. -inline void* b2Alloc(int32 size) -{ - return b2Alloc_Default(size); -} - -/// If you implement b2Alloc, you should also implement this function. -inline void b2Free(void* mem) -{ - b2Free_Default(mem); -} - -/// Default logging function -B2_API void b2Log_Default(const char* string, va_list args); - -/// Implement this to use your own logging. -inline void b2Log(const char* string, ...) -{ - va_list args; - va_start(args, string); - b2Log_Default(string, args); - va_end(args); -} - -#endif // B2_USER_SETTINGS - -#include "b2_common.h" - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_shape.h b/3rdparty/box2d/include/box2d/b2_shape.h deleted file mode 100644 index cbed2b863358..000000000000 --- a/3rdparty/box2d/include/box2d/b2_shape.h +++ /dev/null @@ -1,110 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_SHAPE_H -#define B2_SHAPE_H - -#include "b2_api.h" -#include "b2_math.h" -#include "b2_collision.h" - -class b2BlockAllocator; - -/// This holds the mass data computed for a shape. -struct B2_API b2MassData -{ - /// The mass of the shape, usually in kilograms. - float mass; - - /// The position of the shape's centroid relative to the shape's origin. - b2Vec2 center; - - /// The rotational inertia of the shape about the local origin. - float I; -}; - -/// A shape is used for collision detection. You can create a shape however you like. -/// Shapes used for simulation in b2World are created automatically when a b2Fixture -/// is created. Shapes may encapsulate a one or more child shapes. -class B2_API b2Shape -{ -public: - - enum Type - { - e_circle = 0, - e_edge = 1, - e_polygon = 2, - e_chain = 3, - e_typeCount = 4 - }; - - virtual ~b2Shape() {} - - /// Clone the concrete shape using the provided allocator. - virtual b2Shape* Clone(b2BlockAllocator* allocator) const = 0; - - /// Get the type of this shape. You can use this to down cast to the concrete shape. - /// @return the shape type. - Type GetType() const; - - /// Get the number of child primitives. - virtual int32 GetChildCount() const = 0; - - /// Test a point for containment in this shape. This only works for convex shapes. - /// @param xf the shape world transform. - /// @param p a point in world coordinates. - virtual bool TestPoint(const b2Transform& xf, const b2Vec2& p) const = 0; - - /// Cast a ray against a child shape. - /// @param output the ray-cast results. - /// @param input the ray-cast input parameters. - /// @param transform the transform to be applied to the shape. - /// @param childIndex the child shape index - virtual bool RayCast(b2RayCastOutput* output, const b2RayCastInput& input, - const b2Transform& transform, int32 childIndex) const = 0; - - /// Given a transform, compute the associated axis aligned bounding box for a child shape. - /// @param aabb returns the axis aligned box. - /// @param xf the world transform of the shape. - /// @param childIndex the child shape - virtual void ComputeAABB(b2AABB* aabb, const b2Transform& xf, int32 childIndex) const = 0; - - /// Compute the mass properties of this shape using its dimensions and density. - /// The inertia tensor is computed about the local origin. - /// @param massData returns the mass data for this shape. - /// @param density the density in kilograms per meter squared. - virtual void ComputeMass(b2MassData* massData, float density) const = 0; - - Type m_type; - - /// Radius of a shape. For polygonal shapes this must be b2_polygonRadius. There is no support for - /// making rounded polygons. - float m_radius; -}; - -inline b2Shape::Type b2Shape::GetType() const -{ - return m_type; -} - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_stack_allocator.h b/3rdparty/box2d/include/box2d/b2_stack_allocator.h deleted file mode 100644 index 1db2af5d95f3..000000000000 --- a/3rdparty/box2d/include/box2d/b2_stack_allocator.h +++ /dev/null @@ -1,65 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_STACK_ALLOCATOR_H -#define B2_STACK_ALLOCATOR_H - -#include "b2_api.h" -#include "b2_settings.h" - -const int32 b2_stackSize = 100 * 1024; // 100k -const int32 b2_maxStackEntries = 32; - -struct B2_API b2StackEntry -{ - char* data; - int32 size; - bool usedMalloc; -}; - -// This is a stack allocator used for fast per step allocations. -// You must nest allocate/free pairs. The code will assert -// if you try to interleave multiple allocate/free pairs. -class B2_API b2StackAllocator -{ -public: - b2StackAllocator(); - ~b2StackAllocator(); - - void* Allocate(int32 size); - void Free(void* p); - - int32 GetMaxAllocation() const; - -private: - - char m_data[b2_stackSize]; - int32 m_index; - - int32 m_allocation; - int32 m_maxAllocation; - - b2StackEntry m_entries[b2_maxStackEntries]; - int32 m_entryCount; -}; - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_time_of_impact.h b/3rdparty/box2d/include/box2d/b2_time_of_impact.h deleted file mode 100644 index 04d46262e4ca..000000000000 --- a/3rdparty/box2d/include/box2d/b2_time_of_impact.h +++ /dev/null @@ -1,63 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_TIME_OF_IMPACT_H -#define B2_TIME_OF_IMPACT_H - -#include "b2_api.h" -#include "b2_math.h" -#include "b2_distance.h" - -/// Input parameters for b2TimeOfImpact -struct B2_API b2TOIInput -{ - b2DistanceProxy proxyA; - b2DistanceProxy proxyB; - b2Sweep sweepA; - b2Sweep sweepB; - float tMax; // defines sweep interval [0, tMax] -}; - -/// Output parameters for b2TimeOfImpact. -struct B2_API b2TOIOutput -{ - enum State - { - e_unknown, - e_failed, - e_overlapped, - e_touching, - e_separated - }; - - State state; - float t; -}; - -/// Compute the upper bound on time before two shapes penetrate. Time is represented as -/// a fraction between [0,tMax]. This uses a swept separating axis and may miss some intermediate, -/// non-tunneling collisions. If you change the time interval, you should call this function -/// again. -/// Note: use b2Distance to compute the contact point and normal at the time of impact. -B2_API void b2TimeOfImpact(b2TOIOutput* output, const b2TOIInput* input); - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_time_step.h b/3rdparty/box2d/include/box2d/b2_time_step.h deleted file mode 100644 index 13d6292ec15c..000000000000 --- a/3rdparty/box2d/include/box2d/b2_time_step.h +++ /dev/null @@ -1,74 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#ifndef B2_TIME_STEP_H -#define B2_TIME_STEP_H - -#include "b2_api.h" -#include "b2_math.h" - -/// Profiling data. Times are in milliseconds. -struct B2_API b2Profile -{ - float step; - float collide; - float solve; - float solveInit; - float solveVelocity; - float solvePosition; - float broadphase; - float solveTOI; -}; - -/// This is an internal structure. -struct B2_API b2TimeStep -{ - float dt; // time step - float inv_dt; // inverse time step (0 if dt == 0). - float dtRatio; // dt * inv_dt0 - int32 velocityIterations; - int32 positionIterations; - bool warmStarting; -}; - -/// This is an internal structure. -struct B2_API b2Position -{ - b2Vec2 c; - float a; -}; - -/// This is an internal structure. -struct B2_API b2Velocity -{ - b2Vec2 v; - float w; -}; - -/// Solver Data -struct B2_API b2SolverData -{ - b2TimeStep step; - b2Position* positions; - b2Velocity* velocities; -}; - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_timer.h b/3rdparty/box2d/include/box2d/b2_timer.h deleted file mode 100644 index 7893c32703bc..000000000000 --- a/3rdparty/box2d/include/box2d/b2_timer.h +++ /dev/null @@ -1,55 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_TIMER_H -#define B2_TIMER_H - -#include "b2_api.h" -#include "b2_settings.h" - -/// Timer for profiling. This has platform specific code and may -/// not work on every platform. -class B2_API b2Timer -{ -public: - - /// Constructor - b2Timer(); - - /// Reset the timer. - void Reset(); - - /// Get the time since construction or the last reset. - float GetMilliseconds() const; - -private: - -#if defined(_WIN32) - double m_start; - static double s_invFrequency; -#elif defined(__linux__) || defined (__APPLE__) - unsigned long long m_start_sec; - unsigned long long m_start_usec; -#endif -}; - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_types.h b/3rdparty/box2d/include/box2d/b2_types.h deleted file mode 100644 index e0d4377591d6..000000000000 --- a/3rdparty/box2d/include/box2d/b2_types.h +++ /dev/null @@ -1,33 +0,0 @@ -// MIT License - -// Copyright (c) 2020 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_TYPES_H -#define B2_TYPES_H - -typedef signed char int8; -typedef signed short int16; -typedef signed int int32; -typedef unsigned char uint8; -typedef unsigned short uint16; -typedef unsigned int uint32; - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_weld_joint.h b/3rdparty/box2d/include/box2d/b2_weld_joint.h deleted file mode 100644 index 38501f20ab05..000000000000 --- a/3rdparty/box2d/include/box2d/b2_weld_joint.h +++ /dev/null @@ -1,133 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_WELD_JOINT_H -#define B2_WELD_JOINT_H - -#include "b2_api.h" -#include "b2_joint.h" - -/// Weld joint definition. You need to specify local anchor points -/// where they are attached and the relative body angle. The position -/// of the anchor points is important for computing the reaction torque. -struct B2_API b2WeldJointDef : public b2JointDef -{ - b2WeldJointDef() - { - type = e_weldJoint; - localAnchorA.Set(0.0f, 0.0f); - localAnchorB.Set(0.0f, 0.0f); - referenceAngle = 0.0f; - stiffness = 0.0f; - damping = 0.0f; - } - - /// Initialize the bodies, anchors, reference angle, stiffness, and damping. - /// @param bodyA the first body connected by this joint - /// @param bodyB the second body connected by this joint - /// @param anchor the point of connection in world coordinates - void Initialize(b2Body* bodyA, b2Body* bodyB, const b2Vec2& anchor); - - /// The local anchor point relative to bodyA's origin. - b2Vec2 localAnchorA; - - /// The local anchor point relative to bodyB's origin. - b2Vec2 localAnchorB; - - /// The bodyB angle minus bodyA angle in the reference state (radians). - float referenceAngle; - - /// The rotational stiffness in N*m - /// Disable softness with a value of 0 - float stiffness; - - /// The rotational damping in N*m*s - float damping; -}; - -/// A weld joint essentially glues two bodies together. A weld joint may -/// distort somewhat because the island constraint solver is approximate. -class B2_API b2WeldJoint : public b2Joint -{ -public: - b2Vec2 GetAnchorA() const override; - b2Vec2 GetAnchorB() const override; - - b2Vec2 GetReactionForce(float inv_dt) const override; - float GetReactionTorque(float inv_dt) const override; - - /// The local anchor point relative to bodyA's origin. - const b2Vec2& GetLocalAnchorA() const { return m_localAnchorA; } - - /// The local anchor point relative to bodyB's origin. - const b2Vec2& GetLocalAnchorB() const { return m_localAnchorB; } - - /// Get the reference angle. - float GetReferenceAngle() const { return m_referenceAngle; } - - /// Set/get stiffness in N*m - void SetStiffness(float stiffness) { m_stiffness = stiffness; } - float GetStiffness() const { return m_stiffness; } - - /// Set/get damping in N*m*s - void SetDamping(float damping) { m_damping = damping; } - float GetDamping() const { return m_damping; } - - /// Dump to b2Log - void Dump() override; - -protected: - - friend class b2Joint; - - b2WeldJoint(const b2WeldJointDef* def); - - void InitVelocityConstraints(const b2SolverData& data) override; - void SolveVelocityConstraints(const b2SolverData& data) override; - bool SolvePositionConstraints(const b2SolverData& data) override; - - float m_stiffness; - float m_damping; - float m_bias; - - // Solver shared - b2Vec2 m_localAnchorA; - b2Vec2 m_localAnchorB; - float m_referenceAngle; - float m_gamma; - b2Vec3 m_impulse; - - // Solver temp - int32 m_indexA; - int32 m_indexB; - b2Vec2 m_rA; - b2Vec2 m_rB; - b2Vec2 m_localCenterA; - b2Vec2 m_localCenterB; - float m_invMassA; - float m_invMassB; - float m_invIA; - float m_invIB; - b2Mat33 m_mass; -}; - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_wheel_joint.h b/3rdparty/box2d/include/box2d/b2_wheel_joint.h deleted file mode 100644 index 8576adbd20ec..000000000000 --- a/3rdparty/box2d/include/box2d/b2_wheel_joint.h +++ /dev/null @@ -1,240 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_WHEEL_JOINT_H -#define B2_WHEEL_JOINT_H - -#include "b2_api.h" -#include "b2_joint.h" - -/// Wheel joint definition. This requires defining a line of -/// motion using an axis and an anchor point. The definition uses local -/// anchor points and a local axis so that the initial configuration -/// can violate the constraint slightly. The joint translation is zero -/// when the local anchor points coincide in world space. Using local -/// anchors and a local axis helps when saving and loading a game. -struct B2_API b2WheelJointDef : public b2JointDef -{ - b2WheelJointDef() - { - type = e_wheelJoint; - localAnchorA.SetZero(); - localAnchorB.SetZero(); - localAxisA.Set(1.0f, 0.0f); - enableLimit = false; - lowerTranslation = 0.0f; - upperTranslation = 0.0f; - enableMotor = false; - maxMotorTorque = 0.0f; - motorSpeed = 0.0f; - stiffness = 0.0f; - damping = 0.0f; - } - - /// Initialize the bodies, anchors, axis, and reference angle using the world - /// anchor and world axis. - void Initialize(b2Body* bodyA, b2Body* bodyB, const b2Vec2& anchor, const b2Vec2& axis); - - /// The local anchor point relative to bodyA's origin. - b2Vec2 localAnchorA; - - /// The local anchor point relative to bodyB's origin. - b2Vec2 localAnchorB; - - /// The local translation axis in bodyA. - b2Vec2 localAxisA; - - /// Enable/disable the joint limit. - bool enableLimit; - - /// The lower translation limit, usually in meters. - float lowerTranslation; - - /// The upper translation limit, usually in meters. - float upperTranslation; - - /// Enable/disable the joint motor. - bool enableMotor; - - /// The maximum motor torque, usually in N-m. - float maxMotorTorque; - - /// The desired motor speed in radians per second. - float motorSpeed; - - /// Suspension stiffness. Typically in units N/m. - float stiffness; - - /// Suspension damping. Typically in units of N*s/m. - float damping; -}; - -/// A wheel joint. This joint provides two degrees of freedom: translation -/// along an axis fixed in bodyA and rotation in the plane. In other words, it is a point to -/// line constraint with a rotational motor and a linear spring/damper. The spring/damper is -/// initialized upon creation. This joint is designed for vehicle suspensions. -class B2_API b2WheelJoint : public b2Joint -{ -public: - b2Vec2 GetAnchorA() const override; - b2Vec2 GetAnchorB() const override; - - b2Vec2 GetReactionForce(float inv_dt) const override; - float GetReactionTorque(float inv_dt) const override; - - /// The local anchor point relative to bodyA's origin. - const b2Vec2& GetLocalAnchorA() const { return m_localAnchorA; } - - /// The local anchor point relative to bodyB's origin. - const b2Vec2& GetLocalAnchorB() const { return m_localAnchorB; } - - /// The local joint axis relative to bodyA. - const b2Vec2& GetLocalAxisA() const { return m_localXAxisA; } - - /// Get the current joint translation, usually in meters. - float GetJointTranslation() const; - - /// Get the current joint linear speed, usually in meters per second. - float GetJointLinearSpeed() const; - - /// Get the current joint angle in radians. - float GetJointAngle() const; - - /// Get the current joint angular speed in radians per second. - float GetJointAngularSpeed() const; - - /// Is the joint limit enabled? - bool IsLimitEnabled() const; - - /// Enable/disable the joint translation limit. - void EnableLimit(bool flag); - - /// Get the lower joint translation limit, usually in meters. - float GetLowerLimit() const; - - /// Get the upper joint translation limit, usually in meters. - float GetUpperLimit() const; - - /// Set the joint translation limits, usually in meters. - void SetLimits(float lower, float upper); - - /// Is the joint motor enabled? - bool IsMotorEnabled() const; - - /// Enable/disable the joint motor. - void EnableMotor(bool flag); - - /// Set the motor speed, usually in radians per second. - void SetMotorSpeed(float speed); - - /// Get the motor speed, usually in radians per second. - float GetMotorSpeed() const; - - /// Set/Get the maximum motor force, usually in N-m. - void SetMaxMotorTorque(float torque); - float GetMaxMotorTorque() const; - - /// Get the current motor torque given the inverse time step, usually in N-m. - float GetMotorTorque(float inv_dt) const; - - /// Access spring stiffness - void SetStiffness(float stiffness); - float GetStiffness() const; - - /// Access damping - void SetDamping(float damping); - float GetDamping() const; - - /// Dump to b2Log - void Dump() override; - - /// - void Draw(b2Draw* draw) const override; - -protected: - - friend class b2Joint; - b2WheelJoint(const b2WheelJointDef* def); - - void InitVelocityConstraints(const b2SolverData& data) override; - void SolveVelocityConstraints(const b2SolverData& data) override; - bool SolvePositionConstraints(const b2SolverData& data) override; - - b2Vec2 m_localAnchorA; - b2Vec2 m_localAnchorB; - b2Vec2 m_localXAxisA; - b2Vec2 m_localYAxisA; - - float m_impulse; - float m_motorImpulse; - float m_springImpulse; - - float m_lowerImpulse; - float m_upperImpulse; - float m_translation; - float m_lowerTranslation; - float m_upperTranslation; - - float m_maxMotorTorque; - float m_motorSpeed; - - bool m_enableLimit; - bool m_enableMotor; - - float m_stiffness; - float m_damping; - - // Solver temp - int32 m_indexA; - int32 m_indexB; - b2Vec2 m_localCenterA; - b2Vec2 m_localCenterB; - float m_invMassA; - float m_invMassB; - float m_invIA; - float m_invIB; - - b2Vec2 m_ax, m_ay; - float m_sAx, m_sBx; - float m_sAy, m_sBy; - - float m_mass; - float m_motorMass; - float m_axialMass; - float m_springMass; - - float m_bias; - float m_gamma; - -}; - -inline float b2WheelJoint::GetMotorSpeed() const -{ - return m_motorSpeed; -} - -inline float b2WheelJoint::GetMaxMotorTorque() const -{ - return m_maxMotorTorque; -} - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_world.h b/3rdparty/box2d/include/box2d/b2_world.h deleted file mode 100644 index afd73bd4657a..000000000000 --- a/3rdparty/box2d/include/box2d/b2_world.h +++ /dev/null @@ -1,348 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_WORLD_H -#define B2_WORLD_H - -#include "b2_api.h" -#include "b2_block_allocator.h" -#include "b2_contact_manager.h" -#include "b2_math.h" -#include "b2_stack_allocator.h" -#include "b2_time_step.h" -#include "b2_world_callbacks.h" - -struct b2AABB; -struct b2BodyDef; -struct b2Color; -struct b2JointDef; -class b2Body; -class b2Draw; -class b2Fixture; -class b2Joint; - -/// The world class manages all physics entities, dynamic simulation, -/// and asynchronous queries. The world also contains efficient memory -/// management facilities. -class B2_API b2World -{ -public: - /// Construct a world object. - /// @param gravity the world gravity vector. - b2World(const b2Vec2& gravity); - - /// Destruct the world. All physics entities are destroyed and all heap memory is released. - ~b2World(); - - /// Register a destruction listener. The listener is owned by you and must - /// remain in scope. - void SetDestructionListener(b2DestructionListener* listener); - - /// Register a contact filter to provide specific control over collision. - /// Otherwise the default filter is used (b2_defaultFilter). The listener is - /// owned by you and must remain in scope. - void SetContactFilter(b2ContactFilter* filter); - - /// Register a contact event listener. The listener is owned by you and must - /// remain in scope. - void SetContactListener(b2ContactListener* listener); - - /// Register a routine for debug drawing. The debug draw functions are called - /// inside with b2World::DebugDraw method. The debug draw object is owned - /// by you and must remain in scope. - void SetDebugDraw(b2Draw* debugDraw); - - /// Create a rigid body given a definition. No reference to the definition - /// is retained. - /// @warning This function is locked during callbacks. - b2Body* CreateBody(const b2BodyDef* def); - - /// Destroy a rigid body given a definition. No reference to the definition - /// is retained. This function is locked during callbacks. - /// @warning This automatically deletes all associated shapes and joints. - /// @warning This function is locked during callbacks. - void DestroyBody(b2Body* body); - - /// Create a joint to constrain bodies together. No reference to the definition - /// is retained. This may cause the connected bodies to cease colliding. - /// @warning This function is locked during callbacks. - b2Joint* CreateJoint(const b2JointDef* def); - - /// Destroy a joint. This may cause the connected bodies to begin colliding. - /// @warning This function is locked during callbacks. - void DestroyJoint(b2Joint* joint); - - /// Take a time step. This performs collision detection, integration, - /// and constraint solution. - /// @param timeStep the amount of time to simulate, this should not vary. - /// @param velocityIterations for the velocity constraint solver. - /// @param positionIterations for the position constraint solver. - void Step( float timeStep, - int32 velocityIterations, - int32 positionIterations); - - /// Manually clear the force buffer on all bodies. By default, forces are cleared automatically - /// after each call to Step. The default behavior is modified by calling SetAutoClearForces. - /// The purpose of this function is to support sub-stepping. Sub-stepping is often used to maintain - /// a fixed sized time step under a variable frame-rate. - /// When you perform sub-stepping you will disable auto clearing of forces and instead call - /// ClearForces after all sub-steps are complete in one pass of your game loop. - /// @see SetAutoClearForces - void ClearForces(); - - /// Call this to draw shapes and other debug draw data. This is intentionally non-const. - void DebugDraw(); - - /// Query the world for all fixtures that potentially overlap the - /// provided AABB. - /// @param callback a user implemented callback class. - /// @param aabb the query box. - void QueryAABB(b2QueryCallback* callback, const b2AABB& aabb) const; - - /// Ray-cast the world for all fixtures in the path of the ray. Your callback - /// controls whether you get the closest point, any point, or n-points. - /// The ray-cast ignores shapes that contain the starting point. - /// @param callback a user implemented callback class. - /// @param point1 the ray starting point - /// @param point2 the ray ending point - void RayCast(b2RayCastCallback* callback, const b2Vec2& point1, const b2Vec2& point2) const; - - /// Get the world body list. With the returned body, use b2Body::GetNext to get - /// the next body in the world list. A nullptr body indicates the end of the list. - /// @return the head of the world body list. - b2Body* GetBodyList(); - const b2Body* GetBodyList() const; - - /// Get the world joint list. With the returned joint, use b2Joint::GetNext to get - /// the next joint in the world list. A nullptr joint indicates the end of the list. - /// @return the head of the world joint list. - b2Joint* GetJointList(); - const b2Joint* GetJointList() const; - - /// Get the world contact list. With the returned contact, use b2Contact::GetNext to get - /// the next contact in the world list. A nullptr contact indicates the end of the list. - /// @return the head of the world contact list. - /// @warning contacts are created and destroyed in the middle of a time step. - /// Use b2ContactListener to avoid missing contacts. - b2Contact* GetContactList(); - const b2Contact* GetContactList() const; - - /// Enable/disable sleep. - void SetAllowSleeping(bool flag); - bool GetAllowSleeping() const { return m_allowSleep; } - - /// Enable/disable warm starting. For testing. - void SetWarmStarting(bool flag) { m_warmStarting = flag; } - bool GetWarmStarting() const { return m_warmStarting; } - - /// Enable/disable continuous physics. For testing. - void SetContinuousPhysics(bool flag) { m_continuousPhysics = flag; } - bool GetContinuousPhysics() const { return m_continuousPhysics; } - - /// Enable/disable single stepped continuous physics. For testing. - void SetSubStepping(bool flag) { m_subStepping = flag; } - bool GetSubStepping() const { return m_subStepping; } - - /// Get the number of broad-phase proxies. - int32 GetProxyCount() const; - - /// Get the number of bodies. - int32 GetBodyCount() const; - - /// Get the number of joints. - int32 GetJointCount() const; - - /// Get the number of contacts (each may have 0 or more contact points). - int32 GetContactCount() const; - - /// Get the height of the dynamic tree. - int32 GetTreeHeight() const; - - /// Get the balance of the dynamic tree. - int32 GetTreeBalance() const; - - /// Get the quality metric of the dynamic tree. The smaller the better. - /// The minimum is 1. - float GetTreeQuality() const; - - /// Change the global gravity vector. - void SetGravity(const b2Vec2& gravity); - - /// Get the global gravity vector. - b2Vec2 GetGravity() const; - - /// Is the world locked (in the middle of a time step). - bool IsLocked() const; - - /// Set flag to control automatic clearing of forces after each time step. - void SetAutoClearForces(bool flag); - - /// Get the flag that controls automatic clearing of forces after each time step. - bool GetAutoClearForces() const; - - /// Shift the world origin. Useful for large worlds. - /// The body shift formula is: position -= newOrigin - /// @param newOrigin the new origin with respect to the old origin - void ShiftOrigin(const b2Vec2& newOrigin); - - /// Get the contact manager for testing. - const b2ContactManager& GetContactManager() const; - - /// Get the current profile. - const b2Profile& GetProfile() const; - - /// Dump the world into the log file. - /// @warning this should be called outside of a time step. - void Dump(); - -private: - - friend class b2Body; - friend class b2Fixture; - friend class b2ContactManager; - friend class b2Controller; - - b2World(const b2World&) = delete; - void operator=(const b2World&) = delete; - - void Solve(const b2TimeStep& step); - void SolveTOI(const b2TimeStep& step); - - void DrawShape(b2Fixture* shape, const b2Transform& xf, const b2Color& color); - - b2BlockAllocator m_blockAllocator; - b2StackAllocator m_stackAllocator; - - b2ContactManager m_contactManager; - - b2Body* m_bodyList; - b2Joint* m_jointList; - - int32 m_bodyCount; - int32 m_jointCount; - - b2Vec2 m_gravity; - bool m_allowSleep; - - b2DestructionListener* m_destructionListener; - b2Draw* m_debugDraw; - - // This is used to compute the time step ratio to - // support a variable time step. - float m_inv_dt0; - - bool m_newContacts; - bool m_locked; - bool m_clearForces; - - // These are for debugging the solver. - bool m_warmStarting; - bool m_continuousPhysics; - bool m_subStepping; - - bool m_stepComplete; - - b2Profile m_profile; -}; - -inline b2Body* b2World::GetBodyList() -{ - return m_bodyList; -} - -inline const b2Body* b2World::GetBodyList() const -{ - return m_bodyList; -} - -inline b2Joint* b2World::GetJointList() -{ - return m_jointList; -} - -inline const b2Joint* b2World::GetJointList() const -{ - return m_jointList; -} - -inline b2Contact* b2World::GetContactList() -{ - return m_contactManager.m_contactList; -} - -inline const b2Contact* b2World::GetContactList() const -{ - return m_contactManager.m_contactList; -} - -inline int32 b2World::GetBodyCount() const -{ - return m_bodyCount; -} - -inline int32 b2World::GetJointCount() const -{ - return m_jointCount; -} - -inline int32 b2World::GetContactCount() const -{ - return m_contactManager.m_contactCount; -} - -inline void b2World::SetGravity(const b2Vec2& gravity) -{ - m_gravity = gravity; -} - -inline b2Vec2 b2World::GetGravity() const -{ - return m_gravity; -} - -inline bool b2World::IsLocked() const -{ - return m_locked; -} - -inline void b2World::SetAutoClearForces(bool flag) -{ - m_clearForces = flag; -} - -/// Get the flag that controls automatic clearing of forces after each time step. -inline bool b2World::GetAutoClearForces() const -{ - return m_clearForces; -} - -inline const b2ContactManager& b2World::GetContactManager() const -{ - return m_contactManager; -} - -inline const b2Profile& b2World::GetProfile() const -{ - return m_profile; -} - -#endif diff --git a/3rdparty/box2d/include/box2d/b2_world_callbacks.h b/3rdparty/box2d/include/box2d/b2_world_callbacks.h deleted file mode 100644 index da45640e2f08..000000000000 --- a/3rdparty/box2d/include/box2d/b2_world_callbacks.h +++ /dev/null @@ -1,161 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_WORLD_CALLBACKS_H -#define B2_WORLD_CALLBACKS_H - -#include "b2_api.h" -#include "b2_settings.h" - -struct b2Vec2; -struct b2Transform; -class b2Fixture; -class b2Body; -class b2Joint; -class b2Contact; -struct b2ContactResult; -struct b2Manifold; - -/// Joints and fixtures are destroyed when their associated -/// body is destroyed. Implement this listener so that you -/// may nullify references to these joints and shapes. -class B2_API b2DestructionListener -{ -public: - virtual ~b2DestructionListener() {} - - /// Called when any joint is about to be destroyed due - /// to the destruction of one of its attached bodies. - virtual void SayGoodbye(b2Joint* joint) = 0; - - /// Called when any fixture is about to be destroyed due - /// to the destruction of its parent body. - virtual void SayGoodbye(b2Fixture* fixture) = 0; -}; - -/// Implement this class to provide collision filtering. In other words, you can implement -/// this class if you want finer control over contact creation. -class B2_API b2ContactFilter -{ -public: - virtual ~b2ContactFilter() {} - - /// Return true if contact calculations should be performed between these two shapes. - /// @warning for performance reasons this is only called when the AABBs begin to overlap. - virtual bool ShouldCollide(b2Fixture* fixtureA, b2Fixture* fixtureB); -}; - -/// Contact impulses for reporting. Impulses are used instead of forces because -/// sub-step forces may approach infinity for rigid body collisions. These -/// match up one-to-one with the contact points in b2Manifold. -struct B2_API b2ContactImpulse -{ - float normalImpulses[b2_maxManifoldPoints]; - float tangentImpulses[b2_maxManifoldPoints]; - int32 count; -}; - -/// Implement this class to get contact information. You can use these results for -/// things like sounds and game logic. You can also get contact results by -/// traversing the contact lists after the time step. However, you might miss -/// some contacts because continuous physics leads to sub-stepping. -/// Additionally you may receive multiple callbacks for the same contact in a -/// single time step. -/// You should strive to make your callbacks efficient because there may be -/// many callbacks per time step. -/// @warning You cannot create/destroy Box2D entities inside these callbacks. -class B2_API b2ContactListener -{ -public: - virtual ~b2ContactListener() {} - - /// Called when two fixtures begin to touch. - virtual void BeginContact(b2Contact* contact) { B2_NOT_USED(contact); } - - /// Called when two fixtures cease to touch. - virtual void EndContact(b2Contact* contact) { B2_NOT_USED(contact); } - - /// This is called after a contact is updated. This allows you to inspect a - /// contact before it goes to the solver. If you are careful, you can modify the - /// contact manifold (e.g. disable contact). - /// A copy of the old manifold is provided so that you can detect changes. - /// Note: this is called only for awake bodies. - /// Note: this is called even when the number of contact points is zero. - /// Note: this is not called for sensors. - /// Note: if you set the number of contact points to zero, you will not - /// get an EndContact callback. However, you may get a BeginContact callback - /// the next step. - virtual void PreSolve(b2Contact* contact, const b2Manifold* oldManifold) - { - B2_NOT_USED(contact); - B2_NOT_USED(oldManifold); - } - - /// This lets you inspect a contact after the solver is finished. This is useful - /// for inspecting impulses. - /// Note: the contact manifold does not include time of impact impulses, which can be - /// arbitrarily large if the sub-step is small. Hence the impulse is provided explicitly - /// in a separate data structure. - /// Note: this is only called for contacts that are touching, solid, and awake. - virtual void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse) - { - B2_NOT_USED(contact); - B2_NOT_USED(impulse); - } -}; - -/// Callback class for AABB queries. -/// See b2World::Query -class B2_API b2QueryCallback -{ -public: - virtual ~b2QueryCallback() {} - - /// Called for each fixture found in the query AABB. - /// @return false to terminate the query. - virtual bool ReportFixture(b2Fixture* fixture) = 0; -}; - -/// Callback class for ray casts. -/// See b2World::RayCast -class B2_API b2RayCastCallback -{ -public: - virtual ~b2RayCastCallback() {} - - /// Called for each fixture found in the query. You control how the ray cast - /// proceeds by returning a float: - /// return -1: ignore this fixture and continue - /// return 0: terminate the ray cast - /// return fraction: clip the ray to this point - /// return 1: don't clip the ray and continue - /// @param fixture the fixture hit by the ray - /// @param point the point of initial intersection - /// @param normal the normal vector at the point of intersection - /// @param fraction the fraction along the ray at the point of intersection - /// @return -1 to filter, 0 to terminate, fraction to clip the ray for - /// closest hit, 1 to continue - virtual float ReportFixture( b2Fixture* fixture, const b2Vec2& point, - const b2Vec2& normal, float fraction) = 0; -}; - -#endif diff --git a/3rdparty/box2d/include/box2d/base.h b/3rdparty/box2d/include/box2d/base.h new file mode 100644 index 000000000000..5d8a7deb36db --- /dev/null +++ b/3rdparty/box2d/include/box2d/base.h @@ -0,0 +1,113 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +// clang-format off +// +// Shared library macros +#if defined( _MSC_VER ) && defined( box2d_EXPORTS ) + // build the Windows DLL + #define BOX2D_EXPORT __declspec( dllexport ) +#elif defined( _MSC_VER ) && defined( BOX2D_DLL ) + // using the Windows DLL + #define BOX2D_EXPORT __declspec( dllimport ) +#elif defined( box2d_EXPORTS ) + // building or using the Box2D shared library + #define BOX2D_EXPORT __attribute__( ( visibility( "default" ) ) ) +#else + // static library + #define BOX2D_EXPORT +#endif + +// C++ macros +#ifdef __cplusplus + #define B2_API extern "C" BOX2D_EXPORT + #define B2_INLINE inline + #define B2_LITERAL(T) T + #define B2_ZERO_INIT {} +#else + #define B2_API BOX2D_EXPORT + #define B2_INLINE static inline + /// Used for C literals like (b2Vec2){1.0f, 2.0f} where C++ requires b2Vec2{1.0f, 2.0f} + #define B2_LITERAL(T) (T) + #define B2_ZERO_INIT {0} +#endif +// clang-format on + +/** + * @defgroup base Base + * Base functionality + * @{ + */ + +/// Prototype for user allocation function +/// @param size the allocation size in bytes +/// @param alignment the required alignment, guaranteed to be a power of 2 +typedef void* b2AllocFcn( unsigned int size, int alignment ); + +/// Prototype for user free function +/// @param mem the memory previously allocated through `b2AllocFcn` +typedef void b2FreeFcn( void* mem ); + +/// Prototype for the user assert callback. Return 0 to skip the debugger break. +typedef int b2AssertFcn( const char* condition, const char* fileName, int lineNumber ); + +/// This allows the user to override the allocation functions. These should be +/// set during application startup. +B2_API void b2SetAllocator( b2AllocFcn* allocFcn, b2FreeFcn* freeFcn ); + +/// @return the total bytes allocated by Box2D +B2_API int b2GetByteCount( void ); + +/// Override the default assert callback +/// @param assertFcn a non-null assert callback +B2_API void b2SetAssertFcn( b2AssertFcn* assertFcn ); + +/// Version numbering scheme. +/// See https://semver.org/ +typedef struct b2Version +{ + /// Significant changes + int major; + + /// Incremental changes + int minor; + + /// Bug fixes + int revision; +} b2Version; + +/// Get the current version of Box2D +B2_API b2Version b2GetVersion( void ); + +/**@}*/ + +//! @cond +// Timer for profiling. This has platform specific code and may not work on every platform. +typedef struct b2Timer +{ +#if defined( _WIN32 ) + int64_t start; +#elif defined( __linux__ ) || defined( __APPLE__ ) + unsigned long long start_sec; + unsigned long long start_usec; +#else + int32_t dummy; +#endif +} b2Timer; + +B2_API b2Timer b2CreateTimer( void ); +B2_API int64_t b2GetTicks( b2Timer* timer ); +B2_API float b2GetMilliseconds( const b2Timer* timer ); +B2_API float b2GetMillisecondsAndReset( b2Timer* timer ); +B2_API void b2SleepMilliseconds( int milliseconds ); +B2_API void b2Yield( void ); + +// Simple djb2 hash function for determinism testing +#define B2_HASH_INIT 5381 +B2_API uint32_t b2Hash( uint32_t hash, const uint8_t* data, int count ); + +//! @endcond diff --git a/3rdparty/box2d/include/box2d/box2d.h b/3rdparty/box2d/include/box2d/box2d.h index 55c695822c71..8d7634be1f2a 100644 --- a/3rdparty/box2d/include/box2d/box2d.h +++ b/3rdparty/box2d/include/box2d/box2d.h @@ -1,58 +1,1098 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef BOX2D_H -#define BOX2D_H - -// These include files constitute the main Box2D API - -#include "b2_settings.h" -#include "b2_draw.h" -#include "b2_timer.h" - -#include "b2_chain_shape.h" -#include "b2_circle_shape.h" -#include "b2_edge_shape.h" -#include "b2_polygon_shape.h" - -#include "b2_broad_phase.h" -#include "b2_dynamic_tree.h" - -#include "b2_body.h" -#include "b2_contact.h" -#include "b2_fixture.h" -#include "b2_time_step.h" -#include "b2_world.h" -#include "b2_world_callbacks.h" - -#include "b2_distance_joint.h" -#include "b2_friction_joint.h" -#include "b2_gear_joint.h" -#include "b2_motor_joint.h" -#include "b2_mouse_joint.h" -#include "b2_prismatic_joint.h" -#include "b2_pulley_joint.h" -#include "b2_revolute_joint.h" -#include "b2_weld_joint.h" -#include "b2_wheel_joint.h" - -#endif +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include "base.h" +#include "collision.h" +#include "id.h" +#include "types.h" + +#include + +/** + * @defgroup world World + * These functions allow you to create a simulation world. + * + * You can add rigid bodies and joint constraints to the world and run the simulation. You can get contact + * information to get contact points and normals as well as events. You can query to world, checking for overlaps and casting rays + * or shapes. There is also debugging information such as debug draw, timing information, and counters. You can find documentation + * here: https://box2d.org/ + * @{ + */ + +/// Create a world for rigid body simulation. A world contains bodies, shapes, and constraints. You make create +/// up to 128 worlds. Each world is completely independent and may be simulated in parallel. +/// @return the world id. +B2_API b2WorldId b2CreateWorld( const b2WorldDef* def ); + +/// Destroy a world +B2_API void b2DestroyWorld( b2WorldId worldId ); + +/// World id validation. Provides validation for up to 64K allocations. +B2_API bool b2World_IsValid( b2WorldId id ); + +/// Simulate a world for one time step. This performs collision detection, integration, and constraint solution. +/// @param worldId The world to simulate +/// @param timeStep The amount of time to simulate, this should be a fixed number. Typically 1/60. +/// @param subStepCount The number of sub-steps, increasing the sub-step count can increase accuracy. Typically 4. +B2_API void b2World_Step( b2WorldId worldId, float timeStep, int subStepCount ); + +/// Call this to draw shapes and other debug draw data +B2_API void b2World_Draw( b2WorldId worldId, b2DebugDraw* draw ); + +/// Get the body events for the current time step. The event data is transient. Do not store a reference to this data. +B2_API b2BodyEvents b2World_GetBodyEvents( b2WorldId worldId ); + +/// Get sensor events for the current time step. The event data is transient. Do not store a reference to this data. +B2_API b2SensorEvents b2World_GetSensorEvents( b2WorldId worldId ); + +/// Get contact events for this current time step. The event data is transient. Do not store a reference to this data. +B2_API b2ContactEvents b2World_GetContactEvents( b2WorldId worldId ); + +/// Overlap test for all shapes that *potentially* overlap the provided AABB +B2_API void b2World_OverlapAABB( b2WorldId worldId, b2AABB aabb, b2QueryFilter filter, b2OverlapResultFcn* fcn, void* context ); + +/// Overlap test for for all shapes that overlap the provided circle +B2_API void b2World_OverlapCircle( b2WorldId worldId, const b2Circle* circle, b2Transform transform, b2QueryFilter filter, + b2OverlapResultFcn* fcn, void* context ); + +/// Overlap test for all shapes that overlap the provided capsule +B2_API void b2World_OverlapCapsule( b2WorldId worldId, const b2Capsule* capsule, b2Transform transform, b2QueryFilter filter, + b2OverlapResultFcn* fcn, void* context ); + +/// Overlap test for all shapes that overlap the provided polygon +B2_API void b2World_OverlapPolygon( b2WorldId worldId, const b2Polygon* polygon, b2Transform transform, b2QueryFilter filter, + b2OverlapResultFcn* fcn, void* context ); + +/// Cast a ray into the world to collect shapes in the path of the ray. +/// Your callback function controls whether you get the closest point, any point, or n-points. +/// The ray-cast ignores shapes that contain the starting point. +/// @param worldId The world to cast the ray against +/// @param origin The start point of the ray +/// @param translation The translation of the ray from the start point to the end point +/// @param filter Contains bit flags to filter unwanted shapes from the results +/// @param fcn A user implemented callback function +/// @param context A user context that is passed along to the callback function +/// @note The callback function may receive shapes in any order +B2_API void b2World_CastRay( b2WorldId worldId, b2Vec2 origin, b2Vec2 translation, b2QueryFilter filter, b2CastResultFcn* fcn, + void* context ); + +/// Cast a ray into the world to collect the closest hit. This is a convenience function. +/// This is less general than b2World_CastRay() and does not allow for custom filtering. +B2_API b2RayResult b2World_CastRayClosest( b2WorldId worldId, b2Vec2 origin, b2Vec2 translation, b2QueryFilter filter ); + +/// Cast a circle through the world. Similar to a cast ray except that a circle is cast instead of a point. +B2_API void b2World_CastCircle( b2WorldId worldId, const b2Circle* circle, b2Transform originTransform, b2Vec2 translation, + b2QueryFilter filter, b2CastResultFcn* fcn, void* context ); + +/// Cast a capsule through the world. Similar to a cast ray except that a capsule is cast instead of a point. +B2_API void b2World_CastCapsule( b2WorldId worldId, const b2Capsule* capsule, b2Transform originTransform, b2Vec2 translation, + b2QueryFilter filter, b2CastResultFcn* fcn, void* context ); + +/// Cast a polygon through the world. Similar to a cast ray except that a polygon is cast instead of a point. +B2_API void b2World_CastPolygon( b2WorldId worldId, const b2Polygon* polygon, b2Transform originTransform, b2Vec2 translation, + b2QueryFilter filter, b2CastResultFcn* fcn, void* context ); + +/// Enable/disable sleep. If your application does not need sleeping, you can gain some performance +/// by disabling sleep completely at the world level. +/// @see b2WorldDef +B2_API void b2World_EnableSleeping( b2WorldId worldId, bool flag ); + +/// Is body sleeping enabled? +B2_API bool b2World_IsSleepingEnabled( b2WorldId worldId ); + +/// Enable/disable continuous collision between dynamic and static bodies. Generally you should keep continuous +/// collision enabled to prevent fast moving objects from going through static objects. The performance gain from +/// disabling continuous collision is minor. +/// @see b2WorldDef +B2_API void b2World_EnableContinuous( b2WorldId worldId, bool flag ); + +/// Is continuous collision enabled? +B2_API bool b2World_IsContinuousEnabled( b2WorldId worldId ); + +/// Adjust the restitution threshold. It is recommended not to make this value very small +/// because it will prevent bodies from sleeping. Typically in meters per second. +/// @see b2WorldDef +B2_API void b2World_SetRestitutionThreshold( b2WorldId worldId, float value ); + +/// Get the the restitution speed threshold. Typically in meters per second. +B2_API float b2World_GetRestitutionThreshold( b2WorldId worldId ); + +/// Adjust the hit event threshold. This controls the collision velocity needed to generate a b2ContactHitEvent. +/// Typically in meters per second. +/// @see b2WorldDef::hitEventThreshold +B2_API void b2World_SetHitEventThreshold( b2WorldId worldId, float value ); + +/// Get the the hit event speed threshold. Typically in meters per second. +B2_API float b2World_GetHitEventThreshold( b2WorldId worldId ); + +/// Register the custom filter callback. This is optional. +B2_API void b2World_SetCustomFilterCallback( b2WorldId worldId, b2CustomFilterFcn* fcn, void* context ); + +/// Register the pre-solve callback. This is optional. +B2_API void b2World_SetPreSolveCallback( b2WorldId worldId, b2PreSolveFcn* fcn, void* context ); + +/// Set the gravity vector for the entire world. Box2D has no concept of an up direction and this +/// is left as a decision for the application. Typically in m/s^2. +/// @see b2WorldDef +B2_API void b2World_SetGravity( b2WorldId worldId, b2Vec2 gravity ); + +/// Get the gravity vector +B2_API b2Vec2 b2World_GetGravity( b2WorldId worldId ); + +/// Apply a radial explosion +/// @param worldId The world id +/// @param position The center of the explosion +/// @param radius The radius of the explosion +/// @param impulse The impulse of the explosion, typically in kg * m / s or N * s. +B2_API void b2World_Explode( b2WorldId worldId, b2Vec2 position, float radius, float impulse ); + +/// Adjust contact tuning parameters +/// @param worldId The world id +/// @param hertz The contact stiffness (cycles per second) +/// @param dampingRatio The contact bounciness with 1 being critical damping (non-dimensional) +/// @param pushVelocity The maximum contact constraint push out velocity (meters per second) +/// @note Advanced feature +B2_API void b2World_SetContactTuning( b2WorldId worldId, float hertz, float dampingRatio, float pushVelocity ); + +/// Enable/disable constraint warm starting. Advanced feature for testing. Disabling +/// sleeping greatly reduces stability and provides no performance gain. +B2_API void b2World_EnableWarmStarting( b2WorldId worldId, bool flag ); + +/// Is constraint warm starting enabled? +B2_API bool b2World_IsWarmStartingEnabled( b2WorldId worldId ); + +/// Get the current world performance profile +B2_API b2Profile b2World_GetProfile( b2WorldId worldId ); + +/// Get world counters and sizes +B2_API b2Counters b2World_GetCounters( b2WorldId worldId ); + +/// Dump memory stats to box2d_memory.txt +B2_API void b2World_DumpMemoryStats( b2WorldId worldId ); + +/** @} */ + +/** + * @defgroup body Body + * This is the body API. + * @{ + */ + +/// Create a rigid body given a definition. No reference to the definition is retained. So you can create the definition +/// on the stack and pass it as a pointer. +/// @code{.c} +/// b2BodyDef bodyDef = b2DefaultBodyDef(); +/// b2BodyId myBodyId = b2CreateBody(myWorldId, &bodyDef); +/// @endcode +/// @warning This function is locked during callbacks. +B2_API b2BodyId b2CreateBody( b2WorldId worldId, const b2BodyDef* def ); + +/// Destroy a rigid body given an id. This destroys all shapes and joints attached to the body. +/// Do not keep references to the associated shapes and joints. +B2_API void b2DestroyBody( b2BodyId bodyId ); + +/// Body identifier validation. Can be used to detect orphaned ids. Provides validation for up to 64K allocations. +B2_API bool b2Body_IsValid( b2BodyId id ); + +/// Get the body type: static, kinematic, or dynamic +B2_API b2BodyType b2Body_GetType( b2BodyId bodyId ); + +/// Change the body type. This is an expensive operation. This automatically updates the mass +/// properties regardless of the automatic mass setting. +B2_API void b2Body_SetType( b2BodyId bodyId, b2BodyType type ); + +/// Set the user data for a body +B2_API void b2Body_SetUserData( b2BodyId bodyId, void* userData ); + +/// Get the user data stored in a body +B2_API void* b2Body_GetUserData( b2BodyId bodyId ); + +/// Get the world position of a body. This is the location of the body origin. +B2_API b2Vec2 b2Body_GetPosition( b2BodyId bodyId ); + +/// Get the world rotation of a body as a cosine/sine pair (complex number) +B2_API b2Rot b2Body_GetRotation( b2BodyId bodyId ); + +/// Get the world transform of a body. +B2_API b2Transform b2Body_GetTransform( b2BodyId bodyId ); + +/// Set the world transform of a body. This acts as a teleport and is fairly expensive. +/// @note Generally you should create a body with then intended transform. +/// @see b2BodyDef::position and b2BodyDef::angle +B2_API void b2Body_SetTransform( b2BodyId bodyId, b2Vec2 position, b2Rot rotation ); + +/// Get a local point on a body given a world point +B2_API b2Vec2 b2Body_GetLocalPoint( b2BodyId bodyId, b2Vec2 worldPoint ); + +/// Get a world point on a body given a local point +B2_API b2Vec2 b2Body_GetWorldPoint( b2BodyId bodyId, b2Vec2 localPoint ); + +/// Get a local vector on a body given a world vector +B2_API b2Vec2 b2Body_GetLocalVector( b2BodyId bodyId, b2Vec2 worldVector ); + +/// Get a world vector on a body given a local vector +B2_API b2Vec2 b2Body_GetWorldVector( b2BodyId bodyId, b2Vec2 localVector ); + +/// Get the linear velocity of a body's center of mass. Typically in meters per second. +B2_API b2Vec2 b2Body_GetLinearVelocity( b2BodyId bodyId ); + +/// Get the angular velocity of a body in radians per second +B2_API float b2Body_GetAngularVelocity( b2BodyId bodyId ); + +/// Set the linear velocity of a body. Typically in meters per second. +B2_API void b2Body_SetLinearVelocity( b2BodyId bodyId, b2Vec2 linearVelocity ); + +/// Set the angular velocity of a body in radians per second +B2_API void b2Body_SetAngularVelocity( b2BodyId bodyId, float angularVelocity ); + +/// Apply a force at a world point. If the force is not applied at the center of mass, +/// it will generate a torque and affect the angular velocity. This optionally wakes up the body. +/// The force is ignored if the body is not awake. +/// @param bodyId The body id +/// @param force The world force vector, typically in newtons (N) +/// @param point The world position of the point of application +/// @param wake Option to wake up the body +B2_API void b2Body_ApplyForce( b2BodyId bodyId, b2Vec2 force, b2Vec2 point, bool wake ); + +/// Apply a force to the center of mass. This optionally wakes up the body. +/// The force is ignored if the body is not awake. +/// @param bodyId The body id +/// @param force the world force vector, usually in newtons (N). +/// @param wake also wake up the body +B2_API void b2Body_ApplyForceToCenter( b2BodyId bodyId, b2Vec2 force, bool wake ); + +/// Apply a torque. This affects the angular velocity without affecting the linear velocity. +/// This optionally wakes the body. The torque is ignored if the body is not awake. +/// @param bodyId The body id +/// @param torque about the z-axis (out of the screen), typically in N*m. +/// @param wake also wake up the body +B2_API void b2Body_ApplyTorque( b2BodyId bodyId, float torque, bool wake ); + +/// Apply an impulse at a point. This immediately modifies the velocity. +/// It also modifies the angular velocity if the point of application +/// is not at the center of mass. This optionally wakes the body. +/// The impulse is ignored if the body is not awake. +/// @param bodyId The body id +/// @param impulse the world impulse vector, typically in N*s or kg*m/s. +/// @param point the world position of the point of application. +/// @param wake also wake up the body +/// @warning This should be used for one-shot impulses. If you need a steady force, +/// use a force instead, which will work better with the sub-stepping solver. +B2_API void b2Body_ApplyLinearImpulse( b2BodyId bodyId, b2Vec2 impulse, b2Vec2 point, bool wake ); + +/// Apply an impulse to the center of mass. This immediately modifies the velocity. +/// The impulse is ignored if the body is not awake. This optionally wakes the body. +/// @param bodyId The body id +/// @param impulse the world impulse vector, typically in N*s or kg*m/s. +/// @param wake also wake up the body +/// @warning This should be used for one-shot impulses. If you need a steady force, +/// use a force instead, which will work better with the sub-stepping solver. +B2_API void b2Body_ApplyLinearImpulseToCenter( b2BodyId bodyId, b2Vec2 impulse, bool wake ); + +/// Apply an angular impulse. The impulse is ignored if the body is not awake. +/// This optionally wakes the body. +/// @param bodyId The body id +/// @param impulse the angular impulse, typically in units of kg*m*m/s +/// @param wake also wake up the body +/// @warning This should be used for one-shot impulses. If you need a steady force, +/// use a force instead, which will work better with the sub-stepping solver. +B2_API void b2Body_ApplyAngularImpulse( b2BodyId bodyId, float impulse, bool wake ); + +/// Get the mass of the body, typically in kilograms +B2_API float b2Body_GetMass( b2BodyId bodyId ); + +/// Get the rotational inertia of the body, typically in kg*m^2 +B2_API float b2Body_GetRotationalInertia( b2BodyId bodyId ); + +/// Get the center of mass position of the body in local space +B2_API b2Vec2 b2Body_GetLocalCenterOfMass( b2BodyId bodyId ); + +/// Get the center of mass position of the body in world space +B2_API b2Vec2 b2Body_GetWorldCenterOfMass( b2BodyId bodyId ); + +/// Override the body's mass properties. Normally this is computed automatically using the +/// shape geometry and density. This information is lost if a shape is added or removed or if the +/// body type changes. +B2_API void b2Body_SetMassData( b2BodyId bodyId, b2MassData massData ); + +/// Get the mass data for a body +B2_API b2MassData b2Body_GetMassData( b2BodyId bodyId ); + +/// This update the mass properties to the sum of the mass properties of the shapes. +/// This normally does not need to be called unless you called SetMassData to override +/// the mass and you later want to reset the mass. +/// You may also use this when automatic mass computation has been disabled. +/// You should call this regardless of body type. +B2_API void b2Body_ApplyMassFromShapes( b2BodyId bodyId ); + +/// Set the automatic mass setting. Normally this is set in b2BodyDef before creation. +/// @see b2BodyDef::automaticMass +B2_API void b2Body_SetAutomaticMass( b2BodyId bodyId, bool automaticMass ); + +/// Get the automatic mass setting +B2_API bool b2Body_GetAutomaticMass( b2BodyId bodyId ); + +/// Adjust the linear damping. Normally this is set in b2BodyDef before creation. +B2_API void b2Body_SetLinearDamping( b2BodyId bodyId, float linearDamping ); + +/// Get the current linear damping. +B2_API float b2Body_GetLinearDamping( b2BodyId bodyId ); + +/// Adjust the angular damping. Normally this is set in b2BodyDef before creation. +B2_API void b2Body_SetAngularDamping( b2BodyId bodyId, float angularDamping ); + +/// Get the current angular damping. +B2_API float b2Body_GetAngularDamping( b2BodyId bodyId ); + +/// Adjust the gravity scale. Normally this is set in b2BodyDef before creation. +/// @see b2BodyDef::gravityScale +B2_API void b2Body_SetGravityScale( b2BodyId bodyId, float gravityScale ); + +/// Get the current gravity scale +B2_API float b2Body_GetGravityScale( b2BodyId bodyId ); + +/// @return true if this body is awake +B2_API bool b2Body_IsAwake( b2BodyId bodyId ); + +/// Wake a body from sleep. This wakes the entire island the body is touching. +/// @warning Putting a body to sleep will put the entire island of bodies touching this body to sleep, +/// which can be expensive and possibly unintuitive. +B2_API void b2Body_SetAwake( b2BodyId bodyId, bool awake ); + +/// Enable or disable sleeping for this body. If sleeping is disabled the body will wake. +B2_API void b2Body_EnableSleep( b2BodyId bodyId, bool enableSleep ); + +/// Returns true if sleeping is enabled for this body +B2_API bool b2Body_IsSleepEnabled( b2BodyId bodyId ); + +/// Set the sleep threshold, typically in meters per second +B2_API void b2Body_SetSleepThreshold( b2BodyId bodyId, float sleepThreshold ); + +/// Get the sleep threshold, typically in meters per second. +B2_API float b2Body_GetSleepThreshold( b2BodyId bodyId ); + +/// Returns true if this body is enabled +B2_API bool b2Body_IsEnabled( b2BodyId bodyId ); + +/// Disable a body by removing it completely from the simulation. This is expensive. +B2_API void b2Body_Disable( b2BodyId bodyId ); + +/// Enable a body by adding it to the simulation. This is expensive. +B2_API void b2Body_Enable( b2BodyId bodyId ); + +/// Set this body to have fixed rotation. This causes the mass to be reset in all cases. +B2_API void b2Body_SetFixedRotation( b2BodyId bodyId, bool flag ); + +/// Does this body have fixed rotation? +B2_API bool b2Body_IsFixedRotation( b2BodyId bodyId ); + +/// Set this body to be a bullet. A bullet does continuous collision detection +/// against dynamic bodies (but not other bullets). +B2_API void b2Body_SetBullet( b2BodyId bodyId, bool flag ); + +/// Is this body a bullet? +B2_API bool b2Body_IsBullet( b2BodyId bodyId ); + +/// Enable/disable hit events on all shapes +/// @see b2ShapeDef::enableHitEvents +B2_API void b2Body_EnableHitEvents( b2BodyId bodyId, bool enableHitEvents ); + +/// Get the world that owns this body +B2_API b2WorldId b2Body_GetWorld( b2BodyId bodyId ); + +/// Get the number of shapes on this body +B2_API int b2Body_GetShapeCount( b2BodyId bodyId ); + +/// Get the shape ids for all shapes on this body, up to the provided capacity. +/// @returns the number of shape ids stored in the user array +B2_API int b2Body_GetShapes( b2BodyId bodyId, b2ShapeId* shapeArray, int capacity ); + +/// Get the number of joints on this body +B2_API int b2Body_GetJointCount( b2BodyId bodyId ); + +/// Get the joint ids for all joints on this body, up to the provided capacity +/// @returns the number of joint ids stored in the user array +B2_API int b2Body_GetJoints( b2BodyId bodyId, b2JointId* jointArray, int capacity ); + +/// Get the maximum capacity required for retrieving all the touching contacts on a body +B2_API int b2Body_GetContactCapacity( b2BodyId bodyId ); + +/// Get the touching contact data for a body +B2_API int b2Body_GetContactData( b2BodyId bodyId, b2ContactData* contactData, int capacity ); + +/// Get the current world AABB that contains all the attached shapes. Note that this may not encompass the body origin. +/// If there are no shapes attached then the returned AABB is empty and centered on the body origin. +B2_API b2AABB b2Body_ComputeAABB( b2BodyId bodyId ); + +/** @} */ + +/** + * @defgroup shape Shape + * Functions to create, destroy, and access. + * Shapes bind raw geometry to bodies and hold material properties including friction and restitution. + * @{ + */ + +/// Create a circle shape and attach it to a body. The shape definition and geometry are fully cloned. +/// Contacts are not created until the next time step. +/// @return the shape id for accessing the shape +B2_API b2ShapeId b2CreateCircleShape( b2BodyId bodyId, const b2ShapeDef* def, const b2Circle* circle ); + +/// Create a line segment shape and attach it to a body. The shape definition and geometry are fully cloned. +/// Contacts are not created until the next time step. +/// @return the shape id for accessing the shape +B2_API b2ShapeId b2CreateSegmentShape( b2BodyId bodyId, const b2ShapeDef* def, const b2Segment* segment ); + +/// Create a capsule shape and attach it to a body. The shape definition and geometry are fully cloned. +/// Contacts are not created until the next time step. +/// @return the shape id for accessing the shape +B2_API b2ShapeId b2CreateCapsuleShape( b2BodyId bodyId, const b2ShapeDef* def, const b2Capsule* capsule ); + +/// Create a polygon shape and attach it to a body. The shape definition and geometry are fully cloned. +/// Contacts are not created until the next time step. +/// @return the shape id for accessing the shape +B2_API b2ShapeId b2CreatePolygonShape( b2BodyId bodyId, const b2ShapeDef* def, const b2Polygon* polygon ); + +/// Destroy a shape +B2_API void b2DestroyShape( b2ShapeId shapeId ); + +/// Shape identifier validation. Provides validation for up to 64K allocations. +B2_API bool b2Shape_IsValid( b2ShapeId id ); + +/// Get the type of a shape +B2_API b2ShapeType b2Shape_GetType( b2ShapeId shapeId ); + +/// Get the id of the body that a shape is attached to +B2_API b2BodyId b2Shape_GetBody( b2ShapeId shapeId ); + +/// Get the world that owns this shape +B2_API b2WorldId b2Shape_GetWorld( b2ShapeId shapeId ); + +/// Returns true If the shape is a sensor +B2_API bool b2Shape_IsSensor( b2ShapeId shapeId ); + +/// Set the user data for a shape +B2_API void b2Shape_SetUserData( b2ShapeId shapeId, void* userData ); + +/// Get the user data for a shape. This is useful when you get a shape id +/// from an event or query. +B2_API void* b2Shape_GetUserData( b2ShapeId shapeId ); + +/// Set the mass density of a shape, typically in kg/m^2. +/// This will not update the mass properties on the parent body. +/// @see b2ShapeDef::density, b2Body_ApplyMassFromShapes +B2_API void b2Shape_SetDensity( b2ShapeId shapeId, float density ); + +/// Get the density of a shape, typically in kg/m^2 +B2_API float b2Shape_GetDensity( b2ShapeId shapeId ); + +/// Set the friction on a shape +/// @see b2ShapeDef::friction +B2_API void b2Shape_SetFriction( b2ShapeId shapeId, float friction ); + +/// Get the friction of a shape +B2_API float b2Shape_GetFriction( b2ShapeId shapeId ); + +/// Set the shape restitution (bounciness) +/// @see b2ShapeDef::restitution +B2_API void b2Shape_SetRestitution( b2ShapeId shapeId, float restitution ); + +/// Get the shape restitution +B2_API float b2Shape_GetRestitution( b2ShapeId shapeId ); + +/// Get the shape filter +B2_API b2Filter b2Shape_GetFilter( b2ShapeId shapeId ); + +/// Set the current filter. This is almost as expensive as recreating the shape. +/// @see b2ShapeDef::filter +B2_API void b2Shape_SetFilter( b2ShapeId shapeId, b2Filter filter ); + +/// Enable sensor events for this shape. Only applies to kinematic and dynamic bodies. Ignored for sensors. +/// @see b2ShapeDef::isSensor +B2_API void b2Shape_EnableSensorEvents( b2ShapeId shapeId, bool flag ); + +/// Returns true if sensor events are enabled +B2_API bool b2Shape_AreSensorEventsEnabled( b2ShapeId shapeId ); + +/// Enable contact events for this shape. Only applies to kinematic and dynamic bodies. Ignored for sensors. +/// @see b2ShapeDef::enableContactEvents +B2_API void b2Shape_EnableContactEvents( b2ShapeId shapeId, bool flag ); + +/// Returns true if contact events are enabled +B2_API bool b2Shape_AreContactEventsEnabled( b2ShapeId shapeId ); + +/// Enable pre-solve contact events for this shape. Only applies to dynamic bodies. These are expensive +/// and must be carefully handled due to multithreading. Ignored for sensors. +/// @see b2PreSolveFcn +B2_API void b2Shape_EnablePreSolveEvents( b2ShapeId shapeId, bool flag ); + +/// Returns true if pre-solve events are enabled +B2_API bool b2Shape_ArePreSolveEventsEnabled( b2ShapeId shapeId ); + +/// Enable contact hit events for this shape. Ignored for sensors. +/// @see b2WorldDef.hitEventThreshold +B2_API void b2Shape_EnableHitEvents( b2ShapeId shapeId, bool flag ); + +/// Returns true if hit events are enabled +B2_API bool b2Shape_AreHitEventsEnabled( b2ShapeId shapeId ); + +/// Test a point for overlap with a shape +B2_API bool b2Shape_TestPoint( b2ShapeId shapeId, b2Vec2 point ); + +/// Ray cast a shape directly +B2_API b2CastOutput b2Shape_RayCast( b2ShapeId shapeId, const b2RayCastInput* input ); + +/// Get a copy of the shape's circle. Asserts the type is correct. +B2_API b2Circle b2Shape_GetCircle( b2ShapeId shapeId ); + +/// Get a copy of the shape's line segment. Asserts the type is correct. +B2_API b2Segment b2Shape_GetSegment( b2ShapeId shapeId ); + +/// Get a copy of the shape's chain segment. These come from chain shapes. +/// Asserts the type is correct. +B2_API b2ChainSegment b2Shape_GetChainSegment( b2ShapeId shapeId ); + +/// Get a copy of the shape's capsule. Asserts the type is correct. +B2_API b2Capsule b2Shape_GetCapsule( b2ShapeId shapeId ); + +/// Get a copy of the shape's convex polygon. Asserts the type is correct. +B2_API b2Polygon b2Shape_GetPolygon( b2ShapeId shapeId ); + +/// Allows you to change a shape to be a circle or update the current circle. +/// This does not modify the mass properties. +/// @see b2Body_ApplyMassFromShapes +B2_API void b2Shape_SetCircle( b2ShapeId shapeId, const b2Circle* circle ); + +/// Allows you to change a shape to be a capsule or update the current capsule. +/// This does not modify the mass properties. +/// @see b2Body_ApplyMassFromShapes +B2_API void b2Shape_SetCapsule( b2ShapeId shapeId, const b2Capsule* capsule ); + +/// Allows you to change a shape to be a segment or update the current segment. +B2_API void b2Shape_SetSegment( b2ShapeId shapeId, const b2Segment* segment ); + +/// Allows you to change a shape to be a polygon or update the current polygon. +/// This does not modify the mass properties. +/// @see b2Body_ApplyMassFromShapes +B2_API void b2Shape_SetPolygon( b2ShapeId shapeId, const b2Polygon* polygon ); + +/// Get the parent chain id if the shape type is a chain segment, otherwise +/// returns b2_nullChainId. +B2_API b2ChainId b2Shape_GetParentChain( b2ShapeId shapeId ); + +/// Get the maximum capacity required for retrieving all the touching contacts on a shape +B2_API int b2Shape_GetContactCapacity( b2ShapeId shapeId ); + +/// Get the touching contact data for a shape. The provided shapeId will be either shapeIdA or shapeIdB on the contact data. +B2_API int b2Shape_GetContactData( b2ShapeId shapeId, b2ContactData* contactData, int capacity ); + +/// Get the current world AABB +B2_API b2AABB b2Shape_GetAABB( b2ShapeId shapeId ); + +/// Get the closest point on a shape to a target point. Target and result are in world space. +B2_API b2Vec2 b2Shape_GetClosestPoint( b2ShapeId shapeId, b2Vec2 target ); + +/// Chain Shape + +/// Create a chain shape +/// @see b2ChainDef for details +B2_API b2ChainId b2CreateChain( b2BodyId bodyId, const b2ChainDef* def ); + +/// Destroy a chain shape +B2_API void b2DestroyChain( b2ChainId chainId ); + +/// Get the world that owns this chain shape +B2_API b2WorldId b2Chain_GetWorld( b2ChainId chainId ); + +/// Get the number of segments on this chain +B2_API int b2Chain_GetSegmentCount( b2ChainId chainId ); + +/// Fill a user array with chain segment shape ids up to the specified capacity. Returns +/// the actual number of segments returned. +B2_API int b2Chain_GetSegments( b2ChainId chainId, b2ShapeId* segmentArray, int capacity ); + +/// Set the chain friction +/// @see b2ChainDef::friction +B2_API void b2Chain_SetFriction( b2ChainId chainId, float friction ); + +/// Set the chain restitution (bounciness) +/// @see b2ChainDef::restitution +B2_API void b2Chain_SetRestitution( b2ChainId chainId, float restitution ); + +/// Chain identifier validation. Provides validation for up to 64K allocations. +B2_API bool b2Chain_IsValid( b2ChainId id ); + +/** @} */ + +/** + * @defgroup joint Joint + * @brief Joints allow you to connect rigid bodies together while allowing various forms of relative motions. + * @{ + */ + +/// Destroy a joint +B2_API void b2DestroyJoint( b2JointId jointId ); + +/// Joint identifier validation. Provides validation for up to 64K allocations. +B2_API bool b2Joint_IsValid( b2JointId id ); + +/// Get the joint type +B2_API b2JointType b2Joint_GetType( b2JointId jointId ); + +/// Get body A id on a joint +B2_API b2BodyId b2Joint_GetBodyA( b2JointId jointId ); + +/// Get body B id on a joint +B2_API b2BodyId b2Joint_GetBodyB( b2JointId jointId ); + +/// Get the world that owns this joint +B2_API b2WorldId b2Joint_GetWorld( b2JointId jointId ); + +/// Get the local anchor on bodyA +B2_API b2Vec2 b2Joint_GetLocalAnchorA( b2JointId jointId ); + +/// Get the local anchor on bodyB +B2_API b2Vec2 b2Joint_GetLocalAnchorB( b2JointId jointId ); + +/// Toggle collision between connected bodies +B2_API void b2Joint_SetCollideConnected( b2JointId jointId, bool shouldCollide ); + +/// Is collision allowed between connected bodies? +B2_API bool b2Joint_GetCollideConnected( b2JointId jointId ); + +/// Set the user data on a joint +B2_API void b2Joint_SetUserData( b2JointId jointId, void* userData ); + +/// Get the user data on a joint +B2_API void* b2Joint_GetUserData( b2JointId jointId ); + +/// Wake the bodies connect to this joint +B2_API void b2Joint_WakeBodies( b2JointId jointId ); + +/// Get the current constraint force for this joint +B2_API b2Vec2 b2Joint_GetConstraintForce( b2JointId jointId ); + +/// Get the current constraint torque for this joint +B2_API float b2Joint_GetConstraintTorque( b2JointId jointId ); + +/** + * @defgroup distance_joint Distance Joint + * @brief Functions for the distance joint. + * @{ + */ + +/// Create a distance joint +/// @see b2DistanceJointDef for details +B2_API b2JointId b2CreateDistanceJoint( b2WorldId worldId, const b2DistanceJointDef* def ); + +/// Set the rest length of a distance joint +/// @param jointId The id for a distance joint +/// @param length The new distance joint length +B2_API void b2DistanceJoint_SetLength( b2JointId jointId, float length ); + +/// Get the rest length of a distance joint +B2_API float b2DistanceJoint_GetLength( b2JointId jointId ); + +/// Enable/disable the distance joint spring. When disabled the distance joint is rigid. +B2_API void b2DistanceJoint_EnableSpring( b2JointId jointId, bool enableSpring ); + +/// Is the distance joint spring enabled? +B2_API bool b2DistanceJoint_IsSpringEnabled( b2JointId jointId ); + +/// Set the spring stiffness in Hertz +B2_API void b2DistanceJoint_SetSpringHertz( b2JointId jointId, float hertz ); + +/// Set the spring damping ratio, non-dimensional +B2_API void b2DistanceJoint_SetSpringDampingRatio( b2JointId jointId, float dampingRatio ); + +/// Get the spring Hertz +B2_API float b2DistanceJoint_GetSpringHertz( b2JointId jointId ); + +/// Get the spring damping ratio +B2_API float b2DistanceJoint_GetSpringDampingRatio( b2JointId jointId ); + +/// Enable joint limit. The limit only works if the joint spring is enabled. Otherwise the joint is rigid +/// and the limit has no effect. +B2_API void b2DistanceJoint_EnableLimit( b2JointId jointId, bool enableLimit ); + +/// Is the distance joint limit enabled? +B2_API bool b2DistanceJoint_IsLimitEnabled( b2JointId jointId ); + +/// Set the minimum and maximum length parameters of a distance joint +B2_API void b2DistanceJoint_SetLengthRange( b2JointId jointId, float minLength, float maxLength ); + +/// Get the distance joint minimum length +B2_API float b2DistanceJoint_GetMinLength( b2JointId jointId ); + +/// Get the distance joint maximum length +B2_API float b2DistanceJoint_GetMaxLength( b2JointId jointId ); + +/// Get the current length of a distance joint +B2_API float b2DistanceJoint_GetCurrentLength( b2JointId jointId ); + +/// Enable/disable the distance joint motor +B2_API void b2DistanceJoint_EnableMotor( b2JointId jointId, bool enableMotor ); + +/// Is the distance joint motor enabled? +B2_API bool b2DistanceJoint_IsMotorEnabled( b2JointId jointId ); + +/// Set the distance joint motor speed, typically in meters per second +B2_API void b2DistanceJoint_SetMotorSpeed( b2JointId jointId, float motorSpeed ); + +/// Get the distance joint motor speed, typically in meters per second +B2_API float b2DistanceJoint_GetMotorSpeed( b2JointId jointId ); + +/// Set the distance joint maximum motor force, typically in newtons +B2_API void b2DistanceJoint_SetMaxMotorForce( b2JointId jointId, float force ); + +/// Get the distance joint maximum motor force, typically in newtons +B2_API float b2DistanceJoint_GetMaxMotorForce( b2JointId jointId ); + +/// Get the distance joint current motor force, typically in newtons +B2_API float b2DistanceJoint_GetMotorForce( b2JointId jointId ); + +/** @} */ + +/** + * @defgroup motor_joint Motor Joint + * @brief Functions for the motor joint. + * + * The motor joint is used to drive the relative transform between two bodies. It takes + * a relative position and rotation and applies the forces and torques needed to achieve + * that relative transform over time. + * @{ + */ + +/// Create a motor joint +/// @see b2MotorJointDef for details +B2_API b2JointId b2CreateMotorJoint( b2WorldId worldId, const b2MotorJointDef* def ); + +/// Set the motor joint linear offset target +B2_API void b2MotorJoint_SetLinearOffset( b2JointId jointId, b2Vec2 linearOffset ); + +/// Get the motor joint linear offset target +B2_API b2Vec2 b2MotorJoint_GetLinearOffset( b2JointId jointId ); + +/// Set the motor joint angular offset target in radians +B2_API void b2MotorJoint_SetAngularOffset( b2JointId jointId, float angularOffset ); + +/// Get the motor joint angular offset target in radians +B2_API float b2MotorJoint_GetAngularOffset( b2JointId jointId ); + +/// Set the motor joint maximum force, typically in newtons +B2_API void b2MotorJoint_SetMaxForce( b2JointId jointId, float maxForce ); + +/// Get the motor joint maximum force, typically in newtons +B2_API float b2MotorJoint_GetMaxForce( b2JointId jointId ); + +/// Set the motor joint maximum torque, typically in newton-meters +B2_API void b2MotorJoint_SetMaxTorque( b2JointId jointId, float maxTorque ); + +/// Get the motor joint maximum torque, typically in newton-meters +B2_API float b2MotorJoint_GetMaxTorque( b2JointId jointId ); + +/// Set the motor joint correction factor, typically in [0, 1] +B2_API void b2MotorJoint_SetCorrectionFactor( b2JointId jointId, float correctionFactor ); + +/// Get the motor joint correction factor, typically in [0, 1] +B2_API float b2MotorJoint_GetCorrectionFactor( b2JointId jointId ); + +/**@}*/ + +/** + * @defgroup mouse_joint Mouse Joint + * @brief Functions for the mouse joint. + * + * The mouse joint is designed for use in the samples application, but you may find it useful in applications where + * the user moves a rigid body with a cursor. + * @{ + */ + +/// Create a mouse joint +/// @see b2MouseJointDef for details +B2_API b2JointId b2CreateMouseJoint( b2WorldId worldId, const b2MouseJointDef* def ); + +/// Set the mouse joint target +B2_API void b2MouseJoint_SetTarget( b2JointId jointId, b2Vec2 target ); + +/// Get the mouse joint target +B2_API b2Vec2 b2MouseJoint_GetTarget( b2JointId jointId ); + +/// Set the mouse joint spring stiffness in Hertz +B2_API void b2MouseJoint_SetSpringHertz( b2JointId jointId, float hertz ); + +/// Get the mouse joint spring stiffness in Hertz +B2_API float b2MouseJoint_GetSpringHertz( b2JointId jointId ); + +/// Set the mouse joint spring damping ratio, non-dimensional +B2_API void b2MouseJoint_SetSpringDampingRatio( b2JointId jointId, float dampingRatio ); + +/// Get the mouse joint damping ratio, non-dimensional +B2_API float b2MouseJoint_GetSpringDampingRatio( b2JointId jointId ); + +/// Set the mouse joint maximum force, typically in newtons +B2_API void b2MouseJoint_SetMaxForce( b2JointId jointId, float maxForce ); + +/// Get the mouse joint maximum force, typically in newtons +B2_API float b2MouseJoint_GetMaxForce( b2JointId jointId ); + +/**@}*/ + +/** + * @defgroup prismatic_joint Prismatic Joint + * @brief A prismatic joint allows for translation along a single axis with no rotation. + * + * The prismatic joint is useful for things like pistons and moving platforms, where you want a body to translate + * along an axis and have no rotation. Also called a *slider* joint. + * @{ + */ + +/// Create a prismatic (slider) joint. +/// @see b2PrismaticJointDef for details +B2_API b2JointId b2CreatePrismaticJoint( b2WorldId worldId, const b2PrismaticJointDef* def ); + +/// Enable/disable the joint spring. +B2_API void b2PrismaticJoint_EnableSpring( b2JointId jointId, bool enableSpring ); + +/// Is the prismatic joint spring enabled or not? +B2_API bool b2PrismaticJoint_IsSpringEnabled( b2JointId jointId ); + +/// Set the prismatic joint stiffness in Hertz. +/// This should usually be less than a quarter of the simulation rate. For example, if the simulation +/// runs at 60Hz then the joint stiffness should be 15Hz or less. +B2_API void b2PrismaticJoint_SetSpringHertz( b2JointId jointId, float hertz ); + +/// Get the prismatic joint stiffness in Hertz +B2_API float b2PrismaticJoint_GetSpringHertz( b2JointId jointId ); + +/// Set the prismatic joint damping ratio (non-dimensional) +B2_API void b2PrismaticJoint_SetSpringDampingRatio( b2JointId jointId, float dampingRatio ); + +/// Get the prismatic spring damping ratio (non-dimensional) +B2_API float b2PrismaticJoint_GetSpringDampingRatio( b2JointId jointId ); + +/// Enable/disable a prismatic joint limit +B2_API void b2PrismaticJoint_EnableLimit( b2JointId jointId, bool enableLimit ); + +/// Is the prismatic joint limit enabled? +B2_API bool b2PrismaticJoint_IsLimitEnabled( b2JointId jointId ); + +/// Get the prismatic joint lower limit +B2_API float b2PrismaticJoint_GetLowerLimit( b2JointId jointId ); + +/// Get the prismatic joint upper limit +B2_API float b2PrismaticJoint_GetUpperLimit( b2JointId jointId ); + +/// Set the prismatic joint limits +B2_API void b2PrismaticJoint_SetLimits( b2JointId jointId, float lower, float upper ); + +/// Enable/disable a prismatic joint motor +B2_API void b2PrismaticJoint_EnableMotor( b2JointId jointId, bool enableMotor ); + +/// Is the prismatic joint motor enabled? +B2_API bool b2PrismaticJoint_IsMotorEnabled( b2JointId jointId ); + +/// Set the prismatic joint motor speed, typically in meters per second +B2_API void b2PrismaticJoint_SetMotorSpeed( b2JointId jointId, float motorSpeed ); + +/// Get the prismatic joint motor speed, typically in meters per second +B2_API float b2PrismaticJoint_GetMotorSpeed( b2JointId jointId ); + +/// Set the prismatic joint maximum motor force, typically in newtons +B2_API void b2PrismaticJoint_SetMaxMotorForce( b2JointId jointId, float force ); + +/// Get the prismatic joint maximum motor force, typically in newtons +B2_API float b2PrismaticJoint_GetMaxMotorForce( b2JointId jointId ); + +/// Get the prismatic joint current motor force, typically in newtons +B2_API float b2PrismaticJoint_GetMotorForce( b2JointId jointId ); + +/** @} */ + +/** + * @defgroup revolute_joint Revolute Joint + * @brief A revolute joint allows for relative rotation in the 2D plane with no relative translation. + * + * The revolute joint is probably the most common joint. It can be used for ragdolls and chains. + * Also called a *hinge* or *pin* joint. + * @{ + */ + +/// Create a revolute joint +/// @see b2RevoluteJointDef for details +B2_API b2JointId b2CreateRevoluteJoint( b2WorldId worldId, const b2RevoluteJointDef* def ); + +/// Enable/disable the revolute joint spring +B2_API void b2RevoluteJoint_EnableSpring( b2JointId jointId, bool enableSpring ); + +/// It the revolute angular spring enabled? +B2_API bool b2RevoluteJoint_IsSpringEnabled( b2JointId jointId ); + +/// Set the revolute joint spring stiffness in Hertz +B2_API void b2RevoluteJoint_SetSpringHertz( b2JointId jointId, float hertz ); + +/// Get the revolute joint spring stiffness in Hertz +B2_API float b2RevoluteJoint_GetSpringHertz( b2JointId jointId ); + +/// Set the revolute joint spring damping ratio, non-dimensional +B2_API void b2RevoluteJoint_SetSpringDampingRatio( b2JointId jointId, float dampingRatio ); + +/// Get the revolute joint spring damping ratio, non-dimensional +B2_API float b2RevoluteJoint_GetSpringDampingRatio( b2JointId jointId ); + +/// Get the revolute joint current angle in radians relative to the reference angle +/// @see b2RevoluteJointDef::referenceAngle +B2_API float b2RevoluteJoint_GetAngle( b2JointId jointId ); + +/// Enable/disable the revolute joint limit +B2_API void b2RevoluteJoint_EnableLimit( b2JointId jointId, bool enableLimit ); + +/// Is the revolute joint limit enabled? +B2_API bool b2RevoluteJoint_IsLimitEnabled( b2JointId jointId ); + +/// Get the revolute joint lower limit in radians +B2_API float b2RevoluteJoint_GetLowerLimit( b2JointId jointId ); + +/// Get the revolute joint upper limit in radians +B2_API float b2RevoluteJoint_GetUpperLimit( b2JointId jointId ); + +/// Set the revolute joint limits in radians +B2_API void b2RevoluteJoint_SetLimits( b2JointId jointId, float lower, float upper ); + +/// Enable/disable a revolute joint motor +B2_API void b2RevoluteJoint_EnableMotor( b2JointId jointId, bool enableMotor ); + +/// Is the revolute joint motor enabled? +B2_API bool b2RevoluteJoint_IsMotorEnabled( b2JointId jointId ); + +/// Set the revolute joint motor speed in radians per second +B2_API void b2RevoluteJoint_SetMotorSpeed( b2JointId jointId, float motorSpeed ); + +/// Get the revolute joint motor speed in radians per second +B2_API float b2RevoluteJoint_GetMotorSpeed( b2JointId jointId ); + +/// Get the revolute joint current motor torque, typically in newton-meters +B2_API float b2RevoluteJoint_GetMotorTorque( b2JointId jointId ); + +/// Set the revolute joint maximum motor torque, typically in newton-meters +B2_API void b2RevoluteJoint_SetMaxMotorTorque( b2JointId jointId, float torque ); + +/// Get the revolute joint maximum motor torque, typically in newton-meters +B2_API float b2RevoluteJoint_GetMaxMotorTorque( b2JointId jointId ); + +/**@}*/ + +/** + * @defgroup weld_joint Weld Joint + * @brief A weld joint fully constrains the relative transform between two bodies while allowing for springiness + * + * A weld joint constrains the relative rotation and translation between two bodies. Both rotation and translation + * can have damped springs. + * + * @note The accuracy of weld joint is limited by the accuracy of the solver. Long chains of weld joints may flex. + * @{ + */ + +/// Create a weld joint +/// @see b2WeldJointDef for details +B2_API b2JointId b2CreateWeldJoint( b2WorldId worldId, const b2WeldJointDef* def ); + +/// Set the weld joint linear stiffness in Hertz. 0 is rigid. +B2_API void b2WeldJoint_SetLinearHertz( b2JointId jointId, float hertz ); + +/// Get the weld joint linear stiffness in Hertz +B2_API float b2WeldJoint_GetLinearHertz( b2JointId jointId ); + +/// Set the weld joint linear damping ratio (non-dimensional) +B2_API void b2WeldJoint_SetLinearDampingRatio( b2JointId jointId, float dampingRatio ); + +/// Get the weld joint linear damping ratio (non-dimensional) +B2_API float b2WeldJoint_GetLinearDampingRatio( b2JointId jointId ); + +/// Set the weld joint angular stiffness in Hertz. 0 is rigid. +B2_API void b2WeldJoint_SetAngularHertz( b2JointId jointId, float hertz ); + +/// Get the weld joint angular stiffness in Hertz +B2_API float b2WeldJoint_GetAngularHertz( b2JointId jointId ); + +/// Set weld joint angular damping ratio, non-dimensional +B2_API void b2WeldJoint_SetAngularDampingRatio( b2JointId jointId, float dampingRatio ); + +/// Get the weld joint angular damping ratio, non-dimensional +B2_API float b2WeldJoint_GetAngularDampingRatio( b2JointId jointId ); + +/** @} */ + +/** + * @defgroup wheel_joint Wheel Joint + * The wheel joint can be used to simulate wheels on vehicles. + * + * The wheel joint restricts body B to move along a local axis in body A. Body B is free to + * rotate. Supports a linear spring, linear limits, and a rotational motor. + * + * @{ + */ + +/// Create a wheel joint +/// @see b2WheelJointDef for details +B2_API b2JointId b2CreateWheelJoint( b2WorldId worldId, const b2WheelJointDef* def ); + +/// Enable/disable the wheel joint spring +B2_API void b2WheelJoint_EnableSpring( b2JointId jointId, bool enableSpring ); + +/// Is the wheel joint spring enabled? +B2_API bool b2WheelJoint_IsSpringEnabled( b2JointId jointId ); + +/// Set the wheel joint stiffness in Hertz +B2_API void b2WheelJoint_SetSpringHertz( b2JointId jointId, float hertz ); + +/// Get the wheel joint stiffness in Hertz +B2_API float b2WheelJoint_GetSpringHertz( b2JointId jointId ); + +/// Set the wheel joint damping ratio, non-dimensional +B2_API void b2WheelJoint_SetSpringDampingRatio( b2JointId jointId, float dampingRatio ); + +/// Get the wheel joint damping ratio, non-dimensional +B2_API float b2WheelJoint_GetSpringDampingRatio( b2JointId jointId ); + +/// Enable/disable the wheel joint limit +B2_API void b2WheelJoint_EnableLimit( b2JointId jointId, bool enableLimit ); + +/// Is the wheel joint limit enabled? +B2_API bool b2WheelJoint_IsLimitEnabled( b2JointId jointId ); + +/// Get the wheel joint lower limit +B2_API float b2WheelJoint_GetLowerLimit( b2JointId jointId ); + +/// Get the wheel joint upper limit +B2_API float b2WheelJoint_GetUpperLimit( b2JointId jointId ); + +/// Set the wheel joint limits +B2_API void b2WheelJoint_SetLimits( b2JointId jointId, float lower, float upper ); + +/// Enable/disable the wheel joint motor +B2_API void b2WheelJoint_EnableMotor( b2JointId jointId, bool enableMotor ); + +/// Is the wheel joint motor enabled? +B2_API bool b2WheelJoint_IsMotorEnabled( b2JointId jointId ); + +/// Set the wheel joint motor speed in radians per second +B2_API void b2WheelJoint_SetMotorSpeed( b2JointId jointId, float motorSpeed ); + +/// Get the wheel joint motor speed in radians per second +B2_API float b2WheelJoint_GetMotorSpeed( b2JointId jointId ); + +/// Set the wheel joint maximum motor torque, typically in newton-meters +B2_API void b2WheelJoint_SetMaxMotorTorque( b2JointId jointId, float torque ); + +/// Get the wheel joint maximum motor torque, typically in newton-meters +B2_API float b2WheelJoint_GetMaxMotorTorque( b2JointId jointId ); + +/// Get the wheel joint current motor torque, typically in newton-meters +B2_API float b2WheelJoint_GetMotorTorque( b2JointId jointId ); + +/**@}*/ + +/**@}*/ diff --git a/3rdparty/box2d/include/box2d/collision.h b/3rdparty/box2d/include/box2d/collision.h new file mode 100644 index 000000000000..4b296c0b38c3 --- /dev/null +++ b/3rdparty/box2d/include/box2d/collision.h @@ -0,0 +1,795 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include "base.h" +#include "math_functions.h" + +#include + +typedef struct b2Circle b2Circle; +typedef struct b2Capsule b2Capsule; +typedef struct b2DistanceCache b2DistanceCache; +typedef struct b2Polygon b2Polygon; +typedef struct b2Segment b2Segment; +typedef struct b2ChainSegment b2ChainSegment; + +typedef struct b2Hull b2Hull; + +/** + * @defgroup geometry Geometry + * @brief Geometry types and algorithms + * + * Definitions of circles, capsules, segments, and polygons. Various algorithms to compute hulls, mass properties, and so on. + * @{ + */ + +/// The maximum number of vertices on a convex polygon. Changing this affects performance even if you +/// don't use more vertices. +#define b2_maxPolygonVertices 8 + +/// Low level ray-cast input data +typedef struct b2RayCastInput +{ + /// Start point of the ray cast + b2Vec2 origin; + + /// Translation of the ray cast + b2Vec2 translation; + + /// The maximum fraction of the translation to consider, typically 1 + float maxFraction; +} b2RayCastInput; + +/// Low level shape cast input in generic form. This allows casting an arbitrary point +/// cloud wrap with a radius. For example, a circle is a single point with a non-zero radius. +/// A capsule is two points with a non-zero radius. A box is four points with a zero radius. +typedef struct b2ShapeCastInput +{ + /// A point cloud to cast + b2Vec2 points[b2_maxPolygonVertices]; + + /// The number of points + int32_t count; + + /// The radius around the point cloud + float radius; + + /// The translation of the shape cast + b2Vec2 translation; + + /// The maximum fraction of the translation to consider, typically 1 + float maxFraction; +} b2ShapeCastInput; + +/// Low level ray-cast or shape-cast output data +typedef struct b2CastOutput +{ + /// The surface normal at the hit point + b2Vec2 normal; + + /// The surface hit point + b2Vec2 point; + + /// The fraction of the input translation at collision + float fraction; + + /// The number of iterations used + int32_t iterations; + + /// Did the cast hit? + bool hit; +} b2CastOutput; + +/// This holds the mass data computed for a shape. +typedef struct b2MassData +{ + /// The mass of the shape, usually in kilograms. + float mass; + + /// The position of the shape's centroid relative to the shape's origin. + b2Vec2 center; + + /// The rotational inertia of the shape about the local origin. + float rotationalInertia; +} b2MassData; + +/// A solid circle +typedef struct b2Circle +{ + /// The local center + b2Vec2 center; + + /// The radius + float radius; +} b2Circle; + +/// A solid capsule can be viewed as two semicircles connected +/// by a rectangle. +typedef struct b2Capsule +{ + /// Local center of the first semicircle + b2Vec2 center1; + + /// Local center of the second semicircle + b2Vec2 center2; + + /// The radius of the semicircles + float radius; +} b2Capsule; + +/// A solid convex polygon. It is assumed that the interior of the polygon is to +/// the left of each edge. +/// Polygons have a maximum number of vertices equal to b2_maxPolygonVertices. +/// In most cases you should not need many vertices for a convex polygon. +/// @warning DO NOT fill this out manually, instead use a helper function like +/// b2MakePolygon or b2MakeBox. +typedef struct b2Polygon +{ + /// The polygon vertices + b2Vec2 vertices[b2_maxPolygonVertices]; + + /// The outward normal vectors of the polygon sides + b2Vec2 normals[b2_maxPolygonVertices]; + + /// The centroid of the polygon + b2Vec2 centroid; + + /// The external radius for rounded polygons + float radius; + + /// The number of polygon vertices + int32_t count; +} b2Polygon; + +/// A line segment with two-sided collision. +typedef struct b2Segment +{ + /// The first point + b2Vec2 point1; + + /// The second point + b2Vec2 point2; +} b2Segment; + +/// A line segment with one-sided collision. Only collides on the right side. +/// Several of these are generated for a chain shape. +/// ghost1 -> point1 -> point2 -> ghost2 +typedef struct b2ChainSegment +{ + /// The tail ghost vertex + b2Vec2 ghost1; + + /// The line segment + b2Segment segment; + + /// The head ghost vertex + b2Vec2 ghost2; + + /// The owning chain shape index (internal usage only) + int32_t chainId; +} b2ChainSegment; + +/// Validate ray cast input data (NaN, etc) +B2_API bool b2IsValidRay( const b2RayCastInput* input ); + +/// Make a convex polygon from a convex hull. This will assert if the hull is not valid. +/// @warning Do not manually fill in the hull data, it must come directly from b2ComputeHull +B2_API b2Polygon b2MakePolygon( const b2Hull* hull, float radius ); + +/// Make an offset convex polygon from a convex hull. This will assert if the hull is not valid. +/// @warning Do not manually fill in the hull data, it must come directly from b2ComputeHull +B2_API b2Polygon b2MakeOffsetPolygon( const b2Hull* hull, float radius, b2Transform transform ); + +/// Make a square polygon, bypassing the need for a convex hull. +/// @param h the half-width +B2_API b2Polygon b2MakeSquare( float h ); + +/// Make a box (rectangle) polygon, bypassing the need for a convex hull. +/// @param hx the half-width +/// @param hy the half-height +B2_API b2Polygon b2MakeBox( float hx, float hy ); + +/// Make a rounded box, bypassing the need for a convex hull. +/// @param hx the half-width +/// @param hy the half-height +/// @param radius the radius of the rounded extension +B2_API b2Polygon b2MakeRoundedBox( float hx, float hy, float radius ); + +/// Make an offset box, bypassing the need for a convex hull. +/// @param hx the half-width +/// @param hy the half-height +/// @param center the local position of the center of the box +/// @param rotation the local rotation of the box +B2_API b2Polygon b2MakeOffsetBox( float hx, float hy, b2Vec2 center, b2Rot rotation ); + +/// Transform a polygon. This is useful for transferring a shape from one body to another. +B2_API b2Polygon b2TransformPolygon( b2Transform transform, const b2Polygon* polygon ); + +/// Compute mass properties of a circle +B2_API b2MassData b2ComputeCircleMass( const b2Circle* shape, float density ); + +/// Compute mass properties of a capsule +B2_API b2MassData b2ComputeCapsuleMass( const b2Capsule* shape, float density ); + +/// Compute mass properties of a polygon +B2_API b2MassData b2ComputePolygonMass( const b2Polygon* shape, float density ); + +/// Compute the bounding box of a transformed circle +B2_API b2AABB b2ComputeCircleAABB( const b2Circle* shape, b2Transform transform ); + +/// Compute the bounding box of a transformed capsule +B2_API b2AABB b2ComputeCapsuleAABB( const b2Capsule* shape, b2Transform transform ); + +/// Compute the bounding box of a transformed polygon +B2_API b2AABB b2ComputePolygonAABB( const b2Polygon* shape, b2Transform transform ); + +/// Compute the bounding box of a transformed line segment +B2_API b2AABB b2ComputeSegmentAABB( const b2Segment* shape, b2Transform transform ); + +/// Test a point for overlap with a circle in local space +B2_API bool b2PointInCircle( b2Vec2 point, const b2Circle* shape ); + +/// Test a point for overlap with a capsule in local space +B2_API bool b2PointInCapsule( b2Vec2 point, const b2Capsule* shape ); + +/// Test a point for overlap with a convex polygon in local space +B2_API bool b2PointInPolygon( b2Vec2 point, const b2Polygon* shape ); + +/// Ray cast versus circle in shape local space. Initial overlap is treated as a miss. +B2_API b2CastOutput b2RayCastCircle( const b2RayCastInput* input, const b2Circle* shape ); + +/// Ray cast versus capsule in shape local space. Initial overlap is treated as a miss. +B2_API b2CastOutput b2RayCastCapsule( const b2RayCastInput* input, const b2Capsule* shape ); + +/// Ray cast versus segment in shape local space. Optionally treat the segment as one-sided with hits from +/// the left side being treated as a miss. +B2_API b2CastOutput b2RayCastSegment( const b2RayCastInput* input, const b2Segment* shape, bool oneSided ); + +/// Ray cast versus polygon in shape local space. Initial overlap is treated as a miss. +B2_API b2CastOutput b2RayCastPolygon( const b2RayCastInput* input, const b2Polygon* shape ); + +/// Shape cast versus a circle. Initial overlap is treated as a miss. +B2_API b2CastOutput b2ShapeCastCircle( const b2ShapeCastInput* input, const b2Circle* shape ); + +/// Shape cast versus a capsule. Initial overlap is treated as a miss. +B2_API b2CastOutput b2ShapeCastCapsule( const b2ShapeCastInput* input, const b2Capsule* shape ); + +/// Shape cast versus a line segment. Initial overlap is treated as a miss. +B2_API b2CastOutput b2ShapeCastSegment( const b2ShapeCastInput* input, const b2Segment* shape ); + +/// Shape cast versus a convex polygon. Initial overlap is treated as a miss. +B2_API b2CastOutput b2ShapeCastPolygon( const b2ShapeCastInput* input, const b2Polygon* shape ); + +/// A convex hull. Used to create convex polygons. +/// @warning Do not modify these values directly, instead use b2ComputeHull() +typedef struct b2Hull +{ + /// The final points of the hull + b2Vec2 points[b2_maxPolygonVertices]; + + /// The number of points + int32_t count; +} b2Hull; + +/// Compute the convex hull of a set of points. Returns an empty hull if it fails. +/// Some failure cases: +/// - all points very close together +/// - all points on a line +/// - less than 3 points +/// - more than b2_maxPolygonVertices points +/// This welds close points and removes collinear points. +/// @warning Do not modify a hull once it has been computed +B2_API b2Hull b2ComputeHull( const b2Vec2* points, int32_t count ); + +/// This determines if a hull is valid. Checks for: +/// - convexity +/// - collinear points +/// This is expensive and should not be called at runtime. +B2_API bool b2ValidateHull( const b2Hull* hull ); + +/**@}*/ + +/** + * @defgroup distance Distance + * Functions for computing the distance between shapes. + * + * These are advanced functions you can use to perform distance calculations. There + * are functions for computing the closest points between shapes, doing linear shape casts, + * and doing rotational shape casts. The latter is called time of impact (TOI). + * @{ + */ + +/// Result of computing the distance between two line segments +typedef struct b2SegmentDistanceResult +{ + /// The closest point on the first segment + b2Vec2 closest1; + + /// The closest point on the second segment + b2Vec2 closest2; + + /// The barycentric coordinate on the first segment + float fraction1; + + /// The barycentric coordinate on the second segment + float fraction2; + + /// The squared distance between the closest points + float distanceSquared; +} b2SegmentDistanceResult; + +/// Compute the distance between two line segments, clamping at the end points if needed. +B2_API b2SegmentDistanceResult b2SegmentDistance( b2Vec2 p1, b2Vec2 q1, b2Vec2 p2, b2Vec2 q2 ); + +/// A distance proxy is used by the GJK algorithm. It encapsulates any shape. +typedef struct b2DistanceProxy +{ + /// The point cloud + b2Vec2 points[b2_maxPolygonVertices]; + + /// The number of points + int32_t count; + + /// The external radius of the point cloud + float radius; +} b2DistanceProxy; + +/// Used to warm start b2Distance. Set count to zero on first call or +/// use zero initialization. +typedef struct b2DistanceCache +{ + /// The number of stored simplex points + uint16_t count; + + /// The cached simplex indices on shape A + uint8_t indexA[3]; + + /// The cached simplex indices on shape B + uint8_t indexB[3]; +} b2DistanceCache; + +static const b2DistanceCache b2_emptyDistanceCache = B2_ZERO_INIT; + +/// Input for b2ShapeDistance +typedef struct b2DistanceInput +{ + /// The proxy for shape A + b2DistanceProxy proxyA; + + /// The proxy for shape B + b2DistanceProxy proxyB; + + /// The world transform for shape A + b2Transform transformA; + + /// The world transform for shape B + b2Transform transformB; + + /// Should the proxy radius be considered? + bool useRadii; +} b2DistanceInput; + +/// Output for b2ShapeDistance +typedef struct b2DistanceOutput +{ + b2Vec2 pointA; ///< Closest point on shapeA + b2Vec2 pointB; ///< Closest point on shapeB + float distance; ///< The final distance, zero if overlapped + int32_t iterations; ///< Number of GJK iterations used + int32_t simplexCount; ///< The number of simplexes stored in the simplex array +} b2DistanceOutput; + +/// Simplex vertex for debugging the GJK algorithm +typedef struct b2SimplexVertex +{ + b2Vec2 wA; ///< support point in proxyA + b2Vec2 wB; ///< support point in proxyB + b2Vec2 w; ///< wB - wA + float a; ///< barycentric coordinate for closest point + int32_t indexA; ///< wA index + int32_t indexB; ///< wB index +} b2SimplexVertex; + +/// Simplex from the GJK algorithm +typedef struct b2Simplex +{ + b2SimplexVertex v1, v2, v3; ///< vertices + int32_t count; ///< number of valid vertices +} b2Simplex; + +/// Compute the closest points between two shapes represented as point clouds. +/// b2DistanceCache cache is input/output. On the first call set b2DistanceCache.count to zero. +/// The underlying GJK algorithm may be debugged by passing in debug simplexes and capacity. You may pass in NULL and 0 for these. +B2_API b2DistanceOutput b2ShapeDistance( b2DistanceCache* cache, const b2DistanceInput* input, b2Simplex* simplexes, + int simplexCapacity ); + +/// Input parameters for b2ShapeCast +typedef struct b2ShapeCastPairInput +{ + b2DistanceProxy proxyA; ///< The proxy for shape A + b2DistanceProxy proxyB; ///< The proxy for shape B + b2Transform transformA; ///< The world transform for shape A + b2Transform transformB; ///< The world transform for shape B + b2Vec2 translationB; ///< The translation of shape B + float maxFraction; ///< The fraction of the translation to consider, typically 1 +} b2ShapeCastPairInput; + +/// Perform a linear shape cast of shape B moving and shape A fixed. Determines the hit point, normal, and translation fraction. +B2_API b2CastOutput b2ShapeCast( const b2ShapeCastPairInput* input ); + +/// Make a proxy for use in GJK and related functions. +B2_API b2DistanceProxy b2MakeProxy( const b2Vec2* vertices, int32_t count, float radius ); + +/// This describes the motion of a body/shape for TOI computation. Shapes are defined with respect to the body origin, +/// which may not coincide with the center of mass. However, to support dynamics we must interpolate the center of mass +/// position. +typedef struct b2Sweep +{ + b2Vec2 localCenter; ///< Local center of mass position + b2Vec2 c1; ///< Starting center of mass world position + b2Vec2 c2; ///< Ending center of mass world position + b2Rot q1; ///< Starting world rotation + b2Rot q2; ///< Ending world rotation +} b2Sweep; + +/// Evaluate the transform sweep at a specific time. +B2_API b2Transform b2GetSweepTransform( const b2Sweep* sweep, float time ); + +/// Input parameters for b2TimeOfImpact +typedef struct b2TOIInput +{ + b2DistanceProxy proxyA; ///< The proxy for shape A + b2DistanceProxy proxyB; ///< The proxy for shape B + b2Sweep sweepA; ///< The movement of shape A + b2Sweep sweepB; ///< The movement of shape B + float tMax; ///< Defines the sweep interval [0, tMax] +} b2TOIInput; + +/// Describes the TOI output +typedef enum b2TOIState +{ + b2_toiStateUnknown, + b2_toiStateFailed, + b2_toiStateOverlapped, + b2_toiStateHit, + b2_toiStateSeparated +} b2TOIState; + +/// Output parameters for b2TimeOfImpact. +typedef struct b2TOIOutput +{ + b2TOIState state; ///< The type of result + float t; ///< The time of the collision +} b2TOIOutput; + +/// Compute the upper bound on time before two shapes penetrate. Time is represented as +/// a fraction between [0,tMax]. This uses a swept separating axis and may miss some intermediate, +/// non-tunneling collisions. If you change the time interval, you should call this function +/// again. +B2_API b2TOIOutput b2TimeOfImpact( const b2TOIInput* input ); + +/**@}*/ + +/** + * @defgroup collision Collision + * @brief Functions for colliding pairs of shapes + * @{ + */ + +/// A manifold point is a contact point belonging to a contact +/// manifold. It holds details related to the geometry and dynamics +/// of the contact points. +typedef struct b2ManifoldPoint +{ + /// Location of the contact point in world space. Subject to precision loss at large coordinates. + /// @note Should only be used for debugging. + b2Vec2 point; + + /// Location of the contact point relative to bodyA's origin in world space + /// @note When used internally to the Box2D solver, these are relative to the center of mass. + b2Vec2 anchorA; + + /// Location of the contact point relative to bodyB's origin in world space + b2Vec2 anchorB; + + /// The separation of the contact point, negative if penetrating + float separation; + + /// The impulse along the manifold normal vector. + float normalImpulse; + + /// The friction impulse + float tangentImpulse; + + /// The maximum normal impulse applied during sub-stepping + /// todo not sure this is needed + float maxNormalImpulse; + + /// Relative normal velocity pre-solve. Used for hit events. If the normal impulse is + /// zero then there was no hit. Negative means shapes are approaching. + float normalVelocity; + + /// Uniquely identifies a contact point between two shapes + uint16_t id; + + /// Did this contact point exist the previous step? + bool persisted; +} b2ManifoldPoint; + +/// A contact manifold describes the contact points between colliding shapes +typedef struct b2Manifold +{ + /// The manifold points, up to two are possible in 2D + b2ManifoldPoint points[2]; + + /// The unit normal vector in world space, points from shape A to bodyB + b2Vec2 normal; + + /// The number of contacts points, will be 0, 1, or 2 + int32_t pointCount; +} b2Manifold; + +/// Compute the contact manifold between two circles +B2_API b2Manifold b2CollideCircles( const b2Circle* circleA, b2Transform xfA, const b2Circle* circleB, b2Transform xfB ); + +/// Compute the contact manifold between a capsule and circle +B2_API b2Manifold b2CollideCapsuleAndCircle( const b2Capsule* capsuleA, b2Transform xfA, const b2Circle* circleB, + b2Transform xfB ); + +/// Compute the contact manifold between an segment and a circle +B2_API b2Manifold b2CollideSegmentAndCircle( const b2Segment* segmentA, b2Transform xfA, const b2Circle* circleB, + b2Transform xfB ); + +/// Compute the contact manifold between a polygon and a circle +B2_API b2Manifold b2CollidePolygonAndCircle( const b2Polygon* polygonA, b2Transform xfA, const b2Circle* circleB, + b2Transform xfB ); + +/// Compute the contact manifold between a capsule and circle +B2_API b2Manifold b2CollideCapsules( const b2Capsule* capsuleA, b2Transform xfA, const b2Capsule* capsuleB, b2Transform xfB ); + +/// Compute the contact manifold between an segment and a capsule +B2_API b2Manifold b2CollideSegmentAndCapsule( const b2Segment* segmentA, b2Transform xfA, const b2Capsule* capsuleB, + b2Transform xfB ); + +/// Compute the contact manifold between a polygon and capsule +B2_API b2Manifold b2CollidePolygonAndCapsule( const b2Polygon* polygonA, b2Transform xfA, const b2Capsule* capsuleB, + b2Transform xfB ); + +/// Compute the contact manifold between two polygons +B2_API b2Manifold b2CollidePolygons( const b2Polygon* polygonA, b2Transform xfA, const b2Polygon* polygonB, b2Transform xfB ); + +/// Compute the contact manifold between an segment and a polygon +B2_API b2Manifold b2CollideSegmentAndPolygon( const b2Segment* segmentA, b2Transform xfA, const b2Polygon* polygonB, + b2Transform xfB ); + +/// Compute the contact manifold between a chain segment and a circle +B2_API b2Manifold b2CollideChainSegmentAndCircle( const b2ChainSegment* segmentA, b2Transform xfA, + const b2Circle* circleB, b2Transform xfB ); + +/// Compute the contact manifold between a chain segment and a capsule +B2_API b2Manifold b2CollideChainSegmentAndCapsule( const b2ChainSegment* segmentA, b2Transform xfA, + const b2Capsule* capsuleB, b2Transform xfB, b2DistanceCache* cache ); + +/// Compute the contact manifold between a chain segment and a rounded polygon +B2_API b2Manifold b2CollideChainSegmentAndPolygon( const b2ChainSegment* segmentA, b2Transform xfA, + const b2Polygon* polygonB, b2Transform xfB, b2DistanceCache* cache ); + +/**@}*/ + +/** + * @defgroup tree Dynamic Tree + * The dynamic tree is a binary AABB tree to organize and query large numbers of geometric objects + * + * Box2D uses the dynamic tree internally to sort collision shapes into a binary bounding volume hierarchy. + * This data structure may have uses in games for organizing other geometry data and may be used independently + * of Box2D rigid body simulation. + * + * A dynamic AABB tree broad-phase, inspired by Nathanael Presson's btDbvt. + * A dynamic tree arranges data in a binary tree to accelerate + * queries such as AABB queries and ray casts. Leaf nodes are proxies + * with an AABB. These are used to hold a user collision object, such as a reference to a b2Shape. + * Nodes are pooled and relocatable, so I use node indices rather than pointers. + * The dynamic tree is made available for advanced users that would like to use it to organize + * spatial game data besides rigid bodies. + * + * @note This is an advanced feature and normally not used by applications directly. + * @{ + */ + +/// The default category bit for a tree proxy. Used for collision filtering. +#define b2_defaultCategoryBits ( 1 ) + +/// Convenience mask bits to use when you don't need collision filtering and just want +/// all results. +#define b2_defaultMaskBits ( UINT64_MAX ) + +/// A node in the dynamic tree. This is private data placed here for performance reasons. +typedef struct b2TreeNode +{ + /// The node bounding box + b2AABB aabb; // 16 + + /// Category bits for collision filtering + uint64_t categoryBits; // 8 + + union + { + /// The node parent index + int32_t parent; + + /// The node freelist next index + int32_t next; + }; // 4 + + /// Child 1 index + int32_t child1; // 4 + + /// Child 2 index + int32_t child2; // 4 + + /// User data + // todo could be union with child index + int32_t userData; // 4 + + /// Leaf = 0, free node = -1 + int16_t height; // 2 + + /// Has the AABB been enlarged? + bool enlarged; // 1 + + /// Padding for clarity + char pad[5]; +} b2TreeNode; + +/// The dynamic tree structure. This should be considered private data. +/// It is placed here for performance reasons. +typedef struct b2DynamicTree +{ + /// The tree nodes + b2TreeNode* nodes; + + /// The root index + int32_t root; + + /// The number of nodes + int32_t nodeCount; + + /// The allocated node space + int32_t nodeCapacity; + + /// Node free list + int32_t freeList; + + /// Number of proxies created + int32_t proxyCount; + + /// Leaf indices for rebuild + int32_t* leafIndices; + + /// Leaf bounding boxes for rebuild + b2AABB* leafBoxes; + + /// Leaf bounding box centers for rebuild + b2Vec2* leafCenters; + + /// Bins for sorting during rebuild + int32_t* binIndices; + + /// Allocated space for rebuilding + int32_t rebuildCapacity; +} b2DynamicTree; + +/// Constructing the tree initializes the node pool. +B2_API b2DynamicTree b2DynamicTree_Create( void ); + +/// Destroy the tree, freeing the node pool. +B2_API void b2DynamicTree_Destroy( b2DynamicTree* tree ); + +/// Create a proxy. Provide an AABB and a userData value. +B2_API int32_t b2DynamicTree_CreateProxy( b2DynamicTree* tree, b2AABB aabb, uint64_t categoryBits, int32_t userData ); + +/// Destroy a proxy. This asserts if the id is invalid. +B2_API void b2DynamicTree_DestroyProxy( b2DynamicTree* tree, int32_t proxyId ); + +/// Move a proxy to a new AABB by removing and reinserting into the tree. +B2_API void b2DynamicTree_MoveProxy( b2DynamicTree* tree, int32_t proxyId, b2AABB aabb ); + +/// Enlarge a proxy and enlarge ancestors as necessary. +B2_API void b2DynamicTree_EnlargeProxy( b2DynamicTree* tree, int32_t proxyId, b2AABB aabb ); + +/// This function receives proxies found in the AABB query. +/// @return true if the query should continue +typedef bool b2TreeQueryCallbackFcn( int32_t proxyId, int32_t userData, void* context ); + +/// Query an AABB for overlapping proxies. The callback class is called for each proxy that overlaps the supplied AABB. +B2_API void b2DynamicTree_Query( const b2DynamicTree* tree, b2AABB aabb, uint64_t maskBits, b2TreeQueryCallbackFcn* callback, + void* context ); + +/// This function receives clipped raycast input for a proxy. The function +/// returns the new ray fraction. +/// - return a value of 0 to terminate the ray cast +/// - return a value less than input->maxFraction to clip the ray +/// - return a value of input->maxFraction to continue the ray cast without clipping +typedef float b2TreeRayCastCallbackFcn( const b2RayCastInput* input, int32_t proxyId, int32_t userData, void* context ); + +/// Ray-cast against the proxies in the tree. This relies on the callback +/// to perform a exact ray-cast in the case were the proxy contains a shape. +/// The callback also performs the any collision filtering. This has performance +/// roughly equal to k * log(n), where k is the number of collisions and n is the +/// number of proxies in the tree. +/// Bit-wise filtering using mask bits can greatly improve performance in some scenarios. +/// @param tree the dynamic tree to ray cast +/// @param input the ray-cast input data. The ray extends from p1 to p1 + maxFraction * (p2 - p1) +/// @param maskBits filter bits: `bool accept = (maskBits & node->categoryBits) != 0;` +/// @param callback a callback class that is called for each proxy that is hit by the ray +/// @param context user context that is passed to the callback +B2_API void b2DynamicTree_RayCast( const b2DynamicTree* tree, const b2RayCastInput* input, uint64_t maskBits, + b2TreeRayCastCallbackFcn* callback, void* context ); + +/// This function receives clipped ray-cast input for a proxy. The function +/// returns the new ray fraction. +/// - return a value of 0 to terminate the ray-cast +/// - return a value less than input->maxFraction to clip the ray +/// - return a value of input->maxFraction to continue the ray cast without clipping +typedef float b2TreeShapeCastCallbackFcn( const b2ShapeCastInput* input, int32_t proxyId, int32_t userData, void* context ); + +/// Ray-cast against the proxies in the tree. This relies on the callback +/// to perform a exact ray-cast in the case were the proxy contains a shape. +/// The callback also performs the any collision filtering. This has performance +/// roughly equal to k * log(n), where k is the number of collisions and n is the +/// number of proxies in the tree. +/// @param tree the dynamic tree to ray cast +/// @param input the ray-cast input data. The ray extends from p1 to p1 + maxFraction * (p2 - p1). +/// @param maskBits filter bits: `bool accept = (maskBits & node->categoryBits) != 0;` +/// @param callback a callback class that is called for each proxy that is hit by the shape +/// @param context user context that is passed to the callback +B2_API void b2DynamicTree_ShapeCast( const b2DynamicTree* tree, const b2ShapeCastInput* input, uint64_t maskBits, + b2TreeShapeCastCallbackFcn* callback, void* context ); + +/// Validate this tree. For testing. +B2_API void b2DynamicTree_Validate( const b2DynamicTree* tree ); + +/// Compute the height of the binary tree in O(N) time. Should not be +/// called often. +B2_API int b2DynamicTree_GetHeight( const b2DynamicTree* tree ); + +/// Get the maximum balance of the tree. The balance is the difference in height of the two children of a node. +B2_API int b2DynamicTree_GetMaxBalance( const b2DynamicTree* tree ); + +/// Get the ratio of the sum of the node areas to the root area. +B2_API float b2DynamicTree_GetAreaRatio( const b2DynamicTree* tree ); + +/// Build an optimal tree. Very expensive. For testing. +B2_API void b2DynamicTree_RebuildBottomUp( b2DynamicTree* tree ); + +/// Get the number of proxies created +B2_API int b2DynamicTree_GetProxyCount( const b2DynamicTree* tree ); + +/// Rebuild the tree while retaining subtrees that haven't changed. Returns the number of boxes sorted. +B2_API int b2DynamicTree_Rebuild( b2DynamicTree* tree, bool fullBuild ); + +/// Shift the world origin. Useful for large worlds. +/// The shift formula is: position -= newOrigin +/// @param tree the tree to shift +/// @param newOrigin the new origin with respect to the old origin +B2_API void b2DynamicTree_ShiftOrigin( b2DynamicTree* tree, b2Vec2 newOrigin ); + +/// Get the number of bytes used by this tree +B2_API int b2DynamicTree_GetByteCount( const b2DynamicTree* tree ); + +/// Get proxy user data +/// @return the proxy user data or 0 if the id is invalid +B2_INLINE int32_t b2DynamicTree_GetUserData( const b2DynamicTree* tree, int32_t proxyId ) +{ + return tree->nodes[proxyId].userData; +} + +/// Get the AABB of a proxy +B2_INLINE b2AABB b2DynamicTree_GetAABB( const b2DynamicTree* tree, int32_t proxyId ) +{ + return tree->nodes[proxyId].aabb; +} + +/**@}*/ diff --git a/3rdparty/box2d/include/box2d/id.h b/3rdparty/box2d/include/box2d/id.h new file mode 100644 index 000000000000..72c31f628817 --- /dev/null +++ b/3rdparty/box2d/include/box2d/id.h @@ -0,0 +1,92 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include "base.h" + +#include + +/** + * @defgroup id Ids + * These ids serve as handles to internal Box2D objects. + * These should be considered opaque data and passed by value. + * Include this header if you need the id types and not the whole Box2D API. + * All ids are considered null if initialized to zero. + * + * For example in C++: + * + * @code{.cxx} + * b2WorldId worldId = {}; + * @endcode + * + * Or in C: + * + * @code{.c} + * b2WorldId worldId = {0}; + * @endcode + * + * These are both considered null. + * + * @warning Do not use the internals of these ids. They are subject to change. Ids should be treated as opaque objects. + * @warning You should use ids to access objects in Box2D. Do not access files within the src folder. Such usage is unsupported. + * @{ + */ + +/// World id references a world instance. This should be treated as an opaque handle. +typedef struct b2WorldId +{ + uint16_t index1; + uint16_t revision; +} b2WorldId; + +/// Body id references a body instance. This should be treated as an opaque handle. +typedef struct b2BodyId +{ + int32_t index1; + uint16_t world0; + uint16_t revision; +} b2BodyId; + +/// Shape id references a shape instance. This should be treated as an opaque handle. +typedef struct b2ShapeId +{ + int32_t index1; + uint16_t world0; + uint16_t revision; +} b2ShapeId; + +/// Joint id references a joint instance. This should be treated as an opaque handle. +typedef struct b2JointId +{ + int32_t index1; + uint16_t world0; + uint16_t revision; +} b2JointId; + +/// Chain id references a chain instances. This should be treated as an opaque handle. +typedef struct b2ChainId +{ + int32_t index1; + uint16_t world0; + uint16_t revision; +} b2ChainId; + +/// Use these to make your identifiers null. +/// You may also use zero initialization to get null. +static const b2WorldId b2_nullWorldId = B2_ZERO_INIT; +static const b2BodyId b2_nullBodyId = B2_ZERO_INIT; +static const b2ShapeId b2_nullShapeId = B2_ZERO_INIT; +static const b2JointId b2_nullJointId = B2_ZERO_INIT; +static const b2ChainId b2_nullChainId = B2_ZERO_INIT; + +/// Macro to determine if any id is null. +#define B2_IS_NULL( id ) ( id.index1 == 0 ) + +/// Macro to determine if any id is non-null. +#define B2_IS_NON_NULL( id ) ( id.index1 != 0 ) + +/// Compare two ids for equality. Doesn't work for b2WorldId. +#define B2_ID_EQUALS( id1, id2 ) ( id1.index1 == id2.index1 && id1.world0 == id2.world0 && id1.revision == id2.revision ) + +/**@}*/ diff --git a/3rdparty/box2d/include/box2d/math_functions.h b/3rdparty/box2d/include/box2d/math_functions.h new file mode 100644 index 000000000000..4955953478e8 --- /dev/null +++ b/3rdparty/box2d/include/box2d/math_functions.h @@ -0,0 +1,696 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include "base.h" + +#include +#include +#include + +/** + * @defgroup math Math + * @brief Vector math types and functions + * @{ + */ + +/// https://en.wikipedia.org/wiki/Pi +#define b2_pi 3.14159265359f + +/// 2D vector +/// This can be used to represent a point or free vector +typedef struct b2Vec2 +{ + /// coordinates + float x, y; +} b2Vec2; + +/// Cosine and sine pair +/// This uses a custom implementation designed for cross platform determinism +typedef struct b2CosSin +{ + /// cosine and sine + float cosine; + float sine; +} b2CosSin; + +/// 2D rotation +/// This is similar to using a complex number for rotation +typedef struct b2Rot +{ + /// cosine and sine + float c, s; +} b2Rot; + +/// A 2D rigid transform +typedef struct b2Transform +{ + b2Vec2 p; + b2Rot q; +} b2Transform; + +/// A 2-by-2 Matrix +typedef struct b2Mat22 +{ + /// columns + b2Vec2 cx, cy; +} b2Mat22; + +/// Axis-aligned bounding box +typedef struct b2AABB +{ + b2Vec2 lowerBound; + b2Vec2 upperBound; +} b2AABB; + +/**@}*/ + +/** + * @addtogroup math + * @{ + */ + +static const b2Vec2 b2Vec2_zero = { 0.0f, 0.0f }; +static const b2Rot b2Rot_identity = { 1.0f, 0.0f }; +static const b2Transform b2Transform_identity = { { 0.0f, 0.0f }, { 1.0f, 0.0f } }; +static const b2Mat22 b2Mat22_zero = { { 0.0f, 0.0f }, { 0.0f, 0.0f } }; + +/// Compute an approximate arctangent in the range [-pi, pi] +/// This is hand coded for cross platform determinism. The atan2f +/// function in the standard library is not cross platform deterministic. +B2_API float b2Atan2( float y, float x ); + +/// @return the minimum of two floats +B2_INLINE float b2MinFloat( float a, float b ) +{ + return a < b ? a : b; +} + +/// @return the maximum of two floats +B2_INLINE float b2MaxFloat( float a, float b ) +{ + return a > b ? a : b; +} + +/// @return the absolute value of a float +B2_INLINE float b2AbsFloat( float a ) +{ + return a < 0 ? -a : a; +} + +/// @return a float clamped between a lower and upper bound +B2_INLINE float b2ClampFloat( float a, float lower, float upper ) +{ + return a < lower ? lower : ( a > upper ? upper : a ); +} + +/// @return the minimum of two integers +B2_INLINE int b2MinInt( int a, int b ) +{ + return a < b ? a : b; +} + +/// @return the maximum of two integers +B2_INLINE int b2MaxInt( int a, int b ) +{ + return a > b ? a : b; +} + +/// @return the absolute value of an integer +B2_INLINE int b2AbsInt( int a ) +{ + return a < 0 ? -a : a; +} + +/// @return an integer clamped between a lower and upper bound +B2_INLINE int b2ClampInt( int a, int lower, int upper ) +{ + return a < lower ? lower : ( a > upper ? upper : a ); +} + +/// Vector dot product +B2_INLINE float b2Dot( b2Vec2 a, b2Vec2 b ) +{ + return a.x * b.x + a.y * b.y; +} + +/// Vector cross product. In 2D this yields a scalar. +B2_INLINE float b2Cross( b2Vec2 a, b2Vec2 b ) +{ + return a.x * b.y - a.y * b.x; +} + +/// Perform the cross product on a vector and a scalar. In 2D this produces a vector. +B2_INLINE b2Vec2 b2CrossVS( b2Vec2 v, float s ) +{ + return B2_LITERAL( b2Vec2 ){ s * v.y, -s * v.x }; +} + +/// Perform the cross product on a scalar and a vector. In 2D this produces a vector. +B2_INLINE b2Vec2 b2CrossSV( float s, b2Vec2 v ) +{ + return B2_LITERAL( b2Vec2 ){ -s * v.y, s * v.x }; +} + +/// Get a left pointing perpendicular vector. Equivalent to b2CrossSV(1.0f, v) +B2_INLINE b2Vec2 b2LeftPerp( b2Vec2 v ) +{ + return B2_LITERAL( b2Vec2 ){ -v.y, v.x }; +} + +/// Get a right pointing perpendicular vector. Equivalent to b2CrossVS(v, 1.0f) +B2_INLINE b2Vec2 b2RightPerp( b2Vec2 v ) +{ + return B2_LITERAL( b2Vec2 ){ v.y, -v.x }; +} + +/// Vector addition +B2_INLINE b2Vec2 b2Add( b2Vec2 a, b2Vec2 b ) +{ + return B2_LITERAL( b2Vec2 ){ a.x + b.x, a.y + b.y }; +} + +/// Vector subtraction +B2_INLINE b2Vec2 b2Sub( b2Vec2 a, b2Vec2 b ) +{ + return B2_LITERAL( b2Vec2 ){ a.x - b.x, a.y - b.y }; +} + +/// Vector negation +B2_INLINE b2Vec2 b2Neg( b2Vec2 a ) +{ + return B2_LITERAL( b2Vec2 ){ -a.x, -a.y }; +} + +/// Vector linear interpolation +/// https://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/ +B2_INLINE b2Vec2 b2Lerp( b2Vec2 a, b2Vec2 b, float t ) +{ + return B2_LITERAL( b2Vec2 ){ ( 1.0f - t ) * a.x + t * b.x, ( 1.0f - t ) * a.y + t * b.y }; +} + +/// Component-wise multiplication +B2_INLINE b2Vec2 b2Mul( b2Vec2 a, b2Vec2 b ) +{ + return B2_LITERAL( b2Vec2 ){ a.x * b.x, a.y * b.y }; +} + +/// Multiply a scalar and vector +B2_INLINE b2Vec2 b2MulSV( float s, b2Vec2 v ) +{ + return B2_LITERAL( b2Vec2 ){ s * v.x, s * v.y }; +} + +/// a + s * b +B2_INLINE b2Vec2 b2MulAdd( b2Vec2 a, float s, b2Vec2 b ) +{ + return B2_LITERAL( b2Vec2 ){ a.x + s * b.x, a.y + s * b.y }; +} + +/// a - s * b +B2_INLINE b2Vec2 b2MulSub( b2Vec2 a, float s, b2Vec2 b ) +{ + return B2_LITERAL( b2Vec2 ){ a.x - s * b.x, a.y - s * b.y }; +} + +/// Component-wise absolute vector +B2_INLINE b2Vec2 b2Abs( b2Vec2 a ) +{ + b2Vec2 b; + b.x = b2AbsFloat( a.x ); + b.y = b2AbsFloat( a.y ); + return b; +} + +/// Component-wise minimum vector +B2_INLINE b2Vec2 b2Min( b2Vec2 a, b2Vec2 b ) +{ + b2Vec2 c; + c.x = b2MinFloat( a.x, b.x ); + c.y = b2MinFloat( a.y, b.y ); + return c; +} + +/// Component-wise maximum vector +B2_INLINE b2Vec2 b2Max( b2Vec2 a, b2Vec2 b ) +{ + b2Vec2 c; + c.x = b2MaxFloat( a.x, b.x ); + c.y = b2MaxFloat( a.y, b.y ); + return c; +} + +/// Component-wise clamp vector v into the range [a, b] +B2_INLINE b2Vec2 b2Clamp( b2Vec2 v, b2Vec2 a, b2Vec2 b ) +{ + b2Vec2 c; + c.x = b2ClampFloat( v.x, a.x, b.x ); + c.y = b2ClampFloat( v.y, a.y, b.y ); + return c; +} + +/// Get the length of this vector (the norm) +B2_INLINE float b2Length( b2Vec2 v ) +{ + return sqrtf( v.x * v.x + v.y * v.y ); +} + +/// Get the distance between two points +B2_INLINE float b2Distance( b2Vec2 a, b2Vec2 b ) +{ + float dx = b.x - a.x; + float dy = b.y - a.y; + return sqrtf( dx * dx + dy * dy ); +} + +/// Convert a vector into a unit vector if possible, otherwise returns the zero vector. +B2_INLINE b2Vec2 b2Normalize( b2Vec2 v ) +{ + float length = sqrtf( v.x * v.x + v.y * v.y ); + if ( length < FLT_EPSILON ) + { + return b2Vec2_zero; + } + + float invLength = 1.0f / length; + b2Vec2 n = { invLength * v.x, invLength * v.y }; + return n; +} + +/// Convert a vector into a unit vector if possible, otherwise returns the zero vector. Also +/// outputs the length. +B2_INLINE b2Vec2 b2GetLengthAndNormalize( float* length, b2Vec2 v ) +{ + *length = b2Length( v ); + if ( *length < FLT_EPSILON ) + { + return b2Vec2_zero; + } + + float invLength = 1.0f / *length; + b2Vec2 n = { invLength * v.x, invLength * v.y }; + return n; +} + +/// Normalize rotation +B2_INLINE b2Rot b2NormalizeRot( b2Rot q ) +{ + float mag = sqrtf( q.s * q.s + q.c * q.c ); + float invMag = mag > 0.0 ? 1.0f / mag : 0.0f; + b2Rot qn = { q.c * invMag, q.s * invMag }; + return qn; +} + +/// Integration rotation from angular velocity +/// @param q1 initial rotation +/// @param deltaAngle the angular displacement in radians +B2_INLINE b2Rot b2IntegrateRotation( b2Rot q1, float deltaAngle ) +{ + // dc/dt = -omega * sin(t) + // ds/dt = omega * cos(t) + // c2 = c1 - omega * h * s1 + // s2 = s1 + omega * h * c1 + b2Rot q2 = { q1.c - deltaAngle * q1.s, q1.s + deltaAngle * q1.c }; + float mag = sqrtf( q2.s * q2.s + q2.c * q2.c ); + float invMag = mag > 0.0 ? 1.0f / mag : 0.0f; + b2Rot qn = { q2.c * invMag, q2.s * invMag }; + return qn; +} + +/// Get the length squared of this vector +B2_INLINE float b2LengthSquared( b2Vec2 v ) +{ + return v.x * v.x + v.y * v.y; +} + +/// Get the distance squared between points +B2_INLINE float b2DistanceSquared( b2Vec2 a, b2Vec2 b ) +{ + b2Vec2 c = { b.x - a.x, b.y - a.y }; + return c.x * c.x + c.y * c.y; +} + +/// Make a rotation using an angle in radians +B2_API b2CosSin b2ComputeCosSin( float angle ); + +/// Make a rotation using an angle in radians +B2_INLINE b2Rot b2MakeRot( float angle ) +{ + b2CosSin cs = b2ComputeCosSin( angle ); + return B2_LITERAL( b2Rot ){ cs.cosine, cs.sine }; +} + +/// Is this rotation normalized? +B2_INLINE bool b2IsNormalized( b2Rot q ) +{ + // larger tolerance due to failure on mingw 32-bit + float qq = q.s * q.s + q.c * q.c; + return 1.0f - 0.0006f < qq && qq < 1.0f + 0.0006f; +} + +/// Normalized linear interpolation +/// https://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/ +B2_INLINE b2Rot b2NLerp( b2Rot q1, b2Rot q2, float t ) +{ + float omt = 1.0f - t; + b2Rot q = { + omt * q1.c + t * q2.c, + omt * q1.s + t * q2.s, + }; + + return b2NormalizeRot( q ); +} + +/// Compute the angular velocity necessary to rotate between two rotations over a give time +/// @param q1 initial rotation +/// @param q2 final rotation +/// @param inv_h inverse time step +B2_INLINE float b2ComputeAngularVelocity( b2Rot q1, b2Rot q2, float inv_h ) +{ + // ds/dt = omega * cos(t) + // dc/dt = -omega * sin(t) + // s2 = s1 + omega * h * c1 + // c2 = c1 - omega * h * s1 + + // omega * h * s1 = c1 - c2 + // omega * h * c1 = s2 - s1 + // omega * h = (c1 - c2) * s1 + (s2 - s1) * c1; + // omega * h = s1 * c1 - c2 * s1 + s2 * c1 - s1 * c1 + // omega * h = s2 * c1 - c2 * s1 = sin(a2 - a1) ~= a2 - a1 for small delta + float omega = inv_h * ( q2.s * q1.c - q2.c * q1.s ); + return omega; +} + +/// Get the angle in radians in the range [-pi, pi] +B2_INLINE float b2Rot_GetAngle( b2Rot q ) +{ + return b2Atan2( q.s, q.c ); +} + +/// Get the x-axis +B2_INLINE b2Vec2 b2Rot_GetXAxis( b2Rot q ) +{ + b2Vec2 v = { q.c, q.s }; + return v; +} + +/// Get the y-axis +B2_INLINE b2Vec2 b2Rot_GetYAxis( b2Rot q ) +{ + b2Vec2 v = { -q.s, q.c }; + return v; +} + +/// Multiply two rotations: q * r +B2_INLINE b2Rot b2MulRot( b2Rot q, b2Rot r ) +{ + // [qc -qs] * [rc -rs] = [qc*rc-qs*rs -qc*rs-qs*rc] + // [qs qc] [rs rc] [qs*rc+qc*rs -qs*rs+qc*rc] + // s(q + r) = qs * rc + qc * rs + // c(q + r) = qc * rc - qs * rs + b2Rot qr; + qr.s = q.s * r.c + q.c * r.s; + qr.c = q.c * r.c - q.s * r.s; + return qr; +} + +/// Transpose multiply two rotations: qT * r +B2_INLINE b2Rot b2InvMulRot( b2Rot q, b2Rot r ) +{ + // [ qc qs] * [rc -rs] = [qc*rc+qs*rs -qc*rs+qs*rc] + // [-qs qc] [rs rc] [-qs*rc+qc*rs qs*rs+qc*rc] + // s(q - r) = qc * rs - qs * rc + // c(q - r) = qc * rc + qs * rs + b2Rot qr; + qr.s = q.c * r.s - q.s * r.c; + qr.c = q.c * r.c + q.s * r.s; + return qr; +} + +/// relative angle between b and a (rot_b * inv(rot_a)) +B2_INLINE float b2RelativeAngle( b2Rot b, b2Rot a ) +{ + // sin(b - a) = bs * ac - bc * as + // cos(b - a) = bc * ac + bs * as + float s = b.s * a.c - b.c * a.s; + float c = b.c * a.c + b.s * a.s; + return b2Atan2( s, c ); +} + +/// Convert an angle in the range [-2*pi, 2*pi] into the range [-pi, pi] +B2_INLINE float b2UnwindAngle( float angle ) +{ + if ( angle < -b2_pi ) + { + return angle + 2.0f * b2_pi; + } + else if ( angle > b2_pi ) + { + return angle - 2.0f * b2_pi; + } + + return angle; +} + +/// Convert any into the range [-pi, pi] (slow) +B2_INLINE float b2UnwindLargeAngle( float angle ) +{ + while ( angle > b2_pi ) + { + angle -= 2.0f * b2_pi; + } + + while ( angle < -b2_pi ) + { + angle += 2.0f * b2_pi; + } + + return angle; +} + +/// Rotate a vector +B2_INLINE b2Vec2 b2RotateVector( b2Rot q, b2Vec2 v ) +{ + return B2_LITERAL( b2Vec2 ){ q.c * v.x - q.s * v.y, q.s * v.x + q.c * v.y }; +} + +/// Inverse rotate a vector +B2_INLINE b2Vec2 b2InvRotateVector( b2Rot q, b2Vec2 v ) +{ + return B2_LITERAL( b2Vec2 ){ q.c * v.x + q.s * v.y, -q.s * v.x + q.c * v.y }; +} + +/// Transform a point (e.g. local space to world space) +B2_INLINE b2Vec2 b2TransformPoint( b2Transform t, const b2Vec2 p ) +{ + float x = ( t.q.c * p.x - t.q.s * p.y ) + t.p.x; + float y = ( t.q.s * p.x + t.q.c * p.y ) + t.p.y; + + return B2_LITERAL( b2Vec2 ){ x, y }; +} + +/// Inverse transform a point (e.g. world space to local space) +B2_INLINE b2Vec2 b2InvTransformPoint( b2Transform t, const b2Vec2 p ) +{ + float vx = p.x - t.p.x; + float vy = p.y - t.p.y; + return B2_LITERAL( b2Vec2 ){ t.q.c * vx + t.q.s * vy, -t.q.s * vx + t.q.c * vy }; +} + +/// v2 = A.q.Rot(B.q.Rot(v1) + B.p) + A.p +/// = (A.q * B.q).Rot(v1) + A.q.Rot(B.p) + A.p +B2_INLINE b2Transform b2MulTransforms( b2Transform A, b2Transform B ) +{ + b2Transform C; + C.q = b2MulRot( A.q, B.q ); + C.p = b2Add( b2RotateVector( A.q, B.p ), A.p ); + return C; +} + +/// v2 = A.q' * (B.q * v1 + B.p - A.p) +/// = A.q' * B.q * v1 + A.q' * (B.p - A.p) +B2_INLINE b2Transform b2InvMulTransforms( b2Transform A, b2Transform B ) +{ + b2Transform C; + C.q = b2InvMulRot( A.q, B.q ); + C.p = b2InvRotateVector( A.q, b2Sub( B.p, A.p ) ); + return C; +} + +/// Multiply a 2-by-2 matrix times a 2D vector +B2_INLINE b2Vec2 b2MulMV( b2Mat22 A, b2Vec2 v ) +{ + b2Vec2 u = { + A.cx.x * v.x + A.cy.x * v.y, + A.cx.y * v.x + A.cy.y * v.y, + }; + return u; +} + +/// Get the inverse of a 2-by-2 matrix +B2_INLINE b2Mat22 b2GetInverse22( b2Mat22 A ) +{ + float a = A.cx.x, b = A.cy.x, c = A.cx.y, d = A.cy.y; + float det = a * d - b * c; + if ( det != 0.0f ) + { + det = 1.0f / det; + } + + b2Mat22 B = { + { det * d, -det * c }, + { -det * b, det * a }, + }; + return B; +} + +/// Solve A * x = b, where b is a column vector. This is more efficient +/// than computing the inverse in one-shot cases. +B2_INLINE b2Vec2 b2Solve22( b2Mat22 A, b2Vec2 b ) +{ + float a11 = A.cx.x, a12 = A.cy.x, a21 = A.cx.y, a22 = A.cy.y; + float det = a11 * a22 - a12 * a21; + if ( det != 0.0f ) + { + det = 1.0f / det; + } + b2Vec2 x = { det * ( a22 * b.x - a12 * b.y ), det * ( a11 * b.y - a21 * b.x ) }; + return x; +} + +/// Does a fully contain b +B2_INLINE bool b2AABB_Contains( b2AABB a, b2AABB b ) +{ + bool s = true; + s = s && a.lowerBound.x <= b.lowerBound.x; + s = s && a.lowerBound.y <= b.lowerBound.y; + s = s && b.upperBound.x <= a.upperBound.x; + s = s && b.upperBound.y <= a.upperBound.y; + return s; +} + +/// Get the center of the AABB. +B2_INLINE b2Vec2 b2AABB_Center( b2AABB a ) +{ + b2Vec2 b = { 0.5f * ( a.lowerBound.x + a.upperBound.x ), 0.5f * ( a.lowerBound.y + a.upperBound.y ) }; + return b; +} + +/// Get the extents of the AABB (half-widths). +B2_INLINE b2Vec2 b2AABB_Extents( b2AABB a ) +{ + b2Vec2 b = { 0.5f * ( a.upperBound.x - a.lowerBound.x ), 0.5f * ( a.upperBound.y - a.lowerBound.y ) }; + return b; +} + +/// Union of two AABBs +B2_INLINE b2AABB b2AABB_Union( b2AABB a, b2AABB b ) +{ + b2AABB c; + c.lowerBound.x = b2MinFloat( a.lowerBound.x, b.lowerBound.x ); + c.lowerBound.y = b2MinFloat( a.lowerBound.y, b.lowerBound.y ); + c.upperBound.x = b2MaxFloat( a.upperBound.x, b.upperBound.x ); + c.upperBound.y = b2MaxFloat( a.upperBound.y, b.upperBound.y ); + return c; +} + +/// Is this a valid number? Not NaN or infinity. +B2_API bool b2IsValid( float a ); + +/// Is this a valid vector? Not NaN or infinity. +B2_API bool b2Vec2_IsValid( b2Vec2 v ); + +/// Is this a valid rotation? Not NaN or infinity. Is normalized. +B2_API bool b2Rot_IsValid( b2Rot q ); + +/// Is this a valid bounding box? Not Nan or infinity. Upper bound greater than or equal to lower bound. +B2_API bool b2AABB_IsValid( b2AABB aabb ); + +/// Box2D bases all length units on meters, but you may need different units for your game. +/// You can set this value to use different units. This should be done at application startup +/// and only modified once. Default value is 1. +/// @warning This must be modified before any calls to Box2D +B2_API void b2SetLengthUnitsPerMeter( float lengthUnits ); + +/// Get the current length units per meter. +B2_API float b2GetLengthUnitsPerMeter( void ); + +/**@}*/ + +/** + * @defgroup math_cpp C++ Math + * @brief Math operator overloads for C++ + * + * See math_functions.h for details. + * @{ + */ + +#ifdef __cplusplus + +/// Unary add one vector to another +inline void operator+=( b2Vec2& a, b2Vec2 b ) +{ + a.x += b.x; + a.y += b.y; +} + +/// Unary subtract one vector from another +inline void operator-=( b2Vec2& a, b2Vec2 b ) +{ + a.x -= b.x; + a.y -= b.y; +} + +/// Unary multiply a vector by a scalar +inline void operator*=( b2Vec2& a, float b ) +{ + a.x *= b; + a.y *= b; +} + +/// Unary negate a vector +inline b2Vec2 operator-( b2Vec2 a ) +{ + return { -a.x, -a.y }; +} + +/// Binary vector addition +inline b2Vec2 operator+( b2Vec2 a, b2Vec2 b ) +{ + return { a.x + b.x, a.y + b.y }; +} + +/// Binary vector subtraction +inline b2Vec2 operator-( b2Vec2 a, b2Vec2 b ) +{ + return { a.x - b.x, a.y - b.y }; +} + +/// Binary scalar and vector multiplication +inline b2Vec2 operator*( float a, b2Vec2 b ) +{ + return { a * b.x, a * b.y }; +} + +/// Binary scalar and vector multiplication +inline b2Vec2 operator*( b2Vec2 a, float b ) +{ + return { a.x * b, a.y * b }; +} + +/// Binary vector equality +inline bool operator==( b2Vec2 a, b2Vec2 b ) +{ + return a.x == b.x && a.y == b.y; +} + +/// Binary vector inequality +inline bool operator!=( b2Vec2 a, b2Vec2 b ) +{ + return a.x != b.x || a.y != b.y; +} + +#endif + +/**@}*/ diff --git a/3rdparty/box2d/include/box2d/types.h b/3rdparty/box2d/include/box2d/types.h new file mode 100644 index 000000000000..2bec968ba177 --- /dev/null +++ b/3rdparty/box2d/include/box2d/types.h @@ -0,0 +1,1315 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include "base.h" +#include "collision.h" +#include "id.h" +#include "math_functions.h" + +#include +#include + +/// Task interface +/// This is prototype for a Box2D task. Your task system is expected to invoke the Box2D task with these arguments. +/// The task spans a range of the parallel-for: [startIndex, endIndex) +/// The worker index must correctly identify each worker in the user thread pool, expected in [0, workerCount). +/// A worker must only exist on only one thread at a time and is analogous to the thread index. +/// The task context is the context pointer sent from Box2D when it is enqueued. +/// The startIndex and endIndex are expected in the range [0, itemCount) where itemCount is the argument to b2EnqueueTaskCallback +/// below. Box2D expects startIndex < endIndex and will execute a loop like this: +/// +/// @code{.c} +/// for (int i = startIndex; i < endIndex; ++i) +/// { +/// DoWork(); +/// } +/// @endcode +/// @ingroup world +typedef void b2TaskCallback( int32_t startIndex, int32_t endIndex, uint32_t workerIndex, void* taskContext ); + +/// These functions can be provided to Box2D to invoke a task system. These are designed to work well with enkiTS. +/// Returns a pointer to the user's task object. May be nullptr. A nullptr indicates to Box2D that the work was executed +/// serially within the callback and there is no need to call b2FinishTaskCallback. +/// The itemCount is the number of Box2D work items that are to be partitioned among workers by the user's task system. +/// This is essentially a parallel-for. The minRange parameter is a suggestion of the minimum number of items to assign +/// per worker to reduce overhead. For example, suppose the task is small and that itemCount is 16. A minRange of 8 suggests +/// that your task system should split the work items among just two workers, even if you have more available. +/// In general the range [startIndex, endIndex) send to b2TaskCallback should obey: +/// endIndex - startIndex >= minRange +/// The exception of course is when itemCount < minRange. +/// @ingroup world +typedef void* b2EnqueueTaskCallback( b2TaskCallback* task, int32_t itemCount, int32_t minRange, void* taskContext, + void* userContext ); + +/// Finishes a user task object that wraps a Box2D task. +/// @ingroup world +typedef void b2FinishTaskCallback( void* userTask, void* userContext ); + +/// Result from b2World_RayCastClosest +/// @ingroup world +typedef struct b2RayResult +{ + b2ShapeId shapeId; + b2Vec2 point; + b2Vec2 normal; + float fraction; + bool hit; +} b2RayResult; + +/// World definition used to create a simulation world. +/// Must be initialized using b2DefaultWorldDef(). +/// @ingroup world +typedef struct b2WorldDef +{ + /// Gravity vector. Box2D has no up-vector defined. + b2Vec2 gravity; + + /// Restitution velocity threshold, usually in m/s. Collisions above this + /// speed have restitution applied (will bounce). + float restitutionThreshold; + + /// This parameter controls how fast overlap is resolved and has units of meters per second + float contactPushoutVelocity; + + /// Threshold velocity for hit events. Usually meters per second. + float hitEventThreshold; + + /// Contact stiffness. Cycles per second. + float contactHertz; + + /// Contact bounciness. Non-dimensional. + float contactDampingRatio; + + /// Joint stiffness. Cycles per second. + float jointHertz; + + /// Joint bounciness. Non-dimensional. + float jointDampingRatio; + + /// Maximum linear velocity. Usually meters per second. + float maximumLinearVelocity; + + /// Can bodies go to sleep to improve performance + bool enableSleep; + + /// Enable continuous collision + bool enableContinuous; + + /// Number of workers to use with the provided task system. Box2D performs best when using only + /// performance cores and accessing a single L2 cache. Efficiency cores and hyper-threading provide + /// little benefit and may even harm performance. + int32_t workerCount; + + /// Function to spawn tasks + b2EnqueueTaskCallback* enqueueTask; + + /// Function to finish a task + b2FinishTaskCallback* finishTask; + + /// User context that is provided to enqueueTask and finishTask + void* userTaskContext; + + /// Used internally to detect a valid definition. DO NOT SET. + int32_t internalValue; +} b2WorldDef; + +/// Use this to initialize your world definition +/// @ingroup world +B2_API b2WorldDef b2DefaultWorldDef( void ); + +/// The body simulation type. +/// Each body is one of these three types. The type determines how the body behaves in the simulation. +/// @ingroup body +typedef enum b2BodyType +{ + /// zero mass, zero velocity, may be manually moved + b2_staticBody = 0, + + /// zero mass, velocity set by user, moved by solver + b2_kinematicBody = 1, + + /// positive mass, velocity determined by forces, moved by solver + b2_dynamicBody = 2, + + /// number of body types + b2_bodyTypeCount, +} b2BodyType; + +/// A body definition holds all the data needed to construct a rigid body. +/// You can safely re-use body definitions. Shapes are added to a body after construction. +/// Body definitions are temporary objects used to bundle creation parameters. +/// Must be initialized using b2DefaultBodyDef(). +/// @ingroup body +typedef struct b2BodyDef +{ + /// The body type: static, kinematic, or dynamic. + b2BodyType type; + + /// The initial world position of the body. Bodies should be created with the desired position. + /// @note Creating bodies at the origin and then moving them nearly doubles the cost of body creation, especially + /// if the body is moved after shapes have been added. + b2Vec2 position; + + /// The initial world rotation of the body. Use b2MakeRot() if you have an angle. + b2Rot rotation; + + /// The initial linear velocity of the body's origin. Typically in meters per second. + b2Vec2 linearVelocity; + + /// The initial angular velocity of the body. Radians per second. + float angularVelocity; + + /// Linear damping is use to reduce the linear velocity. The damping parameter + /// can be larger than 1 but the damping effect becomes sensitive to the + /// time step when the damping parameter is large. + /// Generally linear damping is undesirable because it makes objects move slowly + /// as if they are floating. + float linearDamping; + + /// Angular damping is use to reduce the angular velocity. The damping parameter + /// can be larger than 1.0f but the damping effect becomes sensitive to the + /// time step when the damping parameter is large. + /// Angular damping can be use slow down rotating bodies. + float angularDamping; + + /// Scale the gravity applied to this body. Non-dimensional. + float gravityScale; + + /// Sleep velocity threshold, default is 0.05 meter per second + float sleepThreshold; + + /// Use this to store application specific body data. + void* userData; + + /// Set this flag to false if this body should never fall asleep. + bool enableSleep; + + /// Is this body initially awake or sleeping? + bool isAwake; + + /// Should this body be prevented from rotating? Useful for characters. + bool fixedRotation; + + /// Treat this body as high speed object that performs continuous collision detection + /// against dynamic and kinematic bodies, but not other bullet bodies. + /// @warning Bullets should be used sparingly. They are not a solution for general dynamic-versus-dynamic + /// continuous collision. They may interfere with joint constraints. + bool isBullet; + + /// Used to disable a body. A disabled body does not move or collide. + bool isEnabled; + + /// Automatically compute mass and related properties on this body from shapes. + /// Triggers whenever a shape is add/removed/changed. Default is true. + bool automaticMass; + + /// This allows this body to bypass rotational speed limits. Should only be used + /// for circular objects, like wheels. + bool allowFastRotation; + + /// Used internally to detect a valid definition. DO NOT SET. + int32_t internalValue; +} b2BodyDef; + +/// Use this to initialize your body definition +/// @ingroup body +B2_API b2BodyDef b2DefaultBodyDef( void ); + +/// This is used to filter collision on shapes. It affects shape-vs-shape collision +/// and shape-versus-query collision (such as b2World_CastRay). +/// @ingroup shape +typedef struct b2Filter +{ + /// The collision category bits. Normally you would just set one bit. The category bits should + /// represent your application object types. For example: + /// @code{.cpp} + /// enum MyCategories + /// { + /// Static = 0x00000001, + /// Dynamic = 0x00000002, + /// Debris = 0x00000004, + /// Player = 0x00000008, + /// // etc + /// }; + /// @endcode + uint64_t categoryBits; + + /// The collision mask bits. This states the categories that this + /// shape would accept for collision. + /// For example, you may want your player to only collide with static objects + /// and other players. + /// @code{.c} + /// maskBits = Static | Player; + /// @endcode + uint64_t maskBits; + + /// Collision groups allow a certain group of objects to never collide (negative) + /// or always collide (positive). A group index of zero has no effect. Non-zero group filtering + /// always wins against the mask bits. + /// For example, you may want ragdolls to collide with other ragdolls but you don't want + /// ragdoll self-collision. In this case you would give each ragdoll a unique negative group index + /// and apply that group index to all shapes on the ragdoll. + int32_t groupIndex; +} b2Filter; + +/// Use this to initialize your filter +/// @ingroup shape +B2_API b2Filter b2DefaultFilter( void ); + +/// The query filter is used to filter collisions between queries and shapes. For example, +/// you may want a ray-cast representing a projectile to hit players and the static environment +/// but not debris. +/// @ingroup shape +typedef struct b2QueryFilter +{ + /// The collision category bits of this query. Normally you would just set one bit. + uint64_t categoryBits; + + /// The collision mask bits. This states the shape categories that this + /// query would accept for collision. + uint64_t maskBits; +} b2QueryFilter; + +/// Use this to initialize your query filter +/// @ingroup shape +B2_API b2QueryFilter b2DefaultQueryFilter( void ); + +/// Shape type +/// @ingroup shape +typedef enum b2ShapeType +{ + /// A circle with an offset + b2_circleShape, + + /// A capsule is an extruded circle + b2_capsuleShape, + + /// A line segment + b2_segmentShape, + + /// A convex polygon + b2_polygonShape, + + /// A line segment owned by a chain shape + b2_chainSegmentShape, + + /// The number of shape types + b2_shapeTypeCount +} b2ShapeType; + +/// Used to create a shape. +/// This is a temporary object used to bundle shape creation parameters. You may use +/// the same shape definition to create multiple shapes. +/// Must be initialized using b2DefaultShapeDef(). +/// @ingroup shape +typedef struct b2ShapeDef +{ + /// Use this to store application specific shape data. + void* userData; + + /// The Coulomb (dry) friction coefficient, usually in the range [0,1]. + float friction; + + /// The restitution (bounce) usually in the range [0,1]. + float restitution; + + /// The density, usually in kg/m^2. + float density; + + /// Collision filtering data. + b2Filter filter; + + /// Custom debug draw color. + uint32_t customColor; + + /// A sensor shape generates overlap events but never generates a collision response. + /// Sensors do not collide with other sensors and do not have continuous collision. + /// Instead use a ray or shape cast for those scenarios. + bool isSensor; + + /// Enable sensor events for this shape. Only applies to kinematic and dynamic bodies. Ignored for sensors. + bool enableSensorEvents; + + /// Enable contact events for this shape. Only applies to kinematic and dynamic bodies. Ignored for sensors. + bool enableContactEvents; + + /// Enable hit events for this shape. Only applies to kinematic and dynamic bodies. Ignored for sensors. + bool enableHitEvents; + + /// Enable pre-solve contact events for this shape. Only applies to dynamic bodies. These are expensive + /// and must be carefully handled due to threading. Ignored for sensors. + bool enablePreSolveEvents; + + /// Normally shapes on static bodies don't invoke contact creation when they are added to the world. This overrides + /// that behavior and causes contact creation. This significantly slows down static body creation which can be important + /// when there are many static shapes. + /// This is implicitly always true for sensors. + bool forceContactCreation; + + /// Used internally to detect a valid definition. DO NOT SET. + int32_t internalValue; +} b2ShapeDef; + +/// Use this to initialize your shape definition +/// @ingroup shape +B2_API b2ShapeDef b2DefaultShapeDef( void ); + +/// Used to create a chain of line segments. This is designed to eliminate ghost collisions with some limitations. +/// - chains are one-sided +/// - chains have no mass and should be used on static bodies +/// - chains have a counter-clockwise winding order +/// - chains are either a loop or open +/// - a chain must have at least 4 points +/// - the distance between any two points must be greater than b2_linearSlop +/// - a chain shape should not self intersect (this is not validated) +/// - an open chain shape has NO COLLISION on the first and final edge +/// - you may overlap two open chains on their first three and/or last three points to get smooth collision +/// - a chain shape creates multiple line segment shapes on the body +/// https://en.wikipedia.org/wiki/Polygonal_chain +/// Must be initialized using b2DefaultChainDef(). +/// @warning Do not use chain shapes unless you understand the limitations. This is an advanced feature. +/// @ingroup shape +typedef struct b2ChainDef +{ + /// Use this to store application specific shape data. + void* userData; + + /// An array of at least 4 points. These are cloned and may be temporary. + const b2Vec2* points; + + /// The point count, must be 4 or more. + int32_t count; + + /// The friction coefficient, usually in the range [0,1]. + float friction; + + /// The restitution (elasticity) usually in the range [0,1]. + float restitution; + + /// Contact filtering data. + b2Filter filter; + + /// Indicates a closed chain formed by connecting the first and last points + bool isLoop; + + /// Used internally to detect a valid definition. DO NOT SET. + int32_t internalValue; +} b2ChainDef; + +/// Use this to initialize your chain definition +/// @ingroup shape +B2_API b2ChainDef b2DefaultChainDef( void ); + +//! @cond +/// Profiling data. Times are in milliseconds. +typedef struct b2Profile +{ + float step; + float pairs; + float collide; + float solve; + float buildIslands; + float solveConstraints; + float prepareTasks; + float solverTasks; + float prepareConstraints; + float integrateVelocities; + float warmStart; + float solveVelocities; + float integratePositions; + float relaxVelocities; + float applyRestitution; + float storeImpulses; + float finalizeBodies; + float splitIslands; + float sleepIslands; + float hitEvents; + float broadphase; + float continuous; +} b2Profile; + +/// Counters that give details of the simulation size. +typedef struct b2Counters +{ + int32_t bodyCount; + int32_t shapeCount; + int32_t contactCount; + int32_t jointCount; + int32_t islandCount; + int32_t stackUsed; + int32_t staticTreeHeight; + int32_t treeHeight; + int32_t byteCount; + int32_t taskCount; + int32_t colorCounts[12]; +} b2Counters; +//! @endcond + +/// Joint type enumeration +/// +/// This is useful because all joint types use b2JointId and sometimes you +/// want to get the type of a joint. +/// @ingroup joint +typedef enum b2JointType +{ + b2_distanceJoint, + b2_motorJoint, + b2_mouseJoint, + b2_prismaticJoint, + b2_revoluteJoint, + b2_weldJoint, + b2_wheelJoint, +} b2JointType; + +/// Distance joint definition +/// +/// This requires defining an anchor point on both +/// bodies and the non-zero distance of the distance joint. The definition uses +/// local anchor points so that the initial configuration can violate the +/// constraint slightly. This helps when saving and loading a game. +/// @ingroup distance_joint +typedef struct b2DistanceJointDef +{ + /// The first attached body + b2BodyId bodyIdA; + + /// The second attached body + b2BodyId bodyIdB; + + /// The local anchor point relative to bodyA's origin + b2Vec2 localAnchorA; + + /// The local anchor point relative to bodyB's origin + b2Vec2 localAnchorB; + + /// The rest length of this joint. Clamped to a stable minimum value. + float length; + + /// Enable the distance constraint to behave like a spring. If false + /// then the distance joint will be rigid, overriding the limit and motor. + bool enableSpring; + + /// The spring linear stiffness Hertz, cycles per second + float hertz; + + /// The spring linear damping ratio, non-dimensional + float dampingRatio; + + /// Enable/disable the joint limit + bool enableLimit; + + /// Minimum length. Clamped to a stable minimum value. + float minLength; + + /// Maximum length. Must be greater than or equal to the minimum length. + float maxLength; + + /// Enable/disable the joint motor + bool enableMotor; + + /// The maximum motor force, usually in newtons + float maxMotorForce; + + /// The desired motor speed, usually in meters per second + float motorSpeed; + + /// Set this flag to true if the attached bodies should collide + bool collideConnected; + + /// User data pointer + void* userData; + + /// Used internally to detect a valid definition. DO NOT SET. + int32_t internalValue; +} b2DistanceJointDef; + +/// Use this to initialize your joint definition +/// @ingroup distance_joint +B2_API b2DistanceJointDef b2DefaultDistanceJointDef( void ); + +/// A motor joint is used to control the relative motion between two bodies +/// +/// A typical usage is to control the movement of a dynamic body with respect to the ground. +/// @ingroup motor_joint +typedef struct b2MotorJointDef +{ + /// The first attached body + b2BodyId bodyIdA; + + /// The second attached body + b2BodyId bodyIdB; + + /// Position of bodyB minus the position of bodyA, in bodyA's frame + b2Vec2 linearOffset; + + /// The bodyB angle minus bodyA angle in radians + float angularOffset; + + /// The maximum motor force in newtons + float maxForce; + + /// The maximum motor torque in newton-meters + float maxTorque; + + /// Position correction factor in the range [0,1] + float correctionFactor; + + /// Set this flag to true if the attached bodies should collide + bool collideConnected; + + /// User data pointer + void* userData; + + /// Used internally to detect a valid definition. DO NOT SET. + int32_t internalValue; +} b2MotorJointDef; + +/// Use this to initialize your joint definition +/// @ingroup motor_joint +B2_API b2MotorJointDef b2DefaultMotorJointDef( void ); + +/// A mouse joint is used to make a point on a body track a specified world point. +/// +/// This a soft constraint and allows the constraint to stretch without +/// applying huge forces. This also applies rotation constraint heuristic to improve control. +/// @ingroup mouse_joint +typedef struct b2MouseJointDef +{ + /// The first attached body. + b2BodyId bodyIdA; + + /// The second attached body. + b2BodyId bodyIdB; + + /// The initial target point in world space + b2Vec2 target; + + /// Stiffness in hertz + float hertz; + + /// Damping ratio, non-dimensional + float dampingRatio; + + /// Maximum force, typically in newtons + float maxForce; + + /// Set this flag to true if the attached bodies should collide. + bool collideConnected; + + /// User data pointer + void* userData; + + /// Used internally to detect a valid definition. DO NOT SET. + int32_t internalValue; +} b2MouseJointDef; + +/// Use this to initialize your joint definition +/// @ingroup mouse_joint +B2_API b2MouseJointDef b2DefaultMouseJointDef( void ); + +/// Prismatic joint definition +/// +/// This requires defining a line of motion using an axis and an anchor point. +/// The definition uses local anchor points and a local axis so that the initial +/// configuration can violate the constraint slightly. The joint translation is zero +/// when the local anchor points coincide in world space. +/// @ingroup prismatic_joint +typedef struct b2PrismaticJointDef +{ + /// The first attached body + b2BodyId bodyIdA; + + /// The second attached body + b2BodyId bodyIdB; + + /// The local anchor point relative to bodyA's origin + b2Vec2 localAnchorA; + + /// The local anchor point relative to bodyB's origin + b2Vec2 localAnchorB; + + /// The local translation unit axis in bodyA + b2Vec2 localAxisA; + + /// The constrained angle between the bodies: bodyB_angle - bodyA_angle + float referenceAngle; + + /// Enable a linear spring along the prismatic joint axis + bool enableSpring; + + /// The spring stiffness Hertz, cycles per second + float hertz; + + /// The spring damping ratio, non-dimensional + float dampingRatio; + + /// Enable/disable the joint limit + bool enableLimit; + + /// The lower translation limit + float lowerTranslation; + + /// The upper translation limit + float upperTranslation; + + /// Enable/disable the joint motor + bool enableMotor; + + /// The maximum motor force, typically in newtons + float maxMotorForce; + + /// The desired motor speed, typically in meters per second + float motorSpeed; + + /// Set this flag to true if the attached bodies should collide + bool collideConnected; + + /// User data pointer + void* userData; + + /// Used internally to detect a valid definition. DO NOT SET. + int32_t internalValue; +} b2PrismaticJointDef; + +/// Use this to initialize your joint definition +/// @ingroupd prismatic_joint +B2_API b2PrismaticJointDef b2DefaultPrismaticJointDef( void ); + +/// Revolute joint definition +/// +/// This requires defining an anchor point where the bodies are joined. +/// The definition uses local anchor points so that the +/// initial configuration can violate the constraint slightly. You also need to +/// specify the initial relative angle for joint limits. This helps when saving +/// and loading a game. +/// The local anchor points are measured from the body's origin +/// rather than the center of mass because: +/// 1. you might not know where the center of mass will be +/// 2. if you add/remove shapes from a body and recompute the mass, the joints will be broken +/// @ingroup revolute_joint +typedef struct b2RevoluteJointDef +{ + /// The first attached body + b2BodyId bodyIdA; + + /// The second attached body + b2BodyId bodyIdB; + + /// The local anchor point relative to bodyA's origin + b2Vec2 localAnchorA; + + /// The local anchor point relative to bodyB's origin + b2Vec2 localAnchorB; + + /// The bodyB angle minus bodyA angle in the reference state (radians). + /// This defines the zero angle for the joint limit. + float referenceAngle; + + /// Enable a rotational spring on the revolute hinge axis + bool enableSpring; + + /// The spring stiffness Hertz, cycles per second + float hertz; + + /// The spring damping ratio, non-dimensional + float dampingRatio; + + /// A flag to enable joint limits + bool enableLimit; + + /// The lower angle for the joint limit in radians + float lowerAngle; + + /// The upper angle for the joint limit in radians + float upperAngle; + + /// A flag to enable the joint motor + bool enableMotor; + + /// The maximum motor torque, typically in newton-meters + float maxMotorTorque; + + /// The desired motor speed in radians per second + float motorSpeed; + + /// Scale the debug draw + float drawSize; + + /// Set this flag to true if the attached bodies should collide + bool collideConnected; + + /// User data pointer + void* userData; + + /// Used internally to detect a valid definition. DO NOT SET. + int32_t internalValue; +} b2RevoluteJointDef; + +/// Use this to initialize your joint definition. +/// @ingroup revolute_joint +B2_API b2RevoluteJointDef b2DefaultRevoluteJointDef( void ); + +/// Weld joint definition +/// +/// A weld joint connect to bodies together rigidly. This constraint provides springs to mimic +/// soft-body simulation. +/// @note The approximate solver in Box2D cannot hold many bodies together rigidly +/// @ingroup weld_joint +typedef struct b2WeldJointDef +{ + /// The first attached body + b2BodyId bodyIdA; + + /// The second attached body + b2BodyId bodyIdB; + + /// The local anchor point relative to bodyA's origin + b2Vec2 localAnchorA; + + /// The local anchor point relative to bodyB's origin + b2Vec2 localAnchorB; + + /// The bodyB angle minus bodyA angle in the reference state (radians) + float referenceAngle; + + /// Linear stiffness expressed as Hertz (cycles per second). Use zero for maximum stiffness. + float linearHertz; + + /// Angular stiffness as Hertz (cycles per second). Use zero for maximum stiffness. + float angularHertz; + + /// Linear damping ratio, non-dimensional. Use 1 for critical damping. + float linearDampingRatio; + + /// Linear damping ratio, non-dimensional. Use 1 for critical damping. + float angularDampingRatio; + + /// Set this flag to true if the attached bodies should collide + bool collideConnected; + + /// User data pointer + void* userData; + + /// Used internally to detect a valid definition. DO NOT SET. + int32_t internalValue; +} b2WeldJointDef; + +/// Use this to initialize your joint definition +/// @ingroup weld_joint +B2_API b2WeldJointDef b2DefaultWeldJointDef( void ); + +/// Wheel joint definition +/// +/// This requires defining a line of motion using an axis and an anchor point. +/// The definition uses local anchor points and a local axis so that the initial +/// configuration can violate the constraint slightly. The joint translation is zero +/// when the local anchor points coincide in world space. +/// @ingroup wheel_joint +typedef struct b2WheelJointDef +{ + /// The first attached body + b2BodyId bodyIdA; + + /// The second attached body + b2BodyId bodyIdB; + + /// The local anchor point relative to bodyA's origin + b2Vec2 localAnchorA; + + /// The local anchor point relative to bodyB's origin + b2Vec2 localAnchorB; + + /// The local translation unit axis in bodyA + b2Vec2 localAxisA; + + /// Enable a linear spring along the local axis + bool enableSpring; + + /// Spring stiffness in Hertz + float hertz; + + /// Spring damping ratio, non-dimensional + float dampingRatio; + + /// Enable/disable the joint linear limit + bool enableLimit; + + /// The lower translation limit + float lowerTranslation; + + /// The upper translation limit + float upperTranslation; + + /// Enable/disable the joint rotational motor + bool enableMotor; + + /// The maximum motor torque, typically in newton-meters + float maxMotorTorque; + + /// The desired motor speed in radians per second + float motorSpeed; + + /// Set this flag to true if the attached bodies should collide + bool collideConnected; + + /// User data pointer + void* userData; + + /// Used internally to detect a valid definition. DO NOT SET. + int32_t internalValue; +} b2WheelJointDef; + +/// Use this to initialize your joint definition +/// @ingroup wheel_joint +B2_API b2WheelJointDef b2DefaultWheelJointDef( void ); + +/** + * @defgroup events Events + * World event types. + * + * Events are used to collect events that occur during the world time step. These events + * are then available to query after the time step is complete. This is preferable to callbacks + * because Box2D uses multithreaded simulation. + * + * Also when events occur in the simulation step it may be problematic to modify the world, which is + * often what applications want to do when events occur. + * + * With event arrays, you can scan the events in a loop and modify the world. However, you need to be careful + * that some event data may become invalid. There are several samples that show how to do this safely. + * + * @{ + */ + +/// A begin touch event is generated when a shape starts to overlap a sensor shape. +typedef struct b2SensorBeginTouchEvent +{ + /// The id of the sensor shape + b2ShapeId sensorShapeId; + + /// The id of the dynamic shape that began touching the sensor shape + b2ShapeId visitorShapeId; +} b2SensorBeginTouchEvent; + +/// An end touch event is generated when a shape stops overlapping a sensor shape. +typedef struct b2SensorEndTouchEvent +{ + /// The id of the sensor shape + b2ShapeId sensorShapeId; + + /// The id of the dynamic shape that stopped touching the sensor shape + b2ShapeId visitorShapeId; +} b2SensorEndTouchEvent; + +/// Sensor events are buffered in the Box2D world and are available +/// as begin/end overlap event arrays after the time step is complete. +/// Note: these may become invalid if bodies and/or shapes are destroyed +typedef struct b2SensorEvents +{ + /// Array of sensor begin touch events + b2SensorBeginTouchEvent* beginEvents; + + /// Array of sensor end touch events + b2SensorEndTouchEvent* endEvents; + + /// The number of begin touch events + int32_t beginCount; + + /// The number of end touch events + int32_t endCount; +} b2SensorEvents; + +/// A begin touch event is generated when two shapes begin touching. +typedef struct b2ContactBeginTouchEvent +{ + /// Id of the first shape + b2ShapeId shapeIdA; + + /// Id of the second shape + b2ShapeId shapeIdB; +} b2ContactBeginTouchEvent; + +/// An end touch event is generated when two shapes stop touching. +typedef struct b2ContactEndTouchEvent +{ + /// Id of the first shape + b2ShapeId shapeIdA; + + /// Id of the second shape + b2ShapeId shapeIdB; +} b2ContactEndTouchEvent; + +/// A hit touch event is generated when two shapes collide with a speed faster than the hit speed threshold. +typedef struct b2ContactHitEvent +{ + /// Id of the first shape + b2ShapeId shapeIdA; + + /// Id of the second shape + b2ShapeId shapeIdB; + + /// Point where the shapes hit + b2Vec2 point; + + /// Normal vector pointing from shape A to shape B + b2Vec2 normal; + + /// The speed the shapes are approaching. Always positive. Typically in meters per second. + float approachSpeed; +} b2ContactHitEvent; + +/// Contact events are buffered in the Box2D world and are available +/// as event arrays after the time step is complete. +/// Note: these may become invalid if bodies and/or shapes are destroyed +typedef struct b2ContactEvents +{ + /// Array of begin touch events + b2ContactBeginTouchEvent* beginEvents; + + /// Array of end touch events + b2ContactEndTouchEvent* endEvents; + + /// Array of hit events + b2ContactHitEvent* hitEvents; + + /// Number of begin touch events + int32_t beginCount; + + /// Number of end touch events + int32_t endCount; + + /// Number of hit events + int32_t hitCount; +} b2ContactEvents; + +/// Body move events triggered when a body moves. +/// Triggered when a body moves due to simulation. Not reported for bodies moved by the user. +/// This also has a flag to indicate that the body went to sleep so the application can also +/// sleep that actor/entity/object associated with the body. +/// On the other hand if the flag does not indicate the body went to sleep then the application +/// can treat the actor/entity/object associated with the body as awake. +/// This is an efficient way for an application to update game object transforms rather than +/// calling functions such as b2Body_GetTransform() because this data is delivered as a contiguous array +/// and it is only populated with bodies that have moved. +/// @note If sleeping is disabled all dynamic and kinematic bodies will trigger move events. +typedef struct b2BodyMoveEvent +{ + b2Transform transform; + b2BodyId bodyId; + void* userData; + bool fellAsleep; +} b2BodyMoveEvent; + +/// Body events are buffered in the Box2D world and are available +/// as event arrays after the time step is complete. +/// Note: this data becomes invalid if bodies are destroyed +typedef struct b2BodyEvents +{ + /// Array of move events + b2BodyMoveEvent* moveEvents; + + /// Number of move events + int32_t moveCount; +} b2BodyEvents; + +/// The contact data for two shapes. By convention the manifold normal points +/// from shape A to shape B. +/// @see b2Shape_GetContactData() and b2Body_GetContactData() +typedef struct b2ContactData +{ + b2ShapeId shapeIdA; + b2ShapeId shapeIdB; + b2Manifold manifold; +} b2ContactData; + +/**@}*/ + +/// Prototype for a contact filter callback. +/// This is called when a contact pair is considered for collision. This allows you to +/// perform custom logic to prevent collision between shapes. This is only called if +/// one of the two shapes has custom filtering enabled. @see b2ShapeDef. +/// Notes: +/// - this function must be thread-safe +/// - this is only called if one of the two shapes has enabled custom filtering +/// - this is called only for awake dynamic bodies +/// Return false if you want to disable the collision +/// @warning Do not attempt to modify the world inside this callback +/// @ingroup world +typedef bool b2CustomFilterFcn( b2ShapeId shapeIdA, b2ShapeId shapeIdB, void* context ); + +/// Prototype for a pre-solve callback. +/// This is called after a contact is updated. This allows you to inspect a +/// contact before it goes to the solver. If you are careful, you can modify the +/// contact manifold (e.g. modify the normal). +/// Notes: +/// - this function must be thread-safe +/// - this is only called if the shape has enabled pre-solve events +/// - this is called only for awake dynamic bodies +/// - this is not called for sensors +/// - the supplied manifold has impulse values from the previous step +/// Return false if you want to disable the contact this step +/// @warning Do not attempt to modify the world inside this callback +/// @ingroup world +typedef bool b2PreSolveFcn( b2ShapeId shapeIdA, b2ShapeId shapeIdB, b2Manifold* manifold, void* context ); + +/// Prototype callback for overlap queries. +/// Called for each shape found in the query. +/// @see b2World_QueryAABB +/// @return false to terminate the query. +/// @ingroup world +typedef bool b2OverlapResultFcn( b2ShapeId shapeId, void* context ); + +/// Prototype callback for ray casts. +/// Called for each shape found in the query. You control how the ray cast +/// proceeds by returning a float: +/// return -1: ignore this shape and continue +/// return 0: terminate the ray cast +/// return fraction: clip the ray to this point +/// return 1: don't clip the ray and continue +/// @param shapeId the shape hit by the ray +/// @param point the point of initial intersection +/// @param normal the normal vector at the point of intersection +/// @param fraction the fraction along the ray at the point of intersection +/// @param context the user context +/// @return -1 to filter, 0 to terminate, fraction to clip the ray for closest hit, 1 to continue +/// @see b2World_CastRay +/// @ingroup world +typedef float b2CastResultFcn( b2ShapeId shapeId, b2Vec2 point, b2Vec2 normal, float fraction, void* context ); + +/// These colors are used for debug draw. +/// See https://www.rapidtables.com/web/color/index.html +typedef enum b2HexColor +{ + b2_colorAliceBlue = 0xf0f8ff, + b2_colorAntiqueWhite = 0xfaebd7, + b2_colorAquamarine = 0x7fffd4, + b2_colorAzure = 0xf0ffff, + b2_colorBeige = 0xf5f5dc, + b2_colorBisque = 0xffe4c4, + b2_colorBlack = 0x000000, + b2_colorBlanchedAlmond = 0xffebcd, + b2_colorBlue = 0x0000ff, + b2_colorBlueViolet = 0x8a2be2, + b2_colorBrown = 0xa52a2a, + b2_colorBurlywood = 0xdeb887, + b2_colorCadetBlue = 0x5f9ea0, + b2_colorChartreuse = 0x7fff00, + b2_colorChocolate = 0xd2691e, + b2_colorCoral = 0xff7f50, + b2_colorCornflowerBlue = 0x6495ed, + b2_colorCornsilk = 0xfff8dc, + b2_colorCrimson = 0xdc143c, + b2_colorCyan = 0x00ffff, + b2_colorDarkBlue = 0x00008b, + b2_colorDarkCyan = 0x008b8b, + b2_colorDarkGoldenrod = 0xb8860b, + b2_colorDarkGray = 0xa9a9a9, + b2_colorDarkGreen = 0x006400, + b2_colorDarkKhaki = 0xbdb76b, + b2_colorDarkMagenta = 0x8b008b, + b2_colorDarkOliveGreen = 0x556b2f, + b2_colorDarkOrange = 0xff8c00, + b2_colorDarkOrchid = 0x9932cc, + b2_colorDarkRed = 0x8b0000, + b2_colorDarkSalmon = 0xe9967a, + b2_colorDarkSeaGreen = 0x8fbc8f, + b2_colorDarkSlateBlue = 0x483d8b, + b2_colorDarkSlateGray = 0x2f4f4f, + b2_colorDarkTurquoise = 0x00ced1, + b2_colorDarkViolet = 0x9400d3, + b2_colorDeepPink = 0xff1493, + b2_colorDeepSkyBlue = 0x00bfff, + b2_colorDimGray = 0x696969, + b2_colorDodgerBlue = 0x1e90ff, + b2_colorFirebrick = 0xb22222, + b2_colorFloralWhite = 0xfffaf0, + b2_colorForestGreen = 0x228b22, + b2_colorGainsboro = 0xdcdcdc, + b2_colorGhostWhite = 0xf8f8ff, + b2_colorGold = 0xffd700, + b2_colorGoldenrod = 0xdaa520, + b2_colorGray = 0xbebebe, + b2_colorGray1 = 0x1a1a1a, + b2_colorGray2 = 0x333333, + b2_colorGray3 = 0x4d4d4d, + b2_colorGray4 = 0x666666, + b2_colorGray5 = 0x7f7f7f, + b2_colorGray6 = 0x999999, + b2_colorGray7 = 0xb3b3b3, + b2_colorGray8 = 0xcccccc, + b2_colorGray9 = 0xe5e5e5, + b2_colorGreen = 0x00ff00, + b2_colorGreenYellow = 0xadff2f, + b2_colorHoneydew = 0xf0fff0, + b2_colorHotPink = 0xff69b4, + b2_colorIndianRed = 0xcd5c5c, + b2_colorIndigo = 0x4b0082, + b2_colorIvory = 0xfffff0, + b2_colorKhaki = 0xf0e68c, + b2_colorLavender = 0xe6e6fa, + b2_colorLavenderBlush = 0xfff0f5, + b2_colorLawnGreen = 0x7cfc00, + b2_colorLemonChiffon = 0xfffacd, + b2_colorLightBlue = 0xadd8e6, + b2_colorLightCoral = 0xf08080, + b2_colorLightCyan = 0xe0ffff, + b2_colorLightGoldenrod = 0xeedd82, + b2_colorLightGoldenrodYellow = 0xfafad2, + b2_colorLightGray = 0xd3d3d3, + b2_colorLightGreen = 0x90ee90, + b2_colorLightPink = 0xffb6c1, + b2_colorLightSalmon = 0xffa07a, + b2_colorLightSeaGreen = 0x20b2aa, + b2_colorLightSkyBlue = 0x87cefa, + b2_colorLightSlateBlue = 0x8470ff, + b2_colorLightSlateGray = 0x778899, + b2_colorLightSteelBlue = 0xb0c4de, + b2_colorLightYellow = 0xffffe0, + b2_colorLimeGreen = 0x32cd32, + b2_colorLinen = 0xfaf0e6, + b2_colorMagenta = 0xff00ff, + b2_colorMaroon = 0xb03060, + b2_colorMediumAquamarine = 0x66cdaa, + b2_colorMediumBlue = 0x0000cd, + b2_colorMediumOrchid = 0xba55d3, + b2_colorMediumPurple = 0x9370db, + b2_colorMediumSeaGreen = 0x3cb371, + b2_colorMediumSlateBlue = 0x7b68ee, + b2_colorMediumSpringGreen = 0x00fa9a, + b2_colorMediumTurquoise = 0x48d1cc, + b2_colorMediumVioletRed = 0xc71585, + b2_colorMidnightBlue = 0x191970, + b2_colorMintCream = 0xf5fffa, + b2_colorMistyRose = 0xffe4e1, + b2_colorMoccasin = 0xffe4b5, + b2_colorNavajoWhite = 0xffdead, + b2_colorNavyBlue = 0x000080, + b2_colorOldLace = 0xfdf5e6, + b2_colorOlive = 0x808000, + b2_colorOliveDrab = 0x6b8e23, + b2_colorOrange = 0xffa500, + b2_colorOrangeRed = 0xff4500, + b2_colorOrchid = 0xda70d6, + b2_colorPaleGoldenrod = 0xeee8aa, + b2_colorPaleGreen = 0x98fb98, + b2_colorPaleTurquoise = 0xafeeee, + b2_colorPaleVioletRed = 0xdb7093, + b2_colorPapayaWhip = 0xffefd5, + b2_colorPeachPuff = 0xffdab9, + b2_colorPeru = 0xcd853f, + b2_colorPink = 0xffc0cb, + b2_colorPlum = 0xdda0dd, + b2_colorPowderBlue = 0xb0e0e6, + b2_colorPurple = 0xa020f0, + b2_colorRebeccaPurple = 0x663399, + b2_colorRed = 0xff0000, + b2_colorRosyBrown = 0xbc8f8f, + b2_colorRoyalBlue = 0x4169e1, + b2_colorSaddleBrown = 0x8b4513, + b2_colorSalmon = 0xfa8072, + b2_colorSandyBrown = 0xf4a460, + b2_colorSeaGreen = 0x2e8b57, + b2_colorSeashell = 0xfff5ee, + b2_colorSienna = 0xa0522d, + b2_colorSilver = 0xc0c0c0, + b2_colorSkyBlue = 0x87ceeb, + b2_colorSlateBlue = 0x6a5acd, + b2_colorSlateGray = 0x708090, + b2_colorSnow = 0xfffafa, + b2_colorSpringGreen = 0x00ff7f, + b2_colorSteelBlue = 0x4682b4, + b2_colorTan = 0xd2b48c, + b2_colorTeal = 0x008080, + b2_colorThistle = 0xd8bfd8, + b2_colorTomato = 0xff6347, + b2_colorTurquoise = 0x40e0d0, + b2_colorViolet = 0xee82ee, + b2_colorVioletRed = 0xd02090, + b2_colorWheat = 0xf5deb3, + b2_colorWhite = 0xffffff, + b2_colorWhiteSmoke = 0xf5f5f5, + b2_colorYellow = 0xffff00, + b2_colorYellowGreen = 0x9acd32, + b2_colorBox2DRed = 0xdc3132, + b2_colorBox2DBlue = 0x30aebf, + b2_colorBox2DGreen = 0x8cc924, + b2_colorBox2DYellow = 0xffee8c +} b2HexColor; + +/// This struct holds callbacks you can implement to draw a Box2D world. +/// This structure should be zero initialized. +/// @ingroup world +typedef struct b2DebugDraw +{ + /// Draw a closed polygon provided in CCW order. + void ( *DrawPolygon )( const b2Vec2* vertices, int vertexCount, b2HexColor color, void* context ); + + /// Draw a solid closed polygon provided in CCW order. + void ( *DrawSolidPolygon )( b2Transform transform, const b2Vec2* vertices, int vertexCount, float radius, b2HexColor color, + void* context ); + + /// Draw a circle. + void ( *DrawCircle )( b2Vec2 center, float radius, b2HexColor color, void* context ); + + /// Draw a solid circle. + void ( *DrawSolidCircle )( b2Transform transform, float radius, b2HexColor color, void* context ); + + /// Draw a solid capsule. + void ( *DrawSolidCapsule )( b2Vec2 p1, b2Vec2 p2, float radius, b2HexColor color, void* context ); + + /// Draw a line segment. + void ( *DrawSegment )( b2Vec2 p1, b2Vec2 p2, b2HexColor color, void* context ); + + /// Draw a transform. Choose your own length scale. + void ( *DrawTransform )( b2Transform transform, void* context ); + + /// Draw a point. + void ( *DrawPoint )( b2Vec2 p, float size, b2HexColor color, void* context ); + + /// Draw a string. + void ( *DrawString )( b2Vec2 p, const char* s, void* context ); + + /// Bounds to use if restricting drawing to a rectangular region + b2AABB drawingBounds; + + /// Option to restrict drawing to a rectangular region. May suffer from unstable depth sorting. + bool useDrawingBounds; + + /// Option to draw shapes + bool drawShapes; + + /// Option to draw joints + bool drawJoints; + + /// Option to draw additional information for joints + bool drawJointExtras; + + /// Option to draw the bounding boxes for shapes + bool drawAABBs; + + /// Option to draw the mass and center of mass of dynamic bodies + bool drawMass; + + /// Option to draw contact points + bool drawContacts; + + /// Option to visualize the graph coloring used for contacts and joints + bool drawGraphColors; + + /// Option to draw contact normals + bool drawContactNormals; + + /// Option to draw contact normal impulses + bool drawContactImpulses; + + /// Option to draw contact friction impulses + bool drawFrictionImpulses; + + /// User context that is passed as an argument to drawing callback functions + void* context; +} b2DebugDraw; + +/// Use this to initialize your drawing interface. This allows you to implement a sub-set +/// of the drawing functions. +B2_API b2DebugDraw b2DefaultDebugDraw( void ); diff --git a/3rdparty/box2d/src/CMakeLists.txt b/3rdparty/box2d/src/CMakeLists.txt new file mode 100644 index 000000000000..695fff850172 --- /dev/null +++ b/3rdparty/box2d/src/CMakeLists.txt @@ -0,0 +1,205 @@ +set(BOX2D_SOURCE_FILES + aabb.c + aabb.h + array.c + array.h + bitset.c + bitset.h + body.c + body.h + broad_phase.c + broad_phase.h + constraint_graph.c + constraint_graph.h + contact.c + contact.h + contact_solver.c + contact_solver.h + core.c + core.h + ctz.h + distance.c + distance_joint.c + dynamic_tree.c + geometry.c + hull.c + id_pool.c + id_pool.h + island.c + island.h + joint.c + joint.h + manifold.c + math_functions.c + motor_joint.c + mouse_joint.c + prismatic_joint.c + revolute_joint.c + shape.c + shape.h + solver.c + solver.h + solver_set.c + solver_set.h + stack_allocator.c + stack_allocator.h + table.c + table.h + timer.c + types.c + weld_joint.c + wheel_joint.c + world.c + world.h +) + +set(BOX2D_API_FILES + ../include/box2d/base.h + ../include/box2d/box2d.h + ../include/box2d/collision.h + ../include/box2d/id.h + ../include/box2d/math_functions.h + ../include/box2d/types.h +) + +# Hide internal functions +# todo need to investigate this more +# https://gcc.gnu.org/wiki/Visibility +set(CMAKE_C_VISIBILITY_PRESET hidden) +set(CMAKE_VISIBILITY_INLINES_HIDDEN 1) + +add_library(box2d ${BOX2D_SOURCE_FILES} ${BOX2D_API_FILES}) + +# Generate box2d_export.h to handles shared library builds +# turned this off to make Box2D easier to use without cmake +# include(GenerateExportHeader) +# generate_export_header(box2d) + +target_include_directories(box2d + PUBLIC + $ + $ + $ + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} +) + +set(CMAKE_DEBUG_POSTFIX "d") + +# Box2D uses C17 +set_target_properties(box2d PROPERTIES + C_STANDARD 17 + C_STANDARD_REQUIRED YES + C_EXTENSIONS YES + VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR} + DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX} +) + +if (BOX2D_PROFILE) + target_compile_definitions(box2d PRIVATE BOX2D_PROFILE) + + FetchContent_Declare( + tracy + GIT_REPOSITORY https://github.com/wolfpld/tracy.git + GIT_TAG master + GIT_SHALLOW TRUE + GIT_PROGRESS TRUE + ) + FetchContent_MakeAvailable(tracy) + + target_link_libraries(box2d PUBLIC TracyClient) +endif() + +if (BOX2D_VALIDATE) + target_compile_definitions(box2d PRIVATE BOX2D_VALIDATE) +endif() + +if (BOX2D_ENABLE_SIMD) + target_compile_definitions(box2d PRIVATE BOX2D_ENABLE_SIMD) +endif() + +if (MSVC) + message(STATUS "Box2D on MSVC") + if (BUILD_SHARED_LIBS) + # this is needed by DLL users to import Box2D symbols + target_compile_definitions(box2d INTERFACE BOX2D_DLL) + endif() + + # Visual Studio won't load the natvis unless it is in the project + target_sources(box2d PRIVATE box2d.natvis) + + # Enable asserts in release with debug info + target_compile_definitions(box2d PUBLIC "$<$:B2_ENABLE_ASSERT>") + + # Atomics are still considered experimental in Visual Studio 17.8 + target_compile_options(box2d PRIVATE /experimental:c11atomics) + + if (BOX2D_AVX2) + message(STATUS "Box2D using AVX2") + target_compile_definitions(box2d PRIVATE BOX2D_AVX2) + target_compile_options(box2d PRIVATE /arch:AVX2) + endif() + +elseif (MINGW) + message(STATUS "Box2D on MinGW") + if (BOX2D_AVX2) + message(STATUS "Box2D using AVX2") + target_compile_definitions(box2d PRIVATE BOX2D_AVX2) + target_compile_options(box2d PRIVATE -mavx2) + else() + endif() +elseif (APPLE) + message(STATUS "Box2D on Apple") +elseif (EMSCRIPTEN) + message(STATUS "Box2D on Emscripten") + target_compile_options(box2d PRIVATE -msimd128 -msse2) +elseif (UNIX) + message(STATUS "Box2D using Unix") + if ("${CMAKE_HOST_SYSTEM_PROCESSOR}" STREQUAL "aarch64") + # raspberry pi + # -mfpu=neon + # target_compile_options(box2d PRIVATE) + else() + # x64 + if (BOX2D_AVX2) + message(STATUS "Box2D using AVX2") + # FMA -mfma -mavx -march=native + target_compile_definitions(box2d PRIVATE BOX2D_AVX2) + target_compile_options(box2d PRIVATE -mavx2) + else() + endif() + endif() +endif() + +source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" PREFIX "src" FILES ${BOX2D_SOURCE_FILES}) +source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/../include" PREFIX "include" FILES ${BOX2D_API_FILES}) + +add_library(box2d::box2d ALIAS box2d) + +install( + TARGETS box2d + EXPORT box2dConfig + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} +) + +install( + EXPORT box2dConfig + NAMESPACE box2d:: + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/box2d" +) + +include(CMakePackageConfigHelpers) + +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/box2dConfigVersion.cmake" + COMPATIBILITY SameMajorVersion +) + +install( + FILES "${CMAKE_CURRENT_BINARY_DIR}/box2dConfigVersion.cmake" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/box2d" +) + diff --git a/3rdparty/box2d/src/aabb.c b/3rdparty/box2d/src/aabb.c new file mode 100644 index 000000000000..2fabfa50d987 --- /dev/null +++ b/3rdparty/box2d/src/aabb.c @@ -0,0 +1,155 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#include "aabb.h" + +#include "box2d/math_functions.h" + +#include + +bool b2AABB_IsValid( b2AABB a ) +{ + b2Vec2 d = b2Sub( a.upperBound, a.lowerBound ); + bool valid = d.x >= 0.0f && d.y >= 0.0f; + valid = valid && b2Vec2_IsValid( a.lowerBound ) && b2Vec2_IsValid( a.upperBound ); + return valid; +} + +// From Real-time Collision Detection, p179. +b2CastOutput b2AABB_RayCast( b2AABB a, b2Vec2 p1, b2Vec2 p2 ) +{ + // Radius not handled + b2CastOutput output = { 0 }; + + float tmin = -FLT_MAX; + float tmax = FLT_MAX; + + b2Vec2 p = p1; + b2Vec2 d = b2Sub( p2, p1 ); + b2Vec2 absD = b2Abs( d ); + + b2Vec2 normal = b2Vec2_zero; + + // x-coordinate + if ( absD.x < FLT_EPSILON ) + { + // parallel + if ( p.x < a.lowerBound.x || a.upperBound.x < p.x ) + { + return output; + } + } + else + { + float inv_d = 1.0f / d.x; + float t1 = ( a.lowerBound.x - p.x ) * inv_d; + float t2 = ( a.upperBound.x - p.x ) * inv_d; + + // Sign of the normal vector. + float s = -1.0f; + + if ( t1 > t2 ) + { + float tmp = t1; + t1 = t2; + t2 = tmp; + s = 1.0f; + } + + // Push the min up + if ( t1 > tmin ) + { + normal.y = 0.0f; + normal.x = s; + tmin = t1; + } + + // Pull the max down + tmax = b2MinFloat( tmax, t2 ); + + if ( tmin > tmax ) + { + return output; + } + } + + // y-coordinate + if ( absD.y < FLT_EPSILON ) + { + // parallel + if ( p.y < a.lowerBound.y || a.upperBound.y < p.y ) + { + return output; + } + } + else + { + float inv_d = 1.0f / d.y; + float t1 = ( a.lowerBound.y - p.y ) * inv_d; + float t2 = ( a.upperBound.y - p.y ) * inv_d; + + // Sign of the normal vector. + float s = -1.0f; + + if ( t1 > t2 ) + { + float tmp = t1; + t1 = t2; + t2 = tmp; + s = 1.0f; + } + + // Push the min up + if ( t1 > tmin ) + { + normal.x = 0.0f; + normal.y = s; + tmin = t1; + } + + // Pull the max down + tmax = b2MinFloat( tmax, t2 ); + + if ( tmin > tmax ) + { + return output; + } + } + + // Does the ray start inside the box? + // Does the ray intersect beyond the max fraction? + if ( tmin < 0.0f || 1.0f < tmin ) + { + return output; + } + + // Intersection. + output.fraction = tmin; + output.normal = normal; + output.point = b2Lerp( p1, p2, tmin ); + output.hit = true; + return output; +} + +#if 0 +bool b2TestOverlap( const b2Shape* shapeA, int32_t indexA, + const b2Shape* shapeB, int32_t indexB, + b2Transform xfA, b2Transform xfB) +{ + b2DistanceInput input; + input->proxyA.Set(shapeA, indexA); + input->proxyB.Set(shapeB, indexB); + input->transformA = xfA; + input->transformB = xfB; + input->useRadii = true; + + b2DistanceCache cache; + cache.count = 0; + + b2DistanceOutput output; + + b2Distance(&output, &cache, &input); + + return output.distance < 10.0f * b2_epsilon; +} +#endif diff --git a/3rdparty/box2d/src/aabb.h b/3rdparty/box2d/src/aabb.h new file mode 100644 index 000000000000..6fa99c4eafad --- /dev/null +++ b/3rdparty/box2d/src/aabb.h @@ -0,0 +1,64 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include "box2d/types.h" + +// Ray cast an AABB +b2CastOutput b2AABB_RayCast( b2AABB a, b2Vec2 p1, b2Vec2 p2 ); + +// Get the perimeter length +static inline float b2Perimeter( b2AABB a ) +{ + float wx = a.upperBound.x - a.lowerBound.x; + float wy = a.upperBound.y - a.lowerBound.y; + return 2.0f * ( wx + wy ); +} + +/// Enlarge a to contain b +/// @return true if the AABB grew +static inline bool b2EnlargeAABB( b2AABB* a, b2AABB b ) +{ + bool changed = false; + if ( b.lowerBound.x < a->lowerBound.x ) + { + a->lowerBound.x = b.lowerBound.x; + changed = true; + } + + if ( b.lowerBound.y < a->lowerBound.y ) + { + a->lowerBound.y = b.lowerBound.y; + changed = true; + } + + if ( a->upperBound.x < b.upperBound.x ) + { + a->upperBound.x = b.upperBound.x; + changed = true; + } + + if ( a->upperBound.y < b.upperBound.y ) + { + a->upperBound.y = b.upperBound.y; + changed = true; + } + + return changed; +} + +/// Do a and b overlap +static inline bool b2AABB_Overlaps( b2AABB a, b2AABB b ) +{ + b2Vec2 d1 = { b.lowerBound.x - a.upperBound.x, b.lowerBound.y - a.upperBound.y }; + b2Vec2 d2 = { a.lowerBound.x - b.upperBound.x, a.lowerBound.y - b.upperBound.y }; + + if ( d1.x > 0.0f || d1.y > 0.0f ) + return false; + + if ( d2.x > 0.0f || d2.y > 0.0f ) + return false; + + return true; +} diff --git a/3rdparty/box2d/src/array.c b/3rdparty/box2d/src/array.c new file mode 100644 index 000000000000..7f3847cb2513 --- /dev/null +++ b/3rdparty/box2d/src/array.c @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#include "array.h" + +#include + +B2_ARRAY_SOURCE( int, b2Int ); diff --git a/3rdparty/box2d/src/array.h b/3rdparty/box2d/src/array.h new file mode 100644 index 000000000000..e590d292f291 --- /dev/null +++ b/3rdparty/box2d/src/array.h @@ -0,0 +1,174 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include "core.h" + +// Macro generated functions for dynamic arrays +// Pros +// - type safe +// - array data debuggable (visible count and capacity) +// - bounds checking +// - forward declaration +// - simple implementation +// - generates functions (like C++ templates) +// - functions have https://en.wikipedia.org/wiki/Sequence_point +// - avoids stretchy buffer dropped pointer update bugs +// Cons +// - cannot debug +// - breaks code navigation + +// Array declaration that doesn't need the type T to be defined +#define B2_ARRAY_DECLARE( T, PREFIX ) \ + typedef struct \ + { \ + struct T* data; \ + int count; \ + int capacity; \ + } PREFIX##Array; \ + PREFIX##Array PREFIX##Array_Create( int capacity ); \ + void PREFIX##Array_Reserve( PREFIX##Array* a, int newCapacity ); \ + void PREFIX##Array_Destroy( PREFIX##Array* a ); + +#define B2_DECLARE_ARRAY_NATIVE( T, PREFIX ) \ + typedef struct \ + { \ + T* data; \ + int count; \ + int capacity; \ + } PREFIX##Array; \ + /* Create array with initial capacity. Zero initialization is also supported */ \ + PREFIX##Array PREFIX##Array_Create( int capacity ); \ + void PREFIX##Array_Reserve( PREFIX##Array* a, int newCapacity ); \ + void PREFIX##Array_Destroy( PREFIX##Array* a ); + +// Inline array functions that need the type T to be defined +#define B2_ARRAY_INLINE( T, PREFIX ) \ + /* Resize */ \ + static inline void PREFIX##Array_Resize( PREFIX##Array* a, int count ) \ + { \ + PREFIX##Array_Reserve( a, count ); \ + a->count = count; \ + } \ + /* Get */ \ + static inline T* PREFIX##Array_Get( PREFIX##Array* a, int index ) \ + { \ + B2_ASSERT( 0 <= index && index < a->count ); \ + return a->data + index; \ + } \ + /* Add */ \ + static inline T* PREFIX##Array_Add( PREFIX##Array* a ) \ + { \ + if ( a->count == a->capacity ) \ + { \ + int newCapacity = a->capacity < 2 ? 2 : a->capacity + ( a->capacity >> 1 ); \ + PREFIX##Array_Reserve( a, newCapacity ); \ + } \ + a->count += 1; \ + return a->data + ( a->count - 1 ); \ + } \ + /* Push */ \ + static inline void PREFIX##Array_Push( PREFIX##Array* a, T value ) \ + { \ + if ( a->count == a->capacity ) \ + { \ + int newCapacity = a->capacity < 2 ? 2 : a->capacity + ( a->capacity >> 1 ); \ + PREFIX##Array_Reserve( a, newCapacity ); \ + } \ + a->data[a->count] = value; \ + a->count += 1; \ + } \ + /* Set */ \ + static inline void PREFIX##Array_Set( PREFIX##Array* a, int index, T value ) \ + { \ + B2_ASSERT( 0 <= index && index < a->count ); \ + a->data[index] = value; \ + } \ + /* RemoveSwap */ \ + static inline int PREFIX##Array_RemoveSwap( PREFIX##Array* a, int index ) \ + { \ + B2_ASSERT( 0 <= index && index < a->count ); \ + int movedIndex = B2_NULL_INDEX; \ + if ( index != a->count - 1 ) \ + { \ + movedIndex = a->count - 1; \ + a->data[index] = a->data[movedIndex]; \ + } \ + a->count -= 1; \ + return movedIndex; \ + } \ + /* Pop */ \ + static inline T PREFIX##Array_Pop( PREFIX##Array* a ) \ + { \ + B2_ASSERT( a->count > 0 ); \ + T value = a->data[a->count - 1]; \ + a->count -= 1; \ + return value; \ + } \ + /* Clear */ \ + static inline void PREFIX##Array_Clear( PREFIX##Array* a ) \ + { \ + a->count = 0; \ + } \ + /* ByteCount */ \ + static inline int PREFIX##Array_ByteCount( PREFIX##Array* a ) \ + { \ + return (int)( a->capacity * sizeof( T ) ); \ + } + +// Array implementations to be instantiated in a source file where the type T is known +#define B2_ARRAY_SOURCE( T, PREFIX ) \ + /* Create */ \ + PREFIX##Array PREFIX##Array_Create( int capacity ) \ + { \ + PREFIX##Array a = { 0 }; \ + if ( capacity > 0 ) \ + { \ + a.data = b2Alloc( capacity * sizeof( T ) ); \ + a.capacity = capacity; \ + } \ + return a; \ + } \ + /* Reserve */ \ + void PREFIX##Array_Reserve( PREFIX##Array* a, int newCapacity ) \ + { \ + if ( newCapacity <= a->capacity ) \ + { \ + return; \ + } \ + a->data = b2GrowAlloc( a->data, a->capacity * sizeof( T ), newCapacity * sizeof( T ) ); \ + a->capacity = newCapacity; \ + } \ + /* Destroy */ \ + void PREFIX##Array_Destroy( PREFIX##Array* a ) \ + { \ + b2Free( a->data, a->capacity * sizeof( T ) ); \ + a->data = NULL; \ + a->count = 0; \ + a->capacity = 0; \ + } + +B2_DECLARE_ARRAY_NATIVE( int, b2Int ); +B2_ARRAY_INLINE( int, b2Int ); + +// Declare all the arrays +B2_ARRAY_DECLARE( b2Body, b2Body ); +B2_ARRAY_DECLARE( b2BodyMoveEvent, b2BodyMoveEvent ); +B2_ARRAY_DECLARE( b2BodySim, b2BodySim ); +B2_ARRAY_DECLARE( b2BodyState, b2BodyState ); +B2_ARRAY_DECLARE( b2ChainShape, b2ChainShape ); +B2_ARRAY_DECLARE( b2Contact, b2Contact ); +B2_ARRAY_DECLARE( b2ContactBeginTouchEvent, b2ContactBeginTouchEvent ); +B2_ARRAY_DECLARE( b2ContactEndTouchEvent, b2ContactEndTouchEvent ); +B2_ARRAY_DECLARE( b2ContactHitEvent, b2ContactHitEvent ); +B2_ARRAY_DECLARE( b2ContactSim, b2ContactSim ); +B2_ARRAY_DECLARE( b2Island, b2Island ); +B2_ARRAY_DECLARE( b2IslandSim, b2IslandSim ); +B2_ARRAY_DECLARE( b2Joint, b2Joint ); +B2_ARRAY_DECLARE( b2JointSim, b2JointSim ); +B2_ARRAY_DECLARE( b2SensorBeginTouchEvent, b2SensorBeginTouchEvent ); +B2_ARRAY_DECLARE( b2SensorEndTouchEvent, b2SensorEndTouchEvent ); +B2_ARRAY_DECLARE( b2Shape, b2Shape ); +B2_ARRAY_DECLARE( b2SolverSet, b2SolverSet ); +B2_ARRAY_DECLARE( b2TaskContext, b2TaskContext ); diff --git a/3rdparty/box2d/src/bitset.c b/3rdparty/box2d/src/bitset.c new file mode 100644 index 000000000000..a30464c67037 --- /dev/null +++ b/3rdparty/box2d/src/bitset.c @@ -0,0 +1,67 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#include "bitset.h" + +#include + +b2BitSet b2CreateBitSet( uint32_t bitCapacity ) +{ + b2BitSet bitSet = { 0 }; + + bitSet.blockCapacity = ( bitCapacity + sizeof( uint64_t ) * 8 - 1 ) / ( sizeof( uint64_t ) * 8 ); + bitSet.blockCount = 0; + bitSet.bits = b2Alloc( bitSet.blockCapacity * sizeof( uint64_t ) ); + memset( bitSet.bits, 0, bitSet.blockCapacity * sizeof( uint64_t ) ); + return bitSet; +} + +void b2DestroyBitSet( b2BitSet* bitSet ) +{ + b2Free( bitSet->bits, bitSet->blockCapacity * sizeof( uint64_t ) ); + bitSet->blockCapacity = 0; + bitSet->blockCount = 0; + bitSet->bits = NULL; +} + +void b2SetBitCountAndClear( b2BitSet* bitSet, uint32_t bitCount ) +{ + uint32_t blockCount = ( bitCount + sizeof( uint64_t ) * 8 - 1 ) / ( sizeof( uint64_t ) * 8 ); + if ( bitSet->blockCapacity < blockCount ) + { + b2DestroyBitSet( bitSet ); + uint32_t newBitCapacity = bitCount + ( bitCount >> 1 ); + *bitSet = b2CreateBitSet( newBitCapacity ); + } + + bitSet->blockCount = blockCount; + memset( bitSet->bits, 0, bitSet->blockCount * sizeof( uint64_t ) ); +} + +void b2GrowBitSet( b2BitSet* bitSet, uint32_t blockCount ) +{ + B2_ASSERT( blockCount > bitSet->blockCount ); + if ( blockCount > bitSet->blockCapacity ) + { + uint32_t oldCapacity = bitSet->blockCapacity; + bitSet->blockCapacity = blockCount + blockCount / 2; + uint64_t* newBits = b2Alloc( bitSet->blockCapacity * sizeof( uint64_t ) ); + memset( newBits, 0, bitSet->blockCapacity * sizeof( uint64_t ) ); + B2_ASSERT( bitSet->bits != NULL ); + memcpy( newBits, bitSet->bits, oldCapacity * sizeof( uint64_t ) ); + b2Free( bitSet->bits, oldCapacity * sizeof( uint64_t ) ); + bitSet->bits = newBits; + } + + bitSet->blockCount = blockCount; +} + +void b2InPlaceUnion( b2BitSet* B2_RESTRICT setA, const b2BitSet* B2_RESTRICT setB ) +{ + B2_ASSERT( setA->blockCount == setB->blockCount ); + uint32_t blockCount = setA->blockCount; + for ( uint32_t i = 0; i < blockCount; ++i ) + { + setA->bits[i] |= setB->bits[i]; + } +} diff --git a/3rdparty/box2d/src/bitset.h b/3rdparty/box2d/src/bitset.h new file mode 100644 index 000000000000..848b486d3807 --- /dev/null +++ b/3rdparty/box2d/src/bitset.h @@ -0,0 +1,65 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include "core.h" + +#include +#include + +// Bit set provides fast operations on large arrays of bits. +typedef struct b2BitSet +{ + uint64_t* bits; + uint32_t blockCapacity; + uint32_t blockCount; +} b2BitSet; + +b2BitSet b2CreateBitSet( uint32_t bitCapacity ); +void b2DestroyBitSet( b2BitSet* bitSet ); +void b2SetBitCountAndClear( b2BitSet* bitSet, uint32_t bitCount ); +void b2InPlaceUnion( b2BitSet* setA, const b2BitSet* setB ); +void b2GrowBitSet( b2BitSet* bitSet, uint32_t blockCount ); + +static inline void b2SetBit( b2BitSet* bitSet, uint32_t bitIndex ) +{ + uint32_t blockIndex = bitIndex / 64; + B2_ASSERT( blockIndex < bitSet->blockCount ); + bitSet->bits[blockIndex] |= ( (uint64_t)1 << bitIndex % 64 ); +} + +static inline void b2SetBitGrow( b2BitSet* bitSet, uint32_t bitIndex ) +{ + uint32_t blockIndex = bitIndex / 64; + if ( blockIndex >= bitSet->blockCount ) + { + b2GrowBitSet( bitSet, blockIndex + 1 ); + } + bitSet->bits[blockIndex] |= ( (uint64_t)1 << bitIndex % 64 ); +} + +static inline void b2ClearBit( b2BitSet* bitSet, uint32_t bitIndex ) +{ + uint32_t blockIndex = bitIndex / 64; + if ( blockIndex >= bitSet->blockCount ) + { + return; + } + bitSet->bits[blockIndex] &= ~( (uint64_t)1 << bitIndex % 64 ); +} + +static inline bool b2GetBit( const b2BitSet* bitSet, uint32_t bitIndex ) +{ + uint32_t blockIndex = bitIndex / 64; + if ( blockIndex >= bitSet->blockCount ) + { + return false; + } + return ( bitSet->bits[blockIndex] & ( (uint64_t)1 << bitIndex % 64 ) ) != 0; +} + +static inline int b2GetBitSetBytes( b2BitSet* bitSet ) +{ + return bitSet->blockCapacity * sizeof( uint64_t ); +} diff --git a/3rdparty/box2d/src/body.c b/3rdparty/box2d/src/body.c new file mode 100644 index 000000000000..639914258a57 --- /dev/null +++ b/3rdparty/box2d/src/body.c @@ -0,0 +1,1761 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#include "body.h" + +#include "aabb.h" +#include "array.h" +#include "contact.h" +#include "core.h" +#include "id_pool.h" +#include "island.h" +#include "joint.h" +#include "shape.h" +#include "solver_set.h" +#include "world.h" + +// needed for dll export +#include "box2d/box2d.h" +#include "box2d/id.h" + +#include + +// Implement functions for b2BodyArray +B2_ARRAY_SOURCE( b2Body, b2Body ); +B2_ARRAY_SOURCE( b2BodySim, b2BodySim ); +B2_ARRAY_SOURCE( b2BodyState, b2BodyState ); + +// Get a validated body from a world using an id. +b2Body* b2GetBodyFullId( b2World* world, b2BodyId bodyId ) +{ + B2_ASSERT( b2Body_IsValid( bodyId ) ); + + // id index starts at one so that zero can represent null + return b2BodyArray_Get( &world->bodies, bodyId.index1 - 1 ); +} + +b2Transform b2GetBodyTransformQuick( b2World* world, b2Body* body ) +{ + b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, body->setIndex ); + b2BodySim* bodySim = b2BodySimArray_Get( &set->bodySims, body->localIndex ); + return bodySim->transform; +} + +b2Transform b2GetBodyTransform( b2World* world, int bodyId ) +{ + b2Body* body = b2BodyArray_Get( &world->bodies, bodyId ); + return b2GetBodyTransformQuick( world, body ); +} + +// Create a b2BodyId from a raw id. +b2BodyId b2MakeBodyId( b2World* world, int bodyId ) +{ + b2Body* body = b2BodyArray_Get( &world->bodies, bodyId ); + return ( b2BodyId ){ bodyId + 1, world->worldId, body->revision }; +} + +b2BodySim* b2GetBodySim( b2World* world, b2Body* body ) +{ + b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, body->setIndex); + b2BodySim* bodySim = b2BodySimArray_Get( &set->bodySims, body->localIndex ); + return bodySim; +} + +b2BodyState* b2GetBodyState( b2World* world, b2Body* body ) +{ + if ( body->setIndex == b2_awakeSet ) + { + b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet ); + return b2BodyStateArray_Get( &set->bodyStates, body->localIndex ); + } + + return NULL; +} + +static void b2CreateIslandForBody( b2World* world, int setIndex, b2Body* body ) +{ + B2_ASSERT( body->islandId == B2_NULL_INDEX ); + B2_ASSERT( body->islandPrev == B2_NULL_INDEX ); + B2_ASSERT( body->islandNext == B2_NULL_INDEX ); + B2_ASSERT( setIndex != b2_disabledSet ); + + b2Island* island = b2CreateIsland( world, setIndex ); + + body->islandId = island->islandId; + island->headBody = body->id; + island->tailBody = body->id; + island->bodyCount = 1; +} + +static void b2RemoveBodyFromIsland( b2World* world, b2Body* body ) +{ + if ( body->islandId == B2_NULL_INDEX ) + { + B2_ASSERT( body->islandPrev == B2_NULL_INDEX ); + B2_ASSERT( body->islandNext == B2_NULL_INDEX ); + return; + } + + int islandId = body->islandId; + b2Island* island = b2IslandArray_Get( &world->islands, islandId ); + + // Fix the island's linked list of sims + if ( body->islandPrev != B2_NULL_INDEX ) + { + b2Body* prevBody = b2BodyArray_Get( &world->bodies, body->islandPrev ); + prevBody->islandNext = body->islandNext; + } + + if ( body->islandNext != B2_NULL_INDEX ) + { + b2Body* nextBody = b2BodyArray_Get( &world->bodies, body->islandNext ); + nextBody->islandPrev = body->islandPrev; + } + + B2_ASSERT( island->bodyCount > 0 ); + island->bodyCount -= 1; + bool islandDestroyed = false; + + if ( island->headBody == body->id ) + { + island->headBody = body->islandNext; + + if ( island->headBody == B2_NULL_INDEX ) + { + // Destroy empty island + B2_ASSERT( island->tailBody == body->id ); + B2_ASSERT( island->bodyCount == 0 ); + B2_ASSERT( island->contactCount == 0 ); + B2_ASSERT( island->jointCount == 0 ); + + // Free the island + b2DestroyIsland( world, island->islandId ); + islandDestroyed = true; + } + } + else if ( island->tailBody == body->id ) + { + island->tailBody = body->islandPrev; + } + + if ( islandDestroyed == false ) + { + b2ValidateIsland( world, islandId ); + } + + body->islandId = B2_NULL_INDEX; + body->islandPrev = B2_NULL_INDEX; + body->islandNext = B2_NULL_INDEX; +} + +static void b2DestroyBodyContacts( b2World* world, b2Body* body, bool wakeBodies ) +{ + // Destroy the attached contacts + int edgeKey = body->headContactKey; + while ( edgeKey != B2_NULL_INDEX ) + { + int contactId = edgeKey >> 1; + int edgeIndex = edgeKey & 1; + + b2Contact* contact = b2ContactArray_Get( &world->contacts, contactId ); + edgeKey = contact->edges[edgeIndex].nextKey; + b2DestroyContact( world, contact, wakeBodies ); + } + + b2ValidateSolverSets( world ); +} + +b2BodyId b2CreateBody( b2WorldId worldId, const b2BodyDef* def ) +{ + b2CheckDef( def ); + B2_ASSERT( b2Vec2_IsValid( def->position ) ); + B2_ASSERT( b2Rot_IsValid( def->rotation ) ); + B2_ASSERT( b2Vec2_IsValid( def->linearVelocity ) ); + B2_ASSERT( b2IsValid( def->angularVelocity ) ); + B2_ASSERT( b2IsValid( def->linearDamping ) && def->linearDamping >= 0.0f ); + B2_ASSERT( b2IsValid( def->angularDamping ) && def->angularDamping >= 0.0f ); + B2_ASSERT( b2IsValid( def->sleepThreshold ) && def->sleepThreshold >= 0.0f ); + B2_ASSERT( b2IsValid( def->gravityScale ) ); + + b2World* world = b2GetWorldFromId( worldId ); + B2_ASSERT( world->locked == false ); + + if ( world->locked ) + { + return b2_nullBodyId; + } + + bool isAwake = ( def->isAwake || def->enableSleep == false ) && def->isEnabled; + + // determine the solver set + int setId; + if ( def->isEnabled == false ) + { + // any body type can be disabled + setId = b2_disabledSet; + } + else if ( def->type == b2_staticBody ) + { + setId = b2_staticSet; + } + else if ( isAwake == true ) + { + setId = b2_awakeSet; + } + else + { + // new set for a sleeping body in its own island + setId = b2AllocId( &world->solverSetIdPool ); + if ( setId == world->solverSets.count ) + { + // Create a zero initialized solver set. All sub-arrays are also zero initialized. + b2SolverSetArray_Push( &world->solverSets, ( b2SolverSet ){ 0 } ); + } + else + { + B2_ASSERT( world->solverSets.data[setId].setIndex == B2_NULL_INDEX ); + } + + world->solverSets.data[setId].setIndex = setId; + } + + B2_ASSERT( 0 <= setId && setId < world->solverSets.count ); + + int bodyId = b2AllocId( &world->bodyIdPool ); + + b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, setId ); + b2BodySim* bodySim = b2BodySimArray_Add( &set->bodySims ); + *bodySim = ( b2BodySim ){ 0 }; + bodySim->transform.p = def->position; + bodySim->transform.q = def->rotation; + bodySim->center = def->position; + bodySim->rotation0 = bodySim->transform.q; + bodySim->center0 = bodySim->center; + bodySim->localCenter = b2Vec2_zero; + bodySim->force = b2Vec2_zero; + bodySim->torque = 0.0f; + bodySim->mass = 0.0f; + bodySim->invMass = 0.0f; + bodySim->inertia = 0.0f; + bodySim->invInertia = 0.0f; + bodySim->minExtent = b2_huge; + bodySim->maxExtent = 0.0f; + bodySim->linearDamping = def->linearDamping; + bodySim->angularDamping = def->angularDamping; + bodySim->gravityScale = def->gravityScale; + bodySim->bodyId = bodyId; + bodySim->isBullet = def->isBullet; + bodySim->allowFastRotation = def->allowFastRotation; + bodySim->enlargeAABB = false; + bodySim->isFast = false; + bodySim->isSpeedCapped = false; + + if ( setId == b2_awakeSet ) + { + b2BodyState* bodyState = b2BodyStateArray_Add( &set->bodyStates ); + B2_ASSERT( ( (uintptr_t)bodyState & 0x1F ) == 0 ); + + *bodyState = ( b2BodyState ){ 0 }; + bodyState->linearVelocity = def->linearVelocity; + bodyState->angularVelocity = def->angularVelocity; + bodyState->deltaRotation = b2Rot_identity; + } + + if ( bodyId == world->bodies.count ) + { + b2BodyArray_Push( &world->bodies, ( b2Body ){ 0 } ); + } + else + { + B2_ASSERT( world->bodies.data[bodyId].id == B2_NULL_INDEX ); + } + + b2Body* body = b2BodyArray_Get( &world->bodies, bodyId ); + body->userData = def->userData; + body->setIndex = setId; + body->localIndex = set->bodySims.count - 1; + body->revision += 1; + body->headShapeId = B2_NULL_INDEX; + body->shapeCount = 0; + body->headChainId = B2_NULL_INDEX; + body->headContactKey = B2_NULL_INDEX; + body->contactCount = 0; + body->headJointKey = B2_NULL_INDEX; + body->jointCount = 0; + body->islandId = B2_NULL_INDEX; + body->islandPrev = B2_NULL_INDEX; + body->islandNext = B2_NULL_INDEX; + body->bodyMoveIndex = B2_NULL_INDEX; + body->id = bodyId; + body->sleepThreshold = def->sleepThreshold; + body->sleepTime = 0.0f; + body->type = def->type; + body->enableSleep = def->enableSleep; + body->fixedRotation = def->fixedRotation; + body->isSpeedCapped = false; + body->isMarked = false; + body->automaticMass = def->automaticMass; + + // dynamic and kinematic bodies that are enabled need a island + if ( setId >= b2_awakeSet ) + { + b2CreateIslandForBody( world, setId, body ); + } + + b2ValidateSolverSets( world ); + + b2BodyId id = { bodyId + 1, world->worldId, body->revision }; + return id; +} + +bool b2IsBodyAwake( b2World* world, b2Body* body ) +{ + return body->setIndex == b2_awakeSet; +} + +bool b2WakeBody( b2World* world, b2Body* body ) +{ + if ( body->setIndex >= b2_firstSleepingSet ) + { + b2WakeSolverSet( world, body->setIndex ); + return true; + } + + return false; +} + +void b2DestroyBody( b2BodyId bodyId ) +{ + b2World* world = b2GetWorldLocked( bodyId.world0 ); + if ( world == NULL ) + { + return; + } + + b2Body* body = b2GetBodyFullId( world, bodyId ); + + // Wake bodies attached to this body, even if this body is static. + bool wakeBodies = true; + + // Destroy the attached joints + int edgeKey = body->headJointKey; + while ( edgeKey != B2_NULL_INDEX ) + { + int jointId = edgeKey >> 1; + int edgeIndex = edgeKey & 1; + + b2Joint* joint = b2JointArray_Get( &world->joints, jointId ); + edgeKey = joint->edges[edgeIndex].nextKey; + + // Careful because this modifies the list being traversed + b2DestroyJointInternal( world, joint, wakeBodies ); + } + + // Destroy all contacts attached to this body. + b2DestroyBodyContacts( world, body, wakeBodies ); + + // Destroy the attached shapes and their broad-phase proxies. + int shapeId = body->headShapeId; + while ( shapeId != B2_NULL_INDEX ) + { + b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId ); + + b2DestroyShapeProxy( shape, &world->broadPhase ); + + // Return shape to free list. + b2FreeId( &world->shapeIdPool, shapeId ); + shape->id = B2_NULL_INDEX; + + shapeId = shape->nextShapeId; + } + + // Destroy the attached chains. The associated shapes have already been destroyed above. + int chainId = body->headChainId; + while ( chainId != B2_NULL_INDEX ) + { + b2ChainShape* chain = b2ChainShapeArray_Get( &world->chainShapes, chainId ); + + b2Free( chain->shapeIndices, chain->count * sizeof( int ) ); + chain->shapeIndices = NULL; + + // Return chain to free list. + b2FreeId( &world->chainIdPool, chainId ); + chain->id = B2_NULL_INDEX; + + chainId = chain->nextChainId; + } + + b2RemoveBodyFromIsland( world, body ); + + // Remove body sim from solver set that owns it + b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, body->setIndex ); + int movedIndex = b2BodySimArray_RemoveSwap( &set->bodySims, body->localIndex ); + if ( movedIndex != B2_NULL_INDEX ) + { + // Fix moved body index + b2BodySim* movedSim = set->bodySims.data + body->localIndex; + int movedId = movedSim->bodyId; + b2Body* movedBody = b2BodyArray_Get( &world->bodies, movedId ); + B2_ASSERT( movedBody->localIndex == movedIndex ); + movedBody->localIndex = body->localIndex; + } + + // Remove body state from awake set + if ( body->setIndex == b2_awakeSet ) + { + int result = b2BodyStateArray_RemoveSwap( &set->bodyStates, body->localIndex ); + B2_MAYBE_UNUSED( result ); + B2_ASSERT( result == movedIndex ); + } + + // Free body and id (preserve body revision) + b2FreeId( &world->bodyIdPool, body->id ); + + body->setIndex = B2_NULL_INDEX; + body->localIndex = B2_NULL_INDEX; + body->id = B2_NULL_INDEX; + + b2ValidateSolverSets( world ); +} + +int b2Body_GetContactCapacity( b2BodyId bodyId ) +{ + b2World* world = b2GetWorldLocked( bodyId.world0 ); + if ( world == NULL ) + { + return 0; + } + + b2Body* body = b2GetBodyFullId( world, bodyId ); + + // Conservative and fast + return body->contactCount; +} + +// todo what about sensors? +// todo sample needed +int b2Body_GetContactData( b2BodyId bodyId, b2ContactData* contactData, int capacity ) +{ + b2World* world = b2GetWorldLocked( bodyId.world0 ); + if ( world == NULL ) + { + return 0; + } + + b2Body* body = b2GetBodyFullId( world, bodyId ); + + int contactKey = body->headContactKey; + int index = 0; + while ( contactKey != B2_NULL_INDEX && index < capacity ) + { + int contactId = contactKey >> 1; + int edgeIndex = contactKey & 1; + + b2Contact* contact = b2ContactArray_Get( &world->contacts, contactId ); + + // Is contact touching? + if ( contact->flags & b2_contactTouchingFlag ) + { + b2Shape* shapeA = b2ShapeArray_Get( &world->shapes, contact->shapeIdA ); + b2Shape* shapeB = b2ShapeArray_Get( &world->shapes, contact->shapeIdB ); + + contactData[index].shapeIdA = ( b2ShapeId ){ shapeA->id + 1, bodyId.world0, shapeA->revision }; + contactData[index].shapeIdB = ( b2ShapeId ){ shapeB->id + 1, bodyId.world0, shapeB->revision }; + + b2ContactSim* contactSim = b2GetContactSim( world, contact ); + contactData[index].manifold = contactSim->manifold; + + index += 1; + } + + contactKey = contact->edges[edgeIndex].nextKey; + } + + B2_ASSERT( index <= capacity ); + + return index; +} + +b2AABB b2Body_ComputeAABB( b2BodyId bodyId ) +{ + b2World* world = b2GetWorldLocked( bodyId.world0 ); + if ( world == NULL ) + { + return ( b2AABB ){ 0 }; + } + + b2Body* body = b2GetBodyFullId( world, bodyId ); + if ( body->headShapeId == B2_NULL_INDEX ) + { + b2Transform transform = b2GetBodyTransform( world, body->id ); + return ( b2AABB ){ transform.p, transform.p }; + } + + b2Shape* shape = b2ShapeArray_Get( &world->shapes, body->headShapeId ); + b2AABB aabb = shape->aabb; + while ( shape->nextShapeId != B2_NULL_INDEX ) + { + shape = b2ShapeArray_Get( &world->shapes, shape->nextShapeId ); + aabb = b2AABB_Union( aabb, shape->aabb ); + } + + return aabb; +} + +void b2UpdateBodyMassData( b2World* world, b2Body* body ) +{ + b2BodySim* bodySim = b2GetBodySim( world, body ); + + // Compute mass data from shapes. Each shape has its own density. + bodySim->mass = 0.0f; + bodySim->invMass = 0.0f; + bodySim->inertia = 0.0f; + bodySim->invInertia = 0.0f; + bodySim->localCenter = b2Vec2_zero; + bodySim->minExtent = b2_huge; + bodySim->maxExtent = 0.0f; + + // Static and kinematic sims have zero mass. + if ( body->type != b2_dynamicBody ) + { + bodySim->center = bodySim->transform.p; + + // Need extents for kinematic bodies for sleeping to work correctly. + if ( body->type == b2_kinematicBody ) + { + int shapeId = body->headShapeId; + while ( shapeId != B2_NULL_INDEX ) + { + const b2Shape* s = b2ShapeArray_Get( &world->shapes, shapeId ); + + b2ShapeExtent extent = b2ComputeShapeExtent( s, b2Vec2_zero ); + bodySim->minExtent = b2MinFloat( bodySim->minExtent, extent.minExtent ); + bodySim->maxExtent = b2MaxFloat( bodySim->maxExtent, extent.maxExtent ); + + shapeId = s->nextShapeId; + } + } + + return; + } + + // Accumulate mass over all shapes. + b2Vec2 localCenter = b2Vec2_zero; + int shapeId = body->headShapeId; + while ( shapeId != B2_NULL_INDEX ) + { + const b2Shape* s = b2ShapeArray_Get( &world->shapes, shapeId ); + shapeId = s->nextShapeId; + + if ( s->density == 0.0f ) + { + continue; + } + + b2MassData massData = b2ComputeShapeMass( s ); + bodySim->mass += massData.mass; + localCenter = b2MulAdd( localCenter, massData.mass, massData.center ); + bodySim->inertia += massData.rotationalInertia; + } + + // Compute center of mass. + if ( bodySim->mass > 0.0f ) + { + bodySim->invMass = 1.0f / bodySim->mass; + localCenter = b2MulSV( bodySim->invMass, localCenter ); + } + + if ( bodySim->inertia > 0.0f && body->fixedRotation == false ) + { + // Center the inertia about the center of mass. + bodySim->inertia -= bodySim->mass * b2Dot( localCenter, localCenter ); + B2_ASSERT( bodySim->inertia > 0.0f ); + bodySim->invInertia = 1.0f / bodySim->inertia; + } + else + { + bodySim->inertia = 0.0f; + bodySim->invInertia = 0.0f; + } + + // Move center of mass. + b2Vec2 oldCenter = bodySim->center; + bodySim->localCenter = localCenter; + bodySim->center = b2TransformPoint( bodySim->transform, bodySim->localCenter ); + + // Update center of mass velocity + b2BodyState* state = b2GetBodyState( world, body ); + if ( state != NULL ) + { + b2Vec2 deltaLinear = b2CrossSV( state->angularVelocity, b2Sub( bodySim->center, oldCenter ) ); + state->linearVelocity = b2Add( state->linearVelocity, deltaLinear ); + } + + // Compute body extents relative to center of mass + shapeId = body->headShapeId; + while ( shapeId != B2_NULL_INDEX ) + { + const b2Shape* s = b2ShapeArray_Get( &world->shapes, shapeId ); + + b2ShapeExtent extent = b2ComputeShapeExtent( s, localCenter ); + bodySim->minExtent = b2MinFloat( bodySim->minExtent, extent.minExtent ); + bodySim->maxExtent = b2MaxFloat( bodySim->maxExtent, extent.maxExtent ); + + shapeId = s->nextShapeId; + } +} + +b2Vec2 b2Body_GetPosition( b2BodyId bodyId ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + b2Transform transform = b2GetBodyTransformQuick( world, body ); + return transform.p; +} + +b2Rot b2Body_GetRotation( b2BodyId bodyId ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + b2Transform transform = b2GetBodyTransformQuick( world, body ); + return transform.q; +} + +b2Transform b2Body_GetTransform( b2BodyId bodyId ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + return b2GetBodyTransformQuick( world, body ); +} + +b2Vec2 b2Body_GetLocalPoint( b2BodyId bodyId, b2Vec2 worldPoint ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + b2Transform transform = b2GetBodyTransformQuick( world, body ); + return b2InvTransformPoint( transform, worldPoint ); +} + +b2Vec2 b2Body_GetWorldPoint( b2BodyId bodyId, b2Vec2 localPoint ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + b2Transform transform = b2GetBodyTransformQuick( world, body ); + return b2TransformPoint( transform, localPoint ); +} + +b2Vec2 b2Body_GetLocalVector( b2BodyId bodyId, b2Vec2 worldVector ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + b2Transform transform = b2GetBodyTransformQuick( world, body ); + return b2InvRotateVector( transform.q, worldVector ); +} + +b2Vec2 b2Body_GetWorldVector( b2BodyId bodyId, b2Vec2 localVector ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + b2Transform transform = b2GetBodyTransformQuick( world, body ); + return b2RotateVector( transform.q, localVector ); +} + +void b2Body_SetTransform( b2BodyId bodyId, b2Vec2 position, b2Rot rotation ) +{ + B2_ASSERT( b2Vec2_IsValid( position ) ); + B2_ASSERT( b2Rot_IsValid( rotation ) ); + B2_ASSERT( b2Body_IsValid( bodyId ) ); + b2World* world = b2GetWorld( bodyId.world0 ); + B2_ASSERT( world->locked == false ); + + b2Body* body = b2GetBodyFullId( world, bodyId ); + b2BodySim* bodySim = b2GetBodySim( world, body ); + + bodySim->transform.p = position; + bodySim->transform.q = rotation; + bodySim->center = b2TransformPoint( bodySim->transform, bodySim->localCenter ); + + bodySim->rotation0 = bodySim->transform.q; + bodySim->center0 = bodySim->center; + + b2BroadPhase* broadPhase = &world->broadPhase; + + b2Transform transform = bodySim->transform; + const float margin = b2_aabbMargin; + const float speculativeDistance = b2_speculativeDistance; + + int shapeId = body->headShapeId; + while ( shapeId != B2_NULL_INDEX ) + { + b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId ); + b2AABB aabb = b2ComputeShapeAABB( shape, transform ); + aabb.lowerBound.x -= speculativeDistance; + aabb.lowerBound.y -= speculativeDistance; + aabb.upperBound.x += speculativeDistance; + aabb.upperBound.y += speculativeDistance; + shape->aabb = aabb; + + if ( b2AABB_Contains( shape->fatAABB, aabb ) == false ) + { + b2AABB fatAABB; + fatAABB.lowerBound.x = aabb.lowerBound.x - margin; + fatAABB.lowerBound.y = aabb.lowerBound.y - margin; + fatAABB.upperBound.x = aabb.upperBound.x + margin; + fatAABB.upperBound.y = aabb.upperBound.y + margin; + shape->fatAABB = fatAABB; + + // They body could be disabled + if ( shape->proxyKey != B2_NULL_INDEX ) + { + b2BroadPhase_MoveProxy( broadPhase, shape->proxyKey, fatAABB ); + } + } + + shapeId = shape->nextShapeId; + } +} + +b2Vec2 b2Body_GetLinearVelocity( b2BodyId bodyId ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + b2BodyState* state = b2GetBodyState( world, body ); + if ( state != NULL ) + { + return state->linearVelocity; + } + return b2Vec2_zero; +} + +float b2Body_GetAngularVelocity( b2BodyId bodyId ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + b2BodyState* state = b2GetBodyState( world, body ); + if ( state != NULL ) + { + return state->angularVelocity; + } + return 0.0; +} + +void b2Body_SetLinearVelocity( b2BodyId bodyId, b2Vec2 linearVelocity ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + + if ( body->type == b2_staticBody ) + { + return; + } + + if ( b2LengthSquared( linearVelocity ) > 0.0f ) + { + b2WakeBody( world, body ); + } + + b2BodyState* state = b2GetBodyState( world, body ); + if ( state == NULL ) + { + return; + } + + state->linearVelocity = linearVelocity; +} + +void b2Body_SetAngularVelocity( b2BodyId bodyId, float angularVelocity ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + + if (body->type == b2_staticBody || body->fixedRotation) + { + return; + } + + if ( angularVelocity != 0.0f ) + { + b2WakeBody( world, body ); + } + + b2BodyState* state = b2GetBodyState( world, body ); + if ( state == NULL ) + { + return; + } + + state->angularVelocity = angularVelocity; +} + +void b2Body_ApplyForce( b2BodyId bodyId, b2Vec2 force, b2Vec2 point, bool wake ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + + if ( wake && body->setIndex >= b2_firstSleepingSet ) + { + b2WakeBody( world, body ); + } + + if ( body->setIndex == b2_awakeSet ) + { + b2BodySim* bodySim = b2GetBodySim( world, body ); + bodySim->force = b2Add( bodySim->force, force ); + bodySim->torque += b2Cross( b2Sub( point, bodySim->center ), force ); + } +} + +void b2Body_ApplyForceToCenter( b2BodyId bodyId, b2Vec2 force, bool wake ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + + if ( wake && body->setIndex >= b2_firstSleepingSet ) + { + b2WakeBody( world, body ); + } + + if ( body->setIndex == b2_awakeSet ) + { + b2BodySim* bodySim = b2GetBodySim( world, body ); + bodySim->force = b2Add( bodySim->force, force ); + } +} + +void b2Body_ApplyTorque( b2BodyId bodyId, float torque, bool wake ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + + if ( wake && body->setIndex >= b2_firstSleepingSet ) + { + b2WakeBody( world, body ); + } + + if ( body->setIndex == b2_awakeSet ) + { + b2BodySim* bodySim = b2GetBodySim( world, body ); + bodySim->torque += torque; + } +} + +void b2Body_ApplyLinearImpulse( b2BodyId bodyId, b2Vec2 impulse, b2Vec2 point, bool wake ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + + if ( wake && body->setIndex >= b2_firstSleepingSet ) + { + b2WakeBody( world, body ); + } + + if ( body->setIndex == b2_awakeSet ) + { + int localIndex = body->localIndex; + b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet ); + b2BodyState* state = b2BodyStateArray_Get( &set->bodyStates, localIndex ); + b2BodySim* bodySim = b2BodySimArray_Get( &set->bodySims, localIndex ); + state->linearVelocity = b2MulAdd( state->linearVelocity, bodySim->invMass, impulse ); + state->angularVelocity += bodySim->invInertia * b2Cross( b2Sub( point, bodySim->center ), impulse ); + } +} + +void b2Body_ApplyLinearImpulseToCenter( b2BodyId bodyId, b2Vec2 impulse, bool wake ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + + if ( wake && body->setIndex >= b2_firstSleepingSet ) + { + b2WakeBody( world, body ); + } + + if ( body->setIndex == b2_awakeSet ) + { + int localIndex = body->localIndex; + b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet ); + b2BodyState* state = b2BodyStateArray_Get( &set->bodyStates, localIndex ); + b2BodySim* bodySim = b2BodySimArray_Get( &set->bodySims, localIndex ); + state->linearVelocity = b2MulAdd( state->linearVelocity, bodySim->invMass, impulse ); + } +} + +void b2Body_ApplyAngularImpulse( b2BodyId bodyId, float impulse, bool wake ) +{ + B2_ASSERT( b2Body_IsValid( bodyId ) ); + b2World* world = b2GetWorld( bodyId.world0 ); + + int id = bodyId.index1 - 1; + b2Body* body = b2BodyArray_Get( &world->bodies, id ); + B2_ASSERT( body->revision == bodyId.revision ); + + if ( wake && body->setIndex >= b2_firstSleepingSet ) + { + // this will not invalidate body pointer + b2WakeBody( world, body ); + } + + if ( body->setIndex == b2_awakeSet ) + { + int localIndex = body->localIndex; + b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet ); + b2BodyState* state = b2BodyStateArray_Get( &set->bodyStates, localIndex ); + b2BodySim* bodySim = b2BodySimArray_Get( &set->bodySims, localIndex ); + state->angularVelocity += bodySim->invInertia * impulse; + } +} + +b2BodyType b2Body_GetType( b2BodyId bodyId ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + return body->type; +} + +// Changing the body type is quite complex mainly due to joints. +// Considerations: +// - body and joints must be moved to the correct set +// - islands must be updated +// - graph coloring must be correct +// - any body connected to a joint may be disabled +// - joints between static bodies must go into the static set +void b2Body_SetType( b2BodyId bodyId, b2BodyType type ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + + b2BodyType originalType = body->type; + if ( originalType == type ) + { + return; + } + + if ( body->setIndex == b2_disabledSet ) + { + // Disabled bodies don't change solver sets or islands when they change type. + body->type = type; + + // Body type affects the mass + b2UpdateBodyMassData( world, body ); + return; + } + + // Destroy all contacts but don't wake bodies. + bool wakeBodies = false; + b2DestroyBodyContacts( world, body, wakeBodies ); + + // Wake this body because we assume below that it is awake or static. + b2WakeBody( world, body ); + + // Unlink all joints and wake attached bodies. + { + int jointKey = body->headJointKey; + while ( jointKey != B2_NULL_INDEX ) + { + int jointId = jointKey >> 1; + int edgeIndex = jointKey & 1; + + b2Joint* joint = b2JointArray_Get( &world->joints, jointId ); + if ( joint->islandId != B2_NULL_INDEX ) + { + b2UnlinkJoint( world, joint ); + } + + // A body going from static to dynamic or kinematic goes to the awake set + // and other attached bodies must be awake as well. For consistency, this is + // done for all cases. + b2Body* bodyA = b2BodyArray_Get( &world->bodies, joint->edges[0].bodyId ); + b2Body* bodyB = b2BodyArray_Get( &world->bodies, joint->edges[1].bodyId ); + b2WakeBody( world, bodyA ); + b2WakeBody( world, bodyB ); + + jointKey = joint->edges[edgeIndex].nextKey; + } + } + + body->type = type; + + if ( originalType == b2_staticBody ) + { + // Body is going from static to dynamic or kinematic. It only makes sense to move it to the awake set. + B2_ASSERT( body->setIndex == b2_staticSet ); + + b2SolverSet* staticSet = b2SolverSetArray_Get( &world->solverSets, b2_staticSet ); + b2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet ); + + // Transfer body to awake set + b2TransferBody( world, awakeSet, staticSet, body ); + + // Create island for body + b2CreateIslandForBody( world, b2_awakeSet, body ); + + // Transfer static joints to awake set + int jointKey = body->headJointKey; + while ( jointKey != B2_NULL_INDEX ) + { + int jointId = jointKey >> 1; + int edgeIndex = jointKey & 1; + + b2Joint* joint = b2JointArray_Get( &world->joints, jointId ); + + // Transfer the joint if it is in the static set + if ( joint->setIndex == b2_staticSet ) + { + b2TransferJoint( world, awakeSet, staticSet, joint ); + } + else if ( joint->setIndex == b2_awakeSet ) + { + // In this case the joint must be re-inserted into the constraint graph to ensure the correct + // graph color. + + // First transfer to the static set. + b2TransferJoint( world, staticSet, awakeSet, joint ); + + // Now transfer it back to the awake set and into the graph coloring. + b2TransferJoint( world, awakeSet, staticSet, joint ); + } + else + { + // Otherwise the joint must be disabled. + B2_ASSERT( joint->setIndex == b2_disabledSet ); + } + + jointKey = joint->edges[edgeIndex].nextKey; + } + + // Recreate shape proxies in movable tree. + b2Transform transform = b2GetBodyTransformQuick( world, body ); + int shapeId = body->headShapeId; + while ( shapeId != B2_NULL_INDEX ) + { + b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId ); + shapeId = shape->nextShapeId; + b2DestroyShapeProxy( shape, &world->broadPhase ); + bool forcePairCreation = true; + b2BodyType proxyType = type; + b2CreateShapeProxy( shape, &world->broadPhase, proxyType, transform, forcePairCreation ); + } + } + else if ( type == b2_staticBody ) + { + // The body is going from dynamic/kinematic to static. It should be awake. + B2_ASSERT( body->setIndex == b2_awakeSet ); + + b2SolverSet* staticSet = b2SolverSetArray_Get( &world->solverSets, b2_staticSet ); + b2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet ); + + // Transfer body to static set + b2TransferBody( world, staticSet, awakeSet, body ); + + // Remove body from island. + b2RemoveBodyFromIsland( world, body ); + + // Maybe transfer joints to static set. + int jointKey = body->headJointKey; + while ( jointKey != B2_NULL_INDEX ) + { + int jointId = jointKey >> 1; + int edgeIndex = jointKey & 1; + + b2Joint* joint = b2JointArray_Get( &world->joints, jointId ); + jointKey = joint->edges[edgeIndex].nextKey; + + int otherEdgeIndex = edgeIndex ^ 1; + b2Body* otherBody = b2BodyArray_Get( &world->bodies, joint->edges[otherEdgeIndex].bodyId ); + + // Skip disabled joint + if ( joint->setIndex == b2_disabledSet ) + { + // Joint is disable, should be connected to a disabled body + B2_ASSERT( otherBody->setIndex == b2_disabledSet ); + continue; + } + + // Since the body was not static, the joint must be awake. + B2_ASSERT( joint->setIndex == b2_awakeSet ); + + // Only transfer joint to static set if both bodies are static. + if ( otherBody->setIndex == b2_staticSet ) + { + b2TransferJoint( world, staticSet, awakeSet, joint ); + } + else + { + // The other body must be awake. + B2_ASSERT( otherBody->setIndex == b2_awakeSet ); + + // The joint must live in a graph color. + B2_ASSERT( 0 <= joint->colorIndex && joint->colorIndex < b2_graphColorCount ); + + // In this case the joint must be re-inserted into the constraint graph to ensure the correct + // graph color. + + // First transfer to the static set. + b2TransferJoint( world, staticSet, awakeSet, joint ); + + // Now transfer it back to the awake set and into the graph coloring. + b2TransferJoint( world, awakeSet, staticSet, joint ); + } + } + + // Recreate shape proxies in static tree. + b2Transform transform = b2GetBodyTransformQuick( world, body ); + int shapeId = body->headShapeId; + while ( shapeId != B2_NULL_INDEX ) + { + b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId ); + shapeId = shape->nextShapeId; + b2DestroyShapeProxy( shape, &world->broadPhase ); + bool forcePairCreation = true; + b2CreateShapeProxy( shape, &world->broadPhase, b2_staticBody, transform, forcePairCreation ); + } + } + else + { + B2_ASSERT( originalType == b2_dynamicBody || originalType == b2_kinematicBody ); + B2_ASSERT( type == b2_dynamicBody || type == b2_kinematicBody ); + + // Recreate shape proxies in static tree. + b2Transform transform = b2GetBodyTransformQuick( world, body ); + int shapeId = body->headShapeId; + while ( shapeId != B2_NULL_INDEX ) + { + b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId ); + shapeId = shape->nextShapeId; + b2DestroyShapeProxy( shape, &world->broadPhase ); + b2BodyType proxyType = type; + bool forcePairCreation = true; + b2CreateShapeProxy( shape, &world->broadPhase, proxyType, transform, forcePairCreation ); + } + } + + // Relink all joints + { + int jointKey = body->headJointKey; + while ( jointKey != B2_NULL_INDEX ) + { + int jointId = jointKey >> 1; + int edgeIndex = jointKey & 1; + + b2Joint* joint = b2JointArray_Get( &world->joints, jointId ); + jointKey = joint->edges[edgeIndex].nextKey; + + int otherEdgeIndex = edgeIndex ^ 1; + int otherBodyId = joint->edges[otherEdgeIndex].bodyId; + b2Body* otherBody = b2BodyArray_Get( &world->bodies, otherBodyId ); + + if ( otherBody->setIndex == b2_disabledSet ) + { + continue; + } + + if ( body->type == b2_staticBody && otherBody->type == b2_staticBody ) + { + continue; + } + + b2LinkJoint( world, joint, false ); + } + + b2MergeAwakeIslands( world ); + } + + // Body type affects the mass + b2UpdateBodyMassData( world, body ); + + b2ValidateSolverSets( world ); +} + +void b2Body_SetUserData( b2BodyId bodyId, void* userData ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + body->userData = userData; +} + +void* b2Body_GetUserData( b2BodyId bodyId ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + return body->userData; +} + +float b2Body_GetMass( b2BodyId bodyId ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + b2BodySim* bodySim = b2GetBodySim( world, body ); + return bodySim->mass; +} + +float b2Body_GetRotationalInertia( b2BodyId bodyId ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + b2BodySim* bodySim = b2GetBodySim( world, body ); + return bodySim->inertia; +} + +b2Vec2 b2Body_GetLocalCenterOfMass( b2BodyId bodyId ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + b2BodySim* bodySim = b2GetBodySim( world, body ); + return bodySim->localCenter; +} + +b2Vec2 b2Body_GetWorldCenterOfMass( b2BodyId bodyId ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + b2BodySim* bodySim = b2GetBodySim( world, body ); + return bodySim->center; +} + +void b2Body_SetMassData( b2BodyId bodyId, b2MassData massData ) +{ + B2_ASSERT( b2IsValid( massData.mass ) && massData.mass >= 0.0f ); + B2_ASSERT( b2IsValid( massData.rotationalInertia ) && massData.rotationalInertia >= 0.0f ); + B2_ASSERT( b2Vec2_IsValid( massData.center ) ); + + b2World* world = b2GetWorldLocked( bodyId.world0 ); + if ( world == NULL ) + { + return; + } + + b2Body* body = b2GetBodyFullId( world, bodyId ); + b2BodySim* bodySim = b2GetBodySim( world, body ); + + bodySim->mass = massData.mass; + bodySim->inertia = massData.rotationalInertia; + bodySim->localCenter = massData.center; + + b2Vec2 center = b2TransformPoint( bodySim->transform, massData.center ); + bodySim->center = center; + bodySim->center0 = center; + + bodySim->invMass = bodySim->mass > 0.0f ? 1.0f / bodySim->mass : 0.0f; + bodySim->invInertia = bodySim->inertia > 0.0f ? 1.0f / bodySim->inertia : 0.0f; +} + +b2MassData b2Body_GetMassData( b2BodyId bodyId ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + b2BodySim* bodySim = b2GetBodySim( world, body ); + b2MassData massData = { bodySim->mass, bodySim->localCenter, bodySim->inertia }; + return massData; +} + +void b2Body_ApplyMassFromShapes( b2BodyId bodyId ) +{ + b2World* world = b2GetWorldLocked( bodyId.world0 ); + if ( world == NULL ) + { + return; + } + + b2Body* body = b2GetBodyFullId( world, bodyId ); + b2UpdateBodyMassData( world, body ); +} + +void b2Body_SetAutomaticMass( b2BodyId bodyId, bool automaticMass ) +{ + b2World* world = b2GetWorldLocked( bodyId.world0 ); + if ( world == NULL ) + { + return; + } + + b2Body* body = b2GetBodyFullId( world, bodyId ); + body->automaticMass = automaticMass; +} + +bool b2Body_GetAutomaticMass( b2BodyId bodyId ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + return body->automaticMass; +} + +void b2Body_SetLinearDamping( b2BodyId bodyId, float linearDamping ) +{ + B2_ASSERT( b2IsValid( linearDamping ) && linearDamping >= 0.0f ); + + b2World* world = b2GetWorldLocked( bodyId.world0 ); + if ( world == NULL ) + { + return; + } + + b2Body* body = b2GetBodyFullId( world, bodyId ); + b2BodySim* bodySim = b2GetBodySim( world, body ); + bodySim->linearDamping = linearDamping; +} + +float b2Body_GetLinearDamping( b2BodyId bodyId ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + b2BodySim* bodySim = b2GetBodySim( world, body ); + return bodySim->linearDamping; +} + +void b2Body_SetAngularDamping( b2BodyId bodyId, float angularDamping ) +{ + B2_ASSERT( b2IsValid( angularDamping ) && angularDamping >= 0.0f ); + + b2World* world = b2GetWorldLocked( bodyId.world0 ); + if ( world == NULL ) + { + return; + } + + b2Body* body = b2GetBodyFullId( world, bodyId ); + b2BodySim* bodySim = b2GetBodySim( world, body ); + bodySim->angularDamping = angularDamping; +} + +float b2Body_GetAngularDamping( b2BodyId bodyId ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + b2BodySim* bodySim = b2GetBodySim( world, body ); + return bodySim->angularDamping; +} + +void b2Body_SetGravityScale( b2BodyId bodyId, float gravityScale ) +{ + B2_ASSERT( b2Body_IsValid( bodyId ) ); + B2_ASSERT( b2IsValid( gravityScale ) ); + + b2World* world = b2GetWorldLocked( bodyId.world0 ); + if ( world == NULL ) + { + return; + } + + b2Body* body = b2GetBodyFullId( world, bodyId ); + b2BodySim* bodySim = b2GetBodySim( world, body ); + bodySim->gravityScale = gravityScale; +} + +float b2Body_GetGravityScale( b2BodyId bodyId ) +{ + B2_ASSERT( b2Body_IsValid( bodyId ) ); + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + b2BodySim* bodySim = b2GetBodySim( world, body ); + return bodySim->gravityScale; +} + +bool b2Body_IsAwake( b2BodyId bodyId ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + return body->setIndex == b2_awakeSet; +} + +void b2Body_SetAwake( b2BodyId bodyId, bool awake ) +{ + b2World* world = b2GetWorldLocked( bodyId.world0 ); + if ( world == NULL ) + { + return; + } + + b2Body* body = b2GetBodyFullId( world, bodyId ); + + if ( awake && body->setIndex >= b2_firstSleepingSet ) + { + b2WakeBody( world, body ); + } + else if ( awake == false && body->setIndex == b2_awakeSet ) + { + b2Island* island = b2IslandArray_Get( &world->islands, body->islandId ); + if ( island->constraintRemoveCount > 0 ) + { + // Must split the island before sleeping. This is expensive. + b2SplitIsland( world, body->islandId ); + } + + b2TrySleepIsland( world, body->islandId ); + } +} + +bool b2Body_IsEnabled( b2BodyId bodyId ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + return body->setIndex != b2_disabledSet; +} + +bool b2Body_IsSleepEnabled( b2BodyId bodyId ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + return body->enableSleep; +} + +void b2Body_SetSleepThreshold( b2BodyId bodyId, float sleepThreshold ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + body->sleepThreshold = sleepThreshold; +} + +float b2Body_GetSleepThreshold( b2BodyId bodyId ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + return body->sleepThreshold; +} + +void b2Body_EnableSleep( b2BodyId bodyId, bool enableSleep ) +{ + b2World* world = b2GetWorldLocked( bodyId.world0 ); + if ( world == NULL ) + { + return; + } + + b2Body* body = b2GetBodyFullId( world, bodyId ); + body->enableSleep = enableSleep; + + if ( enableSleep == false ) + { + b2WakeBody( world, body ); + } +} + +// Disabling a body requires a lot of detailed bookkeeping, but it is a valuable feature. +// The most challenging aspect that joints may connect to bodies that are not disabled. +void b2Body_Disable( b2BodyId bodyId ) +{ + b2World* world = b2GetWorldLocked( bodyId.world0 ); + if ( world == NULL ) + { + return; + } + + b2Body* body = b2GetBodyFullId( world, bodyId ); + if ( body->setIndex == b2_disabledSet ) + { + return; + } + + // Destroy contacts and wake bodies touching this body. This avoid floating bodies. + // This is necessary even for static bodies. + bool wakeBodies = true; + b2DestroyBodyContacts( world, body, wakeBodies ); + + // Disabled bodies are not in an island. + b2RemoveBodyFromIsland( world, body ); + + // Remove shapes from broad-phase + int shapeId = body->headShapeId; + while ( shapeId != B2_NULL_INDEX ) + { + b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId ); + shapeId = shape->nextShapeId; + b2DestroyShapeProxy( shape, &world->broadPhase ); + } + + // Transfer simulation data to disabled set + b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, body->setIndex ); + b2SolverSet* disabledSet = b2SolverSetArray_Get( &world->solverSets, b2_disabledSet ); + + // Transfer body sim + b2TransferBody( world, disabledSet, set, body ); + + // Unlink joints and transfer + int jointKey = body->headJointKey; + while ( jointKey != B2_NULL_INDEX ) + { + int jointId = jointKey >> 1; + int edgeIndex = jointKey & 1; + + b2Joint* joint = b2JointArray_Get( &world->joints, jointId ); + jointKey = joint->edges[edgeIndex].nextKey; + + // joint may already be disabled by other body + if ( joint->setIndex == b2_disabledSet ) + { + continue; + } + + B2_ASSERT( joint->setIndex == set->setIndex || set->setIndex == b2_staticSet ); + + // Remove joint from island + if ( joint->islandId != B2_NULL_INDEX ) + { + b2UnlinkJoint( world, joint ); + } + + // Transfer joint to disabled set + b2SolverSet* jointSet = b2SolverSetArray_Get( &world->solverSets, joint->setIndex ); + b2TransferJoint( world, disabledSet, jointSet, joint ); + } + + b2ValidateConnectivity( world ); + b2ValidateSolverSets( world ); +} + +void b2Body_Enable( b2BodyId bodyId ) +{ + b2World* world = b2GetWorldLocked( bodyId.world0 ); + if ( world == NULL ) + { + return; + } + + b2Body* body = b2GetBodyFullId( world, bodyId ); + if ( body->setIndex != b2_disabledSet ) + { + return; + } + + b2SolverSet* disabledSet = b2SolverSetArray_Get( &world->solverSets, b2_disabledSet ); + int setId = body->type == b2_staticBody ? b2_staticSet : b2_awakeSet; + b2SolverSet* targetSet = b2SolverSetArray_Get( &world->solverSets, setId ); + + b2TransferBody( world, targetSet, disabledSet, body ); + + b2Transform transform = b2GetBodyTransformQuick( world, body ); + + // Add shapes to broad-phase + b2BodyType proxyType = body->type; + bool forcePairCreation = true; + int shapeId = body->headShapeId; + while ( shapeId != B2_NULL_INDEX ) + { + b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId ); + shapeId = shape->nextShapeId; + + b2CreateShapeProxy( shape, &world->broadPhase, proxyType, transform, forcePairCreation ); + } + + if ( setId != b2_staticSet ) + { + b2CreateIslandForBody( world, setId, body ); + } + + // Transfer joints. If the other body is disabled, don't transfer. + // If the other body is sleeping, wake it. + bool mergeIslands = false; + int jointKey = body->headJointKey; + while ( jointKey != B2_NULL_INDEX ) + { + int jointId = jointKey >> 1; + int edgeIndex = jointKey & 1; + + b2Joint* joint = b2JointArray_Get( &world->joints, jointId ); + B2_ASSERT( joint->setIndex == b2_disabledSet ); + B2_ASSERT( joint->islandId == B2_NULL_INDEX ); + + jointKey = joint->edges[edgeIndex].nextKey; + + b2Body* bodyA = b2BodyArray_Get( &world->bodies, joint->edges[0].bodyId ); + b2Body* bodyB = b2BodyArray_Get( &world->bodies, joint->edges[1].bodyId ); + + if ( bodyA->setIndex == b2_disabledSet || bodyB->setIndex == b2_disabledSet ) + { + // one body is still disabled + continue; + } + + // Transfer joint first + int jointSetId; + if ( bodyA->setIndex == b2_staticSet && bodyB->setIndex == b2_staticSet ) + { + jointSetId = b2_staticSet; + } + else if ( bodyA->setIndex == b2_staticSet ) + { + jointSetId = bodyB->setIndex; + } + else + { + jointSetId = bodyA->setIndex; + } + + b2SolverSet* jointSet = b2SolverSetArray_Get( &world->solverSets, jointSetId ); + b2TransferJoint( world, jointSet, disabledSet, joint ); + + // Now that the joint is in the correct set, I can link the joint in the island. + if ( jointSetId != b2_staticSet ) + { + b2LinkJoint( world, joint, mergeIslands ); + } + } + + // Now merge islands + b2MergeAwakeIslands( world ); + + b2ValidateSolverSets( world ); +} + +void b2Body_SetFixedRotation( b2BodyId bodyId, bool flag ) +{ + b2World* world = b2GetWorldLocked( bodyId.world0 ); + if ( world == NULL ) + { + return; + } + + b2Body* body = b2GetBodyFullId( world, bodyId ); + if ( body->fixedRotation != flag ) + { + body->fixedRotation = flag; + + b2BodyState* state = b2GetBodyState( world, body ); + if ( state != NULL ) + { + state->angularVelocity = 0.0f; + } + b2UpdateBodyMassData( world, body ); + } +} + +bool b2Body_IsFixedRotation( b2BodyId bodyId ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + return body->fixedRotation; +} + +void b2Body_SetBullet( b2BodyId bodyId, bool flag ) +{ + b2World* world = b2GetWorldLocked( bodyId.world0 ); + if ( world == NULL ) + { + return; + } + + b2Body* body = b2GetBodyFullId( world, bodyId ); + b2BodySim* bodySim = b2GetBodySim( world, body ); + bodySim->isBullet = flag; +} + +bool b2Body_IsBullet( b2BodyId bodyId ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + b2BodySim* bodySim = b2GetBodySim( world, body ); + return bodySim->isBullet; +} + +void b2Body_EnableHitEvents( b2BodyId bodyId, bool enableHitEvents ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + int shapeId = body->headShapeId; + while ( shapeId != B2_NULL_INDEX ) + { + b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId ); + shape->enableHitEvents = enableHitEvents; + shapeId = shape->nextShapeId; + } +} + +b2WorldId b2Body_GetWorld( b2BodyId bodyId ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + return ( b2WorldId ){ bodyId.world0 + 1, world->revision }; +} + +int b2Body_GetShapeCount( b2BodyId bodyId ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + return body->shapeCount; +} + +int b2Body_GetShapes( b2BodyId bodyId, b2ShapeId* shapeArray, int capacity ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + int shapeId = body->headShapeId; + int shapeCount = 0; + while ( shapeId != B2_NULL_INDEX && shapeCount < capacity ) + { + b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId ); + b2ShapeId id = { shape->id + 1, bodyId.world0, shape->revision }; + shapeArray[shapeCount] = id; + shapeCount += 1; + + shapeId = shape->nextShapeId; + } + + return shapeCount; +} + +int b2Body_GetJointCount( b2BodyId bodyId ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + return body->jointCount; +} + +int b2Body_GetJoints( b2BodyId bodyId, b2JointId* jointArray, int capacity ) +{ + b2World* world = b2GetWorld( bodyId.world0 ); + b2Body* body = b2GetBodyFullId( world, bodyId ); + int jointKey = body->headJointKey; + + int jointCount = 0; + while ( jointKey != B2_NULL_INDEX && jointCount < capacity ) + { + int jointId = jointKey >> 1; + int edgeIndex = jointKey & 1; + + b2Joint* joint = b2JointArray_Get( &world->joints, jointId ); + + b2JointId id = { jointId + 1, bodyId.world0, joint->revision }; + jointArray[jointCount] = id; + jointCount += 1; + + jointKey = joint->edges[edgeIndex].nextKey; + } + + return jointCount; +} + +bool b2ShouldBodiesCollide( b2World* world, b2Body* bodyA, b2Body* bodyB ) +{ + if ( bodyA->type != b2_dynamicBody && bodyB->type != b2_dynamicBody ) + { + return false; + } + + int jointKey; + int otherBodyId; + if ( bodyA->jointCount < bodyB->jointCount ) + { + jointKey = bodyA->headJointKey; + otherBodyId = bodyB->id; + } + else + { + jointKey = bodyB->headJointKey; + otherBodyId = bodyA->id; + } + + while ( jointKey != B2_NULL_INDEX ) + { + int jointId = jointKey >> 1; + int edgeIndex = jointKey & 1; + int otherEdgeIndex = edgeIndex ^ 1; + + b2Joint* joint = b2JointArray_Get( &world->joints, jointId ); + if ( joint->collideConnected == false && joint->edges[otherEdgeIndex].bodyId == otherBodyId ) + { + return false; + } + + jointKey = joint->edges[edgeIndex].nextKey; + } + + return true; +} diff --git a/3rdparty/box2d/src/body.h b/3rdparty/box2d/src/body.h new file mode 100644 index 000000000000..2265ed0b56f1 --- /dev/null +++ b/3rdparty/box2d/src/body.h @@ -0,0 +1,172 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include "array.h" + +#include "box2d/math_functions.h" +#include "box2d/types.h" + +typedef struct b2Polygon b2Polygon; +typedef struct b2World b2World; +typedef struct b2JointSim b2JointSim; +typedef struct b2ContactSim b2ContactSim; +typedef struct b2Shape b2Shape; +typedef struct b2Body b2Body; + +// Body organizational details that are not used in the solver. +typedef struct b2Body +{ + void* userData; + + // index of solver set stored in b2World + // may be B2_NULL_INDEX + int setIndex; + + // body sim and state index within set + // may be B2_NULL_INDEX + int localIndex; + + // [31 : contactId | 1 : edgeIndex] + int headContactKey; + int contactCount; + + // todo maybe move this to the body sim + int headShapeId; + int shapeCount; + + int headChainId; + + // [31 : jointId | 1 : edgeIndex] + int headJointKey; + int jointCount; + + // All enabled dynamic and kinematic bodies are in an island. + int islandId; + + // doubly-linked island list + int islandPrev; + int islandNext; + + float sleepThreshold; + float sleepTime; + + // this is used to adjust the fellAsleep flag in the body move array + int bodyMoveIndex; + + int id; + + b2BodyType type; + + // This is monotonically advanced when a body is allocated in this slot + // Used to check for invalid b2BodyId + uint16_t revision; + + bool enableSleep; + bool fixedRotation; + bool isSpeedCapped; + bool isMarked; + bool automaticMass; +} b2Body; + +// The body state is designed for fast conversion to and from SIMD via scatter-gather. +// Only awake dynamic and kinematic bodies have a body state. +// This is used in the performance critical constraint solver +// +// 32 bytes +typedef struct b2BodyState +{ + b2Vec2 linearVelocity; // 8 + float angularVelocity; // 4 + int flags; // 4 + + // Using delta position reduces round-off error far from the origin + b2Vec2 deltaPosition; // 8 + + // Using delta rotation because I cannot access the full rotation on static bodies in + // the solver and must use zero delta rotation for static bodies (c,s) = (1,0) + b2Rot deltaRotation; // 8 +} b2BodyState; + +// Identity body state, notice the deltaRotation is {1, 0} +static const b2BodyState b2_identityBodyState = { { 0.0f, 0.0f }, 0.0f, 0, { 0.0f, 0.0f }, { 1.0f, 0.0f } }; + +// Body simulation data used for integration of position and velocity +// Transform data used for collision and solver preparation. +typedef struct b2BodySim +{ + // todo better to have transform in sim or in base body? Try both! + // transform for body origin + b2Transform transform; + + // center of mass position in world space + b2Vec2 center; + + // previous rotation and COM for TOI + b2Rot rotation0; + b2Vec2 center0; + + // location of center of mass relative to the body origin + b2Vec2 localCenter; + + b2Vec2 force; + float torque; + + float mass, invMass; + + // Rotational inertia about the center of mass. + float inertia, invInertia; + + float minExtent; + float maxExtent; + float linearDamping; + float angularDamping; + float gravityScale; + + // body data can be moved around, the id is stable (used in b2BodyId) + int bodyId; + + // todo eliminate + bool isFast; + bool isBullet; + bool isSpeedCapped; + bool allowFastRotation; + bool enlargeAABB; +} b2BodySim; + +// Get a validated body from a world using an id. +b2Body* b2GetBodyFullId( b2World* world, b2BodyId bodyId ); + +b2Transform b2GetBodyTransformQuick( b2World* world, b2Body* body ); +b2Transform b2GetBodyTransform( b2World* world, int bodyId ); + +// Create a b2BodyId from a raw id. +b2BodyId b2MakeBodyId( b2World* world, int bodyId ); + +bool b2ShouldBodiesCollide( b2World* world, b2Body* bodyA, b2Body* bodyB ); +bool b2IsBodyAwake( b2World* world, b2Body* body ); + +b2BodySim* b2GetBodySim( b2World* world, b2Body* body ); +b2BodyState* b2GetBodyState( b2World* world, b2Body* body ); + +// careful calling this because it can invalidate body, state, joint, and contact pointers +bool b2WakeBody( b2World* world, b2Body* body ); + +void b2UpdateBodyMassData( b2World* world, b2Body* body ); + +static inline b2Sweep b2MakeSweep( const b2BodySim* bodySim ) +{ + b2Sweep s; + s.c1 = bodySim->center0; + s.c2 = bodySim->center; + s.q1 = bodySim->rotation0; + s.q2 = bodySim->transform.q; + s.localCenter = bodySim->localCenter; + return s; +} + +// Define inline functions for arrays +B2_ARRAY_INLINE( b2Body, b2Body ); +B2_ARRAY_INLINE( b2BodySim, b2BodySim ); +B2_ARRAY_INLINE( b2BodyState, b2BodyState ); diff --git a/3rdparty/box2d/src/box2d.natvis b/3rdparty/box2d/src/box2d.natvis new file mode 100644 index 000000000000..89e4f92b76d1 --- /dev/null +++ b/3rdparty/box2d/src/box2d.natvis @@ -0,0 +1,27 @@ + + + + [{m128_f32[0]}, {m128_f32[1]}, {m128_f32[2]}, {m128_f32[3]}] + + m128_f32[0] + m128_f32[1] + m128_f32[2] + m128_f32[3] + (void*)this + + + + [{m256_f32[0]}, {m256_f32[1]}, {m256_f32[2]}, {m256_f32[3]}, {m256_f32[4]}, {m256_f32[5]}, {m256_f32[6]}, {m256_f32[7]}] + + m256_f32[0] + m256_f32[1] + m256_f32[2] + m256_f32[3] + m256_f32[4] + m256_f32[5] + m256_f32[6] + m256_f32[7] + (void*)this + + + diff --git a/3rdparty/box2d/src/broad_phase.c b/3rdparty/box2d/src/broad_phase.c new file mode 100644 index 000000000000..8043ea13d10b --- /dev/null +++ b/3rdparty/box2d/src/broad_phase.c @@ -0,0 +1,490 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#if defined( _MSC_VER ) && !defined( _CRT_SECURE_NO_WARNINGS ) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "broad_phase.h" + +#include "aabb.h" +#include "array.h" +#include "body.h" +#include "contact.h" +#include "core.h" +#include "shape.h" +#include "stack_allocator.h" +#include "world.h" + +#include +#include +#include + +// #include + +// static FILE* s_file = NULL; + +void b2CreateBroadPhase( b2BroadPhase* bp ) +{ + _Static_assert( b2_bodyTypeCount == 3, "must be three body types" ); + + // if (s_file == NULL) + //{ + // s_file = fopen("pairs01.txt", "a"); + // fprintf(s_file, "============\n\n"); + // } + + bp->proxyCount = 0; + bp->moveSet = b2CreateSet( 16 ); + bp->moveArray = b2IntArray_Create( 16 ); + bp->moveResults = NULL; + bp->movePairs = NULL; + bp->movePairCapacity = 0; + bp->movePairIndex = 0; + bp->pairSet = b2CreateSet( 32 ); + + for ( int i = 0; i < b2_bodyTypeCount; ++i ) + { + bp->trees[i] = b2DynamicTree_Create(); + } +} + +void b2DestroyBroadPhase( b2BroadPhase* bp ) +{ + for ( int i = 0; i < b2_bodyTypeCount; ++i ) + { + b2DynamicTree_Destroy( bp->trees + i ); + } + + b2DestroySet( &bp->moveSet ); + b2IntArray_Destroy( &bp->moveArray ); + b2DestroySet( &bp->pairSet ); + + memset( bp, 0, sizeof( b2BroadPhase ) ); + + // if (s_file != NULL) + //{ + // fclose(s_file); + // s_file = NULL; + // } +} + +static inline void b2UnBufferMove( b2BroadPhase* bp, int proxyKey ) +{ + bool found = b2RemoveKey( &bp->moveSet, proxyKey + 1 ); + + if ( found ) + { + // Purge from move buffer. Linear search. + // todo if I can iterate the move set then I don't need the moveArray + int count = bp->moveArray.count; + for ( int i = 0; i < count; ++i ) + { + if ( bp->moveArray.data[i] == proxyKey ) + { + b2IntArray_RemoveSwap( &bp->moveArray, i ); + break; + } + } + } +} + +int b2BroadPhase_CreateProxy( b2BroadPhase* bp, b2BodyType proxyType, b2AABB aabb, uint64_t categoryBits, int shapeIndex, + bool forcePairCreation ) +{ + B2_ASSERT( 0 <= proxyType && proxyType < b2_bodyTypeCount ); + int proxyId = b2DynamicTree_CreateProxy( bp->trees + proxyType, aabb, categoryBits, shapeIndex ); + int proxyKey = B2_PROXY_KEY( proxyId, proxyType ); + if ( proxyType != b2_staticBody || forcePairCreation ) + { + b2BufferMove( bp, proxyKey ); + } + return proxyKey; +} + +void b2BroadPhase_DestroyProxy( b2BroadPhase* bp, int proxyKey ) +{ + B2_ASSERT( bp->moveArray.count == (int)bp->moveSet.count ); + b2UnBufferMove( bp, proxyKey ); + + --bp->proxyCount; + + b2BodyType proxyType = B2_PROXY_TYPE( proxyKey ); + int proxyId = B2_PROXY_ID( proxyKey ); + + B2_ASSERT( 0 <= proxyType && proxyType <= b2_bodyTypeCount ); + b2DynamicTree_DestroyProxy( bp->trees + proxyType, proxyId ); +} + +void b2BroadPhase_MoveProxy( b2BroadPhase* bp, int proxyKey, b2AABB aabb ) +{ + b2BodyType proxyType = B2_PROXY_TYPE( proxyKey ); + int proxyId = B2_PROXY_ID( proxyKey ); + + b2DynamicTree_MoveProxy( bp->trees + proxyType, proxyId, aabb ); + b2BufferMove( bp, proxyKey ); +} + +void b2BroadPhase_EnlargeProxy( b2BroadPhase* bp, int proxyKey, b2AABB aabb ) +{ + B2_ASSERT( proxyKey != B2_NULL_INDEX ); + int typeIndex = B2_PROXY_TYPE( proxyKey ); + int proxyId = B2_PROXY_ID( proxyKey ); + + B2_ASSERT( typeIndex != b2_staticBody ); + + b2DynamicTree_EnlargeProxy( bp->trees + typeIndex, proxyId, aabb ); + b2BufferMove( bp, proxyKey ); +} + +typedef struct b2MovePair +{ + int shapeIndexA; + int shapeIndexB; + b2MovePair* next; + bool heap; +} b2MovePair; + +typedef struct b2MoveResult +{ + b2MovePair* pairList; +} b2MoveResult; + +typedef struct b2QueryPairContext +{ + b2World* world; + b2MoveResult* moveResult; + b2BodyType queryTreeType; + int queryProxyKey; + int queryShapeIndex; +} b2QueryPairContext; + +// This is called from b2DynamicTree::Query when we are gathering pairs. +static bool b2PairQueryCallback( int proxyId, int shapeId, void* context ) +{ + b2QueryPairContext* queryContext = context; + b2BroadPhase* bp = &queryContext->world->broadPhase; + + int proxyKey = B2_PROXY_KEY( proxyId, queryContext->queryTreeType ); + + // A proxy cannot form a pair with itself. + if ( proxyKey == queryContext->queryProxyKey ) + { + return true; + } + + // Is this proxy also moving? + if ( queryContext->queryTreeType != b2_staticBody && proxyKey < queryContext->queryProxyKey ) + { + bool moved = b2ContainsKey( &bp->moveSet, proxyKey + 1 ); + if ( moved ) + { + // Both proxies are moving. Avoid duplicate pairs. + return true; + } + } + + uint64_t pairKey = B2_SHAPE_PAIR_KEY( shapeId, queryContext->queryShapeIndex ); + if ( b2ContainsKey( &bp->pairSet, pairKey ) ) + { + // contact exists + return true; + } + + int shapeIdA, shapeIdB; + if ( proxyKey < queryContext->queryProxyKey ) + { + shapeIdA = shapeId; + shapeIdB = queryContext->queryShapeIndex; + } + else + { + shapeIdA = queryContext->queryShapeIndex; + shapeIdB = shapeId; + } + + b2World* world = queryContext->world; + + b2Shape* shapeA = b2ShapeArray_Get( &world->shapes, shapeIdA ); + b2Shape* shapeB = b2ShapeArray_Get( &world->shapes, shapeIdB ); + + int bodyIdA = shapeA->bodyId; + int bodyIdB = shapeB->bodyId; + + // Are the shapes on the same body? + if ( bodyIdA == bodyIdB ) + { + return true; + } + + if ( b2ShouldShapesCollide( shapeA->filter, shapeB->filter ) == false ) + { + return true; + } + + // Sensors don't collide with other sensors + if ( shapeA->isSensor == true && shapeB->isSensor == true ) + { + return true; + } + + // Does a joint override collision? + b2Body* bodyA = b2BodyArray_Get( &world->bodies, bodyIdA ); + b2Body* bodyB = b2BodyArray_Get( &world->bodies, bodyIdB ); + if ( b2ShouldBodiesCollide( world, bodyA, bodyB ) == false ) + { + return true; + } + + // Custom user filter + b2CustomFilterFcn* customFilterFcn = queryContext->world->customFilterFcn; + if ( customFilterFcn != NULL ) + { + b2ShapeId idA = { shapeIdA + 1, world->worldId, shapeA->revision }; + b2ShapeId idB = { shapeIdB + 1, world->worldId, shapeB->revision }; + bool shouldCollide = customFilterFcn( idA, idB, queryContext->world->customFilterContext ); + if ( shouldCollide == false ) + { + return true; + } + } + + // #todo per thread to eliminate atomic? + int pairIndex = atomic_fetch_add( &bp->movePairIndex, 1 ); + + b2MovePair* pair; + if ( pairIndex < bp->movePairCapacity ) + { + pair = bp->movePairs + pairIndex; + pair->heap = false; + } + else + { + pair = b2Alloc( sizeof( b2MovePair ) ); + pair->heap = true; + } + + pair->shapeIndexA = shapeIdA; + pair->shapeIndexB = shapeIdB; + pair->next = queryContext->moveResult->pairList; + queryContext->moveResult->pairList = pair; + + // continue the query + return true; +} + +void b2FindPairsTask( int startIndex, int endIndex, uint32_t threadIndex, void* context ) +{ + b2TracyCZoneNC( pair_task, "Pair Task", b2_colorAquamarine, true ); + + B2_MAYBE_UNUSED( threadIndex ); + + b2World* world = context; + b2BroadPhase* bp = &world->broadPhase; + + b2QueryPairContext queryContext; + queryContext.world = world; + + for ( int i = startIndex; i < endIndex; ++i ) + { + // Initialize move result for this moved proxy + queryContext.moveResult = bp->moveResults + i; + queryContext.moveResult->pairList = NULL; + + int proxyKey = bp->moveArray.data[i]; + if ( proxyKey == B2_NULL_INDEX ) + { + // proxy was destroyed after it moved + continue; + } + + b2BodyType proxyType = B2_PROXY_TYPE( proxyKey ); + + int proxyId = B2_PROXY_ID( proxyKey ); + queryContext.queryProxyKey = proxyKey; + + const b2DynamicTree* baseTree = bp->trees + proxyType; + + // We have to query the tree with the fat AABB so that + // we don't fail to create a contact that may touch later. + b2AABB fatAABB = b2DynamicTree_GetAABB( baseTree, proxyId ); + queryContext.queryShapeIndex = b2DynamicTree_GetUserData( baseTree, proxyId ); + + // Query trees. Only dynamic proxies collide with kinematic and static proxies. + // Using b2_defaultMaskBits so that b2Filter::groupIndex works. + if ( proxyType == b2_dynamicBody ) + { + // consider using bits = groupIndex > 0 ? b2_defaultMaskBits : maskBits + queryContext.queryTreeType = b2_kinematicBody; + b2DynamicTree_Query( bp->trees + b2_kinematicBody, fatAABB, b2_defaultMaskBits, b2PairQueryCallback, &queryContext ); + + queryContext.queryTreeType = b2_staticBody; + b2DynamicTree_Query( bp->trees + b2_staticBody, fatAABB, b2_defaultMaskBits, b2PairQueryCallback, &queryContext ); + } + + // All proxies collide with dynamic proxies + // Using b2_defaultMaskBits so that b2Filter::groupIndex works. + queryContext.queryTreeType = b2_dynamicBody; + b2DynamicTree_Query( bp->trees + b2_dynamicBody, fatAABB, b2_defaultMaskBits, b2PairQueryCallback, &queryContext ); + } + + b2TracyCZoneEnd( pair_task ); +} + +void b2UpdateBroadPhasePairs( b2World* world ) +{ + b2BroadPhase* bp = &world->broadPhase; + + int moveCount = bp->moveArray.count; + B2_ASSERT( moveCount == (int)bp->moveSet.count ); + + if ( moveCount == 0 ) + { + return; + } + + b2TracyCZoneNC( update_pairs, "Pairs", b2_colorMagenta, true ); + + b2StackAllocator* alloc = &world->stackAllocator; + + // todo these could be in the step context + bp->moveResults = b2AllocateStackItem( alloc, moveCount * sizeof( b2MoveResult ), "move results" ); + bp->movePairCapacity = 16 * moveCount; + bp->movePairs = b2AllocateStackItem( alloc, bp->movePairCapacity * sizeof( b2MovePair ), "move pairs" ); + bp->movePairIndex = 0; + +#ifndef NDEBUG + extern _Atomic int g_probeCount; + g_probeCount = 0; +#endif + + int minRange = 64; + void* userPairTask = world->enqueueTaskFcn( &b2FindPairsTask, moveCount, minRange, world, world->userTaskContext ); + world->finishTaskFcn( userPairTask, world->userTaskContext ); + world->taskCount += 1; + + b2TracyCZoneNC( create_contacts, "Create Contacts", b2_colorGold, true ); + + // Single-threaded work + // - Clear move flags + // - Create contacts in deterministic order + for ( int i = 0; i < moveCount; ++i ) + { + b2MoveResult* result = bp->moveResults + i; + b2MovePair* pair = result->pairList; + while ( pair != NULL ) + { + int shapeIdA = pair->shapeIndexA; + int shapeIdB = pair->shapeIndexB; + + // if (s_file != NULL) + //{ + // fprintf(s_file, "%d %d\n", shapeIdA, shapeIdB); + // } + + b2Shape* shapeA = b2ShapeArray_Get( &world->shapes, shapeIdA ); + b2Shape* shapeB = b2ShapeArray_Get( &world->shapes, shapeIdB ); + + b2CreateContact( world, shapeA, shapeB ); + + if ( pair->heap ) + { + b2MovePair* temp = pair; + pair = pair->next; + b2Free( temp, sizeof( b2MovePair ) ); + } + else + { + pair = pair->next; + } + } + + // if (s_file != NULL) + //{ + // fprintf(s_file, "\n"); + // } + } + + // if (s_file != NULL) + //{ + // fprintf(s_file, "count = %d\n\n", pairCount); + // } + + // Reset move buffer + b2IntArray_Clear( &bp->moveArray ); + b2ClearSet( &bp->moveSet ); + + b2FreeStackItem( alloc, bp->movePairs ); + bp->movePairs = NULL; + b2FreeStackItem( alloc, bp->moveResults ); + bp->moveResults = NULL; + + b2ValidateSolverSets( world ); + + b2TracyCZoneEnd( create_contacts ); + + b2TracyCZoneEnd( update_pairs ); +} + +bool b2BroadPhase_TestOverlap( const b2BroadPhase* bp, int proxyKeyA, int proxyKeyB ) +{ + int typeIndexA = B2_PROXY_TYPE( proxyKeyA ); + int proxyIdA = B2_PROXY_ID( proxyKeyA ); + int typeIndexB = B2_PROXY_TYPE( proxyKeyB ); + int proxyIdB = B2_PROXY_ID( proxyKeyB ); + + b2AABB aabbA = b2DynamicTree_GetAABB( bp->trees + typeIndexA, proxyIdA ); + b2AABB aabbB = b2DynamicTree_GetAABB( bp->trees + typeIndexB, proxyIdB ); + return b2AABB_Overlaps( aabbA, aabbB ); +} + +void b2BroadPhase_RebuildTrees( b2BroadPhase* bp ) +{ + b2DynamicTree_Rebuild( bp->trees + b2_dynamicBody, false ); + b2DynamicTree_Rebuild( bp->trees + b2_kinematicBody, false ); +} + +int b2BroadPhase_GetShapeIndex( b2BroadPhase* bp, int proxyKey ) +{ + int typeIndex = B2_PROXY_TYPE( proxyKey ); + int proxyId = B2_PROXY_ID( proxyKey ); + + return b2DynamicTree_GetUserData( bp->trees + typeIndex, proxyId ); +} + +void b2ValidateBroadphase( const b2BroadPhase* bp ) +{ + b2DynamicTree_Validate( bp->trees + b2_dynamicBody ); + b2DynamicTree_Validate( bp->trees + b2_kinematicBody ); + + // TODO_ERIN validate every shape AABB is contained in tree AABB +} + +void b2ValidateNoEnlarged( const b2BroadPhase* bp ) +{ +#if B2_VALIDATE == 1 + for ( int j = 0; j < b2_bodyTypeCount; ++j ) + { + const b2DynamicTree* tree = bp->trees + j; + int capacity = tree->nodeCapacity; + const b2TreeNode* nodes = tree->nodes; + for ( int i = 0; i < capacity; ++i ) + { + const b2TreeNode* node = nodes + i; + if ( node->height < 0 ) + { + continue; + } + + if ( node->enlarged == true ) + { + capacity += 0; + } + + B2_ASSERT( node->enlarged == false ); + } + } +#else + B2_MAYBE_UNUSED( bp ); +#endif +} diff --git a/3rdparty/box2d/src/broad_phase.h b/3rdparty/box2d/src/broad_phase.h new file mode 100644 index 000000000000..ad53ef12d76b --- /dev/null +++ b/3rdparty/box2d/src/broad_phase.h @@ -0,0 +1,83 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include "array.h" +#include "table.h" + +#include "box2d/collision.h" +#include "box2d/types.h" + +typedef struct b2Shape b2Shape; +typedef struct b2MovePair b2MovePair; +typedef struct b2MoveResult b2MoveResult; +typedef struct b2StackAllocator b2StackAllocator; +typedef struct b2World b2World; + +// Store the proxy type in the lower 2 bits of the proxy key. This leaves 30 bits for the id. +#define B2_PROXY_TYPE( KEY ) ( (b2BodyType)( ( KEY ) & 3 ) ) +#define B2_PROXY_ID( KEY ) ( ( KEY ) >> 2 ) +#define B2_PROXY_KEY( ID, TYPE ) ( ( ( ID ) << 2 ) | ( TYPE ) ) + +/// The broad-phase is used for computing pairs and performing volume queries and ray casts. +/// This broad-phase does not persist pairs. Instead, this reports potentially new pairs. +/// It is up to the client to consume the new pairs and to track subsequent overlap. +typedef struct b2BroadPhase +{ + b2DynamicTree trees[b2_bodyTypeCount]; + int proxyCount; + + // The move set and array are used to track shapes that have moved significantly + // and need a pair query for new contacts. The array has a deterministic order. + // todo perhaps just a move set? + // todo implement a 32bit hash set for faster lookup + // todo moveSet can grow quite large on the first time step and remain large + b2HashSet moveSet; + b2IntArray moveArray; + + // These are the results from the pair query and are used to create new contacts + // in deterministic order. + // todo these could be in the step context + b2MoveResult* moveResults; + b2MovePair* movePairs; + int movePairCapacity; + _Atomic int movePairIndex; + + // Tracks shape pairs that have a b2Contact + // todo pairSet can grow quite large on the first time step and remain large + b2HashSet pairSet; + +} b2BroadPhase; + +void b2CreateBroadPhase( b2BroadPhase* bp ); +void b2DestroyBroadPhase( b2BroadPhase* bp ); + +int b2BroadPhase_CreateProxy( b2BroadPhase* bp, b2BodyType proxyType, b2AABB aabb, uint64_t categoryBits, int shapeIndex, + bool forcePairCreation ); +void b2BroadPhase_DestroyProxy( b2BroadPhase* bp, int proxyKey ); + +void b2BroadPhase_MoveProxy( b2BroadPhase* bp, int proxyKey, b2AABB aabb ); +void b2BroadPhase_EnlargeProxy( b2BroadPhase* bp, int proxyKey, b2AABB aabb ); + +void b2BroadPhase_RebuildTrees( b2BroadPhase* bp ); + +int b2BroadPhase_GetShapeIndex( b2BroadPhase* bp, int proxyKey ); + +void b2UpdateBroadPhasePairs( b2World* world ); +bool b2BroadPhase_TestOverlap( const b2BroadPhase* bp, int proxyKeyA, int proxyKeyB ); + +void b2ValidateBroadphase( const b2BroadPhase* bp ); +void b2ValidateNoEnlarged( const b2BroadPhase* bp ); + +// This is what triggers new contact pairs to be created +// Warning: this must be called in deterministic order +static inline void b2BufferMove( b2BroadPhase* bp, int queryProxy ) +{ + // Adding 1 because 0 is the sentinel + bool alreadyAdded = b2AddKey( &bp->moveSet, queryProxy + 1 ); + if ( alreadyAdded == false ) + { + b2IntArray_Push( &bp->moveArray, queryProxy ); + } +} diff --git a/3rdparty/box2d/src/collision/b2_broad_phase.cpp b/3rdparty/box2d/src/collision/b2_broad_phase.cpp deleted file mode 100644 index d063a3aa9bf4..000000000000 --- a/3rdparty/box2d/src/collision/b2_broad_phase.cpp +++ /dev/null @@ -1,131 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "box2d/b2_broad_phase.h" -#include - -b2BroadPhase::b2BroadPhase() -{ - m_proxyCount = 0; - - m_pairCapacity = 16; - m_pairCount = 0; - m_pairBuffer = (b2Pair*)b2Alloc(m_pairCapacity * sizeof(b2Pair)); - - m_moveCapacity = 16; - m_moveCount = 0; - m_moveBuffer = (int32*)b2Alloc(m_moveCapacity * sizeof(int32)); -} - -b2BroadPhase::~b2BroadPhase() -{ - b2Free(m_moveBuffer); - b2Free(m_pairBuffer); -} - -int32 b2BroadPhase::CreateProxy(const b2AABB& aabb, void* userData) -{ - int32 proxyId = m_tree.CreateProxy(aabb, userData); - ++m_proxyCount; - BufferMove(proxyId); - return proxyId; -} - -void b2BroadPhase::DestroyProxy(int32 proxyId) -{ - UnBufferMove(proxyId); - --m_proxyCount; - m_tree.DestroyProxy(proxyId); -} - -void b2BroadPhase::MoveProxy(int32 proxyId, const b2AABB& aabb, const b2Vec2& displacement) -{ - bool buffer = m_tree.MoveProxy(proxyId, aabb, displacement); - if (buffer) - { - BufferMove(proxyId); - } -} - -void b2BroadPhase::TouchProxy(int32 proxyId) -{ - BufferMove(proxyId); -} - -void b2BroadPhase::BufferMove(int32 proxyId) -{ - if (m_moveCount == m_moveCapacity) - { - int32* oldBuffer = m_moveBuffer; - m_moveCapacity *= 2; - m_moveBuffer = (int32*)b2Alloc(m_moveCapacity * sizeof(int32)); - memcpy(m_moveBuffer, oldBuffer, m_moveCount * sizeof(int32)); - b2Free(oldBuffer); - } - - m_moveBuffer[m_moveCount] = proxyId; - ++m_moveCount; -} - -void b2BroadPhase::UnBufferMove(int32 proxyId) -{ - for (int32 i = 0; i < m_moveCount; ++i) - { - if (m_moveBuffer[i] == proxyId) - { - m_moveBuffer[i] = e_nullProxy; - } - } -} - -// This is called from b2DynamicTree::Query when we are gathering pairs. -bool b2BroadPhase::QueryCallback(int32 proxyId) -{ - // A proxy cannot form a pair with itself. - if (proxyId == m_queryProxyId) - { - return true; - } - - const bool moved = m_tree.WasMoved(proxyId); - if (moved && proxyId > m_queryProxyId) - { - // Both proxies are moving. Avoid duplicate pairs. - return true; - } - - // Grow the pair buffer as needed. - if (m_pairCount == m_pairCapacity) - { - b2Pair* oldBuffer = m_pairBuffer; - m_pairCapacity = m_pairCapacity + (m_pairCapacity >> 1); - m_pairBuffer = (b2Pair*)b2Alloc(m_pairCapacity * sizeof(b2Pair)); - memcpy(m_pairBuffer, oldBuffer, m_pairCount * sizeof(b2Pair)); - b2Free(oldBuffer); - } - - m_pairBuffer[m_pairCount].proxyIdA = b2Min(proxyId, m_queryProxyId); - m_pairBuffer[m_pairCount].proxyIdB = b2Max(proxyId, m_queryProxyId); - ++m_pairCount; - - return true; -} diff --git a/3rdparty/box2d/src/collision/b2_chain_shape.cpp b/3rdparty/box2d/src/collision/b2_chain_shape.cpp deleted file mode 100644 index b964a4303c04..000000000000 --- a/3rdparty/box2d/src/collision/b2_chain_shape.cpp +++ /dev/null @@ -1,185 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "box2d/b2_chain_shape.h" -#include "box2d/b2_edge_shape.h" - -#include "box2d/b2_block_allocator.h" - -#include -#include - -b2ChainShape::~b2ChainShape() -{ - Clear(); -} - -void b2ChainShape::Clear() -{ - b2Free(m_vertices); - m_vertices = nullptr; - m_count = 0; -} - -void b2ChainShape::CreateLoop(const b2Vec2* vertices, int32 count) -{ - b2Assert(m_vertices == nullptr && m_count == 0); - b2Assert(count >= 3); - if (count < 3) - { - return; - } - - for (int32 i = 1; i < count; ++i) - { - b2Vec2 v1 = vertices[i-1]; - b2Vec2 v2 = vertices[i]; - // If the code crashes here, it means your vertices are too close together. - b2Assert(b2DistanceSquared(v1, v2) > b2_linearSlop * b2_linearSlop); - } - - m_count = count + 1; - m_vertices = (b2Vec2*)b2Alloc(m_count * sizeof(b2Vec2)); - memcpy(m_vertices, vertices, count * sizeof(b2Vec2)); - m_vertices[count] = m_vertices[0]; - m_prevVertex = m_vertices[m_count - 2]; - m_nextVertex = m_vertices[1]; -} - -void b2ChainShape::CreateChain(const b2Vec2* vertices, int32 count, const b2Vec2& prevVertex, const b2Vec2& nextVertex) -{ - b2Assert(m_vertices == nullptr && m_count == 0); - b2Assert(count >= 2); - for (int32 i = 1; i < count; ++i) - { - // If the code crashes here, it means your vertices are too close together. - b2Assert(b2DistanceSquared(vertices[i-1], vertices[i]) > b2_linearSlop * b2_linearSlop); - } - - m_count = count; - m_vertices = (b2Vec2*)b2Alloc(count * sizeof(b2Vec2)); - memcpy(m_vertices, vertices, m_count * sizeof(b2Vec2)); - - m_prevVertex = prevVertex; - m_nextVertex = nextVertex; -} - -b2Shape* b2ChainShape::Clone(b2BlockAllocator* allocator) const -{ - void* mem = allocator->Allocate(sizeof(b2ChainShape)); - b2ChainShape* clone = new (mem) b2ChainShape; - clone->CreateChain(m_vertices, m_count, m_prevVertex, m_nextVertex); - return clone; -} - -int32 b2ChainShape::GetChildCount() const -{ - // edge count = vertex count - 1 - return m_count - 1; -} - -void b2ChainShape::GetChildEdge(b2EdgeShape* edge, int32 index) const -{ - b2Assert(0 <= index && index < m_count - 1); - edge->m_type = b2Shape::e_edge; - edge->m_radius = m_radius; - - edge->m_vertex1 = m_vertices[index + 0]; - edge->m_vertex2 = m_vertices[index + 1]; - edge->m_oneSided = true; - - if (index > 0) - { - edge->m_vertex0 = m_vertices[index - 1]; - } - else - { - edge->m_vertex0 = m_prevVertex; - } - - if (index < m_count - 2) - { - edge->m_vertex3 = m_vertices[index + 2]; - } - else - { - edge->m_vertex3 = m_nextVertex; - } -} - -bool b2ChainShape::TestPoint(const b2Transform& xf, const b2Vec2& p) const -{ - B2_NOT_USED(xf); - B2_NOT_USED(p); - return false; -} - -bool b2ChainShape::RayCast(b2RayCastOutput* output, const b2RayCastInput& input, - const b2Transform& xf, int32 childIndex) const -{ - b2Assert(childIndex < m_count); - - b2EdgeShape edgeShape; - - int32 i1 = childIndex; - int32 i2 = childIndex + 1; - if (i2 == m_count) - { - i2 = 0; - } - - edgeShape.m_vertex1 = m_vertices[i1]; - edgeShape.m_vertex2 = m_vertices[i2]; - - return edgeShape.RayCast(output, input, xf, 0); -} - -void b2ChainShape::ComputeAABB(b2AABB* aabb, const b2Transform& xf, int32 childIndex) const -{ - b2Assert(childIndex < m_count); - - int32 i1 = childIndex; - int32 i2 = childIndex + 1; - if (i2 == m_count) - { - i2 = 0; - } - - b2Vec2 v1 = b2Mul(xf, m_vertices[i1]); - b2Vec2 v2 = b2Mul(xf, m_vertices[i2]); - - b2Vec2 lower = b2Min(v1, v2); - b2Vec2 upper = b2Max(v1, v2); - - b2Vec2 r(m_radius, m_radius); - aabb->lowerBound = lower - r; - aabb->upperBound = upper + r; -} - -void b2ChainShape::ComputeMass(b2MassData* massData, float density) const -{ - B2_NOT_USED(density); - - massData->mass = 0.0f; - massData->center.SetZero(); - massData->I = 0.0f; -} diff --git a/3rdparty/box2d/src/collision/b2_circle_shape.cpp b/3rdparty/box2d/src/collision/b2_circle_shape.cpp deleted file mode 100644 index ecc69293b584..000000000000 --- a/3rdparty/box2d/src/collision/b2_circle_shape.cpp +++ /dev/null @@ -1,105 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "box2d/b2_circle_shape.h" -#include "box2d/b2_block_allocator.h" - -#include - -b2Shape* b2CircleShape::Clone(b2BlockAllocator* allocator) const -{ - void* mem = allocator->Allocate(sizeof(b2CircleShape)); - b2CircleShape* clone = new (mem) b2CircleShape; - *clone = *this; - return clone; -} - -int32 b2CircleShape::GetChildCount() const -{ - return 1; -} - -bool b2CircleShape::TestPoint(const b2Transform& transform, const b2Vec2& p) const -{ - b2Vec2 center = transform.p + b2Mul(transform.q, m_p); - b2Vec2 d = p - center; - return b2Dot(d, d) <= m_radius * m_radius; -} - -// Collision Detection in Interactive 3D Environments by Gino van den Bergen -// From Section 3.1.2 -// x = s + a * r -// norm(x) = radius -bool b2CircleShape::RayCast(b2RayCastOutput* output, const b2RayCastInput& input, - const b2Transform& transform, int32 childIndex) const -{ - B2_NOT_USED(childIndex); - - b2Vec2 position = transform.p + b2Mul(transform.q, m_p); - b2Vec2 s = input.p1 - position; - float b = b2Dot(s, s) - m_radius * m_radius; - - // Solve quadratic equation. - b2Vec2 r = input.p2 - input.p1; - float c = b2Dot(s, r); - float rr = b2Dot(r, r); - float sigma = c * c - rr * b; - - // Check for negative discriminant and short segment. - if (sigma < 0.0f || rr < b2_epsilon) - { - return false; - } - - // Find the point of intersection of the line with the circle. - float a = -(c + b2Sqrt(sigma)); - - // Is the intersection point on the segment? - if (0.0f <= a && a <= input.maxFraction * rr) - { - a /= rr; - output->fraction = a; - output->normal = s + a * r; - output->normal.Normalize(); - return true; - } - - return false; -} - -void b2CircleShape::ComputeAABB(b2AABB* aabb, const b2Transform& transform, int32 childIndex) const -{ - B2_NOT_USED(childIndex); - - b2Vec2 p = transform.p + b2Mul(transform.q, m_p); - aabb->lowerBound.Set(p.x - m_radius, p.y - m_radius); - aabb->upperBound.Set(p.x + m_radius, p.y + m_radius); -} - -void b2CircleShape::ComputeMass(b2MassData* massData, float density) const -{ - massData->mass = density * b2_pi * m_radius * m_radius; - massData->center = m_p; - - // inertia about the local origin - massData->I = massData->mass * (0.5f * m_radius * m_radius + b2Dot(m_p, m_p)); -} diff --git a/3rdparty/box2d/src/collision/b2_collide_circle.cpp b/3rdparty/box2d/src/collision/b2_collide_circle.cpp deleted file mode 100644 index 469da55907e9..000000000000 --- a/3rdparty/box2d/src/collision/b2_collide_circle.cpp +++ /dev/null @@ -1,158 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "box2d/b2_collision.h" -#include "box2d/b2_circle_shape.h" -#include "box2d/b2_polygon_shape.h" - -void b2CollideCircles( - b2Manifold* manifold, - const b2CircleShape* circleA, const b2Transform& xfA, - const b2CircleShape* circleB, const b2Transform& xfB) -{ - manifold->pointCount = 0; - - b2Vec2 pA = b2Mul(xfA, circleA->m_p); - b2Vec2 pB = b2Mul(xfB, circleB->m_p); - - b2Vec2 d = pB - pA; - float distSqr = b2Dot(d, d); - float rA = circleA->m_radius, rB = circleB->m_radius; - float radius = rA + rB; - if (distSqr > radius * radius) - { - return; - } - - manifold->type = b2Manifold::e_circles; - manifold->localPoint = circleA->m_p; - manifold->localNormal.SetZero(); - manifold->pointCount = 1; - - manifold->points[0].localPoint = circleB->m_p; - manifold->points[0].id.key = 0; -} - -void b2CollidePolygonAndCircle( - b2Manifold* manifold, - const b2PolygonShape* polygonA, const b2Transform& xfA, - const b2CircleShape* circleB, const b2Transform& xfB) -{ - manifold->pointCount = 0; - - // Compute circle position in the frame of the polygon. - b2Vec2 c = b2Mul(xfB, circleB->m_p); - b2Vec2 cLocal = b2MulT(xfA, c); - - // Find the min separating edge. - int32 normalIndex = 0; - float separation = -b2_maxFloat; - float radius = polygonA->m_radius + circleB->m_radius; - int32 vertexCount = polygonA->m_count; - const b2Vec2* vertices = polygonA->m_vertices; - const b2Vec2* normals = polygonA->m_normals; - - for (int32 i = 0; i < vertexCount; ++i) - { - float s = b2Dot(normals[i], cLocal - vertices[i]); - - if (s > radius) - { - // Early out. - return; - } - - if (s > separation) - { - separation = s; - normalIndex = i; - } - } - - // Vertices that subtend the incident face. - int32 vertIndex1 = normalIndex; - int32 vertIndex2 = vertIndex1 + 1 < vertexCount ? vertIndex1 + 1 : 0; - b2Vec2 v1 = vertices[vertIndex1]; - b2Vec2 v2 = vertices[vertIndex2]; - - // If the center is inside the polygon ... - if (separation < b2_epsilon) - { - manifold->pointCount = 1; - manifold->type = b2Manifold::e_faceA; - manifold->localNormal = normals[normalIndex]; - manifold->localPoint = 0.5f * (v1 + v2); - manifold->points[0].localPoint = circleB->m_p; - manifold->points[0].id.key = 0; - return; - } - - // Compute barycentric coordinates - float u1 = b2Dot(cLocal - v1, v2 - v1); - float u2 = b2Dot(cLocal - v2, v1 - v2); - if (u1 <= 0.0f) - { - if (b2DistanceSquared(cLocal, v1) > radius * radius) - { - return; - } - - manifold->pointCount = 1; - manifold->type = b2Manifold::e_faceA; - manifold->localNormal = cLocal - v1; - manifold->localNormal.Normalize(); - manifold->localPoint = v1; - manifold->points[0].localPoint = circleB->m_p; - manifold->points[0].id.key = 0; - } - else if (u2 <= 0.0f) - { - if (b2DistanceSquared(cLocal, v2) > radius * radius) - { - return; - } - - manifold->pointCount = 1; - manifold->type = b2Manifold::e_faceA; - manifold->localNormal = cLocal - v2; - manifold->localNormal.Normalize(); - manifold->localPoint = v2; - manifold->points[0].localPoint = circleB->m_p; - manifold->points[0].id.key = 0; - } - else - { - b2Vec2 faceCenter = 0.5f * (v1 + v2); - float s = b2Dot(cLocal - faceCenter, normals[vertIndex1]); - if (s > radius) - { - return; - } - - manifold->pointCount = 1; - manifold->type = b2Manifold::e_faceA; - manifold->localNormal = normals[vertIndex1]; - manifold->localPoint = faceCenter; - manifold->points[0].localPoint = circleB->m_p; - manifold->points[0].id.key = 0; - } -} diff --git a/3rdparty/box2d/src/collision/b2_collide_edge.cpp b/3rdparty/box2d/src/collision/b2_collide_edge.cpp deleted file mode 100644 index e06b90097fad..000000000000 --- a/3rdparty/box2d/src/collision/b2_collide_edge.cpp +++ /dev/null @@ -1,524 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "box2d/b2_collision.h" -#include "box2d/b2_circle_shape.h" -#include "box2d/b2_edge_shape.h" -#include "box2d/b2_polygon_shape.h" - - -// Compute contact points for edge versus circle. -// This accounts for edge connectivity. -void b2CollideEdgeAndCircle(b2Manifold* manifold, - const b2EdgeShape* edgeA, const b2Transform& xfA, - const b2CircleShape* circleB, const b2Transform& xfB) -{ - manifold->pointCount = 0; - - // Compute circle in frame of edge - b2Vec2 Q = b2MulT(xfA, b2Mul(xfB, circleB->m_p)); - - b2Vec2 A = edgeA->m_vertex1, B = edgeA->m_vertex2; - b2Vec2 e = B - A; - - // Normal points to the right for a CCW winding - b2Vec2 n(e.y, -e.x); - float offset = b2Dot(n, Q - A); - - bool oneSided = edgeA->m_oneSided; - if (oneSided && offset < 0.0f) - { - return; - } - - // Barycentric coordinates - float u = b2Dot(e, B - Q); - float v = b2Dot(e, Q - A); - - float radius = edgeA->m_radius + circleB->m_radius; - - b2ContactFeature cf; - cf.indexB = 0; - cf.typeB = b2ContactFeature::e_vertex; - - // Region A - if (v <= 0.0f) - { - b2Vec2 P = A; - b2Vec2 d = Q - P; - float dd = b2Dot(d, d); - if (dd > radius * radius) - { - return; - } - - // Is there an edge connected to A? - if (edgeA->m_oneSided) - { - b2Vec2 A1 = edgeA->m_vertex0; - b2Vec2 B1 = A; - b2Vec2 e1 = B1 - A1; - float u1 = b2Dot(e1, B1 - Q); - - // Is the circle in Region AB of the previous edge? - if (u1 > 0.0f) - { - return; - } - } - - cf.indexA = 0; - cf.typeA = b2ContactFeature::e_vertex; - manifold->pointCount = 1; - manifold->type = b2Manifold::e_circles; - manifold->localNormal.SetZero(); - manifold->localPoint = P; - manifold->points[0].id.key = 0; - manifold->points[0].id.cf = cf; - manifold->points[0].localPoint = circleB->m_p; - return; - } - - // Region B - if (u <= 0.0f) - { - b2Vec2 P = B; - b2Vec2 d = Q - P; - float dd = b2Dot(d, d); - if (dd > radius * radius) - { - return; - } - - // Is there an edge connected to B? - if (edgeA->m_oneSided) - { - b2Vec2 B2 = edgeA->m_vertex3; - b2Vec2 A2 = B; - b2Vec2 e2 = B2 - A2; - float v2 = b2Dot(e2, Q - A2); - - // Is the circle in Region AB of the next edge? - if (v2 > 0.0f) - { - return; - } - } - - cf.indexA = 1; - cf.typeA = b2ContactFeature::e_vertex; - manifold->pointCount = 1; - manifold->type = b2Manifold::e_circles; - manifold->localNormal.SetZero(); - manifold->localPoint = P; - manifold->points[0].id.key = 0; - manifold->points[0].id.cf = cf; - manifold->points[0].localPoint = circleB->m_p; - return; - } - - // Region AB - float den = b2Dot(e, e); - b2Assert(den > 0.0f); - b2Vec2 P = (1.0f / den) * (u * A + v * B); - b2Vec2 d = Q - P; - float dd = b2Dot(d, d); - if (dd > radius * radius) - { - return; - } - - if (offset < 0.0f) - { - n.Set(-n.x, -n.y); - } - n.Normalize(); - - cf.indexA = 0; - cf.typeA = b2ContactFeature::e_face; - manifold->pointCount = 1; - manifold->type = b2Manifold::e_faceA; - manifold->localNormal = n; - manifold->localPoint = A; - manifold->points[0].id.key = 0; - manifold->points[0].id.cf = cf; - manifold->points[0].localPoint = circleB->m_p; -} - -// This structure is used to keep track of the best separating axis. -struct b2EPAxis -{ - enum Type - { - e_unknown, - e_edgeA, - e_edgeB - }; - - b2Vec2 normal; - Type type; - int32 index; - float separation; -}; - -// This holds polygon B expressed in frame A. -struct b2TempPolygon -{ - b2Vec2 vertices[b2_maxPolygonVertices]; - b2Vec2 normals[b2_maxPolygonVertices]; - int32 count; -}; - -// Reference face used for clipping -struct b2ReferenceFace -{ - int32 i1, i2; - b2Vec2 v1, v2; - b2Vec2 normal; - - b2Vec2 sideNormal1; - float sideOffset1; - - b2Vec2 sideNormal2; - float sideOffset2; -}; - -static b2EPAxis b2ComputeEdgeSeparation(const b2TempPolygon& polygonB, const b2Vec2& v1, const b2Vec2& normal1) -{ - b2EPAxis axis; - axis.type = b2EPAxis::e_edgeA; - axis.index = -1; - axis.separation = -FLT_MAX; - axis.normal.SetZero(); - - b2Vec2 axes[2] = { normal1, -normal1 }; - - // Find axis with least overlap (min-max problem) - for (int32 j = 0; j < 2; ++j) - { - float sj = FLT_MAX; - - // Find deepest polygon vertex along axis j - for (int32 i = 0; i < polygonB.count; ++i) - { - float si = b2Dot(axes[j], polygonB.vertices[i] - v1); - if (si < sj) - { - sj = si; - } - } - - if (sj > axis.separation) - { - axis.index = j; - axis.separation = sj; - axis.normal = axes[j]; - } - } - - return axis; -} - -static b2EPAxis b2ComputePolygonSeparation(const b2TempPolygon& polygonB, const b2Vec2& v1, const b2Vec2& v2) -{ - b2EPAxis axis; - axis.type = b2EPAxis::e_unknown; - axis.index = -1; - axis.separation = -FLT_MAX; - axis.normal.SetZero(); - - for (int32 i = 0; i < polygonB.count; ++i) - { - b2Vec2 n = -polygonB.normals[i]; - - float s1 = b2Dot(n, polygonB.vertices[i] - v1); - float s2 = b2Dot(n, polygonB.vertices[i] - v2); - float s = b2Min(s1, s2); - - if (s > axis.separation) - { - axis.type = b2EPAxis::e_edgeB; - axis.index = i; - axis.separation = s; - axis.normal = n; - } - } - - return axis; -} - -void b2CollideEdgeAndPolygon(b2Manifold* manifold, - const b2EdgeShape* edgeA, const b2Transform& xfA, - const b2PolygonShape* polygonB, const b2Transform& xfB) -{ - manifold->pointCount = 0; - - b2Transform xf = b2MulT(xfA, xfB); - - b2Vec2 centroidB = b2Mul(xf, polygonB->m_centroid); - - b2Vec2 v1 = edgeA->m_vertex1; - b2Vec2 v2 = edgeA->m_vertex2; - - b2Vec2 edge1 = v2 - v1; - edge1.Normalize(); - - // Normal points to the right for a CCW winding - b2Vec2 normal1(edge1.y, -edge1.x); - float offset1 = b2Dot(normal1, centroidB - v1); - - bool oneSided = edgeA->m_oneSided; - if (oneSided && offset1 < 0.0f) - { - return; - } - - // Get polygonB in frameA - b2TempPolygon tempPolygonB; - tempPolygonB.count = polygonB->m_count; - for (int32 i = 0; i < polygonB->m_count; ++i) - { - tempPolygonB.vertices[i] = b2Mul(xf, polygonB->m_vertices[i]); - tempPolygonB.normals[i] = b2Mul(xf.q, polygonB->m_normals[i]); - } - - float radius = polygonB->m_radius + edgeA->m_radius; - - b2EPAxis edgeAxis = b2ComputeEdgeSeparation(tempPolygonB, v1, normal1); - if (edgeAxis.separation > radius) - { - return; - } - - b2EPAxis polygonAxis = b2ComputePolygonSeparation(tempPolygonB, v1, v2); - if (polygonAxis.separation > radius) - { - return; - } - - // Use hysteresis for jitter reduction. - const float k_relativeTol = 0.98f; - const float k_absoluteTol = 0.001f; - - b2EPAxis primaryAxis; - if (polygonAxis.separation - radius > k_relativeTol * (edgeAxis.separation - radius) + k_absoluteTol) - { - primaryAxis = polygonAxis; - } - else - { - primaryAxis = edgeAxis; - } - - if (oneSided) - { - // Smooth collision - // See https://box2d.org/posts/2020/06/ghost-collisions/ - - b2Vec2 edge0 = v1 - edgeA->m_vertex0; - edge0.Normalize(); - b2Vec2 normal0(edge0.y, -edge0.x); - bool convex1 = b2Cross(edge0, edge1) >= 0.0f; - - b2Vec2 edge2 = edgeA->m_vertex3 - v2; - edge2.Normalize(); - b2Vec2 normal2(edge2.y, -edge2.x); - bool convex2 = b2Cross(edge1, edge2) >= 0.0f; - - const float sinTol = 0.1f; - bool side1 = b2Dot(primaryAxis.normal, edge1) <= 0.0f; - - // Check Gauss Map - if (side1) - { - if (convex1) - { - if (b2Cross(primaryAxis.normal, normal0) > sinTol) - { - // Skip region - return; - } - - // Admit region - } - else - { - // Snap region - primaryAxis = edgeAxis; - } - } - else - { - if (convex2) - { - if (b2Cross(normal2, primaryAxis.normal) > sinTol) - { - // Skip region - return; - } - - // Admit region - } - else - { - // Snap region - primaryAxis = edgeAxis; - } - } - } - - b2ClipVertex clipPoints[2]; - b2ReferenceFace ref; - if (primaryAxis.type == b2EPAxis::e_edgeA) - { - manifold->type = b2Manifold::e_faceA; - - // Search for the polygon normal that is most anti-parallel to the edge normal. - int32 bestIndex = 0; - float bestValue = b2Dot(primaryAxis.normal, tempPolygonB.normals[0]); - for (int32 i = 1; i < tempPolygonB.count; ++i) - { - float value = b2Dot(primaryAxis.normal, tempPolygonB.normals[i]); - if (value < bestValue) - { - bestValue = value; - bestIndex = i; - } - } - - int32 i1 = bestIndex; - int32 i2 = i1 + 1 < tempPolygonB.count ? i1 + 1 : 0; - - clipPoints[0].v = tempPolygonB.vertices[i1]; - clipPoints[0].id.cf.indexA = 0; - clipPoints[0].id.cf.indexB = static_cast(i1); - clipPoints[0].id.cf.typeA = b2ContactFeature::e_face; - clipPoints[0].id.cf.typeB = b2ContactFeature::e_vertex; - - clipPoints[1].v = tempPolygonB.vertices[i2]; - clipPoints[1].id.cf.indexA = 0; - clipPoints[1].id.cf.indexB = static_cast(i2); - clipPoints[1].id.cf.typeA = b2ContactFeature::e_face; - clipPoints[1].id.cf.typeB = b2ContactFeature::e_vertex; - - ref.i1 = 0; - ref.i2 = 1; - ref.v1 = v1; - ref.v2 = v2; - ref.normal = primaryAxis.normal; - ref.sideNormal1 = -edge1; - ref.sideNormal2 = edge1; - } - else - { - manifold->type = b2Manifold::e_faceB; - - clipPoints[0].v = v2; - clipPoints[0].id.cf.indexA = 1; - clipPoints[0].id.cf.indexB = static_cast(primaryAxis.index); - clipPoints[0].id.cf.typeA = b2ContactFeature::e_vertex; - clipPoints[0].id.cf.typeB = b2ContactFeature::e_face; - - clipPoints[1].v = v1; - clipPoints[1].id.cf.indexA = 0; - clipPoints[1].id.cf.indexB = static_cast(primaryAxis.index); - clipPoints[1].id.cf.typeA = b2ContactFeature::e_vertex; - clipPoints[1].id.cf.typeB = b2ContactFeature::e_face; - - ref.i1 = primaryAxis.index; - ref.i2 = ref.i1 + 1 < tempPolygonB.count ? ref.i1 + 1 : 0; - ref.v1 = tempPolygonB.vertices[ref.i1]; - ref.v2 = tempPolygonB.vertices[ref.i2]; - ref.normal = tempPolygonB.normals[ref.i1]; - - // CCW winding - ref.sideNormal1.Set(ref.normal.y, -ref.normal.x); - ref.sideNormal2 = -ref.sideNormal1; - } - - ref.sideOffset1 = b2Dot(ref.sideNormal1, ref.v1); - ref.sideOffset2 = b2Dot(ref.sideNormal2, ref.v2); - - // Clip incident edge against reference face side planes - b2ClipVertex clipPoints1[2]; - b2ClipVertex clipPoints2[2]; - int32 np; - - // Clip to side 1 - np = b2ClipSegmentToLine(clipPoints1, clipPoints, ref.sideNormal1, ref.sideOffset1, ref.i1); - - if (np < b2_maxManifoldPoints) - { - return; - } - - // Clip to side 2 - np = b2ClipSegmentToLine(clipPoints2, clipPoints1, ref.sideNormal2, ref.sideOffset2, ref.i2); - - if (np < b2_maxManifoldPoints) - { - return; - } - - // Now clipPoints2 contains the clipped points. - if (primaryAxis.type == b2EPAxis::e_edgeA) - { - manifold->localNormal = ref.normal; - manifold->localPoint = ref.v1; - } - else - { - manifold->localNormal = polygonB->m_normals[ref.i1]; - manifold->localPoint = polygonB->m_vertices[ref.i1]; - } - - int32 pointCount = 0; - for (int32 i = 0; i < b2_maxManifoldPoints; ++i) - { - float separation; - - separation = b2Dot(ref.normal, clipPoints2[i].v - ref.v1); - - if (separation <= radius) - { - b2ManifoldPoint* cp = manifold->points + pointCount; - - if (primaryAxis.type == b2EPAxis::e_edgeA) - { - cp->localPoint = b2MulT(xf, clipPoints2[i].v); - cp->id = clipPoints2[i].id; - } - else - { - cp->localPoint = clipPoints2[i].v; - cp->id.cf.typeA = clipPoints2[i].id.cf.typeB; - cp->id.cf.typeB = clipPoints2[i].id.cf.typeA; - cp->id.cf.indexA = clipPoints2[i].id.cf.indexB; - cp->id.cf.indexB = clipPoints2[i].id.cf.indexA; - } - - ++pointCount; - } - } - - manifold->pointCount = pointCount; -} diff --git a/3rdparty/box2d/src/collision/b2_collide_polygon.cpp b/3rdparty/box2d/src/collision/b2_collide_polygon.cpp deleted file mode 100644 index f3fa850bcb45..000000000000 --- a/3rdparty/box2d/src/collision/b2_collide_polygon.cpp +++ /dev/null @@ -1,243 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "box2d/b2_collision.h" -#include "box2d/b2_polygon_shape.h" - -// Find the max separation between poly1 and poly2 using edge normals from poly1. -static float b2FindMaxSeparation(int32* edgeIndex, - const b2PolygonShape* poly1, const b2Transform& xf1, - const b2PolygonShape* poly2, const b2Transform& xf2) -{ - int32 count1 = poly1->m_count; - int32 count2 = poly2->m_count; - const b2Vec2* n1s = poly1->m_normals; - const b2Vec2* v1s = poly1->m_vertices; - const b2Vec2* v2s = poly2->m_vertices; - b2Transform xf = b2MulT(xf2, xf1); - - int32 bestIndex = 0; - float maxSeparation = -b2_maxFloat; - for (int32 i = 0; i < count1; ++i) - { - // Get poly1 normal in frame2. - b2Vec2 n = b2Mul(xf.q, n1s[i]); - b2Vec2 v1 = b2Mul(xf, v1s[i]); - - // Find deepest point for normal i. - float si = b2_maxFloat; - for (int32 j = 0; j < count2; ++j) - { - float sij = b2Dot(n, v2s[j] - v1); - if (sij < si) - { - si = sij; - } - } - - if (si > maxSeparation) - { - maxSeparation = si; - bestIndex = i; - } - } - - *edgeIndex = bestIndex; - return maxSeparation; -} - -static void b2FindIncidentEdge(b2ClipVertex c[2], - const b2PolygonShape* poly1, const b2Transform& xf1, int32 edge1, - const b2PolygonShape* poly2, const b2Transform& xf2) -{ - const b2Vec2* normals1 = poly1->m_normals; - - int32 count2 = poly2->m_count; - const b2Vec2* vertices2 = poly2->m_vertices; - const b2Vec2* normals2 = poly2->m_normals; - - b2Assert(0 <= edge1 && edge1 < poly1->m_count); - - // Get the normal of the reference edge in poly2's frame. - b2Vec2 normal1 = b2MulT(xf2.q, b2Mul(xf1.q, normals1[edge1])); - - // Find the incident edge on poly2. - int32 index = 0; - float minDot = b2_maxFloat; - for (int32 i = 0; i < count2; ++i) - { - float dot = b2Dot(normal1, normals2[i]); - if (dot < minDot) - { - minDot = dot; - index = i; - } - } - - // Build the clip vertices for the incident edge. - int32 i1 = index; - int32 i2 = i1 + 1 < count2 ? i1 + 1 : 0; - - c[0].v = b2Mul(xf2, vertices2[i1]); - c[0].id.cf.indexA = (uint8)edge1; - c[0].id.cf.indexB = (uint8)i1; - c[0].id.cf.typeA = b2ContactFeature::e_face; - c[0].id.cf.typeB = b2ContactFeature::e_vertex; - - c[1].v = b2Mul(xf2, vertices2[i2]); - c[1].id.cf.indexA = (uint8)edge1; - c[1].id.cf.indexB = (uint8)i2; - c[1].id.cf.typeA = b2ContactFeature::e_face; - c[1].id.cf.typeB = b2ContactFeature::e_vertex; -} - -// Find edge normal of max separation on A - return if separating axis is found -// Find edge normal of max separation on B - return if separation axis is found -// Choose reference edge as min(minA, minB) -// Find incident edge -// Clip - -// The normal points from 1 to 2 -void b2CollidePolygons(b2Manifold* manifold, - const b2PolygonShape* polyA, const b2Transform& xfA, - const b2PolygonShape* polyB, const b2Transform& xfB) -{ - manifold->pointCount = 0; - float totalRadius = polyA->m_radius + polyB->m_radius; - - int32 edgeA = 0; - float separationA = b2FindMaxSeparation(&edgeA, polyA, xfA, polyB, xfB); - if (separationA > totalRadius) - return; - - int32 edgeB = 0; - float separationB = b2FindMaxSeparation(&edgeB, polyB, xfB, polyA, xfA); - if (separationB > totalRadius) - return; - - const b2PolygonShape* poly1; // reference polygon - const b2PolygonShape* poly2; // incident polygon - b2Transform xf1, xf2; - int32 edge1; // reference edge - uint8 flip; - const float k_tol = 0.1f * b2_linearSlop; - - if (separationB > separationA + k_tol) - { - poly1 = polyB; - poly2 = polyA; - xf1 = xfB; - xf2 = xfA; - edge1 = edgeB; - manifold->type = b2Manifold::e_faceB; - flip = 1; - } - else - { - poly1 = polyA; - poly2 = polyB; - xf1 = xfA; - xf2 = xfB; - edge1 = edgeA; - manifold->type = b2Manifold::e_faceA; - flip = 0; - } - - b2ClipVertex incidentEdge[2]; - b2FindIncidentEdge(incidentEdge, poly1, xf1, edge1, poly2, xf2); - - int32 count1 = poly1->m_count; - const b2Vec2* vertices1 = poly1->m_vertices; - - int32 iv1 = edge1; - int32 iv2 = edge1 + 1 < count1 ? edge1 + 1 : 0; - - b2Vec2 v11 = vertices1[iv1]; - b2Vec2 v12 = vertices1[iv2]; - - b2Vec2 localTangent = v12 - v11; - localTangent.Normalize(); - - b2Vec2 localNormal = b2Cross(localTangent, 1.0f); - b2Vec2 planePoint = 0.5f * (v11 + v12); - - b2Vec2 tangent = b2Mul(xf1.q, localTangent); - b2Vec2 normal = b2Cross(tangent, 1.0f); - - v11 = b2Mul(xf1, v11); - v12 = b2Mul(xf1, v12); - - // Face offset. - float frontOffset = b2Dot(normal, v11); - - // Side offsets, extended by polytope skin thickness. - float sideOffset1 = -b2Dot(tangent, v11) + totalRadius; - float sideOffset2 = b2Dot(tangent, v12) + totalRadius; - - // Clip incident edge against extruded edge1 side edges. - b2ClipVertex clipPoints1[2]; - b2ClipVertex clipPoints2[2]; - int np; - - // Clip to box side 1 - np = b2ClipSegmentToLine(clipPoints1, incidentEdge, -tangent, sideOffset1, iv1); - - if (np < 2) - return; - - // Clip to negative box side 1 - np = b2ClipSegmentToLine(clipPoints2, clipPoints1, tangent, sideOffset2, iv2); - - if (np < 2) - { - return; - } - - // Now clipPoints2 contains the clipped points. - manifold->localNormal = localNormal; - manifold->localPoint = planePoint; - - int32 pointCount = 0; - for (int32 i = 0; i < b2_maxManifoldPoints; ++i) - { - float separation = b2Dot(normal, clipPoints2[i].v) - frontOffset; - - if (separation <= totalRadius) - { - b2ManifoldPoint* cp = manifold->points + pointCount; - cp->localPoint = b2MulT(xf2, clipPoints2[i].v); - cp->id = clipPoints2[i].id; - if (flip) - { - // Swap features - b2ContactFeature cf = cp->id.cf; - cp->id.cf.indexA = cf.indexB; - cp->id.cf.indexB = cf.indexA; - cp->id.cf.typeA = cf.typeB; - cp->id.cf.typeB = cf.typeA; - } - ++pointCount; - } - } - - manifold->pointCount = pointCount; -} diff --git a/3rdparty/box2d/src/collision/b2_collision.cpp b/3rdparty/box2d/src/collision/b2_collision.cpp deleted file mode 100644 index 750bff00fe7d..000000000000 --- a/3rdparty/box2d/src/collision/b2_collision.cpp +++ /dev/null @@ -1,580 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "box2d/b2_collision.h" -#include "box2d/b2_distance.h" - -void b2WorldManifold::Initialize(const b2Manifold* manifold, - const b2Transform& xfA, float radiusA, - const b2Transform& xfB, float radiusB) -{ - if (manifold->pointCount == 0) - { - return; - } - - switch (manifold->type) - { - case b2Manifold::e_circles: - { - normal.Set(1.0f, 0.0f); - b2Vec2 pointA = b2Mul(xfA, manifold->localPoint); - b2Vec2 pointB = b2Mul(xfB, manifold->points[0].localPoint); - if (b2DistanceSquared(pointA, pointB) > b2_epsilon * b2_epsilon) - { - normal = pointB - pointA; - normal.Normalize(); - } - - b2Vec2 cA = pointA + radiusA * normal; - b2Vec2 cB = pointB - radiusB * normal; - points[0] = 0.5f * (cA + cB); - separations[0] = b2Dot(cB - cA, normal); - } - break; - - case b2Manifold::e_faceA: - { - normal = b2Mul(xfA.q, manifold->localNormal); - b2Vec2 planePoint = b2Mul(xfA, manifold->localPoint); - - for (int32 i = 0; i < manifold->pointCount; ++i) - { - b2Vec2 clipPoint = b2Mul(xfB, manifold->points[i].localPoint); - b2Vec2 cA = clipPoint + (radiusA - b2Dot(clipPoint - planePoint, normal)) * normal; - b2Vec2 cB = clipPoint - radiusB * normal; - points[i] = 0.5f * (cA + cB); - separations[i] = b2Dot(cB - cA, normal); - } - } - break; - - case b2Manifold::e_faceB: - { - normal = b2Mul(xfB.q, manifold->localNormal); - b2Vec2 planePoint = b2Mul(xfB, manifold->localPoint); - - for (int32 i = 0; i < manifold->pointCount; ++i) - { - b2Vec2 clipPoint = b2Mul(xfA, manifold->points[i].localPoint); - b2Vec2 cB = clipPoint + (radiusB - b2Dot(clipPoint - planePoint, normal)) * normal; - b2Vec2 cA = clipPoint - radiusA * normal; - points[i] = 0.5f * (cA + cB); - separations[i] = b2Dot(cA - cB, normal); - } - - // Ensure normal points from A to B. - normal = -normal; - } - break; - } -} - -void b2GetPointStates(b2PointState state1[b2_maxManifoldPoints], b2PointState state2[b2_maxManifoldPoints], - const b2Manifold* manifold1, const b2Manifold* manifold2) -{ - for (int32 i = 0; i < b2_maxManifoldPoints; ++i) - { - state1[i] = b2_nullState; - state2[i] = b2_nullState; - } - - // Detect persists and removes. - for (int32 i = 0; i < manifold1->pointCount; ++i) - { - b2ContactID id = manifold1->points[i].id; - - state1[i] = b2_removeState; - - for (int32 j = 0; j < manifold2->pointCount; ++j) - { - if (manifold2->points[j].id.key == id.key) - { - state1[i] = b2_persistState; - break; - } - } - } - - // Detect persists and adds. - for (int32 i = 0; i < manifold2->pointCount; ++i) - { - b2ContactID id = manifold2->points[i].id; - - state2[i] = b2_addState; - - for (int32 j = 0; j < manifold1->pointCount; ++j) - { - if (manifold1->points[j].id.key == id.key) - { - state2[i] = b2_persistState; - break; - } - } - } -} - -// From Real-time Collision Detection, p179. -bool b2AABB::RayCast(b2RayCastOutput* output, const b2RayCastInput& input) const -{ - float tmin = -b2_maxFloat; - float tmax = b2_maxFloat; - - b2Vec2 p = input.p1; - b2Vec2 d = input.p2 - input.p1; - b2Vec2 absD = b2Abs(d); - - b2Vec2 normal; - - for (int32 i = 0; i < 2; ++i) - { - if (absD(i) < b2_epsilon) - { - // Parallel. - if (p(i) < lowerBound(i) || upperBound(i) < p(i)) - { - return false; - } - } - else - { - float inv_d = 1.0f / d(i); - float t1 = (lowerBound(i) - p(i)) * inv_d; - float t2 = (upperBound(i) - p(i)) * inv_d; - - // Sign of the normal vector. - float s = -1.0f; - - if (t1 > t2) - { - b2Swap(t1, t2); - s = 1.0f; - } - - // Push the min up - if (t1 > tmin) - { - normal.SetZero(); - normal(i) = s; - tmin = t1; - } - - // Pull the max down - tmax = b2Min(tmax, t2); - - if (tmin > tmax) - { - return false; - } - } - } - - // Does the ray start inside the box? - // Does the ray intersect beyond the max fraction? - if (tmin < 0.0f || input.maxFraction < tmin) - { - return false; - } - - // Intersection. - output->fraction = tmin; - output->normal = normal; - return true; -} - -// Sutherland-Hodgman clipping. -int32 b2ClipSegmentToLine(b2ClipVertex vOut[2], const b2ClipVertex vIn[2], - const b2Vec2& normal, float offset, int32 vertexIndexA) -{ - // Start with no output points - int32 count = 0; - - // Calculate the distance of end points to the line - float distance0 = b2Dot(normal, vIn[0].v) - offset; - float distance1 = b2Dot(normal, vIn[1].v) - offset; - - // If the points are behind the plane - if (distance0 <= 0.0f) vOut[count++] = vIn[0]; - if (distance1 <= 0.0f) vOut[count++] = vIn[1]; - - // If the points are on different sides of the plane - if (distance0 * distance1 < 0.0f) - { - // Find intersection point of edge and plane - float interp = distance0 / (distance0 - distance1); - vOut[count].v = vIn[0].v + interp * (vIn[1].v - vIn[0].v); - - // VertexA is hitting edgeB. - vOut[count].id.cf.indexA = static_cast(vertexIndexA); - vOut[count].id.cf.indexB = vIn[0].id.cf.indexB; - vOut[count].id.cf.typeA = b2ContactFeature::e_vertex; - vOut[count].id.cf.typeB = b2ContactFeature::e_face; - ++count; - - b2Assert(count == 2); - } - - return count; -} - -bool b2TestOverlap( const b2Shape* shapeA, int32 indexA, - const b2Shape* shapeB, int32 indexB, - const b2Transform& xfA, const b2Transform& xfB) -{ - b2DistanceInput input; - input.proxyA.Set(shapeA, indexA); - input.proxyB.Set(shapeB, indexB); - input.transformA = xfA; - input.transformB = xfB; - input.useRadii = true; - - b2SimplexCache cache; - cache.count = 0; - - b2DistanceOutput output; - - b2Distance(&output, &cache, &input); - - return output.distance < 10.0f * b2_epsilon; -} - -// quickhull recursion -static b2Hull b2RecurseHull(b2Vec2 p1, b2Vec2 p2, b2Vec2* ps, int32 count) -{ - b2Hull hull; - hull.count = 0; - - if (count == 0) - { - return hull; - } - - // create an edge vector pointing from p1 to p2 - b2Vec2 e = p2 - p1; - e.Normalize(); - - // discard points left of e and find point furthest to the right of e - b2Vec2 rightPoints[b2_maxPolygonVertices]{}; - int32 rightCount = 0; - - int32 bestIndex = 0; - float bestDistance = b2Cross(ps[bestIndex] - p1, e); - if (bestDistance > 0.0f) - { - rightPoints[rightCount++] = ps[bestIndex]; - } - - for (int32 i = 1; i < count; ++i) - { - float distance = b2Cross(ps[i] - p1, e); - if (distance > bestDistance) - { - bestIndex = i; - bestDistance = distance; - } - - if (distance > 0.0f) - { - rightPoints[rightCount++] = ps[i]; - } - } - - if (bestDistance < 2.0f * b2_linearSlop) - { - return hull; - } - - b2Vec2 bestPoint = ps[bestIndex]; - - // compute hull to the right of p1-bestPoint - b2Hull hull1 = b2RecurseHull(p1, bestPoint, rightPoints, rightCount); - - // compute hull to the right of bestPoint-p2 - b2Hull hull2 = b2RecurseHull(bestPoint, p2, rightPoints, rightCount); - - // stich together hulls - for (int32 i = 0; i < hull1.count; ++i) - { - hull.points[hull.count++] = hull1.points[i]; - } - - hull.points[hull.count++] = bestPoint; - - for (int32 i = 0; i < hull2.count; ++i) - { - hull.points[hull.count++] = hull2.points[i]; - } - - b2Assert(hull.count < b2_maxPolygonVertices); - - return hull; -} - -// quickhull algorithm -// - merges vertices based on b2_linearSlop -// - removes collinear points using b2_linearSlop -// - returns an empty hull if it fails -b2Hull b2ComputeHull(const b2Vec2* points, int32 count) -{ - b2Hull hull; - hull.count = 0; - - if (count < 3 || count > b2_maxPolygonVertices) - { - // check your data - return hull; - } - - count = b2Min(count, b2_maxPolygonVertices); - - b2AABB aabb = { {b2_maxFloat, b2_maxFloat}, {-b2_maxFloat, -b2_maxFloat} }; - - // Perform aggressive point welding. First point always remains. - // Also compute the bounding box for later. - b2Vec2 ps[b2_maxPolygonVertices]; - int32 n = 0; - const float tolSqr = 16.0f * b2_linearSlop * b2_linearSlop; - for (int32 i = 0; i < count; ++i) - { - aabb.lowerBound = b2Min(aabb.lowerBound, points[i]); - aabb.upperBound = b2Max(aabb.upperBound, points[i]); - - b2Vec2 vi = points[i]; - - bool unique = true; - for (int32 j = 0; j < i; ++j) - { - b2Vec2 vj = points[j]; - - float distSqr = b2DistanceSquared(vi, vj); - if (distSqr < tolSqr) - { - unique = false; - break; - } - } - - if (unique) - { - ps[n++] = vi; - } - } - - if (n < 3) - { - // all points very close together, check your data and check your scale - return hull; - } - - // Find an extreme point as the first point on the hull - b2Vec2 c = aabb.GetCenter(); - int32 i1 = 0; - float dsq1 = b2DistanceSquared(c, ps[i1]); - for (int32 i = 1; i < n; ++i) - { - float dsq = b2DistanceSquared(c, ps[i]); - if (dsq > dsq1) - { - i1 = i; - dsq1 = dsq; - } - } - - // remove p1 from working set - b2Vec2 p1 = ps[i1]; - ps[i1] = ps[n - 1]; - n = n - 1; - - int32 i2 = 0; - float dsq2 = b2DistanceSquared(p1, ps[i2]); - for (int32 i = 1; i < n; ++i) - { - float dsq = b2DistanceSquared(p1, ps[i]); - if (dsq > dsq2) - { - i2 = i; - dsq2 = dsq; - } - } - - // remove p2 from working set - b2Vec2 p2 = ps[i2]; - ps[i2] = ps[n - 1]; - n = n - 1; - - // split the points into points that are left and right of the line p1-p2. - b2Vec2 rightPoints[b2_maxPolygonVertices - 2]; - int32 rightCount = 0; - - b2Vec2 leftPoints[b2_maxPolygonVertices - 2]; - int32 leftCount = 0; - - b2Vec2 e = p2 - p1; - e.Normalize(); - - for (int32 i = 0; i < n; ++i) - { - float d = b2Cross(ps[i] - p1, e); - - // slop used here to skip points that are very close to the line p1-p2 - if (d >= 2.0f * b2_linearSlop) - { - rightPoints[rightCount++] = ps[i]; - } - else if (d <= -2.0f * b2_linearSlop) - { - leftPoints[leftCount++] = ps[i]; - } - } - - // compute hulls on right and left - b2Hull hull1 = b2RecurseHull(p1, p2, rightPoints, rightCount); - b2Hull hull2 = b2RecurseHull(p2, p1, leftPoints, leftCount); - - if (hull1.count == 0 && hull2.count == 0) - { - // all points collinear - return hull; - } - - // stitch hulls together, preserving CCW winding order - hull.points[hull.count++] = p1; - - for (int32 i = 0; i < hull1.count; ++i) - { - hull.points[hull.count++] = hull1.points[i]; - } - - hull.points[hull.count++] = p2; - - for (int32 i = 0; i < hull2.count; ++i) - { - hull.points[hull.count++] = hull2.points[i]; - } - - b2Assert(hull.count <= b2_maxPolygonVertices); - - // merge collinear - bool searching = true; - while (searching && hull.count > 2) - { - searching = false; - - for (int32 i = 0; i < hull.count; ++i) - { - int32 i1 = i; - int32 i2 = (i + 1) % hull.count; - int32 i3 = (i + 2) % hull.count; - - b2Vec2 p1 = hull.points[i1]; - b2Vec2 p2 = hull.points[i2]; - b2Vec2 p3 = hull.points[i3]; - - b2Vec2 e = p3 - p1; - e.Normalize(); - - b2Vec2 v = p2 - p1; - float distance = b2Cross(p2 - p1, e); - if (distance <= 2.0f * b2_linearSlop) - { - // remove midpoint from hull - for (int32 j = i2; j < hull.count - 1; ++j) - { - hull.points[j] = hull.points[j + 1]; - } - hull.count -= 1; - - // continue searching for collinear points - searching = true; - - break; - } - } - } - - if (hull.count < 3) - { - // all points collinear, shouldn't be reached since this was validated above - hull.count = 0; - } - - return hull; -} - -bool b2ValidateHull(const b2Hull& hull) -{ - if (hull.count < 3 || b2_maxPolygonVertices < hull.count) - { - return false; - } - - // test that every point is behind every edge - for (int32 i = 0; i < hull.count; ++i) - { - // create an edge vector - int32 i1 = i; - int32 i2 = i < hull.count - 1 ? i1 + 1 : 0; - b2Vec2 p = hull.points[i1]; - b2Vec2 e = hull.points[i2] - p; - e.Normalize(); - - for (int32 j = 0; j < hull.count; ++j) - { - // skip points that subtend the current edge - if (j == i1 || j == i2) - { - continue; - } - - float distance = b2Cross(hull.points[j] - p, e); - if (distance >= 0.0f) - { - return false; - } - } - } - - // test for collinear points - for (int32 i = 0; i < hull.count; ++i) - { - int32 i1 = i; - int32 i2 = (i + 1) % hull.count; - int32 i3 = (i + 2) % hull.count; - - b2Vec2 p1 = hull.points[i1]; - b2Vec2 p2 = hull.points[i2]; - b2Vec2 p3 = hull.points[i3]; - - b2Vec2 e = p3 - p1; - e.Normalize(); - - b2Vec2 v = p2 - p1; - float distance = b2Cross(p2 - p1, e); - if (distance <= b2_linearSlop) - { - // p1-p2-p3 are collinear - return false; - } - } - - return true; -} diff --git a/3rdparty/box2d/src/collision/b2_distance.cpp b/3rdparty/box2d/src/collision/b2_distance.cpp deleted file mode 100644 index 16fa3cc3d986..000000000000 --- a/3rdparty/box2d/src/collision/b2_distance.cpp +++ /dev/null @@ -1,744 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "box2d/b2_circle_shape.h" -#include "box2d/b2_distance.h" -#include "box2d/b2_edge_shape.h" -#include "box2d/b2_chain_shape.h" -#include "box2d/b2_polygon_shape.h" - -// GJK using Voronoi regions (Christer Ericson) and Barycentric coordinates. -B2_API int32 b2_gjkCalls, b2_gjkIters, b2_gjkMaxIters; - -void b2DistanceProxy::Set(const b2Shape* shape, int32 index) -{ - switch (shape->GetType()) - { - case b2Shape::e_circle: - { - const b2CircleShape* circle = static_cast(shape); - m_vertices = &circle->m_p; - m_count = 1; - m_radius = circle->m_radius; - } - break; - - case b2Shape::e_polygon: - { - const b2PolygonShape* polygon = static_cast(shape); - m_vertices = polygon->m_vertices; - m_count = polygon->m_count; - m_radius = polygon->m_radius; - } - break; - - case b2Shape::e_chain: - { - const b2ChainShape* chain = static_cast(shape); - b2Assert(0 <= index && index < chain->m_count); - - m_buffer[0] = chain->m_vertices[index]; - if (index + 1 < chain->m_count) - { - m_buffer[1] = chain->m_vertices[index + 1]; - } - else - { - m_buffer[1] = chain->m_vertices[0]; - } - - m_vertices = m_buffer; - m_count = 2; - m_radius = chain->m_radius; - } - break; - - case b2Shape::e_edge: - { - const b2EdgeShape* edge = static_cast(shape); - m_vertices = &edge->m_vertex1; - m_count = 2; - m_radius = edge->m_radius; - } - break; - - default: - b2Assert(false); - } -} - -void b2DistanceProxy::Set(const b2Vec2* vertices, int32 count, float radius) -{ - m_vertices = vertices; - m_count = count; - m_radius = radius; -} - -struct b2SimplexVertex -{ - b2Vec2 wA; // support point in proxyA - b2Vec2 wB; // support point in proxyB - b2Vec2 w; // wB - wA - float a; // barycentric coordinate for closest point - int32 indexA; // wA index - int32 indexB; // wB index -}; - -struct b2Simplex -{ - void ReadCache( const b2SimplexCache* cache, - const b2DistanceProxy* proxyA, const b2Transform& transformA, - const b2DistanceProxy* proxyB, const b2Transform& transformB) - { - b2Assert(cache->count <= 3); - - // Copy data from cache. - m_count = cache->count; - b2SimplexVertex* vertices = &m_v1; - for (int32 i = 0; i < m_count; ++i) - { - b2SimplexVertex* v = vertices + i; - v->indexA = cache->indexA[i]; - v->indexB = cache->indexB[i]; - b2Vec2 wALocal = proxyA->GetVertex(v->indexA); - b2Vec2 wBLocal = proxyB->GetVertex(v->indexB); - v->wA = b2Mul(transformA, wALocal); - v->wB = b2Mul(transformB, wBLocal); - v->w = v->wB - v->wA; - v->a = 0.0f; - } - - // Compute the new simplex metric, if it is substantially different than - // old metric then flush the simplex. - if (m_count > 1) - { - float metric1 = cache->metric; - float metric2 = GetMetric(); - if (metric2 < 0.5f * metric1 || 2.0f * metric1 < metric2 || metric2 < b2_epsilon) - { - // Reset the simplex. - m_count = 0; - } - } - - // If the cache is empty or invalid ... - if (m_count == 0) - { - b2SimplexVertex* v = vertices + 0; - v->indexA = 0; - v->indexB = 0; - b2Vec2 wALocal = proxyA->GetVertex(0); - b2Vec2 wBLocal = proxyB->GetVertex(0); - v->wA = b2Mul(transformA, wALocal); - v->wB = b2Mul(transformB, wBLocal); - v->w = v->wB - v->wA; - v->a = 1.0f; - m_count = 1; - } - } - - void WriteCache(b2SimplexCache* cache) const - { - cache->metric = GetMetric(); - cache->count = uint16(m_count); - const b2SimplexVertex* vertices = &m_v1; - for (int32 i = 0; i < m_count; ++i) - { - cache->indexA[i] = uint8(vertices[i].indexA); - cache->indexB[i] = uint8(vertices[i].indexB); - } - } - - b2Vec2 GetSearchDirection() const - { - switch (m_count) - { - case 1: - return -m_v1.w; - - case 2: - { - b2Vec2 e12 = m_v2.w - m_v1.w; - float sgn = b2Cross(e12, -m_v1.w); - if (sgn > 0.0f) - { - // Origin is left of e12. - return b2Cross(1.0f, e12); - } - else - { - // Origin is right of e12. - return b2Cross(e12, 1.0f); - } - } - - default: - b2Assert(false); - return b2Vec2_zero; - } - } - - b2Vec2 GetClosestPoint() const - { - switch (m_count) - { - case 0: - b2Assert(false); - return b2Vec2_zero; - - case 1: - return m_v1.w; - - case 2: - return m_v1.a * m_v1.w + m_v2.a * m_v2.w; - - case 3: - return b2Vec2_zero; - - default: - b2Assert(false); - return b2Vec2_zero; - } - } - - void GetWitnessPoints(b2Vec2* pA, b2Vec2* pB) const - { - switch (m_count) - { - case 0: - b2Assert(false); - break; - - case 1: - *pA = m_v1.wA; - *pB = m_v1.wB; - break; - - case 2: - *pA = m_v1.a * m_v1.wA + m_v2.a * m_v2.wA; - *pB = m_v1.a * m_v1.wB + m_v2.a * m_v2.wB; - break; - - case 3: - *pA = m_v1.a * m_v1.wA + m_v2.a * m_v2.wA + m_v3.a * m_v3.wA; - *pB = *pA; - break; - - default: - b2Assert(false); - break; - } - } - - float GetMetric() const - { - switch (m_count) - { - case 0: - b2Assert(false); - return 0.0f; - - case 1: - return 0.0f; - - case 2: - return b2Distance(m_v1.w, m_v2.w); - - case 3: - return b2Cross(m_v2.w - m_v1.w, m_v3.w - m_v1.w); - - default: - b2Assert(false); - return 0.0f; - } - } - - void Solve2(); - void Solve3(); - - b2SimplexVertex m_v1, m_v2, m_v3; - int32 m_count; -}; - - -// Solve a line segment using barycentric coordinates. -// -// p = a1 * w1 + a2 * w2 -// a1 + a2 = 1 -// -// The vector from the origin to the closest point on the line is -// perpendicular to the line. -// e12 = w2 - w1 -// dot(p, e) = 0 -// a1 * dot(w1, e) + a2 * dot(w2, e) = 0 -// -// 2-by-2 linear system -// [1 1 ][a1] = [1] -// [w1.e12 w2.e12][a2] = [0] -// -// Define -// d12_1 = dot(w2, e12) -// d12_2 = -dot(w1, e12) -// d12 = d12_1 + d12_2 -// -// Solution -// a1 = d12_1 / d12 -// a2 = d12_2 / d12 -void b2Simplex::Solve2() -{ - b2Vec2 w1 = m_v1.w; - b2Vec2 w2 = m_v2.w; - b2Vec2 e12 = w2 - w1; - - // w1 region - float d12_2 = -b2Dot(w1, e12); - if (d12_2 <= 0.0f) - { - // a2 <= 0, so we clamp it to 0 - m_v1.a = 1.0f; - m_count = 1; - return; - } - - // w2 region - float d12_1 = b2Dot(w2, e12); - if (d12_1 <= 0.0f) - { - // a1 <= 0, so we clamp it to 0 - m_v2.a = 1.0f; - m_count = 1; - m_v1 = m_v2; - return; - } - - // Must be in e12 region. - float inv_d12 = 1.0f / (d12_1 + d12_2); - m_v1.a = d12_1 * inv_d12; - m_v2.a = d12_2 * inv_d12; - m_count = 2; -} - -// Possible regions: -// - points[2] -// - edge points[0]-points[2] -// - edge points[1]-points[2] -// - inside the triangle -void b2Simplex::Solve3() -{ - b2Vec2 w1 = m_v1.w; - b2Vec2 w2 = m_v2.w; - b2Vec2 w3 = m_v3.w; - - // Edge12 - // [1 1 ][a1] = [1] - // [w1.e12 w2.e12][a2] = [0] - // a3 = 0 - b2Vec2 e12 = w2 - w1; - float w1e12 = b2Dot(w1, e12); - float w2e12 = b2Dot(w2, e12); - float d12_1 = w2e12; - float d12_2 = -w1e12; - - // Edge13 - // [1 1 ][a1] = [1] - // [w1.e13 w3.e13][a3] = [0] - // a2 = 0 - b2Vec2 e13 = w3 - w1; - float w1e13 = b2Dot(w1, e13); - float w3e13 = b2Dot(w3, e13); - float d13_1 = w3e13; - float d13_2 = -w1e13; - - // Edge23 - // [1 1 ][a2] = [1] - // [w2.e23 w3.e23][a3] = [0] - // a1 = 0 - b2Vec2 e23 = w3 - w2; - float w2e23 = b2Dot(w2, e23); - float w3e23 = b2Dot(w3, e23); - float d23_1 = w3e23; - float d23_2 = -w2e23; - - // Triangle123 - float n123 = b2Cross(e12, e13); - - float d123_1 = n123 * b2Cross(w2, w3); - float d123_2 = n123 * b2Cross(w3, w1); - float d123_3 = n123 * b2Cross(w1, w2); - - // w1 region - if (d12_2 <= 0.0f && d13_2 <= 0.0f) - { - m_v1.a = 1.0f; - m_count = 1; - return; - } - - // e12 - if (d12_1 > 0.0f && d12_2 > 0.0f && d123_3 <= 0.0f) - { - float inv_d12 = 1.0f / (d12_1 + d12_2); - m_v1.a = d12_1 * inv_d12; - m_v2.a = d12_2 * inv_d12; - m_count = 2; - return; - } - - // e13 - if (d13_1 > 0.0f && d13_2 > 0.0f && d123_2 <= 0.0f) - { - float inv_d13 = 1.0f / (d13_1 + d13_2); - m_v1.a = d13_1 * inv_d13; - m_v3.a = d13_2 * inv_d13; - m_count = 2; - m_v2 = m_v3; - return; - } - - // w2 region - if (d12_1 <= 0.0f && d23_2 <= 0.0f) - { - m_v2.a = 1.0f; - m_count = 1; - m_v1 = m_v2; - return; - } - - // w3 region - if (d13_1 <= 0.0f && d23_1 <= 0.0f) - { - m_v3.a = 1.0f; - m_count = 1; - m_v1 = m_v3; - return; - } - - // e23 - if (d23_1 > 0.0f && d23_2 > 0.0f && d123_1 <= 0.0f) - { - float inv_d23 = 1.0f / (d23_1 + d23_2); - m_v2.a = d23_1 * inv_d23; - m_v3.a = d23_2 * inv_d23; - m_count = 2; - m_v1 = m_v3; - return; - } - - // Must be in triangle123 - float inv_d123 = 1.0f / (d123_1 + d123_2 + d123_3); - m_v1.a = d123_1 * inv_d123; - m_v2.a = d123_2 * inv_d123; - m_v3.a = d123_3 * inv_d123; - m_count = 3; -} - -void b2Distance(b2DistanceOutput* output, - b2SimplexCache* cache, - const b2DistanceInput* input) -{ - ++b2_gjkCalls; - - const b2DistanceProxy* proxyA = &input->proxyA; - const b2DistanceProxy* proxyB = &input->proxyB; - - b2Transform transformA = input->transformA; - b2Transform transformB = input->transformB; - - // Initialize the simplex. - b2Simplex simplex; - simplex.ReadCache(cache, proxyA, transformA, proxyB, transformB); - - // Get simplex vertices as an array. - b2SimplexVertex* vertices = &simplex.m_v1; - const int32 k_maxIters = 20; - - // These store the vertices of the last simplex so that we - // can check for duplicates and prevent cycling. - int32 saveA[3], saveB[3]; - int32 saveCount = 0; - - // Main iteration loop. - int32 iter = 0; - while (iter < k_maxIters) - { - // Copy simplex so we can identify duplicates. - saveCount = simplex.m_count; - for (int32 i = 0; i < saveCount; ++i) - { - saveA[i] = vertices[i].indexA; - saveB[i] = vertices[i].indexB; - } - - switch (simplex.m_count) - { - case 1: - break; - - case 2: - simplex.Solve2(); - break; - - case 3: - simplex.Solve3(); - break; - - default: - b2Assert(false); - } - - // If we have 3 points, then the origin is in the corresponding triangle. - if (simplex.m_count == 3) - { - break; - } - - // Get search direction. - b2Vec2 d = simplex.GetSearchDirection(); - - // Ensure the search direction is numerically fit. - if (d.LengthSquared() < b2_epsilon * b2_epsilon) - { - // The origin is probably contained by a line segment - // or triangle. Thus the shapes are overlapped. - - // We can't return zero here even though there may be overlap. - // In case the simplex is a point, segment, or triangle it is difficult - // to determine if the origin is contained in the CSO or very close to it. - break; - } - - // Compute a tentative new simplex vertex using support points. - b2SimplexVertex* vertex = vertices + simplex.m_count; - vertex->indexA = proxyA->GetSupport(b2MulT(transformA.q, -d)); - vertex->wA = b2Mul(transformA, proxyA->GetVertex(vertex->indexA)); - vertex->indexB = proxyB->GetSupport(b2MulT(transformB.q, d)); - vertex->wB = b2Mul(transformB, proxyB->GetVertex(vertex->indexB)); - vertex->w = vertex->wB - vertex->wA; - - // Iteration count is equated to the number of support point calls. - ++iter; - ++b2_gjkIters; - - // Check for duplicate support points. This is the main termination criteria. - bool duplicate = false; - for (int32 i = 0; i < saveCount; ++i) - { - if (vertex->indexA == saveA[i] && vertex->indexB == saveB[i]) - { - duplicate = true; - break; - } - } - - // If we found a duplicate support point we must exit to avoid cycling. - if (duplicate) - { - break; - } - - // New vertex is ok and needed. - ++simplex.m_count; - } - - b2_gjkMaxIters = b2Max(b2_gjkMaxIters, iter); - - // Prepare output. - simplex.GetWitnessPoints(&output->pointA, &output->pointB); - output->distance = b2Distance(output->pointA, output->pointB); - output->iterations = iter; - - // Cache the simplex. - simplex.WriteCache(cache); - - // Apply radii if requested - if (input->useRadii) - { - if (output->distance < b2_epsilon) - { - // Shapes are too close to safely compute normal - b2Vec2 p = 0.5f * (output->pointA + output->pointB); - output->pointA = p; - output->pointB = p; - output->distance = 0.0f; - } - else - { - // Keep closest points on perimeter even if overlapped, this way - // the points move smoothly. - float rA = proxyA->m_radius; - float rB = proxyB->m_radius; - b2Vec2 normal = output->pointB - output->pointA; - normal.Normalize(); - output->distance = b2Max(0.0f, output->distance - rA - rB); - output->pointA += rA * normal; - output->pointB -= rB * normal; - } - } -} - -// GJK-raycast -// Algorithm by Gino van den Bergen. -// "Smooth Mesh Contacts with GJK" in Game Physics Pearls. 2010 -bool b2ShapeCast(b2ShapeCastOutput * output, const b2ShapeCastInput * input) -{ - output->iterations = 0; - output->lambda = 1.0f; - output->normal.SetZero(); - output->point.SetZero(); - - const b2DistanceProxy* proxyA = &input->proxyA; - const b2DistanceProxy* proxyB = &input->proxyB; - - float radiusA = b2Max(proxyA->m_radius, b2_polygonRadius); - float radiusB = b2Max(proxyB->m_radius, b2_polygonRadius); - float radius = radiusA + radiusB; - - b2Transform xfA = input->transformA; - b2Transform xfB = input->transformB; - - b2Vec2 r = input->translationB; - b2Vec2 n(0.0f, 0.0f); - float lambda = 0.0f; - - // Initial simplex - b2Simplex simplex; - simplex.m_count = 0; - - // Get simplex vertices as an array. - b2SimplexVertex* vertices = &simplex.m_v1; - - // Get support point in -r direction - int32 indexA = proxyA->GetSupport(b2MulT(xfA.q, -r)); - b2Vec2 wA = b2Mul(xfA, proxyA->GetVertex(indexA)); - int32 indexB = proxyB->GetSupport(b2MulT(xfB.q, r)); - b2Vec2 wB = b2Mul(xfB, proxyB->GetVertex(indexB)); - b2Vec2 v = wA - wB; - - // Sigma is the target distance between polygons - float sigma = b2Max(b2_polygonRadius, radius - b2_polygonRadius); - const float tolerance = 0.5f * b2_linearSlop; - - // Main iteration loop. - const int32 k_maxIters = 20; - int32 iter = 0; - while (iter < k_maxIters && v.Length() - sigma > tolerance) - { - b2Assert(simplex.m_count < 3); - - output->iterations += 1; - - // Support in direction -v (A - B) - indexA = proxyA->GetSupport(b2MulT(xfA.q, -v)); - wA = b2Mul(xfA, proxyA->GetVertex(indexA)); - indexB = proxyB->GetSupport(b2MulT(xfB.q, v)); - wB = b2Mul(xfB, proxyB->GetVertex(indexB)); - b2Vec2 p = wA - wB; - - // -v is a normal at p - v.Normalize(); - - // Intersect ray with plane - float vp = b2Dot(v, p); - float vr = b2Dot(v, r); - if (vp - sigma > lambda * vr) - { - if (vr <= 0.0f) - { - return false; - } - - lambda = (vp - sigma) / vr; - if (lambda > 1.0f) - { - return false; - } - - n = -v; - simplex.m_count = 0; - } - - // Reverse simplex since it works with B - A. - // Shift by lambda * r because we want the closest point to the current clip point. - // Note that the support point p is not shifted because we want the plane equation - // to be formed in unshifted space. - b2SimplexVertex* vertex = vertices + simplex.m_count; - vertex->indexA = indexB; - vertex->wA = wB + lambda * r; - vertex->indexB = indexA; - vertex->wB = wA; - vertex->w = vertex->wB - vertex->wA; - vertex->a = 1.0f; - simplex.m_count += 1; - - switch (simplex.m_count) - { - case 1: - break; - - case 2: - simplex.Solve2(); - break; - - case 3: - simplex.Solve3(); - break; - - default: - b2Assert(false); - } - - // If we have 3 points, then the origin is in the corresponding triangle. - if (simplex.m_count == 3) - { - // Overlap - return false; - } - - // Get search direction. - v = simplex.GetClosestPoint(); - - // Iteration count is equated to the number of support point calls. - ++iter; - } - - if (iter == 0) - { - // Initial overlap - return false; - } - - // Prepare output. - b2Vec2 pointA, pointB; - simplex.GetWitnessPoints(&pointB, &pointA); - - if (v.LengthSquared() > 0.0f) - { - n = -v; - n.Normalize(); - } - - output->point = pointA + radiusA * n; - output->normal = n; - output->lambda = lambda; - output->iterations = iter; - return true; -} diff --git a/3rdparty/box2d/src/collision/b2_dynamic_tree.cpp b/3rdparty/box2d/src/collision/b2_dynamic_tree.cpp deleted file mode 100644 index 55a9d3246ad6..000000000000 --- a/3rdparty/box2d/src/collision/b2_dynamic_tree.cpp +++ /dev/null @@ -1,801 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#include "box2d/b2_dynamic_tree.h" -#include - -b2DynamicTree::b2DynamicTree() -{ - m_root = b2_nullNode; - - m_nodeCapacity = 16; - m_nodeCount = 0; - m_nodes = (b2TreeNode*)b2Alloc(m_nodeCapacity * sizeof(b2TreeNode)); - memset(m_nodes, 0, m_nodeCapacity * sizeof(b2TreeNode)); - - // Build a linked list for the free list. - for (int32 i = 0; i < m_nodeCapacity - 1; ++i) - { - m_nodes[i].next = i + 1; - m_nodes[i].height = -1; - } - m_nodes[m_nodeCapacity-1].next = b2_nullNode; - m_nodes[m_nodeCapacity-1].height = -1; - m_freeList = 0; - - m_insertionCount = 0; -} - -b2DynamicTree::~b2DynamicTree() -{ - // This frees the entire tree in one shot. - b2Free(m_nodes); -} - -// Allocate a node from the pool. Grow the pool if necessary. -int32 b2DynamicTree::AllocateNode() -{ - // Expand the node pool as needed. - if (m_freeList == b2_nullNode) - { - b2Assert(m_nodeCount == m_nodeCapacity); - - // The free list is empty. Rebuild a bigger pool. - b2TreeNode* oldNodes = m_nodes; - m_nodeCapacity *= 2; - m_nodes = (b2TreeNode*)b2Alloc(m_nodeCapacity * sizeof(b2TreeNode)); - memcpy(m_nodes, oldNodes, m_nodeCount * sizeof(b2TreeNode)); - b2Free(oldNodes); - - // Build a linked list for the free list. The parent - // pointer becomes the "next" pointer. - for (int32 i = m_nodeCount; i < m_nodeCapacity - 1; ++i) - { - m_nodes[i].next = i + 1; - m_nodes[i].height = -1; - } - m_nodes[m_nodeCapacity-1].next = b2_nullNode; - m_nodes[m_nodeCapacity-1].height = -1; - m_freeList = m_nodeCount; - } - - // Peel a node off the free list. - int32 nodeId = m_freeList; - m_freeList = m_nodes[nodeId].next; - m_nodes[nodeId].parent = b2_nullNode; - m_nodes[nodeId].child1 = b2_nullNode; - m_nodes[nodeId].child2 = b2_nullNode; - m_nodes[nodeId].height = 0; - m_nodes[nodeId].userData = nullptr; - m_nodes[nodeId].moved = false; - ++m_nodeCount; - return nodeId; -} - -// Return a node to the pool. -void b2DynamicTree::FreeNode(int32 nodeId) -{ - b2Assert(0 <= nodeId && nodeId < m_nodeCapacity); - b2Assert(0 < m_nodeCount); - m_nodes[nodeId].next = m_freeList; - m_nodes[nodeId].height = -1; - m_freeList = nodeId; - --m_nodeCount; -} - -// Create a proxy in the tree as a leaf node. We return the index -// of the node instead of a pointer so that we can grow -// the node pool. -int32 b2DynamicTree::CreateProxy(const b2AABB& aabb, void* userData) -{ - int32 proxyId = AllocateNode(); - - // Fatten the aabb. - b2Vec2 r(b2_aabbExtension, b2_aabbExtension); - m_nodes[proxyId].aabb.lowerBound = aabb.lowerBound - r; - m_nodes[proxyId].aabb.upperBound = aabb.upperBound + r; - m_nodes[proxyId].userData = userData; - m_nodes[proxyId].height = 0; - m_nodes[proxyId].moved = true; - - InsertLeaf(proxyId); - - return proxyId; -} - -void b2DynamicTree::DestroyProxy(int32 proxyId) -{ - b2Assert(0 <= proxyId && proxyId < m_nodeCapacity); - b2Assert(m_nodes[proxyId].IsLeaf()); - - RemoveLeaf(proxyId); - FreeNode(proxyId); -} - -bool b2DynamicTree::MoveProxy(int32 proxyId, const b2AABB& aabb, const b2Vec2& displacement) -{ - b2Assert(0 <= proxyId && proxyId < m_nodeCapacity); - - b2Assert(m_nodes[proxyId].IsLeaf()); - - // Extend AABB - b2AABB fatAABB; - b2Vec2 r(b2_aabbExtension, b2_aabbExtension); - fatAABB.lowerBound = aabb.lowerBound - r; - fatAABB.upperBound = aabb.upperBound + r; - - // Predict AABB movement - b2Vec2 d = b2_aabbMultiplier * displacement; - - if (d.x < 0.0f) - { - fatAABB.lowerBound.x += d.x; - } - else - { - fatAABB.upperBound.x += d.x; - } - - if (d.y < 0.0f) - { - fatAABB.lowerBound.y += d.y; - } - else - { - fatAABB.upperBound.y += d.y; - } - - const b2AABB& treeAABB = m_nodes[proxyId].aabb; - if (treeAABB.Contains(aabb)) - { - // The tree AABB still contains the object, but it might be too large. - // Perhaps the object was moving fast but has since gone to sleep. - // The huge AABB is larger than the new fat AABB. - b2AABB hugeAABB; - hugeAABB.lowerBound = fatAABB.lowerBound - 4.0f * r; - hugeAABB.upperBound = fatAABB.upperBound + 4.0f * r; - - if (hugeAABB.Contains(treeAABB)) - { - // The tree AABB contains the object AABB and the tree AABB is - // not too large. No tree update needed. - return false; - } - - // Otherwise the tree AABB is huge and needs to be shrunk - } - - RemoveLeaf(proxyId); - - m_nodes[proxyId].aabb = fatAABB; - - InsertLeaf(proxyId); - - m_nodes[proxyId].moved = true; - - return true; -} - -void b2DynamicTree::InsertLeaf(int32 leaf) -{ - ++m_insertionCount; - - if (m_root == b2_nullNode) - { - m_root = leaf; - m_nodes[m_root].parent = b2_nullNode; - return; - } - - // Find the best sibling for this node - b2AABB leafAABB = m_nodes[leaf].aabb; - int32 index = m_root; - while (m_nodes[index].IsLeaf() == false) - { - int32 child1 = m_nodes[index].child1; - int32 child2 = m_nodes[index].child2; - - float area = m_nodes[index].aabb.GetPerimeter(); - - b2AABB combinedAABB; - combinedAABB.Combine(m_nodes[index].aabb, leafAABB); - float combinedArea = combinedAABB.GetPerimeter(); - - // Cost of creating a new parent for this node and the new leaf - float cost = 2.0f * combinedArea; - - // Minimum cost of pushing the leaf further down the tree - float inheritanceCost = 2.0f * (combinedArea - area); - - // Cost of descending into child1 - float cost1; - if (m_nodes[child1].IsLeaf()) - { - b2AABB aabb; - aabb.Combine(leafAABB, m_nodes[child1].aabb); - cost1 = aabb.GetPerimeter() + inheritanceCost; - } - else - { - b2AABB aabb; - aabb.Combine(leafAABB, m_nodes[child1].aabb); - float oldArea = m_nodes[child1].aabb.GetPerimeter(); - float newArea = aabb.GetPerimeter(); - cost1 = (newArea - oldArea) + inheritanceCost; - } - - // Cost of descending into child2 - float cost2; - if (m_nodes[child2].IsLeaf()) - { - b2AABB aabb; - aabb.Combine(leafAABB, m_nodes[child2].aabb); - cost2 = aabb.GetPerimeter() + inheritanceCost; - } - else - { - b2AABB aabb; - aabb.Combine(leafAABB, m_nodes[child2].aabb); - float oldArea = m_nodes[child2].aabb.GetPerimeter(); - float newArea = aabb.GetPerimeter(); - cost2 = newArea - oldArea + inheritanceCost; - } - - // Descend according to the minimum cost. - if (cost < cost1 && cost < cost2) - { - break; - } - - // Descend - if (cost1 < cost2) - { - index = child1; - } - else - { - index = child2; - } - } - - int32 sibling = index; - - // Create a new parent. - int32 oldParent = m_nodes[sibling].parent; - int32 newParent = AllocateNode(); - m_nodes[newParent].parent = oldParent; - m_nodes[newParent].userData = nullptr; - m_nodes[newParent].aabb.Combine(leafAABB, m_nodes[sibling].aabb); - m_nodes[newParent].height = m_nodes[sibling].height + 1; - - if (oldParent != b2_nullNode) - { - // The sibling was not the root. - if (m_nodes[oldParent].child1 == sibling) - { - m_nodes[oldParent].child1 = newParent; - } - else - { - m_nodes[oldParent].child2 = newParent; - } - - m_nodes[newParent].child1 = sibling; - m_nodes[newParent].child2 = leaf; - m_nodes[sibling].parent = newParent; - m_nodes[leaf].parent = newParent; - } - else - { - // The sibling was the root. - m_nodes[newParent].child1 = sibling; - m_nodes[newParent].child2 = leaf; - m_nodes[sibling].parent = newParent; - m_nodes[leaf].parent = newParent; - m_root = newParent; - } - - // Walk back up the tree fixing heights and AABBs - index = m_nodes[leaf].parent; - while (index != b2_nullNode) - { - index = Balance(index); - - int32 child1 = m_nodes[index].child1; - int32 child2 = m_nodes[index].child2; - - b2Assert(child1 != b2_nullNode); - b2Assert(child2 != b2_nullNode); - - m_nodes[index].height = 1 + b2Max(m_nodes[child1].height, m_nodes[child2].height); - m_nodes[index].aabb.Combine(m_nodes[child1].aabb, m_nodes[child2].aabb); - - index = m_nodes[index].parent; - } - - //Validate(); -} - -void b2DynamicTree::RemoveLeaf(int32 leaf) -{ - if (leaf == m_root) - { - m_root = b2_nullNode; - return; - } - - int32 parent = m_nodes[leaf].parent; - int32 grandParent = m_nodes[parent].parent; - int32 sibling; - if (m_nodes[parent].child1 == leaf) - { - sibling = m_nodes[parent].child2; - } - else - { - sibling = m_nodes[parent].child1; - } - - if (grandParent != b2_nullNode) - { - // Destroy parent and connect sibling to grandParent. - if (m_nodes[grandParent].child1 == parent) - { - m_nodes[grandParent].child1 = sibling; - } - else - { - m_nodes[grandParent].child2 = sibling; - } - m_nodes[sibling].parent = grandParent; - FreeNode(parent); - - // Adjust ancestor bounds. - int32 index = grandParent; - while (index != b2_nullNode) - { - index = Balance(index); - - int32 child1 = m_nodes[index].child1; - int32 child2 = m_nodes[index].child2; - - m_nodes[index].aabb.Combine(m_nodes[child1].aabb, m_nodes[child2].aabb); - m_nodes[index].height = 1 + b2Max(m_nodes[child1].height, m_nodes[child2].height); - - index = m_nodes[index].parent; - } - } - else - { - m_root = sibling; - m_nodes[sibling].parent = b2_nullNode; - FreeNode(parent); - } - - //Validate(); -} - -// Perform a left or right rotation if node A is imbalanced. -// Returns the new root index. -int32 b2DynamicTree::Balance(int32 iA) -{ - b2Assert(iA != b2_nullNode); - - b2TreeNode* A = m_nodes + iA; - if (A->IsLeaf() || A->height < 2) - { - return iA; - } - - int32 iB = A->child1; - int32 iC = A->child2; - b2Assert(0 <= iB && iB < m_nodeCapacity); - b2Assert(0 <= iC && iC < m_nodeCapacity); - - b2TreeNode* B = m_nodes + iB; - b2TreeNode* C = m_nodes + iC; - - int32 balance = C->height - B->height; - - // Rotate C up - if (balance > 1) - { - int32 iF = C->child1; - int32 iG = C->child2; - b2TreeNode* F = m_nodes + iF; - b2TreeNode* G = m_nodes + iG; - b2Assert(0 <= iF && iF < m_nodeCapacity); - b2Assert(0 <= iG && iG < m_nodeCapacity); - - // Swap A and C - C->child1 = iA; - C->parent = A->parent; - A->parent = iC; - - // A's old parent should point to C - if (C->parent != b2_nullNode) - { - if (m_nodes[C->parent].child1 == iA) - { - m_nodes[C->parent].child1 = iC; - } - else - { - b2Assert(m_nodes[C->parent].child2 == iA); - m_nodes[C->parent].child2 = iC; - } - } - else - { - m_root = iC; - } - - // Rotate - if (F->height > G->height) - { - C->child2 = iF; - A->child2 = iG; - G->parent = iA; - A->aabb.Combine(B->aabb, G->aabb); - C->aabb.Combine(A->aabb, F->aabb); - - A->height = 1 + b2Max(B->height, G->height); - C->height = 1 + b2Max(A->height, F->height); - } - else - { - C->child2 = iG; - A->child2 = iF; - F->parent = iA; - A->aabb.Combine(B->aabb, F->aabb); - C->aabb.Combine(A->aabb, G->aabb); - - A->height = 1 + b2Max(B->height, F->height); - C->height = 1 + b2Max(A->height, G->height); - } - - return iC; - } - - // Rotate B up - if (balance < -1) - { - int32 iD = B->child1; - int32 iE = B->child2; - b2TreeNode* D = m_nodes + iD; - b2TreeNode* E = m_nodes + iE; - b2Assert(0 <= iD && iD < m_nodeCapacity); - b2Assert(0 <= iE && iE < m_nodeCapacity); - - // Swap A and B - B->child1 = iA; - B->parent = A->parent; - A->parent = iB; - - // A's old parent should point to B - if (B->parent != b2_nullNode) - { - if (m_nodes[B->parent].child1 == iA) - { - m_nodes[B->parent].child1 = iB; - } - else - { - b2Assert(m_nodes[B->parent].child2 == iA); - m_nodes[B->parent].child2 = iB; - } - } - else - { - m_root = iB; - } - - // Rotate - if (D->height > E->height) - { - B->child2 = iD; - A->child1 = iE; - E->parent = iA; - A->aabb.Combine(C->aabb, E->aabb); - B->aabb.Combine(A->aabb, D->aabb); - - A->height = 1 + b2Max(C->height, E->height); - B->height = 1 + b2Max(A->height, D->height); - } - else - { - B->child2 = iE; - A->child1 = iD; - D->parent = iA; - A->aabb.Combine(C->aabb, D->aabb); - B->aabb.Combine(A->aabb, E->aabb); - - A->height = 1 + b2Max(C->height, D->height); - B->height = 1 + b2Max(A->height, E->height); - } - - return iB; - } - - return iA; -} - -int32 b2DynamicTree::GetHeight() const -{ - if (m_root == b2_nullNode) - { - return 0; - } - - return m_nodes[m_root].height; -} - -// -float b2DynamicTree::GetAreaRatio() const -{ - if (m_root == b2_nullNode) - { - return 0.0f; - } - - const b2TreeNode* root = m_nodes + m_root; - float rootArea = root->aabb.GetPerimeter(); - - float totalArea = 0.0f; - for (int32 i = 0; i < m_nodeCapacity; ++i) - { - const b2TreeNode* node = m_nodes + i; - if (node->height < 0) - { - // Free node in pool - continue; - } - - totalArea += node->aabb.GetPerimeter(); - } - - return totalArea / rootArea; -} - -// Compute the height of a sub-tree. -int32 b2DynamicTree::ComputeHeight(int32 nodeId) const -{ - b2Assert(0 <= nodeId && nodeId < m_nodeCapacity); - b2TreeNode* node = m_nodes + nodeId; - - if (node->IsLeaf()) - { - return 0; - } - - int32 height1 = ComputeHeight(node->child1); - int32 height2 = ComputeHeight(node->child2); - return 1 + b2Max(height1, height2); -} - -int32 b2DynamicTree::ComputeHeight() const -{ - int32 height = ComputeHeight(m_root); - return height; -} - -void b2DynamicTree::ValidateStructure(int32 index) const -{ - if (index == b2_nullNode) - { - return; - } - - if (index == m_root) - { - b2Assert(m_nodes[index].parent == b2_nullNode); - } - - const b2TreeNode* node = m_nodes + index; - - int32 child1 = node->child1; - int32 child2 = node->child2; - - if (node->IsLeaf()) - { - b2Assert(child1 == b2_nullNode); - b2Assert(child2 == b2_nullNode); - b2Assert(node->height == 0); - return; - } - - b2Assert(0 <= child1 && child1 < m_nodeCapacity); - b2Assert(0 <= child2 && child2 < m_nodeCapacity); - - b2Assert(m_nodes[child1].parent == index); - b2Assert(m_nodes[child2].parent == index); - - ValidateStructure(child1); - ValidateStructure(child2); -} - -void b2DynamicTree::ValidateMetrics(int32 index) const -{ - if (index == b2_nullNode) - { - return; - } - - const b2TreeNode* node = m_nodes + index; - - int32 child1 = node->child1; - int32 child2 = node->child2; - - if (node->IsLeaf()) - { - b2Assert(child1 == b2_nullNode); - b2Assert(child2 == b2_nullNode); - b2Assert(node->height == 0); - return; - } - - b2Assert(0 <= child1 && child1 < m_nodeCapacity); - b2Assert(0 <= child2 && child2 < m_nodeCapacity); - - int32 height1 = m_nodes[child1].height; - int32 height2 = m_nodes[child2].height; - int32 height; - height = 1 + b2Max(height1, height2); - b2Assert(node->height == height); - - b2AABB aabb; - aabb.Combine(m_nodes[child1].aabb, m_nodes[child2].aabb); - - b2Assert(aabb.lowerBound == node->aabb.lowerBound); - b2Assert(aabb.upperBound == node->aabb.upperBound); - - ValidateMetrics(child1); - ValidateMetrics(child2); -} - -void b2DynamicTree::Validate() const -{ -#if defined(b2DEBUG) - ValidateStructure(m_root); - ValidateMetrics(m_root); - - int32 freeCount = 0; - int32 freeIndex = m_freeList; - while (freeIndex != b2_nullNode) - { - b2Assert(0 <= freeIndex && freeIndex < m_nodeCapacity); - freeIndex = m_nodes[freeIndex].next; - ++freeCount; - } - - b2Assert(GetHeight() == ComputeHeight()); - - b2Assert(m_nodeCount + freeCount == m_nodeCapacity); -#endif -} - -int32 b2DynamicTree::GetMaxBalance() const -{ - int32 maxBalance = 0; - for (int32 i = 0; i < m_nodeCapacity; ++i) - { - const b2TreeNode* node = m_nodes + i; - if (node->height <= 1) - { - continue; - } - - b2Assert(node->IsLeaf() == false); - - int32 child1 = node->child1; - int32 child2 = node->child2; - int32 balance = b2Abs(m_nodes[child2].height - m_nodes[child1].height); - maxBalance = b2Max(maxBalance, balance); - } - - return maxBalance; -} - -void b2DynamicTree::RebuildBottomUp() -{ - int32* nodes = (int32*)b2Alloc(m_nodeCount * sizeof(int32)); - int32 count = 0; - - // Build array of leaves. Free the rest. - for (int32 i = 0; i < m_nodeCapacity; ++i) - { - if (m_nodes[i].height < 0) - { - // free node in pool - continue; - } - - if (m_nodes[i].IsLeaf()) - { - m_nodes[i].parent = b2_nullNode; - nodes[count] = i; - ++count; - } - else - { - FreeNode(i); - } - } - - while (count > 1) - { - float minCost = b2_maxFloat; - int32 iMin = -1, jMin = -1; - for (int32 i = 0; i < count; ++i) - { - b2AABB aabbi = m_nodes[nodes[i]].aabb; - - for (int32 j = i + 1; j < count; ++j) - { - b2AABB aabbj = m_nodes[nodes[j]].aabb; - b2AABB b; - b.Combine(aabbi, aabbj); - float cost = b.GetPerimeter(); - if (cost < minCost) - { - iMin = i; - jMin = j; - minCost = cost; - } - } - } - - int32 index1 = nodes[iMin]; - int32 index2 = nodes[jMin]; - b2TreeNode* child1 = m_nodes + index1; - b2TreeNode* child2 = m_nodes + index2; - - int32 parentIndex = AllocateNode(); - b2TreeNode* parent = m_nodes + parentIndex; - parent->child1 = index1; - parent->child2 = index2; - parent->height = 1 + b2Max(child1->height, child2->height); - parent->aabb.Combine(child1->aabb, child2->aabb); - parent->parent = b2_nullNode; - - child1->parent = parentIndex; - child2->parent = parentIndex; - - nodes[jMin] = nodes[count-1]; - nodes[iMin] = parentIndex; - --count; - } - - m_root = nodes[0]; - b2Free(nodes); - - Validate(); -} - -void b2DynamicTree::ShiftOrigin(const b2Vec2& newOrigin) -{ - // Build array of leaves. Free the rest. - for (int32 i = 0; i < m_nodeCapacity; ++i) - { - m_nodes[i].aabb.lowerBound -= newOrigin; - m_nodes[i].aabb.upperBound -= newOrigin; - } -} diff --git a/3rdparty/box2d/src/collision/b2_edge_shape.cpp b/3rdparty/box2d/src/collision/b2_edge_shape.cpp deleted file mode 100644 index 65f0606a6483..000000000000 --- a/3rdparty/box2d/src/collision/b2_edge_shape.cpp +++ /dev/null @@ -1,158 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "box2d/b2_edge_shape.h" -#include "box2d/b2_block_allocator.h" -#include - -void b2EdgeShape::SetOneSided(const b2Vec2& v0, const b2Vec2& v1, const b2Vec2& v2, const b2Vec2& v3) -{ - m_vertex0 = v0; - m_vertex1 = v1; - m_vertex2 = v2; - m_vertex3 = v3; - m_oneSided = true; -} - -void b2EdgeShape::SetTwoSided(const b2Vec2& v1, const b2Vec2& v2) -{ - m_vertex1 = v1; - m_vertex2 = v2; - m_oneSided = false; -} - -b2Shape* b2EdgeShape::Clone(b2BlockAllocator* allocator) const -{ - void* mem = allocator->Allocate(sizeof(b2EdgeShape)); - b2EdgeShape* clone = new (mem) b2EdgeShape; - *clone = *this; - return clone; -} - -int32 b2EdgeShape::GetChildCount() const -{ - return 1; -} - -bool b2EdgeShape::TestPoint(const b2Transform& xf, const b2Vec2& p) const -{ - B2_NOT_USED(xf); - B2_NOT_USED(p); - return false; -} - -// p = p1 + t * d -// v = v1 + s * e -// p1 + t * d = v1 + s * e -// s * e - t * d = p1 - v1 -bool b2EdgeShape::RayCast(b2RayCastOutput* output, const b2RayCastInput& input, - const b2Transform& xf, int32 childIndex) const -{ - B2_NOT_USED(childIndex); - - // Put the ray into the edge's frame of reference. - b2Vec2 p1 = b2MulT(xf.q, input.p1 - xf.p); - b2Vec2 p2 = b2MulT(xf.q, input.p2 - xf.p); - b2Vec2 d = p2 - p1; - - b2Vec2 v1 = m_vertex1; - b2Vec2 v2 = m_vertex2; - b2Vec2 e = v2 - v1; - - // Normal points to the right, looking from v1 at v2 - b2Vec2 normal(e.y, -e.x); - normal.Normalize(); - - // q = p1 + t * d - // dot(normal, q - v1) = 0 - // dot(normal, p1 - v1) + t * dot(normal, d) = 0 - float numerator = b2Dot(normal, v1 - p1); - if (m_oneSided && numerator > 0.0f) - { - return false; - } - - float denominator = b2Dot(normal, d); - - if (denominator == 0.0f) - { - return false; - } - - float t = numerator / denominator; - if (t < 0.0f || input.maxFraction < t) - { - return false; - } - - b2Vec2 q = p1 + t * d; - - // q = v1 + s * r - // s = dot(q - v1, r) / dot(r, r) - b2Vec2 r = v2 - v1; - float rr = b2Dot(r, r); - if (rr == 0.0f) - { - return false; - } - - float s = b2Dot(q - v1, r) / rr; - if (s < 0.0f || 1.0f < s) - { - return false; - } - - output->fraction = t; - if (numerator > 0.0f) - { - output->normal = -b2Mul(xf.q, normal); - } - else - { - output->normal = b2Mul(xf.q, normal); - } - return true; -} - -void b2EdgeShape::ComputeAABB(b2AABB* aabb, const b2Transform& xf, int32 childIndex) const -{ - B2_NOT_USED(childIndex); - - b2Vec2 v1 = b2Mul(xf, m_vertex1); - b2Vec2 v2 = b2Mul(xf, m_vertex2); - - b2Vec2 lower = b2Min(v1, v2); - b2Vec2 upper = b2Max(v1, v2); - - b2Vec2 r(m_radius, m_radius); - aabb->lowerBound = lower - r; - aabb->upperBound = upper + r; -} - -void b2EdgeShape::ComputeMass(b2MassData* massData, float density) const -{ - B2_NOT_USED(density); - - massData->mass = 0.0f; - massData->center = 0.5f * (m_vertex1 + m_vertex2); - massData->I = 0.0f; -} diff --git a/3rdparty/box2d/src/collision/b2_polygon_shape.cpp b/3rdparty/box2d/src/collision/b2_polygon_shape.cpp deleted file mode 100644 index 165919b46fa7..000000000000 --- a/3rdparty/box2d/src/collision/b2_polygon_shape.cpp +++ /dev/null @@ -1,366 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "box2d/b2_polygon_shape.h" -#include "box2d/b2_block_allocator.h" - -#include - -b2PolygonShape::b2PolygonShape() -{ - m_type = e_polygon; - m_radius = b2_polygonRadius; - m_count = 0; - m_centroid.SetZero(); -} - -b2Shape* b2PolygonShape::Clone(b2BlockAllocator* allocator) const -{ - void* mem = allocator->Allocate(sizeof(b2PolygonShape)); - b2PolygonShape* clone = new (mem) b2PolygonShape; - *clone = *this; - return clone; -} - -void b2PolygonShape::SetAsBox(float hx, float hy) -{ - m_count = 4; - m_vertices[0].Set(-hx, -hy); - m_vertices[1].Set( hx, -hy); - m_vertices[2].Set( hx, hy); - m_vertices[3].Set(-hx, hy); - m_normals[0].Set(0.0f, -1.0f); - m_normals[1].Set(1.0f, 0.0f); - m_normals[2].Set(0.0f, 1.0f); - m_normals[3].Set(-1.0f, 0.0f); - m_centroid.SetZero(); -} - -void b2PolygonShape::SetAsBox(float hx, float hy, const b2Vec2& center, float angle) -{ - m_count = 4; - m_vertices[0].Set(-hx, -hy); - m_vertices[1].Set( hx, -hy); - m_vertices[2].Set( hx, hy); - m_vertices[3].Set(-hx, hy); - m_normals[0].Set(0.0f, -1.0f); - m_normals[1].Set(1.0f, 0.0f); - m_normals[2].Set(0.0f, 1.0f); - m_normals[3].Set(-1.0f, 0.0f); - m_centroid = center; - - b2Transform xf; - xf.p = center; - xf.q.Set(angle); - - // Transform vertices and normals. - for (int32 i = 0; i < m_count; ++i) - { - m_vertices[i] = b2Mul(xf, m_vertices[i]); - m_normals[i] = b2Mul(xf.q, m_normals[i]); - } -} - -int32 b2PolygonShape::GetChildCount() const -{ - return 1; -} - -static b2Vec2 ComputeCentroid(const b2Vec2* vs, int32 count) -{ - b2Assert(count >= 3); - - b2Vec2 c(0.0f, 0.0f); - float area = 0.0f; - - // Get a reference point for forming triangles. - // Use the first vertex to reduce round-off errors. - b2Vec2 s = vs[0]; - - const float inv3 = 1.0f / 3.0f; - - for (int32 i = 0; i < count; ++i) - { - // Triangle vertices. - b2Vec2 p1 = vs[0] - s; - b2Vec2 p2 = vs[i] - s; - b2Vec2 p3 = i + 1 < count ? vs[i+1] - s : vs[0] - s; - - b2Vec2 e1 = p2 - p1; - b2Vec2 e2 = p3 - p1; - - float D = b2Cross(e1, e2); - - float triangleArea = 0.5f * D; - area += triangleArea; - - // Area weighted centroid - c += triangleArea * inv3 * (p1 + p2 + p3); - } - - // Centroid - b2Assert(area > b2_epsilon); - c = (1.0f / area) * c + s; - return c; -} - -bool b2PolygonShape::Set(const b2Vec2* vertices, int32 count) -{ - b2Hull hull = b2ComputeHull(vertices, count); - - if (hull.count < 3) - { - return false; - } - - Set(hull); - - return true; -} - -void b2PolygonShape::Set(const b2Hull& hull) -{ - b2Assert(hull.count >= 3); - - m_count = hull.count; - - // Copy vertices - for (int32 i = 0; i < hull.count; ++i) - { - m_vertices[i] = hull.points[i]; - } - - // Compute normals. Ensure the edges have non-zero length. - for (int32 i = 0; i < m_count; ++i) - { - int32 i1 = i; - int32 i2 = i + 1 < m_count ? i + 1 : 0; - b2Vec2 edge = m_vertices[i2] - m_vertices[i1]; - b2Assert(edge.LengthSquared() > b2_epsilon * b2_epsilon); - m_normals[i] = b2Cross(edge, 1.0f); - m_normals[i].Normalize(); - } - - // Compute the polygon centroid. - m_centroid = ComputeCentroid(m_vertices, m_count); -} - -bool b2PolygonShape::TestPoint(const b2Transform& xf, const b2Vec2& p) const -{ - b2Vec2 pLocal = b2MulT(xf.q, p - xf.p); - - for (int32 i = 0; i < m_count; ++i) - { - float dot = b2Dot(m_normals[i], pLocal - m_vertices[i]); - if (dot > 0.0f) - { - return false; - } - } - - return true; -} - -bool b2PolygonShape::RayCast(b2RayCastOutput* output, const b2RayCastInput& input, - const b2Transform& xf, int32 childIndex) const -{ - B2_NOT_USED(childIndex); - - // Put the ray into the polygon's frame of reference. - b2Vec2 p1 = b2MulT(xf.q, input.p1 - xf.p); - b2Vec2 p2 = b2MulT(xf.q, input.p2 - xf.p); - b2Vec2 d = p2 - p1; - - float lower = 0.0f, upper = input.maxFraction; - - int32 index = -1; - - for (int32 i = 0; i < m_count; ++i) - { - // p = p1 + a * d - // dot(normal, p - v) = 0 - // dot(normal, p1 - v) + a * dot(normal, d) = 0 - float numerator = b2Dot(m_normals[i], m_vertices[i] - p1); - float denominator = b2Dot(m_normals[i], d); - - if (denominator == 0.0f) - { - if (numerator < 0.0f) - { - return false; - } - } - else - { - // Note: we want this predicate without division: - // lower < numerator / denominator, where denominator < 0 - // Since denominator < 0, we have to flip the inequality: - // lower < numerator / denominator <==> denominator * lower > numerator. - if (denominator < 0.0f && numerator < lower * denominator) - { - // Increase lower. - // The segment enters this half-space. - lower = numerator / denominator; - index = i; - } - else if (denominator > 0.0f && numerator < upper * denominator) - { - // Decrease upper. - // The segment exits this half-space. - upper = numerator / denominator; - } - } - - // The use of epsilon here causes the assert on lower to trip - // in some cases. Apparently the use of epsilon was to make edge - // shapes work, but now those are handled separately. - //if (upper < lower - b2_epsilon) - if (upper < lower) - { - return false; - } - } - - b2Assert(0.0f <= lower && lower <= input.maxFraction); - - if (index >= 0) - { - output->fraction = lower; - output->normal = b2Mul(xf.q, m_normals[index]); - return true; - } - - return false; -} - -void b2PolygonShape::ComputeAABB(b2AABB* aabb, const b2Transform& xf, int32 childIndex) const -{ - B2_NOT_USED(childIndex); - - b2Vec2 lower = b2Mul(xf, m_vertices[0]); - b2Vec2 upper = lower; - - for (int32 i = 1; i < m_count; ++i) - { - b2Vec2 v = b2Mul(xf, m_vertices[i]); - lower = b2Min(lower, v); - upper = b2Max(upper, v); - } - - b2Vec2 r(m_radius, m_radius); - aabb->lowerBound = lower - r; - aabb->upperBound = upper + r; -} - -void b2PolygonShape::ComputeMass(b2MassData* massData, float density) const -{ - // Polygon mass, centroid, and inertia. - // Let rho be the polygon density in mass per unit area. - // Then: - // mass = rho * int(dA) - // centroid.x = (1/mass) * rho * int(x * dA) - // centroid.y = (1/mass) * rho * int(y * dA) - // I = rho * int((x*x + y*y) * dA) - // - // We can compute these integrals by summing all the integrals - // for each triangle of the polygon. To evaluate the integral - // for a single triangle, we make a change of variables to - // the (u,v) coordinates of the triangle: - // x = x0 + e1x * u + e2x * v - // y = y0 + e1y * u + e2y * v - // where 0 <= u && 0 <= v && u + v <= 1. - // - // We integrate u from [0,1-v] and then v from [0,1]. - // We also need to use the Jacobian of the transformation: - // D = cross(e1, e2) - // - // Simplification: triangle centroid = (1/3) * (p1 + p2 + p3) - // - // The rest of the derivation is handled by computer algebra. - - b2Assert(m_count >= 3); - - b2Vec2 center(0.0f, 0.0f); - float area = 0.0f; - float I = 0.0f; - - // Get a reference point for forming triangles. - // Use the first vertex to reduce round-off errors. - b2Vec2 s = m_vertices[0]; - - const float k_inv3 = 1.0f / 3.0f; - - for (int32 i = 0; i < m_count; ++i) - { - // Triangle vertices. - b2Vec2 e1 = m_vertices[i] - s; - b2Vec2 e2 = i + 1 < m_count ? m_vertices[i+1] - s : m_vertices[0] - s; - - float D = b2Cross(e1, e2); - - float triangleArea = 0.5f * D; - area += triangleArea; - - // Area weighted centroid - center += triangleArea * k_inv3 * (e1 + e2); - - float ex1 = e1.x, ey1 = e1.y; - float ex2 = e2.x, ey2 = e2.y; - - float intx2 = ex1*ex1 + ex2*ex1 + ex2*ex2; - float inty2 = ey1*ey1 + ey2*ey1 + ey2*ey2; - - I += (0.25f * k_inv3 * D) * (intx2 + inty2); - } - - // Total mass - massData->mass = density * area; - - // Center of mass - b2Assert(area > b2_epsilon); - center *= 1.0f / area; - massData->center = center + s; - - // Inertia tensor relative to the local origin (point s). - massData->I = density * I; - - // Shift to center of mass then to original body origin. - massData->I += massData->mass * (b2Dot(massData->center, massData->center) - b2Dot(center, center)); -} - -bool b2PolygonShape::Validate() const -{ - if (m_count < 3 || b2_maxPolygonVertices < m_count) - { - return false; - } - - b2Hull hull; - for (int32 i = 0; i < m_count; ++i) - { - hull.points[i] = m_vertices[i]; - } - - hull.count = m_count; - - return b2ValidateHull(hull); -} diff --git a/3rdparty/box2d/src/collision/b2_time_of_impact.cpp b/3rdparty/box2d/src/collision/b2_time_of_impact.cpp deleted file mode 100644 index 7a1011b7e7cc..000000000000 --- a/3rdparty/box2d/src/collision/b2_time_of_impact.cpp +++ /dev/null @@ -1,490 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "box2d/b2_collision.h" -#include "box2d/b2_distance.h" -#include "box2d/b2_circle_shape.h" -#include "box2d/b2_polygon_shape.h" -#include "box2d/b2_time_of_impact.h" -#include "box2d/b2_timer.h" - -#include - -B2_API float b2_toiTime, b2_toiMaxTime; -B2_API int32 b2_toiCalls, b2_toiIters, b2_toiMaxIters; -B2_API int32 b2_toiRootIters, b2_toiMaxRootIters; - -// -struct b2SeparationFunction -{ - enum Type - { - e_points, - e_faceA, - e_faceB - }; - - // TODO_ERIN might not need to return the separation - - float Initialize(const b2SimplexCache* cache, - const b2DistanceProxy* proxyA, const b2Sweep& sweepA, - const b2DistanceProxy* proxyB, const b2Sweep& sweepB, - float t1) - { - m_proxyA = proxyA; - m_proxyB = proxyB; - int32 count = cache->count; - b2Assert(0 < count && count < 3); - - m_sweepA = sweepA; - m_sweepB = sweepB; - - b2Transform xfA, xfB; - m_sweepA.GetTransform(&xfA, t1); - m_sweepB.GetTransform(&xfB, t1); - - if (count == 1) - { - m_type = e_points; - b2Vec2 localPointA = m_proxyA->GetVertex(cache->indexA[0]); - b2Vec2 localPointB = m_proxyB->GetVertex(cache->indexB[0]); - b2Vec2 pointA = b2Mul(xfA, localPointA); - b2Vec2 pointB = b2Mul(xfB, localPointB); - m_axis = pointB - pointA; - float s = m_axis.Normalize(); - return s; - } - else if (cache->indexA[0] == cache->indexA[1]) - { - // Two points on B and one on A. - m_type = e_faceB; - b2Vec2 localPointB1 = proxyB->GetVertex(cache->indexB[0]); - b2Vec2 localPointB2 = proxyB->GetVertex(cache->indexB[1]); - - m_axis = b2Cross(localPointB2 - localPointB1, 1.0f); - m_axis.Normalize(); - b2Vec2 normal = b2Mul(xfB.q, m_axis); - - m_localPoint = 0.5f * (localPointB1 + localPointB2); - b2Vec2 pointB = b2Mul(xfB, m_localPoint); - - b2Vec2 localPointA = proxyA->GetVertex(cache->indexA[0]); - b2Vec2 pointA = b2Mul(xfA, localPointA); - - float s = b2Dot(pointA - pointB, normal); - if (s < 0.0f) - { - m_axis = -m_axis; - s = -s; - } - return s; - } - else - { - // Two points on A and one or two points on B. - m_type = e_faceA; - b2Vec2 localPointA1 = m_proxyA->GetVertex(cache->indexA[0]); - b2Vec2 localPointA2 = m_proxyA->GetVertex(cache->indexA[1]); - - m_axis = b2Cross(localPointA2 - localPointA1, 1.0f); - m_axis.Normalize(); - b2Vec2 normal = b2Mul(xfA.q, m_axis); - - m_localPoint = 0.5f * (localPointA1 + localPointA2); - b2Vec2 pointA = b2Mul(xfA, m_localPoint); - - b2Vec2 localPointB = m_proxyB->GetVertex(cache->indexB[0]); - b2Vec2 pointB = b2Mul(xfB, localPointB); - - float s = b2Dot(pointB - pointA, normal); - if (s < 0.0f) - { - m_axis = -m_axis; - s = -s; - } - return s; - } - } - - // - float FindMinSeparation(int32* indexA, int32* indexB, float t) const - { - b2Transform xfA, xfB; - m_sweepA.GetTransform(&xfA, t); - m_sweepB.GetTransform(&xfB, t); - - switch (m_type) - { - case e_points: - { - b2Vec2 axisA = b2MulT(xfA.q, m_axis); - b2Vec2 axisB = b2MulT(xfB.q, -m_axis); - - *indexA = m_proxyA->GetSupport(axisA); - *indexB = m_proxyB->GetSupport(axisB); - - b2Vec2 localPointA = m_proxyA->GetVertex(*indexA); - b2Vec2 localPointB = m_proxyB->GetVertex(*indexB); - - b2Vec2 pointA = b2Mul(xfA, localPointA); - b2Vec2 pointB = b2Mul(xfB, localPointB); - - float separation = b2Dot(pointB - pointA, m_axis); - return separation; - } - - case e_faceA: - { - b2Vec2 normal = b2Mul(xfA.q, m_axis); - b2Vec2 pointA = b2Mul(xfA, m_localPoint); - - b2Vec2 axisB = b2MulT(xfB.q, -normal); - - *indexA = -1; - *indexB = m_proxyB->GetSupport(axisB); - - b2Vec2 localPointB = m_proxyB->GetVertex(*indexB); - b2Vec2 pointB = b2Mul(xfB, localPointB); - - float separation = b2Dot(pointB - pointA, normal); - return separation; - } - - case e_faceB: - { - b2Vec2 normal = b2Mul(xfB.q, m_axis); - b2Vec2 pointB = b2Mul(xfB, m_localPoint); - - b2Vec2 axisA = b2MulT(xfA.q, -normal); - - *indexB = -1; - *indexA = m_proxyA->GetSupport(axisA); - - b2Vec2 localPointA = m_proxyA->GetVertex(*indexA); - b2Vec2 pointA = b2Mul(xfA, localPointA); - - float separation = b2Dot(pointA - pointB, normal); - return separation; - } - - default: - b2Assert(false); - *indexA = -1; - *indexB = -1; - return 0.0f; - } - } - - // - float Evaluate(int32 indexA, int32 indexB, float t) const - { - b2Transform xfA, xfB; - m_sweepA.GetTransform(&xfA, t); - m_sweepB.GetTransform(&xfB, t); - - switch (m_type) - { - case e_points: - { - b2Vec2 localPointA = m_proxyA->GetVertex(indexA); - b2Vec2 localPointB = m_proxyB->GetVertex(indexB); - - b2Vec2 pointA = b2Mul(xfA, localPointA); - b2Vec2 pointB = b2Mul(xfB, localPointB); - float separation = b2Dot(pointB - pointA, m_axis); - - return separation; - } - - case e_faceA: - { - b2Vec2 normal = b2Mul(xfA.q, m_axis); - b2Vec2 pointA = b2Mul(xfA, m_localPoint); - - b2Vec2 localPointB = m_proxyB->GetVertex(indexB); - b2Vec2 pointB = b2Mul(xfB, localPointB); - - float separation = b2Dot(pointB - pointA, normal); - return separation; - } - - case e_faceB: - { - b2Vec2 normal = b2Mul(xfB.q, m_axis); - b2Vec2 pointB = b2Mul(xfB, m_localPoint); - - b2Vec2 localPointA = m_proxyA->GetVertex(indexA); - b2Vec2 pointA = b2Mul(xfA, localPointA); - - float separation = b2Dot(pointA - pointB, normal); - return separation; - } - - default: - b2Assert(false); - return 0.0f; - } - } - - const b2DistanceProxy* m_proxyA; - const b2DistanceProxy* m_proxyB; - b2Sweep m_sweepA, m_sweepB; - Type m_type; - b2Vec2 m_localPoint; - b2Vec2 m_axis; -}; - -// CCD via the local separating axis method. This seeks progression -// by computing the largest time at which separation is maintained. -void b2TimeOfImpact(b2TOIOutput* output, const b2TOIInput* input) -{ - b2Timer timer; - - ++b2_toiCalls; - - output->state = b2TOIOutput::e_unknown; - output->t = input->tMax; - - const b2DistanceProxy* proxyA = &input->proxyA; - const b2DistanceProxy* proxyB = &input->proxyB; - - b2Sweep sweepA = input->sweepA; - b2Sweep sweepB = input->sweepB; - - // Large rotations can make the root finder fail, so we normalize the - // sweep angles. - sweepA.Normalize(); - sweepB.Normalize(); - - float tMax = input->tMax; - - float totalRadius = proxyA->m_radius + proxyB->m_radius; - float target = b2Max(b2_linearSlop, totalRadius - 3.0f * b2_linearSlop); - float tolerance = 0.25f * b2_linearSlop; - b2Assert(target > tolerance); - - float t1 = 0.0f; - const int32 k_maxIterations = 20; // TODO_ERIN b2Settings - int32 iter = 0; - - // Prepare input for distance query. - b2SimplexCache cache; - cache.count = 0; - b2DistanceInput distanceInput; - distanceInput.proxyA = input->proxyA; - distanceInput.proxyB = input->proxyB; - distanceInput.useRadii = false; - - // The outer loop progressively attempts to compute new separating axes. - // This loop terminates when an axis is repeated (no progress is made). - for(;;) - { - b2Transform xfA, xfB; - sweepA.GetTransform(&xfA, t1); - sweepB.GetTransform(&xfB, t1); - - // Get the distance between shapes. We can also use the results - // to get a separating axis. - distanceInput.transformA = xfA; - distanceInput.transformB = xfB; - b2DistanceOutput distanceOutput; - b2Distance(&distanceOutput, &cache, &distanceInput); - - // If the shapes are overlapped, we give up on continuous collision. - if (distanceOutput.distance <= 0.0f) - { - // Failure! - output->state = b2TOIOutput::e_overlapped; - output->t = 0.0f; - break; - } - - if (distanceOutput.distance < target + tolerance) - { - // Victory! - output->state = b2TOIOutput::e_touching; - output->t = t1; - break; - } - - // Initialize the separating axis. - b2SeparationFunction fcn; - fcn.Initialize(&cache, proxyA, sweepA, proxyB, sweepB, t1); -#if 0 - // Dump the curve seen by the root finder - { - const int32 N = 100; - float dx = 1.0f / N; - float xs[N+1]; - float fs[N+1]; - - float x = 0.0f; - - for (int32 i = 0; i <= N; ++i) - { - sweepA.GetTransform(&xfA, x); - sweepB.GetTransform(&xfB, x); - float f = fcn.Evaluate(xfA, xfB) - target; - - printf("%g %g\n", x, f); - - xs[i] = x; - fs[i] = f; - - x += dx; - } - } -#endif - - // Compute the TOI on the separating axis. We do this by successively - // resolving the deepest point. This loop is bounded by the number of vertices. - bool done = false; - float t2 = tMax; - int32 pushBackIter = 0; - for (;;) - { - // Find the deepest point at t2. Store the witness point indices. - int32 indexA, indexB; - float s2 = fcn.FindMinSeparation(&indexA, &indexB, t2); - - // Is the final configuration separated? - if (s2 > target + tolerance) - { - // Victory! - output->state = b2TOIOutput::e_separated; - output->t = tMax; - done = true; - break; - } - - // Has the separation reached tolerance? - if (s2 > target - tolerance) - { - // Advance the sweeps - t1 = t2; - break; - } - - // Compute the initial separation of the witness points. - float s1 = fcn.Evaluate(indexA, indexB, t1); - - // Check for initial overlap. This might happen if the root finder - // runs out of iterations. - if (s1 < target - tolerance) - { - output->state = b2TOIOutput::e_failed; - output->t = t1; - done = true; - break; - } - - // Check for touching - if (s1 <= target + tolerance) - { - // Victory! t1 should hold the TOI (could be 0.0). - output->state = b2TOIOutput::e_touching; - output->t = t1; - done = true; - break; - } - - // Compute 1D root of: f(x) - target = 0 - int32 rootIterCount = 0; - float a1 = t1, a2 = t2; - for (;;) - { - // Use a mix of the secant rule and bisection. - float t; - if (rootIterCount & 1) - { - // Secant rule to improve convergence. - t = a1 + (target - s1) * (a2 - a1) / (s2 - s1); - } - else - { - // Bisection to guarantee progress. - t = 0.5f * (a1 + a2); - } - - ++rootIterCount; - ++b2_toiRootIters; - - float s = fcn.Evaluate(indexA, indexB, t); - - if (b2Abs(s - target) < tolerance) - { - // t2 holds a tentative value for t1 - t2 = t; - break; - } - - // Ensure we continue to bracket the root. - if (s > target) - { - a1 = t; - s1 = s; - } - else - { - a2 = t; - s2 = s; - } - - if (rootIterCount == 50) - { - break; - } - } - - b2_toiMaxRootIters = b2Max(b2_toiMaxRootIters, rootIterCount); - - ++pushBackIter; - - if (pushBackIter == b2_maxPolygonVertices) - { - break; - } - } - - ++iter; - ++b2_toiIters; - - if (done) - { - break; - } - - if (iter == k_maxIterations) - { - // Root finder got stuck. Semi-victory. - output->state = b2TOIOutput::e_failed; - output->t = t1; - break; - } - } - - b2_toiMaxIters = b2Max(b2_toiMaxIters, iter); - - float time = timer.GetMilliseconds(); - b2_toiMaxTime = b2Max(b2_toiMaxTime, time); - b2_toiTime += time; -} diff --git a/3rdparty/box2d/src/common/b2_block_allocator.cpp b/3rdparty/box2d/src/common/b2_block_allocator.cpp deleted file mode 100644 index 595f2ad393a1..000000000000 --- a/3rdparty/box2d/src/common/b2_block_allocator.cpp +++ /dev/null @@ -1,230 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "box2d/b2_block_allocator.h" -#include -#include -#include - -static const int32 b2_chunkSize = 16 * 1024; -static const int32 b2_maxBlockSize = 640; -static const int32 b2_chunkArrayIncrement = 128; - -// These are the supported object sizes. Actual allocations are rounded up the next size. -static const int32 b2_blockSizes[b2_blockSizeCount] = -{ - 16, // 0 - 32, // 1 - 64, // 2 - 96, // 3 - 128, // 4 - 160, // 5 - 192, // 6 - 224, // 7 - 256, // 8 - 320, // 9 - 384, // 10 - 448, // 11 - 512, // 12 - 640, // 13 -}; - -// This maps an arbitrary allocation size to a suitable slot in b2_blockSizes. -struct b2SizeMap -{ - b2SizeMap() - { - int32 j = 0; - values[0] = 0; - for (int32 i = 1; i <= b2_maxBlockSize; ++i) - { - b2Assert(j < b2_blockSizeCount); - if (i <= b2_blockSizes[j]) - { - values[i] = (uint8)j; - } - else - { - ++j; - values[i] = (uint8)j; - } - } - } - - uint8 values[b2_maxBlockSize + 1]; -}; - -static const b2SizeMap b2_sizeMap; - -struct b2Chunk -{ - int32 blockSize; - b2Block* blocks; -}; - -struct b2Block -{ - b2Block* next; -}; - -b2BlockAllocator::b2BlockAllocator() -{ - b2Assert(b2_blockSizeCount < UCHAR_MAX); - - m_chunkSpace = b2_chunkArrayIncrement; - m_chunkCount = 0; - m_chunks = (b2Chunk*)b2Alloc(m_chunkSpace * sizeof(b2Chunk)); - - memset(m_chunks, 0, m_chunkSpace * sizeof(b2Chunk)); - memset(m_freeLists, 0, sizeof(m_freeLists)); -} - -b2BlockAllocator::~b2BlockAllocator() -{ - for (int32 i = 0; i < m_chunkCount; ++i) - { - b2Free(m_chunks[i].blocks); - } - - b2Free(m_chunks); -} - -void* b2BlockAllocator::Allocate(int32 size) -{ - if (size == 0) - { - return nullptr; - } - - b2Assert(0 < size); - - if (size > b2_maxBlockSize) - { - return b2Alloc(size); - } - - int32 index = b2_sizeMap.values[size]; - b2Assert(0 <= index && index < b2_blockSizeCount); - - if (m_freeLists[index]) - { - b2Block* block = m_freeLists[index]; - m_freeLists[index] = block->next; - return block; - } - else - { - if (m_chunkCount == m_chunkSpace) - { - b2Chunk* oldChunks = m_chunks; - m_chunkSpace += b2_chunkArrayIncrement; - m_chunks = (b2Chunk*)b2Alloc(m_chunkSpace * sizeof(b2Chunk)); - memcpy(m_chunks, oldChunks, m_chunkCount * sizeof(b2Chunk)); - memset(m_chunks + m_chunkCount, 0, b2_chunkArrayIncrement * sizeof(b2Chunk)); - b2Free(oldChunks); - } - - b2Chunk* chunk = m_chunks + m_chunkCount; - chunk->blocks = (b2Block*)b2Alloc(b2_chunkSize); -#if defined(_DEBUG) - memset(chunk->blocks, 0xcd, b2_chunkSize); -#endif - int32 blockSize = b2_blockSizes[index]; - chunk->blockSize = blockSize; - int32 blockCount = b2_chunkSize / blockSize; - b2Assert(blockCount * blockSize <= b2_chunkSize); - for (int32 i = 0; i < blockCount - 1; ++i) - { - b2Block* block = (b2Block*)((int8*)chunk->blocks + blockSize * i); - b2Block* next = (b2Block*)((int8*)chunk->blocks + blockSize * (i + 1)); - block->next = next; - } - b2Block* last = (b2Block*)((int8*)chunk->blocks + blockSize * (blockCount - 1)); - last->next = nullptr; - - m_freeLists[index] = chunk->blocks->next; - ++m_chunkCount; - - return chunk->blocks; - } -} - -void b2BlockAllocator::Free(void* p, int32 size) -{ - if (size == 0) - { - return; - } - - b2Assert(0 < size); - - if (size > b2_maxBlockSize) - { - b2Free(p); - return; - } - - int32 index = b2_sizeMap.values[size]; - b2Assert(0 <= index && index < b2_blockSizeCount); - -#if defined(_DEBUG) - // Verify the memory address and size is valid. - int32 blockSize = b2_blockSizes[index]; - bool found = false; - for (int32 i = 0; i < m_chunkCount; ++i) - { - b2Chunk* chunk = m_chunks + i; - if (chunk->blockSize != blockSize) - { - b2Assert( (int8*)p + blockSize <= (int8*)chunk->blocks || - (int8*)chunk->blocks + b2_chunkSize <= (int8*)p); - } - else - { - if ((int8*)chunk->blocks <= (int8*)p && (int8*)p + blockSize <= (int8*)chunk->blocks + b2_chunkSize) - { - found = true; - } - } - } - - b2Assert(found); - - memset(p, 0xfd, blockSize); -#endif - - b2Block* block = (b2Block*)p; - block->next = m_freeLists[index]; - m_freeLists[index] = block; -} - -void b2BlockAllocator::Clear() -{ - for (int32 i = 0; i < m_chunkCount; ++i) - { - b2Free(m_chunks[i].blocks); - } - - m_chunkCount = 0; - memset(m_chunks, 0, m_chunkSpace * sizeof(b2Chunk)); - memset(m_freeLists, 0, sizeof(m_freeLists)); -} diff --git a/3rdparty/box2d/src/common/b2_draw.cpp b/3rdparty/box2d/src/common/b2_draw.cpp deleted file mode 100644 index 1ec11e54aca3..000000000000 --- a/3rdparty/box2d/src/common/b2_draw.cpp +++ /dev/null @@ -1,47 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#include "box2d/b2_draw.h" - -b2Draw::b2Draw() -{ - m_drawFlags = 0; -} - -void b2Draw::SetFlags(uint32 flags) -{ - m_drawFlags = flags; -} - -uint32 b2Draw::GetFlags() const -{ - return m_drawFlags; -} - -void b2Draw::AppendFlags(uint32 flags) -{ - m_drawFlags |= flags; -} - -void b2Draw::ClearFlags(uint32 flags) -{ - m_drawFlags &= ~flags; -} diff --git a/3rdparty/box2d/src/common/b2_math.cpp b/3rdparty/box2d/src/common/b2_math.cpp deleted file mode 100644 index a14460cd501e..000000000000 --- a/3rdparty/box2d/src/common/b2_math.cpp +++ /dev/null @@ -1,98 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "box2d/b2_math.h" - -const b2Vec2 b2Vec2_zero(0.0f, 0.0f); - -/// Solve A * x = b, where b is a column vector. This is more efficient -/// than computing the inverse in one-shot cases. -b2Vec3 b2Mat33::Solve33(const b2Vec3& b) const -{ - float det = b2Dot(ex, b2Cross(ey, ez)); - if (det != 0.0f) - { - det = 1.0f / det; - } - b2Vec3 x; - x.x = det * b2Dot(b, b2Cross(ey, ez)); - x.y = det * b2Dot(ex, b2Cross(b, ez)); - x.z = det * b2Dot(ex, b2Cross(ey, b)); - return x; -} - -/// Solve A * x = b, where b is a column vector. This is more efficient -/// than computing the inverse in one-shot cases. -b2Vec2 b2Mat33::Solve22(const b2Vec2& b) const -{ - float a11 = ex.x, a12 = ey.x, a21 = ex.y, a22 = ey.y; - float det = a11 * a22 - a12 * a21; - if (det != 0.0f) - { - det = 1.0f / det; - } - b2Vec2 x; - x.x = det * (a22 * b.x - a12 * b.y); - x.y = det * (a11 * b.y - a21 * b.x); - return x; -} - -/// -void b2Mat33::GetInverse22(b2Mat33* M) const -{ - float a = ex.x, b = ey.x, c = ex.y, d = ey.y; - float det = a * d - b * c; - if (det != 0.0f) - { - det = 1.0f / det; - } - - M->ex.x = det * d; M->ey.x = -det * b; M->ex.z = 0.0f; - M->ex.y = -det * c; M->ey.y = det * a; M->ey.z = 0.0f; - M->ez.x = 0.0f; M->ez.y = 0.0f; M->ez.z = 0.0f; -} - -/// Returns the zero matrix if singular. -void b2Mat33::GetSymInverse33(b2Mat33* M) const -{ - float det = b2Dot(ex, b2Cross(ey, ez)); - if (det != 0.0f) - { - det = 1.0f / det; - } - - float a11 = ex.x, a12 = ey.x, a13 = ez.x; - float a22 = ey.y, a23 = ez.y; - float a33 = ez.z; - - M->ex.x = det * (a22 * a33 - a23 * a23); - M->ex.y = det * (a13 * a23 - a12 * a33); - M->ex.z = det * (a12 * a23 - a13 * a22); - - M->ey.x = M->ex.y; - M->ey.y = det * (a11 * a33 - a13 * a13); - M->ey.z = det * (a13 * a12 - a11 * a23); - - M->ez.x = M->ex.z; - M->ez.y = M->ey.z; - M->ez.z = det * (a11 * a22 - a12 * a12); -} diff --git a/3rdparty/box2d/src/common/b2_settings.cpp b/3rdparty/box2d/src/common/b2_settings.cpp deleted file mode 100644 index dde28bb3abd7..000000000000 --- a/3rdparty/box2d/src/common/b2_settings.cpp +++ /dev/null @@ -1,74 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#define _CRT_SECURE_NO_WARNINGS - -#include "box2d/b2_settings.h" -#include -#include -#include - -b2Version b2_version = {2, 4, 1}; - -// Memory allocators. Modify these to use your own allocator. -void* b2Alloc_Default(int32 size) -{ - return malloc(size); -} - -void b2Free_Default(void* mem) -{ - free(mem); -} - -// You can modify this to use your logging facility. -void b2Log_Default(const char* string, va_list args) -{ - vprintf(string, args); -} - -FILE* b2_dumpFile = nullptr; - -void b2OpenDump(const char* fileName) -{ - b2Assert(b2_dumpFile == nullptr); - b2_dumpFile = fopen(fileName, "w"); -} - -void b2Dump(const char* string, ...) -{ - if (b2_dumpFile == nullptr) - { - return; - } - - va_list args; - va_start(args, string); - vfprintf(b2_dumpFile, string, args); - va_end(args); -} - -void b2CloseDump() -{ - fclose(b2_dumpFile); - b2_dumpFile = nullptr; -} diff --git a/3rdparty/box2d/src/common/b2_stack_allocator.cpp b/3rdparty/box2d/src/common/b2_stack_allocator.cpp deleted file mode 100644 index 602db1a00494..000000000000 --- a/3rdparty/box2d/src/common/b2_stack_allocator.cpp +++ /dev/null @@ -1,87 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "box2d/b2_stack_allocator.h" -#include "box2d/b2_math.h" - -b2StackAllocator::b2StackAllocator() -{ - m_index = 0; - m_allocation = 0; - m_maxAllocation = 0; - m_entryCount = 0; -} - -b2StackAllocator::~b2StackAllocator() -{ - b2Assert(m_index == 0); - b2Assert(m_entryCount == 0); -} - -void* b2StackAllocator::Allocate(int32 size) -{ - b2Assert(m_entryCount < b2_maxStackEntries); - - b2StackEntry* entry = m_entries + m_entryCount; - entry->size = size; - if (m_index + size > b2_stackSize) - { - entry->data = (char*)b2Alloc(size); - entry->usedMalloc = true; - } - else - { - entry->data = m_data + m_index; - entry->usedMalloc = false; - m_index += size; - } - - m_allocation += size; - m_maxAllocation = b2Max(m_maxAllocation, m_allocation); - ++m_entryCount; - - return entry->data; -} - -void b2StackAllocator::Free(void* p) -{ - b2Assert(m_entryCount > 0); - b2StackEntry* entry = m_entries + m_entryCount - 1; - b2Assert(p == entry->data); - if (entry->usedMalloc) - { - b2Free(p); - } - else - { - m_index -= entry->size; - } - m_allocation -= entry->size; - --m_entryCount; - - p = nullptr; -} - -int32 b2StackAllocator::GetMaxAllocation() const -{ - return m_maxAllocation; -} diff --git a/3rdparty/box2d/src/common/b2_timer.cpp b/3rdparty/box2d/src/common/b2_timer.cpp deleted file mode 100644 index dd7cde7f4709..000000000000 --- a/3rdparty/box2d/src/common/b2_timer.cpp +++ /dev/null @@ -1,125 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "box2d/b2_timer.h" - -#if defined(_WIN32) - -double b2Timer::s_invFrequency = 0.0; - -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif - -#include - -b2Timer::b2Timer() -{ - LARGE_INTEGER largeInteger; - - if (s_invFrequency == 0.0) - { - QueryPerformanceFrequency(&largeInteger); - s_invFrequency = double(largeInteger.QuadPart); - if (s_invFrequency > 0.0) - { - s_invFrequency = 1000.0 / s_invFrequency; - } - } - - QueryPerformanceCounter(&largeInteger); - m_start = double(largeInteger.QuadPart); -} - -void b2Timer::Reset() -{ - LARGE_INTEGER largeInteger; - QueryPerformanceCounter(&largeInteger); - m_start = double(largeInteger.QuadPart); -} - -float b2Timer::GetMilliseconds() const -{ - LARGE_INTEGER largeInteger; - QueryPerformanceCounter(&largeInteger); - double count = double(largeInteger.QuadPart); - float ms = float(s_invFrequency * (count - m_start)); - return ms; -} - -#elif defined(__linux__) || defined (__APPLE__) - -#include - -b2Timer::b2Timer() -{ - Reset(); -} - -void b2Timer::Reset() -{ - timeval t; - gettimeofday(&t, 0); - m_start_sec = t.tv_sec; - m_start_usec = t.tv_usec; -} - -float b2Timer::GetMilliseconds() const -{ - timeval t; - gettimeofday(&t, 0); - time_t start_sec = m_start_sec; - suseconds_t start_usec = m_start_usec; - - // http://www.gnu.org/software/libc/manual/html_node/Elapsed-Time.html - if (t.tv_usec < start_usec) - { - int nsec = (start_usec - t.tv_usec) / 1000000 + 1; - start_usec -= 1000000 * nsec; - start_sec += nsec; - } - - if (t.tv_usec - start_usec > 1000000) - { - int nsec = (t.tv_usec - start_usec) / 1000000; - start_usec += 1000000 * nsec; - start_sec -= nsec; - } - return 1000.0f * (t.tv_sec - start_sec) + 0.001f * (t.tv_usec - start_usec); -} - -#else - -b2Timer::b2Timer() -{ -} - -void b2Timer::Reset() -{ -} - -float b2Timer::GetMilliseconds() const -{ - return 0.0f; -} - -#endif diff --git a/3rdparty/box2d/src/constraint_graph.c b/3rdparty/box2d/src/constraint_graph.c new file mode 100644 index 000000000000..7f8e1ee1405a --- /dev/null +++ b/3rdparty/box2d/src/constraint_graph.c @@ -0,0 +1,318 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#include "constraint_graph.h" + +#include "array.h" +#include "bitset.h" +#include "body.h" +#include "contact.h" +#include "joint.h" +#include "solver_set.h" +#include "world.h" + +#include + +// Solver using graph coloring. Islands are only used for sleep. +// High-Performance Physical Simulations on Next-Generation Architecture with Many Cores +// http://web.eecs.umich.edu/~msmelyan/papers/physsim_onmanycore_itj.pdf + +// Kinematic bodies have to be treated like dynamic bodies in graph coloring. Unlike static bodies, we cannot use a dummy solver +// body for kinematic bodies. We cannot access a kinematic body from multiple threads efficiently because the SIMD solver body +// scatter would write to the same kinematic body from multiple threads. Even if these writes don't modify the body, they will +// cause horrible cache stalls. To make this feasible I would need a way to block these writes. + +// This is used for debugging by making all constraints be assigned to overflow. +#define B2_FORCE_OVERFLOW 0 + +_Static_assert( b2_graphColorCount == 12, "graph color count assumed to be 12" ); + +void b2CreateGraph( b2ConstraintGraph* graph, int bodyCapacity ) +{ + _Static_assert( b2_graphColorCount >= 2, "must have at least two constraint graph colors" ); + _Static_assert( b2_overflowIndex == b2_graphColorCount - 1, "bad over flow index" ); + + *graph = ( b2ConstraintGraph ){ 0 }; + + bodyCapacity = b2MaxInt( bodyCapacity, 8 ); + + // Initialize graph color bit set. + // No bitset for overflow color. + for ( int i = 0; i < b2_overflowIndex; ++i ) + { + b2GraphColor* color = graph->colors + i; + color->bodySet = b2CreateBitSet( bodyCapacity ); + b2SetBitCountAndClear( &color->bodySet, bodyCapacity ); + } +} + +void b2DestroyGraph( b2ConstraintGraph* graph ) +{ + for ( int i = 0; i < b2_graphColorCount; ++i ) + { + b2GraphColor* color = graph->colors + i; + + // The bit set should never be used on the overflow color + B2_ASSERT( i != b2_overflowIndex || color->bodySet.bits == NULL ); + + b2DestroyBitSet( &color->bodySet ); + + b2ContactSimArray_Destroy( &color->contactSims ); + b2JointSimArray_Destroy( &color->jointSims ); + } +} + +// Contacts are always created as non-touching. They get cloned into the constraint +// graph once they are found to be touching. +// todo maybe kinematic bodies should not go into graph +void b2AddContactToGraph( b2World* world, b2ContactSim* contactSim, b2Contact* contact ) +{ + B2_ASSERT( contactSim->manifold.pointCount > 0 ); + B2_ASSERT( contactSim->simFlags & b2_simTouchingFlag ); + B2_ASSERT( contact->flags & b2_contactTouchingFlag ); + + b2ConstraintGraph* graph = &world->constraintGraph; + int colorIndex = b2_overflowIndex; + + int bodyIdA = contact->edges[0].bodyId; + int bodyIdB = contact->edges[1].bodyId; + b2Body* bodyA = b2BodyArray_Get( &world->bodies, bodyIdA ); + b2Body* bodyB = b2BodyArray_Get( &world->bodies, bodyIdB ); + bool staticA = bodyA->setIndex == b2_staticSet; + bool staticB = bodyB->setIndex == b2_staticSet; + B2_ASSERT( staticA == false || staticB == false ); + +#if B2_FORCE_OVERFLOW == 0 + if ( staticA == false && staticB == false ) + { + for ( int i = 0; i < b2_overflowIndex; ++i ) + { + b2GraphColor* color = graph->colors + i; + if ( b2GetBit( &color->bodySet, bodyIdA ) || b2GetBit( &color->bodySet, bodyIdB ) ) + { + continue; + } + + b2SetBitGrow( &color->bodySet, bodyIdA ); + b2SetBitGrow( &color->bodySet, bodyIdB ); + colorIndex = i; + break; + } + } + else if ( staticA == false ) + { + // No static contacts in color 0 + for ( int i = 1; i < b2_overflowIndex; ++i ) + { + b2GraphColor* color = graph->colors + i; + if ( b2GetBit( &color->bodySet, bodyIdA ) ) + { + continue; + } + + b2SetBitGrow( &color->bodySet, bodyIdA ); + colorIndex = i; + break; + } + } + else if ( staticB == false ) + { + // No static contacts in color 0 + for ( int i = 1; i < b2_overflowIndex; ++i ) + { + b2GraphColor* color = graph->colors + i; + if ( b2GetBit( &color->bodySet, bodyIdB ) ) + { + continue; + } + + b2SetBitGrow( &color->bodySet, bodyIdB ); + colorIndex = i; + break; + } + } +#endif + + b2GraphColor* color = graph->colors + colorIndex; + contact->colorIndex = colorIndex; + contact->localIndex = color->contactSims.count; + + b2ContactSim* newContact = b2ContactSimArray_Add( &color->contactSims ); + memcpy( newContact, contactSim, sizeof( b2ContactSim ) ); + + // todo perhaps skip this if the contact is already awake + + if ( staticA ) + { + newContact->bodySimIndexA = B2_NULL_INDEX; + newContact->invMassA = 0.0f; + newContact->invIA = 0.0f; + } + else + { + B2_ASSERT( bodyA->setIndex == b2_awakeSet ); + b2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet ); + + int localIndex = bodyA->localIndex; + newContact->bodySimIndexA = localIndex; + + b2BodySim* bodySimA = b2BodySimArray_Get( &awakeSet->bodySims, localIndex ); + newContact->invMassA = bodySimA->invMass; + newContact->invIA = bodySimA->invInertia; + } + + if ( staticB ) + { + newContact->bodySimIndexB = B2_NULL_INDEX; + newContact->invMassB = 0.0f; + newContact->invIB = 0.0f; + } + else + { + B2_ASSERT( bodyB->setIndex == b2_awakeSet ); + b2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet ); + + int localIndex = bodyB->localIndex; + newContact->bodySimIndexB = localIndex; + + b2BodySim* bodySimB = b2BodySimArray_Get( &awakeSet->bodySims, localIndex ); + newContact->invMassB = bodySimB->invMass; + newContact->invIB = bodySimB->invInertia; + } +} + +void b2RemoveContactFromGraph( b2World* world, int bodyIdA, int bodyIdB, int colorIndex, int localIndex ) +{ + b2ConstraintGraph* graph = &world->constraintGraph; + + B2_ASSERT( 0 <= colorIndex && colorIndex < b2_graphColorCount ); + b2GraphColor* color = graph->colors + colorIndex; + + if ( colorIndex != b2_overflowIndex ) + { + // might clear a bit for a static body, but this has no effect + b2ClearBit( &color->bodySet, bodyIdA ); + b2ClearBit( &color->bodySet, bodyIdB ); + } + + int movedIndex = b2ContactSimArray_RemoveSwap( &color->contactSims, localIndex ); + if ( movedIndex != B2_NULL_INDEX ) + { + // Fix index on swapped contact + b2ContactSim* movedContactSim = color->contactSims.data + localIndex; + + // Fix moved contact + int movedId = movedContactSim->contactId; + b2Contact* movedContact = b2ContactArray_Get( &world->contacts, movedId ); + B2_ASSERT( movedContact->setIndex == b2_awakeSet ); + B2_ASSERT( movedContact->colorIndex == colorIndex ); + B2_ASSERT( movedContact->localIndex == movedIndex ); + movedContact->localIndex = localIndex; + } +} + +static int b2AssignJointColor( b2ConstraintGraph* graph, int bodyIdA, int bodyIdB, bool staticA, bool staticB ) +{ + B2_ASSERT( staticA == false || staticB == false ); + +#if B2_FORCE_OVERFLOW == 0 + if ( staticA == false && staticB == false ) + { + for ( int i = 0; i < b2_overflowIndex; ++i ) + { + b2GraphColor* color = graph->colors + i; + if ( b2GetBit( &color->bodySet, bodyIdA ) || b2GetBit( &color->bodySet, bodyIdB ) ) + { + continue; + } + + b2SetBitGrow( &color->bodySet, bodyIdA ); + b2SetBitGrow( &color->bodySet, bodyIdB ); + return i; + } + } + else if ( staticA == false ) + { + for ( int i = 0; i < b2_overflowIndex; ++i ) + { + b2GraphColor* color = graph->colors + i; + if ( b2GetBit( &color->bodySet, bodyIdA ) ) + { + continue; + } + + b2SetBitGrow( &color->bodySet, bodyIdA ); + return i; + } + } + else if ( staticB == false ) + { + for ( int i = 0; i < b2_overflowIndex; ++i ) + { + b2GraphColor* color = graph->colors + i; + if ( b2GetBit( &color->bodySet, bodyIdB ) ) + { + continue; + } + + b2SetBitGrow( &color->bodySet, bodyIdB ); + return i; + } + } +#endif + + return b2_overflowIndex; +} + +b2JointSim* b2CreateJointInGraph( b2World* world, b2Joint* joint ) +{ + b2ConstraintGraph* graph = &world->constraintGraph; + + int bodyIdA = joint->edges[0].bodyId; + int bodyIdB = joint->edges[1].bodyId; + b2Body* bodyA = b2BodyArray_Get( &world->bodies, bodyIdA ); + b2Body* bodyB = b2BodyArray_Get( &world->bodies, bodyIdB ); + bool staticA = bodyA->setIndex == b2_staticSet; + bool staticB = bodyB->setIndex == b2_staticSet; + + int colorIndex = b2AssignJointColor( graph, bodyIdA, bodyIdB, staticA, staticB ); + + b2JointSim* jointSim = b2JointSimArray_Add( &graph->colors[colorIndex].jointSims ); + joint->colorIndex = colorIndex; + joint->localIndex = graph->colors[colorIndex].jointSims.count - 1; + return jointSim; +} + +void b2AddJointToGraph( b2World* world, b2JointSim* jointSim, b2Joint* joint ) +{ + b2JointSim* jointDst = b2CreateJointInGraph( world, joint ); + memcpy( jointDst, jointSim, sizeof( b2JointSim ) ); +} + +void b2RemoveJointFromGraph( b2World* world, int bodyIdA, int bodyIdB, int colorIndex, int localIndex ) +{ + b2ConstraintGraph* graph = &world->constraintGraph; + + B2_ASSERT( 0 <= colorIndex && colorIndex < b2_graphColorCount ); + b2GraphColor* color = graph->colors + colorIndex; + + if ( colorIndex != b2_overflowIndex ) + { + // May clear static bodies, no effect + b2ClearBit( &color->bodySet, bodyIdA ); + b2ClearBit( &color->bodySet, bodyIdB ); + } + + int movedIndex = b2JointSimArray_RemoveSwap( &color->jointSims, localIndex ); + if ( movedIndex != B2_NULL_INDEX ) + { + // Fix moved joint + b2JointSim* movedJointSim = color->jointSims.data + localIndex; + int movedId = movedJointSim->jointId; + b2Joint* movedJoint = b2JointArray_Get( &world->joints, movedId ); + B2_ASSERT( movedJoint->setIndex == b2_awakeSet ); + B2_ASSERT( movedJoint->colorIndex == colorIndex ); + B2_ASSERT( movedJoint->localIndex == movedIndex ); + movedJoint->localIndex = localIndex; + } +} diff --git a/3rdparty/box2d/src/constraint_graph.h b/3rdparty/box2d/src/constraint_graph.h new file mode 100644 index 000000000000..36b1961c0a5d --- /dev/null +++ b/3rdparty/box2d/src/constraint_graph.h @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include "array.h" +#include "bitset.h" + +typedef struct b2Body b2Body; +typedef struct b2ContactSim b2ContactSim; +typedef struct b2Contact b2Contact; +typedef struct b2ContactConstraint b2ContactConstraint; +typedef struct b2ContactConstraintSIMD b2ContactConstraintSIMD; +typedef struct b2JointSim b2JointSim; +typedef struct b2Joint b2Joint; +typedef struct b2StepContext b2StepContext; +typedef struct b2World b2World; + +// This holds constraints that cannot fit the graph color limit. This happens when a single dynamic body +// is touching many other bodies. +#define b2_overflowIndex b2_graphColorCount - 1 + +typedef struct b2GraphColor +{ + // This bitset is indexed by bodyId so this is over-sized to encompass static bodies + // however I never traverse these bits or use the bit count for anything + // This bitset is unused on the overflow color. + b2BitSet bodySet; + + // cache friendly arrays + b2ContactSimArray contactSims; + b2JointSimArray jointSims; + + // transient + union + { + b2ContactConstraintSIMD* simdConstraints; + b2ContactConstraint* overflowConstraints; + }; +} b2GraphColor; + +typedef struct b2ConstraintGraph +{ + // including overflow at the end + b2GraphColor colors[b2_graphColorCount]; +} b2ConstraintGraph; + +void b2CreateGraph( b2ConstraintGraph* graph, int bodyCapacity ); +void b2DestroyGraph( b2ConstraintGraph* graph ); + +void b2AddContactToGraph( b2World* world, b2ContactSim* contactSim, b2Contact* contact ); +void b2RemoveContactFromGraph( b2World* world, int bodyIdA, int bodyIdB, int colorIndex, int localIndex ); + +b2JointSim* b2CreateJointInGraph( b2World* world, b2Joint* joint ); +void b2AddJointToGraph( b2World* world, b2JointSim* jointSim, b2Joint* joint ); +void b2RemoveJointFromGraph( b2World* world, int bodyIdA, int bodyIdB, int colorIndex, int localIndex ); diff --git a/3rdparty/box2d/src/contact.c b/3rdparty/box2d/src/contact.c new file mode 100644 index 000000000000..24049b7cb9ae --- /dev/null +++ b/3rdparty/box2d/src/contact.c @@ -0,0 +1,584 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#include "contact.h" + +#include "array.h" +#include "body.h" +#include "core.h" +#include "island.h" +#include "shape.h" +#include "solver_set.h" +#include "table.h" +#include "world.h" + +#include "box2d/collision.h" + +#include +#include +#include + +B2_ARRAY_SOURCE( b2Contact, b2Contact ); +B2_ARRAY_SOURCE( b2ContactSim, b2ContactSim ); + +// Contacts and determinism +// A deterministic simulation requires contacts to exist in the same order in b2Island no matter the thread count. +// The order must reproduce from run to run. This is necessary because the Gauss-Seidel constraint solver is order dependent. +// +// Creation: +// - Contacts are created using results from b2UpdateBroadPhasePairs +// - These results are ordered according to the order of the broad-phase move array +// - The move array is ordered according to the shape creation order using a bitset. +// - The island/shape/body order is determined by creation order +// - Logically contacts are only created for awake bodies, so they are immediately added to the awake contact array (serially) +// +// Island linking: +// - The awake contact array is built from the body-contact graph for all awake bodies in awake islands. +// - Awake contacts are solved in parallel and they generate contact state changes. +// - These state changes may link islands together using union find. +// - The state changes are ordered using a bit array that encompasses all contacts +// - As long as contacts are created in deterministic order, island link order is deterministic. +// - This keeps the order of contacts in islands deterministic + +// Friction mixing law. The idea is to allow either shape to drive the friction to zero. +// For example, anything slides on ice. +static inline float b2MixFriction( float friction1, float friction2 ) +{ + return sqrtf( friction1 * friction2 ); +} + +// Restitution mixing law. The idea is allow for anything to bounce off an inelastic surface. +// For example, a superball bounces on anything. +static inline float b2MixRestitution( float restitution1, float restitution2 ) +{ + return restitution1 > restitution2 ? restitution1 : restitution2; +} + +// todo make relative for all +// typedef b2Manifold b2ManifoldFcn(const b2Shape* shapeA, const b2Shape* shapeB, b2Transform xfB, b2DistanceCache* cache); +typedef b2Manifold b2ManifoldFcn( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB, + b2DistanceCache* cache ); + +struct b2ContactRegister +{ + b2ManifoldFcn* fcn; + bool primary; +}; + +static struct b2ContactRegister s_registers[b2_shapeTypeCount][b2_shapeTypeCount]; +static bool s_initialized = false; + +static b2Manifold b2CircleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB, + b2DistanceCache* cache ) +{ + B2_MAYBE_UNUSED( cache ); + return b2CollideCircles( &shapeA->circle, xfA, &shapeB->circle, xfB ); +} + +static b2Manifold b2CapsuleAndCircleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB, + b2DistanceCache* cache ) +{ + B2_MAYBE_UNUSED( cache ); + return b2CollideCapsuleAndCircle( &shapeA->capsule, xfA, &shapeB->circle, xfB ); +} + +static b2Manifold b2CapsuleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB, + b2DistanceCache* cache ) +{ + B2_MAYBE_UNUSED( cache ); + return b2CollideCapsules( &shapeA->capsule, xfA, &shapeB->capsule, xfB ); +} + +static b2Manifold b2PolygonAndCircleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB, + b2DistanceCache* cache ) +{ + B2_MAYBE_UNUSED( cache ); + return b2CollidePolygonAndCircle( &shapeA->polygon, xfA, &shapeB->circle, xfB ); +} + +static b2Manifold b2PolygonAndCapsuleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB, + b2DistanceCache* cache ) +{ + B2_MAYBE_UNUSED( cache ); + return b2CollidePolygonAndCapsule( &shapeA->polygon, xfA, &shapeB->capsule, xfB ); +} + +static b2Manifold b2PolygonManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB, + b2DistanceCache* cache ) +{ + B2_MAYBE_UNUSED( cache ); + return b2CollidePolygons( &shapeA->polygon, xfA, &shapeB->polygon, xfB ); +} + +static b2Manifold b2SegmentAndCircleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB, + b2DistanceCache* cache ) +{ + B2_MAYBE_UNUSED( cache ); + return b2CollideSegmentAndCircle( &shapeA->segment, xfA, &shapeB->circle, xfB ); +} + +static b2Manifold b2SegmentAndCapsuleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB, + b2DistanceCache* cache ) +{ + B2_MAYBE_UNUSED( cache ); + return b2CollideSegmentAndCapsule( &shapeA->segment, xfA, &shapeB->capsule, xfB ); +} + +static b2Manifold b2SegmentAndPolygonManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB, + b2DistanceCache* cache ) +{ + B2_MAYBE_UNUSED( cache ); + return b2CollideSegmentAndPolygon( &shapeA->segment, xfA, &shapeB->polygon, xfB ); +} + +static b2Manifold b2ChainSegmentAndCircleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB, + b2DistanceCache* cache ) +{ + B2_MAYBE_UNUSED( cache ); + return b2CollideChainSegmentAndCircle( &shapeA->chainSegment, xfA, &shapeB->circle, xfB ); +} + +static b2Manifold b2ChainSegmentAndCapsuleManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, + b2Transform xfB, b2DistanceCache* cache ) +{ + return b2CollideChainSegmentAndCapsule( &shapeA->chainSegment, xfA, &shapeB->capsule, xfB, cache ); +} + +static b2Manifold b2ChainSegmentAndPolygonManifold( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, + b2Transform xfB, b2DistanceCache* cache ) +{ + return b2CollideChainSegmentAndPolygon( &shapeA->chainSegment, xfA, &shapeB->polygon, xfB, cache ); +} + +static void b2AddType( b2ManifoldFcn* fcn, b2ShapeType type1, b2ShapeType type2 ) +{ + B2_ASSERT( 0 <= type1 && type1 < b2_shapeTypeCount ); + B2_ASSERT( 0 <= type2 && type2 < b2_shapeTypeCount ); + + s_registers[type1][type2].fcn = fcn; + s_registers[type1][type2].primary = true; + + if ( type1 != type2 ) + { + s_registers[type2][type1].fcn = fcn; + s_registers[type2][type1].primary = false; + } +} + +void b2InitializeContactRegisters( void ) +{ + if ( s_initialized == false ) + { + b2AddType( b2CircleManifold, b2_circleShape, b2_circleShape ); + b2AddType( b2CapsuleAndCircleManifold, b2_capsuleShape, b2_circleShape ); + b2AddType( b2CapsuleManifold, b2_capsuleShape, b2_capsuleShape ); + b2AddType( b2PolygonAndCircleManifold, b2_polygonShape, b2_circleShape ); + b2AddType( b2PolygonAndCapsuleManifold, b2_polygonShape, b2_capsuleShape ); + b2AddType( b2PolygonManifold, b2_polygonShape, b2_polygonShape ); + b2AddType( b2SegmentAndCircleManifold, b2_segmentShape, b2_circleShape ); + b2AddType( b2SegmentAndCapsuleManifold, b2_segmentShape, b2_capsuleShape ); + b2AddType( b2SegmentAndPolygonManifold, b2_segmentShape, b2_polygonShape ); + b2AddType( b2ChainSegmentAndCircleManifold, b2_chainSegmentShape, b2_circleShape ); + b2AddType( b2ChainSegmentAndCapsuleManifold, b2_chainSegmentShape, b2_capsuleShape ); + b2AddType( b2ChainSegmentAndPolygonManifold, b2_chainSegmentShape, b2_polygonShape ); + s_initialized = true; + } +} + +void b2CreateContact( b2World* world, b2Shape* shapeA, b2Shape* shapeB ) +{ + b2ShapeType type1 = shapeA->type; + b2ShapeType type2 = shapeB->type; + + B2_ASSERT( 0 <= type1 && type1 < b2_shapeTypeCount ); + B2_ASSERT( 0 <= type2 && type2 < b2_shapeTypeCount ); + + if ( s_registers[type1][type2].fcn == NULL ) + { + // For example, no segment vs segment collision + return; + } + + if ( s_registers[type1][type2].primary == false ) + { + // flip order + b2CreateContact( world, shapeB, shapeA ); + return; + } + + b2Body* bodyA = b2BodyArray_Get( &world->bodies, shapeA->bodyId ); + b2Body* bodyB = b2BodyArray_Get( &world->bodies, shapeB->bodyId ); + + B2_ASSERT( bodyA->setIndex != b2_disabledSet && bodyB->setIndex != b2_disabledSet ); + B2_ASSERT( bodyA->setIndex != b2_staticSet || bodyB->setIndex != b2_staticSet ); + + int setIndex; + if ( bodyA->setIndex == b2_awakeSet || bodyB->setIndex == b2_awakeSet ) + { + setIndex = b2_awakeSet; + } + else + { + // sleeping and non-touching contacts live in the disabled set + // later if this set is found to be touching then the sleeping + // islands will be linked and the contact moved to the merged island + setIndex = b2_disabledSet; + } + + b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, setIndex ); + + // Create contact key and contact + int contactId = b2AllocId( &world->contactIdPool ); + if ( contactId == world->contacts.count ) + { + b2ContactArray_Push( &world->contacts, ( b2Contact ){ 0 } ); + } + + int shapeIdA = shapeA->id; + int shapeIdB = shapeB->id; + + b2Contact* contact = b2ContactArray_Get( &world->contacts, contactId ); + contact->contactId = contactId; + contact->setIndex = setIndex; + contact->colorIndex = B2_NULL_INDEX; + contact->localIndex = set->contactSims.count; + contact->islandId = B2_NULL_INDEX; + contact->islandPrev = B2_NULL_INDEX; + contact->islandNext = B2_NULL_INDEX; + contact->shapeIdA = shapeIdA; + contact->shapeIdB = shapeIdB; + contact->isMarked = false; + contact->flags = 0; + + if ( shapeA->isSensor || shapeB->isSensor ) + { + contact->flags |= b2_contactSensorFlag; + } + + if ( shapeA->enableSensorEvents || shapeB->enableSensorEvents ) + { + contact->flags |= b2_contactEnableSensorEvents; + } + + if ( shapeA->enableContactEvents || shapeB->enableContactEvents ) + { + contact->flags |= b2_contactEnableContactEvents; + } + + // Connect to body A + { + contact->edges[0].bodyId = shapeA->bodyId; + contact->edges[0].prevKey = B2_NULL_INDEX; + contact->edges[0].nextKey = bodyA->headContactKey; + + int keyA = ( contactId << 1 ) | 0; + int headContactKey = bodyA->headContactKey; + if ( headContactKey != B2_NULL_INDEX ) + { + b2Contact* headContact = b2ContactArray_Get( &world->contacts, headContactKey >> 1 ); + headContact->edges[headContactKey & 1].prevKey = keyA; + } + bodyA->headContactKey = keyA; + bodyA->contactCount += 1; + } + + // Connect to body B + { + contact->edges[1].bodyId = shapeB->bodyId; + contact->edges[1].prevKey = B2_NULL_INDEX; + contact->edges[1].nextKey = bodyB->headContactKey; + + int keyB = ( contactId << 1 ) | 1; + int headContactKey = bodyB->headContactKey; + if ( bodyB->headContactKey != B2_NULL_INDEX ) + { + b2Contact* headContact = b2ContactArray_Get( &world->contacts, headContactKey >> 1 ); + headContact->edges[headContactKey & 1].prevKey = keyB; + } + bodyB->headContactKey = keyB; + bodyB->contactCount += 1; + } + + // Add to pair set for fast lookup + uint64_t pairKey = B2_SHAPE_PAIR_KEY( shapeIdA, shapeIdB ); + b2AddKey( &world->broadPhase.pairSet, pairKey ); + + // Contacts are created as non-touching. Later if they are found to be touching + // they will link islands and be moved into the constraint graph. + b2ContactSim* contactSim = b2ContactSimArray_Add( &set->contactSims ); + contactSim->contactId = contactId; + +#if B2_VALIDATE + contactSim->bodyIdA = shapeA->bodyId; + contactSim->bodyIdB = shapeB->bodyId; +#endif + + contactSim->bodySimIndexA = B2_NULL_INDEX; + contactSim->bodySimIndexB = B2_NULL_INDEX; + contactSim->invMassA = 0.0f; + contactSim->invIA = 0.0f; + contactSim->invMassB = 0.0f; + contactSim->invIB = 0.0f; + contactSim->shapeIdA = shapeIdA; + contactSim->shapeIdB = shapeIdB; + contactSim->cache = b2_emptyDistanceCache; + contactSim->manifold = ( b2Manifold ){ 0 }; + contactSim->friction = b2MixFriction( shapeA->friction, shapeB->friction ); + contactSim->restitution = b2MixRestitution( shapeA->restitution, shapeB->restitution ); + contactSim->tangentSpeed = 0.0f; + contactSim->simFlags = 0; + + if ( shapeA->enablePreSolveEvents || shapeB->enablePreSolveEvents ) + { + contactSim->simFlags |= b2_simEnablePreSolveEvents; + } +} + +// A contact is destroyed when: +// - broad-phase proxies stop overlapping +// - a body is destroyed +// - a body is disabled +// - a body changes type from dynamic to kinematic or static +// - a shape is destroyed +// - contact filtering is modified +// - a shape becomes a sensor (check this!!!) +void b2DestroyContact( b2World* world, b2Contact* contact, bool wakeBodies ) +{ + // Remove pair from set + uint64_t pairKey = B2_SHAPE_PAIR_KEY( contact->shapeIdA, contact->shapeIdB ); + b2RemoveKey( &world->broadPhase.pairSet, pairKey ); + + b2ContactEdge* edgeA = contact->edges + 0; + b2ContactEdge* edgeB = contact->edges + 1; + + int bodyIdA = edgeA->bodyId; + int bodyIdB = edgeB->bodyId; + b2Body* bodyA = b2BodyArray_Get( &world->bodies, bodyIdA ); + b2Body* bodyB = b2BodyArray_Get( &world->bodies, bodyIdB ); + + // if (contactListener && contact->IsTouching()) + //{ + // contactListener->EndContact(contact); + // } + + // Remove from body A + if ( edgeA->prevKey != B2_NULL_INDEX ) + { + b2Contact* prevContact = b2ContactArray_Get( &world->contacts, edgeA->prevKey >> 1 ); + b2ContactEdge* prevEdge = prevContact->edges + ( edgeA->prevKey & 1 ); + prevEdge->nextKey = edgeA->nextKey; + } + + if ( edgeA->nextKey != B2_NULL_INDEX ) + { + b2Contact* nextContact = b2ContactArray_Get( &world->contacts, edgeA->nextKey >> 1 ); + b2ContactEdge* nextEdge = nextContact->edges + ( edgeA->nextKey & 1 ); + nextEdge->prevKey = edgeA->prevKey; + } + + int contactId = contact->contactId; + + int edgeKeyA = ( contactId << 1 ) | 0; + if ( bodyA->headContactKey == edgeKeyA ) + { + bodyA->headContactKey = edgeA->nextKey; + } + + bodyA->contactCount -= 1; + + // Remove from body B + if ( edgeB->prevKey != B2_NULL_INDEX ) + { + b2Contact* prevContact = b2ContactArray_Get( &world->contacts, edgeB->prevKey >> 1 ); + b2ContactEdge* prevEdge = prevContact->edges + ( edgeB->prevKey & 1 ); + prevEdge->nextKey = edgeB->nextKey; + } + + if ( edgeB->nextKey != B2_NULL_INDEX ) + { + b2Contact* nextContact = b2ContactArray_Get( &world->contacts, edgeB->nextKey >> 1 ); + b2ContactEdge* nextEdge = nextContact->edges + ( edgeB->nextKey & 1 ); + nextEdge->prevKey = edgeB->prevKey; + } + + int edgeKeyB = ( contactId << 1 ) | 1; + if ( bodyB->headContactKey == edgeKeyB ) + { + bodyB->headContactKey = edgeB->nextKey; + } + + bodyB->contactCount -= 1; + + // Remove contact from the array that owns it + if ( contact->islandId != B2_NULL_INDEX ) + { + b2UnlinkContact( world, contact ); + } + + if ( contact->colorIndex != B2_NULL_INDEX ) + { + // contact is an active constraint + B2_ASSERT( contact->setIndex == b2_awakeSet ); + b2RemoveContactFromGraph( world, bodyIdA, bodyIdB, contact->colorIndex, contact->localIndex ); + } + else + { + // contact is non-touching or is sleeping or is a sensor + B2_ASSERT( contact->setIndex != b2_awakeSet || ( contact->flags & b2_contactTouchingFlag ) == 0 || + ( contact->flags & b2_contactSensorFlag ) != 0 ); + b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, contact->setIndex ); + int movedIndex = b2ContactSimArray_RemoveSwap( &set->contactSims, contact->localIndex ); + if ( movedIndex != B2_NULL_INDEX ) + { + b2ContactSim* movedContactSim = set->contactSims.data + contact->localIndex; + b2Contact* movedContact = b2ContactArray_Get( &world->contacts, movedContactSim->contactId ); + movedContact->localIndex = contact->localIndex; + } + } + + contact->contactId = B2_NULL_INDEX; + contact->setIndex = B2_NULL_INDEX; + contact->colorIndex = B2_NULL_INDEX; + contact->localIndex = B2_NULL_INDEX; + + b2FreeId( &world->contactIdPool, contactId ); + + if ( wakeBodies ) + { + b2WakeBody( world, bodyA ); + b2WakeBody( world, bodyB ); + } +} + +b2ContactSim* b2GetContactSim( b2World* world, b2Contact* contact ) +{ + if ( contact->setIndex == b2_awakeSet && contact->colorIndex != B2_NULL_INDEX ) + { + // contact lives in constraint graph + B2_ASSERT( 0 <= contact->colorIndex && contact->colorIndex < b2_graphColorCount ); + b2GraphColor* color = world->constraintGraph.colors + contact->colorIndex; + return b2ContactSimArray_Get( &color->contactSims, contact->localIndex ); + } + + b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, contact->setIndex ); + return b2ContactSimArray_Get( &set->contactSims, contact->localIndex ); +} + +bool b2ShouldShapesCollide( b2Filter filterA, b2Filter filterB ) +{ + if ( filterA.groupIndex == filterB.groupIndex && filterA.groupIndex != 0 ) + { + return filterA.groupIndex > 0; + } + + bool collide = ( filterA.maskBits & filterB.categoryBits ) != 0 && ( filterA.categoryBits & filterB.maskBits ) != 0; + return collide; +} + +static bool b2TestShapeOverlap( const b2Shape* shapeA, b2Transform xfA, const b2Shape* shapeB, b2Transform xfB, + b2DistanceCache* cache ) +{ + b2DistanceInput input; + input.proxyA = b2MakeShapeDistanceProxy( shapeA ); + input.proxyB = b2MakeShapeDistanceProxy( shapeB ); + input.transformA = xfA; + input.transformB = xfB; + input.useRadii = true; + + b2DistanceOutput output = b2ShapeDistance( cache, &input, NULL, 0 ); + + return output.distance < 10.0f * FLT_EPSILON; +} + +// Update the contact manifold and touching status. Also updates sensor overlap. +// Note: do not assume the shape AABBs are overlapping or are valid. +bool b2UpdateContact( b2World* world, b2ContactSim* contactSim, b2Shape* shapeA, b2Transform transformA, b2Vec2 centerOffsetA, + b2Shape* shapeB, b2Transform transformB, b2Vec2 centerOffsetB ) +{ + bool touching; + + // Is this contact a sensor? + if ( shapeA->isSensor || shapeB->isSensor ) + { + // Sensors don't generate manifolds or hit events + touching = b2TestShapeOverlap( shapeA, transformA, shapeB, transformB, &contactSim->cache ); + } + else + { + b2Manifold oldManifold = contactSim->manifold; + + // Compute TOI + b2ManifoldFcn* fcn = s_registers[shapeA->type][shapeB->type].fcn; + + contactSim->manifold = fcn( shapeA, transformA, shapeB, transformB, &contactSim->cache ); + + int pointCount = contactSim->manifold.pointCount; + touching = pointCount > 0; + + if ( touching && world->preSolveFcn && ( contactSim->simFlags & b2_simEnablePreSolveEvents ) != 0 ) + { + b2ShapeId shapeIdA = { shapeA->id + 1, world->worldId, shapeA->revision }; + b2ShapeId shapeIdB = { shapeB->id + 1, world->worldId, shapeB->revision }; + + // this call assumes thread safety + touching = world->preSolveFcn( shapeIdA, shapeIdB, &contactSim->manifold, world->preSolveContext ); + if ( touching == false ) + { + // disable contact + contactSim->manifold.pointCount = 0; + } + } + + if ( touching && ( shapeA->enableHitEvents || shapeB->enableHitEvents ) ) + { + contactSim->simFlags |= b2_simEnableHitEvent; + } + else + { + contactSim->simFlags &= ~b2_simEnableHitEvent; + } + + // Match old contact ids to new contact ids and copy the + // stored impulses to warm start the solver. + for ( int i = 0; i < pointCount; ++i ) + { + b2ManifoldPoint* mp2 = contactSim->manifold.points + i; + + // shift anchors to be center of mass relative + mp2->anchorA = b2Sub( mp2->anchorA, centerOffsetA ); + mp2->anchorB = b2Sub( mp2->anchorB, centerOffsetB ); + + mp2->normalImpulse = 0.0f; + mp2->tangentImpulse = 0.0f; + mp2->maxNormalImpulse = 0.0f; + mp2->normalVelocity = 0.0f; + mp2->persisted = false; + + uint16_t id2 = mp2->id; + + for ( int j = 0; j < oldManifold.pointCount; ++j ) + { + b2ManifoldPoint* mp1 = oldManifold.points + j; + + if ( mp1->id == id2 ) + { + mp2->normalImpulse = mp1->normalImpulse; + mp2->tangentImpulse = mp1->tangentImpulse; + mp2->persisted = true; + break; + } + } + } + } + + if ( touching ) + { + contactSim->simFlags |= b2_simTouchingFlag; + } + else + { + contactSim->simFlags &= ~b2_simTouchingFlag; + } + + return touching; +} diff --git a/3rdparty/box2d/src/contact.h b/3rdparty/box2d/src/contact.h new file mode 100644 index 000000000000..6cf570e4e394 --- /dev/null +++ b/3rdparty/box2d/src/contact.h @@ -0,0 +1,157 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include "array.h" +#include "core.h" + +#include "box2d/collision.h" +#include "box2d/types.h" + +typedef struct b2Shape b2Shape; +typedef struct b2World b2World; + +enum b2ContactFlags +{ + // Set when the solid shapes are touching. + b2_contactTouchingFlag = 0x00000001, + + // Contact has a hit event + b2_contactHitEventFlag = 0x00000002, + + // One of the shapes is a sensor + b2_contactSensorFlag = 0x0000004, + + // Set when a sensor is touching + // todo this is not used, perhaps have b2Body_GetSensorContactData() + b2_contactSensorTouchingFlag = 0x00000008, + + // This contact wants sensor events + b2_contactEnableSensorEvents = 0x00000010, + + // This contact wants contact events + b2_contactEnableContactEvents = 0x00000020, +}; + +// A contact edge is used to connect bodies and contacts together +// in a contact graph where each body is a node and each contact +// is an edge. A contact edge belongs to a doubly linked list +// maintained in each attached body. Each contact has two contact +// edges, one for each attached body. +typedef struct b2ContactEdge +{ + int bodyId; + int prevKey; + int nextKey; +} b2ContactEdge; + +// Cold contact data. Used as a persistent handle and for persistent island +// connectivity. +typedef struct b2Contact +{ + // index of simulation set stored in b2World + // B2_NULL_INDEX when slot is free + int setIndex; + + // index into the constraint graph color array + // B2_NULL_INDEX for non-touching or sleeping contacts + // B2_NULL_INDEX when slot is free + int colorIndex; + + // contact index within set or graph color + // B2_NULL_INDEX when slot is free + int localIndex; + + b2ContactEdge edges[2]; + int shapeIdA; + int shapeIdB; + + // A contact only belongs to an island if touching, otherwise B2_NULL_INDEX. + int islandPrev; + int islandNext; + int islandId; + + int contactId; + + // b2ContactFlags + uint32_t flags; + + bool isMarked; +} b2Contact; + +// Shifted to be distinct from b2ContactFlags +enum b2ContactSimFlags +{ + // Set when the shapes are touching, including sensors + b2_simTouchingFlag = 0x00010000, + + // This contact no longer has overlapping AABBs + b2_simDisjoint = 0x00020000, + + // This contact started touching + b2_simStartedTouching = 0x00040000, + + // This contact stopped touching + b2_simStoppedTouching = 0x00080000, + + // This contact has a hit event + b2_simEnableHitEvent = 0x00100000, + + // This contact wants pre-solve events + b2_simEnablePreSolveEvents = 0x00200000, +}; + +/// The class manages contact between two shapes. A contact exists for each overlapping +/// AABB in the broad-phase (except if filtered). Therefore a contact object may exist +/// that has no contact points. +typedef struct b2ContactSim +{ + int contactId; + +#if B2_VALIDATE + int bodyIdA; + int bodyIdB; +#endif + + int bodySimIndexA; + int bodySimIndexB; + + int shapeIdA; + int shapeIdB; + + float invMassA; + float invIA; + + float invMassB; + float invIB; + + b2Manifold manifold; + + // Mixed friction and restitution + float friction; + float restitution; + + // todo for conveyor belts + float tangentSpeed; + + // b2ContactSimFlags + uint32_t simFlags; + + b2DistanceCache cache; +} b2ContactSim; + +void b2InitializeContactRegisters( void ); + +void b2CreateContact( b2World* world, b2Shape* shapeA, b2Shape* shapeB ); +void b2DestroyContact( b2World* world, b2Contact* contact, bool wakeBodies ); + +b2ContactSim* b2GetContactSim( b2World* world, b2Contact* contact ); + +bool b2ShouldShapesCollide( b2Filter filterA, b2Filter filterB ); + +bool b2UpdateContact( b2World* world, b2ContactSim* contactSim, b2Shape* shapeA, b2Transform transformA, b2Vec2 centerOffsetA, + b2Shape* shapeB, b2Transform transformB, b2Vec2 centerOffsetB ); + +B2_ARRAY_INLINE( b2Contact, b2Contact ); +B2_ARRAY_INLINE( b2ContactSim, b2ContactSim ); diff --git a/3rdparty/box2d/src/contact_solver.c b/3rdparty/box2d/src/contact_solver.c new file mode 100644 index 000000000000..9c9d547fba27 --- /dev/null +++ b/3rdparty/box2d/src/contact_solver.c @@ -0,0 +1,2059 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#include "contact_solver.h" + +#include "body.h" +#include "constraint_graph.h" +#include "contact.h" +#include "core.h" +#include "solver_set.h" +#include "world.h" + +#include + +void b2PrepareOverflowContacts( b2StepContext* context ) +{ + b2TracyCZoneNC( prepare_overflow_contact, "Prepare Overflow Contact", b2_colorYellow, true ); + + b2World* world = context->world; + b2ConstraintGraph* graph = context->graph; + b2GraphColor* color = graph->colors + b2_overflowIndex; + b2ContactConstraint* constraints = color->overflowConstraints; + int contactCount = color->contactSims.count; + b2ContactSim* contacts = color->contactSims.data; + b2BodyState* awakeStates = context->states; + +#if B2_VALIDATE + b2Body* bodies = world->bodies.data; +#endif + + // Stiffer for static contacts to avoid bodies getting pushed through the ground + b2Softness contactSoftness = context->contactSoftness; + b2Softness staticSoftness = context->staticSoftness; + + float warmStartScale = world->enableWarmStarting ? 1.0f : 0.0f; + + for ( int i = 0; i < contactCount; ++i ) + { + b2ContactSim* contactSim = contacts + i; + + const b2Manifold* manifold = &contactSim->manifold; + int pointCount = manifold->pointCount; + + B2_ASSERT( 0 < pointCount && pointCount <= 2 ); + + int indexA = contactSim->bodySimIndexA; + int indexB = contactSim->bodySimIndexB; + +#if B2_VALIDATE + b2Body* bodyA = bodies + contactSim->bodyIdA; + int validIndexA = bodyA->setIndex == b2_awakeSet ? bodyA->localIndex : B2_NULL_INDEX; + B2_ASSERT( indexA == validIndexA ); + + b2Body* bodyB = bodies + contactSim->bodyIdB; + int validIndexB = bodyB->setIndex == b2_awakeSet ? bodyB->localIndex : B2_NULL_INDEX; + B2_ASSERT( indexB == validIndexB ); +#endif + + b2ContactConstraint* constraint = constraints + i; + constraint->indexA = indexA; + constraint->indexB = indexB; + constraint->normal = manifold->normal; + constraint->friction = contactSim->friction; + constraint->restitution = contactSim->restitution; + constraint->pointCount = pointCount; + + b2Vec2 vA = b2Vec2_zero; + float wA = 0.0f; + float mA = contactSim->invMassA; + float iA = contactSim->invIA; + if ( indexA != B2_NULL_INDEX ) + { + b2BodyState* stateA = awakeStates + indexA; + vA = stateA->linearVelocity; + wA = stateA->angularVelocity; + } + + b2Vec2 vB = b2Vec2_zero; + float wB = 0.0f; + float mB = contactSim->invMassB; + float iB = contactSim->invIB; + if ( indexB != B2_NULL_INDEX ) + { + b2BodyState* stateB = awakeStates + indexB; + vB = stateB->linearVelocity; + wB = stateB->angularVelocity; + } + + if ( indexA == B2_NULL_INDEX || indexB == B2_NULL_INDEX ) + { + constraint->softness = staticSoftness; + } + else + { + constraint->softness = contactSoftness; + } + + // copy mass into constraint to avoid cache misses during sub-stepping + constraint->invMassA = mA; + constraint->invIA = iA; + constraint->invMassB = mB; + constraint->invIB = iB; + + b2Vec2 normal = constraint->normal; + b2Vec2 tangent = b2RightPerp( constraint->normal ); + + for ( int j = 0; j < pointCount; ++j ) + { + const b2ManifoldPoint* mp = manifold->points + j; + b2ContactConstraintPoint* cp = constraint->points + j; + + cp->normalImpulse = warmStartScale * mp->normalImpulse; + cp->tangentImpulse = warmStartScale * mp->tangentImpulse; + cp->maxNormalImpulse = 0.0f; + + b2Vec2 rA = mp->anchorA; + b2Vec2 rB = mp->anchorB; + + cp->anchorA = rA; + cp->anchorB = rB; + cp->baseSeparation = mp->separation - b2Dot( b2Sub( rB, rA ), normal ); + + float rnA = b2Cross( rA, normal ); + float rnB = b2Cross( rB, normal ); + float kNormal = mA + mB + iA * rnA * rnA + iB * rnB * rnB; + cp->normalMass = kNormal > 0.0f ? 1.0f / kNormal : 0.0f; + + float rtA = b2Cross( rA, tangent ); + float rtB = b2Cross( rB, tangent ); + float kTangent = mA + mB + iA * rtA * rtA + iB * rtB * rtB; + cp->tangentMass = kTangent > 0.0f ? 1.0f / kTangent : 0.0f; + + // Save relative velocity for restitution + b2Vec2 vrA = b2Add( vA, b2CrossSV( wA, rA ) ); + b2Vec2 vrB = b2Add( vB, b2CrossSV( wB, rB ) ); + cp->relativeVelocity = b2Dot( normal, b2Sub( vrB, vrA ) ); + } + } + + b2TracyCZoneEnd( prepare_overflow_contact ); +} + +void b2WarmStartOverflowContacts( b2StepContext* context ) +{ + b2TracyCZoneNC( warmstart_overflow_contact, "WarmStart Overflow Contact", b2_colorDarkOrange, true ); + + b2ConstraintGraph* graph = context->graph; + b2GraphColor* color = graph->colors + b2_overflowIndex; + b2ContactConstraint* constraints = color->overflowConstraints; + int contactCount = color->contactSims.count; + b2World* world = context->world; + b2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet ); + b2BodyState* states = awakeSet->bodyStates.data; + + // This is a dummy state to represent a static body because static bodies don't have a solver body. + b2BodyState dummyState = b2_identityBodyState; + + for ( int i = 0; i < contactCount; ++i ) + { + const b2ContactConstraint* constraint = constraints + i; + + int indexA = constraint->indexA; + int indexB = constraint->indexB; + + b2BodyState* stateA = indexA == B2_NULL_INDEX ? &dummyState : states + indexA; + b2BodyState* stateB = indexB == B2_NULL_INDEX ? &dummyState : states + indexB; + + b2Vec2 vA = stateA->linearVelocity; + float wA = stateA->angularVelocity; + b2Vec2 vB = stateB->linearVelocity; + float wB = stateB->angularVelocity; + + float mA = constraint->invMassA; + float iA = constraint->invIA; + float mB = constraint->invMassB; + float iB = constraint->invIB; + + // Stiffer for static contacts to avoid bodies getting pushed through the ground + b2Vec2 normal = constraint->normal; + b2Vec2 tangent = b2RightPerp( constraint->normal ); + int pointCount = constraint->pointCount; + + for ( int j = 0; j < pointCount; ++j ) + { + const b2ContactConstraintPoint* cp = constraint->points + j; + + // fixed anchors + b2Vec2 rA = cp->anchorA; + b2Vec2 rB = cp->anchorB; + + b2Vec2 P = b2Add( b2MulSV( cp->normalImpulse, normal ), b2MulSV( cp->tangentImpulse, tangent ) ); + wA -= iA * b2Cross( rA, P ); + vA = b2MulAdd( vA, -mA, P ); + wB += iB * b2Cross( rB, P ); + vB = b2MulAdd( vB, mB, P ); + } + + stateA->linearVelocity = vA; + stateA->angularVelocity = wA; + stateB->linearVelocity = vB; + stateB->angularVelocity = wB; + } + + b2TracyCZoneEnd( warmstart_overflow_contact ); +} + +void b2SolveOverflowContacts( b2StepContext* context, bool useBias ) +{ + b2TracyCZoneNC( solve_contact, "Solve Contact", b2_colorAliceBlue, true ); + + b2ConstraintGraph* graph = context->graph; + b2GraphColor* color = graph->colors + b2_overflowIndex; + b2ContactConstraint* constraints = color->overflowConstraints; + int contactCount = color->contactSims.count; + b2World* world = context->world; + b2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet ); + b2BodyState* states = awakeSet->bodyStates.data; + + float inv_h = context->inv_h; + const float pushout = context->world->contactPushoutVelocity; + + // This is a dummy body to represent a static body since static bodies don't have a solver body. + b2BodyState dummyState = b2_identityBodyState; + + for ( int i = 0; i < contactCount; ++i ) + { + b2ContactConstraint* constraint = constraints + i; + float mA = constraint->invMassA; + float iA = constraint->invIA; + float mB = constraint->invMassB; + float iB = constraint->invIB; + + b2BodyState* stateA = constraint->indexA == B2_NULL_INDEX ? &dummyState : states + constraint->indexA; + b2Vec2 vA = stateA->linearVelocity; + float wA = stateA->angularVelocity; + b2Rot dqA = stateA->deltaRotation; + + b2BodyState* stateB = constraint->indexB == B2_NULL_INDEX ? &dummyState : states + constraint->indexB; + b2Vec2 vB = stateB->linearVelocity; + float wB = stateB->angularVelocity; + b2Rot dqB = stateB->deltaRotation; + + b2Vec2 dp = b2Sub( stateB->deltaPosition, stateA->deltaPosition ); + + b2Vec2 normal = constraint->normal; + b2Vec2 tangent = b2RightPerp( normal ); + float friction = constraint->friction; + b2Softness softness = constraint->softness; + + int pointCount = constraint->pointCount; + + for ( int j = 0; j < pointCount; ++j ) + { + b2ContactConstraintPoint* cp = constraint->points + j; + + // compute current separation + // this is subject to round-off error if the anchor is far from the body center of mass + b2Vec2 ds = b2Add( dp, b2Sub( b2RotateVector( dqB, cp->anchorB ), b2RotateVector( dqA, cp->anchorA ) ) ); + float s = b2Dot( ds, normal ) + cp->baseSeparation; + + float velocityBias = 0.0f; + float massScale = 1.0f; + float impulseScale = 0.0f; + if ( s > 0.0f ) + { + // speculative bias + velocityBias = s * inv_h; + } + else if ( useBias ) + { + velocityBias = b2MaxFloat( softness.biasRate * s, -pushout ); + massScale = softness.massScale; + impulseScale = softness.impulseScale; + } + + // fixed anchor points + b2Vec2 rA = cp->anchorA; + b2Vec2 rB = cp->anchorB; + + // relative normal velocity at contact + b2Vec2 vrA = b2Add( vA, b2CrossSV( wA, rA ) ); + b2Vec2 vrB = b2Add( vB, b2CrossSV( wB, rB ) ); + float vn = b2Dot( b2Sub( vrB, vrA ), normal ); + + // incremental normal impulse + float impulse = -cp->normalMass * massScale * ( vn + velocityBias ) - impulseScale * cp->normalImpulse; + + // clamp the accumulated impulse + float newImpulse = b2MaxFloat( cp->normalImpulse + impulse, 0.0f ); + impulse = newImpulse - cp->normalImpulse; + cp->normalImpulse = newImpulse; + cp->maxNormalImpulse = b2MaxFloat( cp->maxNormalImpulse, impulse ); + + // apply normal impulse + b2Vec2 P = b2MulSV( impulse, normal ); + vA = b2MulSub( vA, mA, P ); + wA -= iA * b2Cross( rA, P ); + + vB = b2MulAdd( vB, mB, P ); + wB += iB * b2Cross( rB, P ); + } + + for ( int j = 0; j < pointCount; ++j ) + { + b2ContactConstraintPoint* cp = constraint->points + j; + + // fixed anchor points + b2Vec2 rA = cp->anchorA; + b2Vec2 rB = cp->anchorB; + + // relative tangent velocity at contact + b2Vec2 vrB = b2Add( vB, b2CrossSV( wB, rB ) ); + b2Vec2 vrA = b2Add( vA, b2CrossSV( wA, rA ) ); + float vt = b2Dot( b2Sub( vrB, vrA ), tangent ); + + // incremental tangent impulse + float impulse = cp->tangentMass * ( -vt ); + + // clamp the accumulated force + float maxFriction = friction * cp->normalImpulse; + float newImpulse = b2ClampFloat( cp->tangentImpulse + impulse, -maxFriction, maxFriction ); + impulse = newImpulse - cp->tangentImpulse; + cp->tangentImpulse = newImpulse; + + // apply tangent impulse + b2Vec2 P = b2MulSV( impulse, tangent ); + vA = b2MulSub( vA, mA, P ); + wA -= iA * b2Cross( rA, P ); + vB = b2MulAdd( vB, mB, P ); + wB += iB * b2Cross( rB, P ); + } + + stateA->linearVelocity = vA; + stateA->angularVelocity = wA; + stateB->linearVelocity = vB; + stateB->angularVelocity = wB; + } + + b2TracyCZoneEnd( solve_contact ); +} + +void b2ApplyOverflowRestitution( b2StepContext* context ) +{ + b2TracyCZoneNC( overflow_resitution, "Overflow Restitution", b2_colorViolet, true ); + + b2ConstraintGraph* graph = context->graph; + b2GraphColor* color = graph->colors + b2_overflowIndex; + b2ContactConstraint* constraints = color->overflowConstraints; + int contactCount = color->contactSims.count; + b2World* world = context->world; + b2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet ); + b2BodyState* states = awakeSet->bodyStates.data; + + float threshold = context->world->restitutionThreshold; + + // dummy state to represent a static body + b2BodyState dummyState = b2_identityBodyState; + + for ( int i = 0; i < contactCount; ++i ) + { + b2ContactConstraint* constraint = constraints + i; + + float restitution = constraint->restitution; + if ( restitution == 0.0f ) + { + continue; + } + + float mA = constraint->invMassA; + float iA = constraint->invIA; + float mB = constraint->invMassB; + float iB = constraint->invIB; + + b2BodyState* stateA = constraint->indexA == B2_NULL_INDEX ? &dummyState : states + constraint->indexA; + b2Vec2 vA = stateA->linearVelocity; + float wA = stateA->angularVelocity; + + b2BodyState* stateB = constraint->indexB == B2_NULL_INDEX ? &dummyState : states + constraint->indexB; + b2Vec2 vB = stateB->linearVelocity; + float wB = stateB->angularVelocity; + + b2Vec2 normal = constraint->normal; + int pointCount = constraint->pointCount; + + // it is possible to get more accurate restitution by iterating + // this only makes a difference if there are two contact points + // for (int iter = 0; iter < 10; ++iter) + { + for ( int j = 0; j < pointCount; ++j ) + { + b2ContactConstraintPoint* cp = constraint->points + j; + + // if the normal impulse is zero then there was no collision + // this skips speculative contact points that didn't generate an impulse + // The max normal impulse is used in case there was a collision that moved away within the sub-step process + if ( cp->relativeVelocity > -threshold || cp->maxNormalImpulse == 0.0f ) + { + continue; + } + + // fixed anchor points + b2Vec2 rA = cp->anchorA; + b2Vec2 rB = cp->anchorB; + + // relative normal velocity at contact + b2Vec2 vrB = b2Add( vB, b2CrossSV( wB, rB ) ); + b2Vec2 vrA = b2Add( vA, b2CrossSV( wA, rA ) ); + float vn = b2Dot( b2Sub( vrB, vrA ), normal ); + + // compute normal impulse + float impulse = -cp->normalMass * ( vn + restitution * cp->relativeVelocity ); + + // clamp the accumulated impulse + // todo should this be stored? + float newImpulse = b2MaxFloat( cp->normalImpulse + impulse, 0.0f ); + impulse = newImpulse - cp->normalImpulse; + cp->normalImpulse = newImpulse; + cp->maxNormalImpulse = b2MaxFloat( cp->maxNormalImpulse, impulse ); + + // apply contact impulse + b2Vec2 P = b2MulSV( impulse, normal ); + vA = b2MulSub( vA, mA, P ); + wA -= iA * b2Cross( rA, P ); + vB = b2MulAdd( vB, mB, P ); + wB += iB * b2Cross( rB, P ); + } + } + + stateA->linearVelocity = vA; + stateA->angularVelocity = wA; + stateB->linearVelocity = vB; + stateB->angularVelocity = wB; + } + + b2TracyCZoneEnd( overflow_resitution ); +} + +void b2StoreOverflowImpulses( b2StepContext* context ) +{ + b2TracyCZoneNC( store_impulses, "Store", b2_colorFirebrick, true ); + + b2ConstraintGraph* graph = context->graph; + b2GraphColor* color = graph->colors + b2_overflowIndex; + b2ContactConstraint* constraints = color->overflowConstraints; + b2ContactSim* contacts = color->contactSims.data; + int contactCount = color->contactSims.count; + + // float hitEventThreshold = context->world->hitEventThreshold; + + for ( int i = 0; i < contactCount; ++i ) + { + const b2ContactConstraint* constraint = constraints + i; + b2ContactSim* contact = contacts + i; + b2Manifold* manifold = &contact->manifold; + int pointCount = manifold->pointCount; + + for ( int j = 0; j < pointCount; ++j ) + { + manifold->points[j].normalImpulse = constraint->points[j].normalImpulse; + manifold->points[j].tangentImpulse = constraint->points[j].tangentImpulse; + manifold->points[j].maxNormalImpulse = constraint->points[j].maxNormalImpulse; + manifold->points[j].normalVelocity = constraint->points[j].relativeVelocity; + } + } + + b2TracyCZoneEnd( store_impulses ); +} + +#if defined( B2_SIMD_AVX2 ) + +#include + +// wide float holds 8 numbers +typedef __m256 b2FloatW; + +#elif defined( B2_SIMD_NEON ) + +#include + +// wide float holds 4 numbers +typedef float32x4_t b2FloatW; + +#elif defined( B2_SIMD_SSE2 ) + +#include + +// wide float holds 4 numbers +typedef __m128 b2FloatW; + +#else + +// scalar math +typedef struct b2FloatW +{ + float x, y, z, w; +} b2FloatW; + +#endif + +// Wide vec2 +typedef struct b2Vec2W +{ + b2FloatW X, Y; +} b2Vec2W; + +// Wide rotation +typedef struct b2RotW +{ + b2FloatW C, S; +} b2RotW; + +#if defined( B2_SIMD_AVX2 ) + +static inline b2FloatW b2ZeroW() +{ + return _mm256_setzero_ps(); +} + +static inline b2FloatW b2SplatW( float scalar ) +{ + return _mm256_set1_ps( scalar ); +} + +static inline b2FloatW b2AddW( b2FloatW a, b2FloatW b ) +{ + return _mm256_add_ps( a, b ); +} + +static inline b2FloatW b2SubW( b2FloatW a, b2FloatW b ) +{ + return _mm256_sub_ps( a, b ); +} + +static inline b2FloatW b2MulW( b2FloatW a, b2FloatW b ) +{ + return _mm256_mul_ps( a, b ); +} + +static inline b2FloatW b2MulAddW( b2FloatW a, b2FloatW b, b2FloatW c ) +{ + // FMA can be emulated: https://github.com/lattera/glibc/blob/master/sysdeps/ieee754/dbl-64/s_fmaf.c#L34 + // return _mm256_fmadd_ps( b, c, a ); + return _mm256_add_ps( _mm256_mul_ps( b, c ), a ); +} + +static inline b2FloatW b2MulSubW( b2FloatW a, b2FloatW b, b2FloatW c ) +{ + // return _mm256_fnmadd_ps(b, c, a); + return _mm256_sub_ps( a, _mm256_mul_ps( b, c ) ); +} + +static inline b2FloatW b2MinW( b2FloatW a, b2FloatW b ) +{ + return _mm256_min_ps( a, b ); +} + +static inline b2FloatW b2MaxW( b2FloatW a, b2FloatW b ) +{ + return _mm256_max_ps( a, b ); +} + +static inline b2FloatW b2OrW( b2FloatW a, b2FloatW b ) +{ + return _mm256_or_ps( a, b ); +} + +static inline b2FloatW b2GreaterThanW( b2FloatW a, b2FloatW b ) +{ + return _mm256_cmp_ps( a, b, _CMP_GT_OQ ); +} + +static inline b2FloatW b2EqualsW( b2FloatW a, b2FloatW b ) +{ + return _mm256_cmp_ps( a, b, _CMP_EQ_OQ ); +} + +// component-wise returns mask ? b : a +static inline b2FloatW b2BlendW( b2FloatW a, b2FloatW b, b2FloatW mask ) +{ + return _mm256_blendv_ps( a, b, mask ); +} + +#elif defined( B2_SIMD_NEON ) + +static inline b2FloatW b2ZeroW() +{ + return vdupq_n_f32( 0.0f ); +} + +static inline b2FloatW b2SplatW( float scalar ) +{ + return vdupq_n_f32( scalar ); +} + +static inline b2FloatW b2SetW( float a, float b, float c, float d ) +{ + float32_t array[4] = { a, b, c, d }; + return vld1q_f32( array ); +} + +static inline b2FloatW b2AddW( b2FloatW a, b2FloatW b ) +{ + return vaddq_f32( a, b ); +} + +static inline b2FloatW b2SubW( b2FloatW a, b2FloatW b ) +{ + return vsubq_f32( a, b ); +} + +static inline b2FloatW b2MulW( b2FloatW a, b2FloatW b ) +{ + return vmulq_f32( a, b ); +} + +static inline b2FloatW b2MulAddW( b2FloatW a, b2FloatW b, b2FloatW c ) +{ + return vmlaq_f32( a, b, c ); +} + +static inline b2FloatW b2MulSubW( b2FloatW a, b2FloatW b, b2FloatW c ) +{ + return vmlsq_f32( a, b, c ); +} + +static inline b2FloatW b2MinW( b2FloatW a, b2FloatW b ) +{ + return vminq_f32( a, b ); +} + +static inline b2FloatW b2MaxW( b2FloatW a, b2FloatW b ) +{ + return vmaxq_f32( a, b ); +} + +static inline b2FloatW b2OrW( b2FloatW a, b2FloatW b ) +{ + return vreinterpretq_f32_u32( vorrq_u32( vreinterpretq_u32_f32( a ), vreinterpretq_u32_f32( b ) ) ); +} + +static inline b2FloatW b2GreaterThanW( b2FloatW a, b2FloatW b ) +{ + return vreinterpretq_f32_u32( vcgtq_f32( a, b ) ); +} + +static inline b2FloatW b2EqualsW( b2FloatW a, b2FloatW b ) +{ + return vreinterpretq_f32_u32( vceqq_f32( a, b ) ); +} + +// component-wise returns mask ? b : a +static inline b2FloatW b2BlendW( b2FloatW a, b2FloatW b, b2FloatW mask ) +{ + uint32x4_t mask32 = vreinterpretq_u32_f32( mask ); + return vbslq_f32( mask32, b, a ); +} + +static inline b2FloatW b2LoadW( const float32_t* data ) +{ + return vld1q_f32( data ); +} + +static inline void b2StoreW( float32_t* data, b2FloatW a ) +{ + return vst1q_f32( data, a ); +} + +static inline b2FloatW b2UnpackLoW( b2FloatW a, b2FloatW b ) +{ + return vzip1q_f32( a, b ); +} + +static inline b2FloatW b2UnpackHiW( b2FloatW a, b2FloatW b ) +{ + return vzip2q_f32( a, b ); +} + +#elif defined( B2_SIMD_SSE2 ) + +static inline b2FloatW b2ZeroW() +{ + return _mm_setzero_ps(); +} + +static inline b2FloatW b2SplatW( float scalar ) +{ + return _mm_set1_ps( scalar ); +} + +static inline b2FloatW b2SetW( float a, float b, float c, float d ) +{ + return _mm_setr_ps( a, b, c, d ); +} + +static inline b2FloatW b2AddW( b2FloatW a, b2FloatW b ) +{ + return _mm_add_ps( a, b ); +} + +static inline b2FloatW b2SubW( b2FloatW a, b2FloatW b ) +{ + return _mm_sub_ps( a, b ); +} + +static inline b2FloatW b2MulW( b2FloatW a, b2FloatW b ) +{ + return _mm_mul_ps( a, b ); +} + +static inline b2FloatW b2MulAddW( b2FloatW a, b2FloatW b, b2FloatW c ) +{ + return _mm_add_ps( a, _mm_mul_ps( b, c ) ); +} + +static inline b2FloatW b2MulSubW( b2FloatW a, b2FloatW b, b2FloatW c ) +{ + return _mm_sub_ps( a, _mm_mul_ps( b, c ) ); +} + +static inline b2FloatW b2MinW( b2FloatW a, b2FloatW b ) +{ + return _mm_min_ps( a, b ); +} + +static inline b2FloatW b2MaxW( b2FloatW a, b2FloatW b ) +{ + return _mm_max_ps( a, b ); +} + +static inline b2FloatW b2OrW( b2FloatW a, b2FloatW b ) +{ + return _mm_or_ps( a, b ); +} + +static inline b2FloatW b2GreaterThanW( b2FloatW a, b2FloatW b ) +{ + return _mm_cmpgt_ps( a, b ); +} + +static inline b2FloatW b2EqualsW( b2FloatW a, b2FloatW b ) +{ + return _mm_cmpeq_ps( a, b ); +} + +// component-wise returns mask ? b : a +static inline b2FloatW b2BlendW( b2FloatW a, b2FloatW b, b2FloatW mask ) +{ + return _mm_or_ps( _mm_and_ps( mask, b ), _mm_andnot_ps( mask, a ) ); +} + +static inline b2FloatW b2LoadW( const float* data ) +{ + return _mm_load_ps( data ); +} + +static inline void b2StoreW( float* data, b2FloatW a ) +{ + _mm_store_ps( data, a ); +} + +static inline b2FloatW b2UnpackLoW( b2FloatW a, b2FloatW b ) +{ + return _mm_unpacklo_ps( a, b ); +} + +static inline b2FloatW b2UnpackHiW( b2FloatW a, b2FloatW b ) +{ + return _mm_unpackhi_ps( a, b ); +} + +#else + +static inline b2FloatW b2ZeroW() +{ + return ( b2FloatW ){ 0.0f, 0.0f, 0.0f, 0.0f }; +} + +static inline b2FloatW b2SplatW( float scalar ) +{ + return ( b2FloatW ){ scalar, scalar, scalar, scalar }; +} + +static inline b2FloatW b2AddW( b2FloatW a, b2FloatW b ) +{ + return ( b2FloatW ){ a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w }; +} + +static inline b2FloatW b2SubW( b2FloatW a, b2FloatW b ) +{ + return ( b2FloatW ){ a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w }; +} + +static inline b2FloatW b2MulW( b2FloatW a, b2FloatW b ) +{ + return ( b2FloatW ){ a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w }; +} + +static inline b2FloatW b2MulAddW( b2FloatW a, b2FloatW b, b2FloatW c ) +{ + return ( b2FloatW ){ a.x + b.x * c.x, a.y + b.y * c.y, a.z + b.z * c.z, a.w + b.w * c.w }; +} + +static inline b2FloatW b2MulSubW( b2FloatW a, b2FloatW b, b2FloatW c ) +{ + return ( b2FloatW ){ a.x - b.x * c.x, a.y - b.y * c.y, a.z - b.z * c.z, a.w - b.w * c.w }; +} + +static inline b2FloatW b2MinW( b2FloatW a, b2FloatW b ) +{ + b2FloatW r; + r.x = a.x <= b.x ? a.x : b.x; + r.y = a.y <= b.y ? a.y : b.y; + r.z = a.z <= b.z ? a.z : b.z; + r.w = a.w <= b.w ? a.w : b.w; + return r; +} + +static inline b2FloatW b2MaxW( b2FloatW a, b2FloatW b ) +{ + b2FloatW r; + r.x = a.x >= b.x ? a.x : b.x; + r.y = a.y >= b.y ? a.y : b.y; + r.z = a.z >= b.z ? a.z : b.z; + r.w = a.w >= b.w ? a.w : b.w; + return r; +} + +static inline b2FloatW b2OrW( b2FloatW a, b2FloatW b ) +{ + b2FloatW r; + r.x = a.x != 0.0f || b.x != 0.0f ? 1.0f : 0.0f; + r.y = a.y != 0.0f || b.y != 0.0f ? 1.0f : 0.0f; + r.z = a.z != 0.0f || b.z != 0.0f ? 1.0f : 0.0f; + r.w = a.w != 0.0f || b.w != 0.0f ? 1.0f : 0.0f; + return r; +} + +static inline b2FloatW b2GreaterThanW( b2FloatW a, b2FloatW b ) +{ + b2FloatW r; + r.x = a.x > b.x ? 1.0f : 0.0f; + r.y = a.y > b.y ? 1.0f : 0.0f; + r.z = a.z > b.z ? 1.0f : 0.0f; + r.w = a.w > b.w ? 1.0f : 0.0f; + return r; +} + +static inline b2FloatW b2EqualsW( b2FloatW a, b2FloatW b ) +{ + b2FloatW r; + r.x = a.x == b.x ? 1.0f : 0.0f; + r.y = a.y == b.y ? 1.0f : 0.0f; + r.z = a.z == b.z ? 1.0f : 0.0f; + r.w = a.w == b.w ? 1.0f : 0.0f; + return r; +} + +// component-wise returns mask ? b : a +static inline b2FloatW b2BlendW( b2FloatW a, b2FloatW b, b2FloatW mask ) +{ + b2FloatW r; + r.x = mask.x != 0.0f ? b.x : a.x; + r.y = mask.y != 0.0f ? b.y : a.y; + r.z = mask.z != 0.0f ? b.z : a.z; + r.w = mask.w != 0.0f ? b.w : a.w; + return r; +} + +#endif + +static inline b2FloatW b2DotW( b2Vec2W a, b2Vec2W b ) +{ + return b2AddW( b2MulW( a.X, b.X ), b2MulW( a.Y, b.Y ) ); +} + +static inline b2FloatW b2CrossW( b2Vec2W a, b2Vec2W b ) +{ + return b2SubW( b2MulW( a.X, b.Y ), b2MulW( a.Y, b.X ) ); +} + +static inline b2Vec2W b2RotateVectorW( b2RotW q, b2Vec2W v ) +{ + return ( b2Vec2W ){ b2SubW( b2MulW( q.C, v.X ), b2MulW( q.S, v.Y ) ), b2AddW( b2MulW( q.S, v.X ), b2MulW( q.C, v.Y ) ) }; +} + +// Soft contact constraints with sub-stepping support +// Uses fixed anchors for Jacobians for better behavior on rolling shapes (circles & capsules) +// http://mmacklin.com/smallsteps.pdf +// https://box2d.org/files/ErinCatto_SoftConstraints_GDC2011.pdf + +typedef struct b2ContactConstraintSIMD +{ + int indexA[B2_SIMD_WIDTH]; + int indexB[B2_SIMD_WIDTH]; + + b2FloatW invMassA, invMassB; + b2FloatW invIA, invIB; + b2Vec2W normal; + b2FloatW friction; + b2FloatW biasRate; + b2FloatW massScale; + b2FloatW impulseScale; + b2Vec2W anchorA1, anchorB1; + b2FloatW normalMass1, tangentMass1; + b2FloatW baseSeparation1; + b2FloatW normalImpulse1; + b2FloatW maxNormalImpulse1; + b2FloatW tangentImpulse1; + b2Vec2W anchorA2, anchorB2; + b2FloatW baseSeparation2; + b2FloatW normalImpulse2; + b2FloatW maxNormalImpulse2; + b2FloatW tangentImpulse2; + b2FloatW normalMass2, tangentMass2; + b2FloatW restitution; + b2FloatW relativeVelocity1, relativeVelocity2; +} b2ContactConstraintSIMD; + +int b2GetContactConstraintSIMDByteCount( void ) +{ + return sizeof( b2ContactConstraintSIMD ); +} + +// wide version of b2BodyState +typedef struct b2SimdBody +{ + b2Vec2W v; + b2FloatW w; + b2FloatW flags; + b2Vec2W dp; + b2RotW dq; +} b2SimdBody; + +// Custom gather/scatter for each SIMD type +#if defined( B2_SIMD_AVX2 ) + +// This is a load and 8x8 transpose +static b2SimdBody b2GatherBodies( const b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices ) +{ + _Static_assert( sizeof( b2BodyState ) == 32, "b2BodyState not 32 bytes" ); + B2_ASSERT( ( (uintptr_t)states & 0x1F ) == 0 ); + // b2BodyState b2_identityBodyState = {{0.0f, 0.0f}, 0.0f, 0, {0.0f, 0.0f}, {1.0f, 0.0f}}; + b2FloatW identity = _mm256_setr_ps( 0.0f, 0.0f, 0.0f, 0, 0.0f, 0.0f, 1.0f, 0.0f ); + b2FloatW b0 = indices[0] == B2_NULL_INDEX ? identity : _mm256_load_ps( (float*)( states + indices[0] ) ); + b2FloatW b1 = indices[1] == B2_NULL_INDEX ? identity : _mm256_load_ps( (float*)( states + indices[1] ) ); + b2FloatW b2 = indices[2] == B2_NULL_INDEX ? identity : _mm256_load_ps( (float*)( states + indices[2] ) ); + b2FloatW b3 = indices[3] == B2_NULL_INDEX ? identity : _mm256_load_ps( (float*)( states + indices[3] ) ); + b2FloatW b4 = indices[4] == B2_NULL_INDEX ? identity : _mm256_load_ps( (float*)( states + indices[4] ) ); + b2FloatW b5 = indices[5] == B2_NULL_INDEX ? identity : _mm256_load_ps( (float*)( states + indices[5] ) ); + b2FloatW b6 = indices[6] == B2_NULL_INDEX ? identity : _mm256_load_ps( (float*)( states + indices[6] ) ); + b2FloatW b7 = indices[7] == B2_NULL_INDEX ? identity : _mm256_load_ps( (float*)( states + indices[7] ) ); + + b2FloatW t0 = _mm256_unpacklo_ps( b0, b1 ); + b2FloatW t1 = _mm256_unpackhi_ps( b0, b1 ); + b2FloatW t2 = _mm256_unpacklo_ps( b2, b3 ); + b2FloatW t3 = _mm256_unpackhi_ps( b2, b3 ); + b2FloatW t4 = _mm256_unpacklo_ps( b4, b5 ); + b2FloatW t5 = _mm256_unpackhi_ps( b4, b5 ); + b2FloatW t6 = _mm256_unpacklo_ps( b6, b7 ); + b2FloatW t7 = _mm256_unpackhi_ps( b6, b7 ); + b2FloatW tt0 = _mm256_shuffle_ps( t0, t2, _MM_SHUFFLE( 1, 0, 1, 0 ) ); + b2FloatW tt1 = _mm256_shuffle_ps( t0, t2, _MM_SHUFFLE( 3, 2, 3, 2 ) ); + b2FloatW tt2 = _mm256_shuffle_ps( t1, t3, _MM_SHUFFLE( 1, 0, 1, 0 ) ); + b2FloatW tt3 = _mm256_shuffle_ps( t1, t3, _MM_SHUFFLE( 3, 2, 3, 2 ) ); + b2FloatW tt4 = _mm256_shuffle_ps( t4, t6, _MM_SHUFFLE( 1, 0, 1, 0 ) ); + b2FloatW tt5 = _mm256_shuffle_ps( t4, t6, _MM_SHUFFLE( 3, 2, 3, 2 ) ); + b2FloatW tt6 = _mm256_shuffle_ps( t5, t7, _MM_SHUFFLE( 1, 0, 1, 0 ) ); + b2FloatW tt7 = _mm256_shuffle_ps( t5, t7, _MM_SHUFFLE( 3, 2, 3, 2 ) ); + + b2SimdBody simdBody; + simdBody.v.X = _mm256_permute2f128_ps( tt0, tt4, 0x20 ); + simdBody.v.Y = _mm256_permute2f128_ps( tt1, tt5, 0x20 ); + simdBody.w = _mm256_permute2f128_ps( tt2, tt6, 0x20 ); + simdBody.flags = _mm256_permute2f128_ps( tt3, tt7, 0x20 ); + simdBody.dp.X = _mm256_permute2f128_ps( tt0, tt4, 0x31 ); + simdBody.dp.Y = _mm256_permute2f128_ps( tt1, tt5, 0x31 ); + simdBody.dq.C = _mm256_permute2f128_ps( tt2, tt6, 0x31 ); + simdBody.dq.S = _mm256_permute2f128_ps( tt3, tt7, 0x31 ); + return simdBody; +} + +// This writes everything back to the solver bodies but only the velocities change +static void b2ScatterBodies( b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices, const b2SimdBody* B2_RESTRICT simdBody ) +{ + _Static_assert( sizeof( b2BodyState ) == 32, "b2BodyState not 32 bytes" ); + B2_ASSERT( ( (uintptr_t)states & 0x1F ) == 0 ); + b2FloatW t0 = _mm256_unpacklo_ps( simdBody->v.X, simdBody->v.Y ); + b2FloatW t1 = _mm256_unpackhi_ps( simdBody->v.X, simdBody->v.Y ); + b2FloatW t2 = _mm256_unpacklo_ps( simdBody->w, simdBody->flags ); + b2FloatW t3 = _mm256_unpackhi_ps( simdBody->w, simdBody->flags ); + b2FloatW t4 = _mm256_unpacklo_ps( simdBody->dp.X, simdBody->dp.Y ); + b2FloatW t5 = _mm256_unpackhi_ps( simdBody->dp.X, simdBody->dp.Y ); + b2FloatW t6 = _mm256_unpacklo_ps( simdBody->dq.C, simdBody->dq.S ); + b2FloatW t7 = _mm256_unpackhi_ps( simdBody->dq.C, simdBody->dq.S ); + b2FloatW tt0 = _mm256_shuffle_ps( t0, t2, _MM_SHUFFLE( 1, 0, 1, 0 ) ); + b2FloatW tt1 = _mm256_shuffle_ps( t0, t2, _MM_SHUFFLE( 3, 2, 3, 2 ) ); + b2FloatW tt2 = _mm256_shuffle_ps( t1, t3, _MM_SHUFFLE( 1, 0, 1, 0 ) ); + b2FloatW tt3 = _mm256_shuffle_ps( t1, t3, _MM_SHUFFLE( 3, 2, 3, 2 ) ); + b2FloatW tt4 = _mm256_shuffle_ps( t4, t6, _MM_SHUFFLE( 1, 0, 1, 0 ) ); + b2FloatW tt5 = _mm256_shuffle_ps( t4, t6, _MM_SHUFFLE( 3, 2, 3, 2 ) ); + b2FloatW tt6 = _mm256_shuffle_ps( t5, t7, _MM_SHUFFLE( 1, 0, 1, 0 ) ); + b2FloatW tt7 = _mm256_shuffle_ps( t5, t7, _MM_SHUFFLE( 3, 2, 3, 2 ) ); + + // I don't use any dummy body in the body array because this will lead to multithreaded sharing and the + // associated cache flushing. + if ( indices[0] != B2_NULL_INDEX ) + _mm256_store_ps( (float*)( states + indices[0] ), _mm256_permute2f128_ps( tt0, tt4, 0x20 ) ); + if ( indices[1] != B2_NULL_INDEX ) + _mm256_store_ps( (float*)( states + indices[1] ), _mm256_permute2f128_ps( tt1, tt5, 0x20 ) ); + if ( indices[2] != B2_NULL_INDEX ) + _mm256_store_ps( (float*)( states + indices[2] ), _mm256_permute2f128_ps( tt2, tt6, 0x20 ) ); + if ( indices[3] != B2_NULL_INDEX ) + _mm256_store_ps( (float*)( states + indices[3] ), _mm256_permute2f128_ps( tt3, tt7, 0x20 ) ); + if ( indices[4] != B2_NULL_INDEX ) + _mm256_store_ps( (float*)( states + indices[4] ), _mm256_permute2f128_ps( tt0, tt4, 0x31 ) ); + if ( indices[5] != B2_NULL_INDEX ) + _mm256_store_ps( (float*)( states + indices[5] ), _mm256_permute2f128_ps( tt1, tt5, 0x31 ) ); + if ( indices[6] != B2_NULL_INDEX ) + _mm256_store_ps( (float*)( states + indices[6] ), _mm256_permute2f128_ps( tt2, tt6, 0x31 ) ); + if ( indices[7] != B2_NULL_INDEX ) + _mm256_store_ps( (float*)( states + indices[7] ), _mm256_permute2f128_ps( tt3, tt7, 0x31 ) ); +} + +#elif defined( B2_SIMD_NEON ) + +// This is a load and transpose +static b2SimdBody b2GatherBodies( const b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices ) +{ + _Static_assert( sizeof( b2BodyState ) == 32, "b2BodyState not 32 bytes" ); + B2_ASSERT( ( (uintptr_t)states & 0x1F ) == 0 ); + + // [vx vy w flags] + b2FloatW identityA = b2ZeroW(); + + // [dpx dpy dqc dqs] + + b2FloatW identityB = b2SetW( 0.0f, 0.0f, 1.0f, 0.0f ); + + b2FloatW b1a = indices[0] == B2_NULL_INDEX ? identityA : b2LoadW( (float*)( states + indices[0] ) + 0 ); + b2FloatW b1b = indices[0] == B2_NULL_INDEX ? identityB : b2LoadW( (float*)( states + indices[0] ) + 4 ); + b2FloatW b2a = indices[1] == B2_NULL_INDEX ? identityA : b2LoadW( (float*)( states + indices[1] ) + 0 ); + b2FloatW b2b = indices[1] == B2_NULL_INDEX ? identityB : b2LoadW( (float*)( states + indices[1] ) + 4 ); + b2FloatW b3a = indices[2] == B2_NULL_INDEX ? identityA : b2LoadW( (float*)( states + indices[2] ) + 0 ); + b2FloatW b3b = indices[2] == B2_NULL_INDEX ? identityB : b2LoadW( (float*)( states + indices[2] ) + 4 ); + b2FloatW b4a = indices[3] == B2_NULL_INDEX ? identityA : b2LoadW( (float*)( states + indices[3] ) + 0 ); + b2FloatW b4b = indices[3] == B2_NULL_INDEX ? identityB : b2LoadW( (float*)( states + indices[3] ) + 4 ); + + // [vx1 vx3 vy1 vy3] + b2FloatW t1a = b2UnpackLoW( b1a, b3a ); + + // [vx2 vx4 vy2 vy4] + b2FloatW t2a = b2UnpackLoW( b2a, b4a ); + + // [w1 w3 f1 f3] + b2FloatW t3a = b2UnpackHiW( b1a, b3a ); + + // [w2 w4 f2 f4] + b2FloatW t4a = b2UnpackHiW( b2a, b4a ); + + b2SimdBody simdBody; + simdBody.v.X = b2UnpackLoW( t1a, t2a ); + simdBody.v.Y = b2UnpackHiW( t1a, t2a ); + simdBody.w = b2UnpackLoW( t3a, t4a ); + simdBody.flags = b2UnpackHiW( t3a, t4a ); + + b2FloatW t1b = b2UnpackLoW( b1b, b3b ); + b2FloatW t2b = b2UnpackLoW( b2b, b4b ); + b2FloatW t3b = b2UnpackHiW( b1b, b3b ); + b2FloatW t4b = b2UnpackHiW( b2b, b4b ); + + simdBody.dp.X = b2UnpackLoW( t1b, t2b ); + simdBody.dp.Y = b2UnpackHiW( t1b, t2b ); + simdBody.dq.C = b2UnpackLoW( t3b, t4b ); + simdBody.dq.S = b2UnpackHiW( t3b, t4b ); + + return simdBody; +} + +// This writes only the velocities back to the solver bodies +// https://developer.arm.com/documentation/102107a/0100/Floating-point-4x4-matrix-transposition +static void b2ScatterBodies( b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices, const b2SimdBody* B2_RESTRICT simdBody ) +{ + _Static_assert( sizeof( b2BodyState ) == 32, "b2BodyState not 32 bytes" ); + B2_ASSERT( ( (uintptr_t)states & 0x1F ) == 0 ); + + // b2FloatW x = b2SetW(0.0f, 1.0f, 2.0f, 3.0f); + // b2FloatW y = b2SetW(4.0f, 5.0f, 6.0f, 7.0f); + // b2FloatW z = b2SetW(8.0f, 9.0f, 10.0f, 11.0f); + // b2FloatW w = b2SetW(12.0f, 13.0f, 14.0f, 15.0f); + // + // float32x4x2_t rr1 = vtrnq_f32( x, y ); + // float32x4x2_t rr2 = vtrnq_f32( z, w ); + // + // float32x4_t b1 = vcombine_f32(vget_low_f32(rr1.val[0]), vget_low_f32(rr2.val[0])); + // float32x4_t b2 = vcombine_f32(vget_low_f32(rr1.val[1]), vget_low_f32(rr2.val[1])); + // float32x4_t b3 = vcombine_f32(vget_high_f32(rr1.val[0]), vget_high_f32(rr2.val[0])); + // float32x4_t b4 = vcombine_f32(vget_high_f32(rr1.val[1]), vget_high_f32(rr2.val[1])); + + // transpose + float32x4x2_t r1 = vtrnq_f32( simdBody->v.X, simdBody->v.Y ); + float32x4x2_t r2 = vtrnq_f32( simdBody->w, simdBody->flags ); + + // I don't use any dummy body in the body array because this will lead to multithreaded sharing and the + // associated cache flushing. + if ( indices[0] != B2_NULL_INDEX ) + { + float32x4_t body1 = vcombine_f32( vget_low_f32( r1.val[0] ), vget_low_f32( r2.val[0] ) ); + b2StoreW( (float*)( states + indices[0] ), body1 ); + } + + if ( indices[1] != B2_NULL_INDEX ) + { + float32x4_t body2 = vcombine_f32( vget_low_f32( r1.val[1] ), vget_low_f32( r2.val[1] ) ); + b2StoreW( (float*)( states + indices[1] ), body2 ); + } + + if ( indices[2] != B2_NULL_INDEX ) + { + float32x4_t body3 = vcombine_f32( vget_high_f32( r1.val[0] ), vget_high_f32( r2.val[0] ) ); + b2StoreW( (float*)( states + indices[2] ), body3 ); + } + + if ( indices[3] != B2_NULL_INDEX ) + { + float32x4_t body4 = vcombine_f32( vget_high_f32( r1.val[1] ), vget_high_f32( r2.val[1] ) ); + b2StoreW( (float*)( states + indices[3] ), body4 ); + } +} + +#elif defined( B2_SIMD_SSE2 ) + +// This is a load and transpose +static b2SimdBody b2GatherBodies( const b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices ) +{ + _Static_assert( sizeof( b2BodyState ) == 32, "b2BodyState not 32 bytes" ); + B2_ASSERT( ( (uintptr_t)states & 0x1F ) == 0 ); + + // [vx vy w flags] + b2FloatW identityA = b2ZeroW(); + + // [dpx dpy dqc dqs] + b2FloatW identityB = b2SetW( 0.0f, 0.0f, 1.0f, 0.0f ); + + b2FloatW b1a = indices[0] == B2_NULL_INDEX ? identityA : b2LoadW( (float*)( states + indices[0] ) + 0 ); + b2FloatW b1b = indices[0] == B2_NULL_INDEX ? identityB : b2LoadW( (float*)( states + indices[0] ) + 4 ); + b2FloatW b2a = indices[1] == B2_NULL_INDEX ? identityA : b2LoadW( (float*)( states + indices[1] ) + 0 ); + b2FloatW b2b = indices[1] == B2_NULL_INDEX ? identityB : b2LoadW( (float*)( states + indices[1] ) + 4 ); + b2FloatW b3a = indices[2] == B2_NULL_INDEX ? identityA : b2LoadW( (float*)( states + indices[2] ) + 0 ); + b2FloatW b3b = indices[2] == B2_NULL_INDEX ? identityB : b2LoadW( (float*)( states + indices[2] ) + 4 ); + b2FloatW b4a = indices[3] == B2_NULL_INDEX ? identityA : b2LoadW( (float*)( states + indices[3] ) + 0 ); + b2FloatW b4b = indices[3] == B2_NULL_INDEX ? identityB : b2LoadW( (float*)( states + indices[3] ) + 4 ); + + // [vx1 vx3 vy1 vy3] + b2FloatW t1a = b2UnpackLoW( b1a, b3a ); + + // [vx2 vx4 vy2 vy4] + b2FloatW t2a = b2UnpackLoW( b2a, b4a ); + + // [w1 w3 f1 f3] + b2FloatW t3a = b2UnpackHiW( b1a, b3a ); + + // [w2 w4 f2 f4] + b2FloatW t4a = b2UnpackHiW( b2a, b4a ); + + b2SimdBody simdBody; + simdBody.v.X = b2UnpackLoW( t1a, t2a ); + simdBody.v.Y = b2UnpackHiW( t1a, t2a ); + simdBody.w = b2UnpackLoW( t3a, t4a ); + simdBody.flags = b2UnpackHiW( t3a, t4a ); + + b2FloatW t1b = b2UnpackLoW( b1b, b3b ); + b2FloatW t2b = b2UnpackLoW( b2b, b4b ); + b2FloatW t3b = b2UnpackHiW( b1b, b3b ); + b2FloatW t4b = b2UnpackHiW( b2b, b4b ); + + simdBody.dp.X = b2UnpackLoW( t1b, t2b ); + simdBody.dp.Y = b2UnpackHiW( t1b, t2b ); + simdBody.dq.C = b2UnpackLoW( t3b, t4b ); + simdBody.dq.S = b2UnpackHiW( t3b, t4b ); + + return simdBody; +} + +// This writes only the velocities back to the solver bodies +static void b2ScatterBodies( b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices, const b2SimdBody* B2_RESTRICT simdBody ) +{ + _Static_assert( sizeof( b2BodyState ) == 32, "b2BodyState not 32 bytes" ); + B2_ASSERT( ( (uintptr_t)states & 0x1F ) == 0 ); + + // [vx1 vy1 vx2 vy2] + b2FloatW t1 = b2UnpackLoW( simdBody->v.X, simdBody->v.Y ); + // [vx3 vy3 vx4 vy4] + b2FloatW t2 = b2UnpackHiW( simdBody->v.X, simdBody->v.Y ); + // [w1 f1 w2 f2] + b2FloatW t3 = b2UnpackLoW( simdBody->w, simdBody->flags ); + // [w3 f3 w4 f4] + b2FloatW t4 = b2UnpackHiW( simdBody->w, simdBody->flags ); + + // I don't use any dummy body in the body array because this will lead to multithreaded sharing and the + // associated cache flushing. + if ( indices[0] != B2_NULL_INDEX ) + { + // [t1.x t1.y t3.x t3.y] + b2StoreW( (float*)( states + indices[0] ), _mm_shuffle_ps( t1, t3, _MM_SHUFFLE( 1, 0, 1, 0 ) ) ); + } + + if ( indices[1] != B2_NULL_INDEX ) + { + // [t1.z t1.w t3.z t3.w] + b2StoreW( (float*)( states + indices[1] ), _mm_shuffle_ps( t1, t3, _MM_SHUFFLE( 3, 2, 3, 2 ) ) ); + } + + if ( indices[2] != B2_NULL_INDEX ) + { + // [t2.x t2.y t4.x t4.y] + b2StoreW( (float*)( states + indices[2] ), _mm_shuffle_ps( t2, t4, _MM_SHUFFLE( 1, 0, 1, 0 ) ) ); + } + + if ( indices[3] != B2_NULL_INDEX ) + { + // [t2.z t2.w t4.z t4.w] + b2StoreW( (float*)( states + indices[3] ), _mm_shuffle_ps( t2, t4, _MM_SHUFFLE( 3, 2, 3, 2 ) ) ); + } +} + +#else + +// This is a load and transpose +static b2SimdBody b2GatherBodies( const b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices ) +{ + b2BodyState identity = b2_identityBodyState; + + b2BodyState s1 = indices[0] == B2_NULL_INDEX ? identity : states[indices[0]]; + b2BodyState s2 = indices[1] == B2_NULL_INDEX ? identity : states[indices[1]]; + b2BodyState s3 = indices[2] == B2_NULL_INDEX ? identity : states[indices[2]]; + b2BodyState s4 = indices[3] == B2_NULL_INDEX ? identity : states[indices[3]]; + + b2SimdBody simdBody; + simdBody.v.X = ( b2FloatW ){ s1.linearVelocity.x, s2.linearVelocity.x, s3.linearVelocity.x, s4.linearVelocity.x }; + simdBody.v.Y = ( b2FloatW ){ s1.linearVelocity.y, s2.linearVelocity.y, s3.linearVelocity.y, s4.linearVelocity.y }; + simdBody.w = ( b2FloatW ){ s1.angularVelocity, s2.angularVelocity, s3.angularVelocity, s4.angularVelocity }; + simdBody.flags = ( b2FloatW ){ (float)s1.flags, (float)s2.flags, (float)s3.flags, (float)s4.flags }; + simdBody.dp.X = ( b2FloatW ){ s1.deltaPosition.x, s2.deltaPosition.x, s3.deltaPosition.x, s4.deltaPosition.x }; + simdBody.dp.Y = ( b2FloatW ){ s1.deltaPosition.y, s2.deltaPosition.y, s3.deltaPosition.y, s4.deltaPosition.y }; + simdBody.dq.C = ( b2FloatW ){ s1.deltaRotation.c, s2.deltaRotation.c, s3.deltaRotation.c, s4.deltaRotation.c }; + simdBody.dq.S = ( b2FloatW ){ s1.deltaRotation.s, s2.deltaRotation.s, s3.deltaRotation.s, s4.deltaRotation.s }; + + return simdBody; +} + +// This writes only the velocities back to the solver bodies +static void b2ScatterBodies( b2BodyState* B2_RESTRICT states, int* B2_RESTRICT indices, const b2SimdBody* B2_RESTRICT simdBody ) +{ + if ( indices[0] != B2_NULL_INDEX ) + { + b2BodyState* state = states + indices[0]; + state->linearVelocity.x = simdBody->v.X.x; + state->linearVelocity.y = simdBody->v.Y.x; + state->angularVelocity = simdBody->w.x; + } + + if ( indices[1] != B2_NULL_INDEX ) + { + b2BodyState* state = states + indices[1]; + state->linearVelocity.x = simdBody->v.X.y; + state->linearVelocity.y = simdBody->v.Y.y; + state->angularVelocity = simdBody->w.y; + } + + if ( indices[2] != B2_NULL_INDEX ) + { + b2BodyState* state = states + indices[2]; + state->linearVelocity.x = simdBody->v.X.z; + state->linearVelocity.y = simdBody->v.Y.z; + state->angularVelocity = simdBody->w.z; + } + + if ( indices[3] != B2_NULL_INDEX ) + { + b2BodyState* state = states + indices[3]; + state->linearVelocity.x = simdBody->v.X.w; + state->linearVelocity.y = simdBody->v.Y.w; + state->angularVelocity = simdBody->w.w; + } +} + +#endif + +void b2PrepareContactsTask( int startIndex, int endIndex, b2StepContext* context ) +{ + b2TracyCZoneNC( prepare_contact, "Prepare Contact", b2_colorYellow, true ); + b2World* world = context->world; + b2ContactSim** contacts = context->contacts; + b2ContactConstraintSIMD* constraints = context->simdContactConstraints; + b2BodyState* awakeStates = context->states; +#if B2_VALIDATE + b2Body* bodies = world->bodies.data; +#endif + + // Stiffer for static contacts to avoid bodies getting pushed through the ground + b2Softness contactSoftness = context->contactSoftness; + b2Softness staticSoftness = context->staticSoftness; + + float warmStartScale = world->enableWarmStarting ? 1.0f : 0.0f; + + for ( int i = startIndex; i < endIndex; ++i ) + { + b2ContactConstraintSIMD* constraint = constraints + i; + + for ( int j = 0; j < B2_SIMD_WIDTH; ++j ) + { + b2ContactSim* contactSim = contacts[B2_SIMD_WIDTH * i + j]; + + if ( contactSim != NULL ) + { + const b2Manifold* manifold = &contactSim->manifold; + + int indexA = contactSim->bodySimIndexA; + int indexB = contactSim->bodySimIndexB; + +#if B2_VALIDATE + b2Body* bodyA = bodies + contactSim->bodyIdA; + int validIndexA = bodyA->setIndex == b2_awakeSet ? bodyA->localIndex : B2_NULL_INDEX; + b2Body* bodyB = bodies + contactSim->bodyIdB; + int validIndexB = bodyB->setIndex == b2_awakeSet ? bodyB->localIndex : B2_NULL_INDEX; + + B2_ASSERT( indexA == validIndexA ); + B2_ASSERT( indexB == validIndexB ); +#endif + constraint->indexA[j] = indexA; + constraint->indexB[j] = indexB; + + b2Vec2 vA = b2Vec2_zero; + float wA = 0.0f; + float mA = contactSim->invMassA; + float iA = contactSim->invIA; + if ( indexA != B2_NULL_INDEX ) + { + b2BodyState* stateA = awakeStates + indexA; + vA = stateA->linearVelocity; + wA = stateA->angularVelocity; + } + + b2Vec2 vB = b2Vec2_zero; + float wB = 0.0f; + float mB = contactSim->invMassB; + float iB = contactSim->invIB; + if ( indexB != B2_NULL_INDEX ) + { + b2BodyState* stateB = awakeStates + indexB; + vB = stateB->linearVelocity; + wB = stateB->angularVelocity; + } + + ( (float*)&constraint->invMassA )[j] = mA; + ( (float*)&constraint->invMassB )[j] = mB; + ( (float*)&constraint->invIA )[j] = iA; + ( (float*)&constraint->invIB )[j] = iB; + + b2Softness soft = ( indexA == B2_NULL_INDEX || indexB == B2_NULL_INDEX ) ? staticSoftness : contactSoftness; + + b2Vec2 normal = manifold->normal; + ( (float*)&constraint->normal.X )[j] = normal.x; + ( (float*)&constraint->normal.Y )[j] = normal.y; + + ( (float*)&constraint->friction )[j] = contactSim->friction; + ( (float*)&constraint->restitution )[j] = contactSim->restitution; + ( (float*)&constraint->biasRate )[j] = soft.biasRate; + ( (float*)&constraint->massScale )[j] = soft.massScale; + ( (float*)&constraint->impulseScale )[j] = soft.impulseScale; + + b2Vec2 tangent = b2RightPerp( normal ); + + { + const b2ManifoldPoint* mp = manifold->points + 0; + + b2Vec2 rA = mp->anchorA; + b2Vec2 rB = mp->anchorB; + + ( (float*)&constraint->anchorA1.X )[j] = rA.x; + ( (float*)&constraint->anchorA1.Y )[j] = rA.y; + ( (float*)&constraint->anchorB1.X )[j] = rB.x; + ( (float*)&constraint->anchorB1.Y )[j] = rB.y; + + ( (float*)&constraint->baseSeparation1 )[j] = mp->separation - b2Dot( b2Sub( rB, rA ), normal ); + + ( (float*)&constraint->normalImpulse1 )[j] = warmStartScale * mp->normalImpulse; + ( (float*)&constraint->tangentImpulse1 )[j] = warmStartScale * mp->tangentImpulse; + ( (float*)&constraint->maxNormalImpulse1 )[j] = 0.0f; + + float rnA = b2Cross( rA, normal ); + float rnB = b2Cross( rB, normal ); + float kNormal = mA + mB + iA * rnA * rnA + iB * rnB * rnB; + ( (float*)&constraint->normalMass1 )[j] = kNormal > 0.0f ? 1.0f / kNormal : 0.0f; + + float rtA = b2Cross( rA, tangent ); + float rtB = b2Cross( rB, tangent ); + float kTangent = mA + mB + iA * rtA * rtA + iB * rtB * rtB; + ( (float*)&constraint->tangentMass1 )[j] = kTangent > 0.0f ? 1.0f / kTangent : 0.0f; + + // relative velocity for restitution + b2Vec2 vrA = b2Add( vA, b2CrossSV( wA, rA ) ); + b2Vec2 vrB = b2Add( vB, b2CrossSV( wB, rB ) ); + ( (float*)&constraint->relativeVelocity1 )[j] = b2Dot( normal, b2Sub( vrB, vrA ) ); + } + + int pointCount = manifold->pointCount; + B2_ASSERT( 0 < pointCount && pointCount <= 2 ); + + if ( pointCount == 2 ) + { + const b2ManifoldPoint* mp = manifold->points + 1; + + b2Vec2 rA = mp->anchorA; + b2Vec2 rB = mp->anchorB; + + ( (float*)&constraint->anchorA2.X )[j] = rA.x; + ( (float*)&constraint->anchorA2.Y )[j] = rA.y; + ( (float*)&constraint->anchorB2.X )[j] = rB.x; + ( (float*)&constraint->anchorB2.Y )[j] = rB.y; + + ( (float*)&constraint->baseSeparation2 )[j] = mp->separation - b2Dot( b2Sub( rB, rA ), normal ); + + ( (float*)&constraint->normalImpulse2 )[j] = warmStartScale * mp->normalImpulse; + ( (float*)&constraint->tangentImpulse2 )[j] = warmStartScale * mp->tangentImpulse; + ( (float*)&constraint->maxNormalImpulse2 )[j] = 0.0f; + + float rnA = b2Cross( rA, normal ); + float rnB = b2Cross( rB, normal ); + float kNormal = mA + mB + iA * rnA * rnA + iB * rnB * rnB; + ( (float*)&constraint->normalMass2 )[j] = kNormal > 0.0f ? 1.0f / kNormal : 0.0f; + + float rtA = b2Cross( rA, tangent ); + float rtB = b2Cross( rB, tangent ); + float kTangent = mA + mB + iA * rtA * rtA + iB * rtB * rtB; + ( (float*)&constraint->tangentMass2 )[j] = kTangent > 0.0f ? 1.0f / kTangent : 0.0f; + + // relative velocity for restitution + b2Vec2 vrA = b2Add( vA, b2CrossSV( wA, rA ) ); + b2Vec2 vrB = b2Add( vB, b2CrossSV( wB, rB ) ); + ( (float*)&constraint->relativeVelocity2 )[j] = b2Dot( normal, b2Sub( vrB, vrA ) ); + } + else + { + // dummy data that has no effect + ( (float*)&constraint->baseSeparation2 )[j] = 0.0f; + ( (float*)&constraint->normalImpulse2 )[j] = 0.0f; + ( (float*)&constraint->tangentImpulse2 )[j] = 0.0f; + ( (float*)&constraint->maxNormalImpulse2 )[j] = 0.0f; + ( (float*)&constraint->anchorA2.X )[j] = 0.0f; + ( (float*)&constraint->anchorA2.Y )[j] = 0.0f; + ( (float*)&constraint->anchorB2.X )[j] = 0.0f; + ( (float*)&constraint->anchorB2.Y )[j] = 0.0f; + ( (float*)&constraint->normalMass2 )[j] = 0.0f; + ( (float*)&constraint->tangentMass2 )[j] = 0.0f; + ( (float*)&constraint->relativeVelocity2 )[j] = 0.0f; + } + } + else + { + // SIMD remainder + constraint->indexA[j] = B2_NULL_INDEX; + constraint->indexB[j] = B2_NULL_INDEX; + + ( (float*)&constraint->invMassA )[j] = 0.0f; + ( (float*)&constraint->invMassB )[j] = 0.0f; + ( (float*)&constraint->invIA )[j] = 0.0f; + ( (float*)&constraint->invIB )[j] = 0.0f; + + ( (float*)&constraint->normal.X )[j] = 0.0f; + ( (float*)&constraint->normal.Y )[j] = 0.0f; + ( (float*)&constraint->friction )[j] = 0.0f; + ( (float*)&constraint->biasRate )[j] = 0.0f; + ( (float*)&constraint->massScale )[j] = 0.0f; + ( (float*)&constraint->impulseScale )[j] = 0.0f; + + ( (float*)&constraint->anchorA1.X )[j] = 0.0f; + ( (float*)&constraint->anchorA1.Y )[j] = 0.0f; + ( (float*)&constraint->anchorB1.X )[j] = 0.0f; + ( (float*)&constraint->anchorB1.Y )[j] = 0.0f; + ( (float*)&constraint->baseSeparation1 )[j] = 0.0f; + ( (float*)&constraint->normalImpulse1 )[j] = 0.0f; + ( (float*)&constraint->tangentImpulse1 )[j] = 0.0f; + ( (float*)&constraint->maxNormalImpulse1 )[j] = 0.0f; + ( (float*)&constraint->normalMass1 )[j] = 0.0f; + ( (float*)&constraint->tangentMass1 )[j] = 0.0f; + + ( (float*)&constraint->anchorA2.X )[j] = 0.0f; + ( (float*)&constraint->anchorA2.Y )[j] = 0.0f; + ( (float*)&constraint->anchorB2.X )[j] = 0.0f; + ( (float*)&constraint->anchorB2.Y )[j] = 0.0f; + ( (float*)&constraint->baseSeparation2 )[j] = 0.0f; + ( (float*)&constraint->normalImpulse2 )[j] = 0.0f; + ( (float*)&constraint->tangentImpulse2 )[j] = 0.0f; + ( (float*)&constraint->maxNormalImpulse2 )[j] = 0.0f; + ( (float*)&constraint->normalMass2 )[j] = 0.0f; + ( (float*)&constraint->tangentMass2 )[j] = 0.0f; + + ( (float*)&constraint->restitution )[j] = 0.0f; + ( (float*)&constraint->relativeVelocity1 )[j] = 0.0f; + ( (float*)&constraint->relativeVelocity2 )[j] = 0.0f; + } + } + } + + b2TracyCZoneEnd( prepare_contact ); +} + +void b2WarmStartContactsTask( int startIndex, int endIndex, b2StepContext* context, int colorIndex ) +{ + b2TracyCZoneNC( warm_start_contact, "Warm Start", b2_colorGreen, true ); + + b2BodyState* states = context->states; + b2ContactConstraintSIMD* constraints = context->graph->colors[colorIndex].simdConstraints; + + for ( int i = startIndex; i < endIndex; ++i ) + { + b2ContactConstraintSIMD* c = constraints + i; + b2SimdBody bA = b2GatherBodies( states, c->indexA ); + b2SimdBody bB = b2GatherBodies( states, c->indexB ); + + b2FloatW tangentX = c->normal.Y; + b2FloatW tangentY = b2SubW( b2ZeroW(), c->normal.X ); + + { + // fixed anchors + b2Vec2W rA = c->anchorA1; + b2Vec2W rB = c->anchorB1; + + b2Vec2W P; + P.X = b2AddW( b2MulW( c->normalImpulse1, c->normal.X ), b2MulW( c->tangentImpulse1, tangentX ) ); + P.Y = b2AddW( b2MulW( c->normalImpulse1, c->normal.Y ), b2MulW( c->tangentImpulse1, tangentY ) ); + bA.w = b2MulSubW( bA.w, c->invIA, b2CrossW( rA, P ) ); + bA.v.X = b2MulSubW( bA.v.X, c->invMassA, P.X ); + bA.v.Y = b2MulSubW( bA.v.Y, c->invMassA, P.Y ); + bB.w = b2MulAddW( bB.w, c->invIB, b2CrossW( rB, P ) ); + bB.v.X = b2MulAddW( bB.v.X, c->invMassB, P.X ); + bB.v.Y = b2MulAddW( bB.v.Y, c->invMassB, P.Y ); + } + + { + // fixed anchors + b2Vec2W rA = c->anchorA2; + b2Vec2W rB = c->anchorB2; + + b2Vec2W P; + P.X = b2AddW( b2MulW( c->normalImpulse2, c->normal.X ), b2MulW( c->tangentImpulse2, tangentX ) ); + P.Y = b2AddW( b2MulW( c->normalImpulse2, c->normal.Y ), b2MulW( c->tangentImpulse2, tangentY ) ); + bA.w = b2MulSubW( bA.w, c->invIA, b2CrossW( rA, P ) ); + bA.v.X = b2MulSubW( bA.v.X, c->invMassA, P.X ); + bA.v.Y = b2MulSubW( bA.v.Y, c->invMassA, P.Y ); + bB.w = b2MulAddW( bB.w, c->invIB, b2CrossW( rB, P ) ); + bB.v.X = b2MulAddW( bB.v.X, c->invMassB, P.X ); + bB.v.Y = b2MulAddW( bB.v.Y, c->invMassB, P.Y ); + } + + b2ScatterBodies( states, c->indexA, &bA ); + b2ScatterBodies( states, c->indexB, &bB ); + } + + b2TracyCZoneEnd( warm_start_contact ); +} + +void b2SolveContactsTask( int startIndex, int endIndex, b2StepContext* context, int colorIndex, bool useBias ) +{ + b2TracyCZoneNC( solve_contact, "Solve Contact", b2_colorAliceBlue, true ); + + b2BodyState* states = context->states; + b2ContactConstraintSIMD* constraints = context->graph->colors[colorIndex].simdConstraints; + b2FloatW inv_h = b2SplatW( context->inv_h ); + b2FloatW minBiasVel = b2SplatW( -context->world->contactPushoutVelocity ); + + for ( int i = startIndex; i < endIndex; ++i ) + { + b2ContactConstraintSIMD* c = constraints + i; + + b2SimdBody bA = b2GatherBodies( states, c->indexA ); + b2SimdBody bB = b2GatherBodies( states, c->indexB ); + + b2FloatW biasRate, massScale, impulseScale; + if ( useBias ) + { + biasRate = c->biasRate; + massScale = c->massScale; + impulseScale = c->impulseScale; + } + else + { + biasRate = b2ZeroW(); + massScale = b2SplatW( 1.0f ); + impulseScale = b2ZeroW(); + } + + b2Vec2W dp = { b2SubW( bB.dp.X, bA.dp.X ), b2SubW( bB.dp.Y, bA.dp.Y ) }; + + // point1 non-penetration constraint + { + // moving anchors for current separation + b2Vec2W rsA = b2RotateVectorW( bA.dq, c->anchorA1 ); + b2Vec2W rsB = b2RotateVectorW( bB.dq, c->anchorB1 ); + + // compute current separation + // this is subject to round-off error if the anchor is far from the body center of mass + b2Vec2W ds = { b2AddW( dp.X, b2SubW( rsB.X, rsA.X ) ), b2AddW( dp.Y, b2SubW( rsB.Y, rsA.Y ) ) }; + b2FloatW s = b2AddW( b2DotW( c->normal, ds ), c->baseSeparation1 ); + + // Apply speculative bias if separation is greater than zero, otherwise apply soft constraint bias + b2FloatW mask = b2GreaterThanW( s, b2ZeroW() ); + b2FloatW specBias = b2MulW( s, inv_h ); + b2FloatW softBias = b2MaxW( b2MulW( biasRate, s ), minBiasVel ); + b2FloatW bias = b2BlendW( softBias, specBias, mask ); + + // fixed anchors for Jacobians + b2Vec2W rA = c->anchorA1; + b2Vec2W rB = c->anchorB1; + + // Relative velocity at contact + b2FloatW dvx = b2SubW( b2SubW( bB.v.X, b2MulW( bB.w, rB.Y ) ), b2SubW( bA.v.X, b2MulW( bA.w, rA.Y ) ) ); + b2FloatW dvy = b2SubW( b2AddW( bB.v.Y, b2MulW( bB.w, rB.X ) ), b2AddW( bA.v.Y, b2MulW( bA.w, rA.X ) ) ); + b2FloatW vn = b2AddW( b2MulW( dvx, c->normal.X ), b2MulW( dvy, c->normal.Y ) ); + + // Compute normal impulse + b2FloatW negImpulse = b2AddW( b2MulW( c->normalMass1, b2MulW( massScale, b2AddW( vn, bias ) ) ), + b2MulW( impulseScale, c->normalImpulse1 ) ); + + // Clamp the accumulated impulse + b2FloatW newImpulse = b2MaxW( b2SubW( c->normalImpulse1, negImpulse ), b2ZeroW() ); + b2FloatW impulse = b2SubW( newImpulse, c->normalImpulse1 ); + c->normalImpulse1 = newImpulse; + c->maxNormalImpulse1 = b2MaxW( c->maxNormalImpulse1, newImpulse ); + + // Apply contact impulse + b2FloatW Px = b2MulW( impulse, c->normal.X ); + b2FloatW Py = b2MulW( impulse, c->normal.Y ); + + bA.v.X = b2MulSubW( bA.v.X, c->invMassA, Px ); + bA.v.Y = b2MulSubW( bA.v.Y, c->invMassA, Py ); + bA.w = b2MulSubW( bA.w, c->invIA, b2SubW( b2MulW( rA.X, Py ), b2MulW( rA.Y, Px ) ) ); + + bB.v.X = b2MulAddW( bB.v.X, c->invMassB, Px ); + bB.v.Y = b2MulAddW( bB.v.Y, c->invMassB, Py ); + bB.w = b2MulAddW( bB.w, c->invIB, b2SubW( b2MulW( rB.X, Py ), b2MulW( rB.Y, Px ) ) ); + } + + // second point non-penetration constraint + { + // moving anchors for current separation + b2Vec2W rsA = b2RotateVectorW( bA.dq, c->anchorA2 ); + b2Vec2W rsB = b2RotateVectorW( bB.dq, c->anchorB2 ); + + // compute current separation + b2Vec2W ds = { b2AddW( dp.X, b2SubW( rsB.X, rsA.X ) ), b2AddW( dp.Y, b2SubW( rsB.Y, rsA.Y ) ) }; + b2FloatW s = b2AddW( b2DotW( c->normal, ds ), c->baseSeparation2 ); + + b2FloatW mask = b2GreaterThanW( s, b2ZeroW() ); + b2FloatW specBias = b2MulW( s, inv_h ); + b2FloatW softBias = b2MaxW( b2MulW( biasRate, s ), minBiasVel ); + b2FloatW bias = b2BlendW( softBias, specBias, mask ); + + // fixed anchors for Jacobians + b2Vec2W rA = c->anchorA2; + b2Vec2W rB = c->anchorB2; + + // Relative velocity at contact + b2FloatW dvx = b2SubW( b2SubW( bB.v.X, b2MulW( bB.w, rB.Y ) ), b2SubW( bA.v.X, b2MulW( bA.w, rA.Y ) ) ); + b2FloatW dvy = b2SubW( b2AddW( bB.v.Y, b2MulW( bB.w, rB.X ) ), b2AddW( bA.v.Y, b2MulW( bA.w, rA.X ) ) ); + b2FloatW vn = b2AddW( b2MulW( dvx, c->normal.X ), b2MulW( dvy, c->normal.Y ) ); + + // Compute normal impulse + b2FloatW negImpulse = b2AddW( b2MulW( c->normalMass2, b2MulW( massScale, b2AddW( vn, bias ) ) ), + b2MulW( impulseScale, c->normalImpulse2 ) ); + + // Clamp the accumulated impulse + b2FloatW newImpulse = b2MaxW( b2SubW( c->normalImpulse2, negImpulse ), b2ZeroW() ); + b2FloatW impulse = b2SubW( newImpulse, c->normalImpulse2 ); + c->normalImpulse2 = newImpulse; + c->maxNormalImpulse2 = b2MaxW( c->maxNormalImpulse2, newImpulse ); + + // Apply contact impulse + b2FloatW Px = b2MulW( impulse, c->normal.X ); + b2FloatW Py = b2MulW( impulse, c->normal.Y ); + + bA.v.X = b2MulSubW( bA.v.X, c->invMassA, Px ); + bA.v.Y = b2MulSubW( bA.v.Y, c->invMassA, Py ); + bA.w = b2MulSubW( bA.w, c->invIA, b2SubW( b2MulW( rA.X, Py ), b2MulW( rA.Y, Px ) ) ); + + bB.v.X = b2MulAddW( bB.v.X, c->invMassB, Px ); + bB.v.Y = b2MulAddW( bB.v.Y, c->invMassB, Py ); + bB.w = b2MulAddW( bB.w, c->invIB, b2SubW( b2MulW( rB.X, Py ), b2MulW( rB.Y, Px ) ) ); + } + + b2FloatW tangentX = c->normal.Y; + b2FloatW tangentY = b2SubW( b2ZeroW(), c->normal.X ); + + // point 1 friction constraint + { + // fixed anchors for Jacobians + b2Vec2W rA = c->anchorA1; + b2Vec2W rB = c->anchorB1; + + // Relative velocity at contact + b2FloatW dvx = b2SubW( b2SubW( bB.v.X, b2MulW( bB.w, rB.Y ) ), b2SubW( bA.v.X, b2MulW( bA.w, rA.Y ) ) ); + b2FloatW dvy = b2SubW( b2AddW( bB.v.Y, b2MulW( bB.w, rB.X ) ), b2AddW( bA.v.Y, b2MulW( bA.w, rA.X ) ) ); + b2FloatW vt = b2AddW( b2MulW( dvx, tangentX ), b2MulW( dvy, tangentY ) ); + + // Compute tangent force + b2FloatW negImpulse = b2MulW( c->tangentMass1, vt ); + + // Clamp the accumulated force + b2FloatW maxFriction = b2MulW( c->friction, c->normalImpulse1 ); + b2FloatW newImpulse = b2SubW( c->tangentImpulse1, negImpulse ); + newImpulse = b2MaxW( b2SubW( b2ZeroW(), maxFriction ), b2MinW( newImpulse, maxFriction ) ); + b2FloatW impulse = b2SubW( newImpulse, c->tangentImpulse1 ); + c->tangentImpulse1 = newImpulse; + + // Apply contact impulse + b2FloatW Px = b2MulW( impulse, tangentX ); + b2FloatW Py = b2MulW( impulse, tangentY ); + + bA.v.X = b2MulSubW( bA.v.X, c->invMassA, Px ); + bA.v.Y = b2MulSubW( bA.v.Y, c->invMassA, Py ); + bA.w = b2MulSubW( bA.w, c->invIA, b2SubW( b2MulW( rA.X, Py ), b2MulW( rA.Y, Px ) ) ); + + bB.v.X = b2MulAddW( bB.v.X, c->invMassB, Px ); + bB.v.Y = b2MulAddW( bB.v.Y, c->invMassB, Py ); + bB.w = b2MulAddW( bB.w, c->invIB, b2SubW( b2MulW( rB.X, Py ), b2MulW( rB.Y, Px ) ) ); + } + + // second point friction constraint + { + // fixed anchors for Jacobians + b2Vec2W rA = c->anchorA2; + b2Vec2W rB = c->anchorB2; + + // Relative velocity at contact + b2FloatW dvx = b2SubW( b2SubW( bB.v.X, b2MulW( bB.w, rB.Y ) ), b2SubW( bA.v.X, b2MulW( bA.w, rA.Y ) ) ); + b2FloatW dvy = b2SubW( b2AddW( bB.v.Y, b2MulW( bB.w, rB.X ) ), b2AddW( bA.v.Y, b2MulW( bA.w, rA.X ) ) ); + b2FloatW vt = b2AddW( b2MulW( dvx, tangentX ), b2MulW( dvy, tangentY ) ); + + // Compute tangent force + b2FloatW negImpulse = b2MulW( c->tangentMass2, vt ); + + // Clamp the accumulated force + b2FloatW maxFriction = b2MulW( c->friction, c->normalImpulse2 ); + b2FloatW newImpulse = b2SubW( c->tangentImpulse2, negImpulse ); + newImpulse = b2MaxW( b2SubW( b2ZeroW(), maxFriction ), b2MinW( newImpulse, maxFriction ) ); + b2FloatW impulse = b2SubW( newImpulse, c->tangentImpulse2 ); + c->tangentImpulse2 = newImpulse; + + // Apply contact impulse + b2FloatW Px = b2MulW( impulse, tangentX ); + b2FloatW Py = b2MulW( impulse, tangentY ); + + bA.v.X = b2MulSubW( bA.v.X, c->invMassA, Px ); + bA.v.Y = b2MulSubW( bA.v.Y, c->invMassA, Py ); + bA.w = b2MulSubW( bA.w, c->invIA, b2SubW( b2MulW( rA.X, Py ), b2MulW( rA.Y, Px ) ) ); + + bB.v.X = b2MulAddW( bB.v.X, c->invMassB, Px ); + bB.v.Y = b2MulAddW( bB.v.Y, c->invMassB, Py ); + bB.w = b2MulAddW( bB.w, c->invIB, b2SubW( b2MulW( rB.X, Py ), b2MulW( rB.Y, Px ) ) ); + } + + b2ScatterBodies( states, c->indexA, &bA ); + b2ScatterBodies( states, c->indexB, &bB ); + } + + b2TracyCZoneEnd( solve_contact ); +} + +void b2ApplyRestitutionTask( int startIndex, int endIndex, b2StepContext* context, int colorIndex ) +{ + b2TracyCZoneNC( restitution, "Restitution", b2_colorDodgerBlue, true ); + + b2BodyState* states = context->states; + b2ContactConstraintSIMD* constraints = context->graph->colors[colorIndex].simdConstraints; + b2FloatW threshold = b2SplatW( context->world->restitutionThreshold ); + b2FloatW zero = b2ZeroW(); + + for ( int i = startIndex; i < endIndex; ++i ) + { + b2ContactConstraintSIMD* c = constraints + i; + + b2SimdBody bA = b2GatherBodies( states, c->indexA ); + b2SimdBody bB = b2GatherBodies( states, c->indexB ); + + // first point non-penetration constraint + { + // Set effective mass to zero if restitution should not be applied + b2FloatW mask1 = b2GreaterThanW( b2AddW( c->relativeVelocity1, threshold ), zero ); + b2FloatW mask2 = b2EqualsW( c->maxNormalImpulse1, zero ); + b2FloatW mask = b2OrW( mask1, mask2 ); + b2FloatW mass = b2BlendW( c->normalMass1, zero, mask ); + + // fixed anchors for Jacobians + b2Vec2W rA = c->anchorA1; + b2Vec2W rB = c->anchorB1; + + // Relative velocity at contact + b2FloatW dvx = b2SubW( b2SubW( bB.v.X, b2MulW( bB.w, rB.Y ) ), b2SubW( bA.v.X, b2MulW( bA.w, rA.Y ) ) ); + b2FloatW dvy = b2SubW( b2AddW( bB.v.Y, b2MulW( bB.w, rB.X ) ), b2AddW( bA.v.Y, b2MulW( bA.w, rA.X ) ) ); + b2FloatW vn = b2AddW( b2MulW( dvx, c->normal.X ), b2MulW( dvy, c->normal.Y ) ); + + // Compute normal impulse + b2FloatW negImpulse = b2MulW( mass, b2AddW( vn, b2MulW( c->restitution, c->relativeVelocity1 ) ) ); + + // Clamp the accumulated impulse + b2FloatW newImpulse = b2MaxW( b2SubW( c->normalImpulse1, negImpulse ), b2ZeroW() ); + b2FloatW impulse = b2SubW( newImpulse, c->normalImpulse1 ); + c->normalImpulse1 = newImpulse; + + // Apply contact impulse + b2FloatW Px = b2MulW( impulse, c->normal.X ); + b2FloatW Py = b2MulW( impulse, c->normal.Y ); + + bA.v.X = b2MulSubW( bA.v.X, c->invMassA, Px ); + bA.v.Y = b2MulSubW( bA.v.Y, c->invMassA, Py ); + bA.w = b2MulSubW( bA.w, c->invIA, b2SubW( b2MulW( rA.X, Py ), b2MulW( rA.Y, Px ) ) ); + + bB.v.X = b2MulAddW( bB.v.X, c->invMassB, Px ); + bB.v.Y = b2MulAddW( bB.v.Y, c->invMassB, Py ); + bB.w = b2MulAddW( bB.w, c->invIB, b2SubW( b2MulW( rB.X, Py ), b2MulW( rB.Y, Px ) ) ); + } + + // second point non-penetration constraint + { + // Set effective mass to zero if restitution should not be applied + b2FloatW mask1 = b2GreaterThanW( b2AddW( c->relativeVelocity2, threshold ), zero ); + b2FloatW mask2 = b2EqualsW( c->maxNormalImpulse2, zero ); + b2FloatW mask = b2OrW( mask1, mask2 ); + b2FloatW mass = b2BlendW( c->normalMass2, zero, mask ); + + // fixed anchors for Jacobians + b2Vec2W rA = c->anchorA2; + b2Vec2W rB = c->anchorB2; + + // Relative velocity at contact + b2FloatW dvx = b2SubW( b2SubW( bB.v.X, b2MulW( bB.w, rB.Y ) ), b2SubW( bA.v.X, b2MulW( bA.w, rA.Y ) ) ); + b2FloatW dvy = b2SubW( b2AddW( bB.v.Y, b2MulW( bB.w, rB.X ) ), b2AddW( bA.v.Y, b2MulW( bA.w, rA.X ) ) ); + b2FloatW vn = b2AddW( b2MulW( dvx, c->normal.X ), b2MulW( dvy, c->normal.Y ) ); + + // Compute normal impulse + b2FloatW negImpulse = b2MulW( mass, b2AddW( vn, b2MulW( c->restitution, c->relativeVelocity2 ) ) ); + + // Clamp the accumulated impulse + b2FloatW newImpulse = b2MaxW( b2SubW( c->normalImpulse2, negImpulse ), b2ZeroW() ); + b2FloatW impulse = b2SubW( newImpulse, c->normalImpulse2 ); + c->normalImpulse2 = newImpulse; + + // Apply contact impulse + b2FloatW Px = b2MulW( impulse, c->normal.X ); + b2FloatW Py = b2MulW( impulse, c->normal.Y ); + + bA.v.X = b2MulSubW( bA.v.X, c->invMassA, Px ); + bA.v.Y = b2MulSubW( bA.v.Y, c->invMassA, Py ); + bA.w = b2MulSubW( bA.w, c->invIA, b2SubW( b2MulW( rA.X, Py ), b2MulW( rA.Y, Px ) ) ); + + bB.v.X = b2MulAddW( bB.v.X, c->invMassB, Px ); + bB.v.Y = b2MulAddW( bB.v.Y, c->invMassB, Py ); + bB.w = b2MulAddW( bB.w, c->invIB, b2SubW( b2MulW( rB.X, Py ), b2MulW( rB.Y, Px ) ) ); + } + + b2ScatterBodies( states, c->indexA, &bA ); + b2ScatterBodies( states, c->indexB, &bB ); + } + + b2TracyCZoneEnd( restitution ); +} + +#if B2_SIMD_WIDTH == 8 + +void b2StoreImpulsesTask( int startIndex, int endIndex, b2StepContext* context ) +{ + b2TracyCZoneNC( store_impulses, "Store", b2_colorFirebrick, true ); + + b2ContactSim** contacts = context->contacts; + const b2ContactConstraintSIMD* constraints = context->simdContactConstraints; + + b2Manifold dummy = { 0 }; + + for ( int i = startIndex; i < endIndex; ++i ) + { + const b2ContactConstraintSIMD* c = constraints + i; + const float* normalImpulse1 = (float*)&c->normalImpulse1; + const float* normalImpulse2 = (float*)&c->normalImpulse2; + const float* tangentImpulse1 = (float*)&c->tangentImpulse1; + const float* tangentImpulse2 = (float*)&c->tangentImpulse2; + const float* maxNormalImpulse1 = (float*)&c->maxNormalImpulse1; + const float* maxNormalImpulse2 = (float*)&c->maxNormalImpulse2; + const float* normalVelocity1 = (float*)&c->relativeVelocity1; + const float* normalVelocity2 = (float*)&c->relativeVelocity2; + + int base = 8 * i; + b2Manifold* m0 = contacts[base + 0] == NULL ? &dummy : &contacts[base + 0]->manifold; + b2Manifold* m1 = contacts[base + 1] == NULL ? &dummy : &contacts[base + 1]->manifold; + b2Manifold* m2 = contacts[base + 2] == NULL ? &dummy : &contacts[base + 2]->manifold; + b2Manifold* m3 = contacts[base + 3] == NULL ? &dummy : &contacts[base + 3]->manifold; + b2Manifold* m4 = contacts[base + 4] == NULL ? &dummy : &contacts[base + 4]->manifold; + b2Manifold* m5 = contacts[base + 5] == NULL ? &dummy : &contacts[base + 5]->manifold; + b2Manifold* m6 = contacts[base + 6] == NULL ? &dummy : &contacts[base + 6]->manifold; + b2Manifold* m7 = contacts[base + 7] == NULL ? &dummy : &contacts[base + 7]->manifold; + + m0->points[0].normalImpulse = normalImpulse1[0]; + m0->points[0].tangentImpulse = tangentImpulse1[0]; + m0->points[0].maxNormalImpulse = maxNormalImpulse1[0]; + m0->points[0].normalVelocity = normalVelocity1[0]; + + m0->points[1].normalImpulse = normalImpulse2[0]; + m0->points[1].tangentImpulse = tangentImpulse2[0]; + m0->points[1].maxNormalImpulse = maxNormalImpulse2[0]; + m0->points[1].normalVelocity = normalVelocity2[0]; + + m1->points[0].normalImpulse = normalImpulse1[1]; + m1->points[0].tangentImpulse = tangentImpulse1[1]; + m1->points[0].maxNormalImpulse = maxNormalImpulse1[1]; + m1->points[0].normalVelocity = normalVelocity1[1]; + + m1->points[1].normalImpulse = normalImpulse2[1]; + m1->points[1].tangentImpulse = tangentImpulse2[1]; + m1->points[1].maxNormalImpulse = maxNormalImpulse2[1]; + m1->points[1].normalVelocity = normalVelocity2[1]; + + m2->points[0].normalImpulse = normalImpulse1[2]; + m2->points[0].tangentImpulse = tangentImpulse1[2]; + m2->points[0].maxNormalImpulse = maxNormalImpulse1[2]; + m2->points[0].normalVelocity = normalVelocity1[2]; + + m2->points[1].normalImpulse = normalImpulse2[2]; + m2->points[1].tangentImpulse = tangentImpulse2[2]; + m2->points[1].maxNormalImpulse = maxNormalImpulse2[2]; + m2->points[1].normalVelocity = normalVelocity2[2]; + + m3->points[0].normalImpulse = normalImpulse1[3]; + m3->points[0].tangentImpulse = tangentImpulse1[3]; + m3->points[0].maxNormalImpulse = maxNormalImpulse1[3]; + m3->points[0].normalVelocity = normalVelocity1[3]; + + m3->points[1].normalImpulse = normalImpulse2[3]; + m3->points[1].tangentImpulse = tangentImpulse2[3]; + m3->points[1].maxNormalImpulse = maxNormalImpulse2[3]; + m3->points[1].normalVelocity = normalVelocity2[3]; + + m4->points[0].normalImpulse = normalImpulse1[4]; + m4->points[0].tangentImpulse = tangentImpulse1[4]; + m4->points[0].maxNormalImpulse = maxNormalImpulse1[4]; + m4->points[0].normalVelocity = normalVelocity1[4]; + + m4->points[1].normalImpulse = normalImpulse2[4]; + m4->points[1].tangentImpulse = tangentImpulse2[4]; + m4->points[1].maxNormalImpulse = maxNormalImpulse2[4]; + m4->points[1].normalVelocity = normalVelocity2[4]; + + m5->points[0].normalImpulse = normalImpulse1[5]; + m5->points[0].tangentImpulse = tangentImpulse1[5]; + m5->points[0].maxNormalImpulse = maxNormalImpulse1[5]; + m5->points[0].normalVelocity = normalVelocity1[5]; + + m5->points[1].normalImpulse = normalImpulse2[5]; + m5->points[1].tangentImpulse = tangentImpulse2[5]; + m5->points[1].maxNormalImpulse = maxNormalImpulse2[5]; + m5->points[1].normalVelocity = normalVelocity2[5]; + + m6->points[0].normalImpulse = normalImpulse1[6]; + m6->points[0].tangentImpulse = tangentImpulse1[6]; + m6->points[0].maxNormalImpulse = maxNormalImpulse1[6]; + m6->points[0].normalVelocity = normalVelocity1[6]; + + m6->points[1].normalImpulse = normalImpulse2[6]; + m6->points[1].tangentImpulse = tangentImpulse2[6]; + m6->points[1].maxNormalImpulse = maxNormalImpulse2[6]; + m6->points[1].normalVelocity = normalVelocity2[6]; + + m7->points[0].normalImpulse = normalImpulse1[7]; + m7->points[0].tangentImpulse = tangentImpulse1[7]; + m7->points[0].maxNormalImpulse = maxNormalImpulse1[7]; + m7->points[0].normalVelocity = normalVelocity1[7]; + + m7->points[1].normalImpulse = normalImpulse2[7]; + m7->points[1].tangentImpulse = tangentImpulse2[7]; + m7->points[1].maxNormalImpulse = maxNormalImpulse2[7]; + m7->points[1].normalVelocity = normalVelocity2[7]; + } + + b2TracyCZoneEnd( store_impulses ); +} + +#else + +void b2StoreImpulsesTask( int startIndex, int endIndex, b2StepContext* context ) +{ + b2TracyCZoneNC( store_impulses, "Store", b2_colorFirebrick, true ); + + b2ContactSim** contacts = context->contacts; + const b2ContactConstraintSIMD* constraints = context->simdContactConstraints; + + b2Manifold dummy = { 0 }; + + for ( int i = startIndex; i < endIndex; ++i ) + { + const b2ContactConstraintSIMD* c = constraints + i; + const float* normalImpulse1 = (float*)&c->normalImpulse1; + const float* normalImpulse2 = (float*)&c->normalImpulse2; + const float* tangentImpulse1 = (float*)&c->tangentImpulse1; + const float* tangentImpulse2 = (float*)&c->tangentImpulse2; + const float* maxNormalImpulse1 = (float*)&c->maxNormalImpulse1; + const float* maxNormalImpulse2 = (float*)&c->maxNormalImpulse2; + const float* normalVelocity1 = (float*)&c->relativeVelocity1; + const float* normalVelocity2 = (float*)&c->relativeVelocity2; + + int base = 4 * i; + b2Manifold* m0 = contacts[base + 0] == NULL ? &dummy : &contacts[base + 0]->manifold; + b2Manifold* m1 = contacts[base + 1] == NULL ? &dummy : &contacts[base + 1]->manifold; + b2Manifold* m2 = contacts[base + 2] == NULL ? &dummy : &contacts[base + 2]->manifold; + b2Manifold* m3 = contacts[base + 3] == NULL ? &dummy : &contacts[base + 3]->manifold; + + m0->points[0].normalImpulse = normalImpulse1[0]; + m0->points[0].tangentImpulse = tangentImpulse1[0]; + m0->points[0].maxNormalImpulse = maxNormalImpulse1[0]; + m0->points[0].normalVelocity = normalVelocity1[0]; + + m0->points[1].normalImpulse = normalImpulse2[0]; + m0->points[1].tangentImpulse = tangentImpulse2[0]; + m0->points[1].maxNormalImpulse = maxNormalImpulse2[0]; + m0->points[1].normalVelocity = normalVelocity2[0]; + + m1->points[0].normalImpulse = normalImpulse1[1]; + m1->points[0].tangentImpulse = tangentImpulse1[1]; + m1->points[0].maxNormalImpulse = maxNormalImpulse1[1]; + m1->points[0].normalVelocity = normalVelocity1[1]; + + m1->points[1].normalImpulse = normalImpulse2[1]; + m1->points[1].tangentImpulse = tangentImpulse2[1]; + m1->points[1].maxNormalImpulse = maxNormalImpulse2[1]; + m1->points[1].normalVelocity = normalVelocity2[1]; + + m2->points[0].normalImpulse = normalImpulse1[2]; + m2->points[0].tangentImpulse = tangentImpulse1[2]; + m2->points[0].maxNormalImpulse = maxNormalImpulse1[2]; + m2->points[0].normalVelocity = normalVelocity1[2]; + + m2->points[1].normalImpulse = normalImpulse2[2]; + m2->points[1].tangentImpulse = tangentImpulse2[2]; + m2->points[1].maxNormalImpulse = maxNormalImpulse2[2]; + m2->points[1].normalVelocity = normalVelocity2[2]; + + m3->points[0].normalImpulse = normalImpulse1[3]; + m3->points[0].tangentImpulse = tangentImpulse1[3]; + m3->points[0].maxNormalImpulse = maxNormalImpulse1[3]; + m3->points[0].normalVelocity = normalVelocity1[3]; + + m3->points[1].normalImpulse = normalImpulse2[3]; + m3->points[1].tangentImpulse = tangentImpulse2[3]; + m3->points[1].maxNormalImpulse = maxNormalImpulse2[3]; + m3->points[1].normalVelocity = normalVelocity2[3]; + } + + b2TracyCZoneEnd( store_impulses ); +} + +#endif diff --git a/3rdparty/box2d/src/contact_solver.h b/3rdparty/box2d/src/contact_solver.h new file mode 100644 index 000000000000..911e6a9d840f --- /dev/null +++ b/3rdparty/box2d/src/contact_solver.h @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include "solver.h" + +typedef struct b2ContactSim b2ContactSim; + +typedef struct b2ContactConstraintPoint +{ + b2Vec2 anchorA, anchorB; + float baseSeparation; + float relativeVelocity; + float normalImpulse; + float tangentImpulse; + float maxNormalImpulse; + float normalMass; + float tangentMass; +} b2ContactConstraintPoint; + +typedef struct b2ContactConstraint +{ + int indexA; + int indexB; + b2ContactConstraintPoint points[2]; + b2Vec2 normal; + float invMassA, invMassB; + float invIA, invIB; + float friction; + float restitution; + b2Softness softness; + int pointCount; +} b2ContactConstraint; + +int b2GetContactConstraintSIMDByteCount( void ); + +// Overflow contacts don't fit into the constraint graph coloring +void b2PrepareOverflowContacts( b2StepContext* context ); +void b2WarmStartOverflowContacts( b2StepContext* context ); +void b2SolveOverflowContacts( b2StepContext* context, bool useBias ); +void b2ApplyOverflowRestitution( b2StepContext* context ); +void b2StoreOverflowImpulses( b2StepContext* context ); + +// Contacts that live within the constraint graph coloring +void b2PrepareContactsTask( int startIndex, int endIndex, b2StepContext* context ); +void b2WarmStartContactsTask( int startIndex, int endIndex, b2StepContext* context, int colorIndex ); +void b2SolveContactsTask( int startIndex, int endIndex, b2StepContext* context, int colorIndex, bool useBias ); +void b2ApplyRestitutionTask( int startIndex, int endIndex, b2StepContext* context, int colorIndex ); +void b2StoreImpulsesTask( int startIndex, int endIndex, b2StepContext* context ); diff --git a/3rdparty/box2d/src/core.c b/3rdparty/box2d/src/core.c new file mode 100644 index 000000000000..030fb72827ae --- /dev/null +++ b/3rdparty/box2d/src/core.c @@ -0,0 +1,169 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#include "core.h" + +#if defined( B2_COMPILER_MSVC ) +#define _CRTDBG_MAP_ALLOC +#include +#include +#else +#include +#endif + +#include +#include + +#ifdef BOX2D_PROFILE + +#include +#define b2TracyCAlloc( ptr, size ) TracyCAlloc( ptr, size ) +#define b2TracyCFree( ptr ) TracyCFree( ptr ) + +#else + +#define b2TracyCAlloc( ptr, size ) +#define b2TracyCFree( ptr ) + +#endif + +#include "box2d/math_functions.h" + +#include + +// This allows the user to change the length units at runtime +float b2_lengthUnitsPerMeter = 1.0f; + +void b2SetLengthUnitsPerMeter( float lengthUnits ) +{ + B2_ASSERT( b2IsValid( lengthUnits ) && lengthUnits > 0.0f ); + b2_lengthUnitsPerMeter = lengthUnits; +} + +float b2GetLengthUnitsPerMeter( void ) +{ + return b2_lengthUnitsPerMeter; +} + +static int b2DefaultAssertFcn( const char* condition, const char* fileName, int lineNumber ) +{ + printf( "BOX2D ASSERTION: %s, %s, line %d\n", condition, fileName, lineNumber ); + + // return non-zero to break to debugger + return 1; +} + +b2AssertFcn* b2AssertHandler = b2DefaultAssertFcn; + +void b2SetAssertFcn( b2AssertFcn* assertFcn ) +{ + B2_ASSERT( assertFcn != NULL ); + b2AssertHandler = assertFcn; +} + +b2Version b2GetVersion( void ) +{ + return ( b2Version ){ 3, 1, 0 }; +} + +static b2AllocFcn* b2_allocFcn = NULL; +static b2FreeFcn* b2_freeFcn = NULL; + +static _Atomic int b2_byteCount; + +void b2SetAllocator( b2AllocFcn* allocFcn, b2FreeFcn* freeFcn ) +{ + b2_allocFcn = allocFcn; + b2_freeFcn = freeFcn; +} + +// Use 32 byte alignment for everything. Works with 256bit SIMD. +#define B2_ALIGNMENT 32 + +void* b2Alloc( int size ) +{ + if (size == 0) + { + return NULL; + } + + // This could cause some sharing issues, however Box2D rarely calls b2Alloc. + atomic_fetch_add_explicit( &b2_byteCount, size, memory_order_relaxed ); + + // Allocation must be a multiple of 32 or risk a seg fault + // https://en.cppreference.com/w/c/memory/aligned_alloc + int size32 = ( ( size - 1 ) | 0x1F ) + 1; + + if ( b2_allocFcn != NULL ) + { + void* ptr = b2_allocFcn( size32, B2_ALIGNMENT ); + b2TracyCAlloc( ptr, size ); + + B2_ASSERT( ptr != NULL ); + B2_ASSERT( ( (uintptr_t)ptr & 0x1F ) == 0 ); + + return ptr; + } + +#ifdef B2_PLATFORM_WINDOWS + void* ptr = _aligned_malloc( size32, B2_ALIGNMENT ); +#elif defined( B2_PLATFORM_ANDROID ) + void* ptr = NULL; + if ( posix_memalign( &ptr, B2_ALIGNMENT, size32 ) != 0 ) + { + // allocation failed, exit the application + exit( EXIT_FAILURE ); + } +#else + void* ptr = aligned_alloc( B2_ALIGNMENT, size32 ); +#endif + + b2TracyCAlloc( ptr, size ); + + B2_ASSERT( ptr != NULL ); + B2_ASSERT( ( (uintptr_t)ptr & 0x1F ) == 0 ); + + return ptr; +} + +void b2Free( void* mem, int size ) +{ + if ( mem == NULL ) + { + return; + } + + b2TracyCFree( mem ); + + if ( b2_freeFcn != NULL ) + { + b2_freeFcn( mem ); + } + else + { +#ifdef B2_PLATFORM_WINDOWS + _aligned_free( mem ); +#else + free( mem ); +#endif + } + + atomic_fetch_sub_explicit( &b2_byteCount, size, memory_order_relaxed ); +} + +void* b2GrowAlloc( void* oldMem, int oldSize, int newSize ) +{ + B2_ASSERT( newSize > oldSize ); + void* newMem = b2Alloc( newSize ); + if ( oldSize > 0 ) + { + memcpy( newMem, oldMem, oldSize ); + b2Free( oldMem, oldSize ); + } + return newMem; +} + +int b2GetByteCount( void ) +{ + return atomic_load_explicit( &b2_byteCount, memory_order_relaxed ); +} diff --git a/3rdparty/box2d/src/core.h b/3rdparty/box2d/src/core.h new file mode 100644 index 000000000000..283a224292b7 --- /dev/null +++ b/3rdparty/box2d/src/core.h @@ -0,0 +1,183 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include "box2d/base.h" + +// clang-format off + +#define B2_NULL_INDEX ( -1 ) + +// for performance comparisons +#define B2_RESTRICT restrict + +#ifdef NDEBUG + #define B2_DEBUG 0 +#else + #define B2_DEBUG 1 +#endif + +#if defined( BOX2D_VALIDATE ) && !defined( NDEBUG ) + #define B2_VALIDATE 1 +#else + #define B2_VALIDATE 0 +#endif + +// Define platform +#if defined( _WIN64 ) + #define B2_PLATFORM_WINDOWS +#elif defined( __ANDROID__ ) + #define B2_PLATFORM_ANDROID +#elif defined( __linux__ ) + #define B2_PLATFORM_LINUX +#elif defined( __APPLE__ ) + #include + #if defined( TARGET_OS_IPHONE ) && !TARGET_OS_IPHONE + #define B2_PLATFORM_MACOS + #else + #define B2_PLATFORM_IOS + #endif +#elif defined( __EMSCRIPTEN__ ) + #define B2_PLATFORM_WASM +#else + #define B2_PLATFORM_UNKNOWN +#endif + +// Define CPU +#if defined( __x86_64__ ) || defined( _M_X64 ) || defined( __i386__ ) || defined( _M_IX86 ) + #define B2_CPU_X86_X64 +#elif defined( __aarch64__ ) || defined( _M_ARM64 ) || defined( __arm__ ) || defined( _M_ARM ) + #define B2_CPU_ARM +#elif defined( __EMSCRIPTEN__ ) + #define B2_CPU_WASM +#else + #define B2_CPU_UNKNOWN +#endif + +// Define SIMD +#if defined( BOX2D_ENABLE_SIMD ) + #if defined( B2_CPU_X86_X64 ) + #if defined( BOX2D_AVX2 ) + #define B2_SIMD_AVX2 + #define B2_SIMD_WIDTH 8 + #else + #define B2_SIMD_SSE2 + #define B2_SIMD_WIDTH 4 + #endif + #elif defined( B2_CPU_ARM ) + #define B2_SIMD_NEON + #define B2_SIMD_WIDTH 4 + #elif defined( B2_CPU_WASM ) + #define B2_CPU_WASM + #define B2_SIMD_SSE2 + #define B2_SIMD_WIDTH 4 + #else + #define B2_SIMD_NONE + #define B2_SIMD_WIDTH 4 + #endif +#else + #define B2_SIMD_NONE + #define B2_SIMD_WIDTH 4 +#endif + +// Define compiler +#if defined( __clang__ ) + #define B2_COMPILER_CLANG +#elif defined( __GNUC__ ) + #define B2_COMPILER_GCC +#elif defined( _MSC_VER ) + #define B2_COMPILER_MSVC +#endif + +// see https://github.com/scottt/debugbreak +#if defined( B2_COMPILER_MSVC ) + #define B2_BREAKPOINT __debugbreak() +#elif defined( B2_COMPILER_GCC ) || defined( B2_COMPILER_CLANG ) + #define B2_BREAKPOINT __builtin_trap() +#else + // Unknown compiler + #include + #definef B2_BREAKPOINT assert(0) +#endif + +#if !defined( NDEBUG ) || defined( B2_ENABLE_ASSERT ) + extern b2AssertFcn* b2AssertHandler; + #define B2_ASSERT( condition ) \ + do \ + { \ + if ( !( condition ) && b2AssertHandler( #condition, __FILE__, (int)__LINE__ ) ) \ + B2_BREAKPOINT; \ + } \ + while ( 0 ) +#else + #define B2_ASSERT( ... ) ( (void)0 ) +#endif + +/// Tracy profiler instrumentation +/// https://github.com/wolfpld/tracy +#ifdef BOX2D_PROFILE + #include + #define b2TracyCZoneC( ctx, color, active ) TracyCZoneC( ctx, color, active ) + #define b2TracyCZoneNC( ctx, name, color, active ) TracyCZoneNC( ctx, name, color, active ) + #define b2TracyCZoneEnd( ctx ) TracyCZoneEnd( ctx ) +#else + #define b2TracyCZoneC( ctx, color, active ) + #define b2TracyCZoneNC( ctx, name, color, active ) + #define b2TracyCZoneEnd( ctx ) +#endif + +// clang-format on + +extern float b2_lengthUnitsPerMeter; + +// Used to detect bad values. Positions greater than about 16km will have precision +// problems, so 100km as a limit should be fine in all cases. +#define b2_huge ( 100000.0f * b2_lengthUnitsPerMeter ) + +// Maximum parallel workers. Used to size some static arrays. +#define b2_maxWorkers 64 + +// Maximum number of colors in the constraint graph. Constraints that cannot +// find a color are added to the overflow set which are solved single-threaded. +#define b2_graphColorCount 12 + +// A small length used as a collision and constraint tolerance. Usually it is +// chosen to be numerically significant, but visually insignificant. In meters. +// @warning modifying this can have a significant impact on stability +#define b2_linearSlop ( 0.005f * b2_lengthUnitsPerMeter ) + +// Maximum number of simultaneous worlds that can be allocated +#define b2_maxWorlds 128 + +// The maximum rotation of a body per time step. This limit is very large and is used +// to prevent numerical problems. You shouldn't need to adjust this. +// @warning increasing this to 0.5f * b2_pi or greater will break continuous collision. +#define b2_maxRotation ( 0.25f * b2_pi ) + +// @warning modifying this can have a significant impact on performance and stability +#define b2_speculativeDistance ( 4.0f * b2_linearSlop ) + +// This is used to fatten AABBs in the dynamic tree. This allows proxies +// to move by a small amount without triggering a tree adjustment. +// This is in meters. +// @warning modifying this can have a significant impact on performance +#define b2_aabbMargin ( 0.1f * b2_lengthUnitsPerMeter ) + +// The time that a body must be still before it will go to sleep. In seconds. +#define b2_timeToSleep 0.5f + +// Returns the number of elements of an array +#define B2_ARRAY_COUNT( A ) (int)( sizeof( A ) / sizeof( A[0] ) ) + +// Used to prevent the compiler from warning about unused variables +#define B2_MAYBE_UNUSED( x ) ( (void)( x ) ) + +// Use to validate definitions. Do not take my cookie. +#define B2_SECRET_COOKIE 1152023 + +#define b2CheckDef( DEF ) B2_ASSERT( DEF->internalValue == B2_SECRET_COOKIE ) + +void* b2Alloc( int size ); +void b2Free( void* mem, int size ); +void* b2GrowAlloc( void* oldMem, int oldSize, int newSize ); diff --git a/3rdparty/box2d/src/ctz.h b/3rdparty/box2d/src/ctz.h new file mode 100644 index 000000000000..9959527c0871 --- /dev/null +++ b/3rdparty/box2d/src/ctz.h @@ -0,0 +1,112 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +#if defined( _MSC_VER ) && !defined( __clang__ ) + #include + +// https://en.wikipedia.org/wiki/Find_first_set + +static inline uint32_t b2CTZ32( uint32_t block ) +{ + unsigned long index; + _BitScanForward( &index, block ); + return index; +} + +// This function doesn't need to be fast, so using the Ivy Bridge fallback. +static inline uint32_t b2CLZ32( uint32_t value ) +{ + #if 1 + + // Use BSR (Bit Scan Reverse) which is available on Ivy Bridge + unsigned long index; + if ( _BitScanReverse( &index, value ) ) + { + // BSR gives the index of the most significant 1-bit + // We need to invert this to get the number of leading zeros + return 31 - index; + } + else + { + // If x is 0, BSR sets the zero flag and doesn't modify index + // LZCNT should return 32 for an input of 0 + return 32; + } + + #else + + return __lzcnt( value ); + + #endif +} + +static inline uint32_t b2CTZ64( uint64_t block ) +{ + unsigned long index; + + #ifdef _WIN64 + _BitScanForward64( &index, block ); + #else + // 32-bit fall back + if ( (uint32_t)block != 0 ) + { + _BitScanForward( &index, (uint32_t)block ); + } + else + { + _BitScanForward( &index, (uint32_t)( block >> 32 ) ); + index += 32; + } + #endif + + return index; +} + +#else + +static inline uint32_t b2CTZ32( uint32_t block ) +{ + return __builtin_ctz( block ); +} + +static inline uint32_t b2CLZ32( uint32_t value ) +{ + return __builtin_clz( value ); +} + +static inline uint32_t b2CTZ64( uint64_t block ) +{ + return __builtin_ctzll( block ); +} + +#endif + +static inline bool b2IsPowerOf2( int x ) +{ + return ( x & ( x - 1 ) ) == 0; +} + +static inline int b2BoundingPowerOf2( int x ) +{ + if ( x <= 1 ) + { + return 1; + } + + return 32 - (int)b2CLZ32( (uint32_t)x - 1 ); +} + +static inline int b2RoundUpPowerOf2( int x ) +{ + if ( x <= 1 ) + { + return 1; + } + + return 1 << ( 32 - (int)b2CLZ32( (uint32_t)x - 1 ) ); +} diff --git a/3rdparty/box2d/src/distance.c b/3rdparty/box2d/src/distance.c new file mode 100644 index 000000000000..25f2abc2c1c1 --- /dev/null +++ b/3rdparty/box2d/src/distance.c @@ -0,0 +1,1237 @@ + +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#include "core.h" + +#include "box2d/collision.h" +#include "box2d/math_functions.h" + +#include +#include + +b2Transform b2GetSweepTransform( const b2Sweep* sweep, float time ) +{ + // https://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/ + b2Transform xf; + xf.p = b2Add( b2MulSV( 1.0f - time, sweep->c1 ), b2MulSV( time, sweep->c2 ) ); + + b2Rot q = { + ( 1.0f - time ) * sweep->q1.c + time * sweep->q2.c, + ( 1.0f - time ) * sweep->q1.s + time * sweep->q2.s, + }; + + xf.q = b2NormalizeRot( q ); + + // Shift to origin + xf.p = b2Sub( xf.p, b2RotateVector( xf.q, sweep->localCenter ) ); + return xf; +} + +/// Follows Ericson 5.1.9 Closest Points of Two Line Segments +b2SegmentDistanceResult b2SegmentDistance( b2Vec2 p1, b2Vec2 q1, b2Vec2 p2, b2Vec2 q2 ) +{ + b2SegmentDistanceResult result = { 0 }; + + b2Vec2 d1 = b2Sub( q1, p1 ); + b2Vec2 d2 = b2Sub( q2, p2 ); + b2Vec2 r = b2Sub( p1, p2 ); + float dd1 = b2Dot( d1, d1 ); + float dd2 = b2Dot( d2, d2 ); + float rd1 = b2Dot( r, d1 ); + float rd2 = b2Dot( r, d2 ); + + const float epsSqr = FLT_EPSILON * FLT_EPSILON; + + if ( dd1 < epsSqr || dd2 < epsSqr ) + { + // Handle all degeneracies + if ( dd1 >= epsSqr ) + { + // Segment 2 is degenerate + result.fraction1 = b2ClampFloat( -rd1 / dd1, 0.0f, 1.0f ); + result.fraction2 = 0.0f; + } + else if ( dd2 >= epsSqr ) + { + // Segment 1 is degenerate + result.fraction1 = 0.0f; + result.fraction2 = b2ClampFloat( rd2 / dd2, 0.0f, 1.0f ); + } + else + { + result.fraction1 = 0.0f; + result.fraction2 = 0.0f; + } + } + else + { + // Non-degenerate segments + float d12 = b2Dot( d1, d2 ); + + float denom = dd1 * dd2 - d12 * d12; + + // Fraction on segment 1 + float f1 = 0.0f; + if ( denom != 0.0f ) + { + // not parallel + f1 = b2ClampFloat( ( d12 * rd2 - rd1 * dd2 ) / denom, 0.0f, 1.0f ); + } + + // Compute point on segment 2 closest to p1 + f1 * d1 + float f2 = ( d12 * f1 + rd2 ) / dd2; + + // Clamping of segment 2 requires a do over on segment 1 + if ( f2 < 0.0f ) + { + f2 = 0.0f; + f1 = b2ClampFloat( -rd1 / dd1, 0.0f, 1.0f ); + } + else if ( f2 > 1.0f ) + { + f2 = 1.0f; + f1 = b2ClampFloat( ( d12 - rd1 ) / dd1, 0.0f, 1.0f ); + } + + result.fraction1 = f1; + result.fraction2 = f2; + } + + result.closest1 = b2MulAdd( p1, result.fraction1, d1 ); + result.closest2 = b2MulAdd( p2, result.fraction2, d2 ); + result.distanceSquared = b2DistanceSquared( result.closest1, result.closest2 ); + return result; +} + +// GJK using Voronoi regions (Christer Ericson) and Barycentric coordinates. +// todo try not copying +b2DistanceProxy b2MakeProxy( const b2Vec2* vertices, int count, float radius ) +{ + count = b2MinInt( count, b2_maxPolygonVertices ); + b2DistanceProxy proxy; + for ( int i = 0; i < count; ++i ) + { + proxy.points[i] = vertices[i]; + } + proxy.count = count; + proxy.radius = radius; + return proxy; +} + +static b2Vec2 b2Weight2( float a1, b2Vec2 w1, float a2, b2Vec2 w2 ) +{ + return ( b2Vec2 ){ a1 * w1.x + a2 * w2.x, a1 * w1.y + a2 * w2.y }; +} + +static b2Vec2 b2Weight3( float a1, b2Vec2 w1, float a2, b2Vec2 w2, float a3, b2Vec2 w3 ) +{ + return ( b2Vec2 ){ a1 * w1.x + a2 * w2.x + a3 * w3.x, a1 * w1.y + a2 * w2.y + a3 * w3.y }; +} + +static int b2FindSupport( const b2DistanceProxy* proxy, b2Vec2 direction ) +{ + int bestIndex = 0; + float bestValue = b2Dot( proxy->points[0], direction ); + for ( int i = 1; i < proxy->count; ++i ) + { + float value = b2Dot( proxy->points[i], direction ); + if ( value > bestValue ) + { + bestIndex = i; + bestValue = value; + } + } + + return bestIndex; +} + +static b2Simplex b2MakeSimplexFromCache( const b2DistanceCache* cache, const b2DistanceProxy* proxyA, b2Transform transformA, + const b2DistanceProxy* proxyB, b2Transform transformB ) +{ + B2_ASSERT( cache->count <= 3 ); + b2Simplex s; + + // Copy data from cache. + s.count = cache->count; + + b2SimplexVertex* vertices[] = { &s.v1, &s.v2, &s.v3 }; + for ( int i = 0; i < s.count; ++i ) + { + b2SimplexVertex* v = vertices[i]; + v->indexA = cache->indexA[i]; + v->indexB = cache->indexB[i]; + b2Vec2 wALocal = proxyA->points[v->indexA]; + b2Vec2 wBLocal = proxyB->points[v->indexB]; + v->wA = b2TransformPoint( transformA, wALocal ); + v->wB = b2TransformPoint( transformB, wBLocal ); + v->w = b2Sub( v->wB, v->wA ); + + // invalid + v->a = -1.0f; + } + + // If the cache is empty or invalid ... + if ( s.count == 0 ) + { + b2SimplexVertex* v = vertices[0]; + v->indexA = 0; + v->indexB = 0; + b2Vec2 wALocal = proxyA->points[0]; + b2Vec2 wBLocal = proxyB->points[0]; + v->wA = b2TransformPoint( transformA, wALocal ); + v->wB = b2TransformPoint( transformB, wBLocal ); + v->w = b2Sub( v->wB, v->wA ); + v->a = 1.0f; + s.count = 1; + } + + return s; +} + +static void b2MakeSimplexCache( b2DistanceCache* cache, const b2Simplex* simplex ) +{ + cache->count = (uint16_t)simplex->count; + const b2SimplexVertex* vertices[] = { &simplex->v1, &simplex->v2, &simplex->v3 }; + for ( int i = 0; i < simplex->count; ++i ) + { + cache->indexA[i] = (uint8_t)vertices[i]->indexA; + cache->indexB[i] = (uint8_t)vertices[i]->indexB; + } +} + +// Compute the search direction from the current simplex. +// This is the vector pointing from the closest point on the simplex +// to the origin. +// A more accurate search direction can be computed by using the normal +// vector of the simplex. For example, the normal vector of a line segment +// can be computed more accurately because it does not involve barycentric +// coordinates. +b2Vec2 b2ComputeSimplexSearchDirection( const b2Simplex* simplex ) +{ + switch ( simplex->count ) + { + case 1: + return b2Neg( simplex->v1.w ); + + case 2: + { + b2Vec2 e12 = b2Sub( simplex->v2.w, simplex->v1.w ); + float sgn = b2Cross( e12, b2Neg( simplex->v1.w ) ); + if ( sgn > 0.0f ) + { + // Origin is left of e12. + return b2LeftPerp( e12 ); + } + else + { + // Origin is right of e12. + return b2RightPerp( e12 ); + } + } + + default: + B2_ASSERT( false ); + return b2Vec2_zero; + } +} + +b2Vec2 b2ComputeSimplexClosestPoint( const b2Simplex* s ) +{ + switch ( s->count ) + { + case 0: + B2_ASSERT( false ); + return b2Vec2_zero; + + case 1: + return s->v1.w; + + case 2: + return b2Weight2( s->v1.a, s->v1.w, s->v2.a, s->v2.w ); + + case 3: + return b2Vec2_zero; + + default: + B2_ASSERT( false ); + return b2Vec2_zero; + } +} + +void b2ComputeSimplexWitnessPoints( b2Vec2* a, b2Vec2* b, const b2Simplex* s ) +{ + switch ( s->count ) + { + case 0: + B2_ASSERT( false ); + break; + + case 1: + *a = s->v1.wA; + *b = s->v1.wB; + break; + + case 2: + *a = b2Weight2( s->v1.a, s->v1.wA, s->v2.a, s->v2.wA ); + *b = b2Weight2( s->v1.a, s->v1.wB, s->v2.a, s->v2.wB ); + break; + + case 3: + *a = b2Weight3( s->v1.a, s->v1.wA, s->v2.a, s->v2.wA, s->v3.a, s->v3.wA ); + // TODO_ERIN why are these not equal? + //*b = b2Weight3(s->v1.a, s->v1.wB, s->v2.a, s->v2.wB, s->v3.a, s->v3.wB); + *b = *a; + break; + + default: + B2_ASSERT( false ); + break; + } +} + +// Solve a line segment using barycentric coordinates. +// +// p = a1 * w1 + a2 * w2 +// a1 + a2 = 1 +// +// The vector from the origin to the closest point on the line is +// perpendicular to the line. +// e12 = w2 - w1 +// dot(p, e) = 0 +// a1 * dot(w1, e) + a2 * dot(w2, e) = 0 +// +// 2-by-2 linear system +// [1 1 ][a1] = [1] +// [w1.e12 w2.e12][a2] = [0] +// +// Define +// d12_1 = dot(w2, e12) +// d12_2 = -dot(w1, e12) +// d12 = d12_1 + d12_2 +// +// Solution +// a1 = d12_1 / d12 +// a2 = d12_2 / d12 +void b2SolveSimplex2( b2Simplex* s ) +{ + b2Vec2 w1 = s->v1.w; + b2Vec2 w2 = s->v2.w; + b2Vec2 e12 = b2Sub( w2, w1 ); + + // w1 region + float d12_2 = -b2Dot( w1, e12 ); + if ( d12_2 <= 0.0f ) + { + // a2 <= 0, so we clamp it to 0 + s->v1.a = 1.0f; + s->count = 1; + return; + } + + // w2 region + float d12_1 = b2Dot( w2, e12 ); + if ( d12_1 <= 0.0f ) + { + // a1 <= 0, so we clamp it to 0 + s->v2.a = 1.0f; + s->count = 1; + s->v1 = s->v2; + return; + } + + // Must be in e12 region. + float inv_d12 = 1.0f / ( d12_1 + d12_2 ); + s->v1.a = d12_1 * inv_d12; + s->v2.a = d12_2 * inv_d12; + s->count = 2; +} + +void b2SolveSimplex3( b2Simplex* s ) +{ + b2Vec2 w1 = s->v1.w; + b2Vec2 w2 = s->v2.w; + b2Vec2 w3 = s->v3.w; + + // Edge12 + // [1 1 ][a1] = [1] + // [w1.e12 w2.e12][a2] = [0] + // a3 = 0 + b2Vec2 e12 = b2Sub( w2, w1 ); + float w1e12 = b2Dot( w1, e12 ); + float w2e12 = b2Dot( w2, e12 ); + float d12_1 = w2e12; + float d12_2 = -w1e12; + + // Edge13 + // [1 1 ][a1] = [1] + // [w1.e13 w3.e13][a3] = [0] + // a2 = 0 + b2Vec2 e13 = b2Sub( w3, w1 ); + float w1e13 = b2Dot( w1, e13 ); + float w3e13 = b2Dot( w3, e13 ); + float d13_1 = w3e13; + float d13_2 = -w1e13; + + // Edge23 + // [1 1 ][a2] = [1] + // [w2.e23 w3.e23][a3] = [0] + // a1 = 0 + b2Vec2 e23 = b2Sub( w3, w2 ); + float w2e23 = b2Dot( w2, e23 ); + float w3e23 = b2Dot( w3, e23 ); + float d23_1 = w3e23; + float d23_2 = -w2e23; + + // Triangle123 + float n123 = b2Cross( e12, e13 ); + + float d123_1 = n123 * b2Cross( w2, w3 ); + float d123_2 = n123 * b2Cross( w3, w1 ); + float d123_3 = n123 * b2Cross( w1, w2 ); + + // w1 region + if ( d12_2 <= 0.0f && d13_2 <= 0.0f ) + { + s->v1.a = 1.0f; + s->count = 1; + return; + } + + // e12 + if ( d12_1 > 0.0f && d12_2 > 0.0f && d123_3 <= 0.0f ) + { + float inv_d12 = 1.0f / ( d12_1 + d12_2 ); + s->v1.a = d12_1 * inv_d12; + s->v2.a = d12_2 * inv_d12; + s->count = 2; + return; + } + + // e13 + if ( d13_1 > 0.0f && d13_2 > 0.0f && d123_2 <= 0.0f ) + { + float inv_d13 = 1.0f / ( d13_1 + d13_2 ); + s->v1.a = d13_1 * inv_d13; + s->v3.a = d13_2 * inv_d13; + s->count = 2; + s->v2 = s->v3; + return; + } + + // w2 region + if ( d12_1 <= 0.0f && d23_2 <= 0.0f ) + { + s->v2.a = 1.0f; + s->count = 1; + s->v1 = s->v2; + return; + } + + // w3 region + if ( d13_1 <= 0.0f && d23_1 <= 0.0f ) + { + s->v3.a = 1.0f; + s->count = 1; + s->v1 = s->v3; + return; + } + + // e23 + if ( d23_1 > 0.0f && d23_2 > 0.0f && d123_1 <= 0.0f ) + { + float inv_d23 = 1.0f / ( d23_1 + d23_2 ); + s->v2.a = d23_1 * inv_d23; + s->v3.a = d23_2 * inv_d23; + s->count = 2; + s->v1 = s->v3; + return; + } + + // Must be in triangle123 + float inv_d123 = 1.0f / ( d123_1 + d123_2 + d123_3 ); + s->v1.a = d123_1 * inv_d123; + s->v2.a = d123_2 * inv_d123; + s->v3.a = d123_3 * inv_d123; + s->count = 3; +} + +b2DistanceOutput b2ShapeDistance( b2DistanceCache* cache, const b2DistanceInput* input, b2Simplex* simplexes, + int simplexCapacity ) +{ + b2DistanceOutput output = { 0 }; + + const b2DistanceProxy* proxyA = &input->proxyA; + const b2DistanceProxy* proxyB = &input->proxyB; + + b2Transform transformA = input->transformA; + b2Transform transformB = input->transformB; + + // Initialize the simplex. + b2Simplex simplex = b2MakeSimplexFromCache( cache, proxyA, transformA, proxyB, transformB ); + + int simplexIndex = 0; + if ( simplexes != NULL && simplexIndex < simplexCapacity ) + { + simplexes[simplexIndex] = simplex; + simplexIndex += 1; + } + + // Get simplex vertices as an array. + b2SimplexVertex* vertices[] = { &simplex.v1, &simplex.v2, &simplex.v3 }; + const int k_maxIters = 20; + + // These store the vertices of the last simplex so that we can check for duplicates and prevent cycling. + int saveA[3], saveB[3]; + + // Main iteration loop. + int iter = 0; + while ( iter < k_maxIters ) + { + // Copy simplex so we can identify duplicates. + int saveCount = simplex.count; + for ( int i = 0; i < saveCount; ++i ) + { + saveA[i] = vertices[i]->indexA; + saveB[i] = vertices[i]->indexB; + } + + switch ( simplex.count ) + { + case 1: + break; + + case 2: + b2SolveSimplex2( &simplex ); + break; + + case 3: + b2SolveSimplex3( &simplex ); + break; + + default: + B2_ASSERT( false ); + } + + // If we have 3 points, then the origin is in the corresponding triangle. + if ( simplex.count == 3 ) + { + break; + } + + if ( simplexes != NULL && simplexIndex < simplexCapacity ) + { + simplexes[simplexIndex] = simplex; + simplexIndex += 1; + } + + // Get search direction. + b2Vec2 d = b2ComputeSimplexSearchDirection( &simplex ); + + // Ensure the search direction is numerically fit. + if ( b2Dot( d, d ) < FLT_EPSILON * FLT_EPSILON ) + { + // The origin is probably contained by a line segment + // or triangle. Thus the shapes are overlapped. + + // We can't return zero here even though there may be overlap. + // In case the simplex is a point, segment, or triangle it is difficult + // to determine if the origin is contained in the CSO or very close to it. + break; + } + + // Compute a tentative new simplex vertex using support points. + // support = support(b, d) - support(a, -d) + b2SimplexVertex* vertex = vertices[simplex.count]; + vertex->indexA = b2FindSupport( proxyA, b2InvRotateVector( transformA.q, b2Neg( d ) ) ); + vertex->wA = b2TransformPoint( transformA, proxyA->points[vertex->indexA] ); + vertex->indexB = b2FindSupport( proxyB, b2InvRotateVector( transformB.q, d ) ); + vertex->wB = b2TransformPoint( transformB, proxyB->points[vertex->indexB] ); + vertex->w = b2Sub( vertex->wB, vertex->wA ); + + // Iteration count is equated to the number of support point calls. + ++iter; + + // Check for duplicate support points. This is the main termination criteria. + bool duplicate = false; + for ( int i = 0; i < saveCount; ++i ) + { + if ( vertex->indexA == saveA[i] && vertex->indexB == saveB[i] ) + { + duplicate = true; + break; + } + } + + // If we found a duplicate support point we must exit to avoid cycling. + if ( duplicate ) + { + break; + } + + // New vertex is ok and needed. + ++simplex.count; + } + + if ( simplexes != NULL && simplexIndex < simplexCapacity ) + { + simplexes[simplexIndex] = simplex; + simplexIndex += 1; + } + + // Prepare output + b2ComputeSimplexWitnessPoints( &output.pointA, &output.pointB, &simplex ); + output.distance = b2Distance( output.pointA, output.pointB ); + output.iterations = iter; + output.simplexCount = simplexIndex; + + // Cache the simplex + b2MakeSimplexCache( cache, &simplex ); + + // Apply radii if requested + if ( input->useRadii ) + { + if ( output.distance < FLT_EPSILON ) + { + // Shapes are too close to safely compute normal + b2Vec2 p = ( b2Vec2 ){ 0.5f * ( output.pointA.x + output.pointB.x ), 0.5f * ( output.pointA.y + output.pointB.y ) }; + output.pointA = p; + output.pointB = p; + output.distance = 0.0f; + } + else + { + // Keep closest points on perimeter even if overlapped, this way + // the points move smoothly. + float rA = proxyA->radius; + float rB = proxyB->radius; + output.distance = b2MaxFloat( 0.0f, output.distance - rA - rB ); + b2Vec2 normal = b2Normalize( b2Sub( output.pointB, output.pointA ) ); + b2Vec2 offsetA = ( b2Vec2 ){ rA * normal.x, rA * normal.y }; + b2Vec2 offsetB = ( b2Vec2 ){ rB * normal.x, rB * normal.y }; + output.pointA = b2Add( output.pointA, offsetA ); + output.pointB = b2Sub( output.pointB, offsetB ); + } + } + + return output; +} + +// GJK-raycast +// Algorithm by Gino van den Bergen. +// "Smooth Mesh Contacts with GJK" in Game Physics Pearls. 2010 +// todo this is failing when used to raycast a box +// todo this converges slowly with a radius +b2CastOutput b2ShapeCast( const b2ShapeCastPairInput* input ) +{ + b2CastOutput output = { 0 }; + output.fraction = input->maxFraction; + + b2DistanceProxy proxyA = input->proxyA; + + b2Transform xfA = input->transformA; + b2Transform xfB = input->transformB; + b2Transform xf = b2InvMulTransforms( xfA, xfB ); + + // Put proxyB in proxyA's frame to reduce round-off error + b2DistanceProxy proxyB; + proxyB.count = input->proxyB.count; + proxyB.radius = input->proxyB.radius; + B2_ASSERT( proxyB.count <= b2_maxPolygonVertices ); + + for ( int i = 0; i < proxyB.count; ++i ) + { + proxyB.points[i] = b2TransformPoint( xf, input->proxyB.points[i] ); + } + + float radius = proxyA.radius + proxyB.radius; + + b2Vec2 r = b2RotateVector( xf.q, input->translationB ); + float lambda = 0.0f; + float maxFraction = input->maxFraction; + + // Initial simplex + b2Simplex simplex; + simplex.count = 0; + + // Get simplex vertices as an array. + b2SimplexVertex* vertices[] = { &simplex.v1, &simplex.v2, &simplex.v3 }; + + // Get an initial point in A - B + int indexA = b2FindSupport( &proxyA, b2Neg( r ) ); + b2Vec2 wA = proxyA.points[indexA]; + int indexB = b2FindSupport( &proxyB, r ); + b2Vec2 wB = proxyB.points[indexB]; + b2Vec2 v = b2Sub( wA, wB ); + + // Sigma is the target distance between proxies + const float linearSlop = b2_linearSlop; + const float sigma = b2MaxFloat( linearSlop, radius - linearSlop ); + + // Main iteration loop. + const int k_maxIters = 20; + int iter = 0; + while ( iter < k_maxIters && b2Length( v ) > sigma + 0.5f * linearSlop ) + { + B2_ASSERT( simplex.count < 3 ); + + output.iterations += 1; + + // Support in direction -v (A - B) + indexA = b2FindSupport( &proxyA, b2Neg( v ) ); + wA = proxyA.points[indexA]; + indexB = b2FindSupport( &proxyB, v ); + wB = proxyB.points[indexB]; + b2Vec2 p = b2Sub( wA, wB ); + + // -v is a normal at p, normalize to work with sigma + v = b2Normalize( v ); + + // Intersect ray with plane + float vp = b2Dot( v, p ); + float vr = b2Dot( v, r ); + if ( vp - sigma > lambda * vr ) + { + if ( vr <= 0.0f ) + { + // miss + return output; + } + + lambda = ( vp - sigma ) / vr; + if ( lambda > maxFraction ) + { + // too far + return output; + } + + // reset the simplex + simplex.count = 0; + } + + // Reverse simplex since it works with B - A. + // Shift by lambda * r because we want the closest point to the current clip point. + // Note that the support point p is not shifted because we want the plane equation + // to be formed in unshifted space. + b2SimplexVertex* vertex = vertices[simplex.count]; + vertex->indexA = indexB; + vertex->wA = ( b2Vec2 ){ wB.x + lambda * r.x, wB.y + lambda * r.y }; + vertex->indexB = indexA; + vertex->wB = wA; + vertex->w = b2Sub( vertex->wB, vertex->wA ); + vertex->a = 1.0f; + simplex.count += 1; + + switch ( simplex.count ) + { + case 1: + break; + + case 2: + b2SolveSimplex2( &simplex ); + break; + + case 3: + b2SolveSimplex3( &simplex ); + break; + + default: + B2_ASSERT( false ); + } + + // If we have 3 points, then the origin is in the corresponding triangle. + if ( simplex.count == 3 ) + { + // Overlap + return output; + } + + // Get search direction. + // todo use more accurate segment perpendicular + v = b2ComputeSimplexClosestPoint( &simplex ); + + // Iteration count is equated to the number of support point calls. + ++iter; + } + + if ( iter == 0 || lambda == 0.0f ) + { + // Initial overlap + return output; + } + + // Prepare output. + b2Vec2 pointA, pointB; + b2ComputeSimplexWitnessPoints( &pointB, &pointA, &simplex ); + + b2Vec2 n = b2Normalize( b2Neg( v ) ); + b2Vec2 point = { pointA.x + proxyA.radius * n.x, pointA.y + proxyA.radius * n.y }; + + output.point = b2TransformPoint( xfA, point ); + output.normal = b2RotateVector( xfA.q, n ); + output.fraction = lambda; + output.iterations = iter; + output.hit = true; + return output; +} + +#define B2_TOI_DEBUG 0 + +// Warning: writing to these globals significantly slows multithreading performance +#if B2_TOI_DEBUG +float b2_toiTime, b2_toiMaxTime; +int b2_toiCalls, b2_toiIters, b2_toiMaxIters; +int b2_toiRootIters, b2_toiMaxRootIters; +#endif + +typedef enum b2SeparationType +{ + b2_pointsType, + b2_faceAType, + b2_faceBType +} b2SeparationType; + +typedef struct b2SeparationFunction +{ + const b2DistanceProxy* proxyA; + const b2DistanceProxy* proxyB; + b2Sweep sweepA, sweepB; + b2Vec2 localPoint; + b2Vec2 axis; + b2SeparationType type; +} b2SeparationFunction; + +b2SeparationFunction b2MakeSeparationFunction( const b2DistanceCache* cache, const b2DistanceProxy* proxyA, const b2Sweep* sweepA, + const b2DistanceProxy* proxyB, const b2Sweep* sweepB, float t1 ) +{ + b2SeparationFunction f; + + f.proxyA = proxyA; + f.proxyB = proxyB; + int count = cache->count; + B2_ASSERT( 0 < count && count < 3 ); + + f.sweepA = *sweepA; + f.sweepB = *sweepB; + + b2Transform xfA = b2GetSweepTransform( sweepA, t1 ); + b2Transform xfB = b2GetSweepTransform( sweepB, t1 ); + + if ( count == 1 ) + { + f.type = b2_pointsType; + b2Vec2 localPointA = proxyA->points[cache->indexA[0]]; + b2Vec2 localPointB = proxyB->points[cache->indexB[0]]; + b2Vec2 pointA = b2TransformPoint( xfA, localPointA ); + b2Vec2 pointB = b2TransformPoint( xfB, localPointB ); + f.axis = b2Normalize( b2Sub( pointB, pointA ) ); + f.localPoint = b2Vec2_zero; + return f; + } + + if ( cache->indexA[0] == cache->indexA[1] ) + { + // Two points on B and one on A. + f.type = b2_faceBType; + b2Vec2 localPointB1 = proxyB->points[cache->indexB[0]]; + b2Vec2 localPointB2 = proxyB->points[cache->indexB[1]]; + + f.axis = b2CrossVS( b2Sub( localPointB2, localPointB1 ), 1.0f ); + f.axis = b2Normalize( f.axis ); + b2Vec2 normal = b2RotateVector( xfB.q, f.axis ); + + f.localPoint = ( b2Vec2 ){ 0.5f * ( localPointB1.x + localPointB2.x ), 0.5f * ( localPointB1.y + localPointB2.y ) }; + b2Vec2 pointB = b2TransformPoint( xfB, f.localPoint ); + + b2Vec2 localPointA = proxyA->points[cache->indexA[0]]; + b2Vec2 pointA = b2TransformPoint( xfA, localPointA ); + + float s = b2Dot( b2Sub( pointA, pointB ), normal ); + if ( s < 0.0f ) + { + f.axis = b2Neg( f.axis ); + } + return f; + } + + // Two points on A and one or two points on B. + f.type = b2_faceAType; + b2Vec2 localPointA1 = proxyA->points[cache->indexA[0]]; + b2Vec2 localPointA2 = proxyA->points[cache->indexA[1]]; + + f.axis = b2CrossVS( b2Sub( localPointA2, localPointA1 ), 1.0f ); + f.axis = b2Normalize( f.axis ); + b2Vec2 normal = b2RotateVector( xfA.q, f.axis ); + + f.localPoint = ( b2Vec2 ){ 0.5f * ( localPointA1.x + localPointA2.x ), 0.5f * ( localPointA1.y + localPointA2.y ) }; + b2Vec2 pointA = b2TransformPoint( xfA, f.localPoint ); + + b2Vec2 localPointB = proxyB->points[cache->indexB[0]]; + b2Vec2 pointB = b2TransformPoint( xfB, localPointB ); + + float s = b2Dot( b2Sub( pointB, pointA ), normal ); + if ( s < 0.0f ) + { + f.axis = b2Neg( f.axis ); + } + return f; +} + +static float b2FindMinSeparation( const b2SeparationFunction* f, int* indexA, int* indexB, float t ) +{ + b2Transform xfA = b2GetSweepTransform( &f->sweepA, t ); + b2Transform xfB = b2GetSweepTransform( &f->sweepB, t ); + + switch ( f->type ) + { + case b2_pointsType: + { + b2Vec2 axisA = b2InvRotateVector( xfA.q, f->axis ); + b2Vec2 axisB = b2InvRotateVector( xfB.q, b2Neg( f->axis ) ); + + *indexA = b2FindSupport( f->proxyA, axisA ); + *indexB = b2FindSupport( f->proxyB, axisB ); + + b2Vec2 localPointA = f->proxyA->points[*indexA]; + b2Vec2 localPointB = f->proxyB->points[*indexB]; + + b2Vec2 pointA = b2TransformPoint( xfA, localPointA ); + b2Vec2 pointB = b2TransformPoint( xfB, localPointB ); + + float separation = b2Dot( b2Sub( pointB, pointA ), f->axis ); + return separation; + } + + case b2_faceAType: + { + b2Vec2 normal = b2RotateVector( xfA.q, f->axis ); + b2Vec2 pointA = b2TransformPoint( xfA, f->localPoint ); + + b2Vec2 axisB = b2InvRotateVector( xfB.q, b2Neg( normal ) ); + + *indexA = -1; + *indexB = b2FindSupport( f->proxyB, axisB ); + + b2Vec2 localPointB = f->proxyB->points[*indexB]; + b2Vec2 pointB = b2TransformPoint( xfB, localPointB ); + + float separation = b2Dot( b2Sub( pointB, pointA ), normal ); + return separation; + } + + case b2_faceBType: + { + b2Vec2 normal = b2RotateVector( xfB.q, f->axis ); + b2Vec2 pointB = b2TransformPoint( xfB, f->localPoint ); + + b2Vec2 axisA = b2InvRotateVector( xfA.q, b2Neg( normal ) ); + + *indexB = -1; + *indexA = b2FindSupport( f->proxyA, axisA ); + + b2Vec2 localPointA = f->proxyA->points[*indexA]; + b2Vec2 pointA = b2TransformPoint( xfA, localPointA ); + + float separation = b2Dot( b2Sub( pointA, pointB ), normal ); + return separation; + } + + default: + B2_ASSERT( false ); + *indexA = -1; + *indexB = -1; + return 0.0f; + } +} + +// +float b2EvaluateSeparation( const b2SeparationFunction* f, int indexA, int indexB, float t ) +{ + b2Transform xfA = b2GetSweepTransform( &f->sweepA, t ); + b2Transform xfB = b2GetSweepTransform( &f->sweepB, t ); + + switch ( f->type ) + { + case b2_pointsType: + { + b2Vec2 localPointA = f->proxyA->points[indexA]; + b2Vec2 localPointB = f->proxyB->points[indexB]; + + b2Vec2 pointA = b2TransformPoint( xfA, localPointA ); + b2Vec2 pointB = b2TransformPoint( xfB, localPointB ); + + float separation = b2Dot( b2Sub( pointB, pointA ), f->axis ); + return separation; + } + + case b2_faceAType: + { + b2Vec2 normal = b2RotateVector( xfA.q, f->axis ); + b2Vec2 pointA = b2TransformPoint( xfA, f->localPoint ); + + b2Vec2 localPointB = f->proxyB->points[indexB]; + b2Vec2 pointB = b2TransformPoint( xfB, localPointB ); + + float separation = b2Dot( b2Sub( pointB, pointA ), normal ); + return separation; + } + + case b2_faceBType: + { + b2Vec2 normal = b2RotateVector( xfB.q, f->axis ); + b2Vec2 pointB = b2TransformPoint( xfB, f->localPoint ); + + b2Vec2 localPointA = f->proxyA->points[indexA]; + b2Vec2 pointA = b2TransformPoint( xfA, localPointA ); + + float separation = b2Dot( b2Sub( pointA, pointB ), normal ); + return separation; + } + + default: + B2_ASSERT( false ); + return 0.0f; + } +} + +// CCD via the local separating axis method. This seeks progression +// by computing the largest time at which separation is maintained. +b2TOIOutput b2TimeOfImpact( const b2TOIInput* input ) +{ +#if B2_TOI_DEBUG + b2Timer timer = b2CreateTimer(); + ++b2_toiCalls; +#endif + + b2TOIOutput output; + output.state = b2_toiStateUnknown; + output.t = input->tMax; + + const b2DistanceProxy* proxyA = &input->proxyA; + const b2DistanceProxy* proxyB = &input->proxyB; + + b2Sweep sweepA = input->sweepA; + b2Sweep sweepB = input->sweepB; + B2_ASSERT( b2IsNormalized( sweepA.q1 ) && b2IsNormalized( sweepA.q2 ) ); + B2_ASSERT( b2IsNormalized( sweepB.q1 ) && b2IsNormalized( sweepB.q2 ) ); + + float tMax = input->tMax; + + float totalRadius = proxyA->radius + proxyB->radius; + float target = b2MaxFloat( b2_linearSlop, totalRadius - b2_linearSlop ); + float tolerance = 0.25f * b2_linearSlop; + B2_ASSERT( target > tolerance ); + + float t1 = 0.0f; + const int k_maxIterations = 20; + int iter = 0; + + // Prepare input for distance query. + b2DistanceCache cache = { 0 }; + b2DistanceInput distanceInput; + distanceInput.proxyA = input->proxyA; + distanceInput.proxyB = input->proxyB; + distanceInput.useRadii = false; + + // The outer loop progressively attempts to compute new separating axes. + // This loop terminates when an axis is repeated (no progress is made). + for ( ;; ) + { + b2Transform xfA = b2GetSweepTransform( &sweepA, t1 ); + b2Transform xfB = b2GetSweepTransform( &sweepB, t1 ); + + // Get the distance between shapes. We can also use the results + // to get a separating axis. + distanceInput.transformA = xfA; + distanceInput.transformB = xfB; + b2DistanceOutput distanceOutput = b2ShapeDistance( &cache, &distanceInput, NULL, 0 ); + + // If the shapes are overlapped, we give up on continuous collision. + if ( distanceOutput.distance <= 0.0f ) + { + // Failure! + output.state = b2_toiStateOverlapped; + output.t = 0.0f; + break; + } + + if ( distanceOutput.distance < target + tolerance ) + { + // Victory! + output.state = b2_toiStateHit; + output.t = t1; + break; + } + + // Initialize the separating axis. + b2SeparationFunction fcn = b2MakeSeparationFunction( &cache, proxyA, &sweepA, proxyB, &sweepB, t1 ); +#if 0 + // Dump the curve seen by the root finder + { + const int N = 100; + float dx = 1.0f / N; + float xs[N + 1]; + float fs[N + 1]; + + float x = 0.0f; + + for (int i = 0; i <= N; ++i) + { + sweepA.GetTransform(&xfA, x); + sweepB.GetTransform(&xfB, x); + float f = fcn.Evaluate(xfA, xfB) - target; + + printf("%g %g\n", x, f); + + xs[i] = x; + fs[i] = f; + + x += dx; + } + } +#endif + + // Compute the TOI on the separating axis. We do this by successively + // resolving the deepest point. This loop is bounded by the number of vertices. + bool done = false; + float t2 = tMax; + int pushBackIter = 0; + for ( ;; ) + { + // Find the deepest point at t2. Store the witness point indices. + int indexA, indexB; + float s2 = b2FindMinSeparation( &fcn, &indexA, &indexB, t2 ); + + // Is the final configuration separated? + if ( s2 > target + tolerance ) + { + // Victory! + output.state = b2_toiStateSeparated; + output.t = tMax; + done = true; + break; + } + + // Has the separation reached tolerance? + if ( s2 > target - tolerance ) + { + // Advance the sweeps + t1 = t2; + break; + } + + // Compute the initial separation of the witness points. + float s1 = b2EvaluateSeparation( &fcn, indexA, indexB, t1 ); + + // Check for initial overlap. This might happen if the root finder + // runs out of iterations. + if ( s1 < target - tolerance ) + { + output.state = b2_toiStateFailed; + output.t = t1; + done = true; + break; + } + + // Check for touching + if ( s1 <= target + tolerance ) + { + // Victory! t1 should hold the TOI (could be 0.0). + output.state = b2_toiStateHit; + output.t = t1; + done = true; + break; + } + + // Compute 1D root of: f(x) - target = 0 + int rootIterCount = 0; + float a1 = t1, a2 = t2; + for ( ;; ) + { + // Use a mix of the secant rule and bisection. + float t; + if ( rootIterCount & 1 ) + { + // Secant rule to improve convergence. + t = a1 + ( target - s1 ) * ( a2 - a1 ) / ( s2 - s1 ); + } + else + { + // Bisection to guarantee progress. + t = 0.5f * ( a1 + a2 ); + } + + ++rootIterCount; + +#if B2_TOI_DEBUG + ++b2_toiRootIters; +#endif + + float s = b2EvaluateSeparation( &fcn, indexA, indexB, t ); + + if ( b2AbsFloat( s - target ) < tolerance ) + { + // t2 holds a tentative value for t1 + t2 = t; + break; + } + + // Ensure we continue to bracket the root. + if ( s > target ) + { + a1 = t; + s1 = s; + } + else + { + a2 = t; + s2 = s; + } + + if ( rootIterCount == 50 ) + { + break; + } + } + +#if B2_TOI_DEBUG + b2_toiMaxRootIters = b2MaxInt( b2_toiMaxRootIters, rootIterCount ); +#endif + + ++pushBackIter; + + if ( pushBackIter == b2_maxPolygonVertices ) + { + break; + } + } + + ++iter; +#if B2_TOI_DEBUG + ++b2_toiIters; +#endif + + if ( done ) + { + break; + } + + if ( iter == k_maxIterations ) + { + // Root finder got stuck. Semi-victory. + output.state = b2_toiStateFailed; + output.t = t1; + break; + } + } + +#if B2_TOI_DEBUG + b2_toiMaxIters = b2MaxInt( b2_toiMaxIters, iter ); + + float time = b2GetMilliseconds( &timer ); + b2_toiMaxTime = b2MaxFloat( b2_toiMaxTime, time ); + b2_toiTime += time; +#endif + + return output; +} diff --git a/3rdparty/box2d/src/distance_joint.c b/3rdparty/box2d/src/distance_joint.c new file mode 100644 index 000000000000..cec4573959f1 --- /dev/null +++ b/3rdparty/box2d/src/distance_joint.c @@ -0,0 +1,556 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#if defined( _MSC_VER ) && !defined( _CRT_SECURE_NO_WARNINGS ) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "body.h" +#include "core.h" +#include "joint.h" +#include "solver.h" +#include "solver_set.h" +#include "world.h" + +// needed for dll export +#include "box2d/box2d.h" + +#include + +void b2DistanceJoint_SetLength( b2JointId jointId, float length ) +{ + b2JointSim* base = b2GetJointSimCheckType( jointId, b2_distanceJoint ); + b2DistanceJoint* joint = &base->distanceJoint; + + joint->length = b2ClampFloat( length, b2_linearSlop, b2_huge ); + joint->impulse = 0.0f; + joint->lowerImpulse = 0.0f; + joint->upperImpulse = 0.0f; +} + +float b2DistanceJoint_GetLength( b2JointId jointId ) +{ + b2JointSim* base = b2GetJointSimCheckType( jointId, b2_distanceJoint ); + b2DistanceJoint* joint = &base->distanceJoint; + return joint->length; +} + +void b2DistanceJoint_EnableLimit( b2JointId jointId, bool enableLimit ) +{ + b2JointSim* base = b2GetJointSimCheckType( jointId, b2_distanceJoint ); + b2DistanceJoint* joint = &base->distanceJoint; + joint->enableLimit = enableLimit; +} + +bool b2DistanceJoint_IsLimitEnabled( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_distanceJoint ); + return joint->distanceJoint.enableLimit; +} + +void b2DistanceJoint_SetLengthRange( b2JointId jointId, float minLength, float maxLength ) +{ + b2JointSim* base = b2GetJointSimCheckType( jointId, b2_distanceJoint ); + b2DistanceJoint* joint = &base->distanceJoint; + + minLength = b2ClampFloat( minLength, b2_linearSlop, b2_huge ); + maxLength = b2ClampFloat( maxLength, b2_linearSlop, b2_huge ); + joint->minLength = b2MinFloat( minLength, maxLength ); + joint->maxLength = b2MaxFloat( minLength, maxLength ); + joint->impulse = 0.0f; + joint->lowerImpulse = 0.0f; + joint->upperImpulse = 0.0f; +} + +float b2DistanceJoint_GetMinLength( b2JointId jointId ) +{ + b2JointSim* base = b2GetJointSimCheckType( jointId, b2_distanceJoint ); + b2DistanceJoint* joint = &base->distanceJoint; + return joint->minLength; +} + +float b2DistanceJoint_GetMaxLength( b2JointId jointId ) +{ + b2JointSim* base = b2GetJointSimCheckType( jointId, b2_distanceJoint ); + b2DistanceJoint* joint = &base->distanceJoint; + return joint->maxLength; +} + +float b2DistanceJoint_GetCurrentLength( b2JointId jointId ) +{ + b2JointSim* base = b2GetJointSimCheckType( jointId, b2_distanceJoint ); + + b2World* world = b2GetWorld( jointId.world0 ); + B2_ASSERT( world->locked == false ); + if ( world->locked ) + { + return 0.0f; + } + + b2Transform transformA = b2GetBodyTransform( world, base->bodyIdA ); + b2Transform transformB = b2GetBodyTransform( world, base->bodyIdB ); + + b2Vec2 pA = b2TransformPoint( transformA, base->localOriginAnchorA ); + b2Vec2 pB = b2TransformPoint( transformB, base->localOriginAnchorB ); + b2Vec2 d = b2Sub( pB, pA ); + float length = b2Length( d ); + return length; +} + +void b2DistanceJoint_EnableSpring( b2JointId jointId, bool enableSpring ) +{ + b2JointSim* base = b2GetJointSimCheckType( jointId, b2_distanceJoint ); + base->distanceJoint.enableSpring = enableSpring; +} + +bool b2DistanceJoint_IsSpringEnabled( b2JointId jointId ) +{ + b2JointSim* base = b2GetJointSimCheckType( jointId, b2_distanceJoint ); + return base->distanceJoint.enableSpring; +} + +void b2DistanceJoint_SetSpringHertz( b2JointId jointId, float hertz ) +{ + b2JointSim* base = b2GetJointSimCheckType( jointId, b2_distanceJoint ); + base->distanceJoint.hertz = hertz; +} + +void b2DistanceJoint_SetSpringDampingRatio( b2JointId jointId, float dampingRatio ) +{ + b2JointSim* base = b2GetJointSimCheckType( jointId, b2_distanceJoint ); + base->distanceJoint.dampingRatio = dampingRatio; +} + +float b2DistanceJoint_GetSpringHertz( b2JointId jointId ) +{ + b2JointSim* base = b2GetJointSimCheckType( jointId, b2_distanceJoint ); + b2DistanceJoint* joint = &base->distanceJoint; + return joint->hertz; +} + +float b2DistanceJoint_GetSpringDampingRatio( b2JointId jointId ) +{ + b2JointSim* base = b2GetJointSimCheckType( jointId, b2_distanceJoint ); + b2DistanceJoint* joint = &base->distanceJoint; + return joint->dampingRatio; +} + +void b2DistanceJoint_EnableMotor( b2JointId jointId, bool enableMotor ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_distanceJoint ); + if ( enableMotor != joint->distanceJoint.enableMotor ) + { + joint->distanceJoint.enableMotor = enableMotor; + joint->distanceJoint.motorImpulse = 0.0f; + } +} + +bool b2DistanceJoint_IsMotorEnabled( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_distanceJoint ); + return joint->distanceJoint.enableMotor; +} + +void b2DistanceJoint_SetMotorSpeed( b2JointId jointId, float motorSpeed ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_distanceJoint ); + joint->distanceJoint.motorSpeed = motorSpeed; +} + +float b2DistanceJoint_GetMotorSpeed( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_distanceJoint ); + return joint->distanceJoint.motorSpeed; +} + +float b2DistanceJoint_GetMotorForce( b2JointId jointId ) +{ + b2World* world = b2GetWorld( jointId.world0 ); + b2JointSim* base = b2GetJointSimCheckType( jointId, b2_distanceJoint ); + return world->inv_h * base->distanceJoint.motorImpulse; +} + +void b2DistanceJoint_SetMaxMotorForce( b2JointId jointId, float force ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_distanceJoint ); + joint->distanceJoint.maxMotorForce = force; +} + +float b2DistanceJoint_GetMaxMotorForce( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_distanceJoint ); + return joint->distanceJoint.maxMotorForce; +} + +b2Vec2 b2GetDistanceJointForce( b2World* world, b2JointSim* base ) +{ + b2DistanceJoint* joint = &base->distanceJoint; + + b2Transform transformA = b2GetBodyTransform( world, base->bodyIdA ); + b2Transform transformB = b2GetBodyTransform( world, base->bodyIdB ); + + b2Vec2 pA = b2TransformPoint( transformA, base->localOriginAnchorA ); + b2Vec2 pB = b2TransformPoint( transformB, base->localOriginAnchorB ); + b2Vec2 d = b2Sub( pB, pA ); + b2Vec2 axis = b2Normalize( d ); + float force = ( joint->impulse + joint->lowerImpulse - joint->upperImpulse + joint->motorImpulse ) * world->inv_h; + return b2MulSV( force, axis ); +} + +// 1-D constrained system +// m (v2 - v1) = lambda +// v2 + (beta/h) * x1 + gamma * lambda = 0, gamma has units of inverse mass. +// x2 = x1 + h * v2 + +// 1-D mass-damper-spring system +// m (v2 - v1) + h * d * v2 + h * k * + +// C = norm(p2 - p1) - L +// u = (p2 - p1) / norm(p2 - p1) +// Cdot = dot(u, v2 + cross(w2, r2) - v1 - cross(w1, r1)) +// J = [-u -cross(r1, u) u cross(r2, u)] +// K = J * invM * JT +// = invMass1 + invI1 * cross(r1, u)^2 + invMass2 + invI2 * cross(r2, u)^2 + +void b2PrepareDistanceJoint( b2JointSim* base, b2StepContext* context ) +{ + B2_ASSERT( base->type == b2_distanceJoint ); + + // chase body id to the solver set where the body lives + int idA = base->bodyIdA; + int idB = base->bodyIdB; + + b2World* world = context->world; + b2Body* bodyA = b2BodyArray_Get( &world->bodies, idA ); + b2Body* bodyB = b2BodyArray_Get( &world->bodies, idB ); + + B2_ASSERT( bodyA->setIndex == b2_awakeSet || bodyB->setIndex == b2_awakeSet ); + + b2SolverSet* setA = b2SolverSetArray_Get( &world->solverSets, bodyA->setIndex ); + b2SolverSet* setB = b2SolverSetArray_Get( &world->solverSets, bodyB->setIndex ); + + int localIndexA = bodyA->localIndex; + int localIndexB = bodyB->localIndex; + + b2BodySim* bodySimA = b2BodySimArray_Get( &setA->bodySims, localIndexA ); + b2BodySim* bodySimB = b2BodySimArray_Get( &setB->bodySims, localIndexB ); + + float mA = bodySimA->invMass; + float iA = bodySimA->invInertia; + float mB = bodySimB->invMass; + float iB = bodySimB->invInertia; + + base->invMassA = mA; + base->invMassB = mB; + base->invIA = iA; + base->invIB = iB; + + b2DistanceJoint* joint = &base->distanceJoint; + + joint->indexA = bodyA->setIndex == b2_awakeSet ? localIndexA : B2_NULL_INDEX; + joint->indexB = bodyB->setIndex == b2_awakeSet ? localIndexB : B2_NULL_INDEX; + + // initial anchors in world space + joint->anchorA = b2RotateVector( bodySimA->transform.q, b2Sub( base->localOriginAnchorA, bodySimA->localCenter ) ); + joint->anchorB = b2RotateVector( bodySimB->transform.q, b2Sub( base->localOriginAnchorB, bodySimB->localCenter ) ); + joint->deltaCenter = b2Sub( bodySimB->center, bodySimA->center ); + + b2Vec2 rA = joint->anchorA; + b2Vec2 rB = joint->anchorB; + b2Vec2 separation = b2Add( b2Sub( rB, rA ), joint->deltaCenter ); + b2Vec2 axis = b2Normalize( separation ); + + // compute effective mass + float crA = b2Cross( rA, axis ); + float crB = b2Cross( rB, axis ); + float k = mA + mB + iA * crA * crA + iB * crB * crB; + joint->axialMass = k > 0.0f ? 1.0f / k : 0.0f; + + joint->distanceSoftness = b2MakeSoft( joint->hertz, joint->dampingRatio, context->h ); + + if ( context->enableWarmStarting == false ) + { + joint->impulse = 0.0f; + joint->lowerImpulse = 0.0f; + joint->upperImpulse = 0.0f; + joint->motorImpulse = 0.0f; + } +} + +void b2WarmStartDistanceJoint( b2JointSim* base, b2StepContext* context ) +{ + B2_ASSERT( base->type == b2_distanceJoint ); + + float mA = base->invMassA; + float mB = base->invMassB; + float iA = base->invIA; + float iB = base->invIB; + + // dummy state for static bodies + b2BodyState dummyState = b2_identityBodyState; + + b2DistanceJoint* joint = &base->distanceJoint; + b2BodyState* stateA = joint->indexA == B2_NULL_INDEX ? &dummyState : context->states + joint->indexA; + b2BodyState* stateB = joint->indexB == B2_NULL_INDEX ? &dummyState : context->states + joint->indexB; + + b2Vec2 rA = b2RotateVector( stateA->deltaRotation, joint->anchorA ); + b2Vec2 rB = b2RotateVector( stateB->deltaRotation, joint->anchorB ); + + b2Vec2 ds = b2Add( b2Sub( stateB->deltaPosition, stateA->deltaPosition ), b2Sub( rB, rA ) ); + b2Vec2 separation = b2Add( joint->deltaCenter, ds ); + b2Vec2 axis = b2Normalize( separation ); + + float axialImpulse = joint->impulse + joint->lowerImpulse - joint->upperImpulse + joint->motorImpulse; + b2Vec2 P = b2MulSV( axialImpulse, axis ); + + stateA->linearVelocity = b2MulSub( stateA->linearVelocity, mA, P ); + stateA->angularVelocity -= iA * b2Cross( rA, P ); + stateB->linearVelocity = b2MulAdd( stateB->linearVelocity, mB, P ); + stateB->angularVelocity += iB * b2Cross( rB, P ); +} + +void b2SolveDistanceJoint( b2JointSim* base, b2StepContext* context, bool useBias ) +{ + B2_ASSERT( base->type == b2_distanceJoint ); + + float mA = base->invMassA; + float mB = base->invMassB; + float iA = base->invIA; + float iB = base->invIB; + + // dummy state for static bodies + b2BodyState dummyState = b2_identityBodyState; + + b2DistanceJoint* joint = &base->distanceJoint; + b2BodyState* stateA = joint->indexA == B2_NULL_INDEX ? &dummyState : context->states + joint->indexA; + b2BodyState* stateB = joint->indexB == B2_NULL_INDEX ? &dummyState : context->states + joint->indexB; + + b2Vec2 vA = stateA->linearVelocity; + float wA = stateA->angularVelocity; + b2Vec2 vB = stateB->linearVelocity; + float wB = stateB->angularVelocity; + + // current anchors + b2Vec2 rA = b2RotateVector( stateA->deltaRotation, joint->anchorA ); + b2Vec2 rB = b2RotateVector( stateB->deltaRotation, joint->anchorB ); + + // current separation + b2Vec2 ds = b2Add( b2Sub( stateB->deltaPosition, stateA->deltaPosition ), b2Sub( rB, rA ) ); + b2Vec2 separation = b2Add( joint->deltaCenter, ds ); + + float length = b2Length( separation ); + b2Vec2 axis = b2Normalize( separation ); + + // joint is soft if + // - spring is enabled + // - and (joint limit is disabled or limits are not equal) + if ( joint->enableSpring && ( joint->minLength < joint->maxLength || joint->enableLimit == false ) ) + { + // spring + if ( joint->hertz > 0.0f ) + { + // Cdot = dot(u, v + cross(w, r)) + b2Vec2 vr = b2Add( b2Sub( vB, vA ), b2Sub( b2CrossSV( wB, rB ), b2CrossSV( wA, rA ) ) ); + float Cdot = b2Dot( axis, vr ); + float C = length - joint->length; + float bias = joint->distanceSoftness.biasRate * C; + + float m = joint->distanceSoftness.massScale * joint->axialMass; + float impulse = -m * ( Cdot + bias ) - joint->distanceSoftness.impulseScale * joint->impulse; + joint->impulse += impulse; + + b2Vec2 P = b2MulSV( impulse, axis ); + vA = b2MulSub( vA, mA, P ); + wA -= iA * b2Cross( rA, P ); + vB = b2MulAdd( vB, mB, P ); + wB += iB * b2Cross( rB, P ); + } + + if ( joint->enableLimit ) + { + // lower limit + { + b2Vec2 vr = b2Add( b2Sub( vB, vA ), b2Sub( b2CrossSV( wB, rB ), b2CrossSV( wA, rA ) ) ); + float Cdot = b2Dot( axis, vr ); + + float C = length - joint->minLength; + + float bias = 0.0f; + float massCoeff = 1.0f; + float impulseCoeff = 0.0f; + if ( C > 0.0f ) + { + // speculative + bias = C * context->inv_h; + } + else if ( useBias ) + { + bias = context->jointSoftness.biasRate * C; + massCoeff = context->jointSoftness.massScale; + impulseCoeff = context->jointSoftness.impulseScale; + } + + float impulse = -massCoeff * joint->axialMass * ( Cdot + bias ) - impulseCoeff * joint->lowerImpulse; + float newImpulse = b2MaxFloat( 0.0f, joint->lowerImpulse + impulse ); + impulse = newImpulse - joint->lowerImpulse; + joint->lowerImpulse = newImpulse; + + b2Vec2 P = b2MulSV( impulse, axis ); + vA = b2MulSub( vA, mA, P ); + wA -= iA * b2Cross( rA, P ); + vB = b2MulAdd( vB, mB, P ); + wB += iB * b2Cross( rB, P ); + } + + // upper + { + b2Vec2 vr = b2Add( b2Sub( vA, vB ), b2Sub( b2CrossSV( wA, rA ), b2CrossSV( wB, rB ) ) ); + float Cdot = b2Dot( axis, vr ); + + float C = joint->maxLength - length; + + float bias = 0.0f; + float massScale = 1.0f; + float impulseScale = 0.0f; + if ( C > 0.0f ) + { + // speculative + bias = C * context->inv_h; + } + else if ( useBias ) + { + bias = context->jointSoftness.biasRate * C; + massScale = context->jointSoftness.massScale; + impulseScale = context->jointSoftness.impulseScale; + } + + float impulse = -massScale * joint->axialMass * ( Cdot + bias ) - impulseScale * joint->upperImpulse; + float newImpulse = b2MaxFloat( 0.0f, joint->upperImpulse + impulse ); + impulse = newImpulse - joint->upperImpulse; + joint->upperImpulse = newImpulse; + + b2Vec2 P = b2MulSV( -impulse, axis ); + vA = b2MulSub( vA, mA, P ); + wA -= iA * b2Cross( rA, P ); + vB = b2MulAdd( vB, mB, P ); + wB += iB * b2Cross( rB, P ); + } + } + + if ( joint->enableMotor ) + { + b2Vec2 vr = b2Add( b2Sub( vB, vA ), b2Sub( b2CrossSV( wB, rB ), b2CrossSV( wA, rA ) ) ); + float Cdot = b2Dot( axis, vr ); + float impulse = joint->axialMass * ( joint->motorSpeed - Cdot ); + float oldImpulse = joint->motorImpulse; + float maxImpulse = context->h * joint->maxMotorForce; + joint->motorImpulse = b2ClampFloat( joint->motorImpulse + impulse, -maxImpulse, maxImpulse ); + impulse = joint->motorImpulse - oldImpulse; + + b2Vec2 P = b2MulSV( impulse, axis ); + vA = b2MulSub( vA, mA, P ); + wA -= iA * b2Cross( rA, P ); + vB = b2MulAdd( vB, mB, P ); + wB += iB * b2Cross( rB, P ); + } + } + else + { + // rigid constraint + b2Vec2 vr = b2Add( b2Sub( vB, vA ), b2Sub( b2CrossSV( wB, rB ), b2CrossSV( wA, rA ) ) ); + float Cdot = b2Dot( axis, vr ); + + float C = length - joint->length; + + float bias = 0.0f; + float massScale = 1.0f; + float impulseScale = 0.0f; + if ( useBias ) + { + bias = context->jointSoftness.biasRate * C; + massScale = context->jointSoftness.massScale; + impulseScale = context->jointSoftness.impulseScale; + } + + float impulse = -massScale * joint->axialMass * ( Cdot + bias ) - impulseScale * joint->impulse; + joint->impulse += impulse; + + b2Vec2 P = b2MulSV( impulse, axis ); + vA = b2MulSub( vA, mA, P ); + wA -= iA * b2Cross( rA, P ); + vB = b2MulAdd( vB, mB, P ); + wB += iB * b2Cross( rB, P ); + } + + stateA->linearVelocity = vA; + stateA->angularVelocity = wA; + stateB->linearVelocity = vB; + stateB->angularVelocity = wB; +} + +#if 0 +void b2DistanceJoint::Dump() +{ + int32 indexA = m_bodyA->m_islandIndex; + int32 indexB = m_bodyB->m_islandIndex; + + b2Dump(" b2DistanceJointDef jd;\n"); + b2Dump(" jd.bodyA = sims[%d];\n", indexA); + b2Dump(" jd.bodyB = sims[%d];\n", indexB); + b2Dump(" jd.collideConnected = bool(%d);\n", m_collideConnected); + b2Dump(" jd.localAnchorA.Set(%.9g, %.9g);\n", m_localAnchorA.x, m_localAnchorA.y); + b2Dump(" jd.localAnchorB.Set(%.9g, %.9g);\n", m_localAnchorB.x, m_localAnchorB.y); + b2Dump(" jd.length = %.9g;\n", m_length); + b2Dump(" jd.minLength = %.9g;\n", m_minLength); + b2Dump(" jd.maxLength = %.9g;\n", m_maxLength); + b2Dump(" jd.stiffness = %.9g;\n", m_stiffness); + b2Dump(" jd.damping = %.9g;\n", m_damping); + b2Dump(" joints[%d] = m_world->CreateJoint(&jd);\n", m_index); +} +#endif + +void b2DrawDistanceJoint( b2DebugDraw* draw, b2JointSim* base, b2Transform transformA, b2Transform transformB ) +{ + B2_ASSERT( base->type == b2_distanceJoint ); + + b2DistanceJoint* joint = &base->distanceJoint; + + b2Vec2 pA = b2TransformPoint( transformA, base->localOriginAnchorA ); + b2Vec2 pB = b2TransformPoint( transformB, base->localOriginAnchorB ); + + b2Vec2 axis = b2Normalize( b2Sub( pB, pA ) ); + + if ( joint->minLength < joint->maxLength && joint->enableLimit ) + { + b2Vec2 pMin = b2MulAdd( pA, joint->minLength, axis ); + b2Vec2 pMax = b2MulAdd( pA, joint->maxLength, axis ); + b2Vec2 offset = b2MulSV( 0.05f * b2_lengthUnitsPerMeter, b2RightPerp( axis ) ); + + if ( joint->minLength > b2_linearSlop ) + { + // draw->DrawPoint(pMin, 4.0f, c2, draw->context); + draw->DrawSegment( b2Sub( pMin, offset ), b2Add( pMin, offset ), b2_colorLightGreen, draw->context ); + } + + if ( joint->maxLength < b2_huge ) + { + // draw->DrawPoint(pMax, 4.0f, c3, draw->context); + draw->DrawSegment( b2Sub( pMax, offset ), b2Add( pMax, offset ), b2_colorRed, draw->context ); + } + + if ( joint->minLength > b2_linearSlop && joint->maxLength < b2_huge ) + { + draw->DrawSegment( pMin, pMax, b2_colorGray, draw->context ); + } + } + + draw->DrawSegment( pA, pB, b2_colorWhite, draw->context ); + draw->DrawPoint( pA, 4.0f, b2_colorWhite, draw->context ); + draw->DrawPoint( pB, 4.0f, b2_colorWhite, draw->context ); + + if ( joint->hertz > 0.0f && joint->enableSpring ) + { + b2Vec2 pRest = b2MulAdd( pA, joint->length, axis ); + draw->DrawPoint( pRest, 4.0f, b2_colorBlue, draw->context ); + } +} diff --git a/3rdparty/box2d/src/dynamic_tree.c b/3rdparty/box2d/src/dynamic_tree.c new file mode 100644 index 000000000000..322b5221c0a9 --- /dev/null +++ b/3rdparty/box2d/src/dynamic_tree.c @@ -0,0 +1,1926 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#include "aabb.h" +#include "core.h" + +#include "box2d/collision.h" +#include "box2d/math_functions.h" + +#include +#include + +#define b2_treeStackSize 1024 + +// TODO_ERIN +// - try incrementally sorting internal nodes by height for better cache efficiency during depth first traversal. + +static b2TreeNode b2_defaultTreeNode = { + { { 0.0f, 0.0f }, { 0.0f, 0.0f } }, 0, { B2_NULL_INDEX }, B2_NULL_INDEX, B2_NULL_INDEX, -1, -2, false, + { 0, 0, 0, 0, 0 } }; + +static inline bool b2IsLeaf( const b2TreeNode* node ) +{ + return node->height == 0; +} + +static inline int16_t b2MaxInt16( int16_t a, int16_t b ) +{ + return a > b ? a : b; +} + +b2DynamicTree b2DynamicTree_Create( void ) +{ + _Static_assert( ( sizeof( b2TreeNode ) & 0xF ) == 0, "tree node size not a multiple of 16" ); + + b2DynamicTree tree; + tree.root = B2_NULL_INDEX; + + tree.nodeCapacity = 16; + tree.nodeCount = 0; + tree.nodes = (b2TreeNode*)b2Alloc( tree.nodeCapacity * sizeof( b2TreeNode ) ); + memset( tree.nodes, 0, tree.nodeCapacity * sizeof( b2TreeNode ) ); + + // Build a linked list for the free list. + for ( int32_t i = 0; i < tree.nodeCapacity - 1; ++i ) + { + tree.nodes[i].next = i + 1; + tree.nodes[i].height = -1; + } + tree.nodes[tree.nodeCapacity - 1].next = B2_NULL_INDEX; + tree.nodes[tree.nodeCapacity - 1].height = -1; + tree.freeList = 0; + + tree.proxyCount = 0; + + tree.leafIndices = NULL; + tree.leafBoxes = NULL; + tree.leafCenters = NULL; + tree.binIndices = NULL; + tree.rebuildCapacity = 0; + + return tree; +} + +void b2DynamicTree_Destroy( b2DynamicTree* tree ) +{ + b2Free( tree->nodes, tree->nodeCapacity * sizeof( b2TreeNode ) ); + b2Free( tree->leafIndices, tree->rebuildCapacity * sizeof( int32_t ) ); + b2Free( tree->leafBoxes, tree->rebuildCapacity * sizeof( b2AABB ) ); + b2Free( tree->leafCenters, tree->rebuildCapacity * sizeof( b2Vec2 ) ); + b2Free( tree->binIndices, tree->rebuildCapacity * sizeof( int32_t ) ); + + memset( tree, 0, sizeof( b2DynamicTree ) ); +} + +// Allocate a node from the pool. Grow the pool if necessary. +static int32_t b2AllocateNode( b2DynamicTree* tree ) +{ + // Expand the node pool as needed. + if ( tree->freeList == B2_NULL_INDEX ) + { + B2_ASSERT( tree->nodeCount == tree->nodeCapacity ); + + // The free list is empty. Rebuild a bigger pool. + b2TreeNode* oldNodes = tree->nodes; + int32_t oldCapcity = tree->nodeCapacity; + tree->nodeCapacity += oldCapcity >> 1; + tree->nodes = (b2TreeNode*)b2Alloc( tree->nodeCapacity * sizeof( b2TreeNode ) ); + B2_ASSERT( oldNodes != NULL ); + memcpy( tree->nodes, oldNodes, tree->nodeCount * sizeof( b2TreeNode ) ); + b2Free( oldNodes, oldCapcity * sizeof( b2TreeNode ) ); + + // Build a linked list for the free list. The parent pointer becomes the "next" pointer. + // todo avoid building freelist? + for ( int32_t i = tree->nodeCount; i < tree->nodeCapacity - 1; ++i ) + { + tree->nodes[i].next = i + 1; + tree->nodes[i].height = -1; + } + tree->nodes[tree->nodeCapacity - 1].next = B2_NULL_INDEX; + tree->nodes[tree->nodeCapacity - 1].height = -1; + tree->freeList = tree->nodeCount; + } + + // Peel a node off the free list. + int32_t nodeIndex = tree->freeList; + b2TreeNode* node = tree->nodes + nodeIndex; + tree->freeList = node->next; + *node = b2_defaultTreeNode; + ++tree->nodeCount; + return nodeIndex; +} + +// Return a node to the pool. +static void b2FreeNode( b2DynamicTree* tree, int32_t nodeId ) +{ + B2_ASSERT( 0 <= nodeId && nodeId < tree->nodeCapacity ); + B2_ASSERT( 0 < tree->nodeCount ); + tree->nodes[nodeId].next = tree->freeList; + tree->nodes[nodeId].height = -1; + tree->freeList = nodeId; + --tree->nodeCount; +} + +// Greedy algorithm for sibling selection using the SAH +// We have three nodes A-(B,C) and want to add a leaf D, there are three choices. +// 1: make a new parent for A and D : E-(A-(B,C), D) +// 2: associate D with B +// a: B is a leaf : A-(E-(B,D), C) +// b: B is an internal node: A-(B{D},C) +// 3: associate D with C +// a: C is a leaf : A-(B, E-(C,D)) +// b: C is an internal node: A-(B, C{D}) +// All of these have a clear cost except when B or C is an internal node. Hence we need to be greedy. + +// The cost for cases 1, 2a, and 3a can be computed using the sibling cost formula. +// cost of sibling H = area(union(H, D)) + increased are of ancestors + +// Suppose B (or C) is an internal node, then the lowest cost would be one of two cases: +// case1: D becomes a sibling of B +// case2: D becomes a descendant of B along with a new internal node of area(D). +static int32_t b2FindBestSibling( const b2DynamicTree* tree, b2AABB boxD ) +{ + b2Vec2 centerD = b2AABB_Center( boxD ); + float areaD = b2Perimeter( boxD ); + + const b2TreeNode* nodes = tree->nodes; + int32_t rootIndex = tree->root; + + b2AABB rootBox = nodes[rootIndex].aabb; + + // Area of current node + float areaBase = b2Perimeter( rootBox ); + + // Area of inflated node + float directCost = b2Perimeter( b2AABB_Union( rootBox, boxD ) ); + float inheritedCost = 0.0f; + + int32_t bestSibling = rootIndex; + float bestCost = directCost; + + // Descend the tree from root, following a single greedy path. + int32_t index = rootIndex; + while ( nodes[index].height > 0 ) + { + int32_t child1 = nodes[index].child1; + int32_t child2 = nodes[index].child2; + + // Cost of creating a new parent for this node and the new leaf + float cost = directCost + inheritedCost; + + // Sometimes there are multiple identical costs within tolerance. + // This breaks the ties using the centroid distance. + if ( cost < bestCost ) + { + bestSibling = index; + bestCost = cost; + } + + // Inheritance cost seen by children + inheritedCost += directCost - areaBase; + + bool leaf1 = nodes[child1].height == 0; + bool leaf2 = nodes[child2].height == 0; + + // Cost of descending into child 1 + float lowerCost1 = FLT_MAX; + b2AABB box1 = nodes[child1].aabb; + float directCost1 = b2Perimeter( b2AABB_Union( box1, boxD ) ); + float area1 = 0.0f; + if ( leaf1 ) + { + // Child 1 is a leaf + // Cost of creating new node and increasing area of node P + float cost1 = directCost1 + inheritedCost; + + // Need this here due to while condition above + if ( cost1 < bestCost ) + { + bestSibling = child1; + bestCost = cost1; + } + } + else + { + // Child 1 is an internal node + area1 = b2Perimeter( box1 ); + + // Lower bound cost of inserting under child 1. + lowerCost1 = inheritedCost + directCost1 + b2MinFloat( areaD - area1, 0.0f ); + } + + // Cost of descending into child 2 + float lowerCost2 = FLT_MAX; + b2AABB box2 = nodes[child2].aabb; + float directCost2 = b2Perimeter( b2AABB_Union( box2, boxD ) ); + float area2 = 0.0f; + if ( leaf2 ) + { + // Child 2 is a leaf + // Cost of creating new node and increasing area of node P + float cost2 = directCost2 + inheritedCost; + + // Need this here due to while condition above + if ( cost2 < bestCost ) + { + bestSibling = child2; + bestCost = cost2; + } + } + else + { + // Child 2 is an internal node + area2 = b2Perimeter( box2 ); + + // Lower bound cost of inserting under child 2. This is not the cost + // of child 2, it is the best we can hope for under child 2. + lowerCost2 = inheritedCost + directCost2 + b2MinFloat( areaD - area2, 0.0f ); + } + + if ( leaf1 && leaf2 ) + { + break; + } + + // Can the cost possibly be decreased? + if ( bestCost <= lowerCost1 && bestCost <= lowerCost2 ) + { + break; + } + + if ( lowerCost1 == lowerCost2 && leaf1 == false ) + { + B2_ASSERT( lowerCost1 < FLT_MAX ); + B2_ASSERT( lowerCost2 < FLT_MAX ); + + // No clear choice based on lower bound surface area. This can happen when both + // children fully contain D. Fall back to node distance. + b2Vec2 d1 = b2Sub( b2AABB_Center( box1 ), centerD ); + b2Vec2 d2 = b2Sub( b2AABB_Center( box2 ), centerD ); + lowerCost1 = b2LengthSquared( d1 ); + lowerCost2 = b2LengthSquared( d2 ); + } + + // Descend + if ( lowerCost1 < lowerCost2 && leaf1 == false ) + { + index = child1; + areaBase = area1; + directCost = directCost1; + } + else + { + index = child2; + areaBase = area2; + directCost = directCost2; + } + + B2_ASSERT( nodes[index].height > 0 ); + } + + return bestSibling; +} + +enum b2RotateType +{ + b2_rotateNone, + b2_rotateBF, + b2_rotateBG, + b2_rotateCD, + b2_rotateCE +}; + +// Perform a left or right rotation if node A is imbalanced. +// Returns the new root index. +static void b2RotateNodes( b2DynamicTree* tree, int32_t iA ) +{ + B2_ASSERT( iA != B2_NULL_INDEX ); + + b2TreeNode* nodes = tree->nodes; + + b2TreeNode* A = nodes + iA; + if ( A->height < 2 ) + { + return; + } + + int32_t iB = A->child1; + int32_t iC = A->child2; + B2_ASSERT( 0 <= iB && iB < tree->nodeCapacity ); + B2_ASSERT( 0 <= iC && iC < tree->nodeCapacity ); + + b2TreeNode* B = nodes + iB; + b2TreeNode* C = nodes + iC; + + if ( B->height == 0 ) + { + // B is a leaf and C is internal + B2_ASSERT( C->height > 0 ); + + int32_t iF = C->child1; + int32_t iG = C->child2; + b2TreeNode* F = nodes + iF; + b2TreeNode* G = nodes + iG; + B2_ASSERT( 0 <= iF && iF < tree->nodeCapacity ); + B2_ASSERT( 0 <= iG && iG < tree->nodeCapacity ); + + // Base cost + float costBase = b2Perimeter( C->aabb ); + + // Cost of swapping B and F + b2AABB aabbBG = b2AABB_Union( B->aabb, G->aabb ); + float costBF = b2Perimeter( aabbBG ); + + // Cost of swapping B and G + b2AABB aabbBF = b2AABB_Union( B->aabb, F->aabb ); + float costBG = b2Perimeter( aabbBF ); + + if ( costBase < costBF && costBase < costBG ) + { + // Rotation does not improve cost + return; + } + + if ( costBF < costBG ) + { + // Swap B and F + A->child1 = iF; + C->child1 = iB; + + B->parent = iC; + F->parent = iA; + + C->aabb = aabbBG; + + C->height = 1 + b2MaxInt16( B->height, G->height ); + A->height = 1 + b2MaxInt16( C->height, F->height ); + C->categoryBits = B->categoryBits | G->categoryBits; + A->categoryBits = C->categoryBits | F->categoryBits; + C->enlarged = B->enlarged || G->enlarged; + A->enlarged = C->enlarged || F->enlarged; + } + else + { + // Swap B and G + A->child1 = iG; + C->child2 = iB; + + B->parent = iC; + G->parent = iA; + + C->aabb = aabbBF; + + C->height = 1 + b2MaxInt16( B->height, F->height ); + A->height = 1 + b2MaxInt16( C->height, G->height ); + C->categoryBits = B->categoryBits | F->categoryBits; + A->categoryBits = C->categoryBits | G->categoryBits; + C->enlarged = B->enlarged || F->enlarged; + A->enlarged = C->enlarged || G->enlarged; + } + } + else if ( C->height == 0 ) + { + // C is a leaf and B is internal + B2_ASSERT( B->height > 0 ); + + int iD = B->child1; + int iE = B->child2; + b2TreeNode* D = nodes + iD; + b2TreeNode* E = nodes + iE; + B2_ASSERT( 0 <= iD && iD < tree->nodeCapacity ); + B2_ASSERT( 0 <= iE && iE < tree->nodeCapacity ); + + // Base cost + float costBase = b2Perimeter( B->aabb ); + + // Cost of swapping C and D + b2AABB aabbCE = b2AABB_Union( C->aabb, E->aabb ); + float costCD = b2Perimeter( aabbCE ); + + // Cost of swapping C and E + b2AABB aabbCD = b2AABB_Union( C->aabb, D->aabb ); + float costCE = b2Perimeter( aabbCD ); + + if ( costBase < costCD && costBase < costCE ) + { + // Rotation does not improve cost + return; + } + + if ( costCD < costCE ) + { + // Swap C and D + A->child2 = iD; + B->child1 = iC; + + C->parent = iB; + D->parent = iA; + + B->aabb = aabbCE; + + B->height = 1 + b2MaxInt16( C->height, E->height ); + A->height = 1 + b2MaxInt16( B->height, D->height ); + B->categoryBits = C->categoryBits | E->categoryBits; + A->categoryBits = B->categoryBits | D->categoryBits; + B->enlarged = C->enlarged || E->enlarged; + A->enlarged = B->enlarged || D->enlarged; + } + else + { + // Swap C and E + A->child2 = iE; + B->child2 = iC; + + C->parent = iB; + E->parent = iA; + + B->aabb = aabbCD; + B->height = 1 + b2MaxInt16( C->height, D->height ); + A->height = 1 + b2MaxInt16( B->height, E->height ); + B->categoryBits = C->categoryBits | D->categoryBits; + A->categoryBits = B->categoryBits | E->categoryBits; + B->enlarged = C->enlarged || D->enlarged; + A->enlarged = B->enlarged || E->enlarged; + } + } + else + { + int iD = B->child1; + int iE = B->child2; + int iF = C->child1; + int iG = C->child2; + + b2TreeNode* D = nodes + iD; + b2TreeNode* E = nodes + iE; + b2TreeNode* F = nodes + iF; + b2TreeNode* G = nodes + iG; + + B2_ASSERT( 0 <= iD && iD < tree->nodeCapacity ); + B2_ASSERT( 0 <= iE && iE < tree->nodeCapacity ); + B2_ASSERT( 0 <= iF && iF < tree->nodeCapacity ); + B2_ASSERT( 0 <= iG && iG < tree->nodeCapacity ); + + // Base cost + float areaB = b2Perimeter( B->aabb ); + float areaC = b2Perimeter( C->aabb ); + float costBase = areaB + areaC; + enum b2RotateType bestRotation = b2_rotateNone; + float bestCost = costBase; + + // Cost of swapping B and F + b2AABB aabbBG = b2AABB_Union( B->aabb, G->aabb ); + float costBF = areaB + b2Perimeter( aabbBG ); + if ( costBF < bestCost ) + { + bestRotation = b2_rotateBF; + bestCost = costBF; + } + + // Cost of swapping B and G + b2AABB aabbBF = b2AABB_Union( B->aabb, F->aabb ); + float costBG = areaB + b2Perimeter( aabbBF ); + if ( costBG < bestCost ) + { + bestRotation = b2_rotateBG; + bestCost = costBG; + } + + // Cost of swapping C and D + b2AABB aabbCE = b2AABB_Union( C->aabb, E->aabb ); + float costCD = areaC + b2Perimeter( aabbCE ); + if ( costCD < bestCost ) + { + bestRotation = b2_rotateCD; + bestCost = costCD; + } + + // Cost of swapping C and E + b2AABB aabbCD = b2AABB_Union( C->aabb, D->aabb ); + float costCE = areaC + b2Perimeter( aabbCD ); + if ( costCE < bestCost ) + { + bestRotation = b2_rotateCE; + // bestCost = costCE; + } + + switch ( bestRotation ) + { + case b2_rotateNone: + break; + + case b2_rotateBF: + A->child1 = iF; + C->child1 = iB; + + B->parent = iC; + F->parent = iA; + + C->aabb = aabbBG; + C->height = 1 + b2MaxInt16( B->height, G->height ); + A->height = 1 + b2MaxInt16( C->height, F->height ); + C->categoryBits = B->categoryBits | G->categoryBits; + A->categoryBits = C->categoryBits | F->categoryBits; + C->enlarged = B->enlarged || G->enlarged; + A->enlarged = C->enlarged || F->enlarged; + break; + + case b2_rotateBG: + A->child1 = iG; + C->child2 = iB; + + B->parent = iC; + G->parent = iA; + + C->aabb = aabbBF; + C->height = 1 + b2MaxInt16( B->height, F->height ); + A->height = 1 + b2MaxInt16( C->height, G->height ); + C->categoryBits = B->categoryBits | F->categoryBits; + A->categoryBits = C->categoryBits | G->categoryBits; + C->enlarged = B->enlarged || F->enlarged; + A->enlarged = C->enlarged || G->enlarged; + break; + + case b2_rotateCD: + A->child2 = iD; + B->child1 = iC; + + C->parent = iB; + D->parent = iA; + + B->aabb = aabbCE; + B->height = 1 + b2MaxInt16( C->height, E->height ); + A->height = 1 + b2MaxInt16( B->height, D->height ); + B->categoryBits = C->categoryBits | E->categoryBits; + A->categoryBits = B->categoryBits | D->categoryBits; + B->enlarged = C->enlarged || E->enlarged; + A->enlarged = B->enlarged || D->enlarged; + break; + + case b2_rotateCE: + A->child2 = iE; + B->child2 = iC; + + C->parent = iB; + E->parent = iA; + + B->aabb = aabbCD; + B->height = 1 + b2MaxInt16( C->height, D->height ); + A->height = 1 + b2MaxInt16( B->height, E->height ); + B->categoryBits = C->categoryBits | D->categoryBits; + A->categoryBits = B->categoryBits | E->categoryBits; + B->enlarged = C->enlarged || D->enlarged; + A->enlarged = B->enlarged || E->enlarged; + break; + + default: + B2_ASSERT( false ); + break; + } + } +} + +static void b2InsertLeaf( b2DynamicTree* tree, int32_t leaf, bool shouldRotate ) +{ + if ( tree->root == B2_NULL_INDEX ) + { + tree->root = leaf; + tree->nodes[tree->root].parent = B2_NULL_INDEX; + return; + } + + // Stage 1: find the best sibling for this node + b2AABB leafAABB = tree->nodes[leaf].aabb; + int32_t sibling = b2FindBestSibling( tree, leafAABB ); + + // Stage 2: create a new parent for the leaf and sibling + int32_t oldParent = tree->nodes[sibling].parent; + int32_t newParent = b2AllocateNode( tree ); + + // warning: node pointer can change after allocation + b2TreeNode* nodes = tree->nodes; + nodes[newParent].parent = oldParent; + nodes[newParent].userData = -1; + nodes[newParent].aabb = b2AABB_Union( leafAABB, nodes[sibling].aabb ); + nodes[newParent].categoryBits = nodes[leaf].categoryBits | nodes[sibling].categoryBits; + nodes[newParent].height = nodes[sibling].height + 1; + + if ( oldParent != B2_NULL_INDEX ) + { + // The sibling was not the root. + if ( nodes[oldParent].child1 == sibling ) + { + nodes[oldParent].child1 = newParent; + } + else + { + nodes[oldParent].child2 = newParent; + } + + nodes[newParent].child1 = sibling; + nodes[newParent].child2 = leaf; + nodes[sibling].parent = newParent; + nodes[leaf].parent = newParent; + } + else + { + // The sibling was the root. + nodes[newParent].child1 = sibling; + nodes[newParent].child2 = leaf; + nodes[sibling].parent = newParent; + nodes[leaf].parent = newParent; + tree->root = newParent; + } + + // Stage 3: walk back up the tree fixing heights and AABBs + int32_t index = nodes[leaf].parent; + while ( index != B2_NULL_INDEX ) + { + int32_t child1 = nodes[index].child1; + int32_t child2 = nodes[index].child2; + + B2_ASSERT( child1 != B2_NULL_INDEX ); + B2_ASSERT( child2 != B2_NULL_INDEX ); + + nodes[index].aabb = b2AABB_Union( nodes[child1].aabb, nodes[child2].aabb ); + nodes[index].categoryBits = nodes[child1].categoryBits | nodes[child2].categoryBits; + nodes[index].height = 1 + b2MaxInt16( nodes[child1].height, nodes[child2].height ); + nodes[index].enlarged = nodes[child1].enlarged || nodes[child2].enlarged; + + if ( shouldRotate ) + { + b2RotateNodes( tree, index ); + } + + index = nodes[index].parent; + } +} + +static void b2RemoveLeaf( b2DynamicTree* tree, int32_t leaf ) +{ + if ( leaf == tree->root ) + { + tree->root = B2_NULL_INDEX; + return; + } + + b2TreeNode* nodes = tree->nodes; + + int32_t parent = nodes[leaf].parent; + int32_t grandParent = nodes[parent].parent; + int32_t sibling; + if ( nodes[parent].child1 == leaf ) + { + sibling = nodes[parent].child2; + } + else + { + sibling = nodes[parent].child1; + } + + if ( grandParent != B2_NULL_INDEX ) + { + // Destroy parent and connect sibling to grandParent. + if ( nodes[grandParent].child1 == parent ) + { + nodes[grandParent].child1 = sibling; + } + else + { + nodes[grandParent].child2 = sibling; + } + nodes[sibling].parent = grandParent; + b2FreeNode( tree, parent ); + + // Adjust ancestor bounds. + int32_t index = grandParent; + while ( index != B2_NULL_INDEX ) + { + b2TreeNode* node = nodes + index; + b2TreeNode* child1 = nodes + node->child1; + b2TreeNode* child2 = nodes + node->child2; + + // Fast union using SSE + //__m128 aabb1 = _mm_load_ps(&child1->aabb.lowerBound.x); + //__m128 aabb2 = _mm_load_ps(&child2->aabb.lowerBound.x); + //__m128 lower = _mm_min_ps(aabb1, aabb2); + //__m128 upper = _mm_max_ps(aabb1, aabb2); + //__m128 aabb = _mm_shuffle_ps(lower, upper, _MM_SHUFFLE(3, 2, 1, 0)); + //_mm_store_ps(&node->aabb.lowerBound.x, aabb); + + node->aabb = b2AABB_Union( child1->aabb, child2->aabb ); + node->categoryBits = child1->categoryBits | child2->categoryBits; + node->height = 1 + b2MaxInt16( child1->height, child2->height ); + + index = node->parent; + } + } + else + { + tree->root = sibling; + tree->nodes[sibling].parent = B2_NULL_INDEX; + b2FreeNode( tree, parent ); + } +} + +// Create a proxy in the tree as a leaf node. We return the index of the node instead of a pointer so that we can grow +// the node pool. +int32_t b2DynamicTree_CreateProxy( b2DynamicTree* tree, b2AABB aabb, uint64_t categoryBits, int32_t userData ) +{ + B2_ASSERT( -b2_huge < aabb.lowerBound.x && aabb.lowerBound.x < b2_huge ); + B2_ASSERT( -b2_huge < aabb.lowerBound.y && aabb.lowerBound.y < b2_huge ); + B2_ASSERT( -b2_huge < aabb.upperBound.x && aabb.upperBound.x < b2_huge ); + B2_ASSERT( -b2_huge < aabb.upperBound.y && aabb.upperBound.y < b2_huge ); + + int32_t proxyId = b2AllocateNode( tree ); + b2TreeNode* node = tree->nodes + proxyId; + + node->aabb = aabb; + node->userData = userData; + node->categoryBits = categoryBits; + node->height = 0; + + bool shouldRotate = true; + b2InsertLeaf( tree, proxyId, shouldRotate ); + + tree->proxyCount += 1; + + return proxyId; +} + +void b2DynamicTree_DestroyProxy( b2DynamicTree* tree, int32_t proxyId ) +{ + B2_ASSERT( 0 <= proxyId && proxyId < tree->nodeCapacity ); + B2_ASSERT( b2IsLeaf( tree->nodes + proxyId ) ); + + b2RemoveLeaf( tree, proxyId ); + b2FreeNode( tree, proxyId ); + + B2_ASSERT( tree->proxyCount > 0 ); + tree->proxyCount -= 1; +} + +int32_t b2DynamicTree_GetProxyCount( const b2DynamicTree* tree ) +{ + return tree->proxyCount; +} + +void b2DynamicTree_MoveProxy( b2DynamicTree* tree, int32_t proxyId, b2AABB aabb ) +{ + B2_ASSERT( b2AABB_IsValid( aabb ) ); + B2_ASSERT( aabb.upperBound.x - aabb.lowerBound.x < b2_huge ); + B2_ASSERT( aabb.upperBound.y - aabb.lowerBound.y < b2_huge ); + B2_ASSERT( 0 <= proxyId && proxyId < tree->nodeCapacity ); + B2_ASSERT( b2IsLeaf( tree->nodes + proxyId ) ); + + b2RemoveLeaf( tree, proxyId ); + + tree->nodes[proxyId].aabb = aabb; + + bool shouldRotate = false; + b2InsertLeaf( tree, proxyId, shouldRotate ); +} + +void b2DynamicTree_EnlargeProxy( b2DynamicTree* tree, int32_t proxyId, b2AABB aabb ) +{ + b2TreeNode* nodes = tree->nodes; + + B2_ASSERT( b2AABB_IsValid( aabb ) ); + B2_ASSERT( aabb.upperBound.x - aabb.lowerBound.x < b2_huge ); + B2_ASSERT( aabb.upperBound.y - aabb.lowerBound.y < b2_huge ); + B2_ASSERT( 0 <= proxyId && proxyId < tree->nodeCapacity ); + B2_ASSERT( b2IsLeaf( tree->nodes + proxyId ) ); + + // Caller must ensure this + B2_ASSERT( b2AABB_Contains( nodes[proxyId].aabb, aabb ) == false ); + + nodes[proxyId].aabb = aabb; + + int32_t parentIndex = nodes[proxyId].parent; + while ( parentIndex != B2_NULL_INDEX ) + { + bool changed = b2EnlargeAABB( &nodes[parentIndex].aabb, aabb ); + nodes[parentIndex].enlarged = true; + parentIndex = nodes[parentIndex].parent; + + if ( changed == false ) + { + break; + } + } + + while ( parentIndex != B2_NULL_INDEX ) + { + if ( nodes[parentIndex].enlarged == true ) + { + // early out because this ancestor was previously ascended and marked as enlarged + break; + } + + nodes[parentIndex].enlarged = true; + parentIndex = nodes[parentIndex].parent; + } +} + +int b2DynamicTree_GetHeight( const b2DynamicTree* tree ) +{ + if ( tree->root == B2_NULL_INDEX ) + { + return 0; + } + + return tree->nodes[tree->root].height; +} + +float b2DynamicTree_GetAreaRatio( const b2DynamicTree* tree ) +{ + if ( tree->root == B2_NULL_INDEX ) + { + return 0.0f; + } + + const b2TreeNode* root = tree->nodes + tree->root; + float rootArea = b2Perimeter( root->aabb ); + + float totalArea = 0.0f; + for ( int32_t i = 0; i < tree->nodeCapacity; ++i ) + { + const b2TreeNode* node = tree->nodes + i; + if ( node->height < 0 || b2IsLeaf( node ) || i == tree->root ) + { + // Free node in pool + continue; + } + + totalArea += b2Perimeter( node->aabb ); + } + + return totalArea / rootArea; +} + +// Compute the height of a sub-tree. +static int b2ComputeHeight( const b2DynamicTree* tree, int32_t nodeId ) +{ + B2_ASSERT( 0 <= nodeId && nodeId < tree->nodeCapacity ); + b2TreeNode* node = tree->nodes + nodeId; + + if ( b2IsLeaf( node ) ) + { + return 0; + } + + int32_t height1 = b2ComputeHeight( tree, node->child1 ); + int32_t height2 = b2ComputeHeight( tree, node->child2 ); + return 1 + b2MaxInt16( height1, height2 ); +} + +int b2DynamicTree_ComputeHeight( const b2DynamicTree* tree ) +{ + int height = b2ComputeHeight( tree, tree->root ); + return height; +} + +#if B2_VALIDATE +static void b2ValidateStructure( const b2DynamicTree* tree, int32_t index ) +{ + if ( index == B2_NULL_INDEX ) + { + return; + } + + if ( index == tree->root ) + { + B2_ASSERT( tree->nodes[index].parent == B2_NULL_INDEX ); + } + + const b2TreeNode* node = tree->nodes + index; + + int32_t child1 = node->child1; + int32_t child2 = node->child2; + + if ( b2IsLeaf( node ) ) + { + B2_ASSERT( child1 == B2_NULL_INDEX ); + B2_ASSERT( child2 == B2_NULL_INDEX ); + B2_ASSERT( node->height == 0 ); + return; + } + + B2_ASSERT( 0 <= child1 && child1 < tree->nodeCapacity ); + B2_ASSERT( 0 <= child2 && child2 < tree->nodeCapacity ); + + B2_ASSERT( tree->nodes[child1].parent == index ); + B2_ASSERT( tree->nodes[child2].parent == index ); + + if ( tree->nodes[child1].enlarged || tree->nodes[child2].enlarged ) + { + B2_ASSERT( node->enlarged == true ); + } + + b2ValidateStructure( tree, child1 ); + b2ValidateStructure( tree, child2 ); +} + +static void b2ValidateMetrics( const b2DynamicTree* tree, int32_t index ) +{ + if ( index == B2_NULL_INDEX ) + { + return; + } + + const b2TreeNode* node = tree->nodes + index; + + int32_t child1 = node->child1; + int32_t child2 = node->child2; + + if ( b2IsLeaf( node ) ) + { + B2_ASSERT( child1 == B2_NULL_INDEX ); + B2_ASSERT( child2 == B2_NULL_INDEX ); + B2_ASSERT( node->height == 0 ); + return; + } + + B2_ASSERT( 0 <= child1 && child1 < tree->nodeCapacity ); + B2_ASSERT( 0 <= child2 && child2 < tree->nodeCapacity ); + + int32_t height1 = tree->nodes[child1].height; + int32_t height2 = tree->nodes[child2].height; + int32_t height; + height = 1 + b2MaxInt16( height1, height2 ); + B2_ASSERT( node->height == height ); + + // b2AABB aabb = b2AABB_Union(tree->nodes[child1].aabb, tree->nodes[child2].aabb); + + B2_ASSERT( b2AABB_Contains( node->aabb, tree->nodes[child1].aabb ) ); + B2_ASSERT( b2AABB_Contains( node->aabb, tree->nodes[child2].aabb ) ); + + // B2_ASSERT(aabb.lowerBound.x == node->aabb.lowerBound.x); + // B2_ASSERT(aabb.lowerBound.y == node->aabb.lowerBound.y); + // B2_ASSERT(aabb.upperBound.x == node->aabb.upperBound.x); + // B2_ASSERT(aabb.upperBound.y == node->aabb.upperBound.y); + + uint64_t categoryBits = tree->nodes[child1].categoryBits | tree->nodes[child2].categoryBits; + B2_ASSERT( node->categoryBits == categoryBits ); + + b2ValidateMetrics( tree, child1 ); + b2ValidateMetrics( tree, child2 ); +} +#endif + +void b2DynamicTree_Validate( const b2DynamicTree* tree ) +{ +#if B2_VALIDATE + if ( tree->root == B2_NULL_INDEX ) + { + return; + } + + b2ValidateStructure( tree, tree->root ); + b2ValidateMetrics( tree, tree->root ); + + int32_t freeCount = 0; + int32_t freeIndex = tree->freeList; + while ( freeIndex != B2_NULL_INDEX ) + { + B2_ASSERT( 0 <= freeIndex && freeIndex < tree->nodeCapacity ); + freeIndex = tree->nodes[freeIndex].next; + ++freeCount; + } + + int32_t height = b2DynamicTree_GetHeight( tree ); + int32_t computedHeight = b2DynamicTree_ComputeHeight( tree ); + B2_ASSERT( height == computedHeight ); + + B2_ASSERT( tree->nodeCount + freeCount == tree->nodeCapacity ); +#else + B2_MAYBE_UNUSED( tree ); +#endif +} + +int32_t b2DynamicTree_GetMaxBalance( const b2DynamicTree* tree ) +{ + int32_t maxBalance = 0; + for ( int32_t i = 0; i < tree->nodeCapacity; ++i ) + { + const b2TreeNode* node = tree->nodes + i; + if ( node->height <= 1 ) + { + continue; + } + + B2_ASSERT( b2IsLeaf( node ) == false ); + + int32_t child1 = node->child1; + int32_t child2 = node->child2; + int32_t balance = b2AbsFloat( tree->nodes[child2].height - tree->nodes[child1].height ); + maxBalance = b2MaxInt( maxBalance, balance ); + } + + return maxBalance; +} + +void b2DynamicTree_RebuildBottomUp( b2DynamicTree* tree ) +{ + int32_t* nodes = b2Alloc( tree->nodeCount * sizeof( int32_t ) ); + int32_t count = 0; + + // Build array of leaves. Free the rest. + for ( int32_t i = 0; i < tree->nodeCapacity; ++i ) + { + if ( tree->nodes[i].height < 0 ) + { + // free node in pool + continue; + } + + if ( b2IsLeaf( tree->nodes + i ) ) + { + tree->nodes[i].parent = B2_NULL_INDEX; + nodes[count] = i; + ++count; + } + else + { + b2FreeNode( tree, i ); + } + } + + while ( count > 1 ) + { + float minCost = FLT_MAX; + int32_t iMin = -1, jMin = -1; + for ( int32_t i = 0; i < count; ++i ) + { + b2AABB aabbi = tree->nodes[nodes[i]].aabb; + + for ( int32_t j = i + 1; j < count; ++j ) + { + b2AABB aabbj = tree->nodes[nodes[j]].aabb; + b2AABB b = b2AABB_Union( aabbi, aabbj ); + float cost = b2Perimeter( b ); + if ( cost < minCost ) + { + iMin = i; + jMin = j; + minCost = cost; + } + } + } + + int32_t index1 = nodes[iMin]; + int32_t index2 = nodes[jMin]; + b2TreeNode* child1 = tree->nodes + index1; + b2TreeNode* child2 = tree->nodes + index2; + + int32_t parentIndex = b2AllocateNode( tree ); + b2TreeNode* parent = tree->nodes + parentIndex; + parent->child1 = index1; + parent->child2 = index2; + parent->aabb = b2AABB_Union( child1->aabb, child2->aabb ); + parent->categoryBits = child1->categoryBits | child2->categoryBits; + parent->height = 1 + b2MaxInt16( child1->height, child2->height ); + parent->parent = B2_NULL_INDEX; + + child1->parent = parentIndex; + child2->parent = parentIndex; + + nodes[jMin] = nodes[count - 1]; + nodes[iMin] = parentIndex; + --count; + } + + tree->root = nodes[0]; + b2Free( nodes, tree->nodeCount * sizeof( b2TreeNode ) ); + + b2DynamicTree_Validate( tree ); +} + +void b2DynamicTree_ShiftOrigin( b2DynamicTree* tree, b2Vec2 newOrigin ) +{ + // shift all AABBs + for ( int32_t i = 0; i < tree->nodeCapacity; ++i ) + { + b2TreeNode* n = tree->nodes + i; + n->aabb.lowerBound.x -= newOrigin.x; + n->aabb.lowerBound.y -= newOrigin.y; + n->aabb.upperBound.x -= newOrigin.x; + n->aabb.upperBound.y -= newOrigin.y; + } +} + +int b2DynamicTree_GetByteCount( const b2DynamicTree* tree ) +{ + size_t size = sizeof( b2DynamicTree ) + sizeof( b2TreeNode ) * tree->nodeCapacity + + tree->rebuildCapacity * ( sizeof( int32_t ) + sizeof( b2AABB ) + sizeof( b2Vec2 ) + sizeof( int32_t ) ); + + return (int)size; +} + +void b2DynamicTree_Query( const b2DynamicTree* tree, b2AABB aabb, uint64_t maskBits, b2TreeQueryCallbackFcn* callback, + void* context ) +{ + int32_t stack[b2_treeStackSize]; + int32_t stackCount = 0; + stack[stackCount++] = tree->root; + + while ( stackCount > 0 ) + { + int32_t nodeId = stack[--stackCount]; + if ( nodeId == B2_NULL_INDEX ) + { + continue; + } + + const b2TreeNode* node = tree->nodes + nodeId; + + if ( b2AABB_Overlaps( node->aabb, aabb ) && ( node->categoryBits & maskBits ) != 0 ) + { + if ( b2IsLeaf( node ) ) + { + // callback to user code with proxy id + bool proceed = callback( nodeId, node->userData, context ); + if ( proceed == false ) + { + return; + } + } + else + { + B2_ASSERT( stackCount < b2_treeStackSize - 1 ); + if ( stackCount < b2_treeStackSize - 1 ) + { + stack[stackCount++] = node->child1; + stack[stackCount++] = node->child2; + } + } + } + } +} + +void b2DynamicTree_RayCast( const b2DynamicTree* tree, const b2RayCastInput* input, uint64_t maskBits, + b2TreeRayCastCallbackFcn* callback, void* context ) +{ + b2Vec2 p1 = input->origin; + b2Vec2 d = input->translation; + + b2Vec2 r = b2Normalize( d ); + + // v is perpendicular to the segment. + b2Vec2 v = b2CrossSV( 1.0f, r ); + b2Vec2 abs_v = b2Abs( v ); + + // Separating axis for segment (Gino, p80). + // |dot(v, p1 - c)| > dot(|v|, h) + + float maxFraction = input->maxFraction; + + b2Vec2 p2 = b2MulAdd( p1, maxFraction, d ); + + // Build a bounding box for the segment. + b2AABB segmentAABB = { b2Min( p1, p2 ), b2Max( p1, p2 ) }; + + int32_t stack[b2_treeStackSize]; + int32_t stackCount = 0; + stack[stackCount++] = tree->root; + + b2RayCastInput subInput = *input; + + while ( stackCount > 0 ) + { + int32_t nodeId = stack[--stackCount]; + if ( nodeId == B2_NULL_INDEX ) + { + continue; + } + + const b2TreeNode* node = tree->nodes + nodeId; + if ( b2AABB_Overlaps( node->aabb, segmentAABB ) == false || ( node->categoryBits & maskBits ) == 0 ) + { + continue; + } + + // Separating axis for segment (Gino, p80). + // |dot(v, p1 - c)| > dot(|v|, h) + // radius extension is added to the node in this case + b2Vec2 c = b2AABB_Center( node->aabb ); + b2Vec2 h = b2AABB_Extents( node->aabb ); + float term1 = b2AbsFloat( b2Dot( v, b2Sub( p1, c ) ) ); + float term2 = b2Dot( abs_v, h ); + if ( term2 < term1 ) + { + continue; + } + + if ( b2IsLeaf( node ) ) + { + subInput.maxFraction = maxFraction; + + float value = callback( &subInput, nodeId, node->userData, context ); + + if ( value == 0.0f ) + { + // The client has terminated the ray cast. + return; + } + + if ( 0.0f < value && value < maxFraction ) + { + // Update segment bounding box. + maxFraction = value; + p2 = b2MulAdd( p1, maxFraction, d ); + segmentAABB.lowerBound = b2Min( p1, p2 ); + segmentAABB.upperBound = b2Max( p1, p2 ); + } + } + else + { + B2_ASSERT( stackCount < b2_treeStackSize - 1 ); + if ( stackCount < b2_treeStackSize - 1 ) + { + // TODO_ERIN just put one node on the stack, continue on a child node + // TODO_ERIN test ordering children by nearest to ray origin + stack[stackCount++] = node->child1; + stack[stackCount++] = node->child2; + } + } + } +} + +void b2DynamicTree_ShapeCast( const b2DynamicTree* tree, const b2ShapeCastInput* input, uint64_t maskBits, + b2TreeShapeCastCallbackFcn* callback, void* context ) +{ + if ( input->count == 0 ) + { + return; + } + + b2AABB originAABB = { input->points[0], input->points[0] }; + for ( int i = 1; i < input->count; ++i ) + { + originAABB.lowerBound = b2Min( originAABB.lowerBound, input->points[i] ); + originAABB.upperBound = b2Max( originAABB.upperBound, input->points[i] ); + } + + b2Vec2 radius = { input->radius, input->radius }; + + originAABB.lowerBound = b2Sub( originAABB.lowerBound, radius ); + originAABB.upperBound = b2Add( originAABB.upperBound, radius ); + + b2Vec2 p1 = b2AABB_Center( originAABB ); + b2Vec2 extension = b2AABB_Extents( originAABB ); + + // v is perpendicular to the segment. + b2Vec2 r = input->translation; + b2Vec2 v = b2CrossSV( 1.0f, r ); + b2Vec2 abs_v = b2Abs( v ); + + // Separating axis for segment (Gino, p80). + // |dot(v, p1 - c)| > dot(|v|, h) + + float maxFraction = input->maxFraction; + + // Build total box for the shape cast + b2Vec2 t = b2MulSV( maxFraction, input->translation ); + b2AABB totalAABB = { + b2Min( originAABB.lowerBound, b2Add( originAABB.lowerBound, t ) ), + b2Max( originAABB.upperBound, b2Add( originAABB.upperBound, t ) ), + }; + + b2ShapeCastInput subInput = *input; + + int32_t stack[b2_treeStackSize]; + int32_t stackCount = 0; + stack[stackCount++] = tree->root; + + while ( stackCount > 0 ) + { + int32_t nodeId = stack[--stackCount]; + if ( nodeId == B2_NULL_INDEX ) + { + continue; + } + + const b2TreeNode* node = tree->nodes + nodeId; + if ( b2AABB_Overlaps( node->aabb, totalAABB ) == false || ( node->categoryBits & maskBits ) == 0 ) + { + continue; + } + + // Separating axis for segment (Gino, p80). + // |dot(v, p1 - c)| > dot(|v|, h) + // radius extension is added to the node in this case + b2Vec2 c = b2AABB_Center( node->aabb ); + b2Vec2 h = b2Add( b2AABB_Extents( node->aabb ), extension ); + float term1 = b2AbsFloat( b2Dot( v, b2Sub( p1, c ) ) ); + float term2 = b2Dot( abs_v, h ); + if ( term2 < term1 ) + { + continue; + } + + if ( b2IsLeaf( node ) ) + { + subInput.maxFraction = maxFraction; + + float value = callback( &subInput, nodeId, node->userData, context ); + + if ( value == 0.0f ) + { + // The client has terminated the ray cast. + return; + } + + if ( 0.0f < value && value < maxFraction ) + { + // Update segment bounding box. + maxFraction = value; + t = b2MulSV( maxFraction, input->translation ); + totalAABB.lowerBound = b2Min( originAABB.lowerBound, b2Add( originAABB.lowerBound, t ) ); + totalAABB.upperBound = b2Max( originAABB.upperBound, b2Add( originAABB.upperBound, t ) ); + } + } + else + { + B2_ASSERT( stackCount < b2_treeStackSize - 1 ); + if ( stackCount < b2_treeStackSize - 1 ) + { + // TODO_ERIN just put one node on the stack, continue on a child node + // TODO_ERIN test ordering children by nearest to ray origin + stack[stackCount++] = node->child1; + stack[stackCount++] = node->child2; + } + } + } +} + +// Median split == 0, Surface area heuristic == 1 +#define B2_TREE_HEURISTIC 0 + +#if B2_TREE_HEURISTIC == 0 + +// Median split heuristic +static int32_t b2PartitionMid( int32_t* indices, b2Vec2* centers, int32_t count ) +{ + // Handle trivial case + if ( count <= 2 ) + { + return count / 2; + } + + // todo SIMD? + b2Vec2 lowerBound = centers[0]; + b2Vec2 upperBound = centers[0]; + + for ( int32_t i = 1; i < count; ++i ) + { + lowerBound = b2Min( lowerBound, centers[i] ); + upperBound = b2Max( upperBound, centers[i] ); + } + + b2Vec2 d = b2Sub( upperBound, lowerBound ); + b2Vec2 c = { 0.5f * ( lowerBound.x + upperBound.x ), 0.5f * ( lowerBound.y + upperBound.y ) }; + + // Partition longest axis using the Hoare partition scheme + // https://en.wikipedia.org/wiki/Quicksort + // https://nicholasvadivelu.com/2021/01/11/array-partition/ + int32_t i1 = 0, i2 = count; + if ( d.x > d.y ) + { + float pivot = c.x; + + while ( i1 < i2 ) + { + while ( i1 < i2 && centers[i1].x < pivot ) + { + i1 += 1; + }; + + while ( i1 < i2 && centers[i2 - 1].x >= pivot ) + { + i2 -= 1; + }; + + if ( i1 < i2 ) + { + // Swap indices + { + int32_t temp = indices[i1]; + indices[i1] = indices[i2 - 1]; + indices[i2 - 1] = temp; + } + + // Swap centers + { + b2Vec2 temp = centers[i1]; + centers[i1] = centers[i2 - 1]; + centers[i2 - 1] = temp; + } + + i1 += 1; + i2 -= 1; + } + } + } + else + { + float pivot = c.y; + + while ( i1 < i2 ) + { + while ( i1 < i2 && centers[i1].y < pivot ) + { + i1 += 1; + }; + + while ( i1 < i2 && centers[i2 - 1].y >= pivot ) + { + i2 -= 1; + }; + + if ( i1 < i2 ) + { + // Swap indices + { + int32_t temp = indices[i1]; + indices[i1] = indices[i2 - 1]; + indices[i2 - 1] = temp; + } + + // Swap centers + { + b2Vec2 temp = centers[i1]; + centers[i1] = centers[i2 - 1]; + centers[i2 - 1] = temp; + } + + i1 += 1; + i2 -= 1; + } + } + } + B2_ASSERT( i1 == i2 ); + + if ( i1 > 0 && i1 < count ) + { + return i1; + } + else + { + return count / 2; + } +} + +#else + + #define B2_BIN_COUNT 8 + +typedef struct b2TreeBin +{ + b2AABB aabb; + int32_t count; +} b2TreeBin; + +typedef struct b2TreePlane +{ + b2AABB leftAABB; + b2AABB rightAABB; + int32_t leftCount; + int32_t rightCount; +} b2TreePlane; + +// "On Fast Construction of SAH-based Bounding Volume Hierarchies" by Ingo Wald +// Returns the left child count +static int32_t b2PartitionSAH( int32_t* indices, int32_t* binIndices, b2AABB* boxes, int32_t count ) +{ + B2_ASSERT( count > 0 ); + + b2TreeBin bins[B2_BIN_COUNT]; + b2TreePlane planes[B2_BIN_COUNT - 1]; + + b2Vec2 center = b2AABB_Center( boxes[0] ); + b2AABB centroidAABB; + centroidAABB.lowerBound = center; + centroidAABB.upperBound = center; + + for ( int32_t i = 1; i < count; ++i ) + { + center = b2AABB_Center( boxes[i] ); + centroidAABB.lowerBound = b2Min( centroidAABB.lowerBound, center ); + centroidAABB.upperBound = b2Max( centroidAABB.upperBound, center ); + } + + b2Vec2 d = b2Sub( centroidAABB.upperBound, centroidAABB.lowerBound ); + + // Find longest axis + int32_t axisIndex; + float invD; + if ( d.x > d.y ) + { + axisIndex = 0; + invD = d.x; + } + else + { + axisIndex = 1; + invD = d.y; + } + + invD = invD > 0.0f ? 1.0f / invD : 0.0f; + + // Initialize bin bounds and count + for ( int32_t i = 0; i < B2_BIN_COUNT; ++i ) + { + bins[i].aabb.lowerBound = ( b2Vec2 ){ FLT_MAX, FLT_MAX }; + bins[i].aabb.upperBound = ( b2Vec2 ){ -FLT_MAX, -FLT_MAX }; + bins[i].count = 0; + } + + // Assign boxes to bins and compute bin boxes + // TODO_ERIN optimize + float binCount = B2_BIN_COUNT; + float lowerBoundArray[2] = { centroidAABB.lowerBound.x, centroidAABB.lowerBound.y }; + float minC = lowerBoundArray[axisIndex]; + for ( int32_t i = 0; i < count; ++i ) + { + b2Vec2 c = b2AABB_Center( boxes[i] ); + float cArray[2] = { c.x, c.y }; + int32_t binIndex = (int32_t)( binCount * ( cArray[axisIndex] - minC ) * invD ); + binIndex = b2ClampInt( binIndex, 0, B2_BIN_COUNT - 1 ); + binIndices[i] = binIndex; + bins[binIndex].count += 1; + bins[binIndex].aabb = b2AABB_Union( bins[binIndex].aabb, boxes[i] ); + } + + int32_t planeCount = B2_BIN_COUNT - 1; + + // Prepare all the left planes, candidates for left child + planes[0].leftCount = bins[0].count; + planes[0].leftAABB = bins[0].aabb; + for ( int32_t i = 1; i < planeCount; ++i ) + { + planes[i].leftCount = planes[i - 1].leftCount + bins[i].count; + planes[i].leftAABB = b2AABB_Union( planes[i - 1].leftAABB, bins[i].aabb ); + } + + // Prepare all the right planes, candidates for right child + planes[planeCount - 1].rightCount = bins[planeCount].count; + planes[planeCount - 1].rightAABB = bins[planeCount].aabb; + for ( int32_t i = planeCount - 2; i >= 0; --i ) + { + planes[i].rightCount = planes[i + 1].rightCount + bins[i + 1].count; + planes[i].rightAABB = b2AABB_Union( planes[i + 1].rightAABB, bins[i + 1].aabb ); + } + + // Find best split to minimize SAH + float minCost = FLT_MAX; + int32_t bestPlane = 0; + for ( int32_t i = 0; i < planeCount; ++i ) + { + float leftArea = b2Perimeter( planes[i].leftAABB ); + float rightArea = b2Perimeter( planes[i].rightAABB ); + int32_t leftCount = planes[i].leftCount; + int32_t rightCount = planes[i].rightCount; + + float cost = leftCount * leftArea + rightCount * rightArea; + if ( cost < minCost ) + { + bestPlane = i; + minCost = cost; + } + } + + // Partition node indices and boxes using the Hoare partition scheme + // https://en.wikipedia.org/wiki/Quicksort + // https://nicholasvadivelu.com/2021/01/11/array-partition/ + int32_t i1 = 0, i2 = count; + while ( i1 < i2 ) + { + while ( i1 < i2 && binIndices[i1] < bestPlane ) + { + i1 += 1; + }; + + while ( i1 < i2 && binIndices[i2 - 1] >= bestPlane ) + { + i2 -= 1; + }; + + if ( i1 < i2 ) + { + // Swap indices + { + int32_t temp = indices[i1]; + indices[i1] = indices[i2 - 1]; + indices[i2 - 1] = temp; + } + + // Swap boxes + { + b2AABB temp = boxes[i1]; + boxes[i1] = boxes[i2 - 1]; + boxes[i2 - 1] = temp; + } + + i1 += 1; + i2 -= 1; + } + } + B2_ASSERT( i1 == i2 ); + + if ( i1 > 0 && i1 < count ) + { + return i1; + } + else + { + return count / 2; + } +} + +#endif + +// Temporary data used to track the rebuild of a tree node +struct b2RebuildItem +{ + int32_t nodeIndex; + int32_t childCount; + + // Leaf indices + int32_t startIndex; + int32_t splitIndex; + int32_t endIndex; +}; + +// Returns root node index +static int32_t b2BuildTree( b2DynamicTree* tree, int32_t leafCount ) +{ + b2TreeNode* nodes = tree->nodes; + int32_t* leafIndices = tree->leafIndices; + + if ( leafCount == 1 ) + { + nodes[leafIndices[0]].parent = B2_NULL_INDEX; + return leafIndices[0]; + } + +#if B2_TREE_HEURISTIC == 0 + b2Vec2* leafCenters = tree->leafCenters; +#else + b2AABB* leafBoxes = tree->leafBoxes; + int32_t* binIndices = tree->binIndices; +#endif + + // todo large stack item + struct b2RebuildItem stack[b2_treeStackSize]; + int32_t top = 0; + + stack[0].nodeIndex = b2AllocateNode( tree ); + stack[0].childCount = -1; + stack[0].startIndex = 0; + stack[0].endIndex = leafCount; +#if B2_TREE_HEURISTIC == 0 + stack[0].splitIndex = b2PartitionMid( leafIndices, leafCenters, leafCount ); +#else + stack[0].splitIndex = b2PartitionSAH( leafIndices, binIndices, leafBoxes, leafCount ); +#endif + + while ( true ) + { + struct b2RebuildItem* item = stack + top; + + item->childCount += 1; + + if ( item->childCount == 2 ) + { + // This internal node has both children established + + if ( top == 0 ) + { + // all done + break; + } + + struct b2RebuildItem* parentItem = stack + ( top - 1 ); + b2TreeNode* parentNode = nodes + parentItem->nodeIndex; + + if ( parentItem->childCount == 0 ) + { + B2_ASSERT( parentNode->child1 == B2_NULL_INDEX ); + parentNode->child1 = item->nodeIndex; + } + else + { + B2_ASSERT( parentItem->childCount == 1 ); + B2_ASSERT( parentNode->child2 == B2_NULL_INDEX ); + parentNode->child2 = item->nodeIndex; + } + + b2TreeNode* node = nodes + item->nodeIndex; + + B2_ASSERT( node->parent == B2_NULL_INDEX ); + node->parent = parentItem->nodeIndex; + + B2_ASSERT( node->child1 != B2_NULL_INDEX ); + B2_ASSERT( node->child2 != B2_NULL_INDEX ); + b2TreeNode* child1 = nodes + node->child1; + b2TreeNode* child2 = nodes + node->child2; + + node->aabb = b2AABB_Union( child1->aabb, child2->aabb ); + node->height = 1 + b2MaxInt16( child1->height, child2->height ); + node->categoryBits = child1->categoryBits | child2->categoryBits; + + // Pop stack + top -= 1; + } + else + { + int32_t startIndex, endIndex; + if ( item->childCount == 0 ) + { + startIndex = item->startIndex; + endIndex = item->splitIndex; + } + else + { + B2_ASSERT( item->childCount == 1 ); + startIndex = item->splitIndex; + endIndex = item->endIndex; + } + + int32_t count = endIndex - startIndex; + + if ( count == 1 ) + { + int32_t childIndex = leafIndices[startIndex]; + b2TreeNode* node = nodes + item->nodeIndex; + + if ( item->childCount == 0 ) + { + B2_ASSERT( node->child1 == B2_NULL_INDEX ); + node->child1 = childIndex; + } + else + { + B2_ASSERT( item->childCount == 1 ); + B2_ASSERT( node->child2 == B2_NULL_INDEX ); + node->child2 = childIndex; + } + + b2TreeNode* childNode = nodes + childIndex; + B2_ASSERT( childNode->parent == B2_NULL_INDEX ); + childNode->parent = item->nodeIndex; + } + else + { + B2_ASSERT( count > 0 ); + B2_ASSERT( top < b2_treeStackSize ); + + top += 1; + struct b2RebuildItem* newItem = stack + top; + newItem->nodeIndex = b2AllocateNode( tree ); + newItem->childCount = -1; + newItem->startIndex = startIndex; + newItem->endIndex = endIndex; +#if B2_TREE_HEURISTIC == 0 + newItem->splitIndex = b2PartitionMid( leafIndices + startIndex, leafCenters + startIndex, count ); +#else + newItem->splitIndex = + b2PartitionSAH( leafIndices + startIndex, binIndices + startIndex, leafBoxes + startIndex, count ); +#endif + newItem->splitIndex += startIndex; + } + } + } + + b2TreeNode* rootNode = nodes + stack[0].nodeIndex; + B2_ASSERT( rootNode->parent == B2_NULL_INDEX ); + B2_ASSERT( rootNode->child1 != B2_NULL_INDEX ); + B2_ASSERT( rootNode->child2 != B2_NULL_INDEX ); + + b2TreeNode* child1 = nodes + rootNode->child1; + b2TreeNode* child2 = nodes + rootNode->child2; + + rootNode->aabb = b2AABB_Union( child1->aabb, child2->aabb ); + rootNode->height = 1 + b2MaxInt16( child1->height, child2->height ); + rootNode->categoryBits = child1->categoryBits | child2->categoryBits; + + return stack[0].nodeIndex; +} + +// Not safe to access tree during this operation because it may grow +int32_t b2DynamicTree_Rebuild( b2DynamicTree* tree, bool fullBuild ) +{ + int32_t proxyCount = tree->proxyCount; + if ( proxyCount == 0 ) + { + return 0; + } + + // Ensure capacity for rebuild space + if ( proxyCount > tree->rebuildCapacity ) + { + int32_t newCapacity = proxyCount + proxyCount / 2; + + b2Free( tree->leafIndices, tree->rebuildCapacity * sizeof( int32_t ) ); + tree->leafIndices = b2Alloc( newCapacity * sizeof( int32_t ) ); + +#if B2_TREE_HEURISTIC == 0 + b2Free( tree->leafCenters, tree->rebuildCapacity * sizeof( b2Vec2 ) ); + tree->leafCenters = b2Alloc( newCapacity * sizeof( b2Vec2 ) ); +#else + b2Free( tree->leafBoxes, tree->rebuildCapacity * sizeof( b2AABB ) ); + tree->leafBoxes = b2Alloc( newCapacity * sizeof( b2AABB ) ); + b2Free( tree->binIndices, tree->rebuildCapacity * sizeof( int32_t ) ); + tree->binIndices = b2Alloc( newCapacity * sizeof( int32_t ) ); +#endif + tree->rebuildCapacity = newCapacity; + } + + int32_t leafCount = 0; + int32_t stack[b2_treeStackSize]; + int32_t stackCount = 0; + + int32_t nodeIndex = tree->root; + b2TreeNode* nodes = tree->nodes; + b2TreeNode* node = nodes + nodeIndex; + + // These are the nodes that get sorted to rebuild the tree. + // I'm using indices because the node pool may grow during the build. + int32_t* leafIndices = tree->leafIndices; + +#if B2_TREE_HEURISTIC == 0 + b2Vec2* leafCenters = tree->leafCenters; +#else + b2AABB* leafBoxes = tree->leafBoxes; +#endif + + // Gather all proxy nodes that have grown and all internal nodes that haven't grown. Both are + // considered leaves in the tree rebuild. + // Free all internal nodes that have grown. + // todo use a node growth metric instead of simply enlarged to reduce rebuild size and frequency + // this should be weighed against b2_aabbMargin + while ( true ) + { + if ( node->height == 0 || ( node->enlarged == false && fullBuild == false ) ) + { + leafIndices[leafCount] = nodeIndex; +#if B2_TREE_HEURISTIC == 0 + leafCenters[leafCount] = b2AABB_Center( node->aabb ); +#else + leafBoxes[leafCount] = node->aabb; +#endif + leafCount += 1; + + // Detach + node->parent = B2_NULL_INDEX; + } + else + { + int32_t doomedNodeIndex = nodeIndex; + + // Handle children + nodeIndex = node->child1; + + B2_ASSERT( stackCount < b2_treeStackSize ); + if ( stackCount < b2_treeStackSize ) + { + stack[stackCount++] = node->child2; + } + + node = nodes + nodeIndex; + + // Remove doomed node + b2FreeNode( tree, doomedNodeIndex ); + + continue; + } + + if ( stackCount == 0 ) + { + break; + } + + nodeIndex = stack[--stackCount]; + node = nodes + nodeIndex; + } + +#if B2_VALIDATE == 1 + int32_t capacity = tree->nodeCapacity; + for ( int32_t i = 0; i < capacity; ++i ) + { + if ( nodes[i].height >= 0 ) + { + B2_ASSERT( nodes[i].enlarged == false ); + } + } +#endif + + B2_ASSERT( leafCount <= proxyCount ); + + tree->root = b2BuildTree( tree, leafCount ); + + b2DynamicTree_Validate( tree ); + + return leafCount; +} diff --git a/3rdparty/box2d/src/dynamics/b2_body.cpp b/3rdparty/box2d/src/dynamics/b2_body.cpp deleted file mode 100644 index 00f36856dde1..000000000000 --- a/3rdparty/box2d/src/dynamics/b2_body.cpp +++ /dev/null @@ -1,570 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "box2d/b2_body.h" -#include "box2d/b2_contact.h" -#include "box2d/b2_fixture.h" -#include "box2d/b2_joint.h" -#include "box2d/b2_world.h" - -#include - -b2Body::b2Body(const b2BodyDef* bd, b2World* world) -{ - b2Assert(bd->position.IsValid()); - b2Assert(bd->linearVelocity.IsValid()); - b2Assert(b2IsValid(bd->angle)); - b2Assert(b2IsValid(bd->angularVelocity)); - b2Assert(b2IsValid(bd->angularDamping) && bd->angularDamping >= 0.0f); - b2Assert(b2IsValid(bd->linearDamping) && bd->linearDamping >= 0.0f); - - m_flags = 0; - - if (bd->bullet) - { - m_flags |= e_bulletFlag; - } - if (bd->fixedRotation) - { - m_flags |= e_fixedRotationFlag; - } - if (bd->allowSleep) - { - m_flags |= e_autoSleepFlag; - } - if (bd->awake && bd->type != b2_staticBody) - { - m_flags |= e_awakeFlag; - } - if (bd->enabled) - { - m_flags |= e_enabledFlag; - } - - m_world = world; - - m_xf.p = bd->position; - m_xf.q.Set(bd->angle); - - m_sweep.localCenter.SetZero(); - m_sweep.c0 = m_xf.p; - m_sweep.c = m_xf.p; - m_sweep.a0 = bd->angle; - m_sweep.a = bd->angle; - m_sweep.alpha0 = 0.0f; - - m_jointList = nullptr; - m_contactList = nullptr; - m_prev = nullptr; - m_next = nullptr; - - m_linearVelocity = bd->linearVelocity; - m_angularVelocity = bd->angularVelocity; - - m_linearDamping = bd->linearDamping; - m_angularDamping = bd->angularDamping; - m_gravityScale = bd->gravityScale; - - m_force.SetZero(); - m_torque = 0.0f; - - m_sleepTime = 0.0f; - - m_type = bd->type; - - m_mass = 0.0f; - m_invMass = 0.0f; - - m_I = 0.0f; - m_invI = 0.0f; - - m_userData = bd->userData; - - m_fixtureList = nullptr; - m_fixtureCount = 0; -} - -b2Body::~b2Body() -{ - // shapes and joints are destroyed in b2World::Destroy -} - -void b2Body::SetType(b2BodyType type) -{ - b2Assert(m_world->IsLocked() == false); - if (m_world->IsLocked() == true) - { - return; - } - - if (m_type == type) - { - return; - } - - m_type = type; - - ResetMassData(); - - if (m_type == b2_staticBody) - { - m_linearVelocity.SetZero(); - m_angularVelocity = 0.0f; - m_sweep.a0 = m_sweep.a; - m_sweep.c0 = m_sweep.c; - m_flags &= ~e_awakeFlag; - SynchronizeFixtures(); - } - - SetAwake(true); - - m_force.SetZero(); - m_torque = 0.0f; - - // Delete the attached contacts. - b2ContactEdge* ce = m_contactList; - while (ce) - { - b2ContactEdge* ce0 = ce; - ce = ce->next; - m_world->m_contactManager.Destroy(ce0->contact); - } - m_contactList = nullptr; - - // Touch the proxies so that new contacts will be created (when appropriate) - b2BroadPhase* broadPhase = &m_world->m_contactManager.m_broadPhase; - for (b2Fixture* f = m_fixtureList; f; f = f->m_next) - { - int32 proxyCount = f->m_proxyCount; - for (int32 i = 0; i < proxyCount; ++i) - { - broadPhase->TouchProxy(f->m_proxies[i].proxyId); - } - } -} - -b2Fixture* b2Body::CreateFixture(const b2FixtureDef* def) -{ - b2Assert(m_world->IsLocked() == false); - if (m_world->IsLocked() == true) - { - return nullptr; - } - - b2BlockAllocator* allocator = &m_world->m_blockAllocator; - - void* memory = allocator->Allocate(sizeof(b2Fixture)); - b2Fixture* fixture = new (memory) b2Fixture; - fixture->Create(allocator, this, def); - - if (m_flags & e_enabledFlag) - { - b2BroadPhase* broadPhase = &m_world->m_contactManager.m_broadPhase; - fixture->CreateProxies(broadPhase, m_xf); - } - - fixture->m_next = m_fixtureList; - m_fixtureList = fixture; - ++m_fixtureCount; - - fixture->m_body = this; - - // Adjust mass properties if needed. - if (fixture->m_density > 0.0f) - { - ResetMassData(); - } - - // Let the world know we have a new fixture. This will cause new contacts - // to be created at the beginning of the next time step. - m_world->m_newContacts = true; - - return fixture; -} - -b2Fixture* b2Body::CreateFixture(const b2Shape* shape, float density) -{ - b2FixtureDef def; - def.shape = shape; - def.density = density; - - return CreateFixture(&def); -} - -void b2Body::DestroyFixture(b2Fixture* fixture) -{ - if (fixture == NULL) - { - return; - } - - b2Assert(m_world->IsLocked() == false); - if (m_world->IsLocked() == true) - { - return; - } - - b2Assert(fixture->m_body == this); - - // Remove the fixture from this body's singly linked list. - b2Assert(m_fixtureCount > 0); - b2Fixture** node = &m_fixtureList; - bool found = false; - while (*node != nullptr) - { - if (*node == fixture) - { - *node = fixture->m_next; - found = true; - break; - } - - node = &(*node)->m_next; - } - - // You tried to remove a shape that is not attached to this body. - b2Assert(found); - - const float density = fixture->m_density; - - // Destroy any contacts associated with the fixture. - b2ContactEdge* edge = m_contactList; - while (edge) - { - b2Contact* c = edge->contact; - edge = edge->next; - - b2Fixture* fixtureA = c->GetFixtureA(); - b2Fixture* fixtureB = c->GetFixtureB(); - - if (fixture == fixtureA || fixture == fixtureB) - { - // This destroys the contact and removes it from - // this body's contact list. - m_world->m_contactManager.Destroy(c); - } - } - - b2BlockAllocator* allocator = &m_world->m_blockAllocator; - - if (m_flags & e_enabledFlag) - { - b2BroadPhase* broadPhase = &m_world->m_contactManager.m_broadPhase; - fixture->DestroyProxies(broadPhase); - } - - fixture->m_body = nullptr; - fixture->m_next = nullptr; - fixture->Destroy(allocator); - fixture->~b2Fixture(); - allocator->Free(fixture, sizeof(b2Fixture)); - - --m_fixtureCount; - - // Reset the mass data - if (density > 0.0f) - { - ResetMassData(); - } -} - -void b2Body::ResetMassData() -{ - // Compute mass data from shapes. Each shape has its own density. - m_mass = 0.0f; - m_invMass = 0.0f; - m_I = 0.0f; - m_invI = 0.0f; - m_sweep.localCenter.SetZero(); - - // Static and kinematic bodies have zero mass. - if (m_type == b2_staticBody || m_type == b2_kinematicBody) - { - m_sweep.c0 = m_xf.p; - m_sweep.c = m_xf.p; - m_sweep.a0 = m_sweep.a; - return; - } - - b2Assert(m_type == b2_dynamicBody); - - // Accumulate mass over all fixtures. - b2Vec2 localCenter = b2Vec2_zero; - for (b2Fixture* f = m_fixtureList; f; f = f->m_next) - { - if (f->m_density == 0.0f) - { - continue; - } - - b2MassData massData; - f->GetMassData(&massData); - m_mass += massData.mass; - localCenter += massData.mass * massData.center; - m_I += massData.I; - } - - // Compute center of mass. - if (m_mass > 0.0f) - { - m_invMass = 1.0f / m_mass; - localCenter *= m_invMass; - } - - if (m_I > 0.0f && (m_flags & e_fixedRotationFlag) == 0) - { - // Center the inertia about the center of mass. - m_I -= m_mass * b2Dot(localCenter, localCenter); - b2Assert(m_I > 0.0f); - m_invI = 1.0f / m_I; - - } - else - { - m_I = 0.0f; - m_invI = 0.0f; - } - - // Move center of mass. - b2Vec2 oldCenter = m_sweep.c; - m_sweep.localCenter = localCenter; - m_sweep.c0 = m_sweep.c = b2Mul(m_xf, m_sweep.localCenter); - - // Update center of mass velocity. - m_linearVelocity += b2Cross(m_angularVelocity, m_sweep.c - oldCenter); -} - -void b2Body::SetMassData(const b2MassData* massData) -{ - b2Assert(m_world->IsLocked() == false); - if (m_world->IsLocked() == true) - { - return; - } - - if (m_type != b2_dynamicBody) - { - return; - } - - m_invMass = 0.0f; - m_I = 0.0f; - m_invI = 0.0f; - - m_mass = massData->mass; - if (m_mass <= 0.0f) - { - m_mass = 1.0f; - } - - m_invMass = 1.0f / m_mass; - - if (massData->I > 0.0f && (m_flags & b2Body::e_fixedRotationFlag) == 0) - { - m_I = massData->I - m_mass * b2Dot(massData->center, massData->center); - b2Assert(m_I > 0.0f); - m_invI = 1.0f / m_I; - } - - // Move center of mass. - b2Vec2 oldCenter = m_sweep.c; - m_sweep.localCenter = massData->center; - m_sweep.c0 = m_sweep.c = b2Mul(m_xf, m_sweep.localCenter); - - // Update center of mass velocity. - m_linearVelocity += b2Cross(m_angularVelocity, m_sweep.c - oldCenter); -} - -bool b2Body::ShouldCollide(const b2Body* other) const -{ - // At least one body should be dynamic. - if (m_type != b2_dynamicBody && other->m_type != b2_dynamicBody) - { - return false; - } - - // Does a joint prevent collision? - for (b2JointEdge* jn = m_jointList; jn; jn = jn->next) - { - if (jn->other == other) - { - if (jn->joint->m_collideConnected == false) - { - return false; - } - } - } - - return true; -} - -void b2Body::SetTransform(const b2Vec2& position, float angle) -{ - b2Assert(m_world->IsLocked() == false); - if (m_world->IsLocked() == true) - { - return; - } - - m_xf.q.Set(angle); - m_xf.p = position; - - m_sweep.c = b2Mul(m_xf, m_sweep.localCenter); - m_sweep.a = angle; - - m_sweep.c0 = m_sweep.c; - m_sweep.a0 = angle; - - b2BroadPhase* broadPhase = &m_world->m_contactManager.m_broadPhase; - for (b2Fixture* f = m_fixtureList; f; f = f->m_next) - { - f->Synchronize(broadPhase, m_xf, m_xf); - } - - // Check for new contacts the next step - m_world->m_newContacts = true; -} - -void b2Body::SynchronizeFixtures() -{ - b2BroadPhase* broadPhase = &m_world->m_contactManager.m_broadPhase; - - if (m_flags & b2Body::e_awakeFlag) - { - b2Transform xf1; - xf1.q.Set(m_sweep.a0); - xf1.p = m_sweep.c0 - b2Mul(xf1.q, m_sweep.localCenter); - - for (b2Fixture* f = m_fixtureList; f; f = f->m_next) - { - f->Synchronize(broadPhase, xf1, m_xf); - } - } - else - { - for (b2Fixture* f = m_fixtureList; f; f = f->m_next) - { - f->Synchronize(broadPhase, m_xf, m_xf); - } - } -} - -void b2Body::SetEnabled(bool flag) -{ - b2Assert(m_world->IsLocked() == false); - - if (flag == IsEnabled()) - { - return; - } - - if (flag) - { - m_flags |= e_enabledFlag; - - // Create all proxies. - b2BroadPhase* broadPhase = &m_world->m_contactManager.m_broadPhase; - for (b2Fixture* f = m_fixtureList; f; f = f->m_next) - { - f->CreateProxies(broadPhase, m_xf); - } - - // Contacts are created at the beginning of the next - m_world->m_newContacts = true; - } - else - { - m_flags &= ~e_enabledFlag; - - // Destroy all proxies. - b2BroadPhase* broadPhase = &m_world->m_contactManager.m_broadPhase; - for (b2Fixture* f = m_fixtureList; f; f = f->m_next) - { - f->DestroyProxies(broadPhase); - } - - // Destroy the attached contacts. - b2ContactEdge* ce = m_contactList; - while (ce) - { - b2ContactEdge* ce0 = ce; - ce = ce->next; - m_world->m_contactManager.Destroy(ce0->contact); - } - m_contactList = nullptr; - } -} - -void b2Body::SetFixedRotation(bool flag) -{ - bool status = (m_flags & e_fixedRotationFlag) == e_fixedRotationFlag; - if (status == flag) - { - return; - } - - if (flag) - { - m_flags |= e_fixedRotationFlag; - } - else - { - m_flags &= ~e_fixedRotationFlag; - } - - m_angularVelocity = 0.0f; - - ResetMassData(); -} - -void b2Body::Dump() -{ - int32 bodyIndex = m_islandIndex; - - // %.9g is sufficient to save and load the same value using text - // FLT_DECIMAL_DIG == 9 - - b2Dump("{\n"); - b2Dump(" b2BodyDef bd;\n"); - b2Dump(" bd.type = b2BodyType(%d);\n", m_type); - b2Dump(" bd.position.Set(%.9g, %.9g);\n", m_xf.p.x, m_xf.p.y); - b2Dump(" bd.angle = %.9g;\n", m_sweep.a); - b2Dump(" bd.linearVelocity.Set(%.9g, %.9g);\n", m_linearVelocity.x, m_linearVelocity.y); - b2Dump(" bd.angularVelocity = %.9g;\n", m_angularVelocity); - b2Dump(" bd.linearDamping = %.9g;\n", m_linearDamping); - b2Dump(" bd.angularDamping = %.9g;\n", m_angularDamping); - b2Dump(" bd.allowSleep = bool(%d);\n", m_flags & e_autoSleepFlag); - b2Dump(" bd.awake = bool(%d);\n", m_flags & e_awakeFlag); - b2Dump(" bd.fixedRotation = bool(%d);\n", m_flags & e_fixedRotationFlag); - b2Dump(" bd.bullet = bool(%d);\n", m_flags & e_bulletFlag); - b2Dump(" bd.enabled = bool(%d);\n", m_flags & e_enabledFlag); - b2Dump(" bd.gravityScale = %.9g;\n", m_gravityScale); - b2Dump(" bodies[%d] = m_world->CreateBody(&bd);\n", m_islandIndex); - b2Dump("\n"); - for (b2Fixture* f = m_fixtureList; f; f = f->m_next) - { - b2Dump(" {\n"); - f->Dump(bodyIndex); - b2Dump(" }\n"); - } - b2Dump("}\n"); -} diff --git a/3rdparty/box2d/src/dynamics/b2_chain_circle_contact.cpp b/3rdparty/box2d/src/dynamics/b2_chain_circle_contact.cpp deleted file mode 100644 index 8464fe836312..000000000000 --- a/3rdparty/box2d/src/dynamics/b2_chain_circle_contact.cpp +++ /dev/null @@ -1,57 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "b2_chain_circle_contact.h" -#include "box2d/b2_block_allocator.h" -#include "box2d/b2_fixture.h" -#include "box2d/b2_chain_shape.h" -#include "box2d/b2_edge_shape.h" - -#include - -b2Contact* b2ChainAndCircleContact::Create(b2Fixture* fixtureA, int32 indexA, b2Fixture* fixtureB, int32 indexB, b2BlockAllocator* allocator) -{ - void* mem = allocator->Allocate(sizeof(b2ChainAndCircleContact)); - return new (mem) b2ChainAndCircleContact(fixtureA, indexA, fixtureB, indexB); -} - -void b2ChainAndCircleContact::Destroy(b2Contact* contact, b2BlockAllocator* allocator) -{ - ((b2ChainAndCircleContact*)contact)->~b2ChainAndCircleContact(); - allocator->Free(contact, sizeof(b2ChainAndCircleContact)); -} - -b2ChainAndCircleContact::b2ChainAndCircleContact(b2Fixture* fixtureA, int32 indexA, b2Fixture* fixtureB, int32 indexB) -: b2Contact(fixtureA, indexA, fixtureB, indexB) -{ - b2Assert(m_fixtureA->GetType() == b2Shape::e_chain); - b2Assert(m_fixtureB->GetType() == b2Shape::e_circle); -} - -void b2ChainAndCircleContact::Evaluate(b2Manifold* manifold, const b2Transform& xfA, const b2Transform& xfB) -{ - b2ChainShape* chain = (b2ChainShape*)m_fixtureA->GetShape(); - b2EdgeShape edge; - chain->GetChildEdge(&edge, m_indexA); - b2CollideEdgeAndCircle( manifold, &edge, xfA, - (b2CircleShape*)m_fixtureB->GetShape(), xfB); -} diff --git a/3rdparty/box2d/src/dynamics/b2_chain_circle_contact.h b/3rdparty/box2d/src/dynamics/b2_chain_circle_contact.h deleted file mode 100644 index 33ced64d80be..000000000000 --- a/3rdparty/box2d/src/dynamics/b2_chain_circle_contact.h +++ /dev/null @@ -1,43 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_CHAIN_AND_CIRCLE_CONTACT_H -#define B2_CHAIN_AND_CIRCLE_CONTACT_H - -#include "box2d/b2_contact.h" - -class b2BlockAllocator; - -class b2ChainAndCircleContact : public b2Contact -{ -public: - static b2Contact* Create( b2Fixture* fixtureA, int32 indexA, - b2Fixture* fixtureB, int32 indexB, b2BlockAllocator* allocator); - static void Destroy(b2Contact* contact, b2BlockAllocator* allocator); - - b2ChainAndCircleContact(b2Fixture* fixtureA, int32 indexA, b2Fixture* fixtureB, int32 indexB); - ~b2ChainAndCircleContact() {} - - void Evaluate(b2Manifold* manifold, const b2Transform& xfA, const b2Transform& xfB) override; -}; - -#endif diff --git a/3rdparty/box2d/src/dynamics/b2_chain_polygon_contact.cpp b/3rdparty/box2d/src/dynamics/b2_chain_polygon_contact.cpp deleted file mode 100644 index b8257aab9687..000000000000 --- a/3rdparty/box2d/src/dynamics/b2_chain_polygon_contact.cpp +++ /dev/null @@ -1,57 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "b2_chain_polygon_contact.h" -#include "box2d/b2_block_allocator.h" -#include "box2d/b2_fixture.h" -#include "box2d/b2_chain_shape.h" -#include "box2d/b2_edge_shape.h" - -#include - -b2Contact* b2ChainAndPolygonContact::Create(b2Fixture* fixtureA, int32 indexA, b2Fixture* fixtureB, int32 indexB, b2BlockAllocator* allocator) -{ - void* mem = allocator->Allocate(sizeof(b2ChainAndPolygonContact)); - return new (mem) b2ChainAndPolygonContact(fixtureA, indexA, fixtureB, indexB); -} - -void b2ChainAndPolygonContact::Destroy(b2Contact* contact, b2BlockAllocator* allocator) -{ - ((b2ChainAndPolygonContact*)contact)->~b2ChainAndPolygonContact(); - allocator->Free(contact, sizeof(b2ChainAndPolygonContact)); -} - -b2ChainAndPolygonContact::b2ChainAndPolygonContact(b2Fixture* fixtureA, int32 indexA, b2Fixture* fixtureB, int32 indexB) -: b2Contact(fixtureA, indexA, fixtureB, indexB) -{ - b2Assert(m_fixtureA->GetType() == b2Shape::e_chain); - b2Assert(m_fixtureB->GetType() == b2Shape::e_polygon); -} - -void b2ChainAndPolygonContact::Evaluate(b2Manifold* manifold, const b2Transform& xfA, const b2Transform& xfB) -{ - b2ChainShape* chain = (b2ChainShape*)m_fixtureA->GetShape(); - b2EdgeShape edge; - chain->GetChildEdge(&edge, m_indexA); - b2CollideEdgeAndPolygon( manifold, &edge, xfA, - (b2PolygonShape*)m_fixtureB->GetShape(), xfB); -} diff --git a/3rdparty/box2d/src/dynamics/b2_chain_polygon_contact.h b/3rdparty/box2d/src/dynamics/b2_chain_polygon_contact.h deleted file mode 100644 index 058154fb1529..000000000000 --- a/3rdparty/box2d/src/dynamics/b2_chain_polygon_contact.h +++ /dev/null @@ -1,43 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_CHAIN_AND_POLYGON_CONTACT_H -#define B2_CHAIN_AND_POLYGON_CONTACT_H - -#include "box2d/b2_contact.h" - -class b2BlockAllocator; - -class b2ChainAndPolygonContact : public b2Contact -{ -public: - static b2Contact* Create( b2Fixture* fixtureA, int32 indexA, - b2Fixture* fixtureB, int32 indexB, b2BlockAllocator* allocator); - static void Destroy(b2Contact* contact, b2BlockAllocator* allocator); - - b2ChainAndPolygonContact(b2Fixture* fixtureA, int32 indexA, b2Fixture* fixtureB, int32 indexB); - ~b2ChainAndPolygonContact() {} - - void Evaluate(b2Manifold* manifold, const b2Transform& xfA, const b2Transform& xfB) override; -}; - -#endif diff --git a/3rdparty/box2d/src/dynamics/b2_circle_contact.cpp b/3rdparty/box2d/src/dynamics/b2_circle_contact.cpp deleted file mode 100644 index 738aa84ca9ca..000000000000 --- a/3rdparty/box2d/src/dynamics/b2_circle_contact.cpp +++ /dev/null @@ -1,56 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "b2_circle_contact.h" -#include "box2d/b2_block_allocator.h" -#include "box2d/b2_body.h" -#include "box2d/b2_fixture.h" -#include "box2d/b2_time_of_impact.h" -#include "box2d/b2_world_callbacks.h" - -#include - -b2Contact* b2CircleContact::Create(b2Fixture* fixtureA, int32, b2Fixture* fixtureB, int32, b2BlockAllocator* allocator) -{ - void* mem = allocator->Allocate(sizeof(b2CircleContact)); - return new (mem) b2CircleContact(fixtureA, fixtureB); -} - -void b2CircleContact::Destroy(b2Contact* contact, b2BlockAllocator* allocator) -{ - ((b2CircleContact*)contact)->~b2CircleContact(); - allocator->Free(contact, sizeof(b2CircleContact)); -} - -b2CircleContact::b2CircleContact(b2Fixture* fixtureA, b2Fixture* fixtureB) - : b2Contact(fixtureA, 0, fixtureB, 0) -{ - b2Assert(m_fixtureA->GetType() == b2Shape::e_circle); - b2Assert(m_fixtureB->GetType() == b2Shape::e_circle); -} - -void b2CircleContact::Evaluate(b2Manifold* manifold, const b2Transform& xfA, const b2Transform& xfB) -{ - b2CollideCircles(manifold, - (b2CircleShape*)m_fixtureA->GetShape(), xfA, - (b2CircleShape*)m_fixtureB->GetShape(), xfB); -} diff --git a/3rdparty/box2d/src/dynamics/b2_circle_contact.h b/3rdparty/box2d/src/dynamics/b2_circle_contact.h deleted file mode 100644 index 98e09ea5b93a..000000000000 --- a/3rdparty/box2d/src/dynamics/b2_circle_contact.h +++ /dev/null @@ -1,43 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_CIRCLE_CONTACT_H -#define B2_CIRCLE_CONTACT_H - -#include "box2d/b2_contact.h" - -class b2BlockAllocator; - -class b2CircleContact : public b2Contact -{ -public: - static b2Contact* Create( b2Fixture* fixtureA, int32 indexA, - b2Fixture* fixtureB, int32 indexB, b2BlockAllocator* allocator); - static void Destroy(b2Contact* contact, b2BlockAllocator* allocator); - - b2CircleContact(b2Fixture* fixtureA, b2Fixture* fixtureB); - ~b2CircleContact() {} - - void Evaluate(b2Manifold* manifold, const b2Transform& xfA, const b2Transform& xfB) override; -}; - -#endif diff --git a/3rdparty/box2d/src/dynamics/b2_contact.cpp b/3rdparty/box2d/src/dynamics/b2_contact.cpp deleted file mode 100644 index 1c65bc895f54..000000000000 --- a/3rdparty/box2d/src/dynamics/b2_contact.cpp +++ /dev/null @@ -1,252 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "b2_chain_circle_contact.h" -#include "b2_chain_polygon_contact.h" -#include "b2_circle_contact.h" -#include "b2_contact_solver.h" -#include "b2_edge_circle_contact.h" -#include "b2_edge_polygon_contact.h" -#include "b2_polygon_circle_contact.h" -#include "b2_polygon_contact.h" - -#include "box2d/b2_contact.h" -#include "box2d/b2_block_allocator.h" -#include "box2d/b2_body.h" -#include "box2d/b2_collision.h" -#include "box2d/b2_fixture.h" -#include "box2d/b2_shape.h" -#include "box2d/b2_time_of_impact.h" -#include "box2d/b2_world.h" - -b2ContactRegister b2Contact::s_registers[b2Shape::e_typeCount][b2Shape::e_typeCount]; -bool b2Contact::s_initialized = false; - -void b2Contact::InitializeRegisters() -{ - AddType(b2CircleContact::Create, b2CircleContact::Destroy, b2Shape::e_circle, b2Shape::e_circle); - AddType(b2PolygonAndCircleContact::Create, b2PolygonAndCircleContact::Destroy, b2Shape::e_polygon, b2Shape::e_circle); - AddType(b2PolygonContact::Create, b2PolygonContact::Destroy, b2Shape::e_polygon, b2Shape::e_polygon); - AddType(b2EdgeAndCircleContact::Create, b2EdgeAndCircleContact::Destroy, b2Shape::e_edge, b2Shape::e_circle); - AddType(b2EdgeAndPolygonContact::Create, b2EdgeAndPolygonContact::Destroy, b2Shape::e_edge, b2Shape::e_polygon); - AddType(b2ChainAndCircleContact::Create, b2ChainAndCircleContact::Destroy, b2Shape::e_chain, b2Shape::e_circle); - AddType(b2ChainAndPolygonContact::Create, b2ChainAndPolygonContact::Destroy, b2Shape::e_chain, b2Shape::e_polygon); -} - -void b2Contact::AddType(b2ContactCreateFcn* createFcn, b2ContactDestroyFcn* destoryFcn, - b2Shape::Type type1, b2Shape::Type type2) -{ - b2Assert(0 <= type1 && type1 < b2Shape::e_typeCount); - b2Assert(0 <= type2 && type2 < b2Shape::e_typeCount); - - s_registers[type1][type2].createFcn = createFcn; - s_registers[type1][type2].destroyFcn = destoryFcn; - s_registers[type1][type2].primary = true; - - if (type1 != type2) - { - s_registers[type2][type1].createFcn = createFcn; - s_registers[type2][type1].destroyFcn = destoryFcn; - s_registers[type2][type1].primary = false; - } -} - -b2Contact* b2Contact::Create(b2Fixture* fixtureA, int32 indexA, b2Fixture* fixtureB, int32 indexB, b2BlockAllocator* allocator) -{ - if (s_initialized == false) - { - InitializeRegisters(); - s_initialized = true; - } - - b2Shape::Type type1 = fixtureA->GetType(); - b2Shape::Type type2 = fixtureB->GetType(); - - b2Assert(0 <= type1 && type1 < b2Shape::e_typeCount); - b2Assert(0 <= type2 && type2 < b2Shape::e_typeCount); - - b2ContactCreateFcn* createFcn = s_registers[type1][type2].createFcn; - if (createFcn) - { - if (s_registers[type1][type2].primary) - { - return createFcn(fixtureA, indexA, fixtureB, indexB, allocator); - } - else - { - return createFcn(fixtureB, indexB, fixtureA, indexA, allocator); - } - } - else - { - return nullptr; - } -} - -void b2Contact::Destroy(b2Contact* contact, b2BlockAllocator* allocator) -{ - b2Assert(s_initialized == true); - - b2Fixture* fixtureA = contact->m_fixtureA; - b2Fixture* fixtureB = contact->m_fixtureB; - - if (contact->m_manifold.pointCount > 0 && - fixtureA->IsSensor() == false && - fixtureB->IsSensor() == false) - { - fixtureA->GetBody()->SetAwake(true); - fixtureB->GetBody()->SetAwake(true); - } - - b2Shape::Type typeA = fixtureA->GetType(); - b2Shape::Type typeB = fixtureB->GetType(); - - b2Assert(0 <= typeA && typeA < b2Shape::e_typeCount); - b2Assert(0 <= typeB && typeB < b2Shape::e_typeCount); - - b2ContactDestroyFcn* destroyFcn = s_registers[typeA][typeB].destroyFcn; - destroyFcn(contact, allocator); -} - -b2Contact::b2Contact(b2Fixture* fA, int32 indexA, b2Fixture* fB, int32 indexB) -{ - m_flags = e_enabledFlag; - - m_fixtureA = fA; - m_fixtureB = fB; - - m_indexA = indexA; - m_indexB = indexB; - - m_manifold.pointCount = 0; - - m_prev = nullptr; - m_next = nullptr; - - m_nodeA.contact = nullptr; - m_nodeA.prev = nullptr; - m_nodeA.next = nullptr; - m_nodeA.other = nullptr; - - m_nodeB.contact = nullptr; - m_nodeB.prev = nullptr; - m_nodeB.next = nullptr; - m_nodeB.other = nullptr; - - m_toiCount = 0; - - m_friction = b2MixFriction(m_fixtureA->m_friction, m_fixtureB->m_friction); - m_restitution = b2MixRestitution(m_fixtureA->m_restitution, m_fixtureB->m_restitution); - m_restitutionThreshold = b2MixRestitutionThreshold(m_fixtureA->m_restitutionThreshold, m_fixtureB->m_restitutionThreshold); - - m_tangentSpeed = 0.0f; -} - -// Update the contact manifold and touching status. -// Note: do not assume the fixture AABBs are overlapping or are valid. -void b2Contact::Update(b2ContactListener* listener) -{ - b2Manifold oldManifold = m_manifold; - - // Re-enable this contact. - m_flags |= e_enabledFlag; - - bool touching = false; - bool wasTouching = (m_flags & e_touchingFlag) == e_touchingFlag; - - bool sensorA = m_fixtureA->IsSensor(); - bool sensorB = m_fixtureB->IsSensor(); - bool sensor = sensorA || sensorB; - - b2Body* bodyA = m_fixtureA->GetBody(); - b2Body* bodyB = m_fixtureB->GetBody(); - const b2Transform& xfA = bodyA->GetTransform(); - const b2Transform& xfB = bodyB->GetTransform(); - - // Is this contact a sensor? - if (sensor) - { - const b2Shape* shapeA = m_fixtureA->GetShape(); - const b2Shape* shapeB = m_fixtureB->GetShape(); - touching = b2TestOverlap(shapeA, m_indexA, shapeB, m_indexB, xfA, xfB); - - // Sensors don't generate manifolds. - m_manifold.pointCount = 0; - } - else - { - Evaluate(&m_manifold, xfA, xfB); - touching = m_manifold.pointCount > 0; - - // Match old contact ids to new contact ids and copy the - // stored impulses to warm start the solver. - for (int32 i = 0; i < m_manifold.pointCount; ++i) - { - b2ManifoldPoint* mp2 = m_manifold.points + i; - mp2->normalImpulse = 0.0f; - mp2->tangentImpulse = 0.0f; - b2ContactID id2 = mp2->id; - - for (int32 j = 0; j < oldManifold.pointCount; ++j) - { - b2ManifoldPoint* mp1 = oldManifold.points + j; - - if (mp1->id.key == id2.key) - { - mp2->normalImpulse = mp1->normalImpulse; - mp2->tangentImpulse = mp1->tangentImpulse; - break; - } - } - } - - if (touching != wasTouching) - { - bodyA->SetAwake(true); - bodyB->SetAwake(true); - } - } - - if (touching) - { - m_flags |= e_touchingFlag; - } - else - { - m_flags &= ~e_touchingFlag; - } - - if (wasTouching == false && touching == true && listener) - { - listener->BeginContact(this); - } - - if (wasTouching == true && touching == false && listener) - { - listener->EndContact(this); - } - - if (sensor == false && touching && listener) - { - listener->PreSolve(this, &oldManifold); - } -} diff --git a/3rdparty/box2d/src/dynamics/b2_contact_manager.cpp b/3rdparty/box2d/src/dynamics/b2_contact_manager.cpp deleted file mode 100644 index 56666084cdd8..000000000000 --- a/3rdparty/box2d/src/dynamics/b2_contact_manager.cpp +++ /dev/null @@ -1,293 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "box2d/b2_body.h" -#include "box2d/b2_contact.h" -#include "box2d/b2_contact_manager.h" -#include "box2d/b2_fixture.h" -#include "box2d/b2_world_callbacks.h" - -b2ContactFilter b2_defaultFilter; -b2ContactListener b2_defaultListener; - -b2ContactManager::b2ContactManager() -{ - m_contactList = nullptr; - m_contactCount = 0; - m_contactFilter = &b2_defaultFilter; - m_contactListener = &b2_defaultListener; - m_allocator = nullptr; -} - -void b2ContactManager::Destroy(b2Contact* c) -{ - b2Fixture* fixtureA = c->GetFixtureA(); - b2Fixture* fixtureB = c->GetFixtureB(); - b2Body* bodyA = fixtureA->GetBody(); - b2Body* bodyB = fixtureB->GetBody(); - - if (m_contactListener && c->IsTouching()) - { - m_contactListener->EndContact(c); - } - - // Remove from the world. - if (c->m_prev) - { - c->m_prev->m_next = c->m_next; - } - - if (c->m_next) - { - c->m_next->m_prev = c->m_prev; - } - - if (c == m_contactList) - { - m_contactList = c->m_next; - } - - // Remove from body 1 - if (c->m_nodeA.prev) - { - c->m_nodeA.prev->next = c->m_nodeA.next; - } - - if (c->m_nodeA.next) - { - c->m_nodeA.next->prev = c->m_nodeA.prev; - } - - if (&c->m_nodeA == bodyA->m_contactList) - { - bodyA->m_contactList = c->m_nodeA.next; - } - - // Remove from body 2 - if (c->m_nodeB.prev) - { - c->m_nodeB.prev->next = c->m_nodeB.next; - } - - if (c->m_nodeB.next) - { - c->m_nodeB.next->prev = c->m_nodeB.prev; - } - - if (&c->m_nodeB == bodyB->m_contactList) - { - bodyB->m_contactList = c->m_nodeB.next; - } - - // Call the factory. - b2Contact::Destroy(c, m_allocator); - --m_contactCount; -} - -// This is the top level collision call for the time step. Here -// all the narrow phase collision is processed for the world -// contact list. -void b2ContactManager::Collide() -{ - // Update awake contacts. - b2Contact* c = m_contactList; - while (c) - { - b2Fixture* fixtureA = c->GetFixtureA(); - b2Fixture* fixtureB = c->GetFixtureB(); - int32 indexA = c->GetChildIndexA(); - int32 indexB = c->GetChildIndexB(); - b2Body* bodyA = fixtureA->GetBody(); - b2Body* bodyB = fixtureB->GetBody(); - - // Is this contact flagged for filtering? - if (c->m_flags & b2Contact::e_filterFlag) - { - // Should these bodies collide? - if (bodyB->ShouldCollide(bodyA) == false) - { - b2Contact* cNuke = c; - c = cNuke->GetNext(); - Destroy(cNuke); - continue; - } - - // Check user filtering. - if (m_contactFilter && m_contactFilter->ShouldCollide(fixtureA, fixtureB) == false) - { - b2Contact* cNuke = c; - c = cNuke->GetNext(); - Destroy(cNuke); - continue; - } - - // Clear the filtering flag. - c->m_flags &= ~b2Contact::e_filterFlag; - } - - bool activeA = bodyA->IsAwake() && bodyA->m_type != b2_staticBody; - bool activeB = bodyB->IsAwake() && bodyB->m_type != b2_staticBody; - - // At least one body must be awake and it must be dynamic or kinematic. - if (activeA == false && activeB == false) - { - c = c->GetNext(); - continue; - } - - int32 proxyIdA = fixtureA->m_proxies[indexA].proxyId; - int32 proxyIdB = fixtureB->m_proxies[indexB].proxyId; - bool overlap = m_broadPhase.TestOverlap(proxyIdA, proxyIdB); - - // Here we destroy contacts that cease to overlap in the broad-phase. - if (overlap == false) - { - b2Contact* cNuke = c; - c = cNuke->GetNext(); - Destroy(cNuke); - continue; - } - - // The contact persists. - c->Update(m_contactListener); - c = c->GetNext(); - } -} - -void b2ContactManager::FindNewContacts() -{ - m_broadPhase.UpdatePairs(this); -} - -void b2ContactManager::AddPair(void* proxyUserDataA, void* proxyUserDataB) -{ - b2FixtureProxy* proxyA = (b2FixtureProxy*)proxyUserDataA; - b2FixtureProxy* proxyB = (b2FixtureProxy*)proxyUserDataB; - - b2Fixture* fixtureA = proxyA->fixture; - b2Fixture* fixtureB = proxyB->fixture; - - int32 indexA = proxyA->childIndex; - int32 indexB = proxyB->childIndex; - - b2Body* bodyA = fixtureA->GetBody(); - b2Body* bodyB = fixtureB->GetBody(); - - // Are the fixtures on the same body? - if (bodyA == bodyB) - { - return; - } - - // TODO_ERIN use a hash table to remove a potential bottleneck when both - // bodies have a lot of contacts. - // Does a contact already exist? - b2ContactEdge* edge = bodyB->GetContactList(); - while (edge) - { - if (edge->other == bodyA) - { - b2Fixture* fA = edge->contact->GetFixtureA(); - b2Fixture* fB = edge->contact->GetFixtureB(); - int32 iA = edge->contact->GetChildIndexA(); - int32 iB = edge->contact->GetChildIndexB(); - - if (fA == fixtureA && fB == fixtureB && iA == indexA && iB == indexB) - { - // A contact already exists. - return; - } - - if (fA == fixtureB && fB == fixtureA && iA == indexB && iB == indexA) - { - // A contact already exists. - return; - } - } - - edge = edge->next; - } - - // Does a joint override collision? Is at least one body dynamic? - if (bodyB->ShouldCollide(bodyA) == false) - { - return; - } - - // Check user filtering. - if (m_contactFilter && m_contactFilter->ShouldCollide(fixtureA, fixtureB) == false) - { - return; - } - - // Call the factory. - b2Contact* c = b2Contact::Create(fixtureA, indexA, fixtureB, indexB, m_allocator); - if (c == nullptr) - { - return; - } - - // Contact creation may swap fixtures. - fixtureA = c->GetFixtureA(); - fixtureB = c->GetFixtureB(); - indexA = c->GetChildIndexA(); - indexB = c->GetChildIndexB(); - bodyA = fixtureA->GetBody(); - bodyB = fixtureB->GetBody(); - - // Insert into the world. - c->m_prev = nullptr; - c->m_next = m_contactList; - if (m_contactList != nullptr) - { - m_contactList->m_prev = c; - } - m_contactList = c; - - // Connect to island graph. - - // Connect to body A - c->m_nodeA.contact = c; - c->m_nodeA.other = bodyB; - - c->m_nodeA.prev = nullptr; - c->m_nodeA.next = bodyA->m_contactList; - if (bodyA->m_contactList != nullptr) - { - bodyA->m_contactList->prev = &c->m_nodeA; - } - bodyA->m_contactList = &c->m_nodeA; - - // Connect to body B - c->m_nodeB.contact = c; - c->m_nodeB.other = bodyA; - - c->m_nodeB.prev = nullptr; - c->m_nodeB.next = bodyB->m_contactList; - if (bodyB->m_contactList != nullptr) - { - bodyB->m_contactList->prev = &c->m_nodeB; - } - bodyB->m_contactList = &c->m_nodeB; - - ++m_contactCount; -} diff --git a/3rdparty/box2d/src/dynamics/b2_contact_solver.cpp b/3rdparty/box2d/src/dynamics/b2_contact_solver.cpp deleted file mode 100644 index d6c08fb17972..000000000000 --- a/3rdparty/box2d/src/dynamics/b2_contact_solver.cpp +++ /dev/null @@ -1,843 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "b2_contact_solver.h" - -#include "box2d/b2_body.h" -#include "box2d/b2_contact.h" -#include "box2d/b2_fixture.h" -#include "box2d/b2_stack_allocator.h" -#include "box2d/b2_world.h" - -// Solver debugging is normally disabled because the block solver sometimes has to deal with a poorly conditioned effective mass matrix. -#define B2_DEBUG_SOLVER 0 - -B2_API bool g_blockSolve = true; - -struct b2ContactPositionConstraint -{ - b2Vec2 localPoints[b2_maxManifoldPoints]; - b2Vec2 localNormal; - b2Vec2 localPoint; - int32 indexA; - int32 indexB; - float invMassA, invMassB; - b2Vec2 localCenterA, localCenterB; - float invIA, invIB; - b2Manifold::Type type; - float radiusA, radiusB; - int32 pointCount; -}; - -b2ContactSolver::b2ContactSolver(b2ContactSolverDef* def) -{ - m_step = def->step; - m_allocator = def->allocator; - m_count = def->count; - m_positionConstraints = (b2ContactPositionConstraint*)m_allocator->Allocate(m_count * sizeof(b2ContactPositionConstraint)); - m_velocityConstraints = (b2ContactVelocityConstraint*)m_allocator->Allocate(m_count * sizeof(b2ContactVelocityConstraint)); - m_positions = def->positions; - m_velocities = def->velocities; - m_contacts = def->contacts; - - // Initialize position independent portions of the constraints. - for (int32 i = 0; i < m_count; ++i) - { - b2Contact* contact = m_contacts[i]; - - b2Fixture* fixtureA = contact->m_fixtureA; - b2Fixture* fixtureB = contact->m_fixtureB; - b2Shape* shapeA = fixtureA->GetShape(); - b2Shape* shapeB = fixtureB->GetShape(); - float radiusA = shapeA->m_radius; - float radiusB = shapeB->m_radius; - b2Body* bodyA = fixtureA->GetBody(); - b2Body* bodyB = fixtureB->GetBody(); - b2Manifold* manifold = contact->GetManifold(); - - int32 pointCount = manifold->pointCount; - b2Assert(pointCount > 0); - - b2ContactVelocityConstraint* vc = m_velocityConstraints + i; - vc->friction = contact->m_friction; - vc->restitution = contact->m_restitution; - vc->threshold = contact->m_restitutionThreshold; - vc->tangentSpeed = contact->m_tangentSpeed; - vc->indexA = bodyA->m_islandIndex; - vc->indexB = bodyB->m_islandIndex; - vc->invMassA = bodyA->m_invMass; - vc->invMassB = bodyB->m_invMass; - vc->invIA = bodyA->m_invI; - vc->invIB = bodyB->m_invI; - vc->contactIndex = i; - vc->pointCount = pointCount; - vc->K.SetZero(); - vc->normalMass.SetZero(); - - b2ContactPositionConstraint* pc = m_positionConstraints + i; - pc->indexA = bodyA->m_islandIndex; - pc->indexB = bodyB->m_islandIndex; - pc->invMassA = bodyA->m_invMass; - pc->invMassB = bodyB->m_invMass; - pc->localCenterA = bodyA->m_sweep.localCenter; - pc->localCenterB = bodyB->m_sweep.localCenter; - pc->invIA = bodyA->m_invI; - pc->invIB = bodyB->m_invI; - pc->localNormal = manifold->localNormal; - pc->localPoint = manifold->localPoint; - pc->pointCount = pointCount; - pc->radiusA = radiusA; - pc->radiusB = radiusB; - pc->type = manifold->type; - - for (int32 j = 0; j < pointCount; ++j) - { - b2ManifoldPoint* cp = manifold->points + j; - b2VelocityConstraintPoint* vcp = vc->points + j; - - if (m_step.warmStarting) - { - vcp->normalImpulse = m_step.dtRatio * cp->normalImpulse; - vcp->tangentImpulse = m_step.dtRatio * cp->tangentImpulse; - } - else - { - vcp->normalImpulse = 0.0f; - vcp->tangentImpulse = 0.0f; - } - - vcp->rA.SetZero(); - vcp->rB.SetZero(); - vcp->normalMass = 0.0f; - vcp->tangentMass = 0.0f; - vcp->velocityBias = 0.0f; - - pc->localPoints[j] = cp->localPoint; - } - } -} - -b2ContactSolver::~b2ContactSolver() -{ - m_allocator->Free(m_velocityConstraints); - m_allocator->Free(m_positionConstraints); -} - -// Initialize position dependent portions of the velocity constraints. -void b2ContactSolver::InitializeVelocityConstraints() -{ - for (int32 i = 0; i < m_count; ++i) - { - b2ContactVelocityConstraint* vc = m_velocityConstraints + i; - b2ContactPositionConstraint* pc = m_positionConstraints + i; - - float radiusA = pc->radiusA; - float radiusB = pc->radiusB; - b2Manifold* manifold = m_contacts[vc->contactIndex]->GetManifold(); - - int32 indexA = vc->indexA; - int32 indexB = vc->indexB; - - float mA = vc->invMassA; - float mB = vc->invMassB; - float iA = vc->invIA; - float iB = vc->invIB; - b2Vec2 localCenterA = pc->localCenterA; - b2Vec2 localCenterB = pc->localCenterB; - - b2Vec2 cA = m_positions[indexA].c; - float aA = m_positions[indexA].a; - b2Vec2 vA = m_velocities[indexA].v; - float wA = m_velocities[indexA].w; - - b2Vec2 cB = m_positions[indexB].c; - float aB = m_positions[indexB].a; - b2Vec2 vB = m_velocities[indexB].v; - float wB = m_velocities[indexB].w; - - b2Assert(manifold->pointCount > 0); - - b2Transform xfA, xfB; - xfA.q.Set(aA); - xfB.q.Set(aB); - xfA.p = cA - b2Mul(xfA.q, localCenterA); - xfB.p = cB - b2Mul(xfB.q, localCenterB); - - b2WorldManifold worldManifold; - worldManifold.Initialize(manifold, xfA, radiusA, xfB, radiusB); - - vc->normal = worldManifold.normal; - - int32 pointCount = vc->pointCount; - for (int32 j = 0; j < pointCount; ++j) - { - b2VelocityConstraintPoint* vcp = vc->points + j; - - vcp->rA = worldManifold.points[j] - cA; - vcp->rB = worldManifold.points[j] - cB; - - float rnA = b2Cross(vcp->rA, vc->normal); - float rnB = b2Cross(vcp->rB, vc->normal); - - float kNormal = mA + mB + iA * rnA * rnA + iB * rnB * rnB; - - vcp->normalMass = kNormal > 0.0f ? 1.0f / kNormal : 0.0f; - - b2Vec2 tangent = b2Cross(vc->normal, 1.0f); - - float rtA = b2Cross(vcp->rA, tangent); - float rtB = b2Cross(vcp->rB, tangent); - - float kTangent = mA + mB + iA * rtA * rtA + iB * rtB * rtB; - - vcp->tangentMass = kTangent > 0.0f ? 1.0f / kTangent : 0.0f; - - // Setup a velocity bias for restitution. - vcp->velocityBias = 0.0f; - float vRel = b2Dot(vc->normal, vB + b2Cross(wB, vcp->rB) - vA - b2Cross(wA, vcp->rA)); - if (vRel < -vc->threshold) - { - vcp->velocityBias = -vc->restitution * vRel; - } - } - - // If we have two points, then prepare the block solver. - if (vc->pointCount == 2 && g_blockSolve) - { - b2VelocityConstraintPoint* vcp1 = vc->points + 0; - b2VelocityConstraintPoint* vcp2 = vc->points + 1; - - float rn1A = b2Cross(vcp1->rA, vc->normal); - float rn1B = b2Cross(vcp1->rB, vc->normal); - float rn2A = b2Cross(vcp2->rA, vc->normal); - float rn2B = b2Cross(vcp2->rB, vc->normal); - - float k11 = mA + mB + iA * rn1A * rn1A + iB * rn1B * rn1B; - float k22 = mA + mB + iA * rn2A * rn2A + iB * rn2B * rn2B; - float k12 = mA + mB + iA * rn1A * rn2A + iB * rn1B * rn2B; - - // Ensure a reasonable condition number. - const float k_maxConditionNumber = 1000.0f; - if (k11 * k11 < k_maxConditionNumber * (k11 * k22 - k12 * k12)) - { - // K is safe to invert. - vc->K.ex.Set(k11, k12); - vc->K.ey.Set(k12, k22); - vc->normalMass = vc->K.GetInverse(); - } - else - { - // The constraints are redundant, just use one. - // TODO_ERIN use deepest? - vc->pointCount = 1; - } - } - } -} - -void b2ContactSolver::WarmStart() -{ - // Warm start. - for (int32 i = 0; i < m_count; ++i) - { - b2ContactVelocityConstraint* vc = m_velocityConstraints + i; - - int32 indexA = vc->indexA; - int32 indexB = vc->indexB; - float mA = vc->invMassA; - float iA = vc->invIA; - float mB = vc->invMassB; - float iB = vc->invIB; - int32 pointCount = vc->pointCount; - - b2Vec2 vA = m_velocities[indexA].v; - float wA = m_velocities[indexA].w; - b2Vec2 vB = m_velocities[indexB].v; - float wB = m_velocities[indexB].w; - - b2Vec2 normal = vc->normal; - b2Vec2 tangent = b2Cross(normal, 1.0f); - - for (int32 j = 0; j < pointCount; ++j) - { - b2VelocityConstraintPoint* vcp = vc->points + j; - b2Vec2 P = vcp->normalImpulse * normal + vcp->tangentImpulse * tangent; - wA -= iA * b2Cross(vcp->rA, P); - vA -= mA * P; - wB += iB * b2Cross(vcp->rB, P); - vB += mB * P; - } - - m_velocities[indexA].v = vA; - m_velocities[indexA].w = wA; - m_velocities[indexB].v = vB; - m_velocities[indexB].w = wB; - } -} - -void b2ContactSolver::SolveVelocityConstraints() -{ - for (int32 i = 0; i < m_count; ++i) - { - b2ContactVelocityConstraint* vc = m_velocityConstraints + i; - - int32 indexA = vc->indexA; - int32 indexB = vc->indexB; - float mA = vc->invMassA; - float iA = vc->invIA; - float mB = vc->invMassB; - float iB = vc->invIB; - int32 pointCount = vc->pointCount; - - b2Vec2 vA = m_velocities[indexA].v; - float wA = m_velocities[indexA].w; - b2Vec2 vB = m_velocities[indexB].v; - float wB = m_velocities[indexB].w; - - b2Vec2 normal = vc->normal; - b2Vec2 tangent = b2Cross(normal, 1.0f); - float friction = vc->friction; - - b2Assert(pointCount == 1 || pointCount == 2); - - // Solve tangent constraints first because non-penetration is more important - // than friction. - for (int32 j = 0; j < pointCount; ++j) - { - b2VelocityConstraintPoint* vcp = vc->points + j; - - // Relative velocity at contact - b2Vec2 dv = vB + b2Cross(wB, vcp->rB) - vA - b2Cross(wA, vcp->rA); - - // Compute tangent force - float vt = b2Dot(dv, tangent) - vc->tangentSpeed; - float lambda = vcp->tangentMass * (-vt); - - // b2Clamp the accumulated force - float maxFriction = friction * vcp->normalImpulse; - float newImpulse = b2Clamp(vcp->tangentImpulse + lambda, -maxFriction, maxFriction); - lambda = newImpulse - vcp->tangentImpulse; - vcp->tangentImpulse = newImpulse; - - // Apply contact impulse - b2Vec2 P = lambda * tangent; - - vA -= mA * P; - wA -= iA * b2Cross(vcp->rA, P); - - vB += mB * P; - wB += iB * b2Cross(vcp->rB, P); - } - - // Solve normal constraints - if (pointCount == 1 || g_blockSolve == false) - { - for (int32 j = 0; j < pointCount; ++j) - { - b2VelocityConstraintPoint* vcp = vc->points + j; - - // Relative velocity at contact - b2Vec2 dv = vB + b2Cross(wB, vcp->rB) - vA - b2Cross(wA, vcp->rA); - - // Compute normal impulse - float vn = b2Dot(dv, normal); - float lambda = -vcp->normalMass * (vn - vcp->velocityBias); - - // b2Clamp the accumulated impulse - float newImpulse = b2Max(vcp->normalImpulse + lambda, 0.0f); - lambda = newImpulse - vcp->normalImpulse; - vcp->normalImpulse = newImpulse; - - // Apply contact impulse - b2Vec2 P = lambda * normal; - vA -= mA * P; - wA -= iA * b2Cross(vcp->rA, P); - - vB += mB * P; - wB += iB * b2Cross(vcp->rB, P); - } - } - else - { - // Block solver developed in collaboration with Dirk Gregorius (back in 01/07 on Box2D_Lite). - // Build the mini LCP for this contact patch - // - // vn = A * x + b, vn >= 0, x >= 0 and vn_i * x_i = 0 with i = 1..2 - // - // A = J * W * JT and J = ( -n, -r1 x n, n, r2 x n ) - // b = vn0 - velocityBias - // - // The system is solved using the "Total enumeration method" (s. Murty). The complementary constraint vn_i * x_i - // implies that we must have in any solution either vn_i = 0 or x_i = 0. So for the 2D contact problem the cases - // vn1 = 0 and vn2 = 0, x1 = 0 and x2 = 0, x1 = 0 and vn2 = 0, x2 = 0 and vn1 = 0 need to be tested. The first valid - // solution that satisfies the problem is chosen. - // - // In order to account of the accumulated impulse 'a' (because of the iterative nature of the solver which only requires - // that the accumulated impulse is clamped and not the incremental impulse) we change the impulse variable (x_i). - // - // Substitute: - // - // x = a + d - // - // a := old total impulse - // x := new total impulse - // d := incremental impulse - // - // For the current iteration we extend the formula for the incremental impulse - // to compute the new total impulse: - // - // vn = A * d + b - // = A * (x - a) + b - // = A * x + b - A * a - // = A * x + b' - // b' = b - A * a; - - b2VelocityConstraintPoint* cp1 = vc->points + 0; - b2VelocityConstraintPoint* cp2 = vc->points + 1; - - b2Vec2 a(cp1->normalImpulse, cp2->normalImpulse); - b2Assert(a.x >= 0.0f && a.y >= 0.0f); - - // Relative velocity at contact - b2Vec2 dv1 = vB + b2Cross(wB, cp1->rB) - vA - b2Cross(wA, cp1->rA); - b2Vec2 dv2 = vB + b2Cross(wB, cp2->rB) - vA - b2Cross(wA, cp2->rA); - - // Compute normal velocity - float vn1 = b2Dot(dv1, normal); - float vn2 = b2Dot(dv2, normal); - - b2Vec2 b; - b.x = vn1 - cp1->velocityBias; - b.y = vn2 - cp2->velocityBias; - - // Compute b' - b -= b2Mul(vc->K, a); - - const float k_errorTol = 1e-3f; - B2_NOT_USED(k_errorTol); - - for (;;) - { - // - // Case 1: vn = 0 - // - // 0 = A * x + b' - // - // Solve for x: - // - // x = - inv(A) * b' - // - b2Vec2 x = - b2Mul(vc->normalMass, b); - - if (x.x >= 0.0f && x.y >= 0.0f) - { - // Get the incremental impulse - b2Vec2 d = x - a; - - // Apply incremental impulse - b2Vec2 P1 = d.x * normal; - b2Vec2 P2 = d.y * normal; - vA -= mA * (P1 + P2); - wA -= iA * (b2Cross(cp1->rA, P1) + b2Cross(cp2->rA, P2)); - - vB += mB * (P1 + P2); - wB += iB * (b2Cross(cp1->rB, P1) + b2Cross(cp2->rB, P2)); - - // Accumulate - cp1->normalImpulse = x.x; - cp2->normalImpulse = x.y; - -#if B2_DEBUG_SOLVER == 1 - // Postconditions - dv1 = vB + b2Cross(wB, cp1->rB) - vA - b2Cross(wA, cp1->rA); - dv2 = vB + b2Cross(wB, cp2->rB) - vA - b2Cross(wA, cp2->rA); - - // Compute normal velocity - vn1 = b2Dot(dv1, normal); - vn2 = b2Dot(dv2, normal); - - b2Assert(b2Abs(vn1 - cp1->velocityBias) < k_errorTol); - b2Assert(b2Abs(vn2 - cp2->velocityBias) < k_errorTol); -#endif - break; - } - - // - // Case 2: vn1 = 0 and x2 = 0 - // - // 0 = a11 * x1 + a12 * 0 + b1' - // vn2 = a21 * x1 + a22 * 0 + b2' - // - x.x = - cp1->normalMass * b.x; - x.y = 0.0f; - vn1 = 0.0f; - vn2 = vc->K.ex.y * x.x + b.y; - if (x.x >= 0.0f && vn2 >= 0.0f) - { - // Get the incremental impulse - b2Vec2 d = x - a; - - // Apply incremental impulse - b2Vec2 P1 = d.x * normal; - b2Vec2 P2 = d.y * normal; - vA -= mA * (P1 + P2); - wA -= iA * (b2Cross(cp1->rA, P1) + b2Cross(cp2->rA, P2)); - - vB += mB * (P1 + P2); - wB += iB * (b2Cross(cp1->rB, P1) + b2Cross(cp2->rB, P2)); - - // Accumulate - cp1->normalImpulse = x.x; - cp2->normalImpulse = x.y; - -#if B2_DEBUG_SOLVER == 1 - // Postconditions - dv1 = vB + b2Cross(wB, cp1->rB) - vA - b2Cross(wA, cp1->rA); - - // Compute normal velocity - vn1 = b2Dot(dv1, normal); - - b2Assert(b2Abs(vn1 - cp1->velocityBias) < k_errorTol); -#endif - break; - } - - - // - // Case 3: vn2 = 0 and x1 = 0 - // - // vn1 = a11 * 0 + a12 * x2 + b1' - // 0 = a21 * 0 + a22 * x2 + b2' - // - x.x = 0.0f; - x.y = - cp2->normalMass * b.y; - vn1 = vc->K.ey.x * x.y + b.x; - vn2 = 0.0f; - - if (x.y >= 0.0f && vn1 >= 0.0f) - { - // Resubstitute for the incremental impulse - b2Vec2 d = x - a; - - // Apply incremental impulse - b2Vec2 P1 = d.x * normal; - b2Vec2 P2 = d.y * normal; - vA -= mA * (P1 + P2); - wA -= iA * (b2Cross(cp1->rA, P1) + b2Cross(cp2->rA, P2)); - - vB += mB * (P1 + P2); - wB += iB * (b2Cross(cp1->rB, P1) + b2Cross(cp2->rB, P2)); - - // Accumulate - cp1->normalImpulse = x.x; - cp2->normalImpulse = x.y; - -#if B2_DEBUG_SOLVER == 1 - // Postconditions - dv2 = vB + b2Cross(wB, cp2->rB) - vA - b2Cross(wA, cp2->rA); - - // Compute normal velocity - vn2 = b2Dot(dv2, normal); - - b2Assert(b2Abs(vn2 - cp2->velocityBias) < k_errorTol); -#endif - break; - } - - // - // Case 4: x1 = 0 and x2 = 0 - // - // vn1 = b1 - // vn2 = b2; - x.x = 0.0f; - x.y = 0.0f; - vn1 = b.x; - vn2 = b.y; - - if (vn1 >= 0.0f && vn2 >= 0.0f ) - { - // Resubstitute for the incremental impulse - b2Vec2 d = x - a; - - // Apply incremental impulse - b2Vec2 P1 = d.x * normal; - b2Vec2 P2 = d.y * normal; - vA -= mA * (P1 + P2); - wA -= iA * (b2Cross(cp1->rA, P1) + b2Cross(cp2->rA, P2)); - - vB += mB * (P1 + P2); - wB += iB * (b2Cross(cp1->rB, P1) + b2Cross(cp2->rB, P2)); - - // Accumulate - cp1->normalImpulse = x.x; - cp2->normalImpulse = x.y; - - break; - } - - // No solution, give up. This is hit sometimes, but it doesn't seem to matter. - break; - } - } - - m_velocities[indexA].v = vA; - m_velocities[indexA].w = wA; - m_velocities[indexB].v = vB; - m_velocities[indexB].w = wB; - } -} - -void b2ContactSolver::StoreImpulses() -{ - for (int32 i = 0; i < m_count; ++i) - { - b2ContactVelocityConstraint* vc = m_velocityConstraints + i; - b2Manifold* manifold = m_contacts[vc->contactIndex]->GetManifold(); - - for (int32 j = 0; j < vc->pointCount; ++j) - { - manifold->points[j].normalImpulse = vc->points[j].normalImpulse; - manifold->points[j].tangentImpulse = vc->points[j].tangentImpulse; - } - } -} - -struct b2PositionSolverManifold -{ - void Initialize(b2ContactPositionConstraint* pc, const b2Transform& xfA, const b2Transform& xfB, int32 index) - { - b2Assert(pc->pointCount > 0); - - switch (pc->type) - { - case b2Manifold::e_circles: - { - b2Vec2 pointA = b2Mul(xfA, pc->localPoint); - b2Vec2 pointB = b2Mul(xfB, pc->localPoints[0]); - normal = pointB - pointA; - normal.Normalize(); - point = 0.5f * (pointA + pointB); - separation = b2Dot(pointB - pointA, normal) - pc->radiusA - pc->radiusB; - } - break; - - case b2Manifold::e_faceA: - { - normal = b2Mul(xfA.q, pc->localNormal); - b2Vec2 planePoint = b2Mul(xfA, pc->localPoint); - - b2Vec2 clipPoint = b2Mul(xfB, pc->localPoints[index]); - separation = b2Dot(clipPoint - planePoint, normal) - pc->radiusA - pc->radiusB; - point = clipPoint; - } - break; - - case b2Manifold::e_faceB: - { - normal = b2Mul(xfB.q, pc->localNormal); - b2Vec2 planePoint = b2Mul(xfB, pc->localPoint); - - b2Vec2 clipPoint = b2Mul(xfA, pc->localPoints[index]); - separation = b2Dot(clipPoint - planePoint, normal) - pc->radiusA - pc->radiusB; - point = clipPoint; - - // Ensure normal points from A to B - normal = -normal; - } - break; - } - } - - b2Vec2 normal; - b2Vec2 point; - float separation; -}; - -// Sequential solver. -bool b2ContactSolver::SolvePositionConstraints() -{ - float minSeparation = 0.0f; - - for (int32 i = 0; i < m_count; ++i) - { - b2ContactPositionConstraint* pc = m_positionConstraints + i; - - int32 indexA = pc->indexA; - int32 indexB = pc->indexB; - b2Vec2 localCenterA = pc->localCenterA; - float mA = pc->invMassA; - float iA = pc->invIA; - b2Vec2 localCenterB = pc->localCenterB; - float mB = pc->invMassB; - float iB = pc->invIB; - int32 pointCount = pc->pointCount; - - b2Vec2 cA = m_positions[indexA].c; - float aA = m_positions[indexA].a; - - b2Vec2 cB = m_positions[indexB].c; - float aB = m_positions[indexB].a; - - // Solve normal constraints - for (int32 j = 0; j < pointCount; ++j) - { - b2Transform xfA, xfB; - xfA.q.Set(aA); - xfB.q.Set(aB); - xfA.p = cA - b2Mul(xfA.q, localCenterA); - xfB.p = cB - b2Mul(xfB.q, localCenterB); - - b2PositionSolverManifold psm; - psm.Initialize(pc, xfA, xfB, j); - b2Vec2 normal = psm.normal; - - b2Vec2 point = psm.point; - float separation = psm.separation; - - b2Vec2 rA = point - cA; - b2Vec2 rB = point - cB; - - // Track max constraint error. - minSeparation = b2Min(minSeparation, separation); - - // Prevent large corrections and allow slop. - float C = b2Clamp(b2_baumgarte * (separation + b2_linearSlop), -b2_maxLinearCorrection, 0.0f); - - // Compute the effective mass. - float rnA = b2Cross(rA, normal); - float rnB = b2Cross(rB, normal); - float K = mA + mB + iA * rnA * rnA + iB * rnB * rnB; - - // Compute normal impulse - float impulse = K > 0.0f ? - C / K : 0.0f; - - b2Vec2 P = impulse * normal; - - cA -= mA * P; - aA -= iA * b2Cross(rA, P); - - cB += mB * P; - aB += iB * b2Cross(rB, P); - } - - m_positions[indexA].c = cA; - m_positions[indexA].a = aA; - - m_positions[indexB].c = cB; - m_positions[indexB].a = aB; - } - - // We can't expect minSpeparation >= -b2_linearSlop because we don't - // push the separation above -b2_linearSlop. - return minSeparation >= -3.0f * b2_linearSlop; -} - -// Sequential position solver for position constraints. -bool b2ContactSolver::SolveTOIPositionConstraints(int32 toiIndexA, int32 toiIndexB) -{ - float minSeparation = 0.0f; - - for (int32 i = 0; i < m_count; ++i) - { - b2ContactPositionConstraint* pc = m_positionConstraints + i; - - int32 indexA = pc->indexA; - int32 indexB = pc->indexB; - b2Vec2 localCenterA = pc->localCenterA; - b2Vec2 localCenterB = pc->localCenterB; - int32 pointCount = pc->pointCount; - - float mA = 0.0f; - float iA = 0.0f; - if (indexA == toiIndexA || indexA == toiIndexB) - { - mA = pc->invMassA; - iA = pc->invIA; - } - - float mB = 0.0f; - float iB = 0.0f; - if (indexB == toiIndexA || indexB == toiIndexB) - { - mB = pc->invMassB; - iB = pc->invIB; - } - - b2Vec2 cA = m_positions[indexA].c; - float aA = m_positions[indexA].a; - - b2Vec2 cB = m_positions[indexB].c; - float aB = m_positions[indexB].a; - - // Solve normal constraints - for (int32 j = 0; j < pointCount; ++j) - { - b2Transform xfA, xfB; - xfA.q.Set(aA); - xfB.q.Set(aB); - xfA.p = cA - b2Mul(xfA.q, localCenterA); - xfB.p = cB - b2Mul(xfB.q, localCenterB); - - b2PositionSolverManifold psm; - psm.Initialize(pc, xfA, xfB, j); - b2Vec2 normal = psm.normal; - - b2Vec2 point = psm.point; - float separation = psm.separation; - - b2Vec2 rA = point - cA; - b2Vec2 rB = point - cB; - - // Track max constraint error. - minSeparation = b2Min(minSeparation, separation); - - // Prevent large corrections and allow slop. - float C = b2Clamp(b2_toiBaumgarte * (separation + b2_linearSlop), -b2_maxLinearCorrection, 0.0f); - - // Compute the effective mass. - float rnA = b2Cross(rA, normal); - float rnB = b2Cross(rB, normal); - float K = mA + mB + iA * rnA * rnA + iB * rnB * rnB; - - // Compute normal impulse - float impulse = K > 0.0f ? - C / K : 0.0f; - - b2Vec2 P = impulse * normal; - - cA -= mA * P; - aA -= iA * b2Cross(rA, P); - - cB += mB * P; - aB += iB * b2Cross(rB, P); - } - - m_positions[indexA].c = cA; - m_positions[indexA].a = aA; - - m_positions[indexB].c = cB; - m_positions[indexB].a = aB; - } - - // We can't expect minSpeparation >= -b2_linearSlop because we don't - // push the separation above -b2_linearSlop. - return minSeparation >= -1.5f * b2_linearSlop; -} diff --git a/3rdparty/box2d/src/dynamics/b2_contact_solver.h b/3rdparty/box2d/src/dynamics/b2_contact_solver.h deleted file mode 100644 index 1064738ef793..000000000000 --- a/3rdparty/box2d/src/dynamics/b2_contact_solver.h +++ /dev/null @@ -1,100 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_CONTACT_SOLVER_H -#define B2_CONTACT_SOLVER_H - -#include "box2d/b2_collision.h" -#include "box2d/b2_math.h" -#include "box2d/b2_time_step.h" - -class b2Contact; -class b2Body; -class b2StackAllocator; -struct b2ContactPositionConstraint; - -struct b2VelocityConstraintPoint -{ - b2Vec2 rA; - b2Vec2 rB; - float normalImpulse; - float tangentImpulse; - float normalMass; - float tangentMass; - float velocityBias; -}; - -struct b2ContactVelocityConstraint -{ - b2VelocityConstraintPoint points[b2_maxManifoldPoints]; - b2Vec2 normal; - b2Mat22 normalMass; - b2Mat22 K; - int32 indexA; - int32 indexB; - float invMassA, invMassB; - float invIA, invIB; - float friction; - float restitution; - float threshold; - float tangentSpeed; - int32 pointCount; - int32 contactIndex; -}; - -struct b2ContactSolverDef -{ - b2TimeStep step; - b2Contact** contacts; - int32 count; - b2Position* positions; - b2Velocity* velocities; - b2StackAllocator* allocator; -}; - -class b2ContactSolver -{ -public: - b2ContactSolver(b2ContactSolverDef* def); - ~b2ContactSolver(); - - void InitializeVelocityConstraints(); - - void WarmStart(); - void SolveVelocityConstraints(); - void StoreImpulses(); - - bool SolvePositionConstraints(); - bool SolveTOIPositionConstraints(int32 toiIndexA, int32 toiIndexB); - - b2TimeStep m_step; - b2Position* m_positions; - b2Velocity* m_velocities; - b2StackAllocator* m_allocator; - b2ContactPositionConstraint* m_positionConstraints; - b2ContactVelocityConstraint* m_velocityConstraints; - b2Contact** m_contacts; - int m_count; -}; - -#endif - diff --git a/3rdparty/box2d/src/dynamics/b2_distance_joint.cpp b/3rdparty/box2d/src/dynamics/b2_distance_joint.cpp deleted file mode 100644 index 221309ef00ab..000000000000 --- a/3rdparty/box2d/src/dynamics/b2_distance_joint.cpp +++ /dev/null @@ -1,421 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "box2d/b2_body.h" -#include "box2d/b2_draw.h" -#include "box2d/b2_distance_joint.h" -#include "box2d/b2_time_step.h" - -// 1-D constrained system -// m (v2 - v1) = lambda -// v2 + (beta/h) * x1 + gamma * lambda = 0, gamma has units of inverse mass. -// x2 = x1 + h * v2 - -// 1-D mass-damper-spring system -// m (v2 - v1) + h * d * v2 + h * k * - -// C = norm(p2 - p1) - L -// u = (p2 - p1) / norm(p2 - p1) -// Cdot = dot(u, v2 + cross(w2, r2) - v1 - cross(w1, r1)) -// J = [-u -cross(r1, u) u cross(r2, u)] -// K = J * invM * JT -// = invMass1 + invI1 * cross(r1, u)^2 + invMass2 + invI2 * cross(r2, u)^2 - - -void b2DistanceJointDef::Initialize(b2Body* b1, b2Body* b2, - const b2Vec2& anchor1, const b2Vec2& anchor2) -{ - bodyA = b1; - bodyB = b2; - localAnchorA = bodyA->GetLocalPoint(anchor1); - localAnchorB = bodyB->GetLocalPoint(anchor2); - b2Vec2 d = anchor2 - anchor1; - length = b2Max(d.Length(), b2_linearSlop); - minLength = length; - maxLength = length; -} - -b2DistanceJoint::b2DistanceJoint(const b2DistanceJointDef* def) -: b2Joint(def) -{ - m_localAnchorA = def->localAnchorA; - m_localAnchorB = def->localAnchorB; - m_length = b2Max(def->length, b2_linearSlop); - m_minLength = b2Max(def->minLength, b2_linearSlop); - m_maxLength = b2Max(def->maxLength, m_minLength); - m_stiffness = def->stiffness; - m_damping = def->damping; - - m_gamma = 0.0f; - m_bias = 0.0f; - m_impulse = 0.0f; - m_lowerImpulse = 0.0f; - m_upperImpulse = 0.0f; - m_currentLength = 0.0f; -} - -void b2DistanceJoint::InitVelocityConstraints(const b2SolverData& data) -{ - m_indexA = m_bodyA->m_islandIndex; - m_indexB = m_bodyB->m_islandIndex; - m_localCenterA = m_bodyA->m_sweep.localCenter; - m_localCenterB = m_bodyB->m_sweep.localCenter; - m_invMassA = m_bodyA->m_invMass; - m_invMassB = m_bodyB->m_invMass; - m_invIA = m_bodyA->m_invI; - m_invIB = m_bodyB->m_invI; - - b2Vec2 cA = data.positions[m_indexA].c; - float aA = data.positions[m_indexA].a; - b2Vec2 vA = data.velocities[m_indexA].v; - float wA = data.velocities[m_indexA].w; - - b2Vec2 cB = data.positions[m_indexB].c; - float aB = data.positions[m_indexB].a; - b2Vec2 vB = data.velocities[m_indexB].v; - float wB = data.velocities[m_indexB].w; - - b2Rot qA(aA), qB(aB); - - m_rA = b2Mul(qA, m_localAnchorA - m_localCenterA); - m_rB = b2Mul(qB, m_localAnchorB - m_localCenterB); - m_u = cB + m_rB - cA - m_rA; - - // Handle singularity. - m_currentLength = m_u.Length(); - if (m_currentLength > b2_linearSlop) - { - m_u *= 1.0f / m_currentLength; - } - else - { - m_u.Set(0.0f, 0.0f); - m_mass = 0.0f; - m_impulse = 0.0f; - m_lowerImpulse = 0.0f; - m_upperImpulse = 0.0f; - } - - float crAu = b2Cross(m_rA, m_u); - float crBu = b2Cross(m_rB, m_u); - float invMass = m_invMassA + m_invIA * crAu * crAu + m_invMassB + m_invIB * crBu * crBu; - m_mass = invMass != 0.0f ? 1.0f / invMass : 0.0f; - - if (m_stiffness > 0.0f && m_minLength < m_maxLength) - { - // soft - float C = m_currentLength - m_length; - - float d = m_damping; - float k = m_stiffness; - - // magic formulas - float h = data.step.dt; - - // gamma = 1 / (h * (d + h * k)) - // the extra factor of h in the denominator is since the lambda is an impulse, not a force - m_gamma = h * (d + h * k); - m_gamma = m_gamma != 0.0f ? 1.0f / m_gamma : 0.0f; - m_bias = C * h * k * m_gamma; - - invMass += m_gamma; - m_softMass = invMass != 0.0f ? 1.0f / invMass : 0.0f; - } - else - { - // rigid - m_gamma = 0.0f; - m_bias = 0.0f; - m_softMass = m_mass; - } - - if (data.step.warmStarting) - { - // Scale the impulse to support a variable time step. - m_impulse *= data.step.dtRatio; - m_lowerImpulse *= data.step.dtRatio; - m_upperImpulse *= data.step.dtRatio; - - b2Vec2 P = (m_impulse + m_lowerImpulse - m_upperImpulse) * m_u; - vA -= m_invMassA * P; - wA -= m_invIA * b2Cross(m_rA, P); - vB += m_invMassB * P; - wB += m_invIB * b2Cross(m_rB, P); - } - else - { - m_impulse = 0.0f; - } - - data.velocities[m_indexA].v = vA; - data.velocities[m_indexA].w = wA; - data.velocities[m_indexB].v = vB; - data.velocities[m_indexB].w = wB; -} - -void b2DistanceJoint::SolveVelocityConstraints(const b2SolverData& data) -{ - b2Vec2 vA = data.velocities[m_indexA].v; - float wA = data.velocities[m_indexA].w; - b2Vec2 vB = data.velocities[m_indexB].v; - float wB = data.velocities[m_indexB].w; - - if (m_minLength < m_maxLength) - { - if (m_stiffness > 0.0f) - { - // Cdot = dot(u, v + cross(w, r)) - b2Vec2 vpA = vA + b2Cross(wA, m_rA); - b2Vec2 vpB = vB + b2Cross(wB, m_rB); - float Cdot = b2Dot(m_u, vpB - vpA); - - float impulse = -m_softMass * (Cdot + m_bias + m_gamma * m_impulse); - m_impulse += impulse; - - b2Vec2 P = impulse * m_u; - vA -= m_invMassA * P; - wA -= m_invIA * b2Cross(m_rA, P); - vB += m_invMassB * P; - wB += m_invIB * b2Cross(m_rB, P); - } - - // lower - { - float C = m_currentLength - m_minLength; - float bias = b2Max(0.0f, C) * data.step.inv_dt; - - b2Vec2 vpA = vA + b2Cross(wA, m_rA); - b2Vec2 vpB = vB + b2Cross(wB, m_rB); - float Cdot = b2Dot(m_u, vpB - vpA); - - float impulse = -m_mass * (Cdot + bias); - float oldImpulse = m_lowerImpulse; - m_lowerImpulse = b2Max(0.0f, m_lowerImpulse + impulse); - impulse = m_lowerImpulse - oldImpulse; - b2Vec2 P = impulse * m_u; - - vA -= m_invMassA * P; - wA -= m_invIA * b2Cross(m_rA, P); - vB += m_invMassB * P; - wB += m_invIB * b2Cross(m_rB, P); - } - - // upper - { - float C = m_maxLength - m_currentLength; - float bias = b2Max(0.0f, C) * data.step.inv_dt; - - b2Vec2 vpA = vA + b2Cross(wA, m_rA); - b2Vec2 vpB = vB + b2Cross(wB, m_rB); - float Cdot = b2Dot(m_u, vpA - vpB); - - float impulse = -m_mass * (Cdot + bias); - float oldImpulse = m_upperImpulse; - m_upperImpulse = b2Max(0.0f, m_upperImpulse + impulse); - impulse = m_upperImpulse - oldImpulse; - b2Vec2 P = -impulse * m_u; - - vA -= m_invMassA * P; - wA -= m_invIA * b2Cross(m_rA, P); - vB += m_invMassB * P; - wB += m_invIB * b2Cross(m_rB, P); - } - } - else - { - // Equal limits - - // Cdot = dot(u, v + cross(w, r)) - b2Vec2 vpA = vA + b2Cross(wA, m_rA); - b2Vec2 vpB = vB + b2Cross(wB, m_rB); - float Cdot = b2Dot(m_u, vpB - vpA); - - float impulse = -m_mass * Cdot; - m_impulse += impulse; - - b2Vec2 P = impulse * m_u; - vA -= m_invMassA * P; - wA -= m_invIA * b2Cross(m_rA, P); - vB += m_invMassB * P; - wB += m_invIB * b2Cross(m_rB, P); - } - - data.velocities[m_indexA].v = vA; - data.velocities[m_indexA].w = wA; - data.velocities[m_indexB].v = vB; - data.velocities[m_indexB].w = wB; -} - -bool b2DistanceJoint::SolvePositionConstraints(const b2SolverData& data) -{ - b2Vec2 cA = data.positions[m_indexA].c; - float aA = data.positions[m_indexA].a; - b2Vec2 cB = data.positions[m_indexB].c; - float aB = data.positions[m_indexB].a; - - b2Rot qA(aA), qB(aB); - - b2Vec2 rA = b2Mul(qA, m_localAnchorA - m_localCenterA); - b2Vec2 rB = b2Mul(qB, m_localAnchorB - m_localCenterB); - b2Vec2 u = cB + rB - cA - rA; - - float length = u.Normalize(); - float C; - if (m_minLength == m_maxLength) - { - C = length - m_minLength; - } - else if (length < m_minLength) - { - C = length - m_minLength; - } - else if (m_maxLength < length) - { - C = length - m_maxLength; - } - else - { - return true; - } - - float impulse = -m_mass * C; - b2Vec2 P = impulse * u; - - cA -= m_invMassA * P; - aA -= m_invIA * b2Cross(rA, P); - cB += m_invMassB * P; - aB += m_invIB * b2Cross(rB, P); - - data.positions[m_indexA].c = cA; - data.positions[m_indexA].a = aA; - data.positions[m_indexB].c = cB; - data.positions[m_indexB].a = aB; - - return b2Abs(C) < b2_linearSlop; -} - -b2Vec2 b2DistanceJoint::GetAnchorA() const -{ - return m_bodyA->GetWorldPoint(m_localAnchorA); -} - -b2Vec2 b2DistanceJoint::GetAnchorB() const -{ - return m_bodyB->GetWorldPoint(m_localAnchorB); -} - -b2Vec2 b2DistanceJoint::GetReactionForce(float inv_dt) const -{ - b2Vec2 F = inv_dt * (m_impulse + m_lowerImpulse - m_upperImpulse) * m_u; - return F; -} - -float b2DistanceJoint::GetReactionTorque(float inv_dt) const -{ - B2_NOT_USED(inv_dt); - return 0.0f; -} - -float b2DistanceJoint::SetLength(float length) -{ - m_impulse = 0.0f; - m_length = b2Max(b2_linearSlop, length); - return m_length; -} - -float b2DistanceJoint::SetMinLength(float minLength) -{ - m_lowerImpulse = 0.0f; - m_minLength = b2Clamp(minLength, b2_linearSlop, m_maxLength); - return m_minLength; -} - -float b2DistanceJoint::SetMaxLength(float maxLength) -{ - m_upperImpulse = 0.0f; - m_maxLength = b2Max(maxLength, m_minLength); - return m_maxLength; -} - -float b2DistanceJoint::GetCurrentLength() const -{ - b2Vec2 pA = m_bodyA->GetWorldPoint(m_localAnchorA); - b2Vec2 pB = m_bodyB->GetWorldPoint(m_localAnchorB); - b2Vec2 d = pB - pA; - float length = d.Length(); - return length; -} - -void b2DistanceJoint::Dump() -{ - int32 indexA = m_bodyA->m_islandIndex; - int32 indexB = m_bodyB->m_islandIndex; - - b2Dump(" b2DistanceJointDef jd;\n"); - b2Dump(" jd.bodyA = bodies[%d];\n", indexA); - b2Dump(" jd.bodyB = bodies[%d];\n", indexB); - b2Dump(" jd.collideConnected = bool(%d);\n", m_collideConnected); - b2Dump(" jd.localAnchorA.Set(%.9g, %.9g);\n", m_localAnchorA.x, m_localAnchorA.y); - b2Dump(" jd.localAnchorB.Set(%.9g, %.9g);\n", m_localAnchorB.x, m_localAnchorB.y); - b2Dump(" jd.length = %.9g;\n", m_length); - b2Dump(" jd.minLength = %.9g;\n", m_minLength); - b2Dump(" jd.maxLength = %.9g;\n", m_maxLength); - b2Dump(" jd.stiffness = %.9g;\n", m_stiffness); - b2Dump(" jd.damping = %.9g;\n", m_damping); - b2Dump(" joints[%d] = m_world->CreateJoint(&jd);\n", m_index); -} - -void b2DistanceJoint::Draw(b2Draw* draw) const -{ - const b2Transform& xfA = m_bodyA->GetTransform(); - const b2Transform& xfB = m_bodyB->GetTransform(); - b2Vec2 pA = b2Mul(xfA, m_localAnchorA); - b2Vec2 pB = b2Mul(xfB, m_localAnchorB); - - b2Vec2 axis = pB - pA; - axis.Normalize(); - - b2Color c1(0.7f, 0.7f, 0.7f); - b2Color c2(0.3f, 0.9f, 0.3f); - b2Color c3(0.9f, 0.3f, 0.3f); - b2Color c4(0.4f, 0.4f, 0.4f); - - draw->DrawSegment(pA, pB, c4); - - b2Vec2 pRest = pA + m_length * axis; - draw->DrawPoint(pRest, 8.0f, c1); - - if (m_minLength != m_maxLength) - { - if (m_minLength > b2_linearSlop) - { - b2Vec2 pMin = pA + m_minLength * axis; - draw->DrawPoint(pMin, 4.0f, c2); - } - - if (m_maxLength < FLT_MAX) - { - b2Vec2 pMax = pA + m_maxLength * axis; - draw->DrawPoint(pMax, 4.0f, c3); - } - } -} diff --git a/3rdparty/box2d/src/dynamics/b2_edge_circle_contact.cpp b/3rdparty/box2d/src/dynamics/b2_edge_circle_contact.cpp deleted file mode 100644 index 8a126de6c630..000000000000 --- a/3rdparty/box2d/src/dynamics/b2_edge_circle_contact.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "b2_edge_circle_contact.h" - -#include "box2d/b2_block_allocator.h" -#include "box2d/b2_fixture.h" - -#include - -b2Contact* b2EdgeAndCircleContact::Create(b2Fixture* fixtureA, int32, b2Fixture* fixtureB, int32, b2BlockAllocator* allocator) -{ - void* mem = allocator->Allocate(sizeof(b2EdgeAndCircleContact)); - return new (mem) b2EdgeAndCircleContact(fixtureA, fixtureB); -} - -void b2EdgeAndCircleContact::Destroy(b2Contact* contact, b2BlockAllocator* allocator) -{ - ((b2EdgeAndCircleContact*)contact)->~b2EdgeAndCircleContact(); - allocator->Free(contact, sizeof(b2EdgeAndCircleContact)); -} - -b2EdgeAndCircleContact::b2EdgeAndCircleContact(b2Fixture* fixtureA, b2Fixture* fixtureB) -: b2Contact(fixtureA, 0, fixtureB, 0) -{ - b2Assert(m_fixtureA->GetType() == b2Shape::e_edge); - b2Assert(m_fixtureB->GetType() == b2Shape::e_circle); -} - -void b2EdgeAndCircleContact::Evaluate(b2Manifold* manifold, const b2Transform& xfA, const b2Transform& xfB) -{ - b2CollideEdgeAndCircle( manifold, - (b2EdgeShape*)m_fixtureA->GetShape(), xfA, - (b2CircleShape*)m_fixtureB->GetShape(), xfB); -} diff --git a/3rdparty/box2d/src/dynamics/b2_edge_circle_contact.h b/3rdparty/box2d/src/dynamics/b2_edge_circle_contact.h deleted file mode 100644 index 3efc88e60712..000000000000 --- a/3rdparty/box2d/src/dynamics/b2_edge_circle_contact.h +++ /dev/null @@ -1,43 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_EDGE_AND_CIRCLE_CONTACT_H -#define B2_EDGE_AND_CIRCLE_CONTACT_H - -#include "box2d/b2_contact.h" - -class b2BlockAllocator; - -class b2EdgeAndCircleContact : public b2Contact -{ -public: - static b2Contact* Create( b2Fixture* fixtureA, int32 indexA, - b2Fixture* fixtureB, int32 indexB, b2BlockAllocator* allocator); - static void Destroy(b2Contact* contact, b2BlockAllocator* allocator); - - b2EdgeAndCircleContact(b2Fixture* fixtureA, b2Fixture* fixtureB); - ~b2EdgeAndCircleContact() {} - - void Evaluate(b2Manifold* manifold, const b2Transform& xfA, const b2Transform& xfB) override; -}; - -#endif diff --git a/3rdparty/box2d/src/dynamics/b2_edge_polygon_contact.cpp b/3rdparty/box2d/src/dynamics/b2_edge_polygon_contact.cpp deleted file mode 100644 index e617e3516bbc..000000000000 --- a/3rdparty/box2d/src/dynamics/b2_edge_polygon_contact.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "b2_edge_polygon_contact.h" - -#include "box2d/b2_block_allocator.h" -#include "box2d/b2_fixture.h" - -#include - -b2Contact* b2EdgeAndPolygonContact::Create(b2Fixture* fixtureA, int32, b2Fixture* fixtureB, int32, b2BlockAllocator* allocator) -{ - void* mem = allocator->Allocate(sizeof(b2EdgeAndPolygonContact)); - return new (mem) b2EdgeAndPolygonContact(fixtureA, fixtureB); -} - -void b2EdgeAndPolygonContact::Destroy(b2Contact* contact, b2BlockAllocator* allocator) -{ - ((b2EdgeAndPolygonContact*)contact)->~b2EdgeAndPolygonContact(); - allocator->Free(contact, sizeof(b2EdgeAndPolygonContact)); -} - -b2EdgeAndPolygonContact::b2EdgeAndPolygonContact(b2Fixture* fixtureA, b2Fixture* fixtureB) -: b2Contact(fixtureA, 0, fixtureB, 0) -{ - b2Assert(m_fixtureA->GetType() == b2Shape::e_edge); - b2Assert(m_fixtureB->GetType() == b2Shape::e_polygon); -} - -void b2EdgeAndPolygonContact::Evaluate(b2Manifold* manifold, const b2Transform& xfA, const b2Transform& xfB) -{ - b2CollideEdgeAndPolygon( manifold, - (b2EdgeShape*)m_fixtureA->GetShape(), xfA, - (b2PolygonShape*)m_fixtureB->GetShape(), xfB); -} diff --git a/3rdparty/box2d/src/dynamics/b2_edge_polygon_contact.h b/3rdparty/box2d/src/dynamics/b2_edge_polygon_contact.h deleted file mode 100644 index e6616b534ebb..000000000000 --- a/3rdparty/box2d/src/dynamics/b2_edge_polygon_contact.h +++ /dev/null @@ -1,43 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_EDGE_AND_POLYGON_CONTACT_H -#define B2_EDGE_AND_POLYGON_CONTACT_H - -#include "box2d/b2_contact.h" - -class b2BlockAllocator; - -class b2EdgeAndPolygonContact : public b2Contact -{ -public: - static b2Contact* Create( b2Fixture* fixtureA, int32 indexA, - b2Fixture* fixtureB, int32 indexB, b2BlockAllocator* allocator); - static void Destroy(b2Contact* contact, b2BlockAllocator* allocator); - - b2EdgeAndPolygonContact(b2Fixture* fixtureA, b2Fixture* fixtureB); - ~b2EdgeAndPolygonContact() {} - - void Evaluate(b2Manifold* manifold, const b2Transform& xfA, const b2Transform& xfB) override; -}; - -#endif diff --git a/3rdparty/box2d/src/dynamics/b2_fixture.cpp b/3rdparty/box2d/src/dynamics/b2_fixture.cpp deleted file mode 100644 index 9fd700aa9a2a..000000000000 --- a/3rdparty/box2d/src/dynamics/b2_fixture.cpp +++ /dev/null @@ -1,305 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "box2d/b2_fixture.h" -#include "box2d/b2_block_allocator.h" -#include "box2d/b2_broad_phase.h" -#include "box2d/b2_chain_shape.h" -#include "box2d/b2_circle_shape.h" -#include "box2d/b2_collision.h" -#include "box2d/b2_contact.h" -#include "box2d/b2_edge_shape.h" -#include "box2d/b2_polygon_shape.h" -#include "box2d/b2_world.h" - -b2Fixture::b2Fixture() -{ - m_body = nullptr; - m_next = nullptr; - m_proxies = nullptr; - m_proxyCount = 0; - m_shape = nullptr; - m_density = 0.0f; -} - -void b2Fixture::Create(b2BlockAllocator* allocator, b2Body* body, const b2FixtureDef* def) -{ - m_userData = def->userData; - m_friction = def->friction; - m_restitution = def->restitution; - m_restitutionThreshold = def->restitutionThreshold; - - m_body = body; - m_next = nullptr; - - m_filter = def->filter; - - m_isSensor = def->isSensor; - - m_shape = def->shape->Clone(allocator); - - // Reserve proxy space - int32 childCount = m_shape->GetChildCount(); - m_proxies = (b2FixtureProxy*)allocator->Allocate(childCount * sizeof(b2FixtureProxy)); - for (int32 i = 0; i < childCount; ++i) - { - m_proxies[i].fixture = nullptr; - m_proxies[i].proxyId = b2BroadPhase::e_nullProxy; - } - m_proxyCount = 0; - - m_density = def->density; -} - -void b2Fixture::Destroy(b2BlockAllocator* allocator) -{ - // The proxies must be destroyed before calling this. - b2Assert(m_proxyCount == 0); - - // Free the proxy array. - int32 childCount = m_shape->GetChildCount(); - allocator->Free(m_proxies, childCount * sizeof(b2FixtureProxy)); - m_proxies = nullptr; - - // Free the child shape. - switch (m_shape->m_type) - { - case b2Shape::e_circle: - { - b2CircleShape* s = (b2CircleShape*)m_shape; - s->~b2CircleShape(); - allocator->Free(s, sizeof(b2CircleShape)); - } - break; - - case b2Shape::e_edge: - { - b2EdgeShape* s = (b2EdgeShape*)m_shape; - s->~b2EdgeShape(); - allocator->Free(s, sizeof(b2EdgeShape)); - } - break; - - case b2Shape::e_polygon: - { - b2PolygonShape* s = (b2PolygonShape*)m_shape; - s->~b2PolygonShape(); - allocator->Free(s, sizeof(b2PolygonShape)); - } - break; - - case b2Shape::e_chain: - { - b2ChainShape* s = (b2ChainShape*)m_shape; - s->~b2ChainShape(); - allocator->Free(s, sizeof(b2ChainShape)); - } - break; - - default: - b2Assert(false); - break; - } - - m_shape = nullptr; -} - -void b2Fixture::CreateProxies(b2BroadPhase* broadPhase, const b2Transform& xf) -{ - b2Assert(m_proxyCount == 0); - - // Create proxies in the broad-phase. - m_proxyCount = m_shape->GetChildCount(); - - for (int32 i = 0; i < m_proxyCount; ++i) - { - b2FixtureProxy* proxy = m_proxies + i; - m_shape->ComputeAABB(&proxy->aabb, xf, i); - proxy->proxyId = broadPhase->CreateProxy(proxy->aabb, proxy); - proxy->fixture = this; - proxy->childIndex = i; - } -} - -void b2Fixture::DestroyProxies(b2BroadPhase* broadPhase) -{ - // Destroy proxies in the broad-phase. - for (int32 i = 0; i < m_proxyCount; ++i) - { - b2FixtureProxy* proxy = m_proxies + i; - broadPhase->DestroyProxy(proxy->proxyId); - proxy->proxyId = b2BroadPhase::e_nullProxy; - } - - m_proxyCount = 0; -} - -void b2Fixture::Synchronize(b2BroadPhase* broadPhase, const b2Transform& transform1, const b2Transform& transform2) -{ - if (m_proxyCount == 0) - { - return; - } - - for (int32 i = 0; i < m_proxyCount; ++i) - { - b2FixtureProxy* proxy = m_proxies + i; - - // Compute an AABB that covers the swept shape (may miss some rotation effect). - b2AABB aabb1, aabb2; - m_shape->ComputeAABB(&aabb1, transform1, proxy->childIndex); - m_shape->ComputeAABB(&aabb2, transform2, proxy->childIndex); - - proxy->aabb.Combine(aabb1, aabb2); - - b2Vec2 displacement = aabb2.GetCenter() - aabb1.GetCenter(); - - broadPhase->MoveProxy(proxy->proxyId, proxy->aabb, displacement); - } -} - -void b2Fixture::SetFilterData(const b2Filter& filter) -{ - m_filter = filter; - - Refilter(); -} - -void b2Fixture::Refilter() -{ - if (m_body == nullptr) - { - return; - } - - // Flag associated contacts for filtering. - b2ContactEdge* edge = m_body->GetContactList(); - while (edge) - { - b2Contact* contact = edge->contact; - b2Fixture* fixtureA = contact->GetFixtureA(); - b2Fixture* fixtureB = contact->GetFixtureB(); - if (fixtureA == this || fixtureB == this) - { - contact->FlagForFiltering(); - } - - edge = edge->next; - } - - b2World* world = m_body->GetWorld(); - - if (world == nullptr) - { - return; - } - - // Touch each proxy so that new pairs may be created - b2BroadPhase* broadPhase = &world->m_contactManager.m_broadPhase; - for (int32 i = 0; i < m_proxyCount; ++i) - { - broadPhase->TouchProxy(m_proxies[i].proxyId); - } -} - -void b2Fixture::SetSensor(bool sensor) -{ - if (sensor != m_isSensor) - { - m_body->SetAwake(true); - m_isSensor = sensor; - } -} - -void b2Fixture::Dump(int32 bodyIndex) -{ - b2Dump(" b2FixtureDef fd;\n"); - b2Dump(" fd.friction = %.9g;\n", m_friction); - b2Dump(" fd.restitution = %.9g;\n", m_restitution); - b2Dump(" fd.restitutionThreshold = %.9g;\n", m_restitutionThreshold); - b2Dump(" fd.density = %.9g;\n", m_density); - b2Dump(" fd.isSensor = bool(%d);\n", m_isSensor); - b2Dump(" fd.filter.categoryBits = uint16(%d);\n", m_filter.categoryBits); - b2Dump(" fd.filter.maskBits = uint16(%d);\n", m_filter.maskBits); - b2Dump(" fd.filter.groupIndex = int16(%d);\n", m_filter.groupIndex); - - switch (m_shape->m_type) - { - case b2Shape::e_circle: - { - b2CircleShape* s = (b2CircleShape*)m_shape; - b2Dump(" b2CircleShape shape;\n"); - b2Dump(" shape.m_radius = %.9g;\n", s->m_radius); - b2Dump(" shape.m_p.Set(%.9g, %.9g);\n", s->m_p.x, s->m_p.y); - } - break; - - case b2Shape::e_edge: - { - b2EdgeShape* s = (b2EdgeShape*)m_shape; - b2Dump(" b2EdgeShape shape;\n"); - b2Dump(" shape.m_radius = %.9g;\n", s->m_radius); - b2Dump(" shape.m_vertex0.Set(%.9g, %.9g);\n", s->m_vertex0.x, s->m_vertex0.y); - b2Dump(" shape.m_vertex1.Set(%.9g, %.9g);\n", s->m_vertex1.x, s->m_vertex1.y); - b2Dump(" shape.m_vertex2.Set(%.9g, %.9g);\n", s->m_vertex2.x, s->m_vertex2.y); - b2Dump(" shape.m_vertex3.Set(%.9g, %.9g);\n", s->m_vertex3.x, s->m_vertex3.y); - b2Dump(" shape.m_oneSided = bool(%d);\n", s->m_oneSided); - } - break; - - case b2Shape::e_polygon: - { - b2PolygonShape* s = (b2PolygonShape*)m_shape; - b2Dump(" b2PolygonShape shape;\n"); - b2Dump(" b2Vec2 vs[%d];\n", b2_maxPolygonVertices); - for (int32 i = 0; i < s->m_count; ++i) - { - b2Dump(" vs[%d].Set(%.9g, %.9g);\n", i, s->m_vertices[i].x, s->m_vertices[i].y); - } - b2Dump(" shape.Set(vs, %d);\n", s->m_count); - } - break; - - case b2Shape::e_chain: - { - b2ChainShape* s = (b2ChainShape*)m_shape; - b2Dump(" b2ChainShape shape;\n"); - b2Dump(" b2Vec2 vs[%d];\n", s->m_count); - for (int32 i = 0; i < s->m_count; ++i) - { - b2Dump(" vs[%d].Set(%.9g, %.9g);\n", i, s->m_vertices[i].x, s->m_vertices[i].y); - } - b2Dump(" shape.CreateChain(vs, %d);\n", s->m_count); - b2Dump(" shape.m_prevVertex.Set(%.9g, %.9g);\n", s->m_prevVertex.x, s->m_prevVertex.y); - b2Dump(" shape.m_nextVertex.Set(%.9g, %.9g);\n", s->m_nextVertex.x, s->m_nextVertex.y); - } - break; - - default: - return; - } - - b2Dump("\n"); - b2Dump(" fd.shape = &shape;\n"); - b2Dump("\n"); - b2Dump(" bodies[%d]->CreateFixture(&fd);\n", bodyIndex); -} diff --git a/3rdparty/box2d/src/dynamics/b2_friction_joint.cpp b/3rdparty/box2d/src/dynamics/b2_friction_joint.cpp deleted file mode 100644 index d9d893ad3e09..000000000000 --- a/3rdparty/box2d/src/dynamics/b2_friction_joint.cpp +++ /dev/null @@ -1,255 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "box2d/b2_friction_joint.h" -#include "box2d/b2_body.h" -#include "box2d/b2_time_step.h" - -// Point-to-point constraint -// Cdot = v2 - v1 -// = v2 + cross(w2, r2) - v1 - cross(w1, r1) -// J = [-I -r1_skew I r2_skew ] -// Identity used: -// w k % (rx i + ry j) = w * (-ry i + rx j) - -// Angle constraint -// Cdot = w2 - w1 -// J = [0 0 -1 0 0 1] -// K = invI1 + invI2 - -void b2FrictionJointDef::Initialize(b2Body* bA, b2Body* bB, const b2Vec2& anchor) -{ - bodyA = bA; - bodyB = bB; - localAnchorA = bodyA->GetLocalPoint(anchor); - localAnchorB = bodyB->GetLocalPoint(anchor); -} - -b2FrictionJoint::b2FrictionJoint(const b2FrictionJointDef* def) -: b2Joint(def) -{ - m_localAnchorA = def->localAnchorA; - m_localAnchorB = def->localAnchorB; - - m_linearImpulse.SetZero(); - m_angularImpulse = 0.0f; - - m_maxForce = def->maxForce; - m_maxTorque = def->maxTorque; -} - -void b2FrictionJoint::InitVelocityConstraints(const b2SolverData& data) -{ - m_indexA = m_bodyA->m_islandIndex; - m_indexB = m_bodyB->m_islandIndex; - m_localCenterA = m_bodyA->m_sweep.localCenter; - m_localCenterB = m_bodyB->m_sweep.localCenter; - m_invMassA = m_bodyA->m_invMass; - m_invMassB = m_bodyB->m_invMass; - m_invIA = m_bodyA->m_invI; - m_invIB = m_bodyB->m_invI; - - float aA = data.positions[m_indexA].a; - b2Vec2 vA = data.velocities[m_indexA].v; - float wA = data.velocities[m_indexA].w; - - float aB = data.positions[m_indexB].a; - b2Vec2 vB = data.velocities[m_indexB].v; - float wB = data.velocities[m_indexB].w; - - b2Rot qA(aA), qB(aB); - - // Compute the effective mass matrix. - m_rA = b2Mul(qA, m_localAnchorA - m_localCenterA); - m_rB = b2Mul(qB, m_localAnchorB - m_localCenterB); - - // J = [-I -r1_skew I r2_skew] - // [ 0 -1 0 1] - // r_skew = [-ry; rx] - - // Matlab - // K = [ mA+r1y^2*iA+mB+r2y^2*iB, -r1y*iA*r1x-r2y*iB*r2x, -r1y*iA-r2y*iB] - // [ -r1y*iA*r1x-r2y*iB*r2x, mA+r1x^2*iA+mB+r2x^2*iB, r1x*iA+r2x*iB] - // [ -r1y*iA-r2y*iB, r1x*iA+r2x*iB, iA+iB] - - float mA = m_invMassA, mB = m_invMassB; - float iA = m_invIA, iB = m_invIB; - - b2Mat22 K; - K.ex.x = mA + mB + iA * m_rA.y * m_rA.y + iB * m_rB.y * m_rB.y; - K.ex.y = -iA * m_rA.x * m_rA.y - iB * m_rB.x * m_rB.y; - K.ey.x = K.ex.y; - K.ey.y = mA + mB + iA * m_rA.x * m_rA.x + iB * m_rB.x * m_rB.x; - - m_linearMass = K.GetInverse(); - - m_angularMass = iA + iB; - if (m_angularMass > 0.0f) - { - m_angularMass = 1.0f / m_angularMass; - } - - if (data.step.warmStarting) - { - // Scale impulses to support a variable time step. - m_linearImpulse *= data.step.dtRatio; - m_angularImpulse *= data.step.dtRatio; - - b2Vec2 P(m_linearImpulse.x, m_linearImpulse.y); - vA -= mA * P; - wA -= iA * (b2Cross(m_rA, P) + m_angularImpulse); - vB += mB * P; - wB += iB * (b2Cross(m_rB, P) + m_angularImpulse); - } - else - { - m_linearImpulse.SetZero(); - m_angularImpulse = 0.0f; - } - - data.velocities[m_indexA].v = vA; - data.velocities[m_indexA].w = wA; - data.velocities[m_indexB].v = vB; - data.velocities[m_indexB].w = wB; -} - -void b2FrictionJoint::SolveVelocityConstraints(const b2SolverData& data) -{ - b2Vec2 vA = data.velocities[m_indexA].v; - float wA = data.velocities[m_indexA].w; - b2Vec2 vB = data.velocities[m_indexB].v; - float wB = data.velocities[m_indexB].w; - - float mA = m_invMassA, mB = m_invMassB; - float iA = m_invIA, iB = m_invIB; - - float h = data.step.dt; - - // Solve angular friction - { - float Cdot = wB - wA; - float impulse = -m_angularMass * Cdot; - - float oldImpulse = m_angularImpulse; - float maxImpulse = h * m_maxTorque; - m_angularImpulse = b2Clamp(m_angularImpulse + impulse, -maxImpulse, maxImpulse); - impulse = m_angularImpulse - oldImpulse; - - wA -= iA * impulse; - wB += iB * impulse; - } - - // Solve linear friction - { - b2Vec2 Cdot = vB + b2Cross(wB, m_rB) - vA - b2Cross(wA, m_rA); - - b2Vec2 impulse = -b2Mul(m_linearMass, Cdot); - b2Vec2 oldImpulse = m_linearImpulse; - m_linearImpulse += impulse; - - float maxImpulse = h * m_maxForce; - - if (m_linearImpulse.LengthSquared() > maxImpulse * maxImpulse) - { - m_linearImpulse.Normalize(); - m_linearImpulse *= maxImpulse; - } - - impulse = m_linearImpulse - oldImpulse; - - vA -= mA * impulse; - wA -= iA * b2Cross(m_rA, impulse); - - vB += mB * impulse; - wB += iB * b2Cross(m_rB, impulse); - } - - data.velocities[m_indexA].v = vA; - data.velocities[m_indexA].w = wA; - data.velocities[m_indexB].v = vB; - data.velocities[m_indexB].w = wB; -} - -bool b2FrictionJoint::SolvePositionConstraints(const b2SolverData& data) -{ - B2_NOT_USED(data); - - return true; -} - -b2Vec2 b2FrictionJoint::GetAnchorA() const -{ - return m_bodyA->GetWorldPoint(m_localAnchorA); -} - -b2Vec2 b2FrictionJoint::GetAnchorB() const -{ - return m_bodyB->GetWorldPoint(m_localAnchorB); -} - -b2Vec2 b2FrictionJoint::GetReactionForce(float inv_dt) const -{ - return inv_dt * m_linearImpulse; -} - -float b2FrictionJoint::GetReactionTorque(float inv_dt) const -{ - return inv_dt * m_angularImpulse; -} - -void b2FrictionJoint::SetMaxForce(float force) -{ - b2Assert(b2IsValid(force) && force >= 0.0f); - m_maxForce = force; -} - -float b2FrictionJoint::GetMaxForce() const -{ - return m_maxForce; -} - -void b2FrictionJoint::SetMaxTorque(float torque) -{ - b2Assert(b2IsValid(torque) && torque >= 0.0f); - m_maxTorque = torque; -} - -float b2FrictionJoint::GetMaxTorque() const -{ - return m_maxTorque; -} - -void b2FrictionJoint::Dump() -{ - int32 indexA = m_bodyA->m_islandIndex; - int32 indexB = m_bodyB->m_islandIndex; - - b2Dump(" b2FrictionJointDef jd;\n"); - b2Dump(" jd.bodyA = bodies[%d];\n", indexA); - b2Dump(" jd.bodyB = bodies[%d];\n", indexB); - b2Dump(" jd.collideConnected = bool(%d);\n", m_collideConnected); - b2Dump(" jd.localAnchorA.Set(%.9g, %.9g);\n", m_localAnchorA.x, m_localAnchorA.y); - b2Dump(" jd.localAnchorB.Set(%.9g, %.9g);\n", m_localAnchorB.x, m_localAnchorB.y); - b2Dump(" jd.maxForce = %.9g;\n", m_maxForce); - b2Dump(" jd.maxTorque = %.9g;\n", m_maxTorque); - b2Dump(" joints[%d] = m_world->CreateJoint(&jd);\n", m_index); -} diff --git a/3rdparty/box2d/src/dynamics/b2_gear_joint.cpp b/3rdparty/box2d/src/dynamics/b2_gear_joint.cpp deleted file mode 100644 index 5fb547245e93..000000000000 --- a/3rdparty/box2d/src/dynamics/b2_gear_joint.cpp +++ /dev/null @@ -1,437 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "box2d/b2_gear_joint.h" -#include "box2d/b2_revolute_joint.h" -#include "box2d/b2_prismatic_joint.h" -#include "box2d/b2_body.h" -#include "box2d/b2_time_step.h" - -// Gear Joint: -// C0 = (coordinate1 + ratio * coordinate2)_initial -// C = (coordinate1 + ratio * coordinate2) - C0 = 0 -// J = [J1 ratio * J2] -// K = J * invM * JT -// = J1 * invM1 * J1T + ratio * ratio * J2 * invM2 * J2T -// -// Revolute: -// coordinate = rotation -// Cdot = angularVelocity -// J = [0 0 1] -// K = J * invM * JT = invI -// -// Prismatic: -// coordinate = dot(p - pg, ug) -// Cdot = dot(v + cross(w, r), ug) -// J = [ug cross(r, ug)] -// K = J * invM * JT = invMass + invI * cross(r, ug)^2 - -b2GearJoint::b2GearJoint(const b2GearJointDef* def) -: b2Joint(def) -{ - m_joint1 = def->joint1; - m_joint2 = def->joint2; - - m_typeA = m_joint1->GetType(); - m_typeB = m_joint2->GetType(); - - b2Assert(m_typeA == e_revoluteJoint || m_typeA == e_prismaticJoint); - b2Assert(m_typeB == e_revoluteJoint || m_typeB == e_prismaticJoint); - - float coordinateA, coordinateB; - - // TODO_ERIN there might be some problem with the joint edges in b2Joint. - - m_bodyC = m_joint1->GetBodyA(); - m_bodyA = m_joint1->GetBodyB(); - - // Body B on joint1 must be dynamic - b2Assert(m_bodyA->m_type == b2_dynamicBody); - - // Get geometry of joint1 - b2Transform xfA = m_bodyA->m_xf; - float aA = m_bodyA->m_sweep.a; - b2Transform xfC = m_bodyC->m_xf; - float aC = m_bodyC->m_sweep.a; - - if (m_typeA == e_revoluteJoint) - { - b2RevoluteJoint* revolute = (b2RevoluteJoint*)def->joint1; - m_localAnchorC = revolute->m_localAnchorA; - m_localAnchorA = revolute->m_localAnchorB; - m_referenceAngleA = revolute->m_referenceAngle; - m_localAxisC.SetZero(); - - coordinateA = aA - aC - m_referenceAngleA; - - // position error is measured in radians - m_tolerance = b2_angularSlop; - } - else - { - b2PrismaticJoint* prismatic = (b2PrismaticJoint*)def->joint1; - m_localAnchorC = prismatic->m_localAnchorA; - m_localAnchorA = prismatic->m_localAnchorB; - m_referenceAngleA = prismatic->m_referenceAngle; - m_localAxisC = prismatic->m_localXAxisA; - - b2Vec2 pC = m_localAnchorC; - b2Vec2 pA = b2MulT(xfC.q, b2Mul(xfA.q, m_localAnchorA) + (xfA.p - xfC.p)); - coordinateA = b2Dot(pA - pC, m_localAxisC); - - // position error is measured in meters - m_tolerance = b2_linearSlop; - } - - m_bodyD = m_joint2->GetBodyA(); - m_bodyB = m_joint2->GetBodyB(); - - // Body B on joint2 must be dynamic - b2Assert(m_bodyB->m_type == b2_dynamicBody); - - // Get geometry of joint2 - b2Transform xfB = m_bodyB->m_xf; - float aB = m_bodyB->m_sweep.a; - b2Transform xfD = m_bodyD->m_xf; - float aD = m_bodyD->m_sweep.a; - - if (m_typeB == e_revoluteJoint) - { - b2RevoluteJoint* revolute = (b2RevoluteJoint*)def->joint2; - m_localAnchorD = revolute->m_localAnchorA; - m_localAnchorB = revolute->m_localAnchorB; - m_referenceAngleB = revolute->m_referenceAngle; - m_localAxisD.SetZero(); - - coordinateB = aB - aD - m_referenceAngleB; - } - else - { - b2PrismaticJoint* prismatic = (b2PrismaticJoint*)def->joint2; - m_localAnchorD = prismatic->m_localAnchorA; - m_localAnchorB = prismatic->m_localAnchorB; - m_referenceAngleB = prismatic->m_referenceAngle; - m_localAxisD = prismatic->m_localXAxisA; - - b2Vec2 pD = m_localAnchorD; - b2Vec2 pB = b2MulT(xfD.q, b2Mul(xfB.q, m_localAnchorB) + (xfB.p - xfD.p)); - coordinateB = b2Dot(pB - pD, m_localAxisD); - } - - m_ratio = def->ratio; - - m_constant = coordinateA + m_ratio * coordinateB; - - m_impulse = 0.0f; -} - -void b2GearJoint::InitVelocityConstraints(const b2SolverData& data) -{ - m_indexA = m_bodyA->m_islandIndex; - m_indexB = m_bodyB->m_islandIndex; - m_indexC = m_bodyC->m_islandIndex; - m_indexD = m_bodyD->m_islandIndex; - m_lcA = m_bodyA->m_sweep.localCenter; - m_lcB = m_bodyB->m_sweep.localCenter; - m_lcC = m_bodyC->m_sweep.localCenter; - m_lcD = m_bodyD->m_sweep.localCenter; - m_mA = m_bodyA->m_invMass; - m_mB = m_bodyB->m_invMass; - m_mC = m_bodyC->m_invMass; - m_mD = m_bodyD->m_invMass; - m_iA = m_bodyA->m_invI; - m_iB = m_bodyB->m_invI; - m_iC = m_bodyC->m_invI; - m_iD = m_bodyD->m_invI; - - float aA = data.positions[m_indexA].a; - b2Vec2 vA = data.velocities[m_indexA].v; - float wA = data.velocities[m_indexA].w; - - float aB = data.positions[m_indexB].a; - b2Vec2 vB = data.velocities[m_indexB].v; - float wB = data.velocities[m_indexB].w; - - float aC = data.positions[m_indexC].a; - b2Vec2 vC = data.velocities[m_indexC].v; - float wC = data.velocities[m_indexC].w; - - float aD = data.positions[m_indexD].a; - b2Vec2 vD = data.velocities[m_indexD].v; - float wD = data.velocities[m_indexD].w; - - b2Rot qA(aA), qB(aB), qC(aC), qD(aD); - - m_mass = 0.0f; - - if (m_typeA == e_revoluteJoint) - { - m_JvAC.SetZero(); - m_JwA = 1.0f; - m_JwC = 1.0f; - m_mass += m_iA + m_iC; - } - else - { - b2Vec2 u = b2Mul(qC, m_localAxisC); - b2Vec2 rC = b2Mul(qC, m_localAnchorC - m_lcC); - b2Vec2 rA = b2Mul(qA, m_localAnchorA - m_lcA); - m_JvAC = u; - m_JwC = b2Cross(rC, u); - m_JwA = b2Cross(rA, u); - m_mass += m_mC + m_mA + m_iC * m_JwC * m_JwC + m_iA * m_JwA * m_JwA; - } - - if (m_typeB == e_revoluteJoint) - { - m_JvBD.SetZero(); - m_JwB = m_ratio; - m_JwD = m_ratio; - m_mass += m_ratio * m_ratio * (m_iB + m_iD); - } - else - { - b2Vec2 u = b2Mul(qD, m_localAxisD); - b2Vec2 rD = b2Mul(qD, m_localAnchorD - m_lcD); - b2Vec2 rB = b2Mul(qB, m_localAnchorB - m_lcB); - m_JvBD = m_ratio * u; - m_JwD = m_ratio * b2Cross(rD, u); - m_JwB = m_ratio * b2Cross(rB, u); - m_mass += m_ratio * m_ratio * (m_mD + m_mB) + m_iD * m_JwD * m_JwD + m_iB * m_JwB * m_JwB; - } - - // Compute effective mass. - m_mass = m_mass > 0.0f ? 1.0f / m_mass : 0.0f; - - if (data.step.warmStarting) - { - vA += (m_mA * m_impulse) * m_JvAC; - wA += m_iA * m_impulse * m_JwA; - vB += (m_mB * m_impulse) * m_JvBD; - wB += m_iB * m_impulse * m_JwB; - vC -= (m_mC * m_impulse) * m_JvAC; - wC -= m_iC * m_impulse * m_JwC; - vD -= (m_mD * m_impulse) * m_JvBD; - wD -= m_iD * m_impulse * m_JwD; - } - else - { - m_impulse = 0.0f; - } - - data.velocities[m_indexA].v = vA; - data.velocities[m_indexA].w = wA; - data.velocities[m_indexB].v = vB; - data.velocities[m_indexB].w = wB; - data.velocities[m_indexC].v = vC; - data.velocities[m_indexC].w = wC; - data.velocities[m_indexD].v = vD; - data.velocities[m_indexD].w = wD; -} - -void b2GearJoint::SolveVelocityConstraints(const b2SolverData& data) -{ - b2Vec2 vA = data.velocities[m_indexA].v; - float wA = data.velocities[m_indexA].w; - b2Vec2 vB = data.velocities[m_indexB].v; - float wB = data.velocities[m_indexB].w; - b2Vec2 vC = data.velocities[m_indexC].v; - float wC = data.velocities[m_indexC].w; - b2Vec2 vD = data.velocities[m_indexD].v; - float wD = data.velocities[m_indexD].w; - - float Cdot = b2Dot(m_JvAC, vA - vC) + b2Dot(m_JvBD, vB - vD); - Cdot += (m_JwA * wA - m_JwC * wC) + (m_JwB * wB - m_JwD * wD); - - float impulse = -m_mass * Cdot; - m_impulse += impulse; - - vA += (m_mA * impulse) * m_JvAC; - wA += m_iA * impulse * m_JwA; - vB += (m_mB * impulse) * m_JvBD; - wB += m_iB * impulse * m_JwB; - vC -= (m_mC * impulse) * m_JvAC; - wC -= m_iC * impulse * m_JwC; - vD -= (m_mD * impulse) * m_JvBD; - wD -= m_iD * impulse * m_JwD; - - data.velocities[m_indexA].v = vA; - data.velocities[m_indexA].w = wA; - data.velocities[m_indexB].v = vB; - data.velocities[m_indexB].w = wB; - data.velocities[m_indexC].v = vC; - data.velocities[m_indexC].w = wC; - data.velocities[m_indexD].v = vD; - data.velocities[m_indexD].w = wD; -} - -bool b2GearJoint::SolvePositionConstraints(const b2SolverData& data) -{ - b2Vec2 cA = data.positions[m_indexA].c; - float aA = data.positions[m_indexA].a; - b2Vec2 cB = data.positions[m_indexB].c; - float aB = data.positions[m_indexB].a; - b2Vec2 cC = data.positions[m_indexC].c; - float aC = data.positions[m_indexC].a; - b2Vec2 cD = data.positions[m_indexD].c; - float aD = data.positions[m_indexD].a; - - b2Rot qA(aA), qB(aB), qC(aC), qD(aD); - - float coordinateA, coordinateB; - - b2Vec2 JvAC, JvBD; - float JwA, JwB, JwC, JwD; - float mass = 0.0f; - - if (m_typeA == e_revoluteJoint) - { - JvAC.SetZero(); - JwA = 1.0f; - JwC = 1.0f; - mass += m_iA + m_iC; - - coordinateA = aA - aC - m_referenceAngleA; - } - else - { - b2Vec2 u = b2Mul(qC, m_localAxisC); - b2Vec2 rC = b2Mul(qC, m_localAnchorC - m_lcC); - b2Vec2 rA = b2Mul(qA, m_localAnchorA - m_lcA); - JvAC = u; - JwC = b2Cross(rC, u); - JwA = b2Cross(rA, u); - mass += m_mC + m_mA + m_iC * JwC * JwC + m_iA * JwA * JwA; - - b2Vec2 pC = m_localAnchorC - m_lcC; - b2Vec2 pA = b2MulT(qC, rA + (cA - cC)); - coordinateA = b2Dot(pA - pC, m_localAxisC); - } - - if (m_typeB == e_revoluteJoint) - { - JvBD.SetZero(); - JwB = m_ratio; - JwD = m_ratio; - mass += m_ratio * m_ratio * (m_iB + m_iD); - - coordinateB = aB - aD - m_referenceAngleB; - } - else - { - b2Vec2 u = b2Mul(qD, m_localAxisD); - b2Vec2 rD = b2Mul(qD, m_localAnchorD - m_lcD); - b2Vec2 rB = b2Mul(qB, m_localAnchorB - m_lcB); - JvBD = m_ratio * u; - JwD = m_ratio * b2Cross(rD, u); - JwB = m_ratio * b2Cross(rB, u); - mass += m_ratio * m_ratio * (m_mD + m_mB) + m_iD * JwD * JwD + m_iB * JwB * JwB; - - b2Vec2 pD = m_localAnchorD - m_lcD; - b2Vec2 pB = b2MulT(qD, rB + (cB - cD)); - coordinateB = b2Dot(pB - pD, m_localAxisD); - } - - float C = (coordinateA + m_ratio * coordinateB) - m_constant; - - float impulse = 0.0f; - if (mass > 0.0f) - { - impulse = -C / mass; - } - - cA += m_mA * impulse * JvAC; - aA += m_iA * impulse * JwA; - cB += m_mB * impulse * JvBD; - aB += m_iB * impulse * JwB; - cC -= m_mC * impulse * JvAC; - aC -= m_iC * impulse * JwC; - cD -= m_mD * impulse * JvBD; - aD -= m_iD * impulse * JwD; - - data.positions[m_indexA].c = cA; - data.positions[m_indexA].a = aA; - data.positions[m_indexB].c = cB; - data.positions[m_indexB].a = aB; - data.positions[m_indexC].c = cC; - data.positions[m_indexC].a = aC; - data.positions[m_indexD].c = cD; - data.positions[m_indexD].a = aD; - - if (b2Abs(C) < m_tolerance) - { - return true; - } - - return false; -} - -b2Vec2 b2GearJoint::GetAnchorA() const -{ - return m_bodyA->GetWorldPoint(m_localAnchorA); -} - -b2Vec2 b2GearJoint::GetAnchorB() const -{ - return m_bodyB->GetWorldPoint(m_localAnchorB); -} - -b2Vec2 b2GearJoint::GetReactionForce(float inv_dt) const -{ - b2Vec2 P = m_impulse * m_JvAC; - return inv_dt * P; -} - -float b2GearJoint::GetReactionTorque(float inv_dt) const -{ - float L = m_impulse * m_JwA; - return inv_dt * L; -} - -void b2GearJoint::SetRatio(float ratio) -{ - b2Assert(b2IsValid(ratio)); - m_ratio = ratio; -} - -float b2GearJoint::GetRatio() const -{ - return m_ratio; -} - -void b2GearJoint::Dump() -{ - int32 indexA = m_bodyA->m_islandIndex; - int32 indexB = m_bodyB->m_islandIndex; - - int32 index1 = m_joint1->m_index; - int32 index2 = m_joint2->m_index; - - b2Dump(" b2GearJointDef jd;\n"); - b2Dump(" jd.bodyA = bodies[%d];\n", indexA); - b2Dump(" jd.bodyB = bodies[%d];\n", indexB); - b2Dump(" jd.collideConnected = bool(%d);\n", m_collideConnected); - b2Dump(" jd.joint1 = joints[%d];\n", index1); - b2Dump(" jd.joint2 = joints[%d];\n", index2); - b2Dump(" jd.ratio = %.9g;\n", m_ratio); - b2Dump(" joints[%d] = m_world->CreateJoint(&jd);\n", m_index); -} diff --git a/3rdparty/box2d/src/dynamics/b2_island.cpp b/3rdparty/box2d/src/dynamics/b2_island.cpp deleted file mode 100644 index 34413056b1f4..000000000000 --- a/3rdparty/box2d/src/dynamics/b2_island.cpp +++ /dev/null @@ -1,544 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "box2d/b2_body.h" -#include "box2d/b2_contact.h" -#include "box2d/b2_distance.h" -#include "box2d/b2_fixture.h" -#include "box2d/b2_joint.h" -#include "box2d/b2_stack_allocator.h" -#include "box2d/b2_timer.h" -#include "box2d/b2_world.h" - -#include "b2_contact_solver.h" -#include "b2_island.h" - -/* -Position Correction Notes -========================= -I tried the several algorithms for position correction of the 2D revolute joint. -I looked at these systems: -- simple pendulum (1m diameter sphere on massless 5m stick) with initial angular velocity of 100 rad/s. -- suspension bridge with 30 1m long planks of length 1m. -- multi-link chain with 30 1m long links. - -Here are the algorithms: - -Baumgarte - A fraction of the position error is added to the velocity error. There is no -separate position solver. - -Pseudo Velocities - After the velocity solver and position integration, -the position error, Jacobian, and effective mass are recomputed. Then -the velocity constraints are solved with pseudo velocities and a fraction -of the position error is added to the pseudo velocity error. The pseudo -velocities are initialized to zero and there is no warm-starting. After -the position solver, the pseudo velocities are added to the positions. -This is also called the First Order World method or the Position LCP method. - -Modified Nonlinear Gauss-Seidel (NGS) - Like Pseudo Velocities except the -position error is re-computed for each constraint and the positions are updated -after the constraint is solved. The radius vectors (aka Jacobians) are -re-computed too (otherwise the algorithm has horrible instability). The pseudo -velocity states are not needed because they are effectively zero at the beginning -of each iteration. Since we have the current position error, we allow the -iterations to terminate early if the error becomes smaller than b2_linearSlop. - -Full NGS or just NGS - Like Modified NGS except the effective mass are re-computed -each time a constraint is solved. - -Here are the results: -Baumgarte - this is the cheapest algorithm but it has some stability problems, -especially with the bridge. The chain links separate easily close to the root -and they jitter as they struggle to pull together. This is one of the most common -methods in the field. The big drawback is that the position correction artificially -affects the momentum, thus leading to instabilities and false bounce. I used a -bias factor of 0.2. A larger bias factor makes the bridge less stable, a smaller -factor makes joints and contacts more spongy. - -Pseudo Velocities - the is more stable than the Baumgarte method. The bridge is -stable. However, joints still separate with large angular velocities. Drag the -simple pendulum in a circle quickly and the joint will separate. The chain separates -easily and does not recover. I used a bias factor of 0.2. A larger value lead to -the bridge collapsing when a heavy cube drops on it. - -Modified NGS - this algorithm is better in some ways than Baumgarte and Pseudo -Velocities, but in other ways it is worse. The bridge and chain are much more -stable, but the simple pendulum goes unstable at high angular velocities. - -Full NGS - stable in all tests. The joints display good stiffness. The bridge -still sags, but this is better than infinite forces. - -Recommendations -Pseudo Velocities are not really worthwhile because the bridge and chain cannot -recover from joint separation. In other cases the benefit over Baumgarte is small. - -Modified NGS is not a robust method for the revolute joint due to the violent -instability seen in the simple pendulum. Perhaps it is viable with other constraint -types, especially scalar constraints where the effective mass is a scalar. - -This leaves Baumgarte and Full NGS. Baumgarte has small, but manageable instabilities -and is very fast. I don't think we can escape Baumgarte, especially in highly -demanding cases where high constraint fidelity is not needed. - -Full NGS is robust and easy on the eyes. I recommend this as an option for -higher fidelity simulation and certainly for suspension bridges and long chains. -Full NGS might be a good choice for ragdolls, especially motorized ragdolls where -joint separation can be problematic. The number of NGS iterations can be reduced -for better performance without harming robustness much. - -Each joint in a can be handled differently in the position solver. So I recommend -a system where the user can select the algorithm on a per joint basis. I would -probably default to the slower Full NGS and let the user select the faster -Baumgarte method in performance critical scenarios. -*/ - -/* -Cache Performance - -The Box2D solvers are dominated by cache misses. Data structures are designed -to increase the number of cache hits. Much of misses are due to random access -to body data. The constraint structures are iterated over linearly, which leads -to few cache misses. - -The bodies are not accessed during iteration. Instead read only data, such as -the mass values are stored with the constraints. The mutable data are the constraint -impulses and the bodies velocities/positions. The impulses are held inside the -constraint structures. The body velocities/positions are held in compact, temporary -arrays to increase the number of cache hits. Linear and angular velocity are -stored in a single array since multiple arrays lead to multiple misses. -*/ - -/* -2D Rotation - -R = [cos(theta) -sin(theta)] - [sin(theta) cos(theta) ] - -thetaDot = omega - -Let q1 = cos(theta), q2 = sin(theta). -R = [q1 -q2] - [q2 q1] - -q1Dot = -thetaDot * q2 -q2Dot = thetaDot * q1 - -q1_new = q1_old - dt * w * q2 -q2_new = q2_old + dt * w * q1 -then normalize. - -This might be faster than computing sin+cos. -However, we can compute sin+cos of the same angle fast. -*/ - -b2Island::b2Island( - int32 bodyCapacity, - int32 contactCapacity, - int32 jointCapacity, - b2StackAllocator* allocator, - b2ContactListener* listener) -{ - m_bodyCapacity = bodyCapacity; - m_contactCapacity = contactCapacity; - m_jointCapacity = jointCapacity; - m_bodyCount = 0; - m_contactCount = 0; - m_jointCount = 0; - - m_allocator = allocator; - m_listener = listener; - - m_bodies = (b2Body**)m_allocator->Allocate(bodyCapacity * sizeof(b2Body*)); - m_contacts = (b2Contact**)m_allocator->Allocate(contactCapacity * sizeof(b2Contact*)); - m_joints = (b2Joint**)m_allocator->Allocate(jointCapacity * sizeof(b2Joint*)); - - m_velocities = (b2Velocity*)m_allocator->Allocate(m_bodyCapacity * sizeof(b2Velocity)); - m_positions = (b2Position*)m_allocator->Allocate(m_bodyCapacity * sizeof(b2Position)); -} - -b2Island::~b2Island() -{ - // Warning: the order should reverse the constructor order. - m_allocator->Free(m_positions); - m_allocator->Free(m_velocities); - m_allocator->Free(m_joints); - m_allocator->Free(m_contacts); - m_allocator->Free(m_bodies); -} - -void b2Island::Solve(b2Profile* profile, const b2TimeStep& step, const b2Vec2& gravity, bool allowSleep) -{ - b2Timer timer; - - float h = step.dt; - - // Integrate velocities and apply damping. Initialize the body state. - for (int32 i = 0; i < m_bodyCount; ++i) - { - b2Body* b = m_bodies[i]; - - b2Vec2 c = b->m_sweep.c; - float a = b->m_sweep.a; - b2Vec2 v = b->m_linearVelocity; - float w = b->m_angularVelocity; - - // Store positions for continuous collision. - b->m_sweep.c0 = b->m_sweep.c; - b->m_sweep.a0 = b->m_sweep.a; - - if (b->m_type == b2_dynamicBody) - { - // Integrate velocities. - v += h * b->m_invMass * (b->m_gravityScale * b->m_mass * gravity + b->m_force); - w += h * b->m_invI * b->m_torque; - - // Apply damping. - // ODE: dv/dt + c * v = 0 - // Solution: v(t) = v0 * exp(-c * t) - // Time step: v(t + dt) = v0 * exp(-c * (t + dt)) = v0 * exp(-c * t) * exp(-c * dt) = v * exp(-c * dt) - // v2 = exp(-c * dt) * v1 - // Pade approximation: - // v2 = v1 * 1 / (1 + c * dt) - v *= 1.0f / (1.0f + h * b->m_linearDamping); - w *= 1.0f / (1.0f + h * b->m_angularDamping); - } - - m_positions[i].c = c; - m_positions[i].a = a; - m_velocities[i].v = v; - m_velocities[i].w = w; - } - - timer.Reset(); - - // Solver data - b2SolverData solverData; - solverData.step = step; - solverData.positions = m_positions; - solverData.velocities = m_velocities; - - // Initialize velocity constraints. - b2ContactSolverDef contactSolverDef; - contactSolverDef.step = step; - contactSolverDef.contacts = m_contacts; - contactSolverDef.count = m_contactCount; - contactSolverDef.positions = m_positions; - contactSolverDef.velocities = m_velocities; - contactSolverDef.allocator = m_allocator; - - b2ContactSolver contactSolver(&contactSolverDef); - contactSolver.InitializeVelocityConstraints(); - - if (step.warmStarting) - { - contactSolver.WarmStart(); - } - - for (int32 i = 0; i < m_jointCount; ++i) - { - m_joints[i]->InitVelocityConstraints(solverData); - } - - profile->solveInit = timer.GetMilliseconds(); - - // Solve velocity constraints - timer.Reset(); - for (int32 i = 0; i < step.velocityIterations; ++i) - { - for (int32 j = 0; j < m_jointCount; ++j) - { - m_joints[j]->SolveVelocityConstraints(solverData); - } - - contactSolver.SolveVelocityConstraints(); - } - - // Store impulses for warm starting - contactSolver.StoreImpulses(); - profile->solveVelocity = timer.GetMilliseconds(); - - // Integrate positions - for (int32 i = 0; i < m_bodyCount; ++i) - { - b2Vec2 c = m_positions[i].c; - float a = m_positions[i].a; - b2Vec2 v = m_velocities[i].v; - float w = m_velocities[i].w; - - // Check for large velocities - b2Vec2 translation = h * v; - if (b2Dot(translation, translation) > b2_maxTranslationSquared) - { - float ratio = b2_maxTranslation / translation.Length(); - v *= ratio; - } - - float rotation = h * w; - if (rotation * rotation > b2_maxRotationSquared) - { - float ratio = b2_maxRotation / b2Abs(rotation); - w *= ratio; - } - - // Integrate - c += h * v; - a += h * w; - - m_positions[i].c = c; - m_positions[i].a = a; - m_velocities[i].v = v; - m_velocities[i].w = w; - } - - // Solve position constraints - timer.Reset(); - bool positionSolved = false; - for (int32 i = 0; i < step.positionIterations; ++i) - { - bool contactsOkay = contactSolver.SolvePositionConstraints(); - - bool jointsOkay = true; - for (int32 j = 0; j < m_jointCount; ++j) - { - bool jointOkay = m_joints[j]->SolvePositionConstraints(solverData); - jointsOkay = jointsOkay && jointOkay; - } - - if (contactsOkay && jointsOkay) - { - // Exit early if the position errors are small. - positionSolved = true; - break; - } - } - - // Copy state buffers back to the bodies - for (int32 i = 0; i < m_bodyCount; ++i) - { - b2Body* body = m_bodies[i]; - body->m_sweep.c = m_positions[i].c; - body->m_sweep.a = m_positions[i].a; - body->m_linearVelocity = m_velocities[i].v; - body->m_angularVelocity = m_velocities[i].w; - body->SynchronizeTransform(); - } - - profile->solvePosition = timer.GetMilliseconds(); - - Report(contactSolver.m_velocityConstraints); - - if (allowSleep) - { - float minSleepTime = b2_maxFloat; - - const float linTolSqr = b2_linearSleepTolerance * b2_linearSleepTolerance; - const float angTolSqr = b2_angularSleepTolerance * b2_angularSleepTolerance; - - for (int32 i = 0; i < m_bodyCount; ++i) - { - b2Body* b = m_bodies[i]; - if (b->GetType() == b2_staticBody) - { - continue; - } - - if ((b->m_flags & b2Body::e_autoSleepFlag) == 0 || - b->m_angularVelocity * b->m_angularVelocity > angTolSqr || - b2Dot(b->m_linearVelocity, b->m_linearVelocity) > linTolSqr) - { - b->m_sleepTime = 0.0f; - minSleepTime = 0.0f; - } - else - { - b->m_sleepTime += h; - minSleepTime = b2Min(minSleepTime, b->m_sleepTime); - } - } - - if (minSleepTime >= b2_timeToSleep && positionSolved) - { - for (int32 i = 0; i < m_bodyCount; ++i) - { - b2Body* b = m_bodies[i]; - b->SetAwake(false); - } - } - } -} - -void b2Island::SolveTOI(const b2TimeStep& subStep, int32 toiIndexA, int32 toiIndexB) -{ - b2Assert(toiIndexA < m_bodyCount); - b2Assert(toiIndexB < m_bodyCount); - - // Initialize the body state. - for (int32 i = 0; i < m_bodyCount; ++i) - { - b2Body* b = m_bodies[i]; - m_positions[i].c = b->m_sweep.c; - m_positions[i].a = b->m_sweep.a; - m_velocities[i].v = b->m_linearVelocity; - m_velocities[i].w = b->m_angularVelocity; - } - - b2ContactSolverDef contactSolverDef; - contactSolverDef.contacts = m_contacts; - contactSolverDef.count = m_contactCount; - contactSolverDef.allocator = m_allocator; - contactSolverDef.step = subStep; - contactSolverDef.positions = m_positions; - contactSolverDef.velocities = m_velocities; - b2ContactSolver contactSolver(&contactSolverDef); - - // Solve position constraints. - for (int32 i = 0; i < subStep.positionIterations; ++i) - { - bool contactsOkay = contactSolver.SolveTOIPositionConstraints(toiIndexA, toiIndexB); - if (contactsOkay) - { - break; - } - } - -#if 0 - // Is the new position really safe? - for (int32 i = 0; i < m_contactCount; ++i) - { - b2Contact* c = m_contacts[i]; - b2Fixture* fA = c->GetFixtureA(); - b2Fixture* fB = c->GetFixtureB(); - - b2Body* bA = fA->GetBody(); - b2Body* bB = fB->GetBody(); - - int32 indexA = c->GetChildIndexA(); - int32 indexB = c->GetChildIndexB(); - - b2DistanceInput input; - input.proxyA.Set(fA->GetShape(), indexA); - input.proxyB.Set(fB->GetShape(), indexB); - input.transformA = bA->GetTransform(); - input.transformB = bB->GetTransform(); - input.useRadii = false; - - b2DistanceOutput output; - b2SimplexCache cache; - cache.count = 0; - b2Distance(&output, &cache, &input); - - if (output.distance == 0 || cache.count == 3) - { - cache.count += 0; - } - } -#endif - - // Leap of faith to new safe state. - m_bodies[toiIndexA]->m_sweep.c0 = m_positions[toiIndexA].c; - m_bodies[toiIndexA]->m_sweep.a0 = m_positions[toiIndexA].a; - m_bodies[toiIndexB]->m_sweep.c0 = m_positions[toiIndexB].c; - m_bodies[toiIndexB]->m_sweep.a0 = m_positions[toiIndexB].a; - - // No warm starting is needed for TOI events because warm - // starting impulses were applied in the discrete solver. - contactSolver.InitializeVelocityConstraints(); - - // Solve velocity constraints. - for (int32 i = 0; i < subStep.velocityIterations; ++i) - { - contactSolver.SolveVelocityConstraints(); - } - - // Don't store the TOI contact forces for warm starting - // because they can be quite large. - - float h = subStep.dt; - - // Integrate positions - for (int32 i = 0; i < m_bodyCount; ++i) - { - b2Vec2 c = m_positions[i].c; - float a = m_positions[i].a; - b2Vec2 v = m_velocities[i].v; - float w = m_velocities[i].w; - - // Check for large velocities - b2Vec2 translation = h * v; - if (b2Dot(translation, translation) > b2_maxTranslationSquared) - { - float ratio = b2_maxTranslation / translation.Length(); - v *= ratio; - } - - float rotation = h * w; - if (rotation * rotation > b2_maxRotationSquared) - { - float ratio = b2_maxRotation / b2Abs(rotation); - w *= ratio; - } - - // Integrate - c += h * v; - a += h * w; - - m_positions[i].c = c; - m_positions[i].a = a; - m_velocities[i].v = v; - m_velocities[i].w = w; - - // Sync bodies - b2Body* body = m_bodies[i]; - body->m_sweep.c = c; - body->m_sweep.a = a; - body->m_linearVelocity = v; - body->m_angularVelocity = w; - body->SynchronizeTransform(); - } - - Report(contactSolver.m_velocityConstraints); -} - -void b2Island::Report(const b2ContactVelocityConstraint* constraints) -{ - if (m_listener == nullptr) - { - return; - } - - for (int32 i = 0; i < m_contactCount; ++i) - { - b2Contact* c = m_contacts[i]; - - const b2ContactVelocityConstraint* vc = constraints + i; - - b2ContactImpulse impulse; - impulse.count = vc->pointCount; - for (int32 j = 0; j < vc->pointCount; ++j) - { - impulse.normalImpulses[j] = vc->points[j].normalImpulse; - impulse.tangentImpulses[j] = vc->points[j].tangentImpulse; - } - - m_listener->PostSolve(c, &impulse); - } -} diff --git a/3rdparty/box2d/src/dynamics/b2_island.h b/3rdparty/box2d/src/dynamics/b2_island.h deleted file mode 100644 index 2e28a357b047..000000000000 --- a/3rdparty/box2d/src/dynamics/b2_island.h +++ /dev/null @@ -1,97 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_ISLAND_H -#define B2_ISLAND_H - -#include "box2d/b2_body.h" -#include "box2d/b2_math.h" -#include "box2d/b2_time_step.h" - -class b2Contact; -class b2Joint; -class b2StackAllocator; -class b2ContactListener; -struct b2ContactVelocityConstraint; -struct b2Profile; - -/// This is an internal class. -class b2Island -{ -public: - b2Island(int32 bodyCapacity, int32 contactCapacity, int32 jointCapacity, - b2StackAllocator* allocator, b2ContactListener* listener); - ~b2Island(); - - void Clear() - { - m_bodyCount = 0; - m_contactCount = 0; - m_jointCount = 0; - } - - void Solve(b2Profile* profile, const b2TimeStep& step, const b2Vec2& gravity, bool allowSleep); - - void SolveTOI(const b2TimeStep& subStep, int32 toiIndexA, int32 toiIndexB); - - void Add(b2Body* body) - { - b2Assert(m_bodyCount < m_bodyCapacity); - body->m_islandIndex = m_bodyCount; - m_bodies[m_bodyCount] = body; - ++m_bodyCount; - } - - void Add(b2Contact* contact) - { - b2Assert(m_contactCount < m_contactCapacity); - m_contacts[m_contactCount++] = contact; - } - - void Add(b2Joint* joint) - { - b2Assert(m_jointCount < m_jointCapacity); - m_joints[m_jointCount++] = joint; - } - - void Report(const b2ContactVelocityConstraint* constraints); - - b2StackAllocator* m_allocator; - b2ContactListener* m_listener; - - b2Body** m_bodies; - b2Contact** m_contacts; - b2Joint** m_joints; - - b2Position* m_positions; - b2Velocity* m_velocities; - - int32 m_bodyCount; - int32 m_jointCount; - int32 m_contactCount; - - int32 m_bodyCapacity; - int32 m_contactCapacity; - int32 m_jointCapacity; -}; - -#endif diff --git a/3rdparty/box2d/src/dynamics/b2_joint.cpp b/3rdparty/box2d/src/dynamics/b2_joint.cpp deleted file mode 100644 index 41addbbb2dda..000000000000 --- a/3rdparty/box2d/src/dynamics/b2_joint.cpp +++ /dev/null @@ -1,301 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "box2d/b2_block_allocator.h" -#include "box2d/b2_body.h" -#include "box2d/b2_distance_joint.h" -#include "box2d/b2_draw.h" -#include "box2d/b2_friction_joint.h" -#include "box2d/b2_gear_joint.h" -#include "box2d/b2_motor_joint.h" -#include "box2d/b2_mouse_joint.h" -#include "box2d/b2_prismatic_joint.h" -#include "box2d/b2_pulley_joint.h" -#include "box2d/b2_revolute_joint.h" -#include "box2d/b2_weld_joint.h" -#include "box2d/b2_wheel_joint.h" -#include "box2d/b2_world.h" - -#include - -void b2LinearStiffness(float& stiffness, float& damping, - float frequencyHertz, float dampingRatio, - const b2Body* bodyA, const b2Body* bodyB) -{ - float massA = bodyA->GetMass(); - float massB = bodyB->GetMass(); - float mass; - if (massA > 0.0f && massB > 0.0f) - { - mass = massA * massB / (massA + massB); - } - else if (massA > 0.0f) - { - mass = massA; - } - else - { - mass = massB; - } - - float omega = 2.0f * b2_pi * frequencyHertz; - stiffness = mass * omega * omega; - damping = 2.0f * mass * dampingRatio * omega; -} - -void b2AngularStiffness(float& stiffness, float& damping, - float frequencyHertz, float dampingRatio, - const b2Body* bodyA, const b2Body* bodyB) -{ - float IA = bodyA->GetInertia(); - float IB = bodyB->GetInertia(); - float I; - if (IA > 0.0f && IB > 0.0f) - { - I = IA * IB / (IA + IB); - } - else if (IA > 0.0f) - { - I = IA; - } - else - { - I = IB; - } - - float omega = 2.0f * b2_pi * frequencyHertz; - stiffness = I * omega * omega; - damping = 2.0f * I * dampingRatio * omega; -} - -b2Joint* b2Joint::Create(const b2JointDef* def, b2BlockAllocator* allocator) -{ - b2Joint* joint = nullptr; - - switch (def->type) - { - case e_distanceJoint: - { - void* mem = allocator->Allocate(sizeof(b2DistanceJoint)); - joint = new (mem) b2DistanceJoint(static_cast(def)); - } - break; - - case e_mouseJoint: - { - void* mem = allocator->Allocate(sizeof(b2MouseJoint)); - joint = new (mem) b2MouseJoint(static_cast(def)); - } - break; - - case e_prismaticJoint: - { - void* mem = allocator->Allocate(sizeof(b2PrismaticJoint)); - joint = new (mem) b2PrismaticJoint(static_cast(def)); - } - break; - - case e_revoluteJoint: - { - void* mem = allocator->Allocate(sizeof(b2RevoluteJoint)); - joint = new (mem) b2RevoluteJoint(static_cast(def)); - } - break; - - case e_pulleyJoint: - { - void* mem = allocator->Allocate(sizeof(b2PulleyJoint)); - joint = new (mem) b2PulleyJoint(static_cast(def)); - } - break; - - case e_gearJoint: - { - void* mem = allocator->Allocate(sizeof(b2GearJoint)); - joint = new (mem) b2GearJoint(static_cast(def)); - } - break; - - case e_wheelJoint: - { - void* mem = allocator->Allocate(sizeof(b2WheelJoint)); - joint = new (mem) b2WheelJoint(static_cast(def)); - } - break; - - case e_weldJoint: - { - void* mem = allocator->Allocate(sizeof(b2WeldJoint)); - joint = new (mem) b2WeldJoint(static_cast(def)); - } - break; - - case e_frictionJoint: - { - void* mem = allocator->Allocate(sizeof(b2FrictionJoint)); - joint = new (mem) b2FrictionJoint(static_cast(def)); - } - break; - - case e_motorJoint: - { - void* mem = allocator->Allocate(sizeof(b2MotorJoint)); - joint = new (mem) b2MotorJoint(static_cast(def)); - } - break; - - default: - b2Assert(false); - break; - } - - return joint; -} - -void b2Joint::Destroy(b2Joint* joint, b2BlockAllocator* allocator) -{ - joint->~b2Joint(); - switch (joint->m_type) - { - case e_distanceJoint: - allocator->Free(joint, sizeof(b2DistanceJoint)); - break; - - case e_mouseJoint: - allocator->Free(joint, sizeof(b2MouseJoint)); - break; - - case e_prismaticJoint: - allocator->Free(joint, sizeof(b2PrismaticJoint)); - break; - - case e_revoluteJoint: - allocator->Free(joint, sizeof(b2RevoluteJoint)); - break; - - case e_pulleyJoint: - allocator->Free(joint, sizeof(b2PulleyJoint)); - break; - - case e_gearJoint: - allocator->Free(joint, sizeof(b2GearJoint)); - break; - - case e_wheelJoint: - allocator->Free(joint, sizeof(b2WheelJoint)); - break; - - case e_weldJoint: - allocator->Free(joint, sizeof(b2WeldJoint)); - break; - - case e_frictionJoint: - allocator->Free(joint, sizeof(b2FrictionJoint)); - break; - - case e_motorJoint: - allocator->Free(joint, sizeof(b2MotorJoint)); - break; - - default: - b2Assert(false); - break; - } -} - -b2Joint::b2Joint(const b2JointDef* def) -{ - b2Assert(def->bodyA != def->bodyB); - - m_type = def->type; - m_prev = nullptr; - m_next = nullptr; - m_bodyA = def->bodyA; - m_bodyB = def->bodyB; - m_index = 0; - m_collideConnected = def->collideConnected; - m_islandFlag = false; - m_userData = def->userData; - - m_edgeA.joint = nullptr; - m_edgeA.other = nullptr; - m_edgeA.prev = nullptr; - m_edgeA.next = nullptr; - - m_edgeB.joint = nullptr; - m_edgeB.other = nullptr; - m_edgeB.prev = nullptr; - m_edgeB.next = nullptr; -} - -bool b2Joint::IsEnabled() const -{ - return m_bodyA->IsEnabled() && m_bodyB->IsEnabled(); -} - -void b2Joint::Draw(b2Draw* draw) const -{ - const b2Transform& xf1 = m_bodyA->GetTransform(); - const b2Transform& xf2 = m_bodyB->GetTransform(); - b2Vec2 x1 = xf1.p; - b2Vec2 x2 = xf2.p; - b2Vec2 p1 = GetAnchorA(); - b2Vec2 p2 = GetAnchorB(); - - b2Color color(0.5f, 0.8f, 0.8f); - - switch (m_type) - { - case e_distanceJoint: - draw->DrawSegment(p1, p2, color); - break; - - case e_pulleyJoint: - { - b2PulleyJoint* pulley = (b2PulleyJoint*)this; - b2Vec2 s1 = pulley->GetGroundAnchorA(); - b2Vec2 s2 = pulley->GetGroundAnchorB(); - draw->DrawSegment(s1, p1, color); - draw->DrawSegment(s2, p2, color); - draw->DrawSegment(s1, s2, color); - } - break; - - case e_mouseJoint: - { - b2Color c; - c.Set(0.0f, 1.0f, 0.0f); - draw->DrawPoint(p1, 4.0f, c); - draw->DrawPoint(p2, 4.0f, c); - - c.Set(0.8f, 0.8f, 0.8f); - draw->DrawSegment(p1, p2, c); - - } - break; - - default: - draw->DrawSegment(x1, p1, color); - draw->DrawSegment(p1, p2, color); - draw->DrawSegment(x2, p2, color); - } -} diff --git a/3rdparty/box2d/src/dynamics/b2_motor_joint.cpp b/3rdparty/box2d/src/dynamics/b2_motor_joint.cpp deleted file mode 100644 index 6e0b07538099..000000000000 --- a/3rdparty/box2d/src/dynamics/b2_motor_joint.cpp +++ /dev/null @@ -1,311 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "box2d/b2_body.h" -#include "box2d/b2_motor_joint.h" -#include "box2d/b2_time_step.h" - -// Point-to-point constraint -// Cdot = v2 - v1 -// = v2 + cross(w2, r2) - v1 - cross(w1, r1) -// J = [-I -r1_skew I r2_skew ] -// Identity used: -// w k % (rx i + ry j) = w * (-ry i + rx j) -// -// r1 = offset - c1 -// r2 = -c2 - -// Angle constraint -// Cdot = w2 - w1 -// J = [0 0 -1 0 0 1] -// K = invI1 + invI2 - -void b2MotorJointDef::Initialize(b2Body* bA, b2Body* bB) -{ - bodyA = bA; - bodyB = bB; - b2Vec2 xB = bodyB->GetPosition(); - linearOffset = bodyA->GetLocalPoint(xB); - - float angleA = bodyA->GetAngle(); - float angleB = bodyB->GetAngle(); - angularOffset = angleB - angleA; -} - -b2MotorJoint::b2MotorJoint(const b2MotorJointDef* def) -: b2Joint(def) -{ - m_linearOffset = def->linearOffset; - m_angularOffset = def->angularOffset; - - m_linearImpulse.SetZero(); - m_angularImpulse = 0.0f; - - m_maxForce = def->maxForce; - m_maxTorque = def->maxTorque; - m_correctionFactor = def->correctionFactor; -} - -void b2MotorJoint::InitVelocityConstraints(const b2SolverData& data) -{ - m_indexA = m_bodyA->m_islandIndex; - m_indexB = m_bodyB->m_islandIndex; - m_localCenterA = m_bodyA->m_sweep.localCenter; - m_localCenterB = m_bodyB->m_sweep.localCenter; - m_invMassA = m_bodyA->m_invMass; - m_invMassB = m_bodyB->m_invMass; - m_invIA = m_bodyA->m_invI; - m_invIB = m_bodyB->m_invI; - - b2Vec2 cA = data.positions[m_indexA].c; - float aA = data.positions[m_indexA].a; - b2Vec2 vA = data.velocities[m_indexA].v; - float wA = data.velocities[m_indexA].w; - - b2Vec2 cB = data.positions[m_indexB].c; - float aB = data.positions[m_indexB].a; - b2Vec2 vB = data.velocities[m_indexB].v; - float wB = data.velocities[m_indexB].w; - - b2Rot qA(aA), qB(aB); - - // Compute the effective mass matrix. - m_rA = b2Mul(qA, m_linearOffset - m_localCenterA); - m_rB = b2Mul(qB, -m_localCenterB); - - // J = [-I -r1_skew I r2_skew] - // r_skew = [-ry; rx] - - // Matlab - // K = [ mA+r1y^2*iA+mB+r2y^2*iB, -r1y*iA*r1x-r2y*iB*r2x, -r1y*iA-r2y*iB] - // [ -r1y*iA*r1x-r2y*iB*r2x, mA+r1x^2*iA+mB+r2x^2*iB, r1x*iA+r2x*iB] - // [ -r1y*iA-r2y*iB, r1x*iA+r2x*iB, iA+iB] - - float mA = m_invMassA, mB = m_invMassB; - float iA = m_invIA, iB = m_invIB; - - // Upper 2 by 2 of K for point to point - b2Mat22 K; - K.ex.x = mA + mB + iA * m_rA.y * m_rA.y + iB * m_rB.y * m_rB.y; - K.ex.y = -iA * m_rA.x * m_rA.y - iB * m_rB.x * m_rB.y; - K.ey.x = K.ex.y; - K.ey.y = mA + mB + iA * m_rA.x * m_rA.x + iB * m_rB.x * m_rB.x; - - m_linearMass = K.GetInverse(); - - m_angularMass = iA + iB; - if (m_angularMass > 0.0f) - { - m_angularMass = 1.0f / m_angularMass; - } - - m_linearError = cB + m_rB - cA - m_rA; - m_angularError = aB - aA - m_angularOffset; - - if (data.step.warmStarting) - { - // Scale impulses to support a variable time step. - m_linearImpulse *= data.step.dtRatio; - m_angularImpulse *= data.step.dtRatio; - - b2Vec2 P(m_linearImpulse.x, m_linearImpulse.y); - vA -= mA * P; - wA -= iA * (b2Cross(m_rA, P) + m_angularImpulse); - vB += mB * P; - wB += iB * (b2Cross(m_rB, P) + m_angularImpulse); - } - else - { - m_linearImpulse.SetZero(); - m_angularImpulse = 0.0f; - } - - data.velocities[m_indexA].v = vA; - data.velocities[m_indexA].w = wA; - data.velocities[m_indexB].v = vB; - data.velocities[m_indexB].w = wB; -} - -void b2MotorJoint::SolveVelocityConstraints(const b2SolverData& data) -{ - b2Vec2 vA = data.velocities[m_indexA].v; - float wA = data.velocities[m_indexA].w; - b2Vec2 vB = data.velocities[m_indexB].v; - float wB = data.velocities[m_indexB].w; - - float mA = m_invMassA, mB = m_invMassB; - float iA = m_invIA, iB = m_invIB; - - float h = data.step.dt; - float inv_h = data.step.inv_dt; - - // Solve angular friction - { - float Cdot = wB - wA + inv_h * m_correctionFactor * m_angularError; - float impulse = -m_angularMass * Cdot; - - float oldImpulse = m_angularImpulse; - float maxImpulse = h * m_maxTorque; - m_angularImpulse = b2Clamp(m_angularImpulse + impulse, -maxImpulse, maxImpulse); - impulse = m_angularImpulse - oldImpulse; - - wA -= iA * impulse; - wB += iB * impulse; - } - - // Solve linear friction - { - b2Vec2 Cdot = vB + b2Cross(wB, m_rB) - vA - b2Cross(wA, m_rA) + inv_h * m_correctionFactor * m_linearError; - - b2Vec2 impulse = -b2Mul(m_linearMass, Cdot); - b2Vec2 oldImpulse = m_linearImpulse; - m_linearImpulse += impulse; - - float maxImpulse = h * m_maxForce; - - if (m_linearImpulse.LengthSquared() > maxImpulse * maxImpulse) - { - m_linearImpulse.Normalize(); - m_linearImpulse *= maxImpulse; - } - - impulse = m_linearImpulse - oldImpulse; - - vA -= mA * impulse; - wA -= iA * b2Cross(m_rA, impulse); - - vB += mB * impulse; - wB += iB * b2Cross(m_rB, impulse); - } - - data.velocities[m_indexA].v = vA; - data.velocities[m_indexA].w = wA; - data.velocities[m_indexB].v = vB; - data.velocities[m_indexB].w = wB; -} - -bool b2MotorJoint::SolvePositionConstraints(const b2SolverData& data) -{ - B2_NOT_USED(data); - - return true; -} - -b2Vec2 b2MotorJoint::GetAnchorA() const -{ - return m_bodyA->GetPosition(); -} - -b2Vec2 b2MotorJoint::GetAnchorB() const -{ - return m_bodyB->GetPosition(); -} - -b2Vec2 b2MotorJoint::GetReactionForce(float inv_dt) const -{ - return inv_dt * m_linearImpulse; -} - -float b2MotorJoint::GetReactionTorque(float inv_dt) const -{ - return inv_dt * m_angularImpulse; -} - -void b2MotorJoint::SetMaxForce(float force) -{ - b2Assert(b2IsValid(force) && force >= 0.0f); - m_maxForce = force; -} - -float b2MotorJoint::GetMaxForce() const -{ - return m_maxForce; -} - -void b2MotorJoint::SetMaxTorque(float torque) -{ - b2Assert(b2IsValid(torque) && torque >= 0.0f); - m_maxTorque = torque; -} - -float b2MotorJoint::GetMaxTorque() const -{ - return m_maxTorque; -} - -void b2MotorJoint::SetCorrectionFactor(float factor) -{ - b2Assert(b2IsValid(factor) && 0.0f <= factor && factor <= 1.0f); - m_correctionFactor = factor; -} - -float b2MotorJoint::GetCorrectionFactor() const -{ - return m_correctionFactor; -} - -void b2MotorJoint::SetLinearOffset(const b2Vec2& linearOffset) -{ - if (linearOffset.x != m_linearOffset.x || linearOffset.y != m_linearOffset.y) - { - m_bodyA->SetAwake(true); - m_bodyB->SetAwake(true); - m_linearOffset = linearOffset; - } -} - -const b2Vec2& b2MotorJoint::GetLinearOffset() const -{ - return m_linearOffset; -} - -void b2MotorJoint::SetAngularOffset(float angularOffset) -{ - if (angularOffset != m_angularOffset) - { - m_bodyA->SetAwake(true); - m_bodyB->SetAwake(true); - m_angularOffset = angularOffset; - } -} - -float b2MotorJoint::GetAngularOffset() const -{ - return m_angularOffset; -} - -void b2MotorJoint::Dump() -{ - int32 indexA = m_bodyA->m_islandIndex; - int32 indexB = m_bodyB->m_islandIndex; - - b2Dump(" b2MotorJointDef jd;\n"); - b2Dump(" jd.bodyA = bodies[%d];\n", indexA); - b2Dump(" jd.bodyB = bodies[%d];\n", indexB); - b2Dump(" jd.collideConnected = bool(%d);\n", m_collideConnected); - b2Dump(" jd.linearOffset.Set(%.9g, %.9g);\n", m_linearOffset.x, m_linearOffset.y); - b2Dump(" jd.angularOffset = %.9g;\n", m_angularOffset); - b2Dump(" jd.maxForce = %.9g;\n", m_maxForce); - b2Dump(" jd.maxTorque = %.9g;\n", m_maxTorque); - b2Dump(" jd.correctionFactor = %.9g;\n", m_correctionFactor); - b2Dump(" joints[%d] = m_world->CreateJoint(&jd);\n", m_index); -} diff --git a/3rdparty/box2d/src/dynamics/b2_mouse_joint.cpp b/3rdparty/box2d/src/dynamics/b2_mouse_joint.cpp deleted file mode 100644 index dbc214fae749..000000000000 --- a/3rdparty/box2d/src/dynamics/b2_mouse_joint.cpp +++ /dev/null @@ -1,190 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "box2d/b2_body.h" -#include "box2d/b2_mouse_joint.h" -#include "box2d/b2_time_step.h" - -// p = attached point, m = mouse point -// C = p - m -// Cdot = v -// = v + cross(w, r) -// J = [I r_skew] -// Identity used: -// w k % (rx i + ry j) = w * (-ry i + rx j) - -b2MouseJoint::b2MouseJoint(const b2MouseJointDef* def) -: b2Joint(def) -{ - m_targetA = def->target; - m_localAnchorB = b2MulT(m_bodyB->GetTransform(), m_targetA); - m_maxForce = def->maxForce; - m_stiffness = def->stiffness; - m_damping = def->damping; - - m_impulse.SetZero(); - m_beta = 0.0f; - m_gamma = 0.0f; -} - -void b2MouseJoint::SetTarget(const b2Vec2& target) -{ - if (target != m_targetA) - { - m_bodyB->SetAwake(true); - m_targetA = target; - } -} - -const b2Vec2& b2MouseJoint::GetTarget() const -{ - return m_targetA; -} - -void b2MouseJoint::SetMaxForce(float force) -{ - m_maxForce = force; -} - -float b2MouseJoint::GetMaxForce() const -{ - return m_maxForce; -} - -void b2MouseJoint::InitVelocityConstraints(const b2SolverData& data) -{ - m_indexB = m_bodyB->m_islandIndex; - m_localCenterB = m_bodyB->m_sweep.localCenter; - m_invMassB = m_bodyB->m_invMass; - m_invIB = m_bodyB->m_invI; - - b2Vec2 cB = data.positions[m_indexB].c; - float aB = data.positions[m_indexB].a; - b2Vec2 vB = data.velocities[m_indexB].v; - float wB = data.velocities[m_indexB].w; - - b2Rot qB(aB); - - float d = m_damping; - float k = m_stiffness; - - // magic formulas - // gamma has units of inverse mass. - // beta has units of inverse time. - float h = data.step.dt; - m_gamma = h * (d + h * k); - if (m_gamma != 0.0f) - { - m_gamma = 1.0f / m_gamma; - } - m_beta = h * k * m_gamma; - - // Compute the effective mass matrix. - m_rB = b2Mul(qB, m_localAnchorB - m_localCenterB); - - // K = [(1/m1 + 1/m2) * eye(2) - skew(r1) * invI1 * skew(r1) - skew(r2) * invI2 * skew(r2)] - // = [1/m1+1/m2 0 ] + invI1 * [r1.y*r1.y -r1.x*r1.y] + invI2 * [r1.y*r1.y -r1.x*r1.y] - // [ 0 1/m1+1/m2] [-r1.x*r1.y r1.x*r1.x] [-r1.x*r1.y r1.x*r1.x] - b2Mat22 K; - K.ex.x = m_invMassB + m_invIB * m_rB.y * m_rB.y + m_gamma; - K.ex.y = -m_invIB * m_rB.x * m_rB.y; - K.ey.x = K.ex.y; - K.ey.y = m_invMassB + m_invIB * m_rB.x * m_rB.x + m_gamma; - - m_mass = K.GetInverse(); - - m_C = cB + m_rB - m_targetA; - m_C *= m_beta; - - // Cheat with some damping - wB *= b2Max(0.0f, 1.0f - 0.02f * (60.0f * data.step.dt)); - - if (data.step.warmStarting) - { - m_impulse *= data.step.dtRatio; - vB += m_invMassB * m_impulse; - wB += m_invIB * b2Cross(m_rB, m_impulse); - } - else - { - m_impulse.SetZero(); - } - - data.velocities[m_indexB].v = vB; - data.velocities[m_indexB].w = wB; -} - -void b2MouseJoint::SolveVelocityConstraints(const b2SolverData& data) -{ - b2Vec2 vB = data.velocities[m_indexB].v; - float wB = data.velocities[m_indexB].w; - - // Cdot = v + cross(w, r) - b2Vec2 Cdot = vB + b2Cross(wB, m_rB); - b2Vec2 impulse = b2Mul(m_mass, -(Cdot + m_C + m_gamma * m_impulse)); - - b2Vec2 oldImpulse = m_impulse; - m_impulse += impulse; - float maxImpulse = data.step.dt * m_maxForce; - if (m_impulse.LengthSquared() > maxImpulse * maxImpulse) - { - m_impulse *= maxImpulse / m_impulse.Length(); - } - impulse = m_impulse - oldImpulse; - - vB += m_invMassB * impulse; - wB += m_invIB * b2Cross(m_rB, impulse); - - data.velocities[m_indexB].v = vB; - data.velocities[m_indexB].w = wB; -} - -bool b2MouseJoint::SolvePositionConstraints(const b2SolverData& data) -{ - B2_NOT_USED(data); - return true; -} - -b2Vec2 b2MouseJoint::GetAnchorA() const -{ - return m_targetA; -} - -b2Vec2 b2MouseJoint::GetAnchorB() const -{ - return m_bodyB->GetWorldPoint(m_localAnchorB); -} - -b2Vec2 b2MouseJoint::GetReactionForce(float inv_dt) const -{ - return inv_dt * m_impulse; -} - -float b2MouseJoint::GetReactionTorque(float inv_dt) const -{ - return inv_dt * 0.0f; -} - -void b2MouseJoint::ShiftOrigin(const b2Vec2& newOrigin) -{ - m_targetA -= newOrigin; -} diff --git a/3rdparty/box2d/src/dynamics/b2_polygon_circle_contact.cpp b/3rdparty/box2d/src/dynamics/b2_polygon_circle_contact.cpp deleted file mode 100644 index e4f34f582f74..000000000000 --- a/3rdparty/box2d/src/dynamics/b2_polygon_circle_contact.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "b2_polygon_circle_contact.h" - -#include "box2d/b2_block_allocator.h" -#include "box2d/b2_fixture.h" - -#include - -b2Contact* b2PolygonAndCircleContact::Create(b2Fixture* fixtureA, int32, b2Fixture* fixtureB, int32, b2BlockAllocator* allocator) -{ - void* mem = allocator->Allocate(sizeof(b2PolygonAndCircleContact)); - return new (mem) b2PolygonAndCircleContact(fixtureA, fixtureB); -} - -void b2PolygonAndCircleContact::Destroy(b2Contact* contact, b2BlockAllocator* allocator) -{ - ((b2PolygonAndCircleContact*)contact)->~b2PolygonAndCircleContact(); - allocator->Free(contact, sizeof(b2PolygonAndCircleContact)); -} - -b2PolygonAndCircleContact::b2PolygonAndCircleContact(b2Fixture* fixtureA, b2Fixture* fixtureB) -: b2Contact(fixtureA, 0, fixtureB, 0) -{ - b2Assert(m_fixtureA->GetType() == b2Shape::e_polygon); - b2Assert(m_fixtureB->GetType() == b2Shape::e_circle); -} - -void b2PolygonAndCircleContact::Evaluate(b2Manifold* manifold, const b2Transform& xfA, const b2Transform& xfB) -{ - b2CollidePolygonAndCircle( manifold, - (b2PolygonShape*)m_fixtureA->GetShape(), xfA, - (b2CircleShape*)m_fixtureB->GetShape(), xfB); -} diff --git a/3rdparty/box2d/src/dynamics/b2_polygon_circle_contact.h b/3rdparty/box2d/src/dynamics/b2_polygon_circle_contact.h deleted file mode 100644 index 6ae542510cb4..000000000000 --- a/3rdparty/box2d/src/dynamics/b2_polygon_circle_contact.h +++ /dev/null @@ -1,42 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_POLYGON_AND_CIRCLE_CONTACT_H -#define B2_POLYGON_AND_CIRCLE_CONTACT_H - -#include "box2d/b2_contact.h" - -class b2BlockAllocator; - -class b2PolygonAndCircleContact : public b2Contact -{ -public: - static b2Contact* Create(b2Fixture* fixtureA, int32 indexA, b2Fixture* fixtureB, int32 indexB, b2BlockAllocator* allocator); - static void Destroy(b2Contact* contact, b2BlockAllocator* allocator); - - b2PolygonAndCircleContact(b2Fixture* fixtureA, b2Fixture* fixtureB); - ~b2PolygonAndCircleContact() {} - - void Evaluate(b2Manifold* manifold, const b2Transform& xfA, const b2Transform& xfB) override; -}; - -#endif diff --git a/3rdparty/box2d/src/dynamics/b2_polygon_contact.cpp b/3rdparty/box2d/src/dynamics/b2_polygon_contact.cpp deleted file mode 100644 index e92a9e806318..000000000000 --- a/3rdparty/box2d/src/dynamics/b2_polygon_contact.cpp +++ /dev/null @@ -1,57 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "b2_polygon_contact.h" - -#include "box2d/b2_block_allocator.h" -#include "box2d/b2_body.h" -#include "box2d/b2_fixture.h" -#include "box2d/b2_time_of_impact.h" -#include "box2d/b2_world_callbacks.h" - -#include - -b2Contact* b2PolygonContact::Create(b2Fixture* fixtureA, int32, b2Fixture* fixtureB, int32, b2BlockAllocator* allocator) -{ - void* mem = allocator->Allocate(sizeof(b2PolygonContact)); - return new (mem) b2PolygonContact(fixtureA, fixtureB); -} - -void b2PolygonContact::Destroy(b2Contact* contact, b2BlockAllocator* allocator) -{ - ((b2PolygonContact*)contact)->~b2PolygonContact(); - allocator->Free(contact, sizeof(b2PolygonContact)); -} - -b2PolygonContact::b2PolygonContact(b2Fixture* fixtureA, b2Fixture* fixtureB) - : b2Contact(fixtureA, 0, fixtureB, 0) -{ - b2Assert(m_fixtureA->GetType() == b2Shape::e_polygon); - b2Assert(m_fixtureB->GetType() == b2Shape::e_polygon); -} - -void b2PolygonContact::Evaluate(b2Manifold* manifold, const b2Transform& xfA, const b2Transform& xfB) -{ - b2CollidePolygons( manifold, - (b2PolygonShape*)m_fixtureA->GetShape(), xfA, - (b2PolygonShape*)m_fixtureB->GetShape(), xfB); -} diff --git a/3rdparty/box2d/src/dynamics/b2_polygon_contact.h b/3rdparty/box2d/src/dynamics/b2_polygon_contact.h deleted file mode 100644 index 0516cb0ec623..000000000000 --- a/3rdparty/box2d/src/dynamics/b2_polygon_contact.h +++ /dev/null @@ -1,43 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef B2_POLYGON_CONTACT_H -#define B2_POLYGON_CONTACT_H - -#include "box2d/b2_contact.h" - -class b2BlockAllocator; - -class b2PolygonContact : public b2Contact -{ -public: - static b2Contact* Create( b2Fixture* fixtureA, int32 indexA, - b2Fixture* fixtureB, int32 indexB, b2BlockAllocator* allocator); - static void Destroy(b2Contact* contact, b2BlockAllocator* allocator); - - b2PolygonContact(b2Fixture* fixtureA, b2Fixture* fixtureB); - ~b2PolygonContact() {} - - void Evaluate(b2Manifold* manifold, const b2Transform& xfA, const b2Transform& xfB) override; -}; - -#endif diff --git a/3rdparty/box2d/src/dynamics/b2_prismatic_joint.cpp b/3rdparty/box2d/src/dynamics/b2_prismatic_joint.cpp deleted file mode 100644 index 00e77690eb6b..000000000000 --- a/3rdparty/box2d/src/dynamics/b2_prismatic_joint.cpp +++ /dev/null @@ -1,643 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "box2d/b2_body.h" -#include "box2d/b2_draw.h" -#include "box2d/b2_prismatic_joint.h" -#include "box2d/b2_time_step.h" - -// Linear constraint (point-to-line) -// d = p2 - p1 = x2 + r2 - x1 - r1 -// C = dot(perp, d) -// Cdot = dot(d, cross(w1, perp)) + dot(perp, v2 + cross(w2, r2) - v1 - cross(w1, r1)) -// = -dot(perp, v1) - dot(cross(d + r1, perp), w1) + dot(perp, v2) + dot(cross(r2, perp), v2) -// J = [-perp, -cross(d + r1, perp), perp, cross(r2,perp)] -// -// Angular constraint -// C = a2 - a1 + a_initial -// Cdot = w2 - w1 -// J = [0 0 -1 0 0 1] -// -// K = J * invM * JT -// -// J = [-a -s1 a s2] -// [0 -1 0 1] -// a = perp -// s1 = cross(d + r1, a) = cross(p2 - x1, a) -// s2 = cross(r2, a) = cross(p2 - x2, a) - -// Motor/Limit linear constraint -// C = dot(ax1, d) -// Cdot = -dot(ax1, v1) - dot(cross(d + r1, ax1), w1) + dot(ax1, v2) + dot(cross(r2, ax1), v2) -// J = [-ax1 -cross(d+r1,ax1) ax1 cross(r2,ax1)] - -// Predictive limit is applied even when the limit is not active. -// Prevents a constraint speed that can lead to a constraint error in one time step. -// Want C2 = C1 + h * Cdot >= 0 -// Or: -// Cdot + C1/h >= 0 -// I do not apply a negative constraint error because that is handled in position correction. -// So: -// Cdot + max(C1, 0)/h >= 0 - -// Block Solver -// We develop a block solver that includes the angular and linear constraints. This makes the limit stiffer. -// -// The Jacobian has 2 rows: -// J = [-uT -s1 uT s2] // linear -// [0 -1 0 1] // angular -// -// u = perp -// s1 = cross(d + r1, u), s2 = cross(r2, u) -// a1 = cross(d + r1, v), a2 = cross(r2, v) - -void b2PrismaticJointDef::Initialize(b2Body* bA, b2Body* bB, const b2Vec2& anchor, const b2Vec2& axis) -{ - bodyA = bA; - bodyB = bB; - localAnchorA = bodyA->GetLocalPoint(anchor); - localAnchorB = bodyB->GetLocalPoint(anchor); - localAxisA = bodyA->GetLocalVector(axis); - referenceAngle = bodyB->GetAngle() - bodyA->GetAngle(); -} - -b2PrismaticJoint::b2PrismaticJoint(const b2PrismaticJointDef* def) -: b2Joint(def) -{ - m_localAnchorA = def->localAnchorA; - m_localAnchorB = def->localAnchorB; - m_localXAxisA = def->localAxisA; - m_localXAxisA.Normalize(); - m_localYAxisA = b2Cross(1.0f, m_localXAxisA); - m_referenceAngle = def->referenceAngle; - - m_impulse.SetZero(); - m_axialMass = 0.0f; - m_motorImpulse = 0.0f; - m_lowerImpulse = 0.0f; - m_upperImpulse = 0.0f; - - m_lowerTranslation = def->lowerTranslation; - m_upperTranslation = def->upperTranslation; - - b2Assert(m_lowerTranslation <= m_upperTranslation); - - m_maxMotorForce = def->maxMotorForce; - m_motorSpeed = def->motorSpeed; - m_enableLimit = def->enableLimit; - m_enableMotor = def->enableMotor; - - m_translation = 0.0f; - m_axis.SetZero(); - m_perp.SetZero(); -} - -void b2PrismaticJoint::InitVelocityConstraints(const b2SolverData& data) -{ - m_indexA = m_bodyA->m_islandIndex; - m_indexB = m_bodyB->m_islandIndex; - m_localCenterA = m_bodyA->m_sweep.localCenter; - m_localCenterB = m_bodyB->m_sweep.localCenter; - m_invMassA = m_bodyA->m_invMass; - m_invMassB = m_bodyB->m_invMass; - m_invIA = m_bodyA->m_invI; - m_invIB = m_bodyB->m_invI; - - b2Vec2 cA = data.positions[m_indexA].c; - float aA = data.positions[m_indexA].a; - b2Vec2 vA = data.velocities[m_indexA].v; - float wA = data.velocities[m_indexA].w; - - b2Vec2 cB = data.positions[m_indexB].c; - float aB = data.positions[m_indexB].a; - b2Vec2 vB = data.velocities[m_indexB].v; - float wB = data.velocities[m_indexB].w; - - b2Rot qA(aA), qB(aB); - - // Compute the effective masses. - b2Vec2 rA = b2Mul(qA, m_localAnchorA - m_localCenterA); - b2Vec2 rB = b2Mul(qB, m_localAnchorB - m_localCenterB); - b2Vec2 d = (cB - cA) + rB - rA; - - float mA = m_invMassA, mB = m_invMassB; - float iA = m_invIA, iB = m_invIB; - - // Compute motor Jacobian and effective mass. - { - m_axis = b2Mul(qA, m_localXAxisA); - m_a1 = b2Cross(d + rA, m_axis); - m_a2 = b2Cross(rB, m_axis); - - m_axialMass = mA + mB + iA * m_a1 * m_a1 + iB * m_a2 * m_a2; - if (m_axialMass > 0.0f) - { - m_axialMass = 1.0f / m_axialMass; - } - } - - // Prismatic constraint. - { - m_perp = b2Mul(qA, m_localYAxisA); - - m_s1 = b2Cross(d + rA, m_perp); - m_s2 = b2Cross(rB, m_perp); - - float k11 = mA + mB + iA * m_s1 * m_s1 + iB * m_s2 * m_s2; - float k12 = iA * m_s1 + iB * m_s2; - float k22 = iA + iB; - if (k22 == 0.0f) - { - // For bodies with fixed rotation. - k22 = 1.0f; - } - - m_K.ex.Set(k11, k12); - m_K.ey.Set(k12, k22); - } - - if (m_enableLimit) - { - m_translation = b2Dot(m_axis, d); - } - else - { - m_lowerImpulse = 0.0f; - m_upperImpulse = 0.0f; - } - - if (m_enableMotor == false) - { - m_motorImpulse = 0.0f; - } - - if (data.step.warmStarting) - { - // Account for variable time step. - m_impulse *= data.step.dtRatio; - m_motorImpulse *= data.step.dtRatio; - m_lowerImpulse *= data.step.dtRatio; - m_upperImpulse *= data.step.dtRatio; - - float axialImpulse = m_motorImpulse + m_lowerImpulse - m_upperImpulse; - b2Vec2 P = m_impulse.x * m_perp + axialImpulse * m_axis; - float LA = m_impulse.x * m_s1 + m_impulse.y + axialImpulse * m_a1; - float LB = m_impulse.x * m_s2 + m_impulse.y + axialImpulse * m_a2; - - vA -= mA * P; - wA -= iA * LA; - - vB += mB * P; - wB += iB * LB; - } - else - { - m_impulse.SetZero(); - m_motorImpulse = 0.0f; - m_lowerImpulse = 0.0f; - m_upperImpulse = 0.0f; - } - - data.velocities[m_indexA].v = vA; - data.velocities[m_indexA].w = wA; - data.velocities[m_indexB].v = vB; - data.velocities[m_indexB].w = wB; -} - -void b2PrismaticJoint::SolveVelocityConstraints(const b2SolverData& data) -{ - b2Vec2 vA = data.velocities[m_indexA].v; - float wA = data.velocities[m_indexA].w; - b2Vec2 vB = data.velocities[m_indexB].v; - float wB = data.velocities[m_indexB].w; - - float mA = m_invMassA, mB = m_invMassB; - float iA = m_invIA, iB = m_invIB; - - // Solve linear motor constraint - if (m_enableMotor) - { - float Cdot = b2Dot(m_axis, vB - vA) + m_a2 * wB - m_a1 * wA; - float impulse = m_axialMass * (m_motorSpeed - Cdot); - float oldImpulse = m_motorImpulse; - float maxImpulse = data.step.dt * m_maxMotorForce; - m_motorImpulse = b2Clamp(m_motorImpulse + impulse, -maxImpulse, maxImpulse); - impulse = m_motorImpulse - oldImpulse; - - b2Vec2 P = impulse * m_axis; - float LA = impulse * m_a1; - float LB = impulse * m_a2; - - vA -= mA * P; - wA -= iA * LA; - vB += mB * P; - wB += iB * LB; - } - - if (m_enableLimit) - { - // Lower limit - { - float C = m_translation - m_lowerTranslation; - float Cdot = b2Dot(m_axis, vB - vA) + m_a2 * wB - m_a1 * wA; - float impulse = -m_axialMass * (Cdot + b2Max(C, 0.0f) * data.step.inv_dt); - float oldImpulse = m_lowerImpulse; - m_lowerImpulse = b2Max(m_lowerImpulse + impulse, 0.0f); - impulse = m_lowerImpulse - oldImpulse; - - b2Vec2 P = impulse * m_axis; - float LA = impulse * m_a1; - float LB = impulse * m_a2; - - vA -= mA * P; - wA -= iA * LA; - vB += mB * P; - wB += iB * LB; - } - - // Upper limit - // Note: signs are flipped to keep C positive when the constraint is satisfied. - // This also keeps the impulse positive when the limit is active. - { - float C = m_upperTranslation - m_translation; - float Cdot = b2Dot(m_axis, vA - vB) + m_a1 * wA - m_a2 * wB; - float impulse = -m_axialMass * (Cdot + b2Max(C, 0.0f) * data.step.inv_dt); - float oldImpulse = m_upperImpulse; - m_upperImpulse = b2Max(m_upperImpulse + impulse, 0.0f); - impulse = m_upperImpulse - oldImpulse; - - b2Vec2 P = impulse * m_axis; - float LA = impulse * m_a1; - float LB = impulse * m_a2; - - vA += mA * P; - wA += iA * LA; - vB -= mB * P; - wB -= iB * LB; - } - } - - // Solve the prismatic constraint in block form. - { - b2Vec2 Cdot; - Cdot.x = b2Dot(m_perp, vB - vA) + m_s2 * wB - m_s1 * wA; - Cdot.y = wB - wA; - - b2Vec2 df = m_K.Solve(-Cdot); - m_impulse += df; - - b2Vec2 P = df.x * m_perp; - float LA = df.x * m_s1 + df.y; - float LB = df.x * m_s2 + df.y; - - vA -= mA * P; - wA -= iA * LA; - - vB += mB * P; - wB += iB * LB; - } - - data.velocities[m_indexA].v = vA; - data.velocities[m_indexA].w = wA; - data.velocities[m_indexB].v = vB; - data.velocities[m_indexB].w = wB; -} - -// A velocity based solver computes reaction forces(impulses) using the velocity constraint solver.Under this context, -// the position solver is not there to resolve forces.It is only there to cope with integration error. -// -// Therefore, the pseudo impulses in the position solver do not have any physical meaning.Thus it is okay if they suck. -// -// We could take the active state from the velocity solver.However, the joint might push past the limit when the velocity -// solver indicates the limit is inactive. -bool b2PrismaticJoint::SolvePositionConstraints(const b2SolverData& data) -{ - b2Vec2 cA = data.positions[m_indexA].c; - float aA = data.positions[m_indexA].a; - b2Vec2 cB = data.positions[m_indexB].c; - float aB = data.positions[m_indexB].a; - - b2Rot qA(aA), qB(aB); - - float mA = m_invMassA, mB = m_invMassB; - float iA = m_invIA, iB = m_invIB; - - // Compute fresh Jacobians - b2Vec2 rA = b2Mul(qA, m_localAnchorA - m_localCenterA); - b2Vec2 rB = b2Mul(qB, m_localAnchorB - m_localCenterB); - b2Vec2 d = cB + rB - cA - rA; - - b2Vec2 axis = b2Mul(qA, m_localXAxisA); - float a1 = b2Cross(d + rA, axis); - float a2 = b2Cross(rB, axis); - b2Vec2 perp = b2Mul(qA, m_localYAxisA); - - float s1 = b2Cross(d + rA, perp); - float s2 = b2Cross(rB, perp); - - b2Vec3 impulse; - b2Vec2 C1; - C1.x = b2Dot(perp, d); - C1.y = aB - aA - m_referenceAngle; - - float linearError = b2Abs(C1.x); - float angularError = b2Abs(C1.y); - - bool active = false; - float C2 = 0.0f; - if (m_enableLimit) - { - float translation = b2Dot(axis, d); - if (b2Abs(m_upperTranslation - m_lowerTranslation) < 2.0f * b2_linearSlop) - { - C2 = translation; - linearError = b2Max(linearError, b2Abs(translation)); - active = true; - } - else if (translation <= m_lowerTranslation) - { - C2 = b2Min(translation - m_lowerTranslation, 0.0f); - linearError = b2Max(linearError, m_lowerTranslation - translation); - active = true; - } - else if (translation >= m_upperTranslation) - { - C2 = b2Max(translation - m_upperTranslation, 0.0f); - linearError = b2Max(linearError, translation - m_upperTranslation); - active = true; - } - } - - if (active) - { - float k11 = mA + mB + iA * s1 * s1 + iB * s2 * s2; - float k12 = iA * s1 + iB * s2; - float k13 = iA * s1 * a1 + iB * s2 * a2; - float k22 = iA + iB; - if (k22 == 0.0f) - { - // For fixed rotation - k22 = 1.0f; - } - float k23 = iA * a1 + iB * a2; - float k33 = mA + mB + iA * a1 * a1 + iB * a2 * a2; - - b2Mat33 K; - K.ex.Set(k11, k12, k13); - K.ey.Set(k12, k22, k23); - K.ez.Set(k13, k23, k33); - - b2Vec3 C; - C.x = C1.x; - C.y = C1.y; - C.z = C2; - - impulse = K.Solve33(-C); - } - else - { - float k11 = mA + mB + iA * s1 * s1 + iB * s2 * s2; - float k12 = iA * s1 + iB * s2; - float k22 = iA + iB; - if (k22 == 0.0f) - { - k22 = 1.0f; - } - - b2Mat22 K; - K.ex.Set(k11, k12); - K.ey.Set(k12, k22); - - b2Vec2 impulse1 = K.Solve(-C1); - impulse.x = impulse1.x; - impulse.y = impulse1.y; - impulse.z = 0.0f; - } - - b2Vec2 P = impulse.x * perp + impulse.z * axis; - float LA = impulse.x * s1 + impulse.y + impulse.z * a1; - float LB = impulse.x * s2 + impulse.y + impulse.z * a2; - - cA -= mA * P; - aA -= iA * LA; - cB += mB * P; - aB += iB * LB; - - data.positions[m_indexA].c = cA; - data.positions[m_indexA].a = aA; - data.positions[m_indexB].c = cB; - data.positions[m_indexB].a = aB; - - return linearError <= b2_linearSlop && angularError <= b2_angularSlop; -} - -b2Vec2 b2PrismaticJoint::GetAnchorA() const -{ - return m_bodyA->GetWorldPoint(m_localAnchorA); -} - -b2Vec2 b2PrismaticJoint::GetAnchorB() const -{ - return m_bodyB->GetWorldPoint(m_localAnchorB); -} - -b2Vec2 b2PrismaticJoint::GetReactionForce(float inv_dt) const -{ - return inv_dt * (m_impulse.x * m_perp + (m_motorImpulse + m_lowerImpulse - m_upperImpulse) * m_axis); -} - -float b2PrismaticJoint::GetReactionTorque(float inv_dt) const -{ - return inv_dt * m_impulse.y; -} - -float b2PrismaticJoint::GetJointTranslation() const -{ - b2Vec2 pA = m_bodyA->GetWorldPoint(m_localAnchorA); - b2Vec2 pB = m_bodyB->GetWorldPoint(m_localAnchorB); - b2Vec2 d = pB - pA; - b2Vec2 axis = m_bodyA->GetWorldVector(m_localXAxisA); - - float translation = b2Dot(d, axis); - return translation; -} - -float b2PrismaticJoint::GetJointSpeed() const -{ - b2Body* bA = m_bodyA; - b2Body* bB = m_bodyB; - - b2Vec2 rA = b2Mul(bA->m_xf.q, m_localAnchorA - bA->m_sweep.localCenter); - b2Vec2 rB = b2Mul(bB->m_xf.q, m_localAnchorB - bB->m_sweep.localCenter); - b2Vec2 p1 = bA->m_sweep.c + rA; - b2Vec2 p2 = bB->m_sweep.c + rB; - b2Vec2 d = p2 - p1; - b2Vec2 axis = b2Mul(bA->m_xf.q, m_localXAxisA); - - b2Vec2 vA = bA->m_linearVelocity; - b2Vec2 vB = bB->m_linearVelocity; - float wA = bA->m_angularVelocity; - float wB = bB->m_angularVelocity; - - float speed = b2Dot(d, b2Cross(wA, axis)) + b2Dot(axis, vB + b2Cross(wB, rB) - vA - b2Cross(wA, rA)); - return speed; -} - -bool b2PrismaticJoint::IsLimitEnabled() const -{ - return m_enableLimit; -} - -void b2PrismaticJoint::EnableLimit(bool flag) -{ - if (flag != m_enableLimit) - { - m_bodyA->SetAwake(true); - m_bodyB->SetAwake(true); - m_enableLimit = flag; - m_lowerImpulse = 0.0f; - m_upperImpulse = 0.0f; - } -} - -float b2PrismaticJoint::GetLowerLimit() const -{ - return m_lowerTranslation; -} - -float b2PrismaticJoint::GetUpperLimit() const -{ - return m_upperTranslation; -} - -void b2PrismaticJoint::SetLimits(float lower, float upper) -{ - b2Assert(lower <= upper); - if (lower != m_lowerTranslation || upper != m_upperTranslation) - { - m_bodyA->SetAwake(true); - m_bodyB->SetAwake(true); - m_lowerTranslation = lower; - m_upperTranslation = upper; - m_lowerImpulse = 0.0f; - m_upperImpulse = 0.0f; - } -} - -bool b2PrismaticJoint::IsMotorEnabled() const -{ - return m_enableMotor; -} - -void b2PrismaticJoint::EnableMotor(bool flag) -{ - if (flag != m_enableMotor) - { - m_bodyA->SetAwake(true); - m_bodyB->SetAwake(true); - m_enableMotor = flag; - } -} - -void b2PrismaticJoint::SetMotorSpeed(float speed) -{ - if (speed != m_motorSpeed) - { - m_bodyA->SetAwake(true); - m_bodyB->SetAwake(true); - m_motorSpeed = speed; - } -} - -void b2PrismaticJoint::SetMaxMotorForce(float force) -{ - if (force != m_maxMotorForce) - { - m_bodyA->SetAwake(true); - m_bodyB->SetAwake(true); - m_maxMotorForce = force; - } -} - -float b2PrismaticJoint::GetMotorForce(float inv_dt) const -{ - return inv_dt * m_motorImpulse; -} - -void b2PrismaticJoint::Dump() -{ - // FLT_DECIMAL_DIG == 9 - - int32 indexA = m_bodyA->m_islandIndex; - int32 indexB = m_bodyB->m_islandIndex; - - b2Dump(" b2PrismaticJointDef jd;\n"); - b2Dump(" jd.bodyA = bodies[%d];\n", indexA); - b2Dump(" jd.bodyB = bodies[%d];\n", indexB); - b2Dump(" jd.collideConnected = bool(%d);\n", m_collideConnected); - b2Dump(" jd.localAnchorA.Set(%.9g, %.9g);\n", m_localAnchorA.x, m_localAnchorA.y); - b2Dump(" jd.localAnchorB.Set(%.9g, %.9g);\n", m_localAnchorB.x, m_localAnchorB.y); - b2Dump(" jd.localAxisA.Set(%.9g, %.9g);\n", m_localXAxisA.x, m_localXAxisA.y); - b2Dump(" jd.referenceAngle = %.9g;\n", m_referenceAngle); - b2Dump(" jd.enableLimit = bool(%d);\n", m_enableLimit); - b2Dump(" jd.lowerTranslation = %.9g;\n", m_lowerTranslation); - b2Dump(" jd.upperTranslation = %.9g;\n", m_upperTranslation); - b2Dump(" jd.enableMotor = bool(%d);\n", m_enableMotor); - b2Dump(" jd.motorSpeed = %.9g;\n", m_motorSpeed); - b2Dump(" jd.maxMotorForce = %.9g;\n", m_maxMotorForce); - b2Dump(" joints[%d] = m_world->CreateJoint(&jd);\n", m_index); -} - -void b2PrismaticJoint::Draw(b2Draw* draw) const -{ - const b2Transform& xfA = m_bodyA->GetTransform(); - const b2Transform& xfB = m_bodyB->GetTransform(); - b2Vec2 pA = b2Mul(xfA, m_localAnchorA); - b2Vec2 pB = b2Mul(xfB, m_localAnchorB); - - b2Vec2 axis = b2Mul(xfA.q, m_localXAxisA); - - b2Color c1(0.7f, 0.7f, 0.7f); - b2Color c2(0.3f, 0.9f, 0.3f); - b2Color c3(0.9f, 0.3f, 0.3f); - b2Color c4(0.3f, 0.3f, 0.9f); - b2Color c5(0.4f, 0.4f, 0.4f); - - draw->DrawSegment(pA, pB, c5); - - if (m_enableLimit) - { - b2Vec2 lower = pA + m_lowerTranslation * axis; - b2Vec2 upper = pA + m_upperTranslation * axis; - b2Vec2 perp = b2Mul(xfA.q, m_localYAxisA); - draw->DrawSegment(lower, upper, c1); - draw->DrawSegment(lower - 0.5f * perp, lower + 0.5f * perp, c2); - draw->DrawSegment(upper - 0.5f * perp, upper + 0.5f * perp, c3); - } - else - { - draw->DrawSegment(pA - 1.0f * axis, pA + 1.0f * axis, c1); - } - - draw->DrawPoint(pA, 5.0f, c1); - draw->DrawPoint(pB, 5.0f, c4); -} diff --git a/3rdparty/box2d/src/dynamics/b2_pulley_joint.cpp b/3rdparty/box2d/src/dynamics/b2_pulley_joint.cpp deleted file mode 100644 index 099e57e49d91..000000000000 --- a/3rdparty/box2d/src/dynamics/b2_pulley_joint.cpp +++ /dev/null @@ -1,352 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "box2d/b2_body.h" -#include "box2d/b2_pulley_joint.h" -#include "box2d/b2_time_step.h" - -// Pulley: -// length1 = norm(p1 - s1) -// length2 = norm(p2 - s2) -// C0 = (length1 + ratio * length2)_initial -// C = C0 - (length1 + ratio * length2) -// u1 = (p1 - s1) / norm(p1 - s1) -// u2 = (p2 - s2) / norm(p2 - s2) -// Cdot = -dot(u1, v1 + cross(w1, r1)) - ratio * dot(u2, v2 + cross(w2, r2)) -// J = -[u1 cross(r1, u1) ratio * u2 ratio * cross(r2, u2)] -// K = J * invM * JT -// = invMass1 + invI1 * cross(r1, u1)^2 + ratio^2 * (invMass2 + invI2 * cross(r2, u2)^2) - -void b2PulleyJointDef::Initialize(b2Body* bA, b2Body* bB, - const b2Vec2& groundA, const b2Vec2& groundB, - const b2Vec2& anchorA, const b2Vec2& anchorB, - float r) -{ - bodyA = bA; - bodyB = bB; - groundAnchorA = groundA; - groundAnchorB = groundB; - localAnchorA = bodyA->GetLocalPoint(anchorA); - localAnchorB = bodyB->GetLocalPoint(anchorB); - b2Vec2 dA = anchorA - groundA; - lengthA = dA.Length(); - b2Vec2 dB = anchorB - groundB; - lengthB = dB.Length(); - ratio = r; - b2Assert(ratio > b2_epsilon); -} - -b2PulleyJoint::b2PulleyJoint(const b2PulleyJointDef* def) -: b2Joint(def) -{ - m_groundAnchorA = def->groundAnchorA; - m_groundAnchorB = def->groundAnchorB; - m_localAnchorA = def->localAnchorA; - m_localAnchorB = def->localAnchorB; - - m_lengthA = def->lengthA; - m_lengthB = def->lengthB; - - b2Assert(def->ratio != 0.0f); - m_ratio = def->ratio; - - m_constant = def->lengthA + m_ratio * def->lengthB; - - m_impulse = 0.0f; -} - -void b2PulleyJoint::InitVelocityConstraints(const b2SolverData& data) -{ - m_indexA = m_bodyA->m_islandIndex; - m_indexB = m_bodyB->m_islandIndex; - m_localCenterA = m_bodyA->m_sweep.localCenter; - m_localCenterB = m_bodyB->m_sweep.localCenter; - m_invMassA = m_bodyA->m_invMass; - m_invMassB = m_bodyB->m_invMass; - m_invIA = m_bodyA->m_invI; - m_invIB = m_bodyB->m_invI; - - b2Vec2 cA = data.positions[m_indexA].c; - float aA = data.positions[m_indexA].a; - b2Vec2 vA = data.velocities[m_indexA].v; - float wA = data.velocities[m_indexA].w; - - b2Vec2 cB = data.positions[m_indexB].c; - float aB = data.positions[m_indexB].a; - b2Vec2 vB = data.velocities[m_indexB].v; - float wB = data.velocities[m_indexB].w; - - b2Rot qA(aA), qB(aB); - - m_rA = b2Mul(qA, m_localAnchorA - m_localCenterA); - m_rB = b2Mul(qB, m_localAnchorB - m_localCenterB); - - // Get the pulley axes. - m_uA = cA + m_rA - m_groundAnchorA; - m_uB = cB + m_rB - m_groundAnchorB; - - float lengthA = m_uA.Length(); - float lengthB = m_uB.Length(); - - if (lengthA > 10.0f * b2_linearSlop) - { - m_uA *= 1.0f / lengthA; - } - else - { - m_uA.SetZero(); - } - - if (lengthB > 10.0f * b2_linearSlop) - { - m_uB *= 1.0f / lengthB; - } - else - { - m_uB.SetZero(); - } - - // Compute effective mass. - float ruA = b2Cross(m_rA, m_uA); - float ruB = b2Cross(m_rB, m_uB); - - float mA = m_invMassA + m_invIA * ruA * ruA; - float mB = m_invMassB + m_invIB * ruB * ruB; - - m_mass = mA + m_ratio * m_ratio * mB; - - if (m_mass > 0.0f) - { - m_mass = 1.0f / m_mass; - } - - if (data.step.warmStarting) - { - // Scale impulses to support variable time steps. - m_impulse *= data.step.dtRatio; - - // Warm starting. - b2Vec2 PA = -(m_impulse) * m_uA; - b2Vec2 PB = (-m_ratio * m_impulse) * m_uB; - - vA += m_invMassA * PA; - wA += m_invIA * b2Cross(m_rA, PA); - vB += m_invMassB * PB; - wB += m_invIB * b2Cross(m_rB, PB); - } - else - { - m_impulse = 0.0f; - } - - data.velocities[m_indexA].v = vA; - data.velocities[m_indexA].w = wA; - data.velocities[m_indexB].v = vB; - data.velocities[m_indexB].w = wB; -} - -void b2PulleyJoint::SolveVelocityConstraints(const b2SolverData& data) -{ - b2Vec2 vA = data.velocities[m_indexA].v; - float wA = data.velocities[m_indexA].w; - b2Vec2 vB = data.velocities[m_indexB].v; - float wB = data.velocities[m_indexB].w; - - b2Vec2 vpA = vA + b2Cross(wA, m_rA); - b2Vec2 vpB = vB + b2Cross(wB, m_rB); - - float Cdot = -b2Dot(m_uA, vpA) - m_ratio * b2Dot(m_uB, vpB); - float impulse = -m_mass * Cdot; - m_impulse += impulse; - - b2Vec2 PA = -impulse * m_uA; - b2Vec2 PB = -m_ratio * impulse * m_uB; - vA += m_invMassA * PA; - wA += m_invIA * b2Cross(m_rA, PA); - vB += m_invMassB * PB; - wB += m_invIB * b2Cross(m_rB, PB); - - data.velocities[m_indexA].v = vA; - data.velocities[m_indexA].w = wA; - data.velocities[m_indexB].v = vB; - data.velocities[m_indexB].w = wB; -} - -bool b2PulleyJoint::SolvePositionConstraints(const b2SolverData& data) -{ - b2Vec2 cA = data.positions[m_indexA].c; - float aA = data.positions[m_indexA].a; - b2Vec2 cB = data.positions[m_indexB].c; - float aB = data.positions[m_indexB].a; - - b2Rot qA(aA), qB(aB); - - b2Vec2 rA = b2Mul(qA, m_localAnchorA - m_localCenterA); - b2Vec2 rB = b2Mul(qB, m_localAnchorB - m_localCenterB); - - // Get the pulley axes. - b2Vec2 uA = cA + rA - m_groundAnchorA; - b2Vec2 uB = cB + rB - m_groundAnchorB; - - float lengthA = uA.Length(); - float lengthB = uB.Length(); - - if (lengthA > 10.0f * b2_linearSlop) - { - uA *= 1.0f / lengthA; - } - else - { - uA.SetZero(); - } - - if (lengthB > 10.0f * b2_linearSlop) - { - uB *= 1.0f / lengthB; - } - else - { - uB.SetZero(); - } - - // Compute effective mass. - float ruA = b2Cross(rA, uA); - float ruB = b2Cross(rB, uB); - - float mA = m_invMassA + m_invIA * ruA * ruA; - float mB = m_invMassB + m_invIB * ruB * ruB; - - float mass = mA + m_ratio * m_ratio * mB; - - if (mass > 0.0f) - { - mass = 1.0f / mass; - } - - float C = m_constant - lengthA - m_ratio * lengthB; - float linearError = b2Abs(C); - - float impulse = -mass * C; - - b2Vec2 PA = -impulse * uA; - b2Vec2 PB = -m_ratio * impulse * uB; - - cA += m_invMassA * PA; - aA += m_invIA * b2Cross(rA, PA); - cB += m_invMassB * PB; - aB += m_invIB * b2Cross(rB, PB); - - data.positions[m_indexA].c = cA; - data.positions[m_indexA].a = aA; - data.positions[m_indexB].c = cB; - data.positions[m_indexB].a = aB; - - return linearError < b2_linearSlop; -} - -b2Vec2 b2PulleyJoint::GetAnchorA() const -{ - return m_bodyA->GetWorldPoint(m_localAnchorA); -} - -b2Vec2 b2PulleyJoint::GetAnchorB() const -{ - return m_bodyB->GetWorldPoint(m_localAnchorB); -} - -b2Vec2 b2PulleyJoint::GetReactionForce(float inv_dt) const -{ - b2Vec2 P = m_impulse * m_uB; - return inv_dt * P; -} - -float b2PulleyJoint::GetReactionTorque(float inv_dt) const -{ - B2_NOT_USED(inv_dt); - return 0.0f; -} - -b2Vec2 b2PulleyJoint::GetGroundAnchorA() const -{ - return m_groundAnchorA; -} - -b2Vec2 b2PulleyJoint::GetGroundAnchorB() const -{ - return m_groundAnchorB; -} - -float b2PulleyJoint::GetLengthA() const -{ - return m_lengthA; -} - -float b2PulleyJoint::GetLengthB() const -{ - return m_lengthB; -} - -float b2PulleyJoint::GetRatio() const -{ - return m_ratio; -} - -float b2PulleyJoint::GetCurrentLengthA() const -{ - b2Vec2 p = m_bodyA->GetWorldPoint(m_localAnchorA); - b2Vec2 s = m_groundAnchorA; - b2Vec2 d = p - s; - return d.Length(); -} - -float b2PulleyJoint::GetCurrentLengthB() const -{ - b2Vec2 p = m_bodyB->GetWorldPoint(m_localAnchorB); - b2Vec2 s = m_groundAnchorB; - b2Vec2 d = p - s; - return d.Length(); -} - -void b2PulleyJoint::Dump() -{ - int32 indexA = m_bodyA->m_islandIndex; - int32 indexB = m_bodyB->m_islandIndex; - - b2Dump(" b2PulleyJointDef jd;\n"); - b2Dump(" jd.bodyA = bodies[%d];\n", indexA); - b2Dump(" jd.bodyB = bodies[%d];\n", indexB); - b2Dump(" jd.collideConnected = bool(%d);\n", m_collideConnected); - b2Dump(" jd.groundAnchorA.Set(%.9g, %.9g);\n", m_groundAnchorA.x, m_groundAnchorA.y); - b2Dump(" jd.groundAnchorB.Set(%.9g, %.9g);\n", m_groundAnchorB.x, m_groundAnchorB.y); - b2Dump(" jd.localAnchorA.Set(%.9g, %.9g);\n", m_localAnchorA.x, m_localAnchorA.y); - b2Dump(" jd.localAnchorB.Set(%.9g, %.9g);\n", m_localAnchorB.x, m_localAnchorB.y); - b2Dump(" jd.lengthA = %.9g;\n", m_lengthA); - b2Dump(" jd.lengthB = %.9g;\n", m_lengthB); - b2Dump(" jd.ratio = %.9g;\n", m_ratio); - b2Dump(" joints[%d] = m_world->CreateJoint(&jd);\n", m_index); -} - -void b2PulleyJoint::ShiftOrigin(const b2Vec2& newOrigin) -{ - m_groundAnchorA -= newOrigin; - m_groundAnchorB -= newOrigin; -} diff --git a/3rdparty/box2d/src/dynamics/b2_revolute_joint.cpp b/3rdparty/box2d/src/dynamics/b2_revolute_joint.cpp deleted file mode 100644 index f7cc4cc29afe..000000000000 --- a/3rdparty/box2d/src/dynamics/b2_revolute_joint.cpp +++ /dev/null @@ -1,501 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "box2d/b2_body.h" -#include "box2d/b2_draw.h" -#include "box2d/b2_revolute_joint.h" -#include "box2d/b2_time_step.h" - -// Point-to-point constraint -// C = p2 - p1 -// Cdot = v2 - v1 -// = v2 + cross(w2, r2) - v1 - cross(w1, r1) -// J = [-I -r1_skew I r2_skew ] -// Identity used: -// w k % (rx i + ry j) = w * (-ry i + rx j) - -// Motor constraint -// Cdot = w2 - w1 -// J = [0 0 -1 0 0 1] -// K = invI1 + invI2 - -void b2RevoluteJointDef::Initialize(b2Body* bA, b2Body* bB, const b2Vec2& anchor) -{ - bodyA = bA; - bodyB = bB; - localAnchorA = bodyA->GetLocalPoint(anchor); - localAnchorB = bodyB->GetLocalPoint(anchor); - referenceAngle = bodyB->GetAngle() - bodyA->GetAngle(); -} - -b2RevoluteJoint::b2RevoluteJoint(const b2RevoluteJointDef* def) -: b2Joint(def) -{ - m_localAnchorA = def->localAnchorA; - m_localAnchorB = def->localAnchorB; - m_referenceAngle = def->referenceAngle; - - m_impulse.SetZero(); - m_axialMass = 0.0f; - m_motorImpulse = 0.0f; - m_lowerImpulse = 0.0f; - m_upperImpulse = 0.0f; - - m_lowerAngle = def->lowerAngle; - m_upperAngle = def->upperAngle; - m_maxMotorTorque = def->maxMotorTorque; - m_motorSpeed = def->motorSpeed; - m_enableLimit = def->enableLimit; - m_enableMotor = def->enableMotor; - - m_angle = 0.0f; -} - -void b2RevoluteJoint::InitVelocityConstraints(const b2SolverData& data) -{ - m_indexA = m_bodyA->m_islandIndex; - m_indexB = m_bodyB->m_islandIndex; - m_localCenterA = m_bodyA->m_sweep.localCenter; - m_localCenterB = m_bodyB->m_sweep.localCenter; - m_invMassA = m_bodyA->m_invMass; - m_invMassB = m_bodyB->m_invMass; - m_invIA = m_bodyA->m_invI; - m_invIB = m_bodyB->m_invI; - - float aA = data.positions[m_indexA].a; - b2Vec2 vA = data.velocities[m_indexA].v; - float wA = data.velocities[m_indexA].w; - - float aB = data.positions[m_indexB].a; - b2Vec2 vB = data.velocities[m_indexB].v; - float wB = data.velocities[m_indexB].w; - - b2Rot qA(aA), qB(aB); - - m_rA = b2Mul(qA, m_localAnchorA - m_localCenterA); - m_rB = b2Mul(qB, m_localAnchorB - m_localCenterB); - - // J = [-I -r1_skew I r2_skew] - // r_skew = [-ry; rx] - - // Matlab - // K = [ mA+r1y^2*iA+mB+r2y^2*iB, -r1y*iA*r1x-r2y*iB*r2x] - // [ -r1y*iA*r1x-r2y*iB*r2x, mA+r1x^2*iA+mB+r2x^2*iB] - - float mA = m_invMassA, mB = m_invMassB; - float iA = m_invIA, iB = m_invIB; - - m_K.ex.x = mA + mB + m_rA.y * m_rA.y * iA + m_rB.y * m_rB.y * iB; - m_K.ey.x = -m_rA.y * m_rA.x * iA - m_rB.y * m_rB.x * iB; - m_K.ex.y = m_K.ey.x; - m_K.ey.y = mA + mB + m_rA.x * m_rA.x * iA + m_rB.x * m_rB.x * iB; - - m_axialMass = iA + iB; - bool fixedRotation; - if (m_axialMass > 0.0f) - { - m_axialMass = 1.0f / m_axialMass; - fixedRotation = false; - } - else - { - fixedRotation = true; - } - - m_angle = aB - aA - m_referenceAngle; - if (m_enableLimit == false || fixedRotation) - { - m_lowerImpulse = 0.0f; - m_upperImpulse = 0.0f; - } - - if (m_enableMotor == false || fixedRotation) - { - m_motorImpulse = 0.0f; - } - - if (data.step.warmStarting) - { - // Scale impulses to support a variable time step. - m_impulse *= data.step.dtRatio; - m_motorImpulse *= data.step.dtRatio; - m_lowerImpulse *= data.step.dtRatio; - m_upperImpulse *= data.step.dtRatio; - - float axialImpulse = m_motorImpulse + m_lowerImpulse - m_upperImpulse; - b2Vec2 P(m_impulse.x, m_impulse.y); - - vA -= mA * P; - wA -= iA * (b2Cross(m_rA, P) + axialImpulse); - - vB += mB * P; - wB += iB * (b2Cross(m_rB, P) + axialImpulse); - } - else - { - m_impulse.SetZero(); - m_motorImpulse = 0.0f; - m_lowerImpulse = 0.0f; - m_upperImpulse = 0.0f; - } - - data.velocities[m_indexA].v = vA; - data.velocities[m_indexA].w = wA; - data.velocities[m_indexB].v = vB; - data.velocities[m_indexB].w = wB; -} - -void b2RevoluteJoint::SolveVelocityConstraints(const b2SolverData& data) -{ - b2Vec2 vA = data.velocities[m_indexA].v; - float wA = data.velocities[m_indexA].w; - b2Vec2 vB = data.velocities[m_indexB].v; - float wB = data.velocities[m_indexB].w; - - float mA = m_invMassA, mB = m_invMassB; - float iA = m_invIA, iB = m_invIB; - - bool fixedRotation = (iA + iB == 0.0f); - - // Solve motor constraint. - if (m_enableMotor && fixedRotation == false) - { - float Cdot = wB - wA - m_motorSpeed; - float impulse = -m_axialMass * Cdot; - float oldImpulse = m_motorImpulse; - float maxImpulse = data.step.dt * m_maxMotorTorque; - m_motorImpulse = b2Clamp(m_motorImpulse + impulse, -maxImpulse, maxImpulse); - impulse = m_motorImpulse - oldImpulse; - - wA -= iA * impulse; - wB += iB * impulse; - } - - if (m_enableLimit && fixedRotation == false) - { - // Lower limit - { - float C = m_angle - m_lowerAngle; - float Cdot = wB - wA; - float impulse = -m_axialMass * (Cdot + b2Max(C, 0.0f) * data.step.inv_dt); - float oldImpulse = m_lowerImpulse; - m_lowerImpulse = b2Max(m_lowerImpulse + impulse, 0.0f); - impulse = m_lowerImpulse - oldImpulse; - - wA -= iA * impulse; - wB += iB * impulse; - } - - // Upper limit - // Note: signs are flipped to keep C positive when the constraint is satisfied. - // This also keeps the impulse positive when the limit is active. - { - float C = m_upperAngle - m_angle; - float Cdot = wA - wB; - float impulse = -m_axialMass * (Cdot + b2Max(C, 0.0f) * data.step.inv_dt); - float oldImpulse = m_upperImpulse; - m_upperImpulse = b2Max(m_upperImpulse + impulse, 0.0f); - impulse = m_upperImpulse - oldImpulse; - - wA += iA * impulse; - wB -= iB * impulse; - } - } - - // Solve point-to-point constraint - { - b2Vec2 Cdot = vB + b2Cross(wB, m_rB) - vA - b2Cross(wA, m_rA); - b2Vec2 impulse = m_K.Solve(-Cdot); - - m_impulse.x += impulse.x; - m_impulse.y += impulse.y; - - vA -= mA * impulse; - wA -= iA * b2Cross(m_rA, impulse); - - vB += mB * impulse; - wB += iB * b2Cross(m_rB, impulse); - } - - data.velocities[m_indexA].v = vA; - data.velocities[m_indexA].w = wA; - data.velocities[m_indexB].v = vB; - data.velocities[m_indexB].w = wB; -} - -bool b2RevoluteJoint::SolvePositionConstraints(const b2SolverData& data) -{ - b2Vec2 cA = data.positions[m_indexA].c; - float aA = data.positions[m_indexA].a; - b2Vec2 cB = data.positions[m_indexB].c; - float aB = data.positions[m_indexB].a; - - b2Rot qA(aA), qB(aB); - - float angularError = 0.0f; - float positionError = 0.0f; - - bool fixedRotation = (m_invIA + m_invIB == 0.0f); - - // Solve angular limit constraint - if (m_enableLimit && fixedRotation == false) - { - float angle = aB - aA - m_referenceAngle; - float C = 0.0f; - - if (b2Abs(m_upperAngle - m_lowerAngle) < 2.0f * b2_angularSlop) - { - // Prevent large angular corrections - C = b2Clamp(angle - m_lowerAngle, -b2_maxAngularCorrection, b2_maxAngularCorrection); - } - else if (angle <= m_lowerAngle) - { - // Prevent large angular corrections and allow some slop. - C = b2Clamp(angle - m_lowerAngle + b2_angularSlop, -b2_maxAngularCorrection, 0.0f); - } - else if (angle >= m_upperAngle) - { - // Prevent large angular corrections and allow some slop. - C = b2Clamp(angle - m_upperAngle - b2_angularSlop, 0.0f, b2_maxAngularCorrection); - } - - float limitImpulse = -m_axialMass * C; - aA -= m_invIA * limitImpulse; - aB += m_invIB * limitImpulse; - angularError = b2Abs(C); - } - - // Solve point-to-point constraint. - { - qA.Set(aA); - qB.Set(aB); - b2Vec2 rA = b2Mul(qA, m_localAnchorA - m_localCenterA); - b2Vec2 rB = b2Mul(qB, m_localAnchorB - m_localCenterB); - - b2Vec2 C = cB + rB - cA - rA; - positionError = C.Length(); - - float mA = m_invMassA, mB = m_invMassB; - float iA = m_invIA, iB = m_invIB; - - b2Mat22 K; - K.ex.x = mA + mB + iA * rA.y * rA.y + iB * rB.y * rB.y; - K.ex.y = -iA * rA.x * rA.y - iB * rB.x * rB.y; - K.ey.x = K.ex.y; - K.ey.y = mA + mB + iA * rA.x * rA.x + iB * rB.x * rB.x; - - b2Vec2 impulse = -K.Solve(C); - - cA -= mA * impulse; - aA -= iA * b2Cross(rA, impulse); - - cB += mB * impulse; - aB += iB * b2Cross(rB, impulse); - } - - data.positions[m_indexA].c = cA; - data.positions[m_indexA].a = aA; - data.positions[m_indexB].c = cB; - data.positions[m_indexB].a = aB; - - return positionError <= b2_linearSlop && angularError <= b2_angularSlop; -} - -b2Vec2 b2RevoluteJoint::GetAnchorA() const -{ - return m_bodyA->GetWorldPoint(m_localAnchorA); -} - -b2Vec2 b2RevoluteJoint::GetAnchorB() const -{ - return m_bodyB->GetWorldPoint(m_localAnchorB); -} - -b2Vec2 b2RevoluteJoint::GetReactionForce(float inv_dt) const -{ - b2Vec2 P(m_impulse.x, m_impulse.y); - return inv_dt * P; -} - -float b2RevoluteJoint::GetReactionTorque(float inv_dt) const -{ - return inv_dt * (m_motorImpulse + m_lowerImpulse - m_upperImpulse); -} - -float b2RevoluteJoint::GetJointAngle() const -{ - b2Body* bA = m_bodyA; - b2Body* bB = m_bodyB; - return bB->m_sweep.a - bA->m_sweep.a - m_referenceAngle; -} - -float b2RevoluteJoint::GetJointSpeed() const -{ - b2Body* bA = m_bodyA; - b2Body* bB = m_bodyB; - return bB->m_angularVelocity - bA->m_angularVelocity; -} - -bool b2RevoluteJoint::IsMotorEnabled() const -{ - return m_enableMotor; -} - -void b2RevoluteJoint::EnableMotor(bool flag) -{ - if (flag != m_enableMotor) - { - m_bodyA->SetAwake(true); - m_bodyB->SetAwake(true); - m_enableMotor = flag; - } -} - -float b2RevoluteJoint::GetMotorTorque(float inv_dt) const -{ - return inv_dt * m_motorImpulse; -} - -void b2RevoluteJoint::SetMotorSpeed(float speed) -{ - if (speed != m_motorSpeed) - { - m_bodyA->SetAwake(true); - m_bodyB->SetAwake(true); - m_motorSpeed = speed; - } -} - -void b2RevoluteJoint::SetMaxMotorTorque(float torque) -{ - if (torque != m_maxMotorTorque) - { - m_bodyA->SetAwake(true); - m_bodyB->SetAwake(true); - m_maxMotorTorque = torque; - } -} - -bool b2RevoluteJoint::IsLimitEnabled() const -{ - return m_enableLimit; -} - -void b2RevoluteJoint::EnableLimit(bool flag) -{ - if (flag != m_enableLimit) - { - m_bodyA->SetAwake(true); - m_bodyB->SetAwake(true); - m_enableLimit = flag; - m_lowerImpulse = 0.0f; - m_upperImpulse = 0.0f; - } -} - -float b2RevoluteJoint::GetLowerLimit() const -{ - return m_lowerAngle; -} - -float b2RevoluteJoint::GetUpperLimit() const -{ - return m_upperAngle; -} - -void b2RevoluteJoint::SetLimits(float lower, float upper) -{ - b2Assert(lower <= upper); - - if (lower != m_lowerAngle || upper != m_upperAngle) - { - m_bodyA->SetAwake(true); - m_bodyB->SetAwake(true); - m_lowerImpulse = 0.0f; - m_upperImpulse = 0.0f; - m_lowerAngle = lower; - m_upperAngle = upper; - } -} - -void b2RevoluteJoint::Dump() -{ - int32 indexA = m_bodyA->m_islandIndex; - int32 indexB = m_bodyB->m_islandIndex; - - b2Dump(" b2RevoluteJointDef jd;\n"); - b2Dump(" jd.bodyA = bodies[%d];\n", indexA); - b2Dump(" jd.bodyB = bodies[%d];\n", indexB); - b2Dump(" jd.collideConnected = bool(%d);\n", m_collideConnected); - b2Dump(" jd.localAnchorA.Set(%.9g, %.9g);\n", m_localAnchorA.x, m_localAnchorA.y); - b2Dump(" jd.localAnchorB.Set(%.9g, %.9g);\n", m_localAnchorB.x, m_localAnchorB.y); - b2Dump(" jd.referenceAngle = %.9g;\n", m_referenceAngle); - b2Dump(" jd.enableLimit = bool(%d);\n", m_enableLimit); - b2Dump(" jd.lowerAngle = %.9g;\n", m_lowerAngle); - b2Dump(" jd.upperAngle = %.9g;\n", m_upperAngle); - b2Dump(" jd.enableMotor = bool(%d);\n", m_enableMotor); - b2Dump(" jd.motorSpeed = %.9g;\n", m_motorSpeed); - b2Dump(" jd.maxMotorTorque = %.9g;\n", m_maxMotorTorque); - b2Dump(" joints[%d] = m_world->CreateJoint(&jd);\n", m_index); -} - -/// -void b2RevoluteJoint::Draw(b2Draw* draw) const -{ - const b2Transform& xfA = m_bodyA->GetTransform(); - const b2Transform& xfB = m_bodyB->GetTransform(); - b2Vec2 pA = b2Mul(xfA, m_localAnchorA); - b2Vec2 pB = b2Mul(xfB, m_localAnchorB); - - b2Color c1(0.7f, 0.7f, 0.7f); - b2Color c2(0.3f, 0.9f, 0.3f); - b2Color c3(0.9f, 0.3f, 0.3f); - b2Color c4(0.3f, 0.3f, 0.9f); - b2Color c5(0.4f, 0.4f, 0.4f); - - draw->DrawPoint(pA, 5.0f, c4); - draw->DrawPoint(pB, 5.0f, c5); - - float aA = m_bodyA->GetAngle(); - float aB = m_bodyB->GetAngle(); - float angle = aB - aA - m_referenceAngle; - - const float L = 0.5f; - - b2Vec2 r = L * b2Vec2(cosf(angle), sinf(angle)); - draw->DrawSegment(pB, pB + r, c1); - draw->DrawCircle(pB, L, c1); - - if (m_enableLimit) - { - b2Vec2 rlo = L * b2Vec2(cosf(m_lowerAngle), sinf(m_lowerAngle)); - b2Vec2 rhi = L * b2Vec2(cosf(m_upperAngle), sinf(m_upperAngle)); - - draw->DrawSegment(pB, pB + rlo, c2); - draw->DrawSegment(pB, pB + rhi, c3); - } - - b2Color color(0.5f, 0.8f, 0.8f); - draw->DrawSegment(xfA.p, pA, color); - draw->DrawSegment(pA, pB, color); - draw->DrawSegment(xfB.p, pB, color); -} diff --git a/3rdparty/box2d/src/dynamics/b2_weld_joint.cpp b/3rdparty/box2d/src/dynamics/b2_weld_joint.cpp deleted file mode 100644 index df3ee0a3eaf1..000000000000 --- a/3rdparty/box2d/src/dynamics/b2_weld_joint.cpp +++ /dev/null @@ -1,344 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "box2d/b2_body.h" -#include "box2d/b2_time_step.h" -#include "box2d/b2_weld_joint.h" - -// Point-to-point constraint -// C = p2 - p1 -// Cdot = v2 - v1 -// = v2 + cross(w2, r2) - v1 - cross(w1, r1) -// J = [-I -r1_skew I r2_skew ] -// Identity used: -// w k % (rx i + ry j) = w * (-ry i + rx j) - -// Angle constraint -// C = angle2 - angle1 - referenceAngle -// Cdot = w2 - w1 -// J = [0 0 -1 0 0 1] -// K = invI1 + invI2 - -void b2WeldJointDef::Initialize(b2Body* bA, b2Body* bB, const b2Vec2& anchor) -{ - bodyA = bA; - bodyB = bB; - localAnchorA = bodyA->GetLocalPoint(anchor); - localAnchorB = bodyB->GetLocalPoint(anchor); - referenceAngle = bodyB->GetAngle() - bodyA->GetAngle(); -} - -b2WeldJoint::b2WeldJoint(const b2WeldJointDef* def) -: b2Joint(def) -{ - m_localAnchorA = def->localAnchorA; - m_localAnchorB = def->localAnchorB; - m_referenceAngle = def->referenceAngle; - m_stiffness = def->stiffness; - m_damping = def->damping; - - m_impulse.SetZero(); -} - -void b2WeldJoint::InitVelocityConstraints(const b2SolverData& data) -{ - m_indexA = m_bodyA->m_islandIndex; - m_indexB = m_bodyB->m_islandIndex; - m_localCenterA = m_bodyA->m_sweep.localCenter; - m_localCenterB = m_bodyB->m_sweep.localCenter; - m_invMassA = m_bodyA->m_invMass; - m_invMassB = m_bodyB->m_invMass; - m_invIA = m_bodyA->m_invI; - m_invIB = m_bodyB->m_invI; - - float aA = data.positions[m_indexA].a; - b2Vec2 vA = data.velocities[m_indexA].v; - float wA = data.velocities[m_indexA].w; - - float aB = data.positions[m_indexB].a; - b2Vec2 vB = data.velocities[m_indexB].v; - float wB = data.velocities[m_indexB].w; - - b2Rot qA(aA), qB(aB); - - m_rA = b2Mul(qA, m_localAnchorA - m_localCenterA); - m_rB = b2Mul(qB, m_localAnchorB - m_localCenterB); - - // J = [-I -r1_skew I r2_skew] - // [ 0 -1 0 1] - // r_skew = [-ry; rx] - - // Matlab - // K = [ mA+r1y^2*iA+mB+r2y^2*iB, -r1y*iA*r1x-r2y*iB*r2x, -r1y*iA-r2y*iB] - // [ -r1y*iA*r1x-r2y*iB*r2x, mA+r1x^2*iA+mB+r2x^2*iB, r1x*iA+r2x*iB] - // [ -r1y*iA-r2y*iB, r1x*iA+r2x*iB, iA+iB] - - float mA = m_invMassA, mB = m_invMassB; - float iA = m_invIA, iB = m_invIB; - - b2Mat33 K; - K.ex.x = mA + mB + m_rA.y * m_rA.y * iA + m_rB.y * m_rB.y * iB; - K.ey.x = -m_rA.y * m_rA.x * iA - m_rB.y * m_rB.x * iB; - K.ez.x = -m_rA.y * iA - m_rB.y * iB; - K.ex.y = K.ey.x; - K.ey.y = mA + mB + m_rA.x * m_rA.x * iA + m_rB.x * m_rB.x * iB; - K.ez.y = m_rA.x * iA + m_rB.x * iB; - K.ex.z = K.ez.x; - K.ey.z = K.ez.y; - K.ez.z = iA + iB; - - if (m_stiffness > 0.0f) - { - K.GetInverse22(&m_mass); - - float invM = iA + iB; - - float C = aB - aA - m_referenceAngle; - - // Damping coefficient - float d = m_damping; - - // Spring stiffness - float k = m_stiffness; - - // magic formulas - float h = data.step.dt; - m_gamma = h * (d + h * k); - m_gamma = m_gamma != 0.0f ? 1.0f / m_gamma : 0.0f; - m_bias = C * h * k * m_gamma; - - invM += m_gamma; - m_mass.ez.z = invM != 0.0f ? 1.0f / invM : 0.0f; - } - else if (K.ez.z == 0.0f) - { - K.GetInverse22(&m_mass); - m_gamma = 0.0f; - m_bias = 0.0f; - } - else - { - K.GetSymInverse33(&m_mass); - m_gamma = 0.0f; - m_bias = 0.0f; - } - - if (data.step.warmStarting) - { - // Scale impulses to support a variable time step. - m_impulse *= data.step.dtRatio; - - b2Vec2 P(m_impulse.x, m_impulse.y); - - vA -= mA * P; - wA -= iA * (b2Cross(m_rA, P) + m_impulse.z); - - vB += mB * P; - wB += iB * (b2Cross(m_rB, P) + m_impulse.z); - } - else - { - m_impulse.SetZero(); - } - - data.velocities[m_indexA].v = vA; - data.velocities[m_indexA].w = wA; - data.velocities[m_indexB].v = vB; - data.velocities[m_indexB].w = wB; -} - -void b2WeldJoint::SolveVelocityConstraints(const b2SolverData& data) -{ - b2Vec2 vA = data.velocities[m_indexA].v; - float wA = data.velocities[m_indexA].w; - b2Vec2 vB = data.velocities[m_indexB].v; - float wB = data.velocities[m_indexB].w; - - float mA = m_invMassA, mB = m_invMassB; - float iA = m_invIA, iB = m_invIB; - - if (m_stiffness > 0.0f) - { - float Cdot2 = wB - wA; - - float impulse2 = -m_mass.ez.z * (Cdot2 + m_bias + m_gamma * m_impulse.z); - m_impulse.z += impulse2; - - wA -= iA * impulse2; - wB += iB * impulse2; - - b2Vec2 Cdot1 = vB + b2Cross(wB, m_rB) - vA - b2Cross(wA, m_rA); - - b2Vec2 impulse1 = -b2Mul22(m_mass, Cdot1); - m_impulse.x += impulse1.x; - m_impulse.y += impulse1.y; - - b2Vec2 P = impulse1; - - vA -= mA * P; - wA -= iA * b2Cross(m_rA, P); - - vB += mB * P; - wB += iB * b2Cross(m_rB, P); - } - else - { - b2Vec2 Cdot1 = vB + b2Cross(wB, m_rB) - vA - b2Cross(wA, m_rA); - float Cdot2 = wB - wA; - b2Vec3 Cdot(Cdot1.x, Cdot1.y, Cdot2); - - b2Vec3 impulse = -b2Mul(m_mass, Cdot); - m_impulse += impulse; - - b2Vec2 P(impulse.x, impulse.y); - - vA -= mA * P; - wA -= iA * (b2Cross(m_rA, P) + impulse.z); - - vB += mB * P; - wB += iB * (b2Cross(m_rB, P) + impulse.z); - } - - data.velocities[m_indexA].v = vA; - data.velocities[m_indexA].w = wA; - data.velocities[m_indexB].v = vB; - data.velocities[m_indexB].w = wB; -} - -bool b2WeldJoint::SolvePositionConstraints(const b2SolverData& data) -{ - b2Vec2 cA = data.positions[m_indexA].c; - float aA = data.positions[m_indexA].a; - b2Vec2 cB = data.positions[m_indexB].c; - float aB = data.positions[m_indexB].a; - - b2Rot qA(aA), qB(aB); - - float mA = m_invMassA, mB = m_invMassB; - float iA = m_invIA, iB = m_invIB; - - b2Vec2 rA = b2Mul(qA, m_localAnchorA - m_localCenterA); - b2Vec2 rB = b2Mul(qB, m_localAnchorB - m_localCenterB); - - float positionError, angularError; - - b2Mat33 K; - K.ex.x = mA + mB + rA.y * rA.y * iA + rB.y * rB.y * iB; - K.ey.x = -rA.y * rA.x * iA - rB.y * rB.x * iB; - K.ez.x = -rA.y * iA - rB.y * iB; - K.ex.y = K.ey.x; - K.ey.y = mA + mB + rA.x * rA.x * iA + rB.x * rB.x * iB; - K.ez.y = rA.x * iA + rB.x * iB; - K.ex.z = K.ez.x; - K.ey.z = K.ez.y; - K.ez.z = iA + iB; - - if (m_stiffness > 0.0f) - { - b2Vec2 C1 = cB + rB - cA - rA; - - positionError = C1.Length(); - angularError = 0.0f; - - b2Vec2 P = -K.Solve22(C1); - - cA -= mA * P; - aA -= iA * b2Cross(rA, P); - - cB += mB * P; - aB += iB * b2Cross(rB, P); - } - else - { - b2Vec2 C1 = cB + rB - cA - rA; - float C2 = aB - aA - m_referenceAngle; - - positionError = C1.Length(); - angularError = b2Abs(C2); - - b2Vec3 C(C1.x, C1.y, C2); - - b2Vec3 impulse; - if (K.ez.z > 0.0f) - { - impulse = -K.Solve33(C); - } - else - { - b2Vec2 impulse2 = -K.Solve22(C1); - impulse.Set(impulse2.x, impulse2.y, 0.0f); - } - - b2Vec2 P(impulse.x, impulse.y); - - cA -= mA * P; - aA -= iA * (b2Cross(rA, P) + impulse.z); - - cB += mB * P; - aB += iB * (b2Cross(rB, P) + impulse.z); - } - - data.positions[m_indexA].c = cA; - data.positions[m_indexA].a = aA; - data.positions[m_indexB].c = cB; - data.positions[m_indexB].a = aB; - - return positionError <= b2_linearSlop && angularError <= b2_angularSlop; -} - -b2Vec2 b2WeldJoint::GetAnchorA() const -{ - return m_bodyA->GetWorldPoint(m_localAnchorA); -} - -b2Vec2 b2WeldJoint::GetAnchorB() const -{ - return m_bodyB->GetWorldPoint(m_localAnchorB); -} - -b2Vec2 b2WeldJoint::GetReactionForce(float inv_dt) const -{ - b2Vec2 P(m_impulse.x, m_impulse.y); - return inv_dt * P; -} - -float b2WeldJoint::GetReactionTorque(float inv_dt) const -{ - return inv_dt * m_impulse.z; -} - -void b2WeldJoint::Dump() -{ - int32 indexA = m_bodyA->m_islandIndex; - int32 indexB = m_bodyB->m_islandIndex; - - b2Dump(" b2WeldJointDef jd;\n"); - b2Dump(" jd.bodyA = bodies[%d];\n", indexA); - b2Dump(" jd.bodyB = bodies[%d];\n", indexB); - b2Dump(" jd.collideConnected = bool(%d);\n", m_collideConnected); - b2Dump(" jd.localAnchorA.Set(%.9g, %.9g);\n", m_localAnchorA.x, m_localAnchorA.y); - b2Dump(" jd.localAnchorB.Set(%.9g, %.9g);\n", m_localAnchorB.x, m_localAnchorB.y); - b2Dump(" jd.referenceAngle = %.9g;\n", m_referenceAngle); - b2Dump(" jd.stiffness = %.9g;\n", m_stiffness); - b2Dump(" jd.damping = %.9g;\n", m_damping); - b2Dump(" joints[%d] = m_world->CreateJoint(&jd);\n", m_index); -} diff --git a/3rdparty/box2d/src/dynamics/b2_wheel_joint.cpp b/3rdparty/box2d/src/dynamics/b2_wheel_joint.cpp deleted file mode 100644 index c23b98460973..000000000000 --- a/3rdparty/box2d/src/dynamics/b2_wheel_joint.cpp +++ /dev/null @@ -1,672 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "box2d/b2_body.h" -#include "box2d/b2_draw.h" -#include "box2d/b2_wheel_joint.h" -#include "box2d/b2_time_step.h" - -// Linear constraint (point-to-line) -// d = pB - pA = xB + rB - xA - rA -// C = dot(ay, d) -// Cdot = dot(d, cross(wA, ay)) + dot(ay, vB + cross(wB, rB) - vA - cross(wA, rA)) -// = -dot(ay, vA) - dot(cross(d + rA, ay), wA) + dot(ay, vB) + dot(cross(rB, ay), vB) -// J = [-ay, -cross(d + rA, ay), ay, cross(rB, ay)] - -// Spring linear constraint -// C = dot(ax, d) -// Cdot = = -dot(ax, vA) - dot(cross(d + rA, ax), wA) + dot(ax, vB) + dot(cross(rB, ax), vB) -// J = [-ax -cross(d+rA, ax) ax cross(rB, ax)] - -// Motor rotational constraint -// Cdot = wB - wA -// J = [0 0 -1 0 0 1] - -void b2WheelJointDef::Initialize(b2Body* bA, b2Body* bB, const b2Vec2& anchor, const b2Vec2& axis) -{ - bodyA = bA; - bodyB = bB; - localAnchorA = bodyA->GetLocalPoint(anchor); - localAnchorB = bodyB->GetLocalPoint(anchor); - localAxisA = bodyA->GetLocalVector(axis); -} - -b2WheelJoint::b2WheelJoint(const b2WheelJointDef* def) -: b2Joint(def) -{ - m_localAnchorA = def->localAnchorA; - m_localAnchorB = def->localAnchorB; - m_localXAxisA = def->localAxisA; - m_localYAxisA = b2Cross(1.0f, m_localXAxisA); - - m_mass = 0.0f; - m_impulse = 0.0f; - m_motorMass = 0.0f; - m_motorImpulse = 0.0f; - m_springMass = 0.0f; - m_springImpulse = 0.0f; - - m_axialMass = 0.0f; - m_lowerImpulse = 0.0f; - m_upperImpulse = 0.0f; - m_lowerTranslation = def->lowerTranslation; - m_upperTranslation = def->upperTranslation; - m_enableLimit = def->enableLimit; - - m_maxMotorTorque = def->maxMotorTorque; - m_motorSpeed = def->motorSpeed; - m_enableMotor = def->enableMotor; - - m_bias = 0.0f; - m_gamma = 0.0f; - - m_ax.SetZero(); - m_ay.SetZero(); - - m_stiffness = def->stiffness; - m_damping = def->damping; -} - -void b2WheelJoint::InitVelocityConstraints(const b2SolverData& data) -{ - m_indexA = m_bodyA->m_islandIndex; - m_indexB = m_bodyB->m_islandIndex; - m_localCenterA = m_bodyA->m_sweep.localCenter; - m_localCenterB = m_bodyB->m_sweep.localCenter; - m_invMassA = m_bodyA->m_invMass; - m_invMassB = m_bodyB->m_invMass; - m_invIA = m_bodyA->m_invI; - m_invIB = m_bodyB->m_invI; - - float mA = m_invMassA, mB = m_invMassB; - float iA = m_invIA, iB = m_invIB; - - b2Vec2 cA = data.positions[m_indexA].c; - float aA = data.positions[m_indexA].a; - b2Vec2 vA = data.velocities[m_indexA].v; - float wA = data.velocities[m_indexA].w; - - b2Vec2 cB = data.positions[m_indexB].c; - float aB = data.positions[m_indexB].a; - b2Vec2 vB = data.velocities[m_indexB].v; - float wB = data.velocities[m_indexB].w; - - b2Rot qA(aA), qB(aB); - - // Compute the effective masses. - b2Vec2 rA = b2Mul(qA, m_localAnchorA - m_localCenterA); - b2Vec2 rB = b2Mul(qB, m_localAnchorB - m_localCenterB); - b2Vec2 d = cB + rB - cA - rA; - - // Point to line constraint - { - m_ay = b2Mul(qA, m_localYAxisA); - m_sAy = b2Cross(d + rA, m_ay); - m_sBy = b2Cross(rB, m_ay); - - m_mass = mA + mB + iA * m_sAy * m_sAy + iB * m_sBy * m_sBy; - - if (m_mass > 0.0f) - { - m_mass = 1.0f / m_mass; - } - } - - // Spring constraint - m_ax = b2Mul(qA, m_localXAxisA); - m_sAx = b2Cross(d + rA, m_ax); - m_sBx = b2Cross(rB, m_ax); - - const float invMass = mA + mB + iA * m_sAx * m_sAx + iB * m_sBx * m_sBx; - if (invMass > 0.0f) - { - m_axialMass = 1.0f / invMass; - } - else - { - m_axialMass = 0.0f; - } - - m_springMass = 0.0f; - m_bias = 0.0f; - m_gamma = 0.0f; - - if (m_stiffness > 0.0f && invMass > 0.0f) - { - m_springMass = 1.0f / invMass; - - float C = b2Dot(d, m_ax); - - // magic formulas - float h = data.step.dt; - m_gamma = h * (m_damping + h * m_stiffness); - if (m_gamma > 0.0f) - { - m_gamma = 1.0f / m_gamma; - } - - m_bias = C * h * m_stiffness * m_gamma; - - m_springMass = invMass + m_gamma; - if (m_springMass > 0.0f) - { - m_springMass = 1.0f / m_springMass; - } - } - else - { - m_springImpulse = 0.0f; - } - - if (m_enableLimit) - { - m_translation = b2Dot(m_ax, d); - } - else - { - m_lowerImpulse = 0.0f; - m_upperImpulse = 0.0f; - } - - if (m_enableMotor) - { - m_motorMass = iA + iB; - if (m_motorMass > 0.0f) - { - m_motorMass = 1.0f / m_motorMass; - } - } - else - { - m_motorMass = 0.0f; - m_motorImpulse = 0.0f; - } - - if (data.step.warmStarting) - { - // Account for variable time step. - m_impulse *= data.step.dtRatio; - m_springImpulse *= data.step.dtRatio; - m_motorImpulse *= data.step.dtRatio; - - float axialImpulse = m_springImpulse + m_lowerImpulse - m_upperImpulse; - b2Vec2 P = m_impulse * m_ay + axialImpulse * m_ax; - float LA = m_impulse * m_sAy + axialImpulse * m_sAx + m_motorImpulse; - float LB = m_impulse * m_sBy + axialImpulse * m_sBx + m_motorImpulse; - - vA -= m_invMassA * P; - wA -= m_invIA * LA; - - vB += m_invMassB * P; - wB += m_invIB * LB; - } - else - { - m_impulse = 0.0f; - m_springImpulse = 0.0f; - m_motorImpulse = 0.0f; - m_lowerImpulse = 0.0f; - m_upperImpulse = 0.0f; - } - - data.velocities[m_indexA].v = vA; - data.velocities[m_indexA].w = wA; - data.velocities[m_indexB].v = vB; - data.velocities[m_indexB].w = wB; -} - -void b2WheelJoint::SolveVelocityConstraints(const b2SolverData& data) -{ - float mA = m_invMassA, mB = m_invMassB; - float iA = m_invIA, iB = m_invIB; - - b2Vec2 vA = data.velocities[m_indexA].v; - float wA = data.velocities[m_indexA].w; - b2Vec2 vB = data.velocities[m_indexB].v; - float wB = data.velocities[m_indexB].w; - - // Solve spring constraint - { - float Cdot = b2Dot(m_ax, vB - vA) + m_sBx * wB - m_sAx * wA; - float impulse = -m_springMass * (Cdot + m_bias + m_gamma * m_springImpulse); - m_springImpulse += impulse; - - b2Vec2 P = impulse * m_ax; - float LA = impulse * m_sAx; - float LB = impulse * m_sBx; - - vA -= mA * P; - wA -= iA * LA; - - vB += mB * P; - wB += iB * LB; - } - - // Solve rotational motor constraint - { - float Cdot = wB - wA - m_motorSpeed; - float impulse = -m_motorMass * Cdot; - - float oldImpulse = m_motorImpulse; - float maxImpulse = data.step.dt * m_maxMotorTorque; - m_motorImpulse = b2Clamp(m_motorImpulse + impulse, -maxImpulse, maxImpulse); - impulse = m_motorImpulse - oldImpulse; - - wA -= iA * impulse; - wB += iB * impulse; - } - - if (m_enableLimit) - { - // Lower limit - { - float C = m_translation - m_lowerTranslation; - float Cdot = b2Dot(m_ax, vB - vA) + m_sBx * wB - m_sAx * wA; - float impulse = -m_axialMass * (Cdot + b2Max(C, 0.0f) * data.step.inv_dt); - float oldImpulse = m_lowerImpulse; - m_lowerImpulse = b2Max(m_lowerImpulse + impulse, 0.0f); - impulse = m_lowerImpulse - oldImpulse; - - b2Vec2 P = impulse * m_ax; - float LA = impulse * m_sAx; - float LB = impulse * m_sBx; - - vA -= mA * P; - wA -= iA * LA; - vB += mB * P; - wB += iB * LB; - } - - // Upper limit - // Note: signs are flipped to keep C positive when the constraint is satisfied. - // This also keeps the impulse positive when the limit is active. - { - float C = m_upperTranslation - m_translation; - float Cdot = b2Dot(m_ax, vA - vB) + m_sAx * wA - m_sBx * wB; - float impulse = -m_axialMass * (Cdot + b2Max(C, 0.0f) * data.step.inv_dt); - float oldImpulse = m_upperImpulse; - m_upperImpulse = b2Max(m_upperImpulse + impulse, 0.0f); - impulse = m_upperImpulse - oldImpulse; - - b2Vec2 P = impulse * m_ax; - float LA = impulse * m_sAx; - float LB = impulse * m_sBx; - - vA += mA * P; - wA += iA * LA; - vB -= mB * P; - wB -= iB * LB; - } - } - - // Solve point to line constraint - { - float Cdot = b2Dot(m_ay, vB - vA) + m_sBy * wB - m_sAy * wA; - float impulse = -m_mass * Cdot; - m_impulse += impulse; - - b2Vec2 P = impulse * m_ay; - float LA = impulse * m_sAy; - float LB = impulse * m_sBy; - - vA -= mA * P; - wA -= iA * LA; - - vB += mB * P; - wB += iB * LB; - } - - data.velocities[m_indexA].v = vA; - data.velocities[m_indexA].w = wA; - data.velocities[m_indexB].v = vB; - data.velocities[m_indexB].w = wB; -} - -bool b2WheelJoint::SolvePositionConstraints(const b2SolverData& data) -{ - b2Vec2 cA = data.positions[m_indexA].c; - float aA = data.positions[m_indexA].a; - b2Vec2 cB = data.positions[m_indexB].c; - float aB = data.positions[m_indexB].a; - - float linearError = 0.0f; - - if (m_enableLimit) - { - b2Rot qA(aA), qB(aB); - - b2Vec2 rA = b2Mul(qA, m_localAnchorA - m_localCenterA); - b2Vec2 rB = b2Mul(qB, m_localAnchorB - m_localCenterB); - b2Vec2 d = (cB - cA) + rB - rA; - - b2Vec2 ax = b2Mul(qA, m_localXAxisA); - float sAx = b2Cross(d + rA, m_ax); - float sBx = b2Cross(rB, m_ax); - - float C = 0.0f; - float translation = b2Dot(ax, d); - if (b2Abs(m_upperTranslation - m_lowerTranslation) < 2.0f * b2_linearSlop) - { - C = translation; - } - else if (translation <= m_lowerTranslation) - { - C = b2Min(translation - m_lowerTranslation, 0.0f); - } - else if (translation >= m_upperTranslation) - { - C = b2Max(translation - m_upperTranslation, 0.0f); - } - - if (C != 0.0f) - { - - float invMass = m_invMassA + m_invMassB + m_invIA * sAx * sAx + m_invIB * sBx * sBx; - float impulse = 0.0f; - if (invMass != 0.0f) - { - impulse = -C / invMass; - } - - b2Vec2 P = impulse * ax; - float LA = impulse * sAx; - float LB = impulse * sBx; - - cA -= m_invMassA * P; - aA -= m_invIA * LA; - cB += m_invMassB * P; - aB += m_invIB * LB; - - linearError = b2Abs(C); - } - } - - // Solve perpendicular constraint - { - b2Rot qA(aA), qB(aB); - - b2Vec2 rA = b2Mul(qA, m_localAnchorA - m_localCenterA); - b2Vec2 rB = b2Mul(qB, m_localAnchorB - m_localCenterB); - b2Vec2 d = (cB - cA) + rB - rA; - - b2Vec2 ay = b2Mul(qA, m_localYAxisA); - - float sAy = b2Cross(d + rA, ay); - float sBy = b2Cross(rB, ay); - - float C = b2Dot(d, ay); - - float invMass = m_invMassA + m_invMassB + m_invIA * m_sAy * m_sAy + m_invIB * m_sBy * m_sBy; - - float impulse = 0.0f; - if (invMass != 0.0f) - { - impulse = - C / invMass; - } - - b2Vec2 P = impulse * ay; - float LA = impulse * sAy; - float LB = impulse * sBy; - - cA -= m_invMassA * P; - aA -= m_invIA * LA; - cB += m_invMassB * P; - aB += m_invIB * LB; - - linearError = b2Max(linearError, b2Abs(C)); - } - - data.positions[m_indexA].c = cA; - data.positions[m_indexA].a = aA; - data.positions[m_indexB].c = cB; - data.positions[m_indexB].a = aB; - - return linearError <= b2_linearSlop; -} - -b2Vec2 b2WheelJoint::GetAnchorA() const -{ - return m_bodyA->GetWorldPoint(m_localAnchorA); -} - -b2Vec2 b2WheelJoint::GetAnchorB() const -{ - return m_bodyB->GetWorldPoint(m_localAnchorB); -} - -b2Vec2 b2WheelJoint::GetReactionForce(float inv_dt) const -{ - return inv_dt * (m_impulse * m_ay + (m_springImpulse + m_lowerImpulse - m_upperImpulse) * m_ax); -} - -float b2WheelJoint::GetReactionTorque(float inv_dt) const -{ - return inv_dt * m_motorImpulse; -} - -float b2WheelJoint::GetJointTranslation() const -{ - b2Body* bA = m_bodyA; - b2Body* bB = m_bodyB; - - b2Vec2 pA = bA->GetWorldPoint(m_localAnchorA); - b2Vec2 pB = bB->GetWorldPoint(m_localAnchorB); - b2Vec2 d = pB - pA; - b2Vec2 axis = bA->GetWorldVector(m_localXAxisA); - - float translation = b2Dot(d, axis); - return translation; -} - -float b2WheelJoint::GetJointLinearSpeed() const -{ - b2Body* bA = m_bodyA; - b2Body* bB = m_bodyB; - - b2Vec2 rA = b2Mul(bA->m_xf.q, m_localAnchorA - bA->m_sweep.localCenter); - b2Vec2 rB = b2Mul(bB->m_xf.q, m_localAnchorB - bB->m_sweep.localCenter); - b2Vec2 p1 = bA->m_sweep.c + rA; - b2Vec2 p2 = bB->m_sweep.c + rB; - b2Vec2 d = p2 - p1; - b2Vec2 axis = b2Mul(bA->m_xf.q, m_localXAxisA); - - b2Vec2 vA = bA->m_linearVelocity; - b2Vec2 vB = bB->m_linearVelocity; - float wA = bA->m_angularVelocity; - float wB = bB->m_angularVelocity; - - float speed = b2Dot(d, b2Cross(wA, axis)) + b2Dot(axis, vB + b2Cross(wB, rB) - vA - b2Cross(wA, rA)); - return speed; -} - -float b2WheelJoint::GetJointAngle() const -{ - b2Body* bA = m_bodyA; - b2Body* bB = m_bodyB; - return bB->m_sweep.a - bA->m_sweep.a; -} - -float b2WheelJoint::GetJointAngularSpeed() const -{ - float wA = m_bodyA->m_angularVelocity; - float wB = m_bodyB->m_angularVelocity; - return wB - wA; -} - -bool b2WheelJoint::IsLimitEnabled() const -{ - return m_enableLimit; -} - -void b2WheelJoint::EnableLimit(bool flag) -{ - if (flag != m_enableLimit) - { - m_bodyA->SetAwake(true); - m_bodyB->SetAwake(true); - m_enableLimit = flag; - m_lowerImpulse = 0.0f; - m_upperImpulse = 0.0f; - } -} - -float b2WheelJoint::GetLowerLimit() const -{ - return m_lowerTranslation; -} - -float b2WheelJoint::GetUpperLimit() const -{ - return m_upperTranslation; -} - -void b2WheelJoint::SetLimits(float lower, float upper) -{ - b2Assert(lower <= upper); - if (lower != m_lowerTranslation || upper != m_upperTranslation) - { - m_bodyA->SetAwake(true); - m_bodyB->SetAwake(true); - m_lowerTranslation = lower; - m_upperTranslation = upper; - m_lowerImpulse = 0.0f; - m_upperImpulse = 0.0f; - } -} - -bool b2WheelJoint::IsMotorEnabled() const -{ - return m_enableMotor; -} - -void b2WheelJoint::EnableMotor(bool flag) -{ - if (flag != m_enableMotor) - { - m_bodyA->SetAwake(true); - m_bodyB->SetAwake(true); - m_enableMotor = flag; - } -} - -void b2WheelJoint::SetMotorSpeed(float speed) -{ - if (speed != m_motorSpeed) - { - m_bodyA->SetAwake(true); - m_bodyB->SetAwake(true); - m_motorSpeed = speed; - } -} - -void b2WheelJoint::SetMaxMotorTorque(float torque) -{ - if (torque != m_maxMotorTorque) - { - m_bodyA->SetAwake(true); - m_bodyB->SetAwake(true); - m_maxMotorTorque = torque; - } -} - -float b2WheelJoint::GetMotorTorque(float inv_dt) const -{ - return inv_dt * m_motorImpulse; -} - -void b2WheelJoint::SetStiffness(float stiffness) -{ - m_stiffness = stiffness; -} - -float b2WheelJoint::GetStiffness() const -{ - return m_stiffness; -} - -void b2WheelJoint::SetDamping(float damping) -{ - m_damping = damping; -} - -float b2WheelJoint::GetDamping() const -{ - return m_damping; -} - -void b2WheelJoint::Dump() -{ - // FLT_DECIMAL_DIG == 9 - - int32 indexA = m_bodyA->m_islandIndex; - int32 indexB = m_bodyB->m_islandIndex; - - b2Dump(" b2WheelJointDef jd;\n"); - b2Dump(" jd.bodyA = bodies[%d];\n", indexA); - b2Dump(" jd.bodyB = bodies[%d];\n", indexB); - b2Dump(" jd.collideConnected = bool(%d);\n", m_collideConnected); - b2Dump(" jd.localAnchorA.Set(%.9g, %.9g);\n", m_localAnchorA.x, m_localAnchorA.y); - b2Dump(" jd.localAnchorB.Set(%.9g, %.9g);\n", m_localAnchorB.x, m_localAnchorB.y); - b2Dump(" jd.localAxisA.Set(%.9g, %.9g);\n", m_localXAxisA.x, m_localXAxisA.y); - b2Dump(" jd.enableMotor = bool(%d);\n", m_enableMotor); - b2Dump(" jd.motorSpeed = %.9g;\n", m_motorSpeed); - b2Dump(" jd.maxMotorTorque = %.9g;\n", m_maxMotorTorque); - b2Dump(" jd.stiffness = %.9g;\n", m_stiffness); - b2Dump(" jd.damping = %.9g;\n", m_damping); - b2Dump(" joints[%d] = m_world->CreateJoint(&jd);\n", m_index); -} - -/// -void b2WheelJoint::Draw(b2Draw* draw) const -{ - const b2Transform& xfA = m_bodyA->GetTransform(); - const b2Transform& xfB = m_bodyB->GetTransform(); - b2Vec2 pA = b2Mul(xfA, m_localAnchorA); - b2Vec2 pB = b2Mul(xfB, m_localAnchorB); - - b2Vec2 axis = b2Mul(xfA.q, m_localXAxisA); - - b2Color c1(0.7f, 0.7f, 0.7f); - b2Color c2(0.3f, 0.9f, 0.3f); - b2Color c3(0.9f, 0.3f, 0.3f); - b2Color c4(0.3f, 0.3f, 0.9f); - b2Color c5(0.4f, 0.4f, 0.4f); - - draw->DrawSegment(pA, pB, c5); - - if (m_enableLimit) - { - b2Vec2 lower = pA + m_lowerTranslation * axis; - b2Vec2 upper = pA + m_upperTranslation * axis; - b2Vec2 perp = b2Mul(xfA.q, m_localYAxisA); - draw->DrawSegment(lower, upper, c1); - draw->DrawSegment(lower - 0.5f * perp, lower + 0.5f * perp, c2); - draw->DrawSegment(upper - 0.5f * perp, upper + 0.5f * perp, c3); - } - else - { - draw->DrawSegment(pA - 1.0f * axis, pA + 1.0f * axis, c1); - } - - draw->DrawPoint(pA, 5.0f, c1); - draw->DrawPoint(pB, 5.0f, c4); -} diff --git a/3rdparty/box2d/src/dynamics/b2_world.cpp b/3rdparty/box2d/src/dynamics/b2_world.cpp deleted file mode 100644 index 78ec084c800b..000000000000 --- a/3rdparty/box2d/src/dynamics/b2_world.cpp +++ /dev/null @@ -1,1322 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "b2_contact_solver.h" -#include "b2_island.h" - -#include "box2d/b2_body.h" -#include "box2d/b2_broad_phase.h" -#include "box2d/b2_chain_shape.h" -#include "box2d/b2_circle_shape.h" -#include "box2d/b2_collision.h" -#include "box2d/b2_contact.h" -#include "box2d/b2_draw.h" -#include "box2d/b2_edge_shape.h" -#include "box2d/b2_fixture.h" -#include "box2d/b2_polygon_shape.h" -#include "box2d/b2_pulley_joint.h" -#include "box2d/b2_time_of_impact.h" -#include "box2d/b2_timer.h" -#include "box2d/b2_world.h" - -#include - -b2World::b2World(const b2Vec2& gravity) -{ - m_destructionListener = nullptr; - m_debugDraw = nullptr; - - m_bodyList = nullptr; - m_jointList = nullptr; - - m_bodyCount = 0; - m_jointCount = 0; - - m_warmStarting = true; - m_continuousPhysics = true; - m_subStepping = false; - - m_stepComplete = true; - - m_allowSleep = true; - m_gravity = gravity; - - m_newContacts = false; - m_locked = false; - m_clearForces = true; - - m_inv_dt0 = 0.0f; - - m_contactManager.m_allocator = &m_blockAllocator; - - memset(&m_profile, 0, sizeof(b2Profile)); -} - -b2World::~b2World() -{ - // Some shapes allocate using b2Alloc. - b2Body* b = m_bodyList; - while (b) - { - b2Body* bNext = b->m_next; - - b2Fixture* f = b->m_fixtureList; - while (f) - { - b2Fixture* fNext = f->m_next; - f->m_proxyCount = 0; - f->Destroy(&m_blockAllocator); - f = fNext; - } - - b = bNext; - } -} - -void b2World::SetDestructionListener(b2DestructionListener* listener) -{ - m_destructionListener = listener; -} - -void b2World::SetContactFilter(b2ContactFilter* filter) -{ - m_contactManager.m_contactFilter = filter; -} - -void b2World::SetContactListener(b2ContactListener* listener) -{ - m_contactManager.m_contactListener = listener; -} - -void b2World::SetDebugDraw(b2Draw* debugDraw) -{ - m_debugDraw = debugDraw; -} - -b2Body* b2World::CreateBody(const b2BodyDef* def) -{ - b2Assert(IsLocked() == false); - if (IsLocked()) - { - return nullptr; - } - - void* mem = m_blockAllocator.Allocate(sizeof(b2Body)); - b2Body* b = new (mem) b2Body(def, this); - - // Add to world doubly linked list. - b->m_prev = nullptr; - b->m_next = m_bodyList; - if (m_bodyList) - { - m_bodyList->m_prev = b; - } - m_bodyList = b; - ++m_bodyCount; - - return b; -} - -void b2World::DestroyBody(b2Body* b) -{ - b2Assert(m_bodyCount > 0); - b2Assert(IsLocked() == false); - if (IsLocked()) - { - return; - } - - // Delete the attached joints. - b2JointEdge* je = b->m_jointList; - while (je) - { - b2JointEdge* je0 = je; - je = je->next; - - if (m_destructionListener) - { - m_destructionListener->SayGoodbye(je0->joint); - } - - DestroyJoint(je0->joint); - - b->m_jointList = je; - } - b->m_jointList = nullptr; - - // Delete the attached contacts. - b2ContactEdge* ce = b->m_contactList; - while (ce) - { - b2ContactEdge* ce0 = ce; - ce = ce->next; - m_contactManager.Destroy(ce0->contact); - } - b->m_contactList = nullptr; - - // Delete the attached fixtures. This destroys broad-phase proxies. - b2Fixture* f = b->m_fixtureList; - while (f) - { - b2Fixture* f0 = f; - f = f->m_next; - - if (m_destructionListener) - { - m_destructionListener->SayGoodbye(f0); - } - - f0->DestroyProxies(&m_contactManager.m_broadPhase); - f0->Destroy(&m_blockAllocator); - f0->~b2Fixture(); - m_blockAllocator.Free(f0, sizeof(b2Fixture)); - - b->m_fixtureList = f; - b->m_fixtureCount -= 1; - } - b->m_fixtureList = nullptr; - b->m_fixtureCount = 0; - - // Remove world body list. - if (b->m_prev) - { - b->m_prev->m_next = b->m_next; - } - - if (b->m_next) - { - b->m_next->m_prev = b->m_prev; - } - - if (b == m_bodyList) - { - m_bodyList = b->m_next; - } - - --m_bodyCount; - b->~b2Body(); - m_blockAllocator.Free(b, sizeof(b2Body)); -} - -b2Joint* b2World::CreateJoint(const b2JointDef* def) -{ - b2Assert(IsLocked() == false); - if (IsLocked()) - { - return nullptr; - } - - b2Joint* j = b2Joint::Create(def, &m_blockAllocator); - - // Connect to the world list. - j->m_prev = nullptr; - j->m_next = m_jointList; - if (m_jointList) - { - m_jointList->m_prev = j; - } - m_jointList = j; - ++m_jointCount; - - // Connect to the bodies' doubly linked lists. - j->m_edgeA.joint = j; - j->m_edgeA.other = j->m_bodyB; - j->m_edgeA.prev = nullptr; - j->m_edgeA.next = j->m_bodyA->m_jointList; - if (j->m_bodyA->m_jointList) j->m_bodyA->m_jointList->prev = &j->m_edgeA; - j->m_bodyA->m_jointList = &j->m_edgeA; - - j->m_edgeB.joint = j; - j->m_edgeB.other = j->m_bodyA; - j->m_edgeB.prev = nullptr; - j->m_edgeB.next = j->m_bodyB->m_jointList; - if (j->m_bodyB->m_jointList) j->m_bodyB->m_jointList->prev = &j->m_edgeB; - j->m_bodyB->m_jointList = &j->m_edgeB; - - b2Body* bodyA = def->bodyA; - b2Body* bodyB = def->bodyB; - - // If the joint prevents collisions, then flag any contacts for filtering. - if (def->collideConnected == false) - { - b2ContactEdge* edge = bodyB->GetContactList(); - while (edge) - { - if (edge->other == bodyA) - { - // Flag the contact for filtering at the next time step (where either - // body is awake). - edge->contact->FlagForFiltering(); - } - - edge = edge->next; - } - } - - // Note: creating a joint doesn't wake the bodies. - - return j; -} - -void b2World::DestroyJoint(b2Joint* j) -{ - b2Assert(IsLocked() == false); - if (IsLocked()) - { - return; - } - - bool collideConnected = j->m_collideConnected; - - // Remove from the doubly linked list. - if (j->m_prev) - { - j->m_prev->m_next = j->m_next; - } - - if (j->m_next) - { - j->m_next->m_prev = j->m_prev; - } - - if (j == m_jointList) - { - m_jointList = j->m_next; - } - - // Disconnect from island graph. - b2Body* bodyA = j->m_bodyA; - b2Body* bodyB = j->m_bodyB; - - // Wake up connected bodies. - bodyA->SetAwake(true); - bodyB->SetAwake(true); - - // Remove from body 1. - if (j->m_edgeA.prev) - { - j->m_edgeA.prev->next = j->m_edgeA.next; - } - - if (j->m_edgeA.next) - { - j->m_edgeA.next->prev = j->m_edgeA.prev; - } - - if (&j->m_edgeA == bodyA->m_jointList) - { - bodyA->m_jointList = j->m_edgeA.next; - } - - j->m_edgeA.prev = nullptr; - j->m_edgeA.next = nullptr; - - // Remove from body 2 - if (j->m_edgeB.prev) - { - j->m_edgeB.prev->next = j->m_edgeB.next; - } - - if (j->m_edgeB.next) - { - j->m_edgeB.next->prev = j->m_edgeB.prev; - } - - if (&j->m_edgeB == bodyB->m_jointList) - { - bodyB->m_jointList = j->m_edgeB.next; - } - - j->m_edgeB.prev = nullptr; - j->m_edgeB.next = nullptr; - - b2Joint::Destroy(j, &m_blockAllocator); - - b2Assert(m_jointCount > 0); - --m_jointCount; - - // If the joint prevents collisions, then flag any contacts for filtering. - if (collideConnected == false) - { - b2ContactEdge* edge = bodyB->GetContactList(); - while (edge) - { - if (edge->other == bodyA) - { - // Flag the contact for filtering at the next time step (where either - // body is awake). - edge->contact->FlagForFiltering(); - } - - edge = edge->next; - } - } -} - -// -void b2World::SetAllowSleeping(bool flag) -{ - if (flag == m_allowSleep) - { - return; - } - - m_allowSleep = flag; - if (m_allowSleep == false) - { - for (b2Body* b = m_bodyList; b; b = b->m_next) - { - b->SetAwake(true); - } - } -} - -// Find islands, integrate and solve constraints, solve position constraints -void b2World::Solve(const b2TimeStep& step) -{ - m_profile.solveInit = 0.0f; - m_profile.solveVelocity = 0.0f; - m_profile.solvePosition = 0.0f; - - // Size the island for the worst case. - b2Island island(m_bodyCount, - m_contactManager.m_contactCount, - m_jointCount, - &m_stackAllocator, - m_contactManager.m_contactListener); - - // Clear all the island flags. - for (b2Body* b = m_bodyList; b; b = b->m_next) - { - b->m_flags &= ~b2Body::e_islandFlag; - } - for (b2Contact* c = m_contactManager.m_contactList; c; c = c->m_next) - { - c->m_flags &= ~b2Contact::e_islandFlag; - } - for (b2Joint* j = m_jointList; j; j = j->m_next) - { - j->m_islandFlag = false; - } - - // Build and simulate all awake islands. - int32 stackSize = m_bodyCount; - b2Body** stack = (b2Body**)m_stackAllocator.Allocate(stackSize * sizeof(b2Body*)); - for (b2Body* seed = m_bodyList; seed; seed = seed->m_next) - { - if (seed->m_flags & b2Body::e_islandFlag) - { - continue; - } - - if (seed->IsAwake() == false || seed->IsEnabled() == false) - { - continue; - } - - // The seed can be dynamic or kinematic. - if (seed->GetType() == b2_staticBody) - { - continue; - } - - // Reset island and stack. - island.Clear(); - int32 stackCount = 0; - stack[stackCount++] = seed; - seed->m_flags |= b2Body::e_islandFlag; - - // Perform a depth first search (DFS) on the constraint graph. - while (stackCount > 0) - { - // Grab the next body off the stack and add it to the island. - b2Body* b = stack[--stackCount]; - b2Assert(b->IsEnabled() == true); - island.Add(b); - - // To keep islands as small as possible, we don't - // propagate islands across static bodies. - if (b->GetType() == b2_staticBody) - { - continue; - } - - // Make sure the body is awake (without resetting sleep timer). - b->m_flags |= b2Body::e_awakeFlag; - - // Search all contacts connected to this body. - for (b2ContactEdge* ce = b->m_contactList; ce; ce = ce->next) - { - b2Contact* contact = ce->contact; - - // Has this contact already been added to an island? - if (contact->m_flags & b2Contact::e_islandFlag) - { - continue; - } - - // Is this contact solid and touching? - if (contact->IsEnabled() == false || - contact->IsTouching() == false) - { - continue; - } - - // Skip sensors. - bool sensorA = contact->m_fixtureA->m_isSensor; - bool sensorB = contact->m_fixtureB->m_isSensor; - if (sensorA || sensorB) - { - continue; - } - - island.Add(contact); - contact->m_flags |= b2Contact::e_islandFlag; - - b2Body* other = ce->other; - - // Was the other body already added to this island? - if (other->m_flags & b2Body::e_islandFlag) - { - continue; - } - - b2Assert(stackCount < stackSize); - stack[stackCount++] = other; - other->m_flags |= b2Body::e_islandFlag; - } - - // Search all joints connect to this body. - for (b2JointEdge* je = b->m_jointList; je; je = je->next) - { - if (je->joint->m_islandFlag == true) - { - continue; - } - - b2Body* other = je->other; - - // Don't simulate joints connected to disabled bodies. - if (other->IsEnabled() == false) - { - continue; - } - - island.Add(je->joint); - je->joint->m_islandFlag = true; - - if (other->m_flags & b2Body::e_islandFlag) - { - continue; - } - - b2Assert(stackCount < stackSize); - stack[stackCount++] = other; - other->m_flags |= b2Body::e_islandFlag; - } - } - - b2Profile profile; - island.Solve(&profile, step, m_gravity, m_allowSleep); - m_profile.solveInit += profile.solveInit; - m_profile.solveVelocity += profile.solveVelocity; - m_profile.solvePosition += profile.solvePosition; - - // Post solve cleanup. - for (int32 i = 0; i < island.m_bodyCount; ++i) - { - // Allow static bodies to participate in other islands. - b2Body* b = island.m_bodies[i]; - if (b->GetType() == b2_staticBody) - { - b->m_flags &= ~b2Body::e_islandFlag; - } - } - } - - m_stackAllocator.Free(stack); - - { - b2Timer timer; - // Synchronize fixtures, check for out of range bodies. - for (b2Body* b = m_bodyList; b; b = b->GetNext()) - { - // If a body was not in an island then it did not move. - if ((b->m_flags & b2Body::e_islandFlag) == 0) - { - continue; - } - - if (b->GetType() == b2_staticBody) - { - continue; - } - - // Update fixtures (for broad-phase). - b->SynchronizeFixtures(); - } - - // Look for new contacts. - m_contactManager.FindNewContacts(); - m_profile.broadphase = timer.GetMilliseconds(); - } -} - -// Find TOI contacts and solve them. -void b2World::SolveTOI(const b2TimeStep& step) -{ - b2Island island(2 * b2_maxTOIContacts, b2_maxTOIContacts, 0, &m_stackAllocator, m_contactManager.m_contactListener); - - if (m_stepComplete) - { - for (b2Body* b = m_bodyList; b; b = b->m_next) - { - b->m_flags &= ~b2Body::e_islandFlag; - b->m_sweep.alpha0 = 0.0f; - } - - for (b2Contact* c = m_contactManager.m_contactList; c; c = c->m_next) - { - // Invalidate TOI - c->m_flags &= ~(b2Contact::e_toiFlag | b2Contact::e_islandFlag); - c->m_toiCount = 0; - c->m_toi = 1.0f; - } - } - - // Find TOI events and solve them. - for (;;) - { - // Find the first TOI. - b2Contact* minContact = nullptr; - float minAlpha = 1.0f; - - for (b2Contact* c = m_contactManager.m_contactList; c; c = c->m_next) - { - // Is this contact disabled? - if (c->IsEnabled() == false) - { - continue; - } - - // Prevent excessive sub-stepping. - if (c->m_toiCount > b2_maxSubSteps) - { - continue; - } - - float alpha = 1.0f; - if (c->m_flags & b2Contact::e_toiFlag) - { - // This contact has a valid cached TOI. - alpha = c->m_toi; - } - else - { - b2Fixture* fA = c->GetFixtureA(); - b2Fixture* fB = c->GetFixtureB(); - - // Is there a sensor? - if (fA->IsSensor() || fB->IsSensor()) - { - continue; - } - - b2Body* bA = fA->GetBody(); - b2Body* bB = fB->GetBody(); - - b2BodyType typeA = bA->m_type; - b2BodyType typeB = bB->m_type; - b2Assert(typeA == b2_dynamicBody || typeB == b2_dynamicBody); - - bool activeA = bA->IsAwake() && typeA != b2_staticBody; - bool activeB = bB->IsAwake() && typeB != b2_staticBody; - - // Is at least one body active (awake and dynamic or kinematic)? - if (activeA == false && activeB == false) - { - continue; - } - - bool collideA = bA->IsBullet() || typeA != b2_dynamicBody; - bool collideB = bB->IsBullet() || typeB != b2_dynamicBody; - - // Are these two non-bullet dynamic bodies? - if (collideA == false && collideB == false) - { - continue; - } - - // Compute the TOI for this contact. - // Put the sweeps onto the same time interval. - float alpha0 = bA->m_sweep.alpha0; - - if (bA->m_sweep.alpha0 < bB->m_sweep.alpha0) - { - alpha0 = bB->m_sweep.alpha0; - bA->m_sweep.Advance(alpha0); - } - else if (bB->m_sweep.alpha0 < bA->m_sweep.alpha0) - { - alpha0 = bA->m_sweep.alpha0; - bB->m_sweep.Advance(alpha0); - } - - b2Assert(alpha0 < 1.0f); - - int32 indexA = c->GetChildIndexA(); - int32 indexB = c->GetChildIndexB(); - - // Compute the time of impact in interval [0, minTOI] - b2TOIInput input; - input.proxyA.Set(fA->GetShape(), indexA); - input.proxyB.Set(fB->GetShape(), indexB); - input.sweepA = bA->m_sweep; - input.sweepB = bB->m_sweep; - input.tMax = 1.0f; - - b2TOIOutput output; - b2TimeOfImpact(&output, &input); - - // Beta is the fraction of the remaining portion of the . - float beta = output.t; - if (output.state == b2TOIOutput::e_touching) - { - alpha = b2Min(alpha0 + (1.0f - alpha0) * beta, 1.0f); - } - else - { - alpha = 1.0f; - } - - c->m_toi = alpha; - c->m_flags |= b2Contact::e_toiFlag; - } - - if (alpha < minAlpha) - { - // This is the minimum TOI found so far. - minContact = c; - minAlpha = alpha; - } - } - - if (minContact == nullptr || 1.0f - 10.0f * b2_epsilon < minAlpha) - { - // No more TOI events. Done! - m_stepComplete = true; - break; - } - - // Advance the bodies to the TOI. - b2Fixture* fA = minContact->GetFixtureA(); - b2Fixture* fB = minContact->GetFixtureB(); - b2Body* bA = fA->GetBody(); - b2Body* bB = fB->GetBody(); - - b2Sweep backup1 = bA->m_sweep; - b2Sweep backup2 = bB->m_sweep; - - bA->Advance(minAlpha); - bB->Advance(minAlpha); - - // The TOI contact likely has some new contact points. - minContact->Update(m_contactManager.m_contactListener); - minContact->m_flags &= ~b2Contact::e_toiFlag; - ++minContact->m_toiCount; - - // Is the contact solid? - if (minContact->IsEnabled() == false || minContact->IsTouching() == false) - { - // Restore the sweeps. - minContact->SetEnabled(false); - bA->m_sweep = backup1; - bB->m_sweep = backup2; - bA->SynchronizeTransform(); - bB->SynchronizeTransform(); - continue; - } - - bA->SetAwake(true); - bB->SetAwake(true); - - // Build the island - island.Clear(); - island.Add(bA); - island.Add(bB); - island.Add(minContact); - - bA->m_flags |= b2Body::e_islandFlag; - bB->m_flags |= b2Body::e_islandFlag; - minContact->m_flags |= b2Contact::e_islandFlag; - - // Get contacts on bodyA and bodyB. - b2Body* bodies[2] = {bA, bB}; - for (int32 i = 0; i < 2; ++i) - { - b2Body* body = bodies[i]; - if (body->m_type == b2_dynamicBody) - { - for (b2ContactEdge* ce = body->m_contactList; ce; ce = ce->next) - { - if (island.m_bodyCount == island.m_bodyCapacity) - { - break; - } - - if (island.m_contactCount == island.m_contactCapacity) - { - break; - } - - b2Contact* contact = ce->contact; - - // Has this contact already been added to the island? - if (contact->m_flags & b2Contact::e_islandFlag) - { - continue; - } - - // Only add static, kinematic, or bullet bodies. - b2Body* other = ce->other; - if (other->m_type == b2_dynamicBody && - body->IsBullet() == false && other->IsBullet() == false) - { - continue; - } - - // Skip sensors. - bool sensorA = contact->m_fixtureA->m_isSensor; - bool sensorB = contact->m_fixtureB->m_isSensor; - if (sensorA || sensorB) - { - continue; - } - - // Tentatively advance the body to the TOI. - b2Sweep backup = other->m_sweep; - if ((other->m_flags & b2Body::e_islandFlag) == 0) - { - other->Advance(minAlpha); - } - - // Update the contact points - contact->Update(m_contactManager.m_contactListener); - - // Was the contact disabled by the user? - if (contact->IsEnabled() == false) - { - other->m_sweep = backup; - other->SynchronizeTransform(); - continue; - } - - // Are there contact points? - if (contact->IsTouching() == false) - { - other->m_sweep = backup; - other->SynchronizeTransform(); - continue; - } - - // Add the contact to the island - contact->m_flags |= b2Contact::e_islandFlag; - island.Add(contact); - - // Has the other body already been added to the island? - if (other->m_flags & b2Body::e_islandFlag) - { - continue; - } - - // Add the other body to the island. - other->m_flags |= b2Body::e_islandFlag; - - if (other->m_type != b2_staticBody) - { - other->SetAwake(true); - } - - island.Add(other); - } - } - } - - b2TimeStep subStep; - subStep.dt = (1.0f - minAlpha) * step.dt; - subStep.inv_dt = 1.0f / subStep.dt; - subStep.dtRatio = 1.0f; - subStep.positionIterations = 20; - subStep.velocityIterations = step.velocityIterations; - subStep.warmStarting = false; - island.SolveTOI(subStep, bA->m_islandIndex, bB->m_islandIndex); - - // Reset island flags and synchronize broad-phase proxies. - for (int32 i = 0; i < island.m_bodyCount; ++i) - { - b2Body* body = island.m_bodies[i]; - body->m_flags &= ~b2Body::e_islandFlag; - - if (body->m_type != b2_dynamicBody) - { - continue; - } - - body->SynchronizeFixtures(); - - // Invalidate all contact TOIs on this displaced body. - for (b2ContactEdge* ce = body->m_contactList; ce; ce = ce->next) - { - ce->contact->m_flags &= ~(b2Contact::e_toiFlag | b2Contact::e_islandFlag); - } - } - - // Commit fixture proxy movements to the broad-phase so that new contacts are created. - // Also, some contacts can be destroyed. - m_contactManager.FindNewContacts(); - - if (m_subStepping) - { - m_stepComplete = false; - break; - } - } -} - -void b2World::Step(float dt, int32 velocityIterations, int32 positionIterations) -{ - b2Timer stepTimer; - - // If new fixtures were added, we need to find the new contacts. - if (m_newContacts) - { - m_contactManager.FindNewContacts(); - m_newContacts = false; - } - - m_locked = true; - - b2TimeStep step; - step.dt = dt; - step.velocityIterations = velocityIterations; - step.positionIterations = positionIterations; - if (dt > 0.0f) - { - step.inv_dt = 1.0f / dt; - } - else - { - step.inv_dt = 0.0f; - } - - step.dtRatio = m_inv_dt0 * dt; - - step.warmStarting = m_warmStarting; - - // Update contacts. This is where some contacts are destroyed. - { - b2Timer timer; - m_contactManager.Collide(); - m_profile.collide = timer.GetMilliseconds(); - } - - // Integrate velocities, solve velocity constraints, and integrate positions. - if (m_stepComplete && step.dt > 0.0f) - { - b2Timer timer; - Solve(step); - m_profile.solve = timer.GetMilliseconds(); - } - - // Handle TOI events. - if (m_continuousPhysics && step.dt > 0.0f) - { - b2Timer timer; - SolveTOI(step); - m_profile.solveTOI = timer.GetMilliseconds(); - } - - if (step.dt > 0.0f) - { - m_inv_dt0 = step.inv_dt; - } - - if (m_clearForces) - { - ClearForces(); - } - - m_locked = false; - - m_profile.step = stepTimer.GetMilliseconds(); -} - -void b2World::ClearForces() -{ - for (b2Body* body = m_bodyList; body; body = body->GetNext()) - { - body->m_force.SetZero(); - body->m_torque = 0.0f; - } -} - -struct b2WorldQueryWrapper -{ - bool QueryCallback(int32 proxyId) - { - b2FixtureProxy* proxy = (b2FixtureProxy*)broadPhase->GetUserData(proxyId); - return callback->ReportFixture(proxy->fixture); - } - - const b2BroadPhase* broadPhase; - b2QueryCallback* callback; -}; - -void b2World::QueryAABB(b2QueryCallback* callback, const b2AABB& aabb) const -{ - b2WorldQueryWrapper wrapper; - wrapper.broadPhase = &m_contactManager.m_broadPhase; - wrapper.callback = callback; - m_contactManager.m_broadPhase.Query(&wrapper, aabb); -} - -struct b2WorldRayCastWrapper -{ - float RayCastCallback(const b2RayCastInput& input, int32 proxyId) - { - void* userData = broadPhase->GetUserData(proxyId); - b2FixtureProxy* proxy = (b2FixtureProxy*)userData; - b2Fixture* fixture = proxy->fixture; - int32 index = proxy->childIndex; - b2RayCastOutput output; - bool hit = fixture->RayCast(&output, input, index); - - if (hit) - { - float fraction = output.fraction; - b2Vec2 point = (1.0f - fraction) * input.p1 + fraction * input.p2; - return callback->ReportFixture(fixture, point, output.normal, fraction); - } - - return input.maxFraction; - } - - const b2BroadPhase* broadPhase; - b2RayCastCallback* callback; -}; - -void b2World::RayCast(b2RayCastCallback* callback, const b2Vec2& point1, const b2Vec2& point2) const -{ - b2WorldRayCastWrapper wrapper; - wrapper.broadPhase = &m_contactManager.m_broadPhase; - wrapper.callback = callback; - b2RayCastInput input; - input.maxFraction = 1.0f; - input.p1 = point1; - input.p2 = point2; - m_contactManager.m_broadPhase.RayCast(&wrapper, input); -} - -void b2World::DrawShape(b2Fixture* fixture, const b2Transform& xf, const b2Color& color) -{ - switch (fixture->GetType()) - { - case b2Shape::e_circle: - { - b2CircleShape* circle = (b2CircleShape*)fixture->GetShape(); - - b2Vec2 center = b2Mul(xf, circle->m_p); - float radius = circle->m_radius; - b2Vec2 axis = b2Mul(xf.q, b2Vec2(1.0f, 0.0f)); - - m_debugDraw->DrawSolidCircle(center, radius, axis, color); - } - break; - - case b2Shape::e_edge: - { - b2EdgeShape* edge = (b2EdgeShape*)fixture->GetShape(); - b2Vec2 v1 = b2Mul(xf, edge->m_vertex1); - b2Vec2 v2 = b2Mul(xf, edge->m_vertex2); - m_debugDraw->DrawSegment(v1, v2, color); - - if (edge->m_oneSided == false) - { - m_debugDraw->DrawPoint(v1, 4.0f, color); - m_debugDraw->DrawPoint(v2, 4.0f, color); - } - } - break; - - case b2Shape::e_chain: - { - b2ChainShape* chain = (b2ChainShape*)fixture->GetShape(); - int32 count = chain->m_count; - const b2Vec2* vertices = chain->m_vertices; - - b2Vec2 v1 = b2Mul(xf, vertices[0]); - for (int32 i = 1; i < count; ++i) - { - b2Vec2 v2 = b2Mul(xf, vertices[i]); - m_debugDraw->DrawSegment(v1, v2, color); - v1 = v2; - } - } - break; - - case b2Shape::e_polygon: - { - b2PolygonShape* poly = (b2PolygonShape*)fixture->GetShape(); - int32 vertexCount = poly->m_count; - b2Assert(vertexCount <= b2_maxPolygonVertices); - b2Vec2 vertices[b2_maxPolygonVertices]; - - for (int32 i = 0; i < vertexCount; ++i) - { - vertices[i] = b2Mul(xf, poly->m_vertices[i]); - } - - m_debugDraw->DrawSolidPolygon(vertices, vertexCount, color); - } - break; - - default: - break; - } -} - -void b2World::DebugDraw() -{ - if (m_debugDraw == nullptr) - { - return; - } - - uint32 flags = m_debugDraw->GetFlags(); - - if (flags & b2Draw::e_shapeBit) - { - for (b2Body* b = m_bodyList; b; b = b->GetNext()) - { - const b2Transform& xf = b->GetTransform(); - for (b2Fixture* f = b->GetFixtureList(); f; f = f->GetNext()) - { - if (b->GetType() == b2_dynamicBody && b->m_mass == 0.0f) - { - // Bad body - DrawShape(f, xf, b2Color(1.0f, 0.0f, 0.0f)); - } - else if (b->IsEnabled() == false) - { - DrawShape(f, xf, b2Color(0.5f, 0.5f, 0.3f)); - } - else if (b->GetType() == b2_staticBody) - { - DrawShape(f, xf, b2Color(0.5f, 0.9f, 0.5f)); - } - else if (b->GetType() == b2_kinematicBody) - { - DrawShape(f, xf, b2Color(0.5f, 0.5f, 0.9f)); - } - else if (b->IsAwake() == false) - { - DrawShape(f, xf, b2Color(0.6f, 0.6f, 0.6f)); - } - else - { - DrawShape(f, xf, b2Color(0.9f, 0.7f, 0.7f)); - } - } - } - } - - if (flags & b2Draw::e_jointBit) - { - for (b2Joint* j = m_jointList; j; j = j->GetNext()) - { - j->Draw(m_debugDraw); - } - } - - if (flags & b2Draw::e_pairBit) - { - b2Color color(0.3f, 0.9f, 0.9f); - for (b2Contact* c = m_contactManager.m_contactList; c; c = c->GetNext()) - { - b2Fixture* fixtureA = c->GetFixtureA(); - b2Fixture* fixtureB = c->GetFixtureB(); - int32 indexA = c->GetChildIndexA(); - int32 indexB = c->GetChildIndexB(); - b2Vec2 cA = fixtureA->GetAABB(indexA).GetCenter(); - b2Vec2 cB = fixtureB->GetAABB(indexB).GetCenter(); - - m_debugDraw->DrawSegment(cA, cB, color); - } - } - - if (flags & b2Draw::e_aabbBit) - { - b2Color color(0.9f, 0.3f, 0.9f); - b2BroadPhase* bp = &m_contactManager.m_broadPhase; - - for (b2Body* b = m_bodyList; b; b = b->GetNext()) - { - if (b->IsEnabled() == false) - { - continue; - } - - for (b2Fixture* f = b->GetFixtureList(); f; f = f->GetNext()) - { - for (int32 i = 0; i < f->m_proxyCount; ++i) - { - b2FixtureProxy* proxy = f->m_proxies + i; - b2AABB aabb = bp->GetFatAABB(proxy->proxyId); - b2Vec2 vs[4]; - vs[0].Set(aabb.lowerBound.x, aabb.lowerBound.y); - vs[1].Set(aabb.upperBound.x, aabb.lowerBound.y); - vs[2].Set(aabb.upperBound.x, aabb.upperBound.y); - vs[3].Set(aabb.lowerBound.x, aabb.upperBound.y); - - m_debugDraw->DrawPolygon(vs, 4, color); - } - } - } - } - - if (flags & b2Draw::e_centerOfMassBit) - { - for (b2Body* b = m_bodyList; b; b = b->GetNext()) - { - b2Transform xf = b->GetTransform(); - xf.p = b->GetWorldCenter(); - m_debugDraw->DrawTransform(xf); - } - } -} - -int32 b2World::GetProxyCount() const -{ - return m_contactManager.m_broadPhase.GetProxyCount(); -} - -int32 b2World::GetTreeHeight() const -{ - return m_contactManager.m_broadPhase.GetTreeHeight(); -} - -int32 b2World::GetTreeBalance() const -{ - return m_contactManager.m_broadPhase.GetTreeBalance(); -} - -float b2World::GetTreeQuality() const -{ - return m_contactManager.m_broadPhase.GetTreeQuality(); -} - -void b2World::ShiftOrigin(const b2Vec2& newOrigin) -{ - b2Assert(m_locked == false); - if (m_locked) - { - return; - } - - for (b2Body* b = m_bodyList; b; b = b->m_next) - { - b->m_xf.p -= newOrigin; - b->m_sweep.c0 -= newOrigin; - b->m_sweep.c -= newOrigin; - } - - for (b2Joint* j = m_jointList; j; j = j->m_next) - { - j->ShiftOrigin(newOrigin); - } - - m_contactManager.m_broadPhase.ShiftOrigin(newOrigin); -} - -void b2World::Dump() -{ - if (m_locked) - { - return; - } - - b2OpenDump("box2d_dump.inl"); - - b2Dump("b2Vec2 g(%.9g, %.9g);\n", m_gravity.x, m_gravity.y); - b2Dump("m_world->SetGravity(g);\n"); - - b2Dump("b2Body** bodies = (b2Body**)b2Alloc(%d * sizeof(b2Body*));\n", m_bodyCount); - b2Dump("b2Joint** joints = (b2Joint**)b2Alloc(%d * sizeof(b2Joint*));\n", m_jointCount); - - int32 i = 0; - for (b2Body* b = m_bodyList; b; b = b->m_next) - { - b->m_islandIndex = i; - b->Dump(); - ++i; - } - - i = 0; - for (b2Joint* j = m_jointList; j; j = j->m_next) - { - j->m_index = i; - ++i; - } - - // First pass on joints, skip gear joints. - for (b2Joint* j = m_jointList; j; j = j->m_next) - { - if (j->m_type == e_gearJoint) - { - continue; - } - - b2Dump("{\n"); - j->Dump(); - b2Dump("}\n"); - } - - // Second pass on joints, only gear joints. - for (b2Joint* j = m_jointList; j; j = j->m_next) - { - if (j->m_type != e_gearJoint) - { - continue; - } - - b2Dump("{\n"); - j->Dump(); - b2Dump("}\n"); - } - - b2Dump("b2Free(joints);\n"); - b2Dump("b2Free(bodies);\n"); - b2Dump("joints = nullptr;\n"); - b2Dump("bodies = nullptr;\n"); - - b2CloseDump(); -} diff --git a/3rdparty/box2d/src/dynamics/b2_world_callbacks.cpp b/3rdparty/box2d/src/dynamics/b2_world_callbacks.cpp deleted file mode 100644 index e1583e221b3b..000000000000 --- a/3rdparty/box2d/src/dynamics/b2_world_callbacks.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "box2d/b2_fixture.h" -#include "box2d/b2_world_callbacks.h" - -// Return true if contact calculations should be performed between these two shapes. -// If you implement your own collision filter you may want to build from this implementation. -bool b2ContactFilter::ShouldCollide(b2Fixture* fixtureA, b2Fixture* fixtureB) -{ - const b2Filter& filterA = fixtureA->GetFilterData(); - const b2Filter& filterB = fixtureB->GetFilterData(); - - if (filterA.groupIndex == filterB.groupIndex && filterA.groupIndex != 0) - { - return filterA.groupIndex > 0; - } - - bool collide = (filterA.maskBits & filterB.categoryBits) != 0 && (filterA.categoryBits & filterB.maskBits) != 0; - return collide; -} diff --git a/3rdparty/box2d/src/geometry.c b/3rdparty/box2d/src/geometry.c new file mode 100644 index 000000000000..94d33d3689ab --- /dev/null +++ b/3rdparty/box2d/src/geometry.c @@ -0,0 +1,893 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#include "aabb.h" +#include "core.h" +#include "shape.h" + +#include "box2d/collision.h" +#include "box2d/math_functions.h" + +#include +#include + +_Static_assert( b2_maxPolygonVertices > 2, "must be 3 or more" ); + +bool b2IsValidRay( const b2RayCastInput* input ) +{ + bool isValid = b2Vec2_IsValid( input->origin ) && b2Vec2_IsValid( input->translation ) && b2IsValid( input->maxFraction ) && + 0.0f <= input->maxFraction && input->maxFraction < b2_huge; + return isValid; +} + +static b2Vec2 b2ComputePolygonCentroid( const b2Vec2* vertices, int32_t count ) +{ + b2Vec2 center = { 0.0f, 0.0f }; + float area = 0.0f; + + // Get a reference point for forming triangles. + // Use the first vertex to reduce round-off errors. + b2Vec2 origin = vertices[0]; + + const float inv3 = 1.0f / 3.0f; + + for ( int32_t i = 1; i < count - 1; ++i ) + { + // Triangle edges + b2Vec2 e1 = b2Sub( vertices[i], origin ); + b2Vec2 e2 = b2Sub( vertices[i + 1], origin ); + float a = 0.5f * b2Cross( e1, e2 ); + + // Area weighted centroid + center = b2MulAdd( center, a * inv3, b2Add( e1, e2 ) ); + area += a; + } + + B2_ASSERT( area > FLT_EPSILON ); + float invArea = 1.0f / area; + center.x *= invArea; + center.y *= invArea; + + // Restore offset + center = b2Add( origin, center ); + + return center; +} + +b2Polygon b2MakePolygon( const b2Hull* hull, float radius ) +{ + B2_ASSERT( b2ValidateHull( hull ) ); + + if ( hull->count < 3 ) + { + // Handle a bad hull when assertions are disabled + return b2MakeSquare( 0.5f ); + } + + b2Polygon shape = { 0 }; + shape.count = hull->count; + shape.radius = radius; + + // Copy vertices + for ( int32_t i = 0; i < shape.count; ++i ) + { + shape.vertices[i] = hull->points[i]; + } + + // Compute normals. Ensure the edges have non-zero length. + for ( int32_t i = 0; i < shape.count; ++i ) + { + int32_t i1 = i; + int32_t i2 = i + 1 < shape.count ? i + 1 : 0; + b2Vec2 edge = b2Sub( shape.vertices[i2], shape.vertices[i1] ); + B2_ASSERT( b2Dot( edge, edge ) > FLT_EPSILON * FLT_EPSILON ); + shape.normals[i] = b2Normalize( b2CrossVS( edge, 1.0f ) ); + } + + shape.centroid = b2ComputePolygonCentroid( shape.vertices, shape.count ); + + return shape; +} + +b2Polygon b2MakeOffsetPolygon( const b2Hull* hull, float radius, b2Transform transform ) +{ + B2_ASSERT( b2ValidateHull( hull ) ); + + if ( hull->count < 3 ) + { + // Handle a bad hull when assertions are disabled + return b2MakeSquare( 0.5f ); + } + + b2Polygon shape = { 0 }; + shape.count = hull->count; + shape.radius = radius; + + // Copy vertices + for ( int32_t i = 0; i < shape.count; ++i ) + { + shape.vertices[i] = b2TransformPoint( transform, hull->points[i] ); + } + + // Compute normals. Ensure the edges have non-zero length. + for ( int32_t i = 0; i < shape.count; ++i ) + { + int32_t i1 = i; + int32_t i2 = i + 1 < shape.count ? i + 1 : 0; + b2Vec2 edge = b2Sub( shape.vertices[i2], shape.vertices[i1] ); + B2_ASSERT( b2Dot( edge, edge ) > FLT_EPSILON * FLT_EPSILON ); + shape.normals[i] = b2Normalize( b2CrossVS( edge, 1.0f ) ); + } + + shape.centroid = b2ComputePolygonCentroid( shape.vertices, shape.count ); + + return shape; +} + +b2Polygon b2MakeSquare( float h ) +{ + return b2MakeBox( h, h ); +} + +b2Polygon b2MakeBox( float hx, float hy ) +{ + B2_ASSERT( b2IsValid( hx ) && hx > 0.0f ); + B2_ASSERT( b2IsValid( hy ) && hy > 0.0f ); + + b2Polygon shape = { 0 }; + shape.count = 4; + shape.vertices[0] = ( b2Vec2 ){ -hx, -hy }; + shape.vertices[1] = ( b2Vec2 ){ hx, -hy }; + shape.vertices[2] = ( b2Vec2 ){ hx, hy }; + shape.vertices[3] = ( b2Vec2 ){ -hx, hy }; + shape.normals[0] = ( b2Vec2 ){ 0.0f, -1.0f }; + shape.normals[1] = ( b2Vec2 ){ 1.0f, 0.0f }; + shape.normals[2] = ( b2Vec2 ){ 0.0f, 1.0f }; + shape.normals[3] = ( b2Vec2 ){ -1.0f, 0.0f }; + shape.radius = 0.0f; + shape.centroid = b2Vec2_zero; + return shape; +} + +b2Polygon b2MakeRoundedBox( float hx, float hy, float radius ) +{ + b2Polygon shape = b2MakeBox( hx, hy ); + shape.radius = radius; + return shape; +} + +b2Polygon b2MakeOffsetBox( float hx, float hy, b2Vec2 center, b2Rot rotation ) +{ + b2Transform xf; + xf.p = center; + xf.q = rotation; + + b2Polygon shape = { 0 }; + shape.count = 4; + shape.vertices[0] = b2TransformPoint( xf, ( b2Vec2 ){ -hx, -hy } ); + shape.vertices[1] = b2TransformPoint( xf, ( b2Vec2 ){ hx, -hy } ); + shape.vertices[2] = b2TransformPoint( xf, ( b2Vec2 ){ hx, hy } ); + shape.vertices[3] = b2TransformPoint( xf, ( b2Vec2 ){ -hx, hy } ); + shape.normals[0] = b2RotateVector( xf.q, ( b2Vec2 ){ 0.0f, -1.0f } ); + shape.normals[1] = b2RotateVector( xf.q, ( b2Vec2 ){ 1.0f, 0.0f } ); + shape.normals[2] = b2RotateVector( xf.q, ( b2Vec2 ){ 0.0f, 1.0f } ); + shape.normals[3] = b2RotateVector( xf.q, ( b2Vec2 ){ -1.0f, 0.0f } ); + shape.radius = 0.0f; + shape.centroid = center; + return shape; +} + +b2Polygon b2TransformPolygon( b2Transform transform, const b2Polygon* polygon ) +{ + b2Polygon p = *polygon; + + for ( int i = 0; i < p.count; ++i ) + { + p.vertices[i] = b2TransformPoint( transform, p.vertices[i] ); + p.normals[i] = b2RotateVector( transform.q, p.normals[i] ); + } + + p.centroid = b2TransformPoint( transform, p.centroid ); + + return p; +} + +b2MassData b2ComputeCircleMass( const b2Circle* shape, float density ) +{ + float rr = shape->radius * shape->radius; + + b2MassData massData; + massData.mass = density * b2_pi * rr; + massData.center = shape->center; + + // inertia about the local origin + massData.rotationalInertia = massData.mass * ( 0.5f * rr + b2Dot( shape->center, shape->center ) ); + + return massData; +} + +b2MassData b2ComputeCapsuleMass( const b2Capsule* shape, float density ) +{ + float radius = shape->radius; + float rr = radius * radius; + b2Vec2 p1 = shape->center1; + b2Vec2 p2 = shape->center2; + float length = b2Length( b2Sub( p2, p1 ) ); + float ll = length * length; + + float circleMass = density * ( b2_pi * radius * radius ); + float boxMass = density * ( 2.0f * radius * length ); + + b2MassData massData; + massData.mass = circleMass + boxMass; + massData.center.x = 0.5f * ( p1.x + p2.x ); + massData.center.y = 0.5f * ( p1.y + p2.y ); + + // two offset half circles, both halves add up to full circle and each half is offset by half length + // semi-circle centroid = 4 r / 3 pi + // Need to apply parallel-axis theorem twice: + // 1. shift semi-circle centroid to origin + // 2. shift semi-circle to box end + // m * ((h + lc)^2 - lc^2) = m * (h^2 + 2 * h * lc) + // See: https://en.wikipedia.org/wiki/Parallel_axis_theorem + // I verified this formula by computing the convex hull of a 128 vertex capsule + + // half circle centroid + float lc = 4.0f * radius / ( 3.0f * b2_pi ); + + // half length of rectangular portion of capsule + float h = 0.5f * length; + + float circleInertia = circleMass * ( 0.5f * rr + h * h + 2.0f * h * lc ); + float boxInertia = boxMass * ( 4.0f * rr + ll ) / 12.0f; + massData.rotationalInertia = circleInertia + boxInertia; + + // inertia about the local origin + massData.rotationalInertia += massData.mass * b2Dot( massData.center, massData.center ); + + return massData; +} + +b2MassData b2ComputePolygonMass( const b2Polygon* shape, float density ) +{ + // Polygon mass, centroid, and inertia. + // Let rho be the polygon density in mass per unit area. + // Then: + // mass = rho * int(dA) + // centroid.x = (1/mass) * rho * int(x * dA) + // centroid.y = (1/mass) * rho * int(y * dA) + // I = rho * int((x*x + y*y) * dA) + // + // We can compute these integrals by summing all the integrals + // for each triangle of the polygon. To evaluate the integral + // for a single triangle, we make a change of variables to + // the (u,v) coordinates of the triangle: + // x = x0 + e1x * u + e2x * v + // y = y0 + e1y * u + e2y * v + // where 0 <= u && 0 <= v && u + v <= 1. + // + // We integrate u from [0,1-v] and then v from [0,1]. + // We also need to use the Jacobian of the transformation: + // D = cross(e1, e2) + // + // Simplification: triangle centroid = (1/3) * (p1 + p2 + p3) + // + // The rest of the derivation is handled by computer algebra. + + B2_ASSERT( shape->count > 0 ); + + if ( shape->count == 1 ) + { + b2Circle circle; + circle.center = shape->vertices[0]; + circle.radius = shape->radius; + return b2ComputeCircleMass( &circle, density ); + } + + if ( shape->count == 2 ) + { + b2Capsule capsule; + capsule.center1 = shape->vertices[0]; + capsule.center2 = shape->vertices[1]; + capsule.radius = shape->radius; + return b2ComputeCapsuleMass( &capsule, density ); + } + + b2Vec2 vertices[b2_maxPolygonVertices] = { 0 }; + int32_t count = shape->count; + float radius = shape->radius; + + if ( radius > 0.0f ) + { + // Approximate mass of rounded polygons by pushing out the vertices. + float sqrt2 = 1.412f; + for ( int32_t i = 0; i < count; ++i ) + { + int32_t j = i == 0 ? count - 1 : i - 1; + b2Vec2 n1 = shape->normals[j]; + b2Vec2 n2 = shape->normals[i]; + + b2Vec2 mid = b2Normalize( b2Add( n1, n2 ) ); + vertices[i] = b2MulAdd( shape->vertices[i], sqrt2 * radius, mid ); + } + } + else + { + for ( int32_t i = 0; i < count; ++i ) + { + vertices[i] = shape->vertices[i]; + } + } + + b2Vec2 center = { 0.0f, 0.0f }; + float area = 0.0f; + float rotationalInertia = 0.0f; + + // Get a reference point for forming triangles. + // Use the first vertex to reduce round-off errors. + b2Vec2 r = vertices[0]; + + const float inv3 = 1.0f / 3.0f; + + for ( int32_t i = 1; i < count - 1; ++i ) + { + // Triangle edges + b2Vec2 e1 = b2Sub( vertices[i], r ); + b2Vec2 e2 = b2Sub( vertices[i + 1], r ); + + float D = b2Cross( e1, e2 ); + + float triangleArea = 0.5f * D; + area += triangleArea; + + // Area weighted centroid, r at origin + center = b2MulAdd( center, triangleArea * inv3, b2Add( e1, e2 ) ); + + float ex1 = e1.x, ey1 = e1.y; + float ex2 = e2.x, ey2 = e2.y; + + float intx2 = ex1 * ex1 + ex2 * ex1 + ex2 * ex2; + float inty2 = ey1 * ey1 + ey2 * ey1 + ey2 * ey2; + + rotationalInertia += ( 0.25f * inv3 * D ) * ( intx2 + inty2 ); + } + + b2MassData massData; + + // Total mass + massData.mass = density * area; + + // Center of mass, shift back from origin at r + B2_ASSERT( area > FLT_EPSILON ); + float invArea = 1.0f / area; + center.x *= invArea; + center.y *= invArea; + massData.center = b2Add( r, center ); + + // Inertia tensor relative to the local origin (point s). + massData.rotationalInertia = density * rotationalInertia; + + // Shift to center of mass then to original body origin. + massData.rotationalInertia += massData.mass * ( b2Dot( massData.center, massData.center ) - b2Dot( center, center ) ); + + return massData; +} + +b2AABB b2ComputeCircleAABB( const b2Circle* shape, b2Transform xf ) +{ + b2Vec2 p = b2TransformPoint( xf, shape->center ); + float r = shape->radius; + + b2AABB aabb = { { p.x - r, p.y - r }, { p.x + r, p.y + r } }; + return aabb; +} + +b2AABB b2ComputeCapsuleAABB( const b2Capsule* shape, b2Transform xf ) +{ + b2Vec2 v1 = b2TransformPoint( xf, shape->center1 ); + b2Vec2 v2 = b2TransformPoint( xf, shape->center2 ); + + b2Vec2 r = { shape->radius, shape->radius }; + b2Vec2 lower = b2Sub( b2Min( v1, v2 ), r ); + b2Vec2 upper = b2Add( b2Max( v1, v2 ), r ); + + b2AABB aabb = { lower, upper }; + return aabb; +} + +b2AABB b2ComputePolygonAABB( const b2Polygon* shape, b2Transform xf ) +{ + B2_ASSERT( shape->count > 0 ); + b2Vec2 lower = b2TransformPoint( xf, shape->vertices[0] ); + b2Vec2 upper = lower; + + for ( int32_t i = 1; i < shape->count; ++i ) + { + b2Vec2 v = b2TransformPoint( xf, shape->vertices[i] ); + lower = b2Min( lower, v ); + upper = b2Max( upper, v ); + } + + b2Vec2 r = { shape->radius, shape->radius }; + lower = b2Sub( lower, r ); + upper = b2Add( upper, r ); + + b2AABB aabb = { lower, upper }; + return aabb; +} + +b2AABB b2ComputeSegmentAABB( const b2Segment* shape, b2Transform xf ) +{ + b2Vec2 v1 = b2TransformPoint( xf, shape->point1 ); + b2Vec2 v2 = b2TransformPoint( xf, shape->point2 ); + + b2Vec2 lower = b2Min( v1, v2 ); + b2Vec2 upper = b2Max( v1, v2 ); + + b2AABB aabb = { lower, upper }; + return aabb; +} + +bool b2PointInCircle( b2Vec2 point, const b2Circle* shape ) +{ + b2Vec2 center = shape->center; + return b2DistanceSquared( point, center ) <= shape->radius * shape->radius; +} + +bool b2PointInCapsule( b2Vec2 point, const b2Capsule* shape ) +{ + float rr = shape->radius * shape->radius; + b2Vec2 p1 = shape->center1; + b2Vec2 p2 = shape->center2; + + b2Vec2 d = b2Sub( p2, p1 ); + float dd = b2Dot( d, d ); + if ( dd == 0.0f ) + { + // Capsule is really a circle + return b2DistanceSquared( point, p1 ) <= rr; + } + + // Get closest point on capsule segment + // c = p1 + t * d + // dot(point - c, d) = 0 + // dot(point - p1 - t * d, d) = 0 + // t = dot(point - p1, d) / dot(d, d) + float t = b2Dot( b2Sub( point, p1 ), d ) / dd; + t = b2ClampFloat( t, 0.0f, 1.0f ); + b2Vec2 c = b2MulAdd( p1, t, d ); + + // Is query point within radius around closest point? + return b2DistanceSquared( point, c ) <= rr; +} + +bool b2PointInPolygon( b2Vec2 point, const b2Polygon* shape ) +{ + b2DistanceInput input = { 0 }; + input.proxyA = b2MakeProxy( shape->vertices, shape->count, 0.0f ); + input.proxyB = b2MakeProxy( &point, 1, 0.0f ); + input.transformA = b2Transform_identity; + input.transformB = b2Transform_identity; + input.useRadii = false; + + b2DistanceCache cache = { 0 }; + b2DistanceOutput output = b2ShapeDistance( &cache, &input, NULL, 0 ); + + return output.distance <= shape->radius; +} + +// Precision Improvements for Ray / Sphere Intersection - Ray Tracing Gems 2019 +// http://www.codercorner.com/blog/?p=321 +b2CastOutput b2RayCastCircle( const b2RayCastInput* input, const b2Circle* shape ) +{ + B2_ASSERT( b2IsValidRay( input ) ); + + b2Vec2 p = shape->center; + + b2CastOutput output = { 0 }; + + // Shift ray so circle center is the origin + b2Vec2 s = b2Sub( input->origin, p ); + float length; + b2Vec2 d = b2GetLengthAndNormalize( &length, input->translation ); + if ( length == 0.0f ) + { + // zero length ray + return output; + } + + // Find closest point on ray to origin + + // solve: dot(s + t * d, d) = 0 + float t = -b2Dot( s, d ); + + // c is the closest point on the line to the origin + b2Vec2 c = b2MulAdd( s, t, d ); + + float cc = b2Dot( c, c ); + float r = shape->radius; + float rr = r * r; + + if ( cc > rr ) + { + // closest point is outside the circle + return output; + } + + // Pythagorus + float h = sqrtf( rr - cc ); + + float fraction = t - h; + + if ( fraction < 0.0f || input->maxFraction * length < fraction ) + { + // outside the range of the ray segment + return output; + } + + b2Vec2 hitPoint = b2MulAdd( s, fraction, d ); + + output.fraction = fraction / length; + output.normal = b2Normalize( hitPoint ); + output.point = b2MulAdd( p, shape->radius, output.normal ); + output.hit = true; + + return output; +} + +b2CastOutput b2RayCastCapsule( const b2RayCastInput* input, const b2Capsule* shape ) +{ + B2_ASSERT( b2IsValidRay( input ) ); + + b2CastOutput output = { 0 }; + + b2Vec2 v1 = shape->center1; + b2Vec2 v2 = shape->center2; + + b2Vec2 e = b2Sub( v2, v1 ); + + float capsuleLength; + b2Vec2 a = b2GetLengthAndNormalize( &capsuleLength, e ); + + if ( capsuleLength < FLT_EPSILON ) + { + // Capsule is really a circle + b2Circle circle = { v1, shape->radius }; + return b2RayCastCircle( input, &circle ); + } + + b2Vec2 p1 = input->origin; + b2Vec2 d = input->translation; + + // Ray from capsule start to ray start + b2Vec2 q = b2Sub( p1, v1 ); + float qa = b2Dot( q, a ); + + // Vector to ray start that is perpendicular to capsule axis + b2Vec2 qp = b2MulAdd( q, -qa, a ); + + float radius = shape->radius; + + // Does the ray start within the infinite length capsule? + if ( b2Dot( qp, qp ) < radius * radius ) + { + if ( qa < 0.0f ) + { + // start point behind capsule segment + b2Circle circle = { v1, shape->radius }; + return b2RayCastCircle( input, &circle ); + } + + if ( qa > 1.0f ) + { + // start point ahead of capsule segment + b2Circle circle = { v2, shape->radius }; + return b2RayCastCircle( input, &circle ); + } + + // ray starts inside capsule -> no hit + return output; + } + + // Perpendicular to capsule axis, pointing right + b2Vec2 n = { a.y, -a.x }; + + float rayLength; + b2Vec2 u = b2GetLengthAndNormalize( &rayLength, d ); + + // Intersect ray with infinite length capsule + // v1 + radius * n + s1 * a = p1 + s2 * u + // v1 - radius * n + s1 * a = p1 + s2 * u + + // s1 * a - s2 * u = b + // b = q - radius * ap + // or + // b = q + radius * ap + + // Cramer's rule [a -u] + float den = -a.x * u.y + u.x * a.y; + if ( -FLT_EPSILON < den && den < FLT_EPSILON ) + { + // Ray is parallel to capsule and outside infinite length capsule + return output; + } + + b2Vec2 b1 = b2MulSub( q, radius, n ); + b2Vec2 b2 = b2MulAdd( q, radius, n ); + + float invDen = 1.0f / den; + + // Cramer's rule [a b1] + float s21 = ( a.x * b1.y - b1.x * a.y ) * invDen; + + // Cramer's rule [a b2] + float s22 = ( a.x * b2.y - b2.x * a.y ) * invDen; + + float s2; + b2Vec2 b; + if ( s21 < s22 ) + { + s2 = s21; + b = b1; + } + else + { + s2 = s22; + b = b2; + n = b2Neg( n ); + } + + if ( s2 < 0.0f || input->maxFraction * rayLength < s2 ) + { + return output; + } + + // Cramer's rule [b -u] + float s1 = ( -b.x * u.y + u.x * b.y ) * invDen; + + if ( s1 < 0.0f ) + { + // ray passes behind capsule segment + b2Circle circle = { v1, shape->radius }; + return b2RayCastCircle( input, &circle ); + } + else if ( capsuleLength < s1 ) + { + // ray passes ahead of capsule segment + b2Circle circle = { v2, shape->radius }; + return b2RayCastCircle( input, &circle ); + } + else + { + // ray hits capsule side + output.fraction = s2 / rayLength; + output.point = b2Add( b2Lerp( v1, v2, s1 / capsuleLength ), b2MulSV( shape->radius, n ) ); + output.normal = n; + output.hit = true; + return output; + } +} + +// Ray vs line segment +b2CastOutput b2RayCastSegment( const b2RayCastInput* input, const b2Segment* shape, bool oneSided ) +{ + if ( oneSided ) + { + // Skip left-side collision + float offset = b2Cross( b2Sub( input->origin, shape->point1 ), b2Sub( shape->point2, shape->point1 ) ); + if ( offset < 0.0f ) + { + b2CastOutput output = { 0 }; + return output; + } + } + + // Put the ray into the edge's frame of reference. + b2Vec2 p1 = input->origin; + b2Vec2 d = input->translation; + + b2Vec2 v1 = shape->point1; + b2Vec2 v2 = shape->point2; + b2Vec2 e = b2Sub( v2, v1 ); + + b2CastOutput output = { 0 }; + + float length; + b2Vec2 eUnit = b2GetLengthAndNormalize( &length, e ); + if ( length == 0.0f ) + { + return output; + } + + // Normal points to the right, looking from v1 towards v2 + b2Vec2 normal = b2RightPerp( eUnit ); + + // Intersect ray with infinite segment using normal + // Similar to intersecting a ray with an infinite plane + // p = p1 + t * d + // dot(normal, p - v1) = 0 + // dot(normal, p1 - v1) + t * dot(normal, d) = 0 + float numerator = b2Dot( normal, b2Sub( v1, p1 ) ); + float denominator = b2Dot( normal, d ); + + if ( denominator == 0.0f ) + { + // parallel + return output; + } + + float t = numerator / denominator; + if ( t < 0.0f || input->maxFraction < t ) + { + // out of ray range + return output; + } + + // Intersection point on infinite segment + b2Vec2 p = b2MulAdd( p1, t, d ); + + // Compute position of p along segment + // p = v1 + s * e + // s = dot(p - v1, e) / dot(e, e) + + float s = b2Dot( b2Sub( p, v1 ), eUnit ); + if ( s < 0.0f || length < s ) + { + // out of segment range + return output; + } + + if ( numerator > 0.0f ) + { + normal = b2Neg( normal ); + } + + output.fraction = t; + output.point = b2MulAdd( p1, t, d ); + output.normal = normal; + output.hit = true; + + return output; +} + +b2CastOutput b2RayCastPolygon( const b2RayCastInput* input, const b2Polygon* shape ) +{ + B2_ASSERT( b2IsValidRay( input ) ); + + if ( shape->radius == 0.0f ) + { + // Put the ray into the polygon's frame of reference. + b2Vec2 p1 = input->origin; + b2Vec2 d = input->translation; + + float lower = 0.0f, upper = input->maxFraction; + + int32_t index = -1; + + b2CastOutput output = { 0 }; + + for ( int32_t i = 0; i < shape->count; ++i ) + { + // p = p1 + a * d + // dot(normal, p - v) = 0 + // dot(normal, p1 - v) + a * dot(normal, d) = 0 + float numerator = b2Dot( shape->normals[i], b2Sub( shape->vertices[i], p1 ) ); + float denominator = b2Dot( shape->normals[i], d ); + + if ( denominator == 0.0f ) + { + if ( numerator < 0.0f ) + { + return output; + } + } + else + { + // Note: we want this predicate without division: + // lower < numerator / denominator, where denominator < 0 + // Since denominator < 0, we have to flip the inequality: + // lower < numerator / denominator <==> denominator * lower > numerator. + if ( denominator < 0.0f && numerator < lower * denominator ) + { + // Increase lower. + // The segment enters this half-space. + lower = numerator / denominator; + index = i; + } + else if ( denominator > 0.0f && numerator < upper * denominator ) + { + // Decrease upper. + // The segment exits this half-space. + upper = numerator / denominator; + } + } + + // The use of epsilon here causes the B2_ASSERT on lower to trip + // in some cases. Apparently the use of epsilon was to make edge + // shapes work, but now those are handled separately. + // if (upper < lower - b2_epsilon) + if ( upper < lower ) + { + return output; + } + } + + B2_ASSERT( 0.0f <= lower && lower <= input->maxFraction ); + + if ( index >= 0 ) + { + output.fraction = lower; + output.normal = shape->normals[index]; + output.point = b2MulAdd( p1, lower, d ); + output.hit = true; + } + + return output; + } + + // TODO_ERIN this is not working for ray vs box (zero radii) + b2ShapeCastPairInput castInput; + castInput.proxyA = b2MakeProxy( shape->vertices, shape->count, shape->radius ); + castInput.proxyB = b2MakeProxy( &input->origin, 1, 0.0f ); + castInput.transformA = b2Transform_identity; + castInput.transformB = b2Transform_identity; + castInput.translationB = input->translation; + castInput.maxFraction = input->maxFraction; + return b2ShapeCast( &castInput ); +} + +b2CastOutput b2ShapeCastCircle( const b2ShapeCastInput* input, const b2Circle* shape ) +{ + b2ShapeCastPairInput pairInput; + pairInput.proxyA = b2MakeProxy( &shape->center, 1, shape->radius ); + pairInput.proxyB = b2MakeProxy( input->points, input->count, input->radius ); + pairInput.transformA = b2Transform_identity; + pairInput.transformB = b2Transform_identity; + pairInput.translationB = input->translation; + pairInput.maxFraction = input->maxFraction; + + b2CastOutput output = b2ShapeCast( &pairInput ); + return output; +} + +b2CastOutput b2ShapeCastCapsule( const b2ShapeCastInput* input, const b2Capsule* shape ) +{ + b2ShapeCastPairInput pairInput; + pairInput.proxyA = b2MakeProxy( &shape->center1, 2, shape->radius ); + pairInput.proxyB = b2MakeProxy( input->points, input->count, input->radius ); + pairInput.transformA = b2Transform_identity; + pairInput.transformB = b2Transform_identity; + pairInput.translationB = input->translation; + pairInput.maxFraction = input->maxFraction; + + b2CastOutput output = b2ShapeCast( &pairInput ); + return output; +} + +b2CastOutput b2ShapeCastSegment( const b2ShapeCastInput* input, const b2Segment* shape ) +{ + b2ShapeCastPairInput pairInput; + pairInput.proxyA = b2MakeProxy( &shape->point1, 2, 0.0f ); + pairInput.proxyB = b2MakeProxy( input->points, input->count, input->radius ); + pairInput.transformA = b2Transform_identity; + pairInput.transformB = b2Transform_identity; + pairInput.translationB = input->translation; + pairInput.maxFraction = input->maxFraction; + + b2CastOutput output = b2ShapeCast( &pairInput ); + return output; +} + +b2CastOutput b2ShapeCastPolygon( const b2ShapeCastInput* input, const b2Polygon* shape ) +{ + b2ShapeCastPairInput pairInput; + pairInput.proxyA = b2MakeProxy( shape->vertices, shape->count, shape->radius ); + pairInput.proxyB = b2MakeProxy( input->points, input->count, input->radius ); + pairInput.transformA = b2Transform_identity; + pairInput.transformB = b2Transform_identity; + pairInput.translationB = input->translation; + pairInput.maxFraction = input->maxFraction; + + b2CastOutput output = b2ShapeCast( &pairInput ); + return output; +} diff --git a/3rdparty/box2d/src/hull.c b/3rdparty/box2d/src/hull.c new file mode 100644 index 000000000000..41fd76de1581 --- /dev/null +++ b/3rdparty/box2d/src/hull.c @@ -0,0 +1,327 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#include "core.h" + +#include "box2d/collision.h" +#include "box2d/math_functions.h" + +#include + +// quickhull recursion +static b2Hull b2RecurseHull( b2Vec2 p1, b2Vec2 p2, b2Vec2* ps, int count ) +{ + b2Hull hull; + hull.count = 0; + + if ( count == 0 ) + { + return hull; + } + + // create an edge vector pointing from p1 to p2 + b2Vec2 e = b2Normalize( b2Sub( p2, p1 ) ); + + // discard points left of e and find point furthest to the right of e + b2Vec2 rightPoints[b2_maxPolygonVertices]; + int rightCount = 0; + + int bestIndex = 0; + float bestDistance = b2Cross( b2Sub( ps[bestIndex], p1 ), e ); + if ( bestDistance > 0.0f ) + { + rightPoints[rightCount++] = ps[bestIndex]; + } + + for ( int i = 1; i < count; ++i ) + { + float distance = b2Cross( b2Sub( ps[i], p1 ), e ); + if ( distance > bestDistance ) + { + bestIndex = i; + bestDistance = distance; + } + + if ( distance > 0.0f ) + { + rightPoints[rightCount++] = ps[i]; + } + } + + if ( bestDistance < 2.0f * b2_linearSlop ) + { + return hull; + } + + b2Vec2 bestPoint = ps[bestIndex]; + + // compute hull to the right of p1-bestPoint + b2Hull hull1 = b2RecurseHull( p1, bestPoint, rightPoints, rightCount ); + + // compute hull to the right of bestPoint-p2 + b2Hull hull2 = b2RecurseHull( bestPoint, p2, rightPoints, rightCount ); + + // stitch together hulls + for ( int i = 0; i < hull1.count; ++i ) + { + hull.points[hull.count++] = hull1.points[i]; + } + + hull.points[hull.count++] = bestPoint; + + for ( int i = 0; i < hull2.count; ++i ) + { + hull.points[hull.count++] = hull2.points[i]; + } + + B2_ASSERT( hull.count < b2_maxPolygonVertices ); + + return hull; +} + +// quickhull algorithm +// - merges vertices based on b2_linearSlop +// - removes collinear points using b2_linearSlop +// - returns an empty hull if it fails +b2Hull b2ComputeHull( const b2Vec2* points, int count ) +{ + b2Hull hull; + hull.count = 0; + + if ( count < 3 || count > b2_maxPolygonVertices ) + { + // check your data + return hull; + } + + count = b2MinFloat( count, b2_maxPolygonVertices ); + + b2AABB aabb = { { FLT_MAX, FLT_MAX }, { -FLT_MAX, -FLT_MAX } }; + + // Perform aggressive point welding. First point always remains. + // Also compute the bounding box for later. + b2Vec2 ps[b2_maxPolygonVertices]; + int n = 0; + const float linearSlop = b2_linearSlop; + const float tolSqr = 16.0f * linearSlop * linearSlop; + for ( int i = 0; i < count; ++i ) + { + aabb.lowerBound = b2Min( aabb.lowerBound, points[i] ); + aabb.upperBound = b2Max( aabb.upperBound, points[i] ); + + b2Vec2 vi = points[i]; + + bool unique = true; + for ( int j = 0; j < i; ++j ) + { + b2Vec2 vj = points[j]; + + float distSqr = b2DistanceSquared( vi, vj ); + if ( distSqr < tolSqr ) + { + unique = false; + break; + } + } + + if ( unique ) + { + ps[n++] = vi; + } + } + + if ( n < 3 ) + { + // all points very close together, check your data and check your scale + return hull; + } + + // Find an extreme point as the first point on the hull + b2Vec2 c = b2AABB_Center( aabb ); + int f1 = 0; + float dsq1 = b2DistanceSquared( c, ps[f1] ); + for ( int i = 1; i < n; ++i ) + { + float dsq = b2DistanceSquared( c, ps[i] ); + if ( dsq > dsq1 ) + { + f1 = i; + dsq1 = dsq; + } + } + + // remove p1 from working set + b2Vec2 p1 = ps[f1]; + ps[f1] = ps[n - 1]; + n = n - 1; + + int f2 = 0; + float dsq2 = b2DistanceSquared( p1, ps[f2] ); + for ( int i = 1; i < n; ++i ) + { + float dsq = b2DistanceSquared( p1, ps[i] ); + if ( dsq > dsq2 ) + { + f2 = i; + dsq2 = dsq; + } + } + + // remove p2 from working set + b2Vec2 p2 = ps[f2]; + ps[f2] = ps[n - 1]; + n = n - 1; + + // split the points into points that are left and right of the line p1-p2. + b2Vec2 rightPoints[b2_maxPolygonVertices - 2]; + int rightCount = 0; + + b2Vec2 leftPoints[b2_maxPolygonVertices - 2]; + int leftCount = 0; + + b2Vec2 e = b2Normalize( b2Sub( p2, p1 ) ); + + for ( int i = 0; i < n; ++i ) + { + float d = b2Cross( b2Sub( ps[i], p1 ), e ); + + // slop used here to skip points that are very close to the line p1-p2 + if ( d >= 2.0f * linearSlop ) + { + rightPoints[rightCount++] = ps[i]; + } + else if ( d <= -2.0f * linearSlop ) + { + leftPoints[leftCount++] = ps[i]; + } + } + + // compute hulls on right and left + b2Hull hull1 = b2RecurseHull( p1, p2, rightPoints, rightCount ); + b2Hull hull2 = b2RecurseHull( p2, p1, leftPoints, leftCount ); + + if ( hull1.count == 0 && hull2.count == 0 ) + { + // all points collinear + return hull; + } + + // stitch hulls together, preserving CCW winding order + hull.points[hull.count++] = p1; + + for ( int i = 0; i < hull1.count; ++i ) + { + hull.points[hull.count++] = hull1.points[i]; + } + + hull.points[hull.count++] = p2; + + for ( int i = 0; i < hull2.count; ++i ) + { + hull.points[hull.count++] = hull2.points[i]; + } + + B2_ASSERT( hull.count <= b2_maxPolygonVertices ); + + // merge collinear + bool searching = true; + while ( searching && hull.count > 2 ) + { + searching = false; + + for ( int i = 0; i < hull.count; ++i ) + { + int i1 = i; + int i2 = ( i + 1 ) % hull.count; + int i3 = ( i + 2 ) % hull.count; + + b2Vec2 s1 = hull.points[i1]; + b2Vec2 s2 = hull.points[i2]; + b2Vec2 s3 = hull.points[i3]; + + // unit edge vector for s1-s3 + b2Vec2 r = b2Normalize( b2Sub( s3, s1 ) ); + + float distance = b2Cross( b2Sub( s2, s1 ), r ); + if ( distance <= 2.0f * linearSlop ) + { + // remove midpoint from hull + for ( int j = i2; j < hull.count - 1; ++j ) + { + hull.points[j] = hull.points[j + 1]; + } + hull.count -= 1; + + // continue searching for collinear points + searching = true; + + break; + } + } + } + + if ( hull.count < 3 ) + { + // all points collinear, shouldn't be reached since this was validated above + hull.count = 0; + } + + return hull; +} + +bool b2ValidateHull( const b2Hull* hull ) +{ + if ( hull->count < 3 || b2_maxPolygonVertices < hull->count ) + { + return false; + } + + // test that every point is behind every edge + for ( int i = 0; i < hull->count; ++i ) + { + // create an edge vector + int i1 = i; + int i2 = i < hull->count - 1 ? i1 + 1 : 0; + b2Vec2 p = hull->points[i1]; + b2Vec2 e = b2Normalize( b2Sub( hull->points[i2], p ) ); + + for ( int j = 0; j < hull->count; ++j ) + { + // skip points that subtend the current edge + if ( j == i1 || j == i2 ) + { + continue; + } + + float distance = b2Cross( b2Sub( hull->points[j], p ), e ); + if ( distance >= 0.0f ) + { + return false; + } + } + } + + // test for collinear points + const float linearSlop = b2_linearSlop; + for ( int i = 0; i < hull->count; ++i ) + { + int i1 = i; + int i2 = ( i + 1 ) % hull->count; + int i3 = ( i + 2 ) % hull->count; + + b2Vec2 p1 = hull->points[i1]; + b2Vec2 p2 = hull->points[i2]; + b2Vec2 p3 = hull->points[i3]; + + b2Vec2 e = b2Normalize( b2Sub( p3, p1 ) ); + + float distance = b2Cross( b2Sub( p2, p1 ), e ); + if ( distance <= linearSlop ) + { + // p1-p2-p3 are collinear + return false; + } + } + + return true; +} diff --git a/3rdparty/box2d/src/id_pool.c b/3rdparty/box2d/src/id_pool.c new file mode 100644 index 000000000000..2cbb06ec4916 --- /dev/null +++ b/3rdparty/box2d/src/id_pool.c @@ -0,0 +1,73 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#include "id_pool.h" + +#include "array.h" + +b2IdPool b2CreateIdPool() +{ + b2IdPool pool = { 0 }; + pool.freeArray = b2IntArray_Create( 32 ); + return pool; +} + +void b2DestroyIdPool( b2IdPool* pool ) +{ + b2IntArray_Destroy( &pool->freeArray ); + *pool = ( b2IdPool ){ 0 }; +} + +int b2AllocId( b2IdPool* pool ) +{ + int count = pool->freeArray.count; + if (count > 0 ) + { + int id = b2IntArray_Pop(&pool->freeArray); + return id; + } + + int id = pool->nextIndex; + pool->nextIndex += 1; + return id; +} + +void b2FreeId( b2IdPool* pool, int id ) +{ + B2_ASSERT( pool->nextIndex > 0 ); + B2_ASSERT( 0 <= id && id < pool->nextIndex ); + + if ( id == pool->nextIndex ) + { + pool->nextIndex -= 1; + return; + } + + b2IntArray_Push( &pool->freeArray, id ); +} + +#if B2_VALIDATE + +void b2ValidateFreeId( b2IdPool* pool, int id ) +{ + int freeCount = pool->freeArray.count; + for ( int i = 0; i < freeCount; ++i ) + { + if ( pool->freeArray.data[i] == id ) + { + return; + } + } + + B2_ASSERT( 0 ); +} + +#else + +void b2ValidateFreeId( b2IdPool* pool, int id ) +{ + B2_MAYBE_UNUSED( pool ); + B2_MAYBE_UNUSED( id ); +} + +#endif diff --git a/3rdparty/box2d/src/id_pool.h b/3rdparty/box2d/src/id_pool.h new file mode 100644 index 000000000000..6a72080db22f --- /dev/null +++ b/3rdparty/box2d/src/id_pool.h @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include "array.h" + +typedef struct b2IdPool +{ + b2IntArray freeArray; + int nextIndex; +} b2IdPool; + +b2IdPool b2CreateIdPool(); +void b2DestroyIdPool( b2IdPool* pool ); + +int b2AllocId( b2IdPool* pool ); +void b2FreeId( b2IdPool* pool, int id ); +void b2ValidateFreeId( b2IdPool* pool, int id ); + +static inline int b2GetIdCount( b2IdPool* pool ) +{ + return pool->nextIndex - pool->freeArray.count; +} + +static inline int b2GetIdCapacity( b2IdPool* pool ) +{ + return pool->nextIndex; +} + +static inline int b2GetIdBytes( b2IdPool* pool ) +{ + return b2IntArray_ByteCount(&pool->freeArray); +} diff --git a/3rdparty/box2d/src/island.c b/3rdparty/box2d/src/island.c new file mode 100644 index 000000000000..4f1b322ffe4d --- /dev/null +++ b/3rdparty/box2d/src/island.c @@ -0,0 +1,979 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#include "island.h" + +#include "body.h" +#include "contact.h" +#include "core.h" +#include "joint.h" +#include "solver_set.h" +#include "world.h" + +#include + +B2_ARRAY_SOURCE( b2Island, b2Island ); +B2_ARRAY_SOURCE( b2IslandSim, b2IslandSim ); + +b2Island* b2CreateIsland( b2World* world, int setIndex ) +{ + B2_ASSERT( setIndex == b2_awakeSet || setIndex >= b2_firstSleepingSet ); + + int islandId = b2AllocId( &world->islandIdPool ); + + if ( islandId == world->islands.count ) + { + b2Island emptyIsland = { 0 }; + b2IslandArray_Push( &world->islands, emptyIsland ); + } + else + { + B2_ASSERT( world->islands.data[islandId].setIndex == B2_NULL_INDEX ); + } + + b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, setIndex ); + + b2Island* island = b2IslandArray_Get( &world->islands, islandId ); + island->setIndex = setIndex; + island->localIndex = set->islandSims.count; + island->islandId = islandId; + island->headBody = B2_NULL_INDEX; + island->tailBody = B2_NULL_INDEX; + island->bodyCount = 0; + island->headContact = B2_NULL_INDEX; + island->tailContact = B2_NULL_INDEX; + island->contactCount = 0; + island->headJoint = B2_NULL_INDEX; + island->tailJoint = B2_NULL_INDEX; + island->jointCount = 0; + island->parentIsland = B2_NULL_INDEX; + island->constraintRemoveCount = 0; + + b2IslandSim* islandSim = b2IslandSimArray_Add( &set->islandSims ); + islandSim->islandId = islandId; + + return island; +} + +void b2DestroyIsland( b2World* world, int islandId ) +{ + // assume island is empty + b2Island* island = b2IslandArray_Get( &world->islands, islandId ); + b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, island->setIndex ); + int movedIndex = b2IslandSimArray_RemoveSwap( &set->islandSims, island->localIndex ); + if ( movedIndex != B2_NULL_INDEX ) + { + // Fix index on moved element + b2IslandSim* movedElement = set->islandSims.data + island->localIndex; + int movedId = movedElement->islandId; + b2Island* movedIsland = b2IslandArray_Get( &world->islands, movedId ); + B2_ASSERT( movedIsland->localIndex == movedIndex ); + movedIsland->localIndex = island->localIndex; + } + + // Free island and id (preserve island revision) + island->islandId = B2_NULL_INDEX; + island->setIndex = B2_NULL_INDEX; + island->localIndex = B2_NULL_INDEX; + b2FreeId( &world->islandIdPool, islandId ); +} + +static void b2AddContactToIsland( b2World* world, int islandId, b2Contact* contact ) +{ + B2_ASSERT( contact->islandId == B2_NULL_INDEX ); + B2_ASSERT( contact->islandPrev == B2_NULL_INDEX ); + B2_ASSERT( contact->islandNext == B2_NULL_INDEX ); + + b2Island* island = b2IslandArray_Get( &world->islands, islandId ); + + if ( island->headContact != B2_NULL_INDEX ) + { + contact->islandNext = island->headContact; + b2Contact* headContact = b2ContactArray_Get( &world->contacts, island->headContact); + headContact->islandPrev = contact->contactId; + } + + island->headContact = contact->contactId; + if ( island->tailContact == B2_NULL_INDEX ) + { + island->tailContact = island->headContact; + } + + island->contactCount += 1; + contact->islandId = islandId; + + b2ValidateIsland( world, islandId ); +} + +// Link a contact into an island. +// This performs union-find and path compression to join islands. +// https://en.wikipedia.org/wiki/Disjoint-set_data_structure +void b2LinkContact( b2World* world, b2Contact* contact ) +{ + B2_ASSERT( ( contact->flags & b2_contactTouchingFlag ) != 0 && ( contact->flags & b2_contactSensorFlag ) == 0 ); + + int bodyIdA = contact->edges[0].bodyId; + int bodyIdB = contact->edges[1].bodyId; + + b2Body* bodyA = b2BodyArray_Get( &world->bodies, bodyIdA ); + b2Body* bodyB = b2BodyArray_Get( &world->bodies, bodyIdB ); + + B2_ASSERT( bodyA->setIndex != b2_disabledSet && bodyB->setIndex != b2_disabledSet ); + B2_ASSERT( bodyA->setIndex != b2_staticSet || bodyB->setIndex != b2_staticSet ); + + // Wake bodyB if bodyA is awake and bodyB is sleeping + if ( bodyA->setIndex == b2_awakeSet && bodyB->setIndex >= b2_firstSleepingSet ) + { + b2WakeSolverSet( world, bodyB->setIndex ); + } + + // Wake bodyA if bodyB is awake and bodyA is sleeping + if ( bodyB->setIndex == b2_awakeSet && bodyA->setIndex >= b2_firstSleepingSet ) + { + b2WakeSolverSet( world, bodyA->setIndex ); + } + + int islandIdA = bodyA->islandId; + int islandIdB = bodyB->islandId; + + // Static bodies have null island indices. + B2_ASSERT( bodyA->setIndex != b2_staticSet || islandIdA == B2_NULL_INDEX ); + B2_ASSERT( bodyB->setIndex != b2_staticSet || islandIdB == B2_NULL_INDEX ); + B2_ASSERT( islandIdA != B2_NULL_INDEX || islandIdB != B2_NULL_INDEX ); + + if ( islandIdA == islandIdB ) + { + // Contact in same island + b2AddContactToIsland( world, islandIdA, contact ); + return; + } + + // Union-find root of islandA + b2Island* islandA = NULL; + if ( islandIdA != B2_NULL_INDEX ) + { + islandA = b2IslandArray_Get( &world->islands, islandIdA ); + int parentId = islandA->parentIsland; + while ( parentId != B2_NULL_INDEX ) + { + b2Island* parent = b2IslandArray_Get( &world->islands, parentId ); + if ( parent->parentIsland != B2_NULL_INDEX ) + { + // path compression + islandA->parentIsland = parent->parentIsland; + } + + islandA = parent; + islandIdA = parentId; + parentId = islandA->parentIsland; + } + } + + // Union-find root of islandB + b2Island* islandB = NULL; + if ( islandIdB != B2_NULL_INDEX ) + { + islandB = b2IslandArray_Get( &world->islands, islandIdB ); + int parentId = islandB->parentIsland; + while ( islandB->parentIsland != B2_NULL_INDEX ) + { + b2Island* parent = b2IslandArray_Get( &world->islands, parentId ); + if ( parent->parentIsland != B2_NULL_INDEX ) + { + // path compression + islandB->parentIsland = parent->parentIsland; + } + + islandB = parent; + islandIdB = parentId; + parentId = islandB->parentIsland; + } + } + + B2_ASSERT( islandA != NULL || islandB != NULL ); + + // Union-Find link island roots + if ( islandA != islandB && islandA != NULL && islandB != NULL ) + { + B2_ASSERT( islandA != islandB ); + B2_ASSERT( islandB->parentIsland == B2_NULL_INDEX ); + islandB->parentIsland = islandIdA; + } + + if ( islandA != NULL ) + { + b2AddContactToIsland( world, islandIdA, contact ); + } + else + { + b2AddContactToIsland( world, islandIdB, contact ); + } +} + +// This is called when a contact no longer has contact points or when a contact is destroyed. +void b2UnlinkContact( b2World* world, b2Contact* contact ) +{ + B2_ASSERT( ( contact->flags & b2_contactSensorFlag ) == 0 ); + B2_ASSERT( contact->islandId != B2_NULL_INDEX ); + + // remove from island + int islandId = contact->islandId; + b2Island* island = b2IslandArray_Get( &world->islands, islandId ); + + if ( contact->islandPrev != B2_NULL_INDEX ) + { + b2Contact* prevContact = b2ContactArray_Get( &world->contacts, contact->islandPrev); + B2_ASSERT( prevContact->islandNext == contact->contactId ); + prevContact->islandNext = contact->islandNext; + } + + if ( contact->islandNext != B2_NULL_INDEX ) + { + b2Contact* nextContact = b2ContactArray_Get( &world->contacts, contact->islandNext ); + B2_ASSERT( nextContact->islandPrev == contact->contactId ); + nextContact->islandPrev = contact->islandPrev; + } + + if ( island->headContact == contact->contactId ) + { + island->headContact = contact->islandNext; + } + + if ( island->tailContact == contact->contactId ) + { + island->tailContact = contact->islandPrev; + } + + B2_ASSERT( island->contactCount > 0 ); + island->contactCount -= 1; + island->constraintRemoveCount += 1; + + contact->islandId = B2_NULL_INDEX; + contact->islandPrev = B2_NULL_INDEX; + contact->islandNext = B2_NULL_INDEX; + + b2ValidateIsland( world, islandId ); +} + +static void b2AddJointToIsland( b2World* world, int islandId, b2Joint* joint ) +{ + B2_ASSERT( joint->islandId == B2_NULL_INDEX ); + B2_ASSERT( joint->islandPrev == B2_NULL_INDEX ); + B2_ASSERT( joint->islandNext == B2_NULL_INDEX ); + + b2Island* island = b2IslandArray_Get( &world->islands, islandId ); + + if ( island->headJoint != B2_NULL_INDEX ) + { + joint->islandNext = island->headJoint; + b2Joint* headJoint = b2JointArray_Get( &world->joints, island->headJoint ); + headJoint->islandPrev = joint->jointId; + } + + island->headJoint = joint->jointId; + if ( island->tailJoint == B2_NULL_INDEX ) + { + island->tailJoint = island->headJoint; + } + + island->jointCount += 1; + joint->islandId = islandId; + + b2ValidateIsland( world, islandId ); +} + +void b2LinkJoint( b2World* world, b2Joint* joint, bool mergeIslands ) +{ + b2Body* bodyA = b2BodyArray_Get( &world->bodies, joint->edges[0].bodyId ); + b2Body* bodyB = b2BodyArray_Get( &world->bodies, joint->edges[1].bodyId ); + + if ( bodyA->setIndex == b2_awakeSet && bodyB->setIndex >= b2_firstSleepingSet ) + { + b2WakeSolverSet( world, bodyB->setIndex ); + } + else if ( bodyB->setIndex == b2_awakeSet && bodyA->setIndex >= b2_firstSleepingSet ) + { + b2WakeSolverSet( world, bodyA->setIndex ); + } + + int islandIdA = bodyA->islandId; + int islandIdB = bodyB->islandId; + + B2_ASSERT( islandIdA != B2_NULL_INDEX || islandIdB != B2_NULL_INDEX ); + + if ( islandIdA == islandIdB ) + { + // Joint in same island + b2AddJointToIsland( world, islandIdA, joint ); + return; + } + + // Union-find root of islandA + b2Island* islandA = NULL; + if ( islandIdA != B2_NULL_INDEX ) + { + islandA = b2IslandArray_Get( &world->islands, islandIdA ); + while ( islandA->parentIsland != B2_NULL_INDEX ) + { + b2Island* parent = b2IslandArray_Get( &world->islands, islandA->parentIsland ); + if ( parent->parentIsland != B2_NULL_INDEX ) + { + // path compression + islandA->parentIsland = parent->parentIsland; + } + + islandIdA = islandA->parentIsland; + islandA = parent; + } + } + + // Union-find root of islandB + b2Island* islandB = NULL; + if ( islandIdB != B2_NULL_INDEX ) + { + islandB = b2IslandArray_Get( &world->islands, islandIdB ); + while ( islandB->parentIsland != B2_NULL_INDEX ) + { + b2Island* parent = b2IslandArray_Get( &world->islands, islandB->parentIsland ); + if ( parent->parentIsland != B2_NULL_INDEX ) + { + // path compression + islandB->parentIsland = parent->parentIsland; + } + + islandIdB = islandB->parentIsland; + islandB = parent; + } + } + + B2_ASSERT( islandA != NULL || islandB != NULL ); + + // Union-Find link island roots + if ( islandA != islandB && islandA != NULL && islandB != NULL ) + { + B2_ASSERT( islandA != islandB ); + B2_ASSERT( islandB->parentIsland == B2_NULL_INDEX ); + islandB->parentIsland = islandIdA; + } + + if ( islandA != NULL ) + { + b2AddJointToIsland( world, islandIdA, joint ); + } + else + { + b2AddJointToIsland( world, islandIdB, joint ); + } + + // Joints need to have islands merged immediately when they are created + // to keep the island graph valid. + // However, when a body type is being changed the merge can be deferred until + // all joints are linked. + if (mergeIslands) + { + b2MergeAwakeIslands( world ); + } +} + +void b2UnlinkJoint( b2World* world, b2Joint* joint ) +{ + B2_ASSERT( joint->islandId != B2_NULL_INDEX ); + + // remove from island + int islandId = joint->islandId; + b2Island* island = b2IslandArray_Get( &world->islands, islandId ); + + if ( joint->islandPrev != B2_NULL_INDEX ) + { + b2Joint* prevJoint = b2JointArray_Get( &world->joints, joint->islandPrev ); + B2_ASSERT( prevJoint->islandNext == joint->jointId ); + prevJoint->islandNext = joint->islandNext; + } + + if ( joint->islandNext != B2_NULL_INDEX ) + { + b2Joint* nextJoint = b2JointArray_Get( &world->joints, joint->islandNext ); + B2_ASSERT( nextJoint->islandPrev == joint->jointId ); + nextJoint->islandPrev = joint->islandPrev; + } + + if ( island->headJoint == joint->jointId ) + { + island->headJoint = joint->islandNext; + } + + if ( island->tailJoint == joint->jointId ) + { + island->tailJoint = joint->islandPrev; + } + + B2_ASSERT( island->jointCount > 0 ); + island->jointCount -= 1; + island->constraintRemoveCount += 1; + + joint->islandId = B2_NULL_INDEX; + joint->islandPrev = B2_NULL_INDEX; + joint->islandNext = B2_NULL_INDEX; + + b2ValidateIsland( world, islandId ); +} + +// Merge an island into its root island. +// todo we can assume all islands are awake here +static void b2MergeIsland( b2World* world, b2Island* island ) +{ + B2_ASSERT( island->parentIsland != B2_NULL_INDEX ); + + int rootId = island->parentIsland; + b2Island* rootIsland = b2IslandArray_Get( &world->islands, rootId ); + B2_ASSERT( rootIsland->parentIsland == B2_NULL_INDEX ); + + // remap island indices + int bodyId = island->headBody; + while ( bodyId != B2_NULL_INDEX ) + { + b2Body* body = b2BodyArray_Get( &world->bodies, bodyId ); + body->islandId = rootId; + bodyId = body->islandNext; + } + + int contactId = island->headContact; + while ( contactId != B2_NULL_INDEX ) + { + b2Contact* contact = b2ContactArray_Get( &world->contacts, contactId ); + contact->islandId = rootId; + contactId = contact->islandNext; + } + + int jointId = island->headJoint; + while ( jointId != B2_NULL_INDEX ) + { + b2Joint* joint = b2JointArray_Get( &world->joints, jointId ); + joint->islandId = rootId; + jointId = joint->islandNext; + } + + // connect body lists + B2_ASSERT( rootIsland->tailBody != B2_NULL_INDEX ); + b2Body* tailBody = b2BodyArray_Get( &world->bodies, rootIsland->tailBody ); + B2_ASSERT( tailBody->islandNext == B2_NULL_INDEX ); + tailBody->islandNext = island->headBody; + + B2_ASSERT( island->headBody != B2_NULL_INDEX ); + b2Body* headBody = b2BodyArray_Get( &world->bodies, island->headBody ); + B2_ASSERT( headBody->islandPrev == B2_NULL_INDEX ); + headBody->islandPrev = rootIsland->tailBody; + + rootIsland->tailBody = island->tailBody; + rootIsland->bodyCount += island->bodyCount; + + // connect contact lists + if ( rootIsland->headContact == B2_NULL_INDEX ) + { + // Root island has no contacts + B2_ASSERT( rootIsland->tailContact == B2_NULL_INDEX && rootIsland->contactCount == 0 ); + rootIsland->headContact = island->headContact; + rootIsland->tailContact = island->tailContact; + rootIsland->contactCount = island->contactCount; + } + else if ( island->headContact != B2_NULL_INDEX ) + { + // Both islands have contacts + B2_ASSERT( island->tailContact != B2_NULL_INDEX && island->contactCount > 0 ); + B2_ASSERT( rootIsland->tailContact != B2_NULL_INDEX && rootIsland->contactCount > 0 ); + + b2Contact* tailContact = b2ContactArray_Get( &world->contacts, rootIsland->tailContact ); + B2_ASSERT( tailContact->islandNext == B2_NULL_INDEX ); + tailContact->islandNext = island->headContact; + + b2Contact* headContact = b2ContactArray_Get( &world->contacts, island->headContact ); + B2_ASSERT( headContact->islandPrev == B2_NULL_INDEX ); + headContact->islandPrev = rootIsland->tailContact; + + rootIsland->tailContact = island->tailContact; + rootIsland->contactCount += island->contactCount; + } + + if ( rootIsland->headJoint == B2_NULL_INDEX ) + { + // Root island has no joints + B2_ASSERT( rootIsland->tailJoint == B2_NULL_INDEX && rootIsland->jointCount == 0 ); + rootIsland->headJoint = island->headJoint; + rootIsland->tailJoint = island->tailJoint; + rootIsland->jointCount = island->jointCount; + } + else if ( island->headJoint != B2_NULL_INDEX ) + { + // Both islands have joints + B2_ASSERT( island->tailJoint != B2_NULL_INDEX && island->jointCount > 0 ); + B2_ASSERT( rootIsland->tailJoint != B2_NULL_INDEX && rootIsland->jointCount > 0 ); + + b2Joint* tailJoint = b2JointArray_Get( &world->joints, rootIsland->tailJoint ); + B2_ASSERT( tailJoint->islandNext == B2_NULL_INDEX ); + tailJoint->islandNext = island->headJoint; + + b2Joint* headJoint = b2JointArray_Get( &world->joints, island->headJoint ); + B2_ASSERT( headJoint->islandPrev == B2_NULL_INDEX ); + headJoint->islandPrev = rootIsland->tailJoint; + + rootIsland->tailJoint = island->tailJoint; + rootIsland->jointCount += island->jointCount; + } + + // Track removed constraints + rootIsland->constraintRemoveCount += island->constraintRemoveCount; + + b2ValidateIsland( world, rootId ); +} + +// Iterate over all awake islands and merge any that need merging +// Islands that get merged into a root island will be removed from the awake island array +// and returned to the pool. +// todo this might be faster if b2IslandSim held the connectivity data +void b2MergeAwakeIslands( b2World* world ) +{ + b2TracyCZoneNC( merge_islands, "Merge Islands", b2_colorMediumTurquoise, true ); + + b2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet ); + b2IslandSim* islandSims = awakeSet->islandSims.data; + int awakeIslandCount = awakeSet->islandSims.count; + + // Step 1: Ensure every child island points to its root island. This avoids merging a child island with + // a parent island that has already been merged with a grand-parent island. + for ( int i = 0; i < awakeIslandCount; ++i ) + { + int islandId = islandSims[i].islandId; + + b2Island* island = b2IslandArray_Get( &world->islands, islandId ); + + // find the root island + int rootId = islandId; + b2Island* rootIsland = island; + while ( rootIsland->parentIsland != B2_NULL_INDEX ) + { + b2Island* parent = b2IslandArray_Get( &world->islands, rootIsland->parentIsland ); + if ( parent->parentIsland != B2_NULL_INDEX ) + { + // path compression + rootIsland->parentIsland = parent->parentIsland; + } + + rootId = rootIsland->parentIsland; + rootIsland = parent; + } + + if ( rootIsland != island ) + { + island->parentIsland = rootId; + } + } + + // Step 2: merge every awake island into its parent (which must be a root island) + // Reverse to support removal from awake array. + for ( int i = awakeIslandCount - 1; i >= 0; --i ) + { + int islandId = islandSims[i].islandId; + b2Island* island = b2IslandArray_Get( &world->islands, islandId ); + + if ( island->parentIsland == B2_NULL_INDEX ) + { + continue; + } + + b2MergeIsland( world, island ); + + // this call does a remove swap from the end of the island sim array + b2DestroyIsland( world, islandId ); + } + + b2ValidateConnectivity( world ); + + b2TracyCZoneEnd( merge_islands ); +} + +#define B2_CONTACT_REMOVE_THRESHOLD 1 + +void b2SplitIsland( b2World* world, int baseId ) +{ + b2Island* baseIsland = b2IslandArray_Get( &world->islands, baseId ); + int setIndex = baseIsland->setIndex; + + if ( setIndex != b2_awakeSet ) + { + // can only split awake island + return; + } + + if ( baseIsland->constraintRemoveCount == 0 ) + { + // this island doesn't need to be split + return; + } + + b2ValidateIsland( world, baseId ); + + int bodyCount = baseIsland->bodyCount; + + b2Body* bodies = world->bodies.data; + b2StackAllocator* alloc = &world->stackAllocator; + + // No lock is needed because I ensure the allocator is not used while this task is active. + int* stack = b2AllocateStackItem( alloc, bodyCount * sizeof( int ), "island stack" ); + int* bodyIds = b2AllocateStackItem( alloc, bodyCount * sizeof( int ), "body ids" ); + + // Build array containing all body indices from base island. These + // serve as seed bodies for the depth first search (DFS). + int index = 0; + int nextBody = baseIsland->headBody; + while ( nextBody != B2_NULL_INDEX ) + { + bodyIds[index++] = nextBody; + b2Body* body = bodies + nextBody; + + // Clear visitation mark + body->isMarked = false; + + nextBody = body->islandNext; + } + B2_ASSERT( index == bodyCount ); + + // Clear contact island flags. Only need to consider contacts + // already in the base island. + int nextContactId = baseIsland->headContact; + while ( nextContactId != B2_NULL_INDEX ) + { + b2Contact* contact = b2ContactArray_Get( &world->contacts, nextContactId ); + contact->isMarked = false; + nextContactId = contact->islandNext; + } + + // Clear joint island flags. + int nextJoint = baseIsland->headJoint; + while ( nextJoint != B2_NULL_INDEX ) + { + b2Joint* joint = b2JointArray_Get( &world->joints, nextJoint ); + joint->isMarked = false; + nextJoint = joint->islandNext; + } + + // Done with the base split island. + b2DestroyIsland( world, baseId ); + + // Each island is found as a depth first search starting from a seed body + for ( int i = 0; i < bodyCount; ++i ) + { + int seedIndex = bodyIds[i]; + b2Body* seed = bodies + seedIndex; + B2_ASSERT( seed->setIndex == setIndex ); + + if ( seed->isMarked == true ) + { + // The body has already been visited + continue; + } + + int stackCount = 0; + stack[stackCount++] = seedIndex; + seed->isMarked = true; + + // Create new island + // No lock needed because only a single island can split per time step. No islands are being used during the constraint + // solve. However, islands are touched during body finalization. + b2Island* island = b2CreateIsland( world, setIndex ); + + int islandId = island->islandId; + + // Perform a depth first search (DFS) on the constraint graph. + while ( stackCount > 0 ) + { + // Grab the next body off the stack and add it to the island. + int bodyId = stack[--stackCount]; + b2Body* body = bodies + bodyId; + B2_ASSERT( body->setIndex == b2_awakeSet ); + B2_ASSERT( body->isMarked == true ); + + // Add body to island + body->islandId = islandId; + if ( island->tailBody != B2_NULL_INDEX ) + { + bodies[island->tailBody].islandNext = bodyId; + } + body->islandPrev = island->tailBody; + body->islandNext = B2_NULL_INDEX; + island->tailBody = bodyId; + + if ( island->headBody == B2_NULL_INDEX ) + { + island->headBody = bodyId; + } + + island->bodyCount += 1; + + // Search all contacts connected to this body. + int contactKey = body->headContactKey; + while ( contactKey != B2_NULL_INDEX ) + { + int contactId = contactKey >> 1; + int edgeIndex = contactKey & 1; + + b2Contact* contact = b2ContactArray_Get( &world->contacts, contactId ); + B2_ASSERT( contact->contactId == contactId ); + + // Next key + contactKey = contact->edges[edgeIndex].nextKey; + + // Has this contact already been added to this island? + if ( contact->isMarked ) + { + continue; + } + + // Skip sensors + if ( contact->flags & b2_contactSensorFlag ) + { + continue; + } + + // Is this contact enabled and touching? + if ( ( contact->flags & b2_contactTouchingFlag ) == 0 ) + { + continue; + } + + contact->isMarked = true; + + int otherEdgeIndex = edgeIndex ^ 1; + int otherBodyId = contact->edges[otherEdgeIndex].bodyId; + b2Body* otherBody = bodies + otherBodyId; + + // Maybe add other body to stack + if ( otherBody->isMarked == false && otherBody->setIndex != b2_staticSet ) + { + B2_ASSERT( stackCount < bodyCount ); + stack[stackCount++] = otherBodyId; + otherBody->isMarked = true; + } + + // Add contact to island + contact->islandId = islandId; + if ( island->tailContact != B2_NULL_INDEX ) + { + b2Contact* tailContact = b2ContactArray_Get( &world->contacts, island->tailContact ); + tailContact->islandNext = contactId; + } + contact->islandPrev = island->tailContact; + contact->islandNext = B2_NULL_INDEX; + island->tailContact = contactId; + + if ( island->headContact == B2_NULL_INDEX ) + { + island->headContact = contactId; + } + + island->contactCount += 1; + } + + // Search all joints connect to this body. + int jointKey = body->headJointKey; + while ( jointKey != B2_NULL_INDEX ) + { + int jointId = jointKey >> 1; + int edgeIndex = jointKey & 1; + + b2Joint* joint = b2JointArray_Get( &world->joints, jointId ); + B2_ASSERT( joint->jointId == jointId ); + + // Next key + jointKey = joint->edges[edgeIndex].nextKey; + + // Has this joint already been added to this island? + if ( joint->isMarked ) + { + continue; + } + + joint->isMarked = true; + + int otherEdgeIndex = edgeIndex ^ 1; + int otherBodyId = joint->edges[otherEdgeIndex].bodyId; + b2Body* otherBody = bodies + otherBodyId; + + // Don't simulate joints connected to disabled bodies. + if ( otherBody->setIndex == b2_disabledSet ) + { + continue; + } + + // Maybe add other body to stack + if ( otherBody->isMarked == false && otherBody->setIndex == b2_awakeSet ) + { + B2_ASSERT( stackCount < bodyCount ); + stack[stackCount++] = otherBodyId; + otherBody->isMarked = true; + } + + // Add joint to island + joint->islandId = islandId; + if ( island->tailJoint != B2_NULL_INDEX ) + { + b2Joint* tailJoint = b2JointArray_Get( &world->joints, island->tailJoint ); + tailJoint->islandNext = jointId; + } + joint->islandPrev = island->tailJoint; + joint->islandNext = B2_NULL_INDEX; + island->tailJoint = jointId; + + if ( island->headJoint == B2_NULL_INDEX ) + { + island->headJoint = jointId; + } + + island->jointCount += 1; + } + } + + b2ValidateIsland( world, islandId ); + } + + b2FreeStackItem( alloc, bodyIds ); + b2FreeStackItem( alloc, stack ); +} + +// Split an island because some contacts and/or joints have been removed. +// This is called during the constraint solve while islands are not being touched. This uses DFS and touches a lot of memory, +// so it can be quite slow. +// Note: contacts/joints connected to static bodies must belong to an island but don't affect island connectivity +// Note: static bodies are never in an island +// Note: this task interacts with some allocators without locks under the assumption that no other tasks +// are interacting with these data structures. +void b2SplitIslandTask( int startIndex, int endIndex, uint32_t threadIndex, void* context ) +{ + b2TracyCZoneNC( split, "Split Island", b2_colorHoneydew, true ); + + B2_MAYBE_UNUSED( startIndex ); + B2_MAYBE_UNUSED( endIndex ); + B2_MAYBE_UNUSED( threadIndex ); + + b2Timer timer = b2CreateTimer(); + b2World* world = context; + + B2_ASSERT( world->splitIslandId != B2_NULL_INDEX ); + + b2SplitIsland( world, world->splitIslandId ); + + world->profile.splitIslands += b2GetMilliseconds( &timer ); + b2TracyCZoneEnd( split ); +} + +#if B2_VALIDATE +void b2ValidateIsland( b2World* world, int islandId ) +{ + b2Island* island = b2IslandArray_Get( &world->islands, islandId ); + B2_ASSERT( island->islandId == islandId ); + B2_ASSERT( island->setIndex != B2_NULL_INDEX ); + B2_ASSERT( island->headBody != B2_NULL_INDEX ); + + { + B2_ASSERT( island->tailBody != B2_NULL_INDEX ); + B2_ASSERT( island->bodyCount > 0 ); + if ( island->bodyCount > 1 ) + { + B2_ASSERT( island->tailBody != island->headBody ); + } + B2_ASSERT( island->bodyCount <= b2GetIdCount( &world->bodyIdPool ) ); + + int count = 0; + int bodyId = island->headBody; + while ( bodyId != B2_NULL_INDEX ) + { + b2Body* body = b2BodyArray_Get(&world->bodies, bodyId); + B2_ASSERT( body->islandId == islandId ); + B2_ASSERT( body->setIndex == island->setIndex ); + count += 1; + + if ( count == island->bodyCount ) + { + B2_ASSERT( bodyId == island->tailBody ); + } + + bodyId = body->islandNext; + } + B2_ASSERT( count == island->bodyCount ); + } + + if ( island->headContact != B2_NULL_INDEX ) + { + B2_ASSERT( island->tailContact != B2_NULL_INDEX ); + B2_ASSERT( island->contactCount > 0 ); + if ( island->contactCount > 1 ) + { + B2_ASSERT( island->tailContact != island->headContact ); + } + B2_ASSERT( island->contactCount <= b2GetIdCount( &world->contactIdPool ) ); + + int count = 0; + int contactId = island->headContact; + while ( contactId != B2_NULL_INDEX ) + { + b2Contact* contact = b2ContactArray_Get( &world->contacts, contactId ); + B2_ASSERT( contact->setIndex == island->setIndex ); + B2_ASSERT( contact->islandId == islandId ); + count += 1; + + if ( count == island->contactCount ) + { + B2_ASSERT( contactId == island->tailContact ); + } + + contactId = contact->islandNext; + } + B2_ASSERT( count == island->contactCount ); + } + else + { + B2_ASSERT( island->tailContact == B2_NULL_INDEX ); + B2_ASSERT( island->contactCount == 0 ); + } + + if ( island->headJoint != B2_NULL_INDEX ) + { + B2_ASSERT( island->tailJoint != B2_NULL_INDEX ); + B2_ASSERT( island->jointCount > 0 ); + if ( island->jointCount > 1 ) + { + B2_ASSERT( island->tailJoint != island->headJoint ); + } + B2_ASSERT( island->jointCount <= b2GetIdCount( &world->jointIdPool ) ); + + int count = 0; + int jointId = island->headJoint; + while ( jointId != B2_NULL_INDEX ) + { + b2Joint* joint = b2JointArray_Get( &world->joints, jointId ); + B2_ASSERT( joint->setIndex == island->setIndex ); + count += 1; + + if ( count == island->jointCount ) + { + B2_ASSERT( jointId == island->tailJoint ); + } + + jointId = joint->islandNext; + } + B2_ASSERT( count == island->jointCount ); + } + else + { + B2_ASSERT( island->tailJoint == B2_NULL_INDEX ); + B2_ASSERT( island->jointCount == 0 ); + } +} + +#else + +void b2ValidateIsland( b2World* world, int islandId ) +{ + B2_MAYBE_UNUSED( world ); + B2_MAYBE_UNUSED( islandId ); +} +#endif diff --git a/3rdparty/box2d/src/island.h b/3rdparty/box2d/src/island.h new file mode 100644 index 000000000000..c4334641394c --- /dev/null +++ b/3rdparty/box2d/src/island.h @@ -0,0 +1,90 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include "array.h" + +#include +#include + +typedef struct b2Body b2Body; +typedef struct b2Contact b2Contact; +typedef struct b2Joint b2Joint; +typedef struct b2StepContext b2StepContext; +typedef struct b2World b2World; + +// Deterministic solver +// +// Collide all awake contacts +// Use bit array to emit start/stop touching events in defined order, per thread. Try using contact index, assuming contacts are +// created in a deterministic order. bit-wise OR together bit arrays and issue changes: +// - start touching: merge islands - temporary linked list - mark root island dirty - wake all - largest island is root +// - stop touching: increment constraintRemoveCount + +// Persistent island for awake bodies, joints, and contacts +// https://en.wikipedia.org/wiki/Component_(graph_theory) +// https://en.wikipedia.org/wiki/Dynamic_connectivity +// map from int to solver set and index +typedef struct b2Island +{ + // index of solver set stored in b2World + // may be B2_NULL_INDEX + int setIndex; + + // island index within set + // may be B2_NULL_INDEX + int localIndex; + + int islandId; + + int headBody; + int tailBody; + int bodyCount; + + int headContact; + int tailContact; + int contactCount; + + int headJoint; + int tailJoint; + int jointCount; + + // Union find + int parentIsland; + + // Keeps track of how many contacts have been removed from this island. + // This is used to determine if an island is a candidate for splitting. + int constraintRemoveCount; +} b2Island; + +typedef struct b2IslandSim +{ + int islandId; + +} b2IslandSim; + +b2Island* b2CreateIsland( b2World* world, int setIndex ); +void b2DestroyIsland( b2World* world, int islandId ); + +// Link contacts into the island graph when it starts having contact points +void b2LinkContact( b2World* world, b2Contact* contact ); + +// Unlink contact from the island graph when it stops having contact points +void b2UnlinkContact( b2World* world, b2Contact* contact ); + +// Link a joint into the island graph when it is created +void b2LinkJoint( b2World* world, b2Joint* joint, bool mergeIslands ); + +// Unlink a joint from the island graph when it is destroyed +void b2UnlinkJoint( b2World* world, b2Joint* joint ); + +void b2MergeAwakeIslands( b2World* world ); + +void b2SplitIsland( b2World* world, int baseId ); +void b2SplitIslandTask( int startIndex, int endIndex, uint32_t threadIndex, void* context ); + +void b2ValidateIsland( b2World* world, int islandId ); + +B2_ARRAY_INLINE( b2Island, b2Island ); +B2_ARRAY_INLINE( b2IslandSim, b2IslandSim ); diff --git a/3rdparty/box2d/src/joint.c b/3rdparty/box2d/src/joint.c new file mode 100644 index 000000000000..d1af86f83b75 --- /dev/null +++ b/3rdparty/box2d/src/joint.c @@ -0,0 +1,1241 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#include "joint.h" + +#include "body.h" +#include "contact.h" +#include "core.h" +#include "island.h" +#include "shape.h" +#include "solver.h" +#include "solver_set.h" +#include "world.h" + +// needed for dll export +#include "box2d/box2d.h" + +#include + +B2_ARRAY_SOURCE( b2Joint, b2Joint ); +B2_ARRAY_SOURCE( b2JointSim, b2JointSim ); + +b2DistanceJointDef b2DefaultDistanceJointDef( void ) +{ + b2DistanceJointDef def = { 0 }; + def.length = 1.0f; + def.maxLength = b2_huge; + def.internalValue = B2_SECRET_COOKIE; + return def; +} + +b2MotorJointDef b2DefaultMotorJointDef( void ) +{ + b2MotorJointDef def = { 0 }; + def.maxForce = 1.0f; + def.maxTorque = 1.0f; + def.correctionFactor = 0.3f; + def.internalValue = B2_SECRET_COOKIE; + return def; +} + +b2MouseJointDef b2DefaultMouseJointDef( void ) +{ + b2MouseJointDef def = { 0 }; + def.hertz = 4.0f; + def.dampingRatio = 1.0f; + def.maxForce = 1.0f; + def.internalValue = B2_SECRET_COOKIE; + return def; +} + +b2PrismaticJointDef b2DefaultPrismaticJointDef( void ) +{ + b2PrismaticJointDef def = { 0 }; + def.localAxisA = ( b2Vec2 ){ 1.0f, 0.0f }; + def.internalValue = B2_SECRET_COOKIE; + return def; +} + +b2RevoluteJointDef b2DefaultRevoluteJointDef( void ) +{ + b2RevoluteJointDef def = { 0 }; + def.drawSize = 0.25f; + def.internalValue = B2_SECRET_COOKIE; + return def; +} + +b2WeldJointDef b2DefaultWeldJointDef( void ) +{ + b2WeldJointDef def = { 0 }; + def.internalValue = B2_SECRET_COOKIE; + return def; +} + +b2WheelJointDef b2DefaultWheelJointDef( void ) +{ + b2WheelJointDef def = { 0 }; + def.localAxisA.y = 1.0f; + def.enableSpring = true; + def.hertz = 1.0f; + def.dampingRatio = 0.7f; + def.internalValue = B2_SECRET_COOKIE; + return def; +} + +static b2Joint* b2GetJointFullId( b2World* world, b2JointId jointId ) +{ + int id = jointId.index1 - 1; + b2Joint* joint = b2JointArray_Get( &world->joints, id ); + B2_ASSERT( joint->jointId == id && joint->revision == jointId.revision ); + return joint; +} + +b2JointSim* b2GetJointSim( b2World* world, b2Joint* joint ) +{ + if ( joint->setIndex == b2_awakeSet ) + { + B2_ASSERT( 0 <= joint->colorIndex && joint->colorIndex < b2_graphColorCount ); + b2GraphColor* color = world->constraintGraph.colors + joint->colorIndex; + return b2JointSimArray_Get( &color->jointSims, joint->localIndex ); + } + + b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, joint->setIndex ); + return b2JointSimArray_Get( &set->jointSims, joint->localIndex ); +} + +b2JointSim* b2GetJointSimCheckType( b2JointId jointId, b2JointType type ) +{ + B2_MAYBE_UNUSED( type ); + + b2World* world = b2GetWorld( jointId.world0 ); + B2_ASSERT( world->locked == false ); + if ( world->locked ) + { + return NULL; + } + + b2Joint* joint = b2GetJointFullId( world, jointId ); + B2_ASSERT( joint->type == type ); + b2JointSim* jointSim = b2GetJointSim( world, joint ); + B2_ASSERT( jointSim->type == type ); + return jointSim; +} + +typedef struct b2JointPair +{ + b2Joint* joint; + b2JointSim* jointSim; +} b2JointPair; + +static b2JointPair b2CreateJoint( b2World* world, b2Body* bodyA, b2Body* bodyB, void* userData, float drawSize, b2JointType type, + bool collideConnected ) +{ + int bodyIdA = bodyA->id; + int bodyIdB = bodyB->id; + int maxSetIndex = b2MaxInt( bodyA->setIndex, bodyB->setIndex ); + + // Create joint id and joint + int jointId = b2AllocId( &world->jointIdPool ); + if ( jointId == world->joints.count ) + { + b2JointArray_Push( &world->joints, ( b2Joint ){ 0 } ); + } + + b2Joint* joint = b2JointArray_Get( &world->joints, jointId ); + joint->jointId = jointId; + joint->userData = userData; + joint->revision += 1; + joint->setIndex = B2_NULL_INDEX; + joint->colorIndex = B2_NULL_INDEX; + joint->localIndex = B2_NULL_INDEX; + joint->islandId = B2_NULL_INDEX; + joint->islandPrev = B2_NULL_INDEX; + joint->islandNext = B2_NULL_INDEX; + joint->drawSize = drawSize; + joint->type = type; + joint->collideConnected = collideConnected; + joint->isMarked = false; + + // Doubly linked list on bodyA + joint->edges[0].bodyId = bodyIdA; + joint->edges[0].prevKey = B2_NULL_INDEX; + joint->edges[0].nextKey = bodyA->headJointKey; + + int keyA = ( jointId << 1 ) | 0; + if ( bodyA->headJointKey != B2_NULL_INDEX ) + { + b2Joint* jointA = b2JointArray_Get( &world->joints, bodyA->headJointKey >> 1 ); + b2JointEdge* edgeA = jointA->edges + ( bodyA->headJointKey & 1 ); + edgeA->prevKey = keyA; + } + bodyA->headJointKey = keyA; + bodyA->jointCount += 1; + + // Doubly linked list on bodyB + joint->edges[1].bodyId = bodyIdB; + joint->edges[1].prevKey = B2_NULL_INDEX; + joint->edges[1].nextKey = bodyB->headJointKey; + + int keyB = ( jointId << 1 ) | 1; + if ( bodyB->headJointKey != B2_NULL_INDEX ) + { + b2Joint* jointB = b2JointArray_Get( &world->joints, bodyB->headJointKey >> 1 ); + b2JointEdge* edgeB = jointB->edges + ( bodyB->headJointKey & 1 ); + edgeB->prevKey = keyB; + } + bodyB->headJointKey = keyB; + bodyB->jointCount += 1; + + b2JointSim* jointSim; + + if ( bodyA->setIndex == b2_disabledSet || bodyB->setIndex == b2_disabledSet ) + { + // if either body is disabled, create in disabled set + b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, b2_disabledSet ); + joint->setIndex = b2_disabledSet; + joint->localIndex = set->jointSims.count; + + jointSim = b2JointSimArray_Add( &set->jointSims ); + jointSim->jointId = jointId; + jointSim->bodyIdA = bodyIdA; + jointSim->bodyIdB = bodyIdB; + } + else if ( bodyA->setIndex == b2_staticSet && bodyB->setIndex == b2_staticSet ) + { + // joint is connecting static bodies + b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, b2_staticSet ); + joint->setIndex = b2_staticSet; + joint->localIndex = set->jointSims.count; + + jointSim = b2JointSimArray_Add( &set->jointSims ); + jointSim->jointId = jointId; + jointSim->bodyIdA = bodyIdA; + jointSim->bodyIdB = bodyIdB; + } + else if ( bodyA->setIndex == b2_awakeSet || bodyB->setIndex == b2_awakeSet ) + { + // if either body is sleeping, wake it + if ( maxSetIndex >= b2_firstSleepingSet ) + { + b2WakeSolverSet( world, maxSetIndex ); + } + + joint->setIndex = b2_awakeSet; + + jointSim = b2CreateJointInGraph( world, joint ); + jointSim->jointId = jointId; + jointSim->bodyIdA = bodyIdA; + jointSim->bodyIdB = bodyIdB; + } + else + { + // joint connected between sleeping and/or static bodies + B2_ASSERT( bodyA->setIndex >= b2_firstSleepingSet || bodyB->setIndex >= b2_firstSleepingSet ); + B2_ASSERT( bodyA->setIndex != b2_staticSet || bodyB->setIndex != b2_staticSet ); + + // joint should go into the sleeping set (not static set) + int setIndex = maxSetIndex; + + b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, setIndex ); + joint->setIndex = setIndex; + joint->localIndex = set->jointSims.count; + jointSim = b2JointSimArray_Add( &set->jointSims ); + jointSim->jointId = jointId; + jointSim->bodyIdA = bodyIdA; + jointSim->bodyIdB = bodyIdB; + + if ( bodyA->setIndex != bodyB->setIndex && bodyA->setIndex >= b2_firstSleepingSet && + bodyB->setIndex >= b2_firstSleepingSet ) + { + // merge sleeping sets + b2MergeSolverSets( world, bodyA->setIndex, bodyB->setIndex ); + B2_ASSERT( bodyA->setIndex == bodyB->setIndex ); + + // fix potentially invalid set index + setIndex = bodyA->setIndex; + + b2SolverSet* mergedSet = b2SolverSetArray_Get( &world->solverSets, setIndex ); + + // Careful! The joint sim pointer was orphaned by the set merge. + jointSim = b2JointSimArray_Get( &mergedSet->jointSims, joint->localIndex ); + } + + B2_ASSERT( joint->setIndex == setIndex ); + } + + B2_ASSERT( jointSim->jointId == jointId ); + B2_ASSERT( jointSim->bodyIdA == bodyIdA ); + B2_ASSERT( jointSim->bodyIdB == bodyIdB ); + + if ( joint->setIndex > b2_disabledSet ) + { + // Add edge to island graph + bool mergeIslands = true; + b2LinkJoint( world, joint, mergeIslands ); + } + + b2ValidateSolverSets( world ); + + return ( b2JointPair ){ joint, jointSim }; +} + +static void b2DestroyContactsBetweenBodies( b2World* world, b2Body* bodyA, b2Body* bodyB ) +{ + int contactKey; + int otherBodyId; + + // use the smaller of the two contact lists + if ( bodyA->contactCount < bodyB->contactCount ) + { + contactKey = bodyA->headContactKey; + otherBodyId = bodyB->id; + } + else + { + contactKey = bodyB->headContactKey; + otherBodyId = bodyA->id; + } + + // no need to wake bodies when a joint removes collision between them + bool wakeBodies = false; + + // destroy the contacts + while ( contactKey != B2_NULL_INDEX ) + { + int contactId = contactKey >> 1; + int edgeIndex = contactKey & 1; + + b2Contact* contact = b2ContactArray_Get( &world->contacts, contactId ); + contactKey = contact->edges[edgeIndex].nextKey; + + int otherEdgeIndex = edgeIndex ^ 1; + if ( contact->edges[otherEdgeIndex].bodyId == otherBodyId ) + { + // Careful, this removes the contact from the current doubly linked list + b2DestroyContact( world, contact, wakeBodies ); + } + } + + b2ValidateSolverSets( world ); +} + +b2JointId b2CreateDistanceJoint( b2WorldId worldId, const b2DistanceJointDef* def ) +{ + b2CheckDef( def ); + b2World* world = b2GetWorldFromId( worldId ); + + B2_ASSERT( world->locked == false ); + + if ( world->locked ) + { + return ( b2JointId ){ 0 }; + } + + B2_ASSERT( b2Body_IsValid( def->bodyIdA ) ); + B2_ASSERT( b2Body_IsValid( def->bodyIdB ) ); + B2_ASSERT( b2IsValid( def->length ) && def->length > 0.0f ); + + b2Body* bodyA = b2GetBodyFullId( world, def->bodyIdA ); + b2Body* bodyB = b2GetBodyFullId( world, def->bodyIdB ); + + b2JointPair pair = b2CreateJoint( world, bodyA, bodyB, def->userData, 1.0f, b2_distanceJoint, def->collideConnected ); + + b2JointSim* joint = pair.jointSim; + joint->type = b2_distanceJoint; + joint->localOriginAnchorA = def->localAnchorA; + joint->localOriginAnchorB = def->localAnchorB; + + b2DistanceJoint empty = { 0 }; + joint->distanceJoint = empty; + joint->distanceJoint.length = b2MaxFloat( def->length, b2_linearSlop ); + joint->distanceJoint.hertz = def->hertz; + joint->distanceJoint.dampingRatio = def->dampingRatio; + joint->distanceJoint.minLength = b2MaxFloat( def->minLength, b2_linearSlop ); + joint->distanceJoint.maxLength = b2MaxFloat( def->minLength, def->maxLength ); + joint->distanceJoint.maxMotorForce = def->maxMotorForce; + joint->distanceJoint.motorSpeed = def->motorSpeed; + joint->distanceJoint.enableSpring = def->enableSpring; + joint->distanceJoint.enableLimit = def->enableLimit; + joint->distanceJoint.enableMotor = def->enableMotor; + joint->distanceJoint.impulse = 0.0f; + joint->distanceJoint.lowerImpulse = 0.0f; + joint->distanceJoint.upperImpulse = 0.0f; + joint->distanceJoint.motorImpulse = 0.0f; + + // If the joint prevents collisions, then destroy all contacts between attached bodies + if ( def->collideConnected == false ) + { + b2DestroyContactsBetweenBodies( world, bodyA, bodyB ); + } + + b2JointId jointId = { joint->jointId + 1, world->worldId, pair.joint->revision }; + return jointId; +} + +b2JointId b2CreateMotorJoint( b2WorldId worldId, const b2MotorJointDef* def ) +{ + b2CheckDef( def ); + b2World* world = b2GetWorldFromId( worldId ); + + B2_ASSERT( world->locked == false ); + + if ( world->locked ) + { + return ( b2JointId ){ 0 }; + } + + b2Body* bodyA = b2GetBodyFullId( world, def->bodyIdA ); + b2Body* bodyB = b2GetBodyFullId( world, def->bodyIdB ); + + b2JointPair pair = b2CreateJoint( world, bodyA, bodyB, def->userData, 1.0f, b2_motorJoint, def->collideConnected ); + b2JointSim* joint = pair.jointSim; + + joint->type = b2_motorJoint; + joint->localOriginAnchorA = ( b2Vec2 ){ 0.0f, 0.0f }; + joint->localOriginAnchorB = ( b2Vec2 ){ 0.0f, 0.0f }; + joint->motorJoint = ( b2MotorJoint ){ 0 }; + joint->motorJoint.linearOffset = def->linearOffset; + joint->motorJoint.angularOffset = def->angularOffset; + joint->motorJoint.maxForce = def->maxForce; + joint->motorJoint.maxTorque = def->maxTorque; + joint->motorJoint.correctionFactor = b2ClampFloat( def->correctionFactor, 0.0f, 1.0f ); + + // If the joint prevents collisions, then destroy all contacts between attached bodies + if ( def->collideConnected == false ) + { + b2DestroyContactsBetweenBodies( world, bodyA, bodyB ); + } + + b2JointId jointId = { joint->jointId + 1, world->worldId, pair.joint->revision }; + return jointId; +} + +b2JointId b2CreateMouseJoint( b2WorldId worldId, const b2MouseJointDef* def ) +{ + b2CheckDef( def ); + b2World* world = b2GetWorldFromId( worldId ); + + B2_ASSERT( world->locked == false ); + + if ( world->locked ) + { + return ( b2JointId ){ 0 }; + } + + b2Body* bodyA = b2GetBodyFullId( world, def->bodyIdA ); + b2Body* bodyB = b2GetBodyFullId( world, def->bodyIdB ); + + b2Transform transformA = b2GetBodyTransformQuick( world, bodyA ); + b2Transform transformB = b2GetBodyTransformQuick( world, bodyB ); + + b2JointPair pair = b2CreateJoint( world, bodyA, bodyB, def->userData, 1.0f, b2_mouseJoint, def->collideConnected ); + + b2JointSim* joint = pair.jointSim; + joint->type = b2_mouseJoint; + joint->localOriginAnchorA = b2InvTransformPoint( transformA, def->target ); + joint->localOriginAnchorB = b2InvTransformPoint( transformB, def->target ); + + b2MouseJoint empty = { 0 }; + joint->mouseJoint = empty; + joint->mouseJoint.targetA = def->target; + joint->mouseJoint.hertz = def->hertz; + joint->mouseJoint.dampingRatio = def->dampingRatio; + joint->mouseJoint.maxForce = def->maxForce; + + b2JointId jointId = { joint->jointId + 1, world->worldId, pair.joint->revision }; + return jointId; +} + +b2JointId b2CreateRevoluteJoint( b2WorldId worldId, const b2RevoluteJointDef* def ) +{ + b2CheckDef( def ); + b2World* world = b2GetWorldFromId( worldId ); + + B2_ASSERT( world->locked == false ); + + if ( world->locked ) + { + return ( b2JointId ){ 0 }; + } + + b2Body* bodyA = b2GetBodyFullId( world, def->bodyIdA ); + b2Body* bodyB = b2GetBodyFullId( world, def->bodyIdB ); + + b2JointPair pair = + b2CreateJoint( world, bodyA, bodyB, def->userData, def->drawSize, b2_revoluteJoint, def->collideConnected ); + + b2JointSim* joint = pair.jointSim; + joint->type = b2_revoluteJoint; + joint->localOriginAnchorA = def->localAnchorA; + joint->localOriginAnchorB = def->localAnchorB; + + b2RevoluteJoint empty = { 0 }; + joint->revoluteJoint = empty; + + joint->revoluteJoint.referenceAngle = b2ClampFloat( def->referenceAngle, -b2_pi, b2_pi ); + joint->revoluteJoint.linearImpulse = b2Vec2_zero; + joint->revoluteJoint.axialMass = 0.0f; + joint->revoluteJoint.springImpulse = 0.0f; + joint->revoluteJoint.motorImpulse = 0.0f; + joint->revoluteJoint.lowerImpulse = 0.0f; + joint->revoluteJoint.upperImpulse = 0.0f; + joint->revoluteJoint.hertz = def->hertz; + joint->revoluteJoint.dampingRatio = def->dampingRatio; + joint->revoluteJoint.lowerAngle = b2MinFloat( def->lowerAngle, def->upperAngle ); + joint->revoluteJoint.upperAngle = b2MaxFloat( def->lowerAngle, def->upperAngle ); + joint->revoluteJoint.lowerAngle = b2ClampFloat( joint->revoluteJoint.lowerAngle, -b2_pi, b2_pi ); + joint->revoluteJoint.upperAngle = b2ClampFloat( joint->revoluteJoint.upperAngle, -b2_pi, b2_pi ); + joint->revoluteJoint.maxMotorTorque = def->maxMotorTorque; + joint->revoluteJoint.motorSpeed = def->motorSpeed; + joint->revoluteJoint.enableSpring = def->enableSpring; + joint->revoluteJoint.enableLimit = def->enableLimit; + joint->revoluteJoint.enableMotor = def->enableMotor; + + // If the joint prevents collisions, then destroy all contacts between attached bodies + if ( def->collideConnected == false ) + { + b2DestroyContactsBetweenBodies( world, bodyA, bodyB ); + } + + b2JointId jointId = { joint->jointId + 1, world->worldId, pair.joint->revision }; + return jointId; +} + +b2JointId b2CreatePrismaticJoint( b2WorldId worldId, const b2PrismaticJointDef* def ) +{ + b2CheckDef( def ); + b2World* world = b2GetWorldFromId( worldId ); + + B2_ASSERT( world->locked == false ); + + if ( world->locked ) + { + return ( b2JointId ){ 0 }; + } + + b2Body* bodyA = b2GetBodyFullId( world, def->bodyIdA ); + b2Body* bodyB = b2GetBodyFullId( world, def->bodyIdB ); + + b2JointPair pair = b2CreateJoint( world, bodyA, bodyB, def->userData, 1.0f, b2_prismaticJoint, def->collideConnected ); + + b2JointSim* joint = pair.jointSim; + joint->type = b2_prismaticJoint; + joint->localOriginAnchorA = def->localAnchorA; + joint->localOriginAnchorB = def->localAnchorB; + + b2PrismaticJoint empty = { 0 }; + joint->prismaticJoint = empty; + + joint->prismaticJoint.localAxisA = b2Normalize( def->localAxisA ); + joint->prismaticJoint.referenceAngle = def->referenceAngle; + joint->prismaticJoint.impulse = b2Vec2_zero; + joint->prismaticJoint.axialMass = 0.0f; + joint->prismaticJoint.springImpulse = 0.0f; + joint->prismaticJoint.motorImpulse = 0.0f; + joint->prismaticJoint.lowerImpulse = 0.0f; + joint->prismaticJoint.upperImpulse = 0.0f; + joint->prismaticJoint.hertz = def->hertz; + joint->prismaticJoint.dampingRatio = def->dampingRatio; + joint->prismaticJoint.lowerTranslation = def->lowerTranslation; + joint->prismaticJoint.upperTranslation = def->upperTranslation; + joint->prismaticJoint.maxMotorForce = def->maxMotorForce; + joint->prismaticJoint.motorSpeed = def->motorSpeed; + joint->prismaticJoint.enableSpring = def->enableSpring; + joint->prismaticJoint.enableLimit = def->enableLimit; + joint->prismaticJoint.enableMotor = def->enableMotor; + + // If the joint prevents collisions, then destroy all contacts between attached bodies + if ( def->collideConnected == false ) + { + b2DestroyContactsBetweenBodies( world, bodyA, bodyB ); + } + + b2JointId jointId = { joint->jointId + 1, world->worldId, pair.joint->revision }; + return jointId; +} + +b2JointId b2CreateWeldJoint( b2WorldId worldId, const b2WeldJointDef* def ) +{ + b2CheckDef( def ); + b2World* world = b2GetWorldFromId( worldId ); + + B2_ASSERT( world->locked == false ); + + if ( world->locked ) + { + return ( b2JointId ){ 0 }; + } + + b2Body* bodyA = b2GetBodyFullId( world, def->bodyIdA ); + b2Body* bodyB = b2GetBodyFullId( world, def->bodyIdB ); + + b2JointPair pair = b2CreateJoint( world, bodyA, bodyB, def->userData, 1.0f, b2_weldJoint, def->collideConnected ); + + b2JointSim* joint = pair.jointSim; + joint->type = b2_weldJoint; + joint->localOriginAnchorA = def->localAnchorA; + joint->localOriginAnchorB = def->localAnchorB; + + b2WeldJoint empty = { 0 }; + joint->weldJoint = empty; + joint->weldJoint.referenceAngle = def->referenceAngle; + joint->weldJoint.linearHertz = def->linearHertz; + joint->weldJoint.linearDampingRatio = def->linearDampingRatio; + joint->weldJoint.angularHertz = def->angularHertz; + joint->weldJoint.angularDampingRatio = def->angularDampingRatio; + joint->weldJoint.linearImpulse = b2Vec2_zero; + joint->weldJoint.angularImpulse = 0.0f; + + // If the joint prevents collisions, then destroy all contacts between attached bodies + if ( def->collideConnected == false ) + { + b2DestroyContactsBetweenBodies( world, bodyA, bodyB ); + } + + b2JointId jointId = { joint->jointId + 1, world->worldId, pair.joint->revision }; + return jointId; +} + +b2JointId b2CreateWheelJoint( b2WorldId worldId, const b2WheelJointDef* def ) +{ + b2CheckDef( def ); + b2World* world = b2GetWorldFromId( worldId ); + + B2_ASSERT( world->locked == false ); + + if ( world->locked ) + { + return ( b2JointId ){ 0 }; + } + + b2Body* bodyA = b2GetBodyFullId( world, def->bodyIdA ); + b2Body* bodyB = b2GetBodyFullId( world, def->bodyIdB ); + + b2JointPair pair = b2CreateJoint( world, bodyA, bodyB, def->userData, 1.0f, b2_wheelJoint, def->collideConnected ); + + b2JointSim* joint = pair.jointSim; + joint->type = b2_wheelJoint; + joint->localOriginAnchorA = def->localAnchorA; + joint->localOriginAnchorB = def->localAnchorB; + + joint->wheelJoint = ( b2WheelJoint ){ 0 }; + joint->wheelJoint.localAxisA = b2Normalize( def->localAxisA ); + joint->wheelJoint.perpMass = 0.0f; + joint->wheelJoint.axialMass = 0.0f; + joint->wheelJoint.motorImpulse = 0.0f; + joint->wheelJoint.lowerImpulse = 0.0f; + joint->wheelJoint.upperImpulse = 0.0f; + joint->wheelJoint.lowerTranslation = def->lowerTranslation; + joint->wheelJoint.upperTranslation = def->upperTranslation; + joint->wheelJoint.maxMotorTorque = def->maxMotorTorque; + joint->wheelJoint.motorSpeed = def->motorSpeed; + joint->wheelJoint.hertz = def->hertz; + joint->wheelJoint.dampingRatio = def->dampingRatio; + joint->wheelJoint.enableSpring = def->enableSpring; + joint->wheelJoint.enableLimit = def->enableLimit; + joint->wheelJoint.enableMotor = def->enableMotor; + + // If the joint prevents collisions, then destroy all contacts between attached bodies + if ( def->collideConnected == false ) + { + b2DestroyContactsBetweenBodies( world, bodyA, bodyB ); + } + + b2JointId jointId = { joint->jointId + 1, world->worldId, pair.joint->revision }; + return jointId; +} + +void b2DestroyJointInternal( b2World* world, b2Joint* joint, bool wakeBodies ) +{ + int jointId = joint->jointId; + + b2JointEdge* edgeA = joint->edges + 0; + b2JointEdge* edgeB = joint->edges + 1; + + int idA = edgeA->bodyId; + int idB = edgeB->bodyId; + b2Body* bodyA = b2BodyArray_Get( &world->bodies, idA ); + b2Body* bodyB = b2BodyArray_Get( &world->bodies, idB ); + + // Remove from body A + if ( edgeA->prevKey != B2_NULL_INDEX ) + { + b2Joint* prevJoint = b2JointArray_Get( &world->joints, edgeA->prevKey >> 1 ); + b2JointEdge* prevEdge = prevJoint->edges + ( edgeA->prevKey & 1 ); + prevEdge->nextKey = edgeA->nextKey; + } + + if ( edgeA->nextKey != B2_NULL_INDEX ) + { + b2Joint* nextJoint = b2JointArray_Get( &world->joints, edgeA->nextKey >> 1 ); + b2JointEdge* nextEdge = nextJoint->edges + ( edgeA->nextKey & 1 ); + nextEdge->prevKey = edgeA->prevKey; + } + + int edgeKeyA = ( jointId << 1 ) | 0; + if ( bodyA->headJointKey == edgeKeyA ) + { + bodyA->headJointKey = edgeA->nextKey; + } + + bodyA->jointCount -= 1; + + // Remove from body B + if ( edgeB->prevKey != B2_NULL_INDEX ) + { + b2Joint* prevJoint = b2JointArray_Get( &world->joints, edgeB->prevKey >> 1 ); + b2JointEdge* prevEdge = prevJoint->edges + ( edgeB->prevKey & 1 ); + prevEdge->nextKey = edgeB->nextKey; + } + + if ( edgeB->nextKey != B2_NULL_INDEX ) + { + b2Joint* nextJoint = b2JointArray_Get( &world->joints, edgeB->nextKey >> 1 ); + b2JointEdge* nextEdge = nextJoint->edges + ( edgeB->nextKey & 1 ); + nextEdge->prevKey = edgeB->prevKey; + } + + int edgeKeyB = ( jointId << 1 ) | 1; + if ( bodyB->headJointKey == edgeKeyB ) + { + bodyB->headJointKey = edgeB->nextKey; + } + + bodyB->jointCount -= 1; + + if ( joint->islandId != B2_NULL_INDEX ) + { + B2_ASSERT( joint->setIndex > b2_disabledSet ); + b2UnlinkJoint( world, joint ); + } + else + { + B2_ASSERT( joint->setIndex <= b2_disabledSet ); + } + + // Remove joint from solver set that owns it + int setIndex = joint->setIndex; + int localIndex = joint->localIndex; + + if ( setIndex == b2_awakeSet ) + { + b2RemoveJointFromGraph( world, joint->edges[0].bodyId, joint->edges[1].bodyId, joint->colorIndex, localIndex ); + } + else + { + b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, setIndex ); + int movedIndex = b2JointSimArray_RemoveSwap( &set->jointSims, localIndex ); + if ( movedIndex != B2_NULL_INDEX ) + { + // Fix moved joint + b2JointSim* movedJointSim = set->jointSims.data + localIndex; + int movedId = movedJointSim->jointId; + b2Joint* movedJoint = b2JointArray_Get( &world->joints, movedId ); + B2_ASSERT( movedJoint->localIndex == movedIndex ); + movedJoint->localIndex = localIndex; + } + } + + // Free joint and id (preserve joint revision) + joint->setIndex = B2_NULL_INDEX; + joint->localIndex = B2_NULL_INDEX; + joint->colorIndex = B2_NULL_INDEX; + joint->jointId = B2_NULL_INDEX; + b2FreeId( &world->jointIdPool, jointId ); + + if ( wakeBodies ) + { + b2WakeBody( world, bodyA ); + b2WakeBody( world, bodyB ); + } + + b2ValidateSolverSets( world ); +} + +void b2DestroyJoint( b2JointId jointId ) +{ + b2World* world = b2GetWorld( jointId.world0 ); + B2_ASSERT( world->locked == false ); + + if ( world->locked ) + { + return; + } + + b2Joint* joint = b2GetJointFullId( world, jointId ); + + b2DestroyJointInternal( world, joint, true ); +} + +b2JointType b2Joint_GetType( b2JointId jointId ) +{ + b2World* world = b2GetWorld( jointId.world0 ); + b2Joint* joint = b2GetJointFullId( world, jointId ); + return joint->type; +} + +b2BodyId b2Joint_GetBodyA( b2JointId jointId ) +{ + b2World* world = b2GetWorld( jointId.world0 ); + b2Joint* joint = b2GetJointFullId( world, jointId ); + return b2MakeBodyId( world, joint->edges[0].bodyId ); +} + +b2BodyId b2Joint_GetBodyB( b2JointId jointId ) +{ + b2World* world = b2GetWorld( jointId.world0 ); + b2Joint* joint = b2GetJointFullId( world, jointId ); + return b2MakeBodyId( world, joint->edges[1].bodyId ); +} + +b2WorldId b2Joint_GetWorld( b2JointId jointId ) +{ + b2World* world = b2GetWorld( jointId.world0 ); + return ( b2WorldId ){ jointId.world0 + 1, world->revision }; +} + +b2Vec2 b2Joint_GetLocalAnchorA( b2JointId jointId ) +{ + b2World* world = b2GetWorld( jointId.world0 ); + b2Joint* joint = b2GetJointFullId( world, jointId ); + b2JointSim* jointSim = b2GetJointSim( world, joint ); + return jointSim->localOriginAnchorA; +} + +b2Vec2 b2Joint_GetLocalAnchorB( b2JointId jointId ) +{ + b2World* world = b2GetWorld( jointId.world0 ); + b2Joint* joint = b2GetJointFullId( world, jointId ); + b2JointSim* jointSim = b2GetJointSim( world, joint ); + return jointSim->localOriginAnchorB; +} + +void b2Joint_SetCollideConnected( b2JointId jointId, bool shouldCollide ) +{ + b2World* world = b2GetWorldLocked( jointId.world0 ); + if ( world == NULL ) + { + return; + } + + b2Joint* joint = b2GetJointFullId( world, jointId ); + if ( joint->collideConnected == shouldCollide ) + { + return; + } + + joint->collideConnected = shouldCollide; + + b2Body* bodyA = b2BodyArray_Get( &world->bodies, joint->edges[0].bodyId ); + b2Body* bodyB = b2BodyArray_Get( &world->bodies, joint->edges[1].bodyId ); + + if ( shouldCollide ) + { + // need to tell the broad-phase to look for new pairs for one of the + // two bodies. Pick the one with the fewest shapes. + int shapeCountA = bodyA->shapeCount; + int shapeCountB = bodyB->shapeCount; + + int shapeId = shapeCountA < shapeCountB ? bodyA->headShapeId : bodyB->headShapeId; + while ( shapeId != B2_NULL_INDEX ) + { + b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId ); + + if ( shape->proxyKey != B2_NULL_INDEX ) + { + b2BufferMove( &world->broadPhase, shape->proxyKey ); + } + + shapeId = shape->nextShapeId; + } + } + else + { + b2DestroyContactsBetweenBodies( world, bodyA, bodyB ); + } +} + +bool b2Joint_GetCollideConnected( b2JointId jointId ) +{ + b2World* world = b2GetWorld( jointId.world0 ); + b2Joint* joint = b2GetJointFullId( world, jointId ); + return joint->collideConnected; +} + +void b2Joint_SetUserData( b2JointId jointId, void* userData ) +{ + b2World* world = b2GetWorld( jointId.world0 ); + b2Joint* joint = b2GetJointFullId( world, jointId ); + joint->userData = userData; +} + +void* b2Joint_GetUserData( b2JointId jointId ) +{ + b2World* world = b2GetWorld( jointId.world0 ); + b2Joint* joint = b2GetJointFullId( world, jointId ); + return joint->userData; +} + +void b2Joint_WakeBodies( b2JointId jointId ) +{ + b2World* world = b2GetWorldLocked( jointId.world0 ); + if ( world == NULL ) + { + return; + } + + b2Joint* joint = b2GetJointFullId( world, jointId ); + b2Body* bodyA = b2BodyArray_Get( &world->bodies, joint->edges[0].bodyId ); + b2Body* bodyB = b2BodyArray_Get( &world->bodies, joint->edges[1].bodyId ); + + b2WakeBody( world, bodyA ); + b2WakeBody( world, bodyB ); +} + +extern b2Vec2 b2GetDistanceJointForce( b2World* world, b2JointSim* base ); +extern b2Vec2 b2GetMotorJointForce( b2World* world, b2JointSim* base ); +extern b2Vec2 b2GetMouseJointForce( b2World* world, b2JointSim* base ); +extern b2Vec2 b2GetPrismaticJointForce( b2World* world, b2JointSim* base ); +extern b2Vec2 b2GetRevoluteJointForce( b2World* world, b2JointSim* base ); +extern b2Vec2 b2GetWeldJointForce( b2World* world, b2JointSim* base ); +extern b2Vec2 b2GetWheelJointForce( b2World* world, b2JointSim* base ); + +b2Vec2 b2Joint_GetConstraintForce( b2JointId jointId ) +{ + b2World* world = b2GetWorld( jointId.world0 ); + b2Joint* joint = b2GetJointFullId( world, jointId ); + b2JointSim* base = b2GetJointSim( world, joint ); + + switch ( joint->type ) + { + case b2_distanceJoint: + return b2GetDistanceJointForce( world, base ); + + case b2_motorJoint: + return b2GetMotorJointForce( world, base ); + + case b2_mouseJoint: + return b2GetMouseJointForce( world, base ); + + case b2_prismaticJoint: + return b2GetPrismaticJointForce( world, base ); + + case b2_revoluteJoint: + return b2GetRevoluteJointForce( world, base ); + + case b2_weldJoint: + return b2GetWeldJointForce( world, base ); + + case b2_wheelJoint: + return b2GetWheelJointForce( world, base ); + + default: + B2_ASSERT( false ); + return b2Vec2_zero; + } +} + +extern float b2GetMotorJointTorque( b2World* world, b2JointSim* base ); +extern float b2GetMouseJointTorque( b2World* world, b2JointSim* base ); +extern float b2GetPrismaticJointTorque( b2World* world, b2JointSim* base ); +extern float b2GetRevoluteJointTorque( b2World* world, b2JointSim* base ); +extern float b2GetWeldJointTorque( b2World* world, b2JointSim* base ); +extern float b2GetWheelJointTorque( b2World* world, b2JointSim* base ); + +float b2Joint_GetConstraintTorque( b2JointId jointId ) +{ + b2World* world = b2GetWorld( jointId.world0 ); + b2Joint* joint = b2GetJointFullId( world, jointId ); + b2JointSim* base = b2GetJointSim( world, joint ); + + switch ( joint->type ) + { + case b2_distanceJoint: + return 0.0f; + + case b2_motorJoint: + return b2GetMotorJointTorque( world, base ); + + case b2_mouseJoint: + return b2GetMouseJointTorque( world, base ); + + case b2_prismaticJoint: + return b2GetPrismaticJointTorque( world, base ); + + case b2_revoluteJoint: + return b2GetRevoluteJointTorque( world, base ); + + case b2_weldJoint: + return b2GetWeldJointTorque( world, base ); + + case b2_wheelJoint: + return b2GetWheelJointTorque( world, base ); + + default: + B2_ASSERT( false ); + return 0.0f; + } +} + +extern void b2PrepareDistanceJoint( b2JointSim* base, b2StepContext* context ); +extern void b2PrepareMotorJoint( b2JointSim* base, b2StepContext* context ); +extern void b2PrepareMouseJoint( b2JointSim* base, b2StepContext* context ); +extern void b2PreparePrismaticJoint( b2JointSim* base, b2StepContext* context ); +extern void b2PrepareRevoluteJoint( b2JointSim* base, b2StepContext* context ); +extern void b2PrepareWeldJoint( b2JointSim* base, b2StepContext* context ); +extern void b2PrepareWheelJoint( b2JointSim* base, b2StepContext* context ); + +void b2PrepareJoint( b2JointSim* joint, b2StepContext* context ) +{ + switch ( joint->type ) + { + case b2_distanceJoint: + b2PrepareDistanceJoint( joint, context ); + break; + + case b2_motorJoint: + b2PrepareMotorJoint( joint, context ); + break; + + case b2_mouseJoint: + b2PrepareMouseJoint( joint, context ); + break; + + case b2_prismaticJoint: + b2PreparePrismaticJoint( joint, context ); + break; + + case b2_revoluteJoint: + b2PrepareRevoluteJoint( joint, context ); + break; + + case b2_weldJoint: + b2PrepareWeldJoint( joint, context ); + break; + + case b2_wheelJoint: + b2PrepareWheelJoint( joint, context ); + break; + + default: + B2_ASSERT( false ); + } +} + +extern void b2WarmStartDistanceJoint( b2JointSim* base, b2StepContext* context ); +extern void b2WarmStartMotorJoint( b2JointSim* base, b2StepContext* context ); +extern void b2WarmStartMouseJoint( b2JointSim* base, b2StepContext* context ); +extern void b2WarmStartPrismaticJoint( b2JointSim* base, b2StepContext* context ); +extern void b2WarmStartRevoluteJoint( b2JointSim* base, b2StepContext* context ); +extern void b2WarmStartWeldJoint( b2JointSim* base, b2StepContext* context ); +extern void b2WarmStartWheelJoint( b2JointSim* base, b2StepContext* context ); + +void b2WarmStartJoint( b2JointSim* joint, b2StepContext* context ) +{ + switch ( joint->type ) + { + case b2_distanceJoint: + b2WarmStartDistanceJoint( joint, context ); + break; + + case b2_motorJoint: + b2WarmStartMotorJoint( joint, context ); + break; + + case b2_mouseJoint: + b2WarmStartMouseJoint( joint, context ); + break; + + case b2_prismaticJoint: + b2WarmStartPrismaticJoint( joint, context ); + break; + + case b2_revoluteJoint: + b2WarmStartRevoluteJoint( joint, context ); + break; + + case b2_weldJoint: + b2WarmStartWeldJoint( joint, context ); + break; + + case b2_wheelJoint: + b2WarmStartWheelJoint( joint, context ); + break; + + default: + B2_ASSERT( false ); + } +} + +extern void b2SolveDistanceJoint( b2JointSim* base, b2StepContext* context, bool useBias ); +extern void b2SolveMotorJoint( b2JointSim* base, b2StepContext* context, bool useBias ); +extern void b2SolveMouseJoint( b2JointSim* base, b2StepContext* context ); +extern void b2SolvePrismaticJoint( b2JointSim* base, b2StepContext* context, bool useBias ); +extern void b2SolveRevoluteJoint( b2JointSim* base, b2StepContext* context, bool useBias ); +extern void b2SolveWeldJoint( b2JointSim* base, b2StepContext* context, bool useBias ); +extern void b2SolveWheelJoint( b2JointSim* base, b2StepContext* context, bool useBias ); + +void b2SolveJoint( b2JointSim* joint, b2StepContext* context, bool useBias ) +{ + switch ( joint->type ) + { + case b2_distanceJoint: + b2SolveDistanceJoint( joint, context, useBias ); + break; + + case b2_motorJoint: + b2SolveMotorJoint( joint, context, useBias ); + break; + + case b2_mouseJoint: + b2SolveMouseJoint( joint, context ); + break; + + case b2_prismaticJoint: + b2SolvePrismaticJoint( joint, context, useBias ); + break; + + case b2_revoluteJoint: + b2SolveRevoluteJoint( joint, context, useBias ); + break; + + case b2_weldJoint: + b2SolveWeldJoint( joint, context, useBias ); + break; + + case b2_wheelJoint: + b2SolveWheelJoint( joint, context, useBias ); + break; + + default: + B2_ASSERT( false ); + } +} + +void b2PrepareOverflowJoints( b2StepContext* context ) +{ + b2TracyCZoneNC( prepare_joints, "PrepJoints", b2_colorOldLace, true ); + + b2ConstraintGraph* graph = context->graph; + b2JointSim* joints = graph->colors[b2_overflowIndex].jointSims.data; + int jointCount = graph->colors[b2_overflowIndex].jointSims.count; + + for ( int i = 0; i < jointCount; ++i ) + { + b2JointSim* joint = joints + i; + b2PrepareJoint( joint, context ); + } + + b2TracyCZoneEnd( prepare_joints ); +} + +void b2WarmStartOverflowJoints( b2StepContext* context ) +{ + b2TracyCZoneNC( prepare_joints, "PrepJoints", b2_colorOldLace, true ); + + b2ConstraintGraph* graph = context->graph; + b2JointSim* joints = graph->colors[b2_overflowIndex].jointSims.data; + int jointCount = graph->colors[b2_overflowIndex].jointSims.count; + + for ( int i = 0; i < jointCount; ++i ) + { + b2JointSim* joint = joints + i; + b2WarmStartJoint( joint, context ); + } + + b2TracyCZoneEnd( prepare_joints ); +} + +void b2SolveOverflowJoints( b2StepContext* context, bool useBias ) +{ + b2TracyCZoneNC( solve_joints, "SolveJoints", b2_colorLemonChiffon, true ); + + b2ConstraintGraph* graph = context->graph; + b2JointSim* joints = graph->colors[b2_overflowIndex].jointSims.data; + int jointCount = graph->colors[b2_overflowIndex].jointSims.count; + + for ( int i = 0; i < jointCount; ++i ) + { + b2JointSim* joint = joints + i; + b2SolveJoint( joint, context, useBias ); + } + + b2TracyCZoneEnd( solve_joints ); +} + +extern void b2DrawDistanceJoint( b2DebugDraw* draw, b2JointSim* base, b2Transform transformA, b2Transform transformB ); +extern void b2DrawPrismaticJoint( b2DebugDraw* draw, b2JointSim* base, b2Transform transformA, b2Transform transformB ); +extern void b2DrawRevoluteJoint( b2DebugDraw* draw, b2JointSim* base, b2Transform transformA, b2Transform transformB, + float drawSize ); +extern void b2DrawWheelJoint( b2DebugDraw* draw, b2JointSim* base, b2Transform transformA, b2Transform transformB ); + +void b2DrawJoint( b2DebugDraw* draw, b2World* world, b2Joint* joint ) +{ + b2Body* bodyA = b2BodyArray_Get( &world->bodies, joint->edges[0].bodyId ); + b2Body* bodyB = b2BodyArray_Get( &world->bodies, joint->edges[1].bodyId ); + if ( bodyA->setIndex == b2_disabledSet || bodyB->setIndex == b2_disabledSet ) + { + return; + } + + b2JointSim* jointSim = b2GetJointSim( world, joint ); + + b2Transform transformA = b2GetBodyTransformQuick( world, bodyA ); + b2Transform transformB = b2GetBodyTransformQuick( world, bodyB ); + b2Vec2 pA = b2TransformPoint( transformA, jointSim->localOriginAnchorA ); + b2Vec2 pB = b2TransformPoint( transformB, jointSim->localOriginAnchorB ); + + b2HexColor color = b2_colorDarkSeaGreen; + + switch ( joint->type ) + { + case b2_distanceJoint: + b2DrawDistanceJoint( draw, jointSim, transformA, transformB ); + break; + + case b2_mouseJoint: + { + b2Vec2 target = jointSim->mouseJoint.targetA; + + b2HexColor c1 = b2_colorGreen; + draw->DrawPoint( target, 4.0f, c1, draw->context ); + draw->DrawPoint( pB, 4.0f, c1, draw->context ); + + b2HexColor c2 = b2_colorGray8; + draw->DrawSegment( target, pB, c2, draw->context ); + } + break; + + case b2_prismaticJoint: + b2DrawPrismaticJoint( draw, jointSim, transformA, transformB ); + break; + + case b2_revoluteJoint: + b2DrawRevoluteJoint( draw, jointSim, transformA, transformB, joint->drawSize ); + break; + + case b2_wheelJoint: + b2DrawWheelJoint( draw, jointSim, transformA, transformB ); + break; + + default: + draw->DrawSegment( transformA.p, pA, color, draw->context ); + draw->DrawSegment( pA, pB, color, draw->context ); + draw->DrawSegment( transformB.p, pB, color, draw->context ); + } + + if ( draw->drawGraphColors ) + { + b2HexColor colors[b2_graphColorCount] = { b2_colorRed, b2_colorOrange, b2_colorYellow, b2_colorGreen, + b2_colorCyan, b2_colorBlue, b2_colorViolet, b2_colorPink, + b2_colorChocolate, b2_colorGoldenrod, b2_colorCoral, b2_colorBlack }; + + int colorIndex = joint->colorIndex; + if ( colorIndex != B2_NULL_INDEX ) + { + b2Vec2 p = b2Lerp( pA, pB, 0.5f ); + draw->DrawPoint( p, 5.0f, colors[colorIndex], draw->context ); + } + } +} diff --git a/3rdparty/box2d/src/joint.h b/3rdparty/box2d/src/joint.h new file mode 100644 index 000000000000..80ac04bfb6bb --- /dev/null +++ b/3rdparty/box2d/src/joint.h @@ -0,0 +1,289 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT +#pragma once + +#include "array.h" +#include "solver.h" + +#include "box2d/types.h" + +typedef struct b2DebugDraw b2DebugDraw; +typedef struct b2StepContext b2StepContext; +typedef struct b2World b2World; + +/// A joint edge is used to connect bodies and joints together +/// in a joint graph where each body is a node and each joint +/// is an edge. A joint edge belongs to a doubly linked list +/// maintained in each attached body. Each joint has two joint +/// nodes, one for each attached body. +typedef struct b2JointEdge +{ + int bodyId; + int prevKey; + int nextKey; +} b2JointEdge; + +// Map from b2JointId to b2Joint in the solver sets +typedef struct b2Joint +{ + void* userData; + + // index of simulation set stored in b2World + // B2_NULL_INDEX when slot is free + int setIndex; + + // index into the constraint graph color array, may be B2_NULL_INDEX for sleeping/disabled joints + // B2_NULL_INDEX when slot is free + int colorIndex; + + // joint index within set or graph color + // B2_NULL_INDEX when slot is free + int localIndex; + + b2JointEdge edges[2]; + + int jointId; + int islandId; + int islandPrev; + int islandNext; + + // This is monotonically advanced when a body is allocated in this slot + // Used to check for invalid b2JointId + int revision; + + float drawSize; + + b2JointType type; + bool isMarked; + bool collideConnected; + +} b2Joint; + +typedef struct b2DistanceJoint +{ + float length; + float hertz; + float dampingRatio; + float minLength; + float maxLength; + + float maxMotorForce; + float motorSpeed; + + float impulse; + float lowerImpulse; + float upperImpulse; + float motorImpulse; + + int indexA; + int indexB; + b2Vec2 anchorA; + b2Vec2 anchorB; + b2Vec2 deltaCenter; + b2Softness distanceSoftness; + float axialMass; + + bool enableSpring; + bool enableLimit; + bool enableMotor; +} b2DistanceJoint; + +typedef struct b2MotorJoint +{ + b2Vec2 linearOffset; + float angularOffset; + b2Vec2 linearImpulse; + float angularImpulse; + float maxForce; + float maxTorque; + float correctionFactor; + + int indexA; + int indexB; + b2Vec2 anchorA; + b2Vec2 anchorB; + b2Vec2 deltaCenter; + float deltaAngle; + b2Mat22 linearMass; + float angularMass; +} b2MotorJoint; + +typedef struct b2MouseJoint +{ + b2Vec2 targetA; + float hertz; + float dampingRatio; + float maxForce; + + b2Vec2 linearImpulse; + float angularImpulse; + + b2Softness linearSoftness; + b2Softness angularSoftness; + int indexB; + b2Vec2 anchorB; + b2Vec2 deltaCenter; + b2Mat22 linearMass; +} b2MouseJoint; + +typedef struct b2PrismaticJoint +{ + b2Vec2 localAxisA; + b2Vec2 impulse; + float springImpulse; + float motorImpulse; + float lowerImpulse; + float upperImpulse; + float hertz; + float dampingRatio; + float maxMotorForce; + float motorSpeed; + float referenceAngle; + float lowerTranslation; + float upperTranslation; + + int indexA; + int indexB; + b2Vec2 anchorA; + b2Vec2 anchorB; + b2Vec2 axisA; + b2Vec2 deltaCenter; + float deltaAngle; + float axialMass; + b2Softness springSoftness; + + bool enableSpring; + bool enableLimit; + bool enableMotor; +} b2PrismaticJoint; + +typedef struct b2RevoluteJoint +{ + b2Vec2 linearImpulse; + float springImpulse; + float motorImpulse; + float lowerImpulse; + float upperImpulse; + float hertz; + float dampingRatio; + float maxMotorTorque; + float motorSpeed; + float referenceAngle; + float lowerAngle; + float upperAngle; + + int indexA; + int indexB; + b2Vec2 anchorA; + b2Vec2 anchorB; + b2Vec2 deltaCenter; + float deltaAngle; + float axialMass; + b2Softness springSoftness; + + bool enableSpring; + bool enableMotor; + bool enableLimit; +} b2RevoluteJoint; + +typedef struct b2WeldJoint +{ + float referenceAngle; + float linearHertz; + float linearDampingRatio; + float angularHertz; + float angularDampingRatio; + + b2Softness linearSoftness; + b2Softness angularSoftness; + b2Vec2 linearImpulse; + float angularImpulse; + + int indexA; + int indexB; + b2Vec2 anchorA; + b2Vec2 anchorB; + b2Vec2 deltaCenter; + float deltaAngle; + float axialMass; +} b2WeldJoint; + +typedef struct b2WheelJoint +{ + b2Vec2 localAxisA; + float perpImpulse; + float motorImpulse; + float springImpulse; + float lowerImpulse; + float upperImpulse; + float maxMotorTorque; + float motorSpeed; + float lowerTranslation; + float upperTranslation; + float hertz; + float dampingRatio; + + int indexA; + int indexB; + b2Vec2 anchorA; + b2Vec2 anchorB; + b2Vec2 axisA; + b2Vec2 deltaCenter; + float perpMass; + float motorMass; + float axialMass; + b2Softness springSoftness; + + bool enableSpring; + bool enableMotor; + bool enableLimit; +} b2WheelJoint; + +/// The base joint class. Joints are used to constraint two bodies together in +/// various fashions. Some joints also feature limits and motors. +typedef struct b2JointSim +{ + int jointId; + + int bodyIdA; + int bodyIdB; + + b2JointType type; + + // Anchors relative to body origin + b2Vec2 localOriginAnchorA; + b2Vec2 localOriginAnchorB; + + float invMassA, invMassB; + float invIA, invIB; + + union + { + b2DistanceJoint distanceJoint; + b2MotorJoint motorJoint; + b2MouseJoint mouseJoint; + b2RevoluteJoint revoluteJoint; + b2PrismaticJoint prismaticJoint; + b2WeldJoint weldJoint; + b2WheelJoint wheelJoint; + }; +} b2JointSim; + +void b2DestroyJointInternal( b2World* world, b2Joint* joint, bool wakeBodies ); + +b2JointSim* b2GetJointSim( b2World* world, b2Joint* joint ); +b2JointSim* b2GetJointSimCheckType( b2JointId jointId, b2JointType type ); + +void b2PrepareJoint( b2JointSim* joint, b2StepContext* context ); +void b2WarmStartJoint( b2JointSim* joint, b2StepContext* context ); +void b2SolveJoint( b2JointSim* joint, b2StepContext* context, bool useBias ); + +void b2PrepareOverflowJoints( b2StepContext* context ); +void b2WarmStartOverflowJoints( b2StepContext* context ); +void b2SolveOverflowJoints( b2StepContext* context, bool useBias ); + +void b2DrawJoint( b2DebugDraw* draw, b2World* world, b2Joint* joint ); + +// Define inline functions for arrays +B2_ARRAY_INLINE( b2Joint, b2Joint ); +B2_ARRAY_INLINE( b2JointSim, b2JointSim ); diff --git a/3rdparty/box2d/src/manifold.c b/3rdparty/box2d/src/manifold.c new file mode 100644 index 000000000000..b66785d2850d --- /dev/null +++ b/3rdparty/box2d/src/manifold.c @@ -0,0 +1,1601 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#include "core.h" + +#include "box2d/collision.h" +#include "box2d/math_functions.h" + +#include +#include + +#define B2_MAKE_ID( A, B ) ( (uint8_t)( A ) << 8 | (uint8_t)( B ) ) + +static b2Polygon b2MakeCapsule( b2Vec2 p1, b2Vec2 p2, float radius ) +{ + b2Polygon shape = { 0 }; + shape.vertices[0] = p1; + shape.vertices[1] = p2; + shape.centroid = b2Lerp( p1, p2, 0.5f ); + + b2Vec2 d = b2Sub( p2, p1 ); + B2_ASSERT( b2LengthSquared( d ) > FLT_EPSILON ); + b2Vec2 axis = b2Normalize( d ); + b2Vec2 normal = b2RightPerp( axis ); + + shape.normals[0] = normal; + shape.normals[1] = b2Neg( normal ); + shape.count = 2; + shape.radius = radius; + + return shape; +} + +// point = qA * localAnchorA + pA +// localAnchorB = qBc * (point - pB) +// anchorB = point - pB = qA * localAnchorA + pA - pB +// = anchorA + (pA - pB) +b2Manifold b2CollideCircles( const b2Circle* circleA, b2Transform xfA, const b2Circle* circleB, b2Transform xfB ) +{ + b2Manifold manifold = { 0 }; + + b2Transform xf = b2InvMulTransforms( xfA, xfB ); + + b2Vec2 pointA = circleA->center; + b2Vec2 pointB = b2TransformPoint( xf, circleB->center ); + + float distance; + b2Vec2 normal = b2GetLengthAndNormalize( &distance, b2Sub( pointB, pointA ) ); + + float radiusA = circleA->radius; + float radiusB = circleB->radius; + + float separation = distance - radiusA - radiusB; + if ( separation > b2_speculativeDistance ) + { + return manifold; + } + + b2Vec2 cA = b2MulAdd( pointA, radiusA, normal ); + b2Vec2 cB = b2MulAdd( pointB, -radiusB, normal ); + b2Vec2 contactPointA = b2Lerp( cA, cB, 0.5f ); + + manifold.normal = b2RotateVector( xfA.q, normal ); + b2ManifoldPoint* mp = manifold.points + 0; + mp->anchorA = b2RotateVector( xfA.q, contactPointA ); + mp->anchorB = b2Add( mp->anchorA, b2Sub( xfA.p, xfB.p ) ); + mp->point = b2Add( xfA.p, mp->anchorA ); + mp->separation = separation; + mp->id = 0; + manifold.pointCount = 1; + return manifold; +} + +/// Compute the collision manifold between a capsule and circle +b2Manifold b2CollideCapsuleAndCircle( const b2Capsule* capsuleA, b2Transform xfA, const b2Circle* circleB, b2Transform xfB ) +{ + b2Manifold manifold = { 0 }; + + b2Transform xf = b2InvMulTransforms( xfA, xfB ); + + // Compute circle position in the frame of the capsule. + b2Vec2 pB = b2TransformPoint( xf, circleB->center ); + + // Compute closest point + b2Vec2 p1 = capsuleA->center1; + b2Vec2 p2 = capsuleA->center2; + + b2Vec2 e = b2Sub( p2, p1 ); + + // dot(p - pA, e) = 0 + // dot(p - (p1 + s1 * e), e) = 0 + // s1 = dot(p - p1, e) + b2Vec2 pA; + float s1 = b2Dot( b2Sub( pB, p1 ), e ); + float s2 = b2Dot( b2Sub( p2, pB ), e ); + if ( s1 < 0.0f ) + { + // p1 region + pA = p1; + } + else if ( s2 < 0.0f ) + { + // p2 region + pA = p2; + } + else + { + // circle colliding with segment interior + float s = s1 / b2Dot( e, e ); + pA = b2MulAdd( p1, s, e ); + } + + float distance; + b2Vec2 normal = b2GetLengthAndNormalize( &distance, b2Sub( pB, pA ) ); + + float radiusA = capsuleA->radius; + float radiusB = circleB->radius; + float separation = distance - radiusA - radiusB; + if ( separation > b2_speculativeDistance ) + { + return manifold; + } + + b2Vec2 cA = b2MulAdd( pA, radiusA, normal ); + b2Vec2 cB = b2MulAdd( pB, -radiusB, normal ); + b2Vec2 contactPointA = b2Lerp( cA, cB, 0.5f ); + + manifold.normal = b2RotateVector( xfA.q, normal ); + b2ManifoldPoint* mp = manifold.points + 0; + mp->anchorA = b2RotateVector( xfA.q, contactPointA ); + mp->anchorB = b2Add( mp->anchorA, b2Sub( xfA.p, xfB.p ) ); + mp->point = b2Add( xfA.p, mp->anchorA ); + mp->separation = separation; + mp->id = 0; + manifold.pointCount = 1; + return manifold; +} + +b2Manifold b2CollidePolygonAndCircle( const b2Polygon* polygonA, b2Transform xfA, const b2Circle* circleB, b2Transform xfB ) +{ + b2Manifold manifold = { 0 }; + const float speculativeDistance = b2_speculativeDistance; + + b2Transform xf = b2InvMulTransforms( xfA, xfB ); + + // Compute circle position in the frame of the polygon. + b2Vec2 c = b2TransformPoint( xf, circleB->center ); + float radiusA = polygonA->radius; + float radiusB = circleB->radius; + float radius = radiusA + radiusB; + + // Find the min separating edge. + int32_t normalIndex = 0; + float separation = -FLT_MAX; + int32_t vertexCount = polygonA->count; + const b2Vec2* vertices = polygonA->vertices; + const b2Vec2* normals = polygonA->normals; + + for ( int32_t i = 0; i < vertexCount; ++i ) + { + float s = b2Dot( normals[i], b2Sub( c, vertices[i] ) ); + if ( s > separation ) + { + separation = s; + normalIndex = i; + } + } + + if ( separation > radius + speculativeDistance ) + { + return manifold; + } + + // Vertices of the reference edge. + int32_t vertIndex1 = normalIndex; + int32_t vertIndex2 = vertIndex1 + 1 < vertexCount ? vertIndex1 + 1 : 0; + b2Vec2 v1 = vertices[vertIndex1]; + b2Vec2 v2 = vertices[vertIndex2]; + + // Compute barycentric coordinates + float u1 = b2Dot( b2Sub( c, v1 ), b2Sub( v2, v1 ) ); + float u2 = b2Dot( b2Sub( c, v2 ), b2Sub( v1, v2 ) ); + + if ( u1 < 0.0f && separation > FLT_EPSILON ) + { + // Circle center is closest to v1 and safely outside the polygon + b2Vec2 normal = b2Normalize( b2Sub( c, v1 ) ); + separation = b2Dot( b2Sub( c, v1 ), normal ); + if ( separation > radius + speculativeDistance ) + { + return manifold; + } + + b2Vec2 cA = b2MulAdd( v1, radiusA, normal ); + b2Vec2 cB = b2MulSub( c, radiusB, normal ); + b2Vec2 contactPointA = b2Lerp( cA, cB, 0.5f ); + + manifold.normal = b2RotateVector( xfA.q, normal ); + b2ManifoldPoint* mp = manifold.points + 0; + mp->anchorA = b2RotateVector( xfA.q, contactPointA ); + mp->anchorB = b2Add( mp->anchorA, b2Sub( xfA.p, xfB.p ) ); + mp->point = b2Add( xfA.p, mp->anchorA ); + mp->separation = b2Dot( b2Sub( cB, cA ), normal ); + mp->id = 0; + manifold.pointCount = 1; + } + else if ( u2 < 0.0f && separation > FLT_EPSILON ) + { + // Circle center is closest to v2 and safely outside the polygon + b2Vec2 normal = b2Normalize( b2Sub( c, v2 ) ); + separation = b2Dot( b2Sub( c, v2 ), normal ); + if ( separation > radius + speculativeDistance ) + { + return manifold; + } + + b2Vec2 cA = b2MulAdd( v2, radiusA, normal ); + b2Vec2 cB = b2MulSub( c, radiusB, normal ); + b2Vec2 contactPointA = b2Lerp( cA, cB, 0.5f ); + + manifold.normal = b2RotateVector( xfA.q, normal ); + b2ManifoldPoint* mp = manifold.points + 0; + mp->anchorA = b2RotateVector( xfA.q, contactPointA ); + mp->anchorB = b2Add( mp->anchorA, b2Sub( xfA.p, xfB.p ) ); + mp->point = b2Add( xfA.p, mp->anchorA ); + mp->separation = b2Dot( b2Sub( cB, cA ), normal ); + mp->id = 0; + manifold.pointCount = 1; + } + else + { + // Circle center is between v1 and v2. Center may be inside polygon + b2Vec2 normal = normals[normalIndex]; + manifold.normal = b2RotateVector( xfA.q, normal ); + + // cA is the projection of the circle center onto to the reference edge + b2Vec2 cA = b2MulAdd( c, radiusA - b2Dot( b2Sub( c, v1 ), normal ), normal ); + + // cB is the deepest point on the circle with respect to the reference edge + b2Vec2 cB = b2MulSub( c, radiusB, normal ); + + b2Vec2 contactPointA = b2Lerp( cA, cB, 0.5f ); + + // The contact point is the midpoint in world space + b2ManifoldPoint* mp = manifold.points + 0; + mp->anchorA = b2RotateVector( xfA.q, contactPointA ); + mp->anchorB = b2Add( mp->anchorA, b2Sub( xfA.p, xfB.p ) ); + mp->point = b2Add( xfA.p, mp->anchorA ); + mp->separation = separation - radius; + mp->id = 0; + manifold.pointCount = 1; + } + + return manifold; +} + +// Follows Ericson 5.1.9 Closest Points of Two Line Segments +// Adds some logic to support clipping to get two contact points +b2Manifold b2CollideCapsules( const b2Capsule* capsuleA, b2Transform xfA, const b2Capsule* capsuleB, b2Transform xfB ) +{ + b2Vec2 origin = capsuleA->center1; + + // Shift polyA to origin + // pw = q * pb + p + // pw = q * (pbs + origin) + p + // pw = q * pbs + (p + q * origin) + b2Transform sfA = { b2Add( xfA.p, b2RotateVector( xfA.q, origin ) ), xfA.q }; + b2Transform xf = b2InvMulTransforms( sfA, xfB ); + + b2Vec2 p1 = b2Vec2_zero; + b2Vec2 q1 = b2Sub( capsuleA->center2, origin ); + + b2Vec2 p2 = b2TransformPoint( xf, capsuleB->center1 ); + b2Vec2 q2 = b2TransformPoint( xf, capsuleB->center2 ); + + b2Vec2 d1 = b2Sub( q1, p1 ); + b2Vec2 d2 = b2Sub( q2, p2 ); + + float dd1 = b2Dot( d1, d1 ); + float dd2 = b2Dot( d2, d2 ); + + const float epsSqr = FLT_EPSILON * FLT_EPSILON; + B2_ASSERT( dd1 > epsSqr && dd2 > epsSqr ); + + b2Vec2 r = b2Sub( p1, p2 ); + float rd1 = b2Dot( r, d1 ); + float rd2 = b2Dot( r, d2 ); + + float d12 = b2Dot( d1, d2 ); + + float denom = dd1 * dd2 - d12 * d12; + + // Fraction on segment 1 + float f1 = 0.0f; + if ( denom != 0.0f ) + { + // not parallel + f1 = b2ClampFloat( ( d12 * rd2 - rd1 * dd2 ) / denom, 0.0f, 1.0f ); + } + + // Compute point on segment 2 closest to p1 + f1 * d1 + float f2 = ( d12 * f1 + rd2 ) / dd2; + + // Clamping of segment 2 requires a do over on segment 1 + if ( f2 < 0.0f ) + { + f2 = 0.0f; + f1 = b2ClampFloat( -rd1 / dd1, 0.0f, 1.0f ); + } + else if ( f2 > 1.0f ) + { + f2 = 1.0f; + f1 = b2ClampFloat( ( d12 - rd1 ) / dd1, 0.0f, 1.0f ); + } + + b2Vec2 closest1 = b2MulAdd( p1, f1, d1 ); + b2Vec2 closest2 = b2MulAdd( p2, f2, d2 ); + float distanceSquared = b2DistanceSquared( closest1, closest2 ); + + b2Manifold manifold = { 0 }; + float radiusA = capsuleA->radius; + float radiusB = capsuleB->radius; + float radius = radiusA + radiusB; + float maxDistance = radius + b2_speculativeDistance; + + if ( distanceSquared > maxDistance * maxDistance ) + { + return manifold; + } + + float distance = sqrt( distanceSquared ); + + float length1, length2; + b2Vec2 u1 = b2GetLengthAndNormalize( &length1, d1 ); + b2Vec2 u2 = b2GetLengthAndNormalize( &length2, d2 ); + + // Does segment B project outside segment A? + float fp2 = b2Dot( b2Sub( p2, p1 ), u1 ); + float fq2 = b2Dot( b2Sub( q2, p1 ), u1 ); + bool outsideA = ( fp2 <= 0.0f && fq2 <= 0.0f ) || ( fp2 >= length1 && fq2 >= length1 ); + + // Does segment A project outside segment B? + float fp1 = b2Dot( b2Sub( p1, p2 ), u2 ); + float fq1 = b2Dot( b2Sub( q1, p2 ), u2 ); + bool outsideB = ( fp1 <= 0.0f && fq1 <= 0.0f ) || ( fp1 >= length2 && fq1 >= length2 ); + + if ( outsideA == false && outsideB == false) + { + // attempt to clip + // this may yield contact points with excessive separation + // in that case the algorithm falls back to single point collision + + // find reference edge using SAT + b2Vec2 normalA; + float separationA; + + { + normalA = b2LeftPerp( u1 ); + float ss1 = b2Dot( b2Sub( p2, p1 ), normalA ); + float ss2 = b2Dot( b2Sub( q2, p1 ), normalA ); + float s1p = ss1 < ss2 ? ss1 : ss2; + float s1n = -ss1 < -ss2 ? -ss1 : -ss2; + + if ( s1p > s1n ) + { + separationA = s1p; + } + else + { + separationA = s1n; + normalA = b2Neg( normalA ); + } + } + + b2Vec2 normalB; + float separationB; + { + normalB = b2LeftPerp( u2 ); + float ss1 = b2Dot( b2Sub( p1, p2 ), normalB ); + float ss2 = b2Dot( b2Sub( q1, p2 ), normalB ); + float s1p = ss1 < ss2 ? ss1 : ss2; + float s1n = -ss1 < -ss2 ? -ss1 : -ss2; + + if ( s1p > s1n ) + { + separationB = s1p; + } + else + { + separationB = s1n; + normalB = b2Neg( normalB ); + } + } + + if ( separationA >= separationB ) + { + manifold.normal = normalA; + + b2Vec2 cp = p2; + b2Vec2 cq = q2; + + // clip to p1 + if ( fp2 < 0.0f && fq2 > 0.0f ) + { + cp = b2Lerp( p2, q2, ( 0.0f - fp2 ) / ( fq2 - fp2 ) ); + } + else if ( fq2 < 0.0f && fp2 > 0.0f ) + { + cq = b2Lerp( q2, p2, ( 0.0f - fq2 ) / ( fp2 - fq2 ) ); + } + + // clip to q1 + if ( fp2 > length1 && fq2 < length1 ) + { + cp = b2Lerp( p2, q2, ( fp2 - length1 ) / ( fp2 - fq2 ) ); + } + else if ( fq2 > length1 && fp2 < length1 ) + { + cq = b2Lerp( q2, p2, ( fq2 - length1 ) / ( fq2 - fp2 ) ); + } + + float sp = b2Dot( b2Sub( cp, p1 ), normalA ); + float sq = b2Dot( b2Sub( cq, p1 ), normalA ); + + if ( sp <= distance + b2_linearSlop || sq <= distance + b2_linearSlop ) + { + b2ManifoldPoint* mp; + mp = manifold.points + 0; + mp->anchorA = b2MulAdd( cp, 0.5f * ( radiusA - radiusB - sp ), normalA ); + mp->separation = sp - radius; + mp->id = B2_MAKE_ID( 0, 0 ); + + mp = manifold.points + 1; + mp->anchorA = b2MulAdd( cq, 0.5f * ( radiusA - radiusB - sq ), normalA ); + mp->separation = sq - radius; + mp->id = B2_MAKE_ID( 0, 1 ); + manifold.pointCount = 2; + } + } + else + { + // normal always points from A to B + manifold.normal = b2Neg( normalB ); + + b2Vec2 cp = p1; + b2Vec2 cq = q1; + + // clip to p2 + if ( fp1 < 0.0f && fq1 > 0.0f ) + { + cp = b2Lerp( p1, q1, ( 0.0f - fp1 ) / ( fq1 - fp1 ) ); + } + else if ( fq1 < 0.0f && fp1 > 0.0f ) + { + cq = b2Lerp( q1, p1, ( 0.0f - fq1 ) / ( fp1 - fq1 ) ); + } + + // clip to q2 + if ( fp1 > length2 && fq1 < length2 ) + { + cp = b2Lerp( p1, q1, ( fp1 - length2 ) / ( fp1 - fq1 ) ); + } + else if ( fq1 > length2 && fp1 < length2 ) + { + cq = b2Lerp( q1, p1, ( fq1 - length2 ) / ( fq1 - fp1 ) ); + } + + float sp = b2Dot( b2Sub( cp, p2 ), normalB ); + float sq = b2Dot( b2Sub( cq, p2 ), normalB ); + + if ( sp <= distance + b2_linearSlop || sq <= distance + b2_linearSlop ) + { + b2ManifoldPoint* mp; + mp = manifold.points + 0; + mp->anchorA = b2MulAdd( cp, 0.5f * ( radiusB - radiusA - sp ), normalB ); + mp->separation = sp - radius; + mp->id = B2_MAKE_ID( 0, 0 ); + mp = manifold.points + 1; + mp->anchorA = b2MulAdd( cq, 0.5f * ( radiusB - radiusA - sq ), normalB ); + mp->separation = sq - radius; + mp->id = B2_MAKE_ID( 1, 0 ); + manifold.pointCount = 2; + } + } + } + + if (manifold.pointCount == 0) + { + // single point collision + b2Vec2 normal = b2Sub( closest2, closest1 ); + if ( b2Dot( normal, normal ) > epsSqr ) + { + normal = b2Normalize( normal ); + } + else + { + normal = b2LeftPerp( u1 ); + } + + b2Vec2 c1 = b2MulAdd( closest1, radiusA, normal ); + b2Vec2 c2 = b2MulAdd( closest2, -radiusB, normal ); + + int i1 = f1 == 0.0f ? 0 : 1; + int i2 = f2 == 0.0f ? 0 : 1; + + manifold.normal = normal; + manifold.points[0].anchorA = b2Lerp( c1, c2, 0.5f ); + manifold.points[0].separation = sqrtf( distanceSquared ) - radius; + manifold.points[0].id = B2_MAKE_ID( i1, i2 ); + manifold.pointCount = 1; + } + + // Convert manifold to world space + if ( manifold.pointCount > 0 ) + { + manifold.normal = b2RotateVector( xfA.q, manifold.normal ); + for ( int i = 0; i < manifold.pointCount; ++i ) + { + b2ManifoldPoint* mp = manifold.points + i; + + // anchor points relative to shape origin in world space + mp->anchorA = b2RotateVector( xfA.q, b2Add( mp->anchorA, origin ) ); + mp->anchorB = b2Add( mp->anchorA, b2Sub( xfA.p, xfB.p ) ); + mp->point = b2Add( xfA.p, mp->anchorA ); + } + } + + return manifold; +} + +b2Manifold b2CollideSegmentAndCapsule( const b2Segment* segmentA, b2Transform xfA, const b2Capsule* capsuleB, b2Transform xfB ) +{ + b2Capsule capsuleA = { segmentA->point1, segmentA->point2, 0.0f }; + return b2CollideCapsules( &capsuleA, xfA, capsuleB, xfB ); +} + +b2Manifold b2CollidePolygonAndCapsule( const b2Polygon* polygonA, b2Transform xfA, const b2Capsule* capsuleB, b2Transform xfB ) +{ + b2Polygon polyB = b2MakeCapsule( capsuleB->center1, capsuleB->center2, capsuleB->radius ); + return b2CollidePolygons( polygonA, xfA, &polyB, xfB ); +} + +// Polygon clipper used to compute contact points when there are potentially two contact points. +static b2Manifold b2ClipPolygons( const b2Polygon* polyA, const b2Polygon* polyB, int32_t edgeA, int32_t edgeB, bool flip ) +{ + b2Manifold manifold = { 0 }; + + // reference polygon + const b2Polygon* poly1; + int32_t i11, i12; + + // incident polygon + const b2Polygon* poly2; + int32_t i21, i22; + + if ( flip ) + { + poly1 = polyB; + poly2 = polyA; + i11 = edgeB; + i12 = edgeB + 1 < polyB->count ? edgeB + 1 : 0; + i21 = edgeA; + i22 = edgeA + 1 < polyA->count ? edgeA + 1 : 0; + } + else + { + poly1 = polyA; + poly2 = polyB; + i11 = edgeA; + i12 = edgeA + 1 < polyA->count ? edgeA + 1 : 0; + i21 = edgeB; + i22 = edgeB + 1 < polyB->count ? edgeB + 1 : 0; + } + + b2Vec2 normal = poly1->normals[i11]; + + // Reference edge vertices + b2Vec2 v11 = poly1->vertices[i11]; + b2Vec2 v12 = poly1->vertices[i12]; + + // Incident edge vertices + b2Vec2 v21 = poly2->vertices[i21]; + b2Vec2 v22 = poly2->vertices[i22]; + + b2Vec2 tangent = b2CrossSV( 1.0f, normal ); + + float lower1 = 0.0f; + float upper1 = b2Dot( b2Sub( v12, v11 ), tangent ); + + // Incident edge points opposite of tangent due to CCW winding + float upper2 = b2Dot( b2Sub( v21, v11 ), tangent ); + float lower2 = b2Dot( b2Sub( v22, v11 ), tangent ); + + // This check can fail slightly due to mismatch with GJK code. + // Perhaps fall back to a single point here? Otherwise we get two coincident points. + // if (upper2 < lower1 || upper1 < lower2) + //{ + // // numeric failure + // B2_ASSERT(false); + // return manifold; + //} + + b2Vec2 vLower; + if ( lower2 < lower1 && upper2 - lower2 > FLT_EPSILON ) + { + vLower = b2Lerp( v22, v21, ( lower1 - lower2 ) / ( upper2 - lower2 ) ); + } + else + { + vLower = v22; + } + + b2Vec2 vUpper; + if ( upper2 > upper1 && upper2 - lower2 > FLT_EPSILON ) + { + vUpper = b2Lerp( v22, v21, ( upper1 - lower2 ) / ( upper2 - lower2 ) ); + } + else + { + vUpper = v21; + } + + // todo vLower can be very close to vUpper, reduce to one point? + + float separationLower = b2Dot( b2Sub( vLower, v11 ), normal ); + float separationUpper = b2Dot( b2Sub( vUpper, v11 ), normal ); + + float r1 = poly1->radius; + float r2 = poly2->radius; + + // Put contact points at midpoint, accounting for radii + vLower = b2MulAdd( vLower, 0.5f * ( r1 - r2 - separationLower ), normal ); + vUpper = b2MulAdd( vUpper, 0.5f * ( r1 - r2 - separationUpper ), normal ); + + float radius = r1 + r2; + + if ( flip == false ) + { + manifold.normal = normal; + b2ManifoldPoint* cp = manifold.points + 0; + + { + cp->anchorA = vLower; + cp->separation = separationLower - radius; + cp->id = B2_MAKE_ID( i11, i22 ); + manifold.pointCount += 1; + cp += 1; + } + + { + cp->anchorA = vUpper; + cp->separation = separationUpper - radius; + cp->id = B2_MAKE_ID( i12, i21 ); + manifold.pointCount += 1; + } + } + else + { + manifold.normal = b2Neg( normal ); + b2ManifoldPoint* cp = manifold.points + 0; + + { + cp->anchorA = vUpper; + cp->separation = separationUpper - radius; + cp->id = B2_MAKE_ID( i21, i12 ); + manifold.pointCount += 1; + cp += 1; + } + + { + cp->anchorA = vLower; + cp->separation = separationLower - radius; + cp->id = B2_MAKE_ID( i22, i11 ); + manifold.pointCount += 1; + } + } + + return manifold; +} + +// Find the max separation between poly1 and poly2 using edge normals from poly1. +static float b2FindMaxSeparation( int32_t* edgeIndex, const b2Polygon* poly1, const b2Polygon* poly2 ) +{ + int32_t count1 = poly1->count; + int32_t count2 = poly2->count; + const b2Vec2* n1s = poly1->normals; + const b2Vec2* v1s = poly1->vertices; + const b2Vec2* v2s = poly2->vertices; + + int32_t bestIndex = 0; + float maxSeparation = -FLT_MAX; + for ( int32_t i = 0; i < count1; ++i ) + { + // Get poly1 normal in frame2. + b2Vec2 n = n1s[i]; + b2Vec2 v1 = v1s[i]; + + // Find the deepest point for normal i. + float si = FLT_MAX; + for ( int32_t j = 0; j < count2; ++j ) + { + float sij = b2Dot( n, b2Sub( v2s[j], v1 ) ); + if ( sij < si ) + { + si = sij; + } + } + + if ( si > maxSeparation ) + { + maxSeparation = si; + bestIndex = i; + } + } + + *edgeIndex = bestIndex; + return maxSeparation; +} + +// Due to speculation, every polygon is rounded +// Algorithm: +// +// compute edge separation using the separating axis test (SAT) +// if (separation > speculation_distance) +// return +// find reference and incident edge +// if separation >= 0.1f * b2_linearSlop +// compute closest points between reference and incident edge +// if vertices are closest +// single vertex-vertex contact +// else +// clip edges +// end +// else +// clip edges +// end + +b2Manifold b2CollidePolygons( const b2Polygon* polygonA, b2Transform xfA, const b2Polygon* polygonB, b2Transform xfB ) +{ + b2Vec2 origin = polygonA->vertices[0]; + + // Shift polyA to origin + // pw = q * pb + p + // pw = q * (pbs + origin) + p + // pw = q * pbs + (p + q * origin) + b2Transform sfA = { b2Add( xfA.p, b2RotateVector( xfA.q, origin ) ), xfA.q }; + b2Transform xf = b2InvMulTransforms( sfA, xfB ); + + b2Polygon localPolyA; + localPolyA.count = polygonA->count; + localPolyA.radius = polygonA->radius; + localPolyA.vertices[0] = b2Vec2_zero; + localPolyA.normals[0] = polygonA->normals[0]; + for ( int i = 1; i < localPolyA.count; ++i ) + { + localPolyA.vertices[i] = b2Sub( polygonA->vertices[i], origin ); + localPolyA.normals[i] = polygonA->normals[i]; + } + + // Put polyB in polyA's frame to reduce round-off error + b2Polygon localPolyB; + localPolyB.count = polygonB->count; + localPolyB.radius = polygonB->radius; + for ( int i = 0; i < localPolyB.count; ++i ) + { + localPolyB.vertices[i] = b2TransformPoint( xf, polygonB->vertices[i] ); + localPolyB.normals[i] = b2RotateVector( xf.q, polygonB->normals[i] ); + } + + int edgeA = 0; + float separationA = b2FindMaxSeparation( &edgeA, &localPolyA, &localPolyB ); + + int edgeB = 0; + float separationB = b2FindMaxSeparation( &edgeB, &localPolyB, &localPolyA ); + + float radius = localPolyA.radius + localPolyB.radius; + + if ( separationA > b2_speculativeDistance + radius || separationB > b2_speculativeDistance + radius ) + { + return ( b2Manifold ){ 0 }; + } + + // Find incident edge + bool flip; + if ( separationA >= separationB ) + { + flip = false; + + b2Vec2 searchDirection = localPolyA.normals[edgeA]; + + // Find the incident edge on polyB + int count = localPolyB.count; + const b2Vec2* normals = localPolyB.normals; + edgeB = 0; + float minDot = FLT_MAX; + for ( int i = 0; i < count; ++i ) + { + float dot = b2Dot( searchDirection, normals[i] ); + if ( dot < minDot ) + { + minDot = dot; + edgeB = i; + } + } + } + else + { + flip = true; + + b2Vec2 searchDirection = localPolyB.normals[edgeB]; + + // Find the incident edge on polyA + int count = localPolyA.count; + const b2Vec2* normals = localPolyA.normals; + edgeA = 0; + float minDot = FLT_MAX; + for ( int i = 0; i < count; ++i ) + { + float dot = b2Dot( searchDirection, normals[i] ); + if ( dot < minDot ) + { + minDot = dot; + edgeA = i; + } + } + } + + b2Manifold manifold = { 0 }; + + // Using slop here to ensure vertex-vertex normal vectors can be safely normalized + // todo this means edge clipping needs to handle slightly non-overlapping edges. + if ( separationA > 0.1f * b2_linearSlop || separationB > 0.1f * b2_linearSlop ) + { + // Polygons are disjoint. Find closest points between reference edge and incident edge + // Reference edge on polygon A + int i11 = edgeA; + int i12 = edgeA + 1 < localPolyA.count ? edgeA + 1 : 0; + int i21 = edgeB; + int i22 = edgeB + 1 < localPolyB.count ? edgeB + 1 : 0; + + b2Vec2 v11 = localPolyA.vertices[i11]; + b2Vec2 v12 = localPolyA.vertices[i12]; + b2Vec2 v21 = localPolyB.vertices[i21]; + b2Vec2 v22 = localPolyB.vertices[i22]; + + b2SegmentDistanceResult result = b2SegmentDistance( v11, v12, v21, v22 ); + + if ( result.fraction1 == 0.0f && result.fraction2 == 0.0f ) + { + // v11 - v21 + b2Vec2 normal = b2Sub( v21, v11 ); + B2_ASSERT( result.distanceSquared > 0.0f ); + float distance = sqrtf( result.distanceSquared ); + if ( distance > b2_speculativeDistance + radius ) + { + return manifold; + } + float invDistance = 1.0f / distance; + normal.x *= invDistance; + normal.y *= invDistance; + + b2Vec2 c1 = b2MulAdd( v11, localPolyA.radius, normal ); + b2Vec2 c2 = b2MulAdd( v21, -localPolyB.radius, normal ); + + manifold.normal = normal; + manifold.points[0].anchorA = b2Lerp( c1, c2, 0.5f ); + manifold.points[0].separation = distance - radius; + manifold.points[0].id = B2_MAKE_ID( i11, i21 ); + manifold.pointCount = 1; + } + else if ( result.fraction1 == 0.0f && result.fraction2 == 1.0f ) + { + // v11 - v22 + b2Vec2 normal = b2Sub( v22, v11 ); + B2_ASSERT( result.distanceSquared > 0.0f ); + float distance = sqrtf( result.distanceSquared ); + if ( distance > b2_speculativeDistance + radius ) + { + return manifold; + } + float invDistance = 1.0f / distance; + normal.x *= invDistance; + normal.y *= invDistance; + + b2Vec2 c1 = b2MulAdd( v11, localPolyA.radius, normal ); + b2Vec2 c2 = b2MulAdd( v22, -localPolyB.radius, normal ); + + manifold.normal = normal; + manifold.points[0].anchorA = b2Lerp( c1, c2, 0.5f ); + manifold.points[0].separation = distance - radius; + manifold.points[0].id = B2_MAKE_ID( i11, i22 ); + manifold.pointCount = 1; + } + else if ( result.fraction1 == 1.0f && result.fraction2 == 0.0f ) + { + // v12 - v21 + b2Vec2 normal = b2Sub( v21, v12 ); + B2_ASSERT( result.distanceSquared > 0.0f ); + float distance = sqrtf( result.distanceSquared ); + if ( distance > b2_speculativeDistance + radius ) + { + return manifold; + } + float invDistance = 1.0f / distance; + normal.x *= invDistance; + normal.y *= invDistance; + + b2Vec2 c1 = b2MulAdd( v12, localPolyA.radius, normal ); + b2Vec2 c2 = b2MulAdd( v21, -localPolyB.radius, normal ); + + manifold.normal = normal; + manifold.points[0].anchorA = b2Lerp( c1, c2, 0.5f ); + manifold.points[0].separation = distance - radius; + manifold.points[0].id = B2_MAKE_ID( i12, i21 ); + manifold.pointCount = 1; + } + else if ( result.fraction1 == 1.0f && result.fraction2 == 1.0f ) + { + // v12 - v22 + b2Vec2 normal = b2Sub( v22, v12 ); + B2_ASSERT( result.distanceSquared > 0.0f ); + float distance = sqrtf( result.distanceSquared ); + if ( distance > b2_speculativeDistance + radius ) + { + return manifold; + } + float invDistance = 1.0f / distance; + normal.x *= invDistance; + normal.y *= invDistance; + + b2Vec2 c1 = b2MulAdd( v12, localPolyA.radius, normal ); + b2Vec2 c2 = b2MulAdd( v22, -localPolyB.radius, normal ); + + manifold.normal = normal; + manifold.points[0].anchorA = b2Lerp( c1, c2, 0.5f ); + manifold.points[0].separation = distance - radius; + manifold.points[0].id = B2_MAKE_ID( i12, i22 ); + manifold.pointCount = 1; + } + else + { + // Edge region + manifold = b2ClipPolygons( &localPolyA, &localPolyB, edgeA, edgeB, flip ); + } + } + else + { + // Polygons overlap + manifold = b2ClipPolygons( &localPolyA, &localPolyB, edgeA, edgeB, flip ); + } + + // Convert manifold to world space + if ( manifold.pointCount > 0 ) + { + manifold.normal = b2RotateVector( xfA.q, manifold.normal ); + for ( int i = 0; i < manifold.pointCount; ++i ) + { + b2ManifoldPoint* mp = manifold.points + i; + + // anchor points relative to shape origin in world space + mp->anchorA = b2RotateVector( xfA.q, b2Add( mp->anchorA, origin ) ); + mp->anchorB = b2Add( mp->anchorA, b2Sub( xfA.p, xfB.p ) ); + mp->point = b2Add( xfA.p, mp->anchorA ); + } + } + + return manifold; +} + +b2Manifold b2CollideSegmentAndCircle( const b2Segment* segmentA, b2Transform xfA, const b2Circle* circleB, b2Transform xfB ) +{ + b2Capsule capsuleA = { segmentA->point1, segmentA->point2, 0.0f }; + return b2CollideCapsuleAndCircle( &capsuleA, xfA, circleB, xfB ); +} + +b2Manifold b2CollideSegmentAndPolygon( const b2Segment* segmentA, b2Transform xfA, const b2Polygon* polygonB, b2Transform xfB ) +{ + b2Polygon polygonA = b2MakeCapsule( segmentA->point1, segmentA->point2, 0.0f ); + return b2CollidePolygons( &polygonA, xfA, polygonB, xfB ); +} + +b2Manifold b2CollideChainSegmentAndCircle( const b2ChainSegment* segmentA, b2Transform xfA, const b2Circle* circleB, + b2Transform xfB ) +{ + b2Manifold manifold = { 0 }; + + b2Transform xf = b2InvMulTransforms( xfA, xfB ); + + // Compute circle in frame of segment + b2Vec2 pB = b2TransformPoint( xf, circleB->center ); + + b2Vec2 p1 = segmentA->segment.point1; + b2Vec2 p2 = segmentA->segment.point2; + b2Vec2 e = b2Sub( p2, p1 ); + + // Normal points to the right + float offset = b2Dot( b2RightPerp( e ), b2Sub( pB, p1 ) ); + if ( offset < 0.0f ) + { + // collision is one-sided + return manifold; + } + + // Barycentric coordinates + float u = b2Dot( e, b2Sub( p2, pB ) ); + float v = b2Dot( e, b2Sub( pB, p1 ) ); + + b2Vec2 pA; + + if ( v <= 0.0f ) + { + // Behind point1? + // Is pB in the Voronoi region of the previous edge? + b2Vec2 prevEdge = b2Sub( p1, segmentA->ghost1 ); + float uPrev = b2Dot( prevEdge, b2Sub( pB, p1 ) ); + if ( uPrev <= 0.0f ) + { + return manifold; + } + + pA = p1; + } + else if ( u <= 0.0f ) + { + // Ahead of point2? + b2Vec2 nextEdge = b2Sub( segmentA->ghost2, p2 ); + float vNext = b2Dot( nextEdge, b2Sub( pB, p2 ) ); + + // Is pB in the Voronoi region of the next edge? + if ( vNext > 0.0f ) + { + return manifold; + } + + pA = p2; + } + else + { + float ee = b2Dot( e, e ); + pA = ( b2Vec2 ){ u * p1.x + v * p2.x, u * p1.y + v * p2.y }; + pA = ee > 0.0f ? b2MulSV( 1.0f / ee, pA ) : p1; + } + + float distance; + b2Vec2 normal = b2GetLengthAndNormalize( &distance, b2Sub( pB, pA ) ); + + float radius = circleB->radius; + float separation = distance - radius; + if ( separation > b2_speculativeDistance ) + { + return manifold; + } + + b2Vec2 cA = pA; + b2Vec2 cB = b2MulAdd( pB, -radius, normal ); + b2Vec2 contactPointA = b2Lerp( cA, cB, 0.5f ); + + manifold.normal = b2RotateVector( xfA.q, normal ); + + b2ManifoldPoint* mp = manifold.points + 0; + mp->anchorA = b2RotateVector( xfA.q, contactPointA ); + mp->anchorB = b2Add( mp->anchorA, b2Sub( xfA.p, xfB.p ) ); + mp->point = b2Add( xfA.p, mp->anchorA ); + mp->separation = separation; + mp->id = 0; + manifold.pointCount = 1; + return manifold; +} + +b2Manifold b2CollideChainSegmentAndCapsule( const b2ChainSegment* segmentA, b2Transform xfA, const b2Capsule* capsuleB, + b2Transform xfB, b2DistanceCache* cache ) +{ + b2Polygon polyB = b2MakeCapsule( capsuleB->center1, capsuleB->center2, capsuleB->radius ); + return b2CollideChainSegmentAndPolygon( segmentA, xfA, &polyB, xfB, cache ); +} + +static b2Manifold b2ClipSegments( b2Vec2 a1, b2Vec2 a2, b2Vec2 b1, b2Vec2 b2, b2Vec2 normal, float ra, float rb, uint16_t id1, + uint16_t id2 ) +{ + b2Manifold manifold = { 0 }; + + b2Vec2 tangent = b2LeftPerp( normal ); + + // Barycentric coordinates of each point relative to a1 along tangent + float lower1 = 0.0f; + float upper1 = b2Dot( b2Sub( a2, a1 ), tangent ); + + // Incident edge points opposite of tangent due to CCW winding + float upper2 = b2Dot( b2Sub( b1, a1 ), tangent ); + float lower2 = b2Dot( b2Sub( b2, a1 ), tangent ); + + // Do segments overlap? + if ( upper2 < lower1 || upper1 < lower2 ) + { + return manifold; + } + + b2Vec2 vLower; + if ( lower2 < lower1 && upper2 - lower2 > FLT_EPSILON ) + { + vLower = b2Lerp( b2, b1, ( lower1 - lower2 ) / ( upper2 - lower2 ) ); + } + else + { + vLower = b2; + } + + b2Vec2 vUpper; + if ( upper2 > upper1 && upper2 - lower2 > FLT_EPSILON ) + { + vUpper = b2Lerp( b2, b1, ( upper1 - lower2 ) / ( upper2 - lower2 ) ); + } + else + { + vUpper = b1; + } + + // todo vLower can be very close to vUpper, reduce to one point? + + float separationLower = b2Dot( b2Sub( vLower, a1 ), normal ); + float separationUpper = b2Dot( b2Sub( vUpper, a1 ), normal ); + + // Put contact points at midpoint, accounting for radii + vLower = b2MulAdd( vLower, 0.5f * ( ra - rb - separationLower ), normal ); + vUpper = b2MulAdd( vUpper, 0.5f * ( ra - rb - separationUpper ), normal ); + + float radius = ra + rb; + + manifold.normal = normal; + { + b2ManifoldPoint* cp = manifold.points + 0; + cp->anchorA = vLower; + cp->separation = separationLower - radius; + cp->id = id1; + } + + { + b2ManifoldPoint* cp = manifold.points + 1; + cp->anchorA = vUpper; + cp->separation = separationUpper - radius; + cp->id = id2; + } + + manifold.pointCount = 2; + + return manifold; +} + +enum b2NormalType +{ + // This means the normal points in a direction that is non-smooth relative to a convex vertex and should be skipped + b2_normalSkip, + + // This means the normal points in a direction that is smooth relative to a convex vertex and should be used for collision + b2_normalAdmit, + + // This means the normal is in a region of a concave vertex and should be snapped to the segment normal + b2_normalSnap +}; + +struct b2ChainSegmentParams +{ + b2Vec2 edge1; + b2Vec2 normal0; + b2Vec2 normal2; + bool convex1; + bool convex2; +}; + +// Evaluate Gauss map +// See https://box2d.org/posts/2020/06/ghost-collisions/ +static enum b2NormalType b2ClassifyNormal( struct b2ChainSegmentParams params, b2Vec2 normal ) +{ + const float sinTol = 0.01f; + + if ( b2Dot( normal, params.edge1 ) <= 0.0f ) + { + // Normal points towards the segment tail + if ( params.convex1 ) + { + if ( b2Cross( normal, params.normal0 ) > sinTol ) + { + return b2_normalSkip; + } + + return b2_normalAdmit; + } + else + { + return b2_normalSnap; + } + } + else + { + // Normal points towards segment head + if ( params.convex2 ) + { + if ( b2Cross( params.normal2, normal ) > sinTol ) + { + return b2_normalSkip; + } + + return b2_normalAdmit; + } + else + { + return b2_normalSnap; + } + } +} + +b2Manifold b2CollideChainSegmentAndPolygon( const b2ChainSegment* segmentA, b2Transform xfA, const b2Polygon* polygonB, + b2Transform xfB, b2DistanceCache* cache ) +{ + b2Manifold manifold = { 0 }; + + b2Transform xf = b2InvMulTransforms( xfA, xfB ); + + b2Vec2 centroidB = b2TransformPoint( xf, polygonB->centroid ); + float radiusB = polygonB->radius; + + b2Vec2 p1 = segmentA->segment.point1; + b2Vec2 p2 = segmentA->segment.point2; + + b2Vec2 edge1 = b2Normalize( b2Sub( p2, p1 ) ); + + struct b2ChainSegmentParams smoothParams = { 0 }; + smoothParams.edge1 = edge1; + + const float convexTol = 0.01f; + b2Vec2 edge0 = b2Normalize( b2Sub( p1, segmentA->ghost1 ) ); + smoothParams.normal0 = b2RightPerp( edge0 ); + smoothParams.convex1 = b2Cross( edge0, edge1 ) >= convexTol; + + b2Vec2 edge2 = b2Normalize( b2Sub( segmentA->ghost2, p2 ) ); + smoothParams.normal2 = b2RightPerp( edge2 ); + smoothParams.convex2 = b2Cross( edge1, edge2 ) >= convexTol; + + // Normal points to the right + b2Vec2 normal1 = b2RightPerp( edge1 ); + bool behind1 = b2Dot( normal1, b2Sub( centroidB, p1 ) ) < 0.0f; + bool behind0 = true; + bool behind2 = true; + if ( smoothParams.convex1 ) + { + behind0 = b2Dot( smoothParams.normal0, b2Sub( centroidB, p1 ) ) < 0.0f; + } + + if ( smoothParams.convex2 ) + { + behind2 = b2Dot( smoothParams.normal2, b2Sub( centroidB, p2 ) ) < 0.0f; + } + + if ( behind1 && behind0 && behind2 ) + { + // one-sided collision + return manifold; + } + + // Get polygonB in frameA + int32_t count = polygonB->count; + b2Vec2 vertices[b2_maxPolygonVertices]; + b2Vec2 normals[b2_maxPolygonVertices]; + for ( int32_t i = 0; i < count; ++i ) + { + vertices[i] = b2TransformPoint( xf, polygonB->vertices[i] ); + normals[i] = b2RotateVector( xf.q, polygonB->normals[i] ); + } + + // Distance doesn't work correctly with partial polygons + b2DistanceInput input; + input.proxyA = b2MakeProxy( &segmentA->segment.point1, 2, 0.0f ); + input.proxyB = b2MakeProxy( vertices, count, 0.0f ); + input.transformA = b2Transform_identity; + input.transformB = b2Transform_identity; + input.useRadii = false; + + b2DistanceOutput output = b2ShapeDistance( cache, &input, NULL, 0 ); + + if ( output.distance > radiusB + b2_speculativeDistance ) + { + return manifold; + } + + // Snap concave normals for partial polygon + b2Vec2 n0 = smoothParams.convex1 ? smoothParams.normal0 : normal1; + b2Vec2 n2 = smoothParams.convex2 ? smoothParams.normal2 : normal1; + + // Index of incident vertex on polygon + int32_t incidentIndex = -1; + int32_t incidentNormal = -1; + + if ( behind1 == false && output.distance > 0.1f * b2_linearSlop ) + { + // The closest features may be two vertices or an edge and a vertex even when there should + // be face contact + + if ( cache->count == 1 ) + { + // vertex-vertex collision + b2Vec2 pA = output.pointA; + b2Vec2 pB = output.pointB; + + b2Vec2 normal = b2Normalize( b2Sub( pB, pA ) ); + + enum b2NormalType type = b2ClassifyNormal( smoothParams, normal ); + if ( type == b2_normalSkip ) + { + return manifold; + } + + if ( type == b2_normalAdmit ) + { + manifold.normal = b2RotateVector( xfA.q, normal ); + b2ManifoldPoint* cp = manifold.points + 0; + cp->anchorA = b2RotateVector( xfA.q, pA ); + cp->anchorB = b2Add( cp->anchorA, b2Sub( xfA.p, xfB.p ) ); + cp->point = b2Add( xfA.p, cp->anchorA ); + cp->separation = output.distance - radiusB; + cp->id = B2_MAKE_ID( cache->indexA[0], cache->indexB[0] ); + manifold.pointCount = 1; + return manifold; + } + + // fall through b2_normalSnap + incidentIndex = cache->indexB[0]; + } + else + { + // vertex-edge collision + B2_ASSERT( cache->count == 2 ); + + int32_t ia1 = cache->indexA[0]; + int32_t ia2 = cache->indexA[1]; + int32_t ib1 = cache->indexB[0]; + int32_t ib2 = cache->indexB[1]; + + if ( ia1 == ia2 ) + { + // 1 point on A, expect 2 points on B + B2_ASSERT( ib1 != ib2 ); + + // Find polygon normal most aligned with vector between closest points. + // This effectively sorts ib1 and ib2 + b2Vec2 normalB = b2Sub( output.pointA, output.pointB ); + float dot1 = b2Dot( normalB, normals[ib1] ); + float dot2 = b2Dot( normalB, normals[ib2] ); + int32_t ib = dot1 > dot2 ? ib1 : ib2; + + // Use accurate normal + normalB = normals[ib]; + + enum b2NormalType type = b2ClassifyNormal( smoothParams, b2Neg( normalB ) ); + if ( type == b2_normalSkip ) + { + return manifold; + } + + if ( type == b2_normalAdmit ) + { + // Get polygon edge associated with normal + ib1 = ib; + ib2 = ib < count - 1 ? ib + 1 : 0; + + b2Vec2 b1 = vertices[ib1]; + b2Vec2 b2 = vertices[ib2]; + + // Find incident segment vertex + dot1 = b2Dot( normalB, b2Sub( p1, b1 ) ); + dot2 = b2Dot( normalB, b2Sub( p2, b1 ) ); + + if ( dot1 < dot2 ) + { + if ( b2Dot( n0, normalB ) < b2Dot( normal1, normalB ) ) + { + // Neighbor is incident + return manifold; + } + } + else + { + if ( b2Dot( n2, normalB ) < b2Dot( normal1, normalB ) ) + { + // Neighbor is incident + return manifold; + } + } + + manifold = + b2ClipSegments( b1, b2, p1, p2, normalB, radiusB, 0.0f, B2_MAKE_ID( ib1, 1 ), B2_MAKE_ID( ib2, 0 ) ); + manifold.normal = b2RotateVector( xfA.q, b2Neg( normalB ) ); + manifold.points[0].anchorA = b2RotateVector( xfA.q, manifold.points[0].anchorA ); + manifold.points[1].anchorA = b2RotateVector( xfA.q, manifold.points[1].anchorA ); + b2Vec2 pAB = b2Sub( xfA.p, xfB.p ); + manifold.points[0].anchorB = b2Add( manifold.points[0].anchorA, pAB ); + manifold.points[1].anchorB = b2Add( manifold.points[1].anchorA, pAB ); + manifold.points[0].point = b2Add( xfA.p, manifold.points[0].anchorA ); + manifold.points[1].point = b2Add( xfA.p, manifold.points[1].anchorA ); + return manifold; + } + + // fall through b2_normalSnap + incidentNormal = ib; + } + else + { + // Get index of incident polygonB vertex + float dot1 = b2Dot( normal1, b2Sub( vertices[ib1], p1 ) ); + float dot2 = b2Dot( normal1, b2Sub( vertices[ib2], p2 ) ); + incidentIndex = dot1 < dot2 ? ib1 : ib2; + } + } + } + else + { + // SAT edge normal + float edgeSeparation = FLT_MAX; + + for ( int32_t i = 0; i < count; ++i ) + { + float s = b2Dot( normal1, b2Sub( vertices[i], p1 ) ); + if ( s < edgeSeparation ) + { + edgeSeparation = s; + incidentIndex = i; + } + } + + // Check convex neighbor for edge separation + if ( smoothParams.convex1 ) + { + float s0 = FLT_MAX; + + for ( int32_t i = 0; i < count; ++i ) + { + float s = b2Dot( smoothParams.normal0, b2Sub( vertices[i], p1 ) ); + if ( s < s0 ) + { + s0 = s; + } + } + + if ( s0 > edgeSeparation ) + { + edgeSeparation = s0; + + // Indicate neighbor owns edge separation + incidentIndex = -1; + } + } + + // Check convex neighbor for edge separation + if ( smoothParams.convex2 ) + { + float s2 = FLT_MAX; + + for ( int32_t i = 0; i < count; ++i ) + { + float s = b2Dot( smoothParams.normal2, b2Sub( vertices[i], p2 ) ); + if ( s < s2 ) + { + s2 = s; + } + } + + if ( s2 > edgeSeparation ) + { + edgeSeparation = s2; + + // Indicate neighbor owns edge separation + incidentIndex = -1; + } + } + + // SAT polygon normals + float polygonSeparation = -FLT_MAX; + int32_t referenceIndex = -1; + + for ( int32_t i = 0; i < count; ++i ) + { + b2Vec2 n = normals[i]; + + enum b2NormalType type = b2ClassifyNormal( smoothParams, b2Neg( n ) ); + if ( type != b2_normalAdmit ) + { + continue; + } + + // Check the infinite sides of the partial polygon + // if ((smoothParams.convex1 && b2Cross(n0, n) > 0.0f) || (smoothParams.convex2 && b2Cross(n, n2) > 0.0f)) + //{ + // continue; + //} + + b2Vec2 p = vertices[i]; + float s = b2MinFloat( b2Dot( n, b2Sub( p2, p ) ), b2Dot( n, b2Sub( p1, p ) ) ); + + if ( s > polygonSeparation ) + { + polygonSeparation = s; + referenceIndex = i; + } + } + + if ( polygonSeparation > edgeSeparation ) + { + int32_t ia1 = referenceIndex; + int32_t ia2 = ia1 < count - 1 ? ia1 + 1 : 0; + b2Vec2 a1 = vertices[ia1]; + b2Vec2 a2 = vertices[ia2]; + + b2Vec2 n = normals[ia1]; + + float dot1 = b2Dot( n, b2Sub( p1, a1 ) ); + float dot2 = b2Dot( n, b2Sub( p2, a1 ) ); + + if ( dot1 < dot2 ) + { + if ( b2Dot( n0, n ) < b2Dot( normal1, n ) ) + { + // Neighbor is incident + return manifold; + } + } + else + { + if ( b2Dot( n2, n ) < b2Dot( normal1, n ) ) + { + // Neighbor is incident + return manifold; + } + } + + manifold = b2ClipSegments( a1, a2, p1, p2, normals[ia1], radiusB, 0.0f, B2_MAKE_ID( ia1, 1 ), B2_MAKE_ID( ia2, 0 ) ); + manifold.normal = b2RotateVector( xfA.q, b2Neg( normals[ia1] ) ); + manifold.points[0].anchorA = b2RotateVector( xfA.q, manifold.points[0].anchorA ); + manifold.points[1].anchorA = b2RotateVector( xfA.q, manifold.points[1].anchorA ); + b2Vec2 pAB = b2Sub( xfA.p, xfB.p ); + manifold.points[0].anchorB = b2Add( manifold.points[0].anchorA, pAB ); + manifold.points[1].anchorB = b2Add( manifold.points[1].anchorA, pAB ); + manifold.points[0].point = b2Add( xfA.p, manifold.points[0].anchorA ); + manifold.points[1].point = b2Add( xfA.p, manifold.points[1].anchorA ); + return manifold; + } + + if ( incidentIndex == -1 ) + { + // neighboring segment is the separating axis + return manifold; + } + + // fall through segment normal axis + } + + B2_ASSERT( incidentNormal != -1 || incidentIndex != -1 ); + + // Segment normal + + // Find incident polygon normal: normal adjacent to deepest vertex that is most anti-parallel to segment normal + b2Vec2 b1, b2; + int32_t ib1, ib2; + + if ( incidentNormal != -1 ) + { + ib1 = incidentNormal; + ib2 = ib1 < count - 1 ? ib1 + 1 : 0; + b1 = vertices[ib1]; + b2 = vertices[ib2]; + } + else + { + int32_t i2 = incidentIndex; + int32_t i1 = i2 > 0 ? i2 - 1 : count - 1; + float d1 = b2Dot( normal1, normals[i1] ); + float d2 = b2Dot( normal1, normals[i2] ); + if ( d1 < d2 ) + { + ib1 = i1, ib2 = i2; + b1 = vertices[ib1]; + b2 = vertices[ib2]; + } + else + { + ib1 = i2, ib2 = i2 < count - 1 ? i2 + 1 : 0; + b1 = vertices[ib1]; + b2 = vertices[ib2]; + } + } + + manifold = b2ClipSegments( p1, p2, b1, b2, normal1, 0.0f, radiusB, B2_MAKE_ID( 0, ib2 ), B2_MAKE_ID( 1, ib1 ) ); + manifold.normal = b2RotateVector( xfA.q, manifold.normal ); + manifold.points[0].anchorA = b2RotateVector( xfA.q, manifold.points[0].anchorA ); + manifold.points[1].anchorA = b2RotateVector( xfA.q, manifold.points[1].anchorA ); + b2Vec2 pAB = b2Sub( xfA.p, xfB.p ); + manifold.points[0].anchorB = b2Add( manifold.points[0].anchorA, pAB ); + manifold.points[1].anchorB = b2Add( manifold.points[1].anchorA, pAB ); + manifold.points[0].point = b2Add( xfA.p, manifold.points[0].anchorA ); + manifold.points[1].point = b2Add( xfA.p, manifold.points[1].anchorA ); + + return manifold; +} diff --git a/3rdparty/box2d/src/math_functions.c b/3rdparty/box2d/src/math_functions.c new file mode 100644 index 000000000000..039ace249ba0 --- /dev/null +++ b/3rdparty/box2d/src/math_functions.c @@ -0,0 +1,147 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#include "box2d/math_functions.h" + +#include "core.h" + +#include + +bool b2IsValid( float a ) +{ + if ( isnan( a ) ) + { + return false; + } + + if ( isinf( a ) ) + { + return false; + } + + return true; +} + +bool b2Vec2_IsValid( b2Vec2 v ) +{ + if ( isnan( v.x ) || isnan( v.y ) ) + { + return false; + } + + if ( isinf( v.x ) || isinf( v.y ) ) + { + return false; + } + + return true; +} + +bool b2Rot_IsValid( b2Rot q ) +{ + if ( isnan( q.s ) || isnan( q.c ) ) + { + return false; + } + + if ( isinf( q.s ) || isinf( q.c ) ) + { + return false; + } + + return b2IsNormalized( q ); +} + +// https://mazzo.li/posts/vectorized-atan2.html +static inline float b2Atan( float x ) +{ + float a1 = 0.99997726f; + float a3 = -0.33262347f; + float a5 = 0.19354346f; + float a7 = -0.11643287f; + float a9 = 0.05265332f; + float a11 = -0.01172120f; + + float x2 = x * x; + return x * ( a1 + x2 * ( a3 + x2 * ( a5 + x2 * ( a7 + x2 * ( a9 + x2 * a11 ) ) ) ) ); +} + +// I tested atan2f and got different results on Apple Clang (Arm) than MSVC (x64). +float b2Atan2( float y, float x ) +{ + float pi = b2_pi; + float halfPi = 0.5f * b2_pi; + + bool swap = b2AbsFloat( x ) < b2AbsFloat( y ); + float atanInput = ( swap ? x : y ) / ( swap ? y : x ); + + // Approximate atan + float res = b2Atan( atanInput ); + + // If swapped, adjust atan output + res = swap ? ( atanInput >= 0.0f ? halfPi : -halfPi ) - res : res; + // Adjust quadrants + if ( x >= 0.0f && y >= 0.0f ) + { + } // 1st quadrant + else if ( x < 0.0f && y >= 0.0f ) + { + res = pi + res; + } // 2nd quadrant + else if ( x < 0.0f && y < 0.0f ) + { + res = -pi + res; + } // 3rd quadrant + else if ( x >= 0.0f && y < 0.0f ) + { + } // 4th quadrant + + return res; +} + +// Approximate cosine and sine for determinism. In my testing cosf and sinf produced +// the same results on x64 and ARM using MSVC, GCC, and Clang. However, I don't trust +// this result. +// https://en.wikipedia.org/wiki/Bh%C4%81skara_I%27s_sine_approximation_formula +b2CosSin b2ComputeCosSin( float angle ) +{ + // return ( b2CosSin ){ cosf( angle ), sinf( angle ) }; + + float x = b2UnwindLargeAngle( angle ); + float pi2 = b2_pi * b2_pi; + + b2Rot q; + + // cosine needs angle in [-pi/2, pi/2] + if (x < -0.5f * b2_pi) + { + float y = x + b2_pi; + float y2 = y * y; + q.c = -( pi2 - 4.0f * y2 ) / ( pi2 + y2 ); + } + else if (x > 0.5f * b2_pi) + { + float y = x - b2_pi; + float y2 = y * y; + q.c = -( pi2 - 4.0f * y2 ) / ( pi2 + y2 ); + } + else + { + float y2 = x * x; + q.c = ( pi2 - 4.0f * y2 ) / ( pi2 + y2 ); + } + + // sine needs angle in [0, pi] + if (x < 0.0f) + { + float y = x + b2_pi; + q.s = -16.0f * y * ( b2_pi - y ) / ( 5.0f * pi2 - 4.0f * y * ( b2_pi - y ) ); + } + else + { + q.s = 16.0f * x * ( b2_pi - x ) / ( 5.0f * pi2 - 4.0f * x * ( b2_pi - x ) ); + } + + q = b2NormalizeRot( q ); + return ( b2CosSin ){ q.c, q.s }; +} diff --git a/3rdparty/box2d/src/motor_joint.c b/3rdparty/box2d/src/motor_joint.c new file mode 100644 index 000000000000..b41f685a80a8 --- /dev/null +++ b/3rdparty/box2d/src/motor_joint.c @@ -0,0 +1,282 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#include "body.h" +#include "core.h" +#include "joint.h" +#include "solver.h" +#include "solver_set.h" +#include "world.h" + +// needed for dll export +#include "box2d/box2d.h" + +void b2MotorJoint_SetLinearOffset( b2JointId jointId, b2Vec2 linearOffset ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_motorJoint ); + joint->motorJoint.linearOffset = linearOffset; +} + +b2Vec2 b2MotorJoint_GetLinearOffset( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_motorJoint ); + return joint->motorJoint.linearOffset; +} + +void b2MotorJoint_SetAngularOffset( b2JointId jointId, float angularOffset ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_motorJoint ); + joint->motorJoint.angularOffset = b2ClampFloat( angularOffset, -b2_pi, b2_pi ); +} + +float b2MotorJoint_GetAngularOffset( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_motorJoint ); + return joint->motorJoint.angularOffset; +} + +void b2MotorJoint_SetMaxForce( b2JointId jointId, float maxForce ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_motorJoint ); + joint->motorJoint.maxForce = b2MaxFloat( 0.0f, maxForce ); +} + +float b2MotorJoint_GetMaxForce( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_motorJoint ); + return joint->motorJoint.maxForce; +} + +void b2MotorJoint_SetMaxTorque( b2JointId jointId, float maxTorque ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_motorJoint ); + joint->motorJoint.maxTorque = b2MaxFloat( 0.0f, maxTorque ); +} + +float b2MotorJoint_GetMaxTorque( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_motorJoint ); + return joint->motorJoint.maxTorque; +} + +void b2MotorJoint_SetCorrectionFactor( b2JointId jointId, float correctionFactor ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_motorJoint ); + joint->motorJoint.correctionFactor = b2ClampFloat( correctionFactor, 0.0f, 1.0f ); +} + +float b2MotorJoint_GetCorrectionFactor( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_motorJoint ); + return joint->motorJoint.correctionFactor; +} + +b2Vec2 b2GetMotorJointForce( b2World* world, b2JointSim* base ) +{ + b2Vec2 force = b2MulSV( world->inv_h, base->motorJoint.linearImpulse ); + return force; +} + +float b2GetMotorJointTorque( b2World* world, b2JointSim* base ) +{ + return world->inv_h * base->motorJoint.angularImpulse; +} + +// Point-to-point constraint +// C = p2 - p1 +// Cdot = v2 - v1 +// = v2 + cross(w2, r2) - v1 - cross(w1, r1) +// J = [-I -r1_skew I r2_skew ] +// Identity used: +// w k % (rx i + ry j) = w * (-ry i + rx j) + +// Angle constraint +// C = angle2 - angle1 - referenceAngle +// Cdot = w2 - w1 +// J = [0 0 -1 0 0 1] +// K = invI1 + invI2 + +void b2PrepareMotorJoint( b2JointSim* base, b2StepContext* context ) +{ + B2_ASSERT( base->type == b2_motorJoint ); + + // chase body id to the solver set where the body lives + int idA = base->bodyIdA; + int idB = base->bodyIdB; + + b2World* world = context->world; + + b2Body* bodyA = b2BodyArray_Get( &world->bodies, idA ); + b2Body* bodyB = b2BodyArray_Get( &world->bodies, idB ); + + B2_ASSERT( bodyA->setIndex == b2_awakeSet || bodyB->setIndex == b2_awakeSet ); + + b2SolverSet* setA = b2SolverSetArray_Get( &world->solverSets, bodyA->setIndex ); + b2SolverSet* setB = b2SolverSetArray_Get( &world->solverSets, bodyB->setIndex ); + + int localIndexA = bodyA->localIndex; + int localIndexB = bodyB->localIndex; + + b2BodySim* bodySimA = b2BodySimArray_Get( &setA->bodySims, localIndexA ); + b2BodySim* bodySimB = b2BodySimArray_Get( &setB->bodySims, localIndexB ); + + float mA = bodySimA->invMass; + float iA = bodySimA->invInertia; + float mB = bodySimB->invMass; + float iB = bodySimB->invInertia; + + base->invMassA = mA; + base->invMassB = mB; + base->invIA = iA; + base->invIB = iB; + + b2MotorJoint* joint = &base->motorJoint; + joint->indexA = bodyA->setIndex == b2_awakeSet ? localIndexA : B2_NULL_INDEX; + joint->indexB = bodyB->setIndex == b2_awakeSet ? localIndexB : B2_NULL_INDEX; + + joint->anchorA = b2RotateVector( bodySimA->transform.q, b2Sub( base->localOriginAnchorA, bodySimA->localCenter ) ); + joint->anchorB = b2RotateVector( bodySimB->transform.q, b2Sub( base->localOriginAnchorB, bodySimB->localCenter ) ); + joint->deltaCenter = b2Sub( b2Sub( bodySimB->center, bodySimA->center ), joint->linearOffset ); + joint->deltaAngle = b2RelativeAngle( bodySimB->transform.q, bodySimA->transform.q ) - joint->angularOffset; + joint->deltaAngle = b2UnwindAngle( joint->deltaAngle ); + + b2Vec2 rA = joint->anchorA; + b2Vec2 rB = joint->anchorB; + + b2Mat22 K; + K.cx.x = mA + mB + rA.y * rA.y * iA + rB.y * rB.y * iB; + K.cx.y = -rA.y * rA.x * iA - rB.y * rB.x * iB; + K.cy.x = K.cx.y; + K.cy.y = mA + mB + rA.x * rA.x * iA + rB.x * rB.x * iB; + joint->linearMass = b2GetInverse22( K ); + + float ka = iA + iB; + joint->angularMass = ka > 0.0f ? 1.0f / ka : 0.0f; + + if ( context->enableWarmStarting == false ) + { + joint->linearImpulse = b2Vec2_zero; + joint->angularImpulse = 0.0f; + } +} + +void b2WarmStartMotorJoint( b2JointSim* base, b2StepContext* context ) +{ + float mA = base->invMassA; + float mB = base->invMassB; + float iA = base->invIA; + float iB = base->invIB; + + b2MotorJoint* joint = &base->motorJoint; + + // dummy state for static bodies + b2BodyState dummyState = b2_identityBodyState; + + b2BodyState* bodyA = joint->indexA == B2_NULL_INDEX ? &dummyState : context->states + joint->indexA; + b2BodyState* bodyB = joint->indexB == B2_NULL_INDEX ? &dummyState : context->states + joint->indexB; + + b2Vec2 rA = b2RotateVector( bodyA->deltaRotation, joint->anchorA ); + b2Vec2 rB = b2RotateVector( bodyB->deltaRotation, joint->anchorB ); + + bodyA->linearVelocity = b2MulSub( bodyA->linearVelocity, mA, joint->linearImpulse ); + bodyA->angularVelocity -= iA * ( b2Cross( rA, joint->linearImpulse ) + joint->angularImpulse ); + bodyB->linearVelocity = b2MulAdd( bodyB->linearVelocity, mB, joint->linearImpulse ); + bodyB->angularVelocity += iB * ( b2Cross( rB, joint->linearImpulse ) + joint->angularImpulse ); +} + +void b2SolveMotorJoint( b2JointSim* base, b2StepContext* context, bool useBias ) +{ + B2_ASSERT( base->type == b2_motorJoint ); + + float mA = base->invMassA; + float mB = base->invMassB; + float iA = base->invIA; + float iB = base->invIB; + + // dummy state for static bodies + b2BodyState dummyState = b2_identityBodyState; + + b2MotorJoint* joint = &base->motorJoint; + b2BodyState* bodyA = joint->indexA == B2_NULL_INDEX ? &dummyState : context->states + joint->indexA; + b2BodyState* bodyB = joint->indexB == B2_NULL_INDEX ? &dummyState : context->states + joint->indexB; + + b2Vec2 vA = bodyA->linearVelocity; + float wA = bodyA->angularVelocity; + b2Vec2 vB = bodyB->linearVelocity; + float wB = bodyB->angularVelocity; + + // angular constraint + { + float angularSeperation = b2RelativeAngle( bodyB->deltaRotation, bodyA->deltaRotation ) + joint->deltaAngle; + angularSeperation = b2UnwindAngle( angularSeperation ); + + float angularBias = context->inv_h * joint->correctionFactor * angularSeperation; + + float Cdot = wB - wA; + float impulse = -joint->angularMass * ( Cdot + angularBias ); + + float oldImpulse = joint->angularImpulse; + float maxImpulse = context->h * joint->maxTorque; + joint->angularImpulse = b2ClampFloat( joint->angularImpulse + impulse, -maxImpulse, maxImpulse ); + impulse = joint->angularImpulse - oldImpulse; + + wA -= iA * impulse; + wB += iB * impulse; + } + + // linear constraint + { + b2Vec2 rA = b2RotateVector( bodyA->deltaRotation, joint->anchorA ); + b2Vec2 rB = b2RotateVector( bodyB->deltaRotation, joint->anchorB ); + + b2Vec2 ds = b2Add( b2Sub( bodyB->deltaPosition, bodyA->deltaPosition ), b2Sub( rB, rA ) ); + b2Vec2 linearSeparation = b2Add( joint->deltaCenter, ds ); + b2Vec2 linearBias = b2MulSV( context->inv_h * joint->correctionFactor, linearSeparation ); + + b2Vec2 Cdot = b2Sub( b2Add( vB, b2CrossSV( wB, rB ) ), b2Add( vA, b2CrossSV( wA, rA ) ) ); + b2Vec2 b = b2MulMV( joint->linearMass, b2Add( Cdot, linearBias ) ); + b2Vec2 impulse = { -b.x, -b.y }; + + b2Vec2 oldImpulse = joint->linearImpulse; + float maxImpulse = context->h * joint->maxForce; + joint->linearImpulse = b2Add( joint->linearImpulse, impulse ); + + if ( b2LengthSquared( joint->linearImpulse ) > maxImpulse * maxImpulse ) + { + joint->linearImpulse = b2Normalize( joint->linearImpulse ); + joint->linearImpulse.x *= maxImpulse; + joint->linearImpulse.y *= maxImpulse; + } + + impulse = b2Sub( joint->linearImpulse, oldImpulse ); + + vA = b2MulSub( vA, mA, impulse ); + wA -= iA * b2Cross( rA, impulse ); + vB = b2MulAdd( vB, mB, impulse ); + wB += iB * b2Cross( rB, impulse ); + } + + bodyA->linearVelocity = vA; + bodyA->angularVelocity = wA; + bodyB->linearVelocity = vB; + bodyB->angularVelocity = wB; +} + +#if 0 +void b2DumpMotorJoint() +{ + int32 indexA = m_bodyA->m_islandIndex; + int32 indexB = m_bodyB->m_islandIndex; + + b2Dump(" b2MotorJointDef jd;\n"); + b2Dump(" jd.bodyA = sims[%d];\n", indexA); + b2Dump(" jd.bodyB = sims[%d];\n", indexB); + b2Dump(" jd.collideConnected = bool(%d);\n", m_collideConnected); + b2Dump(" jd.localAnchorA.Set(%.9g, %.9g);\n", m_localAnchorA.x, m_localAnchorA.y); + b2Dump(" jd.localAnchorB.Set(%.9g, %.9g);\n", m_localAnchorB.x, m_localAnchorB.y); + b2Dump(" jd.referenceAngle = %.9g;\n", m_referenceAngle); + b2Dump(" jd.stiffness = %.9g;\n", m_stiffness); + b2Dump(" jd.damping = %.9g;\n", m_damping); + b2Dump(" joints[%d] = m_world->CreateJoint(&jd);\n", m_index); +} +#endif diff --git a/3rdparty/box2d/src/mouse_joint.c b/3rdparty/box2d/src/mouse_joint.c new file mode 100644 index 000000000000..4bbf6ef76455 --- /dev/null +++ b/3rdparty/box2d/src/mouse_joint.c @@ -0,0 +1,214 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#include "body.h" +#include "core.h" +#include "joint.h" +#include "solver.h" +#include "solver_set.h" +#include "world.h" + +// needed for dll export +#include "box2d/box2d.h" + +void b2MouseJoint_SetTarget( b2JointId jointId, b2Vec2 target ) +{ + B2_ASSERT( b2Vec2_IsValid( target ) ); + b2JointSim* base = b2GetJointSimCheckType( jointId, b2_mouseJoint ); + base->mouseJoint.targetA = target; +} + +b2Vec2 b2MouseJoint_GetTarget( b2JointId jointId ) +{ + b2JointSim* base = b2GetJointSimCheckType( jointId, b2_mouseJoint ); + return base->mouseJoint.targetA; +} + +void b2MouseJoint_SetSpringHertz( b2JointId jointId, float hertz ) +{ + B2_ASSERT( b2IsValid( hertz ) && hertz >= 0.0f ); + b2JointSim* base = b2GetJointSimCheckType( jointId, b2_mouseJoint ); + base->mouseJoint.hertz = hertz; +} + +float b2MouseJoint_GetSpringHertz( b2JointId jointId ) +{ + b2JointSim* base = b2GetJointSimCheckType( jointId, b2_mouseJoint ); + return base->mouseJoint.hertz; +} + +void b2MouseJoint_SetSpringDampingRatio( b2JointId jointId, float dampingRatio ) +{ + B2_ASSERT( b2IsValid( dampingRatio ) && dampingRatio >= 0.0f ); + b2JointSim* base = b2GetJointSimCheckType( jointId, b2_mouseJoint ); + base->mouseJoint.dampingRatio = dampingRatio; +} + +float b2MouseJoint_GetSpringDampingRatio( b2JointId jointId ) +{ + b2JointSim* base = b2GetJointSimCheckType( jointId, b2_mouseJoint ); + return base->mouseJoint.dampingRatio; +} + +void b2MouseJoint_SetMaxForce( b2JointId jointId, float maxForce ) +{ + B2_ASSERT( b2IsValid( maxForce ) && maxForce >= 0.0f ); + b2JointSim* base = b2GetJointSimCheckType( jointId, b2_mouseJoint ); + base->mouseJoint.maxForce = maxForce; +} + +float b2MouseJoint_GetMaxForce( b2JointId jointId ) +{ + b2JointSim* base = b2GetJointSimCheckType( jointId, b2_mouseJoint ); + return base->mouseJoint.maxForce; +} + +b2Vec2 b2GetMouseJointForce( b2World* world, b2JointSim* base ) +{ + b2Vec2 force = b2MulSV( world->inv_h, base->mouseJoint.linearImpulse ); + return force; +} + +float b2GetMouseJointTorque( b2World* world, b2JointSim* base ) +{ + return world->inv_h * base->mouseJoint.angularImpulse; +} + +void b2PrepareMouseJoint( b2JointSim* base, b2StepContext* context ) +{ + B2_ASSERT( base->type == b2_mouseJoint ); + + // chase body id to the solver set where the body lives + int idB = base->bodyIdB; + + b2World* world = context->world; + + b2Body* bodyB = b2BodyArray_Get( &world->bodies, idB ); + + B2_ASSERT( bodyB->setIndex == b2_awakeSet ); + b2SolverSet* setB = b2SolverSetArray_Get( &world->solverSets, bodyB->setIndex ); + + int localIndexB = bodyB->localIndex; + b2BodySim* bodySimB = b2BodySimArray_Get( &setB->bodySims, localIndexB ); + + base->invMassB = bodySimB->invMass; + base->invIB = bodySimB->invInertia; + + b2MouseJoint* joint = &base->mouseJoint; + joint->indexB = bodyB->setIndex == b2_awakeSet ? localIndexB : B2_NULL_INDEX; + joint->anchorB = b2RotateVector( bodySimB->transform.q, b2Sub( base->localOriginAnchorB, bodySimB->localCenter ) ); + + joint->linearSoftness = b2MakeSoft( joint->hertz, joint->dampingRatio, context->h ); + + float angularHertz = 0.5f; + float angularDampingRatio = 0.1f; + joint->angularSoftness = b2MakeSoft( angularHertz, angularDampingRatio, context->h ); + + b2Vec2 rB = joint->anchorB; + float mB = bodySimB->invMass; + float iB = bodySimB->invInertia; + + // K = [(1/m1 + 1/m2) * eye(2) - skew(r1) * invI1 * skew(r1) - skew(r2) * invI2 * skew(r2)] + // = [1/m1+1/m2 0 ] + invI1 * [r1.y*r1.y -r1.x*r1.y] + invI2 * [r1.y*r1.y -r1.x*r1.y] + // [ 0 1/m1+1/m2] [-r1.x*r1.y r1.x*r1.x] [-r1.x*r1.y r1.x*r1.x] + b2Mat22 K; + K.cx.x = mB + iB * rB.y * rB.y; + K.cx.y = -iB * rB.x * rB.y; + K.cy.x = K.cx.y; + K.cy.y = mB + iB * rB.x * rB.x; + + joint->linearMass = b2GetInverse22( K ); + joint->deltaCenter = b2Sub( bodySimB->center, joint->targetA ); + + if ( context->enableWarmStarting == false ) + { + joint->linearImpulse = b2Vec2_zero; + joint->angularImpulse = 0.0f; + } +} + +void b2WarmStartMouseJoint( b2JointSim* base, b2StepContext* context ) +{ + B2_ASSERT( base->type == b2_mouseJoint ); + + float mB = base->invMassB; + float iB = base->invIB; + + b2MouseJoint* joint = &base->mouseJoint; + + b2BodyState* stateB = context->states + joint->indexB; + b2Vec2 vB = stateB->linearVelocity; + float wB = stateB->angularVelocity; + + b2Rot dqB = stateB->deltaRotation; + b2Vec2 rB = b2RotateVector( dqB, joint->anchorB ); + + vB = b2MulAdd( vB, mB, joint->linearImpulse ); + wB += iB * ( b2Cross( rB, joint->linearImpulse ) + joint->angularImpulse ); + + stateB->linearVelocity = vB; + stateB->angularVelocity = wB; +} + +void b2SolveMouseJoint( b2JointSim* base, b2StepContext* context ) +{ + float mB = base->invMassB; + float iB = base->invIB; + + b2MouseJoint* joint = &base->mouseJoint; + b2BodyState* stateB = context->states + joint->indexB; + + b2Vec2 vB = stateB->linearVelocity; + float wB = stateB->angularVelocity; + + // Softness with no bias to reduce rotation speed + { + float massScale = joint->angularSoftness.massScale; + float impulseScale = joint->angularSoftness.impulseScale; + + float impulse = iB > 0.0f ? -wB / iB : 0.0f; + impulse = massScale * impulse - impulseScale * joint->angularImpulse; + joint->angularImpulse += impulse; + + wB += iB * impulse; + } + + float maxImpulse = joint->maxForce * context->h; + + { + b2Rot dqB = stateB->deltaRotation; + b2Vec2 rB = b2RotateVector( dqB, joint->anchorB ); + b2Vec2 Cdot = b2Add( vB, b2CrossSV( wB, rB ) ); + + b2Vec2 separation = b2Add( b2Add( stateB->deltaPosition, rB ), joint->deltaCenter ); + b2Vec2 bias = b2MulSV( joint->linearSoftness.biasRate, separation ); + + float massScale = joint->linearSoftness.massScale; + float impulseScale = joint->linearSoftness.impulseScale; + + b2Vec2 b = b2MulMV( joint->linearMass, b2Add( Cdot, bias ) ); + + b2Vec2 impulse; + impulse.x = -massScale * b.x - impulseScale * joint->linearImpulse.x; + impulse.y = -massScale * b.y - impulseScale * joint->linearImpulse.y; + + b2Vec2 oldImpulse = joint->linearImpulse; + joint->linearImpulse.x += impulse.x; + joint->linearImpulse.y += impulse.y; + + float mag = b2Length( joint->linearImpulse ); + if ( mag > maxImpulse ) + { + joint->linearImpulse = b2MulSV( maxImpulse, b2Normalize( joint->linearImpulse ) ); + } + + impulse.x = joint->linearImpulse.x - oldImpulse.x; + impulse.y = joint->linearImpulse.y - oldImpulse.y; + + vB = b2MulAdd( vB, mB, impulse ); + wB += iB * b2Cross( rB, impulse ); + } + + stateB->linearVelocity = vB; + stateB->angularVelocity = wB; +} diff --git a/3rdparty/box2d/src/prismatic_joint.c b/3rdparty/box2d/src/prismatic_joint.c new file mode 100644 index 000000000000..ba6836600ce7 --- /dev/null +++ b/3rdparty/box2d/src/prismatic_joint.c @@ -0,0 +1,600 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#include "body.h" +#include "core.h" +#include "joint.h" +#include "solver.h" +#include "solver_set.h" +#include "world.h" + +// needed for dll export +#include "box2d/box2d.h" + +#include + +void b2PrismaticJoint_EnableSpring( b2JointId jointId, bool enableSpring ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_prismaticJoint ); + if ( enableSpring != joint->prismaticJoint.enableSpring ) + { + joint->prismaticJoint.enableSpring = enableSpring; + joint->prismaticJoint.springImpulse = 0.0f; + } +} + +bool b2PrismaticJoint_IsSpringEnabled( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_prismaticJoint ); + return joint->prismaticJoint.enableSpring; +} + +void b2PrismaticJoint_SetSpringHertz( b2JointId jointId, float hertz ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_prismaticJoint ); + joint->prismaticJoint.hertz = hertz; +} + +float b2PrismaticJoint_GetSpringHertz( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_prismaticJoint ); + return joint->prismaticJoint.hertz; +} + +void b2PrismaticJoint_SetSpringDampingRatio( b2JointId jointId, float dampingRatio ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_prismaticJoint ); + joint->prismaticJoint.dampingRatio = dampingRatio; +} + +float b2PrismaticJoint_GetSpringDampingRatio( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_prismaticJoint ); + return joint->prismaticJoint.dampingRatio; +} + +void b2PrismaticJoint_EnableLimit( b2JointId jointId, bool enableLimit ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_prismaticJoint ); + if ( enableLimit != joint->prismaticJoint.enableLimit ) + { + joint->prismaticJoint.enableLimit = enableLimit; + joint->prismaticJoint.lowerImpulse = 0.0f; + joint->prismaticJoint.upperImpulse = 0.0f; + } +} + +bool b2PrismaticJoint_IsLimitEnabled( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_prismaticJoint ); + return joint->prismaticJoint.enableLimit; +} + +float b2PrismaticJoint_GetLowerLimit( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_prismaticJoint ); + return joint->prismaticJoint.lowerTranslation; +} + +float b2PrismaticJoint_GetUpperLimit( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_prismaticJoint ); + return joint->prismaticJoint.upperTranslation; +} + +void b2PrismaticJoint_SetLimits( b2JointId jointId, float lower, float upper ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_prismaticJoint ); + if ( lower != joint->prismaticJoint.lowerTranslation || upper != joint->prismaticJoint.upperTranslation ) + { + joint->prismaticJoint.lowerTranslation = b2MinFloat( lower, upper ); + joint->prismaticJoint.upperTranslation = b2MaxFloat( lower, upper ); + joint->prismaticJoint.lowerImpulse = 0.0f; + joint->prismaticJoint.upperImpulse = 0.0f; + } +} + +void b2PrismaticJoint_EnableMotor( b2JointId jointId, bool enableMotor ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_prismaticJoint ); + if ( enableMotor != joint->prismaticJoint.enableMotor ) + { + joint->prismaticJoint.enableMotor = enableMotor; + joint->prismaticJoint.motorImpulse = 0.0f; + } +} + +bool b2PrismaticJoint_IsMotorEnabled( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_prismaticJoint ); + return joint->prismaticJoint.enableMotor; +} + +void b2PrismaticJoint_SetMotorSpeed( b2JointId jointId, float motorSpeed ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_prismaticJoint ); + joint->prismaticJoint.motorSpeed = motorSpeed; +} + +float b2PrismaticJoint_GetMotorSpeed( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_prismaticJoint ); + return joint->prismaticJoint.motorSpeed; +} + +float b2PrismaticJoint_GetMotorForce( b2JointId jointId ) +{ + b2World* world = b2GetWorld( jointId.world0 ); + b2JointSim* base = b2GetJointSimCheckType( jointId, b2_prismaticJoint ); + return world->inv_h * base->prismaticJoint.motorImpulse; +} + +void b2PrismaticJoint_SetMaxMotorForce( b2JointId jointId, float force ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_prismaticJoint ); + joint->prismaticJoint.maxMotorForce = force; +} + +float b2PrismaticJoint_GetMaxMotorForce( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_prismaticJoint ); + return joint->prismaticJoint.maxMotorForce; +} + +b2Vec2 b2GetPrismaticJointForce( b2World* world, b2JointSim* base ) +{ + int idA = base->bodyIdA; + b2Transform transformA = b2GetBodyTransform( world, idA ); + + b2PrismaticJoint* joint = &base->prismaticJoint; + + b2Vec2 axisA = b2RotateVector( transformA.q, joint->localAxisA ); + b2Vec2 perpA = b2LeftPerp( axisA ); + + float inv_h = world->inv_h; + float perpForce = inv_h * joint->impulse.x; + float axialForce = inv_h * ( joint->motorImpulse + joint->lowerImpulse - joint->upperImpulse ); + + b2Vec2 force = b2Add( b2MulSV( perpForce, perpA ), b2MulSV( axialForce, axisA ) ); + return force; +} + +float b2GetPrismaticJointTorque( b2World* world, b2JointSim* base ) +{ + return world->inv_h * base->prismaticJoint.impulse.y; +} + +// Linear constraint (point-to-line) +// d = p2 - p1 = x2 + r2 - x1 - r1 +// C = dot(perp, d) +// Cdot = dot(d, cross(w1, perp)) + dot(perp, v2 + cross(w2, r2) - v1 - cross(w1, r1)) +// = -dot(perp, v1) - dot(cross(d + r1, perp), w1) + dot(perp, v2) + dot(cross(r2, perp), v2) +// J = [-perp, -cross(d + r1, perp), perp, cross(r2,perp)] +// +// Angular constraint +// C = a2 - a1 + a_initial +// Cdot = w2 - w1 +// J = [0 0 -1 0 0 1] +// +// K = J * invM * JT +// +// J = [-a -s1 a s2] +// [0 -1 0 1] +// a = perp +// s1 = cross(d + r1, a) = cross(p2 - x1, a) +// s2 = cross(r2, a) = cross(p2 - x2, a) + +// Motor/Limit linear constraint +// C = dot(ax1, d) +// Cdot = -dot(ax1, v1) - dot(cross(d + r1, ax1), w1) + dot(ax1, v2) + dot(cross(r2, ax1), v2) +// J = [-ax1 -cross(d+r1,ax1) ax1 cross(r2,ax1)] + +// Predictive limit is applied even when the limit is not active. +// Prevents a constraint speed that can lead to a constraint error in one time step. +// Want C2 = C1 + h * Cdot >= 0 +// Or: +// Cdot + C1/h >= 0 +// I do not apply a negative constraint error because that is handled in position correction. +// So: +// Cdot + max(C1, 0)/h >= 0 + +// Block Solver +// We develop a block solver that includes the angular and linear constraints. This makes the limit stiffer. +// +// The Jacobian has 2 rows: +// J = [-uT -s1 uT s2] // linear +// [0 -1 0 1] // angular +// +// u = perp +// s1 = cross(d + r1, u), s2 = cross(r2, u) +// a1 = cross(d + r1, v), a2 = cross(r2, v) + +void b2PreparePrismaticJoint( b2JointSim* base, b2StepContext* context ) +{ + B2_ASSERT( base->type == b2_prismaticJoint ); + + // chase body id to the solver set where the body lives + int idA = base->bodyIdA; + int idB = base->bodyIdB; + + b2World* world = context->world; + + b2Body* bodyA = b2BodyArray_Get( &world->bodies, idA ); + b2Body* bodyB = b2BodyArray_Get( &world->bodies, idB ); + + B2_ASSERT( bodyA->setIndex == b2_awakeSet || bodyB->setIndex == b2_awakeSet ); + b2SolverSet* setA = b2SolverSetArray_Get( &world->solverSets, bodyA->setIndex ); + b2SolverSet* setB = b2SolverSetArray_Get( &world->solverSets, bodyB->setIndex ); + + int localIndexA = bodyA->localIndex; + int localIndexB = bodyB->localIndex; + + b2BodySim* bodySimA = b2BodySimArray_Get( &setA->bodySims, localIndexA ); + b2BodySim* bodySimB = b2BodySimArray_Get( &setB->bodySims, localIndexB ); + + float mA = bodySimA->invMass; + float iA = bodySimA->invInertia; + float mB = bodySimB->invMass; + float iB = bodySimB->invInertia; + + base->invMassA = mA; + base->invMassB = mB; + base->invIA = iA; + base->invIB = iB; + + b2PrismaticJoint* joint = &base->prismaticJoint; + joint->indexA = bodyA->setIndex == b2_awakeSet ? localIndexA : B2_NULL_INDEX; + joint->indexB = bodyB->setIndex == b2_awakeSet ? localIndexB : B2_NULL_INDEX; + + b2Rot qA = bodySimA->transform.q; + b2Rot qB = bodySimB->transform.q; + + joint->anchorA = b2RotateVector( qA, b2Sub( base->localOriginAnchorA, bodySimA->localCenter ) ); + joint->anchorB = b2RotateVector( qB, b2Sub( base->localOriginAnchorB, bodySimB->localCenter ) ); + joint->axisA = b2RotateVector( qA, joint->localAxisA ); + joint->deltaCenter = b2Sub( bodySimB->center, bodySimA->center ); + joint->deltaAngle = b2RelativeAngle( qB, qA ) - joint->referenceAngle; + + b2Vec2 rA = joint->anchorA; + b2Vec2 rB = joint->anchorB; + + b2Vec2 d = b2Add( joint->deltaCenter, b2Sub( rB, rA ) ); + float a1 = b2Cross( b2Add( d, rA ), joint->axisA ); + float a2 = b2Cross( rB, joint->axisA ); + + // effective masses + float k = mA + mB + iA * a1 * a1 + iB * a2 * a2; + joint->axialMass = k > 0.0f ? 1.0f / k : 0.0f; + + joint->springSoftness = b2MakeSoft( joint->hertz, joint->dampingRatio, context->h ); + + if ( context->enableWarmStarting == false ) + { + joint->impulse = b2Vec2_zero; + joint->springImpulse = 0.0f; + joint->motorImpulse = 0.0f; + joint->lowerImpulse = 0.0f; + joint->upperImpulse = 0.0f; + } +} + +void b2WarmStartPrismaticJoint( b2JointSim* base, b2StepContext* context ) +{ + B2_ASSERT( base->type == b2_prismaticJoint ); + + float mA = base->invMassA; + float mB = base->invMassB; + float iA = base->invIA; + float iB = base->invIB; + + // dummy state for static bodies + b2BodyState dummyState = b2_identityBodyState; + + b2PrismaticJoint* joint = &base->prismaticJoint; + + b2BodyState* stateA = joint->indexA == B2_NULL_INDEX ? &dummyState : context->states + joint->indexA; + b2BodyState* stateB = joint->indexB == B2_NULL_INDEX ? &dummyState : context->states + joint->indexB; + + b2Vec2 rA = b2RotateVector( stateA->deltaRotation, joint->anchorA ); + b2Vec2 rB = b2RotateVector( stateB->deltaRotation, joint->anchorB ); + + b2Vec2 d = b2Add( b2Add( b2Sub( stateB->deltaPosition, stateA->deltaPosition ), joint->deltaCenter ), b2Sub( rB, rA ) ); + b2Vec2 axisA = b2RotateVector( stateA->deltaRotation, joint->axisA ); + + // impulse is applied at anchor point on body B + float a1 = b2Cross( b2Add( d, rA ), axisA ); + float a2 = b2Cross( rB, axisA ); + float axialImpulse = joint->springImpulse + joint->motorImpulse + joint->lowerImpulse - joint->upperImpulse; + + // perpendicular constraint + b2Vec2 perpA = b2LeftPerp( axisA ); + float s1 = b2Cross( b2Add( d, rA ), perpA ); + float s2 = b2Cross( rB, perpA ); + float perpImpulse = joint->impulse.x; + float angleImpulse = joint->impulse.y; + + b2Vec2 P = b2Add( b2MulSV( axialImpulse, axisA ), b2MulSV( perpImpulse, perpA ) ); + float LA = axialImpulse * a1 + perpImpulse * s1 + angleImpulse; + float LB = axialImpulse * a2 + perpImpulse * s2 + angleImpulse; + + stateA->linearVelocity = b2MulSub( stateA->linearVelocity, mA, P ); + stateA->angularVelocity -= iA * LA; + stateB->linearVelocity = b2MulAdd( stateB->linearVelocity, mB, P ); + stateB->angularVelocity += iB * LB; +} + +void b2SolvePrismaticJoint( b2JointSim* base, b2StepContext* context, bool useBias ) +{ + B2_ASSERT( base->type == b2_prismaticJoint ); + + float mA = base->invMassA; + float mB = base->invMassB; + float iA = base->invIA; + float iB = base->invIB; + + // dummy state for static bodies + b2BodyState dummyState = b2_identityBodyState; + + b2PrismaticJoint* joint = &base->prismaticJoint; + + b2BodyState* stateA = joint->indexA == B2_NULL_INDEX ? &dummyState : context->states + joint->indexA; + b2BodyState* stateB = joint->indexB == B2_NULL_INDEX ? &dummyState : context->states + joint->indexB; + + b2Vec2 vA = stateA->linearVelocity; + float wA = stateA->angularVelocity; + b2Vec2 vB = stateB->linearVelocity; + float wB = stateB->angularVelocity; + + // current anchors + b2Vec2 rA = b2RotateVector( stateA->deltaRotation, joint->anchorA ); + b2Vec2 rB = b2RotateVector( stateB->deltaRotation, joint->anchorB ); + + b2Vec2 d = b2Add( b2Add( b2Sub( stateB->deltaPosition, stateA->deltaPosition ), joint->deltaCenter ), b2Sub( rB, rA ) ); + b2Vec2 axisA = b2RotateVector( stateA->deltaRotation, joint->axisA ); + float translation = b2Dot( axisA, d ); + + // These scalars are for torques generated by axial forces + float a1 = b2Cross( b2Add( d, rA ), axisA ); + float a2 = b2Cross( rB, axisA ); + + // spring constraint + if ( joint->enableSpring ) + { + // This is a real spring and should be applied even during relax + float C = translation; + float bias = joint->springSoftness.biasRate * C; + float massScale = joint->springSoftness.massScale; + float impulseScale = joint->springSoftness.impulseScale; + + float Cdot = b2Dot( axisA, b2Sub( vB, vA ) ) + a2 * wB - a1 * wA; + float impulse = -massScale * joint->axialMass * ( Cdot + bias ) - impulseScale * joint->springImpulse; + joint->springImpulse += impulse; + + b2Vec2 P = b2MulSV( impulse, axisA ); + float LA = impulse * a1; + float LB = impulse * a2; + + vA = b2MulSub( vA, mA, P ); + wA -= iA * LA; + vB = b2MulAdd( vB, mB, P ); + wB += iB * LB; + } + + // Solve motor constraint + if ( joint->enableMotor ) + { + float Cdot = b2Dot( axisA, b2Sub( vB, vA ) ) + a2 * wB - a1 * wA; + float impulse = joint->axialMass * ( joint->motorSpeed - Cdot ); + float oldImpulse = joint->motorImpulse; + float maxImpulse = context->h * joint->maxMotorForce; + joint->motorImpulse = b2ClampFloat( joint->motorImpulse + impulse, -maxImpulse, maxImpulse ); + impulse = joint->motorImpulse - oldImpulse; + + b2Vec2 P = b2MulSV( impulse, axisA ); + float LA = impulse * a1; + float LB = impulse * a2; + + vA = b2MulSub( vA, mA, P ); + wA -= iA * LA; + vB = b2MulAdd( vB, mB, P ); + wB += iB * LB; + } + + if ( joint->enableLimit ) + { + // Lower limit + { + float C = translation - joint->lowerTranslation; + float bias = 0.0f; + float massScale = 1.0f; + float impulseScale = 0.0f; + + if ( C > 0.0f ) + { + // speculation + bias = C * context->inv_h; + } + else if ( useBias ) + { + bias = context->jointSoftness.biasRate * C; + massScale = context->jointSoftness.massScale; + impulseScale = context->jointSoftness.impulseScale; + } + + float oldImpulse = joint->lowerImpulse; + float Cdot = b2Dot( axisA, b2Sub( vB, vA ) ) + a2 * wB - a1 * wA; + float impulse = -joint->axialMass * massScale * ( Cdot + bias ) - impulseScale * oldImpulse; + joint->lowerImpulse = b2MaxFloat( oldImpulse + impulse, 0.0f ); + impulse = joint->lowerImpulse - oldImpulse; + + b2Vec2 P = b2MulSV( impulse, axisA ); + float LA = impulse * a1; + float LB = impulse * a2; + + vA = b2MulSub( vA, mA, P ); + wA -= iA * LA; + vB = b2MulAdd( vB, mB, P ); + wB += iB * LB; + } + + // Upper limit + // Note: signs are flipped to keep C positive when the constraint is satisfied. + // This also keeps the impulse positive when the limit is active. + { + // sign flipped + float C = joint->upperTranslation - translation; + float bias = 0.0f; + float massScale = 1.0f; + float impulseScale = 0.0f; + + if ( C > 0.0f ) + { + // speculation + bias = C * context->inv_h; + } + else if ( useBias ) + { + bias = context->jointSoftness.biasRate * C; + massScale = context->jointSoftness.massScale; + impulseScale = context->jointSoftness.impulseScale; + } + + float oldImpulse = joint->upperImpulse; + // sign flipped + float Cdot = b2Dot( axisA, b2Sub( vA, vB ) ) + a1 * wA - a2 * wB; + float impulse = -joint->axialMass * massScale * ( Cdot + bias ) - impulseScale * oldImpulse; + joint->upperImpulse = b2MaxFloat( oldImpulse + impulse, 0.0f ); + impulse = joint->upperImpulse - oldImpulse; + + b2Vec2 P = b2MulSV( impulse, axisA ); + float LA = impulse * a1; + float LB = impulse * a2; + + // sign flipped + vA = b2MulAdd( vA, mA, P ); + wA += iA * LA; + vB = b2MulSub( vB, mB, P ); + wB -= iB * LB; + } + } + + // Solve the prismatic constraint in block form + { + b2Vec2 perpA = b2LeftPerp( axisA ); + + // These scalars are for torques generated by the perpendicular constraint force + float s1 = b2Cross( b2Add( d, rA ), perpA ); + float s2 = b2Cross( rB, perpA ); + + b2Vec2 Cdot; + Cdot.x = b2Dot( perpA, b2Sub( vB, vA ) ) + s2 * wB - s1 * wA; + Cdot.y = wB - wA; + + b2Vec2 bias = b2Vec2_zero; + float massScale = 1.0f; + float impulseScale = 0.0f; + if ( useBias ) + { + b2Vec2 C; + C.x = b2Dot( perpA, d ); + C.y = b2RelativeAngle( stateB->deltaRotation, stateA->deltaRotation ) + joint->deltaAngle; + + bias = b2MulSV( context->jointSoftness.biasRate, C ); + massScale = context->jointSoftness.massScale; + impulseScale = context->jointSoftness.impulseScale; + } + + float k11 = mA + mB + iA * s1 * s1 + iB * s2 * s2; + float k12 = iA * s1 + iB * s2; + float k22 = iA + iB; + if ( k22 == 0.0f ) + { + // For bodies with fixed rotation. + k22 = 1.0f; + } + + b2Mat22 K = { { k11, k12 }, { k12, k22 } }; + + b2Vec2 b = b2Solve22( K, b2Add( Cdot, bias ) ); + b2Vec2 impulse; + impulse.x = -massScale * b.x - impulseScale * joint->impulse.x; + impulse.y = -massScale * b.y - impulseScale * joint->impulse.y; + + joint->impulse.x += impulse.x; + joint->impulse.y += impulse.y; + + b2Vec2 P = b2MulSV( impulse.x, perpA ); + float LA = impulse.x * s1 + impulse.y; + float LB = impulse.x * s2 + impulse.y; + + vA = b2MulSub( vA, mA, P ); + wA -= iA * LA; + vB = b2MulAdd( vB, mB, P ); + wB += iB * LB; + } + + stateA->linearVelocity = vA; + stateA->angularVelocity = wA; + stateB->linearVelocity = vB; + stateB->angularVelocity = wB; +} + +#if 0 +void b2PrismaticJoint::Dump() +{ + int32 indexA = joint->bodyA->joint->islandIndex; + int32 indexB = joint->bodyB->joint->islandIndex; + + b2Dump(" b2PrismaticJointDef jd;\n"); + b2Dump(" jd.bodyA = sims[%d];\n", indexA); + b2Dump(" jd.bodyB = sims[%d];\n", indexB); + b2Dump(" jd.collideConnected = bool(%d);\n", joint->collideConnected); + b2Dump(" jd.localAnchorA.Set(%.9g, %.9g);\n", joint->localAnchorA.x, joint->localAnchorA.y); + b2Dump(" jd.localAnchorB.Set(%.9g, %.9g);\n", joint->localAnchorB.x, joint->localAnchorB.y); + b2Dump(" jd.referenceAngle = %.9g;\n", joint->referenceAngle); + b2Dump(" jd.enableLimit = bool(%d);\n", joint->enableLimit); + b2Dump(" jd.lowerAngle = %.9g;\n", joint->lowerAngle); + b2Dump(" jd.upperAngle = %.9g;\n", joint->upperAngle); + b2Dump(" jd.enableMotor = bool(%d);\n", joint->enableMotor); + b2Dump(" jd.motorSpeed = %.9g;\n", joint->motorSpeed); + b2Dump(" jd.maxMotorTorque = %.9g;\n", joint->maxMotorTorque); + b2Dump(" joints[%d] = joint->world->CreateJoint(&jd);\n", joint->index); +} +#endif + +void b2DrawPrismaticJoint( b2DebugDraw* draw, b2JointSim* base, b2Transform transformA, b2Transform transformB ) +{ + B2_ASSERT( base->type == b2_prismaticJoint ); + + b2PrismaticJoint* joint = &base->prismaticJoint; + + b2Vec2 pA = b2TransformPoint( transformA, base->localOriginAnchorA ); + b2Vec2 pB = b2TransformPoint( transformB, base->localOriginAnchorB ); + + b2Vec2 axis = b2RotateVector( transformA.q, joint->localAxisA ); + + b2HexColor c1 = b2_colorGray7; + b2HexColor c2 = b2_colorGreen; + b2HexColor c3 = b2_colorRed; + b2HexColor c4 = b2_colorBlue; + b2HexColor c5 = b2_colorGray4; + + draw->DrawSegment( pA, pB, c5, draw->context ); + + if ( joint->enableLimit ) + { + b2Vec2 lower = b2MulAdd( pA, joint->lowerTranslation, axis ); + b2Vec2 upper = b2MulAdd( pA, joint->upperTranslation, axis ); + b2Vec2 perp = b2LeftPerp( axis ); + draw->DrawSegment( lower, upper, c1, draw->context ); + draw->DrawSegment( b2MulSub( lower, 0.1f, perp ), b2MulAdd( lower, 0.1f, perp ), c2, draw->context ); + draw->DrawSegment( b2MulSub( upper, 0.1f, perp ), b2MulAdd( upper, 0.1f, perp ), c3, draw->context ); + } + else + { + draw->DrawSegment( b2MulSub( pA, 1.0f, axis ), b2MulAdd( pA, 1.0f, axis ), c1, draw->context ); + } + + draw->DrawPoint( pA, 5.0f, c1, draw->context ); + draw->DrawPoint( pB, 5.0f, c4, draw->context ); +} diff --git a/3rdparty/box2d/src/revolute_joint.c b/3rdparty/box2d/src/revolute_joint.c new file mode 100644 index 000000000000..98b8eb09c87d --- /dev/null +++ b/3rdparty/box2d/src/revolute_joint.c @@ -0,0 +1,547 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#if defined( _MSC_VER ) && !defined( _CRT_SECURE_NO_WARNINGS ) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "body.h" +#include "core.h" +#include "joint.h" +#include "solver.h" +#include "solver_set.h" +#include "world.h" + +// needed for dll export +#include "box2d/box2d.h" + +#include + +void b2RevoluteJoint_EnableSpring( b2JointId jointId, bool enableSpring ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint ); + if ( enableSpring != joint->revoluteJoint.enableSpring ) + { + joint->revoluteJoint.enableSpring = enableSpring; + joint->revoluteJoint.springImpulse = 0.0f; + } +} + +bool b2RevoluteJoint_IsSpringEnabled( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint ); + return joint->revoluteJoint.enableSpring; +} + +void b2RevoluteJoint_SetSpringHertz( b2JointId jointId, float hertz ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint ); + joint->revoluteJoint.hertz = hertz; +} + +float b2RevoluteJoint_GetSpringHertz( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint ); + return joint->revoluteJoint.hertz; +} + +void b2RevoluteJoint_SetSpringDampingRatio( b2JointId jointId, float dampingRatio ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint ); + joint->revoluteJoint.dampingRatio = dampingRatio; +} + +float b2RevoluteJoint_GetSpringDampingRatio( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint ); + return joint->revoluteJoint.dampingRatio; +} + +float b2RevoluteJoint_GetAngle( b2JointId jointId ) +{ + b2World* world = b2GetWorld( jointId.world0 ); + b2JointSim* jointSim = b2GetJointSimCheckType( jointId, b2_revoluteJoint ); + b2Transform transformA = b2GetBodyTransform( world, jointSim->bodyIdA ); + b2Transform transformB = b2GetBodyTransform( world, jointSim->bodyIdB ); + + float angle = b2RelativeAngle( transformB.q, transformA.q ) - jointSim->revoluteJoint.referenceAngle; + angle = b2UnwindAngle( angle ); + return angle; +} + +void b2RevoluteJoint_EnableLimit( b2JointId jointId, bool enableLimit ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint ); + if ( enableLimit != joint->revoluteJoint.enableLimit ) + { + joint->revoluteJoint.enableLimit = enableLimit; + joint->revoluteJoint.lowerImpulse = 0.0f; + joint->revoluteJoint.upperImpulse = 0.0f; + } +} + +bool b2RevoluteJoint_IsLimitEnabled( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint ); + return joint->revoluteJoint.enableLimit; +} + +float b2RevoluteJoint_GetLowerLimit( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint ); + return joint->revoluteJoint.lowerAngle; +} + +float b2RevoluteJoint_GetUpperLimit( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint ); + return joint->revoluteJoint.upperAngle; +} + +void b2RevoluteJoint_SetLimits( b2JointId jointId, float lower, float upper ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint ); + if ( lower != joint->revoluteJoint.lowerAngle || upper != joint->revoluteJoint.upperAngle ) + { + joint->revoluteJoint.lowerAngle = b2MinFloat( lower, upper ); + joint->revoluteJoint.upperAngle = b2MaxFloat( lower, upper ); + joint->revoluteJoint.lowerImpulse = 0.0f; + joint->revoluteJoint.upperImpulse = 0.0f; + } +} + +void b2RevoluteJoint_EnableMotor( b2JointId jointId, bool enableMotor ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint ); + if ( enableMotor != joint->revoluteJoint.enableMotor ) + { + joint->revoluteJoint.enableMotor = enableMotor; + joint->revoluteJoint.motorImpulse = 0.0f; + } +} + +bool b2RevoluteJoint_IsMotorEnabled( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint ); + return joint->revoluteJoint.enableMotor; +} + +void b2RevoluteJoint_SetMotorSpeed( b2JointId jointId, float motorSpeed ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint ); + joint->revoluteJoint.motorSpeed = motorSpeed; +} + +float b2RevoluteJoint_GetMotorSpeed( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint ); + return joint->revoluteJoint.motorSpeed; +} + +float b2RevoluteJoint_GetMotorTorque( b2JointId jointId ) +{ + b2World* world = b2GetWorld( jointId.world0 ); + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint ); + return world->inv_h * joint->revoluteJoint.motorImpulse; +} + +void b2RevoluteJoint_SetMaxMotorTorque( b2JointId jointId, float torque ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint ); + joint->revoluteJoint.maxMotorTorque = torque; +} + +float b2RevoluteJoint_GetMaxMotorTorque( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_revoluteJoint ); + return joint->revoluteJoint.maxMotorTorque; +} + +b2Vec2 b2GetRevoluteJointForce( b2World* world, b2JointSim* base ) +{ + b2Vec2 force = b2MulSV( world->inv_h, base->revoluteJoint.linearImpulse ); + return force; +} + +float b2GetRevoluteJointTorque( b2World* world, b2JointSim* base ) +{ + const b2RevoluteJoint* revolute = &base->revoluteJoint; + float torque = world->inv_h * ( revolute->motorImpulse + revolute->lowerImpulse - revolute->upperImpulse ); + return torque; +} + +// Point-to-point constraint +// C = p2 - p1 +// Cdot = v2 - v1 +// = v2 + cross(w2, r2) - v1 - cross(w1, r1) +// J = [-I -r1_skew I r2_skew ] +// Identity used: +// w k % (rx i + ry j) = w * (-ry i + rx j) + +// Motor constraint +// Cdot = w2 - w1 +// J = [0 0 -1 0 0 1] +// K = invI1 + invI2 + +// Body State +// The solver operates on the body state. The body state array does not hold static bodies. Static bodies are shared +// across worker threads. It would be okay to read their states, but writing to them would cause cache thrashing across +// workers, even if the values don't change. +// This causes some trouble when computing anchors. I rotate the anchors using the body rotation every sub-step. For static +// bodies the anchor doesn't rotate. Body A or B could be static and this can lead to lots of branching. This branching +// should be minimized. +// +// Solution 1: +// Use delta rotations. This means anchors need to be prepared in world space. The delta rotation for static bodies will be +// identity. Base separation and angles need to be computed. Manifolds will be behind a frame, but that is probably best if bodies +// move fast. +// +// Solution 2: +// Use full rotation. The anchors for static bodies will be in world space while the anchors for dynamic bodies will be in local +// space. Potentially confusing and bug prone. + +void b2PrepareRevoluteJoint( b2JointSim* base, b2StepContext* context ) +{ + B2_ASSERT( base->type == b2_revoluteJoint ); + + // chase body id to the solver set where the body lives + int idA = base->bodyIdA; + int idB = base->bodyIdB; + + b2World* world = context->world; + + b2Body* bodyA = b2BodyArray_Get(&world->bodies, idA); + b2Body* bodyB = b2BodyArray_Get(&world->bodies, idB); + + B2_ASSERT( bodyA->setIndex == b2_awakeSet || bodyB->setIndex == b2_awakeSet ); + b2SolverSet* setA = b2SolverSetArray_Get( &world->solverSets, bodyA->setIndex ); + b2SolverSet* setB = b2SolverSetArray_Get( &world->solverSets, bodyB->setIndex ); + + int localIndexA = bodyA->localIndex; + int localIndexB = bodyB->localIndex; + + b2BodySim* bodySimA = b2BodySimArray_Get( &setA->bodySims, localIndexA ); + b2BodySim* bodySimB = b2BodySimArray_Get( &setB->bodySims, localIndexB ); + + float mA = bodySimA->invMass; + float iA = bodySimA->invInertia; + float mB = bodySimB->invMass; + float iB = bodySimB->invInertia; + + base->invMassA = mA; + base->invMassB = mB; + base->invIA = iA; + base->invIB = iB; + + b2RevoluteJoint* joint = &base->revoluteJoint; + + joint->indexA = bodyA->setIndex == b2_awakeSet ? localIndexA : B2_NULL_INDEX; + joint->indexB = bodyB->setIndex == b2_awakeSet ? localIndexB : B2_NULL_INDEX; + + // initial anchors in world space + joint->anchorA = b2RotateVector( bodySimA->transform.q, b2Sub( base->localOriginAnchorA, bodySimA->localCenter ) ); + joint->anchorB = b2RotateVector( bodySimB->transform.q, b2Sub( base->localOriginAnchorB, bodySimB->localCenter ) ); + joint->deltaCenter = b2Sub( bodySimB->center, bodySimA->center ); + joint->deltaAngle = b2RelativeAngle( bodySimB->transform.q, bodySimA->transform.q ) - joint->referenceAngle; + joint->deltaAngle = b2UnwindAngle( joint->deltaAngle ); + + float k = iA + iB; + joint->axialMass = k > 0.0f ? 1.0f / k : 0.0f; + + joint->springSoftness = b2MakeSoft( joint->hertz, joint->dampingRatio, context->h ); + + if ( context->enableWarmStarting == false ) + { + joint->linearImpulse = b2Vec2_zero; + joint->springImpulse = 0.0f; + joint->motorImpulse = 0.0f; + joint->lowerImpulse = 0.0f; + joint->upperImpulse = 0.0f; + } +} + +void b2WarmStartRevoluteJoint( b2JointSim* base, b2StepContext* context ) +{ + B2_ASSERT( base->type == b2_revoluteJoint ); + + float mA = base->invMassA; + float mB = base->invMassB; + float iA = base->invIA; + float iB = base->invIB; + + // dummy state for static bodies + b2BodyState dummyState = b2_identityBodyState; + + b2RevoluteJoint* joint = &base->revoluteJoint; + b2BodyState* stateA = joint->indexA == B2_NULL_INDEX ? &dummyState : context->states + joint->indexA; + b2BodyState* stateB = joint->indexB == B2_NULL_INDEX ? &dummyState : context->states + joint->indexB; + + b2Vec2 rA = b2RotateVector( stateA->deltaRotation, joint->anchorA ); + b2Vec2 rB = b2RotateVector( stateB->deltaRotation, joint->anchorB ); + + float axialImpulse = joint->springImpulse + joint->motorImpulse + joint->lowerImpulse - joint->upperImpulse; + + stateA->linearVelocity = b2MulSub( stateA->linearVelocity, mA, joint->linearImpulse ); + stateA->angularVelocity -= iA * ( b2Cross( rA, joint->linearImpulse ) + axialImpulse ); + + stateB->linearVelocity = b2MulAdd( stateB->linearVelocity, mB, joint->linearImpulse ); + stateB->angularVelocity += iB * ( b2Cross( rB, joint->linearImpulse ) + axialImpulse ); +} + +void b2SolveRevoluteJoint( b2JointSim* base, b2StepContext* context, bool useBias ) +{ + B2_ASSERT( base->type == b2_revoluteJoint ); + + float mA = base->invMassA; + float mB = base->invMassB; + float iA = base->invIA; + float iB = base->invIB; + + // dummy state for static bodies + b2BodyState dummyState = b2_identityBodyState; + + b2RevoluteJoint* joint = &base->revoluteJoint; + + b2BodyState* stateA = joint->indexA == B2_NULL_INDEX ? &dummyState : context->states + joint->indexA; + b2BodyState* stateB = joint->indexB == B2_NULL_INDEX ? &dummyState : context->states + joint->indexB; + + b2Vec2 vA = stateA->linearVelocity; + float wA = stateA->angularVelocity; + b2Vec2 vB = stateB->linearVelocity; + float wB = stateB->angularVelocity; + + bool fixedRotation = ( iA + iB == 0.0f ); + // const float maxBias = context->maxBiasVelocity; + + // Solve spring. + if ( joint->enableSpring && fixedRotation == false ) + { + float C = b2RelativeAngle( stateB->deltaRotation, stateA->deltaRotation ) + joint->deltaAngle; + float bias = joint->springSoftness.biasRate * C; + float massScale = joint->springSoftness.massScale; + float impulseScale = joint->springSoftness.impulseScale; + + float Cdot = wB - wA; + float impulse = -massScale * joint->axialMass * ( Cdot + bias ) - impulseScale * joint->springImpulse; + joint->springImpulse += impulse; + + wA -= iA * impulse; + wB += iB * impulse; + } + + // Solve motor constraint. + if ( joint->enableMotor && fixedRotation == false ) + { + float Cdot = wB - wA - joint->motorSpeed; + float impulse = -joint->axialMass * Cdot; + float oldImpulse = joint->motorImpulse; + float maxImpulse = context->h * joint->maxMotorTorque; + joint->motorImpulse = b2ClampFloat( joint->motorImpulse + impulse, -maxImpulse, maxImpulse ); + impulse = joint->motorImpulse - oldImpulse; + + wA -= iA * impulse; + wB += iB * impulse; + } + + if ( joint->enableLimit && fixedRotation == false ) + { + float jointAngle = b2RelativeAngle( stateB->deltaRotation, stateA->deltaRotation ) + joint->deltaAngle; + jointAngle = b2UnwindAngle( jointAngle ); + + // Lower limit + { + float C = jointAngle - joint->lowerAngle; + float bias = 0.0f; + float massScale = 1.0f; + float impulseScale = 0.0f; + if ( C > 0.0f ) + { + // speculation + bias = C * context->inv_h; + } + else if ( useBias ) + { + bias = context->jointSoftness.biasRate * C; + massScale = context->jointSoftness.massScale; + impulseScale = context->jointSoftness.impulseScale; + } + + float Cdot = wB - wA; + float impulse = -massScale * joint->axialMass * ( Cdot + bias ) - impulseScale * joint->lowerImpulse; + float oldImpulse = joint->lowerImpulse; + joint->lowerImpulse = b2MaxFloat( joint->lowerImpulse + impulse, 0.0f ); + impulse = joint->lowerImpulse - oldImpulse; + + wA -= iA * impulse; + wB += iB * impulse; + } + + // Upper limit + // Note: signs are flipped to keep C positive when the constraint is satisfied. + // This also keeps the impulse positive when the limit is active. + { + float C = joint->upperAngle - jointAngle; + float bias = 0.0f; + float massScale = 1.0f; + float impulseScale = 0.0f; + if ( C > 0.0f ) + { + // speculation + bias = C * context->inv_h; + } + else if ( useBias ) + { + bias = context->jointSoftness.biasRate * C; + massScale = context->jointSoftness.massScale; + impulseScale = context->jointSoftness.impulseScale; + } + + // sign flipped on Cdot + float Cdot = wA - wB; + float impulse = -massScale * joint->axialMass * ( Cdot + bias ) - impulseScale * joint->lowerImpulse; + float oldImpulse = joint->upperImpulse; + joint->upperImpulse = b2MaxFloat( joint->upperImpulse + impulse, 0.0f ); + impulse = joint->upperImpulse - oldImpulse; + + // sign flipped on applied impulse + wA += iA * impulse; + wB -= iB * impulse; + } + } + + // Solve point-to-point constraint + { + // J = [-I -r1_skew I r2_skew] + // r_skew = [-ry; rx] + // K = [ mA+r1y^2*iA+mB+r2y^2*iB, -r1y*iA*r1x-r2y*iB*r2x] + // [ -r1y*iA*r1x-r2y*iB*r2x, mA+r1x^2*iA+mB+r2x^2*iB] + + // current anchors + b2Vec2 rA = b2RotateVector( stateA->deltaRotation, joint->anchorA ); + b2Vec2 rB = b2RotateVector( stateB->deltaRotation, joint->anchorB ); + + b2Vec2 Cdot = b2Sub( b2Add( vB, b2CrossSV( wB, rB ) ), b2Add( vA, b2CrossSV( wA, rA ) ) ); + + b2Vec2 bias = b2Vec2_zero; + float massScale = 1.0f; + float impulseScale = 0.0f; + if ( useBias ) + { + b2Vec2 dcA = stateA->deltaPosition; + b2Vec2 dcB = stateB->deltaPosition; + + b2Vec2 separation = b2Add( b2Add( b2Sub( dcB, dcA ), b2Sub( rB, rA ) ), joint->deltaCenter ); + bias = b2MulSV( context->jointSoftness.biasRate, separation ); + massScale = context->jointSoftness.massScale; + impulseScale = context->jointSoftness.impulseScale; + } + + b2Mat22 K; + K.cx.x = mA + mB + rA.y * rA.y * iA + rB.y * rB.y * iB; + K.cy.x = -rA.y * rA.x * iA - rB.y * rB.x * iB; + K.cx.y = K.cy.x; + K.cy.y = mA + mB + rA.x * rA.x * iA + rB.x * rB.x * iB; + b2Vec2 b = b2Solve22( K, b2Add( Cdot, bias ) ); + + b2Vec2 impulse; + impulse.x = -massScale * b.x - impulseScale * joint->linearImpulse.x; + impulse.y = -massScale * b.y - impulseScale * joint->linearImpulse.y; + joint->linearImpulse.x += impulse.x; + joint->linearImpulse.y += impulse.y; + + vA = b2MulSub( vA, mA, impulse ); + wA -= iA * b2Cross( rA, impulse ); + vB = b2MulAdd( vB, mB, impulse ); + wB += iB * b2Cross( rB, impulse ); + } + + stateA->linearVelocity = vA; + stateA->angularVelocity = wA; + stateB->linearVelocity = vB; + stateB->angularVelocity = wB; +} + +#if 0 +void b2RevoluteJoint::Dump() +{ + int32 indexA = joint->bodyA->joint->islandIndex; + int32 indexB = joint->bodyB->joint->islandIndex; + + b2Dump(" b2RevoluteJointDef jd;\n"); + b2Dump(" jd.bodyA = bodies[%d];\n", indexA); + b2Dump(" jd.bodyB = bodies[%d];\n", indexB); + b2Dump(" jd.collideConnected = bool(%d);\n", joint->collideConnected); + b2Dump(" jd.localAnchorA.Set(%.9g, %.9g);\n", joint->localAnchorA.x, joint->localAnchorA.y); + b2Dump(" jd.localAnchorB.Set(%.9g, %.9g);\n", joint->localAnchorB.x, joint->localAnchorB.y); + b2Dump(" jd.referenceAngle = %.9g;\n", joint->referenceAngle); + b2Dump(" jd.enableLimit = bool(%d);\n", joint->enableLimit); + b2Dump(" jd.lowerAngle = %.9g;\n", joint->lowerAngle); + b2Dump(" jd.upperAngle = %.9g;\n", joint->upperAngle); + b2Dump(" jd.enableMotor = bool(%d);\n", joint->enableMotor); + b2Dump(" jd.motorSpeed = %.9g;\n", joint->motorSpeed); + b2Dump(" jd.maxMotorTorque = %.9g;\n", joint->maxMotorTorque); + b2Dump(" joints[%d] = joint->world->CreateJoint(&jd);\n", joint->index); +} +#endif + +void b2DrawRevoluteJoint( b2DebugDraw* draw, b2JointSim* base, b2Transform transformA, b2Transform transformB, float drawSize ) +{ + B2_ASSERT( base->type == b2_revoluteJoint ); + + b2RevoluteJoint* joint = &base->revoluteJoint; + + b2Vec2 pA = b2TransformPoint( transformA, base->localOriginAnchorA ); + b2Vec2 pB = b2TransformPoint( transformB, base->localOriginAnchorB ); + + b2HexColor c1 = b2_colorGray7; + b2HexColor c2 = b2_colorGreen; + b2HexColor c3 = b2_colorRed; + + const float L = drawSize; + // draw->DrawPoint(pA, 3.0f, b2_colorGray40, draw->context); + // draw->DrawPoint(pB, 3.0f, b2_colorLightBlue, draw->context); + draw->DrawCircle( pB, L, c1, draw->context ); + + float angle = b2RelativeAngle( transformB.q, transformA.q ); + + b2Rot rot = b2MakeRot( angle ); + b2Vec2 r = { L * rot.c, L * rot.s }; + b2Vec2 pC = b2Add( pB, r ); + draw->DrawSegment( pB, pC, c1, draw->context ); + + if ( draw->drawJointExtras ) + { + float jointAngle = b2UnwindAngle( angle - joint->referenceAngle ); + char buffer[32]; + snprintf( buffer, 32, " %.1f deg", 180.0f * jointAngle / b2_pi ); + draw->DrawString( pC, buffer, draw->context ); + } + + float lowerAngle = joint->lowerAngle + joint->referenceAngle; + float upperAngle = joint->upperAngle + joint->referenceAngle; + + if ( joint->enableLimit ) + { + b2Rot rotLo = b2MakeRot( lowerAngle ); + b2Vec2 rlo = { L * rotLo.c, L * rotLo.s }; + + b2Rot rotHi = b2MakeRot( upperAngle ); + b2Vec2 rhi = { L * rotHi.c, L * rotHi.s }; + + draw->DrawSegment( pB, b2Add( pB, rlo ), c2, draw->context ); + draw->DrawSegment( pB, b2Add( pB, rhi ), c3, draw->context ); + + b2Rot rotRef = b2MakeRot( joint->referenceAngle ); + b2Vec2 ref = ( b2Vec2 ){ L * rotRef.c, L * rotRef.s }; + draw->DrawSegment( pB, b2Add( pB, ref ), b2_colorBlue, draw->context ); + } + + b2HexColor color = b2_colorGold; + draw->DrawSegment( transformA.p, pA, color, draw->context ); + draw->DrawSegment( pA, pB, color, draw->context ); + draw->DrawSegment( transformB.p, pB, color, draw->context ); + + // char buffer[32]; + // sprintf(buffer, "%.1f", b2Length(joint->impulse)); + // draw->DrawString(pA, buffer, draw->context); +} diff --git a/3rdparty/box2d/src/rope/b2_rope.cpp b/3rdparty/box2d/src/rope/b2_rope.cpp deleted file mode 100644 index 71fbe79ee9f7..000000000000 --- a/3rdparty/box2d/src/rope/b2_rope.cpp +++ /dev/null @@ -1,809 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "box2d/b2_draw.h" -#include "box2d/b2_rope.h" - -#include - -struct b2RopeStretch -{ - int32 i1, i2; - float invMass1, invMass2; - float L; - float lambda; - float spring; - float damper; -}; - -struct b2RopeBend -{ - int32 i1, i2, i3; - float invMass1, invMass2, invMass3; - float invEffectiveMass; - float lambda; - float L1, L2; - float alpha1, alpha2; - float spring; - float damper; -}; - -b2Rope::b2Rope() -{ - m_position.SetZero(); - m_count = 0; - m_stretchCount = 0; - m_bendCount = 0; - m_stretchConstraints = nullptr; - m_bendConstraints = nullptr; - m_bindPositions = nullptr; - m_ps = nullptr; - m_p0s = nullptr; - m_vs = nullptr; - m_invMasses = nullptr; - m_gravity.SetZero(); -} - -b2Rope::~b2Rope() -{ - b2Free(m_stretchConstraints); - b2Free(m_bendConstraints); - b2Free(m_bindPositions); - b2Free(m_ps); - b2Free(m_p0s); - b2Free(m_vs); - b2Free(m_invMasses); -} - -void b2Rope::Create(const b2RopeDef& def) -{ - b2Assert(def.count >= 3); - m_position = def.position; - m_count = def.count; - m_bindPositions = (b2Vec2*)b2Alloc(m_count * sizeof(b2Vec2)); - m_ps = (b2Vec2*)b2Alloc(m_count * sizeof(b2Vec2)); - m_p0s = (b2Vec2*)b2Alloc(m_count * sizeof(b2Vec2)); - m_vs = (b2Vec2*)b2Alloc(m_count * sizeof(b2Vec2)); - m_invMasses = (float*)b2Alloc(m_count * sizeof(float)); - - for (int32 i = 0; i < m_count; ++i) - { - m_bindPositions[i] = def.vertices[i]; - m_ps[i] = def.vertices[i] + m_position; - m_p0s[i] = def.vertices[i] + m_position; - m_vs[i].SetZero(); - - float m = def.masses[i]; - if (m > 0.0f) - { - m_invMasses[i] = 1.0f / m; - } - else - { - m_invMasses[i] = 0.0f; - } - } - - m_stretchCount = m_count - 1; - m_bendCount = m_count - 2; - - m_stretchConstraints = (b2RopeStretch*)b2Alloc(m_stretchCount * sizeof(b2RopeStretch)); - m_bendConstraints = (b2RopeBend*)b2Alloc(m_bendCount * sizeof(b2RopeBend)); - - for (int32 i = 0; i < m_stretchCount; ++i) - { - b2RopeStretch& c = m_stretchConstraints[i]; - - b2Vec2 p1 = m_ps[i]; - b2Vec2 p2 = m_ps[i+1]; - - c.i1 = i; - c.i2 = i + 1; - c.L = b2Distance(p1, p2); - c.invMass1 = m_invMasses[i]; - c.invMass2 = m_invMasses[i + 1]; - c.lambda = 0.0f; - c.damper = 0.0f; - c.spring = 0.0f; - } - - for (int32 i = 0; i < m_bendCount; ++i) - { - b2RopeBend& c = m_bendConstraints[i]; - - b2Vec2 p1 = m_ps[i]; - b2Vec2 p2 = m_ps[i + 1]; - b2Vec2 p3 = m_ps[i + 2]; - - c.i1 = i; - c.i2 = i + 1; - c.i3 = i + 2; - c.invMass1 = m_invMasses[i]; - c.invMass2 = m_invMasses[i + 1]; - c.invMass3 = m_invMasses[i + 2]; - c.invEffectiveMass = 0.0f; - c.L1 = b2Distance(p1, p2); - c.L2 = b2Distance(p2, p3); - c.lambda = 0.0f; - - // Pre-compute effective mass (TODO use flattened config) - b2Vec2 e1 = p2 - p1; - b2Vec2 e2 = p3 - p2; - float L1sqr = e1.LengthSquared(); - float L2sqr = e2.LengthSquared(); - - if (L1sqr * L2sqr == 0.0f) - { - continue; - } - - b2Vec2 Jd1 = (-1.0f / L1sqr) * e1.Skew(); - b2Vec2 Jd2 = (1.0f / L2sqr) * e2.Skew(); - - b2Vec2 J1 = -Jd1; - b2Vec2 J2 = Jd1 - Jd2; - b2Vec2 J3 = Jd2; - - c.invEffectiveMass = c.invMass1 * b2Dot(J1, J1) + c.invMass2 * b2Dot(J2, J2) + c.invMass3 * b2Dot(J3, J3); - - b2Vec2 r = p3 - p1; - - float rr = r.LengthSquared(); - if (rr == 0.0f) - { - continue; - } - - // a1 = h2 / (h1 + h2) - // a2 = h1 / (h1 + h2) - c.alpha1 = b2Dot(e2, r) / rr; - c.alpha2 = b2Dot(e1, r) / rr; - } - - m_gravity = def.gravity; - - SetTuning(def.tuning); -} - -void b2Rope::SetTuning(const b2RopeTuning& tuning) -{ - m_tuning = tuning; - - // Pre-compute spring and damper values based on tuning - - const float bendOmega = 2.0f * b2_pi * m_tuning.bendHertz; - - for (int32 i = 0; i < m_bendCount; ++i) - { - b2RopeBend& c = m_bendConstraints[i]; - - float L1sqr = c.L1 * c.L1; - float L2sqr = c.L2 * c.L2; - - if (L1sqr * L2sqr == 0.0f) - { - c.spring = 0.0f; - c.damper = 0.0f; - continue; - } - - // Flatten the triangle formed by the two edges - float J2 = 1.0f / c.L1 + 1.0f / c.L2; - float sum = c.invMass1 / L1sqr + c.invMass2 * J2 * J2 + c.invMass3 / L2sqr; - if (sum == 0.0f) - { - c.spring = 0.0f; - c.damper = 0.0f; - continue; - } - - float mass = 1.0f / sum; - - c.spring = mass * bendOmega * bendOmega; - c.damper = 2.0f * mass * m_tuning.bendDamping * bendOmega; - } - - const float stretchOmega = 2.0f * b2_pi * m_tuning.stretchHertz; - - for (int32 i = 0; i < m_stretchCount; ++i) - { - b2RopeStretch& c = m_stretchConstraints[i]; - - float sum = c.invMass1 + c.invMass2; - if (sum == 0.0f) - { - continue; - } - - float mass = 1.0f / sum; - - c.spring = mass * stretchOmega * stretchOmega; - c.damper = 2.0f * mass * m_tuning.stretchDamping * stretchOmega; - } -} - -void b2Rope::Step(float dt, int32 iterations, const b2Vec2& position) -{ - if (dt == 0.0f) - { - return; - } - - const float inv_dt = 1.0f / dt; - float d = expf(- dt * m_tuning.damping); - - // Apply gravity and damping - for (int32 i = 0; i < m_count; ++i) - { - if (m_invMasses[i] > 0.0f) - { - m_vs[i] *= d; - m_vs[i] += dt * m_gravity; - } - else - { - m_vs[i] = inv_dt * (m_bindPositions[i] + position - m_p0s[i]); - } - } - - // Apply bending spring - if (m_tuning.bendingModel == b2_springAngleBendingModel) - { - ApplyBendForces(dt); - } - - for (int32 i = 0; i < m_bendCount; ++i) - { - m_bendConstraints[i].lambda = 0.0f; - } - - for (int32 i = 0; i < m_stretchCount; ++i) - { - m_stretchConstraints[i].lambda = 0.0f; - } - - // Update position - for (int32 i = 0; i < m_count; ++i) - { - m_ps[i] += dt * m_vs[i]; - } - - // Solve constraints - for (int32 i = 0; i < iterations; ++i) - { - if (m_tuning.bendingModel == b2_pbdAngleBendingModel) - { - SolveBend_PBD_Angle(); - } - else if (m_tuning.bendingModel == b2_xpbdAngleBendingModel) - { - SolveBend_XPBD_Angle(dt); - } - else if (m_tuning.bendingModel == b2_pbdDistanceBendingModel) - { - SolveBend_PBD_Distance(); - } - else if (m_tuning.bendingModel == b2_pbdHeightBendingModel) - { - SolveBend_PBD_Height(); - } - else if (m_tuning.bendingModel == b2_pbdTriangleBendingModel) - { - SolveBend_PBD_Triangle(); - } - - if (m_tuning.stretchingModel == b2_pbdStretchingModel) - { - SolveStretch_PBD(); - } - else if (m_tuning.stretchingModel == b2_xpbdStretchingModel) - { - SolveStretch_XPBD(dt); - } - } - - // Constrain velocity - for (int32 i = 0; i < m_count; ++i) - { - m_vs[i] = inv_dt * (m_ps[i] - m_p0s[i]); - m_p0s[i] = m_ps[i]; - } -} - -void b2Rope::Reset(const b2Vec2& position) -{ - m_position = position; - - for (int32 i = 0; i < m_count; ++i) - { - m_ps[i] = m_bindPositions[i] + m_position; - m_p0s[i] = m_bindPositions[i] + m_position; - m_vs[i].SetZero(); - } - - for (int32 i = 0; i < m_bendCount; ++i) - { - m_bendConstraints[i].lambda = 0.0f; - } - - for (int32 i = 0; i < m_stretchCount; ++i) - { - m_stretchConstraints[i].lambda = 0.0f; - } -} - -void b2Rope::SolveStretch_PBD() -{ - const float stiffness = m_tuning.stretchStiffness; - - for (int32 i = 0; i < m_stretchCount; ++i) - { - const b2RopeStretch& c = m_stretchConstraints[i]; - - b2Vec2 p1 = m_ps[c.i1]; - b2Vec2 p2 = m_ps[c.i2]; - - b2Vec2 d = p2 - p1; - float L = d.Normalize(); - - float sum = c.invMass1 + c.invMass2; - if (sum == 0.0f) - { - continue; - } - - float s1 = c.invMass1 / sum; - float s2 = c.invMass2 / sum; - - p1 -= stiffness * s1 * (c.L - L) * d; - p2 += stiffness * s2 * (c.L - L) * d; - - m_ps[c.i1] = p1; - m_ps[c.i2] = p2; - } -} - -void b2Rope::SolveStretch_XPBD(float dt) -{ - b2Assert(dt > 0.0f); - - for (int32 i = 0; i < m_stretchCount; ++i) - { - b2RopeStretch& c = m_stretchConstraints[i]; - - b2Vec2 p1 = m_ps[c.i1]; - b2Vec2 p2 = m_ps[c.i2]; - - b2Vec2 dp1 = p1 - m_p0s[c.i1]; - b2Vec2 dp2 = p2 - m_p0s[c.i2]; - - b2Vec2 u = p2 - p1; - float L = u.Normalize(); - - b2Vec2 J1 = -u; - b2Vec2 J2 = u; - - float sum = c.invMass1 + c.invMass2; - if (sum == 0.0f) - { - continue; - } - - const float alpha = 1.0f / (c.spring * dt * dt); // 1 / kg - const float beta = dt * dt * c.damper; // kg * s - const float sigma = alpha * beta / dt; // non-dimensional - float C = L - c.L; - - // This is using the initial velocities - float Cdot = b2Dot(J1, dp1) + b2Dot(J2, dp2); - - float B = C + alpha * c.lambda + sigma * Cdot; - float sum2 = (1.0f + sigma) * sum + alpha; - - float impulse = -B / sum2; - - p1 += (c.invMass1 * impulse) * J1; - p2 += (c.invMass2 * impulse) * J2; - - m_ps[c.i1] = p1; - m_ps[c.i2] = p2; - c.lambda += impulse; - } -} - -void b2Rope::SolveBend_PBD_Angle() -{ - const float stiffness = m_tuning.bendStiffness; - - for (int32 i = 0; i < m_bendCount; ++i) - { - const b2RopeBend& c = m_bendConstraints[i]; - - b2Vec2 p1 = m_ps[c.i1]; - b2Vec2 p2 = m_ps[c.i2]; - b2Vec2 p3 = m_ps[c.i3]; - - b2Vec2 d1 = p2 - p1; - b2Vec2 d2 = p3 - p2; - float a = b2Cross(d1, d2); - float b = b2Dot(d1, d2); - - float angle = b2Atan2(a, b); - - float L1sqr, L2sqr; - - if (m_tuning.isometric) - { - L1sqr = c.L1 * c.L1; - L2sqr = c.L2 * c.L2; - } - else - { - L1sqr = d1.LengthSquared(); - L2sqr = d2.LengthSquared(); - } - - if (L1sqr * L2sqr == 0.0f) - { - continue; - } - - b2Vec2 Jd1 = (-1.0f / L1sqr) * d1.Skew(); - b2Vec2 Jd2 = (1.0f / L2sqr) * d2.Skew(); - - b2Vec2 J1 = -Jd1; - b2Vec2 J2 = Jd1 - Jd2; - b2Vec2 J3 = Jd2; - - float sum; - if (m_tuning.fixedEffectiveMass) - { - sum = c.invEffectiveMass; - } - else - { - sum = c.invMass1 * b2Dot(J1, J1) + c.invMass2 * b2Dot(J2, J2) + c.invMass3 * b2Dot(J3, J3); - } - - if (sum == 0.0f) - { - sum = c.invEffectiveMass; - } - - float impulse = -stiffness * angle / sum; - - p1 += (c.invMass1 * impulse) * J1; - p2 += (c.invMass2 * impulse) * J2; - p3 += (c.invMass3 * impulse) * J3; - - m_ps[c.i1] = p1; - m_ps[c.i2] = p2; - m_ps[c.i3] = p3; - } -} - -void b2Rope::SolveBend_XPBD_Angle(float dt) -{ - b2Assert(dt > 0.0f); - - for (int32 i = 0; i < m_bendCount; ++i) - { - b2RopeBend& c = m_bendConstraints[i]; - - b2Vec2 p1 = m_ps[c.i1]; - b2Vec2 p2 = m_ps[c.i2]; - b2Vec2 p3 = m_ps[c.i3]; - - b2Vec2 dp1 = p1 - m_p0s[c.i1]; - b2Vec2 dp2 = p2 - m_p0s[c.i2]; - b2Vec2 dp3 = p3 - m_p0s[c.i3]; - - b2Vec2 d1 = p2 - p1; - b2Vec2 d2 = p3 - p2; - - float L1sqr, L2sqr; - - if (m_tuning.isometric) - { - L1sqr = c.L1 * c.L1; - L2sqr = c.L2 * c.L2; - } - else - { - L1sqr = d1.LengthSquared(); - L2sqr = d2.LengthSquared(); - } - - if (L1sqr * L2sqr == 0.0f) - { - continue; - } - - float a = b2Cross(d1, d2); - float b = b2Dot(d1, d2); - - float angle = b2Atan2(a, b); - - b2Vec2 Jd1 = (-1.0f / L1sqr) * d1.Skew(); - b2Vec2 Jd2 = (1.0f / L2sqr) * d2.Skew(); - - b2Vec2 J1 = -Jd1; - b2Vec2 J2 = Jd1 - Jd2; - b2Vec2 J3 = Jd2; - - float sum; - if (m_tuning.fixedEffectiveMass) - { - sum = c.invEffectiveMass; - } - else - { - sum = c.invMass1 * b2Dot(J1, J1) + c.invMass2 * b2Dot(J2, J2) + c.invMass3 * b2Dot(J3, J3); - } - - if (sum == 0.0f) - { - continue; - } - - const float alpha = 1.0f / (c.spring * dt * dt); - const float beta = dt * dt * c.damper; - const float sigma = alpha * beta / dt; - float C = angle; - - // This is using the initial velocities - float Cdot = b2Dot(J1, dp1) + b2Dot(J2, dp2) + b2Dot(J3, dp3); - - float B = C + alpha * c.lambda + sigma * Cdot; - float sum2 = (1.0f + sigma) * sum + alpha; - - float impulse = -B / sum2; - - p1 += (c.invMass1 * impulse) * J1; - p2 += (c.invMass2 * impulse) * J2; - p3 += (c.invMass3 * impulse) * J3; - - m_ps[c.i1] = p1; - m_ps[c.i2] = p2; - m_ps[c.i3] = p3; - c.lambda += impulse; - } -} - -void b2Rope::ApplyBendForces(float dt) -{ - // omega = 2 * pi * hz - const float omega = 2.0f * b2_pi * m_tuning.bendHertz; - - for (int32 i = 0; i < m_bendCount; ++i) - { - const b2RopeBend& c = m_bendConstraints[i]; - - b2Vec2 p1 = m_ps[c.i1]; - b2Vec2 p2 = m_ps[c.i2]; - b2Vec2 p3 = m_ps[c.i3]; - - b2Vec2 v1 = m_vs[c.i1]; - b2Vec2 v2 = m_vs[c.i2]; - b2Vec2 v3 = m_vs[c.i3]; - - b2Vec2 d1 = p2 - p1; - b2Vec2 d2 = p3 - p2; - - float L1sqr, L2sqr; - - if (m_tuning.isometric) - { - L1sqr = c.L1 * c.L1; - L2sqr = c.L2 * c.L2; - } - else - { - L1sqr = d1.LengthSquared(); - L2sqr = d2.LengthSquared(); - } - - if (L1sqr * L2sqr == 0.0f) - { - continue; - } - - float a = b2Cross(d1, d2); - float b = b2Dot(d1, d2); - - float angle = b2Atan2(a, b); - - b2Vec2 Jd1 = (-1.0f / L1sqr) * d1.Skew(); - b2Vec2 Jd2 = (1.0f / L2sqr) * d2.Skew(); - - b2Vec2 J1 = -Jd1; - b2Vec2 J2 = Jd1 - Jd2; - b2Vec2 J3 = Jd2; - - float sum; - if (m_tuning.fixedEffectiveMass) - { - sum = c.invEffectiveMass; - } - else - { - sum = c.invMass1 * b2Dot(J1, J1) + c.invMass2 * b2Dot(J2, J2) + c.invMass3 * b2Dot(J3, J3); - } - - if (sum == 0.0f) - { - continue; - } - - float mass = 1.0f / sum; - - const float spring = mass * omega * omega; - const float damper = 2.0f * mass * m_tuning.bendDamping * omega; - - float C = angle; - float Cdot = b2Dot(J1, v1) + b2Dot(J2, v2) + b2Dot(J3, v3); - - float impulse = -dt * (spring * C + damper * Cdot); - - m_vs[c.i1] += (c.invMass1 * impulse) * J1; - m_vs[c.i2] += (c.invMass2 * impulse) * J2; - m_vs[c.i3] += (c.invMass3 * impulse) * J3; - } -} - -void b2Rope::SolveBend_PBD_Distance() -{ - const float stiffness = m_tuning.bendStiffness; - - for (int32 i = 0; i < m_bendCount; ++i) - { - const b2RopeBend& c = m_bendConstraints[i]; - - int32 i1 = c.i1; - int32 i2 = c.i3; - - b2Vec2 p1 = m_ps[i1]; - b2Vec2 p2 = m_ps[i2]; - - b2Vec2 d = p2 - p1; - float L = d.Normalize(); - - float sum = c.invMass1 + c.invMass3; - if (sum == 0.0f) - { - continue; - } - - float s1 = c.invMass1 / sum; - float s2 = c.invMass3 / sum; - - p1 -= stiffness * s1 * (c.L1 + c.L2 - L) * d; - p2 += stiffness * s2 * (c.L1 + c.L2 - L) * d; - - m_ps[i1] = p1; - m_ps[i2] = p2; - } -} - -// Constraint based implementation of: -// P. Volino: Simple Linear Bending Stiffness in Particle Systems -void b2Rope::SolveBend_PBD_Height() -{ - const float stiffness = m_tuning.bendStiffness; - - for (int32 i = 0; i < m_bendCount; ++i) - { - const b2RopeBend& c = m_bendConstraints[i]; - - b2Vec2 p1 = m_ps[c.i1]; - b2Vec2 p2 = m_ps[c.i2]; - b2Vec2 p3 = m_ps[c.i3]; - - // Barycentric coordinates are held constant - b2Vec2 d = c.alpha1 * p1 + c.alpha2 * p3 - p2; - float dLen = d.Length(); - - if (dLen == 0.0f) - { - continue; - } - - b2Vec2 dHat = (1.0f / dLen) * d; - - b2Vec2 J1 = c.alpha1 * dHat; - b2Vec2 J2 = -dHat; - b2Vec2 J3 = c.alpha2 * dHat; - - float sum = c.invMass1 * c.alpha1 * c.alpha1 + c.invMass2 + c.invMass3 * c.alpha2 * c.alpha2; - - if (sum == 0.0f) - { - continue; - } - - float C = dLen; - float mass = 1.0f / sum; - float impulse = -stiffness * mass * C; - - p1 += (c.invMass1 * impulse) * J1; - p2 += (c.invMass2 * impulse) * J2; - p3 += (c.invMass3 * impulse) * J3; - - m_ps[c.i1] = p1; - m_ps[c.i2] = p2; - m_ps[c.i3] = p3; - } -} - -// M. Kelager: A Triangle Bending Constraint Model for PBD -void b2Rope::SolveBend_PBD_Triangle() -{ - const float stiffness = m_tuning.bendStiffness; - - for (int32 i = 0; i < m_bendCount; ++i) - { - const b2RopeBend& c = m_bendConstraints[i]; - - b2Vec2 b0 = m_ps[c.i1]; - b2Vec2 v = m_ps[c.i2]; - b2Vec2 b1 = m_ps[c.i3]; - - float wb0 = c.invMass1; - float wv = c.invMass2; - float wb1 = c.invMass3; - - float W = wb0 + wb1 + 2.0f * wv; - float invW = stiffness / W; - - b2Vec2 d = v - (1.0f / 3.0f) * (b0 + v + b1); - - b2Vec2 db0 = 2.0f * wb0 * invW * d; - b2Vec2 dv = -4.0f * wv * invW * d; - b2Vec2 db1 = 2.0f * wb1 * invW * d; - - b0 += db0; - v += dv; - b1 += db1; - - m_ps[c.i1] = b0; - m_ps[c.i2] = v; - m_ps[c.i3] = b1; - } -} - -void b2Rope::Draw(b2Draw* draw) const -{ - b2Color c(0.4f, 0.5f, 0.7f); - b2Color pg(0.1f, 0.8f, 0.1f); - b2Color pd(0.7f, 0.2f, 0.4f); - - for (int32 i = 0; i < m_count - 1; ++i) - { - draw->DrawSegment(m_ps[i], m_ps[i+1], c); - - const b2Color& pc = m_invMasses[i] > 0.0f ? pd : pg; - draw->DrawPoint(m_ps[i], 5.0f, pc); - } - - const b2Color& pc = m_invMasses[m_count - 1] > 0.0f ? pd : pg; - draw->DrawPoint(m_ps[m_count - 1], 5.0f, pc); -} diff --git a/3rdparty/box2d/src/shape.c b/3rdparty/box2d/src/shape.c new file mode 100644 index 000000000000..9d4cb8bace01 --- /dev/null +++ b/3rdparty/box2d/src/shape.c @@ -0,0 +1,1370 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#include "shape.h" + +#include "body.h" +#include "broad_phase.h" +#include "contact.h" +#include "world.h" + +// needed for dll export +#include "box2d/box2d.h" + +#include + +B2_ARRAY_SOURCE( b2ChainShape, b2ChainShape ); +B2_ARRAY_SOURCE( b2Shape, b2Shape ); + +static b2Shape* b2GetShape( b2World* world, b2ShapeId shapeId ) +{ + int id = shapeId.index1 - 1; + b2Shape* shape = b2ShapeArray_Get( &world->shapes, id ); + B2_ASSERT( shape->id == id && shape->revision == shapeId.revision ); + return shape; +} + +static b2ChainShape* b2GetChainShape( b2World* world, b2ChainId chainId ) +{ + int id = chainId.index1 - 1; + b2ChainShape* chain = b2ChainShapeArray_Get( &world->chainShapes, id ); + B2_ASSERT( chain->id == id && chain->revision == chainId.revision ); + return chain; +} + +static void b2UpdateShapeAABBs( b2Shape* shape, b2Transform transform, b2BodyType proxyType ) +{ + // Compute a bounding box with a speculative margin + const float speculativeDistance = b2_speculativeDistance; + const float aabbMargin = b2_aabbMargin; + + b2AABB aabb = b2ComputeShapeAABB( shape, transform ); + aabb.lowerBound.x -= speculativeDistance; + aabb.lowerBound.y -= speculativeDistance; + aabb.upperBound.x += speculativeDistance; + aabb.upperBound.y += speculativeDistance; + shape->aabb = aabb; + + // Smaller margin for static bodies. Cannot be zero due to TOI tolerance. + float margin = proxyType == b2_staticBody ? speculativeDistance : aabbMargin; + b2AABB fatAABB; + fatAABB.lowerBound.x = aabb.lowerBound.x - margin; + fatAABB.lowerBound.y = aabb.lowerBound.y - margin; + fatAABB.upperBound.x = aabb.upperBound.x + margin; + fatAABB.upperBound.y = aabb.upperBound.y + margin; + shape->fatAABB = fatAABB; +} + +static b2Shape* b2CreateShapeInternal( b2World* world, b2Body* body, b2Transform transform, const b2ShapeDef* def, + const void* geometry, b2ShapeType shapeType ) +{ + B2_ASSERT( b2IsValid( def->density ) && def->density >= 0.0f ); + B2_ASSERT( b2IsValid( def->friction ) && def->friction >= 0.0f ); + B2_ASSERT( b2IsValid( def->restitution ) && def->restitution >= 0.0f ); + + int shapeId = b2AllocId( &world->shapeIdPool ); + + if ( shapeId == world->shapes.count ) + { + b2ShapeArray_Push( &world->shapes, ( b2Shape ){ 0 } ); + } + else + { + B2_ASSERT( world->shapes.data[shapeId].id == B2_NULL_INDEX ); + } + + b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId ); + + switch ( shapeType ) + { + case b2_capsuleShape: + shape->capsule = *(const b2Capsule*)geometry; + break; + + case b2_circleShape: + shape->circle = *(const b2Circle*)geometry; + break; + + case b2_polygonShape: + shape->polygon = *(const b2Polygon*)geometry; + break; + + case b2_segmentShape: + shape->segment = *(const b2Segment*)geometry; + break; + + case b2_chainSegmentShape: + shape->chainSegment = *(const b2ChainSegment*)geometry; + break; + + default: + B2_ASSERT( false ); + break; + } + + shape->id = shapeId; + shape->bodyId = body->id; + shape->type = shapeType; + shape->density = def->density; + shape->friction = def->friction; + shape->restitution = def->restitution; + shape->filter = def->filter; + shape->userData = def->userData; + shape->customColor = def->customColor; + shape->isSensor = def->isSensor; + shape->enlargedAABB = false; + shape->enableSensorEvents = def->enableSensorEvents; + shape->enableContactEvents = def->enableContactEvents; + shape->enableHitEvents = def->enableHitEvents; + shape->enablePreSolveEvents = def->enablePreSolveEvents; + shape->isFast = false; + shape->proxyKey = B2_NULL_INDEX; + shape->localCentroid = b2GetShapeCentroid( shape ); + shape->aabb = ( b2AABB ){ b2Vec2_zero, b2Vec2_zero }; + shape->fatAABB = ( b2AABB ){ b2Vec2_zero, b2Vec2_zero }; + shape->revision += 1; + + if ( body->setIndex != b2_disabledSet ) + { + b2BodyType proxyType = body->type; + b2CreateShapeProxy( shape, &world->broadPhase, proxyType, transform, def->forceContactCreation || def->isSensor ); + } + + // Add to shape doubly linked list + if ( body->headShapeId != B2_NULL_INDEX ) + { + b2Shape* headShape = b2ShapeArray_Get( &world->shapes, body->headShapeId ); + headShape->prevShapeId = shapeId; + } + + shape->prevShapeId = B2_NULL_INDEX; + shape->nextShapeId = body->headShapeId; + body->headShapeId = shapeId; + body->shapeCount += 1; + + b2ValidateSolverSets( world ); + + return shape; +} + +b2ShapeId b2CreateShape( b2BodyId bodyId, const b2ShapeDef* def, const void* geometry, b2ShapeType shapeType ) +{ + b2CheckDef( def ); + B2_ASSERT( b2IsValid( def->density ) && def->density >= 0.0f ); + B2_ASSERT( b2IsValid( def->friction ) && def->friction >= 0.0f ); + B2_ASSERT( b2IsValid( def->restitution ) && def->restitution >= 0.0f ); + + b2World* world = b2GetWorldLocked( bodyId.world0 ); + if ( world == NULL ) + { + return ( b2ShapeId ){ 0 }; + } + + b2Body* body = b2GetBodyFullId( world, bodyId ); + b2Transform transform = b2GetBodyTransformQuick( world, body ); + + b2Shape* shape = b2CreateShapeInternal( world, body, transform, def, geometry, shapeType ); + + if ( body->automaticMass == true ) + { + b2UpdateBodyMassData( world, body ); + } + + b2ValidateSolverSets( world ); + + b2ShapeId id = { shape->id + 1, bodyId.world0, shape->revision }; + return id; +} + +b2ShapeId b2CreateCircleShape( b2BodyId bodyId, const b2ShapeDef* def, const b2Circle* circle ) +{ + return b2CreateShape( bodyId, def, circle, b2_circleShape ); +} + +b2ShapeId b2CreateCapsuleShape( b2BodyId bodyId, const b2ShapeDef* def, const b2Capsule* capsule ) +{ + float lengthSqr = b2DistanceSquared( capsule->center1, capsule->center2 ); + if ( lengthSqr <= b2_linearSlop * b2_linearSlop ) + { + b2Circle circle = { b2Lerp( capsule->center1, capsule->center2, 0.5f ), capsule->radius }; + return b2CreateShape( bodyId, def, &circle, b2_circleShape ); + } + + return b2CreateShape( bodyId, def, capsule, b2_capsuleShape ); +} + +b2ShapeId b2CreatePolygonShape( b2BodyId bodyId, const b2ShapeDef* def, const b2Polygon* polygon ) +{ + B2_ASSERT( b2IsValid( polygon->radius ) && polygon->radius >= 0.0f ); + return b2CreateShape( bodyId, def, polygon, b2_polygonShape ); +} + +b2ShapeId b2CreateSegmentShape( b2BodyId bodyId, const b2ShapeDef* def, const b2Segment* segment ) +{ + float lengthSqr = b2DistanceSquared( segment->point1, segment->point2 ); + if ( lengthSqr <= b2_linearSlop * b2_linearSlop ) + { + B2_ASSERT( false ); + return b2_nullShapeId; + } + + return b2CreateShape( bodyId, def, segment, b2_segmentShape ); +} + +// Destroy a shape on a body. This doesn't need to be called when destroying a body. +void b2DestroyShapeInternal( b2World* world, b2Shape* shape, b2Body* body, bool wakeBodies ) +{ + int shapeId = shape->id; + + // Remove the shape from the body's doubly linked list. + if ( shape->prevShapeId != B2_NULL_INDEX ) + { + b2Shape* prevShape = b2ShapeArray_Get( &world->shapes, shape->prevShapeId ); + prevShape->nextShapeId = shape->nextShapeId; + } + + if ( shape->nextShapeId != B2_NULL_INDEX ) + { + b2Shape* nextShape = b2ShapeArray_Get( &world->shapes, shape->nextShapeId ); + nextShape->prevShapeId = shape->prevShapeId; + } + + if ( shapeId == body->headShapeId ) + { + body->headShapeId = shape->nextShapeId; + } + + body->shapeCount -= 1; + + // Remove from broad-phase. + b2DestroyShapeProxy( shape, &world->broadPhase ); + + // Destroy any contacts associated with the shape. + int contactKey = body->headContactKey; + while ( contactKey != B2_NULL_INDEX ) + { + int contactId = contactKey >> 1; + int edgeIndex = contactKey & 1; + + b2Contact* contact = b2ContactArray_Get( &world->contacts, contactId ); + contactKey = contact->edges[edgeIndex].nextKey; + + if ( contact->shapeIdA == shapeId || contact->shapeIdB == shapeId ) + { + b2DestroyContact( world, contact, wakeBodies ); + } + } + + // Return shape to free list. + b2FreeId( &world->shapeIdPool, shapeId ); + shape->id = B2_NULL_INDEX; + + b2ValidateSolverSets( world ); +} + +void b2DestroyShape( b2ShapeId shapeId ) +{ + b2World* world = b2GetWorldLocked( shapeId.world0 ); + + b2Shape* shape = b2GetShape( world, shapeId ); + + // need to wake bodies because this might be a static body + bool wakeBodies = true; + + b2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId ); + b2DestroyShapeInternal( world, shape, body, wakeBodies ); + + if ( body->automaticMass == true ) + { + b2UpdateBodyMassData( world, body ); + } +} + +b2ChainId b2CreateChain( b2BodyId bodyId, const b2ChainDef* def ) +{ + b2CheckDef( def ); + B2_ASSERT( b2IsValid( def->friction ) && def->friction >= 0.0f ); + B2_ASSERT( b2IsValid( def->restitution ) && def->restitution >= 0.0f ); + B2_ASSERT( def->count >= 4 ); + + b2World* world = b2GetWorldLocked( bodyId.world0 ); + if ( world == NULL ) + { + return ( b2ChainId ){ 0 }; + } + + b2Body* body = b2GetBodyFullId( world, bodyId ); + b2Transform transform = b2GetBodyTransformQuick( world, body ); + + int chainId = b2AllocId( &world->chainIdPool ); + + if ( chainId == world->chainShapes.count ) + { + b2ChainShapeArray_Push( &world->chainShapes, ( b2ChainShape ){ 0 } ); + } + else + { + B2_ASSERT( world->chainShapes.data[chainId].id == B2_NULL_INDEX ); + } + + b2ChainShape* chainShape = b2ChainShapeArray_Get( &world->chainShapes, chainId ); + + chainShape->id = chainId; + chainShape->bodyId = body->id; + chainShape->nextChainId = body->headChainId; + chainShape->revision += 1; + body->headChainId = chainId; + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.userData = def->userData; + shapeDef.restitution = def->restitution; + shapeDef.friction = def->friction; + shapeDef.filter = def->filter; + shapeDef.enableContactEvents = false; + shapeDef.enableHitEvents = false; + shapeDef.enableSensorEvents = false; + + int n = def->count; + const b2Vec2* points = def->points; + + if ( def->isLoop ) + { + chainShape->count = n; + chainShape->shapeIndices = b2Alloc( n * sizeof( int ) ); + + b2ChainSegment chainSegment; + + int prevIndex = n - 1; + for ( int i = 0; i < n - 2; ++i ) + { + chainSegment.ghost1 = points[prevIndex]; + chainSegment.segment.point1 = points[i]; + chainSegment.segment.point2 = points[i + 1]; + chainSegment.ghost2 = points[i + 2]; + chainSegment.chainId = chainId; + prevIndex = i; + + b2Shape* shape = b2CreateShapeInternal( world, body, transform, &shapeDef, &chainSegment, b2_chainSegmentShape ); + chainShape->shapeIndices[i] = shape->id; + } + + { + chainSegment.ghost1 = points[n - 3]; + chainSegment.segment.point1 = points[n - 2]; + chainSegment.segment.point2 = points[n - 1]; + chainSegment.ghost2 = points[0]; + chainSegment.chainId = chainId; + b2Shape* shape = b2CreateShapeInternal( world, body, transform, &shapeDef, &chainSegment, b2_chainSegmentShape ); + chainShape->shapeIndices[n - 2] = shape->id; + } + + { + chainSegment.ghost1 = points[n - 2]; + chainSegment.segment.point1 = points[n - 1]; + chainSegment.segment.point2 = points[0]; + chainSegment.ghost2 = points[1]; + chainSegment.chainId = chainId; + b2Shape* shape = b2CreateShapeInternal( world, body, transform, &shapeDef, &chainSegment, b2_chainSegmentShape ); + chainShape->shapeIndices[n - 1] = shape->id; + } + } + else + { + chainShape->count = n - 3; + chainShape->shapeIndices = b2Alloc( n * sizeof( int ) ); + + b2ChainSegment chainSegment; + + for ( int i = 0; i < n - 3; ++i ) + { + chainSegment.ghost1 = points[i]; + chainSegment.segment.point1 = points[i + 1]; + chainSegment.segment.point2 = points[i + 2]; + chainSegment.ghost2 = points[i + 3]; + chainSegment.chainId = chainId; + + b2Shape* shape = b2CreateShapeInternal( world, body, transform, &shapeDef, &chainSegment, b2_chainSegmentShape ); + chainShape->shapeIndices[i] = shape->id; + } + } + + b2ChainId id = { chainId + 1, world->worldId, chainShape->revision }; + return id; +} + +void b2DestroyChain( b2ChainId chainId ) +{ + b2World* world = b2GetWorldLocked( chainId.world0 ); + + b2ChainShape* chain = b2GetChainShape(world, chainId); + bool wakeBodies = true; + + b2Body* body = b2BodyArray_Get( &world->bodies, chain->bodyId ); + + // Remove the chain from the body's singly linked list. + int* chainIdPtr = &body->headChainId; + bool found = false; + while ( *chainIdPtr != B2_NULL_INDEX ) + { + if ( *chainIdPtr == chain->id ) + { + *chainIdPtr = chain->nextChainId; + found = true; + break; + } + + chainIdPtr = &( world->chainShapes.data[*chainIdPtr].nextChainId ); + } + + B2_ASSERT( found == true ); + if ( found == false ) + { + return; + } + + int count = chain->count; + for ( int i = 0; i < count; ++i ) + { + int shapeId = chain->shapeIndices[i]; + b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId ); + b2DestroyShapeInternal( world, shape, body, wakeBodies ); + } + + b2Free( chain->shapeIndices, chain->count * sizeof( int ) ); + chain->shapeIndices = NULL; + + // Return chain to free list. + b2FreeId( &world->chainIdPool, chain->id ); + chain->id = B2_NULL_INDEX; + + b2ValidateSolverSets( world ); +} + +b2WorldId b2Chain_GetWorld(b2ChainId chainId) +{ + b2World* world = b2GetWorld( chainId.world0 ); + return ( b2WorldId ){ chainId.world0 + 1, world->revision }; +} + +int b2Chain_GetSegmentCount(b2ChainId chainId) +{ + b2World* world = b2GetWorldLocked( chainId.world0 ); + b2ChainShape* chain = b2GetChainShape( world, chainId ); + return chain->count; +} + +int b2Chain_GetSegments(b2ChainId chainId, b2ShapeId* segmentArray, int capacity) +{ + b2World* world = b2GetWorldLocked( chainId.world0 ); + b2ChainShape* chain = b2GetChainShape( world, chainId ); + + int count = b2MinInt(chain->count, capacity); + for ( int i = 0; i < count; ++i ) + { + int shapeId = chain->shapeIndices[i]; + b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId ); + segmentArray[i] = ( b2ShapeId ){ shapeId + 1, chainId.world0, shape->revision }; + } + + return count; +} + +b2AABB b2ComputeShapeAABB( const b2Shape* shape, b2Transform xf ) +{ + switch ( shape->type ) + { + case b2_capsuleShape: + return b2ComputeCapsuleAABB( &shape->capsule, xf ); + case b2_circleShape: + return b2ComputeCircleAABB( &shape->circle, xf ); + case b2_polygonShape: + return b2ComputePolygonAABB( &shape->polygon, xf ); + case b2_segmentShape: + return b2ComputeSegmentAABB( &shape->segment, xf ); + case b2_chainSegmentShape: + return b2ComputeSegmentAABB( &shape->chainSegment.segment, xf ); + default: + { + B2_ASSERT( false ); + b2AABB empty = { xf.p, xf.p }; + return empty; + } + } +} + +b2Vec2 b2GetShapeCentroid( const b2Shape* shape ) +{ + switch ( shape->type ) + { + case b2_capsuleShape: + return b2Lerp( shape->capsule.center1, shape->capsule.center2, 0.5f ); + case b2_circleShape: + return shape->circle.center; + case b2_polygonShape: + return shape->polygon.centroid; + case b2_segmentShape: + return b2Lerp( shape->segment.point1, shape->segment.point2, 0.5f ); + case b2_chainSegmentShape: + return b2Lerp( shape->chainSegment.segment.point1, shape->chainSegment.segment.point2, 0.5f ); + default: + return b2Vec2_zero; + } +} + +// todo maybe compute this on shape creation +float b2GetShapePerimeter( const b2Shape* shape ) +{ + switch ( shape->type ) + { + case b2_capsuleShape: + return 2.0f * b2Length( b2Sub( shape->capsule.center1, shape->capsule.center2 ) ) + + 2.0f * b2_pi * shape->capsule.radius; + case b2_circleShape: + return 2.0f * b2_pi * shape->circle.radius; + case b2_polygonShape: + { + const b2Vec2* points = shape->polygon.vertices; + int count = shape->polygon.count; + float perimeter = 2.0f * b2_pi * shape->polygon.radius; + B2_ASSERT( count > 0 ); + b2Vec2 prev = points[count - 1]; + for ( int i = 0; i < count; ++i ) + { + b2Vec2 next = points[i]; + perimeter += b2Length( b2Sub( next, prev ) ); + prev = next; + } + + return perimeter; + } + case b2_segmentShape: + return 2.0f * b2Length( b2Sub( shape->segment.point1, shape->segment.point2 ) ); + case b2_chainSegmentShape: + return 2.0f * b2Length( b2Sub( shape->chainSegment.segment.point1, shape->chainSegment.segment.point2 ) ); + default: + return 0.0f; + } +} + +b2MassData b2ComputeShapeMass( const b2Shape* shape ) +{ + switch ( shape->type ) + { + case b2_capsuleShape: + return b2ComputeCapsuleMass( &shape->capsule, shape->density ); + case b2_circleShape: + return b2ComputeCircleMass( &shape->circle, shape->density ); + case b2_polygonShape: + return b2ComputePolygonMass( &shape->polygon, shape->density ); + default: + { + return ( b2MassData ){ 0 }; + } + } +} + +b2ShapeExtent b2ComputeShapeExtent( const b2Shape* shape, b2Vec2 localCenter ) +{ + b2ShapeExtent extent = { 0 }; + + switch ( shape->type ) + { + case b2_capsuleShape: + { + float radius = shape->capsule.radius; + extent.minExtent = radius; + b2Vec2 c1 = b2Sub( shape->capsule.center1, localCenter ); + b2Vec2 c2 = b2Sub( shape->capsule.center2, localCenter ); + extent.maxExtent = sqrtf( b2MaxFloat( b2LengthSquared( c1 ), b2LengthSquared( c2 ) ) ) + radius; + } + break; + + case b2_circleShape: + { + float radius = shape->circle.radius; + extent.minExtent = radius; + extent.maxExtent = b2Length( b2Sub( shape->circle.center, localCenter ) ) + radius; + } + break; + + case b2_polygonShape: + { + const b2Polygon* poly = &shape->polygon; + float minExtent = b2_huge; + float maxExtentSqr = 0.0f; + int count = poly->count; + for ( int i = 0; i < count; ++i ) + { + b2Vec2 v = poly->vertices[i]; + float planeOffset = b2Dot( poly->normals[i], b2Sub( v, poly->centroid ) ); + minExtent = b2MinFloat( minExtent, planeOffset ); + + float distanceSqr = b2LengthSquared( b2Sub( v, localCenter ) ); + maxExtentSqr = b2MaxFloat( maxExtentSqr, distanceSqr ); + } + + extent.minExtent = minExtent + poly->radius; + extent.maxExtent = sqrtf( maxExtentSqr ) + poly->radius; + } + break; + + case b2_segmentShape: + { + extent.minExtent = 0.0f; + b2Vec2 c1 = b2Sub( shape->segment.point1, localCenter ); + b2Vec2 c2 = b2Sub( shape->segment.point2, localCenter ); + extent.maxExtent = sqrtf( b2MaxFloat( b2LengthSquared( c1 ), b2LengthSquared( c2 ) ) ); + } + break; + + case b2_chainSegmentShape: + { + extent.minExtent = 0.0f; + b2Vec2 c1 = b2Sub( shape->chainSegment.segment.point1, localCenter ); + b2Vec2 c2 = b2Sub( shape->chainSegment.segment.point2, localCenter ); + extent.maxExtent = sqrtf( b2MaxFloat( b2LengthSquared( c1 ), b2LengthSquared( c2 ) ) ); + } + break; + + default: + break; + } + + return extent; +} + +b2CastOutput b2RayCastShape( const b2RayCastInput* input, const b2Shape* shape, b2Transform transform ) +{ + b2RayCastInput localInput = *input; + localInput.origin = b2InvTransformPoint( transform, input->origin ); + localInput.translation = b2InvRotateVector( transform.q, input->translation ); + + b2CastOutput output = { 0 }; + switch ( shape->type ) + { + case b2_capsuleShape: + output = b2RayCastCapsule( &localInput, &shape->capsule ); + break; + case b2_circleShape: + output = b2RayCastCircle( &localInput, &shape->circle ); + break; + case b2_polygonShape: + output = b2RayCastPolygon( &localInput, &shape->polygon ); + break; + case b2_segmentShape: + output = b2RayCastSegment( &localInput, &shape->segment, false ); + break; + case b2_chainSegmentShape: + output = b2RayCastSegment( &localInput, &shape->chainSegment.segment, true ); + break; + default: + return output; + } + + output.point = b2TransformPoint( transform, output.point ); + output.normal = b2RotateVector( transform.q, output.normal ); + return output; +} + +b2CastOutput b2ShapeCastShape( const b2ShapeCastInput* input, const b2Shape* shape, b2Transform transform ) +{ + b2ShapeCastInput localInput = *input; + + for ( int i = 0; i < localInput.count; ++i ) + { + localInput.points[i] = b2InvTransformPoint( transform, input->points[i] ); + } + + localInput.translation = b2InvRotateVector( transform.q, input->translation ); + + b2CastOutput output = { 0 }; + switch ( shape->type ) + { + case b2_capsuleShape: + output = b2ShapeCastCapsule( &localInput, &shape->capsule ); + break; + case b2_circleShape: + output = b2ShapeCastCircle( &localInput, &shape->circle ); + break; + case b2_polygonShape: + output = b2ShapeCastPolygon( &localInput, &shape->polygon ); + break; + case b2_segmentShape: + output = b2ShapeCastSegment( &localInput, &shape->segment ); + break; + case b2_chainSegmentShape: + output = b2ShapeCastSegment( &localInput, &shape->chainSegment.segment ); + break; + default: + return output; + } + + output.point = b2TransformPoint( transform, output.point ); + output.normal = b2RotateVector( transform.q, output.normal ); + return output; +} + +void b2CreateShapeProxy( b2Shape* shape, b2BroadPhase* bp, b2BodyType type, b2Transform transform, bool forcePairCreation ) +{ + B2_ASSERT( shape->proxyKey == B2_NULL_INDEX ); + + b2UpdateShapeAABBs( shape, transform, type ); + + // Create proxies in the broad-phase. + shape->proxyKey = + b2BroadPhase_CreateProxy( bp, type, shape->fatAABB, shape->filter.categoryBits, shape->id, forcePairCreation ); + B2_ASSERT( B2_PROXY_TYPE( shape->proxyKey ) < b2_bodyTypeCount ); +} + +void b2DestroyShapeProxy( b2Shape* shape, b2BroadPhase* bp ) +{ + if ( shape->proxyKey != B2_NULL_INDEX ) + { + b2BroadPhase_DestroyProxy( bp, shape->proxyKey ); + shape->proxyKey = B2_NULL_INDEX; + } +} + +b2DistanceProxy b2MakeShapeDistanceProxy( const b2Shape* shape ) +{ + switch ( shape->type ) + { + case b2_capsuleShape: + return b2MakeProxy( &shape->capsule.center1, 2, shape->capsule.radius ); + case b2_circleShape: + return b2MakeProxy( &shape->circle.center, 1, shape->circle.radius ); + case b2_polygonShape: + return b2MakeProxy( shape->polygon.vertices, shape->polygon.count, shape->polygon.radius ); + case b2_segmentShape: + return b2MakeProxy( &shape->segment.point1, 2, 0.0f ); + case b2_chainSegmentShape: + return b2MakeProxy( &shape->chainSegment.segment.point1, 2, 0.0f ); + default: + { + B2_ASSERT( false ); + b2DistanceProxy empty = { 0 }; + return empty; + } + } +} + +b2BodyId b2Shape_GetBody( b2ShapeId shapeId ) +{ + b2World* world = b2GetWorld( shapeId.world0 ); + b2Shape* shape = b2GetShape( world, shapeId ); + return b2MakeBodyId( world, shape->bodyId ); +} + +b2WorldId b2Shape_GetWorld(b2ShapeId shapeId) +{ + b2World* world = b2GetWorld( shapeId.world0 ); + return ( b2WorldId ){ shapeId.world0 + 1, world->revision }; +} + +void b2Shape_SetUserData( b2ShapeId shapeId, void* userData ) +{ + b2World* world = b2GetWorld( shapeId.world0 ); + b2Shape* shape = b2GetShape( world, shapeId ); + shape->userData = userData; +} + +void* b2Shape_GetUserData( b2ShapeId shapeId ) +{ + b2World* world = b2GetWorld( shapeId.world0 ); + b2Shape* shape = b2GetShape( world, shapeId ); + return shape->userData; +} + +bool b2Shape_IsSensor( b2ShapeId shapeId ) +{ + b2World* world = b2GetWorld( shapeId.world0 ); + b2Shape* shape = b2GetShape( world, shapeId ); + return shape->isSensor; +} + +bool b2Shape_TestPoint( b2ShapeId shapeId, b2Vec2 point ) +{ + b2World* world = b2GetWorld( shapeId.world0 ); + b2Shape* shape = b2GetShape( world, shapeId ); + + b2Transform transform = b2GetBodyTransform( world, shape->bodyId ); + b2Vec2 localPoint = b2InvTransformPoint( transform, point ); + + switch ( shape->type ) + { + case b2_capsuleShape: + return b2PointInCapsule( localPoint, &shape->capsule ); + + case b2_circleShape: + return b2PointInCircle( localPoint, &shape->circle ); + + case b2_polygonShape: + return b2PointInPolygon( localPoint, &shape->polygon ); + + default: + return false; + } +} + +// todo_erin untested +b2CastOutput b2Shape_RayCast( b2ShapeId shapeId, const b2RayCastInput* input ) +{ + b2World* world = b2GetWorld( shapeId.world0 ); + b2Shape* shape = b2GetShape( world, shapeId ); + + b2Transform transform = b2GetBodyTransform( world, shape->bodyId ); + + // input in local coordinates + b2RayCastInput localInput; + localInput.origin = b2InvTransformPoint( transform, input->origin ); + localInput.translation = b2InvRotateVector( transform.q, input->translation ); + localInput.maxFraction = input->maxFraction; + + b2CastOutput output = { 0 }; + switch ( shape->type ) + { + case b2_capsuleShape: + output = b2RayCastCapsule( &localInput, &shape->capsule ); + break; + + case b2_circleShape: + output = b2RayCastCircle( &localInput, &shape->circle ); + break; + + case b2_segmentShape: + output = b2RayCastSegment( &localInput, &shape->segment, false ); + break; + + case b2_polygonShape: + output = b2RayCastPolygon( &localInput, &shape->polygon ); + break; + + case b2_chainSegmentShape: + output = b2RayCastSegment( &localInput, &shape->chainSegment.segment, true ); + break; + + default: + B2_ASSERT( false ); + return output; + } + + if ( output.hit ) + { + // convert to world coordinates + output.normal = b2RotateVector( transform.q, output.normal ); + output.point = b2TransformPoint( transform, output.point ); + } + + return output; +} + +void b2Shape_SetDensity( b2ShapeId shapeId, float density ) +{ + B2_ASSERT( b2IsValid( density ) && density >= 0.0f ); + + b2World* world = b2GetWorldLocked( shapeId.world0 ); + if ( world == NULL ) + { + return; + } + + b2Shape* shape = b2GetShape( world, shapeId ); + if ( density == shape->density ) + { + // early return to avoid expensive function + return; + } + + shape->density = density; +} + +float b2Shape_GetDensity( b2ShapeId shapeId ) +{ + b2World* world = b2GetWorld( shapeId.world0 ); + b2Shape* shape = b2GetShape( world, shapeId ); + return shape->density; +} + +void b2Shape_SetFriction( b2ShapeId shapeId, float friction ) +{ + B2_ASSERT( b2IsValid( friction ) && friction >= 0.0f ); + + b2World* world = b2GetWorld( shapeId.world0 ); + B2_ASSERT( world->locked == false ); + if ( world->locked ) + { + return; + } + + b2Shape* shape = b2GetShape( world, shapeId ); + shape->friction = friction; +} + +float b2Shape_GetFriction( b2ShapeId shapeId ) +{ + b2World* world = b2GetWorld( shapeId.world0 ); + b2Shape* shape = b2GetShape( world, shapeId ); + return shape->friction; +} + +void b2Shape_SetRestitution( b2ShapeId shapeId, float restitution ) +{ + B2_ASSERT( b2IsValid( restitution ) && restitution >= 0.0f ); + + b2World* world = b2GetWorld( shapeId.world0 ); + B2_ASSERT( world->locked == false ); + if ( world->locked ) + { + return; + } + + b2Shape* shape = b2GetShape( world, shapeId ); + shape->restitution = restitution; +} + +float b2Shape_GetRestitution( b2ShapeId shapeId ) +{ + b2World* world = b2GetWorld( shapeId.world0 ); + b2Shape* shape = b2GetShape( world, shapeId ); + return shape->restitution; +} + +b2Filter b2Shape_GetFilter( b2ShapeId shapeId ) +{ + b2World* world = b2GetWorld( shapeId.world0 ); + b2Shape* shape = b2GetShape( world, shapeId ); + return shape->filter; +} + +static void b2ResetProxy( b2World* world, b2Shape* shape, bool wakeBodies, bool destroyProxy ) +{ + b2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId ); + + int shapeId = shape->id; + + // destroy all contacts associated with this shape + int contactKey = body->headContactKey; + while ( contactKey != B2_NULL_INDEX ) + { + int contactId = contactKey >> 1; + int edgeIndex = contactKey & 1; + + b2Contact* contact = b2ContactArray_Get( &world->contacts, contactId ); + contactKey = contact->edges[edgeIndex].nextKey; + + if ( contact->shapeIdA == shapeId || contact->shapeIdB == shapeId ) + { + b2DestroyContact( world, contact, wakeBodies ); + } + } + + b2Transform transform = b2GetBodyTransformQuick( world, body ); + if ( shape->proxyKey != B2_NULL_INDEX ) + { + b2BodyType proxyType = B2_PROXY_TYPE( shape->proxyKey ); + b2UpdateShapeAABBs( shape, transform, proxyType ); + + if ( destroyProxy ) + { + b2BroadPhase_DestroyProxy( &world->broadPhase, shape->proxyKey ); + + bool forcePairCreation = true; + shape->proxyKey = b2BroadPhase_CreateProxy( &world->broadPhase, proxyType, shape->fatAABB, shape->filter.categoryBits, + shapeId, forcePairCreation ); + } + else + { + b2BroadPhase_MoveProxy( &world->broadPhase, shape->proxyKey, shape->fatAABB ); + } + } + else + { + b2BodyType proxyType = body->type; + b2UpdateShapeAABBs( shape, transform, proxyType ); + } + + b2ValidateSolverSets( world ); +} + +void b2Shape_SetFilter( b2ShapeId shapeId, b2Filter filter ) +{ + b2World* world = b2GetWorldLocked( shapeId.world0 ); + if ( world == NULL ) + { + return; + } + + b2Shape* shape = b2GetShape( world, shapeId ); + if ( filter.maskBits == shape->filter.maskBits && filter.categoryBits == shape->filter.categoryBits && + filter.groupIndex == shape->filter.groupIndex ) + { + return; + } + + // If the category bits change, I need to destroy the proxy because it affects the tree sorting. + bool destroyProxy = filter.categoryBits == shape->filter.categoryBits; + + shape->filter = filter; + + // need to wake bodies because a filter change may destroy contacts + bool wakeBodies = true; + b2ResetProxy( world, shape, wakeBodies, destroyProxy ); +} + +void b2Shape_EnableSensorEvents( b2ShapeId shapeId, bool flag ) +{ + b2World* world = b2GetWorldLocked( shapeId.world0 ); + if ( world == NULL ) + { + return; + } + + b2Shape* shape = b2GetShape( world, shapeId ); + shape->enableSensorEvents = flag; +} + +bool b2Shape_AreSensorEventsEnabled( b2ShapeId shapeId ) +{ + b2World* world = b2GetWorld( shapeId.world0 ); + b2Shape* shape = b2GetShape( world, shapeId ); + return shape->enableSensorEvents; +} + +void b2Shape_EnableContactEvents( b2ShapeId shapeId, bool flag ) +{ + b2World* world = b2GetWorldLocked( shapeId.world0 ); + if ( world == NULL ) + { + return; + } + + b2Shape* shape = b2GetShape( world, shapeId ); + shape->enableContactEvents = flag; +} + +bool b2Shape_AreContactEventsEnabled( b2ShapeId shapeId ) +{ + b2World* world = b2GetWorld( shapeId.world0 ); + b2Shape* shape = b2GetShape( world, shapeId ); + return shape->enableContactEvents; +} + +void b2Shape_EnablePreSolveEvents( b2ShapeId shapeId, bool flag ) +{ + b2World* world = b2GetWorldLocked( shapeId.world0 ); + if ( world == NULL ) + { + return; + } + + b2Shape* shape = b2GetShape( world, shapeId ); + shape->enablePreSolveEvents = flag; +} + +bool b2Shape_ArePreSolveEventsEnabled( b2ShapeId shapeId ) +{ + b2World* world = b2GetWorld( shapeId.world0 ); + b2Shape* shape = b2GetShape( world, shapeId ); + return shape->enablePreSolveEvents; +} + +void b2Shape_EnableHitEvents( b2ShapeId shapeId, bool flag ) +{ + b2World* world = b2GetWorldLocked( shapeId.world0 ); + if ( world == NULL ) + { + return; + } + + b2Shape* shape = b2GetShape( world, shapeId ); + shape->enableHitEvents = flag; +} + +bool b2Shape_AreHitEventsEnabled( b2ShapeId shapeId ) +{ + b2World* world = b2GetWorld( shapeId.world0 ); + b2Shape* shape = b2GetShape( world, shapeId ); + return shape->enableHitEvents; +} + +b2ShapeType b2Shape_GetType( b2ShapeId shapeId ) +{ + b2World* world = b2GetWorld( shapeId.world0 ); + b2Shape* shape = b2GetShape( world, shapeId ); + return shape->type; +} + +b2Circle b2Shape_GetCircle( b2ShapeId shapeId ) +{ + b2World* world = b2GetWorld( shapeId.world0 ); + b2Shape* shape = b2GetShape( world, shapeId ); + B2_ASSERT( shape->type == b2_circleShape ); + return shape->circle; +} + +b2Segment b2Shape_GetSegment( b2ShapeId shapeId ) +{ + b2World* world = b2GetWorld( shapeId.world0 ); + b2Shape* shape = b2GetShape( world, shapeId ); + B2_ASSERT( shape->type == b2_segmentShape ); + return shape->segment; +} + +b2ChainSegment b2Shape_GetChainSegment( b2ShapeId shapeId ) +{ + b2World* world = b2GetWorld( shapeId.world0 ); + b2Shape* shape = b2GetShape( world, shapeId ); + B2_ASSERT( shape->type == b2_chainSegmentShape ); + return shape->chainSegment; +} + +b2Capsule b2Shape_GetCapsule( b2ShapeId shapeId ) +{ + b2World* world = b2GetWorld( shapeId.world0 ); + b2Shape* shape = b2GetShape( world, shapeId ); + B2_ASSERT( shape->type == b2_capsuleShape ); + return shape->capsule; +} + +b2Polygon b2Shape_GetPolygon( b2ShapeId shapeId ) +{ + b2World* world = b2GetWorld( shapeId.world0 ); + b2Shape* shape = b2GetShape( world, shapeId ); + B2_ASSERT( shape->type == b2_polygonShape ); + return shape->polygon; +} + +void b2Shape_SetCircle( b2ShapeId shapeId, const b2Circle* circle ) +{ + b2World* world = b2GetWorldLocked( shapeId.world0 ); + if ( world == NULL ) + { + return; + } + + b2Shape* shape = b2GetShape( world, shapeId ); + shape->circle = *circle; + shape->type = b2_circleShape; + + // need to wake bodies so they can react to the shape change + bool wakeBodies = true; + bool destroyProxy = true; + b2ResetProxy( world, shape, wakeBodies, destroyProxy ); +} + +void b2Shape_SetCapsule( b2ShapeId shapeId, const b2Capsule* capsule ) +{ + b2World* world = b2GetWorldLocked( shapeId.world0 ); + if ( world == NULL ) + { + return; + } + + b2Shape* shape = b2GetShape( world, shapeId ); + shape->capsule = *capsule; + shape->type = b2_capsuleShape; + + // need to wake bodies so they can react to the shape change + bool wakeBodies = true; + bool destroyProxy = true; + b2ResetProxy( world, shape, wakeBodies, destroyProxy ); +} + +void b2Shape_SetSegment( b2ShapeId shapeId, const b2Segment* segment ) +{ + b2World* world = b2GetWorldLocked( shapeId.world0 ); + if ( world == NULL ) + { + return; + } + + b2Shape* shape = b2GetShape( world, shapeId ); + shape->segment = *segment; + shape->type = b2_segmentShape; + + // need to wake bodies so they can react to the shape change + bool wakeBodies = true; + bool destroyProxy = true; + b2ResetProxy( world, shape, wakeBodies, destroyProxy ); +} + +void b2Shape_SetPolygon( b2ShapeId shapeId, const b2Polygon* polygon ) +{ + b2World* world = b2GetWorldLocked( shapeId.world0 ); + if ( world == NULL ) + { + return; + } + + b2Shape* shape = b2GetShape( world, shapeId ); + shape->polygon = *polygon; + shape->type = b2_polygonShape; + + // need to wake bodies so they can react to the shape change + bool wakeBodies = true; + bool destroyProxy = true; + b2ResetProxy( world, shape, wakeBodies, destroyProxy ); +} + +b2ChainId b2Shape_GetParentChain( b2ShapeId shapeId ) +{ + b2World* world = b2GetWorld( shapeId.world0 ); + b2Shape* shape = b2GetShape( world, shapeId ); + if ( shape->type == b2_chainSegmentShape ) + { + int chainId = shape->chainSegment.chainId; + if ( chainId != B2_NULL_INDEX ) + { + b2ChainShape* chain = b2ChainShapeArray_Get(&world->chainShapes, chainId); + b2ChainId id = { chainId + 1, shapeId.world0, chain->revision }; + return id; + } + } + + return ( b2ChainId ){ 0 }; +} + +void b2Chain_SetFriction( b2ChainId chainId, float friction ) +{ + b2World* world = b2GetWorldLocked( chainId.world0 ); + if ( world == NULL ) + { + return; + } + + b2ChainShape* chainShape = b2GetChainShape( world, chainId ); + + int count = chainShape->count; + + for ( int i = 0; i < count; ++i ) + { + int shapeId = chainShape->shapeIndices[i]; + b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId ); + shape->friction = friction; + } +} + +void b2Chain_SetRestitution( b2ChainId chainId, float restitution ) +{ + b2World* world = b2GetWorldLocked( chainId.world0 ); + if ( world == NULL ) + { + return; + } + + b2ChainShape* chainShape = b2GetChainShape( world, chainId ); + + int count = chainShape->count; + + for ( int i = 0; i < count; ++i ) + { + int shapeId = chainShape->shapeIndices[i]; + b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId ); + shape->restitution = restitution; + } +} + +int b2Shape_GetContactCapacity( b2ShapeId shapeId ) +{ + b2World* world = b2GetWorldLocked( shapeId.world0 ); + if ( world == NULL ) + { + return 0; + } + + b2Shape* shape = b2GetShape( world, shapeId ); + if ( shape->isSensor ) + { + return 0; + } + + b2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId ); + + // Conservative and fast + return body->contactCount; +} + +// todo sample needed +int b2Shape_GetContactData( b2ShapeId shapeId, b2ContactData* contactData, int capacity ) +{ + b2World* world = b2GetWorldLocked( shapeId.world0 ); + if ( world == NULL ) + { + return 0; + } + + b2Shape* shape = b2GetShape( world, shapeId ); + if ( shape->isSensor ) + { + return 0; + } + + b2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId ); + int contactKey = body->headContactKey; + int index = 0; + while ( contactKey != B2_NULL_INDEX && index < capacity ) + { + int contactId = contactKey >> 1; + int edgeIndex = contactKey & 1; + + b2Contact* contact = b2ContactArray_Get( &world->contacts, contactId ); + + // Does contact involve this shape and is it touching? + if ( ( contact->shapeIdA == shapeId.index1 - 1 || contact->shapeIdB == shapeId.index1 - 1 ) && + ( contact->flags & b2_contactTouchingFlag ) != 0 ) + { + b2Shape* shapeA = world->shapes.data + contact->shapeIdA; + b2Shape* shapeB = world->shapes.data + contact->shapeIdB; + + contactData[index].shapeIdA = ( b2ShapeId ){ shapeA->id + 1, shapeId.world0, shapeA->revision }; + contactData[index].shapeIdB = ( b2ShapeId ){ shapeB->id + 1, shapeId.world0, shapeB->revision }; + + b2ContactSim* contactSim = b2GetContactSim( world, contact ); + contactData[index].manifold = contactSim->manifold; + index += 1; + } + + contactKey = contact->edges[edgeIndex].nextKey; + } + + B2_ASSERT( index <= capacity ); + + return index; +} + +b2AABB b2Shape_GetAABB( b2ShapeId shapeId ) +{ + b2World* world = b2GetWorld( shapeId.world0 ); + if ( world == NULL ) + { + return ( b2AABB ){ 0 }; + } + + b2Shape* shape = b2GetShape( world, shapeId ); + return shape->aabb; +} + +b2Vec2 b2Shape_GetClosestPoint( b2ShapeId shapeId, b2Vec2 target ) +{ + b2World* world = b2GetWorld( shapeId.world0 ); + if ( world == NULL ) + { + return ( b2Vec2 ){ 0 }; + } + + b2Shape* shape = b2GetShape( world, shapeId ); + b2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId ); + b2Transform transform = b2GetBodyTransformQuick( world, body ); + + b2DistanceInput input; + input.proxyA = b2MakeShapeDistanceProxy( shape ); + input.proxyB = b2MakeProxy( &target, 1, 0.0f ); + input.transformA = transform; + input.transformB = b2Transform_identity; + input.useRadii = true; + + b2DistanceCache cache = { 0 }; + b2DistanceOutput output = b2ShapeDistance( &cache, &input, NULL, 0 ); + + return output.pointA; +} diff --git a/3rdparty/box2d/src/shape.h b/3rdparty/box2d/src/shape.h new file mode 100644 index 000000000000..e5efe65e4d6e --- /dev/null +++ b/3rdparty/box2d/src/shape.h @@ -0,0 +1,85 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include "world.h" + +#include "box2d/types.h" + +typedef struct b2BroadPhase b2BroadPhase; +typedef struct b2World b2World; + +typedef struct b2Shape +{ + int id; + int bodyId; + int prevShapeId; + int nextShapeId; + b2ShapeType type; + float density; + float friction; + float restitution; + + b2AABB aabb; + b2AABB fatAABB; + b2Vec2 localCentroid; + int proxyKey; + + b2Filter filter; + void* userData; + uint32_t customColor; + + union + { + b2Capsule capsule; + b2Circle circle; + b2Polygon polygon; + b2Segment segment; + b2ChainSegment chainSegment; + }; + + uint16_t revision; + bool isSensor; + bool enableSensorEvents; + bool enableContactEvents; + bool enableHitEvents; + bool enablePreSolveEvents; + bool enlargedAABB; + bool isFast; +} b2Shape; + +typedef struct b2ChainShape +{ + int id; + int bodyId; + int nextChainId; + int* shapeIndices; + int count; + uint16_t revision; +} b2ChainShape; + +typedef struct b2ShapeExtent +{ + float minExtent; + float maxExtent; +} b2ShapeExtent; + +void b2CreateShapeProxy( b2Shape* shape, b2BroadPhase* bp, b2BodyType type, b2Transform transform, bool forcePairCreation ); +void b2DestroyShapeProxy( b2Shape* shape, b2BroadPhase* bp ); + +b2MassData b2ComputeShapeMass( const b2Shape* shape ); +b2ShapeExtent b2ComputeShapeExtent( const b2Shape* shape, b2Vec2 localCenter ); +b2AABB b2ComputeShapeAABB( const b2Shape* shape, b2Transform transform ); +b2Vec2 b2GetShapeCentroid( const b2Shape* shape ); +float b2GetShapePerimeter( const b2Shape* shape ); + +b2DistanceProxy b2MakeShapeDistanceProxy( const b2Shape* shape ); + +b2CastOutput b2RayCastShape( const b2RayCastInput* input, const b2Shape* shape, b2Transform transform ); +b2CastOutput b2ShapeCastShape( const b2ShapeCastInput* input, const b2Shape* shape, b2Transform transform ); + +b2Transform b2GetOwnerTransform( b2World* world, b2Shape* shape ); + +B2_ARRAY_INLINE( b2ChainShape, b2ChainShape ); +B2_ARRAY_INLINE( b2Shape, b2Shape ); diff --git a/3rdparty/box2d/src/solver.c b/3rdparty/box2d/src/solver.c new file mode 100644 index 000000000000..28a4c2b256d7 --- /dev/null +++ b/3rdparty/box2d/src/solver.c @@ -0,0 +1,2045 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#include "solver.h" + +#include "array.h" +#include "bitset.h" +#include "body.h" +#include "contact.h" +#include "contact_solver.h" +#include "core.h" +#include "ctz.h" +#include "island.h" +#include "joint.h" +#include "shape.h" +#include "solver_set.h" +#include "stack_allocator.h" +#include "world.h" + +#include +#include +#include +#include + +#if ( defined( __GNUC__ ) || defined( __clang__ ) ) && ( defined( __i386__ ) || defined( __x86_64__ ) ) +static inline void b2Pause( void ) +{ + __asm__ __volatile__( "pause\n" ); +} +#elif ( defined( __arm__ ) && defined( __ARM_ARCH ) && __ARM_ARCH >= 7 ) || defined( __aarch64__ ) +static inline void b2Pause( void ) +{ + __asm__ __volatile__( "yield" ::: "memory" ); +} +#elif defined( _MSC_VER ) && ( defined( _M_IX86 ) || defined( _M_X64 ) ) +//#include +static inline void b2Pause( void ) +{ + _mm_pause(); +} +#elif defined( _MSC_VER ) && ( defined( _M_ARM ) || defined( _M_ARM64 ) ) +static inline void b2Pause( void ) +{ + __yield(); +} +#else +static inline void b2Pause( void ) +{ +} +#endif + +typedef struct b2WorkerContext +{ + b2StepContext* context; + int workerIndex; + void* userTask; +} b2WorkerContext; + +// Integrate velocities and apply damping +static void b2IntegrateVelocitiesTask( int startIndex, int endIndex, b2StepContext* context ) +{ + b2TracyCZoneNC( integrate_velocity, "IntVel", b2_colorDeepPink, true ); + + b2BodyState* states = context->states; + b2BodySim* sims = context->sims; + + b2Vec2 gravity = context->world->gravity; + float h = context->h; + float maxLinearSpeed = context->maxLinearVelocity; + float maxAngularSpeed = b2_maxRotation * context->inv_dt; + float maxLinearSpeedSquared = maxLinearSpeed * maxLinearSpeed; + float maxAngularSpeedSquared = maxAngularSpeed * maxAngularSpeed; + + for ( int i = startIndex; i < endIndex; ++i ) + { + b2BodySim* sim = sims + i; + b2BodyState* state = states + i; + + b2Vec2 v = state->linearVelocity; + float w = state->angularVelocity; + + // Apply forces, torque, gravity, and damping + // Apply damping. + // Differential equation: dv/dt + c * v = 0 + // Solution: v(t) = v0 * exp(-c * t) + // Time step: v(t + dt) = v0 * exp(-c * (t + dt)) = v0 * exp(-c * t) * exp(-c * dt) = v(t) * exp(-c * dt) + // v2 = exp(-c * dt) * v1 + // Pade approximation: + // v2 = v1 * 1 / (1 + c * dt) + float linearDamping = 1.0f / ( 1.0f + h * sim->linearDamping ); + float angularDamping = 1.0f / ( 1.0f + h * sim->angularDamping ); + b2Vec2 linearVelocityDelta = b2MulSV( h * sim->invMass, b2MulAdd( sim->force, sim->mass * sim->gravityScale, gravity ) ); + float angularVelocityDelta = h * sim->invInertia * sim->torque; + + v = b2MulAdd( linearVelocityDelta, linearDamping, v ); + w = angularVelocityDelta + angularDamping * w; + + // Clamp to max linear speed + if ( b2Dot( v, v ) > maxLinearSpeedSquared ) + { + float ratio = maxLinearSpeed / b2Length( v ); + v = b2MulSV( ratio, v ); + sim->isSpeedCapped = true; + } + + // Clamp to max angular speed + if ( w * w > maxAngularSpeedSquared && sim->allowFastRotation == false ) + { + float ratio = maxAngularSpeed / b2AbsFloat( w ); + w *= ratio; + sim->isSpeedCapped = true; + } + + state->linearVelocity = v; + state->angularVelocity = w; + } + + b2TracyCZoneEnd( integrate_velocity ); +} + +static void b2PrepareJointsTask( int startIndex, int endIndex, b2StepContext* context ) +{ + b2TracyCZoneNC( prepare_joints, "PrepJoints", b2_colorOldLace, true ); + + b2JointSim** joints = context->joints; + + for ( int i = startIndex; i < endIndex; ++i ) + { + b2JointSim* joint = joints[i]; + b2PrepareJoint( joint, context ); + } + + b2TracyCZoneEnd( prepare_joints ); +} + +static void b2WarmStartJointsTask( int startIndex, int endIndex, b2StepContext* context, int colorIndex ) +{ + b2TracyCZoneNC( warm_joints, "WarmJoints", b2_colorGold, true ); + + b2GraphColor* color = context->graph->colors + colorIndex; + b2JointSim* joints = color->jointSims.data; + B2_ASSERT( 0 <= startIndex && startIndex < color->jointSims.count ); + B2_ASSERT( startIndex <= endIndex && endIndex <= color->jointSims.count ); + + for ( int i = startIndex; i < endIndex; ++i ) + { + b2JointSim* joint = joints + i; + b2WarmStartJoint( joint, context ); + } + + b2TracyCZoneEnd( warm_joints ); +} + +static void b2SolveJointsTask( int startIndex, int endIndex, b2StepContext* context, int colorIndex, bool useBias ) +{ + b2TracyCZoneNC( solve_joints, "SolveJoints", b2_colorLemonChiffon, true ); + + b2GraphColor* color = context->graph->colors + colorIndex; + b2JointSim* joints = color->jointSims.data; + B2_ASSERT( 0 <= startIndex && startIndex < color->jointSims.count ); + B2_ASSERT( startIndex <= endIndex && endIndex <= color->jointSims.count ); + + for ( int i = startIndex; i < endIndex; ++i ) + { + b2JointSim* joint = joints + i; + b2SolveJoint( joint, context, useBias ); + } + + b2TracyCZoneEnd( solve_joints ); +} + +static void b2IntegratePositionsTask( int startIndex, int endIndex, b2StepContext* context ) +{ + b2TracyCZoneNC( integrate_positions, "IntPos", b2_colorDarkSeaGreen, true ); + + b2BodyState* states = context->states; + float h = context->h; + + B2_ASSERT( startIndex <= endIndex ); + + for ( int i = startIndex; i < endIndex; ++i ) + { + b2BodyState* state = states + i; + state->deltaRotation = b2IntegrateRotation( state->deltaRotation, h * state->angularVelocity ); + state->deltaPosition = b2MulAdd( state->deltaPosition, h, state->linearVelocity ); + } + + b2TracyCZoneEnd( integrate_positions ); +} + +static void b2FinalizeBodiesTask( int startIndex, int endIndex, uint32_t threadIndex, void* context ) +{ + b2TracyCZoneNC( finalize_bodies, "FinalizeBodies", b2_colorViolet, true ); + + b2StepContext* stepContext = context; + b2World* world = stepContext->world; + bool enableSleep = world->enableSleep; + b2BodyState* states = stepContext->states; + b2BodySim* sims = stepContext->sims; + b2Body* bodies = world->bodies.data; + float timeStep = stepContext->dt; + float invTimeStep = stepContext->inv_dt; + + uint16_t worldId = world->worldId; + + // The body move event array has should already have the correct size + B2_ASSERT( endIndex <= world->bodyMoveEvents.count ); + b2BodyMoveEvent* moveEvents = world->bodyMoveEvents.data; + + b2BitSet* enlargedSimBitSet = &world->taskContexts.data[threadIndex].enlargedSimBitSet; + b2BitSet* awakeIslandBitSet = &world->taskContexts.data[threadIndex].awakeIslandBitSet; + b2TaskContext* taskContext = world->taskContexts.data + threadIndex; + + bool enableContinuous = world->enableContinuous; + + const float speculativeDistance = b2_speculativeDistance; + const float aabbMargin = b2_aabbMargin; + + B2_ASSERT( startIndex <= endIndex ); + + for ( int simIndex = startIndex; simIndex < endIndex; ++simIndex ) + { + b2BodyState* state = states + simIndex; + b2BodySim* sim = sims + simIndex; + + b2Vec2 v = state->linearVelocity; + float w = state->angularVelocity; + + B2_ASSERT( b2Vec2_IsValid( v ) ); + B2_ASSERT( b2IsValid( w ) ); + + sim->center = b2Add( sim->center, state->deltaPosition ); + sim->transform.q = b2NormalizeRot( b2MulRot( state->deltaRotation, sim->transform.q ) ); + + // Use the velocity of the farthest point on the body to account for rotation. + float maxVelocity = b2Length( v ) + b2AbsFloat( w ) * sim->maxExtent; + + // Sleep needs to observe position correction as well as true velocity. + float maxDeltaPosition = b2Length( state->deltaPosition ) + b2AbsFloat( state->deltaRotation.s ) * sim->maxExtent; + + // Position correction is not as important for sleep as true velocity. + float positionSleepFactor = 0.5f; + + float sleepVelocity = b2MaxFloat( maxVelocity, positionSleepFactor * invTimeStep * maxDeltaPosition ); + + // reset state deltas + state->deltaPosition = b2Vec2_zero; + state->deltaRotation = b2Rot_identity; + + sim->transform.p = b2Sub( sim->center, b2RotateVector( sim->transform.q, sim->localCenter ) ); + + // cache miss here, however I need the shape list below + b2Body* body = bodies + sim->bodyId; + body->bodyMoveIndex = simIndex; + moveEvents[simIndex].transform = sim->transform; + moveEvents[simIndex].bodyId = ( b2BodyId ){ sim->bodyId + 1, worldId, body->revision }; + moveEvents[simIndex].userData = body->userData; + moveEvents[simIndex].fellAsleep = false; + + // reset applied force and torque + sim->force = b2Vec2_zero; + sim->torque = 0.0f; + + body->isSpeedCapped = sim->isSpeedCapped; + sim->isSpeedCapped = false; + + sim->isFast = false; + + if ( enableSleep == false || body->enableSleep == false || sleepVelocity > body->sleepThreshold ) + { + // Body is not sleepy + body->sleepTime = 0.0f; + + const float saftetyFactor = 0.5f; + if ( body->type == b2_dynamicBody && enableContinuous && maxVelocity * timeStep > saftetyFactor * sim->minExtent ) + { + // Store in fast array for the continuous collision stage + // This is deterministic because the order of TOI sweeps doesn't matter + if ( sim->isBullet ) + { + int bulletIndex = atomic_fetch_add( &stepContext->bulletBodyCount, 1 ); + stepContext->bulletBodies[bulletIndex] = simIndex; + } + else + { + int fastIndex = atomic_fetch_add( &stepContext->fastBodyCount, 1 ); + stepContext->fastBodies[fastIndex] = simIndex; + } + + sim->isFast = true; + } + else + { + // Body is safe to advance + sim->center0 = sim->center; + sim->rotation0 = sim->transform.q; + } + } + else + { + // Body is safe to advance and is falling asleep + sim->center0 = sim->center; + sim->rotation0 = sim->transform.q; + body->sleepTime += timeStep; + } + + // Any single body in an island can keep it awake + b2Island* island = b2IslandArray_Get( &world->islands, body->islandId ); + if ( body->sleepTime < b2_timeToSleep ) + { + // keep island awake + int islandIndex = island->localIndex; + b2SetBit( awakeIslandBitSet, islandIndex ); + } + else if ( island->constraintRemoveCount > 0 ) + { + // body wants to sleep but its island needs splitting first + if ( body->sleepTime > taskContext->splitSleepTime ) + { + // pick the sleepiest candidate + taskContext->splitIslandId = body->islandId; + taskContext->splitSleepTime = body->sleepTime; + } + } + + // Update shapes AABBs + b2Transform transform = sim->transform; + bool isFast = sim->isFast; + int shapeId = body->headShapeId; + while ( shapeId != B2_NULL_INDEX ) + { + b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId ); + + B2_ASSERT( shape->isFast == false ); + + if ( isFast ) + { + // The AABB is updated after continuous collision. + // Add to moved shapes regardless of AABB changes. + shape->isFast = true; + + // Bit-set to keep the move array sorted + b2SetBit( enlargedSimBitSet, simIndex ); + } + else + { + b2AABB aabb = b2ComputeShapeAABB( shape, transform ); + aabb.lowerBound.x -= speculativeDistance; + aabb.lowerBound.y -= speculativeDistance; + aabb.upperBound.x += speculativeDistance; + aabb.upperBound.y += speculativeDistance; + shape->aabb = aabb; + + B2_ASSERT( shape->enlargedAABB == false ); + + if ( b2AABB_Contains( shape->fatAABB, aabb ) == false ) + { + b2AABB fatAABB; + fatAABB.lowerBound.x = aabb.lowerBound.x - aabbMargin; + fatAABB.lowerBound.y = aabb.lowerBound.y - aabbMargin; + fatAABB.upperBound.x = aabb.upperBound.x + aabbMargin; + fatAABB.upperBound.y = aabb.upperBound.y + aabbMargin; + shape->fatAABB = fatAABB; + + shape->enlargedAABB = true; + + // Bit-set to keep the move array sorted + b2SetBit( enlargedSimBitSet, simIndex ); + } + } + + shapeId = shape->nextShapeId; + } + } + + b2TracyCZoneEnd( finalize_bodies ); +} + +/* + typedef enum b2SolverStageType +{ + b2_stagePrepareJoints, + b2_stagePrepareContacts, + b2_stageIntegrateVelocities, + b2_stageWarmStart, + b2_stageSolve, + b2_stageIntegratePositions, + b2_stageRelax, + b2_stageRestitution, + b2_stageStoreImpulses +} b2SolverStageType; + +typedef enum b2SolverBlockType +{ + b2_bodyBlock, + b2_jointBlock, + b2_contactBlock, + b2_graphJointBlock, + b2_graphContactBlock +} b2SolverBlockType; +*/ + +static void b2ExecuteBlock( b2SolverStage* stage, b2StepContext* context, b2SolverBlock* block ) +{ + b2SolverStageType stageType = stage->type; + b2SolverBlockType blockType = block->blockType; + int startIndex = block->startIndex; + int endIndex = startIndex + block->count; + + switch ( stageType ) + { + case b2_stagePrepareJoints: + b2PrepareJointsTask( startIndex, endIndex, context ); + break; + + case b2_stagePrepareContacts: + b2PrepareContactsTask( startIndex, endIndex, context ); + break; + + case b2_stageIntegrateVelocities: + b2IntegrateVelocitiesTask( startIndex, endIndex, context ); + break; + + case b2_stageWarmStart: + if ( context->world->enableWarmStarting ) + { + if ( blockType == b2_graphContactBlock ) + { + b2WarmStartContactsTask( startIndex, endIndex, context, stage->colorIndex ); + } + else if ( blockType == b2_graphJointBlock ) + { + b2WarmStartJointsTask( startIndex, endIndex, context, stage->colorIndex ); + } + } + break; + + case b2_stageSolve: + if ( blockType == b2_graphContactBlock ) + { + b2SolveContactsTask( startIndex, endIndex, context, stage->colorIndex, true ); + } + else if ( blockType == b2_graphJointBlock ) + { + b2SolveJointsTask( startIndex, endIndex, context, stage->colorIndex, true ); + } + break; + + case b2_stageIntegratePositions: + b2IntegratePositionsTask( startIndex, endIndex, context ); + break; + + case b2_stageRelax: + if ( blockType == b2_graphContactBlock ) + { + b2SolveContactsTask( startIndex, endIndex, context, stage->colorIndex, false ); + } + else if ( blockType == b2_graphJointBlock ) + { + b2SolveJointsTask( startIndex, endIndex, context, stage->colorIndex, false ); + } + break; + + case b2_stageRestitution: + if ( blockType == b2_graphContactBlock ) + { + b2ApplyRestitutionTask( startIndex, endIndex, context, stage->colorIndex ); + } + break; + + case b2_stageStoreImpulses: + b2StoreImpulsesTask( startIndex, endIndex, context ); + break; + } +} + +static inline int GetWorkerStartIndex( int workerIndex, int blockCount, int workerCount ) +{ + if ( blockCount <= workerCount ) + { + return workerIndex < blockCount ? workerIndex : B2_NULL_INDEX; + } + + int blocksPerWorker = blockCount / workerCount; + int remainder = blockCount - blocksPerWorker * workerCount; + return blocksPerWorker * workerIndex + b2MinInt( remainder, workerIndex ); +} + +static void b2ExecuteStage( b2SolverStage* stage, b2StepContext* context, int previousSyncIndex, int syncIndex, int workerIndex ) +{ + int completedCount = 0; + b2SolverBlock* blocks = stage->blocks; + int blockCount = stage->blockCount; + + int expectedSyncIndex = previousSyncIndex; + + int startIndex = GetWorkerStartIndex( workerIndex, blockCount, context->workerCount ); + if ( startIndex == B2_NULL_INDEX ) + { + return; + } + + B2_ASSERT( 0 <= startIndex && startIndex < blockCount ); + + int blockIndex = startIndex; + + // Caution: this can change expectedSyncIndex + while ( atomic_compare_exchange_strong( &blocks[blockIndex].syncIndex, &expectedSyncIndex, syncIndex ) == true ) + { + B2_ASSERT( stage->type != b2_stagePrepareContacts || syncIndex < 2 ); + + B2_ASSERT( completedCount < blockCount ); + + b2ExecuteBlock( stage, context, blocks + blockIndex ); + + completedCount += 1; + blockIndex += 1; + if ( blockIndex >= blockCount ) + { + // Keep looking for work + blockIndex = 0; + } + + expectedSyncIndex = previousSyncIndex; + } + + // Search backwards for blocks + blockIndex = startIndex - 1; + while ( true ) + { + if ( blockIndex < 0 ) + { + blockIndex = blockCount - 1; + } + + expectedSyncIndex = previousSyncIndex; + + // Caution: this can change expectedSyncIndex + if ( atomic_compare_exchange_strong( &blocks[blockIndex].syncIndex, &expectedSyncIndex, syncIndex ) == false ) + { + break; + } + + b2ExecuteBlock( stage, context, blocks + blockIndex ); + completedCount += 1; + blockIndex -= 1; + } + + (void)atomic_fetch_add( &stage->completionCount, completedCount ); +} + +static void b2ExecuteMainStage( b2SolverStage* stage, b2StepContext* context, uint32_t syncBits ) +{ + int blockCount = stage->blockCount; + if ( blockCount == 0 ) + { + return; + } + + if ( blockCount == 1 ) + { + b2ExecuteBlock( stage, context, stage->blocks ); + } + else + { + atomic_store( &context->atomicSyncBits, syncBits ); + + int syncIndex = ( syncBits >> 16 ) & 0xFFFF; + B2_ASSERT( syncIndex > 0 ); + int previousSyncIndex = syncIndex - 1; + + b2ExecuteStage( stage, context, previousSyncIndex, syncIndex, 0 ); + + // todo consider using the cycle counter as well + while ( atomic_load( &stage->completionCount ) != blockCount ) + { + b2Pause(); + } + + atomic_store( &stage->completionCount, 0 ); + } +} + +// This should not use the thread index because thread 0 can be called twice by enkiTS. +void b2SolverTask( int startIndex, int endIndex, uint32_t threadIndexDontUse, void* taskContext ) +{ + B2_MAYBE_UNUSED( startIndex ); + B2_MAYBE_UNUSED( endIndex ); + B2_MAYBE_UNUSED( threadIndexDontUse ); + + b2WorkerContext* workerContext = taskContext; + int workerIndex = workerContext->workerIndex; + b2StepContext* context = workerContext->context; + int activeColorCount = context->activeColorCount; + b2SolverStage* stages = context->stages; + b2Profile* profile = &context->world->profile; + + if ( workerIndex == 0 ) + { + // Main thread synchronizes the workers and does work itself. + // + // Stages are re-used by loops so that I don't need more stages for large iteration counts. + // The sync indices grow monotonically for the body/graph/constraint groupings because they share solver blocks. + // The stage index and sync indices are combined in to sync bits for atomic synchronization. + // The workers need to compute the previous sync index for a given stage so that CAS works correctly. This + // setup makes this easy to do. + + /* + b2_stagePrepareJoints, + b2_stagePrepareContacts, + b2_stageIntegrateVelocities, + b2_stageWarmStart, + b2_stageSolve, + b2_stageIntegratePositions, + b2_stageRelax, + b2_stageRestitution, + b2_stageStoreImpulses + */ + + b2Timer timer = b2CreateTimer(); + + int bodySyncIndex = 1; + int stageIndex = 0; + + // This stage loops over all awake joints + uint32_t jointSyncIndex = 1; + uint32_t syncBits = ( jointSyncIndex << 16 ) | stageIndex; + B2_ASSERT( stages[stageIndex].type == b2_stagePrepareJoints ); + b2ExecuteMainStage( stages + stageIndex, context, syncBits ); + stageIndex += 1; + jointSyncIndex += 1; + + // This stage loops over all contact constraints + uint32_t contactSyncIndex = 1; + syncBits = ( contactSyncIndex << 16 ) | stageIndex; + B2_ASSERT( stages[stageIndex].type == b2_stagePrepareContacts ); + b2ExecuteMainStage( stages + stageIndex, context, syncBits ); + stageIndex += 1; + contactSyncIndex += 1; + + int graphSyncIndex = 1; + + // Single-threaded overflow work. These constraints don't fit in the graph coloring. + b2PrepareOverflowJoints( context ); + b2PrepareOverflowContacts( context ); + + profile->prepareConstraints += b2GetMillisecondsAndReset( &timer ); + + int subStepCount = context->subStepCount; + for ( int i = 0; i < subStepCount; ++i ) + { + // stage index restarted each iteration + // syncBits still increases monotonically because the upper bits increase each iteration + int iterStageIndex = stageIndex; + + // integrate velocities + syncBits = ( bodySyncIndex << 16 ) | iterStageIndex; + B2_ASSERT( stages[iterStageIndex].type == b2_stageIntegrateVelocities ); + b2ExecuteMainStage( stages + iterStageIndex, context, syncBits ); + iterStageIndex += 1; + bodySyncIndex += 1; + + profile->integrateVelocities += b2GetMillisecondsAndReset( &timer ); + + // warm start constraints + b2WarmStartOverflowJoints( context ); + b2WarmStartOverflowContacts( context ); + + for ( int colorIndex = 0; colorIndex < activeColorCount; ++colorIndex ) + { + syncBits = ( graphSyncIndex << 16 ) | iterStageIndex; + B2_ASSERT( stages[iterStageIndex].type == b2_stageWarmStart ); + b2ExecuteMainStage( stages + iterStageIndex, context, syncBits ); + iterStageIndex += 1; + } + graphSyncIndex += 1; + + profile->warmStart += b2GetMillisecondsAndReset( &timer ); + + // solve constraints + bool useBias = true; + b2SolveOverflowJoints( context, useBias ); + b2SolveOverflowContacts( context, useBias ); + + for ( int colorIndex = 0; colorIndex < activeColorCount; ++colorIndex ) + { + syncBits = ( graphSyncIndex << 16 ) | iterStageIndex; + B2_ASSERT( stages[iterStageIndex].type == b2_stageSolve ); + b2ExecuteMainStage( stages + iterStageIndex, context, syncBits ); + iterStageIndex += 1; + } + graphSyncIndex += 1; + + profile->solveVelocities += b2GetMillisecondsAndReset( &timer ); + + // integrate positions + B2_ASSERT( stages[iterStageIndex].type == b2_stageIntegratePositions ); + syncBits = ( bodySyncIndex << 16 ) | iterStageIndex; + b2ExecuteMainStage( stages + iterStageIndex, context, syncBits ); + iterStageIndex += 1; + bodySyncIndex += 1; + + profile->integratePositions += b2GetMillisecondsAndReset( &timer ); + + // relax constraints + useBias = false; + b2SolveOverflowJoints( context, useBias ); + b2SolveOverflowContacts( context, useBias ); + + for ( int colorIndex = 0; colorIndex < activeColorCount; ++colorIndex ) + { + syncBits = ( graphSyncIndex << 16 ) | iterStageIndex; + B2_ASSERT( stages[iterStageIndex].type == b2_stageRelax ); + b2ExecuteMainStage( stages + iterStageIndex, context, syncBits ); + iterStageIndex += 1; + } + graphSyncIndex += 1; + + profile->relaxVelocities += b2GetMillisecondsAndReset( &timer ); + } + + // advance the stage according to the sub-stepping tasks just completed + // integrate velocities / warm start / solve / integrate positions / relax + stageIndex += 1 + activeColorCount + activeColorCount + 1 + activeColorCount; + + // Restitution + { + b2ApplyOverflowRestitution( context ); + + int iterStageIndex = stageIndex; + for ( int colorIndex = 0; colorIndex < activeColorCount; ++colorIndex ) + { + syncBits = ( graphSyncIndex << 16 ) | iterStageIndex; + B2_ASSERT( stages[iterStageIndex].type == b2_stageRestitution ); + b2ExecuteMainStage( stages + iterStageIndex, context, syncBits ); + iterStageIndex += 1; + } + // graphSyncIndex += 1; + stageIndex += activeColorCount; + } + + profile->applyRestitution += b2GetMillisecondsAndReset( &timer ); + + b2StoreOverflowImpulses( context ); + + syncBits = ( contactSyncIndex << 16 ) | stageIndex; + B2_ASSERT( stages[stageIndex].type == b2_stageStoreImpulses ); + b2ExecuteMainStage( stages + stageIndex, context, syncBits ); + + profile->storeImpulses += b2GetMillisecondsAndReset( &timer ); + + // Signal workers to finish + atomic_store( &context->atomicSyncBits, UINT_MAX ); + + B2_ASSERT( stageIndex + 1 == context->stageCount ); + return; + } + + // Worker spins and waits for work + uint32_t lastSyncBits = 0; + // uint64_t maxSpinTime = 10; + while ( true ) + { + // Spin until main thread bumps changes the sync bits. This can waste significant time overall, but it is necessary for + // parallel simulation with graph coloring. + uint32_t syncBits; + int spinCount = 0; + while ( ( syncBits = atomic_load( &context->atomicSyncBits ) ) == lastSyncBits ) + { + if ( spinCount > 5 ) + { + b2Yield(); + spinCount = 0; + } + else + { + // Using the cycle counter helps to account for variation in mm_pause timing across different + // CPUs. However, this is X64 only. + // uint64_t prev = __rdtsc(); + // do + //{ + // b2Pause(); + //} + // while ((__rdtsc() - prev) < maxSpinTime); + // maxSpinTime += 10; + b2Pause(); + b2Pause(); + spinCount += 1; + } + } + + if ( syncBits == UINT_MAX ) + { + // sentinel hit + break; + } + + int stageIndex = syncBits & 0xFFFF; + B2_ASSERT( stageIndex < context->stageCount ); + + int syncIndex = ( syncBits >> 16 ) & 0xFFFF; + B2_ASSERT( syncIndex > 0 ); + + int previousSyncIndex = syncIndex - 1; + + b2SolverStage* stage = stages + stageIndex; + b2ExecuteStage( stage, context, previousSyncIndex, syncIndex, workerIndex ); + + lastSyncBits = syncBits; + } +} + +struct b2ContinuousContext +{ + b2World* world; + b2BodySim* fastBodySim; + b2Shape* fastShape; + b2Vec2 centroid1, centroid2; + b2Sweep sweep; + float fraction; +}; + +// todo this may lead to pauses for scenarios where pre-solve would disable collision +static bool b2ContinuousQueryCallback( int proxyId, int shapeId, void* context ) +{ + B2_MAYBE_UNUSED( proxyId ); + + struct b2ContinuousContext* continuousContext = context; + b2Shape* fastShape = continuousContext->fastShape; + b2BodySim* fastBodySim = continuousContext->fastBodySim; + + // Skip same shape + if ( shapeId == fastShape->id ) + { + return true; + } + + b2World* world = continuousContext->world; + + b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId ); + + // Skip same body + if ( shape->bodyId == fastShape->bodyId ) + { + return true; + } + + // Skip filtered shapes + bool canCollide = b2ShouldShapesCollide( fastShape->filter, shape->filter ); + if ( canCollide == false ) + { + return true; + } + + // Skip sensors + if ( shape->isSensor == true ) + { + return true; + } + + b2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId ); + b2BodySim* bodySim = b2GetBodySim( world, body ); + B2_ASSERT( body->type == b2_staticBody || fastBodySim->isBullet ); + + // Skip bullets + if ( bodySim->isBullet ) + { + return true; + } + + // Skip filtered bodies + b2Body* fastBody = b2BodyArray_Get( &world->bodies, fastBodySim->bodyId ); + canCollide = b2ShouldBodiesCollide( world, fastBody, body ); + if ( canCollide == false ) + { + return true; + } + + // Custom user filtering + b2CustomFilterFcn* customFilterFcn = world->customFilterFcn; + if ( customFilterFcn != NULL ) + { + b2ShapeId idA = { shape->id + 1, world->worldId, shape->revision }; + b2ShapeId idB = { fastShape->id + 1, world->worldId, fastShape->revision }; + canCollide = customFilterFcn( idA, idB, world->customFilterContext ); + if ( canCollide == false ) + { + return true; + } + } + + // Prevent pausing on chain segment junctions + if ( shape->type == b2_chainSegmentShape ) + { + b2Transform transform = bodySim->transform; + b2Vec2 p1 = b2TransformPoint( transform, shape->chainSegment.segment.point1 ); + b2Vec2 p2 = b2TransformPoint( transform, shape->chainSegment.segment.point2 ); + b2Vec2 e = b2Sub( p2, p1 ); + b2Vec2 c1 = continuousContext->centroid1; + b2Vec2 c2 = continuousContext->centroid2; + float offset1 = b2Cross( b2Sub( c1, p1 ), e ); + float offset2 = b2Cross( b2Sub( c2, p1 ), e ); + + if ( offset1 < 0.0f || offset2 > 0.0f ) + { + // Started behind or finished in front + return true; + } + } + + b2TOIInput input; + input.proxyA = b2MakeShapeDistanceProxy( shape ); + input.proxyB = b2MakeShapeDistanceProxy( fastShape ); + input.sweepA = b2MakeSweep( bodySim ); + input.sweepB = continuousContext->sweep; + input.tMax = continuousContext->fraction; + + b2TOIOutput output = b2TimeOfImpact( &input ); + if ( 0.0f < output.t && output.t < continuousContext->fraction ) + { + continuousContext->fraction = output.t; + } + else if ( 0.0f == output.t ) + { + // fallback to TOI of a small circle around the fast shape centroid + b2Vec2 centroid = b2GetShapeCentroid( fastShape ); + input.proxyB = b2MakeProxy( ¢roid, 1, b2_speculativeDistance ); + output = b2TimeOfImpact( &input ); + if ( 0.0f < output.t && output.t < continuousContext->fraction ) + { + continuousContext->fraction = output.t; + } + } + + return true; +} + +// Continuous collision of dynamic versus static +static void b2SolveContinuous( b2World* world, int bodySimIndex ) +{ + b2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet ); + b2BodySim* fastBodySim = b2BodySimArray_Get( &awakeSet->bodySims, bodySimIndex ); + B2_ASSERT( fastBodySim->isFast ); + + b2Sweep sweep = b2MakeSweep( fastBodySim ); + + b2Transform xf1; + xf1.q = sweep.q1; + xf1.p = b2Sub( sweep.c1, b2RotateVector( sweep.q1, sweep.localCenter ) ); + + b2Transform xf2; + xf2.q = sweep.q2; + xf2.p = b2Sub( sweep.c2, b2RotateVector( sweep.q2, sweep.localCenter ) ); + + b2DynamicTree* staticTree = world->broadPhase.trees + b2_staticBody; + b2DynamicTree* kinematicTree = world->broadPhase.trees + b2_kinematicBody; + b2DynamicTree* dynamicTree = world->broadPhase.trees + b2_dynamicBody; + + struct b2ContinuousContext context; + context.world = world; + context.sweep = sweep; + context.fastBodySim = fastBodySim; + context.fraction = 1.0f; + + bool isBullet = fastBodySim->isBullet; + + b2Body* fastBody = b2BodyArray_Get( &world->bodies, fastBodySim->bodyId ); + int shapeId = fastBody->headShapeId; + while ( shapeId != B2_NULL_INDEX ) + { + b2Shape* fastShape = b2ShapeArray_Get( &world->shapes, shapeId ); + B2_ASSERT( fastShape->isFast == true ); + + shapeId = fastShape->nextShapeId; + + // Clear flag (keep set on body) + fastShape->isFast = false; + + context.fastShape = fastShape; + context.centroid1 = b2TransformPoint( xf1, fastShape->localCentroid ); + context.centroid2 = b2TransformPoint( xf2, fastShape->localCentroid ); + + b2AABB box1 = fastShape->aabb; + b2AABB box2 = b2ComputeShapeAABB( fastShape, xf2 ); + b2AABB box = b2AABB_Union( box1, box2 ); + + // Store this for later + fastShape->aabb = box2; + + // No continuous collision for sensors + if ( fastShape->isSensor ) + { + continue; + } + + b2DynamicTree_Query( staticTree, box, b2_defaultMaskBits, b2ContinuousQueryCallback, &context ); + + if ( isBullet ) + { + b2DynamicTree_Query( kinematicTree, box, b2_defaultMaskBits, b2ContinuousQueryCallback, &context ); + b2DynamicTree_Query( dynamicTree, box, b2_defaultMaskBits, b2ContinuousQueryCallback, &context ); + } + } + + const float speculativeDistance = b2_speculativeDistance; + const float aabbMargin = b2_aabbMargin; + + if ( context.fraction < 1.0f ) + { + // Handle time of impact event + b2Rot q = b2NLerp( sweep.q1, sweep.q2, context.fraction ); + b2Vec2 c = b2Lerp( sweep.c1, sweep.c2, context.fraction ); + b2Vec2 origin = b2Sub( c, b2RotateVector( q, sweep.localCenter ) ); + + // Advance body + b2Transform transform = { origin, q }; + fastBodySim->transform = transform; + fastBodySim->center = c; + fastBodySim->rotation0 = q; + fastBodySim->center0 = c; + + // Prepare AABBs for broad-phase + shapeId = fastBody->headShapeId; + while ( shapeId != B2_NULL_INDEX ) + { + b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId ); + + // Must recompute aabb at the interpolated transform + b2AABB aabb = b2ComputeShapeAABB( shape, transform ); + aabb.lowerBound.x -= speculativeDistance; + aabb.lowerBound.y -= speculativeDistance; + aabb.upperBound.x += speculativeDistance; + aabb.upperBound.y += speculativeDistance; + shape->aabb = aabb; + + if ( b2AABB_Contains( shape->fatAABB, aabb ) == false ) + { + b2AABB fatAABB; + fatAABB.lowerBound.x = aabb.lowerBound.x - aabbMargin; + fatAABB.lowerBound.y = aabb.lowerBound.y - aabbMargin; + fatAABB.upperBound.x = aabb.upperBound.x + aabbMargin; + fatAABB.upperBound.y = aabb.upperBound.y + aabbMargin; + shape->fatAABB = fatAABB; + + shape->enlargedAABB = true; + fastBodySim->enlargeAABB = true; + } + + shapeId = shape->nextShapeId; + } + } + else + { + // No time of impact event + + // Advance body + fastBodySim->rotation0 = fastBodySim->transform.q; + fastBodySim->center0 = fastBodySim->center; + + // Prepare AABBs for broad-phase + shapeId = fastBody->headShapeId; + while ( shapeId != B2_NULL_INDEX ) + { + b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId ); + + // shape->aabb is still valid + + if ( b2AABB_Contains( shape->fatAABB, shape->aabb ) == false ) + { + b2AABB fatAABB; + fatAABB.lowerBound.x = shape->aabb.lowerBound.x - aabbMargin; + fatAABB.lowerBound.y = shape->aabb.lowerBound.y - aabbMargin; + fatAABB.upperBound.x = shape->aabb.upperBound.x + aabbMargin; + fatAABB.upperBound.y = shape->aabb.upperBound.y + aabbMargin; + shape->fatAABB = fatAABB; + + shape->enlargedAABB = true; + fastBodySim->enlargeAABB = true; + } + + shapeId = shape->nextShapeId; + } + } +} + +static void b2FastBodyTask( int startIndex, int endIndex, uint32_t threadIndex, void* taskContext ) +{ + B2_MAYBE_UNUSED( threadIndex ); + + b2TracyCZoneNC( fast_body_task, "Fast Body Task", b2_colorCyan, true ); + + b2StepContext* stepContext = taskContext; + + B2_ASSERT( startIndex <= endIndex ); + + for ( int i = startIndex; i < endIndex; ++i ) + { + int simIndex = stepContext->fastBodies[i]; + b2SolveContinuous( stepContext->world, simIndex ); + } + + b2TracyCZoneEnd( fast_body_task ); +} + +static void b2BulletBodyTask( int startIndex, int endIndex, uint32_t threadIndex, void* taskContext ) +{ + B2_MAYBE_UNUSED( threadIndex ); + + b2TracyCZoneNC( bullet_body_task, "Bullet Body Task", b2_colorLightSkyBlue, true ); + + b2StepContext* stepContext = taskContext; + + B2_ASSERT( startIndex <= endIndex ); + + for ( int i = startIndex; i < endIndex; ++i ) + { + int simIndex = stepContext->bulletBodies[i]; + b2SolveContinuous( stepContext->world, simIndex ); + } + + b2TracyCZoneEnd( bullet_body_task ); +} + +#if B2_SIMD_WIDTH == 8 +#define B2_SIMD_SHIFT 3 +#else +#define B2_SIMD_SHIFT 2 +#endif + +// Solve with graph coloring +void b2Solve( b2World* world, b2StepContext* stepContext ) +{ + b2Timer timer = b2CreateTimer(); + + world->stepIndex += 1; + + b2MergeAwakeIslands( world ); + + world->profile.buildIslands = b2GetMillisecondsAndReset( &timer ); + + b2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet ); + int awakeBodyCount = awakeSet->bodySims.count; + if ( awakeBodyCount == 0 ) + { + // Nothing to simulate, however I must still finish the broad-phase rebuild. + if ( world->userTreeTask != NULL ) + { + world->finishTaskFcn( world->userTreeTask, world->userTaskContext ); + world->userTreeTask = NULL; + world->activeTaskCount -= 1; + } + + b2ValidateNoEnlarged( &world->broadPhase ); + return; + } + + b2TracyCZoneNC( solve, "Solve", b2_colorMistyRose, true ); + + // Prepare buffers for continuous collision (fast bodies) + stepContext->fastBodyCount = 0; + stepContext->fastBodies = b2AllocateStackItem( &world->stackAllocator, awakeBodyCount * sizeof( int ), "fast bodies" ); + stepContext->bulletBodyCount = 0; + stepContext->bulletBodies = b2AllocateStackItem( &world->stackAllocator, awakeBodyCount * sizeof( int ), "bullet bodies" ); + + b2TracyCZoneNC( graph_solver, "Graph", b2_colorSeaGreen, true ); + + // Solve constraints using graph coloring + { + b2TracyCZoneNC( prepare_stages, "Prepare Stages", b2_colorDarkOrange, true ); + + b2ConstraintGraph* graph = &world->constraintGraph; + b2GraphColor* colors = graph->colors; + + stepContext->sims = awakeSet->bodySims.data; + stepContext->states = awakeSet->bodyStates.data; + + // count contacts, joints, and colors + int awakeContactCount = 0; + int awakeJointCount = 0; + int activeColorCount = 0; + for ( int i = 0; i < b2_graphColorCount - 1; ++i ) + { + int perColorContactCount = colors[i].contactSims.count; + int perColorJointCount = colors[i].jointSims.count; + int occupancyCount = perColorContactCount + perColorJointCount; + activeColorCount += occupancyCount > 0 ? 1 : 0; + awakeContactCount += perColorContactCount; + awakeJointCount += perColorJointCount; + } + + // Deal with void** + { + b2BodyMoveEventArray_Resize( &world->bodyMoveEvents, awakeBodyCount ); + } + + // Each worker receives at most M blocks of work. The workers may receive less than there is not sufficient work. + // Each block of work has a minimum number of elements (block size). This in turn may limit number of blocks. + // If there are many elements then the block size is increased so there are still at most M blocks of work per worker. + // M is a tunable number that has two goals: + // 1. keep M small to reduce overhead + // 2. keep M large enough for other workers to be able to steal work + // The block size is a power of two to make math efficient. + + int workerCount = world->workerCount; + const int blocksPerWorker = 4; + const int maxBlockCount = blocksPerWorker * workerCount; + + // Configure blocks for tasks that parallel-for bodies + int bodyBlockSize = 1 << 5; + int bodyBlockCount; + if ( awakeBodyCount > bodyBlockSize * maxBlockCount ) + { + // Too many blocks, increase block size + bodyBlockSize = awakeBodyCount / maxBlockCount; + bodyBlockCount = maxBlockCount; + } + else + { + bodyBlockCount = ( ( awakeBodyCount - 1 ) >> 5 ) + 1; + } + + // Configure blocks for tasks parallel-for each active graph color + // The blocks are a mix of SIMD contact blocks and joint blocks + int activeColorIndices[b2_graphColorCount]; + + int colorContactCounts[b2_graphColorCount]; + int colorContactBlockSizes[b2_graphColorCount]; + int colorContactBlockCounts[b2_graphColorCount]; + + int colorJointCounts[b2_graphColorCount]; + int colorJointBlockSizes[b2_graphColorCount]; + int colorJointBlockCounts[b2_graphColorCount]; + + int graphBlockCount = 0; + + // c is the active color index + int simdContactCount = 0; + int c = 0; + for ( int i = 0; i < b2_graphColorCount - 1; ++i ) + { + int colorContactCount = colors[i].contactSims.count; + int colorJointCount = colors[i].jointSims.count; + + if ( colorContactCount + colorJointCount > 0 ) + { + activeColorIndices[c] = i; + + // 4/8-way SIMD + int colorContactCountSIMD = colorContactCount > 0 ? ( ( colorContactCount - 1 ) >> B2_SIMD_SHIFT ) + 1 : 0; + + colorContactCounts[c] = colorContactCountSIMD; + + // determine the number of contact work blocks for this color + if ( colorContactCountSIMD > blocksPerWorker * maxBlockCount ) + { + // too many contact blocks + colorContactBlockSizes[c] = colorContactCountSIMD / maxBlockCount; + colorContactBlockCounts[c] = maxBlockCount; + } + else if ( colorContactCountSIMD > 0 ) + { + // dividing by blocksPerWorker (4) + colorContactBlockSizes[c] = blocksPerWorker; + colorContactBlockCounts[c] = ( ( colorContactCountSIMD - 1 ) >> 2 ) + 1; + } + else + { + // no contacts in this color + colorContactBlockSizes[c] = 0; + colorContactBlockCounts[c] = 0; + } + + colorJointCounts[c] = colorJointCount; + + // determine number of joint work blocks for this color + if ( colorJointCount > blocksPerWorker * maxBlockCount ) + { + // too many joint blocks + colorJointBlockSizes[c] = colorJointCount / maxBlockCount; + colorJointBlockCounts[c] = maxBlockCount; + } + else if ( colorJointCount > 0 ) + { + // dividing by blocksPerWorker (4) + colorJointBlockSizes[c] = blocksPerWorker; + colorJointBlockCounts[c] = ( ( colorJointCount - 1 ) >> 2 ) + 1; + } + else + { + colorJointBlockSizes[c] = 0; + colorJointBlockCounts[c] = 0; + } + + graphBlockCount += colorContactBlockCounts[c] + colorJointBlockCounts[c]; + simdContactCount += colorContactCountSIMD; + c += 1; + } + } + activeColorCount = c; + + // Gather contact pointers for easy parallel-for traversal. Some may be NULL due to SIMD remainders. + b2ContactSim** contacts = b2AllocateStackItem( + &world->stackAllocator, B2_SIMD_WIDTH * simdContactCount * sizeof( b2ContactSim* ), "contact pointers" ); + + // Gather joint pointers for easy parallel-for traversal. + b2JointSim** joints = + b2AllocateStackItem( &world->stackAllocator, awakeJointCount * sizeof( b2JointSim* ), "joint pointers" ); + + int simdConstraintSize = b2GetContactConstraintSIMDByteCount(); + b2ContactConstraintSIMD* simdContactConstraints = + b2AllocateStackItem( &world->stackAllocator, simdContactCount * simdConstraintSize, "contact constraint" ); + + int overflowContactCount = colors[b2_overflowIndex].contactSims.count; + b2ContactConstraint* overflowContactConstraints = b2AllocateStackItem( + &world->stackAllocator, overflowContactCount * sizeof( b2ContactConstraint ), "overflow contact constraint" ); + + graph->colors[b2_overflowIndex].overflowConstraints = overflowContactConstraints; + + // Distribute transient constraints to each graph color and build flat arrays of contact and joint pointers + { + int contactBase = 0; + int jointBase = 0; + for ( int i = 0; i < activeColorCount; ++i ) + { + int j = activeColorIndices[i]; + b2GraphColor* color = colors + j; + + int colorContactCount = color->contactSims.count; + + if ( colorContactCount == 0 ) + { + color->simdConstraints = NULL; + } + else + { + color->simdConstraints = + (b2ContactConstraintSIMD*)( (uint8_t*)simdContactConstraints + contactBase * simdConstraintSize ); + + for ( int k = 0; k < colorContactCount; ++k ) + { + contacts[B2_SIMD_WIDTH * contactBase + k] = color->contactSims.data + k; + } + + // remainder + int colorContactCountSIMD = ( ( colorContactCount - 1 ) >> B2_SIMD_SHIFT ) + 1; + for ( int k = colorContactCount; k < B2_SIMD_WIDTH * colorContactCountSIMD; ++k ) + { + contacts[B2_SIMD_WIDTH * contactBase + k] = NULL; + } + + contactBase += colorContactCountSIMD; + } + + int colorJointCount = color->jointSims.count; + for ( int k = 0; k < colorJointCount; ++k ) + { + joints[jointBase + k] = color->jointSims.data + k; + } + jointBase += colorJointCount; + } + + B2_ASSERT( contactBase == simdContactCount ); + B2_ASSERT( jointBase == awakeJointCount ); + } + + // Define work blocks for preparing contacts and storing contact impulses + int contactBlockSize = blocksPerWorker; + int contactBlockCount = simdContactCount > 0 ? ( ( simdContactCount - 1 ) >> 2 ) + 1 : 0; + if ( simdContactCount > contactBlockSize * maxBlockCount ) + { + // Too many blocks, increase block size + contactBlockSize = simdContactCount / maxBlockCount; + contactBlockCount = maxBlockCount; + } + + // Define work blocks for preparing joints + int jointBlockSize = blocksPerWorker; + int jointBlockCount = awakeJointCount > 0 ? ( ( awakeJointCount - 1 ) >> 2 ) + 1 : 0; + if ( awakeJointCount > jointBlockSize * maxBlockCount ) + { + // Too many blocks, increase block size + jointBlockSize = awakeJointCount / maxBlockCount; + jointBlockCount = maxBlockCount; + } + + int stageCount = 0; + + // b2_stagePrepareJoints + stageCount += 1; + // b2_stagePrepareContacts + stageCount += 1; + // b2_stageIntegrateVelocities + stageCount += 1; + // b2_stageWarmStart + stageCount += activeColorCount; + // b2_stageSolve + stageCount += activeColorCount; + // b2_stageIntegratePositions + stageCount += 1; + // b2_stageRelax + stageCount += activeColorCount; + // b2_stageRestitution + stageCount += activeColorCount; + // b2_stageStoreImpulses + stageCount += 1; + + b2SolverStage* stages = b2AllocateStackItem( &world->stackAllocator, stageCount * sizeof( b2SolverStage ), "stages" ); + b2SolverBlock* bodyBlocks = + b2AllocateStackItem( &world->stackAllocator, bodyBlockCount * sizeof( b2SolverBlock ), "body blocks" ); + b2SolverBlock* contactBlocks = + b2AllocateStackItem( &world->stackAllocator, contactBlockCount * sizeof( b2SolverBlock ), "contact blocks" ); + b2SolverBlock* jointBlocks = + b2AllocateStackItem( &world->stackAllocator, jointBlockCount * sizeof( b2SolverBlock ), "joint blocks" ); + b2SolverBlock* graphBlocks = + b2AllocateStackItem( &world->stackAllocator, graphBlockCount * sizeof( b2SolverBlock ), "graph blocks" ); + + // Split an awake island. This modifies: + // - stack allocator + // - world island array and solver set + // - island indices on bodies, contacts, and joints + // I'm squeezing this task in here because it may be expensive and this is a safe place to put it. + // Note: cannot split islands in parallel with FinalizeBodies + void* splitIslandTask = NULL; + if ( world->splitIslandId != B2_NULL_INDEX ) + { + splitIslandTask = world->enqueueTaskFcn( &b2SplitIslandTask, 1, 1, world, world->userTaskContext ); + world->taskCount += 1; + world->activeTaskCount += splitIslandTask == NULL ? 0 : 1; + } + + // Prepare body work blocks + for ( int i = 0; i < bodyBlockCount; ++i ) + { + b2SolverBlock* block = bodyBlocks + i; + block->startIndex = i * bodyBlockSize; + block->count = (int16_t)bodyBlockSize; + block->blockType = b2_bodyBlock; + block->syncIndex = 0; + } + bodyBlocks[bodyBlockCount - 1].count = (int16_t)( awakeBodyCount - ( bodyBlockCount - 1 ) * bodyBlockSize ); + + // Prepare joint work blocks + for ( int i = 0; i < jointBlockCount; ++i ) + { + b2SolverBlock* block = jointBlocks + i; + block->startIndex = i * jointBlockSize; + block->count = (int16_t)jointBlockSize; + block->blockType = b2_jointBlock; + block->syncIndex = 0; + } + + if ( jointBlockCount > 0 ) + { + jointBlocks[jointBlockCount - 1].count = (int16_t)( awakeJointCount - ( jointBlockCount - 1 ) * jointBlockSize ); + } + + // Prepare contact work blocks + for ( int i = 0; i < contactBlockCount; ++i ) + { + b2SolverBlock* block = contactBlocks + i; + block->startIndex = i * contactBlockSize; + block->count = (int16_t)contactBlockSize; + block->blockType = b2_contactBlock; + block->syncIndex = 0; + } + + if ( contactBlockCount > 0 ) + { + contactBlocks[contactBlockCount - 1].count = + (int16_t)( simdContactCount - ( contactBlockCount - 1 ) * contactBlockSize ); + } + + // Prepare graph work blocks + b2SolverBlock* graphColorBlocks[b2_graphColorCount]; + b2SolverBlock* baseGraphBlock = graphBlocks; + + for ( int i = 0; i < activeColorCount; ++i ) + { + graphColorBlocks[i] = baseGraphBlock; + + int colorJointBlockCount = colorJointBlockCounts[i]; + int colorJointBlockSize = colorJointBlockSizes[i]; + for ( int j = 0; j < colorJointBlockCount; ++j ) + { + b2SolverBlock* block = baseGraphBlock + j; + block->startIndex = j * colorJointBlockSize; + block->count = (int16_t)colorJointBlockSize; + block->blockType = b2_graphJointBlock; + block->syncIndex = 0; + } + + if ( colorJointBlockCount > 0 ) + { + baseGraphBlock[colorJointBlockCount - 1].count = + (int16_t)( colorJointCounts[i] - ( colorJointBlockCount - 1 ) * colorJointBlockSize ); + baseGraphBlock += colorJointBlockCount; + } + + int colorContactBlockCount = colorContactBlockCounts[i]; + int colorContactBlockSize = colorContactBlockSizes[i]; + for ( int j = 0; j < colorContactBlockCount; ++j ) + { + b2SolverBlock* block = baseGraphBlock + j; + block->startIndex = j * colorContactBlockSize; + block->count = (int16_t)colorContactBlockSize; + block->blockType = b2_graphContactBlock; + block->syncIndex = 0; + } + + if ( colorContactBlockCount > 0 ) + { + baseGraphBlock[colorContactBlockCount - 1].count = + (int16_t)( colorContactCounts[i] - ( colorContactBlockCount - 1 ) * colorContactBlockSize ); + baseGraphBlock += colorContactBlockCount; + } + } + + ptrdiff_t blockDiff = baseGraphBlock - graphBlocks; + B2_ASSERT( blockDiff == graphBlockCount ); + + b2SolverStage* stage = stages; + + // Prepare joints + stage->type = b2_stagePrepareJoints; + stage->blocks = jointBlocks; + stage->blockCount = jointBlockCount; + stage->colorIndex = -1; + stage->completionCount = 0; + stage += 1; + + // Prepare contacts + stage->type = b2_stagePrepareContacts; + stage->blocks = contactBlocks; + stage->blockCount = contactBlockCount; + stage->colorIndex = -1; + stage->completionCount = 0; + stage += 1; + + // Integrate velocities + stage->type = b2_stageIntegrateVelocities; + stage->blocks = bodyBlocks; + stage->blockCount = bodyBlockCount; + stage->colorIndex = -1; + stage->completionCount = 0; + stage += 1; + + // Warm start + for ( int i = 0; i < activeColorCount; ++i ) + { + stage->type = b2_stageWarmStart; + stage->blocks = graphColorBlocks[i]; + stage->blockCount = colorJointBlockCounts[i] + colorContactBlockCounts[i]; + stage->colorIndex = activeColorIndices[i]; + stage->completionCount = 0; + stage += 1; + } + + // Solve graph + for ( int i = 0; i < activeColorCount; ++i ) + { + stage->type = b2_stageSolve; + stage->blocks = graphColorBlocks[i]; + stage->blockCount = colorJointBlockCounts[i] + colorContactBlockCounts[i]; + stage->colorIndex = activeColorIndices[i]; + stage->completionCount = 0; + stage += 1; + } + + // Integrate positions + stage->type = b2_stageIntegratePositions; + stage->blocks = bodyBlocks; + stage->blockCount = bodyBlockCount; + stage->colorIndex = -1; + stage->completionCount = 0; + stage += 1; + + // Relax constraints + for ( int i = 0; i < activeColorCount; ++i ) + { + stage->type = b2_stageRelax; + stage->blocks = graphColorBlocks[i]; + stage->blockCount = colorJointBlockCounts[i] + colorContactBlockCounts[i]; + stage->colorIndex = activeColorIndices[i]; + stage->completionCount = 0; + stage += 1; + } + + // Restitution + // Note: joint blocks mixed in, could have joint limit restitution + for ( int i = 0; i < activeColorCount; ++i ) + { + stage->type = b2_stageRestitution; + stage->blocks = graphColorBlocks[i]; + stage->blockCount = colorJointBlockCounts[i] + colorContactBlockCounts[i]; + stage->colorIndex = activeColorIndices[i]; + stage->completionCount = 0; + stage += 1; + } + + // Store impulses + stage->type = b2_stageStoreImpulses; + stage->blocks = contactBlocks; + stage->blockCount = contactBlockCount; + stage->colorIndex = -1; + stage->completionCount = 0; + stage += 1; + + B2_ASSERT( (int)( stage - stages ) == stageCount ); + + B2_ASSERT( workerCount <= b2_maxWorkers ); + b2WorkerContext workerContext[b2_maxWorkers]; + + stepContext->graph = graph; + stepContext->joints = joints; + stepContext->contacts = contacts; + stepContext->simdContactConstraints = simdContactConstraints; + stepContext->activeColorCount = activeColorCount; + stepContext->workerCount = workerCount; + stepContext->stageCount = stageCount; + stepContext->stages = stages; + stepContext->atomicSyncBits = 0; + + world->profile.prepareTasks = b2GetMillisecondsAndReset( &timer ); + + b2TracyCZoneEnd( prepare_stages ); + + // Must use worker index because thread 0 can be assigned multiple tasks by enkiTS + for ( int i = 0; i < workerCount; ++i ) + { + workerContext[i].context = stepContext; + workerContext[i].workerIndex = i; + workerContext[i].userTask = world->enqueueTaskFcn( b2SolverTask, 1, 1, workerContext + i, world->userTaskContext ); + world->taskCount += 1; + world->activeTaskCount += workerContext[i].userTask == NULL ? 0 : 1; + } + + // Finish island split + if ( splitIslandTask != NULL ) + { + world->finishTaskFcn( splitIslandTask, world->userTaskContext ); + world->activeTaskCount -= 1; + } + world->splitIslandId = B2_NULL_INDEX; + + // Finish constraint solve + for ( int i = 0; i < workerCount; ++i ) + { + if ( workerContext[i].userTask != NULL ) + { + world->finishTaskFcn( workerContext[i].userTask, world->userTaskContext ); + world->activeTaskCount -= 1; + } + } + + world->profile.solverTasks = b2GetMillisecondsAndReset( &timer ); + + // Prepare contact, enlarged body, and island bit sets used in body finalization. + int awakeIslandCount = awakeSet->islandSims.count; + for ( int i = 0; i < world->workerCount; ++i ) + { + b2TaskContext* taskContext = world->taskContexts.data + i; + b2SetBitCountAndClear( &taskContext->enlargedSimBitSet, awakeBodyCount ); + b2SetBitCountAndClear( &taskContext->awakeIslandBitSet, awakeIslandCount ); + taskContext->splitIslandId = B2_NULL_INDEX; + taskContext->splitSleepTime = 0.0f; + } + + // Finalize bodies. Must happen after the constraint solver and after island splitting. + void* finalizeBodiesTask = + world->enqueueTaskFcn( b2FinalizeBodiesTask, awakeBodyCount, 64, stepContext, world->userTaskContext ); + world->taskCount += 1; + if ( finalizeBodiesTask != NULL ) + { + world->finishTaskFcn( finalizeBodiesTask, world->userTaskContext ); + } + + world->profile.finalizeBodies = b2GetMillisecondsAndReset( &timer ); + + b2FreeStackItem( &world->stackAllocator, graphBlocks ); + b2FreeStackItem( &world->stackAllocator, jointBlocks ); + b2FreeStackItem( &world->stackAllocator, contactBlocks ); + b2FreeStackItem( &world->stackAllocator, bodyBlocks ); + b2FreeStackItem( &world->stackAllocator, stages ); + b2FreeStackItem( &world->stackAllocator, overflowContactConstraints ); + b2FreeStackItem( &world->stackAllocator, simdContactConstraints ); + b2FreeStackItem( &world->stackAllocator, joints ); + b2FreeStackItem( &world->stackAllocator, contacts ); + } + + b2TracyCZoneEnd( graph_solver ); + world->profile.solveConstraints = b2GetMillisecondsAndReset( &timer ); + + // Report hit events + // todo perhaps optimize this with a bitset + { + b2TracyCZoneNC( hit_events, "Hit", b2_colorVioletRed, true ); + + B2_ASSERT( world->contactHitEvents.count == 0 ); + + float threshold = world->hitEventThreshold; + b2GraphColor* colors = world->constraintGraph.colors; + for ( int i = 0; i < b2_graphColorCount; ++i ) + { + b2GraphColor* color = colors + i; + int contactCount = color->contactSims.count; + b2ContactSim* contactSims = color->contactSims.data; + for ( int j = 0; j < contactCount; ++j ) + { + b2ContactSim* contactSim = contactSims + j; + if ( ( contactSim->simFlags & b2_simEnableHitEvent ) == 0 ) + { + continue; + } + + b2ContactHitEvent event = { 0 }; + event.approachSpeed = threshold; + + bool hit = false; + int pointCount = contactSim->manifold.pointCount; + for ( int k = 0; k < pointCount; ++k ) + { + b2ManifoldPoint* mp = contactSim->manifold.points + k; + float approachSpeed = -mp->normalVelocity; + if ( approachSpeed > event.approachSpeed && mp->normalImpulse > 0.0f ) + { + event.approachSpeed = approachSpeed; + event.point = mp->point; + hit = true; + } + } + + if ( hit == true ) + { + event.normal = contactSim->manifold.normal; + + b2Shape* shapeA = b2ShapeArray_Get( &world->shapes, contactSim->shapeIdA ); + b2Shape* shapeB = b2ShapeArray_Get( &world->shapes, contactSim->shapeIdB ); + + event.shapeIdA = ( b2ShapeId ){ shapeA->id + 1, world->worldId, shapeA->revision }; + event.shapeIdB = ( b2ShapeId ){ shapeB->id + 1, world->worldId, shapeB->revision }; + + b2ContactHitEventArray_Push( &world->contactHitEvents, event ); + } + } + } + + b2TracyCZoneEnd( hit_events ); + } + + world->profile.hitEvents = b2GetMillisecondsAndReset( &timer ); + + // Finish the user tree task that was queued earlier in the time step. This must be complete before touching the broad-phase. + if ( world->userTreeTask != NULL ) + { + world->finishTaskFcn( world->userTreeTask, world->userTaskContext ); + world->userTreeTask = NULL; + world->activeTaskCount -= 1; + } + + b2ValidateNoEnlarged( &world->broadPhase ); + + b2TracyCZoneNC( broad_phase, "Broadphase", b2_colorPurple, true ); + + b2TracyCZoneNC( enlarge_proxies, "Enlarge Proxies", b2_colorDarkTurquoise, true ); + + // Gather bits for all sim bodies that have enlarged AABBs + b2BitSet* simBitSet = &world->taskContexts.data[0].enlargedSimBitSet; + for ( int i = 1; i < world->workerCount; ++i ) + { + b2InPlaceUnion( simBitSet, &world->taskContexts.data[i].enlargedSimBitSet ); + } + + // Enlarge broad-phase proxies and build move array + // Apply shape AABB changes to broad-phase. This also create the move array which must be + // in deterministic order. I'm tracking sim bodies because the number of shape ids can be huge. + { + b2BroadPhase* broadPhase = &world->broadPhase; + uint32_t wordCount = simBitSet->blockCount; + uint64_t* bits = simBitSet->bits; + + // Fast array access is important here + b2Body* bodyArray = world->bodies.data; + b2BodySim* bodySimArray = awakeSet->bodySims.data; + b2Shape* shapeArray = world->shapes.data; + + for ( uint32_t k = 0; k < wordCount; ++k ) + { + uint64_t word = bits[k]; + while ( word != 0 ) + { + uint32_t ctz = b2CTZ64( word ); + uint32_t bodySimIndex = 64 * k + ctz; + + b2BodySim* bodySim = bodySimArray + bodySimIndex; + b2Body* body = bodyArray + bodySim->bodyId; + + int shapeId = body->headShapeId; + while ( shapeId != B2_NULL_INDEX ) + { + b2Shape* shape = shapeArray + shapeId; + + if ( shape->enlargedAABB ) + { + B2_ASSERT( shape->isFast == false ); + + b2BroadPhase_EnlargeProxy( broadPhase, shape->proxyKey, shape->fatAABB ); + shape->enlargedAABB = false; + } + else if ( shape->isFast ) + { + // Shape is fast. It's aabb will be enlarged in continuous collision. + b2BufferMove( broadPhase, shape->proxyKey ); + } + + shapeId = shape->nextShapeId; + } + + // Clear the smallest set bit + word = word & ( word - 1 ); + } + } + } + + b2TracyCZoneEnd( enlarge_proxies ); + + b2ValidateBroadphase( &world->broadPhase ); + + world->profile.broadphase = b2GetMillisecondsAndReset( &timer ); + + b2TracyCZoneEnd( broad_phase ); + + b2TracyCZoneNC( continuous_collision, "Continuous", b2_colorDarkGoldenrod, true ); + + // Parallel continuous collision + if ( stepContext->fastBodyCount > 0 ) + { + // fast bodies + int minRange = 8; + void* userFastBodyTask = + world->enqueueTaskFcn( &b2FastBodyTask, stepContext->fastBodyCount, minRange, stepContext, world->userTaskContext ); + world->taskCount += 1; + if ( userFastBodyTask != NULL ) + { + world->finishTaskFcn( userFastBodyTask, world->userTaskContext ); + } + } + + // Serially enlarge broad-phase proxies for fast shapes + // Doing this here so that bullet shapes see them + { + b2BroadPhase* broadPhase = &world->broadPhase; + b2DynamicTree* dynamicTree = broadPhase->trees + b2_dynamicBody; + + // Fast array access is important here + b2Body* bodyArray = world->bodies.data; + b2BodySim* bodySimArray = awakeSet->bodySims.data; + b2Shape* shapeArray = world->shapes.data; + + int* fastBodySimIndices = stepContext->fastBodies; + int fastBodyCount = stepContext->fastBodyCount; + + // This loop has non-deterministic order but it shouldn't affect the result + for ( int i = 0; i < fastBodyCount; ++i ) + { + b2BodySim* fastBodySim = bodySimArray + fastBodySimIndices[i]; + if ( fastBodySim->enlargeAABB == false ) + { + continue; + } + + // clear flag + fastBodySim->enlargeAABB = false; + + int bodyId = fastBodySim->bodyId; + + B2_ASSERT( 0 <= bodyId && bodyId < world->bodies.count ); + b2Body* fastBody = bodyArray + bodyId; + + int shapeId = fastBody->headShapeId; + while ( shapeId != B2_NULL_INDEX ) + { + b2Shape* shape = shapeArray + shapeId; + if ( shape->enlargedAABB == false ) + { + shapeId = shape->nextShapeId; + continue; + } + + // clear flag + shape->enlargedAABB = false; + + int proxyKey = shape->proxyKey; + int proxyId = B2_PROXY_ID( proxyKey ); + B2_ASSERT( B2_PROXY_TYPE( proxyKey ) == b2_dynamicBody ); + + // all fast shapes should already be in the move buffer + B2_ASSERT( b2ContainsKey( &broadPhase->moveSet, proxyKey + 1 ) ); + + b2DynamicTree_EnlargeProxy( dynamicTree, proxyId, shape->fatAABB ); + + shapeId = shape->nextShapeId; + } + } + } + + if ( stepContext->bulletBodyCount > 0 ) + { + // bullet bodies + int minRange = 8; + void* userBulletBodyTask = world->enqueueTaskFcn( &b2BulletBodyTask, stepContext->bulletBodyCount, minRange, stepContext, + world->userTaskContext ); + world->taskCount += 1; + if ( userBulletBodyTask != NULL ) + { + world->finishTaskFcn( userBulletBodyTask, world->userTaskContext ); + } + } + + // Serially enlarge broad-phase proxies for bullet shapes + { + b2BroadPhase* broadPhase = &world->broadPhase; + b2DynamicTree* dynamicTree = broadPhase->trees + b2_dynamicBody; + + // Fast array access is important here + b2Body* bodyArray = world->bodies.data; + b2BodySim* bodySimArray = awakeSet->bodySims.data; + b2Shape* shapeArray = world->shapes.data; + + // Serially enlarge broad-phase proxies for bullet shapes + int* bulletBodySimIndices = stepContext->bulletBodies; + int bulletBodyCount = stepContext->bulletBodyCount; + + // This loop has non-deterministic order but it shouldn't affect the result + for ( int i = 0; i < bulletBodyCount; ++i ) + { + b2BodySim* bulletBodySim = bodySimArray + bulletBodySimIndices[i]; + if ( bulletBodySim->enlargeAABB == false ) + { + continue; + } + + // clear flag + bulletBodySim->enlargeAABB = false; + + int bodyId = bulletBodySim->bodyId; + B2_ASSERT( 0 <= bodyId && bodyId < world->bodies.count ); + b2Body* bulletBody = bodyArray + bodyId; + + int shapeId = bulletBody->headShapeId; + while ( shapeId != B2_NULL_INDEX ) + { + b2Shape* shape = shapeArray + shapeId; + if ( shape->enlargedAABB == false ) + { + shapeId = shape->nextShapeId; + continue; + } + + // clear flag + shape->enlargedAABB = false; + + int proxyKey = shape->proxyKey; + int proxyId = B2_PROXY_ID( proxyKey ); + B2_ASSERT( B2_PROXY_TYPE( proxyKey ) == b2_dynamicBody ); + + // all fast shapes should already be in the move buffer + B2_ASSERT( b2ContainsKey( &broadPhase->moveSet, proxyKey + 1 ) ); + + b2DynamicTree_EnlargeProxy( dynamicTree, proxyId, shape->fatAABB ); + + shapeId = shape->nextShapeId; + } + } + } + + b2TracyCZoneEnd( continuous_collision ); + + b2FreeStackItem( &world->stackAllocator, stepContext->bulletBodies ); + stepContext->bulletBodies = NULL; + stepContext->bulletBodyCount = 0; + + b2FreeStackItem( &world->stackAllocator, stepContext->fastBodies ); + stepContext->fastBodies = NULL; + stepContext->fastBodyCount = 0; + + world->profile.continuous = b2GetMillisecondsAndReset( &timer ); + + // Island sleeping + // This must be done last because putting islands to sleep invalidates the enlarged body bits. + if ( world->enableSleep == true ) + { + b2TracyCZoneNC( sleep_islands, "Island Sleep", b2_colorGainsboro, true ); + + // Collect split island candidate for the next time step. No need to split if sleeping is disabled. + B2_ASSERT( world->splitIslandId == B2_NULL_INDEX ); + float splitSleepTimer = 0.0f; + for ( int i = 0; i < world->workerCount; ++i ) + { + b2TaskContext* taskContext = world->taskContexts.data + i; + if ( taskContext->splitIslandId != B2_NULL_INDEX && taskContext->splitSleepTime >= splitSleepTimer ) + { + B2_ASSERT( taskContext->splitSleepTime > 0.0f ); + + // Tie breaking for determinism. Largest island id wins. Needed due to work stealing. + if ( taskContext->splitSleepTime == splitSleepTimer && taskContext->splitIslandId < world->splitIslandId ) + { + continue; + } + + world->splitIslandId = taskContext->splitIslandId; + splitSleepTimer = taskContext->splitSleepTime; + } + } + + b2BitSet* awakeIslandBitSet = &world->taskContexts.data[0].awakeIslandBitSet; + for ( int i = 1; i < world->workerCount; ++i ) + { + b2InPlaceUnion( awakeIslandBitSet, &world->taskContexts.data[i].awakeIslandBitSet ); + } + + // Need to process in reverse because this moves islands to sleeping solver sets. + b2IslandSim* islands = awakeSet->islandSims.data; + int count = awakeSet->islandSims.count; + for ( int islandIndex = count - 1; islandIndex >= 0; islandIndex -= 1 ) + { + if ( b2GetBit( awakeIslandBitSet, islandIndex ) == true ) + { + // this island is still awake + continue; + } + + b2IslandSim* island = islands + islandIndex; + int islandId = island->islandId; + + b2TrySleepIsland( world, islandId ); + } + + b2ValidateSolverSets( world ); + + b2TracyCZoneEnd( sleep_islands ); + } + + world->profile.sleepIslands = b2GetMillisecondsAndReset( &timer ); + b2TracyCZoneEnd( solve ); +} diff --git a/3rdparty/box2d/src/solver.h b/3rdparty/box2d/src/solver.h new file mode 100644 index 000000000000..7d5cedd2226d --- /dev/null +++ b/3rdparty/box2d/src/solver.h @@ -0,0 +1,157 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include "box2d/math_functions.h" + +#include +#include + +typedef struct b2BodySim b2BodySim; +typedef struct b2BodyState b2BodyState; +typedef struct b2ContactSim b2ContactSim; +typedef struct b2JointSim b2JointSim; +typedef struct b2World b2World; + +typedef struct b2Softness +{ + float biasRate; + float massScale; + float impulseScale; +} b2Softness; + +typedef enum b2SolverStageType +{ + b2_stagePrepareJoints, + b2_stagePrepareContacts, + b2_stageIntegrateVelocities, + b2_stageWarmStart, + b2_stageSolve, + b2_stageIntegratePositions, + b2_stageRelax, + b2_stageRestitution, + b2_stageStoreImpulses +} b2SolverStageType; + +typedef enum b2SolverBlockType +{ + b2_bodyBlock, + b2_jointBlock, + b2_contactBlock, + b2_graphJointBlock, + b2_graphContactBlock +} b2SolverBlockType; + +// Each block of work has a sync index that gets incremented when a worker claims the block. This ensures only a single worker +// claims a block, yet lets work be distributed dynamically across multiple workers (work stealing). This also reduces contention +// on a single block index atomic. For non-iterative stages the sync index is simply set to one. For iterative stages (solver +// iteration) the same block of work is executed once per iteration and the atomic sync index is shared across iterations, so it +// increases monotonically. +typedef struct b2SolverBlock +{ + int startIndex; + int16_t count; + int16_t blockType; // b2SolverBlockType + // todo consider false sharing of this atomic + _Atomic int syncIndex; +} b2SolverBlock; + +// Each stage must be completed before going to the next stage. +// Non-iterative stages use a stage instance once while iterative stages re-use the same instance each iteration. +typedef struct b2SolverStage +{ + b2SolverStageType type; + b2SolverBlock* blocks; + int blockCount; + int colorIndex; + // todo consider false sharing of this atomic + _Atomic int completionCount; +} b2SolverStage; + +// Context for a time step. Recreated each time step. +typedef struct b2StepContext +{ + // time step + float dt; + + // inverse time step (0 if dt == 0). + float inv_dt; + + // sub-step + float h; + float inv_h; + + int subStepCount; + + b2Softness jointSoftness; + b2Softness contactSoftness; + b2Softness staticSoftness; + + float restitutionThreshold; + float maxLinearVelocity; + + struct b2World* world; + struct b2ConstraintGraph* graph; + + // shortcut to body states from awake set + b2BodyState* states; + + // shortcut to body sims from awake set + b2BodySim* sims; + + // array of all shape ids for shapes that have enlarged AABBs + int* enlargedShapes; + int enlargedShapeCount; + + // Array of fast bodies that need continuous collision handling + int* fastBodies; + _Atomic int fastBodyCount; + + // Array of bullet bodies that need continuous collision handling + int* bulletBodies; + _Atomic int bulletBodyCount; + + // joint pointers for simplified parallel-for access. + b2JointSim** joints; + + // contact pointers for simplified parallel-for access. + // - parallel-for collide with no gaps + // - parallel-for prepare and store contacts with NULL gaps for SIMD remainders + // despite being an array of pointers, these are contiguous sub-arrays corresponding + // to constraint graph colors + b2ContactSim** contacts; + + struct b2ContactConstraintSIMD* simdContactConstraints; + int activeColorCount; + int workerCount; + + b2SolverStage* stages; + int stageCount; + bool enableWarmStarting; + + // todo padding to prevent false sharing + char dummy1[64]; + + // sync index (16-bits) | stage type (16-bits) + _Atomic unsigned int atomicSyncBits; + + char dummy2[64]; + +} b2StepContext; + +static inline b2Softness b2MakeSoft( float hertz, float zeta, float h ) +{ + if ( hertz == 0.0f ) + { + return ( b2Softness ){ 0.0f, 1.0f, 0.0f }; + } + + float omega = 2.0f * b2_pi * hertz; + float a1 = 2.0f * zeta + h * omega; + float a2 = h * omega * a1; + float a3 = 1.0f / ( 1.0f + a2 ); + return ( b2Softness ){ omega / a1, a2 * a3, a3 }; +} + +void b2Solve( b2World* world, b2StepContext* stepContext ); diff --git a/3rdparty/box2d/src/solver_set.c b/3rdparty/box2d/src/solver_set.c new file mode 100644 index 000000000000..4f59b08cb3d6 --- /dev/null +++ b/3rdparty/box2d/src/solver_set.c @@ -0,0 +1,613 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#include "solver_set.h" + +#include "body.h" +#include "constraint_graph.h" +#include "contact.h" +#include "core.h" +#include "island.h" +#include "joint.h" +#include "world.h" + +#include + +B2_ARRAY_SOURCE( b2SolverSet, b2SolverSet ); + +void b2DestroySolverSet( b2World* world, int setIndex ) +{ + b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, setIndex ); + b2BodySimArray_Destroy( &set->bodySims ); + b2BodyStateArray_Destroy( &set->bodyStates ); + b2ContactSimArray_Destroy( &set->contactSims ); + b2JointSimArray_Destroy( &set->jointSims ); + b2IslandSimArray_Destroy( &set->islandSims ); + b2FreeId( &world->solverSetIdPool, setIndex ); + *set = ( b2SolverSet ){ 0 }; + set->setIndex = B2_NULL_INDEX; +} + +// Wake a solver set. Does not merge islands. +// Contacts can be in several places: +// 1. non-touching contacts in the disabled set +// 2. non-touching contacts already in the awake set +// 3. touching contacts in the sleeping set +// This handles contact types 1 and 3. Type 2 doesn't need any action. +void b2WakeSolverSet( b2World* world, int setIndex ) +{ + B2_ASSERT( setIndex >= b2_firstSleepingSet ); + b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, setIndex ); + b2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet ); + b2SolverSet* disabledSet = b2SolverSetArray_Get( &world->solverSets, b2_disabledSet ); + + b2Body* bodies = world->bodies.data; + + int bodyCount = set->bodySims.count; + for ( int i = 0; i < bodyCount; ++i ) + { + b2BodySim* simSrc = set->bodySims.data + i; + + b2Body* body = bodies + simSrc->bodyId; + B2_ASSERT( body->setIndex == setIndex ); + body->setIndex = b2_awakeSet; + body->localIndex = awakeSet->bodySims.count; + + // Reset sleep timer + body->sleepTime = 0.0f; + + b2BodySim* simDst = b2BodySimArray_Add( &awakeSet->bodySims ); + memcpy( simDst, simSrc, sizeof( b2BodySim ) ); + + b2BodyState* state = b2BodyStateArray_Add( &awakeSet->bodyStates ); + *state = b2_identityBodyState; + + // move non-touching contacts from disabled set to awake set + int contactKey = body->headContactKey; + while ( contactKey != B2_NULL_INDEX ) + { + int edgeIndex = contactKey & 1; + int contactId = contactKey >> 1; + + b2Contact* contact = b2ContactArray_Get( &world->contacts, contactId ); + + contactKey = contact->edges[edgeIndex].nextKey; + + if ( contact->setIndex != b2_disabledSet ) + { + B2_ASSERT( contact->setIndex == b2_awakeSet || contact->setIndex == setIndex ); + continue; + } + + int localIndex = contact->localIndex; + b2ContactSim* contactSim = b2ContactSimArray_Get( &disabledSet->contactSims, localIndex ); + + B2_ASSERT( ( contact->flags & b2_contactTouchingFlag ) == 0 && contactSim->manifold.pointCount == 0 ); + + contact->setIndex = b2_awakeSet; + contact->localIndex = awakeSet->contactSims.count; + b2ContactSim* awakeContactSim = b2ContactSimArray_Add( &awakeSet->contactSims ); + memcpy( awakeContactSim, contactSim, sizeof( b2ContactSim ) ); + + int movedLocalIndex = b2ContactSimArray_RemoveSwap( &disabledSet->contactSims, localIndex ); + if ( movedLocalIndex != B2_NULL_INDEX ) + { + // fix moved element + b2ContactSim* movedContactSim = disabledSet->contactSims.data + localIndex; + b2Contact* movedContact = b2ContactArray_Get( &world->contacts, movedContactSim->contactId ); + B2_ASSERT( movedContact->localIndex == movedLocalIndex ); + movedContact->localIndex = localIndex; + } + } + } + + // transfer touching contacts from sleeping set to contact graph + { + int contactCount = set->contactSims.count; + for ( int i = 0; i < contactCount; ++i ) + { + b2ContactSim* contactSim = set->contactSims.data + i; + b2Contact* contact = b2ContactArray_Get( &world->contacts, contactSim->contactId ); + B2_ASSERT( contact->flags & b2_contactTouchingFlag ); + B2_ASSERT( contactSim->simFlags & b2_simTouchingFlag ); + B2_ASSERT( contactSim->manifold.pointCount > 0 ); + B2_ASSERT( contact->setIndex == setIndex ); + b2AddContactToGraph( world, contactSim, contact ); + contact->setIndex = b2_awakeSet; + } + } + + // transfer joints from sleeping set to awake set + { + int jointCount = set->jointSims.count; + for ( int i = 0; i < jointCount; ++i ) + { + b2JointSim* jointSim = set->jointSims.data + i; + b2Joint* joint = b2JointArray_Get( &world->joints, +jointSim->jointId ); + B2_ASSERT( joint->setIndex == setIndex ); + b2AddJointToGraph( world, jointSim, joint ); + joint->setIndex = b2_awakeSet; + } + } + + // transfer island from sleeping set to awake set + // Usually a sleeping set has only one island, but it is possible + // that joints are created between sleeping islands and they + // are moved to the same sleeping set. + { + int islandCount = set->islandSims.count; + for ( int i = 0; i < islandCount; ++i ) + { + b2IslandSim* islandSrc = set->islandSims.data + i; + b2Island* island = b2IslandArray_Get( &world->islands, islandSrc->islandId ); + island->setIndex = b2_awakeSet; + island->localIndex = awakeSet->islandSims.count; + b2IslandSim* islandDst = b2IslandSimArray_Add( &awakeSet->islandSims ); + memcpy( islandDst, islandSrc, sizeof( b2IslandSim ) ); + } + } + + // destroy the sleeping set + b2DestroySolverSet( world, setIndex ); + + b2ValidateSolverSets( world ); +} + +void b2TrySleepIsland( b2World* world, int islandId ) +{ + b2Island* island = b2IslandArray_Get( &world->islands, islandId ); + B2_ASSERT( island->setIndex == b2_awakeSet ); + + // cannot put an island to sleep while it has a pending split + if ( island->constraintRemoveCount > 0 ) + { + return; + } + + // island is sleeping + // - create new sleeping solver set + // - move island to sleeping solver set + // - identify non-touching contacts that should move to sleeping solver set or disabled set + // - remove old island + // - fix island + int sleepSetId = b2AllocId( &world->solverSetIdPool ); + if ( sleepSetId == world->solverSets.count ) + { + b2SolverSet set = { 0 }; + set.setIndex = B2_NULL_INDEX; + b2SolverSetArray_Push( &world->solverSets, set ); + } + + b2SolverSet* sleepSet = b2SolverSetArray_Get( &world->solverSets, sleepSetId ); + *sleepSet = ( b2SolverSet ){ 0 }; + + // grab awake set after creating the sleep set because the solver set array may have been resized + b2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet ); + B2_ASSERT( 0 <= island->localIndex && island->localIndex < awakeSet->islandSims.count ); + + sleepSet->setIndex = sleepSetId; + sleepSet->bodySims = b2BodySimArray_Create( island->bodyCount ); + sleepSet->contactSims = b2ContactSimArray_Create( island->contactCount ); + sleepSet->jointSims = b2JointSimArray_Create( island->jointCount ); + + // move awake bodies to sleeping set + // this shuffles around bodies in the awake set + { + b2SolverSet* disabledSet = b2SolverSetArray_Get( &world->solverSets, b2_disabledSet ); + int bodyId = island->headBody; + while ( bodyId != B2_NULL_INDEX ) + { + b2Body* body = b2BodyArray_Get( &world->bodies, bodyId ); + B2_ASSERT( body->setIndex == b2_awakeSet ); + B2_ASSERT( body->islandId == islandId ); + + // Update the body move event to indicate this body fell asleep + // It could happen the body is forced asleep before it ever moves. + if ( body->bodyMoveIndex != B2_NULL_INDEX ) + { + b2BodyMoveEvent* moveEvent = b2BodyMoveEventArray_Get( &world->bodyMoveEvents, body->bodyMoveIndex ); + B2_ASSERT( moveEvent->bodyId.index1 - 1 == bodyId ); + B2_ASSERT( moveEvent->bodyId.revision == body->revision ); + moveEvent->fellAsleep = true; + body->bodyMoveIndex = B2_NULL_INDEX; + } + + int awakeBodyIndex = body->localIndex; + b2BodySim* awakeSim = b2BodySimArray_Get( &awakeSet->bodySims, awakeBodyIndex ); + + // move body sim to sleep set + int sleepBodyIndex = sleepSet->bodySims.count; + b2BodySim* sleepBodySim = b2BodySimArray_Add( &sleepSet->bodySims ); + memcpy( sleepBodySim, awakeSim, sizeof( b2BodySim ) ); + + int movedIndex = b2BodySimArray_RemoveSwap( &awakeSet->bodySims, awakeBodyIndex ); + if ( movedIndex != B2_NULL_INDEX ) + { + // fix local index on moved element + b2BodySim* movedSim = awakeSet->bodySims.data + awakeBodyIndex; + int movedId = movedSim->bodyId; + b2Body* movedBody = b2BodyArray_Get( &world->bodies, movedId ); + B2_ASSERT( movedBody->localIndex == movedIndex ); + movedBody->localIndex = awakeBodyIndex; + } + + // destroy state, no need to clone + b2BodyStateArray_RemoveSwap( &awakeSet->bodyStates, awakeBodyIndex ); + + body->setIndex = sleepSetId; + body->localIndex = sleepBodyIndex; + + // Move non-touching contacts to the disabled set. + // Non-touching contacts may exist between sleeping islands and there is no clear ownership. + int contactKey = body->headContactKey; + while ( contactKey != B2_NULL_INDEX ) + { + int contactId = contactKey >> 1; + int edgeIndex = contactKey & 1; + + b2Contact* contact = b2ContactArray_Get( &world->contacts, contactId ); + + B2_ASSERT( contact->setIndex == b2_awakeSet || contact->setIndex == b2_disabledSet ); + contactKey = contact->edges[edgeIndex].nextKey; + + if ( contact->setIndex == b2_disabledSet ) + { + // already moved to disabled set by another body in the island + continue; + } + + if ( contact->colorIndex != B2_NULL_INDEX ) + { + // contact is touching and will be moved separately + B2_ASSERT( ( contact->flags & b2_contactTouchingFlag ) != 0 ); + continue; + } + + // the other body may still be awake, it still may go to sleep and then it will be responsible + // for moving this contact to the disabled set. + int otherEdgeIndex = edgeIndex ^ 1; + int otherBodyId = contact->edges[otherEdgeIndex].bodyId; + b2Body* otherBody = b2BodyArray_Get( &world->bodies, otherBodyId ); + if ( otherBody->setIndex == b2_awakeSet ) + { + continue; + } + + int localIndex = contact->localIndex; + b2ContactSim* contactSim = b2ContactSimArray_Get( &awakeSet->contactSims, localIndex ); + + B2_ASSERT( contactSim->manifold.pointCount == 0 ); + B2_ASSERT( ( contact->flags & b2_contactTouchingFlag ) == 0 || ( contact->flags & b2_contactSensorFlag ) != 0 ); + + // move the non-touching contact to the disabled set + contact->setIndex = b2_disabledSet; + contact->localIndex = disabledSet->contactSims.count; + b2ContactSim* disabledContactSim = b2ContactSimArray_Add( &disabledSet->contactSims ); + memcpy( disabledContactSim, contactSim, sizeof( b2ContactSim ) ); + + int movedLocalIndex = b2ContactSimArray_RemoveSwap( &awakeSet->contactSims, localIndex ); + if ( movedLocalIndex != B2_NULL_INDEX ) + { + // fix moved element + b2ContactSim* movedContactSim = awakeSet->contactSims.data + localIndex; + b2Contact* movedContact = b2ContactArray_Get( &world->contacts, movedContactSim->contactId ); + B2_ASSERT( movedContact->localIndex == movedLocalIndex ); + movedContact->localIndex = localIndex; + } + } + + bodyId = body->islandNext; + } + } + + // move touching contacts + // this shuffles contacts in the awake set + { + int contactId = island->headContact; + while ( contactId != B2_NULL_INDEX ) + { + b2Contact* contact = b2ContactArray_Get( &world->contacts, contactId ); + B2_ASSERT( contact->setIndex == b2_awakeSet ); + B2_ASSERT( contact->islandId == islandId ); + int colorIndex = contact->colorIndex; + B2_ASSERT( 0 <= colorIndex && colorIndex < b2_graphColorCount ); + + b2GraphColor* color = world->constraintGraph.colors + colorIndex; + + // Remove bodies from graph coloring associated with this constraint + if ( colorIndex != b2_overflowIndex ) + { + // might clear a bit for a static body, but this has no effect + b2ClearBit( &color->bodySet, contact->edges[0].bodyId ); + b2ClearBit( &color->bodySet, contact->edges[1].bodyId ); + } + + int localIndex = contact->localIndex; + b2ContactSim* awakeContactSim = b2ContactSimArray_Get( &color->contactSims, localIndex ); + + int sleepContactIndex = sleepSet->contactSims.count; + b2ContactSim* sleepContactSim = b2ContactSimArray_Add( &sleepSet->contactSims ); + memcpy( sleepContactSim, awakeContactSim, sizeof( b2ContactSim ) ); + + int movedLocalIndex = b2ContactSimArray_RemoveSwap( &color->contactSims, localIndex ); + if ( movedLocalIndex != B2_NULL_INDEX ) + { + // fix moved element + b2ContactSim* movedContactSim = color->contactSims.data + localIndex; + b2Contact* movedContact = b2ContactArray_Get( &world->contacts, movedContactSim->contactId ); + B2_ASSERT( movedContact->localIndex == movedLocalIndex ); + movedContact->localIndex = localIndex; + } + + contact->setIndex = sleepSetId; + contact->colorIndex = B2_NULL_INDEX; + contact->localIndex = sleepContactIndex; + + contactId = contact->islandNext; + } + } + + // move joints + // this shuffles joints in the awake set + { + int jointId = island->headJoint; + while ( jointId != B2_NULL_INDEX ) + { + b2Joint* joint = b2JointArray_Get( &world->joints, jointId ); + B2_ASSERT( joint->setIndex == b2_awakeSet ); + B2_ASSERT( joint->islandId == islandId ); + int colorIndex = joint->colorIndex; + int localIndex = joint->localIndex; + + B2_ASSERT( 0 <= colorIndex && colorIndex < b2_graphColorCount ); + + b2GraphColor* color = world->constraintGraph.colors + colorIndex; + + b2JointSim* awakeJointSim = b2JointSimArray_Get( &color->jointSims, localIndex ); + + if ( colorIndex != b2_overflowIndex ) + { + // might clear a bit for a static body, but this has no effect + b2ClearBit( &color->bodySet, joint->edges[0].bodyId ); + b2ClearBit( &color->bodySet, joint->edges[1].bodyId ); + } + + int sleepJointIndex = sleepSet->jointSims.count; + b2JointSim* sleepJointSim = b2JointSimArray_Add( &sleepSet->jointSims ); + memcpy( sleepJointSim, awakeJointSim, sizeof( b2JointSim ) ); + + int movedIndex = b2JointSimArray_RemoveSwap( &color->jointSims, localIndex ); + if ( movedIndex != B2_NULL_INDEX ) + { + // fix moved element + b2JointSim* movedJointSim = color->jointSims.data + localIndex; + int movedId = movedJointSim->jointId; + b2Joint* movedJoint = b2JointArray_Get( &world->joints, movedId ); + B2_ASSERT( movedJoint->localIndex == movedIndex ); + movedJoint->localIndex = localIndex; + } + + joint->setIndex = sleepSetId; + joint->colorIndex = B2_NULL_INDEX; + joint->localIndex = sleepJointIndex; + + jointId = joint->islandNext; + } + } + + // move island struct + { + B2_ASSERT( island->setIndex == b2_awakeSet ); + + int islandIndex = island->localIndex; + b2IslandSim* sleepIsland = b2IslandSimArray_Add( &sleepSet->islandSims ); + sleepIsland->islandId = islandId; + + int movedIslandIndex = b2IslandSimArray_RemoveSwap( &awakeSet->islandSims, islandIndex ); + if ( movedIslandIndex != B2_NULL_INDEX ) + { + // fix index on moved element + b2IslandSim* movedIslandSim = awakeSet->islandSims.data + islandIndex; + int movedIslandId = movedIslandSim->islandId; + b2Island* movedIsland = b2IslandArray_Get( &world->islands, movedIslandId ); + B2_ASSERT( movedIsland->localIndex == movedIslandIndex ); + movedIsland->localIndex = islandIndex; + } + + island->setIndex = sleepSetId; + island->localIndex = 0; + } + + b2ValidateSolverSets( world ); +} + +// This is called when joints are created between sets. I want to allow the sets +// to continue sleeping if both are asleep. Otherwise one set is waked. +// Islands will get merge when the set is waked. +void b2MergeSolverSets( b2World* world, int setId1, int setId2 ) +{ + B2_ASSERT( setId1 >= b2_firstSleepingSet ); + B2_ASSERT( setId2 >= b2_firstSleepingSet ); + b2SolverSet* set1 = b2SolverSetArray_Get( &world->solverSets, setId1 ); + b2SolverSet* set2 = b2SolverSetArray_Get( &world->solverSets, setId2 ); + + // Move the fewest number of bodies + if ( set1->bodySims.count < set2->bodySims.count ) + { + b2SolverSet* tempSet = set1; + set1 = set2; + set2 = tempSet; + + int tempId = setId1; + setId1 = setId2; + setId2 = tempId; + } + + // transfer bodies + { + b2Body* bodies = world->bodies.data; + int bodyCount = set2->bodySims.count; + for ( int i = 0; i < bodyCount; ++i ) + { + b2BodySim* simSrc = set2->bodySims.data + i; + + b2Body* body = bodies + simSrc->bodyId; + B2_ASSERT( body->setIndex == setId2 ); + body->setIndex = setId1; + body->localIndex = set1->bodySims.count; + + b2BodySim* simDst = b2BodySimArray_Add( &set1->bodySims ); + memcpy( simDst, simSrc, sizeof( b2BodySim ) ); + } + } + + // transfer contacts + { + int contactCount = set2->contactSims.count; + for ( int i = 0; i < contactCount; ++i ) + { + b2ContactSim* contactSrc = set2->contactSims.data + i; + + b2Contact* contact = b2ContactArray_Get( &world->contacts, contactSrc->contactId ); + B2_ASSERT( contact->setIndex == setId2 ); + contact->setIndex = setId1; + contact->localIndex = set1->contactSims.count; + + b2ContactSim* contactDst = b2ContactSimArray_Add( &set1->contactSims ); + memcpy( contactDst, contactSrc, sizeof( b2ContactSim ) ); + } + } + + // transfer joints + { + int jointCount = set2->jointSims.count; + for ( int i = 0; i < jointCount; ++i ) + { + b2JointSim* jointSrc = set2->jointSims.data + i; + + b2Joint* joint = b2JointArray_Get( &world->joints, jointSrc->jointId ); + B2_ASSERT( joint->setIndex == setId2 ); + joint->setIndex = setId1; + joint->localIndex = set1->jointSims.count; + + b2JointSim* jointDst = b2JointSimArray_Add( &set1->jointSims ); + memcpy( jointDst, jointSrc, sizeof( b2JointSim ) ); + } + } + + // transfer islands + { + int islandCount = set2->islandSims.count; + for ( int i = 0; i < islandCount; ++i ) + { + b2IslandSim* islandSrc = set2->islandSims.data + i; + int islandId = islandSrc->islandId; + + b2Island* island = b2IslandArray_Get( &world->islands, islandId ); + island->setIndex = setId1; + island->localIndex = set1->islandSims.count; + + b2IslandSim* islandDst = b2IslandSimArray_Add( &set1->islandSims ); + memcpy( islandDst, islandSrc, sizeof( b2IslandSim ) ); + } + } + + // destroy the merged set + b2DestroySolverSet( world, setId2 ); + + b2ValidateSolverSets( world ); +} + +void b2TransferBody( b2World* world, b2SolverSet* targetSet, b2SolverSet* sourceSet, b2Body* body ) +{ + B2_ASSERT( targetSet != sourceSet ); + + int sourceIndex = body->localIndex; + b2BodySim* sourceSim = b2BodySimArray_Get( &sourceSet->bodySims, sourceIndex ); + + int targetIndex = targetSet->bodySims.count; + b2BodySim* targetSim = b2BodySimArray_Add( &targetSet->bodySims ); + memcpy( targetSim, sourceSim, sizeof( b2BodySim ) ); + + // Remove body sim from solver set that owns it + int movedIndex = b2BodySimArray_RemoveSwap( &sourceSet->bodySims, sourceIndex ); + if ( movedIndex != B2_NULL_INDEX ) + { + // Fix moved body index + b2BodySim* movedSim = sourceSet->bodySims.data + sourceIndex; + int movedId = movedSim->bodyId; + b2Body* movedBody = b2BodyArray_Get( &world->bodies, movedId ); + B2_ASSERT( movedBody->localIndex == movedIndex ); + movedBody->localIndex = sourceIndex; + } + + if ( sourceSet->setIndex == b2_awakeSet ) + { + b2BodyStateArray_RemoveSwap( &sourceSet->bodyStates, sourceIndex ); + } + else if ( targetSet->setIndex == b2_awakeSet ) + { + b2BodyState* state = b2BodyStateArray_Add( &targetSet->bodyStates ); + *state = b2_identityBodyState; + } + + body->setIndex = targetSet->setIndex; + body->localIndex = targetIndex; +} + +void b2TransferJoint( b2World* world, b2SolverSet* targetSet, b2SolverSet* sourceSet, b2Joint* joint ) +{ + B2_ASSERT( targetSet != sourceSet ); + + int localIndex = joint->localIndex; + int colorIndex = joint->colorIndex; + + // Retrieve source. + b2JointSim* sourceSim; + if ( sourceSet->setIndex == b2_awakeSet ) + { + B2_ASSERT( 0 <= colorIndex && colorIndex < b2_graphColorCount ); + b2GraphColor* color = world->constraintGraph.colors + colorIndex; + + sourceSim = b2JointSimArray_Get( &color->jointSims, localIndex ); + } + else + { + B2_ASSERT( colorIndex == B2_NULL_INDEX ); + sourceSim = b2JointSimArray_Get( &sourceSet->jointSims, +localIndex ); + } + + // Create target and copy. Fix joint. + if ( targetSet->setIndex == b2_awakeSet ) + { + b2AddJointToGraph( world, sourceSim, joint ); + joint->setIndex = b2_awakeSet; + } + else + { + joint->setIndex = targetSet->setIndex; + joint->localIndex = targetSet->jointSims.count; + joint->colorIndex = B2_NULL_INDEX; + + b2JointSim* targetSim = b2JointSimArray_Add( &targetSet->jointSims ); + memcpy( targetSim, sourceSim, sizeof( b2JointSim ) ); + } + + // Destroy source. + if ( sourceSet->setIndex == b2_awakeSet ) + { + b2RemoveJointFromGraph( world, joint->edges[0].bodyId, joint->edges[1].bodyId, colorIndex, localIndex ); + } + else + { + int movedIndex = b2JointSimArray_RemoveSwap( &sourceSet->jointSims, localIndex ); + if ( movedIndex != B2_NULL_INDEX ) + { + // fix swapped element + b2JointSim* movedJointSim = sourceSet->jointSims.data + localIndex; + int movedId = movedJointSim->jointId; + b2Joint* movedJoint = b2JointArray_Get( &world->joints, movedId ); + movedJoint->localIndex = localIndex; + } + } +} diff --git a/3rdparty/box2d/src/solver_set.h b/3rdparty/box2d/src/solver_set.h new file mode 100644 index 000000000000..1df615b8d2e7 --- /dev/null +++ b/3rdparty/box2d/src/solver_set.h @@ -0,0 +1,57 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include "array.h" + +typedef struct b2Body b2Body; +typedef struct b2Joint b2Joint; +typedef struct b2World b2World; + +// This holds solver set data. The following sets are used: +// - static set for all static bodies (no contacts or joints) +// - active set for all active bodies with body states (no contacts or joints) +// - disabled set for disabled bodies and their joints +// - all further sets are sleeping island sets along with their contacts and joints +// The purpose of solver sets is to achieve high memory locality. +// https://www.youtube.com/watch?v=nZNd5FjSquk +typedef struct b2SolverSet +{ + // Body array. Empty for unused set. + b2BodySimArray bodySims; + + // Body state only exists for active set + b2BodyStateArray bodyStates; + + // This holds sleeping/disabled joints. Empty for static/active set. + b2JointSimArray jointSims; + + // This holds all contacts for sleeping sets. + // This holds non-touching contacts for the awake set. + b2ContactSimArray contactSims; + + // The awake set has an array of islands. Sleeping sets normally have a single islands. However, joints + // created between sleeping sets causes the sets to merge, leaving them with multiple islands. These sleeping + // islands will be naturally merged with the set is woken. + // The static and disabled sets have no islands. + // Islands live in the solver sets to limit the number of islands that need to be considered for sleeping. + b2IslandSimArray islandSims; + + // Aligns with b2World::solverSetIdPool. Used to create a stable id for body/contact/joint/islands. + int setIndex; +} b2SolverSet; + +void b2DestroySolverSet( b2World* world, int setIndex ); + +void b2WakeSolverSet( b2World* world, int setIndex ); +void b2TrySleepIsland( b2World* world, int islandId ); + +// Merge set 2 into set 1 then destroy set 2. +// Warning: any pointers into these sets will be orphaned. +void b2MergeSolverSets( b2World* world, int setIndex1, int setIndex2 ); + +void b2TransferBody( b2World* world, b2SolverSet* targetSet, b2SolverSet* sourceSet, b2Body* body ); +void b2TransferJoint( b2World* world, b2SolverSet* targetSet, b2SolverSet* sourceSet, b2Joint* joint ); + +B2_ARRAY_INLINE( b2SolverSet, b2SolverSet ); diff --git a/3rdparty/box2d/src/stack_allocator.c b/3rdparty/box2d/src/stack_allocator.c new file mode 100644 index 000000000000..2b6b408c1f72 --- /dev/null +++ b/3rdparty/box2d/src/stack_allocator.c @@ -0,0 +1,121 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#include "stack_allocator.h" + +#include "array.h" +#include "core.h" + +#include +#include + +typedef struct b2StackEntry +{ + char* data; + const char* name; + int size; + bool usedMalloc; +} b2StackEntry; + +B2_ARRAY_INLINE( b2StackEntry, b2StackEntry ); +B2_ARRAY_SOURCE( b2StackEntry, b2StackEntry ); + +b2StackAllocator b2CreateStackAllocator( int capacity ) +{ + B2_ASSERT( capacity >= 0 ); + b2StackAllocator allocator = { 0 }; + allocator.capacity = capacity; + allocator.data = b2Alloc( capacity ); + allocator.allocation = 0; + allocator.maxAllocation = 0; + allocator.index = 0; + allocator.entries = b2StackEntryArray_Create( 32 ); + return allocator; +} + +void b2DestroyStackAllocator( b2StackAllocator* allocator ) +{ + b2StackEntryArray_Destroy( &allocator->entries ); + b2Free( allocator->data, allocator->capacity ); +} + +void* b2AllocateStackItem( b2StackAllocator* alloc, int size, const char* name ) +{ + // ensure allocation is 32 byte aligned to support 256-bit SIMD + int size32 = ( ( size - 1 ) | 0x1F ) + 1; + + b2StackEntry entry; + entry.size = size32; + entry.name = name; + if ( alloc->index + size32 > alloc->capacity ) + { + // fall back to the heap (undesirable) + entry.data = b2Alloc( size32 ); + entry.usedMalloc = true; + + B2_ASSERT( ( (uintptr_t)entry.data & 0x1F ) == 0 ); + } + else + { + entry.data = alloc->data + alloc->index; + entry.usedMalloc = false; + alloc->index += size32; + + B2_ASSERT( ( (uintptr_t)entry.data & 0x1F ) == 0 ); + } + + alloc->allocation += size32; + if ( alloc->allocation > alloc->maxAllocation ) + { + alloc->maxAllocation = alloc->allocation; + } + + b2StackEntryArray_Push( &alloc->entries, entry ); + return entry.data; +} + +void b2FreeStackItem( b2StackAllocator* alloc, void* mem ) +{ + int entryCount = alloc->entries.count; + B2_ASSERT( entryCount > 0 ); + b2StackEntry* entry = alloc->entries.data + ( entryCount - 1 ); + B2_ASSERT( mem == entry->data ); + if ( entry->usedMalloc ) + { + b2Free( mem, entry->size ); + } + else + { + alloc->index -= entry->size; + } + alloc->allocation -= entry->size; + b2StackEntryArray_Pop( &alloc->entries ); +} + +void b2GrowStack( b2StackAllocator* alloc ) +{ + // Stack must not be in use + B2_ASSERT( alloc->allocation == 0 ); + + if ( alloc->maxAllocation > alloc->capacity ) + { + b2Free( alloc->data, alloc->capacity ); + alloc->capacity = alloc->maxAllocation + alloc->maxAllocation / 2; + alloc->data = b2Alloc( alloc->capacity ); + } +} + +int b2GetStackCapacity( b2StackAllocator* alloc ) +{ + return alloc->capacity; +} + +int b2GetStackAllocation( b2StackAllocator* alloc ) +{ + return alloc->allocation; +} + +int b2GetMaxStackAllocation( b2StackAllocator* alloc ) +{ + return alloc->maxAllocation; +} diff --git a/3rdparty/box2d/src/stack_allocator.h b/3rdparty/box2d/src/stack_allocator.h new file mode 100644 index 000000000000..e6d6149a2403 --- /dev/null +++ b/3rdparty/box2d/src/stack_allocator.h @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include "array.h" + +B2_ARRAY_DECLARE( b2StackEntry, b2StackEntry ); + +// This is a stack-like arena allocator used for fast per step allocations. +// You must nest allocate/free pairs. The code will B2_ASSERT +// if you try to interleave multiple allocate/free pairs. +// This allocator uses the heap if space is insufficient. +// I could remove the need to free entries individually. +typedef struct b2StackAllocator +{ + char* data; + int capacity; + int index; + + int allocation; + int maxAllocation; + + b2StackEntryArray entries; +} b2StackAllocator; + +b2StackAllocator b2CreateStackAllocator( int capacity ); +void b2DestroyStackAllocator( b2StackAllocator* allocator ); + +void* b2AllocateStackItem( b2StackAllocator* alloc, int size, const char* name ); +void b2FreeStackItem( b2StackAllocator* alloc, void* mem ); + +// Grow the stack based on usage +void b2GrowStack( b2StackAllocator* alloc ); + +int b2GetStackCapacity( b2StackAllocator* alloc ); +int b2GetStackAllocation( b2StackAllocator* alloc ); +int b2GetMaxStackAllocation( b2StackAllocator* alloc ); diff --git a/3rdparty/box2d/src/table.c b/3rdparty/box2d/src/table.c new file mode 100644 index 000000000000..c13d9525d8e3 --- /dev/null +++ b/3rdparty/box2d/src/table.c @@ -0,0 +1,231 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#include "table.h" + +#include "core.h" +#include "ctz.h" + +#include +#include +#include + +#if B2_DEBUG +_Atomic int g_probeCount; +#endif + +// todo compare with https://github.com/skeeto/scratch/blob/master/set32/set32.h + +b2HashSet b2CreateSet( int32_t capacity ) +{ + b2HashSet set = { 0 }; + + // Capacity must be a power of 2 + if ( capacity > 16 ) + { + set.capacity = b2RoundUpPowerOf2( capacity ); + } + else + { + set.capacity = 16; + } + + set.count = 0; + set.items = b2Alloc( capacity * sizeof( b2SetItem ) ); + memset( set.items, 0, capacity * sizeof( b2SetItem ) ); + + return set; +} + +void b2DestroySet( b2HashSet* set ) +{ + b2Free( set->items, set->capacity * sizeof( b2SetItem ) ); + set->items = NULL; + set->count = 0; + set->capacity = 0; +} + +void b2ClearSet( b2HashSet* set ) +{ + set->count = 0; + memset( set->items, 0, set->capacity * sizeof( b2SetItem ) ); +} + +// I need a good hash because the keys are built from pairs of increasing integers. +// A simple hash like hash = (integer1 XOR integer2) has many collisions. +// https://lemire.me/blog/2018/08/15/fast-strongly-universal-64-bit-hashing-everywhere/ +// https://preshing.com/20130107/this-hash-set-is-faster-than-a-judy-array/ +// TODO_ERIN try: https://www.jandrewrogers.com/2019/02/12/fast-perfect-hashing/ +static inline uint32_t b2KeyHash( uint64_t key ) +{ + uint64_t h = key; + h ^= h >> 33; + h *= 0xff51afd7ed558ccdL; + h ^= h >> 33; + h *= 0xc4ceb9fe1a85ec53L; + h ^= h >> 33; + + return (uint32_t)h; +} + +int32_t b2FindSlot( const b2HashSet* set, uint64_t key, uint32_t hash ) +{ + uint32_t capacity = set->capacity; + int32_t index = hash & ( capacity - 1 ); + const b2SetItem* items = set->items; + while ( items[index].hash != 0 && items[index].key != key ) + { +#if B2_DEBUG + atomic_fetch_add( &g_probeCount, 1 ); +#endif + index = ( index + 1 ) & ( capacity - 1 ); + } + + return index; +} + +static void b2AddKeyHaveCapacity( b2HashSet* set, uint64_t key, uint32_t hash ) +{ + int32_t index = b2FindSlot( set, key, hash ); + b2SetItem* items = set->items; + B2_ASSERT( items[index].hash == 0 ); + + items[index].key = key; + items[index].hash = hash; + set->count += 1; +} + +static void b2GrowTable( b2HashSet* set ) +{ + uint32_t oldCount = set->count; + B2_MAYBE_UNUSED( oldCount ); + + uint32_t oldCapacity = set->capacity; + b2SetItem* oldItems = set->items; + + set->count = 0; + // Capacity must be a power of 2 + set->capacity = 2 * oldCapacity; + set->items = b2Alloc( set->capacity * sizeof( b2SetItem ) ); + memset( set->items, 0, set->capacity * sizeof( b2SetItem ) ); + + // Transfer items into new array + for ( uint32_t i = 0; i < oldCapacity; ++i ) + { + b2SetItem* item = oldItems + i; + if ( item->hash == 0 ) + { + // this item was empty + continue; + } + + b2AddKeyHaveCapacity( set, item->key, item->hash ); + } + + B2_ASSERT( set->count == oldCount ); + + b2Free( oldItems, oldCapacity * sizeof( b2SetItem ) ); +} + +bool b2ContainsKey( const b2HashSet* set, uint64_t key ) +{ + // key of zero is a sentinel + B2_ASSERT( key != 0 ); + uint32_t hash = b2KeyHash( key ); + int32_t index = b2FindSlot( set, key, hash ); + return set->items[index].key == key; +} + +int b2GetHashSetBytes( b2HashSet* set ) +{ + return set->capacity * (int)sizeof( b2SetItem ); +} + +bool b2AddKey( b2HashSet* set, uint64_t key ) +{ + // key of zero is a sentinel + B2_ASSERT( key != 0 ); + + uint32_t hash = b2KeyHash( key ); + B2_ASSERT( hash != 0 ); + + int32_t index = b2FindSlot( set, key, hash ); + if ( set->items[index].hash != 0 ) + { + // Already in set + B2_ASSERT( set->items[index].hash == hash && set->items[index].key == key ); + return true; + } + + if ( 2 * set->count >= set->capacity ) + { + b2GrowTable( set ); + } + + b2AddKeyHaveCapacity( set, key, hash ); + return false; +} + +// See https://en.wikipedia.org/wiki/Open_addressing +bool b2RemoveKey( b2HashSet* set, uint64_t key ) +{ + uint32_t hash = b2KeyHash( key ); + int32_t i = b2FindSlot( set, key, hash ); + b2SetItem* items = set->items; + if ( items[i].hash == 0 ) + { + // Not in set + return false; + } + + // Mark item i as unoccupied + items[i].key = 0; + items[i].hash = 0; + + B2_ASSERT( set->count > 0 ); + set->count -= 1; + + // Attempt to fill item i + int32_t j = i; + uint32_t capacity = set->capacity; + for ( ;; ) + { + j = ( j + 1 ) & ( capacity - 1 ); + if ( items[j].hash == 0 ) + { + break; + } + + // k is the first item for the hash of j + int32_t k = items[j].hash & ( capacity - 1 ); + + // determine if k lies cyclically in (i,j] + // i <= j: | i..k..j | + // i > j: |.k..j i....| or |....j i..k.| + if ( i <= j ) + { + if ( i < k && k <= j ) + { + continue; + } + } + else + { + if ( i < k || k <= j ) + { + continue; + } + } + + // Move j into i + items[i] = items[j]; + + // Mark item j as unoccupied + items[j].key = 0; + items[j].hash = 0; + + i = j; + } + + return true; +} diff --git a/3rdparty/box2d/src/table.h b/3rdparty/box2d/src/table.h new file mode 100644 index 000000000000..04949949b748 --- /dev/null +++ b/3rdparty/box2d/src/table.h @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +#define B2_SHAPE_PAIR_KEY( K1, K2 ) K1 < K2 ? (uint64_t)K1 << 32 | (uint64_t)K2 : (uint64_t)K2 << 32 | (uint64_t)K1 + +typedef struct b2SetItem +{ + uint64_t key; + uint32_t hash; +} b2SetItem; + +typedef struct b2HashSet +{ + b2SetItem* items; + uint32_t capacity; + uint32_t count; +} b2HashSet; + +b2HashSet b2CreateSet( int32_t capacity ); +void b2DestroySet( b2HashSet* set ); + +void b2ClearSet( b2HashSet* set ); + +// Returns true if key was already in set +bool b2AddKey( b2HashSet* set, uint64_t key ); + +// Returns true if the key was found +bool b2RemoveKey( b2HashSet* set, uint64_t key ); + +bool b2ContainsKey( const b2HashSet* set, uint64_t key ); + +int b2GetHashSetBytes( b2HashSet* set ); diff --git a/3rdparty/box2d/src/timer.c b/3rdparty/box2d/src/timer.c new file mode 100644 index 000000000000..0aecd57baac2 --- /dev/null +++ b/3rdparty/box2d/src/timer.c @@ -0,0 +1,202 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#include "box2d/base.h" + +#include + +#if defined( _WIN32 ) + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#include + +static double s_invFrequency = 0.0; + +b2Timer b2CreateTimer( void ) +{ + LARGE_INTEGER largeInteger; + + if ( s_invFrequency == 0.0 ) + { + QueryPerformanceFrequency( &largeInteger ); + + s_invFrequency = (double)largeInteger.QuadPart; + if ( s_invFrequency > 0.0 ) + { + s_invFrequency = 1000.0 / s_invFrequency; + } + } + + QueryPerformanceCounter( &largeInteger ); + b2Timer timer; + timer.start = largeInteger.QuadPart; + return timer; +} + +int64_t b2GetTicks( b2Timer* timer ) +{ + LARGE_INTEGER largeInteger; + QueryPerformanceCounter( &largeInteger ); + int64_t ticks = largeInteger.QuadPart; + int64_t count = ticks - timer->start; + timer->start = ticks; + return count; +} + +float b2GetMilliseconds( const b2Timer* timer ) +{ + LARGE_INTEGER largeInteger; + QueryPerformanceCounter( &largeInteger ); + int64_t count = largeInteger.QuadPart; + float ms = (float)( s_invFrequency * ( count - timer->start ) ); + return ms; +} + +float b2GetMillisecondsAndReset( b2Timer* timer ) +{ + LARGE_INTEGER largeInteger; + QueryPerformanceCounter( &largeInteger ); + int64_t count = largeInteger.QuadPart; + float ms = (float)( s_invFrequency * ( count - timer->start ) ); + timer->start = count; + return ms; +} + +void b2SleepMilliseconds( int milliseconds ) +{ + // also SwitchToThread() + Sleep( (DWORD)milliseconds ); +} + +void b2Yield() +{ + SwitchToThread(); +} + +#elif defined( __linux__ ) || defined( __APPLE__ ) + +#include +#include +#include + +b2Timer b2CreateTimer( void ) +{ + b2Timer timer; + struct timeval t; + gettimeofday( &t, 0 ); + timer.start_sec = t.tv_sec; + timer.start_usec = t.tv_usec; + return timer; +} + +float b2GetMilliseconds( const b2Timer* timer ) +{ + struct timeval t; + gettimeofday( &t, 0 ); + time_t start_sec = timer->start_sec; + suseconds_t start_usec = (suseconds_t)timer->start_usec; + + // http://www.gnu.org/software/libc/manual/html_node/Elapsed-Time.html + if ( t.tv_usec < start_usec ) + { + int nsec = ( start_usec - t.tv_usec ) / 1000000 + 1; + start_usec -= 1000000 * nsec; + start_sec += nsec; + } + + if ( t.tv_usec - start_usec > 1000000 ) + { + int nsec = ( t.tv_usec - start_usec ) / 1000000; + start_usec += 1000000 * nsec; + start_sec -= nsec; + } + return 1000.0f * ( t.tv_sec - start_sec ) + 0.001f * ( t.tv_usec - start_usec ); +} + +float b2GetMillisecondsAndReset( b2Timer* timer ) +{ + struct timeval t; + gettimeofday( &t, 0 ); + time_t start_sec = timer->start_sec; + suseconds_t start_usec = (suseconds_t)timer->start_usec; + + // http://www.gnu.org/software/libc/manual/html_node/Elapsed-Time.html + if ( t.tv_usec < start_usec ) + { + int nsec = ( start_usec - t.tv_usec ) / 1000000 + 1; + start_usec -= 1000000 * nsec; + start_sec += nsec; + } + + if ( t.tv_usec - start_usec > 1000000 ) + { + int nsec = ( t.tv_usec - start_usec ) / 1000000; + start_usec += 1000000 * nsec; + start_sec -= nsec; + } + + timer->start_sec = t.tv_sec; + timer->start_usec = t.tv_usec; + + return 1000.0f * ( t.tv_sec - start_sec ) + 0.001f * ( t.tv_usec - start_usec ); +} + +void b2SleepMilliseconds( int milliseconds ) +{ + struct timespec ts; + ts.tv_sec = milliseconds / 1000; + ts.tv_nsec = ( milliseconds % 1000 ) * 1000000; + nanosleep( &ts, NULL ); +} + +void b2Yield() +{ + sched_yield(); +} + +#else + +b2Timer b2CreateTimer( void ) +{ + b2Timer timer = { 0 }; + return timer; +} + +float b2GetMilliseconds( const b2Timer* timer ) +{ + ( (void)( timer ) ); + return 0.0f; +} + +float b2GetMillisecondsAndReset( b2Timer* timer ) +{ + ( (void)( timer ) ); + return 0.0f; +} + +void b2SleepMilliseconds( int milliseconds ) +{ + ( (void)( milliseconds ) ); +} + +void b2Yield() +{ +} + +#endif + +// djb2 hash +// https://en.wikipedia.org/wiki/List_of_hash_functions +uint32_t b2Hash( uint32_t hash, const uint8_t* data, int count ) +{ + uint32_t result = hash; + for ( size_t i = 0; i < count; i++ ) + { + result = ( result << 5 ) + result + data[i]; + } + + return result; +} diff --git a/3rdparty/box2d/src/types.c b/3rdparty/box2d/src/types.c new file mode 100644 index 000000000000..69614cf2eff7 --- /dev/null +++ b/3rdparty/box2d/src/types.c @@ -0,0 +1,164 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#include "box2d/types.h" + +#include "core.h" + +b2WorldDef b2DefaultWorldDef( void ) +{ + b2WorldDef def = { 0 }; + def.gravity.x = 0.0f; + def.gravity.y = -10.0f; + def.hitEventThreshold = 1.0f * b2_lengthUnitsPerMeter; + def.restitutionThreshold = 1.0f * b2_lengthUnitsPerMeter; + def.contactPushoutVelocity = 3.0f * b2_lengthUnitsPerMeter; + def.contactHertz = 30.0; + def.contactDampingRatio = 10.0f; + def.jointHertz = 60.0; + def.jointDampingRatio = 2.0f; + // 400 meters per second, faster than the speed of sound + def.maximumLinearVelocity = 400.0f * b2_lengthUnitsPerMeter; + def.enableSleep = true; + def.enableContinuous = true; + def.internalValue = B2_SECRET_COOKIE; + return def; +} + +b2BodyDef b2DefaultBodyDef( void ) +{ + b2BodyDef def = { 0 }; + def.type = b2_staticBody; + def.rotation = b2Rot_identity; + def.sleepThreshold = 0.05f * b2_lengthUnitsPerMeter; + def.gravityScale = 1.0f; + def.enableSleep = true; + def.isAwake = true; + def.isEnabled = true; + def.automaticMass = true; + def.internalValue = B2_SECRET_COOKIE; + return def; +} + +b2Filter b2DefaultFilter( void ) +{ + b2Filter filter = { 0x0001ULL, UINT64_MAX, 0 }; + return filter; +} + +b2QueryFilter b2DefaultQueryFilter( void ) +{ + b2QueryFilter filter = { 0x0001ULL, UINT64_MAX }; + return filter; +} + +b2ShapeDef b2DefaultShapeDef( void ) +{ + b2ShapeDef def = { 0 }; + def.friction = 0.6f; + def.density = 1.0f; + def.filter = b2DefaultFilter(); + def.enableSensorEvents = true; + def.enableContactEvents = true; + def.internalValue = B2_SECRET_COOKIE; + return def; +} + +b2ChainDef b2DefaultChainDef( void ) +{ + b2ChainDef def = { 0 }; + def.friction = 0.6f; + def.filter = b2DefaultFilter(); + def.internalValue = B2_SECRET_COOKIE; + return def; +} + +static void b2EmptyDrawPolygon( const b2Vec2* vertices, int vertexCount, b2HexColor color, void* context ) +{ + B2_MAYBE_UNUSED( vertices ); + B2_MAYBE_UNUSED( vertexCount ); + B2_MAYBE_UNUSED( color ); + B2_MAYBE_UNUSED( context ); +} + +static void b2EmptyDrawSolidPolygon( b2Transform transform, const b2Vec2* vertices, int vertexCount, float radius, b2HexColor color, + void* context ) +{ + B2_MAYBE_UNUSED( transform ); + B2_MAYBE_UNUSED( vertices ); + B2_MAYBE_UNUSED( vertexCount ); + B2_MAYBE_UNUSED( radius ); + B2_MAYBE_UNUSED( color ); + B2_MAYBE_UNUSED( context ); +} + +static void b2EmptyDrawCircle( b2Vec2 center, float radius, b2HexColor color, void* context ) +{ + B2_MAYBE_UNUSED( center ); + B2_MAYBE_UNUSED( radius ); + B2_MAYBE_UNUSED( color ); + B2_MAYBE_UNUSED( context ); +} + +static void b2EmptyDrawSolidCircle( b2Transform transform, float radius, b2HexColor color, void* context ) +{ + B2_MAYBE_UNUSED( transform ); + B2_MAYBE_UNUSED( radius ); + B2_MAYBE_UNUSED( color ); + B2_MAYBE_UNUSED( context ); +} + +static void b2EmptyDrawSolidCapsule( b2Vec2 p1, b2Vec2 p2, float radius, b2HexColor color, void* context ) +{ + B2_MAYBE_UNUSED( p1 ); + B2_MAYBE_UNUSED( p2 ); + B2_MAYBE_UNUSED( radius ); + B2_MAYBE_UNUSED( color ); + B2_MAYBE_UNUSED( context ); +} + +static void b2EmptyDrawSegment( b2Vec2 p1, b2Vec2 p2, b2HexColor color, void* context ) +{ + B2_MAYBE_UNUSED( p1 ); + B2_MAYBE_UNUSED( p2 ); + B2_MAYBE_UNUSED( color ); + B2_MAYBE_UNUSED( context ); +} + +static void b2EmptyDrawTransform( b2Transform transform, void* context ) +{ + B2_MAYBE_UNUSED( transform ); + B2_MAYBE_UNUSED( context ); +} + +static void b2EmptyDrawPoint( b2Vec2 p, float size, b2HexColor color, void* context ) +{ + B2_MAYBE_UNUSED( p ); + B2_MAYBE_UNUSED( size ); + B2_MAYBE_UNUSED( color ); + B2_MAYBE_UNUSED( context ); +} + +static void b2EmptyDrawString( b2Vec2 p, const char* s, void* context ) +{ + B2_MAYBE_UNUSED( p ); + B2_MAYBE_UNUSED( s ); + B2_MAYBE_UNUSED( context ); +} + +b2DebugDraw b2DefaultDebugDraw(void) +{ + b2DebugDraw draw = { 0 }; + + // These allow the user to skip some implementations and not hit null exceptions. + draw.DrawPolygon = b2EmptyDrawPolygon; + draw.DrawSolidPolygon = b2EmptyDrawSolidPolygon; + draw.DrawCircle = b2EmptyDrawCircle; + draw.DrawSolidCircle = b2EmptyDrawSolidCircle; + draw.DrawSolidCapsule = b2EmptyDrawSolidCapsule; + draw.DrawSegment = b2EmptyDrawSegment; + draw.DrawTransform = b2EmptyDrawTransform; + draw.DrawPoint = b2EmptyDrawPoint; + draw.DrawString = b2EmptyDrawString; + return draw; +} diff --git a/3rdparty/box2d/src/weld_joint.c b/3rdparty/box2d/src/weld_joint.c new file mode 100644 index 000000000000..ea8b5a0965bc --- /dev/null +++ b/3rdparty/box2d/src/weld_joint.c @@ -0,0 +1,298 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#include "body.h" +#include "core.h" +#include "joint.h" +#include "solver.h" +#include "solver_set.h" +#include "world.h" + +// needed for dll export +#include "box2d/box2d.h" + +void b2WeldJoint_SetLinearHertz( b2JointId jointId, float hertz ) +{ + B2_ASSERT( b2IsValid( hertz ) && hertz >= 0.0f ); + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_weldJoint ); + joint->weldJoint.linearHertz = hertz; +} + +float b2WeldJoint_GetLinearHertz( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_weldJoint ); + return joint->weldJoint.linearHertz; +} + +void b2WeldJoint_SetLinearDampingRatio( b2JointId jointId, float dampingRatio ) +{ + B2_ASSERT( b2IsValid( dampingRatio ) && dampingRatio >= 0.0f ); + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_weldJoint ); + joint->weldJoint.linearDampingRatio = dampingRatio; +} + +float b2WeldJoint_GetLinearDampingRatio( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_weldJoint ); + return joint->weldJoint.linearDampingRatio; +} + +void b2WeldJoint_SetAngularHertz( b2JointId jointId, float hertz ) +{ + B2_ASSERT( b2IsValid( hertz ) && hertz >= 0.0f ); + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_weldJoint ); + joint->weldJoint.angularHertz = hertz; +} + +float b2WeldJoint_GetAngularHertz( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_weldJoint ); + return joint->weldJoint.angularHertz; +} + +void b2WeldJoint_SetAngularDampingRatio( b2JointId jointId, float dampingRatio ) +{ + B2_ASSERT( b2IsValid( dampingRatio ) && dampingRatio >= 0.0f ); + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_weldJoint ); + joint->weldJoint.angularDampingRatio = dampingRatio; +} + +float b2WeldJoint_GetAngularDampingRatio( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_weldJoint ); + return joint->weldJoint.angularDampingRatio; +} + +b2Vec2 b2GetWeldJointForce( b2World* world, b2JointSim* base ) +{ + b2Vec2 force = b2MulSV( world->inv_h, base->weldJoint.linearImpulse ); + return force; +} + +float b2GetWeldJointTorque( b2World* world, b2JointSim* base ) +{ + return world->inv_h * base->weldJoint.angularImpulse; +} + +// Point-to-point constraint +// C = p2 - p1 +// Cdot = v2 - v1 +// = v2 + cross(w2, r2) - v1 - cross(w1, r1) +// J = [-I -r1_skew I r2_skew ] +// Identity used: +// w k % (rx i + ry j) = w * (-ry i + rx j) + +// Angle constraint +// C = angle2 - angle1 - referenceAngle +// Cdot = w2 - w1 +// J = [0 0 -1 0 0 1] +// K = invI1 + invI2 + +void b2PrepareWeldJoint( b2JointSim* base, b2StepContext* context ) +{ + B2_ASSERT( base->type == b2_weldJoint ); + + // chase body id to the solver set where the body lives + int idA = base->bodyIdA; + int idB = base->bodyIdB; + + b2World* world = context->world; + + b2Body* bodyA = b2BodyArray_Get( &world->bodies, idA ); + b2Body* bodyB = b2BodyArray_Get( &world->bodies, idB ); + + B2_ASSERT( bodyA->setIndex == b2_awakeSet || bodyB->setIndex == b2_awakeSet ); + b2SolverSet* setA = b2SolverSetArray_Get( &world->solverSets, bodyA->setIndex ); + b2SolverSet* setB = b2SolverSetArray_Get( &world->solverSets, bodyB->setIndex ); + + int localIndexA = bodyA->localIndex; + int localIndexB = bodyB->localIndex; + + b2BodySim* bodySimA = b2BodySimArray_Get( &setA->bodySims, localIndexA ); + b2BodySim* bodySimB = b2BodySimArray_Get( &setB->bodySims, localIndexB ); + + float mA = bodySimA->invMass; + float iA = bodySimA->invInertia; + float mB = bodySimB->invMass; + float iB = bodySimB->invInertia; + + base->invMassA = mA; + base->invMassB = mB; + base->invIA = iA; + base->invIB = iB; + + b2WeldJoint* joint = &base->weldJoint; + joint->indexA = bodyA->setIndex == b2_awakeSet ? localIndexA : B2_NULL_INDEX; + joint->indexB = bodyB->setIndex == b2_awakeSet ? localIndexB : B2_NULL_INDEX; + + b2Rot qA = bodySimA->transform.q; + b2Rot qB = bodySimB->transform.q; + + joint->anchorA = b2RotateVector( qA, b2Sub( base->localOriginAnchorA, bodySimA->localCenter ) ); + joint->anchorB = b2RotateVector( qB, b2Sub( base->localOriginAnchorB, bodySimB->localCenter ) ); + joint->deltaCenter = b2Sub( bodySimB->center, bodySimA->center ); + joint->deltaAngle = b2RelativeAngle( qB, qA ) - joint->referenceAngle; + + float ka = iA + iB; + joint->axialMass = ka > 0.0f ? 1.0f / ka : 0.0f; + + const float h = context->dt; + + if ( joint->linearHertz == 0.0f ) + { + joint->linearSoftness = context->jointSoftness; + } + else + { + joint->linearSoftness = b2MakeSoft( joint->linearHertz, joint->linearDampingRatio, context->h ); + } + + if ( joint->angularHertz == 0.0f ) + { + joint->angularSoftness = context->jointSoftness; + } + else + { + joint->angularSoftness = b2MakeSoft( joint->angularHertz, joint->angularDampingRatio, context->h ); + } + + if ( context->enableWarmStarting == false ) + { + joint->linearImpulse = b2Vec2_zero; + joint->angularImpulse = 0.0f; + } +} + +void b2WarmStartWeldJoint( b2JointSim* base, b2StepContext* context ) +{ + float mA = base->invMassA; + float mB = base->invMassB; + float iA = base->invIA; + float iB = base->invIB; + + // dummy state for static bodies + b2BodyState dummyState = b2_identityBodyState; + + b2WeldJoint* joint = &base->weldJoint; + + b2BodyState* stateA = joint->indexA == B2_NULL_INDEX ? &dummyState : context->states + joint->indexA; + b2BodyState* stateB = joint->indexB == B2_NULL_INDEX ? &dummyState : context->states + joint->indexB; + + b2Vec2 rA = b2RotateVector( stateA->deltaRotation, joint->anchorA ); + b2Vec2 rB = b2RotateVector( stateB->deltaRotation, joint->anchorB ); + + stateA->linearVelocity = b2MulSub( stateA->linearVelocity, mA, joint->linearImpulse ); + stateA->angularVelocity -= iA * ( b2Cross( rA, joint->linearImpulse ) + joint->angularImpulse ); + + stateB->linearVelocity = b2MulAdd( stateB->linearVelocity, mB, joint->linearImpulse ); + stateB->angularVelocity += iB * ( b2Cross( rB, joint->linearImpulse ) + joint->angularImpulse ); +} + +void b2SolveWeldJoint( b2JointSim* base, b2StepContext* context, bool useBias ) +{ + B2_ASSERT( base->type == b2_weldJoint ); + + float mA = base->invMassA; + float mB = base->invMassB; + float iA = base->invIA; + float iB = base->invIB; + + // dummy state for static bodies + b2BodyState dummyState = b2_identityBodyState; + + b2WeldJoint* joint = &base->weldJoint; + + b2BodyState* stateA = joint->indexA == B2_NULL_INDEX ? &dummyState : context->states + joint->indexA; + b2BodyState* stateB = joint->indexB == B2_NULL_INDEX ? &dummyState : context->states + joint->indexB; + + b2Vec2 vA = stateA->linearVelocity; + float wA = stateA->angularVelocity; + b2Vec2 vB = stateB->linearVelocity; + float wB = stateB->angularVelocity; + + // angular constraint + { + float bias = 0.0f; + float massScale = 1.0f; + float impulseScale = 0.0f; + if ( useBias || joint->angularHertz > 0.0f ) + { + float C = b2RelativeAngle( stateB->deltaRotation, stateA->deltaRotation ) + joint->deltaAngle; + bias = joint->angularSoftness.biasRate * C; + massScale = joint->angularSoftness.massScale; + impulseScale = joint->angularSoftness.impulseScale; + } + + float Cdot = wB - wA; + float impulse = -massScale * joint->axialMass * ( Cdot + bias ) - impulseScale * joint->angularImpulse; + joint->angularImpulse += impulse; + + wA -= iA * impulse; + wB += iB * impulse; + } + + // linear constraint + { + b2Vec2 rA = b2RotateVector( stateA->deltaRotation, joint->anchorA ); + b2Vec2 rB = b2RotateVector( stateB->deltaRotation, joint->anchorB ); + + b2Vec2 bias = b2Vec2_zero; + float massScale = 1.0f; + float impulseScale = 0.0f; + if ( useBias || joint->linearHertz > 0.0f ) + { + b2Vec2 dcA = stateA->deltaPosition; + b2Vec2 dcB = stateB->deltaPosition; + b2Vec2 C = b2Add( b2Add( b2Sub( dcB, dcA ), b2Sub( rB, rA ) ), joint->deltaCenter ); + + bias = b2MulSV( joint->linearSoftness.biasRate, C ); + massScale = joint->linearSoftness.massScale; + impulseScale = joint->linearSoftness.impulseScale; + } + + b2Vec2 Cdot = b2Sub( b2Add( vB, b2CrossSV( wB, rB ) ), b2Add( vA, b2CrossSV( wA, rA ) ) ); + + b2Mat22 K; + K.cx.x = mA + mB + rA.y * rA.y * iA + rB.y * rB.y * iB; + K.cy.x = -rA.y * rA.x * iA - rB.y * rB.x * iB; + K.cx.y = K.cy.x; + K.cy.y = mA + mB + rA.x * rA.x * iA + rB.x * rB.x * iB; + b2Vec2 b = b2Solve22( K, b2Add( Cdot, bias ) ); + + b2Vec2 impulse = { + -massScale * b.x - impulseScale * joint->linearImpulse.x, + -massScale * b.y - impulseScale * joint->linearImpulse.y, + }; + + joint->linearImpulse = b2Add( joint->linearImpulse, impulse ); + + vA = b2MulSub( vA, mA, impulse ); + wA -= iA * b2Cross( rA, impulse ); + vB = b2MulAdd( vB, mB, impulse ); + wB += iB * b2Cross( rB, impulse ); + } + + stateA->linearVelocity = vA; + stateA->angularVelocity = wA; + stateB->linearVelocity = vB; + stateB->angularVelocity = wB; +} + +#if 0 +void b2DumpWeldJoint() +{ + int32 indexA = m_bodyA->m_islandIndex; + int32 indexB = m_bodyB->m_islandIndex; + + b2Dump(" b2WeldJointDef jd;\n"); + b2Dump(" jd.bodyA = sims[%d];\n", indexA); + b2Dump(" jd.bodyB = sims[%d];\n", indexB); + b2Dump(" jd.collideConnected = bool(%d);\n", m_collideConnected); + b2Dump(" jd.localAnchorA.Set(%.9g, %.9g);\n", m_localAnchorA.x, m_localAnchorA.y); + b2Dump(" jd.localAnchorB.Set(%.9g, %.9g);\n", m_localAnchorB.x, m_localAnchorB.y); + b2Dump(" jd.referenceAngle = %.9g;\n", m_referenceAngle); + b2Dump(" jd.stiffness = %.9g;\n", m_stiffness); + b2Dump(" jd.damping = %.9g;\n", m_damping); + b2Dump(" joints[%d] = m_world->CreateJoint(&jd);\n", m_index); +} +#endif diff --git a/3rdparty/box2d/src/wheel_joint.c b/3rdparty/box2d/src/wheel_joint.c new file mode 100644 index 000000000000..e4d96b401505 --- /dev/null +++ b/3rdparty/box2d/src/wheel_joint.c @@ -0,0 +1,554 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#include "body.h" +#include "core.h" +#include "joint.h" +#include "solver.h" +#include "solver_set.h" +#include "world.h" + +// needed for dll export +#include "box2d/box2d.h" + +#include + +void b2WheelJoint_EnableSpring( b2JointId jointId, bool enableSpring ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_wheelJoint ); + + if ( enableSpring != joint->wheelJoint.enableSpring ) + { + joint->wheelJoint.enableSpring = enableSpring; + joint->wheelJoint.springImpulse = 0.0f; + } +} + +bool b2WheelJoint_IsSpringEnabled( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_wheelJoint ); + return joint->wheelJoint.enableSpring; +} + +void b2WheelJoint_SetSpringHertz( b2JointId jointId, float hertz ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_wheelJoint ); + joint->wheelJoint.hertz = hertz; +} + +float b2WheelJoint_GetSpringHertz( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_wheelJoint ); + return joint->wheelJoint.hertz; +} + +void b2WheelJoint_SetSpringDampingRatio( b2JointId jointId, float dampingRatio ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_wheelJoint ); + joint->wheelJoint.dampingRatio = dampingRatio; +} + +float b2WheelJoint_GetSpringDampingRatio( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_wheelJoint ); + return joint->wheelJoint.dampingRatio; +} + +void b2WheelJoint_EnableLimit( b2JointId jointId, bool enableLimit ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_wheelJoint ); + if ( joint->wheelJoint.enableLimit != enableLimit ) + { + joint->wheelJoint.lowerImpulse = 0.0f; + joint->wheelJoint.upperImpulse = 0.0f; + joint->wheelJoint.enableLimit = enableLimit; + } +} + +bool b2WheelJoint_IsLimitEnabled( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_wheelJoint ); + return joint->wheelJoint.enableLimit; +} + +float b2WheelJoint_GetLowerLimit( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_wheelJoint ); + return joint->wheelJoint.lowerTranslation; +} + +float b2WheelJoint_GetUpperLimit( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_wheelJoint ); + return joint->wheelJoint.upperTranslation; +} + +void b2WheelJoint_SetLimits( b2JointId jointId, float lower, float upper ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_wheelJoint ); + if ( lower != joint->wheelJoint.lowerTranslation || upper != joint->wheelJoint.upperTranslation ) + { + joint->wheelJoint.lowerTranslation = b2MinFloat( lower, upper ); + joint->wheelJoint.upperTranslation = b2MaxFloat( lower, upper ); + joint->wheelJoint.lowerImpulse = 0.0f; + joint->wheelJoint.upperImpulse = 0.0f; + } +} + +void b2WheelJoint_EnableMotor( b2JointId jointId, bool enableMotor ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_wheelJoint ); + if ( joint->wheelJoint.enableMotor != enableMotor ) + { + joint->wheelJoint.motorImpulse = 0.0f; + joint->wheelJoint.enableMotor = enableMotor; + } +} + +bool b2WheelJoint_IsMotorEnabled( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_wheelJoint ); + return joint->wheelJoint.enableMotor; +} + +void b2WheelJoint_SetMotorSpeed( b2JointId jointId, float motorSpeed ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_wheelJoint ); + joint->wheelJoint.motorSpeed = motorSpeed; +} + +float b2WheelJoint_GetMotorSpeed( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_wheelJoint ); + return joint->wheelJoint.motorSpeed; +} + +float b2WheelJoint_GetMotorTorque( b2JointId jointId ) +{ + b2World* world = b2GetWorld( jointId.world0 ); + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_wheelJoint ); + return world->inv_h * joint->wheelJoint.motorImpulse; +} + +void b2WheelJoint_SetMaxMotorTorque( b2JointId jointId, float torque ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_wheelJoint ); + joint->wheelJoint.maxMotorTorque = torque; +} + +float b2WheelJoint_GetMaxMotorTorque( b2JointId jointId ) +{ + b2JointSim* joint = b2GetJointSimCheckType( jointId, b2_wheelJoint ); + return joint->wheelJoint.maxMotorTorque; +} + +b2Vec2 b2GetWheelJointForce( b2World* world, b2JointSim* base ) +{ + b2WheelJoint* joint = &base->wheelJoint; + + // This is a frame behind + b2Vec2 axisA = joint->axisA; + b2Vec2 perpA = b2LeftPerp( axisA ); + + float perpForce = world->inv_h * joint->perpImpulse; + float axialForce = world->inv_h * ( joint->springImpulse + joint->lowerImpulse - joint->upperImpulse ); + + b2Vec2 force = b2Add( b2MulSV( perpForce, perpA ), b2MulSV( axialForce, axisA ) ); + return force; +} + +float b2GetWheelJointTorque( b2World* world, b2JointSim* base ) +{ + return world->inv_h * base->wheelJoint.motorImpulse; +} + +// Linear constraint (point-to-line) +// d = pB - pA = xB + rB - xA - rA +// C = dot(ay, d) +// Cdot = dot(d, cross(wA, ay)) + dot(ay, vB + cross(wB, rB) - vA - cross(wA, rA)) +// = -dot(ay, vA) - dot(cross(d + rA, ay), wA) + dot(ay, vB) + dot(cross(rB, ay), vB) +// J = [-ay, -cross(d + rA, ay), ay, cross(rB, ay)] + +// Spring linear constraint +// C = dot(ax, d) +// Cdot = = -dot(ax, vA) - dot(cross(d + rA, ax), wA) + dot(ax, vB) + dot(cross(rB, ax), vB) +// J = [-ax -cross(d+rA, ax) ax cross(rB, ax)] + +// Motor rotational constraint +// Cdot = wB - wA +// J = [0 0 -1 0 0 1] + +void b2PrepareWheelJoint( b2JointSim* base, b2StepContext* context ) +{ + B2_ASSERT( base->type == b2_wheelJoint ); + + // chase body id to the solver set where the body lives + int idA = base->bodyIdA; + int idB = base->bodyIdB; + + b2World* world = context->world; + + b2Body* bodyA = b2BodyArray_Get( &world->bodies, idA ); + b2Body* bodyB = b2BodyArray_Get( &world->bodies, idB ); + + B2_ASSERT( bodyA->setIndex == b2_awakeSet || bodyB->setIndex == b2_awakeSet ); + b2SolverSet* setA = b2SolverSetArray_Get( &world->solverSets, bodyA->setIndex ); + b2SolverSet* setB = b2SolverSetArray_Get( &world->solverSets, bodyB->setIndex ); + + int localIndexA = bodyA->localIndex; + int localIndexB = bodyB->localIndex; + + b2BodySim* bodySimA = b2BodySimArray_Get( &setA->bodySims, localIndexA ); + b2BodySim* bodySimB = b2BodySimArray_Get( &setB->bodySims, localIndexB ); + + float mA = bodySimA->invMass; + float iA = bodySimA->invInertia; + float mB = bodySimB->invMass; + float iB = bodySimB->invInertia; + + base->invMassA = mA; + base->invMassB = mB; + base->invIA = iA; + base->invIB = iB; + + b2WheelJoint* joint = &base->wheelJoint; + + joint->indexA = bodyA->setIndex == b2_awakeSet ? localIndexA : B2_NULL_INDEX; + joint->indexB = bodyB->setIndex == b2_awakeSet ? localIndexB : B2_NULL_INDEX; + + b2Rot qA = bodySimA->transform.q; + b2Rot qB = bodySimB->transform.q; + + joint->anchorA = b2RotateVector( qA, b2Sub( base->localOriginAnchorA, bodySimA->localCenter ) ); + joint->anchorB = b2RotateVector( qB, b2Sub( base->localOriginAnchorB, bodySimB->localCenter ) ); + joint->axisA = b2RotateVector( qA, joint->localAxisA ); + joint->deltaCenter = b2Sub( bodySimB->center, bodySimA->center ); + + b2Vec2 rA = joint->anchorA; + b2Vec2 rB = joint->anchorB; + + b2Vec2 d = b2Add( joint->deltaCenter, b2Sub( rB, rA ) ); + b2Vec2 axisA = joint->axisA; + b2Vec2 perpA = b2LeftPerp( axisA ); + + // perpendicular constraint (keep wheel on line) + float s1 = b2Cross( b2Add( d, rA ), perpA ); + float s2 = b2Cross( rB, perpA ); + + float kp = mA + mB + iA * s1 * s1 + iB * s2 * s2; + joint->perpMass = kp > 0.0f ? 1.0f / kp : 0.0f; + + // spring constraint + float a1 = b2Cross( b2Add( d, rA ), axisA ); + float a2 = b2Cross( rB, axisA ); + + float ka = mA + mB + iA * a1 * a1 + iB * a2 * a2; + joint->axialMass = ka > 0.0f ? 1.0f / ka : 0.0f; + + joint->springSoftness = b2MakeSoft( joint->hertz, joint->dampingRatio, context->h ); + + float km = iA + iB; + joint->motorMass = km > 0.0f ? 1.0f / km : 0.0f; + + if ( context->enableWarmStarting == false ) + { + joint->perpImpulse = 0.0f; + joint->springImpulse = 0.0f; + joint->motorImpulse = 0.0f; + joint->lowerImpulse = 0.0f; + joint->upperImpulse = 0.0f; + } +} + +void b2WarmStartWheelJoint( b2JointSim* base, b2StepContext* context ) +{ + B2_ASSERT( base->type == b2_wheelJoint ); + + float mA = base->invMassA; + float mB = base->invMassB; + float iA = base->invIA; + float iB = base->invIB; + + // dummy state for static bodies + b2BodyState dummyState = b2_identityBodyState; + + b2WheelJoint* joint = &base->wheelJoint; + + b2BodyState* stateA = joint->indexA == B2_NULL_INDEX ? &dummyState : context->states + joint->indexA; + b2BodyState* stateB = joint->indexB == B2_NULL_INDEX ? &dummyState : context->states + joint->indexB; + + b2Vec2 rA = b2RotateVector( stateA->deltaRotation, joint->anchorA ); + b2Vec2 rB = b2RotateVector( stateB->deltaRotation, joint->anchorB ); + + b2Vec2 d = b2Add( b2Add( b2Sub( stateB->deltaPosition, stateA->deltaPosition ), joint->deltaCenter ), b2Sub( rB, rA ) ); + b2Vec2 axisA = b2RotateVector( stateA->deltaRotation, joint->axisA ); + b2Vec2 perpA = b2LeftPerp( axisA ); + + float a1 = b2Cross( b2Add( d, rA ), axisA ); + float a2 = b2Cross( rB, axisA ); + float s1 = b2Cross( b2Add( d, rA ), perpA ); + float s2 = b2Cross( rB, perpA ); + + float axialImpulse = joint->springImpulse + joint->lowerImpulse - joint->upperImpulse; + + b2Vec2 P = b2Add( b2MulSV( axialImpulse, axisA ), b2MulSV( joint->perpImpulse, perpA ) ); + float LA = axialImpulse * a1 + joint->perpImpulse * s1 + joint->motorImpulse; + float LB = axialImpulse * a2 + joint->perpImpulse * s2 + joint->motorImpulse; + + stateA->linearVelocity = b2MulSub( stateA->linearVelocity, mA, P ); + stateA->angularVelocity -= iA * LA; + stateB->linearVelocity = b2MulAdd( stateB->linearVelocity, mB, P ); + stateB->angularVelocity += iB * LB; +} + +void b2SolveWheelJoint( b2JointSim* base, b2StepContext* context, bool useBias ) +{ + B2_ASSERT( base->type == b2_wheelJoint ); + + float mA = base->invMassA; + float mB = base->invMassB; + float iA = base->invIA; + float iB = base->invIB; + + // dummy state for static bodies + b2BodyState dummyState = b2_identityBodyState; + + b2WheelJoint* joint = &base->wheelJoint; + + // This is a dummy body to represent a static body since static bodies don't have a solver body. + b2BodyState dummyBody = { 0 }; + + b2BodyState* stateA = joint->indexA == B2_NULL_INDEX ? &dummyState : context->states + joint->indexA; + b2BodyState* stateB = joint->indexB == B2_NULL_INDEX ? &dummyState : context->states + joint->indexB; + + b2Vec2 vA = stateA->linearVelocity; + float wA = stateA->angularVelocity; + b2Vec2 vB = stateB->linearVelocity; + float wB = stateB->angularVelocity; + + bool fixedRotation = ( iA + iB == 0.0f ); + + // current anchors + b2Vec2 rA = b2RotateVector( stateA->deltaRotation, joint->anchorA ); + b2Vec2 rB = b2RotateVector( stateB->deltaRotation, joint->anchorB ); + + b2Vec2 d = b2Add( b2Add( b2Sub( stateB->deltaPosition, stateA->deltaPosition ), joint->deltaCenter ), b2Sub( rB, rA ) ); + b2Vec2 axisA = b2RotateVector( stateA->deltaRotation, joint->axisA ); + float translation = b2Dot( axisA, d ); + + float a1 = b2Cross( b2Add( d, rA ), axisA ); + float a2 = b2Cross( rB, axisA ); + + // motor constraint + if ( joint->enableMotor && fixedRotation == false ) + { + float Cdot = wB - wA - joint->motorSpeed; + float impulse = -joint->motorMass * Cdot; + float oldImpulse = joint->motorImpulse; + float maxImpulse = context->h * joint->maxMotorTorque; + joint->motorImpulse = b2ClampFloat( joint->motorImpulse + impulse, -maxImpulse, maxImpulse ); + impulse = joint->motorImpulse - oldImpulse; + + wA -= iA * impulse; + wB += iB * impulse; + } + + // spring constraint + if ( joint->enableSpring ) + { + // This is a real spring and should be applied even during relax + float C = translation; + float bias = joint->springSoftness.biasRate * C; + float massScale = joint->springSoftness.massScale; + float impulseScale = joint->springSoftness.impulseScale; + + float Cdot = b2Dot( axisA, b2Sub( vB, vA ) ) + a2 * wB - a1 * wA; + float impulse = -massScale * joint->axialMass * ( Cdot + bias ) - impulseScale * joint->springImpulse; + joint->springImpulse += impulse; + + b2Vec2 P = b2MulSV( impulse, axisA ); + float LA = impulse * a1; + float LB = impulse * a2; + + vA = b2MulSub( vA, mA, P ); + wA -= iA * LA; + vB = b2MulAdd( vB, mB, P ); + wB += iB * LB; + } + + if ( joint->enableLimit ) + { + float translation = b2Dot( axisA, d ); + + // Lower limit + { + float C = translation - joint->lowerTranslation; + float bias = 0.0f; + float massScale = 1.0f; + float impulseScale = 0.0f; + + if ( C > 0.0f ) + { + // speculation + bias = C * context->inv_h; + } + else if ( useBias ) + { + bias = context->jointSoftness.biasRate * C; + massScale = context->jointSoftness.massScale; + impulseScale = context->jointSoftness.impulseScale; + } + + float Cdot = b2Dot( axisA, b2Sub( vB, vA ) ) + a2 * wB - a1 * wA; + float impulse = -massScale * joint->axialMass * ( Cdot + bias ) - impulseScale * joint->lowerImpulse; + float oldImpulse = joint->lowerImpulse; + joint->lowerImpulse = b2MaxFloat( oldImpulse + impulse, 0.0f ); + impulse = joint->lowerImpulse - oldImpulse; + + b2Vec2 P = b2MulSV( impulse, axisA ); + float LA = impulse * a1; + float LB = impulse * a2; + + vA = b2MulSub( vA, mA, P ); + wA -= iA * LA; + vB = b2MulAdd( vB, mB, P ); + wB += iB * LB; + } + + // Upper limit + // Note: signs are flipped to keep C positive when the constraint is satisfied. + // This also keeps the impulse positive when the limit is active. + { + // sign flipped + float C = joint->upperTranslation - translation; + float bias = 0.0f; + float massScale = 1.0f; + float impulseScale = 0.0f; + + if ( C > 0.0f ) + { + // speculation + bias = C * context->inv_h; + } + else if ( useBias ) + { + bias = context->jointSoftness.biasRate * C; + massScale = context->jointSoftness.massScale; + impulseScale = context->jointSoftness.impulseScale; + } + + // sign flipped on Cdot + float Cdot = b2Dot( axisA, b2Sub( vA, vB ) ) + a1 * wA - a2 * wB; + float impulse = -massScale * joint->axialMass * ( Cdot + bias ) - impulseScale * joint->upperImpulse; + float oldImpulse = joint->upperImpulse; + joint->upperImpulse = b2MaxFloat( oldImpulse + impulse, 0.0f ); + impulse = joint->upperImpulse - oldImpulse; + + b2Vec2 P = b2MulSV( impulse, axisA ); + float LA = impulse * a1; + float LB = impulse * a2; + + // sign flipped on applied impulse + vA = b2MulAdd( vA, mA, P ); + wA += iA * LA; + vB = b2MulSub( vB, mB, P ); + wB -= iB * LB; + } + } + + // point to line constraint + { + b2Vec2 perpA = b2LeftPerp( axisA ); + + float bias = 0.0f; + float massScale = 1.0f; + float impulseScale = 0.0f; + if ( useBias ) + { + float C = b2Dot( perpA, d ); + bias = context->jointSoftness.biasRate * C; + massScale = context->jointSoftness.massScale; + impulseScale = context->jointSoftness.impulseScale; + } + + float s1 = b2Cross( b2Add( d, rA ), perpA ); + float s2 = b2Cross( rB, perpA ); + float Cdot = b2Dot( perpA, b2Sub( vB, vA ) ) + s2 * wB - s1 * wA; + + float impulse = -massScale * joint->perpMass * ( Cdot + bias ) - impulseScale * joint->perpImpulse; + joint->perpImpulse += impulse; + + b2Vec2 P = b2MulSV( impulse, perpA ); + float LA = impulse * s1; + float LB = impulse * s2; + + vA = b2MulSub( vA, mA, P ); + wA -= iA * LA; + vB = b2MulAdd( vB, mB, P ); + wB += iB * LB; + } + + stateA->linearVelocity = vA; + stateA->angularVelocity = wA; + stateB->linearVelocity = vB; + stateB->angularVelocity = wB; +} + +#if 0 +void b2WheelJoint_Dump() +{ + int32 indexA = joint->bodyA->joint->islandIndex; + int32 indexB = joint->bodyB->joint->islandIndex; + + b2Dump(" b2WheelJointDef jd;\n"); + b2Dump(" jd.bodyA = sims[%d];\n", indexA); + b2Dump(" jd.bodyB = sims[%d];\n", indexB); + b2Dump(" jd.collideConnected = bool(%d);\n", joint->collideConnected); + b2Dump(" jd.localAnchorA.Set(%.9g, %.9g);\n", joint->localAnchorA.x, joint->localAnchorA.y); + b2Dump(" jd.localAnchorB.Set(%.9g, %.9g);\n", joint->localAnchorB.x, joint->localAnchorB.y); + b2Dump(" jd.referenceAngle = %.9g;\n", joint->referenceAngle); + b2Dump(" jd.enableLimit = bool(%d);\n", joint->enableLimit); + b2Dump(" jd.lowerAngle = %.9g;\n", joint->lowerAngle); + b2Dump(" jd.upperAngle = %.9g;\n", joint->upperAngle); + b2Dump(" jd.enableMotor = bool(%d);\n", joint->enableMotor); + b2Dump(" jd.motorSpeed = %.9g;\n", joint->motorSpeed); + b2Dump(" jd.maxMotorTorque = %.9g;\n", joint->maxMotorTorque); + b2Dump(" joints[%d] = joint->world->CreateJoint(&jd);\n", joint->index); +} +#endif + +void b2DrawWheelJoint( b2DebugDraw* draw, b2JointSim* base, b2Transform transformA, b2Transform transformB ) +{ + B2_ASSERT( base->type == b2_wheelJoint ); + + b2WheelJoint* joint = &base->wheelJoint; + + b2Vec2 pA = b2TransformPoint( transformA, base->localOriginAnchorA ); + b2Vec2 pB = b2TransformPoint( transformB, base->localOriginAnchorB ); + b2Vec2 axis = b2RotateVector( transformA.q, joint->localAxisA ); + + b2HexColor c1 = b2_colorGray7; + b2HexColor c2 = b2_colorGreen; + b2HexColor c3 = b2_colorRed; + b2HexColor c4 = b2_colorGray4; + b2HexColor c5 = b2_colorBlue; + + draw->DrawSegment( pA, pB, c5, draw->context ); + + if ( joint->enableLimit ) + { + b2Vec2 lower = b2MulAdd( pA, joint->lowerTranslation, axis ); + b2Vec2 upper = b2MulAdd( pA, joint->upperTranslation, axis ); + b2Vec2 perp = b2LeftPerp( axis ); + draw->DrawSegment( lower, upper, c1, draw->context ); + draw->DrawSegment( b2MulSub( lower, 0.1f, perp ), b2MulAdd( lower, 0.1f, perp ), c2, draw->context ); + draw->DrawSegment( b2MulSub( upper, 0.1f, perp ), b2MulAdd( upper, 0.1f, perp ), c3, draw->context ); + } + else + { + draw->DrawSegment( b2MulSub( pA, 1.0f, axis ), b2MulAdd( pA, 1.0f, axis ), c1, draw->context ); + } + + draw->DrawPoint( pA, 5.0f, c1, draw->context ); + draw->DrawPoint( pB, 5.0f, c4, draw->context ); +} diff --git a/3rdparty/box2d/src/world.c b/3rdparty/box2d/src/world.c new file mode 100644 index 000000000000..53d462dacc2e --- /dev/null +++ b/3rdparty/box2d/src/world.c @@ -0,0 +1,2972 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#if defined( _MSC_VER ) && !defined( _CRT_SECURE_NO_WARNINGS ) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "world.h" + +#include "aabb.h" +#include "array.h" +#include "bitset.h" +#include "body.h" +#include "broad_phase.h" +#include "constraint_graph.h" +#include "contact.h" +#include "core.h" +#include "ctz.h" +#include "island.h" +#include "joint.h" +#include "shape.h" +#include "solver.h" +#include "solver_set.h" +#include "stack_allocator.h" + +#include "box2d/box2d.h" + +#include +#include +#include + +_Static_assert( b2_maxWorlds > 0, "must be 1 or more" ); +b2World b2_worlds[b2_maxWorlds]; + +B2_ARRAY_SOURCE( b2BodyMoveEvent, b2BodyMoveEvent ); +B2_ARRAY_SOURCE( b2ContactBeginTouchEvent, b2ContactBeginTouchEvent ); +B2_ARRAY_SOURCE( b2ContactEndTouchEvent, b2ContactEndTouchEvent ); +B2_ARRAY_SOURCE( b2ContactHitEvent, b2ContactHitEvent ); +B2_ARRAY_SOURCE( b2SensorBeginTouchEvent, b2SensorBeginTouchEvent ); +B2_ARRAY_SOURCE( b2SensorEndTouchEvent, b2SensorEndTouchEvent ); +B2_ARRAY_SOURCE( b2TaskContext, b2TaskContext ); + +b2World* b2GetWorldFromId( b2WorldId id ) +{ + B2_ASSERT( 1 <= id.index1 && id.index1 <= b2_maxWorlds ); + b2World* world = b2_worlds + ( id.index1 - 1 ); + B2_ASSERT( id.index1 == world->worldId + 1 ); + B2_ASSERT( id.revision == world->revision ); + return world; +} + +b2World* b2GetWorld( int index ) +{ + B2_ASSERT( 0 <= index && index < b2_maxWorlds ); + b2World* world = b2_worlds + index; + B2_ASSERT( world->worldId == index ); + return world; +} + +b2World* b2GetWorldLocked( int index ) +{ + B2_ASSERT( 0 <= index && index < b2_maxWorlds ); + b2World* world = b2_worlds + index; + B2_ASSERT( world->worldId == index ); + if ( world->locked ) + { + B2_ASSERT( false ); + return NULL; + } + + return world; +} + +static void* b2DefaultAddTaskFcn( b2TaskCallback* task, int count, int minRange, void* taskContext, void* userContext ) +{ + B2_MAYBE_UNUSED( minRange ); + B2_MAYBE_UNUSED( userContext ); + task( 0, count, 0, taskContext ); + return NULL; +} + +static void b2DefaultFinishTaskFcn( void* userTask, void* userContext ) +{ + B2_MAYBE_UNUSED( userTask ); + B2_MAYBE_UNUSED( userContext ); +} + +b2WorldId b2CreateWorld( const b2WorldDef* def ) +{ + _Static_assert( b2_maxWorlds < UINT16_MAX, "b2_maxWorlds limit exceeded" ); + b2CheckDef( def ); + + int worldId = B2_NULL_INDEX; + for ( int i = 0; i < b2_maxWorlds; ++i ) + { + if ( b2_worlds[i].inUse == false ) + { + worldId = i; + break; + } + } + + if ( worldId == B2_NULL_INDEX ) + { + return ( b2WorldId ){ 0 }; + } + + b2InitializeContactRegisters(); + + b2World* world = b2_worlds + worldId; + uint16_t revision = world->revision; + + *world = ( b2World ){ 0 }; + + world->worldId = (uint16_t)worldId; + world->revision = revision; + world->inUse = true; + + world->stackAllocator = b2CreateStackAllocator( 2048 ); + b2CreateBroadPhase( &world->broadPhase ); + b2CreateGraph( &world->constraintGraph, 16 ); + + // pools + world->bodyIdPool = b2CreateIdPool(); + world->bodies = b2BodyArray_Create( 16 ); + world->solverSets = b2SolverSetArray_Create( 8 ); + + // add empty static, active, and disabled body sets + world->solverSetIdPool = b2CreateIdPool(); + b2SolverSet set = { 0 }; + + // static set + set.setIndex = b2AllocId( &world->solverSetIdPool ); + b2SolverSetArray_Push( &world->solverSets, set ); + B2_ASSERT( world->solverSets.data[b2_staticSet].setIndex == b2_staticSet ); + + // disabled set + set.setIndex = b2AllocId( &world->solverSetIdPool ); + b2SolverSetArray_Push( &world->solverSets, set ); + B2_ASSERT( world->solverSets.data[b2_disabledSet].setIndex == b2_disabledSet ); + + // awake set + set.setIndex = b2AllocId( &world->solverSetIdPool ); + b2SolverSetArray_Push( &world->solverSets, set ); + B2_ASSERT( world->solverSets.data[b2_awakeSet].setIndex == b2_awakeSet ); + + world->shapeIdPool = b2CreateIdPool(); + world->shapes = b2ShapeArray_Create( 16 ); + + world->chainIdPool = b2CreateIdPool(); + world->chainShapes = b2ChainShapeArray_Create( 4 ); + + world->contactIdPool = b2CreateIdPool(); + world->contacts = b2ContactArray_Create( 16 ); + + world->jointIdPool = b2CreateIdPool(); + world->joints = b2JointArray_Create( 16 ); + + world->islandIdPool = b2CreateIdPool(); + world->islands = b2IslandArray_Create( 8 ); + + world->bodyMoveEvents = b2BodyMoveEventArray_Create( 4 ); + world->sensorBeginEvents = b2SensorBeginTouchEventArray_Create( 4 ); + world->sensorEndEvents = b2SensorEndTouchEventArray_Create( 4 ); + world->contactBeginEvents = b2ContactBeginTouchEventArray_Create( 4 ); + world->contactEndEvents = b2ContactEndTouchEventArray_Create( 4 ); + world->contactHitEvents = b2ContactHitEventArray_Create( 4 ); + + world->stepIndex = 0; + world->splitIslandId = B2_NULL_INDEX; + world->activeTaskCount = 0; + world->taskCount = 0; + world->gravity = def->gravity; + world->hitEventThreshold = def->hitEventThreshold; + world->restitutionThreshold = def->restitutionThreshold; + world->maxLinearVelocity = def->maximumLinearVelocity; + world->contactPushoutVelocity = def->contactPushoutVelocity; + world->contactHertz = def->contactHertz; + world->contactDampingRatio = def->contactDampingRatio; + world->jointHertz = def->jointHertz; + world->jointDampingRatio = def->jointDampingRatio; + world->enableSleep = def->enableSleep; + world->locked = false; + world->enableWarmStarting = true; + world->enableContinuous = def->enableContinuous; + world->userTreeTask = NULL; + + if ( def->workerCount > 0 && def->enqueueTask != NULL && def->finishTask != NULL ) + { + world->workerCount = b2MinInt( def->workerCount, b2_maxWorkers ); + world->enqueueTaskFcn = def->enqueueTask; + world->finishTaskFcn = def->finishTask; + world->userTaskContext = def->userTaskContext; + } + else + { + world->workerCount = 1; + world->enqueueTaskFcn = b2DefaultAddTaskFcn; + world->finishTaskFcn = b2DefaultFinishTaskFcn; + world->userTaskContext = NULL; + } + + world->taskContexts = b2TaskContextArray_Create( world->workerCount ); + b2TaskContextArray_Resize( &world->taskContexts, world->workerCount ); + + for ( int i = 0; i < world->workerCount; ++i ) + { + world->taskContexts.data[i].contactStateBitSet = b2CreateBitSet( 1024 ); + world->taskContexts.data[i].enlargedSimBitSet = b2CreateBitSet( 256 ); + world->taskContexts.data[i].awakeIslandBitSet = b2CreateBitSet( 256 ); + } + + world->debugBodySet = b2CreateBitSet( 256 ); + world->debugJointSet = b2CreateBitSet( 256 ); + world->debugContactSet = b2CreateBitSet( 256 ); + + // add one to worldId so that 0 represents a null b2WorldId + return ( b2WorldId ){ (uint16_t)( worldId + 1 ), world->revision }; +} + +void b2DestroyWorld( b2WorldId worldId ) +{ + b2World* world = b2GetWorldFromId( worldId ); + + b2DestroyBitSet( &world->debugBodySet ); + b2DestroyBitSet( &world->debugJointSet ); + b2DestroyBitSet( &world->debugContactSet ); + + for ( int i = 0; i < world->workerCount; ++i ) + { + b2DestroyBitSet( &world->taskContexts.data[i].contactStateBitSet ); + b2DestroyBitSet( &world->taskContexts.data[i].enlargedSimBitSet ); + b2DestroyBitSet( &world->taskContexts.data[i].awakeIslandBitSet ); + } + + b2TaskContextArray_Destroy( &world->taskContexts ); + + b2BodyMoveEventArray_Destroy( &world->bodyMoveEvents ); + b2SensorBeginTouchEventArray_Destroy( &world->sensorBeginEvents ); + b2SensorEndTouchEventArray_Destroy( &world->sensorEndEvents ); + b2ContactBeginTouchEventArray_Destroy( &world->contactBeginEvents ); + b2ContactEndTouchEventArray_Destroy( &world->contactEndEvents ); + b2ContactHitEventArray_Destroy( &world->contactHitEvents ); + + int chainCapacity = world->chainShapes.count; + for ( int i = 0; i < chainCapacity; ++i ) + { + b2ChainShape* chain = world->chainShapes.data + i; + if ( chain->id != B2_NULL_INDEX ) + { + b2Free( chain->shapeIndices, chain->count * sizeof( int ) ); + } + else + { + B2_ASSERT( chain->shapeIndices == NULL ); + } + } + + b2BodyArray_Destroy( &world->bodies ); + b2ShapeArray_Destroy( &world->shapes ); + b2ChainShapeArray_Destroy( &world->chainShapes ); + b2ContactArray_Destroy( &world->contacts ); + b2JointArray_Destroy( &world->joints ); + b2IslandArray_Destroy( &world->islands ); + + // Destroy solver sets + int setCapacity = world->solverSets.count; + for ( int i = 0; i < setCapacity; ++i ) + { + b2SolverSet* set = world->solverSets.data + i; + if ( set->setIndex != B2_NULL_INDEX ) + { + b2DestroySolverSet( world, i ); + } + } + + b2SolverSetArray_Destroy( &world->solverSets ); + + b2DestroyGraph( &world->constraintGraph ); + b2DestroyBroadPhase( &world->broadPhase ); + + b2DestroyIdPool( &world->bodyIdPool ); + b2DestroyIdPool( &world->shapeIdPool ); + b2DestroyIdPool( &world->chainIdPool ); + b2DestroyIdPool( &world->contactIdPool ); + b2DestroyIdPool( &world->jointIdPool ); + b2DestroyIdPool( &world->islandIdPool ); + b2DestroyIdPool( &world->solverSetIdPool ); + + b2DestroyStackAllocator( &world->stackAllocator ); + + // Wipe world but preserve revision + uint16_t revision = world->revision; + *world = ( b2World ){ 0 }; + world->worldId = B2_NULL_INDEX; + world->revision = revision + 1; +} + +static void b2CollideTask( int startIndex, int endIndex, uint32_t threadIndex, void* context ) +{ + b2TracyCZoneNC( collide_task, "Collide Task", b2_colorDodgerBlue, true ); + + b2StepContext* stepContext = context; + b2World* world = stepContext->world; + B2_ASSERT( threadIndex < world->workerCount ); + b2TaskContext* taskContext = world->taskContexts.data + threadIndex; + b2ContactSim** contactSims = stepContext->contacts; + b2Shape* shapes = world->shapes.data; + b2Body* bodies = world->bodies.data; + + B2_ASSERT( startIndex < endIndex ); + + for ( int i = startIndex; i < endIndex; ++i ) + { + b2ContactSim* contactSim = contactSims[i]; + + int contactId = contactSim->contactId; + + b2Shape* shapeA = shapes + contactSim->shapeIdA; + b2Shape* shapeB = shapes + contactSim->shapeIdB; + + // Do proxies still overlap? + bool overlap = b2AABB_Overlaps( shapeA->fatAABB, shapeB->fatAABB ); + if ( overlap == false ) + { + contactSim->simFlags |= b2_simDisjoint; + contactSim->simFlags &= ~b2_simTouchingFlag; + b2SetBit( &taskContext->contactStateBitSet, contactId ); + } + else + { + bool wasTouching = ( contactSim->simFlags & b2_simTouchingFlag ); + + // Update contact respecting shape/body order (A,B) + b2Body* bodyA = bodies + shapeA->bodyId; + b2Body* bodyB = bodies + shapeB->bodyId; + b2BodySim* bodySimA = b2GetBodySim( world, bodyA ); + b2BodySim* bodySimB = b2GetBodySim( world, bodyB ); + + // avoid cache misses in b2PrepareContactsTask + contactSim->bodySimIndexA = bodyA->setIndex == b2_awakeSet ? bodyA->localIndex : B2_NULL_INDEX; + contactSim->invMassA = bodySimA->invMass; + contactSim->invIA = bodySimA->invInertia; + + contactSim->bodySimIndexB = bodyB->setIndex == b2_awakeSet ? bodyB->localIndex : B2_NULL_INDEX; + contactSim->invMassB = bodySimB->invMass; + contactSim->invIB = bodySimB->invInertia; + + b2Transform transformA = bodySimA->transform; + b2Transform transformB = bodySimB->transform; + + b2Vec2 centerOffsetA = b2RotateVector( transformA.q, bodySimA->localCenter ); + b2Vec2 centerOffsetB = b2RotateVector( transformB.q, bodySimB->localCenter ); + + // This updates solid contacts and sensors + bool touching = + b2UpdateContact( world, contactSim, shapeA, transformA, centerOffsetA, shapeB, transformB, centerOffsetB ); + + // State changes that affect island connectivity. Also contact and sensor events. + if ( touching == true && wasTouching == false ) + { + contactSim->simFlags |= b2_simStartedTouching; + b2SetBit( &taskContext->contactStateBitSet, contactId ); + } + else if ( touching == false && wasTouching == true ) + { + contactSim->simFlags |= b2_simStoppedTouching; + b2SetBit( &taskContext->contactStateBitSet, contactId ); + } + } + } + + b2TracyCZoneEnd( collide_task ); +} + +static void b2UpdateTreesTask( int startIndex, int endIndex, uint32_t threadIndex, void* context ) +{ + B2_MAYBE_UNUSED( startIndex ); + B2_MAYBE_UNUSED( endIndex ); + B2_MAYBE_UNUSED( threadIndex ); + + b2TracyCZoneNC( tree_task, "Rebuild Trees", b2_colorSnow, true ); + + b2World* world = context; + b2BroadPhase_RebuildTrees( &world->broadPhase ); + + b2TracyCZoneEnd( tree_task ); +} + +static void b2AddNonTouchingContact( b2World* world, b2Contact* contact, b2ContactSim* contactSim ) +{ + B2_ASSERT( contact->setIndex == b2_awakeSet ); + b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet ); + contact->colorIndex = B2_NULL_INDEX; + contact->localIndex = set->contactSims.count; + + b2ContactSim* newContactSim = b2ContactSimArray_Add( &set->contactSims ); + memcpy( newContactSim, contactSim, sizeof( b2ContactSim ) ); +} + +static void b2RemoveNonTouchingContact( b2World* world, int setIndex, int localIndex ) +{ + b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, setIndex ); + int movedIndex = b2ContactSimArray_RemoveSwap( &set->contactSims, localIndex ); + if ( movedIndex != B2_NULL_INDEX ) + { + b2ContactSim* movedContactSim = set->contactSims.data + localIndex; + b2Contact* movedContact = b2ContactArray_Get(&world->contacts, movedContactSim->contactId); + B2_ASSERT( movedContact->setIndex == setIndex ); + B2_ASSERT( movedContact->localIndex == movedIndex ); + B2_ASSERT( movedContact->colorIndex == B2_NULL_INDEX ); + movedContact->localIndex = localIndex; + } +} + +// Narrow-phase collision +static void b2Collide( b2StepContext* context ) +{ + b2World* world = context->world; + + B2_ASSERT( world->workerCount > 0 ); + + b2TracyCZoneNC( collide, "Collide", b2_colorDarkOrchid, true ); + + // Tasks that can be done in parallel with the narrow-phase + // - rebuild the collision tree for dynamic and kinematic bodies to keep their query performance good + world->userTreeTask = world->enqueueTaskFcn( &b2UpdateTreesTask, 1, 1, world, world->userTaskContext ); + world->taskCount += 1; + world->activeTaskCount += world->userTreeTask == NULL ? 0 : 1; + + // gather contacts into a single array for easier parallel-for + int contactCount = 0; + b2GraphColor* graphColors = world->constraintGraph.colors; + for ( int i = 0; i < b2_graphColorCount; ++i ) + { + contactCount += graphColors[i].contactSims.count; + } + + int nonTouchingCount = world->solverSets.data[b2_awakeSet].contactSims.count; + contactCount += nonTouchingCount; + + if ( contactCount == 0 ) + { + b2TracyCZoneEnd( collide ); + return; + } + + b2ContactSim** contactSims = b2AllocateStackItem( &world->stackAllocator, contactCount * sizeof( b2ContactSim ), "contacts" ); + + int contactIndex = 0; + for ( int i = 0; i < b2_graphColorCount; ++i ) + { + b2GraphColor* color = graphColors + i; + int count = color->contactSims.count; + b2ContactSim* base = color->contactSims.data; + for ( int j = 0; j < count; ++j ) + { + contactSims[contactIndex] = base + j; + contactIndex += 1; + } + } + + { + b2ContactSim* base = world->solverSets.data[b2_awakeSet].contactSims.data; + for ( int i = 0; i < nonTouchingCount; ++i ) + { + contactSims[contactIndex] = base + i; + contactIndex += 1; + } + } + + B2_ASSERT( contactIndex == contactCount ); + + context->contacts = contactSims; + + // Contact bit set on ids because contact pointers are unstable as they move between touching and not touching. + int contactIdCapacity = b2GetIdCapacity( &world->contactIdPool ); + for ( int i = 0; i < world->workerCount; ++i ) + { + b2SetBitCountAndClear( &world->taskContexts.data[i].contactStateBitSet, contactIdCapacity ); + } + + // Task should take at least 40us on a 4GHz CPU (10K cycles) + int minRange = 64; + void* userCollideTask = world->enqueueTaskFcn( &b2CollideTask, contactCount, minRange, context, world->userTaskContext ); + world->taskCount += 1; + if ( userCollideTask != NULL ) + { + world->finishTaskFcn( userCollideTask, world->userTaskContext ); + } + + b2FreeStackItem( &world->stackAllocator, contactSims ); + context->contacts = NULL; + contactSims = NULL; + + // Serially update contact state + b2TracyCZoneNC( contact_state, "Contact State", b2_colorCoral, true ); + + // Bitwise OR all contact bits + b2BitSet* bitSet = &world->taskContexts.data[0].contactStateBitSet; + for ( int i = 1; i < world->workerCount; ++i ) + { + b2InPlaceUnion( bitSet, &world->taskContexts.data[i].contactStateBitSet ); + } + + b2SolverSet* awakeSet = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet ); + + const b2Shape* shapes = world->shapes.data; + int16_t worldId = world->worldId; + + // Process contact state changes. Iterate over set bits + for ( uint32_t k = 0; k < bitSet->blockCount; ++k ) + { + uint64_t bits = bitSet->bits[k]; + while ( bits != 0 ) + { + uint32_t ctz = b2CTZ64( bits ); + int contactId = (int)( 64 * k + ctz ); + + b2Contact* contact = b2ContactArray_Get(&world->contacts, contactId); + B2_ASSERT( contact->setIndex == b2_awakeSet ); + + int colorIndex = contact->colorIndex; + int localIndex = contact->localIndex; + + b2ContactSim* contactSim = NULL; + if ( colorIndex != B2_NULL_INDEX ) + { + // contact lives in constraint graph + B2_ASSERT( 0 <= colorIndex && colorIndex < b2_graphColorCount ); + b2GraphColor* color = graphColors + colorIndex; + contactSim = b2ContactSimArray_Get( &color->contactSims, localIndex ); + } + else + { + contactSim = b2ContactSimArray_Get( &awakeSet->contactSims, localIndex ); + } + + const b2Shape* shapeA = shapes + contact->shapeIdA; + const b2Shape* shapeB = shapes + contact->shapeIdB; + b2ShapeId shapeIdA = { shapeA->id + 1, worldId, shapeA->revision }; + b2ShapeId shapeIdB = { shapeB->id + 1, worldId, shapeB->revision }; + uint32_t flags = contact->flags; + uint32_t simFlags = contactSim->simFlags; + + if ( simFlags & b2_simDisjoint ) + { + // Was touching? + if ( ( flags & b2_contactTouchingFlag ) != 0 && ( flags & b2_contactEnableContactEvents ) != 0 ) + { + b2ContactEndTouchEvent event = { shapeIdA, shapeIdB }; + b2ContactEndTouchEventArray_Push( &world->contactEndEvents, event ); + } + + // Bounding boxes no longer overlap + contact->flags &= ~b2_contactTouchingFlag; + b2DestroyContact( world, contact, false ); + contact = NULL; + contactSim = NULL; + } + else if ( simFlags & b2_simStartedTouching ) + { + B2_ASSERT( contact->islandId == B2_NULL_INDEX ); + if ( ( flags & b2_contactSensorFlag ) != 0 ) + { + // Contact is a sensor + if ( ( flags & b2_contactEnableSensorEvents ) != 0 ) + { + if ( shapeA->isSensor ) + { + b2SensorBeginTouchEvent event = { shapeIdA, shapeIdB }; + b2SensorBeginTouchEventArray_Push( &world->sensorBeginEvents, event ); + } + + if ( shapeB->isSensor ) + { + b2SensorBeginTouchEvent event = { shapeIdB, shapeIdA }; + b2SensorBeginTouchEventArray_Push( &world->sensorBeginEvents, event ); + } + } + + contactSim->simFlags &= ~b2_simStartedTouching; + contact->flags |= b2_contactSensorTouchingFlag; + } + else + { + // Contact is solid + if ( flags & b2_contactEnableContactEvents ) + { + b2ContactBeginTouchEvent event = { shapeIdA, shapeIdB }; + b2ContactBeginTouchEventArray_Push( &world->contactBeginEvents, event ); + } + + B2_ASSERT( contactSim->manifold.pointCount > 0 ); + B2_ASSERT( contact->setIndex == b2_awakeSet ); + + // Link first because this wakes colliding bodies and ensures the body sims + // are in the correct place. + contact->flags |= b2_contactTouchingFlag; + b2LinkContact( world, contact ); + + // Make sure these didn't change + B2_ASSERT( contact->colorIndex == B2_NULL_INDEX ); + B2_ASSERT( contact->localIndex == localIndex ); + + // Contact sim pointer may have become orphaned due to awake set growth, + // so I just need to refresh it. + contactSim = b2ContactSimArray_Get( &awakeSet->contactSims, localIndex ); + + contactSim->simFlags &= ~b2_simStartedTouching; + + b2AddContactToGraph( world, contactSim, contact ); + b2RemoveNonTouchingContact( world, b2_awakeSet, localIndex ); + contactSim = NULL; + } + } + else if ( simFlags & b2_simStoppedTouching ) + { + contactSim->simFlags &= ~b2_simStoppedTouching; + + if ( ( flags & b2_contactSensorFlag ) != 0 ) + { + // Contact is a sensor + contact->flags &= ~b2_contactSensorTouchingFlag; + + if ( ( flags & b2_contactEnableSensorEvents ) != 0 ) + { + if ( shapeA->isSensor ) + { + b2SensorEndTouchEvent event = { shapeIdA, shapeIdB }; + b2SensorEndTouchEventArray_Push( &world->sensorEndEvents, event ); + } + + if ( shapeB->isSensor ) + { + b2SensorEndTouchEvent event = { shapeIdB, shapeIdA }; + b2SensorEndTouchEventArray_Push( &world->sensorEndEvents, event ); + } + } + } + else + { + // Contact is solid + contact->flags &= ~b2_contactTouchingFlag; + + if ( contact->flags & b2_contactEnableContactEvents ) + { + b2ContactEndTouchEvent event = { shapeIdA, shapeIdB }; + b2ContactEndTouchEventArray_Push( &world->contactEndEvents, event ); + } + + B2_ASSERT( contactSim->manifold.pointCount == 0 ); + + b2UnlinkContact( world, contact ); + int bodyIdA = contact->edges[0].bodyId; + int bodyIdB = contact->edges[1].bodyId; + + b2AddNonTouchingContact( world, contact, contactSim ); + b2RemoveContactFromGraph( world, bodyIdA, bodyIdB, colorIndex, localIndex ); + contact = NULL; + contactSim = NULL; + } + } + + // Clear the smallest set bit + bits = bits & ( bits - 1 ); + } + } + + b2ValidateSolverSets( world ); + b2ValidateContacts( world ); + + b2TracyCZoneEnd( contact_state ); + b2TracyCZoneEnd( collide ); +} + +void b2World_Step( b2WorldId worldId, float timeStep, int subStepCount ) +{ + b2World* world = b2GetWorldFromId( worldId ); + B2_ASSERT( world->locked == false ); + if ( world->locked ) + { + return; + } + + // Prepare to capture events + // Ensure user does not access stale data if there is an early return + b2BodyMoveEventArray_Clear( &world->bodyMoveEvents ); + b2SensorBeginTouchEventArray_Clear( &world->sensorBeginEvents ); + b2SensorEndTouchEventArray_Clear( &world->sensorEndEvents ); + b2ContactBeginTouchEventArray_Clear( &world->contactBeginEvents ); + b2ContactEndTouchEventArray_Clear( &world->contactEndEvents ); + b2ContactHitEventArray_Clear( &world->contactHitEvents ); + + world->profile = ( b2Profile ){ 0 }; + + if ( timeStep == 0.0f ) + { + // todo would be useful to still process collision while paused + return; + } + + b2TracyCZoneNC( world_step, "Step", b2_colorChartreuse, true ); + + world->locked = true; + world->activeTaskCount = 0; + world->taskCount = 0; + + b2Timer stepTimer = b2CreateTimer(); + + // Update collision pairs and create contacts + { + b2Timer timer = b2CreateTimer(); + b2UpdateBroadPhasePairs( world ); + world->profile.pairs = b2GetMilliseconds( &timer ); + } + + b2StepContext context = { 0 }; + context.world = world; + context.dt = timeStep; + context.subStepCount = b2MaxInt( 1, subStepCount ); + + if ( timeStep > 0.0f ) + { + context.inv_dt = 1.0f / timeStep; + context.h = timeStep / context.subStepCount; + context.inv_h = context.subStepCount * context.inv_dt; + } + else + { + context.inv_dt = 0.0f; + context.h = 0.0f; + context.inv_h = 0.0f; + } + + world->inv_h = context.inv_h; + + // Hertz values get reduced for large time steps + float contactHertz = b2MinFloat( world->contactHertz, 0.25f * context.inv_h ); + float jointHertz = b2MinFloat( world->jointHertz, 0.125f * context.inv_h ); + + context.contactSoftness = b2MakeSoft( contactHertz, world->contactDampingRatio, context.h ); + context.staticSoftness = b2MakeSoft( 2.0f * contactHertz, world->contactDampingRatio, context.h ); + context.jointSoftness = b2MakeSoft( jointHertz, world->jointDampingRatio, context.h ); + + context.restitutionThreshold = world->restitutionThreshold; + context.maxLinearVelocity = world->maxLinearVelocity; + context.enableWarmStarting = world->enableWarmStarting; + + // Update contacts + { + b2Timer timer = b2CreateTimer(); + b2Collide( &context ); + world->profile.collide = b2GetMilliseconds( &timer ); + } + + // Integrate velocities, solve velocity constraints, and integrate positions. + if ( context.dt > 0.0f ) + { + b2Timer timer = b2CreateTimer(); + b2Solve( world, &context ); + world->profile.solve = b2GetMilliseconds( &timer ); + } + + world->locked = false; + + world->profile.step = b2GetMilliseconds( &stepTimer ); + + B2_ASSERT( b2GetStackAllocation( &world->stackAllocator ) == 0 ); + + // Ensure stack is large enough + b2GrowStack( &world->stackAllocator ); + + // Make sure all tasks that were started were also finished + B2_ASSERT( world->activeTaskCount == 0 ); + + b2TracyCZoneEnd( world_step ); +} + +static void b2DrawShape( b2DebugDraw* draw, b2Shape* shape, b2Transform xf, b2HexColor color ) +{ + switch ( shape->type ) + { + case b2_capsuleShape: + { + b2Capsule* capsule = &shape->capsule; + b2Vec2 p1 = b2TransformPoint( xf, capsule->center1 ); + b2Vec2 p2 = b2TransformPoint( xf, capsule->center2 ); + draw->DrawSolidCapsule( p1, p2, capsule->radius, color, draw->context ); + } + break; + + case b2_circleShape: + { + b2Circle* circle = &shape->circle; + xf.p = b2TransformPoint( xf, circle->center ); + draw->DrawSolidCircle( xf, circle->radius, color, draw->context ); + } + break; + + case b2_polygonShape: + { + b2Polygon* poly = &shape->polygon; + draw->DrawSolidPolygon( xf, poly->vertices, poly->count, poly->radius, color, draw->context ); + } + break; + + case b2_segmentShape: + { + b2Segment* segment = &shape->segment; + b2Vec2 p1 = b2TransformPoint( xf, segment->point1 ); + b2Vec2 p2 = b2TransformPoint( xf, segment->point2 ); + draw->DrawSegment( p1, p2, color, draw->context ); + } + break; + + case b2_chainSegmentShape: + { + b2Segment* segment = &shape->chainSegment.segment; + b2Vec2 p1 = b2TransformPoint( xf, segment->point1 ); + b2Vec2 p2 = b2TransformPoint( xf, segment->point2 ); + draw->DrawSegment( p1, p2, color, draw->context ); + draw->DrawPoint( p2, 4.0f, color, draw->context ); + draw->DrawSegment( p1, b2Lerp( p1, p2, 0.1f ), b2_colorPaleGreen, draw->context ); + } + break; + + default: + break; + } +} + +struct DrawContext +{ + b2World* world; + b2DebugDraw* draw; +}; + +static bool DrawQueryCallback( int proxyId, int shapeId, void* context ) +{ + B2_MAYBE_UNUSED( proxyId ); + + struct DrawContext* drawContext = context; + b2World* world = drawContext->world; + b2DebugDraw* draw = drawContext->draw; + + b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId ); + B2_ASSERT( shape->id == shapeId ); + + b2SetBit( &world->debugBodySet, shape->bodyId ); + + if ( draw->drawShapes ) + { + b2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId ); + b2BodySim* bodySim = b2GetBodySim( world, body ); + + b2HexColor color; + + if ( shape->customColor != 0 ) + { + color = shape->customColor; + } + else if ( body->type == b2_dynamicBody && bodySim->mass == 0.0f ) + { + // Bad body + color = b2_colorRed; + } + else if ( body->setIndex == b2_disabledSet ) + { + color = b2_colorSlateGray; + } + else if ( shape->isSensor ) + { + color = b2_colorWheat; + } + else if ( bodySim->isBullet && body->setIndex == b2_awakeSet ) + { + color = b2_colorTurquoise; + } + else if ( body->isSpeedCapped ) + { + color = b2_colorYellow; + } + else if ( bodySim->isFast ) + { + color = b2_colorSalmon; + } + else if ( body->type == b2_staticBody ) + { + color = b2_colorPaleGreen; + } + else if ( body->type == b2_kinematicBody ) + { + color = b2_colorRoyalBlue; + } + else if ( body->setIndex == b2_awakeSet ) + { + color = b2_colorPink; + } + else + { + color = b2_colorGray; + } + + b2DrawShape( draw, shape, bodySim->transform, color ); + } + + if ( draw->drawAABBs ) + { + b2AABB aabb = shape->fatAABB; + + b2Vec2 vs[4] = { { aabb.lowerBound.x, aabb.lowerBound.y }, + { aabb.upperBound.x, aabb.lowerBound.y }, + { aabb.upperBound.x, aabb.upperBound.y }, + { aabb.lowerBound.x, aabb.upperBound.y } }; + + draw->DrawPolygon( vs, 4, b2_colorGold, draw->context ); + } + + return true; +} + +// todo this has varying order for moving shapes, causing flicker when overlapping shapes are moving +// solution: display order by shape id modulus 3, keep 3 buckets in GLSolid* and flush in 3 passes. +static void b2DrawWithBounds( b2World* world, b2DebugDraw* draw ) +{ + B2_ASSERT( b2AABB_IsValid( draw->drawingBounds ) ); + + const float k_impulseScale = 1.0f; + const float k_axisScale = 0.3f; + b2HexColor speculativeColor = b2_colorGray3; + b2HexColor addColor = b2_colorGreen; + b2HexColor persistColor = b2_colorBlue; + b2HexColor normalColor = b2_colorGray9; + b2HexColor impulseColor = b2_colorMagenta; + b2HexColor frictionColor = b2_colorYellow; + + b2HexColor graphColors[b2_graphColorCount] = { b2_colorRed, b2_colorOrange, b2_colorYellow, b2_colorGreen, + b2_colorCyan, b2_colorBlue, b2_colorViolet, b2_colorPink, + b2_colorChocolate, b2_colorGoldenrod, b2_colorCoral, b2_colorBlack }; + + int bodyCapacity = b2GetIdCapacity( &world->bodyIdPool ); + b2SetBitCountAndClear( &world->debugBodySet, bodyCapacity ); + + int jointCapacity = b2GetIdCapacity( &world->jointIdPool ); + b2SetBitCountAndClear( &world->debugJointSet, jointCapacity ); + + int contactCapacity = b2GetIdCapacity( &world->contactIdPool ); + b2SetBitCountAndClear( &world->debugContactSet, contactCapacity ); + + struct DrawContext drawContext = { world, draw }; + + for ( int i = 0; i < b2_bodyTypeCount; ++i ) + { + b2DynamicTree_Query( world->broadPhase.trees + i, draw->drawingBounds, b2_defaultMaskBits, DrawQueryCallback, + &drawContext ); + } + + uint32_t wordCount = world->debugBodySet.blockCount; + uint64_t* bits = world->debugBodySet.bits; + for ( uint32_t k = 0; k < wordCount; ++k ) + { + uint64_t word = bits[k]; + while ( word != 0 ) + { + uint32_t ctz = b2CTZ64( word ); + uint32_t bodyId = 64 * k + ctz; + + b2Body* body = b2BodyArray_Get( &world->bodies, bodyId ); + + if ( draw->drawMass && body->type == b2_dynamicBody ) + { + b2Vec2 offset = { 0.1f, 0.1f }; + b2BodySim* bodySim = b2GetBodySim( world, body ); + + b2Transform transform = { bodySim->center, bodySim->transform.q }; + draw->DrawTransform( transform, draw->context ); + + b2Vec2 p = b2TransformPoint( transform, offset ); + + char buffer[32]; + snprintf( buffer, 32, " %.2f", bodySim->mass ); + draw->DrawString( p, buffer, draw->context ); + } + + if ( draw->drawJoints ) + { + int jointKey = body->headJointKey; + while ( jointKey != B2_NULL_INDEX ) + { + int jointId = jointKey >> 1; + int edgeIndex = jointKey & 1; + b2Joint* joint = b2JointArray_Get( &world->joints, jointId ); + + // avoid double draw + if ( b2GetBit( &world->debugJointSet, jointId ) == false ) + { + b2DrawJoint( draw, world, joint ); + b2SetBit( &world->debugJointSet, jointId ); + } + else + { + // todo testing + edgeIndex += 0; + } + + jointKey = joint->edges[edgeIndex].nextKey; + } + } + + const float linearSlop = b2_linearSlop; + if ( draw->drawContacts && body->type == b2_dynamicBody && body->setIndex == b2_awakeSet ) + { + int contactKey = body->headContactKey; + while ( contactKey != B2_NULL_INDEX ) + { + int contactId = contactKey >> 1; + int edgeIndex = contactKey & 1; + b2Contact* contact = b2ContactArray_Get(&world->contacts, contactId); + contactKey = contact->edges[edgeIndex].nextKey; + + if ( contact->setIndex != b2_awakeSet || contact->colorIndex == B2_NULL_INDEX ) + { + continue; + } + + // avoid double draw + if ( b2GetBit( &world->debugContactSet, contactId ) == false ) + { + B2_ASSERT( 0 <= contact->colorIndex && contact->colorIndex < b2_graphColorCount ); + + b2GraphColor* gc = world->constraintGraph.colors + contact->colorIndex; + b2ContactSim* contactSim = b2ContactSimArray_Get( &gc->contactSims, contact->localIndex ); + int pointCount = contactSim->manifold.pointCount; + b2Vec2 normal = contactSim->manifold.normal; + char buffer[32]; + + for ( int j = 0; j < pointCount; ++j ) + { + b2ManifoldPoint* point = contactSim->manifold.points + j; + + if ( draw->drawGraphColors ) + { + // graph color + float pointSize = contact->colorIndex == b2_overflowIndex ? 7.5f : 5.0f; + draw->DrawPoint( point->point, pointSize, graphColors[contact->colorIndex], draw->context ); + // g_draw.DrawString(point->position, "%d", point->color); + } + else if ( point->separation > linearSlop ) + { + // Speculative + draw->DrawPoint( point->point, 5.0f, speculativeColor, draw->context ); + } + else if ( point->persisted == false ) + { + // Add + draw->DrawPoint( point->point, 10.0f, addColor, draw->context ); + } + else if ( point->persisted == true ) + { + // Persist + draw->DrawPoint( point->point, 5.0f, persistColor, draw->context ); + } + + if ( draw->drawContactNormals ) + { + b2Vec2 p1 = point->point; + b2Vec2 p2 = b2MulAdd( p1, k_axisScale, normal ); + draw->DrawSegment( p1, p2, normalColor, draw->context ); + } + else if ( draw->drawContactImpulses ) + { + b2Vec2 p1 = point->point; + b2Vec2 p2 = b2MulAdd( p1, k_impulseScale * point->normalImpulse, normal ); + draw->DrawSegment( p1, p2, impulseColor, draw->context ); + snprintf( buffer, B2_ARRAY_COUNT( buffer ), "%.1f", 1000.0f * point->normalImpulse ); + draw->DrawString( p1, buffer, draw->context ); + } + + if ( draw->drawFrictionImpulses ) + { + b2Vec2 tangent = b2RightPerp( normal ); + b2Vec2 p1 = point->point; + b2Vec2 p2 = b2MulAdd( p1, k_impulseScale * point->tangentImpulse, tangent ); + draw->DrawSegment( p1, p2, frictionColor, draw->context ); + snprintf( buffer, B2_ARRAY_COUNT( buffer ), "%.1f", 1000.0f * point->tangentImpulse ); + draw->DrawString( p1, buffer, draw->context ); + } + } + + b2SetBit( &world->debugContactSet, contactId ); + } + else + { + // todo testing + edgeIndex += 0; + } + + contactKey = contact->edges[edgeIndex].nextKey; + } + } + + // Clear the smallest set bit + word = word & ( word - 1 ); + } + } +} + +void b2World_Draw( b2WorldId worldId, b2DebugDraw* draw ) +{ + b2World* world = b2GetWorldFromId( worldId ); + B2_ASSERT( world->locked == false ); + if ( world->locked ) + { + return; + } + + // todo it seems bounds drawing is fast enough for regular usage + if ( draw->useDrawingBounds ) + { + b2DrawWithBounds( world, draw ); + return; + } + + if ( draw->drawShapes ) + { + int setCount = world->solverSets.count; + for ( int setIndex = 0; setIndex < setCount; ++setIndex ) + { + b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, setIndex ); + int bodyCount = set->bodySims.count; + for ( int bodyIndex = 0; bodyIndex < bodyCount; ++bodyIndex ) + { + b2BodySim* bodySim = set->bodySims.data + bodyIndex; + b2Body* body = b2BodyArray_Get( &world->bodies, bodySim->bodyId ); + B2_ASSERT( body->setIndex == setIndex ); + + b2Transform xf = bodySim->transform; + int shapeId = body->headShapeId; + while ( shapeId != B2_NULL_INDEX ) + { + b2Shape* shape = world->shapes.data + shapeId; + b2HexColor color; + + if ( shape->customColor != 0 ) + { + color = shape->customColor; + } + else if ( body->type == b2_dynamicBody && bodySim->mass == 0.0f ) + { + // Bad body + color = b2_colorRed; + } + else if ( body->setIndex == b2_disabledSet ) + { + color = b2_colorSlateGray; + } + else if ( shape->isSensor ) + { + color = b2_colorWheat; + } + else if ( bodySim->isBullet && body->setIndex == b2_awakeSet ) + { + color = b2_colorTurquoise; + } + else if ( body->isSpeedCapped ) + { + color = b2_colorYellow; + } + else if ( bodySim->isFast ) + { + color = b2_colorSalmon; + } + else if ( body->type == b2_staticBody ) + { + color = b2_colorPaleGreen; + } + else if ( body->type == b2_kinematicBody ) + { + color = b2_colorRoyalBlue; + } + else if ( body->setIndex == b2_awakeSet ) + { + color = b2_colorPink; + } + else + { + color = b2_colorGray; + } + + b2DrawShape( draw, shape, xf, color ); + shapeId = shape->nextShapeId; + } + } + } + } + + if ( draw->drawJoints ) + { + int count = world->joints.count; + for ( int i = 0; i < count; ++i ) + { + b2Joint* joint = world->joints.data + i; + if ( joint->setIndex == B2_NULL_INDEX ) + { + continue; + } + + b2DrawJoint( draw, world, joint ); + } + } + + if ( draw->drawAABBs ) + { + b2HexColor color = b2_colorGold; + + int setCount = world->solverSets.count; + for ( int setIndex = 0; setIndex < setCount; ++setIndex ) + { + b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, setIndex ); + int bodyCount = set->bodySims.count; + for ( int bodyIndex = 0; bodyIndex < bodyCount; ++bodyIndex ) + { + b2BodySim* bodySim = set->bodySims.data + bodyIndex; + + char buffer[32]; + snprintf( buffer, 32, "%d", bodySim->bodyId ); + draw->DrawString( bodySim->center, buffer, draw->context ); + + b2Body* body = b2BodyArray_Get( &world->bodies, bodySim->bodyId ); + B2_ASSERT( body->setIndex == setIndex ); + + int shapeId = body->headShapeId; + while ( shapeId != B2_NULL_INDEX ) + { + b2Shape* shape = world->shapes.data + shapeId; + b2AABB aabb = shape->fatAABB; + + b2Vec2 vs[4] = { { aabb.lowerBound.x, aabb.lowerBound.y }, + { aabb.upperBound.x, aabb.lowerBound.y }, + { aabb.upperBound.x, aabb.upperBound.y }, + { aabb.lowerBound.x, aabb.upperBound.y } }; + + draw->DrawPolygon( vs, 4, color, draw->context ); + + shapeId = shape->nextShapeId; + } + } + } + } + + if ( draw->drawMass ) + { + b2Vec2 offset = { 0.1f, 0.1f }; + int setCount = world->solverSets.count; + for ( int setIndex = 0; setIndex < setCount; ++setIndex ) + { + b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, setIndex ); + int bodyCount = set->bodySims.count; + for ( int bodyIndex = 0; bodyIndex < bodyCount; ++bodyIndex ) + { + b2BodySim* bodySim = set->bodySims.data + bodyIndex; + + b2Transform transform = { bodySim->center, bodySim->transform.q }; + draw->DrawTransform( transform, draw->context ); + + b2Vec2 p = b2TransformPoint( transform, offset ); + + char buffer[32]; + snprintf( buffer, 32, " %.2f", bodySim->mass ); + draw->DrawString( p, buffer, draw->context ); + } + } + } + + if ( draw->drawContacts ) + { + const float k_impulseScale = 1.0f; + const float k_axisScale = 0.3f; + const float linearSlop = b2_linearSlop; + + b2HexColor speculativeColor = b2_colorGray3; + b2HexColor addColor = b2_colorGreen; + b2HexColor persistColor = b2_colorBlue; + b2HexColor normalColor = b2_colorGray9; + b2HexColor impulseColor = b2_colorMagenta; + b2HexColor frictionColor = b2_colorYellow; + + b2HexColor colors[b2_graphColorCount] = { b2_colorRed, b2_colorOrange, b2_colorYellow, b2_colorGreen, + b2_colorCyan, b2_colorBlue, b2_colorViolet, b2_colorPink, + b2_colorChocolate, b2_colorGoldenrod, b2_colorCoral, b2_colorBlack }; + + for ( int colorIndex = 0; colorIndex < b2_graphColorCount; ++colorIndex ) + { + b2GraphColor* graphColor = world->constraintGraph.colors + colorIndex; + + int contactCount = graphColor->contactSims.count; + for ( int contactIndex = 0; contactIndex < contactCount; ++contactIndex ) + { + b2ContactSim* contact = graphColor->contactSims.data + contactIndex; + int pointCount = contact->manifold.pointCount; + b2Vec2 normal = contact->manifold.normal; + char buffer[32]; + + for ( int j = 0; j < pointCount; ++j ) + { + b2ManifoldPoint* point = contact->manifold.points + j; + + if ( draw->drawGraphColors && 0 <= colorIndex && colorIndex <= b2_graphColorCount ) + { + // graph color + float pointSize = colorIndex == b2_overflowIndex ? 7.5f : 5.0f; + draw->DrawPoint( point->point, pointSize, colors[colorIndex], draw->context ); + // g_draw.DrawString(point->position, "%d", point->color); + } + else if ( point->separation > linearSlop ) + { + // Speculative + draw->DrawPoint( point->point, 5.0f, speculativeColor, draw->context ); + } + else if ( point->persisted == false ) + { + // Add + draw->DrawPoint( point->point, 10.0f, addColor, draw->context ); + } + else if ( point->persisted == true ) + { + // Persist + draw->DrawPoint( point->point, 5.0f, persistColor, draw->context ); + } + + if ( draw->drawContactNormals ) + { + b2Vec2 p1 = point->point; + b2Vec2 p2 = b2MulAdd( p1, k_axisScale, normal ); + draw->DrawSegment( p1, p2, normalColor, draw->context ); + } + else if ( draw->drawContactImpulses ) + { + b2Vec2 p1 = point->point; + b2Vec2 p2 = b2MulAdd( p1, k_impulseScale * point->normalImpulse, normal ); + draw->DrawSegment( p1, p2, impulseColor, draw->context ); + snprintf( buffer, B2_ARRAY_COUNT( buffer ), "%.2f", 1000.0f * point->normalImpulse ); + draw->DrawString( p1, buffer, draw->context ); + } + + if ( draw->drawFrictionImpulses ) + { + b2Vec2 tangent = b2RightPerp( normal ); + b2Vec2 p1 = point->point; + b2Vec2 p2 = b2MulAdd( p1, k_impulseScale * point->tangentImpulse, tangent ); + draw->DrawSegment( p1, p2, frictionColor, draw->context ); + snprintf( buffer, B2_ARRAY_COUNT( buffer ), "%.2f", point->normalImpulse ); + draw->DrawString( p1, buffer, draw->context ); + } + } + } + } + } +} + +b2BodyEvents b2World_GetBodyEvents( b2WorldId worldId ) +{ + b2World* world = b2GetWorldFromId( worldId ); + B2_ASSERT( world->locked == false ); + if ( world->locked ) + { + return ( b2BodyEvents ){ 0 }; + } + + int count = world->bodyMoveEvents.count; + b2BodyEvents events = { world->bodyMoveEvents.data, count }; + return events; +} + +b2SensorEvents b2World_GetSensorEvents( b2WorldId worldId ) +{ + b2World* world = b2GetWorldFromId( worldId ); + B2_ASSERT( world->locked == false ); + if ( world->locked ) + { + return ( b2SensorEvents ){ 0 }; + } + + int beginCount = world->sensorBeginEvents.count; + int endCount = world->sensorEndEvents.count; + + b2SensorEvents events = { world->sensorBeginEvents.data, world->sensorEndEvents.data, beginCount, endCount }; + return events; +} + +b2ContactEvents b2World_GetContactEvents( b2WorldId worldId ) +{ + b2World* world = b2GetWorldFromId( worldId ); + B2_ASSERT( world->locked == false ); + if ( world->locked ) + { + return ( b2ContactEvents ){ 0 }; + } + + int beginCount = world->contactBeginEvents.count; + int endCount = world->contactEndEvents.count; + int hitCount = world->contactHitEvents.count; + + b2ContactEvents events = { + world->contactBeginEvents.data, world->contactEndEvents.data, world->contactHitEvents.data, beginCount, endCount, hitCount, + }; + + return events; +} + +bool b2World_IsValid( b2WorldId id ) +{ + if ( id.index1 < 1 || b2_maxWorlds < id.index1 ) + { + return false; + } + + b2World* world = b2_worlds + ( id.index1 - 1 ); + + if ( world->worldId != id.index1 - 1 ) + { + // world is not allocated + return false; + } + + return id.revision == world->revision; +} + +bool b2Body_IsValid( b2BodyId id ) +{ + if ( id.world0 < 0 || b2_maxWorlds <= id.world0 ) + { + // invalid world + return false; + } + + b2World* world = b2_worlds + id.world0; + if ( world->worldId != id.world0 ) + { + // world is free + return false; + } + + if ( id.index1 < 1 || world->bodies.count < id.index1 ) + { + // invalid index + return false; + } + + b2Body* body = world->bodies.data + ( id.index1 - 1 ); + if ( body->setIndex == B2_NULL_INDEX ) + { + // this was freed + return false; + } + + B2_ASSERT( body->localIndex != B2_NULL_INDEX ); + + if ( body->revision != id.revision ) + { + // this id is orphaned + return false; + } + + return true; +} + +bool b2Shape_IsValid( b2ShapeId id ) +{ + if ( b2_maxWorlds <= id.world0 ) + { + return false; + } + + b2World* world = b2_worlds + id.world0; + if ( world->worldId != id.world0 ) + { + // world is free + return false; + } + + int shapeId = id.index1 - 1; + if ( shapeId < 0 || world->shapes.count <= shapeId ) + { + return false; + } + + b2Shape* shape = world->shapes.data + shapeId; + if ( shape->id == B2_NULL_INDEX ) + { + // shape is free + return false; + } + + B2_ASSERT( shape->id == shapeId ); + + return id.revision == shape->revision; +} + +bool b2Chain_IsValid( b2ChainId id ) +{ + if ( id.world0 < 0 || b2_maxWorlds <= id.world0 ) + { + return false; + } + + b2World* world = b2_worlds + id.world0; + if ( world->worldId != id.world0 ) + { + // world is free + return false; + } + + int chainId = id.index1 - 1; + if ( chainId < 0 || world->chainShapes.count <= chainId ) + { + return false; + } + + b2ChainShape* chain = world->chainShapes.data + chainId; + if ( chain->id == B2_NULL_INDEX ) + { + // chain is free + return false; + } + + B2_ASSERT( chain->id == chainId ); + + return id.revision == chain->revision; +} + +bool b2Joint_IsValid( b2JointId id ) +{ + if ( id.world0 < 0 || b2_maxWorlds <= id.world0 ) + { + return false; + } + + b2World* world = b2_worlds + id.world0; + if ( world->worldId != id.world0 ) + { + // world is free + return false; + } + + int jointId = id.index1 - 1; + if ( jointId < 0 || world->joints.count <= jointId ) + { + return false; + } + + b2Joint* joint = world->joints.data + jointId; + if ( joint->jointId == B2_NULL_INDEX ) + { + // joint is free + return false; + } + + B2_ASSERT( joint->jointId == jointId ); + + return id.revision == joint->revision; +} + +void b2World_EnableSleeping( b2WorldId worldId, bool flag ) +{ + b2World* world = b2GetWorldFromId( worldId ); + B2_ASSERT( world->locked == false ); + if ( world->locked ) + { + return; + } + + if ( flag == world->enableSleep ) + { + return; + } + + world->enableSleep = flag; + + if ( flag == false ) + { + int setCount = world->solverSets.count; + for ( int i = b2_firstSleepingSet; i < setCount; ++i ) + { + b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, i ); + if ( set->bodySims.count > 0 ) + { + b2WakeSolverSet( world, i ); + } + } + } +} + +bool b2World_IsSleepingEnabled( b2WorldId worldId ) +{ + b2World* world = b2GetWorldFromId( worldId ); + return world->enableSleep; +} + +void b2World_EnableWarmStarting( b2WorldId worldId, bool flag ) +{ + b2World* world = b2GetWorldFromId( worldId ); + B2_ASSERT( world->locked == false ); + if ( world->locked ) + { + return; + } + + world->enableWarmStarting = flag; +} + +bool b2World_IsWarmStartingEnabled( b2WorldId worldId ) +{ + b2World* world = b2GetWorldFromId( worldId ); + return world->enableWarmStarting; +} + +void b2World_EnableContinuous( b2WorldId worldId, bool flag ) +{ + b2World* world = b2GetWorldFromId( worldId ); + B2_ASSERT( world->locked == false ); + if ( world->locked ) + { + return; + } + + world->enableContinuous = flag; +} + +bool b2World_IsContinuousEnabled( b2WorldId worldId ) +{ + b2World* world = b2GetWorldFromId( worldId ); + return world->enableContinuous; +} + +void b2World_SetRestitutionThreshold( b2WorldId worldId, float value ) +{ + b2World* world = b2GetWorldFromId( worldId ); + B2_ASSERT( world->locked == false ); + if ( world->locked ) + { + return; + } + + world->restitutionThreshold = b2ClampFloat( value, 0.0f, FLT_MAX ); +} + +float b2World_GetRestitutionThreshold( b2WorldId worldId ) +{ + b2World* world = b2GetWorldFromId( worldId ); + return world->restitutionThreshold; +} + +void b2World_SetHitEventThreshold( b2WorldId worldId, float value ) +{ + b2World* world = b2GetWorldFromId( worldId ); + B2_ASSERT( world->locked == false ); + if ( world->locked ) + { + return; + } + + world->hitEventThreshold = b2ClampFloat( value, 0.0f, FLT_MAX ); +} + +float b2World_GetHitEventThreshold( b2WorldId worldId ) +{ + b2World* world = b2GetWorldFromId( worldId ); + return world->hitEventThreshold; +} + +void b2World_SetContactTuning( b2WorldId worldId, float hertz, float dampingRatio, float pushOut ) +{ + b2World* world = b2GetWorldFromId( worldId ); + B2_ASSERT( world->locked == false ); + if ( world->locked ) + { + return; + } + + world->contactHertz = b2ClampFloat( hertz, 0.0f, FLT_MAX ); + world->contactDampingRatio = b2ClampFloat( dampingRatio, 0.0f, FLT_MAX ); + world->contactPushoutVelocity = b2ClampFloat( pushOut, 0.0f, FLT_MAX ); +} + +b2Profile b2World_GetProfile( b2WorldId worldId ) +{ + b2World* world = b2GetWorldFromId( worldId ); + return world->profile; +} + +b2Counters b2World_GetCounters( b2WorldId worldId ) +{ + b2World* world = b2GetWorldFromId( worldId ); + b2Counters s = { 0 }; + s.bodyCount = b2GetIdCount( &world->bodyIdPool ); + s.shapeCount = b2GetIdCount( &world->shapeIdPool ); + s.contactCount = b2GetIdCount( &world->contactIdPool ); + s.jointCount = b2GetIdCount( &world->jointIdPool ); + s.islandCount = b2GetIdCount( &world->islandIdPool ); + + b2DynamicTree* staticTree = world->broadPhase.trees + b2_staticBody; + s.staticTreeHeight = b2DynamicTree_GetHeight( staticTree ); + + b2DynamicTree* dynamicTree = world->broadPhase.trees + b2_dynamicBody; + b2DynamicTree* kinematicTree = world->broadPhase.trees + b2_kinematicBody; + s.treeHeight = b2MaxInt( b2DynamicTree_GetHeight( dynamicTree ), b2DynamicTree_GetHeight( kinematicTree ) ); + + s.stackUsed = b2GetMaxStackAllocation( &world->stackAllocator ); + s.byteCount = b2GetByteCount(); + s.taskCount = world->taskCount; + + for ( int i = 0; i < b2_graphColorCount; ++i ) + { + s.colorCounts[i] = world->constraintGraph.colors[i].contactSims.count + world->constraintGraph.colors[i].jointSims.count; + } + return s; +} + +void b2World_DumpMemoryStats( b2WorldId worldId ) +{ + FILE* file = fopen( "box2d_memory.txt", "w" ); + if ( file == NULL ) + { + return; + } + + b2World* world = b2GetWorldFromId( worldId ); + + // id pools + fprintf( file, "id pools\n" ); + fprintf( file, "body ids: %d\n", b2GetIdBytes( &world->bodyIdPool ) ); + fprintf( file, "solver set ids: %d\n", b2GetIdBytes( &world->solverSetIdPool ) ); + fprintf( file, "joint ids: %d\n", b2GetIdBytes( &world->jointIdPool ) ); + fprintf( file, "contact ids: %d\n", b2GetIdBytes( &world->contactIdPool ) ); + fprintf( file, "island ids: %d\n", b2GetIdBytes( &world->islandIdPool ) ); + fprintf( file, "shape ids: %d\n", b2GetIdBytes( &world->shapeIdPool ) ); + fprintf( file, "chain ids: %d\n", b2GetIdBytes( &world->chainIdPool ) ); + fprintf( file, "\n" ); + + // world arrays + fprintf( file, "world arrays\n" ); + fprintf( file, "bodies: %d\n", b2BodyArray_ByteCount( &world->bodies ) ); + fprintf( file, "solver sets: %d\n", b2SolverSetArray_ByteCount( &world->solverSets ) ); + fprintf( file, "joints: %d\n", b2JointArray_ByteCount( &world->joints ) ); + fprintf( file, "contacts: %d\n", b2ContactArray_ByteCount( &world->contacts ) ); + fprintf( file, "islands: %d\n", b2IslandArray_ByteCount( &world->islands ) ); + fprintf( file, "shapes: %d\n", b2ShapeArray_ByteCount( &world->shapes ) ); + fprintf( file, "chains: %d\n", b2ChainShapeArray_ByteCount( &world->chainShapes ) ); + fprintf( file, "\n" ); + + // broad-phase + fprintf( file, "broad-phase\n" ); + fprintf( file, "static tree: %d\n", b2DynamicTree_GetByteCount( world->broadPhase.trees + b2_staticBody ) ); + fprintf( file, "kinematic tree: %d\n", b2DynamicTree_GetByteCount( world->broadPhase.trees + b2_kinematicBody ) ); + fprintf( file, "dynamic tree: %d\n", b2DynamicTree_GetByteCount( world->broadPhase.trees + b2_dynamicBody ) ); + b2HashSet* moveSet = &world->broadPhase.moveSet; + fprintf( file, "moveSet: %d (%d, %d)\n", b2GetHashSetBytes( moveSet ), moveSet->count, moveSet->capacity ); + fprintf( file, "moveArray: %d\n", b2IntArray_ByteCount( &world->broadPhase.moveArray ) ); + b2HashSet* pairSet = &world->broadPhase.pairSet; + fprintf( file, "pairSet: %d (%d, %d)\n", b2GetHashSetBytes( pairSet ), pairSet->count, pairSet->capacity ); + fprintf( file, "\n" ); + + // solver sets + int bodySimCapacity = 0; + int bodyStateCapacity = 0; + int jointSimCapacity = 0; + int contactSimCapacity = 0; + int islandSimCapacity = 0; + int solverSetCapacity = world->solverSets.count; + for ( int i = 0; i < solverSetCapacity; ++i ) + { + b2SolverSet* set = world->solverSets.data + i; + if ( set->setIndex == B2_NULL_INDEX ) + { + continue; + } + + bodySimCapacity += set->bodySims.capacity; + bodyStateCapacity += set->bodyStates.capacity; + jointSimCapacity += set->jointSims.capacity; + contactSimCapacity += set->contactSims.capacity; + islandSimCapacity += set->islandSims.capacity; + } + + fprintf( file, "solver sets\n" ); + fprintf( file, "body sim: %d\n", bodySimCapacity * (int)sizeof( b2BodySim ) ); + fprintf( file, "body state: %d\n", bodyStateCapacity * (int)sizeof( b2BodyState ) ); + fprintf( file, "joint sim: %d\n", jointSimCapacity * (int)sizeof( b2JointSim ) ); + fprintf( file, "contact sim: %d\n", contactSimCapacity * (int)sizeof( b2ContactSim ) ); + fprintf( file, "island sim: %d\n", islandSimCapacity * (int)sizeof( islandSimCapacity ) ); + fprintf( file, "\n" ); + + // constraint graph + int bodyBitSetBytes = 0; + contactSimCapacity = 0; + jointSimCapacity = 0; + for ( int i = 0; i < b2_graphColorCount; ++i ) + { + b2GraphColor* c = world->constraintGraph.colors + i; + bodyBitSetBytes += b2GetBitSetBytes( &c->bodySet ); + contactSimCapacity += c->contactSims.capacity; + jointSimCapacity += c->jointSims.capacity; + } + + fprintf( file, "constraint graph\n" ); + fprintf( file, "body bit sets: %d\n", bodyBitSetBytes ); + fprintf( file, "joint sim: %d\n", jointSimCapacity * (int)sizeof( b2JointSim ) ); + fprintf( file, "contact sim: %d\n", contactSimCapacity * (int)sizeof( b2ContactSim ) ); + fprintf( file, "\n" ); + + // stack allocator + fprintf( file, "stack allocator: %d\n\n", world->stackAllocator.capacity ); + + // chain shapes + // todo + + fclose( file ); +} + +typedef struct WorldQueryContext +{ + b2World* world; + b2OverlapResultFcn* fcn; + b2QueryFilter filter; + void* userContext; +} WorldQueryContext; + +static bool TreeQueryCallback( int proxyId, int shapeId, void* context ) +{ + B2_MAYBE_UNUSED( proxyId ); + + WorldQueryContext* worldContext = context; + b2World* world = worldContext->world; + + b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId ); + + b2Filter shapeFilter = shape->filter; + b2QueryFilter queryFilter = worldContext->filter; + + if ( ( shapeFilter.categoryBits & queryFilter.maskBits ) == 0 || ( shapeFilter.maskBits & queryFilter.categoryBits ) == 0 ) + { + return true; + } + + b2ShapeId id = { shapeId + 1, world->worldId, shape->revision }; + bool result = worldContext->fcn( id, worldContext->userContext ); + return result; +} + +void b2World_OverlapAABB( b2WorldId worldId, b2AABB aabb, b2QueryFilter filter, b2OverlapResultFcn* fcn, void* context ) +{ + b2World* world = b2GetWorldFromId( worldId ); + B2_ASSERT( world->locked == false ); + if ( world->locked ) + { + return; + } + + B2_ASSERT( b2AABB_IsValid( aabb ) ); + + WorldQueryContext worldContext = { world, fcn, filter, context }; + + for ( int i = 0; i < b2_bodyTypeCount; ++i ) + { + b2DynamicTree_Query( world->broadPhase.trees + i, aabb, filter.maskBits, TreeQueryCallback, &worldContext ); + } +} + +typedef struct WorldOverlapContext +{ + b2World* world; + b2OverlapResultFcn* fcn; + b2QueryFilter filter; + b2DistanceProxy proxy; + b2Transform transform; + void* userContext; +} WorldOverlapContext; + +static bool TreeOverlapCallback( int proxyId, int shapeId, void* context ) +{ + B2_MAYBE_UNUSED( proxyId ); + + WorldOverlapContext* worldContext = context; + b2World* world = worldContext->world; + + b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId ); + + b2Filter shapeFilter = shape->filter; + b2QueryFilter queryFilter = worldContext->filter; + + if ( ( shapeFilter.categoryBits & queryFilter.maskBits ) == 0 || ( shapeFilter.maskBits & queryFilter.categoryBits ) == 0 ) + { + return true; + } + + b2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId ); + b2Transform transform = b2GetBodyTransformQuick( world, body ); + + b2DistanceInput input; + input.proxyA = worldContext->proxy; + input.proxyB = b2MakeShapeDistanceProxy( shape ); + input.transformA = worldContext->transform; + input.transformB = transform; + input.useRadii = true; + + b2DistanceCache cache = { 0 }; + b2DistanceOutput output = b2ShapeDistance( &cache, &input, NULL, 0 ); + + if ( output.distance > 0.0f ) + { + return true; + } + + b2ShapeId id = { shape->id + 1, world->worldId, shape->revision }; + bool result = worldContext->fcn( id, worldContext->userContext ); + return result; +} + +void b2World_OverlapCircle( b2WorldId worldId, const b2Circle* circle, b2Transform transform, b2QueryFilter filter, + b2OverlapResultFcn* fcn, void* context ) +{ + b2World* world = b2GetWorldFromId( worldId ); + B2_ASSERT( world->locked == false ); + if ( world->locked ) + { + return; + } + + B2_ASSERT( b2Vec2_IsValid( transform.p ) ); + B2_ASSERT( b2Rot_IsValid( transform.q ) ); + + b2AABB aabb = b2ComputeCircleAABB( circle, transform ); + WorldOverlapContext worldContext = { + world, fcn, filter, b2MakeProxy( &circle->center, 1, circle->radius ), transform, context, + }; + + for ( int i = 0; i < b2_bodyTypeCount; ++i ) + { + b2DynamicTree_Query( world->broadPhase.trees + i, aabb, filter.maskBits, TreeOverlapCallback, &worldContext ); + } +} + +void b2World_OverlapCapsule( b2WorldId worldId, const b2Capsule* capsule, b2Transform transform, b2QueryFilter filter, + b2OverlapResultFcn* fcn, void* context ) +{ + b2World* world = b2GetWorldFromId( worldId ); + B2_ASSERT( world->locked == false ); + if ( world->locked ) + { + return; + } + + B2_ASSERT( b2Vec2_IsValid( transform.p ) ); + B2_ASSERT( b2Rot_IsValid( transform.q ) ); + + b2AABB aabb = b2ComputeCapsuleAABB( capsule, transform ); + WorldOverlapContext worldContext = { + world, fcn, filter, b2MakeProxy( &capsule->center1, 2, capsule->radius ), transform, context, + }; + + for ( int i = 0; i < b2_bodyTypeCount; ++i ) + { + b2DynamicTree_Query( world->broadPhase.trees + i, aabb, filter.maskBits, TreeOverlapCallback, &worldContext ); + } +} + +void b2World_OverlapPolygon( b2WorldId worldId, const b2Polygon* polygon, b2Transform transform, b2QueryFilter filter, + b2OverlapResultFcn* fcn, void* context ) +{ + b2World* world = b2GetWorldFromId( worldId ); + B2_ASSERT( world->locked == false ); + if ( world->locked ) + { + return; + } + + B2_ASSERT( b2Vec2_IsValid( transform.p ) ); + B2_ASSERT( b2Rot_IsValid( transform.q ) ); + + b2AABB aabb = b2ComputePolygonAABB( polygon, transform ); + WorldOverlapContext worldContext = { + world, fcn, filter, b2MakeProxy( polygon->vertices, polygon->count, polygon->radius ), transform, context, + }; + + for ( int i = 0; i < b2_bodyTypeCount; ++i ) + { + b2DynamicTree_Query( world->broadPhase.trees + i, aabb, filter.maskBits, TreeOverlapCallback, &worldContext ); + } +} + +typedef struct WorldRayCastContext +{ + b2World* world; + b2CastResultFcn* fcn; + b2QueryFilter filter; + float fraction; + void* userContext; +} WorldRayCastContext; + +static float RayCastCallback( const b2RayCastInput* input, int proxyId, int shapeId, void* context ) +{ + B2_MAYBE_UNUSED( proxyId ); + + WorldRayCastContext* worldContext = context; + b2World* world = worldContext->world; + + b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId ); + b2Filter shapeFilter = shape->filter; + b2QueryFilter queryFilter = worldContext->filter; + + if ( ( shapeFilter.categoryBits & queryFilter.maskBits ) == 0 || ( shapeFilter.maskBits & queryFilter.categoryBits ) == 0 ) + { + return input->maxFraction; + } + + b2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId ); + b2Transform transform = b2GetBodyTransformQuick( world, body ); + b2CastOutput output = b2RayCastShape( input, shape, transform ); + + if ( output.hit ) + { + b2ShapeId id = { shapeId + 1, world->worldId, shape->revision }; + float fraction = worldContext->fcn( id, output.point, output.normal, output.fraction, worldContext->userContext ); + worldContext->fraction = fraction; + return fraction; + } + + return input->maxFraction; +} + +void b2World_CastRay( b2WorldId worldId, b2Vec2 origin, b2Vec2 translation, b2QueryFilter filter, b2CastResultFcn* fcn, + void* context ) +{ + b2World* world = b2GetWorldFromId( worldId ); + B2_ASSERT( world->locked == false ); + if ( world->locked ) + { + return; + } + + B2_ASSERT( b2Vec2_IsValid( origin ) ); + B2_ASSERT( b2Vec2_IsValid( translation ) ); + + b2RayCastInput input = { origin, translation, 1.0f }; + + WorldRayCastContext worldContext = { world, fcn, filter, 1.0f, context }; + + for ( int i = 0; i < b2_bodyTypeCount; ++i ) + { + b2DynamicTree_RayCast( world->broadPhase.trees + i, &input, filter.maskBits, RayCastCallback, &worldContext ); + + if ( worldContext.fraction == 0.0f ) + { + return; + } + + input.maxFraction = worldContext.fraction; + } +} + +// This callback finds the closest hit. This is the most common callback used in games. +static float b2RayCastClosestFcn( b2ShapeId shapeId, b2Vec2 point, b2Vec2 normal, float fraction, void* context ) +{ + b2RayResult* rayResult = (b2RayResult*)context; + rayResult->shapeId = shapeId; + rayResult->point = point; + rayResult->normal = normal; + rayResult->fraction = fraction; + rayResult->hit = true; + return fraction; +} + +b2RayResult b2World_CastRayClosest( b2WorldId worldId, b2Vec2 origin, b2Vec2 translation, b2QueryFilter filter ) +{ + b2RayResult result = { 0 }; + + b2World* world = b2GetWorldFromId( worldId ); + B2_ASSERT( world->locked == false ); + if ( world->locked ) + { + return result; + } + + B2_ASSERT( b2Vec2_IsValid( origin ) ); + B2_ASSERT( b2Vec2_IsValid( translation ) ); + + b2RayCastInput input = { origin, translation, 1.0f }; + WorldRayCastContext worldContext = { world, b2RayCastClosestFcn, filter, 1.0f, &result }; + + for ( int i = 0; i < b2_bodyTypeCount; ++i ) + { + b2DynamicTree_RayCast( world->broadPhase.trees + i, &input, filter.maskBits, RayCastCallback, &worldContext ); + + if ( worldContext.fraction == 0.0f ) + { + return result; + } + + input.maxFraction = worldContext.fraction; + } + + return result; +} + +static float ShapeCastCallback( const b2ShapeCastInput* input, int proxyId, int shapeId, void* context ) +{ + B2_MAYBE_UNUSED( proxyId ); + + WorldRayCastContext* worldContext = context; + b2World* world = worldContext->world; + + b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId ); + b2Filter shapeFilter = shape->filter; + b2QueryFilter queryFilter = worldContext->filter; + + if ( ( shapeFilter.categoryBits & queryFilter.maskBits ) == 0 || ( shapeFilter.maskBits & queryFilter.categoryBits ) == 0 ) + { + return input->maxFraction; + } + + b2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId ); + b2Transform transform = b2GetBodyTransformQuick( world, body ); + b2CastOutput output = b2ShapeCastShape( input, shape, transform ); + + if ( output.hit ) + { + b2ShapeId id = { shapeId + 1, world->worldId, shape->revision }; + float fraction = worldContext->fcn( id, output.point, output.normal, output.fraction, worldContext->userContext ); + worldContext->fraction = fraction; + return fraction; + } + + return input->maxFraction; +} + +void b2World_CastCircle( b2WorldId worldId, const b2Circle* circle, b2Transform originTransform, b2Vec2 translation, + b2QueryFilter filter, b2CastResultFcn* fcn, void* context ) +{ + b2World* world = b2GetWorldFromId( worldId ); + B2_ASSERT( world->locked == false ); + if ( world->locked ) + { + return; + } + + B2_ASSERT( b2Vec2_IsValid( originTransform.p ) ); + B2_ASSERT( b2Rot_IsValid( originTransform.q ) ); + B2_ASSERT( b2Vec2_IsValid( translation ) ); + + b2ShapeCastInput input; + input.points[0] = b2TransformPoint( originTransform, circle->center ); + input.count = 1; + input.radius = circle->radius; + input.translation = translation; + input.maxFraction = 1.0f; + + WorldRayCastContext worldContext = { world, fcn, filter, 1.0f, context }; + + for ( int i = 0; i < b2_bodyTypeCount; ++i ) + { + b2DynamicTree_ShapeCast( world->broadPhase.trees + i, &input, filter.maskBits, ShapeCastCallback, &worldContext ); + + if ( worldContext.fraction == 0.0f ) + { + return; + } + + input.maxFraction = worldContext.fraction; + } +} + +void b2World_CastCapsule( b2WorldId worldId, const b2Capsule* capsule, b2Transform originTransform, b2Vec2 translation, + b2QueryFilter filter, b2CastResultFcn* fcn, void* context ) +{ + b2World* world = b2GetWorldFromId( worldId ); + B2_ASSERT( world->locked == false ); + if ( world->locked ) + { + return; + } + + B2_ASSERT( b2Vec2_IsValid( originTransform.p ) ); + B2_ASSERT( b2Rot_IsValid( originTransform.q ) ); + B2_ASSERT( b2Vec2_IsValid( translation ) ); + + b2ShapeCastInput input; + input.points[0] = b2TransformPoint( originTransform, capsule->center1 ); + input.points[1] = b2TransformPoint( originTransform, capsule->center2 ); + input.count = 2; + input.radius = capsule->radius; + input.translation = translation; + input.maxFraction = 1.0f; + + WorldRayCastContext worldContext = { world, fcn, filter, 1.0f, context }; + + for ( int i = 0; i < b2_bodyTypeCount; ++i ) + { + b2DynamicTree_ShapeCast( world->broadPhase.trees + i, &input, filter.maskBits, ShapeCastCallback, &worldContext ); + + if ( worldContext.fraction == 0.0f ) + { + return; + } + + input.maxFraction = worldContext.fraction; + } +} + +void b2World_CastPolygon( b2WorldId worldId, const b2Polygon* polygon, b2Transform originTransform, b2Vec2 translation, + b2QueryFilter filter, b2CastResultFcn* fcn, void* context ) +{ + b2World* world = b2GetWorldFromId( worldId ); + B2_ASSERT( world->locked == false ); + if ( world->locked ) + { + return; + } + + B2_ASSERT( b2Vec2_IsValid( originTransform.p ) ); + B2_ASSERT( b2Rot_IsValid( originTransform.q ) ); + B2_ASSERT( b2Vec2_IsValid( translation ) ); + + b2ShapeCastInput input; + for ( int i = 0; i < polygon->count; ++i ) + { + input.points[i] = b2TransformPoint( originTransform, polygon->vertices[i] ); + } + input.count = polygon->count; + input.radius = polygon->radius; + input.translation = translation; + input.maxFraction = 1.0f; + + WorldRayCastContext worldContext = { world, fcn, filter, 1.0f, context }; + + for ( int i = 0; i < b2_bodyTypeCount; ++i ) + { + b2DynamicTree_ShapeCast( world->broadPhase.trees + i, &input, filter.maskBits, ShapeCastCallback, &worldContext ); + + if ( worldContext.fraction == 0.0f ) + { + return; + } + + input.maxFraction = worldContext.fraction; + } +} + +#if 0 + +void b2World_ShiftOrigin(b2WorldId worldId, b2Vec2 newOrigin) +{ + B2_ASSERT(m_locked == false); + if (m_locked) + { + return; + } + + for (b2Body* b = m_bodyList; b; b = b->m_next) + { + b->m_xf.p -= newOrigin; + b->m_sweep.c0 -= newOrigin; + b->m_sweep.c -= newOrigin; + } + + for (b2Joint* j = m_jointList; j; j = j->m_next) + { + j->ShiftOrigin(newOrigin); + } + + m_contactManager.m_broadPhase.ShiftOrigin(newOrigin); +} + +void b2World_Dump() +{ + if (m_locked) + { + return; + } + + b2OpenDump("box2d_dump.inl"); + + b2Dump("b2Vec2 g(%.9g, %.9g);\n", m_gravity.x, m_gravity.y); + b2Dump("m_world->SetGravity(g);\n"); + + b2Dump("b2Body** sims = (b2Body**)b2Alloc(%d * sizeof(b2Body*));\n", m_bodyCount); + b2Dump("b2Joint** joints = (b2Joint**)b2Alloc(%d * sizeof(b2Joint*));\n", m_jointCount); + + int32 i = 0; + for (b2Body* b = m_bodyList; b; b = b->m_next) + { + b->m_islandIndex = i; + b->Dump(); + ++i; + } + + i = 0; + for (b2Joint* j = m_jointList; j; j = j->m_next) + { + j->m_index = i; + ++i; + } + + // First pass on joints, skip gear joints. + for (b2Joint* j = m_jointList; j; j = j->m_next) + { + if (j->m_type == e_gearJoint) + { + continue; + } + + b2Dump("{\n"); + j->Dump(); + b2Dump("}\n"); + } + + // Second pass on joints, only gear joints. + for (b2Joint* j = m_jointList; j; j = j->m_next) + { + if (j->m_type != e_gearJoint) + { + continue; + } + + b2Dump("{\n"); + j->Dump(); + b2Dump("}\n"); + } + + b2Dump("b2Free(joints);\n"); + b2Dump("b2Free(sims);\n"); + b2Dump("joints = nullptr;\n"); + b2Dump("sims = nullptr;\n"); + + b2CloseDump(); +} +#endif + +void b2World_SetCustomFilterCallback( b2WorldId worldId, b2CustomFilterFcn* fcn, void* context ) +{ + b2World* world = b2GetWorldFromId( worldId ); + world->customFilterFcn = fcn; + world->customFilterContext = context; +} + +void b2World_SetPreSolveCallback( b2WorldId worldId, b2PreSolveFcn* fcn, void* context ) +{ + b2World* world = b2GetWorldFromId( worldId ); + world->preSolveFcn = fcn; + world->preSolveContext = context; +} + +void b2World_SetGravity( b2WorldId worldId, b2Vec2 gravity ) +{ + b2World* world = b2GetWorldFromId( worldId ); + world->gravity = gravity; +} + +b2Vec2 b2World_GetGravity( b2WorldId worldId ) +{ + b2World* world = b2GetWorldFromId( worldId ); + return world->gravity; +} + +struct ExplosionContext +{ + b2World* world; + b2Vec2 position; + float radius; + float magnitude; +}; + +static bool ExplosionCallback( int proxyId, int shapeId, void* context ) +{ + B2_MAYBE_UNUSED( proxyId ); + + struct ExplosionContext* explosionContext = context; + b2World* world = explosionContext->world; + + b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId ); + + b2Body* body = b2BodyArray_Get( &world->bodies, shape->bodyId ); + if ( body->type == b2_kinematicBody ) + { + return true; + } + + b2WakeBody( world, body ); + + if ( body->setIndex != b2_awakeSet ) + { + return true; + } + + b2Transform transform = b2GetBodyTransformQuick( world, body ); + + b2DistanceInput input; + input.proxyA = b2MakeShapeDistanceProxy( shape ); + input.proxyB = b2MakeProxy( &explosionContext->position, 1, 0.0f ); + input.transformA = transform; + input.transformB = b2Transform_identity; + input.useRadii = true; + + b2DistanceCache cache = { 0 }; + b2DistanceOutput output = b2ShapeDistance( &cache, &input, NULL, 0 ); + + if ( output.distance > explosionContext->radius ) + { + return true; + } + + b2Vec2 closestPoint = output.pointA; + + if ( output.distance == 0.0f ) + { + b2Vec2 localCentroid = b2GetShapeCentroid( shape ); + closestPoint = b2TransformPoint( transform, localCentroid ); + } + + float falloff = 0.4f; + float perimeter = b2GetShapePerimeter( shape ); + float magnitude = explosionContext->magnitude * perimeter * ( 1.0f - falloff * output.distance / explosionContext->radius ); + + b2Vec2 direction = b2Normalize( b2Sub( closestPoint, explosionContext->position ) ); + b2Vec2 impulse = b2MulSV( magnitude, direction ); + + int localIndex = body->localIndex; + b2SolverSet* set = b2SolverSetArray_Get( &world->solverSets, b2_awakeSet ); + b2BodyState* state = b2BodyStateArray_Get( &set->bodyStates, localIndex ); + b2BodySim* bodySim = b2BodySimArray_Get( &set->bodySims, localIndex ); + state->linearVelocity = b2MulAdd( state->linearVelocity, bodySim->invMass, impulse ); + state->angularVelocity += bodySim->invInertia * b2Cross( b2Sub( closestPoint, bodySim->center ), impulse ); + + return true; +} + +void b2World_Explode( b2WorldId worldId, b2Vec2 position, float radius, float magnitude ) +{ + B2_ASSERT( b2Vec2_IsValid( position ) ); + B2_ASSERT( b2IsValid( radius ) && radius > 0.0f ); + B2_ASSERT( b2IsValid( magnitude ) ); + + b2World* world = b2GetWorldFromId( worldId ); + B2_ASSERT( world->locked == false ); + if ( world->locked ) + { + return; + } + + struct ExplosionContext explosionContext = { world, position, radius, magnitude }; + + b2AABB aabb; + aabb.lowerBound.x = position.x - radius; + aabb.lowerBound.y = position.y - radius; + aabb.upperBound.x = position.x + radius; + aabb.upperBound.y = position.y + radius; + + b2DynamicTree_Query( world->broadPhase.trees + b2_dynamicBody, aabb, b2_defaultMaskBits, ExplosionCallback, + &explosionContext ); +} + +#if B2_VALIDATE +// When validating islands ids I have to compare the root island +// ids because islands are not merged until the next time step. +static int b2GetRootIslandId( b2World* world, int islandId ) +{ + if ( islandId == B2_NULL_INDEX ) + { + return B2_NULL_INDEX; + } + + b2Island* island = b2IslandArray_Get( &world->islands, islandId ); + + int rootId = islandId; + b2Island* rootIsland = island; + while ( rootIsland->parentIsland != B2_NULL_INDEX ) + { + b2Island* parent = b2IslandArray_Get( &world->islands, rootIsland->parentIsland ); + rootId = rootIsland->parentIsland; + rootIsland = parent; + } + + return rootId; +} + +// This validates island graph connectivity for each body +void b2ValidateConnectivity( b2World* world ) +{ + b2Body* bodies = world->bodies.data; + int bodyCapacity = world->bodies.count; + + for ( int bodyIndex = 0; bodyIndex < bodyCapacity; ++bodyIndex ) + { + b2Body* body = bodies + bodyIndex; + if ( body->id == B2_NULL_INDEX ) + { + b2ValidateFreeId( &world->bodyIdPool, bodyIndex ); + continue; + } + + B2_ASSERT( bodyIndex == body->id ); + + // Need to get the root island because islands are not merged until the next time step + int bodyIslandId = b2GetRootIslandId( world, body->islandId ); + int bodySetIndex = body->setIndex; + + int contactKey = body->headContactKey; + while ( contactKey != B2_NULL_INDEX ) + { + int contactId = contactKey >> 1; + int edgeIndex = contactKey & 1; + + b2Contact* contact = b2ContactArray_Get(&world->contacts, contactId); + + bool touching = ( contact->flags & b2_contactTouchingFlag ) != 0; + if ( touching && ( contact->flags & b2_contactSensorFlag ) == 0 ) + { + if ( bodySetIndex != b2_staticSet ) + { + int contactIslandId = b2GetRootIslandId( world, contact->islandId ); + B2_ASSERT( contactIslandId == bodyIslandId ); + } + } + else + { + B2_ASSERT( contact->islandId == B2_NULL_INDEX ); + } + + contactKey = contact->edges[edgeIndex].nextKey; + } + + int jointKey = body->headJointKey; + while ( jointKey != B2_NULL_INDEX ) + { + int jointId = jointKey >> 1; + int edgeIndex = jointKey & 1; + + b2Joint* joint = b2JointArray_Get( &world->joints, jointId ); + + int otherEdgeIndex = edgeIndex ^ 1; + + b2Body* otherBody = b2BodyArray_Get( &world->bodies, joint->edges[otherEdgeIndex].bodyId ); + + if ( bodySetIndex == b2_disabledSet || otherBody->setIndex == b2_disabledSet ) + { + B2_ASSERT( joint->islandId == B2_NULL_INDEX ); + } + else if ( bodySetIndex == b2_staticSet ) + { + if ( otherBody->setIndex == b2_staticSet ) + { + B2_ASSERT( joint->islandId == B2_NULL_INDEX ); + } + } + else + { + int jointIslandId = b2GetRootIslandId( world, joint->islandId ); + B2_ASSERT( jointIslandId == bodyIslandId ); + } + + jointKey = joint->edges[edgeIndex].nextKey; + } + } +} + +// Validates solver sets, but not island connectivity +void b2ValidateSolverSets( b2World* world ) +{ + B2_ASSERT( b2GetIdCapacity( &world->bodyIdPool ) == world->bodies.count ); + B2_ASSERT( b2GetIdCapacity( &world->contactIdPool ) == world->contacts.count ); + B2_ASSERT( b2GetIdCapacity( &world->jointIdPool ) == world->joints.count ); + B2_ASSERT( b2GetIdCapacity( &world->islandIdPool ) == world->islands.count ); + B2_ASSERT( b2GetIdCapacity( &world->solverSetIdPool ) == world->solverSets.count ); + + int activeSetCount = 0; + int totalBodyCount = 0; + int totalJointCount = 0; + int totalContactCount = 0; + int totalIslandCount = 0; + + // Validate all solver sets + int setCount = world->solverSets.count; + for ( int setIndex = 0; setIndex < setCount; ++setIndex ) + { + b2SolverSet* set = world->solverSets.data + setIndex; + if ( set->setIndex != B2_NULL_INDEX ) + { + activeSetCount += 1; + + if ( setIndex == b2_staticSet ) + { + B2_ASSERT( set->contactSims.count == 0 ); + B2_ASSERT( set->islandSims.count == 0 ); + B2_ASSERT( set->bodyStates.count == 0 ); + } + else if ( setIndex == b2_awakeSet ) + { + B2_ASSERT( set->bodySims.count == set->bodyStates.count ); + B2_ASSERT( set->jointSims.count == 0 ); + } + else if ( setIndex == b2_disabledSet ) + { + B2_ASSERT( set->islandSims.count == 0 ); + B2_ASSERT( set->bodyStates.count == 0 ); + } + else + { + B2_ASSERT( set->bodyStates.count == 0 ); + } + + // Validate bodies + { + b2Body* bodies = world->bodies.data; + B2_ASSERT( set->bodySims.count >= 0 ); + totalBodyCount += set->bodySims.count; + for ( int i = 0; i < set->bodySims.count; ++i ) + { + b2BodySim* bodySim = set->bodySims.data + i; + + int bodyId = bodySim->bodyId; + B2_ASSERT( 0 <= bodyId && bodyId < world->bodies.count ); + b2Body* body = bodies + bodyId; + B2_ASSERT( body->setIndex == setIndex ); + B2_ASSERT( body->localIndex == i ); + B2_ASSERT( body->revision == body->revision ); + + if ( setIndex == b2_disabledSet ) + { + B2_ASSERT( body->headContactKey == B2_NULL_INDEX ); + } + + // Validate body shapes + int prevShapeId = B2_NULL_INDEX; + int shapeId = body->headShapeId; + while ( shapeId != B2_NULL_INDEX ) + { + b2Shape* shape = b2ShapeArray_Get( &world->shapes, shapeId ); + B2_ASSERT( shape->id == shapeId ); + B2_ASSERT( shape->prevShapeId == prevShapeId ); + + if ( setIndex == b2_disabledSet ) + { + B2_ASSERT( shape->proxyKey == B2_NULL_INDEX ); + } + else if ( setIndex == b2_staticSet ) + { + B2_ASSERT( B2_PROXY_TYPE( shape->proxyKey ) == b2_staticBody ); + } + else + { + b2BodyType proxyType = B2_PROXY_TYPE( shape->proxyKey ); + B2_ASSERT( proxyType == b2_kinematicBody || proxyType == b2_dynamicBody ); + } + + prevShapeId = shapeId; + shapeId = shape->nextShapeId; + } + + // Validate body contacts + int contactKey = body->headContactKey; + while ( contactKey != B2_NULL_INDEX ) + { + int contactId = contactKey >> 1; + int edgeIndex = contactKey & 1; + + b2Contact* contact = b2ContactArray_Get(&world->contacts, contactId); + B2_ASSERT( contact->setIndex != b2_staticSet ); + B2_ASSERT( contact->edges[0].bodyId == bodyId || contact->edges[1].bodyId == bodyId ); + contactKey = contact->edges[edgeIndex].nextKey; + } + + // Validate body joints + int jointKey = body->headJointKey; + while ( jointKey != B2_NULL_INDEX ) + { + int jointId = jointKey >> 1; + int edgeIndex = jointKey & 1; + + b2Joint* joint = b2JointArray_Get( &world->joints, jointId ); + + int otherEdgeIndex = edgeIndex ^ 1; + + b2Body* otherBody = b2BodyArray_Get( &world->bodies, joint->edges[otherEdgeIndex].bodyId ); + + if ( setIndex == b2_disabledSet || otherBody->setIndex == b2_disabledSet ) + { + B2_ASSERT( joint->setIndex == b2_disabledSet ); + } + else if ( setIndex == b2_staticSet && otherBody->setIndex == b2_staticSet ) + { + B2_ASSERT( joint->setIndex == b2_staticSet ); + } + else if ( setIndex == b2_awakeSet ) + { + B2_ASSERT( joint->setIndex == b2_awakeSet ); + } + else if ( setIndex >= b2_firstSleepingSet ) + { + B2_ASSERT( joint->setIndex == setIndex ); + } + + b2JointSim* jointSim = b2GetJointSim( world, joint ); + B2_ASSERT( jointSim->jointId == jointId ); + B2_ASSERT( jointSim->bodyIdA == joint->edges[0].bodyId ); + B2_ASSERT( jointSim->bodyIdB == joint->edges[1].bodyId ); + + jointKey = joint->edges[edgeIndex].nextKey; + } + } + } + + // Validate contacts + { + B2_ASSERT( set->contactSims.count >= 0 ); + totalContactCount += set->contactSims.count; + for ( int i = 0; i < set->contactSims.count; ++i ) + { + b2ContactSim* contactSim = set->contactSims.data + i; + b2Contact* contact = b2ContactArray_Get(&world->contacts, contactSim->contactId); + if ( setIndex == b2_awakeSet ) + { + // contact should be non-touching if awake + // or it could be this contact hasn't been transferred yet + B2_ASSERT( contactSim->manifold.pointCount == 0 || + ( contactSim->simFlags & b2_simStartedTouching ) != 0 ); + } + B2_ASSERT( contact->setIndex == setIndex ); + B2_ASSERT( contact->colorIndex == B2_NULL_INDEX ); + B2_ASSERT( contact->localIndex == i ); + } + } + + // Validate joints + { + B2_ASSERT( set->jointSims.count >= 0 ); + totalJointCount += set->jointSims.count; + for ( int i = 0; i < set->jointSims.count; ++i ) + { + b2JointSim* jointSim = set->jointSims.data + i; + b2Joint* joint = b2JointArray_Get( &world->joints, jointSim->jointId ); + B2_ASSERT( joint->setIndex == setIndex ); + B2_ASSERT( joint->colorIndex == B2_NULL_INDEX ); + B2_ASSERT( joint->localIndex == i ); + } + } + + // Validate islands + { + B2_ASSERT( set->islandSims.count >= 0 ); + totalIslandCount += set->islandSims.count; + for ( int i = 0; i < set->islandSims.count; ++i ) + { + b2IslandSim* islandSim = set->islandSims.data + i; + b2Island* island = b2IslandArray_Get( &world->islands, islandSim->islandId ); + B2_ASSERT( island->setIndex == setIndex ); + B2_ASSERT( island->localIndex == i ); + } + } + } + else + { + B2_ASSERT( set->bodySims.count == 0 ); + B2_ASSERT( set->contactSims.count == 0 ); + B2_ASSERT( set->jointSims.count == 0 ); + B2_ASSERT( set->islandSims.count == 0 ); + B2_ASSERT( set->bodyStates.count == 0 ); + } + } + + int setIdCount = b2GetIdCount( &world->solverSetIdPool ); + B2_ASSERT( activeSetCount == setIdCount ); + + int bodyIdCount = b2GetIdCount( &world->bodyIdPool ); + B2_ASSERT( totalBodyCount == bodyIdCount ); + + int islandIdCount = b2GetIdCount( &world->islandIdPool ); + B2_ASSERT( totalIslandCount == islandIdCount ); + + // Validate constraint graph + for ( int colorIndex = 0; colorIndex < b2_graphColorCount; ++colorIndex ) + { + b2GraphColor* color = world->constraintGraph.colors + colorIndex; + { + B2_ASSERT( color->contactSims.count >= 0 ); + totalContactCount += color->contactSims.count; + for ( int i = 0; i < color->contactSims.count; ++i ) + { + b2ContactSim* contactSim = color->contactSims.data + i; + b2Contact* contact = b2ContactArray_Get(&world->contacts, contactSim->contactId); + // contact should be touching in the constraint graph or awaiting transfer to non-touching + B2_ASSERT( contactSim->manifold.pointCount > 0 || + ( contactSim->simFlags & ( b2_simStoppedTouching | b2_simDisjoint ) ) != 0 ); + B2_ASSERT( contact->setIndex == b2_awakeSet ); + B2_ASSERT( contact->colorIndex == colorIndex ); + B2_ASSERT( contact->localIndex == i ); + + int bodyIdA = contact->edges[0].bodyId; + int bodyIdB = contact->edges[1].bodyId; + + if ( colorIndex < b2_overflowIndex ) + { + b2Body* bodyA = b2BodyArray_Get( &world->bodies, bodyIdA ); + b2Body* bodyB = b2BodyArray_Get( &world->bodies, bodyIdB ); + B2_ASSERT( b2GetBit( &color->bodySet, bodyIdA ) == ( bodyA->type != b2_staticBody ) ); + B2_ASSERT( b2GetBit( &color->bodySet, bodyIdB ) == ( bodyB->type != b2_staticBody ) ); + } + } + } + + { + B2_ASSERT( color->jointSims.count >= 0 ); + totalJointCount += color->jointSims.count; + for ( int i = 0; i < color->jointSims.count; ++i ) + { + b2JointSim* jointSim = color->jointSims.data + i; + b2Joint* joint = b2JointArray_Get( &world->joints, jointSim->jointId ); + B2_ASSERT( joint->setIndex == b2_awakeSet ); + B2_ASSERT( joint->colorIndex == colorIndex ); + B2_ASSERT( joint->localIndex == i ); + + int bodyIdA = joint->edges[0].bodyId; + int bodyIdB = joint->edges[1].bodyId; + + if ( colorIndex < b2_overflowIndex ) + { + b2Body* bodyA = b2BodyArray_Get( &world->bodies, bodyIdA ); + b2Body* bodyB = b2BodyArray_Get( &world->bodies, bodyIdB ); + B2_ASSERT( b2GetBit( &color->bodySet, bodyIdA ) == ( bodyA->type != b2_staticBody ) ); + B2_ASSERT( b2GetBit( &color->bodySet, bodyIdB ) == ( bodyB->type != b2_staticBody ) ); + } + } + } + } + + int contactIdCount = b2GetIdCount( &world->contactIdPool ); + B2_ASSERT( totalContactCount == contactIdCount ); + B2_ASSERT( totalContactCount == world->broadPhase.pairSet.count ); + + int jointIdCount = b2GetIdCount( &world->jointIdPool ); + B2_ASSERT( totalJointCount == jointIdCount ); + +// Validate shapes +// This is very slow on compounds +#if 0 + int shapeCapacity = b2Array(world->shapeArray).count; + for (int shapeIndex = 0; shapeIndex < shapeCapacity; shapeIndex += 1) + { + b2Shape* shape = world->shapeArray + shapeIndex; + if (shape->id != shapeIndex) + { + continue; + } + + B2_ASSERT(0 <= shape->bodyId && shape->bodyId < b2Array(world->bodyArray).count); + + b2Body* body = world->bodyArray + shape->bodyId; + B2_ASSERT(0 <= body->setIndex && body->setIndex < b2Array(world->solverSetArray).count); + + b2SolverSet* set = world->solverSetArray + body->setIndex; + B2_ASSERT(0 <= body->localIndex && body->localIndex < set->sims.count); + + b2BodySim* bodySim = set->sims.data + body->localIndex; + B2_ASSERT(bodySim->bodyId == shape->bodyId); + + bool found = false; + int shapeCount = 0; + int index = body->headShapeId; + while (index != B2_NULL_INDEX) + { + b2CheckId(world->shapeArray, index); + b2Shape* s = world->shapeArray + index; + if (index == shapeIndex) + { + found = true; + } + + index = s->nextShapeId; + shapeCount += 1; + } + + B2_ASSERT(found); + B2_ASSERT(shapeCount == body->shapeCount); + } +#endif +} + +// Validate contact touching status. +void b2ValidateContacts( b2World* world ) +{ + int contactCount = world->contacts.count; + B2_ASSERT( contactCount == b2GetIdCapacity( &world->contactIdPool ) ); + int allocatedContactCount = 0; + + for ( int contactIndex = 0; contactIndex < contactCount; ++contactIndex ) + { + b2Contact* contact = b2ContactArray_Get(&world->contacts, contactIndex); + if ( contact->contactId == B2_NULL_INDEX ) + { + continue; + } + + B2_ASSERT( contact->contactId == contactIndex ); + + allocatedContactCount += 1; + + bool touching = ( contact->flags & b2_contactTouchingFlag ) != 0; + bool sensorTouching = ( contact->flags & b2_contactSensorTouchingFlag ) != 0; + bool isSensor = ( contact->flags & b2_contactSensorFlag ) != 0; + + B2_ASSERT( touching == false || sensorTouching == false ); + B2_ASSERT( touching == false || isSensor == false ); + + int setId = contact->setIndex; + + if ( setId == b2_awakeSet ) + { + // If touching and not a sensor + if ( touching && isSensor == false ) + { + B2_ASSERT( 0 <= contact->colorIndex && contact->colorIndex < b2_graphColorCount ); + } + else + { + B2_ASSERT( contact->colorIndex == B2_NULL_INDEX ); + } + } + else if ( setId >= b2_firstSleepingSet ) + { + // Only touching contacts allowed in a sleeping set + B2_ASSERT( touching == true && isSensor == false ); + } + else + { + // Sleeping and non-touching contacts or sensor contacts belong in the disabled set + B2_ASSERT( touching == false && setId == b2_disabledSet ); + } + + b2ContactSim* contactSim = b2GetContactSim( world, contact ); + B2_ASSERT( contactSim->contactId == contactIndex ); + B2_ASSERT( contactSim->bodyIdA == contact->edges[0].bodyId ); + B2_ASSERT( contactSim->bodyIdB == contact->edges[1].bodyId ); + + // Sim touching is true for solid and sensor contacts + bool simTouching = ( contactSim->simFlags & b2_simTouchingFlag ) != 0; + B2_ASSERT( touching == simTouching || sensorTouching == simTouching ); + + B2_ASSERT( 0 <= contactSim->manifold.pointCount && contactSim->manifold.pointCount <= 2 ); + } + + int contactIdCount = b2GetIdCount( &world->contactIdPool ); + B2_ASSERT( allocatedContactCount == contactIdCount ); +} + +#else + +void b2ValidateConnectivity( b2World* world ) +{ + B2_MAYBE_UNUSED( world ); +} + +void b2ValidateSolverSets( b2World* world ) +{ + B2_MAYBE_UNUSED( world ); +} + +void b2ValidateContacts( b2World* world ) +{ + B2_MAYBE_UNUSED( world ); +} + +#endif diff --git a/3rdparty/box2d/src/world.h b/3rdparty/box2d/src/world.h new file mode 100644 index 000000000000..8bba9ef5293a --- /dev/null +++ b/3rdparty/box2d/src/world.h @@ -0,0 +1,180 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include "array.h" +#include "bitset.h" +#include "broad_phase.h" +#include "constraint_graph.h" +#include "id_pool.h" +#include "stack_allocator.h" + +#include "box2d/types.h" + +typedef struct b2ContactSim b2ContactSim; + +enum b2SetType +{ + b2_staticSet = 0, + b2_disabledSet = 1, + b2_awakeSet = 2, + b2_firstSleepingSet = 3, +}; + +// Per thread task storage +typedef struct b2TaskContext +{ + // These bits align with the b2ConstraintGraph::contactBlocks and signal a change in contact status + b2BitSet contactStateBitSet; + + // Used to track bodies with shapes that have enlarged AABBs. This avoids having a bit array + // that is very large when there are many static shapes. + b2BitSet enlargedSimBitSet; + + // Used to put islands to sleep + b2BitSet awakeIslandBitSet; + + // Per worker split island candidate + float splitSleepTime; + int splitIslandId; + +} b2TaskContext; + +/// The world class manages all physics entities, dynamic simulation, +/// and asynchronous queries. The world also contains efficient memory +/// management facilities. +typedef struct b2World +{ + b2StackAllocator stackAllocator; + b2BroadPhase broadPhase; + b2ConstraintGraph constraintGraph; + + // The body id pool is used to allocate and recycle body ids. Body ids + // provide a stable identifier for users, but incur caches misses when used + // to access body data. Aligns with b2Body. + b2IdPool bodyIdPool; + + // This is a sparse array that maps body ids to the body data + // stored in solver sets. As sims move within a set or across set. + // Indices come from id pool. + b2BodyArray bodies; + + // Provides free list for solver sets. + b2IdPool solverSetIdPool; + + // Solvers sets allow sims to be stored in contiguous arrays. The first + // set is all static sims. The second set is active sims. The third set is disabled + // sims. The remaining sets are sleeping islands. + b2SolverSetArray solverSets; + + // Used to create stable ids for joints + b2IdPool jointIdPool; + + // This is a sparse array that maps joint ids to the joint data stored in the constraint graph + // or in the solver sets. + b2JointArray joints; + + // Used to create stable ids for contacts + b2IdPool contactIdPool; + + // This is a sparse array that maps contact ids to the contact data stored in the constraint graph + // or in the solver sets. + b2ContactArray contacts; + + // Used to create stable ids for islands + b2IdPool islandIdPool; + + // This is a sparse array that maps island ids to the island data stored in the solver sets. + b2IslandArray islands; + + b2IdPool shapeIdPool; + b2IdPool chainIdPool; + + // These are sparse arrays that point into the pools above + b2ShapeArray shapes; + b2ChainShapeArray chainShapes; + + // Per thread storage + b2TaskContextArray taskContexts; + + b2BodyMoveEventArray bodyMoveEvents; + b2SensorBeginTouchEventArray sensorBeginEvents; + b2SensorEndTouchEventArray sensorEndEvents; + b2ContactBeginTouchEventArray contactBeginEvents; + b2ContactEndTouchEventArray contactEndEvents; + b2ContactHitEventArray contactHitEvents; + + // Used to track debug draw + b2BitSet debugBodySet; + b2BitSet debugJointSet; + b2BitSet debugContactSet; + + // Id that is incremented every time step + uint64_t stepIndex; + + // Identify islands for splitting as follows: + // - I want to split islands so smaller islands can sleep + // - when a body comes to rest and its sleep timer trips, I can look at the island and flag it for splitting + // if it has removed constraints + // - islands that have removed constraints must be put split first because I don't want to wake bodies incorrectly + // - otherwise I can use the awake islands that have bodies wanting to sleep as the splitting candidates + // - if no bodies want to sleep then there is no reason to perform island splitting + int splitIslandId; + + b2Vec2 gravity; + float hitEventThreshold; + float restitutionThreshold; + float maxLinearVelocity; + float contactPushoutVelocity; + float contactHertz; + float contactDampingRatio; + float jointHertz; + float jointDampingRatio; + + uint16_t revision; + + b2Profile profile; + + b2PreSolveFcn* preSolveFcn; + void* preSolveContext; + + b2CustomFilterFcn* customFilterFcn; + void* customFilterContext; + + int workerCount; + b2EnqueueTaskCallback* enqueueTaskFcn; + b2FinishTaskCallback* finishTaskFcn; + void* userTaskContext; + void* userTreeTask; + + // Remember type step used for reporting forces and torques + float inv_h; + + int activeTaskCount; + int taskCount; + + uint16_t worldId; + + bool enableSleep; + bool locked; + bool enableWarmStarting; + bool enableContinuous; + bool inUse; +} b2World; + +b2World* b2GetWorldFromId( b2WorldId id ); +b2World* b2GetWorld( int index ); +b2World* b2GetWorldLocked( int index ); + +void b2ValidateConnectivity( b2World* world ); +void b2ValidateSolverSets( b2World* world ); +void b2ValidateContacts( b2World* world ); + +B2_ARRAY_INLINE( b2BodyMoveEvent, b2BodyMoveEvent ); +B2_ARRAY_INLINE( b2ContactBeginTouchEvent, b2ContactBeginTouchEvent ); +B2_ARRAY_INLINE( b2ContactEndTouchEvent, b2ContactEndTouchEvent ); +B2_ARRAY_INLINE( b2ContactHitEvent, b2ContactHitEvent ); +B2_ARRAY_INLINE( b2SensorBeginTouchEvent, b2SensorBeginTouchEvent ); +B2_ARRAY_INLINE( b2SensorEndTouchEvent, b2SensorEndTouchEvent ); +B2_ARRAY_INLINE( b2TaskContext, b2TaskContext ); diff --git a/3rdparty/chipmunk/CMakeLists.txt b/3rdparty/chipmunk/CMakeLists.txt deleted file mode 100644 index 39cef51c1305..000000000000 --- a/3rdparty/chipmunk/CMakeLists.txt +++ /dev/null @@ -1,72 +0,0 @@ -cmake_minimum_required(VERSION 3.10) - -project(chipmunk) - -# to change the prefix, run cmake with the parameter: -# -D CMAKE_INSTALL_PREFIX=/my/prefix - -# to change the build type, run cmake with the parameter: -# -D CMAKE_BUILD_TYPE= -# run "cmake --help-variable CMAKE_BUILD_TYPE" for details -if(NOT CMAKE_BUILD_TYPE) - SET(CMAKE_BUILD_TYPE Release CACHE STRING - "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." - FORCE) -endif() - -# to manually select install locations of libraries and executables -# -D LIB_INSTALL_DIR mylib -# -D BIN_INSTALL_DIR newbin -set(LIB_INSTALL_DIR lib CACHE STRING "Install location of libraries") -set(BIN_INSTALL_DIR bin CACHE STRING "Install location of executables") - -# other options for the build, you can i.e. activate the shared library by passing -# -D CP_BUILD_SHARED=ON -# to cmake. Other options analog -if(ANDROID) - option(CP_BUILD_DEMOS "Build the demo applications" OFF) - option(CP_INSTALL_DEMOS "Install the demo applications" OFF) - option(CP_BUILD_SHARED "Build and install the shared library" ON) - option(CP_BUILD_STATIC "Build as static library" ON) - option(CP_INSTALL_STATIC "Install the static library" OFF) -else() - option(CP_BUILD_DEMOS "Build the demo applications" ON) - option(CP_INSTALL_DEMOS "Install the demo applications" OFF) - option(CP_BUILD_SHARED "Build and install the shared library" ON) - option(CP_BUILD_STATIC "Build as static library" ON) - option(CP_INSTALL_STATIC "Install the static library" ON) -endif() - -option(CP_USE_DOUBLES ON) -if(NOT CP_USE_DOUBLES) - add_definitions(-DCP_USE_DOUBLES=0) -endif() - -if(CMAKE_C_COMPILER_ID STREQUAL "Clang") - option(FORCE_CLANG_BLOCKS "Force enable Clang blocks" YES) -endif() - -# sanity checks... -if(CP_INSTALL_DEMOS) - set(CP_BUILD_DEMOS ON FORCE) -endif() - -# these need the static lib too -if(CP_BUILD_DEMOS OR CP_INSTALL_STATIC) - set(CP_BUILD_STATIC ON FORCE) -endif() - -if(NOT MSVC) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99") # always use gnu99 - if(FORCE_CLANG_BLOCKS) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fblocks") - endif() - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -ffast-math") # extend release-profile with fast-math - set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wall") # extend debug-profile with -Wall -endif() - -add_subdirectory(src) - -if(CP_BUILD_DEMOS) - add_subdirectory(demo) -endif() diff --git a/3rdparty/chipmunk/include/chipmunk/chipmunk.h b/3rdparty/chipmunk/include/chipmunk/chipmunk.h deleted file mode 100644 index 68359d3fa4ca..000000000000 --- a/3rdparty/chipmunk/include/chipmunk/chipmunk.h +++ /dev/null @@ -1,235 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef CHIPMUNK_H -#define CHIPMUNK_H - -#include -#include - -#ifndef alloca - #ifdef _WIN32 - #include - #elif defined(__FreeBSD__) - /* already included in */ - #else - #include - #endif -#endif - -#ifdef _WIN32 - #define CP_EXPORT __declspec(dllexport) -#else - #define CP_EXPORT -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -CP_EXPORT void cpMessage(const char *condition, const char *file, int line, int isError, int isHardError, const char *message, ...); -#ifdef NDEBUG - #define cpAssertWarn(__condition__, ...) - #define cpAssertSoft(__condition__, ...) -#else - #define cpAssertSoft(__condition__, ...) if(!(__condition__)){cpMessage(#__condition__, __FILE__, __LINE__, 1, 0, __VA_ARGS__); abort();} - #define cpAssertWarn(__condition__, ...) if(!(__condition__)) cpMessage(#__condition__, __FILE__, __LINE__, 0, 0, __VA_ARGS__) -#endif - -// Hard assertions are used in situations where the program definitely will crash anyway, and the reason is inexpensive to detect. -#define cpAssertHard(__condition__, ...) if(!(__condition__)){cpMessage(#__condition__, __FILE__, __LINE__, 1, 1, __VA_ARGS__); abort();} - -#include "chipmunk_types.h" - -/// @defgroup misc Misc -/// @{ - -/// Allocated size for various Chipmunk buffers -#ifndef CP_BUFFER_BYTES - #define CP_BUFFER_BYTES (32*1024) -#endif - -#ifndef cpcalloc - /// Chipmunk calloc() alias. - #define cpcalloc calloc -#endif - -#ifndef cprealloc - /// Chipmunk realloc() alias. - #define cprealloc realloc -#endif - -#ifndef cpfree - /// Chipmunk free() alias. - #define cpfree free -#endif - -typedef struct cpArray cpArray; -typedef struct cpHashSet cpHashSet; - -typedef struct cpBody cpBody; - -typedef struct cpShape cpShape; -typedef struct cpCircleShape cpCircleShape; -typedef struct cpSegmentShape cpSegmentShape; -typedef struct cpPolyShape cpPolyShape; - -typedef struct cpConstraint cpConstraint; -typedef struct cpPinJoint cpPinJoint; -typedef struct cpSlideJoint cpSlideJoint; -typedef struct cpPivotJoint cpPivotJoint; -typedef struct cpGrooveJoint cpGrooveJoint; -typedef struct cpDampedSpring cpDampedSpring; -typedef struct cpDampedRotarySpring cpDampedRotarySpring; -typedef struct cpRotaryLimitJoint cpRotaryLimitJoint; -typedef struct cpRatchetJoint cpRatchetJoint; -typedef struct cpGearJoint cpGearJoint; -typedef struct cpSimpleMotorJoint cpSimpleMotorJoint; - -typedef struct cpCollisionHandler cpCollisionHandler; -typedef struct cpContactPointSet cpContactPointSet; -typedef struct cpArbiter cpArbiter; - -typedef struct cpSpace cpSpace; - -#include "cpVect.h" -#include "cpBB.h" -#include "cpTransform.h" -#include "cpSpatialIndex.h" - -#include "cpArbiter.h" - -#include "cpBody.h" -#include "cpShape.h" -#include "cpPolyShape.h" - -#include "cpConstraint.h" - -#include "cpSpace.h" - -// patch me: axis link android required -#include "cpHastySpace.h" - -// Chipmunk 7.0.3 -#define CP_VERSION_MAJOR 7 -#define CP_VERSION_MINOR 0 -#define CP_VERSION_RELEASE 3 - -/// Version string. -CP_EXPORT extern const char *cpVersionString; - -/// Calculate the moment of inertia for a circle. -/// @c r1 and @c r2 are the inner and outer diameters. A solid circle has an inner diameter of 0. -CP_EXPORT cpFloat cpMomentForCircle(cpFloat m, cpFloat r1, cpFloat r2, cpVect offset); - -/// Calculate area of a hollow circle. -/// @c r1 and @c r2 are the inner and outer diameters. A solid circle has an inner diameter of 0. -CP_EXPORT cpFloat cpAreaForCircle(cpFloat r1, cpFloat r2); - -/// Calculate the moment of inertia for a line segment. -/// Beveling radius is not supported. -CP_EXPORT cpFloat cpMomentForSegment(cpFloat m, cpVect a, cpVect b, cpFloat radius); - -/// Calculate the area of a fattened (capsule shaped) line segment. -CP_EXPORT cpFloat cpAreaForSegment(cpVect a, cpVect b, cpFloat radius); - -/// Calculate the moment of inertia for a solid polygon shape assuming it's center of gravity is at it's centroid. The offset is added to each vertex. -CP_EXPORT cpFloat cpMomentForPoly(cpFloat m, int count, const cpVect *verts, cpVect offset, cpFloat radius); - -/// Calculate the signed area of a polygon. A Clockwise winding gives positive area. -/// This is probably backwards from what you expect, but matches Chipmunk's the winding for poly shapes. -CP_EXPORT cpFloat cpAreaForPoly(const int count, const cpVect *verts, cpFloat radius); - -/// Calculate the natural centroid of a polygon. -CP_EXPORT cpVect cpCentroidForPoly(const int count, const cpVect *verts); - -/// Calculate the moment of inertia for a solid box. -CP_EXPORT cpFloat cpMomentForBox(cpFloat m, cpFloat width, cpFloat height); - -/// Calculate the moment of inertia for a solid box. -CP_EXPORT cpFloat cpMomentForBox2(cpFloat m, cpBB box); - -/// Calculate the convex hull of a given set of points. Returns the count of points in the hull. -/// @c result must be a pointer to a @c cpVect array with at least @c count elements. If @c verts == @c result, then @c verts will be reduced inplace. -/// @c first is an optional pointer to an integer to store where the first vertex in the hull came from (i.e. verts[first] == result[0]) -/// @c tol is the allowed amount to shrink the hull when simplifying it. A tolerance of 0.0 creates an exact hull. -CP_EXPORT int cpConvexHull(int count, const cpVect *verts, cpVect *result, int *first, cpFloat tol); - -/// Convenience macro to work with cpConvexHull. -/// @c count and @c verts is the input array passed to cpConvexHull(). -/// @c count_var and @c verts_var are the names of the variables the macro creates to store the result. -/// The output vertex array is allocated on the stack using alloca() so it will be freed automatically, but cannot be returned from the current scope. -#define CP_CONVEX_HULL(__count__, __verts__, __count_var__, __verts_var__) \ -cpVect *__verts_var__ = (cpVect *)alloca(__count__*sizeof(cpVect)); \ -int __count_var__ = cpConvexHull(__count__, __verts__, __verts_var__, NULL, 0.0); \ - -/// Returns the closest point on the line segment ab, to the point p. -static inline cpVect -cpClosetPointOnSegment(const cpVect p, const cpVect a, const cpVect b) -{ - cpVect delta = cpvsub(a, b); - cpFloat t = cpfclamp01(cpvdot(delta, cpvsub(p, b))/cpvlengthsq(delta)); - return cpvadd(b, cpvmult(delta, t)); -} - -#if defined(__has_extension) -#if __has_extension(blocks) -// Define alternate block based alternatives for a few of the callback heavy functions. -// Collision handlers are post-step callbacks are not included to avoid memory management issues. -// If you want to use blocks for those and are aware of how to correctly manage the memory, the implementation is trivial. - -void cpSpaceEachBody_b(cpSpace *space, void (^block)(cpBody *body)); -void cpSpaceEachShape_b(cpSpace *space, void (^block)(cpShape *shape)); -void cpSpaceEachConstraint_b(cpSpace *space, void (^block)(cpConstraint *constraint)); - -void cpBodyEachShape_b(cpBody *body, void (^block)(cpShape *shape)); -void cpBodyEachConstraint_b(cpBody *body, void (^block)(cpConstraint *constraint)); -void cpBodyEachArbiter_b(cpBody *body, void (^block)(cpArbiter *arbiter)); - -typedef void (^cpSpacePointQueryBlock)(cpShape *shape, cpVect point, cpFloat distance, cpVect gradient); -void cpSpacePointQuery_b(cpSpace *space, cpVect point, cpFloat maxDistance, cpShapeFilter filter, cpSpacePointQueryBlock block); - -typedef void (^cpSpaceSegmentQueryBlock)(cpShape *shape, cpVect point, cpVect normal, cpFloat alpha); -void cpSpaceSegmentQuery_b(cpSpace *space, cpVect start, cpVect end, cpFloat radius, cpShapeFilter filter, cpSpaceSegmentQueryBlock block); - -typedef void (^cpSpaceBBQueryBlock)(cpShape *shape); -void cpSpaceBBQuery_b(cpSpace *space, cpBB bb, cpShapeFilter filter, cpSpaceBBQueryBlock block); - -typedef void (^cpSpaceShapeQueryBlock)(cpShape *shape, cpContactPointSet *points); -cpBool cpSpaceShapeQuery_b(cpSpace *space, cpShape *shape, cpSpaceShapeQueryBlock block); - -#endif -#endif - - -//@} - -#ifdef __cplusplus -} - -static inline cpVect operator *(const cpVect v, const cpFloat s){return cpvmult(v, s);} -static inline cpVect operator +(const cpVect v1, const cpVect v2){return cpvadd(v1, v2);} -static inline cpVect operator -(const cpVect v1, const cpVect v2){return cpvsub(v1, v2);} -static inline cpBool operator ==(const cpVect v1, const cpVect v2){return cpveql(v1, v2);} -static inline cpVect operator -(const cpVect v){return cpvneg(v);} - -#endif -#endif diff --git a/3rdparty/chipmunk/include/chipmunk/chipmunk_ffi.h b/3rdparty/chipmunk/include/chipmunk/chipmunk_ffi.h deleted file mode 100644 index 86e3d9fcd6fd..000000000000 --- a/3rdparty/chipmunk/include/chipmunk/chipmunk_ffi.h +++ /dev/null @@ -1,105 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifdef CHIPMUNK_FFI - -// Create non static inlined copies of Chipmunk functions, useful for working with dynamic FFIs -// For many languages, it may be faster to reimplement these functions natively instead. -// Note: This file should only be included by chipmunk.c. - -#ifdef _MSC_VER - #if _MSC_VER >= 1600 - #define MAKE_REF(name) CP_EXPORT decltype(name) *_##name = name - #else - #define MAKE_REF(name) - #endif -#else - #define MAKE_REF(name) __typeof__(name) *_##name = name -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -MAKE_REF(cpv); // makes a variable named _cpv that contains the function pointer for cpv() -MAKE_REF(cpveql); -MAKE_REF(cpvadd); -MAKE_REF(cpvneg); -MAKE_REF(cpvsub); -MAKE_REF(cpvmult); -MAKE_REF(cpvdot); -MAKE_REF(cpvcross); -MAKE_REF(cpvperp); -MAKE_REF(cpvrperp); -MAKE_REF(cpvproject); -MAKE_REF(cpvforangle); -MAKE_REF(cpvtoangle); -MAKE_REF(cpvrotate); -MAKE_REF(cpvunrotate); -MAKE_REF(cpvlengthsq); -MAKE_REF(cpvlength); -MAKE_REF(cpvlerp); -MAKE_REF(cpvnormalize); -MAKE_REF(cpvclamp); -MAKE_REF(cpvlerpconst); -MAKE_REF(cpvdist); -MAKE_REF(cpvdistsq); -MAKE_REF(cpvnear); - -MAKE_REF(cpfmax); -MAKE_REF(cpfmin); -MAKE_REF(cpfabs); -MAKE_REF(cpfclamp); -MAKE_REF(cpflerp); -MAKE_REF(cpflerpconst); - -MAKE_REF(cpBBNew); -MAKE_REF(cpBBNewForExtents); -MAKE_REF(cpBBNewForCircle); -MAKE_REF(cpBBIntersects); -MAKE_REF(cpBBContainsBB); -MAKE_REF(cpBBContainsVect); -MAKE_REF(cpBBMerge); -MAKE_REF(cpBBExpand); -MAKE_REF(cpBBCenter); -MAKE_REF(cpBBArea); -MAKE_REF(cpBBMergedArea); -MAKE_REF(cpBBSegmentQuery); -MAKE_REF(cpBBIntersectsSegment); -MAKE_REF(cpBBClampVect); - -MAKE_REF(cpSpatialIndexDestroy); -MAKE_REF(cpSpatialIndexCount); -MAKE_REF(cpSpatialIndexEach); -MAKE_REF(cpSpatialIndexContains); -MAKE_REF(cpSpatialIndexInsert); -MAKE_REF(cpSpatialIndexRemove); -MAKE_REF(cpSpatialIndexReindex); -MAKE_REF(cpSpatialIndexReindexObject); -MAKE_REF(cpSpatialIndexSegmentQuery); -MAKE_REF(cpSpatialIndexQuery); -MAKE_REF(cpSpatialIndexReindexQuery); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/3rdparty/chipmunk/include/chipmunk/chipmunk_private.h b/3rdparty/chipmunk/include/chipmunk/chipmunk_private.h deleted file mode 100644 index e606ba16c0e6..000000000000 --- a/3rdparty/chipmunk/include/chipmunk/chipmunk_private.h +++ /dev/null @@ -1,344 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef CHIPMUNK_PRIVATE_H -#define CHIPMUNK_PRIVATE_H - -#include "chipmunk/chipmunk.h" -#include "chipmunk/chipmunk_structs.h" - -#define CP_HASH_COEF (3344921057ul) -#define CP_HASH_PAIR(A, B) ((cpHashValue)(A)*CP_HASH_COEF ^ (cpHashValue)(B)*CP_HASH_COEF) - -// TODO: Eww. Magic numbers. -#define MAGIC_EPSILON 1e-5 - - -//MARK: cpArray - -cpArray *cpArrayNew(int size); - -void cpArrayFree(cpArray *arr); - -void cpArrayPush(cpArray *arr, void *object); -void *cpArrayPop(cpArray *arr); -void cpArrayDeleteObj(cpArray *arr, void *obj); -cpBool cpArrayContains(cpArray *arr, void *ptr); - -void cpArrayFreeEach(cpArray *arr, void (freeFunc)(void*)); - - -//MARK: cpHashSet - -typedef cpBool (*cpHashSetEqlFunc)(const void *ptr, const void *elt); -typedef void *(*cpHashSetTransFunc)(const void *ptr, void *data); - -cpHashSet *cpHashSetNew(int size, cpHashSetEqlFunc eqlFunc); -void cpHashSetSetDefaultValue(cpHashSet *set, void *default_value); - -void cpHashSetFree(cpHashSet *set); - -int cpHashSetCount(cpHashSet *set); -const void *cpHashSetInsert(cpHashSet *set, cpHashValue hash, const void *ptr, cpHashSetTransFunc trans, void *data); -const void *cpHashSetRemove(cpHashSet *set, cpHashValue hash, const void *ptr); -const void *cpHashSetFind(cpHashSet *set, cpHashValue hash, const void *ptr); - -typedef void (*cpHashSetIteratorFunc)(void *elt, void *data); -void cpHashSetEach(cpHashSet *set, cpHashSetIteratorFunc func, void *data); - -typedef cpBool (*cpHashSetFilterFunc)(void *elt, void *data); -void cpHashSetFilter(cpHashSet *set, cpHashSetFilterFunc func, void *data); - - -//MARK: Bodies - -void cpBodyAddShape(cpBody *body, cpShape *shape); -void cpBodyRemoveShape(cpBody *body, cpShape *shape); - -//void cpBodyAccumulateMassForShape(cpBody *body, cpShape *shape); -void cpBodyAccumulateMassFromShapes(cpBody *body); - -void cpBodyRemoveConstraint(cpBody *body, cpConstraint *constraint); - - -//MARK: Spatial Index Functions - -cpSpatialIndex *cpSpatialIndexInit(cpSpatialIndex *index, cpSpatialIndexClass *klass, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex); - - -//MARK: Arbiters - -cpArbiter* cpArbiterInit(cpArbiter *arb, cpShape *a, cpShape *b); - -static inline struct cpArbiterThread * -cpArbiterThreadForBody(cpArbiter *arb, cpBody *body) -{ - return (arb->body_a == body ? &arb->thread_a : &arb->thread_b); -} - -void cpArbiterUnthread(cpArbiter *arb); - -void cpArbiterUpdate(cpArbiter *arb, struct cpCollisionInfo *info, cpSpace *space); -void cpArbiterPreStep(cpArbiter *arb, cpFloat dt, cpFloat bias, cpFloat slop); -void cpArbiterApplyCachedImpulse(cpArbiter *arb, cpFloat dt_coef); -void cpArbiterApplyImpulse(cpArbiter *arb); - - -//MARK: Shapes/Collisions - -cpShape *cpShapeInit(cpShape *shape, const cpShapeClass *klass, cpBody *body, struct cpShapeMassInfo massInfo); - -static inline cpBool -cpShapeActive(cpShape *shape) -{ - // checks if the shape is added to a shape list. - // TODO could this just check the space now? - return (shape->prev || (shape->body && shape->body->shapeList == shape)); -} - -// Note: This function returns contact points with r1/r2 in absolute coordinates, not body relative. -struct cpCollisionInfo cpCollide(const cpShape *a, const cpShape *b, cpCollisionID id, struct cpContact *contacts); - -static inline void -CircleSegmentQuery(cpShape *shape, cpVect center, cpFloat r1, cpVect a, cpVect b, cpFloat r2, cpSegmentQueryInfo *info) -{ - cpVect da = cpvsub(a, center); - cpVect db = cpvsub(b, center); - cpFloat rsum = r1 + r2; - - cpFloat qa = cpvdot(da, da) - 2.0f*cpvdot(da, db) + cpvdot(db, db); - cpFloat qb = cpvdot(da, db) - cpvdot(da, da); - cpFloat det = qb*qb - qa*(cpvdot(da, da) - rsum*rsum); - - if(det >= 0.0f){ - cpFloat t = (-qb - cpfsqrt(det))/(qa); - if(0.0f<= t && t <= 1.0f){ - cpVect n = cpvnormalize(cpvlerp(da, db, t)); - - info->shape = shape; - info->point = cpvsub(cpvlerp(a, b, t), cpvmult(n, r2)); - info->normal = n; - info->alpha = t; - } - } -} - -static inline cpBool -cpShapeFilterReject(cpShapeFilter a, cpShapeFilter b) -{ - // Reject the collision if: - return ( - // They are in the same non-zero group. - (a.group != 0 && a.group == b.group) || - // One of the category/mask combinations fails. - (a.categories & b.mask) == 0 || - (b.categories & a.mask) == 0 - ); -} - -void cpLoopIndexes(const cpVect *verts, int count, int *start, int *end); - - -//MARK: Constraints -// TODO naming conventions here - -void cpConstraintInit(cpConstraint *constraint, const struct cpConstraintClass *klass, cpBody *a, cpBody *b); - -static inline void -cpConstraintActivateBodies(cpConstraint *constraint) -{ - cpBody *a = constraint->a; cpBodyActivate(a); - cpBody *b = constraint->b; cpBodyActivate(b); -} - -static inline cpVect -relative_velocity(cpBody *a, cpBody *b, cpVect r1, cpVect r2){ - cpVect v1_sum = cpvadd(a->v, cpvmult(cpvperp(r1), a->w)); - cpVect v2_sum = cpvadd(b->v, cpvmult(cpvperp(r2), b->w)); - - return cpvsub(v2_sum, v1_sum); -} - -static inline cpFloat -normal_relative_velocity(cpBody *a, cpBody *b, cpVect r1, cpVect r2, cpVect n){ - return cpvdot(relative_velocity(a, b, r1, r2), n); -} - -static inline void -apply_impulse(cpBody *body, cpVect j, cpVect r){ - body->v = cpvadd(body->v, cpvmult(j, body->m_inv)); - body->w += body->i_inv*cpvcross(r, j); -} - -static inline void -apply_impulses(cpBody *a , cpBody *b, cpVect r1, cpVect r2, cpVect j) -{ - apply_impulse(a, cpvneg(j), r1); - apply_impulse(b, j, r2); -} - -static inline void -apply_bias_impulse(cpBody *body, cpVect j, cpVect r) -{ - body->v_bias = cpvadd(body->v_bias, cpvmult(j, body->m_inv)); - body->w_bias += body->i_inv*cpvcross(r, j); -} - -static inline void -apply_bias_impulses(cpBody *a , cpBody *b, cpVect r1, cpVect r2, cpVect j) -{ - apply_bias_impulse(a, cpvneg(j), r1); - apply_bias_impulse(b, j, r2); -} - -static inline cpFloat -k_scalar_body(cpBody *body, cpVect r, cpVect n) -{ - cpFloat rcn = cpvcross(r, n); - return body->m_inv + body->i_inv*rcn*rcn; -} - -static inline cpFloat -k_scalar(cpBody *a, cpBody *b, cpVect r1, cpVect r2, cpVect n) -{ - cpFloat value = k_scalar_body(a, r1, n) + k_scalar_body(b, r2, n); - cpAssertSoft(value != 0.0, "Unsolvable collision or constraint."); - - return value; -} - -static inline cpMat2x2 -k_tensor(cpBody *a, cpBody *b, cpVect r1, cpVect r2) -{ - cpFloat m_sum = a->m_inv + b->m_inv; - - // start with Identity*m_sum - cpFloat k11 = m_sum, k12 = 0.0f; - cpFloat k21 = 0.0f, k22 = m_sum; - - // add the influence from r1 - cpFloat a_i_inv = a->i_inv; - cpFloat r1xsq = r1.x * r1.x * a_i_inv; - cpFloat r1ysq = r1.y * r1.y * a_i_inv; - cpFloat r1nxy = -r1.x * r1.y * a_i_inv; - k11 += r1ysq; k12 += r1nxy; - k21 += r1nxy; k22 += r1xsq; - - // add the influnce from r2 - cpFloat b_i_inv = b->i_inv; - cpFloat r2xsq = r2.x * r2.x * b_i_inv; - cpFloat r2ysq = r2.y * r2.y * b_i_inv; - cpFloat r2nxy = -r2.x * r2.y * b_i_inv; - k11 += r2ysq; k12 += r2nxy; - k21 += r2nxy; k22 += r2xsq; - - // invert - cpFloat det = k11*k22 - k12*k21; - cpAssertSoft(det != 0.0, "Unsolvable constraint."); - - cpFloat det_inv = 1.0f/det; - return cpMat2x2New( - k22*det_inv, -k12*det_inv, - -k21*det_inv, k11*det_inv - ); -} - -static inline cpFloat -bias_coef(cpFloat errorBias, cpFloat dt) -{ - return 1.0f - cpfpow(errorBias, dt); -} - - -//MARK: Spaces - -#define cpAssertSpaceUnlocked(space) \ - cpAssertHard(!space->locked, \ - "This operation cannot be done safely during a call to cpSpaceStep() or during a query. " \ - "Put these calls into a post-step callback." \ - ); - -void cpSpaceSetStaticBody(cpSpace *space, cpBody *body); - -extern cpCollisionHandler cpCollisionHandlerDoNothing; - -void cpSpaceProcessComponents(cpSpace *space, cpFloat dt); - -void cpSpacePushFreshContactBuffer(cpSpace *space); -struct cpContact *cpContactBufferGetArray(cpSpace *space); -void cpSpacePushContacts(cpSpace *space, int count); - -cpPostStepCallback *cpSpaceGetPostStepCallback(cpSpace *space, void *key); - -cpBool cpSpaceArbiterSetFilter(cpArbiter *arb, cpSpace *space); -void cpSpaceFilterArbiters(cpSpace *space, cpBody *body, cpShape *filter); - -void cpSpaceActivateBody(cpSpace *space, cpBody *body); -void cpSpaceLock(cpSpace *space); -void cpSpaceUnlock(cpSpace *space, cpBool runPostStep); - -static inline void -cpSpaceUncacheArbiter(cpSpace *space, cpArbiter *arb) -{ - const cpShape *a = arb->a, *b = arb->b; - const cpShape *shape_pair[] = {a, b}; - cpHashValue arbHashID = CP_HASH_PAIR((cpHashValue)a, (cpHashValue)b); - cpHashSetRemove(space->cachedArbiters, arbHashID, shape_pair); - cpArrayDeleteObj(space->arbiters, arb); -} - -static inline cpArray * -cpSpaceArrayForBodyType(cpSpace *space, cpBodyType type) -{ - return (type == CP_BODY_TYPE_STATIC ? space->staticBodies : space->dynamicBodies); -} - -void cpShapeUpdateFunc(cpShape *shape, void *unused); -cpCollisionID cpSpaceCollideShapes(cpShape *a, cpShape *b, cpCollisionID id, cpSpace *space); - - -//MARK: Foreach loops - -static inline cpConstraint * -cpConstraintNext(cpConstraint *node, cpBody *body) -{ - return (node->a == body ? node->next_a : node->next_b); -} - -#define CP_BODY_FOREACH_CONSTRAINT(bdy, var)\ - for(cpConstraint *var = bdy->constraintList; var; var = cpConstraintNext(var, bdy)) - -static inline cpArbiter * -cpArbiterNext(cpArbiter *node, cpBody *body) -{ - return (node->body_a == body ? node->thread_a.next : node->thread_b.next); -} - -#define CP_BODY_FOREACH_ARBITER(bdy, var)\ - for(cpArbiter *var = bdy->arbiterList; var; var = cpArbiterNext(var, bdy)) - -#define CP_BODY_FOREACH_SHAPE(body, var)\ - for(cpShape *var = body->shapeList; var; var = var->next) - -#define CP_BODY_FOREACH_COMPONENT(root, var)\ - for(cpBody *var = root; var; var = var->sleeping.next) - -#endif diff --git a/3rdparty/chipmunk/include/chipmunk/chipmunk_structs.h b/3rdparty/chipmunk/include/chipmunk/chipmunk_structs.h deleted file mode 100644 index 1485795d4e97..000000000000 --- a/3rdparty/chipmunk/include/chipmunk/chipmunk_structs.h +++ /dev/null @@ -1,450 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -// All of the struct definitions for Chipmunk should be considered part of the private API. -// However, it is very valuable to know the struct sizes for preallocating memory. - -#ifndef CHIPMUNK_STRUCTS_H -#define CHIPMUNK_STRUCTS_H - -#include "chipmunk/chipmunk.h" - -struct cpArray { - int num, max; - void **arr; -}; - -struct cpBody { - // Integration functions - cpBodyVelocityFunc velocity_func; - cpBodyPositionFunc position_func; - - // mass and it's inverse - cpFloat m; - cpFloat m_inv; - - // moment of inertia and it's inverse - cpFloat i; - cpFloat i_inv; - - // center of gravity - cpVect cog; - - // position, velocity, force - cpVect p; - cpVect v; - cpVect f; - - // Angle, angular velocity, torque (radians) - cpFloat a; - cpFloat w; - cpFloat t; - - cpTransform transform; - - cpDataPointer userData; - - // "pseudo-velocities" used for eliminating overlap. - // Erin Catto has some papers that talk about what these are. - cpVect v_bias; - cpFloat w_bias; - - cpSpace *space; - - cpShape *shapeList; - cpArbiter *arbiterList; - cpConstraint *constraintList; - - struct { - cpBody *root; - cpBody *next; - cpFloat idleTime; - } sleeping; -}; - -enum cpArbiterState { - // Arbiter is active and its the first collision. - CP_ARBITER_STATE_FIRST_COLLISION, - // Arbiter is active and its not the first collision. - CP_ARBITER_STATE_NORMAL, - // Collision has been explicitly ignored. - // Either by returning false from a begin collision handler or calling cpArbiterIgnore(). - CP_ARBITER_STATE_IGNORE, - // Collison is no longer active. A space will cache an arbiter for up to cpSpace.collisionPersistence more steps. - CP_ARBITER_STATE_CACHED, - // Collison arbiter is invalid because one of the shapes was removed. - CP_ARBITER_STATE_INVALIDATED, -}; - -struct cpArbiterThread { - struct cpArbiter *next, *prev; -}; - -struct cpContact { - cpVect r1, r2; - - cpFloat nMass, tMass; - cpFloat bounce; // TODO: look for an alternate bounce solution. - - cpFloat jnAcc, jtAcc, jBias; - cpFloat bias; - - cpHashValue hash; -}; - -struct cpCollisionInfo { - const cpShape *a, *b; - cpCollisionID id; - - cpVect n; - - int count; - // TODO Should this be a unique struct type? - struct cpContact *arr; -}; - -struct cpArbiter { - cpFloat e; - cpFloat u; - cpVect surface_vr; - - cpDataPointer data; - - const cpShape *a, *b; - cpBody *body_a, *body_b; - struct cpArbiterThread thread_a, thread_b; - - int count; - struct cpContact *contacts; - cpVect n; - - // Regular, wildcard A and wildcard B collision handlers. - cpCollisionHandler *handler, *handlerA, *handlerB; - cpBool swapped; - - cpTimestamp stamp; - enum cpArbiterState state; -}; - -struct cpShapeMassInfo { - cpFloat m; - cpFloat i; - cpVect cog; - cpFloat area; -}; - -typedef enum cpShapeType{ - CP_CIRCLE_SHAPE, - CP_SEGMENT_SHAPE, - CP_POLY_SHAPE, - CP_NUM_SHAPES -} cpShapeType; - -typedef cpBB (*cpShapeCacheDataImpl)(cpShape *shape, cpTransform transform); -typedef void (*cpShapeDestroyImpl)(cpShape *shape); -typedef void (*cpShapePointQueryImpl)(const cpShape *shape, cpVect p, cpPointQueryInfo *info); -typedef void (*cpShapeSegmentQueryImpl)(const cpShape *shape, cpVect a, cpVect b, cpFloat radius, cpSegmentQueryInfo *info); - -typedef struct cpShapeClass cpShapeClass; - -struct cpShapeClass { - cpShapeType type; - - cpShapeCacheDataImpl cacheData; - cpShapeDestroyImpl destroy; - cpShapePointQueryImpl pointQuery; - cpShapeSegmentQueryImpl segmentQuery; -}; - -struct cpShape { - const cpShapeClass *klass; - - cpSpace *space; - cpBody *body; - struct cpShapeMassInfo massInfo; - cpBB bb; - - cpBool sensor; - - cpFloat e; - cpFloat u; - cpVect surfaceV; - - cpDataPointer userData; - - cpCollisionType type; - cpShapeFilter filter; - - cpShape *next; - cpShape *prev; - - cpHashValue hashid; -}; - -struct cpCircleShape { - cpShape shape; - - cpVect c, tc; - cpFloat r; -}; - -struct cpSegmentShape { - cpShape shape; - - cpVect a, b, n; - cpVect ta, tb, tn; - cpFloat r; - - cpVect a_tangent, b_tangent; -}; - -struct cpSplittingPlane { - cpVect v0, n; -}; - -#define CP_POLY_SHAPE_INLINE_ALLOC 6 - -struct cpPolyShape { - cpShape shape; - - cpFloat r; - - int count; - // The untransformed planes are appended at the end of the transformed planes. - struct cpSplittingPlane *planes; - - // Allocate a small number of splitting planes internally for simple poly. - struct cpSplittingPlane _planes[2*CP_POLY_SHAPE_INLINE_ALLOC]; -}; - -typedef void (*cpConstraintPreStepImpl)(cpConstraint *constraint, cpFloat dt); -typedef void (*cpConstraintApplyCachedImpulseImpl)(cpConstraint *constraint, cpFloat dt_coef); -typedef void (*cpConstraintApplyImpulseImpl)(cpConstraint *constraint, cpFloat dt); -typedef cpFloat (*cpConstraintGetImpulseImpl)(cpConstraint *constraint); - -typedef struct cpConstraintClass { - cpConstraintPreStepImpl preStep; - cpConstraintApplyCachedImpulseImpl applyCachedImpulse; - cpConstraintApplyImpulseImpl applyImpulse; - cpConstraintGetImpulseImpl getImpulse; -} cpConstraintClass; - -struct cpConstraint { - const cpConstraintClass *klass; - - cpSpace *space; - - cpBody *a, *b; - cpConstraint *next_a, *next_b; - - cpFloat maxForce; - cpFloat errorBias; - cpFloat maxBias; - - cpBool collideBodies; - - cpConstraintPreSolveFunc preSolve; - cpConstraintPostSolveFunc postSolve; - - cpDataPointer userData; -}; - -struct cpPinJoint { - cpConstraint constraint; - cpVect anchorA, anchorB; - cpFloat dist; - - cpVect r1, r2; - cpVect n; - cpFloat nMass; - - cpFloat jnAcc; - cpFloat bias; -}; - -struct cpSlideJoint { - cpConstraint constraint; - cpVect anchorA, anchorB; - cpFloat min, max; - - cpVect r1, r2; - cpVect n; - cpFloat nMass; - - cpFloat jnAcc; - cpFloat bias; -}; - -struct cpPivotJoint { - cpConstraint constraint; - cpVect anchorA, anchorB; - - cpVect r1, r2; - cpMat2x2 k; - - cpVect jAcc; - cpVect bias; -}; - -struct cpGrooveJoint { - cpConstraint constraint; - cpVect grv_n, grv_a, grv_b; - cpVect anchorB; - - cpVect grv_tn; - cpFloat clamp; - cpVect r1, r2; - cpMat2x2 k; - - cpVect jAcc; - cpVect bias; -}; - -struct cpDampedSpring { - cpConstraint constraint; - cpVect anchorA, anchorB; - cpFloat restLength; - cpFloat stiffness; - cpFloat damping; - cpDampedSpringForceFunc springForceFunc; - - cpFloat target_vrn; - cpFloat v_coef; - - cpVect r1, r2; - cpFloat nMass; - cpVect n; - - cpFloat jAcc; -}; - -struct cpDampedRotarySpring { - cpConstraint constraint; - cpFloat restAngle; - cpFloat stiffness; - cpFloat damping; - cpDampedRotarySpringTorqueFunc springTorqueFunc; - - cpFloat target_wrn; - cpFloat w_coef; - - cpFloat iSum; - cpFloat jAcc; -}; - -struct cpRotaryLimitJoint { - cpConstraint constraint; - cpFloat min, max; - - cpFloat iSum; - - cpFloat bias; - cpFloat jAcc; -}; - -struct cpRatchetJoint { - cpConstraint constraint; - cpFloat angle, phase, ratchet; - - cpFloat iSum; - - cpFloat bias; - cpFloat jAcc; -}; - -struct cpGearJoint { - cpConstraint constraint; - cpFloat phase, ratio; - cpFloat ratio_inv; - - cpFloat iSum; - - cpFloat bias; - cpFloat jAcc; -}; - -struct cpSimpleMotor { - cpConstraint constraint; - cpFloat rate; - - cpFloat iSum; - - cpFloat jAcc; -}; - -typedef struct cpContactBufferHeader cpContactBufferHeader; -typedef void (*cpSpaceArbiterApplyImpulseFunc)(cpArbiter *arb); - -struct cpSpace { - int iterations; - - cpVect gravity; - cpFloat damping; - - cpFloat idleSpeedThreshold; - cpFloat sleepTimeThreshold; - - cpFloat collisionSlop; - cpFloat collisionBias; - cpTimestamp collisionPersistence; - - cpDataPointer userData; - - cpTimestamp stamp; - cpFloat curr_dt; - - cpArray *dynamicBodies; - cpArray *staticBodies; - cpArray *rousedBodies; - cpArray *sleepingComponents; - - cpHashValue shapeIDCounter; - cpSpatialIndex *staticShapes; - cpSpatialIndex *dynamicShapes; - - cpArray *constraints; - - cpArray *arbiters; - cpContactBufferHeader *contactBuffersHead; - cpHashSet *cachedArbiters; - cpArray *pooledArbiters; - - cpArray *allocatedBuffers; - int locked; - - cpBool usesWildcards; - cpHashSet *collisionHandlers; - cpCollisionHandler defaultHandler; - - cpBool skipPostStep; - cpArray *postStepCallbacks; - - cpBody *staticBody; - cpBody _staticBody; -}; - -typedef struct cpPostStepCallback { - cpPostStepFunc func; - void *key; - void *data; -} cpPostStepCallback; - -#endif diff --git a/3rdparty/chipmunk/include/chipmunk/chipmunk_types.h b/3rdparty/chipmunk/include/chipmunk/chipmunk_types.h deleted file mode 100644 index 285e2c41b1e6..000000000000 --- a/3rdparty/chipmunk/include/chipmunk/chipmunk_types.h +++ /dev/null @@ -1,272 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef CHIPMUNK_TYPES_H -#define CHIPMUNK_TYPES_H - -// EGNX use float precision, so disable chipmunk double use. -#define CP_USE_CGTYPES 0 -#define CP_USE_DOUBLES 0 - -#include -#include -#include - -#ifdef __APPLE__ - #include "TargetConditionals.h" -#endif - -// Use CGTypes by default on iOS and Mac. -// Also enables usage of doubles on 64 bit. -// Performance is usually very comparable when the CPU cache is well utilised. -#if (TARGET_OS_IPHONE || TARGET_OS_MAC) && (!defined CP_USE_CGTYPES) - #define CP_USE_CGTYPES 1 -#endif - -#if CP_USE_CGTYPES - #if TARGET_OS_IPHONE - #include - #include - #elif TARGET_OS_MAC - #include - #endif - - #if defined(__LP64__) && __LP64__ - #define CP_USE_DOUBLES 1 - #else - #define CP_USE_DOUBLES 0 - #endif -#endif - -#ifndef CP_USE_DOUBLES - // Use doubles by default for higher precision. - #define CP_USE_DOUBLES 1 -#endif - -/// @defgroup basicTypes Basic Types -/// Most of these types can be configured at compile time. -/// @{ - -#if CP_USE_DOUBLES -/// Chipmunk's floating point type. -/// Can be reconfigured at compile time. - typedef double cpFloat; - #define cpfsqrt sqrt - #define cpfsin sin - #define cpfcos cos - #define cpfacos acos - #define cpfatan2 atan2 - #define cpfmod fmod - #define cpfexp exp - #define cpfpow pow - #define cpffloor floor - #define cpfceil ceil - #define CPFLOAT_MIN DBL_MIN -#else - typedef float cpFloat; - #define cpfsqrt sqrtf - #define cpfsin sinf - #define cpfcos cosf - #define cpfacos acosf - #define cpfatan2 atan2f - #define cpfmod fmodf - #define cpfexp expf - #define cpfpow powf - #define cpffloor floorf - #define cpfceil ceilf - #define CPFLOAT_MIN FLT_MIN -#endif - -#ifndef INFINITY - #ifdef _MSC_VER - union MSVC_EVIL_FLOAT_HACK - { - unsigned __int8 Bytes[4]; - float Value; - }; - static union MSVC_EVIL_FLOAT_HACK INFINITY_HACK = {{0x00, 0x00, 0x80, 0x7F}}; - #define INFINITY (INFINITY_HACK.Value) - #endif - - #ifdef __GNUC__ - #define INFINITY (__builtin_inf()) - #endif - - #ifndef INFINITY - #define INFINITY (1e1000) - #endif -#endif - - -#define CP_PI ((cpFloat)3.14159265358979323846264338327950288) - - -/// Return the max of two cpFloats. -static inline cpFloat cpfmax(cpFloat a, cpFloat b) -{ - return (a > b) ? a : b; -} - -/// Return the min of two cpFloats. -static inline cpFloat cpfmin(cpFloat a, cpFloat b) -{ - return (a < b) ? a : b; -} - -/// Return the absolute value of a cpFloat. -static inline cpFloat cpfabs(cpFloat f) -{ - return (f < 0) ? -f : f; -} - -/// Clamp @c f to be between @c min and @c max. -static inline cpFloat cpfclamp(cpFloat f, cpFloat min, cpFloat max) -{ - return cpfmin(cpfmax(f, min), max); -} - -/// Clamp @c f to be between 0 and 1. -static inline cpFloat cpfclamp01(cpFloat f) -{ - return cpfmax(0.0f, cpfmin(f, 1.0f)); -} - - - -/// Linearly interpolate (or extrapolate) between @c f1 and @c f2 by @c t percent. -static inline cpFloat cpflerp(cpFloat f1, cpFloat f2, cpFloat t) -{ - return f1*(1.0f - t) + f2*t; -} - -/// Linearly interpolate from @c f1 to @c f2 by no more than @c d. -static inline cpFloat cpflerpconst(cpFloat f1, cpFloat f2, cpFloat d) -{ - return f1 + cpfclamp(f2 - f1, -d, d); -} - -/// Hash value type. -#ifdef CP_HASH_VALUE_TYPE - typedef CP_HASH_VALUE_TYPE cpHashValue; -#else - typedef uintptr_t cpHashValue; -#endif - -/// Type used internally to cache colliding object info for cpCollideShapes(). -/// Should be at least 32 bits. -typedef uint32_t cpCollisionID; - -// Oh C, how we love to define our own boolean types to get compiler compatibility -/// Chipmunk's boolean type. -#ifdef CP_BOOL_TYPE - typedef CP_BOOL_TYPE cpBool; -#else - typedef unsigned char cpBool; -#endif - -#ifndef cpTrue -/// true value. - #define cpTrue 1 -#endif - -#ifndef cpFalse -/// false value. - #define cpFalse 0 -#endif - -#ifdef CP_DATA_POINTER_TYPE - typedef CP_DATA_POINTER_TYPE cpDataPointer; -#else -/// Type used for user data pointers. - typedef void * cpDataPointer; -#endif - -#ifdef CP_COLLISION_TYPE_TYPE - typedef CP_COLLISION_TYPE_TYPE cpCollisionType; -#else -/// Type used for cpSpace.collision_type. - typedef uintptr_t cpCollisionType; -#endif - -#ifdef CP_GROUP_TYPE - typedef CP_GROUP_TYPE cpGroup; -#else -/// Type used for cpShape.group. - typedef uintptr_t cpGroup; -#endif - -#ifdef CP_BITMASK_TYPE - typedef CP_BITMASK_TYPE cpBitmask; -#else -/// Type used for cpShapeFilter category and mask. - typedef unsigned int cpBitmask; -#endif - -#ifdef CP_TIMESTAMP_TYPE - typedef CP_TIMESTAMP_TYPE cpTimestamp; -#else -/// Type used for various timestamps in Chipmunk. - typedef unsigned int cpTimestamp; -#endif - -#ifndef CP_NO_GROUP -/// Value for cpShape.group signifying that a shape is in no group. - #define CP_NO_GROUP ((cpGroup)0) -#endif - -#ifndef CP_ALL_CATEGORIES -/// Value for cpShape.layers signifying that a shape is in every layer. - #define CP_ALL_CATEGORIES (~(cpBitmask)0) -#endif - -#ifndef CP_WILDCARD_COLLISION_TYPE -/// cpCollisionType value internally reserved for hashing wildcard handlers. - #define CP_WILDCARD_COLLISION_TYPE (~(cpCollisionType)0) -#endif - -/// @} - -// CGPoints are structurally the same, and allow -// easy interoperability with other Cocoa libraries -#if CP_USE_CGTYPES - typedef CGPoint cpVect; -#else -/// Chipmunk's 2D vector type. -/// @addtogroup cpVect - typedef struct cpVect{cpFloat x,y;} cpVect; -#endif - -#if CP_USE_CGTYPES - typedef CGAffineTransform cpTransform; -#else - /// Column major affine transform. - typedef struct cpTransform { - cpFloat a, b, c, d, tx, ty; - } cpTransform; -#endif - -// NUKE -typedef struct cpMat2x2 { - // Row major [[a, b][c d]] - cpFloat a, b, c, d; -} cpMat2x2; - -#endif diff --git a/3rdparty/chipmunk/include/chipmunk/chipmunk_unsafe.h b/3rdparty/chipmunk/include/chipmunk/chipmunk_unsafe.h deleted file mode 100644 index 990bd012af45..000000000000 --- a/3rdparty/chipmunk/include/chipmunk/chipmunk_unsafe.h +++ /dev/null @@ -1,66 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* This header defines a number of "unsafe" operations on Chipmunk objects. - * In this case "unsafe" is referring to operations which may reduce the - * physical accuracy or numerical stability of the simulation, but will not - * cause crashes. - * - * The prime example is mutating collision shapes. Chipmunk does not support - * this directly. Mutating shapes using this API will caused objects in contact - * to be pushed apart using Chipmunk's overlap solver, but not using real - * persistent velocities. Probably not what you meant, but perhaps close enough. - */ - -/// @defgroup unsafe Chipmunk Unsafe Shape Operations -/// These functions are used for mutating collision shapes. -/// Chipmunk does not have any way to get velocity information on changing shapes, -/// so the results will be unrealistic. You must explicity include the chipmunk_unsafe.h header to use them. -/// @{ - -#ifndef CHIPMUNK_UNSAFE_H -#define CHIPMUNK_UNSAFE_H - -#ifdef __cplusplus -extern "C" { -#endif - -/// Set the radius of a circle shape. -CP_EXPORT void cpCircleShapeSetRadius(cpShape *shape, cpFloat radius); -/// Set the offset of a circle shape. -CP_EXPORT void cpCircleShapeSetOffset(cpShape *shape, cpVect offset); - -/// Set the endpoints of a segment shape. -CP_EXPORT void cpSegmentShapeSetEndpoints(cpShape *shape, cpVect a, cpVect b); -/// Set the radius of a segment shape. -CP_EXPORT void cpSegmentShapeSetRadius(cpShape *shape, cpFloat radius); - -/// Set the vertexes of a poly shape. -CP_EXPORT void cpPolyShapeSetVerts(cpShape *shape, int count, cpVect *verts, cpTransform transform); -CP_EXPORT void cpPolyShapeSetVertsRaw(cpShape *shape, int count, cpVect *verts); -/// Set the radius of a poly shape. -CP_EXPORT void cpPolyShapeSetRadius(cpShape *shape, cpFloat radius); - -#ifdef __cplusplus -} -#endif -#endif -/// @} diff --git a/3rdparty/chipmunk/include/chipmunk/cpArbiter.h b/3rdparty/chipmunk/include/chipmunk/cpArbiter.h deleted file mode 100644 index 1dc130afb040..000000000000 --- a/3rdparty/chipmunk/include/chipmunk/cpArbiter.h +++ /dev/null @@ -1,145 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/// @defgroup cpArbiter cpArbiter -/// The cpArbiter struct tracks pairs of colliding shapes. -/// They are also used in conjuction with collision handler callbacks -/// allowing you to retrieve information on the collision or change it. -/// A unique arbiter value is used for each pair of colliding objects. It persists until the shapes separate. -/// @{ - -#define CP_MAX_CONTACTS_PER_ARBITER 2 - -/// Get the restitution (elasticity) that will be applied to the pair of colliding objects. -CP_EXPORT cpFloat cpArbiterGetRestitution(const cpArbiter *arb); -/// Override the restitution (elasticity) that will be applied to the pair of colliding objects. -CP_EXPORT void cpArbiterSetRestitution(cpArbiter *arb, cpFloat restitution); -/// Get the friction coefficient that will be applied to the pair of colliding objects. -CP_EXPORT cpFloat cpArbiterGetFriction(const cpArbiter *arb); -/// Override the friction coefficient that will be applied to the pair of colliding objects. -CP_EXPORT void cpArbiterSetFriction(cpArbiter *arb, cpFloat friction); - -// Get the relative surface velocity of the two shapes in contact. -CP_EXPORT cpVect cpArbiterGetSurfaceVelocity(cpArbiter *arb); - -// Override the relative surface velocity of the two shapes in contact. -// By default this is calculated to be the difference of the two surface velocities clamped to the tangent plane. -CP_EXPORT void cpArbiterSetSurfaceVelocity(cpArbiter *arb, cpVect vr); - -/// Get the user data pointer associated with this pair of colliding objects. -CP_EXPORT cpDataPointer cpArbiterGetUserData(const cpArbiter *arb); -/// Set a user data point associated with this pair of colliding objects. -/// If you need to perform any cleanup for this pointer, you must do it yourself, in the separate callback for instance. -CP_EXPORT void cpArbiterSetUserData(cpArbiter *arb, cpDataPointer userData); - -/// Calculate the total impulse including the friction that was applied by this arbiter. -/// This function should only be called from a post-solve, post-step or cpBodyEachArbiter callback. -CP_EXPORT cpVect cpArbiterTotalImpulse(const cpArbiter *arb); -/// Calculate the amount of energy lost in a collision including static, but not dynamic friction. -/// This function should only be called from a post-solve, post-step or cpBodyEachArbiter callback. -CP_EXPORT cpFloat cpArbiterTotalKE(const cpArbiter *arb); - -/// Mark a collision pair to be ignored until the two objects separate. -/// Pre-solve and post-solve callbacks will not be called, but the separate callback will be called. -CP_EXPORT cpBool cpArbiterIgnore(cpArbiter *arb); - -/// Return the colliding shapes involved for this arbiter. -/// The order of their cpSpace.collision_type values will match -/// the order set when the collision handler was registered. -CP_EXPORT void cpArbiterGetShapes(const cpArbiter *arb, cpShape **a, cpShape **b); - -/// A macro shortcut for defining and retrieving the shapes from an arbiter. -#define CP_ARBITER_GET_SHAPES(__arb__, __a__, __b__) cpShape *__a__, *__b__; cpArbiterGetShapes(__arb__, &__a__, &__b__); - -/// Return the colliding bodies involved for this arbiter. -/// The order of the cpSpace.collision_type the bodies are associated with values will match -/// the order set when the collision handler was registered. -CP_EXPORT void cpArbiterGetBodies(const cpArbiter *arb, cpBody **a, cpBody **b); - -/// A macro shortcut for defining and retrieving the bodies from an arbiter. -#define CP_ARBITER_GET_BODIES(__arb__, __a__, __b__) cpBody *__a__, *__b__; cpArbiterGetBodies(__arb__, &__a__, &__b__); - -/// A struct that wraps up the important collision data for an arbiter. -struct cpContactPointSet { - /// The number of contact points in the set. - int count; - - /// The normal of the collision. - cpVect normal; - - /// The array of contact points. - struct { - /// The position of the contact on the surface of each shape. - cpVect pointA, pointB; - /// Penetration distance of the two shapes. Overlapping means it will be negative. - /// This value is calculated as cpvdot(cpvsub(point2, point1), normal) and is ignored by cpArbiterSetContactPointSet(). - cpFloat distance; - } points[CP_MAX_CONTACTS_PER_ARBITER]; -}; - -/// Return a contact set from an arbiter. -CP_EXPORT cpContactPointSet cpArbiterGetContactPointSet(const cpArbiter *arb); - -/// Replace the contact point set for an arbiter. -/// This can be a very powerful feature, but use it with caution! -CP_EXPORT void cpArbiterSetContactPointSet(cpArbiter *arb, cpContactPointSet *set); - -/// Returns true if this is the first step a pair of objects started colliding. -CP_EXPORT cpBool cpArbiterIsFirstContact(const cpArbiter *arb); -/// Returns true if the separate callback is due to a shape being removed from the space. -CP_EXPORT cpBool cpArbiterIsRemoval(const cpArbiter *arb); - -/// Get the number of contact points for this arbiter. -CP_EXPORT int cpArbiterGetCount(const cpArbiter *arb); -/// Get the normal of the collision. -CP_EXPORT cpVect cpArbiterGetNormal(const cpArbiter *arb); -/// Get the position of the @c ith contact point on the surface of the first shape. -CP_EXPORT cpVect cpArbiterGetPointA(const cpArbiter *arb, int i); -/// Get the position of the @c ith contact point on the surface of the second shape. -CP_EXPORT cpVect cpArbiterGetPointB(const cpArbiter *arb, int i); -/// Get the depth of the @c ith contact point. -CP_EXPORT cpFloat cpArbiterGetDepth(const cpArbiter *arb, int i); - -/// If you want a custom callback to invoke the wildcard callback for the first collision type, you must call this function explicitly. -/// You must decide how to handle the wildcard's return value since it may disagree with the other wildcard handler's return value or your own. -CP_EXPORT cpBool cpArbiterCallWildcardBeginA(cpArbiter *arb, cpSpace *space); -/// If you want a custom callback to invoke the wildcard callback for the second collision type, you must call this function explicitly. -/// You must decide how to handle the wildcard's return value since it may disagree with the other wildcard handler's return value or your own. -CP_EXPORT cpBool cpArbiterCallWildcardBeginB(cpArbiter *arb, cpSpace *space); - -/// If you want a custom callback to invoke the wildcard callback for the first collision type, you must call this function explicitly. -/// You must decide how to handle the wildcard's return value since it may disagree with the other wildcard handler's return value or your own. -CP_EXPORT cpBool cpArbiterCallWildcardPreSolveA(cpArbiter *arb, cpSpace *space); -/// If you want a custom callback to invoke the wildcard callback for the second collision type, you must call this function explicitly. -/// You must decide how to handle the wildcard's return value since it may disagree with the other wildcard handler's return value or your own. -CP_EXPORT cpBool cpArbiterCallWildcardPreSolveB(cpArbiter *arb, cpSpace *space); - -/// If you want a custom callback to invoke the wildcard callback for the first collision type, you must call this function explicitly. -CP_EXPORT void cpArbiterCallWildcardPostSolveA(cpArbiter *arb, cpSpace *space); -/// If you want a custom callback to invoke the wildcard callback for the second collision type, you must call this function explicitly. -CP_EXPORT void cpArbiterCallWildcardPostSolveB(cpArbiter *arb, cpSpace *space); - -/// If you want a custom callback to invoke the wildcard callback for the first collision type, you must call this function explicitly. -CP_EXPORT void cpArbiterCallWildcardSeparateA(cpArbiter *arb, cpSpace *space); -/// If you want a custom callback to invoke the wildcard callback for the second collision type, you must call this function explicitly. -CP_EXPORT void cpArbiterCallWildcardSeparateB(cpArbiter *arb, cpSpace *space); - -/// @} diff --git a/3rdparty/chipmunk/include/chipmunk/cpBB.h b/3rdparty/chipmunk/include/chipmunk/cpBB.h deleted file mode 100644 index 8fc87049cc40..000000000000 --- a/3rdparty/chipmunk/include/chipmunk/cpBB.h +++ /dev/null @@ -1,187 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef CHIPMUNK_BB_H -#define CHIPMUNK_BB_H - -#include "chipmunk_types.h" -#include "cpVect.h" - -/// @defgroup cpBBB cpBB -/// Chipmunk's axis-aligned 2D bounding box type along with a few handy routines. -/// @{ - -/// Chipmunk's axis-aligned 2D bounding box type. (left, bottom, right, top) -typedef struct cpBB{ - cpFloat l, b, r ,t; -} cpBB; - -/// Convenience constructor for cpBB structs. -static inline cpBB cpBBNew(const cpFloat l, const cpFloat b, const cpFloat r, const cpFloat t) -{ - cpBB bb = {l, b, r, t}; - return bb; -} - -/// Constructs a cpBB centered on a point with the given extents (half sizes). -static inline cpBB -cpBBNewForExtents(const cpVect c, const cpFloat hw, const cpFloat hh) -{ - return cpBBNew(c.x - hw, c.y - hh, c.x + hw, c.y + hh); -} - -/// Constructs a cpBB for a circle with the given position and radius. -static inline cpBB cpBBNewForCircle(const cpVect p, const cpFloat r) -{ - return cpBBNewForExtents(p, r, r); -} - -/// Returns true if @c a and @c b intersect. -static inline cpBool cpBBIntersects(const cpBB a, const cpBB b) -{ - return (a.l <= b.r && b.l <= a.r && a.b <= b.t && b.b <= a.t); -} - -/// Returns true if @c other lies completely within @c bb. -static inline cpBool cpBBContainsBB(const cpBB bb, const cpBB other) -{ - return (bb.l <= other.l && bb.r >= other.r && bb.b <= other.b && bb.t >= other.t); -} - -/// Returns true if @c bb contains @c v. -static inline cpBool cpBBContainsVect(const cpBB bb, const cpVect v) -{ - return (bb.l <= v.x && bb.r >= v.x && bb.b <= v.y && bb.t >= v.y); -} - -/// Returns a bounding box that holds both bounding boxes. -static inline cpBB cpBBMerge(const cpBB a, const cpBB b){ - return cpBBNew( - cpfmin(a.l, b.l), - cpfmin(a.b, b.b), - cpfmax(a.r, b.r), - cpfmax(a.t, b.t) - ); -} - -/// Returns a bounding box that holds both @c bb and @c v. -static inline cpBB cpBBExpand(const cpBB bb, const cpVect v){ - return cpBBNew( - cpfmin(bb.l, v.x), - cpfmin(bb.b, v.y), - cpfmax(bb.r, v.x), - cpfmax(bb.t, v.y) - ); -} - -/// Returns the center of a bounding box. -static inline cpVect -cpBBCenter(cpBB bb) -{ - return cpvlerp(cpv(bb.l, bb.b), cpv(bb.r, bb.t), 0.5f); -} - -/// Returns the area of the bounding box. -static inline cpFloat cpBBArea(cpBB bb) -{ - return (bb.r - bb.l)*(bb.t - bb.b); -} - -/// Merges @c a and @c b and returns the area of the merged bounding box. -static inline cpFloat cpBBMergedArea(cpBB a, cpBB b) -{ - return (cpfmax(a.r, b.r) - cpfmin(a.l, b.l))*(cpfmax(a.t, b.t) - cpfmin(a.b, b.b)); -} - -/// Returns the fraction along the segment query the cpBB is hit. Returns INFINITY if it doesn't hit. -static inline cpFloat cpBBSegmentQuery(cpBB bb, cpVect a, cpVect b) -{ - cpVect delta = cpvsub(b, a); - cpFloat tmin = -INFINITY, tmax = INFINITY; - - if(delta.x == 0.0f){ - if(a.x < bb.l || bb.r < a.x) return INFINITY; - } else { - cpFloat t1 = (bb.l - a.x)/delta.x; - cpFloat t2 = (bb.r - a.x)/delta.x; - tmin = cpfmax(tmin, cpfmin(t1, t2)); - tmax = cpfmin(tmax, cpfmax(t1, t2)); - } - - if(delta.y == 0.0f){ - if(a.y < bb.b || bb.t < a.y) return INFINITY; - } else { - cpFloat t1 = (bb.b - a.y)/delta.y; - cpFloat t2 = (bb.t - a.y)/delta.y; - tmin = cpfmax(tmin, cpfmin(t1, t2)); - tmax = cpfmin(tmax, cpfmax(t1, t2)); - } - - if(tmin <= tmax && 0.0f <= tmax && tmin <= 1.0f){ - return cpfmax(tmin, 0.0f); - } else { - return INFINITY; - } -} - -/// Return true if the bounding box intersects the line segment with ends @c a and @c b. -static inline cpBool cpBBIntersectsSegment(cpBB bb, cpVect a, cpVect b) -{ - return (cpBBSegmentQuery(bb, a, b) != INFINITY); -} - -/// Clamp a vector to a bounding box. -static inline cpVect -cpBBClampVect(const cpBB bb, const cpVect v) -{ - return cpv(cpfclamp(v.x, bb.l, bb.r), cpfclamp(v.y, bb.b, bb.t)); -} - -/// Wrap a vector to a bounding box. -static inline cpVect -cpBBWrapVect(const cpBB bb, const cpVect v) -{ - cpFloat dx = cpfabs(bb.r - bb.l); - cpFloat modx = cpfmod(v.x - bb.l, dx); - cpFloat x = (modx > 0.0f) ? modx : modx + dx; - - cpFloat dy = cpfabs(bb.t - bb.b); - cpFloat mody = cpfmod(v.y - bb.b, dy); - cpFloat y = (mody > 0.0f) ? mody : mody + dy; - - return cpv(x + bb.l, y + bb.b); -} - -/// Returns a bounding box offseted by @c v. -static inline cpBB -cpBBOffset(const cpBB bb, const cpVect v) -{ - return cpBBNew( - bb.l + v.x, - bb.b + v.y, - bb.r + v.x, - bb.t + v.y - ); -} - -///@} - -#endif diff --git a/3rdparty/chipmunk/include/chipmunk/cpBody.h b/3rdparty/chipmunk/include/chipmunk/cpBody.h deleted file mode 100644 index 7e6943d15794..000000000000 --- a/3rdparty/chipmunk/include/chipmunk/cpBody.h +++ /dev/null @@ -1,189 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/// @defgroup cpBody cpBody -/// Chipmunk's rigid body type. Rigid bodies hold the physical properties of an object like -/// it's mass, and position and velocity of it's center of gravity. They don't have an shape on their own. -/// They are given a shape by creating collision shapes (cpShape) that point to the body. -/// @{ - -typedef enum cpBodyType { - /// A dynamic body is one that is affected by gravity, forces, and collisions. - /// This is the default body type. - CP_BODY_TYPE_DYNAMIC, - /// A kinematic body is an infinite mass, user controlled body that is not affected by gravity, forces or collisions. - /// Instead the body only moves based on it's velocity. - /// Dynamic bodies collide normally with kinematic bodies, though the kinematic body will be unaffected. - /// Collisions between two kinematic bodies, or a kinematic body and a static body produce collision callbacks, but no collision response. - CP_BODY_TYPE_KINEMATIC, - /// A static body is a body that never (or rarely) moves. If you move a static body, you must call one of the cpSpaceReindex*() functions. - /// Chipmunk uses this information to optimize the collision detection. - /// Static bodies do not produce collision callbacks when colliding with other static bodies. - CP_BODY_TYPE_STATIC, -} cpBodyType; - -/// Rigid body velocity update function type. -typedef void (*cpBodyVelocityFunc)(cpBody *body, cpVect gravity, cpFloat damping, cpFloat dt); -/// Rigid body position update function type. -typedef void (*cpBodyPositionFunc)(cpBody *body, cpFloat dt); - -/// Allocate a cpBody. -CP_EXPORT cpBody* cpBodyAlloc(void); -/// Initialize a cpBody. -CP_EXPORT cpBody* cpBodyInit(cpBody *body, cpFloat mass, cpFloat moment); -/// Allocate and initialize a cpBody. -CP_EXPORT cpBody* cpBodyNew(cpFloat mass, cpFloat moment); - -/// Allocate and initialize a cpBody, and set it as a kinematic body. -CP_EXPORT cpBody* cpBodyNewKinematic(void); -/// Allocate and initialize a cpBody, and set it as a static body. -CP_EXPORT cpBody* cpBodyNewStatic(void); - -/// Destroy a cpBody. -CP_EXPORT void cpBodyDestroy(cpBody *body); -/// Destroy and free a cpBody. -CP_EXPORT void cpBodyFree(cpBody *body); - -// Defined in cpSpace.c -/// Wake up a sleeping or idle body. -CP_EXPORT void cpBodyActivate(cpBody *body); -/// Wake up any sleeping or idle bodies touching a static body. -CP_EXPORT void cpBodyActivateStatic(cpBody *body, cpShape *filter); - -/// Force a body to fall asleep immediately. -CP_EXPORT void cpBodySleep(cpBody *body); -/// Force a body to fall asleep immediately along with other bodies in a group. -CP_EXPORT void cpBodySleepWithGroup(cpBody *body, cpBody *group); - -/// Returns true if the body is sleeping. -CP_EXPORT cpBool cpBodyIsSleeping(const cpBody *body); - -/// Get the type of the body. -CP_EXPORT cpBodyType cpBodyGetType(cpBody *body); -/// Set the type of the body. -CP_EXPORT void cpBodySetType(cpBody *body, cpBodyType type); - -/// Get the space this body is added to. -CP_EXPORT cpSpace* cpBodyGetSpace(const cpBody *body); - -/// Get the mass of the body. -CP_EXPORT cpFloat cpBodyGetMass(const cpBody *body); -/// Set the mass of the body. -CP_EXPORT void cpBodySetMass(cpBody *body, cpFloat m); - -/// Get the moment of inertia of the body. -CP_EXPORT cpFloat cpBodyGetMoment(const cpBody *body); -/// Set the moment of inertia of the body. -CP_EXPORT void cpBodySetMoment(cpBody *body, cpFloat i); - -/// Set the position of a body. -CP_EXPORT cpVect cpBodyGetPosition(const cpBody *body); -/// Set the position of the body. -CP_EXPORT void cpBodySetPosition(cpBody *body, cpVect pos); - -/// Get the offset of the center of gravity in body local coordinates. -CP_EXPORT cpVect cpBodyGetCenterOfGravity(const cpBody *body); -/// Set the offset of the center of gravity in body local coordinates. -CP_EXPORT void cpBodySetCenterOfGravity(cpBody *body, cpVect cog); - -/// Get the velocity of the body. -CP_EXPORT cpVect cpBodyGetVelocity(const cpBody *body); -/// Set the velocity of the body. -CP_EXPORT void cpBodySetVelocity(cpBody *body, cpVect velocity); - -/// Get the force applied to the body for the next time step. -CP_EXPORT cpVect cpBodyGetForce(const cpBody *body); -/// Set the force applied to the body for the next time step. -CP_EXPORT void cpBodySetForce(cpBody *body, cpVect force); - -/// Get the angle of the body. -CP_EXPORT cpFloat cpBodyGetAngle(const cpBody *body); -/// Set the angle of a body. -CP_EXPORT void cpBodySetAngle(cpBody *body, cpFloat a); - -/// Get the angular velocity of the body. -CP_EXPORT cpFloat cpBodyGetAngularVelocity(const cpBody *body); -/// Set the angular velocity of the body. -CP_EXPORT void cpBodySetAngularVelocity(cpBody *body, cpFloat angularVelocity); - -/// Get the torque applied to the body for the next time step. -CP_EXPORT cpFloat cpBodyGetTorque(const cpBody *body); -/// Set the torque applied to the body for the next time step. -CP_EXPORT void cpBodySetTorque(cpBody *body, cpFloat torque); - -/// Get the rotation vector of the body. (The x basis vector of it's transform.) -CP_EXPORT cpVect cpBodyGetRotation(const cpBody *body); - -/// Get the user data pointer assigned to the body. -CP_EXPORT cpDataPointer cpBodyGetUserData(const cpBody *body); -/// Set the user data pointer assigned to the body. -CP_EXPORT void cpBodySetUserData(cpBody *body, cpDataPointer userData); - -/// Set the callback used to update a body's velocity. -CP_EXPORT void cpBodySetVelocityUpdateFunc(cpBody *body, cpBodyVelocityFunc velocityFunc); -/// Set the callback used to update a body's position. -/// NOTE: It's not generally recommended to override this unless you call the default position update function. -CP_EXPORT void cpBodySetPositionUpdateFunc(cpBody *body, cpBodyPositionFunc positionFunc); - -/// Default velocity integration function.. -CP_EXPORT void cpBodyUpdateVelocity(cpBody *body, cpVect gravity, cpFloat damping, cpFloat dt); -/// Default position integration function. -CP_EXPORT void cpBodyUpdatePosition(cpBody *body, cpFloat dt); - -/// Convert body relative/local coordinates to absolute/world coordinates. -CP_EXPORT cpVect cpBodyLocalToWorld(const cpBody *body, const cpVect point); -/// Convert body absolute/world coordinates to relative/local coordinates. -CP_EXPORT cpVect cpBodyWorldToLocal(const cpBody *body, const cpVect point); - -/// Apply a force to a body. Both the force and point are expressed in world coordinates. -CP_EXPORT void cpBodyApplyForceAtWorldPoint(cpBody *body, cpVect force, cpVect point); -/// Apply a force to a body. Both the force and point are expressed in body local coordinates. -CP_EXPORT void cpBodyApplyForceAtLocalPoint(cpBody *body, cpVect force, cpVect point); - -/// Apply an impulse to a body. Both the impulse and point are expressed in world coordinates. -CP_EXPORT void cpBodyApplyImpulseAtWorldPoint(cpBody *body, cpVect impulse, cpVect point); -/// Apply an impulse to a body. Both the impulse and point are expressed in body local coordinates. -CP_EXPORT void cpBodyApplyImpulseAtLocalPoint(cpBody *body, cpVect impulse, cpVect point); - -/// Get the velocity on a body (in world units) at a point on the body in world coordinates. -CP_EXPORT cpVect cpBodyGetVelocityAtWorldPoint(const cpBody *body, cpVect point); -/// Get the velocity on a body (in world units) at a point on the body in local coordinates. -CP_EXPORT cpVect cpBodyGetVelocityAtLocalPoint(const cpBody *body, cpVect point); - -/// Get the amount of kinetic energy contained by the body. -CP_EXPORT cpFloat cpBodyKineticEnergy(const cpBody *body); - -/// Body/shape iterator callback function type. -typedef void (*cpBodyShapeIteratorFunc)(cpBody *body, cpShape *shape, void *data); -/// Call @c func once for each shape attached to @c body and added to the space. -CP_EXPORT void cpBodyEachShape(cpBody *body, cpBodyShapeIteratorFunc func, void *data); - -/// Body/constraint iterator callback function type. -typedef void (*cpBodyConstraintIteratorFunc)(cpBody *body, cpConstraint *constraint, void *data); -/// Call @c func once for each constraint attached to @c body and added to the space. -CP_EXPORT void cpBodyEachConstraint(cpBody *body, cpBodyConstraintIteratorFunc func, void *data); - -/// Body/arbiter iterator callback function type. -typedef void (*cpBodyArbiterIteratorFunc)(cpBody *body, cpArbiter *arbiter, void *data); -/// Call @c func once for each arbiter that is currently active on the body. -CP_EXPORT void cpBodyEachArbiter(cpBody *body, cpBodyArbiterIteratorFunc func, void *data); - -///@} diff --git a/3rdparty/chipmunk/include/chipmunk/cpConstraint.h b/3rdparty/chipmunk/include/chipmunk/cpConstraint.h deleted file mode 100644 index b1a439f7be24..000000000000 --- a/3rdparty/chipmunk/include/chipmunk/cpConstraint.h +++ /dev/null @@ -1,95 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/// @defgroup cpConstraint cpConstraint -/// @{ - -/// Callback function type that gets called before solving a joint. -typedef void (*cpConstraintPreSolveFunc)(cpConstraint *constraint, cpSpace *space); -/// Callback function type that gets called after solving a joint. -typedef void (*cpConstraintPostSolveFunc)(cpConstraint *constraint, cpSpace *space); - -/// Destroy a constraint. -CP_EXPORT void cpConstraintDestroy(cpConstraint *constraint); -/// Destroy and free a constraint. -CP_EXPORT void cpConstraintFree(cpConstraint *constraint); - -/// Get the cpSpace this constraint is added to. -CP_EXPORT cpSpace* cpConstraintGetSpace(const cpConstraint *constraint); - -/// Get the first body the constraint is attached to. -CP_EXPORT cpBody* cpConstraintGetBodyA(const cpConstraint *constraint); - -/// Get the second body the constraint is attached to. -CP_EXPORT cpBody* cpConstraintGetBodyB(const cpConstraint *constraint); - -/// Get the maximum force that this constraint is allowed to use. -CP_EXPORT cpFloat cpConstraintGetMaxForce(const cpConstraint *constraint); -/// Set the maximum force that this constraint is allowed to use. (defaults to INFINITY) -CP_EXPORT void cpConstraintSetMaxForce(cpConstraint *constraint, cpFloat maxForce); - -/// Get rate at which joint error is corrected. -CP_EXPORT cpFloat cpConstraintGetErrorBias(const cpConstraint *constraint); -/// Set rate at which joint error is corrected. -/// Defaults to pow(1.0 - 0.1, 60.0) meaning that it will -/// correct 10% of the error every 1/60th of a second. -CP_EXPORT void cpConstraintSetErrorBias(cpConstraint *constraint, cpFloat errorBias); - -/// Get the maximum rate at which joint error is corrected. -CP_EXPORT cpFloat cpConstraintGetMaxBias(const cpConstraint *constraint); -/// Set the maximum rate at which joint error is corrected. (defaults to INFINITY) -CP_EXPORT void cpConstraintSetMaxBias(cpConstraint *constraint, cpFloat maxBias); - -/// Get if the two bodies connected by the constraint are allowed to collide or not. -CP_EXPORT cpBool cpConstraintGetCollideBodies(const cpConstraint *constraint); -/// Set if the two bodies connected by the constraint are allowed to collide or not. (defaults to cpFalse) -CP_EXPORT void cpConstraintSetCollideBodies(cpConstraint *constraint, cpBool collideBodies); - -/// Get the pre-solve function that is called before the solver runs. -CP_EXPORT cpConstraintPreSolveFunc cpConstraintGetPreSolveFunc(const cpConstraint *constraint); -/// Set the pre-solve function that is called before the solver runs. -CP_EXPORT void cpConstraintSetPreSolveFunc(cpConstraint *constraint, cpConstraintPreSolveFunc preSolveFunc); - -/// Get the post-solve function that is called before the solver runs. -CP_EXPORT cpConstraintPostSolveFunc cpConstraintGetPostSolveFunc(const cpConstraint *constraint); -/// Set the post-solve function that is called before the solver runs. -CP_EXPORT void cpConstraintSetPostSolveFunc(cpConstraint *constraint, cpConstraintPostSolveFunc postSolveFunc); - -/// Get the user definable data pointer for this constraint -CP_EXPORT cpDataPointer cpConstraintGetUserData(const cpConstraint *constraint); -/// Set the user definable data pointer for this constraint -CP_EXPORT void cpConstraintSetUserData(cpConstraint *constraint, cpDataPointer userData); - -/// Get the last impulse applied by this constraint. -CP_EXPORT cpFloat cpConstraintGetImpulse(cpConstraint *constraint); - -#include "cpPinJoint.h" -#include "cpSlideJoint.h" -#include "cpPivotJoint.h" -#include "cpGrooveJoint.h" -#include "cpDampedSpring.h" -#include "cpDampedRotarySpring.h" -#include "cpRotaryLimitJoint.h" -#include "cpRatchetJoint.h" -#include "cpGearJoint.h" -#include "cpSimpleMotor.h" - -///@} diff --git a/3rdparty/chipmunk/include/chipmunk/cpDampedRotarySpring.h b/3rdparty/chipmunk/include/chipmunk/cpDampedRotarySpring.h deleted file mode 100644 index 6f60e86e301a..000000000000 --- a/3rdparty/chipmunk/include/chipmunk/cpDampedRotarySpring.h +++ /dev/null @@ -1,58 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/// @defgroup cpDampedRotarySpring cpDampedRotarySpring -/// @{ - -/// Check if a constraint is a damped rotary springs. -CP_EXPORT cpBool cpConstraintIsDampedRotarySpring(const cpConstraint *constraint); - -/// Function type used for damped rotary spring force callbacks. -typedef cpFloat (*cpDampedRotarySpringTorqueFunc)(struct cpConstraint *spring, cpFloat relativeAngle); - -/// Allocate a damped rotary spring. -CP_EXPORT cpDampedRotarySpring* cpDampedRotarySpringAlloc(void); -/// Initialize a damped rotary spring. -CP_EXPORT cpDampedRotarySpring* cpDampedRotarySpringInit(cpDampedRotarySpring *joint, cpBody *a, cpBody *b, cpFloat restAngle, cpFloat stiffness, cpFloat damping); -/// Allocate and initialize a damped rotary spring. -CP_EXPORT cpConstraint* cpDampedRotarySpringNew(cpBody *a, cpBody *b, cpFloat restAngle, cpFloat stiffness, cpFloat damping); - -/// Get the rest length of the spring. -CP_EXPORT cpFloat cpDampedRotarySpringGetRestAngle(const cpConstraint *constraint); -/// Set the rest length of the spring. -CP_EXPORT void cpDampedRotarySpringSetRestAngle(cpConstraint *constraint, cpFloat restAngle); - -/// Get the stiffness of the spring in force/distance. -CP_EXPORT cpFloat cpDampedRotarySpringGetStiffness(const cpConstraint *constraint); -/// Set the stiffness of the spring in force/distance. -CP_EXPORT void cpDampedRotarySpringSetStiffness(cpConstraint *constraint, cpFloat stiffness); - -/// Get the damping of the spring. -CP_EXPORT cpFloat cpDampedRotarySpringGetDamping(const cpConstraint *constraint); -/// Set the damping of the spring. -CP_EXPORT void cpDampedRotarySpringSetDamping(cpConstraint *constraint, cpFloat damping); - -/// Get the damping of the spring. -CP_EXPORT cpDampedRotarySpringTorqueFunc cpDampedRotarySpringGetSpringTorqueFunc(const cpConstraint *constraint); -/// Set the damping of the spring. -CP_EXPORT void cpDampedRotarySpringSetSpringTorqueFunc(cpConstraint *constraint, cpDampedRotarySpringTorqueFunc springTorqueFunc); - -/// @} diff --git a/3rdparty/chipmunk/include/chipmunk/cpDampedSpring.h b/3rdparty/chipmunk/include/chipmunk/cpDampedSpring.h deleted file mode 100644 index b332fc7f02dc..000000000000 --- a/3rdparty/chipmunk/include/chipmunk/cpDampedSpring.h +++ /dev/null @@ -1,68 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/// @defgroup cpDampedSpring cpDampedSpring -/// @{ - -/// Check if a constraint is a slide joint. -CP_EXPORT cpBool cpConstraintIsDampedSpring(const cpConstraint *constraint); - -/// Function type used for damped spring force callbacks. -typedef cpFloat (*cpDampedSpringForceFunc)(cpConstraint *spring, cpFloat dist); - -/// Allocate a damped spring. -CP_EXPORT cpDampedSpring* cpDampedSpringAlloc(void); -/// Initialize a damped spring. -CP_EXPORT cpDampedSpring* cpDampedSpringInit(cpDampedSpring *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat restLength, cpFloat stiffness, cpFloat damping); -/// Allocate and initialize a damped spring. -CP_EXPORT cpConstraint* cpDampedSpringNew(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat restLength, cpFloat stiffness, cpFloat damping); - -/// Get the location of the first anchor relative to the first body. -CP_EXPORT cpVect cpDampedSpringGetAnchorA(const cpConstraint *constraint); -/// Set the location of the first anchor relative to the first body. -CP_EXPORT void cpDampedSpringSetAnchorA(cpConstraint *constraint, cpVect anchorA); - -/// Get the location of the second anchor relative to the second body. -CP_EXPORT cpVect cpDampedSpringGetAnchorB(const cpConstraint *constraint); -/// Set the location of the second anchor relative to the second body. -CP_EXPORT void cpDampedSpringSetAnchorB(cpConstraint *constraint, cpVect anchorB); - -/// Get the rest length of the spring. -CP_EXPORT cpFloat cpDampedSpringGetRestLength(const cpConstraint *constraint); -/// Set the rest length of the spring. -CP_EXPORT void cpDampedSpringSetRestLength(cpConstraint *constraint, cpFloat restLength); - -/// Get the stiffness of the spring in force/distance. -CP_EXPORT cpFloat cpDampedSpringGetStiffness(const cpConstraint *constraint); -/// Set the stiffness of the spring in force/distance. -CP_EXPORT void cpDampedSpringSetStiffness(cpConstraint *constraint, cpFloat stiffness); - -/// Get the damping of the spring. -CP_EXPORT cpFloat cpDampedSpringGetDamping(const cpConstraint *constraint); -/// Set the damping of the spring. -CP_EXPORT void cpDampedSpringSetDamping(cpConstraint *constraint, cpFloat damping); - -/// Get the damping of the spring. -CP_EXPORT cpDampedSpringForceFunc cpDampedSpringGetSpringForceFunc(const cpConstraint *constraint); -/// Set the damping of the spring. -CP_EXPORT void cpDampedSpringSetSpringForceFunc(cpConstraint *constraint, cpDampedSpringForceFunc springForceFunc); - -/// @} diff --git a/3rdparty/chipmunk/include/chipmunk/cpGearJoint.h b/3rdparty/chipmunk/include/chipmunk/cpGearJoint.h deleted file mode 100644 index 8cd80e0b906c..000000000000 --- a/3rdparty/chipmunk/include/chipmunk/cpGearJoint.h +++ /dev/null @@ -1,45 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/// @defgroup cpGearJoint cpGearJoint -/// @{ - -/// Check if a constraint is a damped rotary springs. -CP_EXPORT cpBool cpConstraintIsGearJoint(const cpConstraint *constraint); - -/// Allocate a gear joint. -CP_EXPORT cpGearJoint* cpGearJointAlloc(void); -/// Initialize a gear joint. -CP_EXPORT cpGearJoint* cpGearJointInit(cpGearJoint *joint, cpBody *a, cpBody *b, cpFloat phase, cpFloat ratio); -/// Allocate and initialize a gear joint. -CP_EXPORT cpConstraint* cpGearJointNew(cpBody *a, cpBody *b, cpFloat phase, cpFloat ratio); - -/// Get the phase offset of the gears. -CP_EXPORT cpFloat cpGearJointGetPhase(const cpConstraint *constraint); -/// Set the phase offset of the gears. -CP_EXPORT void cpGearJointSetPhase(cpConstraint *constraint, cpFloat phase); - -/// Get the angular distance of each ratchet. -CP_EXPORT cpFloat cpGearJointGetRatio(const cpConstraint *constraint); -/// Set the ratio of a gear joint. -CP_EXPORT void cpGearJointSetRatio(cpConstraint *constraint, cpFloat ratio); - -/// @} diff --git a/3rdparty/chipmunk/include/chipmunk/cpGrooveJoint.h b/3rdparty/chipmunk/include/chipmunk/cpGrooveJoint.h deleted file mode 100644 index 8bdafc14aa2f..000000000000 --- a/3rdparty/chipmunk/include/chipmunk/cpGrooveJoint.h +++ /dev/null @@ -1,50 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/// @defgroup cpGrooveJoint cpGrooveJoint -/// @{ - -/// Check if a constraint is a slide joint. -CP_EXPORT cpBool cpConstraintIsGrooveJoint(const cpConstraint *constraint); - -/// Allocate a groove joint. -CP_EXPORT cpGrooveJoint* cpGrooveJointAlloc(void); -/// Initialize a groove joint. -CP_EXPORT cpGrooveJoint* cpGrooveJointInit(cpGrooveJoint *joint, cpBody *a, cpBody *b, cpVect groove_a, cpVect groove_b, cpVect anchorB); -/// Allocate and initialize a groove joint. -CP_EXPORT cpConstraint* cpGrooveJointNew(cpBody *a, cpBody *b, cpVect groove_a, cpVect groove_b, cpVect anchorB); - -/// Get the first endpoint of the groove relative to the first body. -CP_EXPORT cpVect cpGrooveJointGetGrooveA(const cpConstraint *constraint); -/// Set the first endpoint of the groove relative to the first body. -CP_EXPORT void cpGrooveJointSetGrooveA(cpConstraint *constraint, cpVect grooveA); - -/// Get the first endpoint of the groove relative to the first body. -CP_EXPORT cpVect cpGrooveJointGetGrooveB(const cpConstraint *constraint); -/// Set the first endpoint of the groove relative to the first body. -CP_EXPORT void cpGrooveJointSetGrooveB(cpConstraint *constraint, cpVect grooveB); - -/// Get the location of the second anchor relative to the second body. -CP_EXPORT cpVect cpGrooveJointGetAnchorB(const cpConstraint *constraint); -/// Set the location of the second anchor relative to the second body. -CP_EXPORT void cpGrooveJointSetAnchorB(cpConstraint *constraint, cpVect anchorB); - -/// @} diff --git a/3rdparty/chipmunk/include/chipmunk/cpHastySpace.h b/3rdparty/chipmunk/include/chipmunk/cpHastySpace.h deleted file mode 100644 index 6de2283b9fb7..000000000000 --- a/3rdparty/chipmunk/include/chipmunk/cpHastySpace.h +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2013 Howling Moon Software. All rights reserved. -// See http://chipmunk2d.net/legal.php for more information. - -/// cpHastySpace is exclusive to Chipmunk Pro -/// Currently it enables ARM NEON optimizations in the solver, but in the future will include other optimizations such as -/// a multi-threaded solver and multi-threaded collision broadphases. - -struct cpHastySpace; -typedef struct cpHastySpace cpHastySpace; - -/// Create a new hasty space. -/// On ARM platforms that support NEON, this will enable the vectorized solver. -/// cpHastySpace also supports multiple threads, but runs single threaded by default for determinism. -CP_EXPORT cpSpace *cpHastySpaceNew(void); -CP_EXPORT void cpHastySpaceFree(cpSpace *space); - -/// Set the number of threads to use for the solver. -/// Currently Chipmunk is limited to 2 threads as using more generally provides very minimal performance gains. -/// Passing 0 as the thread count on iOS or OS X will cause Chipmunk to automatically detect the number of threads it should use. -/// On other platforms passing 0 for the thread count will set 1 thread. -CP_EXPORT void cpHastySpaceSetThreads(cpSpace *space, unsigned long threads); - -/// Returns the number of threads the solver is using to run. -CP_EXPORT unsigned long cpHastySpaceGetThreads(cpSpace *space); - -/// When stepping a hasty space, you must use this function. -CP_EXPORT void cpHastySpaceStep(cpSpace *space, cpFloat dt); diff --git a/3rdparty/chipmunk/include/chipmunk/cpMarch.h b/3rdparty/chipmunk/include/chipmunk/cpMarch.h deleted file mode 100644 index cc1f5c061d1f..000000000000 --- a/3rdparty/chipmunk/include/chipmunk/cpMarch.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2013 Howling Moon Software. All rights reserved. -// See http://chipmunk2d.net/legal.php for more information. - -/// Function type used as a callback from the marching squares algorithm to sample an image function. -/// It passes you the point to sample and your context pointer, and you return the density. -typedef cpFloat (*cpMarchSampleFunc)(cpVect point, void *data); - -/// Function type used as a callback from the marching squares algorithm to output a line segment. -/// It passes you the two endpoints and your context pointer. -typedef void (*cpMarchSegmentFunc)(cpVect v0, cpVect v1, void *data); - -/// Trace an anti-aliased contour of an image along a particular threshold. -/// The given number of samples will be taken and spread across the bounding box area using the sampling function and context. -/// The segment function will be called for each segment detected that lies along the density contour for @c threshold. -CP_EXPORT void cpMarchSoft( - cpBB bb, unsigned long x_samples, unsigned long y_samples, cpFloat threshold, - cpMarchSegmentFunc segment, void *segment_data, - cpMarchSampleFunc sample, void *sample_data -); - -/// Trace an aliased curve of an image along a particular threshold. -/// The given number of samples will be taken and spread across the bounding box area using the sampling function and context. -/// The segment function will be called for each segment detected that lies along the density contour for @c threshold. -CP_EXPORT void cpMarchHard( - cpBB bb, unsigned long x_samples, unsigned long y_samples, cpFloat threshold, - cpMarchSegmentFunc segment, void *segment_data, - cpMarchSampleFunc sample, void *sample_data -); diff --git a/3rdparty/chipmunk/include/chipmunk/cpPinJoint.h b/3rdparty/chipmunk/include/chipmunk/cpPinJoint.h deleted file mode 100644 index 45aaa3e333da..000000000000 --- a/3rdparty/chipmunk/include/chipmunk/cpPinJoint.h +++ /dev/null @@ -1,50 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/// @defgroup cpPinJoint cpPinJoint -/// @{ - -/// Check if a constraint is a pin joint. -CP_EXPORT cpBool cpConstraintIsPinJoint(const cpConstraint *constraint); - -/// Allocate a pin joint. -CP_EXPORT cpPinJoint* cpPinJointAlloc(void); -/// Initialize a pin joint. -CP_EXPORT cpPinJoint* cpPinJointInit(cpPinJoint *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB); -/// Allocate and initialize a pin joint. -CP_EXPORT cpConstraint* cpPinJointNew(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB); - -/// Get the location of the first anchor relative to the first body. -CP_EXPORT cpVect cpPinJointGetAnchorA(const cpConstraint *constraint); -/// Set the location of the first anchor relative to the first body. -CP_EXPORT void cpPinJointSetAnchorA(cpConstraint *constraint, cpVect anchorA); - -/// Get the location of the second anchor relative to the second body. -CP_EXPORT cpVect cpPinJointGetAnchorB(const cpConstraint *constraint); -/// Set the location of the second anchor relative to the second body. -CP_EXPORT void cpPinJointSetAnchorB(cpConstraint *constraint, cpVect anchorB); - -/// Get the distance the joint will maintain between the two anchors. -CP_EXPORT cpFloat cpPinJointGetDist(const cpConstraint *constraint); -/// Set the distance the joint will maintain between the two anchors. -CP_EXPORT void cpPinJointSetDist(cpConstraint *constraint, cpFloat dist); - -///@} diff --git a/3rdparty/chipmunk/include/chipmunk/cpPivotJoint.h b/3rdparty/chipmunk/include/chipmunk/cpPivotJoint.h deleted file mode 100644 index 4a620ef2553d..000000000000 --- a/3rdparty/chipmunk/include/chipmunk/cpPivotJoint.h +++ /dev/null @@ -1,47 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/// @defgroup cpPivotJoint cpPivotJoint -/// @{ - -/// Check if a constraint is a slide joint. -CP_EXPORT cpBool cpConstraintIsPivotJoint(const cpConstraint *constraint); - -/// Allocate a pivot joint -CP_EXPORT cpPivotJoint* cpPivotJointAlloc(void); -/// Initialize a pivot joint. -CP_EXPORT cpPivotJoint* cpPivotJointInit(cpPivotJoint *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB); -/// Allocate and initialize a pivot joint. -CP_EXPORT cpConstraint* cpPivotJointNew(cpBody *a, cpBody *b, cpVect pivot); -/// Allocate and initialize a pivot joint with specific anchors. -CP_EXPORT cpConstraint* cpPivotJointNew2(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB); - -/// Get the location of the first anchor relative to the first body. -CP_EXPORT cpVect cpPivotJointGetAnchorA(const cpConstraint *constraint); -/// Set the location of the first anchor relative to the first body. -CP_EXPORT void cpPivotJointSetAnchorA(cpConstraint *constraint, cpVect anchorA); - -/// Get the location of the second anchor relative to the second body. -CP_EXPORT cpVect cpPivotJointGetAnchorB(const cpConstraint *constraint); -/// Set the location of the second anchor relative to the second body. -CP_EXPORT void cpPivotJointSetAnchorB(cpConstraint *constraint, cpVect anchorB); - -/// @} diff --git a/3rdparty/chipmunk/include/chipmunk/cpPolyShape.h b/3rdparty/chipmunk/include/chipmunk/cpPolyShape.h deleted file mode 100644 index 25f688b896ae..000000000000 --- a/3rdparty/chipmunk/include/chipmunk/cpPolyShape.h +++ /dev/null @@ -1,56 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/// @defgroup cpPolyShape cpPolyShape -/// @{ - -/// Allocate a polygon shape. -CP_EXPORT cpPolyShape* cpPolyShapeAlloc(void); -/// Initialize a polygon shape with rounded corners. -/// A convex hull will be created from the vertexes. -CP_EXPORT cpPolyShape* cpPolyShapeInit(cpPolyShape *poly, cpBody *body, int count, const cpVect *verts, cpTransform transform, cpFloat radius); -/// Initialize a polygon shape with rounded corners. -/// The vertexes must be convex with a counter-clockwise winding. -CP_EXPORT cpPolyShape* cpPolyShapeInitRaw(cpPolyShape *poly, cpBody *body, int count, const cpVect *verts, cpFloat radius); -/// Allocate and initialize a polygon shape with rounded corners. -/// A convex hull will be created from the vertexes. -CP_EXPORT cpShape* cpPolyShapeNew(cpBody *body, int count, const cpVect *verts, cpTransform transform, cpFloat radius); -/// Allocate and initialize a polygon shape with rounded corners. -/// The vertexes must be convex with a counter-clockwise winding. -CP_EXPORT cpShape* cpPolyShapeNewRaw(cpBody *body, int count, const cpVect *verts, cpFloat radius); - -/// Initialize a box shaped polygon shape with rounded corners. -CP_EXPORT cpPolyShape* cpBoxShapeInit(cpPolyShape *poly, cpBody *body, cpFloat width, cpFloat height, cpFloat radius); -/// Initialize an offset box shaped polygon shape with rounded corners. -CP_EXPORT cpPolyShape* cpBoxShapeInit2(cpPolyShape *poly, cpBody *body, cpBB box, cpFloat radius); -/// Allocate and initialize a box shaped polygon shape. -CP_EXPORT cpShape* cpBoxShapeNew(cpBody *body, cpFloat width, cpFloat height, cpFloat radius); -/// Allocate and initialize an offset box shaped polygon shape. -CP_EXPORT cpShape* cpBoxShapeNew2(cpBody *body, cpBB box, cpFloat radius); - -/// Get the number of verts in a polygon shape. -CP_EXPORT int cpPolyShapeGetCount(const cpShape *shape); -/// Get the @c ith vertex of a polygon shape. -CP_EXPORT cpVect cpPolyShapeGetVert(const cpShape *shape, int index); -/// Get the radius of a polygon shape. -CP_EXPORT cpFloat cpPolyShapeGetRadius(const cpShape *shape); - -/// @} diff --git a/3rdparty/chipmunk/include/chipmunk/cpPolyline.h b/3rdparty/chipmunk/include/chipmunk/cpPolyline.h deleted file mode 100644 index 9a6ebed3caa1..000000000000 --- a/3rdparty/chipmunk/include/chipmunk/cpPolyline.h +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2013 Howling Moon Software. All rights reserved. -// See http://chipmunk2d.net/legal.php for more information. - -// Polylines are just arrays of vertexes. -// They are looped if the first vertex is equal to the last. -// cpPolyline structs are intended to be passed by value and destroyed when you are done with them. -typedef struct cpPolyline { - int count, capacity; - cpVect verts[]; -} cpPolyline; - -/// Destroy and free a polyline instance. -CP_EXPORT void cpPolylineFree(cpPolyline *line); - -/// Returns true if the first vertex is equal to the last. -CP_EXPORT cpBool cpPolylineIsClosed(cpPolyline *line); - -/** - Returns a copy of a polyline simplified by using the Douglas-Peucker algorithm. - This works very well on smooth or gently curved shapes, but not well on straight edged or angular shapes. -*/ -CP_EXPORT cpPolyline *cpPolylineSimplifyCurves(cpPolyline *line, cpFloat tol); - -/** - Returns a copy of a polyline simplified by discarding "flat" vertexes. - This works well on straight edged or angular shapes, not as well on smooth shapes. -*/ -CP_EXPORT cpPolyline *cpPolylineSimplifyVertexes(cpPolyline *line, cpFloat tol); - -/// Get the convex hull of a polyline as a looped polyline. -CP_EXPORT cpPolyline *cpPolylineToConvexHull(cpPolyline *line, cpFloat tol); - - -/// Polyline sets are collections of polylines, generally built by cpMarchSoft() or cpMarchHard(). -typedef struct cpPolylineSet { - int count, capacity; - cpPolyline **lines; -} cpPolylineSet; - -/// Allocate a new polyline set. -CP_EXPORT cpPolylineSet *cpPolylineSetAlloc(void); - -/// Initialize a new polyline set. -CP_EXPORT cpPolylineSet *cpPolylineSetInit(cpPolylineSet *set); - -/// Allocate and initialize a polyline set. -CP_EXPORT cpPolylineSet *cpPolylineSetNew(void); - -/// Destroy a polyline set. -CP_EXPORT void cpPolylineSetDestroy(cpPolylineSet *set, cpBool freePolylines); - -/// Destroy and free a polyline set. -CP_EXPORT void cpPolylineSetFree(cpPolylineSet *set, cpBool freePolylines); - -/** - Add a line segment to a polyline set. - A segment will either start a new polyline, join two others, or add to or loop an existing polyline. - This is mostly intended to be used as a callback directly from cpMarchSoft() or cpMarchHard(). -*/ -CP_EXPORT void cpPolylineSetCollectSegment(cpVect v0, cpVect v1, cpPolylineSet *lines); - -/** - Get an approximate convex decomposition from a polyline. - Returns a cpPolylineSet of convex hulls that match the original shape to within 'tol'. - NOTE: If the input is a self intersecting polygon, the output might end up overly simplified. -*/ - -CP_EXPORT cpPolylineSet *cpPolylineConvexDecomposition(cpPolyline *line, cpFloat tol); - -#define cpPolylineConvexDecomposition_BETA cpPolylineConvexDecomposition diff --git a/3rdparty/chipmunk/include/chipmunk/cpRatchetJoint.h b/3rdparty/chipmunk/include/chipmunk/cpRatchetJoint.h deleted file mode 100644 index 3ed4c915ee75..000000000000 --- a/3rdparty/chipmunk/include/chipmunk/cpRatchetJoint.h +++ /dev/null @@ -1,50 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/// @defgroup cpRatchetJoint cpRatchetJoint -/// @{ - -/// Check if a constraint is a damped rotary springs. -CP_EXPORT cpBool cpConstraintIsRatchetJoint(const cpConstraint *constraint); - -/// Allocate a ratchet joint. -CP_EXPORT cpRatchetJoint* cpRatchetJointAlloc(void); -/// Initialize a ratched joint. -CP_EXPORT cpRatchetJoint* cpRatchetJointInit(cpRatchetJoint *joint, cpBody *a, cpBody *b, cpFloat phase, cpFloat ratchet); -/// Allocate and initialize a ratchet joint. -CP_EXPORT cpConstraint* cpRatchetJointNew(cpBody *a, cpBody *b, cpFloat phase, cpFloat ratchet); - -/// Get the angle of the current ratchet tooth. -CP_EXPORT cpFloat cpRatchetJointGetAngle(const cpConstraint *constraint); -/// Set the angle of the current ratchet tooth. -CP_EXPORT void cpRatchetJointSetAngle(cpConstraint *constraint, cpFloat angle); - -/// Get the phase offset of the ratchet. -CP_EXPORT cpFloat cpRatchetJointGetPhase(const cpConstraint *constraint); -/// Get the phase offset of the ratchet. -CP_EXPORT void cpRatchetJointSetPhase(cpConstraint *constraint, cpFloat phase); - -/// Get the angular distance of each ratchet. -CP_EXPORT cpFloat cpRatchetJointGetRatchet(const cpConstraint *constraint); -/// Set the angular distance of each ratchet. -CP_EXPORT void cpRatchetJointSetRatchet(cpConstraint *constraint, cpFloat ratchet); - -/// @} diff --git a/3rdparty/chipmunk/include/chipmunk/cpRobust.h b/3rdparty/chipmunk/include/chipmunk/cpRobust.h deleted file mode 100644 index e4b2c42082e3..000000000000 --- a/3rdparty/chipmunk/include/chipmunk/cpRobust.h +++ /dev/null @@ -1,11 +0,0 @@ -#include "chipmunk/cpVect.h" - -// This is a private header for functions (currently just one) that need strict floating point results. -// It was easier to put this in it's own file than to fiddle with 4 different compiler specific pragmas or attributes. -// "Fast math" should be disabled here. - -// Check if c is to the left of segment (a, b). -cpBool cpCheckPointGreater(const cpVect a, const cpVect b, const cpVect c); - -// Check if p is behind one of v0 or v1 on axis n. -cpBool cpCheckAxis(cpVect v0, cpVect v1, cpVect p, cpVect n); diff --git a/3rdparty/chipmunk/include/chipmunk/cpRotaryLimitJoint.h b/3rdparty/chipmunk/include/chipmunk/cpRotaryLimitJoint.h deleted file mode 100644 index fac7ad859166..000000000000 --- a/3rdparty/chipmunk/include/chipmunk/cpRotaryLimitJoint.h +++ /dev/null @@ -1,45 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/// @defgroup cpRotaryLimitJoint cpRotaryLimitJoint -/// @{ - -/// Check if a constraint is a damped rotary springs. -CP_EXPORT cpBool cpConstraintIsRotaryLimitJoint(const cpConstraint *constraint); - -/// Allocate a damped rotary limit joint. -CP_EXPORT cpRotaryLimitJoint* cpRotaryLimitJointAlloc(void); -/// Initialize a damped rotary limit joint. -CP_EXPORT cpRotaryLimitJoint* cpRotaryLimitJointInit(cpRotaryLimitJoint *joint, cpBody *a, cpBody *b, cpFloat min, cpFloat max); -/// Allocate and initialize a damped rotary limit joint. -CP_EXPORT cpConstraint* cpRotaryLimitJointNew(cpBody *a, cpBody *b, cpFloat min, cpFloat max); - -/// Get the minimum distance the joint will maintain between the two anchors. -CP_EXPORT cpFloat cpRotaryLimitJointGetMin(const cpConstraint *constraint); -/// Set the minimum distance the joint will maintain between the two anchors. -CP_EXPORT void cpRotaryLimitJointSetMin(cpConstraint *constraint, cpFloat min); - -/// Get the maximum distance the joint will maintain between the two anchors. -CP_EXPORT cpFloat cpRotaryLimitJointGetMax(const cpConstraint *constraint); -/// Set the maximum distance the joint will maintain between the two anchors. -CP_EXPORT void cpRotaryLimitJointSetMax(cpConstraint *constraint, cpFloat max); - -/// @} diff --git a/3rdparty/chipmunk/include/chipmunk/cpShape.h b/3rdparty/chipmunk/include/chipmunk/cpShape.h deleted file mode 100644 index c78ed05d092b..000000000000 --- a/3rdparty/chipmunk/include/chipmunk/cpShape.h +++ /dev/null @@ -1,199 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/// @defgroup cpShape cpShape -/// The cpShape struct defines the shape of a rigid body. -/// @{ - -/// Point query info struct. -typedef struct cpPointQueryInfo { - /// The nearest shape, NULL if no shape was within range. - const cpShape *shape; - /// The closest point on the shape's surface. (in world space coordinates) - cpVect point; - /// The distance to the point. The distance is negative if the point is inside the shape. - cpFloat distance; - /// The gradient of the signed distance function. - /// The value should be similar to info.p/info.d, but accurate even for very small values of info.d. - cpVect gradient; -} cpPointQueryInfo; - -/// Segment query info struct. -typedef struct cpSegmentQueryInfo { - /// The shape that was hit, or NULL if no collision occured. - const cpShape *shape; - /// The point of impact. - cpVect point; - /// The normal of the surface hit. - cpVect normal; - /// The normalized distance along the query segment in the range [0, 1]. - cpFloat alpha; -} cpSegmentQueryInfo; - -/// Fast collision filtering type that is used to determine if two objects collide before calling collision or query callbacks. -typedef struct cpShapeFilter { - /// Two objects with the same non-zero group value do not collide. - /// This is generally used to group objects in a composite object together to disable self collisions. - cpGroup group; - /// A bitmask of user definable categories that this object belongs to. - /// The category/mask combinations of both objects in a collision must agree for a collision to occur. - cpBitmask categories; - /// A bitmask of user definable category types that this object object collides with. - /// The category/mask combinations of both objects in a collision must agree for a collision to occur. - cpBitmask mask; -} cpShapeFilter; - -/// Collision filter value for a shape that will collide with anything except CP_SHAPE_FILTER_NONE. -static const cpShapeFilter CP_SHAPE_FILTER_ALL = {CP_NO_GROUP, CP_ALL_CATEGORIES, CP_ALL_CATEGORIES}; -/// Collision filter value for a shape that does not collide with anything. -static const cpShapeFilter CP_SHAPE_FILTER_NONE = {CP_NO_GROUP, ~CP_ALL_CATEGORIES, ~CP_ALL_CATEGORIES}; - -/// Create a new collision filter. -static inline cpShapeFilter -cpShapeFilterNew(cpGroup group, cpBitmask categories, cpBitmask mask) -{ - cpShapeFilter filter = {group, categories, mask}; - return filter; -} - -/// Destroy a shape. -CP_EXPORT void cpShapeDestroy(cpShape *shape); -/// Destroy and Free a shape. -CP_EXPORT void cpShapeFree(cpShape *shape); - -/// Update, cache and return the bounding box of a shape based on the body it's attached to. -CP_EXPORT cpBB cpShapeCacheBB(cpShape *shape); -/// Update, cache and return the bounding box of a shape with an explicit transformation. -CP_EXPORT cpBB cpShapeUpdate(cpShape *shape, cpTransform transform); - -/// Perform a nearest point query. It finds the closest point on the surface of shape to a specific point. -/// The value returned is the distance between the points. A negative distance means the point is inside the shape. -CP_EXPORT cpFloat cpShapePointQuery(const cpShape *shape, cpVect p, cpPointQueryInfo *out); - -/// Perform a segment query against a shape. @c info must be a pointer to a valid cpSegmentQueryInfo structure. -CP_EXPORT cpBool cpShapeSegmentQuery(const cpShape *shape, cpVect a, cpVect b, cpFloat radius, cpSegmentQueryInfo *info); - -/// Return contact information about two shapes. -CP_EXPORT cpContactPointSet cpShapesCollide(const cpShape *a, const cpShape *b); - -/// The cpSpace this body is added to. -CP_EXPORT cpSpace* cpShapeGetSpace(const cpShape *shape); - -/// The cpBody this shape is connected to. -CP_EXPORT cpBody* cpShapeGetBody(const cpShape *shape); -/// Set the cpBody this shape is connected to. -/// Can only be used if the shape is not currently added to a space. -CP_EXPORT void cpShapeSetBody(cpShape *shape, cpBody *body); - -/// Get the mass of the shape if you are having Chipmunk calculate mass properties for you. -CP_EXPORT cpFloat cpShapeGetMass(cpShape *shape); -/// Set the mass of this shape to have Chipmunk calculate mass properties for you. -CP_EXPORT void cpShapeSetMass(cpShape *shape, cpFloat mass); - -/// Get the density of the shape if you are having Chipmunk calculate mass properties for you. -CP_EXPORT cpFloat cpShapeGetDensity(cpShape *shape); -/// Set the density of this shape to have Chipmunk calculate mass properties for you. -CP_EXPORT void cpShapeSetDensity(cpShape *shape, cpFloat density); - -/// Get the calculated moment of inertia for this shape. -CP_EXPORT cpFloat cpShapeGetMoment(cpShape *shape); -/// Get the calculated area of this shape. -CP_EXPORT cpFloat cpShapeGetArea(cpShape *shape); -/// Get the centroid of this shape. -CP_EXPORT cpVect cpShapeGetCenterOfGravity(cpShape *shape); - -/// Get the bounding box that contains the shape given it's current position and angle. -CP_EXPORT cpBB cpShapeGetBB(const cpShape *shape); - -/// Get if the shape is set to be a sensor or not. -CP_EXPORT cpBool cpShapeGetSensor(const cpShape *shape); -/// Set if the shape is a sensor or not. -CP_EXPORT void cpShapeSetSensor(cpShape *shape, cpBool sensor); - -/// Get the elasticity of this shape. -CP_EXPORT cpFloat cpShapeGetElasticity(const cpShape *shape); -/// Set the elasticity of this shape. -CP_EXPORT void cpShapeSetElasticity(cpShape *shape, cpFloat elasticity); - -/// Get the friction of this shape. -CP_EXPORT cpFloat cpShapeGetFriction(const cpShape *shape); -/// Set the friction of this shape. -CP_EXPORT void cpShapeSetFriction(cpShape *shape, cpFloat friction); - -/// Get the surface velocity of this shape. -CP_EXPORT cpVect cpShapeGetSurfaceVelocity(const cpShape *shape); -/// Set the surface velocity of this shape. -CP_EXPORT void cpShapeSetSurfaceVelocity(cpShape *shape, cpVect surfaceVelocity); - -/// Get the user definable data pointer of this shape. -CP_EXPORT cpDataPointer cpShapeGetUserData(const cpShape *shape); -/// Set the user definable data pointer of this shape. -CP_EXPORT void cpShapeSetUserData(cpShape *shape, cpDataPointer userData); - -/// Set the collision type of this shape. -CP_EXPORT cpCollisionType cpShapeGetCollisionType(const cpShape *shape); -/// Get the collision type of this shape. -CP_EXPORT void cpShapeSetCollisionType(cpShape *shape, cpCollisionType collisionType); - -/// Get the collision filtering parameters of this shape. -CP_EXPORT cpShapeFilter cpShapeGetFilter(const cpShape *shape); -/// Set the collision filtering parameters of this shape. -CP_EXPORT void cpShapeSetFilter(cpShape *shape, cpShapeFilter filter); - - -/// @} -/// @defgroup cpCircleShape cpCircleShape - -/// Allocate a circle shape. -CP_EXPORT cpCircleShape* cpCircleShapeAlloc(void); -/// Initialize a circle shape. -CP_EXPORT cpCircleShape* cpCircleShapeInit(cpCircleShape *circle, cpBody *body, cpFloat radius, cpVect offset); -/// Allocate and initialize a circle shape. -CP_EXPORT cpShape* cpCircleShapeNew(cpBody *body, cpFloat radius, cpVect offset); - -/// Get the offset of a circle shape. -CP_EXPORT cpVect cpCircleShapeGetOffset(const cpShape *shape); -/// Get the radius of a circle shape. -CP_EXPORT cpFloat cpCircleShapeGetRadius(const cpShape *shape); - -/// @} -/// @defgroup cpSegmentShape cpSegmentShape - -/// Allocate a segment shape. -CP_EXPORT cpSegmentShape* cpSegmentShapeAlloc(void); -/// Initialize a segment shape. -CP_EXPORT cpSegmentShape* cpSegmentShapeInit(cpSegmentShape *seg, cpBody *body, cpVect a, cpVect b, cpFloat radius); -/// Allocate and initialize a segment shape. -CP_EXPORT cpShape* cpSegmentShapeNew(cpBody *body, cpVect a, cpVect b, cpFloat radius); - -/// Let Chipmunk know about the geometry of adjacent segments to avoid colliding with endcaps. -CP_EXPORT void cpSegmentShapeSetNeighbors(cpShape *shape, cpVect prev, cpVect next); - -/// Get the first endpoint of a segment shape. -CP_EXPORT cpVect cpSegmentShapeGetA(const cpShape *shape); -/// Get the second endpoint of a segment shape. -CP_EXPORT cpVect cpSegmentShapeGetB(const cpShape *shape); -/// Get the normal of a segment shape. -CP_EXPORT cpVect cpSegmentShapeGetNormal(const cpShape *shape); -/// Get the first endpoint of a segment shape. -CP_EXPORT cpFloat cpSegmentShapeGetRadius(const cpShape *shape); - -/// @} diff --git a/3rdparty/chipmunk/include/chipmunk/cpSimpleMotor.h b/3rdparty/chipmunk/include/chipmunk/cpSimpleMotor.h deleted file mode 100644 index 811b01143e25..000000000000 --- a/3rdparty/chipmunk/include/chipmunk/cpSimpleMotor.h +++ /dev/null @@ -1,43 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/// @defgroup cpSimpleMotor cpSimpleMotor -/// @{ - -/// Opaque struct type for damped rotary springs. -typedef struct cpSimpleMotor cpSimpleMotor; - -/// Check if a constraint is a damped rotary springs. -CP_EXPORT cpBool cpConstraintIsSimpleMotor(const cpConstraint *constraint); - -/// Allocate a simple motor. -CP_EXPORT cpSimpleMotor* cpSimpleMotorAlloc(void); -/// initialize a simple motor. -CP_EXPORT cpSimpleMotor* cpSimpleMotorInit(cpSimpleMotor *joint, cpBody *a, cpBody *b, cpFloat rate); -/// Allocate and initialize a simple motor. -CP_EXPORT cpConstraint* cpSimpleMotorNew(cpBody *a, cpBody *b, cpFloat rate); - -/// Get the rate of the motor. -CP_EXPORT cpFloat cpSimpleMotorGetRate(const cpConstraint *constraint); -/// Set the rate of the motor. -CP_EXPORT void cpSimpleMotorSetRate(cpConstraint *constraint, cpFloat rate); - -/// @} diff --git a/3rdparty/chipmunk/include/chipmunk/cpSlideJoint.h b/3rdparty/chipmunk/include/chipmunk/cpSlideJoint.h deleted file mode 100644 index c41f9a42eead..000000000000 --- a/3rdparty/chipmunk/include/chipmunk/cpSlideJoint.h +++ /dev/null @@ -1,55 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/// @defgroup cpSlideJoint cpSlideJoint -/// @{ - -/// Check if a constraint is a slide joint. -CP_EXPORT cpBool cpConstraintIsSlideJoint(const cpConstraint *constraint); - -/// Allocate a slide joint. -CP_EXPORT cpSlideJoint* cpSlideJointAlloc(void); -/// Initialize a slide joint. -CP_EXPORT cpSlideJoint* cpSlideJointInit(cpSlideJoint *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat min, cpFloat max); -/// Allocate and initialize a slide joint. -CP_EXPORT cpConstraint* cpSlideJointNew(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat min, cpFloat max); - -/// Get the location of the first anchor relative to the first body. -CP_EXPORT cpVect cpSlideJointGetAnchorA(const cpConstraint *constraint); -/// Set the location of the first anchor relative to the first body. -CP_EXPORT void cpSlideJointSetAnchorA(cpConstraint *constraint, cpVect anchorA); - -/// Get the location of the second anchor relative to the second body. -CP_EXPORT cpVect cpSlideJointGetAnchorB(const cpConstraint *constraint); -/// Set the location of the second anchor relative to the second body. -CP_EXPORT void cpSlideJointSetAnchorB(cpConstraint *constraint, cpVect anchorB); - -/// Get the minimum distance the joint will maintain between the two anchors. -CP_EXPORT cpFloat cpSlideJointGetMin(const cpConstraint *constraint); -/// Set the minimum distance the joint will maintain between the two anchors. -CP_EXPORT void cpSlideJointSetMin(cpConstraint *constraint, cpFloat min); - -/// Get the maximum distance the joint will maintain between the two anchors. -CP_EXPORT cpFloat cpSlideJointGetMax(const cpConstraint *constraint); -/// Set the maximum distance the joint will maintain between the two anchors. -CP_EXPORT void cpSlideJointSetMax(cpConstraint *constraint, cpFloat max); - -/// @} diff --git a/3rdparty/chipmunk/include/chipmunk/cpSpace.h b/3rdparty/chipmunk/include/chipmunk/cpSpace.h deleted file mode 100644 index 7bbabb857d52..000000000000 --- a/3rdparty/chipmunk/include/chipmunk/cpSpace.h +++ /dev/null @@ -1,319 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/// @defgroup cpSpace cpSpace -/// @{ - -//MARK: Definitions - -/// Collision begin event function callback type. -/// Returning false from a begin callback causes the collision to be ignored until -/// the the separate callback is called when the objects stop colliding. -typedef cpBool (*cpCollisionBeginFunc)(cpArbiter *arb, cpSpace *space, cpDataPointer userData); -/// Collision pre-solve event function callback type. -/// Returning false from a pre-step callback causes the collision to be ignored until the next step. -typedef cpBool (*cpCollisionPreSolveFunc)(cpArbiter *arb, cpSpace *space, cpDataPointer userData); -/// Collision post-solve event function callback type. -typedef void (*cpCollisionPostSolveFunc)(cpArbiter *arb, cpSpace *space, cpDataPointer userData); -/// Collision separate event function callback type. -typedef void (*cpCollisionSeparateFunc)(cpArbiter *arb, cpSpace *space, cpDataPointer userData); - -/// Struct that holds function callback pointers to configure custom collision handling. -/// Collision handlers have a pair of types; when a collision occurs between two shapes that have these types, the collision handler functions are triggered. -struct cpCollisionHandler { - /// Collision type identifier of the first shape that this handler recognizes. - /// In the collision handler callback, the shape with this type will be the first argument. Read only. - const cpCollisionType typeA; - /// Collision type identifier of the second shape that this handler recognizes. - /// In the collision handler callback, the shape with this type will be the second argument. Read only. - const cpCollisionType typeB; - /// This function is called when two shapes with types that match this collision handler begin colliding. - cpCollisionBeginFunc beginFunc; - /// This function is called each step when two shapes with types that match this collision handler are colliding. - /// It's called before the collision solver runs so that you can affect a collision's outcome. - cpCollisionPreSolveFunc preSolveFunc; - /// This function is called each step when two shapes with types that match this collision handler are colliding. - /// It's called after the collision solver runs so that you can read back information about the collision to trigger events in your game. - cpCollisionPostSolveFunc postSolveFunc; - /// This function is called when two shapes with types that match this collision handler stop colliding. - cpCollisionSeparateFunc separateFunc; - /// This is a user definable context pointer that is passed to all of the collision handler functions. - cpDataPointer userData; -}; - -// TODO: Make timestep a parameter? - - -//MARK: Memory and Initialization - -/// Allocate a cpSpace. -CP_EXPORT cpSpace* cpSpaceAlloc(void); -/// Initialize a cpSpace. -CP_EXPORT cpSpace* cpSpaceInit(cpSpace *space); -/// Allocate and initialize a cpSpace. -CP_EXPORT cpSpace* cpSpaceNew(void); - -/// Destroy a cpSpace. -CP_EXPORT void cpSpaceDestroy(cpSpace *space); -/// Destroy and free a cpSpace. -CP_EXPORT void cpSpaceFree(cpSpace *space); - - -//MARK: Properties - -/// Number of iterations to use in the impulse solver to solve contacts and other constraints. -CP_EXPORT int cpSpaceGetIterations(const cpSpace *space); -CP_EXPORT void cpSpaceSetIterations(cpSpace *space, int iterations); - -/// Gravity to pass to rigid bodies when integrating velocity. -CP_EXPORT cpVect cpSpaceGetGravity(const cpSpace *space); -CP_EXPORT void cpSpaceSetGravity(cpSpace *space, cpVect gravity); - -/// Damping rate expressed as the fraction of velocity bodies retain each second. -/// A value of 0.9 would mean that each body's velocity will drop 10% per second. -/// The default value is 1.0, meaning no damping is applied. -/// @note This damping value is different than those of cpDampedSpring and cpDampedRotarySpring. -CP_EXPORT cpFloat cpSpaceGetDamping(const cpSpace *space); -CP_EXPORT void cpSpaceSetDamping(cpSpace *space, cpFloat damping); - -/// Speed threshold for a body to be considered idle. -/// The default value of 0 means to let the space guess a good threshold based on gravity. -CP_EXPORT cpFloat cpSpaceGetIdleSpeedThreshold(const cpSpace *space); -CP_EXPORT void cpSpaceSetIdleSpeedThreshold(cpSpace *space, cpFloat idleSpeedThreshold); - -/// Time a group of bodies must remain idle in order to fall asleep. -/// Enabling sleeping also implicitly enables the the contact graph. -/// The default value of INFINITY disables the sleeping algorithm. -CP_EXPORT cpFloat cpSpaceGetSleepTimeThreshold(const cpSpace *space); -CP_EXPORT void cpSpaceSetSleepTimeThreshold(cpSpace *space, cpFloat sleepTimeThreshold); - -/// Amount of encouraged penetration between colliding shapes. -/// Used to reduce oscillating contacts and keep the collision cache warm. -/// Defaults to 0.1. If you have poor simulation quality, -/// increase this number as much as possible without allowing visible amounts of overlap. -CP_EXPORT cpFloat cpSpaceGetCollisionSlop(const cpSpace *space); -CP_EXPORT void cpSpaceSetCollisionSlop(cpSpace *space, cpFloat collisionSlop); - -/// Determines how fast overlapping shapes are pushed apart. -/// Expressed as a fraction of the error remaining after each second. -/// Defaults to pow(1.0 - 0.1, 60.0) meaning that Chipmunk fixes 10% of overlap each frame at 60Hz. -CP_EXPORT cpFloat cpSpaceGetCollisionBias(const cpSpace *space); -CP_EXPORT void cpSpaceSetCollisionBias(cpSpace *space, cpFloat collisionBias); - -/// Number of frames that contact information should persist. -/// Defaults to 3. There is probably never a reason to change this value. -CP_EXPORT cpTimestamp cpSpaceGetCollisionPersistence(const cpSpace *space); -CP_EXPORT void cpSpaceSetCollisionPersistence(cpSpace *space, cpTimestamp collisionPersistence); - -/// User definable data pointer. -/// Generally this points to your game's controller or game state -/// class so you can access it when given a cpSpace reference in a callback. -CP_EXPORT cpDataPointer cpSpaceGetUserData(const cpSpace *space); -CP_EXPORT void cpSpaceSetUserData(cpSpace *space, cpDataPointer userData); - -/// The Space provided static body for a given cpSpace. -/// This is merely provided for convenience and you are not required to use it. -CP_EXPORT cpBody* cpSpaceGetStaticBody(const cpSpace *space); - -/// Returns the current (or most recent) time step used with the given space. -/// Useful from callbacks if your time step is not a compile-time global. -CP_EXPORT cpFloat cpSpaceGetCurrentTimeStep(const cpSpace *space); - -/// returns true from inside a callback when objects cannot be added/removed. -CP_EXPORT cpBool cpSpaceIsLocked(cpSpace *space); - - -//MARK: Collision Handlers - -/// Create or return the existing collision handler that is called for all collisions that are not handled by a more specific collision handler. -CP_EXPORT cpCollisionHandler *cpSpaceAddDefaultCollisionHandler(cpSpace *space); -/// Create or return the existing collision handler for the specified pair of collision types. -/// If wildcard handlers are used with either of the collision types, it's the responibility of the custom handler to invoke the wildcard handlers. -CP_EXPORT cpCollisionHandler *cpSpaceAddCollisionHandler(cpSpace *space, cpCollisionType a, cpCollisionType b); -/// Create or return the existing wildcard collision handler for the specified type. -CP_EXPORT cpCollisionHandler *cpSpaceAddWildcardHandler(cpSpace *space, cpCollisionType type); - - -//MARK: Add/Remove objects - -/// Add a collision shape to the simulation. -/// If the shape is attached to a static body, it will be added as a static shape. -CP_EXPORT cpShape* cpSpaceAddShape(cpSpace *space, cpShape *shape); -/// Add a rigid body to the simulation. -CP_EXPORT cpBody* cpSpaceAddBody(cpSpace *space, cpBody *body); -/// Add a constraint to the simulation. -CP_EXPORT cpConstraint* cpSpaceAddConstraint(cpSpace *space, cpConstraint *constraint); - -/// Remove a collision shape from the simulation. -CP_EXPORT void cpSpaceRemoveShape(cpSpace *space, cpShape *shape); -/// Remove a rigid body from the simulation. -CP_EXPORT void cpSpaceRemoveBody(cpSpace *space, cpBody *body); -/// Remove a constraint from the simulation. -CP_EXPORT void cpSpaceRemoveConstraint(cpSpace *space, cpConstraint *constraint); - -/// Test if a collision shape has been added to the space. -CP_EXPORT cpBool cpSpaceContainsShape(cpSpace *space, cpShape *shape); -/// Test if a rigid body has been added to the space. -CP_EXPORT cpBool cpSpaceContainsBody(cpSpace *space, cpBody *body); -/// Test if a constraint has been added to the space. -CP_EXPORT cpBool cpSpaceContainsConstraint(cpSpace *space, cpConstraint *constraint); - -//MARK: Post-Step Callbacks - -/// Post Step callback function type. -typedef void (*cpPostStepFunc)(cpSpace *space, void *key, void *data); -/// Schedule a post-step callback to be called when cpSpaceStep() finishes. -/// You can only register one callback per unique value for @c key. -/// Returns true only if @c key has never been scheduled before. -/// It's possible to pass @c NULL for @c func if you only want to mark @c key as being used. -CP_EXPORT cpBool cpSpaceAddPostStepCallback(cpSpace *space, cpPostStepFunc func, void *key, void *data); - - -//MARK: Queries - -// TODO: Queries and iterators should take a cpSpace parametery. -// TODO: They should also be abortable. - -/// Nearest point query callback function type. -typedef void (*cpSpacePointQueryFunc)(cpShape *shape, cpVect point, cpFloat distance, cpVect gradient, void *data); -/// Query the space at a point and call @c func for each shape found. -CP_EXPORT void cpSpacePointQuery(cpSpace *space, cpVect point, cpFloat maxDistance, cpShapeFilter filter, cpSpacePointQueryFunc func, void *data); -/// Query the space at a point and return the nearest shape found. Returns NULL if no shapes were found. -CP_EXPORT cpShape *cpSpacePointQueryNearest(cpSpace *space, cpVect point, cpFloat maxDistance, cpShapeFilter filter, cpPointQueryInfo *out); - -/// Segment query callback function type. -typedef void (*cpSpaceSegmentQueryFunc)(cpShape *shape, cpVect point, cpVect normal, cpFloat alpha, void *data); -/// Perform a directed line segment query (like a raycast) against the space calling @c func for each shape intersected. -CP_EXPORT void cpSpaceSegmentQuery(cpSpace *space, cpVect start, cpVect end, cpFloat radius, cpShapeFilter filter, cpSpaceSegmentQueryFunc func, void *data); -/// Perform a directed line segment query (like a raycast) against the space and return the first shape hit. Returns NULL if no shapes were hit. -CP_EXPORT cpShape *cpSpaceSegmentQueryFirst(cpSpace *space, cpVect start, cpVect end, cpFloat radius, cpShapeFilter filter, cpSegmentQueryInfo *out); - -/// Rectangle Query callback function type. -typedef void (*cpSpaceBBQueryFunc)(cpShape *shape, void *data); -/// Perform a fast rectangle query on the space calling @c func for each shape found. -/// Only the shape's bounding boxes are checked for overlap, not their full shape. -CP_EXPORT void cpSpaceBBQuery(cpSpace *space, cpBB bb, cpShapeFilter filter, cpSpaceBBQueryFunc func, void *data); - -/// Shape query callback function type. -typedef void (*cpSpaceShapeQueryFunc)(cpShape *shape, cpContactPointSet *points, void *data); -/// Query a space for any shapes overlapping the given shape and call @c func for each shape found. -CP_EXPORT cpBool cpSpaceShapeQuery(cpSpace *space, cpShape *shape, cpSpaceShapeQueryFunc func, void *data); - - -//MARK: Iteration - -/// Space/body iterator callback function type. -typedef void (*cpSpaceBodyIteratorFunc)(cpBody *body, void *data); -/// Call @c func for each body in the space. -CP_EXPORT void cpSpaceEachBody(cpSpace *space, cpSpaceBodyIteratorFunc func, void *data); - -/// Space/body iterator callback function type. -typedef void (*cpSpaceShapeIteratorFunc)(cpShape *shape, void *data); -/// Call @c func for each shape in the space. -CP_EXPORT void cpSpaceEachShape(cpSpace *space, cpSpaceShapeIteratorFunc func, void *data); - -/// Space/constraint iterator callback function type. -typedef void (*cpSpaceConstraintIteratorFunc)(cpConstraint *constraint, void *data); -/// Call @c func for each shape in the space. -CP_EXPORT void cpSpaceEachConstraint(cpSpace *space, cpSpaceConstraintIteratorFunc func, void *data); - - -//MARK: Indexing - -/// Update the collision detection info for the static shapes in the space. -CP_EXPORT void cpSpaceReindexStatic(cpSpace *space); -/// Update the collision detection data for a specific shape in the space. -CP_EXPORT void cpSpaceReindexShape(cpSpace *space, cpShape *shape); -/// Update the collision detection data for all shapes attached to a body. -CP_EXPORT void cpSpaceReindexShapesForBody(cpSpace *space, cpBody *body); - -/// Switch the space to use a spatial has as it's spatial index. -CP_EXPORT void cpSpaceUseSpatialHash(cpSpace *space, cpFloat dim, int count); - - -//MARK: Time Stepping - -/// Step the space forward in time by @c dt. -CP_EXPORT void cpSpaceStep(cpSpace *space, cpFloat dt); - - -//MARK: Debug API - -#ifndef CP_SPACE_DISABLE_DEBUG_API - -/// Color type to use with the space debug drawing API. -typedef struct cpSpaceDebugColor { - float r, g, b, a; -} cpSpaceDebugColor; - -/// Callback type for a function that draws a filled, stroked circle. -typedef void (*cpSpaceDebugDrawCircleImpl)(cpVect pos, cpFloat angle, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor, cpDataPointer data); -/// Callback type for a function that draws a line segment. -typedef void (*cpSpaceDebugDrawSegmentImpl)(cpVect a, cpVect b, cpSpaceDebugColor color, cpDataPointer data); -/// Callback type for a function that draws a thick line segment. -typedef void (*cpSpaceDebugDrawFatSegmentImpl)(cpVect a, cpVect b, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor, cpDataPointer data); -/// Callback type for a function that draws a convex polygon. -typedef void (*cpSpaceDebugDrawPolygonImpl)(int count, const cpVect *verts, cpFloat radius, cpSpaceDebugColor outlineColor, cpSpaceDebugColor fillColor, cpDataPointer data); -/// Callback type for a function that draws a dot. -typedef void (*cpSpaceDebugDrawDotImpl)(cpFloat size, cpVect pos, cpSpaceDebugColor color, cpDataPointer data); -/// Callback type for a function that returns a color for a given shape. This gives you an opportunity to color shapes based on how they are used in your engine. -typedef cpSpaceDebugColor (*cpSpaceDebugDrawColorForShapeImpl)(cpShape *shape, cpDataPointer data); - -typedef enum cpSpaceDebugDrawFlags { - CP_SPACE_DEBUG_DRAW_SHAPES = 1<<0, - CP_SPACE_DEBUG_DRAW_CONSTRAINTS = 1<<1, - CP_SPACE_DEBUG_DRAW_COLLISION_POINTS = 1<<2, -} cpSpaceDebugDrawFlags; - -/// Struct used with cpSpaceDebugDraw() containing drawing callbacks and other drawing settings. -typedef struct cpSpaceDebugDrawOptions { - /// Function that will be invoked to draw circles. - cpSpaceDebugDrawCircleImpl drawCircle; - /// Function that will be invoked to draw line segments. - cpSpaceDebugDrawSegmentImpl drawSegment; - /// Function that will be invoked to draw thick line segments. - cpSpaceDebugDrawFatSegmentImpl drawFatSegment; - /// Function that will be invoked to draw convex polygons. - cpSpaceDebugDrawPolygonImpl drawPolygon; - /// Function that will be invoked to draw dots. - cpSpaceDebugDrawDotImpl drawDot; - - /// Flags that request which things to draw (collision shapes, constraints, contact points). - cpSpaceDebugDrawFlags flags; - /// Outline color passed to the drawing function. - cpSpaceDebugColor shapeOutlineColor; - /// Function that decides what fill color to draw shapes using. - cpSpaceDebugDrawColorForShapeImpl colorForShape; - /// Color passed to drawing functions for constraints. - cpSpaceDebugColor constraintColor; - /// Color passed to drawing functions for collision points. - cpSpaceDebugColor collisionPointColor; - - /// User defined context pointer passed to all of the callback functions as the 'data' argument. - cpDataPointer data; -} cpSpaceDebugDrawOptions; - -/// Debug draw the current state of the space using the supplied drawing options. -CP_EXPORT void cpSpaceDebugDraw(cpSpace *space, cpSpaceDebugDrawOptions *options); - -#endif - -/// @} diff --git a/3rdparty/chipmunk/include/chipmunk/cpSpatialIndex.h b/3rdparty/chipmunk/include/chipmunk/cpSpatialIndex.h deleted file mode 100644 index 1f7c68ca959d..000000000000 --- a/3rdparty/chipmunk/include/chipmunk/cpSpatialIndex.h +++ /dev/null @@ -1,227 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/** - @defgroup cpSpatialIndex cpSpatialIndex - - Spatial indexes are data structures that are used to accelerate collision detection - and spatial queries. Chipmunk provides a number of spatial index algorithms to pick from - and they are programmed in a generic way so that you can use them for holding more than - just cpShape structs. - - It works by using @c void pointers to the objects you add and using a callback to ask your code - for bounding boxes when it needs them. Several types of queries can be performed an index as well - as reindexing and full collision information. All communication to the spatial indexes is performed - through callback functions. - - Spatial indexes should be treated as opaque structs. - This meanns you shouldn't be reading any of the struct fields. - @{ -*/ - -//MARK: Spatial Index - -/// Spatial index bounding box callback function type. -/// The spatial index calls this function and passes you a pointer to an object you added -/// when it needs to get the bounding box associated with that object. -typedef cpBB (*cpSpatialIndexBBFunc)(void *obj); -/// Spatial index/object iterator callback function type. -typedef void (*cpSpatialIndexIteratorFunc)(void *obj, void *data); -/// Spatial query callback function type. -typedef cpCollisionID (*cpSpatialIndexQueryFunc)(void *obj1, void *obj2, cpCollisionID id, void *data); -/// Spatial segment query callback function type. -typedef cpFloat (*cpSpatialIndexSegmentQueryFunc)(void *obj1, void *obj2, void *data); - - -typedef struct cpSpatialIndexClass cpSpatialIndexClass; -typedef struct cpSpatialIndex cpSpatialIndex; - -/// @private -struct cpSpatialIndex { - cpSpatialIndexClass *klass; - - cpSpatialIndexBBFunc bbfunc; - - cpSpatialIndex *staticIndex, *dynamicIndex; -}; - - -//MARK: Spatial Hash - -typedef struct cpSpaceHash cpSpaceHash; - -/// Allocate a spatial hash. -CP_EXPORT cpSpaceHash* cpSpaceHashAlloc(void); -/// Initialize a spatial hash. -CP_EXPORT cpSpatialIndex* cpSpaceHashInit(cpSpaceHash *hash, cpFloat celldim, int numcells, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex); -/// Allocate and initialize a spatial hash. -CP_EXPORT cpSpatialIndex* cpSpaceHashNew(cpFloat celldim, int cells, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex); - -/// Change the cell dimensions and table size of the spatial hash to tune it. -/// The cell dimensions should roughly match the average size of your objects -/// and the table size should be ~10 larger than the number of objects inserted. -/// Some trial and error is required to find the optimum numbers for efficiency. -CP_EXPORT void cpSpaceHashResize(cpSpaceHash *hash, cpFloat celldim, int numcells); - -//MARK: AABB Tree - -typedef struct cpBBTree cpBBTree; - -/// Allocate a bounding box tree. -CP_EXPORT cpBBTree* cpBBTreeAlloc(void); -/// Initialize a bounding box tree. -CP_EXPORT cpSpatialIndex* cpBBTreeInit(cpBBTree *tree, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex); -/// Allocate and initialize a bounding box tree. -CP_EXPORT cpSpatialIndex* cpBBTreeNew(cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex); - -/// Perform a static top down optimization of the tree. -CP_EXPORT void cpBBTreeOptimize(cpSpatialIndex *index); - -/// Bounding box tree velocity callback function. -/// This function should return an estimate for the object's velocity. -typedef cpVect (*cpBBTreeVelocityFunc)(void *obj); -/// Set the velocity function for the bounding box tree to enable temporal coherence. -CP_EXPORT void cpBBTreeSetVelocityFunc(cpSpatialIndex *index, cpBBTreeVelocityFunc func); - -//MARK: Single Axis Sweep - -typedef struct cpSweep1D cpSweep1D; - -/// Allocate a 1D sort and sweep broadphase. -CP_EXPORT cpSweep1D* cpSweep1DAlloc(void); -/// Initialize a 1D sort and sweep broadphase. -CP_EXPORT cpSpatialIndex* cpSweep1DInit(cpSweep1D *sweep, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex); -/// Allocate and initialize a 1D sort and sweep broadphase. -CP_EXPORT cpSpatialIndex* cpSweep1DNew(cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex); - -//MARK: Spatial Index Implementation - -typedef void (*cpSpatialIndexDestroyImpl)(cpSpatialIndex *index); - -typedef int (*cpSpatialIndexCountImpl)(cpSpatialIndex *index); -typedef void (*cpSpatialIndexEachImpl)(cpSpatialIndex *index, cpSpatialIndexIteratorFunc func, void *data); - -typedef cpBool (*cpSpatialIndexContainsImpl)(cpSpatialIndex *index, void *obj, cpHashValue hashid); -typedef void (*cpSpatialIndexInsertImpl)(cpSpatialIndex *index, void *obj, cpHashValue hashid); -typedef void (*cpSpatialIndexRemoveImpl)(cpSpatialIndex *index, void *obj, cpHashValue hashid); - -typedef void (*cpSpatialIndexReindexImpl)(cpSpatialIndex *index); -typedef void (*cpSpatialIndexReindexObjectImpl)(cpSpatialIndex *index, void *obj, cpHashValue hashid); -typedef void (*cpSpatialIndexReindexQueryImpl)(cpSpatialIndex *index, cpSpatialIndexQueryFunc func, void *data); - -typedef void (*cpSpatialIndexQueryImpl)(cpSpatialIndex *index, void *obj, cpBB bb, cpSpatialIndexQueryFunc func, void *data); -typedef void (*cpSpatialIndexSegmentQueryImpl)(cpSpatialIndex *index, void *obj, cpVect a, cpVect b, cpFloat t_exit, cpSpatialIndexSegmentQueryFunc func, void *data); - -struct cpSpatialIndexClass { - cpSpatialIndexDestroyImpl destroy; - - cpSpatialIndexCountImpl count; - cpSpatialIndexEachImpl each; - - cpSpatialIndexContainsImpl contains; - cpSpatialIndexInsertImpl insert; - cpSpatialIndexRemoveImpl remove; - - cpSpatialIndexReindexImpl reindex; - cpSpatialIndexReindexObjectImpl reindexObject; - cpSpatialIndexReindexQueryImpl reindexQuery; - - cpSpatialIndexQueryImpl query; - cpSpatialIndexSegmentQueryImpl segmentQuery; -}; - -/// Destroy and free a spatial index. -CP_EXPORT void cpSpatialIndexFree(cpSpatialIndex *index); -/// Collide the objects in @c dynamicIndex against the objects in @c staticIndex using the query callback function. -CP_EXPORT void cpSpatialIndexCollideStatic(cpSpatialIndex *dynamicIndex, cpSpatialIndex *staticIndex, cpSpatialIndexQueryFunc func, void *data); - -/// Destroy a spatial index. -static inline void cpSpatialIndexDestroy(cpSpatialIndex *index) -{ - if(index->klass) index->klass->destroy(index); -} - -/// Get the number of objects in the spatial index. -static inline int cpSpatialIndexCount(cpSpatialIndex *index) -{ - return index->klass->count(index); -} - -/// Iterate the objects in the spatial index. @c func will be called once for each object. -static inline void cpSpatialIndexEach(cpSpatialIndex *index, cpSpatialIndexIteratorFunc func, void *data) -{ - index->klass->each(index, func, data); -} - -/// Returns true if the spatial index contains the given object. -/// Most spatial indexes use hashed storage, so you must provide a hash value too. -static inline cpBool cpSpatialIndexContains(cpSpatialIndex *index, void *obj, cpHashValue hashid) -{ - return index->klass->contains(index, obj, hashid); -} - -/// Add an object to a spatial index. -/// Most spatial indexes use hashed storage, so you must provide a hash value too. -static inline void cpSpatialIndexInsert(cpSpatialIndex *index, void *obj, cpHashValue hashid) -{ - index->klass->insert(index, obj, hashid); -} - -/// Remove an object from a spatial index. -/// Most spatial indexes use hashed storage, so you must provide a hash value too. -static inline void cpSpatialIndexRemove(cpSpatialIndex *index, void *obj, cpHashValue hashid) -{ - index->klass->remove(index, obj, hashid); -} - -/// Perform a full reindex of a spatial index. -static inline void cpSpatialIndexReindex(cpSpatialIndex *index) -{ - index->klass->reindex(index); -} - -/// Reindex a single object in the spatial index. -static inline void cpSpatialIndexReindexObject(cpSpatialIndex *index, void *obj, cpHashValue hashid) -{ - index->klass->reindexObject(index, obj, hashid); -} - -/// Perform a rectangle query against the spatial index, calling @c func for each potential match. -static inline void cpSpatialIndexQuery(cpSpatialIndex *index, void *obj, cpBB bb, cpSpatialIndexQueryFunc func, void *data) -{ - index->klass->query(index, obj, bb, func, data); -} - -/// Perform a segment query against the spatial index, calling @c func for each potential match. -static inline void cpSpatialIndexSegmentQuery(cpSpatialIndex *index, void *obj, cpVect a, cpVect b, cpFloat t_exit, cpSpatialIndexSegmentQueryFunc func, void *data) -{ - index->klass->segmentQuery(index, obj, a, b, t_exit, func, data); -} - -/// Simultaneously reindex and find all colliding objects. -/// @c func will be called once for each potentially overlapping pair of objects found. -/// If the spatial index was initialized with a static index, it will collide it's objects against that as well. -static inline void cpSpatialIndexReindexQuery(cpSpatialIndex *index, cpSpatialIndexQueryFunc func, void *data) -{ - index->klass->reindexQuery(index, func, data); -} - -///@} diff --git a/3rdparty/chipmunk/include/chipmunk/cpTransform.h b/3rdparty/chipmunk/include/chipmunk/cpTransform.h deleted file mode 100644 index 4a6256b91564..000000000000 --- a/3rdparty/chipmunk/include/chipmunk/cpTransform.h +++ /dev/null @@ -1,198 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef CHIPMUNK_TRANSFORM_H -#define CHIPMUNK_TRANSFORM_H - -#include "chipmunk_types.h" -#include "cpVect.h" -#include "cpBB.h" - -/// Identity transform matrix. -static const cpTransform cpTransformIdentity = {1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}; - -/// Construct a new transform matrix. -/// (a, b) is the x basis vector. -/// (c, d) is the y basis vector. -/// (tx, ty) is the translation. -static inline cpTransform -cpTransformNew(cpFloat a, cpFloat b, cpFloat c, cpFloat d, cpFloat tx, cpFloat ty) -{ - cpTransform t = {a, b, c, d, tx, ty}; - return t; -} - -/// Construct a new transform matrix in transposed order. -static inline cpTransform -cpTransformNewTranspose(cpFloat a, cpFloat c, cpFloat tx, cpFloat b, cpFloat d, cpFloat ty) -{ - cpTransform t = {a, b, c, d, tx, ty}; - return t; -} - -/// Get the inverse of a transform matrix. -static inline cpTransform -cpTransformInverse(cpTransform t) -{ - cpFloat inv_det = 1.0/(t.a*t.d - t.c*t.b); - return cpTransformNewTranspose( - t.d*inv_det, -t.c*inv_det, (t.c*t.ty - t.tx*t.d)*inv_det, - -t.b*inv_det, t.a*inv_det, (t.tx*t.b - t.a*t.ty)*inv_det - ); -} - -/// Multiply two transformation matrices. -static inline cpTransform -cpTransformMult(cpTransform t1, cpTransform t2) -{ - return cpTransformNewTranspose( - t1.a*t2.a + t1.c*t2.b, t1.a*t2.c + t1.c*t2.d, t1.a*t2.tx + t1.c*t2.ty + t1.tx, - t1.b*t2.a + t1.d*t2.b, t1.b*t2.c + t1.d*t2.d, t1.b*t2.tx + t1.d*t2.ty + t1.ty - ); -} - -/// Transform an absolute point. (i.e. a vertex) -static inline cpVect -cpTransformPoint(cpTransform t, cpVect p) -{ - return cpv(t.a*p.x + t.c*p.y + t.tx, t.b*p.x + t.d*p.y + t.ty); -} - -/// Transform a vector (i.e. a normal) -static inline cpVect -cpTransformVect(cpTransform t, cpVect v) -{ - return cpv(t.a*v.x + t.c*v.y, t.b*v.x + t.d*v.y); -} - -/// Transform a cpBB. -static inline cpBB -cpTransformbBB(cpTransform t, cpBB bb) -{ - cpVect center = cpBBCenter(bb); - cpFloat hw = (bb.r - bb.l)*0.5; - cpFloat hh = (bb.t - bb.b)*0.5; - - cpFloat a = t.a*hw, b = t.c*hh, d = t.b*hw, e = t.d*hh; - cpFloat hw_max = cpfmax(cpfabs(a + b), cpfabs(a - b)); - cpFloat hh_max = cpfmax(cpfabs(d + e), cpfabs(d - e)); - return cpBBNewForExtents(cpTransformPoint(t, center), hw_max, hh_max); -} - -/// Create a transation matrix. -static inline cpTransform -cpTransformTranslate(cpVect translate) -{ - return cpTransformNewTranspose( - 1.0, 0.0, translate.x, - 0.0, 1.0, translate.y - ); -} - -/// Create a scale matrix. -static inline cpTransform -cpTransformScale(cpFloat scaleX, cpFloat scaleY) -{ - return cpTransformNewTranspose( - scaleX, 0.0, 0.0, - 0.0, scaleY, 0.0 - ); -} - -/// Create a rotation matrix. -static inline cpTransform -cpTransformRotate(cpFloat radians) -{ - cpVect rot = cpvforangle(radians); - return cpTransformNewTranspose( - rot.x, -rot.y, 0.0, - rot.y, rot.x, 0.0 - ); -} - -/// Create a rigid transformation matrix. (transation + rotation) -static inline cpTransform -cpTransformRigid(cpVect translate, cpFloat radians) -{ - cpVect rot = cpvforangle(radians); - return cpTransformNewTranspose( - rot.x, -rot.y, translate.x, - rot.y, rot.x, translate.y - ); -} - -/// Fast inverse of a rigid transformation matrix. -static inline cpTransform -cpTransformRigidInverse(cpTransform t) -{ - return cpTransformNewTranspose( - t.d, -t.c, (t.c*t.ty - t.tx*t.d), - -t.b, t.a, (t.tx*t.b - t.a*t.ty) - ); -} - -//MARK: Miscellaneous (but useful) transformation matrices. -// See source for documentation... - -static inline cpTransform -cpTransformWrap(cpTransform outer, cpTransform inner) -{ - return cpTransformMult(cpTransformInverse(outer), cpTransformMult(inner, outer)); -} - -static inline cpTransform -cpTransformWrapInverse(cpTransform outer, cpTransform inner) -{ - return cpTransformMult(outer, cpTransformMult(inner, cpTransformInverse(outer))); -} - -static inline cpTransform -cpTransformOrtho(cpBB bb) -{ - return cpTransformNewTranspose( - 2.0/(bb.r - bb.l), 0.0, -(bb.r + bb.l)/(bb.r - bb.l), - 0.0, 2.0/(bb.t - bb.b), -(bb.t + bb.b)/(bb.t - bb.b) - ); -} - -static inline cpTransform -cpTransformBoneScale(cpVect v0, cpVect v1) -{ - cpVect d = cpvsub(v1, v0); - return cpTransformNewTranspose( - d.x, -d.y, v0.x, - d.y, d.x, v0.y - ); -} - -static inline cpTransform -cpTransformAxialScale(cpVect axis, cpVect pivot, cpFloat scale) -{ - cpFloat A = axis.x*axis.y*(scale - 1.0); - cpFloat B = cpvdot(axis, pivot)*(1.0 - scale); - - return cpTransformNewTranspose( - scale*axis.x*axis.x + axis.y*axis.y, A, axis.x*B, - A, axis.x*axis.x + scale*axis.y*axis.y, axis.y*B - ); -} - -#endif diff --git a/3rdparty/chipmunk/include/chipmunk/cpVect.h b/3rdparty/chipmunk/include/chipmunk/cpVect.h deleted file mode 100644 index 8ec02bdce35c..000000000000 --- a/3rdparty/chipmunk/include/chipmunk/cpVect.h +++ /dev/null @@ -1,230 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef CHIPMUNK_VECT_H -#define CHIPMUNK_VECT_H - -#include "chipmunk_types.h" - -/// @defgroup cpVect cpVect -/// Chipmunk's 2D vector type along with a handy 2D vector math lib. -/// @{ - -/// Constant for the zero vector. -static const cpVect cpvzero = {0.0f,0.0f}; - -/// Convenience constructor for cpVect structs. -static inline cpVect cpv(const cpFloat x, const cpFloat y) -{ - cpVect v = {x, y}; - return v; -} - -/// Check if two vectors are equal. (Be careful when comparing floating point numbers!) -static inline cpBool cpveql(const cpVect v1, const cpVect v2) -{ - return (v1.x == v2.x && v1.y == v2.y); -} - -/// Add two vectors -static inline cpVect cpvadd(const cpVect v1, const cpVect v2) -{ - return cpv(v1.x + v2.x, v1.y + v2.y); -} - -/// Subtract two vectors. -static inline cpVect cpvsub(const cpVect v1, const cpVect v2) -{ - return cpv(v1.x - v2.x, v1.y - v2.y); -} - -/// Negate a vector. -static inline cpVect cpvneg(const cpVect v) -{ - return cpv(-v.x, -v.y); -} - -/// Scalar multiplication. -static inline cpVect cpvmult(const cpVect v, const cpFloat s) -{ - return cpv(v.x*s, v.y*s); -} - -/// Vector dot product. -static inline cpFloat cpvdot(const cpVect v1, const cpVect v2) -{ - return v1.x*v2.x + v1.y*v2.y; -} - -/// 2D vector cross product analog. -/// The cross product of 2D vectors results in a 3D vector with only a z component. -/// This function returns the magnitude of the z value. -static inline cpFloat cpvcross(const cpVect v1, const cpVect v2) -{ - return v1.x*v2.y - v1.y*v2.x; -} - -/// Returns a perpendicular vector. (90 degree rotation) -static inline cpVect cpvperp(const cpVect v) -{ - return cpv(-v.y, v.x); -} - -/// Returns a perpendicular vector. (-90 degree rotation) -static inline cpVect cpvrperp(const cpVect v) -{ - return cpv(v.y, -v.x); -} - -/// Returns the vector projection of v1 onto v2. -static inline cpVect cpvproject(const cpVect v1, const cpVect v2) -{ - return cpvmult(v2, cpvdot(v1, v2)/cpvdot(v2, v2)); -} - -/// Returns the unit length vector for the given angle (in radians). -static inline cpVect cpvforangle(const cpFloat a) -{ - return cpv(cpfcos(a), cpfsin(a)); -} - -/// Returns the angular direction v is pointing in (in radians). -static inline cpFloat cpvtoangle(const cpVect v) -{ - return cpfatan2(v.y, v.x); -} - -/// Uses complex number multiplication to rotate v1 by v2. Scaling will occur if v1 is not a unit vector. -static inline cpVect cpvrotate(const cpVect v1, const cpVect v2) -{ - return cpv(v1.x*v2.x - v1.y*v2.y, v1.x*v2.y + v1.y*v2.x); -} - -/// Inverse of cpvrotate(). -static inline cpVect cpvunrotate(const cpVect v1, const cpVect v2) -{ - return cpv(v1.x*v2.x + v1.y*v2.y, v1.y*v2.x - v1.x*v2.y); -} - -/// Returns the squared length of v. Faster than cpvlength() when you only need to compare lengths. -static inline cpFloat cpvlengthsq(const cpVect v) -{ - return cpvdot(v, v); -} - -/// Returns the length of v. -static inline cpFloat cpvlength(const cpVect v) -{ - return cpfsqrt(cpvdot(v, v)); -} - -/// Linearly interpolate between v1 and v2. -static inline cpVect cpvlerp(const cpVect v1, const cpVect v2, const cpFloat t) -{ - return cpvadd(cpvmult(v1, 1.0f - t), cpvmult(v2, t)); -} - -/// Returns a normalized copy of v. -static inline cpVect cpvnormalize(const cpVect v) -{ - // Neat trick I saw somewhere to avoid div/0. - return cpvmult(v, 1.0f/(cpvlength(v) + CPFLOAT_MIN)); -} - -/// Spherical linearly interpolate between v1 and v2. -static inline cpVect -cpvslerp(const cpVect v1, const cpVect v2, const cpFloat t) -{ - cpFloat dot = cpvdot(cpvnormalize(v1), cpvnormalize(v2)); - cpFloat omega = cpfacos(cpfclamp(dot, -1.0f, 1.0f)); - - if(omega < 1e-3){ - // If the angle between two vectors is very small, lerp instead to avoid precision issues. - return cpvlerp(v1, v2, t); - } else { - cpFloat denom = 1.0f/cpfsin(omega); - return cpvadd(cpvmult(v1, cpfsin((1.0f - t)*omega)*denom), cpvmult(v2, cpfsin(t*omega)*denom)); - } -} - -/// Spherical linearly interpolate between v1 towards v2 by no more than angle a radians -static inline cpVect -cpvslerpconst(const cpVect v1, const cpVect v2, const cpFloat a) -{ - cpFloat dot = cpvdot(cpvnormalize(v1), cpvnormalize(v2)); - cpFloat omega = cpfacos(cpfclamp(dot, -1.0f, 1.0f)); - - return cpvslerp(v1, v2, cpfmin(a, omega)/omega); -} - -/// Clamp v to length len. -static inline cpVect cpvclamp(const cpVect v, const cpFloat len) -{ - return (cpvdot(v,v) > len*len) ? cpvmult(cpvnormalize(v), len) : v; -} - -/// Linearly interpolate between v1 towards v2 by distance d. -static inline cpVect cpvlerpconst(cpVect v1, cpVect v2, cpFloat d) -{ - return cpvadd(v1, cpvclamp(cpvsub(v2, v1), d)); -} - -/// Returns the distance between v1 and v2. -static inline cpFloat cpvdist(const cpVect v1, const cpVect v2) -{ - return cpvlength(cpvsub(v1, v2)); -} - -/// Returns the squared distance between v1 and v2. Faster than cpvdist() when you only need to compare distances. -static inline cpFloat cpvdistsq(const cpVect v1, const cpVect v2) -{ - return cpvlengthsq(cpvsub(v1, v2)); -} - -/// Returns true if the distance between v1 and v2 is less than dist. -static inline cpBool cpvnear(const cpVect v1, const cpVect v2, const cpFloat dist) -{ - return cpvdistsq(v1, v2) < dist*dist; -} - -/// @} - -/// @defgroup cpMat2x2 cpMat2x2 -/// 2x2 matrix type used for tensors and such. -/// @{ - -// NUKE -static inline cpMat2x2 -cpMat2x2New(cpFloat a, cpFloat b, cpFloat c, cpFloat d) -{ - cpMat2x2 m = {a, b, c, d}; - return m; -} - -static inline cpVect -cpMat2x2Transform(cpMat2x2 m, cpVect v) -{ - return cpv(v.x*m.a + v.y*m.b, v.x*m.c + v.y*m.d); -} - -///@} - -#endif diff --git a/3rdparty/chipmunk/src/CMakeLists.txt b/3rdparty/chipmunk/src/CMakeLists.txt deleted file mode 100644 index 3fb3f317626c..000000000000 --- a/3rdparty/chipmunk/src/CMakeLists.txt +++ /dev/null @@ -1,59 +0,0 @@ -file(GLOB chipmunk_source_files "*.c") -file(GLOB chipmunk_public_header "${chipmunk_SOURCE_DIR}/include/chipmunk/*.h") - -include_directories(${chipmunk_SOURCE_DIR}/include) - -# Chipmunk2D 7.0.3 -set(CHIPMUNK_VERSION_MAJOR 7) -set(CHIPMUNK_VERSION_MINOR 0) -set(CHIPMUNK_VERSION_PATCH 3) -set(CHIPMUNK_VERSION "${CHIPMUNK_VERSION_MAJOR}.${CHIPMUNK_VERSION_MINOR}.${CHIPMUNK_VERSION_PATCH}") -message("Configuring Chipmunk2D version ${CHIPMUNK_VERSION}") - - -if(CP_BUILD_SHARED) - add_library(chipmunk SHARED - ${chipmunk_source_files} - ) - # Tell MSVC to compile the code as C++. - if(MSVC) - set_source_files_properties(${chipmunk_source_files} PROPERTIES LANGUAGE CXX) - set_target_properties(chipmunk PROPERTIES LINKER_LANGUAGE CXX) - endif(MSVC) - # set the lib's version number - # But avoid on Android because symlinks to version numbered .so's don't work with Android's Java-side loadLibrary. - if(NOT ANDROID) - set_target_properties(chipmunk PROPERTIES - SOVERSION ${CHIPMUNK_VERSION_MAJOR} - VERSION ${CHIPMUNK_VERSION}) - endif(NOT ANDROID) - if(ANDROID OR UNIX) - # need to explicitly link to the math library because the CMake/Android toolchains may not do it automatically - target_link_libraries(chipmunk m) - endif(ANDROID OR UNIX) - install(TARGETS chipmunk RUNTIME DESTINATION ${BIN_INSTALL_DIR} - LIBRARY DESTINATION ${LIB_INSTALL_DIR} - ARCHIVE DESTINATION ${LIB_INSTALL_DIR}) -endif(CP_BUILD_SHARED) - -if(CP_BUILD_STATIC) - add_library(chipmunk STATIC - ${chipmunk_source_files} - ) - # Tell MSVC to compile the code as C++. - if(MSVC) - set_source_files_properties(${chipmunk_source_files} PROPERTIES LANGUAGE CXX) - set_target_properties(chipmunk PROPERTIES LINKER_LANGUAGE CXX) - endif(MSVC) - # Sets chipmunk to output "libchipmunk.a" not "libchipmunk.a" - set_target_properties(chipmunk PROPERTIES OUTPUT_NAME chipmunk) - if(CP_INSTALL_STATIC) - install(TARGETS chipmunk ARCHIVE DESTINATION ${LIB_INSTALL_DIR}) - endif(CP_INSTALL_STATIC) -endif(CP_BUILD_STATIC) - -if(CP_BUILD_SHARED OR CP_INSTALL_STATIC) - # FIXME: change to PUBLIC_HEADER to allow building frameworks - install(FILES ${chipmunk_public_header} DESTINATION include/chipmunk) - install(FILES ${chipmunk_constraint_header} DESTINATION include/chipmunk/constraints) -endif(CP_BUILD_SHARED OR CP_INSTALL_STATIC) diff --git a/3rdparty/chipmunk/src/chipmunk.c b/3rdparty/chipmunk/src/chipmunk.c deleted file mode 100644 index a6cc9d6d4d16..000000000000 --- a/3rdparty/chipmunk/src/chipmunk.c +++ /dev/null @@ -1,331 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include -#if defined(ANDROID) -# include -#endif - -#include "chipmunk/chipmunk_private.h" - -void -cpMessage(const char *condition, const char *file, int line, int isError, int isHardError, const char *message, ...) -{ - fprintf(stderr, (isError ? "Aborting due to Chipmunk error: " : "Chipmunk warning: ")); - - va_list vargs; - va_start(vargs, message); { -#if defined(ANDROID) - __android_log_print( ANDROID_LOG_INFO, "Chipmunk", "%s(%d)", file, line ); - __android_log_print( ANDROID_LOG_INFO, "Chipmunk", message, vargs ); -#else - vfprintf(stderr, message, vargs); - fprintf(stderr, "\n"); -#endif - } va_end(vargs); - -#if defined(ANDROID) - __android_log_print(ANDROID_LOG_INFO, "Chipmunk", "\tFailed condition: %s\n", condition); - __android_log_print(ANDROID_LOG_INFO, "Chipmunk", "\tSource:%s:%d\n", file, line); -#else - fprintf(stderr, "\tFailed condition: %s\n", condition); - fprintf(stderr, "\tSource:%s:%d\n", file, line); -#endif -} - -#define STR(s) #s -#define XSTR(s) STR(s) - -const char *cpVersionString = XSTR(CP_VERSION_MAJOR) "." XSTR(CP_VERSION_MINOR) "." XSTR(CP_VERSION_RELEASE); - -//MARK: Misc Functions - -cpFloat -cpMomentForCircle(cpFloat m, cpFloat r1, cpFloat r2, cpVect offset) -{ - return m*(0.5f*(r1*r1 + r2*r2) + cpvlengthsq(offset)); -} - -cpFloat -cpAreaForCircle(cpFloat r1, cpFloat r2) -{ - return (cpFloat)CP_PI*cpfabs(r1*r1 - r2*r2); -} - -cpFloat -cpMomentForSegment(cpFloat m, cpVect a, cpVect b, cpFloat r) -{ - cpVect offset = cpvlerp(a, b, 0.5f); - - // This approximates the shape as a box for rounded segments, but it's quite close. - cpFloat length = cpvdist(b, a) + 2.0f*r; - return m*((length*length + 4.0f*r*r)/12.0f + cpvlengthsq(offset)); -} - -cpFloat -cpAreaForSegment(cpVect a, cpVect b, cpFloat r) -{ - return r*((cpFloat)CP_PI*r + 2.0f*cpvdist(a, b)); -} - -cpFloat -cpMomentForPoly(cpFloat m, int count, const cpVect *verts, cpVect offset, cpFloat r) -{ - // TODO account for radius. - if(count == 2) return cpMomentForSegment(m, verts[0], verts[1], 0.0f); - - cpFloat sum1 = 0.0f; - cpFloat sum2 = 0.0f; - for(int i=0; i max.x || (v.x == max.x && v.y > max.y)){ - max = v; - (*end) = i; - } - } -} - -#define SWAP(__A__, __B__) {cpVect __TMP__ = __A__; __A__ = __B__; __B__ = __TMP__;} - -static int -QHullPartition(cpVect *verts, int count, cpVect a, cpVect b, cpFloat tol) -{ - if(count == 0) return 0; - - cpFloat max = 0; - int pivot = 0; - - cpVect delta = cpvsub(b, a); - cpFloat valueTol = tol*cpvlength(delta); - - int head = 0; - for(int tail = count-1; head <= tail;){ - cpFloat value = cpvcross(cpvsub(verts[head], a), delta); - if(value > valueTol){ - if(value > max){ - max = value; - pivot = head; - } - - head++; - } else { - SWAP(verts[head], verts[tail]); - tail--; - } - } - - // move the new pivot to the front if it's not already there. - if(pivot != 0) SWAP(verts[0], verts[pivot]); - return head; -} - -static int -QHullReduce(cpFloat tol, cpVect *verts, int count, cpVect a, cpVect pivot, cpVect b, cpVect *result) -{ - if(count < 0){ - return 0; - } else if(count == 0) { - result[0] = pivot; - return 1; - } else { - int left_count = QHullPartition(verts, count, a, pivot, tol); - int index = QHullReduce(tol, verts + 1, left_count - 1, a, verts[0], pivot, result); - - result[index++] = pivot; - - int right_count = QHullPartition(verts + left_count, count - left_count, pivot, b, tol); - return index + QHullReduce(tol, verts + left_count + 1, right_count - 1, pivot, verts[left_count], b, result + index); - } -} - -// QuickHull seemed like a neat algorithm, and efficient-ish for large input sets. -// My implementation performs an in place reduction using the result array as scratch space. -int -cpConvexHull(int count, const cpVect *verts, cpVect *result, int *first, cpFloat tol) -{ - if(verts != result){ - // Copy the line vertexes into the empty part of the result polyline to use as a scratch buffer. - memcpy(result, verts, count*sizeof(cpVect)); - } - - // Degenerate case, all points are the same. - int start, end; - cpLoopIndexes(verts, count, &start, &end); - if(start == end){ - if(first) (*first) = 0; - return 1; - } - - SWAP(result[0], result[start]); - SWAP(result[1], result[end == 0 ? start : end]); - - cpVect a = result[0]; - cpVect b = result[1]; - - if(first) (*first) = start; - return QHullReduce(tol, result + 2, count - 2, a, b, a, result + 1) + 1; -} - -//MARK: Alternate Block Iterators - -#if defined(__has_extension) -#if __has_extension(blocks) - -static void IteratorFunc(void *ptr, void (^block)(void *ptr)){block(ptr);} - -void cpSpaceEachBody_b(cpSpace *space, void (^block)(cpBody *body)){ - cpSpaceEachBody(space, (cpSpaceBodyIteratorFunc)IteratorFunc, block); -} - -void cpSpaceEachShape_b(cpSpace *space, void (^block)(cpShape *shape)){ - cpSpaceEachShape(space, (cpSpaceShapeIteratorFunc)IteratorFunc, block); -} - -void cpSpaceEachConstraint_b(cpSpace *space, void (^block)(cpConstraint *constraint)){ - cpSpaceEachConstraint(space, (cpSpaceConstraintIteratorFunc)IteratorFunc, block); -} - -static void BodyIteratorFunc(cpBody *body, void *ptr, void (^block)(void *ptr)){block(ptr);} - -void cpBodyEachShape_b(cpBody *body, void (^block)(cpShape *shape)){ - cpBodyEachShape(body, (cpBodyShapeIteratorFunc)BodyIteratorFunc, block); -} - -void cpBodyEachConstraint_b(cpBody *body, void (^block)(cpConstraint *constraint)){ - cpBodyEachConstraint(body, (cpBodyConstraintIteratorFunc)BodyIteratorFunc, block); -} - -void cpBodyEachArbiter_b(cpBody *body, void (^block)(cpArbiter *arbiter)){ - cpBodyEachArbiter(body, (cpBodyArbiterIteratorFunc)BodyIteratorFunc, block); -} - -static void PointQueryIteratorFunc(cpShape *shape, cpVect p, cpFloat d, cpVect g, cpSpacePointQueryBlock block){block(shape, p, d, g);} -void cpSpacePointQuery_b(cpSpace *space, cpVect point, cpFloat maxDistance, cpShapeFilter filter, cpSpacePointQueryBlock block){ - cpSpacePointQuery(space, point, maxDistance, filter, (cpSpacePointQueryFunc)PointQueryIteratorFunc, block); -} - -static void SegmentQueryIteratorFunc(cpShape *shape, cpVect p, cpVect n, cpFloat t, cpSpaceSegmentQueryBlock block){block(shape, p, n, t);} -void cpSpaceSegmentQuery_b(cpSpace *space, cpVect start, cpVect end, cpFloat radius, cpShapeFilter filter, cpSpaceSegmentQueryBlock block){ - cpSpaceSegmentQuery(space, start, end, radius, filter, (cpSpaceSegmentQueryFunc)SegmentQueryIteratorFunc, block); -} - -void cpSpaceBBQuery_b(cpSpace *space, cpBB bb, cpShapeFilter filter, cpSpaceBBQueryBlock block){ - cpSpaceBBQuery(space, bb, filter, (cpSpaceBBQueryFunc)IteratorFunc, block); -} - -static void ShapeQueryIteratorFunc(cpShape *shape, cpContactPointSet *points, cpSpaceShapeQueryBlock block){block(shape, points);} -cpBool cpSpaceShapeQuery_b(cpSpace *space, cpShape *shape, cpSpaceShapeQueryBlock block){ - return cpSpaceShapeQuery(space, shape, (cpSpaceShapeQueryFunc)ShapeQueryIteratorFunc, block); -} - -#endif -#endif - -#include "chipmunk/chipmunk_ffi.h" diff --git a/3rdparty/chipmunk/src/cpArbiter.c b/3rdparty/chipmunk/src/cpArbiter.c deleted file mode 100644 index 5248e6aaec08..000000000000 --- a/3rdparty/chipmunk/src/cpArbiter.c +++ /dev/null @@ -1,498 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk_private.h" - -// TODO: make this generic so I can reuse it for constraints also. -static inline void -unthreadHelper(cpArbiter *arb, cpBody *body) -{ - struct cpArbiterThread *thread = cpArbiterThreadForBody(arb, body); - cpArbiter *prev = thread->prev; - cpArbiter *next = thread->next; - - if(prev){ - cpArbiterThreadForBody(prev, body)->next = next; - } else if(body->arbiterList == arb) { - // IFF prev is NULL and body->arbiterList == arb, is arb at the head of the list. - // This function may be called for an arbiter that was never in a list. - // In that case, we need to protect it from wiping out the body->arbiterList pointer. - body->arbiterList = next; - } - - if(next) cpArbiterThreadForBody(next, body)->prev = prev; - - thread->prev = NULL; - thread->next = NULL; -} - -void -cpArbiterUnthread(cpArbiter *arb) -{ - unthreadHelper(arb, arb->body_a); - unthreadHelper(arb, arb->body_b); -} - -cpBool cpArbiterIsFirstContact(const cpArbiter *arb) -{ - return arb->state == CP_ARBITER_STATE_FIRST_COLLISION; -} - -cpBool cpArbiterIsRemoval(const cpArbiter *arb) -{ - return arb->state == CP_ARBITER_STATE_INVALIDATED; -} - -int cpArbiterGetCount(const cpArbiter *arb) -{ - // Return 0 contacts if we are in a separate callback. - return (arb->state < CP_ARBITER_STATE_CACHED ? arb->count : 0); -} - -cpVect -cpArbiterGetNormal(const cpArbiter *arb) -{ - return cpvmult(arb->n, arb->swapped ? -1.0f : 1.0); -} - -cpVect -cpArbiterGetPointA(const cpArbiter *arb, int i) -{ - cpAssertHard(0 <= i && i < cpArbiterGetCount(arb), "Index error: The specified contact index is invalid for this arbiter"); - return cpvadd(arb->body_a->p, arb->contacts[i].r1); -} - -cpVect -cpArbiterGetPointB(const cpArbiter *arb, int i) -{ - cpAssertHard(0 <= i && i < cpArbiterGetCount(arb), "Index error: The specified contact index is invalid for this arbiter"); - return cpvadd(arb->body_b->p, arb->contacts[i].r2); -} - -cpFloat -cpArbiterGetDepth(const cpArbiter *arb, int i) -{ - cpAssertHard(0 <= i && i < cpArbiterGetCount(arb), "Index error: The specified contact index is invalid for this arbiter"); - - struct cpContact *con = &arb->contacts[i]; - return cpvdot(cpvadd(cpvsub(con->r2, con->r1), cpvsub(arb->body_b->p, arb->body_a->p)), arb->n); -} - -cpContactPointSet -cpArbiterGetContactPointSet(const cpArbiter *arb) -{ - cpContactPointSet set; - set.count = cpArbiterGetCount(arb); - - cpBool swapped = arb->swapped; - cpVect n = arb->n; - set.normal = (swapped ? cpvneg(n) : n); - - for(int i=0; ibody_a->p, arb->contacts[i].r1); - cpVect p2 = cpvadd(arb->body_b->p, arb->contacts[i].r2); - - set.points[i].pointA = (swapped ? p2 : p1); - set.points[i].pointB = (swapped ? p1 : p2); - set.points[i].distance = cpvdot(cpvsub(p2, p1), n); - } - - return set; -} - -void -cpArbiterSetContactPointSet(cpArbiter *arb, cpContactPointSet *set) -{ - int count = set->count; - cpAssertHard(count == arb->count, "The number of contact points cannot be changed."); - - cpBool swapped = arb->swapped; - arb->n = (swapped ? cpvneg(set->normal) : set->normal); - - for(int i=0; ipoints[i].pointA; - cpVect p2 = set->points[i].pointB; - - arb->contacts[i].r1 = cpvsub(swapped ? p2 : p1, arb->body_a->p); - arb->contacts[i].r2 = cpvsub(swapped ? p1 : p2, arb->body_b->p); - } -} - -cpVect -cpArbiterTotalImpulse(const cpArbiter *arb) -{ - struct cpContact *contacts = arb->contacts; - cpVect n = arb->n; - cpVect sum = cpvzero; - - for(int i=0, count=cpArbiterGetCount(arb); ijnAcc, con->jtAcc))); - } - - return (arb->swapped ? sum : cpvneg(sum)); - return cpvzero; -} - -cpFloat -cpArbiterTotalKE(const cpArbiter *arb) -{ - cpFloat eCoef = (1 - arb->e)/(1 + arb->e); - cpFloat sum = 0.0; - - struct cpContact *contacts = arb->contacts; - for(int i=0, count=cpArbiterGetCount(arb); ijnAcc; - cpFloat jtAcc = con->jtAcc; - - sum += eCoef*jnAcc*jnAcc/con->nMass + jtAcc*jtAcc/con->tMass; - } - - return sum; -} - -cpBool -cpArbiterIgnore(cpArbiter *arb) -{ - arb->state = CP_ARBITER_STATE_IGNORE; - return cpFalse; -} - -cpFloat -cpArbiterGetRestitution(const cpArbiter *arb) -{ - return arb->e; -} - -void -cpArbiterSetRestitution(cpArbiter *arb, cpFloat restitution) -{ - arb->e = restitution; -} - -cpFloat -cpArbiterGetFriction(const cpArbiter *arb) -{ - return arb->u; -} - -void -cpArbiterSetFriction(cpArbiter *arb, cpFloat friction) -{ - arb->u = friction; -} - -cpVect -cpArbiterGetSurfaceVelocity(cpArbiter *arb) -{ - return cpvmult(arb->surface_vr, arb->swapped ? -1.0f : 1.0); -} - -void -cpArbiterSetSurfaceVelocity(cpArbiter *arb, cpVect vr) -{ - arb->surface_vr = cpvmult(vr, arb->swapped ? -1.0f : 1.0); -} - -cpDataPointer -cpArbiterGetUserData(const cpArbiter *arb) -{ - return arb->data; -} - -void -cpArbiterSetUserData(cpArbiter *arb, cpDataPointer userData) -{ - arb->data = userData; -} - -void -cpArbiterGetShapes(const cpArbiter *arb, cpShape **a, cpShape **b) -{ - if(arb->swapped){ - (*a) = (cpShape *)arb->b; - (*b) = (cpShape *)arb->a; - } else { - (*a) = (cpShape *)arb->a; - (*b) = (cpShape *)arb->b; - } -} - -void cpArbiterGetBodies(const cpArbiter *arb, cpBody **a, cpBody **b) -{ - CP_ARBITER_GET_SHAPES(arb, shape_a, shape_b); - (*a) = shape_a->body; - (*b) = shape_b->body; -} - -cpBool -cpArbiterCallWildcardBeginA(cpArbiter *arb, cpSpace *space) -{ - cpCollisionHandler *handler = arb->handlerA; - return handler->beginFunc(arb, space, handler->userData); -} - -cpBool -cpArbiterCallWildcardBeginB(cpArbiter *arb, cpSpace *space) -{ - cpCollisionHandler *handler = arb->handlerB; - arb->swapped = !arb->swapped; - cpBool retval = handler->beginFunc(arb, space, handler->userData); - arb->swapped = !arb->swapped; - return retval; -} - -cpBool -cpArbiterCallWildcardPreSolveA(cpArbiter *arb, cpSpace *space) -{ - cpCollisionHandler *handler = arb->handlerA; - return handler->preSolveFunc(arb, space, handler->userData); -} - -cpBool -cpArbiterCallWildcardPreSolveB(cpArbiter *arb, cpSpace *space) -{ - cpCollisionHandler *handler = arb->handlerB; - arb->swapped = !arb->swapped; - cpBool retval = handler->preSolveFunc(arb, space, handler->userData); - arb->swapped = !arb->swapped; - return retval; -} - -void -cpArbiterCallWildcardPostSolveA(cpArbiter *arb, cpSpace *space) -{ - cpCollisionHandler *handler = arb->handlerA; - handler->postSolveFunc(arb, space, handler->userData); -} - -void -cpArbiterCallWildcardPostSolveB(cpArbiter *arb, cpSpace *space) -{ - cpCollisionHandler *handler = arb->handlerB; - arb->swapped = !arb->swapped; - handler->postSolveFunc(arb, space, handler->userData); - arb->swapped = !arb->swapped; -} - -void -cpArbiterCallWildcardSeparateA(cpArbiter *arb, cpSpace *space) -{ - cpCollisionHandler *handler = arb->handlerA; - handler->separateFunc(arb, space, handler->userData); -} - -void -cpArbiterCallWildcardSeparateB(cpArbiter *arb, cpSpace *space) -{ - cpCollisionHandler *handler = arb->handlerB; - arb->swapped = !arb->swapped; - handler->separateFunc(arb, space, handler->userData); - arb->swapped = !arb->swapped; -} - -cpArbiter* -cpArbiterInit(cpArbiter *arb, cpShape *a, cpShape *b) -{ - arb->handler = NULL; - arb->swapped = cpFalse; - - arb->handler = NULL; - arb->handlerA = NULL; - arb->handlerB = NULL; - - arb->e = 0.0f; - arb->u = 0.0f; - arb->surface_vr = cpvzero; - - arb->count = 0; - arb->contacts = NULL; - - arb->a = a; arb->body_a = a->body; - arb->b = b; arb->body_b = b->body; - - arb->thread_a.next = NULL; - arb->thread_b.next = NULL; - arb->thread_a.prev = NULL; - arb->thread_b.prev = NULL; - - arb->stamp = 0; - arb->state = CP_ARBITER_STATE_FIRST_COLLISION; - - arb->data = NULL; - - return arb; -} - -static inline cpCollisionHandler * -cpSpaceLookupHandler(cpSpace *space, cpCollisionType a, cpCollisionType b, cpCollisionHandler *defaultValue) -{ - cpCollisionType types[] = {a, b}; - cpCollisionHandler *handler = (cpCollisionHandler *)cpHashSetFind(space->collisionHandlers, CP_HASH_PAIR(a, b), types); - return (handler ? handler : defaultValue); -} - -void -cpArbiterUpdate(cpArbiter *arb, struct cpCollisionInfo *info, cpSpace *space) -{ - const cpShape *a = info->a, *b = info->b; - - // For collisions between two similar primitive types, the order could have been swapped since the last frame. - arb->a = a; arb->body_a = a->body; - arb->b = b; arb->body_b = b->body; - - // Iterate over the possible pairs to look for hash value matches. - for(int i=0; icount; i++){ - struct cpContact *con = &info->arr[i]; - - // r1 and r2 store absolute offsets at init time. - // Need to convert them to relative offsets. - con->r1 = cpvsub(con->r1, a->body->p); - con->r2 = cpvsub(con->r2, b->body->p); - - // Cached impulses are not zeroed at init time. - con->jnAcc = con->jtAcc = 0.0f; - - for(int j=0; jcount; j++){ - struct cpContact *old = &arb->contacts[j]; - - // This could trigger false positives, but is fairly unlikely nor serious if it does. - if(con->hash == old->hash){ - // Copy the persistant contact information. - con->jnAcc = old->jnAcc; - con->jtAcc = old->jtAcc; - } - } - } - - arb->contacts = info->arr; - arb->count = info->count; - arb->n = info->n; - - arb->e = a->e * b->e; - arb->u = a->u * b->u; - - cpVect surface_vr = cpvsub(b->surfaceV, a->surfaceV); - arb->surface_vr = cpvsub(surface_vr, cpvmult(info->n, cpvdot(surface_vr, info->n))); - - cpCollisionType typeA = info->a->type, typeB = info->b->type; - cpCollisionHandler *defaultHandler = &space->defaultHandler; - cpCollisionHandler *handler = arb->handler = cpSpaceLookupHandler(space, typeA, typeB, defaultHandler); - - // Check if the types match, but don't swap for a default handler which use the wildcard for type A. - cpBool swapped = arb->swapped = (typeA != handler->typeA && handler->typeA != CP_WILDCARD_COLLISION_TYPE); - - if(handler != defaultHandler || space->usesWildcards){ - // The order of the main handler swaps the wildcard handlers too. Uffda. - arb->handlerA = cpSpaceLookupHandler(space, (swapped ? typeB : typeA), CP_WILDCARD_COLLISION_TYPE, &cpCollisionHandlerDoNothing); - arb->handlerB = cpSpaceLookupHandler(space, (swapped ? typeA : typeB), CP_WILDCARD_COLLISION_TYPE, &cpCollisionHandlerDoNothing); - } - - // mark it as new if it's been cached - if(arb->state == CP_ARBITER_STATE_CACHED) arb->state = CP_ARBITER_STATE_FIRST_COLLISION; -} - -void -cpArbiterPreStep(cpArbiter *arb, cpFloat dt, cpFloat slop, cpFloat bias) -{ - cpBody *a = arb->body_a; - cpBody *b = arb->body_b; - cpVect n = arb->n; - cpVect body_delta = cpvsub(b->p, a->p); - - for(int i=0; icount; i++){ - struct cpContact *con = &arb->contacts[i]; - - // Calculate the mass normal and mass tangent. - con->nMass = 1.0f/k_scalar(a, b, con->r1, con->r2, n); - con->tMass = 1.0f/k_scalar(a, b, con->r1, con->r2, cpvperp(n)); - - // Calculate the target bias velocity. - cpFloat dist = cpvdot(cpvadd(cpvsub(con->r2, con->r1), body_delta), n); - con->bias = -bias*cpfmin(0.0f, dist + slop)/dt; - con->jBias = 0.0f; - - // Calculate the target bounce velocity. - con->bounce = normal_relative_velocity(a, b, con->r1, con->r2, n)*arb->e; - } -} - -void -cpArbiterApplyCachedImpulse(cpArbiter *arb, cpFloat dt_coef) -{ - if(cpArbiterIsFirstContact(arb)) return; - - cpBody *a = arb->body_a; - cpBody *b = arb->body_b; - cpVect n = arb->n; - - for(int i=0; icount; i++){ - struct cpContact *con = &arb->contacts[i]; - cpVect j = cpvrotate(n, cpv(con->jnAcc, con->jtAcc)); - apply_impulses(a, b, con->r1, con->r2, cpvmult(j, dt_coef)); - } -} - -// TODO: is it worth splitting velocity/position correction? - -void -cpArbiterApplyImpulse(cpArbiter *arb) -{ - cpBody *a = arb->body_a; - cpBody *b = arb->body_b; - cpVect n = arb->n; - cpVect surface_vr = arb->surface_vr; - cpFloat friction = arb->u; - - for(int i=0; icount; i++){ - struct cpContact *con = &arb->contacts[i]; - cpFloat nMass = con->nMass; - cpVect r1 = con->r1; - cpVect r2 = con->r2; - - cpVect vb1 = cpvadd(a->v_bias, cpvmult(cpvperp(r1), a->w_bias)); - cpVect vb2 = cpvadd(b->v_bias, cpvmult(cpvperp(r2), b->w_bias)); - cpVect vr = cpvadd(relative_velocity(a, b, r1, r2), surface_vr); - - cpFloat vbn = cpvdot(cpvsub(vb2, vb1), n); - cpFloat vrn = cpvdot(vr, n); - cpFloat vrt = cpvdot(vr, cpvperp(n)); - - cpFloat jbn = (con->bias - vbn)*nMass; - cpFloat jbnOld = con->jBias; - con->jBias = cpfmax(jbnOld + jbn, 0.0f); - - cpFloat jn = -(con->bounce + vrn)*nMass; - cpFloat jnOld = con->jnAcc; - con->jnAcc = cpfmax(jnOld + jn, 0.0f); - - cpFloat jtMax = friction*con->jnAcc; - cpFloat jt = -vrt*con->tMass; - cpFloat jtOld = con->jtAcc; - con->jtAcc = cpfclamp(jtOld + jt, -jtMax, jtMax); - - apply_bias_impulses(a, b, r1, r2, cpvmult(n, con->jBias - jbnOld)); - apply_impulses(a, b, r1, r2, cpvrotate(n, cpv(con->jnAcc - jnOld, con->jtAcc - jtOld))); - } -} diff --git a/3rdparty/chipmunk/src/cpArray.c b/3rdparty/chipmunk/src/cpArray.c deleted file mode 100644 index a1f8df526fc4..000000000000 --- a/3rdparty/chipmunk/src/cpArray.c +++ /dev/null @@ -1,101 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include - -#include "chipmunk/chipmunk_private.h" - - -cpArray * -cpArrayNew(int size) -{ - cpArray *arr = (cpArray *)cpcalloc(1, sizeof(cpArray)); - - arr->num = 0; - arr->max = (size ? size : 4); - arr->arr = (void **)cpcalloc(arr->max, sizeof(void*)); - - return arr; -} - -void -cpArrayFree(cpArray *arr) -{ - if(arr){ - cpfree(arr->arr); - arr->arr = NULL; - - cpfree(arr); - } -} - -void -cpArrayPush(cpArray *arr, void *object) -{ - if(arr->num == arr->max){ - arr->max = 3*(arr->max + 1)/2; - arr->arr = (void **)cprealloc(arr->arr, arr->max*sizeof(void*)); - } - - arr->arr[arr->num] = object; - arr->num++; -} - -void * -cpArrayPop(cpArray *arr) -{ - arr->num--; - - void *value = arr->arr[arr->num]; - arr->arr[arr->num] = NULL; - - return value; -} - -void -cpArrayDeleteObj(cpArray *arr, void *obj) -{ - for(int i=0; inum; i++){ - if(arr->arr[i] == obj){ - arr->num--; - - arr->arr[i] = arr->arr[arr->num]; - arr->arr[arr->num] = NULL; - - return; - } - } -} - -void -cpArrayFreeEach(cpArray *arr, void (freeFunc)(void*)) -{ - for(int i=0; inum; i++) freeFunc(arr->arr[i]); -} - -cpBool -cpArrayContains(cpArray *arr, void *ptr) -{ - for(int i=0; inum; i++) - if(arr->arr[i] == ptr) return cpTrue; - - return cpFalse; -} diff --git a/3rdparty/chipmunk/src/cpBBTree.c b/3rdparty/chipmunk/src/cpBBTree.c deleted file mode 100644 index 2cef7bc755c8..000000000000 --- a/3rdparty/chipmunk/src/cpBBTree.c +++ /dev/null @@ -1,896 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "stdlib.h" -#include "stdio.h" - -#include "chipmunk/chipmunk_private.h" - -static inline cpSpatialIndexClass *Klass(void); - -typedef struct Node Node; -typedef struct Pair Pair; - -struct cpBBTree { - cpSpatialIndex spatialIndex; - cpBBTreeVelocityFunc velocityFunc; - - cpHashSet *leaves; - Node *root; - - Node *pooledNodes; - Pair *pooledPairs; - cpArray *allocatedBuffers; - - cpTimestamp stamp; -}; - -struct Node { - void *obj; - cpBB bb; - Node *parent; - - union { - // Internal nodes - struct { Node *a, *b; } children; - - // Leaves - struct { - cpTimestamp stamp; - Pair *pairs; - } leaf; - } node; -}; - -// Can't use anonymous unions and still get good x-compiler compatability -#define A node.children.a -#define B node.children.b -#define STAMP node.leaf.stamp -#define PAIRS node.leaf.pairs - -typedef struct Thread { - Pair *prev; - Node *leaf; - Pair *next; -} Thread; - -struct Pair { - Thread a, b; - cpCollisionID id; -}; - -//MARK: Misc Functions - -static inline cpBB -GetBB(cpBBTree *tree, void *obj) -{ - cpBB bb = tree->spatialIndex.bbfunc(obj); - - cpBBTreeVelocityFunc velocityFunc = tree->velocityFunc; - if(velocityFunc){ - cpFloat coef = 0.1f; - cpFloat x = (bb.r - bb.l)*coef; - cpFloat y = (bb.t - bb.b)*coef; - - cpVect v = cpvmult(velocityFunc(obj), 0.1f); - return cpBBNew(bb.l + cpfmin(-x, v.x), bb.b + cpfmin(-y, v.y), bb.r + cpfmax(x, v.x), bb.t + cpfmax(y, v.y)); - } else { - return bb; - } -} - -static inline cpBBTree * -GetTree(cpSpatialIndex *index) -{ - return (index && index->klass == Klass() ? (cpBBTree *)index : NULL); -} - -static inline Node * -GetRootIfTree(cpSpatialIndex *index){ - return (index && index->klass == Klass() ? ((cpBBTree *)index)->root : NULL); -} - -static inline cpBBTree * -GetMasterTree(cpBBTree *tree) -{ - cpBBTree *dynamicTree = GetTree(tree->spatialIndex.dynamicIndex); - return (dynamicTree ? dynamicTree : tree); -} - -static inline void -IncrementStamp(cpBBTree *tree) -{ - cpBBTree *dynamicTree = GetTree(tree->spatialIndex.dynamicIndex); - if(dynamicTree){ - dynamicTree->stamp++; - } else { - tree->stamp++; - } -} - -//MARK: Pair/Thread Functions - -static void -PairRecycle(cpBBTree *tree, Pair *pair) -{ - // Share the pool of the master tree. - // TODO: would be lovely to move the pairs stuff into an external data structure. - tree = GetMasterTree(tree); - - pair->a.next = tree->pooledPairs; - tree->pooledPairs = pair; -} - -static Pair * -PairFromPool(cpBBTree *tree) -{ - // Share the pool of the master tree. - // TODO: would be lovely to move the pairs stuff into an external data structure. - tree = GetMasterTree(tree); - - Pair *pair = tree->pooledPairs; - - if(pair){ - tree->pooledPairs = pair->a.next; - return pair; - } else { - // Pool is exhausted, make more - int count = CP_BUFFER_BYTES/sizeof(Pair); - cpAssertHard(count, "Internal Error: Buffer size is too small."); - - Pair *buffer = (Pair *)cpcalloc(1, CP_BUFFER_BYTES); - cpArrayPush(tree->allocatedBuffers, buffer); - - // push all but the first one, return the first instead - for(int i=1; ia.leaf == thread.leaf) next->a.prev = prev; else next->b.prev = prev; - } - - if(prev){ - if(prev->a.leaf == thread.leaf) prev->a.next = next; else prev->b.next = next; - } else { - thread.leaf->PAIRS = next; - } -} - -static void -PairsClear(Node *leaf, cpBBTree *tree) -{ - Pair *pair = leaf->PAIRS; - leaf->PAIRS = NULL; - - while(pair){ - if(pair->a.leaf == leaf){ - Pair *next = pair->a.next; - ThreadUnlink(pair->b); - PairRecycle(tree, pair); - pair = next; - } else { - Pair *next = pair->b.next; - ThreadUnlink(pair->a); - PairRecycle(tree, pair); - pair = next; - } - } -} - -static void -PairInsert(Node *a, Node *b, cpBBTree *tree) -{ - Pair *nextA = a->PAIRS, *nextB = b->PAIRS; - Pair *pair = PairFromPool(tree); - Pair temp = {{NULL, a, nextA},{NULL, b, nextB}, 0}; - - a->PAIRS = b->PAIRS = pair; - *pair = temp; - - if(nextA){ - if(nextA->a.leaf == a) nextA->a.prev = pair; else nextA->b.prev = pair; - } - - if(nextB){ - if(nextB->a.leaf == b) nextB->a.prev = pair; else nextB->b.prev = pair; - } -} - - -//MARK: Node Functions - -static void -NodeRecycle(cpBBTree *tree, Node *node) -{ - node->parent = tree->pooledNodes; - tree->pooledNodes = node; -} - -static Node * -NodeFromPool(cpBBTree *tree) -{ - Node *node = tree->pooledNodes; - - if(node){ - tree->pooledNodes = node->parent; - return node; - } else { - // Pool is exhausted, make more - int count = CP_BUFFER_BYTES/sizeof(Node); - cpAssertHard(count, "Internal Error: Buffer size is too small."); - - Node *buffer = (Node *)cpcalloc(1, CP_BUFFER_BYTES); - cpArrayPush(tree->allocatedBuffers, buffer); - - // push all but the first one, return the first instead - for(int i=1; iA = value; - value->parent = node; -} - -static inline void -NodeSetB(Node *node, Node *value) -{ - node->B = value; - value->parent = node; -} - -static Node * -NodeNew(cpBBTree *tree, Node *a, Node *b) -{ - Node *node = NodeFromPool(tree); - - node->obj = NULL; - node->bb = cpBBMerge(a->bb, b->bb); - node->parent = NULL; - - NodeSetA(node, a); - NodeSetB(node, b); - - return node; -} - -static inline cpBool -NodeIsLeaf(Node *node) -{ - return (node->obj != NULL); -} - -static inline Node * -NodeOther(Node *node, Node *child) -{ - return (node->A == child ? node->B : node->A); -} - -static inline void -NodeReplaceChild(Node *parent, Node *child, Node *value, cpBBTree *tree) -{ - cpAssertSoft(!NodeIsLeaf(parent), "Internal Error: Cannot replace child of a leaf."); - cpAssertSoft(child == parent->A || child == parent->B, "Internal Error: Node is not a child of parent."); - - if(parent->A == child){ - NodeRecycle(tree, parent->A); - NodeSetA(parent, value); - } else { - NodeRecycle(tree, parent->B); - NodeSetB(parent, value); - } - - for(Node *node=parent; node; node = node->parent){ - node->bb = cpBBMerge(node->A->bb, node->B->bb); - } -} - -//MARK: Subtree Functions - -static inline cpFloat -cpBBProximity(cpBB a, cpBB b) -{ - return cpfabs(a.l + a.r - b.l - b.r) + cpfabs(a.b + a.t - b.b - b.t); -} - -static Node * -SubtreeInsert(Node *subtree, Node *leaf, cpBBTree *tree) -{ - if(subtree == NULL){ - return leaf; - } else if(NodeIsLeaf(subtree)){ - return NodeNew(tree, leaf, subtree); - } else { - cpFloat cost_a = cpBBArea(subtree->B->bb) + cpBBMergedArea(subtree->A->bb, leaf->bb); - cpFloat cost_b = cpBBArea(subtree->A->bb) + cpBBMergedArea(subtree->B->bb, leaf->bb); - - if(cost_a == cost_b){ - cost_a = cpBBProximity(subtree->A->bb, leaf->bb); - cost_b = cpBBProximity(subtree->B->bb, leaf->bb); - } - - if(cost_b < cost_a){ - NodeSetB(subtree, SubtreeInsert(subtree->B, leaf, tree)); - } else { - NodeSetA(subtree, SubtreeInsert(subtree->A, leaf, tree)); - } - - subtree->bb = cpBBMerge(subtree->bb, leaf->bb); - return subtree; - } -} - -static void -SubtreeQuery(Node *subtree, void *obj, cpBB bb, cpSpatialIndexQueryFunc func, void *data) -{ - if(cpBBIntersects(subtree->bb, bb)){ - if(NodeIsLeaf(subtree)){ - func(obj, subtree->obj, 0, data); - } else { - SubtreeQuery(subtree->A, obj, bb, func, data); - SubtreeQuery(subtree->B, obj, bb, func, data); - } - } -} - - -static cpFloat -SubtreeSegmentQuery(Node *subtree, void *obj, cpVect a, cpVect b, cpFloat t_exit, cpSpatialIndexSegmentQueryFunc func, void *data) -{ - if(NodeIsLeaf(subtree)){ - return func(obj, subtree->obj, data); - } else { - cpFloat t_a = cpBBSegmentQuery(subtree->A->bb, a, b); - cpFloat t_b = cpBBSegmentQuery(subtree->B->bb, a, b); - - if(t_a < t_b){ - if(t_a < t_exit) t_exit = cpfmin(t_exit, SubtreeSegmentQuery(subtree->A, obj, a, b, t_exit, func, data)); - if(t_b < t_exit) t_exit = cpfmin(t_exit, SubtreeSegmentQuery(subtree->B, obj, a, b, t_exit, func, data)); - } else { - if(t_b < t_exit) t_exit = cpfmin(t_exit, SubtreeSegmentQuery(subtree->B, obj, a, b, t_exit, func, data)); - if(t_a < t_exit) t_exit = cpfmin(t_exit, SubtreeSegmentQuery(subtree->A, obj, a, b, t_exit, func, data)); - } - - return t_exit; - } -} - -static void -SubtreeRecycle(cpBBTree *tree, Node *node) -{ - if(!NodeIsLeaf(node)){ - SubtreeRecycle(tree, node->A); - SubtreeRecycle(tree, node->B); - NodeRecycle(tree, node); - } -} - -static inline Node * -SubtreeRemove(Node *subtree, Node *leaf, cpBBTree *tree) -{ - if(leaf == subtree){ - return NULL; - } else { - Node *parent = leaf->parent; - if(parent == subtree){ - Node *other = NodeOther(subtree, leaf); - other->parent = subtree->parent; - NodeRecycle(tree, subtree); - return other; - } else { - NodeReplaceChild(parent->parent, parent, NodeOther(parent, leaf), tree); - return subtree; - } - } -} - -//MARK: Marking Functions - -typedef struct MarkContext { - cpBBTree *tree; - Node *staticRoot; - cpSpatialIndexQueryFunc func; - void *data; -} MarkContext; - -static void -MarkLeafQuery(Node *subtree, Node *leaf, cpBool left, MarkContext *context) -{ - if(cpBBIntersects(leaf->bb, subtree->bb)){ - if(NodeIsLeaf(subtree)){ - if(left){ - PairInsert(leaf, subtree, context->tree); - } else { - if(subtree->STAMP < leaf->STAMP) PairInsert(subtree, leaf, context->tree); - context->func(leaf->obj, subtree->obj, 0, context->data); - } - } else { - MarkLeafQuery(subtree->A, leaf, left, context); - MarkLeafQuery(subtree->B, leaf, left, context); - } - } -} - -static void -MarkLeaf(Node *leaf, MarkContext *context) -{ - cpBBTree *tree = context->tree; - if(leaf->STAMP == GetMasterTree(tree)->stamp){ - Node *staticRoot = context->staticRoot; - if(staticRoot) MarkLeafQuery(staticRoot, leaf, cpFalse, context); - - for(Node *node = leaf; node->parent; node = node->parent){ - if(node == node->parent->A){ - MarkLeafQuery(node->parent->B, leaf, cpTrue, context); - } else { - MarkLeafQuery(node->parent->A, leaf, cpFalse, context); - } - } - } else { - Pair *pair = leaf->PAIRS; - while(pair){ - if(leaf == pair->b.leaf){ - pair->id = context->func(pair->a.leaf->obj, leaf->obj, pair->id, context->data); - pair = pair->b.next; - } else { - pair = pair->a.next; - } - } - } -} - -static void -MarkSubtree(Node *subtree, MarkContext *context) -{ - if(NodeIsLeaf(subtree)){ - MarkLeaf(subtree, context); - } else { - MarkSubtree(subtree->A, context); - MarkSubtree(subtree->B, context); // TODO: Force TCO here? - } -} - -//MARK: Leaf Functions - -static Node * -LeafNew(cpBBTree *tree, void *obj, cpBB bb) -{ - Node *node = NodeFromPool(tree); - node->obj = obj; - node->bb = GetBB(tree, obj); - - node->parent = NULL; - node->STAMP = 0; - node->PAIRS = NULL; - - return node; -} - -static cpBool -LeafUpdate(Node *leaf, cpBBTree *tree) -{ - Node *root = tree->root; - cpBB bb = tree->spatialIndex.bbfunc(leaf->obj); - - if(!cpBBContainsBB(leaf->bb, bb)){ - leaf->bb = GetBB(tree, leaf->obj); - - root = SubtreeRemove(root, leaf, tree); - tree->root = SubtreeInsert(root, leaf, tree); - - PairsClear(leaf, tree); - leaf->STAMP = GetMasterTree(tree)->stamp; - - return cpTrue; - } else { - return cpFalse; - } -} - -static cpCollisionID VoidQueryFunc(void *obj1, void *obj2, cpCollisionID id, void *data){return id;} - -static void -LeafAddPairs(Node *leaf, cpBBTree *tree) -{ - cpSpatialIndex *dynamicIndex = tree->spatialIndex.dynamicIndex; - if(dynamicIndex){ - Node *dynamicRoot = GetRootIfTree(dynamicIndex); - if(dynamicRoot){ - cpBBTree *dynamicTree = GetTree(dynamicIndex); - MarkContext context = {dynamicTree, NULL, NULL, NULL}; - MarkLeafQuery(dynamicRoot, leaf, cpTrue, &context); - } - } else { - Node *staticRoot = GetRootIfTree(tree->spatialIndex.staticIndex); - MarkContext context = {tree, staticRoot, VoidQueryFunc, NULL}; - MarkLeaf(leaf, &context); - } -} - -//MARK: Memory Management Functions - -cpBBTree * -cpBBTreeAlloc(void) -{ - return (cpBBTree *)cpcalloc(1, sizeof(cpBBTree)); -} - -static int -leafSetEql(void *obj, Node *node) -{ - return (obj == node->obj); -} - -static void * -leafSetTrans(void *obj, cpBBTree *tree) -{ - return LeafNew(tree, obj, tree->spatialIndex.bbfunc(obj)); -} - -cpSpatialIndex * -cpBBTreeInit(cpBBTree *tree, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex) -{ - cpSpatialIndexInit((cpSpatialIndex *)tree, Klass(), bbfunc, staticIndex); - - tree->velocityFunc = NULL; - - tree->leaves = cpHashSetNew(0, (cpHashSetEqlFunc)leafSetEql); - tree->root = NULL; - - tree->pooledNodes = NULL; - tree->allocatedBuffers = cpArrayNew(0); - - tree->stamp = 0; - - return (cpSpatialIndex *)tree; -} - -void -cpBBTreeSetVelocityFunc(cpSpatialIndex *index, cpBBTreeVelocityFunc func) -{ - if(index->klass != Klass()){ - cpAssertWarn(cpFalse, "Ignoring cpBBTreeSetVelocityFunc() call to non-tree spatial index."); - return; - } - - ((cpBBTree *)index)->velocityFunc = func; -} - -cpSpatialIndex * -cpBBTreeNew(cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex) -{ - return cpBBTreeInit(cpBBTreeAlloc(), bbfunc, staticIndex); -} - -static void -cpBBTreeDestroy(cpBBTree *tree) -{ - cpHashSetFree(tree->leaves); - - if(tree->allocatedBuffers) cpArrayFreeEach(tree->allocatedBuffers, cpfree); - cpArrayFree(tree->allocatedBuffers); -} - -//MARK: Insert/Remove - -static void -cpBBTreeInsert(cpBBTree *tree, void *obj, cpHashValue hashid) -{ - Node *leaf = (Node *)cpHashSetInsert(tree->leaves, hashid, obj, (cpHashSetTransFunc)leafSetTrans, tree); - - Node *root = tree->root; - tree->root = SubtreeInsert(root, leaf, tree); - - leaf->STAMP = GetMasterTree(tree)->stamp; - LeafAddPairs(leaf, tree); - IncrementStamp(tree); -} - -static void -cpBBTreeRemove(cpBBTree *tree, void *obj, cpHashValue hashid) -{ - Node *leaf = (Node *)cpHashSetRemove(tree->leaves, hashid, obj); - - tree->root = SubtreeRemove(tree->root, leaf, tree); - PairsClear(leaf, tree); - NodeRecycle(tree, leaf); -} - -static cpBool -cpBBTreeContains(cpBBTree *tree, void *obj, cpHashValue hashid) -{ - return (cpHashSetFind(tree->leaves, hashid, obj) != NULL); -} - -//MARK: Reindex - -static void LeafUpdateWrap(Node *leaf, cpBBTree *tree) {LeafUpdate(leaf, tree);} - -static void -cpBBTreeReindexQuery(cpBBTree *tree, cpSpatialIndexQueryFunc func, void *data) -{ - if(!tree->root) return; - - // LeafUpdate() may modify tree->root. Don't cache it. - cpHashSetEach(tree->leaves, (cpHashSetIteratorFunc)LeafUpdateWrap, tree); - - cpSpatialIndex *staticIndex = tree->spatialIndex.staticIndex; - Node *staticRoot = (staticIndex && staticIndex->klass == Klass() ? ((cpBBTree *)staticIndex)->root : NULL); - - MarkContext context = {tree, staticRoot, func, data}; - MarkSubtree(tree->root, &context); - if(staticIndex && !staticRoot) cpSpatialIndexCollideStatic((cpSpatialIndex *)tree, staticIndex, func, data); - - IncrementStamp(tree); -} - -static void -cpBBTreeReindex(cpBBTree *tree) -{ - cpBBTreeReindexQuery(tree, VoidQueryFunc, NULL); -} - -static void -cpBBTreeReindexObject(cpBBTree *tree, void *obj, cpHashValue hashid) -{ - Node *leaf = (Node *)cpHashSetFind(tree->leaves, hashid, obj); - if(leaf){ - if(LeafUpdate(leaf, tree)) LeafAddPairs(leaf, tree); - IncrementStamp(tree); - } -} - -//MARK: Query - -static void -cpBBTreeSegmentQuery(cpBBTree *tree, void *obj, cpVect a, cpVect b, cpFloat t_exit, cpSpatialIndexSegmentQueryFunc func, void *data) -{ - Node *root = tree->root; - if(root) SubtreeSegmentQuery(root, obj, a, b, t_exit, func, data); -} - -static void -cpBBTreeQuery(cpBBTree *tree, void *obj, cpBB bb, cpSpatialIndexQueryFunc func, void *data) -{ - if(tree->root) SubtreeQuery(tree->root, obj, bb, func, data); -} - -//MARK: Misc - -static int -cpBBTreeCount(cpBBTree *tree) -{ - return cpHashSetCount(tree->leaves); -} - -typedef struct eachContext { - cpSpatialIndexIteratorFunc func; - void *data; -} eachContext; - -static void each_helper(Node *node, eachContext *context){context->func(node->obj, context->data);} - -static void -cpBBTreeEach(cpBBTree *tree, cpSpatialIndexIteratorFunc func, void *data) -{ - eachContext context = {func, data}; - cpHashSetEach(tree->leaves, (cpHashSetIteratorFunc)each_helper, &context); -} - -static cpSpatialIndexClass klass = { - (cpSpatialIndexDestroyImpl)cpBBTreeDestroy, - - (cpSpatialIndexCountImpl)cpBBTreeCount, - (cpSpatialIndexEachImpl)cpBBTreeEach, - - (cpSpatialIndexContainsImpl)cpBBTreeContains, - (cpSpatialIndexInsertImpl)cpBBTreeInsert, - (cpSpatialIndexRemoveImpl)cpBBTreeRemove, - - (cpSpatialIndexReindexImpl)cpBBTreeReindex, - (cpSpatialIndexReindexObjectImpl)cpBBTreeReindexObject, - (cpSpatialIndexReindexQueryImpl)cpBBTreeReindexQuery, - - (cpSpatialIndexQueryImpl)cpBBTreeQuery, - (cpSpatialIndexSegmentQueryImpl)cpBBTreeSegmentQuery, -}; - -static inline cpSpatialIndexClass *Klass(){return &klass;} - - -//MARK: Tree Optimization - -static int -cpfcompare(const cpFloat *a, const cpFloat *b){ - return (*a < *b ? -1 : (*b < *a ? 1 : 0)); -} - -static void -fillNodeArray(Node *node, Node ***cursor){ - (**cursor) = node; - (*cursor)++; -} - -static Node * -partitionNodes(cpBBTree *tree, Node **nodes, int count) -{ - if(count == 1){ - return nodes[0]; - } else if(count == 2) { - return NodeNew(tree, nodes[0], nodes[1]); - } - - // Find the AABB for these nodes - cpBB bb = nodes[0]->bb; - for(int i=1; ibb); - - // Split it on it's longest axis - cpBool splitWidth = (bb.r - bb.l > bb.t - bb.b); - - // Sort the bounds and use the median as the splitting point - cpFloat *bounds = (cpFloat *)cpcalloc(count*2, sizeof(cpFloat)); - if(splitWidth){ - for(int i=0; ibb.l; - bounds[2*i + 1] = nodes[i]->bb.r; - } - } else { - for(int i=0; ibb.b; - bounds[2*i + 1] = nodes[i]->bb.t; - } - } - - qsort(bounds, count*2, sizeof(cpFloat), (int (*)(const void *, const void *))cpfcompare); - cpFloat split = (bounds[count - 1] + bounds[count])*0.5f; // use the medain as the split - cpfree(bounds); - - // Generate the child BBs - cpBB a = bb, b = bb; - if(splitWidth) a.r = b.l = split; else a.t = b.b = split; - - // Partition the nodes - int right = count; - for(int left=0; left < right;){ - Node *node = nodes[left]; - if(cpBBMergedArea(node->bb, b) < cpBBMergedArea(node->bb, a)){ -// if(cpBBProximity(node->bb, b) < cpBBProximity(node->bb, a)){ - right--; - nodes[left] = nodes[right]; - nodes[right] = node; - } else { - left++; - } - } - - if(right == count){ - Node *node = NULL; - for(int i=0; iroot; -// Node *node = root; -// int bit = 0; -// unsigned int path = tree->opath; -// -// while(!NodeIsLeaf(node)){ -// node = (path&(1<a : node->b); -// bit = (bit + 1)&(sizeof(unsigned int)*8 - 1); -// } -// -// root = subtreeRemove(root, node, tree); -// tree->root = subtreeInsert(root, node, tree); -// } -//} - -void -cpBBTreeOptimize(cpSpatialIndex *index) -{ - if(index->klass != &klass){ - cpAssertWarn(cpFalse, "Ignoring cpBBTreeOptimize() call to non-tree spatial index."); - return; - } - - cpBBTree *tree = (cpBBTree *)index; - Node *root = tree->root; - if(!root) return; - - int count = cpBBTreeCount(tree); - Node **nodes = (Node **)cpcalloc(count, sizeof(Node *)); - Node **cursor = nodes; - - cpHashSetEach(tree->leaves, (cpHashSetIteratorFunc)fillNodeArray, &cursor); - - SubtreeRecycle(tree, root); - tree->root = partitionNodes(tree, nodes, count); - cpfree(nodes); -} - -//MARK: Debug Draw - -//#define CP_BBTREE_DEBUG_DRAW -#ifdef CP_BBTREE_DEBUG_DRAW -#include "OpenGL/gl.h" -#include "OpenGL/glu.h" -#include - -static void -NodeRender(Node *node, int depth) -{ - if(!NodeIsLeaf(node) && depth <= 10){ - NodeRender(node->a, depth + 1); - NodeRender(node->b, depth + 1); - } - - cpBB bb = node->bb; - -// GLfloat v = depth/2.0f; -// glColor3f(1.0f - v, v, 0.0f); - glLineWidth(cpfmax(5.0f - depth, 1.0f)); - glBegin(GL_LINES); { - glVertex2f(bb.l, bb.b); - glVertex2f(bb.l, bb.t); - - glVertex2f(bb.l, bb.t); - glVertex2f(bb.r, bb.t); - - glVertex2f(bb.r, bb.t); - glVertex2f(bb.r, bb.b); - - glVertex2f(bb.r, bb.b); - glVertex2f(bb.l, bb.b); - }; glEnd(); -} - -void -cpBBTreeRenderDebug(cpSpatialIndex *index){ - if(index->klass != &klass){ - cpAssertWarn(cpFalse, "Ignoring cpBBTreeRenderDebug() call to non-tree spatial index."); - return; - } - - cpBBTree *tree = (cpBBTree *)index; - if(tree->root) NodeRender(tree->root, 0); -} -#endif diff --git a/3rdparty/chipmunk/src/cpBody.c b/3rdparty/chipmunk/src/cpBody.c deleted file mode 100644 index 4ba4b494ec52..000000000000 --- a/3rdparty/chipmunk/src/cpBody.c +++ /dev/null @@ -1,626 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include - -#include "chipmunk/chipmunk_private.h" - -cpBody* -cpBodyAlloc(void) -{ - return (cpBody *)cpcalloc(1, sizeof(cpBody)); -} - -cpBody * -cpBodyInit(cpBody *body, cpFloat mass, cpFloat moment) -{ - body->space = NULL; - body->shapeList = NULL; - body->arbiterList = NULL; - body->constraintList = NULL; - - body->velocity_func = cpBodyUpdateVelocity; - body->position_func = cpBodyUpdatePosition; - - body->sleeping.root = NULL; - body->sleeping.next = NULL; - body->sleeping.idleTime = 0.0f; - - body->p = cpvzero; - body->v = cpvzero; - body->f = cpvzero; - - body->w = 0.0f; - body->t = 0.0f; - - body->v_bias = cpvzero; - body->w_bias = 0.0f; - - body->userData = NULL; - - // Setters must be called after full initialization so the sanity checks don't assert on garbage data. - cpBodySetMass(body, mass); - cpBodySetMoment(body, moment); - cpBodySetAngle(body, 0.0f); - - return body; -} - -cpBody* -cpBodyNew(cpFloat mass, cpFloat moment) -{ - return cpBodyInit(cpBodyAlloc(), mass, moment); -} - -cpBody* -cpBodyNewKinematic() -{ - cpBody *body = cpBodyNew(0.0f, 0.0f); - cpBodySetType(body, CP_BODY_TYPE_KINEMATIC); - - return body; -} - -cpBody* -cpBodyNewStatic() -{ - cpBody *body = cpBodyNew(0.0f, 0.0f); - cpBodySetType(body, CP_BODY_TYPE_STATIC); - - return body; -} - -void cpBodyDestroy(cpBody *body){} - -void -cpBodyFree(cpBody *body) -{ - if(body){ - cpBodyDestroy(body); - cpfree(body); - } -} - -#ifdef NDEBUG - #define cpAssertSaneBody(body) -#else - static void cpv_assert_nan(cpVect v, const char *message){cpAssertHard(v.x == v.x && v.y == v.y, message);} - static void cpv_assert_infinite(cpVect v, const char *message){cpAssertHard(cpfabs(v.x) != INFINITY && cpfabs(v.y) != INFINITY, message);} - static void cpv_assert_sane(cpVect v, const char *message){cpv_assert_nan(v, message); cpv_assert_infinite(v, message);} - - static void - cpBodySanityCheck(const cpBody *body) - { - cpAssertHard(body->m == body->m && body->m_inv == body->m_inv, "Body's mass is NaN."); - cpAssertHard(body->i == body->i && body->i_inv == body->i_inv, "Body's moment is NaN."); - cpAssertHard(body->m >= 0.0f, "Body's mass is negative."); - cpAssertHard(body->i >= 0.0f, "Body's moment is negative."); - - cpv_assert_sane(body->p, "Body's position is invalid."); - cpv_assert_sane(body->v, "Body's velocity is invalid."); - cpv_assert_sane(body->f, "Body's force is invalid."); - - cpAssertHard(body->a == body->a && cpfabs(body->a) != INFINITY, "Body's angle is invalid."); - cpAssertHard(body->w == body->w && cpfabs(body->w) != INFINITY, "Body's angular velocity is invalid."); - cpAssertHard(body->t == body->t && cpfabs(body->t) != INFINITY, "Body's torque is invalid."); - } - - #define cpAssertSaneBody(body) cpBodySanityCheck(body) -#endif - -cpBool -cpBodyIsSleeping(const cpBody *body) -{ - return (body->sleeping.root != ((cpBody*)0)); -} - -cpBodyType -cpBodyGetType(cpBody *body) -{ - if(body->sleeping.idleTime == INFINITY){ - return CP_BODY_TYPE_STATIC; - } else if(body->m == INFINITY){ - return CP_BODY_TYPE_KINEMATIC; - } else { - return CP_BODY_TYPE_DYNAMIC; - } -} - -void -cpBodySetType(cpBody *body, cpBodyType type) -{ - cpBodyType oldType = cpBodyGetType(body); - if(oldType == type) return; - - // Static bodies have their idle timers set to infinity. - // Non-static bodies should have their idle timer reset. - body->sleeping.idleTime = (type == CP_BODY_TYPE_STATIC ? INFINITY : 0.0f); - - if(type == CP_BODY_TYPE_DYNAMIC){ - body->m = body->i = 0.0f; - body->m_inv = body->i_inv = INFINITY; - - cpBodyAccumulateMassFromShapes(body); - } else { - body->m = body->i = INFINITY; - body->m_inv = body->i_inv = 0.0f; - - body->v = cpvzero; - body->w = 0.0f; - } - - // If the body is added to a space already, we'll need to update some space data structures. - cpSpace *space = cpBodyGetSpace(body); - if(space != NULL){ - cpAssertSpaceUnlocked(space); - - if(oldType == CP_BODY_TYPE_STATIC){ - // TODO This is probably not necessary -// cpBodyActivateStatic(body, NULL); - } else { - cpBodyActivate(body); - } - - // Move the bodies to the correct array. - cpArray *fromArray = cpSpaceArrayForBodyType(space, oldType); - cpArray *toArray = cpSpaceArrayForBodyType(space, type); - if(fromArray != toArray){ - cpArrayDeleteObj(fromArray, body); - cpArrayPush(toArray, body); - } - - // Move the body's shapes to the correct spatial index. - cpSpatialIndex *fromIndex = (oldType == CP_BODY_TYPE_STATIC ? space->staticShapes : space->dynamicShapes); - cpSpatialIndex *toIndex = (type == CP_BODY_TYPE_STATIC ? space->staticShapes : space->dynamicShapes); - if(fromIndex != toIndex){ - CP_BODY_FOREACH_SHAPE(body, shape){ - cpSpatialIndexRemove(fromIndex, shape, shape->hashid); - cpSpatialIndexInsert(toIndex, shape, shape->hashid); - } - } - } -} - - - -// Should *only* be called when shapes with mass info are modified, added or removed. -void -cpBodyAccumulateMassFromShapes(cpBody *body) -{ - if(body == NULL || cpBodyGetType(body) != CP_BODY_TYPE_DYNAMIC) return; - - // Reset the body's mass data. - body->m = body->i = 0.0f; - body->cog = cpvzero; - - // Cache the position to realign it at the end. - cpVect pos = cpBodyGetPosition(body); - - // Accumulate mass from shapes. - CP_BODY_FOREACH_SHAPE(body, shape){ - struct cpShapeMassInfo *info = &shape->massInfo; - cpFloat m = info->m; - - if(m > 0.0f){ - cpFloat msum = body->m + m; - - body->i += m*info->i + cpvdistsq(body->cog, info->cog)*(m*body->m)/msum; - body->cog = cpvlerp(body->cog, info->cog, m/msum); - body->m = msum; - } - } - - // Recalculate the inverses. - body->m_inv = 1.0f/body->m; - body->i_inv = 1.0f/body->i; - - // Realign the body since the CoG has probably moved. - cpBodySetPosition(body, pos); - cpAssertSaneBody(body); -} - -cpSpace * -cpBodyGetSpace(const cpBody *body) -{ - return body->space; -} - -cpFloat -cpBodyGetMass(const cpBody *body) -{ - return body->m; -} - -void -cpBodySetMass(cpBody *body, cpFloat mass) -{ - cpAssertHard(cpBodyGetType(body) == CP_BODY_TYPE_DYNAMIC, "You cannot set the mass of kinematic or static bodies."); - cpAssertHard(0.0f <= mass && mass < INFINITY, "Mass must be positive and finite."); - - cpBodyActivate(body); - body->m = mass; - body->m_inv = mass == 0.0f ? INFINITY : 1.0f/mass; - cpAssertSaneBody(body); -} - -cpFloat -cpBodyGetMoment(const cpBody *body) -{ - return body->i; -} - -void -cpBodySetMoment(cpBody *body, cpFloat moment) -{ - cpAssertHard(moment >= 0.0f, "Moment of Inertia must be positive."); - - cpBodyActivate(body); - body->i = moment; - body->i_inv = moment == 0.0f ? INFINITY : 1.0f/moment; - cpAssertSaneBody(body); -} - -cpVect -cpBodyGetRotation(const cpBody *body) -{ - return cpv(body->transform.a, body->transform.b); -} - -void -cpBodyAddShape(cpBody *body, cpShape *shape) -{ - cpShape *next = body->shapeList; - if(next) next->prev = shape; - - shape->next = next; - body->shapeList = shape; - - if(shape->massInfo.m > 0.0f){ - cpBodyAccumulateMassFromShapes(body); - } -} - -void -cpBodyRemoveShape(cpBody *body, cpShape *shape) -{ - cpShape *prev = shape->prev; - cpShape *next = shape->next; - - if(prev){ - prev->next = next; - } else { - body->shapeList = next; - } - - if(next){ - next->prev = prev; - } - - shape->prev = NULL; - shape->next = NULL; - - if(cpBodyGetType(body) == CP_BODY_TYPE_DYNAMIC && shape->massInfo.m > 0.0f){ - cpBodyAccumulateMassFromShapes(body); - } -} - -static cpConstraint * -filterConstraints(cpConstraint *node, cpBody *body, cpConstraint *filter) -{ - if(node == filter){ - return cpConstraintNext(node, body); - } else if(node->a == body){ - node->next_a = filterConstraints(node->next_a, body, filter); - } else { - node->next_b = filterConstraints(node->next_b, body, filter); - } - - return node; -} - -void -cpBodyRemoveConstraint(cpBody *body, cpConstraint *constraint) -{ - body->constraintList = filterConstraints(body->constraintList, body, constraint); -} - -// 'p' is the position of the CoG -static void -SetTransform(cpBody *body, cpVect p, cpFloat a) -{ - cpVect rot = cpvforangle(a); - cpVect c = body->cog; - - body->transform = cpTransformNewTranspose( - rot.x, -rot.y, p.x - (c.x*rot.x - c.y*rot.y), - rot.y, rot.x, p.y - (c.x*rot.y + c.y*rot.x) - ); -} - -static inline cpFloat -SetAngle(cpBody *body, cpFloat a) -{ - body->a = a; - cpAssertSaneBody(body); - - return a; -} - -cpVect -cpBodyGetPosition(const cpBody *body) -{ - return cpTransformPoint(body->transform, cpvzero); -} - -void -cpBodySetPosition(cpBody *body, cpVect position) -{ - cpBodyActivate(body); - cpVect p = body->p = cpvadd(cpTransformVect(body->transform, body->cog), position); - cpAssertSaneBody(body); - - SetTransform(body, p, body->a); -} - -cpVect -cpBodyGetCenterOfGravity(const cpBody *body) -{ - return body->cog; -} - -void -cpBodySetCenterOfGravity(cpBody *body, cpVect cog) -{ - cpBodyActivate(body); - body->cog = cog; - cpAssertSaneBody(body); -} - -cpVect -cpBodyGetVelocity(const cpBody *body) -{ - return body->v; -} - -void -cpBodySetVelocity(cpBody *body, cpVect velocity) -{ - cpBodyActivate(body); - body->v = velocity; - cpAssertSaneBody(body); -} - -cpVect -cpBodyGetForce(const cpBody *body) -{ - return body->f; -} - -void -cpBodySetForce(cpBody *body, cpVect force) -{ - cpBodyActivate(body); - body->f = force; - cpAssertSaneBody(body); -} - -cpFloat -cpBodyGetAngle(const cpBody *body) -{ - return body->a; -} - -void -cpBodySetAngle(cpBody *body, cpFloat angle) -{ - cpBodyActivate(body); - SetAngle(body, angle); - - SetTransform(body, body->p, angle); -} - -cpFloat -cpBodyGetAngularVelocity(const cpBody *body) -{ - return body->w; -} - -void -cpBodySetAngularVelocity(cpBody *body, cpFloat angularVelocity) -{ - cpBodyActivate(body); - body->w = angularVelocity; - cpAssertSaneBody(body); -} - -cpFloat -cpBodyGetTorque(const cpBody *body) -{ - return body->t; -} - -void -cpBodySetTorque(cpBody *body, cpFloat torque) -{ - cpBodyActivate(body); - body->t = torque; - cpAssertSaneBody(body); -} - -cpDataPointer -cpBodyGetUserData(const cpBody *body) -{ - return body->userData; -} - -void -cpBodySetUserData(cpBody *body, cpDataPointer userData) -{ - body->userData = userData; -} - -void -cpBodySetVelocityUpdateFunc(cpBody *body, cpBodyVelocityFunc velocityFunc) -{ - body->velocity_func = velocityFunc; -} - -void -cpBodySetPositionUpdateFunc(cpBody *body, cpBodyPositionFunc positionFunc) -{ - body->position_func = positionFunc; -} - -void -cpBodyUpdateVelocity(cpBody *body, cpVect gravity, cpFloat damping, cpFloat dt) -{ - // Skip kinematic bodies. - if(cpBodyGetType(body) == CP_BODY_TYPE_KINEMATIC) return; - - cpAssertSoft(body->m > 0.0f && body->i > 0.0f, "Body's mass and moment must be positive to simulate. (Mass: %f Moment: %f)", body->m, body->i); - - body->v = cpvadd(cpvmult(body->v, damping), cpvmult(cpvadd(gravity, cpvmult(body->f, body->m_inv)), dt)); - body->w = body->w*damping + body->t*body->i_inv*dt; - - // Reset forces. - body->f = cpvzero; - body->t = 0.0f; - - cpAssertSaneBody(body); -} - -void -cpBodyUpdatePosition(cpBody *body, cpFloat dt) -{ - cpVect p = body->p = cpvadd(body->p, cpvmult(cpvadd(body->v, body->v_bias), dt)); - cpFloat a = SetAngle(body, body->a + (body->w + body->w_bias)*dt); - SetTransform(body, p, a); - - body->v_bias = cpvzero; - body->w_bias = 0.0f; - - cpAssertSaneBody(body); -} - -cpVect -cpBodyLocalToWorld(const cpBody *body, const cpVect point) -{ - return cpTransformPoint(body->transform, point); -} - -cpVect -cpBodyWorldToLocal(const cpBody *body, const cpVect point) -{ - return cpTransformPoint(cpTransformRigidInverse(body->transform), point); -} - -void -cpBodyApplyForceAtWorldPoint(cpBody *body, cpVect force, cpVect point) -{ - cpBodyActivate(body); - body->f = cpvadd(body->f, force); - - cpVect r = cpvsub(point, cpTransformPoint(body->transform, body->cog)); - body->t += cpvcross(r, force); -} - -void -cpBodyApplyForceAtLocalPoint(cpBody *body, cpVect force, cpVect point) -{ - cpBodyApplyForceAtWorldPoint(body, cpTransformVect(body->transform, force), cpTransformPoint(body->transform, point)); -} - -void -cpBodyApplyImpulseAtWorldPoint(cpBody *body, cpVect impulse, cpVect point) -{ - cpBodyActivate(body); - - cpVect r = cpvsub(point, cpTransformPoint(body->transform, body->cog)); - apply_impulse(body, impulse, r); -} - -void -cpBodyApplyImpulseAtLocalPoint(cpBody *body, cpVect impulse, cpVect point) -{ - cpBodyApplyImpulseAtWorldPoint(body, cpTransformVect(body->transform, impulse), cpTransformPoint(body->transform, point)); -} - -cpVect -cpBodyGetVelocityAtLocalPoint(const cpBody *body, cpVect point) -{ - cpVect r = cpTransformVect(body->transform, cpvsub(point, body->cog)); - return cpvadd(body->v, cpvmult(cpvperp(r), body->w)); -} - -cpVect -cpBodyGetVelocityAtWorldPoint(const cpBody *body, cpVect point) -{ - cpVect r = cpvsub(point, cpTransformPoint(body->transform, body->cog)); - return cpvadd(body->v, cpvmult(cpvperp(r), body->w)); -} - -cpFloat -cpBodyKineticEnergy(const cpBody *body) -{ - // Need to do some fudging to avoid NaNs - cpFloat vsq = cpvdot(body->v, body->v); - cpFloat wsq = body->w*body->w; - return (vsq ? vsq*body->m : 0.0f) + (wsq ? wsq*body->i : 0.0f); -} - -void -cpBodyEachShape(cpBody *body, cpBodyShapeIteratorFunc func, void *data) -{ - cpShape *shape = body->shapeList; - while(shape){ - cpShape *next = shape->next; - func(body, shape, data); - shape = next; - } -} - -void -cpBodyEachConstraint(cpBody *body, cpBodyConstraintIteratorFunc func, void *data) -{ - cpConstraint *constraint = body->constraintList; - while(constraint){ - cpConstraint *next = cpConstraintNext(constraint, body); - func(body, constraint, data); - constraint = next; - } -} - -void -cpBodyEachArbiter(cpBody *body, cpBodyArbiterIteratorFunc func, void *data) -{ - cpArbiter *arb = body->arbiterList; - while(arb){ - cpArbiter *next = cpArbiterNext(arb, body); - - cpBool swapped = arb->swapped; { - arb->swapped = (body == arb->body_b); - func(body, arb, data); - } arb->swapped = swapped; - - arb = next; - } -} diff --git a/3rdparty/chipmunk/src/cpCollision.c b/3rdparty/chipmunk/src/cpCollision.c deleted file mode 100644 index 33b3f59917db..000000000000 --- a/3rdparty/chipmunk/src/cpCollision.c +++ /dev/null @@ -1,726 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include - -#include "chipmunk/chipmunk_private.h" -#include "chipmunk/cpRobust.h" - -#if DEBUG && 0 -#include "ChipmunkDemo.h" -#define DRAW_ALL 0 -#define DRAW_GJK (0 || DRAW_ALL) -#define DRAW_EPA (0 || DRAW_ALL) -#define DRAW_CLOSEST (0 || DRAW_ALL) -#define DRAW_CLIP (0 || DRAW_ALL) - -#define PRINT_LOG 0 -#endif - -#define MAX_GJK_ITERATIONS 30 -#define MAX_EPA_ITERATIONS 30 -#define WARN_GJK_ITERATIONS 20 -#define WARN_EPA_ITERATIONS 20 - -static inline void -cpCollisionInfoPushContact(struct cpCollisionInfo *info, cpVect p1, cpVect p2, cpHashValue hash) -{ - cpAssertSoft(info->count <= CP_MAX_CONTACTS_PER_ARBITER, "Internal error: Tried to push too many contacts."); - - struct cpContact *con = &info->arr[info->count]; - con->r1 = p1; - con->r2 = p2; - con->hash = hash; - - info->count++; -} - -//MARK: Support Points and Edges: - -// Support points are the maximal points on a shape's perimeter along a certain axis. -// The GJK and EPA algorithms use support points to iteratively sample the surface of the two shapes' minkowski difference. - -static inline int -PolySupportPointIndex(const int count, const struct cpSplittingPlane *planes, const cpVect n) -{ - cpFloat max = -INFINITY; - int index = 0; - - for(int i=0; i max){ - max = d; - index = i; - } - } - - return index; -} - -struct SupportPoint { - cpVect p; - // Save an index of the point so it can be cheaply looked up as a starting point for the next frame. - cpCollisionID index; -}; - -static inline struct SupportPoint -SupportPointNew(cpVect p, cpCollisionID index) -{ - struct SupportPoint point = {p, index}; - return point; -} - -typedef struct SupportPoint (*SupportPointFunc)(const cpShape *shape, const cpVect n); - -static inline struct SupportPoint -CircleSupportPoint(const cpCircleShape *circle, const cpVect n) -{ - return SupportPointNew(circle->tc, 0); -} - -static inline struct SupportPoint -SegmentSupportPoint(const cpSegmentShape *seg, const cpVect n) -{ - if(cpvdot(seg->ta, n) > cpvdot(seg->tb, n)){ - return SupportPointNew(seg->ta, 0); - } else { - return SupportPointNew(seg->tb, 1); - } -} - -static inline struct SupportPoint -PolySupportPoint(const cpPolyShape *poly, const cpVect n) -{ - const struct cpSplittingPlane *planes = poly->planes; - int i = PolySupportPointIndex(poly->count, planes, n); - return SupportPointNew(planes[i].v0, i); -} - -// A point on the surface of two shape's minkowski difference. -struct MinkowskiPoint { - // Cache the two original support points. - cpVect a, b; - // b - a - cpVect ab; - // Concatenate the two support point indexes. - cpCollisionID id; -}; - -static inline struct MinkowskiPoint -MinkowskiPointNew(const struct SupportPoint a, const struct SupportPoint b) -{ - struct MinkowskiPoint point = {a.p, b.p, cpvsub(b.p, a.p), (a.index & 0xFF)<<8 | (b.index & 0xFF)}; - return point; -} - -struct SupportContext { - const cpShape *shape1, *shape2; - SupportPointFunc func1, func2; -}; - -// Calculate the maximal point on the minkowski difference of two shapes along a particular axis. -static inline struct MinkowskiPoint -Support(const struct SupportContext *ctx, const cpVect n) -{ - struct SupportPoint a = ctx->func1(ctx->shape1, cpvneg(n)); - struct SupportPoint b = ctx->func2(ctx->shape2, n); - return MinkowskiPointNew(a, b); -} - -struct EdgePoint { - cpVect p; - // Keep a hash value for Chipmunk's collision hashing mechanism. - cpHashValue hash; -}; - -// Support edges are the edges of a polygon or segment shape that are in contact. -struct Edge { - struct EdgePoint a, b; - cpFloat r; - cpVect n; -}; - -static struct Edge -SupportEdgeForPoly(const cpPolyShape *poly, const cpVect n) -{ - int count = poly->count; - int i1 = PolySupportPointIndex(poly->count, poly->planes, n); - - // TODO: get rid of mod eventually, very expensive on ARM - int i0 = (i1 - 1 + count)%count; - int i2 = (i1 + 1)%count; - - const struct cpSplittingPlane *planes = poly->planes; - cpHashValue hashid = poly->shape.hashid; - if(cpvdot(n, planes[i1].n) > cpvdot(n, planes[i2].n)){ - struct Edge edge = {{planes[i0].v0, CP_HASH_PAIR(hashid, i0)}, {planes[i1].v0, CP_HASH_PAIR(hashid, i1)}, poly->r, planes[i1].n}; - return edge; - } else { - struct Edge edge = {{planes[i1].v0, CP_HASH_PAIR(hashid, i1)}, {planes[i2].v0, CP_HASH_PAIR(hashid, i2)}, poly->r, planes[i2].n}; - return edge; - } -} - -static struct Edge -SupportEdgeForSegment(const cpSegmentShape *seg, const cpVect n) -{ - cpHashValue hashid = seg->shape.hashid; - if(cpvdot(seg->tn, n) > 0.0){ - struct Edge edge = {{seg->ta, CP_HASH_PAIR(hashid, 0)}, {seg->tb, CP_HASH_PAIR(hashid, 1)}, seg->r, seg->tn}; - return edge; - } else { - struct Edge edge = {{seg->tb, CP_HASH_PAIR(hashid, 1)}, {seg->ta, CP_HASH_PAIR(hashid, 0)}, seg->r, cpvneg(seg->tn)}; - return edge; - } -} - -// Find the closest p(t) to (0, 0) where p(t) = a*(1-t)/2 + b*(1+t)/2 -// The range for t is [-1, 1] to avoid floating point issues if the parameters are swapped. -static inline cpFloat -ClosestT(const cpVect a, const cpVect b) -{ - cpVect delta = cpvsub(b, a); - return -cpfclamp(cpvdot(delta, cpvadd(a, b))/(cpvlengthsq(delta) + CPFLOAT_MIN), -1.0f, 1.0f); -} - -// Basically the same as cpvlerp(), except t = [-1, 1] -static inline cpVect -LerpT(const cpVect a, const cpVect b, const cpFloat t) -{ - cpFloat ht = 0.5f*t; - return cpvadd(cpvmult(a, 0.5f - ht), cpvmult(b, 0.5f + ht)); -} - -// Closest points on the surface of two shapes. -struct ClosestPoints { - // Surface points in absolute coordinates. - cpVect a, b; - // Minimum separating axis of the two shapes. - cpVect n; - // Signed distance between the points. - cpFloat d; - // Concatenation of the id's of the minkoski points. - cpCollisionID id; -}; - -// Calculate the closest points on two shapes given the closest edge on their minkowski difference to (0, 0) -static inline struct ClosestPoints -ClosestPointsNew(const struct MinkowskiPoint v0, const struct MinkowskiPoint v1) -{ - // Find the closest p(t) on the minkowski difference to (0, 0) - cpFloat t = ClosestT(v0.ab, v1.ab); - cpVect p = LerpT(v0.ab, v1.ab, t); - - // Interpolate the original support points using the same 't' value as above. - // This gives you the closest surface points in absolute coordinates. NEAT! - cpVect pa = LerpT(v0.a, v1.a, t); - cpVect pb = LerpT(v0.b, v1.b, t); - cpCollisionID id = (v0.id & 0xFFFF)<<16 | (v1.id & 0xFFFF); - - // First try calculating the MSA from the minkowski difference edge. - // This gives us a nice, accurate MSA when the surfaces are close together. - cpVect delta = cpvsub(v1.ab, v0.ab); - cpVect n = cpvnormalize(cpvrperp(delta)); - cpFloat d = cpvdot(n, p); - - if(d <= 0.0f || (-1.0f < t && t < 1.0f)){ - // If the shapes are overlapping, or we have a regular vertex/edge collision, we are done. - struct ClosestPoints points = {pa, pb, n, d, id}; - return points; - } else { - // Vertex/vertex collisions need special treatment since the MSA won't be shared with an axis of the minkowski difference. - cpFloat d2 = cpvlength(p); - cpVect n2 = cpvmult(p, 1.0f/(d2 + CPFLOAT_MIN)); - - struct ClosestPoints points = {pa, pb, n2, d2, id}; - return points; - } -} - -//MARK: EPA Functions - -static inline cpFloat -ClosestDist(const cpVect v0,const cpVect v1) -{ - return cpvlengthsq(LerpT(v0, v1, ClosestT(v0, v1))); -} - -// Recursive implementation of the EPA loop. -// Each recursion adds a point to the convex hull until it's known that we have the closest point on the surface. -static struct ClosestPoints -EPARecurse(const struct SupportContext *ctx, const int count, const struct MinkowskiPoint *hull, const int iteration) -{ - int mini = 0; - cpFloat minDist = INFINITY; - - // TODO: precalculate this when building the hull and save a step. - // Find the closest segment hull[i] and hull[i + 1] to (0, 0) - for(int j=0, i=count-1; j MAX_GJK_ITERATIONS){ - cpAssertWarn(iteration < WARN_GJK_ITERATIONS, "High GJK iterations: %d", iteration); - return ClosestPointsNew(v0, v1); - } - - if(cpCheckPointGreater(v1.ab, v0.ab, cpvzero)){ - // Origin is behind axis. Flip and try again. - return GJKRecurse(ctx, v1, v0, iteration); - } else { - cpFloat t = ClosestT(v0.ab, v1.ab); - cpVect n = (-1.0f < t && t < 1.0f ? cpvperp(cpvsub(v1.ab, v0.ab)) : cpvneg(LerpT(v0.ab, v1.ab, t))); - struct MinkowskiPoint p = Support(ctx, n); - -#if DRAW_GJK - ChipmunkDebugDrawSegment(v0.ab, v1.ab, RGBAColor(1, 1, 1, 1)); - cpVect c = cpvlerp(v0.ab, v1.ab, 0.5); - ChipmunkDebugDrawSegment(c, cpvadd(c, cpvmult(cpvnormalize(n), 5.0)), RGBAColor(1, 0, 0, 1)); - - ChipmunkDebugDrawDot(5.0, p.ab, LAColor(1, 1)); -#endif - - if(cpCheckPointGreater(p.ab, v0.ab, cpvzero) && cpCheckPointGreater(v1.ab, p.ab, cpvzero)){ - // The triangle v0, p, v1 contains the origin. Use EPA to find the MSA. - cpAssertWarn(iteration < WARN_GJK_ITERATIONS, "High GJK->EPA iterations: %d", iteration); - return EPA(ctx, v0, p, v1); - } else { - if(cpCheckAxis(v0.ab, v1.ab, p.ab, n)){ - // The edge v0, v1 that we already have is the closest to (0, 0) since p was not closer. - cpAssertWarn(iteration < WARN_GJK_ITERATIONS, "High GJK iterations: %d", iteration); - return ClosestPointsNew(v0, v1); - } else { - // p was closer to the origin than our existing edge. - // Need to figure out which existing point to drop. - if(ClosestDist(v0.ab, p.ab) < ClosestDist(p.ab, v1.ab)){ - return GJKRecurse(ctx, v0, p, iteration + 1); - } else { - return GJKRecurse(ctx, p, v1, iteration + 1); - } - } - } - } -} - -// Get a SupportPoint from a cached shape and index. -static struct SupportPoint -ShapePoint(const cpShape *shape, const int i) -{ - switch(shape->klass->type){ - case CP_CIRCLE_SHAPE: { - return SupportPointNew(((cpCircleShape *)shape)->tc, 0); - } case CP_SEGMENT_SHAPE: { - cpSegmentShape *seg = (cpSegmentShape *)shape; - return SupportPointNew(i == 0 ? seg->ta : seg->tb, i); - } case CP_POLY_SHAPE: { - cpPolyShape *poly = (cpPolyShape *)shape; - // Poly shapes may change vertex count. - int index = (i < poly->count ? i : 0); - return SupportPointNew(poly->planes[index].v0, index); - } default: { - return SupportPointNew(cpvzero, 0); - } - } -} - -// Find the closest points between two shapes using the GJK algorithm. -static struct ClosestPoints -GJK(const struct SupportContext *ctx, cpCollisionID *id) -{ -#if DRAW_GJK || DRAW_EPA - int count1 = 1; - int count2 = 1; - - switch(ctx->shape1->klass->type){ - case CP_SEGMENT_SHAPE: count1 = 2; break; - case CP_POLY_SHAPE: count1 = ((cpPolyShape *)ctx->shape1)->count; break; - default: break; - } - - switch(ctx->shape2->klass->type){ - case CP_SEGMENT_SHAPE: count1 = 2; break; - case CP_POLY_SHAPE: count2 = ((cpPolyShape *)ctx->shape2)->count; break; - default: break; - } - - - // draw the minkowski difference origin - cpVect origin = cpvzero; - ChipmunkDebugDrawDot(5.0, origin, RGBAColor(1,0,0,1)); - - int mdiffCount = count1*count2; - cpVect *mdiffVerts = alloca(mdiffCount*sizeof(cpVect)); - - for(int i=0; ishape2, j).p, ShapePoint(ctx->shape1, i).p); - mdiffVerts[i*count2 + j] = v; - ChipmunkDebugDrawDot(2.0, v, RGBAColor(1, 0, 0, 1)); - } - } - - cpVect *hullVerts = alloca(mdiffCount*sizeof(cpVect)); - int hullCount = cpConvexHull(mdiffCount, mdiffVerts, hullVerts, NULL, 0.0); - - ChipmunkDebugDrawPolygon(hullCount, hullVerts, 0.0, RGBAColor(1, 0, 0, 1), RGBAColor(1, 0, 0, 0.25)); -#endif - - struct MinkowskiPoint v0, v1; - if(*id){ - // Use the minkowski points from the last frame as a starting point using the cached indexes. - v0 = MinkowskiPointNew(ShapePoint(ctx->shape1, (*id>>24)&0xFF), ShapePoint(ctx->shape2, (*id>>16)&0xFF)); - v1 = MinkowskiPointNew(ShapePoint(ctx->shape1, (*id>> 8)&0xFF), ShapePoint(ctx->shape2, (*id )&0xFF)); - } else { - // No cached indexes, use the shapes' bounding box centers as a guess for a starting axis. - cpVect axis = cpvperp(cpvsub(cpBBCenter(ctx->shape1->bb), cpBBCenter(ctx->shape2->bb))); - v0 = Support(ctx, axis); - v1 = Support(ctx, cpvneg(axis)); - } - - struct ClosestPoints points = GJKRecurse(ctx, v0, v1, 1); - *id = points.id; - return points; -} - -//MARK: Contact Clipping - -// Given two support edges, find contact point pairs on their surfaces. -static inline void -ContactPoints(const struct Edge e1, const struct Edge e2, const struct ClosestPoints points, struct cpCollisionInfo *info) -{ - cpFloat mindist = e1.r + e2.r; - if(points.d <= mindist){ -#ifdef DRAW_CLIP - ChipmunkDebugDrawFatSegment(e1.a.p, e1.b.p, e1.r, RGBAColor(0, 1, 0, 1), LAColor(0, 0)); - ChipmunkDebugDrawFatSegment(e2.a.p, e2.b.p, e2.r, RGBAColor(1, 0, 0, 1), LAColor(0, 0)); -#endif - cpVect n = info->n = points.n; - - // Distances along the axis parallel to n - cpFloat d_e1_a = cpvcross(e1.a.p, n); - cpFloat d_e1_b = cpvcross(e1.b.p, n); - cpFloat d_e2_a = cpvcross(e2.a.p, n); - cpFloat d_e2_b = cpvcross(e2.b.p, n); - - // TODO + min isn't a complete fix. - cpFloat e1_denom = 1.0f/(d_e1_b - d_e1_a + CPFLOAT_MIN); - cpFloat e2_denom = 1.0f/(d_e2_b - d_e2_a + CPFLOAT_MIN); - - // Project the endpoints of the two edges onto the opposing edge, clamping them as necessary. - // Compare the projected points to the collision normal to see if the shapes overlap there. - { - cpVect p1 = cpvadd(cpvmult(n, e1.r), cpvlerp(e1.a.p, e1.b.p, cpfclamp01((d_e2_b - d_e1_a)*e1_denom))); - cpVect p2 = cpvadd(cpvmult(n, -e2.r), cpvlerp(e2.a.p, e2.b.p, cpfclamp01((d_e1_a - d_e2_a)*e2_denom))); - cpFloat dist = cpvdot(cpvsub(p2, p1), n); - if(dist <= 0.0f){ - cpHashValue hash_1a2b = CP_HASH_PAIR(e1.a.hash, e2.b.hash); - cpCollisionInfoPushContact(info, p1, p2, hash_1a2b); - } - }{ - cpVect p1 = cpvadd(cpvmult(n, e1.r), cpvlerp(e1.a.p, e1.b.p, cpfclamp01((d_e2_a - d_e1_a)*e1_denom))); - cpVect p2 = cpvadd(cpvmult(n, -e2.r), cpvlerp(e2.a.p, e2.b.p, cpfclamp01((d_e1_b - d_e2_a)*e2_denom))); - cpFloat dist = cpvdot(cpvsub(p2, p1), n); - if(dist <= 0.0f){ - cpHashValue hash_1b2a = CP_HASH_PAIR(e1.b.hash, e2.a.hash); - cpCollisionInfoPushContact(info, p1, p2, hash_1b2a); - } - } - } -} - -//MARK: Collision Functions - -typedef void (*CollisionFunc)(const cpShape *a, const cpShape *b, struct cpCollisionInfo *info); - -// Collide circle shapes. -static void -CircleToCircle(const cpCircleShape *c1, const cpCircleShape *c2, struct cpCollisionInfo *info) -{ - cpFloat mindist = c1->r + c2->r; - cpVect delta = cpvsub(c2->tc, c1->tc); - cpFloat distsq = cpvlengthsq(delta); - - if(distsq < mindist*mindist){ - cpFloat dist = cpfsqrt(distsq); - cpVect n = info->n = (dist ? cpvmult(delta, 1.0f/dist) : cpv(1.0f, 0.0f)); - cpCollisionInfoPushContact(info, cpvadd(c1->tc, cpvmult(n, c1->r)), cpvadd(c2->tc, cpvmult(n, -c2->r)), 0); - } -} - -static void -CircleToSegment(const cpCircleShape *circle, const cpSegmentShape *segment, struct cpCollisionInfo *info) -{ - cpVect seg_a = segment->ta; - cpVect seg_b = segment->tb; - cpVect center = circle->tc; - - // Find the closest point on the segment to the circle. - cpVect seg_delta = cpvsub(seg_b, seg_a); - cpFloat closest_t = cpfclamp01(cpvdot(seg_delta, cpvsub(center, seg_a))/cpvlengthsq(seg_delta)); - cpVect closest = cpvadd(seg_a, cpvmult(seg_delta, closest_t)); - - // Compare the radii of the two shapes to see if they are colliding. - cpFloat mindist = circle->r + segment->r; - cpVect delta = cpvsub(closest, center); - cpFloat distsq = cpvlengthsq(delta); - if(distsq < mindist*mindist){ - cpFloat dist = cpfsqrt(distsq); - // Handle coincident shapes as gracefully as possible. - cpVect n = info->n = (dist ? cpvmult(delta, 1.0f/dist) : segment->tn); - - // Reject endcap collisions if tangents are provided. - cpVect rot = cpBodyGetRotation(segment->shape.body); - if( - (closest_t != 0.0f || cpvdot(n, cpvrotate(segment->a_tangent, rot)) >= 0.0) && - (closest_t != 1.0f || cpvdot(n, cpvrotate(segment->b_tangent, rot)) >= 0.0) - ){ - cpCollisionInfoPushContact(info, cpvadd(center, cpvmult(n, circle->r)), cpvadd(closest, cpvmult(n, -segment->r)), 0); - } - } -} - -static void -SegmentToSegment(const cpSegmentShape *seg1, const cpSegmentShape *seg2, struct cpCollisionInfo *info) -{ - struct SupportContext context = {(cpShape *)seg1, (cpShape *)seg2, (SupportPointFunc)SegmentSupportPoint, (SupportPointFunc)SegmentSupportPoint}; - struct ClosestPoints points = GJK(&context, &info->id); - -#if DRAW_CLOSEST -#if PRINT_LOG -// ChipmunkDemoPrintString("Distance: %.2f\n", points.d); -#endif - - ChipmunkDebugDrawDot(6.0, points.a, RGBAColor(1, 1, 1, 1)); - ChipmunkDebugDrawDot(6.0, points.b, RGBAColor(1, 1, 1, 1)); - ChipmunkDebugDrawSegment(points.a, points.b, RGBAColor(1, 1, 1, 1)); - ChipmunkDebugDrawSegment(points.a, cpvadd(points.a, cpvmult(points.n, 10.0)), RGBAColor(1, 0, 0, 1)); -#endif - - cpVect n = points.n; - cpVect rot1 = cpBodyGetRotation(seg1->shape.body); - cpVect rot2 = cpBodyGetRotation(seg2->shape.body); - - // If the closest points are nearer than the sum of the radii... - if( - points.d <= (seg1->r + seg2->r) && ( - // Reject endcap collisions if tangents are provided. - (!cpveql(points.a, seg1->ta) || cpvdot(n, cpvrotate(seg1->a_tangent, rot1)) <= 0.0) && - (!cpveql(points.a, seg1->tb) || cpvdot(n, cpvrotate(seg1->b_tangent, rot1)) <= 0.0) && - (!cpveql(points.b, seg2->ta) || cpvdot(n, cpvrotate(seg2->a_tangent, rot2)) >= 0.0) && - (!cpveql(points.b, seg2->tb) || cpvdot(n, cpvrotate(seg2->b_tangent, rot2)) >= 0.0) - ) - ){ - ContactPoints(SupportEdgeForSegment(seg1, n), SupportEdgeForSegment(seg2, cpvneg(n)), points, info); - } -} - -static void -PolyToPoly(const cpPolyShape *poly1, const cpPolyShape *poly2, struct cpCollisionInfo *info) -{ - struct SupportContext context = {(cpShape *)poly1, (cpShape *)poly2, (SupportPointFunc)PolySupportPoint, (SupportPointFunc)PolySupportPoint}; - struct ClosestPoints points = GJK(&context, &info->id); - -#if DRAW_CLOSEST -#if PRINT_LOG -// ChipmunkDemoPrintString("Distance: %.2f\n", points.d); -#endif - - ChipmunkDebugDrawDot(3.0, points.a, RGBAColor(1, 1, 1, 1)); - ChipmunkDebugDrawDot(3.0, points.b, RGBAColor(1, 1, 1, 1)); - ChipmunkDebugDrawSegment(points.a, points.b, RGBAColor(1, 1, 1, 1)); - ChipmunkDebugDrawSegment(points.a, cpvadd(points.a, cpvmult(points.n, 10.0)), RGBAColor(1, 0, 0, 1)); -#endif - - // If the closest points are nearer than the sum of the radii... - if(points.d - poly1->r - poly2->r <= 0.0){ - ContactPoints(SupportEdgeForPoly(poly1, points.n), SupportEdgeForPoly(poly2, cpvneg(points.n)), points, info); - } -} - -static void -SegmentToPoly(const cpSegmentShape *seg, const cpPolyShape *poly, struct cpCollisionInfo *info) -{ - struct SupportContext context = {(cpShape *)seg, (cpShape *)poly, (SupportPointFunc)SegmentSupportPoint, (SupportPointFunc)PolySupportPoint}; - struct ClosestPoints points = GJK(&context, &info->id); - -#if DRAW_CLOSEST -#if PRINT_LOG -// ChipmunkDemoPrintString("Distance: %.2f\n", points.d); -#endif - - ChipmunkDebugDrawDot(3.0, points.a, RGBAColor(1, 1, 1, 1)); - ChipmunkDebugDrawDot(3.0, points.b, RGBAColor(1, 1, 1, 1)); - ChipmunkDebugDrawSegment(points.a, points.b, RGBAColor(1, 1, 1, 1)); - ChipmunkDebugDrawSegment(points.a, cpvadd(points.a, cpvmult(points.n, 10.0)), RGBAColor(1, 0, 0, 1)); -#endif - - cpVect n = points.n; - cpVect rot = cpBodyGetRotation(seg->shape.body); - - if( - // If the closest points are nearer than the sum of the radii... - points.d - seg->r - poly->r <= 0.0 && ( - // Reject endcap collisions if tangents are provided. - (!cpveql(points.a, seg->ta) || cpvdot(n, cpvrotate(seg->a_tangent, rot)) <= 0.0) && - (!cpveql(points.a, seg->tb) || cpvdot(n, cpvrotate(seg->b_tangent, rot)) <= 0.0) - ) - ){ - ContactPoints(SupportEdgeForSegment(seg, n), SupportEdgeForPoly(poly, cpvneg(n)), points, info); - } -} - -static void -CircleToPoly(const cpCircleShape *circle, const cpPolyShape *poly, struct cpCollisionInfo *info) -{ - struct SupportContext context = {(cpShape *)circle, (cpShape *)poly, (SupportPointFunc)CircleSupportPoint, (SupportPointFunc)PolySupportPoint}; - struct ClosestPoints points = GJK(&context, &info->id); - -#if DRAW_CLOSEST - ChipmunkDebugDrawDot(3.0, points.a, RGBAColor(1, 1, 1, 1)); - ChipmunkDebugDrawDot(3.0, points.b, RGBAColor(1, 1, 1, 1)); - ChipmunkDebugDrawSegment(points.a, points.b, RGBAColor(1, 1, 1, 1)); - ChipmunkDebugDrawSegment(points.a, cpvadd(points.a, cpvmult(points.n, 10.0)), RGBAColor(1, 0, 0, 1)); -#endif - - // If the closest points are nearer than the sum of the radii... - if(points.d <= circle->r + poly->r){ - cpVect n = info->n = points.n; - cpCollisionInfoPushContact(info, cpvadd(points.a, cpvmult(n, circle->r)), cpvadd(points.b, cpvmult(n, -poly->r)), 0); - } -} - -static void -CollisionError(const cpShape *circle, const cpShape *poly, struct cpCollisionInfo *info) -{ - cpAssertHard(cpFalse, "Internal Error: Shape types are not sorted."); -} - - -static const CollisionFunc BuiltinCollisionFuncs[9] = { - (CollisionFunc)CircleToCircle, - CollisionError, - CollisionError, - (CollisionFunc)CircleToSegment, - (CollisionFunc)SegmentToSegment, - CollisionError, - (CollisionFunc)CircleToPoly, - (CollisionFunc)SegmentToPoly, - (CollisionFunc)PolyToPoly, -}; -static const CollisionFunc *CollisionFuncs = BuiltinCollisionFuncs; - -struct cpCollisionInfo -cpCollide(const cpShape *a, const cpShape *b, cpCollisionID id, struct cpContact *contacts) -{ - struct cpCollisionInfo info = {a, b, id, cpvzero, 0, contacts}; - - // Make sure the shape types are in order. - if(a->klass->type > b->klass->type){ - info.a = b; - info.b = a; - } - - CollisionFuncs[info.a->klass->type + info.b->klass->type*CP_NUM_SHAPES](info.a, info.b, &info); - -// if(0){ -// for(int i=0; iklass = klass; - - constraint->a = a; - constraint->b = b; - constraint->space = NULL; - - constraint->next_a = NULL; - constraint->next_b = NULL; - - constraint->maxForce = (cpFloat)INFINITY; - constraint->errorBias = cpfpow(1.0f - 0.1f, 60.0f); - constraint->maxBias = (cpFloat)INFINITY; - - constraint->collideBodies = cpTrue; - - constraint->preSolve = NULL; - constraint->postSolve = NULL; -} - -cpSpace * -cpConstraintGetSpace(const cpConstraint *constraint) -{ - return constraint->space; -} - -cpBody * -cpConstraintGetBodyA(const cpConstraint *constraint) -{ - return constraint->a; -} - -cpBody * -cpConstraintGetBodyB(const cpConstraint *constraint) -{ - return constraint->b; -} - -cpFloat -cpConstraintGetMaxForce(const cpConstraint *constraint) -{ - return constraint->maxForce; -} - -void -cpConstraintSetMaxForce(cpConstraint *constraint, cpFloat maxForce) -{ - cpAssertHard(maxForce >= 0.0f, "maxForce must be positive."); - cpConstraintActivateBodies(constraint); - constraint->maxForce = maxForce; -} - -cpFloat -cpConstraintGetErrorBias(const cpConstraint *constraint) -{ - return constraint->errorBias; -} - -void -cpConstraintSetErrorBias(cpConstraint *constraint, cpFloat errorBias) -{ - cpAssertHard(errorBias >= 0.0f, "errorBias must be positive."); - cpConstraintActivateBodies(constraint); - constraint->errorBias = errorBias; -} - -cpFloat -cpConstraintGetMaxBias(const cpConstraint *constraint) -{ - return constraint->maxBias; -} - -void -cpConstraintSetMaxBias(cpConstraint *constraint, cpFloat maxBias) -{ - cpAssertHard(maxBias >= 0.0f, "maxBias must be positive."); - cpConstraintActivateBodies(constraint); - constraint->maxBias = maxBias; -} - -cpBool -cpConstraintGetCollideBodies(const cpConstraint *constraint) -{ - return constraint->collideBodies; -} - -void -cpConstraintSetCollideBodies(cpConstraint *constraint, cpBool collideBodies) -{ - cpConstraintActivateBodies(constraint); - constraint->collideBodies = collideBodies; -} - -cpConstraintPreSolveFunc -cpConstraintGetPreSolveFunc(const cpConstraint *constraint) -{ - return constraint->preSolve; -} - -void -cpConstraintSetPreSolveFunc(cpConstraint *constraint, cpConstraintPreSolveFunc preSolveFunc) -{ - constraint->preSolve = preSolveFunc; -} - -cpConstraintPostSolveFunc -cpConstraintGetPostSolveFunc(const cpConstraint *constraint) -{ - return constraint->postSolve; -} - -void -cpConstraintSetPostSolveFunc(cpConstraint *constraint, cpConstraintPostSolveFunc postSolveFunc) -{ - constraint->postSolve = postSolveFunc; -} - -cpDataPointer -cpConstraintGetUserData(const cpConstraint *constraint) -{ - return constraint->userData; -} - -void -cpConstraintSetUserData(cpConstraint *constraint, cpDataPointer userData) -{ - constraint->userData = userData; -} - - -cpFloat -cpConstraintGetImpulse(cpConstraint *constraint) -{ - return constraint->klass->getImpulse(constraint); -} diff --git a/3rdparty/chipmunk/src/cpDampedRotarySpring.c b/3rdparty/chipmunk/src/cpDampedRotarySpring.c deleted file mode 100644 index 8d38a545e0f9..000000000000 --- a/3rdparty/chipmunk/src/cpDampedRotarySpring.c +++ /dev/null @@ -1,178 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk_private.h" - -static cpFloat -defaultSpringTorque(cpDampedRotarySpring *spring, cpFloat relativeAngle){ - return (relativeAngle - spring->restAngle)*spring->stiffness; -} - -static void -preStep(cpDampedRotarySpring *spring, cpFloat dt) -{ - cpBody *a = spring->constraint.a; - cpBody *b = spring->constraint.b; - - cpFloat moment = a->i_inv + b->i_inv; - cpAssertSoft(moment != 0.0, "Unsolvable spring."); - spring->iSum = 1.0f/moment; - - spring->w_coef = 1.0f - cpfexp(-spring->damping*dt*moment); - spring->target_wrn = 0.0f; - - // apply spring torque - cpFloat j_spring = spring->springTorqueFunc((cpConstraint *)spring, a->a - b->a)*dt; - spring->jAcc = j_spring; - - a->w -= j_spring*a->i_inv; - b->w += j_spring*b->i_inv; -} - -static void applyCachedImpulse(cpDampedRotarySpring *spring, cpFloat dt_coef){} - -static void -applyImpulse(cpDampedRotarySpring *spring, cpFloat dt) -{ - cpBody *a = spring->constraint.a; - cpBody *b = spring->constraint.b; - - // compute relative velocity - cpFloat wrn = a->w - b->w;//normal_relative_velocity(a, b, r1, r2, n) - spring->target_vrn; - - // compute velocity loss from drag - // not 100% certain this is derived correctly, though it makes sense - cpFloat w_damp = (spring->target_wrn - wrn)*spring->w_coef; - spring->target_wrn = wrn + w_damp; - - //apply_impulses(a, b, spring->r1, spring->r2, cpvmult(spring->n, v_damp*spring->nMass)); - cpFloat j_damp = w_damp*spring->iSum; - spring->jAcc += j_damp; - - a->w += j_damp*a->i_inv; - b->w -= j_damp*b->i_inv; -} - -static cpFloat -getImpulse(cpDampedRotarySpring *spring) -{ - return spring->jAcc; -} - -static const cpConstraintClass klass = { - (cpConstraintPreStepImpl)preStep, - (cpConstraintApplyCachedImpulseImpl)applyCachedImpulse, - (cpConstraintApplyImpulseImpl)applyImpulse, - (cpConstraintGetImpulseImpl)getImpulse, -}; - -cpDampedRotarySpring * -cpDampedRotarySpringAlloc(void) -{ - return (cpDampedRotarySpring *)cpcalloc(1, sizeof(cpDampedRotarySpring)); -} - -cpDampedRotarySpring * -cpDampedRotarySpringInit(cpDampedRotarySpring *spring, cpBody *a, cpBody *b, cpFloat restAngle, cpFloat stiffness, cpFloat damping) -{ - cpConstraintInit((cpConstraint *)spring, &klass, a, b); - - spring->restAngle = restAngle; - spring->stiffness = stiffness; - spring->damping = damping; - spring->springTorqueFunc = (cpDampedRotarySpringTorqueFunc)defaultSpringTorque; - - spring->jAcc = 0.0f; - - return spring; -} - -cpConstraint * -cpDampedRotarySpringNew(cpBody *a, cpBody *b, cpFloat restAngle, cpFloat stiffness, cpFloat damping) -{ - return (cpConstraint *)cpDampedRotarySpringInit(cpDampedRotarySpringAlloc(), a, b, restAngle, stiffness, damping); -} - -cpBool -cpConstraintIsDampedRotarySpring(const cpConstraint *constraint) -{ - return (constraint->klass == &klass); -} - -cpFloat -cpDampedRotarySpringGetRestAngle(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsDampedRotarySpring(constraint), "Constraint is not a damped rotary spring."); - return ((cpDampedRotarySpring *)constraint)->restAngle; -} - -void -cpDampedRotarySpringSetRestAngle(cpConstraint *constraint, cpFloat restAngle) -{ - cpAssertHard(cpConstraintIsDampedRotarySpring(constraint), "Constraint is not a damped rotary spring."); - cpConstraintActivateBodies(constraint); - ((cpDampedRotarySpring *)constraint)->restAngle = restAngle; -} - -cpFloat -cpDampedRotarySpringGetStiffness(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsDampedRotarySpring(constraint), "Constraint is not a damped rotary spring."); - return ((cpDampedRotarySpring *)constraint)->stiffness; -} - -void -cpDampedRotarySpringSetStiffness(cpConstraint *constraint, cpFloat stiffness) -{ - cpAssertHard(cpConstraintIsDampedRotarySpring(constraint), "Constraint is not a damped rotary spring."); - cpConstraintActivateBodies(constraint); - ((cpDampedRotarySpring *)constraint)->stiffness = stiffness; -} - -cpFloat -cpDampedRotarySpringGetDamping(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsDampedRotarySpring(constraint), "Constraint is not a damped rotary spring."); - return ((cpDampedRotarySpring *)constraint)->damping; -} - -void -cpDampedRotarySpringSetDamping(cpConstraint *constraint, cpFloat damping) -{ - cpAssertHard(cpConstraintIsDampedRotarySpring(constraint), "Constraint is not a damped rotary spring."); - cpConstraintActivateBodies(constraint); - ((cpDampedRotarySpring *)constraint)->damping = damping; -} - -cpDampedRotarySpringTorqueFunc -cpDampedRotarySpringGetSpringTorqueFunc(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsDampedRotarySpring(constraint), "Constraint is not a damped rotary spring."); - return ((cpDampedRotarySpring *)constraint)->springTorqueFunc; -} - -void -cpDampedRotarySpringSetSpringTorqueFunc(cpConstraint *constraint, cpDampedRotarySpringTorqueFunc springTorqueFunc) -{ - cpAssertHard(cpConstraintIsDampedRotarySpring(constraint), "Constraint is not a damped rotary spring."); - cpConstraintActivateBodies(constraint); - ((cpDampedRotarySpring *)constraint)->springTorqueFunc = springTorqueFunc; -} diff --git a/3rdparty/chipmunk/src/cpDampedSpring.c b/3rdparty/chipmunk/src/cpDampedSpring.c deleted file mode 100644 index e4d019e9a02f..000000000000 --- a/3rdparty/chipmunk/src/cpDampedSpring.c +++ /dev/null @@ -1,216 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk_private.h" - -static cpFloat -defaultSpringForce(cpDampedSpring *spring, cpFloat dist){ - return (spring->restLength - dist)*spring->stiffness; -} - -static void -preStep(cpDampedSpring *spring, cpFloat dt) -{ - cpBody *a = spring->constraint.a; - cpBody *b = spring->constraint.b; - - spring->r1 = cpTransformVect(a->transform, cpvsub(spring->anchorA, a->cog)); - spring->r2 = cpTransformVect(b->transform, cpvsub(spring->anchorB, b->cog)); - - cpVect delta = cpvsub(cpvadd(b->p, spring->r2), cpvadd(a->p, spring->r1)); - cpFloat dist = cpvlength(delta); - spring->n = cpvmult(delta, 1.0f/(dist ? dist : INFINITY)); - - cpFloat k = k_scalar(a, b, spring->r1, spring->r2, spring->n); - cpAssertSoft(k != 0.0, "Unsolvable spring."); - spring->nMass = 1.0f/k; - - spring->target_vrn = 0.0f; - spring->v_coef = 1.0f - cpfexp(-spring->damping*dt*k); - - // apply spring force - cpFloat f_spring = spring->springForceFunc((cpConstraint *)spring, dist); - cpFloat j_spring = spring->jAcc = f_spring*dt; - apply_impulses(a, b, spring->r1, spring->r2, cpvmult(spring->n, j_spring)); -} - -static void applyCachedImpulse(cpDampedSpring *spring, cpFloat dt_coef){} - -static void -applyImpulse(cpDampedSpring *spring, cpFloat dt) -{ - cpBody *a = spring->constraint.a; - cpBody *b = spring->constraint.b; - - cpVect n = spring->n; - cpVect r1 = spring->r1; - cpVect r2 = spring->r2; - - // compute relative velocity - cpFloat vrn = normal_relative_velocity(a, b, r1, r2, n); - - // compute velocity loss from drag - cpFloat v_damp = (spring->target_vrn - vrn)*spring->v_coef; - spring->target_vrn = vrn + v_damp; - - cpFloat j_damp = v_damp*spring->nMass; - spring->jAcc += j_damp; - apply_impulses(a, b, spring->r1, spring->r2, cpvmult(spring->n, j_damp)); -} - -static cpFloat -getImpulse(cpDampedSpring *spring) -{ - return spring->jAcc; -} - -static const cpConstraintClass klass = { - (cpConstraintPreStepImpl)preStep, - (cpConstraintApplyCachedImpulseImpl)applyCachedImpulse, - (cpConstraintApplyImpulseImpl)applyImpulse, - (cpConstraintGetImpulseImpl)getImpulse, -}; - -cpDampedSpring * -cpDampedSpringAlloc(void) -{ - return (cpDampedSpring *)cpcalloc(1, sizeof(cpDampedSpring)); -} - -cpDampedSpring * -cpDampedSpringInit(cpDampedSpring *spring, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat restLength, cpFloat stiffness, cpFloat damping) -{ - cpConstraintInit((cpConstraint *)spring, &klass, a, b); - - spring->anchorA = anchorA; - spring->anchorB = anchorB; - - spring->restLength = restLength; - spring->stiffness = stiffness; - spring->damping = damping; - spring->springForceFunc = (cpDampedSpringForceFunc)defaultSpringForce; - - spring->jAcc = 0.0f; - - return spring; -} - -cpConstraint * -cpDampedSpringNew(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat restLength, cpFloat stiffness, cpFloat damping) -{ - return (cpConstraint *)cpDampedSpringInit(cpDampedSpringAlloc(), a, b, anchorA, anchorB, restLength, stiffness, damping); -} - -cpBool -cpConstraintIsDampedSpring(const cpConstraint *constraint) -{ - return (constraint->klass == &klass); -} - -cpVect -cpDampedSpringGetAnchorA(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring."); - return ((cpDampedSpring *)constraint)->anchorA; -} - -void -cpDampedSpringSetAnchorA(cpConstraint *constraint, cpVect anchorA) -{ - cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring."); - cpConstraintActivateBodies(constraint); - ((cpDampedSpring *)constraint)->anchorA = anchorA; -} - -cpVect -cpDampedSpringGetAnchorB(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring."); - return ((cpDampedSpring *)constraint)->anchorB; -} - -void -cpDampedSpringSetAnchorB(cpConstraint *constraint, cpVect anchorB) -{ - cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring."); - cpConstraintActivateBodies(constraint); - ((cpDampedSpring *)constraint)->anchorB = anchorB; -} - -cpFloat -cpDampedSpringGetRestLength(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring."); - return ((cpDampedSpring *)constraint)->restLength; -} - -void -cpDampedSpringSetRestLength(cpConstraint *constraint, cpFloat restLength) -{ - cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring."); - cpConstraintActivateBodies(constraint); - ((cpDampedSpring *)constraint)->restLength = restLength; -} - -cpFloat -cpDampedSpringGetStiffness(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring."); - return ((cpDampedSpring *)constraint)->stiffness; -} - -void -cpDampedSpringSetStiffness(cpConstraint *constraint, cpFloat stiffness) -{ - cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring."); - cpConstraintActivateBodies(constraint); - ((cpDampedSpring *)constraint)->stiffness = stiffness; -} - -cpFloat -cpDampedSpringGetDamping(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring."); - return ((cpDampedSpring *)constraint)->damping; -} - -void -cpDampedSpringSetDamping(cpConstraint *constraint, cpFloat damping) -{ - cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring."); - cpConstraintActivateBodies(constraint); - ((cpDampedSpring *)constraint)->damping = damping; -} - -cpDampedSpringForceFunc -cpDampedSpringGetSpringForceFunc(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring."); - return ((cpDampedSpring *)constraint)->springForceFunc; -} - -void -cpDampedSpringSetSpringForceFunc(cpConstraint *constraint, cpDampedSpringForceFunc springForceFunc) -{ - cpAssertHard(cpConstraintIsDampedSpring(constraint), "Constraint is not a damped spring."); - cpConstraintActivateBodies(constraint); - ((cpDampedSpring *)constraint)->springForceFunc = springForceFunc; -} diff --git a/3rdparty/chipmunk/src/cpGearJoint.c b/3rdparty/chipmunk/src/cpGearJoint.c deleted file mode 100644 index 3670173b3c59..000000000000 --- a/3rdparty/chipmunk/src/cpGearJoint.c +++ /dev/null @@ -1,145 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk_private.h" - -static void -preStep(cpGearJoint *joint, cpFloat dt) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - // calculate moment of inertia coefficient. - joint->iSum = 1.0f/(a->i_inv*joint->ratio_inv + joint->ratio*b->i_inv); - - // calculate bias velocity - cpFloat maxBias = joint->constraint.maxBias; - joint->bias = cpfclamp(-bias_coef(joint->constraint.errorBias, dt)*(b->a*joint->ratio - a->a - joint->phase)/dt, -maxBias, maxBias); -} - -static void -applyCachedImpulse(cpGearJoint *joint, cpFloat dt_coef) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - cpFloat j = joint->jAcc*dt_coef; - a->w -= j*a->i_inv*joint->ratio_inv; - b->w += j*b->i_inv; -} - -static void -applyImpulse(cpGearJoint *joint, cpFloat dt) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - // compute relative rotational velocity - cpFloat wr = b->w*joint->ratio - a->w; - - cpFloat jMax = joint->constraint.maxForce*dt; - - // compute normal impulse - cpFloat j = (joint->bias - wr)*joint->iSum; - cpFloat jOld = joint->jAcc; - joint->jAcc = cpfclamp(jOld + j, -jMax, jMax); - j = joint->jAcc - jOld; - - // apply impulse - a->w -= j*a->i_inv*joint->ratio_inv; - b->w += j*b->i_inv; -} - -static cpFloat -getImpulse(cpGearJoint *joint) -{ - return cpfabs(joint->jAcc); -} - -static const cpConstraintClass klass = { - (cpConstraintPreStepImpl)preStep, - (cpConstraintApplyCachedImpulseImpl)applyCachedImpulse, - (cpConstraintApplyImpulseImpl)applyImpulse, - (cpConstraintGetImpulseImpl)getImpulse, -}; - -cpGearJoint * -cpGearJointAlloc(void) -{ - return (cpGearJoint *)cpcalloc(1, sizeof(cpGearJoint)); -} - -cpGearJoint * -cpGearJointInit(cpGearJoint *joint, cpBody *a, cpBody *b, cpFloat phase, cpFloat ratio) -{ - cpConstraintInit((cpConstraint *)joint, &klass, a, b); - - joint->phase = phase; - joint->ratio = ratio; - joint->ratio_inv = 1.0f/ratio; - - joint->jAcc = 0.0f; - - return joint; -} - -cpConstraint * -cpGearJointNew(cpBody *a, cpBody *b, cpFloat phase, cpFloat ratio) -{ - return (cpConstraint *)cpGearJointInit(cpGearJointAlloc(), a, b, phase, ratio); -} - -cpBool -cpConstraintIsGearJoint(const cpConstraint *constraint) -{ - return (constraint->klass == &klass); -} - -cpFloat -cpGearJointGetPhase(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsGearJoint(constraint), "Constraint is not a ratchet joint."); - return ((cpGearJoint *)constraint)->phase; -} - -void -cpGearJointSetPhase(cpConstraint *constraint, cpFloat phase) -{ - cpAssertHard(cpConstraintIsGearJoint(constraint), "Constraint is not a ratchet joint."); - cpConstraintActivateBodies(constraint); - ((cpGearJoint *)constraint)->phase = phase; -} - -cpFloat -cpGearJointGetRatio(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsGearJoint(constraint), "Constraint is not a ratchet joint."); - return ((cpGearJoint *)constraint)->ratio; -} - -void -cpGearJointSetRatio(cpConstraint *constraint, cpFloat ratio) -{ - cpAssertHard(cpConstraintIsGearJoint(constraint), "Constraint is not a ratchet joint."); - cpConstraintActivateBodies(constraint); - ((cpGearJoint *)constraint)->ratio = ratio; - ((cpGearJoint *)constraint)->ratio_inv = 1.0f/ratio; -} diff --git a/3rdparty/chipmunk/src/cpGrooveJoint.c b/3rdparty/chipmunk/src/cpGrooveJoint.c deleted file mode 100644 index 50d1857d440b..000000000000 --- a/3rdparty/chipmunk/src/cpGrooveJoint.c +++ /dev/null @@ -1,197 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk_private.h" - -static void -preStep(cpGrooveJoint *joint, cpFloat dt) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - // calculate endpoints in worldspace - cpVect ta = cpTransformPoint(a->transform, joint->grv_a); - cpVect tb = cpTransformPoint(a->transform, joint->grv_b); - - // calculate axis - cpVect n = cpTransformVect(a->transform, joint->grv_n); - cpFloat d = cpvdot(ta, n); - - joint->grv_tn = n; - joint->r2 = cpTransformVect(b->transform, cpvsub(joint->anchorB, b->cog)); - - // calculate tangential distance along the axis of r2 - cpFloat td = cpvcross(cpvadd(b->p, joint->r2), n); - // calculate clamping factor and r2 - if(td <= cpvcross(ta, n)){ - joint->clamp = 1.0f; - joint->r1 = cpvsub(ta, a->p); - } else if(td >= cpvcross(tb, n)){ - joint->clamp = -1.0f; - joint->r1 = cpvsub(tb, a->p); - } else { - joint->clamp = 0.0f; - joint->r1 = cpvsub(cpvadd(cpvmult(cpvperp(n), -td), cpvmult(n, d)), a->p); - } - - // Calculate mass tensor - joint->k = k_tensor(a, b, joint->r1, joint->r2); - - // calculate bias velocity - cpVect delta = cpvsub(cpvadd(b->p, joint->r2), cpvadd(a->p, joint->r1)); - joint->bias = cpvclamp(cpvmult(delta, -bias_coef(joint->constraint.errorBias, dt)/dt), joint->constraint.maxBias); -} - -static void -applyCachedImpulse(cpGrooveJoint *joint, cpFloat dt_coef) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - apply_impulses(a, b, joint->r1, joint->r2, cpvmult(joint->jAcc, dt_coef)); -} - -static inline cpVect -grooveConstrain(cpGrooveJoint *joint, cpVect j, cpFloat dt){ - cpVect n = joint->grv_tn; - cpVect jClamp = (joint->clamp*cpvcross(j, n) > 0.0f) ? j : cpvproject(j, n); - return cpvclamp(jClamp, joint->constraint.maxForce*dt); -} - -static void -applyImpulse(cpGrooveJoint *joint, cpFloat dt) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - cpVect r1 = joint->r1; - cpVect r2 = joint->r2; - - // compute impulse - cpVect vr = relative_velocity(a, b, r1, r2); - - cpVect j = cpMat2x2Transform(joint->k, cpvsub(joint->bias, vr)); - cpVect jOld = joint->jAcc; - joint->jAcc = grooveConstrain(joint, cpvadd(jOld, j), dt); - j = cpvsub(joint->jAcc, jOld); - - // apply impulse - apply_impulses(a, b, joint->r1, joint->r2, j); -} - -static cpFloat -getImpulse(cpGrooveJoint *joint) -{ - return cpvlength(joint->jAcc); -} - -static const cpConstraintClass klass = { - (cpConstraintPreStepImpl)preStep, - (cpConstraintApplyCachedImpulseImpl)applyCachedImpulse, - (cpConstraintApplyImpulseImpl)applyImpulse, - (cpConstraintGetImpulseImpl)getImpulse, -}; - -cpGrooveJoint * -cpGrooveJointAlloc(void) -{ - return (cpGrooveJoint *)cpcalloc(1, sizeof(cpGrooveJoint)); -} - -cpGrooveJoint * -cpGrooveJointInit(cpGrooveJoint *joint, cpBody *a, cpBody *b, cpVect groove_a, cpVect groove_b, cpVect anchorB) -{ - cpConstraintInit((cpConstraint *)joint, &klass, a, b); - - joint->grv_a = groove_a; - joint->grv_b = groove_b; - joint->grv_n = cpvperp(cpvnormalize(cpvsub(groove_b, groove_a))); - joint->anchorB = anchorB; - - joint->jAcc = cpvzero; - - return joint; -} - -cpConstraint * -cpGrooveJointNew(cpBody *a, cpBody *b, cpVect groove_a, cpVect groove_b, cpVect anchorB) -{ - return (cpConstraint *)cpGrooveJointInit(cpGrooveJointAlloc(), a, b, groove_a, groove_b, anchorB); -} - -cpBool -cpConstraintIsGrooveJoint(const cpConstraint *constraint) -{ - return (constraint->klass == &klass); -} - -cpVect -cpGrooveJointGetGrooveA(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsGrooveJoint(constraint), "Constraint is not a groove joint."); - return ((cpGrooveJoint *)constraint)->grv_a; -} - -void -cpGrooveJointSetGrooveA(cpConstraint *constraint, cpVect value) -{ - cpAssertHard(cpConstraintIsGrooveJoint(constraint), "Constraint is not a groove joint."); - cpGrooveJoint *g = (cpGrooveJoint *)constraint; - - g->grv_a = value; - g->grv_n = cpvperp(cpvnormalize(cpvsub(g->grv_b, value))); - - cpConstraintActivateBodies(constraint); -} - -cpVect -cpGrooveJointGetGrooveB(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsGrooveJoint(constraint), "Constraint is not a groove joint."); - return ((cpGrooveJoint *)constraint)->grv_b; -} - -void -cpGrooveJointSetGrooveB(cpConstraint *constraint, cpVect value) -{ - cpAssertHard(cpConstraintIsGrooveJoint(constraint), "Constraint is not a groove joint."); - cpGrooveJoint *g = (cpGrooveJoint *)constraint; - - g->grv_b = value; - g->grv_n = cpvperp(cpvnormalize(cpvsub(value, g->grv_a))); - - cpConstraintActivateBodies(constraint); -} - -cpVect -cpGrooveJointGetAnchorB(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsGrooveJoint(constraint), "Constraint is not a groove joint."); - return ((cpGrooveJoint *)constraint)->anchorB; -} - -void -cpGrooveJointSetAnchorB(cpConstraint *constraint, cpVect anchorB) -{ - cpAssertHard(cpConstraintIsGrooveJoint(constraint), "Constraint is not a groove joint."); - cpConstraintActivateBodies(constraint); - ((cpGrooveJoint *)constraint)->anchorB = anchorB; -} diff --git a/3rdparty/chipmunk/src/cpHashSet.c b/3rdparty/chipmunk/src/cpHashSet.c deleted file mode 100644 index b2918defb531..000000000000 --- a/3rdparty/chipmunk/src/cpHashSet.c +++ /dev/null @@ -1,253 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk_private.h" -#include "prime.h" - -typedef struct cpHashSetBin { - void *elt; - cpHashValue hash; - struct cpHashSetBin *next; -} cpHashSetBin; - -struct cpHashSet { - unsigned int entries, size; - - cpHashSetEqlFunc eql; - void *default_value; - - cpHashSetBin **table; - cpHashSetBin *pooledBins; - - cpArray *allocatedBuffers; -}; - -void -cpHashSetFree(cpHashSet *set) -{ - if(set){ - cpfree(set->table); - - cpArrayFreeEach(set->allocatedBuffers, cpfree); - cpArrayFree(set->allocatedBuffers); - - cpfree(set); - } -} - -cpHashSet * -cpHashSetNew(int size, cpHashSetEqlFunc eqlFunc) -{ - cpHashSet *set = (cpHashSet *)cpcalloc(1, sizeof(cpHashSet)); - - set->size = next_prime(size); - set->entries = 0; - - set->eql = eqlFunc; - set->default_value = NULL; - - set->table = (cpHashSetBin **)cpcalloc(set->size, sizeof(cpHashSetBin *)); - set->pooledBins = NULL; - - set->allocatedBuffers = cpArrayNew(0); - - return set; -} - -void -cpHashSetSetDefaultValue(cpHashSet *set, void *default_value) -{ - set->default_value = default_value; -} - -static int -setIsFull(cpHashSet *set) -{ - return (set->entries >= set->size); -} - -static void -cpHashSetResize(cpHashSet *set) -{ - // Get the next approximate doubled prime. - unsigned int newSize = next_prime(set->size + 1); - // Allocate a new table. - cpHashSetBin **newTable = (cpHashSetBin **)cpcalloc(newSize, sizeof(cpHashSetBin *)); - - // Iterate over the chains. - for(unsigned int i=0; isize; i++){ - // Rehash the bins into the new table. - cpHashSetBin *bin = set->table[i]; - while(bin){ - cpHashSetBin *next = bin->next; - - cpHashValue idx = bin->hash%newSize; - bin->next = newTable[idx]; - newTable[idx] = bin; - - bin = next; - } - } - - cpfree(set->table); - - set->table = newTable; - set->size = newSize; -} - -static inline void -recycleBin(cpHashSet *set, cpHashSetBin *bin) -{ - bin->next = set->pooledBins; - set->pooledBins = bin; - bin->elt = NULL; -} - -static cpHashSetBin * -getUnusedBin(cpHashSet *set) -{ - cpHashSetBin *bin = set->pooledBins; - - if(bin){ - set->pooledBins = bin->next; - return bin; - } else { - // Pool is exhausted, make more - int count = CP_BUFFER_BYTES/sizeof(cpHashSetBin); - cpAssertHard(count, "Internal Error: Buffer size is too small."); - - cpHashSetBin *buffer = (cpHashSetBin *)cpcalloc(1, CP_BUFFER_BYTES); - cpArrayPush(set->allocatedBuffers, buffer); - - // push all but the first one, return it instead - for(int i=1; ientries; -} - -const void * -cpHashSetInsert(cpHashSet *set, cpHashValue hash, const void *ptr, cpHashSetTransFunc trans, void *data) -{ - cpHashValue idx = hash%set->size; - - // Find the bin with the matching element. - cpHashSetBin *bin = set->table[idx]; - while(bin && !set->eql(ptr, bin->elt)) - bin = bin->next; - - // Create it if necessary. - if(!bin){ - bin = getUnusedBin(set); - bin->hash = hash; - bin->elt = (trans ? trans(ptr, data) : data); - - bin->next = set->table[idx]; - set->table[idx] = bin; - - set->entries++; - if(setIsFull(set)) cpHashSetResize(set); - } - - return bin->elt; -} - -const void * -cpHashSetRemove(cpHashSet *set, cpHashValue hash, const void *ptr) -{ - cpHashValue idx = hash%set->size; - - cpHashSetBin **prev_ptr = &set->table[idx]; - cpHashSetBin *bin = set->table[idx]; - - // Find the bin - while(bin && !set->eql(ptr, bin->elt)){ - prev_ptr = &bin->next; - bin = bin->next; - } - - // Remove it if it exists. - if(bin){ - // Update the previous linked list pointer - (*prev_ptr) = bin->next; - set->entries--; - - const void *elt = bin->elt; - recycleBin(set, bin); - - return elt; - } - - return NULL; -} - -const void * -cpHashSetFind(cpHashSet *set, cpHashValue hash, const void *ptr) -{ - cpHashValue idx = hash%set->size; - cpHashSetBin *bin = set->table[idx]; - while(bin && !set->eql(ptr, bin->elt)) - bin = bin->next; - - return (bin ? bin->elt : set->default_value); -} - -void -cpHashSetEach(cpHashSet *set, cpHashSetIteratorFunc func, void *data) -{ - for(unsigned int i=0; isize; i++){ - cpHashSetBin *bin = set->table[i]; - while(bin){ - cpHashSetBin *next = bin->next; - func(bin->elt, data); - bin = next; - } - } -} - -void -cpHashSetFilter(cpHashSet *set, cpHashSetFilterFunc func, void *data) -{ - for(unsigned int i=0; isize; i++){ - // The rest works similarly to cpHashSetRemove() above. - cpHashSetBin **prev_ptr = &set->table[i]; - cpHashSetBin *bin = set->table[i]; - while(bin){ - cpHashSetBin *next = bin->next; - - if(func(bin->elt, data)){ - prev_ptr = &bin->next; - } else { - (*prev_ptr) = next; - - set->entries--; - recycleBin(set, bin); - } - - bin = next; - } - } -} diff --git a/3rdparty/chipmunk/src/cpHastySpace.c b/3rdparty/chipmunk/src/cpHastySpace.c deleted file mode 100644 index 8422c3ebe64a..000000000000 --- a/3rdparty/chipmunk/src/cpHastySpace.c +++ /dev/null @@ -1,700 +0,0 @@ -// Copyright 2013 Howling Moon Software. All rights reserved. -// See http://chipmunk2d.net/legal.php for more information. - -#include -#include - -//TODO: Move all the thread stuff to another file - -//#include - -#ifdef __APPLE__ -#include -#endif - -#ifndef _WIN32 -#include -#elif defined(__MINGW32__) -#include -#else -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif - -#ifndef NOMINMAX -#define NOMINMAX -#endif - -#include // _beginthreadex -#include - -#ifndef ETIMEDOUT -#define ETIMEDOUT 1 -#endif - -// Simple pthread implementation for Windows -// Made from scratch to avoid the LGPL licence from pthread-win32 -enum { - SIGNAL = 0, - BROADCAST = 1, - MAX_EVENTS = 2 -}; - -typedef HANDLE pthread_t; -typedef struct -{ - // Based on http://www.cs.wustl.edu/~schmidt/win32-cv-1.html since Windows has no condition variable until NT6 - UINT waiters_count; - // Count of the number of waiters. - - CRITICAL_SECTION waiters_count_lock; - // Serialize access to . - - HANDLE events[MAX_EVENTS]; -} pthread_cond_t; -typedef CRITICAL_SECTION pthread_mutex_t; - -typedef struct {} pthread_condattr_t; // Dummy; - -int pthread_cond_destroy(pthread_cond_t* cv) -{ - CloseHandle(cv->events[BROADCAST]); - CloseHandle(cv->events[SIGNAL]); - - DeleteCriticalSection(&cv->waiters_count_lock); - - return 0; -} - -int pthread_cond_init(pthread_cond_t* cv, const pthread_condattr_t* attr) -{ - // Initialize the count to 0. - cv->waiters_count = 0; - - // Create an auto-reset event. - cv->events[SIGNAL] = CreateEvent(NULL, // no security - FALSE, // auto-reset event - FALSE, // non-signaled initially - NULL); // unnamed - - // Create a manual-reset event. - cv->events[BROADCAST] = CreateEvent(NULL, // no security - TRUE, // manual-reset - FALSE, // non-signaled initially - NULL); // unnamed - - InitializeCriticalSection(&cv->waiters_count_lock); - - return 0; -} - -int pthread_cond_broadcast(pthread_cond_t *cv) -{ - // Avoid race conditions. - EnterCriticalSection(&cv->waiters_count_lock); - int have_waiters = cv->waiters_count > 0; - LeaveCriticalSection(&cv->waiters_count_lock); - - if (have_waiters) - SetEvent(cv->events[BROADCAST]); - - return 0; -} - -int pthread_cond_signal(pthread_cond_t* cv) -{ - // Avoid race conditions. - EnterCriticalSection(&cv->waiters_count_lock); - int have_waiters = cv->waiters_count > 0; - LeaveCriticalSection(&cv->waiters_count_lock); - - if (have_waiters) - SetEvent(cv->events[SIGNAL]); - - return 0; -} - -int pthread_cond_wait(pthread_cond_t* cv, pthread_mutex_t* external_mutex) -{ - // Avoid race conditions. - EnterCriticalSection(&cv->waiters_count_lock); - cv->waiters_count++; - LeaveCriticalSection(&cv->waiters_count_lock); - - // It's ok to release the here since Win32 - // manual-reset events maintain state when used with - // . This avoids the "lost wakeup" bug... - LeaveCriticalSection(external_mutex); - - // Wait for either event to become signaled due to - // being called or being called. - int result = WaitForMultipleObjects(2, cv->events, FALSE, INFINITE); - - EnterCriticalSection(&cv->waiters_count_lock); - cv->waiters_count--; - int last_waiter = - result == WAIT_OBJECT_0 + BROADCAST - && cv->waiters_count == 0; - LeaveCriticalSection(&cv->waiters_count_lock); - - // Some thread called . - if (last_waiter) - // We're the last waiter to be notified or to stop waiting, so - // reset the manual event. - ResetEvent(cv->events[BROADCAST]); - - // Reacquire the . - EnterCriticalSection(external_mutex); - - return result == WAIT_TIMEOUT ? ETIMEDOUT : 0; -} - -typedef struct {} pthread_mutexattr_t; //< Dummy - -int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* attr) -{ - InitializeCriticalSection(mutex); - return 0; -} - -int pthread_mutex_destroy(pthread_mutex_t* mutex) -{ - DeleteCriticalSection(mutex); - return 0; -} - -int pthread_mutex_lock(pthread_mutex_t* mutex) -{ - EnterCriticalSection(mutex); - return 0; -} - -int pthread_mutex_unlock(pthread_mutex_t* mutex) -{ - LeaveCriticalSection(mutex); - return 0; -} - -typedef struct {} pthread_attr_t; - -typedef struct -{ - void *(*start_routine) (void *); - void* arg; -} pthread_internal_thread; - -unsigned int __stdcall ThreadProc(void* userdata) -{ - pthread_internal_thread* ud = (pthread_internal_thread*) userdata; - ud->start_routine(ud->arg); - - free(ud); - - return 0; -} - -int pthread_create(pthread_t* thread, const pthread_attr_t* attr, void *(*start_routine) (void *), void *arg) -{ - pthread_internal_thread* ud = (pthread_internal_thread*) malloc(sizeof(pthread_internal_thread)); - ud->start_routine = start_routine; - ud->arg = arg; - - *thread = (HANDLE) (_beginthreadex(NULL, 0, &ThreadProc, ud, 0, NULL)); - if (!*thread) - return 1; - - return 0; -} - -int pthread_join(pthread_t thread, void **value_ptr) -{ - WaitForSingleObject(thread, INFINITE); - CloseHandle(thread); - - return 0; -} - -#endif - -#include "chipmunk/chipmunk_private.h" -#include "chipmunk/cpHastySpace.h" - - -//MARK: ARM NEON Solver - -#if __ARM_NEON__ -#include - -// Tested and known to work fine with Clang 3.0 and GCC 4.2 -// Doesn't work with Clang 1.6, and I have no idea why. -#if defined(__clang_major__) && __clang_major__ < 3 - #error Compiler not supported. -#endif - -#if CP_USE_DOUBLES - #if !__arm64 - #error Cannot use CP_USE_DOUBLES on 32 bit ARM. - #endif - - typedef float64_t cpFloat_t; - typedef float64x2_t cpFloatx2_t; - #define vld vld1q_f64 - #define vdup_n vdupq_n_f64 - #define vst vst1q_f64 - #define vst_lane vst1q_lane_f64 - #define vadd vaddq_f64 - #define vsub vsubq_f64 - #define vpadd vpaddq_f64 - #define vmul vmulq_f64 - #define vmul_n vmulq_n_f64 - #define vneg vnegq_f64 - #define vget_lane vgetq_lane_f64 - #define vset_lane vsetq_lane_f64 - #define vmin vminq_f64 - #define vmax vmaxq_f64 - #define vrev(__a) __builtin_shufflevector(__a, __a, 1, 0) -#else - typedef float32_t cpFloat_t; - typedef float32x2_t cpFloatx2_t; - #define vld vld1_f32 - #define vdup_n vdup_n_f32 - #define vst vst1_f32 - #define vst_lane vst1_lane_f32 - #define vadd vadd_f32 - #define vsub vsub_f32 - #define vpadd vpadd_f32 - #define vmul vmul_f32 - #define vmul_n vmul_n_f32 - #define vneg vneg_f32 - #define vget_lane vget_lane_f32 - #define vset_lane vset_lane_f32 - #define vmin vmin_f32 - #define vmax vmax_f32 - #define vrev vrev64_f32 -#endif - -// TODO could probably do better here, maybe using vcreate? -// especially for the constants -// Maybe use the {} notation for GCC/Clang? -static inline cpFloatx2_t -vmake(cpFloat_t x, cpFloat_t y) -{ -// cpFloatx2_t v = {}; -// v = vset_lane(x, v, 0); -// v = vset_lane(y, v, 1); -// -// return v; - - // This might not be super compatible, but all the NEON headers use it... - return (cpFloatx2_t){x, y}; -} - -static void -cpArbiterApplyImpulse_NEON(cpArbiter *arb) -{ - cpBody *a = arb->body_a; - cpBody *b = arb->body_b; - cpFloatx2_t surface_vr = vld((cpFloat_t *)&arb->surface_vr); - cpFloatx2_t n = vld((cpFloat_t *)&arb->n); - cpFloat_t friction = arb->u; - - int numContacts = arb->count; - struct cpContact *contacts = arb->contacts; - for(int i=0; ir1); - cpFloatx2_t r2 = vld((cpFloat_t *)&con->r2); - - cpFloatx2_t perp = vmake(-1.0, 1.0); - cpFloatx2_t r1p = vmul(vrev(r1), perp); - cpFloatx2_t r2p = vmul(vrev(r2), perp); - - cpFloatx2_t vBias_a = vld((cpFloat_t *)&a->v_bias); - cpFloatx2_t vBias_b = vld((cpFloat_t *)&b->v_bias); - cpFloatx2_t wBias = vmake(a->w_bias, b->w_bias); - - cpFloatx2_t vb1 = vadd(vBias_a, vmul_n(r1p, vget_lane(wBias, 0))); - cpFloatx2_t vb2 = vadd(vBias_b, vmul_n(r2p, vget_lane(wBias, 1))); - cpFloatx2_t vbr = vsub(vb2, vb1); - - cpFloatx2_t v_a = vld((cpFloat_t *)&a->v); - cpFloatx2_t v_b = vld((cpFloat_t *)&b->v); - cpFloatx2_t w = vmake(a->w, b->w); - cpFloatx2_t v1 = vadd(v_a, vmul_n(r1p, vget_lane(w, 0))); - cpFloatx2_t v2 = vadd(v_b, vmul_n(r2p, vget_lane(w, 1))); - cpFloatx2_t vr = vsub(v2, v1); - - cpFloatx2_t vbn_vrn = vpadd(vmul(vbr, n), vmul(vr, n)); - - cpFloatx2_t v_offset = vmake(con->bias, -con->bounce); - cpFloatx2_t jOld = vmake(con->jBias, con->jnAcc); - cpFloatx2_t jbn_jn = vmul_n(vsub(v_offset, vbn_vrn), con->nMass); - jbn_jn = vmax(vadd(jOld, jbn_jn), vdup_n(0.0)); - cpFloatx2_t jApply = vsub(jbn_jn, jOld); - - cpFloatx2_t t = vmul(vrev(n), perp); - cpFloatx2_t vrt_tmp = vmul(vadd(vr, surface_vr), t); - cpFloatx2_t vrt = vpadd(vrt_tmp, vrt_tmp); - - cpFloatx2_t jtOld = {}; jtOld = vset_lane(con->jtAcc, jtOld, 0); - cpFloatx2_t jtMax = vrev(vmul_n(jbn_jn, friction)); - cpFloatx2_t jt = vmul_n(vrt, -con->tMass); - jt = vmax(vneg(jtMax), vmin(vadd(jtOld, jt), jtMax)); - cpFloatx2_t jtApply = vsub(jt, jtOld); - - cpFloatx2_t i_inv = vmake(-a->i_inv, b->i_inv); - cpFloatx2_t nperp = vmake(1.0, -1.0); - - cpFloatx2_t jBias = vmul_n(n, vget_lane(jApply, 0)); - cpFloatx2_t jBiasCross = vmul(vrev(jBias), nperp); - cpFloatx2_t biasCrosses = vpadd(vmul(r1, jBiasCross), vmul(r2, jBiasCross)); - wBias = vadd(wBias, vmul(i_inv, biasCrosses)); - - vBias_a = vsub(vBias_a, vmul_n(jBias, a->m_inv)); - vBias_b = vadd(vBias_b, vmul_n(jBias, b->m_inv)); - - cpFloatx2_t j = vadd(vmul_n(n, vget_lane(jApply, 1)), vmul_n(t, vget_lane(jtApply, 0))); - cpFloatx2_t jCross = vmul(vrev(j), nperp); - cpFloatx2_t crosses = vpadd(vmul(r1, jCross), vmul(r2, jCross)); - w = vadd(w, vmul(i_inv, crosses)); - - v_a = vsub(v_a, vmul_n(j, a->m_inv)); - v_b = vadd(v_b, vmul_n(j, b->m_inv)); - - // TODO would moving these earlier help pipeline them better? - vst((cpFloat_t *)&a->v_bias, vBias_a); - vst((cpFloat_t *)&b->v_bias, vBias_b); - vst_lane((cpFloat_t *)&a->w_bias, wBias, 0); - vst_lane((cpFloat_t *)&b->w_bias, wBias, 1); - - vst((cpFloat_t *)&a->v, v_a); - vst((cpFloat_t *)&b->v, v_b); - vst_lane((cpFloat_t *)&a->w, w, 0); - vst_lane((cpFloat_t *)&b->w, w, 1); - - vst_lane((cpFloat_t *)&con->jBias, jbn_jn, 0); - vst_lane((cpFloat_t *)&con->jnAcc, jbn_jn, 1); - vst_lane((cpFloat_t *)&con->jtAcc, jt, 0); - } -} - -#endif - -//MARK: PThreads - -// Right now using more than 2 threads probably wont help your performance any. -// If you are using a ridiculous number of iterations it could help though. -#define MAX_THREADS 2 - -struct ThreadContext { - pthread_t thread; - cpHastySpace *space; - unsigned long thread_num; -}; - -typedef void (*cpHastySpaceWorkFunction)(cpSpace *space, unsigned long worker, unsigned long worker_count); - -struct cpHastySpace { - cpSpace space; - - // Number of worker threads (including the main thread) - unsigned long num_threads; - - // Number of worker threads currently executing. (also including the main thread) - unsigned long num_working; - - // Number of constraints (plus contacts) that must exist per step to start the worker threads. - unsigned long constraint_count_threshold; - - pthread_mutex_t mutex; - pthread_cond_t cond_work, cond_resume; - - // Work function to invoke. - cpHastySpaceWorkFunction work; - - struct ThreadContext workers[MAX_THREADS - 1]; -}; - -static void * -WorkerThreadLoop(struct ThreadContext *context) -{ - cpHastySpace *hasty = context->space; - - unsigned long thread = context->thread_num; - unsigned long num_threads = hasty->num_threads; - - for(;;){ - pthread_mutex_lock(&hasty->mutex); { - if(--hasty->num_working == 0){ - pthread_cond_signal(&hasty->cond_resume); - } - - pthread_cond_wait(&hasty->cond_work, &hasty->mutex); - } pthread_mutex_unlock(&hasty->mutex); - - cpHastySpaceWorkFunction func = hasty->work; - if(func){ - hasty->work(&hasty->space, thread, num_threads); - } else { - break; - } - } - - return NULL; -} - -static void -RunWorkers(cpHastySpace *hasty, cpHastySpaceWorkFunction func) -{ - hasty->num_working = hasty->num_threads - 1; - hasty->work = func; - - if(hasty->num_working > 0){ - pthread_mutex_lock(&hasty->mutex); { - pthread_cond_broadcast(&hasty->cond_work); - } pthread_mutex_unlock(&hasty->mutex); - - func((cpSpace *)hasty, 0, hasty->num_threads); - - pthread_mutex_lock(&hasty->mutex); { - if(hasty->num_working > 0){ - pthread_cond_wait(&hasty->cond_resume, &hasty->mutex); - } - } pthread_mutex_unlock(&hasty->mutex); - } else { - func((cpSpace *)hasty, 0, hasty->num_threads); - } - - hasty->work = NULL; -} - -static void -Solver(cpSpace *space, unsigned long worker, unsigned long worker_count) -{ - cpArray *constraints = space->constraints; - cpArray *arbiters = space->arbiters; - - cpFloat dt = space->curr_dt; - unsigned long iterations = (space->iterations + worker_count - 1)/worker_count; - - for(unsigned long i=0; inum; j++){ - cpArbiter *arb = (cpArbiter *)arbiters->arr[j]; - #ifdef __ARM_NEON__ - cpArbiterApplyImpulse_NEON(arb); - #else - cpArbiterApplyImpulse(arb); - #endif - } - - for(int j=0; jnum; j++){ - cpConstraint *constraint = (cpConstraint *)constraints->arr[j]; - constraint->klass->applyImpulse(constraint, dt); - } - } -} - -//MARK: Thread Management Functions - -static void -HaltThreads(cpHastySpace *hasty) -{ - pthread_mutex_t *mutex = &hasty->mutex; - pthread_mutex_lock(mutex); { - hasty->work = NULL; // NULL work function means break and exit - pthread_cond_broadcast(&hasty->cond_work); - } pthread_mutex_unlock(mutex); - - for(unsigned long i=0; i<(hasty->num_threads-1); i++){ - pthread_join(hasty->workers[i].thread, NULL); - } -} - -void -cpHastySpaceSetThreads(cpSpace *space, unsigned long threads) -{ -#if TARGET_IPHONE_SIMULATOR == 1 - // Individual values appear to be written non-atomically when compiled as debug for the simulator. - // No idea why, so threads are disabled. - threads = 1; -#endif - - cpHastySpace *hasty = (cpHastySpace *)space; - HaltThreads(hasty); - -#ifdef __APPLE__ - if(threads == 0){ - size_t size = sizeof(threads); - sysctlbyname("hw.ncpu", &threads, &size, NULL, 0); - } -#else - if(threads == 0) threads = 1; -#endif - - hasty->num_threads = (threads < MAX_THREADS ? threads : MAX_THREADS); - hasty->num_working = hasty->num_threads - 1; - - // Create the worker threads and wait for them to signal ready. - if(hasty->num_working > 0){ - pthread_mutex_lock(&hasty->mutex); - for(unsigned long i=0; i<(hasty->num_threads-1); i++){ - hasty->workers[i].space = hasty; - hasty->workers[i].thread_num = i + 1; - - pthread_create(&hasty->workers[i].thread, NULL, (void*(*)(void*))WorkerThreadLoop, &hasty->workers[i]); - } - - pthread_cond_wait(&hasty->cond_resume, &hasty->mutex); - pthread_mutex_unlock(&hasty->mutex); - } -} - -unsigned long -cpHastySpaceGetThreads(cpSpace *space) -{ - return ((cpHastySpace *)space)->num_threads; -} - -//MARK: Overriden cpSpace Functions. - -cpSpace * -cpHastySpaceNew(void) -{ - cpHastySpace *hasty = (cpHastySpace *)cpcalloc(1, sizeof(cpHastySpace)); - cpSpaceInit((cpSpace *)hasty); - - pthread_mutex_init(&hasty->mutex, NULL); - pthread_cond_init(&hasty->cond_work, NULL); - pthread_cond_init(&hasty->cond_resume, NULL); - - // TODO magic number, should test this more thoroughly. - hasty->constraint_count_threshold = 50; - - // Default to 1 thread for determinism. - hasty->num_threads = 1; - cpHastySpaceSetThreads((cpSpace *)hasty, 1); - - return (cpSpace *)hasty; -} - -void -cpHastySpaceFree(cpSpace *space) -{ - cpHastySpace *hasty = (cpHastySpace *)space; - - HaltThreads(hasty); - - pthread_mutex_destroy(&hasty->mutex); - pthread_cond_destroy(&hasty->cond_work); - pthread_cond_destroy(&hasty->cond_resume); - - cpSpaceFree(space); -} - -void -cpHastySpaceStep(cpSpace *space, cpFloat dt) -{ - // don't step if the timestep is 0! - if(dt == 0.0f) return; - - space->stamp++; - - cpFloat prev_dt = space->curr_dt; - space->curr_dt = dt; - - cpArray *bodies = space->dynamicBodies; - cpArray *constraints = space->constraints; - cpArray *arbiters = space->arbiters; - - // Reset and empty the arbiter list. - for(int i=0; inum; i++){ - cpArbiter *arb = (cpArbiter *)arbiters->arr[i]; - arb->state = CP_ARBITER_STATE_NORMAL; - - // If both bodies are awake, unthread the arbiter from the contact graph. - if(!cpBodyIsSleeping(arb->body_a) && !cpBodyIsSleeping(arb->body_b)){ - cpArbiterUnthread(arb); - } - } - arbiters->num = 0; - - cpSpaceLock(space); { - // Integrate positions - for(int i=0; inum; i++){ - cpBody *body = (cpBody *)bodies->arr[i]; - body->position_func(body, dt); - } - - // Find colliding pairs. - cpSpacePushFreshContactBuffer(space); - cpSpatialIndexEach(space->dynamicShapes, (cpSpatialIndexIteratorFunc)cpShapeUpdateFunc, NULL); - cpSpatialIndexReindexQuery(space->dynamicShapes, (cpSpatialIndexQueryFunc)cpSpaceCollideShapes, space); - } cpSpaceUnlock(space, cpFalse); - - // Rebuild the contact graph (and detect sleeping components if sleeping is enabled) - cpSpaceProcessComponents(space, dt); - - cpSpaceLock(space); { - // Clear out old cached arbiters and call separate callbacks - cpHashSetFilter(space->cachedArbiters, (cpHashSetFilterFunc)cpSpaceArbiterSetFilter, space); - - // Prestep the arbiters and constraints. - cpFloat slop = space->collisionSlop; - cpFloat biasCoef = 1.0f - cpfpow(space->collisionBias, dt); - for(int i=0; inum; i++){ - cpArbiterPreStep((cpArbiter *)arbiters->arr[i], dt, slop, biasCoef); - } - - for(int i=0; inum; i++){ - cpConstraint *constraint = (cpConstraint *)constraints->arr[i]; - - cpConstraintPreSolveFunc preSolve = constraint->preSolve; - if(preSolve) preSolve(constraint, space); - - constraint->klass->preStep(constraint, dt); - } - - // Integrate velocities. - cpFloat damping = cpfpow(space->damping, dt); - cpVect gravity = space->gravity; - for(int i=0; inum; i++){ - cpBody *body = (cpBody *)bodies->arr[i]; - body->velocity_func(body, gravity, damping, dt); - } - - // Apply cached impulses - cpFloat dt_coef = (prev_dt == 0.0f ? 0.0f : dt/prev_dt); - for(int i=0; inum; i++){ - cpArbiterApplyCachedImpulse((cpArbiter *)arbiters->arr[i], dt_coef); - } - - for(int i=0; inum; i++){ - cpConstraint *constraint = (cpConstraint *)constraints->arr[i]; - constraint->klass->applyCachedImpulse(constraint, dt_coef); - } - - // Run the impulse solver. - cpHastySpace *hasty = (cpHastySpace *)space; - if((unsigned long)(arbiters->num + constraints->num) > hasty->constraint_count_threshold){ - RunWorkers(hasty, Solver); - } else { - Solver(space, 0, 1); - } - - // Run the constraint post-solve callbacks - for(int i=0; inum; i++){ - cpConstraint *constraint = (cpConstraint *)constraints->arr[i]; - - cpConstraintPostSolveFunc postSolve = constraint->postSolve; - if(postSolve) postSolve(constraint, space); - } - - // run the post-solve callbacks - for(int i=0; inum; i++){ - cpArbiter *arb = (cpArbiter *) arbiters->arr[i]; - - cpCollisionHandler *handler = arb->handler; - handler->postSolveFunc(arb, space, handler->userData); - } - } cpSpaceUnlock(space, cpTrue); -} diff --git a/3rdparty/chipmunk/src/cpMarch.c b/3rdparty/chipmunk/src/cpMarch.c deleted file mode 100644 index 1ba0dabb4601..000000000000 --- a/3rdparty/chipmunk/src/cpMarch.c +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2013 Howling Moon Software. All rights reserved. -// See http://chipmunk2d.net/legal.php for more information. - -#include -#include -#include - -#include "chipmunk/chipmunk.h" -#include "chipmunk/cpMarch.h" - - -typedef void (*cpMarchCellFunc)( - cpFloat t, cpFloat a, cpFloat b, cpFloat c, cpFloat d, - cpFloat x0, cpFloat x1, cpFloat y0, cpFloat y1, - cpMarchSegmentFunc segment, void *segment_data -); - -// The looping and sample caching code is shared between cpMarchHard() and cpMarchSoft(). -static void -cpMarchCells( - cpBB bb, unsigned long x_samples, unsigned long y_samples, cpFloat t, - cpMarchSegmentFunc segment, void *segment_data, - cpMarchSampleFunc sample, void *sample_data, - cpMarchCellFunc cell -){ - cpFloat x_denom = 1.0/(cpFloat)(x_samples - 1); - cpFloat y_denom = 1.0/(cpFloat)(y_samples - 1); - - // TODO range assertions and short circuit for 0 sized windows. - - // Keep a copy of the previous row to avoid double lookups. - cpFloat *buffer = (cpFloat *)cpcalloc(x_samples, sizeof(cpFloat)); - for(unsigned long i=0; it)<<0 | (b>t)<<1 | (c>t)<<2 | (d>t)<<3){ - case 0x1: seg(cpv(x0, midlerp(y0,y1,a,c,t)), cpv(midlerp(x0,x1,a,b,t), y0), segment, segment_data); break; - case 0x2: seg(cpv(midlerp(x0,x1,a,b,t), y0), cpv(x1, midlerp(y0,y1,b,d,t)), segment, segment_data); break; - case 0x3: seg(cpv(x0, midlerp(y0,y1,a,c,t)), cpv(x1, midlerp(y0,y1,b,d,t)), segment, segment_data); break; - case 0x4: seg(cpv(midlerp(x0,x1,c,d,t), y1), cpv(x0, midlerp(y0,y1,a,c,t)), segment, segment_data); break; - case 0x5: seg(cpv(midlerp(x0,x1,c,d,t), y1), cpv(midlerp(x0,x1,a,b,t), y0), segment, segment_data); break; - case 0x6: seg(cpv(midlerp(x0,x1,a,b,t), y0), cpv(x1, midlerp(y0,y1,b,d,t)), segment, segment_data); - seg(cpv(midlerp(x0,x1,c,d,t), y1), cpv(x0, midlerp(y0,y1,a,c,t)), segment, segment_data); break; - case 0x7: seg(cpv(midlerp(x0,x1,c,d,t), y1), cpv(x1, midlerp(y0,y1,b,d,t)), segment, segment_data); break; - case 0x8: seg(cpv(x1, midlerp(y0,y1,b,d,t)), cpv(midlerp(x0,x1,c,d,t), y1), segment, segment_data); break; - case 0x9: seg(cpv(x0, midlerp(y0,y1,a,c,t)), cpv(midlerp(x0,x1,a,b,t), y0), segment, segment_data); - seg(cpv(x1, midlerp(y0,y1,b,d,t)), cpv(midlerp(x0,x1,c,d,t), y1), segment, segment_data); break; - case 0xA: seg(cpv(midlerp(x0,x1,a,b,t), y0), cpv(midlerp(x0,x1,c,d,t), y1), segment, segment_data); break; - case 0xB: seg(cpv(x0, midlerp(y0,y1,a,c,t)), cpv(midlerp(x0,x1,c,d,t), y1), segment, segment_data); break; - case 0xC: seg(cpv(x1, midlerp(y0,y1,b,d,t)), cpv(x0, midlerp(y0,y1,a,c,t)), segment, segment_data); break; - case 0xD: seg(cpv(x1, midlerp(y0,y1,b,d,t)), cpv(midlerp(x0,x1,a,b,t), y0), segment, segment_data); break; - case 0xE: seg(cpv(midlerp(x0,x1,a,b,t), y0), cpv(x0, midlerp(y0,y1,a,c,t)), segment, segment_data); break; - default: break; // 0x0 and 0xF - } -} - -void -cpMarchSoft( - cpBB bb, unsigned long x_samples, unsigned long y_samples, cpFloat t, - cpMarchSegmentFunc segment, void *segment_data, - cpMarchSampleFunc sample, void *sample_data -){ - cpMarchCells(bb, x_samples, y_samples, t, segment, segment_data, sample, sample_data, cpMarchCellSoft); -} - - -// TODO should flip this around eventually. -static inline void -segs(cpVect a, cpVect b, cpVect c, cpMarchSegmentFunc f, void *data) -{ - seg(b, c, f, data); - seg(a, b, f, data); -} - -static void -cpMarchCellHard( - cpFloat t, cpFloat a, cpFloat b, cpFloat c, cpFloat d, - cpFloat x0, cpFloat x1, cpFloat y0, cpFloat y1, - cpMarchSegmentFunc segment, void *segment_data -){ - // midpoints - cpFloat xm = cpflerp(x0, x1, 0.5f); - cpFloat ym = cpflerp(y0, y1, 0.5f); - - switch((a>t)<<0 | (b>t)<<1 | (c>t)<<2 | (d>t)<<3){ - case 0x1: segs(cpv(x0, ym), cpv(xm, ym), cpv(xm, y0), segment, segment_data); break; - case 0x2: segs(cpv(xm, y0), cpv(xm, ym), cpv(x1, ym), segment, segment_data); break; - case 0x3: seg(cpv(x0, ym), cpv(x1, ym), segment, segment_data); break; - case 0x4: segs(cpv(xm, y1), cpv(xm, ym), cpv(x0, ym), segment, segment_data); break; - case 0x5: seg(cpv(xm, y1), cpv(xm, y0), segment, segment_data); break; - case 0x6: segs(cpv(xm, y0), cpv(xm, ym), cpv(x0, ym), segment, segment_data); - segs(cpv(xm, y1), cpv(xm, ym), cpv(x1, ym), segment, segment_data); break; - case 0x7: segs(cpv(xm, y1), cpv(xm, ym), cpv(x1, ym), segment, segment_data); break; - case 0x8: segs(cpv(x1, ym), cpv(xm, ym), cpv(xm, y1), segment, segment_data); break; - case 0x9: segs(cpv(x1, ym), cpv(xm, ym), cpv(xm, y0), segment, segment_data); - segs(cpv(x0, ym), cpv(xm, ym), cpv(xm, y1), segment, segment_data); break; - case 0xA: seg(cpv(xm, y0), cpv(xm, y1), segment, segment_data); break; - case 0xB: segs(cpv(x0, ym), cpv(xm, ym), cpv(xm, y1), segment, segment_data); break; - case 0xC: seg(cpv(x1, ym), cpv(x0, ym), segment, segment_data); break; - case 0xD: segs(cpv(x1, ym), cpv(xm, ym), cpv(xm, y0), segment, segment_data); break; - case 0xE: segs(cpv(xm, y0), cpv(xm, ym), cpv(x0, ym), segment, segment_data); break; - default: break; // 0x0 and 0xF - } -} - -void -cpMarchHard( - cpBB bb, unsigned long x_samples, unsigned long y_samples, cpFloat t, - cpMarchSegmentFunc segment, void *segment_data, - cpMarchSampleFunc sample, void *sample_data -){ - cpMarchCells(bb, x_samples, y_samples, t, segment, segment_data, sample, sample_data, cpMarchCellHard); -} diff --git a/3rdparty/chipmunk/src/cpPinJoint.c b/3rdparty/chipmunk/src/cpPinJoint.c deleted file mode 100644 index 545e78bf8b50..000000000000 --- a/3rdparty/chipmunk/src/cpPinJoint.c +++ /dev/null @@ -1,172 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk_private.h" - -static void -preStep(cpPinJoint *joint, cpFloat dt) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - joint->r1 = cpTransformVect(a->transform, cpvsub(joint->anchorA, a->cog)); - joint->r2 = cpTransformVect(b->transform, cpvsub(joint->anchorB, b->cog)); - - cpVect delta = cpvsub(cpvadd(b->p, joint->r2), cpvadd(a->p, joint->r1)); - cpFloat dist = cpvlength(delta); - joint->n = cpvmult(delta, 1.0f/(dist ? dist : (cpFloat)INFINITY)); - - // calculate mass normal - joint->nMass = 1.0f/k_scalar(a, b, joint->r1, joint->r2, joint->n); - - // calculate bias velocity - cpFloat maxBias = joint->constraint.maxBias; - joint->bias = cpfclamp(-bias_coef(joint->constraint.errorBias, dt)*(dist - joint->dist)/dt, -maxBias, maxBias); -} - -static void -applyCachedImpulse(cpPinJoint *joint, cpFloat dt_coef) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - cpVect j = cpvmult(joint->n, joint->jnAcc*dt_coef); - apply_impulses(a, b, joint->r1, joint->r2, j); -} - -static void -applyImpulse(cpPinJoint *joint, cpFloat dt) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - cpVect n = joint->n; - - // compute relative velocity - cpFloat vrn = normal_relative_velocity(a, b, joint->r1, joint->r2, n); - - cpFloat jnMax = joint->constraint.maxForce*dt; - - // compute normal impulse - cpFloat jn = (joint->bias - vrn)*joint->nMass; - cpFloat jnOld = joint->jnAcc; - joint->jnAcc = cpfclamp(jnOld + jn, -jnMax, jnMax); - jn = joint->jnAcc - jnOld; - - // apply impulse - apply_impulses(a, b, joint->r1, joint->r2, cpvmult(n, jn)); -} - -static cpFloat -getImpulse(cpPinJoint *joint) -{ - return cpfabs(joint->jnAcc); -} - -static const cpConstraintClass klass = { - (cpConstraintPreStepImpl)preStep, - (cpConstraintApplyCachedImpulseImpl)applyCachedImpulse, - (cpConstraintApplyImpulseImpl)applyImpulse, - (cpConstraintGetImpulseImpl)getImpulse, -}; - - -cpPinJoint * -cpPinJointAlloc(void) -{ - return (cpPinJoint *)cpcalloc(1, sizeof(cpPinJoint)); -} - -cpPinJoint * -cpPinJointInit(cpPinJoint *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB) -{ - cpConstraintInit((cpConstraint *)joint, &klass, a, b); - - joint->anchorA = anchorA; - joint->anchorB = anchorB; - - // STATIC_BODY_CHECK - cpVect p1 = (a ? cpTransformPoint(a->transform, anchorA) : anchorA); - cpVect p2 = (b ? cpTransformPoint(b->transform, anchorB) : anchorB); - joint->dist = cpvlength(cpvsub(p2, p1)); - - cpAssertWarn(joint->dist > 0.0, "You created a 0 length pin joint. A pivot joint will be much more stable."); - - joint->jnAcc = 0.0f; - - return joint; -} - -cpConstraint * -cpPinJointNew(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB) -{ - return (cpConstraint *)cpPinJointInit(cpPinJointAlloc(), a, b, anchorA, anchorB); -} - -cpBool -cpConstraintIsPinJoint(const cpConstraint *constraint) -{ - return (constraint->klass == &klass); -} - -cpVect -cpPinJointGetAnchorA(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsPinJoint(constraint), "Constraint is not a pin joint."); - return ((cpPinJoint *)constraint)->anchorA; -} - -void -cpPinJointSetAnchorA(cpConstraint *constraint, cpVect anchorA) -{ - cpAssertHard(cpConstraintIsPinJoint(constraint), "Constraint is not a pin joint."); - cpConstraintActivateBodies(constraint); - ((cpPinJoint *)constraint)->anchorA = anchorA; -} - -cpVect -cpPinJointGetAnchorB(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsPinJoint(constraint), "Constraint is not a pin joint."); - return ((cpPinJoint *)constraint)->anchorB; -} - -void -cpPinJointSetAnchorB(cpConstraint *constraint, cpVect anchorB) -{ - cpAssertHard(cpConstraintIsPinJoint(constraint), "Constraint is not a pin joint."); - cpConstraintActivateBodies(constraint); - ((cpPinJoint *)constraint)->anchorB = anchorB; -} - -cpFloat -cpPinJointGetDist(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsPinJoint(constraint), "Constraint is not a pin joint."); - return ((cpPinJoint *)constraint)->dist; -} - -void -cpPinJointSetDist(cpConstraint *constraint, cpFloat dist) -{ - cpAssertHard(cpConstraintIsPinJoint(constraint), "Constraint is not a pin joint."); - cpConstraintActivateBodies(constraint); - ((cpPinJoint *)constraint)->dist = dist; -} diff --git a/3rdparty/chipmunk/src/cpPivotJoint.c b/3rdparty/chipmunk/src/cpPivotJoint.c deleted file mode 100644 index e45ba072bce5..000000000000 --- a/3rdparty/chipmunk/src/cpPivotJoint.c +++ /dev/null @@ -1,152 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk_private.h" - -static void -preStep(cpPivotJoint *joint, cpFloat dt) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - joint->r1 = cpTransformVect(a->transform, cpvsub(joint->anchorA, a->cog)); - joint->r2 = cpTransformVect(b->transform, cpvsub(joint->anchorB, b->cog)); - - // Calculate mass tensor - joint-> k = k_tensor(a, b, joint->r1, joint->r2); - - // calculate bias velocity - cpVect delta = cpvsub(cpvadd(b->p, joint->r2), cpvadd(a->p, joint->r1)); - joint->bias = cpvclamp(cpvmult(delta, -bias_coef(joint->constraint.errorBias, dt)/dt), joint->constraint.maxBias); -} - -static void -applyCachedImpulse(cpPivotJoint *joint, cpFloat dt_coef) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - apply_impulses(a, b, joint->r1, joint->r2, cpvmult(joint->jAcc, dt_coef)); -} - -static void -applyImpulse(cpPivotJoint *joint, cpFloat dt) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - cpVect r1 = joint->r1; - cpVect r2 = joint->r2; - - // compute relative velocity - cpVect vr = relative_velocity(a, b, r1, r2); - - // compute normal impulse - cpVect j = cpMat2x2Transform(joint->k, cpvsub(joint->bias, vr)); - cpVect jOld = joint->jAcc; - joint->jAcc = cpvclamp(cpvadd(joint->jAcc, j), joint->constraint.maxForce*dt); - j = cpvsub(joint->jAcc, jOld); - - // apply impulse - apply_impulses(a, b, joint->r1, joint->r2, j); -} - -static cpFloat -getImpulse(cpConstraint *joint) -{ - return cpvlength(((cpPivotJoint *)joint)->jAcc); -} - -static const cpConstraintClass klass = { - (cpConstraintPreStepImpl)preStep, - (cpConstraintApplyCachedImpulseImpl)applyCachedImpulse, - (cpConstraintApplyImpulseImpl)applyImpulse, - (cpConstraintGetImpulseImpl)getImpulse, -}; - -cpPivotJoint * -cpPivotJointAlloc(void) -{ - return (cpPivotJoint *)cpcalloc(1, sizeof(cpPivotJoint)); -} - -cpPivotJoint * -cpPivotJointInit(cpPivotJoint *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB) -{ - cpConstraintInit((cpConstraint *)joint, &klass, a, b); - - joint->anchorA = anchorA; - joint->anchorB = anchorB; - - joint->jAcc = cpvzero; - - return joint; -} - -cpConstraint * -cpPivotJointNew2(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB) -{ - return (cpConstraint *)cpPivotJointInit(cpPivotJointAlloc(), a, b, anchorA, anchorB); -} - -cpConstraint * -cpPivotJointNew(cpBody *a, cpBody *b, cpVect pivot) -{ - cpVect anchorA = (a ? cpBodyWorldToLocal(a, pivot) : pivot); - cpVect anchorB = (b ? cpBodyWorldToLocal(b, pivot) : pivot); - return cpPivotJointNew2(a, b, anchorA, anchorB); -} - -cpBool -cpConstraintIsPivotJoint(const cpConstraint *constraint) -{ - return (constraint->klass == &klass); -} - -cpVect -cpPivotJointGetAnchorA(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsPivotJoint(constraint), "Constraint is not a pivot joint."); - return ((cpPivotJoint *)constraint)->anchorA; -} - -void -cpPivotJointSetAnchorA(cpConstraint *constraint, cpVect anchorA) -{ - cpAssertHard(cpConstraintIsPivotJoint(constraint), "Constraint is not a pivot joint."); - cpConstraintActivateBodies(constraint); - ((cpPivotJoint *)constraint)->anchorA = anchorA; -} - -cpVect -cpPivotJointGetAnchorB(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsPivotJoint(constraint), "Constraint is not a pivot joint."); - return ((cpPivotJoint *)constraint)->anchorB; -} - -void -cpPivotJointSetAnchorB(cpConstraint *constraint, cpVect anchorB) -{ - cpAssertHard(cpConstraintIsPivotJoint(constraint), "Constraint is not a pivot joint."); - cpConstraintActivateBodies(constraint); - ((cpPivotJoint *)constraint)->anchorB = anchorB; -} diff --git a/3rdparty/chipmunk/src/cpPolyShape.c b/3rdparty/chipmunk/src/cpPolyShape.c deleted file mode 100644 index 554a7f8f313c..000000000000 --- a/3rdparty/chipmunk/src/cpPolyShape.c +++ /dev/null @@ -1,324 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk_private.h" -#include "chipmunk/chipmunk_unsafe.h" - -cpPolyShape * -cpPolyShapeAlloc(void) -{ - return (cpPolyShape *)cpcalloc(1, sizeof(cpPolyShape)); -} - -static void -cpPolyShapeDestroy(cpPolyShape *poly) -{ - if(poly->count > CP_POLY_SHAPE_INLINE_ALLOC){ - cpfree(poly->planes); - } -} - -static cpBB -cpPolyShapeCacheData(cpPolyShape *poly, cpTransform transform) -{ - int count = poly->count; - struct cpSplittingPlane *dst = poly->planes; - struct cpSplittingPlane *src = dst + count; - - cpFloat l = (cpFloat)INFINITY, r = -(cpFloat)INFINITY; - cpFloat b = (cpFloat)INFINITY, t = -(cpFloat)INFINITY; - - for(int i=0; ir; - return (poly->shape.bb = cpBBNew(l - radius, b - radius, r + radius, t + radius)); -} - -static void -cpPolyShapePointQuery(cpPolyShape *poly, cpVect p, cpPointQueryInfo *info){ - int count = poly->count; - struct cpSplittingPlane *planes = poly->planes; - cpFloat r = poly->r; - - cpVect v0 = planes[count - 1].v0; - cpFloat minDist = INFINITY; - cpVect closestPoint = cpvzero; - cpVect closestNormal = cpvzero; - cpBool outside = cpFalse; - - for(int i=0; i 0.0f); - - cpVect closest = cpClosetPointOnSegment(p, v0, v1); - - cpFloat dist = cpvdist(p, closest); - if(dist < minDist){ - minDist = dist; - closestPoint = closest; - closestNormal = planes[i].n; - } - - v0 = v1; - } - - cpFloat dist = (outside ? minDist : -minDist); - cpVect g = cpvmult(cpvsub(p, closestPoint), 1.0f/dist); - - info->shape = (cpShape *)poly; - info->point = cpvadd(closestPoint, cpvmult(g, r)); - info->distance = dist - r; - - // Use the normal of the closest segment if the distance is small. - info->gradient = (minDist > MAGIC_EPSILON ? g : closestNormal); -} - -static void -cpPolyShapeSegmentQuery(cpPolyShape *poly, cpVect a, cpVect b, cpFloat r2, cpSegmentQueryInfo *info) -{ - struct cpSplittingPlane *planes = poly->planes; - int count = poly->count; - cpFloat r = poly->r; - cpFloat rsum = r + r2; - - for(int i=0; ishape = (cpShape *)poly; - info->point = cpvsub(cpvlerp(a, b, t), cpvmult(n, r2)); - info->normal = n; - info->alpha = t; - } - } - - // Also check against the beveled vertexes. - if(rsum > 0.0f){ - for(int i=0; ishape, planes[i].v0, r, a, b, r2, &circle_info); - if(circle_info.alpha < info->alpha) (*info) = circle_info; - } - } -} - -static void -SetVerts(cpPolyShape *poly, int count, const cpVect *verts) -{ - poly->count = count; - if(count <= CP_POLY_SHAPE_INLINE_ALLOC){ - poly->planes = poly->_planes; - } else { - poly->planes = (struct cpSplittingPlane *)cpcalloc(2*count, sizeof(struct cpSplittingPlane)); - } - - for(int i=0; iplanes[i + count].v0 = b; - poly->planes[i + count].n = n; - } -} - -static struct cpShapeMassInfo -cpPolyShapeMassInfo(cpFloat mass, int count, const cpVect *verts, cpFloat radius) -{ - // TODO moment is approximate due to radius. - - cpVect centroid = cpCentroidForPoly(count, verts); - struct cpShapeMassInfo info = { - mass, cpMomentForPoly(1.0f, count, verts, cpvneg(centroid), radius), - centroid, - cpAreaForPoly(count, verts, radius), - }; - - return info; -} - -static const cpShapeClass polyClass = { - CP_POLY_SHAPE, - (cpShapeCacheDataImpl)cpPolyShapeCacheData, - (cpShapeDestroyImpl)cpPolyShapeDestroy, - (cpShapePointQueryImpl)cpPolyShapePointQuery, - (cpShapeSegmentQueryImpl)cpPolyShapeSegmentQuery, -}; - -cpPolyShape * -cpPolyShapeInit(cpPolyShape *poly, cpBody *body, int count, const cpVect *verts, cpTransform transform, cpFloat radius) -{ - cpVect *hullVerts = (cpVect *)alloca(count*sizeof(cpVect)); - - // Transform the verts before building the hull in case of a negative scale. - for(int i=0; ir = radius; - - return poly; -} - -cpShape * -cpPolyShapeNew(cpBody *body, int count, const cpVect *verts, cpTransform transform, cpFloat radius) -{ - return (cpShape *)cpPolyShapeInit(cpPolyShapeAlloc(), body, count, verts, transform, radius); -} - -cpShape * -cpPolyShapeNewRaw(cpBody *body, int count, const cpVect *verts, cpFloat radius) -{ - return (cpShape *)cpPolyShapeInitRaw(cpPolyShapeAlloc(), body, count, verts, radius); -} - -cpPolyShape * -cpBoxShapeInit(cpPolyShape *poly, cpBody *body, cpFloat width, cpFloat height, cpFloat radius) -{ - cpFloat hw = width/2.0f; - cpFloat hh = height/2.0f; - - return cpBoxShapeInit2(poly, body, cpBBNew(-hw, -hh, hw, hh), radius); -} - -cpPolyShape * -cpBoxShapeInit2(cpPolyShape *poly, cpBody *body, cpBB box, cpFloat radius) -{ - cpVect verts[4] = { - cpv(box.r, box.b), - cpv(box.r, box.t), - cpv(box.l, box.t), - cpv(box.l, box.b), - }; - - return cpPolyShapeInitRaw(poly, body, 4, verts, radius); -} - -cpShape * -cpBoxShapeNew(cpBody *body, cpFloat width, cpFloat height, cpFloat radius) -{ - return (cpShape *)cpBoxShapeInit(cpPolyShapeAlloc(), body, width, height, radius); -} - -cpShape * -cpBoxShapeNew2(cpBody *body, cpBB box, cpFloat radius) -{ - return (cpShape *)cpBoxShapeInit2(cpPolyShapeAlloc(), body, box, radius); -} - -int -cpPolyShapeGetCount(const cpShape *shape) -{ - cpAssertHard(shape->klass == &polyClass, "Shape is not a poly shape."); - return ((cpPolyShape *)shape)->count; -} - -cpVect -cpPolyShapeGetVert(const cpShape *shape, int i) -{ - cpAssertHard(shape->klass == &polyClass, "Shape is not a poly shape."); - - int count = cpPolyShapeGetCount(shape); - cpAssertHard(0 <= i && i < count, "Index out of range."); - - return ((cpPolyShape *)shape)->planes[i + count].v0; -} - -cpFloat -cpPolyShapeGetRadius(const cpShape *shape) -{ - cpAssertHard(shape->klass == &polyClass, "Shape is not a poly shape."); - return ((cpPolyShape *)shape)->r; -} - -// Unsafe API (chipmunk_unsafe.h) - -void -cpPolyShapeSetVerts(cpShape *shape, int count, cpVect *verts, cpTransform transform) -{ - cpVect *hullVerts = (cpVect *)alloca(count*sizeof(cpVect)); - - // Transform the verts before building the hull in case of a negative scale. - for(int i=0; iklass == &polyClass, "Shape is not a poly shape."); - cpPolyShape *poly = (cpPolyShape *)shape; - cpPolyShapeDestroy(poly); - - SetVerts(poly, count, verts); - - cpFloat mass = shape->massInfo.m; - shape->massInfo = cpPolyShapeMassInfo(shape->massInfo.m, count, verts, poly->r); - if(mass > 0.0f) cpBodyAccumulateMassFromShapes(shape->body); -} - -void -cpPolyShapeSetRadius(cpShape *shape, cpFloat radius) -{ - cpAssertHard(shape->klass == &polyClass, "Shape is not a poly shape."); - cpPolyShape *poly = (cpPolyShape *)shape; - poly->r = radius; - - - // TODO radius is not handled by moment/area -// cpFloat mass = shape->massInfo.m; -// shape->massInfo = cpPolyShapeMassInfo(shape->massInfo.m, poly->count, poly->verts, poly->r); -// if(mass > 0.0f) cpBodyAccumulateMassFromShapes(shape->body); -} diff --git a/3rdparty/chipmunk/src/cpPolyline.c b/3rdparty/chipmunk/src/cpPolyline.c deleted file mode 100644 index 5b375348d4ea..000000000000 --- a/3rdparty/chipmunk/src/cpPolyline.c +++ /dev/null @@ -1,652 +0,0 @@ -// Copyright 2013 Howling Moon Software. All rights reserved. -// See http://chipmunk2d.net/legal.php for more information. - -#include -#include -#include -#include - -#include "chipmunk/chipmunk_private.h" -#include "chipmunk/cpPolyline.h" - - -static inline int Next(int i, int count){return (i+1)%count;} - -//MARK: Polylines - -#define DEFAULT_POLYLINE_CAPACITY 16 - -static int -cpPolylineSizeForCapacity(int capacity) -{ - return sizeof(cpPolyline) + capacity*sizeof(cpVect); -} - -static cpPolyline * -cpPolylineMake(int capacity) -{ - capacity = (capacity > DEFAULT_POLYLINE_CAPACITY ? capacity : DEFAULT_POLYLINE_CAPACITY); - - cpPolyline *line = (cpPolyline *)cpcalloc(1, cpPolylineSizeForCapacity(capacity)); - line->count = 0; - line->capacity = capacity; - - return line; -} - -static cpPolyline * -cpPolylineMake2(int capacity, cpVect a, cpVect b) -{ - cpPolyline *line = cpPolylineMake(capacity); - line->count = 2; - line->verts[0] = a; - line->verts[1] = b; - - return line; -} - -static cpPolyline * -cpPolylineShrink(cpPolyline *line) -{ - line->capacity = line->count; - return (cpPolyline*) cprealloc(line, cpPolylineSizeForCapacity(line->count)); -} - -void -cpPolylineFree(cpPolyline *line) -{ - cpfree(line); -} - -// Grow the allocated memory for a polyline. -static cpPolyline * -cpPolylineGrow(cpPolyline *line, int count) -{ - line->count += count; - - int capacity = line->capacity; - while(line->count > capacity) capacity *= 2; - - if(line->capacity < capacity){ - line->capacity = capacity; - line = (cpPolyline*) cprealloc(line, cpPolylineSizeForCapacity(capacity)); - } - - return line; -} - -// Push v onto the end of line. -static cpPolyline * -cpPolylinePush(cpPolyline *line, cpVect v) -{ - int count = line->count; - line = cpPolylineGrow(line, 1); - line->verts[count] = v; - - return line; -} - -// Push v onto the beginning of line. -static cpPolyline * -cpPolylineEnqueue(cpPolyline *line, cpVect v) -{ - // TODO could optimize this to grow in both directions. - // Probably doesn't matter though. - int count = line->count; - line = cpPolylineGrow(line, 1); - memmove(line->verts + 1, line->verts, count*sizeof(cpVect)); - line->verts[0] = v; - - return line; -} - -// Returns true if the polyline starts and ends with the same vertex. -cpBool -cpPolylineIsClosed(cpPolyline *line) -{ - return (line->count > 1 && cpveql(line->verts[0], line->verts[line->count-1])); -} - -// Check if a cpPolyline is longer than a certain length -// Takes a range which can wrap around if the polyline is looped. -static cpBool -cpPolylineIsShort(cpVect *points, int count, int start, int end, cpFloat min) -{ - cpFloat length = 0.0f; - for(int i=start; i!=end; i=Next(i, count)){ - length += cpvdist(points[i], points[Next(i, count)]); - if(length > min) return cpFalse; - } - - return cpTrue; -} - -//MARK: Polyline Simplification - -static inline cpFloat -Sharpness(cpVect a, cpVect b, cpVect c) -{ - // TODO could speed this up by caching the normals instead of calculating each twice. - return cpvdot(cpvnormalize(cpvsub(a, b)), cpvnormalize(cpvsub(c, b))); -} - -// Join similar adjacent line segments together. Works well for hard edged shapes. -// 'tol' is the minimum anglular difference in radians of a vertex. -cpPolyline * -cpPolylineSimplifyVertexes(cpPolyline *line, cpFloat tol) -{ - cpPolyline *reduced = cpPolylineMake2(0, line->verts[0], line->verts[1]); - - cpFloat minSharp = -cpfcos(tol); - - for(int i=2; icount; i++){ - cpVect vert = line->verts[i]; - cpFloat sharp = Sharpness(reduced->verts[reduced->count - 2], reduced->verts[reduced->count - 1], vert); - - if(sharp <= minSharp){ - reduced->verts[reduced->count - 1] = vert; - } else { - reduced = cpPolylinePush(reduced, vert); - } - } - - if( - cpPolylineIsClosed(line) && - Sharpness(reduced->verts[reduced->count - 2], reduced->verts[0], reduced->verts[1]) < minSharp - ){ - reduced->verts[0] = reduced->verts[reduced->count - 2]; - reduced->count--; - } - - // TODO shrink - return reduced; -} - -// Recursive function used by cpPolylineSimplifyCurves(). -static cpPolyline * -DouglasPeucker( - cpVect *verts, cpPolyline *reduced, - int length, int start, int end, - cpFloat min, cpFloat tol -){ - // Early exit if the points are adjacent - if((end - start + length)%length < 2) return reduced; - - cpVect a = verts[start]; - cpVect b = verts[end]; - - // Check if the length is below the threshold - if(cpvnear(a, b, min) && cpPolylineIsShort(verts, length, start, end, min)) return reduced; - - // Find the maximal vertex to split and recurse on - cpFloat max = 0.0; - int maxi = start; - - cpVect n = cpvnormalize(cpvperp(cpvsub(b, a))); - cpFloat d = cpvdot(n, a); - - for(int i=Next(start, length); i!=end; i=Next(i, length)){ - cpFloat dist = fabs(cpvdot(n, verts[i]) - d); - - if(dist > max){ - max = dist; - maxi = i; - } - } - - if(max > tol){ - reduced = DouglasPeucker(verts, reduced, length, start, maxi, min, tol); - reduced = cpPolylinePush(reduced, verts[maxi]); - reduced = DouglasPeucker(verts, reduced, length, maxi, end, min, tol); - } - - return reduced; -} - -// Recursively reduce the vertex count on a polyline. Works best for smooth shapes. -// 'tol' is the maximum error for the reduction. -// The reduced polyline will never be farther than this distance from the original polyline. -cpPolyline * -cpPolylineSimplifyCurves(cpPolyline *line, cpFloat tol) -{ - cpPolyline *reduced = cpPolylineMake(line->count); - - cpFloat min = tol/2.0f; - - if(cpPolylineIsClosed(line)){ - int start, end; - cpLoopIndexes(line->verts, line->count - 1, &start, &end); - - reduced = cpPolylinePush(reduced, line->verts[start]); - reduced = DouglasPeucker(line->verts, reduced, line->count - 1, start, end, min, tol); - reduced = cpPolylinePush(reduced, line->verts[end]); - reduced = DouglasPeucker(line->verts, reduced, line->count - 1, end, start, min, tol); - reduced = cpPolylinePush(reduced, line->verts[start]); - } else { - reduced = cpPolylinePush(reduced, line->verts[0]); - reduced = DouglasPeucker(line->verts, reduced, line->count, 0, line->count - 1, min, tol); - reduced = cpPolylinePush(reduced, line->verts[line->count - 1]); - } - - return cpPolylineShrink(reduced); -} - -//MARK: Polyline Sets - -cpPolylineSet * -cpPolylineSetAlloc(void) -{ - return (cpPolylineSet *)cpcalloc(1, sizeof(cpPolylineSet)); -} - -cpPolylineSet * -cpPolylineSetInit(cpPolylineSet *set) -{ - set->count = 0; - set->capacity = 8; - set->lines = (cpPolyline**) cpcalloc(set->capacity, sizeof(cpPolyline)); - - return set; -} - - -cpPolylineSet * -cpPolylineSetNew(void) -{ - return cpPolylineSetInit(cpPolylineSetAlloc()); -} - -void -cpPolylineSetDestroy(cpPolylineSet *set, cpBool freePolylines) -{ - if(freePolylines){ - for(int i=0; icount; i++){ - cpPolylineFree(set->lines[i]); - } - } - - cpfree(set->lines); -} - - -void -cpPolylineSetFree(cpPolylineSet *set, cpBool freePolylines) -{ - if(set){ - cpPolylineSetDestroy(set, freePolylines); - cpfree(set); - } -} - -// Find the polyline that ends with v. -static int -cpPolylineSetFindEnds(cpPolylineSet *set, cpVect v){ - int count = set->count; - cpPolyline **lines = set->lines; - - for(int i=0; iverts[line->count - 1], v)) return i; - } - - return -1; -} - -// Find the polyline that starts with v. -static int -cpPolylineSetFindStarts(cpPolylineSet *set, cpVect v){ - int count = set->count; - cpPolyline **lines = set->lines; - - for(int i=0; iverts[0], v)) return i; - } - - return -1; -} - -// Add a new polyline to a polyline set. -static void -cpPolylineSetPush(cpPolylineSet *set, cpPolyline *line) -{ - // grow set - set->count++; - if(set->count > set->capacity){ - set->capacity *= 2; - set->lines = (cpPolyline**) cprealloc(set->lines, set->capacity*sizeof(cpPolyline)); - } - - set->lines[set->count - 1] = line; -} - -// Add a new polyline to a polyline set. -static void -cpPolylineSetAdd(cpPolylineSet *set, cpVect v0, cpVect v1) -{ - cpPolylineSetPush(set, cpPolylineMake2(DEFAULT_POLYLINE_CAPACITY, v0, v1)); -} - -// Join two cpPolylines in a polyline set together. -static void -cpPolylineSetJoin(cpPolylineSet *set, int before, int after) -{ - cpPolyline *lbefore = set->lines[before]; - cpPolyline *lafter = set->lines[after]; - - // append - int count = lbefore->count; - lbefore = cpPolylineGrow(lbefore, lafter->count); - memmove(lbefore->verts + count, lafter->verts, lafter->count*sizeof(cpVect)); - set->lines[before] = lbefore; - - // delete lafter - set->count--; - cpPolylineFree(set->lines[after]); - set->lines[after] = set->lines[set->count]; -} - -// Add a segment to a polyline set. -// A segment will either start a new polyline, join two others, or add to or loop an existing polyline. -void -cpPolylineSetCollectSegment(cpVect v0, cpVect v1, cpPolylineSet *lines) -{ - int before = cpPolylineSetFindEnds(lines, v0); - int after = cpPolylineSetFindStarts(lines, v1); - - if(before >= 0 && after >= 0){ - if(before == after){ - // loop by pushing v1 onto before - lines->lines[before] = cpPolylinePush(lines->lines[before], v1); - } else { - // join before and after - cpPolylineSetJoin(lines, before, after); - } - } else if(before >= 0){ - // push v1 onto before - lines->lines[before] = cpPolylinePush(lines->lines[before], v1); - } else if(after >= 0){ - // enqueue v0 onto after - lines->lines[after] = cpPolylineEnqueue(lines->lines[after], v0); - } else { - // create new line from v0 and v1 - cpPolylineSetAdd(lines, v0, v1); - } -} - -//MARK: Convex Hull Functions - -cpPolyline * -cpPolylineToConvexHull(cpPolyline *line, cpFloat tol) -{ - cpPolyline *hull = cpPolylineMake(line->count + 1); - hull->count = cpConvexHull(line->count, line->verts, hull->verts, NULL, tol); - hull = cpPolylinePush(hull, hull->verts[0]); - - return cpPolylineShrink(hull); -} - -//MARK: Approximate Concave Decompostition - -struct Notch { - int i; - cpFloat d; - cpVect v; - cpVect n; -}; - -static cpFloat -FindSteiner(int count, cpVect *verts, struct Notch notch) -{ - cpFloat min = INFINITY; - cpFloat feature = -1.0; - - for(int i=1; i= 0.0 && dist <= min){ - min = dist; - feature = index + t; - } - } - } - - return feature; -} - -//static cpFloat -//FindSteiner2(cpVect *verts, int count, struct Notch notch) -//{ -// cpVect a = verts[(notch.i + count - 1)%count]; -// cpVect b = verts[(notch.i + 1)%count]; -// cpVect n = cpvnormalize(cpvadd(cpvnormalize(cpvsub(notch.v, a)), cpvnormalize(cpvsub(notch.v, b)))); -// -// cpFloat min = INFINITY; -// cpFloat feature = -1.0; -// -// for(int i=1; i= 0.0 && dist <= min){ -// min = dist; -// feature = index + t; -// } -// } -// } -// -// cpAssertSoft(feature >= 0.0, "No closest features detected. This is likely due to a self intersecting polygon."); -// return feature; -//} - -//struct Range {cpFloat min, max;}; -//static inline struct Range -//clip_range(cpVect delta_a, cpVect delta_b, cpVect clip) -//{ -// cpFloat da = cpvcross(delta_a, clip); -// cpFloat db = cpvcross(delta_b, clip); -// cpFloat clamp = da/(da - db); -// if(da > db){ -// return (struct Range){-INFINITY, clamp}; -// } else if(da < db){ -// return (struct Range){clamp, INFINITY}; -// } else { -// return (struct Range){-INFINITY, INFINITY}; -// } -//} -// -//static cpFloat -//FindSteiner3(cpVect *verts, int count, struct Notch notch) -//{ -// cpFloat min = INFINITY; -// cpFloat feature = -1.0; -// -// cpVect support_a = verts[(notch.i - 1 + count)%count]; -// cpVect support_b = verts[(notch.i + 1)%count]; -// -// cpVect clip_a = cpvlerp(support_a, support_b, 0.1); -// cpVect clip_b = cpvlerp(support_b, support_b, 0.9); -// -// for(int i=1; i 0.0){ -// struct Range range1 = clip_range(delta_a, delta_b, cpvsub(notch.v, clip_a)); -// struct Range range2 = clip_range(delta_a, delta_b, cpvsub(clip_b, notch.v)); -// -// cpFloat min_t = cpfmax(0.0, cpfmax(range1.min, range2.min)); -// cpFloat max_t = cpfmin(1.0, cpfmin(range1.max, range2.max)); -// -// // Ignore if the segment has been completely clipped away. -// if(min_t < max_t){ -// cpVect seg_delta = cpvsub(seg_b, seg_a); -// cpFloat closest_t = cpfclamp(cpvdot(seg_delta, cpvsub(notch.v, seg_a))/cpvlengthsq(seg_delta), min_t, max_t); -// cpVect closest = cpvlerp(seg_a, seg_b, closest_t); -// -// cpFloat dist = cpvdistsq(notch.v, closest); -// if(dist < min){ -// min = dist; -// feature = index + closest_t; -// } -// } -// } -// } -// -// cpAssertWarn(feature >= 0.0, "Internal Error: No closest features detected."); -// return feature; -//} - -//static cpBool -//VertexUnobscured(int count, cpVect *verts, int index, int notch_i) -//{ -// cpVect v = verts[notch_i]; -// cpVect n = cpvnormalize(cpvsub(verts[index], v)); -// -// for(int i=0; i= 0.0, "No closest features detected. This is likely due to a self intersecting polygon."); -// return feature; -//} - -static struct Notch -DeepestNotch(int count, cpVect *verts, int hullCount, cpVect *hullVerts, int first, cpFloat tol) -{ - struct Notch notch = {}; - int j = Next(first, count); - - for(int i=0; i notch.d){ - notch.d = depth; - notch.i = j; - notch.v = v; - notch.n = n; - } - - j = Next(j, count); - v = verts[j]; - } - - j = Next(j, count); - } - - return notch; -} - -static inline int IMAX(int a, int b){return (a > b ? a : b);} - -static void -ApproximateConcaveDecomposition(cpVect *verts, int count, cpFloat tol, cpPolylineSet *set) -{ - int first; - cpVect *hullVerts = (cpVect*) alloca(count*sizeof(cpVect)); - int hullCount = cpConvexHull(count, verts, hullVerts, &first, 0.0); - - if(hullCount != count){ - struct Notch notch = DeepestNotch(count, verts, hullCount, hullVerts, first, tol); - - if(notch.d > tol){ - cpFloat steiner_it = FindSteiner(count, verts, notch); - - if(steiner_it >= 0.0){ - int steiner_i = (int)steiner_it; - cpVect steiner = cpvlerp(verts[steiner_i], verts[Next(steiner_i, count)], steiner_it - steiner_i); - - // Vertex counts NOT including the steiner point. - int sub1_count = (steiner_i - notch.i + count)%count + 1; - int sub2_count = count - (steiner_i - notch.i + count)%count; - cpVect *scratch = (cpVect*) alloca((IMAX(sub1_count, sub2_count) + 1)*sizeof(cpVect)); - - for(int i=0; iverts, hullVerts, hullCount*sizeof(cpVect)); - hull->verts[hullCount] = hullVerts[0]; - hull->count = hullCount + 1; - - cpPolylineSetPush(set, hull); -} - -cpPolylineSet * -cpPolylineConvexDecomposition_BETA(cpPolyline *line, cpFloat tol) -{ - cpAssertSoft(cpPolylineIsClosed(line), "Cannot decompose an open polygon."); - cpAssertSoft(cpAreaForPoly(line->count, line->verts, 0.0) >= 0.0, "Winding is backwards. (Are you passing a hole?)"); - - cpPolylineSet *set = cpPolylineSetNew(); - ApproximateConcaveDecomposition(line->verts, line->count - 1, tol, set); - - return set; -} diff --git a/3rdparty/chipmunk/src/cpRatchetJoint.c b/3rdparty/chipmunk/src/cpRatchetJoint.c deleted file mode 100644 index b3c9687e3562..000000000000 --- a/3rdparty/chipmunk/src/cpRatchetJoint.c +++ /dev/null @@ -1,179 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk_private.h" - -static void -preStep(cpRatchetJoint *joint, cpFloat dt) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - cpFloat angle = joint->angle; - cpFloat phase = joint->phase; - cpFloat ratchet = joint->ratchet; - - cpFloat delta = b->a - a->a; - cpFloat diff = angle - delta; - cpFloat pdist = 0.0f; - - if(diff*ratchet > 0.0f){ - pdist = diff; - } else { - joint->angle = cpffloor((delta - phase)/ratchet)*ratchet + phase; - } - - // calculate moment of inertia coefficient. - joint->iSum = 1.0f/(a->i_inv + b->i_inv); - - // calculate bias velocity - cpFloat maxBias = joint->constraint.maxBias; - joint->bias = cpfclamp(-bias_coef(joint->constraint.errorBias, dt)*pdist/dt, -maxBias, maxBias); - - // If the bias is 0, the joint is not at a limit. Reset the impulse. - if(!joint->bias) joint->jAcc = 0.0f; -} - -static void -applyCachedImpulse(cpRatchetJoint *joint, cpFloat dt_coef) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - cpFloat j = joint->jAcc*dt_coef; - a->w -= j*a->i_inv; - b->w += j*b->i_inv; -} - -static void -applyImpulse(cpRatchetJoint *joint, cpFloat dt) -{ - if(!joint->bias) return; // early exit - - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - // compute relative rotational velocity - cpFloat wr = b->w - a->w; - cpFloat ratchet = joint->ratchet; - - cpFloat jMax = joint->constraint.maxForce*dt; - - // compute normal impulse - cpFloat j = -(joint->bias + wr)*joint->iSum; - cpFloat jOld = joint->jAcc; - joint->jAcc = cpfclamp((jOld + j)*ratchet, 0.0f, jMax*cpfabs(ratchet))/ratchet; - j = joint->jAcc - jOld; - - // apply impulse - a->w -= j*a->i_inv; - b->w += j*b->i_inv; -} - -static cpFloat -getImpulse(cpRatchetJoint *joint) -{ - return cpfabs(joint->jAcc); -} - -static const cpConstraintClass klass = { - (cpConstraintPreStepImpl)preStep, - (cpConstraintApplyCachedImpulseImpl)applyCachedImpulse, - (cpConstraintApplyImpulseImpl)applyImpulse, - (cpConstraintGetImpulseImpl)getImpulse, -}; - -cpRatchetJoint * -cpRatchetJointAlloc(void) -{ - return (cpRatchetJoint *)cpcalloc(1, sizeof(cpRatchetJoint)); -} - -cpRatchetJoint * -cpRatchetJointInit(cpRatchetJoint *joint, cpBody *a, cpBody *b, cpFloat phase, cpFloat ratchet) -{ - cpConstraintInit((cpConstraint *)joint, &klass, a, b); - - joint->angle = 0.0f; - joint->phase = phase; - joint->ratchet = ratchet; - - // STATIC_BODY_CHECK - joint->angle = (b ? b->a : 0.0f) - (a ? a->a : 0.0f); - - return joint; -} - -cpConstraint * -cpRatchetJointNew(cpBody *a, cpBody *b, cpFloat phase, cpFloat ratchet) -{ - return (cpConstraint *)cpRatchetJointInit(cpRatchetJointAlloc(), a, b, phase, ratchet); -} - -cpBool -cpConstraintIsRatchetJoint(const cpConstraint *constraint) -{ - return (constraint->klass == &klass); -} - -cpFloat -cpRatchetJointGetAngle(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsRatchetJoint(constraint), "Constraint is not a ratchet joint."); - return ((cpRatchetJoint *)constraint)->angle; -} - -void -cpRatchetJointSetAngle(cpConstraint *constraint, cpFloat angle) -{ - cpAssertHard(cpConstraintIsRatchetJoint(constraint), "Constraint is not a ratchet joint."); - cpConstraintActivateBodies(constraint); - ((cpRatchetJoint *)constraint)->angle = angle; -} - -cpFloat -cpRatchetJointGetPhase(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsRatchetJoint(constraint), "Constraint is not a ratchet joint."); - return ((cpRatchetJoint *)constraint)->phase; -} - -void -cpRatchetJointSetPhase(cpConstraint *constraint, cpFloat phase) -{ - cpAssertHard(cpConstraintIsRatchetJoint(constraint), "Constraint is not a ratchet joint."); - cpConstraintActivateBodies(constraint); - ((cpRatchetJoint *)constraint)->phase = phase; -} -cpFloat -cpRatchetJointGetRatchet(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsRatchetJoint(constraint), "Constraint is not a ratchet joint."); - return ((cpRatchetJoint *)constraint)->ratchet; -} - -void -cpRatchetJointSetRatchet(cpConstraint *constraint, cpFloat ratchet) -{ - cpAssertHard(cpConstraintIsRatchetJoint(constraint), "Constraint is not a ratchet joint."); - cpConstraintActivateBodies(constraint); - ((cpRatchetJoint *)constraint)->ratchet = ratchet; -} diff --git a/3rdparty/chipmunk/src/cpRobust.c b/3rdparty/chipmunk/src/cpRobust.c deleted file mode 100644 index 57507d14e6b4..000000000000 --- a/3rdparty/chipmunk/src/cpRobust.c +++ /dev/null @@ -1,13 +0,0 @@ -#include "chipmunk/cpRobust.h" - - -cpBool -cpCheckPointGreater(const cpVect a, const cpVect b, const cpVect c) -{ - return (b.y - a.y)*(a.x + b.x - 2*c.x) > (b.x - a.x)*(a.y + b.y - 2*c.y); -} - -cpBool -cpCheckAxis(cpVect v0, cpVect v1, cpVect p, cpVect n){ - return cpvdot(p, n) <= cpfmax(cpvdot(v0, n), cpvdot(v1, n)); -} diff --git a/3rdparty/chipmunk/src/cpRotaryLimitJoint.c b/3rdparty/chipmunk/src/cpRotaryLimitJoint.c deleted file mode 100644 index 548adbebf053..000000000000 --- a/3rdparty/chipmunk/src/cpRotaryLimitJoint.c +++ /dev/null @@ -1,160 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk_private.h" - -static void -preStep(cpRotaryLimitJoint *joint, cpFloat dt) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - cpFloat dist = b->a - a->a; - cpFloat pdist = 0.0f; - if(dist > joint->max) { - pdist = joint->max - dist; - } else if(dist < joint->min) { - pdist = joint->min - dist; - } - - // calculate moment of inertia coefficient. - joint->iSum = 1.0f/(a->i_inv + b->i_inv); - - // calculate bias velocity - cpFloat maxBias = joint->constraint.maxBias; - joint->bias = cpfclamp(-bias_coef(joint->constraint.errorBias, dt)*pdist/dt, -maxBias, maxBias); - - // If the bias is 0, the joint is not at a limit. Reset the impulse. - if(!joint->bias) joint->jAcc = 0.0f; -} - -static void -applyCachedImpulse(cpRotaryLimitJoint *joint, cpFloat dt_coef) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - cpFloat j = joint->jAcc*dt_coef; - a->w -= j*a->i_inv; - b->w += j*b->i_inv; -} - -static void -applyImpulse(cpRotaryLimitJoint *joint, cpFloat dt) -{ - if(!joint->bias) return; // early exit - - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - // compute relative rotational velocity - cpFloat wr = b->w - a->w; - - cpFloat jMax = joint->constraint.maxForce*dt; - - // compute normal impulse - cpFloat j = -(joint->bias + wr)*joint->iSum; - cpFloat jOld = joint->jAcc; - if(joint->bias < 0.0f){ - joint->jAcc = cpfclamp(jOld + j, 0.0f, jMax); - } else { - joint->jAcc = cpfclamp(jOld + j, -jMax, 0.0f); - } - j = joint->jAcc - jOld; - - // apply impulse - a->w -= j*a->i_inv; - b->w += j*b->i_inv; -} - -static cpFloat -getImpulse(cpRotaryLimitJoint *joint) -{ - return cpfabs(joint->jAcc); -} - -static const cpConstraintClass klass = { - (cpConstraintPreStepImpl)preStep, - (cpConstraintApplyCachedImpulseImpl)applyCachedImpulse, - (cpConstraintApplyImpulseImpl)applyImpulse, - (cpConstraintGetImpulseImpl)getImpulse, -}; - -cpRotaryLimitJoint * -cpRotaryLimitJointAlloc(void) -{ - return (cpRotaryLimitJoint *)cpcalloc(1, sizeof(cpRotaryLimitJoint)); -} - -cpRotaryLimitJoint * -cpRotaryLimitJointInit(cpRotaryLimitJoint *joint, cpBody *a, cpBody *b, cpFloat min, cpFloat max) -{ - cpConstraintInit((cpConstraint *)joint, &klass, a, b); - - joint->min = min; - joint->max = max; - - joint->jAcc = 0.0f; - - return joint; -} - -cpConstraint * -cpRotaryLimitJointNew(cpBody *a, cpBody *b, cpFloat min, cpFloat max) -{ - return (cpConstraint *)cpRotaryLimitJointInit(cpRotaryLimitJointAlloc(), a, b, min, max); -} - -cpBool -cpConstraintIsRotaryLimitJoint(const cpConstraint *constraint) -{ - return (constraint->klass == &klass); -} - -cpFloat -cpRotaryLimitJointGetMin(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsRotaryLimitJoint(constraint), "Constraint is not a rotary limit joint."); - return ((cpRotaryLimitJoint *)constraint)->min; -} - -void -cpRotaryLimitJointSetMin(cpConstraint *constraint, cpFloat min) -{ - cpAssertHard(cpConstraintIsRotaryLimitJoint(constraint), "Constraint is not a rotary limit joint."); - cpConstraintActivateBodies(constraint); - ((cpRotaryLimitJoint *)constraint)->min = min; -} - -cpFloat -cpRotaryLimitJointGetMax(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsRotaryLimitJoint(constraint), "Constraint is not a rotary limit joint."); - return ((cpRotaryLimitJoint *)constraint)->max; -} - -void -cpRotaryLimitJointSetMax(cpConstraint *constraint, cpFloat max) -{ - cpAssertHard(cpConstraintIsRotaryLimitJoint(constraint), "Constraint is not a rotary limit joint."); - cpConstraintActivateBodies(constraint); - ((cpRotaryLimitJoint *)constraint)->max = max; -} diff --git a/3rdparty/chipmunk/src/cpShape.c b/3rdparty/chipmunk/src/cpShape.c deleted file mode 100644 index 513b5353ed48..000000000000 --- a/3rdparty/chipmunk/src/cpShape.c +++ /dev/null @@ -1,604 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk_private.h" -#include "chipmunk/chipmunk_unsafe.h" - -#define CP_DefineShapeGetter(struct, type, member, name) \ -CP_DeclareShapeGetter(struct, type, name){ \ - cpAssertHard(shape->klass == &struct##Class, "shape is not a "#struct); \ - return ((struct *)shape)->member; \ -} - -cpShape * -cpShapeInit(cpShape *shape, const cpShapeClass *klass, cpBody *body, struct cpShapeMassInfo massInfo) -{ - shape->klass = klass; - - shape->body = body; - shape->massInfo = massInfo; - - shape->sensor = 0; - - shape->e = 0.0f; - shape->u = 0.0f; - shape->surfaceV = cpvzero; - - shape->type = 0; - shape->filter.group = CP_NO_GROUP; - shape->filter.categories = CP_ALL_CATEGORIES; - shape->filter.mask = CP_ALL_CATEGORIES; - - shape->userData = NULL; - - shape->space = NULL; - - shape->next = NULL; - shape->prev = NULL; - - return shape; -} - -void -cpShapeDestroy(cpShape *shape) -{ - if(shape->klass && shape->klass->destroy) shape->klass->destroy(shape); -} - -void -cpShapeFree(cpShape *shape) -{ - if(shape){ - cpShapeDestroy(shape); - cpfree(shape); - } -} - -cpSpace * -cpShapeGetSpace(const cpShape *shape) -{ - return shape->space; -} - -cpBody * -cpShapeGetBody(const cpShape *shape) -{ - return shape->body; -} - -void -cpShapeSetBody(cpShape *shape, cpBody *body) -{ - cpAssertHard(!cpShapeActive(shape), "You cannot change the body on an active shape. You must remove the shape from the space before changing the body."); - shape->body = body; -} - -cpFloat cpShapeGetMass(cpShape *shape){ return shape->massInfo.m; } - -void -cpShapeSetMass(cpShape *shape, cpFloat mass){ - cpBody *body = shape->body; - cpBodyActivate(body); - - shape->massInfo.m = mass; - cpBodyAccumulateMassFromShapes(body); -} - -cpFloat cpShapeGetDensity(cpShape *shape){ return shape->massInfo.m/shape->massInfo.area; } -void cpShapeSetDensity(cpShape *shape, cpFloat density){ cpShapeSetMass(shape, density*shape->massInfo.area); } - -cpFloat cpShapeGetMoment(cpShape *shape){ return shape->massInfo.m*shape->massInfo.i; } -cpFloat cpShapeGetArea(cpShape *shape){ return shape->massInfo.area; } -cpVect cpShapeGetCenterOfGravity(cpShape *shape) { return shape->massInfo.cog; } - -cpBB -cpShapeGetBB(const cpShape *shape) -{ - return shape->bb; -} - -cpBool -cpShapeGetSensor(const cpShape *shape) -{ - return shape->sensor; -} - -void -cpShapeSetSensor(cpShape *shape, cpBool sensor) -{ - cpBodyActivate(shape->body); - shape->sensor = sensor; -} - -cpFloat -cpShapeGetElasticity(const cpShape *shape) -{ - return shape->e; -} - -void -cpShapeSetElasticity(cpShape *shape, cpFloat elasticity) -{ - cpAssertHard(elasticity >= 0.0f, "Elasticity must be positive."); - cpBodyActivate(shape->body); - shape->e = elasticity; -} - -cpFloat -cpShapeGetFriction(const cpShape *shape) -{ - return shape->u; -} - -void -cpShapeSetFriction(cpShape *shape, cpFloat friction) -{ - cpAssertHard(friction >= 0.0f, "Friction must be postive."); - cpBodyActivate(shape->body); - shape->u = friction; -} - -cpVect -cpShapeGetSurfaceVelocity(const cpShape *shape) -{ - return shape->surfaceV; -} - -void -cpShapeSetSurfaceVelocity(cpShape *shape, cpVect surfaceVelocity) -{ - cpBodyActivate(shape->body); - shape->surfaceV = surfaceVelocity; -} - -cpDataPointer -cpShapeGetUserData(const cpShape *shape) -{ - return shape->userData; -} - -void -cpShapeSetUserData(cpShape *shape, cpDataPointer userData) -{ - shape->userData = userData; -} - -cpCollisionType -cpShapeGetCollisionType(const cpShape *shape) -{ - return shape->type; -} - -void -cpShapeSetCollisionType(cpShape *shape, cpCollisionType collisionType) -{ - cpBodyActivate(shape->body); - shape->type = collisionType; -} - -cpShapeFilter -cpShapeGetFilter(const cpShape *shape) -{ - return shape->filter; -} - -void -cpShapeSetFilter(cpShape *shape, cpShapeFilter filter) -{ - cpBodyActivate(shape->body); - shape->filter = filter; -} - -cpBB -cpShapeCacheBB(cpShape *shape) -{ - return cpShapeUpdate(shape, shape->body->transform); -} - -cpBB -cpShapeUpdate(cpShape *shape, cpTransform transform) -{ - return (shape->bb = shape->klass->cacheData(shape, transform)); -} - -cpFloat -cpShapePointQuery(const cpShape *shape, cpVect p, cpPointQueryInfo *info) -{ - cpPointQueryInfo blank = {NULL, cpvzero, INFINITY, cpvzero}; - if(info){ - (*info) = blank; - } else { - info = ␣ - } - - shape->klass->pointQuery(shape, p, info); - return info->distance; -} - - -cpBool -cpShapeSegmentQuery(const cpShape *shape, cpVect a, cpVect b, cpFloat radius, cpSegmentQueryInfo *info){ - cpSegmentQueryInfo blank = {NULL, b, cpvzero, 1.0f}; - if(info){ - (*info) = blank; - } else { - info = ␣ - } - - cpPointQueryInfo nearest; - shape->klass->pointQuery(shape, a, &nearest); - if(nearest.distance <= radius){ - info->shape = shape; - info->alpha = 0.0; - info->normal = cpvnormalize(cpvsub(a, nearest.point)); - } else { - shape->klass->segmentQuery(shape, a, b, radius, info); - } - - return (info->shape != NULL); -} - -cpContactPointSet -cpShapesCollide(const cpShape *a, const cpShape *b) -{ - struct cpContact contacts[CP_MAX_CONTACTS_PER_ARBITER]; - struct cpCollisionInfo info = cpCollide(a, b, 0, contacts); - - cpContactPointSet set; - set.count = info.count; - - // cpCollideShapes() may have swapped the contact order. Flip the normal. - cpBool swapped = (a != info.a); - set.normal = (swapped ? cpvneg(info.n) : info.n); - - for(int i=0; itc = cpTransformPoint(transform, circle->c); - return cpBBNewForCircle(c, circle->r); -} - -static void -cpCircleShapePointQuery(cpCircleShape *circle, cpVect p, cpPointQueryInfo *info) -{ - cpVect delta = cpvsub(p, circle->tc); - cpFloat d = cpvlength(delta); - cpFloat r = circle->r; - - info->shape = (cpShape *)circle; - cpFloat r_over_d = d > 0.0f ? r/d : r; - info->point = cpvadd(circle->tc, cpvmult(delta, r_over_d)); // TODO: div/0 - info->distance = d - r; - - // Use up for the gradient if the distance is very small. - info->gradient = (d > MAGIC_EPSILON ? cpvmult(delta, 1.0f/d) : cpv(0.0f, 1.0f)); -} - -static void -cpCircleShapeSegmentQuery(cpCircleShape *circle, cpVect a, cpVect b, cpFloat radius, cpSegmentQueryInfo *info) -{ - CircleSegmentQuery((cpShape *)circle, circle->tc, circle->r, a, b, radius, info); -} - -static struct cpShapeMassInfo -cpCircleShapeMassInfo(cpFloat mass, cpFloat radius, cpVect center) -{ - struct cpShapeMassInfo info = { - mass, cpMomentForCircle(1.0f, 0.0f, radius, cpvzero), - center, - cpAreaForCircle(0.0f, radius), - }; - - return info; -} - -static const cpShapeClass cpCircleShapeClass = { - CP_CIRCLE_SHAPE, - (cpShapeCacheDataImpl)cpCircleShapeCacheData, - NULL, - (cpShapePointQueryImpl)cpCircleShapePointQuery, - (cpShapeSegmentQueryImpl)cpCircleShapeSegmentQuery, -}; - -cpCircleShape * -cpCircleShapeInit(cpCircleShape *circle, cpBody *body, cpFloat radius, cpVect offset) -{ - circle->c = offset; - circle->r = radius; - - cpShapeInit((cpShape *)circle, &cpCircleShapeClass, body, cpCircleShapeMassInfo(0.0f, radius, offset)); - - return circle; -} - -cpShape * -cpCircleShapeNew(cpBody *body, cpFloat radius, cpVect offset) -{ - return (cpShape *)cpCircleShapeInit(cpCircleShapeAlloc(), body, radius, offset); -} - -cpVect -cpCircleShapeGetOffset(const cpShape *shape) -{ - cpAssertHard(shape->klass == &cpCircleShapeClass, "Shape is not a circle shape."); - return ((cpCircleShape *)shape)->c; -} - -cpFloat -cpCircleShapeGetRadius(const cpShape *shape) -{ - cpAssertHard(shape->klass == &cpCircleShapeClass, "Shape is not a circle shape."); - return ((cpCircleShape *)shape)->r; -} - - -cpSegmentShape * -cpSegmentShapeAlloc(void) -{ - return (cpSegmentShape *)cpcalloc(1, sizeof(cpSegmentShape)); -} - -static cpBB -cpSegmentShapeCacheData(cpSegmentShape *seg, cpTransform transform) -{ - seg->ta = cpTransformPoint(transform, seg->a); - seg->tb = cpTransformPoint(transform, seg->b); - seg->tn = cpTransformVect(transform, seg->n); - - cpFloat l,r,b,t; - - if(seg->ta.x < seg->tb.x){ - l = seg->ta.x; - r = seg->tb.x; - } else { - l = seg->tb.x; - r = seg->ta.x; - } - - if(seg->ta.y < seg->tb.y){ - b = seg->ta.y; - t = seg->tb.y; - } else { - b = seg->tb.y; - t = seg->ta.y; - } - - cpFloat rad = seg->r; - return cpBBNew(l - rad, b - rad, r + rad, t + rad); -} - -static void -cpSegmentShapePointQuery(cpSegmentShape *seg, cpVect p, cpPointQueryInfo *info) -{ - cpVect closest = cpClosetPointOnSegment(p, seg->ta, seg->tb); - - cpVect delta = cpvsub(p, closest); - cpFloat d = cpvlength(delta); - cpFloat r = seg->r; - cpVect g = cpvmult(delta, 1.0f/d); - - info->shape = (cpShape *)seg; - info->point = (d ? cpvadd(closest, cpvmult(g, r)) : closest); - info->distance = d - r; - - // Use the segment's normal if the distance is very small. - info->gradient = (d > MAGIC_EPSILON ? g : seg->n); -} - -static void -cpSegmentShapeSegmentQuery(cpSegmentShape *seg, cpVect a, cpVect b, cpFloat r2, cpSegmentQueryInfo *info) -{ - cpVect n = seg->tn; - cpFloat d = cpvdot(cpvsub(seg->ta, a), n); - cpFloat r = seg->r + r2; - - cpVect flipped_n = (d > 0.0f ? cpvneg(n) : n); - cpVect seg_offset = cpvsub(cpvmult(flipped_n, r), a); - - // Make the endpoints relative to 'a' and move them by the thickness of the segment. - cpVect seg_a = cpvadd(seg->ta, seg_offset); - cpVect seg_b = cpvadd(seg->tb, seg_offset); - cpVect delta = cpvsub(b, a); - - if(cpvcross(delta, seg_a)*cpvcross(delta, seg_b) <= 0.0f){ - cpFloat d_offset = d + (d > 0.0f ? -r : r); - cpFloat ad = -d_offset; - cpFloat bd = cpvdot(delta, n) - d_offset; - - if(ad*bd < 0.0f){ - cpFloat t = ad/(ad - bd); - - info->shape = (cpShape *)seg; - info->point = cpvsub(cpvlerp(a, b, t), cpvmult(flipped_n, r2)); - info->normal = flipped_n; - info->alpha = t; - } - } else if(r != 0.0f){ - cpSegmentQueryInfo info1 = {NULL, b, cpvzero, 1.0f}; - cpSegmentQueryInfo info2 = {NULL, b, cpvzero, 1.0f}; - CircleSegmentQuery((cpShape *)seg, seg->ta, seg->r, a, b, r2, &info1); - CircleSegmentQuery((cpShape *)seg, seg->tb, seg->r, a, b, r2, &info2); - - if(info1.alpha < info2.alpha){ - (*info) = info1; - } else { - (*info) = info2; - } - } -} - -static struct cpShapeMassInfo -cpSegmentShapeMassInfo(cpFloat mass, cpVect a, cpVect b, cpFloat r) -{ - struct cpShapeMassInfo info = { - mass, cpMomentForBox(1.0f, cpvdist(a, b) + 2.0f*r, 2.0f*r), // TODO is an approximation. - cpvlerp(a, b, 0.5f), - cpAreaForSegment(a, b, r), - }; - - return info; -} - -static const cpShapeClass cpSegmentShapeClass = { - CP_SEGMENT_SHAPE, - (cpShapeCacheDataImpl)cpSegmentShapeCacheData, - NULL, - (cpShapePointQueryImpl)cpSegmentShapePointQuery, - (cpShapeSegmentQueryImpl)cpSegmentShapeSegmentQuery, -}; - -cpSegmentShape * -cpSegmentShapeInit(cpSegmentShape *seg, cpBody *body, cpVect a, cpVect b, cpFloat r) -{ - seg->a = a; - seg->b = b; - seg->n = cpvrperp(cpvnormalize(cpvsub(b, a))); - - seg->r = r; - - seg->a_tangent = cpvzero; - seg->b_tangent = cpvzero; - - cpShapeInit((cpShape *)seg, &cpSegmentShapeClass, body, cpSegmentShapeMassInfo(0.0f, a, b, r)); - - return seg; -} - -cpShape* -cpSegmentShapeNew(cpBody *body, cpVect a, cpVect b, cpFloat r) -{ - return (cpShape *)cpSegmentShapeInit(cpSegmentShapeAlloc(), body, a, b, r); -} - -cpVect -cpSegmentShapeGetA(const cpShape *shape) -{ - cpAssertHard(shape->klass == &cpSegmentShapeClass, "Shape is not a segment shape."); - return ((cpSegmentShape *)shape)->a; -} - -cpVect -cpSegmentShapeGetB(const cpShape *shape) -{ - cpAssertHard(shape->klass == &cpSegmentShapeClass, "Shape is not a segment shape."); - return ((cpSegmentShape *)shape)->b; -} - -cpVect -cpSegmentShapeGetNormal(const cpShape *shape) -{ - cpAssertHard(shape->klass == &cpSegmentShapeClass, "Shape is not a segment shape."); - return ((cpSegmentShape *)shape)->n; -} - -cpFloat -cpSegmentShapeGetRadius(const cpShape *shape) -{ - cpAssertHard(shape->klass == &cpSegmentShapeClass, "Shape is not a segment shape."); - return ((cpSegmentShape *)shape)->r; -} - -void -cpSegmentShapeSetNeighbors(cpShape *shape, cpVect prev, cpVect next) -{ - cpAssertHard(shape->klass == &cpSegmentShapeClass, "Shape is not a segment shape."); - cpSegmentShape *seg = (cpSegmentShape *)shape; - - seg->a_tangent = cpvsub(prev, seg->a); - seg->b_tangent = cpvsub(next, seg->b); -} - -// Unsafe API (chipmunk_unsafe.h) - -// TODO setters should wake the shape up? - -void -cpCircleShapeSetRadius(cpShape *shape, cpFloat radius) -{ - cpAssertHard(shape->klass == &cpCircleShapeClass, "Shape is not a circle shape."); - cpCircleShape *circle = (cpCircleShape *)shape; - - circle->r = radius; - - cpFloat mass = shape->massInfo.m; - shape->massInfo = cpCircleShapeMassInfo(mass, circle->r, circle->c); - if(mass > 0.0f) cpBodyAccumulateMassFromShapes(shape->body); -} - -void -cpCircleShapeSetOffset(cpShape *shape, cpVect offset) -{ - cpAssertHard(shape->klass == &cpCircleShapeClass, "Shape is not a circle shape."); - cpCircleShape *circle = (cpCircleShape *)shape; - - circle->c = offset; - - cpFloat mass = shape->massInfo.m; - shape->massInfo = cpCircleShapeMassInfo(shape->massInfo.m, circle->r, circle->c); - if(mass > 0.0f) cpBodyAccumulateMassFromShapes(shape->body); -} - -void -cpSegmentShapeSetEndpoints(cpShape *shape, cpVect a, cpVect b) -{ - cpAssertHard(shape->klass == &cpSegmentShapeClass, "Shape is not a segment shape."); - cpSegmentShape *seg = (cpSegmentShape *)shape; - - seg->a = a; - seg->b = b; - seg->n = cpvperp(cpvnormalize(cpvsub(b, a))); - - cpFloat mass = shape->massInfo.m; - shape->massInfo = cpSegmentShapeMassInfo(shape->massInfo.m, seg->a, seg->b, seg->r); - if(mass > 0.0f) cpBodyAccumulateMassFromShapes(shape->body); -} - -void -cpSegmentShapeSetRadius(cpShape *shape, cpFloat radius) -{ - cpAssertHard(shape->klass == &cpSegmentShapeClass, "Shape is not a segment shape."); - cpSegmentShape *seg = (cpSegmentShape *)shape; - - seg->r = radius; - - cpFloat mass = shape->massInfo.m; - shape->massInfo = cpSegmentShapeMassInfo(shape->massInfo.m, seg->a, seg->b, seg->r); - if(mass > 0.0f) cpBodyAccumulateMassFromShapes(shape->body); -} diff --git a/3rdparty/chipmunk/src/cpSimpleMotor.c b/3rdparty/chipmunk/src/cpSimpleMotor.c deleted file mode 100644 index 2bea74a525da..000000000000 --- a/3rdparty/chipmunk/src/cpSimpleMotor.c +++ /dev/null @@ -1,123 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk_private.h" - -static void -preStep(cpSimpleMotor *joint, cpFloat dt) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - // calculate moment of inertia coefficient. - joint->iSum = 1.0f/(a->i_inv + b->i_inv); -} - -static void -applyCachedImpulse(cpSimpleMotor *joint, cpFloat dt_coef) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - cpFloat j = joint->jAcc*dt_coef; - a->w -= j*a->i_inv; - b->w += j*b->i_inv; -} - -static void -applyImpulse(cpSimpleMotor *joint, cpFloat dt) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - // compute relative rotational velocity - cpFloat wr = b->w - a->w + joint->rate; - - cpFloat jMax = joint->constraint.maxForce*dt; - - // compute normal impulse - cpFloat j = -wr*joint->iSum; - cpFloat jOld = joint->jAcc; - joint->jAcc = cpfclamp(jOld + j, -jMax, jMax); - j = joint->jAcc - jOld; - - // apply impulse - a->w -= j*a->i_inv; - b->w += j*b->i_inv; -} - -static cpFloat -getImpulse(cpSimpleMotor *joint) -{ - return cpfabs(joint->jAcc); -} - -static const cpConstraintClass klass = { - (cpConstraintPreStepImpl)preStep, - (cpConstraintApplyCachedImpulseImpl)applyCachedImpulse, - (cpConstraintApplyImpulseImpl)applyImpulse, - (cpConstraintGetImpulseImpl)getImpulse, -}; - -cpSimpleMotor * -cpSimpleMotorAlloc(void) -{ - return (cpSimpleMotor *)cpcalloc(1, sizeof(cpSimpleMotor)); -} - -cpSimpleMotor * -cpSimpleMotorInit(cpSimpleMotor *joint, cpBody *a, cpBody *b, cpFloat rate) -{ - cpConstraintInit((cpConstraint *)joint, &klass, a, b); - - joint->rate = rate; - - joint->jAcc = 0.0f; - - return joint; -} - -cpConstraint * -cpSimpleMotorNew(cpBody *a, cpBody *b, cpFloat rate) -{ - return (cpConstraint *)cpSimpleMotorInit(cpSimpleMotorAlloc(), a, b, rate); -} - -cpBool -cpConstraintIsSimpleMotor(const cpConstraint *constraint) -{ - return (constraint->klass == &klass); -} - -cpFloat -cpSimpleMotorGetRate(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsSimpleMotor(constraint), "Constraint is not a SimpleMotor."); - return ((cpSimpleMotor *)constraint)->rate; -} - -void -cpSimpleMotorSetRate(cpConstraint *constraint, cpFloat rate) -{ - cpAssertHard(cpConstraintIsSimpleMotor(constraint), "Constraint is not a SimpleMotor."); - cpConstraintActivateBodies(constraint); - ((cpSimpleMotor *)constraint)->rate = rate; -} diff --git a/3rdparty/chipmunk/src/cpSlideJoint.c b/3rdparty/chipmunk/src/cpSlideJoint.c deleted file mode 100644 index 61afe33e4b1a..000000000000 --- a/3rdparty/chipmunk/src/cpSlideJoint.c +++ /dev/null @@ -1,195 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk_private.h" - -static void -preStep(cpSlideJoint *joint, cpFloat dt) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - joint->r1 = cpTransformVect(a->transform, cpvsub(joint->anchorA, a->cog)); - joint->r2 = cpTransformVect(b->transform, cpvsub(joint->anchorB, b->cog)); - - cpVect delta = cpvsub(cpvadd(b->p, joint->r2), cpvadd(a->p, joint->r1)); - cpFloat dist = cpvlength(delta); - cpFloat pdist = 0.0f; - if(dist > joint->max) { - pdist = dist - joint->max; - joint->n = cpvnormalize(delta); - } else if(dist < joint->min) { - pdist = joint->min - dist; - joint->n = cpvneg(cpvnormalize(delta)); - } else { - joint->n = cpvzero; - joint->jnAcc = 0.0f; - } - - // calculate mass normal - joint->nMass = 1.0f/k_scalar(a, b, joint->r1, joint->r2, joint->n); - - // calculate bias velocity - cpFloat maxBias = joint->constraint.maxBias; - joint->bias = cpfclamp(-bias_coef(joint->constraint.errorBias, dt)*pdist/dt, -maxBias, maxBias); -} - -static void -applyCachedImpulse(cpSlideJoint *joint, cpFloat dt_coef) -{ - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - cpVect j = cpvmult(joint->n, joint->jnAcc*dt_coef); - apply_impulses(a, b, joint->r1, joint->r2, j); -} - -static void -applyImpulse(cpSlideJoint *joint, cpFloat dt) -{ - if(cpveql(joint->n, cpvzero)) return; // early exit - - cpBody *a = joint->constraint.a; - cpBody *b = joint->constraint.b; - - cpVect n = joint->n; - cpVect r1 = joint->r1; - cpVect r2 = joint->r2; - - // compute relative velocity - cpVect vr = relative_velocity(a, b, r1, r2); - cpFloat vrn = cpvdot(vr, n); - - // compute normal impulse - cpFloat jn = (joint->bias - vrn)*joint->nMass; - cpFloat jnOld = joint->jnAcc; - joint->jnAcc = cpfclamp(jnOld + jn, -joint->constraint.maxForce*dt, 0.0f); - jn = joint->jnAcc - jnOld; - - // apply impulse - apply_impulses(a, b, joint->r1, joint->r2, cpvmult(n, jn)); -} - -static cpFloat -getImpulse(cpConstraint *joint) -{ - return cpfabs(((cpSlideJoint *)joint)->jnAcc); -} - -static const cpConstraintClass klass = { - (cpConstraintPreStepImpl)preStep, - (cpConstraintApplyCachedImpulseImpl)applyCachedImpulse, - (cpConstraintApplyImpulseImpl)applyImpulse, - (cpConstraintGetImpulseImpl)getImpulse, -}; - -cpSlideJoint * -cpSlideJointAlloc(void) -{ - return (cpSlideJoint *)cpcalloc(1, sizeof(cpSlideJoint)); -} - -cpSlideJoint * -cpSlideJointInit(cpSlideJoint *joint, cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat min, cpFloat max) -{ - cpConstraintInit((cpConstraint *)joint, &klass, a, b); - - joint->anchorA = anchorA; - joint->anchorB = anchorB; - joint->min = min; - joint->max = max; - - joint->jnAcc = 0.0f; - - return joint; -} - -cpConstraint * -cpSlideJointNew(cpBody *a, cpBody *b, cpVect anchorA, cpVect anchorB, cpFloat min, cpFloat max) -{ - return (cpConstraint *)cpSlideJointInit(cpSlideJointAlloc(), a, b, anchorA, anchorB, min, max); -} - -cpBool -cpConstraintIsSlideJoint(const cpConstraint *constraint) -{ - return (constraint->klass == &klass); -} - -cpVect -cpSlideJointGetAnchorA(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsSlideJoint(constraint), "Constraint is not a slide joint."); - return ((cpSlideJoint *)constraint)->anchorA; -} - -void -cpSlideJointSetAnchorA(cpConstraint *constraint, cpVect anchorA) -{ - cpAssertHard(cpConstraintIsSlideJoint(constraint), "Constraint is not a slide joint."); - cpConstraintActivateBodies(constraint); - ((cpSlideJoint *)constraint)->anchorA = anchorA; -} - -cpVect -cpSlideJointGetAnchorB(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsSlideJoint(constraint), "Constraint is not a slide joint."); - return ((cpSlideJoint *)constraint)->anchorB; -} - -void -cpSlideJointSetAnchorB(cpConstraint *constraint, cpVect anchorB) -{ - cpAssertHard(cpConstraintIsSlideJoint(constraint), "Constraint is not a slide joint."); - cpConstraintActivateBodies(constraint); - ((cpSlideJoint *)constraint)->anchorB = anchorB; -} - -cpFloat -cpSlideJointGetMin(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsSlideJoint(constraint), "Constraint is not a slide joint."); - return ((cpSlideJoint *)constraint)->min; -} - -void -cpSlideJointSetMin(cpConstraint *constraint, cpFloat min) -{ - cpAssertHard(cpConstraintIsSlideJoint(constraint), "Constraint is not a slide joint."); - cpConstraintActivateBodies(constraint); - ((cpSlideJoint *)constraint)->min = min; -} - -cpFloat -cpSlideJointGetMax(const cpConstraint *constraint) -{ - cpAssertHard(cpConstraintIsSlideJoint(constraint), "Constraint is not a slide joint."); - return ((cpSlideJoint *)constraint)->max; -} - -void -cpSlideJointSetMax(cpConstraint *constraint, cpFloat max) -{ - cpAssertHard(cpConstraintIsSlideJoint(constraint), "Constraint is not a slide joint."); - cpConstraintActivateBodies(constraint); - ((cpSlideJoint *)constraint)->max = max; -} diff --git a/3rdparty/chipmunk/src/cpSpace.c b/3rdparty/chipmunk/src/cpSpace.c deleted file mode 100644 index b319d3a4e831..000000000000 --- a/3rdparty/chipmunk/src/cpSpace.c +++ /dev/null @@ -1,701 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include - -#include "chipmunk/chipmunk_private.h" - -//MARK: Contact Set Helpers - -// Equal function for arbiterSet. -static cpBool -arbiterSetEql(cpShape **shapes, cpArbiter *arb) -{ - cpShape *a = shapes[0]; - cpShape *b = shapes[1]; - - return ((a == arb->a && b == arb->b) || (b == arb->a && a == arb->b)); -} - -//MARK: Collision Handler Set HelperFunctions - -// Equals function for collisionHandlers. -static cpBool -handlerSetEql(cpCollisionHandler *check, cpCollisionHandler *pair) -{ - return ( - (check->typeA == pair->typeA && check->typeB == pair->typeB) || - (check->typeB == pair->typeA && check->typeA == pair->typeB) - ); -} - -// Transformation function for collisionHandlers. -static void * -handlerSetTrans(cpCollisionHandler *handler, void *unused) -{ - cpCollisionHandler *copy = (cpCollisionHandler *)cpcalloc(1, sizeof(cpCollisionHandler)); - memcpy(copy, handler, sizeof(cpCollisionHandler)); - - return copy; -} - -//MARK: Misc Helper Funcs - -// Default collision functions. - -static cpBool -DefaultBegin(cpArbiter *arb, cpSpace *space, cpDataPointer data){ - cpBool retA = cpArbiterCallWildcardBeginA(arb, space); - cpBool retB = cpArbiterCallWildcardBeginB(arb, space); - return retA && retB; -} - -static cpBool -DefaultPreSolve(cpArbiter *arb, cpSpace *space, cpDataPointer data){ - cpBool retA = cpArbiterCallWildcardPreSolveA(arb, space); - cpBool retB = cpArbiterCallWildcardPreSolveB(arb, space); - return retA && retB; -} - -static void -DefaultPostSolve(cpArbiter *arb, cpSpace *space, cpDataPointer data){ - cpArbiterCallWildcardPostSolveA(arb, space); - cpArbiterCallWildcardPostSolveB(arb, space); -} - -static void -DefaultSeparate(cpArbiter *arb, cpSpace *space, cpDataPointer data){ - cpArbiterCallWildcardSeparateA(arb, space); - cpArbiterCallWildcardSeparateB(arb, space); -} - -// Use the wildcard identifier since the default handler should never match any type pair. -static cpCollisionHandler cpCollisionHandlerDefault = { - CP_WILDCARD_COLLISION_TYPE, CP_WILDCARD_COLLISION_TYPE, - DefaultBegin, DefaultPreSolve, DefaultPostSolve, DefaultSeparate, NULL -}; - -static cpBool AlwaysCollide(cpArbiter *arb, cpSpace *space, cpDataPointer data){return cpTrue;} -static void DoNothing(cpArbiter *arb, cpSpace *space, cpDataPointer data){} - -cpCollisionHandler cpCollisionHandlerDoNothing = { - CP_WILDCARD_COLLISION_TYPE, CP_WILDCARD_COLLISION_TYPE, - AlwaysCollide, AlwaysCollide, DoNothing, DoNothing, NULL -}; - -// function to get the estimated velocity of a shape for the cpBBTree. -static cpVect ShapeVelocityFunc(cpShape *shape){return shape->body->v;} - -// Used for disposing of collision handlers. -static void FreeWrap(void *ptr, void *unused){cpfree(ptr);} - -//MARK: Memory Management Functions - -cpSpace * -cpSpaceAlloc(void) -{ - return (cpSpace *)cpcalloc(1, sizeof(cpSpace)); -} - -cpSpace* -cpSpaceInit(cpSpace *space) -{ -#ifndef NDEBUG - static cpBool done = cpFalse; - if(!done){ - printf("Initializing cpSpace - Chipmunk v%s (Debug Enabled)\n", cpVersionString); - printf("Compile with -DNDEBUG defined to disable debug mode and runtime assertion checks\n"); - done = cpTrue; - } -#endif - - space->iterations = 10; - - space->gravity = cpvzero; - space->damping = 1.0f; - - space->collisionSlop = 0.1f; - space->collisionBias = cpfpow(1.0f - 0.1f, 60.0f); - space->collisionPersistence = 3; - - space->locked = 0; - space->stamp = 0; - - space->shapeIDCounter = 0; - space->staticShapes = cpBBTreeNew((cpSpatialIndexBBFunc)cpShapeGetBB, NULL); - space->dynamicShapes = cpBBTreeNew((cpSpatialIndexBBFunc)cpShapeGetBB, space->staticShapes); - cpBBTreeSetVelocityFunc(space->dynamicShapes, (cpBBTreeVelocityFunc)ShapeVelocityFunc); - - space->allocatedBuffers = cpArrayNew(0); - - space->dynamicBodies = cpArrayNew(0); - space->staticBodies = cpArrayNew(0); - space->sleepingComponents = cpArrayNew(0); - space->rousedBodies = cpArrayNew(0); - - space->sleepTimeThreshold = INFINITY; - space->idleSpeedThreshold = 0.0f; - - space->arbiters = cpArrayNew(0); - space->pooledArbiters = cpArrayNew(0); - - space->contactBuffersHead = NULL; - space->cachedArbiters = cpHashSetNew(0, (cpHashSetEqlFunc)arbiterSetEql); - - space->constraints = cpArrayNew(0); - - space->usesWildcards = cpFalse; - memcpy(&space->defaultHandler, &cpCollisionHandlerDoNothing, sizeof(cpCollisionHandler)); - space->collisionHandlers = cpHashSetNew(0, (cpHashSetEqlFunc)handlerSetEql); - - space->postStepCallbacks = cpArrayNew(0); - space->skipPostStep = cpFalse; - - cpBody *staticBody = cpBodyInit(&space->_staticBody, 0.0f, 0.0f); - cpBodySetType(staticBody, CP_BODY_TYPE_STATIC); - cpSpaceSetStaticBody(space, staticBody); - - return space; -} - -cpSpace* -cpSpaceNew(void) -{ - return cpSpaceInit(cpSpaceAlloc()); -} - -static void cpBodyActivateWrap(cpBody *body, void *unused){cpBodyActivate(body);} - -void -cpSpaceDestroy(cpSpace *space) -{ - cpSpaceEachBody(space, (cpSpaceBodyIteratorFunc)cpBodyActivateWrap, NULL); - - cpSpatialIndexFree(space->staticShapes); - cpSpatialIndexFree(space->dynamicShapes); - - cpArrayFree(space->dynamicBodies); - cpArrayFree(space->staticBodies); - cpArrayFree(space->sleepingComponents); - cpArrayFree(space->rousedBodies); - - cpArrayFree(space->constraints); - - cpHashSetFree(space->cachedArbiters); - - cpArrayFree(space->arbiters); - cpArrayFree(space->pooledArbiters); - - if(space->allocatedBuffers){ - cpArrayFreeEach(space->allocatedBuffers, cpfree); - cpArrayFree(space->allocatedBuffers); - } - - if(space->postStepCallbacks){ - cpArrayFreeEach(space->postStepCallbacks, cpfree); - cpArrayFree(space->postStepCallbacks); - } - - if(space->collisionHandlers) cpHashSetEach(space->collisionHandlers, FreeWrap, NULL); - cpHashSetFree(space->collisionHandlers); -} - -void -cpSpaceFree(cpSpace *space) -{ - if(space){ - cpSpaceDestroy(space); - cpfree(space); - } -} - - -//MARK: Basic properties: - -int -cpSpaceGetIterations(const cpSpace *space) -{ - return space->iterations; -} - -void -cpSpaceSetIterations(cpSpace *space, int iterations) -{ - cpAssertHard(iterations > 0, "Iterations must be positive and non-zero."); - space->iterations = iterations; -} - -cpVect -cpSpaceGetGravity(const cpSpace *space) -{ - return space->gravity; -} - -void -cpSpaceSetGravity(cpSpace *space, cpVect gravity) -{ - space->gravity = gravity; - - // Wake up all of the bodies since the gravity changed. - cpArray *components = space->sleepingComponents; - for(int i=0; inum; i++){ - cpBodyActivate((cpBody *)components->arr[i]); - } -} - -cpFloat -cpSpaceGetDamping(const cpSpace *space) -{ - return space->damping; -} - -void -cpSpaceSetDamping(cpSpace *space, cpFloat damping) -{ - cpAssertHard(damping >= 0.0, "Damping must be positive."); - space->damping = damping; -} - -cpFloat -cpSpaceGetIdleSpeedThreshold(const cpSpace *space) -{ - return space->idleSpeedThreshold; -} - -void -cpSpaceSetIdleSpeedThreshold(cpSpace *space, cpFloat idleSpeedThreshold) -{ - space->idleSpeedThreshold = idleSpeedThreshold; -} - -cpFloat -cpSpaceGetSleepTimeThreshold(const cpSpace *space) -{ - return space->sleepTimeThreshold; -} - -void -cpSpaceSetSleepTimeThreshold(cpSpace *space, cpFloat sleepTimeThreshold) -{ - space->sleepTimeThreshold = sleepTimeThreshold; -} - -cpFloat -cpSpaceGetCollisionSlop(const cpSpace *space) -{ - return space->collisionSlop; -} - -void -cpSpaceSetCollisionSlop(cpSpace *space, cpFloat collisionSlop) -{ - space->collisionSlop = collisionSlop; -} - -cpFloat -cpSpaceGetCollisionBias(const cpSpace *space) -{ - return space->collisionBias; -} - -void -cpSpaceSetCollisionBias(cpSpace *space, cpFloat collisionBias) -{ - space->collisionBias = collisionBias; -} - -cpTimestamp -cpSpaceGetCollisionPersistence(const cpSpace *space) -{ - return space->collisionPersistence; -} - -void -cpSpaceSetCollisionPersistence(cpSpace *space, cpTimestamp collisionPersistence) -{ - space->collisionPersistence = collisionPersistence; -} - -cpDataPointer -cpSpaceGetUserData(const cpSpace *space) -{ - return space->userData; -} - -void -cpSpaceSetUserData(cpSpace *space, cpDataPointer userData) -{ - space->userData = userData; -} - -cpBody * -cpSpaceGetStaticBody(const cpSpace *space) -{ - return space->staticBody; -} - -cpFloat -cpSpaceGetCurrentTimeStep(const cpSpace *space) -{ - return space->curr_dt; -} - -void -cpSpaceSetStaticBody(cpSpace *space, cpBody *body) -{ - if(space->staticBody != NULL){ - cpAssertHard(space->staticBody->shapeList == NULL, "Internal Error: Changing the designated static body while the old one still had shapes attached."); - space->staticBody->space = NULL; - } - - space->staticBody = body; - body->space = space; -} - -cpBool -cpSpaceIsLocked(cpSpace *space) -{ - return (space->locked > 0); -} - -//MARK: Collision Handler Function Management - -static void -cpSpaceUseWildcardDefaultHandler(cpSpace *space) -{ - // Spaces default to using the slightly faster "do nothing" default handler until wildcards are potentially needed. - if(!space->usesWildcards){ - space->usesWildcards = cpTrue; - memcpy(&space->defaultHandler, &cpCollisionHandlerDefault, sizeof(cpCollisionHandler)); - } -} - -cpCollisionHandler *cpSpaceAddDefaultCollisionHandler(cpSpace *space) -{ - cpSpaceUseWildcardDefaultHandler(space); - return &space->defaultHandler; -} - -cpCollisionHandler *cpSpaceAddCollisionHandler(cpSpace *space, cpCollisionType a, cpCollisionType b) -{ - cpHashValue hash = CP_HASH_PAIR(a, b); - cpCollisionHandler handler = {a, b, DefaultBegin, DefaultPreSolve, DefaultPostSolve, DefaultSeparate, NULL}; - return (cpCollisionHandler*)cpHashSetInsert(space->collisionHandlers, hash, &handler, (cpHashSetTransFunc)handlerSetTrans, NULL); -} - -cpCollisionHandler * -cpSpaceAddWildcardHandler(cpSpace *space, cpCollisionType type) -{ - cpSpaceUseWildcardDefaultHandler(space); - - cpHashValue hash = CP_HASH_PAIR(type, CP_WILDCARD_COLLISION_TYPE); - cpCollisionHandler handler = {type, CP_WILDCARD_COLLISION_TYPE, AlwaysCollide, AlwaysCollide, DoNothing, DoNothing, NULL}; - return (cpCollisionHandler*)cpHashSetInsert(space->collisionHandlers, hash, &handler, (cpHashSetTransFunc)handlerSetTrans, NULL); -} - - -//MARK: Body, Shape, and Joint Management -cpShape * -cpSpaceAddShape(cpSpace *space, cpShape *shape) -{ - cpAssertHard(shape->space != space, "You have already added this shape to this space. You must not add it a second time."); - cpAssertHard(!shape->space, "You have already added this shape to another space. You cannot add it to a second."); - cpAssertHard(shape->body, "The shape's body is not defined."); - cpAssertHard(shape->body->space == space, "The shape's body must be added to the space before the shape."); - cpAssertSpaceUnlocked(space); - - cpBody *body = shape->body; - - cpBool isStatic = (cpBodyGetType(body) == CP_BODY_TYPE_STATIC); - if(!isStatic) cpBodyActivate(body); - cpBodyAddShape(body, shape); - - shape->hashid = space->shapeIDCounter++; - cpShapeUpdate(shape, body->transform); - cpSpatialIndexInsert(isStatic ? space->staticShapes : space->dynamicShapes, shape, shape->hashid); - shape->space = space; - - return shape; -} - -cpBody * -cpSpaceAddBody(cpSpace *space, cpBody *body) -{ - cpAssertHard(body->space != space, "You have already added this body to this space. You must not add it a second time."); - cpAssertHard(!body->space, "You have already added this body to another space. You cannot add it to a second."); - cpAssertSpaceUnlocked(space); - - cpArrayPush(cpSpaceArrayForBodyType(space, cpBodyGetType(body)), body); - body->space = space; - - return body; -} - -cpConstraint * -cpSpaceAddConstraint(cpSpace *space, cpConstraint *constraint) -{ - cpAssertHard(constraint->space != space, "You have already added this constraint to this space. You must not add it a second time."); - cpAssertHard(!constraint->space, "You have already added this constraint to another space. You cannot add it to a second."); - cpAssertSpaceUnlocked(space); - - cpBody *a = constraint->a, *b = constraint->b; - cpAssertHard(a != NULL && b != NULL, "Constraint is attached to a NULL body."); -// cpAssertHard(a->space == space && b->space == space, "The constraint's bodies must be added to the space before the constraint."); - - cpBodyActivate(a); - cpBodyActivate(b); - cpArrayPush(space->constraints, constraint); - - // Push onto the heads of the bodies' constraint lists - constraint->next_a = a->constraintList; a->constraintList = constraint; - constraint->next_b = b->constraintList; b->constraintList = constraint; - constraint->space = space; - - return constraint; -} - -struct arbiterFilterContext { - cpSpace *space; - cpBody *body; - cpShape *shape; -}; - -static cpBool -cachedArbitersFilter(cpArbiter *arb, struct arbiterFilterContext *context) -{ - cpShape *shape = context->shape; - cpBody *body = context->body; - - - // Match on the filter shape, or if it's NULL the filter body - if( - (body == arb->body_a && (shape == arb->a || shape == NULL)) || - (body == arb->body_b && (shape == arb->b || shape == NULL)) - ){ - // Call separate when removing shapes. - if(shape && arb->state != CP_ARBITER_STATE_CACHED){ - // Invalidate the arbiter since one of the shapes was removed. - arb->state = CP_ARBITER_STATE_INVALIDATED; - - cpCollisionHandler *handler = arb->handler; - handler->separateFunc(arb, context->space, handler->userData); - } - - cpArbiterUnthread(arb); - cpArrayDeleteObj(context->space->arbiters, arb); - cpArrayPush(context->space->pooledArbiters, arb); - - return cpFalse; - } - - return cpTrue; -} - -void -cpSpaceFilterArbiters(cpSpace *space, cpBody *body, cpShape *filter) -{ - cpSpaceLock(space); { - struct arbiterFilterContext context = {space, body, filter}; - cpHashSetFilter(space->cachedArbiters, (cpHashSetFilterFunc)cachedArbitersFilter, &context); - } cpSpaceUnlock(space, cpTrue); -} - -void -cpSpaceRemoveShape(cpSpace *space, cpShape *shape) -{ - cpBody *body = shape->body; - cpAssertHard(cpSpaceContainsShape(space, shape), "Cannot remove a shape that was not added to the space. (Removed twice maybe?)"); - cpAssertSpaceUnlocked(space); - - cpBool isStatic = (cpBodyGetType(body) == CP_BODY_TYPE_STATIC); - if(isStatic){ - cpBodyActivateStatic(body, shape); - } else { - cpBodyActivate(body); - } - - cpBodyRemoveShape(body, shape); - cpSpaceFilterArbiters(space, body, shape); - cpSpatialIndexRemove(isStatic ? space->staticShapes : space->dynamicShapes, shape, shape->hashid); - shape->space = NULL; - shape->hashid = 0; -} - -void -cpSpaceRemoveBody(cpSpace *space, cpBody *body) -{ - cpAssertHard(body != cpSpaceGetStaticBody(space), "Cannot remove the designated static body for the space."); - cpAssertHard(cpSpaceContainsBody(space, body), "Cannot remove a body that was not added to the space. (Removed twice maybe?)"); -// cpAssertHard(body->shapeList == NULL, "Cannot remove a body from the space before removing the bodies attached to it."); -// cpAssertHard(body->constraintList == NULL, "Cannot remove a body from the space before removing the constraints attached to it."); - cpAssertSpaceUnlocked(space); - - cpBodyActivate(body); -// cpSpaceFilterArbiters(space, body, NULL); - cpArrayDeleteObj(cpSpaceArrayForBodyType(space, cpBodyGetType(body)), body); - body->space = NULL; -} - -void -cpSpaceRemoveConstraint(cpSpace *space, cpConstraint *constraint) -{ - cpAssertHard(cpSpaceContainsConstraint(space, constraint), "Cannot remove a constraint that was not added to the space. (Removed twice maybe?)"); - cpAssertSpaceUnlocked(space); - - cpBodyActivate(constraint->a); - cpBodyActivate(constraint->b); - cpArrayDeleteObj(space->constraints, constraint); - - cpBodyRemoveConstraint(constraint->a, constraint); - cpBodyRemoveConstraint(constraint->b, constraint); - constraint->space = NULL; -} - -cpBool cpSpaceContainsShape(cpSpace *space, cpShape *shape) -{ - return (shape->space == space); -} - -cpBool cpSpaceContainsBody(cpSpace *space, cpBody *body) -{ - return (body->space == space); -} - -cpBool cpSpaceContainsConstraint(cpSpace *space, cpConstraint *constraint) -{ - return (constraint->space == space); -} - -//MARK: Iteration - -void -cpSpaceEachBody(cpSpace *space, cpSpaceBodyIteratorFunc func, void *data) -{ - cpSpaceLock(space); { - cpArray *bodies = space->dynamicBodies; - for(int i=0; inum; i++){ - func((cpBody *)bodies->arr[i], data); - } - - cpArray *otherBodies = space->staticBodies; - for(int i=0; inum; i++){ - func((cpBody *)otherBodies->arr[i], data); - } - - cpArray *components = space->sleepingComponents; - for(int i=0; inum; i++){ - cpBody *root = (cpBody *)components->arr[i]; - - cpBody *body = root; - while(body){ - cpBody *next = body->sleeping.next; - func(body, data); - body = next; - } - } - } cpSpaceUnlock(space, cpTrue); -} - -typedef struct spaceShapeContext { - cpSpaceShapeIteratorFunc func; - void *data; -} spaceShapeContext; - -static void -spaceEachShapeIterator(cpShape *shape, spaceShapeContext *context) -{ - context->func(shape, context->data); -} - -void -cpSpaceEachShape(cpSpace *space, cpSpaceShapeIteratorFunc func, void *data) -{ - cpSpaceLock(space); { - spaceShapeContext context = {func, data}; - cpSpatialIndexEach(space->dynamicShapes, (cpSpatialIndexIteratorFunc)spaceEachShapeIterator, &context); - cpSpatialIndexEach(space->staticShapes, (cpSpatialIndexIteratorFunc)spaceEachShapeIterator, &context); - } cpSpaceUnlock(space, cpTrue); -} - -void -cpSpaceEachConstraint(cpSpace *space, cpSpaceConstraintIteratorFunc func, void *data) -{ - cpSpaceLock(space); { - cpArray *constraints = space->constraints; - - for(int i=0; inum; i++){ - func((cpConstraint *)constraints->arr[i], data); - } - } cpSpaceUnlock(space, cpTrue); -} - -//MARK: Spatial Index Management - -void -cpSpaceReindexStatic(cpSpace *space) -{ - cpAssertHard(!space->locked, "You cannot manually reindex objects while the space is locked. Wait until the current query or step is complete."); - - cpSpatialIndexEach(space->staticShapes, (cpSpatialIndexIteratorFunc)&cpShapeUpdateFunc, NULL); - cpSpatialIndexReindex(space->staticShapes); -} - -void -cpSpaceReindexShape(cpSpace *space, cpShape *shape) -{ - cpAssertHard(!space->locked, "You cannot manually reindex objects while the space is locked. Wait until the current query or step is complete."); - - cpShapeCacheBB(shape); - - // attempt to rehash the shape in both hashes - cpSpatialIndexReindexObject(space->dynamicShapes, shape, shape->hashid); - cpSpatialIndexReindexObject(space->staticShapes, shape, shape->hashid); -} - -void -cpSpaceReindexShapesForBody(cpSpace *space, cpBody *body) -{ - CP_BODY_FOREACH_SHAPE(body, shape) cpSpaceReindexShape(space, shape); -} - - -static void -copyShapes(cpShape *shape, cpSpatialIndex *index) -{ - cpSpatialIndexInsert(index, shape, shape->hashid); -} - -void -cpSpaceUseSpatialHash(cpSpace *space, cpFloat dim, int count) -{ - cpSpatialIndex *staticShapes = cpSpaceHashNew(dim, count, (cpSpatialIndexBBFunc)cpShapeGetBB, NULL); - cpSpatialIndex *dynamicShapes = cpSpaceHashNew(dim, count, (cpSpatialIndexBBFunc)cpShapeGetBB, staticShapes); - - cpSpatialIndexEach(space->staticShapes, (cpSpatialIndexIteratorFunc)copyShapes, staticShapes); - cpSpatialIndexEach(space->dynamicShapes, (cpSpatialIndexIteratorFunc)copyShapes, dynamicShapes); - - cpSpatialIndexFree(space->staticShapes); - cpSpatialIndexFree(space->dynamicShapes); - - space->staticShapes = staticShapes; - space->dynamicShapes = dynamicShapes; -} diff --git a/3rdparty/chipmunk/src/cpSpaceComponent.c b/3rdparty/chipmunk/src/cpSpaceComponent.c deleted file mode 100644 index 7b2d60699111..000000000000 --- a/3rdparty/chipmunk/src/cpSpaceComponent.c +++ /dev/null @@ -1,349 +0,0 @@ -/* Copyright (c) 2007 Scott Lembcke - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include - -#include "chipmunk/chipmunk_private.h" - -//MARK: Sleeping Functions - -void -cpSpaceActivateBody(cpSpace *space, cpBody *body) -{ - cpAssertHard(cpBodyGetType(body) == CP_BODY_TYPE_DYNAMIC, "Internal error: Attempting to activate a non-dynamic body."); - - if(space->locked){ - // cpSpaceActivateBody() is called again once the space is unlocked - if(!cpArrayContains(space->rousedBodies, body)) cpArrayPush(space->rousedBodies, body); - } else { - cpAssertSoft(body->sleeping.root == NULL && body->sleeping.next == NULL, "Internal error: Activating body non-NULL node pointers."); - cpArrayPush(space->dynamicBodies, body); - - CP_BODY_FOREACH_SHAPE(body, shape){ - cpSpatialIndexRemove(space->staticShapes, shape, shape->hashid); - cpSpatialIndexInsert(space->dynamicShapes, shape, shape->hashid); - } - - CP_BODY_FOREACH_ARBITER(body, arb){ - cpBody *bodyA = arb->body_a; - - // Arbiters are shared between two bodies that are always woken up together. - // You only want to restore the arbiter once, so bodyA is arbitrarily chosen to own the arbiter. - // The edge case is when static bodies are involved as the static bodies never actually sleep. - // If the static body is bodyB then all is good. If the static body is bodyA, that can easily be checked. - if(body == bodyA || cpBodyGetType(bodyA) == CP_BODY_TYPE_STATIC){ - int numContacts = arb->count; - struct cpContact *contacts = arb->contacts; - - // Restore contact values back to the space's contact buffer memory - arb->contacts = cpContactBufferGetArray(space); - memcpy(arb->contacts, contacts, numContacts*sizeof(struct cpContact)); - cpSpacePushContacts(space, numContacts); - - // Reinsert the arbiter into the arbiter cache - const cpShape *a = arb->a, *b = arb->b; - const cpShape *shape_pair[] = {a, b}; - cpHashValue arbHashID = CP_HASH_PAIR((cpHashValue)a, (cpHashValue)b); - cpHashSetInsert(space->cachedArbiters, arbHashID, shape_pair, NULL, arb); - - // Update the arbiter's state - arb->stamp = space->stamp; - cpArrayPush(space->arbiters, arb); - - cpfree(contacts); - } - } - - CP_BODY_FOREACH_CONSTRAINT(body, constraint){ - cpBody *bodyA = constraint->a; - if(body == bodyA || cpBodyGetType(bodyA) == CP_BODY_TYPE_STATIC) cpArrayPush(space->constraints, constraint); - } - } -} - -static void -cpSpaceDeactivateBody(cpSpace *space, cpBody *body) -{ - cpAssertHard(cpBodyGetType(body) == CP_BODY_TYPE_DYNAMIC, "Internal error: Attempting to deactivate a non-dynamic body."); - - cpArrayDeleteObj(space->dynamicBodies, body); - - CP_BODY_FOREACH_SHAPE(body, shape){ - cpSpatialIndexRemove(space->dynamicShapes, shape, shape->hashid); - cpSpatialIndexInsert(space->staticShapes, shape, shape->hashid); - } - - CP_BODY_FOREACH_ARBITER(body, arb){ - cpBody *bodyA = arb->body_a; - if(body == bodyA || cpBodyGetType(bodyA) == CP_BODY_TYPE_STATIC){ - cpSpaceUncacheArbiter(space, arb); - - // Save contact values to a new block of memory so they won't time out - size_t bytes = arb->count*sizeof(struct cpContact); - struct cpContact *contacts = (struct cpContact *)cpcalloc(1, bytes); - memcpy(contacts, arb->contacts, bytes); - arb->contacts = contacts; - } - } - - CP_BODY_FOREACH_CONSTRAINT(body, constraint){ - cpBody *bodyA = constraint->a; - if(body == bodyA || cpBodyGetType(bodyA) == CP_BODY_TYPE_STATIC) cpArrayDeleteObj(space->constraints, constraint); - } -} - -static inline cpBody * -ComponentRoot(cpBody *body) -{ - return (body ? body->sleeping.root : NULL); -} - -void -cpBodyActivate(cpBody *body) -{ - if(body != NULL && cpBodyGetType(body) == CP_BODY_TYPE_DYNAMIC){ - body->sleeping.idleTime = 0.0f; - - cpBody *root = ComponentRoot(body); - if(root && cpBodyIsSleeping(root)){ - // TODO should cpBodyIsSleeping(root) be an assertion? - cpAssertSoft(cpBodyGetType(root) == CP_BODY_TYPE_DYNAMIC, "Internal Error: Non-dynamic body component root detected."); - - cpSpace *space = root->space; - cpBody *body = root; - while(body){ - cpBody *next = body->sleeping.next; - - body->sleeping.idleTime = 0.0f; - body->sleeping.root = NULL; - body->sleeping.next = NULL; - cpSpaceActivateBody(space, body); - - body = next; - } - - cpArrayDeleteObj(space->sleepingComponents, root); - } - - CP_BODY_FOREACH_ARBITER(body, arb){ - // Reset the idle timer of things the body is touching as well. - // That way things don't get left hanging in the air. - cpBody *other = (arb->body_a == body ? arb->body_b : arb->body_a); - if(cpBodyGetType(other) != CP_BODY_TYPE_STATIC) other->sleeping.idleTime = 0.0f; - } - } -} - -void -cpBodyActivateStatic(cpBody *body, cpShape *filter) -{ - cpAssertHard(cpBodyGetType(body) == CP_BODY_TYPE_STATIC, "cpBodyActivateStatic() called on a non-static body."); - - CP_BODY_FOREACH_ARBITER(body, arb){ - if(!filter || filter == arb->a || filter == arb->b){ - cpBodyActivate(arb->body_a == body ? arb->body_b : arb->body_a); - } - } - - // TODO: should also activate joints? -} - -static inline void -cpBodyPushArbiter(cpBody *body, cpArbiter *arb) -{ - cpAssertSoft(cpArbiterThreadForBody(arb, body)->next == NULL, "Internal Error: Dangling contact graph pointers detected. (A)"); - cpAssertSoft(cpArbiterThreadForBody(arb, body)->prev == NULL, "Internal Error: Dangling contact graph pointers detected. (B)"); - - cpArbiter *next = body->arbiterList; - cpAssertSoft(next == NULL || cpArbiterThreadForBody(next, body)->prev == NULL, "Internal Error: Dangling contact graph pointers detected. (C)"); - cpArbiterThreadForBody(arb, body)->next = next; - - if(next) cpArbiterThreadForBody(next, body)->prev = arb; - body->arbiterList = arb; -} - -static inline void -ComponentAdd(cpBody *root, cpBody *body){ - body->sleeping.root = root; - - if(body != root){ - body->sleeping.next = root->sleeping.next; - root->sleeping.next = body; - } -} - -static inline void -FloodFillComponent(cpBody *root, cpBody *body) -{ - // Kinematic bodies cannot be put to sleep and prevent bodies they are touching from sleeping. - // Static bodies are effectively sleeping all the time. - if(cpBodyGetType(body) == CP_BODY_TYPE_DYNAMIC){ - cpBody *other_root = ComponentRoot(body); - if(other_root == NULL){ - ComponentAdd(root, body); - CP_BODY_FOREACH_ARBITER(body, arb) FloodFillComponent(root, (body == arb->body_a ? arb->body_b : arb->body_a)); - CP_BODY_FOREACH_CONSTRAINT(body, constraint) FloodFillComponent(root, (body == constraint->a ? constraint->b : constraint->a)); - } else { - cpAssertSoft(other_root == root, "Internal Error: Inconsistency dectected in the contact graph."); - } - } -} - -static inline cpBool -ComponentActive(cpBody *root, cpFloat threshold) -{ - CP_BODY_FOREACH_COMPONENT(root, body){ - if(body->sleeping.idleTime < threshold) return cpTrue; - } - - return cpFalse; -} - -void -cpSpaceProcessComponents(cpSpace *space, cpFloat dt) -{ - cpBool sleep = (space->sleepTimeThreshold != INFINITY); - cpArray *bodies = space->dynamicBodies; - -#ifndef NDEBUG - for(int i=0; inum; i++){ - cpBody *body = (cpBody*)bodies->arr[i]; - - cpAssertSoft(body->sleeping.next == NULL, "Internal Error: Dangling next pointer detected in contact graph."); - cpAssertSoft(body->sleeping.root == NULL, "Internal Error: Dangling root pointer detected in contact graph."); - } -#endif - - // Calculate the kinetic energy of all the bodies. - if(sleep){ - cpFloat dv = space->idleSpeedThreshold; - cpFloat dvsq = (dv ? dv*dv : cpvlengthsq(space->gravity)*dt*dt); - - // update idling and reset component nodes - for(int i=0; inum; i++){ - cpBody *body = (cpBody*)bodies->arr[i]; - - // TODO should make a separate array for kinematic bodies. - if(cpBodyGetType(body) != CP_BODY_TYPE_DYNAMIC) continue; - - // Need to deal with infinite mass objects - cpFloat keThreshold = (dvsq ? body->m*dvsq : 0.0f); - body->sleeping.idleTime = (cpBodyKineticEnergy(body) > keThreshold ? 0.0f : body->sleeping.idleTime + dt); - } - } - - // Awaken any sleeping bodies found and then push arbiters to the bodies' lists. - cpArray *arbiters = space->arbiters; - for(int i=0, count=arbiters->num; iarr[i]; - cpBody *a = arb->body_a, *b = arb->body_b; - - if(sleep){ - // TODO checking cpBodyIsSleepin() redundant? - if(cpBodyGetType(b) == CP_BODY_TYPE_KINEMATIC || cpBodyIsSleeping(a)) cpBodyActivate(a); - if(cpBodyGetType(a) == CP_BODY_TYPE_KINEMATIC || cpBodyIsSleeping(b)) cpBodyActivate(b); - } - - cpBodyPushArbiter(a, arb); - cpBodyPushArbiter(b, arb); - } - - if(sleep){ - // Bodies should be held active if connected by a joint to a kinematic. - cpArray *constraints = space->constraints; - for(int i=0; inum; i++){ - cpConstraint *constraint = (cpConstraint *)constraints->arr[i]; - cpBody *a = constraint->a, *b = constraint->b; - - if(cpBodyGetType(b) == CP_BODY_TYPE_KINEMATIC) cpBodyActivate(a); - if(cpBodyGetType(a) == CP_BODY_TYPE_KINEMATIC) cpBodyActivate(b); - } - - // Generate components and deactivate sleeping ones - for(int i=0; inum;){ - cpBody *body = (cpBody*)bodies->arr[i]; - - if(ComponentRoot(body) == NULL){ - // Body not in a component yet. Perform a DFS to flood fill mark - // the component in the contact graph using this body as the root. - FloodFillComponent(body, body); - - // Check if the component should be put to sleep. - if(!ComponentActive(body, space->sleepTimeThreshold)){ - cpArrayPush(space->sleepingComponents, body); - CP_BODY_FOREACH_COMPONENT(body, other) cpSpaceDeactivateBody(space, other); - - // cpSpaceDeactivateBody() removed the current body from the list. - // Skip incrementing the index counter. - continue; - } - } - - i++; - - // Only sleeping bodies retain their component node pointers. - body->sleeping.root = NULL; - body->sleeping.next = NULL; - } - } -} - -void -cpBodySleep(cpBody *body) -{ - cpBodySleepWithGroup(body, NULL); -} - -void -cpBodySleepWithGroup(cpBody *body, cpBody *group){ - cpAssertHard(cpBodyGetType(body) == CP_BODY_TYPE_DYNAMIC, "Non-dynamic bodies cannot be put to sleep."); - - cpSpace *space = body->space; - cpAssertHard(!cpSpaceIsLocked(space), "Bodies cannot be put to sleep during a query or a call to cpSpaceStep(). Put these calls into a post-step callback."); - cpAssertHard(cpSpaceGetSleepTimeThreshold(space) < INFINITY, "Sleeping is not enabled on the space. You cannot sleep a body without setting a sleep time threshold on the space."); - cpAssertHard(group == NULL || cpBodyIsSleeping(group), "Cannot use a non-sleeping body as a group identifier."); - - if(cpBodyIsSleeping(body)){ - cpAssertHard(ComponentRoot(body) == ComponentRoot(group), "The body is already sleeping and it's group cannot be reassigned."); - return; - } - - CP_BODY_FOREACH_SHAPE(body, shape) cpShapeCacheBB(shape); - cpSpaceDeactivateBody(space, body); - - if(group){ - cpBody *root = ComponentRoot(group); - - body->sleeping.root = root; - body->sleeping.next = root->sleeping.next; - body->sleeping.idleTime = 0.0f; - - root->sleeping.next = body; - } else { - body->sleeping.root = body; - body->sleeping.next = NULL; - body->sleeping.idleTime = 0.0f; - - cpArrayPush(space->sleepingComponents, body); - } - - cpArrayDeleteObj(space->dynamicBodies, body); -} diff --git a/3rdparty/chipmunk/src/cpSpaceDebug.c b/3rdparty/chipmunk/src/cpSpaceDebug.c deleted file mode 100644 index 6b80894b0f18..000000000000 --- a/3rdparty/chipmunk/src/cpSpaceDebug.c +++ /dev/null @@ -1,187 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk_private.h" - -#ifndef CP_SPACE_DISABLE_DEBUG_API - -static void -cpSpaceDebugDrawShape(cpShape *shape, cpSpaceDebugDrawOptions *options) -{ - cpBody *body = shape->body; - cpDataPointer data = options->data; - - cpSpaceDebugColor outline_color = options->shapeOutlineColor; - cpSpaceDebugColor fill_color = options->colorForShape(shape, data); - - switch(shape->klass->type){ - case CP_CIRCLE_SHAPE: { - cpCircleShape *circle = (cpCircleShape *)shape; - options->drawCircle(circle->tc, body->a, circle->r, outline_color, fill_color, data); - break; - } - case CP_SEGMENT_SHAPE: { - cpSegmentShape *seg = (cpSegmentShape *)shape; - options->drawFatSegment(seg->ta, seg->tb, seg->r, outline_color, fill_color, data); - break; - } - case CP_POLY_SHAPE: { - cpPolyShape *poly = (cpPolyShape *)shape; - - int count = poly->count; - struct cpSplittingPlane *planes = poly->planes; - cpVect *verts = (cpVect *)alloca(count*sizeof(cpVect)); - - for(int i=0; idrawPolygon(count, verts, poly->r, outline_color, fill_color, data); - break; - } - default: break; - } -} - -static const cpVect spring_verts[] = { - {0.00f, 0.0f}, - {0.20f, 0.0f}, - {0.25f, 3.0f}, - {0.30f,-6.0f}, - {0.35f, 6.0f}, - {0.40f,-6.0f}, - {0.45f, 6.0f}, - {0.50f,-6.0f}, - {0.55f, 6.0f}, - {0.60f,-6.0f}, - {0.65f, 6.0f}, - {0.70f,-3.0f}, - {0.75f, 6.0f}, - {0.80f, 0.0f}, - {1.00f, 0.0f}, -}; -static const int spring_count = sizeof(spring_verts)/sizeof(cpVect); - -static void -cpSpaceDebugDrawConstraint(cpConstraint *constraint, cpSpaceDebugDrawOptions *options) -{ - cpDataPointer data = options->data; - cpSpaceDebugColor color = options->constraintColor; - - cpBody *body_a = constraint->a; - cpBody *body_b = constraint->b; - - if(cpConstraintIsPinJoint(constraint)){ - cpPinJoint *joint = (cpPinJoint *)constraint; - - cpVect a = cpTransformPoint(body_a->transform, joint->anchorA); - cpVect b = cpTransformPoint(body_b->transform, joint->anchorB); - - options->drawDot(5, a, color, data); - options->drawDot(5, b, color, data); - options->drawSegment(a, b, color, data); - } else if(cpConstraintIsSlideJoint(constraint)){ - cpSlideJoint *joint = (cpSlideJoint *)constraint; - - cpVect a = cpTransformPoint(body_a->transform, joint->anchorA); - cpVect b = cpTransformPoint(body_b->transform, joint->anchorB); - - options->drawDot(5, a, color, data); - options->drawDot(5, b, color, data); - options->drawSegment(a, b, color, data); - } else if(cpConstraintIsPivotJoint(constraint)){ - cpPivotJoint *joint = (cpPivotJoint *)constraint; - - cpVect a = cpTransformPoint(body_a->transform, joint->anchorA); - cpVect b = cpTransformPoint(body_b->transform, joint->anchorB); - - options->drawDot(5, a, color, data); - options->drawDot(5, b, color, data); - } else if(cpConstraintIsGrooveJoint(constraint)){ - cpGrooveJoint *joint = (cpGrooveJoint *)constraint; - - cpVect a = cpTransformPoint(body_a->transform, joint->grv_a); - cpVect b = cpTransformPoint(body_a->transform, joint->grv_b); - cpVect c = cpTransformPoint(body_b->transform, joint->anchorB); - - options->drawDot(5, c, color, data); - options->drawSegment(a, b, color, data); - } else if(cpConstraintIsDampedSpring(constraint)){ - cpDampedSpring *spring = (cpDampedSpring *)constraint; - - cpVect a = cpTransformPoint(body_a->transform, spring->anchorA); - cpVect b = cpTransformPoint(body_b->transform, spring->anchorB); - - options->drawDot(5, a, color, data); - options->drawDot(5, b, color, data); - - cpVect delta = cpvsub(b, a); - cpFloat cos = delta.x; - cpFloat sin = delta.y; - cpFloat s = 1.0f/cpvlength(delta); - - cpVect r1 = cpv(cos, -sin*s); - cpVect r2 = cpv(sin, cos*s); - - cpVect *verts = (cpVect *)alloca(spring_count*sizeof(cpVect)); - for(int i=0; idrawSegment(verts[i], verts[i + 1], color, data); - } - } -} - -void -cpSpaceDebugDraw(cpSpace *space, cpSpaceDebugDrawOptions *options) -{ - if(options->flags & CP_SPACE_DEBUG_DRAW_SHAPES){ - cpSpaceEachShape(space, (cpSpaceShapeIteratorFunc)cpSpaceDebugDrawShape, options); - } - - if(options->flags & CP_SPACE_DEBUG_DRAW_CONSTRAINTS){ - cpSpaceEachConstraint(space, (cpSpaceConstraintIteratorFunc)cpSpaceDebugDrawConstraint, options); - } - - if(options->flags & CP_SPACE_DEBUG_DRAW_COLLISION_POINTS){ - cpArray *arbiters = space->arbiters; - cpSpaceDebugColor color = options->collisionPointColor; - cpSpaceDebugDrawSegmentImpl draw_seg = options->drawSegment; - cpDataPointer data = options->data; - - for(int i=0; inum; i++){ - cpArbiter *arb = (cpArbiter*)arbiters->arr[i]; - cpVect n = arb->n; - - for(int j=0; jcount; j++){ - cpVect p1 = cpvadd(arb->body_a->p, arb->contacts[j].r1); - cpVect p2 = cpvadd(arb->body_b->p, arb->contacts[j].r2); - - cpFloat d = 2.0f; - cpVect a = cpvadd(p1, cpvmult(n, -d)); - cpVect b = cpvadd(p2, cpvmult(n, d)); - draw_seg(a, b, color, data); - } - } - } -} - -#endif diff --git a/3rdparty/chipmunk/src/cpSpaceHash.c b/3rdparty/chipmunk/src/cpSpaceHash.c deleted file mode 100644 index 556c8d38dc2c..000000000000 --- a/3rdparty/chipmunk/src/cpSpaceHash.c +++ /dev/null @@ -1,634 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk_private.h" -#include "prime.h" - -typedef struct cpSpaceHashBin cpSpaceHashBin; -typedef struct cpHandle cpHandle; - -struct cpSpaceHash { - cpSpatialIndex spatialIndex; - - int numcells; - cpFloat celldim; - - cpSpaceHashBin **table; - cpHashSet *handleSet; - - cpSpaceHashBin *pooledBins; - cpArray *pooledHandles; - cpArray *allocatedBuffers; - - cpTimestamp stamp; -}; - - -//MARK: Handle Functions - -struct cpHandle { - void *obj; - int retain; - cpTimestamp stamp; -}; - -static cpHandle* -cpHandleInit(cpHandle *hand, void *obj) -{ - hand->obj = obj; - hand->retain = 0; - hand->stamp = 0; - - return hand; -} - -static inline void cpHandleRetain(cpHandle *hand){hand->retain++;} - -static inline void -cpHandleRelease(cpHandle *hand, cpArray *pooledHandles) -{ - hand->retain--; - if(hand->retain == 0) cpArrayPush(pooledHandles, hand); -} - -static int handleSetEql(void *obj, cpHandle *hand){return (obj == hand->obj);} - -static void * -handleSetTrans(void *obj, cpSpaceHash *hash) -{ - if(hash->pooledHandles->num == 0){ - // handle pool is exhausted, make more - int count = CP_BUFFER_BYTES/sizeof(cpHandle); - cpAssertHard(count, "Internal Error: Buffer size is too small."); - - cpHandle *buffer = (cpHandle *)cpcalloc(1, CP_BUFFER_BYTES); - cpArrayPush(hash->allocatedBuffers, buffer); - - for(int i=0; ipooledHandles, buffer + i); - } - - cpHandle *hand = cpHandleInit((cpHandle *)cpArrayPop(hash->pooledHandles), obj); - cpHandleRetain(hand); - - return hand; -} - -//MARK: Bin Functions - -struct cpSpaceHashBin { - cpHandle *handle; - cpSpaceHashBin *next; -}; - -static inline void -recycleBin(cpSpaceHash *hash, cpSpaceHashBin *bin) -{ - bin->next = hash->pooledBins; - hash->pooledBins = bin; -} - -static inline void -clearTableCell(cpSpaceHash *hash, int idx) -{ - cpSpaceHashBin *bin = hash->table[idx]; - while(bin){ - cpSpaceHashBin *next = bin->next; - - cpHandleRelease(bin->handle, hash->pooledHandles); - recycleBin(hash, bin); - - bin = next; - } - - hash->table[idx] = NULL; -} - -static void -clearTable(cpSpaceHash *hash) -{ - for(int i=0; inumcells; i++) clearTableCell(hash, i); -} - -// Get a recycled or new bin. -static inline cpSpaceHashBin * -getEmptyBin(cpSpaceHash *hash) -{ - cpSpaceHashBin *bin = hash->pooledBins; - - if(bin){ - hash->pooledBins = bin->next; - return bin; - } else { - // Pool is exhausted, make more - int count = CP_BUFFER_BYTES/sizeof(cpSpaceHashBin); - cpAssertHard(count, "Internal Error: Buffer size is too small."); - - cpSpaceHashBin *buffer = (cpSpaceHashBin *)cpcalloc(1, CP_BUFFER_BYTES); - cpArrayPush(hash->allocatedBuffers, buffer); - - // push all but the first one, return the first instead - for(int i=1; itable); - - hash->numcells = numcells; - hash->table = (cpSpaceHashBin **)cpcalloc(numcells, sizeof(cpSpaceHashBin *)); -} - -static inline cpSpatialIndexClass *Klass(void); - -cpSpatialIndex * -cpSpaceHashInit(cpSpaceHash *hash, cpFloat celldim, int numcells, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex) -{ - cpSpatialIndexInit((cpSpatialIndex *)hash, Klass(), bbfunc, staticIndex); - - cpSpaceHashAllocTable(hash, next_prime(numcells)); - hash->celldim = celldim; - - hash->handleSet = cpHashSetNew(0, (cpHashSetEqlFunc)handleSetEql); - - hash->pooledHandles = cpArrayNew(0); - - hash->pooledBins = NULL; - hash->allocatedBuffers = cpArrayNew(0); - - hash->stamp = 1; - - return (cpSpatialIndex *)hash; -} - -cpSpatialIndex * -cpSpaceHashNew(cpFloat celldim, int cells, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex) -{ - return cpSpaceHashInit(cpSpaceHashAlloc(), celldim, cells, bbfunc, staticIndex); -} - -static void -cpSpaceHashDestroy(cpSpaceHash *hash) -{ - if(hash->table) clearTable(hash); - cpfree(hash->table); - - cpHashSetFree(hash->handleSet); - - cpArrayFreeEach(hash->allocatedBuffers, cpfree); - cpArrayFree(hash->allocatedBuffers); - cpArrayFree(hash->pooledHandles); -} - -//MARK: Helper Functions - -static inline cpBool -containsHandle(cpSpaceHashBin *bin, cpHandle *hand) -{ - while(bin){ - if(bin->handle == hand) return cpTrue; - bin = bin->next; - } - - return cpFalse; -} - -// The hash function itself. -static inline cpHashValue -hash_func(cpHashValue x, cpHashValue y, cpHashValue n) -{ - return (x*1640531513ul ^ y*2654435789ul) % n; -} - -// Much faster than (int)floor(f) -// Profiling showed floor() to be a sizable performance hog -static inline int -floor_int(cpFloat f) -{ - int i = (int)f; - return (f < 0.0f && f != i ? i - 1 : i); -} - -static inline void -hashHandle(cpSpaceHash *hash, cpHandle *hand, cpBB bb) -{ - // Find the dimensions in cell coordinates. - cpFloat dim = hash->celldim; - int l = floor_int(bb.l/dim); // Fix by ShiftZ - int r = floor_int(bb.r/dim); - int b = floor_int(bb.b/dim); - int t = floor_int(bb.t/dim); - - int n = hash->numcells; - for(int i=l; i<=r; i++){ - for(int j=b; j<=t; j++){ - cpHashValue idx = hash_func(i,j,n); - cpSpaceHashBin *bin = hash->table[idx]; - - // Don't add an object twice to the same cell. - if(containsHandle(bin, hand)) continue; - - cpHandleRetain(hand); - // Insert a new bin for the handle in this cell. - cpSpaceHashBin *newBin = getEmptyBin(hash); - newBin->handle = hand; - newBin->next = bin; - hash->table[idx] = newBin; - } - } -} - -//MARK: Basic Operations - -static void -cpSpaceHashInsert(cpSpaceHash *hash, void *obj, cpHashValue hashid) -{ - cpHandle *hand = (cpHandle *)cpHashSetInsert(hash->handleSet, hashid, obj, (cpHashSetTransFunc)handleSetTrans, hash); - hashHandle(hash, hand, hash->spatialIndex.bbfunc(obj)); -} - -static void -cpSpaceHashRehashObject(cpSpaceHash *hash, void *obj, cpHashValue hashid) -{ - cpHandle *hand = (cpHandle *)cpHashSetRemove(hash->handleSet, hashid, obj); - - if(hand){ - hand->obj = NULL; - cpHandleRelease(hand, hash->pooledHandles); - - cpSpaceHashInsert(hash, obj, hashid); - } -} - -static void -rehash_helper(cpHandle *hand, cpSpaceHash *hash) -{ - hashHandle(hash, hand, hash->spatialIndex.bbfunc(hand->obj)); -} - -static void -cpSpaceHashRehash(cpSpaceHash *hash) -{ - clearTable(hash); - cpHashSetEach(hash->handleSet, (cpHashSetIteratorFunc)rehash_helper, hash); -} - -static void -cpSpaceHashRemove(cpSpaceHash *hash, void *obj, cpHashValue hashid) -{ - cpHandle *hand = (cpHandle *)cpHashSetRemove(hash->handleSet, hashid, obj); - - if(hand){ - hand->obj = NULL; - cpHandleRelease(hand, hash->pooledHandles); - } -} - -typedef struct eachContext { - cpSpatialIndexIteratorFunc func; - void *data; -} eachContext; - -static void eachHelper(cpHandle *hand, eachContext *context){context->func(hand->obj, context->data);} - -static void -cpSpaceHashEach(cpSpaceHash *hash, cpSpatialIndexIteratorFunc func, void *data) -{ - eachContext context = {func, data}; - cpHashSetEach(hash->handleSet, (cpHashSetIteratorFunc)eachHelper, &context); -} - -static void -remove_orphaned_handles(cpSpaceHash *hash, cpSpaceHashBin **bin_ptr) -{ - cpSpaceHashBin *bin = *bin_ptr; - while(bin){ - cpHandle *hand = bin->handle; - cpSpaceHashBin *next = bin->next; - - if(!hand->obj){ - // orphaned handle, unlink and recycle the bin - (*bin_ptr) = bin->next; - recycleBin(hash, bin); - - cpHandleRelease(hand, hash->pooledHandles); - } else { - bin_ptr = &bin->next; - } - - bin = next; - } -} - -//MARK: Query Functions - -static inline void -query_helper(cpSpaceHash *hash, cpSpaceHashBin **bin_ptr, void *obj, cpSpatialIndexQueryFunc func, void *data) -{ - restart: - for(cpSpaceHashBin *bin = *bin_ptr; bin; bin = bin->next){ - cpHandle *hand = bin->handle; - void *other = hand->obj; - - if(hand->stamp == hash->stamp || obj == other){ - continue; - } else if(other){ - func(obj, other, 0, data); - hand->stamp = hash->stamp; - } else { - // The object for this handle has been removed - // cleanup this cell and restart the query - remove_orphaned_handles(hash, bin_ptr); - goto restart; // GCC not smart enough/able to tail call an inlined function. - } - } -} - -static void -cpSpaceHashQuery(cpSpaceHash *hash, void *obj, cpBB bb, cpSpatialIndexQueryFunc func, void *data) -{ - // Get the dimensions in cell coordinates. - cpFloat dim = hash->celldim; - int l = floor_int(bb.l/dim); // Fix by ShiftZ - int r = floor_int(bb.r/dim); - int b = floor_int(bb.b/dim); - int t = floor_int(bb.t/dim); - - int n = hash->numcells; - cpSpaceHashBin **table = hash->table; - - // Iterate over the cells and query them. - for(int i=l; i<=r; i++){ - for(int j=b; j<=t; j++){ - query_helper(hash, &table[hash_func(i,j,n)], obj, func, data); - } - } - - hash->stamp++; -} - -// Similar to struct eachPair above. -typedef struct queryRehashContext { - cpSpaceHash *hash; - cpSpatialIndexQueryFunc func; - void *data; -} queryRehashContext; - -// Hashset iterator func used with cpSpaceHashQueryRehash(). -static void -queryRehash_helper(cpHandle *hand, queryRehashContext *context) -{ - cpSpaceHash *hash = context->hash; - cpSpatialIndexQueryFunc func = context->func; - void *data = context->data; - - cpFloat dim = hash->celldim; - int n = hash->numcells; - - void *obj = hand->obj; - cpBB bb = hash->spatialIndex.bbfunc(obj); - - int l = floor_int(bb.l/dim); - int r = floor_int(bb.r/dim); - int b = floor_int(bb.b/dim); - int t = floor_int(bb.t/dim); - - cpSpaceHashBin **table = hash->table; - - for(int i=l; i<=r; i++){ - for(int j=b; j<=t; j++){ - cpHashValue idx = hash_func(i,j,n); - cpSpaceHashBin *bin = table[idx]; - - if(containsHandle(bin, hand)) continue; - - cpHandleRetain(hand); // this MUST be done first in case the object is removed in func() - query_helper(hash, &bin, obj, func, data); - - cpSpaceHashBin *newBin = getEmptyBin(hash); - newBin->handle = hand; - newBin->next = bin; - table[idx] = newBin; - } - } - - // Increment the stamp for each object hashed. - hash->stamp++; -} - -static void -cpSpaceHashReindexQuery(cpSpaceHash *hash, cpSpatialIndexQueryFunc func, void *data) -{ - clearTable(hash); - - queryRehashContext context = {hash, func, data}; - cpHashSetEach(hash->handleSet, (cpHashSetIteratorFunc)queryRehash_helper, &context); - - cpSpatialIndexCollideStatic((cpSpatialIndex *)hash, hash->spatialIndex.staticIndex, func, data); -} - -static inline cpFloat -segmentQuery_helper(cpSpaceHash *hash, cpSpaceHashBin **bin_ptr, void *obj, cpSpatialIndexSegmentQueryFunc func, void *data) -{ - cpFloat t = 1.0f; - - restart: - for(cpSpaceHashBin *bin = *bin_ptr; bin; bin = bin->next){ - cpHandle *hand = bin->handle; - void *other = hand->obj; - - // Skip over certain conditions - if(hand->stamp == hash->stamp){ - continue; - } else if(other){ - t = cpfmin(t, func(obj, other, data)); - hand->stamp = hash->stamp; - } else { - // The object for this handle has been removed - // cleanup this cell and restart the query - remove_orphaned_handles(hash, bin_ptr); - goto restart; // GCC not smart enough/able to tail call an inlined function. - } - } - - return t; -} - -// modified from http://playtechs.blogspot.com/2007/03/raytracing-on-grid.html -static void -cpSpaceHashSegmentQuery(cpSpaceHash *hash, void *obj, cpVect a, cpVect b, cpFloat t_exit, cpSpatialIndexSegmentQueryFunc func, void *data) -{ - a = cpvmult(a, 1.0f/hash->celldim); - b = cpvmult(b, 1.0f/hash->celldim); - - int cell_x = floor_int(a.x), cell_y = floor_int(a.y); - - cpFloat t = 0; - - int x_inc, y_inc; - cpFloat temp_v, temp_h; - - if (b.x > a.x){ - x_inc = 1; - temp_h = (cpffloor(a.x + 1.0f) - a.x); - } else { - x_inc = -1; - temp_h = (a.x - cpffloor(a.x)); - } - - if (b.y > a.y){ - y_inc = 1; - temp_v = (cpffloor(a.y + 1.0f) - a.y); - } else { - y_inc = -1; - temp_v = (a.y - cpffloor(a.y)); - } - - // Division by zero is *very* slow on ARM - cpFloat dx = cpfabs(b.x - a.x), dy = cpfabs(b.y - a.y); - cpFloat dt_dx = (dx ? 1.0f/dx : INFINITY), dt_dy = (dy ? 1.0f/dy : INFINITY); - - // fix NANs in horizontal directions - cpFloat next_h = (temp_h ? temp_h*dt_dx : dt_dx); - cpFloat next_v = (temp_v ? temp_v*dt_dy : dt_dy); - - int n = hash->numcells; - cpSpaceHashBin **table = hash->table; - - while(t < t_exit){ - cpHashValue idx = hash_func(cell_x, cell_y, n); - t_exit = cpfmin(t_exit, segmentQuery_helper(hash, &table[idx], obj, func, data)); - - if (next_v < next_h){ - cell_y += y_inc; - t = next_v; - next_v += dt_dy; - } else { - cell_x += x_inc; - t = next_h; - next_h += dt_dx; - } - } - - hash->stamp++; -} - -//MARK: Misc - -void -cpSpaceHashResize(cpSpaceHash *hash, cpFloat celldim, int numcells) -{ - if(hash->spatialIndex.klass != Klass()){ - cpAssertWarn(cpFalse, "Ignoring cpSpaceHashResize() call to non-cpSpaceHash spatial index."); - return; - } - - clearTable(hash); - - hash->celldim = celldim; - cpSpaceHashAllocTable(hash, next_prime(numcells)); -} - -static int -cpSpaceHashCount(cpSpaceHash *hash) -{ - return cpHashSetCount(hash->handleSet); -} - -static int -cpSpaceHashContains(cpSpaceHash *hash, void *obj, cpHashValue hashid) -{ - return cpHashSetFind(hash->handleSet, hashid, obj) != NULL; -} - -static cpSpatialIndexClass klass = { - (cpSpatialIndexDestroyImpl)cpSpaceHashDestroy, - - (cpSpatialIndexCountImpl)cpSpaceHashCount, - (cpSpatialIndexEachImpl)cpSpaceHashEach, - (cpSpatialIndexContainsImpl)cpSpaceHashContains, - - (cpSpatialIndexInsertImpl)cpSpaceHashInsert, - (cpSpatialIndexRemoveImpl)cpSpaceHashRemove, - - (cpSpatialIndexReindexImpl)cpSpaceHashRehash, - (cpSpatialIndexReindexObjectImpl)cpSpaceHashRehashObject, - (cpSpatialIndexReindexQueryImpl)cpSpaceHashReindexQuery, - - (cpSpatialIndexQueryImpl)cpSpaceHashQuery, - (cpSpatialIndexSegmentQueryImpl)cpSpaceHashSegmentQuery, -}; - -static inline cpSpatialIndexClass *Klass(){return &klass;} - -//MARK: Debug Drawing - -//#define CP_BBTREE_DEBUG_DRAW -#ifdef CP_BBTREE_DEBUG_DRAW -#include "OpenGL/gl.h" -#include "OpenGL/glu.h" -#include - -void -cpSpaceHashRenderDebug(cpSpatialIndex *index) -{ - if(index->klass != &klass){ - cpAssertWarn(cpFalse, "Ignoring cpSpaceHashRenderDebug() call to non-spatial hash spatial index."); - return; - } - - cpSpaceHash *hash = (cpSpaceHash *)index; - cpBB bb = cpBBNew(-320, -240, 320, 240); - - cpFloat dim = hash->celldim; - int n = hash->numcells; - - int l = (int)floor(bb.l/dim); - int r = (int)floor(bb.r/dim); - int b = (int)floor(bb.b/dim); - int t = (int)floor(bb.t/dim); - - for(int i=l; i<=r; i++){ - for(int j=b; j<=t; j++){ - int cell_count = 0; - - int index = hash_func(i,j,n); - for(cpSpaceHashBin *bin = hash->table[index]; bin; bin = bin->next) - cell_count++; - - GLfloat v = 1.0f - (GLfloat)cell_count/10.0f; - glColor3f(v,v,v); - glRectf(i*dim, j*dim, (i + 1)*dim, (j + 1)*dim); - } - } -} -#endif diff --git a/3rdparty/chipmunk/src/cpSpaceQuery.c b/3rdparty/chipmunk/src/cpSpaceQuery.c deleted file mode 100644 index 1ce4a10c15a3..000000000000 --- a/3rdparty/chipmunk/src/cpSpaceQuery.c +++ /dev/null @@ -1,246 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk_private.h" - -//MARK: Nearest Point Query Functions - -struct PointQueryContext { - cpVect point; - cpFloat maxDistance; - cpShapeFilter filter; - cpSpacePointQueryFunc func; -}; - -static cpCollisionID -NearestPointQuery(struct PointQueryContext *context, cpShape *shape, cpCollisionID id, void *data) -{ - if( - !cpShapeFilterReject(shape->filter, context->filter) - ){ - cpPointQueryInfo info; - cpShapePointQuery(shape, context->point, &info); - - if(info.shape && info.distance < context->maxDistance) context->func(shape, info.point, info.distance, info.gradient, data); - } - - return id; -} - -void -cpSpacePointQuery(cpSpace *space, cpVect point, cpFloat maxDistance, cpShapeFilter filter, cpSpacePointQueryFunc func, void *data) -{ - struct PointQueryContext context = {point, maxDistance, filter, func}; - cpBB bb = cpBBNewForCircle(point, cpfmax(maxDistance, 0.0f)); - - cpSpaceLock(space); { - cpSpatialIndexQuery(space->dynamicShapes, &context, bb, (cpSpatialIndexQueryFunc)NearestPointQuery, data); - cpSpatialIndexQuery(space->staticShapes, &context, bb, (cpSpatialIndexQueryFunc)NearestPointQuery, data); - } cpSpaceUnlock(space, cpTrue); -} - -static cpCollisionID -NearestPointQueryNearest(struct PointQueryContext *context, cpShape *shape, cpCollisionID id, cpPointQueryInfo *out) -{ - if( - !cpShapeFilterReject(shape->filter, context->filter) && !shape->sensor - ){ - cpPointQueryInfo info; - cpShapePointQuery(shape, context->point, &info); - - if(info.distance < out->distance) (*out) = info; - } - - return id; -} - -cpShape * -cpSpacePointQueryNearest(cpSpace *space, cpVect point, cpFloat maxDistance, cpShapeFilter filter, cpPointQueryInfo *out) -{ - cpPointQueryInfo info = {NULL, cpvzero, maxDistance, cpvzero}; - if(out){ - (*out) = info; - } else { - out = &info; - } - - struct PointQueryContext context = { - point, maxDistance, - filter, - NULL - }; - - cpBB bb = cpBBNewForCircle(point, cpfmax(maxDistance, 0.0f)); - cpSpatialIndexQuery(space->dynamicShapes, &context, bb, (cpSpatialIndexQueryFunc)NearestPointQueryNearest, out); - cpSpatialIndexQuery(space->staticShapes, &context, bb, (cpSpatialIndexQueryFunc)NearestPointQueryNearest, out); - - return (cpShape *)out->shape; -} - - -//MARK: Segment Query Functions - -struct SegmentQueryContext { - cpVect start, end; - cpFloat radius; - cpShapeFilter filter; - cpSpaceSegmentQueryFunc func; -}; - -static cpFloat -SegmentQuery(struct SegmentQueryContext *context, cpShape *shape, void *data) -{ - cpSegmentQueryInfo info; - - if( - !cpShapeFilterReject(shape->filter, context->filter) && - cpShapeSegmentQuery(shape, context->start, context->end, context->radius, &info) - ){ - context->func(shape, info.point, info.normal, info.alpha, data); - } - - return 1.0f; -} - -void -cpSpaceSegmentQuery(cpSpace *space, cpVect start, cpVect end, cpFloat radius, cpShapeFilter filter, cpSpaceSegmentQueryFunc func, void *data) -{ - struct SegmentQueryContext context = { - start, end, - radius, - filter, - func, - }; - - cpSpaceLock(space); { - cpSpatialIndexSegmentQuery(space->staticShapes, &context, start, end, 1.0f, (cpSpatialIndexSegmentQueryFunc)SegmentQuery, data); - cpSpatialIndexSegmentQuery(space->dynamicShapes, &context, start, end, 1.0f, (cpSpatialIndexSegmentQueryFunc)SegmentQuery, data); - } cpSpaceUnlock(space, cpTrue); -} - -static cpFloat -SegmentQueryFirst(struct SegmentQueryContext *context, cpShape *shape, cpSegmentQueryInfo *out) -{ - cpSegmentQueryInfo info; - - if( - !cpShapeFilterReject(shape->filter, context->filter) && !shape->sensor && - cpShapeSegmentQuery(shape, context->start, context->end, context->radius, &info) && - info.alpha < out->alpha - ){ - (*out) = info; - } - - return out->alpha; -} - -cpShape * -cpSpaceSegmentQueryFirst(cpSpace *space, cpVect start, cpVect end, cpFloat radius, cpShapeFilter filter, cpSegmentQueryInfo *out) -{ - cpSegmentQueryInfo info = {NULL, end, cpvzero, 1.0f}; - if(out){ - (*out) = info; - } else { - out = &info; - } - - struct SegmentQueryContext context = { - start, end, - radius, - filter, - NULL - }; - - cpSpatialIndexSegmentQuery(space->staticShapes, &context, start, end, 1.0f, (cpSpatialIndexSegmentQueryFunc)SegmentQueryFirst, out); - cpSpatialIndexSegmentQuery(space->dynamicShapes, &context, start, end, out->alpha, (cpSpatialIndexSegmentQueryFunc)SegmentQueryFirst, out); - - return (cpShape *)out->shape; -} - -//MARK: BB Query Functions - -struct BBQueryContext { - cpBB bb; - cpShapeFilter filter; - cpSpaceBBQueryFunc func; -}; - -static cpCollisionID -BBQuery(struct BBQueryContext *context, cpShape *shape, cpCollisionID id, void *data) -{ - if( - !cpShapeFilterReject(shape->filter, context->filter) && - cpBBIntersects(context->bb, shape->bb) - ){ - context->func(shape, data); - } - - return id; -} - -void -cpSpaceBBQuery(cpSpace *space, cpBB bb, cpShapeFilter filter, cpSpaceBBQueryFunc func, void *data) -{ - struct BBQueryContext context = {bb, filter, func}; - - cpSpaceLock(space); { - cpSpatialIndexQuery(space->dynamicShapes, &context, bb, (cpSpatialIndexQueryFunc)BBQuery, data); - cpSpatialIndexQuery(space->staticShapes, &context, bb, (cpSpatialIndexQueryFunc)BBQuery, data); - } cpSpaceUnlock(space, cpTrue); -} - -//MARK: Shape Query Functions - -struct ShapeQueryContext { - cpSpaceShapeQueryFunc func; - void *data; - cpBool anyCollision; -}; - -// Callback from the spatial hash. -static cpCollisionID -ShapeQuery(cpShape *a, cpShape *b, cpCollisionID id, struct ShapeQueryContext *context) -{ - if(cpShapeFilterReject(a->filter, b->filter) || a == b) return id; - - cpContactPointSet set = cpShapesCollide(a, b); - if(set.count){ - if(context->func) context->func(b, &set, context->data); - context->anyCollision = !(a->sensor || b->sensor); - } - - return id; -} - -cpBool -cpSpaceShapeQuery(cpSpace *space, cpShape *shape, cpSpaceShapeQueryFunc func, void *data) -{ - cpBody *body = shape->body; - cpBB bb = (body ? cpShapeUpdate(shape, body->transform) : shape->bb); - struct ShapeQueryContext context = {func, data, cpFalse}; - - cpSpaceLock(space); { - cpSpatialIndexQuery(space->dynamicShapes, shape, bb, (cpSpatialIndexQueryFunc)ShapeQuery, &context); - cpSpatialIndexQuery(space->staticShapes, shape, bb, (cpSpatialIndexQueryFunc)ShapeQuery, &context); - } cpSpaceUnlock(space, cpTrue); - - return context.anyCollision; -} diff --git a/3rdparty/chipmunk/src/cpSpaceStep.c b/3rdparty/chipmunk/src/cpSpaceStep.c deleted file mode 100644 index 85cbb3d0c865..000000000000 --- a/3rdparty/chipmunk/src/cpSpaceStep.c +++ /dev/null @@ -1,445 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk_private.h" - -//MARK: Post Step Callback Functions - -cpPostStepCallback * -cpSpaceGetPostStepCallback(cpSpace *space, void *key) -{ - cpArray *arr = space->postStepCallbacks; - for(int i=0; inum; i++){ - cpPostStepCallback *callback = (cpPostStepCallback *)arr->arr[i]; - if(callback && callback->key == key) return callback; - } - - return NULL; -} - -static void PostStepDoNothing(cpSpace *space, void *obj, void *data){} - -cpBool -cpSpaceAddPostStepCallback(cpSpace *space, cpPostStepFunc func, void *key, void *data) -{ - cpAssertWarn(space->locked, - "Adding a post-step callback when the space is not locked is unnecessary. " - "Post-step callbacks will not called until the end of the next call to cpSpaceStep() or the next query."); - - if(!cpSpaceGetPostStepCallback(space, key)){ - cpPostStepCallback *callback = (cpPostStepCallback *)cpcalloc(1, sizeof(cpPostStepCallback)); - callback->func = (func ? func : PostStepDoNothing); - callback->key = key; - callback->data = data; - - cpArrayPush(space->postStepCallbacks, callback); - return cpTrue; - } else { - return cpFalse; - } -} - -//MARK: Locking Functions - -void -cpSpaceLock(cpSpace *space) -{ - space->locked++; -} - -void -cpSpaceUnlock(cpSpace *space, cpBool runPostStep) -{ - space->locked--; - cpAssertHard(space->locked >= 0, "Internal Error: Space lock underflow."); - - if(space->locked == 0){ - cpArray *waking = space->rousedBodies; - - for(int i=0, count=waking->num; iarr[i]); - waking->arr[i] = NULL; - } - - waking->num = 0; - - if(space->locked == 0 && runPostStep && !space->skipPostStep){ - space->skipPostStep = cpTrue; - - cpArray *arr = space->postStepCallbacks; - for(int i=0; inum; i++){ - cpPostStepCallback *callback = (cpPostStepCallback *)arr->arr[i]; - cpPostStepFunc func = callback->func; - - // Mark the func as NULL in case calling it calls cpSpaceRunPostStepCallbacks() again. - // TODO: need more tests around this case I think. - callback->func = NULL; - if(func) func(space, callback->key, callback->data); - - arr->arr[i] = NULL; - cpfree(callback); - } - - arr->num = 0; - space->skipPostStep = cpFalse; - } - } -} - -//MARK: Contact Buffer Functions - -struct cpContactBufferHeader { - cpTimestamp stamp; - cpContactBufferHeader *next; - unsigned int numContacts; -}; - -#define CP_CONTACTS_BUFFER_SIZE ((CP_BUFFER_BYTES - sizeof(cpContactBufferHeader))/sizeof(struct cpContact)) -typedef struct cpContactBuffer { - cpContactBufferHeader header; - struct cpContact contacts[CP_CONTACTS_BUFFER_SIZE]; -} cpContactBuffer; - -static cpContactBufferHeader * -cpSpaceAllocContactBuffer(cpSpace *space) -{ - cpContactBuffer *buffer = (cpContactBuffer *)cpcalloc(1, sizeof(cpContactBuffer)); - cpArrayPush(space->allocatedBuffers, buffer); - return (cpContactBufferHeader *)buffer; -} - -static cpContactBufferHeader * -cpContactBufferHeaderInit(cpContactBufferHeader *header, cpTimestamp stamp, cpContactBufferHeader *splice) -{ - header->stamp = stamp; - header->next = (splice ? splice->next : header); - header->numContacts = 0; - - return header; -} - -void -cpSpacePushFreshContactBuffer(cpSpace *space) -{ - cpTimestamp stamp = space->stamp; - - cpContactBufferHeader *head = space->contactBuffersHead; - - if(!head){ - // No buffers have been allocated, make one - space->contactBuffersHead = cpContactBufferHeaderInit(cpSpaceAllocContactBuffer(space), stamp, NULL); - } else if(stamp - head->next->stamp > space->collisionPersistence){ - // The tail buffer is available, rotate the ring - cpContactBufferHeader *tail = head->next; - space->contactBuffersHead = cpContactBufferHeaderInit(tail, stamp, tail); - } else { - // Allocate a new buffer and push it into the ring - cpContactBufferHeader *buffer = cpContactBufferHeaderInit(cpSpaceAllocContactBuffer(space), stamp, head); - space->contactBuffersHead = head->next = buffer; - } -} - - -struct cpContact * -cpContactBufferGetArray(cpSpace *space) -{ - if(space->contactBuffersHead->numContacts + CP_MAX_CONTACTS_PER_ARBITER > CP_CONTACTS_BUFFER_SIZE){ - // contact buffer could overflow on the next collision, push a fresh one. - cpSpacePushFreshContactBuffer(space); - } - - cpContactBufferHeader *head = space->contactBuffersHead; - return ((cpContactBuffer *)head)->contacts + head->numContacts; -} - -void -cpSpacePushContacts(cpSpace *space, int count) -{ - cpAssertHard(count <= CP_MAX_CONTACTS_PER_ARBITER, "Internal Error: Contact buffer overflow!"); - space->contactBuffersHead->numContacts += count; -} - -static void -cpSpacePopContacts(cpSpace *space, int count){ - space->contactBuffersHead->numContacts -= count; -} - -//MARK: Collision Detection Functions - -static void * -cpSpaceArbiterSetTrans(cpShape **shapes, cpSpace *space) -{ - if(space->pooledArbiters->num == 0){ - // arbiter pool is exhausted, make more - int count = CP_BUFFER_BYTES/sizeof(cpArbiter); - cpAssertHard(count, "Internal Error: Buffer size too small."); - - cpArbiter *buffer = (cpArbiter *)cpcalloc(1, CP_BUFFER_BYTES); - cpArrayPush(space->allocatedBuffers, buffer); - - for(int i=0; ipooledArbiters, buffer + i); - } - - return cpArbiterInit((cpArbiter *)cpArrayPop(space->pooledArbiters), shapes[0], shapes[1]); -} - -static inline cpBool -QueryRejectConstraint(cpBody *a, cpBody *b) -{ - CP_BODY_FOREACH_CONSTRAINT(a, constraint){ - if( - !constraint->collideBodies && ( - (constraint->a == a && constraint->b == b) || - (constraint->a == b && constraint->b == a) - ) - ) return cpTrue; - } - - return cpFalse; -} - -static inline cpBool -QueryReject(cpShape *a, cpShape *b) -{ - return ( - // BBoxes must overlap - !cpBBIntersects(a->bb, b->bb) - // Don't collide shapes attached to the same body. - || a->body == b->body - // Don't collide shapes that are filtered. - || cpShapeFilterReject(a->filter, b->filter) - // Don't collide bodies if they have a constraint with collideBodies == cpFalse. - || QueryRejectConstraint(a->body, b->body) - ); -} - -// Callback from the spatial hash. -cpCollisionID -cpSpaceCollideShapes(cpShape *a, cpShape *b, cpCollisionID id, cpSpace *space) -{ - // Reject any of the simple cases - if(QueryReject(a,b)) return id; - - // Narrow-phase collision detection. - struct cpCollisionInfo info = cpCollide(a, b, id, cpContactBufferGetArray(space)); - - if(info.count == 0) return info.id; // Shapes are not colliding. - cpSpacePushContacts(space, info.count); - - // Get an arbiter from space->arbiterSet for the two shapes. - // This is where the persistant contact magic comes from. - const cpShape *shape_pair[] = {info.a, info.b}; - cpHashValue arbHashID = CP_HASH_PAIR((cpHashValue)info.a, (cpHashValue)info.b); - cpArbiter *arb = (cpArbiter *)cpHashSetInsert(space->cachedArbiters, arbHashID, shape_pair, (cpHashSetTransFunc)cpSpaceArbiterSetTrans, space); - cpArbiterUpdate(arb, &info, space); - - cpCollisionHandler *handler = arb->handler; - - // Call the begin function first if it's the first step - if(arb->state == CP_ARBITER_STATE_FIRST_COLLISION && !handler->beginFunc(arb, space, handler->userData)){ - cpArbiterIgnore(arb); // permanently ignore the collision until separation - } - - if( - // Ignore the arbiter if it has been flagged - (arb->state != CP_ARBITER_STATE_IGNORE) && - // Call preSolve - handler->preSolveFunc(arb, space, handler->userData) && - // Check (again) in case the pre-solve() callback called cpArbiterIgnored(). - arb->state != CP_ARBITER_STATE_IGNORE && - // Process, but don't add collisions for sensors. - !(a->sensor || b->sensor) && - // Don't process collisions between two infinite mass bodies. - // This includes collisions between two kinematic bodies, or a kinematic body and a static body. - !(a->body->m == INFINITY && b->body->m == INFINITY) - ){ - cpArrayPush(space->arbiters, arb); - } else { - cpSpacePopContacts(space, info.count); - - arb->contacts = NULL; - arb->count = 0; - - // Normally arbiters are set as used after calling the post-solve callback. - // However, post-solve() callbacks are not called for sensors or arbiters rejected from pre-solve. - if(arb->state != CP_ARBITER_STATE_IGNORE) arb->state = CP_ARBITER_STATE_NORMAL; - } - - // Time stamp the arbiter so we know it was used recently. - arb->stamp = space->stamp; - return info.id; -} - -// Hashset filter func to throw away old arbiters. -cpBool -cpSpaceArbiterSetFilter(cpArbiter *arb, cpSpace *space) -{ - cpTimestamp ticks = space->stamp - arb->stamp; - - cpBody *a = arb->body_a, *b = arb->body_b; - - // TODO: should make an arbiter state for this so it doesn't require filtering arbiters for dangling body pointers on body removal. - // Preserve arbiters on sensors and rejected arbiters for sleeping objects. - // This prevents errant separate callbacks from happenening. - if( - (cpBodyGetType(a) == CP_BODY_TYPE_STATIC || cpBodyIsSleeping(a)) && - (cpBodyGetType(b) == CP_BODY_TYPE_STATIC || cpBodyIsSleeping(b)) - ){ - return cpTrue; - } - - // Arbiter was used last frame, but not this one - if(ticks >= 1 && arb->state != CP_ARBITER_STATE_CACHED){ - arb->state = CP_ARBITER_STATE_CACHED; - cpCollisionHandler *handler = arb->handler; - handler->separateFunc(arb, space, handler->userData); - } - - if(ticks >= space->collisionPersistence){ - arb->contacts = NULL; - arb->count = 0; - - cpArrayPush(space->pooledArbiters, arb); - return cpFalse; - } - - return cpTrue; -} - -//MARK: All Important cpSpaceStep() Function - - void -cpShapeUpdateFunc(cpShape *shape, void *unused) -{ - cpShapeCacheBB(shape); -} - -void -cpSpaceStep(cpSpace *space, cpFloat dt) -{ - // don't step if the timestep is 0! - if(dt == 0.0f) return; - - space->stamp++; - - cpFloat prev_dt = space->curr_dt; - space->curr_dt = dt; - - cpArray *bodies = space->dynamicBodies; - cpArray *constraints = space->constraints; - cpArray *arbiters = space->arbiters; - - // Reset and empty the arbiter lists. - for(int i=0; inum; i++){ - cpArbiter *arb = (cpArbiter *)arbiters->arr[i]; - arb->state = CP_ARBITER_STATE_NORMAL; - - // If both bodies are awake, unthread the arbiter from the contact graph. - if(!cpBodyIsSleeping(arb->body_a) && !cpBodyIsSleeping(arb->body_b)){ - cpArbiterUnthread(arb); - } - } - arbiters->num = 0; - - cpSpaceLock(space); { - // Integrate positions - for(int i=0; inum; i++){ - cpBody *body = (cpBody *)bodies->arr[i]; - body->position_func(body, dt); - } - - // Find colliding pairs. - cpSpacePushFreshContactBuffer(space); - cpSpatialIndexEach(space->dynamicShapes, (cpSpatialIndexIteratorFunc)cpShapeUpdateFunc, NULL); - cpSpatialIndexReindexQuery(space->dynamicShapes, (cpSpatialIndexQueryFunc)cpSpaceCollideShapes, space); - } cpSpaceUnlock(space, cpFalse); - - // Rebuild the contact graph (and detect sleeping components if sleeping is enabled) - cpSpaceProcessComponents(space, dt); - - cpSpaceLock(space); { - // Clear out old cached arbiters and call separate callbacks - cpHashSetFilter(space->cachedArbiters, (cpHashSetFilterFunc)cpSpaceArbiterSetFilter, space); - - // Prestep the arbiters and constraints. - cpFloat slop = space->collisionSlop; - cpFloat biasCoef = 1.0f - cpfpow(space->collisionBias, dt); - for(int i=0; inum; i++){ - cpArbiterPreStep((cpArbiter *)arbiters->arr[i], dt, slop, biasCoef); - } - - for(int i=0; inum; i++){ - cpConstraint *constraint = (cpConstraint *)constraints->arr[i]; - - cpConstraintPreSolveFunc preSolve = constraint->preSolve; - if(preSolve) preSolve(constraint, space); - - constraint->klass->preStep(constraint, dt); - } - - // Integrate velocities. - cpFloat damping = cpfpow(space->damping, dt); - cpVect gravity = space->gravity; - for(int i=0; inum; i++){ - cpBody *body = (cpBody *)bodies->arr[i]; - body->velocity_func(body, gravity, damping, dt); - } - - // Apply cached impulses - cpFloat dt_coef = (prev_dt == 0.0f ? 0.0f : dt/prev_dt); - for(int i=0; inum; i++){ - cpArbiterApplyCachedImpulse((cpArbiter *)arbiters->arr[i], dt_coef); - } - - for(int i=0; inum; i++){ - cpConstraint *constraint = (cpConstraint *)constraints->arr[i]; - constraint->klass->applyCachedImpulse(constraint, dt_coef); - } - - // Run the impulse solver. - for(int i=0; iiterations; i++){ - for(int j=0; jnum; j++){ - cpArbiterApplyImpulse((cpArbiter *)arbiters->arr[j]); - } - - for(int j=0; jnum; j++){ - cpConstraint *constraint = (cpConstraint *)constraints->arr[j]; - constraint->klass->applyImpulse(constraint, dt); - } - } - - // Run the constraint post-solve callbacks - for(int i=0; inum; i++){ - cpConstraint *constraint = (cpConstraint *)constraints->arr[i]; - - cpConstraintPostSolveFunc postSolve = constraint->postSolve; - if(postSolve) postSolve(constraint, space); - } - - // run the post-solve callbacks - for(int i=0; inum; i++){ - cpArbiter *arb = (cpArbiter *) arbiters->arr[i]; - - cpCollisionHandler *handler = arb->handler; - handler->postSolveFunc(arb, space, handler->userData); - } - } cpSpaceUnlock(space, cpTrue); -} diff --git a/3rdparty/chipmunk/src/cpSpatialIndex.c b/3rdparty/chipmunk/src/cpSpatialIndex.c deleted file mode 100644 index 3fb7cb5d9113..000000000000 --- a/3rdparty/chipmunk/src/cpSpatialIndex.c +++ /dev/null @@ -1,69 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk_private.h" - -void -cpSpatialIndexFree(cpSpatialIndex *index) -{ - if(index){ - cpSpatialIndexDestroy(index); - cpfree(index); - } -} - -cpSpatialIndex * -cpSpatialIndexInit(cpSpatialIndex *index, cpSpatialIndexClass *klass, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex) -{ - index->klass = klass; - index->bbfunc = bbfunc; - index->staticIndex = staticIndex; - - if(staticIndex){ - cpAssertHard(!staticIndex->dynamicIndex, "This static index is already associated with a dynamic index."); - staticIndex->dynamicIndex = index; - } - - return index; -} - -typedef struct dynamicToStaticContext { - cpSpatialIndexBBFunc bbfunc; - cpSpatialIndex *staticIndex; - cpSpatialIndexQueryFunc queryFunc; - void *data; -} dynamicToStaticContext; - -static void -dynamicToStaticIter(void *obj, dynamicToStaticContext *context) -{ - cpSpatialIndexQuery(context->staticIndex, obj, context->bbfunc(obj), context->queryFunc, context->data); -} - -void -cpSpatialIndexCollideStatic(cpSpatialIndex *dynamicIndex, cpSpatialIndex *staticIndex, cpSpatialIndexQueryFunc func, void *data) -{ - if(staticIndex && cpSpatialIndexCount(staticIndex) > 0){ - dynamicToStaticContext context = {dynamicIndex->bbfunc, staticIndex, func, data}; - cpSpatialIndexEach(dynamicIndex, (cpSpatialIndexIteratorFunc)dynamicToStaticIter, &context); - } -} - diff --git a/3rdparty/chipmunk/src/cpSweep1D.c b/3rdparty/chipmunk/src/cpSweep1D.c deleted file mode 100644 index 94c4e2255c6f..000000000000 --- a/3rdparty/chipmunk/src/cpSweep1D.c +++ /dev/null @@ -1,254 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk_private.h" - -static inline cpSpatialIndexClass *Klass(void); - -//MARK: Basic Structures - -typedef struct Bounds { - cpFloat min, max; -} Bounds; - -typedef struct TableCell { - void *obj; - Bounds bounds; -} TableCell; - -struct cpSweep1D -{ - cpSpatialIndex spatialIndex; - - int num; - int max; - TableCell *table; -}; - -static inline cpBool -BoundsOverlap(Bounds a, Bounds b) -{ - return (a.min <= b.max && b.min <= a.max); -} - -static inline Bounds -BBToBounds(cpSweep1D *sweep, cpBB bb) -{ - Bounds bounds = {bb.l, bb.r}; - return bounds; -} - -static inline TableCell -MakeTableCell(cpSweep1D *sweep, void *obj) -{ - TableCell cell = {obj, BBToBounds(sweep, sweep->spatialIndex.bbfunc(obj))}; - return cell; -} - -//MARK: Memory Management Functions - -cpSweep1D * -cpSweep1DAlloc(void) -{ - return (cpSweep1D *)cpcalloc(1, sizeof(cpSweep1D)); -} - -static void -ResizeTable(cpSweep1D *sweep, int size) -{ - sweep->max = size; - sweep->table = (TableCell *)cprealloc(sweep->table, size*sizeof(TableCell)); -} - -cpSpatialIndex * -cpSweep1DInit(cpSweep1D *sweep, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex) -{ - cpSpatialIndexInit((cpSpatialIndex *)sweep, Klass(), bbfunc, staticIndex); - - sweep->num = 0; - ResizeTable(sweep, 32); - - return (cpSpatialIndex *)sweep; -} - -cpSpatialIndex * -cpSweep1DNew(cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex) -{ - return cpSweep1DInit(cpSweep1DAlloc(), bbfunc, staticIndex); -} - -static void -cpSweep1DDestroy(cpSweep1D *sweep) -{ - cpfree(sweep->table); - sweep->table = NULL; -} - -//MARK: Misc - -static int -cpSweep1DCount(cpSweep1D *sweep) -{ - return sweep->num; -} - -static void -cpSweep1DEach(cpSweep1D *sweep, cpSpatialIndexIteratorFunc func, void *data) -{ - TableCell *table = sweep->table; - for(int i=0, count=sweep->num; itable; - for(int i=0, count=sweep->num; inum == sweep->max) ResizeTable(sweep, sweep->max*2); - - sweep->table[sweep->num] = MakeTableCell(sweep, obj); - sweep->num++; -} - -static void -cpSweep1DRemove(cpSweep1D *sweep, void *obj, cpHashValue hashid) -{ - TableCell *table = sweep->table; - for(int i=0, count=sweep->num; inum; - - table[i] = table[num]; - table[num].obj = NULL; - - return; - } - } -} - -//MARK: Reindexing Functions - -static void -cpSweep1DReindexObject(cpSweep1D *sweep, void *obj, cpHashValue hashid) -{ - // Nothing to do here -} - -static void -cpSweep1DReindex(cpSweep1D *sweep) -{ - // Nothing to do here - // Could perform a sort, but queries are not accelerated anyway. -} - -//MARK: Query Functions - -static void -cpSweep1DQuery(cpSweep1D *sweep, void *obj, cpBB bb, cpSpatialIndexQueryFunc func, void *data) -{ - // Implementing binary search here would allow you to find an upper limit - // but not a lower limit. Probably not worth the hassle. - - Bounds bounds = BBToBounds(sweep, bb); - - TableCell *table = sweep->table; - for(int i=0, count=sweep->num; itable; - for(int i=0, count=sweep->num; ibounds.min < b->bounds.min ? -1 : (a->bounds.min > b->bounds.min ? 1 : 0)); -} - -static void -cpSweep1DReindexQuery(cpSweep1D *sweep, cpSpatialIndexQueryFunc func, void *data) -{ - TableCell *table = sweep->table; - int count = sweep->num; - - // Update bounds and sort - for(int i=0; ispatialIndex.staticIndex, func, data); -} - -static cpSpatialIndexClass klass = { - (cpSpatialIndexDestroyImpl)cpSweep1DDestroy, - - (cpSpatialIndexCountImpl)cpSweep1DCount, - (cpSpatialIndexEachImpl)cpSweep1DEach, - (cpSpatialIndexContainsImpl)cpSweep1DContains, - - (cpSpatialIndexInsertImpl)cpSweep1DInsert, - (cpSpatialIndexRemoveImpl)cpSweep1DRemove, - - (cpSpatialIndexReindexImpl)cpSweep1DReindex, - (cpSpatialIndexReindexObjectImpl)cpSweep1DReindexObject, - (cpSpatialIndexReindexQueryImpl)cpSweep1DReindexQuery, - - (cpSpatialIndexQueryImpl)cpSweep1DQuery, - (cpSpatialIndexSegmentQueryImpl)cpSweep1DSegmentQuery, -}; - -static inline cpSpatialIndexClass *Klass(){return &klass;} - diff --git a/3rdparty/chipmunk/src/prime.h b/3rdparty/chipmunk/src/prime.h deleted file mode 100644 index d470c2cdd76d..000000000000 --- a/3rdparty/chipmunk/src/prime.h +++ /dev/null @@ -1,68 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -// Used for resizing hash tables. -// Values approximately double. -// http://planetmath.org/encyclopedia/GoodHashTablePrimes.html -static int primes[] = { - 5, - 13, - 23, - 47, - 97, - 193, - 389, - 769, - 1543, - 3079, - 6151, - 12289, - 24593, - 49157, - 98317, - 196613, - 393241, - 786433, - 1572869, - 3145739, - 6291469, - 12582917, - 25165843, - 50331653, - 100663319, - 201326611, - 402653189, - 805306457, - 1610612741, - 0, -}; - -static inline int -next_prime(int n) -{ - int i = 0; - while(n > primes[i]){ - i++; - cpAssertHard(primes[i], "Tried to resize a hash table to a size greater than 1610612741 O_o"); // realistically this should never happen - } - - return primes[i]; -} diff --git a/3rdparty/fast_float/ascii_number.h b/3rdparty/fast_float/ascii_number.h new file mode 100644 index 000000000000..9001016ac615 --- /dev/null +++ b/3rdparty/fast_float/ascii_number.h @@ -0,0 +1,588 @@ +#ifndef FASTFLOAT_ASCII_NUMBER_H +#define FASTFLOAT_ASCII_NUMBER_H + +#include +#include +#include +#include +#include +#include + +#include "float_common.h" + +#ifdef FASTFLOAT_SSE2 +#include +#endif + +#ifdef FASTFLOAT_NEON +#include +#endif + +namespace fast_float { + +template fastfloat_really_inline constexpr bool has_simd_opt() { +#ifdef FASTFLOAT_HAS_SIMD + return std::is_same::value; +#else + return false; +#endif +} + +// Next function can be micro-optimized, but compilers are entirely +// able to optimize it well. +template +fastfloat_really_inline constexpr bool is_integer(UC c) noexcept { + return !(c > UC('9') || c < UC('0')); +} + +fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) { + return (val & 0xFF00000000000000) >> 56 | (val & 0x00FF000000000000) >> 40 | + (val & 0x0000FF0000000000) >> 24 | (val & 0x000000FF00000000) >> 8 | + (val & 0x00000000FF000000) << 8 | (val & 0x0000000000FF0000) << 24 | + (val & 0x000000000000FF00) << 40 | (val & 0x00000000000000FF) << 56; +} + +// Read 8 UC into a u64. Truncates UC if not char. +template +fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t +read8_to_u64(UC const *chars) { + if (cpp20_and_in_constexpr() || !std::is_same::value) { + uint64_t val = 0; + for (int i = 0; i < 8; ++i) { + val |= uint64_t(uint8_t(*chars)) << (i * 8); + ++chars; + } + return val; + } + uint64_t val; + ::memcpy(&val, chars, sizeof(uint64_t)); +#if FASTFLOAT_IS_BIG_ENDIAN == 1 + // Need to read as-if the number was in little-endian order. + val = byteswap(val); +#endif + return val; +} + +#ifdef FASTFLOAT_SSE2 + +fastfloat_really_inline uint64_t simd_read8_to_u64(__m128i const data) { + FASTFLOAT_SIMD_DISABLE_WARNINGS + __m128i const packed = _mm_packus_epi16(data, data); +#ifdef FASTFLOAT_64BIT + return uint64_t(_mm_cvtsi128_si64(packed)); +#else + uint64_t value; + // Visual Studio + older versions of GCC don't support _mm_storeu_si64 + _mm_storel_epi64(reinterpret_cast<__m128i *>(&value), packed); + return value; +#endif + FASTFLOAT_SIMD_RESTORE_WARNINGS +} + +fastfloat_really_inline uint64_t simd_read8_to_u64(char16_t const *chars) { + FASTFLOAT_SIMD_DISABLE_WARNINGS + return simd_read8_to_u64( + _mm_loadu_si128(reinterpret_cast<__m128i const *>(chars))); + FASTFLOAT_SIMD_RESTORE_WARNINGS +} + +#elif defined(FASTFLOAT_NEON) + +fastfloat_really_inline uint64_t simd_read8_to_u64(uint16x8_t const data) { + FASTFLOAT_SIMD_DISABLE_WARNINGS + uint8x8_t utf8_packed = vmovn_u16(data); + return vget_lane_u64(vreinterpret_u64_u8(utf8_packed), 0); + FASTFLOAT_SIMD_RESTORE_WARNINGS +} + +fastfloat_really_inline uint64_t simd_read8_to_u64(char16_t const *chars) { + FASTFLOAT_SIMD_DISABLE_WARNINGS + return simd_read8_to_u64( + vld1q_u16(reinterpret_cast(chars))); + FASTFLOAT_SIMD_RESTORE_WARNINGS +} + +#endif // FASTFLOAT_SSE2 + +// MSVC SFINAE is broken pre-VS2017 +#if defined(_MSC_VER) && _MSC_VER <= 1900 +template +#else +template ()) = 0> +#endif +// dummy for compile +uint64_t simd_read8_to_u64(UC const *) { + return 0; +} + +// credit @aqrit +fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint32_t +parse_eight_digits_unrolled(uint64_t val) { + uint64_t const mask = 0x000000FF000000FF; + uint64_t const mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32) + uint64_t const mul2 = 0x0000271000000001; // 1 + (10000ULL << 32) + val -= 0x3030303030303030; + val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8; + val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32; + return uint32_t(val); +} + +// Call this if chars are definitely 8 digits. +template +fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint32_t +parse_eight_digits_unrolled(UC const *chars) noexcept { + if (cpp20_and_in_constexpr() || !has_simd_opt()) { + return parse_eight_digits_unrolled(read8_to_u64(chars)); // truncation okay + } + return parse_eight_digits_unrolled(simd_read8_to_u64(chars)); +} + +// credit @aqrit +fastfloat_really_inline constexpr bool +is_made_of_eight_digits_fast(uint64_t val) noexcept { + return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) & + 0x8080808080808080)); +} + +#ifdef FASTFLOAT_HAS_SIMD + +// Call this if chars might not be 8 digits. +// Using this style (instead of is_made_of_eight_digits_fast() then +// parse_eight_digits_unrolled()) ensures we don't load SIMD registers twice. +fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool +simd_parse_if_eight_digits_unrolled(char16_t const *chars, + uint64_t &i) noexcept { + if (cpp20_and_in_constexpr()) { + return false; + } +#ifdef FASTFLOAT_SSE2 + FASTFLOAT_SIMD_DISABLE_WARNINGS + __m128i const data = + _mm_loadu_si128(reinterpret_cast<__m128i const *>(chars)); + + // (x - '0') <= 9 + // http://0x80.pl/articles/simd-parsing-int-sequences.html + __m128i const t0 = _mm_add_epi16(data, _mm_set1_epi16(32720)); + __m128i const t1 = _mm_cmpgt_epi16(t0, _mm_set1_epi16(-32759)); + + if (_mm_movemask_epi8(t1) == 0) { + i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data)); + return true; + } else + return false; + FASTFLOAT_SIMD_RESTORE_WARNINGS +#elif defined(FASTFLOAT_NEON) + FASTFLOAT_SIMD_DISABLE_WARNINGS + uint16x8_t const data = vld1q_u16(reinterpret_cast(chars)); + + // (x - '0') <= 9 + // http://0x80.pl/articles/simd-parsing-int-sequences.html + uint16x8_t const t0 = vsubq_u16(data, vmovq_n_u16('0')); + uint16x8_t const mask = vcltq_u16(t0, vmovq_n_u16('9' - '0' + 1)); + + if (vminvq_u16(mask) == 0xFFFF) { + i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data)); + return true; + } else + return false; + FASTFLOAT_SIMD_RESTORE_WARNINGS +#else + (void)chars; + (void)i; + return false; +#endif // FASTFLOAT_SSE2 +} + +#endif // FASTFLOAT_HAS_SIMD + +// MSVC SFINAE is broken pre-VS2017 +#if defined(_MSC_VER) && _MSC_VER <= 1900 +template +#else +template ()) = 0> +#endif +// dummy for compile +bool simd_parse_if_eight_digits_unrolled(UC const *, uint64_t &) { + return 0; +} + +template ::value) = 0> +fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void +loop_parse_if_eight_digits(UC const *&p, UC const *const pend, uint64_t &i) { + if (!has_simd_opt()) { + return; + } + while ((std::distance(p, pend) >= 8) && + simd_parse_if_eight_digits_unrolled( + p, i)) { // in rare cases, this will overflow, but that's ok + p += 8; + } +} + +fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void +loop_parse_if_eight_digits(char const *&p, char const *const pend, + uint64_t &i) { + // optimizes better than parse_if_eight_digits_unrolled() for UC = char. + while ((std::distance(p, pend) >= 8) && + is_made_of_eight_digits_fast(read8_to_u64(p))) { + i = i * 100000000 + + parse_eight_digits_unrolled(read8_to_u64( + p)); // in rare cases, this will overflow, but that's ok + p += 8; + } +} + +enum class parse_error { + no_error, + // [JSON-only] The minus sign must be followed by an integer. + missing_integer_after_sign, + // A sign must be followed by an integer or dot. + missing_integer_or_dot_after_sign, + // [JSON-only] The integer part must not have leading zeros. + leading_zeros_in_integer_part, + // [JSON-only] The integer part must have at least one digit. + no_digits_in_integer_part, + // [JSON-only] If there is a decimal point, there must be digits in the + // fractional part. + no_digits_in_fractional_part, + // The mantissa must have at least one digit. + no_digits_in_mantissa, + // Scientific notation requires an exponential part. + missing_exponential_part, +}; + +template struct parsed_number_string_t { + int64_t exponent{0}; + uint64_t mantissa{0}; + UC const *lastmatch{nullptr}; + bool negative{false}; + bool valid{false}; + bool too_many_digits{false}; + // contains the range of the significant digits + span integer{}; // non-nullable + span fraction{}; // nullable + parse_error error{parse_error::no_error}; +}; + +using byte_span = span; +using parsed_number_string = parsed_number_string_t; + +template +fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t +report_parse_error(UC const *p, parse_error error) { + parsed_number_string_t answer; + answer.valid = false; + answer.lastmatch = p; + answer.error = error; + return answer; +} + +// Assuming that you use no more than 19 digits, this will +// parse an ASCII string. +template +fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t +parse_number_string(UC const *p, UC const *pend, + parse_options_t options) noexcept { + chars_format const fmt = detail::adjust_for_feature_macros(options.format); + UC const decimal_point = options.decimal_point; + + parsed_number_string_t answer; + answer.valid = false; + answer.too_many_digits = false; + // assume p < pend, so dereference without checks; + answer.negative = (*p == UC('-')); + // C++17 20.19.3.(7.1) explicitly forbids '+' sign here + if ((*p == UC('-')) || + (uint64_t(fmt & chars_format::allow_leading_plus) && + !uint64_t(fmt & detail::basic_json_fmt) && *p == UC('+'))) { + ++p; + if (p == pend) { + return report_parse_error( + p, parse_error::missing_integer_or_dot_after_sign); + } + if (uint64_t(fmt & detail::basic_json_fmt)) { + if (!is_integer(*p)) { // a sign must be followed by an integer + return report_parse_error(p, + parse_error::missing_integer_after_sign); + } + } else { + if (!is_integer(*p) && + (*p != + decimal_point)) { // a sign must be followed by an integer or the dot + return report_parse_error( + p, parse_error::missing_integer_or_dot_after_sign); + } + } + } + UC const *const start_digits = p; + + uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad) + + while ((p != pend) && is_integer(*p)) { + // a multiplication by 10 is cheaper than an arbitrary integer + // multiplication + i = 10 * i + + uint64_t(*p - + UC('0')); // might overflow, we will handle the overflow later + ++p; + } + UC const *const end_of_integer_part = p; + int64_t digit_count = int64_t(end_of_integer_part - start_digits); + answer.integer = span(start_digits, size_t(digit_count)); + if (uint64_t(fmt & detail::basic_json_fmt)) { + // at least 1 digit in integer part, without leading zeros + if (digit_count == 0) { + return report_parse_error(p, parse_error::no_digits_in_integer_part); + } + if ((start_digits[0] == UC('0') && digit_count > 1)) { + return report_parse_error(start_digits, + parse_error::leading_zeros_in_integer_part); + } + } + + int64_t exponent = 0; + bool const has_decimal_point = (p != pend) && (*p == decimal_point); + if (has_decimal_point) { + ++p; + UC const *before = p; + // can occur at most twice without overflowing, but let it occur more, since + // for integers with many digits, digit parsing is the primary bottleneck. + loop_parse_if_eight_digits(p, pend, i); + + while ((p != pend) && is_integer(*p)) { + uint8_t digit = uint8_t(*p - UC('0')); + ++p; + i = i * 10 + digit; // in rare cases, this will overflow, but that's ok + } + exponent = before - p; + answer.fraction = span(before, size_t(p - before)); + digit_count -= exponent; + } + if (uint64_t(fmt & detail::basic_json_fmt)) { + // at least 1 digit in fractional part + if (has_decimal_point && exponent == 0) { + return report_parse_error(p, + parse_error::no_digits_in_fractional_part); + } + } else if (digit_count == + 0) { // we must have encountered at least one integer! + return report_parse_error(p, parse_error::no_digits_in_mantissa); + } + int64_t exp_number = 0; // explicit exponential part + if ((uint64_t(fmt & chars_format::scientific) && (p != pend) && + ((UC('e') == *p) || (UC('E') == *p))) || + (uint64_t(fmt & detail::basic_fortran_fmt) && (p != pend) && + ((UC('+') == *p) || (UC('-') == *p) || (UC('d') == *p) || + (UC('D') == *p)))) { + UC const *location_of_e = p; + if ((UC('e') == *p) || (UC('E') == *p) || (UC('d') == *p) || + (UC('D') == *p)) { + ++p; + } + bool neg_exp = false; + if ((p != pend) && (UC('-') == *p)) { + neg_exp = true; + ++p; + } else if ((p != pend) && + (UC('+') == + *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1) + ++p; + } + if ((p == pend) || !is_integer(*p)) { + if (!uint64_t(fmt & chars_format::fixed)) { + // The exponential part is invalid for scientific notation, so it must + // be a trailing token for fixed notation. However, fixed notation is + // disabled, so report a scientific notation error. + return report_parse_error(p, parse_error::missing_exponential_part); + } + // Otherwise, we will be ignoring the 'e'. + p = location_of_e; + } else { + while ((p != pend) && is_integer(*p)) { + uint8_t digit = uint8_t(*p - UC('0')); + if (exp_number < 0x10000000) { + exp_number = 10 * exp_number + digit; + } + ++p; + } + if (neg_exp) { + exp_number = -exp_number; + } + exponent += exp_number; + } + } else { + // If it scientific and not fixed, we have to bail out. + if (uint64_t(fmt & chars_format::scientific) && + !uint64_t(fmt & chars_format::fixed)) { + return report_parse_error(p, parse_error::missing_exponential_part); + } + } + answer.lastmatch = p; + answer.valid = true; + + // If we frequently had to deal with long strings of digits, + // we could extend our code by using a 128-bit integer instead + // of a 64-bit integer. However, this is uncommon. + // + // We can deal with up to 19 digits. + if (digit_count > 19) { // this is uncommon + // It is possible that the integer had an overflow. + // We have to handle the case where we have 0.0000somenumber. + // We need to be mindful of the case where we only have zeroes... + // E.g., 0.000000000...000. + UC const *start = start_digits; + while ((start != pend) && (*start == UC('0') || *start == decimal_point)) { + if (*start == UC('0')) { + digit_count--; + } + start++; + } + + if (digit_count > 19) { + answer.too_many_digits = true; + // Let us start again, this time, avoiding overflows. + // We don't need to check if is_integer, since we use the + // pre-tokenized spans from above. + i = 0; + p = answer.integer.ptr; + UC const *int_end = p + answer.integer.len(); + uint64_t const minimal_nineteen_digit_integer{1000000000000000000}; + while ((i < minimal_nineteen_digit_integer) && (p != int_end)) { + i = i * 10 + uint64_t(*p - UC('0')); + ++p; + } + if (i >= minimal_nineteen_digit_integer) { // We have a big integers + exponent = end_of_integer_part - p + exp_number; + } else { // We have a value with a fractional component. + p = answer.fraction.ptr; + UC const *frac_end = p + answer.fraction.len(); + while ((i < minimal_nineteen_digit_integer) && (p != frac_end)) { + i = i * 10 + uint64_t(*p - UC('0')); + ++p; + } + exponent = answer.fraction.ptr - p + exp_number; + } + // We have now corrected both exponent and i, to a truncated value + } + } + answer.exponent = exponent; + answer.mantissa = i; + return answer; +} + +template +fastfloat_really_inline FASTFLOAT_CONSTEXPR20 from_chars_result_t +parse_int_string(UC const *p, UC const *pend, T &value, + parse_options_t options) { + chars_format const fmt = detail::adjust_for_feature_macros(options.format); + int const base = options.base; + + from_chars_result_t answer; + + UC const *const first = p; + + bool const negative = (*p == UC('-')); +#ifdef FASTFLOAT_VISUAL_STUDIO +#pragma warning(push) +#pragma warning(disable : 4127) +#endif + if (!std::is_signed::value && negative) { +#ifdef FASTFLOAT_VISUAL_STUDIO +#pragma warning(pop) +#endif + answer.ec = std::errc::invalid_argument; + answer.ptr = first; + return answer; + } + if ((*p == UC('-')) || + (uint64_t(fmt & chars_format::allow_leading_plus) && (*p == UC('+')))) { + ++p; + } + + UC const *const start_num = p; + + while (p != pend && *p == UC('0')) { + ++p; + } + + bool const has_leading_zeros = p > start_num; + + UC const *const start_digits = p; + + uint64_t i = 0; + if (base == 10) { + loop_parse_if_eight_digits(p, pend, i); // use SIMD if possible + } + while (p != pend) { + uint8_t digit = ch_to_digit(*p); + if (digit >= base) { + break; + } + i = uint64_t(base) * i + digit; // might overflow, check this later + p++; + } + + size_t digit_count = size_t(p - start_digits); + + if (digit_count == 0) { + if (has_leading_zeros) { + value = 0; + answer.ec = std::errc(); + answer.ptr = p; + } else { + answer.ec = std::errc::invalid_argument; + answer.ptr = first; + } + return answer; + } + + answer.ptr = p; + + // check u64 overflow + size_t max_digits = max_digits_u64(base); + if (digit_count > max_digits) { + answer.ec = std::errc::result_out_of_range; + return answer; + } + // this check can be eliminated for all other types, but they will all require + // a max_digits(base) equivalent + if (digit_count == max_digits && i < min_safe_u64(base)) { + answer.ec = std::errc::result_out_of_range; + return answer; + } + + // check other types overflow + if (!std::is_same::value) { + if (i > uint64_t(std::numeric_limits::max()) + uint64_t(negative)) { + answer.ec = std::errc::result_out_of_range; + return answer; + } + } + + if (negative) { +#ifdef FASTFLOAT_VISUAL_STUDIO +#pragma warning(push) +#pragma warning(disable : 4146) +#endif + // this weird workaround is required because: + // - converting unsigned to signed when its value is greater than signed max + // is UB pre-C++23. + // - reinterpret_casting (~i + 1) would work, but it is not constexpr + // this is always optimized into a neg instruction (note: T is an integer + // type) + value = T(-std::numeric_limits::max() - + T(i - uint64_t(std::numeric_limits::max()))); +#ifdef FASTFLOAT_VISUAL_STUDIO +#pragma warning(pop) +#endif + } else { + value = T(i); + } + + answer.ec = std::errc(); + return answer; +} + +} // namespace fast_float + +#endif diff --git a/3rdparty/fast_float/bigint.h b/3rdparty/fast_float/bigint.h new file mode 100644 index 000000000000..74901e3956f2 --- /dev/null +++ b/3rdparty/fast_float/bigint.h @@ -0,0 +1,638 @@ +#ifndef FASTFLOAT_BIGINT_H +#define FASTFLOAT_BIGINT_H + +#include +#include +#include +#include + +#include "float_common.h" + +namespace fast_float { + +// the limb width: we want efficient multiplication of double the bits in +// limb, or for 64-bit limbs, at least 64-bit multiplication where we can +// extract the high and low parts efficiently. this is every 64-bit +// architecture except for sparc, which emulates 128-bit multiplication. +// we might have platforms where `CHAR_BIT` is not 8, so let's avoid +// doing `8 * sizeof(limb)`. +#if defined(FASTFLOAT_64BIT) && !defined(__sparc) +#define FASTFLOAT_64BIT_LIMB 1 +typedef uint64_t limb; +constexpr size_t limb_bits = 64; +#else +#define FASTFLOAT_32BIT_LIMB +typedef uint32_t limb; +constexpr size_t limb_bits = 32; +#endif + +typedef span limb_span; + +// number of bits in a bigint. this needs to be at least the number +// of bits required to store the largest bigint, which is +// `log2(10**(digits + max_exp))`, or `log2(10**(767 + 342))`, or +// ~3600 bits, so we round to 4000. +constexpr size_t bigint_bits = 4000; +constexpr size_t bigint_limbs = bigint_bits / limb_bits; + +// vector-like type that is allocated on the stack. the entire +// buffer is pre-allocated, and only the length changes. +template struct stackvec { + limb data[size]; + // we never need more than 150 limbs + uint16_t length{0}; + + stackvec() = default; + stackvec(stackvec const &) = delete; + stackvec &operator=(stackvec const &) = delete; + stackvec(stackvec &&) = delete; + stackvec &operator=(stackvec &&other) = delete; + + // create stack vector from existing limb span. + FASTFLOAT_CONSTEXPR20 stackvec(limb_span s) { + FASTFLOAT_ASSERT(try_extend(s)); + } + + FASTFLOAT_CONSTEXPR14 limb &operator[](size_t index) noexcept { + FASTFLOAT_DEBUG_ASSERT(index < length); + return data[index]; + } + + FASTFLOAT_CONSTEXPR14 const limb &operator[](size_t index) const noexcept { + FASTFLOAT_DEBUG_ASSERT(index < length); + return data[index]; + } + + // index from the end of the container + FASTFLOAT_CONSTEXPR14 const limb &rindex(size_t index) const noexcept { + FASTFLOAT_DEBUG_ASSERT(index < length); + size_t rindex = length - index - 1; + return data[rindex]; + } + + // set the length, without bounds checking. + FASTFLOAT_CONSTEXPR14 void set_len(size_t len) noexcept { + length = uint16_t(len); + } + + constexpr size_t len() const noexcept { return length; } + + constexpr bool is_empty() const noexcept { return length == 0; } + + constexpr size_t capacity() const noexcept { return size; } + + // append item to vector, without bounds checking + FASTFLOAT_CONSTEXPR14 void push_unchecked(limb value) noexcept { + data[length] = value; + length++; + } + + // append item to vector, returning if item was added + FASTFLOAT_CONSTEXPR14 bool try_push(limb value) noexcept { + if (len() < capacity()) { + push_unchecked(value); + return true; + } else { + return false; + } + } + + // add items to the vector, from a span, without bounds checking + FASTFLOAT_CONSTEXPR20 void extend_unchecked(limb_span s) noexcept { + limb *ptr = data + length; + std::copy_n(s.ptr, s.len(), ptr); + set_len(len() + s.len()); + } + + // try to add items to the vector, returning if items were added + FASTFLOAT_CONSTEXPR20 bool try_extend(limb_span s) noexcept { + if (len() + s.len() <= capacity()) { + extend_unchecked(s); + return true; + } else { + return false; + } + } + + // resize the vector, without bounds checking + // if the new size is longer than the vector, assign value to each + // appended item. + FASTFLOAT_CONSTEXPR20 + void resize_unchecked(size_t new_len, limb value) noexcept { + if (new_len > len()) { + size_t count = new_len - len(); + limb *first = data + len(); + limb *last = first + count; + ::std::fill(first, last, value); + set_len(new_len); + } else { + set_len(new_len); + } + } + + // try to resize the vector, returning if the vector was resized. + FASTFLOAT_CONSTEXPR20 bool try_resize(size_t new_len, limb value) noexcept { + if (new_len > capacity()) { + return false; + } else { + resize_unchecked(new_len, value); + return true; + } + } + + // check if any limbs are non-zero after the given index. + // this needs to be done in reverse order, since the index + // is relative to the most significant limbs. + FASTFLOAT_CONSTEXPR14 bool nonzero(size_t index) const noexcept { + while (index < len()) { + if (rindex(index) != 0) { + return true; + } + index++; + } + return false; + } + + // normalize the big integer, so most-significant zero limbs are removed. + FASTFLOAT_CONSTEXPR14 void normalize() noexcept { + while (len() > 0 && rindex(0) == 0) { + length--; + } + } +}; + +fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t +empty_hi64(bool &truncated) noexcept { + truncated = false; + return 0; +} + +fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t +uint64_hi64(uint64_t r0, bool &truncated) noexcept { + truncated = false; + int shl = leading_zeroes(r0); + return r0 << shl; +} + +fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t +uint64_hi64(uint64_t r0, uint64_t r1, bool &truncated) noexcept { + int shl = leading_zeroes(r0); + if (shl == 0) { + truncated = r1 != 0; + return r0; + } else { + int shr = 64 - shl; + truncated = (r1 << shl) != 0; + return (r0 << shl) | (r1 >> shr); + } +} + +fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t +uint32_hi64(uint32_t r0, bool &truncated) noexcept { + return uint64_hi64(r0, truncated); +} + +fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t +uint32_hi64(uint32_t r0, uint32_t r1, bool &truncated) noexcept { + uint64_t x0 = r0; + uint64_t x1 = r1; + return uint64_hi64((x0 << 32) | x1, truncated); +} + +fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t +uint32_hi64(uint32_t r0, uint32_t r1, uint32_t r2, bool &truncated) noexcept { + uint64_t x0 = r0; + uint64_t x1 = r1; + uint64_t x2 = r2; + return uint64_hi64(x0, (x1 << 32) | x2, truncated); +} + +// add two small integers, checking for overflow. +// we want an efficient operation. for msvc, where +// we don't have built-in intrinsics, this is still +// pretty fast. +fastfloat_really_inline FASTFLOAT_CONSTEXPR20 limb +scalar_add(limb x, limb y, bool &overflow) noexcept { + limb z; +// gcc and clang +#if defined(__has_builtin) +#if __has_builtin(__builtin_add_overflow) + if (!cpp20_and_in_constexpr()) { + overflow = __builtin_add_overflow(x, y, &z); + return z; + } +#endif +#endif + + // generic, this still optimizes correctly on MSVC. + z = x + y; + overflow = z < x; + return z; +} + +// multiply two small integers, getting both the high and low bits. +fastfloat_really_inline FASTFLOAT_CONSTEXPR20 limb +scalar_mul(limb x, limb y, limb &carry) noexcept { +#ifdef FASTFLOAT_64BIT_LIMB +#if defined(__SIZEOF_INT128__) + // GCC and clang both define it as an extension. + __uint128_t z = __uint128_t(x) * __uint128_t(y) + __uint128_t(carry); + carry = limb(z >> limb_bits); + return limb(z); +#else + // fallback, no native 128-bit integer multiplication with carry. + // on msvc, this optimizes identically, somehow. + value128 z = full_multiplication(x, y); + bool overflow; + z.low = scalar_add(z.low, carry, overflow); + z.high += uint64_t(overflow); // cannot overflow + carry = z.high; + return z.low; +#endif +#else + uint64_t z = uint64_t(x) * uint64_t(y) + uint64_t(carry); + carry = limb(z >> limb_bits); + return limb(z); +#endif +} + +// add scalar value to bigint starting from offset. +// used in grade school multiplication +template +inline FASTFLOAT_CONSTEXPR20 bool small_add_from(stackvec &vec, limb y, + size_t start) noexcept { + size_t index = start; + limb carry = y; + bool overflow; + while (carry != 0 && index < vec.len()) { + vec[index] = scalar_add(vec[index], carry, overflow); + carry = limb(overflow); + index += 1; + } + if (carry != 0) { + FASTFLOAT_TRY(vec.try_push(carry)); + } + return true; +} + +// add scalar value to bigint. +template +fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool +small_add(stackvec &vec, limb y) noexcept { + return small_add_from(vec, y, 0); +} + +// multiply bigint by scalar value. +template +inline FASTFLOAT_CONSTEXPR20 bool small_mul(stackvec &vec, + limb y) noexcept { + limb carry = 0; + for (size_t index = 0; index < vec.len(); index++) { + vec[index] = scalar_mul(vec[index], y, carry); + } + if (carry != 0) { + FASTFLOAT_TRY(vec.try_push(carry)); + } + return true; +} + +// add bigint to bigint starting from index. +// used in grade school multiplication +template +FASTFLOAT_CONSTEXPR20 bool large_add_from(stackvec &x, limb_span y, + size_t start) noexcept { + // the effective x buffer is from `xstart..x.len()`, so exit early + // if we can't get that current range. + if (x.len() < start || y.len() > x.len() - start) { + FASTFLOAT_TRY(x.try_resize(y.len() + start, 0)); + } + + bool carry = false; + for (size_t index = 0; index < y.len(); index++) { + limb xi = x[index + start]; + limb yi = y[index]; + bool c1 = false; + bool c2 = false; + xi = scalar_add(xi, yi, c1); + if (carry) { + xi = scalar_add(xi, 1, c2); + } + x[index + start] = xi; + carry = c1 | c2; + } + + // handle overflow + if (carry) { + FASTFLOAT_TRY(small_add_from(x, 1, y.len() + start)); + } + return true; +} + +// add bigint to bigint. +template +fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool +large_add_from(stackvec &x, limb_span y) noexcept { + return large_add_from(x, y, 0); +} + +// grade-school multiplication algorithm +template +FASTFLOAT_CONSTEXPR20 bool long_mul(stackvec &x, limb_span y) noexcept { + limb_span xs = limb_span(x.data, x.len()); + stackvec z(xs); + limb_span zs = limb_span(z.data, z.len()); + + if (y.len() != 0) { + limb y0 = y[0]; + FASTFLOAT_TRY(small_mul(x, y0)); + for (size_t index = 1; index < y.len(); index++) { + limb yi = y[index]; + stackvec zi; + if (yi != 0) { + // re-use the same buffer throughout + zi.set_len(0); + FASTFLOAT_TRY(zi.try_extend(zs)); + FASTFLOAT_TRY(small_mul(zi, yi)); + limb_span zis = limb_span(zi.data, zi.len()); + FASTFLOAT_TRY(large_add_from(x, zis, index)); + } + } + } + + x.normalize(); + return true; +} + +// grade-school multiplication algorithm +template +FASTFLOAT_CONSTEXPR20 bool large_mul(stackvec &x, limb_span y) noexcept { + if (y.len() == 1) { + FASTFLOAT_TRY(small_mul(x, y[0])); + } else { + FASTFLOAT_TRY(long_mul(x, y)); + } + return true; +} + +template struct pow5_tables { + static constexpr uint32_t large_step = 135; + static constexpr uint64_t small_power_of_5[] = { + 1UL, + 5UL, + 25UL, + 125UL, + 625UL, + 3125UL, + 15625UL, + 78125UL, + 390625UL, + 1953125UL, + 9765625UL, + 48828125UL, + 244140625UL, + 1220703125UL, + 6103515625UL, + 30517578125UL, + 152587890625UL, + 762939453125UL, + 3814697265625UL, + 19073486328125UL, + 95367431640625UL, + 476837158203125UL, + 2384185791015625UL, + 11920928955078125UL, + 59604644775390625UL, + 298023223876953125UL, + 1490116119384765625UL, + 7450580596923828125UL, + }; +#ifdef FASTFLOAT_64BIT_LIMB + constexpr static limb large_power_of_5[] = { + 1414648277510068013UL, 9180637584431281687UL, 4539964771860779200UL, + 10482974169319127550UL, 198276706040285095UL}; +#else + constexpr static limb large_power_of_5[] = { + 4279965485U, 329373468U, 4020270615U, 2137533757U, 4287402176U, + 1057042919U, 1071430142U, 2440757623U, 381945767U, 46164893U}; +#endif +}; + +#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE + +template constexpr uint32_t pow5_tables::large_step; + +template constexpr uint64_t pow5_tables::small_power_of_5[]; + +template constexpr limb pow5_tables::large_power_of_5[]; + +#endif + +// big integer type. implements a small subset of big integer +// arithmetic, using simple algorithms since asymptotically +// faster algorithms are slower for a small number of limbs. +// all operations assume the big-integer is normalized. +struct bigint : pow5_tables<> { + // storage of the limbs, in little-endian order. + stackvec vec; + + FASTFLOAT_CONSTEXPR20 bigint() : vec() {} + + bigint(bigint const &) = delete; + bigint &operator=(bigint const &) = delete; + bigint(bigint &&) = delete; + bigint &operator=(bigint &&other) = delete; + + FASTFLOAT_CONSTEXPR20 bigint(uint64_t value) : vec() { +#ifdef FASTFLOAT_64BIT_LIMB + vec.push_unchecked(value); +#else + vec.push_unchecked(uint32_t(value)); + vec.push_unchecked(uint32_t(value >> 32)); +#endif + vec.normalize(); + } + + // get the high 64 bits from the vector, and if bits were truncated. + // this is to get the significant digits for the float. + FASTFLOAT_CONSTEXPR20 uint64_t hi64(bool &truncated) const noexcept { +#ifdef FASTFLOAT_64BIT_LIMB + if (vec.len() == 0) { + return empty_hi64(truncated); + } else if (vec.len() == 1) { + return uint64_hi64(vec.rindex(0), truncated); + } else { + uint64_t result = uint64_hi64(vec.rindex(0), vec.rindex(1), truncated); + truncated |= vec.nonzero(2); + return result; + } +#else + if (vec.len() == 0) { + return empty_hi64(truncated); + } else if (vec.len() == 1) { + return uint32_hi64(vec.rindex(0), truncated); + } else if (vec.len() == 2) { + return uint32_hi64(vec.rindex(0), vec.rindex(1), truncated); + } else { + uint64_t result = + uint32_hi64(vec.rindex(0), vec.rindex(1), vec.rindex(2), truncated); + truncated |= vec.nonzero(3); + return result; + } +#endif + } + + // compare two big integers, returning the large value. + // assumes both are normalized. if the return value is + // negative, other is larger, if the return value is + // positive, this is larger, otherwise they are equal. + // the limbs are stored in little-endian order, so we + // must compare the limbs in ever order. + FASTFLOAT_CONSTEXPR20 int compare(bigint const &other) const noexcept { + if (vec.len() > other.vec.len()) { + return 1; + } else if (vec.len() < other.vec.len()) { + return -1; + } else { + for (size_t index = vec.len(); index > 0; index--) { + limb xi = vec[index - 1]; + limb yi = other.vec[index - 1]; + if (xi > yi) { + return 1; + } else if (xi < yi) { + return -1; + } + } + return 0; + } + } + + // shift left each limb n bits, carrying over to the new limb + // returns true if we were able to shift all the digits. + FASTFLOAT_CONSTEXPR20 bool shl_bits(size_t n) noexcept { + // Internally, for each item, we shift left by n, and add the previous + // right shifted limb-bits. + // For example, we transform (for u8) shifted left 2, to: + // b10100100 b01000010 + // b10 b10010001 b00001000 + FASTFLOAT_DEBUG_ASSERT(n != 0); + FASTFLOAT_DEBUG_ASSERT(n < sizeof(limb) * 8); + + size_t shl = n; + size_t shr = limb_bits - shl; + limb prev = 0; + for (size_t index = 0; index < vec.len(); index++) { + limb xi = vec[index]; + vec[index] = (xi << shl) | (prev >> shr); + prev = xi; + } + + limb carry = prev >> shr; + if (carry != 0) { + return vec.try_push(carry); + } + return true; + } + + // move the limbs left by `n` limbs. + FASTFLOAT_CONSTEXPR20 bool shl_limbs(size_t n) noexcept { + FASTFLOAT_DEBUG_ASSERT(n != 0); + if (n + vec.len() > vec.capacity()) { + return false; + } else if (!vec.is_empty()) { + // move limbs + limb *dst = vec.data + n; + limb const *src = vec.data; + std::copy_backward(src, src + vec.len(), dst + vec.len()); + // fill in empty limbs + limb *first = vec.data; + limb *last = first + n; + ::std::fill(first, last, 0); + vec.set_len(n + vec.len()); + return true; + } else { + return true; + } + } + + // move the limbs left by `n` bits. + FASTFLOAT_CONSTEXPR20 bool shl(size_t n) noexcept { + size_t rem = n % limb_bits; + size_t div = n / limb_bits; + if (rem != 0) { + FASTFLOAT_TRY(shl_bits(rem)); + } + if (div != 0) { + FASTFLOAT_TRY(shl_limbs(div)); + } + return true; + } + + // get the number of leading zeros in the bigint. + FASTFLOAT_CONSTEXPR20 int ctlz() const noexcept { + if (vec.is_empty()) { + return 0; + } else { +#ifdef FASTFLOAT_64BIT_LIMB + return leading_zeroes(vec.rindex(0)); +#else + // no use defining a specialized leading_zeroes for a 32-bit type. + uint64_t r0 = vec.rindex(0); + return leading_zeroes(r0 << 32); +#endif + } + } + + // get the number of bits in the bigint. + FASTFLOAT_CONSTEXPR20 int bit_length() const noexcept { + int lz = ctlz(); + return int(limb_bits * vec.len()) - lz; + } + + FASTFLOAT_CONSTEXPR20 bool mul(limb y) noexcept { return small_mul(vec, y); } + + FASTFLOAT_CONSTEXPR20 bool add(limb y) noexcept { return small_add(vec, y); } + + // multiply as if by 2 raised to a power. + FASTFLOAT_CONSTEXPR20 bool pow2(uint32_t exp) noexcept { return shl(exp); } + + // multiply as if by 5 raised to a power. + FASTFLOAT_CONSTEXPR20 bool pow5(uint32_t exp) noexcept { + // multiply by a power of 5 + size_t large_length = sizeof(large_power_of_5) / sizeof(limb); + limb_span large = limb_span(large_power_of_5, large_length); + while (exp >= large_step) { + FASTFLOAT_TRY(large_mul(vec, large)); + exp -= large_step; + } +#ifdef FASTFLOAT_64BIT_LIMB + uint32_t small_step = 27; + limb max_native = 7450580596923828125UL; +#else + uint32_t small_step = 13; + limb max_native = 1220703125U; +#endif + while (exp >= small_step) { + FASTFLOAT_TRY(small_mul(vec, max_native)); + exp -= small_step; + } + if (exp != 0) { + // Work around clang bug https://godbolt.org/z/zedh7rrhc + // This is similar to https://github.com/llvm/llvm-project/issues/47746, + // except the workaround described there don't work here + FASTFLOAT_TRY(small_mul( + vec, limb(((void)small_power_of_5[0], small_power_of_5[exp])))); + } + + return true; + } + + // multiply as if by 10 raised to a power. + FASTFLOAT_CONSTEXPR20 bool pow10(uint32_t exp) noexcept { + FASTFLOAT_TRY(pow5(exp)); + return pow2(exp); + } +}; + +} // namespace fast_float + +#endif diff --git a/3rdparty/fast_float/constexpr_feature_detect.h b/3rdparty/fast_float/constexpr_feature_detect.h new file mode 100644 index 000000000000..7624beafcacf --- /dev/null +++ b/3rdparty/fast_float/constexpr_feature_detect.h @@ -0,0 +1,46 @@ +#ifndef FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H +#define FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H + +#ifdef __has_include +#if __has_include() +#include +#endif +#endif + +// Testing for https://wg21.link/N3652, adopted in C++14 +#if __cpp_constexpr >= 201304 +#define FASTFLOAT_CONSTEXPR14 constexpr +#else +#define FASTFLOAT_CONSTEXPR14 +#endif + +#if defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L +#define FASTFLOAT_HAS_BIT_CAST 1 +#else +#define FASTFLOAT_HAS_BIT_CAST 0 +#endif + +#if defined(__cpp_lib_is_constant_evaluated) && \ + __cpp_lib_is_constant_evaluated >= 201811L +#define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 1 +#else +#define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 0 +#endif + +// Testing for relevant C++20 constexpr library features +#if FASTFLOAT_HAS_IS_CONSTANT_EVALUATED && FASTFLOAT_HAS_BIT_CAST && \ + __cpp_lib_constexpr_algorithms >= 201806L /*For std::copy and std::fill*/ +#define FASTFLOAT_CONSTEXPR20 constexpr +#define FASTFLOAT_IS_CONSTEXPR 1 +#else +#define FASTFLOAT_CONSTEXPR20 +#define FASTFLOAT_IS_CONSTEXPR 0 +#endif + +#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +#define FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE 0 +#else +#define FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE 1 +#endif + +#endif // FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H diff --git a/3rdparty/fast_float/decimal_to_binary.h b/3rdparty/fast_float/decimal_to_binary.h new file mode 100644 index 000000000000..948768265eb3 --- /dev/null +++ b/3rdparty/fast_float/decimal_to_binary.h @@ -0,0 +1,212 @@ +#ifndef FASTFLOAT_DECIMAL_TO_BINARY_H +#define FASTFLOAT_DECIMAL_TO_BINARY_H + +#include "float_common.h" +#include "fast_table.h" +#include +#include +#include +#include +#include +#include + +namespace fast_float { + +// This will compute or rather approximate w * 5**q and return a pair of 64-bit +// words approximating the result, with the "high" part corresponding to the +// most significant bits and the low part corresponding to the least significant +// bits. +// +template +fastfloat_really_inline FASTFLOAT_CONSTEXPR20 value128 +compute_product_approximation(int64_t q, uint64_t w) { + int const index = 2 * int(q - powers::smallest_power_of_five); + // For small values of q, e.g., q in [0,27], the answer is always exact + // because The line value128 firstproduct = full_multiplication(w, + // power_of_five_128[index]); gives the exact answer. + value128 firstproduct = + full_multiplication(w, powers::power_of_five_128[index]); + static_assert((bit_precision >= 0) && (bit_precision <= 64), + " precision should be in (0,64]"); + constexpr uint64_t precision_mask = + (bit_precision < 64) ? (uint64_t(0xFFFFFFFFFFFFFFFF) >> bit_precision) + : uint64_t(0xFFFFFFFFFFFFFFFF); + if ((firstproduct.high & precision_mask) == + precision_mask) { // could further guard with (lower + w < lower) + // regarding the second product, we only need secondproduct.high, but our + // expectation is that the compiler will optimize this extra work away if + // needed. + value128 secondproduct = + full_multiplication(w, powers::power_of_five_128[index + 1]); + firstproduct.low += secondproduct.high; + if (secondproduct.high > firstproduct.low) { + firstproduct.high++; + } + } + return firstproduct; +} + +namespace detail { +/** + * For q in (0,350), we have that + * f = (((152170 + 65536) * q ) >> 16); + * is equal to + * floor(p) + q + * where + * p = log(5**q)/log(2) = q * log(5)/log(2) + * + * For negative values of q in (-400,0), we have that + * f = (((152170 + 65536) * q ) >> 16); + * is equal to + * -ceil(p) + q + * where + * p = log(5**-q)/log(2) = -q * log(5)/log(2) + */ +constexpr fastfloat_really_inline int32_t power(int32_t q) noexcept { + return (((152170 + 65536) * q) >> 16) + 63; +} +} // namespace detail + +// create an adjusted mantissa, biased by the invalid power2 +// for significant digits already multiplied by 10 ** q. +template +fastfloat_really_inline FASTFLOAT_CONSTEXPR14 adjusted_mantissa +compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept { + int hilz = int(w >> 63) ^ 1; + adjusted_mantissa answer; + answer.mantissa = w << hilz; + int bias = binary::mantissa_explicit_bits() - binary::minimum_exponent(); + answer.power2 = int32_t(detail::power(int32_t(q)) + bias - hilz - lz - 62 + + invalid_am_bias); + return answer; +} + +// w * 10 ** q, without rounding the representation up. +// the power2 in the exponent will be adjusted by invalid_am_bias. +template +fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa +compute_error(int64_t q, uint64_t w) noexcept { + int lz = leading_zeroes(w); + w <<= lz; + value128 product = + compute_product_approximation(q, w); + return compute_error_scaled(q, product.high, lz); +} + +// Computers w * 10 ** q. +// The returned value should be a valid number that simply needs to be +// packed. However, in some very rare cases, the computation will fail. In such +// cases, we return an adjusted_mantissa with a negative power of 2: the caller +// should recompute in such cases. +template +fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa +compute_float(int64_t q, uint64_t w) noexcept { + adjusted_mantissa answer; + if ((w == 0) || (q < binary::smallest_power_of_ten())) { + answer.power2 = 0; + answer.mantissa = 0; + // result should be zero + return answer; + } + if (q > binary::largest_power_of_ten()) { + // we want to get infinity: + answer.power2 = binary::infinite_power(); + answer.mantissa = 0; + return answer; + } + // At this point in time q is in [powers::smallest_power_of_five, + // powers::largest_power_of_five]. + + // We want the most significant bit of i to be 1. Shift if needed. + int lz = leading_zeroes(w); + w <<= lz; + + // The required precision is binary::mantissa_explicit_bits() + 3 because + // 1. We need the implicit bit + // 2. We need an extra bit for rounding purposes + // 3. We might lose a bit due to the "upperbit" routine (result too small, + // requiring a shift) + + value128 product = + compute_product_approximation(q, w); + // The computed 'product' is always sufficient. + // Mathematical proof: + // Noble Mushtak and Daniel Lemire, Fast Number Parsing Without Fallback (to + // appear) See script/mushtak_lemire.py + + // The "compute_product_approximation" function can be slightly slower than a + // branchless approach: value128 product = compute_product(q, w); but in + // practice, we can win big with the compute_product_approximation if its + // additional branch is easily predicted. Which is best is data specific. + int upperbit = int(product.high >> 63); + int shift = upperbit + 64 - binary::mantissa_explicit_bits() - 3; + + answer.mantissa = product.high >> shift; + + answer.power2 = int32_t(detail::power(int32_t(q)) + upperbit - lz - + binary::minimum_exponent()); + if (answer.power2 <= 0) { // we have a subnormal? + // Here have that answer.power2 <= 0 so -answer.power2 >= 0 + if (-answer.power2 + 1 >= + 64) { // if we have more than 64 bits below the minimum exponent, you + // have a zero for sure. + answer.power2 = 0; + answer.mantissa = 0; + // result should be zero + return answer; + } + // next line is safe because -answer.power2 + 1 < 64 + answer.mantissa >>= -answer.power2 + 1; + // Thankfully, we can't have both "round-to-even" and subnormals because + // "round-to-even" only occurs for powers close to 0 in the 32-bit and + // and 64-bit case (with no more than 19 digits). + answer.mantissa += (answer.mantissa & 1); // round up + answer.mantissa >>= 1; + // There is a weird scenario where we don't have a subnormal but just. + // Suppose we start with 2.2250738585072013e-308, we end up + // with 0x3fffffffffffff x 2^-1023-53 which is technically subnormal + // whereas 0x40000000000000 x 2^-1023-53 is normal. Now, we need to round + // up 0x3fffffffffffff x 2^-1023-53 and once we do, we are no longer + // subnormal, but we can only know this after rounding. + // So we only declare a subnormal if we are smaller than the threshold. + answer.power2 = + (answer.mantissa < (uint64_t(1) << binary::mantissa_explicit_bits())) + ? 0 + : 1; + return answer; + } + + // usually, we round *up*, but if we fall right in between and and we have an + // even basis, we need to round down + // We are only concerned with the cases where 5**q fits in single 64-bit word. + if ((product.low <= 1) && (q >= binary::min_exponent_round_to_even()) && + (q <= binary::max_exponent_round_to_even()) && + ((answer.mantissa & 3) == 1)) { // we may fall between two floats! + // To be in-between two floats we need that in doing + // answer.mantissa = product.high >> (upperbit + 64 - + // binary::mantissa_explicit_bits() - 3); + // ... we dropped out only zeroes. But if this happened, then we can go + // back!!! + if ((answer.mantissa << shift) == product.high) { + answer.mantissa &= ~uint64_t(1); // flip it so that we do not round up + } + } + + answer.mantissa += (answer.mantissa & 1); // round up + answer.mantissa >>= 1; + if (answer.mantissa >= (uint64_t(2) << binary::mantissa_explicit_bits())) { + answer.mantissa = (uint64_t(1) << binary::mantissa_explicit_bits()); + answer.power2++; // undo previous addition + } + + answer.mantissa &= ~(uint64_t(1) << binary::mantissa_explicit_bits()); + if (answer.power2 >= binary::infinite_power()) { // infinity + answer.power2 = binary::infinite_power(); + answer.mantissa = 0; + } + return answer; +} + +} // namespace fast_float + +#endif diff --git a/3rdparty/fast_float/digit_comparison.h b/3rdparty/fast_float/digit_comparison.h new file mode 100644 index 000000000000..d7ef3d9acc16 --- /dev/null +++ b/3rdparty/fast_float/digit_comparison.h @@ -0,0 +1,457 @@ +#ifndef FASTFLOAT_DIGIT_COMPARISON_H +#define FASTFLOAT_DIGIT_COMPARISON_H + +#include +#include +#include +#include + +#include "float_common.h" +#include "bigint.h" +#include "ascii_number.h" + +namespace fast_float { + +// 1e0 to 1e19 +constexpr static uint64_t powers_of_ten_uint64[] = {1UL, + 10UL, + 100UL, + 1000UL, + 10000UL, + 100000UL, + 1000000UL, + 10000000UL, + 100000000UL, + 1000000000UL, + 10000000000UL, + 100000000000UL, + 1000000000000UL, + 10000000000000UL, + 100000000000000UL, + 1000000000000000UL, + 10000000000000000UL, + 100000000000000000UL, + 1000000000000000000UL, + 10000000000000000000UL}; + +// calculate the exponent, in scientific notation, of the number. +// this algorithm is not even close to optimized, but it has no practical +// effect on performance: in order to have a faster algorithm, we'd need +// to slow down performance for faster algorithms, and this is still fast. +template +fastfloat_really_inline FASTFLOAT_CONSTEXPR14 int32_t +scientific_exponent(parsed_number_string_t &num) noexcept { + uint64_t mantissa = num.mantissa; + int32_t exponent = int32_t(num.exponent); + while (mantissa >= 10000) { + mantissa /= 10000; + exponent += 4; + } + while (mantissa >= 100) { + mantissa /= 100; + exponent += 2; + } + while (mantissa >= 10) { + mantissa /= 10; + exponent += 1; + } + return exponent; +} + +// this converts a native floating-point number to an extended-precision float. +template +fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa +to_extended(T value) noexcept { + using equiv_uint = equiv_uint_t; + constexpr equiv_uint exponent_mask = binary_format::exponent_mask(); + constexpr equiv_uint mantissa_mask = binary_format::mantissa_mask(); + constexpr equiv_uint hidden_bit_mask = binary_format::hidden_bit_mask(); + + adjusted_mantissa am; + int32_t bias = binary_format::mantissa_explicit_bits() - + binary_format::minimum_exponent(); + equiv_uint bits; +#if FASTFLOAT_HAS_BIT_CAST + bits = std::bit_cast(value); +#else + ::memcpy(&bits, &value, sizeof(T)); +#endif + if ((bits & exponent_mask) == 0) { + // denormal + am.power2 = 1 - bias; + am.mantissa = bits & mantissa_mask; + } else { + // normal + am.power2 = int32_t((bits & exponent_mask) >> + binary_format::mantissa_explicit_bits()); + am.power2 -= bias; + am.mantissa = (bits & mantissa_mask) | hidden_bit_mask; + } + + return am; +} + +// get the extended precision value of the halfway point between b and b+u. +// we are given a native float that represents b, so we need to adjust it +// halfway between b and b+u. +template +fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa +to_extended_halfway(T value) noexcept { + adjusted_mantissa am = to_extended(value); + am.mantissa <<= 1; + am.mantissa += 1; + am.power2 -= 1; + return am; +} + +// round an extended-precision float to the nearest machine float. +template +fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void round(adjusted_mantissa &am, + callback cb) noexcept { + int32_t mantissa_shift = 64 - binary_format::mantissa_explicit_bits() - 1; + if (-am.power2 >= mantissa_shift) { + // have a denormal float + int32_t shift = -am.power2 + 1; + cb(am, std::min(shift, 64)); + // check for round-up: if rounding-nearest carried us to the hidden bit. + am.power2 = (am.mantissa < + (uint64_t(1) << binary_format::mantissa_explicit_bits())) + ? 0 + : 1; + return; + } + + // have a normal float, use the default shift. + cb(am, mantissa_shift); + + // check for carry + if (am.mantissa >= + (uint64_t(2) << binary_format::mantissa_explicit_bits())) { + am.mantissa = (uint64_t(1) << binary_format::mantissa_explicit_bits()); + am.power2++; + } + + // check for infinite: we could have carried to an infinite power + am.mantissa &= ~(uint64_t(1) << binary_format::mantissa_explicit_bits()); + if (am.power2 >= binary_format::infinite_power()) { + am.power2 = binary_format::infinite_power(); + am.mantissa = 0; + } +} + +template +fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void +round_nearest_tie_even(adjusted_mantissa &am, int32_t shift, + callback cb) noexcept { + uint64_t const mask = (shift == 64) ? UINT64_MAX : (uint64_t(1) << shift) - 1; + uint64_t const halfway = (shift == 0) ? 0 : uint64_t(1) << (shift - 1); + uint64_t truncated_bits = am.mantissa & mask; + bool is_above = truncated_bits > halfway; + bool is_halfway = truncated_bits == halfway; + + // shift digits into position + if (shift == 64) { + am.mantissa = 0; + } else { + am.mantissa >>= shift; + } + am.power2 += shift; + + bool is_odd = (am.mantissa & 1) == 1; + am.mantissa += uint64_t(cb(is_odd, is_halfway, is_above)); +} + +fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void +round_down(adjusted_mantissa &am, int32_t shift) noexcept { + if (shift == 64) { + am.mantissa = 0; + } else { + am.mantissa >>= shift; + } + am.power2 += shift; +} + +template +fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void +skip_zeros(UC const *&first, UC const *last) noexcept { + uint64_t val; + while (!cpp20_and_in_constexpr() && + std::distance(first, last) >= int_cmp_len()) { + ::memcpy(&val, first, sizeof(uint64_t)); + if (val != int_cmp_zeros()) { + break; + } + first += int_cmp_len(); + } + while (first != last) { + if (*first != UC('0')) { + break; + } + first++; + } +} + +// determine if any non-zero digits were truncated. +// all characters must be valid digits. +template +fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool +is_truncated(UC const *first, UC const *last) noexcept { + // do 8-bit optimizations, can just compare to 8 literal 0s. + uint64_t val; + while (!cpp20_and_in_constexpr() && + std::distance(first, last) >= int_cmp_len()) { + ::memcpy(&val, first, sizeof(uint64_t)); + if (val != int_cmp_zeros()) { + return true; + } + first += int_cmp_len(); + } + while (first != last) { + if (*first != UC('0')) { + return true; + } + ++first; + } + return false; +} + +template +fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool +is_truncated(span s) noexcept { + return is_truncated(s.ptr, s.ptr + s.len()); +} + +template +fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void +parse_eight_digits(UC const *&p, limb &value, size_t &counter, + size_t &count) noexcept { + value = value * 100000000 + parse_eight_digits_unrolled(p); + p += 8; + counter += 8; + count += 8; +} + +template +fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void +parse_one_digit(UC const *&p, limb &value, size_t &counter, + size_t &count) noexcept { + value = value * 10 + limb(*p - UC('0')); + p++; + counter++; + count++; +} + +fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void +add_native(bigint &big, limb power, limb value) noexcept { + big.mul(power); + big.add(value); +} + +fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void +round_up_bigint(bigint &big, size_t &count) noexcept { + // need to round-up the digits, but need to avoid rounding + // ....9999 to ...10000, which could cause a false halfway point. + add_native(big, 10, 1); + count++; +} + +// parse the significant digits into a big integer +template +inline FASTFLOAT_CONSTEXPR20 void +parse_mantissa(bigint &result, parsed_number_string_t &num, + size_t max_digits, size_t &digits) noexcept { + // try to minimize the number of big integer and scalar multiplication. + // therefore, try to parse 8 digits at a time, and multiply by the largest + // scalar value (9 or 19 digits) for each step. + size_t counter = 0; + digits = 0; + limb value = 0; +#ifdef FASTFLOAT_64BIT_LIMB + size_t step = 19; +#else + size_t step = 9; +#endif + + // process all integer digits. + UC const *p = num.integer.ptr; + UC const *pend = p + num.integer.len(); + skip_zeros(p, pend); + // process all digits, in increments of step per loop + while (p != pend) { + while ((std::distance(p, pend) >= 8) && (step - counter >= 8) && + (max_digits - digits >= 8)) { + parse_eight_digits(p, value, counter, digits); + } + while (counter < step && p != pend && digits < max_digits) { + parse_one_digit(p, value, counter, digits); + } + if (digits == max_digits) { + // add the temporary value, then check if we've truncated any digits + add_native(result, limb(powers_of_ten_uint64[counter]), value); + bool truncated = is_truncated(p, pend); + if (num.fraction.ptr != nullptr) { + truncated |= is_truncated(num.fraction); + } + if (truncated) { + round_up_bigint(result, digits); + } + return; + } else { + add_native(result, limb(powers_of_ten_uint64[counter]), value); + counter = 0; + value = 0; + } + } + + // add our fraction digits, if they're available. + if (num.fraction.ptr != nullptr) { + p = num.fraction.ptr; + pend = p + num.fraction.len(); + if (digits == 0) { + skip_zeros(p, pend); + } + // process all digits, in increments of step per loop + while (p != pend) { + while ((std::distance(p, pend) >= 8) && (step - counter >= 8) && + (max_digits - digits >= 8)) { + parse_eight_digits(p, value, counter, digits); + } + while (counter < step && p != pend && digits < max_digits) { + parse_one_digit(p, value, counter, digits); + } + if (digits == max_digits) { + // add the temporary value, then check if we've truncated any digits + add_native(result, limb(powers_of_ten_uint64[counter]), value); + bool truncated = is_truncated(p, pend); + if (truncated) { + round_up_bigint(result, digits); + } + return; + } else { + add_native(result, limb(powers_of_ten_uint64[counter]), value); + counter = 0; + value = 0; + } + } + } + + if (counter != 0) { + add_native(result, limb(powers_of_ten_uint64[counter]), value); + } +} + +template +inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa +positive_digit_comp(bigint &bigmant, int32_t exponent) noexcept { + FASTFLOAT_ASSERT(bigmant.pow10(uint32_t(exponent))); + adjusted_mantissa answer; + bool truncated; + answer.mantissa = bigmant.hi64(truncated); + int bias = binary_format::mantissa_explicit_bits() - + binary_format::minimum_exponent(); + answer.power2 = bigmant.bit_length() - 64 + bias; + + round(answer, [truncated](adjusted_mantissa &a, int32_t shift) { + round_nearest_tie_even( + a, shift, + [truncated](bool is_odd, bool is_halfway, bool is_above) -> bool { + return is_above || (is_halfway && truncated) || + (is_odd && is_halfway); + }); + }); + + return answer; +} + +// the scaling here is quite simple: we have, for the real digits `m * 10^e`, +// and for the theoretical digits `n * 2^f`. Since `e` is always negative, +// to scale them identically, we do `n * 2^f * 5^-f`, so we now have `m * 2^e`. +// we then need to scale by `2^(f- e)`, and then the two significant digits +// are of the same magnitude. +template +inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp( + bigint &bigmant, adjusted_mantissa am, int32_t exponent) noexcept { + bigint &real_digits = bigmant; + int32_t real_exp = exponent; + + // get the value of `b`, rounded down, and get a bigint representation of b+h + adjusted_mantissa am_b = am; + // gcc7 buf: use a lambda to remove the noexcept qualifier bug with + // -Wnoexcept-type. + round(am_b, + [](adjusted_mantissa &a, int32_t shift) { round_down(a, shift); }); + T b; + to_float(false, am_b, b); + adjusted_mantissa theor = to_extended_halfway(b); + bigint theor_digits(theor.mantissa); + int32_t theor_exp = theor.power2; + + // scale real digits and theor digits to be same power. + int32_t pow2_exp = theor_exp - real_exp; + uint32_t pow5_exp = uint32_t(-real_exp); + if (pow5_exp != 0) { + FASTFLOAT_ASSERT(theor_digits.pow5(pow5_exp)); + } + if (pow2_exp > 0) { + FASTFLOAT_ASSERT(theor_digits.pow2(uint32_t(pow2_exp))); + } else if (pow2_exp < 0) { + FASTFLOAT_ASSERT(real_digits.pow2(uint32_t(-pow2_exp))); + } + + // compare digits, and use it to director rounding + int ord = real_digits.compare(theor_digits); + adjusted_mantissa answer = am; + round(answer, [ord](adjusted_mantissa &a, int32_t shift) { + round_nearest_tie_even( + a, shift, [ord](bool is_odd, bool _, bool __) -> bool { + (void)_; // not needed, since we've done our comparison + (void)__; // not needed, since we've done our comparison + if (ord > 0) { + return true; + } else if (ord < 0) { + return false; + } else { + return is_odd; + } + }); + }); + + return answer; +} + +// parse the significant digits as a big integer to unambiguously round the +// the significant digits. here, we are trying to determine how to round +// an extended float representation close to `b+h`, halfway between `b` +// (the float rounded-down) and `b+u`, the next positive float. this +// algorithm is always correct, and uses one of two approaches. when +// the exponent is positive relative to the significant digits (such as +// 1234), we create a big-integer representation, get the high 64-bits, +// determine if any lower bits are truncated, and use that to direct +// rounding. in case of a negative exponent relative to the significant +// digits (such as 1.2345), we create a theoretical representation of +// `b` as a big-integer type, scaled to the same binary exponent as +// the actual digits. we then compare the big integer representations +// of both, and use that to direct rounding. +template +inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa +digit_comp(parsed_number_string_t &num, adjusted_mantissa am) noexcept { + // remove the invalid exponent bias + am.power2 -= invalid_am_bias; + + int32_t sci_exp = scientific_exponent(num); + size_t max_digits = binary_format::max_digits(); + size_t digits = 0; + bigint bigmant; + parse_mantissa(bigmant, num, max_digits, digits); + // can't underflow, since digits is at most max_digits. + int32_t exponent = sci_exp + 1 - int32_t(digits); + if (exponent >= 0) { + return positive_digit_comp(bigmant, exponent); + } else { + return negative_digit_comp(bigmant, am, exponent); + } +} + +} // namespace fast_float + +#endif diff --git a/3rdparty/fast_float/fast_float.h b/3rdparty/fast_float/fast_float.h new file mode 100644 index 000000000000..af65c96bde37 --- /dev/null +++ b/3rdparty/fast_float/fast_float.h @@ -0,0 +1,59 @@ + +#ifndef FASTFLOAT_FAST_FLOAT_H +#define FASTFLOAT_FAST_FLOAT_H + +#include "float_common.h" + +namespace fast_float { +/** + * This function parses the character sequence [first,last) for a number. It + * parses floating-point numbers expecting a locale-indepent format equivalent + * to what is used by std::strtod in the default ("C") locale. The resulting + * floating-point value is the closest floating-point values (using either float + * or double), using the "round to even" convention for values that would + * otherwise fall right in-between two values. That is, we provide exact parsing + * according to the IEEE standard. + * + * Given a successful parse, the pointer (`ptr`) in the returned value is set to + * point right after the parsed number, and the `value` referenced is set to the + * parsed value. In case of error, the returned `ec` contains a representative + * error, otherwise the default (`std::errc()`) value is stored. + * + * The implementation does not throw and does not allocate memory (e.g., with + * `new` or `malloc`). + * + * Like the C++17 standard, the `fast_float::from_chars` functions take an + * optional last argument of the type `fast_float::chars_format`. It is a bitset + * value: we check whether `fmt & fast_float::chars_format::fixed` and `fmt & + * fast_float::chars_format::scientific` are set to determine whether we allow + * the fixed point and scientific notation respectively. The default is + * `fast_float::chars_format::general` which allows both `fixed` and + * `scientific`. + */ +template ::value)> +FASTFLOAT_CONSTEXPR20 from_chars_result_t +from_chars(UC const *first, UC const *last, T &value, + chars_format fmt = chars_format::general) noexcept; + +/** + * Like from_chars, but accepts an `options` argument to govern number parsing. + * Both for floating-point types and integer types. + */ +template +FASTFLOAT_CONSTEXPR20 from_chars_result_t +from_chars_advanced(UC const *first, UC const *last, T &value, + parse_options_t options) noexcept; + +/** + * from_chars for integer types. + */ +template ::value)> +FASTFLOAT_CONSTEXPR20 from_chars_result_t +from_chars(UC const *first, UC const *last, T &value, int base = 10) noexcept; + +} // namespace fast_float + +#include "parse_number.h" +#endif // FASTFLOAT_FAST_FLOAT_H diff --git a/3rdparty/fast_float/fast_table.h b/3rdparty/fast_float/fast_table.h new file mode 100644 index 000000000000..69f9b2c9245f --- /dev/null +++ b/3rdparty/fast_float/fast_table.h @@ -0,0 +1,708 @@ +#ifndef FASTFLOAT_FAST_TABLE_H +#define FASTFLOAT_FAST_TABLE_H + +#include + +namespace fast_float { + +/** + * When mapping numbers from decimal to binary, + * we go from w * 10^q to m * 2^p but we have + * 10^q = 5^q * 2^q, so effectively + * we are trying to match + * w * 2^q * 5^q to m * 2^p. Thus the powers of two + * are not a concern since they can be represented + * exactly using the binary notation, only the powers of five + * affect the binary significand. + */ + +/** + * The smallest non-zero float (binary64) is 2^-1074. + * We take as input numbers of the form w x 10^q where w < 2^64. + * We have that w * 10^-343 < 2^(64-344) 5^-343 < 2^-1076. + * However, we have that + * (2^64-1) * 10^-342 = (2^64-1) * 2^-342 * 5^-342 > 2^-1074. + * Thus it is possible for a number of the form w * 10^-342 where + * w is a 64-bit value to be a non-zero floating-point number. + ********* + * Any number of form w * 10^309 where w>= 1 is going to be + * infinite in binary64 so we never need to worry about powers + * of 5 greater than 308. + */ +template struct powers_template { + + constexpr static int smallest_power_of_five = + binary_format::smallest_power_of_ten(); + constexpr static int largest_power_of_five = + binary_format::largest_power_of_ten(); + constexpr static int number_of_entries = + 2 * (largest_power_of_five - smallest_power_of_five + 1); + // Powers of five from 5^-342 all the way to 5^308 rounded toward one. + constexpr static uint64_t power_of_five_128[number_of_entries] = { + 0xeef453d6923bd65a, 0x113faa2906a13b3f, + 0x9558b4661b6565f8, 0x4ac7ca59a424c507, + 0xbaaee17fa23ebf76, 0x5d79bcf00d2df649, + 0xe95a99df8ace6f53, 0xf4d82c2c107973dc, + 0x91d8a02bb6c10594, 0x79071b9b8a4be869, + 0xb64ec836a47146f9, 0x9748e2826cdee284, + 0xe3e27a444d8d98b7, 0xfd1b1b2308169b25, + 0x8e6d8c6ab0787f72, 0xfe30f0f5e50e20f7, + 0xb208ef855c969f4f, 0xbdbd2d335e51a935, + 0xde8b2b66b3bc4723, 0xad2c788035e61382, + 0x8b16fb203055ac76, 0x4c3bcb5021afcc31, + 0xaddcb9e83c6b1793, 0xdf4abe242a1bbf3d, + 0xd953e8624b85dd78, 0xd71d6dad34a2af0d, + 0x87d4713d6f33aa6b, 0x8672648c40e5ad68, + 0xa9c98d8ccb009506, 0x680efdaf511f18c2, + 0xd43bf0effdc0ba48, 0x212bd1b2566def2, + 0x84a57695fe98746d, 0x14bb630f7604b57, + 0xa5ced43b7e3e9188, 0x419ea3bd35385e2d, + 0xcf42894a5dce35ea, 0x52064cac828675b9, + 0x818995ce7aa0e1b2, 0x7343efebd1940993, + 0xa1ebfb4219491a1f, 0x1014ebe6c5f90bf8, + 0xca66fa129f9b60a6, 0xd41a26e077774ef6, + 0xfd00b897478238d0, 0x8920b098955522b4, + 0x9e20735e8cb16382, 0x55b46e5f5d5535b0, + 0xc5a890362fddbc62, 0xeb2189f734aa831d, + 0xf712b443bbd52b7b, 0xa5e9ec7501d523e4, + 0x9a6bb0aa55653b2d, 0x47b233c92125366e, + 0xc1069cd4eabe89f8, 0x999ec0bb696e840a, + 0xf148440a256e2c76, 0xc00670ea43ca250d, + 0x96cd2a865764dbca, 0x380406926a5e5728, + 0xbc807527ed3e12bc, 0xc605083704f5ecf2, + 0xeba09271e88d976b, 0xf7864a44c633682e, + 0x93445b8731587ea3, 0x7ab3ee6afbe0211d, + 0xb8157268fdae9e4c, 0x5960ea05bad82964, + 0xe61acf033d1a45df, 0x6fb92487298e33bd, + 0x8fd0c16206306bab, 0xa5d3b6d479f8e056, + 0xb3c4f1ba87bc8696, 0x8f48a4899877186c, + 0xe0b62e2929aba83c, 0x331acdabfe94de87, + 0x8c71dcd9ba0b4925, 0x9ff0c08b7f1d0b14, + 0xaf8e5410288e1b6f, 0x7ecf0ae5ee44dd9, + 0xdb71e91432b1a24a, 0xc9e82cd9f69d6150, + 0x892731ac9faf056e, 0xbe311c083a225cd2, + 0xab70fe17c79ac6ca, 0x6dbd630a48aaf406, + 0xd64d3d9db981787d, 0x92cbbccdad5b108, + 0x85f0468293f0eb4e, 0x25bbf56008c58ea5, + 0xa76c582338ed2621, 0xaf2af2b80af6f24e, + 0xd1476e2c07286faa, 0x1af5af660db4aee1, + 0x82cca4db847945ca, 0x50d98d9fc890ed4d, + 0xa37fce126597973c, 0xe50ff107bab528a0, + 0xcc5fc196fefd7d0c, 0x1e53ed49a96272c8, + 0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7a, + 0x9faacf3df73609b1, 0x77b191618c54e9ac, + 0xc795830d75038c1d, 0xd59df5b9ef6a2417, + 0xf97ae3d0d2446f25, 0x4b0573286b44ad1d, + 0x9becce62836ac577, 0x4ee367f9430aec32, + 0xc2e801fb244576d5, 0x229c41f793cda73f, + 0xf3a20279ed56d48a, 0x6b43527578c1110f, + 0x9845418c345644d6, 0x830a13896b78aaa9, + 0xbe5691ef416bd60c, 0x23cc986bc656d553, + 0xedec366b11c6cb8f, 0x2cbfbe86b7ec8aa8, + 0x94b3a202eb1c3f39, 0x7bf7d71432f3d6a9, + 0xb9e08a83a5e34f07, 0xdaf5ccd93fb0cc53, + 0xe858ad248f5c22c9, 0xd1b3400f8f9cff68, + 0x91376c36d99995be, 0x23100809b9c21fa1, + 0xb58547448ffffb2d, 0xabd40a0c2832a78a, + 0xe2e69915b3fff9f9, 0x16c90c8f323f516c, + 0x8dd01fad907ffc3b, 0xae3da7d97f6792e3, + 0xb1442798f49ffb4a, 0x99cd11cfdf41779c, + 0xdd95317f31c7fa1d, 0x40405643d711d583, + 0x8a7d3eef7f1cfc52, 0x482835ea666b2572, + 0xad1c8eab5ee43b66, 0xda3243650005eecf, + 0xd863b256369d4a40, 0x90bed43e40076a82, + 0x873e4f75e2224e68, 0x5a7744a6e804a291, + 0xa90de3535aaae202, 0x711515d0a205cb36, + 0xd3515c2831559a83, 0xd5a5b44ca873e03, + 0x8412d9991ed58091, 0xe858790afe9486c2, + 0xa5178fff668ae0b6, 0x626e974dbe39a872, + 0xce5d73ff402d98e3, 0xfb0a3d212dc8128f, + 0x80fa687f881c7f8e, 0x7ce66634bc9d0b99, + 0xa139029f6a239f72, 0x1c1fffc1ebc44e80, + 0xc987434744ac874e, 0xa327ffb266b56220, + 0xfbe9141915d7a922, 0x4bf1ff9f0062baa8, + 0x9d71ac8fada6c9b5, 0x6f773fc3603db4a9, + 0xc4ce17b399107c22, 0xcb550fb4384d21d3, + 0xf6019da07f549b2b, 0x7e2a53a146606a48, + 0x99c102844f94e0fb, 0x2eda7444cbfc426d, + 0xc0314325637a1939, 0xfa911155fefb5308, + 0xf03d93eebc589f88, 0x793555ab7eba27ca, + 0x96267c7535b763b5, 0x4bc1558b2f3458de, + 0xbbb01b9283253ca2, 0x9eb1aaedfb016f16, + 0xea9c227723ee8bcb, 0x465e15a979c1cadc, + 0x92a1958a7675175f, 0xbfacd89ec191ec9, + 0xb749faed14125d36, 0xcef980ec671f667b, + 0xe51c79a85916f484, 0x82b7e12780e7401a, + 0x8f31cc0937ae58d2, 0xd1b2ecb8b0908810, + 0xb2fe3f0b8599ef07, 0x861fa7e6dcb4aa15, + 0xdfbdcece67006ac9, 0x67a791e093e1d49a, + 0x8bd6a141006042bd, 0xe0c8bb2c5c6d24e0, + 0xaecc49914078536d, 0x58fae9f773886e18, + 0xda7f5bf590966848, 0xaf39a475506a899e, + 0x888f99797a5e012d, 0x6d8406c952429603, + 0xaab37fd7d8f58178, 0xc8e5087ba6d33b83, + 0xd5605fcdcf32e1d6, 0xfb1e4a9a90880a64, + 0x855c3be0a17fcd26, 0x5cf2eea09a55067f, + 0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481e, + 0xd0601d8efc57b08b, 0xf13b94daf124da26, + 0x823c12795db6ce57, 0x76c53d08d6b70858, + 0xa2cb1717b52481ed, 0x54768c4b0c64ca6e, + 0xcb7ddcdda26da268, 0xa9942f5dcf7dfd09, + 0xfe5d54150b090b02, 0xd3f93b35435d7c4c, + 0x9efa548d26e5a6e1, 0xc47bc5014a1a6daf, + 0xc6b8e9b0709f109a, 0x359ab6419ca1091b, + 0xf867241c8cc6d4c0, 0xc30163d203c94b62, + 0x9b407691d7fc44f8, 0x79e0de63425dcf1d, + 0xc21094364dfb5636, 0x985915fc12f542e4, + 0xf294b943e17a2bc4, 0x3e6f5b7b17b2939d, + 0x979cf3ca6cec5b5a, 0xa705992ceecf9c42, + 0xbd8430bd08277231, 0x50c6ff782a838353, + 0xece53cec4a314ebd, 0xa4f8bf5635246428, + 0x940f4613ae5ed136, 0x871b7795e136be99, + 0xb913179899f68584, 0x28e2557b59846e3f, + 0xe757dd7ec07426e5, 0x331aeada2fe589cf, + 0x9096ea6f3848984f, 0x3ff0d2c85def7621, + 0xb4bca50b065abe63, 0xfed077a756b53a9, + 0xe1ebce4dc7f16dfb, 0xd3e8495912c62894, + 0x8d3360f09cf6e4bd, 0x64712dd7abbbd95c, + 0xb080392cc4349dec, 0xbd8d794d96aacfb3, + 0xdca04777f541c567, 0xecf0d7a0fc5583a0, + 0x89e42caaf9491b60, 0xf41686c49db57244, + 0xac5d37d5b79b6239, 0x311c2875c522ced5, + 0xd77485cb25823ac7, 0x7d633293366b828b, + 0x86a8d39ef77164bc, 0xae5dff9c02033197, + 0xa8530886b54dbdeb, 0xd9f57f830283fdfc, + 0xd267caa862a12d66, 0xd072df63c324fd7b, + 0x8380dea93da4bc60, 0x4247cb9e59f71e6d, + 0xa46116538d0deb78, 0x52d9be85f074e608, + 0xcd795be870516656, 0x67902e276c921f8b, + 0x806bd9714632dff6, 0xba1cd8a3db53b6, + 0xa086cfcd97bf97f3, 0x80e8a40eccd228a4, + 0xc8a883c0fdaf7df0, 0x6122cd128006b2cd, + 0xfad2a4b13d1b5d6c, 0x796b805720085f81, + 0x9cc3a6eec6311a63, 0xcbe3303674053bb0, + 0xc3f490aa77bd60fc, 0xbedbfc4411068a9c, + 0xf4f1b4d515acb93b, 0xee92fb5515482d44, + 0x991711052d8bf3c5, 0x751bdd152d4d1c4a, + 0xbf5cd54678eef0b6, 0xd262d45a78a0635d, + 0xef340a98172aace4, 0x86fb897116c87c34, + 0x9580869f0e7aac0e, 0xd45d35e6ae3d4da0, + 0xbae0a846d2195712, 0x8974836059cca109, + 0xe998d258869facd7, 0x2bd1a438703fc94b, + 0x91ff83775423cc06, 0x7b6306a34627ddcf, + 0xb67f6455292cbf08, 0x1a3bc84c17b1d542, + 0xe41f3d6a7377eeca, 0x20caba5f1d9e4a93, + 0x8e938662882af53e, 0x547eb47b7282ee9c, + 0xb23867fb2a35b28d, 0xe99e619a4f23aa43, + 0xdec681f9f4c31f31, 0x6405fa00e2ec94d4, + 0x8b3c113c38f9f37e, 0xde83bc408dd3dd04, + 0xae0b158b4738705e, 0x9624ab50b148d445, + 0xd98ddaee19068c76, 0x3badd624dd9b0957, + 0x87f8a8d4cfa417c9, 0xe54ca5d70a80e5d6, + 0xa9f6d30a038d1dbc, 0x5e9fcf4ccd211f4c, + 0xd47487cc8470652b, 0x7647c3200069671f, + 0x84c8d4dfd2c63f3b, 0x29ecd9f40041e073, + 0xa5fb0a17c777cf09, 0xf468107100525890, + 0xcf79cc9db955c2cc, 0x7182148d4066eeb4, + 0x81ac1fe293d599bf, 0xc6f14cd848405530, + 0xa21727db38cb002f, 0xb8ada00e5a506a7c, + 0xca9cf1d206fdc03b, 0xa6d90811f0e4851c, + 0xfd442e4688bd304a, 0x908f4a166d1da663, + 0x9e4a9cec15763e2e, 0x9a598e4e043287fe, + 0xc5dd44271ad3cdba, 0x40eff1e1853f29fd, + 0xf7549530e188c128, 0xd12bee59e68ef47c, + 0x9a94dd3e8cf578b9, 0x82bb74f8301958ce, + 0xc13a148e3032d6e7, 0xe36a52363c1faf01, + 0xf18899b1bc3f8ca1, 0xdc44e6c3cb279ac1, + 0x96f5600f15a7b7e5, 0x29ab103a5ef8c0b9, + 0xbcb2b812db11a5de, 0x7415d448f6b6f0e7, + 0xebdf661791d60f56, 0x111b495b3464ad21, + 0x936b9fcebb25c995, 0xcab10dd900beec34, + 0xb84687c269ef3bfb, 0x3d5d514f40eea742, + 0xe65829b3046b0afa, 0xcb4a5a3112a5112, + 0x8ff71a0fe2c2e6dc, 0x47f0e785eaba72ab, + 0xb3f4e093db73a093, 0x59ed216765690f56, + 0xe0f218b8d25088b8, 0x306869c13ec3532c, + 0x8c974f7383725573, 0x1e414218c73a13fb, + 0xafbd2350644eeacf, 0xe5d1929ef90898fa, + 0xdbac6c247d62a583, 0xdf45f746b74abf39, + 0x894bc396ce5da772, 0x6b8bba8c328eb783, + 0xab9eb47c81f5114f, 0x66ea92f3f326564, + 0xd686619ba27255a2, 0xc80a537b0efefebd, + 0x8613fd0145877585, 0xbd06742ce95f5f36, + 0xa798fc4196e952e7, 0x2c48113823b73704, + 0xd17f3b51fca3a7a0, 0xf75a15862ca504c5, + 0x82ef85133de648c4, 0x9a984d73dbe722fb, + 0xa3ab66580d5fdaf5, 0xc13e60d0d2e0ebba, + 0xcc963fee10b7d1b3, 0x318df905079926a8, + 0xffbbcfe994e5c61f, 0xfdf17746497f7052, + 0x9fd561f1fd0f9bd3, 0xfeb6ea8bedefa633, + 0xc7caba6e7c5382c8, 0xfe64a52ee96b8fc0, + 0xf9bd690a1b68637b, 0x3dfdce7aa3c673b0, + 0x9c1661a651213e2d, 0x6bea10ca65c084e, + 0xc31bfa0fe5698db8, 0x486e494fcff30a62, + 0xf3e2f893dec3f126, 0x5a89dba3c3efccfa, + 0x986ddb5c6b3a76b7, 0xf89629465a75e01c, + 0xbe89523386091465, 0xf6bbb397f1135823, + 0xee2ba6c0678b597f, 0x746aa07ded582e2c, + 0x94db483840b717ef, 0xa8c2a44eb4571cdc, + 0xba121a4650e4ddeb, 0x92f34d62616ce413, + 0xe896a0d7e51e1566, 0x77b020baf9c81d17, + 0x915e2486ef32cd60, 0xace1474dc1d122e, + 0xb5b5ada8aaff80b8, 0xd819992132456ba, + 0xe3231912d5bf60e6, 0x10e1fff697ed6c69, + 0x8df5efabc5979c8f, 0xca8d3ffa1ef463c1, + 0xb1736b96b6fd83b3, 0xbd308ff8a6b17cb2, + 0xddd0467c64bce4a0, 0xac7cb3f6d05ddbde, + 0x8aa22c0dbef60ee4, 0x6bcdf07a423aa96b, + 0xad4ab7112eb3929d, 0x86c16c98d2c953c6, + 0xd89d64d57a607744, 0xe871c7bf077ba8b7, + 0x87625f056c7c4a8b, 0x11471cd764ad4972, + 0xa93af6c6c79b5d2d, 0xd598e40d3dd89bcf, + 0xd389b47879823479, 0x4aff1d108d4ec2c3, + 0x843610cb4bf160cb, 0xcedf722a585139ba, + 0xa54394fe1eedb8fe, 0xc2974eb4ee658828, + 0xce947a3da6a9273e, 0x733d226229feea32, + 0x811ccc668829b887, 0x806357d5a3f525f, + 0xa163ff802a3426a8, 0xca07c2dcb0cf26f7, + 0xc9bcff6034c13052, 0xfc89b393dd02f0b5, + 0xfc2c3f3841f17c67, 0xbbac2078d443ace2, + 0x9d9ba7832936edc0, 0xd54b944b84aa4c0d, + 0xc5029163f384a931, 0xa9e795e65d4df11, + 0xf64335bcf065d37d, 0x4d4617b5ff4a16d5, + 0x99ea0196163fa42e, 0x504bced1bf8e4e45, + 0xc06481fb9bcf8d39, 0xe45ec2862f71e1d6, + 0xf07da27a82c37088, 0x5d767327bb4e5a4c, + 0x964e858c91ba2655, 0x3a6a07f8d510f86f, + 0xbbe226efb628afea, 0x890489f70a55368b, + 0xeadab0aba3b2dbe5, 0x2b45ac74ccea842e, + 0x92c8ae6b464fc96f, 0x3b0b8bc90012929d, + 0xb77ada0617e3bbcb, 0x9ce6ebb40173744, + 0xe55990879ddcaabd, 0xcc420a6a101d0515, + 0x8f57fa54c2a9eab6, 0x9fa946824a12232d, + 0xb32df8e9f3546564, 0x47939822dc96abf9, + 0xdff9772470297ebd, 0x59787e2b93bc56f7, + 0x8bfbea76c619ef36, 0x57eb4edb3c55b65a, + 0xaefae51477a06b03, 0xede622920b6b23f1, + 0xdab99e59958885c4, 0xe95fab368e45eced, + 0x88b402f7fd75539b, 0x11dbcb0218ebb414, + 0xaae103b5fcd2a881, 0xd652bdc29f26a119, + 0xd59944a37c0752a2, 0x4be76d3346f0495f, + 0x857fcae62d8493a5, 0x6f70a4400c562ddb, + 0xa6dfbd9fb8e5b88e, 0xcb4ccd500f6bb952, + 0xd097ad07a71f26b2, 0x7e2000a41346a7a7, + 0x825ecc24c873782f, 0x8ed400668c0c28c8, + 0xa2f67f2dfa90563b, 0x728900802f0f32fa, + 0xcbb41ef979346bca, 0x4f2b40a03ad2ffb9, + 0xfea126b7d78186bc, 0xe2f610c84987bfa8, + 0x9f24b832e6b0f436, 0xdd9ca7d2df4d7c9, + 0xc6ede63fa05d3143, 0x91503d1c79720dbb, + 0xf8a95fcf88747d94, 0x75a44c6397ce912a, + 0x9b69dbe1b548ce7c, 0xc986afbe3ee11aba, + 0xc24452da229b021b, 0xfbe85badce996168, + 0xf2d56790ab41c2a2, 0xfae27299423fb9c3, + 0x97c560ba6b0919a5, 0xdccd879fc967d41a, + 0xbdb6b8e905cb600f, 0x5400e987bbc1c920, + 0xed246723473e3813, 0x290123e9aab23b68, + 0x9436c0760c86e30b, 0xf9a0b6720aaf6521, + 0xb94470938fa89bce, 0xf808e40e8d5b3e69, + 0xe7958cb87392c2c2, 0xb60b1d1230b20e04, + 0x90bd77f3483bb9b9, 0xb1c6f22b5e6f48c2, + 0xb4ecd5f01a4aa828, 0x1e38aeb6360b1af3, + 0xe2280b6c20dd5232, 0x25c6da63c38de1b0, + 0x8d590723948a535f, 0x579c487e5a38ad0e, + 0xb0af48ec79ace837, 0x2d835a9df0c6d851, + 0xdcdb1b2798182244, 0xf8e431456cf88e65, + 0x8a08f0f8bf0f156b, 0x1b8e9ecb641b58ff, + 0xac8b2d36eed2dac5, 0xe272467e3d222f3f, + 0xd7adf884aa879177, 0x5b0ed81dcc6abb0f, + 0x86ccbb52ea94baea, 0x98e947129fc2b4e9, + 0xa87fea27a539e9a5, 0x3f2398d747b36224, + 0xd29fe4b18e88640e, 0x8eec7f0d19a03aad, + 0x83a3eeeef9153e89, 0x1953cf68300424ac, + 0xa48ceaaab75a8e2b, 0x5fa8c3423c052dd7, + 0xcdb02555653131b6, 0x3792f412cb06794d, + 0x808e17555f3ebf11, 0xe2bbd88bbee40bd0, + 0xa0b19d2ab70e6ed6, 0x5b6aceaeae9d0ec4, + 0xc8de047564d20a8b, 0xf245825a5a445275, + 0xfb158592be068d2e, 0xeed6e2f0f0d56712, + 0x9ced737bb6c4183d, 0x55464dd69685606b, + 0xc428d05aa4751e4c, 0xaa97e14c3c26b886, + 0xf53304714d9265df, 0xd53dd99f4b3066a8, + 0x993fe2c6d07b7fab, 0xe546a8038efe4029, + 0xbf8fdb78849a5f96, 0xde98520472bdd033, + 0xef73d256a5c0f77c, 0x963e66858f6d4440, + 0x95a8637627989aad, 0xdde7001379a44aa8, + 0xbb127c53b17ec159, 0x5560c018580d5d52, + 0xe9d71b689dde71af, 0xaab8f01e6e10b4a6, + 0x9226712162ab070d, 0xcab3961304ca70e8, + 0xb6b00d69bb55c8d1, 0x3d607b97c5fd0d22, + 0xe45c10c42a2b3b05, 0x8cb89a7db77c506a, + 0x8eb98a7a9a5b04e3, 0x77f3608e92adb242, + 0xb267ed1940f1c61c, 0x55f038b237591ed3, + 0xdf01e85f912e37a3, 0x6b6c46dec52f6688, + 0x8b61313bbabce2c6, 0x2323ac4b3b3da015, + 0xae397d8aa96c1b77, 0xabec975e0a0d081a, + 0xd9c7dced53c72255, 0x96e7bd358c904a21, + 0x881cea14545c7575, 0x7e50d64177da2e54, + 0xaa242499697392d2, 0xdde50bd1d5d0b9e9, + 0xd4ad2dbfc3d07787, 0x955e4ec64b44e864, + 0x84ec3c97da624ab4, 0xbd5af13bef0b113e, + 0xa6274bbdd0fadd61, 0xecb1ad8aeacdd58e, + 0xcfb11ead453994ba, 0x67de18eda5814af2, + 0x81ceb32c4b43fcf4, 0x80eacf948770ced7, + 0xa2425ff75e14fc31, 0xa1258379a94d028d, + 0xcad2f7f5359a3b3e, 0x96ee45813a04330, + 0xfd87b5f28300ca0d, 0x8bca9d6e188853fc, + 0x9e74d1b791e07e48, 0x775ea264cf55347e, + 0xc612062576589dda, 0x95364afe032a819e, + 0xf79687aed3eec551, 0x3a83ddbd83f52205, + 0x9abe14cd44753b52, 0xc4926a9672793543, + 0xc16d9a0095928a27, 0x75b7053c0f178294, + 0xf1c90080baf72cb1, 0x5324c68b12dd6339, + 0x971da05074da7bee, 0xd3f6fc16ebca5e04, + 0xbce5086492111aea, 0x88f4bb1ca6bcf585, + 0xec1e4a7db69561a5, 0x2b31e9e3d06c32e6, + 0x9392ee8e921d5d07, 0x3aff322e62439fd0, + 0xb877aa3236a4b449, 0x9befeb9fad487c3, + 0xe69594bec44de15b, 0x4c2ebe687989a9b4, + 0x901d7cf73ab0acd9, 0xf9d37014bf60a11, + 0xb424dc35095cd80f, 0x538484c19ef38c95, + 0xe12e13424bb40e13, 0x2865a5f206b06fba, + 0x8cbccc096f5088cb, 0xf93f87b7442e45d4, + 0xafebff0bcb24aafe, 0xf78f69a51539d749, + 0xdbe6fecebdedd5be, 0xb573440e5a884d1c, + 0x89705f4136b4a597, 0x31680a88f8953031, + 0xabcc77118461cefc, 0xfdc20d2b36ba7c3e, + 0xd6bf94d5e57a42bc, 0x3d32907604691b4d, + 0x8637bd05af6c69b5, 0xa63f9a49c2c1b110, + 0xa7c5ac471b478423, 0xfcf80dc33721d54, + 0xd1b71758e219652b, 0xd3c36113404ea4a9, + 0x83126e978d4fdf3b, 0x645a1cac083126ea, + 0xa3d70a3d70a3d70a, 0x3d70a3d70a3d70a4, + 0xcccccccccccccccc, 0xcccccccccccccccd, + 0x8000000000000000, 0x0, + 0xa000000000000000, 0x0, + 0xc800000000000000, 0x0, + 0xfa00000000000000, 0x0, + 0x9c40000000000000, 0x0, + 0xc350000000000000, 0x0, + 0xf424000000000000, 0x0, + 0x9896800000000000, 0x0, + 0xbebc200000000000, 0x0, + 0xee6b280000000000, 0x0, + 0x9502f90000000000, 0x0, + 0xba43b74000000000, 0x0, + 0xe8d4a51000000000, 0x0, + 0x9184e72a00000000, 0x0, + 0xb5e620f480000000, 0x0, + 0xe35fa931a0000000, 0x0, + 0x8e1bc9bf04000000, 0x0, + 0xb1a2bc2ec5000000, 0x0, + 0xde0b6b3a76400000, 0x0, + 0x8ac7230489e80000, 0x0, + 0xad78ebc5ac620000, 0x0, + 0xd8d726b7177a8000, 0x0, + 0x878678326eac9000, 0x0, + 0xa968163f0a57b400, 0x0, + 0xd3c21bcecceda100, 0x0, + 0x84595161401484a0, 0x0, + 0xa56fa5b99019a5c8, 0x0, + 0xcecb8f27f4200f3a, 0x0, + 0x813f3978f8940984, 0x4000000000000000, + 0xa18f07d736b90be5, 0x5000000000000000, + 0xc9f2c9cd04674ede, 0xa400000000000000, + 0xfc6f7c4045812296, 0x4d00000000000000, + 0x9dc5ada82b70b59d, 0xf020000000000000, + 0xc5371912364ce305, 0x6c28000000000000, + 0xf684df56c3e01bc6, 0xc732000000000000, + 0x9a130b963a6c115c, 0x3c7f400000000000, + 0xc097ce7bc90715b3, 0x4b9f100000000000, + 0xf0bdc21abb48db20, 0x1e86d40000000000, + 0x96769950b50d88f4, 0x1314448000000000, + 0xbc143fa4e250eb31, 0x17d955a000000000, + 0xeb194f8e1ae525fd, 0x5dcfab0800000000, + 0x92efd1b8d0cf37be, 0x5aa1cae500000000, + 0xb7abc627050305ad, 0xf14a3d9e40000000, + 0xe596b7b0c643c719, 0x6d9ccd05d0000000, + 0x8f7e32ce7bea5c6f, 0xe4820023a2000000, + 0xb35dbf821ae4f38b, 0xdda2802c8a800000, + 0xe0352f62a19e306e, 0xd50b2037ad200000, + 0x8c213d9da502de45, 0x4526f422cc340000, + 0xaf298d050e4395d6, 0x9670b12b7f410000, + 0xdaf3f04651d47b4c, 0x3c0cdd765f114000, + 0x88d8762bf324cd0f, 0xa5880a69fb6ac800, + 0xab0e93b6efee0053, 0x8eea0d047a457a00, + 0xd5d238a4abe98068, 0x72a4904598d6d880, + 0x85a36366eb71f041, 0x47a6da2b7f864750, + 0xa70c3c40a64e6c51, 0x999090b65f67d924, + 0xd0cf4b50cfe20765, 0xfff4b4e3f741cf6d, + 0x82818f1281ed449f, 0xbff8f10e7a8921a4, + 0xa321f2d7226895c7, 0xaff72d52192b6a0d, + 0xcbea6f8ceb02bb39, 0x9bf4f8a69f764490, + 0xfee50b7025c36a08, 0x2f236d04753d5b4, + 0x9f4f2726179a2245, 0x1d762422c946590, + 0xc722f0ef9d80aad6, 0x424d3ad2b7b97ef5, + 0xf8ebad2b84e0d58b, 0xd2e0898765a7deb2, + 0x9b934c3b330c8577, 0x63cc55f49f88eb2f, + 0xc2781f49ffcfa6d5, 0x3cbf6b71c76b25fb, + 0xf316271c7fc3908a, 0x8bef464e3945ef7a, + 0x97edd871cfda3a56, 0x97758bf0e3cbb5ac, + 0xbde94e8e43d0c8ec, 0x3d52eeed1cbea317, + 0xed63a231d4c4fb27, 0x4ca7aaa863ee4bdd, + 0x945e455f24fb1cf8, 0x8fe8caa93e74ef6a, + 0xb975d6b6ee39e436, 0xb3e2fd538e122b44, + 0xe7d34c64a9c85d44, 0x60dbbca87196b616, + 0x90e40fbeea1d3a4a, 0xbc8955e946fe31cd, + 0xb51d13aea4a488dd, 0x6babab6398bdbe41, + 0xe264589a4dcdab14, 0xc696963c7eed2dd1, + 0x8d7eb76070a08aec, 0xfc1e1de5cf543ca2, + 0xb0de65388cc8ada8, 0x3b25a55f43294bcb, + 0xdd15fe86affad912, 0x49ef0eb713f39ebe, + 0x8a2dbf142dfcc7ab, 0x6e3569326c784337, + 0xacb92ed9397bf996, 0x49c2c37f07965404, + 0xd7e77a8f87daf7fb, 0xdc33745ec97be906, + 0x86f0ac99b4e8dafd, 0x69a028bb3ded71a3, + 0xa8acd7c0222311bc, 0xc40832ea0d68ce0c, + 0xd2d80db02aabd62b, 0xf50a3fa490c30190, + 0x83c7088e1aab65db, 0x792667c6da79e0fa, + 0xa4b8cab1a1563f52, 0x577001b891185938, + 0xcde6fd5e09abcf26, 0xed4c0226b55e6f86, + 0x80b05e5ac60b6178, 0x544f8158315b05b4, + 0xa0dc75f1778e39d6, 0x696361ae3db1c721, + 0xc913936dd571c84c, 0x3bc3a19cd1e38e9, + 0xfb5878494ace3a5f, 0x4ab48a04065c723, + 0x9d174b2dcec0e47b, 0x62eb0d64283f9c76, + 0xc45d1df942711d9a, 0x3ba5d0bd324f8394, + 0xf5746577930d6500, 0xca8f44ec7ee36479, + 0x9968bf6abbe85f20, 0x7e998b13cf4e1ecb, + 0xbfc2ef456ae276e8, 0x9e3fedd8c321a67e, + 0xefb3ab16c59b14a2, 0xc5cfe94ef3ea101e, + 0x95d04aee3b80ece5, 0xbba1f1d158724a12, + 0xbb445da9ca61281f, 0x2a8a6e45ae8edc97, + 0xea1575143cf97226, 0xf52d09d71a3293bd, + 0x924d692ca61be758, 0x593c2626705f9c56, + 0xb6e0c377cfa2e12e, 0x6f8b2fb00c77836c, + 0xe498f455c38b997a, 0xb6dfb9c0f956447, + 0x8edf98b59a373fec, 0x4724bd4189bd5eac, + 0xb2977ee300c50fe7, 0x58edec91ec2cb657, + 0xdf3d5e9bc0f653e1, 0x2f2967b66737e3ed, + 0x8b865b215899f46c, 0xbd79e0d20082ee74, + 0xae67f1e9aec07187, 0xecd8590680a3aa11, + 0xda01ee641a708de9, 0xe80e6f4820cc9495, + 0x884134fe908658b2, 0x3109058d147fdcdd, + 0xaa51823e34a7eede, 0xbd4b46f0599fd415, + 0xd4e5e2cdc1d1ea96, 0x6c9e18ac7007c91a, + 0x850fadc09923329e, 0x3e2cf6bc604ddb0, + 0xa6539930bf6bff45, 0x84db8346b786151c, + 0xcfe87f7cef46ff16, 0xe612641865679a63, + 0x81f14fae158c5f6e, 0x4fcb7e8f3f60c07e, + 0xa26da3999aef7749, 0xe3be5e330f38f09d, + 0xcb090c8001ab551c, 0x5cadf5bfd3072cc5, + 0xfdcb4fa002162a63, 0x73d9732fc7c8f7f6, + 0x9e9f11c4014dda7e, 0x2867e7fddcdd9afa, + 0xc646d63501a1511d, 0xb281e1fd541501b8, + 0xf7d88bc24209a565, 0x1f225a7ca91a4226, + 0x9ae757596946075f, 0x3375788de9b06958, + 0xc1a12d2fc3978937, 0x52d6b1641c83ae, + 0xf209787bb47d6b84, 0xc0678c5dbd23a49a, + 0x9745eb4d50ce6332, 0xf840b7ba963646e0, + 0xbd176620a501fbff, 0xb650e5a93bc3d898, + 0xec5d3fa8ce427aff, 0xa3e51f138ab4cebe, + 0x93ba47c980e98cdf, 0xc66f336c36b10137, + 0xb8a8d9bbe123f017, 0xb80b0047445d4184, + 0xe6d3102ad96cec1d, 0xa60dc059157491e5, + 0x9043ea1ac7e41392, 0x87c89837ad68db2f, + 0xb454e4a179dd1877, 0x29babe4598c311fb, + 0xe16a1dc9d8545e94, 0xf4296dd6fef3d67a, + 0x8ce2529e2734bb1d, 0x1899e4a65f58660c, + 0xb01ae745b101e9e4, 0x5ec05dcff72e7f8f, + 0xdc21a1171d42645d, 0x76707543f4fa1f73, + 0x899504ae72497eba, 0x6a06494a791c53a8, + 0xabfa45da0edbde69, 0x487db9d17636892, + 0xd6f8d7509292d603, 0x45a9d2845d3c42b6, + 0x865b86925b9bc5c2, 0xb8a2392ba45a9b2, + 0xa7f26836f282b732, 0x8e6cac7768d7141e, + 0xd1ef0244af2364ff, 0x3207d795430cd926, + 0x8335616aed761f1f, 0x7f44e6bd49e807b8, + 0xa402b9c5a8d3a6e7, 0x5f16206c9c6209a6, + 0xcd036837130890a1, 0x36dba887c37a8c0f, + 0x802221226be55a64, 0xc2494954da2c9789, + 0xa02aa96b06deb0fd, 0xf2db9baa10b7bd6c, + 0xc83553c5c8965d3d, 0x6f92829494e5acc7, + 0xfa42a8b73abbf48c, 0xcb772339ba1f17f9, + 0x9c69a97284b578d7, 0xff2a760414536efb, + 0xc38413cf25e2d70d, 0xfef5138519684aba, + 0xf46518c2ef5b8cd1, 0x7eb258665fc25d69, + 0x98bf2f79d5993802, 0xef2f773ffbd97a61, + 0xbeeefb584aff8603, 0xaafb550ffacfd8fa, + 0xeeaaba2e5dbf6784, 0x95ba2a53f983cf38, + 0x952ab45cfa97a0b2, 0xdd945a747bf26183, + 0xba756174393d88df, 0x94f971119aeef9e4, + 0xe912b9d1478ceb17, 0x7a37cd5601aab85d, + 0x91abb422ccb812ee, 0xac62e055c10ab33a, + 0xb616a12b7fe617aa, 0x577b986b314d6009, + 0xe39c49765fdf9d94, 0xed5a7e85fda0b80b, + 0x8e41ade9fbebc27d, 0x14588f13be847307, + 0xb1d219647ae6b31c, 0x596eb2d8ae258fc8, + 0xde469fbd99a05fe3, 0x6fca5f8ed9aef3bb, + 0x8aec23d680043bee, 0x25de7bb9480d5854, + 0xada72ccc20054ae9, 0xaf561aa79a10ae6a, + 0xd910f7ff28069da4, 0x1b2ba1518094da04, + 0x87aa9aff79042286, 0x90fb44d2f05d0842, + 0xa99541bf57452b28, 0x353a1607ac744a53, + 0xd3fa922f2d1675f2, 0x42889b8997915ce8, + 0x847c9b5d7c2e09b7, 0x69956135febada11, + 0xa59bc234db398c25, 0x43fab9837e699095, + 0xcf02b2c21207ef2e, 0x94f967e45e03f4bb, + 0x8161afb94b44f57d, 0x1d1be0eebac278f5, + 0xa1ba1ba79e1632dc, 0x6462d92a69731732, + 0xca28a291859bbf93, 0x7d7b8f7503cfdcfe, + 0xfcb2cb35e702af78, 0x5cda735244c3d43e, + 0x9defbf01b061adab, 0x3a0888136afa64a7, + 0xc56baec21c7a1916, 0x88aaa1845b8fdd0, + 0xf6c69a72a3989f5b, 0x8aad549e57273d45, + 0x9a3c2087a63f6399, 0x36ac54e2f678864b, + 0xc0cb28a98fcf3c7f, 0x84576a1bb416a7dd, + 0xf0fdf2d3f3c30b9f, 0x656d44a2a11c51d5, + 0x969eb7c47859e743, 0x9f644ae5a4b1b325, + 0xbc4665b596706114, 0x873d5d9f0dde1fee, + 0xeb57ff22fc0c7959, 0xa90cb506d155a7ea, + 0x9316ff75dd87cbd8, 0x9a7f12442d588f2, + 0xb7dcbf5354e9bece, 0xc11ed6d538aeb2f, + 0xe5d3ef282a242e81, 0x8f1668c8a86da5fa, + 0x8fa475791a569d10, 0xf96e017d694487bc, + 0xb38d92d760ec4455, 0x37c981dcc395a9ac, + 0xe070f78d3927556a, 0x85bbe253f47b1417, + 0x8c469ab843b89562, 0x93956d7478ccec8e, + 0xaf58416654a6babb, 0x387ac8d1970027b2, + 0xdb2e51bfe9d0696a, 0x6997b05fcc0319e, + 0x88fcf317f22241e2, 0x441fece3bdf81f03, + 0xab3c2fddeeaad25a, 0xd527e81cad7626c3, + 0xd60b3bd56a5586f1, 0x8a71e223d8d3b074, + 0x85c7056562757456, 0xf6872d5667844e49, + 0xa738c6bebb12d16c, 0xb428f8ac016561db, + 0xd106f86e69d785c7, 0xe13336d701beba52, + 0x82a45b450226b39c, 0xecc0024661173473, + 0xa34d721642b06084, 0x27f002d7f95d0190, + 0xcc20ce9bd35c78a5, 0x31ec038df7b441f4, + 0xff290242c83396ce, 0x7e67047175a15271, + 0x9f79a169bd203e41, 0xf0062c6e984d386, + 0xc75809c42c684dd1, 0x52c07b78a3e60868, + 0xf92e0c3537826145, 0xa7709a56ccdf8a82, + 0x9bbcc7a142b17ccb, 0x88a66076400bb691, + 0xc2abf989935ddbfe, 0x6acff893d00ea435, + 0xf356f7ebf83552fe, 0x583f6b8c4124d43, + 0x98165af37b2153de, 0xc3727a337a8b704a, + 0xbe1bf1b059e9a8d6, 0x744f18c0592e4c5c, + 0xeda2ee1c7064130c, 0x1162def06f79df73, + 0x9485d4d1c63e8be7, 0x8addcb5645ac2ba8, + 0xb9a74a0637ce2ee1, 0x6d953e2bd7173692, + 0xe8111c87c5c1ba99, 0xc8fa8db6ccdd0437, + 0x910ab1d4db9914a0, 0x1d9c9892400a22a2, + 0xb54d5e4a127f59c8, 0x2503beb6d00cab4b, + 0xe2a0b5dc971f303a, 0x2e44ae64840fd61d, + 0x8da471a9de737e24, 0x5ceaecfed289e5d2, + 0xb10d8e1456105dad, 0x7425a83e872c5f47, + 0xdd50f1996b947518, 0xd12f124e28f77719, + 0x8a5296ffe33cc92f, 0x82bd6b70d99aaa6f, + 0xace73cbfdc0bfb7b, 0x636cc64d1001550b, + 0xd8210befd30efa5a, 0x3c47f7e05401aa4e, + 0x8714a775e3e95c78, 0x65acfaec34810a71, + 0xa8d9d1535ce3b396, 0x7f1839a741a14d0d, + 0xd31045a8341ca07c, 0x1ede48111209a050, + 0x83ea2b892091e44d, 0x934aed0aab460432, + 0xa4e4b66b68b65d60, 0xf81da84d5617853f, + 0xce1de40642e3f4b9, 0x36251260ab9d668e, + 0x80d2ae83e9ce78f3, 0xc1d72b7c6b426019, + 0xa1075a24e4421730, 0xb24cf65b8612f81f, + 0xc94930ae1d529cfc, 0xdee033f26797b627, + 0xfb9b7cd9a4a7443c, 0x169840ef017da3b1, + 0x9d412e0806e88aa5, 0x8e1f289560ee864e, + 0xc491798a08a2ad4e, 0xf1a6f2bab92a27e2, + 0xf5b5d7ec8acb58a2, 0xae10af696774b1db, + 0x9991a6f3d6bf1765, 0xacca6da1e0a8ef29, + 0xbff610b0cc6edd3f, 0x17fd090a58d32af3, + 0xeff394dcff8a948e, 0xddfc4b4cef07f5b0, + 0x95f83d0a1fb69cd9, 0x4abdaf101564f98e, + 0xbb764c4ca7a4440f, 0x9d6d1ad41abe37f1, + 0xea53df5fd18d5513, 0x84c86189216dc5ed, + 0x92746b9be2f8552c, 0x32fd3cf5b4e49bb4, + 0xb7118682dbb66a77, 0x3fbc8c33221dc2a1, + 0xe4d5e82392a40515, 0xfabaf3feaa5334a, + 0x8f05b1163ba6832d, 0x29cb4d87f2a7400e, + 0xb2c71d5bca9023f8, 0x743e20e9ef511012, + 0xdf78e4b2bd342cf6, 0x914da9246b255416, + 0x8bab8eefb6409c1a, 0x1ad089b6c2f7548e, + 0xae9672aba3d0c320, 0xa184ac2473b529b1, + 0xda3c0f568cc4f3e8, 0xc9e5d72d90a2741e, + 0x8865899617fb1871, 0x7e2fa67c7a658892, + 0xaa7eebfb9df9de8d, 0xddbb901b98feeab7, + 0xd51ea6fa85785631, 0x552a74227f3ea565, + 0x8533285c936b35de, 0xd53a88958f87275f, + 0xa67ff273b8460356, 0x8a892abaf368f137, + 0xd01fef10a657842c, 0x2d2b7569b0432d85, + 0x8213f56a67f6b29b, 0x9c3b29620e29fc73, + 0xa298f2c501f45f42, 0x8349f3ba91b47b8f, + 0xcb3f2f7642717713, 0x241c70a936219a73, + 0xfe0efb53d30dd4d7, 0xed238cd383aa0110, + 0x9ec95d1463e8a506, 0xf4363804324a40aa, + 0xc67bb4597ce2ce48, 0xb143c6053edcd0d5, + 0xf81aa16fdc1b81da, 0xdd94b7868e94050a, + 0x9b10a4e5e9913128, 0xca7cf2b4191c8326, + 0xc1d4ce1f63f57d72, 0xfd1c2f611f63a3f0, + 0xf24a01a73cf2dccf, 0xbc633b39673c8cec, + 0x976e41088617ca01, 0xd5be0503e085d813, + 0xbd49d14aa79dbc82, 0x4b2d8644d8a74e18, + 0xec9c459d51852ba2, 0xddf8e7d60ed1219e, + 0x93e1ab8252f33b45, 0xcabb90e5c942b503, + 0xb8da1662e7b00a17, 0x3d6a751f3b936243, + 0xe7109bfba19c0c9d, 0xcc512670a783ad4, + 0x906a617d450187e2, 0x27fb2b80668b24c5, + 0xb484f9dc9641e9da, 0xb1f9f660802dedf6, + 0xe1a63853bbd26451, 0x5e7873f8a0396973, + 0x8d07e33455637eb2, 0xdb0b487b6423e1e8, + 0xb049dc016abc5e5f, 0x91ce1a9a3d2cda62, + 0xdc5c5301c56b75f7, 0x7641a140cc7810fb, + 0x89b9b3e11b6329ba, 0xa9e904c87fcb0a9d, + 0xac2820d9623bf429, 0x546345fa9fbdcd44, + 0xd732290fbacaf133, 0xa97c177947ad4095, + 0x867f59a9d4bed6c0, 0x49ed8eabcccc485d, + 0xa81f301449ee8c70, 0x5c68f256bfff5a74, + 0xd226fc195c6a2f8c, 0x73832eec6fff3111, + 0x83585d8fd9c25db7, 0xc831fd53c5ff7eab, + 0xa42e74f3d032f525, 0xba3e7ca8b77f5e55, + 0xcd3a1230c43fb26f, 0x28ce1bd2e55f35eb, + 0x80444b5e7aa7cf85, 0x7980d163cf5b81b3, + 0xa0555e361951c366, 0xd7e105bcc332621f, + 0xc86ab5c39fa63440, 0x8dd9472bf3fefaa7, + 0xfa856334878fc150, 0xb14f98f6f0feb951, + 0x9c935e00d4b9d8d2, 0x6ed1bf9a569f33d3, + 0xc3b8358109e84f07, 0xa862f80ec4700c8, + 0xf4a642e14c6262c8, 0xcd27bb612758c0fa, + 0x98e7e9cccfbd7dbd, 0x8038d51cb897789c, + 0xbf21e44003acdd2c, 0xe0470a63e6bd56c3, + 0xeeea5d5004981478, 0x1858ccfce06cac74, + 0x95527a5202df0ccb, 0xf37801e0c43ebc8, + 0xbaa718e68396cffd, 0xd30560258f54e6ba, + 0xe950df20247c83fd, 0x47c6b82ef32a2069, + 0x91d28b7416cdd27e, 0x4cdc331d57fa5441, + 0xb6472e511c81471d, 0xe0133fe4adf8e952, + 0xe3d8f9e563a198e5, 0x58180fddd97723a6, + 0x8e679c2f5e44ff8f, 0x570f09eaa7ea7648, + }; +}; + +#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE + +template +constexpr uint64_t + powers_template::power_of_five_128[number_of_entries]; + +#endif + +using powers = powers_template<>; + +} // namespace fast_float + +#endif diff --git a/3rdparty/fast_float/float_common.h b/3rdparty/fast_float/float_common.h new file mode 100644 index 000000000000..ef499ce79259 --- /dev/null +++ b/3rdparty/fast_float/float_common.h @@ -0,0 +1,1240 @@ +#ifndef FASTFLOAT_FLOAT_COMMON_H +#define FASTFLOAT_FLOAT_COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#ifdef __has_include +#if __has_include() && (__cplusplus > 202002L || _MSVC_LANG > 202002L) +#include +#endif +#endif +#include "constexpr_feature_detect.h" + +#define FASTFLOAT_VERSION_MAJOR 8 +#define FASTFLOAT_VERSION_MINOR 0 +#define FASTFLOAT_VERSION_PATCH 0 + +#define FASTFLOAT_STRINGIZE_IMPL(x) #x +#define FASTFLOAT_STRINGIZE(x) FASTFLOAT_STRINGIZE_IMPL(x) + +#define FASTFLOAT_VERSION_STR \ + FASTFLOAT_STRINGIZE(FASTFLOAT_VERSION_MAJOR) \ + "." FASTFLOAT_STRINGIZE(FASTFLOAT_VERSION_MINOR) "." FASTFLOAT_STRINGIZE( \ + FASTFLOAT_VERSION_PATCH) + +#define FASTFLOAT_VERSION \ + (FASTFLOAT_VERSION_MAJOR * 10000 + FASTFLOAT_VERSION_MINOR * 100 + \ + FASTFLOAT_VERSION_PATCH) + +namespace fast_float { + +enum class chars_format : uint64_t; + +namespace detail { +constexpr chars_format basic_json_fmt = chars_format(1 << 5); +constexpr chars_format basic_fortran_fmt = chars_format(1 << 6); +} // namespace detail + +enum class chars_format : uint64_t { + scientific = 1 << 0, + fixed = 1 << 2, + hex = 1 << 3, + no_infnan = 1 << 4, + // RFC 8259: https://datatracker.ietf.org/doc/html/rfc8259#section-6 + json = uint64_t(detail::basic_json_fmt) | fixed | scientific | no_infnan, + // Extension of RFC 8259 where, e.g., "inf" and "nan" are allowed. + json_or_infnan = uint64_t(detail::basic_json_fmt) | fixed | scientific, + fortran = uint64_t(detail::basic_fortran_fmt) | fixed | scientific, + general = fixed | scientific, + allow_leading_plus = 1 << 7, + skip_white_space = 1 << 8, +}; + +template struct from_chars_result_t { + UC const *ptr; + std::errc ec; +}; + +using from_chars_result = from_chars_result_t; + +template struct parse_options_t { + constexpr explicit parse_options_t(chars_format fmt = chars_format::general, + UC dot = UC('.'), int b = 10) + : format(fmt), decimal_point(dot), base(b) {} + + /** Which number formats are accepted */ + chars_format format; + /** The character used as decimal point */ + UC decimal_point; + /** The base used for integers */ + int base; +}; + +using parse_options = parse_options_t; + +} // namespace fast_float + +#if FASTFLOAT_HAS_BIT_CAST +#include +#endif + +#if (defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \ + defined(__amd64) || defined(__aarch64__) || defined(_M_ARM64) || \ + defined(__MINGW64__) || defined(__s390x__) || \ + (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || \ + defined(__PPC64LE__)) || \ + defined(__loongarch64)) +#define FASTFLOAT_64BIT 1 +#elif (defined(__i386) || defined(__i386__) || defined(_M_IX86) || \ + defined(__arm__) || defined(_M_ARM) || defined(__ppc__) || \ + defined(__MINGW32__) || defined(__EMSCRIPTEN__)) +#define FASTFLOAT_32BIT 1 +#else + // Need to check incrementally, since SIZE_MAX is a size_t, avoid overflow. +// We can never tell the register width, but the SIZE_MAX is a good +// approximation. UINTPTR_MAX and INTPTR_MAX are optional, so avoid them for max +// portability. +#if SIZE_MAX == 0xffff +#error Unknown platform (16-bit, unsupported) +#elif SIZE_MAX == 0xffffffff +#define FASTFLOAT_32BIT 1 +#elif SIZE_MAX == 0xffffffffffffffff +#define FASTFLOAT_64BIT 1 +#else +#error Unknown platform (not 32-bit, not 64-bit?) +#endif +#endif + +#if ((defined(_WIN32) || defined(_WIN64)) && !defined(__clang__)) || \ + (defined(_M_ARM64) && !defined(__MINGW32__)) +#include +#endif + +#if defined(_MSC_VER) && !defined(__clang__) +#define FASTFLOAT_VISUAL_STUDIO 1 +#endif + +#if defined __BYTE_ORDER__ && defined __ORDER_BIG_ENDIAN__ +#define FASTFLOAT_IS_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +#elif defined _WIN32 +#define FASTFLOAT_IS_BIG_ENDIAN 0 +#else +#if defined(__APPLE__) || defined(__FreeBSD__) +#include +#elif defined(sun) || defined(__sun) +#include +#elif defined(__MVS__) +#include +#else +#ifdef __has_include +#if __has_include() +#include +#endif //__has_include() +#endif //__has_include +#endif +# +#ifndef __BYTE_ORDER__ +// safe choice +#define FASTFLOAT_IS_BIG_ENDIAN 0 +#endif +# +#ifndef __ORDER_LITTLE_ENDIAN__ +// safe choice +#define FASTFLOAT_IS_BIG_ENDIAN 0 +#endif +# +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define FASTFLOAT_IS_BIG_ENDIAN 0 +#else +#define FASTFLOAT_IS_BIG_ENDIAN 1 +#endif +#endif + +#if defined(__SSE2__) || (defined(FASTFLOAT_VISUAL_STUDIO) && \ + (defined(_M_AMD64) || defined(_M_X64) || \ + (defined(_M_IX86_FP) && _M_IX86_FP == 2))) +#define FASTFLOAT_SSE2 1 +#endif + +#if defined(__aarch64__) || defined(_M_ARM64) +#define FASTFLOAT_NEON 1 +#endif + +#if defined(FASTFLOAT_SSE2) || defined(FASTFLOAT_NEON) +#define FASTFLOAT_HAS_SIMD 1 +#endif + +#if defined(__GNUC__) +// disable -Wcast-align=strict (GCC only) +#define FASTFLOAT_SIMD_DISABLE_WARNINGS \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wcast-align\"") +#else +#define FASTFLOAT_SIMD_DISABLE_WARNINGS +#endif + +#if defined(__GNUC__) +#define FASTFLOAT_SIMD_RESTORE_WARNINGS _Pragma("GCC diagnostic pop") +#else +#define FASTFLOAT_SIMD_RESTORE_WARNINGS +#endif + +#ifdef FASTFLOAT_VISUAL_STUDIO +#define fastfloat_really_inline __forceinline +#else +#define fastfloat_really_inline inline __attribute__((always_inline)) +#endif + +#ifndef FASTFLOAT_ASSERT +#define FASTFLOAT_ASSERT(x) \ + { ((void)(x)); } +#endif + +#ifndef FASTFLOAT_DEBUG_ASSERT +#define FASTFLOAT_DEBUG_ASSERT(x) \ + { ((void)(x)); } +#endif + +// rust style `try!()` macro, or `?` operator +#define FASTFLOAT_TRY(x) \ + { \ + if (!(x)) \ + return false; \ + } + +#define FASTFLOAT_ENABLE_IF(...) \ + typename std::enable_if<(__VA_ARGS__), int>::type + +namespace fast_float { + +fastfloat_really_inline constexpr bool cpp20_and_in_constexpr() { +#if FASTFLOAT_HAS_IS_CONSTANT_EVALUATED + return std::is_constant_evaluated(); +#else + return false; +#endif +} + +template +struct is_supported_float_type + : std::integral_constant< + bool, std::is_same::value || std::is_same::value +#ifdef __STDCPP_FLOAT64_T__ + || std::is_same::value +#endif +#ifdef __STDCPP_FLOAT32_T__ + || std::is_same::value +#endif +#ifdef __STDCPP_FLOAT16_T__ + || std::is_same::value +#endif +#ifdef __STDCPP_BFLOAT16_T__ + || std::is_same::value +#endif + > { +}; + +template +using equiv_uint_t = typename std::conditional< + sizeof(T) == 1, uint8_t, + typename std::conditional< + sizeof(T) == 2, uint16_t, + typename std::conditional::type>::type>::type; + +template struct is_supported_integer_type : std::is_integral {}; + +template +struct is_supported_char_type + : std::integral_constant::value || + std::is_same::value || + std::is_same::value || + std::is_same::value +#ifdef __cpp_char8_t + || std::is_same::value +#endif + > { +}; + +// Compares two ASCII strings in a case insensitive manner. +template +inline FASTFLOAT_CONSTEXPR14 bool +fastfloat_strncasecmp(UC const *actual_mixedcase, UC const *expected_lowercase, + size_t length) { + for (size_t i = 0; i < length; ++i) { + UC const actual = actual_mixedcase[i]; + if ((actual < 256 ? actual | 32 : actual) != expected_lowercase[i]) { + return false; + } + } + return true; +} + +#ifndef FLT_EVAL_METHOD +#error "FLT_EVAL_METHOD should be defined, please include cfloat." +#endif + +// a pointer and a length to a contiguous block of memory +template struct span { + T const *ptr; + size_t length; + + constexpr span(T const *_ptr, size_t _length) : ptr(_ptr), length(_length) {} + + constexpr span() : ptr(nullptr), length(0) {} + + constexpr size_t len() const noexcept { return length; } + + FASTFLOAT_CONSTEXPR14 const T &operator[](size_t index) const noexcept { + FASTFLOAT_DEBUG_ASSERT(index < length); + return ptr[index]; + } +}; + +struct value128 { + uint64_t low; + uint64_t high; + + constexpr value128(uint64_t _low, uint64_t _high) : low(_low), high(_high) {} + + constexpr value128() : low(0), high(0) {} +}; + +/* Helper C++14 constexpr generic implementation of leading_zeroes */ +fastfloat_really_inline FASTFLOAT_CONSTEXPR14 int +leading_zeroes_generic(uint64_t input_num, int last_bit = 0) { + if (input_num & uint64_t(0xffffffff00000000)) { + input_num >>= 32; + last_bit |= 32; + } + if (input_num & uint64_t(0xffff0000)) { + input_num >>= 16; + last_bit |= 16; + } + if (input_num & uint64_t(0xff00)) { + input_num >>= 8; + last_bit |= 8; + } + if (input_num & uint64_t(0xf0)) { + input_num >>= 4; + last_bit |= 4; + } + if (input_num & uint64_t(0xc)) { + input_num >>= 2; + last_bit |= 2; + } + if (input_num & uint64_t(0x2)) { /* input_num >>= 1; */ + last_bit |= 1; + } + return 63 - last_bit; +} + +/* result might be undefined when input_num is zero */ +fastfloat_really_inline FASTFLOAT_CONSTEXPR20 int +leading_zeroes(uint64_t input_num) { + assert(input_num > 0); + if (cpp20_and_in_constexpr()) { + return leading_zeroes_generic(input_num); + } +#ifdef FASTFLOAT_VISUAL_STUDIO +#if defined(_M_X64) || defined(_M_ARM64) + unsigned long leading_zero = 0; + // Search the mask data from most significant bit (MSB) + // to least significant bit (LSB) for a set bit (1). + _BitScanReverse64(&leading_zero, input_num); + return (int)(63 - leading_zero); +#else + return leading_zeroes_generic(input_num); +#endif +#else + return __builtin_clzll(input_num); +#endif +} + +// slow emulation routine for 32-bit +fastfloat_really_inline constexpr uint64_t emulu(uint32_t x, uint32_t y) { + return x * (uint64_t)y; +} + +fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t +umul128_generic(uint64_t ab, uint64_t cd, uint64_t *hi) { + uint64_t ad = emulu((uint32_t)(ab >> 32), (uint32_t)cd); + uint64_t bd = emulu((uint32_t)ab, (uint32_t)cd); + uint64_t adbc = ad + emulu((uint32_t)ab, (uint32_t)(cd >> 32)); + uint64_t adbc_carry = (uint64_t)(adbc < ad); + uint64_t lo = bd + (adbc << 32); + *hi = emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) + + (adbc_carry << 32) + (uint64_t)(lo < bd); + return lo; +} + +#ifdef FASTFLOAT_32BIT + +// slow emulation routine for 32-bit +#if !defined(__MINGW64__) +fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t _umul128(uint64_t ab, + uint64_t cd, + uint64_t *hi) { + return umul128_generic(ab, cd, hi); +} +#endif // !__MINGW64__ + +#endif // FASTFLOAT_32BIT + +// compute 64-bit a*b +fastfloat_really_inline FASTFLOAT_CONSTEXPR20 value128 +full_multiplication(uint64_t a, uint64_t b) { + if (cpp20_and_in_constexpr()) { + value128 answer; + answer.low = umul128_generic(a, b, &answer.high); + return answer; + } + value128 answer; +#if defined(_M_ARM64) && !defined(__MINGW32__) + // ARM64 has native support for 64-bit multiplications, no need to emulate + // But MinGW on ARM64 doesn't have native support for 64-bit multiplications + answer.high = __umulh(a, b); + answer.low = a * b; +#elif defined(FASTFLOAT_32BIT) || \ + (defined(_WIN64) && !defined(__clang__) && !defined(_M_ARM64)) + answer.low = _umul128(a, b, &answer.high); // _umul128 not available on ARM64 +#elif defined(FASTFLOAT_64BIT) && defined(__SIZEOF_INT128__) + __uint128_t r = ((__uint128_t)a) * b; + answer.low = uint64_t(r); + answer.high = uint64_t(r >> 64); +#else + answer.low = umul128_generic(a, b, &answer.high); +#endif + return answer; +} + +struct adjusted_mantissa { + uint64_t mantissa{0}; + int32_t power2{0}; // a negative value indicates an invalid result + adjusted_mantissa() = default; + + constexpr bool operator==(adjusted_mantissa const &o) const { + return mantissa == o.mantissa && power2 == o.power2; + } + + constexpr bool operator!=(adjusted_mantissa const &o) const { + return mantissa != o.mantissa || power2 != o.power2; + } +}; + +// Bias so we can get the real exponent with an invalid adjusted_mantissa. +constexpr static int32_t invalid_am_bias = -0x8000; + +// used for binary_format_lookup_tables::max_mantissa +constexpr uint64_t constant_55555 = 5 * 5 * 5 * 5 * 5; + +template struct binary_format_lookup_tables; + +template struct binary_format : binary_format_lookup_tables { + using equiv_uint = equiv_uint_t; + + static constexpr int mantissa_explicit_bits(); + static constexpr int minimum_exponent(); + static constexpr int infinite_power(); + static constexpr int sign_index(); + static constexpr int + min_exponent_fast_path(); // used when fegetround() == FE_TONEAREST + static constexpr int max_exponent_fast_path(); + static constexpr int max_exponent_round_to_even(); + static constexpr int min_exponent_round_to_even(); + static constexpr uint64_t max_mantissa_fast_path(int64_t power); + static constexpr uint64_t + max_mantissa_fast_path(); // used when fegetround() == FE_TONEAREST + static constexpr int largest_power_of_ten(); + static constexpr int smallest_power_of_ten(); + static constexpr T exact_power_of_ten(int64_t power); + static constexpr size_t max_digits(); + static constexpr equiv_uint exponent_mask(); + static constexpr equiv_uint mantissa_mask(); + static constexpr equiv_uint hidden_bit_mask(); +}; + +template struct binary_format_lookup_tables { + static constexpr double powers_of_ten[] = { + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, + 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22}; + + // Largest integer value v so that (5**index * v) <= 1<<53. + // 0x20000000000000 == 1 << 53 + static constexpr uint64_t max_mantissa[] = { + 0x20000000000000, + 0x20000000000000 / 5, + 0x20000000000000 / (5 * 5), + 0x20000000000000 / (5 * 5 * 5), + 0x20000000000000 / (5 * 5 * 5 * 5), + 0x20000000000000 / (constant_55555), + 0x20000000000000 / (constant_55555 * 5), + 0x20000000000000 / (constant_55555 * 5 * 5), + 0x20000000000000 / (constant_55555 * 5 * 5 * 5), + 0x20000000000000 / (constant_55555 * 5 * 5 * 5 * 5), + 0x20000000000000 / (constant_55555 * constant_55555), + 0x20000000000000 / (constant_55555 * constant_55555 * 5), + 0x20000000000000 / (constant_55555 * constant_55555 * 5 * 5), + 0x20000000000000 / (constant_55555 * constant_55555 * 5 * 5 * 5), + 0x20000000000000 / (constant_55555 * constant_55555 * constant_55555), + 0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5), + 0x20000000000000 / + (constant_55555 * constant_55555 * constant_55555 * 5 * 5), + 0x20000000000000 / + (constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5), + 0x20000000000000 / + (constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5 * 5), + 0x20000000000000 / + (constant_55555 * constant_55555 * constant_55555 * constant_55555), + 0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * + constant_55555 * 5), + 0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * + constant_55555 * 5 * 5), + 0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * + constant_55555 * 5 * 5 * 5), + 0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * + constant_55555 * 5 * 5 * 5 * 5)}; +}; + +#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE + +template +constexpr double binary_format_lookup_tables::powers_of_ten[]; + +template +constexpr uint64_t binary_format_lookup_tables::max_mantissa[]; + +#endif + +template struct binary_format_lookup_tables { + static constexpr float powers_of_ten[] = {1e0f, 1e1f, 1e2f, 1e3f, 1e4f, 1e5f, + 1e6f, 1e7f, 1e8f, 1e9f, 1e10f}; + + // Largest integer value v so that (5**index * v) <= 1<<24. + // 0x1000000 == 1<<24 + static constexpr uint64_t max_mantissa[] = { + 0x1000000, + 0x1000000 / 5, + 0x1000000 / (5 * 5), + 0x1000000 / (5 * 5 * 5), + 0x1000000 / (5 * 5 * 5 * 5), + 0x1000000 / (constant_55555), + 0x1000000 / (constant_55555 * 5), + 0x1000000 / (constant_55555 * 5 * 5), + 0x1000000 / (constant_55555 * 5 * 5 * 5), + 0x1000000 / (constant_55555 * 5 * 5 * 5 * 5), + 0x1000000 / (constant_55555 * constant_55555), + 0x1000000 / (constant_55555 * constant_55555 * 5)}; +}; + +#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE + +template +constexpr float binary_format_lookup_tables::powers_of_ten[]; + +template +constexpr uint64_t binary_format_lookup_tables::max_mantissa[]; + +#endif + +template <> +inline constexpr int binary_format::min_exponent_fast_path() { +#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) + return 0; +#else + return -22; +#endif +} + +template <> +inline constexpr int binary_format::min_exponent_fast_path() { +#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) + return 0; +#else + return -10; +#endif +} + +template <> +inline constexpr int binary_format::mantissa_explicit_bits() { + return 52; +} + +template <> +inline constexpr int binary_format::mantissa_explicit_bits() { + return 23; +} + +template <> +inline constexpr int binary_format::max_exponent_round_to_even() { + return 23; +} + +template <> +inline constexpr int binary_format::max_exponent_round_to_even() { + return 10; +} + +template <> +inline constexpr int binary_format::min_exponent_round_to_even() { + return -4; +} + +template <> +inline constexpr int binary_format::min_exponent_round_to_even() { + return -17; +} + +template <> inline constexpr int binary_format::minimum_exponent() { + return -1023; +} + +template <> inline constexpr int binary_format::minimum_exponent() { + return -127; +} + +template <> inline constexpr int binary_format::infinite_power() { + return 0x7FF; +} + +template <> inline constexpr int binary_format::infinite_power() { + return 0xFF; +} + +template <> inline constexpr int binary_format::sign_index() { + return 63; +} + +template <> inline constexpr int binary_format::sign_index() { + return 31; +} + +template <> +inline constexpr int binary_format::max_exponent_fast_path() { + return 22; +} + +template <> +inline constexpr int binary_format::max_exponent_fast_path() { + return 10; +} + +template <> +inline constexpr uint64_t binary_format::max_mantissa_fast_path() { + return uint64_t(2) << mantissa_explicit_bits(); +} + +template <> +inline constexpr uint64_t binary_format::max_mantissa_fast_path() { + return uint64_t(2) << mantissa_explicit_bits(); +} + +// credit: Jakub Jelínek +#ifdef __STDCPP_FLOAT16_T__ +template struct binary_format_lookup_tables { + static constexpr std::float16_t powers_of_ten[] = {1e0f16, 1e1f16, 1e2f16, + 1e3f16, 1e4f16}; + + // Largest integer value v so that (5**index * v) <= 1<<11. + // 0x800 == 1<<11 + static constexpr uint64_t max_mantissa[] = {0x800, + 0x800 / 5, + 0x800 / (5 * 5), + 0x800 / (5 * 5 * 5), + 0x800 / (5 * 5 * 5 * 5), + 0x800 / (constant_55555)}; +}; + +#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE + +template +constexpr std::float16_t + binary_format_lookup_tables::powers_of_ten[]; + +template +constexpr uint64_t + binary_format_lookup_tables::max_mantissa[]; + +#endif + +template <> +inline constexpr std::float16_t +binary_format::exact_power_of_ten(int64_t power) { + // Work around clang bug https://godbolt.org/z/zedh7rrhc + return (void)powers_of_ten[0], powers_of_ten[power]; +} + +template <> +inline constexpr binary_format::equiv_uint +binary_format::exponent_mask() { + return 0x7C00; +} + +template <> +inline constexpr binary_format::equiv_uint +binary_format::mantissa_mask() { + return 0x03FF; +} + +template <> +inline constexpr binary_format::equiv_uint +binary_format::hidden_bit_mask() { + return 0x0400; +} + +template <> +inline constexpr int binary_format::max_exponent_fast_path() { + return 4; +} + +template <> +inline constexpr int binary_format::mantissa_explicit_bits() { + return 10; +} + +template <> +inline constexpr uint64_t +binary_format::max_mantissa_fast_path() { + return uint64_t(2) << mantissa_explicit_bits(); +} + +template <> +inline constexpr uint64_t +binary_format::max_mantissa_fast_path(int64_t power) { + // caller is responsible to ensure that + // power >= 0 && power <= 4 + // + // Work around clang bug https://godbolt.org/z/zedh7rrhc + return (void)max_mantissa[0], max_mantissa[power]; +} + +template <> +inline constexpr int binary_format::min_exponent_fast_path() { + return 0; +} + +template <> +inline constexpr int +binary_format::max_exponent_round_to_even() { + return 5; +} + +template <> +inline constexpr int +binary_format::min_exponent_round_to_even() { + return -22; +} + +template <> +inline constexpr int binary_format::minimum_exponent() { + return -15; +} + +template <> +inline constexpr int binary_format::infinite_power() { + return 0x1F; +} + +template <> inline constexpr int binary_format::sign_index() { + return 15; +} + +template <> +inline constexpr int binary_format::largest_power_of_ten() { + return 4; +} + +template <> +inline constexpr int binary_format::smallest_power_of_ten() { + return -27; +} + +template <> +inline constexpr size_t binary_format::max_digits() { + return 22; +} +#endif // __STDCPP_FLOAT16_T__ + +// credit: Jakub Jelínek +#ifdef __STDCPP_BFLOAT16_T__ +template struct binary_format_lookup_tables { + static constexpr std::bfloat16_t powers_of_ten[] = {1e0bf16, 1e1bf16, 1e2bf16, + 1e3bf16}; + + // Largest integer value v so that (5**index * v) <= 1<<8. + // 0x100 == 1<<8 + static constexpr uint64_t max_mantissa[] = {0x100, 0x100 / 5, 0x100 / (5 * 5), + 0x100 / (5 * 5 * 5), + 0x100 / (5 * 5 * 5 * 5)}; +}; + +#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE + +template +constexpr std::bfloat16_t + binary_format_lookup_tables::powers_of_ten[]; + +template +constexpr uint64_t + binary_format_lookup_tables::max_mantissa[]; + +#endif + +template <> +inline constexpr std::bfloat16_t +binary_format::exact_power_of_ten(int64_t power) { + // Work around clang bug https://godbolt.org/z/zedh7rrhc + return (void)powers_of_ten[0], powers_of_ten[power]; +} + +template <> +inline constexpr int binary_format::max_exponent_fast_path() { + return 3; +} + +template <> +inline constexpr binary_format::equiv_uint +binary_format::exponent_mask() { + return 0x7F80; +} + +template <> +inline constexpr binary_format::equiv_uint +binary_format::mantissa_mask() { + return 0x007F; +} + +template <> +inline constexpr binary_format::equiv_uint +binary_format::hidden_bit_mask() { + return 0x0080; +} + +template <> +inline constexpr int binary_format::mantissa_explicit_bits() { + return 7; +} + +template <> +inline constexpr uint64_t +binary_format::max_mantissa_fast_path() { + return uint64_t(2) << mantissa_explicit_bits(); +} + +template <> +inline constexpr uint64_t +binary_format::max_mantissa_fast_path(int64_t power) { + // caller is responsible to ensure that + // power >= 0 && power <= 3 + // + // Work around clang bug https://godbolt.org/z/zedh7rrhc + return (void)max_mantissa[0], max_mantissa[power]; +} + +template <> +inline constexpr int binary_format::min_exponent_fast_path() { + return 0; +} + +template <> +inline constexpr int +binary_format::max_exponent_round_to_even() { + return 3; +} + +template <> +inline constexpr int +binary_format::min_exponent_round_to_even() { + return -24; +} + +template <> +inline constexpr int binary_format::minimum_exponent() { + return -127; +} + +template <> +inline constexpr int binary_format::infinite_power() { + return 0xFF; +} + +template <> inline constexpr int binary_format::sign_index() { + return 15; +} + +template <> +inline constexpr int binary_format::largest_power_of_ten() { + return 38; +} + +template <> +inline constexpr int binary_format::smallest_power_of_ten() { + return -60; +} + +template <> +inline constexpr size_t binary_format::max_digits() { + return 98; +} +#endif // __STDCPP_BFLOAT16_T__ + +template <> +inline constexpr uint64_t +binary_format::max_mantissa_fast_path(int64_t power) { + // caller is responsible to ensure that + // power >= 0 && power <= 22 + // + // Work around clang bug https://godbolt.org/z/zedh7rrhc + return (void)max_mantissa[0], max_mantissa[power]; +} + +template <> +inline constexpr uint64_t +binary_format::max_mantissa_fast_path(int64_t power) { + // caller is responsible to ensure that + // power >= 0 && power <= 10 + // + // Work around clang bug https://godbolt.org/z/zedh7rrhc + return (void)max_mantissa[0], max_mantissa[power]; +} + +template <> +inline constexpr double +binary_format::exact_power_of_ten(int64_t power) { + // Work around clang bug https://godbolt.org/z/zedh7rrhc + return (void)powers_of_ten[0], powers_of_ten[power]; +} + +template <> +inline constexpr float binary_format::exact_power_of_ten(int64_t power) { + // Work around clang bug https://godbolt.org/z/zedh7rrhc + return (void)powers_of_ten[0], powers_of_ten[power]; +} + +template <> inline constexpr int binary_format::largest_power_of_ten() { + return 308; +} + +template <> inline constexpr int binary_format::largest_power_of_ten() { + return 38; +} + +template <> +inline constexpr int binary_format::smallest_power_of_ten() { + return -342; +} + +template <> inline constexpr int binary_format::smallest_power_of_ten() { + return -64; +} + +template <> inline constexpr size_t binary_format::max_digits() { + return 769; +} + +template <> inline constexpr size_t binary_format::max_digits() { + return 114; +} + +template <> +inline constexpr binary_format::equiv_uint +binary_format::exponent_mask() { + return 0x7F800000; +} + +template <> +inline constexpr binary_format::equiv_uint +binary_format::exponent_mask() { + return 0x7FF0000000000000; +} + +template <> +inline constexpr binary_format::equiv_uint +binary_format::mantissa_mask() { + return 0x007FFFFF; +} + +template <> +inline constexpr binary_format::equiv_uint +binary_format::mantissa_mask() { + return 0x000FFFFFFFFFFFFF; +} + +template <> +inline constexpr binary_format::equiv_uint +binary_format::hidden_bit_mask() { + return 0x00800000; +} + +template <> +inline constexpr binary_format::equiv_uint +binary_format::hidden_bit_mask() { + return 0x0010000000000000; +} + +template +fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void +to_float(bool negative, adjusted_mantissa am, T &value) { + using equiv_uint = equiv_uint_t; + equiv_uint word = equiv_uint(am.mantissa); + word = equiv_uint(word | equiv_uint(am.power2) + << binary_format::mantissa_explicit_bits()); + word = + equiv_uint(word | equiv_uint(negative) << binary_format::sign_index()); +#if FASTFLOAT_HAS_BIT_CAST + value = std::bit_cast(word); +#else + ::memcpy(&value, &word, sizeof(T)); +#endif +} + +template struct space_lut { + static constexpr bool value[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +}; + +#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE + +template constexpr bool space_lut::value[]; + +#endif + +template constexpr bool is_space(UC c) { + return c < 256 && space_lut<>::value[uint8_t(c)]; +} + +template static constexpr uint64_t int_cmp_zeros() { + static_assert((sizeof(UC) == 1) || (sizeof(UC) == 2) || (sizeof(UC) == 4), + "Unsupported character size"); + return (sizeof(UC) == 1) ? 0x3030303030303030 + : (sizeof(UC) == 2) + ? (uint64_t(UC('0')) << 48 | uint64_t(UC('0')) << 32 | + uint64_t(UC('0')) << 16 | UC('0')) + : (uint64_t(UC('0')) << 32 | UC('0')); +} + +template static constexpr int int_cmp_len() { + return sizeof(uint64_t) / sizeof(UC); +} + +template constexpr UC const *str_const_nan(); + +template <> constexpr char const *str_const_nan() { return "nan"; } + +template <> constexpr wchar_t const *str_const_nan() { return L"nan"; } + +template <> constexpr char16_t const *str_const_nan() { + return u"nan"; +} + +template <> constexpr char32_t const *str_const_nan() { + return U"nan"; +} + +#ifdef __cpp_char8_t +template <> constexpr char8_t const *str_const_nan() { + return u8"nan"; +} +#endif + +template constexpr UC const *str_const_inf(); + +template <> constexpr char const *str_const_inf() { return "infinity"; } + +template <> constexpr wchar_t const *str_const_inf() { + return L"infinity"; +} + +template <> constexpr char16_t const *str_const_inf() { + return u"infinity"; +} + +template <> constexpr char32_t const *str_const_inf() { + return U"infinity"; +} + +#ifdef __cpp_char8_t +template <> constexpr char8_t const *str_const_inf() { + return u8"infinity"; +} +#endif + +template struct int_luts { + static constexpr uint8_t chdigit[] = { + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, + 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255}; + + static constexpr size_t maxdigits_u64[] = { + 64, 41, 32, 28, 25, 23, 22, 21, 20, 19, 18, 18, 17, 17, 16, 16, 16, 16, + 15, 15, 15, 15, 14, 14, 14, 14, 14, 14, 14, 13, 13, 13, 13, 13, 13}; + + static constexpr uint64_t min_safe_u64[] = { + 9223372036854775808ull, 12157665459056928801ull, 4611686018427387904, + 7450580596923828125, 4738381338321616896, 3909821048582988049, + 9223372036854775808ull, 12157665459056928801ull, 10000000000000000000ull, + 5559917313492231481, 2218611106740436992, 8650415919381337933, + 2177953337809371136, 6568408355712890625, 1152921504606846976, + 2862423051509815793, 6746640616477458432, 15181127029874798299ull, + 1638400000000000000, 3243919932521508681, 6221821273427820544, + 11592836324538749809ull, 876488338465357824, 1490116119384765625, + 2481152873203736576, 4052555153018976267, 6502111422497947648, + 10260628712958602189ull, 15943230000000000000ull, 787662783788549761, + 1152921504606846976, 1667889514952984961, 2386420683693101056, + 3379220508056640625, 4738381338321616896}; +}; + +#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE + +template constexpr uint8_t int_luts::chdigit[]; + +template constexpr size_t int_luts::maxdigits_u64[]; + +template constexpr uint64_t int_luts::min_safe_u64[]; + +#endif + +template +fastfloat_really_inline constexpr uint8_t ch_to_digit(UC c) { + return int_luts<>::chdigit[static_cast(c)]; +} + +fastfloat_really_inline constexpr size_t max_digits_u64(int base) { + return int_luts<>::maxdigits_u64[base - 2]; +} + +// If a u64 is exactly max_digits_u64() in length, this is +// the value below which it has definitely overflowed. +fastfloat_really_inline constexpr uint64_t min_safe_u64(int base) { + return int_luts<>::min_safe_u64[base - 2]; +} + +static_assert(std::is_same, uint64_t>::value, + "equiv_uint should be uint64_t for double"); +static_assert(std::numeric_limits::is_iec559, + "double must fulfill the requirements of IEC 559 (IEEE 754)"); + +static_assert(std::is_same, uint32_t>::value, + "equiv_uint should be uint32_t for float"); +static_assert(std::numeric_limits::is_iec559, + "float must fulfill the requirements of IEC 559 (IEEE 754)"); + +#ifdef __STDCPP_FLOAT64_T__ +static_assert(std::is_same, uint64_t>::value, + "equiv_uint should be uint64_t for std::float64_t"); +static_assert( + std::numeric_limits::is_iec559, + "std::float64_t must fulfill the requirements of IEC 559 (IEEE 754)"); +#endif // __STDCPP_FLOAT64_T__ + +#ifdef __STDCPP_FLOAT32_T__ +static_assert(std::is_same, uint32_t>::value, + "equiv_uint should be uint32_t for std::float32_t"); +static_assert( + std::numeric_limits::is_iec559, + "std::float32_t must fulfill the requirements of IEC 559 (IEEE 754)"); +#endif // __STDCPP_FLOAT32_T__ + +#ifdef __STDCPP_FLOAT16_T__ +static_assert( + std::is_same::equiv_uint, uint16_t>::value, + "equiv_uint should be uint16_t for std::float16_t"); +static_assert( + std::numeric_limits::is_iec559, + "std::float16_t must fulfill the requirements of IEC 559 (IEEE 754)"); +#endif // __STDCPP_FLOAT16_T__ + +#ifdef __STDCPP_BFLOAT16_T__ +static_assert( + std::is_same::equiv_uint, uint16_t>::value, + "equiv_uint should be uint16_t for std::bfloat16_t"); +static_assert( + std::numeric_limits::is_iec559, + "std::bfloat16_t must fulfill the requirements of IEC 559 (IEEE 754)"); +#endif // __STDCPP_BFLOAT16_T__ + +constexpr chars_format operator~(chars_format rhs) noexcept { + using int_type = std::underlying_type::type; + return static_cast(~static_cast(rhs)); +} + +constexpr chars_format operator&(chars_format lhs, chars_format rhs) noexcept { + using int_type = std::underlying_type::type; + return static_cast(static_cast(lhs) & + static_cast(rhs)); +} + +constexpr chars_format operator|(chars_format lhs, chars_format rhs) noexcept { + using int_type = std::underlying_type::type; + return static_cast(static_cast(lhs) | + static_cast(rhs)); +} + +constexpr chars_format operator^(chars_format lhs, chars_format rhs) noexcept { + using int_type = std::underlying_type::type; + return static_cast(static_cast(lhs) ^ + static_cast(rhs)); +} + +fastfloat_really_inline FASTFLOAT_CONSTEXPR14 chars_format & +operator&=(chars_format &lhs, chars_format rhs) noexcept { + return lhs = (lhs & rhs); +} + +fastfloat_really_inline FASTFLOAT_CONSTEXPR14 chars_format & +operator|=(chars_format &lhs, chars_format rhs) noexcept { + return lhs = (lhs | rhs); +} + +fastfloat_really_inline FASTFLOAT_CONSTEXPR14 chars_format & +operator^=(chars_format &lhs, chars_format rhs) noexcept { + return lhs = (lhs ^ rhs); +} + +namespace detail { +// adjust for deprecated feature macros +constexpr chars_format adjust_for_feature_macros(chars_format fmt) { + return fmt +#ifdef FASTFLOAT_ALLOWS_LEADING_PLUS + | chars_format::allow_leading_plus +#endif +#ifdef FASTFLOAT_SKIP_WHITE_SPACE + | chars_format::skip_white_space +#endif + ; +} +} // namespace detail + +} // namespace fast_float + +#endif diff --git a/3rdparty/fast_float/parse_number.h b/3rdparty/fast_float/parse_number.h new file mode 100644 index 000000000000..0dbb3a143a20 --- /dev/null +++ b/3rdparty/fast_float/parse_number.h @@ -0,0 +1,399 @@ +#ifndef FASTFLOAT_PARSE_NUMBER_H +#define FASTFLOAT_PARSE_NUMBER_H + +#include "ascii_number.h" +#include "decimal_to_binary.h" +#include "digit_comparison.h" +#include "float_common.h" + +#include +#include +#include +#include + +namespace fast_float { + +namespace detail { +/** + * Special case +inf, -inf, nan, infinity, -infinity. + * The case comparisons could be made much faster given that we know that the + * strings a null-free and fixed. + **/ +template +from_chars_result_t + FASTFLOAT_CONSTEXPR14 parse_infnan(UC const *first, UC const *last, + T &value, chars_format fmt) noexcept { + from_chars_result_t answer{}; + answer.ptr = first; + answer.ec = std::errc(); // be optimistic + // assume first < last, so dereference without checks; + bool const minusSign = (*first == UC('-')); + // C++17 20.19.3.(7.1) explicitly forbids '+' sign here + if ((*first == UC('-')) || + (uint64_t(fmt & chars_format::allow_leading_plus) && + (*first == UC('+')))) { + ++first; + } + if (last - first >= 3) { + if (fastfloat_strncasecmp(first, str_const_nan(), 3)) { + answer.ptr = (first += 3); + value = minusSign ? -std::numeric_limits::quiet_NaN() + : std::numeric_limits::quiet_NaN(); + // Check for possible nan(n-char-seq-opt), C++17 20.19.3.7, + // C11 7.20.1.3.3. At least MSVC produces nan(ind) and nan(snan). + if (first != last && *first == UC('(')) { + for (UC const *ptr = first + 1; ptr != last; ++ptr) { + if (*ptr == UC(')')) { + answer.ptr = ptr + 1; // valid nan(n-char-seq-opt) + break; + } else if (!((UC('a') <= *ptr && *ptr <= UC('z')) || + (UC('A') <= *ptr && *ptr <= UC('Z')) || + (UC('0') <= *ptr && *ptr <= UC('9')) || *ptr == UC('_'))) + break; // forbidden char, not nan(n-char-seq-opt) + } + } + return answer; + } + if (fastfloat_strncasecmp(first, str_const_inf(), 3)) { + if ((last - first >= 8) && + fastfloat_strncasecmp(first + 3, str_const_inf() + 3, 5)) { + answer.ptr = first + 8; + } else { + answer.ptr = first + 3; + } + value = minusSign ? -std::numeric_limits::infinity() + : std::numeric_limits::infinity(); + return answer; + } + } + answer.ec = std::errc::invalid_argument; + return answer; +} + +/** + * Returns true if the floating-pointing rounding mode is to 'nearest'. + * It is the default on most system. This function is meant to be inexpensive. + * Credit : @mwalcott3 + */ +fastfloat_really_inline bool rounds_to_nearest() noexcept { + // https://lemire.me/blog/2020/06/26/gcc-not-nearest/ +#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) + return false; +#endif + // See + // A fast function to check your floating-point rounding mode + // https://lemire.me/blog/2022/11/16/a-fast-function-to-check-your-floating-point-rounding-mode/ + // + // This function is meant to be equivalent to : + // prior: #include + // return fegetround() == FE_TONEAREST; + // However, it is expected to be much faster than the fegetround() + // function call. + // + // The volatile keyword prevents the compiler from computing the function + // at compile-time. + // There might be other ways to prevent compile-time optimizations (e.g., + // asm). The value does not need to be std::numeric_limits::min(), any + // small value so that 1 + x should round to 1 would do (after accounting for + // excess precision, as in 387 instructions). + static float volatile fmin = std::numeric_limits::min(); + float fmini = fmin; // we copy it so that it gets loaded at most once. +// +// Explanation: +// Only when fegetround() == FE_TONEAREST do we have that +// fmin + 1.0f == 1.0f - fmin. +// +// FE_UPWARD: +// fmin + 1.0f > 1 +// 1.0f - fmin == 1 +// +// FE_DOWNWARD or FE_TOWARDZERO: +// fmin + 1.0f == 1 +// 1.0f - fmin < 1 +// +// Note: This may fail to be accurate if fast-math has been +// enabled, as rounding conventions may not apply. +#ifdef FASTFLOAT_VISUAL_STUDIO +#pragma warning(push) +// todo: is there a VS warning? +// see +// https://stackoverflow.com/questions/46079446/is-there-a-warning-for-floating-point-equality-checking-in-visual-studio-2013 +#elif defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wfloat-equal" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + return (fmini + 1.0f == 1.0f - fmini); +#ifdef FASTFLOAT_VISUAL_STUDIO +#pragma warning(pop) +#elif defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif +} + +} // namespace detail + +template struct from_chars_caller { + template + FASTFLOAT_CONSTEXPR20 static from_chars_result_t + call(UC const *first, UC const *last, T &value, + parse_options_t options) noexcept { + return from_chars_advanced(first, last, value, options); + } +}; + +#ifdef __STDCPP_FLOAT32_T__ +template <> struct from_chars_caller { + template + FASTFLOAT_CONSTEXPR20 static from_chars_result_t + call(UC const *first, UC const *last, std::float32_t &value, + parse_options_t options) noexcept { + // if std::float32_t is defined, and we are in C++23 mode; macro set for + // float32; set value to float due to equivalence between float and + // float32_t + float val; + auto ret = from_chars_advanced(first, last, val, options); + value = val; + return ret; + } +}; +#endif + +#ifdef __STDCPP_FLOAT64_T__ +template <> struct from_chars_caller { + template + FASTFLOAT_CONSTEXPR20 static from_chars_result_t + call(UC const *first, UC const *last, std::float64_t &value, + parse_options_t options) noexcept { + // if std::float64_t is defined, and we are in C++23 mode; macro set for + // float64; set value as double due to equivalence between double and + // float64_t + double val; + auto ret = from_chars_advanced(first, last, val, options); + value = val; + return ret; + } +}; +#endif + +template +FASTFLOAT_CONSTEXPR20 from_chars_result_t +from_chars(UC const *first, UC const *last, T &value, + chars_format fmt /*= chars_format::general*/) noexcept { + return from_chars_caller::call(first, last, value, + parse_options_t(fmt)); +} + +/** + * This function overload takes parsed_number_string_t structure that is created + * and populated either by from_chars_advanced function taking chars range and + * parsing options or other parsing custom function implemented by user. + */ +template +FASTFLOAT_CONSTEXPR20 from_chars_result_t +from_chars_advanced(parsed_number_string_t &pns, T &value) noexcept { + + static_assert(is_supported_float_type::value, + "only some floating-point types are supported"); + static_assert(is_supported_char_type::value, + "only char, wchar_t, char16_t and char32_t are supported"); + + from_chars_result_t answer; + + answer.ec = std::errc(); // be optimistic + answer.ptr = pns.lastmatch; + // The implementation of the Clinger's fast path is convoluted because + // we want round-to-nearest in all cases, irrespective of the rounding mode + // selected on the thread. + // We proceed optimistically, assuming that detail::rounds_to_nearest() + // returns true. + if (binary_format::min_exponent_fast_path() <= pns.exponent && + pns.exponent <= binary_format::max_exponent_fast_path() && + !pns.too_many_digits) { + // Unfortunately, the conventional Clinger's fast path is only possible + // when the system rounds to the nearest float. + // + // We expect the next branch to almost always be selected. + // We could check it first (before the previous branch), but + // there might be performance advantages at having the check + // be last. + if (!cpp20_and_in_constexpr() && detail::rounds_to_nearest()) { + // We have that fegetround() == FE_TONEAREST. + // Next is Clinger's fast path. + if (pns.mantissa <= binary_format::max_mantissa_fast_path()) { + value = T(pns.mantissa); + if (pns.exponent < 0) { + value = value / binary_format::exact_power_of_ten(-pns.exponent); + } else { + value = value * binary_format::exact_power_of_ten(pns.exponent); + } + if (pns.negative) { + value = -value; + } + return answer; + } + } else { + // We do not have that fegetround() == FE_TONEAREST. + // Next is a modified Clinger's fast path, inspired by Jakub Jelínek's + // proposal + if (pns.exponent >= 0 && + pns.mantissa <= + binary_format::max_mantissa_fast_path(pns.exponent)) { +#if defined(__clang__) || defined(FASTFLOAT_32BIT) + // Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD + if (pns.mantissa == 0) { + value = pns.negative ? T(-0.) : T(0.); + return answer; + } +#endif + value = T(pns.mantissa) * + binary_format::exact_power_of_ten(pns.exponent); + if (pns.negative) { + value = -value; + } + return answer; + } + } + } + adjusted_mantissa am = + compute_float>(pns.exponent, pns.mantissa); + if (pns.too_many_digits && am.power2 >= 0) { + if (am != compute_float>(pns.exponent, pns.mantissa + 1)) { + am = compute_error>(pns.exponent, pns.mantissa); + } + } + // If we called compute_float>(pns.exponent, pns.mantissa) + // and we have an invalid power (am.power2 < 0), then we need to go the long + // way around again. This is very uncommon. + if (am.power2 < 0) { + am = digit_comp(pns, am); + } + to_float(pns.negative, am, value); + // Test for over/underflow. + if ((pns.mantissa != 0 && am.mantissa == 0 && am.power2 == 0) || + am.power2 == binary_format::infinite_power()) { + answer.ec = std::errc::result_out_of_range; + } + return answer; +} + +template +FASTFLOAT_CONSTEXPR20 from_chars_result_t +from_chars_float_advanced(UC const *first, UC const *last, T &value, + parse_options_t options) noexcept { + + static_assert(is_supported_float_type::value, + "only some floating-point types are supported"); + static_assert(is_supported_char_type::value, + "only char, wchar_t, char16_t and char32_t are supported"); + + chars_format const fmt = detail::adjust_for_feature_macros(options.format); + + from_chars_result_t answer; + if (uint64_t(fmt & chars_format::skip_white_space)) { + while ((first != last) && fast_float::is_space(*first)) { + first++; + } + } + if (first == last) { + answer.ec = std::errc::invalid_argument; + answer.ptr = first; + return answer; + } + parsed_number_string_t pns = + parse_number_string(first, last, options); + if (!pns.valid) { + if (uint64_t(fmt & chars_format::no_infnan)) { + answer.ec = std::errc::invalid_argument; + answer.ptr = first; + return answer; + } else { + return detail::parse_infnan(first, last, value, fmt); + } + } + + // call overload that takes parsed_number_string_t directly. + return from_chars_advanced(pns, value); +} + +template +FASTFLOAT_CONSTEXPR20 from_chars_result_t +from_chars(UC const *first, UC const *last, T &value, int base) noexcept { + + static_assert(is_supported_integer_type::value, + "only integer types are supported"); + static_assert(is_supported_char_type::value, + "only char, wchar_t, char16_t and char32_t are supported"); + + parse_options_t options; + options.base = base; + return from_chars_advanced(first, last, value, options); +} + +template +FASTFLOAT_CONSTEXPR20 from_chars_result_t +from_chars_int_advanced(UC const *first, UC const *last, T &value, + parse_options_t options) noexcept { + + static_assert(is_supported_integer_type::value, + "only integer types are supported"); + static_assert(is_supported_char_type::value, + "only char, wchar_t, char16_t and char32_t are supported"); + + chars_format const fmt = detail::adjust_for_feature_macros(options.format); + int const base = options.base; + + from_chars_result_t answer; + if (uint64_t(fmt & chars_format::skip_white_space)) { + while ((first != last) && fast_float::is_space(*first)) { + first++; + } + } + if (first == last || base < 2 || base > 36) { + answer.ec = std::errc::invalid_argument; + answer.ptr = first; + return answer; + } + + return parse_int_string(first, last, value, options); +} + +template struct from_chars_advanced_caller { + static_assert(TypeIx > 0, "unsupported type"); +}; + +template <> struct from_chars_advanced_caller<1> { + template + FASTFLOAT_CONSTEXPR20 static from_chars_result_t + call(UC const *first, UC const *last, T &value, + parse_options_t options) noexcept { + return from_chars_float_advanced(first, last, value, options); + } +}; + +template <> struct from_chars_advanced_caller<2> { + template + FASTFLOAT_CONSTEXPR20 static from_chars_result_t + call(UC const *first, UC const *last, T &value, + parse_options_t options) noexcept { + return from_chars_int_advanced(first, last, value, options); + } +}; + +template +FASTFLOAT_CONSTEXPR20 from_chars_result_t +from_chars_advanced(UC const *first, UC const *last, T &value, + parse_options_t options) noexcept { + return from_chars_advanced_caller< + size_t(is_supported_float_type::value) + + 2 * size_t(is_supported_integer_type::value)>::call(first, last, value, + options); +} + +} // namespace fast_float + +#endif diff --git a/3rdparty/ghc/filesystem.hpp b/3rdparty/ghc/filesystem.hpp deleted file mode 100644 index 53f4ad4efc09..000000000000 --- a/3rdparty/ghc/filesystem.hpp +++ /dev/null @@ -1,6083 +0,0 @@ -//--------------------------------------------------------------------------------------- -// -// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14/C++17/C++20 -// -//--------------------------------------------------------------------------------------- -// -// Copyright (c) 2018, Steffen Schümann -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -//--------------------------------------------------------------------------------------- -// -// To dynamically select std::filesystem where available on most platforms, -// you could use: -// -// #if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include) -// #if __has_include() && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) -// #define GHC_USE_STD_FS -// #include -// namespace fs = std::filesystem; -// #endif -// #endif -// #ifndef GHC_USE_STD_FS -// #include -// namespace fs = ghc::filesystem; -// #endif -// -//--------------------------------------------------------------------------------------- -#ifndef GHC_FILESYSTEM_H -#define GHC_FILESYSTEM_H - -// #define BSD manifest constant only in -// sys/param.h -#ifndef _WIN32 -#include -#endif - -#ifndef GHC_OS_DETECTED -#if defined(__APPLE__) && defined(__MACH__) -#define GHC_OS_MACOS -#elif defined(__linux__) -#define GHC_OS_LINUX -#if defined(__ANDROID__) -#define GHC_OS_ANDROID -#endif -#elif defined(_WIN64) -#define GHC_OS_WINDOWS -#define GHC_OS_WIN64 -#elif defined(_WIN32) -#define GHC_OS_WINDOWS -#define GHC_OS_WIN32 -#elif defined(__CYGWIN__) -#define GHC_OS_CYGWIN -#elif defined(__sun) && defined(__SVR4) -#define GHC_OS_SOLARIS -#elif defined(__svr4__) -#define GHC_OS_SYS5R4 -#elif defined(BSD) -#define GHC_OS_BSD -#elif defined(__EMSCRIPTEN__) -#define GHC_OS_WEB -#include -#elif defined(__QNX__) -#define GHC_OS_QNX -#elif defined(__HAIKU__) -#define GHC_OS_HAIKU -#else -#error "Operating system currently not supported!" -#endif -#define GHC_OS_DETECTED -#if (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) -#if _MSVC_LANG == 201703L -#define GHC_FILESYSTEM_RUNNING_CPP17 -#else -#define GHC_FILESYSTEM_RUNNING_CPP20 -#endif -#elif (defined(__cplusplus) && __cplusplus >= 201703L) -#if __cplusplus == 201703L -#define GHC_FILESYSTEM_RUNNING_CPP17 -#else -#define GHC_FILESYSTEM_RUNNING_CPP20 -#endif -#endif -#endif - -#if defined(GHC_FILESYSTEM_IMPLEMENTATION) -#define GHC_EXPAND_IMPL -#define GHC_INLINE -#ifdef GHC_OS_WINDOWS -#ifndef GHC_FS_API -#define GHC_FS_API -#endif -#ifndef GHC_FS_API_CLASS -#define GHC_FS_API_CLASS -#endif -#else -#ifndef GHC_FS_API -#define GHC_FS_API __attribute__((visibility("default"))) -#endif -#ifndef GHC_FS_API_CLASS -#define GHC_FS_API_CLASS __attribute__((visibility("default"))) -#endif -#endif -#elif defined(GHC_FILESYSTEM_FWD) -#define GHC_INLINE -#ifdef GHC_OS_WINDOWS -#ifndef GHC_FS_API -#define GHC_FS_API extern -#endif -#ifndef GHC_FS_API_CLASS -#define GHC_FS_API_CLASS -#endif -#else -#ifndef GHC_FS_API -#define GHC_FS_API extern -#endif -#ifndef GHC_FS_API_CLASS -#define GHC_FS_API_CLASS -#endif -#endif -#else -#define GHC_EXPAND_IMPL -#define GHC_INLINE inline -#ifndef GHC_FS_API -#define GHC_FS_API -#endif -#ifndef GHC_FS_API_CLASS -#define GHC_FS_API_CLASS -#endif -#endif - -#ifdef GHC_EXPAND_IMPL - -#ifdef GHC_OS_WINDOWS -#include -// additional includes -#include -#include -#include -#include -#include -#else -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef GHC_OS_ANDROID -#include -#if __ANDROID_API__ < 12 -#include -#endif -#include -#define statvfs statfs -#else -#include -#endif -#ifdef GHC_OS_CYGWIN -#include -#endif -#if !defined(__ANDROID__) || __ANDROID_API__ >= 26 -#include -#endif -#endif -#ifdef GHC_OS_MACOS -#include -#endif - -#if defined(__cpp_impl_three_way_comparison) && defined(__has_include) -#if __has_include() -#define GHC_HAS_THREEWAY_COMP -#include -#endif -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#else // GHC_EXPAND_IMPL - -#if defined(__cpp_impl_three_way_comparison) && defined(__has_include) -#if __has_include() -#define GHC_HAS_THREEWAY_COMP -#include -#endif -#endif -#include -#include -#include -#include -#include -#include -#include -#ifdef GHC_OS_WINDOWS -#include -#endif -#endif // GHC_EXPAND_IMPL - -// After standard library includes. -// Standard library support for std::string_view. -#if defined(__cpp_lib_string_view) -#define GHC_HAS_STD_STRING_VIEW -#elif defined(_LIBCPP_VERSION) && (_LIBCPP_VERSION >= 4000) && (__cplusplus >= 201402) -#define GHC_HAS_STD_STRING_VIEW -#elif defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE >= 7) && (__cplusplus >= 201703) -#define GHC_HAS_STD_STRING_VIEW -#elif defined(_MSC_VER) && (_MSC_VER >= 1910 && _MSVC_LANG >= 201703) -#define GHC_HAS_STD_STRING_VIEW -#endif - -// Standard library support for std::experimental::string_view. -#if defined(_LIBCPP_VERSION) && (_LIBCPP_VERSION >= 3700 && _LIBCPP_VERSION < 7000) && (__cplusplus >= 201402) -#define GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW -#elif defined(__GNUC__) && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 9)) || (__GNUC__ > 4)) && (__cplusplus >= 201402) -#define GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW -#elif defined(__GLIBCXX__) && defined(_GLIBCXX_USE_DUAL_ABI) && (__cplusplus >= 201402) -// macro _GLIBCXX_USE_DUAL_ABI is always defined in libstdc++ from gcc-5 and newer -#define GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW -#endif - -#if defined(GHC_HAS_STD_STRING_VIEW) -#include -#elif defined(GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW) -#include -#endif - -#if !defined(GHC_OS_WINDOWS) && !defined(PATH_MAX) -#define PATH_MAX 4096 -#endif - -//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// Behaviour Switches (see README.md, should match the config in test/filesystem_test.cpp): -//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// Enforce C++17 API where possible when compiling for C++20, handles the following cases: -// * fs::path::u8string() returns std::string instead of std::u8string -// #define GHC_FILESYSTEM_ENFORCE_CPP17_API -//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// LWG #2682 disables the since then invalid use of the copy option create_symlinks on directories -// configure LWG conformance () -#define LWG_2682_BEHAVIOUR -//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// LWG #2395 makes crate_directory/create_directories not emit an error if there is a regular -// file with that name, it is superseded by P1164R1, so only activate if really needed -// #define LWG_2935_BEHAVIOUR -//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// LWG #2936 enables new element wise (more expensive) path comparison -// * if this->root_name().native().compare(p.root_name().native()) != 0 return result -// * if this->has_root_directory() and !p.has_root_directory() return -1 -// * if !this->has_root_directory() and p.has_root_directory() return -1 -// * else result of element wise comparison of path iteration where first comparison is != 0 or 0 -// if all comparisons are 0 (on Windows this implementation does case-insensitive root_name() -// comparison) -#define LWG_2936_BEHAVIOUR -//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// LWG #2937 enforces that fs::equivalent emits an error, if !fs::exists(p1)||!exists(p2) -#define LWG_2937_BEHAVIOUR -//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// UTF8-Everywhere is the original behaviour of ghc::filesystem. But since v1.5 the Windows -// version defaults to std::wstring storage backend. Still all std::string will be interpreted -// as UTF-8 encoded. With this define you can enforce the old behavior on Windows, using -// std::string as backend and for fs::path::native() and char for fs::path::c_str(). This -// needs more conversions, so it is (and was before v1.5) slower, bot might help keeping source -// homogeneous in a multi-platform project. -// #define GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE -//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// Raise errors/exceptions when invalid unicode codepoints or UTF-8 sequences are found, -// instead of replacing them with the unicode replacement character (U+FFFD). -// #define GHC_RAISE_UNICODE_ERRORS -//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// Automatic prefix windows path with "\\?\" if they would break the MAX_PATH length. -// instead of replacing them with the unicode replacement character (U+FFFD). -#ifndef GHC_WIN_DISABLE_AUTO_PREFIXES -#define GHC_WIN_AUTO_PREFIX_LONG_PATH -#endif // GHC_WIN_DISABLE_AUTO_PREFIXES -//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -// ghc::filesystem version in decimal (major * 10000 + minor * 100 + patch) -#define GHC_FILESYSTEM_VERSION 10514L - -#if !defined(GHC_WITH_EXCEPTIONS) && (defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND)) -#define GHC_WITH_EXCEPTIONS -#endif -#if !defined(GHC_WITH_EXCEPTIONS) && defined(GHC_RAISE_UNICODE_ERRORS) -#error "Can't raise unicode errors with exception support disabled" -#endif - -namespace ghc { -namespace filesystem { - -#if defined(GHC_HAS_CUSTOM_STRING_VIEW) -#define GHC_WITH_STRING_VIEW -#elif defined(GHC_HAS_STD_STRING_VIEW) -#define GHC_WITH_STRING_VIEW -using std::basic_string_view; -#elif defined(GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW) -#define GHC_WITH_STRING_VIEW -using std::experimental::basic_string_view; -#endif - -// temporary existing exception type for yet unimplemented parts -class GHC_FS_API_CLASS not_implemented_exception : public std::logic_error -{ -public: - not_implemented_exception() - : std::logic_error("function not implemented yet.") - { - } -}; - -template -class path_helper_base -{ -public: - using value_type = char_type; -#ifdef GHC_OS_WINDOWS - static constexpr value_type preferred_separator = '\\'; -#else - static constexpr value_type preferred_separator = '/'; -#endif -}; - -#if __cplusplus < 201703L -template -constexpr char_type path_helper_base::preferred_separator; -#endif - -#ifdef GHC_OS_WINDOWS -class path; -namespace detail { -bool has_executable_extension(const path& p); -} -#endif - -// [fs.class.path] class path -class GHC_FS_API_CLASS path -#if defined(GHC_OS_WINDOWS) && !defined(GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE) -#define GHC_USE_WCHAR_T -#define GHC_NATIVEWP(p) p.c_str() -#define GHC_PLATFORM_LITERAL(str) L##str - : private path_helper_base -{ -public: - using path_helper_base::value_type; -#else -#define GHC_NATIVEWP(p) p.wstring().c_str() -#define GHC_PLATFORM_LITERAL(str) str - : private path_helper_base -{ -public: - using path_helper_base::value_type; -#endif - using string_type = std::basic_string; - using path_helper_base::preferred_separator; - - // [fs.enum.path.format] enumeration format - /// The path format in which the constructor argument is given. - enum format { - generic_format, ///< The generic format, internally used by - ///< ghc::filesystem::path with slashes - native_format, ///< The format native to the current platform this code - ///< is build for - auto_format, ///< Try to auto-detect the format, fallback to native - }; - - template - struct _is_basic_string : std::false_type - { - }; - template - struct _is_basic_string> : std::true_type - { - }; - template - struct _is_basic_string, std::allocator>> : std::true_type - { - }; -#ifdef GHC_WITH_STRING_VIEW - template - struct _is_basic_string> : std::true_type - { - }; - template - struct _is_basic_string>> : std::true_type - { - }; -#endif - - template - using path_type = typename std::enable_if::value, path>::type; - template -#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) - using path_from_string = - typename std::enable_if<_is_basic_string::value || std::is_same::type>::value || std::is_same::type>::value || std::is_same::type>::value || - std::is_same::type>::value || std::is_same::type>::value || std::is_same::type>::value || - std::is_same::type>::value || std::is_same::type>::value || std::is_same::type>::value || - std::is_same::type>::value, - path>::type; - template - using path_type_EcharT = typename std::enable_if::value || std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value, path>::type; -#else - using path_from_string = - typename std::enable_if<_is_basic_string::value || std::is_same::type>::value || std::is_same::type>::value || - std::is_same::type>::value || std::is_same::type>::value || std::is_same::type>::value || - std::is_same::type>::value || std::is_same::type>::value || std::is_same::type>::value, - path>::type; - template - using path_type_EcharT = typename std::enable_if::value || std::is_same::value || std::is_same::value || std::is_same::value, path>::type; -#endif - // [fs.path.construct] constructors and destructor - path() noexcept; - path(const path& p); - path(path&& p) noexcept; - path(string_type&& source, format fmt = auto_format); - template > - path(const Source& source, format fmt = auto_format); - template - path(InputIterator first, InputIterator last, format fmt = auto_format); -#ifdef GHC_WITH_EXCEPTIONS - template > - path(const Source& source, const std::locale& loc, format fmt = auto_format); - template - path(InputIterator first, InputIterator last, const std::locale& loc, format fmt = auto_format); -#endif - ~path(); - - // [fs.path.assign] assignments - path& operator=(const path& p); - path& operator=(path&& p) noexcept; - path& operator=(string_type&& source); - path& assign(string_type&& source); - template - path& operator=(const Source& source); - template - path& assign(const Source& source); - template - path& assign(InputIterator first, InputIterator last); - - // [fs.path.append] appends - path& operator/=(const path& p); - template - path& operator/=(const Source& source); - template - path& append(const Source& source); - template - path& append(InputIterator first, InputIterator last); - - // [fs.path.concat] concatenation - path& operator+=(const path& x); - path& operator+=(const string_type& x); -#ifdef GHC_WITH_STRING_VIEW - path& operator+=(basic_string_view x); -#endif - path& operator+=(const value_type* x); - path& operator+=(value_type x); - template - path_from_string& operator+=(const Source& x); - template - path_type_EcharT& operator+=(EcharT x); - template - path& concat(const Source& x); - template - path& concat(InputIterator first, InputIterator last); - - // [fs.path.modifiers] modifiers - void clear() noexcept; - path& make_preferred(); - path& remove_filename(); - path& replace_filename(const path& replacement); - path& replace_extension(const path& replacement = path()); - void swap(path& rhs) noexcept; - - // [fs.path.native.obs] native format observers - const string_type& native() const noexcept; - const value_type* c_str() const noexcept; - operator string_type() const; - template , class Allocator = std::allocator> - std::basic_string string(const Allocator& a = Allocator()) const; - std::string string() const; - std::wstring wstring() const; -#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) - std::u8string u8string() const; -#else - std::string u8string() const; -#endif - std::u16string u16string() const; - std::u32string u32string() const; - - // [fs.path.generic.obs] generic format observers - template , class Allocator = std::allocator> - std::basic_string generic_string(const Allocator& a = Allocator()) const; - std::string generic_string() const; - std::wstring generic_wstring() const; -#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) - std::u8string generic_u8string() const; -#else - std::string generic_u8string() const; -#endif - std::u16string generic_u16string() const; - std::u32string generic_u32string() const; - - // [fs.path.compare] compare - int compare(const path& p) const noexcept; - int compare(const string_type& s) const; -#ifdef GHC_WITH_STRING_VIEW - int compare(basic_string_view s) const; -#endif - int compare(const value_type* s) const; - - // [fs.path.decompose] decomposition - path root_name() const; - path root_directory() const; - path root_path() const; - path relative_path() const; - path parent_path() const; - path filename() const; - path stem() const; - path extension() const; - - // [fs.path.query] query - bool empty() const noexcept; - bool has_root_name() const; - bool has_root_directory() const; - bool has_root_path() const; - bool has_relative_path() const; - bool has_parent_path() const; - bool has_filename() const; - bool has_stem() const; - bool has_extension() const; - bool is_absolute() const; - bool is_relative() const; - - // [fs.path.gen] generation - path lexically_normal() const; - path lexically_relative(const path& base) const; - path lexically_proximate(const path& base) const; - - // [fs.path.itr] iterators - class iterator; - using const_iterator = iterator; - iterator begin() const; - iterator end() const; - -private: - using impl_value_type = value_type; - using impl_string_type = std::basic_string; - friend class directory_iterator; - void append_name(const value_type* name); - static constexpr impl_value_type generic_separator = '/'; - template - class input_iterator_range - { - public: - typedef InputIterator iterator; - typedef InputIterator const_iterator; - typedef typename InputIterator::difference_type difference_type; - - input_iterator_range(const InputIterator& first, const InputIterator& last) - : _first(first) - , _last(last) - { - } - - InputIterator begin() const { return _first; } - InputIterator end() const { return _last; } - - private: - InputIterator _first; - InputIterator _last; - }; - friend void swap(path& lhs, path& rhs) noexcept; - friend size_t hash_value(const path& p) noexcept; - friend path canonical(const path& p, std::error_code& ec); - friend bool create_directories(const path& p, std::error_code& ec) noexcept; - string_type::size_type root_name_length() const noexcept; - void postprocess_path_with_format(format fmt); - void check_long_path(); - impl_string_type _path; -#ifdef GHC_OS_WINDOWS - void handle_prefixes(); - friend bool detail::has_executable_extension(const path& p); -#ifdef GHC_WIN_AUTO_PREFIX_LONG_PATH - string_type::size_type _prefixLength{0}; -#else // GHC_WIN_AUTO_PREFIX_LONG_PATH - static const string_type::size_type _prefixLength{0}; -#endif // GHC_WIN_AUTO_PREFIX_LONG_PATH -#else - static const string_type::size_type _prefixLength{0}; -#endif -}; - -// [fs.path.nonmember] path non-member functions -GHC_FS_API void swap(path& lhs, path& rhs) noexcept; -GHC_FS_API size_t hash_value(const path& p) noexcept; -#ifdef GHC_HAS_THREEWAY_COMP -GHC_FS_API std::strong_ordering operator<=>(const path& lhs, const path& rhs) noexcept; -#endif -GHC_FS_API bool operator==(const path& lhs, const path& rhs) noexcept; -GHC_FS_API bool operator!=(const path& lhs, const path& rhs) noexcept; -GHC_FS_API bool operator<(const path& lhs, const path& rhs) noexcept; -GHC_FS_API bool operator<=(const path& lhs, const path& rhs) noexcept; -GHC_FS_API bool operator>(const path& lhs, const path& rhs) noexcept; -GHC_FS_API bool operator>=(const path& lhs, const path& rhs) noexcept; -GHC_FS_API path operator/(const path& lhs, const path& rhs); - -// [fs.path.io] path inserter and extractor -template -std::basic_ostream& operator<<(std::basic_ostream& os, const path& p); -template -std::basic_istream& operator>>(std::basic_istream& is, path& p); - -// [pfs.path.factory] path factory functions -template > -#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) -[[deprecated("use ghc::filesystem::path::path() with std::u8string instead")]] -#endif -path u8path(const Source& source); -template -#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) -[[deprecated("use ghc::filesystem::path::path() with std::u8string instead")]] -#endif -path u8path(InputIterator first, InputIterator last); - -// [fs.class.filesystem_error] class filesystem_error -class GHC_FS_API_CLASS filesystem_error : public std::system_error -{ -public: - filesystem_error(const std::string& what_arg, std::error_code ec); - filesystem_error(const std::string& what_arg, const path& p1, std::error_code ec); - filesystem_error(const std::string& what_arg, const path& p1, const path& p2, std::error_code ec); - const path& path1() const noexcept; - const path& path2() const noexcept; - const char* what() const noexcept override; - -private: - std::string _what_arg; - std::error_code _ec; - path _p1, _p2; -}; - -class GHC_FS_API_CLASS path::iterator -{ -public: - using value_type = const path; - using difference_type = std::ptrdiff_t; - using pointer = const path*; - using reference = const path&; - using iterator_category = std::bidirectional_iterator_tag; - - iterator(); - iterator(const path& p, const impl_string_type::const_iterator& pos); - iterator& operator++(); - iterator operator++(int); - iterator& operator--(); - iterator operator--(int); - bool operator==(const iterator& other) const; - bool operator!=(const iterator& other) const; - reference operator*() const; - pointer operator->() const; - -private: - friend class path; - impl_string_type::const_iterator increment(const impl_string_type::const_iterator& pos) const; - impl_string_type::const_iterator decrement(const impl_string_type::const_iterator& pos) const; - void updateCurrent(); - impl_string_type::const_iterator _first; - impl_string_type::const_iterator _last; - impl_string_type::const_iterator _prefix; - impl_string_type::const_iterator _root; - impl_string_type::const_iterator _iter; - path _current; -}; - -struct space_info -{ - uintmax_t capacity; - uintmax_t free; - uintmax_t available; -}; - -// [fs.enum] enumerations -// [fs.enum.file_type] -enum class file_type { - none, - not_found, - regular, - directory, - symlink, - block, - character, - fifo, - socket, - unknown, -}; - -// [fs.enum.perms] -enum class perms : uint16_t { - none = 0, - - owner_read = 0400, - owner_write = 0200, - owner_exec = 0100, - owner_all = 0700, - - group_read = 040, - group_write = 020, - group_exec = 010, - group_all = 070, - - others_read = 04, - others_write = 02, - others_exec = 01, - others_all = 07, - - all = 0777, - set_uid = 04000, - set_gid = 02000, - sticky_bit = 01000, - - mask = 07777, - unknown = 0xffff -}; - -// [fs.enum.perm.opts] -enum class perm_options : uint16_t { - replace = 3, - add = 1, - remove = 2, - nofollow = 4, -}; - -// [fs.enum.copy.opts] -enum class copy_options : uint16_t { - none = 0, - - skip_existing = 1, - overwrite_existing = 2, - update_existing = 4, - - recursive = 8, - - copy_symlinks = 0x10, - skip_symlinks = 0x20, - - directories_only = 0x40, - create_symlinks = 0x80, -#ifndef GHC_OS_WEB - create_hard_links = 0x100 -#endif -}; - -// [fs.enum.dir.opts] -enum class directory_options : uint16_t { - none = 0, - follow_directory_symlink = 1, - skip_permission_denied = 2, -}; - -// [fs.class.file_status] class file_status -class GHC_FS_API_CLASS file_status -{ -public: - // [fs.file_status.cons] constructors and destructor - file_status() noexcept; - explicit file_status(file_type ft, perms prms = perms::unknown) noexcept; - file_status(const file_status&) noexcept; - file_status(file_status&&) noexcept; - ~file_status(); - // assignments: - file_status& operator=(const file_status&) noexcept; - file_status& operator=(file_status&&) noexcept; - // [fs.file_status.mods] modifiers - void type(file_type ft) noexcept; - void permissions(perms prms) noexcept; - // [fs.file_status.obs] observers - file_type type() const noexcept; - perms permissions() const noexcept; - friend bool operator==(const file_status& lhs, const file_status& rhs) noexcept { return lhs.type() == rhs.type() && lhs.permissions() == rhs.permissions(); } - -private: - file_type _type; - perms _perms; -}; - -using file_time_type = std::chrono::time_point; - -// [fs.class.directory_entry] Class directory_entry -class GHC_FS_API_CLASS directory_entry -{ -public: - // [fs.dir.entry.cons] constructors and destructor - directory_entry() noexcept = default; - directory_entry(const directory_entry&) = default; - directory_entry(directory_entry&&) noexcept = default; -#ifdef GHC_WITH_EXCEPTIONS - explicit directory_entry(const path& p); -#endif - directory_entry(const path& p, std::error_code& ec); - ~directory_entry(); - - // assignments: - directory_entry& operator=(const directory_entry&) = default; - directory_entry& operator=(directory_entry&&) noexcept = default; - - // [fs.dir.entry.mods] modifiers -#ifdef GHC_WITH_EXCEPTIONS - void assign(const path& p); - void replace_filename(const path& p); - void refresh(); -#endif - void assign(const path& p, std::error_code& ec); - void replace_filename(const path& p, std::error_code& ec); - void refresh(std::error_code& ec) noexcept; - - // [fs.dir.entry.obs] observers - const filesystem::path& path() const noexcept; - operator const filesystem::path&() const noexcept; -#ifdef GHC_WITH_EXCEPTIONS - bool exists() const; - bool is_block_file() const; - bool is_character_file() const; - bool is_directory() const; - bool is_fifo() const; - bool is_other() const; - bool is_regular_file() const; - bool is_socket() const; - bool is_symlink() const; - uintmax_t file_size() const; - file_time_type last_write_time() const; - file_status status() const; - file_status symlink_status() const; -#endif - bool exists(std::error_code& ec) const noexcept; - bool is_block_file(std::error_code& ec) const noexcept; - bool is_character_file(std::error_code& ec) const noexcept; - bool is_directory(std::error_code& ec) const noexcept; - bool is_fifo(std::error_code& ec) const noexcept; - bool is_other(std::error_code& ec) const noexcept; - bool is_regular_file(std::error_code& ec) const noexcept; - bool is_socket(std::error_code& ec) const noexcept; - bool is_symlink(std::error_code& ec) const noexcept; - uintmax_t file_size(std::error_code& ec) const noexcept; - file_time_type last_write_time(std::error_code& ec) const noexcept; - file_status status(std::error_code& ec) const noexcept; - file_status symlink_status(std::error_code& ec) const noexcept; - -#ifndef GHC_OS_WEB -#ifdef GHC_WITH_EXCEPTIONS - uintmax_t hard_link_count() const; -#endif - uintmax_t hard_link_count(std::error_code& ec) const noexcept; -#endif - -#ifdef GHC_HAS_THREEWAY_COMP - std::strong_ordering operator<=>(const directory_entry& rhs) const noexcept; -#endif - bool operator<(const directory_entry& rhs) const noexcept; - bool operator==(const directory_entry& rhs) const noexcept; - bool operator!=(const directory_entry& rhs) const noexcept; - bool operator<=(const directory_entry& rhs) const noexcept; - bool operator>(const directory_entry& rhs) const noexcept; - bool operator>=(const directory_entry& rhs) const noexcept; - -private: - friend class directory_iterator; -#ifdef GHC_WITH_EXCEPTIONS - file_type status_file_type() const; -#endif - file_type status_file_type(std::error_code& ec) const noexcept; - filesystem::path _path; - file_status _status; - file_status _symlink_status; - uintmax_t _file_size = static_cast(-1); -#ifndef GHC_OS_WINDOWS - uintmax_t _hard_link_count = static_cast(-1); -#endif - time_t _last_write_time = 0; -}; - -// [fs.class.directory.iterator] Class directory_iterator -class GHC_FS_API_CLASS directory_iterator -{ -public: - class GHC_FS_API_CLASS proxy - { - public: - const directory_entry& operator*() const& noexcept { return _dir_entry; } - directory_entry operator*() && noexcept { return std::move(_dir_entry); } - - private: - explicit proxy(const directory_entry& dir_entry) - : _dir_entry(dir_entry) - { - } - friend class directory_iterator; - friend class recursive_directory_iterator; - directory_entry _dir_entry; - }; - using iterator_category = std::input_iterator_tag; - using value_type = directory_entry; - using difference_type = std::ptrdiff_t; - using pointer = const directory_entry*; - using reference = const directory_entry&; - - // [fs.dir.itr.members] member functions - directory_iterator() noexcept; -#ifdef GHC_WITH_EXCEPTIONS - explicit directory_iterator(const path& p); - directory_iterator(const path& p, directory_options options); -#endif - directory_iterator(const path& p, std::error_code& ec) noexcept; - directory_iterator(const path& p, directory_options options, std::error_code& ec) noexcept; - directory_iterator(const directory_iterator& rhs); - directory_iterator(directory_iterator&& rhs) noexcept; - ~directory_iterator(); - directory_iterator& operator=(const directory_iterator& rhs); - directory_iterator& operator=(directory_iterator&& rhs) noexcept; - const directory_entry& operator*() const; - const directory_entry* operator->() const; -#ifdef GHC_WITH_EXCEPTIONS - directory_iterator& operator++(); -#endif - directory_iterator& increment(std::error_code& ec) noexcept; - - // other members as required by [input.iterators] -#ifdef GHC_WITH_EXCEPTIONS - proxy operator++(int) - { - proxy p{**this}; - ++*this; - return p; - } -#endif - bool operator==(const directory_iterator& rhs) const; - bool operator!=(const directory_iterator& rhs) const; - -private: - friend class recursive_directory_iterator; - class impl; - std::shared_ptr _impl; -}; - -// [fs.dir.itr.nonmembers] directory_iterator non-member functions -GHC_FS_API directory_iterator begin(directory_iterator iter) noexcept; -GHC_FS_API directory_iterator end(const directory_iterator&) noexcept; - -// [fs.class.re.dir.itr] class recursive_directory_iterator -class GHC_FS_API_CLASS recursive_directory_iterator -{ -public: - using iterator_category = std::input_iterator_tag; - using value_type = directory_entry; - using difference_type = std::ptrdiff_t; - using pointer = const directory_entry*; - using reference = const directory_entry&; - - // [fs.rec.dir.itr.members] constructors and destructor - recursive_directory_iterator() noexcept; -#ifdef GHC_WITH_EXCEPTIONS - explicit recursive_directory_iterator(const path& p); - recursive_directory_iterator(const path& p, directory_options options); -#endif - recursive_directory_iterator(const path& p, directory_options options, std::error_code& ec) noexcept; - recursive_directory_iterator(const path& p, std::error_code& ec) noexcept; - recursive_directory_iterator(const recursive_directory_iterator& rhs); - recursive_directory_iterator(recursive_directory_iterator&& rhs) noexcept; - ~recursive_directory_iterator(); - - // [fs.rec.dir.itr.members] observers - directory_options options() const; - int depth() const; - bool recursion_pending() const; - - const directory_entry& operator*() const; - const directory_entry* operator->() const; - - // [fs.rec.dir.itr.members] modifiers recursive_directory_iterator& - recursive_directory_iterator& operator=(const recursive_directory_iterator& rhs); - recursive_directory_iterator& operator=(recursive_directory_iterator&& rhs) noexcept; -#ifdef GHC_WITH_EXCEPTIONS - recursive_directory_iterator& operator++(); -#endif - recursive_directory_iterator& increment(std::error_code& ec) noexcept; - -#ifdef GHC_WITH_EXCEPTIONS - void pop(); -#endif - void pop(std::error_code& ec); - void disable_recursion_pending(); - - // other members as required by [input.iterators] -#ifdef GHC_WITH_EXCEPTIONS - directory_iterator::proxy operator++(int) - { - directory_iterator::proxy proxy{**this}; - ++*this; - return proxy; - } -#endif - bool operator==(const recursive_directory_iterator& rhs) const; - bool operator!=(const recursive_directory_iterator& rhs) const; - -private: - struct recursive_directory_iterator_impl - { - directory_options _options; - bool _recursion_pending; - std::stack _dir_iter_stack; - recursive_directory_iterator_impl(directory_options options, bool recursion_pending) - : _options(options) - , _recursion_pending(recursion_pending) - { - } - }; - std::shared_ptr _impl; -}; - -// [fs.rec.dir.itr.nonmembers] directory_iterator non-member functions -GHC_FS_API recursive_directory_iterator begin(recursive_directory_iterator iter) noexcept; -GHC_FS_API recursive_directory_iterator end(const recursive_directory_iterator&) noexcept; - -// [fs.op.funcs] filesystem operations -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API path absolute(const path& p); -GHC_FS_API path canonical(const path& p); -GHC_FS_API void copy(const path& from, const path& to); -GHC_FS_API void copy(const path& from, const path& to, copy_options options); -GHC_FS_API bool copy_file(const path& from, const path& to); -GHC_FS_API bool copy_file(const path& from, const path& to, copy_options option); -GHC_FS_API void copy_symlink(const path& existing_symlink, const path& new_symlink); -GHC_FS_API bool create_directories(const path& p); -GHC_FS_API bool create_directory(const path& p); -GHC_FS_API bool create_directory(const path& p, const path& attributes); -GHC_FS_API void create_directory_symlink(const path& to, const path& new_symlink); -GHC_FS_API void create_symlink(const path& to, const path& new_symlink); -GHC_FS_API path current_path(); -GHC_FS_API void current_path(const path& p); -GHC_FS_API bool exists(const path& p); -GHC_FS_API bool equivalent(const path& p1, const path& p2); -GHC_FS_API uintmax_t file_size(const path& p); -GHC_FS_API bool is_block_file(const path& p); -GHC_FS_API bool is_character_file(const path& p); -GHC_FS_API bool is_directory(const path& p); -GHC_FS_API bool is_empty(const path& p); -GHC_FS_API bool is_fifo(const path& p); -GHC_FS_API bool is_other(const path& p); -GHC_FS_API bool is_regular_file(const path& p); -GHC_FS_API bool is_socket(const path& p); -GHC_FS_API bool is_symlink(const path& p); -GHC_FS_API file_time_type last_write_time(const path& p); -GHC_FS_API void last_write_time(const path& p, file_time_type new_time); -GHC_FS_API void permissions(const path& p, perms prms, perm_options opts = perm_options::replace); -GHC_FS_API path proximate(const path& p, const path& base = current_path()); -GHC_FS_API path read_symlink(const path& p); -GHC_FS_API path relative(const path& p, const path& base = current_path()); -GHC_FS_API bool remove(const path& p); -GHC_FS_API uintmax_t remove_all(const path& p); -GHC_FS_API void rename(const path& from, const path& to); -GHC_FS_API void resize_file(const path& p, uintmax_t size); -GHC_FS_API space_info space(const path& p); -GHC_FS_API file_status status(const path& p); -GHC_FS_API file_status symlink_status(const path& p); -GHC_FS_API path temp_directory_path(); -GHC_FS_API path weakly_canonical(const path& p); -#endif -GHC_FS_API path absolute(const path& p, std::error_code& ec); -GHC_FS_API path canonical(const path& p, std::error_code& ec); -GHC_FS_API void copy(const path& from, const path& to, std::error_code& ec) noexcept; -GHC_FS_API void copy(const path& from, const path& to, copy_options options, std::error_code& ec) noexcept; -GHC_FS_API bool copy_file(const path& from, const path& to, std::error_code& ec) noexcept; -GHC_FS_API bool copy_file(const path& from, const path& to, copy_options option, std::error_code& ec) noexcept; -GHC_FS_API void copy_symlink(const path& existing_symlink, const path& new_symlink, std::error_code& ec) noexcept; -GHC_FS_API bool create_directories(const path& p, std::error_code& ec) noexcept; -GHC_FS_API bool create_directory(const path& p, std::error_code& ec) noexcept; -GHC_FS_API bool create_directory(const path& p, const path& attributes, std::error_code& ec) noexcept; -GHC_FS_API void create_directory_symlink(const path& to, const path& new_symlink, std::error_code& ec) noexcept; -GHC_FS_API void create_symlink(const path& to, const path& new_symlink, std::error_code& ec) noexcept; -GHC_FS_API path current_path(std::error_code& ec); -GHC_FS_API void current_path(const path& p, std::error_code& ec) noexcept; -GHC_FS_API bool exists(file_status s) noexcept; -GHC_FS_API bool exists(const path& p, std::error_code& ec) noexcept; -GHC_FS_API bool equivalent(const path& p1, const path& p2, std::error_code& ec) noexcept; -GHC_FS_API uintmax_t file_size(const path& p, std::error_code& ec) noexcept; -GHC_FS_API bool is_block_file(file_status s) noexcept; -GHC_FS_API bool is_block_file(const path& p, std::error_code& ec) noexcept; -GHC_FS_API bool is_character_file(file_status s) noexcept; -GHC_FS_API bool is_character_file(const path& p, std::error_code& ec) noexcept; -GHC_FS_API bool is_directory(file_status s) noexcept; -GHC_FS_API bool is_directory(const path& p, std::error_code& ec) noexcept; -GHC_FS_API bool is_empty(const path& p, std::error_code& ec) noexcept; -GHC_FS_API bool is_fifo(file_status s) noexcept; -GHC_FS_API bool is_fifo(const path& p, std::error_code& ec) noexcept; -GHC_FS_API bool is_other(file_status s) noexcept; -GHC_FS_API bool is_other(const path& p, std::error_code& ec) noexcept; -GHC_FS_API bool is_regular_file(file_status s) noexcept; -GHC_FS_API bool is_regular_file(const path& p, std::error_code& ec) noexcept; -GHC_FS_API bool is_socket(file_status s) noexcept; -GHC_FS_API bool is_socket(const path& p, std::error_code& ec) noexcept; -GHC_FS_API bool is_symlink(file_status s) noexcept; -GHC_FS_API bool is_symlink(const path& p, std::error_code& ec) noexcept; -GHC_FS_API file_time_type last_write_time(const path& p, std::error_code& ec) noexcept; -GHC_FS_API void last_write_time(const path& p, file_time_type new_time, std::error_code& ec) noexcept; -GHC_FS_API void permissions(const path& p, perms prms, std::error_code& ec) noexcept; -GHC_FS_API void permissions(const path& p, perms prms, perm_options opts, std::error_code& ec) noexcept; -GHC_FS_API path proximate(const path& p, std::error_code& ec); -GHC_FS_API path proximate(const path& p, const path& base, std::error_code& ec); -GHC_FS_API path read_symlink(const path& p, std::error_code& ec); -GHC_FS_API path relative(const path& p, std::error_code& ec); -GHC_FS_API path relative(const path& p, const path& base, std::error_code& ec); -GHC_FS_API bool remove(const path& p, std::error_code& ec) noexcept; -GHC_FS_API uintmax_t remove_all(const path& p, std::error_code& ec) noexcept; -GHC_FS_API void rename(const path& from, const path& to, std::error_code& ec) noexcept; -GHC_FS_API void resize_file(const path& p, uintmax_t size, std::error_code& ec) noexcept; -GHC_FS_API space_info space(const path& p, std::error_code& ec) noexcept; -GHC_FS_API file_status status(const path& p, std::error_code& ec) noexcept; -GHC_FS_API bool status_known(file_status s) noexcept; -GHC_FS_API file_status symlink_status(const path& p, std::error_code& ec) noexcept; -GHC_FS_API path temp_directory_path(std::error_code& ec) noexcept; -GHC_FS_API path weakly_canonical(const path& p, std::error_code& ec) noexcept; - -#ifndef GHC_OS_WEB -#ifdef GHC_WITH_EXCEPTIONS -GHC_FS_API void create_hard_link(const path& to, const path& new_hard_link); -GHC_FS_API uintmax_t hard_link_count(const path& p); -#endif -GHC_FS_API void create_hard_link(const path& to, const path& new_hard_link, std::error_code& ec) noexcept; -GHC_FS_API uintmax_t hard_link_count(const path& p, std::error_code& ec) noexcept; -#endif - -// Non-C++17 add-on std::fstream wrappers with path -template > -class basic_filebuf : public std::basic_filebuf -{ -public: - basic_filebuf() {} - ~basic_filebuf() override {} - basic_filebuf(const basic_filebuf&) = delete; - const basic_filebuf& operator=(const basic_filebuf&) = delete; - basic_filebuf* open(const path& p, std::ios_base::openmode mode) - { -#if defined(GHC_OS_WINDOWS) && !defined(__GLIBCXX__) - return std::basic_filebuf::open(p.wstring().c_str(), mode) ? this : 0; -#else - return std::basic_filebuf::open(p.string().c_str(), mode) ? this : 0; -#endif - } -}; - -template > -class basic_ifstream : public std::basic_ifstream -{ -public: - basic_ifstream() {} -#if defined(GHC_OS_WINDOWS) && !defined(__GLIBCXX__) - explicit basic_ifstream(const path& p, std::ios_base::openmode mode = std::ios_base::in) - : std::basic_ifstream(p.wstring().c_str(), mode) - { - } - void open(const path& p, std::ios_base::openmode mode = std::ios_base::in) { std::basic_ifstream::open(p.wstring().c_str(), mode); } -#else - explicit basic_ifstream(const path& p, std::ios_base::openmode mode = std::ios_base::in) - : std::basic_ifstream(p.string().c_str(), mode) - { - } - void open(const path& p, std::ios_base::openmode mode = std::ios_base::in) { std::basic_ifstream::open(p.string().c_str(), mode); } -#endif - basic_ifstream(const basic_ifstream&) = delete; - const basic_ifstream& operator=(const basic_ifstream&) = delete; - ~basic_ifstream() override {} -}; - -template > -class basic_ofstream : public std::basic_ofstream -{ -public: - basic_ofstream() {} -#if defined(GHC_OS_WINDOWS) && !defined(__GLIBCXX__) - explicit basic_ofstream(const path& p, std::ios_base::openmode mode = std::ios_base::out) - : std::basic_ofstream(p.wstring().c_str(), mode) - { - } - void open(const path& p, std::ios_base::openmode mode = std::ios_base::out) { std::basic_ofstream::open(p.wstring().c_str(), mode); } -#else - explicit basic_ofstream(const path& p, std::ios_base::openmode mode = std::ios_base::out) - : std::basic_ofstream(p.string().c_str(), mode) - { - } - void open(const path& p, std::ios_base::openmode mode = std::ios_base::out) { std::basic_ofstream::open(p.string().c_str(), mode); } -#endif - basic_ofstream(const basic_ofstream&) = delete; - const basic_ofstream& operator=(const basic_ofstream&) = delete; - ~basic_ofstream() override {} -}; - -template > -class basic_fstream : public std::basic_fstream -{ -public: - basic_fstream() {} -#if defined(GHC_OS_WINDOWS) && !defined(__GLIBCXX__) - explicit basic_fstream(const path& p, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) - : std::basic_fstream(p.wstring().c_str(), mode) - { - } - void open(const path& p, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) { std::basic_fstream::open(p.wstring().c_str(), mode); } -#else - explicit basic_fstream(const path& p, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) - : std::basic_fstream(p.string().c_str(), mode) - { - } - void open(const path& p, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) { std::basic_fstream::open(p.string().c_str(), mode); } -#endif - basic_fstream(const basic_fstream&) = delete; - const basic_fstream& operator=(const basic_fstream&) = delete; - ~basic_fstream() override {} -}; - -typedef basic_filebuf filebuf; -typedef basic_filebuf wfilebuf; -typedef basic_ifstream ifstream; -typedef basic_ifstream wifstream; -typedef basic_ofstream ofstream; -typedef basic_ofstream wofstream; -typedef basic_fstream fstream; -typedef basic_fstream wfstream; - -class GHC_FS_API_CLASS u8arguments -{ -public: - u8arguments(int& argc, char**& argv); - ~u8arguments() - { - _refargc = _argc; - _refargv = _argv; - } - - bool valid() const { return _isvalid; } - -private: - int _argc; - char** _argv; - int& _refargc; - char**& _refargv; - bool _isvalid; -#ifdef GHC_OS_WINDOWS - std::vector _args; - std::vector _argp; -#endif -}; - -//------------------------------------------------------------------------------------------------- -// Implementation -//------------------------------------------------------------------------------------------------- - -namespace detail { -enum utf8_states_t { S_STRT = 0, S_RJCT = 8 }; -GHC_FS_API void appendUTF8(std::string& str, uint32_t unicode); -GHC_FS_API bool is_surrogate(uint32_t c); -GHC_FS_API bool is_high_surrogate(uint32_t c); -GHC_FS_API bool is_low_surrogate(uint32_t c); -GHC_FS_API unsigned consumeUtf8Fragment(const unsigned state, const uint8_t fragment, uint32_t& codepoint); -enum class portable_error { - none = 0, - exists, - not_found, - not_supported, - not_implemented, - invalid_argument, - is_a_directory, -}; -GHC_FS_API std::error_code make_error_code(portable_error err); -#ifdef GHC_OS_WINDOWS -GHC_FS_API std::error_code make_system_error(uint32_t err = 0); -#else -GHC_FS_API std::error_code make_system_error(int err = 0); - -template -struct has_d_type : std::false_type{}; - -template -struct has_d_type : std::true_type {}; - -template -GHC_INLINE file_type file_type_from_dirent_impl(const T&, std::false_type) -{ - return file_type::none; -} - -template -GHC_INLINE file_type file_type_from_dirent_impl(const T& t, std::true_type) -{ - switch (t.d_type) { -#ifdef DT_BLK - case DT_BLK: - return file_type::block; -#endif -#ifdef DT_CHR - case DT_CHR: - return file_type::character; -#endif -#ifdef DT_DIR - case DT_DIR: - return file_type::directory; -#endif -#ifdef DT_FIFO - case DT_FIFO: - return file_type::fifo; -#endif -#ifdef DT_LNK - case DT_LNK: - return file_type::symlink; -#endif -#ifdef DT_REG - case DT_REG: - return file_type::regular; -#endif -#ifdef DT_SOCK - case DT_SOCK: - return file_type::socket; -#endif -#ifdef DT_UNKNOWN - case DT_UNKNOWN: - return file_type::none; -#endif - default: - return file_type::unknown; - } -} - -template -GHC_INLINE file_type file_type_from_dirent(const T& t) -{ - return file_type_from_dirent_impl(t, has_d_type{}); -} -#endif -} // namespace detail - -namespace detail { - -#ifdef GHC_EXPAND_IMPL - -GHC_INLINE std::error_code make_error_code(portable_error err) -{ -#ifdef GHC_OS_WINDOWS - switch (err) { - case portable_error::none: - return std::error_code(); - case portable_error::exists: - return std::error_code(ERROR_ALREADY_EXISTS, std::system_category()); - case portable_error::not_found: - return std::error_code(ERROR_PATH_NOT_FOUND, std::system_category()); - case portable_error::not_supported: - return std::error_code(ERROR_NOT_SUPPORTED, std::system_category()); - case portable_error::not_implemented: - return std::error_code(ERROR_CALL_NOT_IMPLEMENTED, std::system_category()); - case portable_error::invalid_argument: - return std::error_code(ERROR_INVALID_PARAMETER, std::system_category()); - case portable_error::is_a_directory: -#ifdef ERROR_DIRECTORY_NOT_SUPPORTED - return std::error_code(ERROR_DIRECTORY_NOT_SUPPORTED, std::system_category()); -#else - return std::error_code(ERROR_NOT_SUPPORTED, std::system_category()); -#endif - } -#else - switch (err) { - case portable_error::none: - return std::error_code(); - case portable_error::exists: - return std::error_code(EEXIST, std::system_category()); - case portable_error::not_found: - return std::error_code(ENOENT, std::system_category()); - case portable_error::not_supported: - return std::error_code(ENOTSUP, std::system_category()); - case portable_error::not_implemented: - return std::error_code(ENOSYS, std::system_category()); - case portable_error::invalid_argument: - return std::error_code(EINVAL, std::system_category()); - case portable_error::is_a_directory: - return std::error_code(EISDIR, std::system_category()); - } -#endif - return std::error_code(); -} - -#ifdef GHC_OS_WINDOWS -GHC_INLINE std::error_code make_system_error(uint32_t err) -{ - return std::error_code(err ? static_cast(err) : static_cast(::GetLastError()), std::system_category()); -} -#else -GHC_INLINE std::error_code make_system_error(int err) -{ - return std::error_code(err ? err : errno, std::system_category()); -} -#endif - -#endif // GHC_EXPAND_IMPL - -template -using EnableBitmask = typename std::enable_if::value || std::is_same::value || std::is_same::value || std::is_same::value, Enum>::type; -} // namespace detail - -template -constexpr detail::EnableBitmask operator&(Enum X, Enum Y) -{ - using underlying = typename std::underlying_type::type; - return static_cast(static_cast(X) & static_cast(Y)); -} - -template -constexpr detail::EnableBitmask operator|(Enum X, Enum Y) -{ - using underlying = typename std::underlying_type::type; - return static_cast(static_cast(X) | static_cast(Y)); -} - -template -constexpr detail::EnableBitmask operator^(Enum X, Enum Y) -{ - using underlying = typename std::underlying_type::type; - return static_cast(static_cast(X) ^ static_cast(Y)); -} - -template -constexpr detail::EnableBitmask operator~(Enum X) -{ - using underlying = typename std::underlying_type::type; - return static_cast(~static_cast(X)); -} - -template -detail::EnableBitmask& operator&=(Enum& X, Enum Y) -{ - X = X & Y; - return X; -} - -template -detail::EnableBitmask& operator|=(Enum& X, Enum Y) -{ - X = X | Y; - return X; -} - -template -detail::EnableBitmask& operator^=(Enum& X, Enum Y) -{ - X = X ^ Y; - return X; -} - -#ifdef GHC_EXPAND_IMPL - -namespace detail { - -GHC_INLINE bool in_range(uint32_t c, uint32_t lo, uint32_t hi) -{ - return (static_cast(c - lo) < (hi - lo + 1)); -} - -GHC_INLINE bool is_surrogate(uint32_t c) -{ - return in_range(c, 0xd800, 0xdfff); -} - -GHC_INLINE bool is_high_surrogate(uint32_t c) -{ - return (c & 0xfffffc00) == 0xd800; -} - -GHC_INLINE bool is_low_surrogate(uint32_t c) -{ - return (c & 0xfffffc00) == 0xdc00; -} - -GHC_INLINE void appendUTF8(std::string& str, uint32_t unicode) -{ - if (unicode <= 0x7f) { - str.push_back(static_cast(unicode)); - } - else if (unicode >= 0x80 && unicode <= 0x7ff) { - str.push_back(static_cast((unicode >> 6) + 192)); - str.push_back(static_cast((unicode & 0x3f) + 128)); - } - else if ((unicode >= 0x800 && unicode <= 0xd7ff) || (unicode >= 0xe000 && unicode <= 0xffff)) { - str.push_back(static_cast((unicode >> 12) + 224)); - str.push_back(static_cast(((unicode & 0xfff) >> 6) + 128)); - str.push_back(static_cast((unicode & 0x3f) + 128)); - } - else if (unicode >= 0x10000 && unicode <= 0x10ffff) { - str.push_back(static_cast((unicode >> 18) + 240)); - str.push_back(static_cast(((unicode & 0x3ffff) >> 12) + 128)); - str.push_back(static_cast(((unicode & 0xfff) >> 6) + 128)); - str.push_back(static_cast((unicode & 0x3f) + 128)); - } - else { -#ifdef GHC_RAISE_UNICODE_ERRORS - throw filesystem_error("Illegal code point for unicode character.", str, std::make_error_code(std::errc::illegal_byte_sequence)); -#else - appendUTF8(str, 0xfffd); -#endif - } -} - -// Thanks to Bjoern Hoehrmann (https://bjoern.hoehrmann.de/utf-8/decoder/dfa/) -// and Taylor R Campbell for the ideas to this DFA approach of UTF-8 decoding; -// Generating debugging and shrinking my own DFA from scratch was a day of fun! -GHC_INLINE unsigned consumeUtf8Fragment(const unsigned state, const uint8_t fragment, uint32_t& codepoint) -{ - static const uint32_t utf8_state_info[] = { - // encoded states - 0x11111111u, 0x11111111u, 0x77777777u, 0x77777777u, 0x88888888u, 0x88888888u, 0x88888888u, 0x88888888u, 0x22222299u, 0x22222222u, 0x22222222u, 0x22222222u, 0x3333333au, 0x33433333u, 0x9995666bu, 0x99999999u, - 0x88888880u, 0x22818108u, 0x88888881u, 0x88888882u, 0x88888884u, 0x88888887u, 0x88888886u, 0x82218108u, 0x82281108u, 0x88888888u, 0x88888883u, 0x88888885u, 0u, 0u, 0u, 0u, - }; - uint8_t category = fragment < 128 ? 0 : (utf8_state_info[(fragment >> 3) & 0xf] >> ((fragment & 7) << 2)) & 0xf; - codepoint = (state ? (codepoint << 6) | (fragment & 0x3fu) : (0xffu >> category) & fragment); - return state == S_RJCT ? static_cast(S_RJCT) : static_cast((utf8_state_info[category + 16] >> (state << 2)) & 0xf); -} - -GHC_INLINE bool validUtf8(const std::string& utf8String) -{ - std::string::const_iterator iter = utf8String.begin(); - unsigned utf8_state = S_STRT; - std::uint32_t codepoint = 0; - while (iter < utf8String.end()) { - if ((utf8_state = consumeUtf8Fragment(utf8_state, static_cast(*iter++), codepoint)) == S_RJCT) { - return false; - } - } - if (utf8_state) { - return false; - } - return true; -} - -} // namespace detail - -#endif - -namespace detail { - -template ::value && (sizeof(typename Utf8String::value_type) == 1) && (sizeof(typename StringType::value_type) == 1)>::type* = nullptr> -inline StringType fromUtf8(const Utf8String& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) -{ - return StringType(utf8String.begin(), utf8String.end(), alloc); -} - -template ::value && (sizeof(typename Utf8String::value_type) == 1) && (sizeof(typename StringType::value_type) == 2)>::type* = nullptr> -inline StringType fromUtf8(const Utf8String& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) -{ - StringType result(alloc); - result.reserve(utf8String.length()); - auto iter = utf8String.cbegin(); - unsigned utf8_state = S_STRT; - std::uint32_t codepoint = 0; - while (iter < utf8String.cend()) { - if ((utf8_state = consumeUtf8Fragment(utf8_state, static_cast(*iter++), codepoint)) == S_STRT) { - if (codepoint <= 0xffff) { - result += static_cast(codepoint); - } - else { - codepoint -= 0x10000; - result += static_cast((codepoint >> 10) + 0xd800); - result += static_cast((codepoint & 0x3ff) + 0xdc00); - } - codepoint = 0; - } - else if (utf8_state == S_RJCT) { -#ifdef GHC_RAISE_UNICODE_ERRORS - throw filesystem_error("Illegal byte sequence for unicode character.", utf8String, std::make_error_code(std::errc::illegal_byte_sequence)); -#else - result += static_cast(0xfffd); - utf8_state = S_STRT; - codepoint = 0; -#endif - } - } - if (utf8_state) { -#ifdef GHC_RAISE_UNICODE_ERRORS - throw filesystem_error("Illegal byte sequence for unicode character.", utf8String, std::make_error_code(std::errc::illegal_byte_sequence)); -#else - result += static_cast(0xfffd); -#endif - } - return result; -} - -template ::value && (sizeof(typename Utf8String::value_type) == 1) && (sizeof(typename StringType::value_type) == 4)>::type* = nullptr> -inline StringType fromUtf8(const Utf8String& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) -{ - StringType result(alloc); - result.reserve(utf8String.length()); - auto iter = utf8String.cbegin(); - unsigned utf8_state = S_STRT; - std::uint32_t codepoint = 0; - while (iter < utf8String.cend()) { - if ((utf8_state = consumeUtf8Fragment(utf8_state, static_cast(*iter++), codepoint)) == S_STRT) { - result += static_cast(codepoint); - codepoint = 0; - } - else if (utf8_state == S_RJCT) { -#ifdef GHC_RAISE_UNICODE_ERRORS - throw filesystem_error("Illegal byte sequence for unicode character.", utf8String, std::make_error_code(std::errc::illegal_byte_sequence)); -#else - result += static_cast(0xfffd); - utf8_state = S_STRT; - codepoint = 0; -#endif - } - } - if (utf8_state) { -#ifdef GHC_RAISE_UNICODE_ERRORS - throw filesystem_error("Illegal byte sequence for unicode character.", utf8String, std::make_error_code(std::errc::illegal_byte_sequence)); -#else - result += static_cast(0xfffd); -#endif - } - return result; -} - -template -inline StringType fromUtf8(const charT (&utf8String)[N]) -{ -#ifdef GHC_WITH_STRING_VIEW - return fromUtf8(basic_string_view(utf8String, N - 1)); -#else - return fromUtf8(std::basic_string(utf8String, N - 1)); -#endif -} - -template ::value && (sizeof(typename strT::value_type) == 1), int>::type size = 1> -inline std::string toUtf8(const strT& unicodeString) -{ - return std::string(unicodeString.begin(), unicodeString.end()); -} - -template ::value && (sizeof(typename strT::value_type) == 2), int>::type size = 2> -inline std::string toUtf8(const strT& unicodeString) -{ - std::string result; - for (auto iter = unicodeString.begin(); iter != unicodeString.end(); ++iter) { - char32_t c = *iter; - if (is_surrogate(c)) { - ++iter; - if (iter != unicodeString.end() && is_high_surrogate(c) && is_low_surrogate(*iter)) { - appendUTF8(result, (char32_t(c) << 10) + *iter - 0x35fdc00); - } - else { -#ifdef GHC_RAISE_UNICODE_ERRORS - throw filesystem_error("Illegal code point for unicode character.", result, std::make_error_code(std::errc::illegal_byte_sequence)); -#else - appendUTF8(result, 0xfffd); - if (iter == unicodeString.end()) { - break; - } -#endif - } - } - else { - appendUTF8(result, c); - } - } - return result; -} - -template ::value && (sizeof(typename strT::value_type) == 4), int>::type size = 4> -inline std::string toUtf8(const strT& unicodeString) -{ - std::string result; - for (auto c : unicodeString) { - appendUTF8(result, static_cast(c)); - } - return result; -} - -template -inline std::string toUtf8(const charT* unicodeString) -{ -#ifdef GHC_WITH_STRING_VIEW - return toUtf8(basic_string_view>(unicodeString)); -#else - return toUtf8(std::basic_string>(unicodeString)); -#endif -} - -#ifdef GHC_USE_WCHAR_T -template ::value && (sizeof(typename WString::value_type) == 2) && (sizeof(typename StringType::value_type) == 1), bool>::type = false> -inline StringType fromWChar(const WString& wString, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) -{ - auto temp = toUtf8(wString); - return StringType(temp.begin(), temp.end(), alloc); -} - -template ::value && (sizeof(typename WString::value_type) == 2) && (sizeof(typename StringType::value_type) == 2), bool>::type = false> -inline StringType fromWChar(const WString& wString, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) -{ - return StringType(wString.begin(), wString.end(), alloc); -} - -template ::value && (sizeof(typename WString::value_type) == 2) && (sizeof(typename StringType::value_type) == 4), bool>::type = false> -inline StringType fromWChar(const WString& wString, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) -{ - auto temp = toUtf8(wString); - return fromUtf8(temp, alloc); -} - -template ::value && (sizeof(typename strT::value_type) == 1), bool>::type = false> -inline std::wstring toWChar(const strT& unicodeString) -{ - return fromUtf8(unicodeString); -} - -template ::value && (sizeof(typename strT::value_type) == 2), bool>::type = false> -inline std::wstring toWChar(const strT& unicodeString) -{ - return std::wstring(unicodeString.begin(), unicodeString.end()); -} - -template ::value && (sizeof(typename strT::value_type) == 4), bool>::type = false> -inline std::wstring toWChar(const strT& unicodeString) -{ - auto temp = toUtf8(unicodeString); - return fromUtf8(temp); -} - -template -inline std::wstring toWChar(const charT* unicodeString) -{ -#ifdef GHC_WITH_STRING_VIEW - return toWChar(basic_string_view>(unicodeString)); -#else - return toWChar(std::basic_string>(unicodeString)); -#endif -} -#endif // GHC_USE_WCHAR_T - -} // namespace detail - -#ifdef GHC_EXPAND_IMPL - -namespace detail { - -template ::value, bool>::type = true> -GHC_INLINE bool startsWith(const strT& what, const strT& with) -{ - return with.length() <= what.length() && equal(with.begin(), with.end(), what.begin()); -} - -template ::value, bool>::type = true> -GHC_INLINE bool endsWith(const strT& what, const strT& with) -{ - return with.length() <= what.length() && what.compare(what.length() - with.length(), with.size(), with) == 0; -} - -} // namespace detail - -GHC_INLINE void path::check_long_path() -{ -#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) - if (is_absolute() && _path.length() >= MAX_PATH - 12 && !detail::startsWith(_path, impl_string_type(GHC_PLATFORM_LITERAL("\\\\?\\")))) { - postprocess_path_with_format(native_format); - } -#endif -} - -GHC_INLINE void path::postprocess_path_with_format(path::format fmt) -{ -#ifdef GHC_RAISE_UNICODE_ERRORS - if (!detail::validUtf8(_path)) { - path t; - t._path = _path; - throw filesystem_error("Illegal byte sequence for unicode character.", t, std::make_error_code(std::errc::illegal_byte_sequence)); - } -#endif - switch (fmt) { -#ifdef GHC_OS_WINDOWS - case path::native_format: - case path::auto_format: - case path::generic_format: - for (auto& c : _path) { - if (c == generic_separator) { - c = preferred_separator; - } - } -#ifdef GHC_WIN_AUTO_PREFIX_LONG_PATH - if (is_absolute() && _path.length() >= MAX_PATH - 12 && !detail::startsWith(_path, impl_string_type(GHC_PLATFORM_LITERAL("\\\\?\\")))) { - _path = GHC_PLATFORM_LITERAL("\\\\?\\") + _path; - } -#endif - handle_prefixes(); - break; -#else - case path::auto_format: - case path::native_format: - case path::generic_format: - // nothing to do - break; -#endif - } - if (_path.length() > _prefixLength + 2 && _path[_prefixLength] == preferred_separator && _path[_prefixLength + 1] == preferred_separator && _path[_prefixLength + 2] != preferred_separator) { - impl_string_type::iterator new_end = std::unique(_path.begin() + static_cast(_prefixLength) + 2, _path.end(), [](path::value_type lhs, path::value_type rhs) { return lhs == rhs && lhs == preferred_separator; }); - _path.erase(new_end, _path.end()); - } - else { - impl_string_type::iterator new_end = std::unique(_path.begin() + static_cast(_prefixLength), _path.end(), [](path::value_type lhs, path::value_type rhs) { return lhs == rhs && lhs == preferred_separator; }); - _path.erase(new_end, _path.end()); - } -} - -#endif // GHC_EXPAND_IMPL - -template -inline path::path(const Source& source, format fmt) -#ifdef GHC_USE_WCHAR_T - : _path(detail::toWChar(source)) -#else - : _path(detail::toUtf8(source)) -#endif -{ - postprocess_path_with_format(fmt); -} - -template -inline path u8path(const Source& source) -{ - return path(source); -} -template -inline path u8path(InputIterator first, InputIterator last) -{ - return path(first, last); -} - -template -inline path::path(InputIterator first, InputIterator last, format fmt) - : path(std::basic_string::value_type>(first, last), fmt) -{ - // delegated -} - -#ifdef GHC_EXPAND_IMPL - -namespace detail { - -GHC_INLINE bool equals_simple_insensitive(const path::value_type* str1, const path::value_type* str2) -{ -#ifdef GHC_OS_WINDOWS -#ifdef __GNUC__ - while (::tolower((unsigned char)*str1) == ::tolower((unsigned char)*str2++)) { - if (*str1++ == 0) - return true; - } - return false; -#else // __GNUC__ -#ifdef GHC_USE_WCHAR_T - return 0 == ::_wcsicmp(str1, str2); -#else // GHC_USE_WCHAR_T - return 0 == ::_stricmp(str1, str2); -#endif // GHC_USE_WCHAR_T -#endif // __GNUC__ -#else // GHC_OS_WINDOWS - return 0 == ::strcasecmp(str1, str2); -#endif // GHC_OS_WINDOWS -} - -GHC_INLINE int compare_simple_insensitive(const path::value_type* str1, size_t len1, const path::value_type* str2, size_t len2) -{ - while (len1 > 0 && len2 > 0 && ::tolower(static_cast(*str1)) == ::tolower(static_cast(*str2))) { - --len1; - --len2; - ++str1; - ++str2; - } - if (len1 && len2) { - return *str1 < *str2 ? -1 : 1; - } - if (len1 == 0 && len2 == 0) { - return 0; - } - return len1 == 0 ? -1 : 1; -} - -GHC_INLINE const char* strerror_adapter(char* gnu, char*) -{ - return gnu; -} - -GHC_INLINE const char* strerror_adapter(int posix, char* buffer) -{ - if (posix) { - return "Error in strerror_r!"; - } - return buffer; -} - -template -GHC_INLINE std::string systemErrorText(ErrorNumber code = 0) -{ -#if defined(GHC_OS_WINDOWS) - LPVOID msgBuf; - DWORD dw = code ? static_cast(code) : ::GetLastError(); - FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&msgBuf, 0, NULL); - std::string msg = toUtf8(std::wstring((LPWSTR)msgBuf)); - LocalFree(msgBuf); - return msg; -#else - char buffer[512]; - return strerror_adapter(strerror_r(code ? code : errno, buffer, sizeof(buffer)), buffer); -#endif -} - -#ifdef GHC_OS_WINDOWS -using CreateSymbolicLinkW_fp = BOOLEAN(WINAPI*)(LPCWSTR, LPCWSTR, DWORD); -using CreateHardLinkW_fp = BOOLEAN(WINAPI*)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES); - -GHC_INLINE void create_symlink(const path& target_name, const path& new_symlink, bool to_directory, std::error_code& ec) -{ - std::error_code tec; - auto fs = status(target_name, tec); - if ((fs.type() == file_type::directory && !to_directory) || (fs.type() == file_type::regular && to_directory)) { - ec = detail::make_error_code(detail::portable_error::not_supported); - return; - } -#if defined(__GNUC__) && __GNUC__ >= 8 -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wcast-function-type" -#elif defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__clang__) -#pragma warning(push) -#pragma warning(disable : 4191) -#endif - static CreateSymbolicLinkW_fp api_call = reinterpret_cast(GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "CreateSymbolicLinkW")); -#if defined(__GNUC__) && __GNUC__ >= 8 -#pragma GCC diagnostic pop -#elif defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__clang__) -#pragma warning(pop) -#endif - if (api_call) { - if (api_call(GHC_NATIVEWP(new_symlink), GHC_NATIVEWP(target_name), to_directory ? 1 : 0) == 0) { - auto result = ::GetLastError(); - if (result == ERROR_PRIVILEGE_NOT_HELD && api_call(GHC_NATIVEWP(new_symlink), GHC_NATIVEWP(target_name), to_directory ? 3 : 2) != 0) { - return; - } - ec = detail::make_system_error(result); - } - } - else { - ec = detail::make_system_error(ERROR_NOT_SUPPORTED); - } -} - -GHC_INLINE void create_hardlink(const path& target_name, const path& new_hardlink, std::error_code& ec) -{ -#if defined(__GNUC__) && __GNUC__ >= 8 -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wcast-function-type" -#elif defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__clang__) -#pragma warning(push) -#pragma warning(disable : 4191) -#endif - static CreateHardLinkW_fp api_call = reinterpret_cast(GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "CreateHardLinkW")); -#if defined(__GNUC__) && __GNUC__ >= 8 -#pragma GCC diagnostic pop -#elif defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__clang__) -#pragma warning(pop) -#endif - if (api_call) { - if (api_call(GHC_NATIVEWP(new_hardlink), GHC_NATIVEWP(target_name), NULL) == 0) { - ec = detail::make_system_error(); - } - } - else { - ec = detail::make_system_error(ERROR_NOT_SUPPORTED); - } -} - -GHC_INLINE path getFullPathName(const wchar_t* p, std::error_code& ec) -{ - ULONG size = ::GetFullPathNameW(p, 0, 0, 0); - if (size) { - std::vector buf(size, 0); - ULONG s2 = GetFullPathNameW(p, size, buf.data(), nullptr); - if (s2 && s2 < size) { - return path(std::wstring(buf.data(), s2)); - } - } - ec = detail::make_system_error(); - return path(); -} - -#else -GHC_INLINE void create_symlink(const path& target_name, const path& new_symlink, bool, std::error_code& ec) -{ - if (::symlink(target_name.c_str(), new_symlink.c_str()) != 0) { - ec = detail::make_system_error(); - } -} - -#ifndef GHC_OS_WEB -GHC_INLINE void create_hardlink(const path& target_name, const path& new_hardlink, std::error_code& ec) -{ - if (::link(target_name.c_str(), new_hardlink.c_str()) != 0) { - ec = detail::make_system_error(); - } -} -#endif -#endif - -template -GHC_INLINE file_status file_status_from_st_mode(T mode) -{ -#ifdef GHC_OS_WINDOWS - file_type ft = file_type::unknown; - if ((mode & _S_IFDIR) == _S_IFDIR) { - ft = file_type::directory; - } - else if ((mode & _S_IFREG) == _S_IFREG) { - ft = file_type::regular; - } - else if ((mode & _S_IFCHR) == _S_IFCHR) { - ft = file_type::character; - } - perms prms = static_cast(mode & 0xfff); - return file_status(ft, prms); -#else - file_type ft = file_type::unknown; - if (S_ISDIR(mode)) { - ft = file_type::directory; - } - else if (S_ISREG(mode)) { - ft = file_type::regular; - } - else if (S_ISCHR(mode)) { - ft = file_type::character; - } - else if (S_ISBLK(mode)) { - ft = file_type::block; - } - else if (S_ISFIFO(mode)) { - ft = file_type::fifo; - } - else if (S_ISLNK(mode)) { - ft = file_type::symlink; - } - else if (S_ISSOCK(mode)) { - ft = file_type::socket; - } - perms prms = static_cast(mode & 0xfff); - return file_status(ft, prms); -#endif -} - -#ifdef GHC_OS_WINDOWS - -class unique_handle -{ -public: - typedef HANDLE element_type; - - unique_handle() noexcept - : _handle(INVALID_HANDLE_VALUE) - { - } - explicit unique_handle(element_type h) noexcept - : _handle(h) - { - } - unique_handle(unique_handle&& u) noexcept - : _handle(u.release()) - { - } - ~unique_handle() { reset(); } - unique_handle& operator=(unique_handle&& u) noexcept - { - reset(u.release()); - return *this; - } - element_type get() const noexcept { return _handle; } - explicit operator bool() const noexcept { return _handle != INVALID_HANDLE_VALUE; } - element_type release() noexcept - { - element_type tmp = _handle; - _handle = INVALID_HANDLE_VALUE; - return tmp; - } - void reset(element_type h = INVALID_HANDLE_VALUE) noexcept - { - element_type tmp = _handle; - _handle = h; - if (tmp != INVALID_HANDLE_VALUE) { - CloseHandle(tmp); - } - } - void swap(unique_handle& u) noexcept { std::swap(_handle, u._handle); } - -private: - element_type _handle; -}; - -#ifndef REPARSE_DATA_BUFFER_HEADER_SIZE -typedef struct _REPARSE_DATA_BUFFER -{ - ULONG ReparseTag; - USHORT ReparseDataLength; - USHORT Reserved; - union - { - struct - { - USHORT SubstituteNameOffset; - USHORT SubstituteNameLength; - USHORT PrintNameOffset; - USHORT PrintNameLength; - ULONG Flags; - WCHAR PathBuffer[1]; - } SymbolicLinkReparseBuffer; - struct - { - USHORT SubstituteNameOffset; - USHORT SubstituteNameLength; - USHORT PrintNameOffset; - USHORT PrintNameLength; - WCHAR PathBuffer[1]; - } MountPointReparseBuffer; - struct - { - UCHAR DataBuffer[1]; - } GenericReparseBuffer; - } DUMMYUNIONNAME; -} REPARSE_DATA_BUFFER; -#ifndef MAXIMUM_REPARSE_DATA_BUFFER_SIZE -#define MAXIMUM_REPARSE_DATA_BUFFER_SIZE (16 * 1024) -#endif -#endif - -template -struct free_deleter -{ - void operator()(T* p) const { std::free(p); } -}; - -GHC_INLINE std::unique_ptr> getReparseData(const path& p, std::error_code& ec) -{ - unique_handle file(CreateFileW(GHC_NATIVEWP(p), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, 0)); - if (!file) { - ec = detail::make_system_error(); - return nullptr; - } - - std::unique_ptr> reparseData(reinterpret_cast(std::calloc(1, MAXIMUM_REPARSE_DATA_BUFFER_SIZE))); - ULONG bufferUsed; - if (DeviceIoControl(file.get(), FSCTL_GET_REPARSE_POINT, 0, 0, reparseData.get(), MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bufferUsed, 0)) { - return reparseData; - } - else { - ec = detail::make_system_error(); - } - return nullptr; -} -#endif - -GHC_INLINE path resolveSymlink(const path& p, std::error_code& ec) -{ -#ifdef GHC_OS_WINDOWS - path result; - auto reparseData = detail::getReparseData(p, ec); - if (!ec) { - if (reparseData && IsReparseTagMicrosoft(reparseData->ReparseTag)) { - switch (reparseData->ReparseTag) { - case IO_REPARSE_TAG_SYMLINK: { - auto printName = std::wstring(&reparseData->SymbolicLinkReparseBuffer.PathBuffer[reparseData->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(WCHAR)], reparseData->SymbolicLinkReparseBuffer.PrintNameLength / sizeof(WCHAR)); - auto substituteName = - std::wstring(&reparseData->SymbolicLinkReparseBuffer.PathBuffer[reparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)], reparseData->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR)); - if (detail::endsWith(substituteName, printName) && detail::startsWith(substituteName, std::wstring(L"\\??\\"))) { - result = printName; - } - else { - result = substituteName; - } - if (reparseData->SymbolicLinkReparseBuffer.Flags & 0x1 /*SYMLINK_FLAG_RELATIVE*/) { - result = p.parent_path() / result; - } - break; - } - case IO_REPARSE_TAG_MOUNT_POINT: - result = detail::getFullPathName(GHC_NATIVEWP(p), ec); - // result = std::wstring(&reparseData->MountPointReparseBuffer.PathBuffer[reparseData->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)], reparseData->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR)); - break; - default: - break; - } - } - } - return result; -#else - size_t bufferSize = 256; - while (true) { - std::vector buffer(bufferSize, static_cast(0)); - auto rc = ::readlink(p.c_str(), buffer.data(), buffer.size()); - if (rc < 0) { - ec = detail::make_system_error(); - return path(); - } - else if (rc < static_cast(bufferSize)) { - return path(std::string(buffer.data(), static_cast(rc))); - } - bufferSize *= 2; - } - return path(); -#endif -} - -#ifdef GHC_OS_WINDOWS -GHC_INLINE time_t timeFromFILETIME(const FILETIME& ft) -{ - ULARGE_INTEGER ull; - ull.LowPart = ft.dwLowDateTime; - ull.HighPart = ft.dwHighDateTime; - return static_cast(ull.QuadPart / 10000000ULL - 11644473600ULL); -} - -GHC_INLINE void timeToFILETIME(time_t t, FILETIME& ft) -{ - ULARGE_INTEGER ull; - ull.QuadPart = static_cast((t * 10000000LL) + 116444736000000000LL); - ft.dwLowDateTime = ull.LowPart; - ft.dwHighDateTime = ull.HighPart; -} - -template -GHC_INLINE uintmax_t hard_links_from_INFO(const INFO* info) -{ - return static_cast(-1); -} - -template <> -GHC_INLINE uintmax_t hard_links_from_INFO(const BY_HANDLE_FILE_INFORMATION* info) -{ - return info->nNumberOfLinks; -} - -template -GHC_INLINE bool is_symlink_from_INFO(const path &p, const INFO* info, std::error_code& ec) -{ - if ((info->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { - auto reparseData = detail::getReparseData(p, ec); - if (!ec && reparseData && IsReparseTagMicrosoft(reparseData->ReparseTag) && reparseData->ReparseTag == IO_REPARSE_TAG_SYMLINK) { - return true; - } - } - return false; -} - -template <> -GHC_INLINE bool is_symlink_from_INFO(const path &, const WIN32_FIND_DATAW* info, std::error_code&) -{ - // dwReserved0 is undefined unless dwFileAttributes includes the - // FILE_ATTRIBUTE_REPARSE_POINT attribute according to microsoft - // documentation. In practice, dwReserved0 is not reset which - // causes it to report the incorrect symlink status. - // Note that microsoft documentation does not say whether there is - // a null value for dwReserved0, so we test for symlink directly - // instead of returning the tag which requires returning a null - // value for non-reparse-point files. - return (info->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && info->dwReserved0 == IO_REPARSE_TAG_SYMLINK; -} - -template -GHC_INLINE file_status status_from_INFO(const path& p, const INFO* info, std::error_code& ec, uintmax_t* sz = nullptr, time_t* lwt = nullptr) -{ - file_type ft = file_type::unknown; - if (is_symlink_from_INFO(p, info, ec)) { - ft = file_type::symlink; - } - else if ((info->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { - ft = file_type::directory; - } - else { - ft = file_type::regular; - } - perms prms = perms::owner_read | perms::group_read | perms::others_read; - if (!(info->dwFileAttributes & FILE_ATTRIBUTE_READONLY)) { - prms = prms | perms::owner_write | perms::group_write | perms::others_write; - } - if (has_executable_extension(p)) { - prms = prms | perms::owner_exec | perms::group_exec | perms::others_exec; - } - if (sz) { - *sz = static_cast(info->nFileSizeHigh) << (sizeof(info->nFileSizeHigh) * 8) | info->nFileSizeLow; - } - if (lwt) { - *lwt = detail::timeFromFILETIME(info->ftLastWriteTime); - } - return file_status(ft, prms); -} - -#endif - -GHC_INLINE bool is_not_found_error(std::error_code& ec) -{ -#ifdef GHC_OS_WINDOWS - return ec.value() == ERROR_FILE_NOT_FOUND || ec.value() == ERROR_PATH_NOT_FOUND || ec.value() == ERROR_INVALID_NAME; -#else - return ec.value() == ENOENT || ec.value() == ENOTDIR; -#endif -} - -GHC_INLINE file_status symlink_status_ex(const path& p, std::error_code& ec, uintmax_t* sz = nullptr, uintmax_t* nhl = nullptr, time_t* lwt = nullptr) noexcept -{ -#ifdef GHC_OS_WINDOWS - file_status fs; - WIN32_FILE_ATTRIBUTE_DATA attr; - if (!GetFileAttributesExW(GHC_NATIVEWP(p), GetFileExInfoStandard, &attr)) { - ec = detail::make_system_error(); - } - else { - ec.clear(); - fs = detail::status_from_INFO(p, &attr, ec, sz, lwt); - if (nhl) { - *nhl = 0; - } - } - if (detail::is_not_found_error(ec)) { - return file_status(file_type::not_found); - } - return ec ? file_status(file_type::none) : fs; -#else - (void)sz; - (void)nhl; - (void)lwt; - struct ::stat fs; - auto result = ::lstat(p.c_str(), &fs); - if (result == 0) { - ec.clear(); - file_status f_s = detail::file_status_from_st_mode(fs.st_mode); - return f_s; - } - ec = detail::make_system_error(); - if (detail::is_not_found_error(ec)) { - return file_status(file_type::not_found, perms::unknown); - } - return file_status(file_type::none); -#endif -} - -GHC_INLINE file_status status_ex(const path& p, std::error_code& ec, file_status* sls = nullptr, uintmax_t* sz = nullptr, uintmax_t* nhl = nullptr, time_t* lwt = nullptr, int recurse_count = 0) noexcept -{ - ec.clear(); -#ifdef GHC_OS_WINDOWS - if (recurse_count > 16) { - ec = detail::make_system_error(0x2A9 /*ERROR_STOPPED_ON_SYMLINK*/); - return file_status(file_type::unknown); - } - WIN32_FILE_ATTRIBUTE_DATA attr; - if (!::GetFileAttributesExW(GHC_NATIVEWP(p), GetFileExInfoStandard, &attr)) { - ec = detail::make_system_error(); - } - else if (attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { - auto reparseData = detail::getReparseData(p, ec); - if (!ec && reparseData && IsReparseTagMicrosoft(reparseData->ReparseTag) && reparseData->ReparseTag == IO_REPARSE_TAG_SYMLINK) { - path target = resolveSymlink(p, ec); - file_status result; - if (!ec && !target.empty()) { - if (sls) { - *sls = status_from_INFO(p, &attr, ec); - } - return detail::status_ex(target, ec, nullptr, sz, nhl, lwt, recurse_count + 1); - } - return file_status(file_type::unknown); - } - } - if (ec) { - if (detail::is_not_found_error(ec)) { - return file_status(file_type::not_found); - } - return file_status(file_type::none); - } - if (nhl) { - *nhl = 0; - } - return detail::status_from_INFO(p, &attr, ec, sz, lwt); -#else - (void)recurse_count; - struct ::stat st; - auto result = ::lstat(p.c_str(), &st); - if (result == 0) { - ec.clear(); - file_status fs = detail::file_status_from_st_mode(st.st_mode); - if (sls) { - *sls = fs; - } - if (fs.type() == file_type::symlink) { - result = ::stat(p.c_str(), &st); - if (result == 0) { - fs = detail::file_status_from_st_mode(st.st_mode); - } - else { - ec = detail::make_system_error(); - if (detail::is_not_found_error(ec)) { - return file_status(file_type::not_found, perms::unknown); - } - return file_status(file_type::none); - } - } - if (sz) { - *sz = static_cast(st.st_size); - } - if (nhl) { - *nhl = st.st_nlink; - } - if (lwt) { - *lwt = st.st_mtime; - } - return fs; - } - else { - ec = detail::make_system_error(); - if (detail::is_not_found_error(ec)) { - return file_status(file_type::not_found, perms::unknown); - } - return file_status(file_type::none); - } -#endif -} - -} // namespace detail - -GHC_INLINE u8arguments::u8arguments(int& argc, char**& argv) - : _argc(argc) - , _argv(argv) - , _refargc(argc) - , _refargv(argv) - , _isvalid(false) -{ -#ifdef GHC_OS_WINDOWS - LPWSTR* p; - p = ::CommandLineToArgvW(::GetCommandLineW(), &argc); - _args.reserve(static_cast(argc)); - _argp.reserve(static_cast(argc)); - for (size_t i = 0; i < static_cast(argc); ++i) { - _args.push_back(detail::toUtf8(std::wstring(p[i]))); - _argp.push_back((char*)_args[i].data()); - } - argv = _argp.data(); - ::LocalFree(p); - _isvalid = true; -#else - std::setlocale(LC_ALL, ""); -#if defined(__ANDROID__) && __ANDROID_API__ < 26 - _isvalid = true; -#else - if (detail::equals_simple_insensitive(::nl_langinfo(CODESET), "UTF-8")) { - _isvalid = true; - } -#endif -#endif -} - -//----------------------------------------------------------------------------- -// [fs.path.construct] constructors and destructor - -GHC_INLINE path::path() noexcept {} - -GHC_INLINE path::path(const path& p) - : _path(p._path) -#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) - , _prefixLength(p._prefixLength) -#endif -{ -} - -GHC_INLINE path::path(path&& p) noexcept - : _path(std::move(p._path)) -#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) - , _prefixLength(p._prefixLength) -#endif -{ -} - -GHC_INLINE path::path(string_type&& source, format fmt) - : _path(std::move(source)) -{ - postprocess_path_with_format(fmt); -} - -#endif // GHC_EXPAND_IMPL - -#ifdef GHC_WITH_EXCEPTIONS -template -inline path::path(const Source& source, const std::locale& loc, format fmt) - : path(source, fmt) -{ - std::string locName = loc.name(); - if (!(locName.length() >= 5 && (locName.substr(locName.length() - 5) == "UTF-8" || locName.substr(locName.length() - 5) == "utf-8"))) { - throw filesystem_error("This implementation only supports UTF-8 locales!", path(_path), detail::make_error_code(detail::portable_error::not_supported)); - } -} - -template -inline path::path(InputIterator first, InputIterator last, const std::locale& loc, format fmt) - : path(std::basic_string::value_type>(first, last), fmt) -{ - std::string locName = loc.name(); - if (!(locName.length() >= 5 && (locName.substr(locName.length() - 5) == "UTF-8" || locName.substr(locName.length() - 5) == "utf-8"))) { - throw filesystem_error("This implementation only supports UTF-8 locales!", path(_path), detail::make_error_code(detail::portable_error::not_supported)); - } -} -#endif - -#ifdef GHC_EXPAND_IMPL - -GHC_INLINE path::~path() {} - -//----------------------------------------------------------------------------- -// [fs.path.assign] assignments - -GHC_INLINE path& path::operator=(const path& p) -{ - _path = p._path; -#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) - _prefixLength = p._prefixLength; -#endif - return *this; -} - -GHC_INLINE path& path::operator=(path&& p) noexcept -{ - _path = std::move(p._path); -#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) - _prefixLength = p._prefixLength; -#endif - return *this; -} - -GHC_INLINE path& path::operator=(path::string_type&& source) -{ - return assign(source); -} - -GHC_INLINE path& path::assign(path::string_type&& source) -{ - _path = std::move(source); - postprocess_path_with_format(native_format); - return *this; -} - -#endif // GHC_EXPAND_IMPL - -template -inline path& path::operator=(const Source& source) -{ - return assign(source); -} - -template -inline path& path::assign(const Source& source) -{ -#ifdef GHC_USE_WCHAR_T - _path.assign(detail::toWChar(source)); -#else - _path.assign(detail::toUtf8(source)); -#endif - postprocess_path_with_format(native_format); - return *this; -} - -template <> -inline path& path::assign(const path& source) -{ - _path = source._path; -#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) - _prefixLength = source._prefixLength; -#endif - return *this; -} - -template -inline path& path::assign(InputIterator first, InputIterator last) -{ - _path.assign(first, last); - postprocess_path_with_format(native_format); - return *this; -} - -#ifdef GHC_EXPAND_IMPL - -//----------------------------------------------------------------------------- -// [fs.path.append] appends - -GHC_INLINE path& path::operator/=(const path& p) -{ - if (p.empty()) { - // was: if ((!has_root_directory() && is_absolute()) || has_filename()) - if (!_path.empty() && _path[_path.length() - 1] != preferred_separator && _path[_path.length() - 1] != ':') { - _path += preferred_separator; - } - return *this; - } - if ((p.is_absolute() && (_path != root_name()._path || p._path != "/")) || (p.has_root_name() && p.root_name() != root_name())) { - assign(p); - return *this; - } - if (p.has_root_directory()) { - assign(root_name()); - } - else if ((!has_root_directory() && is_absolute()) || has_filename()) { - _path += preferred_separator; - } - auto iter = p.begin(); - bool first = true; - if (p.has_root_name()) { - ++iter; - } - while (iter != p.end()) { - if (!first && !(!_path.empty() && _path[_path.length() - 1] == preferred_separator)) { - _path += preferred_separator; - } - first = false; - _path += (*iter++).native(); - } - check_long_path(); - return *this; -} - -GHC_INLINE void path::append_name(const value_type* name) -{ - if (_path.empty()) { - this->operator/=(path(name)); - } - else { - if (_path.back() != path::preferred_separator) { - _path.push_back(path::preferred_separator); - } - _path += name; - check_long_path(); - } -} - -#endif // GHC_EXPAND_IMPL - -template -inline path& path::operator/=(const Source& source) -{ - return append(source); -} - -template -inline path& path::append(const Source& source) -{ - return this->operator/=(path(source)); -} - -template <> -inline path& path::append(const path& p) -{ - return this->operator/=(p); -} - -template -inline path& path::append(InputIterator first, InputIterator last) -{ - std::basic_string::value_type> part(first, last); - return append(part); -} - -#ifdef GHC_EXPAND_IMPL - -//----------------------------------------------------------------------------- -// [fs.path.concat] concatenation - -GHC_INLINE path& path::operator+=(const path& x) -{ - return concat(x._path); -} - -GHC_INLINE path& path::operator+=(const string_type& x) -{ - return concat(x); -} - -#ifdef GHC_WITH_STRING_VIEW -GHC_INLINE path& path::operator+=(basic_string_view x) -{ - return concat(x); -} -#endif - -GHC_INLINE path& path::operator+=(const value_type* x) -{ -#ifdef GHC_WITH_STRING_VIEW - basic_string_view part(x); -#else - string_type part(x); -#endif - return concat(part); -} - -GHC_INLINE path& path::operator+=(value_type x) -{ -#ifdef GHC_OS_WINDOWS - if (x == generic_separator) { - x = preferred_separator; - } -#endif - if (_path.empty() || _path.back() != preferred_separator) { - _path += x; - } - check_long_path(); - return *this; -} - -#endif // GHC_EXPAND_IMPL - -template -inline path::path_from_string& path::operator+=(const Source& x) -{ - return concat(x); -} - -template -inline path::path_type_EcharT& path::operator+=(EcharT x) -{ -#ifdef GHC_WITH_STRING_VIEW - basic_string_view part(&x, 1); -#else - std::basic_string part(1, x); -#endif - concat(part); - return *this; -} - -template -inline path& path::concat(const Source& x) -{ - path p(x); - _path += p._path; - postprocess_path_with_format(native_format); - return *this; -} -template -inline path& path::concat(InputIterator first, InputIterator last) -{ - _path.append(first, last); - postprocess_path_with_format(native_format); - return *this; -} - -#ifdef GHC_EXPAND_IMPL - -//----------------------------------------------------------------------------- -// [fs.path.modifiers] modifiers -GHC_INLINE void path::clear() noexcept -{ - _path.clear(); -#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) - _prefixLength = 0; -#endif -} - -GHC_INLINE path& path::make_preferred() -{ - // as this filesystem implementation only uses generic_format - // internally, this must be a no-op - return *this; -} - -GHC_INLINE path& path::remove_filename() -{ - if (has_filename()) { - _path.erase(_path.size() - filename()._path.size()); - } - return *this; -} - -GHC_INLINE path& path::replace_filename(const path& replacement) -{ - remove_filename(); - return append(replacement); -} - -GHC_INLINE path& path::replace_extension(const path& replacement) -{ - if (has_extension()) { - _path.erase(_path.size() - extension()._path.size()); - } - if (!replacement.empty() && replacement._path[0] != '.') { - _path += '.'; - } - return concat(replacement); -} - -GHC_INLINE void path::swap(path& rhs) noexcept -{ - _path.swap(rhs._path); -#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) - std::swap(_prefixLength, rhs._prefixLength); -#endif -} - -//----------------------------------------------------------------------------- -// [fs.path.native.obs] native format observers -GHC_INLINE const path::string_type& path::native() const noexcept -{ - return _path; -} - -GHC_INLINE const path::value_type* path::c_str() const noexcept -{ - return native().c_str(); -} - -GHC_INLINE path::operator path::string_type() const -{ - return native(); -} - -#endif // GHC_EXPAND_IMPL - -template -inline std::basic_string path::string(const Allocator& a) const -{ -#ifdef GHC_USE_WCHAR_T - return detail::fromWChar>(_path, a); -#else - return detail::fromUtf8>(_path, a); -#endif -} - -#ifdef GHC_EXPAND_IMPL - -GHC_INLINE std::string path::string() const -{ -#ifdef GHC_USE_WCHAR_T - return detail::toUtf8(native()); -#else - return native(); -#endif -} - -GHC_INLINE std::wstring path::wstring() const -{ -#ifdef GHC_USE_WCHAR_T - return native(); -#else - return detail::fromUtf8(native()); -#endif -} - -#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) -GHC_INLINE std::u8string path::u8string() const -{ -#ifdef GHC_USE_WCHAR_T - return std::u8string(reinterpret_cast(detail::toUtf8(native()).c_str())); -#else - return std::u8string(reinterpret_cast(c_str())); -#endif -} -#else -GHC_INLINE std::string path::u8string() const -{ -#ifdef GHC_USE_WCHAR_T - return detail::toUtf8(native()); -#else - return native(); -#endif -} -#endif - -GHC_INLINE std::u16string path::u16string() const -{ - // TODO: optimize - return detail::fromUtf8(string()); -} - -GHC_INLINE std::u32string path::u32string() const -{ - // TODO: optimize - return detail::fromUtf8(string()); -} - -#endif // GHC_EXPAND_IMPL - -//----------------------------------------------------------------------------- -// [fs.path.generic.obs] generic format observers -template -inline std::basic_string path::generic_string(const Allocator& a) const -{ -#ifdef GHC_OS_WINDOWS -#ifdef GHC_USE_WCHAR_T - auto result = detail::fromWChar, path::string_type>(_path, a); -#else - auto result = detail::fromUtf8>(_path, a); -#endif - for (auto& c : result) { - if (c == preferred_separator) { - c = generic_separator; - } - } - return result; -#else - return detail::fromUtf8>(_path, a); -#endif -} - -#ifdef GHC_EXPAND_IMPL - -GHC_INLINE std::string path::generic_string() const -{ -#ifdef GHC_OS_WINDOWS - return generic_string(); -#else - return _path; -#endif -} - -GHC_INLINE std::wstring path::generic_wstring() const -{ -#ifdef GHC_OS_WINDOWS - return generic_string(); -#else - return detail::fromUtf8(_path); -#endif -} // namespace filesystem - -#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) -GHC_INLINE std::u8string path::generic_u8string() const -{ -#ifdef GHC_OS_WINDOWS - return generic_string(); -#else - return std::u8string(reinterpret_cast(_path.c_str())); -#endif -} -#else -GHC_INLINE std::string path::generic_u8string() const -{ -#ifdef GHC_OS_WINDOWS - return generic_string(); -#else - return _path; -#endif -} -#endif - -GHC_INLINE std::u16string path::generic_u16string() const -{ -#ifdef GHC_OS_WINDOWS - return generic_string(); -#else - return detail::fromUtf8(_path); -#endif -} - -GHC_INLINE std::u32string path::generic_u32string() const -{ -#ifdef GHC_OS_WINDOWS - return generic_string(); -#else - return detail::fromUtf8(_path); -#endif -} - -//----------------------------------------------------------------------------- -// [fs.path.compare] compare -GHC_INLINE int path::compare(const path& p) const noexcept -{ -#ifdef LWG_2936_BEHAVIOUR - auto rnl1 = root_name_length(); - auto rnl2 = p.root_name_length(); -#ifdef GHC_OS_WINDOWS - auto rnc = detail::compare_simple_insensitive(_path.c_str(), rnl1, p._path.c_str(), rnl2); -#else - auto rnc = _path.compare(0, rnl1, p._path, 0, (std::min(rnl1, rnl2))); -#endif - if (rnc) { - return rnc; - } - bool hrd1 = has_root_directory(), hrd2 = p.has_root_directory(); - if (hrd1 != hrd2) { - return hrd1 ? 1 : -1; - } - if (hrd1) { - ++rnl1; - ++rnl2; - } - auto iter1 = _path.begin() + static_cast(rnl1); - auto iter2 = p._path.begin() + static_cast(rnl2); - while (iter1 != _path.end() && iter2 != p._path.end() && *iter1 == *iter2) { - ++iter1; - ++iter2; - } - if (iter1 == _path.end()) { - return iter2 == p._path.end() ? 0 : -1; - } - if (iter2 == p._path.end()) { - return 1; - } - if (*iter1 == preferred_separator) { - return -1; - } - if (*iter2 == preferred_separator) { - return 1; - } - return *iter1 < *iter2 ? -1 : 1; -#else // LWG_2936_BEHAVIOUR -#ifdef GHC_OS_WINDOWS - auto rnl1 = root_name_length(); - auto rnl2 = p.root_name_length(); - auto rnc = detail::compare_simple_insensitive(_path.c_str(), rnl1, p._path.c_str(), rnl2); - if (rnc) { - return rnc; - } - return _path.compare(rnl1, std::string::npos, p._path, rnl2, std::string::npos); -#else - return _path.compare(p._path); -#endif -#endif -} - -GHC_INLINE int path::compare(const string_type& s) const -{ - return compare(path(s)); -} - -#ifdef GHC_WITH_STRING_VIEW -GHC_INLINE int path::compare(basic_string_view s) const -{ - return compare(path(s)); -} -#endif - -GHC_INLINE int path::compare(const value_type* s) const -{ - return compare(path(s)); -} - -//----------------------------------------------------------------------------- -// [fs.path.decompose] decomposition -#ifdef GHC_OS_WINDOWS -GHC_INLINE void path::handle_prefixes() -{ -#if defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) - _prefixLength = 0; - if (_path.length() >= 6 && _path[2] == '?' && std::toupper(static_cast(_path[4])) >= 'A' && std::toupper(static_cast(_path[4])) <= 'Z' && _path[5] == ':') { - if (detail::startsWith(_path, impl_string_type(GHC_PLATFORM_LITERAL("\\\\?\\"))) || detail::startsWith(_path, impl_string_type(GHC_PLATFORM_LITERAL("\\??\\")))) { - _prefixLength = 4; - } - } -#endif // GHC_WIN_AUTO_PREFIX_LONG_PATH -} -#endif - -GHC_INLINE path::string_type::size_type path::root_name_length() const noexcept -{ -#ifdef GHC_OS_WINDOWS - if (_path.length() >= _prefixLength + 2 && std::toupper(static_cast(_path[_prefixLength])) >= 'A' && std::toupper(static_cast(_path[_prefixLength])) <= 'Z' && _path[_prefixLength + 1] == ':') { - return 2; - } -#endif - if (_path.length() > _prefixLength + 2 && _path[_prefixLength] == preferred_separator && _path[_prefixLength + 1] == preferred_separator && _path[_prefixLength + 2] != preferred_separator && std::isprint(_path[_prefixLength + 2])) { - impl_string_type::size_type pos = _path.find(preferred_separator, _prefixLength + 3); - if (pos == impl_string_type::npos) { - return _path.length(); - } - else { - return pos; - } - } - return 0; -} - -GHC_INLINE path path::root_name() const -{ - return path(_path.substr(_prefixLength, root_name_length()), native_format); -} - -GHC_INLINE path path::root_directory() const -{ - if (has_root_directory()) { - static const path _root_dir(std::string(1, preferred_separator), native_format); - return _root_dir; - } - return path(); -} - -GHC_INLINE path path::root_path() const -{ - return path(root_name().string() + root_directory().string(), native_format); -} - -GHC_INLINE path path::relative_path() const -{ - auto rootPathLen = _prefixLength + root_name_length() + (has_root_directory() ? 1 : 0); - return path(_path.substr((std::min)(rootPathLen, _path.length())), generic_format); -} - -GHC_INLINE path path::parent_path() const -{ - auto rootPathLen = _prefixLength + root_name_length() + (has_root_directory() ? 1 : 0); - if (rootPathLen < _path.length()) { - if (empty()) { - return path(); - } - else { - auto piter = end(); - auto iter = piter.decrement(_path.end()); - if (iter > _path.begin() + static_cast(rootPathLen) && *iter != preferred_separator) { - --iter; - } - return path(_path.begin(), iter, native_format); - } - } - else { - return *this; - } -} - -GHC_INLINE path path::filename() const -{ - return !has_relative_path() ? path() : path(*--end()); -} - -GHC_INLINE path path::stem() const -{ - impl_string_type fn = filename().native(); - if (fn != "." && fn != "..") { - impl_string_type::size_type pos = fn.rfind('.'); - if (pos != impl_string_type::npos && pos > 0) { - return path{fn.substr(0, pos), native_format}; - } - } - return path{fn, native_format}; -} - -GHC_INLINE path path::extension() const -{ - if (has_relative_path()) { - auto iter = end(); - const auto& fn = *--iter; - impl_string_type::size_type pos = fn._path.rfind('.'); - if (pos != std::string::npos && pos > 0) { - return path(fn._path.substr(pos), native_format); - } - } - return path(); -} - -#ifdef GHC_OS_WINDOWS -namespace detail { -GHC_INLINE bool has_executable_extension(const path& p) -{ - if (p.has_relative_path()) { - auto iter = p.end(); - const auto& fn = *--iter; - auto pos = fn._path.find_last_of('.'); - if (pos == std::string::npos || pos == 0 || fn._path.length() - pos != 3) { - return false; - } - const path::value_type* ext = fn._path.c_str() + pos + 1; - if (detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("exe")) || detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("cmd")) || detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("bat")) || - detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("com"))) { - return true; - } - } - return false; -} -} // namespace detail -#endif - -//----------------------------------------------------------------------------- -// [fs.path.query] query -GHC_INLINE bool path::empty() const noexcept -{ - return _path.empty(); -} - -GHC_INLINE bool path::has_root_name() const -{ - return root_name_length() > 0; -} - -GHC_INLINE bool path::has_root_directory() const -{ - auto rootLen = _prefixLength + root_name_length(); - return (_path.length() > rootLen && _path[rootLen] == preferred_separator); -} - -GHC_INLINE bool path::has_root_path() const -{ - return has_root_name() || has_root_directory(); -} - -GHC_INLINE bool path::has_relative_path() const -{ - auto rootPathLen = _prefixLength + root_name_length() + (has_root_directory() ? 1 : 0); - return rootPathLen < _path.length(); -} - -GHC_INLINE bool path::has_parent_path() const -{ - return !parent_path().empty(); -} - -GHC_INLINE bool path::has_filename() const -{ - return has_relative_path() && !filename().empty(); -} - -GHC_INLINE bool path::has_stem() const -{ - return !stem().empty(); -} - -GHC_INLINE bool path::has_extension() const -{ - return !extension().empty(); -} - -GHC_INLINE bool path::is_absolute() const -{ -#ifdef GHC_OS_WINDOWS - return has_root_name() && has_root_directory(); -#else - return has_root_directory(); -#endif -} - -GHC_INLINE bool path::is_relative() const -{ - return !is_absolute(); -} - -//----------------------------------------------------------------------------- -// [fs.path.gen] generation -GHC_INLINE path path::lexically_normal() const -{ - path dest; - bool lastDotDot = false; - for (string_type s : *this) { - if (s == ".") { - dest /= ""; - continue; - } - else if (s == ".." && !dest.empty()) { - auto root = root_path(); - if (dest == root) { - continue; - } - else if (*(--dest.end()) != "..") { - if (dest._path.back() == preferred_separator) { - dest._path.pop_back(); - } - dest.remove_filename(); - continue; - } - } - if (!(s.empty() && lastDotDot)) { - dest /= s; - } - lastDotDot = s == ".."; - } - if (dest.empty()) { - dest = "."; - } - return dest; -} - -GHC_INLINE path path::lexically_relative(const path& base) const -{ - if (root_name() != base.root_name() || is_absolute() != base.is_absolute() || (!has_root_directory() && base.has_root_directory())) { - return path(); - } - const_iterator a = begin(), b = base.begin(); - while (a != end() && b != base.end() && *a == *b) { - ++a; - ++b; - } - if (a == end() && b == base.end()) { - return path("."); - } - int count = 0; - for (const auto& element : input_iterator_range(b, base.end())) { - if (element != "." && element != "" && element != "..") { - ++count; - } - else if (element == "..") { - --count; - } - } - if (count < 0) { - return path(); - } - path result; - for (int i = 0; i < count; ++i) { - result /= ".."; - } - for (const auto& element : input_iterator_range(a, end())) { - result /= element; - } - return result; -} - -GHC_INLINE path path::lexically_proximate(const path& base) const -{ - path result = lexically_relative(base); - return result.empty() ? *this : result; -} - -//----------------------------------------------------------------------------- -// [fs.path.itr] iterators -GHC_INLINE path::iterator::iterator() {} - -GHC_INLINE path::iterator::iterator(const path& p, const impl_string_type::const_iterator& pos) - : _first(p._path.begin()) - , _last(p._path.end()) - , _prefix(_first + static_cast(p._prefixLength)) - , _root(p.has_root_directory() ? _first + static_cast(p._prefixLength + p.root_name_length()) : _last) - , _iter(pos) -{ - if (pos != _last) { - updateCurrent(); - } -} - -GHC_INLINE path::impl_string_type::const_iterator path::iterator::increment(const path::impl_string_type::const_iterator& pos) const -{ - path::impl_string_type::const_iterator i = pos; - bool fromStart = i == _first || i == _prefix; - if (i != _last) { - if (fromStart && i == _first && _prefix > _first) { - i = _prefix; - } - else if (*i++ == preferred_separator) { - // we can only sit on a slash if it is a network name or a root - if (i != _last && *i == preferred_separator) { - if (fromStart && !(i + 1 != _last && *(i + 1) == preferred_separator)) { - // leadind double slashes detected, treat this and the - // following until a slash as one unit - i = std::find(++i, _last, preferred_separator); - } - else { - // skip redundant slashes - while (i != _last && *i == preferred_separator) { - ++i; - } - } - } - } - else { -#ifdef GHC_OS_WINDOWS - if (fromStart && i != _last && *i == ':') { - ++i; - } - else { -#else - { -#endif - i = std::find(i, _last, preferred_separator); - } - } - } - return i; -} - -GHC_INLINE path::impl_string_type::const_iterator path::iterator::decrement(const path::impl_string_type::const_iterator& pos) const -{ - path::impl_string_type::const_iterator i = pos; - if (i != _first) { - --i; - // if this is now the root slash or the trailing slash, we are done, - // else check for network name - if (i != _root && (pos != _last || *i != preferred_separator)) { -#ifdef GHC_OS_WINDOWS - static const impl_string_type seps = GHC_PLATFORM_LITERAL("\\:"); - i = std::find_first_of(std::reverse_iterator(i), std::reverse_iterator(_first), seps.begin(), seps.end()).base(); - if (i > _first && *i == ':') { - i++; - } -#else - i = std::find(std::reverse_iterator(i), std::reverse_iterator(_first), preferred_separator).base(); -#endif - // Now we have to check if this is a network name - if (i - _first == 2 && *_first == preferred_separator && *(_first + 1) == preferred_separator) { - i -= 2; - } - } - } - return i; -} - -GHC_INLINE void path::iterator::updateCurrent() -{ - if ((_iter == _last) || (_iter != _first && _iter != _last && (*_iter == preferred_separator && _iter != _root) && (_iter + 1 == _last))) { - _current.clear(); - } - else { - _current.assign(_iter, increment(_iter)); - } -} - -GHC_INLINE path::iterator& path::iterator::operator++() -{ - _iter = increment(_iter); - while (_iter != _last && // we didn't reach the end - _iter != _root && // this is not a root position - *_iter == preferred_separator && // we are on a separator - (_iter + 1) != _last // the slash is not the last char - ) { - ++_iter; - } - updateCurrent(); - return *this; -} - -GHC_INLINE path::iterator path::iterator::operator++(int) -{ - path::iterator i{*this}; - ++(*this); - return i; -} - -GHC_INLINE path::iterator& path::iterator::operator--() -{ - _iter = decrement(_iter); - updateCurrent(); - return *this; -} - -GHC_INLINE path::iterator path::iterator::operator--(int) -{ - auto i = *this; - --(*this); - return i; -} - -GHC_INLINE bool path::iterator::operator==(const path::iterator& other) const -{ - return _iter == other._iter; -} - -GHC_INLINE bool path::iterator::operator!=(const path::iterator& other) const -{ - return _iter != other._iter; -} - -GHC_INLINE path::iterator::reference path::iterator::operator*() const -{ - return _current; -} - -GHC_INLINE path::iterator::pointer path::iterator::operator->() const -{ - return &_current; -} - -GHC_INLINE path::iterator path::begin() const -{ - return iterator(*this, _path.begin()); -} - -GHC_INLINE path::iterator path::end() const -{ - return iterator(*this, _path.end()); -} - -//----------------------------------------------------------------------------- -// [fs.path.nonmember] path non-member functions -GHC_INLINE void swap(path& lhs, path& rhs) noexcept -{ - swap(lhs._path, rhs._path); -} - -GHC_INLINE size_t hash_value(const path& p) noexcept -{ - return std::hash()(p.generic_string()); -} - -#ifdef GHC_HAS_THREEWAY_COMP -GHC_INLINE std::strong_ordering operator<=>(const path& lhs, const path& rhs) noexcept -{ - return lhs.compare(rhs) <=> 0; -} -#endif - -GHC_INLINE bool operator==(const path& lhs, const path& rhs) noexcept -{ - return lhs.compare(rhs) == 0; -} - -GHC_INLINE bool operator!=(const path& lhs, const path& rhs) noexcept -{ - return !(lhs == rhs); -} - -GHC_INLINE bool operator<(const path& lhs, const path& rhs) noexcept -{ - return lhs.compare(rhs) < 0; -} - -GHC_INLINE bool operator<=(const path& lhs, const path& rhs) noexcept -{ - return lhs.compare(rhs) <= 0; -} - -GHC_INLINE bool operator>(const path& lhs, const path& rhs) noexcept -{ - return lhs.compare(rhs) > 0; -} - -GHC_INLINE bool operator>=(const path& lhs, const path& rhs) noexcept -{ - return lhs.compare(rhs) >= 0; -} - -GHC_INLINE path operator/(const path& lhs, const path& rhs) -{ - path result(lhs); - result /= rhs; - return result; -} - -#endif // GHC_EXPAND_IMPL - -//----------------------------------------------------------------------------- -// [fs.path.io] path inserter and extractor -template -inline std::basic_ostream& operator<<(std::basic_ostream& os, const path& p) -{ - os << "\""; - auto ps = p.string(); - for (auto c : ps) { - if (c == '"' || c == '\\') { - os << '\\'; - } - os << c; - } - os << "\""; - return os; -} - -template -inline std::basic_istream& operator>>(std::basic_istream& is, path& p) -{ - std::basic_string tmp; - charT c; - is >> c; - if (c == '"') { - auto sf = is.flags(); - is >> std::noskipws; - while (is) { - auto c2 = is.get(); - if (is) { - if (c2 == '\\') { - c2 = is.get(); - if (is) { - tmp += static_cast(c2); - } - } - else if (c2 == '"') { - break; - } - else { - tmp += static_cast(c2); - } - } - } - if ((sf & std::ios_base::skipws) == std::ios_base::skipws) { - is >> std::skipws; - } - p = path(tmp); - } - else { - is >> tmp; - p = path(static_cast(c) + tmp); - } - return is; -} - -#ifdef GHC_EXPAND_IMPL - -//----------------------------------------------------------------------------- -// [fs.class.filesystem_error] Class filesystem_error -GHC_INLINE filesystem_error::filesystem_error(const std::string& what_arg, std::error_code ec) - : std::system_error(ec, what_arg) - , _what_arg(what_arg) - , _ec(ec) -{ -} - -GHC_INLINE filesystem_error::filesystem_error(const std::string& what_arg, const path& p1, std::error_code ec) - : std::system_error(ec, what_arg) - , _what_arg(what_arg) - , _ec(ec) - , _p1(p1) -{ - if (!_p1.empty()) { - _what_arg += ": '" + _p1.string() + "'"; - } -} - -GHC_INLINE filesystem_error::filesystem_error(const std::string& what_arg, const path& p1, const path& p2, std::error_code ec) - : std::system_error(ec, what_arg) - , _what_arg(what_arg) - , _ec(ec) - , _p1(p1) - , _p2(p2) -{ - if (!_p1.empty()) { - _what_arg += ": '" + _p1.string() + "'"; - } - if (!_p2.empty()) { - _what_arg += ", '" + _p2.string() + "'"; - } -} - -GHC_INLINE const path& filesystem_error::path1() const noexcept -{ - return _p1; -} - -GHC_INLINE const path& filesystem_error::path2() const noexcept -{ - return _p2; -} - -GHC_INLINE const char* filesystem_error::what() const noexcept -{ - return _what_arg.c_str(); -} - -//----------------------------------------------------------------------------- -// [fs.op.funcs] filesystem operations -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE path absolute(const path& p) -{ - std::error_code ec; - path result = absolute(p, ec); - if (ec) { - throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); - } - return result; -} -#endif - -GHC_INLINE path absolute(const path& p, std::error_code& ec) -{ - ec.clear(); -#ifdef GHC_OS_WINDOWS - if (p.empty()) { - return absolute(current_path(ec), ec) / ""; - } - ULONG size = ::GetFullPathNameW(GHC_NATIVEWP(p), 0, 0, 0); - if (size) { - std::vector buf(size, 0); - ULONG s2 = GetFullPathNameW(GHC_NATIVEWP(p), size, buf.data(), nullptr); - if (s2 && s2 < size) { - path result = path(std::wstring(buf.data(), s2)); - if (p.filename() == ".") { - result /= "."; - } - return result; - } - } - ec = detail::make_system_error(); - return path(); -#else - path base = current_path(ec); - if (!ec) { - if (p.empty()) { - return base / p; - } - if (p.has_root_name()) { - if (p.has_root_directory()) { - return p; - } - else { - return p.root_name() / base.root_directory() / base.relative_path() / p.relative_path(); - } - } - else { - if (p.has_root_directory()) { - return base.root_name() / p; - } - else { - return base / p; - } - } - } - ec = detail::make_system_error(); - return path(); -#endif -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE path canonical(const path& p) -{ - std::error_code ec; - auto result = canonical(p, ec); - if (ec) { - throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); - } - return result; -} -#endif - -GHC_INLINE path canonical(const path& p, std::error_code& ec) -{ - if (p.empty()) { - ec = detail::make_error_code(detail::portable_error::not_found); - return path(); - } - path work = p.is_absolute() ? p : absolute(p, ec); - path result; - - auto fs = status(work, ec); - if (ec) { - return path(); - } - if (fs.type() == file_type::not_found) { - ec = detail::make_error_code(detail::portable_error::not_found); - return path(); - } - bool redo; - do { - auto rootPathLen = work._prefixLength + work.root_name_length() + (work.has_root_directory() ? 1 : 0); - redo = false; - result.clear(); - for (auto pe : work) { - if (pe.empty() || pe == ".") { - continue; - } - else if (pe == "..") { - result = result.parent_path(); - continue; - } - else if ((result / pe).string().length() <= rootPathLen) { - result /= pe; - continue; - } - auto sls = symlink_status(result / pe, ec); - if (ec) { - return path(); - } - if (is_symlink(sls)) { - redo = true; - auto target = read_symlink(result / pe, ec); - if (ec) { - return path(); - } - if (target.is_absolute()) { - result = target; - continue; - } - else { - result /= target; - continue; - } - } - else { - result /= pe; - } - } - work = result; - } while (redo); - ec.clear(); - return result; -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE void copy(const path& from, const path& to) -{ - copy(from, to, copy_options::none); -} - -GHC_INLINE void copy(const path& from, const path& to, copy_options options) -{ - std::error_code ec; - copy(from, to, options, ec); - if (ec) { - throw filesystem_error(detail::systemErrorText(ec.value()), from, to, ec); - } -} -#endif - -GHC_INLINE void copy(const path& from, const path& to, std::error_code& ec) noexcept -{ - copy(from, to, copy_options::none, ec); -} - -GHC_INLINE void copy(const path& from, const path& to, copy_options options, std::error_code& ec) noexcept -{ - std::error_code tec; - file_status fs_from, fs_to; - ec.clear(); - if ((options & (copy_options::skip_symlinks | copy_options::copy_symlinks | copy_options::create_symlinks)) != copy_options::none) { - fs_from = symlink_status(from, ec); - } - else { - fs_from = status(from, ec); - } - if (!exists(fs_from)) { - if (!ec) { - ec = detail::make_error_code(detail::portable_error::not_found); - } - return; - } - if ((options & (copy_options::skip_symlinks | copy_options::create_symlinks)) != copy_options::none) { - fs_to = symlink_status(to, tec); - } - else { - fs_to = status(to, tec); - } - if (is_other(fs_from) || is_other(fs_to) || (is_directory(fs_from) && is_regular_file(fs_to)) || (exists(fs_to) && equivalent(from, to, ec))) { - ec = detail::make_error_code(detail::portable_error::invalid_argument); - } - else if (is_symlink(fs_from)) { - if ((options & copy_options::skip_symlinks) == copy_options::none) { - if (!exists(fs_to) && (options & copy_options::copy_symlinks) != copy_options::none) { - copy_symlink(from, to, ec); - } - else { - ec = detail::make_error_code(detail::portable_error::invalid_argument); - } - } - } - else if (is_regular_file(fs_from)) { - if ((options & copy_options::directories_only) == copy_options::none) { - if ((options & copy_options::create_symlinks) != copy_options::none) { - create_symlink(from.is_absolute() ? from : canonical(from, ec), to, ec); - } -#ifndef GHC_OS_WEB - else if ((options & copy_options::create_hard_links) != copy_options::none) { - create_hard_link(from, to, ec); - } -#endif - else if (is_directory(fs_to)) { - copy_file(from, to / from.filename(), options, ec); - } - else { - copy_file(from, to, options, ec); - } - } - } -#ifdef LWG_2682_BEHAVIOUR - else if (is_directory(fs_from) && (options & copy_options::create_symlinks) != copy_options::none) { - ec = detail::make_error_code(detail::portable_error::is_a_directory); - } -#endif - else if (is_directory(fs_from) && (options == copy_options::none || (options & copy_options::recursive) != copy_options::none)) { - if (!exists(fs_to)) { - create_directory(to, from, ec); - if (ec) { - return; - } - } - for (auto iter = directory_iterator(from, ec); iter != directory_iterator(); iter.increment(ec)) { - if (!ec) { - copy(iter->path(), to / iter->path().filename(), options | static_cast(0x8000), ec); - } - if (ec) { - return; - } - } - } - return; -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE bool copy_file(const path& from, const path& to) -{ - return copy_file(from, to, copy_options::none); -} - -GHC_INLINE bool copy_file(const path& from, const path& to, copy_options option) -{ - std::error_code ec; - auto result = copy_file(from, to, option, ec); - if (ec) { - throw filesystem_error(detail::systemErrorText(ec.value()), from, to, ec); - } - return result; -} -#endif - -GHC_INLINE bool copy_file(const path& from, const path& to, std::error_code& ec) noexcept -{ - return copy_file(from, to, copy_options::none, ec); -} - -GHC_INLINE bool copy_file(const path& from, const path& to, copy_options options, std::error_code& ec) noexcept -{ - std::error_code tecf, tect; - auto sf = status(from, tecf); - auto st = status(to, tect); - bool overwrite = false; - ec.clear(); - if (!is_regular_file(sf)) { - ec = tecf; - return false; - } - if (exists(st)) { - if ((options & copy_options::skip_existing) == copy_options::skip_existing) { - return false; - } - if (!is_regular_file(st) || equivalent(from, to, ec) || (options & (copy_options::overwrite_existing | copy_options::update_existing)) == copy_options::none) { - ec = tect ? tect : detail::make_error_code(detail::portable_error::exists); - return false; - } - if ((options & copy_options::update_existing) == copy_options::update_existing) { - auto from_time = last_write_time(from, ec); - if (ec) { - ec = detail::make_system_error(); - return false; - } - auto to_time = last_write_time(to, ec); - if (ec) { - ec = detail::make_system_error(); - return false; - } - if (from_time <= to_time) { - return false; - } - } - overwrite = true; - } -#ifdef GHC_OS_WINDOWS - if (!::CopyFileW(GHC_NATIVEWP(from), GHC_NATIVEWP(to), !overwrite)) { - ec = detail::make_system_error(); - return false; - } - return true; -#else - std::vector buffer(16384, '\0'); - int in = -1, out = -1; - if ((in = ::open(from.c_str(), O_RDONLY)) < 0) { - ec = detail::make_system_error(); - return false; - } - int mode = O_CREAT | O_WRONLY | O_TRUNC; - if (!overwrite) { - mode |= O_EXCL; - } - if ((out = ::open(to.c_str(), mode, static_cast(sf.permissions() & perms::all))) < 0) { - ec = detail::make_system_error(); - ::close(in); - return false; - } - if (st.permissions() != sf.permissions()) { - if (::fchmod(out, static_cast(sf.permissions() & perms::all)) != 0) { - ec = detail::make_system_error(); - ::close(in); - ::close(out); - return false; - } - } - ssize_t br, bw; - while (true) { - do { br = ::read(in, buffer.data(), buffer.size()); } while(errno == EINTR); - if(!br) { - break; - } - if(br < 0) { - ec = detail::make_system_error(); - ::close(in); - ::close(out); - return false; - } - ssize_t offset = 0; - do { - if ((bw = ::write(out, buffer.data() + offset, static_cast(br))) > 0) { - br -= bw; - offset += bw; - } - else if (bw < 0 && errno != EINTR) { - ec = detail::make_system_error(); - ::close(in); - ::close(out); - return false; - } - } while (br); - } - ::close(in); - ::close(out); - return true; -#endif -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE void copy_symlink(const path& existing_symlink, const path& new_symlink) -{ - std::error_code ec; - copy_symlink(existing_symlink, new_symlink, ec); - if (ec) { - throw filesystem_error(detail::systemErrorText(ec.value()), existing_symlink, new_symlink, ec); - } -} -#endif - -GHC_INLINE void copy_symlink(const path& existing_symlink, const path& new_symlink, std::error_code& ec) noexcept -{ - ec.clear(); - auto to = read_symlink(existing_symlink, ec); - if (!ec) { - if (exists(to, ec) && is_directory(to, ec)) { - create_directory_symlink(to, new_symlink, ec); - } - else { - create_symlink(to, new_symlink, ec); - } - } -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE bool create_directories(const path& p) -{ - std::error_code ec; - auto result = create_directories(p, ec); - if (ec) { - throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); - } - return result; -} -#endif - -GHC_INLINE bool create_directories(const path& p, std::error_code& ec) noexcept -{ - path current; - ec.clear(); - bool didCreate = false; - auto rootPathLen = p._prefixLength + p.root_name_length() + (p.has_root_directory() ? 1 : 0); - current = p.native().substr(0, rootPathLen); - path folders(p._path.substr(rootPathLen)); - for (path::string_type part : folders) { - current /= part; - std::error_code tec; - auto fs = status(current, tec); - if (tec && fs.type() != file_type::not_found) { - ec = tec; - return false; - } - if (!exists(fs)) { - create_directory(current, ec); - if (ec) { - std::error_code tmp_ec; - if (is_directory(current, tmp_ec)) { - ec.clear(); - } - else { - return false; - } - } - didCreate = true; - } -#ifndef LWG_2935_BEHAVIOUR - else if (!is_directory(fs)) { - ec = detail::make_error_code(detail::portable_error::exists); - return false; - } -#endif - } - return didCreate; -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE bool create_directory(const path& p) -{ - std::error_code ec; - auto result = create_directory(p, path(), ec); - if (ec) { - throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); - } - return result; -} -#endif - -GHC_INLINE bool create_directory(const path& p, std::error_code& ec) noexcept -{ - return create_directory(p, path(), ec); -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE bool create_directory(const path& p, const path& attributes) -{ - std::error_code ec; - auto result = create_directory(p, attributes, ec); - if (ec) { - throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); - } - return result; -} -#endif - -GHC_INLINE bool create_directory(const path& p, const path& attributes, std::error_code& ec) noexcept -{ - std::error_code tec; - ec.clear(); - auto fs = status(p, tec); -#ifdef LWG_2935_BEHAVIOUR - if (status_known(fs) && exists(fs)) { - return false; - } -#else - if (status_known(fs) && exists(fs) && is_directory(fs)) { - return false; - } -#endif -#ifdef GHC_OS_WINDOWS - if (!attributes.empty()) { - if (!::CreateDirectoryExW(GHC_NATIVEWP(attributes), GHC_NATIVEWP(p), NULL)) { - ec = detail::make_system_error(); - return false; - } - } - else if (!::CreateDirectoryW(GHC_NATIVEWP(p), NULL)) { - ec = detail::make_system_error(); - return false; - } -#else - ::mode_t attribs = static_cast(perms::all); - if (!attributes.empty()) { - struct ::stat fileStat; - if (::stat(attributes.c_str(), &fileStat) != 0) { - ec = detail::make_system_error(); - return false; - } - attribs = fileStat.st_mode; - } - if (::mkdir(p.c_str(), attribs) != 0) { - ec = detail::make_system_error(); - return false; - } -#endif - return true; -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE void create_directory_symlink(const path& to, const path& new_symlink) -{ - std::error_code ec; - create_directory_symlink(to, new_symlink, ec); - if (ec) { - throw filesystem_error(detail::systemErrorText(ec.value()), to, new_symlink, ec); - } -} -#endif - -GHC_INLINE void create_directory_symlink(const path& to, const path& new_symlink, std::error_code& ec) noexcept -{ - detail::create_symlink(to, new_symlink, true, ec); -} - -#ifndef GHC_OS_WEB -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE void create_hard_link(const path& to, const path& new_hard_link) -{ - std::error_code ec; - create_hard_link(to, new_hard_link, ec); - if (ec) { - throw filesystem_error(detail::systemErrorText(ec.value()), to, new_hard_link, ec); - } -} -#endif - -GHC_INLINE void create_hard_link(const path& to, const path& new_hard_link, std::error_code& ec) noexcept -{ - detail::create_hardlink(to, new_hard_link, ec); -} -#endif - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE void create_symlink(const path& to, const path& new_symlink) -{ - std::error_code ec; - create_symlink(to, new_symlink, ec); - if (ec) { - throw filesystem_error(detail::systemErrorText(ec.value()), to, new_symlink, ec); - } -} -#endif - -GHC_INLINE void create_symlink(const path& to, const path& new_symlink, std::error_code& ec) noexcept -{ - detail::create_symlink(to, new_symlink, false, ec); -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE path current_path() -{ - std::error_code ec; - auto result = current_path(ec); - if (ec) { - throw filesystem_error(detail::systemErrorText(ec.value()), ec); - } - return result; -} -#endif - -GHC_INLINE path current_path(std::error_code& ec) -{ - ec.clear(); -#ifdef GHC_OS_WINDOWS - DWORD pathlen = ::GetCurrentDirectoryW(0, 0); - std::unique_ptr buffer(new wchar_t[size_t(pathlen) + 1]); - if (::GetCurrentDirectoryW(pathlen, buffer.get()) == 0) { - ec = detail::make_system_error(); - return path(); - } - return path(std::wstring(buffer.get()), path::native_format); -#elif defined(__GLIBC__) - std::unique_ptr buffer { ::getcwd(NULL, 0), std::free }; - if (buffer == nullptr) { - ec = detail::make_system_error(); - return path(); - } - return path(buffer.get()); -#else - size_t pathlen = static_cast(std::max(int(::pathconf(".", _PC_PATH_MAX)), int(PATH_MAX))); - std::unique_ptr buffer(new char[pathlen + 1]); - if (::getcwd(buffer.get(), pathlen) == nullptr) { - ec = detail::make_system_error(); - return path(); - } - return path(buffer.get()); -#endif -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE void current_path(const path& p) -{ - std::error_code ec; - current_path(p, ec); - if (ec) { - throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); - } -} -#endif - -GHC_INLINE void current_path(const path& p, std::error_code& ec) noexcept -{ - ec.clear(); -#ifdef GHC_OS_WINDOWS - if (!::SetCurrentDirectoryW(GHC_NATIVEWP(p))) { - ec = detail::make_system_error(); - } -#else - if (::chdir(p.string().c_str()) == -1) { - ec = detail::make_system_error(); - } -#endif -} - -GHC_INLINE bool exists(file_status s) noexcept -{ - return status_known(s) && s.type() != file_type::not_found; -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE bool exists(const path& p) -{ - return exists(status(p)); -} -#endif - -GHC_INLINE bool exists(const path& p, std::error_code& ec) noexcept -{ - file_status s = status(p, ec); - if (status_known(s)) { - ec.clear(); - } - return exists(s); -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE bool equivalent(const path& p1, const path& p2) -{ - std::error_code ec; - bool result = equivalent(p1, p2, ec); - if (ec) { - throw filesystem_error(detail::systemErrorText(ec.value()), p1, p2, ec); - } - return result; -} -#endif - -GHC_INLINE bool equivalent(const path& p1, const path& p2, std::error_code& ec) noexcept -{ - ec.clear(); -#ifdef GHC_OS_WINDOWS - detail::unique_handle file1(::CreateFileW(GHC_NATIVEWP(p1), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)); - auto e1 = ::GetLastError(); - detail::unique_handle file2(::CreateFileW(GHC_NATIVEWP(p2), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)); - if (!file1 || !file2) { -#ifdef LWG_2937_BEHAVIOUR - ec = detail::make_system_error(e1 ? e1 : ::GetLastError()); -#else - if (file1 == file2) { - ec = detail::make_system_error(e1 ? e1 : ::GetLastError()); - } -#endif - return false; - } - BY_HANDLE_FILE_INFORMATION inf1, inf2; - if (!::GetFileInformationByHandle(file1.get(), &inf1)) { - ec = detail::make_system_error(); - return false; - } - if (!::GetFileInformationByHandle(file2.get(), &inf2)) { - ec = detail::make_system_error(); - return false; - } - return inf1.ftLastWriteTime.dwLowDateTime == inf2.ftLastWriteTime.dwLowDateTime && inf1.ftLastWriteTime.dwHighDateTime == inf2.ftLastWriteTime.dwHighDateTime && inf1.nFileIndexHigh == inf2.nFileIndexHigh && inf1.nFileIndexLow == inf2.nFileIndexLow && - inf1.nFileSizeHigh == inf2.nFileSizeHigh && inf1.nFileSizeLow == inf2.nFileSizeLow && inf1.dwVolumeSerialNumber == inf2.dwVolumeSerialNumber; -#else - struct ::stat s1, s2; - auto rc1 = ::stat(p1.c_str(), &s1); - auto e1 = errno; - auto rc2 = ::stat(p2.c_str(), &s2); - if (rc1 || rc2) { -#ifdef LWG_2937_BEHAVIOUR - ec = detail::make_system_error(e1 ? e1 : errno); -#else - if (rc1 && rc2) { - ec = detail::make_system_error(e1 ? e1 : errno); - } -#endif - return false; - } - return s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino && s1.st_size == s2.st_size && s1.st_mtime == s2.st_mtime; -#endif -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE uintmax_t file_size(const path& p) -{ - std::error_code ec; - auto result = file_size(p, ec); - if (ec) { - throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); - } - return result; -} -#endif - -GHC_INLINE uintmax_t file_size(const path& p, std::error_code& ec) noexcept -{ - ec.clear(); -#ifdef GHC_OS_WINDOWS - WIN32_FILE_ATTRIBUTE_DATA attr; - if (!GetFileAttributesExW(GHC_NATIVEWP(p), GetFileExInfoStandard, &attr)) { - ec = detail::make_system_error(); - return static_cast(-1); - } - return static_cast(attr.nFileSizeHigh) << (sizeof(attr.nFileSizeHigh) * 8) | attr.nFileSizeLow; -#else - struct ::stat fileStat; - if (::stat(p.c_str(), &fileStat) == -1) { - ec = detail::make_system_error(); - return static_cast(-1); - } - return static_cast(fileStat.st_size); -#endif -} - -#ifndef GHC_OS_WEB -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE uintmax_t hard_link_count(const path& p) -{ - std::error_code ec; - auto result = hard_link_count(p, ec); - if (ec) { - throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); - } - return result; -} -#endif - -GHC_INLINE uintmax_t hard_link_count(const path& p, std::error_code& ec) noexcept -{ - ec.clear(); -#ifdef GHC_OS_WINDOWS - uintmax_t result = static_cast(-1); - detail::unique_handle file(::CreateFileW(GHC_NATIVEWP(p), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)); - BY_HANDLE_FILE_INFORMATION inf; - if (!file) { - ec = detail::make_system_error(); - } - else { - if (!::GetFileInformationByHandle(file.get(), &inf)) { - ec = detail::make_system_error(); - } - else { - result = inf.nNumberOfLinks; - } - } - return result; -#else - uintmax_t result = 0; - file_status fs = detail::status_ex(p, ec, nullptr, nullptr, &result, nullptr); - if (fs.type() == file_type::not_found) { - ec = detail::make_error_code(detail::portable_error::not_found); - } - return ec ? static_cast(-1) : result; -#endif -} -#endif - -GHC_INLINE bool is_block_file(file_status s) noexcept -{ - return s.type() == file_type::block; -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE bool is_block_file(const path& p) -{ - return is_block_file(status(p)); -} -#endif - -GHC_INLINE bool is_block_file(const path& p, std::error_code& ec) noexcept -{ - return is_block_file(status(p, ec)); -} - -GHC_INLINE bool is_character_file(file_status s) noexcept -{ - return s.type() == file_type::character; -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE bool is_character_file(const path& p) -{ - return is_character_file(status(p)); -} -#endif - -GHC_INLINE bool is_character_file(const path& p, std::error_code& ec) noexcept -{ - return is_character_file(status(p, ec)); -} - -GHC_INLINE bool is_directory(file_status s) noexcept -{ - return s.type() == file_type::directory; -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE bool is_directory(const path& p) -{ - return is_directory(status(p)); -} -#endif - -GHC_INLINE bool is_directory(const path& p, std::error_code& ec) noexcept -{ - return is_directory(status(p, ec)); -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE bool is_empty(const path& p) -{ - if (is_directory(p)) { - return directory_iterator(p) == directory_iterator(); - } - else { - return file_size(p) == 0; - } -} -#endif - -GHC_INLINE bool is_empty(const path& p, std::error_code& ec) noexcept -{ - auto fs = status(p, ec); - if (ec) { - return false; - } - if (is_directory(fs)) { - directory_iterator iter(p, ec); - if (ec) { - return false; - } - return iter == directory_iterator(); - } - else { - auto sz = file_size(p, ec); - if (ec) { - return false; - } - return sz == 0; - } -} - -GHC_INLINE bool is_fifo(file_status s) noexcept -{ - return s.type() == file_type::fifo; -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE bool is_fifo(const path& p) -{ - return is_fifo(status(p)); -} -#endif - -GHC_INLINE bool is_fifo(const path& p, std::error_code& ec) noexcept -{ - return is_fifo(status(p, ec)); -} - -GHC_INLINE bool is_other(file_status s) noexcept -{ - return exists(s) && !is_regular_file(s) && !is_directory(s) && !is_symlink(s); -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE bool is_other(const path& p) -{ - return is_other(status(p)); -} -#endif - -GHC_INLINE bool is_other(const path& p, std::error_code& ec) noexcept -{ - return is_other(status(p, ec)); -} - -GHC_INLINE bool is_regular_file(file_status s) noexcept -{ - return s.type() == file_type::regular; -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE bool is_regular_file(const path& p) -{ - return is_regular_file(status(p)); -} -#endif - -GHC_INLINE bool is_regular_file(const path& p, std::error_code& ec) noexcept -{ - return is_regular_file(status(p, ec)); -} - -GHC_INLINE bool is_socket(file_status s) noexcept -{ - return s.type() == file_type::socket; -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE bool is_socket(const path& p) -{ - return is_socket(status(p)); -} -#endif - -GHC_INLINE bool is_socket(const path& p, std::error_code& ec) noexcept -{ - return is_socket(status(p, ec)); -} - -GHC_INLINE bool is_symlink(file_status s) noexcept -{ - return s.type() == file_type::symlink; -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE bool is_symlink(const path& p) -{ - return is_symlink(symlink_status(p)); -} -#endif - -GHC_INLINE bool is_symlink(const path& p, std::error_code& ec) noexcept -{ - return is_symlink(symlink_status(p, ec)); -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE file_time_type last_write_time(const path& p) -{ - std::error_code ec; - auto result = last_write_time(p, ec); - if (ec) { - throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); - } - return result; -} -#endif - -GHC_INLINE file_time_type last_write_time(const path& p, std::error_code& ec) noexcept -{ - time_t result = 0; - ec.clear(); - file_status fs = detail::status_ex(p, ec, nullptr, nullptr, nullptr, &result); - return ec ? (file_time_type::min)() : std::chrono::system_clock::from_time_t(result); -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE void last_write_time(const path& p, file_time_type new_time) -{ - std::error_code ec; - last_write_time(p, new_time, ec); - if (ec) { - throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); - } -} -#endif - -GHC_INLINE void last_write_time(const path& p, file_time_type new_time, std::error_code& ec) noexcept -{ - ec.clear(); - auto d = new_time.time_since_epoch(); -#ifdef GHC_OS_WINDOWS - detail::unique_handle file(::CreateFileW(GHC_NATIVEWP(p), FILE_WRITE_ATTRIBUTES, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL)); - FILETIME ft; - auto tt = std::chrono::duration_cast(d).count() * 10 + 116444736000000000; - ft.dwLowDateTime = static_cast(tt); - ft.dwHighDateTime = static_cast(tt >> 32); - if (!::SetFileTime(file.get(), 0, 0, &ft)) { - ec = detail::make_system_error(); - } -#elif defined(GHC_OS_MACOS) && \ - (__MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_13) || (__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_11_0) || \ - (__TV_OS_VERSION_MIN_REQUIRED < __TVOS_11_0) || (__WATCH_OS_VERSION_MIN_REQUIRED < __WATCHOS_4_0) - struct ::stat fs; - if (::stat(p.c_str(), &fs) == 0) { - struct ::timeval tv[2]; - tv[0].tv_sec = fs.st_atimespec.tv_sec; - tv[0].tv_usec = static_cast(fs.st_atimespec.tv_nsec / 1000); - tv[1].tv_sec = std::chrono::duration_cast(d).count(); - tv[1].tv_usec = static_cast(std::chrono::duration_cast(d).count() % 1000000); - if (::utimes(p.c_str(), tv) == 0) { - return; - } - } - ec = detail::make_system_error(); - return; -#else -#ifndef UTIME_OMIT -#define UTIME_OMIT ((1l << 30) - 2l) -#endif - struct ::timespec times[2]; - times[0].tv_sec = 0; - times[0].tv_nsec = UTIME_OMIT; - times[1].tv_sec = static_cast(std::chrono::duration_cast(d).count()); - times[1].tv_nsec = static_cast(std::chrono::duration_cast(d).count() % 1000000000); -#if defined(__ANDROID_API__) && __ANDROID_API__ < 12 - if (syscall(__NR_utimensat, AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) { -#else - if (::utimensat((int)AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) { -#endif - ec = detail::make_system_error(); - } - return; -#endif -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE void permissions(const path& p, perms prms, perm_options opts) -{ - std::error_code ec; - permissions(p, prms, opts, ec); - if (ec) { - throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); - } -} -#endif - -GHC_INLINE void permissions(const path& p, perms prms, std::error_code& ec) noexcept -{ - permissions(p, prms, perm_options::replace, ec); -} - -GHC_INLINE void permissions(const path& p, perms prms, perm_options opts, std::error_code& ec) noexcept -{ - if (static_cast(opts & (perm_options::replace | perm_options::add | perm_options::remove)) == 0) { - ec = detail::make_error_code(detail::portable_error::invalid_argument); - return; - } - auto fs = symlink_status(p, ec); - if ((opts & perm_options::replace) != perm_options::replace) { - if ((opts & perm_options::add) == perm_options::add) { - prms = fs.permissions() | prms; - } - else { - prms = fs.permissions() & ~prms; - } - } -#ifdef GHC_OS_WINDOWS -#ifdef __GNUC__ - auto oldAttr = GetFileAttributesW(GHC_NATIVEWP(p)); - if (oldAttr != INVALID_FILE_ATTRIBUTES) { - DWORD newAttr = ((prms & perms::owner_write) == perms::owner_write) ? oldAttr & ~(static_cast(FILE_ATTRIBUTE_READONLY)) : oldAttr | FILE_ATTRIBUTE_READONLY; - if (oldAttr == newAttr || SetFileAttributesW(GHC_NATIVEWP(p), newAttr)) { - return; - } - } - ec = detail::make_system_error(); -#else - int mode = 0; - if ((prms & perms::owner_read) == perms::owner_read) { - mode |= _S_IREAD; - } - if ((prms & perms::owner_write) == perms::owner_write) { - mode |= _S_IWRITE; - } - if (::_wchmod(p.wstring().c_str(), mode) != 0) { - ec = detail::make_system_error(); - } -#endif -#else - if ((opts & perm_options::nofollow) != perm_options::nofollow) { - if (::chmod(p.c_str(), static_cast(prms)) != 0) { - ec = detail::make_system_error(); - } - } -#endif -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE path proximate(const path& p, std::error_code& ec) -{ - auto cp = current_path(ec); - if (!ec) { - return proximate(p, cp, ec); - } - return path(); -} -#endif - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE path proximate(const path& p, const path& base) -{ - return weakly_canonical(p).lexically_proximate(weakly_canonical(base)); -} -#endif - -GHC_INLINE path proximate(const path& p, const path& base, std::error_code& ec) -{ - return weakly_canonical(p, ec).lexically_proximate(weakly_canonical(base, ec)); -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE path read_symlink(const path& p) -{ - std::error_code ec; - auto result = read_symlink(p, ec); - if (ec) { - throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); - } - return result; -} -#endif - -GHC_INLINE path read_symlink(const path& p, std::error_code& ec) -{ - file_status fs = symlink_status(p, ec); - if (fs.type() != file_type::symlink) { - ec = detail::make_error_code(detail::portable_error::invalid_argument); - return path(); - } - auto result = detail::resolveSymlink(p, ec); - return ec ? path() : result; -} - -GHC_INLINE path relative(const path& p, std::error_code& ec) -{ - return relative(p, current_path(ec), ec); -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE path relative(const path& p, const path& base) -{ - return weakly_canonical(p).lexically_relative(weakly_canonical(base)); -} -#endif - -GHC_INLINE path relative(const path& p, const path& base, std::error_code& ec) -{ - return weakly_canonical(p, ec).lexically_relative(weakly_canonical(base, ec)); -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE bool remove(const path& p) -{ - std::error_code ec; - auto result = remove(p, ec); - if (ec) { - throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); - } - return result; -} -#endif - -GHC_INLINE bool remove(const path& p, std::error_code& ec) noexcept -{ - ec.clear(); -#ifdef GHC_OS_WINDOWS -#ifdef GHC_USE_WCHAR_T - auto cstr = p.c_str(); -#else - std::wstring np = detail::fromUtf8(p.u8string()); - auto cstr = np.c_str(); -#endif - DWORD attr = GetFileAttributesW(cstr); - if (attr == INVALID_FILE_ATTRIBUTES) { - auto error = ::GetLastError(); - if (error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND) { - return false; - } - ec = detail::make_system_error(error); - } - else if (attr & FILE_ATTRIBUTE_READONLY) { - auto new_attr = attr & ~static_cast(FILE_ATTRIBUTE_READONLY); - if (!SetFileAttributesW(cstr, new_attr)) { - auto error = ::GetLastError(); - ec = detail::make_system_error(error); - } - } - if (!ec) { - if (attr & FILE_ATTRIBUTE_DIRECTORY) { - if (!RemoveDirectoryW(cstr)) { - ec = detail::make_system_error(); - } - } - else { - if (!DeleteFileW(cstr)) { - ec = detail::make_system_error(); - } - } - } -#else - if (::remove(p.c_str()) == -1) { - auto error = errno; - if (error == ENOENT) { - return false; - } - ec = detail::make_system_error(); - } -#endif - return ec ? false : true; -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE uintmax_t remove_all(const path& p) -{ - std::error_code ec; - auto result = remove_all(p, ec); - if (ec) { - throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); - } - return result; -} -#endif - -GHC_INLINE uintmax_t remove_all(const path& p, std::error_code& ec) noexcept -{ - ec.clear(); - uintmax_t count = 0; - if (p == "/") { - ec = detail::make_error_code(detail::portable_error::not_supported); - return static_cast(-1); - } - std::error_code tec; - auto fs = symlink_status(p, tec); - if (exists(fs) && is_directory(fs)) { - for (auto iter = directory_iterator(p, ec); iter != directory_iterator(); iter.increment(ec)) { - if (ec && !detail::is_not_found_error(ec)) { - break; - } - bool is_symlink_result = iter->is_symlink(ec); - if (ec) - return static_cast(-1); - if (!is_symlink_result && iter->is_directory(ec)) { - count += remove_all(iter->path(), ec); - if (ec) { - return static_cast(-1); - } - } - else { - if (!ec) { - remove(iter->path(), ec); - } - if (ec) { - return static_cast(-1); - } - ++count; - } - } - } - if (!ec) { - if (remove(p, ec)) { - ++count; - } - } - if (ec) { - return static_cast(-1); - } - return count; -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE void rename(const path& from, const path& to) -{ - std::error_code ec; - rename(from, to, ec); - if (ec) { - throw filesystem_error(detail::systemErrorText(ec.value()), from, to, ec); - } -} -#endif - -GHC_INLINE void rename(const path& from, const path& to, std::error_code& ec) noexcept -{ - ec.clear(); -#ifdef GHC_OS_WINDOWS - if (from != to) { - if (!MoveFileExW(GHC_NATIVEWP(from), GHC_NATIVEWP(to), (DWORD)MOVEFILE_REPLACE_EXISTING)) { - ec = detail::make_system_error(); - } - } -#else - if (from != to) { - if (::rename(from.c_str(), to.c_str()) != 0) { - ec = detail::make_system_error(); - } - } -#endif -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE void resize_file(const path& p, uintmax_t size) -{ - std::error_code ec; - resize_file(p, size, ec); - if (ec) { - throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); - } -} -#endif - -GHC_INLINE void resize_file(const path& p, uintmax_t size, std::error_code& ec) noexcept -{ - ec.clear(); -#ifdef GHC_OS_WINDOWS - LARGE_INTEGER lisize; - lisize.QuadPart = static_cast(size); - if (lisize.QuadPart < 0) { -#ifdef ERROR_FILE_TOO_LARGE - ec = detail::make_system_error(ERROR_FILE_TOO_LARGE); -#else - ec = detail::make_system_error(223); -#endif - return; - } - detail::unique_handle file(CreateFileW(GHC_NATIVEWP(p), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL)); - if (!file) { - ec = detail::make_system_error(); - } - else if (SetFilePointerEx(file.get(), lisize, NULL, FILE_BEGIN) == 0 || SetEndOfFile(file.get()) == 0) { - ec = detail::make_system_error(); - } -#else - if (::truncate(p.c_str(), static_cast(size)) != 0) { - ec = detail::make_system_error(); - } -#endif -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE space_info space(const path& p) -{ - std::error_code ec; - auto result = space(p, ec); - if (ec) { - throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); - } - return result; -} -#endif - -GHC_INLINE space_info space(const path& p, std::error_code& ec) noexcept -{ - ec.clear(); -#ifdef GHC_OS_WINDOWS - ULARGE_INTEGER freeBytesAvailableToCaller = {{ 0, 0 }}; - ULARGE_INTEGER totalNumberOfBytes = {{ 0, 0 }}; - ULARGE_INTEGER totalNumberOfFreeBytes = {{ 0, 0 }}; - if (!GetDiskFreeSpaceExW(GHC_NATIVEWP(p), &freeBytesAvailableToCaller, &totalNumberOfBytes, &totalNumberOfFreeBytes)) { - ec = detail::make_system_error(); - return {static_cast(-1), static_cast(-1), static_cast(-1)}; - } - return {static_cast(totalNumberOfBytes.QuadPart), static_cast(totalNumberOfFreeBytes.QuadPart), static_cast(freeBytesAvailableToCaller.QuadPart)}; -#else - struct ::statvfs sfs; - if (::statvfs(p.c_str(), &sfs) != 0) { - ec = detail::make_system_error(); - return {static_cast(-1), static_cast(-1), static_cast(-1)}; - } - return {static_cast(sfs.f_blocks) * static_cast(sfs.f_frsize), static_cast(sfs.f_bfree) * static_cast(sfs.f_frsize), static_cast(sfs.f_bavail) * static_cast(sfs.f_frsize)}; -#endif -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE file_status status(const path& p) -{ - std::error_code ec; - auto result = status(p, ec); - if (result.type() == file_type::none) { - throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); - } - return result; -} -#endif - -GHC_INLINE file_status status(const path& p, std::error_code& ec) noexcept -{ - return detail::status_ex(p, ec); -} - -GHC_INLINE bool status_known(file_status s) noexcept -{ - return s.type() != file_type::none; -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE file_status symlink_status(const path& p) -{ - std::error_code ec; - auto result = symlink_status(p, ec); - if (result.type() == file_type::none) { - throw filesystem_error(detail::systemErrorText(ec.value()), ec); - } - return result; -} -#endif - -GHC_INLINE file_status symlink_status(const path& p, std::error_code& ec) noexcept -{ - return detail::symlink_status_ex(p, ec); -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE path temp_directory_path() -{ - std::error_code ec; - path result = temp_directory_path(ec); - if (ec) { - throw filesystem_error(detail::systemErrorText(ec.value()), ec); - } - return result; -} -#endif - -GHC_INLINE path temp_directory_path(std::error_code& ec) noexcept -{ - ec.clear(); -#ifdef GHC_OS_WINDOWS - wchar_t buffer[512]; - auto rc = GetTempPathW(511, buffer); - if (!rc || rc > 511) { - ec = detail::make_system_error(); - return path(); - } - return path(std::wstring(buffer)); -#else - static const char* temp_vars[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr}; - const char* temp_path = nullptr; - for (auto temp_name = temp_vars; *temp_name != nullptr; ++temp_name) { - temp_path = std::getenv(*temp_name); - if (temp_path) { - return path(temp_path); - } - } - return path("/tmp"); -#endif -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE path weakly_canonical(const path& p) -{ - std::error_code ec; - auto result = weakly_canonical(p, ec); - if (ec) { - throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); - } - return result; -} -#endif - -GHC_INLINE path weakly_canonical(const path& p, std::error_code& ec) noexcept -{ - path result; - ec.clear(); - bool scan = true; - for (auto pe : p) { - if (scan) { - std::error_code tec; - if (exists(result / pe, tec)) { - result /= pe; - } - else { - if (ec) { - return path(); - } - scan = false; - if (!result.empty()) { - result = canonical(result, ec) / pe; - if (ec) { - break; - } - } - else { - result /= pe; - } - } - } - else { - result /= pe; - } - } - if (scan) { - if (!result.empty()) { - result = canonical(result, ec); - } - } - return ec ? path() : result.lexically_normal(); -} - -//----------------------------------------------------------------------------- -// [fs.class.file_status] class file_status -// [fs.file_status.cons] constructors and destructor -GHC_INLINE file_status::file_status() noexcept - : file_status(file_type::none) -{ -} - -GHC_INLINE file_status::file_status(file_type ft, perms prms) noexcept - : _type(ft) - , _perms(prms) -{ -} - -GHC_INLINE file_status::file_status(const file_status& other) noexcept - : _type(other._type) - , _perms(other._perms) -{ -} - -GHC_INLINE file_status::file_status(file_status&& other) noexcept - : _type(other._type) - , _perms(other._perms) -{ -} - -GHC_INLINE file_status::~file_status() {} - -// assignments: -GHC_INLINE file_status& file_status::operator=(const file_status& rhs) noexcept -{ - _type = rhs._type; - _perms = rhs._perms; - return *this; -} - -GHC_INLINE file_status& file_status::operator=(file_status&& rhs) noexcept -{ - _type = rhs._type; - _perms = rhs._perms; - return *this; -} - -// [fs.file_status.mods] modifiers -GHC_INLINE void file_status::type(file_type ft) noexcept -{ - _type = ft; -} - -GHC_INLINE void file_status::permissions(perms prms) noexcept -{ - _perms = prms; -} - -// [fs.file_status.obs] observers -GHC_INLINE file_type file_status::type() const noexcept -{ - return _type; -} - -GHC_INLINE perms file_status::permissions() const noexcept -{ - return _perms; -} - -//----------------------------------------------------------------------------- -// [fs.class.directory_entry] class directory_entry -// [fs.dir.entry.cons] constructors and destructor -// directory_entry::directory_entry() noexcept = default; -// directory_entry::directory_entry(const directory_entry&) = default; -// directory_entry::directory_entry(directory_entry&&) noexcept = default; -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE directory_entry::directory_entry(const filesystem::path& p) - : _path(p) - , _file_size(static_cast(-1)) -#ifndef GHC_OS_WINDOWS - , _hard_link_count(static_cast(-1)) -#endif - , _last_write_time(0) -{ - refresh(); -} -#endif - -GHC_INLINE directory_entry::directory_entry(const filesystem::path& p, std::error_code& ec) - : _path(p) - , _file_size(static_cast(-1)) -#ifndef GHC_OS_WINDOWS - , _hard_link_count(static_cast(-1)) -#endif - , _last_write_time(0) -{ - refresh(ec); -} - -GHC_INLINE directory_entry::~directory_entry() {} - -// assignments: -// directory_entry& directory_entry::operator=(const directory_entry&) = default; -// directory_entry& directory_entry::operator=(directory_entry&&) noexcept = default; - -// [fs.dir.entry.mods] directory_entry modifiers -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE void directory_entry::assign(const filesystem::path& p) -{ - _path = p; - refresh(); -} -#endif - -GHC_INLINE void directory_entry::assign(const filesystem::path& p, std::error_code& ec) -{ - _path = p; - refresh(ec); -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE void directory_entry::replace_filename(const filesystem::path& p) -{ - _path.replace_filename(p); - refresh(); -} -#endif - -GHC_INLINE void directory_entry::replace_filename(const filesystem::path& p, std::error_code& ec) -{ - _path.replace_filename(p); - refresh(ec); -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE void directory_entry::refresh() -{ - std::error_code ec; - refresh(ec); - if (ec && (_status.type() == file_type::none || _symlink_status.type() != file_type::symlink)) { - throw filesystem_error(detail::systemErrorText(ec.value()), _path, ec); - } -} -#endif - -GHC_INLINE void directory_entry::refresh(std::error_code& ec) noexcept -{ -#ifdef GHC_OS_WINDOWS - _status = detail::status_ex(_path, ec, &_symlink_status, &_file_size, nullptr, &_last_write_time); -#else - _status = detail::status_ex(_path, ec, &_symlink_status, &_file_size, &_hard_link_count, &_last_write_time); -#endif -} - -// [fs.dir.entry.obs] directory_entry observers -GHC_INLINE const filesystem::path& directory_entry::path() const noexcept -{ - return _path; -} - -GHC_INLINE directory_entry::operator const filesystem::path&() const noexcept -{ - return _path; -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE file_type directory_entry::status_file_type() const -{ - return _status.type() != file_type::none ? _status.type() : filesystem::status(path()).type(); -} -#endif - -GHC_INLINE file_type directory_entry::status_file_type(std::error_code& ec) const noexcept -{ - if (_status.type() != file_type::none) { - ec.clear(); - return _status.type(); - } - return filesystem::status(path(), ec).type(); -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE bool directory_entry::exists() const -{ - return status_file_type() != file_type::not_found; -} -#endif - -GHC_INLINE bool directory_entry::exists(std::error_code& ec) const noexcept -{ - return status_file_type(ec) != file_type::not_found; -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE bool directory_entry::is_block_file() const -{ - return status_file_type() == file_type::block; -} -#endif -GHC_INLINE bool directory_entry::is_block_file(std::error_code& ec) const noexcept -{ - return status_file_type(ec) == file_type::block; -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE bool directory_entry::is_character_file() const -{ - return status_file_type() == file_type::character; -} -#endif - -GHC_INLINE bool directory_entry::is_character_file(std::error_code& ec) const noexcept -{ - return status_file_type(ec) == file_type::character; -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE bool directory_entry::is_directory() const -{ - return status_file_type() == file_type::directory; -} -#endif - -GHC_INLINE bool directory_entry::is_directory(std::error_code& ec) const noexcept -{ - return status_file_type(ec) == file_type::directory; -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE bool directory_entry::is_fifo() const -{ - return status_file_type() == file_type::fifo; -} -#endif - -GHC_INLINE bool directory_entry::is_fifo(std::error_code& ec) const noexcept -{ - return status_file_type(ec) == file_type::fifo; -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE bool directory_entry::is_other() const -{ - auto ft = status_file_type(); - return ft != file_type::none && ft != file_type::not_found && ft != file_type::regular && ft != file_type::directory && !is_symlink(); -} -#endif - -GHC_INLINE bool directory_entry::is_other(std::error_code& ec) const noexcept -{ - auto ft = status_file_type(ec); - bool other = ft != file_type::none && ft != file_type::not_found && ft != file_type::regular && ft != file_type::directory && !is_symlink(ec); - return !ec && other; -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE bool directory_entry::is_regular_file() const -{ - return status_file_type() == file_type::regular; -} -#endif - -GHC_INLINE bool directory_entry::is_regular_file(std::error_code& ec) const noexcept -{ - return status_file_type(ec) == file_type::regular; -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE bool directory_entry::is_socket() const -{ - return status_file_type() == file_type::socket; -} -#endif - -GHC_INLINE bool directory_entry::is_socket(std::error_code& ec) const noexcept -{ - return status_file_type(ec) == file_type::socket; -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE bool directory_entry::is_symlink() const -{ - return _symlink_status.type() != file_type::none ? _symlink_status.type() == file_type::symlink : filesystem::is_symlink(symlink_status()); -} -#endif - -GHC_INLINE bool directory_entry::is_symlink(std::error_code& ec) const noexcept -{ - if (_symlink_status.type() != file_type::none) { - ec.clear(); - return _symlink_status.type() == file_type::symlink; - } - return filesystem::is_symlink(symlink_status(ec)); -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE uintmax_t directory_entry::file_size() const -{ - if (_file_size != static_cast(-1)) { - return _file_size; - } - return filesystem::file_size(path()); -} -#endif - -GHC_INLINE uintmax_t directory_entry::file_size(std::error_code& ec) const noexcept -{ - if (_file_size != static_cast(-1)) { - ec.clear(); - return _file_size; - } - return filesystem::file_size(path(), ec); -} - -#ifndef GHC_OS_WEB -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE uintmax_t directory_entry::hard_link_count() const -{ -#ifndef GHC_OS_WINDOWS - if (_hard_link_count != static_cast(-1)) { - return _hard_link_count; - } -#endif - return filesystem::hard_link_count(path()); -} -#endif - -GHC_INLINE uintmax_t directory_entry::hard_link_count(std::error_code& ec) const noexcept -{ -#ifndef GHC_OS_WINDOWS - if (_hard_link_count != static_cast(-1)) { - ec.clear(); - return _hard_link_count; - } -#endif - return filesystem::hard_link_count(path(), ec); -} -#endif - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE file_time_type directory_entry::last_write_time() const -{ - if (_last_write_time != 0) { - return std::chrono::system_clock::from_time_t(_last_write_time); - } - return filesystem::last_write_time(path()); -} -#endif - -GHC_INLINE file_time_type directory_entry::last_write_time(std::error_code& ec) const noexcept -{ - if (_last_write_time != 0) { - ec.clear(); - return std::chrono::system_clock::from_time_t(_last_write_time); - } - return filesystem::last_write_time(path(), ec); -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE file_status directory_entry::status() const -{ - if (_status.type() != file_type::none && _status.permissions() != perms::unknown) { - return _status; - } - return filesystem::status(path()); -} -#endif - -GHC_INLINE file_status directory_entry::status(std::error_code& ec) const noexcept -{ - if (_status.type() != file_type::none && _status.permissions() != perms::unknown) { - ec.clear(); - return _status; - } - return filesystem::status(path(), ec); -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE file_status directory_entry::symlink_status() const -{ - if (_symlink_status.type() != file_type::none && _symlink_status.permissions() != perms::unknown) { - return _symlink_status; - } - return filesystem::symlink_status(path()); -} -#endif - -GHC_INLINE file_status directory_entry::symlink_status(std::error_code& ec) const noexcept -{ - if (_symlink_status.type() != file_type::none && _symlink_status.permissions() != perms::unknown) { - ec.clear(); - return _symlink_status; - } - return filesystem::symlink_status(path(), ec); -} - -#ifdef GHC_HAS_THREEWAY_COMP -GHC_INLINE std::strong_ordering directory_entry::operator<=>(const directory_entry& rhs) const noexcept -{ - return _path <=> rhs._path; -} -#endif - -GHC_INLINE bool directory_entry::operator<(const directory_entry& rhs) const noexcept -{ - return _path < rhs._path; -} - -GHC_INLINE bool directory_entry::operator==(const directory_entry& rhs) const noexcept -{ - return _path == rhs._path; -} - -GHC_INLINE bool directory_entry::operator!=(const directory_entry& rhs) const noexcept -{ - return _path != rhs._path; -} - -GHC_INLINE bool directory_entry::operator<=(const directory_entry& rhs) const noexcept -{ - return _path <= rhs._path; -} - -GHC_INLINE bool directory_entry::operator>(const directory_entry& rhs) const noexcept -{ - return _path > rhs._path; -} - -GHC_INLINE bool directory_entry::operator>=(const directory_entry& rhs) const noexcept -{ - return _path >= rhs._path; -} - -//----------------------------------------------------------------------------- -// [fs.class.directory_iterator] class directory_iterator - -#ifdef GHC_OS_WINDOWS -class directory_iterator::impl -{ -public: - impl(const path& p, directory_options options) - : _base(p) - , _options(options) - , _dirHandle(INVALID_HANDLE_VALUE) - { - if (!_base.empty()) { - ZeroMemory(&_findData, sizeof(WIN32_FIND_DATAW)); - if ((_dirHandle = FindFirstFileW(GHC_NATIVEWP((_base / "*")), &_findData)) != INVALID_HANDLE_VALUE) { - if (std::wstring(_findData.cFileName) == L"." || std::wstring(_findData.cFileName) == L"..") { - increment(_ec); - } - else { - _dir_entry._path = _base / std::wstring(_findData.cFileName); - copyToDirEntry(_ec); - } - } - else { - auto error = ::GetLastError(); - _base = filesystem::path(); - if (error != ERROR_ACCESS_DENIED || (options & directory_options::skip_permission_denied) == directory_options::none) { - _ec = detail::make_system_error(); - } - } - } - } - impl(const impl& other) = delete; - ~impl() - { - if (_dirHandle != INVALID_HANDLE_VALUE) { - FindClose(_dirHandle); - _dirHandle = INVALID_HANDLE_VALUE; - } - } - void increment(std::error_code& ec) - { - if (_dirHandle != INVALID_HANDLE_VALUE) { - do { - if (FindNextFileW(_dirHandle, &_findData)) { - _dir_entry._path = _base; -#ifdef GHC_USE_WCHAR_T - _dir_entry._path.append_name(_findData.cFileName); -#else -#ifdef GHC_RAISE_UNICODE_ERRORS - try { - _dir_entry._path.append_name(detail::toUtf8(_findData.cFileName).c_str()); - } - catch (filesystem_error& fe) { - ec = fe.code(); - return; - } -#else - _dir_entry._path.append_name(detail::toUtf8(_findData.cFileName).c_str()); -#endif -#endif - copyToDirEntry(ec); - } - else { - auto err = ::GetLastError(); - if (err != ERROR_NO_MORE_FILES) { - _ec = ec = detail::make_system_error(err); - } - FindClose(_dirHandle); - _dirHandle = INVALID_HANDLE_VALUE; - _dir_entry._path.clear(); - break; - } - } while (std::wstring(_findData.cFileName) == L"." || std::wstring(_findData.cFileName) == L".."); - } - else { - ec = _ec; - } - } - void copyToDirEntry(std::error_code& ec) - { - if (_findData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { - _dir_entry._status = detail::status_ex(_dir_entry._path, ec, &_dir_entry._symlink_status, &_dir_entry._file_size, nullptr, &_dir_entry._last_write_time); - } - else { - _dir_entry._status = detail::status_from_INFO(_dir_entry._path, &_findData, ec, &_dir_entry._file_size, &_dir_entry._last_write_time); - _dir_entry._symlink_status = _dir_entry._status; - } - if (ec) { - if (_dir_entry._status.type() != file_type::none && _dir_entry._symlink_status.type() != file_type::none) { - ec.clear(); - } - else { - _dir_entry._file_size = static_cast(-1); - _dir_entry._last_write_time = 0; - } - } - } - path _base; - directory_options _options; - WIN32_FIND_DATAW _findData; - HANDLE _dirHandle; - directory_entry _dir_entry; - std::error_code _ec; -}; -#else -// POSIX implementation -class directory_iterator::impl -{ -public: - impl(const path& path, directory_options options) - : _base(path) - , _options(options) - , _dir(nullptr) - , _entry(nullptr) - { - if (!path.empty()) { - do { _dir = ::opendir(path.native().c_str()); } while(errno == EINTR); - if (!_dir) { - auto error = errno; - _base = filesystem::path(); - if ((error != EACCES && error != EPERM) || (options & directory_options::skip_permission_denied) == directory_options::none) { - _ec = detail::make_system_error(); - } - } - else { - increment(_ec); - } - } - } - impl(const impl& other) = delete; - ~impl() - { - if (_dir) { - ::closedir(_dir); - } - } - void increment(std::error_code& ec) - { - if (_dir) { - bool skip; - do { - skip = false; - errno = 0; - do { _entry = ::readdir(_dir); } while(errno == EINTR); - if (_entry) { - _dir_entry._path = _base; - _dir_entry._path.append_name(_entry->d_name); - copyToDirEntry(); - if (ec && (ec.value() == EACCES || ec.value() == EPERM) && (_options & directory_options::skip_permission_denied) == directory_options::skip_permission_denied) { - ec.clear(); - skip = true; - } - } - else { - ::closedir(_dir); - _dir = nullptr; - _dir_entry._path.clear(); - if (errno && errno != EINTR) { - ec = detail::make_system_error(); - } - break; - } - } while (skip || std::strcmp(_entry->d_name, ".") == 0 || std::strcmp(_entry->d_name, "..") == 0); - } - } - - void copyToDirEntry() - { - _dir_entry._symlink_status.permissions(perms::unknown); - auto ft = detail::file_type_from_dirent(*_entry); - _dir_entry._symlink_status.type(ft); - if (ft != file_type::symlink) { - _dir_entry._status = _dir_entry._symlink_status; - } - else { - _dir_entry._status.type(file_type::none); - _dir_entry._status.permissions(perms::unknown); - } - _dir_entry._file_size = static_cast(-1); - _dir_entry._hard_link_count = static_cast(-1); - _dir_entry._last_write_time = 0; - } - path _base; - directory_options _options; - DIR* _dir; - struct ::dirent* _entry; - directory_entry _dir_entry; - std::error_code _ec; -}; -#endif - -// [fs.dir.itr.members] member functions -GHC_INLINE directory_iterator::directory_iterator() noexcept - : _impl(new impl(path(), directory_options::none)) -{ -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE directory_iterator::directory_iterator(const path& p) - : _impl(new impl(p, directory_options::none)) -{ - if (_impl->_ec) { - throw filesystem_error(detail::systemErrorText(_impl->_ec.value()), p, _impl->_ec); - } - _impl->_ec.clear(); -} - -GHC_INLINE directory_iterator::directory_iterator(const path& p, directory_options options) - : _impl(new impl(p, options)) -{ - if (_impl->_ec) { - throw filesystem_error(detail::systemErrorText(_impl->_ec.value()), p, _impl->_ec); - } -} -#endif - -GHC_INLINE directory_iterator::directory_iterator(const path& p, std::error_code& ec) noexcept - : _impl(new impl(p, directory_options::none)) -{ - if (_impl->_ec) { - ec = _impl->_ec; - } -} - -GHC_INLINE directory_iterator::directory_iterator(const path& p, directory_options options, std::error_code& ec) noexcept - : _impl(new impl(p, options)) -{ - if (_impl->_ec) { - ec = _impl->_ec; - } -} - -GHC_INLINE directory_iterator::directory_iterator(const directory_iterator& rhs) - : _impl(rhs._impl) -{ -} - -GHC_INLINE directory_iterator::directory_iterator(directory_iterator&& rhs) noexcept - : _impl(std::move(rhs._impl)) -{ -} - -GHC_INLINE directory_iterator::~directory_iterator() {} - -GHC_INLINE directory_iterator& directory_iterator::operator=(const directory_iterator& rhs) -{ - _impl = rhs._impl; - return *this; -} - -GHC_INLINE directory_iterator& directory_iterator::operator=(directory_iterator&& rhs) noexcept -{ - _impl = std::move(rhs._impl); - return *this; -} - -GHC_INLINE const directory_entry& directory_iterator::operator*() const -{ - return _impl->_dir_entry; -} - -GHC_INLINE const directory_entry* directory_iterator::operator->() const -{ - return &_impl->_dir_entry; -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE directory_iterator& directory_iterator::operator++() -{ - std::error_code ec; - _impl->increment(ec); - if (ec) { - throw filesystem_error(detail::systemErrorText(ec.value()), _impl->_dir_entry._path, ec); - } - return *this; -} -#endif - -GHC_INLINE directory_iterator& directory_iterator::increment(std::error_code& ec) noexcept -{ - _impl->increment(ec); - return *this; -} - -GHC_INLINE bool directory_iterator::operator==(const directory_iterator& rhs) const -{ - return _impl->_dir_entry._path == rhs._impl->_dir_entry._path; -} - -GHC_INLINE bool directory_iterator::operator!=(const directory_iterator& rhs) const -{ - return _impl->_dir_entry._path != rhs._impl->_dir_entry._path; -} - -// [fs.dir.itr.nonmembers] directory_iterator non-member functions - -GHC_INLINE directory_iterator begin(directory_iterator iter) noexcept -{ - return iter; -} - -GHC_INLINE directory_iterator end(const directory_iterator&) noexcept -{ - return directory_iterator(); -} - -//----------------------------------------------------------------------------- -// [fs.class.rec.dir.itr] class recursive_directory_iterator - -GHC_INLINE recursive_directory_iterator::recursive_directory_iterator() noexcept - : _impl(new recursive_directory_iterator_impl(directory_options::none, true)) -{ - _impl->_dir_iter_stack.push(directory_iterator()); -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const path& p) - : _impl(new recursive_directory_iterator_impl(directory_options::none, true)) -{ - _impl->_dir_iter_stack.push(directory_iterator(p)); -} - -GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const path& p, directory_options options) - : _impl(new recursive_directory_iterator_impl(options, true)) -{ - _impl->_dir_iter_stack.push(directory_iterator(p, options)); -} -#endif - -GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const path& p, directory_options options, std::error_code& ec) noexcept - : _impl(new recursive_directory_iterator_impl(options, true)) -{ - _impl->_dir_iter_stack.push(directory_iterator(p, options, ec)); -} - -GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const path& p, std::error_code& ec) noexcept - : _impl(new recursive_directory_iterator_impl(directory_options::none, true)) -{ - _impl->_dir_iter_stack.push(directory_iterator(p, ec)); -} - -GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const recursive_directory_iterator& rhs) - : _impl(rhs._impl) -{ -} - -GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(recursive_directory_iterator&& rhs) noexcept - : _impl(std::move(rhs._impl)) -{ -} - -GHC_INLINE recursive_directory_iterator::~recursive_directory_iterator() {} - -// [fs.rec.dir.itr.members] observers -GHC_INLINE directory_options recursive_directory_iterator::options() const -{ - return _impl->_options; -} - -GHC_INLINE int recursive_directory_iterator::depth() const -{ - return static_cast(_impl->_dir_iter_stack.size() - 1); -} - -GHC_INLINE bool recursive_directory_iterator::recursion_pending() const -{ - return _impl->_recursion_pending; -} - -GHC_INLINE const directory_entry& recursive_directory_iterator::operator*() const -{ - return *(_impl->_dir_iter_stack.top()); -} - -GHC_INLINE const directory_entry* recursive_directory_iterator::operator->() const -{ - return &(*(_impl->_dir_iter_stack.top())); -} - -// [fs.rec.dir.itr.members] modifiers recursive_directory_iterator& -GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::operator=(const recursive_directory_iterator& rhs) -{ - _impl = rhs._impl; - return *this; -} - -GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::operator=(recursive_directory_iterator&& rhs) noexcept -{ - _impl = std::move(rhs._impl); - return *this; -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::operator++() -{ - std::error_code ec; - increment(ec); - if (ec) { - throw filesystem_error(detail::systemErrorText(ec.value()), _impl->_dir_iter_stack.empty() ? path() : _impl->_dir_iter_stack.top()->path(), ec); - } - return *this; -} -#endif - -GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::increment(std::error_code& ec) noexcept -{ - bool isSymLink = (*this)->is_symlink(ec); - bool isDir = !ec && (*this)->is_directory(ec); - if (isSymLink && detail::is_not_found_error(ec)) { - ec.clear(); - } - if (!ec) { - if (recursion_pending() && isDir && (!isSymLink || (options() & directory_options::follow_directory_symlink) != directory_options::none)) { - _impl->_dir_iter_stack.push(directory_iterator((*this)->path(), _impl->_options, ec)); - } - else { - _impl->_dir_iter_stack.top().increment(ec); - } - if (!ec) { - while (depth() && _impl->_dir_iter_stack.top() == directory_iterator()) { - _impl->_dir_iter_stack.pop(); - _impl->_dir_iter_stack.top().increment(ec); - } - } - else if (!_impl->_dir_iter_stack.empty()) { - _impl->_dir_iter_stack.pop(); - } - _impl->_recursion_pending = true; - } - return *this; -} - -#ifdef GHC_WITH_EXCEPTIONS -GHC_INLINE void recursive_directory_iterator::pop() -{ - std::error_code ec; - pop(ec); - if (ec) { - throw filesystem_error(detail::systemErrorText(ec.value()), _impl->_dir_iter_stack.empty() ? path() : _impl->_dir_iter_stack.top()->path(), ec); - } -} -#endif - -GHC_INLINE void recursive_directory_iterator::pop(std::error_code& ec) -{ - if (depth() == 0) { - *this = recursive_directory_iterator(); - } - else { - do { - _impl->_dir_iter_stack.pop(); - _impl->_dir_iter_stack.top().increment(ec); - } while (depth() && _impl->_dir_iter_stack.top() == directory_iterator()); - } -} - -GHC_INLINE void recursive_directory_iterator::disable_recursion_pending() -{ - _impl->_recursion_pending = false; -} - -// other members as required by [input.iterators] -GHC_INLINE bool recursive_directory_iterator::operator==(const recursive_directory_iterator& rhs) const -{ - return _impl->_dir_iter_stack.top() == rhs._impl->_dir_iter_stack.top(); -} - -GHC_INLINE bool recursive_directory_iterator::operator!=(const recursive_directory_iterator& rhs) const -{ - return _impl->_dir_iter_stack.top() != rhs._impl->_dir_iter_stack.top(); -} - -// [fs.rec.dir.itr.nonmembers] directory_iterator non-member functions -GHC_INLINE recursive_directory_iterator begin(recursive_directory_iterator iter) noexcept -{ - return iter; -} - -GHC_INLINE recursive_directory_iterator end(const recursive_directory_iterator&) noexcept -{ - return recursive_directory_iterator(); -} - -#endif // GHC_EXPAND_IMPL - -} // namespace filesystem -} // namespace ghc - -// cleanup some macros -#undef GHC_INLINE -#undef GHC_EXPAND_IMPL - -#endif // GHC_FILESYSTEM_H diff --git a/3rdparty/openal/CMakeLists.txt b/3rdparty/openal/CMakeLists.txt index e0b6298fef07..c5969197a291 100644 --- a/3rdparty/openal/CMakeLists.txt +++ b/3rdparty/openal/CMakeLists.txt @@ -75,12 +75,12 @@ if(NOT CMAKE_DEBUG_POSTFIX) FORCE) endif() -set(DEFAULT_TARGET_PROPS +set(ALSOFT_STD_VERSION_PROPS # Require C++17. CXX_STANDARD 17 CXX_STANDARD_REQUIRED TRUE - # Prefer C11, but support C99 and earlier when possible. - C_STANDARD 11) + # Prefer C17, but support earlier when necessary. + C_STANDARD 17) set(CMAKE_MODULE_PATH "${OpenAL_SOURCE_DIR}/cmake") @@ -98,8 +98,11 @@ include(CMakePackageConfigHelpers) include(GNUInstallDirs) find_package(PkgConfig) -find_package(SDL2 QUIET) +find_package(SDL3 QUIET) +# Axmol MODIFY BEGIN +# add_subdirectory(fmt-11.1.1 EXCLUDE_FROM_ALL) +# Axmol MODIFY END option(ALSOFT_DLOPEN "Check for the dlopen API for loading optional libs" ON) @@ -181,8 +184,8 @@ if(NOT LIBTYPE) endif() set(LIB_MAJOR_VERSION "1") -set(LIB_MINOR_VERSION "23") -set(LIB_REVISION "1") +set(LIB_MINOR_VERSION "24") +set(LIB_REVISION "2") set(LIB_VERSION "${LIB_MAJOR_VERSION}.${LIB_MINOR_VERSION}.${LIB_REVISION}") set(LIB_VERSION_NUM ${LIB_MAJOR_VERSION},${LIB_MINOR_VERSION},${LIB_REVISION},0) @@ -203,31 +206,6 @@ if(NOT HAVE_STDC_FORMAT_MACROS) set(CPP_DEFS ${CPP_DEFS} __STDC_FORMAT_MACROS) endif() -if(NOT WIN32) - # Check if _POSIX_C_SOURCE and _XOPEN_SOURCE needs to be set for POSIX functions - check_symbol_exists(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN_DEFAULT) - if(NOT HAVE_POSIX_MEMALIGN_DEFAULT) - set(OLD_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) - set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -D_POSIX_C_SOURCE=200112L -D_XOPEN_SOURCE=600") - check_symbol_exists(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN_POSIX) - if(NOT HAVE_POSIX_MEMALIGN_POSIX) - set(CMAKE_REQUIRED_FLAGS ${OLD_REQUIRED_FLAGS}) - else() - set(CPP_DEFS ${CPP_DEFS} _POSIX_C_SOURCE=200112L _XOPEN_SOURCE=600) - endif() - endif() - unset(OLD_REQUIRED_FLAGS) -endif() - -# C99 has restrict, but C++ does not, so we can only utilize __restrict. -check_cxx_source_compiles("int *__restrict foo; -int main() { return 0; }" HAVE___RESTRICT) -if(HAVE___RESTRICT) - set(CPP_DEFS ${CPP_DEFS} RESTRICT=__restrict) -else() - set(CPP_DEFS ${CPP_DEFS} "RESTRICT=") -endif() - # Some systems may need libatomic for atomic functions to work set(OLD_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) set(CMAKE_REQUIRED_LIBRARIES ${OLD_REQUIRED_LIBRARIES} atomic) @@ -252,12 +230,18 @@ if(ANDROID) endif() if(MSVC) - set(CPP_DEFS ${CPP_DEFS} _CRT_SECURE_NO_WARNINGS) + # NOTE: _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR is temporary. When building on + # VS 2022 17.10 or newer, but using an older runtime, mutexes can crash + # when locked. Ideally the runtime should be updated on the system, but + # until the update becomes more widespread, this helps avoid some pain + # points. + set(CPP_DEFS ${CPP_DEFS} _CRT_SECURE_NO_WARNINGS _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR) check_cxx_compiler_flag(/permissive- HAVE_PERMISSIVE_SWITCH) if(HAVE_PERMISSIVE_SWITCH) set(C_FLAGS ${C_FLAGS} $<$:/permissive->) endif() - set(C_FLAGS ${C_FLAGS} /W4 /w14640 /wd4065 /wd4127 /wd4268 /wd4324 /wd5030 /wd5051) + set(C_FLAGS ${C_FLAGS} /W4 /w14640 /wd4065 /wd4127 /wd4268 /wd4324 /wd5030 /wd5051 + $<$:/EHsc> /utf-8) if(NOT DXSDK_DIR) string(REGEX REPLACE "\\\\" "/" DXSDK_DIR "$ENV{DXSDK_DIR}") @@ -295,17 +279,25 @@ else() endif() endif() + check_cxx_compiler_flag(-Wno-interference-size HAVE_WNO_INTERFERENCE_SIZE) + if(HAVE_WNO_INTERFERENCE_SIZE) + set(C_FLAGS ${C_FLAGS} $<$:-Wno-interference-size>) + endif() + + # Axmol MODIFY BEGIN if(ALSOFT_WERROR) set(C_FLAGS ${C_FLAGS} -Werror) endif() - - # We want RelWithDebInfo to actually include debug stuff (define _DEBUG - # instead of NDEBUG) - foreach(flag_var CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS_RELWITHDEBINFO) - if(${flag_var} MATCHES "-DNDEBUG") - string(REGEX REPLACE "-DNDEBUG" "-D_DEBUG" ${flag_var} "${${flag_var}}") - endif() - endforeach() + # Axmol MODIFY END + + # NOTE: This essentially provides the equivalent of the C++26 feature to + # initialize all local variables with a non-0 bit pattern. Until C++26 is + # adopted, the [[gnu::uninitialized]] attribute will avoid the auto- + # initialization where necessary. + check_c_compiler_flag(-ftrivial-auto-var-init=pattern HAVE_FTRIVIAL_AUTO_VAR_INIT) + if(HAVE_FTRIVIAL_AUTO_VAR_INIT) + set(C_FLAGS ${C_FLAGS} -ftrivial-auto-var-init=pattern) + endif() check_c_compiler_flag(-fno-math-errno HAVE_FNO_MATH_ERRNO) if(HAVE_FNO_MATH_ERRNO) @@ -329,7 +321,7 @@ else() option(ALSOFT_STATIC_STDCXX "Static link libstdc++" OFF) if(ALSOFT_STATIC_STDCXX) set(OLD_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) - set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} "-Wl,--push-state,-Bstatic,-lstdc++,--pop-state") + set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} "-static-libstdc++") check_cxx_source_compiles("int main() { }" HAVE_STATIC_LIBSTDCXX_SWITCH) set(CMAKE_REQUIRED_LIBRARIES ${OLD_REQUIRED_LIBRARIES}) unset(OLD_REQUIRED_LIBRARIES) @@ -337,14 +329,14 @@ else() if(NOT HAVE_STATIC_LIBSTDCXX_SWITCH) message(FATAL_ERROR "Cannot static link libstdc++") endif() - set(LINKER_FLAGS ${LINKER_FLAGS} "-Wl,--push-state,-Bstatic,-lstdc++,--pop-state") + set(LINKER_FLAGS ${LINKER_FLAGS} "-static-libstdc++") endif() if(WIN32) option(ALSOFT_STATIC_WINPTHREAD "Static link libwinpthread" OFF) if(ALSOFT_STATIC_WINPTHREAD) set(OLD_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) - set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} "-Wl,--push-state,-Bstatic,-lwinpthread,--pop-state") + set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} "-Wl,--push-state,-Bstatic,-lstdc++,-lwinpthread,--pop-state") check_cxx_source_compiles("int main() { }" HAVE_STATIC_LIBWINPTHREAD_SWITCH) set(CMAKE_REQUIRED_LIBRARIES ${OLD_REQUIRED_LIBRARIES}) unset(OLD_REQUIRED_LIBRARIES) @@ -352,7 +344,7 @@ else() if(NOT HAVE_STATIC_LIBWINPTHREAD_SWITCH) message(FATAL_ERROR "Cannot static link libwinpthread") endif() - set(LINKER_FLAGS ${LINKER_FLAGS} "-Wl,--push-state,-Bstatic,-lwinpthread,--pop-state") + set(LINKER_FLAGS ${LINKER_FLAGS} "-Wl,--push-state,-Bstatic,-lstdc++,-lwinpthread,--pop-state") endif() endif() endif() @@ -420,7 +412,7 @@ if(ALSOFT_CPUEXT_SSE AND HAVE_XMMINTRIN_H) set(HAVE_SSE 1) endif() if(ALSOFT_REQUIRE_SSE AND NOT HAVE_SSE) - message(FATAL_ERROR "Failed to enabled required SSE CPU extensions") + message(FATAL_ERROR "Failed to enable required SSE CPU extensions") endif() option(ALSOFT_CPUEXT_SSE2 "Enable SSE2 support" ON) @@ -465,7 +457,7 @@ if(ALSOFT_CPUEXT_NEON AND HAVE_ARM_NEON_H) endif() endif() if(ALSOFT_REQUIRE_NEON AND NOT HAVE_NEON) - message(FATAL_ERROR "Failed to enabled required ARM NEON CPU extensions") + message(FATAL_ERROR "Failed to enable required ARM NEON CPU extensions") endif() @@ -512,13 +504,9 @@ if(HAVE_SSE2) endif() -check_include_file(malloc.h HAVE_MALLOC_H) check_include_file(cpuid.h HAVE_CPUID_H) check_include_file(intrin.h HAVE_INTRIN_H) check_include_file(guiddef.h HAVE_GUIDDEF_H) -if(NOT HAVE_GUIDDEF_H) - check_include_file(initguid.h HAVE_INITGUID_H) -endif() # Some systems need libm for some math functions to work set(MATH_LIB ) @@ -565,8 +553,6 @@ if(HAVE_INTRIN_H) }" HAVE_CPUID_INTRINSIC) endif() -check_symbol_exists(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN) -check_symbol_exists(_aligned_malloc malloc.h HAVE__ALIGNED_MALLOC) check_symbol_exists(proc_pidpath libproc.h HAVE_PROC_PIDPATH) if(NOT WIN32) @@ -601,19 +587,15 @@ if(NOT WIN32) endif() endif() -check_symbol_exists(getopt unistd.h HAVE_GETOPT) - # Common sources used by both the OpenAL implementation library, the OpenAL # router, and certain tools and examples. set(COMMON_OBJS + common/alassert.cpp + common/alassert.h common/albit.h common/alcomplex.cpp common/alcomplex.h - common/aldeque.h - common/alfstream.cpp - common/alfstream.h - common/almalloc.cpp common/almalloc.h common/alnumbers.h common/alnumeric.h @@ -624,13 +606,19 @@ set(COMMON_OBJS common/alstring.h common/althrd_setname.cpp common/althrd_setname.h + common/althreads.h common/altraits.h common/atomic.h common/comptr.h common/dynload.cpp common/dynload.h + common/filesystem.cpp + common/filesystem.h + common/flexarray.h common/intrusive_ptr.h common/opthelpers.h + common/pffft.cpp + common/pffft.h common/phase_shifter.h common/polyphase_resampler.cpp common/polyphase_resampler.h @@ -683,7 +671,6 @@ set(CORE_OBJS core/filters/nfc.h core/filters/splitter.cpp core/filters/splitter.h - core/fmt_traits.cpp core/fmt_traits.h core/fpu_ctrl.cpp core/fpu_ctrl.h @@ -699,6 +686,8 @@ set(CORE_OBJS core/mixer.cpp core/mixer.h core/resampler_limits.h + core/storage_formats.cpp + core/storage_formats.h core/uhjfilter.cpp core/uhjfilter.h core/uiddefs.cpp @@ -731,21 +720,21 @@ if(NOT WIN32) else() set(EXTRA_LIBS ${EXTRA_LIBS} ${DBus1_LIBRARIES}) endif() + else() + set(MISSING_VARS "") + if(NOT DBus1_INCLUDE_DIRS) + set(MISSING_VARS "${MISSING_VARS} DBus1_INCLUDE_DIRS") + endif() + if(NOT DBus1_LIBRARIES) + set(MISSING_VARS "${MISSING_VARS} DBus1_LIBRARIES") + endif() + message(STATUS "Could NOT find DBus1 (missing:${MISSING_VARS})") + unset(MISSING_VARS) endif() - else() - set(MISSING_VARS "") - if(NOT DBus1_INCLUDE_DIRS) - set(MISSING_VARS "${MISSING_VARS} DBus1_INCLUDE_DIRS") - endif() - if(NOT DBus1_LIBRARIES) - set(MISSING_VARS "${MISSING_VARS} DBus1_LIBRARIES") - endif() - message(STATUS "Could NOT find DBus1 (missing:${MISSING_VARS})") - unset(MISSING_VARS) endif() endif() if(ALSOFT_REQUIRE_RTKIT AND NOT HAVE_RTKIT) - message(FATAL_ERROR "Failed to enabled required RTKit support") + message(FATAL_ERROR "Failed to enable required RTKit support") endif() # Default mixers, always available @@ -884,8 +873,10 @@ set(HAVE_PULSEAUDIO 0) set(HAVE_COREAUDIO 0) set(HAVE_OPENSL 0) set(HAVE_OBOE 0) +set(HAVE_OTHERIO 0) set(HAVE_WAVE 0) set(HAVE_SDL2 0) +set(HAVE_SDL3 0) if(WIN32 OR HAVE_DLFCN_H) set(IS_LINKED "") @@ -923,7 +914,7 @@ if(ALSOFT_BACKEND_PIPEWIRE AND PkgConfig_FOUND) endif() endif() if(ALSOFT_REQUIRE_PIPEWIRE AND NOT HAVE_PIPEWIRE) - message(FATAL_ERROR "Failed to enabled required PipeWire backend") + message(FATAL_ERROR "Failed to enable required PipeWire backend") endif() # Check PulseAudio backend @@ -940,7 +931,7 @@ if(ALSOFT_BACKEND_PULSEAUDIO) endif() endif() if(ALSOFT_REQUIRE_PULSEAUDIO AND NOT HAVE_PULSEAUDIO) - message(FATAL_ERROR "Failed to enabled required PulseAudio backend") + message(FATAL_ERROR "Failed to enable required PulseAudio backend") endif() if(NOT WIN32) @@ -987,31 +978,35 @@ if(NOT WIN32) endif() endif() - # Check SndIO backend - option(ALSOFT_BACKEND_SNDIO "Enable SndIO backend" ON) + # Check SndIO backend (disabled by default on non-BSDs) + if(BSD) + option(ALSOFT_BACKEND_SNDIO "Enable SndIO backend" ON) + else() + option(ALSOFT_BACKEND_SNDIO "Enable SndIO backend" OFF) + endif() option(ALSOFT_REQUIRE_SNDIO "Require SndIO backend" OFF) if(ALSOFT_BACKEND_SNDIO) - find_package(SoundIO) - if(SOUNDIO_FOUND) + find_package(SndIO) + if(SNDIO_FOUND) set(HAVE_SNDIO 1) set(BACKENDS "${BACKENDS} SndIO (linked),") - set(ALC_OBJS ${ALC_OBJS} alc/backends/sndio.cpp alc/backends/sndio.h) - set(EXTRA_LIBS ${SOUNDIO_LIBRARIES} ${EXTRA_LIBS}) - set(INC_PATHS ${INC_PATHS} ${SOUNDIO_INCLUDE_DIRS}) + set(ALC_OBJS ${ALC_OBJS} alc/backends/sndio.cpp alc/backends/sndio.hpp) + set(EXTRA_LIBS ${SNDIO_LIBRARIES} ${EXTRA_LIBS}) + set(INC_PATHS ${INC_PATHS} ${SNDIO_INCLUDE_DIRS}) endif() endif() endif() if(ALSOFT_REQUIRE_ALSA AND NOT HAVE_ALSA) - message(FATAL_ERROR "Failed to enabled required ALSA backend") + message(FATAL_ERROR "Failed to enable required ALSA backend") endif() if(ALSOFT_REQUIRE_OSS AND NOT HAVE_OSS) - message(FATAL_ERROR "Failed to enabled required OSS backend") + message(FATAL_ERROR "Failed to enable required OSS backend") endif() if(ALSOFT_REQUIRE_SOLARIS AND NOT HAVE_SOLARIS) - message(FATAL_ERROR "Failed to enabled required Solaris backend") + message(FATAL_ERROR "Failed to enable required Solaris backend") endif() if(ALSOFT_REQUIRE_SNDIO AND NOT HAVE_SNDIO) - message(FATAL_ERROR "Failed to enabled required SndIO backend") + message(FATAL_ERROR "Failed to enable required SndIO backend") endif() # Check Windows-only backends @@ -1062,17 +1057,32 @@ if(WIN32) set(HAVE_WASAPI 1) set(BACKENDS "${BACKENDS} WASAPI,") set(ALC_OBJS ${ALC_OBJS} alc/backends/wasapi.cpp alc/backends/wasapi.h) + + if(NOT ALSOFT_UWP) + set(EXTRA_LIBS avrt ${EXTRA_LIBS}) + endif() endif() endif() + + option(ALSOFT_BACKEND_OTHERIO "Enable OtherIO backend" OFF) + option(ALSOFT_REQUIRE_OTHERIO "Require OtherIO backend" OFF) + if(ALSOFT_BACKEND_OTHERIO) + set(HAVE_OTHERIO 1) + set(BACKENDS "${BACKENDS} OtherIO,") + set(ALC_OBJS ${ALC_OBJS} alc/backends/otherio.cpp alc/backends/otherio.h) + endif() endif() if(ALSOFT_REQUIRE_WINMM AND NOT HAVE_WINMM) - message(FATAL_ERROR "Failed to enabled required WinMM backend") + message(FATAL_ERROR "Failed to enable required WinMM backend") endif() if(ALSOFT_REQUIRE_DSOUND AND NOT HAVE_DSOUND) - message(FATAL_ERROR "Failed to enabled required DSound backend") + message(FATAL_ERROR "Failed to enable required DSound backend") endif() if(ALSOFT_REQUIRE_WASAPI AND NOT HAVE_WASAPI) - message(FATAL_ERROR "Failed to enabled required WASAPI backend") + message(FATAL_ERROR "Failed to enable required WASAPI backend") +endif() +if(ALSOFT_REQUIRE_OTHERIO AND NOT HAVE_OTHERIO) + message(FATAL_ERROR "Failed to enable required OtherIO backend") endif() # Check JACK backend @@ -1089,7 +1099,7 @@ if(ALSOFT_BACKEND_JACK) endif() endif() if(ALSOFT_REQUIRE_JACK AND NOT HAVE_JACK) - message(FATAL_ERROR "Failed to enabled required JACK backend") + message(FATAL_ERROR "Failed to enable required JACK backend") endif() # Check CoreAudio backend @@ -1124,7 +1134,7 @@ if(ALSOFT_BACKEND_COREAUDIO) endif() endif() if(ALSOFT_REQUIRE_COREAUDIO AND NOT HAVE_COREAUDIO) - message(FATAL_ERROR "Failed to enabled required CoreAudio backend") + message(FATAL_ERROR "Failed to enable required CoreAudio backend") endif() # Check for Oboe (Android) backend @@ -1135,7 +1145,7 @@ if(ALSOFT_BACKEND_OBOE) if(ANDROID) set(OBOE_SOURCE "" CACHE STRING "Source directory for Oboe.") if(OBOE_SOURCE) - add_subdirectory(${OBOE_SOURCE} ./oboe) + add_subdirectory(${OBOE_SOURCE} ./oboe EXCLUDE_FROM_ALL) set(OBOE_TARGET oboe) else() find_package(oboe CONFIG) @@ -1156,7 +1166,7 @@ if(ALSOFT_BACKEND_OBOE) endif() endif() if(ALSOFT_REQUIRE_OBOE AND NOT HAVE_OBOE) - message(FATAL_ERROR "Failed to enabled required Oboe backend") + message(FATAL_ERROR "Failed to enable required Oboe backend") endif() # Check for OpenSL (Android) backend @@ -1167,13 +1177,13 @@ if(ALSOFT_BACKEND_OPENSL) if(OPENSL_FOUND) set(HAVE_OPENSL 1) set(ALC_OBJS ${ALC_OBJS} alc/backends/opensl.cpp alc/backends/opensl.h) - set(BACKENDS "${BACKENDS} OpenSL,") - set(EXTRA_LIBS ${OPENSL_LIBRARIES} ${EXTRA_LIBS}) + set(BACKENDS "${BACKENDS} OpenSL${IS_LINKED},") + add_backend_libs(${OPENSL_LIBRARIES}) set(INC_PATHS ${INC_PATHS} ${OPENSL_INCLUDE_DIRS}) endif() endif() if(ALSOFT_REQUIRE_OPENSL AND NOT HAVE_OPENSL) - message(FATAL_ERROR "Failed to enabled required OpenSL backend") + message(FATAL_ERROR "Failed to enable required OpenSL backend") endif() # Check PortAudio backend @@ -1184,20 +1194,38 @@ if(ALSOFT_BACKEND_PORTAUDIO) if(PORTAUDIO_FOUND) set(HAVE_PORTAUDIO 1) set(BACKENDS "${BACKENDS} PortAudio${IS_LINKED},") - set(ALC_OBJS ${ALC_OBJS} alc/backends/portaudio.cpp alc/backends/portaudio.h) + set(ALC_OBJS ${ALC_OBJS} alc/backends/portaudio.cpp alc/backends/portaudio.hpp) add_backend_libs(${PORTAUDIO_LIBRARIES}) set(INC_PATHS ${INC_PATHS} ${PORTAUDIO_INCLUDE_DIRS}) endif() endif() if(ALSOFT_REQUIRE_PORTAUDIO AND NOT HAVE_PORTAUDIO) - message(FATAL_ERROR "Failed to enabled required PortAudio backend") + message(FATAL_ERROR "Failed to enable required PortAudio backend") +endif() + +# Check for SDL2 or SDL3 backend +# Off by default, since it adds a runtime dependency. Additionally, both SDL2 +# and SDL3 can't be enabled simultaneously. +option(ALSOFT_BACKEND_SDL3 "Enable SDL3 backend" OFF) +option(ALSOFT_REQUIRE_SDL3 "Require SDL3 backend" OFF) +if(ALSOFT_BACKEND_SDL3) + if(SDL3_FOUND) + set(HAVE_SDL3 1) + set(ALC_OBJS ${ALC_OBJS} alc/backends/sdl3.cpp alc/backends/sdl3.h) + set(BACKENDS "${BACKENDS} SDL3,") + set(EXTRA_LIBS ${EXTRA_LIBS} SDL3::SDL3) + else() + message(STATUS "Could NOT find SDL3") + endif() +endif() +if(ALSOFT_REQUIRE_SDL3 AND NOT HAVE_SDL3) + message(FATAL_ERROR "Failed to enable required SDL3 backend") endif() -# Check for SDL2 backend -# Off by default, since it adds a runtime dependency option(ALSOFT_BACKEND_SDL2 "Enable SDL2 backend" OFF) option(ALSOFT_REQUIRE_SDL2 "Require SDL2 backend" OFF) -if(ALSOFT_BACKEND_SDL2) +if(ALSOFT_BACKEND_SDL2 AND NOT HAVE_SDL3) + find_package(SDL2) if(SDL2_FOUND) set(HAVE_SDL2 1) set(ALC_OBJS ${ALC_OBJS} alc/backends/sdl2.cpp alc/backends/sdl2.h) @@ -1207,8 +1235,8 @@ if(ALSOFT_BACKEND_SDL2) message(STATUS "Could NOT find SDL2") endif() endif() -if(ALSOFT_REQUIRE_SDL2 AND NOT SDL2_FOUND) - message(FATAL_ERROR "Failed to enabled required SDL2 backend") +if(ALSOFT_REQUIRE_SDL2 AND NOT HAVE_SDL2) + message(FATAL_ERROR "Failed to enable required SDL2 backend") endif() # Optionally enable the Wave Writer backend @@ -1249,7 +1277,7 @@ if(ALSOFT_UPDATE_BUILD_VERSION AND GIT_FOUND AND EXISTS "${OpenAL_SOURCE_DIR}/.g VERBATIM ) - add_custom_target(build_version DEPENDS "${OpenAL_BINARY_DIR}/version_witness.txt") + add_custom_target(alsoft.build_version DEPENDS "${OpenAL_BINARY_DIR}/version_witness.txt") else() set(GIT_BRANCH "UNKNOWN") set(GIT_COMMIT_HASH "unknown") @@ -1290,7 +1318,7 @@ if(ALSOFT_UTILS) endif() if(ALSOFT_UTILS OR ALSOFT_EXAMPLES) find_package(SndFile) - if(SDL2_FOUND) + if(SDL3_FOUND) find_package(FFmpeg COMPONENTS AVFORMAT AVCODEC AVUTIL SWSCALE SWRESAMPLE) endif() endif() @@ -1314,12 +1342,14 @@ if(LIBTYPE STREQUAL "STATIC") set(PKG_CONFIG_CFLAGS -DAL_LIBTYPE_STATIC) foreach(FLAG ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB}) # If this is already a linker flag, or is a full path+file, add it - # as-is. If it's an SDL2 target, add the link flag for it. Otherwise, - # it's a name intended to be dressed as -lname. + # as-is. If it's an SDL2 or SDL3 target, add the link flag for it. + # Otherwise, it's a name intended to be dressed as -lname. if(FLAG MATCHES "^-.*" OR EXISTS "${FLAG}") set(PKG_CONFIG_PRIVATE_LIBS "${PKG_CONFIG_PRIVATE_LIBS} ${FLAG}") elseif(FLAG MATCHES "^SDL2::SDL2") set(PKG_CONFIG_PRIVATE_LIBS "${PKG_CONFIG_PRIVATE_LIBS} -lSDL2") + elseif(FLAG MATCHES "^SDL3::SDL3") + set(PKG_CONFIG_PRIVATE_LIBS "${PKG_CONFIG_PRIVATE_LIBS} -lSDL3") else() set(PKG_CONFIG_PRIVATE_LIBS "${PKG_CONFIG_PRIVATE_LIBS} -l${FLAG}") endif() @@ -1330,27 +1360,53 @@ endif() configure_file( "${OpenAL_SOURCE_DIR}/config.h.in" "${OpenAL_BINARY_DIR}/config.h") +configure_file( + "${OpenAL_SOURCE_DIR}/config_backends.h.in" + "${OpenAL_BINARY_DIR}/config_backends.h") +configure_file( + "${OpenAL_SOURCE_DIR}/config_simd.h.in" + "${OpenAL_BINARY_DIR}/config_simd.h") configure_file( "${OpenAL_SOURCE_DIR}/openal.pc.in" "${OpenAL_BINARY_DIR}/openal.pc" @ONLY) -add_library(alcommon STATIC EXCLUDE_FROM_ALL ${COMMON_OBJS}) -target_include_directories(alcommon PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/include) -target_compile_definitions(alcommon PRIVATE ${CPP_DEFS}) -target_compile_options(alcommon PRIVATE ${C_FLAGS}) -set_target_properties(alcommon PROPERTIES ${DEFAULT_TARGET_PROPS} POSITION_INDEPENDENT_CODE TRUE) +add_library(alsoft.common STATIC EXCLUDE_FROM_ALL ${COMMON_OBJS}) +target_include_directories(alsoft.common PRIVATE ${OpenAL_SOURCE_DIR}/include + PUBLIC ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/common) +target_compile_definitions(alsoft.common PRIVATE ${CPP_DEFS}) +target_compile_options(alsoft.common PRIVATE ${C_FLAGS}) +target_link_libraries(alsoft.common PRIVATE fmt::fmt) +set_target_properties(alsoft.common PROPERTIES ${ALSOFT_STD_VERSION_PROPS} + POSITION_INDEPENDENT_CODE TRUE) unset(HAS_ROUTER) set(IMPL_TARGET OpenAL) # Either OpenAL or soft_oal. + +set(NEED_ANALYZE_SOURCE_FILES "") +foreach(obj ${CORE_OBJS} ${OPENAL_OBJS} ${ALC_OBJS} ${COMMON_OBJS}) + IF (NOT ${obj} MATCHES "${CMAKE_BINARY_DIR}/default_hrtf.txt") + list(APPEND NEED_ANALYZE_SOURCE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/${obj}") + endif() +endforeach() +IF (ALSOFT_UTILS) + list(APPEND NEED_ANALYZE_SOURCE_FILES "${CMAKE_SOURCE_DIR}/utils/openal-info.c") +endif() +SET(CLANG_TIDY_EXECUTABLE "clang-tidy") +if(DEFINED ENV{CLANG_TIDY_EXECUTABLE}) + SET(CLANG_TIDY_EXECUTABLE $ENV{CLANG_TIDY_EXECUTABLE}) +endif() +add_custom_target(clang-tidy-check ${CLANG_TIDY_EXECUTABLE} -format-style=file -p ${CMAKE_BINARY_DIR}/compile_commands.json ${NEED_ANALYZE_SOURCE_FILES} DEPENDS ${NEED_ANALYZE_SOURCE_FILES}) + # Build main library if(LIBTYPE STREQUAL "STATIC") add_library(${IMPL_TARGET} STATIC ${COMMON_OBJS} ${OPENAL_OBJS} ${ALC_OBJS} ${CORE_OBJS}) target_compile_definitions(${IMPL_TARGET} PUBLIC AL_LIBTYPE_STATIC) - target_link_libraries(${IMPL_TARGET} PRIVATE ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB}) + target_link_libraries(${IMPL_TARGET} PRIVATE ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB} + $) if(WIN32) # This option is for static linking OpenAL Soft into another project @@ -1375,7 +1431,7 @@ else() PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES "ALC_API=${EXPORT_DECL}" "AL_API=${EXPORT_DECL}" ${CPP_DEFS}) target_compile_options(OpenAL PRIVATE ${C_FLAGS}) - target_link_libraries(OpenAL PRIVATE alcommon ${LINKER_FLAGS}) + target_link_libraries(OpenAL PRIVATE alsoft.common ${LINKER_FLAGS} fmt::fmt) target_include_directories(OpenAL PUBLIC $ @@ -1384,10 +1440,10 @@ else() ${OpenAL_SOURCE_DIR}/common ${OpenAL_BINARY_DIR} ) - set_target_properties(OpenAL PROPERTIES ${DEFAULT_TARGET_PROPS} PREFIX "" + set_target_properties(OpenAL PROPERTIES ${ALSOFT_STD_VERSION_PROPS} PREFIX "" OUTPUT_NAME ${LIBNAME}) - if(TARGET build_version) - add_dependencies(OpenAL build_version) + if(TARGET alsoft.build_version) + add_dependencies(OpenAL alsoft.build_version) endif() set(HAS_ROUTER 1) @@ -1406,24 +1462,30 @@ else() if(WIN32) set_target_properties(${IMPL_TARGET} PROPERTIES PREFIX "") endif() - target_link_libraries(${IMPL_TARGET} PRIVATE alcommon ${LINKER_FLAGS} ${EXTRA_LIBS} ${MATH_LIB}) + target_link_libraries(${IMPL_TARGET} PRIVATE alsoft.common ${LINKER_FLAGS} ${EXTRA_LIBS} + ${MATH_LIB} fmt::fmt) if(ALSOFT_UWP) - set(ALSOFT_CPPWINRT_VERSION "2.0.230706.1" CACHE STRING "The soft-oal default cppwinrt version") - - find_program(NUGET_EXE NAMES nuget) - if(NOT NUGET_EXE) - message("NUGET.EXE not found.") - message(FATAL_ERROR "Please install this executable, and run CMake again.") - endif() + find_package(cppwinrt CONFIG) + if (TARGET Microsoft::CppWinRT) + target_link_libraries(${IMPL_TARGET} PRIVATE Microsoft::CppWinRT) + else() + set(ALSOFT_CPPWINRT_VERSION "2.0.230706.1" CACHE STRING "The soft-oal default cppwinrt version") - exec_program(${NUGET_EXE} - ARGS install "Microsoft.Windows.CppWinRT" -Version ${ALSOFT_CPPWINRT_VERSION} -ExcludeVersion -OutputDirectory "\"${CMAKE_BINARY_DIR}/packages\"") + find_program(NUGET_EXE NAMES nuget) + if(NOT NUGET_EXE) + message("NUGET.EXE not found.") + message(FATAL_ERROR "Please install this executable, and run CMake again.") + endif() - set_target_properties(${IMPL_TARGET} PROPERTIES - VS_PROJECT_IMPORT ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.CppWinRT/build/native/Microsoft.Windows.CppWinRT.props - ) - target_link_libraries(${IMPL_TARGET} PRIVATE ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.CppWinRT/build/native/Microsoft.Windows.CppWinRT.targets) + exec_program(${NUGET_EXE} + ARGS install "Microsoft.Windows.CppWinRT" -Version ${ALSOFT_CPPWINRT_VERSION} -ExcludeVersion -OutputDirectory "\"${CMAKE_BINARY_DIR}/packages\"") + + set_target_properties(${IMPL_TARGET} PROPERTIES + VS_PROJECT_IMPORT ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.CppWinRT/build/native/Microsoft.Windows.CppWinRT.props + ) + target_link_libraries(${IMPL_TARGET} PRIVATE ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.CppWinRT/build/native/Microsoft.Windows.CppWinRT.targets) + endif() endif() if(NOT WIN32 AND NOT APPLE) @@ -1482,18 +1544,18 @@ target_include_directories(${IMPL_TARGET} ${OpenAL_SOURCE_DIR}/common ) -set_target_properties(${IMPL_TARGET} PROPERTIES ${DEFAULT_TARGET_PROPS} +set_target_properties(${IMPL_TARGET} PROPERTIES ${ALSOFT_STD_VERSION_PROPS} OUTPUT_NAME ${LIBNAME} VERSION ${LIB_VERSION} SOVERSION ${LIB_MAJOR_VERSION} ) target_compile_definitions(${IMPL_TARGET} - PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES "ALC_API=${EXPORT_DECL}" "AL_API=${EXPORT_DECL}" + PRIVATE AL_BUILD_LIBRARY AL_ALEXT_PROTOTYPES "ALC_API=${EXPORT_DECL}" "AL_API=${EXPORT_DECL}" ${CPP_DEFS}) target_compile_options(${IMPL_TARGET} PRIVATE ${C_FLAGS}) -if(TARGET build_version) - add_dependencies(${IMPL_TARGET} build_version) +if(TARGET alsoft.build_version) + add_dependencies(${IMPL_TARGET} alsoft.build_version) endif() if(WIN32 AND MINGW AND ALSOFT_BUILD_IMPORT_LIB AND NOT LIBTYPE STREQUAL "STATIC") @@ -1507,7 +1569,7 @@ if(WIN32 AND MINGW AND ALSOFT_BUILD_IMPORT_LIB AND NOT LIBTYPE STREQUAL "STATIC" message(STATUS "WARNING: Cannot find dlltool, disabling .def/.lib generation") endif() else() - target_link_options(OpenAL PRIVATE "-Wl,--output-def,OpenAL32.def") + target_link_options(OpenAL PRIVATE "-Wl,--output-def,${PROJECT_BINARY_DIR}/OpenAL32.def") add_custom_command(TARGET OpenAL POST_BUILD COMMAND "${SED_EXECUTABLE}" -i -e "s/ @[^ ]*//" OpenAL32.def COMMAND "${CMAKE_DLLTOOL}" -d OpenAL32.def -l OpenAL32.lib -D OpenAL32.dll @@ -1616,7 +1678,7 @@ if(ALSOFT_UTILS) target_include_directories(openal-info PRIVATE ${OpenAL_SOURCE_DIR}/common) target_compile_options(openal-info PRIVATE ${C_FLAGS}) target_link_libraries(openal-info PRIVATE ${LINKER_FLAGS} OpenAL ${UNICODE_FLAG}) - set_target_properties(openal-info PROPERTIES ${DEFAULT_TARGET_PROPS}) + set_target_properties(openal-info PROPERTIES ${ALSOFT_STD_VERSION_PROPS}) if(ALSOFT_INSTALL_EXAMPLES) set(EXTRA_INSTALLS ${EXTRA_INSTALLS} openal-info) endif() @@ -1627,30 +1689,31 @@ if(ALSOFT_UTILS) target_include_directories(uhjdecoder PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/common) target_compile_options(uhjdecoder PRIVATE ${C_FLAGS}) - target_link_libraries(uhjdecoder PUBLIC alcommon - PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG}) - set_target_properties(uhjdecoder PROPERTIES ${DEFAULT_TARGET_PROPS}) + target_link_libraries(uhjdecoder PUBLIC alsoft.common + PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG} fmt::fmt) + set_target_properties(uhjdecoder PROPERTIES ${ALSOFT_STD_VERSION_PROPS}) add_executable(uhjencoder utils/uhjencoder.cpp) target_compile_definitions(uhjencoder PRIVATE ${CPP_DEFS}) target_include_directories(uhjencoder PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/common) target_compile_options(uhjencoder PRIVATE ${C_FLAGS}) - target_link_libraries(uhjencoder PUBLIC alcommon - PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG}) - set_target_properties(uhjencoder PROPERTIES ${DEFAULT_TARGET_PROPS}) + target_link_libraries(uhjencoder PUBLIC alsoft.common + PRIVATE ${LINKER_FLAGS} SndFile::SndFile ${UNICODE_FLAG} fmt::fmt) + set_target_properties(uhjencoder PROPERTIES ${ALSOFT_STD_VERSION_PROPS}) endif() if(MYSOFA_FOUND) set(SOFA_SUPPORT_SRCS utils/sofa-support.cpp utils/sofa-support.h) - add_library(sofa-support STATIC EXCLUDE_FROM_ALL ${SOFA_SUPPORT_SRCS}) - target_compile_definitions(sofa-support PRIVATE ${CPP_DEFS}) - target_include_directories(sofa-support PUBLIC ${OpenAL_SOURCE_DIR}/common) - target_compile_options(sofa-support PRIVATE ${C_FLAGS}) - target_link_libraries(sofa-support PUBLIC alcommon MySOFA::MySOFA PRIVATE ${LINKER_FLAGS}) - set_target_properties(sofa-support PROPERTIES ${DEFAULT_TARGET_PROPS}) + add_library(alsoft.sofa-support STATIC EXCLUDE_FROM_ALL ${SOFA_SUPPORT_SRCS}) + target_compile_definitions(alsoft.sofa-support PRIVATE ${CPP_DEFS}) + target_include_directories(alsoft.sofa-support PUBLIC ${OpenAL_SOURCE_DIR}/common) + target_compile_options(alsoft.sofa-support PRIVATE ${C_FLAGS}) + target_link_libraries(alsoft.sofa-support PUBLIC alsoft.common MySOFA::MySOFA + PRIVATE ${LINKER_FLAGS} fmt::fmt) + set_target_properties(alsoft.sofa-support PROPERTIES ${ALSOFT_STD_VERSION_PROPS}) set(MAKEMHR_SRCS utils/makemhr/loaddef.cpp @@ -1659,16 +1722,14 @@ if(ALSOFT_UTILS) utils/makemhr/loadsofa.h utils/makemhr/makemhr.cpp utils/makemhr/makemhr.h) - if(NOT HAVE_GETOPT) - set(MAKEMHR_SRCS ${MAKEMHR_SRCS} utils/getopt.c utils/getopt.h) - endif() add_executable(makemhr ${MAKEMHR_SRCS}) target_compile_definitions(makemhr PRIVATE ${CPP_DEFS}) target_include_directories(makemhr PRIVATE ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/utils) target_compile_options(makemhr PRIVATE ${C_FLAGS}) - target_link_libraries(makemhr PRIVATE ${LINKER_FLAGS} sofa-support ${UNICODE_FLAG}) - set_target_properties(makemhr PROPERTIES ${DEFAULT_TARGET_PROPS}) + target_link_libraries(makemhr PRIVATE ${LINKER_FLAGS} alsoft.sofa-support ${UNICODE_FLAG} + fmt::fmt) + set_target_properties(makemhr PROPERTIES ${ALSOFT_STD_VERSION_PROPS}) if(ALSOFT_INSTALL_EXAMPLES) set(EXTRA_INSTALLS ${EXTRA_INSTALLS} makemhr) endif() @@ -1678,8 +1739,9 @@ if(ALSOFT_UTILS) target_compile_definitions(sofa-info PRIVATE ${CPP_DEFS}) target_include_directories(sofa-info PRIVATE ${OpenAL_SOURCE_DIR}/utils) target_compile_options(sofa-info PRIVATE ${C_FLAGS}) - target_link_libraries(sofa-info PRIVATE ${LINKER_FLAGS} sofa-support ${UNICODE_FLAG}) - set_target_properties(sofa-info PROPERTIES ${DEFAULT_TARGET_PROPS}) + target_link_libraries(sofa-info PRIVATE ${LINKER_FLAGS} alsoft.sofa-support + ${UNICODE_FLAG} fmt::fmt) + set_target_properties(sofa-info PROPERTIES ${ALSOFT_STD_VERSION_PROPS}) endif() message(STATUS "Building utility programs") @@ -1689,93 +1751,111 @@ if(ALSOFT_UTILS) message(STATUS "") endif() - +if(ALSOFT_EXAMPLES) # Axmol MODIFY BEGIN # Add a static library with common functions used by multiple example targets -add_library(al-excommon STATIC EXCLUDE_FROM_ALL +add_library(alsoft.excommon STATIC EXCLUDE_FROM_ALL examples/common/alhelpers.c examples/common/alhelpers.h) -target_compile_definitions(al-excommon PUBLIC ${CPP_DEFS}) -target_include_directories(al-excommon PUBLIC ${OpenAL_SOURCE_DIR}/common) -target_compile_options(al-excommon PUBLIC ${C_FLAGS}) -target_link_libraries(al-excommon PUBLIC OpenAL PRIVATE ${RT_LIB}) -set_target_properties(al-excommon PROPERTIES ${DEFAULT_TARGET_PROPS}) +target_compile_definitions(alsoft.excommon PUBLIC ${CPP_DEFS}) +target_include_directories(alsoft.excommon PUBLIC ${OpenAL_BINARY_DIR} ${OpenAL_SOURCE_DIR}/common) +target_compile_options(alsoft.excommon PUBLIC ${C_FLAGS}) +target_link_libraries(alsoft.excommon PUBLIC OpenAL PRIVATE ${RT_LIB}) +set_target_properties(alsoft.excommon PROPERTIES ${ALSOFT_STD_VERSION_PROPS}) +endif() # Axmol MODIFY END if(ALSOFT_EXAMPLES) add_executable(altonegen examples/altonegen.c) - target_link_libraries(altonegen PRIVATE ${LINKER_FLAGS} ${MATH_LIB} al-excommon ${UNICODE_FLAG}) - set_target_properties(altonegen PROPERTIES ${DEFAULT_TARGET_PROPS}) + target_link_libraries(altonegen PRIVATE ${LINKER_FLAGS} ${MATH_LIB} alsoft.excommon + ${UNICODE_FLAG}) + set_target_properties(altonegen PROPERTIES ${ALSOFT_STD_VERSION_PROPS}) add_executable(alrecord examples/alrecord.c) - target_link_libraries(alrecord PRIVATE ${LINKER_FLAGS} al-excommon ${UNICODE_FLAG}) - set_target_properties(alrecord PROPERTIES ${DEFAULT_TARGET_PROPS}) + target_link_libraries(alrecord PRIVATE ${LINKER_FLAGS} alsoft.excommon ${UNICODE_FLAG}) + set_target_properties(alrecord PROPERTIES ${ALSOFT_STD_VERSION_PROPS}) + + add_executable(aldebug examples/aldebug.cpp) + target_link_libraries(aldebug PRIVATE ${LINKER_FLAGS} alsoft.excommon ${UNICODE_FLAG} + fmt::fmt) + set_target_properties(aldebug PROPERTIES ${ALSOFT_STD_VERSION_PROPS}) + + add_executable(allafplay examples/allafplay.cpp) + target_link_libraries(allafplay PRIVATE ${LINKER_FLAGS} alsoft.common alsoft.excommon + ${UNICODE_FLAG} fmt::fmt) + set_target_properties(allafplay PROPERTIES ${ALSOFT_STD_VERSION_PROPS}) if(ALSOFT_INSTALL_EXAMPLES) - set(EXTRA_INSTALLS ${EXTRA_INSTALLS} altonegen alrecord) + set(EXTRA_INSTALLS ${EXTRA_INSTALLS} altonegen alrecord aldebug allafplay) endif() message(STATUS "Building example programs") if(SNDFILE_FOUND) add_executable(alplay examples/alplay.c) - target_link_libraries(alplay PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon + target_link_libraries(alplay PRIVATE ${LINKER_FLAGS} SndFile::SndFile alsoft.excommon ${UNICODE_FLAG}) - set_target_properties(alplay PROPERTIES ${DEFAULT_TARGET_PROPS}) + set_target_properties(alplay PROPERTIES ${ALSOFT_STD_VERSION_PROPS}) add_executable(alstream examples/alstream.c) - target_link_libraries(alstream PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon + target_link_libraries(alstream PRIVATE ${LINKER_FLAGS} SndFile::SndFile alsoft.excommon ${UNICODE_FLAG}) - set_target_properties(alstream PROPERTIES ${DEFAULT_TARGET_PROPS}) + set_target_properties(alstream PROPERTIES ${ALSOFT_STD_VERSION_PROPS}) add_executable(alreverb examples/alreverb.c) - target_link_libraries(alreverb PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon + target_link_libraries(alreverb PRIVATE ${LINKER_FLAGS} SndFile::SndFile alsoft.excommon ${UNICODE_FLAG}) - set_target_properties(alreverb PROPERTIES ${DEFAULT_TARGET_PROPS}) + set_target_properties(alreverb PROPERTIES ${ALSOFT_STD_VERSION_PROPS}) add_executable(almultireverb examples/almultireverb.c) target_link_libraries(almultireverb - PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon ${MATH_LIB} ${UNICODE_FLAG}) - set_target_properties(almultireverb PROPERTIES ${DEFAULT_TARGET_PROPS}) + PRIVATE ${LINKER_FLAGS} SndFile::SndFile alsoft.excommon ${MATH_LIB} ${UNICODE_FLAG}) + set_target_properties(almultireverb PROPERTIES ${ALSOFT_STD_VERSION_PROPS}) add_executable(allatency examples/allatency.c) - target_link_libraries(allatency PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon + target_link_libraries(allatency PRIVATE ${LINKER_FLAGS} SndFile::SndFile alsoft.excommon ${UNICODE_FLAG}) - set_target_properties(allatency PROPERTIES ${DEFAULT_TARGET_PROPS}) + set_target_properties(allatency PROPERTIES ${ALSOFT_STD_VERSION_PROPS}) add_executable(alhrtf examples/alhrtf.c) target_link_libraries(alhrtf - PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon ${MATH_LIB} ${UNICODE_FLAG}) - set_target_properties(alhrtf PROPERTIES ${DEFAULT_TARGET_PROPS}) + PRIVATE ${LINKER_FLAGS} SndFile::SndFile alsoft.excommon ${MATH_LIB} ${UNICODE_FLAG}) + set_target_properties(alhrtf PROPERTIES ${ALSOFT_STD_VERSION_PROPS}) add_executable(alstreamcb examples/alstreamcb.cpp) - target_link_libraries(alstreamcb PRIVATE ${LINKER_FLAGS} SndFile::SndFile al-excommon - ${UNICODE_FLAG}) - set_target_properties(alstreamcb PROPERTIES ${DEFAULT_TARGET_PROPS}) + target_link_libraries(alstreamcb PRIVATE ${LINKER_FLAGS} SndFile::SndFile alsoft.excommon + ${UNICODE_FLAG} fmt::fmt) + set_target_properties(alstreamcb PROPERTIES ${ALSOFT_STD_VERSION_PROPS}) + + add_executable(aldirect examples/aldirect.cpp) + target_link_libraries(aldirect PRIVATE ${LINKER_FLAGS} SndFile::SndFile alsoft.excommon + ${UNICODE_FLAG} fmt::fmt) + set_target_properties(aldirect PROPERTIES ${ALSOFT_STD_VERSION_PROPS}) add_executable(alconvolve examples/alconvolve.c) - target_link_libraries(alconvolve PRIVATE ${LINKER_FLAGS} alcommon SndFile::SndFile - al-excommon ${UNICODE_FLAG}) - set_target_properties(alconvolve PROPERTIES ${DEFAULT_TARGET_PROPS}) + target_link_libraries(alconvolve PRIVATE ${LINKER_FLAGS} alsoft.common SndFile::SndFile + alsoft.excommon ${UNICODE_FLAG}) + set_target_properties(alconvolve PROPERTIES ${ALSOFT_STD_VERSION_PROPS}) if(ALSOFT_INSTALL_EXAMPLES) set(EXTRA_INSTALLS ${EXTRA_INSTALLS} alplay alstream alreverb almultireverb allatency - alhrtf) + alhrtf aldirect) endif() message(STATUS "Building SndFile example programs") endif() - if(SDL2_FOUND) + # Can't safely use SDL3 and SDL2 together + if(SDL3_FOUND AND NOT HAVE_SDL2) + message(STATUS "Building SDL3 example programs") + add_executable(alloopback examples/alloopback.c) target_link_libraries(alloopback - PRIVATE ${LINKER_FLAGS} SDL2::SDL2 al-excommon ${MATH_LIB}) - set_target_properties(alloopback PROPERTIES ${DEFAULT_TARGET_PROPS}) + PRIVATE ${LINKER_FLAGS} SDL3::SDL3 alsoft.excommon ${MATH_LIB}) + set_target_properties(alloopback PROPERTIES ${ALSOFT_STD_VERSION_PROPS}) if(ALSOFT_INSTALL_EXAMPLES) set(EXTRA_INSTALLS ${EXTRA_INSTALLS} alloopback) endif() - message(STATUS "Building SDL example programs") - set(FFVER_OK FALSE) if(FFMPEG_FOUND) set(FFVER_OK TRUE) @@ -1804,19 +1884,19 @@ if(ALSOFT_EXAMPLES) add_executable(alffplay examples/alffplay.cpp) target_include_directories(alffplay PRIVATE ${FFMPEG_INCLUDE_DIRS}) target_link_libraries(alffplay - PRIVATE ${LINKER_FLAGS} SDL2::SDL2 ${FFMPEG_LIBRARIES} al-excommon) - set_target_properties(alffplay PROPERTIES ${DEFAULT_TARGET_PROPS}) + PRIVATE ${LINKER_FLAGS} SDL3::SDL3 ${FFMPEG_LIBRARIES} alsoft.excommon fmt::fmt) + set_target_properties(alffplay PROPERTIES ${ALSOFT_STD_VERSION_PROPS}) if(ALSOFT_INSTALL_EXAMPLES) set(EXTRA_INSTALLS ${EXTRA_INSTALLS} alffplay) endif() - message(STATUS "Building SDL+FFmpeg example programs") + message(STATUS "Building SDL3+FFmpeg example programs") endif() endif() message(STATUS "") endif() -if (ALSOFT_TESTS) +if(ALSOFT_TESTS) add_subdirectory(tests) endif() diff --git a/3rdparty/openal/ChangeLog b/3rdparty/openal/ChangeLog index e4236f85d224..2a4b6ff14f61 100644 --- a/3rdparty/openal/ChangeLog +++ b/3rdparty/openal/ChangeLog @@ -1,3 +1,110 @@ +openal-soft-1.24.2: + + Implemented the AL_SOFT_bformat_hoa extension. + + Implemented default device change events for the PulseAudio backend. + + Implemented an option for WASAPI exclusive mode playback. + + Fixed reverb being too quiet for sounds from different directions. + + Fixed compiling with certain versions of Clang. + + Fixed compiling for some older macOS versions. + + Fixed building alffplay on systems without pkg-config. + + Improved output format detection for CoreAudio. + + Changed the default resampler back to Cubic Spline. + + Added an SDL3 playback backend. Disabled by default to avoid a runtime + dependency and for compatibility; a single process can't safely use SDL2 + and SDL3 together on some OSs, so enable with care. + + Converted examples from SDL2 to SDL3. + + Integrated fmtlib into the main library and router for logging and string + formatting. + +openal-soft-1.24.1: + + Fixed compilation on PowerPC. + + Fixed compilation on some targets that lack lock-free 64-bit atomics. + + Fixed a crash when parsing certain option values. + + Fixed applying noexcept in the public headers with MSVC. + + Fixed building for UWP with vcpkg. + + Improved compatibility when compiling as C++20 or later. + + Integrated fmtlib for some examples and utilities. + +openal-soft-1.24.0: + + Updated library codebase to C++17. + + Implemented the ALC_SOFT_system_events extension. + + Implemented the AL_EXT_debug extension. + + Implemented the AL_EXT_direct_context extension. + + Implemented speaker configuration and headphones detection on CoreAudio. + + Fixed a potential crash with some extension functions on 32-bit Windows. + + Fixed a crash that can occur when stopping playback with the Oboe backend. + + Fixed calculating the reverb room rolloff. + + Fixed EAX occlusion, obstruction, and exclusion low-pass filter strength. + + Fixed EAX distance factor calculations. + + Fixed querying AL_EFFECTSLOT_EFFECT on auxiliary effect slots. + + Fixed compilation on some macOS systems that lack libdispatch. + + Fixed compilation as a subproject with MinGW. + + Changed the context error state to be thread-local. This is technically out + of spec, but necessary to avoid race conditions with multi-threaded use. + + Split the cubic resampler into 4-point spline and gaussian variants. The + latter prioritizing the suppression of aliasing distortion and harmonics, + the former not reducing high frequencies as much. + + Improved timing precision of starting delayed sources. + + Improved ring modulator quality. + + Improved performance of convolution reverb. + + Improved WASAPI device enumeration performance. + + Added UWP support. + + Added 'noexcept' to functions and function types when compiled as C++. As a + C API, OpenAL can't be expected to throw C++ exceptions, nor can it handle + them if they leave a callback. + + Added an experimental config option for using WASAPI spatial audio output. + + Added enumeration support to the PortAudio backend. + + Added compatibility options to override the AL_VENDOR, AL_VERSION, and + AL_RENDERER strings. + + Added an example to play LAF files. + + Disabled real-time mixing by default for PipeWire playback. + + Disabled the SndIO backend by default on non-BSD targets. + openal-soft-1.23.1: Implemented the AL_SOFT_UHJ_ex extension. diff --git a/3rdparty/openal/OpenALConfig.cmake.in b/3rdparty/openal/OpenALConfig.cmake.in new file mode 100644 index 000000000000..9704d3c496aa --- /dev/null +++ b/3rdparty/openal/OpenALConfig.cmake.in @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.1...3.18) + +include("${CMAKE_CURRENT_LIST_DIR}/OpenALTargets.cmake") + +set(OPENAL_FOUND ON) +set(OPENAL_INCLUDE_DIR $) +set(OPENAL_LIBRARY $) +set(OPENAL_DEFINITIONS $) +set(OPENAL_VERSION_STRING @PACKAGE_VERSION@) diff --git a/3rdparty/openal/README.md b/3rdparty/openal/README.md index dac53e71edff..09f9ec24d909 100644 --- a/3rdparty/openal/README.md +++ b/3rdparty/openal/README.md @@ -78,15 +78,26 @@ API, including some extensions. It also includes utility libraries for math and linear algebra, which can be useful for 3D calculations. Java Bindings: +* [LWJGL](https://github.com/LWJGL/lwjgl3), the Lightweight Java Game Library, +includes Java bindings for the OpenAL API, usable with OpenAL Soft. * [JOAL](https://jogamp.org/joal/www/), part of the JogAmp project, includes Java bindings for the OpenAL API, usable with OpenAL Soft. It also includes a higher level Sound3D Toolkit API and utility functions to make easier use of OpenAL features and capabilities. +Kotlin Bindings: +* [Multiplatform OpenAL](https://git.karmakrafts.dev/kk/multiplatform-openal), developed for the Kleaver project, +includes Kotlin/Native bindings for the OpenAL API, based on OpenAL Soft with support +for Windows, Linux, macOS, iOS and Android. + Python Bindings: * [PyOpenAL](https://pypi.org/project/PyOpenAL/). Also includes methods to play wave files and, with PyOgg, also Vorbis, Opus, and FLAC. +FreePascal/Lazarus Bindings: +* [ALSound](https://github.com/Lulu04/ALSound). Also includes a higher level +API and libsndfile support to simplify loading and playing sounds. + Other bindings for these and other languages also exist. This list will grow as more bindings are found. diff --git a/3rdparty/openal/al/auxeffectslot.cpp b/3rdparty/openal/al/auxeffectslot.cpp index 332524101ab0..e9ccafdda6dd 100644 --- a/3rdparty/openal/al/auxeffectslot.cpp +++ b/3rdparty/openal/al/auxeffectslot.cpp @@ -23,70 +23,83 @@ #include "auxeffectslot.h" #include -#include +#include #include +#include #include #include #include #include -#include +#include +#include +#include +#include #include "AL/al.h" #include "AL/alc.h" +#include "AL/alext.h" #include "AL/efx.h" #include "albit.h" #include "alc/alu.h" #include "alc/context.h" #include "alc/device.h" +#include "alc/effects/base.h" #include "alc/inprogext.h" #include "almalloc.h" #include "alnumeric.h" #include "alspan.h" +#include "atomic.h" #include "buffer.h" +#include "core/buffer_storage.h" +#include "core/device.h" #include "core/except.h" #include "core/fpu_ctrl.h" #include "core/logging.h" #include "direct_defs.h" #include "effect.h" +#include "flexarray.h" #include "opthelpers.h" +#if ALSOFT_EAX +#include "eax/api.h" +#include "eax/call.h" +#include "eax/effect.h" +#include "eax/fx_slot_index.h" +#include "eax/utils.h" +#endif + namespace { -struct FactoryItem { - EffectSlotType Type; - EffectStateFactory* (&GetFactory)(void); -}; -constexpr FactoryItem FactoryList[] = { - { EffectSlotType::None, NullStateFactory_getFactory }, - { EffectSlotType::EAXReverb, ReverbStateFactory_getFactory }, - { EffectSlotType::Reverb, StdReverbStateFactory_getFactory }, - { EffectSlotType::Autowah, AutowahStateFactory_getFactory }, - { EffectSlotType::Chorus, ChorusStateFactory_getFactory }, - { EffectSlotType::Compressor, CompressorStateFactory_getFactory }, - { EffectSlotType::Distortion, DistortionStateFactory_getFactory }, - { EffectSlotType::Echo, EchoStateFactory_getFactory }, - { EffectSlotType::Equalizer, EqualizerStateFactory_getFactory }, - { EffectSlotType::Flanger, FlangerStateFactory_getFactory }, - { EffectSlotType::FrequencyShifter, FshifterStateFactory_getFactory }, - { EffectSlotType::RingModulator, ModulatorStateFactory_getFactory }, - { EffectSlotType::PitchShifter, PshifterStateFactory_getFactory }, - { EffectSlotType::VocalMorpher, VmorpherStateFactory_getFactory }, - { EffectSlotType::DedicatedDialog, DedicatedStateFactory_getFactory }, - { EffectSlotType::DedicatedLFE, DedicatedStateFactory_getFactory }, - { EffectSlotType::Convolution, ConvolutionStateFactory_getFactory }, -}; - -EffectStateFactory *getFactoryByType(EffectSlotType type) +using SubListAllocator = al::allocator>; + +[[nodiscard]] +auto getFactoryByType(EffectSlotType type) -> EffectStateFactory* { - auto iter = std::find_if(std::begin(FactoryList), std::end(FactoryList), - [type](const FactoryItem &item) noexcept -> bool - { return item.Type == type; }); - return (iter != std::end(FactoryList)) ? iter->GetFactory() : nullptr; + switch(type) + { + case EffectSlotType::None: return NullStateFactory_getFactory(); + case EffectSlotType::Reverb: return ReverbStateFactory_getFactory(); + case EffectSlotType::Chorus: return ChorusStateFactory_getFactory(); + case EffectSlotType::Autowah: return AutowahStateFactory_getFactory(); + case EffectSlotType::Compressor: return CompressorStateFactory_getFactory(); + case EffectSlotType::Convolution: return ConvolutionStateFactory_getFactory(); + case EffectSlotType::Dedicated: return DedicatedStateFactory_getFactory(); + case EffectSlotType::Distortion: return DistortionStateFactory_getFactory(); + case EffectSlotType::Echo: return EchoStateFactory_getFactory(); + case EffectSlotType::Equalizer: return EqualizerStateFactory_getFactory(); + case EffectSlotType::Flanger: return ChorusStateFactory_getFactory(); + case EffectSlotType::FrequencyShifter: return FshifterStateFactory_getFactory(); + case EffectSlotType::RingModulator: return ModulatorStateFactory_getFactory(); + case EffectSlotType::PitchShifter: return PshifterStateFactory_getFactory(); + case EffectSlotType::VocalMorpher: return VmorpherStateFactory_getFactory(); + } + return nullptr; } -inline ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id) noexcept +[[nodiscard]] +auto LookupEffectSlot(ALCcontext *context, ALuint id) noexcept -> ALeffectslot* { const size_t lidx{(id-1) >> 6}; const ALuint slidx{(id-1) & 0x3f}; @@ -96,10 +109,11 @@ inline ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id) noexcept EffectSlotSubList &sublist{context->mEffectSlotList[lidx]}; if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY return nullptr; - return sublist.EffectSlots + slidx; + return al::to_address(sublist.EffectSlots->begin() + slidx); } -inline ALeffect *LookupEffect(ALCdevice *device, ALuint id) noexcept +[[nodiscard]] +inline auto LookupEffect(al::Device *device, ALuint id) noexcept -> ALeffect* { const size_t lidx{(id-1) >> 6}; const ALuint slidx{(id-1) & 0x3f}; @@ -109,10 +123,11 @@ inline ALeffect *LookupEffect(ALCdevice *device, ALuint id) noexcept EffectSubList &sublist = device->EffectList[lidx]; if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY return nullptr; - return sublist.Effects + slidx; + return al::to_address(sublist.Effects->begin() + slidx); } -inline ALbuffer *LookupBuffer(ALCdevice *device, ALuint id) noexcept +[[nodiscard]] +inline auto LookupBuffer(al::Device *device, ALuint id) noexcept -> ALbuffer* { const size_t lidx{(id-1) >> 6}; const ALuint slidx{(id-1) & 0x3f}; @@ -122,7 +137,7 @@ inline ALbuffer *LookupBuffer(ALCdevice *device, ALuint id) noexcept BufferSubList &sublist = device->BufferList[lidx]; if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY return nullptr; - return sublist.Buffers + slidx; + return al::to_address(sublist.Buffers->begin() + slidx); } @@ -130,44 +145,44 @@ void AddActiveEffectSlots(const al::span auxslots, ALCcontext *co { if(auxslots.empty()) return; EffectSlotArray *curarray{context->mActiveAuxSlots.load(std::memory_order_acquire)}; - size_t newcount{curarray->size() + auxslots.size()}; + if((curarray->size()>>1) > std::numeric_limits::max()-auxslots.size()) + throw std::runtime_error{"Too many active effect slots"}; + + size_t newcount{(curarray->size()>>1) + auxslots.size()}; + if(newcount > std::numeric_limits::max()>>1) + throw std::runtime_error{"Too many active effect slots"}; - /* Insert the new effect slots into the head of the array, followed by the - * existing ones. + /* Insert the new effect slots into the head of the new array, followed by + * the existing ones. */ - EffectSlotArray *newarray = EffectSlot::CreatePtrArray(newcount); - auto slotiter = std::transform(auxslots.begin(), auxslots.end(), newarray->begin(), - [](ALeffectslot *auxslot) noexcept { return auxslot->mSlot; }); - std::copy(curarray->begin(), curarray->end(), slotiter); + auto newarray = EffectSlot::CreatePtrArray(newcount<<1); + auto new_end = std::transform(auxslots.begin(), auxslots.end(), newarray->begin(), + std::mem_fn(&ALeffectslot::mSlot)); + new_end = std::copy_n(curarray->begin(), curarray->size()>>1, new_end); /* Remove any duplicates (first instance of each will be kept). */ - auto last = newarray->end(); for(auto start=newarray->begin()+1;;) { - last = std::remove(start, last, *(start-1)); - if(start == last) break; + new_end = std::remove(start, new_end, *(start-1)); + if(start == new_end) break; ++start; } - newcount = static_cast(std::distance(newarray->begin(), last)); + newcount = static_cast(std::distance(newarray->begin(), new_end)); /* Reallocate newarray if the new size ended up smaller from duplicate * removal. */ - if(newcount < newarray->size()) UNLIKELY + if(newcount < newarray->size()>>1) UNLIKELY { - curarray = newarray; - newarray = EffectSlot::CreatePtrArray(newcount); - std::copy_n(curarray->begin(), newcount, newarray->begin()); - delete curarray; - curarray = nullptr; + auto oldarray = std::move(newarray); + newarray = EffectSlot::CreatePtrArray(newcount<<1); + new_end = std::copy_n(oldarray->begin(), newcount, newarray->begin()); } - std::uninitialized_fill_n(newarray->end(), newcount, nullptr); + std::fill(new_end, newarray->end(), nullptr); - curarray = context->mActiveAuxSlots.exchange(newarray, std::memory_order_acq_rel); - context->mDevice->waitForMix(); - - std::destroy_n(curarray->end(), curarray->size()); - delete curarray; + auto oldarray = context->mActiveAuxSlots.exchange(std::move(newarray), + std::memory_order_acq_rel); + std::ignore = context->mDevice->waitForMix(); } void RemoveActiveEffectSlots(const al::span auxslots, ALCcontext *context) @@ -178,9 +193,9 @@ void RemoveActiveEffectSlots(const al::span auxslots, ALCcontext /* Don't shrink the allocated array size since we don't know how many (if * any) of the effect slots to remove are in the array. */ - EffectSlotArray *newarray = EffectSlot::CreatePtrArray(curarray->size()); + auto newarray = EffectSlot::CreatePtrArray(curarray->size()); - auto new_end = std::copy(curarray->begin(), curarray->end(), newarray->begin()); + auto new_end = std::copy_n(curarray->begin(), curarray->size()>>1, newarray->begin()); /* Remove elements from newarray that match any ID in slotids. */ for(const ALeffectslot *auxslot : auxslots) { @@ -191,26 +206,22 @@ void RemoveActiveEffectSlots(const al::span auxslots, ALCcontext /* Reallocate with the new size. */ auto newsize = static_cast(std::distance(newarray->begin(), new_end)); - if(newsize != newarray->size()) LIKELY + if(newsize < newarray->size()>>1) LIKELY { - curarray = newarray; - newarray = EffectSlot::CreatePtrArray(newsize); - std::copy_n(curarray->begin(), newsize, newarray->begin()); - - delete curarray; - curarray = nullptr; + auto oldarray = std::move(newarray); + newarray = EffectSlot::CreatePtrArray(newsize<<1); + new_end = std::copy_n(oldarray->begin(), newsize, newarray->begin()); } - std::uninitialized_fill_n(newarray->end(), newsize, nullptr); + std::fill(new_end, newarray->end(), nullptr); - curarray = context->mActiveAuxSlots.exchange(newarray, std::memory_order_acq_rel); - context->mDevice->waitForMix(); - - std::destroy_n(curarray->end(), curarray->size()); - delete curarray; + auto oldarray = context->mActiveAuxSlots.exchange(std::move(newarray), + std::memory_order_acq_rel); + std::ignore = context->mDevice->waitForMix(); } -EffectSlotType EffectSlotTypeFromEnum(ALenum type) +[[nodiscard]] +constexpr auto EffectSlotTypeFromEnum(ALenum type) noexcept -> EffectSlotType { switch(type) { @@ -227,17 +238,18 @@ EffectSlotType EffectSlotTypeFromEnum(ALenum type) case AL_EFFECT_AUTOWAH: return EffectSlotType::Autowah; case AL_EFFECT_COMPRESSOR: return EffectSlotType::Compressor; case AL_EFFECT_EQUALIZER: return EffectSlotType::Equalizer; - case AL_EFFECT_EAXREVERB: return EffectSlotType::EAXReverb; - case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT: return EffectSlotType::DedicatedLFE; - case AL_EFFECT_DEDICATED_DIALOGUE: return EffectSlotType::DedicatedDialog; - case AL_EFFECT_CONVOLUTION_REVERB_SOFT: return EffectSlotType::Convolution; + case AL_EFFECT_EAXREVERB: return EffectSlotType::Reverb; + case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT: return EffectSlotType::Dedicated; + case AL_EFFECT_DEDICATED_DIALOGUE: return EffectSlotType::Dedicated; + case AL_EFFECT_CONVOLUTION_SOFT: return EffectSlotType::Convolution; } - ERR("Unhandled effect enum: 0x%04x\n", type); + ERR("Unhandled effect enum: {:#04x}", as_unsigned(type)); return EffectSlotType::None; } -bool EnsureEffectSlots(ALCcontext *context, size_t needed) -{ +[[nodiscard]] +auto EnsureEffectSlots(ALCcontext *context, size_t needed) noexcept -> bool +try { size_t count{std::accumulate(context->mEffectSlotList.cbegin(), context->mEffectSlotList.cend(), 0_uz, [](size_t cur, const EffectSlotSubList &sublist) noexcept -> size_t @@ -248,22 +260,20 @@ bool EnsureEffectSlots(ALCcontext *context, size_t needed) if(context->mEffectSlotList.size() >= 1<<25) UNLIKELY return false; - context->mEffectSlotList.emplace_back(); - auto sublist = context->mEffectSlotList.end() - 1; - sublist->FreeMask = ~0_u64; - sublist->EffectSlots = static_cast( - al_calloc(alignof(ALeffectslot), sizeof(ALeffectslot)*64)); - if(!sublist->EffectSlots) UNLIKELY - { - context->mEffectSlotList.pop_back(); - return false; - } - count += 64; + EffectSlotSubList sublist{}; + sublist.FreeMask = ~0_u64; + sublist.EffectSlots = SubListAllocator{}.allocate(1); + context->mEffectSlotList.emplace_back(std::move(sublist)); + count += std::tuple_size_v; } return true; } +catch(...) { + return false; +} -ALeffectslot *AllocEffectSlot(ALCcontext *context) +[[nodiscard]] +auto AllocEffectSlot(ALCcontext *context) -> ALeffectslot* { auto sublist = std::find_if(context->mEffectSlotList.begin(), context->mEffectSlotList.end(), [](const EffectSlotSubList &entry) noexcept -> bool @@ -272,7 +282,8 @@ ALeffectslot *AllocEffectSlot(ALCcontext *context) auto slidx = static_cast(al::countr_zero(sublist->FreeMask)); ASSUME(slidx < 64); - ALeffectslot *slot{al::construct_at(sublist->EffectSlots + slidx, context)}; + ALeffectslot *slot{al::construct_at(al::to_address(sublist->EffectSlots->begin() + slidx), + context)}; aluInitEffectPanning(slot->mSlot, context); /* Add 1 to avoid ID 0. */ @@ -312,255 +323,186 @@ inline void UpdateProps(ALeffectslot *slot, ALCcontext *context) } // namespace -AL_API DECL_FUNC2(void, alGenAuxiliaryEffectSlots, ALsizei, ALuint*) +AL_API DECL_FUNC2(void, alGenAuxiliaryEffectSlots, ALsizei,n, ALuint*,effectslots) FORCE_ALIGN void AL_APIENTRY alGenAuxiliaryEffectSlotsDirect(ALCcontext *context, ALsizei n, ALuint *effectslots) noexcept -{ - if(n < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Generating %d effect slots", n); +try { + if(n < 0) + context->throw_error(AL_INVALID_VALUE, "Generating {} effect slots", n); if(n <= 0) UNLIKELY return; - std::lock_guard _{context->mEffectSlotLock}; - ALCdevice *device{context->mALDevice.get()}; - if(static_cast(n) > device->AuxiliaryEffectSlotMax-context->mNumEffectSlots) - { - context->setError(AL_OUT_OF_MEMORY, "Exceeding %u effect slot limit (%u + %d)", + auto slotlock = std::lock_guard{context->mEffectSlotLock}; + auto *device = context->mALDevice.get(); + + const al::span eids{effectslots, static_cast(n)}; + if(context->mNumEffectSlots > device->AuxiliaryEffectSlotMax + || eids.size() > device->AuxiliaryEffectSlotMax-context->mNumEffectSlots) + context->throw_error(AL_OUT_OF_MEMORY, "Exceeding {} effect slot limit ({} + {})", device->AuxiliaryEffectSlotMax, context->mNumEffectSlots, n); - return; - } - if(!EnsureEffectSlots(context, static_cast(n))) - { - context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d effectslot%s", n, + + if(!EnsureEffectSlots(context, eids.size())) + context->throw_error(AL_OUT_OF_MEMORY, "Failed to allocate {} effectslot{}", n, (n==1) ? "" : "s"); - return; - } - if(n == 1) - { - ALeffectslot *slot{AllocEffectSlot(context)}; - effectslots[0] = slot->id; + std::vector slots; + try { + if(eids.size() == 1) + { + /* Special handling for the easy and normal case. */ + eids[0] = AllocEffectSlot(context)->id; + } + else + { + slots.reserve(eids.size()); + std::generate_n(std::back_inserter(slots), eids.size(), + [context]{ return AllocEffectSlot(context); }); + + std::transform(slots.cbegin(), slots.cend(), eids.begin(), + [](ALeffectslot *slot) -> ALuint { return slot->id; }); + } } - else - { - std::vector ids; - ALsizei count{n}; - ids.reserve(static_cast(count)); - do { - ALeffectslot *slot{AllocEffectSlot(context)}; - ids.emplace_back(slot->id); - } while(--count); - std::copy(ids.cbegin(), ids.cend(), effectslots); + catch(std::exception& e) { + ERR("Exception allocating effectslot {} of {}: {}", slots.size()+1, n, e.what()); + auto delete_effectslot = [context](ALeffectslot *slot) -> void + { FreeEffectSlot(context, slot); }; + std::for_each(slots.begin(), slots.end(), delete_effectslot); + context->throw_error(AL_INVALID_OPERATION, "Exception allocating {} effectslots: {}", n, + e.what()); } } +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); +} -AL_API DECL_FUNC2(void, alDeleteAuxiliaryEffectSlots, ALsizei, const ALuint*) +AL_API DECL_FUNC2(void, alDeleteAuxiliaryEffectSlots, ALsizei,n, const ALuint*,effectslots) FORCE_ALIGN void AL_APIENTRY alDeleteAuxiliaryEffectSlotsDirect(ALCcontext *context, ALsizei n, const ALuint *effectslots) noexcept -{ +try { if(n < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Deleting %d effect slots", n); + context->throw_error(AL_INVALID_VALUE, "Deleting {} effect slots", n); if(n <= 0) UNLIKELY return; - std::lock_guard _{context->mEffectSlotLock}; + std::lock_guard slotlock{context->mEffectSlotLock}; if(n == 1) { - ALeffectslot *slot{LookupEffectSlot(context, effectslots[0])}; - if(!slot) UNLIKELY - { - context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslots[0]); - return; - } - if(ReadRef(slot->ref) != 0) UNLIKELY - { - context->setError(AL_INVALID_OPERATION, "Deleting in-use effect slot %u", - effectslots[0]); - return; - } + ALeffectslot *slot{LookupEffectSlot(context, *effectslots)}; + if(!slot) + context->throw_error(AL_INVALID_NAME, "Invalid effect slot ID {}", *effectslots); + if(slot->ref.load(std::memory_order_relaxed) != 0) + context->throw_error(AL_INVALID_OPERATION, "Deleting in-use effect slot {}", + *effectslots); + RemoveActiveEffectSlots({&slot, 1u}, context); FreeEffectSlot(context, slot); } else { - auto slots = std::vector(static_cast(n)); - for(size_t i{0};i < slots.size();++i) - { - ALeffectslot *slot{LookupEffectSlot(context, effectslots[i])}; - if(!slot) UNLIKELY - { - context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslots[i]); - return; - } - if(ReadRef(slot->ref) != 0) UNLIKELY - { - context->setError(AL_INVALID_OPERATION, "Deleting in-use effect slot %u", - effectslots[i]); - return; - } - slots[i] = slot; - } - /* Remove any duplicates. */ - auto slots_end = slots.end(); - for(auto start=slots.begin()+1;start != slots_end;++start) + const al::span eids{effectslots, static_cast(n)}; + std::vector slots; + slots.reserve(eids.size()); + + auto lookupslot = [context](const ALuint eid) -> ALeffectslot* { - slots_end = std::remove(start, slots_end, *(start-1)); - if(start == slots_end) break; - } - slots.erase(slots_end, slots.end()); + ALeffectslot *slot{LookupEffectSlot(context, eid)}; + if(!slot) + context->throw_error(AL_INVALID_NAME, "Invalid effect slot ID {}", eid); + if(slot->ref.load(std::memory_order_relaxed) != 0) + context->throw_error(AL_INVALID_OPERATION, "Deleting in-use effect slot {}", eid); + return slot; + }; + std::transform(eids.cbegin(), eids.cend(), std::back_inserter(slots), lookupslot); /* All effectslots are valid, remove and delete them */ RemoveActiveEffectSlots(slots, context); - for(ALeffectslot *slot : slots) - FreeEffectSlot(context, slot); + + auto delete_effectslot = [context](const ALuint eid) -> void + { + if(ALeffectslot *slot{LookupEffectSlot(context, eid)}) + FreeEffectSlot(context, slot); + }; + std::for_each(eids.begin(), eids.end(), delete_effectslot); } } +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); +} -AL_API DECL_FUNC1(ALboolean, alIsAuxiliaryEffectSlot, ALuint) +AL_API DECL_FUNC1(ALboolean, alIsAuxiliaryEffectSlot, ALuint,effectslot) FORCE_ALIGN ALboolean AL_APIENTRY alIsAuxiliaryEffectSlotDirect(ALCcontext *context, ALuint effectslot) noexcept { - std::lock_guard _{context->mEffectSlotLock}; + std::lock_guard slotlock{context->mEffectSlotLock}; if(LookupEffectSlot(context, effectslot) != nullptr) return AL_TRUE; return AL_FALSE; } -AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlaySOFT(ALuint slotid) noexcept +AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlaySOFT(ALuint) noexcept { ContextRef context{GetContextRef()}; if(!context) UNLIKELY return; - - std::lock_guard _{context->mEffectSlotLock}; - ALeffectslot *slot{LookupEffectSlot(context.get(), slotid)}; - if(!slot) UNLIKELY - { - context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", slotid); - return; - } - if(slot->mState == SlotState::Playing) - return; - - slot->mPropsDirty = false; - slot->updateProps(context.get()); - - AddActiveEffectSlots({&slot, 1}, context.get()); - slot->mState = SlotState::Playing; + context->setError(AL_INVALID_OPERATION, "alAuxiliaryEffectSlotPlaySOFT not supported"); } -AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlayvSOFT(ALsizei n, const ALuint *slotids) noexcept +AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlayvSOFT(ALsizei, const ALuint*) noexcept { ContextRef context{GetContextRef()}; if(!context) UNLIKELY return; - - if(n < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Playing %d effect slots", n); - if(n <= 0) UNLIKELY return; - - auto slots = std::vector(static_cast(n)); - std::lock_guard _{context->mEffectSlotLock}; - for(size_t i{0};i < slots.size();++i) - { - ALeffectslot *slot{LookupEffectSlot(context.get(), slotids[i])}; - if(!slot) UNLIKELY - { - context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", slotids[i]); - return; - } - - if(slot->mState != SlotState::Playing) - { - slot->mPropsDirty = false; - slot->updateProps(context.get()); - } - slots[i] = slot; - }; - - AddActiveEffectSlots(slots, context.get()); - for(auto slot : slots) - slot->mState = SlotState::Playing; + context->setError(AL_INVALID_OPERATION, "alAuxiliaryEffectSlotPlayvSOFT not supported"); } -AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopSOFT(ALuint slotid) noexcept +AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopSOFT(ALuint) noexcept { ContextRef context{GetContextRef()}; if(!context) UNLIKELY return; - - std::lock_guard _{context->mEffectSlotLock}; - ALeffectslot *slot{LookupEffectSlot(context.get(), slotid)}; - if(!slot) UNLIKELY - { - context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", slotid); - return; - } - - RemoveActiveEffectSlots({&slot, 1}, context.get()); - slot->mState = SlotState::Stopped; + context->setError(AL_INVALID_OPERATION, "alAuxiliaryEffectSlotStopSOFT not supported"); } -AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopvSOFT(ALsizei n, const ALuint *slotids) noexcept +AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopvSOFT(ALsizei, const ALuint*) noexcept { ContextRef context{GetContextRef()}; if(!context) UNLIKELY return; - - if(n < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Stopping %d effect slots", n); - if(n <= 0) UNLIKELY return; - - auto slots = std::vector(static_cast(n)); - std::lock_guard _{context->mEffectSlotLock}; - for(size_t i{0};i < slots.size();++i) - { - ALeffectslot *slot{LookupEffectSlot(context.get(), slotids[i])}; - if(!slot) UNLIKELY - { - context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", slotids[i]); - return; - } - - slots[i] = slot; - }; - - RemoveActiveEffectSlots(slots, context.get()); - for(auto slot : slots) - slot->mState = SlotState::Stopped; + context->setError(AL_INVALID_OPERATION, "alAuxiliaryEffectSlotStopvSOFT not supported"); } -AL_API DECL_FUNC3(void, alAuxiliaryEffectSloti, ALuint, ALenum, ALint) +AL_API DECL_FUNC3(void, alAuxiliaryEffectSloti, ALuint,effectslot, ALenum,param, ALint,value) FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotiDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALint value) noexcept -{ - std::lock_guard _{context->mPropLock}; - std::lock_guard __{context->mEffectSlotLock}; - ALeffectslot *slot = LookupEffectSlot(context, effectslot); +try { + std::lock_guard proplock{context->mPropLock}; + std::lock_guard slotlock{context->mEffectSlotLock}; + + ALeffectslot *slot{LookupEffectSlot(context, effectslot)}; if(!slot) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot); + context->throw_error(AL_INVALID_NAME, "Invalid effect slot ID {}", effectslot); ALeffectslot *target{}; - ALCdevice *device{}; ALenum err{}; switch(param) { case AL_EFFECTSLOT_EFFECT: - device = context->mALDevice.get(); - { - std::lock_guard ___{device->EffectLock}; - ALeffect *effect{value ? LookupEffect(device, static_cast(value)) : nullptr}; + auto *device = context->mALDevice.get(); + auto effectlock = std::lock_guard{device->EffectLock}; + auto *effect = value ? LookupEffect(device, static_cast(value)) : nullptr; if(effect) err = slot->initEffect(effect->id, effect->type, effect->Props, context); else { if(value != 0) - return context->setError(AL_INVALID_VALUE, "Invalid effect ID %u", value); + context->throw_error(AL_INVALID_VALUE, "Invalid effect ID {}", value); err = slot->initEffect(0, AL_EFFECT_NULL, EffectProps{}, context); } } - if(err != AL_NO_ERROR) UNLIKELY - { - context->setError(err, "Effect initialization failed"); - return; - } + if(err != AL_NO_ERROR) + context->throw_error(err, "Effect initialization failed"); + if(slot->mState == SlotState::Initial) UNLIKELY { slot->mPropsDirty = false; @@ -570,21 +512,23 @@ FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotiDirect(ALCcontext *context, A slot->mState = SlotState::Playing; return; } - break; + UpdateProps(slot, context); + return; case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO: if(!(value == AL_TRUE || value == AL_FALSE)) - return context->setError(AL_INVALID_VALUE, - "Effect slot auxiliary send auto out of range"); - if(slot->AuxSendAuto == !!value) UNLIKELY - return; - slot->AuxSendAuto = !!value; - break; + context->throw_error(AL_INVALID_VALUE, "Effect slot auxiliary send auto out of range"); + if(!(slot->AuxSendAuto == !!value)) LIKELY + { + slot->AuxSendAuto = !!value; + UpdateProps(slot, context); + } + return; case AL_EFFECTSLOT_TARGET_SOFT: target = LookupEffectSlot(context, static_cast(value)); if(value && !target) - return context->setError(AL_INVALID_VALUE, "Invalid effect slot target ID"); + context->throw_error(AL_INVALID_VALUE, "Invalid effect slot target ID {}", value); if(slot->Target == target) UNLIKELY return; if(target) @@ -593,8 +537,8 @@ FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotiDirect(ALCcontext *context, A while(checker && checker != slot) checker = checker->Target; if(checker) - return context->setError(AL_INVALID_OPERATION, - "Setting target of effect slot ID %u to %u creates circular chain", slot->id, + context->throw_error(AL_INVALID_OPERATION, + "Setting target of effect slot ID {} to {} creates circular chain", slot->id, target->id); } @@ -612,32 +556,70 @@ FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotiDirect(ALCcontext *context, A if(target) IncrementRef(target->ref); slot->Target = target; - break; + UpdateProps(slot, context); + return; case AL_BUFFER: - device = context->mALDevice.get(); - - if(slot->mState == SlotState::Playing) - return context->setError(AL_INVALID_OPERATION, - "Setting buffer on playing effect slot %u", slot->id); - if(ALbuffer *buffer{slot->Buffer}) { - if(buffer->id == static_cast(value)) UNLIKELY + if(buffer->id == static_cast(value)) return; } - else if(value == 0) UNLIKELY + else if(value == 0) return; + if(slot->mState == SlotState::Playing) + { + EffectStateFactory *factory{getFactoryByType(slot->Effect.Type)}; + assert(factory); + al::intrusive_ptr state{factory->create()}; + + auto *device = context->mALDevice.get(); + auto bufferlock = std::unique_lock{device->BufferLock}; + ALbuffer *buffer{}; + if(value) + { + buffer = LookupBuffer(device, static_cast(value)); + if(!buffer) + context->throw_error(AL_INVALID_VALUE, "Invalid buffer ID {}", value); + if(buffer->mCallback) + context->throw_error(AL_INVALID_OPERATION, + "Callback buffer not valid for effects"); + + IncrementRef(buffer->ref); + } + + /* Stop the effect slot from processing while we switch buffers. */ + RemoveActiveEffectSlots({&slot, 1}, context); + + if(ALbuffer *oldbuffer{slot->Buffer}) + DecrementRef(oldbuffer->ref); + slot->Buffer = buffer; + bufferlock.unlock(); + + state->mOutTarget = device->Dry.Buffer; + { + FPUCtl mixer_mode{}; + state->deviceUpdate(device, buffer); + } + slot->Effect.State = std::move(state); + + slot->mPropsDirty = false; + slot->updateProps(context); + AddActiveEffectSlots({&slot, 1}, context); + } + else { - std::lock_guard ___{device->BufferLock}; + auto *device = context->mALDevice.get(); + auto bufferlock = std::unique_lock{device->BufferLock}; ALbuffer *buffer{}; if(value) { buffer = LookupBuffer(device, static_cast(value)); - if(!buffer) return context->setError(AL_INVALID_VALUE, "Invalid buffer ID"); + if(!buffer) + context->throw_error(AL_INVALID_VALUE, "Invalid buffer ID {}", value); if(buffer->mCallback) - return context->setError(AL_INVALID_OPERATION, + context->throw_error(AL_INVALID_OPERATION, "Callback buffer not valid for effects"); IncrementRef(buffer->ref); @@ -646,27 +628,32 @@ FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotiDirect(ALCcontext *context, A if(ALbuffer *oldbuffer{slot->Buffer}) DecrementRef(oldbuffer->ref); slot->Buffer = buffer; + bufferlock.unlock(); FPUCtl mixer_mode{}; auto *state = slot->Effect.State.get(); state->deviceUpdate(device, buffer); + slot->mPropsDirty = true; } - break; + return; case AL_EFFECTSLOT_STATE_SOFT: - return context->setError(AL_INVALID_OPERATION, "AL_EFFECTSLOT_STATE_SOFT is read-only"); - - default: - return context->setError(AL_INVALID_ENUM, "Invalid effect slot integer property 0x%04x", - param); + context->throw_error(AL_INVALID_OPERATION, "AL_EFFECTSLOT_STATE_SOFT is read-only"); } - UpdateProps(slot, context); + + context->throw_error(AL_INVALID_ENUM, "Invalid effect slot integer property {:#04x}", + as_unsigned(param)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC3(void, alAuxiliaryEffectSlotiv, ALuint, ALenum, const ALint*) +AL_API DECL_FUNC3(void, alAuxiliaryEffectSlotiv, ALuint,effectslot, ALenum,param, const ALint*,values) FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotivDirect(ALCcontext *context, ALuint effectslot, ALenum param, const ALint *values) noexcept -{ +try { switch(param) { case AL_EFFECTSLOT_EFFECT: @@ -674,121 +661,134 @@ FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotivDirect(ALCcontext *context, case AL_EFFECTSLOT_TARGET_SOFT: case AL_EFFECTSLOT_STATE_SOFT: case AL_BUFFER: - alAuxiliaryEffectSlotiDirect(context, effectslot, param, values[0]); + alAuxiliaryEffectSlotiDirect(context, effectslot, param, *values); return; } - std::lock_guard _{context->mEffectSlotLock}; - ALeffectslot *slot = LookupEffectSlot(context, effectslot); - if(!slot) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot); + std::lock_guard slotlock{context->mEffectSlotLock}; + ALeffectslot *slot{LookupEffectSlot(context, effectslot)}; + if(!slot) + context->throw_error(AL_INVALID_NAME, "Invalid effect slot ID {}", effectslot); - switch(param) - { - default: - return context->setError(AL_INVALID_ENUM, - "Invalid effect slot integer-vector property 0x%04x", param); - } + context->throw_error(AL_INVALID_ENUM, "Invalid effect slot integer-vector property {:#04x}", + as_unsigned(param)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC3(void, alAuxiliaryEffectSlotf, ALuint, ALenum, ALfloat) +AL_API DECL_FUNC3(void, alAuxiliaryEffectSlotf, ALuint,effectslot, ALenum,param, ALfloat,value) FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotfDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat value) noexcept -{ - std::lock_guard _{context->mPropLock}; - std::lock_guard __{context->mEffectSlotLock}; - ALeffectslot *slot = LookupEffectSlot(context, effectslot); - if(!slot) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot); +try { + std::lock_guard proplock{context->mPropLock}; + std::lock_guard slotlock{context->mEffectSlotLock}; + + ALeffectslot *slot{LookupEffectSlot(context, effectslot)}; + if(!slot) + context->throw_error(AL_INVALID_NAME, "Invalid effect slot ID {}", effectslot); switch(param) { case AL_EFFECTSLOT_GAIN: if(!(value >= 0.0f && value <= 1.0f)) - return context->setError(AL_INVALID_VALUE, "Effect slot gain out of range"); - if(slot->Gain == value) UNLIKELY - return; - slot->Gain = value; - break; - - default: - return context->setError(AL_INVALID_ENUM, "Invalid effect slot float property 0x%04x", - param); + context->throw_error(AL_INVALID_VALUE, "Effect slot gain {} out of range", value); + if(!(slot->Gain == value)) LIKELY + { + slot->Gain = value; + UpdateProps(slot, context); + } + return; } - UpdateProps(slot, context); + + context->throw_error(AL_INVALID_ENUM, "Invalid effect slot float property {:#04x}", + as_unsigned(param)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC3(void, alAuxiliaryEffectSlotfv, ALuint, ALenum, const ALfloat*) +AL_API DECL_FUNC3(void, alAuxiliaryEffectSlotfv, ALuint,effectslot, ALenum,param, const ALfloat*,values) FORCE_ALIGN void AL_APIENTRY alAuxiliaryEffectSlotfvDirect(ALCcontext *context, ALuint effectslot, ALenum param, const ALfloat *values) noexcept -{ +try { switch(param) { case AL_EFFECTSLOT_GAIN: - alAuxiliaryEffectSlotfDirect(context, effectslot, param, values[0]); + alAuxiliaryEffectSlotfDirect(context, effectslot, param, *values); return; } - std::lock_guard _{context->mEffectSlotLock}; - ALeffectslot *slot = LookupEffectSlot(context, effectslot); - if(!slot) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot); + std::lock_guard slotlock{context->mEffectSlotLock}; + ALeffectslot *slot{LookupEffectSlot(context, effectslot)}; + if(!slot) + context->throw_error(AL_INVALID_NAME, "Invalid effect slot ID {}", effectslot); - switch(param) - { - default: - return context->setError(AL_INVALID_ENUM, - "Invalid effect slot float-vector property 0x%04x", param); - } + context->throw_error(AL_INVALID_ENUM, "Invalid effect slot float-vector property {:#04x}", + as_unsigned(param)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSloti, ALuint, ALenum, ALint*) +AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSloti, ALuint,effectslot, ALenum,param, ALint*,value) FORCE_ALIGN void AL_APIENTRY alGetAuxiliaryEffectSlotiDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALint *value) noexcept -{ - std::lock_guard _{context->mEffectSlotLock}; - ALeffectslot *slot = LookupEffectSlot(context, effectslot); - if(!slot) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot); +try { + std::lock_guard slotlock{context->mEffectSlotLock}; + ALeffectslot *slot{LookupEffectSlot(context, effectslot)}; + if(!slot) + context->throw_error(AL_INVALID_NAME, "Invalid effect slot ID {}", effectslot); switch(param) { case AL_EFFECTSLOT_EFFECT: *value = static_cast(slot->EffectId); - break; + return; case AL_EFFECTSLOT_AUXILIARY_SEND_AUTO: *value = slot->AuxSendAuto ? AL_TRUE : AL_FALSE; - break; + return; case AL_EFFECTSLOT_TARGET_SOFT: if(auto *target = slot->Target) *value = static_cast(target->id); else *value = 0; - break; + return; case AL_EFFECTSLOT_STATE_SOFT: *value = static_cast(slot->mState); - break; + return; case AL_BUFFER: if(auto *buffer = slot->Buffer) *value = static_cast(buffer->id); else *value = 0; - break; - - default: - context->setError(AL_INVALID_ENUM, "Invalid effect slot integer property 0x%04x", param); + return; } + + context->throw_error(AL_INVALID_ENUM, "Invalid effect slot integer property {:#04x}", + as_unsigned(param)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSlotiv, ALuint, ALenum, ALint*) +AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSlotiv, ALuint,effectslot, ALenum,param, ALint*,values) FORCE_ALIGN void AL_APIENTRY alGetAuxiliaryEffectSlotivDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALint *values) noexcept -{ +try { switch(param) { case AL_EFFECTSLOT_EFFECT: @@ -800,43 +800,47 @@ FORCE_ALIGN void AL_APIENTRY alGetAuxiliaryEffectSlotivDirect(ALCcontext *contex return; } - std::lock_guard _{context->mEffectSlotLock}; + std::lock_guard slotlock{context->mEffectSlotLock}; ALeffectslot *slot = LookupEffectSlot(context, effectslot); - if(!slot) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot); + if(!slot) + context->throw_error(AL_INVALID_NAME, "Invalid effect slot ID {}", effectslot); - switch(param) - { - default: - context->setError(AL_INVALID_ENUM, "Invalid effect slot integer-vector property 0x%04x", - param); - } + context->throw_error(AL_INVALID_ENUM, "Invalid effect slot integer-vector property {:#04x}", + as_unsigned(param)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSlotf, ALuint, ALenum, ALfloat*) +AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSlotf, ALuint,effectslot, ALenum,param, ALfloat*,value) FORCE_ALIGN void AL_APIENTRY alGetAuxiliaryEffectSlotfDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat *value) noexcept -{ - std::lock_guard _{context->mEffectSlotLock}; - ALeffectslot *slot = LookupEffectSlot(context, effectslot); - if(!slot) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot); +try { + std::lock_guard slotlock{context->mEffectSlotLock}; + ALeffectslot *slot{LookupEffectSlot(context, effectslot)}; + if(!slot) + context->throw_error(AL_INVALID_NAME, "Invalid effect slot ID {}", effectslot); switch(param) { - case AL_EFFECTSLOT_GAIN: - *value = slot->Gain; - break; - - default: - context->setError(AL_INVALID_ENUM, "Invalid effect slot float property 0x%04x", param); + case AL_EFFECTSLOT_GAIN: *value = slot->Gain; return; } + + context->throw_error(AL_INVALID_ENUM, "Invalid effect slot float property {:#04x}", + as_unsigned(param)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSlotfv, ALuint, ALenum, ALfloat*) +AL_API DECL_FUNC3(void, alGetAuxiliaryEffectSlotfv, ALuint,effectslot, ALenum,param, ALfloat*,values) FORCE_ALIGN void AL_APIENTRY alGetAuxiliaryEffectSlotfvDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat *values) noexcept -{ +try { switch(param) { case AL_EFFECTSLOT_GAIN: @@ -844,17 +848,18 @@ FORCE_ALIGN void AL_APIENTRY alGetAuxiliaryEffectSlotfvDirect(ALCcontext *contex return; } - std::lock_guard _{context->mEffectSlotLock}; - ALeffectslot *slot = LookupEffectSlot(context, effectslot); - if(!slot) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", effectslot); + std::lock_guard slotlock{context->mEffectSlotLock}; + ALeffectslot *slot{LookupEffectSlot(context, effectslot)}; + if(!slot) + context->throw_error(AL_INVALID_NAME, "Invalid effect slot ID {}", effectslot); - switch(param) - { - default: - context->setError(AL_INVALID_ENUM, "Invalid effect slot float-vector property 0x%04x", - param); - } + context->throw_error(AL_INVALID_ENUM, "Invalid effect slot float-vector property {:#04x}", + as_unsigned(param)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } @@ -880,12 +885,8 @@ ALeffectslot::~ALeffectslot() DecrementRef(Buffer->ref); Buffer = nullptr; - if(EffectSlotProps *props{mSlot->Update.exchange(nullptr)}) - { - TRACE("Freed unapplied AuxiliaryEffectSlot update %p\n", - decltype(std::declval()){props}); - delete props; - } + if(auto *slot = mSlot->Update.exchange(nullptr, std::memory_order_relaxed)) + slot->State = nullptr; mSlot->mEffectState = nullptr; mSlot->InUse = false; @@ -900,13 +901,13 @@ ALenum ALeffectslot::initEffect(ALuint effectId, ALenum effectType, const Effect EffectStateFactory *factory{getFactoryByType(newtype)}; if(!factory) { - ERR("Failed to find factory for effect slot type %d\n", static_cast(newtype)); + ERR("Failed to find factory for effect slot type {}", + int{al::to_underlying(newtype)}); return AL_INVALID_ENUM; } al::intrusive_ptr state{factory->create()}; - ALCdevice *device{context->mALDevice.get()}; - std::unique_lock statelock{device->StateLock}; + auto *device = context->mALDevice.get(); state->mOutTarget = device->Dry.Buffer; { FPUCtl mixer_mode{}; @@ -923,7 +924,7 @@ ALenum ALeffectslot::initEffect(ALuint effectId, ALenum effectType, const Effect EffectId = effectId; /* Remove state references from old effect slot property updates. */ - EffectSlotProps *props{context->mFreeEffectslotProps.load()}; + EffectSlotProps *props{context->mFreeEffectSlotProps.load()}; while(props) { props->State = nullptr; @@ -933,20 +934,20 @@ ALenum ALeffectslot::initEffect(ALuint effectId, ALenum effectType, const Effect return AL_NO_ERROR; } -void ALeffectslot::updateProps(ALCcontext *context) +void ALeffectslot::updateProps(ALCcontext *context) const { /* Get an unused property container, or allocate a new one as needed. */ - EffectSlotProps *props{context->mFreeEffectslotProps.load(std::memory_order_relaxed)}; + EffectSlotProps *props{context->mFreeEffectSlotProps.load(std::memory_order_acquire)}; if(!props) - props = new EffectSlotProps{}; - else { - EffectSlotProps *next; - do { - next = props->next.load(std::memory_order_relaxed); - } while(context->mFreeEffectslotProps.compare_exchange_weak(props, next, - std::memory_order_seq_cst, std::memory_order_acquire) == 0); + context->allocEffectSlotProps(); + props = context->mFreeEffectSlotProps.load(std::memory_order_acquire); } + EffectSlotProps *next; + do { + next = props->next.load(std::memory_order_relaxed); + } while(!context->mFreeEffectSlotProps.compare_exchange_weak(props, next, + std::memory_order_acq_rel, std::memory_order_acquire)); /* Copy in current property values. */ props->Gain = Gain; @@ -965,35 +966,35 @@ void ALeffectslot::updateProps(ALCcontext *context) * freelist. */ props->State = nullptr; - AtomicReplaceHead(context->mFreeEffectslotProps, props); + AtomicReplaceHead(context->mFreeEffectSlotProps, props); } } void ALeffectslot::SetName(ALCcontext* context, ALuint id, std::string_view name) { - std::lock_guard _{context->mEffectSlotLock}; + std::lock_guard slotlock{context->mEffectSlotLock}; auto slot = LookupEffectSlot(context, id); - if(!slot) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid effect slot ID %u", id); + if(!slot) + context->throw_error(AL_INVALID_NAME, "Invalid effect slot ID {}", id); context->mEffectSlotNames.insert_or_assign(id, name); } void UpdateAllEffectSlotProps(ALCcontext *context) { - std::lock_guard _{context->mEffectSlotLock}; + std::lock_guard slotlock{context->mEffectSlotLock}; for(auto &sublist : context->mEffectSlotList) { uint64_t usemask{~sublist.FreeMask}; while(usemask) { - const int idx{al::countr_zero(usemask)}; + const auto idx = static_cast(al::countr_zero(usemask)); usemask &= ~(1_u64 << idx); - ALeffectslot *slot{sublist.EffectSlots + idx}; + auto &slot = (*sublist.EffectSlots)[idx]; - if(slot->mState != SlotState::Stopped && std::exchange(slot->mPropsDirty, false)) - slot->updateProps(context); + if(std::exchange(slot.mPropsDirty, false)) + slot.updateProps(context); } } } @@ -1007,15 +1008,15 @@ EffectSlotSubList::~EffectSlotSubList() while(usemask) { const int idx{al::countr_zero(usemask)}; - std::destroy_at(EffectSlots+idx); + std::destroy_at(al::to_address(EffectSlots->begin() + idx)); usemask &= ~(1_u64 << idx); } FreeMask = ~usemask; - al_free(EffectSlots); + SubListAllocator{}.deallocate(EffectSlots, 1); EffectSlots = nullptr; } -#ifdef ALSOFT_EAX +#if ALSOFT_EAX void ALeffectslot::eax_initialize(ALCcontext& al_context, EaxFxSlotIndexValue index) { if(index >= EAX_MAX_FXSLOTS) @@ -1162,7 +1163,7 @@ void ALeffectslot::eax_fx_slot_set_defaults() eax_df_ = EaxDirtyFlags{}; } -void ALeffectslot::eax4_fx_slot_get(const EaxCall& call, const Eax4Props& props) const +void ALeffectslot::eax4_fx_slot_get(const EaxCall& call, const Eax4Props& props) { switch(call.get_property_id()) { @@ -1186,7 +1187,7 @@ void ALeffectslot::eax4_fx_slot_get(const EaxCall& call, const Eax4Props& props) } } -void ALeffectslot::eax5_fx_slot_get(const EaxCall& call, const Eax5Props& props) const +void ALeffectslot::eax5_fx_slot_get(const EaxCall& call, const Eax5Props& props) { switch(call.get_property_id()) { @@ -1296,15 +1297,12 @@ void ALeffectslot::eax5_fx_slot_set_all(const EaxCall& call) bool ALeffectslot::eax_fx_slot_should_update_sources() const noexcept { - const auto dirty_bits = + static constexpr auto dirty_bits = eax_occlusion_dirty_bit | eax_occlusion_lf_ratio_dirty_bit | eax_flags_dirty_bit; - if((eax_df_ & dirty_bits) != EaxDirtyFlags{}) - return true; - - return false; + return (eax_df_ & dirty_bits) != EaxDirtyFlags{}; } // Returns `true` if all sources should be updated, or `false` otherwise. @@ -1453,8 +1451,9 @@ void ALeffectslot::eax_set_efx_slot_effect(EaxEffect &effect) const auto error = initEffect(0, effect.al_effect_type_, effect.al_effect_props_, eax_al_context_); - if(error != AL_NO_ERROR) { - ERR(EAX_PREFIX "%s\n", "Failed to initialize an effect."); + if(error != AL_NO_ERROR) + { + ERR(EAX_PREFIX "Failed to initialize an effect."); return; } @@ -1488,9 +1487,9 @@ void ALeffectslot::eax_set_efx_slot_gain(ALfloat gain) if(gain == Gain) return; if(gain < 0.0f || gain > 1.0f) - ERR(EAX_PREFIX "Gain out of range (%f)\n", gain); + ERR(EAX_PREFIX "Slot gain out of range ({:f})", gain); - Gain = clampf(gain, 0.0f, 1.0f); + Gain = std::clamp(gain, 0.0f, 1.0f); mPropsDirty = true; #undef EAX_PREFIX @@ -1498,7 +1497,6 @@ void ALeffectslot::eax_set_efx_slot_gain(ALfloat gain) void ALeffectslot::EaxDeleter::operator()(ALeffectslot* effect_slot) { - assert(effect_slot); eax_delete_al_effect_slot(*effect_slot->eax_al_context_, *effect_slot); } @@ -1506,16 +1504,18 @@ EaxAlEffectSlotUPtr eax_create_al_effect_slot(ALCcontext& context) { #define EAX_PREFIX "[EAX_MAKE_EFFECT_SLOT] " - std::unique_lock effect_slot_lock{context.mEffectSlotLock}; + std::lock_guard slotlock{context.mEffectSlotLock}; auto& device = *context.mALDevice; - if(context.mNumEffectSlots == device.AuxiliaryEffectSlotMax) { - ERR(EAX_PREFIX "%s\n", "Out of memory."); + if(context.mNumEffectSlots == device.AuxiliaryEffectSlotMax) + { + ERR(EAX_PREFIX "Out of memory."); return nullptr; } - if(!EnsureEffectSlots(&context, 1)) { - ERR(EAX_PREFIX "%s\n", "Failed to ensure."); + if(!EnsureEffectSlots(&context, 1)) + { + ERR(EAX_PREFIX "Failed to ensure."); return nullptr; } @@ -1528,10 +1528,11 @@ void eax_delete_al_effect_slot(ALCcontext& context, ALeffectslot& effect_slot) { #define EAX_PREFIX "[EAX_DELETE_EFFECT_SLOT] " - std::lock_guard effect_slot_lock{context.mEffectSlotLock}; + std::lock_guard slotlock{context.mEffectSlotLock}; - if(ReadRef(effect_slot.ref) != 0) { - ERR(EAX_PREFIX "Deleting in-use effect slot %u.\n", effect_slot.id); + if(effect_slot.ref.load(std::memory_order_relaxed) != 0) + { + ERR(EAX_PREFIX "Deleting in-use effect slot {}.", effect_slot.id); return; } diff --git a/3rdparty/openal/al/auxeffectslot.h b/3rdparty/openal/al/auxeffectslot.h index 1ad0ffc4d549..62e6e3a8abcd 100644 --- a/3rdparty/openal/al/auxeffectslot.h +++ b/3rdparty/openal/al/auxeffectslot.h @@ -1,24 +1,26 @@ #ifndef AL_AUXEFFECTSLOT_H #define AL_AUXEFFECTSLOT_H +#include "config.h" + +#include #include -#include +#include #include +#include #include "AL/al.h" #include "AL/alc.h" -#include "AL/efx.h" -#include "alc/device.h" -#include "alc/effects/base.h" #include "almalloc.h" -#include "atomic.h" +#include "alnumeric.h" +#include "core/effects/base.h" #include "core/effectslot.h" #include "intrusive_ptr.h" -#include "vector.h" -#ifdef ALSOFT_EAX +#if ALSOFT_EAX #include +#include "eax/api.h" #include "eax/call.h" #include "eax/effect.h" #include "eax/exception.h" @@ -27,10 +29,8 @@ #endif // ALSOFT_EAX struct ALbuffer; -struct ALeffect; -struct WetBuffer; -#ifdef ALSOFT_EAX +#if ALSOFT_EAX class EaxFxSlotException : public EaxException { public: explicit EaxFxSlotException(const char* message) @@ -39,10 +39,8 @@ class EaxFxSlotException : public EaxException { }; #endif // ALSOFT_EAX -enum class SlotState : ALenum { - Initial = AL_INITIAL, - Playing = AL_PLAYING, - Stopped = AL_STOPPED, +enum class SlotState : bool { + Initial, Playing, }; struct ALeffectslot { @@ -52,49 +50,47 @@ struct ALeffectslot { ALeffectslot *Target{nullptr}; ALbuffer *Buffer{nullptr}; - struct { + struct EffectData { EffectSlotType Type{EffectSlotType::None}; - EffectProps Props{}; + EffectProps Props; al::intrusive_ptr State; - } Effect; + }; + EffectData Effect; bool mPropsDirty{true}; SlotState mState{SlotState::Initial}; - RefCount ref{0u}; + std::atomic ref{0u}; EffectSlot *mSlot{nullptr}; /* Self ID */ ALuint id{}; - ALeffectslot(ALCcontext *context); + explicit ALeffectslot(ALCcontext *context); ALeffectslot(const ALeffectslot&) = delete; ALeffectslot& operator=(const ALeffectslot&) = delete; ~ALeffectslot(); ALenum initEffect(ALuint effectId, ALenum effectType, const EffectProps &effectProps, ALCcontext *context); - void updateProps(ALCcontext *context); + void updateProps(ALCcontext *context) const; static void SetName(ALCcontext *context, ALuint id, std::string_view name); - /* This can be new'd for the context's default effect slot. */ - DEF_NEWDEL(ALeffectslot) - -#ifdef ALSOFT_EAX +#if ALSOFT_EAX public: void eax_initialize(ALCcontext& al_context, EaxFxSlotIndexValue index); - EaxFxSlotIndexValue eax_get_index() const noexcept { return eax_fx_slot_index_; } - const EAX50FXSLOTPROPERTIES& eax_get_eax_fx_slot() const noexcept + [[nodiscard]] auto eax_get_index() const noexcept -> EaxFxSlotIndexValue { return eax_fx_slot_index_; } + [[nodiscard]] auto eax_get_eax_fx_slot() const noexcept -> const EAX50FXSLOTPROPERTIES& { return eax_; } // Returns `true` if all sources should be updated, or `false` otherwise. - bool eax_dispatch(const EaxCall& call) + [[nodiscard]] auto eax_dispatch(const EaxCall& call) -> bool { return call.is_get() ? eax_get(call) : eax_set(call); } void eax_commit(); @@ -198,6 +194,17 @@ struct ALeffectslot { } }; + struct Eax5FlagsValidator { + void operator()(unsigned long ulFlags) const + { + EaxRangeValidator{}( + "Flags", + ulFlags, + 0UL, + ~EAX50FXSLOTFLAGS_RESERVED); + } + }; + struct Eax5OcclusionValidator { void operator()(long lOcclusion) const { @@ -220,21 +227,13 @@ struct ALeffectslot { } }; - struct Eax5FlagsValidator { - void operator()(unsigned long ulFlags) const - { - EaxRangeValidator{}( - "Flags", - ulFlags, - 0UL, - ~EAX50FXSLOTFLAGS_RESERVED); - } - }; - struct Eax5AllValidator { void operator()(const EAX50FXSLOTPROPERTIES& all) const { - Eax4AllValidator{}(static_cast(all)); + Eax4GuidLoadEffectValidator{}(all.guidLoadEffect); + Eax4VolumeValidator{}(all.lVolume); + Eax4LockValidator{}(all.lLock); + Eax5FlagsValidator{}(all.ulFlags); Eax5OcclusionValidator{}(all.lOcclusion); Eax5OcclusionLfRatioValidator{}(all.flOcclusionLFRatio); } @@ -244,7 +243,7 @@ struct ALeffectslot { EaxFxSlotIndexValue eax_fx_slot_index_{}; int eax_version_{}; // Current EAX version. EaxDirtyFlags eax_df_{}; // Dirty flags for the current EAX version. - EaxEffectUPtr eax_effect_{}; + EaxEffectUPtr eax_effect_; Eax5State eax123_{}; // EAX1/EAX2/EAX3 state. Eax4State eax4_{}; // EAX4 state. Eax5State eax5_{}; // EAX5 state. @@ -282,14 +281,14 @@ struct ALeffectslot { dst = src; } - constexpr bool eax4_fx_slot_is_legacy() const noexcept + [[nodiscard]] constexpr auto eax4_fx_slot_is_legacy() const noexcept -> bool { return eax_fx_slot_index_ < 2; } void eax4_fx_slot_ensure_unlocked() const; - static ALenum eax_get_efx_effect_type(const GUID& guid); - const GUID& eax_get_eax_default_effect_guid() const noexcept; - long eax_get_eax_default_lock() const noexcept; + [[nodiscard]] static auto eax_get_efx_effect_type(const GUID& guid) -> ALenum; + [[nodiscard]] auto eax_get_eax_default_effect_guid() const noexcept -> const GUID&; + [[nodiscard]] auto eax_get_eax_default_lock() const noexcept -> long; void eax4_fx_slot_set_defaults(Eax4Props& props) noexcept; void eax5_fx_slot_set_defaults(Eax5Props& props) noexcept; @@ -298,8 +297,8 @@ struct ALeffectslot { void eax_fx_slot_set_current_defaults(); void eax_fx_slot_set_defaults(); - void eax4_fx_slot_get(const EaxCall& call, const Eax4Props& props) const; - void eax5_fx_slot_get(const EaxCall& call, const Eax5Props& props) const; + static void eax4_fx_slot_get(const EaxCall& call, const Eax4Props& props); + static void eax5_fx_slot_get(const EaxCall& call, const Eax5Props& props); void eax_fx_slot_get(const EaxCall& call) const; // Returns `true` if all sources should be updated, or `false` otherwise. bool eax_get(const EaxCall& call); @@ -312,7 +311,7 @@ struct ALeffectslot { void eax4_fx_slot_set_all(const EaxCall& call); void eax5_fx_slot_set_all(const EaxCall& call); - bool eax_fx_slot_should_update_sources() const noexcept; + [[nodiscard]] auto eax_fx_slot_should_update_sources() const noexcept -> bool; // Returns `true` if all sources should be updated, or `false` otherwise. bool eax4_fx_slot_set(const EaxCall& call); @@ -363,11 +362,27 @@ struct ALeffectslot { void UpdateAllEffectSlotProps(ALCcontext *context); -#ifdef ALSOFT_EAX +#if ALSOFT_EAX using EaxAlEffectSlotUPtr = std::unique_ptr; EaxAlEffectSlotUPtr eax_create_al_effect_slot(ALCcontext& context); void eax_delete_al_effect_slot(ALCcontext& context, ALeffectslot& effect_slot); #endif // ALSOFT_EAX +struct EffectSlotSubList { + uint64_t FreeMask{~0_u64}; + gsl::owner*> EffectSlots{nullptr}; + + EffectSlotSubList() noexcept = default; + EffectSlotSubList(const EffectSlotSubList&) = delete; + EffectSlotSubList(EffectSlotSubList&& rhs) noexcept + : FreeMask{rhs.FreeMask}, EffectSlots{rhs.EffectSlots} + { rhs.FreeMask = ~0_u64; rhs.EffectSlots = nullptr; } + ~EffectSlotSubList(); + + EffectSlotSubList& operator=(const EffectSlotSubList&) = delete; + EffectSlotSubList& operator=(EffectSlotSubList&& rhs) noexcept + { std::swap(FreeMask, rhs.FreeMask); std::swap(EffectSlots, rhs.EffectSlots); return *this; } +}; + #endif diff --git a/3rdparty/openal/al/buffer.cpp b/3rdparty/openal/al/buffer.cpp index 8ba874e4dd98..db890e92e6ba 100644 --- a/3rdparty/openal/al/buffer.cpp +++ b/3rdparty/openal/al/buffer.cpp @@ -28,16 +28,15 @@ #include #include #include -#include #include #include #include #include #include -#include #include #include #include +#include #include #include @@ -51,14 +50,17 @@ #include "alc/inprogext.h" #include "almalloc.h" #include "alnumeric.h" -#include "atomic.h" +#include "alspan.h" +#include "core/device.h" #include "core/except.h" #include "core/logging.h" +#include "core/resampler_limits.h" #include "core/voice.h" #include "direct_defs.h" +#include "intrusive_ptr.h" #include "opthelpers.h" -#ifdef ALSOFT_EAX +#if ALSOFT_EAX #include #include "eax/globals.h" @@ -68,7 +70,9 @@ namespace { -std::optional AmbiLayoutFromEnum(ALenum layout) +using SubListAllocator = al::allocator>; + +constexpr auto AmbiLayoutFromEnum(ALenum layout) noexcept -> std::optional { switch(layout) { @@ -77,17 +81,18 @@ std::optional AmbiLayoutFromEnum(ALenum layout) } return std::nullopt; } -ALenum EnumFromAmbiLayout(AmbiLayout layout) +constexpr auto EnumFromAmbiLayout(AmbiLayout layout) -> ALenum { switch(layout) { case AmbiLayout::FuMa: return AL_FUMA_SOFT; case AmbiLayout::ACN: return AL_ACN_SOFT; } - throw std::runtime_error{"Invalid AmbiLayout: "+std::to_string(int(layout))}; + throw std::runtime_error{fmt::format("Invalid AmbiLayout: {}", + int{al::to_underlying(layout)})}; } -std::optional AmbiScalingFromEnum(ALenum scale) +constexpr auto AmbiScalingFromEnum(ALenum scale) noexcept -> std::optional { switch(scale) { @@ -97,7 +102,7 @@ std::optional AmbiScalingFromEnum(ALenum scale) } return std::nullopt; } -ALenum EnumFromAmbiScaling(AmbiScaling scale) +constexpr auto EnumFromAmbiScaling(AmbiScaling scale) -> ALenum { switch(scale) { @@ -106,11 +111,12 @@ ALenum EnumFromAmbiScaling(AmbiScaling scale) case AmbiScaling::N3D: return AL_N3D_SOFT; case AmbiScaling::UHJ: break; } - throw std::runtime_error{"Invalid AmbiScaling: "+std::to_string(int(scale))}; + throw std::runtime_error{fmt::format("Invalid AmbiScaling: {}", + int{al::to_underlying(scale)})}; } -#ifdef ALSOFT_EAX -std::optional EaxStorageFromEnum(ALenum scale) +#if ALSOFT_EAX +constexpr auto EaxStorageFromEnum(ALenum scale) noexcept -> std::optional { switch(scale) { @@ -120,7 +126,7 @@ std::optional EaxStorageFromEnum(ALenum scale) } return std::nullopt; } -ALenum EnumFromEaxStorage(EaxStorage storage) +constexpr auto EnumFromEaxStorage(EaxStorage storage) -> ALenum { switch(storage) { @@ -128,12 +134,13 @@ ALenum EnumFromEaxStorage(EaxStorage storage) case EaxStorage::Accessible: return AL_STORAGE_ACCESSIBLE; case EaxStorage::Hardware: return AL_STORAGE_HARDWARE; } - throw std::runtime_error{"Invalid EaxStorage: "+std::to_string(int(storage))}; + throw std::runtime_error{fmt::format("Invalid EaxStorage: {}", + int{al::to_underlying(storage)})}; } -bool eax_x_ram_check_availability(const ALCdevice &device, const ALbuffer &buffer, - const ALuint newsize) noexcept +auto eax_x_ram_check_availability(const al::Device &device, const ALbuffer &buffer, + const ALuint newsize) noexcept -> bool { ALuint freemem{device.eax_x_ram_free_size}; /* If the buffer is currently in "hardware", add its memory to the free @@ -144,7 +151,7 @@ bool eax_x_ram_check_availability(const ALCdevice &device, const ALbuffer &buffe return freemem >= newsize; } -void eax_x_ram_apply(ALCdevice &device, ALbuffer &buffer) noexcept +void eax_x_ram_apply(al::Device &device, ALbuffer &buffer) noexcept { if(buffer.eax_x_ram_is_hardware) return; @@ -156,7 +163,7 @@ void eax_x_ram_apply(ALCdevice &device, ALbuffer &buffer) noexcept } } -void eax_x_ram_clear(ALCdevice& al_device, ALbuffer& al_buffer) +void eax_x_ram_clear(al::Device& al_device, ALbuffer& al_buffer) noexcept { if(al_buffer.eax_x_ram_is_hardware) al_device.eax_x_ram_free_size += al_buffer.OriginalSize; @@ -172,8 +179,9 @@ constexpr ALbitfieldSOFT INVALID_MAP_FLAGS{~unsigned(AL_MAP_READ_BIT_SOFT | AL_M AL_MAP_PERSISTENT_BIT_SOFT)}; -bool EnsureBuffers(ALCdevice *device, size_t needed) -{ +[[nodiscard]] +auto EnsureBuffers(al::Device *device, size_t needed) noexcept -> bool +try { size_t count{std::accumulate(device->BufferList.cbegin(), device->BufferList.cend(), 0_uz, [](size_t cur, const BufferSubList &sublist) noexcept -> size_t { return cur + static_cast(al::popcount(sublist.FreeMask)); })}; @@ -183,21 +191,20 @@ bool EnsureBuffers(ALCdevice *device, size_t needed) if(device->BufferList.size() >= 1<<25) UNLIKELY return false; - device->BufferList.emplace_back(); - auto sublist = device->BufferList.end() - 1; - sublist->FreeMask = ~0_u64; - sublist->Buffers = static_cast(al_calloc(alignof(ALbuffer), sizeof(ALbuffer)*64)); - if(!sublist->Buffers) UNLIKELY - { - device->BufferList.pop_back(); - return false; - } - count += 64; + BufferSubList sublist{}; + sublist.FreeMask = ~0_u64; + sublist.Buffers = SubListAllocator{}.allocate(1); + device->BufferList.emplace_back(std::move(sublist)); + count += std::tuple_size_v; } return true; } +catch(...) { + return false; +} -ALbuffer *AllocBuffer(ALCdevice *device) +[[nodiscard]] +auto AllocBuffer(al::Device *device) noexcept -> ALbuffer* { auto sublist = std::find_if(device->BufferList.begin(), device->BufferList.end(), [](const BufferSubList &entry) noexcept -> bool @@ -206,7 +213,7 @@ ALbuffer *AllocBuffer(ALCdevice *device) auto slidx = static_cast(al::countr_zero(sublist->FreeMask)); ASSUME(slidx < 64); - ALbuffer *buffer{al::construct_at(sublist->Buffers + slidx)}; + ALbuffer *buffer{al::construct_at(al::to_address(sublist->Buffers->begin() + slidx))}; /* Add 1 to avoid buffer ID 0. */ buffer->id = ((lidx<<6) | slidx) + 1; @@ -216,9 +223,9 @@ ALbuffer *AllocBuffer(ALCdevice *device) return buffer; } -void FreeBuffer(ALCdevice *device, ALbuffer *buffer) +void FreeBuffer(al::Device *device, ALbuffer *buffer) { -#ifdef ALSOFT_EAX +#if ALSOFT_EAX eax_x_ram_clear(*device, *buffer); #endif // ALSOFT_EAX @@ -233,7 +240,8 @@ void FreeBuffer(ALCdevice *device, ALbuffer *buffer) device->BufferList[lidx].FreeMask |= 1_u64 << slidx; } -inline ALbuffer *LookupBuffer(ALCdevice *device, ALuint id) +[[nodiscard]] +auto LookupBuffer(al::Device *device, ALuint id) noexcept -> ALbuffer* { const size_t lidx{(id-1) >> 6}; const ALuint slidx{(id-1) & 0x3f}; @@ -243,11 +251,11 @@ inline ALbuffer *LookupBuffer(ALCdevice *device, ALuint id) BufferSubList &sublist = device->BufferList[lidx]; if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY return nullptr; - return sublist.Buffers + slidx; + return al::to_address(sublist.Buffers->begin() + slidx); } - -ALuint SanitizeAlignment(FmtType type, ALuint align) +[[nodiscard]] +constexpr auto SanitizeAlignment(FmtType type, ALuint align) noexcept -> ALuint { if(align == 0) { @@ -267,47 +275,58 @@ ALuint SanitizeAlignment(FmtType type, ALuint align) if(type == FmtIMA4) { /* IMA4 block alignment must be a multiple of 8, plus 1. */ - if((align&7) == 1) return static_cast(align); + if((align&7) == 1) return align; return 0; } if(type == FmtMSADPCM) { /* MSADPCM block alignment must be a multiple of 2. */ - if((align&1) == 0) return static_cast(align); + if((align&1) == 0) return align; return 0; } - return static_cast(align); + return align; } /** Loads the specified data into the buffer, using the specified format. */ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size, - const FmtChannels DstChannels, const FmtType DstType, const std::byte *SrcData, + const FmtChannels DstChannels, const FmtType DstType, const al::span SrcData, ALbitfieldSOFT access) { - if(ReadRef(ALBuf->ref) != 0 || ALBuf->MappedAccess != 0) UNLIKELY - return context->setError(AL_INVALID_OPERATION, "Modifying storage for in-use buffer %u", + if(ALBuf->ref.load(std::memory_order_relaxed) != 0 || ALBuf->MappedAccess != 0) + context->throw_error(AL_INVALID_OPERATION, "Modifying storage for in-use buffer {}", ALBuf->id); const ALuint unpackalign{ALBuf->UnpackAlign}; const ALuint align{SanitizeAlignment(DstType, unpackalign)}; - if(align < 1) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Invalid unpack alignment %u for %s samples", + if(align < 1) + context->throw_error(AL_INVALID_VALUE, "Invalid unpack alignment {} for {} samples", unpackalign, NameFromFormat(DstType)); const ALuint ambiorder{IsBFormat(DstChannels) ? ALBuf->UnpackAmbiOrder : (IsUHJ(DstChannels) ? 1 : 0)}; + if(ambiorder > 3) + { + if(ALBuf->mAmbiLayout == AmbiLayout::FuMa) + context->throw_error(AL_INVALID_OPERATION, + "Cannot load {}{} order B-Format data with FuMa layout", ALBuf->mAmbiOrder, + GetCounterSuffix(ALBuf->mAmbiOrder)); + if(ALBuf->mAmbiScaling == AmbiScaling::FuMa) + context->throw_error(AL_INVALID_OPERATION, + "Cannot load {}{} order B-Format data with FuMa scaling", ALBuf->mAmbiOrder, + GetCounterSuffix(ALBuf->mAmbiOrder)); + } if((access&AL_PRESERVE_DATA_BIT_SOFT)) { /* Can only preserve data with the same format and alignment. */ - if(ALBuf->mChannels != DstChannels || ALBuf->mType != DstType) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Preserving data of mismatched format"); - if(ALBuf->mBlockAlign != align) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Preserving data of mismatched alignment"); - if(ALBuf->mAmbiOrder != ambiorder) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Preserving data of mismatched order"); + if(ALBuf->mChannels != DstChannels || ALBuf->mType != DstType) + context->throw_error(AL_INVALID_VALUE, "Preserving data of mismatched format"); + if(ALBuf->mBlockAlign != align) + context->throw_error(AL_INVALID_VALUE, "Preserving data of mismatched alignment"); + if(ALBuf->mAmbiOrder != ambiorder) + context->throw_error(AL_INVALID_VALUE, "Preserving data of mismatched order"); } /* Convert the size in bytes to blocks using the unpack block alignment. */ @@ -316,28 +335,28 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size, ((DstType == FmtIMA4) ? (align-1)/2 + 4 : (DstType == FmtMSADPCM) ? (align-2)/2 + 7 : (align * BytesFromFmt(DstType)))}; - if((size%BlockSize) != 0) UNLIKELY - return context->setError(AL_INVALID_VALUE, - "Data size %d is not a multiple of frame size %d (%d unpack alignment)", + if((size%BlockSize) != 0) + context->throw_error(AL_INVALID_VALUE, + "Data size {} is not a multiple of frame size {} ({} unpack alignment)", size, BlockSize, align); const ALuint blocks{size / BlockSize}; - if(blocks > std::numeric_limits::max()/align) UNLIKELY - return context->setError(AL_OUT_OF_MEMORY, - "Buffer size overflow, %d blocks x %d samples per block", blocks, align); - if(blocks > std::numeric_limits::max()/BlockSize) UNLIKELY - return context->setError(AL_OUT_OF_MEMORY, - "Buffer size overflow, %d frames x %d bytes per frame", blocks, BlockSize); + if(blocks > std::numeric_limits::max()/align) + context->throw_error(AL_OUT_OF_MEMORY, + "Buffer size overflow, {} blocks x {} samples per block", blocks, align); + if(blocks > std::numeric_limits::max()/BlockSize) + context->throw_error(AL_OUT_OF_MEMORY, + "Buffer size overflow, {} frames x {} bytes per frame", blocks, BlockSize); const size_t newsize{static_cast(blocks) * BlockSize}; -#ifdef ALSOFT_EAX +#if ALSOFT_EAX if(ALBuf->eax_x_ram_mode == EaxStorage::Hardware) { - ALCdevice &device = *context->mALDevice; + auto &device = *context->mALDevice; if(!eax_x_ram_check_availability(device, *ALBuf, size)) - return context->setError(AL_OUT_OF_MEMORY, - "Out of X-RAM memory (avail: %u, needed: %u)", device.eax_x_ram_free_size, size); + context->throw_error(AL_OUT_OF_MEMORY, "Out of X-RAM memory (avail: {}, needed: {})", + device.eax_x_ram_free_size, size); } #endif @@ -352,18 +371,18 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size, auto newdata = decltype(ALBuf->mDataStorage)(newsize, std::byte{}); if((access&AL_PRESERVE_DATA_BIT_SOFT)) { - const size_t tocopy{minz(newdata.size(), ALBuf->mDataStorage.size())}; + const size_t tocopy{std::min(newdata.size(), ALBuf->mDataStorage.size())}; std::copy_n(ALBuf->mDataStorage.begin(), tocopy, newdata.begin()); } newdata.swap(ALBuf->mDataStorage); } ALBuf->mData = ALBuf->mDataStorage; -#ifdef ALSOFT_EAX +#if ALSOFT_EAX eax_x_ram_clear(*context->mALDevice, *ALBuf); #endif - if(SrcData != nullptr && !ALBuf->mData.empty()) - std::copy_n(SrcData, blocks*BlockSize, ALBuf->mData.begin()); + if(!SrcData.empty() && !ALBuf->mData.empty()) + std::copy_n(SrcData.begin(), blocks*BlockSize, ALBuf->mData.begin()); ALBuf->mBlockAlign = (DstType == FmtIMA4 || DstType == FmtMSADPCM) ? align : 1; ALBuf->OriginalSize = size; @@ -382,7 +401,7 @@ void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size, ALBuf->mLoopStart = 0; ALBuf->mLoopEnd = ALBuf->mSampleLen; -#ifdef ALSOFT_EAX +#if ALSOFT_EAX if(eax_g_is_enabled && ALBuf->eax_x_ram_mode == EaxStorage::Hardware) eax_x_ram_apply(*context->mALDevice, *ALBuf); #endif @@ -393,8 +412,8 @@ void PrepareCallback(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, const FmtChannels DstChannels, const FmtType DstType, ALBUFFERCALLBACKTYPESOFT callback, void *userptr) { - if(ReadRef(ALBuf->ref) != 0 || ALBuf->MappedAccess != 0) UNLIKELY - return context->setError(AL_INVALID_OPERATION, "Modifying callback for in-use buffer %u", + if(ALBuf->ref.load(std::memory_order_relaxed) != 0 || ALBuf->MappedAccess != 0) + context->throw_error(AL_INVALID_OPERATION, "Modifying callback for in-use buffer {}", ALBuf->id); const ALuint ambiorder{IsBFormat(DstChannels) ? ALBuf->UnpackAmbiOrder : @@ -402,6 +421,10 @@ void PrepareCallback(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, const ALuint unpackalign{ALBuf->UnpackAlign}; const ALuint align{SanitizeAlignment(DstType, unpackalign)}; + if(align < 1) + context->throw_error(AL_INVALID_VALUE, "Invalid unpack alignment {} for {} samples", + unpackalign, NameFromFormat(DstType)); + const ALuint BlockSize{ChannelsFromFmt(DstChannels, ambiorder) * ((DstType == FmtIMA4) ? (align-1)/2 + 4 : (DstType == FmtMSADPCM) ? (align-2)/2 + 7 : @@ -420,7 +443,7 @@ void PrepareCallback(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, BufferVectorType(line_blocks*BlockSize).swap(ALBuf->mDataStorage); ALBuf->mData = ALBuf->mDataStorage; -#ifdef ALSOFT_EAX +#if ALSOFT_EAX eax_x_ram_clear(*context->mALDevice, *ALBuf); #endif @@ -442,17 +465,17 @@ void PrepareCallback(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, } /** Prepares the buffer to use caller-specified storage. */ -void PrepareUserPtr(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, +void PrepareUserPtr(ALCcontext *context [[maybe_unused]], ALbuffer *ALBuf, ALsizei freq, const FmtChannels DstChannels, const FmtType DstType, std::byte *sdata, const ALuint sdatalen) { - if(ReadRef(ALBuf->ref) != 0 || ALBuf->MappedAccess != 0) UNLIKELY - return context->setError(AL_INVALID_OPERATION, "Modifying storage for in-use buffer %u", + if(ALBuf->ref.load(std::memory_order_relaxed) != 0 || ALBuf->MappedAccess != 0) + context->throw_error(AL_INVALID_OPERATION, "Modifying storage for in-use buffer {}", ALBuf->id); const ALuint unpackalign{ALBuf->UnpackAlign}; const ALuint align{SanitizeAlignment(DstType, unpackalign)}; - if(align < 1) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Invalid unpack alignment %u for %s samples", + if(align < 1) + context->throw_error(AL_INVALID_VALUE, "Invalid unpack alignment {} for {} samples", unpackalign, NameFromFormat(DstType)); auto get_type_alignment = [](const FmtType type) noexcept -> ALuint @@ -464,6 +487,7 @@ void PrepareUserPtr(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, { case FmtUByte: return alignof(ALubyte); case FmtShort: return alignof(ALshort); + case FmtInt: return alignof(ALint); case FmtFloat: return alignof(ALfloat); case FmtDouble: return alignof(ALdouble); case FmtMulaw: return alignof(ALubyte); @@ -475,7 +499,7 @@ void PrepareUserPtr(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, }; const auto typealign = get_type_alignment(DstType); if((reinterpret_cast(sdata) & (typealign-1)) != 0) - return context->setError(AL_INVALID_VALUE, "Pointer %p is misaligned for %s samples (%u)", + context->throw_error(AL_INVALID_VALUE, "Pointer {} is misaligned for {} samples ({})", static_cast(sdata), NameFromFormat(DstType), typealign); const ALuint ambiorder{IsBFormat(DstChannels) ? ALBuf->UnpackAmbiOrder : @@ -487,34 +511,33 @@ void PrepareUserPtr(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ((DstType == FmtIMA4) ? (align-1)/2 + 4 : (DstType == FmtMSADPCM) ? (align-2)/2 + 7 : (align * BytesFromFmt(DstType)))}; - if((sdatalen%BlockSize) != 0) UNLIKELY - return context->setError(AL_INVALID_VALUE, - "Data size %u is not a multiple of frame size %u (%u unpack alignment)", + if((sdatalen%BlockSize) != 0) + context->throw_error(AL_INVALID_VALUE, + "Data size {} is not a multiple of frame size {} ({} unpack alignment)", sdatalen, BlockSize, align); const ALuint blocks{sdatalen / BlockSize}; - if(blocks > std::numeric_limits::max()/align) UNLIKELY - return context->setError(AL_OUT_OF_MEMORY, - "Buffer size overflow, %d blocks x %d samples per block", blocks, align); - if(blocks > std::numeric_limits::max()/BlockSize) UNLIKELY - return context->setError(AL_OUT_OF_MEMORY, - "Buffer size overflow, %d frames x %d bytes per frame", blocks, BlockSize); + if(blocks > std::numeric_limits::max()/align) + context->throw_error(AL_OUT_OF_MEMORY, + "Buffer size overflow, {} blocks x {} samples per block", blocks, align); + if(blocks > std::numeric_limits::max()/BlockSize) + context->throw_error(AL_OUT_OF_MEMORY, + "Buffer size overflow, {} frames x {} bytes per frame", blocks, BlockSize); -#ifdef ALSOFT_EAX +#if ALSOFT_EAX if(ALBuf->eax_x_ram_mode == EaxStorage::Hardware) { - ALCdevice &device = *context->mALDevice; + auto &device = *context->mALDevice; if(!eax_x_ram_check_availability(device, *ALBuf, sdatalen)) - return context->setError(AL_OUT_OF_MEMORY, - "Out of X-RAM memory (avail: %u, needed: %u)", device.eax_x_ram_free_size, - sdatalen); + context->throw_error(AL_OUT_OF_MEMORY, "Out of X-RAM memory (avail: {}, needed: {})", + device.eax_x_ram_free_size, sdatalen); } #endif decltype(ALBuf->mDataStorage){}.swap(ALBuf->mDataStorage); - ALBuf->mData = {static_cast(sdata), sdatalen}; + ALBuf->mData = al::span{sdata, sdatalen}; -#ifdef ALSOFT_EAX +#if ALSOFT_EAX eax_x_ram_clear(*context->mALDevice, *ALBuf); #endif @@ -534,7 +557,7 @@ void PrepareUserPtr(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALBuf->mLoopStart = 0; ALBuf->mLoopEnd = ALBuf->mSampleLen; -#ifdef ALSOFT_EAX +#if ALSOFT_EAX if(ALBuf->eax_x_ram_mode == EaxStorage::Hardware) eax_x_ram_apply(*context->mALDevice, *ALBuf); #endif @@ -542,184 +565,184 @@ void PrepareUserPtr(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, struct DecompResult { FmtChannels channels; FmtType type; }; -std::optional DecomposeUserFormat(ALenum format) +auto DecomposeUserFormat(ALenum format) noexcept -> std::optional { struct FormatMap { ALenum format; - FmtChannels channels; - FmtType type; + DecompResult result; }; - static const std::array UserFmtList{{ - { AL_FORMAT_MONO8, FmtMono, FmtUByte }, - { AL_FORMAT_MONO16, FmtMono, FmtShort }, - { AL_FORMAT_MONO_FLOAT32, FmtMono, FmtFloat }, - { AL_FORMAT_MONO_DOUBLE_EXT, FmtMono, FmtDouble }, - { AL_FORMAT_MONO_IMA4, FmtMono, FmtIMA4 }, - { AL_FORMAT_MONO_MSADPCM_SOFT, FmtMono, FmtMSADPCM }, - { AL_FORMAT_MONO_MULAW, FmtMono, FmtMulaw }, - { AL_FORMAT_MONO_ALAW_EXT, FmtMono, FmtAlaw }, - - { AL_FORMAT_STEREO8, FmtStereo, FmtUByte }, - { AL_FORMAT_STEREO16, FmtStereo, FmtShort }, - { AL_FORMAT_STEREO_FLOAT32, FmtStereo, FmtFloat }, - { AL_FORMAT_STEREO_DOUBLE_EXT, FmtStereo, FmtDouble }, - { AL_FORMAT_STEREO_IMA4, FmtStereo, FmtIMA4 }, - { AL_FORMAT_STEREO_MSADPCM_SOFT, FmtStereo, FmtMSADPCM }, - { AL_FORMAT_STEREO_MULAW, FmtStereo, FmtMulaw }, - { AL_FORMAT_STEREO_ALAW_EXT, FmtStereo, FmtAlaw }, - - { AL_FORMAT_REAR8, FmtRear, FmtUByte }, - { AL_FORMAT_REAR16, FmtRear, FmtShort }, - { AL_FORMAT_REAR32, FmtRear, FmtFloat }, - { AL_FORMAT_REAR_MULAW, FmtRear, FmtMulaw }, - - { AL_FORMAT_QUAD8_LOKI, FmtQuad, FmtUByte }, - { AL_FORMAT_QUAD16_LOKI, FmtQuad, FmtShort }, - - { AL_FORMAT_QUAD8, FmtQuad, FmtUByte }, - { AL_FORMAT_QUAD16, FmtQuad, FmtShort }, - { AL_FORMAT_QUAD32, FmtQuad, FmtFloat }, - { AL_FORMAT_QUAD_MULAW, FmtQuad, FmtMulaw }, - - { AL_FORMAT_51CHN8, FmtX51, FmtUByte }, - { AL_FORMAT_51CHN16, FmtX51, FmtShort }, - { AL_FORMAT_51CHN32, FmtX51, FmtFloat }, - { AL_FORMAT_51CHN_MULAW, FmtX51, FmtMulaw }, - - { AL_FORMAT_61CHN8, FmtX61, FmtUByte }, - { AL_FORMAT_61CHN16, FmtX61, FmtShort }, - { AL_FORMAT_61CHN32, FmtX61, FmtFloat }, - { AL_FORMAT_61CHN_MULAW, FmtX61, FmtMulaw }, - - { AL_FORMAT_71CHN8, FmtX71, FmtUByte }, - { AL_FORMAT_71CHN16, FmtX71, FmtShort }, - { AL_FORMAT_71CHN32, FmtX71, FmtFloat }, - { AL_FORMAT_71CHN_MULAW, FmtX71, FmtMulaw }, - - { AL_FORMAT_BFORMAT2D_8, FmtBFormat2D, FmtUByte }, - { AL_FORMAT_BFORMAT2D_16, FmtBFormat2D, FmtShort }, - { AL_FORMAT_BFORMAT2D_FLOAT32, FmtBFormat2D, FmtFloat }, - { AL_FORMAT_BFORMAT2D_MULAW, FmtBFormat2D, FmtMulaw }, - - { AL_FORMAT_BFORMAT3D_8, FmtBFormat3D, FmtUByte }, - { AL_FORMAT_BFORMAT3D_16, FmtBFormat3D, FmtShort }, - { AL_FORMAT_BFORMAT3D_FLOAT32, FmtBFormat3D, FmtFloat }, - { AL_FORMAT_BFORMAT3D_MULAW, FmtBFormat3D, FmtMulaw }, - - { AL_FORMAT_UHJ2CHN8_SOFT, FmtUHJ2, FmtUByte }, - { AL_FORMAT_UHJ2CHN16_SOFT, FmtUHJ2, FmtShort }, - { AL_FORMAT_UHJ2CHN_FLOAT32_SOFT, FmtUHJ2, FmtFloat }, - { AL_FORMAT_UHJ2CHN_MULAW_SOFT, FmtUHJ2, FmtMulaw }, - { AL_FORMAT_UHJ2CHN_ALAW_SOFT, FmtUHJ2, FmtAlaw }, - { AL_FORMAT_UHJ2CHN_IMA4_SOFT, FmtUHJ2, FmtIMA4 }, - { AL_FORMAT_UHJ2CHN_MSADPCM_SOFT, FmtUHJ2, FmtMSADPCM }, - - { AL_FORMAT_UHJ3CHN8_SOFT, FmtUHJ3, FmtUByte }, - { AL_FORMAT_UHJ3CHN16_SOFT, FmtUHJ3, FmtShort }, - { AL_FORMAT_UHJ3CHN_FLOAT32_SOFT, FmtUHJ3, FmtFloat }, - { AL_FORMAT_UHJ3CHN_MULAW_SOFT, FmtUHJ3, FmtMulaw }, - { AL_FORMAT_UHJ3CHN_ALAW_SOFT, FmtUHJ3, FmtAlaw }, - - { AL_FORMAT_UHJ4CHN8_SOFT, FmtUHJ4, FmtUByte }, - { AL_FORMAT_UHJ4CHN16_SOFT, FmtUHJ4, FmtShort }, - { AL_FORMAT_UHJ4CHN_FLOAT32_SOFT, FmtUHJ4, FmtFloat }, - { AL_FORMAT_UHJ4CHN_MULAW_SOFT, FmtUHJ4, FmtMulaw }, - { AL_FORMAT_UHJ4CHN_ALAW_SOFT, FmtUHJ4, FmtAlaw }, - }}; - - for(const auto &fmt : UserFmtList) - { - if(fmt.format == format) - return DecompResult{fmt.channels, fmt.type}; - } + static constexpr std::array UserFmtList{ + FormatMap{AL_FORMAT_MONO8, {FmtMono, FmtUByte} }, + FormatMap{AL_FORMAT_MONO16, {FmtMono, FmtShort} }, + FormatMap{AL_FORMAT_MONO_I32, {FmtMono, FmtInt} }, + FormatMap{AL_FORMAT_MONO_FLOAT32, {FmtMono, FmtFloat} }, + FormatMap{AL_FORMAT_MONO_DOUBLE_EXT, {FmtMono, FmtDouble} }, + FormatMap{AL_FORMAT_MONO_IMA4, {FmtMono, FmtIMA4} }, + FormatMap{AL_FORMAT_MONO_MSADPCM_SOFT, {FmtMono, FmtMSADPCM}}, + FormatMap{AL_FORMAT_MONO_MULAW, {FmtMono, FmtMulaw} }, + FormatMap{AL_FORMAT_MONO_ALAW_EXT, {FmtMono, FmtAlaw} }, + + FormatMap{AL_FORMAT_STEREO8, {FmtStereo, FmtUByte} }, + FormatMap{AL_FORMAT_STEREO16, {FmtStereo, FmtShort} }, + FormatMap{AL_FORMAT_STEREO_I32, {FmtStereo, FmtInt} }, + FormatMap{AL_FORMAT_STEREO_FLOAT32, {FmtStereo, FmtFloat} }, + FormatMap{AL_FORMAT_STEREO_DOUBLE_EXT, {FmtStereo, FmtDouble} }, + FormatMap{AL_FORMAT_STEREO_IMA4, {FmtStereo, FmtIMA4} }, + FormatMap{AL_FORMAT_STEREO_MSADPCM_SOFT, {FmtStereo, FmtMSADPCM}}, + FormatMap{AL_FORMAT_STEREO_MULAW, {FmtStereo, FmtMulaw} }, + FormatMap{AL_FORMAT_STEREO_ALAW_EXT, {FmtStereo, FmtAlaw} }, + + FormatMap{AL_FORMAT_REAR8, {FmtRear, FmtUByte}}, + FormatMap{AL_FORMAT_REAR16, {FmtRear, FmtShort}}, + FormatMap{AL_FORMAT_REAR32, {FmtRear, FmtFloat}}, + FormatMap{AL_FORMAT_REAR_I32, {FmtRear, FmtInt} }, + FormatMap{AL_FORMAT_REAR_FLOAT32, {FmtRear, FmtFloat}}, + FormatMap{AL_FORMAT_REAR_MULAW, {FmtRear, FmtMulaw}}, + + FormatMap{AL_FORMAT_QUAD8_LOKI, {FmtQuad, FmtUByte}}, + FormatMap{AL_FORMAT_QUAD16_LOKI, {FmtQuad, FmtShort}}, + + FormatMap{AL_FORMAT_QUAD8, {FmtQuad, FmtUByte}}, + FormatMap{AL_FORMAT_QUAD16, {FmtQuad, FmtShort}}, + FormatMap{AL_FORMAT_QUAD32, {FmtQuad, FmtFloat}}, + FormatMap{AL_FORMAT_QUAD_I32, {FmtQuad, FmtInt} }, + FormatMap{AL_FORMAT_QUAD_FLOAT32, {FmtQuad, FmtFloat}}, + FormatMap{AL_FORMAT_QUAD_MULAW, {FmtQuad, FmtMulaw}}, + + FormatMap{AL_FORMAT_51CHN8, {FmtX51, FmtUByte}}, + FormatMap{AL_FORMAT_51CHN16, {FmtX51, FmtShort}}, + FormatMap{AL_FORMAT_51CHN32, {FmtX51, FmtFloat}}, + FormatMap{AL_FORMAT_51CHN_I32, {FmtX51, FmtInt} }, + FormatMap{AL_FORMAT_51CHN_FLOAT32, {FmtX51, FmtFloat}}, + FormatMap{AL_FORMAT_51CHN_MULAW, {FmtX51, FmtMulaw}}, + + FormatMap{AL_FORMAT_61CHN8, {FmtX61, FmtUByte}}, + FormatMap{AL_FORMAT_61CHN16, {FmtX61, FmtShort}}, + FormatMap{AL_FORMAT_61CHN32, {FmtX61, FmtFloat}}, + FormatMap{AL_FORMAT_61CHN_I32, {FmtX61, FmtInt} }, + FormatMap{AL_FORMAT_61CHN_FLOAT32, {FmtX61, FmtFloat}}, + FormatMap{AL_FORMAT_61CHN_MULAW, {FmtX61, FmtMulaw}}, + + FormatMap{AL_FORMAT_71CHN8, {FmtX71, FmtUByte}}, + FormatMap{AL_FORMAT_71CHN16, {FmtX71, FmtShort}}, + FormatMap{AL_FORMAT_71CHN32, {FmtX71, FmtFloat}}, + FormatMap{AL_FORMAT_71CHN_I32, {FmtX71, FmtInt} }, + FormatMap{AL_FORMAT_71CHN_FLOAT32, {FmtX71, FmtFloat}}, + FormatMap{AL_FORMAT_71CHN_MULAW, {FmtX71, FmtMulaw}}, + + FormatMap{AL_FORMAT_BFORMAT2D_8, {FmtBFormat2D, FmtUByte}}, + FormatMap{AL_FORMAT_BFORMAT2D_16, {FmtBFormat2D, FmtShort}}, + FormatMap{AL_FORMAT_BFORMAT2D_I32, {FmtBFormat2D, FmtInt} }, + FormatMap{AL_FORMAT_BFORMAT2D_FLOAT32, {FmtBFormat2D, FmtFloat}}, + FormatMap{AL_FORMAT_BFORMAT2D_MULAW, {FmtBFormat2D, FmtMulaw}}, + + FormatMap{AL_FORMAT_BFORMAT3D_8, {FmtBFormat3D, FmtUByte}}, + FormatMap{AL_FORMAT_BFORMAT3D_16, {FmtBFormat3D, FmtShort}}, + FormatMap{AL_FORMAT_BFORMAT2D_I32, {FmtBFormat3D, FmtInt} }, + FormatMap{AL_FORMAT_BFORMAT3D_FLOAT32, {FmtBFormat3D, FmtFloat}}, + FormatMap{AL_FORMAT_BFORMAT3D_MULAW, {FmtBFormat3D, FmtMulaw}}, + + FormatMap{AL_FORMAT_UHJ2CHN8_SOFT, {FmtUHJ2, FmtUByte} }, + FormatMap{AL_FORMAT_UHJ2CHN16_SOFT, {FmtUHJ2, FmtShort} }, + FormatMap{AL_FORMAT_UHJ2CHN_I32_SOFT, {FmtUHJ2, FmtInt} }, + FormatMap{AL_FORMAT_UHJ2CHN_FLOAT32_SOFT, {FmtUHJ2, FmtFloat} }, + FormatMap{AL_FORMAT_UHJ2CHN_MULAW_SOFT, {FmtUHJ2, FmtMulaw} }, + FormatMap{AL_FORMAT_UHJ2CHN_ALAW_SOFT, {FmtUHJ2, FmtAlaw} }, + FormatMap{AL_FORMAT_UHJ2CHN_IMA4_SOFT, {FmtUHJ2, FmtIMA4} }, + FormatMap{AL_FORMAT_UHJ2CHN_MSADPCM_SOFT, {FmtUHJ2, FmtMSADPCM}}, + + FormatMap{AL_FORMAT_UHJ3CHN8_SOFT, {FmtUHJ3, FmtUByte}}, + FormatMap{AL_FORMAT_UHJ3CHN16_SOFT, {FmtUHJ3, FmtShort}}, + FormatMap{AL_FORMAT_UHJ3CHN_I32_SOFT, {FmtUHJ3, FmtInt} }, + FormatMap{AL_FORMAT_UHJ3CHN_FLOAT32_SOFT, {FmtUHJ3, FmtFloat}}, + FormatMap{AL_FORMAT_UHJ3CHN_MULAW_SOFT, {FmtUHJ3, FmtMulaw}}, + FormatMap{AL_FORMAT_UHJ3CHN_ALAW_SOFT, {FmtUHJ3, FmtAlaw} }, + + FormatMap{AL_FORMAT_UHJ4CHN8_SOFT, {FmtUHJ4, FmtUByte}}, + FormatMap{AL_FORMAT_UHJ4CHN16_SOFT, {FmtUHJ4, FmtShort}}, + FormatMap{AL_FORMAT_UHJ4CHN_I32_SOFT, {FmtUHJ4, FmtInt} }, + FormatMap{AL_FORMAT_UHJ4CHN_FLOAT32_SOFT, {FmtUHJ4, FmtFloat}}, + FormatMap{AL_FORMAT_UHJ4CHN_MULAW_SOFT, {FmtUHJ4, FmtMulaw}}, + FormatMap{AL_FORMAT_UHJ4CHN_ALAW_SOFT, {FmtUHJ4, FmtAlaw} }, + }; + + auto iter = std::find_if(UserFmtList.cbegin(), UserFmtList.cend(), + [format](const FormatMap &fmt) noexcept { return fmt.format == format; }); + if(iter != UserFmtList.cend()) + return iter->result; return std::nullopt; } } // namespace -AL_API DECL_FUNC2(void, alGenBuffers, ALsizei, ALuint*) +AL_API DECL_FUNC2(void, alGenBuffers, ALsizei,n, ALuint*,buffers) FORCE_ALIGN void AL_APIENTRY alGenBuffersDirect(ALCcontext *context, ALsizei n, ALuint *buffers) noexcept -{ - if(n < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Generating %d buffers", n); +try { + if(n < 0) + context->throw_error(AL_INVALID_VALUE, "Generating {} buffers", n); if(n <= 0) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; - if(!EnsureBuffers(device, static_cast(n))) - { - context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d buffer%s", n, (n==1)?"":"s"); - return; - } + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; - if(n == 1) LIKELY - { - /* Special handling for the easy and normal case. */ - ALbuffer *buffer{AllocBuffer(device)}; - buffers[0] = buffer->id; - } - else - { - /* Store the allocated buffer IDs in a separate local list, to avoid - * modifying the user storage in case of failure. - */ - std::vector ids; - ids.reserve(static_cast(n)); - do { - ALbuffer *buffer{AllocBuffer(device)}; - ids.emplace_back(buffer->id); - } while(--n); - std::copy(ids.begin(), ids.end(), buffers); - } + const al::span bids{buffers, static_cast(n)}; + if(!EnsureBuffers(device, bids.size())) + context->throw_error(AL_OUT_OF_MEMORY, "Failed to allocate {} buffer{}", n, + (n==1) ? "" : "s"); + + std::generate(bids.begin(), bids.end(), [device]{ return AllocBuffer(device)->id; }); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC2(void, alDeleteBuffers, ALsizei, const ALuint*) +AL_API DECL_FUNC2(void, alDeleteBuffers, ALsizei,n, const ALuint*,buffers) FORCE_ALIGN void AL_APIENTRY alDeleteBuffersDirect(ALCcontext *context, ALsizei n, const ALuint *buffers) noexcept -{ - if(n < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Deleting %d buffers", n); +try { + if(n < 0) + context->throw_error(AL_INVALID_VALUE, "Deleting {} buffers", n); if(n <= 0) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; /* First try to find any buffers that are invalid or in-use. */ - auto validate_buffer = [device, &context](const ALuint bid) -> bool + auto validate_buffer = [context,device](const ALuint bid) { - if(!bid) return true; + if(!bid) return; ALbuffer *ALBuf{LookupBuffer(device, bid)}; - if(!ALBuf) UNLIKELY - { - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", bid); - return false; - } - if(ReadRef(ALBuf->ref) != 0) UNLIKELY - { - context->setError(AL_INVALID_OPERATION, "Deleting in-use buffer %u", bid); - return false; - } - return true; + if(!ALBuf) + context->throw_error(AL_INVALID_NAME, "Invalid buffer ID {}", bid); + if(ALBuf->ref.load(std::memory_order_relaxed) != 0) + context->throw_error(AL_INVALID_OPERATION, "Deleting in-use buffer {}", bid); }; - const ALuint *buffers_end = buffers + n; - auto invbuf = std::find_if_not(buffers, buffers_end, validate_buffer); - if(invbuf != buffers_end) UNLIKELY return; + + const al::span bids{buffers, static_cast(n)}; + std::for_each(bids.begin(), bids.end(), validate_buffer); /* All good. Delete non-0 buffer IDs. */ auto delete_buffer = [device](const ALuint bid) -> void { - ALbuffer *buffer{bid ? LookupBuffer(device, bid) : nullptr}; - if(buffer) FreeBuffer(device, buffer); + if(ALbuffer *buffer{bid ? LookupBuffer(device, bid) : nullptr}) + FreeBuffer(device, buffer); }; - std::for_each(buffers, buffers_end, delete_buffer); + std::for_each(bids.begin(), bids.end(), delete_buffer); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC1(ALboolean, alIsBuffer, ALuint) +AL_API DECL_FUNC1(ALboolean, alIsBuffer, ALuint,buffer) FORCE_ALIGN ALboolean AL_APIENTRY alIsBufferDirect(ALCcontext *context, ALuint buffer) noexcept { - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; if(!buffer || LookupBuffer(device, buffer)) return AL_TRUE; return AL_FALSE; @@ -736,192 +759,203 @@ AL_API void AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoid FORCE_ALIGN void AL_APIENTRY alBufferDataDirect(ALCcontext *context, ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq) noexcept { alBufferStorageDirectSOFT(context, buffer, format, data, size, freq, 0); } -AL_API DECL_FUNCEXT6(void, alBufferStorage,SOFT, ALuint, ALenum, const ALvoid*, ALsizei, ALsizei, ALbitfieldSOFT) +AL_API DECL_FUNCEXT6(void, alBufferStorage,SOFT, ALuint,buffer, ALenum,format, const ALvoid*,data, ALsizei,size, ALsizei,freq, ALbitfieldSOFT,flags) FORCE_ALIGN void AL_APIENTRY alBufferStorageDirectSOFT(ALCcontext *context, ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq, ALbitfieldSOFT flags) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; - - ALbuffer *albuf = LookupBuffer(device, buffer); - if(!albuf) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - else if(size < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Negative storage size %d", size); - else if(freq < 1) UNLIKELY - context->setError(AL_INVALID_VALUE, "Invalid sample rate %d", freq); - else if((flags&INVALID_STORAGE_MASK) != 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Invalid storage flags 0x%x", +try { + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; + + ALbuffer *albuf{LookupBuffer(device, buffer)}; + if(!albuf) + context->throw_error(AL_INVALID_NAME, "Invalid buffer ID {}", buffer); + if(size < 0) + context->throw_error(AL_INVALID_VALUE, "Negative storage size {}", size); + if(freq < 1) + context->throw_error(AL_INVALID_VALUE, "Invalid sample rate {}", freq); + if((flags&INVALID_STORAGE_MASK) != 0) + context->throw_error(AL_INVALID_VALUE, "Invalid storage flags {:#x}", flags&INVALID_STORAGE_MASK); - else if((flags&AL_MAP_PERSISTENT_BIT_SOFT) && !(flags&MAP_READ_WRITE_FLAGS)) UNLIKELY - context->setError(AL_INVALID_VALUE, + if((flags&AL_MAP_PERSISTENT_BIT_SOFT) && !(flags&MAP_READ_WRITE_FLAGS)) + context->throw_error(AL_INVALID_VALUE, "Declaring persistently mapped storage without read or write access"); - else - { - auto usrfmt = DecomposeUserFormat(format); - if(!usrfmt) UNLIKELY - context->setError(AL_INVALID_ENUM, "Invalid format 0x%04x", format); - else - { - LoadData(context, albuf, freq, static_cast(size), usrfmt->channels, - usrfmt->type, static_cast(data), flags); - } - } + + auto usrfmt = DecomposeUserFormat(format); + if(!usrfmt) + context->throw_error(AL_INVALID_ENUM, "Invalid format {:#04x}", as_unsigned(format)); + + auto bdata = static_cast(data); + LoadData(context, albuf, freq, static_cast(size), usrfmt->channels, usrfmt->type, + al::span{bdata, bdata ? static_cast(size) : 0u}, flags); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -DECL_FUNC5(void, alBufferDataStatic, ALuint, ALenum, ALvoid*, ALsizei, ALsizei) +FORCE_ALIGN DECL_FUNC5(void, alBufferDataStatic, ALuint,buffer, ALenum,format, ALvoid*,data, ALsizei,size, ALsizei,freq) FORCE_ALIGN void AL_APIENTRY alBufferDataStaticDirect(ALCcontext *context, const ALuint buffer, ALenum format, ALvoid *data, ALsizei size, ALsizei freq) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; - - ALbuffer *albuf = LookupBuffer(device, buffer); - if(!albuf) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - if(size < 0) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Negative storage size %d", size); - if(freq < 1) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Invalid sample rate %d", freq); +try { + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; + + ALbuffer *albuf{LookupBuffer(device, buffer)}; + if(!albuf) + context->throw_error(AL_INVALID_NAME, "Invalid buffer ID {}", buffer); + if(size < 0) + context->throw_error(AL_INVALID_VALUE, "Negative storage size {}", size); + if(freq < 1) + context->throw_error(AL_INVALID_VALUE, "Invalid sample rate {}", freq); auto usrfmt = DecomposeUserFormat(format); - if(!usrfmt) UNLIKELY - return context->setError(AL_INVALID_ENUM, "Invalid format 0x%04x", format); + if(!usrfmt) + context->throw_error(AL_INVALID_ENUM, "Invalid format {:#04x}", as_unsigned(format)); PrepareUserPtr(context, albuf, freq, usrfmt->channels, usrfmt->type, static_cast(data), static_cast(size)); } +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); +} -AL_API DECL_FUNCEXT4(void*, alMapBuffer,SOFT, ALuint, ALsizei, ALsizei, ALbitfieldSOFT) +AL_API DECL_FUNCEXT4(void*, alMapBuffer,SOFT, ALuint,buffer, ALsizei,offset, ALsizei,length, ALbitfieldSOFT,access) FORCE_ALIGN void* AL_APIENTRY alMapBufferDirectSOFT(ALCcontext *context, ALuint buffer, ALsizei offset, ALsizei length, ALbitfieldSOFT access) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; - - ALbuffer *albuf = LookupBuffer(device, buffer); - if(!albuf) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - else if((access&INVALID_MAP_FLAGS) != 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Invalid map flags 0x%x", access&INVALID_MAP_FLAGS); - else if(!(access&MAP_READ_WRITE_FLAGS)) UNLIKELY - context->setError(AL_INVALID_VALUE, "Mapping buffer %u without read or write access", +try { + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; + + ALbuffer *albuf{LookupBuffer(device, buffer)}; + if(!albuf) + context->throw_error(AL_INVALID_NAME, "Invalid buffer ID {}", buffer); + if((access&INVALID_MAP_FLAGS) != 0) + context->throw_error(AL_INVALID_VALUE, "Invalid map flags {:#x}", + access&INVALID_MAP_FLAGS); + if(!(access&MAP_READ_WRITE_FLAGS)) + context->throw_error(AL_INVALID_VALUE, "Mapping buffer {} without read or write access", buffer); - else - { - ALbitfieldSOFT unavailable = (albuf->Access^access) & access; - if(ReadRef(albuf->ref) != 0 && !(access&AL_MAP_PERSISTENT_BIT_SOFT)) UNLIKELY - context->setError(AL_INVALID_OPERATION, - "Mapping in-use buffer %u without persistent mapping", buffer); - else if(albuf->MappedAccess != 0) UNLIKELY - context->setError(AL_INVALID_OPERATION, "Mapping already-mapped buffer %u", buffer); - else if((unavailable&AL_MAP_READ_BIT_SOFT)) UNLIKELY - context->setError(AL_INVALID_VALUE, - "Mapping buffer %u for reading without read access", buffer); - else if((unavailable&AL_MAP_WRITE_BIT_SOFT)) UNLIKELY - context->setError(AL_INVALID_VALUE, - "Mapping buffer %u for writing without write access", buffer); - else if((unavailable&AL_MAP_PERSISTENT_BIT_SOFT)) UNLIKELY - context->setError(AL_INVALID_VALUE, - "Mapping buffer %u persistently without persistent access", buffer); - else if(offset < 0 || length <= 0 - || static_cast(offset) >= albuf->OriginalSize - || static_cast(length) > albuf->OriginalSize - static_cast(offset)) - UNLIKELY - context->setError(AL_INVALID_VALUE, "Mapping invalid range %d+%d for buffer %u", - offset, length, buffer); - else - { - void *retval{albuf->mData.data() + offset}; - albuf->MappedAccess = access; - albuf->MappedOffset = offset; - albuf->MappedSize = length; - return retval; - } - } + const ALbitfieldSOFT unavailable{(albuf->Access^access) & access}; + if(albuf->ref.load(std::memory_order_relaxed) != 0 && !(access&AL_MAP_PERSISTENT_BIT_SOFT)) + context->throw_error(AL_INVALID_OPERATION, + "Mapping in-use buffer {} without persistent mapping", buffer); + if(albuf->MappedAccess != 0) + context->throw_error(AL_INVALID_OPERATION, "Mapping already-mapped buffer {}", buffer); + if((unavailable&AL_MAP_READ_BIT_SOFT)) + context->throw_error(AL_INVALID_VALUE, "Mapping buffer {} for reading without read access", + buffer); + if((unavailable&AL_MAP_WRITE_BIT_SOFT)) + context->throw_error(AL_INVALID_VALUE, + "Mapping buffer {} for writing without write access", buffer); + if((unavailable&AL_MAP_PERSISTENT_BIT_SOFT)) + context->throw_error(AL_INVALID_VALUE, + "Mapping buffer {} persistently without persistent access", buffer); + if(offset < 0 || length <= 0 || static_cast(offset) >= albuf->OriginalSize + || static_cast(length) > albuf->OriginalSize - static_cast(offset)) + context->throw_error(AL_INVALID_VALUE, "Mapping invalid range {}+{} for buffer {}", offset, + length, buffer); + + void *retval{albuf->mData.data() + offset}; + albuf->MappedAccess = access; + albuf->MappedOffset = offset; + albuf->MappedSize = length; + return retval; +} +catch(al::base_exception&) { + return nullptr; +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); return nullptr; } -AL_API DECL_FUNCEXT1(void, alUnmapBuffer,SOFT, ALuint) +AL_API DECL_FUNCEXT1(void, alUnmapBuffer,SOFT, ALuint,buffer) FORCE_ALIGN void AL_APIENTRY alUnmapBufferDirectSOFT(ALCcontext *context, ALuint buffer) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; - - ALbuffer *albuf = LookupBuffer(device, buffer); - if(!albuf) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - else if(albuf->MappedAccess == 0) UNLIKELY - context->setError(AL_INVALID_OPERATION, "Unmapping unmapped buffer %u", buffer); - else - { - albuf->MappedAccess = 0; - albuf->MappedOffset = 0; - albuf->MappedSize = 0; - } +try { + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; + + ALbuffer *albuf{LookupBuffer(device, buffer)}; + if(!albuf) + context->throw_error(AL_INVALID_NAME, "Invalid buffer ID {}", buffer); + if(albuf->MappedAccess == 0) + context->throw_error(AL_INVALID_OPERATION, "Unmapping unmapped buffer {}", buffer); + + albuf->MappedAccess = 0; + albuf->MappedOffset = 0; + albuf->MappedSize = 0; +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNCEXT3(void, alFlushMappedBuffer,SOFT, ALuint, ALsizei, ALsizei) +AL_API DECL_FUNCEXT3(void, alFlushMappedBuffer,SOFT, ALuint,buffer, ALsizei,offset, ALsizei,length) FORCE_ALIGN void AL_APIENTRY alFlushMappedBufferDirectSOFT(ALCcontext *context, ALuint buffer, ALsizei offset, ALsizei length) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; - - ALbuffer *albuf = LookupBuffer(device, buffer); - if(!albuf) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - else if(!(albuf->MappedAccess&AL_MAP_WRITE_BIT_SOFT)) UNLIKELY - context->setError(AL_INVALID_OPERATION, "Flushing buffer %u while not mapped for writing", - buffer); - else if(offset < albuf->MappedOffset || length <= 0 +try { + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; + + ALbuffer *albuf{LookupBuffer(device, buffer)}; + if(!albuf) + context->throw_error(AL_INVALID_NAME, "Invalid buffer ID {}", buffer); + if(!(albuf->MappedAccess&AL_MAP_WRITE_BIT_SOFT)) + context->throw_error(AL_INVALID_OPERATION, + "Flushing buffer {} while not mapped for writing", buffer); + if(offset < albuf->MappedOffset || length <= 0 || offset >= albuf->MappedOffset+albuf->MappedSize - || length > albuf->MappedOffset+albuf->MappedSize-offset) UNLIKELY - context->setError(AL_INVALID_VALUE, "Flushing invalid range %d+%d on buffer %u", offset, + || length > albuf->MappedOffset+albuf->MappedSize-offset) + context->throw_error(AL_INVALID_VALUE, "Flushing invalid range {}+{} on buffer {}", offset, length, buffer); - else - { - /* FIXME: Need to use some method of double-buffering for the mixer and - * app to hold separate memory, which can be safely transferred - * asynchronously. Currently we just say the app shouldn't write where - * OpenAL's reading, and hope for the best... - */ - std::atomic_thread_fence(std::memory_order_seq_cst); - } + + /* FIXME: Need to use some method of double-buffering for the mixer and app + * to hold separate memory, which can be safely transferred asynchronously. + * Currently we just say the app shouldn't write where OpenAL's reading, + * and hope for the best... + */ + std::atomic_thread_fence(std::memory_order_seq_cst); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNCEXT5(void, alBufferSubData,SOFT, ALuint, ALenum, const ALvoid*, ALsizei, ALsizei) +AL_API DECL_FUNCEXT5(void, alBufferSubData,SOFT, ALuint,buffer, ALenum,format, const ALvoid*,data, ALsizei,offset, ALsizei,length) FORCE_ALIGN void AL_APIENTRY alBufferSubDataDirectSOFT(ALCcontext *context, ALuint buffer, ALenum format, const ALvoid *data, ALsizei offset, ALsizei length) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; +try { + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; - ALbuffer *albuf = LookupBuffer(device, buffer); - if(!albuf) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); + ALbuffer *albuf{LookupBuffer(device, buffer)}; + if(!albuf) + context->throw_error(AL_INVALID_NAME, "Invalid buffer ID {}", buffer); auto usrfmt = DecomposeUserFormat(format); - if(!usrfmt) UNLIKELY - return context->setError(AL_INVALID_ENUM, "Invalid format 0x%04x", format); + if(!usrfmt) + context->throw_error(AL_INVALID_ENUM, "Invalid format {:#04x}", as_unsigned(format)); const ALuint unpack_align{albuf->UnpackAlign}; const ALuint align{SanitizeAlignment(usrfmt->type, unpack_align)}; - if(align < 1) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Invalid unpack alignment %u", unpack_align); - if(usrfmt->channels != albuf->mChannels || usrfmt->type != albuf->mType) UNLIKELY - return context->setError(AL_INVALID_ENUM, "Unpacking data with mismatched format"); - if(align != albuf->mBlockAlign) UNLIKELY - return context->setError(AL_INVALID_VALUE, - "Unpacking data with alignment %u does not match original alignment %u", align, + if(align < 1) + context->throw_error(AL_INVALID_VALUE, "Invalid unpack alignment {}", unpack_align); + if(usrfmt->channels != albuf->mChannels || usrfmt->type != albuf->mType) + context->throw_error(AL_INVALID_ENUM, "Unpacking data with mismatched format"); + if(align != albuf->mBlockAlign) + context->throw_error(AL_INVALID_VALUE, + "Unpacking data with alignment {} does not match original alignment {}", align, albuf->mBlockAlign); - if(albuf->isBFormat() && albuf->UnpackAmbiOrder != albuf->mAmbiOrder) UNLIKELY - return context->setError(AL_INVALID_VALUE, - "Unpacking data with mismatched ambisonic order"); - if(albuf->MappedAccess != 0) UNLIKELY - return context->setError(AL_INVALID_OPERATION, "Unpacking data into mapped buffer %u", - buffer); + if(albuf->isBFormat() && albuf->UnpackAmbiOrder != albuf->mAmbiOrder) + context->throw_error(AL_INVALID_VALUE, "Unpacking data with mismatched ambisonic order"); + if(albuf->MappedAccess != 0) + context->throw_error(AL_INVALID_OPERATION, "Unpacking data into mapped buffer {}", buffer); const ALuint num_chans{albuf->channelsFromFmt()}; const ALuint byte_align{ @@ -931,154 +965,185 @@ FORCE_ALIGN void AL_APIENTRY alBufferSubDataDirectSOFT(ALCcontext *context, ALui if(offset < 0 || length < 0 || static_cast(offset) > albuf->OriginalSize || static_cast(length) > albuf->OriginalSize-static_cast(offset)) - UNLIKELY - return context->setError(AL_INVALID_VALUE, "Invalid data sub-range %d+%d on buffer %u", - offset, length, buffer); - if((static_cast(offset)%byte_align) != 0) UNLIKELY - return context->setError(AL_INVALID_VALUE, - "Sub-range offset %d is not a multiple of frame size %d (%d unpack alignment)", + context->throw_error(AL_INVALID_VALUE, "Invalid data sub-range {}+{} on buffer {}", offset, + length, buffer); + if((static_cast(offset)%byte_align) != 0) + context->throw_error(AL_INVALID_VALUE, + "Sub-range offset {} is not a multiple of frame size {} ({} unpack alignment)", offset, byte_align, align); - if((static_cast(length)%byte_align) != 0) UNLIKELY - return context->setError(AL_INVALID_VALUE, - "Sub-range length %d is not a multiple of frame size %d (%d unpack alignment)", + if((static_cast(length)%byte_align) != 0) + context->throw_error(AL_INVALID_VALUE, + "Sub-range length {} is not a multiple of frame size {} ({} unpack alignment)", length, byte_align, align); - assert(al::to_underlying(usrfmt->type) == al::to_underlying(albuf->mType)); - memcpy(albuf->mData.data()+offset, data, static_cast(length)); + std::memcpy(albuf->mData.data()+offset, data, static_cast(length)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC3(void, alBufferf, ALuint, ALenum, ALfloat) +AL_API DECL_FUNC3(void, alBufferf, ALuint,buffer, ALenum,param, ALfloat,value) FORCE_ALIGN void AL_APIENTRY alBufferfDirect(ALCcontext *context, ALuint buffer, ALenum param, - ALfloat /*value*/) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; + ALfloat value [[maybe_unused]]) noexcept +try { + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; - if(LookupBuffer(device, buffer) == nullptr) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - else switch(param) - { - default: - context->setError(AL_INVALID_ENUM, "Invalid buffer float property 0x%04x", param); - } + if(LookupBuffer(device, buffer) == nullptr) + context->throw_error(AL_INVALID_NAME, "Invalid buffer ID {}", buffer); + + context->throw_error(AL_INVALID_ENUM, "Invalid buffer float property {:#04x}", + as_unsigned(param)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC5(void, alBuffer3f, ALuint, ALenum, ALfloat, ALfloat, ALfloat) +AL_API DECL_FUNC5(void, alBuffer3f, ALuint,buffer, ALenum,param, ALfloat,value1, ALfloat,value2, ALfloat,value3) FORCE_ALIGN void AL_APIENTRY alBuffer3fDirect(ALCcontext *context, ALuint buffer, ALenum param, - ALfloat /*value1*/, ALfloat /*value2*/, ALfloat /*value3*/) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; + ALfloat value1 [[maybe_unused]], ALfloat value2 [[maybe_unused]], + ALfloat value3 [[maybe_unused]]) noexcept +try { + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; - if(LookupBuffer(device, buffer) == nullptr) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - else switch(param) - { - default: - context->setError(AL_INVALID_ENUM, "Invalid buffer 3-float property 0x%04x", param); - } + if(LookupBuffer(device, buffer) == nullptr) + context->throw_error(AL_INVALID_NAME, "Invalid buffer ID {}", buffer); + + context->throw_error(AL_INVALID_ENUM, "Invalid buffer 3-float property {:#04x}", + as_unsigned(param)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC3(void, alBufferfv, ALuint, ALenum, const ALfloat*) +AL_API DECL_FUNC3(void, alBufferfv, ALuint,buffer, ALenum,param, const ALfloat*,values) FORCE_ALIGN void AL_APIENTRY alBufferfvDirect(ALCcontext *context, ALuint buffer, ALenum param, const ALfloat *values) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; - - if(LookupBuffer(device, buffer) == nullptr) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - else if(!values) UNLIKELY - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else switch(param) - { - default: - context->setError(AL_INVALID_ENUM, "Invalid buffer float-vector property 0x%04x", param); - } +try { + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; + + if(LookupBuffer(device, buffer) == nullptr) + context->throw_error(AL_INVALID_NAME, "Invalid buffer ID {}", buffer); + if(!values) + context->throw_error(AL_INVALID_VALUE, "NULL pointer"); + + context->throw_error(AL_INVALID_ENUM, "Invalid buffer float-vector property {:#04x}", + as_unsigned(param)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC3(void, alBufferi, ALuint, ALenum, ALint) +AL_API DECL_FUNC3(void, alBufferi, ALuint,buffer, ALenum,param, ALint,value) FORCE_ALIGN void AL_APIENTRY alBufferiDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint value) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; +try { + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; - ALbuffer *albuf = LookupBuffer(device, buffer); - if(!albuf) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - else switch(param) + ALbuffer *albuf{LookupBuffer(device, buffer)}; + if(!albuf) + context->throw_error(AL_INVALID_NAME, "Invalid buffer ID {}", buffer); + + switch(param) { case AL_UNPACK_BLOCK_ALIGNMENT_SOFT: - if(value < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Invalid unpack block alignment %d", value); - else - albuf->UnpackAlign = static_cast(value); - break; + if(value < 0) + context->throw_error(AL_INVALID_VALUE, "Invalid unpack block alignment {}", value); + albuf->UnpackAlign = static_cast(value); + return; case AL_PACK_BLOCK_ALIGNMENT_SOFT: - if(value < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Invalid pack block alignment %d", value); - else - albuf->PackAlign = static_cast(value); - break; + if(value < 0) + context->throw_error(AL_INVALID_VALUE, "Invalid pack block alignment {}", value); + albuf->PackAlign = static_cast(value); + return; case AL_AMBISONIC_LAYOUT_SOFT: - if(ReadRef(albuf->ref) != 0) UNLIKELY - context->setError(AL_INVALID_OPERATION, "Modifying in-use buffer %u's ambisonic layout", - buffer); - else if(const auto layout = AmbiLayoutFromEnum(value)) + if(albuf->ref.load(std::memory_order_relaxed) != 0) + context->throw_error(AL_INVALID_OPERATION, + "Modifying in-use buffer {}'s ambisonic layout", buffer); + if(const auto layout = AmbiLayoutFromEnum(value)) + { + if(layout.value() == AmbiLayout::FuMa && albuf->mAmbiOrder > 3) + context->throw_error(AL_INVALID_OPERATION, + "Cannot set FuMa layout for {}{} order B-Format data", albuf->mAmbiOrder, + GetCounterSuffix(albuf->mAmbiOrder)); albuf->mAmbiLayout = layout.value(); - else UNLIKELY - context->setError(AL_INVALID_VALUE, "Invalid unpack ambisonic layout %04x", value); - break; + return; + } + context->throw_error(AL_INVALID_VALUE, "Invalid unpack ambisonic layout {:#04x}", + as_unsigned(value)); case AL_AMBISONIC_SCALING_SOFT: - if(ReadRef(albuf->ref) != 0) UNLIKELY - context->setError(AL_INVALID_OPERATION, "Modifying in-use buffer %u's ambisonic scaling", - buffer); - else if(const auto scaling = AmbiScalingFromEnum(value)) + if(albuf->ref.load(std::memory_order_relaxed) != 0) + context->throw_error(AL_INVALID_OPERATION, + "Modifying in-use buffer {}'s ambisonic scaling", buffer); + if(const auto scaling = AmbiScalingFromEnum(value)) + { + if(scaling.value() == AmbiScaling::FuMa && albuf->mAmbiOrder > 3) + context->throw_error(AL_INVALID_OPERATION, + "Cannot set FuMa scaling for {}{} order B-Format data", albuf->mAmbiOrder, + GetCounterSuffix(albuf->mAmbiOrder)); albuf->mAmbiScaling = scaling.value(); - else UNLIKELY - context->setError(AL_INVALID_VALUE, "Invalid unpack ambisonic scaling %04x", value); - break; + return; + } + context->throw_error(AL_INVALID_VALUE, "Invalid unpack ambisonic scaling {:#04x}", + as_unsigned(value)); case AL_UNPACK_AMBISONIC_ORDER_SOFT: - if(value < 1 || value > 14) UNLIKELY - context->setError(AL_INVALID_VALUE, "Invalid unpack ambisonic order %d", value); - else - albuf->UnpackAmbiOrder = static_cast(value); - break; - - default: - context->setError(AL_INVALID_ENUM, "Invalid buffer integer property 0x%04x", param); + if(value < 1 || value > 14) + context->throw_error(AL_INVALID_VALUE, "Invalid unpack ambisonic order {}", value); + albuf->UnpackAmbiOrder = static_cast(value); + return; } + + context->throw_error(AL_INVALID_ENUM, "Invalid buffer integer property {:#04x}", + as_unsigned(param)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC5(void, alBuffer3i, ALuint, ALenum, ALint, ALint, ALint) +AL_API DECL_FUNC5(void, alBuffer3i, ALuint,buffer, ALenum,param, ALint,value1, ALint,value2, ALint,value3) FORCE_ALIGN void AL_APIENTRY alBuffer3iDirect(ALCcontext *context, ALuint buffer, ALenum param, - ALint /*value1*/, ALint /*value2*/, ALint /*value3*/) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; + ALint value1 [[maybe_unused]], ALint value2 [[maybe_unused]], ALint value3 [[maybe_unused]]) noexcept +try { + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; - if(LookupBuffer(device, buffer) == nullptr) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - else switch(param) - { - default: - context->setError(AL_INVALID_ENUM, "Invalid buffer 3-integer property 0x%04x", param); - } + if(LookupBuffer(device, buffer) == nullptr) + context->throw_error(AL_INVALID_NAME, "Invalid buffer ID {}", buffer); + + context->throw_error(AL_INVALID_ENUM, "Invalid buffer 3-integer property {:#04x}", + as_unsigned(param)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC3(void, alBufferiv, ALuint, ALenum, const ALint*) +AL_API DECL_FUNC3(void, alBufferiv, ALuint,buffer, ALenum,param, const ALint*,values) FORCE_ALIGN void AL_APIENTRY alBufferivDirect(ALCcontext *context, ALuint buffer, ALenum param, const ALint *values) noexcept -{ - if(!values) UNLIKELY - return context->setError(AL_INVALID_VALUE, "NULL pointer"); +try { + if(!values) + context->throw_error(AL_INVALID_VALUE, "NULL pointer"); switch(param) { @@ -1087,85 +1152,98 @@ FORCE_ALIGN void AL_APIENTRY alBufferivDirect(ALCcontext *context, ALuint buffer case AL_AMBISONIC_LAYOUT_SOFT: case AL_AMBISONIC_SCALING_SOFT: case AL_UNPACK_AMBISONIC_ORDER_SOFT: - alBufferiDirect(context, buffer, param, values[0]); + alBufferiDirect(context, buffer, param, *values); return; } - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; + + ALbuffer *albuf{LookupBuffer(device, buffer)}; + if(!albuf) + context->throw_error(AL_INVALID_NAME, "Invalid buffer ID {}", buffer); - ALbuffer *albuf = LookupBuffer(device, buffer); - if(!albuf) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - else switch(param) + switch(param) { case AL_LOOP_POINTS_SOFT: - if(ReadRef(albuf->ref) != 0) UNLIKELY - context->setError(AL_INVALID_OPERATION, "Modifying in-use buffer %u's loop points", + auto vals = al::span{values, 2_uz}; + if(albuf->ref.load(std::memory_order_relaxed) != 0) + context->throw_error(AL_INVALID_OPERATION, "Modifying in-use buffer {}'s loop points", buffer); - else if(values[0] < 0 || values[0] >= values[1] - || static_cast(values[1]) > albuf->mSampleLen) UNLIKELY - context->setError(AL_INVALID_VALUE, "Invalid loop point range %d -> %d on buffer %u", - values[0], values[1], buffer); - else - { - albuf->mLoopStart = static_cast(values[0]); - albuf->mLoopEnd = static_cast(values[1]); - } - break; + if(vals[0] < 0 || vals[0] >= vals[1] || static_cast(vals[1]) > albuf->mSampleLen) + context->throw_error(AL_INVALID_VALUE, + "Invalid loop point range {} -> {} on buffer {}", vals[0], vals[1], buffer); - default: - context->setError(AL_INVALID_ENUM, "Invalid buffer integer-vector property 0x%04x", param); + albuf->mLoopStart = static_cast(vals[0]); + albuf->mLoopEnd = static_cast(vals[1]); + return; } + + context->throw_error(AL_INVALID_ENUM, "Invalid buffer integer-vector property {:#04x}", + as_unsigned(param)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC3(void, alGetBufferf, ALuint, ALenum, ALfloat*) +AL_API DECL_FUNC3(void, alGetBufferf, ALuint,buffer, ALenum,param, ALfloat*,value) FORCE_ALIGN void AL_APIENTRY alGetBufferfDirect(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *value) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; - - ALbuffer *albuf = LookupBuffer(device, buffer); - if(!albuf) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - else if(!value) UNLIKELY - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else switch(param) +try { + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; + + ALbuffer *albuf{LookupBuffer(device, buffer)}; + if(!albuf) + context->throw_error(AL_INVALID_NAME, "Invalid buffer ID {}", buffer); + if(!value) + context->throw_error(AL_INVALID_VALUE, "NULL pointer"); + + switch(param) { case AL_SEC_LENGTH_SOFT: *value = (albuf->mSampleRate < 1) ? 0.0f : (static_cast(albuf->mSampleLen) / static_cast(albuf->mSampleRate)); - break; - - default: - context->setError(AL_INVALID_ENUM, "Invalid buffer float property 0x%04x", param); + return; } + + context->throw_error(AL_INVALID_ENUM, "Invalid buffer float property {:#04x}", + as_unsigned(param)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC5(void, alGetBuffer3f, ALuint, ALenum, ALfloat*, ALfloat*, ALfloat*) +AL_API DECL_FUNC5(void, alGetBuffer3f, ALuint,buffer, ALenum,param, ALfloat*,value1, ALfloat*,value2, ALfloat*,value3) FORCE_ALIGN void AL_APIENTRY alGetBuffer3fDirect(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; - - if(LookupBuffer(device, buffer) == nullptr) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - else if(!value1 || !value2 || !value3) UNLIKELY - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else switch(param) - { - default: - context->setError(AL_INVALID_ENUM, "Invalid buffer 3-float property 0x%04x", param); - } +try { + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; + + if(LookupBuffer(device, buffer) == nullptr) + context->throw_error(AL_INVALID_NAME, "Invalid buffer ID {}", buffer); + if(!value1 || !value2 || !value3) + context->throw_error(AL_INVALID_VALUE, "NULL pointer"); + + context->throw_error(AL_INVALID_ENUM, "Invalid buffer 3-float property {:#04x}", + as_unsigned(param)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC3(void, alGetBufferfv, ALuint, ALenum, ALfloat*) +AL_API DECL_FUNC3(void, alGetBufferfv, ALuint,buffer, ALenum,param, ALfloat*,values) FORCE_ALIGN void AL_APIENTRY alGetBufferfvDirect(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *values) noexcept -{ +try { switch(param) { case AL_SEC_LENGTH_SOFT: @@ -1173,106 +1251,120 @@ FORCE_ALIGN void AL_APIENTRY alGetBufferfvDirect(ALCcontext *context, ALuint buf return; } - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; - if(LookupBuffer(device, buffer) == nullptr) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - else if(!values) UNLIKELY - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else switch(param) - { - default: - context->setError(AL_INVALID_ENUM, "Invalid buffer float-vector property 0x%04x", param); - } + if(LookupBuffer(device, buffer) == nullptr) + context->throw_error(AL_INVALID_NAME, "Invalid buffer ID {}", buffer); + if(!values) + context->throw_error(AL_INVALID_VALUE, "NULL pointer"); + + context->throw_error(AL_INVALID_ENUM, "Invalid buffer float-vector property {:#04x}", + as_unsigned(param)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC3(void, alGetBufferi, ALuint, ALenum, ALint*) +AL_API DECL_FUNC3(void, alGetBufferi, ALuint,buffer, ALenum,param, ALint*,value) FORCE_ALIGN void AL_APIENTRY alGetBufferiDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint *value) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; - ALbuffer *albuf = LookupBuffer(device, buffer); - if(!albuf) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - else if(!value) UNLIKELY - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else switch(param) +try { + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; + + ALbuffer *albuf{LookupBuffer(device, buffer)}; + if(!albuf) + context->throw_error(AL_INVALID_NAME, "Invalid buffer ID {}", buffer); + if(!value) + context->throw_error(AL_INVALID_VALUE, "NULL pointer"); + + switch(param) { case AL_FREQUENCY: *value = static_cast(albuf->mSampleRate); - break; + return; case AL_BITS: *value = (albuf->mType == FmtIMA4 || albuf->mType == FmtMSADPCM) ? 4 : static_cast(albuf->bytesFromFmt() * 8); - break; + return; case AL_CHANNELS: *value = static_cast(albuf->channelsFromFmt()); - break; + return; case AL_SIZE: *value = albuf->mCallback ? 0 : static_cast(albuf->mData.size()); - break; + return; case AL_BYTE_LENGTH_SOFT: *value = static_cast(albuf->mSampleLen / albuf->mBlockAlign * albuf->blockSizeFromFmt()); - break; + return; case AL_SAMPLE_LENGTH_SOFT: *value = static_cast(albuf->mSampleLen); - break; + return; case AL_UNPACK_BLOCK_ALIGNMENT_SOFT: *value = static_cast(albuf->UnpackAlign); - break; + return; case AL_PACK_BLOCK_ALIGNMENT_SOFT: *value = static_cast(albuf->PackAlign); - break; + return; case AL_AMBISONIC_LAYOUT_SOFT: *value = EnumFromAmbiLayout(albuf->mAmbiLayout); - break; + return; case AL_AMBISONIC_SCALING_SOFT: *value = EnumFromAmbiScaling(albuf->mAmbiScaling); - break; + return; case AL_UNPACK_AMBISONIC_ORDER_SOFT: *value = static_cast(albuf->UnpackAmbiOrder); - break; - - default: - context->setError(AL_INVALID_ENUM, "Invalid buffer integer property 0x%04x", param); + return; } + + context->throw_error(AL_INVALID_ENUM, "Invalid buffer integer property {:#04x}", + as_unsigned(param)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC5(void, alGetBuffer3i, ALuint, ALenum, ALint*, ALint*, ALint*) +AL_API DECL_FUNC5(void, alGetBuffer3i, ALuint,buffer, ALenum,param, ALint*,value1, ALint*,value2, ALint*,value3) FORCE_ALIGN void AL_APIENTRY alGetBuffer3iDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; - if(LookupBuffer(device, buffer) == nullptr) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - else if(!value1 || !value2 || !value3) UNLIKELY - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else switch(param) - { - default: - context->setError(AL_INVALID_ENUM, "Invalid buffer 3-integer property 0x%04x", param); - } +try { + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; + + if(LookupBuffer(device, buffer) == nullptr) + context->throw_error(AL_INVALID_NAME, "Invalid buffer ID {}", buffer); + if(!value1 || !value2 || !value3) + context->throw_error(AL_INVALID_VALUE, "NULL pointer"); + + context->throw_error(AL_INVALID_ENUM, "Invalid buffer 3-integer property {:#04x}", + as_unsigned(param)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC3(void, alGetBufferiv, ALuint, ALenum, ALint*) +AL_API DECL_FUNC3(void, alGetBufferiv, ALuint,buffer, ALenum,param, ALint*,values) FORCE_ALIGN void AL_APIENTRY alGetBufferivDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint *values) noexcept -{ +try { switch(param) { case AL_FREQUENCY: @@ -1291,97 +1383,118 @@ FORCE_ALIGN void AL_APIENTRY alGetBufferivDirect(ALCcontext *context, ALuint buf return; } - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; - ALbuffer *albuf = LookupBuffer(device, buffer); - if(!albuf) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - else if(!values) UNLIKELY - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else switch(param) + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; + + ALbuffer *albuf{LookupBuffer(device, buffer)}; + if(!albuf) + context->throw_error(AL_INVALID_NAME, "Invalid buffer ID {}", buffer); + if(!values) + context->throw_error(AL_INVALID_VALUE, "NULL pointer"); + + switch(param) { case AL_LOOP_POINTS_SOFT: - values[0] = static_cast(albuf->mLoopStart); - values[1] = static_cast(albuf->mLoopEnd); - break; - - default: - context->setError(AL_INVALID_ENUM, "Invalid buffer integer-vector property 0x%04x", param); + auto vals = al::span{values, 2_uz}; + vals[0] = static_cast(albuf->mLoopStart); + vals[1] = static_cast(albuf->mLoopEnd); + return; } + + context->throw_error(AL_INVALID_ENUM, "Invalid buffer integer-vector property {:#04x}", + as_unsigned(param)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNCEXT5(void, alBufferCallback,SOFT, ALuint, ALenum, ALsizei, ALBUFFERCALLBACKTYPESOFT, ALvoid*) +AL_API DECL_FUNCEXT5(void, alBufferCallback,SOFT, ALuint,buffer, ALenum,format, ALsizei,freq, ALBUFFERCALLBACKTYPESOFT,callback, ALvoid*,userptr) FORCE_ALIGN void AL_APIENTRY alBufferCallbackDirectSOFT(ALCcontext *context, ALuint buffer, ALenum format, ALsizei freq, ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; - - ALbuffer *albuf = LookupBuffer(device, buffer); - if(!albuf) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - else if(freq < 1) UNLIKELY - context->setError(AL_INVALID_VALUE, "Invalid sample rate %d", freq); - else if(callback == nullptr) UNLIKELY - context->setError(AL_INVALID_VALUE, "NULL callback"); - else - { - auto usrfmt = DecomposeUserFormat(format); - if(!usrfmt) UNLIKELY - context->setError(AL_INVALID_ENUM, "Invalid format 0x%04x", format); - else - PrepareCallback(context, albuf, freq, usrfmt->channels, usrfmt->type, callback, - userptr); - } +try { + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; + + ALbuffer *albuf{LookupBuffer(device, buffer)}; + if(!albuf) + context->throw_error(AL_INVALID_NAME, "Invalid buffer ID {}", buffer); + if(freq < 1) + context->throw_error(AL_INVALID_VALUE, "Invalid sample rate {}", freq); + if(callback == nullptr) + context->throw_error(AL_INVALID_VALUE, "NULL callback"); + + auto usrfmt = DecomposeUserFormat(format); + if(!usrfmt) + context->throw_error(AL_INVALID_ENUM, "Invalid format {:#04x}", as_unsigned(format)); + + PrepareCallback(context, albuf, freq, usrfmt->channels, usrfmt->type, callback, userptr); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNCEXT3(void, alGetBufferPtr,SOFT, ALuint, ALenum, ALvoid**) +AL_API DECL_FUNCEXT3(void, alGetBufferPtr,SOFT, ALuint,buffer, ALenum,param, ALvoid**,value) FORCE_ALIGN void AL_APIENTRY alGetBufferPtrDirectSOFT(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **value) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; - ALbuffer *albuf = LookupBuffer(device, buffer); - if(!albuf) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - else if(!value) UNLIKELY - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else switch(param) +try { + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; + + ALbuffer *albuf{LookupBuffer(device, buffer)}; + if(!albuf) + context->throw_error(AL_INVALID_NAME, "Invalid buffer ID {}", buffer); + if(!value) + context->throw_error(AL_INVALID_VALUE, "NULL pointer"); + + switch(param) { case AL_BUFFER_CALLBACK_FUNCTION_SOFT: - *value = al::bit_cast(albuf->mCallback); - break; + *value = reinterpret_cast(albuf->mCallback); + return; case AL_BUFFER_CALLBACK_USER_PARAM_SOFT: *value = albuf->mUserData; - break; - - default: - context->setError(AL_INVALID_ENUM, "Invalid buffer pointer property 0x%04x", param); + return; } + + context->throw_error(AL_INVALID_ENUM, "Invalid buffer pointer property {:#04x}", + as_unsigned(param)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNCEXT5(void, alGetBuffer3Ptr,SOFT, ALuint, ALenum, ALvoid**, ALvoid**, ALvoid**) +AL_API DECL_FUNCEXT5(void, alGetBuffer3Ptr,SOFT, ALuint,buffer, ALenum,param, ALvoid**,value1, ALvoid**,value2, ALvoid**,value3) FORCE_ALIGN void AL_APIENTRY alGetBuffer3PtrDirectSOFT(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **value1, ALvoid **value2, ALvoid **value3) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; - if(LookupBuffer(device, buffer) == nullptr) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - else if(!value1 || !value2 || !value3) UNLIKELY - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else switch(param) - { - default: - context->setError(AL_INVALID_ENUM, "Invalid buffer 3-pointer property 0x%04x", param); - } +try { + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; + + if(LookupBuffer(device, buffer) == nullptr) + context->throw_error(AL_INVALID_NAME, "Invalid buffer ID {}", buffer); + if(!value1 || !value2 || !value3) + context->throw_error(AL_INVALID_VALUE, "NULL pointer"); + + context->throw_error(AL_INVALID_ENUM, "Invalid buffer 3-pointer property {:#04x}", + as_unsigned(param)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNCEXT3(void, alGetBufferPtrv,SOFT, ALuint, ALenum, ALvoid**) +AL_API DECL_FUNCEXT3(void, alGetBufferPtrv,SOFT, ALuint,buffer, ALenum,param, ALvoid**,values) FORCE_ALIGN void AL_APIENTRY alGetBufferPtrvDirectSOFT(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **values) noexcept -{ +try { switch(param) { case AL_BUFFER_CALLBACK_FUNCTION_SOFT: @@ -1390,17 +1503,21 @@ FORCE_ALIGN void AL_APIENTRY alGetBufferPtrvDirectSOFT(ALCcontext *context, ALui return; } - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; - if(LookupBuffer(device, buffer) == nullptr) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer); - else if(!values) UNLIKELY - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else switch(param) - { - default: - context->setError(AL_INVALID_ENUM, "Invalid buffer pointer-vector property 0x%04x", param); - } + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; + + if(LookupBuffer(device, buffer) == nullptr) + context->throw_error(AL_INVALID_NAME, "Invalid buffer ID {}", buffer); + if(!values) + context->throw_error(AL_INVALID_VALUE, "NULL pointer"); + + context->throw_error(AL_INVALID_ENUM, "Invalid buffer pointer-vector property {:#04x}", + as_unsigned(param)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } @@ -1444,12 +1561,12 @@ AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum /*format*/) no void ALbuffer::SetName(ALCcontext *context, ALuint id, std::string_view name) { - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; auto buffer = LookupBuffer(device, id); - if(!buffer) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", id); + if(!buffer) + context->throw_error(AL_INVALID_NAME, "Invalid buffer ID {}", id); device->mBufferNames.insert_or_assign(id, name); } @@ -1464,66 +1581,48 @@ BufferSubList::~BufferSubList() while(usemask) { const int idx{al::countr_zero(usemask)}; - std::destroy_at(Buffers+idx); + std::destroy_at(al::to_address(Buffers->begin() + idx)); usemask &= ~(1_u64 << idx); } FreeMask = ~usemask; - al_free(Buffers); + SubListAllocator{}.deallocate(Buffers, 1); Buffers = nullptr; } -#ifdef ALSOFT_EAX -FORCE_ALIGN DECL_FUNC3(ALboolean, EAXSetBufferMode, ALsizei, const ALuint*, ALint) +#if ALSOFT_EAX +FORCE_ALIGN DECL_FUNC3(ALboolean, EAXSetBufferMode, ALsizei,n, const ALuint*,buffers, ALint,value) FORCE_ALIGN ALboolean AL_APIENTRY EAXSetBufferModeDirect(ALCcontext *context, ALsizei n, const ALuint *buffers, ALint value) noexcept -{ -#define EAX_PREFIX "[EAXSetBufferMode] " - +try { if(!eax_g_is_enabled) - { - context->setError(AL_INVALID_OPERATION, EAX_PREFIX "%s", "EAX not enabled."); - return AL_FALSE; - } + context->throw_error(AL_INVALID_OPERATION, "EAX not enabled"); const auto storage = EaxStorageFromEnum(value); if(!storage) - { - context->setError(AL_INVALID_ENUM, EAX_PREFIX "Unsupported X-RAM mode 0x%x", value); - return AL_FALSE; - } + context->throw_error(AL_INVALID_ENUM, "Unsupported X-RAM mode {:#x}", as_unsigned(value)); if(n == 0) return AL_TRUE; if(n < 0) - { - context->setError(AL_INVALID_VALUE, EAX_PREFIX "Buffer count %d out of range", n); - return AL_FALSE; - } - + context->throw_error(AL_INVALID_VALUE, "Buffer count {} out of range", n); if(!buffers) - { - context->setError(AL_INVALID_VALUE, EAX_PREFIX "%s", "Null AL buffers"); - return AL_FALSE; - } + context->throw_error(AL_INVALID_VALUE, "Null AL buffers"); auto device = context->mALDevice.get(); - std::lock_guard device_lock{device->BufferLock}; + std::lock_guard devlock{device->BufferLock}; /* Special-case setting a single buffer, to avoid extraneous allocations. */ if(n == 1) { - const auto bufid = buffers[0]; + const auto bufid = *buffers; if(bufid == AL_NONE) return AL_TRUE; const auto buffer = LookupBuffer(device, bufid); - if(!buffer) UNLIKELY - { - ERR(EAX_PREFIX "Invalid buffer ID %u.\n", bufid); - return AL_FALSE; - } + if(!buffer) + context->throw_error(AL_INVALID_NAME, "Invalid buffer ID {}", bufid); /* TODO: Is the store location allowed to change for in-use buffers, or * only when not set/queued on a source? @@ -1532,13 +1631,10 @@ FORCE_ALIGN ALboolean AL_APIENTRY EAXSetBufferModeDirect(ALCcontext *context, AL if(*storage == EaxStorage::Hardware) { if(!buffer->eax_x_ram_is_hardware - && buffer->OriginalSize > device->eax_x_ram_free_size) UNLIKELY - { - context->setError(AL_OUT_OF_MEMORY, - EAX_PREFIX "Out of X-RAM memory (need: %u, avail: %u)", buffer->OriginalSize, + && buffer->OriginalSize > device->eax_x_ram_free_size) + context->throw_error(AL_OUT_OF_MEMORY, + "Out of X-RAM memory (need: {}, avail: {})", buffer->OriginalSize, device->eax_x_ram_free_size); - return AL_FALSE; - } eax_x_ram_apply(*device, *buffer); } @@ -1550,18 +1646,14 @@ FORCE_ALIGN ALboolean AL_APIENTRY EAXSetBufferModeDirect(ALCcontext *context, AL /* Validate the buffers. */ std::unordered_set buflist; - for(auto i = 0;i < n;++i) + for(const ALuint bufid : al::span{buffers, static_cast(n)}) { - const auto bufid = buffers[i]; if(bufid == AL_NONE) continue; const auto buffer = LookupBuffer(device, bufid); - if(!buffer) UNLIKELY - { - ERR(EAX_PREFIX "Invalid buffer ID %u.\n", bufid); - return AL_FALSE; - } + if(!buffer) + context->throw_error(AL_INVALID_NAME, "Invalid buffer ID {}", bufid); /* TODO: Is the store location allowed to change for in-use buffers, or * only when not set/queued on a source? @@ -1577,22 +1669,16 @@ FORCE_ALIGN ALboolean AL_APIENTRY EAXSetBufferModeDirect(ALCcontext *context, AL { if(!buffer->eax_x_ram_is_hardware) { - if(std::numeric_limits::max()-buffer->OriginalSize < total_needed) UNLIKELY - { - context->setError(AL_OUT_OF_MEMORY, EAX_PREFIX "Size overflow (%u + %zu)\n", + if(std::numeric_limits::max() - buffer->OriginalSize < total_needed) + context->throw_error(AL_OUT_OF_MEMORY, "Size overflow ({} + {})", buffer->OriginalSize, total_needed); - return AL_FALSE; - } + total_needed += buffer->OriginalSize; } } if(total_needed > device->eax_x_ram_free_size) - { - context->setError(AL_OUT_OF_MEMORY, - EAX_PREFIX "Out of X-RAM memory (need: %zu, avail: %u)", total_needed, - device->eax_x_ram_free_size); - return AL_FALSE; - } + context->throw_error(AL_OUT_OF_MEMORY, "Out of X-RAM memory (need: {}, avail: {})", + total_needed, device->eax_x_ram_free_size); } /* Update the mode. */ @@ -1606,41 +1692,40 @@ FORCE_ALIGN ALboolean AL_APIENTRY EAXSetBufferModeDirect(ALCcontext *context, AL } return AL_TRUE; - -#undef EAX_PREFIX +} +catch(al::base_exception&) { + return AL_FALSE; +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); + return AL_FALSE; } -FORCE_ALIGN DECL_FUNC2(ALenum, EAXGetBufferMode, ALuint, ALint*) +FORCE_ALIGN DECL_FUNC2(ALenum, EAXGetBufferMode, ALuint,buffer, ALint*,pReserved) FORCE_ALIGN ALenum AL_APIENTRY EAXGetBufferModeDirect(ALCcontext *context, ALuint buffer, ALint *pReserved) noexcept -{ -#define EAX_PREFIX "[EAXGetBufferMode] " - +try { if(!eax_g_is_enabled) - { - context->setError(AL_INVALID_OPERATION, EAX_PREFIX "%s", "EAX not enabled."); - return AL_NONE; - } + context->throw_error(AL_INVALID_OPERATION, "EAX not enabled."); if(pReserved) - { - context->setError(AL_INVALID_VALUE, EAX_PREFIX "%s", "Non-null reserved parameter"); - return AL_NONE; - } + context->throw_error(AL_INVALID_VALUE, "Non-null reserved parameter"); auto device = context->mALDevice.get(); - std::lock_guard device_lock{device->BufferLock}; + std::lock_guard devlock{device->BufferLock}; const auto al_buffer = LookupBuffer(device, buffer); if(!al_buffer) - { - context->setError(AL_INVALID_NAME, EAX_PREFIX "Invalid buffer ID %u", buffer); - return AL_NONE; - } + context->throw_error(AL_INVALID_NAME, "Invalid buffer ID {}", buffer); return EnumFromEaxStorage(al_buffer->eax_x_ram_mode); - -#undef EAX_PREFIX +} +catch(al::base_exception&) { + return AL_NONE; +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); + return AL_NONE; } #endif // ALSOFT_EAX diff --git a/3rdparty/openal/al/buffer.h b/3rdparty/openal/al/buffer.h index f936cf98493f..187bc6bd1983 100644 --- a/3rdparty/openal/al/buffer.h +++ b/3rdparty/openal/al/buffer.h @@ -1,21 +1,25 @@ #ifndef AL_BUFFER_H #define AL_BUFFER_H +#include "config.h" + +#include #include #include +#include #include +#include #include "AL/al.h" +#include "AL/alc.h" #include "alc/inprogext.h" #include "almalloc.h" -#include "atomic.h" +#include "alnumeric.h" #include "core/buffer_storage.h" #include "vector.h" -#ifdef ALSOFT_EAX -#include "eax/x_ram.h" - +#if ALSOFT_EAX enum class EaxStorage : uint8_t { Automatic, Accessible, @@ -43,19 +47,34 @@ struct ALbuffer : public BufferStorage { ALuint mLoopEnd{0u}; /* Number of times buffer was attached to a source (deletion can only occur when 0) */ - RefCount ref{0u}; + std::atomic ref{0u}; /* Self ID */ ALuint id{0}; static void SetName(ALCcontext *context, ALuint id, std::string_view name); - DISABLE_ALLOC() + DISABLE_ALLOC -#ifdef ALSOFT_EAX +#if ALSOFT_EAX EaxStorage eax_x_ram_mode{EaxStorage::Automatic}; bool eax_x_ram_is_hardware{}; #endif // ALSOFT_EAX }; +struct BufferSubList { + uint64_t FreeMask{~0_u64}; + gsl::owner*> Buffers{nullptr}; + + BufferSubList() noexcept = default; + BufferSubList(const BufferSubList&) = delete; + BufferSubList(BufferSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Buffers{rhs.Buffers} + { rhs.FreeMask = ~0_u64; rhs.Buffers = nullptr; } + ~BufferSubList(); + + BufferSubList& operator=(const BufferSubList&) = delete; + BufferSubList& operator=(BufferSubList&& rhs) noexcept + { std::swap(FreeMask, rhs.FreeMask); std::swap(Buffers, rhs.Buffers); return *this; } +}; + #endif diff --git a/3rdparty/openal/al/debug.cpp b/3rdparty/openal/al/debug.cpp index f5914767a21e..bb742e746925 100644 --- a/3rdparty/openal/al/debug.cpp +++ b/3rdparty/openal/al/debug.cpp @@ -4,31 +4,48 @@ #include #include +#include #include +#include #include #include -#include #include #include +#include +#include #include #include "AL/al.h" +#include "AL/alc.h" +#include "AL/alext.h" #include "alc/context.h" -#include "alc/inprogext.h" +#include "alc/device.h" +#include "alnumeric.h" #include "alspan.h" #include "auxeffectslot.h" #include "buffer.h" +#include "core/except.h" #include "core/logging.h" +#include "core/voice.h" #include "direct_defs.h" #include "effect.h" #include "filter.h" +#include "fmt/core.h" +#include "intrusive_ptr.h" #include "opthelpers.h" #include "source.h" +/* Declared here to prevent compilers from thinking it should be inlined, which + * GCC warns about increasing code size. + */ +DebugGroup::~DebugGroup() = default; + namespace { +using namespace std::string_view_literals; + static_assert(DebugSeverityBase+DebugSeverityCount <= 32, "Too many debug bits"); template @@ -40,7 +57,7 @@ constexpr auto make_array_sequence() { return make_array_sequence(std::make_integer_sequence{}); } -constexpr std::optional GetDebugSource(ALenum source) noexcept +constexpr auto GetDebugSource(ALenum source) noexcept -> std::optional { switch(source) { @@ -53,7 +70,7 @@ constexpr std::optional GetDebugSource(ALenum source) noexcept return std::nullopt; } -constexpr std::optional GetDebugType(ALenum type) noexcept +constexpr auto GetDebugType(ALenum type) noexcept -> std::optional { switch(type) { @@ -70,7 +87,7 @@ constexpr std::optional GetDebugType(ALenum type) noexcept return std::nullopt; } -constexpr std::optional GetDebugSeverity(ALenum severity) noexcept +constexpr auto GetDebugSeverity(ALenum severity) noexcept -> std::optional { switch(severity) { @@ -83,7 +100,7 @@ constexpr std::optional GetDebugSeverity(ALenum severity) noexcep } -ALenum GetDebugSourceEnum(DebugSource source) +constexpr auto GetDebugSourceEnum(DebugSource source) -> ALenum { switch(source) { @@ -93,10 +110,11 @@ ALenum GetDebugSourceEnum(DebugSource source) case DebugSource::Application: return AL_DEBUG_SOURCE_APPLICATION_EXT; case DebugSource::Other: return AL_DEBUG_SOURCE_OTHER_EXT; } - throw std::runtime_error{"Unexpected debug source value "+std::to_string(al::to_underlying(source))}; + throw std::runtime_error{fmt::format("Unexpected debug source value: {}", + int{al::to_underlying(source)})}; } -ALenum GetDebugTypeEnum(DebugType type) +constexpr auto GetDebugTypeEnum(DebugType type) -> ALenum { switch(type) { @@ -110,10 +128,11 @@ ALenum GetDebugTypeEnum(DebugType type) case DebugType::PopGroup: return AL_DEBUG_TYPE_POP_GROUP_EXT; case DebugType::Other: return AL_DEBUG_TYPE_OTHER_EXT; } - throw std::runtime_error{"Unexpected debug type value "+std::to_string(al::to_underlying(type))}; + throw std::runtime_error{fmt::format("Unexpected debug type value: {}", + int{al::to_underlying(type)})}; } -ALenum GetDebugSeverityEnum(DebugSeverity severity) +constexpr auto GetDebugSeverityEnum(DebugSeverity severity) -> ALenum { switch(severity) { @@ -122,50 +141,51 @@ ALenum GetDebugSeverityEnum(DebugSeverity severity) case DebugSeverity::Low: return AL_DEBUG_SEVERITY_LOW_EXT; case DebugSeverity::Notification: return AL_DEBUG_SEVERITY_NOTIFICATION_EXT; } - throw std::runtime_error{"Unexpected debug severity value "+std::to_string(al::to_underlying(severity))}; + throw std::runtime_error{fmt::format("Unexpected debug severity value: {}", + int{al::to_underlying(severity)})}; } -const char *GetDebugSourceName(DebugSource source) +constexpr auto GetDebugSourceName(DebugSource source) noexcept -> std::string_view { switch(source) { - case DebugSource::API: return "API"; - case DebugSource::System: return "Audio System"; - case DebugSource::ThirdParty: return "Third Party"; - case DebugSource::Application: return "Application"; - case DebugSource::Other: return "Other"; + case DebugSource::API: return "API"sv; + case DebugSource::System: return "Audio System"sv; + case DebugSource::ThirdParty: return "Third Party"sv; + case DebugSource::Application: return "Application"sv; + case DebugSource::Other: return "Other"sv; } - return ""; + return ""sv; } -const char *GetDebugTypeName(DebugType type) +constexpr auto GetDebugTypeName(DebugType type) noexcept -> std::string_view { switch(type) { - case DebugType::Error: return "Error"; - case DebugType::DeprecatedBehavior: return "Deprecated Behavior"; - case DebugType::UndefinedBehavior: return "Undefined Behavior"; - case DebugType::Portability: return "Portability"; - case DebugType::Performance: return "Performance"; - case DebugType::Marker: return "Marker"; - case DebugType::PushGroup: return "Push Group"; - case DebugType::PopGroup: return "Pop Group"; - case DebugType::Other: return "Other"; + case DebugType::Error: return "Error"sv; + case DebugType::DeprecatedBehavior: return "Deprecated Behavior"sv; + case DebugType::UndefinedBehavior: return "Undefined Behavior"sv; + case DebugType::Portability: return "Portability"sv; + case DebugType::Performance: return "Performance"sv; + case DebugType::Marker: return "Marker"sv; + case DebugType::PushGroup: return "Push Group"sv; + case DebugType::PopGroup: return "Pop Group"sv; + case DebugType::Other: return "Other"sv; } - return ""; + return ""sv; } -const char *GetDebugSeverityName(DebugSeverity severity) +constexpr auto GetDebugSeverityName(DebugSeverity severity) noexcept -> std::string_view { switch(severity) { - case DebugSeverity::High: return "High"; - case DebugSeverity::Medium: return "Medium"; - case DebugSeverity::Low: return "Low"; - case DebugSeverity::Notification: return "Notification"; + case DebugSeverity::High: return "High"sv; + case DebugSeverity::Medium: return "Medium"sv; + case DebugSeverity::Low: return "Low"sv; + case DebugSeverity::Notification: return "Notification"sv; } - return ""; + return ""sv; } } // namespace @@ -174,13 +194,13 @@ const char *GetDebugSeverityName(DebugSeverity severity) void ALCcontext::sendDebugMessage(std::unique_lock &debuglock, DebugSource source, DebugType type, ALuint id, DebugSeverity severity, std::string_view message) { - if(!mDebugEnabled.load()) UNLIKELY + if(!mDebugEnabled.load(std::memory_order_relaxed)) UNLIKELY return; if(message.length() >= MaxDebugMessageLength) UNLIKELY { - ERR("Debug message too long (%zu >= %d):\n-> %.*s\n", message.length(), - MaxDebugMessageLength, static_cast(message.length()), message.data()); + ERR("Debug message too long ({} >= {}):\n-> {}", message.length(), + MaxDebugMessageLength, message); return; } @@ -215,83 +235,89 @@ void ALCcontext::sendDebugMessage(std::unique_lock &debuglock, Debug mDebugLog.emplace_back(source, type, id, severity, message); else UNLIKELY ERR("Debug message log overflow. Lost message:\n" - " Source: %s\n" - " Type: %s\n" - " ID: %u\n" - " Severity: %s\n" - " Message: \"%.*s\"\n", + " Source: {}\n" + " Type: {}\n" + " ID: {}\n" + " Severity: {}\n" + " Message: \"{}\"", GetDebugSourceName(source), GetDebugTypeName(type), id, - GetDebugSeverityName(severity), static_cast(message.length()), - message.data()); + GetDebugSeverityName(severity), message); } } -FORCE_ALIGN DECL_FUNCEXT2(void, alDebugMessageCallback,EXT, ALDEBUGPROCEXT, void*) +FORCE_ALIGN DECL_FUNCEXT2(void, alDebugMessageCallback,EXT, ALDEBUGPROCEXT,callback, void*,userParam) FORCE_ALIGN void AL_APIENTRY alDebugMessageCallbackDirectEXT(ALCcontext *context, ALDEBUGPROCEXT callback, void *userParam) noexcept { - std::lock_guard _{context->mDebugCbLock}; + std::lock_guard debuglock{context->mDebugCbLock}; context->mDebugCb = callback; context->mDebugParam = userParam; } -FORCE_ALIGN DECL_FUNCEXT6(void, alDebugMessageInsert,EXT, ALenum, ALenum, ALuint, ALenum, ALsizei, const ALchar*) +FORCE_ALIGN DECL_FUNCEXT6(void, alDebugMessageInsert,EXT, ALenum,source, ALenum,type, ALuint,id, ALenum,severity, ALsizei,length, const ALchar*,message) FORCE_ALIGN void AL_APIENTRY alDebugMessageInsertDirectEXT(ALCcontext *context, ALenum source, ALenum type, ALuint id, ALenum severity, ALsizei length, const ALchar *message) noexcept -{ +try { if(!context->mContextFlags.test(ContextFlags::DebugBit)) return; - if(!message) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Null message pointer"); + if(!message) + context->throw_error(AL_INVALID_VALUE, "Null message pointer"); auto msgview = (length < 0) ? std::string_view{message} : std::string_view{message, static_cast(length)}; - if(msgview.length() >= MaxDebugMessageLength) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Debug message too long (%zu >= %d)", - msgview.length(), MaxDebugMessageLength); + if(msgview.size() >= MaxDebugMessageLength) + context->throw_error(AL_INVALID_VALUE, "Debug message too long ({} >= {})", msgview.size(), + MaxDebugMessageLength); auto dsource = GetDebugSource(source); if(!dsource) - return context->setError(AL_INVALID_ENUM, "Invalid debug source 0x%04x", source); + context->throw_error(AL_INVALID_ENUM, "Invalid debug source {:#04x}", as_unsigned(source)); if(*dsource != DebugSource::ThirdParty && *dsource != DebugSource::Application) - return context->setError(AL_INVALID_ENUM, "Debug source 0x%04x not allowed", source); + context->throw_error(AL_INVALID_ENUM, "Debug source {:#04x} not allowed", + as_unsigned(source)); auto dtype = GetDebugType(type); if(!dtype) - return context->setError(AL_INVALID_ENUM, "Invalid debug type 0x%04x", type); + context->throw_error(AL_INVALID_ENUM, "Invalid debug type {:#04x}", as_unsigned(type)); auto dseverity = GetDebugSeverity(severity); if(!dseverity) - return context->setError(AL_INVALID_ENUM, "Invalid debug severity 0x%04x", severity); + context->throw_error(AL_INVALID_ENUM, "Invalid debug severity {:#04x}", + as_unsigned(severity)); context->debugMessage(*dsource, *dtype, id, *dseverity, msgview); } +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); +} -FORCE_ALIGN DECL_FUNCEXT6(void, alDebugMessageControl,EXT, ALenum, ALenum, ALenum, ALsizei, const ALuint*, ALboolean) +FORCE_ALIGN DECL_FUNCEXT6(void, alDebugMessageControl,EXT, ALenum,source, ALenum,type, ALenum,severity, ALsizei,count, const ALuint*,ids, ALboolean,enable) FORCE_ALIGN void AL_APIENTRY alDebugMessageControlDirectEXT(ALCcontext *context, ALenum source, ALenum type, ALenum severity, ALsizei count, const ALuint *ids, ALboolean enable) noexcept -{ +try { if(count > 0) { if(!ids) - return context->setError(AL_INVALID_VALUE, "IDs is null with non-0 count"); + context->throw_error(AL_INVALID_VALUE, "IDs is null with non-0 count"); if(source == AL_DONT_CARE_EXT) - return context->setError(AL_INVALID_OPERATION, + context->throw_error(AL_INVALID_OPERATION, "Debug source cannot be AL_DONT_CARE_EXT with IDs"); if(type == AL_DONT_CARE_EXT) - return context->setError(AL_INVALID_OPERATION, + context->throw_error(AL_INVALID_OPERATION, "Debug type cannot be AL_DONT_CARE_EXT with IDs"); if(severity != AL_DONT_CARE_EXT) - return context->setError(AL_INVALID_OPERATION, + context->throw_error(AL_INVALID_OPERATION, "Debug severity must be AL_DONT_CARE_EXT with IDs"); } if(enable != AL_TRUE && enable != AL_FALSE) - return context->setError(AL_INVALID_ENUM, "Invalid debug enable %d", enable); + context->throw_error(AL_INVALID_ENUM, "Invalid debug enable {}", enable); static constexpr size_t ElemCount{DebugSourceCount + DebugTypeCount + DebugSeverityCount}; static constexpr auto Values = make_array_sequence(); @@ -301,7 +327,8 @@ FORCE_ALIGN void AL_APIENTRY alDebugMessageControlDirectEXT(ALCcontext *context, { auto dsource = GetDebugSource(source); if(!dsource) - return context->setError(AL_INVALID_ENUM, "Invalid debug source 0x%04x", source); + context->throw_error(AL_INVALID_ENUM, "Invalid debug source {:#04x}", + as_unsigned(source)); srcIndices = srcIndices.subspan(al::to_underlying(*dsource), 1); } @@ -310,7 +337,7 @@ FORCE_ALIGN void AL_APIENTRY alDebugMessageControlDirectEXT(ALCcontext *context, { auto dtype = GetDebugType(type); if(!dtype) - return context->setError(AL_INVALID_ENUM, "Invalid debug type 0x%04x", type); + context->throw_error(AL_INVALID_ENUM, "Invalid debug type {:#04x}", as_unsigned(type)); typeIndices = typeIndices.subspan(al::to_underlying(*dtype), 1); } @@ -319,11 +346,12 @@ FORCE_ALIGN void AL_APIENTRY alDebugMessageControlDirectEXT(ALCcontext *context, { auto dseverity = GetDebugSeverity(severity); if(!dseverity) - return context->setError(AL_INVALID_ENUM, "Invalid debug severity 0x%04x", severity); + context->throw_error(AL_INVALID_ENUM, "Invalid debug severity {:#04x}", + as_unsigned(severity)); svrIndices = svrIndices.subspan(al::to_underlying(*dseverity), 1); } - std::lock_guard _{context->mDebugCbLock}; + std::lock_guard debuglock{context->mDebugCbLock}; DebugGroup &debug = context->mDebugGroups.back(); if(count > 0) { @@ -365,36 +393,39 @@ FORCE_ALIGN void AL_APIENTRY alDebugMessageControlDirectEXT(ALCcontext *context, [apply_type](const uint idx){ apply_type(1<= MaxDebugMessageLength) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Debug message too long (%zu >= %d)", - newlen, MaxDebugMessageLength); + if(newlen >= MaxDebugMessageLength) + context->throw_error(AL_INVALID_VALUE, "Debug message too long ({} >= {})", newlen, + MaxDebugMessageLength); length = static_cast(newlen); } - else if(length >= MaxDebugMessageLength) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Debug message too long (%d >= %d)", length, + else if(length >= MaxDebugMessageLength) + context->throw_error(AL_INVALID_VALUE, "Debug message too long ({} >= {})", length, MaxDebugMessageLength); auto dsource = GetDebugSource(source); if(!dsource) - return context->setError(AL_INVALID_ENUM, "Invalid debug source 0x%04x", source); + context->throw_error(AL_INVALID_ENUM, "Invalid debug source {:#04x}", as_unsigned(source)); if(*dsource != DebugSource::ThirdParty && *dsource != DebugSource::Application) - return context->setError(AL_INVALID_ENUM, "Debug source 0x%04x not allowed", source); + context->throw_error(AL_INVALID_ENUM, "Debug source {:#04x} not allowed", + as_unsigned(source)); std::unique_lock debuglock{context->mDebugCbLock}; if(context->mDebugGroups.size() >= MaxDebugGroupDepth) - { - debuglock.unlock(); - return context->setError(AL_STACK_OVERFLOW_EXT, "Pushing too many debug groups"); - } + context->throw_error(AL_STACK_OVERFLOW_EXT, "Pushing too many debug groups"); context->mDebugGroups.emplace_back(*dsource, id, std::string_view{message, static_cast(length)}); @@ -408,17 +439,18 @@ FORCE_ALIGN void AL_APIENTRY alPushDebugGroupDirectEXT(ALCcontext *context, ALen context->sendDebugMessage(debuglock, newback.mSource, DebugType::PushGroup, newback.mId, DebugSeverity::Notification, newback.mMessage); } +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); +} FORCE_ALIGN DECL_FUNCEXT(void, alPopDebugGroup,EXT) FORCE_ALIGN void AL_APIENTRY alPopDebugGroupDirectEXT(ALCcontext *context) noexcept -{ +try { std::unique_lock debuglock{context->mDebugCbLock}; if(context->mDebugGroups.size() <= 1) - { - debuglock.unlock(); - return context->setError(AL_STACK_UNDERFLOW_EXT, - "Attempting to pop the default debug group"); - } + context->throw_error(AL_STACK_UNDERFLOW_EXT, "Attempting to pop the default debug group"); DebugGroup &debug = context->mDebugGroups.back(); const auto source = debug.mSource; @@ -430,89 +462,120 @@ FORCE_ALIGN void AL_APIENTRY alPopDebugGroupDirectEXT(ALCcontext *context) noexc context->sendDebugMessage(debuglock, source, DebugType::PopGroup, id, DebugSeverity::Notification, message); } +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); +} -FORCE_ALIGN DECL_FUNCEXT8(ALuint, alGetDebugMessageLog,EXT, ALuint, ALsizei, ALenum*, ALenum*, ALuint*, ALenum*, ALsizei*, ALchar*) +FORCE_ALIGN DECL_FUNCEXT8(ALuint, alGetDebugMessageLog,EXT, ALuint,count, ALsizei,logBufSize, ALenum*,sources, ALenum*,types, ALuint*,ids, ALenum*,severities, ALsizei*,lengths, ALchar*,logBuf) FORCE_ALIGN ALuint AL_APIENTRY alGetDebugMessageLogDirectEXT(ALCcontext *context, ALuint count, ALsizei logBufSize, ALenum *sources, ALenum *types, ALuint *ids, ALenum *severities, ALsizei *lengths, ALchar *logBuf) noexcept -{ - if(logBufSize < 0) - { - context->setError(AL_INVALID_VALUE, "Negative debug log buffer size"); - return 0; - } - - std::lock_guard _{context->mDebugCbLock}; - ALsizei logBufWritten{0}; +try { + if(logBuf && logBufSize < 0) + context->throw_error(AL_INVALID_VALUE, "Negative debug log buffer size"); + + const auto sourcesSpan = al::span{sources, sources ? count : 0u}; + const auto typesSpan = al::span{types, types ? count : 0u}; + const auto idsSpan = al::span{ids, ids ? count : 0u}; + const auto severitiesSpan = al::span{severities, severities ? count : 0u}; + const auto lengthsSpan = al::span{lengths, lengths ? count : 0u}; + const auto logSpan = al::span{logBuf, logBuf ? static_cast(logBufSize) : 0u}; + + auto sourceiter = sourcesSpan.begin(); + auto typeiter = typesSpan.begin(); + auto iditer = idsSpan.begin(); + auto severityiter = severitiesSpan.begin(); + auto lengthiter = lengthsSpan.begin(); + auto logiter = logSpan.begin(); + + auto debuglock = std::lock_guard{context->mDebugCbLock}; for(ALuint i{0};i < count;++i) { if(context->mDebugLog.empty()) return i; auto &entry = context->mDebugLog.front(); - const size_t tocopy{entry.mMessage.size() + 1}; - if(logBuf) + const auto tocopy = size_t{entry.mMessage.size() + 1}; + if(al::to_address(logiter) != nullptr) { - const size_t avail{static_cast(logBufSize - logBufWritten)}; - if(avail < tocopy) + if(static_cast(std::distance(logiter, logSpan.end())) < tocopy) return i; - std::copy_n(entry.mMessage.data(), tocopy, logBuf+logBufWritten); - logBufWritten += static_cast(tocopy); + logiter = std::copy(entry.mMessage.cbegin(), entry.mMessage.cend(), logiter); + *(logiter++) = '\0'; } - if(sources) sources[i] = GetDebugSourceEnum(entry.mSource); - if(types) types[i] = GetDebugTypeEnum(entry.mType); - if(ids) ids[i] = entry.mId; - if(severities) severities[i] = GetDebugSeverityEnum(entry.mSeverity); - if(lengths) lengths[i] = static_cast(tocopy); + if(al::to_address(sourceiter) != nullptr) + *(sourceiter++) = GetDebugSourceEnum(entry.mSource); + if(al::to_address(typeiter) != nullptr) + *(typeiter++) = GetDebugTypeEnum(entry.mType); + if(al::to_address(iditer) != nullptr) + *(iditer++) = entry.mId; + if(al::to_address(severityiter) != nullptr) + *(severityiter++) = GetDebugSeverityEnum(entry.mSeverity); + if(al::to_address(lengthiter) != nullptr) + *(lengthiter++) = static_cast(tocopy); context->mDebugLog.pop_front(); } return count; } +catch(al::base_exception&) { + return 0; +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); + return 0; +} -FORCE_ALIGN DECL_FUNCEXT4(void, alObjectLabel,EXT, ALenum, ALuint, ALsizei, const ALchar*) +FORCE_ALIGN DECL_FUNCEXT4(void, alObjectLabel,EXT, ALenum,identifier, ALuint,name, ALsizei,length, const ALchar*,label) FORCE_ALIGN void AL_APIENTRY alObjectLabelDirectEXT(ALCcontext *context, ALenum identifier, ALuint name, ALsizei length, const ALchar *label) noexcept -{ - if(!label && length != 0) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Null label pointer"); +try { + if(!label && length != 0) + context->throw_error(AL_INVALID_VALUE, "Null label pointer"); auto objname = (length < 0) ? std::string_view{label} : std::string_view{label, static_cast(length)}; - if(objname.length() >= MaxObjectLabelLength) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Object label length too long (%zu >= %d)", - objname.length(), MaxObjectLabelLength); + if(objname.size() >= MaxObjectLabelLength) + context->throw_error(AL_INVALID_VALUE, "Object label length too long ({} >= {})", + objname.size(), MaxObjectLabelLength); - if(identifier == AL_SOURCE_EXT) - return ALsource::SetName(context, name, objname); - if(identifier == AL_BUFFER) - return ALbuffer::SetName(context, name, objname); - if(identifier == AL_FILTER_EXT) - return ALfilter::SetName(context, name, objname); - if(identifier == AL_EFFECT_EXT) - return ALeffect::SetName(context, name, objname); - if(identifier == AL_AUXILIARY_EFFECT_SLOT_EXT) - return ALeffectslot::SetName(context, name, objname); + switch(identifier) + { + case AL_SOURCE_EXT: ALsource::SetName(context, name, objname); return; + case AL_BUFFER: ALbuffer::SetName(context, name, objname); return; + case AL_FILTER_EXT: ALfilter::SetName(context, name, objname); return; + case AL_EFFECT_EXT: ALeffect::SetName(context, name, objname); return; + case AL_AUXILIARY_EFFECT_SLOT_EXT: ALeffectslot::SetName(context, name, objname); return; + } - return context->setError(AL_INVALID_ENUM, "Invalid name identifier 0x%04x", identifier); + context->throw_error(AL_INVALID_ENUM, "Invalid name identifier {:#04x}", + as_unsigned(identifier)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -FORCE_ALIGN DECL_FUNCEXT5(void, alGetObjectLabel,EXT, ALenum, ALuint, ALsizei, ALsizei*, ALchar*) +FORCE_ALIGN DECL_FUNCEXT5(void, alGetObjectLabel,EXT, ALenum,identifier, ALuint,name, ALsizei,bufSize, ALsizei*,length, ALchar*,label) FORCE_ALIGN void AL_APIENTRY alGetObjectLabelDirectEXT(ALCcontext *context, ALenum identifier, ALuint name, ALsizei bufSize, ALsizei *length, ALchar *label) noexcept -{ - if(bufSize < 0) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Negative label bufSize"); +try { + if(bufSize < 0) + context->throw_error(AL_INVALID_VALUE, "Negative label bufSize"); - if(!label && !length) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Null length and label"); - if(label && bufSize == 0) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Zero label bufSize"); + if(!label && !length) + context->throw_error(AL_INVALID_VALUE, "Null length and label"); + if(label && bufSize == 0) + context->throw_error(AL_INVALID_VALUE, "Zero label bufSize"); - auto copy_name = [name,bufSize,length,label](std::unordered_map &names) + const auto labelOut = al::span{label, label ? static_cast(bufSize) : 0u}; + auto copy_name = [name,length,labelOut](std::unordered_map &names) { std::string_view objname; @@ -520,13 +583,13 @@ FORCE_ALIGN void AL_APIENTRY alGetObjectLabelDirectEXT(ALCcontext *context, ALen if(iter != names.end()) objname = iter->second; - if(!label) - *length = static_cast(objname.length()); + if(labelOut.empty()) + *length = static_cast(objname.size()); else { - const size_t tocopy{minz(objname.length(), static_cast(bufSize)-1)}; - std::memcpy(label, objname.data(), tocopy); - label[tocopy] = '\0'; + const size_t tocopy{std::min(objname.size(), labelOut.size()-1)}; + auto oiter = std::copy_n(objname.cbegin(), tocopy, labelOut.begin()); + *oiter = '\0'; if(length) *length = static_cast(tocopy); } @@ -534,32 +597,38 @@ FORCE_ALIGN void AL_APIENTRY alGetObjectLabelDirectEXT(ALCcontext *context, ALen if(identifier == AL_SOURCE_EXT) { - std::lock_guard _{context->mSourceLock}; + std::lock_guard srclock{context->mSourceLock}; copy_name(context->mSourceNames); } else if(identifier == AL_BUFFER) { - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->BufferLock}; + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->BufferLock}; copy_name(device->mBufferNames); } else if(identifier == AL_FILTER_EXT) { - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->FilterLock}; + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->FilterLock}; copy_name(device->mFilterNames); } else if(identifier == AL_EFFECT_EXT) { - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->EffectLock}; + auto *device = context->mALDevice.get(); + auto buflock = std::lock_guard{device->EffectLock}; copy_name(device->mEffectNames); } else if(identifier == AL_AUXILIARY_EFFECT_SLOT_EXT) { - std::lock_guard _{context->mEffectSlotLock}; + std::lock_guard slotlock{context->mEffectSlotLock}; copy_name(context->mEffectSlotNames); } else - context->setError(AL_INVALID_ENUM, "Invalid name identifier 0x%04x", identifier); + context->throw_error(AL_INVALID_ENUM, "Invalid name identifier {:#04x}", + as_unsigned(identifier)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } diff --git a/3rdparty/openal/al/debug.h b/3rdparty/openal/al/debug.h index 2764bb7f794a..d1792adb41e6 100644 --- a/3rdparty/openal/al/debug.h +++ b/3rdparty/openal/al/debug.h @@ -1,8 +1,9 @@ #ifndef AL_DEBUG_H #define AL_DEBUG_H -#include +#include #include +#include #include using uint = unsigned int; @@ -11,14 +12,14 @@ using uint = unsigned int; /* Somewhat arbitrary. Avoid letting it get out of control if the app enables * logging but never reads it. */ -inline constexpr uint8_t MaxDebugLoggedMessages{64}; -inline constexpr uint16_t MaxDebugMessageLength{1024}; -inline constexpr uint8_t MaxDebugGroupDepth{64}; -inline constexpr uint16_t MaxObjectLabelLength{1024}; +inline constexpr std::uint8_t MaxDebugLoggedMessages{64}; +inline constexpr std::uint16_t MaxDebugMessageLength{1024}; +inline constexpr std::uint8_t MaxDebugGroupDepth{64}; +inline constexpr std::uint16_t MaxObjectLabelLength{1024}; inline constexpr uint DebugSourceBase{0}; -enum class DebugSource : uint8_t { +enum class DebugSource : std::uint8_t { API = 0, System, ThirdParty, @@ -28,7 +29,7 @@ enum class DebugSource : uint8_t { inline constexpr uint DebugSourceCount{5}; inline constexpr uint DebugTypeBase{DebugSourceBase + DebugSourceCount}; -enum class DebugType : uint8_t { +enum class DebugType : std::uint8_t { Error = 0, DeprecatedBehavior, UndefinedBehavior, @@ -42,7 +43,7 @@ enum class DebugType : uint8_t { inline constexpr uint DebugTypeCount{9}; inline constexpr uint DebugSeverityBase{DebugTypeBase + DebugTypeCount}; -enum class DebugSeverity : uint8_t { +enum class DebugSeverity : std::uint8_t { High = 0, Medium, Low, @@ -55,7 +56,7 @@ struct DebugGroup { const DebugSource mSource; std::string mMessage; std::vector mFilters; - std::vector mIdFilters; + std::vector mIdFilters; template DebugGroup(DebugSource source, uint id, T&& message) @@ -63,6 +64,7 @@ struct DebugGroup { { } DebugGroup(const DebugGroup&) = default; DebugGroup(DebugGroup&&) = default; + ~DebugGroup(); }; #endif /* AL_DEBUG_H */ diff --git a/3rdparty/openal/al/direct_defs.h b/3rdparty/openal/al/direct_defs.h index 7526b6112a4d..4119182f0158 100644 --- a/3rdparty/openal/al/direct_defs.h +++ b/3rdparty/openal/al/direct_defs.h @@ -12,116 +12,116 @@ constexpr void DefaultVal() noexcept { } } // namespace detail_ #define DECL_FUNC(R, Name) \ -R AL_APIENTRY Name(void) noexcept \ +auto AL_APIENTRY Name() noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal(); \ return Name##Direct(context.get()); \ } -#define DECL_FUNC1(R, Name, T1) \ -R AL_APIENTRY Name(T1 a) noexcept \ +#define DECL_FUNC1(R, Name, T1,n1) \ +auto AL_APIENTRY Name(T1 n1) noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal(); \ - return Name##Direct(context.get(), a); \ + return Name##Direct(context.get(), n1); \ } -#define DECL_FUNC2(R, Name, T1, T2) \ -R AL_APIENTRY Name(T1 a, T2 b) noexcept \ +#define DECL_FUNC2(R, Name, T1,n1, T2,n2) \ +auto AL_APIENTRY Name(T1 n1, T2 n2) noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal(); \ - return Name##Direct(context.get(), a, b); \ + return Name##Direct(context.get(), n1, n2); \ } -#define DECL_FUNC3(R, Name, T1, T2, T3) \ -R AL_APIENTRY Name(T1 a, T2 b, T3 c) noexcept \ +#define DECL_FUNC3(R, Name, T1,n1, T2,n2, T3,n3) \ +auto AL_APIENTRY Name(T1 n1, T2 n2, T3 n3) noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal(); \ - return Name##Direct(context.get(), a, b, c); \ + return Name##Direct(context.get(), n1, n2, n3); \ } -#define DECL_FUNC4(R, Name, T1, T2, T3, T4) \ -R AL_APIENTRY Name(T1 a, T2 b, T3 c, T4 d) noexcept \ +#define DECL_FUNC4(R, Name, T1,n1, T2,n2, T3,n3, T4,n4) \ +auto AL_APIENTRY Name(T1 n1, T2 n2, T3 n3, T4 n4) noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal(); \ - return Name##Direct(context.get(), a, b, c, d); \ + return Name##Direct(context.get(), n1, n2, n3, n4); \ } -#define DECL_FUNC5(R, Name, T1, T2, T3, T4, T5) \ -R AL_APIENTRY Name(T1 a, T2 b, T3 c, T4 d, T5 e) noexcept \ +#define DECL_FUNC5(R, Name, T1,n1, T2,n2, T3,n3, T4,n4, T5,n5) \ +auto AL_APIENTRY Name(T1 n1, T2 n2, T3 n3, T4 n4, T5 n5) noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal(); \ - return Name##Direct(context.get(), a, b, c, d, e); \ + return Name##Direct(context.get(), n1, n2, n3, n4, n5); \ } #define DECL_FUNCEXT(R, Name,Ext) \ -R AL_APIENTRY Name##Ext(void) noexcept \ +auto AL_APIENTRY Name##Ext() noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal(); \ return Name##Direct##Ext(context.get()); \ } -#define DECL_FUNCEXT1(R, Name,Ext, T1) \ -R AL_APIENTRY Name##Ext(T1 a) noexcept \ +#define DECL_FUNCEXT1(R, Name,Ext, T1,n1) \ +auto AL_APIENTRY Name##Ext(T1 n1) noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal(); \ - return Name##Direct##Ext(context.get(), a); \ + return Name##Direct##Ext(context.get(), n1); \ } -#define DECL_FUNCEXT2(R, Name,Ext, T1, T2) \ -R AL_APIENTRY Name##Ext(T1 a, T2 b) noexcept \ +#define DECL_FUNCEXT2(R, Name,Ext, T1,n1, T2,n2) \ +auto AL_APIENTRY Name##Ext(T1 n1, T2 n2) noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal(); \ - return Name##Direct##Ext(context.get(), a, b); \ + return Name##Direct##Ext(context.get(), n1, n2); \ } -#define DECL_FUNCEXT3(R, Name,Ext, T1, T2, T3) \ -R AL_APIENTRY Name##Ext(T1 a, T2 b, T3 c) noexcept \ +#define DECL_FUNCEXT3(R, Name,Ext, T1,n1, T2,n2, T3,n3) \ +auto AL_APIENTRY Name##Ext(T1 n1, T2 n2, T3 n3) noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal(); \ - return Name##Direct##Ext(context.get(), a, b, c); \ + return Name##Direct##Ext(context.get(), n1, n2, n3); \ } -#define DECL_FUNCEXT4(R, Name,Ext, T1, T2, T3, T4) \ -R AL_APIENTRY Name##Ext(T1 a, T2 b, T3 c, T4 d) noexcept \ +#define DECL_FUNCEXT4(R, Name,Ext, T1,n1, T2,n2, T3,n3, T4,n4) \ +auto AL_APIENTRY Name##Ext(T1 n1, T2 n2, T3 n3, T4 n4) noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal(); \ - return Name##Direct##Ext(context.get(), a, b, c, d); \ + return Name##Direct##Ext(context.get(), n1, n2, n3, n4); \ } -#define DECL_FUNCEXT5(R, Name,Ext, T1, T2, T3, T4, T5) \ -R AL_APIENTRY Name##Ext(T1 a, T2 b, T3 c, T4 d, T5 e) noexcept \ +#define DECL_FUNCEXT5(R, Name,Ext, T1,n1, T2,n2, T3,n3, T4,n4, T5,n5) \ +auto AL_APIENTRY Name##Ext(T1 n1, T2 n2, T3 n3, T4 n4, T5 n5) noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal(); \ - return Name##Direct##Ext(context.get(), a, b, c, d, e); \ + return Name##Direct##Ext(context.get(), n1, n2, n3, n4, n5); \ } -#define DECL_FUNCEXT6(R, Name,Ext, T1, T2, T3, T4, T5, T6) \ -R AL_APIENTRY Name##Ext(T1 a, T2 b, T3 c, T4 d, T5 e, T6 f) noexcept \ +#define DECL_FUNCEXT6(R, Name,Ext, T1,n1, T2,n2, T3,n3, T4,n4, T5,n5, T6,n6) \ +auto AL_APIENTRY Name##Ext(T1 n1, T2 n2, T3 n3, T4 n4, T5 n5, T6 n6) noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal(); \ - return Name##Direct##Ext(context.get(), a, b, c, d, e, f); \ + return Name##Direct##Ext(context.get(), n1, n2, n3, n4, n5, n6); \ } -#define DECL_FUNCEXT8(R, Name,Ext, T1, T2, T3, T4, T5, T6, T7, T8) \ -R AL_APIENTRY Name##Ext(T1 a, T2 b, T3 c, T4 d, T5 e, T6 f, T7 g, T8 h) noexcept \ +#define DECL_FUNCEXT8(R, Name,Ext, T1,n1, T2,n2, T3,n3, T4,n4, T5,n5, T6,n6, T7,n7, T8,n8) \ +auto AL_APIENTRY Name##Ext(T1 n1, T2 n2, T3 n3, T4 n4, T5 n5, T6 n6, T7 n7, T8 n8) noexcept -> R \ { \ auto context = GetContextRef(); \ if(!context) UNLIKELY return detail_::DefaultVal(); \ - return Name##Direct##Ext(context.get(), a, b, c, d, e, f, g, h); \ + return Name##Direct##Ext(context.get(), n1, n2, n3, n4, n5, n6, n7, n8); \ } #endif /* AL_DIRECT_DEFS_H */ diff --git a/3rdparty/openal/al/eax/api.h b/3rdparty/openal/al/eax/api.h index 18d93ef83cc8..925e7b4c732d 100644 --- a/3rdparty/openal/al/eax/api.h +++ b/3rdparty/openal/al/eax/api.h @@ -14,20 +14,23 @@ #include #include #include +#include #ifdef _WIN32 #include #endif #include "AL/al.h" +#include "opthelpers.h" + #ifndef _WIN32 -typedef struct _GUID { +using GUID = struct _GUID { /* NOLINT(*-reserved-identifier) */ std::uint32_t Data1; std::uint16_t Data2; std::uint16_t Data3; - std::uint8_t Data4[8]; -} GUID; + std::array Data4; +}; inline bool operator==(const GUID& lhs, const GUID& rhs) noexcept { return std::memcmp(&lhs, &rhs, sizeof(GUID)) == 0; } @@ -36,11 +39,16 @@ inline bool operator!=(const GUID& lhs, const GUID& rhs) noexcept { return !(lhs == rhs); } #endif // _WIN32 -#define DECL_EQOP(T) \ -friend bool operator==(const T &lhs, const T &rhs) noexcept { return std::memcmp(&lhs, &rhs, sizeof(T)) == 0; } \ -friend bool operator!=(const T &lhs, const T &rhs) noexcept { return !(lhs == rhs); } +/* TODO: This seems to create very inefficient comparisons. C++20 should allow + * creating default comparison operators, avoiding the need for this. + */ +#define DECL_EQOP(T, ...) \ +[[nodiscard]] auto get_members() const noexcept { return std::forward_as_tuple(__VA_ARGS__); } \ +[[nodiscard]] friend bool operator==(const T &lhs, const T &rhs) noexcept \ +{ return lhs.get_members() == rhs.get_members(); } \ +[[nodiscard]] friend bool operator!=(const T &lhs, const T &rhs) noexcept { return !(lhs == rhs); } -extern const GUID DSPROPSETID_EAX_ReverbProperties; +DECL_HIDDEN extern const GUID DSPROPSETID_EAX_ReverbProperties; enum DSPROPERTY_EAX_REVERBPROPERTY : unsigned int { DSPROPERTY_EAX_ALL, @@ -58,7 +66,7 @@ struct EAX_REVERBPROPERTIES { }; // EAX_REVERBPROPERTIES -extern const GUID DSPROPSETID_EAXBUFFER_ReverbProperties; +DECL_HIDDEN extern const GUID DSPROPSETID_EAXBUFFER_ReverbProperties; enum DSPROPERTY_EAXBUFFER_REVERBPROPERTY : unsigned int { DSPROPERTY_EAXBUFFER_ALL, @@ -74,7 +82,7 @@ constexpr auto EAX_BUFFER_MAXREVERBMIX = 1.0F; constexpr auto EAX_REVERBMIX_USEDISTANCE = -1.0F; -extern const GUID DSPROPSETID_EAX20_ListenerProperties; +DECL_HIDDEN extern const GUID DSPROPSETID_EAX20_ListenerProperties; enum DSPROPERTY_EAX20_LISTENERPROPERTY : unsigned int { DSPROPERTY_EAX20LISTENER_NONE, @@ -212,7 +220,7 @@ constexpr auto EAX2LISTENER_DEFAULTFLAGS = EAX2LISTENERFLAGS_DECAYHFLIMIT; -extern const GUID DSPROPSETID_EAX20_BufferProperties; +DECL_HIDDEN extern const GUID DSPROPSETID_EAX20_BufferProperties; enum DSPROPERTY_EAX20_BUFFERPROPERTY : unsigned int { DSPROPERTY_EAX20BUFFER_NONE, @@ -248,9 +256,9 @@ struct EAX20BUFFERPROPERTIES { unsigned long dwFlags; // modifies the behavior of properties }; // EAX20BUFFERPROPERTIES -extern const GUID DSPROPSETID_EAX30_ListenerProperties; +DECL_HIDDEN extern const GUID DSPROPSETID_EAX30_ListenerProperties; -extern const GUID DSPROPSETID_EAX30_BufferProperties; +DECL_HIDDEN extern const GUID DSPROPSETID_EAX30_BufferProperties; constexpr auto EAX_MAX_FXSLOTS = 4; @@ -268,27 +276,29 @@ constexpr auto EAXERR_INCOMPATIBLE_SOURCE_TYPE = -5L; constexpr auto EAXERR_INCOMPATIBLE_EAX_VERSION = -6L; -extern const GUID EAX_NULL_GUID; +DECL_HIDDEN extern const GUID EAX_NULL_GUID; -extern const GUID EAX_PrimaryFXSlotID; +DECL_HIDDEN extern const GUID EAX_PrimaryFXSlotID; struct EAXVECTOR { float x; float y; float z; -}; // EAXVECTOR +}; +[[nodiscard]] inline bool operator==(const EAXVECTOR& lhs, const EAXVECTOR& rhs) noexcept -{ return std::memcmp(&lhs, &rhs, sizeof(EAXVECTOR)) == 0; } +{ return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z; } +[[nodiscard]] inline bool operator!=(const EAXVECTOR& lhs, const EAXVECTOR& rhs) noexcept { return !(lhs == rhs); } -extern const GUID EAXPROPERTYID_EAX40_Context; +DECL_HIDDEN extern const GUID EAXPROPERTYID_EAX40_Context; -extern const GUID EAXPROPERTYID_EAX50_Context; +DECL_HIDDEN extern const GUID EAXPROPERTYID_EAX50_Context; // EAX50 constexpr auto HEADPHONES = 0UL; @@ -362,18 +372,19 @@ constexpr auto EAXCONTEXT_MINMACROFXFACTOR = 0.0F; constexpr auto EAXCONTEXT_MAXMACROFXFACTOR = 1.0F; constexpr auto EAXCONTEXT_DEFAULTMACROFXFACTOR = 0.0F; +constexpr auto EAXCONTEXT_DEFAULTLASTERROR = EAX_OK; -extern const GUID EAXPROPERTYID_EAX40_FXSlot0; -extern const GUID EAXPROPERTYID_EAX50_FXSlot0; -extern const GUID EAXPROPERTYID_EAX40_FXSlot1; -extern const GUID EAXPROPERTYID_EAX50_FXSlot1; -extern const GUID EAXPROPERTYID_EAX40_FXSlot2; -extern const GUID EAXPROPERTYID_EAX50_FXSlot2; -extern const GUID EAXPROPERTYID_EAX40_FXSlot3; -extern const GUID EAXPROPERTYID_EAX50_FXSlot3; +DECL_HIDDEN extern const GUID EAXPROPERTYID_EAX40_FXSlot0; +DECL_HIDDEN extern const GUID EAXPROPERTYID_EAX50_FXSlot0; +DECL_HIDDEN extern const GUID EAXPROPERTYID_EAX40_FXSlot1; +DECL_HIDDEN extern const GUID EAXPROPERTYID_EAX50_FXSlot1; +DECL_HIDDEN extern const GUID EAXPROPERTYID_EAX40_FXSlot2; +DECL_HIDDEN extern const GUID EAXPROPERTYID_EAX50_FXSlot2; +DECL_HIDDEN extern const GUID EAXPROPERTYID_EAX40_FXSlot3; +DECL_HIDDEN extern const GUID EAXPROPERTYID_EAX50_FXSlot3; -extern const GUID EAX40CONTEXT_DEFAULTPRIMARYFXSLOTID; -extern const GUID EAX50CONTEXT_DEFAULTPRIMARYFXSLOTID; +DECL_HIDDEN extern const GUID EAX40CONTEXT_DEFAULTPRIMARYFXSLOTID; +DECL_HIDDEN extern const GUID EAX50CONTEXT_DEFAULTPRIMARYFXSLOTID; enum EAXFXSLOT_PROPERTY : unsigned int { EAXFXSLOT_PARAMETER = 0, @@ -436,8 +447,8 @@ struct EAX50FXSLOTPROPERTIES : public EAX40FXSLOTPROPERTIES { float flOcclusionLFRatio; }; // EAX50FXSLOTPROPERTIES -extern const GUID EAXPROPERTYID_EAX40_Source; -extern const GUID EAXPROPERTYID_EAX50_Source; +DECL_HIDDEN extern const GUID EAXPROPERTYID_EAX40_Source; +DECL_HIDDEN extern const GUID EAXPROPERTYID_EAX50_Source; // Source object properties enum EAXSOURCE_PROPERTY : unsigned int { @@ -654,11 +665,11 @@ struct EAXSPEAKERLEVELPROPERTIES { }; // EAXSPEAKERLEVELPROPERTIES struct EAX40ACTIVEFXSLOTS { - GUID guidActiveFXSlots[EAX40_MAX_ACTIVE_FXSLOTS]; + std::array guidActiveFXSlots; }; // EAX40ACTIVEFXSLOTS struct EAX50ACTIVEFXSLOTS { - GUID guidActiveFXSlots[EAX50_MAX_ACTIVE_FXSLOTS]; + std::array guidActiveFXSlots; }; // EAX50ACTIVEFXSLOTS // Use this structure for EAXSOURCE_OBSTRUCTIONPARAMETERS property. @@ -704,16 +715,16 @@ struct EAXSOURCEEXCLUSIONSENDPROPERTIES { float flExclusionLFRatio; }; // EAXSOURCEEXCLUSIONSENDPROPERTIES -extern const EAX40ACTIVEFXSLOTS EAX40SOURCE_DEFAULTACTIVEFXSLOTID; +DECL_HIDDEN extern const EAX40ACTIVEFXSLOTS EAX40SOURCE_DEFAULTACTIVEFXSLOTID; -extern const EAX50ACTIVEFXSLOTS EAX50SOURCE_3DDEFAULTACTIVEFXSLOTID; +DECL_HIDDEN extern const EAX50ACTIVEFXSLOTS EAX50SOURCE_3DDEFAULTACTIVEFXSLOTID; -extern const EAX50ACTIVEFXSLOTS EAX50SOURCE_2DDEFAULTACTIVEFXSLOTID; +DECL_HIDDEN extern const EAX50ACTIVEFXSLOTS EAX50SOURCE_2DDEFAULTACTIVEFXSLOTID; // EAX Reverb Effect -extern const GUID EAX_REVERB_EFFECT; +DECL_HIDDEN extern const GUID EAX_REVERB_EFFECT; // Reverb effect properties enum EAXREVERB_PROPERTY : unsigned int { @@ -837,7 +848,11 @@ struct EAXREVERBPROPERTIES { float flLFReference; // reference low frequency float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect unsigned long ulFlags; // modifies the behavior of properties - DECL_EQOP(EAXREVERBPROPERTIES) + DECL_EQOP(EAXREVERBPROPERTIES, ulEnvironment, flEnvironmentSize, flEnvironmentDiffusion, lRoom, + lRoomHF, lRoomLF, flDecayTime, flDecayHFRatio, flDecayLFRatio, lReflections, + flReflectionsDelay, vReflectionsPan, lReverb, flReverbDelay, vReverbPan, flEchoTime, + flEchoDepth, flModulationTime, flModulationDepth, flAirAbsorptionHF, flHFReference, + flLFReference, flRoomRolloffFactor, ulFlags) }; // EAXREVERBPROPERTIES @@ -946,18 +961,18 @@ constexpr auto EAXREVERB_DEFAULTFLAGS = using Eax1ReverbPresets = std::array; -extern const Eax1ReverbPresets EAX1REVERB_PRESETS; +DECL_HIDDEN extern const Eax1ReverbPresets EAX1REVERB_PRESETS; using Eax2ReverbPresets = std::array; -extern const Eax2ReverbPresets EAX2REVERB_PRESETS; +DECL_HIDDEN extern const Eax2ReverbPresets EAX2REVERB_PRESETS; using EaxReverbPresets = std::array; -extern const EaxReverbPresets EAXREVERB_PRESETS; +DECL_HIDDEN extern const EaxReverbPresets EAXREVERB_PRESETS; // AGC Compressor Effect -extern const GUID EAX_AGCCOMPRESSOR_EFFECT; +DECL_HIDDEN extern const GUID EAX_AGCCOMPRESSOR_EFFECT; enum EAXAGCCOMPRESSOR_PROPERTY : unsigned int { EAXAGCCOMPRESSOR_NONE, @@ -967,7 +982,7 @@ enum EAXAGCCOMPRESSOR_PROPERTY : unsigned int { struct EAXAGCCOMPRESSORPROPERTIES { unsigned long ulOnOff; // Switch Compressor on or off - DECL_EQOP(EAXAGCCOMPRESSORPROPERTIES) + DECL_EQOP(EAXAGCCOMPRESSORPROPERTIES, ulOnOff) }; // EAXAGCCOMPRESSORPROPERTIES @@ -978,7 +993,7 @@ constexpr auto EAXAGCCOMPRESSOR_DEFAULTONOFF = EAXAGCCOMPRESSOR_MAXONOFF; // Autowah Effect -extern const GUID EAX_AUTOWAH_EFFECT; +DECL_HIDDEN extern const GUID EAX_AUTOWAH_EFFECT; enum EAXAUTOWAH_PROPERTY : unsigned int { EAXAUTOWAH_NONE, @@ -994,7 +1009,7 @@ struct EAXAUTOWAHPROPERTIES { float flReleaseTime; // Release time (seconds) long lResonance; // Resonance (mB) long lPeakLevel; // Peak level (mB) - DECL_EQOP(EAXAUTOWAHPROPERTIES) + DECL_EQOP(EAXAUTOWAHPROPERTIES, flAttackTime, flReleaseTime, lResonance, lPeakLevel) }; // EAXAUTOWAHPROPERTIES @@ -1017,7 +1032,7 @@ constexpr auto EAXAUTOWAH_DEFAULTPEAKLEVEL = 2100L; // Chorus Effect -extern const GUID EAX_CHORUS_EFFECT; +DECL_HIDDEN extern const GUID EAX_CHORUS_EFFECT; enum EAXCHORUS_PROPERTY : unsigned int { EAXCHORUS_NONE, @@ -1042,7 +1057,7 @@ struct EAXCHORUSPROPERTIES { float flDepth; // Depth (0 to 1) float flFeedback; // Feedback (-1 to 1) float flDelay; // Delay (seconds) - DECL_EQOP(EAXCHORUSPROPERTIES) + DECL_EQOP(EAXCHORUSPROPERTIES, ulWaveform, lPhase, flRate, flDepth, flFeedback, flDelay) }; // EAXCHORUSPROPERTIES @@ -1073,7 +1088,7 @@ constexpr auto EAXCHORUS_DEFAULTDELAY = 0.016F; // Distortion Effect -extern const GUID EAX_DISTORTION_EFFECT; +DECL_HIDDEN extern const GUID EAX_DISTORTION_EFFECT; enum EAXDISTORTION_PROPERTY : unsigned int { EAXDISTORTION_NONE, @@ -1091,7 +1106,7 @@ struct EAXDISTORTIONPROPERTIES { float flLowPassCutOff; // Controls the cut-off of the filter pre-distortion (Hz) float flEQCenter; // Controls the center frequency of the EQ post-distortion (Hz) float flEQBandwidth; // Controls the bandwidth of the EQ post-distortion (Hz) - DECL_EQOP(EAXDISTORTIONPROPERTIES) + DECL_EQOP(EAXDISTORTIONPROPERTIES, flEdge, lGain, flLowPassCutOff, flEQCenter, flEQBandwidth) }; // EAXDISTORTIONPROPERTIES @@ -1118,7 +1133,7 @@ constexpr auto EAXDISTORTION_DEFAULTEQBANDWIDTH = 3600.0F; // Echo Effect -extern const GUID EAX_ECHO_EFFECT; +DECL_HIDDEN extern const GUID EAX_ECHO_EFFECT; enum EAXECHO_PROPERTY : unsigned int { EAXECHO_NONE, @@ -1136,7 +1151,7 @@ struct EAXECHOPROPERTIES { float flDamping; // Controls a low-pass filter that dampens the echoes (0 to 1) float flFeedback; // Controls the duration of echo repetition (0 to 1) float flSpread; // Controls the left-right spread of the echoes - DECL_EQOP(EAXECHOPROPERTIES) + DECL_EQOP(EAXECHOPROPERTIES, flDelay, flLRDelay, flDamping, flFeedback, flSpread) }; // EAXECHOPROPERTIES @@ -1163,7 +1178,7 @@ constexpr auto EAXECHO_DEFAULTSPREAD = -1.0F; // Equalizer Effect -extern const GUID EAX_EQUALIZER_EFFECT; +DECL_HIDDEN extern const GUID EAX_EQUALIZER_EFFECT; enum EAXEQUALIZER_PROPERTY : unsigned int { EAXEQUALIZER_NONE, @@ -1191,7 +1206,8 @@ struct EAXEQUALIZERPROPERTIES { float flMid2Width; // (octaves) long lHighGain; // (mB) float flHighCutOff; // (Hz) - DECL_EQOP(EAXEQUALIZERPROPERTIES) + DECL_EQOP(EAXEQUALIZERPROPERTIES, lLowGain, flLowCutOff, lMid1Gain, flMid1Center, flMid1Width, + lMid2Gain, flMid2Center, flMid2Width, lHighGain, flHighCutOff) }; // EAXEQUALIZERPROPERTIES @@ -1238,7 +1254,7 @@ constexpr auto EAXEQUALIZER_DEFAULTHIGHCUTOFF = 6000.0F; // Flanger Effect -extern const GUID EAX_FLANGER_EFFECT; +DECL_HIDDEN extern const GUID EAX_FLANGER_EFFECT; enum EAXFLANGER_PROPERTY : unsigned int { EAXFLANGER_NONE, @@ -1263,7 +1279,7 @@ struct EAXFLANGERPROPERTIES { float flDepth; // Depth (0 to 1) float flFeedback; // Feedback (0 to 1) float flDelay; // Delay (seconds) - DECL_EQOP(EAXFLANGERPROPERTIES) + DECL_EQOP(EAXFLANGERPROPERTIES, ulWaveform, lPhase, flRate, flDepth, flFeedback, flDelay) }; // EAXFLANGERPROPERTIES @@ -1294,7 +1310,7 @@ constexpr auto EAXFLANGER_DEFAULTDELAY = 0.002F; // Frequency Shifter Effect -extern const GUID EAX_FREQUENCYSHIFTER_EFFECT; +DECL_HIDDEN extern const GUID EAX_FREQUENCYSHIFTER_EFFECT; enum EAXFREQUENCYSHIFTER_PROPERTY : unsigned int { EAXFREQUENCYSHIFTER_NONE, @@ -1314,7 +1330,7 @@ struct EAXFREQUENCYSHIFTERPROPERTIES { float flFrequency; // (Hz) unsigned long ulLeftDirection; // see enum above unsigned long ulRightDirection; // see enum above - DECL_EQOP(EAXFREQUENCYSHIFTERPROPERTIES) + DECL_EQOP(EAXFREQUENCYSHIFTERPROPERTIES, flFrequency, ulLeftDirection, ulRightDirection) }; // EAXFREQUENCYSHIFTERPROPERTIES @@ -1333,7 +1349,7 @@ constexpr auto EAXFREQUENCYSHIFTER_DEFAULTRIGHTDIRECTION = EAXFREQUENCYSHIFTER_M // Vocal Morpher Effect -extern const GUID EAX_VOCALMORPHER_EFFECT; +DECL_HIDDEN extern const GUID EAX_VOCALMORPHER_EFFECT; enum EAXVOCALMORPHER_PROPERTY : unsigned int { EAXVOCALMORPHER_NONE, @@ -1393,7 +1409,8 @@ struct EAXVOCALMORPHERPROPERTIES { long lPhonemeBCoarseTuning; // (semitones) unsigned long ulWaveform; // Waveform selector - see enum above float flRate; // (Hz) - DECL_EQOP(EAXVOCALMORPHERPROPERTIES) + DECL_EQOP(EAXVOCALMORPHERPROPERTIES, ulPhonemeA, lPhonemeACoarseTuning, ulPhonemeB, + lPhonemeBCoarseTuning, ulWaveform, flRate) }; // EAXVOCALMORPHERPROPERTIES @@ -1424,7 +1441,7 @@ constexpr auto EAXVOCALMORPHER_DEFAULTRATE = 1.41F; // Pitch Shifter Effect -extern const GUID EAX_PITCHSHIFTER_EFFECT; +DECL_HIDDEN extern const GUID EAX_PITCHSHIFTER_EFFECT; enum EAXPITCHSHIFTER_PROPERTY : unsigned int { EAXPITCHSHIFTER_NONE, @@ -1436,7 +1453,7 @@ enum EAXPITCHSHIFTER_PROPERTY : unsigned int { struct EAXPITCHSHIFTERPROPERTIES { long lCoarseTune; // Amount of pitch shift (semitones) long lFineTune; // Amount of pitch shift (cents) - DECL_EQOP(EAXPITCHSHIFTERPROPERTIES) + DECL_EQOP(EAXPITCHSHIFTERPROPERTIES, lCoarseTune, lFineTune) }; // EAXPITCHSHIFTERPROPERTIES @@ -1451,7 +1468,7 @@ constexpr auto EAXPITCHSHIFTER_DEFAULTFINETUNE = 0L; // Ring Modulator Effect -extern const GUID EAX_RINGMODULATOR_EFFECT; +DECL_HIDDEN extern const GUID EAX_RINGMODULATOR_EFFECT; enum EAXRINGMODULATOR_PROPERTY : unsigned int { EAXRINGMODULATOR_NONE, @@ -1472,7 +1489,7 @@ struct EAXRINGMODULATORPROPERTIES { float flFrequency; // Frequency of modulation (Hz) float flHighPassCutOff; // Cut-off frequency of high-pass filter (Hz) unsigned long ulWaveform; // Waveform selector - see enum above - DECL_EQOP(EAXRINGMODULATORPROPERTIES) + DECL_EQOP(EAXRINGMODULATORPROPERTIES, flFrequency, flHighPassCutOff, ulWaveform) }; // EAXRINGMODULATORPROPERTIES diff --git a/3rdparty/openal/al/eax/call.cpp b/3rdparty/openal/al/eax/call.cpp index 689d5cf1d744..5c69d2e5ec97 100644 --- a/3rdparty/openal/al/eax/call.cpp +++ b/3rdparty/openal/al/eax/call.cpp @@ -15,15 +15,9 @@ class EaxCallException : public EaxException { } // namespace -EaxCall::EaxCall( - EaxCallType type, - const GUID& property_set_guid, - ALuint property_id, - ALuint property_source_id, - ALvoid* property_buffer, - ALuint property_size) - : mCallType{type}, mVersion{0}, mPropertySetId{EaxCallPropertySetId::none} - , mIsDeferred{(property_id & deferred_flag) != 0} +EaxCall::EaxCall(EaxCallType type, const GUID &property_set_guid, ALuint property_id, + ALuint property_source_id, ALvoid *property_buffer, ALuint property_size) + : mCallType{type}, mIsDeferred{(property_id & deferred_flag) != 0} , mPropertyId{property_id & ~deferred_flag}, mPropertySourceId{property_source_id} , mPropertyBuffer{property_buffer}, mPropertyBufferSize{property_size} { @@ -146,23 +140,34 @@ EaxCall::EaxCall( fail("Unsupported property set id."); } - switch(mPropertyId) - { - case EAXCONTEXT_LASTERROR: - case EAXCONTEXT_SPEAKERCONFIG: - case EAXCONTEXT_EAXSESSION: - case EAXFXSLOT_NONE: - case EAXFXSLOT_ALLPARAMETERS: - case EAXFXSLOT_LOADEFFECT: - case EAXFXSLOT_VOLUME: - case EAXFXSLOT_LOCK: - case EAXFXSLOT_FLAGS: - case EAXFXSLOT_OCCLUSION: - case EAXFXSLOT_OCCLUSIONLFRATIO: - // EAX allow to set "defer" flag on immediate-only properties. - // If we don't clear our flag then "applyAllUpdates" in EAX context won't be called. - mIsDeferred = false; - break; + if(mPropertySetId == EaxCallPropertySetId::context) + { + switch(mPropertyId) + { + case EAXCONTEXT_LASTERROR: + case EAXCONTEXT_SPEAKERCONFIG: + case EAXCONTEXT_EAXSESSION: + // EAX allow to set "defer" flag on immediate-only properties. + // If we don't clear our flag then "applyAllUpdates" in EAX context won't be called. + mIsDeferred = false; + break; + } + } + else if(mPropertySetId == EaxCallPropertySetId::fx_slot) + { + switch(mPropertyId) + { + case EAXFXSLOT_NONE: + case EAXFXSLOT_ALLPARAMETERS: + case EAXFXSLOT_LOADEFFECT: + case EAXFXSLOT_VOLUME: + case EAXFXSLOT_LOCK: + case EAXFXSLOT_FLAGS: + case EAXFXSLOT_OCCLUSION: + case EAXFXSLOT_OCCLUSIONLFRATIO: + mIsDeferred = false; + break; + } } if(!mIsDeferred) diff --git a/3rdparty/openal/al/eax/call.h b/3rdparty/openal/al/eax/call.h index 45ff328c04b6..72f96bbe4934 100644 --- a/3rdparty/openal/al/eax/call.h +++ b/3rdparty/openal/al/eax/call.h @@ -31,16 +31,16 @@ class EaxCall { ALvoid* property_buffer, ALuint property_size); - bool is_get() const noexcept { return mCallType == EaxCallType::get; } - bool is_deferred() const noexcept { return mIsDeferred; } - int get_version() const noexcept { return mVersion; } - EaxCallPropertySetId get_property_set_id() const noexcept { return mPropertySetId; } - ALuint get_property_id() const noexcept { return mPropertyId; } - ALuint get_property_al_name() const noexcept { return mPropertySourceId; } - EaxFxSlotIndex get_fx_slot_index() const noexcept { return mFxSlotIndex; } + [[nodiscard]] auto is_get() const noexcept -> bool { return mCallType == EaxCallType::get; } + [[nodiscard]] auto is_deferred() const noexcept -> bool { return mIsDeferred; } + [[nodiscard]] auto get_version() const noexcept -> int { return mVersion; } + [[nodiscard]] auto get_property_set_id() const noexcept -> EaxCallPropertySetId { return mPropertySetId; } + [[nodiscard]] auto get_property_id() const noexcept -> ALuint { return mPropertyId; } + [[nodiscard]] auto get_property_al_name() const noexcept -> ALuint { return mPropertySourceId; } + [[nodiscard]] auto get_fx_slot_index() const noexcept -> EaxFxSlotIndex { return mFxSlotIndex; } template - TValue& get_value() const + [[nodiscard]] auto get_value() const -> TValue& { if(mPropertyBufferSize < sizeof(TValue)) fail_too_small(); @@ -49,32 +49,32 @@ class EaxCall { } template - al::span get_values(size_t max_count) const + [[nodiscard]] auto get_values(size_t max_count) const -> al::span { if(max_count == 0 || mPropertyBufferSize < sizeof(TValue)) fail_too_small(); - const auto count = minz(mPropertyBufferSize / sizeof(TValue), max_count); + const auto count = std::min(mPropertyBufferSize/sizeof(TValue), max_count); return {static_cast(mPropertyBuffer), count}; } template - al::span get_values() const + [[nodiscard]] auto get_values() const -> al::span { return get_values(~0_uz); } template - void set_value(const TValue& value) const + auto set_value(const TValue& value) const -> void { get_value() = value; } private: const EaxCallType mCallType; - int mVersion; - EaxFxSlotIndex mFxSlotIndex; - EaxCallPropertySetId mPropertySetId; + int mVersion{}; + EaxFxSlotIndex mFxSlotIndex{}; + EaxCallPropertySetId mPropertySetId{EaxCallPropertySetId::none}; bool mIsDeferred; const ALuint mPropertyId; diff --git a/3rdparty/openal/al/eax/effect.h b/3rdparty/openal/al/eax/effect.h index afe4d94d0b0f..ac32d0719063 100644 --- a/3rdparty/openal/al/eax/effect.h +++ b/3rdparty/openal/al/eax/effect.h @@ -1,13 +1,12 @@ #ifndef EAX_EFFECT_INCLUDED #define EAX_EFFECT_INCLUDED - #include #include #include -#include "alnumeric.h" #include "AL/al.h" +#include "AL/alext.h" #include "core/effects/base.h" #include "call.h" @@ -36,7 +35,7 @@ struct overloaded : Ts... { using Ts::operator()...; }; template overloaded(Ts...) -> overloaded; -constexpr ALenum EnumFromEaxEffectType(const EaxEffectProps &props) noexcept +constexpr ALenum EnumFromEaxEffectType(const EaxEffectProps &props) { return std::visit(overloaded{ [](const std::monostate&) noexcept { return AL_EFFECT_NULL; }, @@ -100,7 +99,6 @@ struct EaxReverbCommitter { bool commit(const EAX_REVERBPROPERTIES &props); bool commit(const EAX20LISTENERPROPERTIES &props); bool commit(const EAXREVERBPROPERTIES &props); - bool commit(const EaxEffectProps &props); static void SetDefaults(EAX_REVERBPROPERTIES &props); static void SetDefaults(EAX20LISTENERPROPERTIES &props); @@ -110,26 +108,19 @@ struct EaxReverbCommitter { static void Get(const EaxCall &call, const EAX_REVERBPROPERTIES &props); static void Get(const EaxCall &call, const EAX20LISTENERPROPERTIES &props); static void Get(const EaxCall &call, const EAXREVERBPROPERTIES &props); - static void Get(const EaxCall &call, const EaxEffectProps &props); static void Set(const EaxCall &call, EAX_REVERBPROPERTIES &props); static void Set(const EaxCall &call, EAX20LISTENERPROPERTIES &props); static void Set(const EaxCall &call, EAXREVERBPROPERTIES &props); - static void Set(const EaxCall &call, EaxEffectProps &props); - static void translate(const EAX_REVERBPROPERTIES& src, EaxEffectProps& dst) noexcept; - static void translate(const EAX20LISTENERPROPERTIES& src, EaxEffectProps& dst) noexcept; - static void translate(const EAXREVERBPROPERTIES& src, EaxEffectProps& dst) noexcept; + static void translate(const EAX_REVERBPROPERTIES& src, EAXREVERBPROPERTIES& dst) noexcept; + static void translate(const EAX20LISTENERPROPERTIES& src, EAXREVERBPROPERTIES& dst) noexcept; }; template struct EaxCommitter { struct Exception; - EaxCommitter(EaxEffectProps &eaxprops, EffectProps &alprops) - : mEaxProps{eaxprops}, mAlProps{alprops} - { } - EaxEffectProps &mEaxProps; EffectProps &mAlProps; @@ -145,50 +136,155 @@ struct EaxCommitter { [[noreturn]] static void fail_unknown_property_id() { fail(EaxEffectErrorMessages::unknown_property_id()); } - bool commit(const EaxEffectProps &props); +private: + EaxCommitter(EaxEffectProps &eaxprops, EffectProps &alprops) + : mEaxProps{eaxprops}, mAlProps{alprops} + { } - static void SetDefaults(EaxEffectProps &props); - static void Get(const EaxCall &call, const EaxEffectProps &props); - static void Set(const EaxCall &call, EaxEffectProps &props); + friend T; }; struct EaxAutowahCommitter : public EaxCommitter { - using EaxCommitter::EaxCommitter; + template + explicit EaxAutowahCommitter(Args&& ...args) : EaxCommitter{std::forward(args)...} { } + + bool commit(const EAXAUTOWAHPROPERTIES &props); + + static void SetDefaults(EaxEffectProps &props); + static void Get(const EaxCall &call, const EAXAUTOWAHPROPERTIES &props); + static void Set(const EaxCall &call, EAXAUTOWAHPROPERTIES &props); }; struct EaxChorusCommitter : public EaxCommitter { - using EaxCommitter::EaxCommitter; + template + explicit EaxChorusCommitter(Args&& ...args) : EaxCommitter{std::forward(args)...} { } + + bool commit(const EAXCHORUSPROPERTIES &props); + + static void SetDefaults(EaxEffectProps &props); + static void Get(const EaxCall &call, const EAXCHORUSPROPERTIES &props); + static void Set(const EaxCall &call, EAXCHORUSPROPERTIES &props); }; struct EaxCompressorCommitter : public EaxCommitter { - using EaxCommitter::EaxCommitter; + template + explicit EaxCompressorCommitter(Args&& ...args) : EaxCommitter{std::forward(args)...} { } + + bool commit(const EAXAGCCOMPRESSORPROPERTIES &props); + + static void SetDefaults(EaxEffectProps &props); + static void Get(const EaxCall &call, const EAXAGCCOMPRESSORPROPERTIES &props); + static void Set(const EaxCall &call, EAXAGCCOMPRESSORPROPERTIES &props); }; struct EaxDistortionCommitter : public EaxCommitter { - using EaxCommitter::EaxCommitter; + template + explicit EaxDistortionCommitter(Args&& ...args) : EaxCommitter{std::forward(args)...} { } + + bool commit(const EAXDISTORTIONPROPERTIES &props); + + static void SetDefaults(EaxEffectProps &props); + static void Get(const EaxCall &call, const EAXDISTORTIONPROPERTIES &props); + static void Set(const EaxCall &call, EAXDISTORTIONPROPERTIES &props); }; struct EaxEchoCommitter : public EaxCommitter { - using EaxCommitter::EaxCommitter; + template + explicit EaxEchoCommitter(Args&& ...args) : EaxCommitter{std::forward(args)...} { } + + bool commit(const EAXECHOPROPERTIES &props); + + static void SetDefaults(EaxEffectProps &props); + static void Get(const EaxCall &call, const EAXECHOPROPERTIES &props); + static void Set(const EaxCall &call, EAXECHOPROPERTIES &props); }; struct EaxEqualizerCommitter : public EaxCommitter { - using EaxCommitter::EaxCommitter; + template + explicit EaxEqualizerCommitter(Args&& ...args) : EaxCommitter{std::forward(args)...} { } + + bool commit(const EAXEQUALIZERPROPERTIES &props); + + static void SetDefaults(EaxEffectProps &props); + static void Get(const EaxCall &call, const EAXEQUALIZERPROPERTIES &props); + static void Set(const EaxCall &call, EAXEQUALIZERPROPERTIES &props); }; struct EaxFlangerCommitter : public EaxCommitter { - using EaxCommitter::EaxCommitter; + template + explicit EaxFlangerCommitter(Args&& ...args) : EaxCommitter{std::forward(args)...} { } + + bool commit(const EAXFLANGERPROPERTIES &props); + + static void SetDefaults(EaxEffectProps &props); + static void Get(const EaxCall &call, const EAXFLANGERPROPERTIES &props); + static void Set(const EaxCall &call, EAXFLANGERPROPERTIES &props); }; struct EaxFrequencyShifterCommitter : public EaxCommitter { - using EaxCommitter::EaxCommitter; + template + explicit EaxFrequencyShifterCommitter(Args&& ...args) : EaxCommitter{std::forward(args)...} { } + + bool commit(const EAXFREQUENCYSHIFTERPROPERTIES &props); + + static void SetDefaults(EaxEffectProps &props); + static void Get(const EaxCall &call, const EAXFREQUENCYSHIFTERPROPERTIES &props); + static void Set(const EaxCall &call, EAXFREQUENCYSHIFTERPROPERTIES &props); }; struct EaxModulatorCommitter : public EaxCommitter { - using EaxCommitter::EaxCommitter; + template + explicit EaxModulatorCommitter(Args&& ...args) : EaxCommitter{std::forward(args)...} { } + + bool commit(const EAXRINGMODULATORPROPERTIES &props); + + static void SetDefaults(EaxEffectProps &props); + static void Get(const EaxCall &call, const EAXRINGMODULATORPROPERTIES &props); + static void Set(const EaxCall &call, EAXRINGMODULATORPROPERTIES &props); }; struct EaxPitchShifterCommitter : public EaxCommitter { - using EaxCommitter::EaxCommitter; + template + explicit EaxPitchShifterCommitter(Args&& ...args) : EaxCommitter{std::forward(args)...} { } + + bool commit(const EAXPITCHSHIFTERPROPERTIES &props); + + static void SetDefaults(EaxEffectProps &props); + static void Get(const EaxCall &call, const EAXPITCHSHIFTERPROPERTIES &props); + static void Set(const EaxCall &call, EAXPITCHSHIFTERPROPERTIES &props); }; struct EaxVocalMorpherCommitter : public EaxCommitter { - using EaxCommitter::EaxCommitter; + template + explicit EaxVocalMorpherCommitter(Args&& ...args) : EaxCommitter{std::forward(args)...} { } + + bool commit(const EAXVOCALMORPHERPROPERTIES &props); + + static void SetDefaults(EaxEffectProps &props); + static void Get(const EaxCall &call, const EAXVOCALMORPHERPROPERTIES &props); + static void Set(const EaxCall &call, EAXVOCALMORPHERPROPERTIES &props); }; struct EaxNullCommitter : public EaxCommitter { - using EaxCommitter::EaxCommitter; + template + explicit EaxNullCommitter(Args&& ...args) : EaxCommitter{std::forward(args)...} { } + + bool commit(const std::monostate &props); + + static void SetDefaults(EaxEffectProps &props); + static void Get(const EaxCall &call, const std::monostate &props); + static void Set(const EaxCall &call, std::monostate &props); }; +template +struct CommitterFromProps { }; + +template<> struct CommitterFromProps { using type = EaxNullCommitter; }; +template<> struct CommitterFromProps { using type = EaxReverbCommitter; }; +template<> struct CommitterFromProps { using type = EaxChorusCommitter; }; +template<> struct CommitterFromProps { using type = EaxCompressorCommitter; }; +template<> struct CommitterFromProps { using type = EaxAutowahCommitter; }; +template<> struct CommitterFromProps { using type = EaxDistortionCommitter; }; +template<> struct CommitterFromProps { using type = EaxEchoCommitter; }; +template<> struct CommitterFromProps { using type = EaxEqualizerCommitter; }; +template<> struct CommitterFromProps { using type = EaxFlangerCommitter; }; +template<> struct CommitterFromProps { using type = EaxFrequencyShifterCommitter; }; +template<> struct CommitterFromProps { using type = EaxModulatorCommitter; }; +template<> struct CommitterFromProps { using type = EaxPitchShifterCommitter; }; +template<> struct CommitterFromProps { using type = EaxVocalMorpherCommitter; }; + +template +using CommitterFor = typename CommitterFromProps>>::type; + class EaxEffect { public: @@ -196,7 +292,7 @@ class EaxEffect { ~EaxEffect() = default; ALenum al_effect_type_{AL_EFFECT_NULL}; - EffectProps al_effect_props_{}; + EffectProps al_effect_props_; using Props1 = EAX_REVERBPROPERTIES; using Props2 = EAX20LISTENERPROPERTIES; @@ -225,7 +321,7 @@ class EaxEffect { int version_{}; bool changed_{}; - Props4 props_{}; + Props4 props_; State1 state1_{}; State2 state2_{}; State3 state3_{}; @@ -233,51 +329,39 @@ class EaxEffect { State4 state5_{}; - template - void call_set_defaults(Args&& ...args) - { return T::SetDefaults(std::forward(args)...); } - - void call_set_defaults(const ALenum altype, EaxEffectProps &props) + static void call_set_defaults(const ALenum altype, EaxEffectProps &props) { - if(altype == AL_EFFECT_EAXREVERB) - return call_set_defaults(props); - if(altype == AL_EFFECT_CHORUS) - return call_set_defaults(props); - if(altype == AL_EFFECT_AUTOWAH) - return call_set_defaults(props); - if(altype == AL_EFFECT_COMPRESSOR) - return call_set_defaults(props); - if(altype == AL_EFFECT_DISTORTION) - return call_set_defaults(props); - if(altype == AL_EFFECT_ECHO) - return call_set_defaults(props); - if(altype == AL_EFFECT_EQUALIZER) - return call_set_defaults(props); - if(altype == AL_EFFECT_FLANGER) - return call_set_defaults(props); - if(altype == AL_EFFECT_FREQUENCY_SHIFTER) - return call_set_defaults(props); - if(altype == AL_EFFECT_RING_MODULATOR) - return call_set_defaults(props); - if(altype == AL_EFFECT_PITCH_SHIFTER) - return call_set_defaults(props); - if(altype == AL_EFFECT_VOCAL_MORPHER) - return call_set_defaults(props); - return call_set_defaults(props); + switch(altype) + { + case AL_EFFECT_EAXREVERB: return EaxReverbCommitter::SetDefaults(props); + case AL_EFFECT_CHORUS: return EaxChorusCommitter::SetDefaults(props); + case AL_EFFECT_AUTOWAH: return EaxAutowahCommitter::SetDefaults(props); + case AL_EFFECT_COMPRESSOR: return EaxCompressorCommitter::SetDefaults(props); + case AL_EFFECT_DISTORTION: return EaxDistortionCommitter::SetDefaults(props); + case AL_EFFECT_ECHO: return EaxEchoCommitter::SetDefaults(props); + case AL_EFFECT_EQUALIZER: return EaxEqualizerCommitter::SetDefaults(props); + case AL_EFFECT_FLANGER: return EaxFlangerCommitter::SetDefaults(props); + case AL_EFFECT_FREQUENCY_SHIFTER: return EaxFrequencyShifterCommitter::SetDefaults(props); + case AL_EFFECT_RING_MODULATOR: return EaxModulatorCommitter::SetDefaults(props); + case AL_EFFECT_PITCH_SHIFTER: return EaxPitchShifterCommitter::SetDefaults(props); + case AL_EFFECT_VOCAL_MORPHER: return EaxVocalMorpherCommitter::SetDefaults(props); + case AL_EFFECT_NULL: break; + } + return EaxNullCommitter::SetDefaults(props); } template void init() { - call_set_defaults(state1_.d); + EaxReverbCommitter::SetDefaults(state1_.d); state1_.i = state1_.d; - call_set_defaults(state2_.d); + EaxReverbCommitter::SetDefaults(state2_.d); state2_.i = state2_.d; - call_set_defaults(state3_.d); + EaxReverbCommitter::SetDefaults(state3_.d); state3_.i = state3_.d; - call_set_defaults(state4_.d); + T::SetDefaults(state4_.d); state4_.i = state4_.d; - call_set_defaults(state5_.d); + T::SetDefaults(state5_.d); state5_.i = state5_.d; } @@ -285,9 +369,9 @@ class EaxEffect { { switch(eax_version) { - case 1: call_set_defaults(state1_.d); break; - case 2: call_set_defaults(state2_.d); break; - case 3: call_set_defaults(state3_.d); break; + case 1: EaxReverbCommitter::SetDefaults(state1_.d); break; + case 2: EaxReverbCommitter::SetDefaults(state2_.d); break; + case 3: EaxReverbCommitter::SetDefaults(state3_.d); break; case 4: call_set_defaults(altype, state4_.d); break; case 5: call_set_defaults(altype, state5_.d); break; } @@ -295,47 +379,20 @@ class EaxEffect { } -#define EAXCALL(Props, Callable, ...) \ - if(std::holds_alternative(Props)) \ - return Callable(__VA_ARGS__); \ - if(std::holds_alternative(Props)) \ - return Callable(__VA_ARGS__); \ - if(std::holds_alternative(Props)) \ - return Callable(__VA_ARGS__); \ - if(std::holds_alternative(Props)) \ - return Callable(__VA_ARGS__); \ - if(std::holds_alternative(Props)) \ - return Callable(__VA_ARGS__); \ - if(std::holds_alternative(Props)) \ - return Callable(__VA_ARGS__); \ - if(std::holds_alternative(Props)) \ - return Callable(__VA_ARGS__); \ - if(std::holds_alternative(Props)) \ - return Callable(__VA_ARGS__); \ - if(std::holds_alternative(Props)) \ - return Callable(__VA_ARGS__); \ - if(std::holds_alternative(Props)) \ - return Callable(__VA_ARGS__); \ - if(std::holds_alternative(Props)) \ - return Callable(__VA_ARGS__); \ - if(std::holds_alternative(Props)) \ - return Callable(__VA_ARGS__); \ - return Callable(__VA_ARGS__) - - template - static void call_set(Args&& ...args) - { return T::Set(std::forward(args)...); } - static void call_set(const EaxCall &call, EaxEffectProps &props) - { EAXCALL(props, call_set, call, props); } + { + return std::visit([&](auto &arg) + { return CommitterFor::Set(call, arg); }, + props); + } void set(const EaxCall &call) { switch(call.get_version()) { - case 1: call_set(call, state1_.d); break; - case 2: call_set(call, state2_.d); break; - case 3: call_set(call, state3_.d); break; + case 1: EaxReverbCommitter::Set(call, state1_.d); break; + case 2: EaxReverbCommitter::Set(call, state2_.d); break; + case 3: EaxReverbCommitter::Set(call, state3_.d); break; case 4: call_set(call, state4_.d); break; case 5: call_set(call, state5_.d); break; } @@ -343,32 +400,32 @@ class EaxEffect { } - template - static void call_get(Args&& ...args) - { return T::Get(std::forward(args)...); } - static void call_get(const EaxCall &call, const EaxEffectProps &props) - { EAXCALL(props, call_get, call, props); } + { + return std::visit([&](auto &arg) + { return CommitterFor::Get(call, arg); }, + props); + } - void get(const EaxCall &call) + void get(const EaxCall &call) const { switch(call.get_version()) { - case 1: call_get(call, state1_.d); break; - case 2: call_get(call, state2_.d); break; - case 3: call_get(call, state3_.d); break; + case 1: EaxReverbCommitter::Get(call, state1_.d); break; + case 2: EaxReverbCommitter::Get(call, state2_.d); break; + case 3: EaxReverbCommitter::Get(call, state3_.d); break; case 4: call_get(call, state4_.d); break; case 5: call_get(call, state5_.d); break; } } - template - bool call_commit(Args&& ...args) - { return T{props_, al_effect_props_}.commit(std::forward(args)...); } - bool call_commit(const EaxEffectProps &props) - { EAXCALL(props, call_commit, props); } + { + return std::visit([&](auto &arg) + { return CommitterFor{props_, al_effect_props_}.commit(arg); }, + props); + } bool commit(int eax_version) { @@ -383,15 +440,15 @@ class EaxEffect { { case 1: state1_.i = state1_.d; - ret |= call_commit(state1_.d); + ret |= EaxReverbCommitter{props_, al_effect_props_}.commit(state1_.d); break; case 2: state2_.i = state2_.d; - ret |= call_commit(state2_.d); + ret |= EaxReverbCommitter{props_, al_effect_props_}.commit(state2_.d); break; case 3: state3_.i = state3_.d; - ret |= call_commit(state3_.d); + ret |= EaxReverbCommitter{props_, al_effect_props_}.commit(state3_.d); break; case 4: state4_.i = state4_.d; diff --git a/3rdparty/openal/al/eax/exception.h b/3rdparty/openal/al/eax/exception.h index 336654f08af5..64cf7c49e738 100644 --- a/3rdparty/openal/al/eax/exception.h +++ b/3rdparty/openal/al/eax/exception.h @@ -10,9 +10,14 @@ class EaxException : public std::runtime_error { static std::string make_message(std::string_view context, std::string_view message); public: + EaxException() = delete; + EaxException(const EaxException&) = default; + EaxException(EaxException&&) = default; EaxException(std::string_view context, std::string_view message); ~EaxException() override; -}; // EaxException + auto operator=(const EaxException&) -> EaxException& = default; + auto operator=(EaxException&&) -> EaxException& = default; +}; -#endif // !EAX_EXCEPTION_INCLUDED +#endif /* EAX_EXCEPTION_INCLUDED */ diff --git a/3rdparty/openal/al/eax/fx_slots.h b/3rdparty/openal/al/eax/fx_slots.h index 18b2d3ad432e..d2d90b24671c 100644 --- a/3rdparty/openal/al/eax/fx_slots.h +++ b/3rdparty/openal/al/eax/fx_slots.h @@ -6,13 +6,10 @@ #include "al/auxeffectslot.h" -#include "api.h" -#include "call.h" #include "fx_slot_index.h" -class EaxFxSlots -{ +class EaxFxSlots { public: void initialize(ALCcontext& al_context); @@ -25,11 +22,9 @@ class EaxFxSlots } - const ALeffectslot& get( - EaxFxSlotIndex index) const; + [[nodiscard]] auto get(EaxFxSlotIndex index) const -> const ALeffectslot&; - ALeffectslot& get( - EaxFxSlotIndex index); + [[nodiscard]] auto get(EaxFxSlotIndex index) -> ALeffectslot&; private: using Items = std::array; @@ -39,8 +34,7 @@ class EaxFxSlots [[noreturn]] - static void fail( - const char* message); + static void fail(const char* message); void initialize_fx_slots(ALCcontext& al_context); }; // EaxFxSlots diff --git a/3rdparty/openal/al/eax/globals.h b/3rdparty/openal/al/eax/globals.h index ff05d009ff3c..6f3e9078f89a 100644 --- a/3rdparty/openal/al/eax/globals.h +++ b/3rdparty/openal/al/eax/globals.h @@ -3,18 +3,4 @@ inline bool eax_g_is_enabled{true}; -inline constexpr char eax1_ext_name[]{"EAX"}; -inline constexpr char eax2_ext_name[]{"EAX2.0"}; -inline constexpr char eax3_ext_name[]{"EAX3.0"}; -inline constexpr char eax4_ext_name[]{"EAX4.0"}; -inline constexpr char eax5_ext_name[]{"EAX5.0"}; - -inline constexpr char eax_x_ram_ext_name[]{"EAX-RAM"}; - -inline constexpr char eax_eax_set_func_name[]{"EAXSet"}; -inline constexpr char eax_eax_get_func_name[]{"EAXGet"}; - -inline constexpr char eax_eax_set_buffer_mode_func_name[]{"EAXSetBufferMode"}; -inline constexpr char eax_eax_get_buffer_mode_func_name[]{"EAXGetBufferMode"}; - -#endif // !EAX_GLOBALS_INCLUDED +#endif /* EAX_GLOBALS_INCLUDED */ diff --git a/3rdparty/openal/al/eax/utils.cpp b/3rdparty/openal/al/eax/utils.cpp index 53599ac5af74..2eec42772f26 100644 --- a/3rdparty/openal/al/eax/utils.cpp +++ b/3rdparty/openal/al/eax/utils.cpp @@ -17,10 +17,9 @@ void eax_log_exception(std::string_view message) noexcept std::rethrow_exception(exception_ptr); } catch(const std::exception& ex) { - const auto ex_message = ex.what(); - ERR("%.*s %s\n", static_cast(message.length()), message.data(), ex_message); + ERR("{} {}", message, ex.what()); } catch(...) { - ERR("%.*s %s\n", static_cast(message.length()), message.data(), "Generic exception."); + ERR("{} {}", message, "Generic exception."); } } diff --git a/3rdparty/openal/al/eax/x_ram.h b/3rdparty/openal/al/eax/x_ram.h index d10fe6973acb..3616550d2018 100644 --- a/3rdparty/openal/al/eax/x_ram.h +++ b/3rdparty/openal/al/eax/x_ram.h @@ -24,7 +24,6 @@ constexpr auto AL_STORAGE_AUTOMATIC_NAME = "AL_STORAGE_AUTOMATIC"; constexpr auto AL_STORAGE_HARDWARE_NAME = "AL_STORAGE_HARDWARE"; constexpr auto AL_STORAGE_ACCESSIBLE_NAME = "AL_STORAGE_ACCESSIBLE"; - ALboolean AL_APIENTRY EAXSetBufferMode(ALsizei n, const ALuint *buffers, ALint value) noexcept; ALenum AL_APIENTRY EAXGetBufferMode(ALuint buffer, ALint *pReserved) noexcept; diff --git a/3rdparty/openal/al/effect.cpp b/3rdparty/openal/al/effect.cpp index c4b064078a9e..0c549d8ab55c 100644 --- a/3rdparty/openal/al/effect.cpp +++ b/3rdparty/openal/al/effect.cpp @@ -28,9 +28,12 @@ #include #include #include -#include #include +#include +#include +#include #include +#include #include #include "AL/al.h" @@ -39,26 +42,23 @@ #include "AL/efx-presets.h" #include "AL/efx.h" +#include "al/effects/effects.h" #include "albit.h" #include "alc/context.h" #include "alc/device.h" -#include "alc/effects/base.h" #include "alc/inprogext.h" #include "almalloc.h" #include "alnumeric.h" +#include "alspan.h" #include "alstring.h" #include "core/except.h" #include "core/logging.h" #include "direct_defs.h" +#include "intrusive_ptr.h" #include "opthelpers.h" -#ifdef ALSOFT_EAX -#include -#include "eax/exception.h" -#endif // ALSOFT_EAX - -const EffectList gEffectList[16]{ +const std::array gEffectList{{ { "eaxreverb", EAXREVERB_EFFECT, AL_EFFECT_EAXREVERB }, { "reverb", REVERB_EFFECT, AL_EFFECT_REVERB }, { "autowah", AUTOWAH_EFFECT, AL_EFFECT_AUTOWAH }, @@ -74,94 +74,74 @@ const EffectList gEffectList[16]{ { "vmorpher", VMORPHER_EFFECT, AL_EFFECT_VOCAL_MORPHER }, { "dedicated", DEDICATED_EFFECT, AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT }, { "dedicated", DEDICATED_EFFECT, AL_EFFECT_DEDICATED_DIALOGUE }, - { "convolution", CONVOLUTION_EFFECT, AL_EFFECT_CONVOLUTION_REVERB_SOFT }, -}; + { "convolution", CONVOLUTION_EFFECT, AL_EFFECT_CONVOLUTION_SOFT }, +}}; -bool DisabledEffects[MAX_EFFECTS]; - - -effect_exception::effect_exception(ALenum code, const char *msg, ...) : mErrorCode{code} -{ - std::va_list args; - va_start(args, msg); - setMessage(msg, args); - va_end(args); -} -effect_exception::~effect_exception() = default; namespace { -struct EffectPropsItem { - ALenum Type; - const EffectProps &DefaultProps; - const EffectVtable &Vtable; -}; -constexpr EffectPropsItem EffectPropsList[] = { - { AL_EFFECT_NULL, NullEffectProps, NullEffectVtable }, - { AL_EFFECT_EAXREVERB, ReverbEffectProps, ReverbEffectVtable }, - { AL_EFFECT_REVERB, StdReverbEffectProps, StdReverbEffectVtable }, - { AL_EFFECT_AUTOWAH, AutowahEffectProps, AutowahEffectVtable }, - { AL_EFFECT_CHORUS, ChorusEffectProps, ChorusEffectVtable }, - { AL_EFFECT_COMPRESSOR, CompressorEffectProps, CompressorEffectVtable }, - { AL_EFFECT_DISTORTION, DistortionEffectProps, DistortionEffectVtable }, - { AL_EFFECT_ECHO, EchoEffectProps, EchoEffectVtable }, - { AL_EFFECT_EQUALIZER, EqualizerEffectProps, EqualizerEffectVtable }, - { AL_EFFECT_FLANGER, FlangerEffectProps, FlangerEffectVtable }, - { AL_EFFECT_FREQUENCY_SHIFTER, FshifterEffectProps, FshifterEffectVtable }, - { AL_EFFECT_RING_MODULATOR, ModulatorEffectProps, ModulatorEffectVtable }, - { AL_EFFECT_PITCH_SHIFTER, PshifterEffectProps, PshifterEffectVtable }, - { AL_EFFECT_VOCAL_MORPHER, VmorpherEffectProps, VmorpherEffectVtable }, - { AL_EFFECT_DEDICATED_DIALOGUE, DedicatedEffectProps, DedicatedEffectVtable }, - { AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT, DedicatedEffectProps, DedicatedEffectVtable }, - { AL_EFFECT_CONVOLUTION_REVERB_SOFT, ConvolutionEffectProps, ConvolutionEffectVtable }, -}; - - -void ALeffect_setParami(ALeffect *effect, ALenum param, int value) -{ effect->vtab->setParami(&effect->Props, param, value); } -void ALeffect_setParamiv(ALeffect *effect, ALenum param, const int *values) -{ effect->vtab->setParamiv(&effect->Props, param, values); } -void ALeffect_setParamf(ALeffect *effect, ALenum param, float value) -{ effect->vtab->setParamf(&effect->Props, param, value); } -void ALeffect_setParamfv(ALeffect *effect, ALenum param, const float *values) -{ effect->vtab->setParamfv(&effect->Props, param, values); } - -void ALeffect_getParami(const ALeffect *effect, ALenum param, int *value) -{ effect->vtab->getParami(&effect->Props, param, value); } -void ALeffect_getParamiv(const ALeffect *effect, ALenum param, int *values) -{ effect->vtab->getParamiv(&effect->Props, param, values); } -void ALeffect_getParamf(const ALeffect *effect, ALenum param, float *value) -{ effect->vtab->getParamf(&effect->Props, param, value); } -void ALeffect_getParamfv(const ALeffect *effect, ALenum param, float *values) -{ effect->vtab->getParamfv(&effect->Props, param, values); } +using SubListAllocator = al::allocator>; - -const EffectPropsItem *getEffectPropsItemByType(ALenum type) +constexpr auto GetDefaultProps(ALenum type) noexcept -> const EffectProps& { - auto iter = std::find_if(std::begin(EffectPropsList), std::end(EffectPropsList), - [type](const EffectPropsItem &item) noexcept -> bool - { return item.Type == type; }); - return (iter != std::end(EffectPropsList)) ? al::to_address(iter) : nullptr; + switch(type) + { + case AL_EFFECT_NULL: return NullEffectProps; + case AL_EFFECT_EAXREVERB: return ReverbEffectProps; + case AL_EFFECT_REVERB: return StdReverbEffectProps; + case AL_EFFECT_AUTOWAH: return AutowahEffectProps; + case AL_EFFECT_CHORUS: return ChorusEffectProps; + case AL_EFFECT_COMPRESSOR: return CompressorEffectProps; + case AL_EFFECT_DISTORTION: return DistortionEffectProps; + case AL_EFFECT_ECHO: return EchoEffectProps; + case AL_EFFECT_EQUALIZER: return EqualizerEffectProps; + case AL_EFFECT_FLANGER: return FlangerEffectProps; + case AL_EFFECT_FREQUENCY_SHIFTER: return FshifterEffectProps; + case AL_EFFECT_RING_MODULATOR: return ModulatorEffectProps; + case AL_EFFECT_PITCH_SHIFTER: return PshifterEffectProps; + case AL_EFFECT_VOCAL_MORPHER: return VmorpherEffectProps; + case AL_EFFECT_DEDICATED_DIALOGUE: return DedicatedDialogEffectProps; + case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT: return DedicatedLfeEffectProps; + case AL_EFFECT_CONVOLUTION_SOFT: return ConvolutionEffectProps; + } + return NullEffectProps; } -void InitEffectParams(ALeffect *effect, ALenum type) +void InitEffectParams(ALeffect *effect, ALenum type) noexcept { - const EffectPropsItem *item{getEffectPropsItemByType(type)}; - if(item) + switch(type) { - effect->Props = item->DefaultProps; - effect->vtab = &item->Vtable; - } - else - { - effect->Props = EffectProps{}; - effect->vtab = &NullEffectVtable; + case AL_EFFECT_NULL: effect->PropsVariant.emplace(); break; + case AL_EFFECT_EAXREVERB: effect->PropsVariant.emplace(); break; + case AL_EFFECT_REVERB: effect->PropsVariant.emplace(); break; + case AL_EFFECT_AUTOWAH: effect->PropsVariant.emplace(); break; + case AL_EFFECT_CHORUS: effect->PropsVariant.emplace(); break; + case AL_EFFECT_COMPRESSOR: effect->PropsVariant.emplace(); break; + case AL_EFFECT_DISTORTION: effect->PropsVariant.emplace(); break; + case AL_EFFECT_ECHO: effect->PropsVariant.emplace(); break; + case AL_EFFECT_EQUALIZER: effect->PropsVariant.emplace(); break; + case AL_EFFECT_FLANGER: effect->PropsVariant.emplace(); break; + case AL_EFFECT_FREQUENCY_SHIFTER: effect->PropsVariant.emplace(); break; + case AL_EFFECT_RING_MODULATOR: effect->PropsVariant.emplace(); break; + case AL_EFFECT_PITCH_SHIFTER: effect->PropsVariant.emplace(); break; + case AL_EFFECT_VOCAL_MORPHER: effect->PropsVariant.emplace(); break; + case AL_EFFECT_DEDICATED_DIALOGUE: + effect->PropsVariant.emplace(); + break; + case AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT: + effect->PropsVariant.emplace(); + break; + case AL_EFFECT_CONVOLUTION_SOFT: + effect->PropsVariant.emplace(); + break; } + effect->Props = GetDefaultProps(type); effect->type = type; } -bool EnsureEffects(ALCdevice *device, size_t needed) -{ +[[nodiscard]] +auto EnsureEffects(al::Device *device, size_t needed) noexcept -> bool +try { size_t count{std::accumulate(device->EffectList.cbegin(), device->EffectList.cend(), 0_uz, [](size_t cur, const EffectSubList &sublist) noexcept -> size_t { return cur + static_cast(al::popcount(sublist.FreeMask)); })}; @@ -171,21 +151,20 @@ bool EnsureEffects(ALCdevice *device, size_t needed) if(device->EffectList.size() >= 1<<25) UNLIKELY return false; - device->EffectList.emplace_back(); - auto sublist = device->EffectList.end() - 1; - sublist->FreeMask = ~0_u64; - sublist->Effects = static_cast(al_calloc(alignof(ALeffect), sizeof(ALeffect)*64)); - if(!sublist->Effects) UNLIKELY - { - device->EffectList.pop_back(); - return false; - } - count += 64; + EffectSubList sublist{}; + sublist.FreeMask = ~0_u64; + sublist.Effects = SubListAllocator{}.allocate(1); + device->EffectList.emplace_back(std::move(sublist)); + count += std::tuple_size_v; } return true; } +catch(...) { + return false; +} -ALeffect *AllocEffect(ALCdevice *device) +[[nodiscard]] +auto AllocEffect(al::Device *device) noexcept -> ALeffect* { auto sublist = std::find_if(device->EffectList.begin(), device->EffectList.end(), [](const EffectSubList &entry) noexcept -> bool @@ -194,7 +173,7 @@ ALeffect *AllocEffect(ALCdevice *device) auto slidx = static_cast(al::countr_zero(sublist->FreeMask)); ASSUME(slidx < 64); - ALeffect *effect{al::construct_at(sublist->Effects + slidx)}; + ALeffect *effect{al::construct_at(al::to_address(sublist->Effects->begin() + slidx))}; InitEffectParams(effect, AL_EFFECT_NULL); /* Add 1 to avoid effect ID 0. */ @@ -205,7 +184,7 @@ ALeffect *AllocEffect(ALCdevice *device) return effect; } -void FreeEffect(ALCdevice *device, ALeffect *effect) +void FreeEffect(al::Device *device, ALeffect *effect) { device->mEffectNames.erase(effect->id); @@ -218,7 +197,8 @@ void FreeEffect(ALCdevice *device, ALeffect *effect) device->EffectList[lidx].FreeMask |= 1_u64 << slidx; } -inline ALeffect *LookupEffect(ALCdevice *device, ALuint id) +[[nodiscard]] inline +auto LookupEffect(al::Device *device, ALuint id) noexcept -> ALeffect* { const size_t lidx{(id-1) >> 6}; const ALuint slidx{(id-1) & 0x3f}; @@ -228,222 +208,237 @@ inline ALeffect *LookupEffect(ALCdevice *device, ALuint id) EffectSubList &sublist = device->EffectList[lidx]; if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY return nullptr; - return sublist.Effects + slidx; + return al::to_address(sublist.Effects->begin() + slidx); } } // namespace -AL_API DECL_FUNC2(void, alGenEffects, ALsizei, ALuint*) +AL_API DECL_FUNC2(void, alGenEffects, ALsizei,n, ALuint*,effects) FORCE_ALIGN void AL_APIENTRY alGenEffectsDirect(ALCcontext *context, ALsizei n, ALuint *effects) noexcept -{ - if(n < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Generating %d effects", n); +try { + if(n < 0) + context->throw_error(AL_INVALID_VALUE, "Generating {} effects", n); if(n <= 0) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->EffectLock}; - if(!EnsureEffects(device, static_cast(n))) - { - context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d effect%s", n, (n==1)?"":"s"); - return; - } + auto *device = context->mALDevice.get(); + auto effectlock = std::lock_guard{device->EffectLock}; - if(n == 1) LIKELY - { - /* Special handling for the easy and normal case. */ - ALeffect *effect{AllocEffect(device)}; - effects[0] = effect->id; - } - else - { - /* Store the allocated buffer IDs in a separate local list, to avoid - * modifying the user storage in case of failure. - */ - std::vector ids; - ids.reserve(static_cast(n)); - do { - ALeffect *effect{AllocEffect(device)}; - ids.emplace_back(effect->id); - } while(--n); - std::copy(ids.cbegin(), ids.cend(), effects); - } + const al::span eids{effects, static_cast(n)}; + if(!EnsureEffects(device, eids.size())) + context->throw_error(AL_OUT_OF_MEMORY, "Failed to allocate {} effect{}", n, + (n==1) ? "" : "s"); + + std::generate(eids.begin(), eids.end(), [device]{ return AllocEffect(device)->id; }); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC2(void, alDeleteEffects, ALsizei, const ALuint*) +AL_API DECL_FUNC2(void, alDeleteEffects, ALsizei,n, const ALuint*,effects) FORCE_ALIGN void AL_APIENTRY alDeleteEffectsDirect(ALCcontext *context, ALsizei n, const ALuint *effects) noexcept -{ - if(n < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Deleting %d effects", n); +try { + if(n < 0) + context->throw_error(AL_INVALID_VALUE, "Deleting {} effects", n); if(n <= 0) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->EffectLock}; + auto *device = context->mALDevice.get(); + auto effectlock = std::lock_guard{device->EffectLock}; /* First try to find any effects that are invalid. */ auto validate_effect = [device](const ALuint eid) -> bool { return !eid || LookupEffect(device, eid) != nullptr; }; - const ALuint *effects_end = effects + n; - auto inveffect = std::find_if_not(effects, effects_end, validate_effect); - if(inveffect != effects_end) UNLIKELY - { - context->setError(AL_INVALID_NAME, "Invalid effect ID %u", *inveffect); - return; - } + const al::span eids{effects, static_cast(n)}; + auto inveffect = std::find_if_not(eids.begin(), eids.end(), validate_effect); + if(inveffect != eids.end()) + context->throw_error(AL_INVALID_NAME, "Invalid effect ID {}", *inveffect); /* All good. Delete non-0 effect IDs. */ auto delete_effect = [device](ALuint eid) -> void { - ALeffect *effect{eid ? LookupEffect(device, eid) : nullptr}; - if(effect) FreeEffect(device, effect); + if(ALeffect *effect{eid ? LookupEffect(device, eid) : nullptr}) + FreeEffect(device, effect); }; - std::for_each(effects, effects_end, delete_effect); + std::for_each(eids.begin(), eids.end(), delete_effect); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC1(ALboolean, alIsEffect, ALuint) +AL_API DECL_FUNC1(ALboolean, alIsEffect, ALuint,effect) FORCE_ALIGN ALboolean AL_APIENTRY alIsEffectDirect(ALCcontext *context, ALuint effect) noexcept { - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->EffectLock}; + auto *device = context->mALDevice.get(); + auto effectlock = std::lock_guard{device->EffectLock}; if(!effect || LookupEffect(device, effect)) return AL_TRUE; return AL_FALSE; } -AL_API DECL_FUNC3(void, alEffecti, ALuint, ALenum, ALint) +AL_API DECL_FUNC3(void, alEffecti, ALuint,effect, ALenum,param, ALint,value) FORCE_ALIGN void AL_APIENTRY alEffectiDirect(ALCcontext *context, ALuint effect, ALenum param, ALint value) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->EffectLock}; +try { + auto *device = context->mALDevice.get(); + auto effectlock = std::lock_guard{device->EffectLock}; ALeffect *aleffect{LookupEffect(device, effect)}; - if(!aleffect) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect); - else if(param == AL_EFFECT_TYPE) + if(!aleffect) + context->throw_error(AL_INVALID_NAME, "Invalid effect ID {}", effect); + + switch(param) { - bool isOk{value == AL_EFFECT_NULL}; - if(!isOk) + case AL_EFFECT_TYPE: + if(value != AL_EFFECT_NULL) { - for(const EffectList &effectitem : gEffectList) - { - if(value == effectitem.val && !DisabledEffects[effectitem.type]) - { - isOk = true; - break; - } - } + auto check_effect = [value](const EffectList &item) -> bool + { return value == item.val && !DisabledEffects.test(item.type); }; + if(!std::any_of(gEffectList.cbegin(), gEffectList.cend(), check_effect)) + context->throw_error(AL_INVALID_VALUE, "Effect type {:#04x} not supported", + as_unsigned(value)); } - if(isOk) - InitEffectParams(aleffect, value); - else - context->setError(AL_INVALID_VALUE, "Effect type 0x%04x not supported", value); + InitEffectParams(aleffect, value); + return; } - else try + + /* Call the appropriate handler */ + std::visit([context,aleffect,param,value](auto &arg) { - /* Call the appropriate handler */ - ALeffect_setParami(aleffect, param, value); - } - catch(effect_exception &e) { - context->setError(e.errorCode(), "%s", e.what()); - } + using Type = std::remove_cv_t>; + using PropType = typename Type::prop_type; + return arg.SetParami(context, std::get(aleffect->Props), param, value); + }, aleffect->PropsVariant); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC3(void, alEffectiv, ALuint, ALenum, const ALint*) +AL_API DECL_FUNC3(void, alEffectiv, ALuint,effect, ALenum,param, const ALint*,values) FORCE_ALIGN void AL_APIENTRY alEffectivDirect(ALCcontext *context, ALuint effect, ALenum param, const ALint *values) noexcept -{ +try { switch(param) { case AL_EFFECT_TYPE: - alEffectiDirect(context, effect, param, values[0]); + alEffectiDirect(context, effect, param, *values); return; } - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->EffectLock}; + auto *device = context->mALDevice.get(); + auto effectlock = std::lock_guard{device->EffectLock}; ALeffect *aleffect{LookupEffect(device, effect)}; - if(!aleffect) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect); - else try + if(!aleffect) + context->throw_error(AL_INVALID_NAME, "Invalid effect ID {}", effect); + + /* Call the appropriate handler */ + std::visit([context,aleffect,param,values](auto &arg) { - /* Call the appropriate handler */ - ALeffect_setParamiv(aleffect, param, values); - } - catch(effect_exception &e) { - context->setError(e.errorCode(), "%s", e.what()); - } + using Type = std::remove_cv_t>; + using PropType = typename Type::prop_type; + return arg.SetParamiv(context, std::get(aleffect->Props), param, values); + }, aleffect->PropsVariant); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC3(void, alEffectf, ALuint, ALenum, ALfloat) +AL_API DECL_FUNC3(void, alEffectf, ALuint,effect, ALenum,param, ALfloat,value) FORCE_ALIGN void AL_APIENTRY alEffectfDirect(ALCcontext *context, ALuint effect, ALenum param, ALfloat value) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->EffectLock}; +try { + auto *device = context->mALDevice.get(); + auto effectlock = std::lock_guard{device->EffectLock}; ALeffect *aleffect{LookupEffect(device, effect)}; - if(!aleffect) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect); - else try + if(!aleffect) + context->throw_error(AL_INVALID_NAME, "Invalid effect ID {}", effect); + + /* Call the appropriate handler */ + std::visit([context,aleffect,param,value](auto &arg) { - /* Call the appropriate handler */ - ALeffect_setParamf(aleffect, param, value); - } - catch(effect_exception &e) { - context->setError(e.errorCode(), "%s", e.what()); - } + using Type = std::remove_cv_t>; + using PropType = typename Type::prop_type; + return arg.SetParamf(context, std::get(aleffect->Props), param, value); + }, aleffect->PropsVariant); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC3(void, alEffectfv, ALuint, ALenum, const ALfloat*) +AL_API DECL_FUNC3(void, alEffectfv, ALuint,effect, ALenum,param, const ALfloat*,values) FORCE_ALIGN void AL_APIENTRY alEffectfvDirect(ALCcontext *context, ALuint effect, ALenum param, const ALfloat *values) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->EffectLock}; +try { + auto *device = context->mALDevice.get(); + auto effectlock = std::lock_guard{device->EffectLock}; ALeffect *aleffect{LookupEffect(device, effect)}; - if(!aleffect) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect); - else try + if(!aleffect) + context->throw_error(AL_INVALID_NAME, "Invalid effect ID {}", effect); + + /* Call the appropriate handler */ + std::visit([context,aleffect,param,values](auto &arg) { - /* Call the appropriate handler */ - ALeffect_setParamfv(aleffect, param, values); - } - catch(effect_exception &e) { - context->setError(e.errorCode(), "%s", e.what()); - } + using Type = std::remove_cv_t>; + using PropType = typename Type::prop_type; + return arg.SetParamfv(context, std::get(aleffect->Props), param, values); + }, aleffect->PropsVariant); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC3(void, alGetEffecti, ALuint, ALenum, ALint*) +AL_API DECL_FUNC3(void, alGetEffecti, ALuint,effect, ALenum,param, ALint*,value) FORCE_ALIGN void AL_APIENTRY alGetEffectiDirect(ALCcontext *context, ALuint effect, ALenum param, ALint *value) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->EffectLock}; +try { + auto *device = context->mALDevice.get(); + auto effectlock = std::lock_guard{device->EffectLock}; const ALeffect *aleffect{LookupEffect(device, effect)}; - if(!aleffect) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect); - else if(param == AL_EFFECT_TYPE) - *value = aleffect->type; - else try + if(!aleffect) + context->throw_error(AL_INVALID_NAME, "Invalid effect ID {}", effect); + + switch(param) { - /* Call the appropriate handler */ - ALeffect_getParami(aleffect, param, value); - } - catch(effect_exception &e) { - context->setError(e.errorCode(), "%s", e.what()); + case AL_EFFECT_TYPE: + *value = aleffect->type; + return; } + + /* Call the appropriate handler */ + std::visit([context,aleffect,param,value](auto &arg) + { + using Type = std::remove_cv_t>; + using PropType = typename Type::prop_type; + return arg.GetParami(context, std::get(aleffect->Props), param, value); + }, aleffect->PropsVariant); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC3(void, alGetEffectiv, ALuint, ALenum, ALint*) +AL_API DECL_FUNC3(void, alGetEffectiv, ALuint,effect, ALenum,param, ALint*,values) FORCE_ALIGN void AL_APIENTRY alGetEffectivDirect(ALCcontext *context, ALuint effect, ALenum param, ALint *values) noexcept -{ +try { switch(param) { case AL_EFFECT_TYPE: @@ -451,60 +446,75 @@ FORCE_ALIGN void AL_APIENTRY alGetEffectivDirect(ALCcontext *context, ALuint eff return; } - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->EffectLock}; + auto *device = context->mALDevice.get(); + auto effectlock = std::lock_guard{device->EffectLock}; const ALeffect *aleffect{LookupEffect(device, effect)}; - if(!aleffect) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect); - else try + if(!aleffect) + context->throw_error(AL_INVALID_NAME, "Invalid effect ID {}", effect); + + /* Call the appropriate handler */ + std::visit([context,aleffect,param,values](auto &arg) { - /* Call the appropriate handler */ - ALeffect_getParamiv(aleffect, param, values); - } - catch(effect_exception &e) { - context->setError(e.errorCode(), "%s", e.what()); - } + using Type = std::remove_cv_t>; + using PropType = typename Type::prop_type; + return arg.GetParamiv(context, std::get(aleffect->Props), param, values); + }, aleffect->PropsVariant); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC3(void, alGetEffectf, ALuint, ALenum, ALfloat*) +AL_API DECL_FUNC3(void, alGetEffectf, ALuint,effect, ALenum,param, ALfloat*,value) FORCE_ALIGN void AL_APIENTRY alGetEffectfDirect(ALCcontext *context, ALuint effect, ALenum param, ALfloat *value) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->EffectLock}; +try { + auto *device = context->mALDevice.get(); + auto effectlock = std::lock_guard{device->EffectLock}; const ALeffect *aleffect{LookupEffect(device, effect)}; - if(!aleffect) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect); - else try + if(!aleffect) + context->throw_error(AL_INVALID_NAME, "Invalid effect ID {}", effect); + + /* Call the appropriate handler */ + std::visit([context,aleffect,param,value](auto &arg) { - /* Call the appropriate handler */ - ALeffect_getParamf(aleffect, param, value); - } - catch(effect_exception &e) { - context->setError(e.errorCode(), "%s", e.what()); - } + using Type = std::remove_cv_t>; + using PropType = typename Type::prop_type; + return arg.GetParamf(context, std::get(aleffect->Props), param, value); + }, aleffect->PropsVariant); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC3(void, alGetEffectfv, ALuint, ALenum, ALfloat*) +AL_API DECL_FUNC3(void, alGetEffectfv, ALuint,effect, ALenum,param, ALfloat*,values) FORCE_ALIGN void AL_APIENTRY alGetEffectfvDirect(ALCcontext *context, ALuint effect, ALenum param, ALfloat *values) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->EffectLock}; +try { + auto *device = context->mALDevice.get(); + auto effectlock = std::lock_guard{device->EffectLock}; const ALeffect *aleffect{LookupEffect(device, effect)}; - if(!aleffect) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid effect ID %u", effect); - else try + if(!aleffect) + context->throw_error(AL_INVALID_NAME, "Invalid effect ID {}", effect); + + /* Call the appropriate handler */ + std::visit([context,aleffect,param,values](auto &arg) { - /* Call the appropriate handler */ - ALeffect_getParamfv(aleffect, param, values); - } - catch(effect_exception &e) { - context->setError(e.errorCode(), "%s", e.what()); - } + using Type = std::remove_cv_t>; + using PropType = typename Type::prop_type; + return arg.GetParamfv(context, std::get(aleffect->Props), param, values); + }, aleffect->PropsVariant); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } @@ -515,12 +525,12 @@ void InitEffect(ALeffect *effect) void ALeffect::SetName(ALCcontext* context, ALuint id, std::string_view name) { - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->EffectLock}; + auto *device = context->mALDevice.get(); + auto effectlock = std::lock_guard{device->EffectLock}; auto effect = LookupEffect(device, id); - if(!effect) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid effect ID %u", id); + if(!effect) + context->throw_error(AL_INVALID_NAME, "Invalid effect ID {}", id); device->mEffectNames.insert_or_assign(id, name); } @@ -535,20 +545,21 @@ EffectSubList::~EffectSubList() while(usemask) { const int idx{al::countr_zero(usemask)}; - std::destroy_at(Effects+idx); + std::destroy_at(al::to_address(Effects->begin()+idx)); usemask &= ~(1_u64 << idx); } FreeMask = ~usemask; - al_free(Effects); + SubListAllocator{}.deallocate(Effects, 1); Effects = nullptr; } -#define DECL(x) { #x, EFX_REVERB_PRESET_##x } -static const struct { - const char name[32]; +struct EffectPreset { + const char name[32]; /* NOLINT(*-avoid-c-arrays) */ EFXEAXREVERBPROPERTIES props; -} reverblist[] = { +}; +#define DECL(x) EffectPreset{#x, EFX_REVERB_PRESET_##x} +static constexpr std::array reverblist{ DECL(GENERIC), DECL(PADDEDCELL), DECL(ROOM), @@ -678,61 +689,62 @@ static const struct { }; #undef DECL -void LoadReverbPreset(const char *name, ALeffect *effect) +void LoadReverbPreset(const std::string_view name, ALeffect *effect) { - if(al::strcasecmp(name, "NONE") == 0) + using namespace std::string_view_literals; + + if(al::case_compare(name, "NONE"sv) == 0) { InitEffectParams(effect, AL_EFFECT_NULL); - TRACE("Loading reverb '%s'\n", "NONE"); + TRACE("Loading reverb '{}'", "NONE"); return; } - if(!DisabledEffects[EAXREVERB_EFFECT]) + if(!DisabledEffects.test(EAXREVERB_EFFECT)) InitEffectParams(effect, AL_EFFECT_EAXREVERB); - else if(!DisabledEffects[REVERB_EFFECT]) + else if(!DisabledEffects.test(REVERB_EFFECT)) InitEffectParams(effect, AL_EFFECT_REVERB); else InitEffectParams(effect, AL_EFFECT_NULL); for(const auto &reverbitem : reverblist) { - const EFXEAXREVERBPROPERTIES *props; - - if(al::strcasecmp(name, reverbitem.name) != 0) + if(al::case_compare(name, std::data(reverbitem.name)) != 0) continue; - TRACE("Loading reverb '%s'\n", reverbitem.name); - props = &reverbitem.props; - effect->Props.Reverb.Density = props->flDensity; - effect->Props.Reverb.Diffusion = props->flDiffusion; - effect->Props.Reverb.Gain = props->flGain; - effect->Props.Reverb.GainHF = props->flGainHF; - effect->Props.Reverb.GainLF = props->flGainLF; - effect->Props.Reverb.DecayTime = props->flDecayTime; - effect->Props.Reverb.DecayHFRatio = props->flDecayHFRatio; - effect->Props.Reverb.DecayLFRatio = props->flDecayLFRatio; - effect->Props.Reverb.ReflectionsGain = props->flReflectionsGain; - effect->Props.Reverb.ReflectionsDelay = props->flReflectionsDelay; - effect->Props.Reverb.ReflectionsPan[0] = props->flReflectionsPan[0]; - effect->Props.Reverb.ReflectionsPan[1] = props->flReflectionsPan[1]; - effect->Props.Reverb.ReflectionsPan[2] = props->flReflectionsPan[2]; - effect->Props.Reverb.LateReverbGain = props->flLateReverbGain; - effect->Props.Reverb.LateReverbDelay = props->flLateReverbDelay; - effect->Props.Reverb.LateReverbPan[0] = props->flLateReverbPan[0]; - effect->Props.Reverb.LateReverbPan[1] = props->flLateReverbPan[1]; - effect->Props.Reverb.LateReverbPan[2] = props->flLateReverbPan[2]; - effect->Props.Reverb.EchoTime = props->flEchoTime; - effect->Props.Reverb.EchoDepth = props->flEchoDepth; - effect->Props.Reverb.ModulationTime = props->flModulationTime; - effect->Props.Reverb.ModulationDepth = props->flModulationDepth; - effect->Props.Reverb.AirAbsorptionGainHF = props->flAirAbsorptionGainHF; - effect->Props.Reverb.HFReference = props->flHFReference; - effect->Props.Reverb.LFReference = props->flLFReference; - effect->Props.Reverb.RoomRolloffFactor = props->flRoomRolloffFactor; - effect->Props.Reverb.DecayHFLimit = props->iDecayHFLimit ? AL_TRUE : AL_FALSE; + TRACE("Loading reverb '{}'", std::data(reverbitem.name)); + const auto &props = reverbitem.props; + auto &dst = std::get(effect->Props); + dst.Density = props.flDensity; + dst.Diffusion = props.flDiffusion; + dst.Gain = props.flGain; + dst.GainHF = props.flGainHF; + dst.GainLF = props.flGainLF; + dst.DecayTime = props.flDecayTime; + dst.DecayHFRatio = props.flDecayHFRatio; + dst.DecayLFRatio = props.flDecayLFRatio; + dst.ReflectionsGain = props.flReflectionsGain; + dst.ReflectionsDelay = props.flReflectionsDelay; + dst.ReflectionsPan[0] = props.flReflectionsPan[0]; + dst.ReflectionsPan[1] = props.flReflectionsPan[1]; + dst.ReflectionsPan[2] = props.flReflectionsPan[2]; + dst.LateReverbGain = props.flLateReverbGain; + dst.LateReverbDelay = props.flLateReverbDelay; + dst.LateReverbPan[0] = props.flLateReverbPan[0]; + dst.LateReverbPan[1] = props.flLateReverbPan[1]; + dst.LateReverbPan[2] = props.flLateReverbPan[2]; + dst.EchoTime = props.flEchoTime; + dst.EchoDepth = props.flEchoDepth; + dst.ModulationTime = props.flModulationTime; + dst.ModulationDepth = props.flModulationDepth; + dst.AirAbsorptionGainHF = props.flAirAbsorptionGainHF; + dst.HFReference = props.flHFReference; + dst.LFReference = props.flLFReference; + dst.RoomRolloffFactor = props.flRoomRolloffFactor; + dst.DecayHFLimit = props.iDecayHFLimit ? AL_TRUE : AL_FALSE; return; } - WARN("Reverb preset '%s' not found\n", name); + WARN("Reverb preset '{}' not found", name); } bool IsValidEffectType(ALenum type) noexcept @@ -740,10 +752,7 @@ bool IsValidEffectType(ALenum type) noexcept if(type == AL_EFFECT_NULL) return true; - for(const auto &effect_item : gEffectList) - { - if(type == effect_item.val && !DisabledEffects[effect_item.type]) - return true; - } - return false; + auto check_effect = [type](const EffectList &item) noexcept -> bool + { return type == item.val && !DisabledEffects.test(item.type); }; + return std::any_of(gEffectList.cbegin(), gEffectList.cend(), check_effect); } diff --git a/3rdparty/openal/al/effect.h b/3rdparty/openal/al/effect.h index 27e9dd72dc30..c7db522abc58 100644 --- a/3rdparty/openal/al/effect.h +++ b/3rdparty/openal/al/effect.h @@ -1,13 +1,21 @@ #ifndef AL_EFFECT_H #define AL_EFFECT_H +#include +#include +#include #include +#include +#include #include "AL/al.h" +#include "AL/alc.h" #include "AL/efx.h" -#include "al/effects/effects.h" -#include "alc/effects/base.h" +#include "almalloc.h" +#include "alnumeric.h" +#include "core/effects/base.h" +#include "effects/effects.h" enum { @@ -29,38 +37,55 @@ enum { MAX_EFFECTS }; -extern bool DisabledEffects[MAX_EFFECTS]; - -extern float ReverbBoost; +inline std::bitset DisabledEffects; struct EffectList { - const char name[16]; - int type; + const char name[16]; /* NOLINT(*-avoid-c-arrays) */ + ALuint type; ALenum val; }; -extern const EffectList gEffectList[16]; +DECL_HIDDEN extern const std::array gEffectList; +using EffectHandlerVariant = std::variant; struct ALeffect { // Effect type (AL_EFFECT_NULL, ...) ALenum type{AL_EFFECT_NULL}; - EffectProps Props{}; - - const EffectVtable *vtab{nullptr}; + EffectHandlerVariant PropsVariant; + EffectProps Props; /* Self ID */ ALuint id{0u}; static void SetName(ALCcontext *context, ALuint id, std::string_view name); - DISABLE_ALLOC() + DISABLE_ALLOC }; void InitEffect(ALeffect *effect); -void LoadReverbPreset(const char *name, ALeffect *effect); +void LoadReverbPreset(const std::string_view name, ALeffect *effect); bool IsValidEffectType(ALenum type) noexcept; +struct EffectSubList { + uint64_t FreeMask{~0_u64}; + gsl::owner*> Effects{nullptr}; /* 64 */ + + EffectSubList() noexcept = default; + EffectSubList(const EffectSubList&) = delete; + EffectSubList(EffectSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Effects{rhs.Effects} + { rhs.FreeMask = ~0_u64; rhs.Effects = nullptr; } + ~EffectSubList(); + + EffectSubList& operator=(const EffectSubList&) = delete; + EffectSubList& operator=(EffectSubList&& rhs) noexcept + { std::swap(FreeMask, rhs.FreeMask); std::swap(Effects, rhs.Effects); return *this; } +}; + #endif diff --git a/3rdparty/openal/al/effects/autowah.cpp b/3rdparty/openal/al/effects/autowah.cpp index 1a8b43fcfadb..7519d31bd17a 100644 --- a/3rdparty/openal/al/effects/autowah.cpp +++ b/3rdparty/openal/al/effects/autowah.cpp @@ -4,15 +4,14 @@ #include #include -#include - #include "AL/efx.h" -#include "alc/effects/base.h" +#include "alc/context.h" +#include "alnumeric.h" #include "effects.h" -#ifdef ALSOFT_EAX -#include "alnumeric.h" +#if ALSOFT_EAX +#include "al/eax/effect.h" #include "al/eax/exception.h" #include "al/eax/utils.h" #endif // ALSOFT_EAX @@ -20,102 +19,82 @@ namespace { -void Autowah_setParamf(EffectProps *props, ALenum param, float val) +constexpr EffectProps genDefaultProps() noexcept +{ + AutowahProps props{}; + props.AttackTime = AL_AUTOWAH_DEFAULT_ATTACK_TIME; + props.ReleaseTime = AL_AUTOWAH_DEFAULT_RELEASE_TIME; + props.Resonance = AL_AUTOWAH_DEFAULT_RESONANCE; + props.PeakGain = AL_AUTOWAH_DEFAULT_PEAK_GAIN; + return props; +} + +} // namespace + +const EffectProps AutowahEffectProps{genDefaultProps()}; + +void AutowahEffectHandler::SetParami(ALCcontext *context, AutowahProps&, ALenum param, int) +{ context->throw_error(AL_INVALID_ENUM, "Invalid autowah integer property {:#04x}", as_unsigned(param)); } +void AutowahEffectHandler::SetParamiv(ALCcontext *context, AutowahProps&, ALenum param, const int*) +{ context->throw_error(AL_INVALID_ENUM, "Invalid autowah integer vector property {:#04x}", as_unsigned(param)); } + +void AutowahEffectHandler::SetParamf(ALCcontext *context, AutowahProps &props, ALenum param, float val) { switch(param) { case AL_AUTOWAH_ATTACK_TIME: if(!(val >= AL_AUTOWAH_MIN_ATTACK_TIME && val <= AL_AUTOWAH_MAX_ATTACK_TIME)) - throw effect_exception{AL_INVALID_VALUE, "Autowah attack time out of range"}; - props->Autowah.AttackTime = val; - break; + context->throw_error(AL_INVALID_VALUE, "Autowah attack time out of range"); + props.AttackTime = val; + return; case AL_AUTOWAH_RELEASE_TIME: if(!(val >= AL_AUTOWAH_MIN_RELEASE_TIME && val <= AL_AUTOWAH_MAX_RELEASE_TIME)) - throw effect_exception{AL_INVALID_VALUE, "Autowah release time out of range"}; - props->Autowah.ReleaseTime = val; - break; + context->throw_error(AL_INVALID_VALUE, "Autowah release time out of range"); + props.ReleaseTime = val; + return; case AL_AUTOWAH_RESONANCE: if(!(val >= AL_AUTOWAH_MIN_RESONANCE && val <= AL_AUTOWAH_MAX_RESONANCE)) - throw effect_exception{AL_INVALID_VALUE, "Autowah resonance out of range"}; - props->Autowah.Resonance = val; - break; + context->throw_error(AL_INVALID_VALUE, "Autowah resonance out of range"); + props.Resonance = val; + return; case AL_AUTOWAH_PEAK_GAIN: if(!(val >= AL_AUTOWAH_MIN_PEAK_GAIN && val <= AL_AUTOWAH_MAX_PEAK_GAIN)) - throw effect_exception{AL_INVALID_VALUE, "Autowah peak gain out of range"}; - props->Autowah.PeakGain = val; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param}; + context->throw_error(AL_INVALID_VALUE, "Autowah peak gain out of range"); + props.PeakGain = val; + return; } -} -void Autowah_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Autowah_setParamf(props, param, vals[0]); } -void Autowah_setParami(EffectProps*, ALenum param, int) -{ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; } -void Autowah_setParamiv(EffectProps*, ALenum param, const int*) -{ - throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x", - param}; + context->throw_error(AL_INVALID_ENUM, "Invalid autowah float property {:#04x}", + as_unsigned(param)); } +void AutowahEffectHandler::SetParamfv(ALCcontext *context, AutowahProps &props, ALenum param, const float *vals) +{ SetParamf(context, props, param, *vals); } + +void AutowahEffectHandler::GetParami(ALCcontext *context, const AutowahProps&, ALenum param, int*) +{ context->throw_error(AL_INVALID_ENUM, "Invalid autowah integer property {:#04x}", as_unsigned(param)); } +void AutowahEffectHandler::GetParamiv(ALCcontext *context, const AutowahProps&, ALenum param, int*) +{ context->throw_error(AL_INVALID_ENUM, "Invalid autowah integer vector property {:#04x}", as_unsigned(param)); } -void Autowah_getParamf(const EffectProps *props, ALenum param, float *val) +void AutowahEffectHandler::GetParamf(ALCcontext *context, const AutowahProps &props, ALenum param, float *val) { switch(param) { - case AL_AUTOWAH_ATTACK_TIME: - *val = props->Autowah.AttackTime; - break; - - case AL_AUTOWAH_RELEASE_TIME: - *val = props->Autowah.ReleaseTime; - break; - - case AL_AUTOWAH_RESONANCE: - *val = props->Autowah.Resonance; - break; - - case AL_AUTOWAH_PEAK_GAIN: - *val = props->Autowah.PeakGain; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param}; + case AL_AUTOWAH_ATTACK_TIME: *val = props.AttackTime; return; + case AL_AUTOWAH_RELEASE_TIME: *val = props.ReleaseTime; return; + case AL_AUTOWAH_RESONANCE: *val = props.Resonance; return; + case AL_AUTOWAH_PEAK_GAIN: *val = props.PeakGain; return; } + context->throw_error(AL_INVALID_ENUM, "Invalid autowah float property {:#04x}", + as_unsigned(param)); } -void Autowah_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Autowah_getParamf(props, param, vals); } - -void Autowah_getParami(const EffectProps*, ALenum param, int*) -{ throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; } -void Autowah_getParamiv(const EffectProps*, ALenum param, int*) -{ - throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x", - param}; -} +void AutowahEffectHandler::GetParamfv(ALCcontext *context, const AutowahProps &props, ALenum param, float *vals) +{ GetParamf(context, props, param, vals); } -EffectProps genDefaultProps() noexcept -{ - EffectProps props{}; - props.Autowah.AttackTime = AL_AUTOWAH_DEFAULT_ATTACK_TIME; - props.Autowah.ReleaseTime = AL_AUTOWAH_DEFAULT_RELEASE_TIME; - props.Autowah.Resonance = AL_AUTOWAH_DEFAULT_RESONANCE; - props.Autowah.PeakGain = AL_AUTOWAH_DEFAULT_PEAK_GAIN; - return props; -} - -} // namespace - -DEFINE_ALEFFECT_VTABLE(Autowah); - -const EffectProps AutowahEffectProps{genDefaultProps()}; - -#ifdef ALSOFT_EAX +#if ALSOFT_EAX namespace { using AutowahCommitter = EaxCommitter; @@ -189,25 +168,25 @@ template<> throw Exception{message}; } -template<> -bool AutowahCommitter::commit(const EaxEffectProps &props) +bool EaxAutowahCommitter::commit(const EAXAUTOWAHPROPERTIES &props) { - if(props == mEaxProps) + if(auto *cur = std::get_if(&mEaxProps); cur && *cur == props) return false; mEaxProps = props; - - auto &eaxprops = std::get(props); - mAlProps.Autowah.AttackTime = eaxprops.flAttackTime; - mAlProps.Autowah.ReleaseTime = eaxprops.flReleaseTime; - mAlProps.Autowah.Resonance = level_mb_to_gain(static_cast(eaxprops.lResonance)); - mAlProps.Autowah.PeakGain = level_mb_to_gain(static_cast(eaxprops.lPeakLevel)); + mAlProps = [&]{ + AutowahProps ret{}; + ret.AttackTime = props.flAttackTime; + ret.ReleaseTime = props.flReleaseTime; + ret.Resonance = level_mb_to_gain(static_cast(props.lResonance)); + ret.PeakGain = level_mb_to_gain(static_cast(props.lPeakLevel)); + return ret; + }(); return true; } -template<> -void AutowahCommitter::SetDefaults(EaxEffectProps &props) +void EaxAutowahCommitter::SetDefaults(EaxEffectProps &props) { static constexpr EAXAUTOWAHPROPERTIES defprops{[] { @@ -221,10 +200,8 @@ void AutowahCommitter::SetDefaults(EaxEffectProps &props) props = defprops; } -template<> -void AutowahCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) +void EaxAutowahCommitter::Get(const EaxCall &call, const EAXAUTOWAHPROPERTIES &props) { - auto &props = std::get(props_); switch(call.get_property_id()) { case EAXAUTOWAH_NONE: break; @@ -237,10 +214,8 @@ void AutowahCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) } } -template<> -void AutowahCommitter::Set(const EaxCall &call, EaxEffectProps &props_) +void EaxAutowahCommitter::Set(const EaxCall &call, EAXAUTOWAHPROPERTIES &props) { - auto &props = std::get(props_); switch(call.get_property_id()) { case EAXAUTOWAH_NONE: break; diff --git a/3rdparty/openal/al/effects/chorus.cpp b/3rdparty/openal/al/effects/chorus.cpp index 90b38e4d4eed..c10b9d434b83 100644 --- a/3rdparty/openal/al/effects/chorus.cpp +++ b/3rdparty/openal/al/effects/chorus.cpp @@ -7,13 +7,13 @@ #include "AL/al.h" #include "AL/efx.h" -#include "alc/effects/base.h" -#include "core/logging.h" +#include "alc/context.h" +#include "alnumeric.h" #include "effects.h" -#ifdef ALSOFT_EAX +#if ALSOFT_EAX #include -#include "alnumeric.h" +#include "al/eax/effect.h" #include "al/eax/exception.h" #include "al/eax/utils.h" #endif // ALSOFT_EAX @@ -27,7 +27,7 @@ static_assert(FlangerMaxDelay >= AL_FLANGER_MAX_DELAY, "Flanger max delay too sm static_assert(AL_CHORUS_WAVEFORM_SINUSOID == AL_FLANGER_WAVEFORM_SINUSOID, "Chorus/Flanger waveform value mismatch"); static_assert(AL_CHORUS_WAVEFORM_TRIANGLE == AL_FLANGER_WAVEFORM_TRIANGLE, "Chorus/Flanger waveform value mismatch"); -inline std::optional WaveformFromEnum(ALenum type) +constexpr std::optional WaveformFromEnum(ALenum type) noexcept { switch(type) { @@ -36,263 +36,230 @@ inline std::optional WaveformFromEnum(ALenum type) } return std::nullopt; } -inline ALenum EnumFromWaveform(ChorusWaveform type) +constexpr ALenum EnumFromWaveform(ChorusWaveform type) { switch(type) { case ChorusWaveform::Sinusoid: return AL_CHORUS_WAVEFORM_SINUSOID; case ChorusWaveform::Triangle: return AL_CHORUS_WAVEFORM_TRIANGLE; } - throw std::runtime_error{"Invalid chorus waveform: "+std::to_string(static_cast(type))}; + throw std::runtime_error{fmt::format("Invalid chorus waveform: {}", + int{al::to_underlying(type)})}; +} + +constexpr EffectProps genDefaultChorusProps() noexcept +{ + ChorusProps props{}; + props.Waveform = WaveformFromEnum(AL_CHORUS_DEFAULT_WAVEFORM).value(); + props.Phase = AL_CHORUS_DEFAULT_PHASE; + props.Rate = AL_CHORUS_DEFAULT_RATE; + props.Depth = AL_CHORUS_DEFAULT_DEPTH; + props.Feedback = AL_CHORUS_DEFAULT_FEEDBACK; + props.Delay = AL_CHORUS_DEFAULT_DELAY; + return props; } -void Chorus_setParami(EffectProps *props, ALenum param, int val) +constexpr EffectProps genDefaultFlangerProps() noexcept +{ + ChorusProps props{}; + props.Waveform = WaveformFromEnum(AL_FLANGER_DEFAULT_WAVEFORM).value(); + props.Phase = AL_FLANGER_DEFAULT_PHASE; + props.Rate = AL_FLANGER_DEFAULT_RATE; + props.Depth = AL_FLANGER_DEFAULT_DEPTH; + props.Feedback = AL_FLANGER_DEFAULT_FEEDBACK; + props.Delay = AL_FLANGER_DEFAULT_DELAY; + return props; +} + +} // namespace + +const EffectProps ChorusEffectProps{genDefaultChorusProps()}; + +void ChorusEffectHandler::SetParami(ALCcontext *context, ChorusProps &props, ALenum param, int val) { switch(param) { case AL_CHORUS_WAVEFORM: if(auto formopt = WaveformFromEnum(val)) - props->Chorus.Waveform = *formopt; + props.Waveform = *formopt; else - throw effect_exception{AL_INVALID_VALUE, "Invalid chorus waveform: 0x%04x", val}; - break; + context->throw_error(AL_INVALID_VALUE, "Invalid chorus waveform: {:#04x}", + as_unsigned(val)); + return; case AL_CHORUS_PHASE: if(!(val >= AL_CHORUS_MIN_PHASE && val <= AL_CHORUS_MAX_PHASE)) - throw effect_exception{AL_INVALID_VALUE, "Chorus phase out of range: %d", val}; - props->Chorus.Phase = val; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param}; + context->throw_error(AL_INVALID_VALUE, "Chorus phase out of range: {}", val); + props.Phase = val; + return; } + + context->throw_error(AL_INVALID_ENUM, "Invalid chorus integer property {:#04x}", + as_unsigned(param)); } -void Chorus_setParamiv(EffectProps *props, ALenum param, const int *vals) -{ Chorus_setParami(props, param, vals[0]); } -void Chorus_setParamf(EffectProps *props, ALenum param, float val) +void ChorusEffectHandler::SetParamiv(ALCcontext *context, ChorusProps &props, ALenum param, const int *vals) +{ SetParami(context, props, param, *vals); } +void ChorusEffectHandler::SetParamf(ALCcontext *context, ChorusProps &props, ALenum param, float val) { switch(param) { case AL_CHORUS_RATE: if(!(val >= AL_CHORUS_MIN_RATE && val <= AL_CHORUS_MAX_RATE)) - throw effect_exception{AL_INVALID_VALUE, "Chorus rate out of range: %f", val}; - props->Chorus.Rate = val; - break; + context->throw_error(AL_INVALID_VALUE, "Chorus rate out of range: {:f}", val); + props.Rate = val; + return; case AL_CHORUS_DEPTH: if(!(val >= AL_CHORUS_MIN_DEPTH && val <= AL_CHORUS_MAX_DEPTH)) - throw effect_exception{AL_INVALID_VALUE, "Chorus depth out of range: %f", val}; - props->Chorus.Depth = val; - break; + context->throw_error(AL_INVALID_VALUE, "Chorus depth out of range: {:f}", val); + props.Depth = val; + return; case AL_CHORUS_FEEDBACK: if(!(val >= AL_CHORUS_MIN_FEEDBACK && val <= AL_CHORUS_MAX_FEEDBACK)) - throw effect_exception{AL_INVALID_VALUE, "Chorus feedback out of range: %f", val}; - props->Chorus.Feedback = val; - break; + context->throw_error(AL_INVALID_VALUE, "Chorus feedback out of range: {:f}", val); + props.Feedback = val; + return; case AL_CHORUS_DELAY: if(!(val >= AL_CHORUS_MIN_DELAY && val <= AL_CHORUS_MAX_DELAY)) - throw effect_exception{AL_INVALID_VALUE, "Chorus delay out of range: %f", val}; - props->Chorus.Delay = val; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param}; + context->throw_error(AL_INVALID_VALUE, "Chorus delay out of range: {:f}", val); + props.Delay = val; + return; } + + context->throw_error(AL_INVALID_ENUM, "Invalid chorus float property {:#04x}", + as_unsigned(param)); } -void Chorus_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Chorus_setParamf(props, param, vals[0]); } +void ChorusEffectHandler::SetParamfv(ALCcontext *context, ChorusProps &props, ALenum param, const float *vals) +{ SetParamf(context, props, param, *vals); } -void Chorus_getParami(const EffectProps *props, ALenum param, int *val) +void ChorusEffectHandler::GetParami(ALCcontext *context, const ChorusProps &props, ALenum param, int *val) { switch(param) { - case AL_CHORUS_WAVEFORM: - *val = EnumFromWaveform(props->Chorus.Waveform); - break; - - case AL_CHORUS_PHASE: - *val = props->Chorus.Phase; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param}; + case AL_CHORUS_WAVEFORM: *val = EnumFromWaveform(props.Waveform); return; + case AL_CHORUS_PHASE: *val = props.Phase; return; } + + context->throw_error(AL_INVALID_ENUM, "Invalid chorus integer property {:#04x}", + as_unsigned(param)); } -void Chorus_getParamiv(const EffectProps *props, ALenum param, int *vals) -{ Chorus_getParami(props, param, vals); } -void Chorus_getParamf(const EffectProps *props, ALenum param, float *val) +void ChorusEffectHandler::GetParamiv(ALCcontext *context, const ChorusProps &props, ALenum param, int *vals) +{ GetParami(context, props, param, vals); } +void ChorusEffectHandler::GetParamf(ALCcontext *context, const ChorusProps &props, ALenum param, float *val) { switch(param) { - case AL_CHORUS_RATE: - *val = props->Chorus.Rate; - break; - - case AL_CHORUS_DEPTH: - *val = props->Chorus.Depth; - break; - - case AL_CHORUS_FEEDBACK: - *val = props->Chorus.Feedback; - break; - - case AL_CHORUS_DELAY: - *val = props->Chorus.Delay; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param}; + case AL_CHORUS_RATE: *val = props.Rate; return; + case AL_CHORUS_DEPTH: *val = props.Depth; return; + case AL_CHORUS_FEEDBACK: *val = props.Feedback; return; + case AL_CHORUS_DELAY: *val = props.Delay; return; } -} -void Chorus_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Chorus_getParamf(props, param, vals); } -EffectProps genDefaultChorusProps() noexcept -{ - EffectProps props{}; - props.Chorus.Waveform = *WaveformFromEnum(AL_CHORUS_DEFAULT_WAVEFORM); - props.Chorus.Phase = AL_CHORUS_DEFAULT_PHASE; - props.Chorus.Rate = AL_CHORUS_DEFAULT_RATE; - props.Chorus.Depth = AL_CHORUS_DEFAULT_DEPTH; - props.Chorus.Feedback = AL_CHORUS_DEFAULT_FEEDBACK; - props.Chorus.Delay = AL_CHORUS_DEFAULT_DELAY; - return props; + context->throw_error(AL_INVALID_ENUM, "Invalid chorus float property {:#04x}", + as_unsigned(param)); } +void ChorusEffectHandler::GetParamfv(ALCcontext *context, const ChorusProps &props, ALenum param, float *vals) +{ GetParamf(context, props, param, vals); } + +const EffectProps FlangerEffectProps{genDefaultFlangerProps()}; -void Flanger_setParami(EffectProps *props, ALenum param, int val) +void FlangerEffectHandler::SetParami(ALCcontext *context, ChorusProps &props, ALenum param, int val) { switch(param) { case AL_FLANGER_WAVEFORM: if(auto formopt = WaveformFromEnum(val)) - props->Chorus.Waveform = *formopt; + props.Waveform = *formopt; else - throw effect_exception{AL_INVALID_VALUE, "Invalid flanger waveform: 0x%04x", val}; - break; + context->throw_error(AL_INVALID_VALUE, "Invalid flanger waveform: {:#04x}", + as_unsigned(val)); + return; case AL_FLANGER_PHASE: if(!(val >= AL_FLANGER_MIN_PHASE && val <= AL_FLANGER_MAX_PHASE)) - throw effect_exception{AL_INVALID_VALUE, "Flanger phase out of range: %d", val}; - props->Chorus.Phase = val; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param}; + context->throw_error(AL_INVALID_VALUE, "Flanger phase out of range: {}", val); + props.Phase = val; + return; } + + context->throw_error(AL_INVALID_ENUM, "Invalid flanger integer property {:#04x}", + as_unsigned(param)); } -void Flanger_setParamiv(EffectProps *props, ALenum param, const int *vals) -{ Flanger_setParami(props, param, vals[0]); } -void Flanger_setParamf(EffectProps *props, ALenum param, float val) +void FlangerEffectHandler::SetParamiv(ALCcontext *context, ChorusProps &props, ALenum param, const int *vals) +{ SetParami(context, props, param, *vals); } +void FlangerEffectHandler::SetParamf(ALCcontext *context, ChorusProps &props, ALenum param, float val) { switch(param) { case AL_FLANGER_RATE: if(!(val >= AL_FLANGER_MIN_RATE && val <= AL_FLANGER_MAX_RATE)) - throw effect_exception{AL_INVALID_VALUE, "Flanger rate out of range: %f", val}; - props->Chorus.Rate = val; - break; + context->throw_error(AL_INVALID_VALUE, "Flanger rate out of range: {:f}", val); + props.Rate = val; + return; case AL_FLANGER_DEPTH: if(!(val >= AL_FLANGER_MIN_DEPTH && val <= AL_FLANGER_MAX_DEPTH)) - throw effect_exception{AL_INVALID_VALUE, "Flanger depth out of range: %f", val}; - props->Chorus.Depth = val; - break; + context->throw_error(AL_INVALID_VALUE, "Flanger depth out of range: {:f}", val); + props.Depth = val; + return; case AL_FLANGER_FEEDBACK: if(!(val >= AL_FLANGER_MIN_FEEDBACK && val <= AL_FLANGER_MAX_FEEDBACK)) - throw effect_exception{AL_INVALID_VALUE, "Flanger feedback out of range: %f", val}; - props->Chorus.Feedback = val; - break; + context->throw_error(AL_INVALID_VALUE, "Flanger feedback out of range: {:f}", val); + props.Feedback = val; + return; case AL_FLANGER_DELAY: if(!(val >= AL_FLANGER_MIN_DELAY && val <= AL_FLANGER_MAX_DELAY)) - throw effect_exception{AL_INVALID_VALUE, "Flanger delay out of range: %f", val}; - props->Chorus.Delay = val; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param}; + context->throw_error(AL_INVALID_VALUE, "Flanger delay out of range: {:f}", val); + props.Delay = val; + return; } + + context->throw_error(AL_INVALID_ENUM, "Invalid flanger float property {:#04x}", + as_unsigned(param)); } -void Flanger_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Flanger_setParamf(props, param, vals[0]); } +void FlangerEffectHandler::SetParamfv(ALCcontext *context, ChorusProps &props, ALenum param, const float *vals) +{ SetParamf(context, props, param, *vals); } -void Flanger_getParami(const EffectProps *props, ALenum param, int *val) +void FlangerEffectHandler::GetParami(ALCcontext *context, const ChorusProps &props, ALenum param, int *val) { switch(param) { - case AL_FLANGER_WAVEFORM: - *val = EnumFromWaveform(props->Chorus.Waveform); - break; - - case AL_FLANGER_PHASE: - *val = props->Chorus.Phase; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param}; + case AL_FLANGER_WAVEFORM: *val = EnumFromWaveform(props.Waveform); return; + case AL_FLANGER_PHASE: *val = props.Phase; return; } + + context->throw_error(AL_INVALID_ENUM, "Invalid flanger integer property {:#04x}", + as_unsigned(param)); } -void Flanger_getParamiv(const EffectProps *props, ALenum param, int *vals) -{ Flanger_getParami(props, param, vals); } -void Flanger_getParamf(const EffectProps *props, ALenum param, float *val) +void FlangerEffectHandler::GetParamiv(ALCcontext *context, const ChorusProps &props, ALenum param, int *vals) +{ GetParami(context, props, param, vals); } +void FlangerEffectHandler::GetParamf(ALCcontext *context, const ChorusProps &props, ALenum param, float *val) { switch(param) { - case AL_FLANGER_RATE: - *val = props->Chorus.Rate; - break; - - case AL_FLANGER_DEPTH: - *val = props->Chorus.Depth; - break; - - case AL_FLANGER_FEEDBACK: - *val = props->Chorus.Feedback; - break; - - case AL_FLANGER_DELAY: - *val = props->Chorus.Delay; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param}; + case AL_FLANGER_RATE: *val = props.Rate; return; + case AL_FLANGER_DEPTH: *val = props.Depth; return; + case AL_FLANGER_FEEDBACK: *val = props.Feedback; return; + case AL_FLANGER_DELAY: *val = props.Delay; return; } -} -void Flanger_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Flanger_getParamf(props, param, vals); } -EffectProps genDefaultFlangerProps() noexcept -{ - EffectProps props{}; - props.Chorus.Waveform = *WaveformFromEnum(AL_FLANGER_DEFAULT_WAVEFORM); - props.Chorus.Phase = AL_FLANGER_DEFAULT_PHASE; - props.Chorus.Rate = AL_FLANGER_DEFAULT_RATE; - props.Chorus.Depth = AL_FLANGER_DEFAULT_DEPTH; - props.Chorus.Feedback = AL_FLANGER_DEFAULT_FEEDBACK; - props.Chorus.Delay = AL_FLANGER_DEFAULT_DELAY; - return props; + context->throw_error(AL_INVALID_ENUM, "Invalid flanger float property {:#04x}", + as_unsigned(param)); } +void FlangerEffectHandler::GetParamfv(ALCcontext *context, const ChorusProps &props, ALenum param, float *vals) +{ GetParamf(context, props, param, vals); } -} // namespace - -DEFINE_ALEFFECT_VTABLE(Chorus); - -const EffectProps ChorusEffectProps{genDefaultChorusProps()}; - -DEFINE_ALEFFECT_VTABLE(Flanger); -const EffectProps FlangerEffectProps{genDefaultFlangerProps()}; - - -#ifdef ALSOFT_EAX +#if ALSOFT_EAX namespace { struct EaxChorusTraits { - using Props = EAXCHORUSPROPERTIES; + using EaxProps = EAXCHORUSPROPERTIES; using Committer = EaxChorusCommitter; static constexpr auto efx_effect() { return AL_EFFECT_CHORUS; } @@ -357,7 +324,7 @@ struct EaxChorusTraits { }; // EaxChorusTraits struct EaxFlangerTraits { - using Props = EAXFLANGERPROPERTIES; + using EaxProps = EAXFLANGERPROPERTIES; using Committer = EaxFlangerCommitter; static constexpr auto efx_effect() { return AL_EFFECT_FLANGER; } @@ -424,6 +391,7 @@ struct EaxFlangerTraits { template struct ChorusFlangerEffect { using Traits = TTraits; + using EaxProps = typename Traits::EaxProps; using Committer = typename Traits::Committer; using Exception = typename Committer::Exception; @@ -494,7 +462,7 @@ struct ChorusFlangerEffect { }; // DelayValidator struct AllValidator { - void operator()(const typename Traits::Props& all) const + void operator()(const EaxProps& all) const { WaveformValidator{}(all.ulWaveform); PhaseValidator{}(all.lPhase); @@ -508,7 +476,7 @@ struct ChorusFlangerEffect { public: static void SetDefaults(EaxEffectProps &props) { - auto&& all = props.emplace(); + auto&& all = props.emplace(); all.ulWaveform = Traits::eax_default_waveform(); all.lPhase = Traits::eax_default_phase(); all.flRate = Traits::eax_default_rate(); @@ -518,102 +486,83 @@ struct ChorusFlangerEffect { } - static void Get(const EaxCall &call, const EaxEffectProps &props) + static void Get(const EaxCall &call, const EaxProps &all) { - auto&& all = std::get(props); switch(call.get_property_id()) { case Traits::eax_none_param_id(): break; - case Traits::eax_allparameters_param_id(): call.template set_value(all); break; - case Traits::eax_waveform_param_id(): call.template set_value(all.ulWaveform); break; - case Traits::eax_phase_param_id(): call.template set_value(all.lPhase); break; - case Traits::eax_rate_param_id(): call.template set_value(all.flRate); break; - case Traits::eax_depth_param_id(): call.template set_value(all.flDepth); break; - case Traits::eax_feedback_param_id(): call.template set_value(all.flFeedback); break; - case Traits::eax_delay_param_id(): call.template set_value(all.flDelay); break; - default: Committer::fail_unknown_property_id(); } } - static void Set(const EaxCall &call, EaxEffectProps &props) + static void Set(const EaxCall &call, EaxProps &all) { - auto&& all = std::get(props); switch(call.get_property_id()) { case Traits::eax_none_param_id(): break; - case Traits::eax_allparameters_param_id(): Committer::template defer(call, all); break; - case Traits::eax_waveform_param_id(): Committer::template defer(call, all.ulWaveform); break; - case Traits::eax_phase_param_id(): Committer::template defer(call, all.lPhase); break; - case Traits::eax_rate_param_id(): Committer::template defer(call, all.flRate); break; - case Traits::eax_depth_param_id(): Committer::template defer(call, all.flDepth); break; - case Traits::eax_feedback_param_id(): Committer::template defer(call, all.flFeedback); break; - case Traits::eax_delay_param_id(): Committer::template defer(call, all.flDelay); break; - default: Committer::fail_unknown_property_id(); } } - static bool Commit(const EaxEffectProps &props, EaxEffectProps &props_, EffectProps &al_props_) + static bool Commit(const EaxProps &props, EaxEffectProps &props_, ChorusProps &al_props_) { - if(props == props_) + if(auto *cur = std::get_if(&props_); cur && *cur == props) return false; props_ = props; - auto&& dst = std::get(props); - al_props_.Chorus.Waveform = Traits::eax_waveform(dst.ulWaveform); - al_props_.Chorus.Phase = static_cast(dst.lPhase); - al_props_.Chorus.Rate = dst.flRate; - al_props_.Chorus.Depth = dst.flDepth; - al_props_.Chorus.Feedback = dst.flFeedback; - al_props_.Chorus.Delay = dst.flDelay; + al_props_.Waveform = Traits::eax_waveform(props.ulWaveform); + al_props_.Phase = static_cast(props.lPhase); + al_props_.Rate = props.flRate; + al_props_.Depth = props.flDepth; + al_props_.Feedback = props.flFeedback; + al_props_.Delay = props.flDelay; return true; } @@ -638,29 +587,25 @@ template<> throw Exception{message}; } -template<> -bool ChorusCommitter::commit(const EaxEffectProps &props) +bool EaxChorusCommitter::commit(const EAXCHORUSPROPERTIES &props) { using Committer = ChorusFlangerEffect; - return Committer::Commit(props, mEaxProps, mAlProps); + return Committer::Commit(props, mEaxProps, mAlProps.emplace()); } -template<> -void ChorusCommitter::SetDefaults(EaxEffectProps &props) +void EaxChorusCommitter::SetDefaults(EaxEffectProps &props) { using Committer = ChorusFlangerEffect; Committer::SetDefaults(props); } -template<> -void ChorusCommitter::Get(const EaxCall &call, const EaxEffectProps &props) +void EaxChorusCommitter::Get(const EaxCall &call, const EAXCHORUSPROPERTIES &props) { using Committer = ChorusFlangerEffect; Committer::Get(call, props); } -template<> -void ChorusCommitter::Set(const EaxCall &call, EaxEffectProps &props) +void EaxChorusCommitter::Set(const EaxCall &call, EAXCHORUSPROPERTIES &props) { using Committer = ChorusFlangerEffect; Committer::Set(call, props); @@ -679,29 +624,25 @@ template<> throw Exception{message}; } -template<> -bool FlangerCommitter::commit(const EaxEffectProps &props) +bool EaxFlangerCommitter::commit(const EAXFLANGERPROPERTIES &props) { using Committer = ChorusFlangerEffect; - return Committer::Commit(props, mEaxProps, mAlProps); + return Committer::Commit(props, mEaxProps, mAlProps.emplace()); } -template<> -void FlangerCommitter::SetDefaults(EaxEffectProps &props) +void EaxFlangerCommitter::SetDefaults(EaxEffectProps &props) { using Committer = ChorusFlangerEffect; Committer::SetDefaults(props); } -template<> -void FlangerCommitter::Get(const EaxCall &call, const EaxEffectProps &props) +void EaxFlangerCommitter::Get(const EaxCall &call, const EAXFLANGERPROPERTIES &props) { using Committer = ChorusFlangerEffect; Committer::Get(call, props); } -template<> -void FlangerCommitter::Set(const EaxCall &call, EaxEffectProps &props) +void EaxFlangerCommitter::Set(const EaxCall &call, EAXFLANGERPROPERTIES &props) { using Committer = ChorusFlangerEffect; Committer::Set(call, props); diff --git a/3rdparty/openal/al/effects/compressor.cpp b/3rdparty/openal/al/effects/compressor.cpp index ca8af84face0..f5c6786af140 100644 --- a/3rdparty/openal/al/effects/compressor.cpp +++ b/3rdparty/openal/al/effects/compressor.cpp @@ -4,11 +4,12 @@ #include "AL/al.h" #include "AL/efx.h" -#include "alc/effects/base.h" +#include "alc/context.h" +#include "alnumeric.h" #include "effects.h" -#ifdef ALSOFT_EAX -#include "alnumeric.h" +#if ALSOFT_EAX +#include "al/eax/effect.h" #include "al/eax/exception.h" #include "al/eax/utils.h" #endif // ALSOFT_EAX @@ -16,68 +17,57 @@ namespace { -void Compressor_setParami(EffectProps *props, ALenum param, int val) +constexpr EffectProps genDefaultProps() noexcept +{ + CompressorProps props{}; + props.OnOff = AL_COMPRESSOR_DEFAULT_ONOFF; + return props; +} + +} // namespace + +const EffectProps CompressorEffectProps{genDefaultProps()}; + +void CompressorEffectHandler::SetParami(ALCcontext *context, CompressorProps &props, ALenum param, int val) { switch(param) { case AL_COMPRESSOR_ONOFF: if(!(val >= AL_COMPRESSOR_MIN_ONOFF && val <= AL_COMPRESSOR_MAX_ONOFF)) - throw effect_exception{AL_INVALID_VALUE, "Compressor state out of range"}; - props->Compressor.OnOff = (val != AL_FALSE); - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x", - param}; + context->throw_error(AL_INVALID_VALUE, "Compressor state out of range"); + props.OnOff = (val != AL_FALSE); + return; } -} -void Compressor_setParamiv(EffectProps *props, ALenum param, const int *vals) -{ Compressor_setParami(props, param, vals[0]); } -void Compressor_setParamf(EffectProps*, ALenum param, float) -{ throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param}; } -void Compressor_setParamfv(EffectProps*, ALenum param, const float*) -{ - throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x", - param}; -} -void Compressor_getParami(const EffectProps *props, ALenum param, int *val) + context->throw_error(AL_INVALID_ENUM, "Invalid compressor integer property {:#04x}", + as_unsigned(param)); +} +void CompressorEffectHandler::SetParamiv(ALCcontext *context, CompressorProps &props, ALenum param, const int *vals) +{ SetParami(context, props, param, *vals); } +void CompressorEffectHandler::SetParamf(ALCcontext *context, CompressorProps&, ALenum param, float) +{ context->throw_error(AL_INVALID_ENUM, "Invalid compressor float property {:#04x}", as_unsigned(param)); } +void CompressorEffectHandler::SetParamfv(ALCcontext *context, CompressorProps&, ALenum param, const float*) +{ context->throw_error(AL_INVALID_ENUM, "Invalid compressor float-vector property {:#04x}", as_unsigned(param)); } + +void CompressorEffectHandler::GetParami(ALCcontext *context, const CompressorProps &props, ALenum param, int *val) { switch(param) { - case AL_COMPRESSOR_ONOFF: - *val = props->Compressor.OnOff; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid compressor integer property 0x%04x", - param}; + case AL_COMPRESSOR_ONOFF: *val = props.OnOff; return; } -} -void Compressor_getParamiv(const EffectProps *props, ALenum param, int *vals) -{ Compressor_getParami(props, param, vals); } -void Compressor_getParamf(const EffectProps*, ALenum param, float*) -{ throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float property 0x%04x", param}; } -void Compressor_getParamfv(const EffectProps*, ALenum param, float*) -{ - throw effect_exception{AL_INVALID_ENUM, "Invalid compressor float-vector property 0x%04x", - param}; -} -EffectProps genDefaultProps() noexcept -{ - EffectProps props{}; - props.Compressor.OnOff = AL_COMPRESSOR_DEFAULT_ONOFF; - return props; + context->throw_error(AL_INVALID_ENUM, "Invalid compressor integer property {:#04x}", + as_unsigned(param)); } +void CompressorEffectHandler::GetParamiv(ALCcontext *context, const CompressorProps &props, ALenum param, int *vals) +{ GetParami(context, props, param, vals); } +void CompressorEffectHandler::GetParamf(ALCcontext *context, const CompressorProps&, ALenum param, float*) +{ context->throw_error(AL_INVALID_ENUM, "Invalid compressor float property {:#04x}", as_unsigned(param)); } +void CompressorEffectHandler::GetParamfv(ALCcontext *context, const CompressorProps&, ALenum param, float*) +{ context->throw_error(AL_INVALID_ENUM, "Invalid compressor float-vector property {:#04x}", as_unsigned(param)); } -} // namespace -DEFINE_ALEFFECT_VTABLE(Compressor); - -const EffectProps CompressorEffectProps{genDefaultProps()}; - -#ifdef ALSOFT_EAX +#if ALSOFT_EAX namespace { using CompressorCommitter = EaxCommitter; @@ -115,28 +105,24 @@ template<> throw Exception{message}; } -template<> -bool CompressorCommitter::commit(const EaxEffectProps &props) +bool EaxCompressorCommitter::commit(const EAXAGCCOMPRESSORPROPERTIES &props) { - if(props == mEaxProps) + if(auto *cur = std::get_if(&mEaxProps); cur && *cur == props) return false; mEaxProps = props; + mAlProps = CompressorProps{props.ulOnOff != 0}; - mAlProps.Compressor.OnOff = (std::get(props).ulOnOff != 0); return true; } -template<> -void CompressorCommitter::SetDefaults(EaxEffectProps &props) +void EaxCompressorCommitter::SetDefaults(EaxEffectProps &props) { props = EAXAGCCOMPRESSORPROPERTIES{EAXAGCCOMPRESSOR_DEFAULTONOFF}; } -template<> -void CompressorCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) +void EaxCompressorCommitter::Get(const EaxCall &call, const EAXAGCCOMPRESSORPROPERTIES &props) { - auto &props = std::get(props_); switch(call.get_property_id()) { case EAXAGCCOMPRESSOR_NONE: break; @@ -146,10 +132,8 @@ void CompressorCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) } } -template<> -void CompressorCommitter::Set(const EaxCall &call, EaxEffectProps &props_) +void EaxCompressorCommitter::Set(const EaxCall &call, EAXAGCCOMPRESSORPROPERTIES &props) { - auto &props = std::get(props_); switch(call.get_property_id()) { case EAXAGCCOMPRESSOR_NONE: break; diff --git a/3rdparty/openal/al/effects/convolution.cpp b/3rdparty/openal/al/effects/convolution.cpp index 8e850fd3e20b..84de06c18537 100644 --- a/3rdparty/openal/al/effects/convolution.cpp +++ b/3rdparty/openal/al/effects/convolution.cpp @@ -1,93 +1,76 @@ #include "config.h" +#include +#include +#include + #include "AL/al.h" -#include "alc/inprogext.h" -#include "alc/effects/base.h" +#include "alc/context.h" +#include "alc/inprogext.h" +#include "alnumeric.h" +#include "alspan.h" #include "effects.h" namespace { -void Convolution_setParami(EffectProps* /*props*/, ALenum param, int /*val*/) -{ - switch(param) - { - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x", - param}; - } -} -void Convolution_setParamiv(EffectProps *props, ALenum param, const int *vals) -{ - switch(param) - { - default: - Convolution_setParami(props, param, vals[0]); - } -} -void Convolution_setParamf(EffectProps* /*props*/, ALenum param, float /*val*/) +constexpr EffectProps genDefaultProps() noexcept { - switch(param) - { - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x", - param}; - } -} -void Convolution_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ - switch(param) - { - default: - Convolution_setParamf(props, param, vals[0]); - } + ConvolutionProps props{}; + props.OrientAt = {0.0f, 0.0f, -1.0f}; + props.OrientUp = {0.0f, 1.0f, 0.0f}; + return props; } -void Convolution_getParami(const EffectProps* /*props*/, ALenum param, int* /*val*/) -{ - switch(param) - { - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x", - param}; - } -} -void Convolution_getParamiv(const EffectProps *props, ALenum param, int *vals) -{ - switch(param) - { - default: - Convolution_getParami(props, param, vals); - } -} -void Convolution_getParamf(const EffectProps* /*props*/, ALenum param, float* /*val*/) +} // namespace + +const EffectProps ConvolutionEffectProps{genDefaultProps()}; + +void ConvolutionEffectHandler::SetParami(ALCcontext *context, ConvolutionProps& /*props*/, ALenum param, int /*val*/) +{ context->throw_error(AL_INVALID_ENUM, "Invalid convolution effect integer property {:#04x}", as_unsigned(param)); } +void ConvolutionEffectHandler::SetParamiv(ALCcontext *context, ConvolutionProps &props, ALenum param, const int *vals) +{ SetParami(context, props, param, *vals); } + +void ConvolutionEffectHandler::SetParamf(ALCcontext *context, ConvolutionProps& /*props*/, ALenum param, float /*val*/) +{ context->throw_error(AL_INVALID_ENUM, "Invalid convolution effect float property {:#04x}", as_unsigned(param)); } +void ConvolutionEffectHandler::SetParamfv(ALCcontext *context, ConvolutionProps &props, ALenum param, const float *values) { + static constexpr auto finite_checker = [](float val) -> bool { return std::isfinite(val); }; + switch(param) { - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x", - param}; + case AL_CONVOLUTION_ORIENTATION_SOFT: + auto vals = al::span{values, 6_uz}; + if(!std::all_of(vals.cbegin(), vals.cend(), finite_checker)) + context->throw_error(AL_INVALID_VALUE, "Convolution orientation out of range", param); + + std::copy_n(vals.cbegin(), props.OrientAt.size(), props.OrientAt.begin()); + std::copy_n(vals.cbegin()+3, props.OrientUp.size(), props.OrientUp.begin()); + return; } + + SetParamf(context, props, param, *values); } -void Convolution_getParamfv(const EffectProps *props, ALenum param, float *vals) + +void ConvolutionEffectHandler::GetParami(ALCcontext *context, const ConvolutionProps& /*props*/, ALenum param, int* /*val*/) +{ context->throw_error(AL_INVALID_ENUM, "Invalid convolution effect integer property {:#04x}", as_unsigned(param)); } +void ConvolutionEffectHandler::GetParamiv(ALCcontext *context, const ConvolutionProps &props, ALenum param, int *vals) +{ GetParami(context, props, param, vals); } + +void ConvolutionEffectHandler::GetParamf(ALCcontext *context, const ConvolutionProps& /*props*/, ALenum param, float* /*val*/) +{ context->throw_error(AL_INVALID_ENUM, "Invalid convolution effect float property {:#04x}", as_unsigned(param)); } +void ConvolutionEffectHandler::GetParamfv(ALCcontext *context, const ConvolutionProps &props, ALenum param, float *values) { switch(param) { - default: - Convolution_getParamf(props, param, vals); + case AL_CONVOLUTION_ORIENTATION_SOFT: + auto vals = al::span{values, 6_uz}; + std::copy(props.OrientAt.cbegin(), props.OrientAt.cend(), vals.begin()); + std::copy(props.OrientUp.cbegin(), props.OrientUp.cend(), vals.begin()+3); + return; } -} -EffectProps genDefaultProps() noexcept -{ - EffectProps props{}; - return props; + GetParamf(context, props, param, values); } - -} // namespace - -DEFINE_ALEFFECT_VTABLE(Convolution); - -const EffectProps ConvolutionEffectProps{genDefaultProps()}; diff --git a/3rdparty/openal/al/effects/dedicated.cpp b/3rdparty/openal/al/effects/dedicated.cpp index db57003c2af9..5776b42be27c 100644 --- a/3rdparty/openal/al/effects/dedicated.cpp +++ b/3rdparty/openal/al/effects/dedicated.cpp @@ -6,67 +6,108 @@ #include "AL/al.h" #include "AL/alext.h" -#include "alc/effects/base.h" +#include "alc/context.h" +#include "alnumeric.h" #include "effects.h" namespace { -void Dedicated_setParami(EffectProps*, ALenum param, int) -{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; } -void Dedicated_setParamiv(EffectProps*, ALenum param, const int*) +constexpr EffectProps genDefaultDialogProps() noexcept { - throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x", - param}; + DedicatedProps props{}; + props.Target = DedicatedProps::Dialog; + props.Gain = 1.0f; + return props; } -void Dedicated_setParamf(EffectProps *props, ALenum param, float val) + +constexpr EffectProps genDefaultLfeProps() noexcept +{ + DedicatedProps props{}; + props.Target = DedicatedProps::Lfe; + props.Gain = 1.0f; + return props; +} + +} // namespace + +const EffectProps DedicatedDialogEffectProps{genDefaultDialogProps()}; + +void DedicatedDialogEffectHandler::SetParami(ALCcontext *context, DedicatedProps&, ALenum param, int) +{ context->throw_error(AL_INVALID_ENUM, "Invalid dedicated integer property {:#04x}", as_unsigned(param)); } +void DedicatedDialogEffectHandler::SetParamiv(ALCcontext *context, DedicatedProps&, ALenum param, const int*) +{ context->throw_error(AL_INVALID_ENUM, "Invalid dedicated integer-vector property {:#04x}", as_unsigned(param)); } +void DedicatedDialogEffectHandler::SetParamf(ALCcontext *context, DedicatedProps &props, ALenum param, float val) { switch(param) { case AL_DEDICATED_GAIN: if(!(val >= 0.0f && std::isfinite(val))) - throw effect_exception{AL_INVALID_VALUE, "Dedicated gain out of range"}; - props->Dedicated.Gain = val; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param}; + context->throw_error(AL_INVALID_VALUE, "Dedicated gain out of range"); + props.Gain = val; + return; } + + context->throw_error(AL_INVALID_ENUM, "Invalid dedicated float property {:#04x}", + as_unsigned(param)); } -void Dedicated_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Dedicated_setParamf(props, param, vals[0]); } +void DedicatedDialogEffectHandler::SetParamfv(ALCcontext *context, DedicatedProps &props, ALenum param, const float *vals) +{ SetParamf(context, props, param, *vals); } -void Dedicated_getParami(const EffectProps*, ALenum param, int*) -{ throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer property 0x%04x", param}; } -void Dedicated_getParamiv(const EffectProps*, ALenum param, int*) +void DedicatedDialogEffectHandler::GetParami(ALCcontext *context, const DedicatedProps&, ALenum param, int*) +{ context->throw_error(AL_INVALID_ENUM, "Invalid dedicated integer property {:#04x}", as_unsigned(param)); } +void DedicatedDialogEffectHandler::GetParamiv(ALCcontext *context, const DedicatedProps&, ALenum param, int*) +{ context->throw_error(AL_INVALID_ENUM, "Invalid dedicated integer-vector property {:#04x}", as_unsigned(param)); } +void DedicatedDialogEffectHandler::GetParamf(ALCcontext *context, const DedicatedProps &props, ALenum param, float *val) { - throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated integer-vector property 0x%04x", - param}; + switch(param) + { + case AL_DEDICATED_GAIN: *val = props.Gain; return; + } + + context->throw_error(AL_INVALID_ENUM, "Invalid dedicated float property {:#04x}", + as_unsigned(param)); } -void Dedicated_getParamf(const EffectProps *props, ALenum param, float *val) +void DedicatedDialogEffectHandler::GetParamfv(ALCcontext *context, const DedicatedProps &props, ALenum param, float *vals) +{ GetParamf(context, props, param, vals); } + + +const EffectProps DedicatedLfeEffectProps{genDefaultLfeProps()}; + +void DedicatedLfeEffectHandler::SetParami(ALCcontext *context, DedicatedProps&, ALenum param, int) +{ context->throw_error(AL_INVALID_ENUM, "Invalid dedicated integer property {:#04x}", as_unsigned(param)); } +void DedicatedLfeEffectHandler::SetParamiv(ALCcontext *context, DedicatedProps&, ALenum param, const int*) +{ context->throw_error(AL_INVALID_ENUM, "Invalid dedicated integer-vector property {:#04x}", as_unsigned(param)); } +void DedicatedLfeEffectHandler::SetParamf(ALCcontext *context, DedicatedProps &props, ALenum param, float val) { switch(param) { case AL_DEDICATED_GAIN: - *val = props->Dedicated.Gain; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid dedicated float property 0x%04x", param}; + if(!(val >= 0.0f && std::isfinite(val))) + context->throw_error(AL_INVALID_VALUE, "Dedicated gain out of range"); + props.Gain = val; + return; } -} -void Dedicated_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Dedicated_getParamf(props, param, vals); } -EffectProps genDefaultProps() noexcept -{ - EffectProps props{}; - props.Dedicated.Gain = 1.0f; - return props; + context->throw_error(AL_INVALID_ENUM, "Invalid dedicated float property {:#04x}", + as_unsigned(param)); } +void DedicatedLfeEffectHandler::SetParamfv(ALCcontext *context, DedicatedProps &props, ALenum param, const float *vals) +{ SetParamf(context, props, param, *vals); } -} // namespace - -DEFINE_ALEFFECT_VTABLE(Dedicated); +void DedicatedLfeEffectHandler::GetParami(ALCcontext *context, const DedicatedProps&, ALenum param, int*) +{ context->throw_error(AL_INVALID_ENUM, "Invalid dedicated integer property {:#04x}", as_unsigned(param)); } +void DedicatedLfeEffectHandler::GetParamiv(ALCcontext *context, const DedicatedProps&, ALenum param, int*) +{ context->throw_error(AL_INVALID_ENUM, "Invalid dedicated integer-vector property {:#04x}", as_unsigned(param)); } +void DedicatedLfeEffectHandler::GetParamf(ALCcontext *context, const DedicatedProps &props, ALenum param, float *val) +{ + switch(param) + { + case AL_DEDICATED_GAIN: *val = props.Gain; return; + } -const EffectProps DedicatedEffectProps{genDefaultProps()}; + context->throw_error(AL_INVALID_ENUM, "Invalid dedicated float property {:#04x}", + as_unsigned(param)); +} +void DedicatedLfeEffectHandler::GetParamfv(ALCcontext *context, const DedicatedProps &props, ALenum param, float *vals) +{ GetParamf(context, props, param, vals); } diff --git a/3rdparty/openal/al/effects/distortion.cpp b/3rdparty/openal/al/effects/distortion.cpp index e046d8e72490..2109e384bcd1 100644 --- a/3rdparty/openal/al/effects/distortion.cpp +++ b/3rdparty/openal/al/effects/distortion.cpp @@ -4,11 +4,12 @@ #include "AL/al.h" #include "AL/efx.h" -#include "alc/effects/base.h" +#include "alc/context.h" +#include "alnumeric.h" #include "effects.h" -#ifdef ALSOFT_EAX -#include "alnumeric.h" +#if ALSOFT_EAX +#include "al/eax/effect.h" #include "al/eax/exception.h" #include "al/eax/utils.h" #endif // ALSOFT_EAX @@ -16,110 +17,91 @@ namespace { -void Distortion_setParami(EffectProps*, ALenum param, int) -{ throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param}; } -void Distortion_setParamiv(EffectProps*, ALenum param, const int*) +constexpr EffectProps genDefaultProps() noexcept { - throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x", - param}; + DistortionProps props{}; + props.Edge = AL_DISTORTION_DEFAULT_EDGE; + props.Gain = AL_DISTORTION_DEFAULT_GAIN; + props.LowpassCutoff = AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF; + props.EQCenter = AL_DISTORTION_DEFAULT_EQCENTER; + props.EQBandwidth = AL_DISTORTION_DEFAULT_EQBANDWIDTH; + return props; } -void Distortion_setParamf(EffectProps *props, ALenum param, float val) + +} // namespace + +const EffectProps DistortionEffectProps{genDefaultProps()}; + +void DistortionEffectHandler::SetParami(ALCcontext *context, DistortionProps&, ALenum param, int) +{ context->throw_error(AL_INVALID_ENUM, "Invalid distortion integer property {:#04x}", as_unsigned(param)); } +void DistortionEffectHandler::SetParamiv(ALCcontext *context, DistortionProps&, ALenum param, const int*) +{ context->throw_error(AL_INVALID_ENUM, "Invalid distortion integer-vector property {:#04x}", as_unsigned(param)); } + +void DistortionEffectHandler::SetParamf(ALCcontext *context, DistortionProps &props, ALenum param, float val) { switch(param) { case AL_DISTORTION_EDGE: if(!(val >= AL_DISTORTION_MIN_EDGE && val <= AL_DISTORTION_MAX_EDGE)) - throw effect_exception{AL_INVALID_VALUE, "Distortion edge out of range"}; - props->Distortion.Edge = val; - break; + context->throw_error(AL_INVALID_VALUE, "Distortion edge out of range"); + props.Edge = val; + return; case AL_DISTORTION_GAIN: if(!(val >= AL_DISTORTION_MIN_GAIN && val <= AL_DISTORTION_MAX_GAIN)) - throw effect_exception{AL_INVALID_VALUE, "Distortion gain out of range"}; - props->Distortion.Gain = val; - break; + context->throw_error(AL_INVALID_VALUE, "Distortion gain out of range"); + props.Gain = val; + return; case AL_DISTORTION_LOWPASS_CUTOFF: if(!(val >= AL_DISTORTION_MIN_LOWPASS_CUTOFF && val <= AL_DISTORTION_MAX_LOWPASS_CUTOFF)) - throw effect_exception{AL_INVALID_VALUE, "Distortion low-pass cutoff out of range"}; - props->Distortion.LowpassCutoff = val; - break; + context->throw_error(AL_INVALID_VALUE, "Distortion low-pass cutoff out of range"); + props.LowpassCutoff = val; + return; case AL_DISTORTION_EQCENTER: if(!(val >= AL_DISTORTION_MIN_EQCENTER && val <= AL_DISTORTION_MAX_EQCENTER)) - throw effect_exception{AL_INVALID_VALUE, "Distortion EQ center out of range"}; - props->Distortion.EQCenter = val; - break; + context->throw_error(AL_INVALID_VALUE, "Distortion EQ center out of range"); + props.EQCenter = val; + return; case AL_DISTORTION_EQBANDWIDTH: if(!(val >= AL_DISTORTION_MIN_EQBANDWIDTH && val <= AL_DISTORTION_MAX_EQBANDWIDTH)) - throw effect_exception{AL_INVALID_VALUE, "Distortion EQ bandwidth out of range"}; - props->Distortion.EQBandwidth = val; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid distortion float property 0x%04x", param}; + context->throw_error(AL_INVALID_VALUE, "Distortion EQ bandwidth out of range"); + props.EQBandwidth = val; + return; } -} -void Distortion_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Distortion_setParamf(props, param, vals[0]); } -void Distortion_getParami(const EffectProps*, ALenum param, int*) -{ throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param}; } -void Distortion_getParamiv(const EffectProps*, ALenum param, int*) -{ - throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x", - param}; + context->throw_error(AL_INVALID_ENUM, "Invalid distortion float property {:#04x}", + as_unsigned(param)); } -void Distortion_getParamf(const EffectProps *props, ALenum param, float *val) +void DistortionEffectHandler::SetParamfv(ALCcontext *context, DistortionProps &props, ALenum param, const float *vals) +{ SetParamf(context, props, param, *vals); } + +void DistortionEffectHandler::GetParami(ALCcontext *context, const DistortionProps&, ALenum param, int*) +{ context->throw_error(AL_INVALID_ENUM, "Invalid distortion integer property {:#04x}", as_unsigned(param)); } +void DistortionEffectHandler::GetParamiv(ALCcontext *context, const DistortionProps&, ALenum param, int*) +{ context->throw_error(AL_INVALID_ENUM, "Invalid distortion integer-vector property {:#04x}", as_unsigned(param)); } + +void DistortionEffectHandler::GetParamf(ALCcontext *context, const DistortionProps &props, ALenum param, float *val) { switch(param) { - case AL_DISTORTION_EDGE: - *val = props->Distortion.Edge; - break; - - case AL_DISTORTION_GAIN: - *val = props->Distortion.Gain; - break; - - case AL_DISTORTION_LOWPASS_CUTOFF: - *val = props->Distortion.LowpassCutoff; - break; - - case AL_DISTORTION_EQCENTER: - *val = props->Distortion.EQCenter; - break; - - case AL_DISTORTION_EQBANDWIDTH: - *val = props->Distortion.EQBandwidth; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid distortion float property 0x%04x", param}; + case AL_DISTORTION_EDGE: *val = props.Edge; return; + case AL_DISTORTION_GAIN: *val = props.Gain; return; + case AL_DISTORTION_LOWPASS_CUTOFF: *val = props.LowpassCutoff; return; + case AL_DISTORTION_EQCENTER: *val = props.EQCenter; return; + case AL_DISTORTION_EQBANDWIDTH: *val = props.EQBandwidth; return; } -} -void Distortion_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Distortion_getParamf(props, param, vals); } -EffectProps genDefaultProps() noexcept -{ - EffectProps props{}; - props.Distortion.Edge = AL_DISTORTION_DEFAULT_EDGE; - props.Distortion.Gain = AL_DISTORTION_DEFAULT_GAIN; - props.Distortion.LowpassCutoff = AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF; - props.Distortion.EQCenter = AL_DISTORTION_DEFAULT_EQCENTER; - props.Distortion.EQBandwidth = AL_DISTORTION_DEFAULT_EQBANDWIDTH; - return props; + context->throw_error(AL_INVALID_ENUM, "Invalid distortion float property {:#04x}", + as_unsigned(param)); } +void DistortionEffectHandler::GetParamfv(ALCcontext *context, const DistortionProps &props, ALenum param, float *vals) +{ GetParamf(context, props, param, vals); } -} // namespace - -DEFINE_ALEFFECT_VTABLE(Distortion); - -const EffectProps DistortionEffectProps{genDefaultProps()}; -#ifdef ALSOFT_EAX +#if ALSOFT_EAX namespace { using DistortionCommitter = EaxCommitter; @@ -204,26 +186,26 @@ template<> throw Exception{message}; } -template<> -bool DistortionCommitter::commit(const EaxEffectProps &props) +bool EaxDistortionCommitter::commit(const EAXDISTORTIONPROPERTIES &props) { - if(props == mEaxProps) + if(auto *cur = std::get_if(&mEaxProps); cur && *cur == props) return false; mEaxProps = props; - - auto &eaxprops = std::get(props); - mAlProps.Distortion.Edge = eaxprops.flEdge; - mAlProps.Distortion.Gain = level_mb_to_gain(static_cast(eaxprops.lGain)); - mAlProps.Distortion.LowpassCutoff = eaxprops.flLowPassCutOff; - mAlProps.Distortion.EQCenter = eaxprops.flEQCenter; - mAlProps.Distortion.EQBandwidth = eaxprops.flEdge; + mAlProps = [&]{ + DistortionProps ret{}; + ret.Edge = props.flEdge; + ret.Gain = level_mb_to_gain(static_cast(props.lGain)); + ret.LowpassCutoff = props.flLowPassCutOff; + ret.EQCenter = props.flEQCenter; + ret.EQBandwidth = props.flEdge; + return ret; + }(); return true; } -template<> -void DistortionCommitter::SetDefaults(EaxEffectProps &props) +void EaxDistortionCommitter::SetDefaults(EaxEffectProps &props) { static constexpr EAXDISTORTIONPROPERTIES defprops{[] { @@ -238,10 +220,8 @@ void DistortionCommitter::SetDefaults(EaxEffectProps &props) props = defprops; } -template<> -void DistortionCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) +void EaxDistortionCommitter::Get(const EaxCall &call, const EAXDISTORTIONPROPERTIES &props) { - auto &props = std::get(props_); switch(call.get_property_id()) { case EAXDISTORTION_NONE: break; @@ -255,10 +235,8 @@ void DistortionCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) } } -template<> -void DistortionCommitter::Set(const EaxCall &call, EaxEffectProps &props_) +void EaxDistortionCommitter::Set(const EaxCall &call, EAXDISTORTIONPROPERTIES &props) { - auto &props = std::get(props_); switch(call.get_property_id()) { case EAXDISTORTION_NONE: break; diff --git a/3rdparty/openal/al/effects/echo.cpp b/3rdparty/openal/al/effects/echo.cpp index 48aacef34b0f..2a5faf0d0a43 100644 --- a/3rdparty/openal/al/effects/echo.cpp +++ b/3rdparty/openal/al/effects/echo.cpp @@ -4,11 +4,12 @@ #include "AL/al.h" #include "AL/efx.h" -#include "alc/effects/base.h" +#include "alc/context.h" +#include "alnumeric.h" #include "effects.h" -#ifdef ALSOFT_EAX -#include "alnumeric.h" +#if ALSOFT_EAX +#include "al/eax/effect.h" #include "al/eax/exception.h" #include "al/eax/utils.h" #endif // ALSOFT_EAX @@ -19,104 +20,89 @@ namespace { static_assert(EchoMaxDelay >= AL_ECHO_MAX_DELAY, "Echo max delay too short"); static_assert(EchoMaxLRDelay >= AL_ECHO_MAX_LRDELAY, "Echo max left-right delay too short"); -void Echo_setParami(EffectProps*, ALenum param, int) -{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param}; } -void Echo_setParamiv(EffectProps*, ALenum param, const int*) -{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param}; } -void Echo_setParamf(EffectProps *props, ALenum param, float val) +constexpr EffectProps genDefaultProps() noexcept +{ + EchoProps props{}; + props.Delay = AL_ECHO_DEFAULT_DELAY; + props.LRDelay = AL_ECHO_DEFAULT_LRDELAY; + props.Damping = AL_ECHO_DEFAULT_DAMPING; + props.Feedback = AL_ECHO_DEFAULT_FEEDBACK; + props.Spread = AL_ECHO_DEFAULT_SPREAD; + return props; +} + +} // namespace + +const EffectProps EchoEffectProps{genDefaultProps()}; + +void EchoEffectHandler::SetParami(ALCcontext *context, EchoProps&, ALenum param, int) +{ context->throw_error(AL_INVALID_ENUM, "Invalid echo integer property {:#04x}", as_unsigned(param)); } +void EchoEffectHandler::SetParamiv(ALCcontext *context, EchoProps&, ALenum param, const int*) +{ context->throw_error(AL_INVALID_ENUM, "Invalid echo integer-vector property {:#04x}", as_unsigned(param)); } +void EchoEffectHandler::SetParamf(ALCcontext *context, EchoProps &props, ALenum param, float val) { switch(param) { case AL_ECHO_DELAY: if(!(val >= AL_ECHO_MIN_DELAY && val <= AL_ECHO_MAX_DELAY)) - throw effect_exception{AL_INVALID_VALUE, "Echo delay out of range"}; - props->Echo.Delay = val; - break; + context->throw_error(AL_INVALID_VALUE, "Echo delay out of range"); + props.Delay = val; + return; case AL_ECHO_LRDELAY: if(!(val >= AL_ECHO_MIN_LRDELAY && val <= AL_ECHO_MAX_LRDELAY)) - throw effect_exception{AL_INVALID_VALUE, "Echo LR delay out of range"}; - props->Echo.LRDelay = val; - break; + context->throw_error(AL_INVALID_VALUE, "Echo LR delay out of range"); + props.LRDelay = val; + return; case AL_ECHO_DAMPING: if(!(val >= AL_ECHO_MIN_DAMPING && val <= AL_ECHO_MAX_DAMPING)) - throw effect_exception{AL_INVALID_VALUE, "Echo damping out of range"}; - props->Echo.Damping = val; - break; + context->throw_error(AL_INVALID_VALUE, "Echo damping out of range"); + props.Damping = val; + return; case AL_ECHO_FEEDBACK: if(!(val >= AL_ECHO_MIN_FEEDBACK && val <= AL_ECHO_MAX_FEEDBACK)) - throw effect_exception{AL_INVALID_VALUE, "Echo feedback out of range"}; - props->Echo.Feedback = val; - break; + context->throw_error(AL_INVALID_VALUE, "Echo feedback out of range"); + props.Feedback = val; + return; case AL_ECHO_SPREAD: if(!(val >= AL_ECHO_MIN_SPREAD && val <= AL_ECHO_MAX_SPREAD)) - throw effect_exception{AL_INVALID_VALUE, "Echo spread out of range"}; - props->Echo.Spread = val; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param}; + context->throw_error(AL_INVALID_VALUE, "Echo spread out of range"); + props.Spread = val; + return; } + + context->throw_error(AL_INVALID_ENUM, "Invalid echo float property {:#04x}", + as_unsigned(param)); } -void Echo_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Echo_setParamf(props, param, vals[0]); } - -void Echo_getParami(const EffectProps*, ALenum param, int*) -{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param}; } -void Echo_getParamiv(const EffectProps*, ALenum param, int*) -{ throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param}; } -void Echo_getParamf(const EffectProps *props, ALenum param, float *val) +void EchoEffectHandler::SetParamfv(ALCcontext *context, EchoProps &props, ALenum param, const float *vals) +{ SetParamf(context, props, param, *vals); } + +void EchoEffectHandler::GetParami(ALCcontext *context, const EchoProps&, ALenum param, int*) +{ context->throw_error(AL_INVALID_ENUM, "Invalid echo integer property {:#04x}", as_unsigned(param)); } +void EchoEffectHandler::GetParamiv(ALCcontext *context, const EchoProps&, ALenum param, int*) +{ context->throw_error(AL_INVALID_ENUM, "Invalid echo integer-vector property {:#04x}", as_unsigned(param)); } +void EchoEffectHandler::GetParamf(ALCcontext *context, const EchoProps &props, ALenum param, float *val) { switch(param) { - case AL_ECHO_DELAY: - *val = props->Echo.Delay; - break; - - case AL_ECHO_LRDELAY: - *val = props->Echo.LRDelay; - break; - - case AL_ECHO_DAMPING: - *val = props->Echo.Damping; - break; - - case AL_ECHO_FEEDBACK: - *val = props->Echo.Feedback; - break; - - case AL_ECHO_SPREAD: - *val = props->Echo.Spread; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param}; + case AL_ECHO_DELAY: *val = props.Delay; return; + case AL_ECHO_LRDELAY: *val = props.LRDelay; return; + case AL_ECHO_DAMPING: *val = props.Damping; return; + case AL_ECHO_FEEDBACK: *val = props.Feedback; return; + case AL_ECHO_SPREAD: *val = props.Spread; return; } -} -void Echo_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Echo_getParamf(props, param, vals); } -EffectProps genDefaultProps() noexcept -{ - EffectProps props{}; - props.Echo.Delay = AL_ECHO_DEFAULT_DELAY; - props.Echo.LRDelay = AL_ECHO_DEFAULT_LRDELAY; - props.Echo.Damping = AL_ECHO_DEFAULT_DAMPING; - props.Echo.Feedback = AL_ECHO_DEFAULT_FEEDBACK; - props.Echo.Spread = AL_ECHO_DEFAULT_SPREAD; - return props; + context->throw_error(AL_INVALID_ENUM, "Invalid echo float property {:#04x}", + as_unsigned(param)); } +void EchoEffectHandler::GetParamfv(ALCcontext *context, const EchoProps &props, ALenum param, float *vals) +{ GetParamf(context, props, param, vals); } -} // namespace - -DEFINE_ALEFFECT_VTABLE(Echo); -const EffectProps EchoEffectProps{genDefaultProps()}; - -#ifdef ALSOFT_EAX +#if ALSOFT_EAX namespace { using EchoCommitter = EaxCommitter; @@ -201,26 +187,26 @@ template<> throw Exception{message}; } -template<> -bool EchoCommitter::commit(const EaxEffectProps &props) +bool EaxEchoCommitter::commit(const EAXECHOPROPERTIES &props) { - if(props == mEaxProps) + if(auto *cur = std::get_if(&mEaxProps); cur && *cur == props) return false; mEaxProps = props; - - auto &eaxprops = std::get(props); - mAlProps.Echo.Delay = eaxprops.flDelay; - mAlProps.Echo.LRDelay = eaxprops.flLRDelay; - mAlProps.Echo.Damping = eaxprops.flDamping; - mAlProps.Echo.Feedback = eaxprops.flFeedback; - mAlProps.Echo.Spread = eaxprops.flSpread; + mAlProps = [&]{ + EchoProps ret{}; + ret.Delay = props.flDelay; + ret.LRDelay = props.flLRDelay; + ret.Damping = props.flDamping; + ret.Feedback = props.flFeedback; + ret.Spread = props.flSpread; + return ret; + }(); return true; } -template<> -void EchoCommitter::SetDefaults(EaxEffectProps &props) +void EaxEchoCommitter::SetDefaults(EaxEffectProps &props) { static constexpr EAXECHOPROPERTIES defprops{[] { @@ -235,10 +221,8 @@ void EchoCommitter::SetDefaults(EaxEffectProps &props) props = defprops; } -template<> -void EchoCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) +void EaxEchoCommitter::Get(const EaxCall &call, const EAXECHOPROPERTIES &props) { - auto &props = std::get(props_); switch(call.get_property_id()) { case EAXECHO_NONE: break; @@ -252,10 +236,8 @@ void EchoCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) } } -template<> -void EchoCommitter::Set(const EaxCall &call, EaxEffectProps &props_) +void EaxEchoCommitter::Set(const EaxCall &call, EAXECHOPROPERTIES &props) { - auto &props = std::get(props_); switch(call.get_property_id()) { case EAXECHO_NONE: break; diff --git a/3rdparty/openal/al/effects/effects.cpp b/3rdparty/openal/al/effects/effects.cpp index 4a67b5ffeb45..ace4e70665e1 100644 --- a/3rdparty/openal/al/effects/effects.cpp +++ b/3rdparty/openal/al/effects/effects.cpp @@ -1,9 +1,3 @@ #include "config.h" -#ifdef ALSOFT_EAX - -#include -#include "AL/efx.h" #include "effects.h" - -#endif // ALSOFT_EAX diff --git a/3rdparty/openal/al/effects/effects.h b/3rdparty/openal/al/effects/effects.h index 9d57dd820e8a..76e5c9105b17 100644 --- a/3rdparty/openal/al/effects/effects.h +++ b/3rdparty/openal/al/effects/effects.h @@ -1,88 +1,64 @@ #ifndef AL_EFFECTS_EFFECTS_H #define AL_EFFECTS_EFFECTS_H -#include "AL/al.h" - -#include "core/except.h" - -#ifdef ALSOFT_EAX -#include "al/eax/effect.h" -#endif // ALSOFT_EAX - -union EffectProps; - - -class effect_exception final : public al::base_exception { - ALenum mErrorCode; - -public: -#ifdef __USE_MINGW_ANSI_STDIO - [[gnu::format(gnu_printf, 3, 4)]] -#else - [[gnu::format(printf, 3, 4)]] -#endif - effect_exception(ALenum code, const char *msg, ...); - ~effect_exception() override; +#include - ALenum errorCode() const noexcept { return mErrorCode; } -}; - - -struct EffectVtable { - void (*const setParami)(EffectProps *props, ALenum param, int val); - void (*const setParamiv)(EffectProps *props, ALenum param, const int *vals); - void (*const setParamf)(EffectProps *props, ALenum param, float val); - void (*const setParamfv)(EffectProps *props, ALenum param, const float *vals); +#include "AL/alc.h" +#include "AL/al.h" - void (*const getParami)(const EffectProps *props, ALenum param, int *val); - void (*const getParamiv)(const EffectProps *props, ALenum param, int *vals); - void (*const getParamf)(const EffectProps *props, ALenum param, float *val); - void (*const getParamfv)(const EffectProps *props, ALenum param, float *vals); +#include "core/effects/base.h" +#include "opthelpers.h" + +#define DECL_HANDLER(N, T) \ +struct N { \ + using prop_type = T; \ + \ + static void SetParami(ALCcontext *context, prop_type &props, ALenum param, int val); \ + static void SetParamiv(ALCcontext *context, prop_type &props, ALenum param, const int *vals); \ + static void SetParamf(ALCcontext *context, prop_type &props, ALenum param, float val); \ + static void SetParamfv(ALCcontext *context, prop_type &props, ALenum param, const float *vals);\ + static void GetParami(ALCcontext *context, const prop_type &props, ALenum param, int *val); \ + static void GetParamiv(ALCcontext *context, const prop_type &props, ALenum param, int *vals); \ + static void GetParamf(ALCcontext *context, const prop_type &props, ALenum param, float *val); \ + static void GetParamfv(ALCcontext *context, const prop_type &props, ALenum param, float *vals);\ }; - -#define DEFINE_ALEFFECT_VTABLE(T) \ -const EffectVtable T##EffectVtable = { \ - T##_setParami, T##_setParamiv, \ - T##_setParamf, T##_setParamfv, \ - T##_getParami, T##_getParamiv, \ - T##_getParamf, T##_getParamfv, \ -} +DECL_HANDLER(NullEffectHandler, std::monostate) +DECL_HANDLER(ReverbEffectHandler, ReverbProps) +DECL_HANDLER(StdReverbEffectHandler, ReverbProps) +DECL_HANDLER(AutowahEffectHandler, AutowahProps) +DECL_HANDLER(ChorusEffectHandler, ChorusProps) +DECL_HANDLER(CompressorEffectHandler, CompressorProps) +DECL_HANDLER(DistortionEffectHandler, DistortionProps) +DECL_HANDLER(EchoEffectHandler, EchoProps) +DECL_HANDLER(EqualizerEffectHandler, EqualizerProps) +DECL_HANDLER(FlangerEffectHandler, ChorusProps) +DECL_HANDLER(FshifterEffectHandler, FshifterProps) +DECL_HANDLER(ModulatorEffectHandler, ModulatorProps) +DECL_HANDLER(PshifterEffectHandler, PshifterProps) +DECL_HANDLER(VmorpherEffectHandler, VmorpherProps) +DECL_HANDLER(DedicatedDialogEffectHandler, DedicatedProps) +DECL_HANDLER(DedicatedLfeEffectHandler, DedicatedProps) +DECL_HANDLER(ConvolutionEffectHandler, ConvolutionProps) +#undef DECL_HANDLER /* Default properties for the given effect types. */ -extern const EffectProps NullEffectProps; -extern const EffectProps ReverbEffectProps; -extern const EffectProps StdReverbEffectProps; -extern const EffectProps AutowahEffectProps; -extern const EffectProps ChorusEffectProps; -extern const EffectProps CompressorEffectProps; -extern const EffectProps DistortionEffectProps; -extern const EffectProps EchoEffectProps; -extern const EffectProps EqualizerEffectProps; -extern const EffectProps FlangerEffectProps; -extern const EffectProps FshifterEffectProps; -extern const EffectProps ModulatorEffectProps; -extern const EffectProps PshifterEffectProps; -extern const EffectProps VmorpherEffectProps; -extern const EffectProps DedicatedEffectProps; -extern const EffectProps ConvolutionEffectProps; - -/* Vtables to get/set properties for the given effect types. */ -extern const EffectVtable NullEffectVtable; -extern const EffectVtable ReverbEffectVtable; -extern const EffectVtable StdReverbEffectVtable; -extern const EffectVtable AutowahEffectVtable; -extern const EffectVtable ChorusEffectVtable; -extern const EffectVtable CompressorEffectVtable; -extern const EffectVtable DistortionEffectVtable; -extern const EffectVtable EchoEffectVtable; -extern const EffectVtable EqualizerEffectVtable; -extern const EffectVtable FlangerEffectVtable; -extern const EffectVtable FshifterEffectVtable; -extern const EffectVtable ModulatorEffectVtable; -extern const EffectVtable PshifterEffectVtable; -extern const EffectVtable VmorpherEffectVtable; -extern const EffectVtable DedicatedEffectVtable; -extern const EffectVtable ConvolutionEffectVtable; +DECL_HIDDEN extern const EffectProps NullEffectProps; +DECL_HIDDEN extern const EffectProps ReverbEffectProps; +DECL_HIDDEN extern const EffectProps StdReverbEffectProps; +DECL_HIDDEN extern const EffectProps AutowahEffectProps; +DECL_HIDDEN extern const EffectProps ChorusEffectProps; +DECL_HIDDEN extern const EffectProps CompressorEffectProps; +DECL_HIDDEN extern const EffectProps DistortionEffectProps; +DECL_HIDDEN extern const EffectProps EchoEffectProps; +DECL_HIDDEN extern const EffectProps EqualizerEffectProps; +DECL_HIDDEN extern const EffectProps FlangerEffectProps; +DECL_HIDDEN extern const EffectProps FshifterEffectProps; +DECL_HIDDEN extern const EffectProps ModulatorEffectProps; +DECL_HIDDEN extern const EffectProps PshifterEffectProps; +DECL_HIDDEN extern const EffectProps VmorpherEffectProps; +DECL_HIDDEN extern const EffectProps DedicatedDialogEffectProps; +DECL_HIDDEN extern const EffectProps DedicatedLfeEffectProps; +DECL_HIDDEN extern const EffectProps ConvolutionEffectProps; #endif /* AL_EFFECTS_EFFECTS_H */ diff --git a/3rdparty/openal/al/effects/equalizer.cpp b/3rdparty/openal/al/effects/equalizer.cpp index 76d5bdef07cc..360fd5d8f3e3 100644 --- a/3rdparty/openal/al/effects/equalizer.cpp +++ b/3rdparty/openal/al/effects/equalizer.cpp @@ -4,11 +4,12 @@ #include "AL/al.h" #include "AL/efx.h" -#include "alc/effects/base.h" +#include "alc/context.h" +#include "alnumeric.h" #include "effects.h" -#ifdef ALSOFT_EAX -#include "alnumeric.h" +#if ALSOFT_EAX +#include "al/eax/effect.h" #include "al/eax/exception.h" #include "al/eax/utils.h" #endif // ALSOFT_EAX @@ -16,165 +17,129 @@ namespace { -void Equalizer_setParami(EffectProps*, ALenum param, int) -{ throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param}; } -void Equalizer_setParamiv(EffectProps*, ALenum param, const int*) +constexpr EffectProps genDefaultProps() noexcept { - throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x", - param}; + EqualizerProps props{}; + props.LowCutoff = AL_EQUALIZER_DEFAULT_LOW_CUTOFF; + props.LowGain = AL_EQUALIZER_DEFAULT_LOW_GAIN; + props.Mid1Center = AL_EQUALIZER_DEFAULT_MID1_CENTER; + props.Mid1Gain = AL_EQUALIZER_DEFAULT_MID1_GAIN; + props.Mid1Width = AL_EQUALIZER_DEFAULT_MID1_WIDTH; + props.Mid2Center = AL_EQUALIZER_DEFAULT_MID2_CENTER; + props.Mid2Gain = AL_EQUALIZER_DEFAULT_MID2_GAIN; + props.Mid2Width = AL_EQUALIZER_DEFAULT_MID2_WIDTH; + props.HighCutoff = AL_EQUALIZER_DEFAULT_HIGH_CUTOFF; + props.HighGain = AL_EQUALIZER_DEFAULT_HIGH_GAIN; + return props; } -void Equalizer_setParamf(EffectProps *props, ALenum param, float val) + +} // namespace + +const EffectProps EqualizerEffectProps{genDefaultProps()}; + +void EqualizerEffectHandler::SetParami(ALCcontext *context, EqualizerProps&, ALenum param, int) +{ context->throw_error(AL_INVALID_ENUM, "Invalid equalizer integer property {:#04x}", as_unsigned(param)); } +void EqualizerEffectHandler::SetParamiv(ALCcontext *context, EqualizerProps&, ALenum param, const int*) +{ context->throw_error(AL_INVALID_ENUM, "Invalid equalizer integer-vector property {:#04x}", as_unsigned(param)); } +void EqualizerEffectHandler::SetParamf(ALCcontext *context, EqualizerProps &props, ALenum param, float val) { switch(param) { case AL_EQUALIZER_LOW_GAIN: if(!(val >= AL_EQUALIZER_MIN_LOW_GAIN && val <= AL_EQUALIZER_MAX_LOW_GAIN)) - throw effect_exception{AL_INVALID_VALUE, "Equalizer low-band gain out of range"}; - props->Equalizer.LowGain = val; - break; + context->throw_error(AL_INVALID_VALUE, "Equalizer low-band gain out of range"); + props.LowGain = val; + return; case AL_EQUALIZER_LOW_CUTOFF: if(!(val >= AL_EQUALIZER_MIN_LOW_CUTOFF && val <= AL_EQUALIZER_MAX_LOW_CUTOFF)) - throw effect_exception{AL_INVALID_VALUE, "Equalizer low-band cutoff out of range"}; - props->Equalizer.LowCutoff = val; - break; + context->throw_error(AL_INVALID_VALUE, "Equalizer low-band cutoff out of range"); + props.LowCutoff = val; + return; case AL_EQUALIZER_MID1_GAIN: if(!(val >= AL_EQUALIZER_MIN_MID1_GAIN && val <= AL_EQUALIZER_MAX_MID1_GAIN)) - throw effect_exception{AL_INVALID_VALUE, "Equalizer mid1-band gain out of range"}; - props->Equalizer.Mid1Gain = val; - break; + context->throw_error(AL_INVALID_VALUE, "Equalizer mid1-band gain out of range"); + props.Mid1Gain = val; + return; case AL_EQUALIZER_MID1_CENTER: if(!(val >= AL_EQUALIZER_MIN_MID1_CENTER && val <= AL_EQUALIZER_MAX_MID1_CENTER)) - throw effect_exception{AL_INVALID_VALUE, "Equalizer mid1-band center out of range"}; - props->Equalizer.Mid1Center = val; - break; + context->throw_error(AL_INVALID_VALUE, "Equalizer mid1-band center out of range"); + props.Mid1Center = val; + return; case AL_EQUALIZER_MID1_WIDTH: if(!(val >= AL_EQUALIZER_MIN_MID1_WIDTH && val <= AL_EQUALIZER_MAX_MID1_WIDTH)) - throw effect_exception{AL_INVALID_VALUE, "Equalizer mid1-band width out of range"}; - props->Equalizer.Mid1Width = val; - break; + context->throw_error(AL_INVALID_VALUE, "Equalizer mid1-band width out of range"); + props.Mid1Width = val; + return; case AL_EQUALIZER_MID2_GAIN: if(!(val >= AL_EQUALIZER_MIN_MID2_GAIN && val <= AL_EQUALIZER_MAX_MID2_GAIN)) - throw effect_exception{AL_INVALID_VALUE, "Equalizer mid2-band gain out of range"}; - props->Equalizer.Mid2Gain = val; - break; + context->throw_error(AL_INVALID_VALUE, "Equalizer mid2-band gain out of range"); + props.Mid2Gain = val; + return; case AL_EQUALIZER_MID2_CENTER: if(!(val >= AL_EQUALIZER_MIN_MID2_CENTER && val <= AL_EQUALIZER_MAX_MID2_CENTER)) - throw effect_exception{AL_INVALID_VALUE, "Equalizer mid2-band center out of range"}; - props->Equalizer.Mid2Center = val; - break; + context->throw_error(AL_INVALID_VALUE, "Equalizer mid2-band center out of range"); + props.Mid2Center = val; + return; case AL_EQUALIZER_MID2_WIDTH: if(!(val >= AL_EQUALIZER_MIN_MID2_WIDTH && val <= AL_EQUALIZER_MAX_MID2_WIDTH)) - throw effect_exception{AL_INVALID_VALUE, "Equalizer mid2-band width out of range"}; - props->Equalizer.Mid2Width = val; - break; + context->throw_error(AL_INVALID_VALUE, "Equalizer mid2-band width out of range"); + props.Mid2Width = val; + return; case AL_EQUALIZER_HIGH_GAIN: if(!(val >= AL_EQUALIZER_MIN_HIGH_GAIN && val <= AL_EQUALIZER_MAX_HIGH_GAIN)) - throw effect_exception{AL_INVALID_VALUE, "Equalizer high-band gain out of range"}; - props->Equalizer.HighGain = val; - break; + context->throw_error(AL_INVALID_VALUE, "Equalizer high-band gain out of range"); + props.HighGain = val; + return; case AL_EQUALIZER_HIGH_CUTOFF: if(!(val >= AL_EQUALIZER_MIN_HIGH_CUTOFF && val <= AL_EQUALIZER_MAX_HIGH_CUTOFF)) - throw effect_exception{AL_INVALID_VALUE, "Equalizer high-band cutoff out of range"}; - props->Equalizer.HighCutoff = val; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param}; + context->throw_error(AL_INVALID_VALUE, "Equalizer high-band cutoff out of range"); + props.HighCutoff = val; + return; } -} -void Equalizer_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Equalizer_setParamf(props, param, vals[0]); } -void Equalizer_getParami(const EffectProps*, ALenum param, int*) -{ throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer property 0x%04x", param}; } -void Equalizer_getParamiv(const EffectProps*, ALenum param, int*) -{ - throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer integer-vector property 0x%04x", - param}; + context->throw_error(AL_INVALID_ENUM, "Invalid equalizer float property {:#04x}", + as_unsigned(param)); } -void Equalizer_getParamf(const EffectProps *props, ALenum param, float *val) +void EqualizerEffectHandler::SetParamfv(ALCcontext *context, EqualizerProps &props, ALenum param, const float *vals) +{ SetParamf(context, props, param, *vals); } + +void EqualizerEffectHandler::GetParami(ALCcontext *context, const EqualizerProps&, ALenum param, int*) +{ context->throw_error(AL_INVALID_ENUM, "Invalid equalizer integer property {:#04x}", as_unsigned(param)); } +void EqualizerEffectHandler::GetParamiv(ALCcontext *context, const EqualizerProps&, ALenum param, int*) +{ context->throw_error(AL_INVALID_ENUM, "Invalid equalizer integer-vector property {:#04x}", as_unsigned(param)); } +void EqualizerEffectHandler::GetParamf(ALCcontext *context, const EqualizerProps &props, ALenum param, float *val) { switch(param) { - case AL_EQUALIZER_LOW_GAIN: - *val = props->Equalizer.LowGain; - break; - - case AL_EQUALIZER_LOW_CUTOFF: - *val = props->Equalizer.LowCutoff; - break; - - case AL_EQUALIZER_MID1_GAIN: - *val = props->Equalizer.Mid1Gain; - break; - - case AL_EQUALIZER_MID1_CENTER: - *val = props->Equalizer.Mid1Center; - break; - - case AL_EQUALIZER_MID1_WIDTH: - *val = props->Equalizer.Mid1Width; - break; - - case AL_EQUALIZER_MID2_GAIN: - *val = props->Equalizer.Mid2Gain; - break; - - case AL_EQUALIZER_MID2_CENTER: - *val = props->Equalizer.Mid2Center; - break; - - case AL_EQUALIZER_MID2_WIDTH: - *val = props->Equalizer.Mid2Width; - break; - - case AL_EQUALIZER_HIGH_GAIN: - *val = props->Equalizer.HighGain; - break; - - case AL_EQUALIZER_HIGH_CUTOFF: - *val = props->Equalizer.HighCutoff; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid equalizer float property 0x%04x", param}; + case AL_EQUALIZER_LOW_GAIN: *val = props.LowGain; return; + case AL_EQUALIZER_LOW_CUTOFF: *val = props.LowCutoff; return; + case AL_EQUALIZER_MID1_GAIN: *val = props.Mid1Gain; return; + case AL_EQUALIZER_MID1_CENTER: *val = props.Mid1Center; return; + case AL_EQUALIZER_MID1_WIDTH: *val = props.Mid1Width; return; + case AL_EQUALIZER_MID2_GAIN: *val = props.Mid2Gain; return; + case AL_EQUALIZER_MID2_CENTER: *val = props.Mid2Center; return; + case AL_EQUALIZER_MID2_WIDTH: *val = props.Mid2Width; return; + case AL_EQUALIZER_HIGH_GAIN: *val = props.HighGain; return; + case AL_EQUALIZER_HIGH_CUTOFF: *val = props.HighCutoff; return; } -} -void Equalizer_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Equalizer_getParamf(props, param, vals); } -EffectProps genDefaultProps() noexcept -{ - EffectProps props{}; - props.Equalizer.LowCutoff = AL_EQUALIZER_DEFAULT_LOW_CUTOFF; - props.Equalizer.LowGain = AL_EQUALIZER_DEFAULT_LOW_GAIN; - props.Equalizer.Mid1Center = AL_EQUALIZER_DEFAULT_MID1_CENTER; - props.Equalizer.Mid1Gain = AL_EQUALIZER_DEFAULT_MID1_GAIN; - props.Equalizer.Mid1Width = AL_EQUALIZER_DEFAULT_MID1_WIDTH; - props.Equalizer.Mid2Center = AL_EQUALIZER_DEFAULT_MID2_CENTER; - props.Equalizer.Mid2Gain = AL_EQUALIZER_DEFAULT_MID2_GAIN; - props.Equalizer.Mid2Width = AL_EQUALIZER_DEFAULT_MID2_WIDTH; - props.Equalizer.HighCutoff = AL_EQUALIZER_DEFAULT_HIGH_CUTOFF; - props.Equalizer.HighGain = AL_EQUALIZER_DEFAULT_HIGH_GAIN; - return props; + context->throw_error(AL_INVALID_ENUM, "Invalid equalizer float property {:#04x}", + as_unsigned(param)); } +void EqualizerEffectHandler::GetParamfv(ALCcontext *context, const EqualizerProps &props, ALenum param, float *vals) +{ GetParamf(context, props, param, vals); } -} // namespace - -DEFINE_ALEFFECT_VTABLE(Equalizer); -const EffectProps EqualizerEffectProps{genDefaultProps()}; - -#ifdef ALSOFT_EAX +#if ALSOFT_EAX namespace { using EqualizerCommitter = EaxCommitter; @@ -319,31 +284,31 @@ template<> throw Exception{message}; } -template<> -bool EqualizerCommitter::commit(const EaxEffectProps &props) +bool EaxEqualizerCommitter::commit(const EAXEQUALIZERPROPERTIES &props) { - if(props == mEaxProps) + if(auto *cur = std::get_if(&mEaxProps); cur && *cur == props) return false; mEaxProps = props; - - auto &eaxprops = std::get(props); - mAlProps.Equalizer.LowGain = level_mb_to_gain(static_cast(eaxprops.lLowGain)); - mAlProps.Equalizer.LowCutoff = eaxprops.flLowCutOff; - mAlProps.Equalizer.Mid1Gain = level_mb_to_gain(static_cast(eaxprops.lMid1Gain)); - mAlProps.Equalizer.Mid1Center = eaxprops.flMid1Center; - mAlProps.Equalizer.Mid1Width = eaxprops.flMid1Width; - mAlProps.Equalizer.Mid2Gain = level_mb_to_gain(static_cast(eaxprops.lMid2Gain)); - mAlProps.Equalizer.Mid2Center = eaxprops.flMid2Center; - mAlProps.Equalizer.Mid2Width = eaxprops.flMid2Width; - mAlProps.Equalizer.HighGain = level_mb_to_gain(static_cast(eaxprops.lHighGain)); - mAlProps.Equalizer.HighCutoff = eaxprops.flHighCutOff; + mAlProps = [&]{ + EqualizerProps ret{}; + ret.LowGain = level_mb_to_gain(static_cast(props.lLowGain)); + ret.LowCutoff = props.flLowCutOff; + ret.Mid1Gain = level_mb_to_gain(static_cast(props.lMid1Gain)); + ret.Mid1Center = props.flMid1Center; + ret.Mid1Width = props.flMid1Width; + ret.Mid2Gain = level_mb_to_gain(static_cast(props.lMid2Gain)); + ret.Mid2Center = props.flMid2Center; + ret.Mid2Width = props.flMid2Width; + ret.HighGain = level_mb_to_gain(static_cast(props.lHighGain)); + ret.HighCutoff = props.flHighCutOff; + return ret; + }(); return true; } -template<> -void EqualizerCommitter::SetDefaults(EaxEffectProps &props) +void EaxEqualizerCommitter::SetDefaults(EaxEffectProps &props) { static constexpr EAXEQUALIZERPROPERTIES defprops{[] { @@ -363,10 +328,8 @@ void EqualizerCommitter::SetDefaults(EaxEffectProps &props) props = defprops; } -template<> -void EqualizerCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) +void EaxEqualizerCommitter::Get(const EaxCall &call, const EAXEQUALIZERPROPERTIES &props) { - auto &props = std::get(props_); switch(call.get_property_id()) { case EAXEQUALIZER_NONE: break; @@ -385,10 +348,8 @@ void EqualizerCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) } } -template<> -void EqualizerCommitter::Set(const EaxCall &call, EaxEffectProps &props_) +void EaxEqualizerCommitter::Set(const EaxCall &call, EAXEQUALIZERPROPERTIES &props) { - auto &props = std::get(props_); switch(call.get_property_id()) { case EAXEQUALIZER_NONE: break; diff --git a/3rdparty/openal/al/effects/fshifter.cpp b/3rdparty/openal/al/effects/fshifter.cpp index 37c372c35f78..d1c93921ffec 100644 --- a/3rdparty/openal/al/effects/fshifter.cpp +++ b/3rdparty/openal/al/effects/fshifter.cpp @@ -7,12 +7,14 @@ #include "AL/al.h" #include "AL/efx.h" -#include "alc/effects/base.h" +#include "alc/context.h" +#include "alnumeric.h" #include "effects.h" -#ifdef ALSOFT_EAX +#if ALSOFT_EAX #include -#include "alnumeric.h" + +#include "al/eax/effect.h" #include "al/eax/exception.h" #include "al/eax/utils.h" #endif // ALSOFT_EAX @@ -20,7 +22,7 @@ namespace { -std::optional DirectionFromEmum(ALenum value) +constexpr std::optional DirectionFromEmum(ALenum value) noexcept { switch(value) { @@ -30,7 +32,7 @@ std::optional DirectionFromEmum(ALenum value) } return std::nullopt; } -ALenum EnumFromDirection(FShifterDirection dir) +constexpr ALenum EnumFromDirection(FShifterDirection dir) { switch(dir) { @@ -38,105 +40,99 @@ ALenum EnumFromDirection(FShifterDirection dir) case FShifterDirection::Up: return AL_FREQUENCY_SHIFTER_DIRECTION_UP; case FShifterDirection::Off: return AL_FREQUENCY_SHIFTER_DIRECTION_OFF; } - throw std::runtime_error{"Invalid direction: "+std::to_string(static_cast(dir))}; + throw std::runtime_error{fmt::format("Invalid direction: {}", int{al::to_underlying(dir)})}; } -void Fshifter_setParamf(EffectProps *props, ALenum param, float val) +constexpr EffectProps genDefaultProps() noexcept { - switch(param) - { - case AL_FREQUENCY_SHIFTER_FREQUENCY: - if(!(val >= AL_FREQUENCY_SHIFTER_MIN_FREQUENCY && val <= AL_FREQUENCY_SHIFTER_MAX_FREQUENCY)) - throw effect_exception{AL_INVALID_VALUE, "Frequency shifter frequency out of range"}; - props->Fshifter.Frequency = val; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x", - param}; - } + FshifterProps props{}; + props.Frequency = AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY; + props.LeftDirection = DirectionFromEmum(AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION).value(); + props.RightDirection = DirectionFromEmum(AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION).value(); + return props; } -void Fshifter_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Fshifter_setParamf(props, param, vals[0]); } -void Fshifter_setParami(EffectProps *props, ALenum param, int val) +} // namespace + +const EffectProps FshifterEffectProps{genDefaultProps()}; + +void FshifterEffectHandler::SetParami(ALCcontext *context, FshifterProps &props, ALenum param, int val) { switch(param) { case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION: if(auto diropt = DirectionFromEmum(val)) - props->Fshifter.LeftDirection = *diropt; + props.LeftDirection = *diropt; else - throw effect_exception{AL_INVALID_VALUE, - "Unsupported frequency shifter left direction: 0x%04x", val}; - break; + context->throw_error(AL_INVALID_VALUE, + "Unsupported frequency shifter left direction: {:#04x}", as_unsigned(val)); + return; case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION: if(auto diropt = DirectionFromEmum(val)) - props->Fshifter.RightDirection = *diropt; + props.RightDirection = *diropt; else - throw effect_exception{AL_INVALID_VALUE, - "Unsupported frequency shifter right direction: 0x%04x", val}; - break; + context->throw_error(AL_INVALID_VALUE, + "Unsupported frequency shifter right direction: {:#04x}", as_unsigned(val)); + return; + } - default: - throw effect_exception{AL_INVALID_ENUM, - "Invalid frequency shifter integer property 0x%04x", param}; + context->throw_error(AL_INVALID_ENUM, "Invalid frequency shifter integer property {:#04x}", + as_unsigned(param)); +} +void FshifterEffectHandler::SetParamiv(ALCcontext *context, FshifterProps &props, ALenum param, const int *vals) +{ SetParami(context, props, param, *vals); } + +void FshifterEffectHandler::SetParamf(ALCcontext *context, FshifterProps &props, ALenum param, float val) +{ + switch(param) + { + case AL_FREQUENCY_SHIFTER_FREQUENCY: + if(!(val >= AL_FREQUENCY_SHIFTER_MIN_FREQUENCY && val <= AL_FREQUENCY_SHIFTER_MAX_FREQUENCY)) + context->throw_error(AL_INVALID_VALUE, "Frequency shifter frequency out of range"); + props.Frequency = val; + return; } + + context->throw_error(AL_INVALID_ENUM, "Invalid frequency shifter float property {:#04x}", + as_unsigned(param)); } -void Fshifter_setParamiv(EffectProps *props, ALenum param, const int *vals) -{ Fshifter_setParami(props, param, vals[0]); } +void FshifterEffectHandler::SetParamfv(ALCcontext *context, FshifterProps &props, ALenum param, const float *vals) +{ SetParamf(context, props, param, *vals); } -void Fshifter_getParami(const EffectProps *props, ALenum param, int *val) +void FshifterEffectHandler::GetParami(ALCcontext *context, const FshifterProps &props, ALenum param, int *val) { switch(param) { case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION: - *val = EnumFromDirection(props->Fshifter.LeftDirection); - break; + *val = EnumFromDirection(props.LeftDirection); + return; case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION: - *val = EnumFromDirection(props->Fshifter.RightDirection); - break; - default: - throw effect_exception{AL_INVALID_ENUM, - "Invalid frequency shifter integer property 0x%04x", param}; + *val = EnumFromDirection(props.RightDirection); + return; } + + context->throw_error(AL_INVALID_ENUM, "Invalid frequency shifter integer property {:#04x}", + as_unsigned(param)); } -void Fshifter_getParamiv(const EffectProps *props, ALenum param, int *vals) -{ Fshifter_getParami(props, param, vals); } +void FshifterEffectHandler::GetParamiv(ALCcontext *context, const FshifterProps &props, ALenum param, int *vals) +{ GetParami(context, props, param, vals); } -void Fshifter_getParamf(const EffectProps *props, ALenum param, float *val) +void FshifterEffectHandler::GetParamf(ALCcontext *context, const FshifterProps &props, ALenum param, float *val) { switch(param) { - case AL_FREQUENCY_SHIFTER_FREQUENCY: - *val = props->Fshifter.Frequency; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x", - param}; + case AL_FREQUENCY_SHIFTER_FREQUENCY: *val = props.Frequency; return; } -} -void Fshifter_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Fshifter_getParamf(props, param, vals); } -EffectProps genDefaultProps() noexcept -{ - EffectProps props{}; - props.Fshifter.Frequency = AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY; - props.Fshifter.LeftDirection = *DirectionFromEmum(AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION); - props.Fshifter.RightDirection = *DirectionFromEmum(AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION); - return props; + context->throw_error(AL_INVALID_ENUM, "Invalid frequency shifter float property {:#04x}", + as_unsigned(param)); } +void FshifterEffectHandler::GetParamfv(ALCcontext *context, const FshifterProps &props, ALenum param, float *vals) +{ GetParamf(context, props, param, vals); } -} // namespace - -DEFINE_ALEFFECT_VTABLE(Fshifter); -const EffectProps FshifterEffectProps{genDefaultProps()}; - -#ifdef ALSOFT_EAX +#if ALSOFT_EAX namespace { using FrequencyShifterCommitter = EaxCommitter; @@ -197,10 +193,9 @@ template<> throw Exception{message}; } -template<> -bool FrequencyShifterCommitter::commit(const EaxEffectProps &props) +bool EaxFrequencyShifterCommitter::commit(const EAXFREQUENCYSHIFTERPROPERTIES &props) { - if(props == mEaxProps) + if(auto *cur = std::get_if(&mEaxProps); cur && *cur == props) return false; mEaxProps = props; @@ -214,16 +209,18 @@ bool FrequencyShifterCommitter::commit(const EaxEffectProps &props) return FShifterDirection::Off; }; - auto &eaxprops = std::get(props); - mAlProps.Fshifter.Frequency = eaxprops.flFrequency; - mAlProps.Fshifter.LeftDirection = get_direction(eaxprops.ulLeftDirection); - mAlProps.Fshifter.RightDirection = get_direction(eaxprops.ulRightDirection); + mAlProps = [&]{ + FshifterProps ret{}; + ret.Frequency = props.flFrequency; + ret.LeftDirection = get_direction(props.ulLeftDirection); + ret.RightDirection = get_direction(props.ulRightDirection); + return ret; + }(); return true; } -template<> -void FrequencyShifterCommitter::SetDefaults(EaxEffectProps &props) +void EaxFrequencyShifterCommitter::SetDefaults(EaxEffectProps &props) { static constexpr EAXFREQUENCYSHIFTERPROPERTIES defprops{[] { @@ -236,10 +233,8 @@ void FrequencyShifterCommitter::SetDefaults(EaxEffectProps &props) props = defprops; } -template<> -void FrequencyShifterCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) +void EaxFrequencyShifterCommitter::Get(const EaxCall &call, const EAXFREQUENCYSHIFTERPROPERTIES &props) { - auto &props = std::get(props_); switch(call.get_property_id()) { case EAXFREQUENCYSHIFTER_NONE: break; @@ -251,10 +246,8 @@ void FrequencyShifterCommitter::Get(const EaxCall &call, const EaxEffectProps &p } } -template<> -void FrequencyShifterCommitter::Set(const EaxCall &call, EaxEffectProps &props_) +void EaxFrequencyShifterCommitter::Set(const EaxCall &call, EAXFREQUENCYSHIFTERPROPERTIES &props) { - auto &props = std::get(props_); switch(call.get_property_id()) { case EAXFREQUENCYSHIFTER_NONE: break; diff --git a/3rdparty/openal/al/effects/modulator.cpp b/3rdparty/openal/al/effects/modulator.cpp index 366e7ef71154..b3ca22138f9a 100644 --- a/3rdparty/openal/al/effects/modulator.cpp +++ b/3rdparty/openal/al/effects/modulator.cpp @@ -7,12 +7,14 @@ #include "AL/al.h" #include "AL/efx.h" -#include "alc/effects/base.h" +#include "alc/context.h" +#include "alnumeric.h" #include "effects.h" -#ifdef ALSOFT_EAX +#if ALSOFT_EAX #include -#include "alnumeric.h" + +#include "al/eax/effect.h" #include "al/eax/exception.h" #include "al/eax/utils.h" #endif // ALSOFT_EAX @@ -20,7 +22,7 @@ namespace { -std::optional WaveformFromEmum(ALenum value) +constexpr std::optional WaveformFromEmum(ALenum value) noexcept { switch(value) { @@ -30,7 +32,7 @@ std::optional WaveformFromEmum(ALenum value) } return std::nullopt; } -ALenum EnumFromWaveform(ModulatorWaveform type) +constexpr ALenum EnumFromWaveform(ModulatorWaveform type) { switch(type) { @@ -38,111 +40,101 @@ ALenum EnumFromWaveform(ModulatorWaveform type) case ModulatorWaveform::Sawtooth: return AL_RING_MODULATOR_SAWTOOTH; case ModulatorWaveform::Square: return AL_RING_MODULATOR_SQUARE; } - throw std::runtime_error{"Invalid modulator waveform: " + - std::to_string(static_cast(type))}; + throw std::runtime_error{fmt::format("Invalid modulator waveform: {}", + int{al::to_underlying(type)})}; } -void Modulator_setParamf(EffectProps *props, ALenum param, float val) +constexpr EffectProps genDefaultProps() noexcept { - switch(param) - { - case AL_RING_MODULATOR_FREQUENCY: - if(!(val >= AL_RING_MODULATOR_MIN_FREQUENCY && val <= AL_RING_MODULATOR_MAX_FREQUENCY)) - throw effect_exception{AL_INVALID_VALUE, "Modulator frequency out of range: %f", val}; - props->Modulator.Frequency = val; - break; + ModulatorProps props{}; + props.Frequency = AL_RING_MODULATOR_DEFAULT_FREQUENCY; + props.HighPassCutoff = AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF; + props.Waveform = WaveformFromEmum(AL_RING_MODULATOR_DEFAULT_WAVEFORM).value(); + return props; +} - case AL_RING_MODULATOR_HIGHPASS_CUTOFF: - if(!(val >= AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF && val <= AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF)) - throw effect_exception{AL_INVALID_VALUE, "Modulator high-pass cutoff out of range: %f", val}; - props->Modulator.HighPassCutoff = val; - break; +} // namespace - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param}; - } -} -void Modulator_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Modulator_setParamf(props, param, vals[0]); } -void Modulator_setParami(EffectProps *props, ALenum param, int val) +const EffectProps ModulatorEffectProps{genDefaultProps()}; + +void ModulatorEffectHandler::SetParami(ALCcontext *context, ModulatorProps &props, ALenum param, int val) { switch(param) { case AL_RING_MODULATOR_FREQUENCY: case AL_RING_MODULATOR_HIGHPASS_CUTOFF: - Modulator_setParamf(props, param, static_cast(val)); - break; + SetParamf(context, props, param, static_cast(val)); + return; case AL_RING_MODULATOR_WAVEFORM: if(auto formopt = WaveformFromEmum(val)) - props->Modulator.Waveform = *formopt; + props.Waveform = *formopt; else - throw effect_exception{AL_INVALID_VALUE, "Invalid modulator waveform: 0x%04x", val}; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x", - param}; + context->throw_error(AL_INVALID_VALUE, "Invalid modulator waveform: {:#04x}", + as_unsigned(val)); + return; } + + context->throw_error(AL_INVALID_ENUM, "Invalid modulator integer property {:#04x}", + as_unsigned(param)); } -void Modulator_setParamiv(EffectProps *props, ALenum param, const int *vals) -{ Modulator_setParami(props, param, vals[0]); } +void ModulatorEffectHandler::SetParamiv(ALCcontext *context, ModulatorProps &props, ALenum param, const int *vals) +{ SetParami(context, props, param, *vals); } -void Modulator_getParami(const EffectProps *props, ALenum param, int *val) +void ModulatorEffectHandler::SetParamf(ALCcontext *context, ModulatorProps &props, ALenum param, float val) { switch(param) { case AL_RING_MODULATOR_FREQUENCY: - *val = static_cast(props->Modulator.Frequency); - break; - case AL_RING_MODULATOR_HIGHPASS_CUTOFF: - *val = static_cast(props->Modulator.HighPassCutoff); - break; - case AL_RING_MODULATOR_WAVEFORM: - *val = EnumFromWaveform(props->Modulator.Waveform); - break; + if(!(val >= AL_RING_MODULATOR_MIN_FREQUENCY && val <= AL_RING_MODULATOR_MAX_FREQUENCY)) + context->throw_error(AL_INVALID_VALUE, "Modulator frequency out of range: {:f}", val); + props.Frequency = val; + return; - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x", - param}; + case AL_RING_MODULATOR_HIGHPASS_CUTOFF: + if(!(val >= AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF && val <= AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF)) + context->throw_error(AL_INVALID_VALUE, "Modulator high-pass cutoff out of range: {:f}", + val); + props.HighPassCutoff = val; + return; } + + context->throw_error(AL_INVALID_ENUM, "Invalid modulator float property {:#04x}", + as_unsigned(param)); } -void Modulator_getParamiv(const EffectProps *props, ALenum param, int *vals) -{ Modulator_getParami(props, param, vals); } -void Modulator_getParamf(const EffectProps *props, ALenum param, float *val) +void ModulatorEffectHandler::SetParamfv(ALCcontext *context, ModulatorProps &props, ALenum param, const float *vals) +{ SetParamf(context, props, param, *vals); } + +void ModulatorEffectHandler::GetParami(ALCcontext *context, const ModulatorProps &props, ALenum param, int *val) { switch(param) { - case AL_RING_MODULATOR_FREQUENCY: - *val = props->Modulator.Frequency; - break; - case AL_RING_MODULATOR_HIGHPASS_CUTOFF: - *val = props->Modulator.HighPassCutoff; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param}; + case AL_RING_MODULATOR_FREQUENCY: *val = static_cast(props.Frequency); return; + case AL_RING_MODULATOR_HIGHPASS_CUTOFF: *val = static_cast(props.HighPassCutoff); return; + case AL_RING_MODULATOR_WAVEFORM: *val = EnumFromWaveform(props.Waveform); return; } -} -void Modulator_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Modulator_getParamf(props, param, vals); } -EffectProps genDefaultProps() noexcept -{ - EffectProps props{}; - props.Modulator.Frequency = AL_RING_MODULATOR_DEFAULT_FREQUENCY; - props.Modulator.HighPassCutoff = AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF; - props.Modulator.Waveform = *WaveformFromEmum(AL_RING_MODULATOR_DEFAULT_WAVEFORM); - return props; + context->throw_error(AL_INVALID_ENUM, "Invalid modulator integer property {:#04x}", + as_unsigned(param)); } +void ModulatorEffectHandler::GetParamiv(ALCcontext *context, const ModulatorProps &props, ALenum param, int *vals) +{ GetParami(context, props, param, vals); } +void ModulatorEffectHandler::GetParamf(ALCcontext *context, const ModulatorProps &props, ALenum param, float *val) +{ + switch(param) + { + case AL_RING_MODULATOR_FREQUENCY: *val = props.Frequency; return; + case AL_RING_MODULATOR_HIGHPASS_CUTOFF: *val = props.HighPassCutoff; return; + } -} // namespace - -DEFINE_ALEFFECT_VTABLE(Modulator); + context->throw_error(AL_INVALID_ENUM, "Invalid modulator float property {:#04x}", + as_unsigned(param)); +} +void ModulatorEffectHandler::GetParamfv(ALCcontext *context, const ModulatorProps &props, ALenum param, float *vals) +{ GetParamf(context, props, param, vals); } -const EffectProps ModulatorEffectProps{genDefaultProps()}; -#ifdef ALSOFT_EAX +#if ALSOFT_EAX namespace { using ModulatorCommitter = EaxCommitter; @@ -203,10 +195,9 @@ template<> throw Exception{message}; } -template<> -bool ModulatorCommitter::commit(const EaxEffectProps &props) +bool EaxModulatorCommitter::commit(const EAXRINGMODULATORPROPERTIES &props) { - if(props == mEaxProps) + if(auto *cur = std::get_if(&mEaxProps); cur && *cur == props) return false; mEaxProps = props; @@ -222,16 +213,18 @@ bool ModulatorCommitter::commit(const EaxEffectProps &props) return ModulatorWaveform::Sinusoid; }; - auto &eaxprops = std::get(props); - mAlProps.Modulator.Frequency = eaxprops.flFrequency; - mAlProps.Modulator.HighPassCutoff = eaxprops.flHighPassCutOff; - mAlProps.Modulator.Waveform = get_waveform(eaxprops.ulWaveform); + mAlProps = [&]{ + ModulatorProps ret{}; + ret.Frequency = props.flFrequency; + ret.HighPassCutoff = props.flHighPassCutOff; + ret.Waveform = get_waveform(props.ulWaveform); + return ret; + }(); return true; } -template<> -void ModulatorCommitter::SetDefaults(EaxEffectProps &props) +void EaxModulatorCommitter::SetDefaults(EaxEffectProps &props) { static constexpr EAXRINGMODULATORPROPERTIES defprops{[] { @@ -244,10 +237,8 @@ void ModulatorCommitter::SetDefaults(EaxEffectProps &props) props = defprops; } -template<> -void ModulatorCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) +void EaxModulatorCommitter::Get(const EaxCall &call, const EAXRINGMODULATORPROPERTIES &props) { - auto &props = std::get(props_); switch(call.get_property_id()) { case EAXRINGMODULATOR_NONE: break; @@ -259,11 +250,9 @@ void ModulatorCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) } } -template<> -void ModulatorCommitter::Set(const EaxCall &call, EaxEffectProps &props_) +void EaxModulatorCommitter::Set(const EaxCall &call, EAXRINGMODULATORPROPERTIES &props) { - auto &props = std::get(props_); - switch (call.get_property_id()) + switch(call.get_property_id()) { case EAXRINGMODULATOR_NONE: break; case EAXRINGMODULATOR_ALLPARAMETERS: defer(call, props); break; diff --git a/3rdparty/openal/al/effects/null.cpp b/3rdparty/openal/al/effects/null.cpp index 1e8787e79381..ac522313f1c2 100644 --- a/3rdparty/openal/al/effects/null.cpp +++ b/3rdparty/openal/al/effects/null.cpp @@ -4,100 +4,67 @@ #include "AL/al.h" #include "AL/efx.h" -#include "alc/effects/base.h" +#include "alc/context.h" +#include "alnumeric.h" #include "effects.h" -#ifdef ALSOFT_EAX +#if ALSOFT_EAX +#include "al/eax/effect.h" #include "al/eax/exception.h" #endif // ALSOFT_EAX namespace { -void Null_setParami(EffectProps* /*props*/, ALenum param, int /*val*/) +constexpr EffectProps genDefaultProps() noexcept { - switch(param) - { - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x", - param}; - } + return std::monostate{}; } -void Null_setParamiv(EffectProps *props, ALenum param, const int *vals) + +} // namespace + +const EffectProps NullEffectProps{genDefaultProps()}; + +void NullEffectHandler::SetParami(ALCcontext *context, std::monostate& /*props*/, ALenum param, int /*val*/) { - switch(param) - { - default: - Null_setParami(props, param, vals[0]); - } + context->throw_error(AL_INVALID_ENUM, "Invalid null effect integer property {:#04x}", + as_unsigned(param)); } -void Null_setParamf(EffectProps* /*props*/, ALenum param, float /*val*/) +void NullEffectHandler::SetParamiv(ALCcontext *context, std::monostate &props, ALenum param, const int *vals) { - switch(param) - { - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x", - param}; - } + SetParami(context, props, param, *vals); } -void Null_setParamfv(EffectProps *props, ALenum param, const float *vals) +void NullEffectHandler::SetParamf(ALCcontext *context, std::monostate& /*props*/, ALenum param, float /*val*/) { - switch(param) - { - default: - Null_setParamf(props, param, vals[0]); - } + context->throw_error(AL_INVALID_ENUM, "Invalid null effect float property {:#04x}", + as_unsigned(param)); } - -void Null_getParami(const EffectProps* /*props*/, ALenum param, int* /*val*/) +void NullEffectHandler::SetParamfv(ALCcontext *context, std::monostate &props, ALenum param, const float *vals) { - switch(param) - { - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid null effect integer property 0x%04x", - param}; - } + SetParamf(context, props, param, *vals); } -void Null_getParamiv(const EffectProps *props, ALenum param, int *vals) + +void NullEffectHandler::GetParami(ALCcontext *context, const std::monostate& /*props*/, ALenum param, int* /*val*/) { - switch(param) - { - default: - Null_getParami(props, param, vals); - } + context->throw_error(AL_INVALID_ENUM, "Invalid null effect integer property {:#04x}", + as_unsigned(param)); } -void Null_getParamf(const EffectProps* /*props*/, ALenum param, float* /*val*/) +void NullEffectHandler::GetParamiv(ALCcontext *context, const std::monostate &props, ALenum param, int *vals) { - switch(param) - { - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid null effect float property 0x%04x", - param}; - } + GetParami(context, props, param, vals); } -void Null_getParamfv(const EffectProps *props, ALenum param, float *vals) +void NullEffectHandler::GetParamf(ALCcontext *context, const std::monostate& /*props*/, ALenum param, float* /*val*/) { - switch(param) - { - default: - Null_getParamf(props, param, vals); - } + context->throw_error(AL_INVALID_ENUM, "Invalid null effect float property {:#04x}", + as_unsigned(param)); } - -EffectProps genDefaultProps() noexcept +void NullEffectHandler::GetParamfv(ALCcontext *context, const std::monostate &props, ALenum param, float *vals) { - EffectProps props{}; - return props; + GetParamf(context, props, param, vals); } -} // namespace -DEFINE_ALEFFECT_VTABLE(Null); - -const EffectProps NullEffectProps{genDefaultProps()}; - - -#ifdef ALSOFT_EAX +#if ALSOFT_EAX namespace { using NullCommitter = EaxCommitter; @@ -117,29 +84,26 @@ template<> throw Exception{message}; } -template<> -bool NullCommitter::commit(const EaxEffectProps &props) +bool EaxNullCommitter::commit(const std::monostate &props) { - const bool ret{props != mEaxProps}; + const bool ret{std::holds_alternative(mEaxProps)}; mEaxProps = props; + mAlProps = std::monostate{}; return ret; } -template<> -void NullCommitter::SetDefaults(EaxEffectProps &props) +void EaxNullCommitter::SetDefaults(EaxEffectProps &props) { - props.emplace(); + props = std::monostate{}; } -template<> -void NullCommitter::Get(const EaxCall &call, const EaxEffectProps&) +void EaxNullCommitter::Get(const EaxCall &call, const std::monostate&) { if(call.get_property_id() != 0) fail_unknown_property_id(); } -template<> -void NullCommitter::Set(const EaxCall &call, EaxEffectProps&) +void EaxNullCommitter::Set(const EaxCall &call, std::monostate&) { if(call.get_property_id() != 0) fail_unknown_property_id(); diff --git a/3rdparty/openal/al/effects/pshifter.cpp b/3rdparty/openal/al/effects/pshifter.cpp index 10824016b7d3..c07ef27532ff 100644 --- a/3rdparty/openal/al/effects/pshifter.cpp +++ b/3rdparty/openal/al/effects/pshifter.cpp @@ -4,11 +4,12 @@ #include "AL/al.h" #include "AL/efx.h" -#include "alc/effects/base.h" +#include "alc/context.h" +#include "alnumeric.h" #include "effects.h" -#ifdef ALSOFT_EAX -#include "alnumeric.h" +#if ALSOFT_EAX +#include "al/eax/effect.h" #include "al/eax/exception.h" #include "al/eax/utils.h" #endif // ALSOFT_EAX @@ -16,80 +17,67 @@ namespace { -void Pshifter_setParamf(EffectProps*, ALenum param, float) -{ throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param}; } -void Pshifter_setParamfv(EffectProps*, ALenum param, const float*) +constexpr EffectProps genDefaultProps() noexcept { - throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float-vector property 0x%04x", - param}; + PshifterProps props{}; + props.CoarseTune = AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE; + props.FineTune = AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE; + return props; } -void Pshifter_setParami(EffectProps *props, ALenum param, int val) +} // namespace + +const EffectProps PshifterEffectProps{genDefaultProps()}; + +void PshifterEffectHandler::SetParami(ALCcontext *context, PshifterProps &props, ALenum param, int val) { switch(param) { case AL_PITCH_SHIFTER_COARSE_TUNE: if(!(val >= AL_PITCH_SHIFTER_MIN_COARSE_TUNE && val <= AL_PITCH_SHIFTER_MAX_COARSE_TUNE)) - throw effect_exception{AL_INVALID_VALUE, "Pitch shifter coarse tune out of range"}; - props->Pshifter.CoarseTune = val; - break; + context->throw_error(AL_INVALID_VALUE, "Pitch shifter coarse tune out of range"); + props.CoarseTune = val; + return; case AL_PITCH_SHIFTER_FINE_TUNE: if(!(val >= AL_PITCH_SHIFTER_MIN_FINE_TUNE && val <= AL_PITCH_SHIFTER_MAX_FINE_TUNE)) - throw effect_exception{AL_INVALID_VALUE, "Pitch shifter fine tune out of range"}; - props->Pshifter.FineTune = val; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter integer property 0x%04x", - param}; + context->throw_error(AL_INVALID_VALUE, "Pitch shifter fine tune out of range"); + props.FineTune = val; + return; } + + context->throw_error(AL_INVALID_ENUM, "Invalid pitch shifter integer property {:#04x}", + as_unsigned(param)); } -void Pshifter_setParamiv(EffectProps *props, ALenum param, const int *vals) -{ Pshifter_setParami(props, param, vals[0]); } +void PshifterEffectHandler::SetParamiv(ALCcontext *context, PshifterProps &props, ALenum param, const int *vals) +{ SetParami(context, props, param, *vals); } -void Pshifter_getParami(const EffectProps *props, ALenum param, int *val) +void PshifterEffectHandler::SetParamf(ALCcontext *context, PshifterProps&, ALenum param, float) +{ context->throw_error(AL_INVALID_ENUM, "Invalid pitch shifter float property {:#04x}", as_unsigned(param)); } +void PshifterEffectHandler::SetParamfv(ALCcontext *context, PshifterProps &props, ALenum param, const float *vals) +{ SetParamf(context, props, param, *vals); } + +void PshifterEffectHandler::GetParami(ALCcontext *context, const PshifterProps &props, ALenum param, int *val) { switch(param) { - case AL_PITCH_SHIFTER_COARSE_TUNE: - *val = props->Pshifter.CoarseTune; - break; - case AL_PITCH_SHIFTER_FINE_TUNE: - *val = props->Pshifter.FineTune; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter integer property 0x%04x", - param}; + case AL_PITCH_SHIFTER_COARSE_TUNE: *val = props.CoarseTune; return; + case AL_PITCH_SHIFTER_FINE_TUNE: *val = props.FineTune; return; } -} -void Pshifter_getParamiv(const EffectProps *props, ALenum param, int *vals) -{ Pshifter_getParami(props, param, vals); } -void Pshifter_getParamf(const EffectProps*, ALenum param, float*) -{ throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float property 0x%04x", param}; } -void Pshifter_getParamfv(const EffectProps*, ALenum param, float*) -{ - throw effect_exception{AL_INVALID_ENUM, "Invalid pitch shifter float vector-property 0x%04x", - param}; + context->throw_error(AL_INVALID_ENUM, "Invalid pitch shifter integer property {:#04x}", + as_unsigned(param)); } +void PshifterEffectHandler::GetParamiv(ALCcontext *context, const PshifterProps &props, ALenum param, int *vals) +{ GetParami(context, props, param, vals); } -EffectProps genDefaultProps() noexcept -{ - EffectProps props{}; - props.Pshifter.CoarseTune = AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE; - props.Pshifter.FineTune = AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE; - return props; -} +void PshifterEffectHandler::GetParamf(ALCcontext *context, const PshifterProps&, ALenum param, float*) +{ context->throw_error(AL_INVALID_ENUM, "Invalid pitch shifter float property {:#04x}", as_unsigned(param)); } +void PshifterEffectHandler::GetParamfv(ALCcontext *context, const PshifterProps &props, ALenum param, float *vals) +{ GetParamf(context, props, param, vals); } -} // namespace - -DEFINE_ALEFFECT_VTABLE(Pshifter); -const EffectProps PshifterEffectProps{genDefaultProps()}; - -#ifdef ALSOFT_EAX +#if ALSOFT_EAX namespace { using PitchShifterCommitter = EaxCommitter; @@ -138,32 +126,30 @@ template<> throw Exception{message}; } -template<> -bool PitchShifterCommitter::commit(const EaxEffectProps &props) +bool EaxPitchShifterCommitter::commit(const EAXPITCHSHIFTERPROPERTIES &props) { - if(props == mEaxProps) + if(auto *cur = std::get_if(&mEaxProps); cur && *cur == props) return false; mEaxProps = props; - - auto &eaxprops = std::get(props); - mAlProps.Pshifter.CoarseTune = static_cast(eaxprops.lCoarseTune); - mAlProps.Pshifter.FineTune = static_cast(eaxprops.lFineTune); + mAlProps = [&]{ + PshifterProps ret{}; + ret.CoarseTune = static_cast(props.lCoarseTune); + ret.FineTune = static_cast(props.lFineTune); + return ret; + }(); return true; } -template<> -void PitchShifterCommitter::SetDefaults(EaxEffectProps &props) +void EaxPitchShifterCommitter::SetDefaults(EaxEffectProps &props) { props = EAXPITCHSHIFTERPROPERTIES{EAXPITCHSHIFTER_DEFAULTCOARSETUNE, EAXPITCHSHIFTER_DEFAULTFINETUNE}; } -template<> -void PitchShifterCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) +void EaxPitchShifterCommitter::Get(const EaxCall &call, const EAXPITCHSHIFTERPROPERTIES &props) { - auto &props = std::get(props_); switch(call.get_property_id()) { case EAXPITCHSHIFTER_NONE: break; @@ -174,10 +160,8 @@ void PitchShifterCommitter::Get(const EaxCall &call, const EaxEffectProps &props } } -template<> -void PitchShifterCommitter::Set(const EaxCall &call, EaxEffectProps &props_) +void EaxPitchShifterCommitter::Set(const EaxCall &call, EAXPITCHSHIFTERPROPERTIES &props) { - auto &props = std::get(props_); switch(call.get_property_id()) { case EAXPITCHSHIFTER_NONE: break; diff --git a/3rdparty/openal/al/effects/reverb.cpp b/3rdparty/openal/al/effects/reverb.cpp index b037443fafdf..c51858f29283 100644 --- a/3rdparty/openal/al/effects/reverb.cpp +++ b/3rdparty/openal/al/effects/reverb.cpp @@ -1,18 +1,24 @@ #include "config.h" +#include +#include #include +#include #include "AL/al.h" #include "AL/efx.h" -#include "alc/effects/base.h" +#include "alc/context.h" +#include "alnumeric.h" +#include "alspan.h" #include "effects.h" -#ifdef ALSOFT_EAX +#if ALSOFT_EAX #include -#include "alnumeric.h" -#include "AL/efx-presets.h" +#include "al/eax/api.h" +#include "al/eax/call.h" +#include "al/eax/effect.h" #include "al/eax/exception.h" #include "al/eax/utils.h" #endif // ALSOFT_EAX @@ -20,550 +26,433 @@ namespace { -void Reverb_setParami(EffectProps *props, ALenum param, int val) +constexpr EffectProps genDefaultProps() noexcept +{ + ReverbProps props{}; + props.Density = AL_EAXREVERB_DEFAULT_DENSITY; + props.Diffusion = AL_EAXREVERB_DEFAULT_DIFFUSION; + props.Gain = AL_EAXREVERB_DEFAULT_GAIN; + props.GainHF = AL_EAXREVERB_DEFAULT_GAINHF; + props.GainLF = AL_EAXREVERB_DEFAULT_GAINLF; + props.DecayTime = AL_EAXREVERB_DEFAULT_DECAY_TIME; + props.DecayHFRatio = AL_EAXREVERB_DEFAULT_DECAY_HFRATIO; + props.DecayLFRatio = AL_EAXREVERB_DEFAULT_DECAY_LFRATIO; + props.ReflectionsGain = AL_EAXREVERB_DEFAULT_REFLECTIONS_GAIN; + props.ReflectionsDelay = AL_EAXREVERB_DEFAULT_REFLECTIONS_DELAY; + props.ReflectionsPan[0] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ; + props.ReflectionsPan[1] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ; + props.ReflectionsPan[2] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ; + props.LateReverbGain = AL_EAXREVERB_DEFAULT_LATE_REVERB_GAIN; + props.LateReverbDelay = AL_EAXREVERB_DEFAULT_LATE_REVERB_DELAY; + props.LateReverbPan[0] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ; + props.LateReverbPan[1] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ; + props.LateReverbPan[2] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ; + props.EchoTime = AL_EAXREVERB_DEFAULT_ECHO_TIME; + props.EchoDepth = AL_EAXREVERB_DEFAULT_ECHO_DEPTH; + props.ModulationTime = AL_EAXREVERB_DEFAULT_MODULATION_TIME; + props.ModulationDepth = AL_EAXREVERB_DEFAULT_MODULATION_DEPTH; + props.AirAbsorptionGainHF = AL_EAXREVERB_DEFAULT_AIR_ABSORPTION_GAINHF; + props.HFReference = AL_EAXREVERB_DEFAULT_HFREFERENCE; + props.LFReference = AL_EAXREVERB_DEFAULT_LFREFERENCE; + props.RoomRolloffFactor = AL_EAXREVERB_DEFAULT_ROOM_ROLLOFF_FACTOR; + props.DecayHFLimit = AL_EAXREVERB_DEFAULT_DECAY_HFLIMIT; + return props; +} + +constexpr EffectProps genDefaultStdProps() noexcept +{ + ReverbProps props{}; + props.Density = AL_REVERB_DEFAULT_DENSITY; + props.Diffusion = AL_REVERB_DEFAULT_DIFFUSION; + props.Gain = AL_REVERB_DEFAULT_GAIN; + props.GainHF = AL_REVERB_DEFAULT_GAINHF; + props.GainLF = 1.0f; + props.DecayTime = AL_REVERB_DEFAULT_DECAY_TIME; + props.DecayHFRatio = AL_REVERB_DEFAULT_DECAY_HFRATIO; + props.DecayLFRatio = 1.0f; + props.ReflectionsGain = AL_REVERB_DEFAULT_REFLECTIONS_GAIN; + props.ReflectionsDelay = AL_REVERB_DEFAULT_REFLECTIONS_DELAY; + props.ReflectionsPan = {0.0f, 0.0f, 0.0f}; + props.LateReverbGain = AL_REVERB_DEFAULT_LATE_REVERB_GAIN; + props.LateReverbDelay = AL_REVERB_DEFAULT_LATE_REVERB_DELAY; + props.LateReverbPan = {0.0f, 0.0f, 0.0f}; + props.EchoTime = 0.25f; + props.EchoDepth = 0.0f; + props.ModulationTime = 0.25f; + props.ModulationDepth = 0.0f; + props.AirAbsorptionGainHF = AL_REVERB_DEFAULT_AIR_ABSORPTION_GAINHF; + props.HFReference = 5000.0f; + props.LFReference = 250.0f; + props.RoomRolloffFactor = AL_REVERB_DEFAULT_ROOM_ROLLOFF_FACTOR; + props.DecayHFLimit = AL_REVERB_DEFAULT_DECAY_HFLIMIT; + return props; +} + +} // namespace + +const EffectProps ReverbEffectProps{genDefaultProps()}; + +void ReverbEffectHandler::SetParami(ALCcontext *context, ReverbProps &props, ALenum param, int val) { switch(param) { case AL_EAXREVERB_DECAY_HFLIMIT: if(!(val >= AL_EAXREVERB_MIN_DECAY_HFLIMIT && val <= AL_EAXREVERB_MAX_DECAY_HFLIMIT)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb decay hflimit out of range"}; - props->Reverb.DecayHFLimit = val != AL_FALSE; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x", - param}; + context->throw_error(AL_INVALID_VALUE, "EAX Reverb decay hflimit out of range"); + props.DecayHFLimit = val != AL_FALSE; + return; } + context->throw_error(AL_INVALID_ENUM, "Invalid EAX reverb integer property {:#04x}", + as_unsigned(param)); } -void Reverb_setParamiv(EffectProps *props, ALenum param, const int *vals) -{ Reverb_setParami(props, param, vals[0]); } -void Reverb_setParamf(EffectProps *props, ALenum param, float val) +void ReverbEffectHandler::SetParamiv(ALCcontext *context, ReverbProps &props, ALenum param, const int *vals) +{ SetParami(context, props, param, *vals); } +void ReverbEffectHandler::SetParamf(ALCcontext *context, ReverbProps &props, ALenum param, float val) { switch(param) { case AL_EAXREVERB_DENSITY: if(!(val >= AL_EAXREVERB_MIN_DENSITY && val <= AL_EAXREVERB_MAX_DENSITY)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb density out of range"}; - props->Reverb.Density = val; - break; + context->throw_error(AL_INVALID_VALUE, "EAX Reverb density out of range"); + props.Density = val; + return; case AL_EAXREVERB_DIFFUSION: if(!(val >= AL_EAXREVERB_MIN_DIFFUSION && val <= AL_EAXREVERB_MAX_DIFFUSION)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb diffusion out of range"}; - props->Reverb.Diffusion = val; - break; + context->throw_error(AL_INVALID_VALUE, "EAX Reverb diffusion out of range"); + props.Diffusion = val; + return; case AL_EAXREVERB_GAIN: if(!(val >= AL_EAXREVERB_MIN_GAIN && val <= AL_EAXREVERB_MAX_GAIN)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb gain out of range"}; - props->Reverb.Gain = val; - break; + context->throw_error(AL_INVALID_VALUE, "EAX Reverb gain out of range"); + props.Gain = val; + return; case AL_EAXREVERB_GAINHF: if(!(val >= AL_EAXREVERB_MIN_GAINHF && val <= AL_EAXREVERB_MAX_GAINHF)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb gainhf out of range"}; - props->Reverb.GainHF = val; - break; + context->throw_error(AL_INVALID_VALUE, "EAX Reverb gainhf out of range"); + props.GainHF = val; + return; case AL_EAXREVERB_GAINLF: if(!(val >= AL_EAXREVERB_MIN_GAINLF && val <= AL_EAXREVERB_MAX_GAINLF)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb gainlf out of range"}; - props->Reverb.GainLF = val; - break; + context->throw_error(AL_INVALID_VALUE, "EAX Reverb gainlf out of range"); + props.GainLF = val; + return; case AL_EAXREVERB_DECAY_TIME: if(!(val >= AL_EAXREVERB_MIN_DECAY_TIME && val <= AL_EAXREVERB_MAX_DECAY_TIME)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb decay time out of range"}; - props->Reverb.DecayTime = val; - break; + context->throw_error(AL_INVALID_VALUE, "EAX Reverb decay time out of range"); + props.DecayTime = val; + return; case AL_EAXREVERB_DECAY_HFRATIO: if(!(val >= AL_EAXREVERB_MIN_DECAY_HFRATIO && val <= AL_EAXREVERB_MAX_DECAY_HFRATIO)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb decay hfratio out of range"}; - props->Reverb.DecayHFRatio = val; - break; + context->throw_error(AL_INVALID_VALUE, "EAX Reverb decay hfratio out of range"); + props.DecayHFRatio = val; + return; case AL_EAXREVERB_DECAY_LFRATIO: if(!(val >= AL_EAXREVERB_MIN_DECAY_LFRATIO && val <= AL_EAXREVERB_MAX_DECAY_LFRATIO)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb decay lfratio out of range"}; - props->Reverb.DecayLFRatio = val; - break; + context->throw_error(AL_INVALID_VALUE, "EAX Reverb decay lfratio out of range"); + props.DecayLFRatio = val; + return; case AL_EAXREVERB_REFLECTIONS_GAIN: if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_GAIN && val <= AL_EAXREVERB_MAX_REFLECTIONS_GAIN)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb reflections gain out of range"}; - props->Reverb.ReflectionsGain = val; - break; + context->throw_error(AL_INVALID_VALUE, "EAX Reverb reflections gain out of range"); + props.ReflectionsGain = val; + return; case AL_EAXREVERB_REFLECTIONS_DELAY: if(!(val >= AL_EAXREVERB_MIN_REFLECTIONS_DELAY && val <= AL_EAXREVERB_MAX_REFLECTIONS_DELAY)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb reflections delay out of range"}; - props->Reverb.ReflectionsDelay = val; - break; + context->throw_error(AL_INVALID_VALUE, "EAX Reverb reflections delay out of range"); + props.ReflectionsDelay = val; + return; case AL_EAXREVERB_LATE_REVERB_GAIN: if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_GAIN && val <= AL_EAXREVERB_MAX_LATE_REVERB_GAIN)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb late reverb gain out of range"}; - props->Reverb.LateReverbGain = val; - break; + context->throw_error(AL_INVALID_VALUE, "EAX Reverb late reverb gain out of range"); + props.LateReverbGain = val; + return; case AL_EAXREVERB_LATE_REVERB_DELAY: if(!(val >= AL_EAXREVERB_MIN_LATE_REVERB_DELAY && val <= AL_EAXREVERB_MAX_LATE_REVERB_DELAY)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb late reverb delay out of range"}; - props->Reverb.LateReverbDelay = val; - break; + context->throw_error(AL_INVALID_VALUE, "EAX Reverb late reverb delay out of range"); + props.LateReverbDelay = val; + return; case AL_EAXREVERB_ECHO_TIME: if(!(val >= AL_EAXREVERB_MIN_ECHO_TIME && val <= AL_EAXREVERB_MAX_ECHO_TIME)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb echo time out of range"}; - props->Reverb.EchoTime = val; - break; + context->throw_error(AL_INVALID_VALUE, "EAX Reverb echo time out of range"); + props.EchoTime = val; + return; case AL_EAXREVERB_ECHO_DEPTH: if(!(val >= AL_EAXREVERB_MIN_ECHO_DEPTH && val <= AL_EAXREVERB_MAX_ECHO_DEPTH)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb echo depth out of range"}; - props->Reverb.EchoDepth = val; - break; + context->throw_error(AL_INVALID_VALUE, "EAX Reverb echo depth out of range"); + props.EchoDepth = val; + return; case AL_EAXREVERB_MODULATION_TIME: if(!(val >= AL_EAXREVERB_MIN_MODULATION_TIME && val <= AL_EAXREVERB_MAX_MODULATION_TIME)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb modulation time out of range"}; - props->Reverb.ModulationTime = val; - break; + context->throw_error(AL_INVALID_VALUE, "EAX Reverb modulation time out of range"); + props.ModulationTime = val; + return; case AL_EAXREVERB_MODULATION_DEPTH: if(!(val >= AL_EAXREVERB_MIN_MODULATION_DEPTH && val <= AL_EAXREVERB_MAX_MODULATION_DEPTH)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb modulation depth out of range"}; - props->Reverb.ModulationDepth = val; - break; + context->throw_error(AL_INVALID_VALUE, "EAX Reverb modulation depth out of range"); + props.ModulationDepth = val; + return; case AL_EAXREVERB_AIR_ABSORPTION_GAINHF: if(!(val >= AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb air absorption gainhf out of range"}; - props->Reverb.AirAbsorptionGainHF = val; - break; + context->throw_error(AL_INVALID_VALUE, "EAX Reverb air absorption gainhf out of range"); + props.AirAbsorptionGainHF = val; + return; case AL_EAXREVERB_HFREFERENCE: if(!(val >= AL_EAXREVERB_MIN_HFREFERENCE && val <= AL_EAXREVERB_MAX_HFREFERENCE)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb hfreference out of range"}; - props->Reverb.HFReference = val; - break; + context->throw_error(AL_INVALID_VALUE, "EAX Reverb hfreference out of range"); + props.HFReference = val; + return; case AL_EAXREVERB_LFREFERENCE: if(!(val >= AL_EAXREVERB_MIN_LFREFERENCE && val <= AL_EAXREVERB_MAX_LFREFERENCE)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb lfreference out of range"}; - props->Reverb.LFReference = val; - break; + context->throw_error(AL_INVALID_VALUE, "EAX Reverb lfreference out of range"); + props.LFReference = val; + return; case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR: if(!(val >= AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR)) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb room rolloff factor out of range"}; - props->Reverb.RoomRolloffFactor = val; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x", param}; + context->throw_error(AL_INVALID_VALUE, "EAX Reverb room rolloff factor out of range"); + props.RoomRolloffFactor = val; + return; } + context->throw_error(AL_INVALID_ENUM, "Invalid EAX reverb float property {:#04x}", + as_unsigned(param)); } -void Reverb_setParamfv(EffectProps *props, ALenum param, const float *vals) +void ReverbEffectHandler::SetParamfv(ALCcontext *context, ReverbProps &props, ALenum param, const float *vals) { + static constexpr auto finite_checker = [](float f) -> bool { return std::isfinite(f); }; + al::span values; switch(param) { case AL_EAXREVERB_REFLECTIONS_PAN: - if(!(std::isfinite(vals[0]) && std::isfinite(vals[1]) && std::isfinite(vals[2]))) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb reflections pan out of range"}; - props->Reverb.ReflectionsPan[0] = vals[0]; - props->Reverb.ReflectionsPan[1] = vals[1]; - props->Reverb.ReflectionsPan[2] = vals[2]; - break; + values = {vals, 3_uz}; + if(!std::all_of(values.cbegin(), values.cend(), finite_checker)) + context->throw_error(AL_INVALID_VALUE, "EAX Reverb reflections pan out of range"); + std::copy(values.cbegin(), values.cend(), props.ReflectionsPan.begin()); + return; case AL_EAXREVERB_LATE_REVERB_PAN: - if(!(std::isfinite(vals[0]) && std::isfinite(vals[1]) && std::isfinite(vals[2]))) - throw effect_exception{AL_INVALID_VALUE, "EAX Reverb late reverb pan out of range"}; - props->Reverb.LateReverbPan[0] = vals[0]; - props->Reverb.LateReverbPan[1] = vals[1]; - props->Reverb.LateReverbPan[2] = vals[2]; - break; - - default: - Reverb_setParamf(props, param, vals[0]); - break; + values = {vals, 3_uz}; + if(!std::all_of(values.cbegin(), values.cend(), finite_checker)) + context->throw_error(AL_INVALID_VALUE, "EAX Reverb late reverb pan out of range"); + std::copy(values.cbegin(), values.cend(), props.LateReverbPan.begin()); + return; } + SetParamf(context, props, param, *vals); } -void Reverb_getParami(const EffectProps *props, ALenum param, int *val) +void ReverbEffectHandler::GetParami(ALCcontext *context, const ReverbProps &props, ALenum param, int *val) { switch(param) { - case AL_EAXREVERB_DECAY_HFLIMIT: - *val = props->Reverb.DecayHFLimit; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb integer property 0x%04x", - param}; + case AL_EAXREVERB_DECAY_HFLIMIT: *val = props.DecayHFLimit; return; } + context->throw_error(AL_INVALID_ENUM, "Invalid EAX reverb integer property {:#04x}", + as_unsigned(param)); } -void Reverb_getParamiv(const EffectProps *props, ALenum param, int *vals) -{ Reverb_getParami(props, param, vals); } -void Reverb_getParamf(const EffectProps *props, ALenum param, float *val) +void ReverbEffectHandler::GetParamiv(ALCcontext *context, const ReverbProps &props, ALenum param, int *vals) +{ GetParami(context, props, param, vals); } +void ReverbEffectHandler::GetParamf(ALCcontext *context, const ReverbProps &props, ALenum param, float *val) { switch(param) { - case AL_EAXREVERB_DENSITY: - *val = props->Reverb.Density; - break; - - case AL_EAXREVERB_DIFFUSION: - *val = props->Reverb.Diffusion; - break; - - case AL_EAXREVERB_GAIN: - *val = props->Reverb.Gain; - break; - - case AL_EAXREVERB_GAINHF: - *val = props->Reverb.GainHF; - break; - - case AL_EAXREVERB_GAINLF: - *val = props->Reverb.GainLF; - break; - - case AL_EAXREVERB_DECAY_TIME: - *val = props->Reverb.DecayTime; - break; - - case AL_EAXREVERB_DECAY_HFRATIO: - *val = props->Reverb.DecayHFRatio; - break; - - case AL_EAXREVERB_DECAY_LFRATIO: - *val = props->Reverb.DecayLFRatio; - break; - - case AL_EAXREVERB_REFLECTIONS_GAIN: - *val = props->Reverb.ReflectionsGain; - break; - - case AL_EAXREVERB_REFLECTIONS_DELAY: - *val = props->Reverb.ReflectionsDelay; - break; - - case AL_EAXREVERB_LATE_REVERB_GAIN: - *val = props->Reverb.LateReverbGain; - break; - - case AL_EAXREVERB_LATE_REVERB_DELAY: - *val = props->Reverb.LateReverbDelay; - break; - - case AL_EAXREVERB_ECHO_TIME: - *val = props->Reverb.EchoTime; - break; - - case AL_EAXREVERB_ECHO_DEPTH: - *val = props->Reverb.EchoDepth; - break; - - case AL_EAXREVERB_MODULATION_TIME: - *val = props->Reverb.ModulationTime; - break; - - case AL_EAXREVERB_MODULATION_DEPTH: - *val = props->Reverb.ModulationDepth; - break; - - case AL_EAXREVERB_AIR_ABSORPTION_GAINHF: - *val = props->Reverb.AirAbsorptionGainHF; - break; - - case AL_EAXREVERB_HFREFERENCE: - *val = props->Reverb.HFReference; - break; - - case AL_EAXREVERB_LFREFERENCE: - *val = props->Reverb.LFReference; - break; - - case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR: - *val = props->Reverb.RoomRolloffFactor; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid EAX reverb float property 0x%04x", param}; + case AL_EAXREVERB_DENSITY: *val = props.Density; return; + case AL_EAXREVERB_DIFFUSION: *val = props.Diffusion; return; + case AL_EAXREVERB_GAIN: *val = props.Gain; return; + case AL_EAXREVERB_GAINHF: *val = props.GainHF; return; + case AL_EAXREVERB_GAINLF: *val = props.GainLF; return; + case AL_EAXREVERB_DECAY_TIME: *val = props.DecayTime; return; + case AL_EAXREVERB_DECAY_HFRATIO: *val = props.DecayHFRatio; return; + case AL_EAXREVERB_DECAY_LFRATIO: *val = props.DecayLFRatio; return; + case AL_EAXREVERB_REFLECTIONS_GAIN: *val = props.ReflectionsGain; return; + case AL_EAXREVERB_REFLECTIONS_DELAY: *val = props.ReflectionsDelay; return; + case AL_EAXREVERB_LATE_REVERB_GAIN: *val = props.LateReverbGain; return; + case AL_EAXREVERB_LATE_REVERB_DELAY: *val = props.LateReverbDelay; return; + case AL_EAXREVERB_ECHO_TIME: *val = props.EchoTime; return; + case AL_EAXREVERB_ECHO_DEPTH: *val = props.EchoDepth; return; + case AL_EAXREVERB_MODULATION_TIME: *val = props.ModulationTime; return; + case AL_EAXREVERB_MODULATION_DEPTH: *val = props.ModulationDepth; return; + case AL_EAXREVERB_AIR_ABSORPTION_GAINHF: *val = props.AirAbsorptionGainHF; return; + case AL_EAXREVERB_HFREFERENCE: *val = props.HFReference; return; + case AL_EAXREVERB_LFREFERENCE: *val = props.LFReference; return; + case AL_EAXREVERB_ROOM_ROLLOFF_FACTOR: *val = props.RoomRolloffFactor; return; } + + context->throw_error(AL_INVALID_ENUM, "Invalid EAX reverb float property {:#04x}", + as_unsigned(param)); } -void Reverb_getParamfv(const EffectProps *props, ALenum param, float *vals) +void ReverbEffectHandler::GetParamfv(ALCcontext *context, const ReverbProps &props, ALenum param, float *vals) { + al::span values; switch(param) { case AL_EAXREVERB_REFLECTIONS_PAN: - vals[0] = props->Reverb.ReflectionsPan[0]; - vals[1] = props->Reverb.ReflectionsPan[1]; - vals[2] = props->Reverb.ReflectionsPan[2]; - break; + values = {vals, 3_uz}; + std::copy(props.ReflectionsPan.cbegin(), props.ReflectionsPan.cend(), values.begin()); + return; case AL_EAXREVERB_LATE_REVERB_PAN: - vals[0] = props->Reverb.LateReverbPan[0]; - vals[1] = props->Reverb.LateReverbPan[1]; - vals[2] = props->Reverb.LateReverbPan[2]; - break; - - default: - Reverb_getParamf(props, param, vals); - break; + values = {vals, 3_uz}; + std::copy(props.LateReverbPan.cbegin(), props.LateReverbPan.cend(), values.begin()); + return; } -} -EffectProps genDefaultProps() noexcept -{ - EffectProps props{}; - props.Reverb.Density = AL_EAXREVERB_DEFAULT_DENSITY; - props.Reverb.Diffusion = AL_EAXREVERB_DEFAULT_DIFFUSION; - props.Reverb.Gain = AL_EAXREVERB_DEFAULT_GAIN; - props.Reverb.GainHF = AL_EAXREVERB_DEFAULT_GAINHF; - props.Reverb.GainLF = AL_EAXREVERB_DEFAULT_GAINLF; - props.Reverb.DecayTime = AL_EAXREVERB_DEFAULT_DECAY_TIME; - props.Reverb.DecayHFRatio = AL_EAXREVERB_DEFAULT_DECAY_HFRATIO; - props.Reverb.DecayLFRatio = AL_EAXREVERB_DEFAULT_DECAY_LFRATIO; - props.Reverb.ReflectionsGain = AL_EAXREVERB_DEFAULT_REFLECTIONS_GAIN; - props.Reverb.ReflectionsDelay = AL_EAXREVERB_DEFAULT_REFLECTIONS_DELAY; - props.Reverb.ReflectionsPan[0] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ; - props.Reverb.ReflectionsPan[1] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ; - props.Reverb.ReflectionsPan[2] = AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ; - props.Reverb.LateReverbGain = AL_EAXREVERB_DEFAULT_LATE_REVERB_GAIN; - props.Reverb.LateReverbDelay = AL_EAXREVERB_DEFAULT_LATE_REVERB_DELAY; - props.Reverb.LateReverbPan[0] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ; - props.Reverb.LateReverbPan[1] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ; - props.Reverb.LateReverbPan[2] = AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ; - props.Reverb.EchoTime = AL_EAXREVERB_DEFAULT_ECHO_TIME; - props.Reverb.EchoDepth = AL_EAXREVERB_DEFAULT_ECHO_DEPTH; - props.Reverb.ModulationTime = AL_EAXREVERB_DEFAULT_MODULATION_TIME; - props.Reverb.ModulationDepth = AL_EAXREVERB_DEFAULT_MODULATION_DEPTH; - props.Reverb.AirAbsorptionGainHF = AL_EAXREVERB_DEFAULT_AIR_ABSORPTION_GAINHF; - props.Reverb.HFReference = AL_EAXREVERB_DEFAULT_HFREFERENCE; - props.Reverb.LFReference = AL_EAXREVERB_DEFAULT_LFREFERENCE; - props.Reverb.RoomRolloffFactor = AL_EAXREVERB_DEFAULT_ROOM_ROLLOFF_FACTOR; - props.Reverb.DecayHFLimit = AL_EAXREVERB_DEFAULT_DECAY_HFLIMIT; - return props; + GetParamf(context, props, param, vals); } -void StdReverb_setParami(EffectProps *props, ALenum param, int val) +const EffectProps StdReverbEffectProps{genDefaultStdProps()}; + +void StdReverbEffectHandler::SetParami(ALCcontext *context, ReverbProps &props, ALenum param, int val) { switch(param) { case AL_REVERB_DECAY_HFLIMIT: if(!(val >= AL_REVERB_MIN_DECAY_HFLIMIT && val <= AL_REVERB_MAX_DECAY_HFLIMIT)) - throw effect_exception{AL_INVALID_VALUE, "Reverb decay hflimit out of range"}; - props->Reverb.DecayHFLimit = val != AL_FALSE; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param}; + context->throw_error(AL_INVALID_VALUE, "EAX Reverb decay hflimit out of range"); + props.DecayHFLimit = val != AL_FALSE; + return; } + + context->throw_error(AL_INVALID_ENUM, "Invalid EAX reverb integer property {:#04x}", + as_unsigned(param)); } -void StdReverb_setParamiv(EffectProps *props, ALenum param, const int *vals) -{ StdReverb_setParami(props, param, vals[0]); } -void StdReverb_setParamf(EffectProps *props, ALenum param, float val) +void StdReverbEffectHandler::SetParamiv(ALCcontext *context, ReverbProps &props, ALenum param, const int *vals) +{ SetParami(context, props, param, *vals); } +void StdReverbEffectHandler::SetParamf(ALCcontext *context, ReverbProps &props, ALenum param, float val) { switch(param) { case AL_REVERB_DENSITY: if(!(val >= AL_REVERB_MIN_DENSITY && val <= AL_REVERB_MAX_DENSITY)) - throw effect_exception{AL_INVALID_VALUE, "Reverb density out of range"}; - props->Reverb.Density = val; - break; + context->throw_error(AL_INVALID_VALUE, "EAX Reverb density out of range"); + props.Density = val; + return; case AL_REVERB_DIFFUSION: if(!(val >= AL_REVERB_MIN_DIFFUSION && val <= AL_REVERB_MAX_DIFFUSION)) - throw effect_exception{AL_INVALID_VALUE, "Reverb diffusion out of range"}; - props->Reverb.Diffusion = val; - break; + context->throw_error(AL_INVALID_VALUE, "EAX Reverb diffusion out of range"); + props.Diffusion = val; + return; case AL_REVERB_GAIN: if(!(val >= AL_REVERB_MIN_GAIN && val <= AL_REVERB_MAX_GAIN)) - throw effect_exception{AL_INVALID_VALUE, "Reverb gain out of range"}; - props->Reverb.Gain = val; - break; + context->throw_error(AL_INVALID_VALUE, "EAX Reverb gain out of range"); + props.Gain = val; + return; case AL_REVERB_GAINHF: if(!(val >= AL_REVERB_MIN_GAINHF && val <= AL_REVERB_MAX_GAINHF)) - throw effect_exception{AL_INVALID_VALUE, "Reverb gainhf out of range"}; - props->Reverb.GainHF = val; - break; + context->throw_error(AL_INVALID_VALUE, "EAX Reverb gainhf out of range"); + props.GainHF = val; + return; case AL_REVERB_DECAY_TIME: if(!(val >= AL_REVERB_MIN_DECAY_TIME && val <= AL_REVERB_MAX_DECAY_TIME)) - throw effect_exception{AL_INVALID_VALUE, "Reverb decay time out of range"}; - props->Reverb.DecayTime = val; - break; + context->throw_error(AL_INVALID_VALUE, "EAX Reverb decay time out of range"); + props.DecayTime = val; + return; case AL_REVERB_DECAY_HFRATIO: if(!(val >= AL_REVERB_MIN_DECAY_HFRATIO && val <= AL_REVERB_MAX_DECAY_HFRATIO)) - throw effect_exception{AL_INVALID_VALUE, "Reverb decay hfratio out of range"}; - props->Reverb.DecayHFRatio = val; - break; + context->throw_error(AL_INVALID_VALUE, "EAX Reverb decay hfratio out of range"); + props.DecayHFRatio = val; + return; case AL_REVERB_REFLECTIONS_GAIN: if(!(val >= AL_REVERB_MIN_REFLECTIONS_GAIN && val <= AL_REVERB_MAX_REFLECTIONS_GAIN)) - throw effect_exception{AL_INVALID_VALUE, "Reverb reflections gain out of range"}; - props->Reverb.ReflectionsGain = val; - break; + context->throw_error(AL_INVALID_VALUE, "EAX Reverb reflections gain out of range"); + props.ReflectionsGain = val; + return; case AL_REVERB_REFLECTIONS_DELAY: if(!(val >= AL_REVERB_MIN_REFLECTIONS_DELAY && val <= AL_REVERB_MAX_REFLECTIONS_DELAY)) - throw effect_exception{AL_INVALID_VALUE, "Reverb reflections delay out of range"}; - props->Reverb.ReflectionsDelay = val; - break; + context->throw_error(AL_INVALID_VALUE, "EAX Reverb reflections delay out of range"); + props.ReflectionsDelay = val; + return; case AL_REVERB_LATE_REVERB_GAIN: if(!(val >= AL_REVERB_MIN_LATE_REVERB_GAIN && val <= AL_REVERB_MAX_LATE_REVERB_GAIN)) - throw effect_exception{AL_INVALID_VALUE, "Reverb late reverb gain out of range"}; - props->Reverb.LateReverbGain = val; - break; + context->throw_error(AL_INVALID_VALUE, "EAX Reverb late reverb gain out of range"); + props.LateReverbGain = val; + return; case AL_REVERB_LATE_REVERB_DELAY: if(!(val >= AL_REVERB_MIN_LATE_REVERB_DELAY && val <= AL_REVERB_MAX_LATE_REVERB_DELAY)) - throw effect_exception{AL_INVALID_VALUE, "Reverb late reverb delay out of range"}; - props->Reverb.LateReverbDelay = val; - break; + context->throw_error(AL_INVALID_VALUE, "EAX Reverb late reverb delay out of range"); + props.LateReverbDelay = val; + return; case AL_REVERB_AIR_ABSORPTION_GAINHF: if(!(val >= AL_REVERB_MIN_AIR_ABSORPTION_GAINHF && val <= AL_REVERB_MAX_AIR_ABSORPTION_GAINHF)) - throw effect_exception{AL_INVALID_VALUE, "Reverb air absorption gainhf out of range"}; - props->Reverb.AirAbsorptionGainHF = val; - break; + context->throw_error(AL_INVALID_VALUE, "EAX Reverb air absorption gainhf out of range"); + props.AirAbsorptionGainHF = val; + return; case AL_REVERB_ROOM_ROLLOFF_FACTOR: if(!(val >= AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR && val <= AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR)) - throw effect_exception{AL_INVALID_VALUE, "Reverb room rolloff factor out of range"}; - props->Reverb.RoomRolloffFactor = val; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param}; + context->throw_error(AL_INVALID_VALUE, "EAX Reverb room rolloff factor out of range"); + props.RoomRolloffFactor = val; + return; } + + context->throw_error(AL_INVALID_ENUM, "Invalid EAX reverb float property {:#04x}", + as_unsigned(param)); } -void StdReverb_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ StdReverb_setParamf(props, param, vals[0]); } +void StdReverbEffectHandler::SetParamfv(ALCcontext *context, ReverbProps &props, ALenum param, const float *vals) +{ SetParamf(context, props, param, *vals); } -void StdReverb_getParami(const EffectProps *props, ALenum param, int *val) +void StdReverbEffectHandler::GetParami(ALCcontext *context, const ReverbProps &props, ALenum param, int *val) { switch(param) { - case AL_REVERB_DECAY_HFLIMIT: - *val = props->Reverb.DecayHFLimit; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid reverb integer property 0x%04x", param}; + case AL_REVERB_DECAY_HFLIMIT: *val = props.DecayHFLimit; return; } + context->throw_error(AL_INVALID_ENUM, "Invalid EAX reverb integer property {:#04x}", + as_unsigned(param)); } -void StdReverb_getParamiv(const EffectProps *props, ALenum param, int *vals) -{ StdReverb_getParami(props, param, vals); } -void StdReverb_getParamf(const EffectProps *props, ALenum param, float *val) +void StdReverbEffectHandler::GetParamiv(ALCcontext *context, const ReverbProps &props, ALenum param, int *vals) +{ GetParami(context, props, param, vals); } +void StdReverbEffectHandler::GetParamf(ALCcontext *context, const ReverbProps &props, ALenum param, float *val) { switch(param) { - case AL_REVERB_DENSITY: - *val = props->Reverb.Density; - break; - - case AL_REVERB_DIFFUSION: - *val = props->Reverb.Diffusion; - break; - - case AL_REVERB_GAIN: - *val = props->Reverb.Gain; - break; - - case AL_REVERB_GAINHF: - *val = props->Reverb.GainHF; - break; - - case AL_REVERB_DECAY_TIME: - *val = props->Reverb.DecayTime; - break; - - case AL_REVERB_DECAY_HFRATIO: - *val = props->Reverb.DecayHFRatio; - break; - - case AL_REVERB_REFLECTIONS_GAIN: - *val = props->Reverb.ReflectionsGain; - break; - - case AL_REVERB_REFLECTIONS_DELAY: - *val = props->Reverb.ReflectionsDelay; - break; - - case AL_REVERB_LATE_REVERB_GAIN: - *val = props->Reverb.LateReverbGain; - break; - - case AL_REVERB_LATE_REVERB_DELAY: - *val = props->Reverb.LateReverbDelay; - break; - - case AL_REVERB_AIR_ABSORPTION_GAINHF: - *val = props->Reverb.AirAbsorptionGainHF; - break; - - case AL_REVERB_ROOM_ROLLOFF_FACTOR: - *val = props->Reverb.RoomRolloffFactor; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid reverb float property 0x%04x", param}; + case AL_REVERB_DENSITY: *val = props.Density; return; + case AL_REVERB_DIFFUSION: *val = props.Diffusion; return; + case AL_REVERB_GAIN: *val = props.Gain; return; + case AL_REVERB_GAINHF: *val = props.GainHF; return; + case AL_REVERB_DECAY_TIME: *val = props.DecayTime; return; + case AL_REVERB_DECAY_HFRATIO: *val = props.DecayHFRatio; return; + case AL_REVERB_REFLECTIONS_GAIN: *val = props.ReflectionsGain; return; + case AL_REVERB_REFLECTIONS_DELAY: *val = props.ReflectionsDelay; return; + case AL_REVERB_LATE_REVERB_GAIN: *val = props.LateReverbGain; return; + case AL_REVERB_LATE_REVERB_DELAY: *val = props.LateReverbDelay; return; + case AL_REVERB_AIR_ABSORPTION_GAINHF: *val = props.AirAbsorptionGainHF; return; + case AL_REVERB_ROOM_ROLLOFF_FACTOR: *val = props.RoomRolloffFactor; return; } -} -void StdReverb_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ StdReverb_getParamf(props, param, vals); } -EffectProps genDefaultStdProps() noexcept -{ - EffectProps props{}; - props.Reverb.Density = AL_REVERB_DEFAULT_DENSITY; - props.Reverb.Diffusion = AL_REVERB_DEFAULT_DIFFUSION; - props.Reverb.Gain = AL_REVERB_DEFAULT_GAIN; - props.Reverb.GainHF = AL_REVERB_DEFAULT_GAINHF; - props.Reverb.GainLF = 1.0f; - props.Reverb.DecayTime = AL_REVERB_DEFAULT_DECAY_TIME; - props.Reverb.DecayHFRatio = AL_REVERB_DEFAULT_DECAY_HFRATIO; - props.Reverb.DecayLFRatio = 1.0f; - props.Reverb.ReflectionsGain = AL_REVERB_DEFAULT_REFLECTIONS_GAIN; - props.Reverb.ReflectionsDelay = AL_REVERB_DEFAULT_REFLECTIONS_DELAY; - props.Reverb.ReflectionsPan[0] = 0.0f; - props.Reverb.ReflectionsPan[1] = 0.0f; - props.Reverb.ReflectionsPan[2] = 0.0f; - props.Reverb.LateReverbGain = AL_REVERB_DEFAULT_LATE_REVERB_GAIN; - props.Reverb.LateReverbDelay = AL_REVERB_DEFAULT_LATE_REVERB_DELAY; - props.Reverb.LateReverbPan[0] = 0.0f; - props.Reverb.LateReverbPan[1] = 0.0f; - props.Reverb.LateReverbPan[2] = 0.0f; - props.Reverb.EchoTime = 0.25f; - props.Reverb.EchoDepth = 0.0f; - props.Reverb.ModulationTime = 0.25f; - props.Reverb.ModulationDepth = 0.0f; - props.Reverb.AirAbsorptionGainHF = AL_REVERB_DEFAULT_AIR_ABSORPTION_GAINHF; - props.Reverb.HFReference = 5000.0f; - props.Reverb.LFReference = 250.0f; - props.Reverb.RoomRolloffFactor = AL_REVERB_DEFAULT_ROOM_ROLLOFF_FACTOR; - props.Reverb.DecayHFLimit = AL_REVERB_DEFAULT_DECAY_HFLIMIT; - return props; + context->throw_error(AL_INVALID_ENUM, "Invalid EAX reverb float property {:#04x}", + as_unsigned(param)); } +void StdReverbEffectHandler::GetParamfv(ALCcontext *context, const ReverbProps &props, ALenum param, float *vals) +{ GetParamf(context, props, param, vals); } -} // namespace - -DEFINE_ALEFFECT_VTABLE(Reverb); - -const EffectProps ReverbEffectProps{genDefaultProps()}; - -DEFINE_ALEFFECT_VTABLE(StdReverb); - -const EffectProps StdReverbEffectProps{genDefaultStdProps()}; -#ifdef ALSOFT_EAX +#if ALSOFT_EAX namespace { class EaxReverbEffectException : public EaxException @@ -1086,98 +975,87 @@ struct EaxReverbCommitter::Exception : public EaxReverbEffectException throw Exception{message}; } -void EaxReverbCommitter::translate(const EAX_REVERBPROPERTIES& src, EaxEffectProps& dst) noexcept +void EaxReverbCommitter::translate(const EAX_REVERBPROPERTIES& src, EAXREVERBPROPERTIES& dst) noexcept { assert(src.environment <= EAX1REVERB_MAXENVIRONMENT); - auto&& eaxprops = dst.emplace(EAXREVERB_PRESETS[src.environment]); - eaxprops.flDecayTime = src.fDecayTime_sec; - eaxprops.flDecayHFRatio = src.fDamping; - eaxprops.lReverb = mini(static_cast(gain_to_level_mb(src.fVolume)), 0); + dst = EAXREVERB_PRESETS[src.environment]; + dst.flDecayTime = src.fDecayTime_sec; + dst.flDecayHFRatio = src.fDamping; + dst.lReverb = static_cast(std::min(gain_to_level_mb(src.fVolume), 0.0f)); } -void EaxReverbCommitter::translate(const EAX20LISTENERPROPERTIES& src, EaxEffectProps& dst) noexcept +void EaxReverbCommitter::translate(const EAX20LISTENERPROPERTIES& src, EAXREVERBPROPERTIES& dst) noexcept { assert(src.dwEnvironment <= EAX1REVERB_MAXENVIRONMENT); - auto&& eaxprops = dst.emplace(EAXREVERB_PRESETS[src.dwEnvironment]); - eaxprops.ulEnvironment = src.dwEnvironment; - eaxprops.flEnvironmentSize = src.flEnvironmentSize; - eaxprops.flEnvironmentDiffusion = src.flEnvironmentDiffusion; - eaxprops.lRoom = src.lRoom; - eaxprops.lRoomHF = src.lRoomHF; - eaxprops.flDecayTime = src.flDecayTime; - eaxprops.flDecayHFRatio = src.flDecayHFRatio; - eaxprops.lReflections = src.lReflections; - eaxprops.flReflectionsDelay = src.flReflectionsDelay; - eaxprops.lReverb = src.lReverb; - eaxprops.flReverbDelay = src.flReverbDelay; - eaxprops.flAirAbsorptionHF = src.flAirAbsorptionHF; - eaxprops.flRoomRolloffFactor = src.flRoomRolloffFactor; - eaxprops.ulFlags = src.dwFlags; -} - -void EaxReverbCommitter::translate(const EAXREVERBPROPERTIES& src, EaxEffectProps& dst) noexcept -{ - dst = src; + dst = EAXREVERB_PRESETS[src.dwEnvironment]; + dst.ulEnvironment = src.dwEnvironment; + dst.flEnvironmentSize = src.flEnvironmentSize; + dst.flEnvironmentDiffusion = src.flEnvironmentDiffusion; + dst.lRoom = src.lRoom; + dst.lRoomHF = src.lRoomHF; + dst.flDecayTime = src.flDecayTime; + dst.flDecayHFRatio = src.flDecayHFRatio; + dst.lReflections = src.lReflections; + dst.flReflectionsDelay = src.flReflectionsDelay; + dst.lReverb = src.lReverb; + dst.flReverbDelay = src.flReverbDelay; + dst.flAirAbsorptionHF = src.flAirAbsorptionHF; + dst.flRoomRolloffFactor = src.flRoomRolloffFactor; + dst.ulFlags = src.dwFlags; } bool EaxReverbCommitter::commit(const EAX_REVERBPROPERTIES &props) { - EaxEffectProps dst{}; + EAXREVERBPROPERTIES dst{}; translate(props, dst); return commit(dst); } bool EaxReverbCommitter::commit(const EAX20LISTENERPROPERTIES &props) { - EaxEffectProps dst{}; + EAXREVERBPROPERTIES dst{}; translate(props, dst); return commit(dst); } bool EaxReverbCommitter::commit(const EAXREVERBPROPERTIES &props) { - EaxEffectProps dst{}; - translate(props, dst); - return commit(dst); -} - -bool EaxReverbCommitter::commit(const EaxEffectProps &props) -{ - if(props == mEaxProps) + if(auto *cur = std::get_if(&mEaxProps); cur && *cur == props) return false; mEaxProps = props; - auto &eaxprops = std::get(props); - const auto size = eaxprops.flEnvironmentSize; - const auto density = (size * size * size) / 16.0F; - mAlProps.Reverb.Density = std::min(density, AL_EAXREVERB_MAX_DENSITY); - mAlProps.Reverb.Diffusion = eaxprops.flEnvironmentDiffusion; - mAlProps.Reverb.Gain = level_mb_to_gain(static_cast(eaxprops.lRoom)); - mAlProps.Reverb.GainHF = level_mb_to_gain(static_cast(eaxprops.lRoomHF)); - mAlProps.Reverb.GainLF = level_mb_to_gain(static_cast(eaxprops.lRoomLF)); - mAlProps.Reverb.DecayTime = eaxprops.flDecayTime; - mAlProps.Reverb.DecayHFRatio = eaxprops.flDecayHFRatio; - mAlProps.Reverb.DecayLFRatio = eaxprops.flDecayLFRatio; - mAlProps.Reverb.ReflectionsGain = level_mb_to_gain(static_cast(eaxprops.lReflections)); - mAlProps.Reverb.ReflectionsDelay = eaxprops.flReflectionsDelay; - mAlProps.Reverb.ReflectionsPan[0] = eaxprops.vReflectionsPan.x; - mAlProps.Reverb.ReflectionsPan[1] = eaxprops.vReflectionsPan.y; - mAlProps.Reverb.ReflectionsPan[2] = eaxprops.vReflectionsPan.z; - mAlProps.Reverb.LateReverbGain = level_mb_to_gain(static_cast(eaxprops.lReverb)); - mAlProps.Reverb.LateReverbDelay = eaxprops.flReverbDelay; - mAlProps.Reverb.LateReverbPan[0] = eaxprops.vReverbPan.x; - mAlProps.Reverb.LateReverbPan[1] = eaxprops.vReverbPan.y; - mAlProps.Reverb.LateReverbPan[2] = eaxprops.vReverbPan.z; - mAlProps.Reverb.EchoTime = eaxprops.flEchoTime; - mAlProps.Reverb.EchoDepth = eaxprops.flEchoDepth; - mAlProps.Reverb.ModulationTime = eaxprops.flModulationTime; - mAlProps.Reverb.ModulationDepth = eaxprops.flModulationDepth; - mAlProps.Reverb.AirAbsorptionGainHF = level_mb_to_gain(eaxprops.flAirAbsorptionHF); - mAlProps.Reverb.HFReference = eaxprops.flHFReference; - mAlProps.Reverb.LFReference = eaxprops.flLFReference; - mAlProps.Reverb.RoomRolloffFactor = eaxprops.flRoomRolloffFactor; - mAlProps.Reverb.DecayHFLimit = ((eaxprops.ulFlags & EAXREVERBFLAGS_DECAYHFLIMIT) != 0); + const auto size = props.flEnvironmentSize; + const auto density = (size * size * size) / 16.0f; + mAlProps = [&]{ + ReverbProps ret{}; + ret.Density = std::min(density, AL_EAXREVERB_MAX_DENSITY); + ret.Diffusion = props.flEnvironmentDiffusion; + ret.Gain = level_mb_to_gain(static_cast(props.lRoom)); + ret.GainHF = level_mb_to_gain(static_cast(props.lRoomHF)); + ret.GainLF = level_mb_to_gain(static_cast(props.lRoomLF)); + ret.DecayTime = props.flDecayTime; + ret.DecayHFRatio = props.flDecayHFRatio; + ret.DecayLFRatio = props.flDecayLFRatio; + ret.ReflectionsGain = level_mb_to_gain(static_cast(props.lReflections)); + ret.ReflectionsDelay = props.flReflectionsDelay; + ret.ReflectionsPan = {props.vReflectionsPan.x, props.vReflectionsPan.y, + props.vReflectionsPan.z}; + ret.LateReverbGain = level_mb_to_gain(static_cast(props.lReverb)); + ret.LateReverbDelay = props.flReverbDelay; + ret.LateReverbPan = {props.vReverbPan.x, props.vReverbPan.y, props.vReverbPan.z}; + ret.EchoTime = props.flEchoTime; + ret.EchoDepth = props.flEchoDepth; + ret.ModulationTime = props.flModulationTime; + ret.ModulationDepth = props.flModulationDepth; + ret.AirAbsorptionGainHF = level_mb_to_gain(props.flAirAbsorptionHF); + ret.HFReference = props.flHFReference; + ret.LFReference = props.flLFReference; + ret.RoomRolloffFactor = props.flRoomRolloffFactor; + ret.DecayHFLimit = ((props.ulFlags & EAXREVERBFLAGS_DECAYHFLIMIT) != 0); + return ret; + }(); + return true; } @@ -1274,11 +1152,6 @@ void EaxReverbCommitter::Get(const EaxCall &call, const EAXREVERBPROPERTIES &pro } } -void EaxReverbCommitter::Get(const EaxCall &call, const EaxEffectProps &props) -{ - Get(call, std::get(props)); -} - void EaxReverbCommitter::Set(const EaxCall &call, EAX_REVERBPROPERTIES &props) { @@ -1297,71 +1170,23 @@ void EaxReverbCommitter::Set(const EaxCall &call, EAX20LISTENERPROPERTIES &props { switch(call.get_property_id()) { - case DSPROPERTY_EAX20LISTENER_NONE: - break; - - case DSPROPERTY_EAX20LISTENER_ALLPARAMETERS: - defer(call, props); - break; - - case DSPROPERTY_EAX20LISTENER_ROOM: - defer(call, props.lRoom); - break; - - case DSPROPERTY_EAX20LISTENER_ROOMHF: - defer(call, props.lRoomHF); - break; - - case DSPROPERTY_EAX20LISTENER_ROOMROLLOFFFACTOR: - defer(call, props.flRoomRolloffFactor); - break; - - case DSPROPERTY_EAX20LISTENER_DECAYTIME: - defer(call, props.flDecayTime); - break; - - case DSPROPERTY_EAX20LISTENER_DECAYHFRATIO: - defer(call, props.flDecayHFRatio); - break; - - case DSPROPERTY_EAX20LISTENER_REFLECTIONS: - defer(call, props.lReflections); - break; - - case DSPROPERTY_EAX20LISTENER_REFLECTIONSDELAY: - defer(call, props.flReverbDelay); - break; - - case DSPROPERTY_EAX20LISTENER_REVERB: - defer(call, props.lReverb); - break; - - case DSPROPERTY_EAX20LISTENER_REVERBDELAY: - defer(call, props.flReverbDelay); - break; - - case DSPROPERTY_EAX20LISTENER_ENVIRONMENT: - defer(call, props, props.dwEnvironment); - break; - - case DSPROPERTY_EAX20LISTENER_ENVIRONMENTSIZE: - defer(call, props, props.flEnvironmentSize); - break; - - case DSPROPERTY_EAX20LISTENER_ENVIRONMENTDIFFUSION: - defer(call, props.flEnvironmentDiffusion); - break; - - case DSPROPERTY_EAX20LISTENER_AIRABSORPTIONHF: - defer(call, props.flAirAbsorptionHF); - break; - - case DSPROPERTY_EAX20LISTENER_FLAGS: - defer(call, props.dwFlags); - break; - - default: - fail_unknown_property_id(); + case DSPROPERTY_EAX20LISTENER_NONE: break; + case DSPROPERTY_EAX20LISTENER_ALLPARAMETERS: defer(call, props); break; + case DSPROPERTY_EAX20LISTENER_ROOM: defer(call, props.lRoom); break; + case DSPROPERTY_EAX20LISTENER_ROOMHF: defer(call, props.lRoomHF); break; + case DSPROPERTY_EAX20LISTENER_ROOMROLLOFFFACTOR: defer(call, props.flRoomRolloffFactor); break; + case DSPROPERTY_EAX20LISTENER_DECAYTIME: defer(call, props.flDecayTime); break; + case DSPROPERTY_EAX20LISTENER_DECAYHFRATIO: defer(call, props.flDecayHFRatio); break; + case DSPROPERTY_EAX20LISTENER_REFLECTIONS: defer(call, props.lReflections); break; + case DSPROPERTY_EAX20LISTENER_REFLECTIONSDELAY: defer(call, props.flReverbDelay); break; + case DSPROPERTY_EAX20LISTENER_REVERB: defer(call, props.lReverb); break; + case DSPROPERTY_EAX20LISTENER_REVERBDELAY: defer(call, props.flReverbDelay); break; + case DSPROPERTY_EAX20LISTENER_ENVIRONMENT: defer(call, props, props.dwEnvironment); break; + case DSPROPERTY_EAX20LISTENER_ENVIRONMENTSIZE: defer(call, props, props.flEnvironmentSize); break; + case DSPROPERTY_EAX20LISTENER_ENVIRONMENTDIFFUSION: defer(call, props.flEnvironmentDiffusion); break; + case DSPROPERTY_EAX20LISTENER_AIRABSORPTIONHF: defer(call, props.flAirAbsorptionHF); break; + case DSPROPERTY_EAX20LISTENER_FLAGS: defer(call, props.dwFlags); break; + default: fail_unknown_property_id(); } } @@ -1369,117 +1194,34 @@ void EaxReverbCommitter::Set(const EaxCall &call, EAXREVERBPROPERTIES &props) { switch(call.get_property_id()) { - case EAXREVERB_NONE: - break; - - case EAXREVERB_ALLPARAMETERS: - defer(call, props); - break; - - case EAXREVERB_ENVIRONMENT: - defer(call, props, props.ulEnvironment); - break; - - case EAXREVERB_ENVIRONMENTSIZE: - defer(call, props, props.flEnvironmentSize); - break; - - case EAXREVERB_ENVIRONMENTDIFFUSION: - defer3(call, props, props.flEnvironmentDiffusion); - break; - - case EAXREVERB_ROOM: - defer3(call, props, props.lRoom); - break; - - case EAXREVERB_ROOMHF: - defer3(call, props, props.lRoomHF); - break; - - case EAXREVERB_ROOMLF: - defer3(call, props, props.lRoomLF); - break; - - case EAXREVERB_DECAYTIME: - defer3(call, props, props.flDecayTime); - break; - - case EAXREVERB_DECAYHFRATIO: - defer3(call, props, props.flDecayHFRatio); - break; - - case EAXREVERB_DECAYLFRATIO: - defer3(call, props, props.flDecayLFRatio); - break; - - case EAXREVERB_REFLECTIONS: - defer3(call, props, props.lReflections); - break; - - case EAXREVERB_REFLECTIONSDELAY: - defer3(call, props, props.flReflectionsDelay); - break; - - case EAXREVERB_REFLECTIONSPAN: - defer3(call, props, props.vReflectionsPan); - break; - - case EAXREVERB_REVERB: - defer3(call, props, props.lReverb); - break; - - case EAXREVERB_REVERBDELAY: - defer3(call, props, props.flReverbDelay); - break; - - case EAXREVERB_REVERBPAN: - defer3(call, props, props.vReverbPan); - break; - - case EAXREVERB_ECHOTIME: - defer3(call, props, props.flEchoTime); - break; - - case EAXREVERB_ECHODEPTH: - defer3(call, props, props.flEchoDepth); - break; - - case EAXREVERB_MODULATIONTIME: - defer3(call, props, props.flModulationTime); - break; - - case EAXREVERB_MODULATIONDEPTH: - defer3(call, props, props.flModulationDepth); - break; - - case EAXREVERB_AIRABSORPTIONHF: - defer3(call, props, props.flAirAbsorptionHF); - break; - - case EAXREVERB_HFREFERENCE: - defer3(call, props, props.flHFReference); - break; - - case EAXREVERB_LFREFERENCE: - defer3(call, props, props.flLFReference); - break; - - case EAXREVERB_ROOMROLLOFFFACTOR: - defer3(call, props, props.flRoomRolloffFactor); - break; - - case EAXREVERB_FLAGS: - defer3(call, props, props.ulFlags); - break; - - default: - fail_unknown_property_id(); + case EAXREVERB_NONE: break; + case EAXREVERB_ALLPARAMETERS: defer(call, props); break; + case EAXREVERB_ENVIRONMENT: defer(call, props, props.ulEnvironment); break; + case EAXREVERB_ENVIRONMENTSIZE: defer(call, props, props.flEnvironmentSize); break; + case EAXREVERB_ENVIRONMENTDIFFUSION: defer3(call, props, props.flEnvironmentDiffusion); break; + case EAXREVERB_ROOM: defer3(call, props, props.lRoom); break; + case EAXREVERB_ROOMHF: defer3(call, props, props.lRoomHF); break; + case EAXREVERB_ROOMLF: defer3(call, props, props.lRoomLF); break; + case EAXREVERB_DECAYTIME: defer3(call, props, props.flDecayTime); break; + case EAXREVERB_DECAYHFRATIO: defer3(call, props, props.flDecayHFRatio); break; + case EAXREVERB_DECAYLFRATIO: defer3(call, props, props.flDecayLFRatio); break; + case EAXREVERB_REFLECTIONS: defer3(call, props, props.lReflections); break; + case EAXREVERB_REFLECTIONSDELAY: defer3(call, props, props.flReflectionsDelay); break; + case EAXREVERB_REFLECTIONSPAN: defer3(call, props, props.vReflectionsPan); break; + case EAXREVERB_REVERB: defer3(call, props, props.lReverb); break; + case EAXREVERB_REVERBDELAY: defer3(call, props, props.flReverbDelay); break; + case EAXREVERB_REVERBPAN: defer3(call, props, props.vReverbPan); break; + case EAXREVERB_ECHOTIME: defer3(call, props, props.flEchoTime); break; + case EAXREVERB_ECHODEPTH: defer3(call, props, props.flEchoDepth); break; + case EAXREVERB_MODULATIONTIME: defer3(call, props, props.flModulationTime); break; + case EAXREVERB_MODULATIONDEPTH: defer3(call, props, props.flModulationDepth); break; + case EAXREVERB_AIRABSORPTIONHF: defer3(call, props, props.flAirAbsorptionHF); break; + case EAXREVERB_HFREFERENCE: defer3(call, props, props.flHFReference); break; + case EAXREVERB_LFREFERENCE: defer3(call, props, props.flLFReference); break; + case EAXREVERB_ROOMROLLOFFFACTOR: defer3(call, props, props.flRoomRolloffFactor); break; + case EAXREVERB_FLAGS: defer3(call, props, props.ulFlags); break; + default: fail_unknown_property_id(); } } -void EaxReverbCommitter::Set(const EaxCall &call, EaxEffectProps &props) -{ - Set(call, std::get(props)); -} - #endif // ALSOFT_EAX diff --git a/3rdparty/openal/al/effects/vmorpher.cpp b/3rdparty/openal/al/effects/vmorpher.cpp index f255122910ba..982bc7e4a63c 100644 --- a/3rdparty/openal/al/effects/vmorpher.cpp +++ b/3rdparty/openal/al/effects/vmorpher.cpp @@ -7,12 +7,13 @@ #include "AL/al.h" #include "AL/efx.h" -#include "alc/effects/base.h" +#include "alc/context.h" +#include "alnumeric.h" #include "effects.h" -#ifdef ALSOFT_EAX +#if ALSOFT_EAX #include -#include "alnumeric.h" +#include "al/eax/effect.h" #include "al/eax/exception.h" #include "al/eax/utils.h" #endif // ALSOFT_EAX @@ -20,7 +21,7 @@ namespace { -std::optional PhenomeFromEnum(ALenum val) +constexpr std::optional PhenomeFromEnum(ALenum val) noexcept { #define HANDLE_PHENOME(x) case AL_VOCAL_MORPHER_PHONEME_ ## x: \ return VMorpherPhenome::x @@ -60,7 +61,7 @@ std::optional PhenomeFromEnum(ALenum val) return std::nullopt; #undef HANDLE_PHENOME } -ALenum EnumFromPhenome(VMorpherPhenome phenome) +constexpr ALenum EnumFromPhenome(VMorpherPhenome phenome) { #define HANDLE_PHENOME(x) case VMorpherPhenome::x: return AL_VOCAL_MORPHER_PHONEME_ ## x switch(phenome) @@ -96,11 +97,11 @@ ALenum EnumFromPhenome(VMorpherPhenome phenome) HANDLE_PHENOME(V); HANDLE_PHENOME(Z); } - throw std::runtime_error{"Invalid phenome: "+std::to_string(static_cast(phenome))}; + throw std::runtime_error{fmt::format("Invalid phenome: {}", int{al::to_underlying(phenome)})}; #undef HANDLE_PHENOME } -std::optional WaveformFromEmum(ALenum value) +constexpr std::optional WaveformFromEmum(ALenum value) noexcept { switch(value) { @@ -110,7 +111,7 @@ std::optional WaveformFromEmum(ALenum value) } return std::nullopt; } -ALenum EnumFromWaveform(VMorpherWaveform type) +constexpr ALenum EnumFromWaveform(VMorpherWaveform type) { switch(type) { @@ -118,144 +119,122 @@ ALenum EnumFromWaveform(VMorpherWaveform type) case VMorpherWaveform::Triangle: return AL_VOCAL_MORPHER_WAVEFORM_TRIANGLE; case VMorpherWaveform::Sawtooth: return AL_VOCAL_MORPHER_WAVEFORM_SAWTOOTH; } - throw std::runtime_error{"Invalid vocal morpher waveform: " + - std::to_string(static_cast(type))}; + throw std::runtime_error{fmt::format("Invalid vocal morpher waveform: {}", + int{al::to_underlying(type)})}; } -void Vmorpher_setParami(EffectProps *props, ALenum param, int val) +constexpr EffectProps genDefaultProps() noexcept +{ + VmorpherProps props{}; + props.Rate = AL_VOCAL_MORPHER_DEFAULT_RATE; + props.PhonemeA = PhenomeFromEnum(AL_VOCAL_MORPHER_DEFAULT_PHONEMEA).value(); + props.PhonemeB = PhenomeFromEnum(AL_VOCAL_MORPHER_DEFAULT_PHONEMEB).value(); + props.PhonemeACoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEA_COARSE_TUNING; + props.PhonemeBCoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEB_COARSE_TUNING; + props.Waveform = WaveformFromEmum(AL_VOCAL_MORPHER_DEFAULT_WAVEFORM).value(); + return props; +} + +} // namespace + +const EffectProps VmorpherEffectProps{genDefaultProps()}; + +void VmorpherEffectHandler::SetParami(ALCcontext *context, VmorpherProps &props, ALenum param, int val) { switch(param) { case AL_VOCAL_MORPHER_PHONEMEA: if(auto phenomeopt = PhenomeFromEnum(val)) - props->Vmorpher.PhonemeA = *phenomeopt; + props.PhonemeA = *phenomeopt; else - throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-a out of range: 0x%04x", val}; - break; + context->throw_error(AL_INVALID_VALUE, + "Vocal morpher phoneme-a out of range: {:#04x}", as_unsigned(val)); + return; case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING: if(!(val >= AL_VOCAL_MORPHER_MIN_PHONEMEA_COARSE_TUNING && val <= AL_VOCAL_MORPHER_MAX_PHONEMEA_COARSE_TUNING)) - throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-a coarse tuning out of range"}; - props->Vmorpher.PhonemeACoarseTuning = val; - break; + context->throw_error(AL_INVALID_VALUE, + "Vocal morpher phoneme-a coarse tuning out of range"); + props.PhonemeACoarseTuning = val; + return; case AL_VOCAL_MORPHER_PHONEMEB: if(auto phenomeopt = PhenomeFromEnum(val)) - props->Vmorpher.PhonemeB = *phenomeopt; + props.PhonemeB = *phenomeopt; else - throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-b out of range: 0x%04x", val}; - break; + context->throw_error(AL_INVALID_VALUE, + "Vocal morpher phoneme-b out of range: {:#04x}", as_unsigned(val)); + return; case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING: if(!(val >= AL_VOCAL_MORPHER_MIN_PHONEMEB_COARSE_TUNING && val <= AL_VOCAL_MORPHER_MAX_PHONEMEB_COARSE_TUNING)) - throw effect_exception{AL_INVALID_VALUE, "Vocal morpher phoneme-b coarse tuning out of range"}; - props->Vmorpher.PhonemeBCoarseTuning = val; - break; + context->throw_error(AL_INVALID_VALUE, + "Vocal morpher phoneme-b coarse tuning out of range"); + props.PhonemeBCoarseTuning = val; + return; case AL_VOCAL_MORPHER_WAVEFORM: if(auto formopt = WaveformFromEmum(val)) - props->Vmorpher.Waveform = *formopt; + props.Waveform = *formopt; else - throw effect_exception{AL_INVALID_VALUE, "Vocal morpher waveform out of range: 0x%04x", val}; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer property 0x%04x", - param}; + context->throw_error(AL_INVALID_VALUE, "Vocal morpher waveform out of range: {:#04x}", + as_unsigned(val)); + return; } + + context->throw_error(AL_INVALID_ENUM, "Invalid vocal morpher integer property {:#04x}", + as_unsigned(param)); } -void Vmorpher_setParamiv(EffectProps*, ALenum param, const int*) -{ - throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer-vector property 0x%04x", - param}; -} -void Vmorpher_setParamf(EffectProps *props, ALenum param, float val) +void VmorpherEffectHandler::SetParamiv(ALCcontext *context, VmorpherProps &props, ALenum param, const int *vals) +{ SetParami(context, props, param, *vals); } +void VmorpherEffectHandler::SetParamf(ALCcontext *context, VmorpherProps &props, ALenum param, float val) { switch(param) { case AL_VOCAL_MORPHER_RATE: if(!(val >= AL_VOCAL_MORPHER_MIN_RATE && val <= AL_VOCAL_MORPHER_MAX_RATE)) - throw effect_exception{AL_INVALID_VALUE, "Vocal morpher rate out of range"}; - props->Vmorpher.Rate = val; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher float property 0x%04x", - param}; + context->throw_error(AL_INVALID_VALUE, "Vocal morpher rate out of range"); + props.Rate = val; + return; } + + context->throw_error(AL_INVALID_ENUM, "Invalid vocal morpher float property {:#04x}", + as_unsigned(param)); } -void Vmorpher_setParamfv(EffectProps *props, ALenum param, const float *vals) -{ Vmorpher_setParamf(props, param, vals[0]); } +void VmorpherEffectHandler::SetParamfv(ALCcontext *context, VmorpherProps &props, ALenum param, const float *vals) +{ SetParamf(context, props, param, *vals); } -void Vmorpher_getParami(const EffectProps *props, ALenum param, int* val) +void VmorpherEffectHandler::GetParami(ALCcontext *context, const VmorpherProps &props, ALenum param, int* val) { switch(param) { - case AL_VOCAL_MORPHER_PHONEMEA: - *val = EnumFromPhenome(props->Vmorpher.PhonemeA); - break; - - case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING: - *val = props->Vmorpher.PhonemeACoarseTuning; - break; - - case AL_VOCAL_MORPHER_PHONEMEB: - *val = EnumFromPhenome(props->Vmorpher.PhonemeB); - break; - - case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING: - *val = props->Vmorpher.PhonemeBCoarseTuning; - break; - - case AL_VOCAL_MORPHER_WAVEFORM: - *val = EnumFromWaveform(props->Vmorpher.Waveform); - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer property 0x%04x", - param}; + case AL_VOCAL_MORPHER_PHONEMEA: *val = EnumFromPhenome(props.PhonemeA); return; + case AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING: *val = props.PhonemeACoarseTuning; return; + case AL_VOCAL_MORPHER_PHONEMEB: *val = EnumFromPhenome(props.PhonemeB); return; + case AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING: *val = props.PhonemeBCoarseTuning; return; + case AL_VOCAL_MORPHER_WAVEFORM: *val = EnumFromWaveform(props.Waveform); return; } + + context->throw_error(AL_INVALID_ENUM, "Invalid vocal morpher integer property {:#04x}", + as_unsigned(param)); } -void Vmorpher_getParamiv(const EffectProps*, ALenum param, int*) -{ - throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher integer-vector property 0x%04x", - param}; -} -void Vmorpher_getParamf(const EffectProps *props, ALenum param, float *val) +void VmorpherEffectHandler::GetParamiv(ALCcontext *context, const VmorpherProps &props, ALenum param, int *vals) +{ GetParami(context, props, param, vals); } +void VmorpherEffectHandler::GetParamf(ALCcontext *context, const VmorpherProps &props, ALenum param, float *val) { switch(param) { - case AL_VOCAL_MORPHER_RATE: - *val = props->Vmorpher.Rate; - break; - - default: - throw effect_exception{AL_INVALID_ENUM, "Invalid vocal morpher float property 0x%04x", - param}; + case AL_VOCAL_MORPHER_RATE: *val = props.Rate; return; } -} -void Vmorpher_getParamfv(const EffectProps *props, ALenum param, float *vals) -{ Vmorpher_getParamf(props, param, vals); } -EffectProps genDefaultProps() noexcept -{ - EffectProps props{}; - props.Vmorpher.Rate = AL_VOCAL_MORPHER_DEFAULT_RATE; - props.Vmorpher.PhonemeA = *PhenomeFromEnum(AL_VOCAL_MORPHER_DEFAULT_PHONEMEA); - props.Vmorpher.PhonemeB = *PhenomeFromEnum(AL_VOCAL_MORPHER_DEFAULT_PHONEMEB); - props.Vmorpher.PhonemeACoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEA_COARSE_TUNING; - props.Vmorpher.PhonemeBCoarseTuning = AL_VOCAL_MORPHER_DEFAULT_PHONEMEB_COARSE_TUNING; - props.Vmorpher.Waveform = *WaveformFromEmum(AL_VOCAL_MORPHER_DEFAULT_WAVEFORM); - return props; + context->throw_error(AL_INVALID_ENUM, "Invalid vocal morpher float property {:#04x}", + as_unsigned(param)); } +void VmorpherEffectHandler::GetParamfv(ALCcontext *context, const VmorpherProps &props, ALenum param, float *vals) +{ GetParamf(context, props, param, vals); } -} // namespace - -DEFINE_ALEFFECT_VTABLE(Vmorpher); - -const EffectProps VmorpherEffectProps{genDefaultProps()}; -#ifdef ALSOFT_EAX +#if ALSOFT_EAX namespace { using VocalMorpherCommitter = EaxCommitter; @@ -352,10 +331,9 @@ template<> throw Exception{message}; } -template<> -bool VocalMorpherCommitter::commit(const EaxEffectProps &props) +bool EaxVocalMorpherCommitter::commit(const EAXVOCALMORPHERPROPERTIES &props) { - if(props == mEaxProps) + if(auto *cur = std::get_if(&mEaxProps); cur && *cur == props) return false; mEaxProps = props; @@ -407,19 +385,21 @@ bool VocalMorpherCommitter::commit(const EaxEffectProps &props) return VMorpherWaveform::Sinusoid; }; - auto &eaxprops = std::get(props); - mAlProps.Vmorpher.PhonemeA = get_phoneme(eaxprops.ulPhonemeA); - mAlProps.Vmorpher.PhonemeACoarseTuning = static_cast(eaxprops.lPhonemeACoarseTuning); - mAlProps.Vmorpher.PhonemeB = get_phoneme(eaxprops.ulPhonemeB); - mAlProps.Vmorpher.PhonemeBCoarseTuning = static_cast(eaxprops.lPhonemeBCoarseTuning); - mAlProps.Vmorpher.Waveform = get_waveform(eaxprops.ulWaveform); - mAlProps.Vmorpher.Rate = eaxprops.flRate; + mAlProps = [&]{ + VmorpherProps ret{}; + ret.PhonemeA = get_phoneme(props.ulPhonemeA); + ret.PhonemeACoarseTuning = static_cast(props.lPhonemeACoarseTuning); + ret.PhonemeB = get_phoneme(props.ulPhonemeB); + ret.PhonemeBCoarseTuning = static_cast(props.lPhonemeBCoarseTuning); + ret.Waveform = get_waveform(props.ulWaveform); + ret.Rate = props.flRate; + return ret; + }(); return true; } -template<> -void VocalMorpherCommitter::SetDefaults(EaxEffectProps &props) +void EaxVocalMorpherCommitter::SetDefaults(EaxEffectProps &props) { static constexpr EAXVOCALMORPHERPROPERTIES defprops{[] { @@ -435,87 +415,35 @@ void VocalMorpherCommitter::SetDefaults(EaxEffectProps &props) props = defprops; } -template<> -void VocalMorpherCommitter::Get(const EaxCall &call, const EaxEffectProps &props_) +void EaxVocalMorpherCommitter::Get(const EaxCall &call, const EAXVOCALMORPHERPROPERTIES &props) { - auto &props = std::get(props_); switch(call.get_property_id()) { - case EAXVOCALMORPHER_NONE: - break; - - case EAXVOCALMORPHER_ALLPARAMETERS: - call.set_value(props); - break; - - case EAXVOCALMORPHER_PHONEMEA: - call.set_value(props.ulPhonemeA); - break; - - case EAXVOCALMORPHER_PHONEMEACOARSETUNING: - call.set_value(props.lPhonemeACoarseTuning); - break; - - case EAXVOCALMORPHER_PHONEMEB: - call.set_value(props.ulPhonemeB); - break; - - case EAXVOCALMORPHER_PHONEMEBCOARSETUNING: - call.set_value(props.lPhonemeBCoarseTuning); - break; - - case EAXVOCALMORPHER_WAVEFORM: - call.set_value(props.ulWaveform); - break; - - case EAXVOCALMORPHER_RATE: - call.set_value(props.flRate); - break; - - default: - fail_unknown_property_id(); + case EAXVOCALMORPHER_NONE: break; + case EAXVOCALMORPHER_ALLPARAMETERS: call.set_value(props); break; + case EAXVOCALMORPHER_PHONEMEA: call.set_value(props.ulPhonemeA); break; + case EAXVOCALMORPHER_PHONEMEACOARSETUNING: call.set_value(props.lPhonemeACoarseTuning); break; + case EAXVOCALMORPHER_PHONEMEB: call.set_value(props.ulPhonemeB); break; + case EAXVOCALMORPHER_PHONEMEBCOARSETUNING: call.set_value(props.lPhonemeBCoarseTuning); break; + case EAXVOCALMORPHER_WAVEFORM: call.set_value(props.ulWaveform); break; + case EAXVOCALMORPHER_RATE: call.set_value(props.flRate); break; + default: fail_unknown_property_id(); } } -template<> -void VocalMorpherCommitter::Set(const EaxCall &call, EaxEffectProps &props_) +void EaxVocalMorpherCommitter::Set(const EaxCall &call, EAXVOCALMORPHERPROPERTIES &props) { - auto &props = std::get(props_); switch(call.get_property_id()) { - case EAXVOCALMORPHER_NONE: - break; - - case EAXVOCALMORPHER_ALLPARAMETERS: - defer(call, props); - break; - - case EAXVOCALMORPHER_PHONEMEA: - defer(call, props.ulPhonemeA); - break; - - case EAXVOCALMORPHER_PHONEMEACOARSETUNING: - defer(call, props.lPhonemeACoarseTuning); - break; - - case EAXVOCALMORPHER_PHONEMEB: - defer(call, props.ulPhonemeB); - break; - - case EAXVOCALMORPHER_PHONEMEBCOARSETUNING: - defer(call, props.lPhonemeBCoarseTuning); - break; - - case EAXVOCALMORPHER_WAVEFORM: - defer(call, props.ulWaveform); - break; - - case EAXVOCALMORPHER_RATE: - defer(call, props.flRate); - break; - - default: - fail_unknown_property_id(); + case EAXVOCALMORPHER_NONE: break; + case EAXVOCALMORPHER_ALLPARAMETERS: defer(call, props); break; + case EAXVOCALMORPHER_PHONEMEA: defer(call, props.ulPhonemeA); break; + case EAXVOCALMORPHER_PHONEMEACOARSETUNING: defer(call, props.lPhonemeACoarseTuning); break; + case EAXVOCALMORPHER_PHONEMEB: defer(call, props.ulPhonemeB); break; + case EAXVOCALMORPHER_PHONEMEBCOARSETUNING: defer(call, props.lPhonemeBCoarseTuning); break; + case EAXVOCALMORPHER_WAVEFORM: defer(call, props.ulWaveform); break; + case EAXVOCALMORPHER_RATE: defer(call, props.flRate); break; + default: fail_unknown_property_id(); } } diff --git a/3rdparty/openal/al/error.cpp b/3rdparty/openal/al/error.cpp index c23594770b18..4d2015ce2bd6 100644 --- a/3rdparty/openal/al/error.cpp +++ b/3rdparty/openal/al/error.cpp @@ -25,15 +25,14 @@ #include #endif -#include #include #include #include #include #include -#include -#include -#include +#include +#include +#include #include "AL/al.h" #include "AL/alc.h" @@ -41,43 +40,19 @@ #include "al/debug.h" #include "alc/alconfig.h" #include "alc/context.h" -#include "almalloc.h" -#include "alstring.h" +#include "alnumeric.h" #include "core/except.h" #include "core/logging.h" -#include "direct_defs.h" #include "opthelpers.h" #include "strutils.h" -bool TrapALError{false}; - -void ALCcontext::setError(ALenum errorCode, const char *msg, ...) +void ALCcontext::setErrorImpl(ALenum errorCode, const fmt::string_view fmt, fmt::format_args args) { - auto message = std::vector(256); - - va_list args, args2; - va_start(args, msg); - va_copy(args2, args); - int msglen{std::vsnprintf(message.data(), message.size(), msg, args)}; - if(msglen >= 0 && static_cast(msglen) >= message.size()) - { - message.resize(static_cast(msglen) + 1u); - msglen = std::vsnprintf(message.data(), message.size(), msg, args2); - } - va_end(args2); - va_end(args); - - if(msglen >= 0) - msg = message.data(); - else - { - msg = ""; - msglen = static_cast(strlen(msg)); - } + const auto msg = fmt::vformat(fmt, std::move(args)); - WARN("Error generated on context %p, code 0x%04x, \"%s\"\n", - decltype(std::declval()){this}, errorCode, msg); + WARN("Error generated on context {}, code {:#04x}, \"{}\"", + decltype(std::declval()){this}, as_unsigned(errorCode), msg); if(TrapALError) { #ifdef _WIN32 @@ -89,54 +64,66 @@ void ALCcontext::setError(ALenum errorCode, const char *msg, ...) #endif } - ALenum curerr{AL_NO_ERROR}; - mLastError.compare_exchange_strong(curerr, errorCode); + if(mLastThreadError.get() == AL_NO_ERROR) + mLastThreadError.set(errorCode); - debugMessage(DebugSource::API, DebugType::Error, 0, DebugSeverity::High, - {msg, static_cast(msglen)}); + debugMessage(DebugSource::API, DebugType::Error, static_cast(errorCode), + DebugSeverity::High, msg); +} + +void ALCcontext::throw_error_impl(ALenum errorCode, const fmt::string_view fmt, + fmt::format_args args) +{ + setErrorImpl(errorCode, fmt, std::move(args)); + throw al::base_exception{}; } + /* Special-case alGetError since it (potentially) raises a debug signal and * returns a non-default value for a null context. */ -AL_API ALenum AL_APIENTRY alGetError(void) noexcept +AL_API auto AL_APIENTRY alGetError() noexcept -> ALenum { - auto context = GetContextRef(); - if(!context) UNLIKELY + if(auto context = GetContextRef()) LIKELY + return alGetErrorDirect(context.get()); + + auto get_value = [](const char *envname, const char *optname) -> ALenum { - static const ALenum deferror{[](const char *envname, const char *optname) -> ALenum + auto optstr = al::getenv(envname); + if(!optstr) + optstr = ConfigValueStr({}, "game_compat", optname); + if(optstr) { - auto optstr = al::getenv(envname); - if(!optstr) - optstr = ConfigValueStr(nullptr, "game_compat", optname); - - if(optstr) - { - char *end{}; - auto value = std::strtoul(optstr->c_str(), &end, 0); - if(end && *end == '\0' && value <= std::numeric_limits::max()) + try { + auto idx = 0_uz; + auto value = std::stoi(*optstr, &idx, 0); + if(idx >= optstr->size() || std::isspace(optstr->at(idx))) return static_cast(value); - ERR("Invalid default error value: \"%s\"", optstr->c_str()); + } catch(...) { } - return AL_INVALID_OPERATION; - }("__ALSOFT_DEFAULT_ERROR", "default-error")}; + ERR("Invalid default error value: \"{}\"", *optstr); + } + return AL_INVALID_OPERATION; + }; + static const ALenum deferror{get_value("__ALSOFT_DEFAULT_ERROR", "default-error")}; - WARN("Querying error state on null context (implicitly 0x%04x)\n", deferror); - if(TrapALError) - { + WARN("Querying error state on null context (implicitly {:#04x})", as_unsigned(deferror)); + if(TrapALError) + { #ifdef _WIN32 - if(IsDebuggerPresent()) - DebugBreak(); + if(IsDebuggerPresent()) + DebugBreak(); #elif defined(SIGTRAP) - raise(SIGTRAP); + raise(SIGTRAP); #endif - } - return deferror; } - return alGetErrorDirect(context.get()); + return deferror; } FORCE_ALIGN ALenum AL_APIENTRY alGetErrorDirect(ALCcontext *context) noexcept { - return context->mLastError.exchange(AL_NO_ERROR); + ALenum ret{context->mLastThreadError.get()}; + if(ret != AL_NO_ERROR) UNLIKELY + context->mLastThreadError.set(AL_NO_ERROR); + return ret; } diff --git a/3rdparty/openal/al/event.cpp b/3rdparty/openal/al/event.cpp index 8b76ceffde8f..9a8d50b69bf9 100644 --- a/3rdparty/openal/al/event.cpp +++ b/3rdparty/openal/al/event.cpp @@ -3,32 +3,35 @@ #include "event.h" -#include #include -#include +#include #include #include #include #include #include #include -#include #include -#include +#include +#include #include "AL/al.h" #include "AL/alc.h" +#include "AL/alext.h" #include "alc/context.h" -#include "alc/effects/base.h" -#include "alc/inprogext.h" -#include "almalloc.h" +#include "alnumeric.h" +#include "alsem.h" +#include "alspan.h" +#include "alstring.h" #include "core/async_event.h" +#include "core/context.h" +#include "core/effects/base.h" #include "core/except.h" #include "core/logging.h" -#include "core/voice_change.h" #include "debug.h" #include "direct_defs.h" +#include "intrusive_ptr.h" #include "opthelpers.h" #include "ringbuffer.h" @@ -47,27 +50,22 @@ int EventThread(ALCcontext *context) bool quitnow{false}; while(!quitnow) { - auto evt_data = ring->getReadVector().first; + auto evt_data = ring->getReadVector()[0]; if(evt_data.len == 0) { context->mEventSem.wait(); continue; } - std::lock_guard _{context->mEventCbLock}; - do { - auto *evt_ptr = std::launder(reinterpret_cast(evt_data.buf)); - evt_data.buf += sizeof(AsyncEvent); - evt_data.len -= 1; - - AsyncEvent event{std::move(*evt_ptr)}; - std::destroy_at(evt_ptr); - ring->readAdvance(1); - + auto eventlock = std::lock_guard{context->mEventCbLock}; + const auto enabledevts = context->mEnabledEvts.load(std::memory_order_acquire); + auto evt_span = al::span{std::launder(reinterpret_cast(evt_data.buf)), + evt_data.len}; + for(auto &event : evt_span) + { quitnow = std::holds_alternative(event); if(quitnow) UNLIKELY break; - auto enabledevts = context->mEnabledEvts.load(std::memory_order_acquire); auto proc_killthread = [](AsyncKillThread&) { }; auto proc_release = [](AsyncEffectReleaseEvent &evt) { @@ -102,7 +100,7 @@ int EventThread(ALCcontext *context) break; } context->mEventCb(AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT, evt.mId, state, - static_cast(msg.length()), msg.c_str(), context->mEventParam); + al::sizei(msg), msg.c_str(), context->mEventParam); }; auto proc_buffercomp = [context,enabledevts](AsyncBufferCompleteEvent &evt) { @@ -114,25 +112,23 @@ int EventThread(ALCcontext *context) if(evt.mCount == 1) msg += " buffer completed"; else msg += " buffers completed"; context->mEventCb(AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT, evt.mId, evt.mCount, - static_cast(msg.length()), msg.c_str(), context->mEventParam); + al::sizei(msg), msg.c_str(), context->mEventParam); }; auto proc_disconnect = [context,enabledevts](AsyncDisconnectEvent &evt) { - const std::string_view message{evt.msg}; - - context->debugMessage(DebugSource::System, DebugType::Error, 0, - DebugSeverity::High, message); + if(!context->mEventCb + || !enabledevts.test(al::to_underlying(AsyncEnableBits::Disconnected))) + return; - if(context->mEventCb - && enabledevts.test(al::to_underlying(AsyncEnableBits::Disconnected))) - context->mEventCb(AL_EVENT_TYPE_DISCONNECTED_SOFT, 0, 0, - static_cast(message.length()), message.data(), - context->mEventParam); + context->mEventCb(AL_EVENT_TYPE_DISCONNECTED_SOFT, 0, 0, al::sizei(evt.msg), + evt.msg.c_str(), context->mEventParam); }; std::visit(overloaded{proc_srcstate, proc_buffercomp, proc_release, proc_disconnect, proc_killthread}, event); - } while(evt_data.len != 0); + } + std::destroy(evt_span.begin(), evt_span.end()); + ring->readAdvance(evt_span.size()); } return 0; } @@ -157,22 +153,22 @@ void StartEventThrd(ALCcontext *ctx) ctx->mEventThread = std::thread{EventThread, ctx}; } catch(std::exception& e) { - ERR("Failed to start event thread: %s\n", e.what()); + ERR("Failed to start event thread: {}", e.what()); } catch(...) { - ERR("Failed to start event thread! Expect problems.\n"); + ERR("Failed to start event thread! Expect problems."); } } void StopEventThrd(ALCcontext *ctx) { RingBuffer *ring{ctx->mAsyncEvents.get()}; - auto evt_data = ring->getWriteVector().first; + auto evt_data = ring->getWriteVector()[0]; if(evt_data.len == 0) { do { std::this_thread::yield(); - evt_data = ring->getWriteVector().first; + evt_data = ring->getWriteVector()[0]; } while(evt_data.len == 0); } std::ignore = InitAsyncEvent(evt_data.buf); @@ -183,20 +179,24 @@ void StopEventThrd(ALCcontext *ctx) ctx->mEventThread.join(); } -AL_API DECL_FUNCEXT3(void, alEventControl,SOFT, ALsizei, const ALenum*, ALboolean) +AL_API DECL_FUNCEXT3(void, alEventControl,SOFT, ALsizei,count, const ALenum*,types, ALboolean,enable) FORCE_ALIGN void AL_APIENTRY alEventControlDirectSOFT(ALCcontext *context, ALsizei count, const ALenum *types, ALboolean enable) noexcept -{ - if(count < 0) context->setError(AL_INVALID_VALUE, "Controlling %d events", count); - if(count <= 0) return; - if(!types) return context->setError(AL_INVALID_VALUE, "NULL pointer"); +try { + if(count < 0) + context->throw_error(AL_INVALID_VALUE, "Controlling {} events", count); + if(count <= 0) UNLIKELY return; + + if(!types) + context->throw_error(AL_INVALID_VALUE, "NULL pointer"); ContextBase::AsyncEventBitset flags{}; for(ALenum evttype : al::span{types, static_cast(count)}) { auto etype = GetEventType(evttype); if(!etype) - return context->setError(AL_INVALID_ENUM, "Invalid event type 0x%04x", evttype); + context->throw_error(AL_INVALID_ENUM, "Invalid event type {:#04x}", + as_unsigned(evttype)); flags.set(al::to_underlying(*etype)); } @@ -221,15 +221,25 @@ FORCE_ALIGN void AL_APIENTRY alEventControlDirectSOFT(ALCcontext *context, ALsiz /* Wait to ensure the event handler sees the changed flags before * returning. */ - std::lock_guard _{context->mEventCbLock}; + std::lock_guard eventlock{context->mEventCbLock}; } } +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); +} -AL_API DECL_FUNCEXT2(void, alEventCallback,SOFT, ALEVENTPROCSOFT, void*) +AL_API DECL_FUNCEXT2(void, alEventCallback,SOFT, ALEVENTPROCSOFT,callback, void*,userParam) FORCE_ALIGN void AL_APIENTRY alEventCallbackDirectSOFT(ALCcontext *context, ALEVENTPROCSOFT callback, void *userParam) noexcept -{ - std::lock_guard _{context->mEventCbLock}; +try { + std::lock_guard eventlock{context->mEventCbLock}; context->mEventCb = callback; context->mEventParam = userParam; } +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); +} diff --git a/3rdparty/openal/al/extension.cpp b/3rdparty/openal/al/extension.cpp index 6d1ac3275b61..061addc84625 100644 --- a/3rdparty/openal/al/extension.cpp +++ b/3rdparty/openal/al/extension.cpp @@ -20,21 +20,18 @@ #include "config.h" -#include -#include -#include +#include #include "AL/al.h" #include "AL/alc.h" #include "alc/context.h" #include "alstring.h" -#include "core/except.h" #include "direct_defs.h" #include "opthelpers.h" -AL_API DECL_FUNC1(ALboolean, alIsExtensionPresent, const ALchar*) +AL_API DECL_FUNC1(ALboolean, alIsExtensionPresent, const ALchar*,extName) FORCE_ALIGN ALboolean AL_APIENTRY alIsExtensionPresentDirect(ALCcontext *context, const ALchar *extName) noexcept { if(!extName) UNLIKELY @@ -43,10 +40,10 @@ FORCE_ALIGN ALboolean AL_APIENTRY alIsExtensionPresentDirect(ALCcontext *context return AL_FALSE; } - size_t len{strlen(extName)}; + const std::string_view tofind{extName}; for(std::string_view ext : context->mExtensions) { - if(len == ext.length() && al::strncasecmp(ext.data(), extName, len) == 0) + if(al::case_compare(ext, tofind) == 0) return AL_TRUE; } @@ -68,12 +65,12 @@ FORCE_ALIGN ALvoid* AL_APIENTRY alGetProcAddressDirect(ALCcontext*, const ALchar AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *enumName) noexcept { - if(!enumName) return static_cast(0); + if(!enumName) return ALenum{0}; return alcGetEnumValue(nullptr, enumName); } FORCE_ALIGN ALenum AL_APIENTRY alGetEnumValueDirect(ALCcontext*, const ALchar *enumName) noexcept { - if(!enumName) return static_cast(0); + if(!enumName) return ALenum{0}; return alcGetEnumValue(nullptr, enumName); } diff --git a/3rdparty/openal/al/filter.cpp b/3rdparty/openal/al/filter.cpp index f0a078b7a6fa..938ac48cd65f 100644 --- a/3rdparty/openal/al/filter.cpp +++ b/3rdparty/openal/al/filter.cpp @@ -29,8 +29,8 @@ #include #include #include -#include #include +#include #include #include "AL/al.h" @@ -42,36 +42,17 @@ #include "alc/device.h" #include "almalloc.h" #include "alnumeric.h" +#include "alspan.h" #include "core/except.h" +#include "core/logging.h" #include "direct_defs.h" +#include "intrusive_ptr.h" #include "opthelpers.h" namespace { -class filter_exception final : public al::base_exception { - ALenum mErrorCode; - -public: -#ifdef __USE_MINGW_ANSI_STDIO - [[gnu::format(gnu_printf, 3, 4)]] -#else - [[gnu::format(printf, 3, 4)]] -#endif - filter_exception(ALenum code, const char *msg, ...); - ~filter_exception() override; - - ALenum errorCode() const noexcept { return mErrorCode; } -}; - -filter_exception::filter_exception(ALenum code, const char* msg, ...) : mErrorCode{code} -{ - std::va_list args; - va_start(args, msg); - setMessage(msg, args); - va_end(args); -} -filter_exception::~filter_exception() = default; +using SubListAllocator = al::allocator>; void InitFilterParams(ALfilter *filter, ALenum type) @@ -80,43 +61,44 @@ void InitFilterParams(ALfilter *filter, ALenum type) { filter->Gain = AL_LOWPASS_DEFAULT_GAIN; filter->GainHF = AL_LOWPASS_DEFAULT_GAINHF; - filter->HFReference = LOWPASSFREQREF; + filter->HFReference = LowPassFreqRef; filter->GainLF = 1.0f; - filter->LFReference = HIGHPASSFREQREF; + filter->LFReference = HighPassFreqRef; filter->mTypeVariant.emplace(); } else if(type == AL_FILTER_HIGHPASS) { filter->Gain = AL_HIGHPASS_DEFAULT_GAIN; filter->GainHF = 1.0f; - filter->HFReference = LOWPASSFREQREF; + filter->HFReference = LowPassFreqRef; filter->GainLF = AL_HIGHPASS_DEFAULT_GAINLF; - filter->LFReference = HIGHPASSFREQREF; + filter->LFReference = HighPassFreqRef; filter->mTypeVariant.emplace(); } else if(type == AL_FILTER_BANDPASS) { filter->Gain = AL_BANDPASS_DEFAULT_GAIN; filter->GainHF = AL_BANDPASS_DEFAULT_GAINHF; - filter->HFReference = LOWPASSFREQREF; + filter->HFReference = LowPassFreqRef; filter->GainLF = AL_BANDPASS_DEFAULT_GAINLF; - filter->LFReference = HIGHPASSFREQREF; + filter->LFReference = HighPassFreqRef; filter->mTypeVariant.emplace(); } else { filter->Gain = 1.0f; filter->GainHF = 1.0f; - filter->HFReference = LOWPASSFREQREF; + filter->HFReference = LowPassFreqRef; filter->GainLF = 1.0f; - filter->LFReference = HIGHPASSFREQREF; + filter->LFReference = HighPassFreqRef; filter->mTypeVariant.emplace(); } filter->type = type; } -bool EnsureFilters(ALCdevice *device, size_t needed) -{ +[[nodiscard]] +auto EnsureFilters(al::Device *device, size_t needed) noexcept -> bool +try { size_t count{std::accumulate(device->FilterList.cbegin(), device->FilterList.cend(), 0_uz, [](size_t cur, const FilterSubList &sublist) noexcept -> size_t { return cur + static_cast(al::popcount(sublist.FreeMask)); })}; @@ -126,22 +108,21 @@ bool EnsureFilters(ALCdevice *device, size_t needed) if(device->FilterList.size() >= 1<<25) UNLIKELY return false; - device->FilterList.emplace_back(); - auto sublist = device->FilterList.end() - 1; - sublist->FreeMask = ~0_u64; - sublist->Filters = static_cast(al_calloc(alignof(ALfilter), sizeof(ALfilter)*64)); - if(!sublist->Filters) UNLIKELY - { - device->FilterList.pop_back(); - return false; - } - count += 64; + FilterSubList sublist{}; + sublist.FreeMask = ~0_u64; + sublist.Filters = SubListAllocator{}.allocate(1); + device->FilterList.emplace_back(std::move(sublist)); + count += std::tuple_size_v; } return true; } +catch(...) { + return false; +} -ALfilter *AllocFilter(ALCdevice *device) +[[nodiscard]] +auto AllocFilter(al::Device *device) noexcept -> ALfilter* { auto sublist = std::find_if(device->FilterList.begin(), device->FilterList.end(), [](const FilterSubList &entry) noexcept -> bool @@ -150,7 +131,7 @@ ALfilter *AllocFilter(ALCdevice *device) auto slidx = static_cast(al::countr_zero(sublist->FreeMask)); ASSUME(slidx < 64); - ALfilter *filter{al::construct_at(sublist->Filters + slidx)}; + ALfilter *filter{al::construct_at(al::to_address(sublist->Filters->begin() + slidx))}; InitFilterParams(filter, AL_FILTER_NULL); /* Add 1 to avoid filter ID 0. */ @@ -161,7 +142,7 @@ ALfilter *AllocFilter(ALCdevice *device) return filter; } -void FreeFilter(ALCdevice *device, ALfilter *filter) +void FreeFilter(al::Device *device, ALfilter *filter) { device->mFilterNames.erase(filter->id); @@ -175,7 +156,8 @@ void FreeFilter(ALCdevice *device, ALfilter *filter) } -inline ALfilter *LookupFilter(ALCdevice *device, ALuint id) +[[nodiscard]] +auto LookupFilter(al::Device *device, ALuint id) noexcept -> ALfilter* { const size_t lidx{(id-1) >> 6}; const ALuint slidx{(id-1) & 0x3f}; @@ -185,427 +167,398 @@ inline ALfilter *LookupFilter(ALCdevice *device, ALuint id) FilterSubList &sublist = device->FilterList[lidx]; if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY return nullptr; - return sublist.Filters + slidx; + return al::to_address(sublist.Filters->begin() + slidx); } } // namespace /* Null filter parameter handlers */ template<> -void FilterTable::setParami(ALfilter*, ALenum param, int) -{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } +void FilterTable::setParami(ALCcontext *context, ALfilter*, ALenum param, int) +{ context->throw_error(AL_INVALID_ENUM, "Invalid null filter property {:#04x}", as_unsigned(param)); } template<> -void FilterTable::setParamiv(ALfilter*, ALenum param, const int*) -{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } +void FilterTable::setParamiv(ALCcontext *context, ALfilter*, ALenum param, const int*) +{ context->throw_error(AL_INVALID_ENUM, "Invalid null filter property {:#04x}", as_unsigned(param)); } template<> -void FilterTable::setParamf(ALfilter*, ALenum param, float) -{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } +void FilterTable::setParamf(ALCcontext *context, ALfilter*, ALenum param, float) +{ context->throw_error(AL_INVALID_ENUM, "Invalid null filter property {:#04x}", as_unsigned(param)); } template<> -void FilterTable::setParamfv(ALfilter*, ALenum param, const float*) -{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } +void FilterTable::setParamfv(ALCcontext *context, ALfilter*, ALenum param, const float*) +{ context->throw_error(AL_INVALID_ENUM, "Invalid null filter property {:#04x}", as_unsigned(param)); } template<> -void FilterTable::getParami(const ALfilter*, ALenum param, int*) -{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } +void FilterTable::getParami(ALCcontext *context, const ALfilter*, ALenum param, int*) +{ context->throw_error(AL_INVALID_ENUM, "Invalid null filter property {:#04x}", as_unsigned(param)); } template<> -void FilterTable::getParamiv(const ALfilter*, ALenum param, int*) -{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } +void FilterTable::getParamiv(ALCcontext *context, const ALfilter*, ALenum param, int*) +{ context->throw_error(AL_INVALID_ENUM, "Invalid null filter property {:#04x}", as_unsigned(param)); } template<> -void FilterTable::getParamf(const ALfilter*, ALenum param, float*) -{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } +void FilterTable::getParamf(ALCcontext *context, const ALfilter*, ALenum param, float*) +{ context->throw_error(AL_INVALID_ENUM, "Invalid null filter property {:#04x}", as_unsigned(param)); } template<> -void FilterTable::getParamfv(const ALfilter*, ALenum param, float*) -{ throw filter_exception{AL_INVALID_ENUM, "Invalid null filter property 0x%04x", param}; } +void FilterTable::getParamfv(ALCcontext *context, const ALfilter*, ALenum param, float*) +{ context->throw_error(AL_INVALID_ENUM, "Invalid null filter property {:#04x}", as_unsigned(param)); } /* Lowpass parameter handlers */ template<> -void FilterTable::setParami(ALfilter*, ALenum param, int) -{ throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass integer property 0x%04x", param}; } +void FilterTable::setParami(ALCcontext *context, ALfilter*, ALenum param, int) +{ context->throw_error(AL_INVALID_ENUM, "Invalid low-pass integer property {:#04x}", as_unsigned(param)); } template<> -void FilterTable::setParamiv(ALfilter *filter, ALenum param, const int *values) -{ setParami(filter, param, values[0]); } +void FilterTable::setParamiv(ALCcontext *context, ALfilter *filter, ALenum param, const int *values) +{ setParami(context, filter, param, *values); } template<> -void FilterTable::setParamf(ALfilter *filter, ALenum param, float val) +void FilterTable::setParamf(ALCcontext *context, ALfilter *filter, ALenum param, float val) { switch(param) { case AL_LOWPASS_GAIN: if(!(val >= AL_LOWPASS_MIN_GAIN && val <= AL_LOWPASS_MAX_GAIN)) - throw filter_exception{AL_INVALID_VALUE, "Low-pass gain %f out of range", val}; + context->throw_error(AL_INVALID_VALUE, "Low-pass gain {:f} out of range", val); filter->Gain = val; - break; + return; case AL_LOWPASS_GAINHF: if(!(val >= AL_LOWPASS_MIN_GAINHF && val <= AL_LOWPASS_MAX_GAINHF)) - throw filter_exception{AL_INVALID_VALUE, "Low-pass gainhf %f out of range", val}; + context->throw_error(AL_INVALID_VALUE, "Low-pass gainhf {:f} out of range", val); filter->GainHF = val; - break; - - default: - throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass float property 0x%04x", param}; + return; } + context->throw_error(AL_INVALID_ENUM, "Invalid low-pass float property {:#04x}", + as_unsigned(param)); } template<> -void FilterTable::setParamfv(ALfilter *filter, ALenum param, const float *vals) -{ setParamf(filter, param, vals[0]); } +void FilterTable::setParamfv(ALCcontext *context, ALfilter *filter, ALenum param, const float *vals) +{ setParamf(context, filter, param, *vals); } template<> -void FilterTable::getParami(const ALfilter*, ALenum param, int*) -{ throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass integer property 0x%04x", param}; } +void FilterTable::getParami(ALCcontext *context, const ALfilter*, ALenum param, int*) +{ context->throw_error(AL_INVALID_ENUM, "Invalid low-pass integer property {:#04x}", as_unsigned(param)); } template<> -void FilterTable::getParamiv(const ALfilter *filter, ALenum param, int *values) -{ getParami(filter, param, values); } +void FilterTable::getParamiv(ALCcontext *context, const ALfilter *filter, ALenum param, int *values) +{ getParami(context, filter, param, values); } template<> -void FilterTable::getParamf(const ALfilter *filter, ALenum param, float *val) +void FilterTable::getParamf(ALCcontext *context, const ALfilter *filter, ALenum param, float *val) { switch(param) { - case AL_LOWPASS_GAIN: - *val = filter->Gain; - break; - - case AL_LOWPASS_GAINHF: - *val = filter->GainHF; - break; - - default: - throw filter_exception{AL_INVALID_ENUM, "Invalid low-pass float property 0x%04x", param}; + case AL_LOWPASS_GAIN: *val = filter->Gain; return; + case AL_LOWPASS_GAINHF: *val = filter->GainHF; return; } + context->throw_error(AL_INVALID_ENUM, "Invalid low-pass float property {:#04x}", + as_unsigned(param)); } template<> -void FilterTable::getParamfv(const ALfilter *filter, ALenum param, float *vals) -{ getParamf(filter, param, vals); } +void FilterTable::getParamfv(ALCcontext *context, const ALfilter *filter, ALenum param, float *vals) +{ getParamf(context, filter, param, vals); } /* Highpass parameter handlers */ template<> -void FilterTable::setParami(ALfilter*, ALenum param, int) -{ throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass integer property 0x%04x", param}; } +void FilterTable::setParami(ALCcontext *context, ALfilter*, ALenum param, int) +{ context->throw_error(AL_INVALID_ENUM, "Invalid high-pass integer property {:#04x}", as_unsigned(param)); } template<> -void FilterTable::setParamiv(ALfilter *filter, ALenum param, const int *values) -{ setParami(filter, param, values[0]); } +void FilterTable::setParamiv(ALCcontext *context, ALfilter *filter, ALenum param, const int *values) +{ setParami(context, filter, param, *values); } template<> -void FilterTable::setParamf(ALfilter *filter, ALenum param, float val) +void FilterTable::setParamf(ALCcontext *context, ALfilter *filter, ALenum param, float val) { switch(param) { case AL_HIGHPASS_GAIN: if(!(val >= AL_HIGHPASS_MIN_GAIN && val <= AL_HIGHPASS_MAX_GAIN)) - throw filter_exception{AL_INVALID_VALUE, "High-pass gain %f out of range", val}; + context->throw_error(AL_INVALID_VALUE, "High-pass gain {:f} out of range", val); filter->Gain = val; - break; + return; case AL_HIGHPASS_GAINLF: if(!(val >= AL_HIGHPASS_MIN_GAINLF && val <= AL_HIGHPASS_MAX_GAINLF)) - throw filter_exception{AL_INVALID_VALUE, "High-pass gainlf %f out of range", val}; + context->throw_error(AL_INVALID_VALUE, "High-pass gainlf {:f} out of range", val); filter->GainLF = val; - break; - - default: - throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass float property 0x%04x", param}; + return; } + context->throw_error(AL_INVALID_ENUM, "Invalid high-pass float property {:#04x}", + as_unsigned(param)); } template<> -void FilterTable::setParamfv(ALfilter *filter, ALenum param, const float *vals) -{ setParamf(filter, param, vals[0]); } +void FilterTable::setParamfv(ALCcontext *context, ALfilter *filter, ALenum param, const float *vals) +{ setParamf(context, filter, param, *vals); } template<> -void FilterTable::getParami(const ALfilter*, ALenum param, int*) -{ throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass integer property 0x%04x", param}; } +void FilterTable::getParami(ALCcontext *context, const ALfilter*, ALenum param, int*) +{ context->throw_error(AL_INVALID_ENUM, "Invalid high-pass integer property {:#04x}", as_unsigned(param)); } template<> -void FilterTable::getParamiv(const ALfilter *filter, ALenum param, int *values) -{ getParami(filter, param, values); } +void FilterTable::getParamiv(ALCcontext *context, const ALfilter *filter, ALenum param, int *values) +{ getParami(context, filter, param, values); } template<> -void FilterTable::getParamf(const ALfilter *filter, ALenum param, float *val) +void FilterTable::getParamf(ALCcontext *context, const ALfilter *filter, ALenum param, float *val) { switch(param) { - case AL_HIGHPASS_GAIN: - *val = filter->Gain; - break; - - case AL_HIGHPASS_GAINLF: - *val = filter->GainLF; - break; - - default: - throw filter_exception{AL_INVALID_ENUM, "Invalid high-pass float property 0x%04x", param}; + case AL_HIGHPASS_GAIN: *val = filter->Gain; return; + case AL_HIGHPASS_GAINLF: *val = filter->GainLF; return; } + context->throw_error(AL_INVALID_ENUM, "Invalid high-pass float property {:#04x}", + as_unsigned(param)); } template<> -void FilterTable::getParamfv(const ALfilter *filter, ALenum param, float *vals) -{ getParamf(filter, param, vals); } +void FilterTable::getParamfv(ALCcontext *context, const ALfilter *filter, ALenum param, float *vals) +{ getParamf(context, filter, param, vals); } /* Bandpass parameter handlers */ template<> -void FilterTable::setParami(ALfilter*, ALenum param, int) -{ throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass integer property 0x%04x", param}; } +void FilterTable::setParami(ALCcontext *context, ALfilter*, ALenum param, int) +{ context->throw_error(AL_INVALID_ENUM, "Invalid band-pass integer property {:#04x}", as_unsigned(param)); } template<> -void FilterTable::setParamiv(ALfilter *filter, ALenum param, const int *values) -{ setParami(filter, param, values[0]); } +void FilterTable::setParamiv(ALCcontext *context, ALfilter *filter, ALenum param, const int *values) +{ setParami(context, filter, param, *values); } template<> -void FilterTable::setParamf(ALfilter *filter, ALenum param, float val) +void FilterTable::setParamf(ALCcontext *context, ALfilter *filter, ALenum param, float val) { switch(param) { case AL_BANDPASS_GAIN: if(!(val >= AL_BANDPASS_MIN_GAIN && val <= AL_BANDPASS_MAX_GAIN)) - throw filter_exception{AL_INVALID_VALUE, "Band-pass gain %f out of range", val}; + context->throw_error(AL_INVALID_VALUE, "Band-pass gain {:f} out of range", val); filter->Gain = val; - break; + return; case AL_BANDPASS_GAINHF: if(!(val >= AL_BANDPASS_MIN_GAINHF && val <= AL_BANDPASS_MAX_GAINHF)) - throw filter_exception{AL_INVALID_VALUE, "Band-pass gainhf %f out of range", val}; + context->throw_error(AL_INVALID_VALUE, "Band-pass gainhf {:f} out of range", val); filter->GainHF = val; - break; + return; case AL_BANDPASS_GAINLF: if(!(val >= AL_BANDPASS_MIN_GAINLF && val <= AL_BANDPASS_MAX_GAINLF)) - throw filter_exception{AL_INVALID_VALUE, "Band-pass gainlf %f out of range", val}; + context->throw_error(AL_INVALID_VALUE, "Band-pass gainlf {:f} out of range", val); filter->GainLF = val; - break; - - default: - throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass float property 0x%04x", param}; + return; } + context->throw_error(AL_INVALID_ENUM, "Invalid band-pass float property {:#04x}", + as_unsigned(param)); } template<> -void FilterTable::setParamfv(ALfilter *filter, ALenum param, const float *vals) -{ setParamf(filter, param, vals[0]); } +void FilterTable::setParamfv(ALCcontext *context, ALfilter *filter, ALenum param, const float *vals) +{ setParamf(context, filter, param, *vals); } template<> -void FilterTable::getParami(const ALfilter*, ALenum param, int*) -{ throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass integer property 0x%04x", param}; } +void FilterTable::getParami(ALCcontext *context, const ALfilter*, ALenum param, int*) +{ context->throw_error(AL_INVALID_ENUM, "Invalid band-pass integer property {:#04x}", as_unsigned(param)); } template<> -void FilterTable::getParamiv(const ALfilter *filter, ALenum param, int *values) -{ getParami(filter, param, values); } +void FilterTable::getParamiv(ALCcontext *context, const ALfilter *filter, ALenum param, int *values) +{ getParami(context, filter, param, values); } template<> -void FilterTable::getParamf(const ALfilter *filter, ALenum param, float *val) +void FilterTable::getParamf(ALCcontext *context, const ALfilter *filter, ALenum param, float *val) { switch(param) { - case AL_BANDPASS_GAIN: - *val = filter->Gain; - break; - - case AL_BANDPASS_GAINHF: - *val = filter->GainHF; - break; - - case AL_BANDPASS_GAINLF: - *val = filter->GainLF; - break; - - default: - throw filter_exception{AL_INVALID_ENUM, "Invalid band-pass float property 0x%04x", param}; + case AL_BANDPASS_GAIN: *val = filter->Gain; return; + case AL_BANDPASS_GAINHF: *val = filter->GainHF; return; + case AL_BANDPASS_GAINLF: *val = filter->GainLF; return; } + context->throw_error(AL_INVALID_ENUM, "Invalid band-pass float property {:#04x}", + as_unsigned(param)); } template<> -void FilterTable::getParamfv(const ALfilter *filter, ALenum param, float *vals) -{ getParamf(filter, param, vals); } +void FilterTable::getParamfv(ALCcontext *context, const ALfilter *filter, ALenum param, float *vals) +{ getParamf(context, filter, param, vals); } -AL_API DECL_FUNC2(void, alGenFilters, ALsizei, ALuint*) +AL_API DECL_FUNC2(void, alGenFilters, ALsizei,n, ALuint*,filters) FORCE_ALIGN void AL_APIENTRY alGenFiltersDirect(ALCcontext *context, ALsizei n, ALuint *filters) noexcept -{ - if(n < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Generating %d filters", n); +try { + if(n < 0) + context->throw_error(AL_INVALID_VALUE, "Generating {} filters", n); if(n <= 0) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->FilterLock}; - if(!EnsureFilters(device, static_cast(n))) - { - context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d filter%s", n, (n==1)?"":"s"); - return; - } + auto *device = context->mALDevice.get(); + auto filterlock = std::lock_guard{device->FilterLock}; - if(n == 1) LIKELY - { - /* Special handling for the easy and normal case. */ - ALfilter *filter{AllocFilter(device)}; - if(filter) filters[0] = filter->id; - } - else - { - /* Store the allocated buffer IDs in a separate local list, to avoid - * modifying the user storage in case of failure. - */ - std::vector ids; - ids.reserve(static_cast(n)); - do { - ALfilter *filter{AllocFilter(device)}; - ids.emplace_back(filter->id); - } while(--n); - std::copy(ids.begin(), ids.end(), filters); - } + const al::span fids{filters, static_cast(n)}; + if(!EnsureFilters(device, fids.size())) + context->throw_error(AL_OUT_OF_MEMORY, "Failed to allocate {} filter{}", n, + (n==1) ? "" : "s"); + + std::generate(fids.begin(), fids.end(), [device]{ return AllocFilter(device)->id; }); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC2(void, alDeleteFilters, ALsizei, const ALuint*) +AL_API DECL_FUNC2(void, alDeleteFilters, ALsizei,n, const ALuint*,filters) FORCE_ALIGN void AL_APIENTRY alDeleteFiltersDirect(ALCcontext *context, ALsizei n, const ALuint *filters) noexcept -{ - if(n < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Deleting %d filters", n); +try { + if(n < 0) + context->throw_error(AL_INVALID_VALUE, "Deleting {} filters", n); if(n <= 0) UNLIKELY return; - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->FilterLock}; + auto *device = context->mALDevice.get(); + auto filterlock = std::lock_guard{device->FilterLock}; /* First try to find any filters that are invalid. */ auto validate_filter = [device](const ALuint fid) -> bool { return !fid || LookupFilter(device, fid) != nullptr; }; - const ALuint *filters_end = filters + n; - auto invflt = std::find_if_not(filters, filters_end, validate_filter); - if(invflt != filters_end) UNLIKELY - { - context->setError(AL_INVALID_NAME, "Invalid filter ID %u", *invflt); - return; - } + const al::span fids{filters, static_cast(n)}; + auto invflt = std::find_if_not(fids.begin(), fids.end(), validate_filter); + if(invflt != fids.end()) + context->throw_error(AL_INVALID_NAME, "Invalid filter ID {}", *invflt); /* All good. Delete non-0 filter IDs. */ auto delete_filter = [device](const ALuint fid) -> void { - ALfilter *filter{fid ? LookupFilter(device, fid) : nullptr}; - if(filter) FreeFilter(device, filter); + if(ALfilter *filter{fid ? LookupFilter(device, fid) : nullptr}) + FreeFilter(device, filter); }; - std::for_each(filters, filters_end, delete_filter); + std::for_each(fids.begin(), fids.end(), delete_filter); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC1(ALboolean, alIsFilter, ALuint) +AL_API DECL_FUNC1(ALboolean, alIsFilter, ALuint,filter) FORCE_ALIGN ALboolean AL_APIENTRY alIsFilterDirect(ALCcontext *context, ALuint filter) noexcept { - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->FilterLock}; + auto *device = context->mALDevice.get(); + auto filterlock = std::lock_guard{device->FilterLock}; if(!filter || LookupFilter(device, filter)) return AL_TRUE; return AL_FALSE; } -AL_API DECL_FUNC3(void, alFilteri, ALuint, ALenum, ALint) +AL_API DECL_FUNC3(void, alFilteri, ALuint,filter, ALenum,param, ALint,value) FORCE_ALIGN void AL_APIENTRY alFilteriDirect(ALCcontext *context, ALuint filter, ALenum param, ALint value) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->FilterLock}; +try { + auto *device = context->mALDevice.get(); + auto filterlock = std::lock_guard{device->FilterLock}; ALfilter *alfilt{LookupFilter(device, filter)}; - if(!alfilt) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter); - else if(param == AL_FILTER_TYPE) - { - if(value == AL_FILTER_NULL || value == AL_FILTER_LOWPASS - || value == AL_FILTER_HIGHPASS || value == AL_FILTER_BANDPASS) - InitFilterParams(alfilt, value); - else - context->setError(AL_INVALID_VALUE, "Invalid filter type 0x%04x", value); - } - else try + if(!alfilt) + context->throw_error(AL_INVALID_NAME, "Invalid filter ID {}", filter); + + switch(param) { - /* Call the appropriate handler */ - std::visit([alfilt,param,value](auto&& thunk){thunk.setParami(alfilt, param, value);}, - alfilt->mTypeVariant); - } - catch(filter_exception &e) { - context->setError(e.errorCode(), "%s", e.what()); + case AL_FILTER_TYPE: + if(!(value == AL_FILTER_NULL || value == AL_FILTER_LOWPASS + || value == AL_FILTER_HIGHPASS || value == AL_FILTER_BANDPASS)) + context->throw_error(AL_INVALID_VALUE, "Invalid filter type {:#04x}", + as_unsigned(value)); + InitFilterParams(alfilt, value); + return; } + + /* Call the appropriate handler */ + std::visit([context,alfilt,param,value](auto&& thunk) + { thunk.setParami(context, alfilt, param, value); }, alfilt->mTypeVariant); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC3(void, alFilteriv, ALuint, ALenum, const ALint*) +AL_API DECL_FUNC3(void, alFilteriv, ALuint,filter, ALenum,param, const ALint*,values) FORCE_ALIGN void AL_APIENTRY alFilterivDirect(ALCcontext *context, ALuint filter, ALenum param, const ALint *values) noexcept -{ +try { switch(param) { case AL_FILTER_TYPE: - alFilteriDirect(context, filter, param, values[0]); + alFilteriDirect(context, filter, param, *values); return; } - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->FilterLock}; + auto *device = context->mALDevice.get(); + auto filterlock = std::lock_guard{device->FilterLock}; ALfilter *alfilt{LookupFilter(device, filter)}; - if(!alfilt) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter); - else try - { - /* Call the appropriate handler */ - std::visit([alfilt,param,values](auto&& thunk){thunk.setParamiv(alfilt, param, values);}, - alfilt->mTypeVariant); - } - catch(filter_exception &e) { - context->setError(e.errorCode(), "%s", e.what()); - } + if(!alfilt) + context->throw_error(AL_INVALID_NAME, "Invalid filter ID {}", filter); + + /* Call the appropriate handler */ + std::visit([context,alfilt,param,values](auto&& thunk) + { thunk.setParamiv(context, alfilt, param, values); }, alfilt->mTypeVariant); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC3(void, alFilterf, ALuint, ALenum, ALfloat) +AL_API DECL_FUNC3(void, alFilterf, ALuint,filter, ALenum,param, ALfloat,value) FORCE_ALIGN void AL_APIENTRY alFilterfDirect(ALCcontext *context, ALuint filter, ALenum param, ALfloat value) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->FilterLock}; +try { + auto *device = context->mALDevice.get(); + auto filterlock = std::lock_guard{device->FilterLock}; ALfilter *alfilt{LookupFilter(device, filter)}; - if(!alfilt) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter); - else try - { - /* Call the appropriate handler */ - std::visit([alfilt,param,value](auto&& thunk){thunk.setParamf(alfilt, param, value);}, - alfilt->mTypeVariant); - } - catch(filter_exception &e) { - context->setError(e.errorCode(), "%s", e.what()); - } + if(!alfilt) + context->throw_error(AL_INVALID_NAME, "Invalid filter ID {}", filter); + + /* Call the appropriate handler */ + std::visit([context,alfilt,param,value](auto&& thunk) + { thunk.setParamf(context, alfilt, param, value); }, alfilt->mTypeVariant); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC3(void, alFilterfv, ALuint, ALenum, const ALfloat*) +AL_API DECL_FUNC3(void, alFilterfv, ALuint,filter, ALenum,param, const ALfloat*,values) FORCE_ALIGN void AL_APIENTRY alFilterfvDirect(ALCcontext *context, ALuint filter, ALenum param, const ALfloat *values) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->FilterLock}; +try { + auto *device = context->mALDevice.get(); + auto filterlock = std::lock_guard{device->FilterLock}; ALfilter *alfilt{LookupFilter(device, filter)}; - if(!alfilt) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter); - else try - { - /* Call the appropriate handler */ - std::visit([alfilt,param,values](auto&& thunk){thunk.setParamfv(alfilt, param, values);}, - alfilt->mTypeVariant); - } - catch(filter_exception &e) { - context->setError(e.errorCode(), "%s", e.what()); - } + if(!alfilt) + context->throw_error(AL_INVALID_NAME, "Invalid filter ID {}", filter); + + /* Call the appropriate handler */ + std::visit([context,alfilt,param,values](auto&& thunk) + { thunk.setParamfv(context, alfilt, param, values); }, alfilt->mTypeVariant); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC3(void, alGetFilteri, ALuint, ALenum, ALint*) +AL_API DECL_FUNC3(void, alGetFilteri, ALuint,filter, ALenum,param, ALint*,value) FORCE_ALIGN void AL_APIENTRY alGetFilteriDirect(ALCcontext *context, ALuint filter, ALenum param, ALint *value) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->FilterLock}; +try { + auto *device = context->mALDevice.get(); + auto filterlock = std::lock_guard{device->FilterLock}; const ALfilter *alfilt{LookupFilter(device, filter)}; - if(!alfilt) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter); - else if(param == AL_FILTER_TYPE) - *value = alfilt->type; - else try + if(!alfilt) + context->throw_error(AL_INVALID_NAME, "Invalid filter ID {}", filter); + + switch(param) { - /* Call the appropriate handler */ - std::visit([alfilt,param,value](auto&& thunk){thunk.getParami(alfilt, param, value);}, - alfilt->mTypeVariant); - } - catch(filter_exception &e) { - context->setError(e.errorCode(), "%s", e.what()); + case AL_FILTER_TYPE: *value = alfilt->type; return; } + + /* Call the appropriate handler */ + std::visit([context,alfilt,param,value](auto&& thunk) + { thunk.getParami(context, alfilt, param, value); }, alfilt->mTypeVariant); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC3(void, alGetFilteriv, ALuint, ALenum, ALint*) +AL_API DECL_FUNC3(void, alGetFilteriv, ALuint,filter, ALenum,param, ALint*,values) FORCE_ALIGN void AL_APIENTRY alGetFilterivDirect(ALCcontext *context, ALuint filter, ALenum param, ALint *values) noexcept -{ +try { switch(param) { case AL_FILTER_TYPE: @@ -613,74 +566,74 @@ FORCE_ALIGN void AL_APIENTRY alGetFilterivDirect(ALCcontext *context, ALuint fil return; } - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->FilterLock}; + auto *device = context->mALDevice.get(); + auto filterlock = std::lock_guard{device->FilterLock}; const ALfilter *alfilt{LookupFilter(device, filter)}; - if(!alfilt) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter); - else try - { - /* Call the appropriate handler */ - std::visit([alfilt,param,values](auto&& thunk){thunk.getParamiv(alfilt, param, values);}, - alfilt->mTypeVariant); - } - catch(filter_exception &e) { - context->setError(e.errorCode(), "%s", e.what()); - } + if(!alfilt) + context->throw_error(AL_INVALID_NAME, "Invalid filter ID {}", filter); + + /* Call the appropriate handler */ + std::visit([context,alfilt,param,values](auto&& thunk) + { thunk.getParamiv(context, alfilt, param, values); }, alfilt->mTypeVariant); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC3(void, alGetFilterf, ALuint, ALenum, ALfloat*) +AL_API DECL_FUNC3(void, alGetFilterf, ALuint,filter, ALenum,param, ALfloat*,value) FORCE_ALIGN void AL_APIENTRY alGetFilterfDirect(ALCcontext *context, ALuint filter, ALenum param, ALfloat *value) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->FilterLock}; +try { + auto *device = context->mALDevice.get(); + auto filterlock = std::lock_guard{device->FilterLock}; const ALfilter *alfilt{LookupFilter(device, filter)}; - if(!alfilt) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter); - else try - { - /* Call the appropriate handler */ - std::visit([alfilt,param,value](auto&& thunk){thunk.getParamf(alfilt, param, value);}, - alfilt->mTypeVariant); - } - catch(filter_exception &e) { - context->setError(e.errorCode(), "%s", e.what()); - } + if(!alfilt) + context->throw_error(AL_INVALID_NAME, "Invalid filter ID {}", filter); + + /* Call the appropriate handler */ + std::visit([context,alfilt,param,value](auto&& thunk) + { thunk.getParamf(context, alfilt, param, value); }, alfilt->mTypeVariant); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC3(void, alGetFilterfv, ALuint, ALenum, ALfloat*) +AL_API DECL_FUNC3(void, alGetFilterfv, ALuint,filter, ALenum,param, ALfloat*,values) FORCE_ALIGN void AL_APIENTRY alGetFilterfvDirect(ALCcontext *context, ALuint filter, ALenum param, ALfloat *values) noexcept -{ - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->FilterLock}; +try { + auto *device = context->mALDevice.get(); + auto filterlock = std::lock_guard{device->FilterLock}; const ALfilter *alfilt{LookupFilter(device, filter)}; - if(!alfilt) UNLIKELY - context->setError(AL_INVALID_NAME, "Invalid filter ID %u", filter); - else try - { - /* Call the appropriate handler */ - std::visit([alfilt,param,values](auto&& thunk){thunk.getParamfv(alfilt, param, values);}, - alfilt->mTypeVariant); - } - catch(filter_exception &e) { - context->setError(e.errorCode(), "%s", e.what()); - } + if(!alfilt) + context->throw_error(AL_INVALID_NAME, "Invalid filter ID {}", filter); + + /* Call the appropriate handler */ + std::visit([context,alfilt,param,values](auto&& thunk) + { thunk.getParamfv(context, alfilt, param, values); }, alfilt->mTypeVariant); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } void ALfilter::SetName(ALCcontext *context, ALuint id, std::string_view name) { - ALCdevice *device{context->mALDevice.get()}; - std::lock_guard _{device->FilterLock}; + auto *device = context->mALDevice.get(); + auto filterlock = std::lock_guard{device->FilterLock}; auto filter = LookupFilter(device, id); - if(!filter) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid filter ID %u", id); + if(!filter) + context->throw_error(AL_INVALID_NAME, "Invalid filter ID {}", id); device->mFilterNames.insert_or_assign(id, name); } @@ -695,10 +648,10 @@ FilterSubList::~FilterSubList() while(usemask) { const int idx{al::countr_zero(usemask)}; - std::destroy_at(Filters+idx); + std::destroy_at(al::to_address(Filters->begin() + idx)); usemask &= ~(1_u64 << idx); } FreeMask = ~usemask; - al_free(Filters); + SubListAllocator{}.deallocate(Filters, 1); Filters = nullptr; } diff --git a/3rdparty/openal/al/filter.h b/3rdparty/openal/al/filter.h index 505900d46597..70b78ca83227 100644 --- a/3rdparty/openal/al/filter.h +++ b/3rdparty/openal/al/filter.h @@ -1,30 +1,40 @@ #ifndef AL_FILTER_H #define AL_FILTER_H +#include +#include #include +#include #include #include "AL/al.h" #include "AL/alc.h" -#include "AL/alext.h" +#include "AL/efx.h" #include "almalloc.h" +#include "alnumeric.h" -#define LOWPASSFREQREF 5000.0f -#define HIGHPASSFREQREF 250.0f +struct ALfilter; +inline constexpr float LowPassFreqRef{5000.0f}; +inline constexpr float HighPassFreqRef{250.0f}; + template struct FilterTable { - static void setParami(struct ALfilter*, ALenum, int); - static void setParamiv(struct ALfilter*, ALenum, const int*); - static void setParamf(struct ALfilter*, ALenum, float); - static void setParamfv(struct ALfilter*, ALenum, const float*); - - static void getParami(const struct ALfilter*, ALenum, int*); - static void getParamiv(const struct ALfilter*, ALenum, int*); - static void getParamf(const struct ALfilter*, ALenum, float*); - static void getParamfv(const struct ALfilter*, ALenum, float*); + static void setParami(ALCcontext*, ALfilter*, ALenum, int); + static void setParamiv(ALCcontext*, ALfilter*, ALenum, const int*); + static void setParamf(ALCcontext*, ALfilter*, ALenum, float); + static void setParamfv(ALCcontext*, ALfilter*, ALenum, const float*); + + static void getParami(ALCcontext*, const ALfilter*, ALenum, int*); + static void getParamiv(ALCcontext*, const ALfilter*, ALenum, int*); + static void getParamf(ALCcontext*, const ALfilter*, ALenum, float*); + static void getParamfv(ALCcontext*, const ALfilter*, ALenum, float*); + +private: + FilterTable() = default; + friend T; }; struct NullFilterTable : public FilterTable { }; @@ -38,9 +48,9 @@ struct ALfilter { float Gain{1.0f}; float GainHF{1.0f}; - float HFReference{LOWPASSFREQREF}; + float HFReference{LowPassFreqRef}; float GainLF{1.0f}; - float LFReference{HIGHPASSFREQREF}; + float LFReference{HighPassFreqRef}; using TableTypes = std::variant; @@ -51,7 +61,22 @@ struct ALfilter { static void SetName(ALCcontext *context, ALuint id, std::string_view name); - DISABLE_ALLOC() + DISABLE_ALLOC +}; + +struct FilterSubList { + uint64_t FreeMask{~0_u64}; + gsl::owner*> Filters{nullptr}; + + FilterSubList() noexcept = default; + FilterSubList(const FilterSubList&) = delete; + FilterSubList(FilterSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Filters{rhs.Filters} + { rhs.FreeMask = ~0_u64; rhs.Filters = nullptr; } + ~FilterSubList(); + + FilterSubList& operator=(const FilterSubList&) = delete; + FilterSubList& operator=(FilterSubList&& rhs) noexcept + { std::swap(FreeMask, rhs.FreeMask); std::swap(Filters, rhs.Filters); return *this; } }; #endif diff --git a/3rdparty/openal/al/listener.cpp b/3rdparty/openal/al/listener.cpp index ea2ebb3f7e81..9addee0cd15d 100644 --- a/3rdparty/openal/al/listener.cpp +++ b/3rdparty/openal/al/listener.cpp @@ -22,6 +22,7 @@ #include "listener.h" +#include #include #include @@ -30,11 +31,11 @@ #include "AL/efx.h" #include "alc/context.h" -#include "almalloc.h" -#include "atomic.h" +#include "alnumeric.h" +#include "alspan.h" #include "core/except.h" +#include "core/logging.h" #include "direct_defs.h" -#include "opthelpers.h" namespace { @@ -53,7 +54,7 @@ inline void CommitAndUpdateProps(ALCcontext *context) { if(!context->mDeferUpdates) { -#ifdef ALSOFT_EAX +#if ALSOFT_EAX if(context->eaxNeedsCommit()) { context->mPropsDirty = true; @@ -69,122 +70,134 @@ inline void CommitAndUpdateProps(ALCcontext *context) } // namespace -AL_API DECL_FUNC2(void, alListenerf, ALenum, ALfloat) +AL_API DECL_FUNC2(void, alListenerf, ALenum,param, ALfloat,value) FORCE_ALIGN void AL_APIENTRY alListenerfDirect(ALCcontext *context, ALenum param, ALfloat value) noexcept -{ +try { ALlistener &listener = context->mListener; - std::lock_guard _{context->mPropLock}; + std::lock_guard proplock{context->mPropLock}; switch(param) { case AL_GAIN: if(!(value >= 0.0f && std::isfinite(value))) - return context->setError(AL_INVALID_VALUE, "Listener gain out of range"); + context->throw_error(AL_INVALID_VALUE, "Listener gain {:f} out of range", value); listener.Gain = value; UpdateProps(context); - break; + return; case AL_METERS_PER_UNIT: if(!(value >= AL_MIN_METERS_PER_UNIT && value <= AL_MAX_METERS_PER_UNIT)) - return context->setError(AL_INVALID_VALUE, "Listener meters per unit out of range"); + context->throw_error(AL_INVALID_VALUE, "Listener meters per unit {:f} out of range", + value); listener.mMetersPerUnit = value; UpdateProps(context); - break; - - default: - context->setError(AL_INVALID_ENUM, "Invalid listener float property"); + return; } + context->throw_error(AL_INVALID_ENUM, "Invalid listener float property {:#04x}", + as_unsigned(param)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC4(void, alListener3f, ALenum, ALfloat, ALfloat, ALfloat) +AL_API DECL_FUNC4(void, alListener3f, ALenum,param, ALfloat,value1, ALfloat,value2, ALfloat,value3) FORCE_ALIGN void AL_APIENTRY alListener3fDirect(ALCcontext *context, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) noexcept -{ +try { ALlistener &listener = context->mListener; - std::lock_guard _{context->mPropLock}; + std::lock_guard proplock{context->mPropLock}; switch(param) { case AL_POSITION: if(!(std::isfinite(value1) && std::isfinite(value2) && std::isfinite(value3))) - return context->setError(AL_INVALID_VALUE, "Listener position out of range"); + context->throw_error(AL_INVALID_VALUE, "Listener position out of range"); listener.Position[0] = value1; listener.Position[1] = value2; listener.Position[2] = value3; CommitAndUpdateProps(context); - break; + return; case AL_VELOCITY: if(!(std::isfinite(value1) && std::isfinite(value2) && std::isfinite(value3))) - return context->setError(AL_INVALID_VALUE, "Listener velocity out of range"); + context->throw_error(AL_INVALID_VALUE, "Listener velocity out of range"); listener.Velocity[0] = value1; listener.Velocity[1] = value2; listener.Velocity[2] = value3; CommitAndUpdateProps(context); - break; - - default: - context->setError(AL_INVALID_ENUM, "Invalid listener 3-float property"); + return; } + context->throw_error(AL_INVALID_ENUM, "Invalid listener 3-float property {:#04x}", + as_unsigned(param)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC2(void, alListenerfv, ALenum, const ALfloat*) +AL_API DECL_FUNC2(void, alListenerfv, ALenum,param, const ALfloat*,values) FORCE_ALIGN void AL_APIENTRY alListenerfvDirect(ALCcontext *context, ALenum param, const ALfloat *values) noexcept -{ - if(!values) UNLIKELY - return context->setError(AL_INVALID_VALUE, "NULL pointer"); +try { + if(!values) + context->throw_error(AL_INVALID_VALUE, "NULL pointer"); switch(param) { case AL_GAIN: case AL_METERS_PER_UNIT: - alListenerfDirect(context, param, values[0]); + alListenerfDirect(context, param, *values); return; case AL_POSITION: case AL_VELOCITY: - alListener3fDirect(context, param, values[0], values[1], values[2]); + auto vals = al::span{values, 3_uz}; + alListener3fDirect(context, param, vals[0], vals[1], vals[2]); return; } ALlistener &listener = context->mListener; - std::lock_guard _{context->mPropLock}; + std::lock_guard proplock{context->mPropLock}; switch(param) { case AL_ORIENTATION: - if(!(std::isfinite(values[0]) && std::isfinite(values[1]) && std::isfinite(values[2]) && - std::isfinite(values[3]) && std::isfinite(values[4]) && std::isfinite(values[5]))) - return context->setError(AL_INVALID_VALUE, "Listener orientation out of range"); + auto vals = al::span{values, 6_uz}; + if(!std::all_of(vals.cbegin(), vals.cend(), [](float f) { return std::isfinite(f); })) + context->throw_error(AL_INVALID_VALUE, "Listener orientation out of range"); /* AT then UP */ - listener.OrientAt[0] = values[0]; - listener.OrientAt[1] = values[1]; - listener.OrientAt[2] = values[2]; - listener.OrientUp[0] = values[3]; - listener.OrientUp[1] = values[4]; - listener.OrientUp[2] = values[5]; + std::copy_n(vals.cbegin(), 3, listener.OrientAt.begin()); + std::copy_n(vals.cbegin()+3, 3, listener.OrientUp.begin()); CommitAndUpdateProps(context); - break; - - default: - context->setError(AL_INVALID_ENUM, "Invalid listener float-vector property"); + return; } + context->throw_error(AL_INVALID_ENUM, "Invalid listener float-vector property {:#04x}", + as_unsigned(param)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC2(void, alListeneri, ALenum, ALint) +AL_API DECL_FUNC2(void, alListeneri, ALenum,param, ALint,value) FORCE_ALIGN void AL_APIENTRY alListeneriDirect(ALCcontext *context, ALenum param, ALint /*value*/) noexcept -{ - std::lock_guard _{context->mPropLock}; - switch(param) - { - default: - context->setError(AL_INVALID_ENUM, "Invalid listener integer property"); - } +try { + std::lock_guard proplock{context->mPropLock}; + context->throw_error(AL_INVALID_ENUM, "Invalid listener integer property {:#04x}", + as_unsigned(param)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC4(void, alListener3i, ALenum, ALint, ALint, ALint) +AL_API DECL_FUNC4(void, alListener3i, ALenum,param, ALint,value1, ALint,value2, ALint,value3) FORCE_ALIGN void AL_APIENTRY alListener3iDirect(ALCcontext *context, ALenum param, ALint value1, ALint value2, ALint value3) noexcept -{ +try { switch(param) { case AL_POSITION: @@ -194,105 +207,116 @@ FORCE_ALIGN void AL_APIENTRY alListener3iDirect(ALCcontext *context, ALenum para return; } - std::lock_guard _{context->mPropLock}; - switch(param) - { - default: - context->setError(AL_INVALID_ENUM, "Invalid listener 3-integer property"); - } + std::lock_guard proplock{context->mPropLock}; + context->throw_error(AL_INVALID_ENUM, "Invalid listener 3-integer property {:#04x}", + as_unsigned(param)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC2(void, alListeneriv, ALenum, const ALint*) +AL_API DECL_FUNC2(void, alListeneriv, ALenum,param, const ALint*,values) FORCE_ALIGN void AL_APIENTRY alListenerivDirect(ALCcontext *context, ALenum param, const ALint *values) noexcept -{ - if(!values) UNLIKELY - return context->setError(AL_INVALID_VALUE, "NULL pointer"); +try { + if(!values) + context->throw_error(AL_INVALID_VALUE, "NULL pointer"); + al::span vals; switch(param) { case AL_POSITION: case AL_VELOCITY: - alListener3fDirect(context, param, static_cast(values[0]), - static_cast(values[1]), static_cast(values[2])); + vals = {values, 3_uz}; + alListener3fDirect(context, param, static_cast(vals[0]), + static_cast(vals[1]), static_cast(vals[2])); return; case AL_ORIENTATION: - const ALfloat fvals[6]{ - static_cast(values[0]), - static_cast(values[1]), - static_cast(values[2]), - static_cast(values[3]), - static_cast(values[4]), - static_cast(values[5]), + vals = {values, 6_uz}; + const std::array fvals{static_cast(vals[0]), static_cast(vals[1]), + static_cast(vals[2]), static_cast(vals[3]), + static_cast(vals[4]), static_cast(vals[5]), }; - alListenerfvDirect(context, param, fvals); + alListenerfvDirect(context, param, fvals.data()); return; } - std::lock_guard _{context->mPropLock}; - switch(param) - { - default: - context->setError(AL_INVALID_ENUM, "Invalid listener integer-vector property"); - } + std::lock_guard proplock{context->mPropLock}; + context->throw_error(AL_INVALID_ENUM, "Invalid listener integer-vector property {:#04x}", + as_unsigned(param)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC2(void, alGetListenerf, ALenum, ALfloat*) +AL_API DECL_FUNC2(void, alGetListenerf, ALenum,param, ALfloat*,value) FORCE_ALIGN void AL_APIENTRY alGetListenerfDirect(ALCcontext *context, ALenum param, ALfloat *value) noexcept -{ - ALlistener &listener = context->mListener; - std::lock_guard _{context->mPropLock}; +try { if(!value) - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else switch(param) - { - case AL_GAIN: - *value = listener.Gain; - break; + context->throw_error(AL_INVALID_VALUE, "NULL pointer"); - case AL_METERS_PER_UNIT: - *value = listener.mMetersPerUnit; - break; - - default: - context->setError(AL_INVALID_ENUM, "Invalid listener float property"); + ALlistener &listener = context->mListener; + std::lock_guard proplock{context->mPropLock}; + switch(param) + { + case AL_GAIN: *value = listener.Gain; return; + case AL_METERS_PER_UNIT: *value = listener.mMetersPerUnit; return; } + context->throw_error(AL_INVALID_ENUM, "Invalid listener float property {:#04x}", + as_unsigned(param)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC4(void, alGetListener3f, ALenum, ALfloat*, ALfloat*, ALfloat*) +AL_API DECL_FUNC4(void, alGetListener3f, ALenum,param, ALfloat*,value1, ALfloat*,value2, ALfloat*,value3) FORCE_ALIGN void AL_APIENTRY alGetListener3fDirect(ALCcontext *context, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) noexcept -{ - ALlistener &listener = context->mListener; - std::lock_guard _{context->mPropLock}; +try { if(!value1 || !value2 || !value3) - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else switch(param) + context->throw_error(AL_INVALID_VALUE, "NULL pointer"); + + ALlistener &listener = context->mListener; + std::lock_guard proplock{context->mPropLock}; + switch(param) { case AL_POSITION: *value1 = listener.Position[0]; *value2 = listener.Position[1]; *value3 = listener.Position[2]; - break; + return; case AL_VELOCITY: *value1 = listener.Velocity[0]; *value2 = listener.Velocity[1]; *value3 = listener.Velocity[2]; - break; - - default: - context->setError(AL_INVALID_ENUM, "Invalid listener 3-float property"); + return; } + context->throw_error(AL_INVALID_ENUM, "Invalid listener 3-float property {:#04x}", + as_unsigned(param)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC2(void, alGetListenerfv, ALenum, ALfloat*) +AL_API DECL_FUNC2(void, alGetListenerfv, ALenum,param, ALfloat*,values) FORCE_ALIGN void AL_APIENTRY alGetListenerfvDirect(ALCcontext *context, ALenum param, ALfloat *values) noexcept -{ +try { + if(!values) + context->throw_error(AL_INVALID_VALUE, "NULL pointer"); + switch(param) { case AL_GAIN: @@ -302,101 +326,112 @@ FORCE_ALIGN void AL_APIENTRY alGetListenerfvDirect(ALCcontext *context, ALenum p case AL_POSITION: case AL_VELOCITY: - alGetListener3fDirect(context, param, values+0, values+1, values+2); + auto vals = al::span{values, 3_uz}; + alGetListener3fDirect(context, param, &vals[0], &vals[1], &vals[2]); return; } ALlistener &listener = context->mListener; - std::lock_guard _{context->mPropLock}; - if(!values) - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else switch(param) + std::lock_guard proplock{context->mPropLock}; + switch(param) { case AL_ORIENTATION: + al::span vals{values, 6_uz}; // AT then UP - values[0] = listener.OrientAt[0]; - values[1] = listener.OrientAt[1]; - values[2] = listener.OrientAt[2]; - values[3] = listener.OrientUp[0]; - values[4] = listener.OrientUp[1]; - values[5] = listener.OrientUp[2]; - break; - - default: - context->setError(AL_INVALID_ENUM, "Invalid listener float-vector property"); + std::copy_n(listener.OrientAt.cbegin(), 3, vals.begin()); + std::copy_n(listener.OrientUp.cbegin(), 3, vals.begin()+3); + return; } + context->throw_error(AL_INVALID_ENUM, "Invalid listener float-vector property {:#04x}", + as_unsigned(param)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC2(void, alGetListeneri, ALenum, ALint*) +AL_API DECL_FUNC2(void, alGetListeneri, ALenum,param, ALint*,value) FORCE_ALIGN void AL_APIENTRY alGetListeneriDirect(ALCcontext *context, ALenum param, ALint *value) noexcept -{ - std::lock_guard _{context->mPropLock}; - if(!value) - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else switch(param) - { - default: - context->setError(AL_INVALID_ENUM, "Invalid listener integer property"); - } +try { + if(!value) context->throw_error(AL_INVALID_VALUE, "NULL pointer"); + std::lock_guard proplock{context->mPropLock}; + context->throw_error(AL_INVALID_ENUM, "Invalid listener integer property {:#04x}", + as_unsigned(param)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC4(void, alGetListener3i, ALenum, ALint*, ALint*, ALint*) +AL_API DECL_FUNC4(void, alGetListener3i, ALenum,param, ALint*,value1, ALint*,value2, ALint*,value3) FORCE_ALIGN void AL_APIENTRY alGetListener3iDirect(ALCcontext *context, ALenum param, ALint *value1, ALint *value2, ALint *value3) noexcept -{ - ALlistener &listener = context->mListener; - std::lock_guard _{context->mPropLock}; +try { if(!value1 || !value2 || !value3) - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else switch(param) + context->throw_error(AL_INVALID_VALUE, "NULL pointer"); + + ALlistener &listener = context->mListener; + std::lock_guard proplock{context->mPropLock}; + switch(param) { case AL_POSITION: *value1 = static_cast(listener.Position[0]); *value2 = static_cast(listener.Position[1]); *value3 = static_cast(listener.Position[2]); - break; + return; case AL_VELOCITY: *value1 = static_cast(listener.Velocity[0]); *value2 = static_cast(listener.Velocity[1]); *value3 = static_cast(listener.Velocity[2]); - break; - - default: - context->setError(AL_INVALID_ENUM, "Invalid listener 3-integer property"); + return; } + context->throw_error(AL_INVALID_ENUM, "Invalid listener 3-integer property {:#04x}", + as_unsigned(param)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC2(void, alGetListeneriv, ALenum, ALint*) +AL_API DECL_FUNC2(void, alGetListeneriv, ALenum,param, ALint*,values) FORCE_ALIGN void AL_APIENTRY alGetListenerivDirect(ALCcontext *context, ALenum param, ALint *values) noexcept -{ +try { + if(!values) + context->throw_error(AL_INVALID_VALUE, "NULL pointer"); + switch(param) { case AL_POSITION: case AL_VELOCITY: - alGetListener3iDirect(context, param, values+0, values+1, values+2); + auto vals = al::span{values, 3_uz}; + alGetListener3iDirect(context, param, &vals[0], &vals[1], &vals[2]); return; } ALlistener &listener = context->mListener; - std::lock_guard _{context->mPropLock}; - if(!values) - context->setError(AL_INVALID_VALUE, "NULL pointer"); - else switch(param) + std::lock_guard proplock{context->mPropLock}; + + static constexpr auto f2i = [](const float val) noexcept { return static_cast(val); }; + switch(param) { case AL_ORIENTATION: + auto vals = al::span{values, 6_uz}; // AT then UP - values[0] = static_cast(listener.OrientAt[0]); - values[1] = static_cast(listener.OrientAt[1]); - values[2] = static_cast(listener.OrientAt[2]); - values[3] = static_cast(listener.OrientUp[0]); - values[4] = static_cast(listener.OrientUp[1]); - values[5] = static_cast(listener.OrientUp[2]); - break; - - default: - context->setError(AL_INVALID_ENUM, "Invalid listener integer-vector property"); + std::transform(listener.OrientAt.cbegin(), listener.OrientAt.cend(), vals.begin(), f2i); + std::transform(listener.OrientUp.cbegin(), listener.OrientUp.cend(), vals.begin()+3, f2i); + return; } + context->throw_error(AL_INVALID_ENUM, "Invalid listener integer-vector property {:#04x}", + as_unsigned(param)); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } diff --git a/3rdparty/openal/al/listener.h b/3rdparty/openal/al/listener.h index 8153287715e9..7a4ff530f72e 100644 --- a/3rdparty/openal/al/listener.h +++ b/3rdparty/openal/al/listener.h @@ -3,8 +3,6 @@ #include -#include "AL/al.h" -#include "AL/alc.h" #include "AL/efx.h" #include "almalloc.h" @@ -18,7 +16,7 @@ struct ALlistener { float Gain{1.0f}; float mMetersPerUnit{AL_DEFAULT_METERS_PER_UNIT}; - DISABLE_ALLOC() + DISABLE_ALLOC }; #endif diff --git a/3rdparty/openal/al/source.cpp b/3rdparty/openal/al/source.cpp index 2fbd1703aeec..f045d1cd16d9 100644 --- a/3rdparty/openal/al/source.cpp +++ b/3rdparty/openal/al/source.cpp @@ -25,22 +25,24 @@ #include #include #include +#include #include #include -#include -#include #include #include -#include +#include #include #include #include #include -#include #include #include #include -#include +#include +#include +#include +#include +#include #include #include @@ -50,7 +52,6 @@ #include "AL/efx.h" #include "albit.h" -#include "alc/alu.h" #include "alc/backends/base.h" #include "alc/context.h" #include "alc/device.h" @@ -61,30 +62,35 @@ #include "atomic.h" #include "auxeffectslot.h" #include "buffer.h" -#include "core/ambidefs.h" -#include "core/bformatdec.h" +#include "core/buffer_storage.h" #include "core/except.h" -#include "core/filters/nfc.h" -#include "core/filters/splitter.h" #include "core/logging.h" +#include "core/mixer/defs.h" #include "core/voice_change.h" #include "direct_defs.h" -#include "event.h" #include "filter.h" +#include "flexarray.h" +#include "intrusive_ptr.h" #include "opthelpers.h" -#include "ringbuffer.h" -#ifdef ALSOFT_EAX -#include -#endif // ALSOFT_EAX - -bool sBufferSubDataCompat{false}; +#if ALSOFT_EAX +#include "eax/api.h" +#include "eax/call.h" +#include "eax/fx_slot_index.h" +#include "eax/utils.h" +#endif namespace { -using namespace std::placeholders; +using SubListAllocator = al::allocator>; using std::chrono::nanoseconds; using seconds_d = std::chrono::duration; +using source_store_array = std::array; +using source_store_vector = std::vector; +using source_store_variant = std::variant; + +using namespace std::string_view_literals; + Voice *GetSourceVoice(ALsource *source, ALCcontext *context) { @@ -97,7 +103,7 @@ Voice *GetSourceVoice(ALsource *source, ALCcontext *context) if(voice->mSourceID.load(std::memory_order_acquire) == sid) return voice; } - source->VoiceIdx = INVALID_VOICE_IDX; + source->VoiceIdx = InvalidVoiceIndex; return nullptr; } @@ -127,7 +133,7 @@ void UpdateSourceProps(const ALsource *source, Voice *voice, ALCcontext *context props->RefDistance = source->RefDistance; props->MaxDistance = source->MaxDistance; props->RolloffFactor = source->RolloffFactor -#ifdef ALSOFT_EAX +#if ALSOFT_EAX + source->RolloffFactor2 #endif ; @@ -155,6 +161,7 @@ void UpdateSourceProps(const ALsource *source, Voice *voice, ALCcontext *context props->Radius = source->Radius; props->EnhWidth = source->EnhWidth; + props->Panning = source->mPanningEnabled ? source->mPan : 0.0f; props->Direct.Gain = source->Direct.Gain; props->Direct.GainHF = source->Direct.GainHF; @@ -173,7 +180,7 @@ void UpdateSourceProps(const ALsource *source, Voice *voice, ALCcontext *context ret.LFReference = srcsend.LFReference; return ret; }; - std::transform(source->Send.cbegin(), source->Send.cend(), props->Send, copy_send); + std::transform(source->Send.cbegin(), source->Send.cend(), props->Send.begin(), copy_send); if(!props->Send[0].Slot && context->mDefaultSlot) props->Send[0].Slot = context->mDefaultSlot->mSlot; @@ -196,7 +203,7 @@ void UpdateSourceProps(const ALsource *source, Voice *voice, ALCcontext *context */ int64_t GetSourceSampleOffset(ALsource *Source, ALCcontext *context, nanoseconds *clocktime) { - ALCdevice *device{context->mALDevice.get()}; + auto *device = context->mALDevice.get(); const VoiceBufferItem *Current{}; int64_t readPos{}; uint refcount{}; @@ -204,7 +211,7 @@ int64_t GetSourceSampleOffset(ALsource *Source, ALCcontext *context, nanoseconds do { refcount = device->waitForMix(); - *clocktime = GetDeviceClockTime(device); + *clocktime = device->getClockTime(); voice = GetSourceVoice(Source, context); if(voice) { @@ -214,7 +221,7 @@ int64_t GetSourceSampleOffset(ALsource *Source, ALCcontext *context, nanoseconds readPos += voice->mPositionFrac.load(std::memory_order_relaxed); } std::atomic_thread_fence(std::memory_order_acquire); - } while(refcount != device->MixCount.load(std::memory_order_relaxed)); + } while(refcount != device->mMixCount.load(std::memory_order_relaxed)); if(!voice) return 0; @@ -236,7 +243,7 @@ int64_t GetSourceSampleOffset(ALsource *Source, ALCcontext *context, nanoseconds */ double GetSourceSecOffset(ALsource *Source, ALCcontext *context, nanoseconds *clocktime) { - ALCdevice *device{context->mALDevice.get()}; + auto *device = context->mALDevice.get(); const VoiceBufferItem *Current{}; int64_t readPos{}; uint refcount{}; @@ -244,7 +251,7 @@ double GetSourceSecOffset(ALsource *Source, ALCcontext *context, nanoseconds *cl do { refcount = device->waitForMix(); - *clocktime = GetDeviceClockTime(device); + *clocktime = device->getClockTime(); voice = GetSourceVoice(Source, context); if(voice) { @@ -254,7 +261,7 @@ double GetSourceSecOffset(ALsource *Source, ALCcontext *context, nanoseconds *cl readPos += voice->mPositionFrac.load(std::memory_order_relaxed); } std::atomic_thread_fence(std::memory_order_acquire); - } while(refcount != device->MixCount.load(std::memory_order_relaxed)); + } while(refcount != device->mMixCount.load(std::memory_order_relaxed)); if(!voice) return 0.0f; @@ -284,9 +291,9 @@ double GetSourceSecOffset(ALsource *Source, ALCcontext *context, nanoseconds *cl * queue (not the start of the current buffer). */ template -T GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context) +NOINLINE T GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context) { - ALCdevice *device{context->mALDevice.get()}; + auto *device = context->mALDevice.get(); const VoiceBufferItem *Current{}; int64_t readPos{}; uint readPosFrac{}; @@ -304,7 +311,7 @@ T GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context) readPosFrac = voice->mPositionFrac.load(std::memory_order_relaxed); } std::atomic_thread_fence(std::memory_order_acquire); - } while(refcount != device->MixCount.load(std::memory_order_relaxed)); + } while(refcount != device->mMixCount.load(std::memory_order_relaxed)); if(!voice) return T{0}; @@ -336,7 +343,7 @@ T GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context) else { readPos /= BufferFmt->mSampleRate; - offset = static_cast(clampi64(readPos, std::numeric_limits::min(), + offset = static_cast(std::clamp(readPos, std::numeric_limits::min(), std::numeric_limits::max())); } break; @@ -345,7 +352,7 @@ T GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context) if constexpr(std::is_floating_point_v) offset = static_cast(readPos) + static_cast(readPosFrac)/T{MixerFracOne}; else - offset = static_cast(clampi64(readPos, std::numeric_limits::min(), + offset = static_cast(std::clamp(readPos, std::numeric_limits::min(), std::numeric_limits::max())); break; @@ -377,7 +384,7 @@ T GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context) * format (Bytes, Samples or Seconds). */ template -T GetSourceLength(const ALsource *source, ALenum name) +NOINLINE T GetSourceLength(const ALsource *source, ALenum name) { uint64_t length{0}; const ALbuffer *BufferFmt{nullptr}; @@ -397,14 +404,14 @@ T GetSourceLength(const ALsource *source, ALenum name) if constexpr(std::is_floating_point_v) return static_cast(length) / static_cast(BufferFmt->mSampleRate); else - return static_cast(minu64(length/BufferFmt->mSampleRate, + return static_cast(std::min(length/BufferFmt->mSampleRate, std::numeric_limits::max())); case AL_SAMPLE_LENGTH_SOFT: if constexpr(std::is_floating_point_v) return static_cast(length); else - return static_cast(minu64(length, std::numeric_limits::max())); + return static_cast(std::min(length, std::numeric_limits::max())); case AL_BYTE_LENGTH_SOFT: const ALuint BlockSamples{BufferFmt->mBlockAlign}; @@ -416,7 +423,7 @@ T GetSourceLength(const ALsource *source, ALenum name) return static_cast(length); else { - if(length > std::numeric_limits::max()) + if(length > uint64_t{std::numeric_limits::max()}) return RoundDown(std::numeric_limits::max(), static_cast(BlockSize)); return static_cast(length); } @@ -438,7 +445,7 @@ struct VoicePos { * using the given offset type and offset. If the offset is out of range, * returns an empty optional. */ -std::optional GetSampleOffset(al::deque &BufferList, +std::optional GetSampleOffset(std::deque &BufferList, ALenum OffsetType, double Offset) { /* Find the first valid Buffer in the Queue */ @@ -469,7 +476,7 @@ std::optional GetSampleOffset(al::deque &BufferList dblfrac += 1.0; } offset = static_cast(dbloff); - frac = static_cast(mind(dblfrac*MixerFracOne, MixerFracOne-1.0)); + frac = static_cast(std::min(dblfrac*MixerFracOne, MixerFracOne-1.0)); break; case AL_SAMPLE_OFFSET: @@ -480,7 +487,7 @@ std::optional GetSampleOffset(al::deque &BufferList dblfrac += 1.0; } offset = static_cast(dbloff); - frac = static_cast(mind(dblfrac*MixerFracOne, MixerFracOne-1.0)); + frac = static_cast(std::min(dblfrac*MixerFracOne, MixerFracOne-1.0)); break; case AL_BYTE_OFFSET: @@ -521,16 +528,19 @@ std::optional GetSampleOffset(al::deque &BufferList void InitVoice(Voice *voice, ALsource *source, ALbufferQueueItem *BufferList, ALCcontext *context, - ALCdevice *device) + al::Device *device) { voice->mLoopBuffer.store(source->Looping ? &source->mQueue.front() : nullptr, std::memory_order_relaxed); ALbuffer *buffer{BufferList->mBuffer}; voice->mFrequency = buffer->mSampleRate; - voice->mFmtChannels = - (buffer->mChannels == FmtStereo && source->mStereoMode == SourceStereo::Enhanced) ? - FmtSuperStereo : buffer->mChannels; + if(buffer->mChannels == FmtMono && source->mPanningEnabled) + voice->mFmtChannels = FmtMonoDup; + else if(buffer->mChannels == FmtStereo && source->mStereoMode == SourceStereo::Enhanced) + voice->mFmtChannels = FmtSuperStereo; + else + voice->mFmtChannels = buffer->mChannels; voice->mFmtType = buffer->mType; voice->mFrameStep = buffer->channelsFromFmt(); voice->mBytesPerBlock = buffer->blockSizeFromFmt(); @@ -569,7 +579,7 @@ VoiceChange *GetVoiceChanger(ALCcontext *ctx) void SendVoiceChanges(ALCcontext *ctx, VoiceChange *tail) { - ALCdevice *device{ctx->mALDevice.get()}; + auto *device = ctx->mALDevice.get(); VoiceChange *oldhead{ctx->mCurrentVoiceChange.load(std::memory_order_acquire)}; while(VoiceChange *next{oldhead->mNext.load(std::memory_order_relaxed)}) @@ -577,7 +587,7 @@ void SendVoiceChanges(ALCcontext *ctx, VoiceChange *tail) oldhead->mNext.store(tail, std::memory_order_release); const bool connected{device->Connected.load(std::memory_order_acquire)}; - device->waitForMix(); + std::ignore = device->waitForMix(); if(!connected) UNLIKELY { if(ctx->mStopVoicesOnDisconnect.load(std::memory_order_acquire)) @@ -598,8 +608,8 @@ void SendVoiceChanges(ALCcontext *ctx, VoiceChange *tail) } -bool SetVoiceOffset(Voice *oldvoice, const VoicePos &vpos, ALsource *source, ALCcontext *context, - ALCdevice *device) +auto SetVoiceOffset(Voice *oldvoice, const VoicePos &vpos, ALsource *source, ALCcontext *context, + al::Device *device) -> bool { /* First, get a free voice to start at the new offset. */ auto voicelist = context->getVoicesSpan(); @@ -683,7 +693,7 @@ bool SetVoiceOffset(Voice *oldvoice, const VoicePos &vpos, ALsource *source, ALC return true; /* Otherwise, wait for any current mix to finish and check one last time. */ - device->waitForMix(); + std::ignore = device->waitForMix(); if(newvoice->mPlayState.load(std::memory_order_acquire) != Voice::Pending) return true; /* The change-over failed because the old voice stopped before the new @@ -723,26 +733,26 @@ bool EnsureSources(ALCcontext *context, size_t needed) [](size_t cur, const SourceSubList &sublist) noexcept -> size_t { return cur + static_cast(al::popcount(sublist.FreeMask)); })}; - while(needed > count) - { - if(context->mSourceList.size() >= 1<<25) UNLIKELY - return false; - - context->mSourceList.emplace_back(); - auto sublist = context->mSourceList.end() - 1; - sublist->FreeMask = ~0_u64; - sublist->Sources = static_cast(al_calloc(alignof(ALsource), sizeof(ALsource)*64)); - if(!sublist->Sources) UNLIKELY + try { + while(needed > count) { - context->mSourceList.pop_back(); - return false; + if(context->mSourceList.size() >= 1<<25) UNLIKELY + return false; + + SourceSubList sublist{}; + sublist.FreeMask = ~0_u64; + sublist.Sources = SubListAllocator{}.allocate(1); + context->mSourceList.emplace_back(std::move(sublist)); + count += std::tuple_size_v; } - count += 64; + } + catch(...) { + return false; } return true; } -ALsource *AllocSource(ALCcontext *context) +ALsource *AllocSource(ALCcontext *context) noexcept { auto sublist = std::find_if(context->mSourceList.begin(), context->mSourceList.end(), [](const SourceSubList &entry) noexcept -> bool @@ -751,7 +761,10 @@ ALsource *AllocSource(ALCcontext *context) auto slidx = static_cast(al::countr_zero(sublist->FreeMask)); ASSUME(slidx < 64); - ALsource *source{al::construct_at(sublist->Sources + slidx)}; + ALsource *source{al::construct_at(al::to_address(sublist->Sources->begin() + slidx))}; +#if ALSOFT_EAX + source->eaxInitialize(context); +#endif // ALSOFT_EAX /* Add 1 to avoid source ID 0. */ source->id = ((lidx<<6) | slidx) + 1; @@ -799,10 +812,10 @@ inline ALsource *LookupSource(ALCcontext *context, ALuint id) noexcept SourceSubList &sublist{context->mSourceList[lidx]}; if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY return nullptr; - return sublist.Sources + slidx; + return al::to_address(sublist.Sources->begin() + slidx); } -auto LookupBuffer = [](ALCdevice *device, auto id) noexcept -> ALbuffer* +auto LookupBuffer = [](al::Device *device, auto id) noexcept -> ALbuffer* { const auto lidx{(id-1) >> 6}; const auto slidx{(id-1) & 0x3f}; @@ -812,10 +825,10 @@ auto LookupBuffer = [](ALCdevice *device, auto id) noexcept -> ALbuffer* BufferSubList &sublist = device->BufferList[static_cast(lidx)]; if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY return nullptr; - return sublist.Buffers + static_cast(slidx); + return al::to_address(sublist.Buffers->begin() + static_cast(slidx)); }; -auto LookupFilter = [](ALCdevice *device, auto id) noexcept -> ALfilter* +auto LookupFilter = [](al::Device *device, auto id) noexcept -> ALfilter* { const auto lidx{(id-1) >> 6}; const auto slidx{(id-1) & 0x3f}; @@ -825,7 +838,7 @@ auto LookupFilter = [](ALCdevice *device, auto id) noexcept -> ALfilter* FilterSubList &sublist = device->FilterList[static_cast(lidx)]; if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY return nullptr; - return sublist.Filters + static_cast(slidx); + return al::to_address(sublist.Filters->begin() + static_cast(slidx)); }; auto LookupEffectSlot = [](ALCcontext *context, auto id) noexcept -> ALeffectslot* @@ -838,7 +851,7 @@ auto LookupEffectSlot = [](ALCcontext *context, auto id) noexcept -> ALeffectslo EffectSlotSubList &sublist{context->mEffectSlotList[static_cast(lidx)]}; if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY return nullptr; - return sublist.EffectSlots + static_cast(slidx); + return al::to_address(sublist.EffectSlots->begin() + static_cast(slidx)); }; @@ -879,7 +892,8 @@ ALenum EnumFromSpatializeMode(SpatializeMode mode) case SpatializeMode::On: return AL_TRUE; case SpatializeMode::Auto: return AL_AUTO_SOFT; } - throw std::runtime_error{"Invalid SpatializeMode: "+std::to_string(int(mode))}; + throw std::runtime_error{fmt::format("Invalid SpatializeMode: {}", + int{al::to_underlying(mode)})}; } auto DirectModeFromEnum = [](auto mode) noexcept -> std::optional @@ -900,7 +914,7 @@ ALenum EnumFromDirectMode(DirectMode mode) case DirectMode::DropMismatch: return AL_DROP_UNMATCHED_SOFT; case DirectMode::RemixMismatch: return AL_REMIX_UNMATCHED_SOFT; } - throw std::runtime_error{"Invalid DirectMode: "+std::to_string(int(mode))}; + throw std::runtime_error{fmt::format("Invalid DirectMode: {}", int{al::to_underlying(mode)})}; } auto DistanceModelFromALenum = [](auto model) noexcept -> std::optional @@ -929,7 +943,8 @@ ALenum ALenumFromDistanceModel(DistanceModel model) case DistanceModel::Exponent: return AL_EXPONENT_DISTANCE; case DistanceModel::ExponentClamped: return AL_EXPONENT_DISTANCE_CLAMPED; } - throw std::runtime_error{"Unexpected distance model "+std::to_string(static_cast(model))}; + throw std::runtime_error{fmt::format("Unexpected distance model: {}", + int{al::to_underlying(model)})}; } enum SourceProp : ALenum { @@ -1011,6 +1026,10 @@ enum SourceProp : ALenum { /* AL_SOFT_buffer_sub_data */ srcByteRWOffsetsSOFT = AL_BYTE_RW_OFFSETS_SOFT, srcSampleRWOffsetsSOFT = AL_SAMPLE_RW_OFFSETS_SOFT, + + /* AL_SOFT_source_panning */ + srcPanningEnabledSOFT = AL_PANNING_ENABLED_SOFT, + srcPanSOFT = AL_PAN_SOFT, }; @@ -1038,6 +1057,8 @@ constexpr ALuint IntValsByProp(ALenum prop) case AL_SOURCE_RESAMPLER_SOFT: case AL_SOURCE_SPATIALIZE_SOFT: case AL_STEREO_MODE_SOFT: + case AL_PANNING_ENABLED_SOFT: + case AL_PAN_SOFT: return 1; case AL_SOURCE_RADIUS: /*AL_BYTE_RW_OFFSETS_SOFT:*/ @@ -1115,6 +1136,8 @@ constexpr ALuint Int64ValsByProp(ALenum prop) case AL_SOURCE_RESAMPLER_SOFT: case AL_SOURCE_SPATIALIZE_SOFT: case AL_STEREO_MODE_SOFT: + case AL_PANNING_ENABLED_SOFT: + case AL_PAN_SOFT: return 1; case AL_SOURCE_RADIUS: /*AL_BYTE_RW_OFFSETS_SOFT:*/ @@ -1208,6 +1231,8 @@ constexpr ALuint FloatValsByProp(ALenum prop) case AL_SEC_LENGTH_SOFT: case AL_STEREO_MODE_SOFT: case AL_SUPER_STEREO_WIDTH_SOFT: + case AL_PANNING_ENABLED_SOFT: + case AL_PAN_SOFT: return 1; case AL_SOURCE_RADIUS: /*AL_BYTE_RW_OFFSETS_SOFT:*/ @@ -1281,6 +1306,8 @@ constexpr ALuint DoubleValsByProp(ALenum prop) case AL_SEC_LENGTH_SOFT: case AL_STEREO_MODE_SOFT: case AL_SUPER_STEREO_WIDTH_SOFT: + case AL_PANNING_ENABLED_SOFT: + case AL_PAN_SOFT: return 1; case AL_SOURCE_RADIUS: /*AL_BYTE_RW_OFFSETS_SOFT:*/ @@ -1315,18 +1342,6 @@ constexpr ALuint DoubleValsByProp(ALenum prop) } -struct check_exception : std::exception { -}; -struct check_size_exception final : check_exception { - const char *what() const noexcept override - { return "check_size_exception"; } -}; -struct check_value_exception final : check_exception { - const char *what() const noexcept override - { return "check_value_exception"; } -}; - - void UpdateSourceProps(ALsource *source, ALCcontext *context) { if(!context->mDeferUpdates) @@ -1339,7 +1354,7 @@ void UpdateSourceProps(ALsource *source, ALCcontext *context) } source->mPropsDirty = true; } -#ifdef ALSOFT_EAX +#if ALSOFT_EAX void CommitAndUpdateSourceProps(ALsource *source, ALCcontext *context) { if(!context->mDeferUpdates) @@ -1363,70 +1378,56 @@ inline void CommitAndUpdateSourceProps(ALsource *source, ALCcontext *context) template -struct PropType { }; +auto PropTypeName() -> std::string_view = delete; template<> -struct PropType { static const char *Name() { return "integer"; } }; +auto PropTypeName() -> std::string_view { return "integer"sv; } template<> -struct PropType { static const char *Name() { return "int64"; } }; +auto PropTypeName() -> std::string_view { return "int64"sv; } template<> -struct PropType { static const char *Name() { return "float"; } }; +auto PropTypeName() -> std::string_view { return "float"sv; } template<> -struct PropType { static const char *Name() { return "double"; } }; - -template -struct HexPrinter { - char mStr[sizeof(T)*2 + 3]{}; - HexPrinter(T value) - { - using ST = std::make_signed_t>; - if constexpr(std::is_same_v) - std::snprintf(mStr, std::size(mStr), "0x%x", value); - else if constexpr(std::is_same_v) - std::snprintf(mStr, std::size(mStr), "0x%lx", value); - else if constexpr(std::is_same_v) - std::snprintf(mStr, std::size(mStr), "0x%llx", value); - } - - const char *c_str() const noexcept { return mStr; } -}; +auto PropTypeName() -> std::string_view { return "double"sv; } /** * Returns a pair of lambdas to check the following setter. * * The first lambda checks the size of the span is valid for the required size, - * setting the proper context error and throwing a check_size_exception if it - * fails. + * throwing a context error if it fails. * - * The second lambda tests the validity of the value check, setting the proper - * context error and throwing a check_value_exception if it failed. + * The second lambda tests the validity of the value check, throwing a context + * error if it failed. */ +template +struct PairStruct { T First; U Second; }; +template +PairStruct(T,U) -> PairStruct; + template -auto GetCheckers(ALCcontext *const Context, const SourceProp prop, const al::span values) +auto GetCheckers(ALCcontext *context, const SourceProp prop, const al::span values) { - return std::make_pair( + return PairStruct{ [=](size_t expect) -> void { - if(values.size() == expect) LIKELY return; - Context->setError(AL_INVALID_ENUM, "Property 0x%04x expects %zu value(s), got %zu", - prop, expect, values.size()); - throw check_size_exception{}; + if(values.size() == expect) return; + context->throw_error(AL_INVALID_ENUM, "Property {:#04x} expects {} value{}, got {}", + as_unsigned(al::to_underlying(prop)), expect, (expect==1) ? "" : "s", + values.size()); }, - [Context](bool passed) -> void + [context](bool passed) -> void { - if(passed) LIKELY return; - Context->setError(AL_INVALID_VALUE, "Value out of range"); - throw check_value_exception{}; + if(passed) return; + context->throw_error(AL_INVALID_VALUE, "Value out of range"); } - ); + }; } template NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, const SourceProp prop, - const al::span values) try + const al::span values) { - auto&& [CheckSize, CheckValue] = GetCheckers(Context, prop, values); - ALCdevice *device{Context->mALDevice.get()}; + auto [CheckSize, CheckValue] = GetCheckers(Context, prop, values); + auto *device = Context->mALDevice.get(); switch(prop) { @@ -1437,8 +1438,8 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con if constexpr(std::is_integral_v) { /* Query only */ - return Context->setError(AL_INVALID_OPERATION, - "Setting read-only source property 0x%04x", prop); + Context->throw_error(AL_INVALID_OPERATION, + "Setting read-only source property {:#04x}", as_unsigned(al::to_underlying(prop))); } break; @@ -1450,12 +1451,15 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con case AL_SAMPLE_OFFSET_CLOCK_SOFT: case AL_SEC_OFFSET_CLOCK_SOFT: /* Query only */ - return Context->setError(AL_INVALID_OPERATION, - "Setting read-only source property 0x%04x", prop); + Context->throw_error(AL_INVALID_OPERATION, "Setting read-only source property {:#04x}", + as_unsigned(al::to_underlying(prop))); case AL_PITCH: CheckSize(1); - CheckValue(values[0] >= T{0}); + if constexpr(std::is_floating_point_v) + CheckValue(values[0] >= T{0} && std::isfinite(static_cast(values[0]))); + else + CheckValue(values[0] >= T{0}); Source->Pitch = static_cast(values[0]); return UpdateSourceProps(Source, Context); @@ -1476,42 +1480,60 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con case AL_GAIN: CheckSize(1); - CheckValue(values[0] >= T{0}); + if constexpr(std::is_floating_point_v) + CheckValue(values[0] >= T{0} && std::isfinite(static_cast(values[0]))); + else + CheckValue(values[0] >= T{0}); Source->Gain = static_cast(values[0]); return UpdateSourceProps(Source, Context); case AL_MAX_DISTANCE: CheckSize(1); - CheckValue(values[0] >= T{0}); + if constexpr(std::is_floating_point_v) + CheckValue(values[0] >= T{0} && std::isfinite(static_cast(values[0]))); + else + CheckValue(values[0] >= T{0}); Source->MaxDistance = static_cast(values[0]); return CommitAndUpdateSourceProps(Source, Context); case AL_ROLLOFF_FACTOR: CheckSize(1); - CheckValue(values[0] >= T{0}); + if constexpr(std::is_floating_point_v) + CheckValue(values[0] >= T{0} && std::isfinite(static_cast(values[0]))); + else + CheckValue(values[0] >= T{0}); Source->RolloffFactor = static_cast(values[0]); return CommitAndUpdateSourceProps(Source, Context); case AL_REFERENCE_DISTANCE: CheckSize(1); - CheckValue(values[0] >= T{0}); + if constexpr(std::is_floating_point_v) + CheckValue(values[0] >= T{0} && std::isfinite(static_cast(values[0]))); + else + CheckValue(values[0] >= T{0}); Source->RefDistance = static_cast(values[0]); return CommitAndUpdateSourceProps(Source, Context); case AL_MIN_GAIN: CheckSize(1); - CheckValue(values[0] >= T{0}); + if constexpr(std::is_floating_point_v) + CheckValue(values[0] >= T{0} && std::isfinite(static_cast(values[0]))); + else + CheckValue(values[0] >= T{0}); Source->MinGain = static_cast(values[0]); return UpdateSourceProps(Source, Context); case AL_MAX_GAIN: CheckSize(1); - CheckValue(values[0] >= T{0}); + if constexpr(std::is_floating_point_v) + CheckValue(values[0] >= T{0} && std::isfinite(static_cast(values[0]))); + else + CheckValue(values[0] >= T{0}); Source->MaxGain = static_cast(values[0]); return UpdateSourceProps(Source, Context); @@ -1581,7 +1603,7 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con * to ensure it isn't currently looping back or reaching the * end. */ - device->waitForMix(); + std::ignore = device->waitForMix(); } return; } @@ -1591,30 +1613,28 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con if constexpr(std::is_integral_v) { CheckSize(1); - { - const ALenum state{GetSourceState(Source, GetSourceVoice(Source, Context))}; - if(state == AL_PLAYING || state == AL_PAUSED) - return Context->setError(AL_INVALID_OPERATION, - "Setting buffer on playing or paused source %u", Source->id); - } - al::deque oldlist; + if(const ALenum state{GetSourceState(Source, GetSourceVoice(Source, Context))}; + state == AL_PLAYING || state == AL_PAUSED) + Context->throw_error(AL_INVALID_OPERATION, + "Setting buffer on playing or paused source {}", Source->id); + + std::deque oldlist; if(values[0]) { using UT = std::make_unsigned_t; - std::lock_guard _{device->BufferLock}; + std::lock_guard buflock{device->BufferLock}; ALbuffer *buffer{LookupBuffer(device, static_cast(values[0]))}; - if(!buffer) UNLIKELY - return Context->setError(AL_INVALID_VALUE, "Invalid buffer ID %s", - std::to_string(values[0]).c_str()); - if(buffer->MappedAccess && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT)) UNLIKELY - return Context->setError(AL_INVALID_OPERATION, - "Setting non-persistently mapped buffer %u", buffer->id); - if(buffer->mCallback && ReadRef(buffer->ref) != 0) UNLIKELY - return Context->setError(AL_INVALID_OPERATION, - "Setting already-set callback buffer %u", buffer->id); + if(!buffer) + Context->throw_error(AL_INVALID_VALUE, "Invalid buffer ID {}", values[0]); + if(buffer->MappedAccess && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT)) + Context->throw_error(AL_INVALID_OPERATION, + "Setting non-persistently mapped buffer {}", buffer->id); + if(buffer->mCallback && buffer->ref.load(std::memory_order_relaxed) != 0) + Context->throw_error(AL_INVALID_OPERATION, + "Setting already-set callback buffer {}", buffer->id); /* Add the selected buffer to a one-item queue */ - al::deque newlist; + std::deque newlist; newlist.emplace_back(); newlist.back().mCallback = buffer->mCallback; newlist.back().mUserData = buffer->mUserData; @@ -1622,7 +1642,7 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con newlist.back().mSampleLen = buffer->mSampleLen; newlist.back().mLoopStart = buffer->mLoopStart; newlist.back().mLoopEnd = buffer->mLoopEnd; - newlist.back().mSamples = buffer->mData.data(); + newlist.back().mSamples = buffer->mData; newlist.back().mBuffer = buffer; IncrementRef(buffer->ref); @@ -1659,7 +1679,7 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con if(Voice *voice{GetSourceVoice(Source, Context)}) { auto vpos = GetSampleOffset(Source->mQueue, prop, static_cast(values[0])); - if(!vpos) return Context->setError(AL_INVALID_VALUE, "Invalid offset"); + if(!vpos) Context->throw_error(AL_INVALID_VALUE, "Invalid offset"); if(SetVoiceOffset(voice, *vpos, Source, Context, Context->mALDevice.get())) return; @@ -1674,8 +1694,9 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con if constexpr(std::is_integral_v) { /* Query only */ - return Context->setError(AL_INVALID_OPERATION, - "Setting read-only source property 0x%04x", prop); + Context->throw_error(AL_INVALID_OPERATION, + "Setting read-only source property {:#04x}", + as_unsigned(al::to_underlying(prop))); } } break; @@ -1686,8 +1707,9 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con if constexpr(std::is_integral_v) { /* Query only */ - return Context->setError(AL_INVALID_OPERATION, - "Setting read-only source property 0x%04x", prop); + Context->throw_error(AL_INVALID_OPERATION, + "Setting read-only source property {:#04x}", + as_unsigned(al::to_underlying(prop))); } break; } @@ -1707,6 +1729,25 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con Source->EnhWidth = static_cast(values[0]); return UpdateSourceProps(Source, Context); + case AL_PANNING_ENABLED_SOFT: + CheckSize(1); + if(const ALenum state{GetSourceState(Source, GetSourceVoice(Source, Context))}; + state == AL_PLAYING || state == AL_PAUSED) + Context->throw_error(AL_INVALID_OPERATION, + "Modifying panning enabled on playing or paused source {}", Source->id); + + CheckValue(values[0] == AL_FALSE || values[0] == AL_TRUE); + + Source->mPanningEnabled = values[0] != AL_FALSE; + return UpdateSourceProps(Source, Context); + + case AL_PAN_SOFT: + CheckSize(1); + CheckValue(values[0] >= T{-1} && values[0] <= T{1}); + + Source->mPan = static_cast(values[0]); + return UpdateSourceProps(Source, Context); + case AL_STEREO_ANGLES: CheckSize(2); if constexpr(std::is_floating_point_v) @@ -1780,11 +1821,10 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con const auto filterid = static_cast>(values[0]); if(values[0]) { - std::lock_guard _{device->FilterLock}; + std::lock_guard filterlock{device->FilterLock}; ALfilter *filter{LookupFilter(device, filterid)}; - if(!filter) UNLIKELY - return Context->setError(AL_INVALID_VALUE, "Invalid filter ID %s", - std::to_string(filterid).c_str()); + if(!filter) + Context->throw_error(AL_INVALID_VALUE, "Invalid filter ID {}", filterid); Source->Direct.Gain = filter->Gain; Source->Direct.GainHF = filter->GainHF; Source->Direct.HFReference = filter->HFReference; @@ -1795,9 +1835,9 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con { Source->Direct.Gain = 1.0f; Source->Direct.GainHF = 1.0f; - Source->Direct.HFReference = LOWPASSFREQREF; + Source->Direct.HFReference = LowPassFreqRef; Source->Direct.GainLF = 1.0f; - Source->Direct.LFReference = HIGHPASSFREQREF; + Source->Direct.LFReference = HighPassFreqRef; } return UpdateSourceProps(Source, Context); } @@ -1845,8 +1885,8 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con Source->DirectChannels = *mode; return UpdateSourceProps(Source, Context); } - return Context->setError(AL_INVALID_VALUE, "Invalid direct channels mode: %s\n", - HexPrinter{values[0]}.c_str()); + Context->throw_error(AL_INVALID_VALUE, "Invalid direct channels mode: {:#x}", + as_unsigned(values[0])); } break; @@ -1861,8 +1901,8 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con UpdateSourceProps(Source, Context); return; } - return Context->setError(AL_INVALID_VALUE, "Invalid distance model: %s\n", - HexPrinter{values[0]}.c_str()); + Context->throw_error(AL_INVALID_VALUE, "Invalid distance model: {:#x}", + as_unsigned(values[0])); } break; @@ -1886,8 +1926,8 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con Source->mSpatialize = *mode; return UpdateSourceProps(Source, Context); } - return Context->setError(AL_INVALID_VALUE, "Invalid source spatialize mode: %s\n", - HexPrinter{values[0]}.c_str()); + Context->throw_error(AL_INVALID_VALUE, "Invalid source spatialize mode: {}", + values[0]); } break; @@ -1895,19 +1935,18 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con if constexpr(std::is_integral_v) { CheckSize(1); - { - const ALenum state{GetSourceState(Source, GetSourceVoice(Source, Context))}; - if(state == AL_PLAYING || state == AL_PAUSED) - return Context->setError(AL_INVALID_OPERATION, - "Modifying stereo mode on playing or paused source %u", Source->id); - } + if(const ALenum state{GetSourceState(Source, GetSourceVoice(Source, Context))}; + state == AL_PLAYING || state == AL_PAUSED) + Context->throw_error(AL_INVALID_OPERATION, + "Modifying stereo mode on playing or paused source {}", Source->id); + if(auto mode = StereoModeFromEnum(values[0])) { Source->mStereoMode = *mode; return; } - return Context->setError(AL_INVALID_VALUE, "Invalid stereo mode: %s\n", - HexPrinter{values[0]}.c_str()); + Context->throw_error(AL_INVALID_VALUE, "Invalid stereo mode: {:#x}", + as_unsigned(values[0])); } break; @@ -1923,23 +1962,21 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con ALeffectslot *slot{}; if(values[0]) { - if((slot=LookupEffectSlot(Context, slotid)) == nullptr) UNLIKELY - return Context->setError(AL_INVALID_VALUE, "Invalid effect ID %s", - std::to_string(slotid).c_str()); + slot = LookupEffectSlot(Context, slotid); + if(!slot) + Context->throw_error(AL_INVALID_VALUE, "Invalid effect ID {}", slotid); } - if(sendidx >= device->NumAuxSends) UNLIKELY - return Context->setError(AL_INVALID_VALUE, "Invalid send %s", - std::to_string(sendidx).c_str()); + if(sendidx >= device->NumAuxSends) + Context->throw_error(AL_INVALID_VALUE, "Invalid send {}", sendidx); auto &send = Source->Send[static_cast(sendidx)]; if(values[2]) { - std::lock_guard _{device->FilterLock}; + std::lock_guard filterlock{device->FilterLock}; ALfilter *filter{LookupFilter(device, filterid)}; - if(!filter) UNLIKELY - return Context->setError(AL_INVALID_VALUE, "Invalid filter ID %s", - std::to_string(filterid).c_str()); + if(!filter) + Context->throw_error(AL_INVALID_VALUE, "Invalid filter ID {}", filterid); send.Gain = filter->Gain; send.GainHF = filter->GainHF; @@ -1952,9 +1989,9 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con /* Disable filter */ send.Gain = 1.0f; send.GainHF = 1.0f; - send.HFReference = LOWPASSFREQREF; + send.HFReference = LowPassFreqRef; send.GainLF = 1.0f; - send.LFReference = HIGHPASSFREQREF; + send.LFReference = HighPassFreqRef; } /* We must force an update if the current auxiliary slot is valid @@ -1986,113 +2023,108 @@ NOINLINE void SetProperty(ALsource *const Source, ALCcontext *const Context, con break; } - ERR("Unexpected %s property: 0x%04x\n", PropType::Name(), prop); - Context->setError(AL_INVALID_ENUM, "Invalid source %s property 0x%04x", PropType::Name(), - prop); -} -catch(check_exception&) { + Context->throw_error(AL_INVALID_ENUM, "Invalid source {} property {:#04x}", PropTypeName(), + as_unsigned(al::to_underlying(prop))); } template -auto GetSizeChecker(ALCcontext *const Context, const SourceProp prop, const al::span values) +auto GetSizeChecker(ALCcontext *context, const SourceProp prop, const al::span values) { return [=](size_t expect) -> void { if(values.size() == expect) LIKELY return; - Context->setError(AL_INVALID_ENUM, "Property 0x%04x expects %zu value(s), got %zu", - prop, expect, values.size()); - throw check_size_exception{}; + context->throw_error(AL_INVALID_ENUM, "Property {:#04x} expects {} value{}, got {}", + as_unsigned(al::to_underlying(prop)), expect, (expect==1) ? "" : "s", values.size()); }; } template -[[nodiscard]] NOINLINE -bool GetProperty(ALsource *const Source, ALCcontext *const Context, const SourceProp prop, - const al::span values) try +NOINLINE void GetProperty(ALsource *const Source, ALCcontext *const Context, const SourceProp prop, + const al::span values) { using std::chrono::duration_cast; auto CheckSize = GetSizeChecker(Context, prop, values); - ALCdevice *device{Context->mALDevice.get()}; + auto *device = Context->mALDevice.get(); switch(prop) { case AL_GAIN: CheckSize(1); values[0] = static_cast(Source->Gain); - return true; + return; case AL_PITCH: CheckSize(1); values[0] = static_cast(Source->Pitch); - return true; + return; case AL_MAX_DISTANCE: CheckSize(1); values[0] = static_cast(Source->MaxDistance); - return true; + return; case AL_ROLLOFF_FACTOR: CheckSize(1); values[0] = static_cast(Source->RolloffFactor); - return true; + return; case AL_REFERENCE_DISTANCE: CheckSize(1); values[0] = static_cast(Source->RefDistance); - return true; + return; case AL_CONE_INNER_ANGLE: CheckSize(1); values[0] = static_cast(Source->InnerAngle); - return true; + return; case AL_CONE_OUTER_ANGLE: CheckSize(1); values[0] = static_cast(Source->OuterAngle); - return true; + return; case AL_MIN_GAIN: CheckSize(1); values[0] = static_cast(Source->MinGain); - return true; + return; case AL_MAX_GAIN: CheckSize(1); values[0] = static_cast(Source->MaxGain); - return true; + return; case AL_CONE_OUTER_GAIN: CheckSize(1); values[0] = static_cast(Source->OuterGain); - return true; + return; case AL_SEC_OFFSET: case AL_SAMPLE_OFFSET: case AL_BYTE_OFFSET: CheckSize(1); values[0] = GetSourceOffset(Source, prop, Context); - return true; + return; case AL_CONE_OUTER_GAINHF: CheckSize(1); values[0] = static_cast(Source->OuterGainHF); - return true; + return; case AL_AIR_ABSORPTION_FACTOR: CheckSize(1); values[0] = static_cast(Source->AirAbsorptionFactor); - return true; + return; case AL_ROOM_ROLLOFF_FACTOR: CheckSize(1); values[0] = static_cast(Source->RoomRolloffFactor); - return true; + return; case AL_DOPPLER_FACTOR: CheckSize(1); values[0] = static_cast(Source->DopplerFactor); - return true; + return; case AL_SAMPLE_RW_OFFSETS_SOFT: if constexpr(std::is_integral_v) @@ -2106,7 +2138,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source * buffer queue. */ values[1] = values[0]; - return true; + return; } } break; @@ -2118,7 +2150,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source CheckSize(1); values[0] = static_cast(Source->Radius); - return true; + return; } else { @@ -2131,7 +2163,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source * buffer queue. */ values[1] = values[0]; - return true; + return; } break; } @@ -2139,14 +2171,24 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source case AL_SUPER_STEREO_WIDTH_SOFT: CheckSize(1); values[0] = static_cast(Source->EnhWidth); - return true; + return; case AL_BYTE_LENGTH_SOFT: case AL_SAMPLE_LENGTH_SOFT: case AL_SEC_LENGTH_SOFT: CheckSize(1); values[0] = GetSourceLength(Source, prop); - return true; + return; + + case AL_PANNING_ENABLED_SOFT: + CheckSize(1); + values[0] = Source->mPanningEnabled; + return; + + case AL_PAN_SOFT: + CheckSize(1); + values[0] = static_cast(Source->mPan); + return; case AL_STEREO_ANGLES: if constexpr(std::is_floating_point_v) @@ -2154,7 +2196,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source CheckSize(2); values[0] = static_cast(Source->StereoPan[0]); values[1] = static_cast(Source->StereoPan[1]); - return true; + return; } break; @@ -2169,7 +2211,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source nanoseconds srcclock{}; values[0] = GetSourceSampleOffset(Source, Context, &srcclock); { - std::lock_guard _{device->StateLock}; + std::lock_guard statelock{device->StateLock}; clocktime = GetClockLatency(device, device->Backend.get()); } if(srcclock == clocktime.ClockTime) @@ -2183,7 +2225,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source const auto diff = std::min(clocktime.Latency, clocktime.ClockTime-srcclock); values[1] = nanoseconds{clocktime.Latency - diff}.count(); } - return true; + return; } break; @@ -2194,7 +2236,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source nanoseconds srcclock{}; values[0] = GetSourceSampleOffset(Source, Context, &srcclock); values[1] = srcclock.count(); - return true; + return; } break; @@ -2209,7 +2251,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source nanoseconds srcclock{}; values[0] = GetSourceSecOffset(Source, Context, &srcclock); { - std::lock_guard _{device->StateLock}; + std::lock_guard statelock{device->StateLock}; clocktime = GetClockLatency(device, device->Backend.get()); } if(srcclock == clocktime.ClockTime) @@ -2223,7 +2265,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source const auto diff = std::min(clocktime.Latency, clocktime.ClockTime-srcclock); values[1] = duration_cast(clocktime.Latency - diff).count(); } - return true; + return; } break; @@ -2234,7 +2276,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source nanoseconds srcclock{}; values[0] = GetSourceSecOffset(Source, Context, &srcclock); values[1] = duration_cast(srcclock).count(); - return true; + return; } break; @@ -2243,21 +2285,21 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source values[0] = static_cast(Source->Position[0]); values[1] = static_cast(Source->Position[1]); values[2] = static_cast(Source->Position[2]); - return true; + return; case AL_VELOCITY: CheckSize(3); values[0] = static_cast(Source->Velocity[0]); values[1] = static_cast(Source->Velocity[1]); values[2] = static_cast(Source->Velocity[2]); - return true; + return; case AL_DIRECTION: CheckSize(3); values[0] = static_cast(Source->Direction[0]); values[1] = static_cast(Source->Direction[1]); values[2] = static_cast(Source->Direction[2]); - return true; + return; case AL_ORIENTATION: CheckSize(6); @@ -2267,7 +2309,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source values[3] = static_cast(Source->OrientUp[0]); values[4] = static_cast(Source->OrientUp[1]); values[5] = static_cast(Source->OrientUp[2]); - return true; + return; case AL_SOURCE_RELATIVE: @@ -2275,7 +2317,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source { CheckSize(1); values[0] = Source->HeadRelative; - return true; + return; } break; @@ -2284,7 +2326,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source { CheckSize(1); values[0] = Source->Looping; - return true; + return; } break; @@ -2292,7 +2334,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source if constexpr(std::is_integral_v) { CheckSize(1); - ALbufferQueueItem *BufferList{}; + const ALbufferQueueItem *BufferList{}; /* HACK: This query should technically only return the buffer set * on a static source. However, some apps had used it to detect * when a streaming source changed buffers, so report the current @@ -2306,11 +2348,14 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source else if(Voice *voice{GetSourceVoice(Source, Context)}) { VoiceBufferItem *Current{voice->mCurrentBuffer.load(std::memory_order_relaxed)}; - BufferList = static_cast(Current); + const auto iter = std::find_if(Source->mQueue.cbegin(), Source->mQueue.cend(), + [Current](const ALbufferQueueItem &item) noexcept -> bool + { return &item == Current; }); + BufferList = (iter != Source->mQueue.cend()) ? al::to_address(iter) : nullptr; } ALbuffer *buffer{BufferList ? BufferList->mBuffer : nullptr}; values[0] = buffer ? static_cast(buffer->id) : T{0}; - return true; + return; } break; @@ -2319,7 +2364,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source { CheckSize(1); values[0] = GetSourceState(Source, GetSourceVoice(Source, Context)); - return true; + return; } break; @@ -2328,7 +2373,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source { CheckSize(1); values[0] = static_cast(Source->mQueue.size()); - return true; + return; } break; @@ -2360,7 +2405,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source } values[0] = played; } - return true; + return; } break; @@ -2369,7 +2414,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source { CheckSize(1); values[0] = Source->SourceType; - return true; + return; } break; @@ -2378,7 +2423,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source { CheckSize(1); values[0] = Source->DryGainHFAuto; - return true; + return; } break; @@ -2387,7 +2432,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source { CheckSize(1); values[0] = Source->WetGainAuto; - return true; + return; } break; @@ -2396,7 +2441,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source { CheckSize(1); values[0] = Source->WetGainHFAuto; - return true; + return; } break; @@ -2405,7 +2450,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source { CheckSize(1); values[0] = EnumFromDirectMode(Source->DirectChannels); - return true; + return; } break; @@ -2414,7 +2459,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source { CheckSize(1); values[0] = ALenumFromDistanceModel(Source->mDistanceModel); - return true; + return; } break; @@ -2423,7 +2468,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source { CheckSize(1); values[0] = static_cast(Source->mResampler); - return true; + return; } break; @@ -2432,7 +2477,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source { CheckSize(1); values[0] = EnumFromSpatializeMode(Source->mSpatialize); - return true; + return; } break; @@ -2441,7 +2486,7 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source { CheckSize(1); values[0] = EnumFromStereoMode(Source->mStereoMode); - return true; + return; } break; @@ -2450,20 +2495,15 @@ bool GetProperty(ALsource *const Source, ALCcontext *const Context, const Source break; } - ERR("Unexpected %s query property: 0x%04x\n", PropType::Name(), prop); - Context->setError(AL_INVALID_ENUM, "Invalid source %s query property 0x%04x", - PropType::Name(), prop); - return false; -} -catch(check_exception&) { - return false; + Context->throw_error(AL_INVALID_ENUM, "Invalid source {} query property {:#04x}", + PropTypeName(), as_unsigned(al::to_underlying(prop))); } void StartSources(ALCcontext *const context, const al::span srchandles, const nanoseconds start_time=nanoseconds::min()) { - ALCdevice *device{context->mALDevice.get()}; + auto *device = context->mALDevice.get(); /* If the device is disconnected, and voices stop on disconnect, go right * to stopped. */ @@ -2552,7 +2592,7 @@ void StartSources(ALCcontext *const context, const al::span srchandle cur->mSourceID = source->id; cur->mState = VChangeState::Play; source->state = AL_PLAYING; -#ifdef ALSOFT_EAX +#if ALSOFT_EAX if(context->hasEax()) source->eaxCommit(); #endif // ALSOFT_EAX @@ -2572,7 +2612,7 @@ void StartSources(ALCcontext *const context, const al::span srchandle default: assert(voice == nullptr); cur->mOldVoice = nullptr; -#ifdef ALSOFT_EAX +#if ALSOFT_EAX if(context->hasEax()) source->eaxCommit(); #endif // ALSOFT_EAX @@ -2631,592 +2671,700 @@ void StartSources(ALCcontext *const context, const al::span srchandle } // namespace -AL_API DECL_FUNC2(void, alGenSources, ALsizei, ALuint*) +AL_API DECL_FUNC2(void, alGenSources, ALsizei,n, ALuint*,sources) FORCE_ALIGN void AL_APIENTRY alGenSourcesDirect(ALCcontext *context, ALsizei n, ALuint *sources) noexcept -{ - if(n < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Generating %d sources", n); +try { + if(n < 0) + context->throw_error(AL_INVALID_VALUE, "Generating {} sources", n); if(n <= 0) UNLIKELY return; - std::unique_lock srclock{context->mSourceLock}; - ALCdevice *device{context->mALDevice.get()}; - if(static_cast(n) > device->SourcesMax-context->mNumSources) - { - context->setError(AL_OUT_OF_MEMORY, "Exceeding %u source limit (%u + %d)", - device->SourcesMax, context->mNumSources, n); - return; - } - if(!EnsureSources(context, static_cast(n))) - { - context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d source%s", n, (n==1)?"":"s"); - return; - } + auto srclock = std::unique_lock{context->mSourceLock}; + auto *device = context->mALDevice.get(); - if(n == 1) - { - ALsource *source{AllocSource(context)}; - sources[0] = source->id; + const al::span sids{sources, static_cast(n)}; + if(context->mNumSources > device->SourcesMax + || sids.size() > device->SourcesMax-context->mNumSources) + context->throw_error(AL_OUT_OF_MEMORY, "Exceeding {} source limit ({} + {})", + device->SourcesMax, context->mNumSources, n); + if(!EnsureSources(context, sids.size())) + context->throw_error(AL_OUT_OF_MEMORY, "Failed to allocate {} source{}", n, + (n==1) ? "" : "s"); -#ifdef ALSOFT_EAX - source->eaxInitialize(context); -#endif // ALSOFT_EAX - } - else - { - std::vector ids; - ids.reserve(static_cast(n)); - do { - ALsource *source{AllocSource(context)}; - ids.emplace_back(source->id); - -#ifdef ALSOFT_EAX - source->eaxInitialize(context); -#endif // ALSOFT_EAX - } while(--n); - std::copy(ids.cbegin(), ids.cend(), sources); - } + std::generate(sids.begin(), sids.end(), [context]{ return AllocSource(context)->id; }); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC2(void, alDeleteSources, ALsizei, const ALuint*) +AL_API DECL_FUNC2(void, alDeleteSources, ALsizei,n, const ALuint*,sources) FORCE_ALIGN void AL_APIENTRY alDeleteSourcesDirect(ALCcontext *context, ALsizei n, const ALuint *sources) noexcept -{ - if(n < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Deleting %d sources", n); +try { + if(n < 0) + context->throw_error(AL_INVALID_VALUE, "Deleting {} sources", n); if(n <= 0) UNLIKELY return; - std::lock_guard _{context->mSourceLock}; + std::lock_guard srclock{context->mSourceLock}; /* Check that all Sources are valid */ auto validate_source = [context](const ALuint sid) -> bool { return LookupSource(context, sid) != nullptr; }; - const ALuint *sources_end = sources + n; - auto invsrc = std::find_if_not(sources, sources_end, validate_source); - if(invsrc != sources_end) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", *invsrc); + const al::span sids{sources, static_cast(n)}; + auto invsrc = std::find_if_not(sids.begin(), sids.end(), validate_source); + if(invsrc != sids.end()) + context->throw_error(AL_INVALID_NAME, "Invalid source ID {}", *invsrc); /* All good. Delete source IDs. */ auto delete_source = [&context](const ALuint sid) -> void { - ALsource *src{LookupSource(context, sid)}; - if(src) FreeSource(context, src); + if(ALsource *src{LookupSource(context, sid)}) + FreeSource(context, src); }; - std::for_each(sources, sources_end, delete_source); + std::for_each(sids.begin(), sids.end(), delete_source); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC1(ALboolean, alIsSource, ALuint) +AL_API DECL_FUNC1(ALboolean, alIsSource, ALuint,source) FORCE_ALIGN ALboolean AL_APIENTRY alIsSourceDirect(ALCcontext *context, ALuint source) noexcept { - std::lock_guard _{context->mSourceLock}; + std::lock_guard srclock{context->mSourceLock}; if(LookupSource(context, source) != nullptr) return AL_TRUE; return AL_FALSE; } -AL_API DECL_FUNC3(void, alSourcef, ALuint, ALenum, ALfloat) +AL_API DECL_FUNC3(void, alSourcef, ALuint,source, ALenum,param, ALfloat,value) FORCE_ALIGN void AL_APIENTRY alSourcefDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat value) noexcept -{ - std::lock_guard _{context->mPropLock}; - std::lock_guard __{context->mSourceLock}; - ALsource *Source = LookupSource(context, source); - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); +try { + std::lock_guard proplock{context->mPropLock}; + std::lock_guard sourcelock{context->mSourceLock}; + ALsource *Source{LookupSource(context, source)}; + if(!Source) + context->throw_error(AL_INVALID_NAME, "Invalid source ID {}", source); - SetProperty(Source, context, static_cast(param), al::span{&value, 1u}); + SetProperty(Source, context, static_cast(param), {&value, 1u}); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC5(void, alSource3f, ALuint, ALenum, ALfloat, ALfloat, ALfloat) +AL_API DECL_FUNC5(void, alSource3f, ALuint,source, ALenum,param, ALfloat,value1, ALfloat,value2, ALfloat,value3) FORCE_ALIGN void AL_APIENTRY alSource3fDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) noexcept -{ - std::lock_guard _{context->mPropLock}; - std::lock_guard __{context->mSourceLock}; - ALsource *Source = LookupSource(context, source); - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); +try { + std::lock_guard proplock{context->mPropLock}; + std::lock_guard sourcelock{context->mSourceLock}; + ALsource *Source{LookupSource(context, source)}; + if(!Source) + context->throw_error(AL_INVALID_NAME, "Invalid source ID {}", source); - const float fvals[3]{ value1, value2, value3 }; - SetProperty(Source, context, static_cast(param), al::span{fvals}); + const std::array fvals{value1, value2, value3}; + SetProperty(Source, context, static_cast(param), fvals); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC3(void, alSourcefv, ALuint, ALenum, const ALfloat*) +AL_API DECL_FUNC3(void, alSourcefv, ALuint,source, ALenum,param, const ALfloat*,values) FORCE_ALIGN void AL_APIENTRY alSourcefvDirect(ALCcontext *context, ALuint source, ALenum param, const ALfloat *values) noexcept -{ - std::lock_guard _{context->mPropLock}; - std::lock_guard __{context->mSourceLock}; - ALsource *Source = LookupSource(context, source); - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - if(!values) UNLIKELY - return context->setError(AL_INVALID_VALUE, "NULL pointer"); +try { + std::lock_guard proplock{context->mPropLock}; + std::lock_guard sourcelock{context->mSourceLock}; + ALsource *Source{LookupSource(context, source)}; + if(!Source) + context->throw_error(AL_INVALID_NAME, "Invalid source ID {}", source); + if(!values) + context->throw_error(AL_INVALID_VALUE, "NULL pointer"); const ALuint count{FloatValsByProp(param)}; SetProperty(Source, context, static_cast(param), al::span{values, count}); } +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); +} -AL_API DECL_FUNCEXT3(void, alSourced,SOFT, ALuint, ALenum, ALdouble) +AL_API DECL_FUNCEXT3(void, alSourced,SOFT, ALuint,source, ALenum,param, ALdouble,value) FORCE_ALIGN void AL_APIENTRY alSourcedDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble value) noexcept -{ - std::lock_guard _{context->mPropLock}; - std::lock_guard __{context->mSourceLock}; - ALsource *Source = LookupSource(context, source); - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); +try { + std::lock_guard proplock{context->mPropLock}; + std::lock_guard sourcelock{context->mSourceLock}; + ALsource *Source{LookupSource(context, source)}; + if(!Source) + context->throw_error(AL_INVALID_NAME, "Invalid source ID {}", source); - SetProperty(Source, context, static_cast(param), al::span{&value, 1}); + SetProperty(Source, context, static_cast(param), {&value, 1}); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNCEXT5(void, alSource3d,SOFT, ALuint, ALenum, ALdouble, ALdouble, ALdouble) +AL_API DECL_FUNCEXT5(void, alSource3d,SOFT, ALuint,source, ALenum,param, ALdouble,value1, ALdouble,value2, ALdouble,value3) FORCE_ALIGN void AL_APIENTRY alSource3dDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3) noexcept -{ - std::lock_guard _{context->mPropLock}; - std::lock_guard __{context->mSourceLock}; - ALsource *Source = LookupSource(context, source); - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); +try { + std::lock_guard proplock{context->mPropLock}; + std::lock_guard sourcelock{context->mSourceLock}; + ALsource *Source{LookupSource(context, source)}; + if(!Source) + context->throw_error(AL_INVALID_NAME, "Invalid source ID {}", source); - const double dvals[3]{value1, value2, value3}; - SetProperty(Source, context, static_cast(param), al::span{dvals}); + const std::array dvals{value1, value2, value3}; + SetProperty(Source, context, static_cast(param), dvals); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNCEXT3(void, alSourcedv,SOFT, ALuint, ALenum, const ALdouble*) +AL_API DECL_FUNCEXT3(void, alSourcedv,SOFT, ALuint,source, ALenum,param, const ALdouble*,values) FORCE_ALIGN void AL_APIENTRY alSourcedvDirectSOFT(ALCcontext *context, ALuint source, ALenum param, const ALdouble *values) noexcept -{ - std::lock_guard _{context->mPropLock}; - std::lock_guard __{context->mSourceLock}; - ALsource *Source = LookupSource(context, source); - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - if(!values) UNLIKELY - return context->setError(AL_INVALID_VALUE, "NULL pointer"); +try { + std::lock_guard proplock{context->mPropLock}; + std::lock_guard sourcelock{context->mSourceLock}; + ALsource *Source{LookupSource(context, source)}; + if(!Source) + context->throw_error(AL_INVALID_NAME, "Invalid source ID {}", source); + if(!values) + context->throw_error(AL_INVALID_VALUE, "NULL pointer"); const ALuint count{DoubleValsByProp(param)}; SetProperty(Source, context, static_cast(param), al::span{values, count}); } +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); +} -AL_API DECL_FUNC3(void, alSourcei, ALuint, ALenum, ALint) +AL_API DECL_FUNC3(void, alSourcei, ALuint,source, ALenum,param, ALint,value) FORCE_ALIGN void AL_APIENTRY alSourceiDirect(ALCcontext *context, ALuint source, ALenum param, ALint value) noexcept -{ - std::lock_guard _{context->mPropLock}; - std::lock_guard __{context->mSourceLock}; - ALsource *Source = LookupSource(context, source); - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); +try { + std::lock_guard proplock{context->mPropLock}; + std::lock_guard sourcelock{context->mSourceLock}; + ALsource *Source{LookupSource(context, source)}; + if(!Source) + context->throw_error(AL_INVALID_NAME, "Invalid source ID {}", source); - SetProperty(Source, context, static_cast(param), al::span{&value, 1u}); + SetProperty(Source, context, static_cast(param), {&value, 1u}); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC5(void, alSource3i, ALuint, ALenum, ALint, ALint, ALint) +AL_API DECL_FUNC5(void, alSource3i, ALuint,buffer, ALenum,param, ALint,value1, ALint,value2, ALint,value3) FORCE_ALIGN void AL_APIENTRY alSource3iDirect(ALCcontext *context, ALuint source, ALenum param, ALint value1, ALint value2, ALint value3) noexcept -{ - std::lock_guard _{context->mPropLock}; - std::lock_guard __{context->mSourceLock}; - ALsource *Source = LookupSource(context, source); - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); +try { + std::lock_guard proplock{context->mPropLock}; + std::lock_guard sourcelock{context->mSourceLock}; + ALsource *Source{LookupSource(context, source)}; + if(!Source) + context->throw_error(AL_INVALID_NAME, "Invalid source ID {}", source); - const int ivals[3]{ value1, value2, value3 }; - SetProperty(Source, context, static_cast(param), al::span{ivals}); + const std::array ivals{value1, value2, value3}; + SetProperty(Source, context, static_cast(param), ivals); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC3(void, alSourceiv, ALuint, ALenum, const ALint*) +AL_API DECL_FUNC3(void, alSourceiv, ALuint,source, ALenum,param, const ALint*,values) FORCE_ALIGN void AL_APIENTRY alSourceivDirect(ALCcontext *context, ALuint source, ALenum param, const ALint *values) noexcept -{ - std::lock_guard _{context->mPropLock}; - std::lock_guard __{context->mSourceLock}; - ALsource *Source = LookupSource(context, source); - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - if(!values) UNLIKELY - return context->setError(AL_INVALID_VALUE, "NULL pointer"); +try { + std::lock_guard proplock{context->mPropLock}; + std::lock_guard sourcelock{context->mSourceLock}; + ALsource *Source{LookupSource(context, source)}; + if(!Source) + context->throw_error(AL_INVALID_NAME, "Invalid source ID {}", source); + if(!values) + context->throw_error(AL_INVALID_VALUE, "NULL pointer"); const ALuint count{IntValsByProp(param)}; SetProperty(Source, context, static_cast(param), al::span{values, count}); } +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); +} -AL_API DECL_FUNCEXT3(void, alSourcei64,SOFT, ALuint, ALenum, ALint64SOFT) +AL_API DECL_FUNCEXT3(void, alSourcei64,SOFT, ALuint,source, ALenum,param, ALint64SOFT,value) FORCE_ALIGN void AL_APIENTRY alSourcei64DirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT value) noexcept -{ - std::lock_guard _{context->mPropLock}; - std::lock_guard __{context->mSourceLock}; +try { + std::lock_guard proplock{context->mPropLock}; + std::lock_guard sourcelock{context->mSourceLock}; ALsource *Source{LookupSource(context, source)}; - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); + if(!Source) + context->throw_error(AL_INVALID_NAME, "Invalid source ID {}", source); - SetProperty(Source, context, static_cast(param), al::span{&value, 1u}); + SetProperty(Source, context, static_cast(param), {&value, 1u}); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNCEXT5(void, alSource3i64,SOFT, ALuint, ALenum, ALint64SOFT, ALint64SOFT, ALint64SOFT) +AL_API DECL_FUNCEXT5(void, alSource3i64,SOFT, ALuint,source, ALenum,param, ALint64SOFT,value1, ALint64SOFT,value2, ALint64SOFT,value3) FORCE_ALIGN void AL_APIENTRY alSource3i64DirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3) noexcept -{ - std::lock_guard _{context->mPropLock}; - std::lock_guard __{context->mSourceLock}; +try { + std::lock_guard proplock{context->mPropLock}; + std::lock_guard sourcelock{context->mSourceLock}; ALsource *Source{LookupSource(context, source)}; - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); + if(!Source) + context->throw_error(AL_INVALID_NAME, "Invalid source ID {}", source); - const int64_t i64vals[3]{ value1, value2, value3 }; - SetProperty(Source, context, static_cast(param), al::span{i64vals}); + const std::array i64vals{value1, value2, value3}; + SetProperty(Source, context, static_cast(param), i64vals); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNCEXT3(void, alSourcei64v,SOFT, ALuint, ALenum, const ALint64SOFT*) +AL_API DECL_FUNCEXT3(void, alSourcei64v,SOFT, ALuint,source, ALenum,param, const ALint64SOFT*,values) FORCE_ALIGN void AL_APIENTRY alSourcei64vDirectSOFT(ALCcontext *context, ALuint source, ALenum param, const ALint64SOFT *values) noexcept -{ - std::lock_guard _{context->mPropLock}; - std::lock_guard __{context->mSourceLock}; +try { + std::lock_guard proplock{context->mPropLock}; + std::lock_guard sourcelock{context->mSourceLock}; ALsource *Source{LookupSource(context, source)}; - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - if(!values) UNLIKELY - return context->setError(AL_INVALID_VALUE, "NULL pointer"); + if(!Source) + context->throw_error(AL_INVALID_NAME, "Invalid source ID {}", source); + if(!values) + context->throw_error(AL_INVALID_VALUE, "NULL pointer"); const ALuint count{Int64ValsByProp(param)}; SetProperty(Source, context, static_cast(param), al::span{values, count}); } +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); +} -AL_API DECL_FUNC3(void, alGetSourcef, ALuint, ALenum, ALfloat*) +AL_API DECL_FUNC3(void, alGetSourcef, ALuint,source, ALenum,param, ALfloat*,value) FORCE_ALIGN void AL_APIENTRY alGetSourcefDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat *value) noexcept -{ - std::lock_guard _{context->mSourceLock}; +try { + std::lock_guard sourcelock{context->mSourceLock}; ALsource *Source{LookupSource(context, source)}; - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - if(!value) UNLIKELY - return context->setError(AL_INVALID_VALUE, "NULL pointer"); + if(!Source) + context->throw_error(AL_INVALID_NAME, "Invalid source ID {}", source); + if(!value) + context->throw_error(AL_INVALID_VALUE, "NULL pointer"); - std::ignore = GetProperty(Source, context, static_cast(param), al::span{value, 1}); + GetProperty(Source, context, static_cast(param), al::span{value, 1u}); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC5(void, alGetSource3f, ALuint, ALenum, ALfloat*, ALfloat*, ALfloat*) +AL_API DECL_FUNC5(void, alGetSource3f, ALuint,source, ALenum,param, ALfloat*,value1, ALfloat*,value2, ALfloat*,value3) FORCE_ALIGN void AL_APIENTRY alGetSource3fDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) noexcept -{ - std::lock_guard _{context->mSourceLock}; +try { + std::lock_guard sourcelock{context->mSourceLock}; ALsource *Source{LookupSource(context, source)}; - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - if(!(value1 && value2 && value3)) UNLIKELY - return context->setError(AL_INVALID_VALUE, "NULL pointer"); + if(!Source) + context->throw_error(AL_INVALID_NAME, "Invalid source ID {}", source); + if(!(value1 && value2 && value3)) + context->throw_error(AL_INVALID_VALUE, "NULL pointer"); - float fvals[3]; - if(GetProperty(Source, context, static_cast(param), al::span{fvals})) - { - *value1 = fvals[0]; - *value2 = fvals[1]; - *value3 = fvals[2]; - } + std::array fvals{}; + GetProperty(Source, context, static_cast(param), fvals); + *value1 = fvals[0]; + *value2 = fvals[1]; + *value3 = fvals[2]; +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC3(void, alGetSourcefv, ALuint, ALenum, ALfloat*) +AL_API DECL_FUNC3(void, alGetSourcefv, ALuint,source, ALenum,param, ALfloat*,values) FORCE_ALIGN void AL_APIENTRY alGetSourcefvDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat *values) noexcept -{ - std::lock_guard _{context->mSourceLock}; +try { + std::lock_guard sourcelock{context->mSourceLock}; ALsource *Source{LookupSource(context, source)}; - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - if(!values) UNLIKELY - return context->setError(AL_INVALID_VALUE, "NULL pointer"); + if(!Source) + context->throw_error(AL_INVALID_NAME, "Invalid source ID {}", source); + if(!values) + context->throw_error(AL_INVALID_VALUE, "NULL pointer"); const ALuint count{FloatValsByProp(param)}; - std::ignore = GetProperty(Source, context, static_cast(param), - al::span{values, count}); + GetProperty(Source, context, static_cast(param), al::span{values, count}); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNCEXT3(void, alGetSourced,SOFT, ALuint, ALenum, ALdouble*) +AL_API DECL_FUNCEXT3(void, alGetSourced,SOFT, ALuint,source, ALenum,param, ALdouble*,value) FORCE_ALIGN void AL_APIENTRY alGetSourcedDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble *value) noexcept -{ - std::lock_guard _{context->mSourceLock}; +try { + std::lock_guard sourcelock{context->mSourceLock}; ALsource *Source{LookupSource(context, source)}; - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - if(!value) UNLIKELY - return context->setError(AL_INVALID_VALUE, "NULL pointer"); + if(!Source) + context->throw_error(AL_INVALID_NAME, "Invalid source ID {}", source); + if(!value) + context->throw_error(AL_INVALID_VALUE, "NULL pointer"); - std::ignore = GetProperty(Source, context, static_cast(param), al::span{value, 1}); + GetProperty(Source, context, static_cast(param), al::span{value, 1u}); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNCEXT5(void, alGetSource3d,SOFT, ALuint, ALenum, ALdouble*, ALdouble*, ALdouble*) +AL_API DECL_FUNCEXT5(void, alGetSource3d,SOFT, ALuint,source, ALenum,param, ALdouble*,value1, ALdouble*,value2, ALdouble*,value3) FORCE_ALIGN void AL_APIENTRY alGetSource3dDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3) noexcept -{ - std::lock_guard _{context->mSourceLock}; +try { + std::lock_guard sourcelock{context->mSourceLock}; ALsource *Source{LookupSource(context, source)}; - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - if(!(value1 && value2 && value3)) UNLIKELY - return context->setError(AL_INVALID_VALUE, "NULL pointer"); + if(!Source) + context->throw_error(AL_INVALID_NAME, "Invalid source ID {}", source); + if(!(value1 && value2 && value3)) + context->throw_error(AL_INVALID_VALUE, "NULL pointer"); - double dvals[3]; - if(GetProperty(Source, context, static_cast(param), al::span{dvals})) - { - *value1 = dvals[0]; - *value2 = dvals[1]; - *value3 = dvals[2]; - } + std::array dvals{}; + GetProperty(Source, context, static_cast(param), dvals); + *value1 = dvals[0]; + *value2 = dvals[1]; + *value3 = dvals[2]; +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNCEXT3(void, alGetSourcedv,SOFT, ALuint, ALenum, ALdouble*) +AL_API DECL_FUNCEXT3(void, alGetSourcedv,SOFT, ALuint,source, ALenum,param, ALdouble*,values) FORCE_ALIGN void AL_APIENTRY alGetSourcedvDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble *values) noexcept -{ - std::lock_guard _{context->mSourceLock}; +try { + std::lock_guard sourcelock{context->mSourceLock}; ALsource *Source{LookupSource(context, source)}; - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - if(!values) UNLIKELY - return context->setError(AL_INVALID_VALUE, "NULL pointer"); + if(!Source) + context->throw_error(AL_INVALID_NAME, "Invalid source ID {}", source); + if(!values) + context->throw_error(AL_INVALID_VALUE, "NULL pointer"); const ALuint count{DoubleValsByProp(param)}; - std::ignore = GetProperty(Source, context, static_cast(param), - al::span{values, count}); + GetProperty(Source, context, static_cast(param), al::span{values, count}); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC3(void, alGetSourcei, ALuint, ALenum, ALint*) +AL_API DECL_FUNC3(void, alGetSourcei, ALuint,source, ALenum,param, ALint*,value) FORCE_ALIGN void AL_APIENTRY alGetSourceiDirect(ALCcontext *context, ALuint source, ALenum param, ALint *value) noexcept -{ - std::lock_guard _{context->mSourceLock}; +try { + std::lock_guard sourcelock{context->mSourceLock}; ALsource *Source{LookupSource(context, source)}; - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - if(!value) UNLIKELY - return context->setError(AL_INVALID_VALUE, "NULL pointer"); + if(!Source) + context->throw_error(AL_INVALID_NAME, "Invalid source ID {}", source); + if(!value) + context->throw_error(AL_INVALID_VALUE, "NULL pointer"); - std::ignore = GetProperty(Source, context, static_cast(param), al::span{value, 1}); + GetProperty(Source, context, static_cast(param), al::span{value, 1u}); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC5(void, alGetSource3i, ALuint, ALenum, ALint*, ALint*, ALint*) +AL_API DECL_FUNC5(void, alGetSource3i, ALuint,source, ALenum,param, ALint*,value1, ALint*,value2, ALint*,value3) FORCE_ALIGN void AL_APIENTRY alGetSource3iDirect(ALCcontext *context, ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3) noexcept -{ - std::lock_guard _{context->mSourceLock}; +try { + std::lock_guard sourcelock{context->mSourceLock}; ALsource *Source{LookupSource(context, source)}; - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - if(!(value1 && value2 && value3)) UNLIKELY - return context->setError(AL_INVALID_VALUE, "NULL pointer"); - - int ivals[3]; - if(GetProperty(Source, context, static_cast(param), al::span{ivals})) - { - *value1 = ivals[0]; - *value2 = ivals[1]; - *value3 = ivals[2]; - } + if(!Source) + context->throw_error(AL_INVALID_NAME, "Invalid source ID {}", source); + if(!(value1 && value2 && value3)) + context->throw_error(AL_INVALID_VALUE, "NULL pointer"); + + std::array ivals{}; + GetProperty(Source, context, static_cast(param), ivals); + *value1 = ivals[0]; + *value2 = ivals[1]; + *value3 = ivals[2]; +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC3(void, alGetSourceiv, ALuint, ALenum, ALint*) +AL_API DECL_FUNC3(void, alGetSourceiv, ALuint,source, ALenum,param, ALint*,values) FORCE_ALIGN void AL_APIENTRY alGetSourceivDirect(ALCcontext *context, ALuint source, ALenum param, ALint *values) noexcept -{ - std::lock_guard _{context->mSourceLock}; +try { + std::lock_guard sourcelock{context->mSourceLock}; ALsource *Source{LookupSource(context, source)}; - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - if(!values) UNLIKELY - return context->setError(AL_INVALID_VALUE, "NULL pointer"); + if(!Source) + context->throw_error(AL_INVALID_NAME, "Invalid source ID {}", source); + if(!values) + context->throw_error(AL_INVALID_VALUE, "NULL pointer"); const ALuint count{IntValsByProp(param)}; - std::ignore = GetProperty(Source, context, static_cast(param), - al::span{values, count}); + GetProperty(Source, context, static_cast(param), al::span{values, count}); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNCEXT3(void, alGetSourcei64,SOFT, ALuint, ALenum, ALint64SOFT*) +AL_API DECL_FUNCEXT3(void, alGetSourcei64,SOFT, ALuint,source, ALenum,param, ALint64SOFT*,value) FORCE_ALIGN void AL_APIENTRY alGetSourcei64DirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT *value) noexcept -{ - std::lock_guard _{context->mSourceLock}; +try { + std::lock_guard sourcelock{context->mSourceLock}; ALsource *Source{LookupSource(context, source)}; - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - if(!value) UNLIKELY - return context->setError(AL_INVALID_VALUE, "NULL pointer"); + if(!Source) + context->throw_error(AL_INVALID_NAME, "Invalid source ID {}", source); + if(!value) + context->throw_error(AL_INVALID_VALUE, "NULL pointer"); - std::ignore = GetProperty(Source, context, static_cast(param), al::span{value, 1}); + GetProperty(Source, context, static_cast(param), al::span{value, 1u}); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNCEXT5(void, alGetSource3i64,SOFT, ALuint, ALenum, ALint64SOFT*, ALint64SOFT*, ALint64SOFT*) +AL_API DECL_FUNCEXT5(void, alGetSource3i64,SOFT, ALuint,source, ALenum,param, ALint64SOFT*,value1, ALint64SOFT*,value2, ALint64SOFT*,value3) FORCE_ALIGN void AL_APIENTRY alGetSource3i64DirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3) noexcept -{ - std::lock_guard _{context->mSourceLock}; +try { + std::lock_guard sourcelock{context->mSourceLock}; ALsource *Source{LookupSource(context, source)}; - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - if(!(value1 && value2 && value3)) UNLIKELY - return context->setError(AL_INVALID_VALUE, "NULL pointer"); + if(!Source) + context->throw_error(AL_INVALID_NAME, "Invalid source ID {}", source); + if(!(value1 && value2 && value3)) + context->throw_error(AL_INVALID_VALUE, "NULL pointer"); - int64_t i64vals[3]; - if(GetProperty(Source, context, static_cast(param), al::span{i64vals})) - { - *value1 = i64vals[0]; - *value2 = i64vals[1]; - *value3 = i64vals[2]; - } + std::array i64vals{}; + GetProperty(Source, context, static_cast(param), i64vals); + *value1 = i64vals[0]; + *value2 = i64vals[1]; + *value3 = i64vals[2]; +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNCEXT3(void, alGetSourcei64v,SOFT, ALuint, ALenum, ALint64SOFT*) +AL_API DECL_FUNCEXT3(void, alGetSourcei64v,SOFT, ALuint,source, ALenum,param, ALint64SOFT*,values) FORCE_ALIGN void AL_APIENTRY alGetSourcei64vDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT *values) noexcept -{ - std::lock_guard _{context->mSourceLock}; +try { + std::lock_guard sourcelock{context->mSourceLock}; ALsource *Source{LookupSource(context, source)}; - if(!Source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); - if(!values) UNLIKELY - return context->setError(AL_INVALID_VALUE, "NULL pointer"); + if(!Source) + context->throw_error(AL_INVALID_NAME, "Invalid source ID {}", source); + if(!values) + context->throw_error(AL_INVALID_VALUE, "NULL pointer"); const ALuint count{Int64ValsByProp(param)}; - std::ignore = GetProperty(Source, context, static_cast(param), - al::span{values, count}); + GetProperty(Source, context, static_cast(param), al::span{values, count}); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC1(void, alSourcePlay, ALuint) +AL_API DECL_FUNC1(void, alSourcePlay, ALuint,source) FORCE_ALIGN void AL_APIENTRY alSourcePlayDirect(ALCcontext *context, ALuint source) noexcept -{ - std::lock_guard _{context->mSourceLock}; - ALsource *srchandle{LookupSource(context, source)}; - if(!srchandle) - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); +try { + std::lock_guard sourcelock{context->mSourceLock}; + ALsource *Source{LookupSource(context, source)}; + if(!Source) + context->throw_error(AL_INVALID_NAME, "Invalid source ID {}", source); - StartSources(context, {&srchandle, 1}); + StartSources(context, {&Source, 1}); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -FORCE_ALIGN DECL_FUNCEXT2(void, alSourcePlayAtTime,SOFT, ALuint, ALint64SOFT) +FORCE_ALIGN DECL_FUNCEXT2(void, alSourcePlayAtTime,SOFT, ALuint,source, ALint64SOFT,start_time) FORCE_ALIGN void AL_APIENTRY alSourcePlayAtTimeDirectSOFT(ALCcontext *context, ALuint source, ALint64SOFT start_time) noexcept -{ - if(start_time < 0) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Invalid time point %" PRId64, start_time); +try { + if(start_time < 0) + context->throw_error(AL_INVALID_VALUE, "Invalid time point {}", start_time); - std::lock_guard _{context->mSourceLock}; - ALsource *srchandle{LookupSource(context, source)}; - if(!srchandle) - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", source); + std::lock_guard sourcelock{context->mSourceLock}; + ALsource *Source{LookupSource(context, source)}; + if(!Source) + context->throw_error(AL_INVALID_NAME, "Invalid source ID {}", source); - StartSources(context, {&srchandle, 1}, nanoseconds{start_time}); + StartSources(context, {&Source, 1}, nanoseconds{start_time}); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } -AL_API DECL_FUNC2(void, alSourcePlayv, ALsizei, const ALuint*) +AL_API DECL_FUNC2(void, alSourcePlayv, ALsizei,n, const ALuint*,sources) FORCE_ALIGN void AL_APIENTRY alSourcePlayvDirect(ALCcontext *context, ALsizei n, const ALuint *sources) noexcept -{ - if(n < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Playing %d sources", n); +try { + if(n < 0) + context->throw_error(AL_INVALID_VALUE, "Playing {} sources", n); if(n <= 0) UNLIKELY return; - std::vector extra_sources; - std::array source_storage; - al::span srchandles; - if(static_cast(n) <= source_storage.size()) LIKELY - srchandles = al::span{source_storage}.first(static_cast(n)); - else + al::span sids{sources, static_cast(n)}; + source_store_variant source_store; + const auto srchandles = [&source_store](size_t count) -> al::span { - extra_sources.resize(static_cast(n)); - srchandles = extra_sources; - } + if(count > std::tuple_size_v) + return al::span{source_store.emplace(count)}; + return al::span{source_store.emplace()}.first(count); + }(sids.size()); - std::lock_guard _{context->mSourceLock}; - for(auto &srchdl : srchandles) + std::lock_guard sourcelock{context->mSourceLock}; + auto lookup_src = [context](const ALuint sid) -> ALsource* { - srchdl = LookupSource(context, *sources); - if(!srchdl) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", *sources); - ++sources; - } + if(ALsource *src{LookupSource(context, sid)}) + return src; + context->throw_error(AL_INVALID_NAME, "Invalid source ID {}", sid); + }; + std::transform(sids.cbegin(), sids.cend(), srchandles.begin(), lookup_src); StartSources(context, srchandles); } +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); +} -FORCE_ALIGN DECL_FUNCEXT3(void, alSourcePlayAtTimev,SOFT, ALsizei, const ALuint*, ALint64SOFT) +FORCE_ALIGN DECL_FUNCEXT3(void, alSourcePlayAtTimev,SOFT, ALsizei,n, const ALuint*,sources, ALint64SOFT,start_time) FORCE_ALIGN void AL_APIENTRY alSourcePlayAtTimevDirectSOFT(ALCcontext *context, ALsizei n, const ALuint *sources, ALint64SOFT start_time) noexcept -{ - if(n < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Playing %d sources", n); +try { + if(n < 0) + context->throw_error(AL_INVALID_VALUE, "Playing {} sources", n); if(n <= 0) UNLIKELY return; - if(start_time < 0) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Invalid time point %" PRId64, start_time); + if(start_time < 0) + context->throw_error(AL_INVALID_VALUE, "Invalid time point {}", start_time); - std::vector extra_sources; - std::array source_storage; - al::span srchandles; - if(static_cast(n) <= source_storage.size()) LIKELY - srchandles = al::span{source_storage}.first(static_cast(n)); - else + al::span sids{sources, static_cast(n)}; + source_store_variant source_store; + const auto srchandles = [&source_store](size_t count) -> al::span { - extra_sources.resize(static_cast(n)); - srchandles = extra_sources; - } + if(count > std::tuple_size_v) + return al::span{source_store.emplace(count)}; + return al::span{source_store.emplace()}.first(count); + }(sids.size()); - std::lock_guard _{context->mSourceLock}; - for(auto &srchdl : srchandles) + std::lock_guard sourcelock{context->mSourceLock}; + auto lookup_src = [context](const ALuint sid) -> ALsource* { - srchdl = LookupSource(context, *sources); - if(!srchdl) - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", *sources); - ++sources; - } + if(ALsource *src{LookupSource(context, sid)}) + return src; + context->throw_error(AL_INVALID_NAME, "Invalid source ID {}", sid); + }; + std::transform(sids.cbegin(), sids.cend(), srchandles.begin(), lookup_src); StartSources(context, srchandles, nanoseconds{start_time}); } +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); +} -AL_API DECL_FUNC1(void, alSourcePause, ALuint) +AL_API DECL_FUNC1(void, alSourcePause, ALuint,source) FORCE_ALIGN void AL_APIENTRY alSourcePauseDirect(ALCcontext *context, ALuint source) noexcept { alSourcePausevDirect(context, 1, &source); } -AL_API DECL_FUNC2(void, alSourcePausev, ALsizei, const ALuint*) +AL_API DECL_FUNC2(void, alSourcePausev, ALsizei,n, const ALuint*,sources) FORCE_ALIGN void AL_APIENTRY alSourcePausevDirect(ALCcontext *context, ALsizei n, const ALuint *sources) noexcept -{ - if(n < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Pausing %d sources", n); +try { + if(n < 0) + context->throw_error(AL_INVALID_VALUE, "Pausing {} sources", n); if(n <= 0) UNLIKELY return; - std::vector extra_sources; - std::array source_storage; - al::span srchandles; - if(static_cast(n) <= source_storage.size()) LIKELY - srchandles = al::span{source_storage}.first(static_cast(n)); - else + al::span sids{sources, static_cast(n)}; + source_store_variant source_store; + const auto srchandles = [&source_store](size_t count) -> al::span { - extra_sources.resize(static_cast(n)); - srchandles = extra_sources; - } + if(count > std::tuple_size_v) + return al::span{source_store.emplace(count)}; + return al::span{source_store.emplace()}.first(count); + }(sids.size()); - std::lock_guard _{context->mSourceLock}; - for(auto &srchdl : srchandles) + std::lock_guard sourcelock{context->mSourceLock}; + auto lookup_src = [context](const ALuint sid) -> ALsource* { - srchdl = LookupSource(context, *sources); - if(!srchdl) - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", *sources); - ++sources; - } + if(ALsource *src{LookupSource(context, sid)}) + return src; + context->throw_error(AL_INVALID_NAME, "Invalid source ID {}", sid); + }; + std::transform(sids.cbegin(), sids.cend(), srchandles.begin(), lookup_src); /* Pausing has to be done in two steps. First, for each source that's * detected to be playing, chamge the voice (asynchronously) to @@ -3256,39 +3404,42 @@ FORCE_ALIGN void AL_APIENTRY alSourcePausevDirect(ALCcontext *context, ALsizei n } } } +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); +} -AL_API DECL_FUNC1(void, alSourceStop, ALuint) +AL_API DECL_FUNC1(void, alSourceStop, ALuint,source) FORCE_ALIGN void AL_APIENTRY alSourceStopDirect(ALCcontext *context, ALuint source) noexcept { alSourceStopvDirect(context, 1, &source); } -AL_API DECL_FUNC2(void, alSourceStopv, ALsizei, const ALuint*) +AL_API DECL_FUNC2(void, alSourceStopv, ALsizei,n, const ALuint*,sources) FORCE_ALIGN void AL_APIENTRY alSourceStopvDirect(ALCcontext *context, ALsizei n, const ALuint *sources) noexcept -{ - if(n < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Stopping %d sources", n); +try { + if(n < 0) + context->throw_error(AL_INVALID_VALUE, "Stopping {} sources", n); if(n <= 0) UNLIKELY return; - std::vector extra_sources; - std::array source_storage; - al::span srchandles; - if(static_cast(n) <= source_storage.size()) LIKELY - srchandles = al::span{source_storage}.first(static_cast(n)); - else + al::span sids{sources, static_cast(n)}; + source_store_variant source_store; + const auto srchandles = [&source_store](size_t count) -> al::span { - extra_sources.resize(static_cast(n)); - srchandles = extra_sources; - } + if(count > std::tuple_size_v) + return al::span{source_store.emplace(count)}; + return al::span{source_store.emplace()}.first(count); + }(sids.size()); - std::lock_guard _{context->mSourceLock}; - for(auto &srchdl : srchandles) + std::lock_guard sourcelock{context->mSourceLock}; + auto lookup_src = [context](const ALuint sid) -> ALsource* { - srchdl = LookupSource(context, *sources); - if(!srchdl) - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", *sources); - ++sources; - } + if(ALsource *src{LookupSource(context, sid)}) + return src; + context->throw_error(AL_INVALID_NAME, "Invalid source ID {}", sid); + }; + std::transform(sids.cbegin(), sids.cend(), srchandles.begin(), lookup_src); VoiceChange *tail{}, *cur{}; for(ALsource *source : srchandles) @@ -3310,44 +3461,47 @@ FORCE_ALIGN void AL_APIENTRY alSourceStopvDirect(ALCcontext *context, ALsizei n, } source->Offset = 0.0; source->OffsetType = AL_NONE; - source->VoiceIdx = INVALID_VOICE_IDX; + source->VoiceIdx = InvalidVoiceIndex; } if(tail) LIKELY SendVoiceChanges(context, tail); } +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); +} -AL_API DECL_FUNC1(void, alSourceRewind, ALuint) +AL_API DECL_FUNC1(void, alSourceRewind, ALuint,source) FORCE_ALIGN void AL_APIENTRY alSourceRewindDirect(ALCcontext *context, ALuint source) noexcept { alSourceRewindvDirect(context, 1, &source); } -AL_API DECL_FUNC2(void, alSourceRewindv, ALsizei, const ALuint*) +AL_API DECL_FUNC2(void, alSourceRewindv, ALsizei,n, const ALuint*,sources) FORCE_ALIGN void AL_APIENTRY alSourceRewindvDirect(ALCcontext *context, ALsizei n, const ALuint *sources) noexcept -{ - if(n < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Rewinding %d sources", n); +try { + if(n < 0) + context->throw_error(AL_INVALID_VALUE, "Rewinding {} sources", n); if(n <= 0) UNLIKELY return; - std::vector extra_sources; - std::array source_storage; - al::span srchandles; - if(static_cast(n) <= source_storage.size()) LIKELY - srchandles = al::span{source_storage}.first(static_cast(n)); - else + al::span sids{sources, static_cast(n)}; + source_store_variant source_store; + const auto srchandles = [&source_store](size_t count) -> al::span { - extra_sources.resize(static_cast(n)); - srchandles = extra_sources; - } + if(count > std::tuple_size_v) + return al::span{source_store.emplace(count)}; + return al::span{source_store.emplace()}.first(count); + }(sids.size()); - std::lock_guard _{context->mSourceLock}; - for(auto &srchdl : srchandles) + std::lock_guard sourcelock{context->mSourceLock}; + auto lookup_src = [context](const ALuint sid) -> ALsource* { - srchdl = LookupSource(context, *sources); - if(!srchdl) - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", *sources); - ++sources; - } + if(ALsource *src{LookupSource(context, sid)}) + return src; + context->throw_error(AL_INVALID_NAME, "Invalid source ID {}", sid); + }; + std::transform(sids.cbegin(), sids.cend(), srchandles.begin(), lookup_src); VoiceChange *tail{}, *cur{}; for(ALsource *source : srchandles) @@ -3371,32 +3525,37 @@ FORCE_ALIGN void AL_APIENTRY alSourceRewindvDirect(ALCcontext *context, ALsizei } source->Offset = 0.0; source->OffsetType = AL_NONE; - source->VoiceIdx = INVALID_VOICE_IDX; + source->VoiceIdx = InvalidVoiceIndex; } if(tail) LIKELY SendVoiceChanges(context, tail); } +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); +} -AL_API DECL_FUNC3(void, alSourceQueueBuffers, ALuint, ALsizei, const ALuint*) +AL_API DECL_FUNC3(void, alSourceQueueBuffers, ALuint,source, ALsizei,nb, const ALuint*,buffers) FORCE_ALIGN void AL_APIENTRY alSourceQueueBuffersDirect(ALCcontext *context, ALuint src, ALsizei nb, const ALuint *buffers) noexcept -{ - if(nb < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Queueing %d buffers", nb); +try { + if(nb < 0) + context->throw_error(AL_INVALID_VALUE, "Queueing {} buffers", nb); if(nb <= 0) UNLIKELY return; - std::lock_guard _{context->mSourceLock}; + std::lock_guard sourcelock{context->mSourceLock}; ALsource *source{LookupSource(context,src)}; - if(!source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", src); + if(!source) + context->throw_error(AL_INVALID_NAME, "Invalid source ID {}", src); /* Can't queue on a Static Source */ - if(source->SourceType == AL_STATIC) UNLIKELY - return context->setError(AL_INVALID_OPERATION, "Queueing onto static source %u", src); + if(source->SourceType == AL_STATIC) + context->throw_error(AL_INVALID_OPERATION, "Queueing onto static source {}", src); /* Check for a valid Buffer, for its frequency and format */ - ALCdevice *device{context->mALDevice.get()}; + auto *device = context->mALDevice.get(); ALbuffer *BufferFmt{nullptr}; for(auto &item : source->mQueue) { @@ -3405,90 +3564,85 @@ FORCE_ALIGN void AL_APIENTRY alSourceQueueBuffersDirect(ALCcontext *context, ALu } std::unique_lock buflock{device->BufferLock}; + const auto bids = al::span{buffers, static_cast(nb)}; const size_t NewListStart{source->mQueue.size()}; - ALbufferQueueItem *BufferList{nullptr}; - for(ALsizei i{0};i < nb;i++) - { - bool fmt_mismatch{false}; - ALbuffer *buffer{nullptr}; - if(buffers[i] && (buffer=LookupBuffer(device, buffers[i])) == nullptr) + try { + ALbufferQueueItem *BufferList{nullptr}; + std::for_each(bids.cbegin(), bids.cend(), + [context,source,device,&BufferFmt,&BufferList](const ALuint bid) { - context->setError(AL_INVALID_NAME, "Queueing invalid buffer ID %u", buffers[i]); - goto buffer_error; - } - if(buffer) - { - if(buffer->mSampleRate < 1) - { - context->setError(AL_INVALID_OPERATION, "Queueing buffer %u with no format", - buffer->id); - goto buffer_error; - } - if(buffer->mCallback) - { - context->setError(AL_INVALID_OPERATION, "Queueing callback buffer %u", buffer->id); - goto buffer_error; - } - if(buffer->MappedAccess != 0 && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT)) + ALbuffer *buffer{bid ? LookupBuffer(device, bid) : nullptr}; + if(bid && !buffer) + context->throw_error(AL_INVALID_NAME, "Queueing invalid buffer ID {}", bid); + + if(buffer) { - context->setError(AL_INVALID_OPERATION, - "Queueing non-persistently mapped buffer %u", buffer->id); - goto buffer_error; + if(buffer->mSampleRate < 1) + context->throw_error(AL_INVALID_OPERATION, + "Queueing buffer {} with no format", buffer->id); + + if(buffer->mCallback) + context->throw_error(AL_INVALID_OPERATION, "Queueing callback buffer {}", + buffer->id); + + if(buffer->MappedAccess != 0 && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT)) + context->throw_error(AL_INVALID_OPERATION, + "Queueing non-persistently mapped buffer {}", buffer->id); } - } - source->mQueue.emplace_back(); - if(!BufferList) - BufferList = &source->mQueue.back(); - else - { - auto &item = source->mQueue.back(); - BufferList->mNext.store(&item, std::memory_order_relaxed); - BufferList = &item; - } - if(!buffer) continue; - BufferList->mBlockAlign = buffer->mBlockAlign; - BufferList->mSampleLen = buffer->mSampleLen; - BufferList->mLoopEnd = buffer->mSampleLen; - BufferList->mSamples = buffer->mData.data(); - BufferList->mBuffer = buffer; - IncrementRef(buffer->ref); - - if(BufferFmt == nullptr) - BufferFmt = buffer; - else - { - fmt_mismatch |= BufferFmt->mSampleRate != buffer->mSampleRate; - fmt_mismatch |= BufferFmt->mChannels != buffer->mChannels; - fmt_mismatch |= BufferFmt->mType != buffer->mType; - if(BufferFmt->isBFormat()) + source->mQueue.emplace_back(); + if(!BufferList) + BufferList = &source->mQueue.back(); + else { - fmt_mismatch |= BufferFmt->mAmbiLayout != buffer->mAmbiLayout; - fmt_mismatch |= BufferFmt->mAmbiScaling != buffer->mAmbiScaling; + auto &item = source->mQueue.back(); + BufferList->mNext.store(&item, std::memory_order_relaxed); + BufferList = &item; } - fmt_mismatch |= BufferFmt->mAmbiOrder != buffer->mAmbiOrder; - } - if(fmt_mismatch) UNLIKELY - { - context->setError(AL_INVALID_OPERATION, "Queueing buffer with mismatched format\n" - " Expected: %uhz, %s, %s ; Got: %uhz, %s, %s\n", BufferFmt->mSampleRate, - NameFromFormat(BufferFmt->mType), NameFromFormat(BufferFmt->mChannels), - buffer->mSampleRate, NameFromFormat(buffer->mType), - NameFromFormat(buffer->mChannels)); - - buffer_error: - /* A buffer failed (invalid ID or format), so unlock and release - * each buffer we had. - */ - auto iter = source->mQueue.begin() + ptrdiff_t(NewListStart); - for(;iter != source->mQueue.end();++iter) + if(!buffer) return; + BufferList->mBlockAlign = buffer->mBlockAlign; + BufferList->mSampleLen = buffer->mSampleLen; + BufferList->mLoopEnd = buffer->mSampleLen; + BufferList->mSamples = buffer->mData; + BufferList->mBuffer = buffer; + IncrementRef(buffer->ref); + + bool fmt_mismatch{false}; + if(BufferFmt == nullptr) + BufferFmt = buffer; + else { - if(ALbuffer *buf{iter->mBuffer}) - DecrementRef(buf->ref); + fmt_mismatch |= BufferFmt->mSampleRate != buffer->mSampleRate; + fmt_mismatch |= BufferFmt->mChannels != buffer->mChannels; + fmt_mismatch |= BufferFmt->mType != buffer->mType; + if(BufferFmt->isBFormat()) + { + fmt_mismatch |= BufferFmt->mAmbiLayout != buffer->mAmbiLayout; + fmt_mismatch |= BufferFmt->mAmbiScaling != buffer->mAmbiScaling; + } + fmt_mismatch |= BufferFmt->mAmbiOrder != buffer->mAmbiOrder; } - source->mQueue.resize(NewListStart); - return; + if(fmt_mismatch) + context->throw_error(AL_INVALID_OPERATION, + "Queueing buffer with mismatched format\n" + " Expected: {}hz, {}, {} ; Got: {}hz, {}, {}\n", BufferFmt->mSampleRate, + NameFromFormat(BufferFmt->mType), NameFromFormat(BufferFmt->mChannels), + buffer->mSampleRate, NameFromFormat(buffer->mType), + NameFromFormat(buffer->mChannels)); + }); + } + catch(...) { + /* A buffer failed (invalid ID or format), or there was some other + * unexpected error, so unlock and release each buffer we had. + */ + auto iter = source->mQueue.begin() + ptrdiff_t(NewListStart); + for(;iter != source->mQueue.end();++iter) + { + if(ALbuffer *buf{iter->mBuffer}) + DecrementRef(buf->ref); } + source->mQueue.resize(NewListStart); + throw; } /* All buffers good. */ buflock.unlock(); @@ -3502,28 +3656,33 @@ FORCE_ALIGN void AL_APIENTRY alSourceQueueBuffersDirect(ALCcontext *context, ALu (iter-1)->mNext.store(al::to_address(iter), std::memory_order_release); } } +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); +} -AL_API DECL_FUNC3(void, alSourceUnqueueBuffers, ALuint, ALsizei, ALuint*) +AL_API DECL_FUNC3(void, alSourceUnqueueBuffers, ALuint,source, ALsizei,nb, ALuint*,buffers) FORCE_ALIGN void AL_APIENTRY alSourceUnqueueBuffersDirect(ALCcontext *context, ALuint src, ALsizei nb, ALuint *buffers) noexcept -{ - if(nb < 0) UNLIKELY - context->setError(AL_INVALID_VALUE, "Unqueueing %d buffers", nb); +try { + if(nb < 0) + context->throw_error(AL_INVALID_VALUE, "Unqueueing {} buffers", nb); if(nb <= 0) UNLIKELY return; - std::lock_guard _{context->mSourceLock}; + std::lock_guard sourcelock{context->mSourceLock}; ALsource *source{LookupSource(context,src)}; - if(!source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", src); + if(!source) + context->throw_error(AL_INVALID_NAME, "Invalid source ID {}", src); - if(source->SourceType != AL_STREAMING) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Unqueueing from a non-streaming source %u", - src); - if(source->Looping) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Unqueueing from looping source %u", src); + if(source->SourceType != AL_STREAMING) + context->throw_error(AL_INVALID_VALUE, "Unqueueing from a non-streaming source {}", src); + if(source->Looping) + context->throw_error(AL_INVALID_VALUE, "Unqueueing from looping source {}", src); /* Make sure enough buffers have been processed to unqueue. */ - uint processed{0u}; + const al::span bids{buffers, static_cast(nb)}; + size_t processed{0}; if(source->state != AL_INITIAL) LIKELY { VoiceBufferItem *Current{nullptr}; @@ -3536,21 +3695,27 @@ FORCE_ALIGN void AL_APIENTRY alSourceUnqueueBuffersDirect(ALCcontext *context, A ++processed; } } - if(processed < static_cast(nb)) UNLIKELY - return context->setError(AL_INVALID_VALUE, "Unqueueing %d buffer%s (only %u processed)", - nb, (nb==1)?"":"s", processed); + if(processed < bids.size()) + context->throw_error(AL_INVALID_VALUE, "Unqueueing {} buffer{} (only {} processed)", + nb, (nb==1) ? "" : "s", processed); - do { + std::generate(bids.begin(), bids.end(), [source]() noexcept -> ALuint + { auto &head = source->mQueue.front(); + ALuint bid{0}; if(ALbuffer *buffer{head.mBuffer}) { - *(buffers++) = buffer->id; + bid = buffer->id; DecrementRef(buffer->ref); } - else - *(buffers++) = 0; source->mQueue.pop_front(); - } while(--nb); + return bid; + }); +} +catch(al::base_exception&) { +} +catch(std::exception &e) { + ERR("Caught exception: {}", e.what()); } @@ -3563,21 +3728,21 @@ AL_API void AL_APIENTRY alSourceQueueBufferLayersSOFT(ALuint, ALsizei, const ALu } -ALsource::ALsource() +ALsource::ALsource() noexcept { Direct.Gain = 1.0f; Direct.GainHF = 1.0f; - Direct.HFReference = LOWPASSFREQREF; + Direct.HFReference = LowPassFreqRef; Direct.GainLF = 1.0f; - Direct.LFReference = HIGHPASSFREQREF; + Direct.LFReference = HighPassFreqRef; for(auto &send : Send) { send.Slot = nullptr; send.Gain = 1.0f; send.GainHF = 1.0f; - send.HFReference = LOWPASSFREQREF; + send.HFReference = LowPassFreqRef; send.GainLF = 1.0f; - send.LFReference = HIGHPASSFREQREF; + send.LFReference = HighPassFreqRef; } } @@ -3596,13 +3761,13 @@ ALsource::~ALsource() void UpdateAllSourceProps(ALCcontext *context) { - std::lock_guard _{context->mSourceLock}; + std::lock_guard srclock{context->mSourceLock}; auto voicelist = context->getVoicesSpan(); ALuint vidx{0u}; for(Voice *voice : voicelist) { ALuint sid{voice->mSourceID.load(std::memory_order_acquire)}; - ALsource *source = sid ? LookupSource(context, sid) : nullptr; + ALsource *source{sid ? LookupSource(context, sid) : nullptr}; if(source && source->VoiceIdx == vidx) { if(std::exchange(source->mPropsDirty, false)) @@ -3614,11 +3779,11 @@ void UpdateAllSourceProps(ALCcontext *context) void ALsource::SetName(ALCcontext *context, ALuint id, std::string_view name) { - std::lock_guard _{context->mSourceLock}; + std::lock_guard srclock{context->mSourceLock}; auto source = LookupSource(context, id); - if(!source) UNLIKELY - return context->setError(AL_INVALID_NAME, "Invalid source ID %u", id); + if(!source) + context->throw_error(AL_INVALID_NAME, "Invalid source ID {}", id); context->mSourceNames.insert_or_assign(id, name); } @@ -3634,18 +3799,15 @@ SourceSubList::~SourceSubList() { const int idx{al::countr_zero(usemask)}; usemask &= ~(1_u64 << idx); - std::destroy_at(Sources+idx); + std::destroy_at(al::to_address(Sources->begin() + idx)); } FreeMask = ~usemask; - al_free(Sources); + SubListAllocator{}.deallocate(Sources, 1); Sources = nullptr; } -#ifdef ALSOFT_EAX -constexpr const ALsource::EaxFxSlotIds ALsource::eax4_fx_slot_ids; -constexpr const ALsource::EaxFxSlotIds ALsource::eax5_fx_slot_ids; - +#if ALSOFT_EAX void ALsource::eaxInitialize(ALCcontext *context) noexcept { assert(context != nullptr); @@ -3918,29 +4080,30 @@ void ALsource::eax4_translate(const Eax4Props& src, Eax5Props& dst) noexcept // Active FX slots. // - for (auto i = 0; i < EAX50_MAX_ACTIVE_FXSLOTS; ++i) { - auto& dst_id = dst.active_fx_slots.guidActiveFXSlots[i]; - - if (i < EAX40_MAX_ACTIVE_FXSLOTS) { - const auto& src_id = src.active_fx_slots.guidActiveFXSlots[i]; - - if (src_id == EAX_NULL_GUID) - dst_id = EAX_NULL_GUID; - else if (src_id == EAX_PrimaryFXSlotID) - dst_id = EAX_PrimaryFXSlotID; - else if (src_id == EAXPROPERTYID_EAX40_FXSlot0) - dst_id = EAXPROPERTYID_EAX50_FXSlot0; - else if (src_id == EAXPROPERTYID_EAX40_FXSlot1) - dst_id = EAXPROPERTYID_EAX50_FXSlot1; - else if (src_id == EAXPROPERTYID_EAX40_FXSlot2) - dst_id = EAXPROPERTYID_EAX50_FXSlot2; - else if (src_id == EAXPROPERTYID_EAX40_FXSlot3) - dst_id = EAXPROPERTYID_EAX50_FXSlot3; - else - assert(false && "Unknown active FX slot ID."); - } else - dst_id = EAX_NULL_GUID; - } + auto translate_slotid = [](const GUID &src_id) -> GUID + { + if(src_id == EAX_NULL_GUID) + return EAX_NULL_GUID; + if(src_id == EAX_PrimaryFXSlotID) + return EAX_PrimaryFXSlotID; + if(src_id == EAXPROPERTYID_EAX40_FXSlot0) + return EAXPROPERTYID_EAX50_FXSlot0; + if(src_id == EAXPROPERTYID_EAX40_FXSlot1) + return EAXPROPERTYID_EAX50_FXSlot1; + if(src_id == EAXPROPERTYID_EAX40_FXSlot2) + return EAXPROPERTYID_EAX50_FXSlot2; + if(src_id == EAXPROPERTYID_EAX40_FXSlot3) + return EAXPROPERTYID_EAX50_FXSlot3; + + UNLIKELY + ERR("Unexpected active FX slot ID"); + return EAX_NULL_GUID; + }; + const auto src_slots = al::span{src.active_fx_slots.guidActiveFXSlots}; + const auto dst_slots = al::span{dst.active_fx_slots.guidActiveFXSlots}; + auto dstiter = std::transform(src_slots.cbegin(), src_slots.cend(), dst_slots.begin(), + translate_slotid); + std::fill(dstiter, dst_slots.end(), EAX_NULL_GUID); // Speaker levels. // @@ -3961,62 +4124,55 @@ float ALsource::eax_calculate_dst_occlusion_mb( EaxAlLowPassParam ALsource::eax_create_direct_filter_param() const noexcept { - auto gain_mb = - static_cast(mEax.source.lDirect) + - (static_cast(mEax.source.lObstruction) * mEax.source.flObstructionLFRatio) + - eax_calculate_dst_occlusion_mb( - mEax.source.lOcclusion, - mEax.source.flOcclusionDirectRatio, - mEax.source.flOcclusionLFRatio); - - const auto has_source_occlusion = (mEax.source.lOcclusion != 0); + const auto &source = mEax.source; - auto gain_hf_mb = - static_cast(mEax.source.lDirectHF) + - static_cast(mEax.source.lObstruction); + auto gain_mb = static_cast(source.lObstruction) * source.flObstructionLFRatio; + auto gainhf_mb = static_cast(source.lObstruction); for(size_t i{0};i < EAX_MAX_FXSLOTS;++i) { if(!mEaxActiveFxSlots[i]) continue; - if(has_source_occlusion) + if(source.lOcclusion != 0) { const auto& fx_slot = mEaxAlContext->eaxGetFxSlot(i); const auto& fx_slot_eax = fx_slot.eax_get_eax_fx_slot(); - const auto is_environmental_fx = ((fx_slot_eax.ulFlags & EAXFXSLOTFLAGS_ENVIRONMENT) != 0); + const auto is_environmental_fx = ((fx_slot_eax.ulFlags&EAXFXSLOTFLAGS_ENVIRONMENT) != 0); const auto is_primary = (mEaxPrimaryFxSlotId.value_or(-1) == fx_slot.eax_get_index()); - const auto is_listener_environment = (is_environmental_fx && is_primary); - if(is_listener_environment) + if(is_environmental_fx && is_primary) { - gain_mb += eax_calculate_dst_occlusion_mb( - mEax.source.lOcclusion, - mEax.source.flOcclusionDirectRatio, - mEax.source.flOcclusionLFRatio); + gain_mb += eax_calculate_dst_occlusion_mb(source.lOcclusion, + source.flOcclusionDirectRatio, source.flOcclusionLFRatio); - gain_hf_mb += static_cast(mEax.source.lOcclusion) * mEax.source.flOcclusionDirectRatio; + gainhf_mb += static_cast(source.lOcclusion) * source.flOcclusionDirectRatio; } } const auto& send = mEax.sends[i]; - if(send.lOcclusion != 0) { - gain_mb += eax_calculate_dst_occlusion_mb( - send.lOcclusion, - send.flOcclusionDirectRatio, + gain_mb += eax_calculate_dst_occlusion_mb(send.lOcclusion, send.flOcclusionDirectRatio, send.flOcclusionLFRatio); - gain_hf_mb += static_cast(send.lOcclusion) * send.flOcclusionDirectRatio; + gainhf_mb += static_cast(send.lOcclusion) * send.flOcclusionDirectRatio; } } - const auto al_low_pass_param = EaxAlLowPassParam{ - level_mb_to_gain(gain_mb), - minf(level_mb_to_gain(gain_hf_mb), 1.0f)}; + /* gainhf_mb is the absolute mBFS of the filter's high-frequency volume, + * and gain_mb is the absolute mBFS of the filter's low-frequency volume. + * Adjust the HF volume to be relative to the LF volume, to make the + * appropriate main and relative HF filter volumes. + * + * Also add the Direct and DirectHF properties to the filter, which are + * already the main and relative HF volumes. + */ + gainhf_mb -= gain_mb - static_cast(source.lDirectHF); + gain_mb += static_cast(source.lDirect); - return al_low_pass_param; + return EaxAlLowPassParam{level_mb_to_gain(gain_mb), + std::min(level_mb_to_gain(gainhf_mb), 1.0f)}; } EaxAlLowPassParam ALsource::eax_create_room_filter_param( @@ -4024,44 +4180,40 @@ EaxAlLowPassParam ALsource::eax_create_room_filter_param( const EAXSOURCEALLSENDPROPERTIES& send) const noexcept { const auto& fx_slot_eax = fx_slot.eax_get_eax_fx_slot(); - const auto is_environmental_fx = ((fx_slot_eax.ulFlags & EAXFXSLOTFLAGS_ENVIRONMENT) != 0); - const auto is_primary = (mEaxPrimaryFxSlotId.value_or(-1) == fx_slot.eax_get_index()); - const auto is_listener_environment = (is_environmental_fx && is_primary); - - const auto gain_mb = - (static_cast(fx_slot_eax.lOcclusion) * fx_slot_eax.flOcclusionLFRatio) + - static_cast((is_environmental_fx ? mEax.source.lRoom : 0) + send.lSend) + - (is_listener_environment ? - eax_calculate_dst_occlusion_mb( - mEax.source.lOcclusion, - mEax.source.flOcclusionRoomRatio, - mEax.source.flOcclusionLFRatio) : - 0.0f) + - eax_calculate_dst_occlusion_mb( - send.lOcclusion, - send.flOcclusionRoomRatio, - send.flOcclusionLFRatio) + - (is_listener_environment ? - (static_cast(mEax.source.lExclusion) * mEax.source.flExclusionLFRatio) : - 0.0f) + - (static_cast(send.lExclusion) * send.flExclusionLFRatio); - - const auto gain_hf_mb = - static_cast(fx_slot_eax.lOcclusion) + - static_cast((is_environmental_fx ? mEax.source.lRoomHF : 0) + send.lSendHF) + - (is_listener_environment ? - ((static_cast(mEax.source.lOcclusion) * mEax.source.flOcclusionRoomRatio)) : - 0.0f) + - (static_cast(send.lOcclusion) * send.flOcclusionRoomRatio) + - (is_listener_environment ? - static_cast(mEax.source.lExclusion + send.lExclusion) : - 0.0f); - - const auto al_low_pass_param = EaxAlLowPassParam{ - level_mb_to_gain(gain_mb), - minf(level_mb_to_gain(gain_hf_mb), 1.0f)}; - - return al_low_pass_param; + const auto is_environmental_fx = bool{(fx_slot_eax.ulFlags & EAXFXSLOTFLAGS_ENVIRONMENT) != 0}; + const auto is_primary = bool{mEaxPrimaryFxSlotId.value_or(-1) == fx_slot.eax_get_index()}; + + auto gain_mb = (static_cast(fx_slot_eax.lOcclusion) * fx_slot_eax.flOcclusionLFRatio) + + eax_calculate_dst_occlusion_mb(send.lOcclusion, send.flOcclusionRoomRatio, + send.flOcclusionLFRatio) + + (static_cast(send.lExclusion) * send.flExclusionLFRatio); + + auto gainhf_mb = static_cast(fx_slot_eax.lOcclusion) + + (static_cast(send.lOcclusion) * send.flOcclusionRoomRatio); + + if(is_environmental_fx && is_primary) + { + const auto &source = mEax.source; + + gain_mb += eax_calculate_dst_occlusion_mb(source.lOcclusion, source.flOcclusionRoomRatio, + source.flOcclusionLFRatio); + gain_mb += static_cast(source.lExclusion) * source.flExclusionLFRatio; + + gainhf_mb += static_cast(source.lOcclusion) * source.flOcclusionRoomRatio; + gainhf_mb += static_cast(source.lExclusion + send.lExclusion); + } + + gainhf_mb -= gain_mb - static_cast(send.lSendHF); + gain_mb += static_cast(send.lSend); + if(is_environmental_fx) + { + const auto &source = mEax.source; + gain_mb += static_cast(source.lRoom); + gainhf_mb += static_cast(source.lRoomHF); + } + + return EaxAlLowPassParam{level_mb_to_gain(gain_mb), + std::min(level_mb_to_gain(gainhf_mb), 1.0f)}; } void ALsource::eax_update_direct_filter() @@ -4069,9 +4221,9 @@ void ALsource::eax_update_direct_filter() const auto& direct_param = eax_create_direct_filter_param(); Direct.Gain = direct_param.gain; Direct.GainHF = direct_param.gain_hf; - Direct.HFReference = LOWPASSFREQREF; + Direct.HFReference = LowPassFreqRef; Direct.GainLF = 1.0f; - Direct.LFReference = HIGHPASSFREQREF; + Direct.LFReference = HighPassFreqRef; mPropsDirty = true; } @@ -4360,7 +4512,7 @@ void ALsource::eax4_set(const EaxCall& call, Eax4Props& props) break; case EAXSOURCE_ACTIVEFXSLOTID: - eax4_defer_active_fx_slot_id(call, props.active_fx_slots.guidActiveFXSlots); + eax4_defer_active_fx_slot_id(call, al::span{props.active_fx_slots.guidActiveFXSlots}); break; default: @@ -4420,10 +4572,13 @@ void ALsource::eax5_set(const EaxCall& call, Eax5Props& props) case EAXSOURCE_ROLLOFFFACTOR: case EAXSOURCE_ROOMROLLOFFFACTOR: case EAXSOURCE_AIRABSORPTIONFACTOR: - case EAXSOURCE_FLAGS: eax3_set(call, props.source); break; + case EAXSOURCE_FLAGS: + eax_defer(call, props.source.ulFlags); + break; + case EAXSOURCE_SENDPARAMETERS: eax5_defer_sends(call, props.sends); break; @@ -4441,7 +4596,7 @@ void ALsource::eax5_set(const EaxCall& call, Eax5Props& props) break; case EAXSOURCE_ACTIVEFXSLOTID: - eax5_defer_active_fx_slot_id(call, props.active_fx_slots.guidActiveFXSlots); + eax5_defer_active_fx_slot_id(call, al::span{props.active_fx_slots.guidActiveFXSlots}); break; case EAXSOURCE_MACROFXFACTOR: @@ -4477,13 +4632,11 @@ void ALsource::eax_set(const EaxCall& call) mEaxVersion = eax_version; } -void ALsource::eax_get_active_fx_slot_id(const EaxCall& call, const GUID* ids, size_t max_count) +void ALsource::eax_get_active_fx_slot_id(const EaxCall& call, const al::span src_ids) { - assert(ids != nullptr); - assert(max_count == EAX40_MAX_ACTIVE_FXSLOTS || max_count == EAX50_MAX_ACTIVE_FXSLOTS); - const auto dst_ids = call.get_values(max_count); - const auto count = dst_ids.size(); - std::uninitialized_copy_n(ids, count, dst_ids.begin()); + assert(src_ids.size()==EAX40_MAX_ACTIVE_FXSLOTS || src_ids.size()==EAX50_MAX_ACTIVE_FXSLOTS); + const auto dst_ids = call.get_values(src_ids.size()); + std::uninitialized_copy_n(src_ids.begin(), dst_ids.size(), dst_ids.begin()); } void ALsource::eax1_get(const EaxCall& call, const Eax1Props& props) @@ -4731,7 +4884,7 @@ void ALsource::eax4_get(const EaxCall& call, const Eax4Props& props) break; case EAXSOURCE_ACTIVEFXSLOTID: - eax_get_active_fx_slot_id(call, props.active_fx_slots.guidActiveFXSlots, EAX40_MAX_ACTIVE_FXSLOTS); + eax_get_active_fx_slot_id(call, props.active_fx_slots.guidActiveFXSlots); break; default: @@ -4803,7 +4956,7 @@ void ALsource::eax5_get(const EaxCall& call, const Eax5Props& props) break; case EAXSOURCE_ACTIVEFXSLOTID: - eax_get_active_fx_slot_id(call, props.active_fx_slots.guidActiveFXSlots, EAX50_MAX_ACTIVE_FXSLOTS); + eax_get_active_fx_slot_id(call, props.active_fx_slots.guidActiveFXSlots); break; case EAXSOURCE_MACROFXFACTOR: @@ -4843,9 +4996,9 @@ void ALsource::eax_set_al_source_send(ALeffectslot *slot, size_t sendidx, const auto &send = Send[sendidx]; send.Gain = filter.gain; send.GainHF = filter.gain_hf; - send.HFReference = LOWPASSFREQREF; + send.HFReference = LowPassFreqRef; send.GainLF = 1.0f; - send.LFReference = HIGHPASSFREQREF; + send.LFReference = HighPassFreqRef; if(slot != nullptr) IncrementRef(slot->ref); diff --git a/3rdparty/openal/al/source.h b/3rdparty/openal/al/source.h index 2bdeb2a385b1..c69143b498b0 100644 --- a/3rdparty/openal/al/source.h +++ b/3rdparty/openal/al/source.h @@ -1,28 +1,29 @@ #ifndef AL_SOURCE_H #define AL_SOURCE_H +#include "config.h" + #include -#include #include +#include #include -#include #include #include +#include #include "AL/al.h" #include "AL/alc.h" +#include "AL/alext.h" -#include "alc/alu.h" -#include "alc/context.h" -#include "alc/inprogext.h" -#include "aldeque.h" #include "almalloc.h" +#include "alnumbers.h" #include "alnumeric.h" -#include "atomic.h" +#include "alspan.h" +#include "core/context.h" #include "core/voice.h" -#include "vector.h" -#ifdef ALSOFT_EAX +#if ALSOFT_EAX +#include "eax/api.h" #include "eax/call.h" #include "eax/exception.h" #include "eax/fx_slot_index.h" @@ -31,27 +32,25 @@ struct ALbuffer; struct ALeffectslot; - +enum class Resampler : uint8_t; enum class SourceStereo : bool { Normal = AL_NORMAL_SOFT, Enhanced = AL_SUPER_STEREO_SOFT }; -#define DEFAULT_SENDS 2 +inline constexpr size_t DefaultSendCount{2}; -#define INVALID_VOICE_IDX static_cast(-1) +inline constexpr ALuint InvalidVoiceIndex{std::numeric_limits::max()}; -extern bool sBufferSubDataCompat; +inline bool sBufferSubDataCompat{false}; struct ALbufferQueueItem : public VoiceBufferItem { ALbuffer *mBuffer{nullptr}; - - DISABLE_ALLOC() }; -#ifdef ALSOFT_EAX +#if ALSOFT_EAX class EaxSourceException : public EaxException { public: explicit EaxSourceException(const char* message) @@ -72,7 +71,7 @@ struct ALsource { float RefDistance{1.0f}; float MaxDistance{std::numeric_limits::max()}; float RolloffFactor{1.0f}; -#ifdef ALSOFT_EAX +#if ALSOFT_EAX // For EAXSOURCE_ROLLOFFFACTOR, which is distinct from and added to // AL_ROLLOFF_FACTOR float RolloffFactor2{0.0f}; @@ -89,6 +88,7 @@ struct ALsource { DirectMode DirectChannels{DirectMode::Off}; SpatializeMode mSpatialize{SpatializeMode::Auto}; SourceStereo mStereoMode{SourceStereo::Normal}; + bool mPanningEnabled{false}; bool DryGainHFAuto{true}; bool WetGainAuto{true}; @@ -106,24 +106,27 @@ struct ALsource { float Radius{0.0f}; float EnhWidth{0.593f}; + float mPan{0.0f}; /** Direct filter and auxiliary send info. */ - struct { - float Gain; - float GainHF; - float HFReference; - float GainLF; - float LFReference; - } Direct; + struct DirectData { + float Gain{}; + float GainHF{}; + float HFReference{}; + float GainLF{}; + float LFReference{}; + }; + DirectData Direct; + struct SendData { - ALeffectslot *Slot; - float Gain; - float GainHF; - float HFReference; - float GainLF; - float LFReference; + ALeffectslot *Slot{}; + float Gain{}; + float GainHF{}; + float HFReference{}; + float GainLF{}; + float LFReference{}; }; - std::array Send; + std::array Send; /** * Last user-specified offset, and the offset type (bytes, samples, or @@ -139,20 +142,20 @@ struct ALsource { ALenum state{AL_INITIAL}; /** Source Buffer Queue head. */ - al::deque mQueue; + std::deque mQueue; bool mPropsDirty{true}; /* Index into the context's Voices array. Lazily updated, only checked and * reset when looking up the voice. */ - ALuint VoiceIdx{INVALID_VOICE_IDX}; + ALuint VoiceIdx{InvalidVoiceIndex}; /** Self ID */ ALuint id{0}; - ALsource(); + ALsource() noexcept; ~ALsource(); ALsource(const ALsource&) = delete; @@ -160,9 +163,9 @@ struct ALsource { static void SetName(ALCcontext *context, ALuint id, std::string_view name); - DISABLE_ALLOC() + DISABLE_ALLOC -#ifdef ALSOFT_EAX +#if ALSOFT_EAX public: void eaxInitialize(ALCcontext *context) noexcept; void eaxDispatch(const EaxCall& call); @@ -174,18 +177,18 @@ struct ALsource { private: using Exception = EaxSourceException; - static constexpr auto eax_max_speakers = 9; + static constexpr auto eax_max_speakers{9u}; - using EaxFxSlotIds = const GUID* [EAX_MAX_FXSLOTS]; + using EaxFxSlotIds = std::array; - static constexpr const EaxFxSlotIds eax4_fx_slot_ids = { + static constexpr const EaxFxSlotIds eax4_fx_slot_ids{ &EAXPROPERTYID_EAX40_FXSlot0, &EAXPROPERTYID_EAX40_FXSlot1, &EAXPROPERTYID_EAX40_FXSlot2, &EAXPROPERTYID_EAX40_FXSlot3, }; - static constexpr const EaxFxSlotIds eax5_fx_slot_ids = { + static constexpr const EaxFxSlotIds eax5_fx_slot_ids{ &EAXPROPERTYID_EAX50_FXSlot0, &EAXPROPERTYID_EAX50_FXSlot1, &EAXPROPERTYID_EAX50_FXSlot2, @@ -218,11 +221,6 @@ struct ALsource { Eax3Props source; EaxSends sends; EAX40ACTIVEFXSLOTS active_fx_slots; - - bool operator==(const Eax4Props& rhs) noexcept - { - return std::memcmp(this, &rhs, sizeof(Eax4Props)) == 0; - } }; struct Eax4State { @@ -235,11 +233,6 @@ struct ALsource { EaxSends sends; EAX50ACTIVEFXSLOTS active_fx_slots; EaxSpeakerLevels speaker_levels; - - bool operator==(const Eax5Props& rhs) noexcept - { - return std::memcmp(this, &rhs, sizeof(Eax5Props)) == 0; - } }; struct Eax5State { @@ -549,7 +542,24 @@ struct ALsource { struct Eax5SourceAllValidator { void operator()(const EAX50SOURCEPROPERTIES& props) const { - Eax3SourceAllValidator{}(static_cast(props)); + Eax2SourceDirectValidator{}(props.lDirect); + Eax2SourceDirectHfValidator{}(props.lDirectHF); + Eax2SourceRoomValidator{}(props.lRoom); + Eax2SourceRoomHfValidator{}(props.lRoomHF); + Eax2SourceObstructionValidator{}(props.lObstruction); + Eax2SourceObstructionLfRatioValidator{}(props.flObstructionLFRatio); + Eax2SourceOcclusionValidator{}(props.lOcclusion); + Eax2SourceOcclusionLfRatioValidator{}(props.flOcclusionLFRatio); + Eax2SourceOcclusionRoomRatioValidator{}(props.flOcclusionRoomRatio); + Eax3SourceOcclusionDirectRatioValidator{}(props.flOcclusionDirectRatio); + Eax3SourceExclusionValidator{}(props.lExclusion); + Eax3SourceExclusionLfRatioValidator{}(props.flExclusionLFRatio); + Eax2SourceOutsideVolumeHfValidator{}(props.lOutsideVolumeHF); + Eax3SourceDopplerFactorValidator{}(props.flDopplerFactor); + Eax3SourceRolloffFactorValidator{}(props.flRolloffFactor); + Eax2SourceRoomRolloffFactorValidator{}(props.flRoomRolloffFactor); + Eax2SourceAirAbsorptionFactorValidator{}(props.flAirAbsorptionFactor); + Eax5SourceFlagsValidator{}(props.ulFlags); Eax5SourceMacroFXFactorValidator{}(props.flMacroFXFactor); } }; @@ -812,39 +822,38 @@ struct ALsource { [[noreturn]] static void eax_fail_unknown_active_fx_slot_id(); [[noreturn]] static void eax_fail_unknown_receiving_fx_slot_id(); - void eax_set_sends_defaults(EaxSends& sends, const EaxFxSlotIds& ids) noexcept; - void eax1_set_defaults(Eax1Props& props) noexcept; + static void eax_set_sends_defaults(EaxSends& sends, const EaxFxSlotIds& ids) noexcept; + static void eax1_set_defaults(Eax1Props& props) noexcept; void eax1_set_defaults() noexcept; - void eax2_set_defaults(Eax2Props& props) noexcept; + static void eax2_set_defaults(Eax2Props& props) noexcept; void eax2_set_defaults() noexcept; - void eax3_set_defaults(Eax3Props& props) noexcept; + static void eax3_set_defaults(Eax3Props& props) noexcept; void eax3_set_defaults() noexcept; - void eax4_set_sends_defaults(EaxSends& sends) noexcept; - void eax4_set_active_fx_slots_defaults(EAX40ACTIVEFXSLOTS& slots) noexcept; + static void eax4_set_sends_defaults(EaxSends& sends) noexcept; + static void eax4_set_active_fx_slots_defaults(EAX40ACTIVEFXSLOTS& slots) noexcept; void eax4_set_defaults() noexcept; - void eax5_set_source_defaults(EAX50SOURCEPROPERTIES& props) noexcept; - void eax5_set_sends_defaults(EaxSends& sends) noexcept; - void eax5_set_active_fx_slots_defaults(EAX50ACTIVEFXSLOTS& slots) noexcept; - void eax5_set_speaker_levels_defaults(EaxSpeakerLevels& speaker_levels) noexcept; - void eax5_set_defaults(Eax5Props& props) noexcept; + static void eax5_set_source_defaults(EAX50SOURCEPROPERTIES& props) noexcept; + static void eax5_set_sends_defaults(EaxSends& sends) noexcept; + static void eax5_set_active_fx_slots_defaults(EAX50ACTIVEFXSLOTS& slots) noexcept; + static void eax5_set_speaker_levels_defaults(EaxSpeakerLevels& speaker_levels) noexcept; + static void eax5_set_defaults(Eax5Props& props) noexcept; void eax5_set_defaults() noexcept; void eax_set_defaults() noexcept; - void eax1_translate(const Eax1Props& src, Eax5Props& dst) noexcept; - void eax2_translate(const Eax2Props& src, Eax5Props& dst) noexcept; - void eax3_translate(const Eax3Props& src, Eax5Props& dst) noexcept; - void eax4_translate(const Eax4Props& src, Eax5Props& dst) noexcept; + static void eax1_translate(const Eax1Props& src, Eax5Props& dst) noexcept; + static void eax2_translate(const Eax2Props& src, Eax5Props& dst) noexcept; + static void eax3_translate(const Eax3Props& src, Eax5Props& dst) noexcept; + static void eax4_translate(const Eax4Props& src, Eax5Props& dst) noexcept; static float eax_calculate_dst_occlusion_mb( long src_occlusion_mb, float path_ratio, float lf_ratio) noexcept; - EaxAlLowPassParam eax_create_direct_filter_param() const noexcept; + [[nodiscard]] auto eax_create_direct_filter_param() const noexcept -> EaxAlLowPassParam; - EaxAlLowPassParam eax_create_room_filter_param( - const ALeffectslot& fx_slot, - const EAXSOURCEALLSENDPROPERTIES& send) const noexcept; + [[nodiscard]] auto eax_create_room_filter_param(const ALeffectslot& fx_slot, + const EAXSOURCEALLSENDPROPERTIES& send) const noexcept -> EaxAlLowPassParam; void eax_update_direct_filter(); void eax_update_room_filters(); @@ -897,16 +906,16 @@ struct ALsource { } } - void eax_get_active_fx_slot_id(const EaxCall& call, const GUID* ids, size_t max_count); - void eax1_get(const EaxCall& call, const Eax1Props& props); - void eax2_get(const EaxCall& call, const Eax2Props& props); - void eax3_get_obstruction(const EaxCall& call, const Eax3Props& props); - void eax3_get_occlusion(const EaxCall& call, const Eax3Props& props); - void eax3_get_exclusion(const EaxCall& call, const Eax3Props& props); - void eax3_get(const EaxCall& call, const Eax3Props& props); + static void eax_get_active_fx_slot_id(const EaxCall& call, const al::span src_ids); + static void eax1_get(const EaxCall& call, const Eax1Props& props); + static void eax2_get(const EaxCall& call, const Eax2Props& props); + static void eax3_get_obstruction(const EaxCall& call, const Eax3Props& props); + static void eax3_get_occlusion(const EaxCall& call, const Eax3Props& props); + static void eax3_get_exclusion(const EaxCall& call, const Eax3Props& props); + static void eax3_get(const EaxCall& call, const Eax3Props& props); void eax4_get(const EaxCall& call, const Eax4Props& props); - void eax5_get_all_2d(const EaxCall& call, const EAX50SOURCEPROPERTIES& props); - void eax5_get_speaker_levels(const EaxCall& call, const EaxSpeakerLevels& props); + static void eax5_get_all_2d(const EaxCall& call, const EAX50SOURCEPROPERTIES& props); + static void eax5_get_speaker_levels(const EaxCall& call, const EaxSpeakerLevels& props); void eax5_get(const EaxCall& call, const Eax5Props& props); void eax_get(const EaxCall& call); @@ -979,21 +988,21 @@ struct ALsource { } template - void eax_defer_active_fx_slot_id(const EaxCall& call, GUID (&dst_ids)[TIdCount]) + void eax_defer_active_fx_slot_id(const EaxCall& call, const al::span dst_ids) { const auto src_ids = call.get_values(TIdCount); std::for_each(src_ids.cbegin(), src_ids.cend(), TValidator{}); - std::uninitialized_copy(src_ids.cbegin(), src_ids.cend(), dst_ids); + std::uninitialized_copy(src_ids.cbegin(), src_ids.cend(), dst_ids.begin()); } template - void eax4_defer_active_fx_slot_id(const EaxCall& call, GUID (&dst_ids)[TIdCount]) + void eax4_defer_active_fx_slot_id(const EaxCall& call, const al::span dst_ids) { eax_defer_active_fx_slot_id(call, dst_ids); } template - void eax5_defer_active_fx_slot_id(const EaxCall& call, GUID (&dst_ids)[TIdCount]) + void eax5_defer_active_fx_slot_id(const EaxCall& call, const al::span dst_ids) { eax_defer_active_fx_slot_id(call, dst_ids); } @@ -1025,12 +1034,12 @@ struct ALsource { void eax_set_efx_wet_gain_auto(); void eax_set_efx_wet_gain_hf_auto(); - void eax1_set(const EaxCall& call, Eax1Props& props); - void eax2_set(const EaxCall& call, Eax2Props& props); + static void eax1_set(const EaxCall& call, Eax1Props& props); + static void eax2_set(const EaxCall& call, Eax2Props& props); void eax3_set(const EaxCall& call, Eax3Props& props); void eax4_set(const EaxCall& call, Eax4Props& props); - void eax5_defer_all_2d(const EaxCall& call, EAX50SOURCEPROPERTIES& props); - void eax5_defer_speaker_levels(const EaxCall& call, EaxSpeakerLevels& props); + static void eax5_defer_all_2d(const EaxCall& call, EAX50SOURCEPROPERTIES& props); + static void eax5_defer_speaker_levels(const EaxCall& call, EaxSpeakerLevels& props); void eax5_set(const EaxCall& call, Eax5Props& props); void eax_set(const EaxCall& call); @@ -1044,4 +1053,19 @@ struct ALsource { void UpdateAllSourceProps(ALCcontext *context); +struct SourceSubList { + uint64_t FreeMask{~0_u64}; + gsl::owner*> Sources{nullptr}; + + SourceSubList() noexcept = default; + SourceSubList(const SourceSubList&) = delete; + SourceSubList(SourceSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Sources{rhs.Sources} + { rhs.FreeMask = ~0_u64; rhs.Sources = nullptr; } + ~SourceSubList(); + + SourceSubList& operator=(const SourceSubList&) = delete; + SourceSubList& operator=(SourceSubList&& rhs) noexcept + { std::swap(FreeMask, rhs.FreeMask); std::swap(Sources, rhs.Sources); return *this; } +}; + #endif diff --git a/3rdparty/openal/al/state.cpp b/3rdparty/openal/al/state.cpp index 5131edd93a51..fd3f5fefef05 100644 --- a/3rdparty/openal/al/state.cpp +++ b/3rdparty/openal/al/state.cpp @@ -22,27 +22,30 @@ #include "version.h" +#include #include #include -#include +#include #include #include #include #include +#include #include "AL/al.h" #include "AL/alc.h" #include "AL/alext.h" #include "al/debug.h" -#include "albit.h" +#include "al/listener.h" #include "alc/alu.h" #include "alc/context.h" +#include "alc/device.h" #include "alc/inprogext.h" #include "alnumeric.h" #include "atomic.h" #include "core/context.h" -#include "core/except.h" +#include "core/logging.h" #include "core/mixer/defs.h" #include "core/voice.h" #include "direct_defs.h" @@ -50,9 +53,7 @@ #include "opthelpers.h" #include "strutils.h" -#ifdef ALSOFT_EAX -#include "alc/device.h" - +#if ALSOFT_EAX #include "eax/globals.h" #include "eax/x_ram.h" #endif // ALSOFT_EAX @@ -60,19 +61,21 @@ namespace { -constexpr ALchar alVendor[] = "OpenAL Community"; -constexpr ALchar alVersion[] = "1.1 ALSOFT " ALSOFT_VERSION; -constexpr ALchar alRenderer[] = "OpenAL Soft"; +using ALvoidptr = ALvoid*; -// Error Messages -constexpr ALchar alNoError[] = "No Error"; -constexpr ALchar alErrInvalidName[] = "Invalid Name"; -constexpr ALchar alErrInvalidEnum[] = "Invalid Enum"; -constexpr ALchar alErrInvalidValue[] = "Invalid Value"; -constexpr ALchar alErrInvalidOp[] = "Invalid Operation"; -constexpr ALchar alErrOutOfMemory[] = "Out of Memory"; -constexpr ALchar alStackOverflow[] = "Stack Overflow"; -constexpr ALchar alStackUnderflow[] = "Stack Underflow"; +[[nodiscard]] constexpr auto GetVendorString() noexcept { return "OpenAL Community"; } +[[nodiscard]] constexpr auto GetVersionString() noexcept { return "1.1 ALSOFT " ALSOFT_VERSION; } +[[nodiscard]] constexpr auto GetRendererString() noexcept { return "OpenAL Soft"; } + +/* Error Messages */ +[[nodiscard]] constexpr auto GetNoErrorString() noexcept { return "No Error"; } +[[nodiscard]] constexpr auto GetInvalidNameString() noexcept { return "Invalid Name"; } +[[nodiscard]] constexpr auto GetInvalidEnumString() noexcept { return "Invalid Enum"; } +[[nodiscard]] constexpr auto GetInvalidValueString() noexcept { return "Invalid Value"; } +[[nodiscard]] constexpr auto GetInvalidOperationString() noexcept { return "Invalid Operation"; } +[[nodiscard]] constexpr auto GetOutOfMemoryString() noexcept { return "Out of Memory"; } +[[nodiscard]] constexpr auto GetStackOverflowString() noexcept { return "Stack Overflow"; } +[[nodiscard]] constexpr auto GetStackUnderflowString() noexcept { return "Stack Underflow"; } /* Resampler strings */ template struct ResamplerName { }; @@ -80,8 +83,10 @@ template<> struct ResamplerName { static constexpr const ALchar *Get() noexcept { return "Nearest"; } }; template<> struct ResamplerName { static constexpr const ALchar *Get() noexcept { return "Linear"; } }; -template<> struct ResamplerName -{ static constexpr const ALchar *Get() noexcept { return "Cubic"; } }; +template<> struct ResamplerName +{ static constexpr const ALchar *Get() noexcept { return "Cubic Spline"; } }; +template<> struct ResamplerName +{ static constexpr const ALchar *Get() noexcept { return "4-point Gaussian"; } }; template<> struct ResamplerName { static constexpr const ALchar *Get() noexcept { return "11th order Sinc (fast)"; } }; template<> struct ResamplerName @@ -98,7 +103,8 @@ const ALchar *GetResamplerName(const Resampler rtype) { HANDLE_RESAMPLER(Resampler::Point); HANDLE_RESAMPLER(Resampler::Linear); - HANDLE_RESAMPLER(Resampler::Cubic); + HANDLE_RESAMPLER(Resampler::Spline); + HANDLE_RESAMPLER(Resampler::Gaussian); HANDLE_RESAMPLER(Resampler::FastBSinc12); HANDLE_RESAMPLER(Resampler::BSinc12); HANDLE_RESAMPLER(Resampler::FastBSinc24); @@ -109,7 +115,7 @@ const ALchar *GetResamplerName(const Resampler rtype) throw std::runtime_error{"Unexpected resampler index"}; } -std::optional DistanceModelFromALenum(ALenum model) +constexpr auto DistanceModelFromALenum(ALenum model) noexcept -> std::optional { switch(model) { @@ -123,7 +129,7 @@ std::optional DistanceModelFromALenum(ALenum model) } return std::nullopt; } -ALenum ALenumFromDistanceModel(DistanceModel model) +constexpr auto ALenumFromDistanceModel(DistanceModel model) -> ALenum { switch(model) { @@ -139,24 +145,24 @@ ALenum ALenumFromDistanceModel(DistanceModel model) } enum PropertyValue : ALenum { - DopplerFactor = AL_DOPPLER_FACTOR, - DopplerVelocity = AL_DOPPLER_VELOCITY, - DistanceModel = AL_DISTANCE_MODEL, - SpeedOfSound = AL_SPEED_OF_SOUND, - DeferredUpdates = AL_DEFERRED_UPDATES_SOFT, - GainLimit = AL_GAIN_LIMIT_SOFT, - NumResamplers = AL_NUM_RESAMPLERS_SOFT, - DefaultResampler = AL_DEFAULT_RESAMPLER_SOFT, - DebugLoggedMessages = AL_DEBUG_LOGGED_MESSAGES_EXT, - DebugNextLoggedMessageLength = AL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_EXT, - MaxDebugMessageLength = AL_MAX_DEBUG_MESSAGE_LENGTH_EXT, - MaxDebugLoggedMessages = AL_MAX_DEBUG_LOGGED_MESSAGES_EXT, - MaxDebugGroupDepth = AL_MAX_DEBUG_GROUP_STACK_DEPTH_EXT, - MaxLabelLength = AL_MAX_LABEL_LENGTH_EXT, - ContextFlags = AL_CONTEXT_FLAGS_EXT, -#ifdef ALSOFT_EAX - EaxRamSize = AL_EAX_RAM_SIZE, - EaxRamFree = AL_EAX_RAM_FREE, + DopplerFactorProp = AL_DOPPLER_FACTOR, + DopplerVelocityProp = AL_DOPPLER_VELOCITY, + DistanceModelProp = AL_DISTANCE_MODEL, + SpeedOfSoundProp = AL_SPEED_OF_SOUND, + DeferredUpdatesProp = AL_DEFERRED_UPDATES_SOFT, + GainLimitProp = AL_GAIN_LIMIT_SOFT, + NumResamplersProp = AL_NUM_RESAMPLERS_SOFT, + DefaultResamplerProp = AL_DEFAULT_RESAMPLER_SOFT, + DebugLoggedMessagesProp = AL_DEBUG_LOGGED_MESSAGES_EXT, + DebugNextLoggedMessageLengthProp = AL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_EXT, + MaxDebugMessageLengthProp = AL_MAX_DEBUG_MESSAGE_LENGTH_EXT, + MaxDebugLoggedMessagesProp = AL_MAX_DEBUG_LOGGED_MESSAGES_EXT, + MaxDebugGroupDepthProp = AL_MAX_DEBUG_GROUP_STACK_DEPTH_EXT, + MaxLabelLengthProp = AL_MAX_LABEL_LENGTH_EXT, + ContextFlagsProp = AL_CONTEXT_FLAGS_EXT, +#if ALSOFT_EAX + EaxRamSizeProp = AL_EAX_RAM_SIZE, + EaxRamFreeProp = AL_EAX_RAM_FREE, #endif }; @@ -221,14 +227,14 @@ void GetValue(ALCcontext *context, ALenum pname, T *values) case AL_DEBUG_LOGGED_MESSAGES_EXT: { - std::lock_guard _{context->mDebugCbLock}; + std::lock_guard debuglock{context->mDebugCbLock}; *values = cast_value(context->mDebugLog.size()); return; } case AL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_EXT: { - std::lock_guard _{context->mDebugCbLock}; + std::lock_guard debuglock{context->mDebugCbLock}; *values = cast_value(context->mDebugLog.empty() ? 0_uz : (context->mDebugLog.front().mMessage.size()+1)); return; @@ -254,9 +260,8 @@ void GetValue(ALCcontext *context, ALenum pname, T *values) *values = cast_value(context->mContextFlags.to_ulong()); return; -#ifdef ALSOFT_EAX - -#define EAX_ERROR "[alGetInteger] EAX not enabled." +#if ALSOFT_EAX +#define EAX_ERROR "[alGetInteger] EAX not enabled" case AL_EAX_RAM_SIZE: if(eax_g_is_enabled) @@ -264,8 +269,8 @@ void GetValue(ALCcontext *context, ALenum pname, T *values) *values = cast_value(eax_x_ram_max_size); return; } - context->setError(AL_INVALID_ENUM, EAX_ERROR); - return; + ERR(EAX_ERROR); + break; case AL_EAX_RAM_FREE: if(eax_g_is_enabled) @@ -275,14 +280,13 @@ void GetValue(ALCcontext *context, ALenum pname, T *values) *values = cast_value(device->eax_x_ram_free_size); return; } - context->setError(AL_INVALID_ENUM, EAX_ERROR); - return; + ERR(EAX_ERROR); + break; #undef EAX_ERROR - #endif // ALSOFT_EAX } - context->setError(AL_INVALID_ENUM, "Invalid context property 0x%04x", pname); + context->setError(AL_INVALID_ENUM, "Invalid context property {:#04x}", as_unsigned(pname)); } @@ -299,7 +303,7 @@ inline void UpdateProps(ALCcontext *context) /* WARNING: Non-standard export! Not part of any extension, or exposed in the * alcFunctions list. */ -AL_API const ALchar* AL_APIENTRY alsoft_get_version(void) noexcept +AL_API auto AL_APIENTRY alsoft_get_version() noexcept -> const ALchar* { static const auto spoof = al::getenv("ALSOFT_SPOOF_VERSION"); if(spoof) return spoof->c_str(); @@ -307,112 +311,101 @@ AL_API const ALchar* AL_APIENTRY alsoft_get_version(void) noexcept } -AL_API DECL_FUNC1(void, alEnable, ALenum) +AL_API DECL_FUNC1(void, alEnable, ALenum,capability) FORCE_ALIGN void AL_APIENTRY alEnableDirect(ALCcontext *context, ALenum capability) noexcept { switch(capability) { case AL_SOURCE_DISTANCE_MODEL: { - std::lock_guard _{context->mPropLock}; + std::lock_guard proplock{context->mPropLock}; context->mSourceDistanceModel = true; UpdateProps(context); } - break; + return; case AL_DEBUG_OUTPUT_EXT: - context->mDebugEnabled = true; - break; + context->mDebugEnabled.store(true); + return; case AL_STOP_SOURCES_ON_DISCONNECT_SOFT: context->setError(AL_INVALID_OPERATION, "Re-enabling AL_STOP_SOURCES_ON_DISCONNECT_SOFT not yet supported"); - break; - - default: - context->setError(AL_INVALID_VALUE, "Invalid enable property 0x%04x", capability); + return; } + context->setError(AL_INVALID_VALUE, "Invalid enable property {:#04x}", + as_unsigned(capability)); } -AL_API DECL_FUNC1(void, alDisable, ALenum) +AL_API DECL_FUNC1(void, alDisable, ALenum,capability) FORCE_ALIGN void AL_APIENTRY alDisableDirect(ALCcontext *context, ALenum capability) noexcept { switch(capability) { case AL_SOURCE_DISTANCE_MODEL: { - std::lock_guard _{context->mPropLock}; + std::lock_guard proplock{context->mPropLock}; context->mSourceDistanceModel = false; UpdateProps(context); } - break; + return; case AL_DEBUG_OUTPUT_EXT: - context->mDebugEnabled = false; - break; + context->mDebugEnabled.store(false); + return; case AL_STOP_SOURCES_ON_DISCONNECT_SOFT: - context->mStopVoicesOnDisconnect = false; - break; - - default: - context->setError(AL_INVALID_VALUE, "Invalid disable property 0x%04x", capability); + context->mStopVoicesOnDisconnect.store(false); + return; } + context->setError(AL_INVALID_VALUE, "Invalid disable property {:#04x}", + as_unsigned(capability)); } -AL_API DECL_FUNC1(ALboolean, alIsEnabled, ALenum) +AL_API DECL_FUNC1(ALboolean, alIsEnabled, ALenum,capability) FORCE_ALIGN ALboolean AL_APIENTRY alIsEnabledDirect(ALCcontext *context, ALenum capability) noexcept { - std::lock_guard _{context->mPropLock}; - ALboolean value{AL_FALSE}; + std::lock_guard proplock{context->mPropLock}; switch(capability) { - case AL_SOURCE_DISTANCE_MODEL: - value = context->mSourceDistanceModel ? AL_TRUE : AL_FALSE; - break; - - case AL_DEBUG_OUTPUT_EXT: - value = context->mDebugEnabled ? AL_TRUE : AL_FALSE; - break; - + case AL_SOURCE_DISTANCE_MODEL: return context->mSourceDistanceModel ? AL_TRUE : AL_FALSE; + case AL_DEBUG_OUTPUT_EXT: return context->mDebugEnabled ? AL_TRUE : AL_FALSE; case AL_STOP_SOURCES_ON_DISCONNECT_SOFT: - value = context->mStopVoicesOnDisconnect ? AL_TRUE : AL_FALSE; - break; - - default: - context->setError(AL_INVALID_VALUE, "Invalid is enabled property 0x%04x", capability); + return context->mStopVoicesOnDisconnect.load() ? AL_TRUE : AL_FALSE; } - - return value; + context->setError(AL_INVALID_VALUE, "Invalid is enabled property {:#04x}", + as_unsigned(capability)); + return AL_FALSE; } #define DECL_GETFUNC(R, Name, Ext) \ -AL_API R AL_APIENTRY Name##Ext(ALenum pname) noexcept \ +auto AL_APIENTRY Name##Ext(ALenum pname) noexcept -> R \ { \ - R value{}; \ + auto value = R{}; \ auto context = GetContextRef(); \ if(!context) UNLIKELY return value; \ Name##vDirect##Ext(GetContextRef().get(), pname, &value); \ return value; \ } \ -FORCE_ALIGN R AL_APIENTRY Name##Direct##Ext(ALCcontext *context, ALenum pname) noexcept \ +FORCE_ALIGN auto AL_APIENTRY Name##Direct##Ext(ALCcontext *context, ALenum pname) noexcept -> R \ { \ - R value{}; \ + auto value = R{}; \ Name##vDirect##Ext(context, pname, &value); \ return value; \ } -DECL_GETFUNC(ALboolean, alGetBoolean,) -DECL_GETFUNC(ALdouble, alGetDouble,) -DECL_GETFUNC(ALfloat, alGetFloat,) -DECL_GETFUNC(ALint, alGetInteger,) +AL_API DECL_GETFUNC(ALboolean, alGetBoolean,) +AL_API DECL_GETFUNC(ALdouble, alGetDouble,) +AL_API DECL_GETFUNC(ALfloat, alGetFloat,) +AL_API DECL_GETFUNC(ALint, alGetInteger,) -DECL_GETFUNC(ALint64SOFT, alGetInteger64,SOFT) -DECL_GETFUNC(ALvoid*, alGetPointer,SOFT) +DECL_GETFUNC(ALvoidptr, alGetPointer,EXT) +AL_API DECL_GETFUNC(ALint64SOFT, alGetInteger64,SOFT) +AL_API DECL_GETFUNC(ALvoidptr, alGetPointer,SOFT) #undef DECL_GETFUNC -AL_API DECL_FUNC2(void, alGetBooleanv, ALenum, ALboolean*) +AL_API DECL_FUNC2(void, alGetBooleanv, ALenum,pname, ALboolean*,values) FORCE_ALIGN void AL_APIENTRY alGetBooleanvDirect(ALCcontext *context, ALenum pname, ALboolean *values) noexcept { if(!values) UNLIKELY @@ -420,7 +413,7 @@ FORCE_ALIGN void AL_APIENTRY alGetBooleanvDirect(ALCcontext *context, ALenum pna GetValue(context, pname, values); } -AL_API DECL_FUNC2(void, alGetDoublev, ALenum, ALdouble*) +AL_API DECL_FUNC2(void, alGetDoublev, ALenum,pname, ALdouble*,values) FORCE_ALIGN void AL_APIENTRY alGetDoublevDirect(ALCcontext *context, ALenum pname, ALdouble *values) noexcept { if(!values) UNLIKELY @@ -428,7 +421,7 @@ FORCE_ALIGN void AL_APIENTRY alGetDoublevDirect(ALCcontext *context, ALenum pnam GetValue(context, pname, values); } -AL_API DECL_FUNC2(void, alGetFloatv, ALenum, ALfloat*) +AL_API DECL_FUNC2(void, alGetFloatv, ALenum,pname, ALfloat*,values) FORCE_ALIGN void AL_APIENTRY alGetFloatvDirect(ALCcontext *context, ALenum pname, ALfloat *values) noexcept { if(!values) UNLIKELY @@ -436,7 +429,7 @@ FORCE_ALIGN void AL_APIENTRY alGetFloatvDirect(ALCcontext *context, ALenum pname GetValue(context, pname, values); } -AL_API DECL_FUNC2(void, alGetIntegerv, ALenum, ALint*) +AL_API DECL_FUNC2(void, alGetIntegerv, ALenum,pname, ALint*,values) FORCE_ALIGN void AL_APIENTRY alGetIntegervDirect(ALCcontext *context, ALenum pname, ALint *values) noexcept { if(!values) UNLIKELY @@ -444,7 +437,7 @@ FORCE_ALIGN void AL_APIENTRY alGetIntegervDirect(ALCcontext *context, ALenum pna GetValue(context, pname, values); } -AL_API DECL_FUNCEXT2(void, alGetInteger64v,SOFT, ALenum, ALint64SOFT*) +AL_API DECL_FUNCEXT2(void, alGetInteger64v,SOFT, ALenum,pname, ALint64SOFT*,values) FORCE_ALIGN void AL_APIENTRY alGetInteger64vDirectSOFT(ALCcontext *context, ALenum pname, ALint64SOFT *values) noexcept { if(!values) UNLIKELY @@ -452,8 +445,12 @@ FORCE_ALIGN void AL_APIENTRY alGetInteger64vDirectSOFT(ALCcontext *context, ALen GetValue(context, pname, values); } -AL_API DECL_FUNCEXT2(void, alGetPointerv,SOFT, ALenum, ALvoid**) +AL_API DECL_FUNCEXT2(void, alGetPointerv,SOFT, ALenum,pname, ALvoid**,values) FORCE_ALIGN void AL_APIENTRY alGetPointervDirectSOFT(ALCcontext *context, ALenum pname, ALvoid **values) noexcept +{ return alGetPointervDirectEXT(context, pname, values); } + +FORCE_ALIGN DECL_FUNCEXT2(void, alGetPointerv,EXT, ALenum,pname, ALvoid**,values) +FORCE_ALIGN void AL_APIENTRY alGetPointervDirectEXT(ALCcontext *context, ALenum pname, ALvoid **values) noexcept { if(!values) UNLIKELY return context->setError(AL_INVALID_VALUE, "NULL pointer"); @@ -461,159 +458,127 @@ FORCE_ALIGN void AL_APIENTRY alGetPointervDirectSOFT(ALCcontext *context, ALenum switch(pname) { case AL_EVENT_CALLBACK_FUNCTION_SOFT: - *values = al::bit_cast(context->mEventCb); - break; + *values = reinterpret_cast(context->mEventCb); + return; case AL_EVENT_CALLBACK_USER_PARAM_SOFT: *values = context->mEventParam; - break; + return; case AL_DEBUG_CALLBACK_FUNCTION_EXT: - *values = al::bit_cast(context->mDebugCb); - break; + *values = reinterpret_cast(context->mDebugCb); + return; case AL_DEBUG_CALLBACK_USER_PARAM_EXT: *values = context->mDebugParam; - break; - - default: - context->setError(AL_INVALID_ENUM, "Invalid context pointer property 0x%04x", pname); + return; } + context->setError(AL_INVALID_ENUM, "Invalid context pointer property {:#04x}", + as_unsigned(pname)); } -AL_API DECL_FUNC1(const ALchar*, alGetString, ALenum) +AL_API DECL_FUNC1(const ALchar*, alGetString, ALenum,pname) FORCE_ALIGN const ALchar* AL_APIENTRY alGetStringDirect(ALCcontext *context, ALenum pname) noexcept { - const ALchar *value{nullptr}; switch(pname) { case AL_VENDOR: - value = alVendor; - break; - + if(auto device = context->mALDevice.get(); !device->mVendorOverride.empty()) + return device->mVendorOverride.c_str(); + return GetVendorString(); case AL_VERSION: - value = alVersion; - break; - + if(auto device = context->mALDevice.get(); !device->mVersionOverride.empty()) + return device->mVersionOverride.c_str(); + return GetVersionString(); case AL_RENDERER: - value = alRenderer; - break; - - case AL_EXTENSIONS: - value = context->mExtensionsString.c_str(); - break; - - case AL_NO_ERROR: - value = alNoError; - break; - - case AL_INVALID_NAME: - value = alErrInvalidName; - break; - - case AL_INVALID_ENUM: - value = alErrInvalidEnum; - break; - - case AL_INVALID_VALUE: - value = alErrInvalidValue; - break; - - case AL_INVALID_OPERATION: - value = alErrInvalidOp; - break; - - case AL_OUT_OF_MEMORY: - value = alErrOutOfMemory; - break; - - case AL_STACK_OVERFLOW_EXT: - value = alStackOverflow; - break; - - case AL_STACK_UNDERFLOW_EXT: - value = alStackUnderflow; - break; - - default: - context->setError(AL_INVALID_VALUE, "Invalid string property 0x%04x", pname); + if(auto device = context->mALDevice.get(); !device->mRendererOverride.empty()) + return device->mRendererOverride.c_str(); + return GetRendererString(); + case AL_EXTENSIONS: return context->mExtensionsString.c_str(); + case AL_NO_ERROR: return GetNoErrorString(); + case AL_INVALID_NAME: return GetInvalidNameString(); + case AL_INVALID_ENUM: return GetInvalidEnumString(); + case AL_INVALID_VALUE: return GetInvalidValueString(); + case AL_INVALID_OPERATION: return GetInvalidOperationString(); + case AL_OUT_OF_MEMORY: return GetOutOfMemoryString(); + case AL_STACK_OVERFLOW_EXT: return GetStackOverflowString(); + case AL_STACK_UNDERFLOW_EXT: return GetStackUnderflowString(); } - return value; + context->setError(AL_INVALID_VALUE, "Invalid string property {:#04x}", as_unsigned(pname)); + return nullptr; } -AL_API DECL_FUNC1(void, alDopplerFactor, ALfloat) +AL_API DECL_FUNC1(void, alDopplerFactor, ALfloat,value) FORCE_ALIGN void AL_APIENTRY alDopplerFactorDirect(ALCcontext *context, ALfloat value) noexcept { if(!(value >= 0.0f && std::isfinite(value))) - context->setError(AL_INVALID_VALUE, "Doppler factor %f out of range", value); + context->setError(AL_INVALID_VALUE, "Doppler factor {:f} out of range", value); else { - std::lock_guard _{context->mPropLock}; + std::lock_guard proplock{context->mPropLock}; context->mDopplerFactor = value; UpdateProps(context); } } -AL_API DECL_FUNC1(void, alSpeedOfSound, ALfloat) +AL_API DECL_FUNC1(void, alSpeedOfSound, ALfloat,value) FORCE_ALIGN void AL_APIENTRY alSpeedOfSoundDirect(ALCcontext *context, ALfloat value) noexcept { if(!(value > 0.0f && std::isfinite(value))) - context->setError(AL_INVALID_VALUE, "Speed of sound %f out of range", value); + context->setError(AL_INVALID_VALUE, "Speed of sound {:f} out of range", value); else { - std::lock_guard _{context->mPropLock}; + std::lock_guard proplock{context->mPropLock}; context->mSpeedOfSound = value; UpdateProps(context); } } -AL_API DECL_FUNC1(void, alDistanceModel, ALenum) +AL_API DECL_FUNC1(void, alDistanceModel, ALenum,value) FORCE_ALIGN void AL_APIENTRY alDistanceModelDirect(ALCcontext *context, ALenum value) noexcept { if(auto model = DistanceModelFromALenum(value)) { - std::lock_guard _{context->mPropLock}; + std::lock_guard proplock{context->mPropLock}; context->mDistanceModel = *model; if(!context->mSourceDistanceModel) UpdateProps(context); } else - context->setError(AL_INVALID_VALUE, "Distance model 0x%04x out of range", value); + context->setError(AL_INVALID_VALUE, "Distance model {:#04x} out of range", + as_unsigned(value)); } AL_API DECL_FUNCEXT(void, alDeferUpdates,SOFT) FORCE_ALIGN void AL_APIENTRY alDeferUpdatesDirectSOFT(ALCcontext *context) noexcept { - std::lock_guard _{context->mPropLock}; + std::lock_guard proplock{context->mPropLock}; context->deferUpdates(); } AL_API DECL_FUNCEXT(void, alProcessUpdates,SOFT) FORCE_ALIGN void AL_APIENTRY alProcessUpdatesDirectSOFT(ALCcontext *context) noexcept { - std::lock_guard _{context->mPropLock}; + std::lock_guard proplock{context->mPropLock}; context->processUpdates(); } -AL_API DECL_FUNCEXT2(const ALchar*, alGetStringi,SOFT, ALenum,ALsizei) +AL_API DECL_FUNCEXT2(const ALchar*, alGetStringi,SOFT, ALenum,pname, ALsizei,index) FORCE_ALIGN const ALchar* AL_APIENTRY alGetStringiDirectSOFT(ALCcontext *context, ALenum pname, ALsizei index) noexcept { - const ALchar *value{nullptr}; switch(pname) { case AL_RESAMPLER_NAME_SOFT: - if(index < 0 || index > static_cast(Resampler::Max)) - context->setError(AL_INVALID_VALUE, "Resampler name index %d out of range", index); - else - value = GetResamplerName(static_cast(index)); - break; - - default: - context->setError(AL_INVALID_VALUE, "Invalid string indexed property"); + if(index >= 0 && index <= static_cast(Resampler::Max)) + return GetResamplerName(static_cast(index)); + context->setError(AL_INVALID_VALUE, "Resampler name index {} out of range", index); + return nullptr; } - return value; + context->setError(AL_INVALID_VALUE, "Invalid string indexed property {:#04x}", + as_unsigned(pname)); + return nullptr; } @@ -623,16 +588,16 @@ AL_API void AL_APIENTRY alDopplerVelocity(ALfloat value) noexcept if(!context) UNLIKELY return; if(context->mContextFlags.test(ContextFlags::DebugBit)) UNLIKELY - context->debugMessage(DebugSource::API, DebugType::DeprecatedBehavior, 0, + context->debugMessage(DebugSource::API, DebugType::DeprecatedBehavior, 1, DebugSeverity::Medium, "alDopplerVelocity is deprecated in AL 1.1, use alSpeedOfSound; " "alDopplerVelocity(x) -> alSpeedOfSound(343.3f * x)"); if(!(value >= 0.0f && std::isfinite(value))) - context->setError(AL_INVALID_VALUE, "Doppler velocity %f out of range", value); + context->setError(AL_INVALID_VALUE, "Doppler velocity {:f} out of range", value); else { - std::lock_guard _{context->mPropLock}; + std::lock_guard proplock{context->mPropLock}; context->mDopplerVelocity = value; UpdateProps(context.get()); } @@ -641,21 +606,21 @@ AL_API void AL_APIENTRY alDopplerVelocity(ALfloat value) noexcept void UpdateContextProps(ALCcontext *context) { - /* Get an unused proprty container, or allocate a new one as needed. */ + /* Get an unused property container, or allocate a new one as needed. */ ContextProps *props{context->mFreeContextProps.load(std::memory_order_acquire)}; if(!props) - props = new ContextProps{}; - else { - ContextProps *next; - do { - next = props->next.load(std::memory_order_relaxed); - } while(context->mFreeContextProps.compare_exchange_weak(props, next, - std::memory_order_seq_cst, std::memory_order_acquire) == 0); + context->allocContextProps(); + props = context->mFreeContextProps.load(std::memory_order_acquire); } + ContextProps *next; + do { + next = props->next.load(std::memory_order_relaxed); + } while(context->mFreeContextProps.compare_exchange_weak(props, next, + std::memory_order_acq_rel, std::memory_order_acquire) == false); /* Copy in current property values. */ - ALlistener &listener = context->mListener; + const auto &listener = context->mListener; props->Position = listener.Position; props->Velocity = listener.Velocity; props->OrientAt = listener.OrientAt; @@ -667,6 +632,9 @@ void UpdateContextProps(ALCcontext *context) props->DopplerFactor = context->mDopplerFactor; props->DopplerVelocity = context->mDopplerVelocity; props->SpeedOfSound = context->mSpeedOfSound; +#if ALSOFT_EAX + props->DistanceFactor = context->eaxGetDistanceFactor(); +#endif props->SourceDistanceModel = context->mSourceDistanceModel; props->mDistanceModel = context->mDistanceModel; diff --git a/3rdparty/openal/alc/alc.cpp b/3rdparty/openal/alc/alc.cpp index baa4788f3d7e..d378364bed18 100644 --- a/3rdparty/openal/alc/alc.cpp +++ b/3rdparty/openal/alc/alc.cpp @@ -19,6 +19,8 @@ */ #include "config.h" +#include "config_backends.h" +#include "config_simd.h" #include "version.h" @@ -34,15 +36,14 @@ #include #include #include -#include #include #include #include #include -#include #include #include #include +#include #include #include #include @@ -50,10 +51,10 @@ #include #include #include -#include #include #include -#include +#include +#include #include #include @@ -67,11 +68,12 @@ #include "al/debug.h" #include "al/effect.h" #include "al/filter.h" -#include "al/listener.h" #include "al/source.h" +#include "alc/events.h" #include "albit.h" #include "alconfig.h" #include "almalloc.h" +#include "alnumbers.h" #include "alnumeric.h" #include "alspan.h" #include "alstring.h" @@ -85,13 +87,12 @@ #include "core/cpu_caps.h" #include "core/devformat.h" #include "core/device.h" +#include "core/effects/base.h" #include "core/effectslot.h" -#include "core/except.h" +#include "core/filters/nfc.h" #include "core/helpers.h" #include "core/mastering.h" -#include "core/mixer/hrtfdefs.h" #include "core/fpu_ctrl.h" -#include "core/front_stablizer.h" #include "core/logging.h" #include "core/uhjfilter.h" #include "core/voice.h" @@ -99,6 +100,8 @@ #include "device.h" #include "effects/base.h" #include "export_list.h" +#include "flexarray.h" +#include "fmt/core.h" #include "inprogext.h" #include "intrusive_ptr.h" #include "opthelpers.h" @@ -107,59 +110,65 @@ #include "backends/base.h" #include "backends/null.h" #include "backends/loopback.h" -#ifdef HAVE_PIPEWIRE +#if HAVE_PIPEWIRE #include "backends/pipewire.h" #endif -#ifdef HAVE_JACK +#if HAVE_JACK #include "backends/jack.h" #endif -#ifdef HAVE_PULSEAUDIO +#if HAVE_PULSEAUDIO #include "backends/pulseaudio.h" #endif -#ifdef HAVE_ALSA +#if HAVE_ALSA #include "backends/alsa.h" #endif -#ifdef HAVE_WASAPI +#if HAVE_WASAPI #include "backends/wasapi.h" #endif -#ifdef HAVE_COREAUDIO +#if HAVE_COREAUDIO #include "backends/coreaudio.h" #endif -#ifdef HAVE_OPENSL +#if HAVE_OPENSL #include "backends/opensl.h" #endif -#ifdef HAVE_OBOE +#if HAVE_OBOE #include "backends/oboe.h" #endif -#ifdef HAVE_SOLARIS +#if HAVE_SOLARIS #include "backends/solaris.h" #endif -#ifdef HAVE_SNDIO -#include "backends/sndio.h" +#if HAVE_SNDIO +#include "backends/sndio.hpp" #endif -#ifdef HAVE_OSS +#if HAVE_OSS #include "backends/oss.h" #endif -#ifdef HAVE_DSOUND +#if HAVE_DSOUND #include "backends/dsound.h" #endif -#ifdef HAVE_WINMM +#if HAVE_WINMM #include "backends/winmm.h" #endif -#ifdef HAVE_PORTAUDIO -#include "backends/portaudio.h" +#if HAVE_PORTAUDIO +#include "backends/portaudio.hpp" +#endif +#if HAVE_SDL3 +#include "backends/sdl3.h" #endif -#ifdef HAVE_SDL2 +#if HAVE_SDL2 #include "backends/sdl2.h" #endif -#ifdef HAVE_WAVE +#if HAVE_OTHERIO +#include "backends/otherio.h" +#endif +#if HAVE_WAVE #include "backends/wave.h" #endif -#ifdef ALSOFT_EAX +#if ALSOFT_EAX +#include "al/eax/api.h" #include "al/eax/globals.h" -#include "al/eax/x_ram.h" -#endif // ALSOFT_EAX +#endif /************************************************ @@ -173,7 +182,7 @@ BOOL APIENTRY DllMain(HINSTANCE module, DWORD reason, LPVOID /*reserved*/) case DLL_PROCESS_ATTACH: /* Pin the DLL so we won't get unloaded until the process terminates */ GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, - al::bit_cast(module), &module); + reinterpret_cast(module), &module); break; } return TRUE; @@ -182,7 +191,7 @@ BOOL APIENTRY DllMain(HINSTANCE module, DWORD reason, LPVOID /*reserved*/) namespace { -using namespace std::placeholders; +using namespace std::string_view_literals; using std::chrono::seconds; using std::chrono::nanoseconds; @@ -190,64 +199,79 @@ using voidp = void*; using float2 = std::array; +auto gProcessRunning = true; +struct ProcessWatcher { + ProcessWatcher() = default; + ProcessWatcher(const ProcessWatcher&) = delete; + ProcessWatcher& operator=(const ProcessWatcher&) = delete; + ~ProcessWatcher() { gProcessRunning = false; } +}; +ProcessWatcher gProcessWatcher; + /************************************************ * Backends ************************************************/ struct BackendInfo { const char *name; - BackendFactory& (*getFactory)(void); + BackendFactory& (*getFactory)(); }; -BackendInfo BackendList[] = { -#ifdef HAVE_PIPEWIRE - { "pipewire", PipeWireBackendFactory::getFactory }, +std::array BackendList{ +#if HAVE_PIPEWIRE + BackendInfo{"pipewire", PipeWireBackendFactory::getFactory}, #endif -#ifdef HAVE_PULSEAUDIO - { "pulse", PulseBackendFactory::getFactory }, +#if HAVE_PULSEAUDIO + BackendInfo{"pulse", PulseBackendFactory::getFactory}, #endif -#ifdef HAVE_WASAPI - { "wasapi", WasapiBackendFactory::getFactory }, +#if HAVE_WASAPI + BackendInfo{"wasapi", WasapiBackendFactory::getFactory}, +#endif +#if HAVE_COREAUDIO + BackendInfo{"core", CoreAudioBackendFactory::getFactory}, +#endif +#if HAVE_OBOE + BackendInfo{"oboe", OboeBackendFactory::getFactory}, #endif -#ifdef HAVE_COREAUDIO - { "core", CoreAudioBackendFactory::getFactory }, +#if HAVE_OPENSL + BackendInfo{"opensl", OSLBackendFactory::getFactory}, #endif -#ifdef HAVE_OBOE - { "oboe", OboeBackendFactory::getFactory }, +#if HAVE_ALSA + BackendInfo{"alsa", AlsaBackendFactory::getFactory}, #endif -#ifdef HAVE_OPENSL - { "opensl", OSLBackendFactory::getFactory }, +#if HAVE_SOLARIS + BackendInfo{"solaris", SolarisBackendFactory::getFactory}, #endif -#ifdef HAVE_ALSA - { "alsa", AlsaBackendFactory::getFactory }, +#if HAVE_SNDIO + BackendInfo{"sndio", SndIOBackendFactory::getFactory}, #endif -#ifdef HAVE_SOLARIS - { "solaris", SolarisBackendFactory::getFactory }, +#if HAVE_OSS + BackendInfo{"oss", OSSBackendFactory::getFactory}, #endif -#ifdef HAVE_SNDIO - { "sndio", SndIOBackendFactory::getFactory }, +#if HAVE_DSOUND + BackendInfo{"dsound", DSoundBackendFactory::getFactory}, #endif -#ifdef HAVE_OSS - { "oss", OSSBackendFactory::getFactory }, +#if HAVE_WINMM + BackendInfo{"winmm", WinMMBackendFactory::getFactory}, #endif -#ifdef HAVE_JACK - { "jack", JackBackendFactory::getFactory }, +#if HAVE_PORTAUDIO + BackendInfo{"port", PortBackendFactory::getFactory}, #endif -#ifdef HAVE_DSOUND - { "dsound", DSoundBackendFactory::getFactory }, +#if HAVE_SDL3 + BackendInfo{"sdl3", SDL3BackendFactory::getFactory}, #endif -#ifdef HAVE_WINMM - { "winmm", WinMMBackendFactory::getFactory }, +#if HAVE_SDL2 + BackendInfo{"sdl2", SDL2BackendFactory::getFactory}, #endif -#ifdef HAVE_PORTAUDIO - { "port", PortBackendFactory::getFactory }, +#if HAVE_JACK + BackendInfo{"jack", JackBackendFactory::getFactory}, #endif -#ifdef HAVE_SDL2 - { "sdl2", SDL2BackendFactory::getFactory }, +#if HAVE_OTHERIO + BackendInfo{"otherio", OtherIOBackendFactory::getFactory}, #endif - { "null", NullBackendFactory::getFactory }, -#ifdef HAVE_WAVE - { "wave", WaveBackendFactory::getFactory }, + BackendInfo{"null", NullBackendFactory::getFactory}, +#if HAVE_WAVE + BackendInfo{"wave", WaveBackendFactory::getFactory}, #endif }; @@ -255,21 +279,28 @@ BackendFactory *PlaybackFactory{}; BackendFactory *CaptureFactory{}; -constexpr ALCchar alcNoError[] = "No Error"; -constexpr ALCchar alcErrInvalidDevice[] = "Invalid Device"; -constexpr ALCchar alcErrInvalidContext[] = "Invalid Context"; -constexpr ALCchar alcErrInvalidEnum[] = "Invalid Enum"; -constexpr ALCchar alcErrInvalidValue[] = "Invalid Value"; -constexpr ALCchar alcErrOutOfMemory[] = "Out of Memory"; +[[nodiscard]] constexpr auto GetNoErrorString() noexcept { return "No Error"; } +[[nodiscard]] constexpr auto GetInvalidDeviceString() noexcept { return "Invalid Device"; } +[[nodiscard]] constexpr auto GetInvalidContextString() noexcept { return "Invalid Context"; } +[[nodiscard]] constexpr auto GetInvalidEnumString() noexcept { return "Invalid Enum"; } +[[nodiscard]] constexpr auto GetInvalidValueString() noexcept { return "Invalid Value"; } +[[nodiscard]] constexpr auto GetOutOfMemoryString() noexcept { return "Out of Memory"; } +[[nodiscard]] constexpr auto GetDefaultName() noexcept { return "OpenAL Soft\0"; } + +#ifdef _WIN32 +[[nodiscard]] constexpr auto GetDevicePrefix() noexcept { return "OpenAL Soft on "sv; } +#else +[[nodiscard]] constexpr auto GetDevicePrefix() noexcept { return std::string_view{}; } +#endif /************************************************ * Global variables ************************************************/ /* Enumerated device names */ -constexpr ALCchar alcDefaultName[] = "OpenAL Soft\0"; - +std::vector alcAllDevicesArray; +std::vector alcCaptureDeviceArray; std::string alcAllDevicesList; std::string alcCaptureDeviceList; @@ -297,36 +328,41 @@ constexpr uint DitherRNGSeed{22222u}; /************************************************ * ALC information ************************************************/ -constexpr ALCchar alcNoDeviceExtList[] = - "ALC_ENUMERATE_ALL_EXT " - "ALC_ENUMERATION_EXT " - "ALC_EXT_CAPTURE " - "ALC_EXTX_direct_context " - "ALC_EXT_EFX " - "ALC_EXT_thread_local_context " - "ALC_SOFT_loopback " - "ALC_SOFT_loopback_bformat " - "ALC_SOFT_reopen_device " - "ALC_SOFT_system_events"; -constexpr ALCchar alcExtensionList[] = - "ALC_ENUMERATE_ALL_EXT " - "ALC_ENUMERATION_EXT " - "ALC_EXT_CAPTURE " - "ALC_EXT_debug " - "ALC_EXT_DEDICATED " - "ALC_EXTX_direct_context " - "ALC_EXT_disconnect " - "ALC_EXT_EFX " - "ALC_EXT_thread_local_context " - "ALC_SOFT_device_clock " - "ALC_SOFT_HRTF " - "ALC_SOFT_loopback " - "ALC_SOFT_loopback_bformat " - "ALC_SOFT_output_limiter " - "ALC_SOFT_output_mode " - "ALC_SOFT_pause_device " - "ALC_SOFT_reopen_device " - "ALC_SOFT_system_events"; +[[nodiscard]] constexpr auto GetNoDeviceExtList() noexcept -> const char* +{ + return "ALC_ENUMERATE_ALL_EXT " + "ALC_ENUMERATION_EXT " + "ALC_EXT_CAPTURE " + "ALC_EXT_direct_context " + "ALC_EXT_EFX " + "ALC_EXT_thread_local_context " + "ALC_SOFT_loopback " + "ALC_SOFT_loopback_bformat " + "ALC_SOFT_reopen_device " + "ALC_SOFT_system_events"; +} +[[nodiscard]] constexpr auto GetExtensionList() noexcept -> const char* +{ + return "ALC_ENUMERATE_ALL_EXT " + "ALC_ENUMERATION_EXT " + "ALC_EXT_CAPTURE " + "ALC_EXT_debug " + "ALC_EXT_DEDICATED " + "ALC_EXT_direct_context " + "ALC_EXT_disconnect " + "ALC_EXT_EFX " + "ALC_EXT_thread_local_context " + "ALC_SOFT_device_clock " + "ALC_SOFT_HRTF " + "ALC_SOFT_loopback " + "ALC_SOFT_loopback_bformat " + "ALC_SOFT_output_limiter " + "ALC_SOFT_output_mode " + "ALC_SOFT_pause_device " + "ALC_SOFT_reopen_device " + "ALC_SOFT_system_events"; +} + constexpr int alcMajorVersion{1}; constexpr int alcMinorVersion{1}; @@ -334,19 +370,19 @@ constexpr int alcEFXMajorVersion{1}; constexpr int alcEFXMinorVersion{0}; -using DeviceRef = al::intrusive_ptr; +using DeviceRef = al::intrusive_ptr; /************************************************ * Device lists ************************************************/ -std::vector DeviceList; +std::vector DeviceList; std::vector ContextList; std::recursive_mutex ListLock; -void alc_initconfig(void) +void alc_initconfig() { if(auto loglevel = al::getenv("ALSOFT_LOGLEVEL")) { @@ -367,7 +403,7 @@ void alc_initconfig(void) else { auto u8name = wstr_to_utf8(*logfile); - ERR("Failed to open log file '%s'\n", u8name.c_str()); + ERR("Failed to open log file '{}'", u8name); } } #else @@ -375,11 +411,11 @@ void alc_initconfig(void) { FILE *logf{fopen(logfile->c_str(), "wt")}; if(logf) gLogFile = logf; - else ERR("Failed to open log file '%s'\n", logfile->c_str()); + else ERR("Failed to open log file '{}'", *logfile); } #endif - TRACE("Initializing library v%s-%s %s\n", ALSOFT_VERSION, ALSOFT_GIT_COMMIT_HASH, + TRACE("Initializing library v{}-{} {}", ALSOFT_VERSION, ALSOFT_GIT_COMMIT_HASH, ALSOFT_GIT_BRANCH); { std::string names; @@ -395,139 +431,137 @@ void alc_initconfig(void) names += backend.name; } } - TRACE("Supported backends: %s\n", names.c_str()); + TRACE("Supported backends: {}", names); } ReadALConfig(); if(auto suspendmode = al::getenv("__ALSOFT_SUSPEND_CONTEXT")) { - if(al::strcasecmp(suspendmode->c_str(), "ignore") == 0) + if(al::case_compare(*suspendmode, "ignore"sv) == 0) { SuspendDefers = false; - TRACE("Selected context suspend behavior, \"ignore\"\n"); + TRACE("Selected context suspend behavior, \"ignore\""); } else - ERR("Unhandled context suspend behavior setting: \"%s\"\n", suspendmode->c_str()); + ERR("Unhandled context suspend behavior setting: \"{}\"", *suspendmode); } int capfilter{0}; -#if defined(HAVE_SSE4_1) +#if HAVE_SSE4_1 capfilter |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3 | CPU_CAP_SSE4_1; -#elif defined(HAVE_SSE3) +#elif HAVE_SSE3 capfilter |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3; -#elif defined(HAVE_SSE2) +#elif HAVE_SSE2 capfilter |= CPU_CAP_SSE | CPU_CAP_SSE2; -#elif defined(HAVE_SSE) +#elif HAVE_SSE capfilter |= CPU_CAP_SSE; #endif -#ifdef HAVE_NEON +#if HAVE_NEON capfilter |= CPU_CAP_NEON; #endif - if(auto cpuopt = ConfigValueStr(nullptr, nullptr, "disable-cpu-exts")) + if(auto cpuopt = ConfigValueStr({}, {}, "disable-cpu-exts"sv)) { - const char *str{cpuopt->c_str()}; - if(al::strcasecmp(str, "all") == 0) + std::string_view cpulist{*cpuopt}; + if(al::case_compare(cpulist, "all"sv) == 0) capfilter = 0; - else + else while(!cpulist.empty()) { - const char *next = str; - do { - str = next; - while(isspace(str[0])) - str++; - next = strchr(str, ','); - - if(!str[0] || str[0] == ',') - continue; - - size_t len{next ? static_cast(next-str) : strlen(str)}; - while(len > 0 && isspace(str[len-1])) - len--; - if(len == 3 && al::strncasecmp(str, "sse", len) == 0) - capfilter &= ~CPU_CAP_SSE; - else if(len == 4 && al::strncasecmp(str, "sse2", len) == 0) - capfilter &= ~CPU_CAP_SSE2; - else if(len == 4 && al::strncasecmp(str, "sse3", len) == 0) - capfilter &= ~CPU_CAP_SSE3; - else if(len == 6 && al::strncasecmp(str, "sse4.1", len) == 0) - capfilter &= ~CPU_CAP_SSE4_1; - else if(len == 4 && al::strncasecmp(str, "neon", len) == 0) - capfilter &= ~CPU_CAP_NEON; - else - WARN("Invalid CPU extension \"%s\"\n", str); - } while(next++); + auto nextpos = std::min(cpulist.find(','), cpulist.size()); + auto entry = cpulist.substr(0, nextpos); + + while(nextpos < cpulist.size() && cpulist[nextpos] == ',') + ++nextpos; + cpulist.remove_prefix(nextpos); + + while(!entry.empty() && std::isspace(entry.front())) + entry.remove_prefix(1); + while(!entry.empty() && std::isspace(entry.back())) + entry.remove_suffix(1); + if(entry.empty()) + continue; + + if(al::case_compare(entry, "sse"sv) == 0) + capfilter &= ~CPU_CAP_SSE; + else if(al::case_compare(entry, "sse2"sv) == 0) + capfilter &= ~CPU_CAP_SSE2; + else if(al::case_compare(entry, "sse3"sv) == 0) + capfilter &= ~CPU_CAP_SSE3; + else if(al::case_compare(entry, "sse4.1"sv) == 0) + capfilter &= ~CPU_CAP_SSE4_1; + else if(al::case_compare(entry, "neon"sv) == 0) + capfilter &= ~CPU_CAP_NEON; + else + WARN("Invalid CPU extension \"{}\"", entry); } } if(auto cpuopt = GetCPUInfo()) { if(!cpuopt->mVendor.empty() || !cpuopt->mName.empty()) { - TRACE("Vendor ID: \"%s\"\n", cpuopt->mVendor.c_str()); - TRACE("Name: \"%s\"\n", cpuopt->mName.c_str()); + TRACE("Vendor ID: \"{}\"", cpuopt->mVendor); + TRACE("Name: \"{}\"", cpuopt->mName); } const int caps{cpuopt->mCaps}; - TRACE("Extensions:%s%s%s%s%s%s\n", - ((capfilter&CPU_CAP_SSE) ? ((caps&CPU_CAP_SSE) ? " +SSE" : " -SSE") : ""), - ((capfilter&CPU_CAP_SSE2) ? ((caps&CPU_CAP_SSE2) ? " +SSE2" : " -SSE2") : ""), - ((capfilter&CPU_CAP_SSE3) ? ((caps&CPU_CAP_SSE3) ? " +SSE3" : " -SSE3") : ""), - ((capfilter&CPU_CAP_SSE4_1) ? ((caps&CPU_CAP_SSE4_1) ? " +SSE4.1" : " -SSE4.1") : ""), - ((capfilter&CPU_CAP_NEON) ? ((caps&CPU_CAP_NEON) ? " +NEON" : " -NEON") : ""), - ((!capfilter) ? " -none-" : "")); + TRACE("Extensions:{}{}{}{}{}{}", + ((capfilter&CPU_CAP_SSE) ?(caps&CPU_CAP_SSE) ?" +SSE"sv : " -SSE"sv : ""sv), + ((capfilter&CPU_CAP_SSE2) ?(caps&CPU_CAP_SSE2) ?" +SSE2"sv : " -SSE2"sv : ""sv), + ((capfilter&CPU_CAP_SSE3) ?(caps&CPU_CAP_SSE3) ?" +SSE3"sv : " -SSE3"sv : ""sv), + ((capfilter&CPU_CAP_SSE4_1)?(caps&CPU_CAP_SSE4_1)?" +SSE4.1"sv : " -SSE4.1"sv : ""sv), + ((capfilter&CPU_CAP_NEON) ?(caps&CPU_CAP_NEON) ?" +NEON"sv : " -NEON"sv : ""sv), + (!capfilter) ? " -none-"sv : ""sv); CPUCapFlags = caps & capfilter; } - if(auto priopt = ConfigValueInt(nullptr, nullptr, "rt-prio")) + if(auto priopt = ConfigValueInt({}, {}, "rt-prio"sv)) RTPrioLevel = *priopt; - if(auto limopt = ConfigValueBool(nullptr, nullptr, "rt-time-limit")) + if(auto limopt = ConfigValueBool({}, {}, "rt-time-limit"sv)) AllowRTTimeLimit = *limopt; { CompatFlagBitset compatflags{}; - auto checkflag = [](const char *envname, const char *optname) -> bool + auto checkflag = [](const char *envname, const std::string_view optname) -> bool { if(auto optval = al::getenv(envname)) { - if(al::strcasecmp(optval->c_str(), "true") == 0 - || strtol(optval->c_str(), nullptr, 0) == 1) - return true; - return false; + return al::case_compare(*optval, "true"sv) == 0 + || strtol(optval->c_str(), nullptr, 0) == 1; } - return GetConfigValueBool(nullptr, "game_compat", optname, false); + return GetConfigValueBool({}, "game_compat", optname, false); }; - sBufferSubDataCompat = checkflag("__ALSOFT_ENABLE_SUB_DATA_EXT", "enable-sub-data-ext"); - compatflags.set(CompatFlags::ReverseX, checkflag("__ALSOFT_REVERSE_X", "reverse-x")); - compatflags.set(CompatFlags::ReverseY, checkflag("__ALSOFT_REVERSE_Y", "reverse-y")); - compatflags.set(CompatFlags::ReverseZ, checkflag("__ALSOFT_REVERSE_Z", "reverse-z")); + sBufferSubDataCompat = checkflag("__ALSOFT_ENABLE_SUB_DATA_EXT", "enable-sub-data-ext"sv); + compatflags.set(CompatFlags::ReverseX, checkflag("__ALSOFT_REVERSE_X", "reverse-x"sv)); + compatflags.set(CompatFlags::ReverseY, checkflag("__ALSOFT_REVERSE_Y", "reverse-y"sv)); + compatflags.set(CompatFlags::ReverseZ, checkflag("__ALSOFT_REVERSE_Z", "reverse-z"sv)); - aluInit(compatflags, ConfigValueFloat(nullptr, "game_compat", "nfc-scale").value_or(1.0f)); + aluInit(compatflags, ConfigValueFloat({}, "game_compat"sv, "nfc-scale"sv).value_or(1.0f)); } - Voice::InitMixer(ConfigValueStr(nullptr, nullptr, "resampler")); + Voice::InitMixer(ConfigValueStr({}, {}, "resampler"sv)); - if(auto uhjfiltopt = ConfigValueStr(nullptr, "uhj", "decode-filter")) + if(auto uhjfiltopt = ConfigValueStr({}, "uhj"sv, "decode-filter"sv)) { - if(al::strcasecmp(uhjfiltopt->c_str(), "fir256") == 0) + if(al::case_compare(*uhjfiltopt, "fir256"sv) == 0) UhjDecodeQuality = UhjQualityType::FIR256; - else if(al::strcasecmp(uhjfiltopt->c_str(), "fir512") == 0) + else if(al::case_compare(*uhjfiltopt, "fir512"sv) == 0) UhjDecodeQuality = UhjQualityType::FIR512; - else if(al::strcasecmp(uhjfiltopt->c_str(), "iir") == 0) + else if(al::case_compare(*uhjfiltopt, "iir"sv) == 0) UhjDecodeQuality = UhjQualityType::IIR; else - WARN("Unsupported uhj/decode-filter: %s\n", uhjfiltopt->c_str()); + WARN("Unsupported uhj/decode-filter: {}", *uhjfiltopt); } - if(auto uhjfiltopt = ConfigValueStr(nullptr, "uhj", "encode-filter")) + if(auto uhjfiltopt = ConfigValueStr({}, "uhj"sv, "encode-filter"sv)) { - if(al::strcasecmp(uhjfiltopt->c_str(), "fir256") == 0) + if(al::case_compare(*uhjfiltopt, "fir256"sv) == 0) UhjEncodeQuality = UhjQualityType::FIR256; - else if(al::strcasecmp(uhjfiltopt->c_str(), "fir512") == 0) + else if(al::case_compare(*uhjfiltopt, "fir512"sv) == 0) UhjEncodeQuality = UhjQualityType::FIR512; - else if(al::strcasecmp(uhjfiltopt->c_str(), "iir") == 0) + else if(al::case_compare(*uhjfiltopt, "iir"sv) == 0) UhjEncodeQuality = UhjQualityType::IIR; else - WARN("Unsupported uhj/encode-filter: %s\n", uhjfiltopt->c_str()); + WARN("Unsupported uhj/encode-filter: {}", *uhjfiltopt); } - auto traperr = al::getenv("ALSOFT_TRAP_ERROR"); - if(traperr && (al::strcasecmp(traperr->c_str(), "true") == 0 + if(auto traperr = al::getenv("ALSOFT_TRAP_ERROR"); traperr + && (al::case_compare(*traperr, "true"sv) == 0 || std::strtol(traperr->c_str(), nullptr, 0) == 1)) { TrapALError = true; @@ -537,66 +571,69 @@ void alc_initconfig(void) { traperr = al::getenv("ALSOFT_TRAP_AL_ERROR"); if(traperr) - TrapALError = al::strcasecmp(traperr->c_str(), "true") == 0 + TrapALError = al::case_compare(*traperr, "true"sv) == 0 || strtol(traperr->c_str(), nullptr, 0) == 1; else - TrapALError = !!GetConfigValueBool(nullptr, nullptr, "trap-al-error", false); + TrapALError = GetConfigValueBool({}, {}, "trap-al-error"sv, false); traperr = al::getenv("ALSOFT_TRAP_ALC_ERROR"); if(traperr) - TrapALCError = al::strcasecmp(traperr->c_str(), "true") == 0 + TrapALCError = al::case_compare(*traperr, "true"sv) == 0 || strtol(traperr->c_str(), nullptr, 0) == 1; else - TrapALCError = !!GetConfigValueBool(nullptr, nullptr, "trap-alc-error", false); + TrapALCError = GetConfigValueBool({}, {}, "trap-alc-error"sv, false); } - if(auto boostopt = ConfigValueFloat(nullptr, "reverb", "boost")) + if(auto boostopt = ConfigValueFloat({}, "reverb"sv, "boost"sv)) { - const float valf{std::isfinite(*boostopt) ? clampf(*boostopt, -24.0f, 24.0f) : 0.0f}; + const float valf{std::isfinite(*boostopt) ? std::clamp(*boostopt, -24.0f, 24.0f) : 0.0f}; ReverbBoost *= std::pow(10.0f, valf / 20.0f); } - auto BackendListEnd = std::end(BackendList); + auto BackendListEnd = BackendList.end(); auto devopt = al::getenv("ALSOFT_DRIVERS"); - if(devopt || (devopt=ConfigValueStr(nullptr, nullptr, "drivers"))) + if(!devopt) devopt = ConfigValueStr({}, {}, "drivers"sv); + if(devopt) { - auto backendlist_cur = std::begin(BackendList); + auto backendlist_cur = BackendList.begin(); bool endlist{true}; - const char *next{devopt->c_str()}; - do { - const char *devs{next}; - while(isspace(devs[0])) - devs++; - next = strchr(devs, ','); - - const bool delitem{devs[0] == '-'}; - if(devs[0] == '-') devs++; + std::string_view drvlist{*devopt}; + while(!drvlist.empty()) + { + auto nextpos = std::min(drvlist.find(','), drvlist.size()); + auto entry = drvlist.substr(0, nextpos); - if(!devs[0] || devs[0] == ',') + endlist = true; + if(nextpos < drvlist.size()) { endlist = false; - continue; + while(nextpos < drvlist.size() && drvlist[nextpos] == ',') + ++nextpos; } - endlist = true; + drvlist.remove_prefix(nextpos); + + while(!entry.empty() && std::isspace(entry.front())) + entry.remove_prefix(1); + const bool delitem{!entry.empty() && entry.front() == '-'}; + if(delitem) entry.remove_prefix(1); + + while(!entry.empty() && std::isspace(entry.back())) + entry.remove_suffix(1); + if(entry.empty()) + continue; - size_t len{next ? (static_cast(next-devs)) : strlen(devs)}; - while(len > 0 && isspace(devs[len-1])) --len; #ifdef HAVE_WASAPI /* HACK: For backwards compatibility, convert backend references of * mmdevapi to wasapi. This should eventually be removed. */ - if(len == 8 && strncmp(devs, "mmdevapi", len) == 0) - { - devs = "wasapi"; - len = 6; - } + if(entry == "mmdevapi"sv) + entry = "wasapi"sv; #endif - auto find_backend = [devs,len](const BackendInfo &backend) -> bool - { return len == strlen(backend.name) && strncmp(backend.name, devs, len) == 0; }; - auto this_backend = std::find_if(std::begin(BackendList), BackendListEnd, - find_backend); + auto find_backend = [entry](const BackendInfo &backend) -> bool + { return entry == backend.name; }; + auto this_backend = std::find_if(BackendList.begin(), BackendListEnd, find_backend); if(this_backend == BackendListEnd) continue; @@ -605,11 +642,25 @@ void alc_initconfig(void) BackendListEnd = std::move(this_backend+1, BackendListEnd, this_backend); else backendlist_cur = std::rotate(backendlist_cur, this_backend, this_backend+1); - } while(next++); + } if(endlist) BackendListEnd = backendlist_cur; } + else + { + /* Exclude the null and wave writer backends from being considered by + * default. This ensures there will be no available devices if none of + * the normal backends are usable, rather than pretending there is a + * device but outputs nowhere. + */ + while(BackendListEnd != BackendList.begin()) + { + --BackendListEnd; + if(BackendListEnd->name == "null"sv) + break; + } + } auto init_backend = [](BackendInfo &backend) -> void { @@ -619,78 +670,74 @@ void alc_initconfig(void) BackendFactory &factory = backend.getFactory(); if(!factory.init()) { - WARN("Failed to initialize backend \"%s\"\n", backend.name); + WARN("Failed to initialize backend \"{}\"", backend.name); return; } - TRACE("Initialized backend \"%s\"\n", backend.name); + TRACE("Initialized backend \"{}\"", backend.name); if(!PlaybackFactory && factory.querySupport(BackendType::Playback)) { PlaybackFactory = &factory; - TRACE("Added \"%s\" for playback\n", backend.name); + TRACE("Added \"{}\" for playback", backend.name); } if(!CaptureFactory && factory.querySupport(BackendType::Capture)) { CaptureFactory = &factory; - TRACE("Added \"%s\" for capture\n", backend.name); + TRACE("Added \"{}\" for capture", backend.name); } }; - std::for_each(std::begin(BackendList), BackendListEnd, init_backend); + std::for_each(BackendList.begin(), BackendListEnd, init_backend); LoopbackBackendFactory::getFactory().init(); if(!PlaybackFactory) - WARN("No playback backend available!\n"); + WARN("No playback backend available!"); if(!CaptureFactory) - WARN("No capture backend available!\n"); + WARN("No capture backend available!"); - if(auto exclopt = ConfigValueStr(nullptr, nullptr, "excludefx")) + if(auto exclopt = ConfigValueStr({}, {}, "excludefx"sv)) { - const char *next{exclopt->c_str()}; - do { - const char *str{next}; - next = strchr(str, ','); - - if(!str[0] || next == str) - continue; + std::string_view exclude{*exclopt}; + while(!exclude.empty()) + { + const auto nextpos = exclude.find(','); + const auto entry = exclude.substr(0, nextpos); + exclude.remove_prefix((nextpos < exclude.size()) ? nextpos+1 : exclude.size()); - size_t len{next ? static_cast(next-str) : strlen(str)}; - for(const EffectList &effectitem : gEffectList) - { - if(len == strlen(effectitem.name) && - strncmp(effectitem.name, str, len) == 0) - DisabledEffects[effectitem.type] = true; - } - } while(next++); + std::for_each(gEffectList.cbegin(), gEffectList.cend(), + [entry](const EffectList &effectitem) noexcept + { + if(entry == std::data(effectitem.name)) + DisabledEffects.set(effectitem.type); + }); + } } InitEffect(&ALCcontext::sDefaultEffect); auto defrevopt = al::getenv("ALSOFT_DEFAULT_REVERB"); - if(defrevopt || (defrevopt=ConfigValueStr(nullptr, nullptr, "default-reverb"))) - LoadReverbPreset(defrevopt->c_str(), &ALCcontext::sDefaultEffect); + if(!defrevopt) defrevopt = ConfigValueStr({}, {}, "default-reverb"sv); + if(defrevopt) LoadReverbPreset(*defrevopt, &ALCcontext::sDefaultEffect); -#ifdef ALSOFT_EAX +#if ALSOFT_EAX { - static constexpr char eax_block_name[] = "eax"; - - if(const auto eax_enable_opt = ConfigValueBool(nullptr, eax_block_name, "enable")) + if(const auto eax_enable_opt = ConfigValueBool({}, "eax", "enable")) { eax_g_is_enabled = *eax_enable_opt; if(!eax_g_is_enabled) - TRACE("%s\n", "EAX disabled by a configuration."); + TRACE("EAX disabled by a configuration."); } else eax_g_is_enabled = true; - if((DisabledEffects[EAXREVERB_EFFECT] || DisabledEffects[CHORUS_EFFECT]) + if((DisabledEffects.test(EAXREVERB_EFFECT) || DisabledEffects.test(CHORUS_EFFECT)) && eax_g_is_enabled) { eax_g_is_enabled = false; - TRACE("EAX disabled because %s disabled.\n", - (DisabledEffects[EAXREVERB_EFFECT] && DisabledEffects[CHORUS_EFFECT]) - ? "EAXReverb and Chorus are" : - DisabledEffects[EAXREVERB_EFFECT] ? "EAXReverb is" : - DisabledEffects[CHORUS_EFFECT] ? "Chorus is" : ""); + TRACE("EAX disabled because {} disabled.", + (DisabledEffects.test(EAXREVERB_EFFECT) && DisabledEffects.test(CHORUS_EFFECT)) + ? "EAXReverb and Chorus are"sv : + DisabledEffects.test(EAXREVERB_EFFECT) ? "EAXReverb is"sv : + DisabledEffects.test(CHORUS_EFFECT) ? "Chorus is"sv : ""sv); } } #endif // ALSOFT_EAX @@ -706,63 +753,107 @@ void ProbeAllDevicesList() { InitConfig(); - std::lock_guard _{ListLock}; + std::lock_guard listlock{ListLock}; if(!PlaybackFactory) + { + decltype(alcAllDevicesArray){}.swap(alcAllDevicesArray); decltype(alcAllDevicesList){}.swap(alcAllDevicesList); + } else { - std::string names{PlaybackFactory->probe(BackendType::Playback)}; - if(names.empty()) names += '\0'; - names.swap(alcAllDevicesList); + alcAllDevicesArray = PlaybackFactory->enumerate(BackendType::Playback); + if(const auto prefix = GetDevicePrefix(); !prefix.empty()) + std::for_each(alcAllDevicesArray.begin(), alcAllDevicesArray.end(), + [prefix](std::string &name) { name.insert(0, prefix); }); + + decltype(alcAllDevicesList){}.swap(alcAllDevicesList); + if(alcAllDevicesArray.empty()) + alcAllDevicesList += '\0'; + else for(auto &devname : alcAllDevicesArray) + alcAllDevicesList.append(devname) += '\0'; } } void ProbeCaptureDeviceList() { InitConfig(); - std::lock_guard _{ListLock}; + std::lock_guard listlock{ListLock}; if(!CaptureFactory) + { + decltype(alcCaptureDeviceArray){}.swap(alcCaptureDeviceArray); decltype(alcCaptureDeviceList){}.swap(alcCaptureDeviceList); + } else { - std::string names{CaptureFactory->probe(BackendType::Capture)}; - if(names.empty()) names += '\0'; - names.swap(alcCaptureDeviceList); + alcCaptureDeviceArray = CaptureFactory->enumerate(BackendType::Capture); + if(const auto prefix = GetDevicePrefix(); !prefix.empty()) + std::for_each(alcCaptureDeviceArray.begin(), alcCaptureDeviceArray.end(), + [prefix](std::string &name) { name.insert(0, prefix); }); + + decltype(alcCaptureDeviceList){}.swap(alcCaptureDeviceList); + if(alcCaptureDeviceArray.empty()) + alcCaptureDeviceList += '\0'; + else for(auto &devname : alcCaptureDeviceArray) + alcCaptureDeviceList.append(devname) += '\0'; } } +al::span SpanFromAttributeList(const ALCint *attribs) noexcept +{ + al::span attrSpan; + if(attribs) + { + const ALCint *attrEnd{attribs}; + while(*attrEnd != 0) + attrEnd += 2; /* NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) */ + attrSpan = {attribs, attrEnd}; + } + return attrSpan; +} + struct DevFmtPair { DevFmtChannels chans; DevFmtType type; }; std::optional DecomposeDevFormat(ALenum format) { - static const struct { + struct FormatType { ALenum format; DevFmtChannels channels; DevFmtType type; - } list[] = { - { AL_FORMAT_MONO8, DevFmtMono, DevFmtUByte }, - { AL_FORMAT_MONO16, DevFmtMono, DevFmtShort }, - { AL_FORMAT_MONO_FLOAT32, DevFmtMono, DevFmtFloat }, - - { AL_FORMAT_STEREO8, DevFmtStereo, DevFmtUByte }, - { AL_FORMAT_STEREO16, DevFmtStereo, DevFmtShort }, - { AL_FORMAT_STEREO_FLOAT32, DevFmtStereo, DevFmtFloat }, - - { AL_FORMAT_QUAD8, DevFmtQuad, DevFmtUByte }, - { AL_FORMAT_QUAD16, DevFmtQuad, DevFmtShort }, - { AL_FORMAT_QUAD32, DevFmtQuad, DevFmtFloat }, - - { AL_FORMAT_51CHN8, DevFmtX51, DevFmtUByte }, - { AL_FORMAT_51CHN16, DevFmtX51, DevFmtShort }, - { AL_FORMAT_51CHN32, DevFmtX51, DevFmtFloat }, - - { AL_FORMAT_61CHN8, DevFmtX61, DevFmtUByte }, - { AL_FORMAT_61CHN16, DevFmtX61, DevFmtShort }, - { AL_FORMAT_61CHN32, DevFmtX61, DevFmtFloat }, - - { AL_FORMAT_71CHN8, DevFmtX71, DevFmtUByte }, - { AL_FORMAT_71CHN16, DevFmtX71, DevFmtShort }, - { AL_FORMAT_71CHN32, DevFmtX71, DevFmtFloat }, + }; + static constexpr std::array list{ + FormatType{AL_FORMAT_MONO8, DevFmtMono, DevFmtUByte}, + FormatType{AL_FORMAT_MONO16, DevFmtMono, DevFmtShort}, + FormatType{AL_FORMAT_MONO_I32, DevFmtMono, DevFmtInt}, + FormatType{AL_FORMAT_MONO_FLOAT32, DevFmtMono, DevFmtFloat}, + + FormatType{AL_FORMAT_STEREO8, DevFmtStereo, DevFmtUByte}, + FormatType{AL_FORMAT_STEREO16, DevFmtStereo, DevFmtShort}, + FormatType{AL_FORMAT_STEREO_I32, DevFmtStereo, DevFmtInt}, + FormatType{AL_FORMAT_STEREO_FLOAT32, DevFmtStereo, DevFmtFloat}, + + FormatType{AL_FORMAT_QUAD8, DevFmtQuad, DevFmtUByte}, + FormatType{AL_FORMAT_QUAD16, DevFmtQuad, DevFmtShort}, + FormatType{AL_FORMAT_QUAD32, DevFmtQuad, DevFmtFloat}, + FormatType{AL_FORMAT_QUAD_I32, DevFmtQuad, DevFmtInt}, + FormatType{AL_FORMAT_QUAD_FLOAT32, DevFmtQuad, DevFmtFloat}, + + FormatType{AL_FORMAT_51CHN8, DevFmtX51, DevFmtUByte}, + FormatType{AL_FORMAT_51CHN16, DevFmtX51, DevFmtShort}, + FormatType{AL_FORMAT_51CHN32, DevFmtX51, DevFmtFloat}, + FormatType{AL_FORMAT_51CHN_I32, DevFmtX51, DevFmtInt}, + FormatType{AL_FORMAT_51CHN_FLOAT32, DevFmtX51, DevFmtFloat}, + + FormatType{AL_FORMAT_61CHN8, DevFmtX61, DevFmtUByte}, + FormatType{AL_FORMAT_61CHN16, DevFmtX61, DevFmtShort}, + FormatType{AL_FORMAT_61CHN32, DevFmtX61, DevFmtFloat}, + FormatType{AL_FORMAT_61CHN_I32, DevFmtX61, DevFmtInt}, + FormatType{AL_FORMAT_61CHN_FLOAT32, DevFmtX61, DevFmtFloat}, + + FormatType{AL_FORMAT_71CHN8, DevFmtX71, DevFmtUByte}, + FormatType{AL_FORMAT_71CHN16, DevFmtX71, DevFmtShort}, + FormatType{AL_FORMAT_71CHN32, DevFmtX71, DevFmtFloat}, + FormatType{AL_FORMAT_71CHN_I32, DevFmtX71, DevFmtInt}, + FormatType{AL_FORMAT_71CHN_FLOAT32, DevFmtX71, DevFmtFloat}, }; for(const auto &item : list) @@ -786,7 +877,7 @@ std::optional DevFmtTypeFromEnum(ALCenum type) case ALC_UNSIGNED_INT_SOFT: return DevFmtUInt; case ALC_FLOAT_SOFT: return DevFmtFloat; } - WARN("Unsupported format type: 0x%04x\n", type); + WARN("Unsupported format type: {:#04x}", as_unsigned(type)); return std::nullopt; } ALCenum EnumFromDevFmt(DevFmtType type) @@ -801,7 +892,7 @@ ALCenum EnumFromDevFmt(DevFmtType type) case DevFmtUInt: return ALC_UNSIGNED_INT_SOFT; case DevFmtFloat: return ALC_FLOAT_SOFT; } - throw std::runtime_error{"Invalid DevFmtType: "+std::to_string(int(type))}; + throw std::runtime_error{fmt::format("Invalid DevFmtType: {}", int{al::to_underlying(type)})}; } std::optional DevFmtChannelsFromEnum(ALCenum channels) @@ -816,7 +907,7 @@ std::optional DevFmtChannelsFromEnum(ALCenum channels) case ALC_7POINT1_SOFT: return DevFmtX71; case ALC_BFORMAT3D_SOFT: return DevFmtAmbi3D; } - WARN("Unsupported format channels: 0x%04x\n", channels); + WARN("Unsupported format channels: {:#04x}", as_unsigned(channels)); return std::nullopt; } ALCenum EnumFromDevFmt(DevFmtChannels channels) @@ -832,9 +923,11 @@ ALCenum EnumFromDevFmt(DevFmtChannels channels) case DevFmtAmbi3D: return ALC_BFORMAT3D_SOFT; /* FIXME: Shouldn't happen. */ case DevFmtX714: + case DevFmtX7144: case DevFmtX3D71: break; } - throw std::runtime_error{"Invalid DevFmtChannels: "+std::to_string(int(channels))}; + throw std::runtime_error{fmt::format("Invalid DevFmtChannels: {}", + int{al::to_underlying(channels)})}; } std::optional DevAmbiLayoutFromEnum(ALCenum layout) @@ -844,7 +937,7 @@ std::optional DevAmbiLayoutFromEnum(ALCenum layout) case ALC_FUMA_SOFT: return DevAmbiLayout::FuMa; case ALC_ACN_SOFT: return DevAmbiLayout::ACN; } - WARN("Unsupported ambisonic layout: 0x%04x\n", layout); + WARN("Unsupported ambisonic layout: {:#04x}", as_unsigned(layout)); return std::nullopt; } ALCenum EnumFromDevAmbi(DevAmbiLayout layout) @@ -854,7 +947,8 @@ ALCenum EnumFromDevAmbi(DevAmbiLayout layout) case DevAmbiLayout::FuMa: return ALC_FUMA_SOFT; case DevAmbiLayout::ACN: return ALC_ACN_SOFT; } - throw std::runtime_error{"Invalid DevAmbiLayout: "+std::to_string(int(layout))}; + throw std::runtime_error{fmt::format("Invalid DevAmbiLayout: {}", + int{al::to_underlying(layout)})}; } std::optional DevAmbiScalingFromEnum(ALCenum scaling) @@ -865,7 +959,7 @@ std::optional DevAmbiScalingFromEnum(ALCenum scaling) case ALC_SN3D_SOFT: return DevAmbiScaling::SN3D; case ALC_N3D_SOFT: return DevAmbiScaling::N3D; } - WARN("Unsupported ambisonic scaling: 0x%04x\n", scaling); + WARN("Unsupported ambisonic scaling: {:#04x}", as_unsigned(scaling)); return std::nullopt; } ALCenum EnumFromDevAmbi(DevAmbiScaling scaling) @@ -876,86 +970,70 @@ ALCenum EnumFromDevAmbi(DevAmbiScaling scaling) case DevAmbiScaling::SN3D: return ALC_SN3D_SOFT; case DevAmbiScaling::N3D: return ALC_N3D_SOFT; } - throw std::runtime_error{"Invalid DevAmbiScaling: "+std::to_string(int(scaling))}; + throw std::runtime_error{fmt::format("Invalid DevAmbiScaling: {}", + int{al::to_underlying(scaling)})}; } -/* Downmixing channel arrays, to map the given format's missing channels to - * existing ones. Based on Wine's DSound downmix values, which are based on - * PulseAudio's. +/* Downmixing channel arrays, to map a device format's missing channels to + * existing ones. Based on what PipeWire does, though simplified. */ -constexpr std::array FrontStereoSplit{{ - {FrontLeft, 0.5f}, {FrontRight, 0.5f} -}}; -constexpr std::array FrontLeft9{{ - {FrontLeft, 1.0f/9.0f} -}}; -constexpr std::array FrontRight9{{ - {FrontRight, 1.0f/9.0f} -}}; -constexpr std::array BackMonoToFrontSplit{{ - {FrontLeft, 0.5f/9.0f}, {FrontRight, 0.5f/9.0f} -}}; -constexpr std::array LeftStereoSplit{{ - {FrontLeft, 0.5f}, {BackLeft, 0.5f} -}}; -constexpr std::array RightStereoSplit{{ - {FrontRight, 0.5f}, {BackRight, 0.5f} -}}; -constexpr std::array BackStereoSplit{{ - {BackLeft, 0.5f}, {BackRight, 0.5f} -}}; -constexpr std::array SideStereoSplit{{ - {SideLeft, 0.5f}, {SideRight, 0.5f} -}}; -constexpr std::array ToSideLeft{{ - {SideLeft, 1.0f} -}}; -constexpr std::array ToSideRight{{ - {SideRight, 1.0f} -}}; -constexpr std::array BackLeftSplit{{ - {SideLeft, 0.5f}, {BackCenter, 0.5f} -}}; -constexpr std::array BackRightSplit{{ - {SideRight, 0.5f}, {BackCenter, 0.5f} -}}; - -const std::array StereoDownmix{{ - { FrontCenter, FrontStereoSplit }, - { SideLeft, FrontLeft9 }, - { SideRight, FrontRight9 }, - { BackLeft, FrontLeft9 }, - { BackRight, FrontRight9 }, - { BackCenter, BackMonoToFrontSplit }, -}}; -const std::array QuadDownmix{{ - { FrontCenter, FrontStereoSplit }, - { SideLeft, LeftStereoSplit }, - { SideRight, RightStereoSplit }, - { BackCenter, BackStereoSplit }, -}}; -const std::array X51Downmix{{ - { BackLeft, ToSideLeft }, - { BackRight, ToSideRight }, - { BackCenter, SideStereoSplit }, -}}; -const std::array X61Downmix{{ - { BackLeft, BackLeftSplit }, - { BackRight, BackRightSplit }, -}}; -const std::array X71Downmix{{ - { BackCenter, BackStereoSplit }, -}}; - - -std::unique_ptr CreateDeviceLimiter(const ALCdevice *device, const float threshold) +constexpr float inv_sqrt2f{static_cast(1.0 / al::numbers::sqrt2)}; +constexpr std::array FrontStereo3dB{ + InputRemixMap::TargetMix{FrontLeft, inv_sqrt2f}, + InputRemixMap::TargetMix{FrontRight, inv_sqrt2f} +}; +constexpr std::array FrontStereo6dB{ + InputRemixMap::TargetMix{FrontLeft, 0.5f}, + InputRemixMap::TargetMix{FrontRight, 0.5f} +}; +constexpr std::array SideStereo3dB{ + InputRemixMap::TargetMix{SideLeft, inv_sqrt2f}, + InputRemixMap::TargetMix{SideRight, inv_sqrt2f} +}; +constexpr std::array BackStereo3dB{ + InputRemixMap::TargetMix{BackLeft, inv_sqrt2f}, + InputRemixMap::TargetMix{BackRight, inv_sqrt2f} +}; +constexpr std::array FrontLeft3dB{InputRemixMap::TargetMix{FrontLeft, inv_sqrt2f}}; +constexpr std::array FrontRight3dB{InputRemixMap::TargetMix{FrontRight, inv_sqrt2f}}; +constexpr std::array SideLeft0dB{InputRemixMap::TargetMix{SideLeft, 1.0f}}; +constexpr std::array SideRight0dB{InputRemixMap::TargetMix{SideRight, 1.0f}}; +constexpr std::array BackLeft0dB{InputRemixMap::TargetMix{BackLeft, 1.0f}}; +constexpr std::array BackRight0dB{InputRemixMap::TargetMix{BackRight, 1.0f}}; +constexpr std::array BackCenter3dB{InputRemixMap::TargetMix{BackCenter, inv_sqrt2f}}; + +constexpr std::array StereoDownmix{ + InputRemixMap{FrontCenter, FrontStereo3dB}, + InputRemixMap{SideLeft, FrontLeft3dB}, + InputRemixMap{SideRight, FrontRight3dB}, + InputRemixMap{BackLeft, FrontLeft3dB}, + InputRemixMap{BackRight, FrontRight3dB}, + InputRemixMap{BackCenter, FrontStereo6dB}, +}; +constexpr std::array QuadDownmix{ + InputRemixMap{FrontCenter, FrontStereo3dB}, + InputRemixMap{SideLeft, BackLeft0dB}, + InputRemixMap{SideRight, BackRight0dB}, + InputRemixMap{BackCenter, BackStereo3dB}, +}; +constexpr std::array X51Downmix{ + InputRemixMap{BackLeft, SideLeft0dB}, + InputRemixMap{BackRight, SideRight0dB}, + InputRemixMap{BackCenter, SideStereo3dB}, +}; +constexpr std::array X61Downmix{ + InputRemixMap{BackLeft, BackCenter3dB}, + InputRemixMap{BackRight, BackCenter3dB}, +}; +constexpr std::array X71Downmix{ + InputRemixMap{BackCenter, BackStereo3dB}, +}; + + +auto CreateDeviceLimiter(const al::Device *device, const float threshold) + -> std::unique_ptr { - static constexpr bool AutoKnee{true}; - static constexpr bool AutoAttack{true}; - static constexpr bool AutoRelease{true}; - static constexpr bool AutoPostGain{true}; - static constexpr bool AutoDeclip{true}; static constexpr float LookAheadTime{0.001f}; static constexpr float HoldTime{0.002f}; static constexpr float PreGainDb{0.0f}; @@ -965,9 +1043,12 @@ std::unique_ptr CreateDeviceLimiter(const ALCdevice *device, const f static constexpr float AttackTime{0.02f}; static constexpr float ReleaseTime{0.2f}; - return Compressor::Create(device->RealOut.Buffer.size(), static_cast(device->Frequency), - AutoKnee, AutoAttack, AutoRelease, AutoPostGain, AutoDeclip, LookAheadTime, HoldTime, - PreGainDb, PostGainDb, threshold, Ratio, KneeDb, AttackTime, ReleaseTime); + const auto flags = Compressor::FlagBits{}.set(Compressor::AutoKnee).set(Compressor::AutoAttack) + .set(Compressor::AutoRelease).set(Compressor::AutoPostGain).set(Compressor::AutoDeclip); + + return Compressor::Create(device->RealOut.Buffer.size(), + static_cast(device->mSampleRate), flags, LookAheadTime, HoldTime, PreGainDb, + PostGainDb, threshold, Ratio, KneeDb, AttackTime, ReleaseTime); } /** @@ -976,23 +1057,35 @@ std::unique_ptr CreateDeviceLimiter(const ALCdevice *device, const f * to jump forward or back. Must not be called while the device is running/ * mixing. */ -inline void UpdateClockBase(ALCdevice *device) +inline void UpdateClockBase(al::Device *device) { - IncrementRef(device->MixCount); - device->ClockBase += nanoseconds{seconds{device->SamplesDone}} / device->Frequency; - device->SamplesDone = 0; - IncrementRef(device->MixCount); + using std::chrono::duration_cast; + + const auto mixLock = device->getWriteMixLock(); + + auto clockBaseSec = device->mClockBaseSec.load(std::memory_order_relaxed); + auto clockBaseNSec = nanoseconds{device->mClockBaseNSec.load(std::memory_order_relaxed)}; + clockBaseNSec += nanoseconds{seconds{device->mSamplesDone.load(std::memory_order_relaxed)}} + / device->mSampleRate; + + clockBaseSec += duration_cast(clockBaseNSec); + clockBaseNSec %= seconds{1}; + + device->mClockBaseSec.store(clockBaseSec, std::memory_order_relaxed); + device->mClockBaseNSec.store(duration_cast(clockBaseNSec), + std::memory_order_relaxed); + device->mSamplesDone.store(0, std::memory_order_relaxed); } /** * Updates device parameters according to the attribute list (caller is * responsible for holding the list lock). */ -ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) +auto UpdateDeviceParams(al::Device *device, const al::span attrList) -> ALCenum { - if((!attrList || !attrList[0]) && device->Type == DeviceType::Loopback) + if(attrList.empty() && device->Type == DeviceType::Loopback) { - WARN("Missing attributes for loopback device\n"); + WARN("Missing attributes for loopback device"); return ALC_INVALID_VALUE; } @@ -1006,8 +1099,8 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) std::optional opttype; std::optional optlayout; std::optional optscale; - uint period_size{DEFAULT_UPDATE_SIZE}; - uint buffer_size{DEFAULT_UPDATE_SIZE * DEFAULT_NUM_UPDATES}; + uint period_size{DefaultUpdateSize}; + uint buffer_size{DefaultUpdateSize * DefaultNumUpdates}; int hrtf_id{-1}; uint aorder{0u}; @@ -1015,154 +1108,153 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) { /* Get default settings from the user configuration */ - if(auto freqopt = device->configValue(nullptr, "frequency")) + if(auto freqopt = device->configValue({}, "frequency")) { - optsrate = clampu(*freqopt, MIN_OUTPUT_RATE, MAX_OUTPUT_RATE); + optsrate = std::clamp(*freqopt, MinOutputRate, MaxOutputRate); - const double scale{static_cast(*optsrate) / DEFAULT_OUTPUT_RATE}; - period_size = static_cast(period_size*scale + 0.5); + const double scale{static_cast(*optsrate) / double{DefaultOutputRate}}; + period_size = static_cast(std::lround(period_size * scale)); } - if(auto persizeopt = device->configValue(nullptr, "period_size")) - period_size = clampu(*persizeopt, 64, 8192); - if(auto numperopt = device->configValue(nullptr, "periods")) - buffer_size = clampu(*numperopt, 2, 16) * period_size; + if(auto persizeopt = device->configValue({}, "period_size")) + period_size = std::clamp(*persizeopt, 64u, 8192u); + if(auto numperopt = device->configValue({}, "periods")) + buffer_size = std::clamp(*numperopt, 2u, 16u) * period_size; else - buffer_size = period_size * DEFAULT_NUM_UPDATES; + buffer_size = period_size * uint{DefaultNumUpdates}; - if(auto typeopt = device->configValue(nullptr, "sample-type")) + if(auto typeopt = device->configValue({}, "sample-type")) { - static constexpr struct TypeMap { - const char name[8]; + struct TypeMap { + std::string_view name; DevFmtType type; - } typelist[] = { - { "int8", DevFmtByte }, - { "uint8", DevFmtUByte }, - { "int16", DevFmtShort }, - { "uint16", DevFmtUShort }, - { "int32", DevFmtInt }, - { "uint32", DevFmtUInt }, - { "float32", DevFmtFloat }, + }; + constexpr std::array typelist{ + TypeMap{"int8"sv, DevFmtByte }, + TypeMap{"uint8"sv, DevFmtUByte }, + TypeMap{"int16"sv, DevFmtShort }, + TypeMap{"uint16"sv, DevFmtUShort}, + TypeMap{"int32"sv, DevFmtInt }, + TypeMap{"uint32"sv, DevFmtUInt }, + TypeMap{"float32"sv, DevFmtFloat }, }; - const ALCchar *fmt{typeopt->c_str()}; - auto iter = std::find_if(std::begin(typelist), std::end(typelist), - [fmt](const TypeMap &entry) -> bool - { return al::strcasecmp(entry.name, fmt) == 0; }); - if(iter == std::end(typelist)) - ERR("Unsupported sample-type: %s\n", fmt); + auto iter = std::find_if(typelist.begin(), typelist.end(), + [svfmt=std::string_view{*typeopt}](const TypeMap &entry) -> bool + { return al::case_compare(entry.name, svfmt) == 0; }); + if(iter == typelist.end()) + ERR("Unsupported sample-type: {}", *typeopt); else opttype = iter->type; } - if(auto chanopt = device->configValue(nullptr, "channels")) + if(auto chanopt = device->configValue({}, "channels")) { - static constexpr struct ChannelMap { - const char name[16]; + struct ChannelMap { + std::string_view name; DevFmtChannels chans; uint8_t order; - } chanlist[] = { - { "mono", DevFmtMono, 0 }, - { "stereo", DevFmtStereo, 0 }, - { "quad", DevFmtQuad, 0 }, - { "surround51", DevFmtX51, 0 }, - { "surround61", DevFmtX61, 0 }, - { "surround71", DevFmtX71, 0 }, - { "surround714", DevFmtX714, 0 }, - { "surround3d71", DevFmtX3D71, 0 }, - { "surround51rear", DevFmtX51, 0 }, - { "ambi1", DevFmtAmbi3D, 1 }, - { "ambi2", DevFmtAmbi3D, 2 }, - { "ambi3", DevFmtAmbi3D, 3 }, + }; + constexpr std::array chanlist{ + ChannelMap{"mono"sv, DevFmtMono, 0}, + ChannelMap{"stereo"sv, DevFmtStereo, 0}, + ChannelMap{"quad"sv, DevFmtQuad, 0}, + ChannelMap{"surround51"sv, DevFmtX51, 0}, + ChannelMap{"surround61"sv, DevFmtX61, 0}, + ChannelMap{"surround71"sv, DevFmtX71, 0}, + ChannelMap{"surround714"sv, DevFmtX714, 0}, + ChannelMap{"surround7144"sv, DevFmtX7144, 0}, + ChannelMap{"surround3d71"sv, DevFmtX3D71, 0}, + ChannelMap{"surround51rear"sv, DevFmtX51, 0}, + ChannelMap{"ambi1"sv, DevFmtAmbi3D, 1}, + ChannelMap{"ambi2"sv, DevFmtAmbi3D, 2}, + ChannelMap{"ambi3"sv, DevFmtAmbi3D, 3}, }; - const ALCchar *fmt{chanopt->c_str()}; - auto iter = std::find_if(std::begin(chanlist), std::end(chanlist), - [fmt](const ChannelMap &entry) -> bool - { return al::strcasecmp(entry.name, fmt) == 0; }); - if(iter == std::end(chanlist)) - ERR("Unsupported channels: %s\n", fmt); + auto iter = std::find_if(chanlist.begin(), chanlist.end(), + [svfmt=std::string_view{*chanopt}](const ChannelMap &entry) -> bool + { return al::case_compare(entry.name, svfmt) == 0; }); + if(iter == chanlist.end()) + ERR("Unsupported channels: {}", *chanopt); else { optchans = iter->chans; aorder = iter->order; } } - if(auto ambiopt = device->configValue(nullptr, "ambi-format")) + if(auto ambiopt = device->configValue({}, "ambi-format"sv)) { - const ALCchar *fmt{ambiopt->c_str()}; - if(al::strcasecmp(fmt, "fuma") == 0) + if(al::case_compare(*ambiopt, "fuma"sv) == 0) { optlayout = DevAmbiLayout::FuMa; optscale = DevAmbiScaling::FuMa; } - else if(al::strcasecmp(fmt, "acn+fuma") == 0) + else if(al::case_compare(*ambiopt, "acn+fuma"sv) == 0) { optlayout = DevAmbiLayout::ACN; optscale = DevAmbiScaling::FuMa; } - else if(al::strcasecmp(fmt, "ambix") == 0 || al::strcasecmp(fmt, "acn+sn3d") == 0) + else if(al::case_compare(*ambiopt, "ambix"sv) == 0 + || al::case_compare(*ambiopt, "acn+sn3d"sv) == 0) { optlayout = DevAmbiLayout::ACN; optscale = DevAmbiScaling::SN3D; } - else if(al::strcasecmp(fmt, "acn+n3d") == 0) + else if(al::case_compare(*ambiopt, "acn+n3d"sv) == 0) { optlayout = DevAmbiLayout::ACN; optscale = DevAmbiScaling::N3D; } else - ERR("Unsupported ambi-format: %s\n", fmt); + ERR("Unsupported ambi-format: {}", *ambiopt); } - if(auto hrtfopt = device->configValue(nullptr, "hrtf")) + if(auto hrtfopt = device->configValue({}, "hrtf"sv)) { - WARN("general/hrtf is deprecated, please use stereo-encoding instead\n"); + WARN("general/hrtf is deprecated, please use stereo-encoding instead"); - const char *hrtf{hrtfopt->c_str()}; - if(al::strcasecmp(hrtf, "true") == 0) + if(al::case_compare(*hrtfopt, "true"sv) == 0) stereomode = StereoEncoding::Hrtf; - else if(al::strcasecmp(hrtf, "false") == 0) + else if(al::case_compare(*hrtfopt, "false"sv) == 0) { if(!stereomode || *stereomode == StereoEncoding::Hrtf) stereomode = StereoEncoding::Default; } - else if(al::strcasecmp(hrtf, "auto") != 0) - ERR("Unexpected hrtf value: %s\n", hrtf); + else if(al::case_compare(*hrtfopt, "auto"sv) != 0) + ERR("Unexpected hrtf value: {}", *hrtfopt); } } - if(auto encopt = device->configValue(nullptr, "stereo-encoding")) + if(auto encopt = device->configValue({}, "stereo-encoding"sv)) { - const char *mode{encopt->c_str()}; - if(al::strcasecmp(mode, "basic") == 0 || al::strcasecmp(mode, "panpot") == 0) + if(al::case_compare(*encopt, "basic"sv) == 0 || al::case_compare(*encopt, "panpot"sv) == 0) stereomode = StereoEncoding::Basic; - else if(al::strcasecmp(mode, "uhj") == 0) + else if(al::case_compare(*encopt, "uhj") == 0) stereomode = StereoEncoding::Uhj; - else if(al::strcasecmp(mode, "hrtf") == 0) + else if(al::case_compare(*encopt, "hrtf") == 0) stereomode = StereoEncoding::Hrtf; else - ERR("Unexpected stereo-encoding: %s\n", mode); + ERR("Unexpected stereo-encoding: {}", *encopt); } // Check for app-specified attributes - if(attrList && attrList[0]) + if(!attrList.empty()) { ALenum outmode{ALC_ANY_SOFT}; std::optional opthrtf; int freqAttr{}; -#define ATTRIBUTE(a) a: TRACE("%s = %d\n", #a, attrList[attrIdx + 1]); - size_t attrIdx{0}; - while(attrList[attrIdx]) +#define ATTRIBUTE(a) a: TRACE("{} = {}", #a, attrList[attrIdx + 1]); +#define ATTRIBUTE_HEX(a) a: TRACE("{} = {:#x}", #a, as_unsigned(attrList[attrIdx + 1])); + for(size_t attrIdx{0};attrIdx < attrList.size();attrIdx+=2) { switch(attrList[attrIdx]) { - case ATTRIBUTE(ALC_FORMAT_CHANNELS_SOFT) + case ATTRIBUTE_HEX(ALC_FORMAT_CHANNELS_SOFT) if(device->Type == DeviceType::Loopback) optchans = DevFmtChannelsFromEnum(attrList[attrIdx + 1]); break; - case ATTRIBUTE(ALC_FORMAT_TYPE_SOFT) + case ATTRIBUTE_HEX(ALC_FORMAT_TYPE_SOFT) if(device->Type == DeviceType::Loopback) opttype = DevFmtTypeFromEnum(attrList[attrIdx + 1]); break; @@ -1171,12 +1263,12 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) freqAttr = attrList[attrIdx + 1]; break; - case ATTRIBUTE(ALC_AMBISONIC_LAYOUT_SOFT) + case ATTRIBUTE_HEX(ALC_AMBISONIC_LAYOUT_SOFT) if(device->Type == DeviceType::Loopback) optlayout = DevAmbiLayoutFromEnum(attrList[attrIdx + 1]); break; - case ATTRIBUTE(ALC_AMBISONIC_SCALING_SOFT) + case ATTRIBUTE_HEX(ALC_AMBISONIC_SCALING_SOFT) if(device->Type == DeviceType::Loopback) optscale = DevAmbiScalingFromEnum(attrList[attrIdx + 1]); break; @@ -1198,8 +1290,8 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) case ATTRIBUTE(ALC_MAX_AUXILIARY_SENDS) numSends = static_cast(attrList[attrIdx + 1]); - if(numSends > INT_MAX) numSends = 0; - else numSends = minu(numSends, MAX_SENDS); + if(numSends > uint{std::numeric_limits::max()}) numSends = 0; + else numSends = std::min(numSends, uint{MaxSendCount}); break; case ATTRIBUTE(ALC_HRTF_SOFT) @@ -1224,25 +1316,32 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) optlimit = std::nullopt; break; - case ATTRIBUTE(ALC_OUTPUT_MODE_SOFT) + case ATTRIBUTE_HEX(ALC_OUTPUT_MODE_SOFT) outmode = attrList[attrIdx + 1]; break; + case ATTRIBUTE_HEX(ALC_CONTEXT_FLAGS_EXT) + /* Handled in alcCreateContext */ + break; + + case ATTRIBUTE(ALC_SYNC) + /* Ignored attribute */ + break; + default: - TRACE("0x%04X = %d (0x%x)\n", attrList[attrIdx], - attrList[attrIdx + 1], attrList[attrIdx + 1]); + TRACE("{:#04x} = {} ({:#x})", as_unsigned(attrList[attrIdx]), + attrList[attrIdx + 1], as_unsigned(attrList[attrIdx + 1])); break; } - - attrIdx += 2; } +#undef ATTRIBUTE_HEX #undef ATTRIBUTE if(device->Type == DeviceType::Loopback) { if(!optchans || !opttype) return ALC_INVALID_VALUE; - if(freqAttr < MIN_OUTPUT_RATE || freqAttr > MAX_OUTPUT_RATE) + if(freqAttr < int{MinOutputRate} || freqAttr > int{MaxOutputRate}) return ALC_INVALID_VALUE; if(*optchans == DevFmtAmbi3D) { @@ -1292,7 +1391,7 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) if(outmode != ALC_ANY_SOFT) { - using OutputMode = ALCdevice::OutputMode; + using OutputMode = al::Device::OutputMode; switch(OutputMode(outmode)) { case OutputMode::Any: break; @@ -1319,12 +1418,12 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) if(freqAttr) { - uint oldrate = optsrate.value_or(DEFAULT_OUTPUT_RATE); - freqAttr = clampi(freqAttr, MIN_OUTPUT_RATE, MAX_OUTPUT_RATE); + uint oldrate = optsrate.value_or(DefaultOutputRate); + freqAttr = std::clamp(freqAttr, MinOutputRate, MaxOutputRate); const double scale{static_cast(freqAttr) / oldrate}; - period_size = static_cast(period_size*scale + 0.5); - buffer_size = static_cast(buffer_size*scale + 0.5); + period_size = static_cast(std::lround(period_size * scale)); + buffer_size = static_cast(std::lround(buffer_size * scale)); optsrate = static_cast(freqAttr); } } @@ -1332,16 +1431,19 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) /* If a context is already running on the device, stop playback so the * device attributes can be updated. */ - if(device->Flags.test(DeviceRunning)) + if(device->mDeviceState == DeviceState::Playing) + { device->Backend->stop(); - device->Flags.reset(DeviceRunning); + device->mDeviceState = DeviceState::Unprepared; + } UpdateClockBase(device); } - if(device->Flags.test(DeviceRunning)) + if(device->mDeviceState == DeviceState::Playing) return ALC_NO_ERROR; + device->mDeviceState = DeviceState::Unprepared; device->AvgSpeakerDist = 0.0f; device->mNFCtrlFilter = NfcFilter{}; device->mUhjEncoder = nullptr; @@ -1377,7 +1479,7 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) if(device->Type == DeviceType::Loopback) { - device->Frequency = *optsrate; + device->mSampleRate = *optsrate; device->FmtChans = *optchans; device->FmtType = *opttype; if(device->FmtChans == DevFmtAmbi3D) @@ -1393,40 +1495,36 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) device->FmtType = opttype.value_or(DevFmtTypeDefault); device->FmtChans = optchans.value_or(DevFmtChannelsDefault); device->mAmbiOrder = 0; - device->BufferSize = buffer_size; - device->UpdateSize = period_size; - device->Frequency = optsrate.value_or(DEFAULT_OUTPUT_RATE); + device->mBufferSize = buffer_size; + device->mUpdateSize = period_size; + device->mSampleRate = optsrate.value_or(DefaultOutputRate); device->Flags.set(FrequencyRequest, optsrate.has_value()) .set(ChannelsRequest, optchans.has_value()) .set(SampleTypeRequest, opttype.has_value()); if(device->FmtChans == DevFmtAmbi3D) { - device->mAmbiOrder = clampu(aorder, 1, MaxAmbiOrder); + device->mAmbiOrder = std::clamp(aorder, 1u, uint{MaxAmbiOrder}); device->mAmbiLayout = optlayout.value_or(DevAmbiLayout::Default); device->mAmbiScale = optscale.value_or(DevAmbiScaling::Default); if(device->mAmbiOrder > 3 && (device->mAmbiLayout == DevAmbiLayout::FuMa || device->mAmbiScale == DevAmbiScaling::FuMa)) { - ERR("FuMa is incompatible with %d%s order ambisonics (up to 3rd order only)\n", - device->mAmbiOrder, - (((device->mAmbiOrder%100)/10) == 1) ? "th" : - ((device->mAmbiOrder%10) == 1) ? "st" : - ((device->mAmbiOrder%10) == 2) ? "nd" : - ((device->mAmbiOrder%10) == 3) ? "rd" : "th"); + ERR("FuMa is incompatible with {}{} order ambisonics (up to 3rd order only)", + device->mAmbiOrder, GetCounterSuffix(device->mAmbiOrder)); device->mAmbiOrder = 3; } } } - TRACE("Pre-reset: %s%s, %s%s, %s%uhz, %u / %u buffer\n", + TRACE("Pre-reset: {}{}, {}{}, {}{}hz, {} / {} buffer", device->Flags.test(ChannelsRequest)?"*":"", DevFmtChannelsString(device->FmtChans), device->Flags.test(SampleTypeRequest)?"*":"", DevFmtTypeString(device->FmtType), - device->Flags.test(FrequencyRequest)?"*":"", device->Frequency, - device->UpdateSize, device->BufferSize); + device->Flags.test(FrequencyRequest)?"*":"", device->mSampleRate, + device->mUpdateSize, device->mBufferSize); - const uint oldFreq{device->Frequency}; + const uint oldFreq{device->mSampleRate}; const DevFmtChannels oldChans{device->FmtChans}; const DevFmtType oldType{device->FmtType}; try { @@ -1435,44 +1533,43 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) throw al::backend_exception{al::backend_error::DeviceError, "Device reset failure"}; } catch(std::exception &e) { - ERR("Device error: %s\n", e.what()); - device->handleDisconnect("%s", e.what()); + ERR("Device error: {}", e.what()); + device->handleDisconnect("{}", e.what()); return ALC_INVALID_DEVICE; } if(device->FmtChans != oldChans && device->Flags.test(ChannelsRequest)) { - ERR("Failed to set %s, got %s instead\n", DevFmtChannelsString(oldChans), + ERR("Failed to set {}, got {} instead", DevFmtChannelsString(oldChans), DevFmtChannelsString(device->FmtChans)); device->Flags.reset(ChannelsRequest); } if(device->FmtType != oldType && device->Flags.test(SampleTypeRequest)) { - ERR("Failed to set %s, got %s instead\n", DevFmtTypeString(oldType), + ERR("Failed to set {}, got {} instead", DevFmtTypeString(oldType), DevFmtTypeString(device->FmtType)); device->Flags.reset(SampleTypeRequest); } - if(device->Frequency != oldFreq && device->Flags.test(FrequencyRequest)) + if(device->mSampleRate != oldFreq && device->Flags.test(FrequencyRequest)) { - WARN("Failed to set %uhz, got %uhz instead\n", oldFreq, device->Frequency); + WARN("Failed to set {}hz, got {}hz instead", oldFreq, device->mSampleRate); device->Flags.reset(FrequencyRequest); } - TRACE("Post-reset: %s, %s, %uhz, %u / %u buffer\n", + TRACE("Post-reset: {}, {}, {}hz, {} / {} buffer", DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType), - device->Frequency, device->UpdateSize, device->BufferSize); + device->mSampleRate, device->mUpdateSize, device->mBufferSize); if(device->Type != DeviceType::Loopback) { - if(auto modeopt = device->configValue(nullptr, "stereo-mode")) + if(auto modeopt = device->configValue({}, "stereo-mode")) { - const char *mode{modeopt->c_str()}; - if(al::strcasecmp(mode, "headphones") == 0) + if(al::case_compare(*modeopt, "headphones"sv) == 0) device->Flags.set(DirectEar); - else if(al::strcasecmp(mode, "speakers") == 0) + else if(al::case_compare(*modeopt, "speakers"sv) == 0) device->Flags.reset(DirectEar); - else if(al::strcasecmp(mode, "auto") != 0) - ERR("Unexpected stereo-mode: %s\n", mode); + else if(al::case_compare(*modeopt, "auto"sv) != 0) + ERR("Unexpected stereo-mode: {}", *modeopt); } } @@ -1481,27 +1578,27 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) /* Calculate the max number of sources, and split them between the mono and * stereo count given the requested number of stereo sources. */ - if(auto srcsopt = device->configValue(nullptr, "sources")) + if(auto srcsopt = device->configValue({}, "sources"sv)) { if(*srcsopt <= 0) numMono = 256; - else numMono = maxu(*srcsopt, 16); + else numMono = std::max(*srcsopt, 16u); } else { - numMono = minu(numMono, INT_MAX-numStereo); - numMono = maxu(numMono+numStereo, 256); + numMono = std::min(numMono, std::numeric_limits::max()-numStereo); + numMono = std::max(numMono+numStereo, 256u); } - numStereo = minu(numStereo, numMono); + numStereo = std::min(numStereo, numMono); numMono -= numStereo; device->SourcesMax = numMono + numStereo; device->NumMonoSources = numMono; device->NumStereoSources = numStereo; - if(auto sendsopt = device->configValue(nullptr, "sends")) - numSends = minu(numSends, static_cast(clampi(*sendsopt, 0, MAX_SENDS))); + if(auto sendsopt = device->configValue({}, "sends"sv)) + numSends = std::min(numSends, std::clamp(*sendsopt, 0u, uint{MaxSendCount})); device->NumAuxSends = numSends; - TRACE("Max sources: %d (%d + %d), effect slots: %d, sends: %d\n", + TRACE("Max sources: {} ({} + {}), effect slots: {}, sends: {}", device->SourcesMax, device->NumMonoSources, device->NumStereoSources, device->AuxiliaryEffectSlotMax, device->NumAuxSends); @@ -1517,17 +1614,18 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) case DevFmtX61: device->RealOut.RemixMap = X61Downmix; break; case DevFmtX71: device->RealOut.RemixMap = X71Downmix; break; case DevFmtX714: device->RealOut.RemixMap = X71Downmix; break; + case DevFmtX7144: device->RealOut.RemixMap = X71Downmix; break; case DevFmtX3D71: device->RealOut.RemixMap = X51Downmix; break; case DevFmtAmbi3D: break; } - nanoseconds::rep sample_delay{0}; + size_t sample_delay{0}; if(auto *encoder{device->mUhjEncoder.get()}) sample_delay += encoder->getDelay(); - if(device->getConfigValueBool(nullptr, "dither", true)) + if(device->getConfigValueBool({}, "dither"sv, true)) { - int depth{device->configValue(nullptr, "dither-depth").value_or(0)}; + int depth{device->configValue({}, "dither-depth"sv).value_or(0)}; if(depth <= 0) { switch(device->FmtType) @@ -1549,18 +1647,18 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) if(depth > 0) { - depth = clampi(depth, 2, 24); + depth = std::clamp(depth, 2, 24); device->DitherDepth = std::pow(2.0f, static_cast(depth-1)); } } if(!(device->DitherDepth > 0.0f)) - TRACE("Dithering disabled\n"); + TRACE("Dithering disabled"); else - TRACE("Dithering enabled (%d-bit, %g)\n", float2int(std::log2(device->DitherDepth)+0.5f)+1, - device->DitherDepth); + TRACE("Dithering enabled ({}-bit, {:g})", + float2int(std::log2(device->DitherDepth)+0.5f)+1, device->DitherDepth); if(!optlimit) - optlimit = device->configValue(nullptr, "output-limiter"); + optlimit = device->configValue({}, "output-limiter"); /* If the gain limiter is unset, use the limiter for integer-based output * (where samples must be clamped), and don't for floating-point (which can @@ -1582,8 +1680,8 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) break; } } - if(optlimit.value_or(false) == false) - TRACE("Output limiter disabled\n"); + if(!optlimit.value_or(false)) + TRACE("Output limiter disabled"); else { float thrshld{1.0f}; @@ -1610,88 +1708,104 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) sample_delay += limiter->getLookAhead(); device->Limiter = std::move(limiter); - TRACE("Output limiter enabled, %.4fdB limit\n", thrshld_dB); + TRACE("Output limiter enabled, {:.4f}dB limit", thrshld_dB); } /* Convert the sample delay from samples to nanosamples to nanoseconds. */ - device->FixedLatency += nanoseconds{seconds{sample_delay}} / device->Frequency; - TRACE("Fixed device latency: %" PRId64 "ns\n", int64_t{device->FixedLatency.count()}); + sample_delay = std::min(sample_delay, std::numeric_limits::max()); + device->FixedLatency += nanoseconds{seconds{sample_delay}} / device->mSampleRate; + TRACE("Fixed device latency: {}ns", device->FixedLatency.count()); FPUCtl mixer_mode{}; - for(ContextBase *ctxbase : *device->mContexts.load()) + auto reset_context = [device](ContextBase *ctxbase) { - auto *context = static_cast(ctxbase); + auto *context = dynamic_cast(ctxbase); + assert(context != nullptr); + if(!context) return; std::unique_lock proplock{context->mPropLock}; std::unique_lock slotlock{context->mEffectSlotLock}; /* Clear out unused effect slot clusters. */ - auto slot_cluster_not_in_use = [](ContextBase::EffectSlotCluster &cluster) + auto slot_cluster_not_in_use = [](ContextBase::EffectSlotCluster &clusterptr) -> bool { - for(size_t i{0};i < ContextBase::EffectSlotClusterSize;++i) - { - if(cluster[i].InUse) - return false; - } - return true; + return std::none_of(clusterptr->begin(), clusterptr->end(), + std::mem_fn(&EffectSlot::InUse)); }; - auto slotcluster_iter = std::remove_if(context->mEffectSlotClusters.begin(), + auto slotcluster_end = std::remove_if(context->mEffectSlotClusters.begin(), context->mEffectSlotClusters.end(), slot_cluster_not_in_use); - context->mEffectSlotClusters.erase(slotcluster_iter, context->mEffectSlotClusters.end()); + context->mEffectSlotClusters.erase(slotcluster_end, context->mEffectSlotClusters.end()); /* Free all wet buffers. Any in use will be reallocated with an updated * configuration in aluInitEffectPanning. */ - for(auto&& slots : context->mEffectSlotClusters) + auto clear_wetbuffers = [](ContextBase::EffectSlotCluster &clusterptr) { - for(size_t i{0};i < ContextBase::EffectSlotClusterSize;++i) + auto clear_buffer = [](EffectSlot &slot) { - slots[i].mWetBuffer.clear(); - slots[i].mWetBuffer.shrink_to_fit(); - slots[i].Wet.Buffer = {}; - } - } + slot.mWetBuffer.clear(); + slot.mWetBuffer.shrink_to_fit(); + slot.Wet.Buffer = {}; + }; + std::for_each(clusterptr->begin(), clusterptr->end(), clear_buffer); + }; + std::for_each(context->mEffectSlotClusters.begin(), context->mEffectSlotClusters.end(), + clear_wetbuffers); if(ALeffectslot *slot{context->mDefaultSlot.get()}) { - aluInitEffectPanning(slot->mSlot, context); + auto *slotbase = slot->mSlot; + aluInitEffectPanning(slotbase, context); + + if(auto *props = slotbase->Update.exchange(nullptr, std::memory_order_relaxed)) + AtomicReplaceHead(context->mFreeEffectSlotProps, props); EffectState *state{slot->Effect.State.get()}; state->mOutTarget = device->Dry.Buffer; state->deviceUpdate(device, slot->Buffer); - slot->updateProps(context); + slot->mPropsDirty = true; } if(EffectSlotArray *curarray{context->mActiveAuxSlots.load(std::memory_order_relaxed)}) - std::fill_n(curarray->end(), curarray->size(), nullptr); - for(auto &sublist : context->mEffectSlotList) + std::fill(curarray->begin()+ptrdiff_t(curarray->size()>>1), curarray->end(), nullptr); + auto reset_slots = [device,context](EffectSlotSubList &sublist) { uint64_t usemask{~sublist.FreeMask}; while(usemask) { - const int idx{al::countr_zero(usemask)}; - ALeffectslot *slot{sublist.EffectSlots + idx}; + const auto idx = static_cast(al::countr_zero(usemask)); + auto &slot = (*sublist.EffectSlots)[idx]; usemask &= ~(1_u64 << idx); - aluInitEffectPanning(slot->mSlot, context); + auto *slotbase = slot.mSlot; + aluInitEffectPanning(slotbase, context); - EffectState *state{slot->Effect.State.get()}; + if(auto *props = slotbase->Update.exchange(nullptr, std::memory_order_relaxed)) + AtomicReplaceHead(context->mFreeEffectSlotProps, props); + + EffectState *state{slot.Effect.State.get()}; state->mOutTarget = device->Dry.Buffer; - state->deviceUpdate(device, slot->Buffer); - slot->updateProps(context); + state->deviceUpdate(device, slot.Buffer); + slot.mPropsDirty = true; } - } + }; + std::for_each(context->mEffectSlotList.begin(), context->mEffectSlotList.end(), + reset_slots); + + /* Clear all effect slot props to let them get allocated again. */ + context->mEffectSlotPropClusters.clear(); + context->mFreeEffectSlotProps.store(nullptr, std::memory_order_relaxed); slotlock.unlock(); - const uint num_sends{device->NumAuxSends}; std::unique_lock srclock{context->mSourceLock}; - for(auto &sublist : context->mSourceList) + const uint num_sends{device->NumAuxSends}; + auto reset_sources = [num_sends](SourceSubList &sublist) { uint64_t usemask{~sublist.FreeMask}; while(usemask) { - const int idx{al::countr_zero(usemask)}; - ALsource *source{sublist.Sources + idx}; + const auto idx = static_cast(al::countr_zero(usemask)); + auto &source = (*sublist.Sources)[idx]; usemask &= ~(1_u64 << idx); auto clear_send = [](ALsource::SendData &send) -> void @@ -1701,30 +1815,31 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) send.Slot = nullptr; send.Gain = 1.0f; send.GainHF = 1.0f; - send.HFReference = LOWPASSFREQREF; + send.HFReference = LowPassFreqRef; send.GainLF = 1.0f; - send.LFReference = HIGHPASSFREQREF; + send.LFReference = HighPassFreqRef; }; - auto send_begin = source->Send.begin() + static_cast(num_sends); - std::for_each(send_begin, source->Send.end(), clear_send); + const auto sends = al::span{source.Send}.subspan(num_sends); + std::for_each(sends.begin(), sends.end(), clear_send); - source->mPropsDirty = true; + source.mPropsDirty = true; } - } + }; + std::for_each(context->mSourceList.begin(), context->mSourceList.end(), reset_sources); - auto voicelist = context->getVoicesSpan(); - for(Voice *voice : voicelist) + auto reset_voice = [device,num_sends,context](Voice *voice) { /* Clear extraneous property set sends. */ - std::fill(std::begin(voice->mProps.Send)+num_sends, std::end(voice->mProps.Send), - VoiceProps::SendData{}); + const auto sendparams = al::span{voice->mProps.Send}.subspan(num_sends); + std::fill(sendparams.begin(), sendparams.end(), VoiceProps::SendData{}); std::fill(voice->mSend.begin()+num_sends, voice->mSend.end(), Voice::TargetData{}); - for(auto &chandata : voice->mChans) + auto clear_wetparams = [num_sends](Voice::ChannelData &chandata) { - std::fill(chandata.mWetParams.begin()+num_sends, chandata.mWetParams.end(), - SendParams{}); - } + const auto wetparams = al::span{chandata.mWetParams}.subspan(num_sends); + std::fill(wetparams.begin(), wetparams.end(), SendParams{}); + }; + std::for_each(voice->mChans.begin(), voice->mChans.end(), clear_wetparams); if(VoicePropsItem *props{voice->mUpdate.exchange(nullptr, std::memory_order_relaxed)}) AtomicReplaceHead(context->mFreeVoiceProps, props); @@ -1734,10 +1849,13 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) voice->mPlayState.compare_exchange_strong(vstate, Voice::Stopped, std::memory_order_acquire, std::memory_order_acquire); if(voice->mSourceID.load(std::memory_order_relaxed) == 0u) - continue; + return; voice->prepare(device); - } + }; + const auto voicespan = context->getVoicesSpan(); + std::for_each(voicespan.begin(), voicespan.end(), reset_voice); + /* Clear all voice props to let them get allocated again. */ context->mVoicePropClusters.clear(); context->mFreeVoiceProps.store(nullptr, std::memory_order_relaxed); @@ -1745,25 +1863,29 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) context->mPropsDirty = false; UpdateContextProps(context); + UpdateAllEffectSlotProps(context); UpdateAllSourceProps(context); - } + }; + auto ctxspan = al::span{*device->mContexts.load()}; + std::for_each(ctxspan.begin(), ctxspan.end(), reset_context); mixer_mode.leave(); + device->mDeviceState = DeviceState::Configured; if(!device->Flags.test(DevicePaused)) { try { auto backend = device->Backend.get(); backend->start(); - device->Flags.set(DeviceRunning); + device->mDeviceState = DeviceState::Playing; } catch(al::backend_exception& e) { - ERR("%s\n", e.what()); - device->handleDisconnect("%s", e.what()); + ERR("{}", e.what()); + device->handleDisconnect("{}", e.what()); return ALC_INVALID_DEVICE; } - TRACE("Post-start: %s, %s, %uhz, %u / %u buffer\n", + TRACE("Post-start: {}, {}, {}hz, {} / {} buffer", DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType), - device->Frequency, device->UpdateSize, device->BufferSize); + device->mSampleRate, device->mUpdateSize, device->mBufferSize); } return ALC_NO_ERROR; @@ -1773,24 +1895,25 @@ ALCenum UpdateDeviceParams(ALCdevice *device, const int *attrList) * Updates device parameters as above, and also first clears the disconnected * status, if set. */ -bool ResetDeviceParams(ALCdevice *device, const int *attrList) +auto ResetDeviceParams(al::Device *device, const al::span attrList) -> bool { /* If the device was disconnected, reset it since we're opened anew. */ if(!device->Connected.load(std::memory_order_relaxed)) UNLIKELY { /* Make sure disconnection is finished before continuing on. */ - device->waitForMix(); + std::ignore = device->waitForMix(); for(ContextBase *ctxbase : *device->mContexts.load(std::memory_order_acquire)) { - auto *ctx = static_cast(ctxbase); - if(!ctx->mStopVoicesOnDisconnect.load(std::memory_order_acquire)) + auto *ctx = dynamic_cast(ctxbase); + assert(ctx != nullptr); + if(!ctx || !ctx->mStopVoicesOnDisconnect.load(std::memory_order_acquire)) continue; /* Clear any pending voice changes and reallocate voices to get a * clean restart. */ - std::lock_guard __{ctx->mSourceLock}; + std::lock_guard sourcelock{ctx->mSourceLock}; auto *vchg = ctx->mCurrentVoiceChange.load(std::memory_order_acquire); while(auto *next = vchg->mNext.load(std::memory_order_acquire)) vchg = next; @@ -1818,7 +1941,7 @@ bool ResetDeviceParams(ALCdevice *device, const int *attrList) /** Checks if the device handle is valid, and returns a new reference if so. */ DeviceRef VerifyDevice(ALCdevice *device) { - std::lock_guard _{ListLock}; + std::lock_guard listlock{ListLock}; auto iter = std::lower_bound(DeviceList.begin(), DeviceList.end(), device); if(iter != DeviceList.end() && *iter == device) { @@ -1834,7 +1957,7 @@ DeviceRef VerifyDevice(ALCdevice *device) */ ContextRef VerifyContext(ALCcontext *context) { - std::lock_guard _{ListLock}; + std::lock_guard listlock{ListLock}; auto iter = std::lower_bound(ContextList.begin(), ContextList.end(), context); if(iter != ContextList.end() && *iter == context) { @@ -1852,7 +1975,7 @@ FORCE_ALIGN void ALC_APIENTRY alsoft_set_log_callback(LPALSOFTLOGCALLBACK callba } /** Returns a new reference to the currently active context for this thread. */ -ContextRef GetContextRef(void) +ContextRef GetContextRef() noexcept { ALCcontext *context{ALCcontext::getThreadContext()}; if(context) @@ -1871,9 +1994,9 @@ ContextRef GetContextRef(void) return ContextRef{context}; } -void alcSetError(ALCdevice *device, ALCenum errorCode) +void alcSetError(al::Device *device, ALCenum errorCode) { - WARN("Error generated on device %p, code 0x%04x\n", voidp{device}, errorCode); + WARN("Error generated on device {}, code {:#04x}", voidp{device}, as_unsigned(errorCode)); if(TrapALCError) { #ifdef _WIN32 @@ -1897,6 +2020,9 @@ void alcSetError(ALCdevice *device, ALCenum errorCode) ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device) noexcept { + if(!gProcessRunning) + return ALC_INVALID_DEVICE; + DeviceRef dev{VerifyDevice(device)}; if(dev) return dev->LastError.exchange(ALC_NO_ERROR); return LastNullDeviceError.exchange(ALC_NO_ERROR); @@ -1912,7 +2038,7 @@ ALC_API void ALC_APIENTRY alcSuspendContext(ALCcontext *context) noexcept return; } - if(context->mContextFlags.test(ContextFlags::DebugBit)) UNLIKELY + if(ctx->mContextFlags.test(ContextFlags::DebugBit)) UNLIKELY ctx->debugMessage(DebugSource::API, DebugType::Portability, 0, DebugSeverity::Medium, "alcSuspendContext behavior is not portable -- some implementations suspend all " "rendering, some only defer property changes, and some are completely no-op; consider " @@ -1921,7 +2047,7 @@ ALC_API void ALC_APIENTRY alcSuspendContext(ALCcontext *context) noexcept if(SuspendDefers) { - std::lock_guard _{ctx->mPropLock}; + std::lock_guard proplock{ctx->mPropLock}; ctx->deferUpdates(); } } @@ -1935,8 +2061,8 @@ ALC_API void ALC_APIENTRY alcProcessContext(ALCcontext *context) noexcept return; } - if(context->mContextFlags.test(ContextFlags::DebugBit)) UNLIKELY - ctx->debugMessage(DebugSource::API, DebugType::Portability, 0, DebugSeverity::Medium, + if(ctx->mContextFlags.test(ContextFlags::DebugBit)) UNLIKELY + ctx->debugMessage(DebugSource::API, DebugType::Portability, 1, DebugSeverity::Medium, "alcProcessContext behavior is not portable -- some implementations resume rendering, " "some apply deferred property changes, and some are completely no-op; consider using " "alcDeviceResumeSOFT to resume rendering, or alProcessUpdatesSOFT to apply deferred " @@ -1944,137 +2070,108 @@ ALC_API void ALC_APIENTRY alcProcessContext(ALCcontext *context) noexcept if(SuspendDefers) { - std::lock_guard _{ctx->mPropLock}; + std::lock_guard proplock{ctx->mPropLock}; ctx->processUpdates(); } } -ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *Device, ALCenum param) noexcept +ALC_API auto ALC_APIENTRY alcGetString(ALCdevice *Device, ALCenum param) noexcept -> const ALCchar* { - const ALCchar *value{nullptr}; - switch(param) { - case ALC_NO_ERROR: - value = alcNoError; - break; - - case ALC_INVALID_ENUM: - value = alcErrInvalidEnum; - break; - - case ALC_INVALID_VALUE: - value = alcErrInvalidValue; - break; - - case ALC_INVALID_DEVICE: - value = alcErrInvalidDevice; - break; - - case ALC_INVALID_CONTEXT: - value = alcErrInvalidContext; - break; - - case ALC_OUT_OF_MEMORY: - value = alcErrOutOfMemory; - break; + case ALC_NO_ERROR: return GetNoErrorString(); + case ALC_INVALID_ENUM: return GetInvalidEnumString(); + case ALC_INVALID_VALUE: return GetInvalidValueString(); + case ALC_INVALID_DEVICE: return GetInvalidDeviceString(); + case ALC_INVALID_CONTEXT: return GetInvalidContextString(); + case ALC_OUT_OF_MEMORY: return GetOutOfMemoryString(); case ALC_DEVICE_SPECIFIER: - value = alcDefaultName; - break; + return GetDefaultName(); case ALC_ALL_DEVICES_SPECIFIER: if(DeviceRef dev{VerifyDevice(Device)}) { if(dev->Type == DeviceType::Capture) - alcSetError(dev.get(), ALC_INVALID_ENUM); - else if(dev->Type == DeviceType::Loopback) - value = alcDefaultName; - else { - std::lock_guard _{dev->StateLock}; - value = dev->DeviceName.c_str(); + alcSetError(dev.get(), ALC_INVALID_ENUM); + return nullptr; } + if(dev->Type == DeviceType::Loopback) + return GetDefaultName(); + + auto statelock = std::lock_guard{dev->StateLock}; + return dev->mDeviceName.c_str(); } - else - { - ProbeAllDevicesList(); - value = alcAllDevicesList.c_str(); - } - break; + ProbeAllDevicesList(); + return alcAllDevicesList.c_str(); case ALC_CAPTURE_DEVICE_SPECIFIER: if(DeviceRef dev{VerifyDevice(Device)}) { if(dev->Type != DeviceType::Capture) - alcSetError(dev.get(), ALC_INVALID_ENUM); - else { - std::lock_guard _{dev->StateLock}; - value = dev->DeviceName.c_str(); + alcSetError(dev.get(), ALC_INVALID_ENUM); + return nullptr; } + + auto statelock = std::lock_guard{dev->StateLock}; + return dev->mDeviceName.c_str(); } - else - { - ProbeCaptureDeviceList(); - value = alcCaptureDeviceList.c_str(); - } - break; + ProbeCaptureDeviceList(); + return alcCaptureDeviceList.c_str(); /* Default devices are always first in the list */ case ALC_DEFAULT_DEVICE_SPECIFIER: - value = alcDefaultName; - break; + return GetDefaultName(); case ALC_DEFAULT_ALL_DEVICES_SPECIFIER: if(alcAllDevicesList.empty()) ProbeAllDevicesList(); /* Copy first entry as default. */ - alcDefaultAllDevicesSpecifier = alcAllDevicesList.c_str(); - value = alcDefaultAllDevicesSpecifier.c_str(); - break; + if(alcAllDevicesArray.empty()) + return GetDefaultName(); + + alcDefaultAllDevicesSpecifier = alcAllDevicesArray.front(); + return alcDefaultAllDevicesSpecifier.c_str(); case ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER: if(alcCaptureDeviceList.empty()) ProbeCaptureDeviceList(); /* Copy first entry as default. */ - alcCaptureDefaultDeviceSpecifier = alcCaptureDeviceList.c_str(); - value = alcCaptureDefaultDeviceSpecifier.c_str(); - break; + if(alcCaptureDeviceArray.empty()) + return GetDefaultName(); + + alcCaptureDefaultDeviceSpecifier = alcCaptureDeviceArray.front(); + return alcCaptureDefaultDeviceSpecifier.c_str(); case ALC_EXTENSIONS: if(VerifyDevice(Device)) - value = alcExtensionList; - else - value = alcNoDeviceExtList; - break; + return GetExtensionList(); + return GetNoDeviceExtList(); case ALC_HRTF_SPECIFIER_SOFT: if(DeviceRef dev{VerifyDevice(Device)}) { - std::lock_guard _{dev->StateLock}; - value = (dev->mHrtf ? dev->mHrtfName.c_str() : ""); + std::lock_guard statelock{dev->StateLock}; + return dev->mHrtf ? dev->mHrtfName.c_str() : ""; } - else - alcSetError(nullptr, ALC_INVALID_DEVICE); - break; + alcSetError(nullptr, ALC_INVALID_DEVICE); + return nullptr; default: alcSetError(VerifyDevice(Device).get(), ALC_INVALID_ENUM); - break; } - return value; + return nullptr; } - -static size_t GetIntegerv(ALCdevice *device, ALCenum param, const al::span values) +namespace { +auto GetIntegerv(al::Device *device, ALCenum param, const al::span values) -> size_t { - size_t i; - if(values.empty()) { alcSetError(device, ALC_INVALID_VALUE); @@ -2099,7 +2196,7 @@ static size_t GetIntegerv(ALCdevice *device, ALCenum param, const al::span values[0] = alcEFXMinorVersion; return 1; case ALC_MAX_AUXILIARY_SENDS: - values[0] = MAX_SENDS; + values[0] = MaxSendCount; return 1; case ALC_ATTRIBUTES_SIZE: @@ -2125,7 +2222,7 @@ static size_t GetIntegerv(ALCdevice *device, ALCenum param, const al::span return 0; } - std::lock_guard _{device->StateLock}; + std::lock_guard statelock{device->StateLock}; if(device->Type == DeviceType::Capture) { static constexpr int MaxCaptureAttributes{9}; @@ -2135,11 +2232,9 @@ static size_t GetIntegerv(ALCdevice *device, ALCenum param, const al::span values[0] = MaxCaptureAttributes; return 1; case ALC_ALL_ATTRIBUTES: - i = 0; - if(values.size() < MaxCaptureAttributes) - alcSetError(device, ALC_INVALID_VALUE); - else + if(values.size() >= MaxCaptureAttributes) { + size_t i{0}; values[i++] = ALC_MAJOR_VERSION; values[i++] = alcMajorVersion; values[i++] = ALC_MINOR_VERSION; @@ -2150,8 +2245,10 @@ static size_t GetIntegerv(ALCdevice *device, ALCenum param, const al::span values[i++] = device->Connected.load(std::memory_order_relaxed); values[i++] = 0; assert(i == MaxCaptureAttributes); + return i; } - return i; + alcSetError(device, ALC_INVALID_VALUE); + return 0; case ALC_MAJOR_VERSION: values[0] = alcMajorVersion; @@ -2175,24 +2272,22 @@ static size_t GetIntegerv(ALCdevice *device, ALCenum param, const al::span } /* render device */ - auto NumAttrsForDevice = [](ALCdevice *aldev) noexcept + auto NumAttrsForDevice = [device]() noexcept -> uint8_t { - if(aldev->Type == DeviceType::Loopback && aldev->FmtChans == DevFmtAmbi3D) + if(device->Type == DeviceType::Loopback && device->FmtChans == DevFmtAmbi3D) return 37; return 31; }; switch(param) { case ALC_ATTRIBUTES_SIZE: - values[0] = NumAttrsForDevice(device); + values[0] = NumAttrsForDevice(); return 1; case ALC_ALL_ATTRIBUTES: - i = 0; - if(values.size() < static_cast(NumAttrsForDevice(device))) - alcSetError(device, ALC_INVALID_VALUE); - else + if(values.size() >= NumAttrsForDevice()) { + size_t i{0}; values[i++] = ALC_MAJOR_VERSION; values[i++] = alcMajorVersion; values[i++] = ALC_MINOR_VERSION; @@ -2203,11 +2298,11 @@ static size_t GetIntegerv(ALCdevice *device, ALCenum param, const al::span values[i++] = alcEFXMinorVersion; values[i++] = ALC_FREQUENCY; - values[i++] = static_cast(device->Frequency); + values[i++] = static_cast(device->mSampleRate); if(device->Type != DeviceType::Loopback) { values[i++] = ALC_REFRESH; - values[i++] = static_cast(device->Frequency / device->UpdateSize); + values[i++] = static_cast(device->mSampleRate / device->mUpdateSize); values[i++] = ALC_SYNC; values[i++] = ALC_FALSE; @@ -2258,8 +2353,11 @@ static size_t GetIntegerv(ALCdevice *device, ALCenum param, const al::span values[i++] = static_cast(device->getOutputMode1()); values[i++] = 0; + assert(i == NumAttrsForDevice()); + return i; } - return i; + alcSetError(device, ALC_INVALID_VALUE); + return 0; case ALC_MAJOR_VERSION: values[0] = alcMajorVersion; @@ -2278,7 +2376,7 @@ static size_t GetIntegerv(ALCdevice *device, ALCenum param, const al::span return 1; case ALC_FREQUENCY: - values[0] = static_cast(device->Frequency); + values[0] = static_cast(device->mSampleRate); return 1; case ALC_REFRESH: @@ -2287,7 +2385,7 @@ static size_t GetIntegerv(ALCdevice *device, ALCenum param, const al::span alcSetError(device, ALC_INVALID_DEVICE); return 0; } - values[0] = static_cast(device->Frequency / device->UpdateSize); + values[0] = static_cast(device->mSampleRate / device->mUpdateSize); return 1; case ALC_SYNC: @@ -2370,8 +2468,8 @@ static size_t GetIntegerv(ALCdevice *device, ALCenum param, const al::span case ALC_NUM_HRTF_SPECIFIERS_SOFT: device->enumerateHrtfs(); - values[0] = static_cast(minz(device->mHrtfList.size(), - std::numeric_limits::max())); + values[0] = static_cast(std::min(device->mHrtfList.size(), + size_t{std::numeric_limits::max()})); return 1; case ALC_OUTPUT_LIMITER_SOFT: @@ -2391,6 +2489,7 @@ static size_t GetIntegerv(ALCdevice *device, ALCenum param, const al::span } return 0; } +} // namespace ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values) noexcept { @@ -2409,113 +2508,118 @@ ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, alcSetError(dev.get(), ALC_INVALID_VALUE); return; } + const auto valuespan = al::span{values, static_cast(size)}; if(!dev || dev->Type == DeviceType::Capture) { - auto ivals = std::vector(static_cast(size)); + auto ivals = std::vector(valuespan.size()); if(size_t got{GetIntegerv(dev.get(), pname, ivals)}) - std::copy_n(ivals.begin(), got, values); + std::copy_n(ivals.cbegin(), got, valuespan.begin()); return; } /* render device */ - auto NumAttrsForDevice = [](ALCdevice *aldev) noexcept + auto NumAttrsForDevice = [](al::Device *aldev) noexcept -> size_t { if(aldev->Type == DeviceType::Loopback && aldev->FmtChans == DevFmtAmbi3D) return 41; return 35; }; - std::lock_guard _{dev->StateLock}; + std::lock_guard statelock{dev->StateLock}; switch(pname) { case ALC_ATTRIBUTES_SIZE: - *values = NumAttrsForDevice(dev.get()); + valuespan[0] = static_cast(NumAttrsForDevice(dev.get())); break; case ALC_ALL_ATTRIBUTES: - if(size < NumAttrsForDevice(dev.get())) + if(valuespan.size() < NumAttrsForDevice(dev.get())) alcSetError(dev.get(), ALC_INVALID_VALUE); else { size_t i{0}; - values[i++] = ALC_FREQUENCY; - values[i++] = dev->Frequency; + valuespan[i++] = ALC_FREQUENCY; + valuespan[i++] = dev->mSampleRate; if(dev->Type != DeviceType::Loopback) { - values[i++] = ALC_REFRESH; - values[i++] = dev->Frequency / dev->UpdateSize; + valuespan[i++] = ALC_REFRESH; + valuespan[i++] = dev->mSampleRate / dev->mUpdateSize; - values[i++] = ALC_SYNC; - values[i++] = ALC_FALSE; + valuespan[i++] = ALC_SYNC; + valuespan[i++] = ALC_FALSE; } else { - values[i++] = ALC_FORMAT_CHANNELS_SOFT; - values[i++] = EnumFromDevFmt(dev->FmtChans); + valuespan[i++] = ALC_FORMAT_CHANNELS_SOFT; + valuespan[i++] = EnumFromDevFmt(dev->FmtChans); - values[i++] = ALC_FORMAT_TYPE_SOFT; - values[i++] = EnumFromDevFmt(dev->FmtType); + valuespan[i++] = ALC_FORMAT_TYPE_SOFT; + valuespan[i++] = EnumFromDevFmt(dev->FmtType); if(dev->FmtChans == DevFmtAmbi3D) { - values[i++] = ALC_AMBISONIC_LAYOUT_SOFT; - values[i++] = EnumFromDevAmbi(dev->mAmbiLayout); + valuespan[i++] = ALC_AMBISONIC_LAYOUT_SOFT; + valuespan[i++] = EnumFromDevAmbi(dev->mAmbiLayout); - values[i++] = ALC_AMBISONIC_SCALING_SOFT; - values[i++] = EnumFromDevAmbi(dev->mAmbiScale); + valuespan[i++] = ALC_AMBISONIC_SCALING_SOFT; + valuespan[i++] = EnumFromDevAmbi(dev->mAmbiScale); - values[i++] = ALC_AMBISONIC_ORDER_SOFT; - values[i++] = dev->mAmbiOrder; + valuespan[i++] = ALC_AMBISONIC_ORDER_SOFT; + valuespan[i++] = dev->mAmbiOrder; } } - values[i++] = ALC_MONO_SOURCES; - values[i++] = dev->NumMonoSources; + valuespan[i++] = ALC_MONO_SOURCES; + valuespan[i++] = dev->NumMonoSources; - values[i++] = ALC_STEREO_SOURCES; - values[i++] = dev->NumStereoSources; + valuespan[i++] = ALC_STEREO_SOURCES; + valuespan[i++] = dev->NumStereoSources; - values[i++] = ALC_MAX_AUXILIARY_SENDS; - values[i++] = dev->NumAuxSends; + valuespan[i++] = ALC_MAX_AUXILIARY_SENDS; + valuespan[i++] = dev->NumAuxSends; - values[i++] = ALC_HRTF_SOFT; - values[i++] = (dev->mHrtf ? ALC_TRUE : ALC_FALSE); + valuespan[i++] = ALC_HRTF_SOFT; + valuespan[i++] = (dev->mHrtf ? ALC_TRUE : ALC_FALSE); - values[i++] = ALC_HRTF_STATUS_SOFT; - values[i++] = dev->mHrtfStatus; + valuespan[i++] = ALC_HRTF_STATUS_SOFT; + valuespan[i++] = dev->mHrtfStatus; - values[i++] = ALC_OUTPUT_LIMITER_SOFT; - values[i++] = dev->Limiter ? ALC_TRUE : ALC_FALSE; + valuespan[i++] = ALC_OUTPUT_LIMITER_SOFT; + valuespan[i++] = dev->Limiter ? ALC_TRUE : ALC_FALSE; ClockLatency clock{GetClockLatency(dev.get(), dev->Backend.get())}; - values[i++] = ALC_DEVICE_CLOCK_SOFT; - values[i++] = clock.ClockTime.count(); + valuespan[i++] = ALC_DEVICE_CLOCK_SOFT; + valuespan[i++] = clock.ClockTime.count(); - values[i++] = ALC_DEVICE_LATENCY_SOFT; - values[i++] = clock.Latency.count(); + valuespan[i++] = ALC_DEVICE_LATENCY_SOFT; + valuespan[i++] = clock.Latency.count(); - values[i++] = ALC_OUTPUT_MODE_SOFT; - values[i++] = static_cast(device->getOutputMode1()); + valuespan[i++] = ALC_OUTPUT_MODE_SOFT; + valuespan[i++] = al::to_underlying(dev->getOutputMode1()); - values[i++] = 0; + valuespan[i++] = 0; } break; case ALC_DEVICE_CLOCK_SOFT: { uint samplecount, refcount; - nanoseconds basecount; + seconds clocksec; + nanoseconds clocknsec; do { refcount = dev->waitForMix(); - basecount = dev->ClockBase; - samplecount = dev->SamplesDone; - } while(refcount != ReadRef(dev->MixCount)); - basecount += nanoseconds{seconds{samplecount}} / dev->Frequency; - *values = basecount.count(); + samplecount = dev->mSamplesDone.load(std::memory_order_relaxed); + clocksec = dev->mClockBaseSec.load(std::memory_order_relaxed); + clocknsec = dev->mClockBaseNSec.load(std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_acquire); + } while(refcount != dev->mMixCount.load(std::memory_order_relaxed)); + + valuespan[0] = nanoseconds{clocksec + nanoseconds{clocknsec} + + nanoseconds{seconds{samplecount}}/dev->mSampleRate}.count(); } break; case ALC_DEVICE_LATENCY_SOFT: - *values = GetClockLatency(dev.get(), dev->Backend.get()).Latency.count(); + valuespan[0] = GetClockLatency(dev.get(), dev->Backend.get()).Latency.count(); break; case ALC_DEVICE_CLOCK_LATENCY_SOFT: @@ -2524,15 +2628,15 @@ ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, else { ClockLatency clock{GetClockLatency(dev.get(), dev->Backend.get())}; - values[0] = clock.ClockTime.count(); - values[1] = clock.Latency.count(); + valuespan[0] = clock.ClockTime.count(); + valuespan[1] = clock.Latency.count(); } break; default: - auto ivals = std::vector(static_cast(size)); + auto ivals = std::vector(valuespan.size()); if(size_t got{GetIntegerv(dev.get(), pname, ivals)}) - std::copy_n(ivals.begin(), got, values); + std::copy_n(ivals.cbegin(), got, valuespan.begin()); break; } } @@ -2542,23 +2646,22 @@ ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent(ALCdevice *device, const A { DeviceRef dev{VerifyDevice(device)}; if(!extName) - alcSetError(dev.get(), ALC_INVALID_VALUE); - else { - size_t len = strlen(extName); - const char *ptr = (dev ? alcExtensionList : alcNoDeviceExtList); - while(ptr && *ptr) - { - if(al::strncasecmp(ptr, extName, len) == 0 && (ptr[len] == '\0' || isspace(ptr[len]))) - return ALC_TRUE; + alcSetError(dev.get(), ALC_INVALID_VALUE); + return ALC_FALSE; + } - if((ptr=strchr(ptr, ' ')) != nullptr) - { - do { - ++ptr; - } while(isspace(*ptr)); - } - } + const std::string_view tofind{extName}; + const auto extlist = dev ? std::string_view{GetExtensionList()} + : std::string_view{GetNoDeviceExtList()}; + auto matchpos = extlist.find(tofind); + while(matchpos != std::string_view::npos) + { + const auto endpos = matchpos + tofind.size(); + if((matchpos == 0 || std::isspace(extlist[matchpos-1])) + && (endpos == extlist.size() || std::isspace(extlist[endpos]))) + return ALC_TRUE; + matchpos = extlist.find(tofind, matchpos+1); } return ALC_FALSE; } @@ -2576,7 +2679,7 @@ ALC_API ALCvoid* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar return nullptr; } -#ifdef ALSOFT_EAX +#if ALSOFT_EAX if(eax_g_is_enabled) { for(const auto &func : eaxFunctions) @@ -2604,7 +2707,7 @@ ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *e return 0; } -#ifdef ALSOFT_EAX +#if ALSOFT_EAX if(eax_g_is_enabled) { for(const auto &enm : eaxEnumerations) @@ -2643,7 +2746,8 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin dev->LastError.store(ALC_NO_ERROR); - ALCenum err{UpdateDeviceParams(dev.get(), attrList)}; + const auto attrSpan = SpanFromAttributeList(attrList); + ALCenum err{UpdateDeviceParams(dev.get(), attrSpan)}; if(err != ALC_NO_ERROR) { alcSetError(dev.get(), err); @@ -2651,28 +2755,16 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin } ContextFlagBitset ctxflags{0}; - if(attrList) + for(size_t i{0};i < attrSpan.size();i+=2) { - for(size_t i{0};attrList[i];i+=2) + if(attrSpan[i] == ALC_CONTEXT_FLAGS_EXT) { - if(attrList[i] == ALC_CONTEXT_FLAGS_EXT) - { - ctxflags = static_cast(attrList[i+1]); - break; - } + ctxflags = static_cast(attrSpan[i+1]); + break; } } - ContextRef context{[](auto&& ...args) -> ContextRef - { - try { - return ContextRef{new ALCcontext{std::forward(args)...}}; - } - catch(std::exception& e) { - ERR("Failed to create ALCcontext: %s\n", e.what()); - return ContextRef{}; - } - }(dev, ctxflags)}; + auto context = ContextRef{new(std::nothrow) ALCcontext{dev, ctxflags}}; if(!context) { alcSetError(dev.get(), ALC_OUT_OF_MEMORY); @@ -2680,18 +2772,18 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin } context->init(); - if(auto volopt = dev->configValue(nullptr, "volume-adjust")) + if(auto volopt = dev->configValue({}, "volume-adjust")) { const float valf{*volopt}; if(!std::isfinite(valf)) - ERR("volume-adjust must be finite: %f\n", valf); + ERR("volume-adjust must be finite: {:f}", valf); else { - const float db{clampf(valf, -24.0f, 24.0f)}; + const float db{std::clamp(valf, -24.0f, 24.0f)}; if(db != valf) - WARN("volume-adjust clamped: %f, range: +/-%f\n", valf, 24.0f); + WARN("volume-adjust clamped: {:f}, range: +/-24", valf); context->mGainBoost = std::pow(10.0f, db/20.0f); - TRACE("volume-adjust gain: %f\n", context->mGainBoost); + TRACE("volume-adjust gain: {:f}", context->mGainBoost); } } @@ -2701,9 +2793,8 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin /* Allocate a new context array, which holds 1 more than the current/ * old array. */ - auto *oldarray = device->mContexts.load(); - const size_t newcount{oldarray->size()+1}; - std::unique_ptr newarray{ContextArray::Create(newcount)}; + auto *oldarray = dev->mContexts.load(); + auto newarray = ContextArray::Create(oldarray->size() + 1); /* Copy the current/old context handles to the new array, appending the * new context. @@ -2714,19 +2805,16 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin /* Store the new context array in the device. Wait for any current mix * to finish before deleting the old array. */ - dev->mContexts.store(newarray.release()); - if(oldarray != &DeviceBase::sEmptyContextArray) - { - dev->waitForMix(); - delete oldarray; - } + auto prevarray = dev->mContexts.exchange(std::move(newarray)); + std::ignore = dev->waitForMix(); } statelock.unlock(); { - std::lock_guard _{ListLock}; + listlock.lock(); auto iter = std::lower_bound(ContextList.cbegin(), ContextList.cend(), context.get()); ContextList.emplace(iter, context.get()); + listlock.unlock(); } if(ALeffectslot *slot{context->mDefaultSlot.get()}) @@ -2736,15 +2824,18 @@ ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCin if(sloterr == AL_NO_ERROR) slot->updateProps(context.get()); else - ERR("Failed to initialize the default effect\n"); + ERR("Failed to initialize the default effect"); } - TRACE("Created context %p\n", voidp{context.get()}); + TRACE("Created context {}", voidp{context.get()}); return context.release(); } ALC_API void ALC_APIENTRY alcDestroyContext(ALCcontext *context) noexcept { + if(!gProcessRunning) + return; + std::unique_lock listlock{ListLock}; auto iter = std::lower_bound(ContextList.begin(), ContextList.end(), context); if(iter == ContextList.end() || *iter != context) @@ -2760,18 +2851,13 @@ ALC_API void ALC_APIENTRY alcDestroyContext(ALCcontext *context) noexcept ContextRef ctx{*iter}; ContextList.erase(iter); - ALCdevice *Device{ctx->mALDevice.get()}; - - std::lock_guard _{Device->StateLock}; - if(!ctx->deinit() && Device->Flags.test(DeviceRunning)) - { - Device->Backend->stop(); - Device->Flags.reset(DeviceRunning); - } + auto *Device = ctx->mALDevice.get(); + std::lock_guard statelock{Device->StateLock}; + ctx->deinit(); } -ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void) noexcept +ALC_API auto ALC_APIENTRY alcGetCurrentContext() noexcept -> ALCcontext* { ALCcontext *Context{ALCcontext::getThreadContext()}; if(!Context) Context = ALCcontext::sGlobalContext.load(); @@ -2779,7 +2865,7 @@ ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void) noexcept } /** Returns the currently active thread-local context. */ -ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void) noexcept +ALC_API auto ALC_APIENTRY alcGetThreadContext() noexcept -> ALCcontext* { return ALCcontext::getThreadContext(); } ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context) noexcept @@ -2804,7 +2890,7 @@ ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context) noexc * the current context as its refcount is decremented. */ } - ContextRef{ALCcontext::sGlobalContext.exchange(ctx.release())}; + ctx = ContextRef{ALCcontext::sGlobalContext.exchange(ctx.release())}; ALCcontext::sGlobalContextLock.store(false, std::memory_order_release); /* Take ownership of the thread-local context reference (if any), clearing @@ -2861,45 +2947,59 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName) noexcep return nullptr; } - if(deviceName) + std::string_view devname{deviceName ? deviceName : ""}; + if(!devname.empty()) { - TRACE("Opening playback device \"%s\"\n", deviceName); - if(!deviceName[0] || al::strcasecmp(deviceName, alcDefaultName) == 0 + TRACE("Opening playback device \"{}\"", devname); + if(al::case_compare(devname, GetDefaultName()) == 0 #ifdef _WIN32 /* Some old Windows apps hardcode these expecting OpenAL to use a * specific audio API, even when they're not enumerated. Creative's * router effectively ignores them too. */ - || al::strcasecmp(deviceName, "DirectSound3D") == 0 - || al::strcasecmp(deviceName, "DirectSound") == 0 - || al::strcasecmp(deviceName, "MMSYSTEM") == 0 + || al::case_compare(devname, "DirectSound3D"sv) == 0 + || al::case_compare(devname, "DirectSound"sv) == 0 + || al::case_compare(devname, "MMSYSTEM"sv) == 0 #endif /* Some old Linux apps hardcode configuration strings that were * supported by the OpenAL SI. We can't really do anything useful * with them, so just ignore. */ - || (deviceName[0] == '\'' && deviceName[1] == '(') - || al::strcasecmp(deviceName, "openal-soft") == 0) - deviceName = nullptr; + || al::starts_with(devname, "'("sv) + || al::case_compare(devname, "openal-soft"sv) == 0) + devname = {}; + else + { + const auto prefix = GetDevicePrefix(); + if(!prefix.empty() && devname.size() > prefix.size() + && al::starts_with(devname, prefix)) + devname = devname.substr(prefix.size()); + } } else - TRACE("Opening default playback device\n"); + TRACE("Opening default playback device"); const uint DefaultSends{ -#ifdef ALSOFT_EAX +#if ALSOFT_EAX eax_g_is_enabled ? uint{EAX_MAX_FXSLOTS} : #endif // ALSOFT_EAX - DEFAULT_SENDS + uint{DefaultSendCount} }; - DeviceRef device{new ALCdevice{DeviceType::Playback}}; + auto device = DeviceRef{new(std::nothrow) al::Device{DeviceType::Playback}}; + if(!device) + { + WARN("Failed to create playback device handle"); + alcSetError(nullptr, ALC_OUT_OF_MEMORY); + return nullptr; + } /* Set output format */ device->FmtChans = DevFmtChannelsDefault; device->FmtType = DevFmtTypeDefault; - device->Frequency = DEFAULT_OUTPUT_RATE; - device->UpdateSize = DEFAULT_UPDATE_SIZE; - device->BufferSize = DEFAULT_UPDATE_SIZE * DEFAULT_NUM_UPDATES; + device->mSampleRate = DefaultOutputRate; + device->mUpdateSize = DefaultUpdateSize; + device->mBufferSize = DefaultUpdateSize * DefaultNumUpdates; device->SourcesMax = 256; device->NumStereoSources = 1; @@ -2908,47 +3008,55 @@ ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName) noexcep device->NumAuxSends = DefaultSends; try { - /* We need to ensure the device name isn't too long. The string_view is - * printed using the "%.*s" formatter, which uses an int for the - * precision/length. It wouldn't be a significant problem if larger - * values simply printed fewer characters due to truncation, but - * negative values are ignored, treating it like a normal null- - * terminated string, and string_views don't need to be null- - * terminated. - * - * Other than the annoyance of checking, this shouldn't be a problem. - * Two billion bytes is enough for a device name. - */ - const std::string_view devname{deviceName ? deviceName : ""}; - if(devname.length() >= std::numeric_limits::max()) - throw al::backend_exception{al::backend_error::NoDevice, - "Device name too long (%zu >= %d)", devname.length(), - std::numeric_limits::max()}; - auto backend = PlaybackFactory->createBackend(device.get(), BackendType::Playback); - std::lock_guard _{ListLock}; + std::lock_guard listlock{ListLock}; backend->open(devname); + device->mDeviceName = std::string{GetDevicePrefix()}+backend->mDeviceName; device->Backend = std::move(backend); } catch(al::backend_exception &e) { - WARN("Failed to open playback device: %s\n", e.what()); + WARN("Failed to open playback device: {}", e.what()); alcSetError(nullptr, (e.errorCode() == al::backend_error::OutOfMemory) ? ALC_OUT_OF_MEMORY : ALC_INVALID_VALUE); return nullptr; } + auto checkopt = [&device](const char *envname, const std::string_view optname) + { + if(auto optval = al::getenv(envname)) return optval; + return device->configValue("game_compat", optname); + }; + if(auto overrideopt = checkopt("__ALSOFT_VENDOR_OVERRIDE", "vendor-override"sv)) + { + device->mVendorOverride = std::move(*overrideopt); + TRACE("Overriding vendor string: \"{}\"", device->mVendorOverride); + } + if(auto overrideopt = checkopt("__ALSOFT_VERSION_OVERRIDE", "version-override"sv)) + { + device->mVersionOverride = std::move(*overrideopt); + TRACE("Overriding version string: \"{}\"", device->mVersionOverride); + } + if(auto overrideopt = checkopt("__ALSOFT_RENDERER_OVERRIDE", "renderer-override"sv)) { - std::lock_guard _{ListLock}; + device->mRendererOverride = std::move(*overrideopt); + TRACE("Overriding renderer string: \"{}\"", device->mRendererOverride); + } + + { + std::lock_guard listlock{ListLock}; auto iter = std::lower_bound(DeviceList.cbegin(), DeviceList.cend(), device.get()); DeviceList.emplace(iter, device.get()); } - TRACE("Created device %p, \"%s\"\n", voidp{device.get()}, device->DeviceName.c_str()); + TRACE("Created device {}, \"{}\"", voidp{device.get()}, device->mDeviceName); return device.release(); } ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device) noexcept { + if(!gProcessRunning) + return ALC_FALSE; + std::unique_lock listlock{ListLock}; auto iter = std::lower_bound(DeviceList.begin(), DeviceList.end(), device); if(iter == DeviceList.end() || *iter != device) @@ -2975,7 +3083,7 @@ ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device) noexcept auto ctxiter = std::lower_bound(ContextList.begin(), ContextList.end(), ctx); if(ctxiter != ContextList.end() && *ctxiter == ctx) { - orphanctxs.emplace_back(ContextRef{*ctxiter}); + orphanctxs.emplace_back(*ctxiter); ContextList.erase(ctxiter); } } @@ -2983,14 +3091,16 @@ ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device) noexcept for(ContextRef &context : orphanctxs) { - WARN("Releasing orphaned context %p\n", voidp{context.get()}); + WARN("Releasing orphaned context {}", voidp{context.get()}); context->deinit(); } orphanctxs.clear(); - if(dev->Flags.test(DeviceRunning)) + if(dev->mDeviceState == DeviceState::Playing) + { dev->Backend->stop(); - dev->Flags.reset(DeviceRunning); + dev->mDeviceState = DeviceState::Configured; + } return ALC_TRUE; } @@ -3015,17 +3125,31 @@ ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *deviceName, return nullptr; } - if(deviceName) + std::string_view devname{deviceName ? deviceName : ""}; + if(!devname.empty()) { - TRACE("Opening capture device \"%s\"\n", deviceName); - if(!deviceName[0] || al::strcasecmp(deviceName, alcDefaultName) == 0 - || al::strcasecmp(deviceName, "openal-soft") == 0) - deviceName = nullptr; + TRACE("Opening capture device \"{}\"", devname); + if(al::case_compare(devname, GetDefaultName()) == 0 + || al::case_compare(devname, "openal-soft"sv) == 0) + devname = {}; + else + { + const auto prefix = GetDevicePrefix(); + if(!prefix.empty() && devname.size() > prefix.size() + && al::starts_with(devname, prefix)) + devname = devname.substr(prefix.size()); + } } else - TRACE("Opening default capture device\n"); + TRACE("Opening default capture device"); - DeviceRef device{new ALCdevice{DeviceType::Capture}}; + auto device = DeviceRef{new(std::nothrow) al::Device{DeviceType::Capture}}; + if(!device) + { + WARN("Failed to create capture device handle"); + alcSetError(nullptr, ALC_OUT_OF_MEMORY); + return nullptr; + } auto decompfmt = DecomposeDevFormat(format); if(!decompfmt) @@ -3034,51 +3158,50 @@ ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *deviceName, return nullptr; } - device->Frequency = frequency; + device->mSampleRate = frequency; device->FmtChans = decompfmt->chans; device->FmtType = decompfmt->type; device->Flags.set(FrequencyRequest); device->Flags.set(ChannelsRequest); device->Flags.set(SampleTypeRequest); - device->UpdateSize = static_cast(samples); - device->BufferSize = static_cast(samples); + device->mUpdateSize = static_cast(samples); + device->mBufferSize = static_cast(samples); - TRACE("Capture format: %s, %s, %uhz, %u / %u buffer\n", DevFmtChannelsString(device->FmtChans), - DevFmtTypeString(device->FmtType), device->Frequency, device->UpdateSize, - device->BufferSize); + TRACE("Capture format: {}, {}, {}hz, {} / {} buffer", + DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType), + device->mSampleRate, device->mUpdateSize, device->mBufferSize); try { - const std::string_view devname{deviceName ? deviceName : ""}; - if(devname.length() >= std::numeric_limits::max()) - throw al::backend_exception{al::backend_error::NoDevice, - "Device name too long (%zu >= %d)", devname.length(), - std::numeric_limits::max()}; - auto backend = CaptureFactory->createBackend(device.get(), BackendType::Capture); - std::lock_guard _{ListLock}; + std::lock_guard listlock{ListLock}; backend->open(devname); + device->mDeviceName = std::string{GetDevicePrefix()}+backend->mDeviceName; device->Backend = std::move(backend); } catch(al::backend_exception &e) { - WARN("Failed to open capture device: %s\n", e.what()); + WARN("Failed to open capture device: {}", e.what()); alcSetError(nullptr, (e.errorCode() == al::backend_error::OutOfMemory) ? ALC_OUT_OF_MEMORY : ALC_INVALID_VALUE); return nullptr; } { - std::lock_guard _{ListLock}; + std::lock_guard listlock{ListLock}; auto iter = std::lower_bound(DeviceList.cbegin(), DeviceList.cend(), device.get()); DeviceList.emplace(iter, device.get()); } + device->mDeviceState = DeviceState::Configured; - TRACE("Created capture device %p, \"%s\"\n", voidp{device.get()}, device->DeviceName.c_str()); + TRACE("Created capture device {}, \"{}\"", voidp{device.get()}, device->mDeviceName); return device.release(); } ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device) noexcept { + if(!gProcessRunning) + return ALC_FALSE; + std::unique_lock listlock{ListLock}; auto iter = std::lower_bound(DeviceList.begin(), DeviceList.end(), device); if(iter == DeviceList.end() || *iter != device) @@ -3096,10 +3219,12 @@ ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device) noexcep DeviceList.erase(iter); listlock.unlock(); - std::lock_guard _{dev->StateLock}; - if(dev->Flags.test(DeviceRunning)) + std::lock_guard statelock{dev->StateLock}; + if(dev->mDeviceState == DeviceState::Playing) + { dev->Backend->stop(); - dev->Flags.reset(DeviceRunning); + dev->mDeviceState = DeviceState::Configured; + } return ALC_TRUE; } @@ -3113,19 +3238,20 @@ ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device) noexcept return; } - std::lock_guard _{dev->StateLock}; - if(!dev->Connected.load(std::memory_order_acquire)) + std::lock_guard statelock{dev->StateLock}; + if(!dev->Connected.load(std::memory_order_acquire) + || dev->mDeviceState < DeviceState::Configured) alcSetError(dev.get(), ALC_INVALID_DEVICE); - else if(!dev->Flags.test(DeviceRunning)) + else if(dev->mDeviceState != DeviceState::Playing) { try { auto backend = dev->Backend.get(); backend->start(); - dev->Flags.set(DeviceRunning); + dev->mDeviceState = DeviceState::Playing; } catch(al::backend_exception& e) { - ERR("%s\n", e.what()); - dev->handleDisconnect("%s", e.what()); + ERR("{}", e.what()); + dev->handleDisconnect("{}", e.what()); alcSetError(dev.get(), ALC_INVALID_DEVICE); } } @@ -3138,10 +3264,12 @@ ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device) noexcept alcSetError(dev.get(), ALC_INVALID_DEVICE); else { - std::lock_guard _{dev->StateLock}; - if(dev->Flags.test(DeviceRunning)) + std::lock_guard statelock{dev->StateLock}; + if(dev->mDeviceState == DeviceState::Playing) + { dev->Backend->stop(); - dev->Flags.reset(DeviceRunning); + dev->mDeviceState = DeviceState::Configured; + } } } @@ -3162,7 +3290,7 @@ ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, if(samples < 1) return; - std::lock_guard _{dev->StateLock}; + std::lock_guard statelock{dev->StateLock}; BackendBase *backend{dev->Backend.get()}; const auto usamples = static_cast(samples); @@ -3186,30 +3314,36 @@ ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceN InitConfig(); /* Make sure the device name, if specified, is us. */ - if(deviceName && strcmp(deviceName, alcDefaultName) != 0) + if(deviceName && strcmp(deviceName, GetDefaultName()) != 0) { alcSetError(nullptr, ALC_INVALID_VALUE); return nullptr; } const uint DefaultSends{ -#ifdef ALSOFT_EAX +#if ALSOFT_EAX eax_g_is_enabled ? uint{EAX_MAX_FXSLOTS} : #endif // ALSOFT_EAX - DEFAULT_SENDS + uint{DefaultSendCount} }; - DeviceRef device{new ALCdevice{DeviceType::Loopback}}; + auto device = DeviceRef{new(std::nothrow) al::Device{DeviceType::Loopback}}; + if(!device) + { + WARN("Failed to create loopback device handle"); + alcSetError(nullptr, ALC_OUT_OF_MEMORY); + return nullptr; + } device->SourcesMax = 256; device->AuxiliaryEffectSlotMax = 64; device->NumAuxSends = DefaultSends; //Set output format - device->BufferSize = 0; - device->UpdateSize = 0; + device->mBufferSize = 0; + device->mUpdateSize = 0; - device->Frequency = DEFAULT_OUTPUT_RATE; + device->mSampleRate = DefaultOutputRate; device->FmtChans = DevFmtChannelsDefault; device->FmtType = DevFmtTypeDefault; @@ -3220,22 +3354,23 @@ ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceN auto backend = LoopbackBackendFactory::getFactory().createBackend(device.get(), BackendType::Playback); backend->open("Loopback"); + device->mDeviceName = std::string{GetDevicePrefix()}+backend->mDeviceName; device->Backend = std::move(backend); } catch(al::backend_exception &e) { - WARN("Failed to open loopback device: %s\n", e.what()); + WARN("Failed to open loopback device: {}", e.what()); alcSetError(nullptr, (e.errorCode() == al::backend_error::OutOfMemory) ? ALC_OUT_OF_MEMORY : ALC_INVALID_VALUE); return nullptr; } { - std::lock_guard _{ListLock}; + std::lock_guard listlock{ListLock}; auto iter = std::lower_bound(DeviceList.cbegin(), DeviceList.cend(), device.get()); DeviceList.emplace(iter, device.get()); } - TRACE("Created loopback device %p\n", voidp{device.get()}); + TRACE("Created loopback device {}", voidp{device.get()}); return device.release(); } @@ -3252,7 +3387,7 @@ ALC_API ALCboolean ALC_APIENTRY alcIsRenderFormatSupportedSOFT(ALCdevice *device else { if(DevFmtTypeFromEnum(type).has_value() && DevFmtChannelsFromEnum(channels).has_value() - && freq >= MIN_OUTPUT_RATE && freq <= MAX_OUTPUT_RATE) + && freq >= int{MinOutputRate} && freq <= int{MaxOutputRate}) return ALC_TRUE; } @@ -3271,12 +3406,13 @@ ALC_API ALCboolean ALC_APIENTRY alcIsRenderFormatSupportedSOFT(ALCdevice *device #endif ALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, ALCvoid *buffer, ALCsizei samples) noexcept { - if(!device || device->Type != DeviceType::Loopback) UNLIKELY - alcSetError(device, ALC_INVALID_DEVICE); + auto aldev = dynamic_cast(device); + if(!aldev || aldev->Type != DeviceType::Loopback) UNLIKELY + alcSetError(aldev, ALC_INVALID_DEVICE); else if(samples < 0 || (samples > 0 && buffer == nullptr)) UNLIKELY - alcSetError(device, ALC_INVALID_VALUE); + alcSetError(aldev, ALC_INVALID_VALUE); else - device->renderSamples(buffer, static_cast(samples), device->channelsFromFmt()); + aldev->renderSamples(buffer, static_cast(samples), aldev->channelsFromFmt()); } @@ -3292,10 +3428,12 @@ ALC_API void ALC_APIENTRY alcDevicePauseSOFT(ALCdevice *device) noexcept alcSetError(dev.get(), ALC_INVALID_DEVICE); else { - std::lock_guard _{dev->StateLock}; - if(dev->Flags.test(DeviceRunning)) + std::lock_guard statelock{dev->StateLock}; + if(dev->mDeviceState == DeviceState::Playing) + { dev->Backend->stop(); - dev->Flags.reset(DeviceRunning); + dev->mDeviceState = DeviceState::Configured; + } dev->Flags.set(DevicePaused); } } @@ -3310,9 +3448,21 @@ ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device) noexcept return; } - std::lock_guard _{dev->StateLock}; + std::lock_guard statelock{dev->StateLock}; if(!dev->Flags.test(DevicePaused)) return; + if(dev->mDeviceState < DeviceState::Configured) + { + WARN("Cannot resume unconfigured device"); + alcSetError(dev.get(), ALC_INVALID_DEVICE); + return; + } + if(!dev->Connected.load()) + { + WARN("Cannot resume a disconnected device"); + alcSetError(dev.get(), ALC_INVALID_DEVICE); + return; + } dev->Flags.reset(DevicePaused); if(dev->mContexts.load()->empty()) return; @@ -3320,17 +3470,17 @@ ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device) noexcept try { auto backend = dev->Backend.get(); backend->start(); - dev->Flags.set(DeviceRunning); + dev->mDeviceState = DeviceState::Playing; } catch(al::backend_exception& e) { - ERR("%s\n", e.what()); - dev->handleDisconnect("%s", e.what()); + ERR("{}", e.what()); + dev->handleDisconnect("{}", e.what()); alcSetError(dev.get(), ALC_INVALID_DEVICE); return; } - TRACE("Post-resume: %s, %s, %uhz, %u / %u buffer\n", - DevFmtChannelsString(device->FmtChans), DevFmtTypeString(device->FmtType), - device->Frequency, device->UpdateSize, device->BufferSize); + TRACE("Post-resume: {}, {}, {}hz, {} / {} buffer", + DevFmtChannelsString(dev->FmtChans), DevFmtTypeString(dev->FmtType), + dev->mSampleRate, dev->mUpdateSize, dev->mBufferSize); } @@ -3371,17 +3521,19 @@ ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCi alcSetError(dev.get(), ALC_INVALID_DEVICE); return ALC_FALSE; } - std::lock_guard _{dev->StateLock}; + std::lock_guard statelock{dev->StateLock}; listlock.unlock(); /* Force the backend to stop mixing first since we're resetting. Also reset * the connected state so lost devices can attempt recover. */ - if(dev->Flags.test(DeviceRunning)) + if(dev->mDeviceState == DeviceState::Playing) + { dev->Backend->stop(); - dev->Flags.reset(DeviceRunning); + dev->mDeviceState = DeviceState::Configured; + } - return ResetDeviceParams(dev.get(), attribs) ? ALC_TRUE : ALC_FALSE; + return ResetDeviceParams(dev.get(), SpanFromAttributeList(attribs)) ? ALC_TRUE : ALC_FALSE; } @@ -3393,12 +3545,6 @@ ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCi FORCE_ALIGN ALCboolean ALC_APIENTRY alcReopenDeviceSOFT(ALCdevice *device, const ALCchar *deviceName, const ALCint *attribs) noexcept { - if(deviceName) - { - if(!deviceName[0] || al::strcasecmp(deviceName, alcDefaultName) == 0) - deviceName = nullptr; - } - std::unique_lock listlock{ListLock}; DeviceRef dev{VerifyDevice(device)}; if(!dev || dev->Type != DeviceType::Playback) @@ -3407,24 +3553,39 @@ FORCE_ALIGN ALCboolean ALC_APIENTRY alcReopenDeviceSOFT(ALCdevice *device, alcSetError(dev.get(), ALC_INVALID_DEVICE); return ALC_FALSE; } - std::lock_guard _{dev->StateLock}; + std::lock_guard statelock{dev->StateLock}; - /* Force the backend to stop mixing first since we're reopening. */ - if(dev->Flags.test(DeviceRunning)) + std::string_view devname{deviceName ? deviceName : ""}; + if(!devname.empty()) { - auto backend = dev->Backend.get(); - backend->stop(); - dev->Flags.reset(DeviceRunning); + if(devname.length() >= size_t{std::numeric_limits::max()}) + { + ERR("Device name too long ({} >= {})", devname.length(), + std::numeric_limits::max()); + alcSetError(dev.get(), ALC_INVALID_VALUE); + return ALC_FALSE; + } + if(al::case_compare(devname, GetDefaultName()) == 0) + devname = {}; + else + { + const auto prefix = GetDevicePrefix(); + if(!prefix.empty() && devname.size() > prefix.size() + && al::starts_with(devname, prefix)) + devname = devname.substr(prefix.size()); + } + } + + /* Force the backend device to stop first since we're opening another one. */ + const bool wasPlaying{dev->mDeviceState == DeviceState::Playing}; + if(wasPlaying) + { + dev->Backend->stop(); + dev->mDeviceState = DeviceState::Configured; } BackendPtr newbackend; try { - const std::string_view devname{deviceName ? deviceName : ""}; - if(devname.length() >= std::numeric_limits::max()) - throw al::backend_exception{al::backend_error::NoDevice, - "Device name too long (%zu >= %d)", devname.length(), - std::numeric_limits::max()}; - newbackend = PlaybackFactory->createBackend(dev.get(), BackendType::Playback); newbackend->open(devname); } @@ -3432,31 +3593,53 @@ FORCE_ALIGN ALCboolean ALC_APIENTRY alcReopenDeviceSOFT(ALCdevice *device, listlock.unlock(); newbackend = nullptr; - WARN("Failed to reopen playback device: %s\n", e.what()); + WARN("Failed to reopen playback device: {}", e.what()); alcSetError(dev.get(), (e.errorCode() == al::backend_error::OutOfMemory) ? ALC_OUT_OF_MEMORY : ALC_INVALID_VALUE); - /* If the device is connected, not paused, and has contexts, ensure it - * continues playing. - */ - if(dev->Connected.load(std::memory_order_relaxed) && !dev->Flags.test(DevicePaused) - && !dev->mContexts.load(std::memory_order_relaxed)->empty()) + if(dev->Connected.load(std::memory_order_relaxed) && wasPlaying) { try { auto backend = dev->Backend.get(); backend->start(); - dev->Flags.set(DeviceRunning); + dev->mDeviceState = DeviceState::Playing; } catch(al::backend_exception &be) { - ERR("%s\n", be.what()); - dev->handleDisconnect("%s", be.what()); + ERR("{}", be.what()); + dev->handleDisconnect("{}", be.what()); } } return ALC_FALSE; } listlock.unlock(); + dev->mDeviceName = std::string{GetDevicePrefix()}+newbackend->mDeviceName; dev->Backend = std::move(newbackend); - TRACE("Reopened device %p, \"%s\"\n", voidp{dev.get()}, dev->DeviceName.c_str()); + dev->mDeviceState = DeviceState::Unprepared; + TRACE("Reopened device {}, \"{}\"", voidp{dev.get()}, dev->mDeviceName); + + std::string{}.swap(dev->mVendorOverride); + std::string{}.swap(dev->mVersionOverride); + std::string{}.swap(dev->mRendererOverride); + auto checkopt = [&dev](const char *envname, const std::string_view optname) + { + if(auto optval = al::getenv(envname)) return optval; + return dev->configValue("game_compat", optname); + }; + if(auto overrideopt = checkopt("__ALSOFT_VENDOR_OVERRIDE", "vendor-override"sv)) + { + dev->mVendorOverride = std::move(*overrideopt); + TRACE("Overriding vendor string: \"{}\"", dev->mVendorOverride); + } + if(auto overrideopt = checkopt("__ALSOFT_VERSION_OVERRIDE", "version-override"sv)) + { + dev->mVersionOverride = std::move(*overrideopt); + TRACE("Overriding version string: \"{}\"", dev->mVersionOverride); + } + if(auto overrideopt = checkopt("__ALSOFT_RENDERER_OVERRIDE", "renderer-override"sv)) + { + dev->mRendererOverride = std::move(*overrideopt); + TRACE("Overriding renderer string: \"{}\"", dev->mRendererOverride); + } /* Always return true even if resetting fails. It shouldn't fail, but this * is primarily to avoid confusion by the app seeing the function return @@ -3469,6 +3652,38 @@ FORCE_ALIGN ALCboolean ALC_APIENTRY alcReopenDeviceSOFT(ALCdevice *device, * In this way, we essentially act as if the function succeeded, but * immediately disconnects following it. */ - ResetDeviceParams(dev.get(), attribs); + ResetDeviceParams(dev.get(), SpanFromAttributeList(attribs)); return ALC_TRUE; } + +/************************************************ + * ALC event query functions + ************************************************/ + +FORCE_ALIGN ALCenum ALC_APIENTRY alcEventIsSupportedSOFT(ALCenum eventType, ALCenum deviceType) noexcept +{ + auto etype = alc::GetEventType(eventType); + if(!etype) + { + WARN("Invalid event type: {:#04x}", as_unsigned(eventType)); + alcSetError(nullptr, ALC_INVALID_ENUM); + return ALC_FALSE; + } + + auto supported = alc::EventSupport::NoSupport; + switch(deviceType) + { + case ALC_PLAYBACK_DEVICE_SOFT: + if(PlaybackFactory) + supported = PlaybackFactory->queryEventSupport(*etype, BackendType::Playback); + return al::to_underlying(supported); + + case ALC_CAPTURE_DEVICE_SOFT: + if(CaptureFactory) + supported = CaptureFactory->queryEventSupport(*etype, BackendType::Capture); + return al::to_underlying(supported); + } + WARN("Invalid device type: {:#04x}", as_unsigned(deviceType)); + alcSetError(nullptr, ALC_INVALID_ENUM); + return ALC_FALSE; +} diff --git a/3rdparty/openal/alc/alconfig.cpp b/3rdparty/openal/alc/alconfig.cpp index 097ba3a0ca1a..e0a3920ce1b1 100644 --- a/3rdparty/openal/alc/alconfig.cpp +++ b/3rdparty/openal/alc/alconfig.cpp @@ -22,9 +22,6 @@ #include "alconfig.h" -#include -#include -#include #ifdef _WIN32 #include #include @@ -34,18 +31,25 @@ #endif #include -#include +#include +#include +#include +#include +#include +#include #include +#include #include +#include -#include "alfstream.h" +#include "almalloc.h" #include "alstring.h" #include "core/helpers.h" #include "core/logging.h" +#include "filesystem.h" #include "strutils.h" -#include "vector.h" -#if defined(ALSOFT_UWP) +#if ALSOFT_UWP #include // !!This is important!! #include #include @@ -55,6 +59,14 @@ using namespace winrt; namespace { +using namespace std::string_view_literals; + +#if defined(_WIN32) && !defined(_GAMING_XBOX) && !ALSOFT_UWP +struct CoTaskMemDeleter { + void operator()(void *mem) const { CoTaskMemFree(mem); } +}; +#endif + struct ConfigEntry { std::string key; std::string value; @@ -79,57 +91,48 @@ bool readline(std::istream &f, std::string &output) return std::getline(f, output) && !output.empty(); } -std::string expdup(const char *str) +std::string expdup(std::string_view str) { std::string output; - std::string envval; - while(*str != '\0') + while(!str.empty()) { - const char *addstr; - size_t addstrlen; - - if(str[0] != '$') + if(auto nextpos = str.find('$')) { - const char *next = std::strchr(str, '$'); - addstr = str; - addstrlen = next ? static_cast(next-str) : std::strlen(str); + output += str.substr(0, nextpos); + if(nextpos == std::string_view::npos) + break; - str += addstrlen; + str.remove_prefix(nextpos); } - else - { - str++; - if(*str == '$') - { - const char *next = std::strchr(str+1, '$'); - addstr = str; - addstrlen = next ? static_cast(next-str) : std::strlen(str); - str += addstrlen; - } - else - { - const bool hasbraces{(*str == '{')}; + str.remove_prefix(1); + if(str.empty()) + { + output += '$'; + break; + } + if(str.front() == '$') + { + output += '$'; + str.remove_prefix(1); + continue; + } - if(hasbraces) str++; - const char *envstart = str; - while(std::isalnum(*str) || *str == '_') - ++str; - if(hasbraces && *str != '}') - continue; - const std::string envname{envstart, str}; - if(hasbraces) str++; + const bool hasbraces{str.front() == '{'}; + if(hasbraces) str.remove_prefix(1); - envval = al::getenv(envname.c_str()).value_or(std::string{}); - addstr = envval.data(); - addstrlen = envval.length(); - } - } - if(addstrlen == 0) + size_t envend{0}; + while(envend < str.size() && (std::isalnum(str[envend]) || str[envend] == '_')) + ++envend; + if(hasbraces && (envend == str.size() || str[envend] != '}')) continue; + const std::string envname{str.substr(0, envend)}; + if(hasbraces) ++envend; + str.remove_prefix(envend); - output.append(addstr, addstrlen); + if(auto envval = al::getenv(envname.c_str())) + output += *envval; } return output; @@ -147,44 +150,43 @@ void LoadConfigFromFile(std::istream &f) if(buffer[0] == '[') { - auto line = const_cast(buffer.data()); - char *section = line+1; - char *endsection; - - endsection = std::strchr(section, ']'); - if(!endsection || section == endsection) + auto endpos = buffer.find(']', 1); + if(endpos == 1 || endpos == std::string::npos) { - ERR(" config parse error: bad line \"%s\"\n", line); + ERR(" config parse error: bad line \"{}\"", buffer); continue; } - if(endsection[1] != 0) + if(buffer[endpos+1] != '\0') { - char *end = endsection+1; - while(std::isspace(*end)) - ++end; - if(*end != 0 && *end != '#') + size_t last{endpos+1}; + while(last < buffer.size() && std::isspace(buffer[last])) + ++last; + + if(last < buffer.size() && buffer[last] != '#') { - ERR(" config parse error: bad line \"%s\"\n", line); + ERR(" config parse error: bad line \"{}\"", buffer); continue; } } - *endsection = 0; + + auto section = std::string_view{buffer}.substr(1, endpos-1); curSection.clear(); - if(al::strcasecmp(section, "general") != 0) + if(al::case_compare(section, "general"sv) != 0) { do { - char *nextp = std::strchr(section, '%'); - if(!nextp) + auto nextp = section.find('%'); + if(nextp == std::string_view::npos) { curSection += section; break; } - curSection.append(section, nextp); - section = nextp; + curSection += section.substr(0, nextp); + section.remove_prefix(nextp); - if(((section[1] >= '0' && section[1] <= '9') || + if(section.size() > 2 && + ((section[1] >= '0' && section[1] <= '9') || (section[1] >= 'a' && section[1] <= 'f') || (section[1] >= 'A' && section[1] <= 'F')) && ((section[2] >= '0' && section[2] <= '9') || @@ -205,19 +207,19 @@ void LoadConfigFromFile(std::istream &f) else if(section[2] >= 'A' && section[2] <= 'F') b |= (section[2]-'A'+0x0a); curSection += static_cast(b); - section += 3; + section.remove_prefix(3); } - else if(section[1] == '%') + else if(section.size() > 1 && section[1] == '%') { curSection += '%'; - section += 2; + section.remove_prefix(2); } else { curSection += '%'; - section += 1; + section.remove_prefix(1); } - } while(*section != 0); + } while(!section.empty()); } continue; @@ -232,19 +234,20 @@ void LoadConfigFromFile(std::istream &f) auto sep = buffer.find('='); if(sep == std::string::npos) { - ERR(" config parse error: malformed option line: \"%s\"\n", buffer.c_str()); + ERR(" config parse error: malformed option line: \"{}\"", buffer); continue; } - auto keyend = sep++; - while(keyend > 0 && std::isspace(buffer[keyend-1])) - --keyend; - if(!keyend) + auto keypart = std::string_view{buffer}.substr(0, sep++); + while(!keypart.empty() && std::isspace(keypart.back())) + keypart.remove_suffix(1); + if(keypart.empty()) { - ERR(" config parse error: malformed option line: \"%s\"\n", buffer.c_str()); + ERR(" config parse error: malformed option line: \"{}\"", buffer); continue; } - while(sep < buffer.size() && std::isspace(buffer[sep])) - sep++; + auto valpart = std::string_view{buffer}.substr(sep); + while(!valpart.empty() && std::isspace(valpart.front())) + valpart.remove_prefix(1); std::string fullKey; if(!curSection.empty()) @@ -252,20 +255,24 @@ void LoadConfigFromFile(std::istream &f) fullKey += curSection; fullKey += '/'; } - fullKey += buffer.substr(0u, keyend); + fullKey += keypart; - std::string value{(sep < buffer.size()) ? buffer.substr(sep) : std::string{}}; - if(value.size() > 1) + if(valpart.size() > size_t{std::numeric_limits::max()}) + { + ERR(" config parse error: value too long in line \"{}\"", buffer); + continue; + } + if(valpart.size() > 1) { - if((value.front() == '"' && value.back() == '"') - || (value.front() == '\'' && value.back() == '\'')) + if((valpart.front() == '"' && valpart.back() == '"') + || (valpart.front() == '\'' && valpart.back() == '\'')) { - value.pop_back(); - value.erase(value.begin()); + valpart.remove_prefix(1); + valpart.remove_suffix(1); } } - TRACE(" found '%s' = '%s'\n", fullKey.c_str(), value.c_str()); + TRACE(" setting '{}' = '{}'", fullKey, valpart); /* Check if we already have this option set */ auto find_key = [&fullKey](const ConfigEntry &entry) -> bool @@ -273,61 +280,50 @@ void LoadConfigFromFile(std::istream &f) auto ent = std::find_if(ConfOpts.begin(), ConfOpts.end(), find_key); if(ent != ConfOpts.end()) { - if(!value.empty()) - ent->value = expdup(value.c_str()); + if(!valpart.empty()) + ent->value = expdup(valpart); else ConfOpts.erase(ent); } - else if(!value.empty()) - ConfOpts.emplace_back(ConfigEntry{std::move(fullKey), expdup(value.c_str())}); + else if(!valpart.empty()) + ConfOpts.emplace_back(ConfigEntry{std::move(fullKey), expdup(valpart)}); } ConfOpts.shrink_to_fit(); } -const char *GetConfigValue(const char *devName, const char *blockName, const char *keyName) +auto GetConfigValue(const std::string_view devName, const std::string_view blockName, + const std::string_view keyName) -> const std::string& { - if(!keyName) - return nullptr; + static const auto emptyString = std::string{}; + if(keyName.empty()) + return emptyString; std::string key; - if(blockName && al::strcasecmp(blockName, "general") != 0) + if(!blockName.empty() && al::case_compare(blockName, "general"sv) != 0) { key = blockName; - if(devName) - { - key += '/'; - key += devName; - } key += '/'; - key += keyName; } - else + if(!devName.empty()) { - if(devName) - { - key = devName; - key += '/'; - } - key += keyName; + key += devName; + key += '/'; } + key += keyName; auto iter = std::find_if(ConfOpts.cbegin(), ConfOpts.cend(), - [&key](const ConfigEntry &entry) -> bool - { return entry.key == key; }); + [&key](const ConfigEntry &entry) -> bool { return entry.key == key; }); if(iter != ConfOpts.cend()) { - TRACE("Found %s = \"%s\"\n", key.c_str(), iter->value.c_str()); + TRACE("Found option {} = \"{}\"", key, iter->value); if(!iter->value.empty()) - return iter->value.c_str(); - return nullptr; + return iter->value; + return emptyString; } - if(!devName) - { - TRACE("Key %s not found\n", key.c_str()); - return nullptr; - } - return GetConfigValue(nullptr, blockName, keyName); + if(devName.empty()) + return emptyString; + return GetConfigValue({}, blockName, keyName); } } // namespace @@ -336,42 +332,47 @@ const char *GetConfigValue(const char *devName, const char *blockName, const cha #ifdef _WIN32 void ReadALConfig() { + fs::path path; + #if !defined(_GAMING_XBOX) { -#if !defined(ALSOFT_UWP) - WCHAR buffer[MAX_PATH]; - if (!SHGetSpecialFolderPathW(nullptr, buffer, CSIDL_APPDATA, FALSE)) - return; +#if !ALSOFT_UWP + std::unique_ptr bufstore; + const HRESULT hr{SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_DONT_UNEXPAND, + nullptr, al::out_ptr(bufstore))}; + if(SUCCEEDED(hr)) + { + const std::wstring_view buffer{bufstore.get()}; #else winrt::Windows::Storage::ApplicationDataContainer localSettings = winrt::Windows::Storage::ApplicationData::Current().LocalSettings(); - auto buffer = Windows::Storage::ApplicationData::Current().RoamingFolder().Path(); + auto bufstore = Windows::Storage::ApplicationData::Current().RoamingFolder().Path(); + std::wstring_view buffer{bufstore}; + { #endif - std::string filepath{wstr_to_utf8(buffer)}; - filepath += "\\alsoft.ini"; + path = fs::path{buffer}; + path /= L"alsoft.ini"; - TRACE("Loading config %s...\n", filepath.c_str()); - al::ifstream f{filepath}; - if(f.is_open()) - LoadConfigFromFile(f); + TRACE("Loading config {}...", al::u8_as_char(path.u8string())); + if(fs::ifstream f{path}; f.is_open()) + LoadConfigFromFile(f); + } } #endif - - std::string ppath{GetProcBinary().path}; - if(!ppath.empty()) + path = fs::u8path(GetProcBinary().path); + if(!path.empty()) { - ppath += "\\alsoft.ini"; - TRACE("Loading config %s...\n", ppath.c_str()); - al::ifstream f{ppath}; - if(f.is_open()) + path /= L"alsoft.ini"; + TRACE("Loading config {}...", al::u8_as_char(path.u8string())); + if(fs::ifstream f{path}; f.is_open()) LoadConfigFromFile(f); } if(auto confpath = al::getenv(L"ALSOFT_CONF")) { - TRACE("Loading config %s...\n", wstr_to_utf8(confpath->c_str()).c_str()); - al::ifstream f{*confpath}; - if(f.is_open()) + path = *confpath; + TRACE("Loading config {}...", al::u8_as_char(path.u8string())); + if(fs::ifstream f{path}; f.is_open()) LoadConfigFromFile(f); } } @@ -380,13 +381,11 @@ void ReadALConfig() void ReadALConfig() { - const char *str{"/etc/openal/alsoft.conf"}; + fs::path path{"/etc/openal/alsoft.conf"}; - TRACE("Loading config %s...\n", str); - al::ifstream f{str}; - if(f.is_open()) + TRACE("Loading config {}...", al::u8_as_char(path.u8string())); + if(fs::ifstream f{path}; f.is_open()) LoadConfigFromFile(f); - f.close(); std::string confpaths{al::getenv("XDG_CONFIG_DIRS").value_or("/etc/xdg")}; /* Go through the list in reverse, since "the order of base directories @@ -394,48 +393,43 @@ void ReadALConfig() * important". Ergo, we need to load the settings from the later dirs * first so that the settings in the earlier dirs override them. */ - std::string fname; while(!confpaths.empty()) { - auto next = confpaths.find_last_of(':'); + auto next = confpaths.rfind(':'); if(next < confpaths.length()) { - fname = confpaths.substr(next+1); + path = fs::path{std::string_view{confpaths}.substr(next+1)}.lexically_normal(); confpaths.erase(next); } else { - fname = confpaths; + path = fs::path{confpaths}.lexically_normal(); confpaths.clear(); } - if(fname.empty() || fname.front() != '/') - WARN("Ignoring XDG config dir: %s\n", fname.c_str()); + if(!path.is_absolute()) + WARN("Ignoring XDG config dir: {}", al::u8_as_char(path.u8string())); else { - if(fname.back() != '/') fname += "/alsoft.conf"; - else fname += "alsoft.conf"; + path /= "alsoft.conf"; - TRACE("Loading config %s...\n", fname.c_str()); - f = al::ifstream{fname}; - if(f.is_open()) + TRACE("Loading config {}...", al::u8_as_char(path.u8string())); + if(fs::ifstream f{path}; f.is_open()) LoadConfigFromFile(f); } - fname.clear(); } #ifdef __APPLE__ CFBundleRef mainBundle = CFBundleGetMainBundle(); if(mainBundle) { - unsigned char fileName[PATH_MAX]; - CFURLRef configURL; + CFURLRef configURL{CFBundleCopyResourceURL(mainBundle, CFSTR(".alsoftrc"), CFSTR(""), + nullptr)}; - if((configURL=CFBundleCopyResourceURL(mainBundle, CFSTR(".alsoftrc"), CFSTR(""), nullptr)) && - CFURLGetFileSystemRepresentation(configURL, true, fileName, sizeof(fileName))) + std::array fileName{}; + if(configURL && CFURLGetFileSystemRepresentation(configURL, true, fileName.data(), fileName.size())) { - f = al::ifstream{reinterpret_cast(fileName)}; - if(f.is_open()) + if(std::ifstream f{reinterpret_cast(fileName.data())}; f.is_open()) LoadConfigFromFile(f); } } @@ -443,102 +437,133 @@ void ReadALConfig() if(auto homedir = al::getenv("HOME")) { - fname = *homedir; - if(fname.back() != '/') fname += "/.alsoftrc"; - else fname += ".alsoftrc"; + path = *homedir; + path /= ".alsoftrc"; - TRACE("Loading config %s...\n", fname.c_str()); - f = al::ifstream{fname}; - if(f.is_open()) + TRACE("Loading config {}...", al::u8_as_char(path.u8string())); + if(std::ifstream f{path}; f.is_open()) LoadConfigFromFile(f); } if(auto configdir = al::getenv("XDG_CONFIG_HOME")) { - fname = *configdir; - if(fname.back() != '/') fname += "/alsoft.conf"; - else fname += "alsoft.conf"; + path = *configdir; + path /= "alsoft.conf"; } else { - fname.clear(); + path.clear(); if(auto homedir = al::getenv("HOME")) { - fname = *homedir; - if(fname.back() != '/') fname += "/.config/alsoft.conf"; - else fname += ".config/alsoft.conf"; + path = *homedir; + path /= ".config/alsoft.conf"; } } - if(!fname.empty()) + if(!path.empty()) { - TRACE("Loading config %s...\n", fname.c_str()); - f = al::ifstream{fname}; - if(f.is_open()) + TRACE("Loading config {}...", al::u8_as_char(path.u8string())); + if(std::ifstream f{path}; f.is_open()) LoadConfigFromFile(f); } - std::string ppath{GetProcBinary().path}; - if(!ppath.empty()) + path = GetProcBinary().path; + if(!path.empty()) { - if(ppath.back() != '/') ppath += "/alsoft.conf"; - else ppath += "alsoft.conf"; + path /= "alsoft.conf"; - TRACE("Loading config %s...\n", ppath.c_str()); - f = al::ifstream{ppath}; - if(f.is_open()) + TRACE("Loading config {}...", al::u8_as_char(path.u8string())); + if(std::ifstream f{path}; f.is_open()) LoadConfigFromFile(f); } if(auto confname = al::getenv("ALSOFT_CONF")) { - TRACE("Loading config %s...\n", confname->c_str()); - f = al::ifstream{*confname}; - if(f.is_open()) + TRACE("Loading config {}...", *confname); + if(std::ifstream f{*confname}; f.is_open()) LoadConfigFromFile(f); } } #endif -std::optional ConfigValueStr(const char *devName, const char *blockName, const char *keyName) +auto ConfigValueStr(const std::string_view devName, const std::string_view blockName, + const std::string_view keyName) -> std::optional { - if(const char *val{GetConfigValue(devName, blockName, keyName)}) + if(auto&& val = GetConfigValue(devName, blockName, keyName); !val.empty()) return val; return std::nullopt; } -std::optional ConfigValueInt(const char *devName, const char *blockName, const char *keyName) +auto ConfigValueInt(const std::string_view devName, const std::string_view blockName, + const std::string_view keyName) -> std::optional { - if(const char *val{GetConfigValue(devName, blockName, keyName)}) - return static_cast(std::strtol(val, nullptr, 0)); + if(auto&& val = GetConfigValue(devName, blockName, keyName); !val.empty()) try { + return static_cast(std::stol(val, nullptr, 0)); + } + catch(std::exception&) { + WARN("Option is not an int: {} = {}", keyName, val); + } + return std::nullopt; } -std::optional ConfigValueUInt(const char *devName, const char *blockName, const char *keyName) +auto ConfigValueUInt(const std::string_view devName, const std::string_view blockName, + const std::string_view keyName) -> std::optional { - if(const char *val{GetConfigValue(devName, blockName, keyName)}) - return static_cast(std::strtoul(val, nullptr, 0)); + if(auto&& val = GetConfigValue(devName, blockName, keyName); !val.empty()) try { + return static_cast(std::stoul(val, nullptr, 0)); + } + catch(std::exception&) { + WARN("Option is not an unsigned int: {} = {}", keyName, val); + } return std::nullopt; } -std::optional ConfigValueFloat(const char *devName, const char *blockName, const char *keyName) +auto ConfigValueFloat(const std::string_view devName, const std::string_view blockName, + const std::string_view keyName) -> std::optional { - if(const char *val{GetConfigValue(devName, blockName, keyName)}) - return std::strtof(val, nullptr); + if(auto&& val = GetConfigValue(devName, blockName, keyName); !val.empty()) try { + return std::stof(val); + } + catch(std::exception&) { + WARN("Option is not a float: {} = {}", keyName, val); + } return std::nullopt; } -std::optional ConfigValueBool(const char *devName, const char *blockName, const char *keyName) +auto ConfigValueBool(const std::string_view devName, const std::string_view blockName, + const std::string_view keyName) -> std::optional { - if(const char *val{GetConfigValue(devName, blockName, keyName)}) - return al::strcasecmp(val, "on") == 0 || al::strcasecmp(val, "yes") == 0 - || al::strcasecmp(val, "true")==0 || atoi(val) != 0; + if(auto&& val = GetConfigValue(devName, blockName, keyName); !val.empty()) try { + return al::case_compare(val, "on"sv) == 0 || al::case_compare(val, "yes"sv) == 0 + || al::case_compare(val, "true"sv) == 0 || std::stoll(val) != 0; + } + catch(std::out_of_range&) { + /* If out of range, the value is some non-0 (true) value and it doesn't + * matter that it's too big or small. + */ + return true; + } + catch(std::exception&) { + /* If stoll fails to convert for any other reason, it's some other word + * that's treated as false. + */ + return false; + } return std::nullopt; } -bool GetConfigValueBool(const char *devName, const char *blockName, const char *keyName, bool def) +auto GetConfigValueBool(const std::string_view devName, const std::string_view blockName, + const std::string_view keyName, bool def) -> bool { - if(const char *val{GetConfigValue(devName, blockName, keyName)}) - return (al::strcasecmp(val, "on") == 0 || al::strcasecmp(val, "yes") == 0 - || al::strcasecmp(val, "true") == 0 || atoi(val) != 0); + if(auto&& val = GetConfigValue(devName, blockName, keyName); !val.empty()) try { + return al::case_compare(val, "on"sv) == 0 || al::case_compare(val, "yes"sv) == 0 + || al::case_compare(val, "true"sv) == 0 || std::stoll(val) != 0; + } + catch(std::out_of_range&) { + return true; + } + catch(std::exception&) { + return false; + } return def; } diff --git a/3rdparty/openal/alc/alconfig.h b/3rdparty/openal/alc/alconfig.h index 1eb44405bcae..e7daac28ef93 100644 --- a/3rdparty/openal/alc/alconfig.h +++ b/3rdparty/openal/alc/alconfig.h @@ -3,16 +3,23 @@ #include #include +#include void ReadALConfig(); -bool GetConfigValueBool(const char *devName, const char *blockName, const char *keyName, bool def); +bool GetConfigValueBool(const std::string_view devName, const std::string_view blockName, + const std::string_view keyName, bool def); -std::optional ConfigValueStr(const char *devName, const char *blockName, const char *keyName); -std::optional ConfigValueInt(const char *devName, const char *blockName, const char *keyName); -std::optional ConfigValueUInt(const char *devName, const char *blockName, const char *keyName); -std::optional ConfigValueFloat(const char *devName, const char *blockName, const char *keyName); -std::optional ConfigValueBool(const char *devName, const char *blockName, const char *keyName); +std::optional ConfigValueStr(const std::string_view devName, + const std::string_view blockName, const std::string_view keyName); +std::optional ConfigValueInt(const std::string_view devName, const std::string_view blockName, + const std::string_view keyName); +std::optional ConfigValueUInt(const std::string_view devName, + const std::string_view blockName, const std::string_view keyName); +std::optional ConfigValueFloat(const std::string_view devName, + const std::string_view blockName, const std::string_view keyName); +std::optional ConfigValueBool(const std::string_view devName, + const std::string_view blockName, const std::string_view keyName); #endif /* ALCONFIG_H */ diff --git a/3rdparty/openal/alc/alu.cpp b/3rdparty/openal/alc/alu.cpp index 6eb4691edfa3..d8bdb3c65138 100644 --- a/3rdparty/openal/alc/alu.cpp +++ b/3rdparty/openal/alc/alu.cpp @@ -19,6 +19,7 @@ */ #include "config.h" +#include "config_simd.h" #include "alu.h" @@ -26,23 +27,25 @@ #include #include #include -#include -#include +#include #include +#include +#include #include #include -#include #include #include #include -#include #include -#include +#include +#include #include +#include #include "almalloc.h" #include "alnumbers.h" #include "alnumeric.h" +#include "alsem.h" #include "alspan.h" #include "alstring.h" #include "atomic.h" @@ -70,6 +73,7 @@ #include "core/mixer/defs.h" #include "core/mixer/hrtfdefs.h" #include "core/resampler_limits.h" +#include "core/storage_formats.h" #include "core/uhjfilter.h" #include "core/voice.h" #include "core/voice_change.h" @@ -78,19 +82,18 @@ #include "ringbuffer.h" #include "strutils.h" #include "vecmat.h" -#include "vector.h" struct CTag; -#ifdef HAVE_SSE +#if HAVE_SSE struct SSETag; #endif -#ifdef HAVE_SSE2 +#if HAVE_SSE2 struct SSE2Tag; #endif -#ifdef HAVE_SSE4_1 +#if HAVE_SSE4_1 struct SSE4Tag; #endif -#ifdef HAVE_NEON +#if HAVE_NEON struct NEONTag; #endif struct PointTag; @@ -107,15 +110,14 @@ namespace { using uint = unsigned int; using namespace std::chrono; - -using namespace std::placeholders; +using namespace std::string_view_literals; float InitConeScale() { float ret{1.0f}; if(auto optval = al::getenv("__ALSOFT_HALF_ANGLE_CONES")) { - if(al::strcasecmp(optval->c_str(), "true") == 0 + if(al::case_compare(*optval, "true"sv) == 0 || strtol(optval->c_str(), nullptr, 0) == 1) ret *= 0.5f; } @@ -136,18 +138,19 @@ float NfcScale{1.0f}; using HrtfDirectMixerFunc = void(*)(const FloatBufferSpan LeftOut, const FloatBufferSpan RightOut, - const al::span InSamples, float2 *AccumSamples, float *TempBuf, - HrtfChannelState *ChanState, const size_t IrSize, const size_t BufferSize); + const al::span InSamples, const al::span AccumSamples, + const al::span TempBuf, const al::span ChanState, + const size_t IrSize, const size_t SamplesToDo); HrtfDirectMixerFunc MixDirectHrtf{MixDirectHrtf_}; -inline HrtfDirectMixerFunc SelectHrtfMixer(void) +inline HrtfDirectMixerFunc SelectHrtfMixer() { -#ifdef HAVE_NEON +#if HAVE_NEON if((CPUCapFlags&CPU_CAP_NEON)) return MixDirectHrtf_; #endif -#ifdef HAVE_SSE +#if HAVE_SSE if((CPUCapFlags&CPU_CAP_SSE)) return MixDirectHrtf_; #endif @@ -164,7 +167,7 @@ inline void BsincPrepare(const uint increment, BsincState *state, const BSincTab if(increment > MixerFracOne) { sf = MixerFracOne/static_cast(increment) - table->scaleBase; - sf = maxf(0.0f, BSincScaleCount*sf*table->scaleRange - 1.0f); + sf = std::max(0.0f, BSincScaleCount*sf*table->scaleRange - 1.0f); si = float2uint(sf); /* The interpolation factor is fit to this diagonally-symmetric curve * to reduce the transition ripple caused by interpolating different @@ -176,7 +179,7 @@ inline void BsincPrepare(const uint increment, BsincState *state, const BSincTab state->sf = sf; state->m = table->m[si]; state->l = (state->m/2) - 1; - state->filter = table->Tab + table->filterOffset[si]; + state->filter = table->Tab.subspan(table->filterOffset[si]); } inline ResamplerFunc SelectResampler(Resampler resampler, uint increment) @@ -186,25 +189,34 @@ inline ResamplerFunc SelectResampler(Resampler resampler, uint increment) case Resampler::Point: return Resample_; case Resampler::Linear: -#ifdef HAVE_NEON +#if HAVE_NEON if((CPUCapFlags&CPU_CAP_NEON)) return Resample_; #endif -#ifdef HAVE_SSE4_1 +#if HAVE_SSE4_1 if((CPUCapFlags&CPU_CAP_SSE4_1)) return Resample_; #endif -#ifdef HAVE_SSE2 +#if HAVE_SSE2 if((CPUCapFlags&CPU_CAP_SSE2)) return Resample_; #endif return Resample_; - case Resampler::Cubic: -#ifdef HAVE_NEON + case Resampler::Spline: + case Resampler::Gaussian: +#if HAVE_NEON if((CPUCapFlags&CPU_CAP_NEON)) return Resample_; #endif -#ifdef HAVE_SSE +#if HAVE_SSE4_1 + if((CPUCapFlags&CPU_CAP_SSE4_1)) + return Resample_; +#endif +#if HAVE_SSE2 + if((CPUCapFlags&CPU_CAP_SSE2)) + return Resample_; +#endif +#if HAVE_SSE if((CPUCapFlags&CPU_CAP_SSE)) return Resample_; #endif @@ -213,11 +225,11 @@ inline ResamplerFunc SelectResampler(Resampler resampler, uint increment) case Resampler::BSinc24: if(increment > MixerFracOne) { -#ifdef HAVE_NEON +#if HAVE_NEON if((CPUCapFlags&CPU_CAP_NEON)) return Resample_; #endif -#ifdef HAVE_SSE +#if HAVE_SSE if((CPUCapFlags&CPU_CAP_SSE)) return Resample_; #endif @@ -226,11 +238,11 @@ inline ResamplerFunc SelectResampler(Resampler resampler, uint increment) /* fall-through */ case Resampler::FastBSinc12: case Resampler::FastBSinc24: -#ifdef HAVE_NEON +#if HAVE_NEON if((CPUCapFlags&CPU_CAP_NEON)) return Resample_; #endif -#ifdef HAVE_SSE +#if HAVE_SSE if((CPUCapFlags&CPU_CAP_SSE)) return Resample_; #endif @@ -249,7 +261,7 @@ void aluInit(CompatFlagBitset flags, const float nfcscale) YScale = flags.test(CompatFlags::ReverseY) ? -1.0f : 1.0f; ZScale = flags.test(CompatFlags::ReverseZ) ? -1.0f : 1.0f; - NfcScale = clampf(nfcscale, 0.0001f, 10000.0f); + NfcScale = std::clamp(nfcscale, 0.0001f, 10000.0f); } @@ -260,16 +272,19 @@ ResamplerFunc PrepareResampler(Resampler resampler, uint increment, InterpState case Resampler::Point: case Resampler::Linear: break; - case Resampler::Cubic: - state->cubic.filter = gCubicSpline.Tab.data(); + case Resampler::Spline: + state->emplace(al::span{gSplineFilter.mTable}); + break; + case Resampler::Gaussian: + state->emplace(al::span{gGaussianFilter.mTable}); break; case Resampler::FastBSinc12: case Resampler::BSinc12: - BsincPrepare(increment, &state->bsinc, &gBSinc12); + BsincPrepare(increment, &state->emplace(), &gBSinc12); break; case Resampler::FastBSinc24: case Resampler::BSinc24: - BsincPrepare(increment, &state->bsinc, &gBSinc24); + BsincPrepare(increment, &state->emplace(), &gBSinc24); break; } return SelectResampler(resampler, increment); @@ -279,34 +294,33 @@ ResamplerFunc PrepareResampler(Resampler resampler, uint increment, InterpState void DeviceBase::ProcessHrtf(const size_t SamplesToDo) { /* HRTF is stereo output only. */ - const uint lidx{RealOut.ChannelIndex[FrontLeft]}; - const uint ridx{RealOut.ChannelIndex[FrontRight]}; + const size_t lidx{RealOut.ChannelIndex[FrontLeft]}; + const size_t ridx{RealOut.ChannelIndex[FrontRight]}; MixDirectHrtf(RealOut.Buffer[lidx], RealOut.Buffer[ridx], Dry.Buffer, HrtfAccumData, - mHrtfState->mTemp.data(), mHrtfState->mChannels.data(), mHrtfState->mIrSize, SamplesToDo); + mHrtfState->mTemp, mHrtfState->mChannels, mHrtfState->mIrSize, SamplesToDo); } void DeviceBase::ProcessAmbiDec(const size_t SamplesToDo) { - AmbiDecoder->process(RealOut.Buffer, Dry.Buffer.data(), SamplesToDo); + AmbiDecoder->process(RealOut.Buffer, Dry.Buffer, SamplesToDo); } void DeviceBase::ProcessAmbiDecStablized(const size_t SamplesToDo) { /* Decode with front image stablization. */ - const uint lidx{RealOut.ChannelIndex[FrontLeft]}; - const uint ridx{RealOut.ChannelIndex[FrontRight]}; - const uint cidx{RealOut.ChannelIndex[FrontCenter]}; + const size_t lidx{RealOut.ChannelIndex[FrontLeft]}; + const size_t ridx{RealOut.ChannelIndex[FrontRight]}; + const size_t cidx{RealOut.ChannelIndex[FrontCenter]}; - AmbiDecoder->processStablize(RealOut.Buffer, Dry.Buffer.data(), lidx, ridx, cidx, - SamplesToDo); + AmbiDecoder->processStablize(RealOut.Buffer, Dry.Buffer, lidx, ridx, cidx, SamplesToDo); } void DeviceBase::ProcessUhj(const size_t SamplesToDo) { /* UHJ is stereo output only. */ - const uint lidx{RealOut.ChannelIndex[FrontLeft]}; - const uint ridx{RealOut.ChannelIndex[FrontRight]}; + const size_t lidx{RealOut.ChannelIndex[FrontLeft]}; + const size_t ridx{RealOut.ChannelIndex[FrontRight]}; /* Encode to stereo-compatible 2-channel UHJ output. */ mUhjEncoder->encode(RealOut.Buffer[lidx].data(), RealOut.Buffer[ridx].data(), @@ -316,15 +330,15 @@ void DeviceBase::ProcessUhj(const size_t SamplesToDo) void DeviceBase::ProcessBs2b(const size_t SamplesToDo) { /* First, decode the ambisonic mix to the "real" output. */ - AmbiDecoder->process(RealOut.Buffer, Dry.Buffer.data(), SamplesToDo); + AmbiDecoder->process(RealOut.Buffer, Dry.Buffer, SamplesToDo); /* BS2B is stereo output only. */ - const uint lidx{RealOut.ChannelIndex[FrontLeft]}; - const uint ridx{RealOut.ChannelIndex[FrontRight]}; + const size_t lidx{RealOut.ChannelIndex[FrontLeft]}; + const size_t ridx{RealOut.ChannelIndex[FrontRight]}; /* Now apply the BS2B binaural/crossfeed filter. */ - bs2b_cross_feed(Bs2b.get(), RealOut.Buffer[lidx].data(), RealOut.Buffer[ridx].data(), - SamplesToDo); + Bs2b->cross_feed(al::span{RealOut.Buffer[lidx]}.first(SamplesToDo), + al::span{RealOut.Buffer[ridx]}.first(SamplesToDo)); } @@ -350,21 +364,22 @@ inline uint dither_rng(uint *seed) noexcept void UpsampleBFormatTransform( const al::span,MaxAmbiChannels> output, const al::span> upsampler, - const al::span,MaxAmbiChannels> rotator, size_t coeffs_order) + const al::span,MaxAmbiChannels> rotator, + size_t ambi_order) { - const size_t num_chans{AmbiChannelsFromOrder(coeffs_order)}; + const size_t num_chans{AmbiChannelsFromOrder(ambi_order)}; for(size_t i{0};i < upsampler.size();++i) output[i].fill(0.0f); for(size_t i{0};i < upsampler.size();++i) { for(size_t k{0};k < num_chans;++k) { - float *RESTRICT out{output[i].data()}; + const float a{upsampler[i][k]}; /* Write the full number of channels. The compiler will have an * easier time optimizing if it has a fixed length. */ - for(size_t j{0};j < MaxAmbiChannels;++j) - out[j] += upsampler[i][k] * rotator[k][j]; + std::transform(rotator[k].cbegin(), rotator[k].cend(), output[i].cbegin(), + output[i].begin(), [a](float rot, float dst) noexcept { return rot*a + dst; }); } } } @@ -423,11 +438,19 @@ bool CalcContextParams(ContextBase *ctx) ctx->mParams.Velocity = rot * vel; ctx->mParams.Gain = props->Gain * ctx->mGainBoost; - ctx->mParams.MetersPerUnit = props->MetersPerUnit; + ctx->mParams.MetersPerUnit = props->MetersPerUnit +#if ALSOFT_EAX + * props->DistanceFactor +#endif + ; ctx->mParams.AirAbsorptionGainHF = props->AirAbsorptionGainHF; ctx->mParams.DopplerFactor = props->DopplerFactor; - ctx->mParams.SpeedOfSound = props->SpeedOfSound * props->DopplerVelocity; + ctx->mParams.SpeedOfSound = props->SpeedOfSound * props->DopplerVelocity +#if ALSOFT_EAX + / props->DistanceFactor +#endif + ; ctx->mParams.SourceDistanceModel = props->SourceDistanceModel; ctx->mParams.mDistanceModel = props->mDistanceModel; @@ -451,23 +474,27 @@ bool CalcEffectSlotParams(EffectSlot *slot, EffectSlot **sorted_slots, ContextBa slot->Target = props->Target; slot->EffectType = props->Type; slot->mEffectProps = props->Props; - if(props->Type == EffectSlotType::Reverb || props->Type == EffectSlotType::EAXReverb) - { - slot->RoomRolloff = props->Props.Reverb.RoomRolloffFactor; - slot->DecayTime = props->Props.Reverb.DecayTime; - slot->DecayLFRatio = props->Props.Reverb.DecayLFRatio; - slot->DecayHFRatio = props->Props.Reverb.DecayHFRatio; - slot->DecayHFLimit = props->Props.Reverb.DecayHFLimit; - slot->AirAbsorptionGainHF = props->Props.Reverb.AirAbsorptionGainHF; - } - else + + slot->RoomRolloff = 0.0f; + slot->DecayTime = 0.0f; + slot->DecayLFRatio = 0.0f; + slot->DecayHFRatio = 0.0f; + slot->DecayHFLimit = false; + slot->AirAbsorptionGainHF = 1.0f; + if(auto *reverbprops = std::get_if(&props->Props)) { - slot->RoomRolloff = 0.0f; - slot->DecayTime = 0.0f; - slot->DecayLFRatio = 0.0f; - slot->DecayHFRatio = 0.0f; - slot->DecayHFLimit = false; - slot->AirAbsorptionGainHF = 1.0f; + slot->RoomRolloff = reverbprops->RoomRolloffFactor; + slot->AirAbsorptionGainHF = reverbprops->AirAbsorptionGainHF; + /* If this effect slot's Auxiliary Send Auto is off, don't apply the + * automatic send adjustments based on source distance. + */ + if(slot->AuxSendAuto) + { + slot->DecayTime = reverbprops->DecayTime; + slot->DecayLFRatio = reverbprops->DecayLFRatio; + slot->DecayHFRatio = reverbprops->DecayHFRatio; + slot->DecayHFLimit = reverbprops->DecayHFLimit; + } } EffectState *state{props->State.release()}; @@ -482,9 +509,9 @@ bool CalcEffectSlotParams(EffectSlot *slot, EffectSlot **sorted_slots, ContextBa /* Otherwise, if it would be deleted send it off with a release event. */ RingBuffer *ring{context->mAsyncEvents.get()}; auto evt_vec = ring->getWriteVector(); - if(evt_vec.first.len > 0) LIKELY + if(evt_vec[0].len > 0) LIKELY { - auto &evt = InitAsyncEvent(evt_vec.first.buf); + auto &evt = InitAsyncEvent(evt_vec[0].buf); evt.mEffectState = oldstate; ring->writeAdvance(1); } @@ -499,16 +526,15 @@ bool CalcEffectSlotParams(EffectSlot *slot, EffectSlot **sorted_slots, ContextBa } } - AtomicReplaceHead(context->mFreeEffectslotProps, props); + AtomicReplaceHead(context->mFreeEffectSlotProps, props); - EffectTarget output; - if(EffectSlot *target{slot->Target}) - output = EffectTarget{&target->Wet, nullptr}; - else + const auto output = [slot,context]() -> EffectTarget { + if(EffectSlot *target{slot->Target}) + return EffectTarget{&target->Wet, nullptr}; DeviceBase *device{context->mDevice}; - output = EffectTarget{&device->Dry, &device->RealOut}; - } + return EffectTarget{&device->Dry, &device->RealOut}; + }(); state->update(context, slot, &slot->mEffectProps, output); return true; } @@ -603,19 +629,18 @@ inline std::array ScaleAzimuthFront3_2(std::array pos) * precomputed since they're constant. The second-order coefficients are * followed by the third-order coefficients, etc. */ -template -constexpr size_t CalcRotatorSize() -{ return (L*2 + 1)*(L*2 + 1) + CalcRotatorSize(); } - -template<> constexpr size_t CalcRotatorSize<0>() = delete; -template<> constexpr size_t CalcRotatorSize<1>() = delete; -template<> constexpr size_t CalcRotatorSize<2>() { return 5*5; } +constexpr size_t CalcRotatorSize(size_t l) noexcept +{ + if(l >= 2) + return (l*2 + 1)*(l*2 + 1) + CalcRotatorSize(l-1); + return 0; +} struct RotatorCoeffs { struct CoeffValues { float u, v, w; }; - std::array()> mCoeffs{}; + std::array mCoeffs{}; RotatorCoeffs() { @@ -627,17 +652,38 @@ struct RotatorCoeffs { { for(int m{-l};m <= l;++m) { - // compute u,v,w terms of Eq.8.1 (Table I) - const bool d{m == 0}; // the delta function d_m0 - const float denom{static_cast((std::abs(n) == l) ? - (2*l) * (2*l - 1) : (l*l - n*n))}; - - const int abs_m{std::abs(m)}; - coeffs->u = std::sqrt(static_cast(l*l - m*m)/denom); - coeffs->v = std::sqrt(static_cast(l+abs_m-1) * - static_cast(l+abs_m) / denom) * (1.0f+d) * (1.0f - 2.0f*d) * 0.5f; - coeffs->w = std::sqrt(static_cast(l-abs_m-1) * - static_cast(l-abs_m) / denom) * (1.0f-d) * -0.5f; + /* compute u,v,w terms of Eq.8.1 (Table I) + * + * const bool d{m == 0}; // the delta function d_m0 + * const double denom{(std::abs(n) == l) ? + * (2*l) * (2*l - 1) : (l*l - n*n)}; + * + * const int abs_m{std::abs(m)}; + * coeffs->u = std::sqrt((l*l - m*m) / denom); + * coeffs->v = std::sqrt((l+abs_m-1) * (l+abs_m) / denom) * + * (1.0+d) * (1.0 - 2.0*d) * 0.5; + * coeffs->w = std::sqrt((l-abs_m-1) * (l-abs_m) / denom) * + * (1.0-d) * -0.5; + */ + + const double denom{static_cast((std::abs(n) == l) ? + (2*l) * (2*l - 1) : (l*l - n*n))}; + + if(m == 0) + { + coeffs->u = static_cast(std::sqrt(l * l / denom)); + coeffs->v = static_cast(std::sqrt((l-1) * l / denom) * -1.0); + coeffs->w = 0.0f; + } + else + { + const int abs_m{std::abs(m)}; + coeffs->u = static_cast(std::sqrt((l*l - m*m) / denom)); + coeffs->v = static_cast(std::sqrt((l+abs_m-1) * (l+abs_m) / denom) * + 0.5); + coeffs->w = static_cast(std::sqrt((l-abs_m-1) * (l-abs_m) / denom) * + -0.5); + } ++coeffs; } } @@ -656,27 +702,27 @@ void AmbiRotator(AmbiRotateMatrix &matrix, const int order) /* Don't do anything for < 2nd order. */ if(order < 2) return; - auto P = [](const int i, const int l, const int a, const int n, const size_t last_band, - const AmbiRotateMatrix &R) + static constexpr auto P = [](const int i, const int l, const int a, const int n, + const size_t last_band, const AmbiRotateMatrix &R) { - const float ri1{ R[ 1+2][static_cast(i+2)]}; - const float rim1{R[-1+2][static_cast(i+2)]}; - const float ri0{ R[ 0+2][static_cast(i+2)]}; + const float ri1{ R[ 1+2][static_cast(i+2_z)]}; + const float rim1{R[-1+2][static_cast(i+2_z)]}; + const float ri0{ R[ 0+2][static_cast(i+2_z)]}; const size_t y{last_band + static_cast(a+l-1)}; if(n == -l) - return ri1*R[last_band][y] + rim1*R[last_band + static_cast(l-1)*2][y]; + return ri1*R[last_band][y] + rim1*R[last_band + static_cast(l-1_z)*2][y]; if(n == l) - return ri1*R[last_band + static_cast(l-1)*2][y] - rim1*R[last_band][y]; - return ri0*R[last_band + static_cast(n+l-1)][y]; + return ri1*R[last_band + static_cast(l-1_z)*2][y] - rim1*R[last_band][y]; + return ri0*R[last_band + static_cast(l-1_z+n)][y]; }; - auto U = [P](const int l, const int m, const int n, const size_t last_band, + static constexpr auto U = [](const int l, const int m, const int n, const size_t last_band, const AmbiRotateMatrix &R) { return P(0, l, m, n, last_band, R); }; - auto V = [P](const int l, const int m, const int n, const size_t last_band, + static constexpr auto V = [](const int l, const int m, const int n, const size_t last_band, const AmbiRotateMatrix &R) { using namespace al::numbers; @@ -692,7 +738,7 @@ void AmbiRotator(AmbiRotateMatrix &matrix, const int order) const float p1{P(-1, l, -m-1, n, last_band, R)}; return d ? p1*sqrt2_v : (p0 + p1); }; - auto W = [P](const int l, const int m, const int n, const size_t last_band, + static constexpr auto W = [](const int l, const int m, const int n, const size_t last_band, const AmbiRotateMatrix &R) { assert(m != 0); @@ -721,12 +767,12 @@ void AmbiRotator(AmbiRotateMatrix &matrix, const int order) float r{0.0f}; // computes Eq.8.1 - const float u{coeffs->u}; - if(u != 0.0f) r += u * U(l, m, n, last_band, matrix); - const float v{coeffs->v}; - if(v != 0.0f) r += v * V(l, m, n, last_band, matrix); - const float w{coeffs->w}; - if(w != 0.0f) r += w * W(l, m, n, last_band, matrix); + if(const float u{coeffs->u}; u != 0.0f) + r += u * U(l, m, n, last_band, matrix); + if(const float v{coeffs->v}; v != 0.0f) + r += v * V(l, m, n, last_band, matrix); + if(const float w{coeffs->w}; w != 0.0f) + r += w * W(l, m, n, last_band, matrix); matrix[y][x] = r; ++coeffs; @@ -756,51 +802,57 @@ struct GainTriplet { float Base, HF, LF; }; void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, const float zpos, const float Distance, const float Spread, const GainTriplet &DryGain, - const al::span WetGain, EffectSlot *(&SendSlots)[MAX_SENDS], - const VoiceProps *props, const ContextParams &Context, DeviceBase *Device) + const al::span WetGain, + const al::span SendSlots, const VoiceProps *props, + const ContextParams &Context, DeviceBase *Device) { - static constexpr ChanPosMap MonoMap[1]{ - { FrontCenter, std::array{0.0f, 0.0f, -1.0f} } - }, RearMap[2]{ - { BackLeft, std::array{-sin30, 0.0f, cos30} }, - { BackRight, std::array{ sin30, 0.0f, cos30} }, - }, QuadMap[4]{ - { FrontLeft, std::array{-sin45, 0.0f, -cos45} }, - { FrontRight, std::array{ sin45, 0.0f, -cos45} }, - { BackLeft, std::array{-sin45, 0.0f, cos45} }, - { BackRight, std::array{ sin45, 0.0f, cos45} }, - }, X51Map[6]{ - { FrontLeft, std::array{-sin30, 0.0f, -cos30} }, - { FrontRight, std::array{ sin30, 0.0f, -cos30} }, - { FrontCenter, std::array{ 0.0f, 0.0f, -1.0f} }, - { LFE, {} }, - { SideLeft, std::array{-sin110, 0.0f, -cos110} }, - { SideRight, std::array{ sin110, 0.0f, -cos110} }, - }, X61Map[7]{ - { FrontLeft, std::array{-sin30, 0.0f, -cos30} }, - { FrontRight, std::array{ sin30, 0.0f, -cos30} }, - { FrontCenter, std::array{ 0.0f, 0.0f, -1.0f} }, - { LFE, {} }, - { BackCenter, std::array{ 0.0f, 0.0f, 1.0f} }, - { SideLeft, std::array{-1.0f, 0.0f, 0.0f} }, - { SideRight, std::array{ 1.0f, 0.0f, 0.0f} }, - }, X71Map[8]{ - { FrontLeft, std::array{-sin30, 0.0f, -cos30} }, - { FrontRight, std::array{ sin30, 0.0f, -cos30} }, - { FrontCenter, std::array{ 0.0f, 0.0f, -1.0f} }, - { LFE, {} }, - { BackLeft, std::array{-sin30, 0.0f, cos30} }, - { BackRight, std::array{ sin30, 0.0f, cos30} }, - { SideLeft, std::array{ -1.0f, 0.0f, 0.0f} }, - { SideRight, std::array{ 1.0f, 0.0f, 0.0f} }, + static constexpr std::array MonoMap{ + ChanPosMap{FrontCenter, std::array{0.0f, 0.0f, -1.0f}} + }; + static constexpr std::array RearMap{ + ChanPosMap{BackLeft, std::array{-sin30, 0.0f, cos30}}, + ChanPosMap{BackRight, std::array{ sin30, 0.0f, cos30}}, + }; + static constexpr std::array QuadMap{ + ChanPosMap{FrontLeft, std::array{-sin45, 0.0f, -cos45}}, + ChanPosMap{FrontRight, std::array{ sin45, 0.0f, -cos45}}, + ChanPosMap{BackLeft, std::array{-sin45, 0.0f, cos45}}, + ChanPosMap{BackRight, std::array{ sin45, 0.0f, cos45}}, + }; + static constexpr std::array X51Map{ + ChanPosMap{FrontLeft, std::array{-sin30, 0.0f, -cos30}}, + ChanPosMap{FrontRight, std::array{ sin30, 0.0f, -cos30}}, + ChanPosMap{FrontCenter, std::array{ 0.0f, 0.0f, -1.0f}}, + ChanPosMap{LFE, {}}, + ChanPosMap{SideLeft, std::array{-sin110, 0.0f, -cos110}}, + ChanPosMap{SideRight, std::array{ sin110, 0.0f, -cos110}}, + }; + static constexpr std::array X61Map{ + ChanPosMap{FrontLeft, std::array{-sin30, 0.0f, -cos30}}, + ChanPosMap{FrontRight, std::array{ sin30, 0.0f, -cos30}}, + ChanPosMap{FrontCenter, std::array{ 0.0f, 0.0f, -1.0f}}, + ChanPosMap{LFE, {}}, + ChanPosMap{BackCenter, std::array{ 0.0f, 0.0f, 1.0f}}, + ChanPosMap{SideLeft, std::array{-1.0f, 0.0f, 0.0f}}, + ChanPosMap{SideRight, std::array{ 1.0f, 0.0f, 0.0f}}, + }; + static constexpr std::array X71Map{ + ChanPosMap{FrontLeft, std::array{-sin30, 0.0f, -cos30}}, + ChanPosMap{FrontRight, std::array{ sin30, 0.0f, -cos30}}, + ChanPosMap{FrontCenter, std::array{ 0.0f, 0.0f, -1.0f}}, + ChanPosMap{LFE, {}}, + ChanPosMap{BackLeft, std::array{-sin30, 0.0f, cos30}}, + ChanPosMap{BackRight, std::array{ sin30, 0.0f, cos30}}, + ChanPosMap{SideLeft, std::array{ -1.0f, 0.0f, 0.0f}}, + ChanPosMap{SideRight, std::array{ 1.0f, 0.0f, 0.0f}}, }; - ChanPosMap StereoMap[2]{ - { FrontLeft, std::array{-sin30, 0.0f, -cos30} }, - { FrontRight, std::array{ sin30, 0.0f, -cos30} }, + std::array StereoMap{ + ChanPosMap{FrontLeft, std::array{-sin30, 0.0f, -cos30}}, + ChanPosMap{FrontRight, std::array{ sin30, 0.0f, -cos30}}, }; - const auto Frequency = static_cast(Device->Frequency); + const auto Frequency = static_cast(Device->mSampleRate); const uint NumSends{Device->NumAuxSends}; const size_t num_channels{voice->mChans.size()}; @@ -814,49 +866,84 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con [](SendParams ¶ms) -> void { params.Gains.Target.fill(0.0f); }); } - DirectMode DirectChannels{props->DirectChannels}; - const ChanPosMap *chans{nullptr}; - switch(voice->mFmtChannels) + const auto getChans = [props,&StereoMap](FmtChannels chanfmt) noexcept + -> std::pair> { - case FmtMono: - chans = MonoMap; - /* Mono buffers are never played direct. */ - DirectChannels = DirectMode::Off; - break; - - case FmtStereo: - if(DirectChannels == DirectMode::Off) + switch(chanfmt) { - for(size_t i{0};i < 2;++i) + case FmtMono: + /* Mono buffers are never played direct. */ + return {DirectMode::Off, al::span{MonoMap}}; + + case FmtStereo: + case FmtMonoDup: + if(props->DirectChannels == DirectMode::Off) { - /* StereoPan is counter-clockwise in radians. */ - const float a{props->StereoPan[i]}; - StereoMap[i].pos[0] = -std::sin(a); - StereoMap[i].pos[2] = -std::cos(a); + for(size_t i{0};i < 2;++i) + { + /* StereoPan is counter-clockwise in radians. */ + const float a{props->StereoPan[i]}; + StereoMap[i].pos[0] = -std::sin(a); + StereoMap[i].pos[2] = -std::cos(a); + } } + return {props->DirectChannels, al::span{StereoMap}}; + + case FmtRear: return {props->DirectChannels, al::span{RearMap}}; + case FmtQuad: return {props->DirectChannels, al::span{QuadMap}}; + case FmtX51: return {props->DirectChannels, al::span{X51Map}}; + case FmtX61: return {props->DirectChannels, al::span{X61Map}}; + case FmtX71: return {props->DirectChannels, al::span{X71Map}}; + + case FmtBFormat2D: + case FmtBFormat3D: + case FmtUHJ2: + case FmtUHJ3: + case FmtUHJ4: + case FmtSuperStereo: + return {DirectMode::Off, {}}; } - chans = StereoMap; - break; - - case FmtRear: chans = RearMap; break; - case FmtQuad: chans = QuadMap; break; - case FmtX51: chans = X51Map; break; - case FmtX61: chans = X61Map; break; - case FmtX71: chans = X71Map; break; - - case FmtBFormat2D: - case FmtBFormat3D: - case FmtUHJ2: - case FmtUHJ3: - case FmtUHJ4: - case FmtSuperStereo: - DirectChannels = DirectMode::Off; - break; - } + return {props->DirectChannels, {}}; + }; + const auto [DirectChannels,chans] = getChans(voice->mFmtChannels); voice->mFlags.reset(VoiceHasHrtf).reset(VoiceHasNfc); if(auto *decoder{voice->mDecoder.get()}) - decoder->mWidthControl = minf(props->EnhWidth, 0.7f); + decoder->mWidthControl = std::min(props->EnhWidth, 0.7f); + + const float lgain{std::min(1.0f-props->Panning, 1.0f)}; + const float rgain{std::min(1.0f+props->Panning, 1.0f)}; + const float mingain{std::min(lgain, rgain)}; + auto SelectChannelGain = [lgain,rgain,mingain](const Channel chan) noexcept + { + switch(chan) + { + case FrontLeft: return lgain; + case FrontRight: return rgain; + case FrontCenter: break; + case LFE: break; + case BackLeft: return lgain; + case BackRight: return rgain; + case BackCenter: break; + case SideLeft: return lgain; + case SideRight: return rgain; + case TopCenter: break; + case TopFrontLeft: return lgain; + case TopFrontCenter: break; + case TopFrontRight: return rgain; + case TopBackLeft: return lgain; + case TopBackCenter: break; + case TopBackRight: return rgain; + case BottomFrontLeft: return lgain; + case BottomFrontRight: return rgain; + case BottomBackLeft: return lgain; + case BottomBackRight: return rgain; + case Aux0: case Aux1: case Aux2: case Aux3: case Aux4: case Aux5: case Aux6: case Aux7: + case Aux8: case Aux9: case Aux10: case Aux11: case Aux12: case Aux13: case Aux14: + case Aux15: case MaxChannels: break; + } + return mingain; + }; if(IsAmbisonic(voice->mFmtChannels)) { @@ -878,7 +965,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con /* Clamp the distance for really close sources, to prevent * excessive bass. */ - const float mdist{maxf(Distance*NfcScale, Device->AvgSpeakerDist/4.0f)}; + const float mdist{std::max(Distance*NfcScale, Device->AvgSpeakerDist/4.0f)}; const float w0{SpeedOfSoundMetersPerSec / (mdist * Frequency)}; /* Only need to adjust the first channel of a B-Format source. */ @@ -908,12 +995,12 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con if(!(coverage > 0.0f)) { - ComputePanGains(&Device->Dry, coeffs.data(), DryGain.Base*scales[0], + ComputePanGains(&Device->Dry, coeffs, DryGain.Base*scales[0], voice->mChans[0].mDryParams.Gains.Target); for(uint i{0};i < NumSends;i++) { if(const EffectSlot *Slot{SendSlots[i]}) - ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base*scales[0], + ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base*scales[0], voice->mChans[0].mWetParams[i].Gains.Target); } } @@ -995,9 +1082,9 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con /* Convert the rotation matrix for input ordering and scaling, and * whether input is 2D or 3D. */ - const uint8_t *index_map{Is2DAmbisonic(voice->mFmtChannels) ? - GetAmbi2DLayout(voice->mAmbiLayout).data() : - GetAmbiLayout(voice->mAmbiLayout).data()}; + const auto index_map = Is2DAmbisonic(voice->mFmtChannels) ? + GetAmbi2DLayout(voice->mAmbiLayout).subspan(0) : + GetAmbiLayout(voice->mAmbiLayout).subspan(0); /* Scale the panned W signal inversely to coverage (full coverage * means no panned signal), and according to the channel scaling. @@ -1014,16 +1101,17 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con * to the coverage amount) with the directional pan. For all * other channels, use just the (scaled) B-Format signal. */ - for(size_t x{0};x < MaxAmbiChannels;++x) - coeffs[x] += mixmatrix[acn][x] * scale; + std::transform(mixmatrix[acn].cbegin(), mixmatrix[acn].cend(), coeffs.begin(), + coeffs.begin(), [scale](const float in, const float coeff) noexcept + { return in*scale + coeff; }); - ComputePanGains(&Device->Dry, coeffs.data(), DryGain.Base, + ComputePanGains(&Device->Dry, coeffs, DryGain.Base, voice->mChans[c].mDryParams.Gains.Target); for(uint i{0};i < NumSends;i++) { if(const EffectSlot *Slot{SendSlots[i]}) - ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base, + ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base, voice->mChans[c].mWetParams[i].Gains.Target); } @@ -1040,13 +1128,13 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con for(size_t c{0};c < num_channels;c++) { - uint idx{Device->channelIdxByName(chans[c].channel)}; - if(idx != InvalidChannelIndex) - voice->mChans[c].mDryParams.Gains.Target[idx] = DryGain.Base; + const float pangain{SelectChannelGain(chans[c].channel)}; + if(uint idx{Device->channelIdxByName(chans[c].channel)}; idx != InvalidChannelIndex) + voice->mChans[c].mDryParams.Gains.Target[idx] = DryGain.Base * pangain; else if(DirectChannels == DirectMode::RemixMismatch) { - auto match_channel = [chans,c](const InputRemixMap &map) noexcept -> bool - { return chans[c].channel == map.channel; }; + auto match_channel = [channel=chans[c].channel](const InputRemixMap &map) noexcept + { return channel == map.channel; }; auto remap = std::find_if(Device->RealOut.RemixMap.cbegin(), Device->RealOut.RemixMap.cend(), match_channel); if(remap != Device->RealOut.RemixMap.cend()) @@ -1055,8 +1143,8 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con { idx = Device->channelIdxByName(target.channel); if(idx != InvalidChannelIndex) - voice->mChans[c].mDryParams.Gains.Target[idx] = DryGain.Base * - target.mix; + voice->mChans[c].mDryParams.Gains.Target[idx] = DryGain.Base * pangain + * target.mix; } } } @@ -1071,12 +1159,13 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con if(chans[c].channel == LFE) continue; + const float pangain{SelectChannelGain(chans[c].channel)}; const auto coeffs = CalcDirectionCoeffs(chans[c].pos, 0.0f); for(uint i{0};i < NumSends;i++) { if(const EffectSlot *Slot{SendSlots[i]}) - ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base, + ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base * pangain, voice->mChans[c].mWetParams[i].Gains.Target); } } @@ -1092,7 +1181,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con { if(voice->mFmtChannels == FmtMono) { - const float src_ev{std::asin(clampf(ypos, -1.0f, 1.0f))}; + const float src_ev{std::asin(std::clamp(ypos, -1.0f, 1.0f))}; const float src_az{std::atan2(xpos, -zpos)}; Device->mHrtf->getCoeffs(src_ev, src_az, Distance*NfcScale, Spread, @@ -1104,23 +1193,22 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con for(uint i{0};i < NumSends;i++) { if(const EffectSlot *Slot{SendSlots[i]}) - ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base, + ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base, voice->mChans[0].mWetParams[i].Gains.Target); } } else for(size_t c{0};c < num_channels;c++) { - using namespace al::numbers; - /* Skip LFE */ if(chans[c].channel == LFE) continue; + const float pangain{SelectChannelGain(chans[c].channel)}; /* Warp the channel position toward the source position as the * source spread decreases. With no spread, all channels are at * the source position, at full spread (pi*2), each channel is * left unchanged. */ - const float a{1.0f - (inv_pi_v/2.0f)*Spread}; + const float a{1.0f - (al::numbers::inv_pi_v/2.0f)*Spread}; std::array pos{ lerpf(chans[c].pos[0], xpos, a), lerpf(chans[c].pos[1], ypos, a), @@ -1133,19 +1221,19 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con pos[2] /= len; } - const float ev{std::asin(clampf(pos[1], -1.0f, 1.0f))}; + const float ev{std::asin(std::clamp(pos[1], -1.0f, 1.0f))}; const float az{std::atan2(pos[0], -pos[2])}; Device->mHrtf->getCoeffs(ev, az, Distance*NfcScale, 0.0f, voice->mChans[c].mDryParams.Hrtf.Target.Coeffs, voice->mChans[c].mDryParams.Hrtf.Target.Delay); - voice->mChans[c].mDryParams.Hrtf.Target.Gain = DryGain.Base; + voice->mChans[c].mDryParams.Hrtf.Target.Gain = DryGain.Base * pangain; const auto coeffs = CalcDirectionCoeffs(pos, 0.0f); for(uint i{0};i < NumSends;i++) { if(const EffectSlot *Slot{SendSlots[i]}) - ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base, + ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base * pangain, voice->mChans[c].mWetParams[i].Gains.Target); } } @@ -1156,7 +1244,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con * where it can be 0 or full (non-mono sources are always full * spread here). */ - const float spread{Spread * (voice->mFmtChannels == FmtMono)}; + const float spread{Spread * float(voice->mFmtChannels == FmtMono)}; /* Local sources on HRTF play with each channel panned to its * relative location around the listener, providing "virtual @@ -1167,6 +1255,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con /* Skip LFE */ if(chans[c].channel == LFE) continue; + const float pangain{SelectChannelGain(chans[c].channel)}; /* Get the HRIR coefficients and delays for this channel * position. @@ -1177,7 +1266,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con Device->mHrtf->getCoeffs(ev, az, std::numeric_limits::infinity(), spread, voice->mChans[c].mDryParams.Hrtf.Target.Coeffs, voice->mChans[c].mDryParams.Hrtf.Target.Delay); - voice->mChans[c].mDryParams.Hrtf.Target.Gain = DryGain.Base; + voice->mChans[c].mDryParams.Hrtf.Target.Gain = DryGain.Base * pangain; /* Normal panning for auxiliary sends. */ const auto coeffs = CalcDirectionCoeffs(chans[c].pos, spread); @@ -1185,7 +1274,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con for(uint i{0};i < NumSends;i++) { if(const EffectSlot *Slot{SendSlots[i]}) - ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base, + ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base * pangain, voice->mChans[c].mWetParams[i].Gains.Target); } } @@ -1205,7 +1294,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con /* Clamp the distance for really close sources, to prevent * excessive bass. */ - const float mdist{maxf(Distance*NfcScale, Device->AvgSpeakerDist/4.0f)}; + const float mdist{std::max(Distance*NfcScale, Device->AvgSpeakerDist/4.0f)}; const float w0{SpeedOfSoundMetersPerSec / (mdist * Frequency)}; /* Adjust NFC filters. */ @@ -1226,63 +1315,60 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con }; const auto coeffs = calc_coeffs(Device->mRenderMode); - ComputePanGains(&Device->Dry, coeffs.data(), DryGain.Base, + ComputePanGains(&Device->Dry, coeffs, DryGain.Base, voice->mChans[0].mDryParams.Gains.Target); for(uint i{0};i < NumSends;i++) { if(const EffectSlot *Slot{SendSlots[i]}) - ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base, + ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base, voice->mChans[0].mWetParams[i].Gains.Target); } } - else + else for(size_t c{0};c < num_channels;c++) { - using namespace al::numbers; + const auto pangain = SelectChannelGain(chans[c].channel); - for(size_t c{0};c < num_channels;c++) + /* Special-case LFE */ + if(chans[c].channel == LFE) { - /* Special-case LFE */ - if(chans[c].channel == LFE) + if(Device->Dry.Buffer.data() == Device->RealOut.Buffer.data()) { - if(Device->Dry.Buffer.data() == Device->RealOut.Buffer.data()) - { - const uint idx{Device->channelIdxByName(chans[c].channel)}; - if(idx != InvalidChannelIndex) - voice->mChans[c].mDryParams.Gains.Target[idx] = DryGain.Base; - } - continue; + const auto idx = uint{Device->channelIdxByName(chans[c].channel)}; + if(idx != InvalidChannelIndex) + voice->mChans[c].mDryParams.Gains.Target[idx] = DryGain.Base * pangain; } + continue; + } - /* Warp the channel position toward the source position as - * the spread decreases. With no spread, all channels are - * at the source position, at full spread (pi*2), each - * channel position is left unchanged. - */ - const float a{1.0f - (inv_pi_v/2.0f)*Spread}; - std::array pos{ - lerpf(chans[c].pos[0], xpos, a), - lerpf(chans[c].pos[1], ypos, a), - lerpf(chans[c].pos[2], zpos, a)}; - const float len{std::sqrt(pos[0]*pos[0] + pos[1]*pos[1] + pos[2]*pos[2])}; - if(len < 1.0f) - { - pos[0] /= len; - pos[1] /= len; - pos[2] /= len; - } + /* Warp the channel position toward the source position as the + * spread decreases. With no spread, all channels are at the + * source position, at full spread (pi*2), each channel + * position is left unchanged. + */ + const auto a = 1.0f - (al::numbers::inv_pi_v/2.0f)*Spread; + auto pos = std::array{ + lerpf(chans[c].pos[0], xpos, a), + lerpf(chans[c].pos[1], ypos, a), + lerpf(chans[c].pos[2], zpos, a)}; + const auto len = std::sqrt(pos[0]*pos[0] + pos[1]*pos[1] + pos[2]*pos[2]); + if(len < 1.0f) + { + pos[0] /= len; + pos[1] /= len; + pos[2] /= len; + } - if(Device->mRenderMode == RenderMode::Pairwise) - pos = ScaleAzimuthFront3(pos); - const auto coeffs = CalcDirectionCoeffs(pos, 0.0f); + if(Device->mRenderMode == RenderMode::Pairwise) + pos = ScaleAzimuthFront3(pos); + const auto coeffs = CalcDirectionCoeffs(pos, 0.0f); - ComputePanGains(&Device->Dry, coeffs.data(), DryGain.Base, - voice->mChans[c].mDryParams.Gains.Target); - for(uint i{0};i < NumSends;i++) - { - if(const EffectSlot *Slot{SendSlots[i]}) - ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base, - voice->mChans[c].mWetParams[i].Gains.Target); - } + ComputePanGains(&Device->Dry, coeffs, DryGain.Base * pangain, + voice->mChans[c].mDryParams.Gains.Target); + for(uint i{0};i < NumSends;i++) + { + if(const EffectSlot *Slot{SendSlots[i]}) + ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base * pangain, + voice->mChans[c].mWetParams[i].Gains.Target); } } } @@ -1304,9 +1390,11 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con * where it can be 0 or full (non-mono sources are always full * spread here). */ - const float spread{Spread * (voice->mFmtChannels == FmtMono)}; + const float spread{Spread * float(voice->mFmtChannels == FmtMono)}; for(size_t c{0};c < num_channels;c++) { + const float pangain{SelectChannelGain(chans[c].channel)}; + /* Special-case LFE */ if(chans[c].channel == LFE) { @@ -1314,7 +1402,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con { const uint idx{Device->channelIdxByName(chans[c].channel)}; if(idx != InvalidChannelIndex) - voice->mChans[c].mDryParams.Gains.Target[idx] = DryGain.Base; + voice->mChans[c].mDryParams.Gains.Target[idx] = DryGain.Base * pangain; } continue; } @@ -1322,12 +1410,12 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con const auto coeffs = CalcDirectionCoeffs((Device->mRenderMode==RenderMode::Pairwise) ? ScaleAzimuthFront3(chans[c].pos) : chans[c].pos, spread); - ComputePanGains(&Device->Dry, coeffs.data(), DryGain.Base, + ComputePanGains(&Device->Dry, coeffs, DryGain.Base * pangain, voice->mChans[c].mDryParams.Gains.Target); for(uint i{0};i < NumSends;i++) { if(const EffectSlot *Slot{SendSlots[i]}) - ComputePanGains(&Slot->Wet, coeffs.data(), WetGain[i].Base, + ComputePanGains(&Slot->Wet, coeffs, WetGain[i].Base * pangain, voice->mChans[c].mWetParams[i].Gains.Target); } } @@ -1376,7 +1464,7 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con void CalcNonAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBase *context) { DeviceBase *Device{context->mDevice}; - EffectSlot *SendSlots[MAX_SENDS]; + std::array SendSlots{}; voice->mDirect.Buffer = Device->Dry.Buffer; for(uint i{0};i < Device->NumAuxSends;i++) @@ -1393,23 +1481,24 @@ void CalcNonAttnSourceParams(Voice *voice, const VoiceProps *props, const Contex /* Calculate the stepping value */ const auto Pitch = static_cast(voice->mFrequency) / - static_cast(Device->Frequency) * props->Pitch; + static_cast(Device->mSampleRate) * props->Pitch; if(Pitch > float{MaxPitch}) voice->mStep = MaxPitch<mStep = maxu(fastf2u(Pitch * MixerFracOne), 1); + voice->mStep = std::max(fastf2u(Pitch * MixerFracOne), 1u); voice->mResampler = PrepareResampler(props->mResampler, voice->mStep, &voice->mResampleState); /* Calculate gains */ - GainTriplet DryGain; - DryGain.Base = minf(clampf(props->Gain, props->MinGain, props->MaxGain) * props->Direct.Gain * - context->mParams.Gain, GainMixMax); + GainTriplet DryGain{}; + DryGain.Base = std::min(std::clamp(props->Gain, props->MinGain, props->MaxGain) * + props->Direct.Gain * context->mParams.Gain, GainMixMax); DryGain.HF = props->Direct.GainHF; DryGain.LF = props->Direct.GainLF; - GainTriplet WetGain[MAX_SENDS]; + + std::array WetGain{}; for(uint i{0};i < Device->NumAuxSends;i++) { - WetGain[i].Base = minf(clampf(props->Gain, props->MinGain, props->MaxGain) * + WetGain[i].Base = std::min(std::clamp(props->Gain, props->MinGain, props->MaxGain) * props->Send[i].Gain * context->mParams.Gain, GainMixMax); WetGain[i].HF = props->Send[i].GainHF; WetGain[i].LF = props->Send[i].GainLF; @@ -1426,25 +1515,26 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa /* Set mixing buffers and get send parameters. */ voice->mDirect.Buffer = Device->Dry.Buffer; - EffectSlot *SendSlots[MAX_SENDS]; - uint UseDryAttnForRoom{0}; + std::array SendSlots{}; + std::array RoomRolloff{}; for(uint i{0};i < NumSends;i++) { SendSlots[i] = props->Send[i].Slot; if(!SendSlots[i] || SendSlots[i]->EffectType == EffectSlotType::None) + { SendSlots[i] = nullptr; - else if(!SendSlots[i]->AuxSendAuto) + voice->mSend[i].Buffer = {}; + } + else { - /* If the slot's auxiliary send auto is off, the data sent to the - * effect slot is the same as the dry path, sans filter effects. + /* NOTE: Contrary to the EFX docs, the effect's room rolloff factor + * applies to the selected distance model along with the source's + * room rolloff factor, not necessarily the inverse distance model. */ - UseDryAttnForRoom |= 1u<RoomRolloffFactor + SendSlots[i]->RoomRolloff; - if(!SendSlots[i]) - voice->mSend[i].Buffer = {}; - else voice->mSend[i].Buffer = SendSlots[i]->Wet.Buffer; + } } /* Transform source to listener space (convert to head relative) */ @@ -1471,62 +1561,77 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa /* Calculate distance attenuation */ float ClampedDist{Distance}; float DryGainBase{props->Gain}; - float WetGainBase{props->Gain}; + std::array WetGainBase{}; + WetGainBase.fill(props->Gain); + float DryAttnBase{1.0f}; switch(context->mParams.SourceDistanceModel ? props->mDistanceModel : context->mParams.mDistanceModel) { - case DistanceModel::InverseClamped: - if(props->MaxDistance < props->RefDistance) break; - ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance); - /*fall-through*/ - case DistanceModel::Inverse: - if(props->RefDistance > 0.0f) + case DistanceModel::InverseClamped: + if(props->MaxDistance < props->RefDistance) break; + ClampedDist = std::clamp(ClampedDist, props->RefDistance, props->MaxDistance); + /*fall-through*/ + case DistanceModel::Inverse: + if(props->RefDistance > 0.0f) + { + float dist{lerpf(props->RefDistance, ClampedDist, props->RolloffFactor)}; + if(dist > 0.0f) { - float dist{lerpf(props->RefDistance, ClampedDist, props->RolloffFactor)}; - if(dist > 0.0f) DryGainBase *= props->RefDistance / dist; - - dist = lerpf(props->RefDistance, ClampedDist, props->RoomRolloffFactor); - if(dist > 0.0f) WetGainBase *= props->RefDistance / dist; + DryAttnBase = props->RefDistance / dist; + DryGainBase *= DryAttnBase; } - break; - - case DistanceModel::LinearClamped: - if(props->MaxDistance < props->RefDistance) break; - ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance); - /*fall-through*/ - case DistanceModel::Linear: - if(props->MaxDistance != props->RefDistance) - { - float attn{(ClampedDist-props->RefDistance) / - (props->MaxDistance-props->RefDistance) * props->RolloffFactor}; - DryGainBase *= maxf(1.0f - attn, 0.0f); - attn = (ClampedDist-props->RefDistance) / - (props->MaxDistance-props->RefDistance) * props->RoomRolloffFactor; - WetGainBase *= maxf(1.0f - attn, 0.0f); + for(size_t i{0};i < NumSends;++i) + { + dist = lerpf(props->RefDistance, ClampedDist, RoomRolloff[i]); + if(dist > 0.0f) WetGainBase[i] *= props->RefDistance / dist; } - break; - - case DistanceModel::ExponentClamped: - if(props->MaxDistance < props->RefDistance) break; - ClampedDist = clampf(ClampedDist, props->RefDistance, props->MaxDistance); - /*fall-through*/ - case DistanceModel::Exponent: - if(ClampedDist > 0.0f && props->RefDistance > 0.0f) + } + break; + + case DistanceModel::LinearClamped: + if(props->MaxDistance < props->RefDistance) break; + ClampedDist = std::clamp(ClampedDist, props->RefDistance, props->MaxDistance); + /*fall-through*/ + case DistanceModel::Linear: + if(props->MaxDistance != props->RefDistance) + { + float attn{(ClampedDist-props->RefDistance) / + (props->MaxDistance-props->RefDistance) * props->RolloffFactor}; + DryAttnBase = std::max(1.0f - attn, 0.0f); + DryGainBase *= DryAttnBase; + + for(size_t i{0};i < NumSends;++i) { - const float dist_ratio{ClampedDist/props->RefDistance}; - DryGainBase *= std::pow(dist_ratio, -props->RolloffFactor); - WetGainBase *= std::pow(dist_ratio, -props->RoomRolloffFactor); + attn = (ClampedDist-props->RefDistance) / + (props->MaxDistance-props->RefDistance) * RoomRolloff[i]; + WetGainBase[i] *= std::max(1.0f - attn, 0.0f); } - break; + } + break; - case DistanceModel::Disable: - break; + case DistanceModel::ExponentClamped: + if(props->MaxDistance < props->RefDistance) break; + ClampedDist = std::clamp(ClampedDist, props->RefDistance, props->MaxDistance); + /*fall-through*/ + case DistanceModel::Exponent: + if(ClampedDist > 0.0f && props->RefDistance > 0.0f) + { + const float dist_ratio{ClampedDist/props->RefDistance}; + DryAttnBase = std::pow(dist_ratio, -props->RolloffFactor); + DryGainBase *= DryAttnBase; + for(size_t i{0};i < NumSends;++i) + WetGainBase[i] *= std::pow(dist_ratio, -RoomRolloff[i]); + } + break; + + case DistanceModel::Disable: + break; } /* Calculate directional soundcones */ - float ConeHF{1.0f}, WetConeHF{1.0f}; + float ConeHF{1.0f}, WetCone{1.0f}, WetConeHF{1.0f}; if(directional && props->InnerAngle < 360.0f) { static constexpr float Rad2Deg{static_cast(180.0 / al::numbers::pi)}; @@ -1536,52 +1641,62 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa if(Angle >= props->OuterAngle) { ConeGain = props->OuterGain; - ConeHF = lerpf(1.0f, props->OuterGainHF, props->DryGainHFAuto); + if(props->DryGainHFAuto) + ConeHF = props->OuterGainHF; } else if(Angle >= props->InnerAngle) { const float scale{(Angle-props->InnerAngle) / (props->OuterAngle-props->InnerAngle)}; ConeGain = lerpf(1.0f, props->OuterGain, scale); - ConeHF = lerpf(1.0f, props->OuterGainHF, scale * props->DryGainHFAuto); + if(props->DryGainHFAuto) + ConeHF = lerpf(1.0f, props->OuterGainHF, scale); } DryGainBase *= ConeGain; - WetGainBase *= lerpf(1.0f, ConeGain, props->WetGainAuto); - - WetConeHF = lerpf(1.0f, ConeHF, props->WetGainHFAuto); + if(props->WetGainAuto) + WetCone = ConeGain; + if(props->WetGainHFAuto) + WetConeHF = ConeHF; } /* Apply gain and frequency filters */ - DryGainBase = clampf(DryGainBase, props->MinGain, props->MaxGain) * context->mParams.Gain; - WetGainBase = clampf(WetGainBase, props->MinGain, props->MaxGain) * context->mParams.Gain; - GainTriplet DryGain{}; - DryGain.Base = minf(DryGainBase * props->Direct.Gain, GainMixMax); + DryGainBase = std::clamp(DryGainBase, props->MinGain, props->MaxGain) * context->mParams.Gain; + DryGain.Base = std::min(DryGainBase * props->Direct.Gain, GainMixMax); DryGain.HF = ConeHF * props->Direct.GainHF; DryGain.LF = props->Direct.GainLF; - GainTriplet WetGain[MAX_SENDS]{}; + + std::array WetGain{}; for(uint i{0};i < NumSends;i++) { - /* If this effect slot's Auxiliary Send Auto is off, then use the dry - * path distance and cone attenuation, otherwise use the wet (room) - * path distance and cone attenuation. The send filter is used instead - * of the direct filter, regardless. - */ - const bool use_room{!(UseDryAttnForRoom&(1u<Send[i].Gain, GainMixMax); - WetGain[i].HF = (use_room ? WetConeHF : ConeHF) * props->Send[i].GainHF; + const auto gain = std::clamp(WetGainBase[i]*WetCone, props->MinGain, props->MaxGain) * + context->mParams.Gain; + WetGain[i].Base = std::min(gain * props->Send[i].Gain, GainMixMax); + WetGain[i].HF = WetConeHF * props->Send[i].GainHF; WetGain[i].LF = props->Send[i].GainLF; } /* Distance-based air absorption and initial send decay. */ if(Distance > props->RefDistance) LIKELY { - const float distance_base{(Distance-props->RefDistance) * props->RolloffFactor}; - const float distance_meters{distance_base * context->mParams.MetersPerUnit}; - const float dryabsorb{distance_meters * props->AirAbsorptionFactor}; - if(dryabsorb > std::numeric_limits::epsilon()) - DryGain.HF *= std::pow(context->mParams.AirAbsorptionGainHF, dryabsorb); + /* FIXME: In keeping with EAX, the base air absorption gain should be + * taken from the reverb property in the "primary fx slot" when it has + * a reverb effect and the environment flag set, and be applied to the + * direct path and all environment sends, rather than each path using + * the air absorption gain associated with the given slot's effect. At + * this point in the mixer, and even in EFX itself, there's no concept + * of a "primary fx slot" so it's unclear which effect slot should be + * checked. + * + * The HF reference is also intended to be handled the same way, but + * again, there's no concept of a "primary fx slot" here and no way to + * know which effect slot to look at for the reference frequency. + */ + const auto distance_units = float{(Distance-props->RefDistance) * props->RolloffFactor}; + const auto distance_meters = float{distance_units * context->mParams.MetersPerUnit}; + const auto absorb = float{distance_meters * props->AirAbsorptionFactor}; + if(absorb > std::numeric_limits::epsilon()) + DryGain.HF *= std::pow(context->mParams.AirAbsorptionGainHF, absorb); /* If the source's Auxiliary Send Filter Gain Auto is off, no extra * adjustment is applied to the send gains. @@ -1591,72 +1706,25 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa if(!SendSlots[i] || !(SendSlots[i]->DecayTime > 0.0f)) continue; - auto calc_attenuation = [](float distance, float refdist, float rolloff) noexcept - { - const float dist{lerpf(refdist, distance, rolloff)}; - if(dist > refdist) return refdist / dist; - return 1.0f; - }; - - /* The reverb effect's room rolloff factor always applies to an - * inverse distance rolloff model. - */ - WetGain[i].Base *= calc_attenuation(Distance, props->RefDistance, - SendSlots[i]->RoomRolloff); - - if(distance_meters > std::numeric_limits::epsilon()) - WetGain[i].HF *= std::pow(SendSlots[i]->AirAbsorptionGainHF, distance_meters); - - /* If this effect slot's Auxiliary Send Auto is off, don't apply - * the automatic initial reverb decay (should the reverb's room - * rolloff still apply?). - */ - if(!SendSlots[i]->AuxSendAuto) - continue; - - GainTriplet DecayDistance; - /* Calculate the distances to where this effect's decay reaches - * -60dB. - */ - DecayDistance.Base = SendSlots[i]->DecayTime * SpeedOfSoundMetersPerSec; - DecayDistance.LF = DecayDistance.Base * SendSlots[i]->DecayLFRatio; - DecayDistance.HF = DecayDistance.Base * SendSlots[i]->DecayHFRatio; - if(SendSlots[i]->DecayHFLimit) - { - const float airAbsorption{SendSlots[i]->AirAbsorptionGainHF}; - if(airAbsorption < 1.0f) - { - /* Calculate the distance to where this effect's air - * absorption reaches -60dB, and limit the effect's HF - * decay distance (so it doesn't take any longer to decay - * than the air would allow). - */ - static constexpr float log10_decaygain{-3.0f/*std::log10(ReverbDecayGain)*/}; - const float absorb_dist{log10_decaygain / std::log10(airAbsorption)}; - DecayDistance.HF = minf(absorb_dist, DecayDistance.HF); - } - } + if(SendSlots[i]->AirAbsorptionGainHF < 1.0f + && absorb > std::numeric_limits::epsilon()) + WetGain[i].HF *= std::pow(SendSlots[i]->AirAbsorptionGainHF, absorb); - const float baseAttn = calc_attenuation(Distance, props->RefDistance, - props->RolloffFactor); + const float DecayDistance{SendSlots[i]->DecayTime * SpeedOfSoundMetersPerSec}; /* Apply a decay-time transformation to the wet path, based on the * source distance. The initial decay of the reverb effect is * calculated and applied to the wet path. + * + * FIXME: This is very likely not correct. It more likely should + * work by calculating a rolloff dynamically based on the reverb + * parameters (and source distance?) and add it to the room rolloff + * with the reverb and source rolloff parameters. */ - const float fact{distance_base / DecayDistance.Base}; + const float baseAttn{DryAttnBase}; + const float fact{distance_meters / DecayDistance}; const float gain{std::pow(ReverbDecayGain, fact)*(1.0f-baseAttn) + baseAttn}; WetGain[i].Base *= gain; - - if(gain > 0.0f) - { - const float hffact{distance_base / DecayDistance.HF}; - const float gainhf{std::pow(ReverbDecayGain, hffact)*(1.0f-baseAttn) + baseAttn}; - WetGain[i].HF *= minf(gainhf/gain, 1.0f); - const float lffact{distance_base / DecayDistance.LF}; - const float gainlf{std::pow(ReverbDecayGain, lffact)*(1.0f-baseAttn) + baseAttn}; - WetGain[i].LF *= minf(gainlf/gain, 1.0f); - } } } @@ -1699,11 +1767,11 @@ void CalcAttnSourceParams(Voice *voice, const VoiceProps *props, const ContextBa /* Adjust pitch based on the buffer and output frequencies, and calculate * fixed-point stepping value. */ - Pitch *= static_cast(voice->mFrequency) / static_cast(Device->Frequency); + Pitch *= static_cast(voice->mFrequency) / static_cast(Device->mSampleRate); if(Pitch > float{MaxPitch}) voice->mStep = MaxPitch<mStep = maxu(fastf2u(Pitch * MixerFracOne), 1); + voice->mStep = std::max(fastf2u(Pitch * MixerFracOne), 1u); voice->mResampler = PrepareResampler(props->mResampler, voice->mStep, &voice->mResampleState); float spread{0.0f}; @@ -1723,7 +1791,7 @@ void CalcSourceParams(Voice *voice, ContextBase *context, bool force) if(props) { - voice->mProps = *props; + voice->mProps = static_cast(*props); AtomicReplaceHead(context->mFreeVoiceProps, props); } @@ -1742,9 +1810,9 @@ void SendSourceStateEvent(ContextBase *context, uint id, VChangeState state) { RingBuffer *ring{context->mAsyncEvents.get()}; auto evt_vec = ring->getWriteVector(); - if(evt_vec.first.len < 1) return; + if(evt_vec[0].len < 1) return; - auto &evt = InitAsyncEvent(evt_vec.first.buf); + auto &evt = InitAsyncEvent(evt_vec[0].buf); evt.mId = id; switch(state) { @@ -1863,8 +1931,8 @@ void ProcessVoiceChanges(ContextBase *ctx) ctx->mCurrentVoiceChange.store(cur, std::memory_order_release); } -void ProcessParamUpdates(ContextBase *ctx, const EffectSlotArray &slots, - const al::span voices) +void ProcessParamUpdates(ContextBase *ctx, const al::span slots, + const al::span sorted_slots, const al::span voices) { ProcessVoiceChanges(ctx); @@ -1872,9 +1940,9 @@ void ProcessParamUpdates(ContextBase *ctx, const EffectSlotArray &slots, if(!ctx->mHoldUpdates.load(std::memory_order_acquire)) LIKELY { bool force{CalcContextParams(ctx)}; - auto sorted_slots = const_cast(slots.data() + slots.size()); + auto sorted_slot_base = al::to_address(sorted_slots.begin()); for(EffectSlot *slot : slots) - force |= CalcEffectSlotParams(slot, sorted_slots, ctx); + force |= CalcEffectSlotParams(slot, sorted_slot_base, ctx); for(Voice *voice : voices) { @@ -1890,142 +1958,137 @@ void ProcessContexts(DeviceBase *device, const uint SamplesToDo) { ASSUME(SamplesToDo > 0); - const nanoseconds curtime{device->ClockBase + - nanoseconds{seconds{device->SamplesDone}}/device->Frequency}; + const auto curtime = device->getClockTime(); - for(ContextBase *ctx : *device->mContexts.load(std::memory_order_acquire)) + auto proc_context = [SamplesToDo,curtime](ContextBase *ctx) { - const EffectSlotArray &auxslots = *ctx->mActiveAuxSlots.load(std::memory_order_acquire); - const al::span voices{ctx->getVoicesSpanAcquired()}; + const auto auxslotspan = al::span{*ctx->mActiveAuxSlots.load(std::memory_order_acquire)}; + const auto auxslots = auxslotspan.first(auxslotspan.size()>>1); + const auto sorted_slots = auxslotspan.last(auxslotspan.size()>>1); + const auto voices = ctx->getVoicesSpanAcquired(); /* Process pending property updates for objects on the context. */ - ProcessParamUpdates(ctx, auxslots, voices); + ProcessParamUpdates(ctx, auxslots, sorted_slots, voices); /* Clear auxiliary effect slot mixing buffers. */ - for(EffectSlot *slot : auxslots) + auto clear_wetbuffers = [](EffectSlot *slot) { - for(auto &buffer : slot->Wet.Buffer) - buffer.fill(0.0f); - } + auto clear_buffer = [](const FloatBufferSpan buffer) + { std::fill(buffer.begin(), buffer.end(), 0.0f); }; + std::for_each(slot->Wet.Buffer.begin(), slot->Wet.Buffer.end(), clear_buffer); + }; + std::for_each(auxslots.begin(), auxslots.end(), clear_wetbuffers); /* Process voices that have a playing source. */ - for(Voice *voice : voices) + auto proc_voice = [ctx,curtime,SamplesToDo](Voice *voice) { const Voice::State vstate{voice->mPlayState.load(std::memory_order_acquire)}; if(vstate != Voice::Stopped && vstate != Voice::Pending) voice->mix(vstate, ctx, curtime, SamplesToDo); - } + }; + std::for_each(voices.begin(), voices.end(), proc_voice); /* Process effects. */ - if(const size_t num_slots{auxslots.size()}) + if(!auxslots.empty()) { - auto slots = auxslots.data(); - auto slots_end = slots + num_slots; - /* Sort the slots into extra storage, so that effect slots come - * before their effect slot target (or their targets' target). + * before their effect slot target (or their targets' target). Skip + * sorting if it has already been done. */ - const al::span sorted_slots{const_cast(slots_end), - num_slots}; - /* Skip sorting if it has already been done. */ if(!sorted_slots[0]) { - /* First, copy the slots to the sorted list, then partition the - * sorted list so that all slots without a target slot go to - * the end. + /* First, copy the slots to the sorted list and partition them, + * so that all slots without a target slot go to the end. */ - std::copy(slots, slots_end, sorted_slots.begin()); - auto split_point = std::partition(sorted_slots.begin(), sorted_slots.end(), - [](const EffectSlot *slot) noexcept -> bool - { return slot->Target != nullptr; }); + auto has_target = [](const EffectSlot *slot) noexcept -> bool + { return slot->Target != nullptr; }; + auto split_point = std::partition_copy(auxslots.rbegin(), auxslots.rend(), + sorted_slots.begin(), sorted_slots.rbegin(), has_target).first; /* There must be at least one slot without a slot target. */ assert(split_point != sorted_slots.end()); - /* Simple case: no more than 1 slot has a target slot. Either - * all slots go right to the output, or the remaining one must - * target an already-partitioned slot. + /* Starting from the back of the sorted list, continue + * partitioning the front of the list given each target until + * all targets are accounted for. This ensures all slots + * without a target go last, all slots directly targeting those + * last slots go second-to-last, all slots directly targeting + * those second-last slots go third-to-last, etc. */ - if(split_point - sorted_slots.begin() > 1) + auto next_target = sorted_slots.end(); + while(std::distance(sorted_slots.begin(), split_point) > 1) { - /* At least two slots target other slots. Starting from the - * back of the sorted list, continue partitioning the front - * of the list given each target until all targets are - * accounted for. This ensures all slots without a target - * go last, all slots directly targeting those last slots - * go second-to-last, all slots directly targeting those - * second-last slots go third-to-last, etc. + /* This shouldn't happen, but if there's unsorted slots + * left that don't target any sorted slots, they can't + * contribute to the output, so leave them. */ - auto next_target = sorted_slots.end(); - do { - /* This shouldn't happen, but if there's unsorted slots - * left that don't target any sorted slots, they can't - * contribute to the output, so leave them. - */ - if(next_target == split_point) UNLIKELY - break; - - --next_target; - split_point = std::partition(sorted_slots.begin(), split_point, - [next_target](const EffectSlot *slot) noexcept -> bool - { return slot->Target != *next_target; }); - } while(split_point - sorted_slots.begin() > 1); + if(next_target == split_point) UNLIKELY + break; + + --next_target; + auto not_next = [next_target](const EffectSlot *slot) noexcept -> bool + { return slot->Target != *next_target; }; + split_point = std::partition(sorted_slots.begin(), split_point, not_next); } } - for(const EffectSlot *slot : sorted_slots) + auto proc_slot = [SamplesToDo](const EffectSlot *slot) { EffectState *state{slot->mEffectState.get()}; state->process(SamplesToDo, slot->Wet.Buffer, state->mOutTarget); - } + }; + std::for_each(sorted_slots.begin(), sorted_slots.end(), proc_slot); } /* Signal the event handler if there are any events to read. */ - RingBuffer *ring{ctx->mAsyncEvents.get()}; - if(ring->readSpace() > 0) + if(RingBuffer *ring{ctx->mAsyncEvents.get()}; ring->readSpace() > 0) ctx->mEventSem.post(); - } + }; + const auto contexts = al::span{*device->mContexts.load(std::memory_order_acquire)}; + std::for_each(contexts.begin(), contexts.end(), proc_context); } void ApplyDistanceComp(const al::span Samples, const size_t SamplesToDo, - const DistanceComp::ChanData *distcomp) + const al::span chandata) { ASSUME(SamplesToDo > 0); + auto distcomp = chandata.begin(); for(auto &chanbuffer : Samples) { const float gain{distcomp->Gain}; - const size_t base{distcomp->Length}; - float *distbuf{al::assume_aligned<16>(distcomp->Buffer)}; + auto distbuf = al::span{al::assume_aligned<16>(distcomp->Buffer.data()), + distcomp->Buffer.size()}; ++distcomp; - if(base < 1) - continue; + const size_t base{distbuf.size()}; + if(base < 1) continue; - float *inout{al::assume_aligned<16>(chanbuffer.data())}; - auto inout_end = inout + SamplesToDo; + const auto inout = al::span{al::assume_aligned<16>(chanbuffer.data()), SamplesToDo}; if(SamplesToDo >= base) LIKELY { - auto delay_end = std::rotate(inout, inout_end - base, inout_end); - std::swap_ranges(inout, delay_end, distbuf); + auto delay_end = std::rotate(inout.begin(), inout.end()-ptrdiff_t(base), inout.end()); + std::swap_ranges(inout.begin(), delay_end, distbuf.begin()); } else { - auto delay_start = std::swap_ranges(inout, inout_end, distbuf); - std::rotate(distbuf, delay_start, distbuf + base); + auto delay_start = std::swap_ranges(inout.begin(), inout.end(), distbuf.begin()); + std::rotate(distbuf.begin(), delay_start, distbuf.begin()+ptrdiff_t(base)); } - std::transform(inout, inout_end, inout, [gain](float s) { return s * gain; }); + std::transform(inout.begin(), inout.end(), inout.begin(), + [gain](float s) { return s*gain; }); } } void ApplyDither(const al::span Samples, uint *dither_seed, const float quant_scale, const size_t SamplesToDo) { + static constexpr double invRNGRange{1.0 / std::numeric_limits::max()}; ASSUME(SamplesToDo > 0); /* Dithering. Generate whitenoise (uniform distribution of random values * between -1 and +1) and add it to the sample values, after scaling up to - * the desired quantization depth amd before rounding. + * the desired quantization depth and before rounding. */ const float invscale{1.0f / quant_scale}; uint seed{*dither_seed}; @@ -2034,7 +2097,7 @@ void ApplyDither(const al::span Samples, uint *dither_seed, float val{sample * quant_scale}; uint rng0{dither_rng(&seed)}; uint rng1{dither_rng(&seed)}; - val += static_cast(rng0*(1.0/UINT_MAX) - rng1*(1.0/UINT_MAX)); + val += static_cast(rng0*invRNGRange - rng1*invRNGRange); return fast_roundf(val) * invscale; }; for(FloatBufferLine &inout : Samples) @@ -2058,12 +2121,12 @@ template<> inline int32_t SampleConv(float val) noexcept * When scaling and clamping for a signed 32-bit integer, these following * values are the best a float can give. */ - return fastf2i(clampf(val*2147483648.0f, -2147483648.0f, 2147483520.0f)); + return fastf2i(std::clamp(val*2147483648.0f, -2147483648.0f, 2147483520.0f)); } template<> inline int16_t SampleConv(float val) noexcept -{ return static_cast(fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f))); } +{ return static_cast(fastf2i(std::clamp(val*32768.0f, -32768.0f, 32767.0f))); } template<> inline int8_t SampleConv(float val) noexcept -{ return static_cast(fastf2i(clampf(val*128.0f, -128.0f, 127.0f))); } +{ return static_cast(fastf2i(std::clamp(val*128.0f, -128.0f, 127.0f))); } /* Define unsigned output variations. */ template<> inline uint32_t SampleConv(float val) noexcept @@ -2073,34 +2136,51 @@ template<> inline uint16_t SampleConv(float val) noexcept template<> inline uint8_t SampleConv(float val) noexcept { return static_cast(SampleConv(val) + 128); } -template +template void Write(const al::span InBuffer, void *OutBuffer, const size_t Offset, const size_t SamplesToDo, const size_t FrameStep) { ASSUME(FrameStep > 0); ASSUME(SamplesToDo > 0); - DevFmtType_t *outbase{static_cast*>(OutBuffer) + Offset*FrameStep}; + /* Some Clang versions don't like calling subspan on an rvalue here. */ + const auto output_ = al::span{static_cast(OutBuffer), (Offset+SamplesToDo)*FrameStep}; + const auto output = output_.subspan(Offset*FrameStep); size_t c{0}; for(const FloatBufferLine &inbuf : InBuffer) { - DevFmtType_t *out{outbase++}; - auto conv_sample = [FrameStep,&out](const float s) noexcept -> void + auto out = output.begin(); + auto conv_sample = [FrameStep,c,&out](const float s) noexcept { - *out = SampleConv>(s); - out += FrameStep; + out[c] = SampleConv(s); + out += ptrdiff_t(FrameStep); }; - std::for_each(inbuf.begin(), inbuf.begin()+SamplesToDo, conv_sample); + std::for_each_n(inbuf.cbegin(), SamplesToDo, conv_sample); ++c; } if(const size_t extra{FrameStep - c}) { - const auto silence = SampleConv>(0.0f); + const auto silence = SampleConv(0.0f); for(size_t i{0};i < SamplesToDo;++i) - { - std::fill_n(outbase, extra, silence); - outbase += FrameStep; - } + std::fill_n(&output[i*FrameStep + c], extra, silence); + } +} + +template +void Write(const al::span InBuffer, al::span OutBuffers, + const size_t Offset, const size_t SamplesToDo) +{ + ASSUME(SamplesToDo > 0); + + auto srcbuf = InBuffer.cbegin(); + for(auto *dstbuf : OutBuffers) + { + const auto src = al::span{*srcbuf}.first(SamplesToDo); + /* Some Clang versions don't like calling subspan on an rvalue here. */ + const auto dst_ = al::span{static_cast(dstbuf), Offset+SamplesToDo}; + const auto dst = dst_.subspan(Offset); + std::transform(src.cbegin(), src.end(), dst.begin(), SampleConv); + ++srcbuf; } } @@ -2108,28 +2188,28 @@ void Write(const al::span InBuffer, void *OutBuffer, cons uint DeviceBase::renderSamples(const uint numSamples) { - const uint samplesToDo{minu(numSamples, BufferLineSize)}; + const uint samplesToDo{std::min(numSamples, uint{BufferLineSize})}; /* Clear main mixing buffers. */ for(FloatBufferLine &buffer : MixBuffer) buffer.fill(0.0f); - /* Increment the mix count at the start (lsb should now be 1). */ - IncrementRef(MixCount); - - /* Process and mix each context's sources and effects. */ - ProcessContexts(this, samplesToDo); + { + const auto mixLock = getWriteMixLock(); - /* Increment the clock time. Every second's worth of samples is converted - * and added to clock base so that large sample counts don't overflow - * during conversion. This also guarantees a stable conversion. - */ - SamplesDone += samplesToDo; - ClockBase += std::chrono::seconds{SamplesDone / Frequency}; - SamplesDone %= Frequency; + /* Process and mix each context's sources and effects. */ + ProcessContexts(this, samplesToDo); - /* Increment the mix count at the end (lsb should now be 0). */ - IncrementRef(MixCount); + /* Every second's worth of samples is converted and added to clock base + * so that large sample counts don't overflow during conversion. This + * also guarantees a stable conversion. + */ + auto samplesDone = mSamplesDone.load(std::memory_order_relaxed) + samplesToDo; + auto clockBaseSec = mClockBaseSec.load(std::memory_order_relaxed) + + seconds32{samplesDone/mSampleRate}; + mSamplesDone.store(samplesDone%mSampleRate, std::memory_order_relaxed); + mClockBaseSec.store(clockBaseSec, std::memory_order_relaxed); + } /* Apply any needed post-process for finalizing the Dry mix to the RealOut * (Ambisonic decode, UHJ encode, etc). @@ -2137,11 +2217,11 @@ uint DeviceBase::renderSamples(const uint numSamples) postProcess(samplesToDo); /* Apply compression, limiting sample amplitude if needed or desired. */ - if(Limiter) Limiter->process(samplesToDo, RealOut.Buffer.data()); + if(Limiter) Limiter->process(samplesToDo, RealOut.Buffer); /* Apply delays and attenuation for mismatched speaker distances. */ if(ChannelDelays) - ApplyDistanceComp(RealOut.Buffer, samplesToDo, ChannelDelays->mChannels.data()); + ApplyDistanceComp(RealOut.Buffer, samplesToDo, ChannelDelays->mChannels); /* Apply dithering. The compressor should have left enough headroom for the * dither noise to not saturate. @@ -2152,7 +2232,7 @@ uint DeviceBase::renderSamples(const uint numSamples) return samplesToDo; } -void DeviceBase::renderSamples(const al::span outBuffers, const uint numSamples) +void DeviceBase::renderSamples(const al::span outBuffers, const uint numSamples) { FPUCtl mixer_mode{}; uint total{0}; @@ -2160,12 +2240,19 @@ void DeviceBase::renderSamples(const al::span outBuffers, const uint num { const uint samplesToDo{renderSamples(todo)}; - auto *srcbuf = RealOut.Buffer.data(); - for(auto *dstbuf : outBuffers) + switch(FmtType) { - std::copy_n(srcbuf->data(), samplesToDo, dstbuf + total); - ++srcbuf; +#define HANDLE_WRITE(T) case T: \ + Write>(RealOut.Buffer, outBuffers, total, samplesToDo); break; + HANDLE_WRITE(DevFmtByte) + HANDLE_WRITE(DevFmtUByte) + HANDLE_WRITE(DevFmtShort) + HANDLE_WRITE(DevFmtUShort) + HANDLE_WRITE(DevFmtInt) + HANDLE_WRITE(DevFmtUInt) + HANDLE_WRITE(DevFmtFloat) } +#undef HANDLE_WRITE total += samplesToDo; } @@ -2187,7 +2274,7 @@ void DeviceBase::renderSamples(void *outBuffer, const uint numSamples, const siz switch(FmtType) { #define HANDLE_WRITE(T) case T: \ - Write(RealOut.Buffer, outBuffer, total, samplesToDo, frameStep); break; + Write>(RealOut.Buffer, outBuffer, total, samplesToDo, frameStep); break; HANDLE_WRITE(DevFmtByte) HANDLE_WRITE(DevFmtUByte) HANDLE_WRITE(DevFmtShort) @@ -2203,26 +2290,20 @@ void DeviceBase::renderSamples(void *outBuffer, const uint numSamples, const siz } } -void DeviceBase::handleDisconnect(const char *msg, ...) +void DeviceBase::doDisconnect(std::string msg) { - IncrementRef(MixCount); + const auto mixLock = getWriteMixLock(); + if(Connected.exchange(false, std::memory_order_acq_rel)) { AsyncEvent evt{std::in_place_type}; auto &disconnect = std::get(evt); - - va_list args; - va_start(args, msg); - int msglen{vsnprintf(disconnect.msg, sizeof(disconnect.msg), msg, args)}; - va_end(args); - - if(msglen < 0 || static_cast(msglen) >= sizeof(disconnect.msg)) - disconnect.msg[sizeof(disconnect.msg)-1] = 0; + disconnect.msg = std::move(msg); for(ContextBase *ctx : *mContexts.load()) { RingBuffer *ring{ctx->mAsyncEvents.get()}; - auto evt_data = ring->getWriteVector().first; + auto evt_data = ring->getWriteVector()[0]; if(evt_data.len > 0) { al::construct_at(reinterpret_cast(evt_data.buf), evt); @@ -2230,7 +2311,7 @@ void DeviceBase::handleDisconnect(const char *msg, ...) ctx->mEventSem.post(); } - if(!ctx->mStopVoicesOnDisconnect) + if(!ctx->mStopVoicesOnDisconnect.load()) { ProcessVoiceChanges(ctx); continue; @@ -2247,5 +2328,4 @@ void DeviceBase::handleDisconnect(const char *msg, ...) std::for_each(voicelist.begin(), voicelist.end(), stop_voice); } } - IncrementRef(MixCount); } diff --git a/3rdparty/openal/alc/alu.h b/3rdparty/openal/alc/alu.h index 33453487b1f4..6da8d9174064 100644 --- a/3rdparty/openal/alc/alu.h +++ b/3rdparty/openal/alc/alu.h @@ -2,20 +2,23 @@ #define ALU_H #include +#include #include -#include struct ALCcontext; struct ALCdevice; struct EffectSlot; -enum class StereoEncoding : uint8_t; +enum class StereoEncoding : std::uint8_t; +namespace al { +struct Device; +} // namespace al constexpr float GainMixMax{1000.0f}; /* +60dB */ -enum CompatFlags : uint8_t { +enum CompatFlags : std::uint8_t { ReverseX, ReverseY, ReverseZ, @@ -31,7 +34,7 @@ void aluInit(CompatFlagBitset flags, const float nfcscale); * Set up the appropriate panning method and mixing method given the device * properties. */ -void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optional stereomode); +void aluInitRenderer(al::Device *device, int hrtf_id, std::optional stereomode); void aluInitEffectPanning(EffectSlot *slot, ALCcontext *context); diff --git a/3rdparty/openal/alc/backends/alsa.cpp b/3rdparty/openal/alc/backends/alsa.cpp index 0d9ff30dff63..a241be7ab413 100644 --- a/3rdparty/openal/alc/backends/alsa.cpp +++ b/3rdparty/openal/alc/backends/alsa.cpp @@ -29,15 +29,14 @@ #include #include #include -#include #include #include #include +#include #include #include #include -#include "albit.h" #include "alc/alconfig.h" #include "almalloc.h" #include "alnumeric.h" @@ -46,6 +45,7 @@ #include "core/helpers.h" #include "core/logging.h" #include "dynload.h" +#include "fmt/core.h" #include "ringbuffer.h" #include @@ -53,10 +53,12 @@ namespace { -constexpr char alsaDevice[] = "ALSA Default"; +using namespace std::string_view_literals; +[[nodiscard]] constexpr auto GetDefaultName() noexcept { return "ALSA Default"sv; } -#ifdef HAVE_DYNLOAD + +#if HAVE_DYNLOAD #define ALSA_FUNCS(MAGIC) \ MAGIC(snd_strerror); \ MAGIC(snd_pcm_open); \ @@ -252,55 +254,93 @@ std::vector PlaybackDevices; std::vector CaptureDevices; -const char *prefix_name(snd_pcm_stream_t stream) +std::string_view prefix_name(snd_pcm_stream_t stream) noexcept { - assert(stream == SND_PCM_STREAM_PLAYBACK || stream == SND_PCM_STREAM_CAPTURE); - return (stream==SND_PCM_STREAM_PLAYBACK) ? "device-prefix" : "capture-prefix"; + if(stream == SND_PCM_STREAM_PLAYBACK) + return "device-prefix"sv; + return "capture-prefix"sv; } +struct SndCtlCardInfo { + snd_ctl_card_info_t *mInfo{}; + + SndCtlCardInfo() { snd_ctl_card_info_malloc(&mInfo); } + ~SndCtlCardInfo() { if(mInfo) snd_ctl_card_info_free(mInfo); } + SndCtlCardInfo(const SndCtlCardInfo&) = delete; + SndCtlCardInfo& operator=(const SndCtlCardInfo&) = delete; + + [[nodiscard]] + operator snd_ctl_card_info_t*() const noexcept { return mInfo; } /* NOLINT(google-explicit-constructor) */ +}; + +struct SndPcmInfo { + snd_pcm_info_t *mInfo{}; + + SndPcmInfo() { snd_pcm_info_malloc(&mInfo); } + ~SndPcmInfo() { if(mInfo) snd_pcm_info_free(mInfo); } + SndPcmInfo(const SndPcmInfo&) = delete; + SndPcmInfo& operator=(const SndPcmInfo&) = delete; + + [[nodiscard]] + operator snd_pcm_info_t*() const noexcept { return mInfo; } /* NOLINT(google-explicit-constructor) */ +}; + +struct SndCtl { + snd_ctl_t *mHandle{}; + + SndCtl() = default; + ~SndCtl() { if(mHandle) snd_ctl_close(mHandle); } + SndCtl(const SndCtl&) = delete; + SndCtl& operator=(const SndCtl&) = delete; + + [[nodiscard]] + auto open(const char *name, int mode) { return snd_ctl_open(&mHandle, name, mode); } + + [[nodiscard]] + operator snd_ctl_t*() const noexcept { return mHandle; } /* NOLINT(google-explicit-constructor) */ +}; + + std::vector probe_devices(snd_pcm_stream_t stream) { std::vector devlist; - snd_ctl_card_info_t *info; - snd_ctl_card_info_malloc(&info); - snd_pcm_info_t *pcminfo; - snd_pcm_info_malloc(&pcminfo); + SndCtlCardInfo info; + SndPcmInfo pcminfo; - auto defname = ConfigValueStr(nullptr, "alsa", - (stream == SND_PCM_STREAM_PLAYBACK) ? "device" : "capture"); - devlist.emplace_back(alsaDevice, defname ? defname->c_str() : "default"); + auto defname = ConfigValueStr({}, "alsa"sv, + (stream == SND_PCM_STREAM_PLAYBACK) ? "device"sv : "capture"sv); + devlist.emplace_back(GetDefaultName(), defname ? std::string_view{*defname} : "default"sv); - if(auto customdevs = ConfigValueStr(nullptr, "alsa", - (stream == SND_PCM_STREAM_PLAYBACK) ? "custom-devices" : "custom-captures")) + if(auto customdevs = ConfigValueStr({}, "alsa"sv, + (stream == SND_PCM_STREAM_PLAYBACK) ? "custom-devices"sv : "custom-captures"sv)) { - size_t nextpos{customdevs->find_first_not_of(';')}; - size_t curpos; - while((curpos=nextpos) < customdevs->length()) + size_t curpos{customdevs->find_first_not_of(';')}; + while(curpos < customdevs->length()) { - nextpos = customdevs->find_first_of(';', curpos+1); - - size_t seppos{customdevs->find_first_of('=', curpos)}; + size_t nextpos{customdevs->find(';', curpos+1)}; + const size_t seppos{customdevs->find('=', curpos)}; if(seppos == curpos || seppos >= nextpos) { - std::string spec{customdevs->substr(curpos, nextpos-curpos)}; - ERR("Invalid ALSA device specification \"%s\"\n", spec.c_str()); + const auto spec = std::string_view{*customdevs}.substr(curpos, nextpos-curpos); + ERR("Invalid ALSA device specification \"{}\"", spec); } else { - devlist.emplace_back(customdevs->substr(curpos, seppos-curpos), - customdevs->substr(seppos+1, nextpos-seppos-1)); - const auto &entry = devlist.back(); - TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str()); + const std::string_view strview{*customdevs}; + const auto &entry = devlist.emplace_back(strview.substr(curpos, seppos-curpos), + strview.substr(seppos+1, nextpos-seppos-1)); + TRACE("Got device \"{}\", \"{}\"", entry.name, entry.device_name); } if(nextpos < customdevs->length()) nextpos = customdevs->find_first_not_of(';', nextpos+1); + curpos = nextpos; } } - const std::string main_prefix{ - ConfigValueStr(nullptr, "alsa", prefix_name(stream)).value_or("plughw:")}; + const std::string main_prefix{ConfigValueStr({}, "alsa"sv, prefix_name(stream)) + .value_or("plughw:")}; int card{-1}; int err{snd_card_next(&card)}; @@ -308,16 +348,17 @@ std::vector probe_devices(snd_pcm_stream_t stream) { std::string name{"hw:" + std::to_string(card)}; - snd_ctl_t *handle; - if((err=snd_ctl_open(&handle, name.c_str(), 0)) < 0) + SndCtl handle; + err = handle.open(name.c_str(), 0); + if(err < 0) { - ERR("control open (hw:%d): %s\n", card, snd_strerror(err)); + ERR("control open (hw:{}): {}", card, snd_strerror(err)); continue; } - if((err=snd_ctl_card_info(handle, info)) < 0) + err = snd_ctl_card_info(handle, info); + if(err < 0) { - ERR("control hardware info (hw:%d): %s\n", card, snd_strerror(err)); - snd_ctl_close(handle); + ERR("control hardware info (hw:{}): {}", card, snd_strerror(err)); continue; } @@ -326,63 +367,44 @@ std::vector probe_devices(snd_pcm_stream_t stream) name = prefix_name(stream); name += '-'; name += cardid; - const std::string card_prefix{ - ConfigValueStr(nullptr, "alsa", name.c_str()).value_or(main_prefix)}; + const std::string card_prefix{ConfigValueStr({}, "alsa"sv, name).value_or(main_prefix)}; int dev{-1}; while(true) { if(snd_ctl_pcm_next_device(handle, &dev) < 0) - ERR("snd_ctl_pcm_next_device failed\n"); + ERR("snd_ctl_pcm_next_device failed"); if(dev < 0) break; snd_pcm_info_set_device(pcminfo, static_cast(dev)); snd_pcm_info_set_subdevice(pcminfo, 0); snd_pcm_info_set_stream(pcminfo, stream); - if((err=snd_ctl_pcm_info(handle, pcminfo)) < 0) + err = snd_ctl_pcm_info(handle, pcminfo); + if(err < 0) { if(err != -ENOENT) - ERR("control digital audio info (hw:%d): %s\n", card, snd_strerror(err)); + ERR("control digital audio info (hw:{}): {}", card, snd_strerror(err)); continue; } /* "prefix-cardid-dev" */ - name = prefix_name(stream); - name += '-'; - name += cardid; - name += '-'; - name += std::to_string(dev); - const std::string device_prefix{ - ConfigValueStr(nullptr, "alsa", name.c_str()).value_or(card_prefix)}; + name = fmt::format("{}-{}-{}", prefix_name(stream), cardid, dev); + const auto device_prefix = std::string{ConfigValueStr({}, "alsa"sv, name) + .value_or(card_prefix)}; /* "CardName, PcmName (CARD=cardid,DEV=dev)" */ - name = cardname; - name += ", "; - name += snd_pcm_info_get_name(pcminfo); - name += " (CARD="; - name += cardid; - name += ",DEV="; - name += std::to_string(dev); - name += ')'; + name = fmt::format("{}, {} (CARD={},DEV={})", cardname, snd_pcm_info_get_name(pcminfo), + cardid, dev); /* "devprefixCARD=cardid,DEV=dev" */ - std::string device{device_prefix}; - device += "CARD="; - device += cardid; - device += ",DEV="; - device += std::to_string(dev); + auto device = fmt::format("{}CARD={},DEV={}", device_prefix, cardid, dev); - devlist.emplace_back(std::move(name), std::move(device)); - const auto &entry = devlist.back(); - TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str()); + const auto &entry = devlist.emplace_back(std::move(name), std::move(device)); + TRACE("Got device \"{}\", \"{}\"", entry.name, entry.device_name); } - snd_ctl_close(handle); } if(err < 0) - ERR("snd_card_next failed: %s\n", snd_strerror(err)); - - snd_pcm_info_free(pcminfo); - snd_ctl_card_info_free(info); + ERR("snd_card_next failed: {}", snd_strerror(err)); return devlist; } @@ -392,7 +414,6 @@ int verify_state(snd_pcm_t *handle) { snd_pcm_state_t state{snd_pcm_state(handle)}; - int err; switch(state) { case SND_PCM_STATE_OPEN: @@ -405,15 +426,27 @@ int verify_state(snd_pcm_t *handle) break; case SND_PCM_STATE_XRUN: - if((err=snd_pcm_recover(handle, -EPIPE, 1)) < 0) + if(int err{snd_pcm_recover(handle, -EPIPE, 1)}; err < 0) return err; break; case SND_PCM_STATE_SUSPENDED: - if((err=snd_pcm_recover(handle, -ESTRPIPE, 1)) < 0) + if(int err{snd_pcm_recover(handle, -ESTRPIPE, 1)}; err < 0) return err; break; + case SND_PCM_STATE_DISCONNECTED: return -ENODEV; + + /* ALSA headers have made this enum public, leaving us in a bind: use + * the enum despite being private and internal to the libasound, or + * ignore when an enum value isn't handled. We can't rely on it being + * declared either, since older headers don't have it and it could be + * removed in the future. We can't even really rely on its value, since + * being private/internal means it's subject to change, but this is the + * best we can do. + */ + case 1024 /*SND_PCM_STATE_PRIVATE1*/: + assert(state != 1024); } return state; @@ -421,7 +454,7 @@ int verify_state(snd_pcm_t *handle) struct AlsaPlayback final : public BackendBase { - AlsaPlayback(DeviceBase *device) noexcept : BackendBase{device} { } + explicit AlsaPlayback(DeviceBase *device) noexcept : BackendBase{device} { } ~AlsaPlayback() override; int mixerProc(); @@ -443,8 +476,6 @@ struct AlsaPlayback final : public BackendBase { std::atomic mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(AlsaPlayback) }; AlsaPlayback::~AlsaPlayback() @@ -458,31 +489,31 @@ AlsaPlayback::~AlsaPlayback() int AlsaPlayback::mixerProc() { SetRTPriority(); - althrd_setname(MIXER_THREAD_NAME); + althrd_setname(GetMixerThreadName()); - const snd_pcm_uframes_t update_size{mDevice->UpdateSize}; - const snd_pcm_uframes_t buffer_size{mDevice->BufferSize}; + const snd_pcm_uframes_t update_size{mDevice->mUpdateSize}; + const snd_pcm_uframes_t buffer_size{mDevice->mBufferSize}; while(!mKillNow.load(std::memory_order_acquire)) { int state{verify_state(mPcmHandle)}; if(state < 0) { - ERR("Invalid state detected: %s\n", snd_strerror(state)); - mDevice->handleDisconnect("Bad state: %s", snd_strerror(state)); + ERR("Invalid state detected: {}", snd_strerror(state)); + mDevice->handleDisconnect("Bad state: {}", snd_strerror(state)); break; } snd_pcm_sframes_t avails{snd_pcm_avail_update(mPcmHandle)}; if(avails < 0) { - ERR("available update failed: %s\n", snd_strerror(static_cast(avails))); + ERR("available update failed: {}", snd_strerror(static_cast(avails))); continue; } snd_pcm_uframes_t avail{static_cast(avails)}; if(avail > buffer_size) { - WARN("available samples exceeds the buffer size\n"); + WARN("available samples exceeds the buffer size"); snd_pcm_reset(mPcmHandle); continue; } @@ -495,18 +526,18 @@ int AlsaPlayback::mixerProc() int err{snd_pcm_start(mPcmHandle)}; if(err < 0) { - ERR("start failed: %s\n", snd_strerror(err)); + ERR("start failed: {}", snd_strerror(err)); continue; } } if(snd_pcm_wait(mPcmHandle, 1000) == 0) - ERR("Wait timeout... buffer size too low?\n"); + ERR("Wait timeout... buffer size too low?"); continue; } avail -= avail%update_size; // it is possible that contiguous areas are smaller, thus we use a loop - std::lock_guard _{mMutex}; + std::lock_guard dlock{mMutex}; while(avail > 0) { snd_pcm_uframes_t frames{avail}; @@ -516,17 +547,18 @@ int AlsaPlayback::mixerProc() int err{snd_pcm_mmap_begin(mPcmHandle, &areas, &offset, &frames)}; if(err < 0) { - ERR("mmap begin error: %s\n", snd_strerror(err)); + ERR("mmap begin error: {}", snd_strerror(err)); break; } + /* NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) */ char *WritePtr{static_cast(areas->addr) + (offset * areas->step / 8)}; mDevice->renderSamples(WritePtr, static_cast(frames), mFrameStep); snd_pcm_sframes_t commitres{snd_pcm_mmap_commit(mPcmHandle, offset, frames)}; if(commitres < 0 || static_cast(commitres) != frames) { - ERR("mmap commit error: %s\n", + ERR("mmap commit error: {}", snd_strerror(commitres >= 0 ? -EPIPE : static_cast(commitres))); break; } @@ -541,30 +573,30 @@ int AlsaPlayback::mixerProc() int AlsaPlayback::mixerNoMMapProc() { SetRTPriority(); - althrd_setname(MIXER_THREAD_NAME); + althrd_setname(GetMixerThreadName()); - const snd_pcm_uframes_t update_size{mDevice->UpdateSize}; - const snd_pcm_uframes_t buffer_size{mDevice->BufferSize}; + const snd_pcm_uframes_t update_size{mDevice->mUpdateSize}; + const snd_pcm_uframes_t buffer_size{mDevice->mBufferSize}; while(!mKillNow.load(std::memory_order_acquire)) { int state{verify_state(mPcmHandle)}; if(state < 0) { - ERR("Invalid state detected: %s\n", snd_strerror(state)); - mDevice->handleDisconnect("Bad state: %s", snd_strerror(state)); + ERR("Invalid state detected: {}", snd_strerror(state)); + mDevice->handleDisconnect("Bad state: {}", snd_strerror(state)); break; } snd_pcm_sframes_t avail{snd_pcm_avail_update(mPcmHandle)}; if(avail < 0) { - ERR("available update failed: %s\n", snd_strerror(static_cast(avail))); + ERR("available update failed: {}", snd_strerror(static_cast(avail))); continue; } if(static_cast(avail) > buffer_size) { - WARN("available samples exceeds the buffer size\n"); + WARN("available samples exceeds the buffer size"); snd_pcm_reset(mPcmHandle); continue; } @@ -576,22 +608,22 @@ int AlsaPlayback::mixerNoMMapProc() int err{snd_pcm_start(mPcmHandle)}; if(err < 0) { - ERR("start failed: %s\n", snd_strerror(err)); + ERR("start failed: {}", snd_strerror(err)); continue; } } if(snd_pcm_wait(mPcmHandle, 1000) == 0) - ERR("Wait timeout... buffer size too low?\n"); + ERR("Wait timeout... buffer size too low?"); continue; } - std::byte *WritePtr{mBuffer.data()}; + auto WritePtr = mBuffer.begin(); avail = snd_pcm_bytes_to_frames(mPcmHandle, static_cast(mBuffer.size())); - std::lock_guard _{mMutex}; - mDevice->renderSamples(WritePtr, static_cast(avail), mFrameStep); + std::lock_guard dlock{mMutex}; + mDevice->renderSamples(al::to_address(WritePtr), static_cast(avail), mFrameStep); while(avail > 0) { - snd_pcm_sframes_t ret{snd_pcm_writei(mPcmHandle, WritePtr, + snd_pcm_sframes_t ret{snd_pcm_writei(mPcmHandle, al::to_address(WritePtr), static_cast(avail))}; switch(ret) { @@ -638,22 +670,22 @@ void AlsaPlayback::open(std::string_view name) [name](const DevMap &entry) -> bool { return entry.name == name; }); if(iter == PlaybackDevices.cend()) throw al::backend_exception{al::backend_error::NoDevice, - "Device name \"%.*s\" not found", static_cast(name.length()), name.data()}; + "Device name \"{}\" not found", name}; driver = iter->device_name; } else { - name = alsaDevice; - if(auto driveropt = ConfigValueStr(nullptr, "alsa", "device")) + name = GetDefaultName(); + if(auto driveropt = ConfigValueStr({}, "alsa"sv, "device"sv)) driver = std::move(driveropt).value(); } - TRACE("Opening device \"%s\"\n", driver.c_str()); + TRACE("Opening device \"{}\"", driver); snd_pcm_t *pcmHandle{}; int err{snd_pcm_open(&pcmHandle, driver.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)}; if(err < 0) throw al::backend_exception{al::backend_error::NoDevice, - "Could not open ALSA device \"%s\"", driver.c_str()}; + "Could not open ALSA device \"{}\"", driver}; if(mPcmHandle) snd_pcm_close(mPcmHandle); mPcmHandle = pcmHandle; @@ -661,7 +693,7 @@ void AlsaPlayback::open(std::string_view name) /* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */ snd_config_update_free_global(); - mDevice->DeviceName = name; + mDeviceName = name; } bool AlsaPlayback::reset() @@ -692,16 +724,15 @@ bool AlsaPlayback::reset() break; } - bool allowmmap{!!GetConfigValueBool(mDevice->DeviceName.c_str(), "alsa", "mmap", true)}; - uint periodLen{static_cast(mDevice->UpdateSize * 1000000_u64 / mDevice->Frequency)}; - uint bufferLen{static_cast(mDevice->BufferSize * 1000000_u64 / mDevice->Frequency)}; - uint rate{mDevice->Frequency}; + bool allowmmap{GetConfigValueBool(mDevice->mDeviceName, "alsa"sv, "mmap"sv, true)}; + uint periodLen{static_cast(mDevice->mUpdateSize * 1000000_u64 / mDevice->mSampleRate)}; + uint bufferLen{static_cast(mDevice->mBufferSize * 1000000_u64 / mDevice->mSampleRate)}; + uint rate{mDevice->mSampleRate}; - int err{}; HwParamsPtr hp{CreateHwParams()}; #define CHECK(x) do { \ - if((err=(x)) < 0) \ - throw al::backend_exception{al::backend_error::DeviceError, #x " failed: %s", \ + if(int err{x}; err < 0) \ + throw al::backend_exception{al::backend_error::DeviceError, #x " failed: {}", \ snd_strerror(err)}; \ } while(0) CHECK(snd_pcm_hw_params_any(mPcmHandle, hp.get())); @@ -715,17 +746,18 @@ bool AlsaPlayback::reset() /* test and set format (implicitly sets sample bits) */ if(snd_pcm_hw_params_test_format(mPcmHandle, hp.get(), format) < 0) { - static const struct { + struct FormatMap { snd_pcm_format_t format; DevFmtType fmttype; - } formatlist[] = { - { SND_PCM_FORMAT_FLOAT, DevFmtFloat }, - { SND_PCM_FORMAT_S32, DevFmtInt }, - { SND_PCM_FORMAT_U32, DevFmtUInt }, - { SND_PCM_FORMAT_S16, DevFmtShort }, - { SND_PCM_FORMAT_U16, DevFmtUShort }, - { SND_PCM_FORMAT_S8, DevFmtByte }, - { SND_PCM_FORMAT_U8, DevFmtUByte }, + }; + static constexpr std::array formatlist{ + FormatMap{SND_PCM_FORMAT_FLOAT, DevFmtFloat }, + FormatMap{SND_PCM_FORMAT_S32, DevFmtInt }, + FormatMap{SND_PCM_FORMAT_U32, DevFmtUInt }, + FormatMap{SND_PCM_FORMAT_S16, DevFmtShort }, + FormatMap{SND_PCM_FORMAT_U16, DevFmtUShort}, + FormatMap{SND_PCM_FORMAT_S8, DevFmtByte }, + FormatMap{SND_PCM_FORMAT_U8, DevFmtUByte }, }; for(const auto &fmt : formatlist) @@ -750,21 +782,21 @@ bool AlsaPlayback::reset() else mDevice->FmtChans = DevFmtStereo; } /* set rate (implicitly constrains period/buffer parameters) */ - if(!GetConfigValueBool(mDevice->DeviceName.c_str(), "alsa", "allow-resampler", false) + if(!GetConfigValueBool(mDevice->mDeviceName, "alsa", "allow-resampler", false) || !mDevice->Flags.test(FrequencyRequest)) { if(snd_pcm_hw_params_set_rate_resample(mPcmHandle, hp.get(), 0) < 0) - WARN("Failed to disable ALSA resampler\n"); + WARN("Failed to disable ALSA resampler"); } else if(snd_pcm_hw_params_set_rate_resample(mPcmHandle, hp.get(), 1) < 0) - WARN("Failed to enable ALSA resampler\n"); + WARN("Failed to enable ALSA resampler"); CHECK(snd_pcm_hw_params_set_rate_near(mPcmHandle, hp.get(), &rate, nullptr)); /* set period time (implicitly constrains period/buffer parameters) */ - if((err=snd_pcm_hw_params_set_period_time_near(mPcmHandle, hp.get(), &periodLen, nullptr)) < 0) - ERR("snd_pcm_hw_params_set_period_time_near failed: %s\n", snd_strerror(err)); + if(int err{snd_pcm_hw_params_set_period_time_near(mPcmHandle, hp.get(), &periodLen, nullptr)}; err < 0) + ERR("snd_pcm_hw_params_set_period_time_near failed: {}", snd_strerror(err)); /* set buffer time (implicitly sets buffer size/bytes/time and period size/bytes) */ - if((err=snd_pcm_hw_params_set_buffer_time_near(mPcmHandle, hp.get(), &bufferLen, nullptr)) < 0) - ERR("snd_pcm_hw_params_set_buffer_time_near failed: %s\n", snd_strerror(err)); + if(int err{snd_pcm_hw_params_set_buffer_time_near(mPcmHandle, hp.get(), &bufferLen, nullptr)}; err < 0) + ERR("snd_pcm_hw_params_set_buffer_time_near failed: {}", snd_strerror(err)); /* install and prepare hardware configuration */ CHECK(snd_pcm_hw_params(mPcmHandle, hp.get())); @@ -787,9 +819,9 @@ bool AlsaPlayback::reset() #undef CHECK sp = nullptr; - mDevice->BufferSize = static_cast(bufferSizeInFrames); - mDevice->UpdateSize = static_cast(periodSizeInFrames); - mDevice->Frequency = rate; + mDevice->mBufferSize = static_cast(bufferSizeInFrames); + mDevice->mUpdateSize = static_cast(periodSizeInFrames); + mDevice->mSampleRate = rate; setDefaultChannelOrder(); @@ -798,12 +830,11 @@ bool AlsaPlayback::reset() void AlsaPlayback::start() { - int err{}; snd_pcm_access_t access{}; HwParamsPtr hp{CreateHwParams()}; #define CHECK(x) do { \ - if((err=(x)) < 0) \ - throw al::backend_exception{al::backend_error::DeviceError, #x " failed: %s", \ + if(int err{x}; err < 0) \ + throw al::backend_exception{al::backend_error::DeviceError, #x " failed: {}", \ snd_strerror(err)}; \ } while(0) CHECK(snd_pcm_hw_params_current(mPcmHandle, hp.get())); @@ -814,7 +845,7 @@ void AlsaPlayback::start() int (AlsaPlayback::*thread_func)(){}; if(access == SND_PCM_ACCESS_RW_INTERLEAVED) { - auto datalen = snd_pcm_frames_to_bytes(mPcmHandle, mDevice->UpdateSize); + auto datalen = snd_pcm_frames_to_bytes(mPcmHandle, mDevice->mUpdateSize); mBuffer.resize(static_cast(datalen)); thread_func = &AlsaPlayback::mixerNoMMapProc; } @@ -827,11 +858,11 @@ void AlsaPlayback::start() try { mKillNow.store(false, std::memory_order_release); - mThread = std::thread{std::mem_fn(thread_func), this}; + mThread = std::thread{thread_func, this}; } catch(std::exception& e) { throw al::backend_exception{al::backend_error::DeviceError, - "Failed to start mixing thread: %s", e.what()}; + "Failed to start mixing thread: {}", e.what()}; } } @@ -844,31 +875,30 @@ void AlsaPlayback::stop() mBuffer.clear(); int err{snd_pcm_drop(mPcmHandle)}; if(err < 0) - ERR("snd_pcm_drop failed: %s\n", snd_strerror(err)); + ERR("snd_pcm_drop failed: {}", snd_strerror(err)); } ClockLatency AlsaPlayback::getClockLatency() { - ClockLatency ret; - - std::lock_guard _{mMutex}; - ret.ClockTime = GetDeviceClockTime(mDevice); + std::lock_guard dlock{mMutex}; + ClockLatency ret{}; + ret.ClockTime = mDevice->getClockTime(); snd_pcm_sframes_t delay{}; int err{snd_pcm_delay(mPcmHandle, &delay)}; if(err < 0) { - ERR("Failed to get pcm delay: %s\n", snd_strerror(err)); + ERR("Failed to get pcm delay: {}", snd_strerror(err)); delay = 0; } ret.Latency = std::chrono::seconds{std::max(0, delay)}; - ret.Latency /= mDevice->Frequency; + ret.Latency /= mDevice->mSampleRate; return ret; } struct AlsaCapture final : public BackendBase { - AlsaCapture(DeviceBase *device) noexcept : BackendBase{device} { } + explicit AlsaCapture(DeviceBase *device) noexcept : BackendBase{device} { } ~AlsaCapture() override; void open(std::string_view name) override; @@ -886,8 +916,6 @@ struct AlsaCapture final : public BackendBase { RingBufferPtr mRing{nullptr}; snd_pcm_sframes_t mLastAvail{0}; - - DEF_NEWDEL(AlsaCapture) }; AlsaCapture::~AlsaCapture() @@ -910,21 +938,20 @@ void AlsaCapture::open(std::string_view name) [name](const DevMap &entry) -> bool { return entry.name == name; }); if(iter == CaptureDevices.cend()) throw al::backend_exception{al::backend_error::NoDevice, - "Device name \"%.*s\" not found", static_cast(name.length()), name.data()}; + "Device name \"{}\" not found", name}; driver = iter->device_name; } else { - name = alsaDevice; - if(auto driveropt = ConfigValueStr(nullptr, "alsa", "capture")) + name = GetDefaultName(); + if(auto driveropt = ConfigValueStr({}, "alsa"sv, "capture"sv)) driver = std::move(driveropt).value(); } - TRACE("Opening device \"%s\"\n", driver.c_str()); - int err{snd_pcm_open(&mPcmHandle, driver.c_str(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)}; - if(err < 0) + TRACE("Opening device \"{}\"", driver); + if(int err{snd_pcm_open(&mPcmHandle, driver.c_str(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)}; err < 0) throw al::backend_exception{al::backend_error::NoDevice, - "Could not open ALSA device \"%s\"", driver.c_str()}; + "Could not open ALSA device \"{}\"", driver}; /* Free alsa's global config tree. Otherwise valgrind reports a ton of leaks. */ snd_config_update_free_global(); @@ -955,14 +982,16 @@ void AlsaCapture::open(std::string_view name) break; } - snd_pcm_uframes_t bufferSizeInFrames{maxu(mDevice->BufferSize, 100*mDevice->Frequency/1000)}; - snd_pcm_uframes_t periodSizeInFrames{minu(mDevice->BufferSize, 25*mDevice->Frequency/1000)}; + snd_pcm_uframes_t bufferSizeInFrames{std::max(mDevice->mBufferSize, + 100u*mDevice->mSampleRate/1000u)}; + snd_pcm_uframes_t periodSizeInFrames{std::min(mDevice->mBufferSize, + 25u*mDevice->mSampleRate/1000u)}; bool needring{false}; HwParamsPtr hp{CreateHwParams()}; #define CHECK(x) do { \ - if((err=(x)) < 0) \ - throw al::backend_exception{al::backend_error::DeviceError, #x " failed: %s", \ + if(int err{x}; err < 0) \ + throw al::backend_exception{al::backend_error::DeviceError, #x " failed: {}", \ snd_strerror(err)}; \ } while(0) CHECK(snd_pcm_hw_params_any(mPcmHandle, hp.get())); @@ -973,11 +1002,11 @@ void AlsaCapture::open(std::string_view name) /* set channels (implicitly sets frame bits) */ CHECK(snd_pcm_hw_params_set_channels(mPcmHandle, hp.get(), mDevice->channelsFromFmt())); /* set rate (implicitly constrains period/buffer parameters) */ - CHECK(snd_pcm_hw_params_set_rate(mPcmHandle, hp.get(), mDevice->Frequency, 0)); + CHECK(snd_pcm_hw_params_set_rate(mPcmHandle, hp.get(), mDevice->mSampleRate, 0)); /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */ if(snd_pcm_hw_params_set_buffer_size_min(mPcmHandle, hp.get(), &bufferSizeInFrames) < 0) { - TRACE("Buffer too large, using intermediate ring buffer\n"); + TRACE("Buffer too large, using intermediate ring buffer"); needring = true; CHECK(snd_pcm_hw_params_set_buffer_size_near(mPcmHandle, hp.get(), &bufferSizeInFrames)); } @@ -991,22 +1020,20 @@ void AlsaCapture::open(std::string_view name) hp = nullptr; if(needring) - mRing = RingBuffer::Create(mDevice->BufferSize, mDevice->frameSizeFromFmt(), false); + mRing = RingBuffer::Create(mDevice->mBufferSize, mDevice->frameSizeFromFmt(), false); - mDevice->DeviceName = name; + mDeviceName = name; } void AlsaCapture::start() { - int err{snd_pcm_prepare(mPcmHandle)}; - if(err < 0) - throw al::backend_exception{al::backend_error::DeviceError, "snd_pcm_prepare failed: %s", + if(int err{snd_pcm_prepare(mPcmHandle)}; err < 0) + throw al::backend_exception{al::backend_error::DeviceError, "snd_pcm_prepare failed: {}", snd_strerror(err)}; - err = snd_pcm_start(mPcmHandle); - if(err < 0) - throw al::backend_exception{al::backend_error::DeviceError, "snd_pcm_start failed: %s", + if(int err{snd_pcm_start(mPcmHandle)}; err < 0) + throw al::backend_exception{al::backend_error::DeviceError, "snd_pcm_start failed: {}", snd_strerror(err)}; mDoCapture = true; @@ -1029,9 +1056,8 @@ void AlsaCapture::stop() captureSamples(temp.data(), avail); mBuffer = std::move(temp); } - int err{snd_pcm_drop(mPcmHandle)}; - if(err < 0) - ERR("drop failed: %s\n", snd_strerror(err)); + if(int err{snd_pcm_drop(mPcmHandle)}; err < 0) + ERR("snd_pcm_drop failed: {}", snd_strerror(err)); mDoCapture = false; } @@ -1039,10 +1065,13 @@ void AlsaCapture::captureSamples(std::byte *buffer, uint samples) { if(mRing) { - mRing->read(buffer, samples); + std::ignore = mRing->read(buffer, samples); return; } + const auto outspan = al::span{buffer, + static_cast(snd_pcm_frames_to_bytes(mPcmHandle, samples))}; + auto outiter = outspan.begin(); mLastAvail -= samples; while(mDevice->Connected.load(std::memory_order_acquire) && samples > 0) { @@ -1055,20 +1084,21 @@ void AlsaCapture::captureSamples(std::byte *buffer, uint samples) if(static_cast(amt) > samples) amt = samples; amt = snd_pcm_frames_to_bytes(mPcmHandle, amt); - std::copy_n(mBuffer.begin(), amt, buffer); + std::copy_n(mBuffer.begin(), amt, outiter); mBuffer.erase(mBuffer.begin(), mBuffer.begin()+amt); amt = snd_pcm_bytes_to_frames(mPcmHandle, amt); } else if(mDoCapture) - amt = snd_pcm_readi(mPcmHandle, buffer, samples); + amt = snd_pcm_readi(mPcmHandle, al::to_address(outiter), samples); if(amt < 0) { - ERR("read error: %s\n", snd_strerror(static_cast(amt))); + ERR("read error: {}", snd_strerror(static_cast(amt))); if(amt == -EAGAIN) continue; - if((amt=snd_pcm_recover(mPcmHandle, static_cast(amt), 1)) >= 0) + amt = snd_pcm_recover(mPcmHandle, static_cast(amt), 1); + if(amt >= 0) { amt = snd_pcm_start(mPcmHandle); if(amt >= 0) @@ -1077,8 +1107,8 @@ void AlsaCapture::captureSamples(std::byte *buffer, uint samples) if(amt < 0) { const char *err{snd_strerror(static_cast(amt))}; - ERR("restore error: %s\n", err); - mDevice->handleDisconnect("Capture recovery failure: %s", err); + ERR("restore error: {}", err); + mDevice->handleDisconnect("Capture recovery failure: {}", err); break; } /* If the amount available is less than what's asked, we lost it @@ -1088,11 +1118,11 @@ void AlsaCapture::captureSamples(std::byte *buffer, uint samples) continue; } - buffer = buffer + amt; + outiter += amt; samples -= static_cast(amt); } if(samples > 0) - std::fill_n(buffer, snd_pcm_frames_to_bytes(mPcmHandle, samples), + std::fill_n(outiter, snd_pcm_frames_to_bytes(mPcmHandle, samples), std::byte((mDevice->FmtType == DevFmtUByte) ? 0x80 : 0)); } @@ -1103,9 +1133,10 @@ uint AlsaCapture::availableSamples() avail = snd_pcm_avail_update(mPcmHandle); if(avail < 0) { - ERR("avail update failed: %s\n", snd_strerror(static_cast(avail))); + ERR("snd_pcm_avail_update failed: {}", snd_strerror(static_cast(avail))); - if((avail=snd_pcm_recover(mPcmHandle, static_cast(avail), 1)) >= 0) + avail = snd_pcm_recover(mPcmHandle, static_cast(avail), 1); + if(avail >= 0) { if(mDoCapture) avail = snd_pcm_start(mPcmHandle); @@ -1115,33 +1146,34 @@ uint AlsaCapture::availableSamples() if(avail < 0) { const char *err{snd_strerror(static_cast(avail))}; - ERR("restore error: %s\n", err); - mDevice->handleDisconnect("Capture recovery failure: %s", err); + ERR("restore error: {}", err); + mDevice->handleDisconnect("Capture recovery failure: {}", err); } } if(!mRing) { - if(avail < 0) avail = 0; + avail = std::max(avail, 0); avail += snd_pcm_bytes_to_frames(mPcmHandle, static_cast(mBuffer.size())); - if(avail > mLastAvail) mLastAvail = avail; + mLastAvail = std::max(mLastAvail, avail); return static_cast(mLastAvail); } while(avail > 0) { auto vec = mRing->getWriteVector(); - if(vec.first.len == 0) break; + if(vec[0].len == 0) break; - snd_pcm_sframes_t amt{std::min(static_cast(vec.first.len), avail)}; - amt = snd_pcm_readi(mPcmHandle, vec.first.buf, static_cast(amt)); + snd_pcm_sframes_t amt{std::min(static_cast(vec[0].len), avail)}; + amt = snd_pcm_readi(mPcmHandle, vec[0].buf, static_cast(amt)); if(amt < 0) { - ERR("read error: %s\n", snd_strerror(static_cast(amt))); + ERR("read error: {}", snd_strerror(static_cast(amt))); if(amt == -EAGAIN) continue; - if((amt=snd_pcm_recover(mPcmHandle, static_cast(amt), 1)) >= 0) + amt = snd_pcm_recover(mPcmHandle, static_cast(amt), 1); + if(amt >= 0) { if(mDoCapture) amt = snd_pcm_start(mPcmHandle); @@ -1151,8 +1183,8 @@ uint AlsaCapture::availableSamples() if(amt < 0) { const char *err{snd_strerror(static_cast(amt))}; - ERR("restore error: %s\n", err); - mDevice->handleDisconnect("Capture recovery failure: %s", err); + ERR("restore error: {}", err); + mDevice->handleDisconnect("Capture recovery failure: {}", err); break; } avail = amt; @@ -1168,18 +1200,17 @@ uint AlsaCapture::availableSamples() ClockLatency AlsaCapture::getClockLatency() { - ClockLatency ret; - - ret.ClockTime = GetDeviceClockTime(mDevice); + ClockLatency ret{}; + ret.ClockTime = mDevice->getClockTime(); snd_pcm_sframes_t delay{}; int err{snd_pcm_delay(mPcmHandle, &delay)}; if(err < 0) { - ERR("Failed to get pcm delay: %s\n", snd_strerror(err)); + ERR("Failed to get pcm delay: {}", snd_strerror(err)); delay = 0; } ret.Latency = std::chrono::seconds{std::max(0, delay)}; - ret.Latency /= mDevice->Frequency; + ret.Latency /= mDevice->mSampleRate; return ret; } @@ -1189,66 +1220,57 @@ ClockLatency AlsaCapture::getClockLatency() bool AlsaBackendFactory::init() { - bool error{false}; - -#ifdef HAVE_DYNLOAD +#if HAVE_DYNLOAD if(!alsa_handle) { - std::string missing_funcs; - alsa_handle = LoadLib("libasound.so.2"); if(!alsa_handle) { - WARN("Failed to load %s\n", "libasound.so.2"); + WARN("Failed to load {}", "libasound.so.2"); return false; } - error = false; + std::string missing_funcs; #define LOAD_FUNC(f) do { \ - p##f = al::bit_cast(GetSymbol(alsa_handle, #f)); \ - if(p##f == nullptr) { \ - error = true; \ - missing_funcs += "\n" #f; \ - } \ + p##f = reinterpret_cast(GetSymbol(alsa_handle, #f)); \ + if(p##f == nullptr) missing_funcs += "\n" #f; \ } while(0) ALSA_FUNCS(LOAD_FUNC); #undef LOAD_FUNC - if(error) + if(!missing_funcs.empty()) { - WARN("Missing expected functions:%s\n", missing_funcs.c_str()); + WARN("Missing expected functions:{}", missing_funcs); CloseLib(alsa_handle); alsa_handle = nullptr; + return false; } } #endif - return !error; + return true; } bool AlsaBackendFactory::querySupport(BackendType type) { return (type == BackendType::Playback || type == BackendType::Capture); } -std::string AlsaBackendFactory::probe(BackendType type) +auto AlsaBackendFactory::enumerate(BackendType type) -> std::vector { - std::string outnames; - + std::vector outnames; auto add_device = [&outnames](const DevMap &entry) -> void - { - /* +1 to also append the null char (to ensure a null-separated list and - * double-null terminated list). - */ - outnames.append(entry.name.c_str(), entry.name.length()+1); - }; + { outnames.emplace_back(entry.name); }; + switch(type) { case BackendType::Playback: PlaybackDevices = probe_devices(SND_PCM_STREAM_PLAYBACK); + outnames.reserve(PlaybackDevices.size()); std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device); break; case BackendType::Capture: CaptureDevices = probe_devices(SND_PCM_STREAM_CAPTURE); + outnames.reserve(CaptureDevices.size()); std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device); break; } diff --git a/3rdparty/openal/alc/backends/alsa.h b/3rdparty/openal/alc/backends/alsa.h index b256dcf5a996..1327db8a4e82 100644 --- a/3rdparty/openal/alc/backends/alsa.h +++ b/3rdparty/openal/alc/backends/alsa.h @@ -5,15 +5,15 @@ struct AlsaBackendFactory final : public BackendFactory { public: - bool init() override; + auto init() -> bool final; - bool querySupport(BackendType type) override; + auto querySupport(BackendType type) -> bool final; - std::string probe(BackendType type) override; + auto enumerate(BackendType type) -> std::vector final; - BackendPtr createBackend(DeviceBase *device, BackendType type) override; + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; - static BackendFactory &getFactory(); + static auto getFactory() -> BackendFactory&; }; #endif /* BACKENDS_ALSA_H */ diff --git a/3rdparty/openal/alc/backends/base.cpp b/3rdparty/openal/alc/backends/base.cpp index ab3ad028d1d4..9d9ce4a06074 100644 --- a/3rdparty/openal/alc/backends/base.cpp +++ b/3rdparty/openal/alc/backends/base.cpp @@ -3,34 +3,18 @@ #include "base.h" -#include #include #include +#include -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#include - -#include "albit.h" -#include "core/logging.h" -#endif - -#include "atomic.h" #include "core/devformat.h" namespace al { +auto backend_exception::make_string(fmt::string_view fmt, fmt::format_args args) -> std::string +{ return fmt::vformat(fmt, std::move(args)); } -backend_exception::backend_exception(backend_error code, const char *msg, ...) : mErrorCode{code} -{ - std::va_list args; - va_start(args, msg); - setMessage(msg, args); - va_end(args); -} backend_exception::~backend_exception() = default; - } // namespace al @@ -45,27 +29,26 @@ uint BackendBase::availableSamples() ClockLatency BackendBase::getClockLatency() { - ClockLatency ret; + ClockLatency ret{}; uint refcount; do { refcount = mDevice->waitForMix(); - ret.ClockTime = GetDeviceClockTime(mDevice); + ret.ClockTime = mDevice->getClockTime(); std::atomic_thread_fence(std::memory_order_acquire); - } while(refcount != ReadRef(mDevice->MixCount)); + } while(refcount != mDevice->mMixCount.load(std::memory_order_relaxed)); /* NOTE: The device will generally have about all but one periods filled at * any given time during playback. Without a more accurate measurement from * the output, this is an okay approximation. */ - ret.Latency = std::max(std::chrono::seconds{mDevice->BufferSize-mDevice->UpdateSize}, - std::chrono::seconds::zero()); - ret.Latency /= mDevice->Frequency; + ret.Latency = std::chrono::seconds{mDevice->mBufferSize - mDevice->mUpdateSize}; + ret.Latency /= mDevice->mSampleRate; return ret; } -void BackendBase::setDefaultWFXChannelOrder() +void BackendBase::setDefaultWFXChannelOrder() const { mDevice->RealOut.ChannelIndex.fill(InvalidChannelIndex); @@ -125,6 +108,24 @@ void BackendBase::setDefaultWFXChannelOrder() mDevice->RealOut.ChannelIndex[TopBackLeft] = 10; mDevice->RealOut.ChannelIndex[TopBackRight] = 11; break; + case DevFmtX7144: + mDevice->RealOut.ChannelIndex[FrontLeft] = 0; + mDevice->RealOut.ChannelIndex[FrontRight] = 1; + mDevice->RealOut.ChannelIndex[FrontCenter] = 2; + mDevice->RealOut.ChannelIndex[LFE] = 3; + mDevice->RealOut.ChannelIndex[BackLeft] = 4; + mDevice->RealOut.ChannelIndex[BackRight] = 5; + mDevice->RealOut.ChannelIndex[SideLeft] = 6; + mDevice->RealOut.ChannelIndex[SideRight] = 7; + mDevice->RealOut.ChannelIndex[TopFrontLeft] = 8; + mDevice->RealOut.ChannelIndex[TopFrontRight] = 9; + mDevice->RealOut.ChannelIndex[TopBackLeft] = 10; + mDevice->RealOut.ChannelIndex[TopBackRight] = 11; + mDevice->RealOut.ChannelIndex[BottomFrontLeft] = 12; + mDevice->RealOut.ChannelIndex[BottomFrontRight] = 13; + mDevice->RealOut.ChannelIndex[BottomBackLeft] = 14; + mDevice->RealOut.ChannelIndex[BottomBackRight] = 15; + break; case DevFmtX3D71: mDevice->RealOut.ChannelIndex[FrontLeft] = 0; mDevice->RealOut.ChannelIndex[FrontRight] = 1; @@ -140,7 +141,7 @@ void BackendBase::setDefaultWFXChannelOrder() } } -void BackendBase::setDefaultChannelOrder() +void BackendBase::setDefaultChannelOrder() const { mDevice->RealOut.ChannelIndex.fill(InvalidChannelIndex); @@ -178,6 +179,24 @@ void BackendBase::setDefaultChannelOrder() mDevice->RealOut.ChannelIndex[TopBackLeft] = 10; mDevice->RealOut.ChannelIndex[TopBackRight] = 11; break; + case DevFmtX7144: + mDevice->RealOut.ChannelIndex[FrontLeft] = 0; + mDevice->RealOut.ChannelIndex[FrontRight] = 1; + mDevice->RealOut.ChannelIndex[BackLeft] = 2; + mDevice->RealOut.ChannelIndex[BackRight] = 3; + mDevice->RealOut.ChannelIndex[FrontCenter] = 4; + mDevice->RealOut.ChannelIndex[LFE] = 5; + mDevice->RealOut.ChannelIndex[SideLeft] = 6; + mDevice->RealOut.ChannelIndex[SideRight] = 7; + mDevice->RealOut.ChannelIndex[TopFrontLeft] = 8; + mDevice->RealOut.ChannelIndex[TopFrontRight] = 9; + mDevice->RealOut.ChannelIndex[TopBackLeft] = 10; + mDevice->RealOut.ChannelIndex[TopBackRight] = 11; + mDevice->RealOut.ChannelIndex[BottomFrontLeft] = 12; + mDevice->RealOut.ChannelIndex[BottomFrontRight] = 13; + mDevice->RealOut.ChannelIndex[BottomBackLeft] = 14; + mDevice->RealOut.ChannelIndex[BottomBackRight] = 15; + break; case DevFmtX3D71: mDevice->RealOut.ChannelIndex[FrontLeft] = 0; mDevice->RealOut.ChannelIndex[FrontRight] = 1; diff --git a/3rdparty/openal/alc/backends/base.h b/3rdparty/openal/alc/backends/base.h index a4079fe42064..4e59836d1961 100644 --- a/3rdparty/openal/alc/backends/base.h +++ b/3rdparty/openal/alc/backends/base.h @@ -5,12 +5,14 @@ #include #include #include -#include #include #include +#include +#include "alc/events.h" #include "core/device.h" #include "core/except.h" +#include "fmt/core.h" using uint = unsigned int; @@ -33,15 +35,22 @@ struct BackendBase { virtual ClockLatency getClockLatency(); DeviceBase *const mDevice; + std::string mDeviceName; - BackendBase(DeviceBase *device) noexcept : mDevice{device} { } + BackendBase() = delete; + BackendBase(const BackendBase&) = delete; + BackendBase(BackendBase&&) = delete; + explicit BackendBase(DeviceBase *device) noexcept : mDevice{device} { } virtual ~BackendBase() = default; + void operator=(const BackendBase&) = delete; + void operator=(BackendBase&&) = delete; + protected: /** Sets the default channel order used by most non-WaveFormatEx-based APIs. */ - void setDefaultChannelOrder(); + void setDefaultChannelOrder() const; /** Sets the default channel order used by WaveFormatEx. */ - void setDefaultWFXChannelOrder(); + void setDefaultWFXChannelOrder() const; }; using BackendPtr = std::unique_ptr; @@ -51,18 +60,6 @@ enum class BackendType { }; -/* Helper to get the current clock time from the device's ClockBase, and - * SamplesDone converted from the sample rate. - */ -inline std::chrono::nanoseconds GetDeviceClockTime(DeviceBase *device) -{ - using std::chrono::seconds; - using std::chrono::nanoseconds; - - auto ns = nanoseconds{seconds{device->SamplesDone}} / device->Frequency; - return device->ClockBase + ns; -} - /* Helper to get the device latency from the backend, including any fixed * latency from post-processing. */ @@ -75,16 +72,24 @@ inline ClockLatency GetClockLatency(DeviceBase *device, BackendBase *backend) struct BackendFactory { - virtual bool init() = 0; + BackendFactory() = default; + BackendFactory(const BackendFactory&) = delete; + BackendFactory(BackendFactory&&) = delete; + virtual ~BackendFactory() = default; - virtual bool querySupport(BackendType type) = 0; + void operator=(const BackendFactory&) = delete; + void operator=(BackendFactory&&) = delete; - virtual std::string probe(BackendType type) = 0; + virtual auto init() -> bool = 0; - virtual BackendPtr createBackend(DeviceBase *device, BackendType type) = 0; + virtual auto querySupport(BackendType type) -> bool = 0; -protected: - virtual ~BackendFactory() = default; + virtual auto queryEventSupport(alc::EventType, BackendType) -> alc::EventSupport + { return alc::EventSupport::NoSupport; } + + virtual auto enumerate(BackendType type) -> std::vector = 0; + + virtual auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr = 0; }; namespace al { @@ -98,16 +103,21 @@ enum class backend_error { class backend_exception final : public base_exception { backend_error mErrorCode; + static auto make_string(fmt::string_view fmt, fmt::format_args args) -> std::string; + public: -#ifdef __USE_MINGW_ANSI_STDIO - [[gnu::format(gnu_printf, 3, 4)]] -#else - [[gnu::format(printf, 3, 4)]] -#endif - backend_exception(backend_error code, const char *msg, ...); + template + backend_exception(backend_error code, fmt::format_string fmt, Args&& ...args) + : base_exception{make_string(fmt, fmt::make_format_args(args...))}, mErrorCode{code} + { } + backend_exception(const backend_exception&) = default; + backend_exception(backend_exception&&) = default; ~backend_exception() override; - backend_error errorCode() const noexcept { return mErrorCode; } + backend_exception& operator=(const backend_exception&) = default; + backend_exception& operator=(backend_exception&&) = default; + + [[nodiscard]] auto errorCode() const noexcept -> backend_error { return mErrorCode; } }; } // namespace al diff --git a/3rdparty/openal/alc/backends/coreaudio.cpp b/3rdparty/openal/alc/backends/coreaudio.cpp index 1684545b9665..138859cbca11 100644 --- a/3rdparty/openal/alc/backends/coreaudio.cpp +++ b/3rdparty/openal/alc/backends/coreaudio.cpp @@ -24,7 +24,9 @@ #include #include +#include #include +#include #include #include #include @@ -32,14 +34,14 @@ #include #include #include -#include #include "alnumeric.h" +#include "alstring.h" #include "core/converter.h" #include "core/device.h" #include "core/logging.h" +#include "fmt/core.h" #include "ringbuffer.h" -#include "alc/events.h" #include #include @@ -56,10 +58,40 @@ namespace { constexpr auto OutputElement = 0; constexpr auto InputElement = 1; +// These following arrays should always be defined in ascending AudioChannelLabel value order +constexpr std::array MonoChanMap { kAudioChannelLabel_Mono }; +constexpr std::array StereoChanMap { kAudioChannelLabel_Left, kAudioChannelLabel_Right}; +constexpr std::array QuadChanMap { + kAudioChannelLabel_Left, kAudioChannelLabel_Right, + kAudioChannelLabel_LeftSurround, kAudioChannelLabel_RightSurround +}; +constexpr std::array X51ChanMap { + kAudioChannelLabel_Left, kAudioChannelLabel_Right, + kAudioChannelLabel_Center, kAudioChannelLabel_LFEScreen, + kAudioChannelLabel_LeftSurround, kAudioChannelLabel_RightSurround +}; +constexpr std::array X51RearChanMap { + kAudioChannelLabel_Left, kAudioChannelLabel_Right, + kAudioChannelLabel_Center, kAudioChannelLabel_LFEScreen, + kAudioChannelLabel_RearSurroundRight, kAudioChannelLabel_RearSurroundLeft +}; +constexpr std::array X61ChanMap { + kAudioChannelLabel_Left, kAudioChannelLabel_Right, + kAudioChannelLabel_Center, kAudioChannelLabel_LFEScreen, + kAudioChannelLabel_CenterSurround, + kAudioChannelLabel_RearSurroundRight, kAudioChannelLabel_RearSurroundLeft +}; +constexpr std::array X71ChanMap { + kAudioChannelLabel_Left, kAudioChannelLabel_Right, + kAudioChannelLabel_Center, kAudioChannelLabel_LFEScreen, + kAudioChannelLabel_LeftSurround, kAudioChannelLabel_RightSurround, + kAudioChannelLabel_LeftCenter, kAudioChannelLabel_RightCenter +}; + struct FourCCPrinter { char mString[sizeof(UInt32) + 1]{}; - constexpr FourCCPrinter(UInt32 code) noexcept + explicit constexpr FourCCPrinter(UInt32 code) noexcept { for(size_t i{0};i < sizeof(UInt32);++i) { @@ -73,7 +105,7 @@ struct FourCCPrinter { code >>= 8; } } - constexpr FourCCPrinter(int code) noexcept : FourCCPrinter{static_cast(code)} { } + explicit constexpr FourCCPrinter(OSStatus code) noexcept : FourCCPrinter{static_cast(code)} { } constexpr const char *c_str() const noexcept { return mString; } }; @@ -159,19 +191,19 @@ std::string GetDeviceName(AudioDeviceID devId) /* Clear extraneous nul chars that may have been written with the name * string, and return it. */ - while(!devname.back()) + while(!devname.empty() && !devname.back()) devname.pop_back(); return devname; } -UInt32 GetDeviceChannelCount(AudioDeviceID devId, bool isCapture) +auto GetDeviceChannelCount(AudioDeviceID devId, bool isCapture) -> UInt32 { - UInt32 propSize{}; + auto propSize = UInt32{}; auto err = GetDevPropertySize(devId, kAudioDevicePropertyStreamConfiguration, isCapture, 0, &propSize); if(err) { - ERR("kAudioDevicePropertyStreamConfiguration size query failed: '%s' (%u)\n", + ERR("kAudioDevicePropertyStreamConfiguration size query failed: '{}' ({})", FourCCPrinter{err}.c_str(), err); return 0; } @@ -183,15 +215,14 @@ UInt32 GetDeviceChannelCount(AudioDeviceID devId, bool isCapture) buflist); if(err) { - ERR("kAudioDevicePropertyStreamConfiguration query failed: '%s' (%u)\n", + ERR("kAudioDevicePropertyStreamConfiguration query failed: '{}' ({})", FourCCPrinter{err}.c_str(), err); return 0; } - UInt32 numChannels{0}; + auto numChannels = UInt32{0}; for(size_t i{0};i < buflist->mNumberBuffers;++i) numChannels += buflist->mBuffers[i].mNumberChannels; - return numChannels; } @@ -201,14 +232,14 @@ void EnumerateDevices(std::vector &list, bool isCapture) UInt32 propSize{}; if(auto err = GetHwPropertySize(kAudioHardwarePropertyDevices, &propSize)) { - ERR("Failed to get device list size: %u\n", err); + ERR("Failed to get device list size: {}", err); return; } auto devIds = std::vector(propSize/sizeof(AudioDeviceID), kAudioDeviceUnknown); if(auto err = GetHwProperty(kAudioHardwarePropertyDevices, propSize, devIds.data())) { - ERR("Failed to get device list: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err); + ERR("Failed to get device list: '{}' ({})", FourCCPrinter{err}.c_str(), err); return; } @@ -223,7 +254,7 @@ void EnumerateDevices(std::vector &list, bool isCapture) { newdevs.emplace_back(DeviceEntry{defaultId, GetDeviceName(defaultId)}); const auto &entry = newdevs.back(); - TRACE("Got device: %s = ID %u\n", entry.mName.c_str(), entry.mId); + TRACE("Got device: {} = ID {}", entry.mName, entry.mId); } for(const AudioDeviceID devId : devIds) { @@ -240,7 +271,7 @@ void EnumerateDevices(std::vector &list, bool isCapture) { newdevs.emplace_back(DeviceEntry{devId, GetDeviceName(devId)}); const auto &entry = newdevs.back(); - TRACE("Got device: %s = ID %u\n", entry.mName.c_str(), entry.mId); + TRACE("Got device: {} = ID {}", entry.mName, entry.mId); } } @@ -255,14 +286,12 @@ void EnumerateDevices(std::vector &list, bool isCapture) { return entry.mName == curitem->mName; }; if(std::find_if(newdevs.begin(), curitem, check_match) != curitem) { - std::string name{curitem->mName}; - size_t count{1}; + auto name = std::string{curitem->mName}; + auto count = 1_uz; auto check_name = [&name](const DeviceEntry &entry) -> bool { return entry.mName == name; }; do { - name = curitem->mName; - name += " #"; - name += std::to_string(++count); + name = fmt::format("{} #{}", curitem->mName, ++count); } while(std::find_if(newdevs.begin(), curitem, check_name) != curitem); curitem->mName = std::move(name); } @@ -277,18 +306,18 @@ struct DeviceHelper { DeviceHelper() { AudioObjectPropertyAddress addr{kAudioHardwarePropertyDefaultOutputDevice, - kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain}; + kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster}; OSStatus status = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &addr, DeviceListenerProc, nil); if (status != noErr) - ERR("AudioObjectAddPropertyListener fail: %d", status); + ERR("AudioObjectAddPropertyListener fail: {}", status); } ~DeviceHelper() { AudioObjectPropertyAddress addr{kAudioHardwarePropertyDefaultOutputDevice, - kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain}; + kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster}; OSStatus status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &addr, DeviceListenerProc, nil); if (status != noErr) - ERR("AudioObjectRemovePropertyListener fail: %d", status); + ERR("AudioObjectRemovePropertyListener fail: {}", status); } static OSStatus DeviceListenerProc(AudioObjectID /*inObjectID*/, UInt32 inNumberAddresses, @@ -322,7 +351,7 @@ static constexpr char ca_device[] = "CoreAudio Default"; struct CoreAudioPlayback final : public BackendBase { - CoreAudioPlayback(DeviceBase *device) noexcept : BackendBase{device} { } + explicit CoreAudioPlayback(DeviceBase *device) noexcept : BackendBase{device} { } ~CoreAudioPlayback() override; OSStatus MixerProc(AudioUnitRenderActionFlags *ioActionFlags, @@ -338,8 +367,6 @@ struct CoreAudioPlayback final : public BackendBase { uint mFrameSize{0u}; AudioStreamBasicDescription mFormat{}; // This is the OpenAL format as a CoreAudio ASBD - - DEF_NEWDEL(CoreAudioPlayback) }; CoreAudioPlayback::~CoreAudioPlayback() @@ -379,7 +406,7 @@ void CoreAudioPlayback::open(std::string_view name) auto devmatch = std::find_if(PlaybackList.cbegin(), PlaybackList.cend(), find_name); if(devmatch == PlaybackList.cend()) throw al::backend_exception{al::backend_error::NoDevice, - "Device name \"%.*s\" not found", static_cast(name.length()), name.data()}; + "Device name \"{}\" not found", name}; audioDevice = devmatch->mId; } @@ -387,8 +414,8 @@ void CoreAudioPlayback::open(std::string_view name) if(name.empty()) name = ca_device; else if(name != ca_device) - throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", - static_cast(name.length()), name.data()}; + throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found", + name}; #endif /* open the default output unit */ @@ -412,7 +439,7 @@ void CoreAudioPlayback::open(std::string_view name) OSStatus err{AudioComponentInstanceNew(comp, &audioUnit)}; if(err != noErr) throw al::backend_exception{al::backend_error::NoDevice, - "Could not create component instance: '%s' (%u)", FourCCPrinter{err}.c_str(), err}; + "Could not create component instance: '{}' ({})", FourCCPrinter{err}.c_str(), err}; #if CAN_ENUMERATE if(audioDevice != kAudioDeviceUnknown) @@ -423,7 +450,7 @@ void CoreAudioPlayback::open(std::string_view name) err = AudioUnitInitialize(audioUnit); if(err != noErr) throw al::backend_exception{al::backend_error::DeviceError, - "Could not initialize audio unit: '%s' (%u)", FourCCPrinter{err}.c_str(), err}; + "Could not initialize audio unit: '{}' ({})", FourCCPrinter{err}.c_str(), err}; /* WARNING: I don't know if "valid" audio unit values are guaranteed to be * non-0. If not, this logic is broken. @@ -437,7 +464,7 @@ void CoreAudioPlayback::open(std::string_view name) #if CAN_ENUMERATE if(!name.empty()) - mDevice->DeviceName = name; + mDeviceName = name; else { UInt32 propSize{sizeof(audioDevice)}; @@ -446,8 +473,8 @@ void CoreAudioPlayback::open(std::string_view name) kAudioUnitScope_Global, OutputElement, &audioDevice, &propSize); std::string devname{GetDeviceName(audioDevice)}; - if(!devname.empty()) mDevice->DeviceName = std::move(devname); - else mDevice->DeviceName = "Unknown Device Name"; + if(!devname.empty()) mDeviceName = std::move(devname); + else mDeviceName = "Unknown Device Name"; } if(audioDevice != kAudioDeviceUnknown) @@ -456,16 +483,16 @@ void CoreAudioPlayback::open(std::string_view name) err = GetDevProperty(audioDevice, kAudioDevicePropertyDataSource, false, kAudioObjectPropertyElementMaster, sizeof(type), &type); if(err != noErr) - ERR("Failed to get audio device type: %u\n", err); + WARN("Failed to get audio device type: '{}' ({})", FourCCPrinter{err}.c_str(), err); else { - TRACE("Got device type '%s'\n", FourCCPrinter{type}.c_str()); + TRACE("Got device type '{}'", FourCCPrinter{type}.c_str()); mDevice->Flags.set(DirectEar, (type == kIOAudioOutputPortSubTypeHeadphones)); } } #else - mDevice->DeviceName = name; + mDeviceName = name; #endif } @@ -473,7 +500,7 @@ bool CoreAudioPlayback::reset() { OSStatus err{AudioUnitUninitialize(mAudioUnit)}; if(err != noErr) - ERR("AudioUnitUninitialize failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err); + ERR("AudioUnitUninitialize failed: '{}' ({})", FourCCPrinter{err}.c_str(), err); /* retrieve default output unit's properties (output side) */ AudioStreamBasicDescription streamFormat{}; @@ -482,33 +509,75 @@ bool CoreAudioPlayback::reset() OutputElement, &streamFormat, &size); if(err != noErr || size != sizeof(streamFormat)) { - ERR("AudioUnitGetProperty(StreamFormat) failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(), + ERR("AudioUnitGetProperty(StreamFormat) failed: '{}' ({})", FourCCPrinter{err}.c_str(), err); return false; } -#if 0 - TRACE("Output streamFormat of default output unit -\n"); - TRACE(" streamFormat.mFramesPerPacket = %d\n", streamFormat.mFramesPerPacket); - TRACE(" streamFormat.mChannelsPerFrame = %d\n", streamFormat.mChannelsPerFrame); - TRACE(" streamFormat.mBitsPerChannel = %d\n", streamFormat.mBitsPerChannel); - TRACE(" streamFormat.mBytesPerPacket = %d\n", streamFormat.mBytesPerPacket); - TRACE(" streamFormat.mBytesPerFrame = %d\n", streamFormat.mBytesPerFrame); - TRACE(" streamFormat.mSampleRate = %5.0f\n", streamFormat.mSampleRate); -#endif - /* Use the sample rate from the output unit's current parameters, but reset * everything else. */ - if(mDevice->Frequency != streamFormat.mSampleRate) + if(mDevice->mSampleRate != streamFormat.mSampleRate) { - mDevice->BufferSize = static_cast(mDevice->BufferSize*streamFormat.mSampleRate/ - mDevice->Frequency + 0.5); - mDevice->Frequency = static_cast(streamFormat.mSampleRate); + mDevice->mBufferSize = static_cast(mDevice->mBufferSize*streamFormat.mSampleRate/ + mDevice->mSampleRate + 0.5); + mDevice->mSampleRate = static_cast(streamFormat.mSampleRate); } - /* FIXME: How to tell what channels are what in the output device, and how - * to specify what we're giving? e.g. 6.0 vs 5.1 + struct ChannelMap { + DevFmtChannels fmt; + al::span map; + bool is_51rear; + }; + + static constexpr std::array chanmaps{{ + { DevFmtX71, X71ChanMap, false }, + { DevFmtX61, X61ChanMap, false }, + { DevFmtX51, X51ChanMap, false }, + { DevFmtX51, X51RearChanMap, true }, + { DevFmtQuad, QuadChanMap, false }, + { DevFmtStereo, StereoChanMap, false }, + { DevFmtMono, MonoChanMap, false } + }}; + + if(!mDevice->Flags.test(ChannelsRequest)) + { + auto propSize = UInt32{}; + auto writable = Boolean{}; + + err = AudioUnitGetPropertyInfo(mAudioUnit, kAudioUnitProperty_AudioChannelLayout, + kAudioUnitScope_Output, OutputElement, &propSize, &writable); + if(err == noErr) + { + auto layout_data = std::make_unique(propSize); + auto *layout = reinterpret_cast(layout_data.get()); + + err = AudioUnitGetProperty(mAudioUnit, kAudioUnitProperty_AudioChannelLayout, + kAudioUnitScope_Output, OutputElement, layout, &propSize); + if(err == noErr) + { + auto descs = al::span{std::data(layout->mChannelDescriptions), + layout->mNumberChannelDescriptions}; + auto labels = std::vector(descs.size()); + + std::transform(descs.begin(), descs.end(), labels.begin(), + std::mem_fn(&AudioChannelDescription::mChannelLabel)); + sort(labels.begin(), labels.end()); + + auto check_labels = [&labels](const ChannelMap &chanmap) -> bool + { + return std::includes(labels.begin(), labels.end(), chanmap.map.begin(), + chanmap.map.end()); + }; + auto chaniter = std::find_if(chanmaps.cbegin(), chanmaps.cend(), check_labels); + if(chaniter != chanmaps.cend()) + mDevice->FmtChans = chaniter->fmt; + } + } + } + + /* TODO: Also set kAudioUnitProperty_AudioChannelLayout according to the AL + * device's channel configuration. */ streamFormat.mChannelsPerFrame = mDevice->channelsFromFmt(); @@ -550,7 +619,7 @@ bool CoreAudioPlayback::reset() OutputElement, &streamFormat, sizeof(streamFormat)); if(err != noErr) { - ERR("AudioUnitSetProperty(StreamFormat) failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(), + ERR("AudioUnitSetProperty(StreamFormat) failed: '{}' ({})", FourCCPrinter{err}.c_str(), err); return false; } @@ -568,7 +637,7 @@ bool CoreAudioPlayback::reset() kAudioUnitScope_Input, OutputElement, &input, sizeof(AURenderCallbackStruct)); if(err != noErr) { - ERR("AudioUnitSetProperty(SetRenderCallback) failed: '%s' (%u)\n", + ERR("AudioUnitSetProperty(SetRenderCallback) failed: '{}' ({})", FourCCPrinter{err}.c_str(), err); return false; } @@ -577,7 +646,7 @@ bool CoreAudioPlayback::reset() err = AudioUnitInitialize(mAudioUnit); if(err != noErr) { - ERR("AudioUnitInitialize failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err); + ERR("AudioUnitInitialize failed: '{}' ({})", FourCCPrinter{err}.c_str(), err); return false; } @@ -589,19 +658,19 @@ void CoreAudioPlayback::start() const OSStatus err{AudioOutputUnitStart(mAudioUnit)}; if(err != noErr) throw al::backend_exception{al::backend_error::DeviceError, - "AudioOutputUnitStart failed: '%s' (%u)", FourCCPrinter{err}.c_str(), err}; + "AudioOutputUnitStart failed: '{}' ({})", FourCCPrinter{err}.c_str(), err}; } void CoreAudioPlayback::stop() { OSStatus err{AudioOutputUnitStop(mAudioUnit)}; if(err != noErr) - ERR("AudioOutputUnitStop failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err); + ERR("AudioOutputUnitStop failed: '{}' ({})", FourCCPrinter{err}.c_str(), err); } struct CoreAudioCapture final : public BackendBase { - CoreAudioCapture(DeviceBase *device) noexcept : BackendBase{device} { } + explicit CoreAudioCapture(DeviceBase *device) noexcept : BackendBase{device} { } ~CoreAudioCapture() override; OSStatus RecordProc(AudioUnitRenderActionFlags *ioActionFlags, @@ -624,8 +693,6 @@ struct CoreAudioCapture final : public BackendBase { std::vector mCaptureData; RingBufferPtr mRing{nullptr}; - - DEF_NEWDEL(CoreAudioCapture) }; CoreAudioCapture::~CoreAudioCapture() @@ -641,7 +708,7 @@ OSStatus CoreAudioCapture::RecordProc(AudioUnitRenderActionFlags *ioActionFlags, AudioBufferList*) noexcept { union { - std::byte _[maxz(sizeof(AudioBufferList), offsetof(AudioBufferList, mBuffers[1]))]; + std::byte buf[std::max(sizeof(AudioBufferList), offsetof(AudioBufferList, mBuffers[1]))]; AudioBufferList list; } audiobuf{}; @@ -654,11 +721,11 @@ OSStatus CoreAudioCapture::RecordProc(AudioUnitRenderActionFlags *ioActionFlags, inNumberFrames, &audiobuf.list)}; if(err != noErr) { - ERR("AudioUnitRender capture error: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err); + ERR("AudioUnitRender capture error: '{}' ({})", FourCCPrinter{err}.c_str(), err); return err; } - mRing->write(mCaptureData.data(), inNumberFrames); + std::ignore = mRing->write(mCaptureData.data(), inNumberFrames); return noErr; } @@ -680,7 +747,7 @@ void CoreAudioCapture::open(std::string_view name) auto devmatch = std::find_if(CaptureList.cbegin(), CaptureList.cend(), find_name); if(devmatch == CaptureList.cend()) throw al::backend_exception{al::backend_error::NoDevice, - "Device name \"%.*s\" not found", static_cast(name.length()), name.data()}; + "Device name \"{}\" not found", name}; audioDevice = devmatch->mId; } @@ -688,8 +755,8 @@ void CoreAudioCapture::open(std::string_view name) if(name.empty()) name = ca_device; else if(name != ca_device) - throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", - static_cast(name.length()), name.data()}; + throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found", + name}; #endif AudioComponentDescription desc{}; @@ -713,7 +780,7 @@ void CoreAudioCapture::open(std::string_view name) OSStatus err{AudioComponentInstanceNew(comp, &mAudioUnit)}; if(err != noErr) throw al::backend_exception{al::backend_error::NoDevice, - "Could not create component instance: '%s' (%u)", FourCCPrinter{err}.c_str(), err}; + "Could not create component instance: '{}' ({})", FourCCPrinter{err}.c_str(), err}; // Turn off AudioUnit output UInt32 enableIO{0}; @@ -721,7 +788,7 @@ void CoreAudioCapture::open(std::string_view name) kAudioUnitScope_Output, OutputElement, &enableIO, sizeof(enableIO)); if(err != noErr) throw al::backend_exception{al::backend_error::DeviceError, - "Could not disable audio unit output property: '%s' (%u)", FourCCPrinter{err}.c_str(), + "Could not disable audio unit output property: '{}' ({})", FourCCPrinter{err}.c_str(), err}; // Turn on AudioUnit input @@ -730,7 +797,7 @@ void CoreAudioCapture::open(std::string_view name) kAudioUnitScope_Input, InputElement, &enableIO, sizeof(enableIO)); if(err != noErr) throw al::backend_exception{al::backend_error::DeviceError, - "Could not enable audio unit input property: '%s' (%u)", FourCCPrinter{err}.c_str(), + "Could not enable audio unit input property: '{}' ({})", FourCCPrinter{err}.c_str(), err}; #if CAN_ENUMERATE @@ -749,7 +816,7 @@ void CoreAudioCapture::open(std::string_view name) kAudioUnitScope_Global, InputElement, &input, sizeof(AURenderCallbackStruct)); if(err != noErr) throw al::backend_exception{al::backend_error::DeviceError, - "Could not set capture callback: '%s' (%u)", FourCCPrinter{err}.c_str(), err}; + "Could not set capture callback: '{}' ({})", FourCCPrinter{err}.c_str(), err}; // Disable buffer allocation for capture UInt32 flag{0}; @@ -757,14 +824,14 @@ void CoreAudioCapture::open(std::string_view name) kAudioUnitScope_Output, InputElement, &flag, sizeof(flag)); if(err != noErr) throw al::backend_exception{al::backend_error::DeviceError, - "Could not disable buffer allocation property: '%s' (%u)", FourCCPrinter{err}.c_str(), + "Could not disable buffer allocation property: '{}' ({})", FourCCPrinter{err}.c_str(), err}; // Initialize the device err = AudioUnitInitialize(mAudioUnit); if(err != noErr) throw al::backend_exception{al::backend_error::DeviceError, - "Could not initialize audio unit: '%s' (%u)", FourCCPrinter{err}.c_str(), err}; + "Could not initialize audio unit: '{}' ({})", FourCCPrinter{err}.c_str(), err}; // Get the hardware format AudioStreamBasicDescription hardwareFormat{}; @@ -773,7 +840,7 @@ void CoreAudioCapture::open(std::string_view name) InputElement, &hardwareFormat, &propertySize); if(err != noErr || propertySize != sizeof(hardwareFormat)) throw al::backend_exception{al::backend_error::DeviceError, - "Could not get input format: '%s' (%u)", FourCCPrinter{err}.c_str(), err}; + "Could not get input format: '{}' ({})", FourCCPrinter{err}.c_str(), err}; // Set up the requested format description AudioStreamBasicDescription requestedFormat{}; @@ -826,15 +893,16 @@ void CoreAudioCapture::open(std::string_view name) case DevFmtX61: case DevFmtX71: case DevFmtX714: + case DevFmtX7144: case DevFmtX3D71: case DevFmtAmbi3D: - throw al::backend_exception{al::backend_error::DeviceError, "%s not supported", + throw al::backend_exception{al::backend_error::DeviceError, "{} not supported", DevFmtChannelsString(mDevice->FmtChans)}; } requestedFormat.mBytesPerFrame = requestedFormat.mChannelsPerFrame * requestedFormat.mBitsPerChannel / 8; requestedFormat.mBytesPerPacket = requestedFormat.mBytesPerFrame; - requestedFormat.mSampleRate = mDevice->Frequency; + requestedFormat.mSampleRate = mDevice->mSampleRate; requestedFormat.mFormatID = kAudioFormatLinearPCM; requestedFormat.mReserved = 0; requestedFormat.mFramesPerPacket = 1; @@ -854,18 +922,18 @@ void CoreAudioCapture::open(std::string_view name) InputElement, &outputFormat, sizeof(outputFormat)); if(err != noErr) throw al::backend_exception{al::backend_error::DeviceError, - "Could not set input format: '%s' (%u)", FourCCPrinter{err}.c_str(), err}; + "Could not set input format: '{}' ({})", FourCCPrinter{err}.c_str(), err}; /* Calculate the minimum AudioUnit output format frame count for the pre- * conversion ring buffer. Ensure at least 100ms for the total buffer. */ - double srateScale{outputFormat.mSampleRate / mDevice->Frequency}; - auto FrameCount64 = maxu64(static_cast(std::ceil(mDevice->BufferSize*srateScale)), - static_cast(outputFormat.mSampleRate)/10); + double srateScale{outputFormat.mSampleRate / mDevice->mSampleRate}; + auto FrameCount64 = std::max(static_cast(std::ceil(mDevice->mBufferSize*srateScale)), + static_cast(outputFormat.mSampleRate)/10_u64); FrameCount64 += MaxResamplerPadding; if(FrameCount64 > std::numeric_limits::max()) throw al::backend_exception{al::backend_error::DeviceError, - "Calculated frame count is too large: %" PRIu64, FrameCount64}; + "Calculated frame count is too large: {}", FrameCount64}; UInt32 outputFrameCount{}; propertySize = sizeof(outputFrameCount); @@ -873,22 +941,22 @@ void CoreAudioCapture::open(std::string_view name) kAudioUnitScope_Global, OutputElement, &outputFrameCount, &propertySize); if(err != noErr || propertySize != sizeof(outputFrameCount)) throw al::backend_exception{al::backend_error::DeviceError, - "Could not get input frame count: '%s' (%u)", FourCCPrinter{err}.c_str(), err}; + "Could not get input frame count: '{}' ({})", FourCCPrinter{err}.c_str(), err}; mCaptureData.resize(outputFrameCount * mFrameSize); - outputFrameCount = static_cast(maxu64(outputFrameCount, FrameCount64)); + outputFrameCount = static_cast(std::max(uint64_t{outputFrameCount}, FrameCount64)); mRing = RingBuffer::Create(outputFrameCount, mFrameSize, false); /* Set up sample converter if needed */ - if(outputFormat.mSampleRate != mDevice->Frequency) + if(outputFormat.mSampleRate != mDevice->mSampleRate) mConverter = SampleConverter::Create(mDevice->FmtType, mDevice->FmtType, mFormat.mChannelsPerFrame, static_cast(hardwareFormat.mSampleRate), - mDevice->Frequency, Resampler::FastBSinc24); + mDevice->mSampleRate, Resampler::FastBSinc24); #if CAN_ENUMERATE if(!name.empty()) - mDevice->DeviceName = name; + mDeviceName = name; else { UInt32 propSize{sizeof(audioDevice)}; @@ -897,11 +965,11 @@ void CoreAudioCapture::open(std::string_view name) kAudioUnitScope_Global, InputElement, &audioDevice, &propSize); std::string devname{GetDeviceName(audioDevice)}; - if(!devname.empty()) mDevice->DeviceName = std::move(devname); - else mDevice->DeviceName = "Unknown Device Name"; + if(!devname.empty()) mDeviceName = std::move(devname); + else mDeviceName = "Unknown Device Name"; } #else - mDevice->DeviceName = name; + mDeviceName = name; #endif } @@ -911,35 +979,35 @@ void CoreAudioCapture::start() OSStatus err{AudioOutputUnitStart(mAudioUnit)}; if(err != noErr) throw al::backend_exception{al::backend_error::DeviceError, - "AudioOutputUnitStart failed: '%s' (%u)", FourCCPrinter{err}.c_str(), err}; + "AudioOutputUnitStart failed: '{}' ({})", FourCCPrinter{err}.c_str(), err}; } void CoreAudioCapture::stop() { OSStatus err{AudioOutputUnitStop(mAudioUnit)}; if(err != noErr) - ERR("AudioOutputUnitStop failed: '%s' (%u)\n", FourCCPrinter{err}.c_str(), err); + ERR("AudioOutputUnitStop failed: '{}' ({})", FourCCPrinter{err}.c_str(), err); } void CoreAudioCapture::captureSamples(std::byte *buffer, uint samples) { if(!mConverter) { - mRing->read(buffer, samples); + std::ignore = mRing->read(buffer, samples); return; } auto rec_vec = mRing->getReadVector(); - const void *src0{rec_vec.first.buf}; - auto src0len = static_cast(rec_vec.first.len); + const void *src0{rec_vec[0].buf}; + auto src0len = static_cast(rec_vec[0].len); uint got{mConverter->convert(&src0, &src0len, buffer, samples)}; - size_t total_read{rec_vec.first.len - src0len}; - if(got < samples && !src0len && rec_vec.second.len > 0) + size_t total_read{rec_vec[0].len - src0len}; + if(got < samples && !src0len && rec_vec[1].len > 0) { - const void *src1{rec_vec.second.buf}; - auto src1len = static_cast(rec_vec.second.len); + const void *src1{rec_vec[1].buf}; + auto src1len = static_cast(rec_vec[1].len); got += mConverter->convert(&src1, &src1len, buffer + got*mFrameSize, samples-got); - total_read += rec_vec.second.len - src1len; + total_read += rec_vec[1].len - src1len; } mRing->readAdvance(total_read); @@ -970,23 +1038,23 @@ bool CoreAudioBackendFactory::init() bool CoreAudioBackendFactory::querySupport(BackendType type) { return type == BackendType::Playback || type == BackendType::Capture; } -std::string CoreAudioBackendFactory::probe(BackendType type) +auto CoreAudioBackendFactory::enumerate(BackendType type) -> std::vector { - std::string outnames; + std::vector outnames; #if CAN_ENUMERATE auto append_name = [&outnames](const DeviceEntry &entry) -> void - { - /* Includes null char. */ - outnames.append(entry.mName.c_str(), entry.mName.length()+1); - }; + { outnames.emplace_back(entry.mName); }; + switch(type) { case BackendType::Playback: EnumerateDevices(PlaybackList, false); + outnames.reserve(PlaybackList.size()); std::for_each(PlaybackList.cbegin(), PlaybackList.cend(), append_name); break; case BackendType::Capture: EnumerateDevices(CaptureList, true); + outnames.reserve(CaptureList.size()); std::for_each(CaptureList.cbegin(), CaptureList.cend(), append_name); break; } @@ -997,8 +1065,7 @@ std::string CoreAudioBackendFactory::probe(BackendType type) { case BackendType::Playback: case BackendType::Capture: - /* Includes null char. */ - outnames.append(ca_device, sizeof(ca_device)); + outnames.emplace_back(ca_device); break; } #endif @@ -1013,3 +1080,18 @@ BackendPtr CoreAudioBackendFactory::createBackend(DeviceBase *device, BackendTyp return BackendPtr{new CoreAudioCapture{device}}; return nullptr; } + +alc::EventSupport CoreAudioBackendFactory::queryEventSupport(alc::EventType eventType, BackendType) +{ + switch(eventType) + { + case alc::EventType::DefaultDeviceChanged: + return alc::EventSupport::FullSupport; + + case alc::EventType::DeviceAdded: + case alc::EventType::DeviceRemoved: + case alc::EventType::Count: + break; + } + return alc::EventSupport::NoSupport; +} diff --git a/3rdparty/openal/alc/backends/coreaudio.h b/3rdparty/openal/alc/backends/coreaudio.h index 1252edde381c..26c2aaf98ad4 100644 --- a/3rdparty/openal/alc/backends/coreaudio.h +++ b/3rdparty/openal/alc/backends/coreaudio.h @@ -5,15 +5,17 @@ struct CoreAudioBackendFactory final : public BackendFactory { public: - bool init() override; + auto init() -> bool final; - bool querySupport(BackendType type) override; + auto querySupport(BackendType type) -> bool final; - std::string probe(BackendType type) override; + auto queryEventSupport(alc::EventType eventType, BackendType type) -> alc::EventSupport final; - BackendPtr createBackend(DeviceBase *device, BackendType type) override; + auto enumerate(BackendType type) -> std::vector final; - static BackendFactory &getFactory(); + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; + + static auto getFactory() -> BackendFactory&; }; #endif /* BACKENDS_COREAUDIO_H */ diff --git a/3rdparty/openal/alc/backends/dsound.cpp b/3rdparty/openal/alc/backends/dsound.cpp index b5596f1c8051..2bb7f5f5c3f2 100644 --- a/3rdparty/openal/alc/backends/dsound.cpp +++ b/3rdparty/openal/alc/backends/dsound.cpp @@ -35,16 +35,13 @@ #include #include #include -#include +#include +#include #include -#include -#include -#include #include #include #include -#include "albit.h" #include "alnumeric.h" #include "alspan.h" #include "althrd_setname.h" @@ -53,6 +50,7 @@ #include "core/helpers.h" #include "core/logging.h" #include "dynload.h" +#include "fmt/core.h" #include "ringbuffer.h" #include "strutils.h" @@ -91,10 +89,7 @@ DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0 namespace { -#define DEVNAME_HEAD "OpenAL Soft on " - - -#ifdef HAVE_DYNLOAD +#if HAVE_DYNLOAD void *ds_handle; HRESULT (WINAPI *pDirectSoundCreate)(const GUID *pcGuidDevice, IDirectSound **ppDS, IUnknown *pUnkOuter); HRESULT (WINAPI *pDirectSoundEnumerateW)(LPDSENUMCALLBACKW pDSEnumCallback, void *pContext); @@ -147,24 +142,19 @@ BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHAR*, voi return TRUE; auto& devices = *static_cast*>(data); - const std::string basename{DEVNAME_HEAD + wstr_to_utf8(desc)}; + const auto basename = wstr_to_utf8(desc); - int count{1}; - std::string newname{basename}; + auto count = 1; + auto newname = basename; while(checkName(devices, newname)) - { - newname = basename; - newname += " #"; - newname += std::to_string(++count); - } - devices.emplace_back(std::move(newname), *guid); - const DevMap &newentry = devices.back(); + newname = fmt::format("{} #{}", basename, ++count); + const DevMap &newentry = devices.emplace_back(std::move(newname), *guid); OLECHAR *guidstr{nullptr}; HRESULT hr{StringFromCLSID(*guid, &guidstr)}; if(SUCCEEDED(hr)) { - TRACE("Got device \"%s\", GUID \"%ls\"\n", newentry.name.c_str(), guidstr); + TRACE("Got device \"{}\", GUID \"{}\"", newentry.name, wstr_to_utf8(guidstr)); CoTaskMemFree(guidstr); } @@ -173,7 +163,7 @@ BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHAR*, voi struct DSoundPlayback final : public BackendBase { - DSoundPlayback(DeviceBase *device) noexcept : BackendBase{device} { } + explicit DSoundPlayback(DeviceBase *device) noexcept : BackendBase{device} { } ~DSoundPlayback() override; int mixerProc(); @@ -191,8 +181,6 @@ struct DSoundPlayback final : public BackendBase { std::atomic mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(DSoundPlayback) }; DSoundPlayback::~DSoundPlayback() @@ -211,21 +199,22 @@ DSoundPlayback::~DSoundPlayback() FORCE_ALIGN int DSoundPlayback::mixerProc() { SetRTPriority(); - althrd_setname(MIXER_THREAD_NAME); + althrd_setname(GetMixerThreadName()); DSBCAPS DSBCaps{}; DSBCaps.dwSize = sizeof(DSBCaps); HRESULT err{mBuffer->GetCaps(&DSBCaps)}; if(FAILED(err)) { - ERR("Failed to get buffer caps: 0x%lx\n", err); - mDevice->handleDisconnect("Failure retrieving playback buffer info: 0x%lx", err); + ERR("Failed to get buffer caps: {:#x}", as_unsigned(err)); + mDevice->handleDisconnect("Failure retrieving playback buffer info: {:#x}", + as_unsigned(err)); return 1; } const size_t FrameStep{mDevice->channelsFromFmt()}; uint FrameSize{mDevice->frameSizeFromFmt()}; - DWORD FragSize{mDevice->UpdateSize * FrameSize}; + DWORD FragSize{mDevice->mUpdateSize * FrameSize}; bool Playing{false}; DWORD LastCursor{0u}; @@ -245,8 +234,9 @@ FORCE_ALIGN int DSoundPlayback::mixerProc() err = mBuffer->Play(0, 0, DSBPLAY_LOOPING); if(FAILED(err)) { - ERR("Failed to play buffer: 0x%lx\n", err); - mDevice->handleDisconnect("Failure starting playback: 0x%lx", err); + ERR("Failed to play buffer: {:#x}", as_unsigned(err)); + mDevice->handleDisconnect("Failure starting playback: {:#x}", + as_unsigned(err)); return 1; } Playing = true; @@ -254,7 +244,7 @@ FORCE_ALIGN int DSoundPlayback::mixerProc() avail = WaitForSingleObjectEx(mNotifyEvent, 2000, FALSE); if(avail != WAIT_OBJECT_0) - ERR("WaitForSingleObjectEx error: 0x%lx\n", avail); + ERR("WaitForSingleObjectEx error: {:#x}", avail); continue; } avail -= avail%FragSize; @@ -267,7 +257,7 @@ FORCE_ALIGN int DSoundPlayback::mixerProc() // If the buffer is lost, restore it and lock if(err == DSERR_BUFFERLOST) { - WARN("Buffer lost, restoring...\n"); + WARN("Buffer lost, restoring..."); err = mBuffer->Restore(); if(SUCCEEDED(err)) { @@ -277,22 +267,19 @@ FORCE_ALIGN int DSoundPlayback::mixerProc() &WritePtr2, &WriteCnt2, 0); } } - - if(SUCCEEDED(err)) + if(FAILED(err)) { - mDevice->renderSamples(WritePtr1, WriteCnt1/FrameSize, FrameStep); - if(WriteCnt2 > 0) - mDevice->renderSamples(WritePtr2, WriteCnt2/FrameSize, FrameStep); - - mBuffer->Unlock(WritePtr1, WriteCnt1, WritePtr2, WriteCnt2); - } - else - { - ERR("Buffer lock error: %#lx\n", err); - mDevice->handleDisconnect("Failed to lock output buffer: 0x%lx", err); + ERR("Buffer lock error: {:#x}", as_unsigned(err)); + mDevice->handleDisconnect("Failed to lock output buffer: {:#x}", as_unsigned(err)); return 1; } + mDevice->renderSamples(WritePtr1, WriteCnt1/FrameSize, FrameStep); + if(WriteCnt2 > 0) + mDevice->renderSamples(WritePtr2, WriteCnt2/FrameSize, FrameStep); + + mBuffer->Unlock(WritePtr1, WriteCnt1, WritePtr2, WriteCnt2); + // Update old write cursor location LastCursor += WriteCnt1+WriteCnt2; LastCursor %= DSBCaps.dwBufferBytes; @@ -307,12 +294,10 @@ void DSoundPlayback::open(std::string_view name) if(PlaybackDevices.empty()) { /* Initialize COM to prevent name truncation */ - HRESULT hrcom{CoInitialize(nullptr)}; + ComWrapper com{}; hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices); if(FAILED(hr)) - ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr); - if(SUCCEEDED(hrcom)) - CoUninitialize(); + ERR("Error enumerating DirectSound devices: {:#x}", as_unsigned(hr)); } const GUID *guid{nullptr}; @@ -334,8 +319,7 @@ void DSoundPlayback::open(std::string_view name) [&id](const DevMap &entry) -> bool { return entry.guid == id; }); if(iter == PlaybackDevices.cend()) throw al::backend_exception{al::backend_error::NoDevice, - "Device name \"%.*s\" not found", static_cast(name.length()), - name.data()}; + "Device name \"{}\" not found", name}; } guid = &iter->guid; } @@ -354,15 +338,15 @@ void DSoundPlayback::open(std::string_view name) if(SUCCEEDED(hr)) hr = ds->SetCooperativeLevel(GetForegroundWindow(), DSSCL_PRIORITY); if(FAILED(hr)) - throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: 0x%08lx", - hr}; + throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: {:#x}", + as_unsigned(hr)}; mNotifies = nullptr; mBuffer = nullptr; mPrimaryBuffer = nullptr; mDS = std::move(ds); - mDevice->DeviceName = name; + mDeviceName = name; } bool DSoundPlayback::reset() @@ -397,7 +381,7 @@ bool DSoundPlayback::reset() HRESULT hr{mDS->GetSpeakerConfig(&speakers)}; if(FAILED(hr)) throw al::backend_exception{al::backend_error::DeviceError, - "Failed to get speaker config: 0x%08lx", hr}; + "Failed to get speaker config: {:#x}", as_unsigned(hr)}; speakers = DSSPEAKER_CONFIG(speakers); if(!mDevice->Flags.test(ChannelsRequest)) @@ -413,7 +397,7 @@ bool DSoundPlayback::reset() else if(speakers == DSSPEAKER_7POINT1 || speakers == DSSPEAKER_7POINT1_SURROUND) mDevice->FmtChans = DevFmtX71; else - ERR("Unknown system speaker config: 0x%lx\n", speakers); + ERR("Unknown system speaker config: {:#x}", speakers); } mDevice->Flags.set(DirectEar, (speakers == DSSPEAKER_HEADPHONE)); const bool isRear51{speakers == DSSPEAKER_5POINT1_BACK}; @@ -428,81 +412,83 @@ bool DSoundPlayback::reset() case DevFmtX51: OutputType.dwChannelMask = isRear51 ? X5DOT1REAR : X5DOT1; break; case DevFmtX61: OutputType.dwChannelMask = X6DOT1; break; case DevFmtX71: OutputType.dwChannelMask = X7DOT1; break; + case DevFmtX7144: mDevice->FmtChans = DevFmtX714; + /* fall-through */ case DevFmtX714: OutputType.dwChannelMask = X7DOT1DOT4; break; case DevFmtX3D71: OutputType.dwChannelMask = X7DOT1; break; } -retry_open: - hr = S_OK; - OutputType.Format.wFormatTag = WAVE_FORMAT_PCM; - OutputType.Format.nChannels = static_cast(mDevice->channelsFromFmt()); - OutputType.Format.wBitsPerSample = static_cast(mDevice->bytesFromFmt() * 8); - OutputType.Format.nBlockAlign = static_cast(OutputType.Format.nChannels * - OutputType.Format.wBitsPerSample / 8); - OutputType.Format.nSamplesPerSec = mDevice->Frequency; - OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec * - OutputType.Format.nBlockAlign; - OutputType.Format.cbSize = 0; - - if(OutputType.Format.nChannels > 2 || mDevice->FmtType == DevFmtFloat) - { - OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; - OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; - OutputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); - if(mDevice->FmtType == DevFmtFloat) - OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; - else - OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + do { + hr = S_OK; + OutputType.Format.wFormatTag = WAVE_FORMAT_PCM; + OutputType.Format.nChannels = static_cast(mDevice->channelsFromFmt()); + OutputType.Format.wBitsPerSample = static_cast(mDevice->bytesFromFmt() * 8); + OutputType.Format.nBlockAlign = static_cast(OutputType.Format.nChannels * + OutputType.Format.wBitsPerSample / 8); + OutputType.Format.nSamplesPerSec = mDevice->mSampleRate; + OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec * + OutputType.Format.nBlockAlign; + OutputType.Format.cbSize = 0; - mPrimaryBuffer = nullptr; - } - else - { - if(SUCCEEDED(hr) && !mPrimaryBuffer) + if(OutputType.Format.nChannels > 2 || mDevice->FmtType == DevFmtFloat) { - DSBUFFERDESC DSBDescription{}; - DSBDescription.dwSize = sizeof(DSBDescription); - DSBDescription.dwFlags = DSBCAPS_PRIMARYBUFFER; - hr = mDS->CreateSoundBuffer(&DSBDescription, al::out_ptr(mPrimaryBuffer), nullptr); + OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) */ + OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; + OutputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); + if(mDevice->FmtType == DevFmtFloat) + OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + else + OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + + mPrimaryBuffer = nullptr; + } + else + { + if(SUCCEEDED(hr) && !mPrimaryBuffer) + { + DSBUFFERDESC DSBDescription{}; + DSBDescription.dwSize = sizeof(DSBDescription); + DSBDescription.dwFlags = DSBCAPS_PRIMARYBUFFER; + hr = mDS->CreateSoundBuffer(&DSBDescription, al::out_ptr(mPrimaryBuffer), nullptr); + } + if(SUCCEEDED(hr)) + hr = mPrimaryBuffer->SetFormat(&OutputType.Format); } - if(SUCCEEDED(hr)) - hr = mPrimaryBuffer->SetFormat(&OutputType.Format); - } - if(SUCCEEDED(hr)) - { - uint num_updates{mDevice->BufferSize / mDevice->UpdateSize}; + if(FAILED(hr)) + break; + + uint num_updates{mDevice->mBufferSize / mDevice->mUpdateSize}; if(num_updates > MAX_UPDATES) num_updates = MAX_UPDATES; - mDevice->BufferSize = mDevice->UpdateSize * num_updates; + mDevice->mBufferSize = mDevice->mUpdateSize * num_updates; DSBUFFERDESC DSBDescription{}; DSBDescription.dwSize = sizeof(DSBDescription); DSBDescription.dwFlags = DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS; - DSBDescription.dwBufferBytes = mDevice->BufferSize * OutputType.Format.nBlockAlign; + DSBDescription.dwBufferBytes = mDevice->mBufferSize * OutputType.Format.nBlockAlign; DSBDescription.lpwfxFormat = &OutputType.Format; hr = mDS->CreateSoundBuffer(&DSBDescription, al::out_ptr(mBuffer), nullptr); - if(FAILED(hr) && mDevice->FmtType == DevFmtFloat) - { - mDevice->FmtType = DevFmtShort; - goto retry_open; - } - } + if(SUCCEEDED(hr) || mDevice->FmtType != DevFmtFloat) + break; + mDevice->FmtType = DevFmtShort; + } while(FAILED(hr)); if(SUCCEEDED(hr)) { hr = mBuffer->QueryInterface(IID_IDirectSoundNotify, al::out_ptr(mNotifies)); if(SUCCEEDED(hr)) { - uint num_updates{mDevice->BufferSize / mDevice->UpdateSize}; + uint num_updates{mDevice->mBufferSize / mDevice->mUpdateSize}; assert(num_updates <= MAX_UPDATES); - std::array nots; + std::array nots{}; for(uint i{0};i < num_updates;++i) { - nots[i].dwOffset = i * mDevice->UpdateSize * OutputType.Format.nBlockAlign; + nots[i].dwOffset = i * mDevice->mUpdateSize * OutputType.Format.nBlockAlign; nots[i].hEventNotify = mNotifyEvent; } if(mNotifies->SetNotificationPositions(num_updates, nots.data()) != DS_OK) @@ -528,11 +514,11 @@ void DSoundPlayback::start() { try { mKillNow.store(false, std::memory_order_release); - mThread = std::thread{std::mem_fn(&DSoundPlayback::mixerProc), this}; + mThread = std::thread{&DSoundPlayback::mixerProc, this}; } catch(std::exception& e) { throw al::backend_exception{al::backend_error::DeviceError, - "Failed to start mixing thread: %s", e.what()}; + "Failed to start mixing thread: {}", e.what()}; } } @@ -547,7 +533,7 @@ void DSoundPlayback::stop() struct DSoundCapture final : public BackendBase { - DSoundCapture(DeviceBase *device) noexcept : BackendBase{device} { } + explicit DSoundCapture(DeviceBase *device) noexcept : BackendBase{device} { } ~DSoundCapture() override; void open(std::string_view name) override; @@ -562,8 +548,6 @@ struct DSoundCapture final : public BackendBase { DWORD mCursor{0u}; RingBufferPtr mRing; - - DEF_NEWDEL(DSoundCapture) }; DSoundCapture::~DSoundCapture() @@ -583,12 +567,10 @@ void DSoundCapture::open(std::string_view name) if(CaptureDevices.empty()) { /* Initialize COM to prevent name truncation */ - HRESULT hrcom{CoInitialize(nullptr)}; + ComWrapper com{}; hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices); if(FAILED(hr)) - ERR("Error enumerating DirectSound devices (0x%lx)!\n", hr); - if(SUCCEEDED(hrcom)) - CoUninitialize(); + ERR("Error enumerating DirectSound devices: {:#x}", as_unsigned(hr)); } const GUID *guid{nullptr}; @@ -610,8 +592,7 @@ void DSoundCapture::open(std::string_view name) [&id](const DevMap &entry) -> bool { return entry.guid == id; }); if(iter == CaptureDevices.cend()) throw al::backend_exception{al::backend_error::NoDevice, - "Device name \"%.*s\" not found", static_cast(name.length()), - name.data()}; + "Device name \"{}\" not found", name}; } guid = &iter->guid; } @@ -621,9 +602,9 @@ void DSoundCapture::open(std::string_view name) case DevFmtByte: case DevFmtUShort: case DevFmtUInt: - WARN("%s capture samples not supported\n", DevFmtTypeString(mDevice->FmtType)); + WARN("{} capture samples not supported", DevFmtTypeString(mDevice->FmtType)); throw al::backend_exception{al::backend_error::DeviceError, - "%s capture samples not supported", DevFmtTypeString(mDevice->FmtType)}; + "{} capture samples not supported", DevFmtTypeString(mDevice->FmtType)}; case DevFmtUByte: case DevFmtShort: @@ -642,10 +623,11 @@ void DSoundCapture::open(std::string_view name) case DevFmtX61: InputType.dwChannelMask = X6DOT1; break; case DevFmtX71: InputType.dwChannelMask = X7DOT1; break; case DevFmtX714: InputType.dwChannelMask = X7DOT1DOT4; break; + case DevFmtX7144: case DevFmtX3D71: case DevFmtAmbi3D: - WARN("%s capture not supported\n", DevFmtChannelsString(mDevice->FmtChans)); - throw al::backend_exception{al::backend_error::DeviceError, "%s capture not supported", + WARN("{} capture not supported", DevFmtChannelsString(mDevice->FmtChans)); + throw al::backend_exception{al::backend_error::DeviceError, "{} capture not supported", DevFmtChannelsString(mDevice->FmtChans)}; } @@ -654,10 +636,11 @@ void DSoundCapture::open(std::string_view name) InputType.Format.wBitsPerSample = static_cast(mDevice->bytesFromFmt() * 8); InputType.Format.nBlockAlign = static_cast(InputType.Format.nChannels * InputType.Format.wBitsPerSample / 8); - InputType.Format.nSamplesPerSec = mDevice->Frequency; + InputType.Format.nSamplesPerSec = mDevice->mSampleRate; InputType.Format.nAvgBytesPerSec = InputType.Format.nSamplesPerSec * InputType.Format.nBlockAlign; InputType.Format.cbSize = 0; + /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) */ InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample; if(mDevice->FmtType == DevFmtFloat) InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; @@ -670,8 +653,7 @@ void DSoundCapture::open(std::string_view name) InputType.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); } - uint samples{mDevice->BufferSize}; - samples = maxu(samples, 100 * mDevice->Frequency / 1000); + const uint samples{std::max(mDevice->mBufferSize, mDevice->mSampleRate/10u)}; DSCBUFFERDESC DSCBDescription{}; DSCBDescription.dwSize = sizeof(DSCBDescription); @@ -684,7 +666,7 @@ void DSoundCapture::open(std::string_view name) if(SUCCEEDED(hr)) mDSC->CreateCaptureBuffer(&DSCBDescription, al::out_ptr(mDSCbuffer), nullptr); if(SUCCEEDED(hr)) - mRing = RingBuffer::Create(mDevice->BufferSize, InputType.Format.nBlockAlign, false); + mRing = RingBuffer::Create(mDevice->mBufferSize, InputType.Format.nBlockAlign, false); if(FAILED(hr)) { @@ -692,14 +674,14 @@ void DSoundCapture::open(std::string_view name) mDSCbuffer = nullptr; mDSC = nullptr; - throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: 0x%08lx", - hr}; + throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: {:#x}", + as_unsigned(hr)}; } mBufferBytes = DSCBDescription.dwBufferBytes; setDefaultWFXChannelOrder(); - mDevice->DeviceName = name; + mDeviceName = name; } void DSoundCapture::start() @@ -707,7 +689,7 @@ void DSoundCapture::start() const HRESULT hr{mDSCbuffer->Start(DSCBSTART_LOOPING)}; if(FAILED(hr)) throw al::backend_exception{al::backend_error::DeviceError, - "Failure starting capture: 0x%lx", hr}; + "Failure starting capture: {:#x}", as_unsigned(hr)}; } void DSoundCapture::stop() @@ -715,13 +697,13 @@ void DSoundCapture::stop() HRESULT hr{mDSCbuffer->Stop()}; if(FAILED(hr)) { - ERR("stop failed: 0x%08lx\n", hr); - mDevice->handleDisconnect("Failure stopping capture: 0x%lx", hr); + ERR("stop failed: {:#x}", as_unsigned(hr)); + mDevice->handleDisconnect("Failure stopping capture: {:#x}", as_unsigned(hr)); } } void DSoundCapture::captureSamples(std::byte *buffer, uint samples) -{ mRing->read(buffer, samples); } +{ std::ignore = mRing->read(buffer, samples); } uint DSoundCapture::availableSamples() { @@ -744,17 +726,17 @@ uint DSoundCapture::availableSamples() } if(SUCCEEDED(hr)) { - mRing->write(ReadPtr1, ReadCnt1/FrameSize); + std::ignore = mRing->write(ReadPtr1, ReadCnt1/FrameSize); if(ReadPtr2 != nullptr && ReadCnt2 > 0) - mRing->write(ReadPtr2, ReadCnt2/FrameSize); + std::ignore = mRing->write(ReadPtr2, ReadCnt2/FrameSize); hr = mDSCbuffer->Unlock(ReadPtr1, ReadCnt1, ReadPtr2, ReadCnt2); mCursor = ReadCursor; } if(FAILED(hr)) { - ERR("update failed: 0x%08lx\n", hr); - mDevice->handleDisconnect("Failure retrieving capture data: 0x%lx", hr); + ERR("update failed: {:#x}", as_unsigned(hr)); + mDevice->handleDisconnect("Failure retrieving capture data: {:#x}", as_unsigned(hr)); } return static_cast(mRing->readSpace()); @@ -771,18 +753,18 @@ BackendFactory &DSoundBackendFactory::getFactory() bool DSoundBackendFactory::init() { -#ifdef HAVE_DYNLOAD +#if HAVE_DYNLOAD if(!ds_handle) { ds_handle = LoadLib("dsound.dll"); if(!ds_handle) { - ERR("Failed to load dsound.dll\n"); + ERR("Failed to load dsound.dll"); return false; } #define LOAD_FUNC(f) do { \ - p##f = al::bit_cast(GetSymbol(ds_handle, #f)); \ + p##f = reinterpret_cast(GetSymbol(ds_handle, #f)); \ if(!p##f) \ { \ CloseLib(ds_handle); \ @@ -803,40 +785,32 @@ bool DSoundBackendFactory::init() bool DSoundBackendFactory::querySupport(BackendType type) { return (type == BackendType::Playback || type == BackendType::Capture); } -std::string DSoundBackendFactory::probe(BackendType type) +auto DSoundBackendFactory::enumerate(BackendType type) -> std::vector { - std::string outnames; + std::vector outnames; auto add_device = [&outnames](const DevMap &entry) -> void - { - /* +1 to also append the null char (to ensure a null-separated list and - * double-null terminated list). - */ - outnames.append(entry.name.c_str(), entry.name.length()+1); - }; + { outnames.emplace_back(entry.name); }; /* Initialize COM to prevent name truncation */ - HRESULT hr; - HRESULT hrcom{CoInitialize(nullptr)}; + ComWrapper com{}; switch(type) { case BackendType::Playback: PlaybackDevices.clear(); - hr = DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices); - if(FAILED(hr)) - ERR("Error enumerating DirectSound playback devices (0x%lx)!\n", hr); + if(HRESULT hr{DirectSoundEnumerateW(DSoundEnumDevices, &PlaybackDevices)}; FAILED(hr)) + ERR("Error enumerating DirectSound playback devices: {:#x}", as_unsigned(hr)); + outnames.reserve(PlaybackDevices.size()); std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device); break; case BackendType::Capture: CaptureDevices.clear(); - hr = DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices); - if(FAILED(hr)) - ERR("Error enumerating DirectSound capture devices (0x%lx)!\n", hr); + if(HRESULT hr{DirectSoundCaptureEnumerateW(DSoundEnumDevices, &CaptureDevices)};FAILED(hr)) + ERR("Error enumerating DirectSound capture devices: {:#x}", as_unsigned(hr)); + outnames.reserve(CaptureDevices.size()); std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device); break; } - if(SUCCEEDED(hrcom)) - CoUninitialize(); return outnames; } diff --git a/3rdparty/openal/alc/backends/dsound.h b/3rdparty/openal/alc/backends/dsound.h index 787f227a0a20..33adbf2971fe 100644 --- a/3rdparty/openal/alc/backends/dsound.h +++ b/3rdparty/openal/alc/backends/dsound.h @@ -5,15 +5,15 @@ struct DSoundBackendFactory final : public BackendFactory { public: - bool init() override; + auto init() -> bool final; - bool querySupport(BackendType type) override; + auto querySupport(BackendType type) -> bool final; - std::string probe(BackendType type) override; + auto enumerate(BackendType type) -> std::vector final; - BackendPtr createBackend(DeviceBase *device, BackendType type) override; + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; - static BackendFactory &getFactory(); + static auto getFactory() -> BackendFactory&; }; #endif /* BACKENDS_DSOUND_H */ diff --git a/3rdparty/openal/alc/backends/jack.cpp b/3rdparty/openal/alc/backends/jack.cpp index a0a5c4402f72..fb84287f7f04 100644 --- a/3rdparty/openal/alc/backends/jack.cpp +++ b/3rdparty/openal/alc/backends/jack.cpp @@ -29,10 +29,8 @@ #include #include #include -#include #include -#include "albit.h" #include "alc/alconfig.h" #include "alnumeric.h" #include "alsem.h" @@ -41,6 +39,7 @@ #include "core/helpers.h" #include "core/logging.h" #include "dynload.h" +#include "fmt/format.h" #include "ringbuffer.h" #include @@ -49,7 +48,9 @@ namespace { -#ifdef HAVE_DYNLOAD +using namespace std::string_view_literals; + +#if HAVE_DYNLOAD #define JACK_FUNCS(MAGIC) \ MAGIC(jack_client_open); \ MAGIC(jack_client_close); \ @@ -102,20 +103,16 @@ decltype(jack_error_callback) * pjack_error_callback; #endif -constexpr char JackDefaultAudioType[] = JACK_DEFAULT_AUDIO_TYPE; - jack_options_t ClientOptions = JackNullOption; bool jack_load() { - bool error{false}; - -#ifdef HAVE_DYNLOAD +#if HAVE_DYNLOAD if(!jack_handle) { - std::string missing_funcs; - -#ifdef _WIN32 +#if defined(_WIN64) +#define JACKLIB "libjack64.dll" +#elif defined(_WIN32) #define JACKLIB "libjack.dll" #else #define JACKLIB "libjack.so.0" @@ -123,52 +120,58 @@ bool jack_load() jack_handle = LoadLib(JACKLIB); if(!jack_handle) { - WARN("Failed to load %s\n", JACKLIB); + WARN("Failed to load {}", JACKLIB); return false; } - error = false; + std::string missing_funcs; #define LOAD_FUNC(f) do { \ - p##f = al::bit_cast(GetSymbol(jack_handle, #f)); \ - if(p##f == nullptr) { \ - error = true; \ - missing_funcs += "\n" #f; \ - } \ + p##f = reinterpret_cast(GetSymbol(jack_handle, #f)); \ + if(p##f == nullptr) missing_funcs += "\n" #f; \ } while(0) JACK_FUNCS(LOAD_FUNC); #undef LOAD_FUNC /* Optional symbols. These don't exist in all versions of JACK. */ -#define LOAD_SYM(f) p##f = al::bit_cast(GetSymbol(jack_handle, #f)) +#define LOAD_SYM(f) p##f = reinterpret_cast(GetSymbol(jack_handle, #f)) LOAD_SYM(jack_error_callback); #undef LOAD_SYM - if(error) + if(!missing_funcs.empty()) { - WARN("Missing expected functions:%s\n", missing_funcs.c_str()); + WARN("Missing expected functions:{}", missing_funcs); CloseLib(jack_handle); jack_handle = nullptr; + return false; } } #endif - return !error; + return true; } struct JackDeleter { void operator()(void *ptr) { jack_free(ptr); } }; -using JackPortsPtr = std::unique_ptr; +using JackPortsPtr = std::unique_ptr; /* NOLINT(*-avoid-c-arrays) */ struct DeviceEntry { std::string mName; std::string mPattern; + DeviceEntry() = default; + DeviceEntry(const DeviceEntry&) = default; + DeviceEntry(DeviceEntry&&) = default; template DeviceEntry(T&& name, U&& pattern) : mName{std::forward(name)}, mPattern{std::forward(pattern)} { } + ~DeviceEntry(); + + DeviceEntry& operator=(const DeviceEntry&) = default; + DeviceEntry& operator=(DeviceEntry&&) = default; }; +DeviceEntry::~DeviceEntry() = default; std::vector PlaybackList; @@ -177,39 +180,35 @@ void EnumerateDevices(jack_client_t *client, std::vector &list) { std::remove_reference_t{}.swap(list); - if(JackPortsPtr ports{jack_get_ports(client, nullptr, JackDefaultAudioType, JackPortIsInput)}) + if(JackPortsPtr ports{jack_get_ports(client, nullptr, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput)}) { for(size_t i{0};ports[i];++i) { - const char *sep{std::strchr(ports[i], ':')}; - if(!sep || ports[i] == sep) continue; + const std::string_view portname{ports[i]}; + const size_t seppos{portname.find(':')}; + if(seppos == 0 || seppos >= portname.size()) + continue; - const al::span portdev{ports[i], sep}; + const auto portdev = portname.substr(0, seppos); auto check_name = [portdev](const DeviceEntry &entry) -> bool - { - const size_t len{portdev.size()}; - return entry.mName.length() == len - && entry.mName.compare(0, len, portdev.data(), len) == 0; - }; + { return entry.mName == portdev; }; if(std::find_if(list.cbegin(), list.cend(), check_name) != list.cend()) continue; - std::string name{portdev.data(), portdev.size()}; - list.emplace_back(name, name+":"); - const auto &entry = list.back(); - TRACE("Got device: %s = %s\n", entry.mName.c_str(), entry.mPattern.c_str()); + const auto &entry = list.emplace_back(portdev, fmt::format("{}:", portdev)); + TRACE("Got device: {} = {}", entry.mName, entry.mPattern); } /* There are ports but couldn't get device names from them. Add a * generic entry. */ if(ports[0] && list.empty()) { - WARN("No device names found in available ports, adding a generic name.\n"); - list.emplace_back("JACK", ""); + WARN("No device names found in available ports, adding a generic name."); + list.emplace_back("JACK"sv, ""sv); } } - if(auto listopt = ConfigValueStr(nullptr, "jack", "custom-devices")) + if(auto listopt = ConfigValueStr({}, "jack", "custom-devices")) { for(size_t strpos{0};strpos < listopt->size();) { @@ -217,39 +216,32 @@ void EnumerateDevices(jack_client_t *client, std::vector &list) size_t seppos{listopt->find('=', strpos)}; if(seppos >= nextpos || seppos == strpos) { - const std::string entry{listopt->substr(strpos, nextpos-strpos)}; - ERR("Invalid device entry: \"%s\"\n", entry.c_str()); + const auto entry = std::string_view{*listopt}.substr(strpos, nextpos-strpos); + ERR("Invalid device entry: \"{}\"", entry); if(nextpos != std::string::npos) ++nextpos; strpos = nextpos; continue; } - const al::span name{listopt->data()+strpos, seppos-strpos}; - const al::span pattern{listopt->data()+(seppos+1), - std::min(nextpos, listopt->size())-(seppos+1)}; + const auto name = std::string_view{*listopt}.substr(strpos, seppos-strpos); + const auto pattern = std::string_view{*listopt}.substr(seppos+1, + std::min(nextpos, listopt->size())-(seppos+1)); /* Check if this custom pattern already exists in the list. */ auto check_pattern = [pattern](const DeviceEntry &entry) -> bool - { - const size_t len{pattern.size()}; - return entry.mPattern.length() == len - && entry.mPattern.compare(0, len, pattern.data(), len) == 0; - }; + { return entry.mPattern == pattern; }; auto itemmatch = std::find_if(list.begin(), list.end(), check_pattern); if(itemmatch != list.end()) { /* If so, replace the name with this custom one. */ - itemmatch->mName.assign(name.data(), name.size()); - TRACE("Customized device name: %s = %s\n", itemmatch->mName.c_str(), - itemmatch->mPattern.c_str()); + itemmatch->mName = name; + TRACE("Customized device name: {} = {}", itemmatch->mName, itemmatch->mPattern); } else { /* Otherwise, add a new device entry. */ - list.emplace_back(std::string{name.data(), name.size()}, - std::string{pattern.data(), pattern.size()}); - const auto &entry = list.back(); - TRACE("Got custom device: %s = %s\n", entry.mName.c_str(), entry.mPattern.c_str()); + const auto &entry = list.emplace_back(name, pattern); + TRACE("Got custom device: {} = {}", entry.mName, entry.mPattern); } if(nextpos != std::string::npos) ++nextpos; @@ -285,7 +277,7 @@ void EnumerateDevices(jack_client_t *client, std::vector &list) struct JackPlayback final : public BackendBase { - JackPlayback(DeviceBase *device) noexcept : BackendBase{device} { } + explicit JackPlayback(DeviceBase *device) noexcept : BackendBase{device} { } ~JackPlayback() override; int processRt(jack_nframes_t numframes) noexcept; @@ -307,7 +299,7 @@ struct JackPlayback final : public BackendBase { std::string mPortPattern; jack_client_t *mClient{nullptr}; - std::array mPort{}; + std::array mPort{}; std::mutex mMutex; @@ -318,8 +310,6 @@ struct JackPlayback final : public BackendBase { std::atomic mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(JackPlayback) }; JackPlayback::~JackPlayback() @@ -339,22 +329,22 @@ JackPlayback::~JackPlayback() int JackPlayback::processRt(jack_nframes_t numframes) noexcept { - std::array out; - size_t numchans{0}; + auto outptrs = std::array{}; + auto numchans = size_t{0}; for(auto port : mPort) { if(!port || numchans == mDevice->RealOut.Buffer.size()) break; - out[numchans++] = static_cast(jack_port_get_buffer(port, numframes)); + outptrs[numchans++] = jack_port_get_buffer(port, numframes); } + const auto dst = al::span{outptrs}.first(numchans); if(mPlaying.load(std::memory_order_acquire)) LIKELY - mDevice->renderSamples({out.data(), numchans}, static_cast(numframes)); + mDevice->renderSamples(dst, static_cast(numframes)); else { - auto clear_buf = [numframes](float *outbuf) -> void - { std::fill_n(outbuf, numframes, 0.0f); }; - std::for_each(out.begin(), out.begin()+numchans, clear_buf); + std::for_each(dst.begin(), dst.end(), [numframes](void *outbuf) -> void + { std::fill_n(static_cast(outbuf), numframes, 0.0f); }); } return 0; @@ -363,53 +353,46 @@ int JackPlayback::processRt(jack_nframes_t numframes) noexcept int JackPlayback::process(jack_nframes_t numframes) noexcept { - std::array out; + std::array,MaxOutputChannels> out; size_t numchans{0}; for(auto port : mPort) { if(!port) break; - out[numchans++] = static_cast(jack_port_get_buffer(port, numframes)); + out[numchans++] = {static_cast(jack_port_get_buffer(port, numframes)), numframes}; } - jack_nframes_t total{0}; + size_t total{0}; if(mPlaying.load(std::memory_order_acquire)) LIKELY { auto data = mRing->getReadVector(); - jack_nframes_t todo{minu(numframes, static_cast(data.first.len))}; - auto write_first = [&data,numchans,todo](float *outbuf) -> float* + const auto update_size = size_t{mDevice->mUpdateSize}; + + const auto outlen = size_t{numframes / update_size}; + const auto len1 = size_t{std::min(data[0].len/update_size, outlen)}; + const auto len2 = size_t{std::min(data[1].len/update_size, outlen-len1)}; + + auto src = al::span{reinterpret_cast(data[0].buf), update_size*len1*numchans}; + for(size_t i{0};i < len1;++i) { - const float *RESTRICT in = reinterpret_cast(data.first.buf); - auto deinterlace_input = [&in,numchans]() noexcept -> float + for(size_t c{0};c < numchans;++c) { - float ret{*in}; - in += numchans; - return ret; - }; - std::generate_n(outbuf, todo, deinterlace_input); - data.first.buf += sizeof(float); - return outbuf + todo; - }; - std::transform(out.begin(), out.begin()+numchans, out.begin(), write_first); - total += todo; - - todo = minu(numframes-total, static_cast(data.second.len)); - if(todo > 0) + const auto iter = std::copy_n(src.begin(), update_size, out[c].begin()); + out[c] = {iter, out[c].end()}; + src = src.subspan(update_size); + } + total += update_size; + } + + src = al::span{reinterpret_cast(data[1].buf), update_size*len2*numchans}; + for(size_t i{0};i < len2;++i) { - auto write_second = [&data,numchans,todo](float *outbuf) -> float* + for(size_t c{0};c < numchans;++c) { - const float *RESTRICT in = reinterpret_cast(data.second.buf); - auto deinterlace_input = [&in,numchans]() noexcept -> float - { - float ret{*in}; - in += numchans; - return ret; - }; - std::generate_n(outbuf, todo, deinterlace_input); - data.second.buf += sizeof(float); - return outbuf + todo; - }; - std::transform(out.begin(), out.begin()+numchans, out.begin(), write_second); - total += todo; + const auto iter = std::copy_n(src.begin(), update_size, out[c].begin()); + out[c] = {iter, out[c].end()}; + src = src.subspan(update_size); + } + total += update_size; } mRing->readAdvance(total); @@ -418,8 +401,8 @@ int JackPlayback::process(jack_nframes_t numframes) noexcept if(numframes > total) { - const jack_nframes_t todo{numframes - total}; - auto clear_buf = [todo](float *outbuf) -> void { std::fill_n(outbuf, todo, 0.0f); }; + auto clear_buf = [](const al::span outbuf) -> void + { std::fill(outbuf.begin(), outbuf.end(), 0.0f); }; std::for_each(out.begin(), out.begin()+numchans, clear_buf); } @@ -429,31 +412,54 @@ int JackPlayback::process(jack_nframes_t numframes) noexcept int JackPlayback::mixerProc() { SetRTPriority(); - althrd_setname(MIXER_THREAD_NAME); + althrd_setname(GetMixerThreadName()); - const size_t frame_step{mDevice->channelsFromFmt()}; + const auto update_size = uint{mDevice->mUpdateSize}; + const auto num_channels = size_t{mDevice->channelsFromFmt()}; + auto outptrs = std::vector(num_channels); while(!mKillNow.load(std::memory_order_acquire) && mDevice->Connected.load(std::memory_order_acquire)) { - if(mRing->writeSpace() < mDevice->UpdateSize) + if(mRing->writeSpace() < update_size) { mSem.wait(); continue; } auto data = mRing->getWriteVector(); - size_t todo{data.first.len + data.second.len}; - todo -= todo%mDevice->UpdateSize; + const auto len1 = size_t{data[0].len / update_size}; + const auto len2 = size_t{data[1].len / update_size}; - const auto len1 = static_cast(minz(data.first.len, todo)); - const auto len2 = static_cast(minz(data.second.len, todo-len1)); - - std::lock_guard _{mMutex}; - mDevice->renderSamples(data.first.buf, len1, frame_step); + std::lock_guard dlock{mMutex}; + auto buffer = al::span{reinterpret_cast(data[0].buf), data[0].len*num_channels}; + auto bufiter = buffer.begin(); + for(size_t i{0};i < len1;++i) + { + std::generate_n(outptrs.begin(), outptrs.size(), [&bufiter,update_size] + { + auto ret = al::to_address(bufiter); + bufiter += ptrdiff_t(update_size); + return ret; + }); + mDevice->renderSamples(outptrs, update_size); + } if(len2 > 0) - mDevice->renderSamples(data.second.buf, len2, frame_step); - mRing->writeAdvance(todo); + { + buffer = al::span{reinterpret_cast(data[1].buf), data[1].len*num_channels}; + bufiter = buffer.begin(); + for(size_t i{0};i < len2;++i) + { + std::generate_n(outptrs.begin(), outptrs.size(), [&bufiter,update_size] + { + auto ret = al::to_address(bufiter); + bufiter += ptrdiff_t(update_size); + return ret; + }); + mDevice->renderSamples(outptrs, update_size); + } + } + mRing->writeAdvance((len1+len2) * update_size); } return 0; @@ -467,17 +473,18 @@ void JackPlayback::open(std::string_view name) const PathNamePair &binname = GetProcBinary(); const char *client_name{binname.fname.empty() ? "alsoft" : binname.fname.c_str()}; - jack_status_t status; + jack_status_t status{}; mClient = jack_client_open(client_name, ClientOptions, &status, nullptr); if(mClient == nullptr) throw al::backend_exception{al::backend_error::DeviceError, - "Failed to open client connection: 0x%02x", status}; + "Failed to open client connection: {:#02x}", + as_unsigned(al::to_underlying(status))}; if((status&JackServerStarted)) - TRACE("JACK server started\n"); + TRACE("JACK server started"); if((status&JackNameNotUnique)) { client_name = jack_get_client_name(mClient); - TRACE("Client name not unique, got '%s' instead\n", client_name); + TRACE("Client name not unique, got '{}' instead", client_name); } } @@ -496,11 +503,11 @@ void JackPlayback::open(std::string_view name) auto iter = std::find_if(PlaybackList.cbegin(), PlaybackList.cend(), check_name); if(iter == PlaybackList.cend()) throw al::backend_exception{al::backend_error::NoDevice, - "Device name \"%.*s\" not found", static_cast(name.length()), name.data()}; + "Device name \"{}\" not found", name}; mPortPattern = iter->mPattern; } - mDevice->DeviceName = name; + mDeviceName = name; } bool JackPlayback::reset() @@ -510,55 +517,56 @@ bool JackPlayback::reset() std::for_each(mPort.begin(), mPort.end(), unregister_port); mPort.fill(nullptr); - mRTMixing = GetConfigValueBool(mDevice->DeviceName.c_str(), "jack", "rt-mix", true); + mRTMixing = GetConfigValueBool(mDevice->mDeviceName, "jack", "rt-mix", true); jack_set_process_callback(mClient, mRTMixing ? &JackPlayback::processRtC : &JackPlayback::processC, this); /* Ignore the requested buffer metrics and just keep one JACK-sized buffer * ready for when requested. */ - mDevice->Frequency = jack_get_sample_rate(mClient); - mDevice->UpdateSize = jack_get_buffer_size(mClient); + mDevice->mSampleRate = jack_get_sample_rate(mClient); + mDevice->mUpdateSize = jack_get_buffer_size(mClient); if(mRTMixing) { /* Assume only two periods when directly mixing. Should try to query * the total port latency when connected. */ - mDevice->BufferSize = mDevice->UpdateSize * 2; + mDevice->mBufferSize = mDevice->mUpdateSize * 2; } else { - const char *devname{mDevice->DeviceName.c_str()}; - uint bufsize{ConfigValueUInt(devname, "jack", "buffer-size").value_or(mDevice->UpdateSize)}; - bufsize = maxu(NextPowerOf2(bufsize), mDevice->UpdateSize); - mDevice->BufferSize = bufsize + mDevice->UpdateSize; + const auto devname = std::string_view{mDevice->mDeviceName}; + auto bufsize = ConfigValueUInt(devname, "jack", "buffer-size") + .value_or(mDevice->mUpdateSize); + bufsize = std::max(NextPowerOf2(bufsize), mDevice->mUpdateSize); + mDevice->mBufferSize = bufsize + mDevice->mUpdateSize; } /* Force 32-bit float output. */ mDevice->FmtType = DevFmtFloat; int port_num{0}; - auto ports_end = mPort.begin() + mDevice->channelsFromFmt(); - auto bad_port = mPort.begin(); - while(bad_port != ports_end) + auto ports = al::span{mPort}.first(mDevice->channelsFromFmt()); + auto bad_port = ports.begin(); + while(bad_port != ports.end()) { std::string name{"channel_" + std::to_string(++port_num)}; - *bad_port = jack_port_register(mClient, name.c_str(), JackDefaultAudioType, + *bad_port = jack_port_register(mClient, name.c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput | JackPortIsTerminal, 0); if(!*bad_port) break; ++bad_port; } - if(bad_port != ports_end) + if(bad_port != ports.end()) { - ERR("Failed to register enough JACK ports for %s output\n", + ERR("Failed to register enough JACK ports for {} output", DevFmtChannelsString(mDevice->FmtChans)); - if(bad_port == mPort.begin()) return false; + if(bad_port == ports.begin()) return false; - if(bad_port == mPort.begin()+1) + if(bad_port == ports.begin()+1) mDevice->FmtChans = DevFmtMono; else { - ports_end = mPort.begin()+2; + const auto ports_end = ports.begin()+2; while(bad_port != ports_end) { jack_port_unregister(mClient, *(--bad_port)); @@ -578,10 +586,10 @@ void JackPlayback::start() if(jack_activate(mClient)) throw al::backend_exception{al::backend_error::DeviceError, "Failed to activate client"}; - const char *devname{mDevice->DeviceName.c_str()}; + const auto devname = std::string_view{mDevice->mDeviceName}; if(ConfigValueBool(devname, "jack", "connect-ports").value_or(true)) { - JackPortsPtr pnames{jack_get_ports(mClient, mPortPattern.c_str(), JackDefaultAudioType, + JackPortsPtr pnames{jack_get_ports(mClient, mPortPattern.c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput)}; if(!pnames) { @@ -593,11 +601,11 @@ void JackPlayback::start() { if(!pnames[i]) { - ERR("No physical playback port for \"%s\"\n", jack_port_name(mPort[i])); + ERR("No physical playback port for \"{}\"", jack_port_name(mPort[i])); break; } if(jack_connect(mClient, jack_port_name(mPort[i]), pnames[i])) - ERR("Failed to connect output port \"%s\" to \"%s\"\n", jack_port_name(mPort[i]), + ERR("Failed to connect output port \"{}\" to \"{}\"", jack_port_name(mPort[i]), pnames[i]); } } @@ -606,31 +614,32 @@ void JackPlayback::start() * (it won't change again after jack_activate), then allocate the ring * buffer with the appropriate size. */ - mDevice->Frequency = jack_get_sample_rate(mClient); - mDevice->UpdateSize = jack_get_buffer_size(mClient); - mDevice->BufferSize = mDevice->UpdateSize * 2; + mDevice->mSampleRate = jack_get_sample_rate(mClient); + mDevice->mUpdateSize = jack_get_buffer_size(mClient); + mDevice->mBufferSize = mDevice->mUpdateSize * 2; mRing = nullptr; if(mRTMixing) mPlaying.store(true, std::memory_order_release); else { - uint bufsize{ConfigValueUInt(devname, "jack", "buffer-size").value_or(mDevice->UpdateSize)}; - bufsize = maxu(NextPowerOf2(bufsize), mDevice->UpdateSize); - mDevice->BufferSize = bufsize + mDevice->UpdateSize; + uint bufsize{ConfigValueUInt(devname, "jack", "buffer-size") + .value_or(mDevice->mUpdateSize)}; + bufsize = std::max(NextPowerOf2(bufsize), mDevice->mUpdateSize); + mDevice->mBufferSize = bufsize + mDevice->mUpdateSize; mRing = RingBuffer::Create(bufsize, mDevice->frameSizeFromFmt(), true); try { mPlaying.store(true, std::memory_order_release); mKillNow.store(false, std::memory_order_release); - mThread = std::thread{std::mem_fn(&JackPlayback::mixerProc), this}; + mThread = std::thread{&JackPlayback::mixerProc, this}; } catch(std::exception& e) { jack_deactivate(mClient); mPlaying.store(false, std::memory_order_release); throw al::backend_exception{al::backend_error::DeviceError, - "Failed to start mixing thread: %s", e.what()}; + "Failed to start mixing thread: {}", e.what()}; } } } @@ -654,12 +663,11 @@ void JackPlayback::stop() ClockLatency JackPlayback::getClockLatency() { - ClockLatency ret; - - std::lock_guard _{mMutex}; - ret.ClockTime = GetDeviceClockTime(mDevice); - ret.Latency = std::chrono::seconds{mRing ? mRing->readSpace() : mDevice->UpdateSize}; - ret.Latency /= mDevice->Frequency; + std::lock_guard dlock{mMutex}; + ClockLatency ret{}; + ret.ClockTime = mDevice->getClockTime(); + ret.Latency = std::chrono::seconds{mRing ? mRing->readSpace() : mDevice->mUpdateSize}; + ret.Latency /= mDevice->mSampleRate; return ret; } @@ -667,7 +675,7 @@ ClockLatency JackPlayback::getClockLatency() void jack_msg_handler(const char *message) { - WARN("%s\n", message); + WARN("{}", message); } } // namespace @@ -677,7 +685,7 @@ bool JackBackendFactory::init() if(!jack_load()) return false; - if(!GetConfigValueBool(nullptr, "jack", "spawn-server", false)) + if(!GetConfigValueBool({}, "jack", "spawn-server", false)) ClientOptions = static_cast(ClientOptions | JackNoStartServer); const PathNamePair &binname = GetProcBinary(); @@ -685,14 +693,14 @@ bool JackBackendFactory::init() void (*old_error_cb)(const char*){&jack_error_callback ? jack_error_callback : nullptr}; jack_set_error_function(jack_msg_handler); - jack_status_t status; + jack_status_t status{}; jack_client_t *client{jack_client_open(client_name, ClientOptions, &status, nullptr)}; jack_set_error_function(old_error_cb); if(!client) { - WARN("jack_client_open() failed, 0x%02x\n", status); + WARN("jack_client_open() failed, {:#02x}", as_unsigned(al::to_underlying(status))); if((status&JackServerFailed) && !(ClientOptions&JackNoStartServer)) - ERR("Unable to connect to JACK server\n"); + ERR("Unable to connect to JACK server"); return false; } @@ -703,18 +711,15 @@ bool JackBackendFactory::init() bool JackBackendFactory::querySupport(BackendType type) { return (type == BackendType::Playback); } -std::string JackBackendFactory::probe(BackendType type) +auto JackBackendFactory::enumerate(BackendType type) -> std::vector { - std::string outnames; + std::vector outnames; auto append_name = [&outnames](const DeviceEntry &entry) -> void - { - /* Includes null char. */ - outnames.append(entry.mName.c_str(), entry.mName.length()+1); - }; + { outnames.emplace_back(entry.mName); }; const PathNamePair &binname = GetProcBinary(); const char *client_name{binname.fname.empty() ? "alsoft" : binname.fname.c_str()}; - jack_status_t status; + jack_status_t status{}; switch(type) { case BackendType::Playback: @@ -724,7 +729,8 @@ std::string JackBackendFactory::probe(BackendType type) jack_client_close(client); } else - WARN("jack_client_open() failed, 0x%02x\n", status); + WARN("jack_client_open() failed, {:#02x}", as_unsigned(al::to_underlying(status))); + outnames.reserve(PlaybackList.size()); std::for_each(PlaybackList.cbegin(), PlaybackList.cend(), append_name); break; case BackendType::Capture: diff --git a/3rdparty/openal/alc/backends/jack.h b/3rdparty/openal/alc/backends/jack.h index b83f24dda16f..1e4c9f05b1f4 100644 --- a/3rdparty/openal/alc/backends/jack.h +++ b/3rdparty/openal/alc/backends/jack.h @@ -5,15 +5,15 @@ struct JackBackendFactory final : public BackendFactory { public: - bool init() override; + auto init() -> bool final; - bool querySupport(BackendType type) override; + auto querySupport(BackendType type) -> bool final; - std::string probe(BackendType type) override; + auto enumerate(BackendType type) -> std::vector final; - BackendPtr createBackend(DeviceBase *device, BackendType type) override; + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; - static BackendFactory &getFactory(); + static auto getFactory() -> BackendFactory&; }; #endif /* BACKENDS_JACK_H */ diff --git a/3rdparty/openal/alc/backends/loopback.cpp b/3rdparty/openal/alc/backends/loopback.cpp index 2972fc0145d1..66b58bc23b34 100644 --- a/3rdparty/openal/alc/backends/loopback.cpp +++ b/3rdparty/openal/alc/backends/loopback.cpp @@ -28,20 +28,18 @@ namespace { struct LoopbackBackend final : public BackendBase { - LoopbackBackend(DeviceBase *device) noexcept : BackendBase{device} { } + explicit LoopbackBackend(DeviceBase *device) noexcept : BackendBase{device} { } void open(std::string_view name) override; bool reset() override; void start() override; void stop() override; - - DEF_NEWDEL(LoopbackBackend) }; void LoopbackBackend::open(std::string_view name) { - mDevice->DeviceName = name; + mDeviceName = name; } bool LoopbackBackend::reset() @@ -65,8 +63,8 @@ bool LoopbackBackendFactory::init() bool LoopbackBackendFactory::querySupport(BackendType) { return true; } -std::string LoopbackBackendFactory::probe(BackendType) -{ return std::string{}; } +auto LoopbackBackendFactory::enumerate(BackendType) -> std::vector +{ return {}; } BackendPtr LoopbackBackendFactory::createBackend(DeviceBase *device, BackendType) { return BackendPtr{new LoopbackBackend{device}}; } diff --git a/3rdparty/openal/alc/backends/loopback.h b/3rdparty/openal/alc/backends/loopback.h index cb42b3c8e81b..876a052cb566 100644 --- a/3rdparty/openal/alc/backends/loopback.h +++ b/3rdparty/openal/alc/backends/loopback.h @@ -5,15 +5,15 @@ struct LoopbackBackendFactory final : public BackendFactory { public: - bool init() override; + auto init() -> bool final; - bool querySupport(BackendType type) override; + auto querySupport(BackendType type) -> bool final; - std::string probe(BackendType type) override; + auto enumerate(BackendType type) -> std::vector final; - BackendPtr createBackend(DeviceBase *device, BackendType type) override; + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; - static BackendFactory &getFactory(); + static auto getFactory() -> BackendFactory&; }; #endif /* BACKENDS_LOOPBACK_H */ diff --git a/3rdparty/openal/alc/backends/null.cpp b/3rdparty/openal/alc/backends/null.cpp index 3c68e4cec9a8..25511d7fe182 100644 --- a/3rdparty/openal/alc/backends/null.cpp +++ b/3rdparty/openal/alc/backends/null.cpp @@ -27,11 +27,9 @@ #include #include #include -#include #include #include "althrd_setname.h" -#include "almalloc.h" #include "core/device.h" #include "core/helpers.h" @@ -41,12 +39,13 @@ namespace { using std::chrono::seconds; using std::chrono::milliseconds; using std::chrono::nanoseconds; +using namespace std::string_view_literals; -constexpr char nullDevice[] = "No Output"; +[[nodiscard]] constexpr auto GetDeviceName() noexcept { return "No Output"sv; } struct NullBackend final : public BackendBase { - NullBackend(DeviceBase *device) noexcept : BackendBase{device} { } + explicit NullBackend(DeviceBase *device) noexcept : BackendBase{device} { } int mixerProc(); @@ -57,16 +56,14 @@ struct NullBackend final : public BackendBase { std::atomic mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(NullBackend) }; int NullBackend::mixerProc() { - const milliseconds restTime{mDevice->UpdateSize*1000/mDevice->Frequency / 2}; + const milliseconds restTime{mDevice->mUpdateSize*1000/mDevice->mSampleRate / 2}; SetRTPriority(); - althrd_setname(MIXER_THREAD_NAME); + althrd_setname(GetMixerThreadName()); int64_t done{0}; auto start = std::chrono::steady_clock::now(); @@ -76,16 +73,17 @@ int NullBackend::mixerProc() auto now = std::chrono::steady_clock::now(); /* This converts from nanoseconds to nanosamples, then to samples. */ - int64_t avail{std::chrono::duration_cast((now-start) * mDevice->Frequency).count()}; - if(avail-done < mDevice->UpdateSize) + const auto avail = int64_t{std::chrono::duration_cast((now-start) + * mDevice->mSampleRate).count()}; + if(avail-done < mDevice->mUpdateSize) { std::this_thread::sleep_for(restTime); continue; } - while(avail-done >= mDevice->UpdateSize) + while(avail-done >= mDevice->mUpdateSize) { - mDevice->renderSamples(nullptr, mDevice->UpdateSize, 0u); - done += mDevice->UpdateSize; + mDevice->renderSamples(nullptr, mDevice->mUpdateSize, 0u); + done += mDevice->mUpdateSize; } /* For every completed second, increment the start time and reduce the @@ -93,11 +91,11 @@ int NullBackend::mixerProc() * and current time from growing too large, while maintaining the * correct number of samples to render. */ - if(done >= mDevice->Frequency) + if(done >= mDevice->mSampleRate) { - seconds s{done/mDevice->Frequency}; + seconds s{done/mDevice->mSampleRate}; start += s; - done -= mDevice->Frequency*s.count(); + done -= mDevice->mSampleRate*s.count(); } } @@ -108,12 +106,12 @@ int NullBackend::mixerProc() void NullBackend::open(std::string_view name) { if(name.empty()) - name = nullDevice; - else if(name != nullDevice) - throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", - static_cast(name.length()), name.data()}; + name = GetDeviceName(); + else if(name != GetDeviceName()) + throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found", + name}; - mDevice->DeviceName = name; + mDeviceName = name; } bool NullBackend::reset() @@ -126,11 +124,11 @@ void NullBackend::start() { try { mKillNow.store(false, std::memory_order_release); - mThread = std::thread{std::mem_fn(&NullBackend::mixerProc), this}; + mThread = std::thread{&NullBackend::mixerProc, this}; } catch(std::exception& e) { throw al::backend_exception{al::backend_error::DeviceError, - "Failed to start mixing thread: %s", e.what()}; + "Failed to start mixing thread: {}", e.what()}; } } @@ -150,19 +148,17 @@ bool NullBackendFactory::init() bool NullBackendFactory::querySupport(BackendType type) { return (type == BackendType::Playback); } -std::string NullBackendFactory::probe(BackendType type) +auto NullBackendFactory::enumerate(BackendType type) -> std::vector { - std::string outnames; switch(type) { case BackendType::Playback: - /* Includes null char. */ - outnames.append(nullDevice, sizeof(nullDevice)); - break; + /* Include null char. */ + return std::vector{std::string{GetDeviceName()}}; case BackendType::Capture: break; } - return outnames; + return {}; } BackendPtr NullBackendFactory::createBackend(DeviceBase *device, BackendType type) diff --git a/3rdparty/openal/alc/backends/null.h b/3rdparty/openal/alc/backends/null.h index 7048cad6f09a..213842af7e51 100644 --- a/3rdparty/openal/alc/backends/null.h +++ b/3rdparty/openal/alc/backends/null.h @@ -5,15 +5,15 @@ struct NullBackendFactory final : public BackendFactory { public: - bool init() override; + auto init() -> bool final; - bool querySupport(BackendType type) override; + auto querySupport(BackendType type) -> bool final; - std::string probe(BackendType type) override; + auto enumerate(BackendType type) -> std::vector final; - BackendPtr createBackend(DeviceBase *device, BackendType type) override; + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; - static BackendFactory &getFactory(); + static auto getFactory() -> BackendFactory&; }; #endif /* BACKENDS_NULL_H */ diff --git a/3rdparty/openal/alc/backends/oboe.cpp b/3rdparty/openal/alc/backends/oboe.cpp index b7bab19ae4ea..2aa9960cbad0 100644 --- a/3rdparty/openal/alc/backends/oboe.cpp +++ b/3rdparty/openal/alc/backends/oboe.cpp @@ -4,10 +4,11 @@ #include "oboe.h" #include +#include #include -#include #include "alnumeric.h" +#include "alstring.h" #include "core/device.h" #include "core/logging.h" #include "ringbuffer.h" @@ -17,11 +18,13 @@ namespace { -constexpr char device_name[] = "Oboe Default"; +using namespace std::string_view_literals; + +[[nodiscard]] constexpr auto GetDeviceName() noexcept { return "Oboe Default"sv; } struct OboePlayback final : public BackendBase, public oboe::AudioStreamCallback { - OboePlayback(DeviceBase *device) : BackendBase{device} { } + explicit OboePlayback(DeviceBase *device) : BackendBase{device} { } oboe::ManagedStream mStream; @@ -48,21 +51,21 @@ oboe::DataCallbackResult OboePlayback::onAudioReady(oboe::AudioStream *oboeStrea return oboe::DataCallbackResult::Continue; } -void OboePlayback::onErrorAfterClose(oboe::AudioStream* audioStream, oboe::Result error) +void OboePlayback::onErrorAfterClose(oboe::AudioStream*, oboe::Result error) { - if (error == oboe::Result::ErrorDisconnected) { - mDevice->handleDisconnect("Oboe AudioStream was disconnected: %s", oboe::convertToText(error)); - } - TRACE("Error was %s", oboe::convertToText(error)); + if(error == oboe::Result::ErrorDisconnected) + mDevice->handleDisconnect("Oboe AudioStream was disconnected: {}", + oboe::convertToText(error)); + TRACE("Error was {}", oboe::convertToText(error)); } void OboePlayback::open(std::string_view name) { if(name.empty()) - name = device_name; - else if(name != device_name) - throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", - static_cast(name.length()), name.data()}; + name = GetDeviceName(); + else if(name != GetDeviceName()) + throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found", + name}; /* Open a basic output stream, just to ensure it can work. */ oboe::ManagedStream stream; @@ -70,10 +73,10 @@ void OboePlayback::open(std::string_view name) ->setPerformanceMode(oboe::PerformanceMode::LowLatency) ->openManagedStream(stream)}; if(result != oboe::Result::OK) - throw al::backend_exception{al::backend_error::DeviceError, "Failed to create stream: %s", + throw al::backend_exception{al::backend_error::DeviceError, "Failed to create stream: {}", oboe::convertToText(result)}; - mDevice->DeviceName = name; + mDeviceName = name; } bool OboePlayback::reset() @@ -81,6 +84,7 @@ bool OboePlayback::reset() oboe::AudioStreamBuilder builder; builder.setDirection(oboe::Direction::Output); builder.setPerformanceMode(oboe::PerformanceMode::LowLatency); + builder.setUsage(oboe::Usage::Game); /* Don't let Oboe convert. We should be able to handle anything it gives * back. */ @@ -92,7 +96,7 @@ bool OboePlayback::reset() if(mDevice->Flags.test(FrequencyRequest)) { builder.setSampleRateConversionQuality(oboe::SampleRateConversionQuality::High); - builder.setSampleRate(static_cast(mDevice->Frequency)); + builder.setSampleRate(static_cast(mDevice->mSampleRate)); } if(mDevice->Flags.test(ChannelsRequest)) { @@ -142,11 +146,11 @@ bool OboePlayback::reset() result = builder.openManagedStream(mStream); } if(result != oboe::Result::OK) - throw al::backend_exception{al::backend_error::DeviceError, "Failed to create stream: %s", + throw al::backend_exception{al::backend_error::DeviceError, "Failed to create stream: {}", oboe::convertToText(result)}; - mStream->setBufferSizeInFrames(mini(static_cast(mDevice->BufferSize), + mStream->setBufferSizeInFrames(std::min(static_cast(mDevice->mBufferSize), mStream->getBufferCapacityInFrames())); - TRACE("Got stream with properties:\n%s", oboe::convertToText(mStream.get())); + TRACE("Got stream with properties:\n{}", oboe::convertToText(mStream.get())); if(static_cast(mStream->getChannelCount()) != mDevice->channelsFromFmt()) { @@ -156,7 +160,7 @@ bool OboePlayback::reset() mDevice->FmtChans = DevFmtMono; else throw al::backend_exception{al::backend_error::DeviceError, - "Got unhandled channel count: %d", mStream->getChannelCount()}; + "Got unhandled channel count: {}", mStream->getChannelCount()}; } setDefaultWFXChannelOrder(); @@ -173,22 +177,25 @@ bool OboePlayback::reset() mDevice->FmtType = DevFmtInt; break; case oboe::AudioFormat::I24: +#endif +#if OBOE_VERSION_MAJOR > 1 || (OBOE_VERSION_MAJOR == 1 && OBOE_VERSION_MINOR >= 8) + case oboe::AudioFormat::IEC61937: #endif case oboe::AudioFormat::Unspecified: case oboe::AudioFormat::Invalid: throw al::backend_exception{al::backend_error::DeviceError, - "Got unhandled sample type: %s", oboe::convertToText(mStream->getFormat())}; + "Got unhandled sample type: {}", oboe::convertToText(mStream->getFormat())}; } - mDevice->Frequency = static_cast(mStream->getSampleRate()); + mDevice->mSampleRate = static_cast(mStream->getSampleRate()); /* Ensure the period size is no less than 10ms. It's possible for FramesPerCallback to be 0 * indicating variable updates, but OpenAL should have a reasonable minimum update size set. * FramesPerBurst may not necessarily be correct, but hopefully it can act as a minimum * update size. */ - mDevice->UpdateSize = maxu(mDevice->Frequency / 100, + mDevice->mUpdateSize = std::max(mDevice->mSampleRate/100u, static_cast(mStream->getFramesPerBurst())); - mDevice->BufferSize = maxu(mDevice->UpdateSize * 2, + mDevice->mBufferSize = std::max(mDevice->mUpdateSize*2u, static_cast(mStream->getBufferSizeInFrames())); return true; @@ -198,7 +205,7 @@ void OboePlayback::start() { const oboe::Result result{mStream->start()}; if(result != oboe::Result::OK) - throw al::backend_exception{al::backend_error::DeviceError, "Failed to start stream: %s", + throw al::backend_exception{al::backend_error::DeviceError, "Failed to start stream: {}", oboe::convertToText(result)}; } @@ -206,12 +213,12 @@ void OboePlayback::stop() { oboe::Result result{mStream->stop()}; if(result != oboe::Result::OK) - ERR("Failed to stop stream: %s\n", oboe::convertToText(result)); + ERR("Failed to stop stream: {}", oboe::convertToText(result)); } struct OboeCapture final : public BackendBase, public oboe::AudioStreamCallback { - OboeCapture(DeviceBase *device) : BackendBase{device} { } + explicit OboeCapture(DeviceBase *device) : BackendBase{device} { } oboe::ManagedStream mStream; @@ -230,7 +237,7 @@ struct OboeCapture final : public BackendBase, public oboe::AudioStreamCallback oboe::DataCallbackResult OboeCapture::onAudioReady(oboe::AudioStream*, void *audioData, int32_t numFrames) { - mRing->write(audioData, static_cast(numFrames)); + std::ignore = mRing->write(audioData, static_cast(numFrames)); return oboe::DataCallbackResult::Continue; } @@ -238,10 +245,10 @@ oboe::DataCallbackResult OboeCapture::onAudioReady(oboe::AudioStream*, void *aud void OboeCapture::open(std::string_view name) { if(name.empty()) - name = device_name; - else if(name != device_name) - throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", - static_cast(name.length()), name.data()}; + name = GetDeviceName(); + else if(name != GetDeviceName()) + throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found", + name}; oboe::AudioStreamBuilder builder; builder.setDirection(oboe::Direction::Input) @@ -249,7 +256,7 @@ void OboeCapture::open(std::string_view name) ->setSampleRateConversionQuality(oboe::SampleRateConversionQuality::High) ->setChannelConversionAllowed(true) ->setFormatConversionAllowed(true) - ->setSampleRate(static_cast(mDevice->Frequency)) + ->setSampleRate(static_cast(mDevice->mSampleRate)) ->setCallback(this); /* Only use mono or stereo at user request. There's no telling what * other counts may be inferred as. @@ -267,9 +274,10 @@ void OboeCapture::open(std::string_view name) case DevFmtX61: case DevFmtX71: case DevFmtX714: + case DevFmtX7144: case DevFmtX3D71: case DevFmtAmbi3D: - throw al::backend_exception{al::backend_error::DeviceError, "%s capture not supported", + throw al::backend_exception{al::backend_error::DeviceError, "{} capture not supported", DevFmtChannelsString(mDevice->FmtChans)}; } @@ -294,28 +302,28 @@ void OboeCapture::open(std::string_view name) case DevFmtUShort: case DevFmtUInt: throw al::backend_exception{al::backend_error::DeviceError, - "%s capture samples not supported", DevFmtTypeString(mDevice->FmtType)}; + "{} capture samples not supported", DevFmtTypeString(mDevice->FmtType)}; } oboe::Result result{builder.openManagedStream(mStream)}; if(result != oboe::Result::OK) - throw al::backend_exception{al::backend_error::DeviceError, "Failed to create stream: %s", + throw al::backend_exception{al::backend_error::DeviceError, "Failed to create stream: {}", oboe::convertToText(result)}; - TRACE("Got stream with properties:\n%s", oboe::convertToText(mStream.get())); + TRACE("Got stream with properties:\n{}", oboe::convertToText(mStream.get())); /* Ensure a minimum ringbuffer size of 100ms. */ - mRing = RingBuffer::Create(maxu(mDevice->BufferSize, mDevice->Frequency/10), + mRing = RingBuffer::Create(std::max(mDevice->mBufferSize, mDevice->mSampleRate/10u), static_cast(mStream->getBytesPerFrame()), false); - mDevice->DeviceName = name; + mDeviceName = name; } void OboeCapture::start() { const oboe::Result result{mStream->start()}; if(result != oboe::Result::OK) - throw al::backend_exception{al::backend_error::DeviceError, "Failed to start stream: %s", + throw al::backend_exception{al::backend_error::DeviceError, "Failed to start stream: {}", oboe::convertToText(result)}; } @@ -323,14 +331,14 @@ void OboeCapture::stop() { const oboe::Result result{mStream->stop()}; if(result != oboe::Result::OK) - ERR("Failed to stop stream: %s\n", oboe::convertToText(result)); + ERR("Failed to stop stream: {}", oboe::convertToText(result)); } uint OboeCapture::availableSamples() { return static_cast(mRing->readSpace()); } void OboeCapture::captureSamples(std::byte *buffer, uint samples) -{ mRing->read(buffer, samples); } +{ std::ignore = mRing->read(buffer, samples); } } // namespace @@ -339,16 +347,15 @@ bool OboeBackendFactory::init() { return true; } bool OboeBackendFactory::querySupport(BackendType type) { return type == BackendType::Playback || type == BackendType::Capture; } -std::string OboeBackendFactory::probe(BackendType type) +auto OboeBackendFactory::enumerate(BackendType type) -> std::vector { switch(type) { case BackendType::Playback: case BackendType::Capture: - /* Includes null char. */ - return std::string{device_name, sizeof(device_name)}; + return std::vector{std::string{GetDeviceName()}}; } - return std::string{}; + return {}; } BackendPtr OboeBackendFactory::createBackend(DeviceBase *device, BackendType type) diff --git a/3rdparty/openal/alc/backends/oboe.h b/3rdparty/openal/alc/backends/oboe.h index a39c24454f7d..d277cfe7cf84 100644 --- a/3rdparty/openal/alc/backends/oboe.h +++ b/3rdparty/openal/alc/backends/oboe.h @@ -5,15 +5,15 @@ struct OboeBackendFactory final : public BackendFactory { public: - bool init() override; + auto init() -> bool final; - bool querySupport(BackendType type) override; + auto querySupport(BackendType type) -> bool final; - std::string probe(BackendType type) override; + auto enumerate(BackendType type) -> std::vector final; - BackendPtr createBackend(DeviceBase *device, BackendType type) override; + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; - static BackendFactory &getFactory(); + static auto getFactory() -> BackendFactory&; }; #endif /* BACKENDS_OBOE_H */ diff --git a/3rdparty/openal/alc/backends/opensl.cpp b/3rdparty/openal/alc/backends/opensl.cpp index 61e3c9a76792..155972b98f78 100644 --- a/3rdparty/openal/alc/backends/opensl.cpp +++ b/3rdparty/openal/alc/backends/opensl.cpp @@ -23,10 +23,10 @@ #include "opensl.h" -#include #include #include +#include #include #include #include @@ -36,10 +36,12 @@ #include "albit.h" #include "alnumeric.h" #include "alsem.h" +#include "alstring.h" #include "althrd_setname.h" #include "core/device.h" #include "core/helpers.h" #include "core/logging.h" +#include "dynload.h" #include "opthelpers.h" #include "ringbuffer.h" @@ -50,15 +52,43 @@ namespace { +using namespace std::string_view_literals; + + +#if HAVE_DYNLOAD +#define SLES_SYMBOLS(MAGIC) \ + MAGIC(slCreateEngine); \ + MAGIC(SL_IID_ANDROIDCONFIGURATION); \ + MAGIC(SL_IID_ANDROIDSIMPLEBUFFERQUEUE); \ + MAGIC(SL_IID_ENGINE); \ + MAGIC(SL_IID_PLAY); \ + MAGIC(SL_IID_RECORD); + +void *sles_handle; +#define MAKE_SYMBOL(f) decltype(f) * p##f +SLES_SYMBOLS(MAKE_SYMBOL) +#undef MAKE_SYMBOL + +#ifndef IN_IDE_PARSER +#define slCreateEngine (*pslCreateEngine) +#define SL_IID_ANDROIDCONFIGURATION (*pSL_IID_ANDROIDCONFIGURATION) +#define SL_IID_ANDROIDSIMPLEBUFFERQUEUE (*pSL_IID_ANDROIDSIMPLEBUFFERQUEUE) +#define SL_IID_ENGINE (*pSL_IID_ENGINE) +#define SL_IID_PLAY (*pSL_IID_PLAY) +#define SL_IID_RECORD (*pSL_IID_RECORD) +#endif +#endif + + /* Helper macros */ #define EXTRACT_VCALL_ARGS(...) __VA_ARGS__)) #define VCALL(obj, func) ((*(obj))->func((obj), EXTRACT_VCALL_ARGS #define VCALL0(obj, func) ((*(obj))->func((obj) EXTRACT_VCALL_ARGS -constexpr char opensl_device[] = "OpenSL"; - +[[nodiscard]] constexpr auto GetDeviceName() noexcept { return "OpenSL"sv; } +[[nodiscard]] constexpr SLuint32 GetChannelMask(DevFmtChannels chans) noexcept { switch(chans) @@ -82,6 +112,7 @@ constexpr SLuint32 GetChannelMask(DevFmtChannels chans) noexcept SL_SPEAKER_BACK_RIGHT | SL_SPEAKER_SIDE_LEFT | SL_SPEAKER_SIDE_RIGHT | SL_SPEAKER_TOP_FRONT_LEFT | SL_SPEAKER_TOP_FRONT_RIGHT | SL_SPEAKER_TOP_BACK_LEFT | SL_SPEAKER_TOP_BACK_RIGHT; + case DevFmtX7144: case DevFmtAmbi3D: break; } @@ -152,12 +183,12 @@ constexpr const char *res_str(SLresult result) noexcept inline void PrintErr(SLresult res, const char *str) { if(res != SL_RESULT_SUCCESS) UNLIKELY - ERR("%s: %s\n", str, res_str(res)); + ERR("{}: {}", str, res_str(res)); } struct OpenSLPlayback final : public BackendBase { - OpenSLPlayback(DeviceBase *device) noexcept : BackendBase{device} { } + explicit OpenSLPlayback(DeviceBase *device) noexcept : BackendBase{device} { } ~OpenSLPlayback() override; void process(SLAndroidSimpleBufferQueueItf bq) noexcept; @@ -189,8 +220,6 @@ struct OpenSLPlayback final : public BackendBase { std::atomic mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(OpenSLPlayback) }; OpenSLPlayback::~OpenSLPlayback() @@ -229,7 +258,7 @@ void OpenSLPlayback::process(SLAndroidSimpleBufferQueueItf) noexcept int OpenSLPlayback::mixerProc() { SetRTPriority(); - althrd_setname(MIXER_THREAD_NAME); + althrd_setname(GetMixerThreadName()); SLPlayItf player; SLAndroidSimpleBufferQueueItf bufferQueue; @@ -245,7 +274,7 @@ int OpenSLPlayback::mixerProc() const size_t frame_step{mDevice->channelsFromFmt()}; if(SL_RESULT_SUCCESS != result) - mDevice->handleDisconnect("Failed to get playback buffer: 0x%08x", result); + mDevice->handleDisconnect("Failed to get playback buffer: {:#08x}", result); while(SL_RESULT_SUCCESS == result && !mKillNow.load(std::memory_order_acquire) && mDevice->Connected.load(std::memory_order_acquire)) @@ -263,7 +292,7 @@ int OpenSLPlayback::mixerProc() } if(SL_RESULT_SUCCESS != result) { - mDevice->handleDisconnect("Failed to start playback: 0x%08x", result); + mDevice->handleDisconnect("Failed to start playback: {:#08x}", result); break; } @@ -276,35 +305,35 @@ int OpenSLPlayback::mixerProc() std::unique_lock dlock{mMutex}; auto data = mRing->getWriteVector(); - mDevice->renderSamples(data.first.buf, - static_cast(data.first.len)*mDevice->UpdateSize, frame_step); - if(data.second.len > 0) - mDevice->renderSamples(data.second.buf, - static_cast(data.second.len)*mDevice->UpdateSize, frame_step); + mDevice->renderSamples(data[0].buf, + static_cast(data[0].len)*mDevice->mUpdateSize, frame_step); + if(data[1].len > 0) + mDevice->renderSamples(data[1].buf, + static_cast(data[1].len)*mDevice->mUpdateSize, frame_step); - size_t todo{data.first.len + data.second.len}; + const auto todo = size_t{data[0].len + data[1].len}; mRing->writeAdvance(todo); dlock.unlock(); for(size_t i{0};i < todo;i++) { - if(!data.first.len) + if(!data[0].len) { - data.first = data.second; - data.second.buf = nullptr; - data.second.len = 0; + data[0] = data[1]; + data[1].buf = nullptr; + data[1].len = 0; } - result = VCALL(bufferQueue,Enqueue)(data.first.buf, mDevice->UpdateSize*mFrameSize); + result = VCALL(bufferQueue,Enqueue)(data[0].buf, mDevice->mUpdateSize*mFrameSize); PrintErr(result, "bufferQueue->Enqueue"); if(SL_RESULT_SUCCESS != result) { - mDevice->handleDisconnect("Failed to queue audio: 0x%08x", result); + mDevice->handleDisconnect("Failed to queue audio: {:#08x}", result); break; } - data.first.len--; - data.first.buf += mDevice->UpdateSize*mFrameSize; + data[0].len--; + data[0].buf += mDevice->mUpdateSize*mFrameSize; } } @@ -315,10 +344,10 @@ int OpenSLPlayback::mixerProc() void OpenSLPlayback::open(std::string_view name) { if(name.empty()) - name = opensl_device; - else if(name != opensl_device) - throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", - static_cast(name.length()), name.data()}; + name = GetDeviceName(); + else if(name != GetDeviceName()) + throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found", + name}; /* There's only one device, so if it's already open, there's nothing to do. */ if(mEngineObj) return; @@ -359,10 +388,10 @@ void OpenSLPlayback::open(std::string_view name) mEngine = nullptr; throw al::backend_exception{al::backend_error::DeviceError, - "Failed to initialize OpenSL device: 0x%08x", result}; + "Failed to initialize OpenSL device: {:#08x}", result}; } - mDevice->DeviceName = name; + mDeviceName = name; } bool OpenSLPlayback::reset() @@ -375,74 +404,6 @@ bool OpenSLPlayback::reset() mRing = nullptr; -#if 0 - if(!mDevice->Flags.get()) - { - /* FIXME: Disabled until I figure out how to get the Context needed for - * the getSystemService call. - */ - JNIEnv *env = Android_GetJNIEnv(); - jobject jctx = Android_GetContext(); - - /* Get necessary stuff for using java.lang.Integer, - * android.content.Context, and android.media.AudioManager. - */ - jclass int_cls = JCALL(env,FindClass)("java/lang/Integer"); - jmethodID int_parseint = JCALL(env,GetStaticMethodID)(int_cls, - "parseInt", "(Ljava/lang/String;)I" - ); - TRACE("Integer: %p, parseInt: %p\n", int_cls, int_parseint); - - jclass ctx_cls = JCALL(env,FindClass)("android/content/Context"); - jfieldID ctx_audsvc = JCALL(env,GetStaticFieldID)(ctx_cls, - "AUDIO_SERVICE", "Ljava/lang/String;" - ); - jmethodID ctx_getSysSvc = JCALL(env,GetMethodID)(ctx_cls, - "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;" - ); - TRACE("Context: %p, AUDIO_SERVICE: %p, getSystemService: %p\n", - ctx_cls, ctx_audsvc, ctx_getSysSvc); - - jclass audmgr_cls = JCALL(env,FindClass)("android/media/AudioManager"); - jfieldID audmgr_prop_out_srate = JCALL(env,GetStaticFieldID)(audmgr_cls, - "PROPERTY_OUTPUT_SAMPLE_RATE", "Ljava/lang/String;" - ); - jmethodID audmgr_getproperty = JCALL(env,GetMethodID)(audmgr_cls, - "getProperty", "(Ljava/lang/String;)Ljava/lang/String;" - ); - TRACE("AudioManager: %p, PROPERTY_OUTPUT_SAMPLE_RATE: %p, getProperty: %p\n", - audmgr_cls, audmgr_prop_out_srate, audmgr_getproperty); - - const char *strchars; - jstring strobj; - - /* Now make the calls. */ - //AudioManager audMgr = (AudioManager)getSystemService(Context.AUDIO_SERVICE); - strobj = JCALL(env,GetStaticObjectField)(ctx_cls, ctx_audsvc); - jobject audMgr = JCALL(env,CallObjectMethod)(jctx, ctx_getSysSvc, strobj); - strchars = JCALL(env,GetStringUTFChars)(strobj, nullptr); - TRACE("Context.getSystemService(%s) = %p\n", strchars, audMgr); - JCALL(env,ReleaseStringUTFChars)(strobj, strchars); - - //String srateStr = audMgr.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE); - strobj = JCALL(env,GetStaticObjectField)(audmgr_cls, audmgr_prop_out_srate); - jstring srateStr = JCALL(env,CallObjectMethod)(audMgr, audmgr_getproperty, strobj); - strchars = JCALL(env,GetStringUTFChars)(strobj, nullptr); - TRACE("audMgr.getProperty(%s) = %p\n", strchars, srateStr); - JCALL(env,ReleaseStringUTFChars)(strobj, strchars); - - //int sampleRate = Integer.parseInt(srateStr); - sampleRate = JCALL(env,CallStaticIntMethod)(int_cls, int_parseint, srateStr); - - strchars = JCALL(env,GetStringUTFChars)(srateStr, nullptr); - TRACE("Got system sample rate %uhz (%s)\n", sampleRate, strchars); - JCALL(env,ReleaseStringUTFChars)(srateStr, strchars); - - if(!sampleRate) sampleRate = device->Frequency; - else sampleRate = maxu(sampleRate, MIN_OUTPUT_RATE); - } -#endif - mDevice->FmtChans = DevFmtStereo; mDevice->FmtType = DevFmtShort; @@ -463,14 +424,14 @@ bool OpenSLPlayback::reset() SLDataLocator_AndroidSimpleBufferQueue loc_bufq{}; loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; - loc_bufq.numBuffers = mDevice->BufferSize / mDevice->UpdateSize; + loc_bufq.numBuffers = mDevice->mBufferSize / mDevice->mUpdateSize; SLDataSource audioSrc{}; #ifdef SL_ANDROID_DATAFORMAT_PCM_EX SLAndroidDataFormat_PCM_EX format_pcm_ex{}; format_pcm_ex.formatType = SL_ANDROID_DATAFORMAT_PCM_EX; format_pcm_ex.numChannels = mDevice->channelsFromFmt(); - format_pcm_ex.sampleRate = mDevice->Frequency * 1000; + format_pcm_ex.sampleRate = mDevice->mSampleRate * 1000; format_pcm_ex.bitsPerSample = mDevice->bytesFromFmt() * 8; format_pcm_ex.containerSize = format_pcm_ex.bitsPerSample; format_pcm_ex.channelMask = GetChannelMask(mDevice->FmtChans); @@ -501,7 +462,7 @@ bool OpenSLPlayback::reset() SLDataFormat_PCM format_pcm{}; format_pcm.formatType = SL_DATAFORMAT_PCM; format_pcm.numChannels = mDevice->channelsFromFmt(); - format_pcm.samplesPerSec = mDevice->Frequency * 1000; + format_pcm.samplesPerSec = mDevice->mSampleRate * 1000; format_pcm.bitsPerSample = mDevice->bytesFromFmt() * 8; format_pcm.containerSize = format_pcm.bitsPerSample; format_pcm.channelMask = GetChannelMask(mDevice->FmtChans); @@ -538,8 +499,8 @@ bool OpenSLPlayback::reset() } if(SL_RESULT_SUCCESS == result) { - const uint num_updates{mDevice->BufferSize / mDevice->UpdateSize}; - mRing = RingBuffer::Create(num_updates, mFrameSize*mDevice->UpdateSize, true); + const uint num_updates{mDevice->mBufferSize / mDevice->mUpdateSize}; + mRing = RingBuffer::Create(num_updates, mFrameSize*mDevice->mUpdateSize, true); } if(SL_RESULT_SUCCESS != result) @@ -571,15 +532,15 @@ void OpenSLPlayback::start() } if(SL_RESULT_SUCCESS != result) throw al::backend_exception{al::backend_error::DeviceError, - "Failed to register callback: 0x%08x", result}; + "Failed to register callback: {:#08x}", result}; try { mKillNow.store(false, std::memory_order_release); - mThread = std::thread(std::mem_fn(&OpenSLPlayback::mixerProc), this); + mThread = std::thread(&OpenSLPlayback::mixerProc, this); } catch(std::exception& e) { throw al::backend_exception{al::backend_error::DeviceError, - "Failed to start mixing thread: %s", e.what()}; + "Failed to start mixing thread: {}", e.what()}; } } @@ -630,17 +591,17 @@ ClockLatency OpenSLPlayback::getClockLatency() { ClockLatency ret; - std::lock_guard _{mMutex}; - ret.ClockTime = GetDeviceClockTime(mDevice); - ret.Latency = std::chrono::seconds{mRing->readSpace() * mDevice->UpdateSize}; - ret.Latency /= mDevice->Frequency; + std::lock_guard dlock{mMutex}; + ret.ClockTime = mDevice->getClockTime(); + ret.Latency = std::chrono::seconds{mRing->readSpace() * mDevice->mUpdateSize}; + ret.Latency /= mDevice->mSampleRate; return ret; } struct OpenSLCapture final : public BackendBase { - OpenSLCapture(DeviceBase *device) noexcept : BackendBase{device} { } + explicit OpenSLCapture(DeviceBase *device) noexcept : BackendBase{device} { } ~OpenSLCapture() override; void process(SLAndroidSimpleBufferQueueItf bq) noexcept; @@ -662,8 +623,6 @@ struct OpenSLCapture final : public BackendBase { uint mSplOffset{0u}; uint mFrameSize{0}; - - DEF_NEWDEL(OpenSLCapture) }; OpenSLCapture::~OpenSLCapture() @@ -689,10 +648,10 @@ void OpenSLCapture::process(SLAndroidSimpleBufferQueueItf) noexcept void OpenSLCapture::open(std::string_view name) { if(name.empty()) - name = opensl_device; - else if(name != opensl_device) - throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", - static_cast(name.length()), name.data()}; + name = GetDeviceName(); + else if(name != GetDeviceName()) + throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found", + name}; SLresult result{slCreateEngine(&mEngineObj, 0, nullptr, 0, nullptr, nullptr)}; PrintErr(result, "slCreateEngine"); @@ -710,16 +669,16 @@ void OpenSLCapture::open(std::string_view name) { mFrameSize = mDevice->frameSizeFromFmt(); /* Ensure the total length is at least 100ms */ - uint length{maxu(mDevice->BufferSize, mDevice->Frequency/10)}; + uint length{std::max(mDevice->mBufferSize, mDevice->mSampleRate/10u)}; /* Ensure the per-chunk length is at least 10ms, and no more than 50ms. */ - uint update_len{clampu(mDevice->BufferSize/3, mDevice->Frequency/100, - mDevice->Frequency/100*5)}; + uint update_len{std::clamp(mDevice->mBufferSize/3u, mDevice->mSampleRate/100u, + mDevice->mSampleRate/100u*5u)}; uint num_updates{(length+update_len-1) / update_len}; mRing = RingBuffer::Create(num_updates, update_len*mFrameSize, false); - mDevice->UpdateSize = update_len; - mDevice->BufferSize = static_cast(mRing->writeSpace() * update_len); + mDevice->mUpdateSize = update_len; + mDevice->mBufferSize = static_cast(mRing->writeSpace() * update_len); } if(SL_RESULT_SUCCESS == result) { @@ -738,14 +697,14 @@ void OpenSLCapture::open(std::string_view name) SLDataLocator_AndroidSimpleBufferQueue loc_bq{}; loc_bq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; - loc_bq.numBuffers = mDevice->BufferSize / mDevice->UpdateSize; + loc_bq.numBuffers = mDevice->mBufferSize / mDevice->mUpdateSize; SLDataSink audioSnk{}; #ifdef SL_ANDROID_DATAFORMAT_PCM_EX SLAndroidDataFormat_PCM_EX format_pcm_ex{}; format_pcm_ex.formatType = SL_ANDROID_DATAFORMAT_PCM_EX; format_pcm_ex.numChannels = mDevice->channelsFromFmt(); - format_pcm_ex.sampleRate = mDevice->Frequency * 1000; + format_pcm_ex.sampleRate = mDevice->mSampleRate * 1000; format_pcm_ex.bitsPerSample = mDevice->bytesFromFmt() * 8; format_pcm_ex.containerSize = format_pcm_ex.bitsPerSample; format_pcm_ex.channelMask = GetChannelMask(mDevice->FmtChans); @@ -768,7 +727,7 @@ void OpenSLCapture::open(std::string_view name) SLDataFormat_PCM format_pcm{}; format_pcm.formatType = SL_DATAFORMAT_PCM; format_pcm.numChannels = mDevice->channelsFromFmt(); - format_pcm.samplesPerSec = mDevice->Frequency * 1000; + format_pcm.samplesPerSec = mDevice->mSampleRate * 1000; format_pcm.bitsPerSample = mDevice->bytesFromFmt() * 8; format_pcm.containerSize = format_pcm.bitsPerSample; format_pcm.channelMask = GetChannelMask(mDevice->FmtChans); @@ -820,20 +779,20 @@ void OpenSLCapture::open(std::string_view name) } if(SL_RESULT_SUCCESS == result) { - const uint chunk_size{mDevice->UpdateSize * mFrameSize}; + const uint chunk_size{mDevice->mUpdateSize * mFrameSize}; const auto silence = (mDevice->FmtType == DevFmtUByte) ? std::byte{0x80} : std::byte{0}; auto data = mRing->getWriteVector(); - std::fill_n(data.first.buf, data.first.len*chunk_size, silence); - std::fill_n(data.second.buf, data.second.len*chunk_size, silence); - for(size_t i{0u};i < data.first.len && SL_RESULT_SUCCESS == result;i++) + std::fill_n(data[0].buf, data[0].len*chunk_size, silence); + std::fill_n(data[1].buf, data[1].len*chunk_size, silence); + for(size_t i{0u};i < data[0].len && SL_RESULT_SUCCESS == result;i++) { - result = VCALL(bufferQueue,Enqueue)(data.first.buf + chunk_size*i, chunk_size); + result = VCALL(bufferQueue,Enqueue)(data[0].buf + chunk_size*i, chunk_size); PrintErr(result, "bufferQueue->Enqueue"); } - for(size_t i{0u};i < data.second.len && SL_RESULT_SUCCESS == result;i++) + for(size_t i{0u};i < data[1].len && SL_RESULT_SUCCESS == result;i++) { - result = VCALL(bufferQueue,Enqueue)(data.second.buf + chunk_size*i, chunk_size); + result = VCALL(bufferQueue,Enqueue)(data[1].buf + chunk_size*i, chunk_size); PrintErr(result, "bufferQueue->Enqueue"); } } @@ -850,10 +809,10 @@ void OpenSLCapture::open(std::string_view name) mEngine = nullptr; throw al::backend_exception{al::backend_error::DeviceError, - "Failed to initialize OpenSL device: 0x%08x", result}; + "Failed to initialize OpenSL device: {:#08x}", result}; } - mDevice->DeviceName = name; + mDeviceName = name; } void OpenSLCapture::start() @@ -869,7 +828,7 @@ void OpenSLCapture::start() } if(SL_RESULT_SUCCESS != result) throw al::backend_exception{al::backend_error::DeviceError, - "Failed to start capture: 0x%08x", result}; + "Failed to start capture: {:#08x}", result}; } void OpenSLCapture::stop() @@ -887,7 +846,7 @@ void OpenSLCapture::stop() void OpenSLCapture::captureSamples(std::byte *buffer, uint samples) { - const uint update_size{mDevice->UpdateSize}; + const uint update_size{mDevice->mUpdateSize}; const uint chunk_size{update_size * mFrameSize}; /* Read the desired samples from the ring buffer then advance its read @@ -897,8 +856,8 @@ void OpenSLCapture::captureSamples(std::byte *buffer, uint samples) auto rdata = mRing->getReadVector(); for(uint i{0};i < samples;) { - const uint rem{minu(samples - i, update_size - mSplOffset)}; - std::copy_n(rdata.first.buf + mSplOffset*size_t{mFrameSize}, rem*size_t{mFrameSize}, + const uint rem{std::min(samples - i, update_size - mSplOffset)}; + std::copy_n(rdata[0].buf + mSplOffset*size_t{mFrameSize}, rem*size_t{mFrameSize}, buffer + i*size_t{mFrameSize}); mSplOffset += rem; @@ -908,11 +867,11 @@ void OpenSLCapture::captureSamples(std::byte *buffer, uint samples) mSplOffset = 0; ++adv_count; - rdata.first.len -= 1; - if(!rdata.first.len) - rdata.first = rdata.second; + rdata[0].len -= 1; + if(!rdata[0].len) + rdata[0] = rdata[1]; else - rdata.first.buf += chunk_size; + rdata[0].buf += chunk_size; } i += rem; @@ -926,7 +885,7 @@ void OpenSLCapture::captureSamples(std::byte *buffer, uint samples) PrintErr(result, "recordObj->GetInterface"); if(SL_RESULT_SUCCESS != result) UNLIKELY { - mDevice->handleDisconnect("Failed to get capture buffer queue: 0x%08x", result); + mDevice->handleDisconnect("Failed to get capture buffer queue: {:#08x}", result); bufferQueue = nullptr; } } @@ -945,20 +904,20 @@ void OpenSLCapture::captureSamples(std::byte *buffer, uint samples) SLresult result{SL_RESULT_SUCCESS}; auto wdata = mRing->getWriteVector(); - if(adv_count > wdata.second.len) LIKELY + if(adv_count > wdata[1].len) LIKELY { - auto len1 = std::min(wdata.first.len, adv_count-wdata.second.len); - auto buf1 = wdata.first.buf + chunk_size*(wdata.first.len-len1); + auto len1 = std::min(wdata[0].len, adv_count-wdata[1].len); + auto buf1 = wdata[0].buf + chunk_size*(wdata[0].len-len1); for(size_t i{0u};i < len1 && SL_RESULT_SUCCESS == result;i++) { result = VCALL(bufferQueue,Enqueue)(buf1 + chunk_size*i, chunk_size); PrintErr(result, "bufferQueue->Enqueue"); } } - if(wdata.second.len > 0) + if(wdata[1].len > 0) { - auto len2 = std::min(wdata.second.len, adv_count); - auto buf2 = wdata.second.buf + chunk_size*(wdata.second.len-len2); + auto len2 = std::min(wdata[1].len, adv_count); + auto buf2 = wdata[1].buf + chunk_size*(wdata[1].len-len2); for(size_t i{0u};i < len2 && SL_RESULT_SUCCESS == result;i++) { result = VCALL(bufferQueue,Enqueue)(buf2 + chunk_size*i, chunk_size); @@ -968,27 +927,56 @@ void OpenSLCapture::captureSamples(std::byte *buffer, uint samples) } uint OpenSLCapture::availableSamples() -{ return static_cast(mRing->readSpace()*mDevice->UpdateSize - mSplOffset); } +{ return static_cast(mRing->readSpace()*mDevice->mUpdateSize - mSplOffset); } } // namespace -bool OSLBackendFactory::init() { return true; } +bool OSLBackendFactory::init() +{ +#if HAVE_DYNLOAD + if(!sles_handle) + { +#define SLES_LIBNAME "libOpenSLES.so" + sles_handle = LoadLib(SLES_LIBNAME); + if(!sles_handle) + { + WARN("Failed to load {}", SLES_LIBNAME); + return false; + } + + std::string missing_syms; +#define LOAD_SYMBOL(f) do { \ + p##f = reinterpret_cast(GetSymbol(sles_handle, #f)); \ + if(p##f == nullptr) missing_syms += "\n" #f; \ +} while(0) + SLES_SYMBOLS(LOAD_SYMBOL); +#undef LOAD_SYMBOL + + if(!missing_syms.empty()) + { + WARN("Missing expected symbols:{}", missing_syms); + CloseLib(sles_handle); + sles_handle = nullptr; + return false; + } + } +#endif + + return true; +} bool OSLBackendFactory::querySupport(BackendType type) { return (type == BackendType::Playback || type == BackendType::Capture); } -std::string OSLBackendFactory::probe(BackendType type) +auto OSLBackendFactory::enumerate(BackendType type) -> std::vector { - std::string outnames; switch(type) { case BackendType::Playback: case BackendType::Capture: - /* Includes null char. */ - outnames.append(opensl_device, sizeof(opensl_device)); - break; + return std::vector{std::string{GetDeviceName()}}; } - return outnames; + return {}; } BackendPtr OSLBackendFactory::createBackend(DeviceBase *device, BackendType type) diff --git a/3rdparty/openal/alc/backends/opensl.h b/3rdparty/openal/alc/backends/opensl.h index b81624476cc0..9f13dd71f9d0 100644 --- a/3rdparty/openal/alc/backends/opensl.h +++ b/3rdparty/openal/alc/backends/opensl.h @@ -5,15 +5,15 @@ struct OSLBackendFactory final : public BackendFactory { public: - bool init() override; + auto init() -> bool final; - bool querySupport(BackendType type) override; + auto querySupport(BackendType type) -> bool final; - std::string probe(BackendType type) override; + auto enumerate(BackendType type) -> std::vector final; - BackendPtr createBackend(DeviceBase *device, BackendType type) override; + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; - static BackendFactory &getFactory(); + static auto getFactory() -> BackendFactory&; }; #endif /* BACKENDS_OSL_H */ diff --git a/3rdparty/openal/alc/backends/oss.cpp b/3rdparty/openal/alc/backends/oss.cpp index 87d3ba35b9d2..cf73c18cab9b 100644 --- a/3rdparty/openal/alc/backends/oss.cpp +++ b/3rdparty/openal/alc/backends/oss.cpp @@ -33,20 +33,21 @@ #include #include #include -#include #include #include +#include +#include #include #include #include #include "alc/alconfig.h" -#include "almalloc.h" #include "alnumeric.h" #include "althrd_setname.h" #include "core/device.h" #include "core/helpers.h" #include "core/logging.h" +#include "fmt/core.h" #include "ringbuffer.h" #include @@ -79,13 +80,22 @@ namespace { -constexpr char DefaultName[] = "OSS Default"; -std::string DefaultPlayback{"/dev/dsp"}; -std::string DefaultCapture{"/dev/dsp"}; +using namespace std::string_literals; +using namespace std::string_view_literals; + +[[nodiscard]] constexpr auto GetDefaultName() noexcept { return "OSS Default"sv; } + +std::string DefaultPlayback{"/dev/dsp"s}; +std::string DefaultCapture{"/dev/dsp"s}; struct DevMap { std::string name; std::string device_name; + + template + DevMap(T&& name_, U&& devname_) + : name{std::forward(name_)}, device_name{std::forward(devname_)} + { } }; std::vector PlaybackDevices; @@ -98,98 +108,114 @@ std::vector CaptureDevices; #define DSP_CAP_INPUT 0x00010000 void ALCossListPopulate(std::vector &devlist, int type) { - devlist.emplace_back(DevMap{DefaultName, (type==DSP_CAP_INPUT) ? DefaultCapture : DefaultPlayback}); + devlist.emplace_back(GetDefaultName(), (type==DSP_CAP_INPUT) ? DefaultCapture : DefaultPlayback); } #else -void ALCossListAppend(std::vector &list, al::span handle, al::span path) +class FileHandle { + int mFd{-1}; + +public: + FileHandle() = default; + FileHandle(const FileHandle&) = delete; + FileHandle& operator=(const FileHandle&) = delete; + ~FileHandle() { if(mFd != -1) ::close(mFd); } + + template + [[nodiscard]] auto open(const char *fname, Args&& ...args) -> bool + { + close(); + mFd = ::open(fname, std::forward(args)...); + return mFd != -1; + } + void close() + { + if(mFd != -1) + ::close(mFd); + mFd = -1; + } + + [[nodiscard]] + auto get() const noexcept -> int { return mFd; } +}; + +void ALCossListAppend(std::vector &list, std::string_view handle, std::string_view path) { #ifdef ALC_OSS_DEVNODE_TRUC for(size_t i{0};i < path.size();++i) { - if(path[i] == '.' && handle.size() + i >= path.size()) + if(path[i] == '.' && handle.size() >= path.size() - i) { const size_t hoffset{handle.size() + i - path.size()}; if(strncmp(path.data() + i, handle.data() + hoffset, path.size() - i) == 0) - handle = handle.first(hoffset); - path = path.first(i); + handle = handle.substr(0, hoffset); + path = path.substr(0, i); } } #endif if(handle.empty()) handle = path; - std::string basename{handle.data(), handle.size()}; - std::string devname{path.data(), path.size()}; - - auto match_devname = [&devname](const DevMap &entry) -> bool - { return entry.device_name == devname; }; + auto match_devname = [path](const DevMap &entry) -> bool + { return entry.device_name == path; }; if(std::find_if(list.cbegin(), list.cend(), match_devname) != list.cend()) return; - auto checkName = [&list](const std::string &name) -> bool + auto checkName = [&list](const std::string_view name) -> bool { - auto match_name = [&name](const DevMap &entry) -> bool { return entry.name == name; }; + auto match_name = [name](const DevMap &entry) -> bool { return entry.name == name; }; return std::find_if(list.cbegin(), list.cend(), match_name) != list.cend(); }; - int count{1}; - std::string newname{basename}; + auto count = 1; + auto newname = std::string{handle}; while(checkName(newname)) - { - newname = basename; - newname += " #"; - newname += std::to_string(++count); - } + newname = fmt::format("{} #{}", handle, ++count); - list.emplace_back(DevMap{std::move(newname), std::move(devname)}); - const DevMap &entry = list.back(); - - TRACE("Got device \"%s\", \"%s\"\n", entry.name.c_str(), entry.device_name.c_str()); + const auto &entry = list.emplace_back(std::move(newname), path); + TRACE("Got device \"{}\", \"{}\"", entry.name, entry.device_name); } void ALCossListPopulate(std::vector &devlist, int type_flag) { - int fd{open("/dev/mixer", O_RDONLY)}; - if(fd < 0) + oss_sysinfo si{}; + FileHandle file; + if(!file.open("/dev/mixer", O_RDONLY)) { - TRACE("Could not open /dev/mixer: %s\n", strerror(errno)); + TRACE("Could not open /dev/mixer: {}", std::generic_category().message(errno)); goto done; } - oss_sysinfo si; - if(ioctl(fd, SNDCTL_SYSINFO, &si) == -1) + if(ioctl(file.get(), SNDCTL_SYSINFO, &si) == -1) { - TRACE("SNDCTL_SYSINFO failed: %s\n", strerror(errno)); + TRACE("SNDCTL_SYSINFO failed: {}", std::generic_category().message(errno)); goto done; } for(int i{0};i < si.numaudios;i++) { - oss_audioinfo ai; + oss_audioinfo ai{}; ai.dev = i; - if(ioctl(fd, SNDCTL_AUDIOINFO, &ai) == -1) + if(ioctl(file.get(), SNDCTL_AUDIOINFO, &ai) == -1) { - ERR("SNDCTL_AUDIOINFO (%d) failed: %s\n", i, strerror(errno)); + ERR("SNDCTL_AUDIOINFO ({}) failed: {}", i, std::generic_category().message(errno)); continue; } if(!(ai.caps&type_flag) || ai.devnode[0] == '\0') continue; - al::span handle; + std::string_view handle; if(ai.handle[0] != '\0') handle = {ai.handle, strnlen(ai.handle, sizeof(ai.handle))}; else handle = {ai.name, strnlen(ai.name, sizeof(ai.name))}; - al::span devnode{ai.devnode, strnlen(ai.devnode, sizeof(ai.devnode))}; + const std::string_view devnode{ai.devnode, strnlen(ai.devnode, sizeof(ai.devnode))}; ALCossListAppend(devlist, handle, devnode); } done: - if(fd >= 0) - close(fd); - fd = -1; + file.close(); const char *defdev{((type_flag==DSP_CAP_INPUT) ? DefaultCapture : DefaultPlayback).c_str()}; auto iter = std::find_if(devlist.cbegin(), devlist.cend(), @@ -197,7 +223,7 @@ void ALCossListPopulate(std::vector &devlist, int type_flag) { return entry.device_name == defdev; } ); if(iter == devlist.cend()) - devlist.insert(devlist.begin(), DevMap{DefaultName, defdev}); + devlist.insert(devlist.begin(), DevMap{GetDefaultName(), defdev}); else { DevMap entry{std::move(*iter)}; @@ -222,7 +248,7 @@ uint log2i(uint x) struct OSSPlayback final : public BackendBase { - OSSPlayback(DeviceBase *device) noexcept : BackendBase{device} { } + explicit OSSPlayback(DeviceBase *device) noexcept : BackendBase{device} { } ~OSSPlayback() override; int mixerProc(); @@ -238,8 +264,6 @@ struct OSSPlayback final : public BackendBase { std::atomic mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(OSSPlayback) }; OSSPlayback::~OSSPlayback() @@ -253,7 +277,7 @@ OSSPlayback::~OSSPlayback() int OSSPlayback::mixerProc() { SetRTPriority(); - althrd_setname(MIXER_THREAD_NAME); + althrd_setname(GetMixerThreadName()); const size_t frame_step{mDevice->channelsFromFmt()}; const size_t frame_size{mDevice->frameSizeFromFmt()}; @@ -265,38 +289,38 @@ int OSSPlayback::mixerProc() pollitem.fd = mFd; pollitem.events = POLLOUT; - int pret{poll(&pollitem, 1, 1000)}; - if(pret < 0) + if(int pret{poll(&pollitem, 1, 1000)}; pret < 0) { if(errno == EINTR || errno == EAGAIN) continue; - ERR("poll failed: %s\n", strerror(errno)); - mDevice->handleDisconnect("Failed waiting for playback buffer: %s", strerror(errno)); + const auto errstr = std::generic_category().message(errno); + ERR("poll failed: {}", errstr); + mDevice->handleDisconnect("Failed waiting for playback buffer: {}", errstr); break; } - else if(pret == 0) + else if(pret == 0) /* NOLINT(*-else-after-return) 'pret' is local to the if/else blocks */ { - WARN("poll timeout\n"); + WARN("poll timeout"); continue; } - std::byte *write_ptr{mMixData.data()}; - size_t to_write{mMixData.size()}; - mDevice->renderSamples(write_ptr, static_cast(to_write/frame_size), frame_step); - while(to_write > 0 && !mKillNow.load(std::memory_order_acquire)) + al::span write_buf{mMixData}; + mDevice->renderSamples(write_buf.data(), static_cast(write_buf.size()/frame_size), + frame_step); + while(!write_buf.empty() && !mKillNow.load(std::memory_order_acquire)) { - ssize_t wrote{write(mFd, write_ptr, to_write)}; + ssize_t wrote{write(mFd, write_buf.data(), write_buf.size())}; if(wrote < 0) { if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) continue; - ERR("write failed: %s\n", strerror(errno)); - mDevice->handleDisconnect("Failed writing playback samples: %s", strerror(errno)); + const auto errstr = std::generic_category().message(errno); + ERR("write failed: {}", errstr); + mDevice->handleDisconnect("Failed writing playback samples: {}", errstr); break; } - to_write -= static_cast(wrote); - write_ptr += wrote; + write_buf = write_buf.subspan(static_cast(wrote)); } } @@ -308,7 +332,7 @@ void OSSPlayback::open(std::string_view name) { const char *devname{DefaultPlayback.c_str()}; if(name.empty()) - name = DefaultName; + name = GetDefaultName(); else { if(PlaybackDevices.empty()) @@ -320,20 +344,20 @@ void OSSPlayback::open(std::string_view name) ); if(iter == PlaybackDevices.cend()) throw al::backend_exception{al::backend_error::NoDevice, - "Device name \"%.*s\" not found", static_cast(name.length()), name.data()}; + "Device name \"{}\" not found", name}; devname = iter->device_name.c_str(); } - int fd{::open(devname, O_WRONLY)}; + const auto fd = ::open(devname, O_WRONLY); /* NOLINT(cppcoreguidelines-pro-type-vararg) */ if(fd == -1) - throw al::backend_exception{al::backend_error::NoDevice, "Could not open %s: %s", devname, - strerror(errno)}; + throw al::backend_exception{al::backend_error::NoDevice, "Could not open {}: {}", devname, + std::generic_category().message(errno)}; if(mFd != -1) ::close(mFd); mFd = fd; - mDevice->DeviceName = name; + mDeviceName = name; } bool OSSPlayback::reset() @@ -358,38 +382,33 @@ bool OSSPlayback::reset() break; } - uint periods{mDevice->BufferSize / mDevice->UpdateSize}; + uint periods{mDevice->mBufferSize / mDevice->mUpdateSize}; uint numChannels{mDevice->channelsFromFmt()}; - uint ossSpeed{mDevice->Frequency}; + uint ossSpeed{mDevice->mSampleRate}; uint frameSize{numChannels * mDevice->bytesFromFmt()}; /* According to the OSS spec, 16 bytes (log2(16)) is the minimum. */ - uint log2FragmentSize{maxu(log2i(mDevice->UpdateSize*frameSize), 4)}; + uint log2FragmentSize{std::max(log2i(mDevice->mUpdateSize*frameSize), 4u)}; uint numFragmentsLogSize{(periods << 16) | log2FragmentSize}; audio_buf_info info{}; - const char *err; -#define CHECKERR(func) if((func) < 0) { \ - err = #func; \ - goto err; \ -} +#define CHECKERR(func) if((func) < 0) \ + throw al::backend_exception{al::backend_error::DeviceError, #func " failed: {}", \ + std::generic_category().message(errno)}; + /* Don't fail if SETFRAGMENT fails. We can handle just about anything * that's reported back via GETOSPACE */ + /* NOLINTBEGIN(cppcoreguidelines-pro-type-vararg) */ ioctl(mFd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize); CHECKERR(ioctl(mFd, SNDCTL_DSP_SETFMT, &ossFormat)); CHECKERR(ioctl(mFd, SNDCTL_DSP_CHANNELS, &numChannels)); CHECKERR(ioctl(mFd, SNDCTL_DSP_SPEED, &ossSpeed)); CHECKERR(ioctl(mFd, SNDCTL_DSP_GETOSPACE, &info)); - if(0) - { - err: - ERR("%s failed: %s\n", err, strerror(errno)); - return false; - } + /* NOLINTEND(cppcoreguidelines-pro-type-vararg) */ #undef CHECKERR if(mDevice->channelsFromFmt() != numChannels) { - ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(mDevice->FmtChans), + ERR("Failed to set {}, got {} channels instead", DevFmtChannelsString(mDevice->FmtChans), numChannels); return false; } @@ -398,18 +417,18 @@ bool OSSPlayback::reset() (ossFormat == AFMT_U8 && mDevice->FmtType == DevFmtUByte) || (ossFormat == AFMT_S16_NE && mDevice->FmtType == DevFmtShort))) { - ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(mDevice->FmtType), - ossFormat); + ERR("Failed to set {} samples, got OSS format {:#x}", DevFmtTypeString(mDevice->FmtType), + as_unsigned(ossFormat)); return false; } - mDevice->Frequency = ossSpeed; - mDevice->UpdateSize = static_cast(info.fragsize) / frameSize; - mDevice->BufferSize = static_cast(info.fragments) * mDevice->UpdateSize; + mDevice->mSampleRate = ossSpeed; + mDevice->mUpdateSize = static_cast(info.fragsize) / frameSize; + mDevice->mBufferSize = static_cast(info.fragments) * mDevice->mUpdateSize; setDefaultChannelOrder(); - mMixData.resize(mDevice->UpdateSize * mDevice->frameSizeFromFmt()); + mMixData.resize(size_t{mDevice->mUpdateSize} * mDevice->frameSizeFromFmt()); return true; } @@ -418,11 +437,11 @@ void OSSPlayback::start() { try { mKillNow.store(false, std::memory_order_release); - mThread = std::thread{std::mem_fn(&OSSPlayback::mixerProc), this}; + mThread = std::thread{&OSSPlayback::mixerProc, this}; } catch(std::exception& e) { throw al::backend_exception{al::backend_error::DeviceError, - "Failed to start mixing thread: %s", e.what()}; + "Failed to start mixing thread: {}", e.what()}; } } @@ -432,13 +451,13 @@ void OSSPlayback::stop() return; mThread.join(); - if(ioctl(mFd, SNDCTL_DSP_RESET) != 0) - ERR("Error resetting device: %s\n", strerror(errno)); + if(ioctl(mFd, SNDCTL_DSP_RESET) != 0) /* NOLINT(cppcoreguidelines-pro-type-vararg) */ + ERR("Error resetting device: {}", std::generic_category().message(errno)); } struct OSScapture final : public BackendBase { - OSScapture(DeviceBase *device) noexcept : BackendBase{device} { } + explicit OSScapture(DeviceBase *device) noexcept : BackendBase{device} { } ~OSScapture() override; int recordProc(); @@ -455,8 +474,6 @@ struct OSScapture final : public BackendBase { std::atomic mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(OSScapture) }; OSScapture::~OSScapture() @@ -470,7 +487,7 @@ OSScapture::~OSScapture() int OSScapture::recordProc() { SetRTPriority(); - althrd_setname(RECORD_THREAD_NAME); + althrd_setname(GetRecordThreadName()); const size_t frame_size{mDevice->frameSizeFromFmt()}; while(!mKillNow.load(std::memory_order_acquire)) @@ -479,29 +496,30 @@ int OSScapture::recordProc() pollitem.fd = mFd; pollitem.events = POLLIN; - int sret{poll(&pollitem, 1, 1000)}; - if(sret < 0) + if(int pret{poll(&pollitem, 1, 1000)}; pret < 0) { if(errno == EINTR || errno == EAGAIN) continue; - ERR("poll failed: %s\n", strerror(errno)); - mDevice->handleDisconnect("Failed to check capture samples: %s", strerror(errno)); + const auto errstr = std::generic_category().message(errno); + ERR("poll failed: {}", errstr); + mDevice->handleDisconnect("Failed to check capture samples: {}", errstr); break; } - else if(sret == 0) + else if(pret == 0) /* NOLINT(*-else-after-return) 'pret' is local to the if/else blocks */ { - WARN("poll timeout\n"); + WARN("poll timeout"); continue; } auto vec = mRing->getWriteVector(); - if(vec.first.len > 0) + if(vec[0].len > 0) { - ssize_t amt{read(mFd, vec.first.buf, vec.first.len*frame_size)}; + ssize_t amt{read(mFd, vec[0].buf, vec[0].len*frame_size)}; if(amt < 0) { - ERR("read failed: %s\n", strerror(errno)); - mDevice->handleDisconnect("Failed reading capture samples: %s", strerror(errno)); + const auto errstr = std::generic_category().message(errno); + ERR("read failed: {}", errstr); + mDevice->handleDisconnect("Failed reading capture samples: {}", errstr); break; } mRing->writeAdvance(static_cast(amt)/frame_size); @@ -516,7 +534,7 @@ void OSScapture::open(std::string_view name) { const char *devname{DefaultCapture.c_str()}; if(name.empty()) - name = DefaultName; + name = GetDefaultName(); else { if(CaptureDevices.empty()) @@ -528,14 +546,14 @@ void OSScapture::open(std::string_view name) ); if(iter == CaptureDevices.cend()) throw al::backend_exception{al::backend_error::NoDevice, - "Device name \"%.*s\" not found", static_cast(name.length()), name.data()}; + "Device name \"{}\" not found", name}; devname = iter->device_name.c_str(); } - mFd = ::open(devname, O_RDONLY); + mFd = ::open(devname, O_RDONLY); /* NOLINT(cppcoreguidelines-pro-type-vararg) */ if(mFd == -1) - throw al::backend_exception{al::backend_error::NoDevice, "Could not open %s: %s", devname, - strerror(errno)}; + throw al::backend_exception{al::backend_error::NoDevice, "Could not open {}: {}", devname, + std::generic_category().message(errno)}; int ossFormat{}; switch(mDevice->FmtType) @@ -554,55 +572,57 @@ void OSScapture::open(std::string_view name) case DevFmtUInt: case DevFmtFloat: throw al::backend_exception{al::backend_error::DeviceError, - "%s capture samples not supported", DevFmtTypeString(mDevice->FmtType)}; + "{} capture samples not supported", DevFmtTypeString(mDevice->FmtType)}; } uint periods{4}; uint numChannels{mDevice->channelsFromFmt()}; uint frameSize{numChannels * mDevice->bytesFromFmt()}; - uint ossSpeed{mDevice->Frequency}; + uint ossSpeed{mDevice->mSampleRate}; /* according to the OSS spec, 16 bytes are the minimum */ - uint log2FragmentSize{maxu(log2i(mDevice->BufferSize * frameSize / periods), 4)}; + uint log2FragmentSize{std::max(log2i(mDevice->mBufferSize * frameSize / periods), 4u)}; uint numFragmentsLogSize{(periods << 16) | log2FragmentSize}; audio_buf_info info{}; #define CHECKERR(func) if((func) < 0) { \ - throw al::backend_exception{al::backend_error::DeviceError, #func " failed: %s", \ - strerror(errno)}; \ + throw al::backend_exception{al::backend_error::DeviceError, #func " failed: {}", \ + std::generic_category().message(errno)}; \ } + /* NOLINTBEGIN(cppcoreguidelines-pro-type-vararg) */ CHECKERR(ioctl(mFd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize)); CHECKERR(ioctl(mFd, SNDCTL_DSP_SETFMT, &ossFormat)); CHECKERR(ioctl(mFd, SNDCTL_DSP_CHANNELS, &numChannels)); CHECKERR(ioctl(mFd, SNDCTL_DSP_SPEED, &ossSpeed)); CHECKERR(ioctl(mFd, SNDCTL_DSP_GETISPACE, &info)); + /* NOLINTEND(cppcoreguidelines-pro-type-vararg) */ #undef CHECKERR if(mDevice->channelsFromFmt() != numChannels) throw al::backend_exception{al::backend_error::DeviceError, - "Failed to set %s, got %d channels instead", DevFmtChannelsString(mDevice->FmtChans), + "Failed to set {}, got {} channels instead", DevFmtChannelsString(mDevice->FmtChans), numChannels}; if(!((ossFormat == AFMT_S8 && mDevice->FmtType == DevFmtByte) || (ossFormat == AFMT_U8 && mDevice->FmtType == DevFmtUByte) || (ossFormat == AFMT_S16_NE && mDevice->FmtType == DevFmtShort))) throw al::backend_exception{al::backend_error::DeviceError, - "Failed to set %s samples, got OSS format %#x", DevFmtTypeString(mDevice->FmtType), - ossFormat}; + "Failed to set {} samples, got OSS format {:#x}", DevFmtTypeString(mDevice->FmtType), + as_unsigned(ossFormat)}; - mRing = RingBuffer::Create(mDevice->BufferSize, frameSize, false); + mRing = RingBuffer::Create(mDevice->mBufferSize, frameSize, false); - mDevice->DeviceName = name; + mDeviceName = name; } void OSScapture::start() { try { mKillNow.store(false, std::memory_order_release); - mThread = std::thread{std::mem_fn(&OSScapture::recordProc), this}; + mThread = std::thread{&OSScapture::recordProc, this}; } catch(std::exception& e) { throw al::backend_exception{al::backend_error::DeviceError, - "Failed to start recording thread: %s", e.what()}; + "Failed to start recording thread: {}", e.what()}; } } @@ -612,12 +632,12 @@ void OSScapture::stop() return; mThread.join(); - if(ioctl(mFd, SNDCTL_DSP_RESET) != 0) - ERR("Error resetting device: %s\n", strerror(errno)); + if(ioctl(mFd, SNDCTL_DSP_RESET) != 0) /* NOLINT(cppcoreguidelines-pro-type-vararg) */ + ERR("Error resetting device: {}", std::generic_category().message(errno)); } void OSScapture::captureSamples(std::byte *buffer, uint samples) -{ mRing->read(buffer, samples); } +{ std::ignore = mRing->read(buffer, samples); } uint OSScapture::availableSamples() { return static_cast(mRing->readSpace()); } @@ -633,9 +653,9 @@ BackendFactory &OSSBackendFactory::getFactory() bool OSSBackendFactory::init() { - if(auto devopt = ConfigValueStr(nullptr, "oss", "device")) + if(auto devopt = ConfigValueStr({}, "oss", "device")) DefaultPlayback = std::move(*devopt); - if(auto capopt = ConfigValueStr(nullptr, "oss", "capture")) + if(auto capopt = ConfigValueStr({}, "oss", "capture")) DefaultCapture = std::move(*capopt); return true; @@ -644,18 +664,13 @@ bool OSSBackendFactory::init() bool OSSBackendFactory::querySupport(BackendType type) { return (type == BackendType::Playback || type == BackendType::Capture); } -std::string OSSBackendFactory::probe(BackendType type) +auto OSSBackendFactory::enumerate(BackendType type) -> std::vector { - std::string outnames; - + std::vector outnames; auto add_device = [&outnames](const DevMap &entry) -> void { - struct stat buf; - if(stat(entry.device_name.c_str(), &buf) == 0) - { - /* Includes null char. */ - outnames.append(entry.name.c_str(), entry.name.length()+1); - } + if(struct stat buf{}; stat(entry.device_name.c_str(), &buf) == 0) + outnames.emplace_back(entry.name); }; switch(type) @@ -663,12 +678,14 @@ std::string OSSBackendFactory::probe(BackendType type) case BackendType::Playback: PlaybackDevices.clear(); ALCossListPopulate(PlaybackDevices, DSP_CAP_OUTPUT); + outnames.reserve(PlaybackDevices.size()); std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device); break; case BackendType::Capture: CaptureDevices.clear(); ALCossListPopulate(CaptureDevices, DSP_CAP_INPUT); + outnames.reserve(CaptureDevices.size()); std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device); break; } diff --git a/3rdparty/openal/alc/backends/oss.h b/3rdparty/openal/alc/backends/oss.h index 4f2c00b96990..b5faf96a5784 100644 --- a/3rdparty/openal/alc/backends/oss.h +++ b/3rdparty/openal/alc/backends/oss.h @@ -5,15 +5,15 @@ struct OSSBackendFactory final : public BackendFactory { public: - bool init() override; + auto init() -> bool final; - bool querySupport(BackendType type) override; + auto querySupport(BackendType type) -> bool final; - std::string probe(BackendType type) override; + auto enumerate(BackendType type) -> std::vector final; - BackendPtr createBackend(DeviceBase *device, BackendType type) override; + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; - static BackendFactory &getFactory(); + static auto getFactory() -> BackendFactory&; }; #endif /* BACKENDS_OSS_H */ diff --git a/3rdparty/openal/alc/backends/otherio.cpp b/3rdparty/openal/alc/backends/otherio.cpp new file mode 100644 index 000000000000..e7b33a78b55e --- /dev/null +++ b/3rdparty/openal/alc/backends/otherio.cpp @@ -0,0 +1,700 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 2024 by authors. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + +#include "config.h" + +#include "otherio.h" + +#define WIN32_LEAN_AND_MEAN +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#ifndef _WAVEFORMATEXTENSIBLE_ +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "albit.h" +#include "alnumeric.h" +#include "althrd_setname.h" +#include "comptr.h" +#include "core/converter.h" +#include "core/device.h" +#include "core/helpers.h" +#include "core/logging.h" +#include "strutils.h" + + +/* A custom C++ interface that should be capable of interoperating with ASIO + * drivers. + */ +enum class ORIOError : LONG { + Okay = 0, + Success = 0x3f4847a0, + NotPresent = -1000, + HWMalfunction, + InvalidParameter, + InvalidMode, + SPNotAdvancing, + NoClock, + NoMemory, +}; + +/* A 64-bit integer or double, which has the most significant 32-bit word first. */ +struct ORIO64Bit { + uint32_t hi; + uint32_t lo; + + template + auto as() const -> T = delete; +}; + +template<> [[nodiscard]] +auto ORIO64Bit::as() const -> uint64_t { return (uint64_t{hi}<<32) | lo; } +template<> [[nodiscard]] +auto ORIO64Bit::as() const -> int64_t { return static_cast(as()); } +template<> [[nodiscard]] +auto ORIO64Bit::as() const -> double { return al::bit_cast(as()); } + + +enum class ORIOSampleType : LONG { + Int16BE = 0, + Int24BE = 1, + Int32BE = 2, + Float32BE = 3, + Float64BE = 4, + Int32BE16 = 8, + Int32BE18 = 9, + Int32BE20 = 10, + Int32BE24 = 11, + + Int16LE = 16, + Int24LE = 17, + Int32LE = 18, + Float32LE = 19, + Float64LE = 20, + Int32LE16 = 24, + Int32LE18 = 25, + Int32LE20 = 26, + Int32LE24 = 27, + + DSDInt8LSB1 = 32, + DSDInt8MSB1 = 33, + + DSDInt8 = 40, +}; + +struct ORIOClockSource { + LONG mIndex; + LONG mAssocChannel; + LONG mAssocGroup; + LONG mIsCurrent; + std::array mName; +}; + +struct ORIOChannelInfo { + LONG mChannel; + LONG mIsInput; + LONG mIsActive; + LONG mGroup; + ORIOSampleType mSampleType; + std::array mName; +}; + +struct ORIOBufferInfo { + LONG mIsInput; + LONG mChannelNum; + std::array mBuffers; +}; + +struct ORIOTime { + struct TimeInfo { + double mSpeed; + ORIO64Bit mSystemTime; + ORIO64Bit mSamplePosition; + double mSampleRate; + ULONG mFlags; + std::array mReserved; + }; + struct TimeCode { + double mSpeed; + ORIO64Bit mTimeCodeSamples; + ULONG mFlags; + std::array mFuture; + }; + + std::array mReserved; + TimeInfo mTimeInfo; + TimeCode mTimeCode; +}; + +#ifdef _WIN64 +#define ORIO_CALLBACK CALLBACK +#else +#define ORIO_CALLBACK +#endif + +struct ORIOCallbacks { + void (ORIO_CALLBACK*BufferSwitch)(LONG bufferIndex, LONG directProcess) noexcept; + void (ORIO_CALLBACK*SampleRateDidChange)(double srate) noexcept; + auto (ORIO_CALLBACK*Message)(LONG selector, LONG value, void *message, double *opt) noexcept -> LONG; + auto (ORIO_CALLBACK*BufferSwitchTimeInfo)(ORIOTime *timeInfo, LONG bufferIndex, LONG directProcess) noexcept -> ORIOTime*; +}; + +/* COM interfaces don't include a virtual destructor in their pure-virtual + * classes, and we can't add one without breaking ABI. + */ +#ifdef __GNUC__ +_Pragma("GCC diagnostic push") +_Pragma("GCC diagnostic ignored \"-Wnon-virtual-dtor\"") +#endif +/* NOLINTNEXTLINE(cppcoreguidelines-virtual-class-destructor) */ +struct ORIOiface : public IUnknown { + STDMETHOD_(LONG, Init)(void *sysHandle) = 0; + /* A fixed-length span should be passed exactly the same as one pointer. + * This ensures an appropriately-sized buffer for the driver. + */ + STDMETHOD_(void, GetDriverName)(al::span name) = 0; + STDMETHOD_(LONG, GetDriverVersion)() = 0; + STDMETHOD_(void, GetErrorMessage)(al::span message) = 0; + STDMETHOD_(ORIOError, Start)() = 0; + STDMETHOD_(ORIOError, Stop)() = 0; + STDMETHOD_(ORIOError, GetChannels)(LONG *numInput, LONG *numOutput) = 0; + STDMETHOD_(ORIOError, GetLatencies)(LONG *inputLatency, LONG *outputLatency) = 0; + STDMETHOD_(ORIOError, GetBufferSize)(LONG *minSize, LONG *maxSize, LONG *preferredSize, LONG *granularity) = 0; + STDMETHOD_(ORIOError, CanSampleRate)(double srate) = 0; + STDMETHOD_(ORIOError, GetSampleRate)(double *srate) = 0; + STDMETHOD_(ORIOError, SetSampleRate)(double srate) = 0; + STDMETHOD_(ORIOError, GetClockSources)(ORIOClockSource *clocks, LONG *numSources) = 0; + STDMETHOD_(ORIOError, SetClockSource)(LONG index) = 0; + STDMETHOD_(ORIOError, GetSamplePosition)(ORIO64Bit *splPos, ORIO64Bit *tstampNS) = 0; + STDMETHOD_(ORIOError, GetChannelInfo)(ORIOChannelInfo *info) = 0; + STDMETHOD_(ORIOError, CreateBuffers)(ORIOBufferInfo *infos, LONG numInfos, LONG bufferSize, ORIOCallbacks *callbacks) = 0; + STDMETHOD_(ORIOError, DisposeBuffers)() = 0; + STDMETHOD_(ORIOError, ControlPanel)() = 0; + STDMETHOD_(ORIOError, Future)(LONG selector, void *opt) = 0; + STDMETHOD_(ORIOError, OutputReady)() = 0; + + ORIOiface() = default; + ORIOiface(const ORIOiface&) = delete; + auto operator=(const ORIOiface&) -> ORIOiface& = delete; + ~ORIOiface() = delete; +}; +#ifdef __GNUC__ +_Pragma("GCC diagnostic pop") +#endif + +namespace { + +using namespace std::string_view_literals; +using std::chrono::nanoseconds; +using std::chrono::milliseconds; +using std::chrono::seconds; + + +struct DeviceEntry { + std::string mDrvName; + CLSID mDrvGuid{}; +}; + +std::vector gDeviceList; + + +struct KeyCloser { + void operator()(HKEY key) { RegCloseKey(key); } +}; +using KeyPtr = std::unique_ptr,KeyCloser>; + +[[nodiscard]] +auto PopulateDeviceList() -> HRESULT +{ + auto regbase = KeyPtr{}; + auto res = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\ASIO", 0, KEY_READ, + al::out_ptr(regbase)); + if(res != ERROR_SUCCESS) + { + ERR("Error opening HKLM\\Software\\ASIO: {}", res); + return E_NOINTERFACE; + } + + auto numkeys = DWORD{}; + auto maxkeylen = DWORD{}; + res = RegQueryInfoKeyW(regbase.get(), nullptr, nullptr, nullptr, &numkeys, &maxkeylen, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr); + if(res != ERROR_SUCCESS) + { + ERR("Error querying HKLM\\Software\\ASIO info: {}", res); + return E_FAIL; + } + + /* maxkeylen is the max number of unicode characters a subkey is. A unicode + * character can occupy two WCHARs, so ensure there's enough space for them + * and the null char. + */ + auto keyname = std::vector(maxkeylen*2 + 1); + for(DWORD i{0};i < numkeys;++i) + { + auto namelen = static_cast(keyname.size()); + res = RegEnumKeyExW(regbase.get(), i, keyname.data(), &namelen, nullptr, nullptr, nullptr, + nullptr); + if(res != ERROR_SUCCESS) + { + ERR("Error querying HKLM\\Software\\ASIO subkey {}: {}", i, res); + continue; + } + if(namelen == 0) + { + ERR("HKLM\\Software\\ASIO subkey {} is blank?", i); + continue; + } + auto subkeyname = wstr_to_utf8({keyname.data(), namelen}); + + auto subkey = KeyPtr{}; + res = RegOpenKeyExW(regbase.get(), keyname.data(), 0, KEY_READ, al::out_ptr(subkey)); + if(res != ERROR_SUCCESS) + { + ERR("Error opening HKLM\\Software\\ASIO\\{}: {}", subkeyname, res); + continue; + } + + auto idstr = std::array{}; + auto readsize = DWORD{idstr.size()*sizeof(WCHAR)}; + res = RegGetValueW(subkey.get(), L"", L"CLSID", RRF_RT_REG_SZ, nullptr, idstr.data(), + &readsize); + if(res != ERROR_SUCCESS) + { + ERR("Failed to read HKLM\\Software\\ASIO\\{}\\CLSID: {}", subkeyname, res); + continue; + } + idstr.back() = 0; + + auto guid = CLSID{}; + if(auto hr = CLSIDFromString(idstr.data(), &guid); FAILED(hr)) + { + ERR("Failed to parse CLSID \"{}\": {:#x}", wstr_to_utf8(idstr.data()), + as_unsigned(hr)); + continue; + } + + /* The CLSID is also used for the IID. */ + auto iface = ComPtr{}; + auto hr = CoCreateInstance(guid, nullptr, CLSCTX_INPROC_SERVER, guid, al::out_ptr(iface)); + if(SUCCEEDED(hr)) + { +#if !ALSOFT_UWP + if(!iface->Init(GetForegroundWindow())) +#else + if(!iface->Init(nullptr)) +#endif + { + ERR("Failed to initialize {}", subkeyname); + continue; + } + auto drvname = std::array{}; + iface->GetDriverName(drvname); + auto drvver = iface->GetDriverVersion(); + + auto &entry = gDeviceList.emplace_back(); + entry.mDrvName = drvname.data(); + entry.mDrvGuid = guid; + + TRACE("Got {} v{}, CLSID {{{:08X}-{:04X}-{:04X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}}}", + entry.mDrvName, drvver, guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], + guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], + guid.Data4[6], guid.Data4[7]); + } + else + ERR("Failed to create {} instance for CLSID {{{:08X}-{:04X}-{:04X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}}}: {:#x}", + subkeyname.c_str(), guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], + guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], + guid.Data4[6], guid.Data4[7], as_unsigned(hr)); + } + + return S_OK; +} + + +enum class MsgType { + OpenDevice, + ResetDevice, + StartDevice, + StopDevice, + CloseDevice, + + QuitThread +}; + +constexpr const char *GetMessageTypeName(MsgType type) noexcept +{ + switch(type) + { + case MsgType::OpenDevice: return "Open Device"; + case MsgType::ResetDevice: return "Reset Device"; + case MsgType::StartDevice: return "Start Device"; + case MsgType::StopDevice: return "Stop Device"; + case MsgType::CloseDevice: return "Close Device"; + case MsgType::QuitThread: break; + } + return ""; +} + + +/* Proxy interface used by the message handler, to ensure COM objects are used + * on a thread where COM is initialized. + */ +struct OtherIOProxy { + OtherIOProxy() = default; + OtherIOProxy(const OtherIOProxy&) = delete; + OtherIOProxy(OtherIOProxy&&) = delete; + virtual ~OtherIOProxy() = default; + + void operator=(const OtherIOProxy&) = delete; + void operator=(OtherIOProxy&&) = delete; + + virtual HRESULT openProxy(std::string_view name) = 0; + virtual void closeProxy() = 0; + + virtual HRESULT resetProxy() = 0; + virtual HRESULT startProxy() = 0; + virtual void stopProxy() = 0; + + struct Msg { + MsgType mType; + OtherIOProxy *mProxy; + std::string_view mParam; + std::promise mPromise; + + explicit operator bool() const noexcept { return mType != MsgType::QuitThread; } + }; + static inline std::deque mMsgQueue; + static inline std::mutex mMsgQueueLock; + static inline std::condition_variable mMsgQueueCond; + + auto pushMessage(MsgType type, std::string_view param={}) -> std::future + { + auto promise = std::promise{}; + auto future = std::future{promise.get_future()}; + { + auto msglock = std::lock_guard{mMsgQueueLock}; + mMsgQueue.emplace_back(Msg{type, this, param, std::move(promise)}); + } + mMsgQueueCond.notify_one(); + return future; + } + + static auto popMessage() -> Msg + { + auto lock = std::unique_lock{mMsgQueueLock}; + mMsgQueueCond.wait(lock, []{return !mMsgQueue.empty();}); + auto msg = Msg{std::move(mMsgQueue.front())}; + mMsgQueue.pop_front(); + return msg; + } + + static void messageHandler(std::promise *promise); +}; + +void OtherIOProxy::messageHandler(std::promise *promise) +{ + TRACE("Starting COM message thread"); + + auto com = ComWrapper{COINIT_APARTMENTTHREADED}; + if(!com) + { + WARN("Failed to initialize COM: {:#x}", as_unsigned(com.status())); + promise->set_value(com.status()); + return; + } + + auto hr = PopulateDeviceList(); + if(FAILED(hr)) + { + promise->set_value(hr); + return; + } + + promise->set_value(S_OK); + promise = nullptr; + + TRACE("Starting message loop"); + while(Msg msg{popMessage()}) + { + TRACE("Got message \"{}\" ({:#04x}, this={}, param=\"{}\")", + GetMessageTypeName(msg.mType), static_cast(msg.mType), + static_cast(msg.mProxy), msg.mParam); + + switch(msg.mType) + { + case MsgType::OpenDevice: + hr = msg.mProxy->openProxy(msg.mParam); + msg.mPromise.set_value(hr); + continue; + + case MsgType::ResetDevice: + hr = msg.mProxy->resetProxy(); + msg.mPromise.set_value(hr); + continue; + + case MsgType::StartDevice: + hr = msg.mProxy->startProxy(); + msg.mPromise.set_value(hr); + continue; + + case MsgType::StopDevice: + msg.mProxy->stopProxy(); + msg.mPromise.set_value(S_OK); + continue; + + case MsgType::CloseDevice: + msg.mProxy->closeProxy(); + msg.mPromise.set_value(S_OK); + continue; + + case MsgType::QuitThread: + break; + } + ERR("Unexpected message: {}", int{al::to_underlying(msg.mType)}); + msg.mPromise.set_value(E_FAIL); + } + TRACE("Message loop finished"); +} + + +struct OtherIOPlayback final : public BackendBase, OtherIOProxy { + explicit OtherIOPlayback(DeviceBase *device) noexcept : BackendBase{device} { } + ~OtherIOPlayback() final; + + void mixerProc(); + + void open(std::string_view name) final; + auto openProxy(std::string_view name) -> HRESULT final; + void closeProxy() final; + auto reset() -> bool final; + auto resetProxy() -> HRESULT final; + void start() final; + auto startProxy() -> HRESULT final; + void stop() final; + void stopProxy() final; + + HRESULT mOpenStatus{E_FAIL}; + + std::atomic mKillNow{true}; + std::thread mThread; +}; + +OtherIOPlayback::~OtherIOPlayback() +{ + if(SUCCEEDED(mOpenStatus)) + pushMessage(MsgType::CloseDevice).wait(); +} + +void OtherIOPlayback::mixerProc() +{ + const auto restTime = milliseconds{mDevice->mUpdateSize*1000/mDevice->mSampleRate / 2}; + + SetRTPriority(); + althrd_setname(GetMixerThreadName()); + + auto done = int64_t{0}; + auto start = std::chrono::steady_clock::now(); + while(!mKillNow.load(std::memory_order_acquire) + && mDevice->Connected.load(std::memory_order_acquire)) + { + auto now = std::chrono::steady_clock::now(); + + /* This converts from nanoseconds to nanosamples, then to samples. */ + const auto avail = int64_t{std::chrono::duration_cast((now-start) + * mDevice->mSampleRate).count()}; + if(avail-done < mDevice->mUpdateSize) + { + std::this_thread::sleep_for(restTime); + continue; + } + while(avail-done >= mDevice->mUpdateSize) + { + mDevice->renderSamples(nullptr, mDevice->mUpdateSize, 0u); + done += mDevice->mUpdateSize; + } + + if(done >= mDevice->mSampleRate) + { + auto s = seconds{done/mDevice->mSampleRate}; + start += s; + done -= mDevice->mSampleRate*s.count(); + } + } +} + + +void OtherIOPlayback::open(std::string_view name) +{ + if(name.empty() && !gDeviceList.empty()) + name = gDeviceList[0].mDrvName; + else + { + auto iter = std::find_if(gDeviceList.cbegin(), gDeviceList.cend(), + [name](const DeviceEntry &entry) { return entry.mDrvName == name; }); + if(iter == gDeviceList.cend()) + throw al::backend_exception{al::backend_error::NoDevice, + "Device name \"{}\" not found", name}; + } + + mOpenStatus = pushMessage(MsgType::OpenDevice, name).get(); + if(FAILED(mOpenStatus)) + throw al::backend_exception{al::backend_error::DeviceError, "Failed to open \"{}\"", name}; + + mDeviceName = name; +} + +auto OtherIOPlayback::openProxy(std::string_view name [[maybe_unused]]) -> HRESULT +{ + return S_OK; +} + +void OtherIOPlayback::closeProxy() +{ +} + +auto OtherIOPlayback::reset() -> bool +{ + return SUCCEEDED(pushMessage(MsgType::ResetDevice).get()); +} + +auto OtherIOPlayback::resetProxy() -> HRESULT +{ + setDefaultWFXChannelOrder(); + return S_OK; +} + +void OtherIOPlayback::start() +{ + auto hr = pushMessage(MsgType::StartDevice).get(); + if(FAILED(hr)) + throw al::backend_exception{al::backend_error::DeviceError, + "Failed to start playback: {:#x}", as_unsigned(hr)}; +} + +auto OtherIOPlayback::startProxy() -> HRESULT +{ + try { + mKillNow.store(false, std::memory_order_release); + mThread = std::thread{&OtherIOPlayback::mixerProc, this}; + return S_OK; + } + catch(std::exception& e) { + ERR("Failed to start mixing thread: {}", e.what()); + } + return E_FAIL; +} + +void OtherIOPlayback::stop() +{ + pushMessage(MsgType::StopDevice).wait(); +} + +void OtherIOPlayback::stopProxy() +{ + if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable()) + return; + mThread.join(); +} + +} // namespace + + +auto OtherIOBackendFactory::init() -> bool +{ + static HRESULT InitResult{E_FAIL}; + if(FAILED(InitResult)) try + { + auto promise = std::promise{}; + auto future = promise.get_future(); + + std::thread{&OtherIOProxy::messageHandler, &promise}.detach(); + InitResult = future.get(); + } + catch(...) { + } + + return SUCCEEDED(InitResult); +} + +auto OtherIOBackendFactory::querySupport(BackendType type) -> bool +{ return type == BackendType::Playback; } + +auto OtherIOBackendFactory::enumerate(BackendType type) -> std::vector +{ + std::vector outnames; + + switch(type) + { + case BackendType::Playback: + std::for_each(gDeviceList.cbegin(), gDeviceList.cend(), + [&outnames](const DeviceEntry &entry) { outnames.emplace_back(entry.mDrvName); }); + break; + + case BackendType::Capture: + break; + } + + return outnames; +} + +auto OtherIOBackendFactory::createBackend(DeviceBase *device, BackendType type) -> BackendPtr +{ + if(type == BackendType::Playback) + return BackendPtr{new OtherIOPlayback{device}}; + return nullptr; +} + +auto OtherIOBackendFactory::getFactory() -> BackendFactory& +{ + static auto factory = OtherIOBackendFactory{}; + return factory; +} + +auto OtherIOBackendFactory::queryEventSupport(alc::EventType, BackendType) -> alc::EventSupport +{ + return alc::EventSupport::NoSupport; +} diff --git a/3rdparty/openal/alc/backends/otherio.h b/3rdparty/openal/alc/backends/otherio.h new file mode 100644 index 000000000000..64cb436ea7ec --- /dev/null +++ b/3rdparty/openal/alc/backends/otherio.h @@ -0,0 +1,21 @@ +#ifndef BACKENDS_OTHERIO_H +#define BACKENDS_OTHERIO_H + +#include "base.h" + +struct OtherIOBackendFactory final : public BackendFactory { +public: + auto init() -> bool final; + + auto querySupport(BackendType type) -> bool final; + + auto queryEventSupport(alc::EventType eventType, BackendType type) -> alc::EventSupport final; + + auto enumerate(BackendType type) -> std::vector final; + + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; + + static auto getFactory() -> BackendFactory&; +}; + +#endif /* BACKENDS_OTHERIO_H */ diff --git a/3rdparty/openal/alc/backends/pipewire.cpp b/3rdparty/openal/alc/backends/pipewire.cpp index 1c4e2cc4933b..98768542d14a 100644 --- a/3rdparty/openal/alc/backends/pipewire.cpp +++ b/3rdparty/openal/alc/backends/pipewire.cpp @@ -23,26 +23,30 @@ #include "pipewire.h" #include +#include #include +#include +#include +#include #include +#include +#include #include #include #include #include -#include +#include #include #include #include -#include +#include #include -#include +#include #include -#include "albit.h" #include "alc/alconfig.h" -#include "alc/events.h" +#include "alc/backends/base.h" #include "almalloc.h" -#include "alnumeric.h" #include "alspan.h" #include "alstring.h" #include "core/devformat.h" @@ -73,10 +77,15 @@ _Pragma("GCC diagnostic ignored \"-Weverything\"") #include "spa/buffer/buffer.h" #include "spa/param/audio/format-utils.h" #include "spa/param/audio/raw.h" +#include "spa/param/format.h" #include "spa/param/param.h" #include "spa/pod/builder.h" #include "spa/utils/json.h" +/* NOLINTBEGIN : All kinds of unsafe C stuff here from PipeWire headers + * (function-like macros, C style casts in macros, etc), which we can't do + * anything about except wrap into inline functions. + */ namespace { /* Wrap some nasty macros here too... */ template @@ -109,32 +118,72 @@ template constexpr auto get_pod_body(const spa_pod *pod) noexcept { return al::span{static_cast(SPA_POD_BODY(pod)), N}; } -constexpr auto make_pod_builder(void *data, uint32_t size) noexcept -{ return SPA_POD_BUILDER_INIT(data, size); } - constexpr auto get_array_value_type(const spa_pod *pod) noexcept { return SPA_POD_ARRAY_VALUE_TYPE(pod); } +constexpr auto make_pod_builder(void *data, uint32_t size) noexcept +{ return SPA_POD_BUILDER_INIT(data, size); } + constexpr auto PwIdAny = PW_ID_ANY; } // namespace +/* NOLINTEND */ _Pragma("GCC diagnostic pop") namespace { +template [[nodiscard]] constexpr +auto as_const_ptr(T *ptr) noexcept -> std::add_const_t* { return ptr; } + +struct PodDynamicBuilder { +private: + std::vector mStorage; + spa_pod_builder mPod{}; + + int overflow(uint32_t size) noexcept + { + try { + mStorage.resize(size); + } + catch(...) { + ERR("Failed to resize POD storage"); + return -ENOMEM; + } + mPod.data = mStorage.data(); + mPod.size = size; + return 0; + } + +public: + explicit PodDynamicBuilder(uint32_t initSize=1024) : mStorage(initSize) + , mPod{make_pod_builder(mStorage.data(), initSize)} + { + static constexpr auto callbacks{[] + { + spa_pod_builder_callbacks cb{}; + cb.version = SPA_VERSION_POD_BUILDER_CALLBACKS; + cb.overflow = [](void *data, uint32_t size) noexcept + { return static_cast(data)->overflow(size); }; + return cb; + }()}; + + spa_pod_builder_set_callbacks(&mPod, &callbacks, this); + } + + spa_pod_builder *get() noexcept { return &mPod; } +}; + /* Added in 0.3.33, but we currently only require 0.3.23. */ #ifndef PW_KEY_NODE_RATE #define PW_KEY_NODE_RATE "node.rate" #endif +using namespace std::string_view_literals; using std::chrono::seconds; using std::chrono::milliseconds; using std::chrono::nanoseconds; using uint = unsigned int; -constexpr char pwireDevice[] = "PipeWire Output"; -constexpr char pwireInput[] = "PipeWire Input"; - bool check_version(const char *version) { @@ -143,14 +192,13 @@ bool check_version(const char *version) * future. */ int major{0}, minor{0}, revision{0}; + /* NOLINTNEXTLINE(cert-err34-c,cppcoreguidelines-pro-type-vararg) */ int ret{sscanf(version, "%d.%d.%d", &major, &minor, &revision)}; - if(ret == 3 && (major > PW_MAJOR || (major == PW_MAJOR && minor > PW_MINOR) - || (major == PW_MAJOR && minor == PW_MINOR && revision >= PW_MICRO))) - return true; - return false; + return ret == 3 && (major > PW_MAJOR || (major == PW_MAJOR && minor > PW_MINOR) + || (major == PW_MAJOR && minor == PW_MINOR && revision >= PW_MICRO)); } -#ifdef HAVE_DYNLOAD +#if HAVE_DYNLOAD #define PWIRE_FUNCS(MAGIC) \ MAGIC(pw_context_connect) \ MAGIC(pw_context_destroy) \ @@ -201,18 +249,18 @@ bool pwire_load() if(pwire_handle) return true; - static constexpr char pwire_library[] = "libpipewire-0.3.so.0"; + const char *pwire_library{"libpipewire-0.3.so.0"}; std::string missing_funcs; pwire_handle = LoadLib(pwire_library); if(!pwire_handle) { - WARN("Failed to load %s\n", pwire_library); + WARN("Failed to load {}", pwire_library); return false; } #define LOAD_FUNC(f) do { \ - p##f = al::bit_cast(GetSymbol(pwire_handle, #f)); \ + p##f = reinterpret_cast(GetSymbol(pwire_handle, #f)); \ if(p##f == nullptr) missing_funcs += "\n" #f; \ } while(0); PWIRE_FUNCS(LOAD_FUNC) @@ -221,7 +269,7 @@ bool pwire_load() if(!missing_funcs.empty()) { - WARN("Missing expected functions:%s\n", missing_funcs.c_str()); + WARN("Missing expected functions:{}", missing_funcs); CloseLib(pwire_handle); pwire_handle = nullptr; return false; @@ -296,7 +344,7 @@ using Pod_t = typename PodInfo::Type; template al::span> get_array_span(const spa_pod *pod) { - uint32_t nvals; + uint32_t nvals{}; if(void *v{spa_pod_get_array(pod, &nvals)}) { if(get_array_value_type(pod) == T) @@ -330,11 +378,11 @@ To as(From) noexcept = delete; * - pw_metadata */ template<> -pw_proxy* as(pw_registry *reg) noexcept { return al::bit_cast(reg); } +pw_proxy* as(pw_registry *reg) noexcept { return reinterpret_cast(reg); } template<> -pw_proxy* as(pw_node *node) noexcept { return al::bit_cast(node); } +pw_proxy* as(pw_node *node) noexcept { return reinterpret_cast(node); } template<> -pw_proxy* as(pw_metadata *mdata) noexcept { return al::bit_cast(mdata); } +pw_proxy* as(pw_metadata *mdata) noexcept { return reinterpret_cast(mdata); } struct PwContextDeleter { @@ -367,9 +415,10 @@ struct PwStreamDeleter { }; using PwStreamPtr = std::unique_ptr; -/* Enums for bitflags... again... *sigh* */ +/* NOLINTBEGIN(*EnumCastOutOfRange) Enums for bitflags... again... *sigh* */ constexpr pw_stream_flags operator|(pw_stream_flags lhs, pw_stream_flags rhs) noexcept { return static_cast(lhs | al::to_underlying(rhs)); } +/* NOLINTEND(*EnumCastOutOfRange) */ constexpr pw_stream_flags& operator|=(pw_stream_flags &lhs, pw_stream_flags rhs) noexcept { lhs = lhs | rhs; return lhs; } @@ -397,9 +446,11 @@ class ThreadMainloop { explicit operator bool() const noexcept { return mLoop != nullptr; } + [[nodiscard]] auto start() const { return pw_thread_loop_start(mLoop); } auto stop() const { return pw_thread_loop_stop(mLoop); } + [[nodiscard]] auto getLoop() const { return pw_thread_loop_get_loop(mLoop); } auto lock() const { return pw_thread_loop_lock(mLoop); } @@ -407,7 +458,7 @@ class ThreadMainloop { auto signal(bool wait) const { return pw_thread_loop_signal(mLoop, wait); } - auto newContext(pw_properties *props=nullptr, size_t user_data_size=0) + auto newContext(pw_properties *props=nullptr, size_t user_data_size=0) const { return PwContextPtr{pw_context_new(getLoop(), props, user_data_size)}; } static auto Create(const char *name, spa_dict *props=nullptr) @@ -443,8 +494,7 @@ struct NodeProxy { { pw_node_events ret{}; ret.version = PW_VERSION_NODE_EVENTS; - ret.info = [](void *object, const pw_node_info *info) noexcept - { static_cast(object)->infoCallback(info); }; + ret.info = infoCallback; ret.param = [](void *object, int seq, uint32_t id, uint32_t index, uint32_t next, const spa_pod *param) noexcept { static_cast(object)->paramCallback(seq, id, index, next, param); }; return ret; @@ -452,7 +502,7 @@ struct NodeProxy { uint32_t mId{}; - PwNodePtr mNode{}; + PwNodePtr mNode; spa_hook mListener{}; NodeProxy(uint32_t id, PwNodePtr node) @@ -464,16 +514,15 @@ struct NodeProxy { /* Track changes to the enumerable and current formats (indicates the * default and active format, which is what we're interested in). */ - uint32_t fmtids[]{SPA_PARAM_EnumFormat, SPA_PARAM_Format}; - ppw_node_subscribe_params(mNode.get(), std::data(fmtids), std::size(fmtids)); + std::array fmtids{{SPA_PARAM_EnumFormat, SPA_PARAM_Format}}; + ppw_node_subscribe_params(mNode.get(), fmtids.data(), fmtids.size()); } ~NodeProxy() { spa_hook_remove(&mListener); } - void infoCallback(const pw_node_info *info) noexcept; - - void paramCallback(int seq, uint32_t id, uint32_t index, uint32_t next, const spa_pod *param) noexcept; + static void infoCallback(void *object, const pw_node_info *info) noexcept; + void paramCallback(int seq, uint32_t id, uint32_t index, uint32_t next, const spa_pod *param) const noexcept; }; /* A metadata proxy object used to query the default sink and source. */ @@ -482,14 +531,13 @@ struct MetadataProxy { { pw_metadata_events ret{}; ret.version = PW_VERSION_METADATA_EVENTS; - ret.property = [](void *object, uint32_t id, const char *key, const char *type, const char *value) noexcept - { return static_cast(object)->propertyCallback(id, key, type, value); }; + ret.property = propertyCallback; return ret; } uint32_t mId{}; - PwMetadataPtr mMetadata{}; + PwMetadataPtr mMetadata; spa_hook mListener{}; MetadataProxy(uint32_t id, PwMetadataPtr mdata) @@ -501,7 +549,8 @@ struct MetadataProxy { ~MetadataProxy() { spa_hook_remove(&mListener); } - int propertyCallback(uint32_t id, const char *key, const char *type, const char *value) noexcept; + static auto propertyCallback(void *object, uint32_t id, const char *key, const char *type, + const char *value) noexcept -> int; }; @@ -509,10 +558,10 @@ struct MetadataProxy { * to objects being added to or removed from the registry. */ struct EventManager { - ThreadMainloop mLoop{}; - PwContextPtr mContext{}; - PwCorePtr mCore{}; - PwRegistryPtr mRegistry{}; + ThreadMainloop mLoop; + PwContextPtr mContext; + PwCorePtr mCore; + PwRegistryPtr mRegistry; spa_hook mRegistryListener{}; spa_hook mCoreListener{}; @@ -541,7 +590,8 @@ struct EventManager { auto lock() const { return mLoop.lock(); } auto unlock() const { return mLoop.unlock(); } - inline bool initIsDone(std::memory_order m=std::memory_order_seq_cst) noexcept + [[nodiscard]] + auto initIsDone(std::memory_order m=std::memory_order_seq_cst) const noexcept -> bool { return mInitDone.load(m); } /** @@ -647,7 +697,7 @@ struct DeviceNode { void parsePositions(const spa_pod *value, bool force_update) noexcept; void parseChannelCount(const spa_pod *value, bool force_update) noexcept; - void callEvent(alc::EventType type, std::string_view message) + void callEvent(alc::EventType type, std::string_view message) const { /* Source nodes aren't recognized for playback, only Sink and Duplex * nodes are. All node types are recognized for capture. @@ -714,7 +764,7 @@ void DeviceNode::Remove(uint32_t id) { if(n.mId != id) return false; - TRACE("Removing device \"%s\"\n", n.mDevName.c_str()); + TRACE("Removing device \"{}\"", n.mDevName); if(gEventHandler.initIsDone(std::memory_order_relaxed)) { const std::string msg{"Device removed: "+n.mName}; @@ -728,25 +778,32 @@ void DeviceNode::Remove(uint32_t id) } -const spa_audio_channel MonoMap[]{ +constexpr std::array MonoMap{ SPA_AUDIO_CHANNEL_MONO -}, StereoMap[] { +}; +constexpr std::array StereoMap{ SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR -}, QuadMap[]{ +}; +constexpr std::array QuadMap{ SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR -}, X51Map[]{ +}; +constexpr std::array X51Map{ SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR -}, X51RearMap[]{ +}; +constexpr std::array X51RearMap{ SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR -}, X61Map[]{ +}; +constexpr std::array X61Map{ SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, SPA_AUDIO_CHANNEL_RC, SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR -}, X71Map[]{ +}; +constexpr std::array X71Map{ SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR, SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR -}, X714Map[]{ +}; +constexpr std::array X714Map{ SPA_AUDIO_CHANNEL_FL, SPA_AUDIO_CHANNEL_FR, SPA_AUDIO_CHANNEL_FC, SPA_AUDIO_CHANNEL_LFE, SPA_AUDIO_CHANNEL_RL, SPA_AUDIO_CHANNEL_RR, SPA_AUDIO_CHANNEL_SL, SPA_AUDIO_CHANNEL_SR, SPA_AUDIO_CHANNEL_TFL, SPA_AUDIO_CHANNEL_TFR, SPA_AUDIO_CHANNEL_TRL, SPA_AUDIO_CHANNEL_TRR @@ -756,17 +813,15 @@ const spa_audio_channel MonoMap[]{ * Checks if every channel in 'map1' exists in 'map0' (that is, map0 is equal * to or a superset of map1). */ -template -bool MatchChannelMap(const al::span map0, const spa_audio_channel (&map1)[N]) +bool MatchChannelMap(const al::span map0, + const al::span map1) { - if(map0.size() < N) + if(map0.size() < map1.size()) return false; - for(const spa_audio_channel chid : map1) - { - if(std::find(map0.begin(), map0.end(), chid) == map0.end()) - return false; - } - return true; + + auto find_channel = [map0](const spa_audio_channel chid) -> bool + { return std::find(map0.begin(), map0.end(), chid) != map0.end(); }; + return std::all_of(map1.cbegin(), map1.cend(), find_channel); } void DeviceNode::parseSampleRate(const spa_pod *value, bool force_update) noexcept @@ -778,7 +833,7 @@ void DeviceNode::parseSampleRate(const spa_pod *value, bool force_update) noexce const uint podType{get_pod_type(value)}; if(podType != SPA_TYPE_Int) { - WARN(" Unhandled sample rate POD type: %u\n", podType); + WARN(" Unhandled sample rate POD type: {}", podType); return; } @@ -786,15 +841,16 @@ void DeviceNode::parseSampleRate(const spa_pod *value, bool force_update) noexce { if(nvals != 3) { - WARN(" Unexpected SPA_CHOICE_Range count: %u\n", nvals); + WARN(" Unexpected SPA_CHOICE_Range count: {}", nvals); return; } auto srates = get_pod_body(value); /* [0] is the default, [1] is the min, and [2] is the max. */ - TRACE(" sample rate: %d (range: %d -> %d)\n", srates[0], srates[1], srates[2]); + TRACE(" sample rate: {} (range: {} -> {})", srates[0], srates[1], srates[2]); if(!mSampleRate || force_update) - mSampleRate = static_cast(clampi(srates[0], MIN_OUTPUT_RATE, MAX_OUTPUT_RATE)); + mSampleRate = static_cast(std::clamp(srates[0], MinOutputRate, + MaxOutputRate)); return; } @@ -802,7 +858,7 @@ void DeviceNode::parseSampleRate(const spa_pod *value, bool force_update) noexce { if(nvals == 0) { - WARN(" Unexpected SPA_CHOICE_Enum count: %u\n", nvals); + WARN(" Unexpected SPA_CHOICE_Enum count: {}", nvals); return; } auto srates = get_pod_body(value, nvals); @@ -814,13 +870,13 @@ void DeviceNode::parseSampleRate(const spa_pod *value, bool force_update) noexce others += ", "; others += std::to_string(srates[i]); } - TRACE(" sample rate: %d (%s)\n", srates[0], others.c_str()); + TRACE(" sample rate: {} ({})", srates[0], others); /* Pick the first rate listed that's within the allowed range (default * rate if possible). */ for(const auto &rate : srates) { - if(rate >= MIN_OUTPUT_RATE && rate <= MAX_OUTPUT_RATE) + if(rate >= int{MinOutputRate} && rate <= int{MaxOutputRate}) { if(!mSampleRate || force_update) mSampleRate = static_cast(rate); @@ -834,18 +890,19 @@ void DeviceNode::parseSampleRate(const spa_pod *value, bool force_update) noexce { if(nvals != 1) { - WARN(" Unexpected SPA_CHOICE_None count: %u\n", nvals); + WARN(" Unexpected SPA_CHOICE_None count: {}", nvals); return; } auto srates = get_pod_body(value); - TRACE(" sample rate: %d\n", srates[0]); + TRACE(" sample rate: {}", srates[0]); if(!mSampleRate || force_update) - mSampleRate = static_cast(clampi(srates[0], MIN_OUTPUT_RATE, MAX_OUTPUT_RATE)); + mSampleRate = static_cast(std::clamp(srates[0], MinOutputRate, + MaxOutputRate)); return; } - WARN(" Unhandled sample rate choice type: %u\n", choiceType); + WARN(" Unhandled sample rate choice type: {}", choiceType); } void DeviceNode::parsePositions(const spa_pod *value, bool force_update) noexcept @@ -855,7 +912,7 @@ void DeviceNode::parsePositions(const spa_pod *value, bool force_update) noexcep if(choiceType != SPA_CHOICE_None || choiceCount != 1) { - ERR(" Unexpected positions choice: type=%u, count=%u\n", choiceType, choiceCount); + ERR(" Unexpected positions choice: type={}, count={}", choiceType, choiceCount); return; } @@ -886,7 +943,7 @@ void DeviceNode::parsePositions(const spa_pod *value, bool force_update) noexcep else mChannels = DevFmtMono; } - TRACE(" %zu position%s for %s%s\n", chanmap.size(), (chanmap.size()==1)?"":"s", + TRACE(" {} position{} for {}{}", chanmap.size(), (chanmap.size()==1)?"":"s", DevFmtChannelsString(mChannels), mIs51Rear?"(rear)":""); } @@ -898,7 +955,7 @@ void DeviceNode::parseChannelCount(const spa_pod *value, bool force_update) noex if(choiceType != SPA_CHOICE_None || choiceCount != 1) { - ERR(" Unexpected positions choice: type=%u, count=%u\n", choiceType, choiceCount); + ERR(" Unexpected positions choice: type={}, count={}", choiceType, choiceCount); return; } @@ -914,20 +971,21 @@ void DeviceNode::parseChannelCount(const spa_pod *value, bool force_update) noex else if(*chancount >= 1) mChannels = DevFmtMono; } - TRACE(" %d channel%s for %s\n", *chancount, (*chancount==1)?"":"s", + TRACE(" {} channel{} for {}", *chancount, (*chancount==1)?"":"s", DevFmtChannelsString(mChannels)); } -constexpr char MonitorPrefix[]{"Monitor of "}; -constexpr auto MonitorPrefixLen = std::size(MonitorPrefix) - 1; -constexpr char AudioSinkClass[]{"Audio/Sink"}; -constexpr char AudioSourceClass[]{"Audio/Source"}; -constexpr char AudioSourceVirtualClass[]{"Audio/Source/Virtual"}; -constexpr char AudioDuplexClass[]{"Audio/Duplex"}; -constexpr char StreamClass[]{"Stream/"}; +[[nodiscard]] constexpr auto GetMonitorPrefix() noexcept { return "Monitor of "sv; } +[[nodiscard]] constexpr auto GetMonitorSuffix() noexcept { return ".monitor"sv; } +[[nodiscard]] constexpr auto GetAudioSinkClassName() noexcept { return "Audio/Sink"sv; } +[[nodiscard]] constexpr auto GetAudioSourceClassName() noexcept { return "Audio/Source"sv; } +[[nodiscard]] constexpr auto GetAudioDuplexClassName() noexcept { return "Audio/Duplex"sv; } +[[nodiscard]] constexpr auto GetAudioSourceVirtualClassName() noexcept +{ return "Audio/Source/Virtual"sv; } -void NodeProxy::infoCallback(const pw_node_info *info) noexcept + +void NodeProxy::infoCallback(void*, const pw_node_info *info) noexcept { /* We only care about property changes here (media class, name/desc). * Format changes will automatically invoke the param callback. @@ -940,18 +998,19 @@ void NodeProxy::infoCallback(const pw_node_info *info) noexcept /* Can this actually change? */ const char *media_class{spa_dict_lookup(info->props, PW_KEY_MEDIA_CLASS)}; if(!media_class) UNLIKELY return; + const std::string_view className{media_class}; NodeType ntype{}; - if(al::strcasecmp(media_class, AudioSinkClass) == 0) + if(al::case_compare(className, GetAudioSinkClassName()) == 0) ntype = NodeType::Sink; - else if(al::strcasecmp(media_class, AudioSourceClass) == 0 - || al::strcasecmp(media_class, AudioSourceVirtualClass) == 0) + else if(al::case_compare(className, GetAudioSourceClassName()) == 0 + || al::case_compare(className, GetAudioSourceVirtualClassName()) == 0) ntype = NodeType::Source; - else if(al::strcasecmp(media_class, AudioDuplexClass) == 0) + else if(al::case_compare(className, GetAudioDuplexClassName()) == 0) ntype = NodeType::Duplex; else { - TRACE("Dropping device node %u which became type \"%s\"\n", info->id, media_class); + TRACE("Dropping device node {} which became type \"{}\"", info->id, media_class); DeviceNode::Remove(info->id); return; } @@ -970,7 +1029,7 @@ void NodeProxy::infoCallback(const pw_node_info *info) noexcept serial_id = std::strtoull(serial_str, &serial_end, 0); if(*serial_end != '\0' || errno == ERANGE) { - ERR("Unexpected object serial: %s\n", serial_str); + ERR("Unexpected object serial: {}", serial_str); serial_id = info->id; } } @@ -981,21 +1040,26 @@ void NodeProxy::infoCallback(const pw_node_info *info) noexcept else name = "PipeWire node #"+std::to_string(info->id); const char *form_factor{spa_dict_lookup(info->props, PW_KEY_DEVICE_FORM_FACTOR)}; - TRACE("Got %s device \"%s\"%s%s%s\n", AsString(ntype), devName ? devName : "(nil)", + TRACE("Got {} device \"{}\"{}{}{}", AsString(ntype), devName ? devName : "(nil)", form_factor?" (":"", form_factor?form_factor:"", form_factor?")":""); - TRACE(" \"%s\" = ID %" PRIu64 "\n", name.c_str(), serial_id); + TRACE(" \"{}\" = ID {}", name, serial_id); DeviceNode &node = DeviceNode::Add(info->id); node.mSerial = serial_id; /* This method is called both to notify about a new sink/source node, * and update properties for the node. It's unclear what properties can * change for an existing node without being removed first, so err on - * the side of caution: send a DeviceAdded event when the name differs, - * and send a DeviceRemoved event if it had a name that's being - * replaced. + * the side of caution: send a DeviceRemoved event if it had a name + * that's being changed, and send a DeviceAdded event when the name + * differs or it didn't have one. + * + * The DeviceRemoved event needs to be called before the potentially + * new NodeType is set, so the removal event is called for the previous + * device type, while the DeviceAdded event needs to be called after. * - * This is overkill if the name or devname can't change. + * This is overkill if the node type, name, and devname can't change. */ + bool notifyAdd{false}; if(node.mName != name) { if(gEventHandler.initIsDone(std::memory_order_relaxed)) @@ -1005,27 +1069,32 @@ void NodeProxy::infoCallback(const pw_node_info *info) noexcept const std::string msg{"Device removed: "+node.mName}; node.callEvent(alc::EventType::DeviceRemoved, msg); } - const std::string msg{"Device added: "+name}; - node.callEvent(alc::EventType::DeviceAdded, msg); + notifyAdd = true; } node.mName = std::move(name); } node.mDevName = devName ? devName : ""; node.mType = ntype; - node.mIsHeadphones = form_factor && (al::strcasecmp(form_factor, "headphones") == 0 - || al::strcasecmp(form_factor, "headset") == 0); + node.mIsHeadphones = form_factor && (al::case_compare(form_factor, "headphones"sv) == 0 + || al::case_compare(form_factor, "headset"sv) == 0); + if(notifyAdd) + { + const std::string msg{"Device added: "+node.mName}; + node.callEvent(alc::EventType::DeviceAdded, msg); + } } } -void NodeProxy::paramCallback(int, uint32_t id, uint32_t, uint32_t, const spa_pod *param) noexcept +void NodeProxy::paramCallback(int, uint32_t id, uint32_t, uint32_t, const spa_pod *param) const noexcept { if(id == SPA_PARAM_EnumFormat || id == SPA_PARAM_Format) { DeviceNode *node{DeviceNode::Find(mId)}; if(!node) UNLIKELY return; - TRACE("Device ID %" PRIu64 " %s format:\n", node->mSerial, - (id == SPA_PARAM_EnumFormat) ? "enumerable" : "current"); + TRACE("Device ID {} {} format{}:", node->mSerial, + (id == SPA_PARAM_EnumFormat) ? "available" : "current", + (id == SPA_PARAM_EnumFormat) ? "s" : ""); const bool force_update{id == SPA_PARAM_Format}; if(const spa_pod_prop *prop{spa_pod_find_prop(param, nullptr, SPA_FORMAT_AUDIO_rate)}) @@ -1033,42 +1102,45 @@ void NodeProxy::paramCallback(int, uint32_t id, uint32_t, uint32_t, const spa_po if(const spa_pod_prop *prop{spa_pod_find_prop(param, nullptr, SPA_FORMAT_AUDIO_position)}) node->parsePositions(&prop->value, force_update); - else if((prop=spa_pod_find_prop(param, nullptr, SPA_FORMAT_AUDIO_channels)) != nullptr) - node->parseChannelCount(&prop->value, force_update); + else + { + prop = spa_pod_find_prop(param, nullptr, SPA_FORMAT_AUDIO_channels); + if(prop) node->parseChannelCount(&prop->value, force_update); + } } } -int MetadataProxy::propertyCallback(uint32_t id, const char *key, const char *type, - const char *value) noexcept +auto MetadataProxy::propertyCallback(void*, uint32_t id, const char *key, const char *type, + const char *value) noexcept -> int { if(id != PW_ID_CORE) return 0; bool isCapture{}; - if(std::strcmp(key, "default.audio.sink") == 0) + if("default.audio.sink"sv == key) isCapture = false; - else if(std::strcmp(key, "default.audio.source") == 0) + else if("default.audio.source"sv == key) isCapture = true; else return 0; if(!type) { - TRACE("Default %s device cleared\n", isCapture ? "capture" : "playback"); + TRACE("Default {} device cleared", isCapture ? "capture" : "playback"); if(!isCapture) DefaultSinkDevice.clear(); else DefaultSourceDevice.clear(); return 0; } - if(std::strcmp(type, "Spa:String:JSON") != 0) + if("Spa:String:JSON"sv != type) { - ERR("Unexpected %s property type: %s\n", key, type); + ERR("Unexpected {} property type: {}", key, type); return 0; } - spa_json it[2]{}; - spa_json_init(&it[0], value, strlen(value)); - if(spa_json_enter_object(&it[0], &it[1]) <= 0) + std::array it{}; + spa_json_init(it.data(), value, strlen(value)); + if(spa_json_enter_object(&std::get<0>(it), &std::get<1>(it)) <= 0) return 0; auto get_json_string = [](spa_json *iter) @@ -1079,57 +1151,51 @@ int MetadataProxy::propertyCallback(uint32_t id, const char *key, const char *ty int len{spa_json_next(iter, &val)}; if(len <= 0) return str; - str.emplace().resize(static_cast(len), '\0'); - if(spa_json_parse_string(val, len, &str->front()) <= 0) + str.emplace(static_cast(len), '\0'); + if(spa_json_parse_string(val, len, str->data()) <= 0) str.reset(); else while(!str->empty() && str->back() == '\0') str->pop_back(); return str; }; - while(auto propKey = get_json_string(&it[1])) + while(auto propKey = get_json_string(&std::get<1>(it))) { - if(*propKey == "name") + if("name"sv == *propKey) { - auto propValue = get_json_string(&it[1]); + auto propValue = get_json_string(&std::get<1>(it)); if(!propValue) break; - TRACE("Got default %s device \"%s\"\n", isCapture ? "capture" : "playback", - propValue->c_str()); - if(!isCapture) + TRACE("Got default {} device \"{}\"", isCapture ? "capture" : "playback", + *propValue); + if(!isCapture && DefaultSinkDevice != *propValue) { - if(DefaultSinkDevice != *propValue) + if(gEventHandler.mInitDone.load(std::memory_order_relaxed)) { - if(gEventHandler.mInitDone.load(std::memory_order_relaxed)) - { - auto entry = DeviceNode::FindByDevName(*propValue); - const std::string msg{"Default playback device changed: "+ - (entry ? entry->mName : std::string{})}; - alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Playback, - msg); - } - DefaultSinkDevice = std::move(*propValue); + auto entry = DeviceNode::FindByDevName(*propValue); + const std::string message{"Default playback device changed: "+ + (entry ? entry->mName : std::string{})}; + alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Playback, + message); } + DefaultSinkDevice = std::move(*propValue); } - else + else if(isCapture && DefaultSourceDevice != *propValue) { - if(DefaultSourceDevice != *propValue) + if(gEventHandler.mInitDone.load(std::memory_order_relaxed)) { - if(gEventHandler.mInitDone.load(std::memory_order_relaxed)) - { - auto entry = DeviceNode::FindByDevName(*propValue); - const std::string msg{"Default capture device changed: "+ - (entry ? entry->mName : std::string{})}; - alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Capture, - msg); - } - DefaultSourceDevice = std::move(*propValue); + auto entry = DeviceNode::FindByDevName(*propValue); + const std::string message{"Default capture device changed: "+ + (entry ? entry->mName : std::string{})}; + alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Capture, + message); } + DefaultSourceDevice = std::move(*propValue); } } else { const char *v{}; - if(spa_json_next(&it[1], &v) <= 0) + if(spa_json_next(&std::get<1>(it), &v) <= 0) break; } } @@ -1142,28 +1208,28 @@ bool EventManager::init() mLoop = ThreadMainloop::Create("PWEventThread"); if(!mLoop) { - ERR("Failed to create PipeWire event thread loop (errno: %d)\n", errno); + ERR("Failed to create PipeWire event thread loop (errno: {})", errno); return false; } mContext = mLoop.newContext(); if(!mContext) { - ERR("Failed to create PipeWire event context (errno: %d)\n", errno); + ERR("Failed to create PipeWire event context (errno: {})", errno); return false; } mCore = PwCorePtr{pw_context_connect(mContext.get(), nullptr, 0)}; if(!mCore) { - ERR("Failed to connect PipeWire event context (errno: %d)\n", errno); + ERR("Failed to connect PipeWire event context (errno: {})", errno); return false; } mRegistry = PwRegistryPtr{pw_core_get_registry(mCore.get(), PW_VERSION_REGISTRY, 0)}; if(!mRegistry) { - ERR("Failed to get PipeWire event registry (errno: %d)\n", errno); + ERR("Failed to get PipeWire event registry (errno: {})", errno); return false; } @@ -1180,7 +1246,7 @@ bool EventManager::init() if(int res{mLoop.start()}) { - ERR("Failed to start PipeWire event thread loop (res: %d)\n", res); + ERR("Failed to start PipeWire event thread loop (res: {})", res); return false; } @@ -1208,17 +1274,17 @@ void EventManager::addCallback(uint32_t id, uint32_t, const char *type, uint32_t { const char *media_class{spa_dict_lookup(props, PW_KEY_MEDIA_CLASS)}; if(!media_class) return; + const std::string_view className{media_class}; /* Specifically, audio sinks and sources (and duplexes). */ - const bool isGood{al::strcasecmp(media_class, AudioSinkClass) == 0 - || al::strcasecmp(media_class, AudioSourceClass) == 0 - || al::strcasecmp(media_class, AudioSourceVirtualClass) == 0 - || al::strcasecmp(media_class, AudioDuplexClass) == 0}; + const bool isGood{al::case_compare(className, GetAudioSinkClassName()) == 0 + || al::case_compare(className, GetAudioSourceClassName()) == 0 + || al::case_compare(className, GetAudioSourceVirtualClassName()) == 0 + || al::case_compare(className, GetAudioDuplexClassName()) == 0}; if(!isGood) { - if(std::strstr(media_class, "/Video") == nullptr - && std::strncmp(media_class, StreamClass, sizeof(StreamClass)-1) != 0) - TRACE("Ignoring node class %s\n", media_class); + if(!al::contains(className, "/Video"sv) && !al::starts_with(className, "Stream/"sv)) + TRACE("Ignoring node class {}", media_class); return; } @@ -1227,7 +1293,7 @@ void EventManager::addCallback(uint32_t id, uint32_t, const char *type, uint32_t version, 0))}; if(!node) { - ERR("Failed to create node proxy object (errno: %d)\n", errno); + ERR("Failed to create node proxy object (errno: {})", errno); return; } @@ -1248,15 +1314,15 @@ void EventManager::addCallback(uint32_t id, uint32_t, const char *type, uint32_t const char *data_class{spa_dict_lookup(props, PW_KEY_METADATA_NAME)}; if(!data_class) return; - if(std::strcmp(data_class, "default") != 0) + if("default"sv != data_class) { - TRACE("Ignoring metadata \"%s\"\n", data_class); + TRACE("Ignoring metadata \"{}\"", data_class); return; } if(mDefaultMetadata) { - ERR("Duplicate default metadata\n"); + ERR("Duplicate default metadata"); return; } @@ -1264,7 +1330,7 @@ void EventManager::addCallback(uint32_t id, uint32_t, const char *type, uint32_t type, version, 0))}; if(!mdata) { - ERR("Failed to create metadata proxy object (errno: %d)\n", errno); + ERR("Failed to create metadata proxy object (errno: {})", errno); return; } @@ -1321,7 +1387,7 @@ spa_audio_info_raw make_spa_info(DeviceBase *device, bool is51rear, use_f32p_e u case DevFmtFloat: info.format = SPA_AUDIO_FORMAT_F32; break; } - info.rate = device->Frequency; + info.rate = device->mSampleRate; al::span map{}; switch(device->FmtChans) @@ -1337,6 +1403,7 @@ spa_audio_info_raw make_spa_info(DeviceBase *device, bool is51rear, use_f32p_e u case DevFmtX71: map = X71Map; break; case DevFmtX714: map = X714Map; break; case DevFmtX3D71: map = X71Map; break; + case DevFmtX7144: case DevFmtAmbi3D: info.flags |= SPA_AUDIO_FLAG_UNPOSITIONED; info.channels = device->channelsFromFmt(); @@ -1345,7 +1412,7 @@ spa_audio_info_raw make_spa_info(DeviceBase *device, bool is51rear, use_f32p_e u if(!map.empty()) { info.channels = static_cast(map.size()); - std::copy(map.begin(), map.end(), info.position); + std::copy(map.begin(), map.end(), std::begin(info.position)); } return info; @@ -1370,8 +1437,7 @@ class PipeWirePlayback final : public BackendBase { PwStreamPtr mStream; spa_hook mStreamListener{}; spa_io_rate_match *mRateMatch{}; - std::unique_ptr mChannelPtrs; - uint mNumChannels{}; + std::vector mChannelPtrs; static constexpr pw_stream_events CreateEvents() { @@ -1387,14 +1453,12 @@ class PipeWirePlayback final : public BackendBase { } public: - PipeWirePlayback(DeviceBase *device) noexcept : BackendBase{device} { } - ~PipeWirePlayback() + explicit PipeWirePlayback(DeviceBase *device) noexcept : BackendBase{device} { } + ~PipeWirePlayback() final { /* Stop the mainloop so the stream can be properly destroyed. */ if(mLoop) mLoop.stop(); } - - DEF_NEWDEL(PipeWirePlayback) }; @@ -1408,6 +1472,8 @@ void PipeWirePlayback::ioChangedCallback(uint32_t id, void *area, uint32_t size) case SPA_IO_RateMatch: if(size >= sizeof(spa_io_rate_match)) mRateMatch = static_cast(area); + else + mRateMatch = nullptr; break; } } @@ -1418,7 +1484,7 @@ void PipeWirePlayback::outputCallback() noexcept if(!pw_buf) UNLIKELY return; const al::span datas{pw_buf->buffer->datas, - minu(mNumChannels, pw_buf->buffer->n_datas)}; + std::min(mChannelPtrs.size(), size_t{pw_buf->buffer->n_datas})}; #if PW_CHECK_VERSION(0,3,49) /* In 0.3.49, pw_buffer::requested specifies the number of samples needed * by the resampler/graph for this audio update. @@ -1431,18 +1497,18 @@ void PipeWirePlayback::outputCallback() noexcept uint length{mRateMatch ? mRateMatch->size : 0u}; #endif /* If no length is specified, use the device's update size as a fallback. */ - if(!length) UNLIKELY length = mDevice->UpdateSize; + if(!length) UNLIKELY length = mDevice->mUpdateSize; /* For planar formats, each datas[] seems to contain one channel, so store * the pointers in an array. Limit the render length in case the available * buffer length in any one channel is smaller than we wanted (shouldn't * be, but just in case). */ - float **chanptr_end{mChannelPtrs.get()}; + auto chanptr_end = mChannelPtrs.begin(); for(const auto &data : datas) { - length = minu(length, data.maxsize/sizeof(float)); - *chanptr_end = static_cast(data.data); + length = std::min(length, data.maxsize/uint{sizeof(float)}); + *chanptr_end = data.data; ++chanptr_end; data.chunk->offset = 0; @@ -1450,7 +1516,7 @@ void PipeWirePlayback::outputCallback() noexcept data.chunk->size = length * sizeof(float); } - mDevice->renderSamples({mChannelPtrs.get(), chanptr_end}, length); + mDevice->renderSamples(mChannelPtrs, length); pw_buf->size = length; pw_stream_queue_buffer(mStream.get(), pw_buf); @@ -1466,7 +1532,7 @@ void PipeWirePlayback::open(std::string_view name) gEventHandler.waitForInit(); if(name.empty()) { - EventWatcherLockGuard _{gEventHandler}; + EventWatcherLockGuard evtlock{gEventHandler}; auto&& devlist = DeviceNode::GetList(); auto match = devlist.cend(); @@ -1491,15 +1557,15 @@ void PipeWirePlayback::open(std::string_view name) } else { - EventWatcherLockGuard _{gEventHandler}; + EventWatcherLockGuard evtlock{gEventHandler}; auto&& devlist = DeviceNode::GetList(); auto match_name = [name](const DeviceNode &n) -> bool - { return n.mType != NodeType::Source && n.mName == name; }; + { return n.mType != NodeType::Source && (n.mName == name || n.mDevName == name); }; auto match = std::find_if(devlist.cbegin(), devlist.cend(), match_name); if(match == devlist.cend()) throw al::backend_exception{al::backend_error::NoDevice, - "Device name \"%.*s\" not found", static_cast(name.length()), name.data()}; + "Device name \"{}\" not found", name}; targetid = match->mSerial; devname = match->mName; @@ -1512,10 +1578,10 @@ void PipeWirePlayback::open(std::string_view name) mLoop = ThreadMainloop::Create(thread_name.c_str()); if(!mLoop) throw al::backend_exception{al::backend_error::DeviceError, - "Failed to create PipeWire mainloop (errno: %d)", errno}; + "Failed to create PipeWire mainloop (errno: {})", errno}; if(int res{mLoop.start()}) throw al::backend_exception{al::backend_error::DeviceError, - "Failed to start PipeWire mainloop (res: %d)", res}; + "Failed to start PipeWire mainloop (res: {})", res}; } MainloopUniqueLock mlock{mLoop}; if(!mContext) @@ -1524,14 +1590,14 @@ void PipeWirePlayback::open(std::string_view name) mContext = mLoop.newContext(cprops); if(!mContext) throw al::backend_exception{al::backend_error::DeviceError, - "Failed to create PipeWire event context (errno: %d)\n", errno}; + "Failed to create PipeWire event context (errno: {})\n", errno}; } if(!mCore) { mCore = PwCorePtr{pw_context_connect(mContext.get(), nullptr, 0)}; if(!mCore) throw al::backend_exception{al::backend_error::DeviceError, - "Failed to connect PipeWire event context (errno: %d)\n", errno}; + "Failed to connect PipeWire event context (errno: {})\n", errno}; } mlock.unlock(); @@ -1539,21 +1605,21 @@ void PipeWirePlayback::open(std::string_view name) mTargetId = targetid; if(!devname.empty()) - mDevice->DeviceName = std::move(devname); + mDeviceName = std::move(devname); else - mDevice->DeviceName = pwireDevice; + mDeviceName = "PipeWire Output"sv; } bool PipeWirePlayback::reset() { if(mStream) { - MainloopLockGuard _{mLoop}; + MainloopLockGuard looplock{mLoop}; mStream = nullptr; } mStreamListener = {}; mRateMatch = nullptr; - mTimeBase = GetDeviceClockTime(mDevice); + mTimeBase = mDevice->getClockTime(); /* If connecting to a specific device, update various device parameters to * match its format. @@ -1562,7 +1628,7 @@ bool PipeWirePlayback::reset() mDevice->Flags.reset(DirectEar); if(mTargetId != PwIdAny) { - EventWatcherLockGuard _{gEventHandler}; + EventWatcherLockGuard evtlock{gEventHandler}; auto&& devlist = DeviceNode::GetList(); auto match_id = [targetid=mTargetId](const DeviceNode &n) -> bool @@ -1573,12 +1639,13 @@ bool PipeWirePlayback::reset() if(!mDevice->Flags.test(FrequencyRequest) && match->mSampleRate > 0) { /* Scale the update size if the sample rate changes. */ - const double scale{static_cast(match->mSampleRate) / mDevice->Frequency}; - const double numbufs{static_cast(mDevice->BufferSize)/mDevice->UpdateSize}; - mDevice->Frequency = match->mSampleRate; - mDevice->UpdateSize = static_cast(clampd(mDevice->UpdateSize*scale + 0.5, - 64.0, 8192.0)); - mDevice->BufferSize = static_cast(numbufs*mDevice->UpdateSize + 0.5); + const double scale{static_cast(match->mSampleRate) / mDevice->mSampleRate}; + const double updatesize{std::round(mDevice->mUpdateSize * scale)}; + const double buffersize{std::round(mDevice->mBufferSize * scale)}; + + mDevice->mSampleRate = match->mSampleRate; + mDevice->mUpdateSize = static_cast(std::clamp(updatesize, 64.0, 8192.0)); + mDevice->mBufferSize = static_cast(std::max(buffersize, 128.0)); } if(!mDevice->Flags.test(ChannelsRequest) && match->mChannels != InvalidChannelConfig) mDevice->FmtChans = match->mChannels; @@ -1590,16 +1657,10 @@ bool PipeWirePlayback::reset() /* Force planar 32-bit float output for playback. This is what PipeWire * handles internally, and it's easier for us too. */ - spa_audio_info_raw info{make_spa_info(mDevice, is51rear, ForceF32Planar)}; + auto info = spa_audio_info_raw{make_spa_info(mDevice, is51rear, ForceF32Planar)}; - /* TODO: How to tell what an appropriate size is? Examples just use this - * magic value. - */ - constexpr uint32_t pod_buffer_size{1024}; - auto pod_buffer = std::make_unique(pod_buffer_size); - spa_pod_builder b{make_pod_builder(pod_buffer.get(), pod_buffer_size)}; - - const spa_pod *params{spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &info)}; + auto b = PodDynamicBuilder{}; + auto params = as_const_ptr(spa_format_audio_raw_build(b.get(), SPA_PARAM_EnumFormat, &info)); if(!params) throw al::backend_exception{al::backend_error::DeviceError, "Failed to set PipeWire audio format parameters"}; @@ -1608,7 +1669,7 @@ bool PipeWirePlayback::reset() * be useful? */ auto&& binary = GetProcBinary(); - const char *appname{binary.fname.length() ? binary.fname.c_str() : "OpenAL Soft"}; + const char *appname{!binary.fname.empty() ? binary.fname.c_str() : "OpenAL Soft"}; pw_properties *props{pw_properties_new(PW_KEY_NODE_NAME, appname, PW_KEY_NODE_DESCRIPTION, appname, PW_KEY_MEDIA_TYPE, "Audio", @@ -1618,11 +1679,11 @@ bool PipeWirePlayback::reset() nullptr)}; if(!props) throw al::backend_exception{al::backend_error::DeviceError, - "Failed to create PipeWire stream properties (errno: %d)", errno}; + "Failed to create PipeWire stream properties (errno: {})", errno}; - pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%u", mDevice->UpdateSize, - mDevice->Frequency); - pw_properties_setf(props, PW_KEY_NODE_RATE, "1/%u", mDevice->Frequency); + pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%u", mDevice->mUpdateSize, + mDevice->mSampleRate); + pw_properties_setf(props, PW_KEY_NODE_RATE, "1/%u", mDevice->mSampleRate); #ifdef PW_KEY_TARGET_OBJECT pw_properties_setf(props, PW_KEY_TARGET_OBJECT, "%" PRIu64, mTargetId); #else @@ -1634,17 +1695,17 @@ bool PipeWirePlayback::reset() mStream = PwStreamPtr{pw_stream_new(mCore.get(), "Playback Stream", props)}; if(!mStream) throw al::backend_exception{al::backend_error::NoDevice, - "Failed to create PipeWire stream (errno: %d)", errno}; + "Failed to create PipeWire stream (errno: {})", errno}; static constexpr pw_stream_events streamEvents{CreateEvents()}; pw_stream_add_listener(mStream.get(), &mStreamListener, &streamEvents, this); pw_stream_flags flags{PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_INACTIVE | PW_STREAM_FLAG_MAP_BUFFERS}; - if(GetConfigValueBool(mDevice->DeviceName.c_str(), "pipewire", "rt-mix", true)) + if(GetConfigValueBool(mDevice->mDeviceName, "pipewire", "rt-mix", false)) flags |= PW_STREAM_FLAG_RT_PROCESS; if(int res{pw_stream_connect(mStream.get(), PW_DIRECTION_OUTPUT, PwIdAny, flags, ¶ms, 1)}) throw al::backend_exception{al::backend_error::DeviceError, - "Error connecting PipeWire stream (res: %d)", res}; + "Error connecting PipeWire stream (res: {})", res}; /* Wait for the stream to become paused (ready to start streaming). */ plock.wait([stream=mStream.get()]() @@ -1653,20 +1714,19 @@ bool PipeWirePlayback::reset() pw_stream_state state{pw_stream_get_state(stream, &error)}; if(state == PW_STREAM_STATE_ERROR) throw al::backend_exception{al::backend_error::DeviceError, - "Error connecting PipeWire stream: \"%s\"", error}; + "Error connecting PipeWire stream: \"{}\"", error}; return state == PW_STREAM_STATE_PAUSED; }); - /* TODO: Update mDevice->UpdateSize with the stream's quantum, and - * mDevice->BufferSize with the total known buffering delay from the head + /* TODO: Update mDevice->mUpdateSize with the stream's quantum, and + * mDevice->mBufferSize with the total known buffering delay from the head * of this playback stream to the tail of the device output. * * This info is apparently not available until after the stream starts. */ plock.unlock(); - mNumChannels = mDevice->channelsFromFmt(); - mChannelPtrs = std::make_unique(mNumChannels); + mChannelPtrs.resize(mDevice->channelsFromFmt()); setDefaultWFXChannelOrder(); @@ -1678,7 +1738,7 @@ void PipeWirePlayback::start() MainloopUniqueLock plock{mLoop}; if(int res{pw_stream_set_active(mStream.get(), true)}) throw al::backend_exception{al::backend_error::DeviceError, - "Failed to start PipeWire stream (res: %d)", res}; + "Failed to start PipeWire stream (res: {})", res}; /* Wait for the stream to start playing (would be nice to not, but we need * the actual update size which is only available after starting). @@ -1689,7 +1749,7 @@ void PipeWirePlayback::start() pw_stream_state state{pw_stream_get_state(stream, &error)}; if(state == PW_STREAM_STATE_ERROR) throw al::backend_exception{al::backend_error::DeviceError, - "PipeWire stream error: %s", error ? error : "(unknown)"}; + "PipeWire stream error: {}", error ? error : "(unknown)"}; return state == PW_STREAM_STATE_STREAMING; }); @@ -1704,7 +1764,7 @@ void PipeWirePlayback::start() pw_time ptime{}; if(int res{pw_stream_get_time_n(mStream.get(), &ptime, sizeof(ptime))}) { - ERR("Failed to get PipeWire stream time (res: %d)\n", res); + ERR("Failed to get PipeWire stream time (res: {})", res); break; } @@ -1719,12 +1779,12 @@ void PipeWirePlayback::start() const uint totalbuffers{ptime.avail_buffers + ptime.queued_buffers}; /* Ensure the delay is in sample frames. */ - const uint64_t delay{static_cast(ptime.delay) * mDevice->Frequency * + const uint64_t delay{static_cast(ptime.delay) * mDevice->mSampleRate * ptime.rate.num / ptime.rate.denom}; - mDevice->UpdateSize = updatesize; - mDevice->BufferSize = static_cast(ptime.buffered + delay + - totalbuffers*updatesize); + mDevice->mUpdateSize = updatesize; + mDevice->mBufferSize = static_cast(ptime.buffered + delay + + uint64_t{totalbuffers}*updatesize); break; } #else @@ -1734,17 +1794,17 @@ void PipeWirePlayback::start() if(ptime.rate.denom > 0 && updatesize > 0) { /* Ensure the delay is in sample frames. */ - const uint64_t delay{static_cast(ptime.delay) * mDevice->Frequency * + const uint64_t delay{static_cast(ptime.delay) * mDevice->mSampleRate * ptime.rate.num / ptime.rate.denom}; - mDevice->UpdateSize = updatesize; - mDevice->BufferSize = static_cast(delay + updatesize); + mDevice->mUpdateSize = updatesize; + mDevice->mBufferSize = static_cast(delay + updatesize); break; } #endif if(!--wait_count) { - ERR("Timeout getting PipeWire stream buffering info\n"); + ERR("Timeout getting PipeWire stream buffering info"); break; } @@ -1758,8 +1818,7 @@ void PipeWirePlayback::stop() { MainloopUniqueLock plock{mLoop}; if(int res{pw_stream_set_active(mStream.get(), false)}) - throw al::backend_exception{al::backend_error::DeviceError, - "Failed to stop PipeWire stream (res: %d)", res}; + ERR("Failed to stop PipeWire stream (res: {})", res); /* Wait for the stream to stop playing. */ plock.wait([stream=mStream.get()]() @@ -1778,9 +1837,9 @@ ClockLatency PipeWirePlayback::getClockLatency() pw_time ptime{}; if(mStream) { - MainloopLockGuard _{mLoop}; + MainloopLockGuard looplock{mLoop}; if(int res{pw_stream_get_time_n(mStream.get(), &ptime, sizeof(ptime))}) - ERR("Failed to get PipeWire stream time (res: %d)\n", res); + ERR("Failed to get PipeWire stream time (res: {})", res); } /* Now get the mixer time and the CLOCK_MONOTONIC time atomically (i.e. the @@ -1791,10 +1850,10 @@ ClockLatency PipeWirePlayback::getClockLatency() uint refcount; do { refcount = mDevice->waitForMix(); - mixtime = GetDeviceClockTime(mDevice); + mixtime = mDevice->getClockTime(); clock_gettime(CLOCK_MONOTONIC, &tspec); std::atomic_thread_fence(std::memory_order_acquire); - } while(refcount != ReadRef(mDevice->MixCount)); + } while(refcount != mDevice->mMixCount.load(std::memory_order_relaxed)); /* Convert the monotonic clock, stream ticks, and stream delay to * nanoseconds. @@ -1808,7 +1867,7 @@ ClockLatency PipeWirePlayback::getClockLatency() */ ptime.now = monoclock.count(); curtic = mixtime; - delay = nanoseconds{seconds{mDevice->BufferSize}} / mDevice->Frequency; + delay = nanoseconds{seconds{mDevice->mBufferSize}} / mDevice->mSampleRate; } else { @@ -1868,7 +1927,7 @@ class PipeWireCapture final : public BackendBase { PwStreamPtr mStream; spa_hook mStreamListener{}; - RingBufferPtr mRing{}; + RingBufferPtr mRing; static constexpr pw_stream_events CreateEvents() { @@ -1882,10 +1941,8 @@ class PipeWireCapture final : public BackendBase { } public: - PipeWireCapture(DeviceBase *device) noexcept : BackendBase{device} { } - ~PipeWireCapture() { if(mLoop) mLoop.stop(); } - - DEF_NEWDEL(PipeWireCapture) + explicit PipeWireCapture(DeviceBase *device) noexcept : BackendBase{device} { } + ~PipeWireCapture() final { if(mLoop) mLoop.stop(); } }; @@ -1898,10 +1955,11 @@ void PipeWireCapture::inputCallback() noexcept if(!pw_buf) UNLIKELY return; spa_data *bufdata{pw_buf->buffer->datas}; - const uint offset{minu(bufdata->chunk->offset, bufdata->maxsize)}; - const uint size{minu(bufdata->chunk->size, bufdata->maxsize - offset)}; + const uint offset{bufdata->chunk->offset % bufdata->maxsize}; + const auto input = al::span{static_cast(bufdata->data), bufdata->maxsize} + .subspan(offset, std::min(bufdata->chunk->size, bufdata->maxsize - offset)); - mRing->write(static_cast(bufdata->data) + offset, size / mRing->getElemSize()); + std::ignore = mRing->write(input.data(), input.size() / mRing->getElemSize()); pw_stream_queue_buffer(mStream.get(), pw_buf); } @@ -1916,7 +1974,7 @@ void PipeWireCapture::open(std::string_view name) gEventHandler.waitForInit(); if(name.empty()) { - EventWatcherLockGuard _{gEventHandler}; + EventWatcherLockGuard evtlock{gEventHandler}; auto&& devlist = DeviceNode::GetList(); auto match = devlist.cend(); @@ -1942,30 +2000,39 @@ void PipeWireCapture::open(std::string_view name) targetid = match->mSerial; if(match->mType != NodeType::Sink) devname = match->mName; - else devname = MonitorPrefix+match->mName; + else devname = std::string{GetMonitorPrefix()}+match->mName; } else { - EventWatcherLockGuard _{gEventHandler}; + EventWatcherLockGuard evtlock{gEventHandler}; auto&& devlist = DeviceNode::GetList(); + const std::string_view prefix{GetMonitorPrefix()}; + const std::string_view suffix{GetMonitorSuffix()}; auto match_name = [name](const DeviceNode &n) -> bool { return n.mType != NodeType::Sink && n.mName == name; }; auto match = std::find_if(devlist.cbegin(), devlist.cend(), match_name); - if(match == devlist.cend() && name.length() >= MonitorPrefixLen - && std::strncmp(name.data(), MonitorPrefix, MonitorPrefixLen) == 0) + if(match == devlist.cend() && al::starts_with(name, prefix)) { - const std::string_view sinkname{name.substr(MonitorPrefixLen)}; + const std::string_view sinkname{name.substr(prefix.length())}; auto match_sinkname = [sinkname](const DeviceNode &n) -> bool { return n.mType == NodeType::Sink && n.mName == sinkname; }; match = std::find_if(devlist.cbegin(), devlist.cend(), match_sinkname); } + else if(match == devlist.cend() && al::ends_with(name, suffix)) + { + const std::string_view sinkname{name.substr(0, name.size()-suffix.size())}; + auto match_sinkname = [sinkname](const DeviceNode &n) -> bool + { return n.mType == NodeType::Sink && n.mDevName == sinkname; }; + match = std::find_if(devlist.cbegin(), devlist.cend(), match_sinkname); + } if(match == devlist.cend()) throw al::backend_exception{al::backend_error::NoDevice, - "Device name \"%.*s\" not found", static_cast(name.length()), name.data()}; + "Device name \"{}\" not found", name}; targetid = match->mSerial; - devname = name; + if(match->mType != NodeType::Sink) devname = match->mName; + else devname = std::string{GetMonitorPrefix()}+match->mName; } if(!mLoop) @@ -1975,10 +2042,10 @@ void PipeWireCapture::open(std::string_view name) mLoop = ThreadMainloop::Create(thread_name.c_str()); if(!mLoop) throw al::backend_exception{al::backend_error::DeviceError, - "Failed to create PipeWire mainloop (errno: %d)", errno}; + "Failed to create PipeWire mainloop (errno: {})", errno}; if(int res{mLoop.start()}) throw al::backend_exception{al::backend_error::DeviceError, - "Failed to start PipeWire mainloop (res: %d)", res}; + "Failed to start PipeWire mainloop (res: {})", res}; } MainloopUniqueLock mlock{mLoop}; if(!mContext) @@ -1987,14 +2054,14 @@ void PipeWireCapture::open(std::string_view name) mContext = mLoop.newContext(cprops); if(!mContext) throw al::backend_exception{al::backend_error::DeviceError, - "Failed to create PipeWire event context (errno: %d)\n", errno}; + "Failed to create PipeWire event context (errno: {})\n", errno}; } if(!mCore) { mCore = PwCorePtr{pw_context_connect(mContext.get(), nullptr, 0)}; if(!mCore) throw al::backend_exception{al::backend_error::DeviceError, - "Failed to connect PipeWire event context (errno: %d)\n", errno}; + "Failed to connect PipeWire event context (errno: {})\n", errno}; } mlock.unlock(); @@ -2002,15 +2069,15 @@ void PipeWireCapture::open(std::string_view name) mTargetId = targetid; if(!devname.empty()) - mDevice->DeviceName = std::move(devname); + mDeviceName = std::move(devname); else - mDevice->DeviceName = pwireInput; + mDeviceName = "PipeWire Input"sv; bool is51rear{false}; if(mTargetId != PwIdAny) { - EventWatcherLockGuard _{gEventHandler}; + EventWatcherLockGuard evtlock{gEventHandler}; auto&& devlist = DeviceNode::GetList(); auto match_id = [targetid=mTargetId](const DeviceNode &n) -> bool @@ -2019,19 +2086,16 @@ void PipeWireCapture::open(std::string_view name) if(match != devlist.cend()) is51rear = match->mIs51Rear; } - spa_audio_info_raw info{make_spa_info(mDevice, is51rear, UseDevType)}; - - constexpr uint32_t pod_buffer_size{1024}; - auto pod_buffer = std::make_unique(pod_buffer_size); - spa_pod_builder b{make_pod_builder(pod_buffer.get(), pod_buffer_size)}; + auto info = spa_audio_info_raw{make_spa_info(mDevice, is51rear, UseDevType)}; - const spa_pod *params[]{spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &info)}; - if(!params[0]) + auto b = PodDynamicBuilder{}; + auto params = as_const_ptr(spa_format_audio_raw_build(b.get(), SPA_PARAM_EnumFormat, &info)); + if(!params) throw al::backend_exception{al::backend_error::DeviceError, "Failed to set PipeWire audio format parameters"}; auto&& binary = GetProcBinary(); - const char *appname{binary.fname.length() ? binary.fname.c_str() : "OpenAL Soft"}; + const char *appname{!binary.fname.empty() ? binary.fname.c_str() : "OpenAL Soft"}; pw_properties *props{pw_properties_new( PW_KEY_NODE_NAME, appname, PW_KEY_NODE_DESCRIPTION, appname, @@ -2042,15 +2106,15 @@ void PipeWireCapture::open(std::string_view name) nullptr)}; if(!props) throw al::backend_exception{al::backend_error::DeviceError, - "Failed to create PipeWire stream properties (errno: %d)", errno}; + "Failed to create PipeWire stream properties (errno: {})", errno}; /* We don't actually care what the latency/update size is, as long as it's * reasonable. Unfortunately, when unspecified PipeWire seems to default to * around 40ms, which isn't great. So request 20ms instead. */ - pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%u", (mDevice->Frequency+25) / 50, - mDevice->Frequency); - pw_properties_setf(props, PW_KEY_NODE_RATE, "1/%u", mDevice->Frequency); + pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%u", (mDevice->mSampleRate+25) / 50, + mDevice->mSampleRate); + pw_properties_setf(props, PW_KEY_NODE_RATE, "1/%u", mDevice->mSampleRate); #ifdef PW_KEY_TARGET_OBJECT pw_properties_setf(props, PW_KEY_TARGET_OBJECT, "%" PRIu64, mTargetId); #else @@ -2061,15 +2125,15 @@ void PipeWireCapture::open(std::string_view name) mStream = PwStreamPtr{pw_stream_new(mCore.get(), "Capture Stream", props)}; if(!mStream) throw al::backend_exception{al::backend_error::NoDevice, - "Failed to create PipeWire stream (errno: %d)", errno}; + "Failed to create PipeWire stream (errno: {})", errno}; static constexpr pw_stream_events streamEvents{CreateEvents()}; pw_stream_add_listener(mStream.get(), &mStreamListener, &streamEvents, this); constexpr pw_stream_flags Flags{PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_INACTIVE | PW_STREAM_FLAG_MAP_BUFFERS | PW_STREAM_FLAG_RT_PROCESS}; - if(int res{pw_stream_connect(mStream.get(), PW_DIRECTION_INPUT, PwIdAny, Flags, params, 1)}) + if(int res{pw_stream_connect(mStream.get(), PW_DIRECTION_INPUT, PwIdAny, Flags, ¶ms, 1)}) throw al::backend_exception{al::backend_error::DeviceError, - "Error connecting PipeWire stream (res: %d)", res}; + "Error connecting PipeWire stream (res: {})", res}; /* Wait for the stream to become paused (ready to start streaming). */ plock.wait([stream=mStream.get()]() @@ -2078,7 +2142,7 @@ void PipeWireCapture::open(std::string_view name) pw_stream_state state{pw_stream_get_state(stream, &error)}; if(state == PW_STREAM_STATE_ERROR) throw al::backend_exception{al::backend_error::DeviceError, - "Error connecting PipeWire stream: \"%s\"", error}; + "Error connecting PipeWire stream: \"{}\"", error}; return state == PW_STREAM_STATE_PAUSED; }); plock.unlock(); @@ -2086,7 +2150,7 @@ void PipeWireCapture::open(std::string_view name) setDefaultWFXChannelOrder(); /* Ensure at least a 100ms capture buffer. */ - mRing = RingBuffer::Create(maxu(mDevice->Frequency/10, mDevice->BufferSize), + mRing = RingBuffer::Create(std::max(mDevice->mSampleRate/10u, mDevice->mBufferSize), mDevice->frameSizeFromFmt(), false); } @@ -2096,7 +2160,7 @@ void PipeWireCapture::start() MainloopUniqueLock plock{mLoop}; if(int res{pw_stream_set_active(mStream.get(), true)}) throw al::backend_exception{al::backend_error::DeviceError, - "Failed to start PipeWire stream (res: %d)", res}; + "Failed to start PipeWire stream (res: {})", res}; plock.wait([stream=mStream.get()]() { @@ -2104,7 +2168,7 @@ void PipeWireCapture::start() pw_stream_state state{pw_stream_get_state(stream, &error)}; if(state == PW_STREAM_STATE_ERROR) throw al::backend_exception{al::backend_error::DeviceError, - "PipeWire stream error: %s", error ? error : "(unknown)"}; + "PipeWire stream error: {}", error ? error : "(unknown)"}; return state == PW_STREAM_STATE_STREAMING; }); } @@ -2113,8 +2177,7 @@ void PipeWireCapture::stop() { MainloopUniqueLock plock{mLoop}; if(int res{pw_stream_set_active(mStream.get(), false)}) - throw al::backend_exception{al::backend_error::DeviceError, - "Failed to stop PipeWire stream (res: %d)", res}; + ERR("Failed to stop PipeWire stream (res: {})", res); plock.wait([stream=mStream.get()]() { return pw_stream_get_state(stream, nullptr) != PW_STREAM_STATE_STREAMING; }); @@ -2124,7 +2187,7 @@ uint PipeWireCapture::availableSamples() { return static_cast(mRing->readSpace()); } void PipeWireCapture::captureSamples(std::byte *buffer, uint samples) -{ mRing->read(buffer, samples); } +{ std::ignore = mRing->read(buffer, samples); } } // namespace @@ -2137,24 +2200,24 @@ bool PipeWireBackendFactory::init() const char *version{pw_get_library_version()}; if(!check_version(version)) { - WARN("PipeWire version \"%s\" too old (%s or newer required)\n", version, + WARN("PipeWire version \"{}\" too old ({} or newer required)", version, pw_get_headers_version()); return false; } - TRACE("Found PipeWire version \"%s\" (%s or newer)\n", version, pw_get_headers_version()); + TRACE("Found PipeWire version \"{}\" ({} or newer)", version, pw_get_headers_version()); - pw_init(0, nullptr); + pw_init(nullptr, nullptr); if(!gEventHandler.init()) return false; - if(!GetConfigValueBool(nullptr, "pipewire", "assume-audio", false) + if(!GetConfigValueBool({}, "pipewire", "assume-audio", false) && !gEventHandler.waitForAudio()) { gEventHandler.kill(); /* TODO: Temporary warning, until PipeWire gets a proper way to report * audio support. */ - WARN("No audio support detected in PipeWire. See the PipeWire options in alsoftrc.sample if this is wrong.\n"); + WARN("No audio support detected in PipeWire. See the PipeWire options in alsoftrc.sample if this is wrong."); return false; } return true; @@ -2163,12 +2226,12 @@ bool PipeWireBackendFactory::init() bool PipeWireBackendFactory::querySupport(BackendType type) { return type == BackendType::Playback || type == BackendType::Capture; } -std::string PipeWireBackendFactory::probe(BackendType type) +auto PipeWireBackendFactory::enumerate(BackendType type) -> std::vector { - std::string outnames; + std::vector outnames; gEventHandler.waitForInit(); - EventWatcherLockGuard _{gEventHandler}; + EventWatcherLockGuard evtlock{gEventHandler}; auto&& devlist = DeviceNode::GetList(); auto match_defsink = [](const DeviceNode &n) -> bool @@ -2186,31 +2249,31 @@ std::string PipeWireBackendFactory::probe(BackendType type) case BackendType::Playback: defmatch = std::find_if(defmatch, devlist.cend(), match_defsink); if(defmatch != devlist.cend()) - { - /* Includes null char. */ - outnames.append(defmatch->mName.c_str(), defmatch->mName.length()+1); - } + outnames.emplace_back(defmatch->mName); for(auto iter = devlist.cbegin();iter != devlist.cend();++iter) { if(iter != defmatch && iter->mType != NodeType::Source) - outnames.append(iter->mName.c_str(), iter->mName.length()+1); + outnames.emplace_back(iter->mName); } break; case BackendType::Capture: + outnames.reserve(devlist.size()); defmatch = std::find_if(defmatch, devlist.cend(), match_defsource); if(defmatch != devlist.cend()) { if(defmatch->mType == NodeType::Sink) - outnames.append(MonitorPrefix); - outnames.append(defmatch->mName.c_str(), defmatch->mName.length()+1); + outnames.emplace_back(std::string{GetMonitorPrefix()}+defmatch->mName); + else + outnames.emplace_back(defmatch->mName); } for(auto iter = devlist.cbegin();iter != devlist.cend();++iter) { if(iter != defmatch) { if(iter->mType == NodeType::Sink) - outnames.append(MonitorPrefix); - outnames.append(iter->mName.c_str(), iter->mName.length()+1); + outnames.emplace_back(std::string{GetMonitorPrefix()}+iter->mName); + else + outnames.emplace_back(iter->mName); } } break; @@ -2219,6 +2282,7 @@ std::string PipeWireBackendFactory::probe(BackendType type) return outnames; } + BackendPtr PipeWireBackendFactory::createBackend(DeviceBase *device, BackendType type) { if(type == BackendType::Playback) @@ -2233,3 +2297,18 @@ BackendFactory &PipeWireBackendFactory::getFactory() static PipeWireBackendFactory factory{}; return factory; } + +alc::EventSupport PipeWireBackendFactory::queryEventSupport(alc::EventType eventType, BackendType) +{ + switch(eventType) + { + case alc::EventType::DefaultDeviceChanged: + case alc::EventType::DeviceAdded: + case alc::EventType::DeviceRemoved: + return alc::EventSupport::FullSupport; + + case alc::EventType::Count: + break; + } + return alc::EventSupport::NoSupport; +} diff --git a/3rdparty/openal/alc/backends/pipewire.h b/3rdparty/openal/alc/backends/pipewire.h index 5f930239c607..567d18eddd0a 100644 --- a/3rdparty/openal/alc/backends/pipewire.h +++ b/3rdparty/openal/alc/backends/pipewire.h @@ -2,22 +2,26 @@ #define BACKENDS_PIPEWIRE_H #include +#include +#include "alc/events.h" #include "base.h" struct DeviceBase; struct PipeWireBackendFactory final : public BackendFactory { public: - bool init() override; + auto init() -> bool final; - bool querySupport(BackendType type) override; + auto querySupport(BackendType type) -> bool final; - std::string probe(BackendType type) override; + auto queryEventSupport(alc::EventType eventType, BackendType type) -> alc::EventSupport final; - BackendPtr createBackend(DeviceBase *device, BackendType type) override; + auto enumerate(BackendType type) -> std::vector final; - static BackendFactory &getFactory(); + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; + + static auto getFactory() -> BackendFactory&; }; #endif /* BACKENDS_PIPEWIRE_H */ diff --git a/3rdparty/openal/alc/backends/portaudio.cpp b/3rdparty/openal/alc/backends/portaudio.cpp index 979a54d616bc..75497c3f3ae2 100644 --- a/3rdparty/openal/alc/backends/portaudio.cpp +++ b/3rdparty/openal/alc/backends/portaudio.cpp @@ -20,15 +20,14 @@ #include "config.h" -#include "portaudio.h" +#include "portaudio.hpp" +#include #include #include #include -#include "albit.h" #include "alc/alconfig.h" -#include "alnumeric.h" #include "core/device.h" #include "core/logging.h" #include "dynload.h" @@ -39,10 +38,7 @@ namespace { -constexpr char pa_device[] = "PortAudio Default"; - - -#ifdef HAVE_DYNLOAD +#if HAVE_DYNLOAD void *pa_handle; #define MAKE_FUNC(x) decltype(x) * p##x MAKE_FUNC(Pa_Initialize); @@ -52,6 +48,8 @@ MAKE_FUNC(Pa_StartStream); MAKE_FUNC(Pa_StopStream); MAKE_FUNC(Pa_OpenStream); MAKE_FUNC(Pa_CloseStream); +MAKE_FUNC(Pa_GetDeviceCount); +MAKE_FUNC(Pa_GetDeviceInfo); MAKE_FUNC(Pa_GetDefaultOutputDevice); MAKE_FUNC(Pa_GetDefaultInputDevice); MAKE_FUNC(Pa_GetStreamInfo); @@ -65,26 +63,56 @@ MAKE_FUNC(Pa_GetStreamInfo); #define Pa_StopStream pPa_StopStream #define Pa_OpenStream pPa_OpenStream #define Pa_CloseStream pPa_CloseStream +#define Pa_GetDeviceCount pPa_GetDeviceCount +#define Pa_GetDeviceInfo pPa_GetDeviceInfo #define Pa_GetDefaultOutputDevice pPa_GetDefaultOutputDevice #define Pa_GetDefaultInputDevice pPa_GetDefaultInputDevice #define Pa_GetStreamInfo pPa_GetStreamInfo #endif #endif +struct DeviceEntry { + std::string mName; + uint mPlaybackChannels{}; + uint mCaptureChannels{}; +}; +std::vector DeviceNames; + +void EnumerateDevices() +{ + const auto devcount = Pa_GetDeviceCount(); + if(devcount < 0) + { + ERR("Error getting device count: {}", Pa_GetErrorText(devcount)); + return; + } + + std::vector(static_cast(devcount)).swap(DeviceNames); + PaDeviceIndex idx{0}; + for(auto &entry : DeviceNames) + { + if(auto info = Pa_GetDeviceInfo(idx); info && info->name) + { + entry.mName = info->name; + entry.mPlaybackChannels = static_cast(std::max(info->maxOutputChannels, 0)); + entry.mCaptureChannels = static_cast(std::max(info->maxInputChannels, 0)); + TRACE("Device {} \"{}\": {} playback, {} capture channels", idx, entry.mName, + info->maxOutputChannels, info->maxInputChannels); + } + ++idx; + } +} + +struct StreamParamsExt : public PaStreamParameters { uint updateSize; }; struct PortPlayback final : public BackendBase { - PortPlayback(DeviceBase *device) noexcept : BackendBase{device} { } + explicit PortPlayback(DeviceBase *device) noexcept : BackendBase{device} { } ~PortPlayback() override; int writeCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackFlags statusFlags) noexcept; - static int writeCallbackC(const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, - const PaStreamCallbackFlags statusFlags, void *userData) noexcept - { - return static_cast(userData)->writeCallback(inputBuffer, outputBuffer, - framesPerBuffer, timeInfo, statusFlags); - } + + void createStream(PaDeviceIndex deviceid); void open(std::string_view name) override; bool reset() override; @@ -92,17 +120,15 @@ struct PortPlayback final : public BackendBase { void stop() override; PaStream *mStream{nullptr}; - PaStreamParameters mParams{}; - uint mUpdateSize{0u}; - - DEF_NEWDEL(PortPlayback) + StreamParamsExt mParams{}; + PaDeviceIndex mDeviceIdx{-1}; }; PortPlayback::~PortPlayback() { PaError err{mStream ? Pa_CloseStream(mStream) : paNoError}; if(err != paNoError) - ERR("Error closing stream: %s\n", Pa_GetErrorText(err)); + ERR("Error closing stream: {}", Pa_GetErrorText(err)); mStream = nullptr; } @@ -116,100 +142,134 @@ int PortPlayback::writeCallback(const void*, void *outputBuffer, unsigned long f } -void PortPlayback::open(std::string_view name) +void PortPlayback::createStream(PaDeviceIndex deviceid) { - if(name.empty()) - name = pa_device; - else if(name != pa_device) - throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", - static_cast(name.length()), name.data()}; - - PaStreamParameters params{}; - auto devidopt = ConfigValueInt(nullptr, "port", "device"); - if(devidopt && *devidopt >= 0) params.device = *devidopt; - else params.device = Pa_GetDefaultOutputDevice(); - params.suggestedLatency = mDevice->BufferSize / static_cast(mDevice->Frequency); - params.hostApiSpecificStreamInfo = nullptr; - - params.channelCount = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2); + auto &devinfo = DeviceNames.at(static_cast(deviceid)); + auto params = StreamParamsExt{}; + params.device = deviceid; + params.suggestedLatency = mDevice->mBufferSize / static_cast(mDevice->mSampleRate); + params.hostApiSpecificStreamInfo = nullptr; + params.channelCount = static_cast(std::min(devinfo.mPlaybackChannels, + mDevice->channelsFromFmt())); switch(mDevice->FmtType) { - case DevFmtByte: - params.sampleFormat = paInt8; - break; - case DevFmtUByte: - params.sampleFormat = paUInt8; - break; - case DevFmtUShort: - /* fall-through */ - case DevFmtShort: - params.sampleFormat = paInt16; - break; - case DevFmtUInt: - /* fall-through */ - case DevFmtInt: - params.sampleFormat = paInt32; - break; - case DevFmtFloat: - params.sampleFormat = paFloat32; - break; + case DevFmtByte: params.sampleFormat = paInt8; break; + case DevFmtUByte: params.sampleFormat = paUInt8; break; + case DevFmtUShort: [[fallthrough]]; + case DevFmtShort: params.sampleFormat = paInt16; break; + case DevFmtUInt: [[fallthrough]]; + case DevFmtInt: params.sampleFormat = paInt32; break; + case DevFmtFloat: params.sampleFormat = paFloat32; break; } + params.updateSize = mDevice->mUpdateSize; -retry_open: - PaStream *stream{}; - PaError err{Pa_OpenStream(&stream, nullptr, ¶ms, mDevice->Frequency, mDevice->UpdateSize, - paNoFlag, &PortPlayback::writeCallbackC, this)}; - if(err != paNoError) + auto srate = uint{mDevice->mSampleRate}; + + static constexpr auto writeCallback = [](const void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, + const PaStreamCallbackFlags statusFlags, void *userData) noexcept { - if(params.sampleFormat == paFloat32) - { + return static_cast(userData)->writeCallback(inputBuffer, outputBuffer, + framesPerBuffer, timeInfo, statusFlags); + }; + while(PaError err{Pa_OpenStream(&mStream, nullptr, ¶ms, srate, params.updateSize, paNoFlag, + writeCallback, this)}) + { + if(params.updateSize != DefaultUpdateSize) + params.updateSize = DefaultUpdateSize; + else if(srate != 48000u) + srate = (srate != 44100u) ? 44100u : 48000u; + else if(params.sampleFormat != paInt16) params.sampleFormat = paInt16; - goto retry_open; - } - throw al::backend_exception{al::backend_error::NoDevice, "Failed to open stream: %s", - Pa_GetErrorText(err)}; + else if(params.channelCount != 2) + params.channelCount = 2; + else + throw al::backend_exception{al::backend_error::NoDevice, "Failed to open stream: {}", + Pa_GetErrorText(err)}; } - Pa_CloseStream(mStream); - mStream = stream; mParams = params; - mUpdateSize = mDevice->UpdateSize; +} + +void PortPlayback::open(std::string_view name) +{ + if(DeviceNames.empty()) + EnumerateDevices(); + + PaDeviceIndex deviceid{-1}; + if(name.empty()) + { + if(auto devidopt = ConfigValueInt({}, "port", "device")) + deviceid = *devidopt; + if(deviceid < 0 || static_cast(deviceid) >= DeviceNames.size()) + deviceid = Pa_GetDefaultOutputDevice(); + name = DeviceNames.at(static_cast(deviceid)).mName; + } + else + { + auto iter = std::find_if(DeviceNames.cbegin(), DeviceNames.cend(), + [name](const DeviceEntry &entry) + { return entry.mPlaybackChannels > 0 && name == entry.mName; }); + if(iter == DeviceNames.cend()) + throw al::backend_exception{al::backend_error::NoDevice, + "Device name \"{}\" not found", name}; + deviceid = static_cast(std::distance(DeviceNames.cbegin(), iter)); + } + + createStream(deviceid); + mDeviceIdx = deviceid; - mDevice->DeviceName = name; + mDeviceName = name; } bool PortPlayback::reset() { - const PaStreamInfo *streamInfo{Pa_GetStreamInfo(mStream)}; - mDevice->Frequency = static_cast(streamInfo->sampleRate); - mDevice->UpdateSize = mUpdateSize; - - if(mParams.sampleFormat == paInt8) - mDevice->FmtType = DevFmtByte; - else if(mParams.sampleFormat == paUInt8) - mDevice->FmtType = DevFmtUByte; - else if(mParams.sampleFormat == paInt16) - mDevice->FmtType = DevFmtShort; - else if(mParams.sampleFormat == paInt32) - mDevice->FmtType = DevFmtInt; - else if(mParams.sampleFormat == paFloat32) - mDevice->FmtType = DevFmtFloat; - else + if(mStream) { - ERR("Unexpected sample format: 0x%lx\n", mParams.sampleFormat); - return false; + auto err = Pa_CloseStream(mStream); + if(err != paNoError) + ERR("Error closing stream: {}", Pa_GetErrorText(err)); + mStream = nullptr; } - if(mParams.channelCount >= 2) - mDevice->FmtChans = DevFmtStereo; - else if(mParams.channelCount == 1) - mDevice->FmtChans = DevFmtMono; - else + createStream(mDeviceIdx); + + switch(mParams.sampleFormat) { - ERR("Unexpected channel count: %u\n", mParams.channelCount); - return false; + case paFloat32: mDevice->FmtType = DevFmtFloat; break; + case paInt32: mDevice->FmtType = DevFmtInt; break; + case paInt16: mDevice->FmtType = DevFmtShort; break; + case paInt8: mDevice->FmtType = DevFmtByte; break; + case paUInt8: mDevice->FmtType = DevFmtUByte; break; + default: + ERR("Unexpected PortAudio sample format: {}", mParams.sampleFormat); + throw al::backend_exception{al::backend_error::NoDevice, "Invalid sample format: {}", + mParams.sampleFormat}; + } + + if(mParams.channelCount != static_cast(mDevice->channelsFromFmt())) + { + if(mParams.channelCount >= 2) + mDevice->FmtChans = DevFmtStereo; + else if(mParams.channelCount == 1) + mDevice->FmtChans = DevFmtMono; + mDevice->mAmbiOrder = 0; } + + const PaStreamInfo *streamInfo{Pa_GetStreamInfo(mStream)}; + mDevice->mSampleRate = static_cast(std::lround(streamInfo->sampleRate)); + mDevice->mUpdateSize = mParams.updateSize; + mDevice->mBufferSize = mDevice->mUpdateSize * 2; + if(streamInfo->outputLatency > 0.0f) + { + const double sampleLatency{streamInfo->outputLatency * streamInfo->sampleRate}; + TRACE("Reported stream latency: {:f} sec ({:f} samples)", streamInfo->outputLatency, + sampleLatency); + mDevice->mBufferSize = static_cast(std::clamp(sampleLatency, + double(mDevice->mBufferSize), double{std::numeric_limits::max()})); + } + setDefaultChannelOrder(); return true; @@ -217,33 +277,24 @@ bool PortPlayback::reset() void PortPlayback::start() { - const PaError err{Pa_StartStream(mStream)}; - if(err == paNoError) - throw al::backend_exception{al::backend_error::DeviceError, "Failed to start playback: %s", + if(const PaError err{Pa_StartStream(mStream)}; err != paNoError) + throw al::backend_exception{al::backend_error::DeviceError, "Failed to start playback: {}", Pa_GetErrorText(err)}; } void PortPlayback::stop() { - PaError err{Pa_StopStream(mStream)}; - if(err != paNoError) - ERR("Error stopping stream: %s\n", Pa_GetErrorText(err)); + if(PaError err{Pa_StopStream(mStream)}; err != paNoError) + ERR("Error stopping stream: {}", Pa_GetErrorText(err)); } struct PortCapture final : public BackendBase { - PortCapture(DeviceBase *device) noexcept : BackendBase{device} { } + explicit PortCapture(DeviceBase *device) noexcept : BackendBase{device} { } ~PortCapture() override; int readCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackFlags statusFlags) noexcept; - static int readCallbackC(const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, - const PaStreamCallbackFlags statusFlags, void *userData) noexcept - { - return static_cast(userData)->readCallback(inputBuffer, outputBuffer, - framesPerBuffer, timeInfo, statusFlags); - } + const PaStreamCallbackTimeInfo *timeInfo, const PaStreamCallbackFlags statusFlags) const noexcept; void open(std::string_view name) override; void start() override; @@ -252,47 +303,59 @@ struct PortCapture final : public BackendBase { uint availableSamples() override; PaStream *mStream{nullptr}; - PaStreamParameters mParams; + PaStreamParameters mParams{}; RingBufferPtr mRing{nullptr}; - - DEF_NEWDEL(PortCapture) }; PortCapture::~PortCapture() { PaError err{mStream ? Pa_CloseStream(mStream) : paNoError}; if(err != paNoError) - ERR("Error closing stream: %s\n", Pa_GetErrorText(err)); + ERR("Error closing stream: {}", Pa_GetErrorText(err)); mStream = nullptr; } int PortCapture::readCallback(const void *inputBuffer, void*, unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo*, const PaStreamCallbackFlags) noexcept + const PaStreamCallbackTimeInfo*, const PaStreamCallbackFlags) const noexcept { - mRing->write(inputBuffer, framesPerBuffer); + std::ignore = mRing->write(inputBuffer, framesPerBuffer); return 0; } void PortCapture::open(std::string_view name) { + if(DeviceNames.empty()) + EnumerateDevices(); + + int deviceid{}; if(name.empty()) - name = pa_device; - else if(name != pa_device) - throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", - static_cast(name.length()), name.data()}; + { + if(auto devidopt = ConfigValueInt({}, "port", "capture")) + deviceid = *devidopt; + if(deviceid < 0 || static_cast(deviceid) >= DeviceNames.size()) + deviceid = Pa_GetDefaultInputDevice(); + name = DeviceNames.at(static_cast(deviceid)).mName; + } + else + { + auto iter = std::find_if(DeviceNames.cbegin(), DeviceNames.cend(), + [name](const DeviceEntry &entry) + { return entry.mCaptureChannels > 0 && name == entry.mName; }); + if(iter == DeviceNames.cend()) + throw al::backend_exception{al::backend_error::NoDevice, + "Device name \"{}\" not found", name}; + deviceid = static_cast(std::distance(DeviceNames.cbegin(), iter)); + } - uint samples{mDevice->BufferSize}; - samples = maxu(samples, 100 * mDevice->Frequency / 1000); - uint frame_size{mDevice->frameSizeFromFmt()}; + const uint samples{std::max(mDevice->mBufferSize, mDevice->mSampleRate/10u)}; + const uint frame_size{mDevice->frameSizeFromFmt()}; mRing = RingBuffer::Create(samples, frame_size, false); - auto devidopt = ConfigValueInt(nullptr, "port", "capture"); - if(devidopt && *devidopt >= 0) mParams.device = *devidopt; - else mParams.device = Pa_GetDefaultOutputDevice(); + mParams.device = deviceid; mParams.suggestedLatency = 0.0f; mParams.hostApiSpecificStreamInfo = nullptr; @@ -315,34 +378,39 @@ void PortCapture::open(std::string_view name) break; case DevFmtUInt: case DevFmtUShort: - throw al::backend_exception{al::backend_error::DeviceError, "%s samples not supported", + throw al::backend_exception{al::backend_error::DeviceError, "{} samples not supported", DevFmtTypeString(mDevice->FmtType)}; } mParams.channelCount = static_cast(mDevice->channelsFromFmt()); - PaError err{Pa_OpenStream(&mStream, &mParams, nullptr, mDevice->Frequency, - paFramesPerBufferUnspecified, paNoFlag, &PortCapture::readCallbackC, this)}; + static constexpr auto readCallback = [](const void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, + const PaStreamCallbackFlags statusFlags, void *userData) noexcept + { + return static_cast(userData)->readCallback(inputBuffer, outputBuffer, + framesPerBuffer, timeInfo, statusFlags); + }; + PaError err{Pa_OpenStream(&mStream, &mParams, nullptr, mDevice->mSampleRate, + paFramesPerBufferUnspecified, paNoFlag, readCallback, this)}; if(err != paNoError) - throw al::backend_exception{al::backend_error::NoDevice, "Failed to open stream: %s", + throw al::backend_exception{al::backend_error::NoDevice, "Failed to open stream: {}", Pa_GetErrorText(err)}; - mDevice->DeviceName = name; + mDeviceName = name; } void PortCapture::start() { - const PaError err{Pa_StartStream(mStream)}; - if(err != paNoError) + if(const PaError err{Pa_StartStream(mStream)}; err != paNoError) throw al::backend_exception{al::backend_error::DeviceError, - "Failed to start recording: %s", Pa_GetErrorText(err)}; + "Failed to start recording: {}", Pa_GetErrorText(err)}; } void PortCapture::stop() { - PaError err{Pa_StopStream(mStream)}; - if(err != paNoError) - ERR("Error stopping stream: %s\n", Pa_GetErrorText(err)); + if(PaError err{Pa_StopStream(mStream)}; err != paNoError) + ERR("Error stopping stream: {}", Pa_GetErrorText(err)); } @@ -350,16 +418,14 @@ uint PortCapture::availableSamples() { return static_cast(mRing->readSpace()); } void PortCapture::captureSamples(std::byte *buffer, uint samples) -{ mRing->read(buffer, samples); } +{ std::ignore = mRing->read(buffer, samples); } } // namespace bool PortBackendFactory::init() { - PaError err; - -#ifdef HAVE_DYNLOAD +#if HAVE_DYNLOAD if(!pa_handle) { #ifdef _WIN32 @@ -377,7 +443,7 @@ bool PortBackendFactory::init() return false; #define LOAD_FUNC(f) do { \ - p##f = al::bit_cast(GetSymbol(pa_handle, #f)); \ + p##f = reinterpret_cast(GetSymbol(pa_handle, #f)); \ if(p##f == nullptr) \ { \ CloseLib(pa_handle); \ @@ -392,23 +458,27 @@ bool PortBackendFactory::init() LOAD_FUNC(Pa_StopStream); LOAD_FUNC(Pa_OpenStream); LOAD_FUNC(Pa_CloseStream); + LOAD_FUNC(Pa_GetDeviceCount); + LOAD_FUNC(Pa_GetDeviceInfo); LOAD_FUNC(Pa_GetDefaultOutputDevice); LOAD_FUNC(Pa_GetDefaultInputDevice); LOAD_FUNC(Pa_GetStreamInfo); #undef LOAD_FUNC - if((err=Pa_Initialize()) != paNoError) + const PaError err{Pa_Initialize()}; + if(err != paNoError) { - ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err)); + ERR("Pa_Initialize() returned an error: {}", Pa_GetErrorText(err)); CloseLib(pa_handle); pa_handle = nullptr; return false; } } #else - if((err=Pa_Initialize()) != paNoError) + const PaError err{Pa_Initialize()}; + if(err != paNoError) { - ERR("Pa_Initialize() returned an error: %s\n", Pa_GetErrorText(err)); + ERR("Pa_Initialize() returned an error: {}", Pa_GetErrorText(err)); return false; } #endif @@ -418,18 +488,52 @@ bool PortBackendFactory::init() bool PortBackendFactory::querySupport(BackendType type) { return (type == BackendType::Playback || type == BackendType::Capture); } -std::string PortBackendFactory::probe(BackendType type) +auto PortBackendFactory::enumerate(BackendType type) -> std::vector { - std::string outnames; + std::vector devices; + + EnumerateDevices(); + int defaultid{-1}; switch(type) { case BackendType::Playback: + defaultid = Pa_GetDefaultOutputDevice(); + if(auto devidopt = ConfigValueInt({}, "port", "device"); devidopt && *devidopt >= 0 + && static_cast(*devidopt) < DeviceNames.size()) + defaultid = *devidopt; + + for(size_t i{0};i < DeviceNames.size();++i) + { + if(DeviceNames[i].mPlaybackChannels > 0) + { + if(defaultid >= 0 && static_cast(defaultid) == i) + devices.emplace(devices.cbegin(), DeviceNames[i].mName); + else + devices.emplace_back(DeviceNames[i].mName); + } + } + break; + case BackendType::Capture: - /* Includes null char. */ - outnames.append(pa_device, sizeof(pa_device)); + defaultid = Pa_GetDefaultInputDevice(); + if(auto devidopt = ConfigValueInt({}, "port", "capture"); devidopt && *devidopt >= 0 + && static_cast(*devidopt) < DeviceNames.size()) + defaultid = *devidopt; + + for(size_t i{0};i < DeviceNames.size();++i) + { + if(DeviceNames[i].mCaptureChannels > 0) + { + if(defaultid >= 0 && static_cast(defaultid) == i) + devices.emplace(devices.cbegin(), DeviceNames[i].mName); + else + devices.emplace_back(DeviceNames[i].mName); + } + } break; } - return outnames; + + return devices; } BackendPtr PortBackendFactory::createBackend(DeviceBase *device, BackendType type) diff --git a/3rdparty/openal/alc/backends/portaudio.h b/3rdparty/openal/alc/backends/portaudio.h deleted file mode 100644 index c35ccff2d625..000000000000 --- a/3rdparty/openal/alc/backends/portaudio.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef BACKENDS_PORTAUDIO_H -#define BACKENDS_PORTAUDIO_H - -#include "base.h" - -struct PortBackendFactory final : public BackendFactory { -public: - bool init() override; - - bool querySupport(BackendType type) override; - - std::string probe(BackendType type) override; - - BackendPtr createBackend(DeviceBase *device, BackendType type) override; - - static BackendFactory &getFactory(); -}; - -#endif /* BACKENDS_PORTAUDIO_H */ diff --git a/3rdparty/openal/alc/backends/portaudio.hpp b/3rdparty/openal/alc/backends/portaudio.hpp new file mode 100644 index 000000000000..69c78ea9df7c --- /dev/null +++ b/3rdparty/openal/alc/backends/portaudio.hpp @@ -0,0 +1,19 @@ +#ifndef BACKENDS_PORTAUDIO_HPP +#define BACKENDS_PORTAUDIO_HPP + +#include "base.h" + +struct PortBackendFactory final : public BackendFactory { +public: + auto init() -> bool final; + + auto querySupport(BackendType type) -> bool final; + + auto enumerate(BackendType type) -> std::vector final; + + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; + + static auto getFactory() -> BackendFactory&; +}; + +#endif /* BACKENDS_PORTAUDIO_HPP */ diff --git a/3rdparty/openal/alc/backends/pulseaudio.cpp b/3rdparty/openal/alc/backends/pulseaudio.cpp index e2cea8a8e596..aac014157518 100644 --- a/3rdparty/openal/alc/backends/pulseaudio.cpp +++ b/3rdparty/openal/alc/backends/pulseaudio.cpp @@ -28,27 +28,28 @@ #include #include #include +#include +#include +#include #include #include #include #include -#include -#include #include +#include #include #include #include -#include "albit.h" #include "alc/alconfig.h" -#include "alc/events.h" -#include "almalloc.h" #include "alnumeric.h" #include "alspan.h" +#include "base.h" #include "core/devformat.h" #include "core/device.h" #include "core/logging.h" #include "dynload.h" +#include "fmt/core.h" #include "opthelpers.h" #include "strutils.h" @@ -57,9 +58,10 @@ namespace { +using namespace std::string_view_literals; using uint = unsigned int; -#ifdef HAVE_DYNLOAD +#if HAVE_DYNLOAD #define PULSE_FUNCS(MAGIC) \ MAGIC(pa_context_new); \ MAGIC(pa_context_unref); \ @@ -71,8 +73,10 @@ using uint = unsigned int; MAGIC(pa_context_errno); \ MAGIC(pa_context_connect); \ MAGIC(pa_context_get_server_info); \ + MAGIC(pa_context_get_sink_info_by_index); \ MAGIC(pa_context_get_sink_info_by_name); \ MAGIC(pa_context_get_sink_info_list); \ + MAGIC(pa_context_get_source_info_by_index); \ MAGIC(pa_context_get_source_info_by_name); \ MAGIC(pa_context_get_source_info_list); \ MAGIC(pa_stream_new); \ @@ -144,8 +148,10 @@ PULSE_FUNCS(MAKE_FUNC) #define pa_context_errno ppa_context_errno #define pa_context_connect ppa_context_connect #define pa_context_get_server_info ppa_context_get_server_info +#define pa_context_get_sink_info_by_index ppa_context_get_sink_info_by_index #define pa_context_get_sink_info_by_name ppa_context_get_sink_info_by_name #define pa_context_get_sink_info_list ppa_context_get_sink_info_list +#define pa_context_get_source_info_by_index ppa_context_get_source_info_by_index #define pa_context_get_source_info_by_name ppa_context_get_source_info_by_name #define pa_context_get_source_info_list ppa_context_get_source_info_list #define pa_stream_new ppa_stream_new @@ -251,7 +257,7 @@ constexpr pa_channel_map MonoChanMap{ }; -/* *grumble* Don't use enums for bitflags. */ +/* NOLINTBEGIN(*EnumCastOutOfRange) *grumble* Don't use enums for bitflags. */ constexpr pa_stream_flags_t operator|(pa_stream_flags_t lhs, pa_stream_flags_t rhs) { return pa_stream_flags_t(lhs | al::to_underlying(rhs)); } constexpr pa_stream_flags_t& operator|=(pa_stream_flags_t &lhs, pa_stream_flags_t rhs) @@ -277,11 +283,13 @@ constexpr pa_context_flags_t& operator|=(pa_context_flags_t &lhs, pa_context_fla constexpr pa_subscription_mask_t operator|(pa_subscription_mask_t lhs, pa_subscription_mask_t rhs) { return pa_subscription_mask_t(lhs | al::to_underlying(rhs)); } +/* NOLINTEND(*EnumCastOutOfRange) */ struct DevMap { std::string name; std::string device_name; + uint32_t index{}; }; bool checkName(const al::span list, const std::string &name) @@ -293,6 +301,9 @@ bool checkName(const al::span list, const std::string &name) std::vector PlaybackDevices; std::vector CaptureDevices; +std::string DefaultPlaybackDevName; +std::string DefaultCaptureDevName; + /* Global flags and properties */ pa_context_flags_t pulse_ctx_flags; @@ -321,11 +332,12 @@ class PulseMainloop { explicit operator bool() const noexcept { return mLoop != nullptr; } + [[nodiscard]] auto start() const { return pa_threaded_mainloop_start(mLoop); } auto stop() const { return pa_threaded_mainloop_stop(mLoop); } - auto getApi() const { return pa_threaded_mainloop_get_api(mLoop); } - auto getContext() const noexcept { return mContext; } + [[nodiscard]] auto getApi() const { return pa_threaded_mainloop_get_api(mLoop); } + [[nodiscard]] auto getContext() const noexcept { return mContext; } auto lock() const { return pa_threaded_mainloop_lock(mLoop); } auto unlock() const { return pa_threaded_mainloop_unlock(mLoop); } @@ -335,14 +347,40 @@ class PulseMainloop { static auto Create() { return PulseMainloop{pa_threaded_mainloop_new()}; } - void streamSuccessCallback(pa_stream*, int) noexcept { signal(); } + void streamSuccessCallback(pa_stream*, int) const noexcept { signal(); } static void streamSuccessCallbackC(pa_stream *stream, int success, void *pdata) noexcept { static_cast(pdata)->streamSuccessCallback(stream, success); } void close(pa_stream *stream=nullptr); - void deviceSinkCallback(pa_context*, const pa_sink_info *info, int eol) noexcept + void updateDefaultDevice(pa_context*, const pa_server_info *info) const + { + auto default_sink = info->default_sink_name ? std::string_view{info->default_sink_name} + : std::string_view{}; + auto default_src = info->default_source_name ? std::string_view{info->default_source_name} + : std::string_view{}; + + if(default_sink != DefaultPlaybackDevName) + { + TRACE("Default playback device: {}", default_sink); + DefaultPlaybackDevName = default_sink; + + const auto msg = fmt::format("Default playback device changed: {}", default_sink); + alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Playback, msg); + } + if(default_src != DefaultCaptureDevName) + { + TRACE("Default capture device: {}", default_src); + DefaultCaptureDevName = default_src; + + const auto msg = fmt::format("Default capture device changed: {}", default_src); + alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Capture, msg); + } + signal(); + } + + void deviceSinkCallback(pa_context*, const pa_sink_info *info, int eol) const noexcept { if(eol) { @@ -359,21 +397,21 @@ class PulseMainloop { /* Make sure the display name (description) is unique. Append a number * counter as needed. */ - int count{1}; - std::string newname{info->description}; + auto count = 1; + auto newname = std::string{info->description}; while(checkName(PlaybackDevices, newname)) - { - newname = info->description; - newname += " #"; - newname += std::to_string(++count); - } - PlaybackDevices.emplace_back(DevMap{std::move(newname), info->name}); - DevMap &newentry = PlaybackDevices.back(); + newname = fmt::format("{} #{}", info->description, ++count); - TRACE("Got device \"%s\", \"%s\"\n", newentry.name.c_str(), newentry.device_name.c_str()); + const auto &newentry = PlaybackDevices.emplace_back(DevMap{std::move(newname), + info->name, info->index}); + TRACE("Got device \"{}\", \"{}\" ({})", newentry.name, newentry.device_name, + newentry.index); + + const auto msg = fmt::format("Device added: {}", newentry.device_name); + alc::Event(alc::EventType::DeviceAdded, alc::DeviceType::Playback, msg); } - void deviceSourceCallback(pa_context*, const pa_source_info *info, int eol) noexcept + void deviceSourceCallback(pa_context*, const pa_source_info *info, int eol) const noexcept { if(eol) { @@ -390,22 +428,77 @@ class PulseMainloop { /* Make sure the display name (description) is unique. Append a number * counter as needed. */ - int count{1}; - std::string newname{info->description}; + auto count = 1; + auto newname = std::string{info->description}; while(checkName(CaptureDevices, newname)) + newname = fmt::format("{} #{}", info->description, ++count); + + const auto &newentry = CaptureDevices.emplace_back(DevMap{std::move(newname), info->name, + info->index}); + TRACE("Got device \"{}\", \"{}\" ({})", newentry.name, newentry.device_name, + newentry.index); + + const auto msg = fmt::format("Device added: {}", newentry.device_name); + alc::Event(alc::EventType::DeviceAdded, alc::DeviceType::Capture, msg); + } + + void eventCallback(pa_context *context, pa_subscription_event_type_t t, uint32_t idx) noexcept + { + const auto eventFacility = (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK); + const auto eventType = (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK); + + if(eventFacility == PA_SUBSCRIPTION_EVENT_SERVER + && eventType == PA_SUBSCRIPTION_EVENT_CHANGE) { - newname = info->description; - newname += " #"; - newname += std::to_string(++count); + static constexpr auto server_cb = [](pa_context *ctx, const pa_server_info *info, + void *pdata) noexcept + { return static_cast(pdata)->updateDefaultDevice(ctx, info); }; + auto *op = pa_context_get_server_info(context, server_cb, this); + if(op) pa_operation_unref(op); } - CaptureDevices.emplace_back(DevMap{std::move(newname), info->name}); - DevMap &newentry = CaptureDevices.back(); - TRACE("Got device \"%s\", \"%s\"\n", newentry.name.c_str(), newentry.device_name.c_str()); - } + if(eventFacility != PA_SUBSCRIPTION_EVENT_SINK + && eventFacility != PA_SUBSCRIPTION_EVENT_SOURCE) + return; - void probePlaybackDevices(); - void probeCaptureDevices(); + const auto devtype = (eventFacility == PA_SUBSCRIPTION_EVENT_SINK) + ? alc::DeviceType::Playback : alc::DeviceType::Capture; + + if(eventType == PA_SUBSCRIPTION_EVENT_NEW) + { + if(eventFacility == PA_SUBSCRIPTION_EVENT_SINK) + { + static constexpr auto devcallback = [](pa_context *ctx, const pa_sink_info *info, + int eol, void *pdata) noexcept + { return static_cast(pdata)->deviceSinkCallback(ctx, info, eol); }; + auto *op = pa_context_get_sink_info_by_index(context, idx, devcallback, this); + if(op) pa_operation_unref(op); + } + else + { + static constexpr auto devcallback = [](pa_context *ctx, const pa_source_info *info, + int eol, void *pdata) noexcept + { return static_cast(pdata)->deviceSourceCallback(ctx,info,eol); }; + auto *op = pa_context_get_source_info_by_index(context, idx, devcallback, this); + if(op) pa_operation_unref(op); + } + } + else if(eventType == PA_SUBSCRIPTION_EVENT_REMOVE) + { + auto find_index = [idx](const DevMap &entry) noexcept { return entry.index == idx; }; + + auto &devlist = (eventFacility == PA_SUBSCRIPTION_EVENT_SINK) + ? PlaybackDevices : CaptureDevices; + auto iter = std::find_if(devlist.cbegin(), devlist.cend(), find_index); + if(iter != devlist.cend()) + { + devlist.erase(iter); + + const auto msg = fmt::format("Device removed: {}", idx); + alc::Event(alc::EventType::DeviceRemoved, devtype, msg); + } + } + } friend struct MainloopUniqueLock; }; @@ -420,7 +513,7 @@ struct MainloopUniqueLock : public std::unique_lock { auto wait(Predicate done_waiting) const -> void { while(!done_waiting()) wait(); } - void waitForOperation(pa_operation *op) + void waitForOperation(pa_operation *op) const { if(op) { @@ -432,36 +525,38 @@ struct MainloopUniqueLock : public std::unique_lock { void setEventHandler() { - pa_operation *op{pa_context_subscribe(mutex()->mContext, - PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE, - [](pa_context*, int, void *pdata) noexcept - { static_cast(pdata)->signal(); }, - mutex())}; + auto *context = mutex()->mContext; + + /* Watch for device added/removed and server changed events. */ + static constexpr auto submask = PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE + | PA_SUBSCRIPTION_MASK_SERVER; + static constexpr auto do_signal = [](pa_context*, int, void *pdata) noexcept + { static_cast(pdata)->signal(); }; + auto *op = pa_context_subscribe(context, submask, do_signal, mutex()); waitForOperation(op); - /* Watch for device added/removed events. - * - * TODO: Also track the "default" device, in as much as PulseAudio has - * the concept of a default device (whatever device is opened when not - * specifying a specific sink or source name). There doesn't seem to be - * an event for this. - */ - auto handler = [](pa_context*, pa_subscription_event_type_t t, uint32_t, void*) noexcept - { - const auto eventFacility = (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK); - if(eventFacility == PA_SUBSCRIPTION_EVENT_SINK - || eventFacility == PA_SUBSCRIPTION_EVENT_SOURCE) - { - const auto deviceType = (eventFacility == PA_SUBSCRIPTION_EVENT_SINK) - ? alc::DeviceType::Playback : alc::DeviceType::Capture; - const auto eventType = (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK); - if(eventType == PA_SUBSCRIPTION_EVENT_NEW) - alc::Event(alc::EventType::DeviceAdded, deviceType, "Device added"); - else if(eventType == PA_SUBSCRIPTION_EVENT_REMOVE) - alc::Event(alc::EventType::DeviceRemoved, deviceType, "Device removed"); - } - }; - pa_context_set_subscribe_callback(mutex()->mContext, handler, nullptr); + static constexpr auto handler = [](pa_context *ctx, pa_subscription_event_type_t t, + uint32_t index, void *pdata) noexcept + { return static_cast(pdata)->eventCallback(ctx, t, index); }; + pa_context_set_subscribe_callback(context, handler, mutex()); + + /* Fill in the initial device lists, and get the defaults. */ + auto sink_callback = [](pa_context *ctx, const pa_sink_info *info, int eol, void *pdata) noexcept + { return static_cast(pdata)->deviceSinkCallback(ctx, info, eol); }; + + auto src_callback = [](pa_context *ctx, const pa_source_info *info, int eol, void *pdata) noexcept + { return static_cast(pdata)->deviceSourceCallback(ctx, info, eol); }; + + auto server_callback = [](pa_context *ctx, const pa_server_info *info, void *pdata) noexcept + { return static_cast(pdata)->updateDefaultDevice(ctx, info); }; + + auto *sinkop = pa_context_get_sink_info_list(context, sink_callback, mutex()); + auto *srcop = pa_context_get_source_info_list(context, src_callback, mutex()); + auto *serverop = pa_context_get_server_info(context, server_callback, mutex()); + + waitForOperation(sinkop); + waitForOperation(srcop); + waitForOperation(serverop); } @@ -482,6 +577,13 @@ struct MainloopUniqueLock : public std::unique_lock { void connectContext(); pa_stream *connectStream(const char *device_name, pa_stream_flags_t flags, pa_buffer_attr *attr, pa_sample_spec *spec, pa_channel_map *chanmap, BackendType type); + + pa_stream *connectStream(const std::string &device_name, pa_stream_flags_t flags, + pa_buffer_attr *attr, pa_sample_spec *spec, pa_channel_map *chanmap, BackendType type) + { + return connectStream(device_name.empty() ? nullptr : device_name.c_str(), flags, attr, + spec, chanmap, type); + } }; using MainloopLockGuard = std::lock_guard; @@ -489,7 +591,7 @@ PulseMainloop::~PulseMainloop() { if(mContext) { - MainloopUniqueLock _{*this}; + MainloopUniqueLock looplock{*this}; pa_context_disconnect(mContext); pa_context_unref(mContext); } @@ -510,21 +612,20 @@ void MainloopUniqueLock::connectContext() pa_context_set_state_callback(mutex()->mContext, [](pa_context *ctx, void *pdata) noexcept { return static_cast(pdata)->contextStateCallback(ctx); }, this); - int err; - if((err=pa_context_connect(mutex()->mContext, nullptr, pulse_ctx_flags, nullptr)) >= 0) + int err{pa_context_connect(mutex()->mContext, nullptr, pulse_ctx_flags, nullptr)}; + if(err >= 0) { - pa_context_state_t state; - while((state=pa_context_get_state(mutex()->mContext)) != PA_CONTEXT_READY) + wait([&err,this]() { + pa_context_state_t state{pa_context_get_state(mutex()->mContext)}; if(!PA_CONTEXT_IS_GOOD(state)) { err = pa_context_errno(mutex()->mContext); - if(err > 0) err = -err; - break; + if(err > 0) err = -err; + return true; } - - wait(); - } + return state == PA_CONTEXT_READY; + }); } pa_context_set_state_callback(mutex()->mContext, nullptr, nullptr); @@ -532,7 +633,7 @@ void MainloopUniqueLock::connectContext() { pa_context_unref(mutex()->mContext); mutex()->mContext = nullptr; - throw al::backend_exception{al::backend_error::DeviceError, "Context did not connect (%s)", + throw al::backend_exception{al::backend_error::DeviceError, "Context did not connect ({})", pa_strerror(err)}; } } @@ -543,7 +644,7 @@ pa_stream *MainloopUniqueLock::connectStream(const char *device_name, pa_stream_ const char *stream_id{(type==BackendType::Playback) ? "Playback Stream" : "Capture Stream"}; pa_stream *stream{pa_stream_new(mutex()->mContext, stream_id, spec, chanmap)}; if(!stream) - throw al::backend_exception{al::backend_error::OutOfMemory, "pa_stream_new() failed (%s)", + throw al::backend_exception{al::backend_error::OutOfMemory, "pa_stream_new() failed ({})", pa_strerror(pa_context_errno(mutex()->mContext))}; pa_stream_set_state_callback(stream, [](pa_stream *strm, void *pdata) noexcept @@ -555,23 +656,23 @@ pa_stream *MainloopUniqueLock::connectStream(const char *device_name, pa_stream_ if(err < 0) { pa_stream_unref(stream); - throw al::backend_exception{al::backend_error::DeviceError, "%s did not connect (%s)", + throw al::backend_exception{al::backend_error::DeviceError, "%s did not connect ({})", stream_id, pa_strerror(err)}; } - pa_stream_state_t state; - while((state=pa_stream_get_state(stream)) != PA_STREAM_READY) + wait([&err,stream,stream_id,this]() { + pa_stream_state_t state{pa_stream_get_state(stream)}; if(!PA_STREAM_IS_GOOD(state)) { err = pa_context_errno(mutex()->mContext); pa_stream_unref(stream); throw al::backend_exception{al::backend_error::DeviceError, - "%s did not get ready (%s)", stream_id, pa_strerror(err)}; + "{} did not get ready ({})", stream_id, pa_strerror(err)}; } + return state == PA_STREAM_READY; + }); - wait(); - } pa_stream_set_state_callback(stream, nullptr, nullptr); return stream; @@ -582,7 +683,7 @@ void PulseMainloop::close(pa_stream *stream) if(!stream) return; - MainloopUniqueLock _{*this}; + MainloopUniqueLock looplock{*this}; pa_stream_set_state_callback(stream, nullptr, nullptr); pa_stream_set_moved_callback(stream, nullptr, nullptr); pa_stream_set_write_callback(stream, nullptr, nullptr); @@ -592,52 +693,12 @@ void PulseMainloop::close(pa_stream *stream) } -void PulseMainloop::probePlaybackDevices() -{ - PlaybackDevices.clear(); - try { - MainloopUniqueLock plock{*this}; - auto sink_callback = [](pa_context *ctx, const pa_sink_info *info, int eol, void *pdata) noexcept - { return static_cast(pdata)->deviceSinkCallback(ctx, info, eol); }; - - pa_operation *op{pa_context_get_sink_info_by_name(mContext, nullptr, sink_callback, this)}; - plock.waitForOperation(op); - - op = pa_context_get_sink_info_list(mContext, sink_callback, this); - plock.waitForOperation(op); - } - catch(std::exception &e) { - ERR("Error enumerating devices: %s\n", e.what()); - } -} - -void PulseMainloop::probeCaptureDevices() -{ - CaptureDevices.clear(); - try { - MainloopUniqueLock plock{*this}; - auto src_callback = [](pa_context *ctx, const pa_source_info *info, int eol, void *pdata) noexcept - { return static_cast(pdata)->deviceSourceCallback(ctx, info, eol); }; - - pa_operation *op{pa_context_get_source_info_by_name(mContext, nullptr, src_callback, - this)}; - plock.waitForOperation(op); - - op = pa_context_get_source_info_list(mContext, src_callback, this); - plock.waitForOperation(op); - } - catch(std::exception &e) { - ERR("Error enumerating devices: %s\n", e.what()); - } -} - - /* Used for initial connection test and enumeration. */ PulseMainloop gGlobalMainloop; struct PulsePlayback final : public BackendBase { - PulsePlayback(DeviceBase *device) noexcept : BackendBase{device} { } + explicit PulsePlayback(DeviceBase *device) noexcept : BackendBase{device} { } ~PulsePlayback() override; void bufferAttrCallback(pa_stream *stream) noexcept; @@ -655,17 +716,15 @@ struct PulsePlayback final : public BackendBase { PulseMainloop mMainloop; - std::optional mDeviceName{std::nullopt}; + std::optional mDeviceId{std::nullopt}; bool mIs51Rear{false}; - pa_buffer_attr mAttr; - pa_sample_spec mSpec; + pa_buffer_attr mAttr{}; + pa_sample_spec mSpec{}; pa_stream *mStream{nullptr}; uint mFrameSize{0u}; - - DEF_NEWDEL(PulsePlayback) }; PulsePlayback::~PulsePlayback() @@ -680,14 +739,14 @@ void PulsePlayback::bufferAttrCallback(pa_stream *stream) noexcept * leaving it alone means ALC_REFRESH will be off. */ mAttr = *(pa_stream_get_buffer_attr(stream)); - TRACE("minreq=%d, tlength=%d, prebuf=%d\n", mAttr.minreq, mAttr.tlength, mAttr.prebuf); + TRACE("minreq={}, tlength={}, prebuf={}", mAttr.minreq, mAttr.tlength, mAttr.prebuf); } void PulsePlayback::streamStateCallback(pa_stream *stream) noexcept { if(pa_stream_get_state(stream) == PA_STREAM_FAILED) { - ERR("Received stream failure!\n"); + ERR("Received stream failure!"); mDevice->handleDisconnect("Playback stream failure"); } mMainloop.signal(); @@ -706,14 +765,14 @@ void PulsePlayback::streamWriteCallback(pa_stream *stream, size_t nbytes) noexce free_func = pa_xfree; } else - buflen = minz(buflen, nbytes); + buflen = std::min(buflen, nbytes); nbytes -= buflen; mDevice->renderSamples(buf, static_cast(buflen/mFrameSize), mSpec.channels); int ret{pa_stream_write(stream, buf, buflen, free_func, 0, PA_SEEK_RELATIVE)}; if(ret != PA_OK) UNLIKELY - ERR("Failed to write to stream: %d, %s\n", ret, pa_strerror(ret)); + ERR("Failed to write to stream: {}, {}", ret, pa_strerror(ret)); } while(nbytes > 0); } @@ -754,13 +813,13 @@ void PulsePlayback::sinkInfoCallback(pa_context*, const pa_sink_info *info, int else { mIs51Rear = false; - char chanmap_str[PA_CHANNEL_MAP_SNPRINT_MAX]{}; - pa_channel_map_snprint(chanmap_str, sizeof(chanmap_str), &info->channel_map); - WARN("Failed to find format for channel map:\n %s\n", chanmap_str); + std::array chanmap_str{}; + pa_channel_map_snprint(chanmap_str.data(), chanmap_str.size(), &info->channel_map); + WARN("Failed to find format for channel map:\n {}", chanmap_str.data()); } if(info->active_port) - TRACE("Active port: %s (%s)\n", info->active_port->name, info->active_port->description); + TRACE("Active port: {} ({})", info->active_port->name, info->active_port->description); mDevice->Flags.set(DirectEar, (info->active_port && strcmp(info->active_port->name, "analog-output-headphones") == 0)); } @@ -772,35 +831,37 @@ void PulsePlayback::sinkNameCallback(pa_context*, const pa_sink_info *info, int mMainloop.signal(); return; } - mDevice->DeviceName = info->description; + mDeviceName = info->description; } void PulsePlayback::streamMovedCallback(pa_stream *stream) noexcept { - mDeviceName = pa_stream_get_device_name(stream); - TRACE("Stream moved to %s\n", mDeviceName->c_str()); + mDeviceId = pa_stream_get_device_name(stream); + TRACE("Stream moved to {}", *mDeviceId); } void PulsePlayback::open(std::string_view name) { mMainloop = PulseMainloop::Create(); - mMainloop.start(); + if(mMainloop.start() != 0) + throw al::backend_exception{al::backend_error::DeviceError, + "Failed to start device mainloop"}; - const char *pulse_name{nullptr}; - const char *dev_name{nullptr}; + auto pulse_name = std::string{}; if(!name.empty()) { - if(PlaybackDevices.empty()) - mMainloop.probePlaybackDevices(); + auto match_name = [name](const DevMap &entry) -> bool + { return entry.name == name || entry.device_name == name; }; - auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(), - [name](const DevMap &entry) -> bool { return entry.name == name; }); + auto plock = MainloopUniqueLock{gGlobalMainloop}; + auto iter = std::find_if(PlaybackDevices.cbegin(), PlaybackDevices.cend(), match_name); if(iter == PlaybackDevices.cend()) throw al::backend_exception{al::backend_error::NoDevice, - "Device name \"%.*s\" not found", static_cast(name.length()), name.data()}; - pulse_name = iter->device_name.c_str(); - dev_name = iter->name.c_str(); + "Device name \"{}\" not found", name}; + + pulse_name = iter->device_name; + mDeviceName = iter->name; } MainloopUniqueLock plock{mMainloop}; @@ -808,7 +869,7 @@ void PulsePlayback::open(std::string_view name) pa_stream_flags_t flags{PA_STREAM_START_CORKED | PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE | PA_STREAM_FIX_CHANNELS}; - if(!GetConfigValueBool(nullptr, "pulse", "allow-moves", true)) + if(!GetConfigValueBool({}, "pulse", "allow-moves", true)) flags |= PA_STREAM_DONT_MOVE; pa_sample_spec spec{}; @@ -816,37 +877,38 @@ void PulsePlayback::open(std::string_view name) spec.rate = 44100; spec.channels = 2; - if(!pulse_name) + if(pulse_name.empty()) { static const auto defname = al::getenv("ALSOFT_PULSE_DEFAULT"); - if(defname) pulse_name = defname->c_str(); + if(defname) pulse_name = *defname; } - TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)"); + TRACE("Connecting to \"{}\"", pulse_name.empty() ? "(default)"sv:std::string_view{pulse_name}); mStream = plock.connectStream(pulse_name, flags, nullptr, &spec, nullptr, BackendType::Playback); - pa_stream_set_moved_callback(mStream, [](pa_stream *stream, void *pdata) noexcept - { return static_cast(pdata)->streamMovedCallback(stream); }, this); + static constexpr auto move_callback = [](pa_stream *stream, void *pdata) noexcept + { return static_cast(pdata)->streamMovedCallback(stream); }; + pa_stream_set_moved_callback(mStream, move_callback, this); mFrameSize = static_cast(pa_frame_size(pa_stream_get_sample_spec(mStream))); - if(pulse_name) mDeviceName.emplace(pulse_name); - else mDeviceName.reset(); - if(!dev_name) + if(!pulse_name.empty()) + mDeviceId.emplace(std::move(pulse_name)); + + if(mDeviceName.empty()) { - auto name_callback = [](pa_context *context, const pa_sink_info *info, int eol, void *pdata) noexcept + static constexpr auto name_callback = [](pa_context *context, const pa_sink_info *info, + int eol, void *pdata) noexcept { return static_cast(pdata)->sinkNameCallback(context, info, eol); }; pa_operation *op{pa_context_get_sink_info_by_name(mMainloop.getContext(), pa_stream_get_device_name(mStream), name_callback, this)}; plock.waitForOperation(op); } - else - mDevice->DeviceName = dev_name; } bool PulsePlayback::reset() { MainloopUniqueLock plock{mMainloop}; - const auto deviceName = mDeviceName ? mDeviceName->c_str() : nullptr; + const auto deviceName = mDeviceId ? mDeviceId->c_str() : nullptr; if(mStream) { @@ -859,17 +921,17 @@ bool PulsePlayback::reset() mStream = nullptr; } - auto info_callback = [](pa_context *context, const pa_sink_info *info, int eol, void *pdata) noexcept + auto info_cb = [](pa_context *context, const pa_sink_info *info, int eol, void *pdata) noexcept { return static_cast(pdata)->sinkInfoCallback(context, info, eol); }; - pa_operation *op{pa_context_get_sink_info_by_name(mMainloop.getContext(), deviceName, - info_callback, this)}; + pa_operation *op{pa_context_get_sink_info_by_name(mMainloop.getContext(), deviceName, info_cb, + this)}; plock.waitForOperation(op); pa_stream_flags_t flags{PA_STREAM_START_CORKED | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_EARLY_REQUESTS}; - if(!GetConfigValueBool(nullptr, "pulse", "allow-moves", true)) + if(!GetConfigValueBool({}, "pulse", "allow-moves", true)) flags |= PA_STREAM_DONT_MOVE; - if(GetConfigValueBool(mDevice->DeviceName.c_str(), "pulse", "adjust-latency", false)) + if(GetConfigValueBool(mDevice->mDeviceName, "pulse", "adjust-latency", false)) { /* ADJUST_LATENCY can't be specified with EARLY_REQUESTS, for some * reason. So if the user wants to adjust the overall device latency, @@ -878,7 +940,7 @@ bool PulsePlayback::reset() flags &= ~PA_STREAM_EARLY_REQUESTS; flags |= PA_STREAM_ADJUST_LATENCY; } - if(GetConfigValueBool(mDevice->DeviceName.c_str(), "pulse", "fix-rate", false) + if(GetConfigValueBool(mDevice->mDeviceName, "pulse", "fix-rate", false) || !mDevice->Flags.test(FrequencyRequest)) flags |= PA_STREAM_FIX_RATE; @@ -907,6 +969,9 @@ bool PulsePlayback::reset() case DevFmtX3D71: chanmap = X71ChanMap; break; + case DevFmtX7144: + mDevice->FmtChans = DevFmtX714; + /*fall-through*/ case DevFmtX714: chanmap = X714ChanMap; break; @@ -937,59 +1002,62 @@ bool PulsePlayback::reset() mSpec.format = PA_SAMPLE_FLOAT32NE; break; } - mSpec.rate = mDevice->Frequency; + mSpec.rate = mDevice->mSampleRate; mSpec.channels = static_cast(mDevice->channelsFromFmt()); if(pa_sample_spec_valid(&mSpec) == 0) throw al::backend_exception{al::backend_error::DeviceError, "Invalid sample spec"}; const auto frame_size = static_cast(pa_frame_size(&mSpec)); mAttr.maxlength = ~0u; - mAttr.tlength = mDevice->BufferSize * frame_size; + mAttr.tlength = mDevice->mBufferSize * frame_size; mAttr.prebuf = 0u; - mAttr.minreq = mDevice->UpdateSize * frame_size; + mAttr.minreq = mDevice->mUpdateSize * frame_size; mAttr.fragsize = ~0u; mStream = plock.connectStream(deviceName, flags, &mAttr, &mSpec, &chanmap, BackendType::Playback); - pa_stream_set_state_callback(mStream, [](pa_stream *stream, void *pdata) noexcept - { return static_cast(pdata)->streamStateCallback(stream); }, this); - pa_stream_set_moved_callback(mStream, [](pa_stream *stream, void *pdata) noexcept - { return static_cast(pdata)->streamMovedCallback(stream); }, this); + constexpr auto state_callback = [](pa_stream *stream, void *pdata) noexcept + { return static_cast(pdata)->streamStateCallback(stream); }; + pa_stream_set_state_callback(mStream, state_callback, this); + + constexpr auto move_callback = [](pa_stream *stream, void *pdata) noexcept + { return static_cast(pdata)->streamMovedCallback(stream); }; + pa_stream_set_moved_callback(mStream, move_callback, this); mSpec = *(pa_stream_get_sample_spec(mStream)); mFrameSize = static_cast(pa_frame_size(&mSpec)); - if(mDevice->Frequency != mSpec.rate) + if(mDevice->mSampleRate != mSpec.rate) { /* Server updated our playback rate, so modify the buffer attribs * accordingly. */ - const auto scale = static_cast(mSpec.rate) / mDevice->Frequency; - const auto perlen = static_cast(clampd(scale*mDevice->UpdateSize + 0.5, 64.0, - 8192.0)); - const auto buflen = static_cast(clampd(scale*mDevice->BufferSize + 0.5, perlen*2, - std::numeric_limits::max()/mFrameSize)); + const auto scale = static_cast(mSpec.rate) / mDevice->mSampleRate; + const auto perlen = std::clamp(std::round(scale*mDevice->mUpdateSize), 64.0, 8192.0); + const auto bufmax = uint{std::numeric_limits::max()} / mFrameSize; + const auto buflen = std::clamp(std::round(scale*mDevice->mBufferSize), perlen*2.0, + static_cast(bufmax)); mAttr.maxlength = ~0u; - mAttr.tlength = buflen * mFrameSize; + mAttr.tlength = static_cast(buflen) * mFrameSize; mAttr.prebuf = 0u; - mAttr.minreq = perlen * mFrameSize; + mAttr.minreq = static_cast(perlen) * mFrameSize; op = pa_stream_set_buffer_attr(mStream, &mAttr, &PulseMainloop::streamSuccessCallbackC, &mMainloop); plock.waitForOperation(op); - mDevice->Frequency = mSpec.rate; + mDevice->mSampleRate = mSpec.rate; } - auto attr_callback = [](pa_stream *stream, void *pdata) noexcept + constexpr auto attr_callback = [](pa_stream *stream, void *pdata) noexcept { return static_cast(pdata)->bufferAttrCallback(stream); }; pa_stream_set_buffer_attr_callback(mStream, attr_callback, this); bufferAttrCallback(mStream); - mDevice->BufferSize = mAttr.tlength / mFrameSize; - mDevice->UpdateSize = mAttr.minreq / mFrameSize; + mDevice->mBufferSize = mAttr.tlength / mFrameSize; + mDevice->mUpdateSize = mAttr.minreq / mFrameSize; return true; } @@ -1008,8 +1076,9 @@ void PulsePlayback::start() pa_stream_write(mStream, buf, todo, pa_xfree, 0, PA_SEEK_RELATIVE); } - pa_stream_set_write_callback(mStream, [](pa_stream *stream, size_t nbytes, void *pdata)noexcept - { return static_cast(pdata)->streamWriteCallback(stream, nbytes); }, this); + constexpr auto stream_write = [](pa_stream *stream, size_t nbytes, void *pdata) noexcept + { return static_cast(pdata)->streamWriteCallback(stream, nbytes); }; + pa_stream_set_write_callback(mStream, stream_write, this); pa_operation *op{pa_stream_cork(mStream, 0, &PulseMainloop::streamSuccessCallbackC, &mMainloop)}; @@ -1029,13 +1098,13 @@ void PulsePlayback::stop() ClockLatency PulsePlayback::getClockLatency() { - ClockLatency ret; - pa_usec_t latency; - int neg, err; + ClockLatency ret{}; + pa_usec_t latency{}; + int neg{}, err{}; { MainloopUniqueLock plock{mMainloop}; - ret.ClockTime = GetDeviceClockTime(mDevice); + ret.ClockTime = mDevice->getClockTime(); err = pa_stream_get_latency(mStream, &latency, &neg); } @@ -1046,8 +1115,8 @@ ClockLatency PulsePlayback::getClockLatency() * server yet. Give a generic value since nothing better is available. */ if(err != -PA_ERR_NODATA) - ERR("Failed to get stream latency: 0x%x\n", err); - latency = mDevice->BufferSize - mDevice->UpdateSize; + ERR("Failed to get stream latency: {:#x}", as_unsigned(err)); + latency = mDevice->mBufferSize - mDevice->mUpdateSize; neg = 0; } else if(neg) UNLIKELY @@ -1059,7 +1128,7 @@ ClockLatency PulsePlayback::getClockLatency() struct PulseCapture final : public BackendBase { - PulseCapture(DeviceBase *device) noexcept : BackendBase{device} { } + explicit PulseCapture(DeviceBase *device) noexcept : BackendBase{device} { } ~PulseCapture() override; void streamStateCallback(pa_stream *stream) noexcept; @@ -1075,7 +1144,7 @@ struct PulseCapture final : public BackendBase { PulseMainloop mMainloop; - std::optional mDeviceName{std::nullopt}; + std::optional mDeviceId{std::nullopt}; al::span mCapBuffer; size_t mHoleLength{0}; @@ -1088,8 +1157,6 @@ struct PulseCapture final : public BackendBase { pa_sample_spec mSpec{}; pa_stream *mStream{nullptr}; - - DEF_NEWDEL(PulseCapture) }; PulseCapture::~PulseCapture() @@ -1100,7 +1167,7 @@ void PulseCapture::streamStateCallback(pa_stream *stream) noexcept { if(pa_stream_get_state(stream) == PA_STREAM_FAILED) { - ERR("Received stream failure!\n"); + ERR("Received stream failure!"); mDevice->handleDisconnect("Capture stream failure"); } mMainloop.signal(); @@ -1113,13 +1180,13 @@ void PulseCapture::sourceNameCallback(pa_context*, const pa_source_info *info, i mMainloop.signal(); return; } - mDevice->DeviceName = info->description; + mDeviceName = info->description; } void PulseCapture::streamMovedCallback(pa_stream *stream) noexcept { - mDeviceName = pa_stream_get_device_name(stream); - TRACE("Stream moved to %s\n", mDeviceName->c_str()); + mDeviceId = pa_stream_get_device_name(stream); + TRACE("Stream moved to {}", *mDeviceId); } @@ -1128,22 +1195,25 @@ void PulseCapture::open(std::string_view name) if(!mMainloop) { mMainloop = PulseMainloop::Create(); - mMainloop.start(); + if(mMainloop.start() != 0) + throw al::backend_exception{al::backend_error::DeviceError, + "Failed to start device mainloop"}; } - const char *pulse_name{nullptr}; + auto pulse_name = std::string{}; if(!name.empty()) { - if(CaptureDevices.empty()) - mMainloop.probeCaptureDevices(); + auto match_name = [name](const DevMap &entry) -> bool + { return entry.name == name || entry.device_name == name; }; - auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(), - [name](const DevMap &entry) -> bool { return entry.name == name; }); + auto plock = MainloopUniqueLock{gGlobalMainloop}; + auto iter = std::find_if(CaptureDevices.cbegin(), CaptureDevices.cend(), match_name); if(iter == CaptureDevices.cend()) throw al::backend_exception{al::backend_error::NoDevice, - "Device name \"%.*s\" not found", static_cast(name.length()), name.data()}; - pulse_name = iter->device_name.c_str(); - mDevice->DeviceName = iter->name; + "Device name \"{}\" not found", name}; + + pulse_name = iter->device_name; + mDeviceName = iter->name; } MainloopUniqueLock plock{mMainloop}; @@ -1152,30 +1222,17 @@ void PulseCapture::open(std::string_view name) pa_channel_map chanmap{}; switch(mDevice->FmtChans) { - case DevFmtMono: - chanmap = MonoChanMap; - break; - case DevFmtStereo: - chanmap = StereoChanMap; - break; - case DevFmtQuad: - chanmap = QuadChanMap; - break; - case DevFmtX51: - chanmap = X51ChanMap; - break; - case DevFmtX61: - chanmap = X61ChanMap; - break; - case DevFmtX71: - chanmap = X71ChanMap; - break; - case DevFmtX714: - chanmap = X714ChanMap; - break; + case DevFmtMono: chanmap = MonoChanMap; break; + case DevFmtStereo: chanmap = StereoChanMap; break; + case DevFmtQuad: chanmap = QuadChanMap; break; + case DevFmtX51: chanmap = X51ChanMap; break; + case DevFmtX61: chanmap = X61ChanMap; break; + case DevFmtX71: chanmap = X71ChanMap; break; + case DevFmtX714: chanmap = X714ChanMap; break; + case DevFmtX7144: case DevFmtX3D71: case DevFmtAmbi3D: - throw al::backend_exception{al::backend_error::DeviceError, "%s capture not supported", + throw al::backend_exception{al::backend_error::DeviceError, "{} capture not supported", DevFmtChannelsString(mDevice->FmtChans)}; } setDefaultWFXChannelOrder(); @@ -1199,39 +1256,44 @@ void PulseCapture::open(std::string_view name) case DevFmtUShort: case DevFmtUInt: throw al::backend_exception{al::backend_error::DeviceError, - "%s capture samples not supported", DevFmtTypeString(mDevice->FmtType)}; + "{} capture samples not supported", DevFmtTypeString(mDevice->FmtType)}; } - mSpec.rate = mDevice->Frequency; + mSpec.rate = mDevice->mSampleRate; mSpec.channels = static_cast(mDevice->channelsFromFmt()); if(pa_sample_spec_valid(&mSpec) == 0) throw al::backend_exception{al::backend_error::DeviceError, "Invalid sample format"}; const auto frame_size = static_cast(pa_frame_size(&mSpec)); - const uint samples{maxu(mDevice->BufferSize, 100 * mDevice->Frequency / 1000)}; + const uint samples{std::max(mDevice->mBufferSize, mDevice->mSampleRate*100u/1000u)}; mAttr.minreq = ~0u; mAttr.prebuf = ~0u; mAttr.maxlength = samples * frame_size; mAttr.tlength = ~0u; - mAttr.fragsize = minu(samples, 50*mDevice->Frequency/1000) * frame_size; + mAttr.fragsize = std::min(samples, mDevice->mSampleRate*50u/1000u) * frame_size; pa_stream_flags_t flags{PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY}; - if(!GetConfigValueBool(nullptr, "pulse", "allow-moves", true)) + if(!GetConfigValueBool({}, "pulse", "allow-moves", true)) flags |= PA_STREAM_DONT_MOVE; - TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)"); + TRACE("Connecting to \"{}\"", pulse_name.empty() ? "(default)"sv:std::string_view{pulse_name}); mStream = plock.connectStream(pulse_name, flags, &mAttr, &mSpec, &chanmap, BackendType::Capture); - pa_stream_set_moved_callback(mStream, [](pa_stream *stream, void *pdata) noexcept - { return static_cast(pdata)->streamMovedCallback(stream); }, this); - pa_stream_set_state_callback(mStream, [](pa_stream *stream, void *pdata) noexcept - { return static_cast(pdata)->streamStateCallback(stream); }, this); + constexpr auto move_callback = [](pa_stream *stream, void *pdata) noexcept + { return static_cast(pdata)->streamMovedCallback(stream); }; + pa_stream_set_moved_callback(mStream, move_callback, this); - if(pulse_name) mDeviceName.emplace(pulse_name); - else mDeviceName.reset(); - if(mDevice->DeviceName.empty()) + constexpr auto state_callback = [](pa_stream *stream, void *pdata) noexcept + { return static_cast(pdata)->streamStateCallback(stream); }; + pa_stream_set_state_callback(mStream, state_callback, this); + + if(!pulse_name.empty()) + mDeviceId.emplace(std::move(pulse_name)); + + if(mDeviceName.empty()) { - auto name_callback = [](pa_context *context, const pa_source_info *info, int eol, void *pdata) noexcept + constexpr auto name_callback = [](pa_context *context, const pa_source_info *info, int eol, + void *pdata) noexcept { return static_cast(pdata)->sourceNameCallback(context, info, eol); }; pa_operation *op{pa_context_get_source_info_by_name(mMainloop.getContext(), pa_stream_get_device_name(mStream), name_callback, this)}; @@ -1267,7 +1329,7 @@ void PulseCapture::captureSamples(std::byte *buffer, uint samples) { if(mHoleLength > 0) UNLIKELY { - const size_t rem{minz(dstbuf.size(), mHoleLength)}; + const size_t rem{std::min(dstbuf.size(), mHoleLength)}; std::fill_n(dstbuf.begin(), rem, mSilentVal); dstbuf = dstbuf.subspan(rem); mHoleLength -= rem; @@ -1276,7 +1338,7 @@ void PulseCapture::captureSamples(std::byte *buffer, uint samples) } if(!mCapBuffer.empty()) { - const size_t rem{minz(dstbuf.size(), mCapBuffer.size())}; + const size_t rem{std::min(dstbuf.size(), mCapBuffer.size())}; std::copy_n(mCapBuffer.begin(), rem, dstbuf.begin()); dstbuf = dstbuf.subspan(rem); mCapBuffer = mCapBuffer.subspan(rem); @@ -1297,15 +1359,15 @@ void PulseCapture::captureSamples(std::byte *buffer, uint samples) const pa_stream_state_t state{pa_stream_get_state(mStream)}; if(!PA_STREAM_IS_GOOD(state)) UNLIKELY { - mDevice->handleDisconnect("Bad capture state: %u", state); + mDevice->handleDisconnect("Bad capture state: {}", al::to_underlying(state)); break; } - const void *capbuf; - size_t caplen; + const void *capbuf{}; + size_t caplen{}; if(pa_stream_peek(mStream, &capbuf, &caplen) < 0) UNLIKELY { - mDevice->handleDisconnect("Failed retrieving capture samples: %s", + mDevice->handleDisconnect("Failed retrieving capture samples: {}", pa_strerror(pa_context_errno(mMainloop.getContext()))); break; } @@ -1324,7 +1386,7 @@ void PulseCapture::captureSamples(std::byte *buffer, uint samples) uint PulseCapture::availableSamples() { - size_t readable{maxz(mCapBuffer.size(), mHoleLength)}; + size_t readable{std::max(mCapBuffer.size(), mHoleLength)}; if(mDevice->Connected.load(std::memory_order_acquire)) { @@ -1333,8 +1395,8 @@ uint PulseCapture::availableSamples() if(static_cast(got) < 0) UNLIKELY { const char *err{pa_strerror(static_cast(got))}; - ERR("pa_stream_readable_size() failed: %s\n", err); - mDevice->handleDisconnect("Failed getting readable size: %s", err); + ERR("pa_stream_readable_size() failed: {}", err); + mDevice->handleDisconnect("Failed getting readable size: {}", err); } else { @@ -1357,19 +1419,19 @@ uint PulseCapture::availableSamples() ClockLatency PulseCapture::getClockLatency() { - ClockLatency ret; - pa_usec_t latency; - int neg, err; + ClockLatency ret{}; + pa_usec_t latency{}; + int neg{}, err{}; { MainloopUniqueLock plock{mMainloop}; - ret.ClockTime = GetDeviceClockTime(mDevice); + ret.ClockTime = mDevice->getClockTime(); err = pa_stream_get_latency(mStream, &latency, &neg); } if(err != 0) UNLIKELY { - ERR("Failed to get stream latency: 0x%x\n", err); + ERR("Failed to get stream latency: {:#x}", as_unsigned(err)); latency = 0; neg = 0; } @@ -1385,12 +1447,9 @@ ClockLatency PulseCapture::getClockLatency() bool PulseBackendFactory::init() { -#ifdef HAVE_DYNLOAD +#if HAVE_DYNLOAD if(!pulse_handle) { - bool ret{true}; - std::string missing_funcs; - #ifdef _WIN32 #define PALIB "libpulse-0.dll" #elif defined(__APPLE__) && defined(__MACH__) @@ -1401,39 +1460,41 @@ bool PulseBackendFactory::init() pulse_handle = LoadLib(PALIB); if(!pulse_handle) { - WARN("Failed to load %s\n", PALIB); + WARN("Failed to load {}", PALIB); return false; } + std::string missing_funcs; #define LOAD_FUNC(x) do { \ - p##x = al::bit_cast(GetSymbol(pulse_handle, #x)); \ - if(!(p##x)) { \ - ret = false; \ - missing_funcs += "\n" #x; \ - } \ + p##x = reinterpret_cast(GetSymbol(pulse_handle, #x)); \ + if(!(p##x)) missing_funcs += "\n" #x; \ } while(0) PULSE_FUNCS(LOAD_FUNC) #undef LOAD_FUNC - if(!ret) + if(!missing_funcs.empty()) { - WARN("Missing expected functions:%s\n", missing_funcs.c_str()); + WARN("Missing expected functions:{}", missing_funcs); CloseLib(pulse_handle); pulse_handle = nullptr; return false; } } -#endif /* HAVE_DYNLOAD */ +#endif pulse_ctx_flags = PA_CONTEXT_NOFLAGS; - if(!GetConfigValueBool(nullptr, "pulse", "spawn-server", false)) + if(!GetConfigValueBool({}, "pulse", "spawn-server", false)) pulse_ctx_flags |= PA_CONTEXT_NOAUTOSPAWN; try { if(!gGlobalMainloop) { gGlobalMainloop = PulseMainloop::Create(); - gGlobalMainloop.start(); + if(gGlobalMainloop.start() != 0) + { + gGlobalMainloop = nullptr; + return false; + } } MainloopUniqueLock plock{gGlobalMainloop}; @@ -1449,28 +1510,36 @@ bool PulseBackendFactory::init() bool PulseBackendFactory::querySupport(BackendType type) { return type == BackendType::Playback || type == BackendType::Capture; } -std::string PulseBackendFactory::probe(BackendType type) +auto PulseBackendFactory::enumerate(BackendType type) -> std::vector { - std::string outnames; + std::vector outnames; - auto add_device = [&outnames](const DevMap &entry) -> void + auto add_playback_device = [&outnames](const DevMap &entry) -> void { - /* +1 to also append the null char (to ensure a null-separated list and - * double-null terminated list). - */ - outnames.append(entry.name.c_str(), entry.name.length()+1); + if(entry.device_name == DefaultPlaybackDevName) + outnames.emplace(outnames.cbegin(), entry.name); + else + outnames.push_back(entry.name); + }; + auto add_capture_device = [&outnames](const DevMap &entry) -> void + { + if(entry.device_name == DefaultCaptureDevName) + outnames.emplace(outnames.cbegin(), entry.name); + else + outnames.push_back(entry.name); }; + auto plock = MainloopUniqueLock{gGlobalMainloop}; switch(type) { case BackendType::Playback: - gGlobalMainloop.probePlaybackDevices(); - std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device); + outnames.reserve(PlaybackDevices.size()); + std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_playback_device); break; case BackendType::Capture: - gGlobalMainloop.probeCaptureDevices(); - std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device); + outnames.reserve(CaptureDevices.size()); + std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_capture_device); break; } @@ -1491,3 +1560,18 @@ BackendFactory &PulseBackendFactory::getFactory() static PulseBackendFactory factory{}; return factory; } + +alc::EventSupport PulseBackendFactory::queryEventSupport(alc::EventType eventType, BackendType) +{ + switch(eventType) + { + case alc::EventType::DeviceAdded: + case alc::EventType::DeviceRemoved: + case alc::EventType::DefaultDeviceChanged: + return alc::EventSupport::FullSupport; + + case alc::EventType::Count: + break; + } + return alc::EventSupport::NoSupport; +} diff --git a/3rdparty/openal/alc/backends/pulseaudio.h b/3rdparty/openal/alc/backends/pulseaudio.h index 6690fe8a9f6d..c183d4794e28 100644 --- a/3rdparty/openal/alc/backends/pulseaudio.h +++ b/3rdparty/openal/alc/backends/pulseaudio.h @@ -1,19 +1,27 @@ #ifndef BACKENDS_PULSEAUDIO_H #define BACKENDS_PULSEAUDIO_H +#include +#include + +#include "alc/events.h" #include "base.h" +struct DeviceBase; + class PulseBackendFactory final : public BackendFactory { public: - bool init() override; + auto init() -> bool final; + + auto querySupport(BackendType type) -> bool final; - bool querySupport(BackendType type) override; + auto queryEventSupport(alc::EventType eventType, BackendType type) -> alc::EventSupport final; - std::string probe(BackendType type) override; + auto enumerate(BackendType type) -> std::vector final; - BackendPtr createBackend(DeviceBase *device, BackendType type) override; + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; - static BackendFactory &getFactory(); + static auto getFactory() -> BackendFactory&; }; #endif /* BACKENDS_PULSEAUDIO_H */ diff --git a/3rdparty/openal/alc/backends/sdl2.cpp b/3rdparty/openal/alc/backends/sdl2.cpp index f5ed4316a87e..a0e41f4a0e24 100644 --- a/3rdparty/openal/alc/backends/sdl2.cpp +++ b/3rdparty/openal/alc/backends/sdl2.cpp @@ -26,11 +26,10 @@ #include #include #include +#include -#include "almalloc.h" #include "alnumeric.h" #include "core/device.h" -#include "core/logging.h" _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wold-style-cast\"") @@ -40,16 +39,13 @@ _Pragma("GCC diagnostic pop") namespace { -#ifdef _WIN32 -#define DEVNAME_PREFIX "OpenAL Soft on " -#else -#define DEVNAME_PREFIX "" -#endif +using namespace std::string_view_literals; -constexpr char defaultDeviceName[] = DEVNAME_PREFIX "Default Device"; +[[nodiscard]] constexpr auto getDefaultDeviceName() noexcept -> std::string_view +{ return "Default Device"sv; } struct Sdl2Backend final : public BackendBase { - Sdl2Backend(DeviceBase *device) noexcept : BackendBase{device} { } + explicit Sdl2Backend(DeviceBase *device) noexcept : BackendBase{device} { } ~Sdl2Backend() override; void audioCallback(Uint8 *stream, int len) noexcept; @@ -59,15 +55,9 @@ struct Sdl2Backend final : public BackendBase { void start() override; void stop() override; + std::string mSDLName; SDL_AudioDeviceID mDeviceID{0u}; uint mFrameSize{0}; - - uint mFrequency{0u}; - DevFmtChannels mFmtChans{}; - DevFmtType mFmtType{}; - uint mUpdateSize{0u}; - - DEF_NEWDEL(Sdl2Backend) }; Sdl2Backend::~Sdl2Backend() @@ -88,7 +78,7 @@ void Sdl2Backend::open(std::string_view name) { SDL_AudioSpec want{}, have{}; - want.freq = static_cast(mDevice->Frequency); + want.freq = static_cast(mDevice->mSampleRate); switch(mDevice->FmtType) { case DevFmtUByte: want.format = AUDIO_U8; break; @@ -99,8 +89,9 @@ void Sdl2Backend::open(std::string_view name) case DevFmtInt: want.format = AUDIO_S32SYS; break; case DevFmtFloat: want.format = AUDIO_F32; break; } - want.channels = (mDevice->FmtChans == DevFmtMono) ? 1 : 2; - want.samples = static_cast(minu(mDevice->UpdateSize, 8192)); + want.channels = static_cast(std::min(mDevice->channelsFromFmt(), + std::numeric_limits::max())); + want.samples = static_cast(std::min(mDevice->mUpdateSize, 8192u)); want.callback = [](void *ptr, Uint8 *stream, int len) noexcept { return static_cast(ptr)->audioCallback(stream, len); }; want.userdata = this; @@ -108,45 +99,22 @@ void Sdl2Backend::open(std::string_view name) /* Passing nullptr to SDL_OpenAudioDevice opens a default, which isn't * necessarily the first in the list. */ - SDL_AudioDeviceID devid; + const auto defaultDeviceName = getDefaultDeviceName(); if(name.empty() || name == defaultDeviceName) { name = defaultDeviceName; - devid = SDL_OpenAudioDevice(nullptr, SDL_FALSE, &want, &have, SDL_AUDIO_ALLOW_ANY_CHANGE); + mSDLName.clear(); + mDeviceID = SDL_OpenAudioDevice(nullptr, SDL_FALSE, &want, &have, + SDL_AUDIO_ALLOW_ANY_CHANGE); } else { - const size_t prefix_len = strlen(DEVNAME_PREFIX); - if(name.length() >= prefix_len && strncmp(name.data(), DEVNAME_PREFIX, prefix_len) == 0) - { - /* Copy the string_view to a string to ensure it's null terminated - * for this call. - */ - const std::string devname{name.substr(prefix_len)}; - devid = SDL_OpenAudioDevice(devname.c_str(), SDL_FALSE, &want, &have, - SDL_AUDIO_ALLOW_ANY_CHANGE); - } - else - { - const std::string devname{name}; - devid = SDL_OpenAudioDevice(devname.c_str(), SDL_FALSE, &want, &have, - SDL_AUDIO_ALLOW_ANY_CHANGE); - } - } - if(!devid) - throw al::backend_exception{al::backend_error::NoDevice, "%s", SDL_GetError()}; - - DevFmtChannels devchans{}; - if(have.channels >= 2) - devchans = DevFmtStereo; - else if(have.channels == 1) - devchans = DevFmtMono; - else - { - SDL_CloseAudioDevice(devid); - throw al::backend_exception{al::backend_error::DeviceError, - "Unhandled SDL channel count: %d", int{have.channels}}; + mSDLName = name; + mDeviceID = SDL_OpenAudioDevice(mSDLName.c_str(), SDL_FALSE, &want, &have, + SDL_AUDIO_ALLOW_ANY_CHANGE); } + if(!mDeviceID) + throw al::backend_exception{al::backend_error::NoDevice, "{}", SDL_GetError()}; DevFmtType devtype{}; switch(have.format) @@ -158,32 +126,100 @@ void Sdl2Backend::open(std::string_view name) case AUDIO_S32SYS: devtype = DevFmtInt; break; case AUDIO_F32SYS: devtype = DevFmtFloat; break; default: - SDL_CloseAudioDevice(devid); - throw al::backend_exception{al::backend_error::DeviceError, "Unhandled SDL format: 0x%04x", - have.format}; + throw al::backend_exception{al::backend_error::DeviceError, + "Unhandled SDL format: {:#04x}", have.format}; } - if(mDeviceID) - SDL_CloseAudioDevice(mDeviceID); - mDeviceID = devid; - mFrameSize = BytesFromDevFmt(devtype) * have.channels; - mFrequency = static_cast(have.freq); - mFmtChans = devchans; - mFmtType = devtype; - mUpdateSize = have.samples; - mDevice->DeviceName = name; + mDeviceName = name; } bool Sdl2Backend::reset() { - mDevice->Frequency = mFrequency; - mDevice->FmtChans = mFmtChans; - mDevice->FmtType = mFmtType; - mDevice->UpdateSize = mUpdateSize; - mDevice->BufferSize = mUpdateSize * 2; /* SDL always (tries to) use two periods. */ + if(mDeviceID) + SDL_CloseAudioDevice(mDeviceID); + mDeviceID = 0; + + auto want = SDL_AudioSpec{}; + want.freq = static_cast(mDevice->mSampleRate); + switch(mDevice->FmtType) + { + case DevFmtUByte: want.format = AUDIO_U8; break; + case DevFmtByte: want.format = AUDIO_S8; break; + case DevFmtUShort: want.format = AUDIO_U16SYS; break; + case DevFmtShort: want.format = AUDIO_S16SYS; break; + case DevFmtUInt: [[fallthrough]]; + case DevFmtInt: want.format = AUDIO_S32SYS; break; + case DevFmtFloat: want.format = AUDIO_F32; break; + } + want.channels = static_cast(std::min(mDevice->channelsFromFmt(), + std::numeric_limits::max())); + want.samples = static_cast(std::min(mDevice->mUpdateSize, 8192u)); + want.callback = [](void *ptr, Uint8 *stream, int len) noexcept + { return static_cast(ptr)->audioCallback(stream, len); }; + want.userdata = this; + + auto have = SDL_AudioSpec{}; + if(mSDLName.empty()) + { + mDeviceID = SDL_OpenAudioDevice(nullptr, SDL_FALSE, &want, &have, + SDL_AUDIO_ALLOW_ANY_CHANGE); + } + else + { + mDeviceID = SDL_OpenAudioDevice(mSDLName.c_str(), SDL_FALSE, &want, &have, + SDL_AUDIO_ALLOW_ANY_CHANGE); + } + if(!mDeviceID) + throw al::backend_exception{al::backend_error::NoDevice, "{}", SDL_GetError()}; + + if(have.channels != mDevice->channelsFromFmt()) + { + /* SDL guarantees these layouts for the given channel count. */ + if(have.channels == 8) + mDevice->FmtChans = DevFmtX71; + else if(have.channels == 7) + mDevice->FmtChans = DevFmtX61; + else if(have.channels == 6) + mDevice->FmtChans = DevFmtX51; + else if(have.channels == 4) + mDevice->FmtChans = DevFmtQuad; + else if(have.channels >= 2) + mDevice->FmtChans = DevFmtStereo; + else if(have.channels == 1) + mDevice->FmtChans = DevFmtMono; + else + throw al::backend_exception{al::backend_error::DeviceError, + "Unhandled SDL channel count: {}", int{have.channels}}; + mDevice->mAmbiOrder = 0; + } + + switch(have.format) + { + case AUDIO_U8: mDevice->FmtType = DevFmtUByte; break; + case AUDIO_S8: mDevice->FmtType = DevFmtByte; break; + case AUDIO_U16SYS: mDevice->FmtType = DevFmtUShort; break; + case AUDIO_S16SYS: mDevice->FmtType = DevFmtShort; break; + case AUDIO_S32SYS: mDevice->FmtType = DevFmtInt; break; + case AUDIO_F32SYS: mDevice->FmtType = DevFmtFloat; break; + default: + throw al::backend_exception{al::backend_error::DeviceError, + "Unhandled SDL format: {:#04x}", have.format}; + } + + mFrameSize = BytesFromDevFmt(mDevice->FmtType) * have.channels; + + if(have.freq < int{MinOutputRate}) + throw al::backend_exception{al::backend_error::DeviceError, + "Unhandled SDL sample rate: {}", have.freq}; + + mDevice->mSampleRate = static_cast(have.freq); + mDevice->mUpdateSize = have.samples; + mDevice->mBufferSize = std::max(have.size/mFrameSize, mDevice->mUpdateSize*2u); + setDefaultWFXChannelOrder(); + return true; } @@ -207,23 +243,25 @@ bool SDL2BackendFactory::init() bool SDL2BackendFactory::querySupport(BackendType type) { return type == BackendType::Playback; } -std::string SDL2BackendFactory::probe(BackendType type) +auto SDL2BackendFactory::enumerate(BackendType type) -> std::vector { - std::string outnames; + std::vector outnames; if(type != BackendType::Playback) return outnames; int num_devices{SDL_GetNumAudioDevices(SDL_FALSE)}; + if(num_devices <= 0) + return outnames; - /* Includes null char. */ - outnames.append(defaultDeviceName, sizeof(defaultDeviceName)); + outnames.reserve(static_cast(num_devices)+1_uz); + outnames.emplace_back(getDefaultDeviceName()); for(int i{0};i < num_devices;++i) { - std::string name{DEVNAME_PREFIX}; - name += SDL_GetAudioDeviceName(i, SDL_FALSE); - if(!name.empty()) - outnames.append(name.c_str(), name.length()+1); + if(const char *name = SDL_GetAudioDeviceName(i, SDL_FALSE)) + outnames.emplace_back(name); + else + outnames.emplace_back("Unknown Device Name #"+std::to_string(i)); } return outnames; } diff --git a/3rdparty/openal/alc/backends/sdl2.h b/3rdparty/openal/alc/backends/sdl2.h index 3bd8df8633e3..8c5829121607 100644 --- a/3rdparty/openal/alc/backends/sdl2.h +++ b/3rdparty/openal/alc/backends/sdl2.h @@ -5,15 +5,15 @@ struct SDL2BackendFactory final : public BackendFactory { public: - bool init() override; + auto init() -> bool final; - bool querySupport(BackendType type) override; + auto querySupport(BackendType type) -> bool final; - std::string probe(BackendType type) override; + auto enumerate(BackendType type) -> std::vector final; - BackendPtr createBackend(DeviceBase *device, BackendType type) override; + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; - static BackendFactory &getFactory(); + static auto getFactory() -> BackendFactory&; }; #endif /* BACKENDS_SDL2_H */ diff --git a/3rdparty/openal/alc/backends/sdl3.cpp b/3rdparty/openal/alc/backends/sdl3.cpp new file mode 100644 index 000000000000..b10ad591e0da --- /dev/null +++ b/3rdparty/openal/alc/backends/sdl3.cpp @@ -0,0 +1,393 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 2024 by authors. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + +#include "config.h" + +#include "sdl3.h" + +#include +#include +#include +#include +#include +#include + +#include "almalloc.h" +#include "core/device.h" +#include "core/logging.h" + +_Pragma("GCC diagnostic push") +_Pragma("GCC diagnostic ignored \"-Wold-style-cast\"") +#include "SDL3/SDL_audio.h" +#include "SDL3/SDL_init.h" +#include "SDL3/SDL_stdinc.h" +_Pragma("GCC diagnostic pop") + + +namespace { + +using namespace std::string_view_literals; + +_Pragma("GCC diagnostic push") +_Pragma("GCC diagnostic ignored \"-Wold-style-cast\"") +constexpr auto DefaultPlaybackDeviceID = SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK; +_Pragma("GCC diagnostic pop") + + +template +struct SdlDeleter { + /* NOLINTNEXTLINE(cppcoreguidelines-no-malloc) */ + void operator()(gsl::owner ptr) const { SDL_free(ptr); } +}; +template +using unique_sdl_ptr = std::unique_ptr>; + + +struct DeviceEntry { + std::string mName; + SDL_AudioDeviceID mPhysDeviceID{}; +}; + +std::vector gPlaybackDevices; + +void EnumeratePlaybackDevices() +{ + auto numdevs = int{}; + auto devicelist = unique_sdl_ptr{SDL_GetAudioPlaybackDevices(&numdevs)}; + if(!devicelist || numdevs < 0) + { + ERR("Failed to get playback devices: {}", SDL_GetError()); + return; + } + + auto devids = al::span{devicelist.get(), static_cast(numdevs)}; + auto newlist = std::vector{}; + + newlist.reserve(devids.size()); + std::transform(devids.begin(), devids.end(), std::back_inserter(newlist), + [](SDL_AudioDeviceID id) + { + auto *name = SDL_GetAudioDeviceName(id); + if(!name) return DeviceEntry{}; + TRACE("Got device \"{}\", ID {}", name, id); + return DeviceEntry{name, id}; + }); + + gPlaybackDevices.swap(newlist); +} + +[[nodiscard]] constexpr auto getDefaultDeviceName() noexcept -> std::string_view +{ return "Default Device"sv; } + + +struct Sdl3Backend final : public BackendBase { + explicit Sdl3Backend(DeviceBase *device) noexcept : BackendBase{device} { } + ~Sdl3Backend() final; + + void audioCallback(SDL_AudioStream *stream, int additional_amount, int total_amount) noexcept; + + void open(std::string_view name) final; + auto reset() -> bool final; + void start() final; + void stop() final; + + SDL_AudioDeviceID mDeviceID{0}; + SDL_AudioStream *mStream{nullptr}; + uint mNumChannels{0}; + uint mFrameSize{0}; + std::vector mBuffer; +}; + +Sdl3Backend::~Sdl3Backend() +{ + if(mStream) + SDL_DestroyAudioStream(mStream); + mStream = nullptr; +} + +void Sdl3Backend::audioCallback(SDL_AudioStream *stream, int additional_amount, int total_amount) + noexcept +{ + if(additional_amount < 0) + additional_amount = total_amount; + if(additional_amount <= 0) + return; + + const auto ulen = static_cast(additional_amount); + assert((ulen % mFrameSize) == 0); + + if(ulen > mBuffer.size()) + { + mBuffer.resize(ulen); + std::fill(mBuffer.begin(), mBuffer.end(), (mDevice->FmtType == DevFmtUByte) + ? std::byte{0x80} : std::byte{}); + } + + mDevice->renderSamples(mBuffer.data(), ulen / mFrameSize, mNumChannels); + SDL_PutAudioStreamData(stream, mBuffer.data(), additional_amount); +} + +void Sdl3Backend::open(std::string_view name) +{ + const auto defaultDeviceName = getDefaultDeviceName(); + if(name.empty() || name == defaultDeviceName) + { + name = defaultDeviceName; + mDeviceID = DefaultPlaybackDeviceID; + } + else + { + if(gPlaybackDevices.empty()) + EnumeratePlaybackDevices(); + + const auto iter = std::find_if(gPlaybackDevices.cbegin(), gPlaybackDevices.cend(), + [name](const DeviceEntry &entry) { return name == entry.mName; }); + if(iter == gPlaybackDevices.cend()) + throw al::backend_exception{al::backend_error::NoDevice, "No device named {}", name}; + + mDeviceID = iter->mPhysDeviceID; + } + + mStream = SDL_OpenAudioDeviceStream(mDeviceID, nullptr, nullptr, nullptr); + if(!mStream) + throw al::backend_exception{al::backend_error::NoDevice, "{}", SDL_GetError()}; + + auto have = SDL_AudioSpec{}; + auto update_size = int{}; + if(SDL_GetAudioDeviceFormat(SDL_GetAudioStreamDevice(mStream), &have, &update_size)) + { + auto devtype = mDevice->FmtType; + switch(have.format) + { + case SDL_AUDIO_U8: devtype = DevFmtUByte; break; + case SDL_AUDIO_S8: devtype = DevFmtByte; break; + case SDL_AUDIO_S16: devtype = DevFmtShort; break; + case SDL_AUDIO_S32: devtype = DevFmtInt; break; + case SDL_AUDIO_F32: devtype = DevFmtFloat; break; + default: break; + } + mDevice->FmtType = devtype; + + if(have.freq >= int{MinOutputRate} && have.freq <= int{MaxOutputRate}) + mDevice->mSampleRate = static_cast(have.freq); + + /* SDL guarantees these layouts for the given channel count. */ + if(have.channels == 8) + mDevice->FmtChans = DevFmtX71; + else if(have.channels == 7) + mDevice->FmtChans = DevFmtX61; + else if(have.channels == 6) + mDevice->FmtChans = DevFmtX51; + else if(have.channels == 4) + mDevice->FmtChans = DevFmtQuad; + else if(have.channels >= 2) + mDevice->FmtChans = DevFmtStereo; + else if(have.channels == 1) + mDevice->FmtChans = DevFmtMono; + mDevice->mAmbiOrder = 0; + + mNumChannels = static_cast(have.channels); + mFrameSize = mDevice->bytesFromFmt() * mNumChannels; + + if(update_size >= 64) + { + /* We have to assume the total buffer size is just twice the update + * size. SDL doesn't tell us the full end-to-end buffer latency. + */ + mDevice->mUpdateSize = static_cast(update_size); + mDevice->mBufferSize = mDevice->mUpdateSize*2u; + } + else + ERR("Invalid update size from SDL stream: {}", update_size); + } + else + ERR("Failed to get format from SDL stream: {}", SDL_GetError()); + + mDeviceName = name; +} + +auto Sdl3Backend::reset() -> bool +{ + static constexpr auto callback = [](void *ptr, SDL_AudioStream *stream, int additional_amount, + int total_amount) noexcept + { + return static_cast(ptr)->audioCallback(stream, additional_amount, + total_amount); + }; + + if(mStream) + SDL_DestroyAudioStream(mStream); + mStream = nullptr; + + mBuffer.clear(); + mBuffer.shrink_to_fit(); + + auto want = SDL_AudioSpec{}; + if(!SDL_GetAudioDeviceFormat(mDeviceID, &want, nullptr)) + ERR("Failed to get device format: {}", SDL_GetError()); + + if(mDevice->Flags.test(FrequencyRequest) || want.freq < int{MinOutputRate}) + want.freq = static_cast(mDevice->mSampleRate); + if(mDevice->Flags.test(SampleTypeRequest) + || !(want.format == SDL_AUDIO_U8 || want.format == SDL_AUDIO_S8 + || want.format == SDL_AUDIO_S16 || want.format == SDL_AUDIO_S32 + || want.format == SDL_AUDIO_F32)) + { + switch(mDevice->FmtType) + { + case DevFmtUByte: want.format = SDL_AUDIO_U8; break; + case DevFmtByte: want.format = SDL_AUDIO_S8; break; + case DevFmtUShort: [[fallthrough]]; + case DevFmtShort: want.format = SDL_AUDIO_S16; break; + case DevFmtUInt: [[fallthrough]]; + case DevFmtInt: want.format = SDL_AUDIO_S32; break; + case DevFmtFloat: want.format = SDL_AUDIO_F32; break; + } + } + if(mDevice->Flags.test(ChannelsRequest) || want.channels < 1) + want.channels = static_cast(std::min(mDevice->channelsFromFmt(), + std::numeric_limits::max())); + + mStream = SDL_OpenAudioDeviceStream(mDeviceID, &want, callback, this); + if(!mStream) + { + /* If creating the stream failed, try again without a specific format. */ + mStream = SDL_OpenAudioDeviceStream(mDeviceID, nullptr, callback, this); + if(!mStream) + throw al::backend_exception{al::backend_error::DeviceError, + "Failed to recreate stream: {}", SDL_GetError()}; + } + + auto update_size = int{}; + auto have = SDL_AudioSpec{}; + SDL_GetAudioDeviceFormat(SDL_GetAudioStreamDevice(mStream), &have, &update_size); + + have = SDL_AudioSpec{}; + if(!SDL_GetAudioStreamFormat(mStream, &have, nullptr)) + throw al::backend_exception{al::backend_error::DeviceError, + "Failed to get stream format: {}", SDL_GetError()}; + + if(!mDevice->Flags.test(ChannelsRequest) + || (static_cast(have.channels) != mDevice->channelsFromFmt() + && !(mDevice->FmtChans == DevFmtStereo && have.channels >= 2))) + { + /* SDL guarantees these layouts for the given channel count. */ + if(have.channels == 8) + mDevice->FmtChans = DevFmtX71; + else if(have.channels == 7) + mDevice->FmtChans = DevFmtX61; + else if(have.channels == 6) + mDevice->FmtChans = DevFmtX51; + else if(have.channels == 4) + mDevice->FmtChans = DevFmtQuad; + else if(have.channels >= 2) + mDevice->FmtChans = DevFmtStereo; + else if(have.channels == 1) + mDevice->FmtChans = DevFmtMono; + else + throw al::backend_exception{al::backend_error::DeviceError, + "Unhandled SDL channel count: {}", have.channels}; + mDevice->mAmbiOrder = 0; + } + mNumChannels = static_cast(have.channels); + + switch(have.format) + { + case SDL_AUDIO_U8: mDevice->FmtType = DevFmtUByte; break; + case SDL_AUDIO_S8: mDevice->FmtType = DevFmtByte; break; + case SDL_AUDIO_S16: mDevice->FmtType = DevFmtShort; break; + case SDL_AUDIO_S32: mDevice->FmtType = DevFmtInt; break; + case SDL_AUDIO_F32: mDevice->FmtType = DevFmtFloat; break; + default: + throw al::backend_exception{al::backend_error::DeviceError, + "Unhandled SDL format: {:#04x}", al::to_underlying(have.format)}; + } + + mFrameSize = mDevice->bytesFromFmt() * mNumChannels; + + if(have.freq < int{MinOutputRate}) + throw al::backend_exception{al::backend_error::DeviceError, + "Unhandled SDL sample rate: {}", have.freq}; + mDevice->mSampleRate = static_cast(have.freq); + + if(update_size >= 64) + { + mDevice->mUpdateSize = static_cast(update_size); + mDevice->mBufferSize = mDevice->mUpdateSize*2u; + + mBuffer.resize(size_t{mDevice->mUpdateSize} * mFrameSize); + std::fill(mBuffer.begin(), mBuffer.end(), (mDevice->FmtType == DevFmtUByte) + ? std::byte{0x80} : std::byte{}); + } + else + ERR("Invalid update size from SDL stream: {}", update_size); + + setDefaultWFXChannelOrder(); + + return true; +} + +void Sdl3Backend::start() +{ SDL_ResumeAudioStreamDevice(mStream); } + +void Sdl3Backend::stop() +{ SDL_PauseAudioStreamDevice(mStream); } + +} // namespace + +auto SDL3BackendFactory::getFactory() -> BackendFactory& +{ + static SDL3BackendFactory factory{}; + return factory; +} + +auto SDL3BackendFactory::init() -> bool +{ + if(!SDL_InitSubSystem(SDL_INIT_AUDIO)) + return false; + TRACE("Current SDL3 audio driver: \"{}\"", SDL_GetCurrentAudioDriver()); + return true; +} + +auto SDL3BackendFactory::querySupport(BackendType type) -> bool +{ return type == BackendType::Playback; } + +auto SDL3BackendFactory::enumerate(BackendType type) -> std::vector +{ + auto outnames = std::vector{}; + + if(type != BackendType::Playback) + return outnames; + + EnumeratePlaybackDevices(); + outnames.reserve(gPlaybackDevices.size()+1); + outnames.emplace_back(getDefaultDeviceName()); + std::transform(gPlaybackDevices.begin(), gPlaybackDevices.end(), std::back_inserter(outnames), + std::mem_fn(&DeviceEntry::mName)); + + return outnames; +} + +auto SDL3BackendFactory::createBackend(DeviceBase *device, BackendType type) -> BackendPtr +{ + if(type == BackendType::Playback) + return BackendPtr{new Sdl3Backend{device}}; + return nullptr; +} diff --git a/3rdparty/openal/alc/backends/sdl3.h b/3rdparty/openal/alc/backends/sdl3.h new file mode 100644 index 000000000000..4d6730658509 --- /dev/null +++ b/3rdparty/openal/alc/backends/sdl3.h @@ -0,0 +1,19 @@ +#ifndef BACKENDS_SDL3_H +#define BACKENDS_SDL3_H + +#include "base.h" + +struct SDL3BackendFactory final : public BackendFactory { +public: + auto init() -> bool final; + + auto querySupport(BackendType type) -> bool final; + + auto enumerate(BackendType type) -> std::vector final; + + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; + + static auto getFactory() -> BackendFactory&; +}; + +#endif /* BACKENDS_SDL3_H */ diff --git a/3rdparty/openal/alc/backends/sndio.cpp b/3rdparty/openal/alc/backends/sndio.cpp index d54c337b2c89..187e1305781f 100644 --- a/3rdparty/openal/alc/backends/sndio.cpp +++ b/3rdparty/openal/alc/backends/sndio.cpp @@ -20,18 +20,16 @@ #include "config.h" -#include "sndio.h" +#include "sndio.hpp" -#include -#include +#include +#include +#include #include -#include -#include -#include +#include #include #include -#include "alnumeric.h" #include "althrd_setname.h" #include "core/device.h" #include "core/helpers.h" @@ -43,16 +41,18 @@ namespace { -static const char sndio_device[] = "SndIO Default"; +using namespace std::string_view_literals; + +[[nodiscard]] constexpr auto GetDefaultName() noexcept { return "SndIO Default"sv; } struct SioPar : public sio_par { - SioPar() { sio_initpar(this); } + SioPar() : sio_par{} { sio_initpar(this); } void clear() { sio_initpar(this); } }; struct SndioPlayback final : public BackendBase { - SndioPlayback(DeviceBase *device) noexcept : BackendBase{device} { } + explicit SndioPlayback(DeviceBase *device) noexcept : BackendBase{device} { } ~SndioPlayback() override; int mixerProc(); @@ -69,8 +69,6 @@ struct SndioPlayback final : public BackendBase { std::atomic mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(SndioPlayback) }; SndioPlayback::~SndioPlayback() @@ -86,7 +84,7 @@ int SndioPlayback::mixerProc() const size_t frameSize{frameStep * mDevice->bytesFromFmt()}; SetRTPriority(); - althrd_setname(MIXER_THREAD_NAME); + althrd_setname(GetMixerThreadName()); while(!mKillNow.load(std::memory_order_acquire) && mDevice->Connected.load(std::memory_order_acquire)) @@ -100,7 +98,7 @@ int SndioPlayback::mixerProc() size_t wrote{sio_write(mSndHandle, buffer.data(), buffer.size())}; if(wrote > buffer.size() || wrote == 0) { - ERR("sio_write failed: 0x%" PRIx64 "\n", wrote); + ERR("sio_write failed: {:#x}", wrote); mDevice->handleDisconnect("Failed to write playback samples"); break; } @@ -115,10 +113,10 @@ int SndioPlayback::mixerProc() void SndioPlayback::open(std::string_view name) { if(name.empty()) - name = sndio_device; - else if(name != sndio_device) - throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", - static_cast(name.length()), name.data()}; + name = GetDefaultName(); + else if(name != GetDefaultName()) + throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found", + name}; sio_hdl *sndHandle{sio_open(nullptr, SIO_PLAY, 0)}; if(!sndHandle) @@ -128,7 +126,7 @@ void SndioPlayback::open(std::string_view name) sio_close(mSndHandle); mSndHandle = sndHandle; - mDevice->DeviceName = name; + mDeviceName = name; } bool SndioPlayback::reset() @@ -136,72 +134,75 @@ bool SndioPlayback::reset() SioPar par; auto tryfmt = mDevice->FmtType; -retry_params: - switch(tryfmt) + while(true) { - case DevFmtByte: - par.bits = 8; - par.sig = 1; - break; - case DevFmtUByte: - par.bits = 8; - par.sig = 0; - break; - case DevFmtShort: - par.bits = 16; - par.sig = 1; - break; - case DevFmtUShort: - par.bits = 16; - par.sig = 0; - break; - case DevFmtFloat: - case DevFmtInt: - par.bits = 32; - par.sig = 1; - break; - case DevFmtUInt: - par.bits = 32; - par.sig = 0; - break; - } - par.bps = SIO_BPS(par.bits); - par.le = SIO_LE_NATIVE; - par.msb = 1; - - par.rate = mDevice->Frequency; - par.pchan = mDevice->channelsFromFmt(); - - par.round = mDevice->UpdateSize; - par.appbufsz = mDevice->BufferSize - mDevice->UpdateSize; - if(!par.appbufsz) par.appbufsz = mDevice->UpdateSize; + switch(tryfmt) + { + case DevFmtByte: + par.bits = 8; + par.sig = 1; + break; + case DevFmtUByte: + par.bits = 8; + par.sig = 0; + break; + case DevFmtShort: + par.bits = 16; + par.sig = 1; + break; + case DevFmtUShort: + par.bits = 16; + par.sig = 0; + break; + case DevFmtFloat: + case DevFmtInt: + par.bits = 32; + par.sig = 1; + break; + case DevFmtUInt: + par.bits = 32; + par.sig = 0; + break; + } + par.bps = SIO_BPS(par.bits); + par.le = SIO_LE_NATIVE; + par.msb = 1; + + par.rate = mDevice->mSampleRate; + par.pchan = mDevice->channelsFromFmt(); + + par.round = mDevice->mUpdateSize; + par.appbufsz = mDevice->mBufferSize - mDevice->mUpdateSize; + if(!par.appbufsz) par.appbufsz = mDevice->mUpdateSize; + + try { + if(!sio_setpar(mSndHandle, &par)) + throw al::backend_exception{al::backend_error::DeviceError, + "Failed to set device parameters"}; + + par.clear(); + if(!sio_getpar(mSndHandle, &par)) + throw al::backend_exception{al::backend_error::DeviceError, + "Failed to get device parameters"}; + + if(par.bps > 1 && par.le != SIO_LE_NATIVE) + throw al::backend_exception{al::backend_error::DeviceError, + "{}-endian samples not supported", par.le ? "Little" : "Big"}; + if(par.bits < par.bps*8 && !par.msb) + throw al::backend_exception{al::backend_error::DeviceError, + "MSB-padded samples not supported ({} of {} bits)", par.bits, par.bps*8}; + if(par.pchan < 1) + throw al::backend_exception{al::backend_error::DeviceError, + "No playback channels on device"}; - try { - if(!sio_setpar(mSndHandle, &par)) - throw al::backend_exception{al::backend_error::DeviceError, - "Failed to set device parameters"}; - - par.clear(); - if(!sio_getpar(mSndHandle, &par)) - throw al::backend_exception{al::backend_error::DeviceError, - "Failed to get device parameters"}; - - if(par.bps > 1 && par.le != SIO_LE_NATIVE) - throw al::backend_exception{al::backend_error::DeviceError, - "%s-endian samples not supported", par.le ? "Little" : "Big"}; - if(par.bits < par.bps*8 && !par.msb) - throw al::backend_exception{al::backend_error::DeviceError, - "MSB-padded samples not supported (%u of %u bits)", par.bits, par.bps*8}; - if(par.pchan < 1) - throw al::backend_exception{al::backend_error::DeviceError, - "No playback channels on device"}; - } - catch(al::backend_exception &e) { - if(tryfmt == DevFmtShort) - throw; - par.clear(); - tryfmt = DevFmtShort; - goto retry_params; + break; + } + catch(al::backend_exception &e) { + if(tryfmt == DevFmtShort) + throw; + par.clear(); + tryfmt = DevFmtShort; + } } if(par.bps == 1) @@ -212,24 +213,24 @@ bool SndioPlayback::reset() mDevice->FmtType = (par.sig==1) ? DevFmtInt : DevFmtUInt; else throw al::backend_exception{al::backend_error::DeviceError, - "Unhandled sample format: %s %u-bit", (par.sig?"signed":"unsigned"), par.bps*8}; + "Unhandled sample format: {} {}-bit", (par.sig?"signed":"unsigned"), par.bps*8}; mFrameStep = par.pchan; if(par.pchan != mDevice->channelsFromFmt()) { - WARN("Got %u channel%s for %s\n", par.pchan, (par.pchan==1)?"":"s", + WARN("Got {} channel{} for {}", par.pchan, (par.pchan==1)?"":"s", DevFmtChannelsString(mDevice->FmtChans)); if(par.pchan < 2) mDevice->FmtChans = DevFmtMono; else mDevice->FmtChans = DevFmtStereo; } - mDevice->Frequency = par.rate; + mDevice->mSampleRate = par.rate; setDefaultChannelOrder(); - mDevice->UpdateSize = par.round; - mDevice->BufferSize = par.bufsz + par.round; + mDevice->mUpdateSize = par.round; + mDevice->mBufferSize = par.bufsz + par.round; - mBuffer.resize(mDevice->UpdateSize * par.pchan*par.bps); + mBuffer.resize(size_t{mDevice->mUpdateSize} * par.pchan*par.bps); if(par.sig == 1) std::fill(mBuffer.begin(), mBuffer.end(), std::byte{}); else if(par.bits == 8) @@ -249,12 +250,12 @@ void SndioPlayback::start() try { mKillNow.store(false, std::memory_order_release); - mThread = std::thread{std::mem_fn(&SndioPlayback::mixerProc), this}; + mThread = std::thread{&SndioPlayback::mixerProc, this}; } catch(std::exception& e) { sio_stop(mSndHandle); throw al::backend_exception{al::backend_error::DeviceError, - "Failed to start mixing thread: %s", e.what()}; + "Failed to start mixing thread: {}", e.what()}; } } @@ -265,7 +266,7 @@ void SndioPlayback::stop() mThread.join(); if(!sio_stop(mSndHandle)) - ERR("Error stopping device\n"); + ERR("Error stopping device"); } @@ -275,7 +276,7 @@ void SndioPlayback::stop() * capture buffer sizes apps may request. */ struct SndioCapture final : public BackendBase { - SndioCapture(DeviceBase *device) noexcept : BackendBase{device} { } + explicit SndioCapture(DeviceBase *device) noexcept : BackendBase{device} { } ~SndioCapture() override; int recordProc(); @@ -292,8 +293,6 @@ struct SndioCapture final : public BackendBase { std::atomic mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(SndioCapture) }; SndioCapture::~SndioCapture() @@ -306,40 +305,40 @@ SndioCapture::~SndioCapture() int SndioCapture::recordProc() { SetRTPriority(); - althrd_setname(RECORD_THREAD_NAME); + althrd_setname(GetRecordThreadName()); const uint frameSize{mDevice->frameSizeFromFmt()}; int nfds_pre{sio_nfds(mSndHandle)}; if(nfds_pre <= 0) { - mDevice->handleDisconnect("Incorrect return value from sio_nfds(): %d", nfds_pre); + mDevice->handleDisconnect("Incorrect return value from sio_nfds(): {}", nfds_pre); return 1; } - auto fds = std::make_unique(static_cast(nfds_pre)); + auto fds = std::vector(static_cast(nfds_pre)); while(!mKillNow.load(std::memory_order_acquire) && mDevice->Connected.load(std::memory_order_acquire)) { /* Wait until there's some samples to read. */ - const int nfds{sio_pollfd(mSndHandle, fds.get(), POLLIN)}; + const int nfds{sio_pollfd(mSndHandle, fds.data(), POLLIN)}; if(nfds <= 0) { - mDevice->handleDisconnect("Failed to get polling fds: %d", nfds); + mDevice->handleDisconnect("Failed to get polling fds: {}", nfds); break; } - int pollres{::poll(fds.get(), static_cast(nfds), 2000)}; + int pollres{::poll(fds.data(), fds.size(), 2000)}; if(pollres < 0) { if(errno == EINTR) continue; - mDevice->handleDisconnect("Poll error: %s", strerror(errno)); + mDevice->handleDisconnect("Poll error: {}", std::generic_category().message(errno)); break; } if(pollres == 0) continue; - const int revents{sio_revents(mSndHandle, fds.get())}; + const int revents{sio_revents(mSndHandle, fds.data())}; if((revents&POLLHUP)) { mDevice->handleDisconnect("Got POLLHUP from poll events"); @@ -349,7 +348,7 @@ int SndioCapture::recordProc() continue; auto data = mRing->getWriteVector(); - al::span buffer{data.first.buf, data.first.len*frameSize}; + al::span buffer{data[0].buf, data[0].len*frameSize}; while(!buffer.empty()) { size_t got{sio_read(mSndHandle, buffer.data(), buffer.size())}; @@ -357,8 +356,8 @@ int SndioCapture::recordProc() break; if(got > buffer.size()) { - ERR("sio_read failed: 0x%" PRIx64 "\n", got); - mDevice->handleDisconnect("sio_read failed: 0x%" PRIx64, got); + ERR("sio_read failed: {:#x}", got); + mDevice->handleDisconnect("sio_read failed: {:#x}", got); break; } @@ -367,14 +366,14 @@ int SndioCapture::recordProc() if(buffer.empty()) { data = mRing->getWriteVector(); - buffer = {data.first.buf, data.first.len*frameSize}; + buffer = {data[0].buf, data[0].len*frameSize}; } } if(buffer.empty()) { /* Got samples to read, but no place to store it. Drop it. */ - static char junk[4096]; - sio_read(mSndHandle, junk, sizeof(junk) - (sizeof(junk)%frameSize)); + static std::array junk; + sio_read(mSndHandle, junk.data(), junk.size() - (junk.size()%frameSize)); } } @@ -385,10 +384,10 @@ int SndioCapture::recordProc() void SndioCapture::open(std::string_view name) { if(name.empty()) - name = sndio_device; - else if(name != sndio_device) - throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", - static_cast(name.length()), name.data()}; + name = GetDefaultName(); + else if(name != GetDefaultName()) + throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found", + name}; mSndHandle = sio_open(nullptr, SIO_REC, true); if(mSndHandle == nullptr) @@ -423,16 +422,16 @@ void SndioCapture::open(std::string_view name) break; case DevFmtFloat: throw al::backend_exception{al::backend_error::DeviceError, - "%s capture samples not supported", DevFmtTypeString(mDevice->FmtType)}; + "{} capture samples not supported", DevFmtTypeString(mDevice->FmtType)}; } par.bps = SIO_BPS(par.bits); par.le = SIO_LE_NATIVE; par.msb = 1; par.rchan = mDevice->channelsFromFmt(); - par.rate = mDevice->Frequency; + par.rate = mDevice->mSampleRate; - par.appbufsz = maxu(mDevice->BufferSize, mDevice->Frequency/10); - par.round = minu(par.appbufsz/2, mDevice->Frequency/40); + par.appbufsz = std::max(mDevice->mBufferSize, mDevice->mSampleRate/10u); + par.round = std::min(par.appbufsz/2u, mDevice->mSampleRate/40u); if(!sio_setpar(mSndHandle, &par) || !sio_getpar(mSndHandle, &par)) throw al::backend_exception{al::backend_error::DeviceError, @@ -440,10 +439,10 @@ void SndioCapture::open(std::string_view name) if(par.bps > 1 && par.le != SIO_LE_NATIVE) throw al::backend_exception{al::backend_error::DeviceError, - "%s-endian samples not supported", par.le ? "Little" : "Big"}; + "{}-endian samples not supported", par.le ? "Little" : "Big"}; if(par.bits < par.bps*8 && !par.msb) throw al::backend_exception{al::backend_error::DeviceError, - "Padded samples not supported (got %u of %u bits)", par.bits, par.bps*8}; + "Padded samples not supported (got {} of {} bits)", par.bits, par.bps*8}; auto match_fmt = [](DevFmtType fmttype, const sio_par &p) -> bool { @@ -455,19 +454,19 @@ void SndioCapture::open(std::string_view name) || (fmttype == DevFmtUInt && p.bps == 4 && p.sig == 0); }; if(!match_fmt(mDevice->FmtType, par) || mDevice->channelsFromFmt() != par.rchan - || mDevice->Frequency != par.rate) + || mDevice->mSampleRate != par.rate) throw al::backend_exception{al::backend_error::DeviceError, - "Failed to set format %s %s %uhz, got %c%u %u-channel %uhz instead", + "Failed to set format {} {} {}hz, got {}{} {}-channel {}hz instead", DevFmtTypeString(mDevice->FmtType), DevFmtChannelsString(mDevice->FmtChans), - mDevice->Frequency, par.sig?'s':'u', par.bps*8, par.rchan, par.rate}; + mDevice->mSampleRate, par.sig?'s':'u', par.bps*8, par.rchan, par.rate}; - mRing = RingBuffer::Create(mDevice->BufferSize, par.bps*par.rchan, false); - mDevice->BufferSize = static_cast(mRing->writeSpace()); - mDevice->UpdateSize = par.round; + mRing = RingBuffer::Create(mDevice->mBufferSize, size_t{par.bps}*par.rchan, false); + mDevice->mBufferSize = static_cast(mRing->writeSpace()); + mDevice->mUpdateSize = par.round; setDefaultChannelOrder(); - mDevice->DeviceName = name; + mDeviceName = name; } void SndioCapture::start() @@ -477,12 +476,12 @@ void SndioCapture::start() try { mKillNow.store(false, std::memory_order_release); - mThread = std::thread{std::mem_fn(&SndioCapture::recordProc), this}; + mThread = std::thread{&SndioCapture::recordProc, this}; } catch(std::exception& e) { sio_stop(mSndHandle); throw al::backend_exception{al::backend_error::DeviceError, - "Failed to start capture thread: %s", e.what()}; + "Failed to start capture thread: {}", e.what()}; } } @@ -493,11 +492,11 @@ void SndioCapture::stop() mThread.join(); if(!sio_stop(mSndHandle)) - ERR("Error stopping device\n"); + ERR("Error stopping device"); } void SndioCapture::captureSamples(std::byte *buffer, uint samples) -{ mRing->read(buffer, samples); } +{ std::ignore = mRing->read(buffer, samples); } uint SndioCapture::availableSamples() { return static_cast(mRing->readSpace()); } @@ -516,18 +515,15 @@ bool SndIOBackendFactory::init() bool SndIOBackendFactory::querySupport(BackendType type) { return (type == BackendType::Playback || type == BackendType::Capture); } -std::string SndIOBackendFactory::probe(BackendType type) +auto SndIOBackendFactory::enumerate(BackendType type) -> std::vector { - std::string outnames; switch(type) { case BackendType::Playback: case BackendType::Capture: - /* Includes null char. */ - outnames.append(sndio_device, sizeof(sndio_device)); - break; + return std::vector{std::string{GetDefaultName()}}; } - return outnames; + return {}; } BackendPtr SndIOBackendFactory::createBackend(DeviceBase *device, BackendType type) diff --git a/3rdparty/openal/alc/backends/sndio.h b/3rdparty/openal/alc/backends/sndio.h deleted file mode 100644 index d94331911790..000000000000 --- a/3rdparty/openal/alc/backends/sndio.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef BACKENDS_SNDIO_H -#define BACKENDS_SNDIO_H - -#include "base.h" - -struct SndIOBackendFactory final : public BackendFactory { -public: - bool init() override; - - bool querySupport(BackendType type) override; - - std::string probe(BackendType type) override; - - BackendPtr createBackend(DeviceBase *device, BackendType type) override; - - static BackendFactory &getFactory(); -}; - -#endif /* BACKENDS_SNDIO_H */ diff --git a/3rdparty/openal/alc/backends/sndio.hpp b/3rdparty/openal/alc/backends/sndio.hpp new file mode 100644 index 000000000000..4327524ffe08 --- /dev/null +++ b/3rdparty/openal/alc/backends/sndio.hpp @@ -0,0 +1,19 @@ +#ifndef BACKENDS_SNDIO_HPP +#define BACKENDS_SNDIO_HPP + +#include "base.h" + +struct SndIOBackendFactory final : public BackendFactory { +public: + auto init() -> bool final; + + auto querySupport(BackendType type) -> bool final; + + auto enumerate(BackendType type) -> std::vector final; + + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; + + static auto getFactory() -> BackendFactory&; +}; + +#endif /* BACKENDS_SNDIO_HPP */ diff --git a/3rdparty/openal/alc/backends/solaris.cpp b/3rdparty/openal/alc/backends/solaris.cpp index 38f9db19b603..c42015e745f9 100644 --- a/3rdparty/openal/alc/backends/solaris.cpp +++ b/3rdparty/openal/alc/backends/solaris.cpp @@ -41,6 +41,7 @@ #include #include "alc/alconfig.h" +#include "alstring.h" #include "althrd_setname.h" #include "core/device.h" #include "core/helpers.h" @@ -51,13 +52,15 @@ namespace { -constexpr char solaris_device[] = "Solaris Default"; +using namespace std::string_view_literals; + +[[nodiscard]] constexpr auto GetDefaultName() noexcept { return "Solaris Default"sv; } std::string solaris_driver{"/dev/audio"}; struct SolarisBackend final : public BackendBase { - SolarisBackend(DeviceBase *device) noexcept : BackendBase{device} { } + explicit SolarisBackend(DeviceBase *device) noexcept : BackendBase{device} { } ~SolarisBackend() override; int mixerProc(); @@ -74,8 +77,6 @@ struct SolarisBackend final : public BackendBase { std::atomic mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(SolarisBackend) }; SolarisBackend::~SolarisBackend() @@ -88,10 +89,10 @@ SolarisBackend::~SolarisBackend() int SolarisBackend::mixerProc() { SetRTPriority(); - althrd_setname(MIXER_THREAD_NAME); + althrd_setname(GetMixerThreadName()); const size_t frame_step{mDevice->channelsFromFmt()}; - const uint frame_size{mDevice->frameSizeFromFmt()}; + const size_t frame_size{mDevice->frameSizeFromFmt()}; while(!mKillNow.load(std::memory_order_acquire) && mDevice->Connected.load(std::memory_order_acquire)) @@ -105,33 +106,32 @@ int SolarisBackend::mixerProc() { if(errno == EINTR || errno == EAGAIN) continue; - ERR("poll failed: %s\n", strerror(errno)); - mDevice->handleDisconnect("Failed to wait for playback buffer: %s", strerror(errno)); + ERR("poll failed: {}", strerror(errno)); + mDevice->handleDisconnect("Failed to wait for playback buffer: {}", strerror(errno)); break; } else if(pret == 0) { - WARN("poll timeout\n"); + WARN("poll timeout"); continue; } - std::byte *write_ptr{mBuffer.data()}; - size_t to_write{mBuffer.size()}; - mDevice->renderSamples(write_ptr, static_cast(to_write/frame_size), frame_step); - while(to_write > 0 && !mKillNow.load(std::memory_order_acquire)) + al::span buffer{mBuffer}; + mDevice->renderSamples(buffer.data(), static_cast(buffer.size()/frame_size), + frame_step); + while(!buffer.empty() && !mKillNow.load(std::memory_order_acquire)) { - ssize_t wrote{write(mFd, write_ptr, to_write)}; + ssize_t wrote{write(mFd, buffer.data(), buffer.size())}; if(wrote < 0) { if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) continue; - ERR("write failed: %s\n", strerror(errno)); - mDevice->handleDisconnect("Failed to write playback samples: %s", strerror(errno)); + ERR("write failed: {}", strerror(errno)); + mDevice->handleDisconnect("Failed to write playback samples: {}", strerror(errno)); break; } - to_write -= static_cast(wrote); - write_ptr += wrote; + buffer = buffer.subspan(static_cast(wrote)); } } @@ -142,21 +142,21 @@ int SolarisBackend::mixerProc() void SolarisBackend::open(std::string_view name) { if(name.empty()) - name = solaris_device; - else if(name != solaris_device) - throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", - static_cast(name.length()), name.data()}; + name = GetDefaultName(); + else if(name != GetDefaultName()) + throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found", + name}; int fd{::open(solaris_driver.c_str(), O_WRONLY)}; if(fd == -1) - throw al::backend_exception{al::backend_error::NoDevice, "Could not open %s: %s", - solaris_driver.c_str(), strerror(errno)}; + throw al::backend_exception{al::backend_error::NoDevice, "Could not open {}: {}", + solaris_driver, strerror(errno)}; if(mFd != -1) ::close(mFd); mFd = fd; - mDevice->DeviceName = name; + mDeviceName = name; } bool SolarisBackend::reset() @@ -164,7 +164,7 @@ bool SolarisBackend::reset() audio_info_t info; AUDIO_INITINFO(&info); - info.play.sample_rate = mDevice->Frequency; + info.play.sample_rate = mDevice->mSampleRate; info.play.channels = mDevice->channelsFromFmt(); switch(mDevice->FmtType) { @@ -187,11 +187,11 @@ bool SolarisBackend::reset() info.play.encoding = AUDIO_ENCODING_LINEAR; break; } - info.play.buffer_size = mDevice->BufferSize * mDevice->frameSizeFromFmt(); + info.play.buffer_size = mDevice->mBufferSize * mDevice->frameSizeFromFmt(); if(ioctl(mFd, AUDIO_SETINFO, &info) < 0) { - ERR("ioctl failed: %s\n", strerror(errno)); + ERR("ioctl failed: {}", strerror(errno)); return false; } @@ -203,7 +203,7 @@ bool SolarisBackend::reset() mDevice->FmtChans = DevFmtMono; else throw al::backend_exception{al::backend_error::DeviceError, - "Got %u device channels", info.play.channels}; + "Got {} device channels", info.play.channels}; } if(info.play.precision == 8 && info.play.encoding == AUDIO_ENCODING_LINEAR8) @@ -216,20 +216,20 @@ bool SolarisBackend::reset() mDevice->FmtType = DevFmtInt; else { - ERR("Got unhandled sample type: %d (0x%x)\n", info.play.precision, info.play.encoding); + ERR("Got unhandled sample type: {} ({:#x})", info.play.precision, info.play.encoding); return false; } uint frame_size{mDevice->bytesFromFmt() * info.play.channels}; mFrameStep = info.play.channels; - mDevice->Frequency = info.play.sample_rate; - mDevice->BufferSize = info.play.buffer_size / frame_size; + mDevice->mSampleRate = info.play.sample_rate; + mDevice->mBufferSize = info.play.buffer_size / frame_size; /* How to get the actual period size/count? */ - mDevice->UpdateSize = mDevice->BufferSize / 2; + mDevice->mUpdateSize = mDevice->mBufferSize / 2; setDefaultChannelOrder(); - mBuffer.resize(mDevice->UpdateSize * size_t{frame_size}); + mBuffer.resize(mDevice->mUpdateSize * size_t{frame_size}); std::fill(mBuffer.begin(), mBuffer.end(), std::byte{}); return true; @@ -239,11 +239,11 @@ void SolarisBackend::start() { try { mKillNow.store(false, std::memory_order_release); - mThread = std::thread{std::mem_fn(&SolarisBackend::mixerProc), this}; + mThread = std::thread{&SolarisBackend::mixerProc, this}; } catch(std::exception& e) { throw al::backend_exception{al::backend_error::DeviceError, - "Failed to start mixing thread: %s", e.what()}; + "Failed to start mixing thread: {}", e.what()}; } } @@ -254,7 +254,7 @@ void SolarisBackend::stop() mThread.join(); if(ioctl(mFd, AUDIO_DRAIN) < 0) - ERR("Error draining device: %s\n", strerror(errno)); + ERR("Error draining device: {}", strerror(errno)); } } // namespace @@ -267,7 +267,7 @@ BackendFactory &SolarisBackendFactory::getFactory() bool SolarisBackendFactory::init() { - if(auto devopt = ConfigValueStr(nullptr, "solaris", "device")) + if(auto devopt = ConfigValueStr({}, "solaris", "device")) solaris_driver = std::move(*devopt); return true; } @@ -275,23 +275,19 @@ bool SolarisBackendFactory::init() bool SolarisBackendFactory::querySupport(BackendType type) { return type == BackendType::Playback; } -std::string SolarisBackendFactory::probe(BackendType type) +auto SolarisBackendFactory::enumerate(BackendType type) -> std::vector { - std::string outnames; switch(type) { case BackendType::Playback: - { - struct stat buf; - if(stat(solaris_driver.c_str(), &buf) == 0) - outnames.append(solaris_device, sizeof(solaris_device)); - } - break; + if(struct stat buf{}; stat(solaris_driver.c_str(), &buf) == 0) + return std::vector{std::string{GetDefaultName()}}; + break; case BackendType::Capture: break; } - return outnames; + return {}; } BackendPtr SolarisBackendFactory::createBackend(DeviceBase *device, BackendType type) diff --git a/3rdparty/openal/alc/backends/solaris.h b/3rdparty/openal/alc/backends/solaris.h index 5da6ad3a95a2..8415c362b8fb 100644 --- a/3rdparty/openal/alc/backends/solaris.h +++ b/3rdparty/openal/alc/backends/solaris.h @@ -5,15 +5,15 @@ struct SolarisBackendFactory final : public BackendFactory { public: - bool init() override; + auto init() -> bool final; - bool querySupport(BackendType type) override; + auto querySupport(BackendType type) -> bool final; - std::string probe(BackendType type) override; + auto enumerate(BackendType type) -> std::vector final; - BackendPtr createBackend(DeviceBase *device, BackendType type) override; + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; - static BackendFactory &getFactory(); + static auto getFactory() -> BackendFactory&; }; #endif /* BACKENDS_SOLARIS_H */ diff --git a/3rdparty/openal/alc/backends/wasapi.cpp b/3rdparty/openal/alc/backends/wasapi.cpp index e26af7c9c60f..89518fe91f1c 100644 --- a/3rdparty/openal/alc/backends/wasapi.cpp +++ b/3rdparty/openal/alc/backends/wasapi.cpp @@ -25,10 +25,11 @@ #define WIN32_LEAN_AND_MEAN #include -#include -#include +#include +#include #include +#include #include #include #include @@ -50,30 +51,28 @@ #include #include #include -#include -#include #include #include #include +#include #include #include #include "albit.h" #include "alc/alconfig.h" -#include "alc/events.h" #include "alnumeric.h" #include "alspan.h" #include "althrd_setname.h" #include "comptr.h" #include "core/converter.h" #include "core/device.h" -#include "core/helpers.h" #include "core/logging.h" +#include "fmt/core.h" +#include "fmt/chrono.h" #include "ringbuffer.h" #include "strutils.h" -#if defined(ALSOFT_UWP) - +#if ALSOFT_UWP #include // !!This is important!! #include #include @@ -81,11 +80,7 @@ #include #include -using namespace winrt; -using namespace Windows::Foundation; -using namespace Windows::Media::Devices; -using namespace Windows::Devices::Enumeration; -using namespace Windows::Media::Devices; +#include "alstring.h" #endif /* Some headers seem to define these as macros for __uuidof, which is annoying @@ -98,7 +93,7 @@ DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x #ifndef KSDATAFORMAT_SUBTYPE_IEEE_FLOAT DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); #endif -#if !defined(ALSOFT_UWP) +#if !ALSOFT_UWP DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14); DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 0x8c,0x23, 0xe0,0xc0,0xff,0xee,0x7f,0x0e, 0); DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x23,0xe0, 0xc0,0xff,0xee,0x7f,0x0e, 4 ); @@ -106,14 +101,23 @@ DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x namespace { +#if ALSOFT_UWP +using namespace winrt; +using namespace Windows::Foundation; +using namespace Windows::Media::Devices; +using namespace Windows::Devices::Enumeration; +using namespace Windows::Media::Devices; +#endif +#ifndef E_NOTFOUND +#define E_NOTFOUND E_NOINTERFACE +#endif + +using namespace std::string_view_literals; using std::chrono::nanoseconds; using std::chrono::milliseconds; using std::chrono::seconds; -using ReferenceTime = std::chrono::duration>; - -inline constexpr ReferenceTime operator "" _reftime(unsigned long long int n) noexcept -{ return ReferenceTime{static_cast(n)}; } +using ReferenceTime = std::chrono::duration>; #define MONO SPEAKER_FRONT_CENTER @@ -125,7 +129,7 @@ inline constexpr ReferenceTime operator "" _reftime(unsigned long long int n) no #define X7DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT) #define X7DOT1DOT4 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT|SPEAKER_TOP_FRONT_LEFT|SPEAKER_TOP_FRONT_RIGHT|SPEAKER_TOP_BACK_LEFT|SPEAKER_TOP_BACK_RIGHT) -constexpr inline DWORD MaskFromTopBits(DWORD b) noexcept +constexpr auto MaskFromTopBits(DWORD b) noexcept -> DWORD { b |= b>>1; b |= b>>2; @@ -157,9 +161,6 @@ constexpr AudioObjectType ChannelMask_Quad{AudioObjectType_FrontLeft | AudioObje constexpr AudioObjectType ChannelMask_X51{AudioObjectType_FrontLeft | AudioObjectType_FrontRight | AudioObjectType_FrontCenter | AudioObjectType_LowFrequency | AudioObjectType_SideLeft | AudioObjectType_SideRight}; -constexpr AudioObjectType ChannelMask_X51Rear{AudioObjectType_FrontLeft - | AudioObjectType_FrontRight | AudioObjectType_FrontCenter | AudioObjectType_LowFrequency - | AudioObjectType_BackLeft | AudioObjectType_BackRight}; constexpr AudioObjectType ChannelMask_X61{AudioObjectType_FrontLeft | AudioObjectType_FrontRight | AudioObjectType_FrontCenter | AudioObjectType_LowFrequency | AudioObjectType_SideLeft | AudioObjectType_SideRight | AudioObjectType_BackCenter}; @@ -171,10 +172,13 @@ constexpr AudioObjectType ChannelMask_X714{AudioObjectType_FrontLeft | AudioObje | AudioObjectType_SideRight | AudioObjectType_BackLeft | AudioObjectType_BackRight | AudioObjectType_TopFrontLeft | AudioObjectType_TopFrontRight | AudioObjectType_TopBackLeft | AudioObjectType_TopBackRight}; - - -constexpr char DevNameHead[] = "OpenAL Soft on "; -constexpr size_t DevNameHeadLen{std::size(DevNameHead) - 1}; +constexpr AudioObjectType ChannelMask_X7144{AudioObjectType_FrontLeft | AudioObjectType_FrontRight + | AudioObjectType_FrontCenter | AudioObjectType_LowFrequency | AudioObjectType_SideLeft + | AudioObjectType_SideRight | AudioObjectType_BackLeft | AudioObjectType_BackRight + | AudioObjectType_TopFrontLeft | AudioObjectType_TopFrontRight | AudioObjectType_TopBackLeft + | AudioObjectType_TopBackRight | AudioObjectType_BottomFrontLeft + | AudioObjectType_BottomFrontRight | AudioObjectType_BottomBackLeft + | AudioObjectType_BottomBackRight}; template @@ -185,16 +189,15 @@ overloaded(Ts...) -> overloaded; template -auto as_unsigned(T value) noexcept -{ - using UT = std::make_unsigned_t; - return static_cast(value); -} +struct CoTaskMemDeleter { + void operator()(T *ptr) const { CoTaskMemFree(ptr); } +}; +template +using unique_coptr = std::unique_ptr>; /* Scales the given reftime value, rounding the result. */ -template -constexpr uint RefTime2Samples(const ReferenceTime &val, T srate) noexcept +constexpr auto RefTime2Samples(const ReferenceTime &val, DWORD srate) noexcept -> uint { const auto retval = (val*srate + ReferenceTime{seconds{1}}/2) / seconds{1}; return static_cast(std::min(retval, std::numeric_limits::max())); @@ -202,34 +205,66 @@ constexpr uint RefTime2Samples(const ReferenceTime &val, T srate) noexcept class GuidPrinter { - char mMsg[64]; + std::string mMsg; public: - GuidPrinter(const GUID &guid) - { - std::snprintf(mMsg, std::size(mMsg), "{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}", - DWORD{guid.Data1}, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2], - guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]); - } - const char *c_str() const { return mMsg; } + explicit GuidPrinter(const GUID &guid) + : mMsg{fmt::format( + "{{{:08x}-{:04x}-{:04x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}}}", + guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2], + guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7])} + { } + [[nodiscard]] auto str() const noexcept -> const std::string& { return mMsg; } }; struct PropVariant { - PROPVARIANT mProp; + PROPVARIANT mProp{}; public: PropVariant() { PropVariantInit(&mProp); } + PropVariant(const PropVariant &rhs) : PropVariant{} { PropVariantCopy(&mProp, &rhs.mProp); } ~PropVariant() { clear(); } + auto operator=(const PropVariant &rhs) -> PropVariant& + { + if(this != &rhs) + PropVariantCopy(&mProp, &rhs.mProp); + return *this; + } + void clear() { PropVariantClear(&mProp); } PROPVARIANT* get() noexcept { return &mProp; } - PROPVARIANT& operator*() noexcept { return mProp; } - const PROPVARIANT& operator*() const noexcept { return mProp; } + /* NOLINTBEGIN(cppcoreguidelines-pro-type-union-access) */ + [[nodiscard]] + auto type() const noexcept -> VARTYPE { return mProp.vt; } - PROPVARIANT* operator->() noexcept { return &mProp; } - const PROPVARIANT* operator->() const noexcept { return &mProp; } + template [[nodiscard]] + auto value() const -> T + { + if constexpr(std::is_same_v) + { + alassert(mProp.vt == VT_UI4 || mProp.vt == VT_UINT); + return mProp.uintVal; + } + else if constexpr(std::is_same_v || std::is_same_v + || std::is_same_v || std::is_same_v) + { + alassert(mProp.vt == VT_LPWSTR); + return mProp.pwszVal; + } + } + + void setBlob(const al::span data) + { + if constexpr(sizeof(size_t) > sizeof(ULONG)) + alassert(data.size() <= std::numeric_limits::max()); + mProp.vt = VT_BLOB; + mProp.blob.cbSize = static_cast(data.size()); + mProp.blob.pBlobData = data.data(); + } + /* NOLINTEND(cppcoreguidelines-pro-type-union-access) */ }; struct DevMap { @@ -248,9 +283,9 @@ struct DevMap { }; DevMap::~DevMap() = default; -bool checkName(const al::span list, const std::string &name) +bool checkName(const al::span list, const std::string_view name) { - auto match_name = [&name](const DevMap &entry) -> bool { return entry.name == name; }; + auto match_name = [name](const DevMap &entry) -> bool { return entry.name == name; }; return std::find_if(list.cbegin(), list.cend(), match_name) != list.cend(); } @@ -271,19 +306,26 @@ struct DeviceList { struct DeviceListLock : public std::unique_lock { using std::unique_lock::unique_lock; - auto& getPlaybackList() const noexcept { return mutex()->mPlayback; } - auto& getCaptureList() const noexcept { return mutex()->mCapture; } + [[nodiscard]] auto& getPlaybackList() const noexcept { return mutex()->mPlayback; } + [[nodiscard]] auto& getCaptureList() const noexcept { return mutex()->mCapture; } void setPlaybackDefaultId(std::wstring_view devid) const { mutex()->mPlaybackDefaultId = devid; } - std::wstring_view getPlaybackDefaultId() const noexcept { return mutex()->mPlaybackDefaultId; } + [[nodiscard]] auto getPlaybackDefaultId() const noexcept -> std::wstring_view { return mutex()->mPlaybackDefaultId; } void setCaptureDefaultId(std::wstring_view devid) const { mutex()->mCaptureDefaultId = devid; } - std::wstring_view getCaptureDefaultId() const noexcept { return mutex()->mCaptureDefaultId; } + [[nodiscard]] auto getCaptureDefaultId() const noexcept -> std::wstring_view { return mutex()->mCaptureDefaultId; } }; DeviceList gDeviceList; -#if defined(ALSOFT_UWP) +#ifdef AVRTAPI +struct AvrtHandleCloser { + void operator()(HANDLE handle) { AvRevertMmThreadCharacteristics(handle); } +}; +using AvrtHandlePtr = std::unique_ptr,AvrtHandleCloser>; +#endif + +#if ALSOFT_UWP enum EDataFlow { eRender = 0, eCapture = (eRender + 1), @@ -292,7 +334,7 @@ enum EDataFlow { }; #endif -#if defined(ALSOFT_UWP) +#if ALSOFT_UWP using DeviceHandle = Windows::Devices::Enumeration::DeviceInformation; using EventRegistrationToken = winrt::event_token; #else @@ -300,54 +342,42 @@ using DeviceHandle = ComPtr; #endif -using NameGUIDPair = std::pair; -static NameGUIDPair GetDeviceNameAndGuid(const DeviceHandle &device) +struct NameGUIDPair { std::string mName; std::string mGuid; }; +auto GetDeviceNameAndGuid(const DeviceHandle &device) -> NameGUIDPair { - static constexpr char UnknownName[]{"Unknown Device Name"}; - static constexpr char UnknownGuid[]{"Unknown Device GUID"}; -#if !defined(ALSOFT_UWP) - std::string name, guid; + constexpr auto UnknownName = "Unknown Device Name"sv; + constexpr auto UnknownGuid = "Unknown Device GUID"sv; - ComPtr ps; - HRESULT hr = device->OpenPropertyStore(STGM_READ, al::out_ptr(ps)); +#if !ALSOFT_UWP + auto ps = ComPtr{}; + auto hr = device->OpenPropertyStore(STGM_READ, al::out_ptr(ps)); if(FAILED(hr)) { - WARN("OpenPropertyStore failed: 0x%08lx\n", hr); - return std::make_pair(UnknownName, UnknownGuid); + WARN("OpenPropertyStore failed: {:#x}", as_unsigned(hr)); + return NameGUIDPair{std::string{UnknownName}, std::string{UnknownGuid}}; } - PropVariant pvprop; + auto ret = NameGUIDPair{}; + auto pvprop = PropVariant{}; hr = ps->GetValue(al::bit_cast(DEVPKEY_Device_FriendlyName), pvprop.get()); if(FAILED(hr)) - { - WARN("GetValue Device_FriendlyName failed: 0x%08lx\n", hr); - name = UnknownName; - } - else if(pvprop->vt == VT_LPWSTR) - name = wstr_to_utf8(pvprop->pwszVal); + WARN("GetValue Device_FriendlyName failed: {:#x}", as_unsigned(hr)); + else if(pvprop.type() == VT_LPWSTR) + ret.mName = wstr_to_utf8(pvprop.value()); else - { - WARN("Unexpected Device_FriendlyName PROPVARIANT type: 0x%04x\n", pvprop->vt); - name = UnknownName; - } + WARN("Unexpected Device_FriendlyName PROPVARIANT type: {:#04x}", pvprop.type()); pvprop.clear(); hr = ps->GetValue(al::bit_cast(PKEY_AudioEndpoint_GUID), pvprop.get()); if(FAILED(hr)) - { - WARN("GetValue AudioEndpoint_GUID failed: 0x%08lx\n", hr); - guid = UnknownGuid; - } - else if(pvprop->vt == VT_LPWSTR) - guid = wstr_to_utf8(pvprop->pwszVal); + WARN("GetValue AudioEndpoint_GUID failed: {:#x}", as_unsigned(hr)); + else if(pvprop.type() == VT_LPWSTR) + ret.mGuid = wstr_to_utf8(pvprop.value()); else - { - WARN("Unexpected AudioEndpoint_GUID PROPVARIANT type: 0x%04x\n", pvprop->vt); - guid = UnknownGuid; - } + WARN("Unexpected AudioEndpoint_GUID PROPVARIANT type: {:#04x}", pvprop.type()); #else - std::string name{wstr_to_utf8(device.Name())}; - std::string guid; + auto ret = NameGUIDPair{wstr_to_utf8(device.Name()), {}}; + // device->Id is DeviceInterfacePath: \\?\SWD#MMDEVAPI#{0.0.0.00000000}.{a21c17a0-fc1d-405e-ab5a-b513422b57d1}#{e6327cad-dcec-4949-ae8a-991e976a79d2} auto devIfPath = device.Id(); if(auto devIdStart = wcsstr(devIfPath.data(), L"}.")) @@ -355,25 +385,25 @@ static NameGUIDPair GetDeviceNameAndGuid(const DeviceHandle &device) devIdStart += 2; // L"}." if(auto devIdStartEnd = wcschr(devIdStart, L'#')) { - std::wstring wDevId{devIdStart, static_cast(devIdStartEnd - devIdStart)}; - guid = wstr_to_utf8(wDevId.c_str()); - std::transform(guid.begin(), guid.end(), guid.begin(), + ret.mGuid = wstr_to_utf8(std::wstring_view{devIdStart, + static_cast(devIdStartEnd - devIdStart)}); + std::transform(ret.mGuid.begin(), ret.mGuid.end(), ret.mGuid.begin(), [](char ch) { return static_cast(std::toupper(ch)); }); } } - if(name.empty()) name = UnknownName; - if(guid.empty()) guid = UnknownGuid; #endif - return std::make_pair(std::move(name), std::move(guid)); + if(ret.mName.empty()) ret.mName = UnknownName; + if(ret.mGuid.empty()) ret.mGuid = UnknownGuid; + return ret; } -#if !defined(ALSOFT_UWP) +#if !ALSOFT_UWP EndpointFormFactor GetDeviceFormfactor(IMMDevice *device) { ComPtr ps; HRESULT hr{device->OpenPropertyStore(STGM_READ, al::out_ptr(ps))}; if(FAILED(hr)) { - WARN("OpenPropertyStore failed: 0x%08lx\n", hr); + WARN("OpenPropertyStore failed: {:#x}", as_unsigned(hr)); return UnknownFormFactor; } @@ -381,66 +411,73 @@ EndpointFormFactor GetDeviceFormfactor(IMMDevice *device) PropVariant pvform; hr = ps->GetValue(PKEY_AudioEndpoint_FormFactor, pvform.get()); if(FAILED(hr)) - WARN("GetValue AudioEndpoint_FormFactor failed: 0x%08lx\n", hr); - else if(pvform->vt == VT_UI4) - formfactor = static_cast(pvform->ulVal); - else if(pvform->vt != VT_EMPTY) - WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvform->vt); + WARN("GetValue AudioEndpoint_FormFactor failed: {:#x}", as_unsigned(hr)); + else if(pvform.type() == VT_UI4) + formfactor = static_cast(pvform.value()); + else if(pvform.type() != VT_EMPTY) + WARN("Unexpected PROPVARIANT type: {:#04x}", pvform.type()); return formfactor; } #endif -#if defined(ALSOFT_UWP) -struct DeviceHelper final : public IActivateAudioInterfaceCompletionHandler -#else -struct DeviceHelper final : private IMMNotificationClient -#endif -{ - DeviceHelper() +#if ALSOFT_UWP +struct DeviceEnumHelper final : public IActivateAudioInterfaceCompletionHandler { + DeviceEnumHelper() { -#if defined(ALSOFT_UWP) /* TODO: UWP also needs to watch for device added/removed events and * dynamically add/remove devices from the lists. */ mActiveClientEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr); - mRenderDeviceChangedToken = MediaDevice::DefaultAudioRenderDeviceChanged([this](const IInspectable& /*sender*/, const DefaultAudioRenderDeviceChangedEventArgs& args) { - if (args.Role() == AudioDeviceRole::Default) + static constexpr auto playback_cb = [](const IInspectable &sender [[maybe_unused]], + const DefaultAudioRenderDeviceChangedEventArgs &args) + { + if(args.Role() == AudioDeviceRole::Default) { - const std::string msg{ "Default playback device changed: " + + const auto msg = std::string{"Default playback device changed: " + wstr_to_utf8(args.Id())}; - alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Playback, - msg); + alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Playback, msg); } - }); + }; + mRenderDeviceChangedToken = MediaDevice::DefaultAudioRenderDeviceChanged(playback_cb); - mCaptureDeviceChangedToken = MediaDevice::DefaultAudioCaptureDeviceChanged([this](const IInspectable& /*sender*/, const DefaultAudioCaptureDeviceChangedEventArgs& args) { - if (args.Role() == AudioDeviceRole::Default) + static constexpr auto capture_cb = [](const IInspectable &sender [[maybe_unused]], + const DefaultAudioCaptureDeviceChangedEventArgs &args) + { + if(args.Role() == AudioDeviceRole::Default) { - const std::string msg{ "Default capture device changed: " + - wstr_to_utf8(args.Id()) }; - alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Capture, - msg); + const auto msg = std::string{"Default capture device changed: " + + wstr_to_utf8(args.Id())}; + alc::Event(alc::EventType::DefaultDeviceChanged, alc::DeviceType::Capture, msg); } - }); -#endif + }; + mCaptureDeviceChangedToken = MediaDevice::DefaultAudioCaptureDeviceChanged(capture_cb); } - ~DeviceHelper() + + ~DeviceEnumHelper() { -#if defined(ALSOFT_UWP) MediaDevice::DefaultAudioRenderDeviceChanged(mRenderDeviceChangedToken); MediaDevice::DefaultAudioCaptureDeviceChanged(mCaptureDeviceChangedToken); if(mActiveClientEvent != nullptr) CloseHandle(mActiveClientEvent); mActiveClientEvent = nullptr; + } #else + +struct DeviceEnumHelper final : private IMMNotificationClient { + DeviceEnumHelper() = default; + ~DeviceEnumHelper() + { if(mEnumerator) mEnumerator->UnregisterEndpointNotificationCallback(this); mEnumerator = nullptr; -#endif } +#endif + + template + auto as() noexcept -> T { return T{this}; } /** -------------------------- IUnknown ----------------------------- */ std::atomic mRefCount{1}; @@ -466,24 +503,24 @@ struct DeviceHelper final : private IMMNotificationClient // that, from the client's point of view, has a reference count of one. If the client then calls AddRef on the // interface pointer, the reference count becomes two. The client must call Release twice on the interface // pointer to drop all of its references to the object. -#if defined(ALSOFT_UWP) +#if ALSOFT_UWP if(IId == __uuidof(IActivateAudioInterfaceCompletionHandler)) { - *UnknownPtrPtr = static_cast(this); + *UnknownPtrPtr = as(); AddRef(); return S_OK; } #else if(IId == __uuidof(IMMNotificationClient)) { - *UnknownPtrPtr = static_cast(this); + *UnknownPtrPtr = as(); AddRef(); return S_OK; } #endif else if(IId == __uuidof(IAgileObject) || IId == __uuidof(IUnknown)) { - *UnknownPtrPtr = static_cast(this); + *UnknownPtrPtr = as(); AddRef(); return S_OK; } @@ -493,7 +530,7 @@ struct DeviceHelper final : private IMMNotificationClient return E_NOINTERFACE; } -#if defined(ALSOFT_UWP) +#if ALSOFT_UWP /** ----------------------- IActivateAudioInterfaceCompletionHandler ------------ */ HRESULT ActivateCompleted(IActivateAudioInterfaceAsyncOperation*) override { @@ -512,7 +549,7 @@ struct DeviceHelper final : private IMMNotificationClient HRESULT hr{mEnumerator->GetDevice(pwstrDeviceId, al::out_ptr(device))}; if(FAILED(hr)) { - ERR("Failed to get device: 0x%08lx\n", hr); + ERR("Failed to get device: {:#x}", as_unsigned(hr)); return S_OK; } @@ -520,7 +557,7 @@ struct DeviceHelper final : private IMMNotificationClient hr = device->QueryInterface(__uuidof(IMMEndpoint), al::out_ptr(endpoint)); if(FAILED(hr)) { - ERR("Failed to get device endpoint: 0x%08lx\n", hr); + ERR("Failed to get device endpoint: {:#x}", as_unsigned(hr)); return S_OK; } @@ -528,7 +565,7 @@ struct DeviceHelper final : private IMMNotificationClient hr = endpoint->GetDataFlow(&flowdir); if(FAILED(hr)) { - ERR("Failed to get endpoint data flow: 0x%08lx\n", hr); + ERR("Failed to get endpoint data flow: {:#x}", as_unsigned(hr)); return S_OK; } @@ -560,8 +597,8 @@ struct DeviceHelper final : private IMMNotificationClient { return pwstrDeviceId == entry.devid; }); if(iter == list.end()) continue; - TRACE("Removing device \"%s\", \"%s\", \"%ls\"\n", iter->name.c_str(), - iter->endpoint_guid.c_str(), iter->devid.c_str()); + TRACE("Removing device \"{}\", \"{}\", \"{}\"", iter->name, iter->endpoint_guid, + wstr_to_utf8(iter->devid)); std::string msg{"Device removed: "+std::move(iter->name)}; list.erase(iter); @@ -571,6 +608,7 @@ struct DeviceHelper final : private IMMNotificationClient return S_OK; } + /* NOLINTNEXTLINE(clazy-function-args-by-ref) */ STDMETHODIMP OnPropertyValueChanged(LPCWSTR /*pwstrDeviceId*/, const PROPERTYKEY /*key*/) noexcept override { return S_OK; } STDMETHODIMP OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDefaultDeviceId) noexcept override @@ -596,95 +634,34 @@ struct DeviceHelper final : private IMMNotificationClient } #endif - /** -------------------------- DeviceHelper ----------------------------- */ + /** ------------------------ DeviceEnumHelper -------------------------- */ HRESULT init() { -#if !defined(ALSOFT_UWP) +#if !ALSOFT_UWP HRESULT hr{CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER, __uuidof(IMMDeviceEnumerator), al::out_ptr(mEnumerator))}; if(SUCCEEDED(hr)) mEnumerator->RegisterEndpointNotificationCallback(this); else - WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr); - return hr; -#else - return S_OK; -#endif - } - - HRESULT openDevice(std::wstring_view devid, EDataFlow flow, DeviceHandle& device) - { -#if !defined(ALSOFT_UWP) - HRESULT hr{E_FAIL}; - if(mEnumerator) - { - if(devid.empty()) - hr = mEnumerator->GetDefaultAudioEndpoint(flow, eMultimedia, al::out_ptr(device)); - else - hr = mEnumerator->GetDevice(devid.data(), al::out_ptr(device)); - } + WARN("Failed to create IMMDeviceEnumerator instance: {:#x}", as_unsigned(hr)); return hr; #else - const auto deviceRole = Windows::Media::Devices::AudioDeviceRole::Default; - auto devIfPath = - devid.empty() ? (flow == eRender ? MediaDevice::GetDefaultAudioRenderId(deviceRole) : MediaDevice::GetDefaultAudioCaptureId(deviceRole)) - : winrt::hstring(devid.data()); - if (devIfPath.empty()) - return E_POINTER; - - auto&& deviceInfo = DeviceInformation::CreateFromIdAsync(devIfPath, nullptr, DeviceInformationKind::DeviceInterface).get(); - if (!deviceInfo) - return E_NOINTERFACE; - device = deviceInfo; return S_OK; #endif } -#if !defined(ALSOFT_UWP) - static HRESULT activateAudioClient(_In_ DeviceHandle &device, REFIID iid, void **ppv) - { return device->Activate(iid, CLSCTX_INPROC_SERVER, nullptr, ppv); } -#else - HRESULT activateAudioClient(_In_ DeviceHandle &device, _In_ REFIID iid, void **ppv) - { - ComPtr asyncOp; - HRESULT hr{ActivateAudioInterfaceAsync(device.Id().data(), iid, nullptr, this, - al::out_ptr(asyncOp))}; - if(FAILED(hr)) - return hr; - - /* I don't like waiting for INFINITE time, but the activate operation - * can take an indefinite amount of time since it can require user - * input. - */ - DWORD res{WaitForSingleObjectEx(mActiveClientEvent, INFINITE, FALSE)}; - if(res != WAIT_OBJECT_0) - { - ERR("WaitForSingleObjectEx error: 0x%lx\n", res); - return E_FAIL; - } - - HRESULT hrActivateRes{E_FAIL}; - ComPtr punkAudioIface; - hr = asyncOp->GetActivateResult(&hrActivateRes, al::out_ptr(punkAudioIface)); - if(SUCCEEDED(hr)) hr = hrActivateRes; - if(FAILED(hr)) return hr; - - return punkAudioIface->QueryInterface(iid, ppv); - } -#endif - std::wstring probeDevices(EDataFlow flowdir, std::vector &list) { std::wstring defaultId; std::vector{}.swap(list); -#if !defined(ALSOFT_UWP) +#if !ALSOFT_UWP ComPtr coll; HRESULT hr{mEnumerator->EnumAudioEndpoints(flowdir, DEVICE_STATE_ACTIVE, al::out_ptr(coll))}; if(FAILED(hr)) { - ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr); + ERR("Failed to enumerate audio endpoints: {:#x}", as_unsigned(hr)); return defaultId; } @@ -697,11 +674,11 @@ struct DeviceHelper final : private IMMNotificationClient hr = mEnumerator->GetDefaultAudioEndpoint(flowdir, eMultimedia, al::out_ptr(device)); if(SUCCEEDED(hr)) { - if(WCHAR *devid{GetDeviceId(device.get())}) - { - defaultId = devid; - CoTaskMemFree(devid); - } + auto devid = unique_coptr{}; + if(auto hr2 = device->GetId(al::out_ptr(devid)); SUCCEEDED(hr2)) + defaultId = devid.get(); + else + ERR("Failed to get device id: {:#x}", as_unsigned(hr)); device = nullptr; } @@ -711,23 +688,24 @@ struct DeviceHelper final : private IMMNotificationClient if(FAILED(hr)) continue; - if(WCHAR *devid{GetDeviceId(device.get())}) - { - std::ignore = AddDevice(device, devid, list); - CoTaskMemFree(devid); - } + auto devid = unique_coptr{}; + if(auto hr2 = device->GetId(al::out_ptr(devid)); SUCCEEDED(hr2)) + std::ignore = AddDevice(device, devid.get(), list); + else + ERR("Failed to get device id: {:#x}", as_unsigned(hr)); device = nullptr; } #else const auto deviceRole = Windows::Media::Devices::AudioDeviceRole::Default; auto DefaultAudioId = flowdir == eRender ? MediaDevice::GetDefaultAudioRenderId(deviceRole) : MediaDevice::GetDefaultAudioCaptureId(deviceRole); - if (DefaultAudioId.empty()) - return defaultId; - - auto deviceInfo = DeviceInformation::CreateFromIdAsync(DefaultAudioId, nullptr, DeviceInformationKind::DeviceInterface).get(); - if(!deviceInfo) - return defaultId; + if(!DefaultAudioId.empty()) + { + auto deviceInfo = DeviceInformation::CreateFromIdAsync(DefaultAudioId, nullptr, + DeviceInformationKind::DeviceInterface).get(); + if(deviceInfo) + defaultId = deviceInfo.Id().data(); + } // Get the string identifier of the audio renderer auto AudioSelector = flowdir == eRender ? MediaDevice::GetAudioRenderSelector() : MediaDevice::GetAudioCaptureSelector(); @@ -740,7 +718,7 @@ struct DeviceHelper final : private IMMNotificationClient auto deviceCount = DeviceInfoCollection.Size(); for(unsigned int i{0};i < deviceCount;++i) { - deviceInfo = DeviceInfoCollection.GetAt(i); + auto deviceInfo = DeviceInfoCollection.GetAt(i); if(deviceInfo) std::ignore = AddDevice(deviceInfo, deviceInfo.Id().data(), list); } @@ -762,37 +740,19 @@ struct DeviceHelper final : private IMMNotificationClient return false; } - auto name_guid = GetDeviceNameAndGuid(device); - int count{1}; - std::string newname{name_guid.first}; + auto [name, guid] = GetDeviceNameAndGuid(device); + auto count = 1; + auto newname = name; while(checkName(list, newname)) - { - newname = name_guid.first; - newname += " #"; - newname += std::to_string(++count); - } - list.emplace_back(std::move(newname), std::move(name_guid.second), devid); - const DevMap &newentry = list.back(); + newname = fmt::format("{} #{}", name, ++count); + const auto &newentry = list.emplace_back(std::move(newname), std::move(guid), devid); - TRACE("Got device \"%s\", \"%s\", \"%ls\"\n", newentry.name.c_str(), - newentry.endpoint_guid.c_str(), newentry.devid.c_str()); + TRACE("Got device \"{}\", \"{}\", \"{}\"", newentry.name, newentry.endpoint_guid, + wstr_to_utf8(newentry.devid)); return true; } -#if !defined(ALSOFT_UWP) - static WCHAR *GetDeviceId(IMMDevice *device) - { - WCHAR *devid; - - const HRESULT hr{device->GetId(&devid)}; - if(FAILED(hr)) - { - ERR("Failed to get device id: %lx\n", hr); - return nullptr; - } - - return devid; - } +#if !ALSOFT_UWP ComPtr mEnumerator{nullptr}; #else @@ -802,274 +762,348 @@ struct DeviceHelper final : private IMMNotificationClient EventRegistrationToken mRenderDeviceChangedToken; EventRegistrationToken mCaptureDeviceChangedToken; #endif -}; - -bool MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in) -{ - *out = WAVEFORMATEXTENSIBLE{}; - if(in->wFormatTag == WAVE_FORMAT_EXTENSIBLE) - { - *out = *CONTAINING_RECORD(in, const WAVEFORMATEXTENSIBLE, Format); - out->Format.cbSize = sizeof(*out) - sizeof(out->Format); - } - else if(in->wFormatTag == WAVE_FORMAT_PCM) - { - out->Format = *in; - out->Format.cbSize = 0; - out->Samples.wValidBitsPerSample = out->Format.wBitsPerSample; - if(out->Format.nChannels == 1) - out->dwChannelMask = MONO; - else if(out->Format.nChannels == 2) - out->dwChannelMask = STEREO; - else - ERR("Unhandled PCM channel count: %d\n", out->Format.nChannels); - out->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; - } - else if(in->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) - { - out->Format = *in; - out->Format.cbSize = 0; - out->Samples.wValidBitsPerSample = out->Format.wBitsPerSample; - if(out->Format.nChannels == 1) - out->dwChannelMask = MONO; - else if(out->Format.nChannels == 2) - out->dwChannelMask = STEREO; - else - ERR("Unhandled IEEE float channel count: %d\n", out->Format.nChannels); - out->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; - } - else - { - ERR("Unhandled format tag: 0x%04x\n", in->wFormatTag); - return false; - } - return true; -} - -void TraceFormat(const char *msg, const WAVEFORMATEX *format) -{ - constexpr size_t fmtex_extra_size{sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX)}; - if(format->wFormatTag == WAVE_FORMAT_EXTENSIBLE && format->cbSize >= fmtex_extra_size) - { - const WAVEFORMATEXTENSIBLE *fmtex{ - CONTAINING_RECORD(format, const WAVEFORMATEXTENSIBLE, Format)}; - TRACE("%s:\n" - " FormatTag = 0x%04x\n" - " Channels = %d\n" - " SamplesPerSec = %lu\n" - " AvgBytesPerSec = %lu\n" - " BlockAlign = %d\n" - " BitsPerSample = %d\n" - " Size = %d\n" - " Samples = %d\n" - " ChannelMask = 0x%lx\n" - " SubFormat = %s\n", - msg, fmtex->Format.wFormatTag, fmtex->Format.nChannels, fmtex->Format.nSamplesPerSec, - fmtex->Format.nAvgBytesPerSec, fmtex->Format.nBlockAlign, fmtex->Format.wBitsPerSample, - fmtex->Format.cbSize, fmtex->Samples.wReserved, fmtex->dwChannelMask, - GuidPrinter{fmtex->SubFormat}.c_str()); - } - else - TRACE("%s:\n" - " FormatTag = 0x%04x\n" - " Channels = %d\n" - " SamplesPerSec = %lu\n" - " AvgBytesPerSec = %lu\n" - " BlockAlign = %d\n" - " BitsPerSample = %d\n" - " Size = %d\n", - msg, format->wFormatTag, format->nChannels, format->nSamplesPerSec, - format->nAvgBytesPerSec, format->nBlockAlign, format->wBitsPerSample, format->cbSize); -} - - -enum class MsgType { - OpenDevice, - ResetDevice, - StartDevice, - StopDevice, - CloseDevice, - - QuitThread -}; - -constexpr const char *GetMessageTypeName(MsgType type) noexcept -{ - switch(type) - { - case MsgType::OpenDevice: return "Open Device"; - case MsgType::ResetDevice: return "Reset Device"; - case MsgType::StartDevice: return "Start Device"; - case MsgType::StopDevice: return "Stop Device"; - case MsgType::CloseDevice: return "Close Device"; - case MsgType::QuitThread: break; - } - return ""; -} + static inline std::mutex mMsgLock; + static inline std::condition_variable mMsgCond; + static inline bool mQuit{false}; -/* Proxy interface used by the message handler. */ -struct WasapiProxy { - virtual ~WasapiProxy() = default; - - virtual HRESULT openProxy(std::string_view name) = 0; - virtual void closeProxy() = 0; - - virtual HRESULT resetProxy() = 0; - virtual HRESULT startProxy() = 0; - virtual void stopProxy() = 0; - - struct Msg { - MsgType mType; - WasapiProxy *mProxy; - std::string_view mParam; - std::promise mPromise; - - explicit operator bool() const noexcept { return mType != MsgType::QuitThread; } - }; - static inline std::deque mMsgQueue; - static inline std::mutex mMsgQueueLock; - static inline std::condition_variable mMsgQueueCond; - - static inline std::optional sDeviceHelper; - - std::future pushMessage(MsgType type, std::string_view param={}) - { - std::promise promise; - std::future future{promise.get_future()}; - { - std::lock_guard _{mMsgQueueLock}; - mMsgQueue.emplace_back(Msg{type, this, param, std::move(promise)}); - } - mMsgQueueCond.notify_one(); - return future; - } - - static std::future pushMessageStatic(MsgType type) - { - std::promise promise; - std::future future{promise.get_future()}; - { - std::lock_guard _{mMsgQueueLock}; - mMsgQueue.emplace_back(Msg{type, nullptr, {}, std::move(promise)}); - } - mMsgQueueCond.notify_one(); - return future; - } - - static Msg popMessage() + [[nodiscard]] + static bool quit() { - std::unique_lock lock{mMsgQueueLock}; - mMsgQueueCond.wait(lock, []{return !mMsgQueue.empty();}); - Msg msg{std::move(mMsgQueue.front())}; - mMsgQueue.pop_front(); - return msg; + auto lock = std::unique_lock{mMsgLock}; + mMsgCond.wait(lock, []{return mQuit;}); + return mQuit; } - static int messageHandler(std::promise *promise); +public: + static void messageHandler(std::promise *promise); }; -int WasapiProxy::messageHandler(std::promise *promise) +/* Manages a DeviceEnumHelper on its own thread, to track available devices. */ +void DeviceEnumHelper::messageHandler(std::promise *promise) { - TRACE("Starting message thread\n"); + TRACE("Starting watcher thread"); - HRESULT hr{CoInitializeEx(nullptr, COINIT_MULTITHREADED)}; - if(FAILED(hr)) + ComWrapper com{COINIT_MULTITHREADED}; + if(!com) { - WARN("Failed to initialize COM: 0x%08lx\n", hr); - promise->set_value(hr); - return 0; + WARN("Failed to initialize COM: {:#x}", as_unsigned(com.status())); + promise->set_value(com.status()); + return; } - hr = sDeviceHelper.emplace().init(); + DeviceEnumHelper helper; + auto hr = helper.init(); promise->set_value(hr); promise = nullptr; if(FAILED(hr)) - goto skip_loop; + return; { auto devlock = DeviceListLock{gDeviceList}; - auto defaultId = sDeviceHelper->probeDevices(eRender, devlock.getPlaybackList()); + auto defaultId = helper.probeDevices(eRender, devlock.getPlaybackList()); if(!defaultId.empty()) devlock.setPlaybackDefaultId(defaultId); - defaultId = sDeviceHelper->probeDevices(eCapture, devlock.getCaptureList()); + defaultId = helper.probeDevices(eCapture, devlock.getCaptureList()); if(!defaultId.empty()) devlock.setCaptureDefaultId(defaultId); } - TRACE("Starting message loop\n"); - while(Msg msg{popMessage()}) - { - TRACE("Got message \"%s\" (0x%04x, this=%p, param=\"%.*s\")\n", - GetMessageTypeName(msg.mType), static_cast(msg.mType), - static_cast(msg.mProxy), static_cast(msg.mParam.length()), - msg.mParam.data()); - - switch(msg.mType) - { - case MsgType::OpenDevice: - hr = msg.mProxy->openProxy(msg.mParam); - msg.mPromise.set_value(hr); - continue; - - case MsgType::ResetDevice: - hr = msg.mProxy->resetProxy(); - msg.mPromise.set_value(hr); - continue; - - case MsgType::StartDevice: - hr = msg.mProxy->startProxy(); - msg.mPromise.set_value(hr); - continue; - - case MsgType::StopDevice: - msg.mProxy->stopProxy(); - msg.mPromise.set_value(S_OK); - continue; - - case MsgType::CloseDevice: - msg.mProxy->closeProxy(); - msg.mPromise.set_value(S_OK); - continue; - - case MsgType::QuitThread: - break; - } - ERR("Unexpected message: %u\n", static_cast(msg.mType)); - msg.mPromise.set_value(E_FAIL); + TRACE("Watcher thread started"); + while(!quit()) { + /* Do nothing. */ } - TRACE("Message loop finished\n"); - -skip_loop: - sDeviceHelper.reset(); - CoUninitialize(); - - return 0; } -struct WasapiPlayback final : public BackendBase, WasapiProxy { - WasapiPlayback(DeviceBase *device) noexcept : BackendBase{device} { } - ~WasapiPlayback() override; - int mixerProc(); - int mixerSpatialProc(); +#if ALSOFT_UWP +struct DeviceHelper final : public IActivateAudioInterfaceCompletionHandler { + DeviceHelper() + { + mActiveClientEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr); + } + ~DeviceHelper() + { + if(mActiveClientEvent != nullptr) + CloseHandle(mActiveClientEvent); + mActiveClientEvent = nullptr; + } - void open(std::string_view name) override; - HRESULT openProxy(std::string_view name) override; - void closeProxy() override; + template + auto as() noexcept -> T { return T{this}; } - bool reset() override; - HRESULT resetProxy() override; - void start() override; - HRESULT startProxy() override; - void stop() override; - void stopProxy() override; + /** -------------------------- IUnknown ----------------------------- */ + std::atomic mRefCount{1}; + STDMETHODIMP_(ULONG) AddRef() noexcept override { return mRefCount.fetch_add(1u) + 1u; } + STDMETHODIMP_(ULONG) Release() noexcept override { return mRefCount.fetch_sub(1u) - 1u; } - ClockLatency getClockLatency() override; + STDMETHODIMP QueryInterface(const IID& IId, void **UnknownPtrPtr) noexcept override + { + // Three rules of QueryInterface: + // https://docs.microsoft.com/en-us/windows/win32/com/rules-for-implementing-queryinterface + // 1. Objects must have identity. + // 2. The set of interfaces on an object instance must be static. + // 3. It must be possible to query successfully for any interface on an object from any other interface. + + // If ppvObject(the address) is nullptr, then this method returns E_POINTER. + if(!UnknownPtrPtr) + return E_POINTER; + + if(IId == __uuidof(IActivateAudioInterfaceCompletionHandler)) + { + *UnknownPtrPtr = as(); + AddRef(); + return S_OK; + } + else if(IId == __uuidof(IAgileObject) || IId == __uuidof(IUnknown)) + { + *UnknownPtrPtr = as(); + AddRef(); + return S_OK; + } + + // This method returns S_OK if the interface is supported, and E_NOINTERFACE otherwise. + *UnknownPtrPtr = nullptr; + return E_NOINTERFACE; + } + + /** ----------------------- IActivateAudioInterfaceCompletionHandler ------------ */ + HRESULT ActivateCompleted(IActivateAudioInterfaceAsyncOperation*) override + { + SetEvent(mActiveClientEvent); + + // Need to return S_OK + return S_OK; + } + + /** -------------------------- DeviceHelper ----------------------------- */ + [[nodiscard]] constexpr + auto init() -> HRESULT { return S_OK; } + + [[nodiscard]] + auto openDevice(const std::wstring &devid, EDataFlow flow, DeviceHandle &device) -> HRESULT + { + const auto deviceRole = Windows::Media::Devices::AudioDeviceRole::Default; + auto devIfPath = + devid.empty() ? (flow == eRender ? MediaDevice::GetDefaultAudioRenderId(deviceRole) + : MediaDevice::GetDefaultAudioCaptureId(deviceRole)) + : winrt::hstring(devid.c_str()); + if (devIfPath.empty()) + return E_POINTER; + + auto&& deviceInfo = DeviceInformation::CreateFromIdAsync(devIfPath, nullptr, + DeviceInformationKind::DeviceInterface).get(); + if(!deviceInfo) + return E_NOINTERFACE; + device = deviceInfo; + return S_OK; + } + + [[nodiscard]] + auto activateAudioClient(_In_ DeviceHandle &device, _In_ REFIID iid, void **ppv) -> HRESULT + { + ComPtr asyncOp; + HRESULT hr{ActivateAudioInterfaceAsync(device.Id().data(), iid, nullptr, this, + al::out_ptr(asyncOp))}; + if(FAILED(hr)) + return hr; + + /* I don't like waiting for INFINITE time, but the activate operation + * can take an indefinite amount of time since it can require user + * input. + */ + DWORD res{WaitForSingleObjectEx(mActiveClientEvent, INFINITE, FALSE)}; + if(res != WAIT_OBJECT_0) + { + ERR("WaitForSingleObjectEx error: {:#x}", res); + return E_FAIL; + } + + HRESULT hrActivateRes{E_FAIL}; + ComPtr punkAudioIface; + hr = asyncOp->GetActivateResult(&hrActivateRes, al::out_ptr(punkAudioIface)); + if(SUCCEEDED(hr)) hr = hrActivateRes; + if(FAILED(hr)) return hr; + + return punkAudioIface->QueryInterface(iid, ppv); + } + + HANDLE mActiveClientEvent{nullptr}; +}; + +#else + +struct DeviceHelper { + DeviceHelper() = default; + ~DeviceHelper() = default; + + [[nodiscard]] + auto init() -> HRESULT + { + HRESULT hr{CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_INPROC_SERVER, + __uuidof(IMMDeviceEnumerator), al::out_ptr(mEnumerator))}; + if(FAILED(hr)) + WARN("Failed to create IMMDeviceEnumerator instance: {:#x}", as_unsigned(hr)); + return hr; + } + + [[nodiscard]] + auto openDevice(const std::wstring &devid, EDataFlow flow, DeviceHandle &device) const + -> HRESULT + { + HRESULT hr{E_FAIL}; + if(mEnumerator) + { + if(devid.empty()) + hr = mEnumerator->GetDefaultAudioEndpoint(flow, eMultimedia, al::out_ptr(device)); + else + hr = mEnumerator->GetDevice(devid.c_str(), al::out_ptr(device)); + } + return hr; + } + + [[nodiscard]] + static auto activateAudioClient(_In_ DeviceHandle &device, REFIID iid, void **ppv) -> HRESULT + { return device->Activate(iid, CLSCTX_INPROC_SERVER, nullptr, ppv); } + + ComPtr mEnumerator{nullptr}; +}; +#endif + + +bool MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in) +{ + *out = WAVEFORMATEXTENSIBLE{}; + if(in->wFormatTag == WAVE_FORMAT_EXTENSIBLE) + { + *out = *CONTAINING_RECORD(in, const WAVEFORMATEXTENSIBLE, Format); + out->Format.cbSize = sizeof(*out) - sizeof(out->Format); + } + else if(in->wFormatTag == WAVE_FORMAT_PCM) + { + out->Format = *in; + out->Format.cbSize = 0; + /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) */ + out->Samples.wValidBitsPerSample = out->Format.wBitsPerSample; + if(out->Format.nChannels == 1) + out->dwChannelMask = MONO; + else if(out->Format.nChannels == 2) + out->dwChannelMask = STEREO; + else + ERR("Unhandled PCM channel count: {}", out->Format.nChannels); + out->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + } + else if(in->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) + { + out->Format = *in; + out->Format.cbSize = 0; + /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) */ + out->Samples.wValidBitsPerSample = out->Format.wBitsPerSample; + if(out->Format.nChannels == 1) + out->dwChannelMask = MONO; + else if(out->Format.nChannels == 2) + out->dwChannelMask = STEREO; + else + ERR("Unhandled IEEE float channel count: {}", out->Format.nChannels); + out->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + } + else + { + ERR("Unhandled format tag: {:#06x}", in->wFormatTag); + return false; + } + return true; +} + +void TraceFormat(const std::string_view msg, const WAVEFORMATEX *format) +{ + constexpr size_t fmtex_extra_size{sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX)}; + if(format->wFormatTag == WAVE_FORMAT_EXTENSIBLE && format->cbSize >= fmtex_extra_size) + { + const auto *fmtex = CONTAINING_RECORD(format, const WAVEFORMATEXTENSIBLE, Format); + /* NOLINTBEGIN(cppcoreguidelines-pro-type-union-access) */ + TRACE("{}:\n" + " FormatTag = {:#06x}\n" + " Channels = {}\n" + " SamplesPerSec = {}\n" + " AvgBytesPerSec = {}\n" + " BlockAlign = {}\n" + " BitsPerSample = {}\n" + " Size = {}\n" + " Samples = {}\n" + " ChannelMask = {:#x}\n" + " SubFormat = {}", + msg, fmtex->Format.wFormatTag, fmtex->Format.nChannels, fmtex->Format.nSamplesPerSec, + fmtex->Format.nAvgBytesPerSec, fmtex->Format.nBlockAlign, fmtex->Format.wBitsPerSample, + fmtex->Format.cbSize, fmtex->Samples.wReserved, fmtex->dwChannelMask, + GuidPrinter{fmtex->SubFormat}.str()); + /* NOLINTEND(cppcoreguidelines-pro-type-union-access) */ + } + else + TRACE("{}:\n" + " FormatTag = {:#06x}\n" + " Channels = {}\n" + " SamplesPerSec = {}\n" + " AvgBytesPerSec = {}\n" + " BlockAlign = {}\n" + " BitsPerSample = {}\n" + " Size = {}", + msg, format->wFormatTag, format->nChannels, format->nSamplesPerSec, + format->nAvgBytesPerSec, format->nBlockAlign, format->wBitsPerSample, format->cbSize); +} - void prepareFormat(WAVEFORMATEXTENSIBLE &OutputType); - void finalizeFormat(WAVEFORMATEXTENSIBLE &OutputType); - HRESULT mOpenStatus{E_FAIL}; - DeviceHandle mMMDev{nullptr}; +/* Duplicates the first sample of each sample frame to the second sample, at + * half volume. Essentially converting mono to stereo. + */ +template +void DuplicateSamples(al::span insamples, size_t step) +{ + auto samples = al::span{reinterpret_cast(insamples.data()), insamples.size()/sizeof(T)}; + if constexpr(std::is_floating_point_v) + { + for(size_t i{0};i < samples.size();i+=step) + { + const auto s = samples[i] * T{0.5}; + samples[i+1] = samples[i] = s; + } + } + else if constexpr(std::is_signed_v) + { + for(size_t i{0};i < samples.size();i+=step) + { + const auto s = samples[i] / 2; + samples[i+1] = samples[i] = T(s); + } + } + else + { + using ST = std::make_signed_t; + static constexpr auto SignBit = T{1u << (sizeof(T)*8 - 1)}; + for(size_t i{0};i < samples.size();i+=step) + { + const auto s = static_cast(samples[i]^SignBit) / 2; + samples[i+1] = samples[i] = T(s)^SignBit; + } + } +} + +void DuplicateSamples(al::span insamples, DevFmtType sampletype, size_t step) +{ + switch(sampletype) + { + case DevFmtByte: return DuplicateSamples(insamples, step); + case DevFmtUByte: return DuplicateSamples(insamples, step); + case DevFmtShort: return DuplicateSamples(insamples, step); + case DevFmtUShort: return DuplicateSamples(insamples, step); + case DevFmtInt: return DuplicateSamples(insamples, step); + case DevFmtUInt: return DuplicateSamples(insamples, step); + case DevFmtFloat: return DuplicateSamples(insamples, step); + } +} + + +struct WasapiPlayback final : public BackendBase { + explicit WasapiPlayback(DeviceBase *device) noexcept : BackendBase{device} { } + ~WasapiPlayback() override; struct PlainDevice { ComPtr mClient{nullptr}; @@ -1080,30 +1114,80 @@ struct WasapiPlayback final : public BackendBase, WasapiProxy { ComPtr mRender{nullptr}; AudioObjectType mStaticMask{}; }; - std::variant mAudio; + + void mixerProc(PlainDevice &audio); + void mixerProc(SpatialDevice &audio); + + auto openProxy(const std::string_view name, DeviceHelper &helper, DeviceHandle &mmdev) + -> HRESULT; + void finalizeFormat(WAVEFORMATEXTENSIBLE &OutputType); + auto initSpatial(DeviceHelper &helper, DeviceHandle &mmdev, SpatialDevice &audio) -> bool; + auto resetProxy(DeviceHelper &helper, DeviceHandle &mmdev, + std::variant &audiodev) -> HRESULT; + + void proc_thread(std::string&& name); + + + void open(std::string_view name) override; + bool reset() override; + void start() override; + void stop() override; + + ClockLatency getClockLatency() override; + + + std::thread mProcThread; + std::mutex mProcMutex; + std::condition_variable mProcCond; + HRESULT mProcResult{E_FAIL}; + + enum class ThreadState : uint8_t { + Initializing, + Waiting, + Playing, + Done + }; + ThreadState mState{ThreadState::Initializing}; + + enum class ThreadAction : uint8_t { + Nothing, + Configure, + Play, + Quit + }; + ThreadAction mAction{ThreadAction::Nothing}; + + + static inline DWORD sAvIndex{}; + HANDLE mNotifyEvent{nullptr}; - UINT32 mOrigBufferSize{}, mOrigUpdateSize{}; - std::unique_ptr mResampleBuffer{}; + UINT32 mOutBufferSize{}, mOutUpdateSize{}; + std::vector mResampleBuffer; uint mBufferFilled{0}; SampleConverterPtr mResampler; + bool mMonoUpsample{false}; + bool mExclusiveMode{false}; WAVEFORMATEXTENSIBLE mFormat{}; std::atomic mPadding{0u}; std::mutex mMutex; - std::atomic mKillNow{true}; - std::thread mThread; - - DEF_NEWDEL(WasapiPlayback) }; WasapiPlayback::~WasapiPlayback() { - if(SUCCEEDED(mOpenStatus)) - pushMessage(MsgType::CloseDevice).wait(); - mOpenStatus = E_FAIL; + if(mProcThread.joinable()) + { + { + auto plock = std::lock_guard{mProcMutex}; + mKillNow = true; + mAction = ThreadAction::Quit; + } + mProcCond.notify_all(); + mProcThread.join(); + } if(mNotifyEvent != nullptr) CloseHandle(mNotifyEvent); @@ -1111,145 +1195,194 @@ WasapiPlayback::~WasapiPlayback() } -FORCE_ALIGN int WasapiPlayback::mixerProc() +FORCE_ALIGN void WasapiPlayback::mixerProc(PlainDevice &audio) { - HRESULT hr{CoInitializeEx(nullptr, COINIT_MULTITHREADED)}; - if(FAILED(hr)) - { - ERR("CoInitializeEx(nullptr, COINIT_MULTITHREADED) failed: 0x%08lx\n", hr); - mDevice->handleDisconnect("COM init failed: 0x%08lx", hr); - return 1; - } - - auto &audio = std::get(mAudio); + class PriorityControl { + const int mOldPriority; - SetRTPriority(); - althrd_setname(MIXER_THREAD_NAME); + public: + PriorityControl() : mOldPriority{GetThreadPriority(GetCurrentThread())} + { + if(!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL)) + ERR("Failed to set priority level for thread"); + } + ~PriorityControl() + { SetThreadPriority(GetCurrentThread(), mOldPriority); } + }; + auto prioctrl = PriorityControl{}; const uint frame_size{mFormat.Format.nChannels * mFormat.Format.wBitsPerSample / 8u}; - const uint update_size{mOrigUpdateSize}; - const UINT32 buffer_len{mOrigBufferSize}; + const UINT32 buffer_len{mOutBufferSize}; + const void *resbufferptr{}; + + assert(buffer_len > 0); + +#ifdef AVRTAPI + /* TODO: "Audio" or "Pro Audio"? The suggestion is to use "Pro Audio" for + * device periods less than 10ms, and "Audio" for greater than or equal to + * 10ms. + */ + auto taskname = (mOutUpdateSize < mFormat.Format.nSamplesPerSec/100) ? L"Pro Audio" : L"Audio"; + auto avhandle = AvrtHandlePtr{AvSetMmThreadCharacteristicsW(taskname, &sAvIndex)}; +#endif + + auto prefilling = true; + mBufferFilled = 0; while(!mKillNow.load(std::memory_order_relaxed)) { - UINT32 written; - hr = audio.mClient->GetCurrentPadding(&written); - if(FAILED(hr)) + /* For exclusive mode, assume buffer_len sample frames are writable. + * The first pass will be a prefill of the buffer, while subsequent + * passes will only occur after notify events. + * IAudioClient::GetCurrentPadding shouldn't be used with exclusive + * streams that use event notifications, according to the docs, we + * should just assume a buffer length is writable after notification. + */ + auto written = UINT32{}; + if(!mExclusiveMode) { - ERR("Failed to get padding: 0x%08lx\n", hr); - mDevice->handleDisconnect("Failed to retrieve buffer padding: 0x%08lx", hr); - break; + if(auto hr = audio.mClient->GetCurrentPadding(&written); FAILED(hr)) + { + ERR("Failed to get padding: {:#x}", as_unsigned(hr)); + mDevice->handleDisconnect("Failed to retrieve buffer padding: {:#x}", + as_unsigned(hr)); + break; + } + mPadding.store(written, std::memory_order_relaxed); } - mPadding.store(written, std::memory_order_relaxed); - uint len{buffer_len - written}; - if(len < update_size) + if(const auto len = uint{buffer_len - written}) { - DWORD res{WaitForSingleObjectEx(mNotifyEvent, 2000, FALSE)}; - if(res != WAIT_OBJECT_0) - ERR("WaitForSingleObjectEx error: 0x%lx\n", res); - continue; - } - - BYTE *buffer; - hr = audio.mRender->GetBuffer(len, &buffer); - if(SUCCEEDED(hr)) - { - if(mResampler) + auto buffer = LPBYTE{}; + auto hr = audio.mRender->GetBuffer(len, &buffer); + if(SUCCEEDED(hr)) { - std::lock_guard _{mMutex}; - for(UINT32 done{0};done < len;) + if(mResampler) { - if(mBufferFilled == 0) + auto dlock = std::lock_guard{mMutex}; + auto dst = al::span{buffer, size_t{len}*frame_size}; + for(UINT32 done{0};done < len;) { - mDevice->renderSamples(mResampleBuffer.get(), mDevice->UpdateSize, - mFormat.Format.nChannels); - mBufferFilled = mDevice->UpdateSize; + if(mBufferFilled == 0) + { + mDevice->renderSamples(mResampleBuffer.data(), mDevice->mUpdateSize, + mFormat.Format.nChannels); + resbufferptr = mResampleBuffer.data(); + mBufferFilled = mDevice->mUpdateSize; + } + + const auto got = mResampler->convert(&resbufferptr, &mBufferFilled, + dst.data(), len-done); + dst = dst.subspan(size_t{got}*frame_size); + done += got; } + mPadding.store(written + len, std::memory_order_relaxed); + } + else + { + auto dlock = std::lock_guard{mMutex}; + mDevice->renderSamples(buffer, len, mFormat.Format.nChannels); + mPadding.store(written + len, std::memory_order_relaxed); + } - const void *src{mResampleBuffer.get()}; - uint srclen{mBufferFilled}; - uint got{mResampler->convert(&src, &srclen, buffer, len-done)}; - buffer += got*frame_size; - done += got; - - mPadding.store(written + done, std::memory_order_relaxed); - if(srclen) - { - const char *bsrc{static_cast(src)}; - std::copy(bsrc, bsrc + srclen*frame_size, mResampleBuffer.get()); - } - mBufferFilled = srclen; + if(mMonoUpsample) + { + DuplicateSamples(al::span{buffer, size_t{len}*frame_size}, mDevice->FmtType, + mFormat.Format.nChannels); } + + hr = audio.mRender->ReleaseBuffer(len, 0); } - else + if(FAILED(hr)) { - std::lock_guard _{mMutex}; - mDevice->renderSamples(buffer, len, mFormat.Format.nChannels); - mPadding.store(written + len, std::memory_order_relaxed); + ERR("Failed to buffer data: {:#x}", as_unsigned(hr)); + mDevice->handleDisconnect("Failed to send playback samples: {:#x}", + as_unsigned(hr)); + break; } - hr = audio.mRender->ReleaseBuffer(len, 0); } - if(FAILED(hr)) + + if(prefilling) { - ERR("Failed to buffer data: 0x%08lx\n", hr); - mDevice->handleDisconnect("Failed to send playback samples: 0x%08lx", hr); - break; + prefilling = false; + ResetEvent(mNotifyEvent); + if(auto hr = audio.mClient->Start(); FAILED(hr)) + { + ERR("Failed to start audio client: {:#x}", as_unsigned(hr)); + mDevice->handleDisconnect("Failed to start audio client: {:#x}", + as_unsigned(hr)); + break; + } } + + if(DWORD res{WaitForSingleObjectEx(mNotifyEvent, 2000, FALSE)}; res != WAIT_OBJECT_0) + ERR("WaitForSingleObjectEx error: {:#x}", res); } mPadding.store(0u, std::memory_order_release); - - CoUninitialize(); - return 0; + audio.mClient->Stop(); } -FORCE_ALIGN int WasapiPlayback::mixerSpatialProc() +FORCE_ALIGN void WasapiPlayback::mixerProc(SpatialDevice &audio) { - HRESULT hr{CoInitializeEx(nullptr, COINIT_MULTITHREADED)}; - if(FAILED(hr)) - { - ERR("CoInitializeEx(nullptr, COINIT_MULTITHREADED) failed: 0x%08lx\n", hr); - mDevice->handleDisconnect("COM init failed: 0x%08lx", hr); - return 1; - } - - auto &audio = std::get(mAudio); + class PriorityControl { + int mOldPriority; + public: + PriorityControl() : mOldPriority{GetThreadPriority(GetCurrentThread())} + { + if(!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL)) + ERR("Failed to set priority level for thread"); + } + ~PriorityControl() + { SetThreadPriority(GetCurrentThread(), mOldPriority); } + }; + auto prioctrl = PriorityControl{}; - SetRTPriority(); - althrd_setname(MIXER_THREAD_NAME); +#ifdef AVRTAPI + auto taskname = (mOutUpdateSize < mFormat.Format.nSamplesPerSec/100) ? L"Pro Audio" : L"Audio"; + auto avhandle = AvrtHandlePtr{AvSetMmThreadCharacteristicsW(taskname, &sAvIndex)}; +#endif std::vector> channels; - std::vector buffers; - std::vector resbuffers; + std::vector buffers; + std::vector resbuffers; std::vector tmpbuffers; /* TODO: Set mPadding appropriately. There doesn't seem to be a way to * update it dynamically based on the stream, so a fixed size may be the * best we can do. */ - mPadding.store(mDevice->BufferSize-mDevice->UpdateSize, std::memory_order_release); + mPadding.store(mOutBufferSize-mOutUpdateSize, std::memory_order_release); + ResetEvent(mNotifyEvent); + if(HRESULT hr{audio.mRender->Start()}; FAILED(hr)) + { + ERR("Failed to start spatial audio stream: {:#x}", as_unsigned(hr)); + mDevice->handleDisconnect("Failed to start spatial audio stream: {:#x}", as_unsigned(hr)); + return; + } + + mBufferFilled = 0; while(!mKillNow.load(std::memory_order_relaxed)) { if(DWORD res{WaitForSingleObjectEx(mNotifyEvent, 1000, FALSE)}; res != WAIT_OBJECT_0) { - ERR("WaitForSingleObjectEx error: 0x%lx\n", res); + ERR("WaitForSingleObjectEx error: {:#x}", res); - hr = audio.mRender->Reset(); + HRESULT hr{audio.mRender->Reset()}; if(FAILED(hr)) { - ERR("ISpatialAudioObjectRenderStream::Reset failed: 0x%08lx\n", hr); - mDevice->handleDisconnect("Device lost: 0x%08lx", hr); + ERR("ISpatialAudioObjectRenderStream::Reset failed: {:#x}", as_unsigned(hr)); + mDevice->handleDisconnect("Device lost: {:#x}", as_unsigned(hr)); break; } } UINT32 dynamicCount{}, framesToDo{}; - hr = audio.mRender->BeginUpdatingAudioObjects(&dynamicCount, &framesToDo); + HRESULT hr{audio.mRender->BeginUpdatingAudioObjects(&dynamicCount, &framesToDo)}; if(SUCCEEDED(hr)) { if(channels.empty()) UNLIKELY { - auto flags = as_unsigned(audio.mStaticMask); + auto flags = as_unsigned(al::to_underlying(audio.mStaticMask)); channels.reserve(as_unsigned(al::popcount(flags))); while(flags) { @@ -1265,9 +1398,12 @@ FORCE_ALIGN int WasapiPlayback::mixerSpatialProc() { tmpbuffers.resize(buffers.size()); resbuffers.resize(buffers.size()); + auto bufptr = mResampleBuffer.begin(); for(size_t i{0};i < tmpbuffers.size();++i) - resbuffers[i] = reinterpret_cast(mResampleBuffer.get()) + - mDevice->UpdateSize*i; + { + resbuffers[i] = al::to_address(bufptr); + bufptr += ptrdiff_t(mDevice->mUpdateSize*sizeof(float)); + } } } @@ -1275,32 +1411,32 @@ FORCE_ALIGN int WasapiPlayback::mixerSpatialProc() * update, unfortunately. */ std::transform(channels.cbegin(), channels.cend(), buffers.begin(), - [](const ComPtr &obj) -> float* + [](const ComPtr &obj) -> void* { - BYTE *buffer{}; - UINT32 size{}; + auto buffer = LPBYTE{}; + auto size = UINT32{}; obj->GetBuffer(&buffer, &size); - return reinterpret_cast(buffer); + return buffer; }); if(!mResampler) mDevice->renderSamples(buffers, framesToDo); else { - std::lock_guard _{mMutex}; + std::lock_guard dlock{mMutex}; for(UINT32 pos{0};pos < framesToDo;) { if(mBufferFilled == 0) { - mDevice->renderSamples(resbuffers, mDevice->UpdateSize); + mDevice->renderSamples(resbuffers, mDevice->mUpdateSize); std::copy(resbuffers.cbegin(), resbuffers.cend(), tmpbuffers.begin()); - mBufferFilled = mDevice->UpdateSize; + mBufferFilled = mDevice->mUpdateSize; } const uint got{mResampler->convertPlanar(tmpbuffers.data(), &mBufferFilled, - reinterpret_cast(buffers.data()), framesToDo-pos)}; + buffers.data(), framesToDo-pos)}; for(auto &buf : buffers) - buf += got; + buf = static_cast(buf) + got; /* NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) */ pos += got; } } @@ -1309,45 +1445,137 @@ FORCE_ALIGN int WasapiPlayback::mixerSpatialProc() } if(FAILED(hr)) - ERR("Failed to update playback objects: 0x%08lx\n", hr); + ERR("Failed to update playback objects: {:#x}", as_unsigned(hr)); } mPadding.store(0u, std::memory_order_release); + audio.mRender->Stop(); + audio.mRender->Reset(); +} + + +void WasapiPlayback::proc_thread(std::string&& name) +try { + auto com = ComWrapper{COINIT_MULTITHREADED}; + if(!com) + { + const auto hr = as_unsigned(com.status()); + ERR("CoInitializeEx(nullptr, COINIT_MULTITHREADED) failed: {:#x}", hr); + mDevice->handleDisconnect("COM init failed: {:#x}", hr); + + auto plock = std::lock_guard{mProcMutex}; + mProcResult = com.status(); + mState = ThreadState::Done; + mProcCond.notify_all(); + return; + } + + auto helper = DeviceHelper{}; + if(HRESULT hr{helper.init()}; FAILED(hr)) + { + mDevice->handleDisconnect("Helper init failed: {:#x}", as_unsigned(hr)); + + auto plock = std::lock_guard{mProcMutex}; + mProcResult = hr; + mState = ThreadState::Done; + mProcCond.notify_all(); + return; + } + + althrd_setname(GetMixerThreadName()); + + auto mmdev = DeviceHandle{nullptr}; + if(auto hr = openProxy(name, helper, mmdev); FAILED(hr)) + { + auto plock = std::lock_guard{mProcMutex}; + mProcResult = hr; + mState = ThreadState::Done; + mProcCond.notify_all(); + return; + } + + auto audiodev = std::variant{std::in_place_index_t<0>{}}; - CoUninitialize(); - return 0; + auto plock = std::unique_lock{mProcMutex}; + mProcResult = S_OK; + while(mState != ThreadState::Done) + { + mAction = ThreadAction::Nothing; + mState = ThreadState::Waiting; + mProcCond.notify_all(); + + mProcCond.wait(plock, [this]() noexcept { return mAction != ThreadAction::Nothing; }); + switch(mAction) + { + case ThreadAction::Nothing: + break; + + case ThreadAction::Configure: + { + plock.unlock(); + const auto hr = resetProxy(helper, mmdev, audiodev); + plock.lock(); + mProcResult = hr; + } + break; + + case ThreadAction::Play: + mKillNow.store(false, std::memory_order_release); + + mAction = ThreadAction::Nothing; + mState = ThreadState::Playing; + mProcResult = S_OK; + plock.unlock(); + mProcCond.notify_all(); + + std::visit([this](auto &audio) -> void { mixerProc(audio); }, audiodev); + + plock.lock(); + break; + + case ThreadAction::Quit: + mAction = ThreadAction::Nothing; + mState = ThreadState::Done; + mProcCond.notify_all(); + break; + } + } +} +catch(...) { + auto plock = std::lock_guard{mProcMutex}; + mProcResult = E_FAIL; + mAction = ThreadAction::Nothing; + mState = ThreadState::Done; + mProcCond.notify_all(); } void WasapiPlayback::open(std::string_view name) { - if(SUCCEEDED(mOpenStatus)) - throw al::backend_exception{al::backend_error::DeviceError, - "Unexpected duplicate open call"}; - mNotifyEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr); if(mNotifyEvent == nullptr) { - ERR("Failed to create notify events: %lu\n", GetLastError()); + ERR("Failed to create notify events: {}", GetLastError()); throw al::backend_exception{al::backend_error::DeviceError, "Failed to create notify events"}; } - if(name.length() >= DevNameHeadLen - && std::strncmp(name.data(), DevNameHead, DevNameHeadLen) == 0) - { - name = name.substr(DevNameHeadLen); - } + mProcThread = std::thread{&WasapiPlayback::proc_thread, this, std::string{name}}; - mOpenStatus = pushMessage(MsgType::OpenDevice, name).get(); - if(FAILED(mOpenStatus)) - throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: 0x%08lx", - mOpenStatus}; + auto plock = std::unique_lock{mProcMutex}; + mProcCond.wait(plock, [this]() noexcept { return mState != ThreadState::Initializing; }); + + if(mProcResult == E_NOTFOUND) + throw al::backend_exception{al::backend_error::NoDevice, "Device \"{}\" not found", name}; + if(FAILED(mProcResult) || mState == ThreadState::Done) + throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: {:#x}", + as_unsigned(mProcResult)}; } -HRESULT WasapiPlayback::openProxy(std::string_view name) +auto WasapiPlayback::openProxy(const std::string_view name, DeviceHelper &helper, + DeviceHandle &mmdev) -> HRESULT { - std::string devname; - std::wstring devid; + auto devname = std::string{}; + auto devid = std::wstring{}; if(!name.empty()) { auto devlock = DeviceListLock{gDeviceList}; @@ -1359,165 +1587,39 @@ HRESULT WasapiPlayback::openProxy(std::string_view name) { const std::wstring wname{utf8_to_wstr(name)}; iter = std::find_if(list.cbegin(), list.cend(), - [&wname](const DevMap &entry) -> bool - { return entry.devid == wname; }); - } - if(iter == list.cend()) - { - WARN("Failed to find device name matching \"%.*s\"\n", static_cast(name.length()), - name.data()); - return E_FAIL; + [&wname](const DevMap &entry) -> bool { return entry.devid == wname; }); } - devname = iter->name; - devid = iter->devid; - } - - HRESULT hr{sDeviceHelper->openDevice(devid, eRender, mMMDev)}; - if(FAILED(hr)) - { - WARN("Failed to open device \"%s\"\n", devname.empty() ? "(default)" : devname.c_str()); - return hr; - } - if(!devname.empty()) - mDevice->DeviceName = DevNameHead + std::move(devname); - else - mDevice->DeviceName = DevNameHead + GetDeviceNameAndGuid(mMMDev).first; - - return S_OK; -} - -void WasapiPlayback::closeProxy() -{ - mAudio.emplace(); - mMMDev = nullptr; -} - - -void WasapiPlayback::prepareFormat(WAVEFORMATEXTENSIBLE &OutputType) -{ - bool isRear51{false}; - - if(!mDevice->Flags.test(FrequencyRequest)) - mDevice->Frequency = OutputType.Format.nSamplesPerSec; - if(!mDevice->Flags.test(ChannelsRequest)) - { - /* If not requesting a channel configuration, auto-select given what - * fits the mask's lsb (to ensure no gaps in the output channels). If - * there's no mask, we can only assume mono or stereo. - */ - const uint32_t chancount{OutputType.Format.nChannels}; - const DWORD chanmask{OutputType.dwChannelMask}; - if(chancount >= 12 && (chanmask&X714Mask) == X7DOT1DOT4) - mDevice->FmtChans = DevFmtX714; - else if(chancount >= 8 && (chanmask&X71Mask) == X7DOT1) - mDevice->FmtChans = DevFmtX71; - else if(chancount >= 7 && (chanmask&X61Mask) == X6DOT1) - mDevice->FmtChans = DevFmtX61; - else if(chancount >= 6 && (chanmask&X51Mask) == X5DOT1) - mDevice->FmtChans = DevFmtX51; - else if(chancount >= 6 && (chanmask&X51RearMask) == X5DOT1REAR) + if(iter == list.cend()) { - mDevice->FmtChans = DevFmtX51; - isRear51 = true; + WARN("Failed to find device name matching \"{}\"", name); + return E_NOTFOUND; } - else if(chancount >= 4 && (chanmask&QuadMask) == QUAD) - mDevice->FmtChans = DevFmtQuad; - else if(chancount >= 2 && ((chanmask&StereoMask) == STEREO || !chanmask)) - mDevice->FmtChans = DevFmtStereo; - else if(chancount >= 1 && ((chanmask&MonoMask) == MONO || !chanmask)) - mDevice->FmtChans = DevFmtMono; - else - ERR("Unhandled channel config: %d -- 0x%08lx\n", chancount, chanmask); - } - else - { - const uint32_t chancount{OutputType.Format.nChannels}; - const DWORD chanmask{OutputType.dwChannelMask}; - isRear51 = (chancount == 6 && (chanmask&X51RearMask) == X5DOT1REAR); + devname = iter->name; + devid = iter->devid; } - OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; - switch(mDevice->FmtChans) - { - case DevFmtMono: - OutputType.Format.nChannels = 1; - OutputType.dwChannelMask = MONO; - break; - case DevFmtAmbi3D: - mDevice->FmtChans = DevFmtStereo; - /*fall-through*/ - case DevFmtStereo: - OutputType.Format.nChannels = 2; - OutputType.dwChannelMask = STEREO; - break; - case DevFmtQuad: - OutputType.Format.nChannels = 4; - OutputType.dwChannelMask = QUAD; - break; - case DevFmtX51: - OutputType.Format.nChannels = 6; - OutputType.dwChannelMask = isRear51 ? X5DOT1REAR : X5DOT1; - break; - case DevFmtX61: - OutputType.Format.nChannels = 7; - OutputType.dwChannelMask = X6DOT1; - break; - case DevFmtX71: - case DevFmtX3D71: - OutputType.Format.nChannels = 8; - OutputType.dwChannelMask = X7DOT1; - break; - case DevFmtX714: - OutputType.Format.nChannels = 12; - OutputType.dwChannelMask = X7DOT1DOT4; - break; - } - switch(mDevice->FmtType) + if(HRESULT hr{helper.openDevice(devid, eRender, mmdev)}; FAILED(hr)) { - case DevFmtByte: - mDevice->FmtType = DevFmtUByte; - /* fall-through */ - case DevFmtUByte: - OutputType.Format.wBitsPerSample = 8; - OutputType.Samples.wValidBitsPerSample = 8; - OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; - break; - case DevFmtUShort: - mDevice->FmtType = DevFmtShort; - /* fall-through */ - case DevFmtShort: - OutputType.Format.wBitsPerSample = 16; - OutputType.Samples.wValidBitsPerSample = 16; - OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; - break; - case DevFmtUInt: - mDevice->FmtType = DevFmtInt; - /* fall-through */ - case DevFmtInt: - OutputType.Format.wBitsPerSample = 32; - OutputType.Samples.wValidBitsPerSample = 32; - OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; - break; - case DevFmtFloat: - OutputType.Format.wBitsPerSample = 32; - OutputType.Samples.wValidBitsPerSample = 32; - OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; - break; + WARN("Failed to open device \"{}\": {}", devname.empty() + ? "(default)"sv : std::string_view{devname}, as_unsigned(hr)); + return hr; } - OutputType.Format.nSamplesPerSec = mDevice->Frequency; + if(!devname.empty()) + mDeviceName = std::move(devname); + else + mDeviceName = GetDeviceNameAndGuid(mmdev).mName; - OutputType.Format.nBlockAlign = static_cast(OutputType.Format.nChannels * - OutputType.Format.wBitsPerSample / 8); - OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec * - OutputType.Format.nBlockAlign; + return S_OK; } + void WasapiPlayback::finalizeFormat(WAVEFORMATEXTENSIBLE &OutputType) { - if(!GetConfigValueBool(mDevice->DeviceName.c_str(), "wasapi", "allow-resampler", true)) - mDevice->Frequency = OutputType.Format.nSamplesPerSec; + if(!GetConfigValueBool(mDevice->mDeviceName, "wasapi", "allow-resampler", true)) + mDevice->mSampleRate = uint(OutputType.Format.nSamplesPerSec); else - mDevice->Frequency = minu(mDevice->Frequency, OutputType.Format.nSamplesPerSec); + mDevice->mSampleRate = std::min(mDevice->mSampleRate, + uint(OutputType.Format.nSamplesPerSec)); const uint32_t chancount{OutputType.Format.nChannels}; const DWORD chanmask{OutputType.dwChannelMask}; @@ -1535,6 +1637,12 @@ void WasapiPlayback::finalizeFormat(WAVEFORMATEXTENSIBLE &OutputType) { case DevFmtMono: chansok = (chancount >= 1 && ((chanmask&MonoMask) == MONO || !chanmask)); + if(!chansok && chancount >= 2 && (chanmask&StereoMask) == STEREO) + { + /* Mono rendering with stereo+ output is handled specially. */ + chansok = true; + mMonoUpsample = true; + } break; case DevFmtStereo: chansok = (chancount >= 2 && ((chanmask&StereoMask) == STEREO || !chanmask)); @@ -1555,6 +1663,7 @@ void WasapiPlayback::finalizeFormat(WAVEFORMATEXTENSIBLE &OutputType) break; case DevFmtX714: chansok = (chancount >= 12 && ((chanmask&X714Mask) == X7DOT1DOT4 || !chanmask)); + case DevFmtX7144: case DevFmtAmbi3D: break; } @@ -1578,7 +1687,7 @@ void WasapiPlayback::finalizeFormat(WAVEFORMATEXTENSIBLE &OutputType) mDevice->FmtChans = DevFmtMono; else { - ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType.Format.nChannels, + ERR("Unhandled extensible channels: {} -- {:#08x}", OutputType.Format.nChannels, OutputType.dwChannelMask); mDevice->FmtChans = DevFmtStereo; OutputType.Format.nChannels = 2; @@ -1607,381 +1716,567 @@ void WasapiPlayback::finalizeFormat(WAVEFORMATEXTENSIBLE &OutputType) } else { - ERR("Unhandled format sub-type: %s\n", GuidPrinter{OutputType.SubFormat}.c_str()); + ERR("Unhandled format sub-type: {}", GuidPrinter{OutputType.SubFormat}.str()); mDevice->FmtType = DevFmtShort; if(OutputType.Format.wFormatTag != WAVE_FORMAT_EXTENSIBLE) OutputType.Format.wFormatTag = WAVE_FORMAT_PCM; OutputType.Format.wBitsPerSample = 16; OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; } + /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) */ OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; } -bool WasapiPlayback::reset() +auto WasapiPlayback::initSpatial(DeviceHelper &helper, DeviceHandle &mmdev, SpatialDevice &audio) + -> bool { - HRESULT hr{pushMessage(MsgType::ResetDevice).get()}; + HRESULT hr{helper.activateAudioClient(mmdev, __uuidof(ISpatialAudioClient), + al::out_ptr(audio.mClient))}; if(FAILED(hr)) - throw al::backend_exception{al::backend_error::DeviceError, "0x%08lx", hr}; - return true; -} - -HRESULT WasapiPlayback::resetProxy() -{ - if(GetConfigValueBool(mDevice->DeviceName.c_str(), "wasapi", "spatial-api", false)) { - auto &audio = mAudio.emplace(); - HRESULT hr{sDeviceHelper->activateAudioClient(mMMDev, __uuidof(ISpatialAudioClient), - al::out_ptr(audio.mClient))}; - if(FAILED(hr)) - { - ERR("Failed to activate spatial audio client: 0x%08lx\n", hr); - goto no_spatial; - } + ERR("Failed to activate spatial audio client: {:#x}", as_unsigned(hr)); + return false; + } - ComPtr fmtenum; - hr = audio.mClient->GetSupportedAudioObjectFormatEnumerator(al::out_ptr(fmtenum)); - if(FAILED(hr)) - { - ERR("Failed to get format enumerator: 0x%08lx\n", hr); - goto no_spatial; - } + ComPtr fmtenum; + hr = audio.mClient->GetSupportedAudioObjectFormatEnumerator(al::out_ptr(fmtenum)); + if(FAILED(hr)) + { + ERR("Failed to get format enumerator: {:#x}", as_unsigned(hr)); + return false; + } - UINT32 fmtcount{}; - hr = fmtenum->GetCount(&fmtcount); - if(FAILED(hr) || fmtcount == 0) - { - ERR("Failed to get format count: 0x%08lx\n", hr); - goto no_spatial; - } + UINT32 fmtcount{}; + hr = fmtenum->GetCount(&fmtcount); + if(FAILED(hr) || fmtcount == 0) + { + ERR("Failed to get format count: {:#08x}", as_unsigned(hr)); + return false; + } - WAVEFORMATEX *preferredFormat{}; - hr = fmtenum->GetFormat(0, &preferredFormat); - if(FAILED(hr)) - { - ERR("Failed to get preferred format: 0x%08lx\n", hr); - goto no_spatial; - } - TraceFormat("Preferred mix format", preferredFormat); + WAVEFORMATEX *preferredFormat{}; + hr = fmtenum->GetFormat(0, &preferredFormat); + if(FAILED(hr)) + { + ERR("Failed to get preferred format: {:#x}", as_unsigned(hr)); + return false; + } + TraceFormat("Preferred mix format", preferredFormat); - UINT32 maxFrames{}; - hr = audio.mClient->GetMaxFrameCount(preferredFormat, &maxFrames); + UINT32 maxFrames{}; + hr = audio.mClient->GetMaxFrameCount(preferredFormat, &maxFrames); + if(FAILED(hr)) + ERR("Failed to get max frames: {:#x}", as_unsigned(hr)); + else + TRACE("Max sample frames: {}", maxFrames); + for(UINT32 i{1};i < fmtcount;++i) + { + WAVEFORMATEX *otherFormat{}; + hr = fmtenum->GetFormat(i, &otherFormat); if(FAILED(hr)) - ERR("Failed to get max frames: 0x%08lx\n", hr); + ERR("Failed to get format {}: {:#x}", i+1, as_unsigned(hr)); else - TRACE("Max sample frames: %u\n", maxFrames); - for(UINT32 i{1};i < fmtcount;++i) { - WAVEFORMATEX *otherFormat{}; - hr = fmtenum->GetFormat(i, &otherFormat); + TraceFormat("Other mix format", otherFormat); + UINT32 otherMaxFrames{}; + hr = audio.mClient->GetMaxFrameCount(otherFormat, &otherMaxFrames); if(FAILED(hr)) - ERR("Failed to format %u: 0x%08lx\n", i+1, hr); + ERR("Failed to get max frames: {:#x}", as_unsigned(hr)); else - { - TraceFormat("Other mix format", otherFormat); - UINT32 otherMaxFrames{}; - hr = audio.mClient->GetMaxFrameCount(otherFormat, &otherMaxFrames); - if(FAILED(hr)) - ERR("Failed to get max frames: 0x%08lx\n", hr); - else - TRACE("Max sample frames: %u\n", otherMaxFrames); - } + TRACE("Max sample frames: {}", otherMaxFrames); } + } - WAVEFORMATEXTENSIBLE OutputType; - if(!MakeExtensible(&OutputType, preferredFormat)) - goto no_spatial; + WAVEFORMATEXTENSIBLE OutputType; + if(!MakeExtensible(&OutputType, preferredFormat)) + return false; - /* Force 32-bit float. This is currently required for planar output. */ - if(OutputType.Format.wFormatTag != WAVE_FORMAT_EXTENSIBLE - && OutputType.Format.wFormatTag != WAVE_FORMAT_IEEE_FLOAT) - { - OutputType.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; - OutputType.Format.cbSize = 0; - } - if(OutputType.Format.wBitsPerSample != 32) - { - OutputType.Format.nAvgBytesPerSec = OutputType.Format.nAvgBytesPerSec * 32u - / OutputType.Format.wBitsPerSample; - OutputType.Format.nBlockAlign = static_cast(OutputType.Format.nBlockAlign * 32 - / OutputType.Format.wBitsPerSample); - OutputType.Format.wBitsPerSample = 32; - } - OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; - OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + /* This seems to be the format of each "object", which should be mono. */ + if(!(OutputType.Format.nChannels == 1 + && (OutputType.dwChannelMask == MONO || !OutputType.dwChannelMask))) + ERR("Unhandled channel config: {} -- {:#08x}", OutputType.Format.nChannels, + OutputType.dwChannelMask); - /* Match the output rate if not requesting anything specific. */ - if(!mDevice->Flags.test(FrequencyRequest)) - mDevice->Frequency = OutputType.Format.nSamplesPerSec; - - bool isRear51{false}; - if(!mDevice->Flags.test(ChannelsRequest)) - { - const uint32_t chancount{OutputType.Format.nChannels}; - const DWORD chanmask{OutputType.dwChannelMask}; - if(chancount >= 12 && (chanmask&X714Mask) == X7DOT1DOT4) - mDevice->FmtChans = DevFmtX714; - else if(chancount >= 8 && (chanmask&X71Mask) == X7DOT1) - mDevice->FmtChans = DevFmtX71; - else if(chancount >= 7 && (chanmask&X61Mask) == X6DOT1) - mDevice->FmtChans = DevFmtX61; - else if(chancount >= 6 && (chanmask&X51Mask) == X5DOT1) - mDevice->FmtChans = DevFmtX51; - else if(chancount >= 6 && (chanmask&X51RearMask) == X5DOT1REAR) - { - mDevice->FmtChans = DevFmtX51; - isRear51 = true; - } - else if(chancount >= 4 && (chanmask&QuadMask) == QUAD) - mDevice->FmtChans = DevFmtQuad; - else if(chancount >= 2 && ((chanmask&StereoMask) == STEREO || !chanmask)) - mDevice->FmtChans = DevFmtStereo; - /* HACK: Don't autoselect mono. Wine returns this and makes the - * audio terrible. - */ - else if(!(chancount >= 1 && ((chanmask&MonoMask) == MONO || !chanmask))) - ERR("Unhandled channel config: %d -- 0x%08lx\n", chancount, chanmask); - } - else + /* Force 32-bit float. This is currently required for planar output. */ + if(OutputType.Format.wFormatTag != WAVE_FORMAT_EXTENSIBLE + && OutputType.Format.wFormatTag != WAVE_FORMAT_IEEE_FLOAT) + { + OutputType.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; + OutputType.Format.cbSize = 0; + } + if(OutputType.Format.wBitsPerSample != 32) + { + OutputType.Format.nAvgBytesPerSec = OutputType.Format.nAvgBytesPerSec * 32u + / OutputType.Format.wBitsPerSample; + OutputType.Format.nBlockAlign = static_cast(OutputType.Format.nBlockAlign * 32 + / OutputType.Format.wBitsPerSample); + OutputType.Format.wBitsPerSample = 32; + } + /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) */ + OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; + OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + + /* Match the output rate if not requesting anything specific. */ + if(!mDevice->Flags.test(FrequencyRequest)) + mDevice->mSampleRate = OutputType.Format.nSamplesPerSec; + + auto getTypeMask = [](DevFmtChannels chans) noexcept + { + switch(chans) { - const uint32_t chancount{OutputType.Format.nChannels}; - const DWORD chanmask{OutputType.dwChannelMask}; - isRear51 = (chancount == 6 && (chanmask&X51RearMask) == X5DOT1REAR); + case DevFmtMono: return ChannelMask_Mono; + case DevFmtStereo: return ChannelMask_Stereo; + case DevFmtQuad: return ChannelMask_Quad; + case DevFmtX51: return ChannelMask_X51; + case DevFmtX61: return ChannelMask_X61; + case DevFmtX3D71: [[fallthrough]]; + case DevFmtX71: return ChannelMask_X71; + case DevFmtX714: return ChannelMask_X714; + case DevFmtX7144: return ChannelMask_X7144; + case DevFmtAmbi3D: + break; } + return ChannelMask_Stereo; + }; - auto getTypeMask = [isRear51](DevFmtChannels chans) noexcept - { - switch(chans) - { - case DevFmtMono: return ChannelMask_Mono; - case DevFmtStereo: return ChannelMask_Stereo; - case DevFmtQuad: return ChannelMask_Quad; - case DevFmtX51: return isRear51 ? ChannelMask_X51Rear : ChannelMask_X51; - case DevFmtX61: return ChannelMask_X61; - case DevFmtX3D71: - case DevFmtX71: return ChannelMask_X71; - case DevFmtX714: return ChannelMask_X714; - case DevFmtAmbi3D: - break; - } - return ChannelMask_Stereo; - }; + SpatialAudioObjectRenderStreamActivationParams streamParams{}; + streamParams.ObjectFormat = &OutputType.Format; + streamParams.StaticObjectTypeMask = getTypeMask(mDevice->FmtChans); + streamParams.Category = AudioCategory_Media; + streamParams.EventHandle = mNotifyEvent; - SpatialAudioObjectRenderStreamActivationParams streamParams{}; - streamParams.ObjectFormat = &OutputType.Format; - streamParams.StaticObjectTypeMask = getTypeMask(mDevice->FmtChans); - streamParams.Category = AudioCategory_Media; - streamParams.EventHandle = mNotifyEvent; + PropVariant paramProp{}; + paramProp.setBlob({reinterpret_cast(&streamParams), sizeof(streamParams)}); - PropVariant paramProp{}; - paramProp->vt = VT_BLOB; - paramProp->blob.cbSize = sizeof(streamParams); - paramProp->blob.pBlobData = reinterpret_cast(&streamParams); + hr = audio.mClient->ActivateSpatialAudioStream(paramProp.get(), + __uuidof(ISpatialAudioObjectRenderStream), al::out_ptr(audio.mRender)); + if(FAILED(hr)) + { + ERR("Failed to activate spatial audio stream: {:#x}", as_unsigned(hr)); + return false; + } - hr = audio.mClient->ActivateSpatialAudioStream(paramProp.get(), - __uuidof(ISpatialAudioObjectRenderStream), al::out_ptr(audio.mRender)); - if(FAILED(hr)) - { - ERR("Failed to activate spatial audio stream: 0x%08lx\n", hr); - goto no_spatial; - } + audio.mStaticMask = streamParams.StaticObjectTypeMask; + mFormat = OutputType; - audio.mStaticMask = streamParams.StaticObjectTypeMask; - mFormat = OutputType; + mDevice->FmtType = DevFmtFloat; + mDevice->Flags.reset(DirectEar).set(Virtualization); + if(streamParams.StaticObjectTypeMask == ChannelMask_Stereo) + mDevice->FmtChans = DevFmtStereo; + if(!GetConfigValueBool(mDevice->mDeviceName, "wasapi", "allow-resampler", true)) + mDevice->mSampleRate = uint(OutputType.Format.nSamplesPerSec); + else + mDevice->mSampleRate = std::min(mDevice->mSampleRate, + uint(OutputType.Format.nSamplesPerSec)); - mDevice->FmtType = DevFmtFloat; - mDevice->Flags.reset(DirectEar).set(Virtualization); - if(streamParams.StaticObjectTypeMask == ChannelMask_Stereo) - mDevice->FmtChans = DevFmtStereo; - if(!GetConfigValueBool(mDevice->DeviceName.c_str(), "wasapi", "allow-resampler", true)) - mDevice->Frequency = OutputType.Format.nSamplesPerSec; - else - mDevice->Frequency = minu(mDevice->Frequency, OutputType.Format.nSamplesPerSec); - - setDefaultWFXChannelOrder(); - - /* FIXME: Get the real update and buffer size. Presumably the actual - * device is configured once ActivateSpatialAudioStream succeeds, and - * an IAudioClient from the same IMMDevice accesses the same device - * configuration. This isn't obviously correct, but for now assume - * IAudioClient::GetDevicePeriod returns the current device period time - * that ISpatialAudioObjectRenderStream will try to wake up at. - * - * Unfortunately this won't get the buffer size of the - * ISpatialAudioObjectRenderStream, so we only assume there's two - * periods. - */ - mOrigUpdateSize = mDevice->UpdateSize; - mOrigBufferSize = mOrigUpdateSize*2; - ReferenceTime per_time{ReferenceTime{seconds{mDevice->UpdateSize}} / mDevice->Frequency}; + setDefaultWFXChannelOrder(); - ComPtr tmpClient; - hr = sDeviceHelper->activateAudioClient(mMMDev, __uuidof(IAudioClient), - al::out_ptr(tmpClient)); + /* FIXME: Get the real update and buffer size. Presumably the actual device + * is configured once ActivateSpatialAudioStream succeeds, and an + * IAudioClient from the same IMMDevice accesses the same device + * configuration. This isn't obviously correct, but for now assume + * IAudioClient::GetDevicePeriod returns the current device period time + * that ISpatialAudioObjectRenderStream will try to wake up at. + * + * Unfortunately this won't get the buffer size of the + * ISpatialAudioObjectRenderStream, so we only assume there's two periods. + */ + mOutUpdateSize = mDevice->mUpdateSize; + mOutBufferSize = mOutUpdateSize*2; + ReferenceTime per_time{ReferenceTime{seconds{mDevice->mUpdateSize}} / mDevice->mSampleRate}; + + ComPtr tmpClient; + hr = helper.activateAudioClient(mmdev, __uuidof(IAudioClient), al::out_ptr(tmpClient)); + if(FAILED(hr)) + ERR("Failed to activate audio client: {:#x}", as_unsigned(hr)); + else + { + hr = tmpClient->GetDevicePeriod(&reinterpret_cast(per_time), nullptr); if(FAILED(hr)) - ERR("Failed to activate audio client: 0x%08lx\n", hr); + ERR("Failed to get device period: {:#x}", as_unsigned(hr)); else { - hr = tmpClient->GetDevicePeriod(&reinterpret_cast(per_time), nullptr); - if(FAILED(hr)) - ERR("Failed to get device period: 0x%08lx\n", hr); - else - { - mOrigUpdateSize = RefTime2Samples(per_time, mFormat.Format.nSamplesPerSec); - mOrigBufferSize = mOrigUpdateSize*2; - } + mOutUpdateSize = RefTime2Samples(per_time, mFormat.Format.nSamplesPerSec); + mOutBufferSize = mOutUpdateSize*2; } - tmpClient = nullptr; + } + tmpClient = nullptr; - mDevice->UpdateSize = RefTime2Samples(per_time, mDevice->Frequency); - mDevice->BufferSize = mDevice->UpdateSize*2; + mDevice->mUpdateSize = RefTime2Samples(per_time, mDevice->mSampleRate); + mDevice->mBufferSize = mDevice->mUpdateSize*2; - mResampler = nullptr; - mResampleBuffer = nullptr; - mBufferFilled = 0; - if(mDevice->Frequency != mFormat.Format.nSamplesPerSec) - { - const auto flags = as_unsigned(streamParams.StaticObjectTypeMask); - const auto channelCount = as_unsigned(al::popcount(flags)); - mResampler = SampleConverter::Create(mDevice->FmtType, mDevice->FmtType, - channelCount, mDevice->Frequency, mFormat.Format.nSamplesPerSec, - Resampler::FastBSinc24); - mResampleBuffer = std::make_unique(size_t{mDevice->UpdateSize} * channelCount * - mFormat.Format.wBitsPerSample / 8); + mResampler = nullptr; + mResampleBuffer.clear(); + mResampleBuffer.shrink_to_fit(); + mBufferFilled = 0; + if(mDevice->mSampleRate != mFormat.Format.nSamplesPerSec) + { + const auto flags = as_unsigned(al::to_underlying(streamParams.StaticObjectTypeMask)); + const auto channelCount = as_unsigned(al::popcount(flags)); + mResampler = SampleConverter::Create(mDevice->FmtType, mDevice->FmtType, channelCount, + mDevice->mSampleRate, mFormat.Format.nSamplesPerSec, Resampler::FastBSinc24); + mResampleBuffer.resize(size_t{mDevice->mUpdateSize} * channelCount * + mFormat.Format.wBitsPerSample / 8); - TRACE("Created converter for %s/%s format, dst: %luhz (%u), src: %uhz (%u)\n", - DevFmtChannelsString(mDevice->FmtChans), DevFmtTypeString(mDevice->FmtType), - mFormat.Format.nSamplesPerSec, mOrigUpdateSize, mDevice->Frequency, - mDevice->UpdateSize); - } + TRACE("Created converter for {}/{} format, dst: {}hz ({}), src: {}hz ({})", + DevFmtChannelsString(mDevice->FmtChans), DevFmtTypeString(mDevice->FmtType), + mFormat.Format.nSamplesPerSec, mOutUpdateSize, mDevice->mSampleRate, + mDevice->mUpdateSize); + } - return S_OK; + return true; +} + +bool WasapiPlayback::reset() +{ + auto plock = std::unique_lock{mProcMutex}; + if(mState != ThreadState::Waiting) + throw al::backend_exception{al::backend_error::DeviceError, "Invalid state: {}", + unsigned{al::to_underlying(mState)}}; + + mAction = ThreadAction::Configure; + mProcCond.notify_all(); + mProcCond.wait(plock, [this]() noexcept { return mAction != ThreadAction::Configure; }); + + if(FAILED(mProcResult) || mState != ThreadState::Waiting) + throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: {:#x}", + as_unsigned(mProcResult)}; + return true; +} + +auto WasapiPlayback::resetProxy(DeviceHelper &helper, DeviceHandle &mmdev, + std::variant &audiodev) -> HRESULT +{ + if(GetConfigValueBool(mDevice->mDeviceName, "wasapi", "spatial-api", false)) + { + if(initSpatial(helper, mmdev, audiodev.emplace())) + return S_OK; } -no_spatial: mDevice->Flags.reset(Virtualization); + mMonoUpsample = false; + mExclusiveMode = false; - auto &audio = mAudio.emplace(); - HRESULT hr{sDeviceHelper->activateAudioClient(mMMDev, __uuidof(IAudioClient), - al::out_ptr(audio.mClient))}; + auto &audio = audiodev.emplace(); + auto hr = helper.activateAudioClient(mmdev, __uuidof(IAudioClient), + al::out_ptr(audio.mClient)); if(FAILED(hr)) { - ERR("Failed to reactivate audio client: 0x%08lx\n", hr); + ERR("Failed to reactivate audio client: {:#x}", as_unsigned(hr)); return hr; } - WAVEFORMATEX *wfx; - hr = audio.mClient->GetMixFormat(&wfx); + auto wfx = unique_coptr{}; + hr = audio.mClient->GetMixFormat(al::out_ptr(wfx)); if(FAILED(hr)) { - ERR("Failed to get mix format: 0x%08lx\n", hr); + ERR("Failed to get mix format: {:#x}", as_unsigned(hr)); return hr; } - TraceFormat("Device mix format", wfx); - - WAVEFORMATEXTENSIBLE OutputType; - if(!MakeExtensible(&OutputType, wfx)) + TraceFormat("Device mix format", wfx.get()); + + auto OutputType = WAVEFORMATEXTENSIBLE{}; + if(!MakeExtensible(&OutputType, wfx.get())) + return E_FAIL; + wfx = nullptr; + + /* Get the buffer and update sizes as a ReferenceTime before potentially + * altering the sample rate. + */ + const auto buf_time = ReferenceTime{seconds{mDevice->mBufferSize}} / mDevice->mSampleRate; + const auto per_time = ReferenceTime{seconds{mDevice->mUpdateSize}} / mDevice->mSampleRate; + + /* Update the mDevice format for non-requested properties. */ + bool isRear51{false}; + if(!mDevice->Flags.test(FrequencyRequest)) + mDevice->mSampleRate = OutputType.Format.nSamplesPerSec; + if(!mDevice->Flags.test(ChannelsRequest)) + { + /* If not requesting a channel configuration, auto-select given what + * fits the mask's lsb (to ensure no gaps in the output channels). If + * there's no mask, we can only assume mono or stereo. + */ + const uint32_t chancount{OutputType.Format.nChannels}; + const DWORD chanmask{OutputType.dwChannelMask}; + if(chancount >= 12 && (chanmask&X714Mask) == X7DOT1DOT4) + mDevice->FmtChans = DevFmtX714; + else if(chancount >= 8 && (chanmask&X71Mask) == X7DOT1) + mDevice->FmtChans = DevFmtX71; + else if(chancount >= 7 && (chanmask&X61Mask) == X6DOT1) + mDevice->FmtChans = DevFmtX61; + else if(chancount >= 6 && (chanmask&X51Mask) == X5DOT1) + mDevice->FmtChans = DevFmtX51; + else if(chancount >= 6 && (chanmask&X51RearMask) == X5DOT1REAR) + { + mDevice->FmtChans = DevFmtX51; + isRear51 = true; + } + else if(chancount >= 4 && (chanmask&QuadMask) == QUAD) + mDevice->FmtChans = DevFmtQuad; + else if(chancount >= 2 && ((chanmask&StereoMask) == STEREO || !chanmask)) + mDevice->FmtChans = DevFmtStereo; + else if(chancount >= 1 && ((chanmask&MonoMask) == MONO || !chanmask)) + mDevice->FmtChans = DevFmtMono; + else + ERR("Unhandled channel config: {} -- {:#08x}", chancount, chanmask); + } + else + { + const uint32_t chancount{OutputType.Format.nChannels}; + const DWORD chanmask{OutputType.dwChannelMask}; + isRear51 = (chancount == 6 && (chanmask&X51RearMask) == X5DOT1REAR); + } + + /* Request a format matching the mDevice. */ + OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + switch(mDevice->FmtChans) + { + case DevFmtMono: + OutputType.Format.nChannels = 1; + OutputType.dwChannelMask = MONO; + break; + case DevFmtAmbi3D: + mDevice->FmtChans = DevFmtStereo; + [[fallthrough]]; + case DevFmtStereo: + OutputType.Format.nChannels = 2; + OutputType.dwChannelMask = STEREO; + break; + case DevFmtQuad: + OutputType.Format.nChannels = 4; + OutputType.dwChannelMask = QUAD; + break; + case DevFmtX51: + OutputType.Format.nChannels = 6; + OutputType.dwChannelMask = isRear51 ? X5DOT1REAR : X5DOT1; + break; + case DevFmtX61: + OutputType.Format.nChannels = 7; + OutputType.dwChannelMask = X6DOT1; + break; + case DevFmtX71: + case DevFmtX3D71: + OutputType.Format.nChannels = 8; + OutputType.dwChannelMask = X7DOT1; + break; + case DevFmtX7144: + mDevice->FmtChans = DevFmtX714; + [[fallthrough]]; + case DevFmtX714: + OutputType.Format.nChannels = 12; + OutputType.dwChannelMask = X7DOT1DOT4; + break; + } + switch(mDevice->FmtType) { - CoTaskMemFree(wfx); - return E_FAIL; + case DevFmtByte: + mDevice->FmtType = DevFmtUByte; + [[fallthrough]]; + case DevFmtUByte: + OutputType.Format.wBitsPerSample = 8; + OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + break; + case DevFmtUShort: + mDevice->FmtType = DevFmtShort; + [[fallthrough]]; + case DevFmtShort: + OutputType.Format.wBitsPerSample = 16; + OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + break; + case DevFmtUInt: + mDevice->FmtType = DevFmtInt; + [[fallthrough]]; + case DevFmtInt: + OutputType.Format.wBitsPerSample = 32; + OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + break; + case DevFmtFloat: + OutputType.Format.wBitsPerSample = 32; + OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + break; } - CoTaskMemFree(wfx); - wfx = nullptr; - - const ReferenceTime per_time{ReferenceTime{seconds{mDevice->UpdateSize}} / mDevice->Frequency}; - const ReferenceTime buf_time{ReferenceTime{seconds{mDevice->BufferSize}} / mDevice->Frequency}; + /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) */ + OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; + OutputType.Format.nSamplesPerSec = mDevice->mSampleRate; + OutputType.Format.nBlockAlign = static_cast(OutputType.Format.nChannels + * OutputType.Format.wBitsPerSample / 8); + OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec + * OutputType.Format.nBlockAlign; - prepareFormat(OutputType); + const auto sharemode = + GetConfigValueBool(mDevice->mDeviceName, "wasapi", "exclusive-mode", false) + ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED; + mExclusiveMode = (sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE); TraceFormat("Requesting playback format", &OutputType.Format); - hr = audio.mClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx); + hr = audio.mClient->IsFormatSupported(sharemode, &OutputType.Format, al::out_ptr(wfx)); if(FAILED(hr)) { - WARN("Failed to check format support: 0x%08lx\n", hr); - hr = audio.mClient->GetMixFormat(&wfx); + if(sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE) + { + /* For exclusive mode, IAudioClient::IsFormatSupported won't give + * back a supported format. However, a common failure is an + * unsupported sample type, so try a fallback to 16-bit int. + */ + if(hr == AUDCLNT_E_UNSUPPORTED_FORMAT && mDevice->FmtType != DevFmtShort) + { + mDevice->FmtType = DevFmtShort; + + OutputType.Format.wBitsPerSample = 16; + OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) */ + OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample; + OutputType.Format.nBlockAlign = static_cast(OutputType.Format.nChannels + * OutputType.Format.wBitsPerSample / 8); + OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec + * OutputType.Format.nBlockAlign; + + hr = audio.mClient->IsFormatSupported(sharemode, &OutputType.Format, + al::out_ptr(wfx)); + } + } + else + { + WARN("Failed to check format support: {:#x}", as_unsigned(hr)); + hr = audio.mClient->GetMixFormat(al::out_ptr(wfx)); + } } if(FAILED(hr)) { - ERR("Failed to find a supported format: 0x%08lx\n", hr); + ERR("Failed to find a supported format: {:#x}", as_unsigned(hr)); return hr; } - if(wfx != nullptr) + if(wfx) { - TraceFormat("Got playback format", wfx); - if(!MakeExtensible(&OutputType, wfx)) - { - CoTaskMemFree(wfx); + TraceFormat("Got playback format", wfx.get()); + if(!MakeExtensible(&OutputType, wfx.get())) return E_FAIL; - } - CoTaskMemFree(wfx); wfx = nullptr; finalizeFormat(OutputType); } mFormat = OutputType; -#if !defined(ALSOFT_UWP) - const EndpointFormFactor formfactor{GetDeviceFormfactor(mMMDev.get())}; +#if !ALSOFT_UWP + const EndpointFormFactor formfactor{GetDeviceFormfactor(mmdev.get())}; mDevice->Flags.set(DirectEar, (formfactor == Headphones || formfactor == Headset)); #else mDevice->Flags.set(DirectEar, false); #endif setDefaultWFXChannelOrder(); - hr = audio.mClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, - buf_time.count(), 0, &OutputType.Format, nullptr); + if(sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE) + { + auto period_time = per_time; + auto min_period = ReferenceTime{}; + hr = audio.mClient->GetDevicePeriod(nullptr, + &reinterpret_cast(min_period)); + if(FAILED(hr)) + ERR("Failed to get minimum period time: {:#x}", as_unsigned(hr)); + else if(min_period > period_time) + { + period_time = min_period; + WARN("Clamping to minimum period time, {}", nanoseconds{min_period}); + } + + hr = audio.mClient->Initialize(sharemode, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, + period_time.count(), period_time.count(), &OutputType.Format, nullptr); + if(hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) + { + auto newsize = UINT32{}; + hr = audio.mClient->GetBufferSize(&newsize); + if(SUCCEEDED(hr)) + { + period_time = ReferenceTime{seconds{newsize}} + / OutputType.Format.nSamplesPerSec; + WARN("Adjusting to supported period time, {}", nanoseconds{period_time}); + + audio.mClient = nullptr; + hr = helper.activateAudioClient(mmdev, __uuidof(IAudioClient), + al::out_ptr(audio.mClient)); + if(FAILED(hr)) + { + ERR("Failed to reactivate audio client: {:#x}", as_unsigned(hr)); + return hr; + } + hr = audio.mClient->Initialize(sharemode, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, + period_time.count(), period_time.count(), &OutputType.Format, nullptr); + } + } + } + else + hr = audio.mClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, + buf_time.count(), 0, &OutputType.Format, nullptr); if(FAILED(hr)) { - ERR("Failed to initialize audio client: 0x%08lx\n", hr); + ERR("Failed to initialize audio client: {:#x}", as_unsigned(hr)); return hr; } - UINT32 buffer_len{}; - ReferenceTime min_per{}; - hr = audio.mClient->GetDevicePeriod(&reinterpret_cast(min_per), nullptr); + auto buffer_len = UINT32{}; + auto period_time = ReferenceTime{}; + hr = audio.mClient->GetDevicePeriod(&reinterpret_cast(period_time), nullptr); if(SUCCEEDED(hr)) hr = audio.mClient->GetBufferSize(&buffer_len); if(FAILED(hr)) { - ERR("Failed to get audio buffer info: 0x%08lx\n", hr); + ERR("Failed to get audio buffer info: {:#x}", as_unsigned(hr)); return hr; } hr = audio.mClient->SetEventHandle(mNotifyEvent); if(FAILED(hr)) { - ERR("Failed to set event handle: 0x%08lx\n", hr); + ERR("Failed to set event handle: {:#x}", as_unsigned(hr)); return hr; } - /* Find the nearest multiple of the period size to the update size */ - if(min_per < per_time) - min_per *= maxi64((per_time + min_per/2) / min_per, 1); + hr = audio.mClient->GetService(__uuidof(IAudioRenderClient), al::out_ptr(audio.mRender)); + if(FAILED(hr)) + { + ERR("Failed to get IAudioRenderClient: {:#x}", as_unsigned(hr)); + return hr; + } - mOrigBufferSize = buffer_len; - mOrigUpdateSize = minu(RefTime2Samples(min_per, mFormat.Format.nSamplesPerSec), buffer_len/2); + mOutBufferSize = buffer_len; + if(sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE) + { + /* For exclusive mode, the buffer size is the update size, and there's + * implicitly two update periods on the device. + */ + mOutUpdateSize = buffer_len; + mDevice->mUpdateSize = static_cast(uint64_t{buffer_len} * mDevice->mSampleRate / + mFormat.Format.nSamplesPerSec); + mDevice->mBufferSize = mDevice->mUpdateSize * 2; + } + else + { + mOutUpdateSize = RefTime2Samples(period_time, mFormat.Format.nSamplesPerSec); - mDevice->BufferSize = static_cast(uint64_t{buffer_len} * mDevice->Frequency / - mFormat.Format.nSamplesPerSec); - mDevice->UpdateSize = minu(RefTime2Samples(min_per, mDevice->Frequency), - mDevice->BufferSize/2); + mDevice->mBufferSize = static_cast(uint64_t{buffer_len} * mDevice->mSampleRate / + mFormat.Format.nSamplesPerSec); + mDevice->mUpdateSize = std::min(RefTime2Samples(period_time, mDevice->mSampleRate), + mDevice->mBufferSize/2u); + } mResampler = nullptr; - mResampleBuffer = nullptr; + mResampleBuffer.clear(); + mResampleBuffer.shrink_to_fit(); mBufferFilled = 0; - if(mDevice->Frequency != mFormat.Format.nSamplesPerSec) + if(mDevice->mSampleRate != mFormat.Format.nSamplesPerSec) { mResampler = SampleConverter::Create(mDevice->FmtType, mDevice->FmtType, - mFormat.Format.nChannels, mDevice->Frequency, mFormat.Format.nSamplesPerSec, + mFormat.Format.nChannels, mDevice->mSampleRate, mFormat.Format.nSamplesPerSec, Resampler::FastBSinc24); - mResampleBuffer = std::make_unique(size_t{mDevice->UpdateSize} * - mFormat.Format.nChannels * mFormat.Format.wBitsPerSample / 8); + mResampleBuffer.resize(size_t{mDevice->mUpdateSize} * mFormat.Format.nChannels * + mFormat.Format.wBitsPerSample / 8); - TRACE("Created converter for %s/%s format, dst: %luhz (%u), src: %uhz (%u)\n", + TRACE("Created converter for {}/{} format, dst: {}hz ({}), src: {}hz ({})", DevFmtChannelsString(mDevice->FmtChans), DevFmtTypeString(mDevice->FmtType), - mFormat.Format.nSamplesPerSec, mOrigUpdateSize, mDevice->Frequency, - mDevice->UpdateSize); + mFormat.Format.nSamplesPerSec, mOutUpdateSize, mDevice->mSampleRate, + mDevice->mUpdateSize); } return hr; @@ -1990,143 +2285,90 @@ HRESULT WasapiPlayback::resetProxy() void WasapiPlayback::start() { - const HRESULT hr{pushMessage(MsgType::StartDevice).get()}; - if(FAILED(hr)) - throw al::backend_exception{al::backend_error::DeviceError, - "Failed to start playback: 0x%lx", hr}; -} - -HRESULT WasapiPlayback::startProxy() -{ - ResetEvent(mNotifyEvent); - - auto mstate_fallback = [](std::monostate) -> HRESULT - { return E_FAIL; }; - auto start_plain = [&](PlainDevice &audio) -> HRESULT - { - HRESULT hr{audio.mClient->Start()}; - if(FAILED(hr)) - { - ERR("Failed to start audio client: 0x%08lx\n", hr); - return hr; - } - - hr = audio.mClient->GetService(__uuidof(IAudioRenderClient), al::out_ptr(audio.mRender)); - if(SUCCEEDED(hr)) - { - try { - mKillNow.store(false, std::memory_order_release); - mThread = std::thread{std::mem_fn(&WasapiPlayback::mixerProc), this}; - } - catch(...) { - audio.mRender = nullptr; - ERR("Failed to start thread\n"); - hr = E_FAIL; - } - } - if(FAILED(hr)) - audio.mClient->Stop(); - return hr; - }; - auto start_spatial = [&](SpatialDevice &audio) -> HRESULT - { - HRESULT hr{audio.mRender->Start()}; - if(FAILED(hr)) - { - ERR("Failed to start spatial audio stream: 0x%08lx\n", hr); - return hr; - } - - try { - mKillNow.store(false, std::memory_order_release); - mThread = std::thread{std::mem_fn(&WasapiPlayback::mixerSpatialProc), this}; - } - catch(...) { - ERR("Failed to start thread\n"); - hr = E_FAIL; - } - - if(FAILED(hr)) - { - audio.mRender->Stop(); - audio.mRender->Reset(); - } - return hr; - }; - - return std::visit(overloaded{mstate_fallback, start_plain, start_spatial}, mAudio); + auto plock = std::unique_lock{mProcMutex}; + if(mState != ThreadState::Waiting) + throw al::backend_exception{al::backend_error::DeviceError, "Invalid state: {}", + unsigned{al::to_underlying(mState)}}; + + mAction = ThreadAction::Play; + mProcCond.notify_all(); + mProcCond.wait(plock, [this]() noexcept { return mAction != ThreadAction::Play; }); + + if(FAILED(mProcResult) || mState != ThreadState::Playing) + throw al::backend_exception{al::backend_error::DeviceError, "Device playback failed: {:#x}", + as_unsigned(mProcResult)}; } - void WasapiPlayback::stop() -{ pushMessage(MsgType::StopDevice).wait(); } - -void WasapiPlayback::stopProxy() { - if(!mThread.joinable()) - return; - - mKillNow.store(true, std::memory_order_release); - mThread.join(); - - auto mstate_fallback = [](std::monostate) -> void - { }; - auto stop_plain = [](PlainDevice &audio) -> void + auto plock = std::unique_lock{mProcMutex}; + if(mState == ThreadState::Playing) { - audio.mRender = nullptr; - audio.mClient->Stop(); - }; - auto stop_spatial = [](SpatialDevice &audio) -> void - { - audio.mRender->Stop(); - audio.mRender->Reset(); - }; - std::visit(overloaded{mstate_fallback, stop_plain, stop_spatial}, mAudio); + mKillNow = true; + mProcCond.wait(plock, [this]() noexcept { return mState != ThreadState::Playing; }); + } } ClockLatency WasapiPlayback::getClockLatency() { - ClockLatency ret; - - std::lock_guard _{mMutex}; - ret.ClockTime = GetDeviceClockTime(mDevice); + std::lock_guard dlock{mMutex}; + ClockLatency ret{}; + ret.ClockTime = mDevice->getClockTime(); ret.Latency = seconds{mPadding.load(std::memory_order_relaxed)}; ret.Latency /= mFormat.Format.nSamplesPerSec; if(mResampler) { auto extra = mResampler->currentInputDelay(); - ret.Latency += std::chrono::duration_cast(extra) / mDevice->Frequency; - ret.Latency += nanoseconds{seconds{mBufferFilled}} / mDevice->Frequency; + ret.Latency += std::chrono::duration_cast(extra) / mDevice->mSampleRate; + ret.Latency += nanoseconds{seconds{mBufferFilled}} / mDevice->mSampleRate; } return ret; } -struct WasapiCapture final : public BackendBase, WasapiProxy { - WasapiCapture(DeviceBase *device) noexcept : BackendBase{device} { } +struct WasapiCapture final : public BackendBase { + explicit WasapiCapture(DeviceBase *device) noexcept : BackendBase{device} { } ~WasapiCapture() override; - int recordProc(); + void recordProc(IAudioClient *client, IAudioCaptureClient *capture); - void open(std::string_view name) override; - HRESULT openProxy(std::string_view name) override; - void closeProxy() override; + void proc_thread(std::string&& name); - HRESULT resetProxy() override; + auto openProxy(const std::string_view name, DeviceHelper &helper, DeviceHandle &mmdev) + -> HRESULT; + auto resetProxy(DeviceHelper &helper, DeviceHandle &mmdev, ComPtr &client, + ComPtr &capture) -> HRESULT; + + void open(std::string_view name) override; void start() override; - HRESULT startProxy() override; void stop() override; - void stopProxy() override; - void captureSamples(std::byte *buffer, uint samples) override; uint availableSamples() override; - HRESULT mOpenStatus{E_FAIL}; - DeviceHandle mMMDev{nullptr}; - ComPtr mClient{nullptr}; - ComPtr mCapture{nullptr}; + + std::thread mProcThread; + std::mutex mProcMutex; + std::condition_variable mProcCond; + HRESULT mProcResult{E_FAIL}; + + enum class ThreadState : uint8_t { + Initializing, + Waiting, + Recording, + Done + }; + ThreadState mState{ThreadState::Initializing}; + + enum class ThreadAction : uint8_t { + Nothing, + Record, + Quit + }; + ThreadAction mAction{ThreadAction::Nothing}; + + HANDLE mNotifyEvent{nullptr}; ChannelConverter mChannelConv{}; @@ -2135,15 +2377,20 @@ struct WasapiCapture final : public BackendBase, WasapiProxy { std::atomic mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(WasapiCapture) }; WasapiCapture::~WasapiCapture() { - if(SUCCEEDED(mOpenStatus)) - pushMessage(MsgType::CloseDevice).wait(); - mOpenStatus = E_FAIL; + if(mProcThread.joinable()) + { + { + auto plock = std::lock_guard{mProcMutex}; + mKillNow = true; + mAction = ThreadAction::Quit; + } + mProcCond.notify_all(); + mProcThread.join(); + } if(mNotifyEvent != nullptr) CloseHandle(mNotifyEvent); @@ -2151,39 +2398,37 @@ WasapiCapture::~WasapiCapture() } -FORCE_ALIGN int WasapiCapture::recordProc() +FORCE_ALIGN void WasapiCapture::recordProc(IAudioClient *client, IAudioCaptureClient *capture) { - HRESULT hr{CoInitializeEx(nullptr, COINIT_MULTITHREADED)}; - if(FAILED(hr)) + ResetEvent(mNotifyEvent); + if(HRESULT hr{client->Start()}; FAILED(hr)) { - ERR("CoInitializeEx(nullptr, COINIT_MULTITHREADED) failed: 0x%08lx\n", hr); - mDevice->handleDisconnect("COM init failed: 0x%08lx", hr); - return 1; + ERR("Failed to start audio client: {:#x}", as_unsigned(hr)); + mDevice->handleDisconnect("Failed to start audio client: {:#x}", as_unsigned(hr)); + return; } - althrd_setname(RECORD_THREAD_NAME); - std::vector samples; while(!mKillNow.load(std::memory_order_relaxed)) { - UINT32 avail; - hr = mCapture->GetNextPacketSize(&avail); + auto avail = UINT32{}; + auto hr = capture->GetNextPacketSize(&avail); if(FAILED(hr)) - ERR("Failed to get next packet size: 0x%08lx\n", hr); + ERR("Failed to get next packet size: {:#x}", as_unsigned(hr)); else if(avail > 0) { - UINT32 numsamples; - DWORD flags; - BYTE *rdata; + auto numsamples = UINT32{}; + auto flags = DWORD{}; + BYTE *rdata{}; - hr = mCapture->GetBuffer(&rdata, &numsamples, &flags, nullptr, nullptr); + hr = capture->GetBuffer(&rdata, &numsamples, &flags, nullptr, nullptr); if(FAILED(hr)) - ERR("Failed to get capture buffer: 0x%08lx\n", hr); + ERR("Failed to get capture buffer: {:#x}", as_unsigned(hr)); else { if(mChannelConv.is_active()) { - samples.resize(numsamples*2); + samples.resize(numsamples*2_uz); mChannelConv.convert(rdata, samples.data(), numsamples); rdata = reinterpret_cast(samples.data()); } @@ -2193,94 +2438,185 @@ FORCE_ALIGN int WasapiCapture::recordProc() size_t dstframes; if(mSampleConv) { + static constexpr auto lenlimit = size_t{std::numeric_limits::max()}; const void *srcdata{rdata}; uint srcframes{numsamples}; - dstframes = mSampleConv->convert(&srcdata, &srcframes, data.first.buf, - static_cast(minz(data.first.len, INT_MAX))); - if(srcframes > 0 && dstframes == data.first.len && data.second.len > 0) + dstframes = mSampleConv->convert(&srcdata, &srcframes, data[0].buf, + static_cast(std::min(data[0].len, lenlimit))); + if(srcframes > 0 && dstframes == data[0].len && data[1].len > 0) { /* If some source samples remain, all of the first dest * block was filled, and there's space in the second * dest block, do another run for the second block. */ - dstframes += mSampleConv->convert(&srcdata, &srcframes, data.second.buf, - static_cast(minz(data.second.len, INT_MAX))); + dstframes += mSampleConv->convert(&srcdata, &srcframes, data[1].buf, + static_cast(std::min(data[1].len, lenlimit))); } } else { const uint framesize{mDevice->frameSizeFromFmt()}; - size_t len1{minz(data.first.len, numsamples)}; - size_t len2{minz(data.second.len, numsamples-len1)}; + auto dst = al::span{rdata, size_t{numsamples}*framesize}; + size_t len1{std::min(data[0].len, size_t{numsamples})}; + size_t len2{std::min(data[1].len, numsamples-len1)}; - memcpy(data.first.buf, rdata, len1*framesize); + memcpy(data[0].buf, dst.data(), len1*framesize); if(len2 > 0) - memcpy(data.second.buf, rdata+len1*framesize, len2*framesize); + { + dst = dst.subspan(len1*framesize); + memcpy(data[1].buf, dst.data(), len2*framesize); + } dstframes = len1 + len2; } mRing->writeAdvance(dstframes); - hr = mCapture->ReleaseBuffer(numsamples); - if(FAILED(hr)) ERR("Failed to release capture buffer: 0x%08lx\n", hr); + hr = capture->ReleaseBuffer(numsamples); + if(FAILED(hr)) ERR("Failed to release capture buffer: {:#x}", as_unsigned(hr)); } } if(FAILED(hr)) { - mDevice->handleDisconnect("Failed to capture samples: 0x%08lx", hr); + mDevice->handleDisconnect("Failed to capture samples: {:#x}", as_unsigned(hr)); break; } - DWORD res{WaitForSingleObjectEx(mNotifyEvent, 2000, FALSE)}; - if(res != WAIT_OBJECT_0) - ERR("WaitForSingleObjectEx error: 0x%lx\n", res); + if(DWORD res{WaitForSingleObjectEx(mNotifyEvent, 2000, FALSE)}; res != WAIT_OBJECT_0) + ERR("WaitForSingleObjectEx error: {:#x}", res); } - CoUninitialize(); - return 0; + client->Stop(); + client->Reset(); +} + + +void WasapiCapture::proc_thread(std::string&& name) +try { + auto com = ComWrapper{COINIT_MULTITHREADED}; + if(!com) + { + const auto hr = as_unsigned(com.status()); + ERR("CoInitializeEx(nullptr, COINIT_MULTITHREADED) failed: {:#x}", hr); + mDevice->handleDisconnect("COM init failed: {:#x}", hr); + + auto plock = std::lock_guard{mProcMutex}; + mProcResult = com.status(); + mState = ThreadState::Done; + mProcCond.notify_all(); + return; + } + + auto helper = DeviceHelper{}; + if(HRESULT hr{helper.init()}; FAILED(hr)) + { + mDevice->handleDisconnect("Helper init failed: {:#x}", as_unsigned(hr)); + + auto plock = std::lock_guard{mProcMutex}; + mProcResult = hr; + mState = ThreadState::Done; + mProcCond.notify_all(); + return; + } + + althrd_setname(GetRecordThreadName()); + + auto mmdev = DeviceHandle{nullptr}; + if(auto hr = openProxy(name, helper, mmdev); FAILED(hr)) + { + auto plock = std::lock_guard{mProcMutex}; + mProcResult = hr; + mState = ThreadState::Done; + mProcCond.notify_all(); + return; + } + + auto client = ComPtr{}; + auto capture = ComPtr{}; + if(auto hr = resetProxy(helper, mmdev, client, capture); FAILED(hr)) + { + auto plock = std::lock_guard{mProcMutex}; + mProcResult = hr; + mState = ThreadState::Done; + mProcCond.notify_all(); + return; + } + + auto plock = std::unique_lock{mProcMutex}; + mProcResult = S_OK; + while(mState != ThreadState::Done) + { + mAction = ThreadAction::Nothing; + mState = ThreadState::Waiting; + mProcCond.notify_all(); + + mProcCond.wait(plock, [this]() noexcept { return mAction != ThreadAction::Nothing; }); + switch(mAction) + { + case ThreadAction::Nothing: + break; + + case ThreadAction::Record: + mKillNow.store(false, std::memory_order_release); + + mAction = ThreadAction::Nothing; + mState = ThreadState::Recording; + mProcResult = S_OK; + plock.unlock(); + mProcCond.notify_all(); + + recordProc(client.get(), capture.get()); + + plock.lock(); + break; + + case ThreadAction::Quit: + mAction = ThreadAction::Nothing; + mState = ThreadState::Done; + mProcCond.notify_all(); + break; + } + } +} +catch(...) { + auto plock = std::lock_guard{mProcMutex}; + mProcResult = E_FAIL; + mAction = ThreadAction::Nothing; + mState = ThreadState::Done; + mProcCond.notify_all(); } void WasapiCapture::open(std::string_view name) { - if(SUCCEEDED(mOpenStatus)) - throw al::backend_exception{al::backend_error::DeviceError, - "Unexpected duplicate open call"}; - mNotifyEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr); if(mNotifyEvent == nullptr) { - ERR("Failed to create notify events: %lu\n", GetLastError()); + ERR("Failed to create notify events: {}", GetLastError()); throw al::backend_exception{al::backend_error::DeviceError, "Failed to create notify events"}; } - if(name.length() >= DevNameHeadLen - && std::strncmp(name.data(), DevNameHead, DevNameHeadLen) == 0) - { - name = name.substr(DevNameHeadLen); - } + mProcThread = std::thread{&WasapiCapture::proc_thread, this, std::string{name}}; - mOpenStatus = pushMessage(MsgType::OpenDevice, name).get(); - if(FAILED(mOpenStatus)) - throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: 0x%08lx", - mOpenStatus}; + auto plock = std::unique_lock{mProcMutex}; + mProcCond.wait(plock, [this]() noexcept { return mState != ThreadState::Initializing; }); - HRESULT hr{pushMessage(MsgType::ResetDevice).get()}; - if(FAILED(hr)) - { - if(hr == E_OUTOFMEMORY) - throw al::backend_exception{al::backend_error::OutOfMemory, "Out of memory"}; - throw al::backend_exception{al::backend_error::DeviceError, "Device reset failed"}; - } + if(mProcResult == E_NOTFOUND) + throw al::backend_exception{al::backend_error::NoDevice, "Device \"{}\" not found", name}; + if(mProcResult == E_OUTOFMEMORY) + throw al::backend_exception{al::backend_error::OutOfMemory, "Out of memory"}; + if(FAILED(mProcResult) || mState == ThreadState::Done) + throw al::backend_exception{al::backend_error::DeviceError, "Device init failed: {:#x}", + as_unsigned(mProcResult)}; } -HRESULT WasapiCapture::openProxy(std::string_view name) +auto WasapiCapture::openProxy(const std::string_view name, DeviceHelper &helper, + DeviceHandle &mmdev) -> HRESULT { - std::string devname; - std::wstring devid; + auto devname = std::string{}; + auto devid = std::wstring{}; if(!name.empty()) { auto devlock = DeviceListLock{gDeviceList}; @@ -2292,75 +2628,64 @@ HRESULT WasapiCapture::openProxy(std::string_view name) { const std::wstring wname{utf8_to_wstr(name)}; iter = std::find_if(devlist.cbegin(), devlist.cend(), - [&wname](const DevMap &entry) -> bool - { return entry.devid == wname; }); + [&wname](const DevMap &entry) -> bool { return entry.devid == wname; }); } if(iter == devlist.cend()) { - WARN("Failed to find device name matching \"%.*s\"\n", static_cast(name.length()), - name.data()); - return E_FAIL; + WARN("Failed to find device name matching \"{}\"", name); + return E_NOTFOUND; } devname = iter->name; devid = iter->devid; } - HRESULT hr{sDeviceHelper->openDevice(devid, eCapture, mMMDev)}; + auto hr = helper.openDevice(devid, eCapture, mmdev); if(FAILED(hr)) { - WARN("Failed to open device \"%s\"\n", devname.empty() ? "(default)" : devname.c_str()); + WARN("Failed to open device \"{}\"", devname.empty() + ? "(default)"sv : std::string_view{devname}); return hr; } - mClient = nullptr; if(!devname.empty()) - mDevice->DeviceName = DevNameHead + std::move(devname); + mDeviceName = std::move(devname); else - mDevice->DeviceName = DevNameHead + GetDeviceNameAndGuid(mMMDev).first; + mDeviceName = GetDeviceNameAndGuid(mmdev).mName; return S_OK; } -void WasapiCapture::closeProxy() -{ - mClient = nullptr; - mMMDev = nullptr; -} - -HRESULT WasapiCapture::resetProxy() +auto WasapiCapture::resetProxy(DeviceHelper &helper, DeviceHandle &mmdev, + ComPtr &client, ComPtr &capture) -> HRESULT { - mClient = nullptr; + capture = nullptr; + client = nullptr; - HRESULT hr{sDeviceHelper->activateAudioClient(mMMDev, __uuidof(IAudioClient), - al::out_ptr(mClient))}; + auto hr = helper.activateAudioClient(mmdev, __uuidof(IAudioClient), al::out_ptr(client)); if(FAILED(hr)) { - ERR("Failed to reactivate audio client: 0x%08lx\n", hr); + ERR("Failed to reactivate audio client: {:#x}", as_unsigned(hr)); return hr; } - WAVEFORMATEX *wfx; - hr = mClient->GetMixFormat(&wfx); + auto wfx = unique_coptr{}; + hr = client->GetMixFormat(al::out_ptr(wfx)); if(FAILED(hr)) { - ERR("Failed to get capture format: 0x%08lx\n", hr); + ERR("Failed to get capture format: {:#x}", as_unsigned(hr)); return hr; } - TraceFormat("Device capture format", wfx); + TraceFormat("Device capture format", wfx.get()); - WAVEFORMATEXTENSIBLE InputType{}; - if(!MakeExtensible(&InputType, wfx)) - { - CoTaskMemFree(wfx); + auto InputType = WAVEFORMATEXTENSIBLE{}; + if(!MakeExtensible(&InputType, wfx.get())) return E_FAIL; - } - CoTaskMemFree(wfx); wfx = nullptr; const bool isRear51{InputType.Format.nChannels == 6 && (InputType.dwChannelMask&X51RearMask) == X5DOT1REAR}; // Make sure buffer is at least 100ms in size - ReferenceTime buf_time{ReferenceTime{seconds{mDevice->BufferSize}} / mDevice->Frequency}; + ReferenceTime buf_time{ReferenceTime{seconds{mDevice->mBufferSize}} / mDevice->mSampleRate}; buf_time = std::max(buf_time, ReferenceTime{milliseconds{100}}); InputType = {}; @@ -2396,6 +2721,7 @@ HRESULT WasapiCapture::resetProxy() InputType.dwChannelMask = X7DOT1DOT4; break; + case DevFmtX7144: case DevFmtX3D71: case DevFmtAmbi3D: return E_FAIL; @@ -2423,8 +2749,9 @@ HRESULT WasapiCapture::resetProxy() InputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; break; } + /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) */ InputType.Samples.wValidBitsPerSample = InputType.Format.wBitsPerSample; - InputType.Format.nSamplesPerSec = mDevice->Frequency; + InputType.Format.nSamplesPerSec = mDevice->mSampleRate; InputType.Format.nBlockAlign = static_cast(InputType.Format.nChannels * InputType.Format.wBitsPerSample / 8); @@ -2433,15 +2760,15 @@ HRESULT WasapiCapture::resetProxy() InputType.Format.cbSize = sizeof(InputType) - sizeof(InputType.Format); TraceFormat("Requesting capture format", &InputType.Format); - hr = mClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, &InputType.Format, &wfx); + hr = client->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, &InputType.Format, al::out_ptr(wfx)); if(FAILED(hr)) { - WARN("Failed to check capture format support: 0x%08lx\n", hr); - hr = mClient->GetMixFormat(&wfx); + WARN("Failed to check capture format support: {:#x}", as_unsigned(hr)); + hr = client->GetMixFormat(al::out_ptr(wfx)); } if(FAILED(hr)) { - ERR("Failed to find a supported capture format: 0x%08lx\n", hr); + ERR("Failed to find a supported capture format: {:#x}", as_unsigned(hr)); return hr; } @@ -2450,13 +2777,9 @@ HRESULT WasapiCapture::resetProxy() if(wfx != nullptr) { - TraceFormat("Got capture format", wfx); - if(!MakeExtensible(&InputType, wfx)) - { - CoTaskMemFree(wfx); + TraceFormat("Got capture format", wfx.get()); + if(!MakeExtensible(&InputType, wfx.get())) return E_FAIL; - } - CoTaskMemFree(wfx); wfx = nullptr; auto validate_fmt = [](DeviceBase *device, uint32_t chancount, DWORD chanmask) noexcept @@ -2485,6 +2808,8 @@ HRESULT WasapiCapture::resetProxy() return (chancount == 8 && (chanmask == 0 || (chanmask&X71Mask) == X7DOT1)); case DevFmtX714: return (chancount == 12 && (chanmask == 0 || (chanmask&X714Mask) == X7DOT1DOT4)); + case DevFmtX7144: + return (chancount == 16 && chanmask == 0); case DevFmtAmbi3D: return (chanmask == 0 && chancount == device->channelsFromFmt()); } @@ -2492,9 +2817,9 @@ HRESULT WasapiCapture::resetProxy() }; if(!validate_fmt(mDevice, InputType.Format.nChannels, InputType.dwChannelMask)) { - ERR("Failed to match format, wanted: %s %s %uhz, got: 0x%08lx mask %d channel%s %d-bit %luhz\n", + ERR("Failed to match format, wanted: {} {} {}hz, got: {:#08x} mask {} channel{} {}-bit {}hz", DevFmtChannelsString(mDevice->FmtChans), DevFmtTypeString(mDevice->FmtType), - mDevice->Frequency, InputType.dwChannelMask, InputType.Format.nChannels, + mDevice->mSampleRate, InputType.dwChannelMask, InputType.Format.nChannels, (InputType.Format.nChannels==1)?"":"s", InputType.Format.wBitsPerSample, InputType.Format.nSamplesPerSec); return E_FAIL; @@ -2512,7 +2837,7 @@ HRESULT WasapiCapture::resetProxy() srcType = DevFmtInt; else { - ERR("Unhandled integer bit depth: %d\n", InputType.Format.wBitsPerSample); + ERR("Unhandled integer bit depth: {}", InputType.Format.wBitsPerSample); return E_FAIL; } } @@ -2522,13 +2847,13 @@ HRESULT WasapiCapture::resetProxy() srcType = DevFmtFloat; else { - ERR("Unhandled float bit depth: %d\n", InputType.Format.wBitsPerSample); + ERR("Unhandled float bit depth: {}", InputType.Format.wBitsPerSample); return E_FAIL; } } else { - ERR("Unhandled format sub-type: %s\n", GuidPrinter{InputType.SubFormat}.c_str()); + ERR("Unhandled format sub-type: {}", GuidPrinter{InputType.SubFormat}.str()); return E_FAIL; } @@ -2545,7 +2870,7 @@ HRESULT WasapiCapture::resetProxy() mChannelConv = ChannelConverter{srcType, InputType.Format.nChannels, chanmask, mDevice->FmtChans}; - TRACE("Created %s multichannel-to-mono converter\n", DevFmtTypeString(srcType)); + TRACE("Created {} multichannel-to-mono converter", DevFmtTypeString(srcType)); /* The channel converter always outputs float, so change the input type * for the resampler/type-converter. */ @@ -2554,54 +2879,61 @@ HRESULT WasapiCapture::resetProxy() else if(mDevice->FmtChans == DevFmtStereo && InputType.Format.nChannels == 1) { mChannelConv = ChannelConverter{srcType, 1, 0x1, mDevice->FmtChans}; - TRACE("Created %s mono-to-stereo converter\n", DevFmtTypeString(srcType)); + TRACE("Created {} mono-to-stereo converter", DevFmtTypeString(srcType)); srcType = DevFmtFloat; } - if(mDevice->Frequency != InputType.Format.nSamplesPerSec || mDevice->FmtType != srcType) + if(mDevice->mSampleRate != InputType.Format.nSamplesPerSec || mDevice->FmtType != srcType) { mSampleConv = SampleConverter::Create(srcType, mDevice->FmtType, - mDevice->channelsFromFmt(), InputType.Format.nSamplesPerSec, mDevice->Frequency, + mDevice->channelsFromFmt(), InputType.Format.nSamplesPerSec, mDevice->mSampleRate, Resampler::FastBSinc24); if(!mSampleConv) { - ERR("Failed to create converter for %s format, dst: %s %uhz, src: %s %luhz\n", + ERR("Failed to create converter for {} format, dst: {} {}hz, src: {} {}hz", DevFmtChannelsString(mDevice->FmtChans), DevFmtTypeString(mDevice->FmtType), - mDevice->Frequency, DevFmtTypeString(srcType), InputType.Format.nSamplesPerSec); + mDevice->mSampleRate, DevFmtTypeString(srcType), InputType.Format.nSamplesPerSec); return E_FAIL; } - TRACE("Created converter for %s format, dst: %s %uhz, src: %s %luhz\n", + TRACE("Created converter for {} format, dst: {} {}hz, src: {} {}hz", DevFmtChannelsString(mDevice->FmtChans), DevFmtTypeString(mDevice->FmtType), - mDevice->Frequency, DevFmtTypeString(srcType), InputType.Format.nSamplesPerSec); + mDevice->mSampleRate, DevFmtTypeString(srcType), InputType.Format.nSamplesPerSec); } - hr = mClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, + hr = client->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, buf_time.count(), 0, &InputType.Format, nullptr); if(FAILED(hr)) { - ERR("Failed to initialize audio client: 0x%08lx\n", hr); + ERR("Failed to initialize audio client: {:#x}", as_unsigned(hr)); + return hr; + } + + hr = client->GetService(__uuidof(IAudioCaptureClient), al::out_ptr(capture)); + if(FAILED(hr)) + { + ERR("Failed to get IAudioCaptureClient: {:#x}", as_unsigned(hr)); return hr; } UINT32 buffer_len{}; ReferenceTime min_per{}; - hr = mClient->GetDevicePeriod(&reinterpret_cast(min_per), nullptr); + hr = client->GetDevicePeriod(&reinterpret_cast(min_per), nullptr); if(SUCCEEDED(hr)) - hr = mClient->GetBufferSize(&buffer_len); + hr = client->GetBufferSize(&buffer_len); if(FAILED(hr)) { - ERR("Failed to get buffer size: 0x%08lx\n", hr); + ERR("Failed to get buffer size: {:#x}", as_unsigned(hr)); return hr; } - mDevice->UpdateSize = RefTime2Samples(min_per, mDevice->Frequency); - mDevice->BufferSize = buffer_len; + mDevice->mUpdateSize = RefTime2Samples(min_per, mDevice->mSampleRate); + mDevice->mBufferSize = buffer_len; mRing = RingBuffer::Create(buffer_len, mDevice->frameSizeFromFmt(), false); - hr = mClient->SetEventHandle(mNotifyEvent); + hr = client->SetEventHandle(mNotifyEvent); if(FAILED(hr)) { - ERR("Failed to set event handle: 0x%08lx\n", hr); + ERR("Failed to set event handle: {:#x}", as_unsigned(hr)); return hr; } @@ -2611,66 +2943,33 @@ HRESULT WasapiCapture::resetProxy() void WasapiCapture::start() { - const HRESULT hr{pushMessage(MsgType::StartDevice).get()}; - if(FAILED(hr)) - throw al::backend_exception{al::backend_error::DeviceError, - "Failed to start recording: 0x%lx", hr}; + auto plock = std::unique_lock{mProcMutex}; + if(mState != ThreadState::Waiting) + throw al::backend_exception{al::backend_error::DeviceError, "Invalid state: {}", + unsigned{al::to_underlying(mState)}}; + + mAction = ThreadAction::Record; + mProcCond.notify_all(); + mProcCond.wait(plock, [this]() noexcept { return mAction != ThreadAction::Record; }); + + if(FAILED(mProcResult) || mState != ThreadState::Recording) + throw al::backend_exception{al::backend_error::DeviceError, "Device capture failed: {:#x}", + as_unsigned(mProcResult)}; } -HRESULT WasapiCapture::startProxy() +void WasapiCapture::stop() { - ResetEvent(mNotifyEvent); - - HRESULT hr{mClient->Start()}; - if(FAILED(hr)) + auto plock = std::unique_lock{mProcMutex}; + if(mState == ThreadState::Recording) { - ERR("Failed to start audio client: 0x%08lx\n", hr); - return hr; - } - - hr = mClient->GetService(__uuidof(IAudioCaptureClient), al::out_ptr(mCapture)); - if(SUCCEEDED(hr)) - { - try { - mKillNow.store(false, std::memory_order_release); - mThread = std::thread{std::mem_fn(&WasapiCapture::recordProc), this}; - } - catch(...) { - mCapture = nullptr; - ERR("Failed to start thread\n"); - hr = E_FAIL; - } + mKillNow = true; + mProcCond.wait(plock, [this]() noexcept { return mState != ThreadState::Recording; }); } - - if(FAILED(hr)) - { - mClient->Stop(); - mClient->Reset(); - } - - return hr; -} - - -void WasapiCapture::stop() -{ pushMessage(MsgType::StopDevice).wait(); } - -void WasapiCapture::stopProxy() -{ - if(!mCapture || !mThread.joinable()) - return; - - mKillNow.store(true, std::memory_order_release); - mThread.join(); - - mCapture = nullptr; - mClient->Stop(); - mClient->Reset(); } void WasapiCapture::captureSamples(std::byte *buffer, uint samples) -{ mRing->read(buffer, samples); } +{ std::ignore = mRing->read(buffer, samples); } uint WasapiCapture::availableSamples() { return static_cast(mRing->readSpace()); } @@ -2686,7 +2985,7 @@ bool WasapiBackendFactory::init() std::promise promise; auto future = promise.get_future(); - std::thread{&WasapiProxy::messageHandler, &promise}.detach(); + std::thread{&DeviceEnumHelper::messageHandler, &promise}.detach(); InitResult = future.get(); } catch(...) { @@ -2698,9 +2997,9 @@ bool WasapiBackendFactory::init() bool WasapiBackendFactory::querySupport(BackendType type) { return type == BackendType::Playback || type == BackendType::Capture; } -std::string WasapiBackendFactory::probe(BackendType type) +auto WasapiBackendFactory::enumerate(BackendType type) -> std::vector { - std::string outnames; + std::vector outnames; auto devlock = DeviceListLock{gDeviceList}; switch(type) @@ -2712,15 +3011,11 @@ std::string WasapiBackendFactory::probe(BackendType type) { if(entry.devid != defaultId) { - /* +1 to also append the null char (to ensure a null- - * separated list and double-null terminated list). - */ - outnames.append(DevNameHead).append(entry.name.c_str(), entry.name.length()+1); + outnames.emplace_back(entry.name); continue; } /* Default device goes first. */ - std::string name{DevNameHead + entry.name}; - outnames.insert(0, name.c_str(), name.length()+1); + outnames.emplace(outnames.cbegin(), entry.name); } } break; @@ -2732,11 +3027,10 @@ std::string WasapiBackendFactory::probe(BackendType type) { if(entry.devid != defaultId) { - outnames.append(DevNameHead).append(entry.name.c_str(), entry.name.length()+1); + outnames.emplace_back(entry.name); continue; } - std::string name{DevNameHead + entry.name}; - outnames.insert(0, name.c_str(), name.length()+1); + outnames.emplace(outnames.cbegin(), entry.name); } } break; @@ -2759,3 +3053,22 @@ BackendFactory &WasapiBackendFactory::getFactory() static WasapiBackendFactory factory{}; return factory; } + +alc::EventSupport WasapiBackendFactory::queryEventSupport(alc::EventType eventType, BackendType) +{ + switch(eventType) + { + case alc::EventType::DefaultDeviceChanged: + return alc::EventSupport::FullSupport; + + case alc::EventType::DeviceAdded: + case alc::EventType::DeviceRemoved: +#if !ALSOFT_UWP + return alc::EventSupport::FullSupport; +#endif + + case alc::EventType::Count: + break; + } + return alc::EventSupport::NoSupport; +} diff --git a/3rdparty/openal/alc/backends/wasapi.h b/3rdparty/openal/alc/backends/wasapi.h index bb2671ee84a8..f3cb85413658 100644 --- a/3rdparty/openal/alc/backends/wasapi.h +++ b/3rdparty/openal/alc/backends/wasapi.h @@ -5,15 +5,17 @@ struct WasapiBackendFactory final : public BackendFactory { public: - bool init() override; + auto init() -> bool final; - bool querySupport(BackendType type) override; + auto querySupport(BackendType type) -> bool final; - std::string probe(BackendType type) override; + auto queryEventSupport(alc::EventType eventType, BackendType type) -> alc::EventSupport final; - BackendPtr createBackend(DeviceBase *device, BackendType type) override; + auto enumerate(BackendType type) -> std::vector final; - static BackendFactory &getFactory(); + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; + + static auto getFactory() -> BackendFactory&; }; #endif /* BACKENDS_WASAPI_H */ diff --git a/3rdparty/openal/alc/backends/wave.cpp b/3rdparty/openal/alc/backends/wave.cpp index 794d5cb87880..153e61452bf4 100644 --- a/3rdparty/openal/alc/backends/wave.cpp +++ b/3rdparty/openal/alc/backends/wave.cpp @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include @@ -40,14 +40,13 @@ #include "alnumeric.h" #include "althrd_setname.h" #include "core/device.h" -#include "core/helpers.h" #include "core/logging.h" -#include "opthelpers.h" #include "strutils.h" namespace { +using namespace std::string_view_literals; using std::chrono::seconds; using std::chrono::milliseconds; using std::chrono::nanoseconds; @@ -55,43 +54,48 @@ using std::chrono::nanoseconds; using ubyte = unsigned char; using ushort = unsigned short; -constexpr char waveDevice[] = "Wave File Writer"; +struct FileDeleter { + void operator()(gsl::owner f) { fclose(f); } +}; +using FilePtr = std::unique_ptr; + +[[nodiscard]] constexpr auto GetDeviceName() noexcept { return "Wave File Writer"sv; } -constexpr ubyte SUBTYPE_PCM[]{ +constexpr std::array SUBTYPE_PCM{{ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 -}; -constexpr ubyte SUBTYPE_FLOAT[]{ +}}; +constexpr std::array SUBTYPE_FLOAT{{ 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 -}; +}}; -constexpr ubyte SUBTYPE_BFORMAT_PCM[]{ +constexpr std::array SUBTYPE_BFORMAT_PCM{{ 0x01, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1, 0xca, 0x00, 0x00, 0x00 -}; +}}; -constexpr ubyte SUBTYPE_BFORMAT_FLOAT[]{ +constexpr std::array SUBTYPE_BFORMAT_FLOAT{{ 0x03, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1, 0xca, 0x00, 0x00, 0x00 -}; +}}; void fwrite16le(ushort val, FILE *f) { - ubyte data[2]{ static_cast(val&0xff), static_cast((val>>8)&0xff) }; - fwrite(data, 1, 2, f); + std::array data{static_cast(val&0xff), static_cast((val>>8)&0xff)}; + fwrite(data.data(), 1, data.size(), f); } void fwrite32le(uint val, FILE *f) { - ubyte data[4]{ static_cast(val&0xff), static_cast((val>>8)&0xff), - static_cast((val>>16)&0xff), static_cast((val>>24)&0xff) }; - fwrite(data, 1, 4, f); + std::array data{static_cast(val&0xff), static_cast((val>>8)&0xff), + static_cast((val>>16)&0xff), static_cast((val>>24)&0xff)}; + fwrite(data.data(), 1, data.size(), f); } struct WaveBackend final : public BackendBase { - WaveBackend(DeviceBase *device) noexcept : BackendBase{device} { } + explicit WaveBackend(DeviceBase *device) noexcept : BackendBase{device} { } ~WaveBackend() override; int mixerProc(); @@ -101,29 +105,22 @@ struct WaveBackend final : public BackendBase { void start() override; void stop() override; - FILE *mFile{nullptr}; + FilePtr mFile{nullptr}; long mDataStart{-1}; std::vector mBuffer; std::atomic mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(WaveBackend) }; -WaveBackend::~WaveBackend() -{ - if(mFile) - fclose(mFile); - mFile = nullptr; -} +WaveBackend::~WaveBackend() = default; int WaveBackend::mixerProc() { - const milliseconds restTime{mDevice->UpdateSize*1000/mDevice->Frequency / 2}; + const milliseconds restTime{mDevice->mUpdateSize*1000/mDevice->mSampleRate / 2}; - althrd_setname(MIXER_THREAD_NAME); + althrd_setname(GetMixerThreadName()); const size_t frameStep{mDevice->channelsFromFmt()}; const size_t frameSize{mDevice->frameSizeFromFmt()}; @@ -136,17 +133,17 @@ int WaveBackend::mixerProc() auto now = std::chrono::steady_clock::now(); /* This converts from nanoseconds to nanosamples, then to samples. */ - int64_t avail{std::chrono::duration_cast((now-start) * - mDevice->Frequency).count()}; - if(avail-done < mDevice->UpdateSize) + const auto avail = int64_t{std::chrono::duration_cast((now-start) * + mDevice->mSampleRate).count()}; + if(avail-done < mDevice->mUpdateSize) { std::this_thread::sleep_for(restTime); continue; } - while(avail-done >= mDevice->UpdateSize) + while(avail-done >= mDevice->mUpdateSize) { - mDevice->renderSamples(mBuffer.data(), mDevice->UpdateSize, frameStep); - done += mDevice->UpdateSize; + mDevice->renderSamples(mBuffer.data(), mDevice->mUpdateSize, frameStep); + done += mDevice->mUpdateSize; if(al::endian::native != al::endian::little) { @@ -169,10 +166,10 @@ int WaveBackend::mixerProc() } } - const size_t fs{fwrite(mBuffer.data(), frameSize, mDevice->UpdateSize, mFile)}; - if(fs < mDevice->UpdateSize || ferror(mFile)) + const size_t fs{fwrite(mBuffer.data(), frameSize, mDevice->mUpdateSize, mFile.get())}; + if(fs < mDevice->mUpdateSize || ferror(mFile.get())) { - ERR("Error writing to file\n"); + ERR("Error writing to file"); mDevice->handleDisconnect("Failed to write playback samples"); break; } @@ -183,10 +180,10 @@ int WaveBackend::mixerProc() * and current time from growing too large, while maintaining the * correct number of samples to render. */ - if(done >= mDevice->Frequency) + if(done >= mDevice->mSampleRate) { - seconds s{done/mDevice->Frequency}; - done %= mDevice->Frequency; + seconds s{done/mDevice->mSampleRate}; + done %= mDevice->mSampleRate; start += s; } } @@ -196,15 +193,15 @@ int WaveBackend::mixerProc() void WaveBackend::open(std::string_view name) { - auto fname = ConfigValueStr(nullptr, "wave", "file"); + auto fname = ConfigValueStr({}, "wave", "file"); if(!fname) throw al::backend_exception{al::backend_error::NoDevice, "No wave output filename"}; if(name.empty()) - name = waveDevice; - else if(name != waveDevice) - throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", - static_cast(name.length()), name.data()}; + name = GetDeviceName(); + else if(name != GetDeviceName()) + throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found", + name}; /* There's only one "device", so if it's already open, we're done. */ if(mFile) return; @@ -212,28 +209,21 @@ void WaveBackend::open(std::string_view name) #ifdef _WIN32 { std::wstring wname{utf8_to_wstr(fname.value())}; - mFile = _wfopen(wname.c_str(), L"wb"); + mFile = FilePtr{_wfopen(wname.c_str(), L"wb")}; } #else - mFile = fopen(fname->c_str(), "wb"); + mFile = FilePtr{fopen(fname->c_str(), "wb")}; #endif if(!mFile) - throw al::backend_exception{al::backend_error::DeviceError, "Could not open file '%s': %s", - fname->c_str(), strerror(errno)}; + throw al::backend_exception{al::backend_error::DeviceError, "Could not open file '{}': {}", + *fname, std::generic_category().message(errno)}; - mDevice->DeviceName = name; + mDeviceName = name; } bool WaveBackend::reset() { - uint channels{0}, bytes{0}, chanmask{0}; - bool isbformat{false}; - size_t val; - - fseek(mFile, 0, SEEK_SET); - clearerr(mFile); - - if(GetConfigValueBool(nullptr, "wave", "bformat", false)) + if(GetConfigValueBool({}, "wave", "bformat", false)) { mDevice->FmtChans = DevFmtAmbi3D; mDevice->mAmbiOrder = 1; @@ -256,6 +246,8 @@ bool WaveBackend::reset() case DevFmtFloat: break; } + auto chanmask = 0u; + auto isbformat = false; switch(mDevice->FmtChans) { case DevFmtMono: chanmask = 0x04; break; @@ -264,6 +256,9 @@ bool WaveBackend::reset() case DevFmtX51: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x200 | 0x400; break; case DevFmtX61: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x100 | 0x200 | 0x400; break; case DevFmtX71: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020 | 0x200 | 0x400; break; + case DevFmtX7144: + mDevice->FmtChans = DevFmtX714; + [[fallthrough]]; case DevFmtX714: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020 | 0x200 | 0x400 | 0x1000 | 0x4000 | 0x8000 | 0x20000; @@ -272,63 +267,73 @@ bool WaveBackend::reset() case DevFmtX3D71: chanmask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020 | 0x200 | 0x400; break; case DevFmtAmbi3D: /* .amb output requires FuMa */ - mDevice->mAmbiOrder = minu(mDevice->mAmbiOrder, 3); + mDevice->mAmbiOrder = std::min(mDevice->mAmbiOrder, 3u); mDevice->mAmbiLayout = DevAmbiLayout::FuMa; mDevice->mAmbiScale = DevAmbiScaling::FuMa; isbformat = true; chanmask = 0; break; } - bytes = mDevice->bytesFromFmt(); - channels = mDevice->channelsFromFmt(); + const auto bytes = mDevice->bytesFromFmt(); + const auto channels = mDevice->channelsFromFmt(); - rewind(mFile); + if(fseek(mFile.get(), 0, SEEK_CUR) != 0) + { + /* ESPIPE means the underlying file isn't seekable, which is fine for + * piped output. + */ + if(auto errcode = errno; errcode != ESPIPE) + { + ERR("Failed to reset file offset: {} ({})", std::generic_category().message(errcode), + errcode); + } + } + clearerr(mFile.get()); - fputs("RIFF", mFile); - fwrite32le(0xFFFFFFFF, mFile); // 'RIFF' header len; filled in at close + fputs("RIFF", mFile.get()); + fwrite32le(0xFFFFFFFF, mFile.get()); // 'RIFF' header len; filled in at stop - fputs("WAVE", mFile); + fputs("WAVE", mFile.get()); - fputs("fmt ", mFile); - fwrite32le(40, mFile); // 'fmt ' header len; 40 bytes for EXTENSIBLE + fputs("fmt ", mFile.get()); + fwrite32le(40, mFile.get()); // 'fmt ' header len; 40 bytes for EXTENSIBLE // 16-bit val, format type id (extensible: 0xFFFE) - fwrite16le(0xFFFE, mFile); + fwrite16le(0xFFFE, mFile.get()); // 16-bit val, channel count - fwrite16le(static_cast(channels), mFile); + fwrite16le(static_cast(channels), mFile.get()); // 32-bit val, frequency - fwrite32le(mDevice->Frequency, mFile); + fwrite32le(mDevice->mSampleRate, mFile.get()); // 32-bit val, bytes per second - fwrite32le(mDevice->Frequency * channels * bytes, mFile); + fwrite32le(mDevice->mSampleRate * channels * bytes, mFile.get()); // 16-bit val, frame size - fwrite16le(static_cast(channels * bytes), mFile); + fwrite16le(static_cast(channels * bytes), mFile.get()); // 16-bit val, bits per sample - fwrite16le(static_cast(bytes * 8), mFile); + fwrite16le(static_cast(bytes * 8), mFile.get()); // 16-bit val, extra byte count - fwrite16le(22, mFile); + fwrite16le(22, mFile.get()); // 16-bit val, valid bits per sample - fwrite16le(static_cast(bytes * 8), mFile); + fwrite16le(static_cast(bytes * 8), mFile.get()); // 32-bit val, channel mask - fwrite32le(chanmask, mFile); + fwrite32le(chanmask, mFile.get()); // 16 byte GUID, sub-type format - val = fwrite((mDevice->FmtType == DevFmtFloat) ? - (isbformat ? SUBTYPE_BFORMAT_FLOAT : SUBTYPE_FLOAT) : - (isbformat ? SUBTYPE_BFORMAT_PCM : SUBTYPE_PCM), 1, 16, mFile); - (void)val; + std::ignore = fwrite((mDevice->FmtType == DevFmtFloat) ? + (isbformat ? SUBTYPE_BFORMAT_FLOAT.data() : SUBTYPE_FLOAT.data()) : + (isbformat ? SUBTYPE_BFORMAT_PCM.data() : SUBTYPE_PCM.data()), 1, 16, mFile.get()); - fputs("data", mFile); - fwrite32le(0xFFFFFFFF, mFile); // 'data' header len; filled in at close + fputs("data", mFile.get()); + fwrite32le(0xFFFFFFFF, mFile.get()); // 'data' header len; filled in at stop - if(ferror(mFile)) + if(ferror(mFile.get())) { - ERR("Error writing header: %s\n", strerror(errno)); + ERR("Error writing header: {}", std::generic_category().message(errno)); return false; } - mDataStart = ftell(mFile); + mDataStart = ftell(mFile.get()); setDefaultWFXChannelOrder(); - const uint bufsize{mDevice->frameSizeFromFmt() * mDevice->UpdateSize}; + const uint bufsize{mDevice->frameSizeFromFmt() * mDevice->mUpdateSize}; mBuffer.resize(bufsize); return true; @@ -336,15 +341,15 @@ bool WaveBackend::reset() void WaveBackend::start() { - if(mDataStart > 0 && fseek(mFile, 0, SEEK_END) != 0) - WARN("Failed to seek on output file\n"); + if(mDataStart > 0 && fseek(mFile.get(), 0, SEEK_END) != 0) + WARN("Failed to seek on output file"); try { mKillNow.store(false, std::memory_order_release); - mThread = std::thread{std::mem_fn(&WaveBackend::mixerProc), this}; + mThread = std::thread{&WaveBackend::mixerProc, this}; } catch(std::exception& e) { throw al::backend_exception{al::backend_error::DeviceError, - "Failed to start mixing thread: %s", e.what()}; + "Failed to start mixing thread: {}", e.what()}; } } @@ -356,14 +361,14 @@ void WaveBackend::stop() if(mDataStart > 0) { - long size{ftell(mFile)}; + long size{ftell(mFile.get())}; if(size > 0) { long dataLen{size - mDataStart}; - if(fseek(mFile, 4, SEEK_SET) == 0) - fwrite32le(static_cast(size-8), mFile); // 'WAVE' header len - if(fseek(mFile, mDataStart-4, SEEK_SET) == 0) - fwrite32le(static_cast(dataLen), mFile); // 'data' header len + if(fseek(mFile.get(), 4, SEEK_SET) == 0) + fwrite32le(static_cast(size-8), mFile.get()); // 'WAVE' header len + if(fseek(mFile.get(), mDataStart-4, SEEK_SET) == 0) + fwrite32le(static_cast(dataLen), mFile.get()); // 'data' header len } } } @@ -377,19 +382,16 @@ bool WaveBackendFactory::init() bool WaveBackendFactory::querySupport(BackendType type) { return type == BackendType::Playback; } -std::string WaveBackendFactory::probe(BackendType type) +auto WaveBackendFactory::enumerate(BackendType type) -> std::vector { - std::string outnames; switch(type) { case BackendType::Playback: - /* Includes null char. */ - outnames.append(waveDevice, sizeof(waveDevice)); - break; + return std::vector{std::string{GetDeviceName()}}; case BackendType::Capture: break; } - return outnames; + return {}; } BackendPtr WaveBackendFactory::createBackend(DeviceBase *device, BackendType type) diff --git a/3rdparty/openal/alc/backends/wave.h b/3rdparty/openal/alc/backends/wave.h index e768d33617ca..85f4c76ff788 100644 --- a/3rdparty/openal/alc/backends/wave.h +++ b/3rdparty/openal/alc/backends/wave.h @@ -5,15 +5,15 @@ struct WaveBackendFactory final : public BackendFactory { public: - bool init() override; + auto init() -> bool final; - bool querySupport(BackendType type) override; + auto querySupport(BackendType type) -> bool final; - std::string probe(BackendType type) override; + auto enumerate(BackendType type) -> std::vector final; - BackendPtr createBackend(DeviceBase *device, BackendType type) override; + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; - static BackendFactory &getFactory(); + static auto getFactory() -> BackendFactory&; }; #endif /* BACKENDS_WAVE_H */ diff --git a/3rdparty/openal/alc/backends/winmm.cpp b/3rdparty/openal/alc/backends/winmm.cpp index f0fb0a1c2e0d..349a244de843 100644 --- a/3rdparty/openal/alc/backends/winmm.cpp +++ b/3rdparty/openal/alc/backends/winmm.cpp @@ -22,8 +22,8 @@ #include "winmm.h" -#include -#include +#include +#include #include #include @@ -36,7 +36,6 @@ #include #include #include -#include #include "alnumeric.h" #include "alsem.h" @@ -44,8 +43,10 @@ #include "core/device.h" #include "core/helpers.h" #include "core/logging.h" +#include "fmt/core.h" #include "ringbuffer.h" #include "strutils.h" +#include "vector.h" #ifndef WAVE_FORMAT_IEEE_FLOAT #define WAVE_FORMAT_IEEE_FLOAT 0x0003 @@ -53,16 +54,13 @@ namespace { -#define DEVNAME_HEAD "OpenAL Soft on " - - std::vector PlaybackDevices; std::vector CaptureDevices; bool checkName(const std::vector &list, const std::string &name) { return std::find(list.cbegin(), list.cend(), name) != list.cend(); } -void ProbePlaybackDevices(void) +void ProbePlaybackDevices() { PlaybackDevices.clear(); @@ -75,25 +73,21 @@ void ProbePlaybackDevices(void) WAVEOUTCAPSW WaveCaps{}; if(waveOutGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR) { - const std::string basename{DEVNAME_HEAD + wstr_to_utf8(WaveCaps.szPname)}; + const auto basename = wstr_to_utf8(std::data(WaveCaps.szPname)); - int count{1}; - std::string newname{basename}; + auto count = 1; + auto newname = basename; while(checkName(PlaybackDevices, newname)) - { - newname = basename; - newname += " #"; - newname += std::to_string(++count); - } + newname = fmt::format("{} #{}", basename, ++count); dname = std::move(newname); - TRACE("Got device \"%s\", ID %u\n", dname.c_str(), i); + TRACE("Got device \"{}\", ID {}", dname, i); } PlaybackDevices.emplace_back(std::move(dname)); } } -void ProbeCaptureDevices(void) +void ProbeCaptureDevices() { CaptureDevices.clear(); @@ -106,19 +100,15 @@ void ProbeCaptureDevices(void) WAVEINCAPSW WaveCaps{}; if(waveInGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR) { - const std::string basename{DEVNAME_HEAD + wstr_to_utf8(WaveCaps.szPname)}; + const auto basename = wstr_to_utf8(std::data(WaveCaps.szPname)); - int count{1}; - std::string newname{basename}; + auto count = 1; + auto newname = basename; while(checkName(CaptureDevices, newname)) - { - newname = basename; - newname += " #"; - newname += std::to_string(++count); - } + newname = fmt::format("{} #{}", basename, ++count); dname = std::move(newname); - TRACE("Got device \"%s\", ID %u\n", dname.c_str(), i); + TRACE("Got device \"{}\", ID {}", dname, i); } CaptureDevices.emplace_back(std::move(dname)); } @@ -126,7 +116,7 @@ void ProbeCaptureDevices(void) struct WinMMPlayback final : public BackendBase { - WinMMPlayback(DeviceBase *device) noexcept : BackendBase{device} { } + explicit WinMMPlayback(DeviceBase *device) noexcept : BackendBase{device} { } ~WinMMPlayback() override; void CALLBACK waveOutProc(HWAVEOUT device, UINT msg, DWORD_PTR param1, DWORD_PTR param2) noexcept; @@ -144,6 +134,7 @@ struct WinMMPlayback final : public BackendBase { al::semaphore mSem; uint mIdx{0u}; std::array mWaveBuffer{}; + al::vector mBuffer; HWAVEOUT mOutHdl{nullptr}; @@ -151,8 +142,6 @@ struct WinMMPlayback final : public BackendBase { std::atomic mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(WinMMPlayback) }; WinMMPlayback::~WinMMPlayback() @@ -160,9 +149,6 @@ WinMMPlayback::~WinMMPlayback() if(mOutHdl) waveOutClose(mOutHdl); mOutHdl = nullptr; - - al_free(mWaveBuffer[0].lpData); - std::fill(mWaveBuffer.begin(), mWaveBuffer.end(), WAVEHDR{}); } /* WinMMPlayback::waveOutProc @@ -180,7 +166,7 @@ void CALLBACK WinMMPlayback::waveOutProc(HWAVEOUT, UINT msg, DWORD_PTR, DWORD_PT FORCE_ALIGN int WinMMPlayback::mixerProc() { SetRTPriority(); - althrd_setname(MIXER_THREAD_NAME); + althrd_setname(GetMixerThreadName()); while(!mKillNow.load(std::memory_order_acquire) && mDevice->Connected.load(std::memory_order_acquire)) @@ -197,7 +183,7 @@ FORCE_ALIGN int WinMMPlayback::mixerProc() WAVEHDR &waveHdr = mWaveBuffer[widx]; if(++widx == mWaveBuffer.size()) widx = 0; - mDevice->renderSamples(waveHdr.lpData, mDevice->UpdateSize, mFormat.nChannels); + mDevice->renderSamples(waveHdr.lpData, mDevice->mUpdateSize, mFormat.nChannels); mWritable.fetch_sub(1, std::memory_order_acq_rel); waveOutWrite(mOutHdl, &waveHdr, sizeof(WAVEHDR)); } while(--todo); @@ -218,61 +204,57 @@ void WinMMPlayback::open(std::string_view name) std::find(PlaybackDevices.cbegin(), PlaybackDevices.cend(), name) : PlaybackDevices.cbegin(); if(iter == PlaybackDevices.cend()) - throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", - static_cast(name.length()), name.data()}; + throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found", + name}; auto DeviceID = static_cast(std::distance(PlaybackDevices.cbegin(), iter)); DevFmtType fmttype{mDevice->FmtType}; -retry_open: WAVEFORMATEX format{}; - if(fmttype == DevFmtFloat) - { - format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; - format.wBitsPerSample = 32; - } - else - { - format.wFormatTag = WAVE_FORMAT_PCM; - if(fmttype == DevFmtUByte || fmttype == DevFmtByte) - format.wBitsPerSample = 8; - else - format.wBitsPerSample = 16; - } - format.nChannels = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2); - format.nBlockAlign = static_cast(format.wBitsPerSample * format.nChannels / 8); - format.nSamplesPerSec = mDevice->Frequency; - format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; - format.cbSize = 0; - - HWAVEOUT outHandle{}; - MMRESULT res{waveOutOpen(&outHandle, DeviceID, &format, - reinterpret_cast(&WinMMPlayback::waveOutProcC), - reinterpret_cast(this), CALLBACK_FUNCTION)}; - if(res != MMSYSERR_NOERROR) - { + do { + format = WAVEFORMATEX{}; if(fmttype == DevFmtFloat) { - fmttype = DevFmtShort; - goto retry_open; + format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; + format.wBitsPerSample = 32; } - throw al::backend_exception{al::backend_error::DeviceError, "waveOutOpen failed: %u", res}; - } + else + { + format.wFormatTag = WAVE_FORMAT_PCM; + if(fmttype == DevFmtUByte || fmttype == DevFmtByte) + format.wBitsPerSample = 8; + else + format.wBitsPerSample = 16; + } + format.nChannels = ((mDevice->FmtChans == DevFmtMono) ? 1 : 2); + format.nBlockAlign = static_cast(format.wBitsPerSample * format.nChannels / 8); + format.nSamplesPerSec = mDevice->mSampleRate; + format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; + format.cbSize = 0; + + MMRESULT res{waveOutOpen(&mOutHdl, DeviceID, &format, + reinterpret_cast(&WinMMPlayback::waveOutProcC), + reinterpret_cast(this), CALLBACK_FUNCTION)}; + if(res == MMSYSERR_NOERROR) break; + + if(fmttype != DevFmtFloat) + throw al::backend_exception{al::backend_error::DeviceError, "waveOutOpen failed: {}", + res}; + + fmttype = DevFmtShort; + } while(true); - if(mOutHdl) - waveOutClose(mOutHdl); - mOutHdl = outHandle; mFormat = format; - mDevice->DeviceName = PlaybackDevices[DeviceID]; + mDeviceName = PlaybackDevices[DeviceID]; } bool WinMMPlayback::reset() { - mDevice->BufferSize = static_cast(uint64_t{mDevice->BufferSize} * - mFormat.nSamplesPerSec / mDevice->Frequency); - mDevice->BufferSize = (mDevice->BufferSize+3) & ~0x3u; - mDevice->UpdateSize = mDevice->BufferSize / 4; - mDevice->Frequency = mFormat.nSamplesPerSec; + mDevice->mBufferSize = static_cast(uint64_t{mDevice->mBufferSize} * + mFormat.nSamplesPerSec / mDevice->mSampleRate); + mDevice->mBufferSize = (mDevice->mBufferSize+3) & ~0x3u; + mDevice->mUpdateSize = mDevice->mBufferSize / 4; + mDevice->mSampleRate = mFormat.nSamplesPerSec; if(mFormat.wFormatTag == WAVE_FORMAT_IEEE_FLOAT) { @@ -280,7 +262,7 @@ bool WinMMPlayback::reset() mDevice->FmtType = DevFmtFloat; else { - ERR("Unhandled IEEE float sample depth: %d\n", mFormat.wBitsPerSample); + ERR("Unhandled IEEE float sample depth: {}", mFormat.wBitsPerSample); return false; } } @@ -292,13 +274,13 @@ bool WinMMPlayback::reset() mDevice->FmtType = DevFmtUByte; else { - ERR("Unhandled PCM sample depth: %d\n", mFormat.wBitsPerSample); + ERR("Unhandled PCM sample depth: {}", mFormat.wBitsPerSample); return false; } } else { - ERR("Unhandled format tag: 0x%04x\n", mFormat.wFormatTag); + ERR("Unhandled format tag: {:#04x}", as_unsigned(mFormat.wFormatTag)); return false; } @@ -308,16 +290,16 @@ bool WinMMPlayback::reset() mDevice->FmtChans = DevFmtMono; else { - ERR("Unhandled channel count: %d\n", mFormat.nChannels); + ERR("Unhandled channel count: {}", mFormat.nChannels); return false; } setDefaultWFXChannelOrder(); - uint BufferSize{mDevice->UpdateSize * mFormat.nChannels * mDevice->bytesFromFmt()}; + const uint BufferSize{mDevice->mUpdateSize * mFormat.nChannels * mDevice->bytesFromFmt()}; - al_free(mWaveBuffer[0].lpData); + decltype(mBuffer)(BufferSize*mWaveBuffer.size()).swap(mBuffer); mWaveBuffer[0] = WAVEHDR{}; - mWaveBuffer[0].lpData = static_cast(al_calloc(16, BufferSize * mWaveBuffer.size())); + mWaveBuffer[0].lpData = mBuffer.data(); mWaveBuffer[0].dwBufferLength = BufferSize; for(size_t i{1};i < mWaveBuffer.size();i++) { @@ -338,11 +320,11 @@ void WinMMPlayback::start() mWritable.store(static_cast(mWaveBuffer.size()), std::memory_order_release); mKillNow.store(false, std::memory_order_release); - mThread = std::thread{std::mem_fn(&WinMMPlayback::mixerProc), this}; + mThread = std::thread{&WinMMPlayback::mixerProc, this}; } catch(std::exception& e) { throw al::backend_exception{al::backend_error::DeviceError, - "Failed to start mixing thread: %s", e.what()}; + "Failed to start mixing thread: {}", e.what()}; } } @@ -361,7 +343,7 @@ void WinMMPlayback::stop() struct WinMMCapture final : public BackendBase { - WinMMCapture(DeviceBase *device) noexcept : BackendBase{device} { } + explicit WinMMCapture(DeviceBase *device) noexcept : BackendBase{device} { } ~WinMMCapture() override; void CALLBACK waveInProc(HWAVEIN device, UINT msg, DWORD_PTR param1, DWORD_PTR param2) noexcept; @@ -380,6 +362,7 @@ struct WinMMCapture final : public BackendBase { al::semaphore mSem; uint mIdx{0}; std::array mWaveBuffer{}; + al::vector mBuffer; HWAVEIN mInHdl{nullptr}; @@ -389,8 +372,6 @@ struct WinMMCapture final : public BackendBase { std::atomic mKillNow{true}; std::thread mThread; - - DEF_NEWDEL(WinMMCapture) }; WinMMCapture::~WinMMCapture() @@ -399,9 +380,6 @@ WinMMCapture::~WinMMCapture() if(mInHdl) waveInClose(mInHdl); mInHdl = nullptr; - - al_free(mWaveBuffer[0].lpData); - std::fill(mWaveBuffer.begin(), mWaveBuffer.end(), WAVEHDR{}); } /* WinMMCapture::waveInProc @@ -418,7 +396,7 @@ void CALLBACK WinMMCapture::waveInProc(HWAVEIN, UINT msg, DWORD_PTR, DWORD_PTR) int WinMMCapture::captureProc() { - althrd_setname(RECORD_THREAD_NAME); + althrd_setname(GetRecordThreadName()); while(!mKillNow.load(std::memory_order_acquire) && mDevice->Connected.load(std::memory_order_acquire)) @@ -435,7 +413,8 @@ int WinMMCapture::captureProc() WAVEHDR &waveHdr = mWaveBuffer[widx]; widx = (widx+1) % mWaveBuffer.size(); - mRing->write(waveHdr.lpData, waveHdr.dwBytesRecorded / mFormat.nBlockAlign); + std::ignore = mRing->write(waveHdr.lpData, + waveHdr.dwBytesRecorded / mFormat.nBlockAlign); mReadable.fetch_sub(1, std::memory_order_acq_rel); waveInAddBuffer(mInHdl, &waveHdr, sizeof(WAVEHDR)); } while(--todo); @@ -456,8 +435,8 @@ void WinMMCapture::open(std::string_view name) std::find(CaptureDevices.cbegin(), CaptureDevices.cend(), name) : CaptureDevices.cbegin(); if(iter == CaptureDevices.cend()) - throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%.*s\" not found", - static_cast(name.length()), name.data()}; + throw al::backend_exception{al::backend_error::NoDevice, "Device name \"{}\" not found", + name}; auto DeviceID = static_cast(std::distance(CaptureDevices.cbegin(), iter)); switch(mDevice->FmtChans) @@ -471,9 +450,10 @@ void WinMMCapture::open(std::string_view name) case DevFmtX61: case DevFmtX71: case DevFmtX714: + case DevFmtX7144: case DevFmtX3D71: case DevFmtAmbi3D: - throw al::backend_exception{al::backend_error::DeviceError, "%s capture not supported", + throw al::backend_exception{al::backend_error::DeviceError, "{} capture not supported", DevFmtChannelsString(mDevice->FmtChans)}; } @@ -488,7 +468,7 @@ void WinMMCapture::open(std::string_view name) case DevFmtByte: case DevFmtUShort: case DevFmtUInt: - throw al::backend_exception{al::backend_error::DeviceError, "%s samples not supported", + throw al::backend_exception{al::backend_error::DeviceError, "{} samples not supported", DevFmtTypeString(mDevice->FmtType)}; } @@ -498,7 +478,7 @@ void WinMMCapture::open(std::string_view name) mFormat.nChannels = static_cast(mDevice->channelsFromFmt()); mFormat.wBitsPerSample = static_cast(mDevice->bytesFromFmt() * 8); mFormat.nBlockAlign = static_cast(mFormat.wBitsPerSample * mFormat.nChannels / 8); - mFormat.nSamplesPerSec = mDevice->Frequency; + mFormat.nSamplesPerSec = mDevice->mSampleRate; mFormat.nAvgBytesPerSec = mFormat.nSamplesPerSec * mFormat.nBlockAlign; mFormat.cbSize = 0; @@ -506,7 +486,7 @@ void WinMMCapture::open(std::string_view name) reinterpret_cast(&WinMMCapture::waveInProcC), reinterpret_cast(this), CALLBACK_FUNCTION)}; if(res != MMSYSERR_NOERROR) - throw al::backend_exception{al::backend_error::DeviceError, "waveInOpen failed: %u", res}; + throw al::backend_exception{al::backend_error::DeviceError, "waveInOpen failed: {}", res}; // Ensure each buffer is 50ms each DWORD BufferSize{mFormat.nAvgBytesPerSec / 20u}; @@ -514,14 +494,14 @@ void WinMMCapture::open(std::string_view name) // Allocate circular memory buffer for the captured audio // Make sure circular buffer is at least 100ms in size - uint CapturedDataSize{mDevice->BufferSize}; - CapturedDataSize = static_cast(maxz(CapturedDataSize, BufferSize*mWaveBuffer.size())); + const auto CapturedDataSize = std::max(mDevice->mBufferSize, + BufferSize*mWaveBuffer.size()); mRing = RingBuffer::Create(CapturedDataSize, mFormat.nBlockAlign, false); - al_free(mWaveBuffer[0].lpData); + decltype(mBuffer)(BufferSize*mWaveBuffer.size()).swap(mBuffer); mWaveBuffer[0] = WAVEHDR{}; - mWaveBuffer[0].lpData = static_cast(al_calloc(16, BufferSize * mWaveBuffer.size())); + mWaveBuffer[0].lpData = mBuffer.data(); mWaveBuffer[0].dwBufferLength = BufferSize; for(size_t i{1};i < mWaveBuffer.size();++i) { @@ -530,7 +510,7 @@ void WinMMCapture::open(std::string_view name) mWaveBuffer[i].dwBufferLength = mWaveBuffer[i-1].dwBufferLength; } - mDevice->DeviceName = CaptureDevices[DeviceID]; + mDeviceName = CaptureDevices[DeviceID]; } void WinMMCapture::start() @@ -543,13 +523,13 @@ void WinMMCapture::start() } mKillNow.store(false, std::memory_order_release); - mThread = std::thread{std::mem_fn(&WinMMCapture::captureProc), this}; + mThread = std::thread{&WinMMCapture::captureProc, this}; waveInStart(mInHdl); } catch(std::exception& e) { throw al::backend_exception{al::backend_error::DeviceError, - "Failed to start recording thread: %s", e.what()}; + "Failed to start recording thread: {}", e.what()}; } } @@ -573,7 +553,7 @@ void WinMMCapture::stop() } void WinMMCapture::captureSamples(std::byte *buffer, uint samples) -{ mRing->read(buffer, samples); } +{ std::ignore = mRing->read(buffer, samples); } uint WinMMCapture::availableSamples() { return static_cast(mRing->readSpace()); } @@ -587,26 +567,23 @@ bool WinMMBackendFactory::init() bool WinMMBackendFactory::querySupport(BackendType type) { return type == BackendType::Playback || type == BackendType::Capture; } -std::string WinMMBackendFactory::probe(BackendType type) +auto WinMMBackendFactory::enumerate(BackendType type) -> std::vector { - std::string outnames; + std::vector outnames; auto add_device = [&outnames](const std::string &dname) -> void - { - /* +1 to also append the null char (to ensure a null-separated list and - * double-null terminated list). - */ - if(!dname.empty()) - outnames.append(dname.c_str(), dname.length()+1); - }; + { if(!dname.empty()) outnames.emplace_back(dname); }; + switch(type) { case BackendType::Playback: ProbePlaybackDevices(); + outnames.reserve(PlaybackDevices.size()); std::for_each(PlaybackDevices.cbegin(), PlaybackDevices.cend(), add_device); break; case BackendType::Capture: ProbeCaptureDevices(); + outnames.reserve(CaptureDevices.size()); std::for_each(CaptureDevices.cbegin(), CaptureDevices.cend(), add_device); break; } diff --git a/3rdparty/openal/alc/backends/winmm.h b/3rdparty/openal/alc/backends/winmm.h index 45a706aa341e..51ce432ca346 100644 --- a/3rdparty/openal/alc/backends/winmm.h +++ b/3rdparty/openal/alc/backends/winmm.h @@ -5,15 +5,15 @@ struct WinMMBackendFactory final : public BackendFactory { public: - bool init() override; + auto init() -> bool final; - bool querySupport(BackendType type) override; + auto querySupport(BackendType type) -> bool final; - std::string probe(BackendType type) override; + auto enumerate(BackendType type) -> std::vector final; - BackendPtr createBackend(DeviceBase *device, BackendType type) override; + auto createBackend(DeviceBase *device, BackendType type) -> BackendPtr final; - static BackendFactory &getFactory(); + static auto getFactory() -> BackendFactory&; }; #endif /* BACKENDS_WINMM_H */ diff --git a/3rdparty/openal/alc/context.cpp b/3rdparty/openal/alc/context.cpp index 8c930056fdaf..79206fb43632 100644 --- a/3rdparty/openal/alc/context.cpp +++ b/3rdparty/openal/alc/context.cpp @@ -5,13 +5,13 @@ #include #include -#include +#include #include -#include +#include #include -#include -#include +#include #include +#include #include #include "AL/efx.h" @@ -24,75 +24,78 @@ #include "al/listener.h" #include "albit.h" #include "alc/alu.h" +#include "alc/backends/base.h" +#include "alnumeric.h" #include "alspan.h" +#include "atomic.h" #include "core/async_event.h" +#include "core/devformat.h" #include "core/device.h" #include "core/effectslot.h" #include "core/logging.h" -#include "core/voice.h" #include "core/voice_change.h" #include "device.h" +#include "flexarray.h" #include "ringbuffer.h" #include "vecmat.h" -#ifdef ALSOFT_EAX -#include -#include "alstring.h" +#if ALSOFT_EAX +#include "al/eax/call.h" #include "al/eax/globals.h" #endif // ALSOFT_EAX namespace { -using namespace std::placeholders; - +using namespace std::string_view_literals; using voidp = void*; /* Default context extensions */ std::vector getContextExtensions() noexcept { return std::vector{ - "AL_EXT_ALAW", - "AL_EXT_BFORMAT", - "AL_EXT_debug", - "AL_EXTX_direct_context", - "AL_EXT_DOUBLE", - "AL_EXT_EXPONENT_DISTANCE", - "AL_EXT_FLOAT32", - "AL_EXT_IMA4", - "AL_EXT_LINEAR_DISTANCE", - "AL_EXT_MCFORMATS", - "AL_EXT_MULAW", - "AL_EXT_MULAW_BFORMAT", - "AL_EXT_MULAW_MCFORMATS", - "AL_EXT_OFFSET", - "AL_EXT_source_distance_model", - "AL_EXT_SOURCE_RADIUS", - "AL_EXT_STATIC_BUFFER", - "AL_EXT_STEREO_ANGLES", - "AL_LOKI_quadriphonic", - "AL_SOFT_bformat_ex", - "AL_SOFTX_bformat_hoa", - "AL_SOFT_block_alignment", - "AL_SOFT_buffer_length_query", - "AL_SOFT_callback_buffer", - "AL_SOFTX_convolution_reverb", - "AL_SOFT_deferred_updates", - "AL_SOFT_direct_channels", - "AL_SOFT_direct_channels_remix", - "AL_SOFT_effect_target", - "AL_SOFT_events", - "AL_SOFT_gain_clamp_ex", - "AL_SOFTX_hold_on_disconnect", - "AL_SOFT_loop_points", - "AL_SOFTX_map_buffer", - "AL_SOFT_MSADPCM", - "AL_SOFT_source_latency", - "AL_SOFT_source_length", - "AL_SOFT_source_resampler", - "AL_SOFT_source_spatialize", - "AL_SOFT_source_start_delay", - "AL_SOFT_UHJ", - "AL_SOFT_UHJ_ex", + "AL_EXT_ALAW"sv, + "AL_EXT_BFORMAT"sv, + "AL_EXT_debug"sv, + "AL_EXT_direct_context"sv, + "AL_EXT_DOUBLE"sv, + "AL_EXT_EXPONENT_DISTANCE"sv, + "AL_EXT_FLOAT32"sv, + "AL_EXT_IMA4"sv, + "AL_EXT_LINEAR_DISTANCE"sv, + "AL_EXT_MCFORMATS"sv, + "AL_EXT_MULAW"sv, + "AL_EXT_MULAW_BFORMAT"sv, + "AL_EXT_MULAW_MCFORMATS"sv, + "AL_EXT_OFFSET"sv, + "AL_EXT_source_distance_model"sv, + "AL_EXT_SOURCE_RADIUS"sv, + "AL_EXT_STATIC_BUFFER"sv, + "AL_EXT_STEREO_ANGLES"sv, + "AL_LOKI_quadriphonic"sv, + "AL_SOFT_bformat_ex"sv, + "AL_SOFT_bformat_hoa"sv, + "AL_SOFT_block_alignment"sv, + "AL_SOFT_buffer_length_query"sv, + "AL_SOFT_callback_buffer"sv, + "AL_SOFTX_convolution_effect"sv, + "AL_SOFT_deferred_updates"sv, + "AL_SOFT_direct_channels"sv, + "AL_SOFT_direct_channels_remix"sv, + "AL_SOFT_effect_target"sv, + "AL_SOFT_events"sv, + "AL_SOFT_gain_clamp_ex"sv, + "AL_SOFTX_hold_on_disconnect"sv, + "AL_SOFT_loop_points"sv, + "AL_SOFTX_map_buffer"sv, + "AL_SOFT_MSADPCM"sv, + "AL_SOFT_source_latency"sv, + "AL_SOFT_source_length"sv, + "AL_SOFTX_source_panning"sv, + "AL_SOFT_source_resampler"sv, + "AL_SOFT_source_spatialize"sv, + "AL_SOFT_source_start_delay"sv, + "AL_SOFT_UHJ"sv, + "AL_SOFT_UHJ_ex"sv, }; } @@ -107,7 +110,7 @@ ALCcontext::ThreadCtx::~ThreadCtx() if(ALCcontext *ctx{std::exchange(ALCcontext::sLocalContext, nullptr)}) { const bool result{ctx->releaseIfNoDelete()}; - ERR("Context %p current for thread being destroyed%s!\n", voidp{ctx}, + ERR("Context {} current for thread being destroyed{}!", voidp{ctx}, result ? "" : ", leak detected"); } } @@ -116,26 +119,30 @@ thread_local ALCcontext::ThreadCtx ALCcontext::sThreadContext; ALeffect ALCcontext::sDefaultEffect; -ALCcontext::ALCcontext(al::intrusive_ptr device, ContextFlagBitset flags) +ALCcontext::ALCcontext(al::intrusive_ptr device, ContextFlagBitset flags) : ContextBase{device.get()}, mALDevice{std::move(device)}, mContextFlags{flags} { mDebugGroups.emplace_back(DebugSource::Other, 0, std::string{}); mDebugEnabled.store(mContextFlags.test(ContextFlags::DebugBit), std::memory_order_relaxed); + + /* Low-severity debug messages are disabled by default. */ + alDebugMessageControlDirectEXT(this, AL_DONT_CARE_EXT, AL_DONT_CARE_EXT, + AL_DEBUG_SEVERITY_LOW_EXT, 0, nullptr, AL_FALSE); } ALCcontext::~ALCcontext() { - TRACE("Freeing context %p\n", voidp{this}); + TRACE("Freeing context {}", voidp{this}); size_t count{std::accumulate(mSourceList.cbegin(), mSourceList.cend(), 0_uz, [](size_t cur, const SourceSubList &sublist) noexcept -> size_t { return cur + static_cast(al::popcount(~sublist.FreeMask)); })}; if(count > 0) - WARN("%zu Source%s not deleted\n", count, (count==1)?"":"s"); + WARN("{} Source{} not deleted", count, (count==1)?"":"s"); mSourceList.clear(); mNumSources = 0; -#ifdef ALSOFT_EAX +#if ALSOFT_EAX eaxUninitialize(); #endif // ALSOFT_EAX @@ -144,7 +151,7 @@ ALCcontext::~ALCcontext() [](size_t cur, const EffectSlotSubList &sublist) noexcept -> size_t { return cur + static_cast(al::popcount(~sublist.FreeMask)); }); if(count > 0) - WARN("%zu AuxiliaryEffectSlot%s not deleted\n", count, (count==1)?"":"s"); + WARN("{} AuxiliaryEffectSlot{} not deleted", count, (count==1)?"":"s"); mEffectSlotList.clear(); mNumEffectSlots = 0; } @@ -157,16 +164,17 @@ void ALCcontext::init() aluInitEffectPanning(mDefaultSlot->mSlot, this); } - EffectSlotArray *auxslots; + std::unique_ptr auxslots; if(!mDefaultSlot) auxslots = EffectSlot::CreatePtrArray(0); else { - auxslots = EffectSlot::CreatePtrArray(1); + auxslots = EffectSlot::CreatePtrArray(2); (*auxslots)[0] = mDefaultSlot->mSlot; + (*auxslots)[1] = mDefaultSlot->mSlot; mDefaultSlot->mState = SlotState::Playing; } - mActiveAuxSlots.store(auxslots, std::memory_order_relaxed); + mActiveAuxSlots.store(std::move(auxslots), std::memory_order_relaxed); allocVoiceChanges(); { @@ -180,15 +188,15 @@ void ALCcontext::init() if(sBufferSubDataCompat) { - auto iter = std::find(mExtensions.begin(), mExtensions.end(), "AL_EXT_SOURCE_RADIUS"); + auto iter = std::find(mExtensions.begin(), mExtensions.end(), "AL_EXT_SOURCE_RADIUS"sv); if(iter != mExtensions.end()) mExtensions.erase(iter); /* TODO: Would be nice to sort this alphabetically. Needs case- * insensitive searching. */ - mExtensions.emplace_back("AL_SOFT_buffer_sub_data"); + mExtensions.emplace_back("AL_SOFT_buffer_sub_data"sv); } -#ifdef ALSOFT_EAX +#if ALSOFT_EAX eax_initialize_extensions(); #endif // ALSOFT_EAX @@ -211,19 +219,31 @@ void ALCcontext::init() mExtensionsString = std::move(extensions); } +#if ALSOFT_EAX + eax_set_defaults(); +#endif + mParams.Position = alu::Vector{0.0f, 0.0f, 0.0f, 1.0f}; mParams.Matrix = alu::Matrix::Identity(); mParams.Velocity = alu::Vector{}; mParams.Gain = mListener.Gain; - mParams.MetersPerUnit = mListener.mMetersPerUnit; + mParams.MetersPerUnit = mListener.mMetersPerUnit +#if ALSOFT_EAX + * eaxGetDistanceFactor() +#endif + ; mParams.AirAbsorptionGainHF = mAirAbsorptionGainHF; mParams.DopplerFactor = mDopplerFactor; - mParams.SpeedOfSound = mSpeedOfSound * mDopplerVelocity; + mParams.SpeedOfSound = mSpeedOfSound * mDopplerVelocity +#if ALSOFT_EAX + / eaxGetDistanceFactor() +#endif + ; mParams.SourceDistanceModel = mSourceDistanceModel; mParams.mDistanceModel = mDistanceModel; - mAsyncEvents = RingBuffer::Create(511, sizeof(AsyncEvent), false); + mAsyncEvents = RingBuffer::Create(1024, sizeof(AsyncEvent), false); StartEventThrd(this); @@ -231,63 +251,58 @@ void ALCcontext::init() mActiveVoiceCount.store(64, std::memory_order_relaxed); } -bool ALCcontext::deinit() +void ALCcontext::deinit() { if(sLocalContext == this) { - WARN("%p released while current on thread\n", voidp{this}); + WARN("{} released while current on thread", voidp{this}); + auto _ = ContextRef{sLocalContext}; sThreadContext.set(nullptr); - dec_ref(); } - ALCcontext *origctx{this}; - if(sGlobalContext.compare_exchange_strong(origctx, nullptr)) + if(ALCcontext *origctx{this}; sGlobalContext.compare_exchange_strong(origctx, nullptr)) { + auto _ = ContextRef{origctx}; while(sGlobalContextLock.load()) { /* Wait to make sure another thread didn't get the context and is * trying to increment its refcount. */ } - dec_ref(); } - bool ret{}; + bool stopPlayback{}; /* First make sure this context exists in the device's list. */ - auto *oldarray = mDevice->mContexts.load(std::memory_order_acquire); - if(auto toremove = static_cast(std::count(oldarray->begin(), oldarray->end(), this))) + auto oldarray = al::span{*mDevice->mContexts.load(std::memory_order_acquire)}; + if(auto toremove = static_cast(std::count(oldarray.begin(), oldarray.end(), this))) { using ContextArray = al::FlexArray; - auto alloc_ctx_array = [](const size_t count) -> ContextArray* - { - if(count == 0) return &DeviceBase::sEmptyContextArray; - return ContextArray::Create(count).release(); - }; - auto *newarray = alloc_ctx_array(oldarray->size() - toremove); + const auto newsize = size_t{oldarray.size() - toremove}; + auto newarray = ContextArray::Create(newsize); /* Copy the current/old context handles to the new array, excluding the * given context. */ - std::copy_if(oldarray->begin(), oldarray->end(), newarray->begin(), + std::copy_if(oldarray.begin(), oldarray.end(), newarray->begin(), [this](ContextBase *ctx) { return ctx != this; }); /* Store the new context array in the device. Wait for any current mix * to finish before deleting the old array. */ - mDevice->mContexts.store(newarray); - if(oldarray != &DeviceBase::sEmptyContextArray) - { - mDevice->waitForMix(); - delete oldarray; - } + auto prevarray = mDevice->mContexts.exchange(std::move(newarray)); + std::ignore = mDevice->waitForMix(); - ret = !newarray->empty(); + stopPlayback = (newsize == 0); } else - ret = !oldarray->empty(); + stopPlayback = oldarray.empty(); StopEventThrd(this); - return ret; + if(stopPlayback && mALDevice->mDeviceState == DeviceState::Playing) + { + mALDevice->Backend->stop(); + mALDevice->mDeviceState = DeviceState::Configured; + } } void ALCcontext::applyAllUpdates() @@ -300,7 +315,7 @@ void ALCcontext::applyAllUpdates() /* busy-wait */ } -#ifdef ALSOFT_EAX +#if ALSOFT_EAX if(mEaxNeedsCommit) eaxCommit(); #endif @@ -317,7 +332,7 @@ void ALCcontext::applyAllUpdates() } -#ifdef ALSOFT_EAX +#if ALSOFT_EAX namespace { template @@ -328,10 +343,10 @@ void ForEachSource(ALCcontext *context, F func) uint64_t usemask{~sublist.FreeMask}; while(usemask) { - const int idx{al::countr_zero(usemask)}; + const auto idx = static_cast(al::countr_zero(usemask)); usemask &= ~(1_u64 << idx); - func(sublist.Sources[idx]); + func((*sublist.Sources)[idx]); } } } @@ -469,14 +484,14 @@ void ALCcontext::eax_initialize_extensions() if(!eax_g_is_enabled) return; - mExtensions.emplace(mExtensions.begin(), eax_x_ram_ext_name); + mExtensions.emplace(mExtensions.begin(), "EAX-RAM"sv); if(eaxIsCapable()) { - mExtensions.emplace(mExtensions.begin(), eax5_ext_name); - mExtensions.emplace(mExtensions.begin(), eax4_ext_name); - mExtensions.emplace(mExtensions.begin(), eax3_ext_name); - mExtensions.emplace(mExtensions.begin(), eax2_ext_name); - mExtensions.emplace(mExtensions.begin(), eax1_ext_name); + mExtensions.emplace(mExtensions.begin(), "EAX5.0"sv); + mExtensions.emplace(mExtensions.begin(), "EAX4.0"sv); + mExtensions.emplace(mExtensions.begin(), "EAX3.0"sv); + mExtensions.emplace(mExtensions.begin(), "EAX2.0"sv); + mExtensions.emplace(mExtensions.begin(), "EAX"sv); } } @@ -549,10 +564,11 @@ unsigned long ALCcontext::eax_detect_speaker_configuration() const case DevFmtX51: return SPEAKERS_5; case DevFmtX61: return SPEAKERS_6; case DevFmtX71: return SPEAKERS_7; - /* 7.1.4 is compatible with 7.1. This could instead be HEADPHONES to + /* 7.1.4(.4) is compatible with 7.1. This could instead be HEADPHONES to * suggest with-height surround sound (like HRTF). */ case DevFmtX714: return SPEAKERS_7; + case DevFmtX7144: return SPEAKERS_7; /* 3D7.1 is only compatible with 5.1. This could instead be HEADPHONES to * suggest full-sphere surround sound (like HRTF). */ @@ -563,7 +579,8 @@ unsigned long ALCcontext::eax_detect_speaker_configuration() const */ case DevFmtAmbi3D: return SPEAKERS_7; } - ERR(EAX_PREFIX "Unexpected device channel format 0x%x.\n", mDevice->FmtChans); + ERR(EAX_PREFIX "Unexpected device channel format {:#x}.", + uint{al::to_underlying(mDevice->FmtChans)}); return HEADPHONES; #undef EAX_PREFIX @@ -576,7 +593,7 @@ void ALCcontext::eax_update_speaker_configuration() void ALCcontext::eax_set_last_error_defaults() noexcept { - mEaxLastError = EAX_OK; + mEaxLastError = EAXCONTEXT_DEFAULTLASTERROR; } void ALCcontext::eax_session_set_defaults() noexcept @@ -665,6 +682,7 @@ void ALCcontext::eax_get_misc(const EaxCall& call) break; case EAXCONTEXT_LASTERROR: call.set_value(mEaxLastError); + mEaxLastError = EAX_OK; break; case EAXCONTEXT_SPEAKERCONFIG: call.set_value(mEaxSpeakerConfig); @@ -747,10 +765,7 @@ void ALCcontext::eax_context_commit_primary_fx_slot_id() void ALCcontext::eax_context_commit_distance_factor() { - if(mListener.mMetersPerUnit == mEax.flDistanceFactor) - return; - - mListener.mMetersPerUnit = mEax.flDistanceFactor; + /* mEax.flDistanceFactor was changed, so the context props are dirty. */ mPropsDirty = true; } @@ -1013,56 +1028,48 @@ void ALCcontext::eaxCommit() } -FORCE_ALIGN ALenum AL_APIENTRY EAXSet(const GUID *a, ALuint b, ALuint c, ALvoid *d, ALuint e) noexcept +FORCE_ALIGN auto AL_APIENTRY EAXSet(const GUID *property_set_id, ALuint property_id, + ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum { auto context = GetContextRef(); if(!context) UNLIKELY return AL_INVALID_OPERATION; - return EAXSetDirect(context.get(), a, b, c, d, e); + return EAXSetDirect(context.get(), property_set_id, property_id, source_id, value, value_size); } -FORCE_ALIGN ALenum AL_APIENTRY EAXSetDirect(ALCcontext *context, const GUID *property_set_id, - ALuint property_id, ALuint property_source_id, ALvoid *property_value, - ALuint property_value_size) noexcept +FORCE_ALIGN auto AL_APIENTRY EAXSetDirect(ALCcontext *context, const GUID *property_set_id, + ALuint property_id, ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum try { std::lock_guard prop_lock{context->mPropLock}; - return context->eax_eax_set( - property_set_id, - property_id, - property_source_id, - property_value, - property_value_size); + return context->eax_eax_set(property_set_id, property_id, source_id, value, value_size); } catch(...) { - eax_log_exception(__func__); + context->eaxSetLastError(); + eax_log_exception(std::data(__func__)); return AL_INVALID_OPERATION; } -FORCE_ALIGN ALenum AL_APIENTRY EAXGet(const GUID *a, ALuint b, ALuint c, ALvoid *d, ALuint e) noexcept +FORCE_ALIGN auto AL_APIENTRY EAXGet(const GUID *property_set_id, ALuint property_id, + ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum { auto context = GetContextRef(); if(!context) UNLIKELY return AL_INVALID_OPERATION; - return EAXGetDirect(context.get(), a, b, c, d, e); + return EAXGetDirect(context.get(), property_set_id, property_id, source_id, value, value_size); } -FORCE_ALIGN ALenum AL_APIENTRY EAXGetDirect(ALCcontext *context, const GUID *property_set_id, - ALuint property_id, ALuint property_source_id, ALvoid *property_value, - ALuint property_value_size) noexcept +FORCE_ALIGN auto AL_APIENTRY EAXGetDirect(ALCcontext *context, const GUID *property_set_id, + ALuint property_id, ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum try { std::lock_guard prop_lock{context->mPropLock}; - return context->eax_eax_get( - property_set_id, - property_id, - property_source_id, - property_value, - property_value_size); + return context->eax_eax_get(property_set_id, property_id, source_id, value, value_size); } catch(...) { - eax_log_exception(__func__); + context->eaxSetLastError(); + eax_log_exception(std::data(__func__)); return AL_INVALID_OPERATION; } #endif // ALSOFT_EAX diff --git a/3rdparty/openal/alc/context.h b/3rdparty/openal/alc/context.h index 201c88730adc..d8874262cc86 100644 --- a/3rdparty/openal/alc/context.h +++ b/3rdparty/openal/alc/context.h @@ -1,11 +1,14 @@ #ifndef ALC_CONTEXT_H #define ALC_CONTEXT_H +#include "config.h" + #include +#include +#include #include #include #include -#include #include #include #include @@ -17,29 +20,31 @@ #include "AL/alext.h" #include "al/listener.h" -#include "almalloc.h" -#include "alnumeric.h" -#include "atomic.h" +#include "althreads.h" #include "core/context.h" -#include "inprogext.h" +#include "fmt/core.h" #include "intrusive_ptr.h" +#include "opthelpers.h" -#ifdef ALSOFT_EAX -#include "al/eax/call.h" +#if ALSOFT_EAX +#include "al/eax/api.h" #include "al/eax/exception.h" #include "al/eax/fx_slot_index.h" #include "al/eax/fx_slots.h" #include "al/eax/utils.h" + +class EaxCall; #endif // ALSOFT_EAX struct ALeffect; struct ALeffectslot; -struct ALsource; struct DebugGroup; +struct EffectSlotSubList; +struct SourceSubList; -enum class DebugSource : uint8_t; -enum class DebugType : uint8_t; -enum class DebugSeverity : uint8_t; +enum class DebugSource : std::uint8_t; +enum class DebugType : std::uint8_t; +enum class DebugSeverity : std::uint8_t; using uint = unsigned int; @@ -68,47 +73,19 @@ struct DebugLogEntry { }; -struct SourceSubList { - uint64_t FreeMask{~0_u64}; - ALsource *Sources{nullptr}; /* 64 */ - - SourceSubList() noexcept = default; - SourceSubList(const SourceSubList&) = delete; - SourceSubList(SourceSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Sources{rhs.Sources} - { rhs.FreeMask = ~0_u64; rhs.Sources = nullptr; } - ~SourceSubList(); - - SourceSubList& operator=(const SourceSubList&) = delete; - SourceSubList& operator=(SourceSubList&& rhs) noexcept - { std::swap(FreeMask, rhs.FreeMask); std::swap(Sources, rhs.Sources); return *this; } -}; - -struct EffectSlotSubList { - uint64_t FreeMask{~0_u64}; - ALeffectslot *EffectSlots{nullptr}; /* 64 */ - - EffectSlotSubList() noexcept = default; - EffectSlotSubList(const EffectSlotSubList&) = delete; - EffectSlotSubList(EffectSlotSubList&& rhs) noexcept - : FreeMask{rhs.FreeMask}, EffectSlots{rhs.EffectSlots} - { rhs.FreeMask = ~0_u64; rhs.EffectSlots = nullptr; } - ~EffectSlotSubList(); - - EffectSlotSubList& operator=(const EffectSlotSubList&) = delete; - EffectSlotSubList& operator=(EffectSlotSubList&& rhs) noexcept - { std::swap(FreeMask, rhs.FreeMask); std::swap(EffectSlots, rhs.EffectSlots); return *this; } -}; - -struct ALCcontext : public al::intrusive_ref, ContextBase { - const al::intrusive_ptr mALDevice; +namespace al { +struct Device; +} // namespace al +struct ALCcontext final : public al::intrusive_ref, ContextBase { + const al::intrusive_ptr mALDevice; bool mPropsDirty{true}; bool mDeferUpdates{false}; std::mutex mPropLock; - std::atomic mLastError{AL_NO_ERROR}; + al::tss mLastThreadError{AL_NO_ERROR}; const ContextFlagBitset mContextFlags; std::atomic mDebugEnabled{false}; @@ -145,23 +122,23 @@ struct ALCcontext : public al::intrusive_ref, ContextBase { std::unique_ptr mDefaultSlot; std::vector mExtensions; - std::string mExtensionsString{}; + std::string mExtensionsString; std::unordered_map mSourceNames; std::unordered_map mEffectSlotNames; - ALCcontext(al::intrusive_ptr device, ContextFlagBitset flags); + ALCcontext(al::intrusive_ptr device, ContextFlagBitset flags); ALCcontext(const ALCcontext&) = delete; ALCcontext& operator=(const ALCcontext&) = delete; - ~ALCcontext(); + ~ALCcontext() final; void init(); /** * Removes the context from its device and removes it from being current on - * the running thread or globally. Returns true if other contexts still - * exist on the device. + * the running thread or globally. Stops device playback if this was the + * last context on its device. */ - bool deinit(); + void deinit(); /** * Defers/suspends updates for the given context's listener and sources. @@ -186,12 +163,18 @@ struct ALCcontext : public al::intrusive_ref, ContextBase { */ void applyAllUpdates(); -#ifdef __USE_MINGW_ANSI_STDIO - [[gnu::format(gnu_printf, 3, 4)]] -#else - [[gnu::format(printf, 3, 4)]] -#endif - void setError(ALenum errorCode, const char *msg, ...); + void setErrorImpl(ALenum errorCode, const fmt::string_view fmt, fmt::format_args args); + + template + void setError(ALenum errorCode, fmt::format_string msg, Args&& ...args) + { setErrorImpl(errorCode, msg, fmt::make_format_args(args...)); } + + [[noreturn]] + void throw_error_impl(ALenum errorCode, const fmt::string_view fmt, fmt::format_args args); + + template [[noreturn]] + void throw_error(ALenum errorCode, fmt::format_string fmt, Args&&... args) + { throw_error_impl(errorCode, fmt, fmt::make_format_args(args...)); } void sendDebugMessage(std::unique_lock &debuglock, DebugSource source, DebugType type, ALuint id, DebugSeverity severity, std::string_view message); @@ -218,8 +201,19 @@ struct ALCcontext : public al::intrusive_ref, ContextBase { */ class ThreadCtx { public: + ThreadCtx() = default; + ThreadCtx(const ThreadCtx&) = delete; + auto operator=(const ThreadCtx&) -> ThreadCtx& = delete; + ~ThreadCtx(); + /* NOLINTBEGIN(readability-convert-member-functions-to-static) + * This should be non-static to invoke construction of the thread-local + * sThreadContext, so that it's destructor gets run at thread exit to + * clear sLocalContext (which isn't a member variable to make read + * access efficient). + */ void set(ALCcontext *ctx) const noexcept { sLocalContext = ctx; } + /* NOLINTEND(readability-convert-member-functions-to-static) */ }; static thread_local ThreadCtx sThreadContext; @@ -230,10 +224,7 @@ struct ALCcontext : public al::intrusive_ref, ContextBase { /* Default effect that applies to sources that don't have an effect on send 0. */ static ALeffect sDefaultEffect; - DEF_NEWDEL(ALCcontext) - -#ifdef ALSOFT_EAX -public: +#if ALSOFT_EAX bool hasEax() const noexcept { return mEaxIsInitialized; } bool eaxIsCapable() const noexcept; @@ -255,7 +246,11 @@ struct ALCcontext : public al::intrusive_ref, ContextBase { void eaxSetLastError() noexcept; - EaxFxSlotIndex eaxGetPrimaryFxSlotIndex() const noexcept + [[nodiscard]] + auto eaxGetDistanceFactor() const noexcept -> float { return mEax.flDistanceFactor; } + + [[nodiscard]] + auto eaxGetPrimaryFxSlotIndex() const noexcept -> EaxFxSlotIndex { return mEaxPrimaryFxSlotIndex; } const ALeffectslot& eaxGetFxSlot(EaxFxSlotIndexValue fx_slot_index) const @@ -290,12 +285,11 @@ struct ALCcontext : public al::intrusive_ref, ContextBase { Eax5Props d; // Deferred. }; - class ContextException : public EaxException - { + class ContextException final : public EaxException { public: - explicit ContextException(const char* message) + explicit ContextException(const char *message) : EaxException{"EAX_CONTEXT", message} - {} + { } }; struct Eax4PrimaryFxSlotIdValidator { @@ -477,7 +471,7 @@ struct ALCcontext : public al::intrusive_ref, ContextBase { typename TMemberResult, typename TProps, typename TState> - void eax_defer(const EaxCall& call, TState& state, TMemberResult TProps::*member) noexcept + void eax_defer(const EaxCall& call, TState& state, TMemberResult TProps::*member) { const auto& src = call.get_value(); TValidator{}(src); @@ -560,28 +554,20 @@ struct ALCcontext : public al::intrusive_ref, ContextBase { using ContextRef = al::intrusive_ptr; -ContextRef GetContextRef(void); +ContextRef GetContextRef() noexcept; void UpdateContextProps(ALCcontext *context); -extern bool TrapALError; +inline bool TrapALError{false}; -#ifdef ALSOFT_EAX -ALenum AL_APIENTRY EAXSet( - const GUID* property_set_id, - ALuint property_id, - ALuint property_source_id, - ALvoid* property_value, - ALuint property_value_size) noexcept; +#if ALSOFT_EAX +auto AL_APIENTRY EAXSet(const GUID *property_set_id, ALuint property_id, + ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum; -ALenum AL_APIENTRY EAXGet( - const GUID* property_set_id, - ALuint property_id, - ALuint property_source_id, - ALvoid* property_value, - ALuint property_value_size) noexcept; +auto AL_APIENTRY EAXGet(const GUID *property_set_id, ALuint property_id, + ALuint source_id, ALvoid *value, ALuint value_size) noexcept -> ALenum; #endif // ALSOFT_EAX #endif /* ALC_CONTEXT_H */ diff --git a/3rdparty/openal/alc/device.cpp b/3rdparty/openal/alc/device.cpp index 27aa6f36e349..ad6071605d2f 100644 --- a/3rdparty/openal/alc/device.cpp +++ b/3rdparty/openal/alc/device.cpp @@ -3,19 +3,22 @@ #include "device.h" +#include +#include #include -#include +#include "al/buffer.h" +#include "al/effect.h" +#include "al/filter.h" #include "albit.h" -#include "alconfig.h" +#include "alnumeric.h" +#include "atomic.h" #include "backends/base.h" -#include "core/bformatdec.h" -#include "core/bs2b.h" -#include "core/front_stablizer.h" +#include "core/devformat.h" #include "core/hrtf.h" #include "core/logging.h" #include "core/mastering.h" -#include "core/uhjfilter.h" +#include "flexarray.h" namespace { @@ -24,13 +27,14 @@ using voidp = void*; } // namespace +namespace al { -ALCdevice::ALCdevice(DeviceType type) : DeviceBase{type} +Device::Device(DeviceType type) : DeviceBase{type} { } -ALCdevice::~ALCdevice() +Device::~Device() { - TRACE("Freeing device %p\n", voidp{this}); + TRACE("Freeing device {}", voidp{this}); Backend = nullptr; @@ -38,35 +42,35 @@ ALCdevice::~ALCdevice() [](size_t cur, const BufferSubList &sublist) noexcept -> size_t { return cur + static_cast(al::popcount(~sublist.FreeMask)); })}; if(count > 0) - WARN("%zu Buffer%s not deleted\n", count, (count==1)?"":"s"); + WARN("{} Buffer{} not deleted", count, (count==1)?"":"s"); count = std::accumulate(EffectList.cbegin(), EffectList.cend(), 0_uz, [](size_t cur, const EffectSubList &sublist) noexcept -> size_t { return cur + static_cast(al::popcount(~sublist.FreeMask)); }); if(count > 0) - WARN("%zu Effect%s not deleted\n", count, (count==1)?"":"s"); + WARN("{} Effect{} not deleted", count, (count==1)?"":"s"); count = std::accumulate(FilterList.cbegin(), FilterList.cend(), 0_uz, [](size_t cur, const FilterSubList &sublist) noexcept -> size_t { return cur + static_cast(al::popcount(~sublist.FreeMask)); }); if(count > 0) - WARN("%zu Filter%s not deleted\n", count, (count==1)?"":"s"); + WARN("{} Filter{} not deleted", count, (count==1)?"":"s"); } -void ALCdevice::enumerateHrtfs() +void Device::enumerateHrtfs() { - mHrtfList = EnumerateHrtf(configValue(nullptr, "hrtf-paths")); - if(auto defhrtfopt = configValue(nullptr, "default-hrtf")) + mHrtfList = EnumerateHrtf(configValue({}, "hrtf-paths")); + if(auto defhrtfopt = configValue({}, "default-hrtf")) { auto iter = std::find(mHrtfList.begin(), mHrtfList.end(), *defhrtfopt); if(iter == mHrtfList.end()) - WARN("Failed to find default HRTF \"%s\"\n", defhrtfopt->c_str()); + WARN("Failed to find default HRTF \"{}\"", *defhrtfopt); else if(iter != mHrtfList.begin()) std::rotate(mHrtfList.begin(), iter, iter+1); } } -auto ALCdevice::getOutputMode1() const noexcept -> OutputMode1 +auto Device::getOutputMode1() const noexcept -> OutputMode1 { if(mContexts.load(std::memory_order_relaxed)->empty()) return OutputMode1::Any; @@ -85,9 +89,12 @@ auto ALCdevice::getOutputMode1() const noexcept -> OutputMode1 case DevFmtX61: return OutputMode1::X61; case DevFmtX71: return OutputMode1::X71; case DevFmtX714: + case DevFmtX7144: case DevFmtX3D71: case DevFmtAmbi3D: break; } return OutputMode1::Any; } + +} // namespace al diff --git a/3rdparty/openal/alc/device.h b/3rdparty/openal/alc/device.h index 66f37a7ec534..1ca800c8be5c 100644 --- a/3rdparty/openal/alc/device.h +++ b/3rdparty/openal/alc/device.h @@ -1,85 +1,42 @@ #ifndef ALC_DEVICE_H #define ALC_DEVICE_H +#include "config.h" + #include #include #include #include -#include #include #include -#include +#include #include +#include "AL/al.h" #include "AL/alc.h" #include "AL/alext.h" #include "alconfig.h" -#include "almalloc.h" -#include "alnumeric.h" #include "core/device.h" -#include "inprogext.h" #include "intrusive_ptr.h" -#ifdef ALSOFT_EAX +#if ALSOFT_EAX #include "al/eax/x_ram.h" #endif // ALSOFT_EAX -struct ALbuffer; -struct ALeffect; -struct ALfilter; struct BackendBase; +struct BufferSubList; +struct EffectSubList; +struct FilterSubList; using uint = unsigned int; -struct BufferSubList { - uint64_t FreeMask{~0_u64}; - ALbuffer *Buffers{nullptr}; /* 64 */ - - BufferSubList() noexcept = default; - BufferSubList(const BufferSubList&) = delete; - BufferSubList(BufferSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Buffers{rhs.Buffers} - { rhs.FreeMask = ~0_u64; rhs.Buffers = nullptr; } - ~BufferSubList(); - - BufferSubList& operator=(const BufferSubList&) = delete; - BufferSubList& operator=(BufferSubList&& rhs) noexcept - { std::swap(FreeMask, rhs.FreeMask); std::swap(Buffers, rhs.Buffers); return *this; } -}; - -struct EffectSubList { - uint64_t FreeMask{~0_u64}; - ALeffect *Effects{nullptr}; /* 64 */ - - EffectSubList() noexcept = default; - EffectSubList(const EffectSubList&) = delete; - EffectSubList(EffectSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Effects{rhs.Effects} - { rhs.FreeMask = ~0_u64; rhs.Effects = nullptr; } - ~EffectSubList(); - - EffectSubList& operator=(const EffectSubList&) = delete; - EffectSubList& operator=(EffectSubList&& rhs) noexcept - { std::swap(FreeMask, rhs.FreeMask); std::swap(Effects, rhs.Effects); return *this; } -}; - -struct FilterSubList { - uint64_t FreeMask{~0_u64}; - ALfilter *Filters{nullptr}; /* 64 */ +struct ALCdevice { virtual ~ALCdevice() = default; }; - FilterSubList() noexcept = default; - FilterSubList(const FilterSubList&) = delete; - FilterSubList(FilterSubList&& rhs) noexcept : FreeMask{rhs.FreeMask}, Filters{rhs.Filters} - { rhs.FreeMask = ~0_u64; rhs.Filters = nullptr; } - ~FilterSubList(); - - FilterSubList& operator=(const FilterSubList&) = delete; - FilterSubList& operator=(FilterSubList&& rhs) noexcept - { std::swap(FreeMask, rhs.FreeMask); std::swap(Filters, rhs.Filters); return *this; } -}; +namespace al { - -struct ALCdevice : public al::intrusive_ref, DeviceBase { +struct Device final : public ALCdevice, al::intrusive_ref, DeviceBase { /* This lock protects the device state (format, update size, etc) from * being from being changed in multiple threads, or being accessed while * being changed. It's also used to serialize calls to the backend. @@ -129,7 +86,7 @@ struct ALCdevice : public al::intrusive_ref, DeviceBase { std::mutex FilterLock; std::vector FilterList; -#ifdef ALSOFT_EAX +#if ALSOFT_EAX ALuint eax_x_ram_free_size{eax_x_ram_max_size}; #endif // ALSOFT_EAX @@ -138,37 +95,41 @@ struct ALCdevice : public al::intrusive_ref, DeviceBase { std::unordered_map mEffectNames; std::unordered_map mFilterNames; - ALCdevice(DeviceType type); - ~ALCdevice(); + std::string mVendorOverride; + std::string mVersionOverride; + std::string mRendererOverride; + + explicit Device(DeviceType type); + ~Device() final; void enumerateHrtfs(); - bool getConfigValueBool(const char *block, const char *key, bool def) - { return GetConfigValueBool(DeviceName.c_str(), block, key, def); } + bool getConfigValueBool(const std::string_view block, const std::string_view key, bool def) + { return GetConfigValueBool(mDeviceName, block, key, def); } template - inline std::optional configValue(const char *block, const char *key) = delete; - - DEF_NEWDEL(ALCdevice) + auto configValue(const std::string_view block, const std::string_view key) -> std::optional = delete; }; -template<> -inline std::optional ALCdevice::configValue(const char *block, const char *key) -{ return ConfigValueStr(DeviceName.c_str(), block, key); } -template<> -inline std::optional ALCdevice::configValue(const char *block, const char *key) -{ return ConfigValueInt(DeviceName.c_str(), block, key); } -template<> -inline std::optional ALCdevice::configValue(const char *block, const char *key) -{ return ConfigValueUInt(DeviceName.c_str(), block, key); } -template<> -inline std::optional ALCdevice::configValue(const char *block, const char *key) -{ return ConfigValueFloat(DeviceName.c_str(), block, key); } -template<> -inline std::optional ALCdevice::configValue(const char *block, const char *key) -{ return ConfigValueBool(DeviceName.c_str(), block, key); } +template<> inline +auto Device::configValue(const std::string_view block, const std::string_view key) -> std::optional +{ return ConfigValueStr(mDeviceName, block, key); } +template<> inline +auto Device::configValue(const std::string_view block, const std::string_view key) -> std::optional +{ return ConfigValueInt(mDeviceName, block, key); } +template<> inline +auto Device::configValue(const std::string_view block, const std::string_view key) -> std::optional +{ return ConfigValueUInt(mDeviceName, block, key); } +template<> inline +auto Device::configValue(const std::string_view block, const std::string_view key) -> std::optional +{ return ConfigValueFloat(mDeviceName, block, key); } +template<> inline +auto Device::configValue(const std::string_view block, const std::string_view key) -> std::optional +{ return ConfigValueBool(mDeviceName, block, key); } + +} // namespace al /** Stores the latest ALC device error. */ -void alcSetError(ALCdevice *device, ALCenum errorCode); +void alcSetError(al::Device *device, ALCenum errorCode); #endif diff --git a/3rdparty/openal/alc/effects/autowah.cpp b/3rdparty/openal/alc/effects/autowah.cpp index 4f874ef29262..90006c157d9c 100644 --- a/3rdparty/openal/alc/effects/autowah.cpp +++ b/3rdparty/openal/alc/effects/autowah.cpp @@ -22,24 +22,24 @@ #include #include +#include #include -#include -#include +#include #include "alc/effects/base.h" -#include "almalloc.h" #include "alnumbers.h" #include "alnumeric.h" #include "alspan.h" #include "core/ambidefs.h" #include "core/bufferline.h" #include "core/context.h" -#include "core/devformat.h" #include "core/device.h" +#include "core/effects/base.h" #include "core/effectslot.h" #include "core/mixer.h" #include "intrusive_ptr.h" +struct BufferStorage; namespace { @@ -50,35 +50,37 @@ constexpr float QFactor{5.0f}; struct AutowahState final : public EffectState { /* Effect parameters */ - float mAttackRate; - float mReleaseRate; - float mResonanceGain; - float mPeakGain; - float mFreqMinNorm; - float mBandwidthNorm; - float mEnvDelay; + float mAttackRate{}; + float mReleaseRate{}; + float mResonanceGain{}; + float mPeakGain{}; + float mFreqMinNorm{}; + float mBandwidthNorm{}; + float mEnvDelay{}; /* Filter components derived from the envelope. */ - struct { - float cos_w0; - float alpha; - } mEnv[BufferLineSize]; + struct FilterParam { + float cos_w0{}; + float alpha{}; + }; + std::array mEnv; - struct { + struct ChannelData { uint mTargetChannel{InvalidChannelIndex}; - /* Effect filters' history. */ - struct { - float z1, z2; - } mFilter; + struct FilterHistory { + float z1{}, z2{}; + }; + FilterHistory mFilter; /* Effect gains for each output channel */ - float mCurrentGain; - float mTargetGain; - } mChans[MaxAmbiChannels]; + float mCurrentGain{}; + float mTargetGain{}; + }; + std::array mChans; /* Effects buffers */ - alignas(16) float mBufferOut[BufferLineSize]; + alignas(16) FloatBufferLine mBufferOut{}; void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override; @@ -86,8 +88,6 @@ struct AutowahState final : public EffectState { const EffectTarget target) override; void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; - - DEF_NEWDEL(AutowahState) }; void AutowahState::deviceUpdate(const DeviceBase*, const BufferStorage*) @@ -118,18 +118,19 @@ void AutowahState::deviceUpdate(const DeviceBase*, const BufferStorage*) } void AutowahState::update(const ContextBase *context, const EffectSlot *slot, - const EffectProps *props, const EffectTarget target) + const EffectProps *props_, const EffectTarget target) { + auto &props = std::get(*props_); const DeviceBase *device{context->mDevice}; - const auto frequency = static_cast(device->Frequency); + const auto frequency = static_cast(device->mSampleRate); - const float ReleaseTime{clampf(props->Autowah.ReleaseTime, 0.001f, 1.0f)}; + const float ReleaseTime{std::clamp(props.ReleaseTime, 0.001f, 1.0f)}; - mAttackRate = std::exp(-1.0f / (props->Autowah.AttackTime*frequency)); + mAttackRate = std::exp(-1.0f / (props.AttackTime*frequency)); mReleaseRate = std::exp(-1.0f / (ReleaseTime*frequency)); /* 0-20dB Resonance Peak gain */ - mResonanceGain = std::sqrt(std::log10(props->Autowah.Resonance)*10.0f / 3.0f); - mPeakGain = 1.0f - std::log10(props->Autowah.PeakGain / GainScale); + mResonanceGain = std::sqrt(std::log10(props.Resonance)*10.0f / 3.0f); + mPeakGain = 1.0f - std::log10(props.PeakGain / GainScale); mFreqMinNorm = MinFreq / frequency; mBandwidthNorm = (MaxFreq-MinFreq) / frequency; @@ -155,23 +156,22 @@ void AutowahState::process(const size_t samplesToDo, float env_delay{mEnvDelay}; for(size_t i{0u};i < samplesToDo;i++) { - float w0, sample, a; - /* Envelope follower described on the book: Audio Effects, Theory, * Implementation and Application. */ - sample = peak_gain * std::fabs(samplesIn[0][i]); - a = (sample > env_delay) ? attack_rate : release_rate; + const float sample{peak_gain * std::fabs(samplesIn[0][i])}; + const float a{(sample > env_delay) ? attack_rate : release_rate}; env_delay = lerpf(sample, env_delay, a); /* Calculate the cos and alpha components for this sample's filter. */ - w0 = minf((bandwidth*env_delay + freq_min), 0.46f) * (al::numbers::pi_v*2.0f); + const float w0{std::min(bandwidth*env_delay + freq_min, 0.46f) * + (al::numbers::pi_v*2.0f)}; mEnv[i].cos_w0 = std::cos(w0); mEnv[i].alpha = std::sin(w0)/(2.0f * QFactor); } mEnvDelay = env_delay; - auto chandata = std::begin(mChans); + auto chandata = mChans.begin(); for(const auto &insamples : samplesIn) { const size_t outidx{chandata->mTargetChannel}; @@ -194,18 +194,18 @@ void AutowahState::process(const size_t samplesToDo, { const float alpha{mEnv[i].alpha}; const float cos_w0{mEnv[i].cos_w0}; - float input, output; - float a[3], b[3]; - - b[0] = 1.0f + alpha*res_gain; - b[1] = -2.0f * cos_w0; - b[2] = 1.0f - alpha*res_gain; - a[0] = 1.0f + alpha/res_gain; - a[1] = -2.0f * cos_w0; - a[2] = 1.0f - alpha/res_gain; - - input = insamples[i]; - output = input*(b[0]/a[0]) + z1; + + const std::array b{ + 1.0f + alpha*res_gain, + -2.0f * cos_w0, + 1.0f - alpha*res_gain}; + const std::array a{ + 1.0f + alpha/res_gain, + -2.0f * cos_w0, + 1.0f - alpha/res_gain}; + + const float input{insamples[i]}; + const float output{input*(b[0]/a[0]) + z1}; z1 = input*(b[1]/a[0]) - output*(a[1]/a[0]) + z2; z2 = input*(b[2]/a[0]) - output*(a[2]/a[0]); mBufferOut[i] = output; @@ -214,8 +214,8 @@ void AutowahState::process(const size_t samplesToDo, chandata->mFilter.z2 = z2; /* Now, mix the processed sound data to the output. */ - MixSamples({mBufferOut, samplesToDo}, samplesOut[outidx].data(), chandata->mCurrentGain, - chandata->mTargetGain, samplesToDo); + MixSamples(al::span{mBufferOut}.first(samplesToDo), samplesOut[outidx], + chandata->mCurrentGain, chandata->mTargetGain, samplesToDo); ++chandata; } } diff --git a/3rdparty/openal/alc/effects/base.h b/3rdparty/openal/alc/effects/base.h index 95695857a4d7..a64880d26a54 100644 --- a/3rdparty/openal/alc/effects/base.h +++ b/3rdparty/openal/alc/effects/base.h @@ -4,23 +4,27 @@ #include "core/effects/base.h" -EffectStateFactory *NullStateFactory_getFactory(void); -EffectStateFactory *ReverbStateFactory_getFactory(void); -EffectStateFactory *StdReverbStateFactory_getFactory(void); -EffectStateFactory *AutowahStateFactory_getFactory(void); -EffectStateFactory *ChorusStateFactory_getFactory(void); -EffectStateFactory *CompressorStateFactory_getFactory(void); -EffectStateFactory *DistortionStateFactory_getFactory(void); -EffectStateFactory *EchoStateFactory_getFactory(void); -EffectStateFactory *EqualizerStateFactory_getFactory(void); -EffectStateFactory *FlangerStateFactory_getFactory(void); -EffectStateFactory *FshifterStateFactory_getFactory(void); -EffectStateFactory *ModulatorStateFactory_getFactory(void); -EffectStateFactory *PshifterStateFactory_getFactory(void); -EffectStateFactory* VmorpherStateFactory_getFactory(void); - -EffectStateFactory *DedicatedStateFactory_getFactory(void); - -EffectStateFactory *ConvolutionStateFactory_getFactory(void); +/* This is a user config option for modifying the overall output of the reverb + * effect. + */ +inline float ReverbBoost{1.0f}; + + +EffectStateFactory *NullStateFactory_getFactory(); +EffectStateFactory *ReverbStateFactory_getFactory(); +EffectStateFactory *ChorusStateFactory_getFactory(); +EffectStateFactory *AutowahStateFactory_getFactory(); +EffectStateFactory *CompressorStateFactory_getFactory(); +EffectStateFactory *DistortionStateFactory_getFactory(); +EffectStateFactory *EchoStateFactory_getFactory(); +EffectStateFactory *EqualizerStateFactory_getFactory(); +EffectStateFactory *FshifterStateFactory_getFactory(); +EffectStateFactory *ModulatorStateFactory_getFactory(); +EffectStateFactory *PshifterStateFactory_getFactory(); +EffectStateFactory* VmorpherStateFactory_getFactory(); + +EffectStateFactory *DedicatedStateFactory_getFactory(); + +EffectStateFactory *ConvolutionStateFactory_getFactory(); #endif /* EFFECTS_BASE_H */ diff --git a/3rdparty/openal/alc/effects/chorus.cpp b/3rdparty/openal/alc/effects/chorus.cpp index c84531d4411d..414dd458c464 100644 --- a/3rdparty/openal/alc/effects/chorus.cpp +++ b/3rdparty/openal/alc/effects/chorus.cpp @@ -22,20 +22,22 @@ #include #include -#include +#include #include -#include +#include +#include #include #include "alc/effects/base.h" -#include "almalloc.h" #include "alnumbers.h" #include "alnumeric.h" #include "alspan.h" +#include "core/ambidefs.h" #include "core/bufferline.h" #include "core/context.h" -#include "core/devformat.h" +#include "core/cubic_tables.h" #include "core/device.h" +#include "core/effects/base.h" #include "core/effectslot.h" #include "core/mixer.h" #include "core/mixer/defs.h" @@ -43,11 +45,19 @@ #include "intrusive_ptr.h" #include "opthelpers.h" +struct BufferStorage; namespace { using uint = unsigned int; +constexpr auto inv_sqrt2 = static_cast(1.0 / al::numbers::sqrt2); +constexpr auto lcoeffs_pw = CalcDirectionCoeffs(std::array{-1.0f, 0.0f, 0.0f}); +constexpr auto rcoeffs_pw = CalcDirectionCoeffs(std::array{ 1.0f, 0.0f, 0.0f}); +constexpr auto lcoeffs_nrml = CalcDirectionCoeffs(std::array{-inv_sqrt2, 0.0f, inv_sqrt2}); +constexpr auto rcoeffs_nrml = CalcDirectionCoeffs(std::array{ inv_sqrt2, 0.0f, inv_sqrt2}); + + struct ChorusState final : public EffectState { std::vector mDelayBuffer; uint mOffset{0}; @@ -58,16 +68,17 @@ struct ChorusState final : public EffectState { uint mLfoDisp{0}; /* Calculated delays to apply to the left and right outputs. */ - uint mModDelays[2][BufferLineSize]; + std::array,2> mModDelays{}; /* Temp storage for the modulated left and right outputs. */ - alignas(16) float mBuffer[2][BufferLineSize]; + alignas(16) std::array mBuffer{}; /* Gains for left and right outputs. */ - struct { - float Current[MaxAmbiChannels]{}; - float Target[MaxAmbiChannels]{}; - } mGains[2]; + struct OutGains { + std::array Current{}; + std::array Target{}; + }; + std::array mGains; /* effect parameters */ ChorusWaveform mWaveform{}; @@ -78,66 +89,70 @@ struct ChorusState final : public EffectState { void calcTriangleDelays(const size_t todo); void calcSinusoidDelays(const size_t todo); - void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override; - void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props, - const EffectTarget target) override; - void process(const size_t samplesToDo, const al::span samplesIn, - const al::span samplesOut) override; + void deviceUpdate(const DeviceBase *device, const float MaxDelay); + void update(const ContextBase *context, const EffectSlot *slot, const ChorusWaveform waveform, + const float delay, const float depth, const float feedback, const float rate, + int phase, const EffectTarget target); - DEF_NEWDEL(ChorusState) + void deviceUpdate(const DeviceBase *device, const BufferStorage*) final; + void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props_, + const EffectTarget target) final; + void process(const size_t samplesToDo, const al::span samplesIn, + const al::span samplesOut) final; }; + void ChorusState::deviceUpdate(const DeviceBase *Device, const BufferStorage*) { - constexpr float max_delay{maxf(ChorusMaxDelay, FlangerMaxDelay)}; - - const auto frequency = static_cast(Device->Frequency); - const size_t maxlen{NextPowerOf2(float2uint(max_delay*2.0f*frequency) + 1u)}; + constexpr auto MaxDelay = std::max(ChorusMaxDelay, FlangerMaxDelay); + const auto frequency = static_cast(Device->mSampleRate); + const size_t maxlen{NextPowerOf2(float2uint(MaxDelay*2.0f*frequency) + 1u)}; if(maxlen != mDelayBuffer.size()) decltype(mDelayBuffer)(maxlen).swap(mDelayBuffer); std::fill(mDelayBuffer.begin(), mDelayBuffer.end(), 0.0f); for(auto &e : mGains) { - std::fill(std::begin(e.Current), std::end(e.Current), 0.0f); - std::fill(std::begin(e.Target), std::end(e.Target), 0.0f); + e.Current.fill(0.0f); + e.Target.fill(0.0f); } } -void ChorusState::update(const ContextBase *Context, const EffectSlot *Slot, - const EffectProps *props, const EffectTarget target) +void ChorusState::update(const ContextBase *context, const EffectSlot *slot, + const EffectProps *props_, const EffectTarget target) { - constexpr int mindelay{(MaxResamplerPadding>>1) << MixerFracBits}; + static constexpr int mindelay{MaxResamplerEdge << gCubicTable.sTableBits}; + auto &props = std::get(*props_); /* The LFO depth is scaled to be relative to the sample delay. Clamp the * delay and depth to allow enough padding for resampling. */ - const DeviceBase *device{Context->mDevice}; - const auto frequency = static_cast(device->Frequency); + const DeviceBase *device{context->mDevice}; + const auto frequency = static_cast(device->mSampleRate); - mWaveform = props->Chorus.Waveform; + mWaveform = props.Waveform; - mDelay = maxi(float2int(props->Chorus.Delay*frequency*MixerFracOne + 0.5f), mindelay); - mDepth = minf(props->Chorus.Depth * static_cast(mDelay), + const auto stepscale = float{frequency * gCubicTable.sTableSteps}; + mDelay = std::max(float2int(std::round(props.Delay * stepscale)), mindelay); + mDepth = std::min(static_cast(mDelay) * props.Depth, static_cast(mDelay - mindelay)); - mFeedback = props->Chorus.Feedback; + mFeedback = props.Feedback; /* Gains for left and right sides */ - static constexpr auto inv_sqrt2 = static_cast(1.0 / al::numbers::sqrt2); - static constexpr auto lcoeffs_pw = CalcDirectionCoeffs(std::array{-1.0f, 0.0f, 0.0f}); - static constexpr auto rcoeffs_pw = CalcDirectionCoeffs(std::array{ 1.0f, 0.0f, 0.0f}); - static constexpr auto lcoeffs_nrml = CalcDirectionCoeffs(std::array{-inv_sqrt2, 0.0f, inv_sqrt2}); - static constexpr auto rcoeffs_nrml = CalcDirectionCoeffs(std::array{ inv_sqrt2, 0.0f, inv_sqrt2}); - auto &lcoeffs = (device->mRenderMode != RenderMode::Pairwise) ? lcoeffs_nrml : lcoeffs_pw; - auto &rcoeffs = (device->mRenderMode != RenderMode::Pairwise) ? rcoeffs_nrml : rcoeffs_pw; + const bool ispairwise{device->mRenderMode == RenderMode::Pairwise}; + const auto lcoeffs = (!ispairwise) ? al::span{lcoeffs_nrml} : al::span{lcoeffs_pw}; + const auto rcoeffs = (!ispairwise) ? al::span{rcoeffs_nrml} : al::span{rcoeffs_pw}; + /* Attenuate the outputs by -3dB, since we duplicate a single mono input to + * separate left/right outputs. + */ + const auto gain = slot->Gain * (1.0f/al::numbers::sqrt2_v); mOutTarget = target.Main->Buffer; - ComputePanGains(target.Main, lcoeffs.data(), Slot->Gain, mGains[0].Target); - ComputePanGains(target.Main, rcoeffs.data(), Slot->Gain, mGains[1].Target); + ComputePanGains(target.Main, lcoeffs, gain, mGains[0].Target); + ComputePanGains(target.Main, rcoeffs, gain, mGains[1].Target); - float rate{props->Chorus.Rate}; - if(!(rate > 0.0f)) + if(!(props.Rate > 0.0f)) { mLfoOffset = 0; mLfoRange = 1; @@ -149,7 +164,9 @@ void ChorusState::update(const ContextBase *Context, const EffectSlot *Slot, /* Calculate LFO coefficient (number of samples per cycle). Limit the * max range to avoid overflow when calculating the displacement. */ - uint lfo_range{float2uint(minf(frequency/rate + 0.5f, float{INT_MAX/360 - 180}))}; + static constexpr int range_limit{std::numeric_limits::max()/360 - 180}; + const auto range = std::round(frequency / props.Rate); + const uint lfo_range{float2uint(std::min(range, float{range_limit}))}; mLfoOffset = mLfoOffset * lfo_range / mLfoRange; mLfoRange = lfo_range; @@ -164,8 +181,8 @@ void ChorusState::update(const ContextBase *Context, const EffectSlot *Slot, } /* Calculate lfo phase displacement */ - int phase{props->Chorus.Phase}; - if(phase < 0) phase = 360 + phase; + auto phase = props.Phase; + if(phase < 0) phase += 360; mLfoDisp = (mLfoRange*static_cast(phase) + 180) / 360; } } @@ -178,9 +195,6 @@ void ChorusState::calcTriangleDelays(const size_t todo) const float depth{mDepth}; const int delay{mDelay}; - ASSUME(lfo_range > 0); - ASSUME(todo > 0); - auto gen_lfo = [lfo_scale,depth,delay](const uint offset) -> uint { const float offset_norm{static_cast(offset) * lfo_scale}; @@ -188,25 +202,24 @@ void ChorusState::calcTriangleDelays(const size_t todo) }; uint offset{mLfoOffset}; + ASSUME(lfo_range > offset); + auto ldelays = mModDelays[0].begin(); for(size_t i{0};i < todo;) { - size_t rem{minz(todo-i, lfo_range-offset)}; - do { - mModDelays[0][i++] = gen_lfo(offset++); - } while(--rem); - if(offset == lfo_range) - offset = 0; + const size_t rem{std::min(todo-i, size_t{lfo_range-offset})}; + ldelays = std::generate_n(ldelays, rem, [&offset,gen_lfo] { return gen_lfo(offset++); }); + if(offset == lfo_range) offset = 0; + i += rem; } offset = (mLfoOffset+mLfoDisp) % lfo_range; + auto rdelays = mModDelays[1].begin(); for(size_t i{0};i < todo;) { - size_t rem{minz(todo-i, lfo_range-offset)}; - do { - mModDelays[1][i++] = gen_lfo(offset++); - } while(--rem); - if(offset == lfo_range) - offset = 0; + const size_t rem{std::min(todo-i, size_t{lfo_range-offset})}; + rdelays = std::generate_n(rdelays, rem, [&offset,gen_lfo] { return gen_lfo(offset++); }); + if(offset == lfo_range) offset = 0; + i += rem; } mLfoOffset = static_cast(mLfoOffset+todo) % lfo_range; @@ -219,9 +232,6 @@ void ChorusState::calcSinusoidDelays(const size_t todo) const float depth{mDepth}; const int delay{mDelay}; - ASSUME(lfo_range > 0); - ASSUME(todo > 0); - auto gen_lfo = [lfo_scale,depth,delay](const uint offset) -> uint { const float offset_norm{static_cast(offset) * lfo_scale}; @@ -229,25 +239,24 @@ void ChorusState::calcSinusoidDelays(const size_t todo) }; uint offset{mLfoOffset}; + ASSUME(lfo_range > offset); + auto ldelays = mModDelays[0].begin(); for(size_t i{0};i < todo;) { - size_t rem{minz(todo-i, lfo_range-offset)}; - do { - mModDelays[0][i++] = gen_lfo(offset++); - } while(--rem); - if(offset == lfo_range) - offset = 0; + const size_t rem{std::min(todo-i, size_t{lfo_range-offset})}; + ldelays = std::generate_n(ldelays, rem, [&offset,gen_lfo] { return gen_lfo(offset++); }); + if(offset == lfo_range) offset = 0; + i += rem; } offset = (mLfoOffset+mLfoDisp) % lfo_range; + auto rdelays = mModDelays[1].begin(); for(size_t i{0};i < todo;) { - size_t rem{minz(todo-i, lfo_range-offset)}; - do { - mModDelays[1][i++] = gen_lfo(offset++); - } while(--rem); - if(offset == lfo_range) - offset = 0; + const size_t rem{std::min(todo-i, size_t{lfo_range-offset})}; + rdelays = std::generate_n(rdelays, rem, [&offset,gen_lfo] { return gen_lfo(offset++); }); + if(offset == lfo_range) offset = 0; + i += rem; } mLfoOffset = static_cast(mLfoOffset+todo) % lfo_range; @@ -255,10 +264,10 @@ void ChorusState::calcSinusoidDelays(const size_t todo) void ChorusState::process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) { - const size_t bufmask{mDelayBuffer.size()-1}; + const auto delaybuf = al::span{mDelayBuffer}; + const size_t bufmask{delaybuf.size()-1}; const float feedback{mFeedback}; const uint avgdelay{(static_cast(mDelay) + MixerFracHalf) >> MixerFracBits}; - float *RESTRICT delaybuf{mDelayBuffer.data()}; uint offset{mOffset}; if(mWaveform == ChorusWaveform::Sinusoid) @@ -266,35 +275,39 @@ void ChorusState::process(const size_t samplesToDo, const al::span(mBuffer[0])}; - float *RESTRICT rbuffer{al::assume_aligned<16>(mBuffer[1])}; + const auto ldelays = al::span{mModDelays[0]}; + const auto rdelays = al::span{mModDelays[1]}; + const auto lbuffer = al::span{mBuffer[0]}; + const auto rbuffer = al::span{mBuffer[1]}; for(size_t i{0u};i < samplesToDo;++i) { // Feed the buffer's input first (necessary for delays < 1). delaybuf[offset&bufmask] = samplesIn[0][i]; // Tap for the left output. - uint delay{offset - (ldelays[i]>>MixerFracBits)}; - float mu{static_cast(ldelays[i]&MixerFracMask) * (1.0f/MixerFracOne)}; - lbuffer[i] = cubic(delaybuf[(delay+1) & bufmask], delaybuf[(delay ) & bufmask], - delaybuf[(delay-1) & bufmask], delaybuf[(delay-2) & bufmask], mu); + size_t delay{offset - (ldelays[i] >> gCubicTable.sTableBits)}; + size_t phase{ldelays[i] & gCubicTable.sTableMask}; + lbuffer[i] = delaybuf[(delay+1) & bufmask]*gCubicTable.getCoeff0(phase) + + delaybuf[(delay ) & bufmask]*gCubicTable.getCoeff1(phase) + + delaybuf[(delay-1) & bufmask]*gCubicTable.getCoeff2(phase) + + delaybuf[(delay-2) & bufmask]*gCubicTable.getCoeff3(phase); // Tap for the right output. - delay = offset - (rdelays[i]>>MixerFracBits); - mu = static_cast(rdelays[i]&MixerFracMask) * (1.0f/MixerFracOne); - rbuffer[i] = cubic(delaybuf[(delay+1) & bufmask], delaybuf[(delay ) & bufmask], - delaybuf[(delay-1) & bufmask], delaybuf[(delay-2) & bufmask], mu); + delay = offset - (rdelays[i] >> gCubicTable.sTableBits); + phase = rdelays[i] & gCubicTable.sTableMask; + rbuffer[i] = delaybuf[(delay+1) & bufmask]*gCubicTable.getCoeff0(phase) + + delaybuf[(delay ) & bufmask]*gCubicTable.getCoeff1(phase) + + delaybuf[(delay-1) & bufmask]*gCubicTable.getCoeff2(phase) + + delaybuf[(delay-2) & bufmask]*gCubicTable.getCoeff3(phase); // Accumulate feedback from the average delay of the taps. delaybuf[offset&bufmask] += delaybuf[(offset-avgdelay) & bufmask] * feedback; ++offset; } - MixSamples({lbuffer, samplesToDo}, samplesOut, mGains[0].Current, mGains[0].Target, + MixSamples(lbuffer.first(samplesToDo), samplesOut, mGains[0].Current, mGains[0].Target, samplesToDo, 0); - MixSamples({rbuffer, samplesToDo}, samplesOut, mGains[1].Current, mGains[1].Target, + MixSamples(rbuffer.first(samplesToDo), samplesOut, mGains[1].Current, mGains[1].Target, samplesToDo, 0); mOffset = offset; @@ -306,15 +319,6 @@ struct ChorusStateFactory final : public EffectStateFactory { { return al::intrusive_ptr{new ChorusState{}}; } }; - -/* Flanger is basically a chorus with a really short delay. They can both use - * the same processing functions, so piggyback flanger on the chorus functions. - */ -struct FlangerStateFactory final : public EffectStateFactory { - al::intrusive_ptr create() override - { return al::intrusive_ptr{new ChorusState{}}; } -}; - } // namespace EffectStateFactory *ChorusStateFactory_getFactory() @@ -322,9 +326,3 @@ EffectStateFactory *ChorusStateFactory_getFactory() static ChorusStateFactory ChorusFactory{}; return &ChorusFactory; } - -EffectStateFactory *FlangerStateFactory_getFactory() -{ - static FlangerStateFactory FlangerFactory{}; - return &FlangerFactory; -} diff --git a/3rdparty/openal/alc/effects/compressor.cpp b/3rdparty/openal/alc/effects/compressor.cpp index 0a7ed67a78fb..27245b69c42f 100644 --- a/3rdparty/openal/alc/effects/compressor.cpp +++ b/3rdparty/openal/alc/effects/compressor.cpp @@ -32,48 +32,49 @@ #include "config.h" +#include #include +#include #include -#include -#include +#include #include "alc/effects/base.h" -#include "almalloc.h" -#include "alnumeric.h" #include "alspan.h" #include "core/ambidefs.h" #include "core/bufferline.h" -#include "core/devformat.h" #include "core/device.h" +#include "core/effects/base.h" #include "core/effectslot.h" -#include "core/mixer.h" #include "core/mixer/defs.h" #include "intrusive_ptr.h" +struct BufferStorage; struct ContextBase; namespace { -#define AMP_ENVELOPE_MIN 0.5f -#define AMP_ENVELOPE_MAX 2.0f +constexpr float AmpEnvelopeMin{0.5f}; +constexpr float AmpEnvelopeMax{2.0f}; -#define ATTACK_TIME 0.1f /* 100ms to rise from min to max */ -#define RELEASE_TIME 0.2f /* 200ms to drop from max to min */ +constexpr float AttackTime{0.1f}; /* 100ms to rise from min to max */ +constexpr float ReleaseTime{0.2f}; /* 200ms to drop from max to min */ struct CompressorState final : public EffectState { /* Effect gains for each channel */ - struct { + struct TargetGain { uint mTarget{InvalidChannelIndex}; float mGain{1.0f}; - } mChans[MaxAmbiChannels]; + }; + std::array mChans; /* Effect parameters */ bool mEnabled{true}; float mAttackMult{1.0f}; float mReleaseMult{1.0f}; float mEnvFollower{1.0f}; + alignas(16) FloatBufferLine mGains{}; void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override; @@ -81,8 +82,6 @@ struct CompressorState final : public EffectState { const EffectTarget target) override; void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; - - DEF_NEWDEL(CompressorState) }; void CompressorState::deviceUpdate(const DeviceBase *device, const BufferStorage*) @@ -90,20 +89,20 @@ void CompressorState::deviceUpdate(const DeviceBase *device, const BufferStorage /* Number of samples to do a full attack and release (non-integer sample * counts are okay). */ - const float attackCount{static_cast(device->Frequency) * ATTACK_TIME}; - const float releaseCount{static_cast(device->Frequency) * RELEASE_TIME}; + const float attackCount{static_cast(device->mSampleRate) * AttackTime}; + const float releaseCount{static_cast(device->mSampleRate) * ReleaseTime}; /* Calculate per-sample multipliers to attack and release at the desired * rates. */ - mAttackMult = std::pow(AMP_ENVELOPE_MAX/AMP_ENVELOPE_MIN, 1.0f/attackCount); - mReleaseMult = std::pow(AMP_ENVELOPE_MIN/AMP_ENVELOPE_MAX, 1.0f/releaseCount); + mAttackMult = std::pow(AmpEnvelopeMax/AmpEnvelopeMin, 1.0f/attackCount); + mReleaseMult = std::pow(AmpEnvelopeMin/AmpEnvelopeMax, 1.0f/releaseCount); } void CompressorState::update(const ContextBase*, const EffectSlot *slot, const EffectProps *props, const EffectTarget target) { - mEnabled = props->Compressor.OnOff; + mEnabled = std::get(*props).OnOff; mOutTarget = target.Main->Buffer; auto set_channel = [this](size_t idx, uint outchan, float outgain) @@ -117,72 +116,62 @@ void CompressorState::update(const ContextBase*, const EffectSlot *slot, void CompressorState::process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) { - for(size_t base{0u};base < samplesToDo;) + /* Generate the per-sample gains from the signal envelope. */ + float env{mEnvFollower}; + if(mEnabled) { - float gains[256]; - const size_t td{minz(256, samplesToDo-base)}; - - /* Generate the per-sample gains from the signal envelope. */ - float env{mEnvFollower}; - if(mEnabled) + for(size_t i{0u};i < samplesToDo;++i) { - for(size_t i{0u};i < td;++i) - { - /* Clamp the absolute amplitude to the defined envelope limits, - * then attack or release the envelope to reach it. - */ - const float amplitude{clampf(std::fabs(samplesIn[0][base+i]), AMP_ENVELOPE_MIN, - AMP_ENVELOPE_MAX)}; - if(amplitude > env) - env = minf(env*mAttackMult, amplitude); - else if(amplitude < env) - env = maxf(env*mReleaseMult, amplitude); - - /* Apply the reciprocal of the envelope to normalize the volume - * (compress the dynamic range). - */ - gains[i] = 1.0f / env; - } + /* Clamp the absolute amplitude to the defined envelope limits, + * then attack or release the envelope to reach it. + */ + const float amplitude{std::clamp(std::fabs(samplesIn[0][i]), AmpEnvelopeMin, + AmpEnvelopeMax)}; + if(amplitude > env) + env = std::min(env*mAttackMult, amplitude); + else if(amplitude < env) + env = std::max(env*mReleaseMult, amplitude); + + /* Apply the reciprocal of the envelope to normalize the volume + * (compress the dynamic range). + */ + mGains[i] = 1.0f / env; } - else + } + else + { + /* Same as above, except the amplitude is forced to 1. This helps + * ensure smooth gain changes when the compressor is turned on and off. + */ + for(size_t i{0u};i < samplesToDo;++i) { - /* Same as above, except the amplitude is forced to 1. This helps - * ensure smooth gain changes when the compressor is turned on and - * off. - */ - for(size_t i{0u};i < td;++i) - { - const float amplitude{1.0f}; - if(amplitude > env) - env = minf(env*mAttackMult, amplitude); - else if(amplitude < env) - env = maxf(env*mReleaseMult, amplitude); + const float amplitude{1.0f}; + if(amplitude > env) + env = std::min(env*mAttackMult, amplitude); + else if(amplitude < env) + env = std::max(env*mReleaseMult, amplitude); - gains[i] = 1.0f / env; - } + mGains[i] = 1.0f / env; } - mEnvFollower = env; + } + mEnvFollower = env; - /* Now compress the signal amplitude to output. */ - auto chan = std::cbegin(mChans); - for(const auto &input : samplesIn) + /* Now compress the signal amplitude to output. */ + auto chan = mChans.cbegin(); + for(const auto &input : samplesIn) + { + const size_t outidx{chan->mTarget}; + if(outidx != InvalidChannelIndex) { - const size_t outidx{chan->mTarget}; - if(outidx != InvalidChannelIndex) + const auto dst = al::span{samplesOut[outidx]}; + const float gain{chan->mGain}; + if(!(std::fabs(gain) > GainSilenceThreshold)) { - const float *RESTRICT src{input.data() + base}; - float *RESTRICT dst{samplesOut[outidx].data() + base}; - const float gain{chan->mGain}; - if(!(std::fabs(gain) > GainSilenceThreshold)) - { - for(size_t i{0u};i < td;i++) - dst[i] += src[i] * gains[i] * gain; - } + for(size_t i{0u};i < samplesToDo;++i) + dst[i] += input[i] * mGains[i] * gain; } - ++chan; } - - base += td; + ++chan; } } diff --git a/3rdparty/openal/alc/effects/convolution.cpp b/3rdparty/openal/alc/effects/convolution.cpp index f46422d40d94..cafa4e6f1cff 100644 --- a/3rdparty/openal/alc/effects/convolution.cpp +++ b/3rdparty/openal/alc/effects/convolution.cpp @@ -1,19 +1,21 @@ #include "config.h" +#include "config_simd.h" #include #include +#include +#include #include #include +#include #include -#include #include -#include -#include +#include -#ifdef HAVE_SSE_INTRINSICS +#if HAVE_SSE_INTRINSICS #include -#elif defined(HAVE_NEON) +#elif HAVE_NEON #include #endif @@ -29,56 +31,85 @@ #include "core/context.h" #include "core/devformat.h" #include "core/device.h" +#include "core/effects/base.h" #include "core/effectslot.h" #include "core/filters/splitter.h" #include "core/fmt_traits.h" #include "core/mixer.h" +#include "core/uhjfilter.h" #include "intrusive_ptr.h" +#include "opthelpers.h" +#include "pffft.h" #include "polyphase_resampler.h" +#include "vecmat.h" #include "vector.h" namespace { -/* Convolution reverb is implemented using a segmented overlap-add method. The - * impulse response is broken up into multiple segments of 128 samples, and - * each segment has an FFT applied with a 256-sample buffer (the latter half - * left silent) to get its frequency-domain response. The resulting response - * has its positive/non-mirrored frequencies saved (129 bins) in each segment. +/* Convolution is implemented using a segmented overlap-add method. The impulse + * response is split into multiple segments of 128 samples, and each segment + * has an FFT applied with a 256-sample buffer (the latter half left silent) to + * get its frequency-domain response. The resulting response has its positive/ + * non-mirrored frequencies saved (129 bins) in each segment. Note that since + * the 0- and half-frequency bins are real for a real signal, their imaginary + * components are always 0 and can be dropped, allowing their real components + * to be combined so only 128 complex values are stored for the 129 bins. * - * Input samples are similarly broken up into 128-sample segments, with an FFT - * applied to each new incoming segment to get its 129 bins. A history of FFT'd - * input segments is maintained, equal to the length of the impulse response. + * Input samples are similarly broken up into 128-sample segments, with a 256- + * sample FFT applied to each new incoming segment to get its 129 bins. A + * history of FFT'd input segments is maintained, equal to the number of + * impulse response segments. * - * To apply the reverberation, each impulse response segment is convolved with + * To apply the convolution, each impulse response segment is convolved with * its paired input segment (using complex multiplies, far cheaper than FIRs), - * accumulating into a 256-bin FFT buffer. The input history is then shifted to - * align with later impulse response segments for next time. + * accumulating into a 129-bin FFT buffer. The input history is then shifted to + * align with later impulse response segments for the next input segment. * * An inverse FFT is then applied to the accumulated FFT buffer to get a 256- * sample time-domain response for output, which is split in two halves. The * first half is the 128-sample output, and the second half is a 128-sample * (really, 127) delayed extension, which gets added to the output next time. - * Convolving two time-domain responses of lengths N and M results in a time- - * domain signal of length N+M-1, and this holds true regardless of the - * convolution being applied in the frequency domain, so these "overflow" - * samples need to be accounted for. + * Convolving two time-domain responses of length N results in a time-domain + * signal of length N*2 - 1, and this holds true regardless of the convolution + * being applied in the frequency domain, so these "overflow" samples need to + * be accounted for. * - * To avoid a delay with gathering enough input samples to apply an FFT with, - * the first segment is applied directly in the time-domain as the samples come - * in. Once enough have been retrieved, the FFT is applied on the input and - * it's paired with the remaining (FFT'd) filter segments for processing. + * To avoid a delay with gathering enough input samples for the FFT, the first + * segment is applied directly in the time-domain as the samples come in. Once + * enough have been retrieved, the FFT is applied on the input and it's paired + * with the remaining (FFT'd) filter segments for processing. */ -void LoadSamples(float *RESTRICT dst, const std::byte *src, const size_t srcstep, FmtType srctype, - const size_t samples) noexcept +template +inline void LoadSampleArray(const al::span dst, const std::byte *src, + const std::size_t channel, const std::size_t srcstep) noexcept { -#define HANDLE_FMT(T) case T: al::LoadSampleArray(dst, src, srcstep, samples); break + using TypeTraits = al::FmtTypeTraits; + using SampleType = typename TypeTraits::Type; + const auto converter = TypeTraits{}; + assert(channel < srcstep); + + const auto srcspan = al::span{reinterpret_cast(src), dst.size()*srcstep}; + auto ssrc = srcspan.cbegin(); + std::generate(dst.begin(), dst.end(), [converter,channel,srcstep,&ssrc] + { + const auto ret = converter(ssrc[channel]); + ssrc += ptrdiff_t(srcstep); + return ret; + }); +} + +void LoadSamples(const al::span dst, const std::byte *src, const size_t channel, + const size_t srcstep, const FmtType srctype) noexcept +{ +#define HANDLE_FMT(T) case T: LoadSampleArray(dst, src, channel, srcstep); break switch(srctype) { HANDLE_FMT(FmtUByte); HANDLE_FMT(FmtShort); + HANDLE_FMT(FmtInt); HANDLE_FMT(FmtFloat); HANDLE_FMT(FmtDouble); HANDLE_FMT(FmtMulaw); @@ -86,7 +117,7 @@ void LoadSamples(float *RESTRICT dst, const std::byte *src, const size_t srcstep /* FIXME: Handle ADPCM decoding here. */ case FmtIMA4: case FmtMSADPCM: - std::fill_n(dst, samples, 0.0f); + std::fill(dst.begin(), dst.end(), 0.0f); break; } #undef HANDLE_FMT @@ -137,10 +168,11 @@ constexpr size_t ConvolveUpdateSize{256}; constexpr size_t ConvolveUpdateSamples{ConvolveUpdateSize / 2}; -void apply_fir(al::span dst, const float *RESTRICT src, const float *RESTRICT filter) +void apply_fir(al::span dst, const al::span input, const al::span filter) { -#ifdef HAVE_SSE_INTRINSICS - for(float &output : dst) + auto src = input.begin(); +#if HAVE_SSE_INTRINSICS + std::generate(dst.begin(), dst.end(), [&src,filter] { __m128 r4{_mm_setzero_ps()}; for(size_t j{0};j < ConvolveUpdateSamples;j+=4) @@ -150,39 +182,40 @@ void apply_fir(al::span dst, const float *RESTRICT src, const float *REST r4 = _mm_add_ps(r4, _mm_mul_ps(s, coeffs)); } + ++src; + r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3))); r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4)); - output = _mm_cvtss_f32(r4); + return _mm_cvtss_f32(r4); + }); - ++src; - } - -#elif defined(HAVE_NEON) +#elif HAVE_NEON - for(float &output : dst) + std::generate(dst.begin(), dst.end(), [&src,filter] { float32x4_t r4{vdupq_n_f32(0.0f)}; for(size_t j{0};j < ConvolveUpdateSamples;j+=4) r4 = vmlaq_f32(r4, vld1q_f32(&src[j]), vld1q_f32(&filter[j])); - r4 = vaddq_f32(r4, vrev64q_f32(r4)); - output = vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0); - ++src; - } + + r4 = vaddq_f32(r4, vrev64q_f32(r4)); + return vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0); + }); #else - for(float &output : dst) + std::generate(dst.begin(), dst.end(), [&src,filter] { float ret{0.0f}; for(size_t j{0};j < ConvolveUpdateSamples;++j) ret += src[j] * filter[j]; - output = ret; ++src; - } + return ret; + }); #endif } + struct ConvolutionState final : public EffectState { FmtChannels mChannels{}; AmbiLayout mAmbiLayout{}; @@ -190,11 +223,13 @@ struct ConvolutionState final : public EffectState { uint mAmbiOrder{}; size_t mFifoPos{0}; - std::array mInput{}; + alignas(16) std::array mInput{}; al::vector,16> mFilter; al::vector,16> mOutput; - alignas(16) std::array mFftBuffer{}; + PFFFTSetup mFft; + alignas(16) std::array mFftBuffer{}; + alignas(16) std::array mFftWorkBuffer{}; size_t mCurrentSegment{0}; size_t mNumConvolveSegs{0}; @@ -202,13 +237,12 @@ struct ConvolutionState final : public EffectState { struct ChannelData { alignas(16) FloatBufferLine mBuffer{}; float mHfScale{}, mLfScale{}; - BandSplitter mFilter{}; - float Current[MAX_OUTPUT_CHANNELS]{}; - float Target[MAX_OUTPUT_CHANNELS]{}; + BandSplitter mFilter; + std::array Current{}; + std::array Target{}; }; - using ChannelDataArray = al::FlexArray; - std::unique_ptr mChans; - std::unique_ptr mComplexData; + std::vector mChans; + al::vector mComplexData; ConvolutionState() = default; @@ -224,24 +258,22 @@ struct ConvolutionState final : public EffectState { const EffectTarget target) override; void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; - - DEF_NEWDEL(ConvolutionState) }; void ConvolutionState::NormalMix(const al::span samplesOut, const size_t samplesToDo) { - for(auto &chan : *mChans) - MixSamples({chan.mBuffer.data(), samplesToDo}, samplesOut, chan.Current, chan.Target, - samplesToDo, 0); + for(auto &chan : mChans) + MixSamples(al::span{chan.mBuffer}.first(samplesToDo), samplesOut, chan.Current, + chan.Target, samplesToDo, 0); } void ConvolutionState::UpsampleMix(const al::span samplesOut, const size_t samplesToDo) { - for(auto &chan : *mChans) + for(auto &chan : mChans) { - const al::span src{chan.mBuffer.data(), samplesToDo}; + const auto src = al::span{chan.mBuffer}.first(samplesToDo); chan.mFilter.processScale(src, chan.mHfScale, chan.mLfScale); MixSamples(src, samplesOut, chan.Current, chan.Target, samplesToDo, 0); } @@ -253,19 +285,23 @@ void ConvolutionState::deviceUpdate(const DeviceBase *device, const BufferStorag using UhjDecoderType = UhjDecoder<512>; static constexpr auto DecoderPadding = UhjDecoderType::sInputPadding; - constexpr uint MaxConvolveAmbiOrder{1u}; + static constexpr uint MaxConvolveAmbiOrder{1u}; + + if(!mFft) + mFft = PFFFTSetup{ConvolveUpdateSize, PFFFT_REAL}; mFifoPos = 0; mInput.fill(0.0f); decltype(mFilter){}.swap(mFilter); decltype(mOutput){}.swap(mOutput); - mFftBuffer.fill(complex_f{}); + mFftBuffer.fill(0.0f); + mFftWorkBuffer.fill(0.0f); mCurrentSegment = 0; mNumConvolveSegs = 0; - mChans = nullptr; - mComplexData = nullptr; + decltype(mChans){}.swap(mChans); + decltype(mComplexData){}.swap(mComplexData); /* An empty buffer doesn't need a convolution filter. */ if(!buffer || buffer->mSampleLen < 1) return; @@ -273,14 +309,12 @@ void ConvolutionState::deviceUpdate(const DeviceBase *device, const BufferStorag mChannels = buffer->mChannels; mAmbiLayout = IsUHJ(mChannels) ? AmbiLayout::FuMa : buffer->mAmbiLayout; mAmbiScaling = IsUHJ(mChannels) ? AmbiScaling::UHJ : buffer->mAmbiScaling; - mAmbiOrder = minu(buffer->mAmbiOrder, MaxConvolveAmbiOrder); + mAmbiOrder = std::min(buffer->mAmbiOrder, MaxConvolveAmbiOrder); - constexpr size_t m{ConvolveUpdateSize/2 + 1}; - const auto bytesPerSample = BytesFromFmt(buffer->mType); const auto realChannels = buffer->channelsFromFmt(); const auto numChannels = (mChannels == FmtUHJ2) ? 3u : ChannelsFromFmt(mChannels, mAmbiOrder); - mChans = ChannelDataArray::Create(numChannels); + mChans.resize(numChannels); /* The impulse response needs to have the same sample rate as the input and * output. The bsinc24 resampler is decent, but there is high-frequency @@ -288,14 +322,14 @@ void ConvolutionState::deviceUpdate(const DeviceBase *device, const BufferStorag * called very infrequently, go ahead and use the polyphase resampler. */ PPhaseResampler resampler; - if(device->Frequency != buffer->mSampleRate) - resampler.init(buffer->mSampleRate, device->Frequency); + if(device->mSampleRate != buffer->mSampleRate) + resampler.init(buffer->mSampleRate, device->mSampleRate); const auto resampledCount = static_cast( - (uint64_t{buffer->mSampleLen}*device->Frequency+(buffer->mSampleRate-1)) / + (uint64_t{buffer->mSampleLen}*device->mSampleRate+(buffer->mSampleRate-1)) / buffer->mSampleRate); - const BandSplitter splitter{device->mXOverFreq / static_cast(device->Frequency)}; - for(auto &e : *mChans) + const BandSplitter splitter{device->mXOverFreq / static_cast(device->mSampleRate)}; + for(auto &e : mChans) e.mFilter = splitter; mFilter.resize(numChannels, {}); @@ -307,126 +341,150 @@ void ConvolutionState::deviceUpdate(const DeviceBase *device, const BufferStorag * segment is allocated to simplify handling. */ mNumConvolveSegs = (resampledCount+(ConvolveUpdateSamples-1)) / ConvolveUpdateSamples; - mNumConvolveSegs = maxz(mNumConvolveSegs, 2) - 1; + mNumConvolveSegs = std::max(mNumConvolveSegs, 2_uz) - 1_uz; - const size_t complex_length{mNumConvolveSegs * m * (numChannels+1)}; - mComplexData = std::make_unique(complex_length); - std::fill_n(mComplexData.get(), complex_length, complex_f{}); + const size_t complex_length{mNumConvolveSegs * ConvolveUpdateSize * (numChannels+1)}; + mComplexData.resize(complex_length, 0.0f); /* Load the samples from the buffer. */ const size_t srclinelength{RoundUp(buffer->mSampleLen+DecoderPadding, 16)}; - auto srcsamples = std::make_unique(srclinelength * numChannels); - std::fill_n(srcsamples.get(), srclinelength * numChannels, 0.0f); + auto srcsamples = std::vector(srclinelength * numChannels); + std::fill(srcsamples.begin(), srcsamples.end(), 0.0f); for(size_t c{0};c < numChannels && c < realChannels;++c) - LoadSamples(srcsamples.get() + srclinelength*c, buffer->mData.data() + bytesPerSample*c, - realChannels, buffer->mType, buffer->mSampleLen); + LoadSamples(al::span{srcsamples}.subspan(srclinelength*c, buffer->mSampleLen), + buffer->mData.data(), c, realChannels, buffer->mType); if(IsUHJ(mChannels)) { auto decoder = std::make_unique(); std::array samples{}; for(size_t c{0};c < numChannels;++c) - samples[c] = srcsamples.get() + srclinelength*c; + samples[c] = al::to_address(srcsamples.begin() + ptrdiff_t(srclinelength*c)); decoder->decode({samples.data(), numChannels}, buffer->mSampleLen, buffer->mSampleLen); } - auto ressamples = std::make_unique(buffer->mSampleLen + - (resampler ? resampledCount : 0)); - complex_f *filteriter = mComplexData.get() + mNumConvolveSegs*m; + auto ressamples = std::vector(buffer->mSampleLen + (resampler ? resampledCount : 0)); + auto ffttmp = al::vector(ConvolveUpdateSize); + auto fftbuffer = std::vector>(ConvolveUpdateSize); + + auto filteriter = mComplexData.begin() + ptrdiff_t(mNumConvolveSegs*ConvolveUpdateSize); for(size_t c{0};c < numChannels;++c) { + auto bufsamples = al::span{srcsamples}.subspan(srclinelength*c, buffer->mSampleLen); /* Resample to match the device. */ if(resampler) { - std::copy_n(srcsamples.get() + srclinelength*c, buffer->mSampleLen, - ressamples.get() + resampledCount); - resampler.process(buffer->mSampleLen, ressamples.get()+resampledCount, - resampledCount, ressamples.get()); + auto restmp = al::span{ressamples}.subspan(resampledCount, buffer->mSampleLen); + std::copy(bufsamples.cbegin(), bufsamples.cend(), restmp.begin()); + resampler.process(restmp, al::span{ressamples}.first(resampledCount)); } else - std::copy_n(srcsamples.get() + srclinelength*c, buffer->mSampleLen, ressamples.get()); + std::copy(bufsamples.cbegin(), bufsamples.cend(), ressamples.begin()); /* Store the first segment's samples in reverse in the time-domain, to * apply as a FIR filter. */ - const size_t first_size{minz(resampledCount, ConvolveUpdateSamples)}; - std::transform(ressamples.get(), ressamples.get()+first_size, mFilter[c].rbegin(), + const size_t first_size{std::min(size_t{resampledCount}, ConvolveUpdateSamples)}; + auto sampleseg = al::span{ressamples.cbegin(), first_size}; + std::transform(sampleseg.cbegin(), sampleseg.cend(), mFilter[c].rbegin(), [](const double d) noexcept -> float { return static_cast(d); }); - auto fftbuffer = std::vector>(ConvolveUpdateSize); size_t done{first_size}; for(size_t s{0};s < mNumConvolveSegs;++s) { - const size_t todo{minz(resampledCount-done, ConvolveUpdateSamples)}; + const size_t todo{std::min(resampledCount-done, ConvolveUpdateSamples)}; + sampleseg = al::span{ressamples}.subspan(done, todo); - auto iter = std::copy_n(&ressamples[done], todo, fftbuffer.begin()); + /* Apply a double-precision forward FFT for more precise frequency + * measurements. + */ + auto iter = std::copy(sampleseg.cbegin(), sampleseg.cend(), fftbuffer.begin()); done += todo; std::fill(iter, fftbuffer.end(), std::complex{}); - forward_fft(al::span{fftbuffer}); - filteriter = std::copy_n(fftbuffer.cbegin(), m, filteriter); + + /* Convert to, and pack in, a float buffer for PFFFT. Note that the + * first bin stores the real component of the half-frequency bin in + * the imaginary component. Also scale the FFT by its length so the + * iFFT'd output will be normalized. + */ + static constexpr float fftscale{1.0f / float{ConvolveUpdateSize}}; + for(size_t i{0};i < ConvolveUpdateSamples;++i) + { + ffttmp[i*2 ] = static_cast(fftbuffer[i].real()) * fftscale; + ffttmp[i*2 + 1] = static_cast((i == 0) ? + fftbuffer[ConvolveUpdateSamples].real() : fftbuffer[i].imag()) * fftscale; + } + /* Reorder backward to make it suitable for pffft_zconvolve and the + * subsequent pffft_transform(..., PFFFT_BACKWARD). + */ + mFft.zreorder(ffttmp.data(), al::to_address(filteriter), PFFFT_BACKWARD); + filteriter += ConvolveUpdateSize; } } } void ConvolutionState::update(const ContextBase *context, const EffectSlot *slot, - const EffectProps* /*props*/, const EffectTarget target) + const EffectProps *props_, const EffectTarget target) { - /* NOTE: Stereo and Rear are slightly different from normal mixing (as - * defined in alu.cpp). These are 45 degrees from center, rather than the - * 30 degrees used there. - * - * TODO: LFE is not mixed to output. This will require each buffer channel + /* TODO: LFE is not mixed to output. This will require each buffer channel * to have its own output target since the main mixing buffer won't have an * LFE channel (due to being B-Format). */ - static constexpr ChanPosMap MonoMap[1]{ - { FrontCenter, std::array{0.0f, 0.0f, -1.0f} } - }, StereoMap[2]{ - { FrontLeft, std::array{-sin45, 0.0f, -cos45} }, - { FrontRight, std::array{ sin45, 0.0f, -cos45} }, - }, RearMap[2]{ - { BackLeft, std::array{-sin45, 0.0f, cos45} }, - { BackRight, std::array{ sin45, 0.0f, cos45} }, - }, QuadMap[4]{ - { FrontLeft, std::array{-sin45, 0.0f, -cos45} }, - { FrontRight, std::array{ sin45, 0.0f, -cos45} }, - { BackLeft, std::array{-sin45, 0.0f, cos45} }, - { BackRight, std::array{ sin45, 0.0f, cos45} }, - }, X51Map[6]{ - { FrontLeft, std::array{-sin30, 0.0f, -cos30} }, - { FrontRight, std::array{ sin30, 0.0f, -cos30} }, - { FrontCenter, std::array{ 0.0f, 0.0f, -1.0f} }, - { LFE, {} }, - { SideLeft, std::array{-sin110, 0.0f, -cos110} }, - { SideRight, std::array{ sin110, 0.0f, -cos110} }, - }, X61Map[7]{ - { FrontLeft, std::array{-sin30, 0.0f, -cos30} }, - { FrontRight, std::array{ sin30, 0.0f, -cos30} }, - { FrontCenter, std::array{ 0.0f, 0.0f, -1.0f} }, - { LFE, {} }, - { BackCenter, std::array{ 0.0f, 0.0f, 1.0f} }, - { SideLeft, std::array{-1.0f, 0.0f, 0.0f} }, - { SideRight, std::array{ 1.0f, 0.0f, 0.0f} }, - }, X71Map[8]{ - { FrontLeft, std::array{-sin30, 0.0f, -cos30} }, - { FrontRight, std::array{ sin30, 0.0f, -cos30} }, - { FrontCenter, std::array{ 0.0f, 0.0f, -1.0f} }, - { LFE, {} }, - { BackLeft, std::array{-sin30, 0.0f, cos30} }, - { BackRight, std::array{ sin30, 0.0f, cos30} }, - { SideLeft, std::array{ -1.0f, 0.0f, 0.0f} }, - { SideRight, std::array{ 1.0f, 0.0f, 0.0f} }, + static constexpr std::array MonoMap{ + ChanPosMap{FrontCenter, std::array{0.0f, 0.0f, -1.0f}} + }; + static constexpr std::array StereoMap{ + ChanPosMap{FrontLeft, std::array{-sin30, 0.0f, -cos30}}, + ChanPosMap{FrontRight, std::array{ sin30, 0.0f, -cos30}}, + }; + static constexpr std::array RearMap{ + ChanPosMap{BackLeft, std::array{-sin30, 0.0f, cos30}}, + ChanPosMap{BackRight, std::array{ sin30, 0.0f, cos30}}, + }; + static constexpr std::array QuadMap{ + ChanPosMap{FrontLeft, std::array{-sin45, 0.0f, -cos45}}, + ChanPosMap{FrontRight, std::array{ sin45, 0.0f, -cos45}}, + ChanPosMap{BackLeft, std::array{-sin45, 0.0f, cos45}}, + ChanPosMap{BackRight, std::array{ sin45, 0.0f, cos45}}, + }; + static constexpr std::array X51Map{ + ChanPosMap{FrontLeft, std::array{-sin30, 0.0f, -cos30}}, + ChanPosMap{FrontRight, std::array{ sin30, 0.0f, -cos30}}, + ChanPosMap{FrontCenter, std::array{ 0.0f, 0.0f, -1.0f}}, + ChanPosMap{LFE, {}}, + ChanPosMap{SideLeft, std::array{-sin110, 0.0f, -cos110}}, + ChanPosMap{SideRight, std::array{ sin110, 0.0f, -cos110}}, + }; + static constexpr std::array X61Map{ + ChanPosMap{FrontLeft, std::array{-sin30, 0.0f, -cos30}}, + ChanPosMap{FrontRight, std::array{ sin30, 0.0f, -cos30}}, + ChanPosMap{FrontCenter, std::array{ 0.0f, 0.0f, -1.0f}}, + ChanPosMap{LFE, {}}, + ChanPosMap{BackCenter, std::array{ 0.0f, 0.0f, 1.0f} }, + ChanPosMap{SideLeft, std::array{-1.0f, 0.0f, 0.0f} }, + ChanPosMap{SideRight, std::array{ 1.0f, 0.0f, 0.0f} }, + }; + static constexpr std::array X71Map{ + ChanPosMap{FrontLeft, std::array{-sin30, 0.0f, -cos30}}, + ChanPosMap{FrontRight, std::array{ sin30, 0.0f, -cos30}}, + ChanPosMap{FrontCenter, std::array{ 0.0f, 0.0f, -1.0f}}, + ChanPosMap{LFE, {}}, + ChanPosMap{BackLeft, std::array{-sin30, 0.0f, cos30}}, + ChanPosMap{BackRight, std::array{ sin30, 0.0f, cos30}}, + ChanPosMap{SideLeft, std::array{ -1.0f, 0.0f, 0.0f}}, + ChanPosMap{SideRight, std::array{ 1.0f, 0.0f, 0.0f}}, }; if(mNumConvolveSegs < 1) UNLIKELY return; + auto &props = std::get(*props_); mMix = &ConvolutionState::NormalMix; - for(auto &chan : *mChans) - std::fill(std::begin(chan.Target), std::end(chan.Target), 0.0f); + for(auto &chan : mChans) + std::fill(chan.Target.begin(), chan.Target.end(), 0.0f); const float gain{slot->Gain}; if(IsAmbisonic(mChannels)) { @@ -434,40 +492,58 @@ void ConvolutionState::update(const ContextBase *context, const EffectSlot *slot if(mChannels == FmtUHJ2 && !device->mUhjEncoder) { mMix = &ConvolutionState::UpsampleMix; - (*mChans)[0].mHfScale = 1.0f; - (*mChans)[0].mLfScale = DecoderBase::sWLFScale; - (*mChans)[1].mHfScale = 1.0f; - (*mChans)[1].mLfScale = DecoderBase::sXYLFScale; - (*mChans)[2].mHfScale = 1.0f; - (*mChans)[2].mLfScale = DecoderBase::sXYLFScale; + mChans[0].mHfScale = 1.0f; + mChans[0].mLfScale = DecoderBase::sWLFScale; + mChans[1].mHfScale = 1.0f; + mChans[1].mLfScale = DecoderBase::sXYLFScale; + mChans[2].mHfScale = 1.0f; + mChans[2].mLfScale = DecoderBase::sXYLFScale; } else if(device->mAmbiOrder > mAmbiOrder) { mMix = &ConvolutionState::UpsampleMix; const auto scales = AmbiScale::GetHFOrderScales(mAmbiOrder, device->mAmbiOrder, device->m2DMixing); - (*mChans)[0].mHfScale = scales[0]; - (*mChans)[0].mLfScale = 1.0f; - for(size_t i{1};i < mChans->size();++i) + mChans[0].mHfScale = scales[0]; + mChans[0].mLfScale = 1.0f; + for(size_t i{1};i < mChans.size();++i) { - (*mChans)[i].mHfScale = scales[1]; - (*mChans)[i].mLfScale = 1.0f; + mChans[i].mHfScale = scales[1]; + mChans[i].mLfScale = 1.0f; } } mOutTarget = target.Main->Buffer; + alu::Vector N{props.OrientAt[0], props.OrientAt[1], props.OrientAt[2], 0.0f}; + N.normalize(); + alu::Vector V{props.OrientUp[0], props.OrientUp[1], props.OrientUp[2], 0.0f}; + V.normalize(); + /* Build and normalize right-vector */ + alu::Vector U{N.cross_product(V)}; + U.normalize(); + + const std::array mixmatrix{ + std::array{1.0f, 0.0f, 0.0f, 0.0f}, + std::array{0.0f, U[0], -U[1], U[2]}, + std::array{0.0f, -V[0], V[1], -V[2]}, + std::array{0.0f, -N[0], N[1], -N[2]}, + }; + const auto scales = GetAmbiScales(mAmbiScaling); - const uint8_t *index_map{Is2DAmbisonic(mChannels) ? - GetAmbi2DLayout(mAmbiLayout).data() : - GetAmbiLayout(mAmbiLayout).data()}; + const auto index_map = Is2DAmbisonic(mChannels) ? + al::span{GetAmbi2DLayout(mAmbiLayout)}.subspan(0) : + al::span{GetAmbiLayout(mAmbiLayout)}.subspan(0); std::array coeffs{}; - for(size_t c{0u};c < mChans->size();++c) + for(size_t c{0u};c < mChans.size();++c) { const size_t acn{index_map[c]}; - coeffs[acn] = scales[acn]; - ComputePanGains(target.Main, coeffs.data(), gain, (*mChans)[c].Target); - coeffs[acn] = 0.0f; + const float scale{scales[acn]}; + + std::transform(mixmatrix[acn].cbegin(), mixmatrix[acn].cend(), coeffs.begin(), + [scale](const float in) noexcept -> float { return in * scale; }); + + ComputePanGains(target.Main, coeffs, gain, mChans[c].Target); } } else @@ -477,6 +553,7 @@ void ConvolutionState::update(const ContextBase *context, const EffectSlot *slot switch(mChannels) { case FmtMono: chanmap = MonoMap; break; + case FmtMonoDup: chanmap = MonoMap; break; case FmtSuperStereo: case FmtStereo: chanmap = StereoMap; break; case FmtRear: chanmap = RearMap; break; @@ -496,7 +573,7 @@ void ConvolutionState::update(const ContextBase *context, const EffectSlot *slot if(device->mRenderMode == RenderMode::Pairwise) { /* Scales the azimuth of the given vector by 3 if it's in front. - * Effectively scales +/-45 degrees to +/-90 degrees, leaving > +90 + * Effectively scales +/-30 degrees to +/-90 degrees, leaving > +90 * and < -90 alone. */ auto ScaleAzimuthFront = [](std::array pos) -> std::array @@ -511,20 +588,20 @@ void ConvolutionState::update(const ContextBase *context, const EffectSlot *slot float x{pos[0] / len2d}; float z{-pos[2] / len2d}; - /* Z > cos(pi/4) = -45 < azimuth < 45 degrees. */ - if(z > cos45) + /* Z > cos(pi/6) = -30 < azimuth < 30 degrees. */ + if(z > cos30) { - /* Double the angle represented by x,z. */ - const float resx{2.0f*x * z}; - const float resz{z*z - x*x}; + /* Triple the angle represented by x,z. */ + x = x*3.0f - x*x*x*4.0f; + z = z*z*z*4.0f - z*3.0f; /* Scale the vector back to fit in 3D. */ - pos[0] = resx * len2d; - pos[2] = -resz * len2d; + pos[0] = x * len2d; + pos[2] = -z * len2d; } else { - /* If azimuth >= 45 degrees, clamp to 90 degrees. */ + /* If azimuth >= 30 degrees, clamp to 90 degrees. */ pos[0] = std::copysign(len2d, pos[0]); pos[2] = 0.0f; } @@ -536,14 +613,14 @@ void ConvolutionState::update(const ContextBase *context, const EffectSlot *slot { if(chanmap[i].channel == LFE) continue; const auto coeffs = CalcDirectionCoeffs(ScaleAzimuthFront(chanmap[i].pos), 0.0f); - ComputePanGains(target.Main, coeffs.data(), gain, (*mChans)[i].Target); + ComputePanGains(target.Main, coeffs, gain, mChans[i].Target); } } else for(size_t i{0};i < chanmap.size();++i) { if(chanmap[i].channel == LFE) continue; const auto coeffs = CalcDirectionCoeffs(chanmap[i].pos, 0.0f); - ComputePanGains(target.Main, coeffs.data(), gain, (*mChans)[i].Target); + ComputePanGains(target.Main, coeffs, gain, mChans[i].Target); } } } @@ -554,27 +631,26 @@ void ConvolutionState::process(const size_t samplesToDo, if(mNumConvolveSegs < 1) UNLIKELY return; - constexpr size_t m{ConvolveUpdateSize/2 + 1}; size_t curseg{mCurrentSegment}; - auto &chans = *mChans; for(size_t base{0u};base < samplesToDo;) { - const size_t todo{minz(ConvolveUpdateSamples-mFifoPos, samplesToDo-base)}; + const size_t todo{std::min(ConvolveUpdateSamples-mFifoPos, samplesToDo-base)}; - std::copy_n(samplesIn[0].begin() + base, todo, - mInput.begin()+ConvolveUpdateSamples+mFifoPos); + std::copy_n(samplesIn[0].begin() + ptrdiff_t(base), todo, + mInput.begin()+ptrdiff_t(ConvolveUpdateSamples+mFifoPos)); /* Apply the FIR for the newly retrieved input samples, and combine it * with the inverse FFT'd output samples. */ - for(size_t c{0};c < chans.size();++c) + for(size_t c{0};c < mChans.size();++c) { - auto buf_iter = chans[c].mBuffer.begin() + base; - apply_fir({buf_iter, todo}, mInput.data()+1 + mFifoPos, mFilter[c].data()); + auto outspan = al::span{mChans[c].mBuffer}.subspan(base, todo); + apply_fir(outspan, al::span{mInput}.subspan(1+mFifoPos), mFilter[c]); - auto fifo_iter = mOutput[c].begin() + mFifoPos; - std::transform(fifo_iter, fifo_iter+todo, buf_iter, buf_iter, std::plus<>{}); + auto fifospan = al::span{mOutput[c]}.subspan(mFifoPos, todo); + std::transform(fifospan.cbegin(), fifospan.cend(), outspan.cbegin(), outspan.begin(), + std::plus{}); } mFifoPos += todo; @@ -586,59 +662,51 @@ void ConvolutionState::process(const size_t samplesToDo, /* Move the newest input to the front for the next iteration's history. */ std::copy(mInput.cbegin()+ConvolveUpdateSamples, mInput.cend(), mInput.begin()); + std::fill(mInput.begin()+ConvolveUpdateSamples, mInput.end(), 0.0f); - /* Calculate the frequency domain response and add the relevant + /* Calculate the frequency-domain response and add the relevant * frequency bins to the FFT history. */ - auto fftiter = std::copy_n(mInput.cbegin(), ConvolveUpdateSamples, mFftBuffer.begin()); - std::fill(fftiter, mFftBuffer.end(), complex_f{}); - forward_fft(al::span{mFftBuffer}); + mFft.transform(mInput.data(), &mComplexData[curseg*ConvolveUpdateSize], + mFftWorkBuffer.data(), PFFFT_FORWARD); - std::copy_n(mFftBuffer.cbegin(), m, &mComplexData[curseg*m]); - - const complex_f *RESTRICT filter{mComplexData.get() + mNumConvolveSegs*m}; - for(size_t c{0};c < chans.size();++c) + auto filter = mComplexData.cbegin() + ptrdiff_t(mNumConvolveSegs*ConvolveUpdateSize); + for(size_t c{0};c < mChans.size();++c) { - std::fill_n(mFftBuffer.begin(), m, complex_f{}); - /* Convolve each input segment with its IR filter counterpart * (aligned in time). */ - const complex_f *RESTRICT input{&mComplexData[curseg*m]}; + mFftBuffer.fill(0.0f); + auto input = mComplexData.cbegin() + ptrdiff_t(curseg*ConvolveUpdateSize); for(size_t s{curseg};s < mNumConvolveSegs;++s) { - for(size_t i{0};i < m;++i,++input,++filter) - mFftBuffer[i] += *input * *filter; + mFft.zconvolve_accumulate(al::to_address(input), al::to_address(filter), + mFftBuffer.data()); + input += ConvolveUpdateSize; + filter += ConvolveUpdateSize; } - input = mComplexData.get(); + input = mComplexData.cbegin(); for(size_t s{0};s < curseg;++s) { - for(size_t i{0};i < m;++i,++input,++filter) - mFftBuffer[i] += *input * *filter; + mFft.zconvolve_accumulate(al::to_address(input), al::to_address(filter), + mFftBuffer.data()); + input += ConvolveUpdateSize; + filter += ConvolveUpdateSize; } - /* Reconstruct the mirrored/negative frequencies to do a proper - * inverse FFT. - */ - for(size_t i{m};i < ConvolveUpdateSize;++i) - mFftBuffer[i] = std::conj(mFftBuffer[ConvolveUpdateSize-i]); - /* Apply iFFT to get the 256 (really 255) samples for output. The * 128 output samples are combined with the last output's 127 * second-half samples (and this output's second half is * subsequently saved for next time). */ - inverse_fft(al::span{mFftBuffer}); - - /* The iFFT'd response is scaled up by the number of bins, so apply - * the inverse to normalize the output. - */ - for(size_t i{0};i < ConvolveUpdateSamples;++i) - mOutput[c][i] = - (mFftBuffer[i].real()+mOutput[c][ConvolveUpdateSamples+i]) * - (1.0f/float{ConvolveUpdateSize}); - for(size_t i{0};i < ConvolveUpdateSamples;++i) - mOutput[c][ConvolveUpdateSamples+i] = mFftBuffer[ConvolveUpdateSamples+i].real(); + mFft.transform(mFftBuffer.data(), mFftBuffer.data(), mFftWorkBuffer.data(), + PFFFT_BACKWARD); + + /* The filter was attenuated, so the response is already scaled. */ + std::transform(mFftBuffer.cbegin(), mFftBuffer.cbegin()+ConvolveUpdateSamples, + mOutput[c].cbegin()+ConvolveUpdateSamples, mOutput[c].begin(), std::plus{}); + std::copy(mFftBuffer.cbegin()+ConvolveUpdateSamples, mFftBuffer.cend(), + mOutput[c].begin()+ConvolveUpdateSamples); } /* Shift the input history. */ diff --git a/3rdparty/openal/alc/effects/dedicated.cpp b/3rdparty/openal/alc/effects/dedicated.cpp index e82b13d9b968..df1bd5ed0788 100644 --- a/3rdparty/openal/alc/effects/dedicated.cpp +++ b/3rdparty/openal/alc/effects/dedicated.cpp @@ -23,18 +23,19 @@ #include #include #include -#include +#include #include "alc/effects/base.h" -#include "almalloc.h" #include "alspan.h" #include "core/bufferline.h" #include "core/devformat.h" #include "core/device.h" +#include "core/effects/base.h" #include "core/effectslot.h" #include "core/mixer.h" #include "intrusive_ptr.h" +struct BufferStorage; struct ContextBase; @@ -47,45 +48,36 @@ struct DedicatedState final : public EffectState { * gains for all possible output channels and not just the main ambisonic * buffer. */ - float mCurrentGains[MAX_OUTPUT_CHANNELS]; - float mTargetGains[MAX_OUTPUT_CHANNELS]; + std::array mCurrentGains{}; + std::array mTargetGains{}; - void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override; - void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props, - const EffectTarget target) override; + void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) final; + void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props_, + const EffectTarget target) final; void process(const size_t samplesToDo, const al::span samplesIn, - const al::span samplesOut) override; - - DEF_NEWDEL(DedicatedState) + const al::span samplesOut) final; }; void DedicatedState::deviceUpdate(const DeviceBase*, const BufferStorage*) { - std::fill(std::begin(mCurrentGains), std::end(mCurrentGains), 0.0f); + std::fill(mCurrentGains.begin(), mCurrentGains.end(), 0.0f); } void DedicatedState::update(const ContextBase*, const EffectSlot *slot, - const EffectProps *props, const EffectTarget target) + const EffectProps *props_, const EffectTarget target) { - std::fill(std::begin(mTargetGains), std::end(mTargetGains), 0.0f); + std::fill(mTargetGains.begin(), mTargetGains.end(), 0.0f); - const float Gain{slot->Gain * props->Dedicated.Gain}; + auto &props = std::get(*props_); + const float Gain{slot->Gain * props.Gain}; - if(slot->EffectType == EffectSlotType::DedicatedLFE) - { - const uint idx{target.RealOut ? target.RealOut->ChannelIndex[LFE] : InvalidChannelIndex}; - if(idx != InvalidChannelIndex) - { - mOutTarget = target.RealOut->Buffer; - mTargetGains[idx] = Gain; - } - } - else if(slot->EffectType == EffectSlotType::DedicatedDialog) + if(props.Target == DedicatedProps::Dialog) { /* Dialog goes to the front-center speaker if it exists, otherwise it - * plays from the front-center location. */ - const uint idx{target.RealOut ? target.RealOut->ChannelIndex[FrontCenter] + * plays from the front-center location. + */ + const size_t idx{target.RealOut ? target.RealOut->ChannelIndex[FrontCenter] : InvalidChannelIndex}; if(idx != InvalidChannelIndex) { @@ -97,14 +89,23 @@ void DedicatedState::update(const ContextBase*, const EffectSlot *slot, static constexpr auto coeffs = CalcDirectionCoeffs(std::array{0.0f, 0.0f, -1.0f}); mOutTarget = target.Main->Buffer; - ComputePanGains(target.Main, coeffs.data(), Gain, mTargetGains); + ComputePanGains(target.Main, coeffs, Gain, mTargetGains); + } + } + else if(props.Target == DedicatedProps::Lfe) + { + const size_t idx{target.RealOut ? target.RealOut->ChannelIndex[LFE] : InvalidChannelIndex}; + if(idx != InvalidChannelIndex) + { + mOutTarget = target.RealOut->Buffer; + mTargetGains[idx] = Gain; } } } void DedicatedState::process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) { - MixSamples({samplesIn[0].data(), samplesToDo}, samplesOut, mCurrentGains, mTargetGains, + MixSamples(al::span{samplesIn[0]}.first(samplesToDo), samplesOut, mCurrentGains, mTargetGains, samplesToDo, 0); } diff --git a/3rdparty/openal/alc/effects/distortion.cpp b/3rdparty/openal/alc/effects/distortion.cpp index 392d81c8101a..963ddad11195 100644 --- a/3rdparty/openal/alc/effects/distortion.cpp +++ b/3rdparty/openal/alc/effects/distortion.cpp @@ -22,30 +22,32 @@ #include #include +#include #include -#include +#include #include "alc/effects/base.h" -#include "almalloc.h" #include "alnumbers.h" #include "alnumeric.h" #include "alspan.h" +#include "core/ambidefs.h" #include "core/bufferline.h" #include "core/context.h" -#include "core/devformat.h" #include "core/device.h" +#include "core/effects/base.h" #include "core/effectslot.h" #include "core/filters/biquad.h" #include "core/mixer.h" #include "core/mixer/defs.h" #include "intrusive_ptr.h" +struct BufferStorage; namespace { struct DistortionState final : public EffectState { /* Effect gains for each channel */ - float mGain[MaxAmbiChannels]{}; + std::array mGain{}; /* Effect parameters */ BiquadFilter mLowpass; @@ -53,7 +55,7 @@ struct DistortionState final : public EffectState { float mAttenuation{}; float mEdgeCoeff{}; - alignas(16) float mBuffer[2][BufferLineSize]{}; + alignas(16) std::array mBuffer{}; void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override; @@ -61,8 +63,6 @@ struct DistortionState final : public EffectState { const EffectTarget target) override; void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; - - DEF_NEWDEL(DistortionState) }; void DistortionState::deviceUpdate(const DeviceBase*, const BufferStorage*) @@ -72,33 +72,33 @@ void DistortionState::deviceUpdate(const DeviceBase*, const BufferStorage*) } void DistortionState::update(const ContextBase *context, const EffectSlot *slot, - const EffectProps *props, const EffectTarget target) + const EffectProps *props_, const EffectTarget target) { + auto &props = std::get(*props_); const DeviceBase *device{context->mDevice}; /* Store waveshaper edge settings. */ - const float edge{minf(std::sin(al::numbers::pi_v*0.5f * props->Distortion.Edge), - 0.99f)}; + const float edge{std::min(std::sin(al::numbers::pi_v*0.5f * props.Edge), 0.99f)}; mEdgeCoeff = 2.0f * edge / (1.0f-edge); - float cutoff{props->Distortion.LowpassCutoff}; + float cutoff{props.LowpassCutoff}; /* Bandwidth value is constant in octaves. */ float bandwidth{(cutoff / 2.0f) / (cutoff * 0.67f)}; /* Divide normalized frequency by the amount of oversampling done during * processing. */ - auto frequency = static_cast(device->Frequency); + auto frequency = static_cast(device->mSampleRate); mLowpass.setParamsFromBandwidth(BiquadType::LowPass, cutoff/frequency/4.0f, 1.0f, bandwidth); - cutoff = props->Distortion.EQCenter; + cutoff = props.EQCenter; /* Convert bandwidth in Hz to octaves. */ - bandwidth = props->Distortion.EQBandwidth / (cutoff * 0.67f); + bandwidth = props.EQBandwidth / (cutoff * 0.67f); mBandpass.setParamsFromBandwidth(BiquadType::BandPass, cutoff/frequency/4.0f, 1.0f, bandwidth); static constexpr auto coeffs = CalcDirectionCoeffs(std::array{0.0f, 0.0f, -1.0f}); mOutTarget = target.Main->Buffer; - ComputePanGains(target.Main, coeffs.data(), slot->Gain*props->Distortion.Gain, mGain); + ComputePanGains(target.Main, coeffs, slot->Gain*props.Gain, mGain); } void DistortionState::process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) @@ -111,7 +111,7 @@ void DistortionState::process(const size_t samplesToDo, const al::span float { - smp = (1.0f + fc) * smp/(1.0f + fc*std::abs(smp)); - smp = (1.0f + fc) * smp/(1.0f + fc*std::abs(smp)) * -1.0f; - smp = (1.0f + fc) * smp/(1.0f + fc*std::abs(smp)); + smp = (1.0f + fc) * smp/(1.0f + fc*std::fabs(smp)); + smp = (1.0f + fc) * smp/(1.0f + fc*std::fabs(smp)) * -1.0f; + smp = (1.0f + fc) * smp/(1.0f + fc*std::fabs(smp)); return smp; }; - std::transform(std::begin(mBuffer[1]), std::begin(mBuffer[1])+todo, std::begin(mBuffer[0]), + std::transform(mBuffer[1].begin(), mBuffer[1].begin()+todo, mBuffer[0].begin(), proc_sample); /* Third step, do bandpass filtering of distorted signal. */ - mBandpass.process({mBuffer[0], todo}, mBuffer[1]); + mBandpass.process(al::span{mBuffer[0]}.first(todo), mBuffer[1]); todo >>= 2; - const float *outgains{mGain}; - for(FloatBufferLine &output : samplesOut) + auto outgains = mGain.cbegin(); + auto proc_bufline = [this,base,todo,&outgains](FloatBufferSpan output) { /* Fourth step, final, do attenuation and perform decimation, * storing only one sample out of four. */ const float gain{*(outgains++)}; if(!(std::fabs(gain) > GainSilenceThreshold)) - continue; - - for(size_t i{0u};i < todo;i++) - output[base+i] += gain * mBuffer[1][i*4]; - } + return; + + auto src = mBuffer[1].cbegin(); + const auto dst = al::span{output}.subspan(base, todo); + auto dec_sample = [gain,&src](float sample) noexcept -> float + { + sample += *src * gain; + src += 4; + return sample; + }; + std::transform(dst.begin(), dst.end(), dst.begin(), dec_sample); + }; + std::for_each(samplesOut.begin(), samplesOut.end(), proc_bufline); base += todo; } diff --git a/3rdparty/openal/alc/effects/echo.cpp b/3rdparty/openal/alc/effects/echo.cpp index 7824c24694c4..6809ddcbbda7 100644 --- a/3rdparty/openal/alc/effects/echo.cpp +++ b/3rdparty/openal/alc/effects/echo.cpp @@ -22,25 +22,26 @@ #include #include +#include #include -#include -#include +#include #include #include "alc/effects/base.h" -#include "almalloc.h" #include "alnumeric.h" #include "alspan.h" +#include "core/ambidefs.h" #include "core/bufferline.h" #include "core/context.h" -#include "core/devformat.h" #include "core/device.h" +#include "core/effects/base.h" #include "core/effectslot.h" #include "core/filters/biquad.h" #include "core/mixer.h" #include "intrusive_ptr.h" #include "opthelpers.h" +struct BufferStorage; namespace { @@ -53,34 +54,31 @@ struct EchoState final : public EffectState { // The echo is two tap. The delay is the number of samples from before the // current offset - struct { - size_t delay{0u}; - } mTap[2]; + std::array mDelayTap{}; size_t mOffset{0u}; /* The panning gains for the two taps */ - struct { - float Current[MaxAmbiChannels]{}; - float Target[MaxAmbiChannels]{}; - } mGains[2]; + struct OutGains { + std::array Current{}; + std::array Target{}; + }; + std::array mGains; BiquadFilter mFilter; float mFeedGain{0.0f}; - alignas(16) float mTempBuffer[2][BufferLineSize]; + alignas(16) std::array mTempBuffer{}; void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override; void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props, const EffectTarget target) override; void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; - - DEF_NEWDEL(EchoState) }; void EchoState::deviceUpdate(const DeviceBase *Device, const BufferStorage*) { - const auto frequency = static_cast(Device->Frequency); + const auto frequency = static_cast(Device->mSampleRate); // Use the next power of 2 for the buffer length, so the tap offsets can be // wrapped using a mask instead of a modulo @@ -92,56 +90,57 @@ void EchoState::deviceUpdate(const DeviceBase *Device, const BufferStorage*) std::fill(mSampleBuffer.begin(), mSampleBuffer.end(), 0.0f); for(auto &e : mGains) { - std::fill(std::begin(e.Current), std::end(e.Current), 0.0f); - std::fill(std::begin(e.Target), std::end(e.Target), 0.0f); + std::fill(e.Current.begin(), e.Current.end(), 0.0f); + std::fill(e.Target.begin(), e.Target.end(), 0.0f); } } void EchoState::update(const ContextBase *context, const EffectSlot *slot, - const EffectProps *props, const EffectTarget target) + const EffectProps *props_, const EffectTarget target) { + auto &props = std::get(*props_); const DeviceBase *device{context->mDevice}; - const auto frequency = static_cast(device->Frequency); + const auto frequency = static_cast(device->mSampleRate); - mTap[0].delay = maxu(float2uint(props->Echo.Delay*frequency + 0.5f), 1); - mTap[1].delay = float2uint(props->Echo.LRDelay*frequency + 0.5f) + mTap[0].delay; + mDelayTap[0] = std::max(float2uint(std::round(props.Delay*frequency)), 1u); + mDelayTap[1] = float2uint(std::round(props.LRDelay*frequency)) + mDelayTap[0]; - const float gainhf{maxf(1.0f - props->Echo.Damping, 0.0625f)}; /* Limit -24dB */ + const float gainhf{std::max(1.0f - props.Damping, 0.0625f)}; /* Limit -24dB */ mFilter.setParamsFromSlope(BiquadType::HighShelf, LowpassFreqRef/frequency, gainhf, 1.0f); - mFeedGain = props->Echo.Feedback; + mFeedGain = props.Feedback; - /* Convert echo spread (where 0 = center, +/-1 = sides) to angle. */ - const float angle{std::asin(props->Echo.Spread)}; + /* Convert echo spread (where 0 = center, +/-1 = sides) to a 2D vector. */ + const float x{props.Spread}; /* +x = left */ + const float z{std::sqrt(1.0f - x*x)}; - const auto coeffs0 = CalcAngleCoeffs(-angle, 0.0f, 0.0f); - const auto coeffs1 = CalcAngleCoeffs( angle, 0.0f, 0.0f); + const auto coeffs0 = CalcAmbiCoeffs( x, 0.0f, z, 0.0f); + const auto coeffs1 = CalcAmbiCoeffs(-x, 0.0f, z, 0.0f); mOutTarget = target.Main->Buffer; - ComputePanGains(target.Main, coeffs0.data(), slot->Gain, mGains[0].Target); - ComputePanGains(target.Main, coeffs1.data(), slot->Gain, mGains[1].Target); + ComputePanGains(target.Main, coeffs0, slot->Gain, mGains[0].Target); + ComputePanGains(target.Main, coeffs1, slot->Gain, mGains[1].Target); } void EchoState::process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) { - const size_t mask{mSampleBuffer.size()-1}; - float *RESTRICT delaybuf{mSampleBuffer.data()}; + const auto delaybuf = al::span{mSampleBuffer}; + const size_t mask{delaybuf.size()-1}; size_t offset{mOffset}; - size_t tap1{offset - mTap[0].delay}; - size_t tap2{offset - mTap[1].delay}; - float z1, z2; + size_t tap1{offset - mDelayTap[0]}; + size_t tap2{offset - mDelayTap[1]}; ASSUME(samplesToDo > 0); const BiquadFilter filter{mFilter}; - std::tie(z1, z2) = mFilter.getComponents(); + auto [z1, z2] = mFilter.getComponents(); for(size_t i{0u};i < samplesToDo;) { offset &= mask; tap1 &= mask; tap2 &= mask; - size_t td{minz(mask+1 - maxz(offset, maxz(tap1, tap2)), samplesToDo-i)}; + size_t td{std::min(mask+1 - std::max(offset, std::max(tap1, tap2)), samplesToDo-i)}; do { /* Feed the delay buffer's input first. */ delaybuf[offset] = samplesIn[0][i]; @@ -161,8 +160,8 @@ void EchoState::process(const size_t samplesToDo, const al::span #include +#include #include #include -#include -#include +#include #include "alc/effects/base.h" -#include "almalloc.h" #include "alspan.h" #include "core/ambidefs.h" #include "core/bufferline.h" #include "core/context.h" -#include "core/devformat.h" #include "core/device.h" +#include "core/effects/base.h" #include "core/effectslot.h" #include "core/filters/biquad.h" #include "core/mixer.h" #include "intrusive_ptr.h" +struct BufferStorage; namespace { @@ -86,16 +86,17 @@ namespace { struct EqualizerState final : public EffectState { - struct { + struct OutParams { uint mTargetChannel{InvalidChannelIndex}; /* Effect parameters */ - BiquadFilter mFilter[4]; + std::array mFilter; /* Effect gains for each channel */ float mCurrentGain{}; float mTargetGain{}; - } mChans[MaxAmbiChannels]; + }; + std::array mChans; alignas(16) FloatBufferLine mSampleBuffer{}; @@ -105,8 +106,6 @@ struct EqualizerState final : public EffectState { const EffectTarget target) override; void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; - - DEF_NEWDEL(EqualizerState) }; void EqualizerState::deviceUpdate(const DeviceBase*, const BufferStorage*) @@ -114,18 +113,17 @@ void EqualizerState::deviceUpdate(const DeviceBase*, const BufferStorage*) for(auto &e : mChans) { e.mTargetChannel = InvalidChannelIndex; - std::for_each(std::begin(e.mFilter), std::end(e.mFilter), - std::mem_fn(&BiquadFilter::clear)); + std::for_each(e.mFilter.begin(), e.mFilter.end(), std::mem_fn(&BiquadFilter::clear)); e.mCurrentGain = 0.0f; } } void EqualizerState::update(const ContextBase *context, const EffectSlot *slot, - const EffectProps *props, const EffectTarget target) + const EffectProps *props_, const EffectTarget target) { + auto &props = std::get(*props_); const DeviceBase *device{context->mDevice}; - auto frequency = static_cast(device->Frequency); - float gain, f0norm; + auto frequency = static_cast(device->mSampleRate); /* Calculate coefficients for the each type of filter. Note that the shelf * and peaking filters' gain is for the centerpoint of the transition band, @@ -133,22 +131,22 @@ void EqualizerState::update(const ContextBase *context, const EffectSlot *slot, * property gains need their dB halved (sqrt of linear gain) for the * shelf/peak to reach the provided gain. */ - gain = std::sqrt(props->Equalizer.LowGain); - f0norm = props->Equalizer.LowCutoff / frequency; + float gain{std::sqrt(props.LowGain)}; + float f0norm{props.LowCutoff / frequency}; mChans[0].mFilter[0].setParamsFromSlope(BiquadType::LowShelf, f0norm, gain, 0.75f); - gain = std::sqrt(props->Equalizer.Mid1Gain); - f0norm = props->Equalizer.Mid1Center / frequency; + gain = std::sqrt(props.Mid1Gain); + f0norm = props.Mid1Center / frequency; mChans[0].mFilter[1].setParamsFromBandwidth(BiquadType::Peaking, f0norm, gain, - props->Equalizer.Mid1Width); + props.Mid1Width); - gain = std::sqrt(props->Equalizer.Mid2Gain); - f0norm = props->Equalizer.Mid2Center / frequency; + gain = std::sqrt(props.Mid2Gain); + f0norm = props.Mid2Center / frequency; mChans[0].mFilter[2].setParamsFromBandwidth(BiquadType::Peaking, f0norm, gain, - props->Equalizer.Mid2Width); + props.Mid2Width); - gain = std::sqrt(props->Equalizer.HighGain); - f0norm = props->Equalizer.HighCutoff / frequency; + gain = std::sqrt(props.HighGain); + f0norm = props.HighCutoff / frequency; mChans[0].mFilter[3].setParamsFromSlope(BiquadType::HighShelf, f0norm, gain, 0.75f); /* Copy the filter coefficients for the other input channels. */ @@ -171,18 +169,17 @@ void EqualizerState::update(const ContextBase *context, const EffectSlot *slot, void EqualizerState::process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) { - const al::span buffer{mSampleBuffer.data(), samplesToDo}; - auto chan = std::begin(mChans); + const auto buffer = al::span{mSampleBuffer}.first(samplesToDo); + auto chan = mChans.begin(); for(const auto &input : samplesIn) { - const size_t outidx{chan->mTargetChannel}; - if(outidx != InvalidChannelIndex) + if(const size_t outidx{chan->mTargetChannel}; outidx != InvalidChannelIndex) { - const al::span inbuf{input.data(), samplesToDo}; - DualBiquad{chan->mFilter[0], chan->mFilter[1]}.process(inbuf, buffer.begin()); - DualBiquad{chan->mFilter[2], chan->mFilter[3]}.process(buffer, buffer.begin()); + const auto inbuf = al::span{input}.first(samplesToDo); + DualBiquad{chan->mFilter[0], chan->mFilter[1]}.process(inbuf, buffer); + DualBiquad{chan->mFilter[2], chan->mFilter[3]}.process(buffer, buffer); - MixSamples(buffer, samplesOut[outidx].data(), chan->mCurrentGain, chan->mTargetGain, + MixSamples(buffer, samplesOut[outidx], chan->mCurrentGain, chan->mTargetGain, samplesToDo); } ++chan; diff --git a/3rdparty/openal/alc/effects/fshifter.cpp b/3rdparty/openal/alc/effects/fshifter.cpp index ec0cc29fc339..ee04fd71f940 100644 --- a/3rdparty/openal/alc/effects/fshifter.cpp +++ b/3rdparty/openal/alc/effects/fshifter.cpp @@ -25,23 +25,25 @@ #include #include #include -#include +#include #include "alc/effects/base.h" #include "alcomplex.h" -#include "almalloc.h" #include "alnumbers.h" #include "alnumeric.h" #include "alspan.h" +#include "core/ambidefs.h" #include "core/bufferline.h" #include "core/context.h" -#include "core/devformat.h" #include "core/device.h" +#include "core/effects/base.h" #include "core/effectslot.h" #include "core/mixer.h" #include "core/mixer/defs.h" #include "intrusive_ptr.h" +#include "opthelpers.h" +struct BufferStorage; namespace { @@ -57,7 +59,7 @@ constexpr size_t HilStep{HilSize / OversampleFactor}; /* Define a Hann window, used to filter the HIL input and output. */ struct Windower { - alignas(16) std::array mData; + alignas(16) std::array mData{}; Windower() { @@ -91,10 +93,11 @@ struct FshifterState final : public EffectState { alignas(16) FloatBufferLine mBufferOut{}; /* Effect gains for each output channel */ - struct { - float Current[MaxAmbiChannels]{}; - float Target[MaxAmbiChannels]{}; - } mGains[2]; + struct OutGains { + std::array Current{}; + std::array Target{}; + }; + std::array mGains; void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override; @@ -102,8 +105,6 @@ struct FshifterState final : public EffectState { const EffectTarget target) override; void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; - - DEF_NEWDEL(FshifterState) }; void FshifterState::deviceUpdate(const DeviceBase*, const BufferStorage*) @@ -122,20 +123,21 @@ void FshifterState::deviceUpdate(const DeviceBase*, const BufferStorage*) for(auto &gain : mGains) { - std::fill(std::begin(gain.Current), std::end(gain.Current), 0.0f); - std::fill(std::begin(gain.Target), std::end(gain.Target), 0.0f); + gain.Current.fill(0.0f); + gain.Target.fill(0.0f); } } void FshifterState::update(const ContextBase *context, const EffectSlot *slot, - const EffectProps *props, const EffectTarget target) + const EffectProps *props_, const EffectTarget target) { + auto &props = std::get(*props_); const DeviceBase *device{context->mDevice}; - const float step{props->Fshifter.Frequency / static_cast(device->Frequency)}; - mPhaseStep[0] = mPhaseStep[1] = fastf2u(minf(step, 1.0f) * MixerFracOne); + const float step{props.Frequency / static_cast(device->mSampleRate)}; + mPhaseStep[0] = mPhaseStep[1] = fastf2u(std::min(step, 1.0f) * MixerFracOne); - switch(props->Fshifter.LeftDirection) + switch(props.LeftDirection) { case FShifterDirection::Down: mSign[0] = -1.0; @@ -149,7 +151,7 @@ void FshifterState::update(const ContextBase *context, const EffectSlot *slot, break; } - switch(props->Fshifter.RightDirection) + switch(props.RightDirection) { case FShifterDirection::Down: mSign[1] = -1.0; @@ -172,15 +174,15 @@ void FshifterState::update(const ContextBase *context, const EffectSlot *slot, auto &rcoeffs = (device->mRenderMode != RenderMode::Pairwise) ? rcoeffs_nrml : rcoeffs_pw; mOutTarget = target.Main->Buffer; - ComputePanGains(target.Main, lcoeffs.data(), slot->Gain, mGains[0].Target); - ComputePanGains(target.Main, rcoeffs.data(), slot->Gain, mGains[1].Target); + ComputePanGains(target.Main, lcoeffs, slot->Gain, mGains[0].Target); + ComputePanGains(target.Main, rcoeffs, slot->Gain, mGains[1].Target); } void FshifterState::process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) { for(size_t base{0u};base < samplesToDo;) { - size_t todo{minz(HilStep-mCount, samplesToDo-base)}; + size_t todo{std::min(HilStep-mCount, samplesToDo-base)}; /* Fill FIFO buffer with samples data */ const size_t pos{mPos}; @@ -218,25 +220,27 @@ void FshifterState::process(const size_t samplesToDo, const al::span(mBufferOut.data())}; for(size_t c{0};c < 2;++c) { + const double sign{mSign[c]}; const uint phase_step{mPhaseStep[c]}; uint phase_idx{mPhase[c]}; - for(size_t k{0};k < samplesToDo;++k) - { - const double phase{phase_idx * (al::numbers::pi*2.0 / MixerFracOne)}; - BufferOut[k] = static_cast(mOutdata[k].real()*std::cos(phase) + - mOutdata[k].imag()*std::sin(phase)*mSign[c]); - - phase_idx += phase_step; - phase_idx &= MixerFracMask; - } + std::transform(mOutdata.cbegin(), mOutdata.cbegin()+samplesToDo, mBufferOut.begin(), + [&phase_idx,phase_step,sign](const complex_d &in) -> float + { + const double phase{phase_idx * (al::numbers::pi*2.0 / MixerFracOne)}; + const auto out = static_cast(in.real()*std::cos(phase) + + in.imag()*std::sin(phase)*sign); + + phase_idx += phase_step; + phase_idx &= MixerFracMask; + return out; + }); mPhase[c] = phase_idx; /* Now, mix the processed sound data to the output. */ - MixSamples({BufferOut, samplesToDo}, samplesOut, mGains[c].Current, mGains[c].Target, - maxz(samplesToDo, 512), 0); + MixSamples(al::span{mBufferOut}.first(samplesToDo), samplesOut, mGains[c].Current, + mGains[c].Target, std::max(samplesToDo, 512_uz), 0); } } diff --git a/3rdparty/openal/alc/effects/modulator.cpp b/3rdparty/openal/alc/effects/modulator.cpp index f99ba19c03f2..983a945dd712 100644 --- a/3rdparty/openal/alc/effects/modulator.cpp +++ b/3rdparty/openal/alc/effects/modulator.cpp @@ -22,65 +22,56 @@ #include #include +#include +#include #include -#include +#include +#include #include "alc/effects/base.h" -#include "almalloc.h" #include "alnumbers.h" #include "alnumeric.h" #include "alspan.h" #include "core/ambidefs.h" #include "core/bufferline.h" #include "core/context.h" -#include "core/devformat.h" #include "core/device.h" +#include "core/effects/base.h" #include "core/effectslot.h" #include "core/filters/biquad.h" #include "core/mixer.h" #include "intrusive_ptr.h" +#include "opthelpers.h" +struct BufferStorage; namespace { using uint = unsigned int; -inline float Sin(uint index, float scale) -{ return std::sin(static_cast(index) * scale); } - -inline float Saw(uint index, float scale) -{ return static_cast(index)*scale - 1.0f; } - -inline float Square(uint index, float scale) -{ return (static_cast(index)*scale < 0.5f)*2.0f - 1.0f; } +struct SinFunc { + static auto Get(uint index, float scale) noexcept(noexcept(std::sin(0.0f))) -> float + { return std::sin(static_cast(index) * scale); } +}; -inline float One(uint, float) -{ return 1.0f; } +struct SawFunc { + static constexpr auto Get(uint index, float scale) noexcept -> float + { return static_cast(index)*scale - 1.0f; } +}; -struct ModulatorState final : public EffectState { - template - void Modulate(size_t todo) - { - const uint range{mRange}; - const float scale{mIndexScale}; - uint index{mIndex}; +struct SquareFunc { + static constexpr auto Get(uint index, float scale) noexcept -> float + { return float(static_cast(index)*scale < 0.5f)*2.0f - 1.0f; } +}; - ASSUME(range > 1); - ASSUME(todo > 0); +struct OneFunc { + static constexpr auto Get(uint, float) noexcept -> float + { return 1.0f; } +}; - for(size_t i{0};i < todo;) - { - size_t rem{minz(todo-i, range-index)}; - do { - mModSamples[i++] = func(index++, scale); - } while(--rem); - if(index == range) - index = 0; - } - mIndex = index; - } - void (ModulatorState::*mGenModSamples)(size_t){}; +struct ModulatorState final : public EffectState { + std::variant mSampleGen; uint mIndex{0}; uint mRange{1}; @@ -89,14 +80,15 @@ struct ModulatorState final : public EffectState { alignas(16) FloatBufferLine mModSamples{}; alignas(16) FloatBufferLine mBuffer{}; - struct { + struct OutParams { uint mTargetChannel{InvalidChannelIndex}; BiquadFilter mFilter; float mCurrentGain{}; float mTargetGain{}; - } mChans[MaxAmbiChannels]; + }; + std::array mChans; void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override; @@ -104,17 +96,8 @@ struct ModulatorState final : public EffectState { const EffectTarget target) override; void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; - - DEF_NEWDEL(ModulatorState) }; -template<> -void ModulatorState::Modulate(size_t todo) -{ - std::fill_n(mModSamples.begin(), todo, 1.0f); - mIndex = 0; -} - void ModulatorState::deviceUpdate(const DeviceBase*, const BufferStorage*) { for(auto &e : mChans) @@ -126,8 +109,9 @@ void ModulatorState::deviceUpdate(const DeviceBase*, const BufferStorage*) } void ModulatorState::update(const ContextBase *context, const EffectSlot *slot, - const EffectProps *props, const EffectTarget target) + const EffectProps *props_, const EffectTarget target) { + auto &props = std::get(*props_); const DeviceBase *device{context->mDevice}; /* The effective frequency will be adjusted to have a whole number of @@ -137,30 +121,30 @@ void ModulatorState::update(const ContextBase *context, const EffectSlot *slot, * but that may need a more efficient sin function since it needs to do * many iterations per sample. */ - const float samplesPerCycle{props->Modulator.Frequency > 0.0f - ? static_cast(device->Frequency)/props->Modulator.Frequency + 0.5f + const float samplesPerCycle{props.Frequency > 0.0f + ? static_cast(device->mSampleRate)/props.Frequency + 0.5f : 1.0f}; - const uint range{static_cast(clampf(samplesPerCycle, 1.0f, - static_cast(device->Frequency)))}; + const uint range{static_cast(std::clamp(samplesPerCycle, 1.0f, + static_cast(device->mSampleRate)))}; mIndex = static_cast(uint64_t{mIndex} * range / mRange); mRange = range; if(mRange == 1) { mIndexScale = 0.0f; - mGenModSamples = &ModulatorState::Modulate; + mSampleGen.emplace(); } - else if(props->Modulator.Waveform == ModulatorWaveform::Sinusoid) + else if(props.Waveform == ModulatorWaveform::Sinusoid) { mIndexScale = al::numbers::pi_v*2.0f / static_cast(mRange); - mGenModSamples = &ModulatorState::Modulate; + mSampleGen.emplace(); } - else if(props->Modulator.Waveform == ModulatorWaveform::Sawtooth) + else if(props.Waveform == ModulatorWaveform::Sawtooth) { mIndexScale = 2.0f / static_cast(mRange-1); - mGenModSamples = &ModulatorState::Modulate; + mSampleGen.emplace(); } - else /*if(props->Modulator.Waveform == ModulatorWaveform::Square)*/ + else if(props.Waveform == ModulatorWaveform::Square) { /* For square wave, the range should be even (there should be an equal * number of high and low samples). An odd number of samples per cycle @@ -168,11 +152,11 @@ void ModulatorState::update(const ContextBase *context, const EffectSlot *slot, */ mRange = (mRange+1) & ~1u; mIndexScale = 1.0f / static_cast(mRange-1); - mGenModSamples = &ModulatorState::Modulate; + mSampleGen.emplace(); } - float f0norm{props->Modulator.HighPassCutoff / static_cast(device->Frequency)}; - f0norm = clampf(f0norm, 1.0f/512.0f, 0.49f); + float f0norm{props.HighPassCutoff / static_cast(device->mSampleRate)}; + f0norm = std::clamp(f0norm, 1.0f/512.0f, 0.49f); /* Bandwidth value is constant in octaves. */ mChans[0].mFilter.setParamsFromBandwidth(BiquadType::HighPass, f0norm, 1.0f, 0.75f); for(size_t i{1u};i < slot->Wet.Buffer.size();++i) @@ -189,20 +173,39 @@ void ModulatorState::update(const ContextBase *context, const EffectSlot *slot, void ModulatorState::process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) { - (this->*mGenModSamples)(samplesToDo); + ASSUME(samplesToDo > 0); + + std::visit([this,samplesToDo](auto&& type) + { + const uint range{mRange}; + const float scale{mIndexScale}; + uint index{mIndex}; + + ASSUME(range > 1); + + for(size_t i{0};i < samplesToDo;) + { + size_t rem{std::min(samplesToDo-i, size_t{range-index})}; + do { + mModSamples[i++] = type.Get(index++, scale); + } while(--rem); + if(index == range) + index = 0; + } + mIndex = index; + }, mSampleGen); - auto chandata = std::begin(mChans); + auto chandata = mChans.begin(); for(const auto &input : samplesIn) { - const size_t outidx{chandata->mTargetChannel}; - if(outidx != InvalidChannelIndex) + if(const size_t outidx{chandata->mTargetChannel}; outidx != InvalidChannelIndex) { - chandata->mFilter.process({input.data(), samplesToDo}, mBuffer.data()); - for(size_t i{0u};i < samplesToDo;++i) - mBuffer[i] *= mModSamples[i]; + chandata->mFilter.process(al::span{input}.first(samplesToDo), mBuffer); + std::transform(mBuffer.cbegin(), mBuffer.cbegin()+samplesToDo, mModSamples.cbegin(), + mBuffer.begin(), std::multiplies<>{}); - MixSamples({mBuffer.data(), samplesToDo}, samplesOut[outidx].data(), - chandata->mCurrentGain, chandata->mTargetGain, minz(samplesToDo, 64)); + MixSamples(al::span{mBuffer}.first(samplesToDo), samplesOut[outidx], + chandata->mCurrentGain, chandata->mTargetGain, std::min(samplesToDo, 64_uz)); } ++chandata; } diff --git a/3rdparty/openal/alc/effects/null.cpp b/3rdparty/openal/alc/effects/null.cpp index 1f9ae67bc34a..217181a8e710 100644 --- a/3rdparty/openal/alc/effects/null.cpp +++ b/3rdparty/openal/alc/effects/null.cpp @@ -1,14 +1,15 @@ #include "config.h" -#include +#include -#include "almalloc.h" #include "alspan.h" #include "base.h" #include "core/bufferline.h" +#include "core/effects/base.h" #include "intrusive_ptr.h" +struct BufferStorage; struct ContextBase; struct DeviceBase; struct EffectSlot; @@ -25,8 +26,6 @@ struct NullState final : public EffectState { const EffectTarget target) override; void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; - - DEF_NEWDEL(NullState) }; /* This constructs the effect state. It's called when the object is first diff --git a/3rdparty/openal/alc/effects/pshifter.cpp b/3rdparty/openal/alc/effects/pshifter.cpp index 2460cf782751..8b3c6d291cf5 100644 --- a/3rdparty/openal/alc/effects/pshifter.cpp +++ b/3rdparty/openal/alc/effects/pshifter.cpp @@ -25,22 +25,23 @@ #include #include #include -#include +#include #include "alc/effects/base.h" -#include "alcomplex.h" -#include "almalloc.h" #include "alnumbers.h" #include "alnumeric.h" #include "alspan.h" +#include "core/ambidefs.h" #include "core/bufferline.h" -#include "core/devformat.h" #include "core/device.h" +#include "core/effects/base.h" #include "core/effectslot.h" #include "core/mixer.h" #include "core/mixer/defs.h" #include "intrusive_ptr.h" +#include "pffft.h" +struct BufferStorage; struct ContextBase; @@ -58,7 +59,7 @@ constexpr size_t StftStep{StftSize / OversampleFactor}; /* Define a Hann window, used to filter the STFT input and output. */ struct Windower { - alignas(16) std::array mData; + alignas(16) std::array mData{}; Windower() { @@ -82,27 +83,29 @@ struct FrequencyBin { struct PshifterState final : public EffectState { /* Effect parameters */ - size_t mCount; - size_t mPos; - uint mPitchShiftI; - float mPitchShift; + size_t mCount{}; + size_t mPos{}; + uint mPitchShiftI{}; + float mPitchShift{}; /* Effects buffers */ - std::array mFIFO; - std::array mLastPhase; - std::array mSumPhase; - std::array mOutputAccum; + std::array mFIFO{}; + std::array mLastPhase{}; + std::array mSumPhase{}; + std::array mOutputAccum{}; - std::array mFftBuffer; + PFFFTSetup mFft; + alignas(16) std::array mFftBuffer{}; + alignas(16) std::array mFftWorkBuffer{}; - std::array mAnalysisBuffer; - std::array mSynthesisBuffer; + std::array mAnalysisBuffer{}; + std::array mSynthesisBuffer{}; - alignas(16) FloatBufferLine mBufferOut; + alignas(16) FloatBufferLine mBufferOut{}; /* Effect gains for each output channel */ - float mCurrentGains[MaxAmbiChannels]; - float mTargetGains[MaxAmbiChannels]; + std::array mCurrentGains{}; + std::array mTargetGains{}; void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override; @@ -110,8 +113,6 @@ struct PshifterState final : public EffectState { const EffectTarget target) override; void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; - - DEF_NEWDEL(PshifterState) }; void PshifterState::deviceUpdate(const DeviceBase*, const BufferStorage*) @@ -126,26 +127,31 @@ void PshifterState::deviceUpdate(const DeviceBase*, const BufferStorage*) mLastPhase.fill(0.0f); mSumPhase.fill(0.0f); mOutputAccum.fill(0.0f); - mFftBuffer.fill(complex_f{}); + mFftBuffer.fill(0.0f); mAnalysisBuffer.fill(FrequencyBin{}); mSynthesisBuffer.fill(FrequencyBin{}); - std::fill(std::begin(mCurrentGains), std::end(mCurrentGains), 0.0f); - std::fill(std::begin(mTargetGains), std::end(mTargetGains), 0.0f); + mCurrentGains.fill(0.0f); + mTargetGains.fill(0.0f); + + if(!mFft) + mFft = PFFFTSetup{StftSize, PFFFT_REAL}; } void PshifterState::update(const ContextBase*, const EffectSlot *slot, - const EffectProps *props, const EffectTarget target) + const EffectProps *props_, const EffectTarget target) { - const int tune{props->Pshifter.CoarseTune*100 + props->Pshifter.FineTune}; + auto &props = std::get(*props_); + const int tune{props.CoarseTune*100 + props.FineTune}; const float pitch{std::pow(2.0f, static_cast(tune) / 1200.0f)}; - mPitchShiftI = clampu(fastf2u(pitch*MixerFracOne), MixerFracHalf, MixerFracOne*2); + mPitchShiftI = std::clamp(fastf2u(pitch*MixerFracOne), uint{MixerFracHalf}, + uint{MixerFracOne}*2u); mPitchShift = static_cast(mPitchShiftI) * float{1.0f/MixerFracOne}; static constexpr auto coeffs = CalcDirectionCoeffs(std::array{0.0f, 0.0f, -1.0f}); mOutTarget = target.Main->Buffer; - ComputePanGains(target.Main, coeffs.data(), slot->Gain, mTargetGains); + ComputePanGains(target.Main, coeffs, slot->Gain, mTargetGains); } void PshifterState::process(const size_t samplesToDo, @@ -162,7 +168,7 @@ void PshifterState::process(const size_t samplesToDo, for(size_t base{0u};base < samplesToDo;) { - const size_t todo{minz(StftStep-mCount, samplesToDo-base)}; + const size_t todo{std::min(StftStep-mCount, samplesToDo-base)}; /* Retrieve the output samples from the FIFO and fill in the new input * samples. @@ -186,15 +192,19 @@ void PshifterState::process(const size_t samplesToDo, mFftBuffer[k] = mFIFO[src] * gWindow.mData[k]; for(size_t src{0u}, k{StftSize-mPos};src < mPos;++src,++k) mFftBuffer[k] = mFIFO[src] * gWindow.mData[k]; - forward_fft(al::span{mFftBuffer}); + mFft.transform_ordered(mFftBuffer.data(), mFftBuffer.data(), mFftWorkBuffer.data(), + PFFFT_FORWARD); /* Analyze the obtained data. Since the real FFT is symmetric, only * StftHalfSize+1 samples are needed. */ - for(size_t k{0u};k < StftHalfSize+1;k++) + for(size_t k{0u};k < StftHalfSize+1;++k) { - const float magnitude{std::abs(mFftBuffer[k])}; - const float phase{std::arg(mFftBuffer[k])}; + const auto cplx = (k == 0) ? complex_f{mFftBuffer[0]} : + (k == StftHalfSize) ? complex_f{mFftBuffer[1]} : + complex_f{mFftBuffer[k*2], mFftBuffer[k*2 + 1]}; + const float magnitude{std::abs(cplx)}; + const float phase{std::arg(cplx)}; /* Compute the phase difference from the last update and subtract * the expected phase difference for this bin. @@ -232,8 +242,8 @@ void PshifterState::process(const size_t samplesToDo, */ std::fill(mSynthesisBuffer.begin(), mSynthesisBuffer.end(), FrequencyBin{}); - constexpr size_t bin_limit{((StftHalfSize+1)<> MixerFracBits}; @@ -266,21 +276,29 @@ void PshifterState::process(const size_t samplesToDo, tmp -= static_cast(qpd + (qpd%2)); mSumPhase[k] = tmp * al::numbers::pi_v; - mFftBuffer[k] = std::polar(mSynthesisBuffer[k].Magnitude, mSumPhase[k]); + const complex_f cplx{std::polar(mSynthesisBuffer[k].Magnitude, mSumPhase[k])}; + if(k == 0) + mFftBuffer[0] = cplx.real(); + else if(k == StftHalfSize) + mFftBuffer[1] = cplx.real(); + else + { + mFftBuffer[k*2 + 0] = cplx.real(); + mFftBuffer[k*2 + 1] = cplx.imag(); + } } - for(size_t k{StftHalfSize+1};k < StftSize;++k) - mFftBuffer[k] = std::conj(mFftBuffer[StftSize-k]); /* Apply an inverse FFT to get the time-domain signal, and accumulate * for the output with windowing. */ - inverse_fft(al::span{mFftBuffer}); + mFft.transform_ordered(mFftBuffer.data(), mFftBuffer.data(), mFftWorkBuffer.data(), + PFFFT_BACKWARD); static constexpr float scale{3.0f / OversampleFactor / StftSize}; for(size_t dst{mPos}, k{0u};dst < StftSize;++dst,++k) - mOutputAccum[dst] += gWindow.mData[k]*mFftBuffer[k].real() * scale; + mOutputAccum[dst] += gWindow.mData[k]*mFftBuffer[k] * scale; for(size_t dst{0u}, k{StftSize-mPos};dst < mPos;++dst,++k) - mOutputAccum[dst] += gWindow.mData[k]*mFftBuffer[k].real() * scale; + mOutputAccum[dst] += gWindow.mData[k]*mFftBuffer[k] * scale; /* Copy out the accumulated result, then clear for the next iteration. */ std::copy_n(mOutputAccum.begin() + mPos, StftStep, mFIFO.begin() + mPos); @@ -288,8 +306,8 @@ void PshifterState::process(const size_t samplesToDo, } /* Now, mix the processed sound data to the output. */ - MixSamples({mBufferOut.data(), samplesToDo}, samplesOut, mCurrentGains, mTargetGains, - maxz(samplesToDo, 512), 0); + MixSamples(al::span{mBufferOut}.first(samplesToDo), samplesOut, mCurrentGains, mTargetGains, + std::max(samplesToDo, 512_uz), 0); } diff --git a/3rdparty/openal/alc/effects/reverb.cpp b/3rdparty/openal/alc/effects/reverb.cpp index b00f638b27a7..7e907911bc86 100644 --- a/3rdparty/openal/alc/effects/reverb.cpp +++ b/3rdparty/openal/alc/effects/reverb.cpp @@ -22,22 +22,23 @@ #include #include +#include +#include +#include #include #include -#include #include -#include #include "alc/effects/base.h" -#include "almalloc.h" #include "alnumbers.h" #include "alnumeric.h" #include "alspan.h" #include "core/ambidefs.h" #include "core/bufferline.h" #include "core/context.h" -#include "core/devformat.h" +#include "core/cubic_tables.h" #include "core/device.h" +#include "core/effects/base.h" #include "core/effectslot.h" #include "core/filters/biquad.h" #include "core/filters/splitter.h" @@ -45,13 +46,9 @@ #include "core/mixer/defs.h" #include "intrusive_ptr.h" #include "opthelpers.h" -#include "vecmat.h" #include "vector.h" -/* This is a user config option for modifying the overall output of the reverb - * effect. - */ -float ReverbBoost = 1.0f; +struct BufferStorage; namespace { @@ -65,41 +62,6 @@ constexpr float DefaultModulationTime{0.25f}; #define MOD_FRACMASK (MOD_FRACONE-1) -struct CubicFilter { - static constexpr size_t sTableBits{8}; - static constexpr size_t sTableSteps{1 << sTableBits}; - static constexpr size_t sTableMask{sTableSteps - 1}; - - float mFilter[sTableSteps*2 + 1]{}; - - constexpr CubicFilter() - { - /* This creates a lookup table for a cubic spline filter, with 256 - * steps between samples. Only half the coefficients are needed, since - * Coeff2 is just Coeff1 in reverse and Coeff3 is just Coeff0 in - * reverse. - */ - for(size_t i{0};i < sTableSteps;++i) - { - const double mu{static_cast(i) / double{sTableSteps}}; - const double mu2{mu*mu}, mu3{mu2*mu}; - const double a0{-0.5*mu3 + mu2 + -0.5*mu}; - const double a1{ 1.5*mu3 + -2.5*mu2 + 1.0f}; - mFilter[i] = static_cast(a1); - mFilter[sTableSteps+i] = static_cast(a0); - } - } - - constexpr float getCoeff0(size_t i) const noexcept { return mFilter[sTableSteps+i]; } - constexpr float getCoeff1(size_t i) const noexcept { return mFilter[i]; } - constexpr float getCoeff2(size_t i) const noexcept { return mFilter[sTableSteps-i]; } - constexpr float getCoeff3(size_t i) const noexcept { return mFilter[sTableSteps*2-i]; } -}; -constexpr CubicFilter gCubicTable; - - -using namespace std::placeholders; - /* Max samples per process iteration. Used to limit the size needed for * temporary buffers. Must be a multiple of 4 for SIMD alignment. */ @@ -122,35 +84,41 @@ constexpr size_t NUM_LINES{4u}; constexpr float MODULATION_DEPTH_COEFF{0.05f}; -/* The B-Format to A-Format conversion matrix. The arrangement of rows is - * deliberately chosen to align the resulting lines to their spatial opposites - * (0:above front left <-> 3:above back right, 1:below front right <-> 2:below - * back left). It's not quite opposite, since the A-Format results in a - * tetrahedron, but it's close enough. Should the model be extended to 8-lines - * in the future, true opposites can be used. +/* The B-Format to (W-normalized) A-Format conversion matrix. This produces a + * tetrahedral array of discrete signals (boosted by a factor of sqrt(3), to + * reduce the error introduced in the conversion). */ -alignas(16) constexpr float B2A[NUM_LINES][NUM_LINES]{ - { 0.5f, 0.5f, 0.5f, 0.5f }, - { 0.5f, -0.5f, -0.5f, 0.5f }, - { 0.5f, 0.5f, -0.5f, -0.5f }, - { 0.5f, -0.5f, 0.5f, -0.5f } -}; +alignas(16) constexpr std::array,NUM_LINES> B2A{{ + /* W Y Z X */ + {{ 0.5f, 0.5f, 0.5f, 0.5f }}, /* A0 */ + {{ 0.5f, -0.5f, -0.5f, 0.5f }}, /* A1 */ + {{ 0.5f, 0.5f, -0.5f, -0.5f }}, /* A2 */ + {{ 0.5f, -0.5f, 0.5f, -0.5f }} /* A3 */ +}}; -/* Converts A-Format to B-Format for early reflections. */ +/* Converts (W-normalized) A-Format to B-Format for early reflections (scaled + * by 1/sqrt(3) to compensate for the boost in the B2A matrix). + */ alignas(16) constexpr std::array,NUM_LINES> EarlyA2B{{ - {{ 0.5f, 0.5f, 0.5f, 0.5f }}, - {{ 0.5f, -0.5f, 0.5f, -0.5f }}, - {{ 0.5f, -0.5f, -0.5f, 0.5f }}, - {{ 0.5f, 0.5f, -0.5f, -0.5f }} + /* A0 A1 A2 A3 */ + {{ 0.5f, 0.5f, 0.5f, 0.5f }}, /* W */ + {{ 0.5f, -0.5f, 0.5f, -0.5f }}, /* Y */ + {{ 0.5f, -0.5f, -0.5f, 0.5f }}, /* Z */ + {{ 0.5f, 0.5f, -0.5f, -0.5f }} /* X */ }}; -/* Converts A-Format to B-Format for late reverb. */ +/* Converts (W-normalized) A-Format to B-Format for late reverb (scaled + * by 1/sqrt(3) to compensate for the boost in the B2A matrix). The response + * is rotated around Z (ambisonic X) so that the front lines are placed + * horizontally in front, and the rear lines are placed vertically in back. + */ constexpr auto InvSqrt2 = static_cast(1.0/al::numbers::sqrt2); alignas(16) constexpr std::array,NUM_LINES> LateA2B{{ - {{ 0.5f, 0.5f, 0.5f, 0.5f }}, - {{ InvSqrt2, -InvSqrt2, 0.0f, 0.0f }}, - {{ 0.0f, 0.0f, InvSqrt2, -InvSqrt2 }}, - {{ 0.5f, 0.5f, -0.5f, -0.5f }} + /* A0 A1 A2 A3 */ + {{ 0.5f, 0.5f, 0.5f, 0.5f }}, /* W */ + {{ InvSqrt2, -InvSqrt2, 0.0f, 0.0f }}, /* Y */ + {{ 0.0f, 0.0f, -InvSqrt2, InvSqrt2 }}, /* Z */ + {{ 0.5f, 0.5f, -0.5f, -0.5f }} /* X */ }}; /* The all-pass and delay lines have a variable length dependent on the @@ -251,7 +219,7 @@ constexpr std::array EARLY_ALLPASS_LENGTHS{{ * Using an average dimension of 1m, we get: */ constexpr std::array EARLY_LINE_LENGTHS{{ - 5.9850400e-4f, 1.0913150e-3f, 1.5376658e-3f, 1.9419362e-3f + 0.0000000e+0f, 4.9281100e-4f, 9.3916180e-4f, 1.3434322e-3f }}; /* The late all-pass filter lengths are based on the late line lengths: @@ -288,21 +256,17 @@ struct DelayLineI { /* The delay lines use interleaved samples, with the lengths being powers * of 2 to allow the use of bit-masking instead of a modulus for wrapping. */ - size_t Mask{0u}; - union { - uintptr_t LineOffset{0u}; - std::array *Line; - }; + al::span mLine; /* Given the allocated sample buffer, this function updates each delay line * offset. */ - void realizeLineOffset(std::array *sampleBuffer) noexcept - { Line = sampleBuffer + LineOffset; } + void realizeLineOffset(al::span sampleBuffer) noexcept + { mLine = sampleBuffer; } /* Calculate the length of a delay line and store its mask and offset. */ - uint calcLineLength(const float length, const uintptr_t offset, const float frequency, - const uint extra) + static + auto calcLineLength(const float length, const float frequency, const uint extra) -> size_t { /* All line lengths are powers of 2, calculated from their lengths in * seconds, rounded up. @@ -310,23 +274,85 @@ struct DelayLineI { uint samples{float2uint(std::ceil(length*frequency))}; samples = NextPowerOf2(samples + extra); - /* All lines share a single sample buffer. */ - Mask = samples - 1; - LineOffset = offset; - /* Return the sample count for accumulation. */ - return samples; + return samples*NUM_LINES; + } +}; + +struct DelayLineU { + al::span mLine; + + void realizeLineOffset(al::span sampleBuffer) noexcept + { + assert(sampleBuffer.size() > 4 && !(sampleBuffer.size() & (sampleBuffer.size()-1))); + mLine = sampleBuffer; } - void write(size_t offset, const size_t c, const float *RESTRICT in, const size_t count) const noexcept + static + auto calcLineLength(const float length, const float frequency, const uint extra) -> size_t { - ASSUME(count > 0); + uint samples{float2uint(std::ceil(length*frequency))}; + samples = NextPowerOf2(samples + extra); + + return samples*NUM_LINES; + } + + [[nodiscard]] + auto get(size_t chan) const noexcept + { + const size_t stride{mLine.size() / NUM_LINES}; + return mLine.subspan(chan*stride, stride); + } + + void write(size_t offset, const size_t c, al::span in) const noexcept + { + const size_t stride{mLine.size() / NUM_LINES}; + const auto output = mLine.subspan(c*stride); + while(!in.empty()) + { + offset &= stride-1; + const size_t td{std::min(stride - offset, in.size())}; + std::copy_n(in.begin(), td, output.begin() + ptrdiff_t(offset)); + offset += td; + in = in.subspan(td); + } + } + + /* Writes the given input lines to the delay buffer, applying a geometric + * reflection. This effectively applies the matrix + * + * [ +1/2 -1/2 -1/2 -1/2 ] + * [ -1/2 +1/2 -1/2 -1/2 ] + * [ -1/2 -1/2 +1/2 -1/2 ] + * [ -1/2 -1/2 -1/2 +1/2 ] + * + * to the four input lines when writing to the delay buffer. The effect on + * the B-Format signal is negating W, applying a 180-degree phase shift and + * moving each response to its spatially opposite location. + */ + void writeReflected(size_t offset, const al::span in, + const size_t count) const noexcept + { + const size_t stride{mLine.size() / NUM_LINES}; for(size_t i{0u};i < count;) { - offset &= Mask; - size_t td{minz(Mask+1 - offset, count - i)}; + offset &= stride-1; + size_t td{std::min(stride - offset, count - i)}; do { - Line[offset++][c] = in[i++]; + const std::array src{in[0][i], in[1][i], in[2][i], in[3][i]}; + ++i; + + const std::array f{ + (src[0] - src[1] - src[2] - src[3]) * 0.5f, + (src[1] - src[0] - src[2] - src[3]) * 0.5f, + (src[2] - src[0] - src[1] - src[3]) * 0.5f, + (src[3] - src[0] - src[1] - src[2] ) * 0.5f + }; + mLine[0*stride + offset] = f[0]; + mLine[1*stride + offset] = f[1]; + mLine[2*stride + offset] = f[2]; + mLine[3*stride + offset] = f[3]; + ++offset; } while(--td); } } @@ -335,10 +361,19 @@ struct DelayLineI { struct VecAllpass { DelayLineI Delay; float Coeff{0.0f}; - size_t Offset[NUM_LINES]{}; + std::array Offset{}; void process(const al::span samples, size_t offset, - const float xCoeff, const float yCoeff, const size_t todo); + const float xCoeff, const float yCoeff, const size_t todo) const noexcept; +}; + +struct Allpass4 { + DelayLineU Delay; + float Coeff{0.0f}; + std::array Offset{}; + + void process(const al::span samples, const size_t offset, + const size_t todo) const noexcept; }; struct T60Filter { @@ -353,30 +388,37 @@ struct T60Filter { /* Applies the two T60 damping filter sections. */ void process(const al::span samples) - { DualBiquad{HFFilter, LFFilter}.process(samples, samples.data()); } + { DualBiquad{HFFilter, LFFilter}.process(samples, samples); } void clear() noexcept { HFFilter.clear(); LFFilter.clear(); } }; struct EarlyReflections { - /* A Gerzon vector all-pass filter is used to simulate initial diffusion. - * The spread from this filter also helps smooth out the reverb tail. - */ - VecAllpass VecAp; + Allpass4 VecAp; /* An echo line is used to complete the second half of the early * reflections. */ - DelayLineI Delay; - size_t Offset[NUM_LINES]{}; - float Coeff[NUM_LINES]{}; + DelayLineU Delay; + std::array Offset{}; + float Coeff{}; /* The gain for each output channel based on 3D panning. */ - float CurrentGains[NUM_LINES][MaxAmbiChannels]{}; - float TargetGains[NUM_LINES][MaxAmbiChannels]{}; + struct OutGains { + std::array Current{}; + std::array Target{}; + + void clear() { Current.fill(0.0f); Target.fill(0.0); } + }; + std::array Gains{}; void updateLines(const float density_mult, const float diffusion, const float decayTime, const float frequency); + + void clear() + { + std::for_each(Gains.begin(), Gains.end(), std::mem_fn(&OutGains::clear)); + } }; @@ -384,22 +426,29 @@ struct Modulation { /* The vibrato time is tracked with an index over a (MOD_FRACONE) * normalized range. */ - uint Index, Step; + uint Index{0u}, Step{1u}; /* The depth of frequency change, in samples. */ - float Depth; + float Depth{0.0f}; - float ModDelays[MAX_UPDATE_SAMPLES]; + std::array ModDelays{}; void updateModulator(float modTime, float modDepth, float frequency); - void calcDelays(size_t todo); + auto calcDelays(size_t todo) -> al::span; + + void clear() noexcept + { + Index = 0u; + Step = 1u; + Depth = 0.0f; + } }; struct LateReverb { /* A recursive delay line is used fill in the reverb tail. */ - DelayLineI Delay; - size_t Offset[NUM_LINES]{}; + DelayLineU Delay; + std::array Offset{}; /* Attenuation to compensate for the modal density and decay rate of the * late lines. @@ -407,7 +456,7 @@ struct LateReverb { float DensityGain{0.0f}; /* T60 decay filters are used to simulate absorption. */ - T60Filter T60[NUM_LINES]; + std::array T60; Modulation Mod; @@ -415,40 +464,49 @@ struct LateReverb { VecAllpass VecAp; /* The gain for each output channel based on 3D panning. */ - float CurrentGains[NUM_LINES][MaxAmbiChannels]{}; - float TargetGains[NUM_LINES][MaxAmbiChannels]{}; + struct OutGains { + std::array Current{}; + std::array Target{}; + + void clear() { Current.fill(0.0f); Target.fill(0.0); } + }; + std::array Gains{}; void updateLines(const float density_mult, const float diffusion, const float lfDecayTime, const float mfDecayTime, const float hfDecayTime, const float lf0norm, const float hf0norm, const float frequency); - void clear() noexcept + void clear() { - for(auto &filter : T60) - filter.clear(); + std::for_each(T60.begin(), T60.end(), std::mem_fn(&T60Filter::clear)); + Mod.clear(); + std::for_each(Gains.begin(), Gains.end(), std::mem_fn(&OutGains::clear)); } }; struct ReverbPipeline { /* Master effect filters */ - struct { + struct FilterPair { BiquadFilter Lp; BiquadFilter Hp; - } mFilter[NUM_LINES]; + void clear() noexcept { Lp.clear(); Hp.clear(); } + }; + std::array mFilter; - /* Core delay line (early reflections and late reverb tap from this). */ - DelayLineI mEarlyDelayIn; - DelayLineI mLateDelayIn; + /* Late reverb input delay line (early reflections feed this, and late + * reverb taps from it). + */ + DelayLineU mLateDelayIn; - /* Tap points for early reflection delay. */ - size_t mEarlyDelayTap[NUM_LINES][2]{}; - float mEarlyDelayCoeff[NUM_LINES]{}; + /* Tap points for early reflection input delay. */ + std::array,NUM_LINES> mEarlyDelayTap{}; + std::array mEarlyDelayCoeff{}; /* Tap points for late reverb feed and delay. */ - size_t mLateDelayTap[NUM_LINES][2]{}; + std::array,NUM_LINES> mLateDelayTap{}; /* Coefficients for the all-pass and line scattering matrices. */ - float mMixX{0.0f}; + float mMixX{1.0f}; float mMixY{0.0f}; EarlyReflections mEarly; @@ -459,13 +517,13 @@ struct ReverbPipeline { size_t mFadeSampleCount{1}; - void updateDelayLine(const float earlyDelay, const float lateDelay, const float density_mult, - const float decayTime, const float frequency); + void updateDelayLine(const float gain, const float earlyDelay, const float lateDelay, + const float density_mult, const float frequency); void update3DPanning(const al::span ReflectionsPan, const al::span LateReverbPan, const float earlyGain, const float lateGain, const bool doUpmix, const MixParams *mainMix); - void processEarly(size_t offset, const size_t samplesToDo, + void processEarly(const DelayLineU &main_delay, size_t offset, const size_t samplesToDo, const al::span tempSamples, const al::span outSamples); void processLate(size_t offset, const size_t samplesToDo, @@ -474,17 +532,15 @@ struct ReverbPipeline { void clear() noexcept { - for(auto &filter : mFilter) - { - filter.Lp.clear(); - filter.Hp.clear(); - } + std::for_each(mFilter.begin(), mFilter.end(), std::mem_fn(&FilterPair::clear)); + mEarlyDelayTap = {}; + mEarlyDelayCoeff = {}; + mLateDelayTap = {}; + mEarly.clear(); mLate.clear(); - for(auto &filters : mAmbiSplitter) - { - for(auto &filter : filters) - filter.clear(); - } + auto clear_filters = [](const al::span filters) + { std::for_each(filters.begin(), filters.end(), std::mem_fn(&BandSplitter::clear)); }; + std::for_each(mAmbiSplitter.begin(), mAmbiSplitter.end(), clear_filters); } }; @@ -492,9 +548,9 @@ struct ReverbState final : public EffectState { /* All delay lines are allocated as a single buffer to reduce memory * fragmentation and management code. */ - al::vector,16> mSampleBuffer; + al::vector mSampleBuffer; - struct { + struct Params { /* Calculated parameters which indicate if cross-fading is needed after * an update. */ @@ -507,7 +563,8 @@ struct ReverbState final : public EffectState { float ModulationDepth{0.0f}; float HFReference{5000.0f}; float LFReference{250.0f}; - } mParams; + }; + Params mParams; enum PipelineState : uint8_t { DeviceClear, @@ -517,18 +574,20 @@ struct ReverbState final : public EffectState { Normal, }; PipelineState mPipelineState{DeviceClear}; - uint8_t mCurrentPipeline{0}; + bool mCurrentPipeline{false}; + + /* Core delay line (early reflections tap from this). */ + DelayLineU mMainDelay; - ReverbPipeline mPipelines[2]; + std::array mPipelines; /* The current write offset for all delay lines. */ size_t mOffset{}; /* Temporary storage used when processing. */ - union { - alignas(16) FloatBufferLine mTempLine{}; - alignas(16) std::array mTempSamples; - }; + alignas(16) FloatBufferLine mTempLine{}; + alignas(16) std::array mTempSamples{}; + alignas(16) std::array mEarlySamples{}; alignas(16) std::array mLateSamples{}; @@ -538,48 +597,43 @@ struct ReverbState final : public EffectState { void MixOutPlain(ReverbPipeline &pipeline, const al::span samplesOut, - const size_t todo) + const size_t todo) const { - ASSUME(todo > 0); - /* When not upsampling, the panning gains convert to B-Format and pan * at the same time. */ - for(size_t c{0u};c < NUM_LINES;c++) + auto inBuffer = mEarlySamples.cbegin(); + for(auto &gains : pipeline.mEarly.Gains) { - const al::span tmpspan{mEarlySamples[c].data(), todo}; - MixSamples(tmpspan, samplesOut, pipeline.mEarly.CurrentGains[c], - pipeline.mEarly.TargetGains[c], todo, 0); + MixSamples(al::span{*inBuffer++}.first(todo), samplesOut, gains.Current, gains.Target, + todo, 0); } - for(size_t c{0u};c < NUM_LINES;c++) + inBuffer = mLateSamples.cbegin(); + for(auto &gains : pipeline.mLate.Gains) { - const al::span tmpspan{mLateSamples[c].data(), todo}; - MixSamples(tmpspan, samplesOut, pipeline.mLate.CurrentGains[c], - pipeline.mLate.TargetGains[c], todo, 0); + MixSamples(al::span{*inBuffer++}.first(todo), samplesOut, gains.Current, gains.Target, + todo, 0); } } void MixOutAmbiUp(ReverbPipeline &pipeline, const al::span samplesOut, const size_t todo) { - ASSUME(todo > 0); - auto DoMixRow = [](const al::span OutBuffer, const al::span Gains, - const float *InSamples, const size_t InStride) + const al::span InSamples) { + auto inBuffer = InSamples.cbegin(); std::fill(OutBuffer.begin(), OutBuffer.end(), 0.0f); for(const float gain : Gains) { - const float *RESTRICT input{al::assume_aligned<16>(InSamples)}; - InSamples += InStride; - - if(!(std::fabs(gain) > GainSilenceThreshold)) - continue; - - auto mix_sample = [gain](const float sample, const float in) noexcept -> float - { return sample + in*gain; }; - std::transform(OutBuffer.begin(), OutBuffer.end(), input, OutBuffer.begin(), - mix_sample); + if(std::fabs(gain) > GainSilenceThreshold) + { + auto mix_sample = [gain](const float sample, const float in) noexcept -> float + { return sample + in*gain; }; + std::transform(OutBuffer.begin(), OutBuffer.end(), inBuffer->cbegin(), + OutBuffer.begin(), mix_sample); + } + ++inBuffer; } }; @@ -587,29 +641,33 @@ struct ReverbState final : public EffectState { * so the proper HF scaling can be applied to each B-Format channel. * The panning gains then pan and upsample the B-Format channels. */ - const al::span tmpspan{al::assume_aligned<16>(mTempLine.data()), todo}; - for(size_t c{0u};c < NUM_LINES;c++) + const auto tmpspan = al::span{mTempLine}.first(todo); + auto hfscale = float{mOrderScales[0]}; + auto splitter = pipeline.mAmbiSplitter[0].begin(); + auto a2bcoeffs = EarlyA2B.cbegin(); + for(auto &gains : pipeline.mEarly.Gains) { - DoMixRow(tmpspan, EarlyA2B[c], mEarlySamples[0].data(), mEarlySamples[0].size()); + DoMixRow(tmpspan, *(a2bcoeffs++), mEarlySamples); /* Apply scaling to the B-Format's HF response to "upsample" it to * higher-order output. */ - const float hfscale{(c==0) ? mOrderScales[0] : mOrderScales[1]}; - pipeline.mAmbiSplitter[0][c].processHfScale(tmpspan, hfscale); + (splitter++)->processHfScale(tmpspan, hfscale); + hfscale = mOrderScales[1]; - MixSamples(tmpspan, samplesOut, pipeline.mEarly.CurrentGains[c], - pipeline.mEarly.TargetGains[c], todo, 0); + MixSamples(tmpspan, samplesOut, gains.Current, gains.Target, todo, 0); } - for(size_t c{0u};c < NUM_LINES;c++) + hfscale = mOrderScales[0]; + splitter = pipeline.mAmbiSplitter[1].begin(); + a2bcoeffs = LateA2B.cbegin(); + for(auto &gains : pipeline.mLate.Gains) { - DoMixRow(tmpspan, LateA2B[c], mLateSamples[0].data(), mLateSamples[0].size()); + DoMixRow(tmpspan, *(a2bcoeffs++), mLateSamples); - const float hfscale{(c==0) ? mOrderScales[0] : mOrderScales[1]}; - pipeline.mAmbiSplitter[1][c].processHfScale(tmpspan, hfscale); + (splitter++)->processHfScale(tmpspan, hfscale); + hfscale = mOrderScales[1]; - MixSamples(tmpspan, samplesOut, pipeline.mLate.CurrentGains[c], - pipeline.mLate.TargetGains[c], todo, 0); + MixSamples(tmpspan, samplesOut, gains.Current, gains.Target, todo, 0); } } @@ -628,8 +686,6 @@ struct ReverbState final : public EffectState { const EffectTarget target) override; void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; - - DEF_NEWDEL(ReverbState) }; /************************************** @@ -637,18 +693,13 @@ struct ReverbState final : public EffectState { **************************************/ inline float CalcDelayLengthMult(float density) -{ return maxf(5.0f, std::cbrt(density*DENSITY_SCALE)); } +{ return std::max(5.0f, std::cbrt(density*DENSITY_SCALE)); } /* Calculates the delay line metrics and allocates the shared sample buffer * for all lines given the sample rate (frequency). */ void ReverbState::allocLines(const float frequency) { - /* All delay line lengths are calculated to accommodate the full range of - * lengths given their respective parameters. - */ - size_t totalSamples{0u}; - /* Multiplier for the maximum density value, i.e. density=1, which is * actually the least density... */ @@ -658,108 +709,93 @@ void ReverbState::allocLines(const float frequency) * time and depth coefficient, and halfed for the low-to-high frequency * swing. */ - constexpr float max_mod_delay{MaxModulationTime*MODULATION_DEPTH_COEFF / 2.0f}; + static constexpr float max_mod_delay{MaxModulationTime*MODULATION_DEPTH_COEFF / 2.0f}; + + std::array linelengths{}; + size_t oidx{0}; + size_t totalSamples{0u}; + /* The main delay length includes the maximum early reflection delay and + * the largest early tap width. It must also be extended by the update size + * (BufferLineSize) for block processing. + */ + float length{ReverbMaxReflectionsDelay + EARLY_TAP_LENGTHS.back()*multiplier}; + size_t count{mMainDelay.calcLineLength(length, frequency, BufferLineSize)}; + linelengths[oidx++] = count; + totalSamples += count; for(auto &pipeline : mPipelines) { - /* The main delay length includes the maximum early reflection delay, - * the largest early tap width, the maximum late reverb delay, and the - * largest late tap width. Finally, it must also be extended by the - * update size (BufferLineSize) for block processing. - */ - float length{ReverbMaxReflectionsDelay + EARLY_TAP_LENGTHS.back()*multiplier}; - totalSamples += pipeline.mEarlyDelayIn.calcLineLength(length, totalSamples, frequency, - BufferLineSize); - - constexpr float LateLineDiffAvg{(LATE_LINE_LENGTHS.back()-LATE_LINE_LENGTHS.front()) / + static constexpr float LateDiffAvg{(LATE_LINE_LENGTHS.back()-LATE_LINE_LENGTHS.front()) / float{NUM_LINES}}; - length = ReverbMaxLateReverbDelay + LateLineDiffAvg*multiplier; - totalSamples += pipeline.mLateDelayIn.calcLineLength(length, totalSamples, frequency, - BufferLineSize); + length = ReverbMaxLateReverbDelay + LateDiffAvg*multiplier; + count = pipeline.mLateDelayIn.calcLineLength(length, frequency, BufferLineSize); + linelengths[oidx++] = count; + totalSamples += count; /* The early vector all-pass line. */ length = EARLY_ALLPASS_LENGTHS.back() * multiplier; - totalSamples += pipeline.mEarly.VecAp.Delay.calcLineLength(length, totalSamples, frequency, - 0); + count = pipeline.mEarly.VecAp.Delay.calcLineLength(length, frequency, 0); + linelengths[oidx++] = count; + totalSamples += count; /* The early reflection line. */ length = EARLY_LINE_LENGTHS.back() * multiplier; - totalSamples += pipeline.mEarly.Delay.calcLineLength(length, totalSamples, frequency, - MAX_UPDATE_SAMPLES); + count = pipeline.mEarly.Delay.calcLineLength(length, frequency, MAX_UPDATE_SAMPLES); + linelengths[oidx++] = count; + totalSamples += count; /* The late vector all-pass line. */ length = LATE_ALLPASS_LENGTHS.back() * multiplier; - totalSamples += pipeline.mLate.VecAp.Delay.calcLineLength(length, totalSamples, frequency, - 0); + count = pipeline.mLate.VecAp.Delay.calcLineLength(length, frequency, 0); + linelengths[oidx++] = count; + totalSamples += count; /* The late delay lines are calculated from the largest maximum density * line length, and the maximum modulation delay. Four additional * samples are needed for resampling the modulator delay. */ length = LATE_LINE_LENGTHS.back()*multiplier + max_mod_delay; - totalSamples += pipeline.mLate.Delay.calcLineLength(length, totalSamples, frequency, 4); + count = pipeline.mLate.Delay.calcLineLength(length, frequency, 4); + linelengths[oidx++] = count; + totalSamples += count; } + assert(oidx == linelengths.size()); if(totalSamples != mSampleBuffer.size()) decltype(mSampleBuffer)(totalSamples).swap(mSampleBuffer); /* Clear the sample buffer. */ - std::fill(mSampleBuffer.begin(), mSampleBuffer.end(), decltype(mSampleBuffer)::value_type{}); + std::fill(mSampleBuffer.begin(), mSampleBuffer.end(), 0.0f); /* Update all delays to reflect the new sample buffer. */ + auto bufferspan = al::span{mSampleBuffer}; + oidx = 0; + mMainDelay.realizeLineOffset(bufferspan.first(linelengths[oidx])); + bufferspan = bufferspan.subspan(linelengths[oidx++]); for(auto &pipeline : mPipelines) { - pipeline.mEarlyDelayIn.realizeLineOffset(mSampleBuffer.data()); - pipeline.mLateDelayIn.realizeLineOffset(mSampleBuffer.data()); - pipeline.mEarly.VecAp.Delay.realizeLineOffset(mSampleBuffer.data()); - pipeline.mEarly.Delay.realizeLineOffset(mSampleBuffer.data()); - pipeline.mLate.VecAp.Delay.realizeLineOffset(mSampleBuffer.data()); - pipeline.mLate.Delay.realizeLineOffset(mSampleBuffer.data()); + pipeline.mLateDelayIn.realizeLineOffset(bufferspan.first(linelengths[oidx])); + bufferspan = bufferspan.subspan(linelengths[oidx++]); + pipeline.mEarly.VecAp.Delay.realizeLineOffset(bufferspan.first(linelengths[oidx])); + bufferspan = bufferspan.subspan(linelengths[oidx++]); + pipeline.mEarly.Delay.realizeLineOffset(bufferspan.first(linelengths[oidx])); + bufferspan = bufferspan.subspan(linelengths[oidx++]); + pipeline.mLate.VecAp.Delay.realizeLineOffset(bufferspan.first(linelengths[oidx])); + bufferspan = bufferspan.subspan(linelengths[oidx++]); + pipeline.mLate.Delay.realizeLineOffset(bufferspan.first(linelengths[oidx])); + bufferspan = bufferspan.subspan(linelengths[oidx++]); } + assert(oidx == linelengths.size()); } void ReverbState::deviceUpdate(const DeviceBase *device, const BufferStorage*) { - const auto frequency = static_cast(device->Frequency); + const auto frequency = static_cast(device->mSampleRate); /* Allocate the delay lines. */ allocLines(frequency); - for(auto &pipeline : mPipelines) - { - /* Clear filters and gain coefficients since the delay lines were all just - * cleared (if not reallocated). - */ - for(auto &filter : pipeline.mFilter) - { - filter.Lp.clear(); - filter.Hp.clear(); - } - - std::fill(std::begin(pipeline.mEarlyDelayCoeff),std::end(pipeline.mEarlyDelayCoeff), 0.0f); - std::fill(std::begin(pipeline.mEarlyDelayCoeff),std::end(pipeline.mEarlyDelayCoeff), 0.0f); - - pipeline.mLate.DensityGain = 0.0f; - for(auto &t60 : pipeline.mLate.T60) - { - t60.MidGain = 0.0f; - t60.HFFilter.clear(); - t60.LFFilter.clear(); - } - - pipeline.mLate.Mod.Index = 0; - pipeline.mLate.Mod.Step = 1; - pipeline.mLate.Mod.Depth = 0.0f; - - for(auto &gains : pipeline.mEarly.CurrentGains) - std::fill(std::begin(gains), std::end(gains), 0.0f); - for(auto &gains : pipeline.mEarly.TargetGains) - std::fill(std::begin(gains), std::end(gains), 0.0f); - for(auto &gains : pipeline.mLate.CurrentGains) - std::fill(std::begin(gains), std::end(gains), 0.0f); - for(auto &gains : pipeline.mLate.TargetGains) - std::fill(std::begin(gains), std::end(gains), 0.0f); - } + std::for_each(mPipelines.begin(), mPipelines.end(), std::mem_fn(&ReverbPipeline::clear)); mPipelineState = DeviceClear; /* Reset offset base. */ @@ -775,14 +811,14 @@ void ReverbState::deviceUpdate(const DeviceBase *device, const BufferStorage*) mUpmixOutput = false; mOrderScales.fill(1.0f); } - mPipelines[0].mAmbiSplitter[0][0].init(device->mXOverFreq / frequency); - for(auto &pipeline : mPipelines) + + auto splitter = BandSplitter{device->mXOverFreq / frequency}; + auto set_splitters = [&splitter](ReverbPipeline &pipeline) { - std::fill(pipeline.mAmbiSplitter[0].begin(), pipeline.mAmbiSplitter[0].end(), - pipeline.mAmbiSplitter[0][0]); - std::fill(pipeline.mAmbiSplitter[1].begin(), pipeline.mAmbiSplitter[1].end(), - pipeline.mAmbiSplitter[0][0]); - } + std::fill(pipeline.mAmbiSplitter[0].begin(), pipeline.mAmbiSplitter[0].end(), splitter); + std::fill(pipeline.mAmbiSplitter[1].begin(), pipeline.mAmbiSplitter[1].end(), splitter); + }; + std::for_each(mPipelines.begin(), mPipelines.end(), set_splitters); } /************************************** @@ -853,7 +889,7 @@ float CalcLimitedHfRatio(const float hfRatio, const float airAbsorptionGainHF, CalcDecayLength(airAbsorptionGainHF, decayTime)}; /* Using the limit calculated above, apply the upper bound to the HF ratio. */ - return minf(limitRatio, hfRatio); + return std::min(limitRatio, hfRatio); } @@ -890,10 +926,15 @@ void EarlyReflections::updateLines(const float density_mult, const float diffusi /* Calculate the delay length of each delay line. */ length = EARLY_LINE_LENGTHS[i] * density_mult; Offset[i] = float2uint(length * frequency); - - /* Calculate the gain (coefficient) for each line. */ - Coeff[i] = CalcDecayCoeff(length, decayTime); } + + /* Calculate the gain (coefficient) for the secondary reflections based on + * the average delay and decay time. + */ + const auto length = std::reduce(EARLY_LINE_LENGTHS.begin(), EARLY_LINE_LENGTHS.end(), 0.0f) + / float{EARLY_LINE_LENGTHS.size()} * density_mult; + Coeff = CalcDecayCoeff(length, decayTime); + } /* Update the EAX modulation step and depth. Keep in mind that this kind of @@ -909,7 +950,7 @@ void Modulation::updateModulator(float modTime, float modDepth, float frequency) * appropriate step size to generate an LFO, which will vary the feedback * delay over time. */ - Step = maxu(fastf2u(MOD_FRACONE / (frequency * modTime)), 1); + Step = std::max(fastf2u(MOD_FRACONE / (frequency * modTime)), 1u); /* The modulation depth effects the amount of frequency change over the * range of the sinus. It needs to be scaled by the modulation time so that @@ -982,7 +1023,7 @@ void LateReverb::updateLines(const float density_mult, const float diffusion, * includes one sample of delay. Reduce by one to compensate. */ length = LATE_LINE_LENGTHS[i] * density_mult; - Offset[i] = maxu(float2uint(length*frequency + 0.5f), 1u) - 1u; + Offset[i] = std::max(float2uint(length*frequency + 0.5f), 1u) - 1u; /* Approximate the absorption that the vector all-pass would exhibit * given the current diffusion so we don't have to process a full T60 @@ -999,8 +1040,8 @@ void LateReverb::updateLines(const float density_mult, const float diffusion, /* Update the offsets for the main effect delay line. */ -void ReverbPipeline::updateDelayLine(const float earlyDelay, const float lateDelay, - const float density_mult, const float decayTime, const float frequency) +void ReverbPipeline::updateDelayLine(const float gain, const float earlyDelay, + const float lateDelay, const float density_mult, const float frequency) { /* Early reflection taps are decorrelated by means of an average room * reflection approximation described above the definition of the taps. @@ -1012,12 +1053,16 @@ void ReverbPipeline::updateDelayLine(const float earlyDelay, const float lateDel * delay path and offsets that would continue the propagation naturally * into the late lines. */ + mEarlyDelayCoeff[1] = gain; for(size_t i{0u};i < NUM_LINES;i++) { float length{EARLY_TAP_LENGTHS[i]*density_mult}; mEarlyDelayTap[i][1] = float2uint((earlyDelay+length) * frequency); - mEarlyDelayCoeff[i] = CalcDecayCoeff(length, decayTime); + /* Reduce the late delay tap by the shortest early delay line length to + * compensate for the late line input being fed by the delayed early + * output. + */ length = (LATE_LINE_LENGTHS[i] - LATE_LINE_LENGTHS.front())/float{NUM_LINES}*density_mult + lateDelay; mLateDelayTap[i][1] = float2uint(length * frequency); @@ -1038,14 +1083,14 @@ std::array,4> GetTransformFromVector(const al::span norm{{vec[0], vec[1], vec[2]}}; float mag{std::sqrt(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2])}; if(mag > 1.0f) { const float scale{al::numbers::sqrt3_v / mag}; - norm[0] = vec[0] * -scale; - norm[1] = vec[1] * scale; - norm[2] = vec[2] * scale; + norm[0] *= -scale; + norm[1] *= scale; + norm[2] *= scale; mag = 1.0f; } else @@ -1054,9 +1099,9 @@ std::array,4> GetTransformFromVector(const al::span; - norm[1] = vec[1] * al::numbers::sqrt3_v; - norm[2] = vec[2] * al::numbers::sqrt3_v; + norm[0] *= -al::numbers::sqrt3_v; + norm[1] *= al::numbers::sqrt3_v; + norm[2] *= al::numbers::sqrt3_v; } return std::array,4>{{ @@ -1075,45 +1120,41 @@ void ReverbPipeline::update3DPanning(const al::span ReflectionsPa /* Create matrices that transform a B-Format signal according to the * panning vectors. */ - const std::array,4> earlymat{GetTransformFromVector(ReflectionsPan)}; - const std::array,4> latemat{GetTransformFromVector(LateReverbPan)}; + const auto earlymat = GetTransformFromVector(ReflectionsPan); + const auto latemat = GetTransformFromVector(LateReverbPan); - if(doUpmix) + const auto get_coeffs = [&] { - /* When upsampling, combine the early and late transforms with the - * first-order upsample matrix. This results in panning gains that - * apply the panning transform to first-order B-Format, which is then - * upsampled. - */ - auto mult_matrix = [](const al::span,4> mtx1) + if(doUpmix) { - auto&& mtx2 = AmbiScale::FirstOrderUp; - std::array,NUM_LINES> res{}; - - for(size_t i{0};i < mtx1[0].size();++i) + /* When upsampling, combine the early and late transforms with the + * first-order upsample matrix. This results in panning gains that + * apply the panning transform to first-order B-Format, which is + * then upsampled. + */ + auto mult_matrix = [](const al::span,4> mtx1) { - float *RESTRICT dst{res[i].data()}; - for(size_t k{0};k < mtx1.size();++k) + std::array,NUM_LINES> res{}; + const auto mtx2 = al::span{AmbiScale::FirstOrderUp}; + + for(size_t i{0};i < mtx1[0].size();++i) { - const float *RESTRICT src{mtx2[k].data()}; - const float a{mtx1[k][i]}; - for(size_t j{0};j < mtx2[0].size();++j) - dst[j] += a * src[j]; + const al::span dst{res[i]}; + static_assert(dst.size() >= std::tuple_size_v); + for(size_t k{0};k < mtx1.size();++k) + { + const float a{mtx1[k][i]}; + std::transform(mtx2[k].begin(), mtx2[k].end(), dst.begin(), dst.begin(), + [a](const float in, const float out) noexcept -> float + { return a*in + out; }); + } } - } - return res; - }; - auto earlycoeffs = mult_matrix(earlymat); - auto latecoeffs = mult_matrix(latemat); + return res; + }; + return std::array{mult_matrix(earlymat), mult_matrix(latemat)}; + } - for(size_t i{0u};i < NUM_LINES;i++) - ComputePanGains(mainMix, earlycoeffs[i].data(), earlyGain, mEarly.TargetGains[i]); - for(size_t i{0u};i < NUM_LINES;i++) - ComputePanGains(mainMix, latecoeffs[i].data(), lateGain, mLate.TargetGains[i]); - } - else - { /* When not upsampling, combine the early and late A-to-B-Format * conversions with their respective transform. This results panning * gains that convert A-Format to B-Format, which is then panned. @@ -1125,158 +1166,154 @@ void ReverbPipeline::update3DPanning(const al::span ReflectionsPa for(size_t i{0};i < mtx1[0].size();++i) { - float *RESTRICT dst{res[i].data()}; + const al::span dst{res[i]}; + static_assert(dst.size() >= std::tuple_size_v); for(size_t k{0};k < mtx1.size();++k) { const float a{mtx1[k][i]}; - for(size_t j{0};j < mtx2.size();++j) - dst[j] += a * mtx2[j][k]; + std::transform(mtx2[k].begin(), mtx2[k].end(), dst.begin(), dst.begin(), + [a](const float in, const float out) noexcept -> float + { return a*in + out; }); } } return res; }; - auto earlycoeffs = mult_matrix(EarlyA2B, earlymat); - auto latecoeffs = mult_matrix(LateA2B, latemat); - - for(size_t i{0u};i < NUM_LINES;i++) - ComputePanGains(mainMix, earlycoeffs[i].data(), earlyGain, mEarly.TargetGains[i]); - for(size_t i{0u};i < NUM_LINES;i++) - ComputePanGains(mainMix, latecoeffs[i].data(), lateGain, mLate.TargetGains[i]); - } + return std::array{mult_matrix(EarlyA2B, earlymat), mult_matrix(LateA2B, latemat)}; + }; + const auto [earlycoeffs, latecoeffs] = get_coeffs(); + + auto earlygains = mEarly.Gains.begin(); + for(auto &coeffs : earlycoeffs) + ComputePanGains(mainMix, coeffs, earlyGain, (earlygains++)->Target); + auto lategains = mLate.Gains.begin(); + for(auto &coeffs : latecoeffs) + ComputePanGains(mainMix, coeffs, lateGain, (lategains++)->Target); } void ReverbState::update(const ContextBase *Context, const EffectSlot *Slot, - const EffectProps *props, const EffectTarget target) + const EffectProps *props_, const EffectTarget target) { + auto &props = std::get(*props_); const DeviceBase *Device{Context->mDevice}; - const auto frequency = static_cast(Device->Frequency); + const auto frequency = static_cast(Device->mSampleRate); /* If the HF limit parameter is flagged, calculate an appropriate limit * based on the air absorption parameter. */ - float hfRatio{props->Reverb.DecayHFRatio}; - if(props->Reverb.DecayHFLimit && props->Reverb.AirAbsorptionGainHF < 1.0f) - hfRatio = CalcLimitedHfRatio(hfRatio, props->Reverb.AirAbsorptionGainHF, - props->Reverb.DecayTime); + float hfRatio{props.DecayHFRatio}; + if(props.DecayHFLimit && props.AirAbsorptionGainHF < 1.0f) + hfRatio = CalcLimitedHfRatio(hfRatio, props.AirAbsorptionGainHF, props.DecayTime); /* Calculate the LF/HF decay times. */ constexpr float MinDecayTime{0.1f}, MaxDecayTime{20.0f}; - const float lfDecayTime{clampf(props->Reverb.DecayTime*props->Reverb.DecayLFRatio, - MinDecayTime, MaxDecayTime)}; - const float hfDecayTime{clampf(props->Reverb.DecayTime*hfRatio, MinDecayTime, MaxDecayTime)}; + const float lfDecayTime{std::clamp(props.DecayTime*props.DecayLFRatio, MinDecayTime, + MaxDecayTime)}; + const float hfDecayTime{std::clamp(props.DecayTime*hfRatio, MinDecayTime, MaxDecayTime)}; /* Determine if a full update is required. */ const bool fullUpdate{mPipelineState == DeviceClear || /* Density is essentially a master control for the feedback delays, so * changes the offsets of many delay lines. */ - mParams.Density != props->Reverb.Density || + mParams.Density != props.Density || /* Diffusion and decay times influences the decay rate (gain) of the * late reverb T60 filter. */ - mParams.Diffusion != props->Reverb.Diffusion || - mParams.DecayTime != props->Reverb.DecayTime || + mParams.Diffusion != props.Diffusion || + mParams.DecayTime != props.DecayTime || mParams.HFDecayTime != hfDecayTime || mParams.LFDecayTime != lfDecayTime || /* Modulation time and depth both require fading the modulation delay. */ - mParams.ModulationTime != props->Reverb.ModulationTime || - mParams.ModulationDepth != props->Reverb.ModulationDepth || + mParams.ModulationTime != props.ModulationTime || + mParams.ModulationDepth != props.ModulationDepth || /* HF/LF References control the weighting used to calculate the density * gain. */ - mParams.HFReference != props->Reverb.HFReference || - mParams.LFReference != props->Reverb.LFReference}; + mParams.HFReference != props.HFReference || + mParams.LFReference != props.LFReference}; if(fullUpdate) { - mParams.Density = props->Reverb.Density; - mParams.Diffusion = props->Reverb.Diffusion; - mParams.DecayTime = props->Reverb.DecayTime; + mParams.Density = props.Density; + mParams.Diffusion = props.Diffusion; + mParams.DecayTime = props.DecayTime; mParams.HFDecayTime = hfDecayTime; mParams.LFDecayTime = lfDecayTime; - mParams.ModulationTime = props->Reverb.ModulationTime; - mParams.ModulationDepth = props->Reverb.ModulationDepth; - mParams.HFReference = props->Reverb.HFReference; - mParams.LFReference = props->Reverb.LFReference; + mParams.ModulationTime = props.ModulationTime; + mParams.ModulationDepth = props.ModulationDepth; + mParams.HFReference = props.HFReference; + mParams.LFReference = props.LFReference; mPipelineState = (mPipelineState != DeviceClear) ? StartFade : Normal; - mCurrentPipeline ^= 1; + mCurrentPipeline = !mCurrentPipeline; + + auto &oldpipeline = mPipelines[!mCurrentPipeline]; + oldpipeline.mEarlyDelayCoeff[1] = 0.0f; } auto &pipeline = mPipelines[mCurrentPipeline]; + /* The density-based room size (delay length) multiplier. */ + const float density_mult{CalcDelayLengthMult(props.Density)}; + + /* Update the main effect delay and associated taps. */ + pipeline.updateDelayLine(props.Gain, props.ReflectionsDelay, props.LateReverbDelay, + density_mult, frequency); + /* Update early and late 3D panning. */ mOutTarget = target.Main->Buffer; - const float gain{props->Reverb.Gain * Slot->Gain * ReverbBoost}; - pipeline.update3DPanning(props->Reverb.ReflectionsPan, props->Reverb.LateReverbPan, - props->Reverb.ReflectionsGain*gain, props->Reverb.LateReverbGain*gain, mUpmixOutput, - target.Main); + const float gain{Slot->Gain * ReverbBoost}; + pipeline.update3DPanning(props.ReflectionsPan, props.LateReverbPan, props.ReflectionsGain*gain, + props.LateReverbGain*gain, mUpmixOutput, target.Main); /* Calculate the master filters */ - float hf0norm{minf(props->Reverb.HFReference/frequency, 0.49f)}; - pipeline.mFilter[0].Lp.setParamsFromSlope(BiquadType::HighShelf, hf0norm, props->Reverb.GainHF, 1.0f); - float lf0norm{minf(props->Reverb.LFReference/frequency, 0.49f)}; - pipeline.mFilter[0].Hp.setParamsFromSlope(BiquadType::LowShelf, lf0norm, props->Reverb.GainLF, 1.0f); + float hf0norm{std::min(props.HFReference/frequency, 0.49f)}; + pipeline.mFilter[0].Lp.setParamsFromSlope(BiquadType::HighShelf, hf0norm, props.GainHF, 1.0f); + float lf0norm{std::min(props.LFReference/frequency, 0.49f)}; + pipeline.mFilter[0].Hp.setParamsFromSlope(BiquadType::LowShelf, lf0norm, props.GainLF, 1.0f); for(size_t i{1u};i < NUM_LINES;i++) { pipeline.mFilter[i].Lp.copyParamsFrom(pipeline.mFilter[0].Lp); pipeline.mFilter[i].Hp.copyParamsFrom(pipeline.mFilter[0].Hp); } - /* The density-based room size (delay length) multiplier. */ - const float density_mult{CalcDelayLengthMult(props->Reverb.Density)}; - - /* Update the main effect delay and associated taps. */ - pipeline.updateDelayLine(props->Reverb.ReflectionsDelay, props->Reverb.LateReverbDelay, - density_mult, props->Reverb.DecayTime, frequency); - if(fullUpdate) { /* Update the early lines. */ - pipeline.mEarly.updateLines(density_mult, props->Reverb.Diffusion, props->Reverb.DecayTime, - frequency); + pipeline.mEarly.updateLines(density_mult, props.Diffusion, props.DecayTime, frequency); /* Get the mixing matrix coefficients. */ - CalcMatrixCoeffs(props->Reverb.Diffusion, &pipeline.mMixX, &pipeline.mMixY); + CalcMatrixCoeffs(props.Diffusion, &pipeline.mMixX, &pipeline.mMixY); /* Update the modulator rate and depth. */ - pipeline.mLate.Mod.updateModulator(props->Reverb.ModulationTime, - props->Reverb.ModulationDepth, frequency); + pipeline.mLate.Mod.updateModulator(props.ModulationTime, props.ModulationDepth, frequency); /* Update the late lines. */ - pipeline.mLate.updateLines(density_mult, props->Reverb.Diffusion, lfDecayTime, - props->Reverb.DecayTime, hfDecayTime, lf0norm, hf0norm, frequency); + pipeline.mLate.updateLines(density_mult, props.Diffusion, lfDecayTime, props.DecayTime, + hfDecayTime, lf0norm, hf0norm, frequency); } /* Calculate the gain at the start of the late reverb stage, and the gain * difference from the decay target (0.001, or -60dB). */ - const float decayBase{props->Reverb.ReflectionsGain * props->Reverb.LateReverbGain}; + const float decayBase{props.ReflectionsGain * props.LateReverbGain}; const float decayDiff{ReverbDecayGain / decayBase}; - if(decayDiff < 1.0f) - { - /* Given the DecayTime (the amount of time for the late reverb to decay - * by -60dB), calculate the time to decay to -60dB from the start of - * the late reverb. - */ - const float diffTime{std::log10(decayDiff)*(20.0f / -60.0f) * props->Reverb.DecayTime}; + /* Given the DecayTime (the amount of time for the late reverb to decay by + * -60dB), calculate the time to decay to -60dB from the start of the late + * reverb. + * + * Otherwise, if the late reverb already starts at -60dB or less, only + * include the time to get to the late reverb. + */ + const float diffTime{!(decayDiff < 1.0f) ? 0.0f + : (std::log10(decayDiff)*(20.0f / -60.0f) * props.DecayTime)}; - const float decaySamples{(props->Reverb.ReflectionsDelay + props->Reverb.LateReverbDelay - + diffTime) * frequency}; - /* Limit to 100,000 samples (a touch over 2 seconds at 48khz) to - * avoid excessive double-processing. - */ - pipeline.mFadeSampleCount = static_cast(minf(decaySamples, 100'000.0f)); - } - else - { - /* Otherwise, if the late reverb already starts at -60dB or less, only - * include the time to get to the late reverb. - */ - const float decaySamples{(props->Reverb.ReflectionsDelay + props->Reverb.LateReverbDelay) - * frequency}; - pipeline.mFadeSampleCount = static_cast(minf(decaySamples, 100'000.0f)); - } + const float decaySamples{(props.ReflectionsDelay+props.LateReverbDelay+diffTime) + * frequency}; + /* Limit to 100,000 samples (a touch over 2 seconds at 48khz) to avoid + * excessive double-processing. + */ + pipeline.mFadeSampleCount = static_cast(std::min(decaySamples, 100'000.0f)); } @@ -1322,35 +1359,34 @@ void ReverbState::update(const ContextBase *Context, const EffectSlot *Slot, * Where D is a diagonal matrix (of x), and S is a triangular matrix (of y) * whose combination of signs are being iterated. */ -inline auto VectorPartialScatter(const std::array &RESTRICT in, - const float xCoeff, const float yCoeff) -> std::array +inline auto VectorPartialScatter(const std::array &in, const float xCoeff, + const float yCoeff) noexcept -> std::array { - return std::array{{ + return std::array{ xCoeff*in[0] + yCoeff*( in[1] + -in[2] + in[3]), xCoeff*in[1] + yCoeff*(-in[0] + in[2] + in[3]), xCoeff*in[2] + yCoeff*( in[0] + -in[1] + in[3]), xCoeff*in[3] + yCoeff*(-in[0] + -in[1] + -in[2] ) - }}; + }; } -/* Utilizes the above, but reverses the input channels. */ -void VectorScatterRevDelayIn(const DelayLineI delay, size_t offset, const float xCoeff, - const float yCoeff, const al::span in, const size_t count) +/* Utilizes the above, but also applies a line-based reflection on the input + * channels (swapping 0<->3 and 1<->2). + */ +void VectorScatterRev(const float xCoeff, const float yCoeff, + const al::span samples, const size_t count) noexcept { ASSUME(count > 0); - for(size_t i{0u};i < count;) + for(size_t i{0u};i < count;++i) { - offset &= delay.Mask; - size_t td{minz(delay.Mask+1 - offset, count-i)}; - do { - std::array f; - for(size_t j{0u};j < NUM_LINES;j++) - f[NUM_LINES-1-j] = in[j][i]; - ++i; + std::array src{samples[0][i], samples[1][i], samples[2][i], samples[3][i]}; - delay.Line[offset++] = VectorPartialScatter(f, xCoeff, yCoeff); - } while(--td); + src = VectorPartialScatter(std::array{src[3], src[2], src[1], src[0]}, xCoeff, yCoeff); + samples[0][i] = src[0]; + samples[1][i] = src[1]; + samples[2][i] = src[2]; + samples[3][i] = src[3]; } } @@ -1360,167 +1396,229 @@ void VectorScatterRevDelayIn(const DelayLineI delay, size_t offset, const float * It works by vectorizing a regular all-pass filter and replacing the delay * element with a scattering matrix (like the one above) and a diagonal * matrix of delay elements. - * - * Two static specializations are used for transitional (cross-faded) delay - * line processing and non-transitional processing. */ -void VecAllpass::process(const al::span samples, size_t offset, - const float xCoeff, const float yCoeff, const size_t todo) +void VecAllpass::process(const al::span samples, size_t main_offset, + const float xCoeff, const float yCoeff, const size_t todo) const noexcept { - const DelayLineI delay{Delay}; + const auto linelen = size_t{Delay.mLine.size()/NUM_LINES}; const float feedCoeff{Coeff}; ASSUME(todo > 0); - size_t vap_offset[NUM_LINES]; - for(size_t j{0u};j < NUM_LINES;j++) - vap_offset[j] = offset - Offset[j]; for(size_t i{0u};i < todo;) { - for(size_t j{0u};j < NUM_LINES;j++) - vap_offset[j] &= delay.Mask; - offset &= delay.Mask; + std::array vap_offset{}; + std::transform(Offset.cbegin(), Offset.cend(), vap_offset.begin(), + [main_offset,mask=linelen-1](const size_t delay) noexcept -> size_t + { return (main_offset-delay) & mask; }); + main_offset &= linelen-1; + + const auto maxoff = std::accumulate(vap_offset.cbegin(), vap_offset.cend(), main_offset, + [](const size_t offset, const size_t apoffset) { return std::max(offset, apoffset); }); + size_t td{std::min(linelen - maxoff, todo - i)}; - size_t maxoff{offset}; - for(size_t j{0u};j < NUM_LINES;j++) - maxoff = maxz(maxoff, vap_offset[j]); - size_t td{minz(delay.Mask+1 - maxoff, todo - i)}; + auto delayIn = Delay.mLine.begin(); + auto delayOut = Delay.mLine.begin() + ptrdiff_t(main_offset*NUM_LINES); + main_offset += td; do { - std::array f; + std::array f{}; for(size_t j{0u};j < NUM_LINES;j++) { const float input{samples[j][i]}; - const float out{delay.Line[vap_offset[j]++][j] - feedCoeff*input}; + const float out{delayIn[vap_offset[j]*NUM_LINES + j] - feedCoeff*input}; f[j] = input + feedCoeff*out; samples[j][i] = out; } + delayIn += NUM_LINES; ++i; - delay.Line[offset++] = VectorPartialScatter(f, xCoeff, yCoeff); + f = VectorPartialScatter(f, xCoeff, yCoeff); + delayOut = std::copy_n(f.cbegin(), f.size(), delayOut); } while(--td); } } +/* This applies a more typical all-pass to each line, without the scattering + * matrix. + */ +void Allpass4::process(const al::span samples, const size_t offset, + const size_t todo) const noexcept +{ + const DelayLineU delay{Delay}; + const float feedCoeff{Coeff}; + + ASSUME(todo > 0); + + for(size_t j{0u};j < NUM_LINES;j++) + { + auto smpiter = samples[j].begin(); + const auto buffer = delay.get(j); + size_t dstoffset{offset}; + size_t vap_offset{offset - Offset[j]}; + for(size_t i{0u};i < todo;) + { + vap_offset &= buffer.size()-1; + dstoffset &= buffer.size()-1; + + const size_t maxoff{std::max(dstoffset, vap_offset)}; + const size_t td{std::min(buffer.size() - maxoff, todo - i)}; + + auto proc_sample = [buffer,feedCoeff,&vap_offset,&dstoffset](const float x) -> float + { + const float y{buffer[vap_offset++] - feedCoeff*x}; + buffer[dstoffset++] = x + feedCoeff*y; + return y; + }; + smpiter = std::transform(smpiter, smpiter+td, smpiter, proc_sample); + i += td; + } + } +} + + /* This generates early reflections. * * This is done by obtaining the primary reflections (those arriving from the * same direction as the source) from the main delay line. These are * attenuated and all-pass filtered (based on the diffusion parameter). * - * The early lines are then fed in reverse (according to the approximately - * opposite spatial location of the A-Format lines) to create the secondary + * The early lines are then reflected about the origin to create the secondary * reflections (those arriving from the opposite direction as the source). * * The early response is then completed by combining the primary reflections * with the delayed and attenuated output from the early lines. * - * Finally, the early response is reversed, scattered (based on diffusion), + * Finally, the early response is reflected, scattered (based on diffusion), * and fed into the late reverb section of the main delay line. */ -void ReverbPipeline::processEarly(size_t offset, const size_t samplesToDo, - const al::span tempSamples, +void ReverbPipeline::processEarly(const DelayLineU &main_delay, size_t offset, + const size_t samplesToDo, const al::span tempSamples, const al::span outSamples) { - const DelayLineI early_delay{mEarly.Delay}; - const DelayLineI in_delay{mEarlyDelayIn}; + const DelayLineU early_delay{mEarly.Delay}; + const DelayLineU in_delay{main_delay}; const float mixX{mMixX}; const float mixY{mMixY}; - ASSUME(samplesToDo > 0); + ASSUME(samplesToDo <= BufferLineSize); for(size_t base{0};base < samplesToDo;) { - const size_t todo{minz(samplesToDo-base, MAX_UPDATE_SAMPLES)}; + const size_t todo{std::min(samplesToDo-base, MAX_UPDATE_SAMPLES)}; /* First, load decorrelated samples from the main delay line as the * primary reflections. */ - const float fadeStep{1.0f / static_cast(todo)}; - for(size_t j{0u};j < NUM_LINES;j++) + const auto fadeStep = 1.0f / static_cast(todo); + const auto earlycoeff0 = float{mEarlyDelayCoeff[0]}; + const auto earlycoeff1 = float{mEarlyDelayCoeff[1]}; + mEarlyDelayCoeff[0] = mEarlyDelayCoeff[1]; + for(size_t j{0_uz};j < NUM_LINES;j++) { - size_t early_delay_tap0{offset - mEarlyDelayTap[j][0]}; - size_t early_delay_tap1{offset - mEarlyDelayTap[j][1]}; - const float coeff{mEarlyDelayCoeff[j]}; - const float coeffStep{early_delay_tap0 != early_delay_tap1 ? coeff*fadeStep : 0.0f}; - float fadeCount{0.0f}; + const auto input = in_delay.get(j); + auto early_delay_tap0 = size_t{offset - mEarlyDelayTap[j][0]}; + auto early_delay_tap1 = size_t{offset - mEarlyDelayTap[j][1]}; + mEarlyDelayTap[j][0] = mEarlyDelayTap[j][1]; + auto fadeCount = 0.0f; - for(size_t i{0u};i < todo;) + auto tmp = tempSamples[j].begin(); + for(size_t i{0_uz};i < todo;) { - early_delay_tap0 &= in_delay.Mask; - early_delay_tap1 &= in_delay.Mask; - const size_t max_tap{maxz(early_delay_tap0, early_delay_tap1)}; - size_t td{minz(in_delay.Mask+1 - max_tap, todo-i)}; - do { - const float fade0{coeff - coeffStep*fadeCount}; - const float fade1{coeffStep*fadeCount}; + early_delay_tap0 &= input.size()-1; + early_delay_tap1 &= input.size()-1; + const auto max_tap = size_t{std::max(early_delay_tap0, early_delay_tap1)}; + const auto td = size_t{std::min(input.size()-max_tap, todo-i)}; + const auto intap0 = input.subspan(early_delay_tap0, td); + const auto intap1 = input.subspan(early_delay_tap1, td); + + auto do_blend = [earlycoeff0,earlycoeff1,fadeStep,&fadeCount](const float in0, + const float in1) noexcept -> float + { + const auto ret = lerpf(in0*earlycoeff0, in1*earlycoeff1, fadeStep*fadeCount); fadeCount += 1.0f; - tempSamples[j][i++] = in_delay.Line[early_delay_tap0++][j]*fade0 + - in_delay.Line[early_delay_tap1++][j]*fade1; - } while(--td); + return ret; + }; + tmp = std::transform(intap0.begin(), intap0.end(), intap1.begin(), tmp, do_blend); + early_delay_tap0 += td; + early_delay_tap1 += td; + i += td; } - mEarlyDelayTap[j][0] = mEarlyDelayTap[j][1]; + /* Band-pass the incoming samples. */ + auto&& filter = DualBiquad{mFilter[j].Lp, mFilter[j].Hp}; + filter.process(al::span{tempSamples[j]}.first(todo), tempSamples[j]); } - /* Apply a vector all-pass, to help color the initial reflections based - * on the diffusion strength. - */ - mEarly.VecAp.process(tempSamples, offset, mixX, mixY, todo); + /* Apply an all-pass, to help color the initial reflections. */ + mEarly.VecAp.process(tempSamples, offset, todo); - /* Apply a delay and bounce to generate secondary reflections, combine - * with the primary reflections and write out the result for mixing. - */ - for(size_t j{0u};j < NUM_LINES;j++) - early_delay.write(offset, NUM_LINES-1-j, tempSamples[j].data(), todo); - for(size_t j{0u};j < NUM_LINES;j++) + /* Apply a delay and bounce to generate secondary reflections. */ + early_delay.writeReflected(offset, tempSamples, todo); + const auto feedb_coeff = mEarly.Coeff; + for(size_t j{0_uz};j < NUM_LINES;j++) { - size_t feedb_tap{offset - mEarly.Offset[j]}; - const float feedb_coeff{mEarly.Coeff[j]}; - float *RESTRICT out{al::assume_aligned<16>(outSamples[j].data() + base)}; + const auto input = early_delay.get(j); + auto feedb_tap = size_t{offset - mEarly.Offset[j]}; + auto out = outSamples[j].begin() + base; + auto tmp = tempSamples[j].begin(); - for(size_t i{0u};i < todo;) + for(size_t i{0_uz};i < todo;) { - feedb_tap &= early_delay.Mask; - size_t td{minz(early_delay.Mask+1 - feedb_tap, todo - i)}; - do { - tempSamples[j][i] += early_delay.Line[feedb_tap++][j]*feedb_coeff; - out[i] = tempSamples[j][i]; - ++i; - } while(--td); + feedb_tap &= input.size()-1; + + const auto td = size_t{std::min(input.size() - feedb_tap, todo - i)}; + const auto delaySrc = input.subspan(feedb_tap, td); + + /* Combine the main input with the attenuated delayed echo for + * the early output. + */ + out = std::transform(delaySrc.begin(), delaySrc.end(), tmp, out, + [feedb_coeff](const float delayspl, const float mainspl) noexcept -> float + { return delayspl*feedb_coeff + mainspl; }); + + /* Move the (non-attenuated) delayed echo to the temp buffer + * for feeding the late reverb. + */ + tmp = std::copy_n(delaySrc.begin(), delaySrc.size(), tmp); + feedb_tap += td; + i += td; } } - /* Finally, write the result to the late delay line input for the late - * reverb stage to pick up at the appropriate time, applying a scatter - * and bounce to improve the initial diffusion in the late reverb. + /* Finally, apply a scatter and bounce to improve the initial diffusion + * in the late reverb, writing the result to the late delay line input. */ - VectorScatterRevDelayIn(mLateDelayIn, offset, mixX, mixY, tempSamples, todo); + VectorScatterRev(mixX, mixY, tempSamples, todo); + for(size_t j{0_uz};j < NUM_LINES;j++) + mLateDelayIn.write(offset, j, al::span{tempSamples[j]}.first(todo)); base += todo; offset += todo; } } -void Modulation::calcDelays(size_t todo) +auto Modulation::calcDelays(size_t todo) -> al::span { - uint idx{Index}; - const uint step{Step}; - const float depth{Depth}; - for(size_t i{0};i < todo;++i) + auto idx = Index; + const auto step = Step; + const auto depth = Depth * float{gCubicTable.sTableSteps}; + const auto delays = al::span{ModDelays}.first(todo); + std::generate(delays.begin(), delays.end(), [step,depth,&idx] { idx += step; - const float x{static_cast(idx&MOD_FRACMASK) * (1.0f/MOD_FRACONE)}; + const auto x = static_cast(idx&MOD_FRACMASK) * (1.0f/MOD_FRACONE); /* Approximate sin(x*2pi). As long as it roughly fits a sinusoid shape * and stays within [-1...+1], it needn't be perfect. */ - const float lfo{!(idx&(MOD_FRACONE>>1)) + const auto lfo = !(idx&(MOD_FRACONE>>1)) ? ((-16.0f * x * x) + (8.0f * x)) - : ((16.0f * x * x) + (-8.0f * x) + (-16.0f * x) + 8.0f)}; - ModDelays[i] = (lfo+1.0f) * depth; - } + : ((16.0f * x * x) + (-8.0f * x) + (-16.0f * x) + 8.0f); + return float2uint((lfo+1.0f) * depth); + }); Index = idx; + return delays; } @@ -1539,88 +1637,105 @@ void ReverbPipeline::processLate(size_t offset, const size_t samplesToDo, const al::span tempSamples, const al::span outSamples) { - const DelayLineI late_delay{mLate.Delay}; - const DelayLineI in_delay{mLateDelayIn}; + const DelayLineU late_delay{mLate.Delay}; + const DelayLineU in_delay{mLateDelayIn}; const float mixX{mMixX}; const float mixY{mMixY}; - ASSUME(samplesToDo > 0); + ASSUME(samplesToDo <= BufferLineSize); for(size_t base{0};base < samplesToDo;) { - const size_t todo{minz(samplesToDo-base, minz(mLate.Offset[0], MAX_UPDATE_SAMPLES))}; + const size_t todo{std::min(std::min(mLate.Offset[0], MAX_UPDATE_SAMPLES), + samplesToDo-base)}; ASSUME(todo > 0); /* First, calculate the modulated delays for the late feedback. */ - mLate.Mod.calcDelays(todo); + const auto delays = mLate.Mod.calcDelays(todo); - /* Next, load decorrelated samples from the main and feedback delay - * lines. Filter the signal to apply its frequency-dependent decay. + /* Now load samples from the feedback delay lines. Filter the signal to + * apply its frequency-dependent decay. */ + for(size_t j{0_uz};j < NUM_LINES;++j) + { + const auto input = late_delay.get(j); + const auto midGain = mLate.T60[j].MidGain; + auto late_feedb_tap = size_t{offset - mLate.Offset[j]}; + + auto proc_sample = [input,midGain,&late_feedb_tap](const size_t idelay) -> float + { + /* Calculate the read sample offset and sub-sample offset + * between it and the next sample. + */ + const auto delay = late_feedb_tap - (idelay>>gCubicTable.sTableBits); + const auto delayoffset = size_t{idelay & gCubicTable.sTableMask}; + ++late_feedb_tap; + + /* Get the samples around the delayed offset, interpolated for + * output. + */ + const auto out0 = float{input[(delay ) & (input.size()-1)]}; + const auto out1 = float{input[(delay-1) & (input.size()-1)]}; + const auto out2 = float{input[(delay-2) & (input.size()-1)]}; + const auto out3 = float{input[(delay-3) & (input.size()-1)]}; + + const auto out = out0*gCubicTable.getCoeff0(delayoffset) + + out1*gCubicTable.getCoeff1(delayoffset) + + out2*gCubicTable.getCoeff2(delayoffset) + + out3*gCubicTable.getCoeff3(delayoffset); + return out * midGain; + }; + std::transform(delays.begin(), delays.end(), tempSamples[j].begin(), proc_sample); + + mLate.T60[j].process(al::span{tempSamples[j]}.first(todo)); + } + + /* Next load decorrelated samples from the main delay lines. */ const float fadeStep{1.0f / static_cast(todo)}; - for(size_t j{0u};j < NUM_LINES;j++) + for(size_t j{0_uz};j < NUM_LINES;++j) { - size_t late_delay_tap0{offset - mLateDelayTap[j][0]}; - size_t late_delay_tap1{offset - mLateDelayTap[j][1]}; - size_t late_feedb_tap{offset - mLate.Offset[j]}; - const float midGain{mLate.T60[j].MidGain}; - const float densityGain{mLate.DensityGain * midGain}; - const float densityStep{late_delay_tap0 != late_delay_tap1 ? - densityGain*fadeStep : 0.0f}; - float fadeCount{0.0f}; + const auto input = in_delay.get(j); + auto late_delay_tap0 = size_t{offset - mLateDelayTap[j][0]}; + auto late_delay_tap1 = size_t{offset - mLateDelayTap[j][1]}; + mLateDelayTap[j][0] = mLateDelayTap[j][1]; + const auto densityGain = mLate.DensityGain; + const auto densityStep = late_delay_tap0 != late_delay_tap1 + ? densityGain*fadeStep : 0.0f; + auto fadeCount = 0.0f; + auto samples = tempSamples[j].begin(); for(size_t i{0u};i < todo;) { - late_delay_tap0 &= in_delay.Mask; - late_delay_tap1 &= in_delay.Mask; - size_t td{minz(todo-i, in_delay.Mask+1 - maxz(late_delay_tap0, late_delay_tap1))}; - do { - /* Calculate the read offset and offset between it and the - * next sample. - */ - const float fdelay{mLate.Mod.ModDelays[i]}; - const size_t idelay{float2uint(fdelay * float{gCubicTable.sTableSteps})}; - const size_t delay{late_feedb_tap - (idelay>>gCubicTable.sTableBits)}; - const size_t delayoffset{idelay & gCubicTable.sTableMask}; - ++late_feedb_tap; - - /* Get the samples around by the delayed offset. */ - const float out0{late_delay.Line[(delay ) & late_delay.Mask][j]}; - const float out1{late_delay.Line[(delay-1) & late_delay.Mask][j]}; - const float out2{late_delay.Line[(delay-2) & late_delay.Mask][j]}; - const float out3{late_delay.Line[(delay-3) & late_delay.Mask][j]}; - - /* The output is obtained by interpolating the four samples - * that were acquired above, and combined with the main - * delay tap. - */ - const float out{out0*gCubicTable.getCoeff0(delayoffset) - + out1*gCubicTable.getCoeff1(delayoffset) - + out2*gCubicTable.getCoeff2(delayoffset) - + out3*gCubicTable.getCoeff3(delayoffset)}; - const float fade0{densityGain - densityStep*fadeCount}; - const float fade1{densityStep*fadeCount}; + late_delay_tap0 &= input.size()-1; + late_delay_tap1 &= input.size()-1; + const auto td = size_t{std::min(todo - i, + input.size() - std::max(late_delay_tap0, late_delay_tap1))}; + + auto proc_sample = [input,densityGain,densityStep,&late_delay_tap0, + &late_delay_tap1,&fadeCount](const float sample) noexcept -> float + { + const auto fade0 = float{densityGain - densityStep*fadeCount}; + const auto fade1 = float{densityStep*fadeCount}; fadeCount += 1.0f; - tempSamples[j][i] = out*midGain + - in_delay.Line[late_delay_tap0++][j]*fade0 + - in_delay.Line[late_delay_tap1++][j]*fade1; - ++i; - } while(--td); + return input[late_delay_tap0++]*fade0 + input[late_delay_tap1++]*fade1 + + sample; + }; + samples = std::transform(samples, samples+ptrdiff_t(td), samples, proc_sample); + i += td; } - mLateDelayTap[j][0] = mLateDelayTap[j][1]; - - mLate.T60[j].process({tempSamples[j].data(), todo}); } /* Apply a vector all-pass to improve micro-surface diffusion, and * write out the results for mixing. */ mLate.VecAp.process(tempSamples, offset, mixX, mixY, todo); - for(size_t j{0u};j < NUM_LINES;j++) + for(size_t j{0_uz};j < NUM_LINES;++j) std::copy_n(tempSamples[j].begin(), todo, outSamples[j].begin()+base); /* Finally, scatter and bounce the results to refeed the feedback buffer. */ - VectorScatterRevDelayIn(late_delay, offset, mixX, mixY, tempSamples, todo); + VectorScatterRev(mixX, mixY, tempSamples, todo); + for(size_t j{0_uz};j < NUM_LINES;++j) + late_delay.write(offset, j, al::span{tempSamples[j]}.first(todo)); base += todo; offset += todo; @@ -1631,110 +1746,34 @@ void ReverbState::process(const size_t samplesToDo, const al::span 0); + ASSUME(samplesToDo <= BufferLineSize); - auto &oldpipeline = mPipelines[mCurrentPipeline^1]; + auto &oldpipeline = mPipelines[!mCurrentPipeline]; auto &pipeline = mPipelines[mCurrentPipeline]; - if(mPipelineState >= Fading) + /* Convert B-Format to A-Format for processing. */ + const size_t numInput{std::min(samplesIn.size(), NUM_LINES)}; + const al::span tmpspan{al::assume_aligned<16>(mTempLine.data()), samplesToDo}; + for(size_t c{0u};c < NUM_LINES;++c) { - /* Convert B-Format to A-Format for processing. */ - const size_t numInput{minz(samplesIn.size(), NUM_LINES)}; - const al::span tmpspan{al::assume_aligned<16>(mTempLine.data()), samplesToDo}; - for(size_t c{0u};c < NUM_LINES;c++) + std::fill(tmpspan.begin(), tmpspan.end(), 0.0f); + for(size_t i{0};i < numInput;++i) { - std::fill(tmpspan.begin(), tmpspan.end(), 0.0f); - for(size_t i{0};i < numInput;++i) - { - const float gain{B2A[c][i]}; - const float *RESTRICT input{al::assume_aligned<16>(samplesIn[i].data())}; - - auto mix_sample = [gain](const float sample, const float in) noexcept -> float - { return sample + in*gain; }; - std::transform(tmpspan.begin(), tmpspan.end(), input, tmpspan.begin(), - mix_sample); - } + const float gain{B2A[c][i]}; - /* Band-pass the incoming samples and feed the initial delay line. */ - auto&& filter = DualBiquad{pipeline.mFilter[c].Lp, pipeline.mFilter[c].Hp}; - filter.process(tmpspan, tmpspan.data()); - pipeline.mEarlyDelayIn.write(offset, c, tmpspan.cbegin(), samplesToDo); + auto mix_sample = [gain](const float sample, const float in) noexcept -> float + { return sample + in*gain; }; + std::transform(tmpspan.begin(), tmpspan.end(), samplesIn[i].begin(), tmpspan.begin(), + mix_sample); } - if(mPipelineState == Fading) - { - /* Give the old pipeline silence if it's still fading out. */ - for(size_t c{0u};c < NUM_LINES;c++) - { - std::fill(tmpspan.begin(), tmpspan.end(), 0.0f); - auto&& filter = DualBiquad{oldpipeline.mFilter[c].Lp, oldpipeline.mFilter[c].Hp}; - filter.process(tmpspan, tmpspan.data()); - oldpipeline.mEarlyDelayIn.write(offset, c, tmpspan.cbegin(), samplesToDo); - } - } + mMainDelay.write(offset, c, tmpspan); } - else - { - /* At the start of a fade, fade in input for the current pipeline, and - * fade out input for the old pipeline. - */ - const size_t numInput{minz(samplesIn.size(), NUM_LINES)}; - const al::span tmpspan{al::assume_aligned<16>(mTempLine.data()), samplesToDo}; - const float fadeStep{1.0f / static_cast(samplesToDo)}; - - for(size_t c{0u};c < NUM_LINES;c++) - { - std::fill(tmpspan.begin(), tmpspan.end(), 0.0f); - for(size_t i{0};i < numInput;++i) - { - const float gain{B2A[c][i]}; - const float *RESTRICT input{al::assume_aligned<16>(samplesIn[i].data())}; - - auto mix_sample = [gain](const float sample, const float in) noexcept -> float - { return sample + in*gain; }; - std::transform(tmpspan.begin(), tmpspan.end(), input, tmpspan.begin(), - mix_sample); - } - float stepCount{0.0f}; - for(float &sample : tmpspan) - { - stepCount += 1.0f; - sample *= stepCount*fadeStep; - } - - auto&& filter = DualBiquad{pipeline.mFilter[c].Lp, pipeline.mFilter[c].Hp}; - filter.process(tmpspan, tmpspan.data()); - pipeline.mEarlyDelayIn.write(offset, c, tmpspan.cbegin(), samplesToDo); - } - for(size_t c{0u};c < NUM_LINES;c++) - { - std::fill(tmpspan.begin(), tmpspan.end(), 0.0f); - for(size_t i{0};i < numInput;++i) - { - const float gain{B2A[c][i]}; - const float *RESTRICT input{al::assume_aligned<16>(samplesIn[i].data())}; - auto mix_sample = [gain](const float sample, const float in) noexcept -> float - { return sample + in*gain; }; - std::transform(tmpspan.begin(), tmpspan.end(), input, tmpspan.begin(), - mix_sample); - } - float stepCount{0.0f}; - for(float &sample : tmpspan) - { - stepCount += 1.0f; - sample *= 1.0f - stepCount*fadeStep; - } - - auto&& filter = DualBiquad{oldpipeline.mFilter[c].Lp, oldpipeline.mFilter[c].Hp}; - filter.process(tmpspan, tmpspan.data()); - oldpipeline.mEarlyDelayIn.write(offset, c, tmpspan.cbegin(), samplesToDo); - } - mPipelineState = Fading; - } + mPipelineState = std::max(Fading, mPipelineState); /* Process reverb for these samples. and mix them to the output. */ - pipeline.processEarly(offset, samplesToDo, mTempSamples, mEarlySamples); + pipeline.processEarly(mMainDelay, offset, samplesToDo, mTempSamples, mEarlySamples); pipeline.processLate(offset, samplesToDo, mTempSamples, mLateSamples); mixOut(pipeline, samplesOut, samplesToDo); @@ -1743,9 +1782,9 @@ void ReverbState::process(const size_t samplesToDo, const al::span= oldpipeline.mFadeSampleCount) { - for(auto &gains : oldpipeline.mEarly.TargetGains) - std::fill(std::begin(gains), std::end(gains), 0.0f); - for(auto &gains : oldpipeline.mLate.TargetGains) - std::fill(std::begin(gains), std::end(gains), 0.0f); + for(auto &gains : oldpipeline.mEarly.Gains) + std::fill(gains.Target.begin(), gains.Target.end(), 0.0f); + for(auto &gains : oldpipeline.mLate.Gains) + std::fill(gains.Target.begin(), gains.Target.end(), 0.0f); oldpipeline.mFadeSampleCount = 0; mPipelineState = Cleanup; } @@ -1770,7 +1809,7 @@ void ReverbState::process(const size_t samplesToDo, const al::span{new ReverbState{}}; } }; -struct StdReverbStateFactory final : public EffectStateFactory { - al::intrusive_ptr create() override - { return al::intrusive_ptr{new ReverbState{}}; } -}; - } // namespace EffectStateFactory *ReverbStateFactory_getFactory() @@ -1797,9 +1831,3 @@ EffectStateFactory *ReverbStateFactory_getFactory() static ReverbStateFactory ReverbFactory{}; return &ReverbFactory; } - -EffectStateFactory *StdReverbStateFactory_getFactory() -{ - static StdReverbStateFactory ReverbFactory{}; - return &ReverbFactory; -} diff --git a/3rdparty/openal/alc/effects/vmorpher.cpp b/3rdparty/openal/alc/effects/vmorpher.cpp index 872c7add1698..d9141bb6edcd 100644 --- a/3rdparty/openal/alc/effects/vmorpher.cpp +++ b/3rdparty/openal/alc/effects/vmorpher.cpp @@ -34,68 +34,69 @@ #include #include +#include #include #include -#include +#include #include "alc/effects/base.h" -#include "almalloc.h" #include "alnumbers.h" #include "alnumeric.h" #include "alspan.h" #include "core/ambidefs.h" #include "core/bufferline.h" #include "core/context.h" -#include "core/devformat.h" #include "core/device.h" +#include "core/effects/base.h" #include "core/effectslot.h" #include "core/mixer.h" #include "intrusive_ptr.h" +struct BufferStorage; namespace { using uint = unsigned int; -#define MAX_UPDATE_SAMPLES 256 -#define NUM_FORMANTS 4 -#define NUM_FILTERS 2 -#define Q_FACTOR 5.0f - -#define VOWEL_A_INDEX 0 -#define VOWEL_B_INDEX 1 +constexpr size_t MaxUpdateSamples{256}; +constexpr size_t NumFormants{4}; +constexpr float RcpQFactor{1.0f / 5.0f}; +enum : size_t { + VowelAIndex, + VowelBIndex, + NumFilters +}; -#define WAVEFORM_FRACBITS 24 -#define WAVEFORM_FRACONE (1<*2.0f / WAVEFORM_FRACONE}; + constexpr float scale{al::numbers::pi_v*2.0f / float{WaveformFracOne}}; return std::sin(static_cast(index) * scale)*0.5f + 0.5f; } inline float Saw(uint index) -{ return static_cast(index) / float{WAVEFORM_FRACONE}; } +{ return static_cast(index) / float{WaveformFracOne}; } inline float Triangle(uint index) -{ return std::fabs(static_cast(index)*(2.0f/WAVEFORM_FRACONE) - 1.0f); } +{ return std::fabs(static_cast(index)*(2.0f/WaveformFracOne) - 1.0f); } inline float Half(uint) { return 0.5f; } template -void Oscillate(float *RESTRICT dst, uint index, const uint step, size_t todo) +void Oscillate(const al::span dst, uint index, const uint step) { - for(size_t i{0u};i < todo;i++) + std::generate(dst.begin(), dst.end(), [&index,step] { index += step; - index &= WAVEFORM_FRACMASK; - dst[i] = func(index); - } + index &= WaveformFracMask; + return func(index); + }); } -struct FormantFilter -{ +struct FormantFilter { float mCoeff{0.0f}; float mGain{1.0f}; float mS1{0.0f}; @@ -106,34 +107,38 @@ struct FormantFilter : mCoeff{std::tan(al::numbers::pi_v * f0norm)}, mGain{gain} { } - inline void process(const float *samplesIn, float *samplesOut, const size_t numInput) + void process(const float *samplesIn, float *samplesOut, const size_t numInput) noexcept { /* A state variable filter from a topology-preserving transform. * Based on a talk given by Ivan Cohen: https://www.youtube.com/watch?v=esjHXGPyrhg */ const float g{mCoeff}; const float gain{mGain}; - const float h{1.0f / (1.0f + (g/Q_FACTOR) + (g*g))}; + const float h{1.0f / (1.0f + (g*RcpQFactor) + (g*g))}; + const float coeff{RcpQFactor + g}; float s1{mS1}; float s2{mS2}; - for(size_t i{0u};i < numInput;i++) - { - const float H{(samplesIn[i] - (1.0f/Q_FACTOR + g)*s1 - s2)*h}; - const float B{g*H + s1}; - const float L{g*B + s2}; + const auto input = al::span{samplesIn, numInput}; + const auto output = al::span{samplesOut, numInput}; + std::transform(input.cbegin(), input.cend(), output.cbegin(), output.begin(), + [g,gain,h,coeff,&s1,&s2](const float in, const float out) noexcept -> float + { + const float H{(in - coeff*s1 - s2)*h}; + const float B{g*H + s1}; + const float L{g*B + s2}; - s1 = g*H + B; - s2 = g*B + L; + s1 = g*H + B; + s2 = g*B + L; - // Apply peak and accumulate samples. - samplesOut[i] += B * gain; - } + // Apply peak and accumulate samples. + return out + B*gain; + }); mS1 = s1; mS2 = s2; } - inline void clear() + void clear() noexcept { mS1 = 0.0f; mS2 = 0.0f; @@ -142,26 +147,27 @@ struct FormantFilter struct VmorpherState final : public EffectState { - struct { + struct OutParams { uint mTargetChannel{InvalidChannelIndex}; /* Effect parameters */ - FormantFilter mFormants[NUM_FILTERS][NUM_FORMANTS]; + std::array,NumFilters> mFormants; /* Effect gains for each channel */ float mCurrentGain{}; float mTargetGain{}; - } mChans[MaxAmbiChannels]; + }; + std::array mChans; - void (*mGetSamples)(float*RESTRICT, uint, const uint, size_t){}; + void (*mGetSamples)(const al::span dst, uint index, const uint step){}; uint mIndex{0}; uint mStep{1}; /* Effects buffers */ - alignas(16) float mSampleBufferA[MAX_UPDATE_SAMPLES]{}; - alignas(16) float mSampleBufferB[MAX_UPDATE_SAMPLES]{}; - alignas(16) float mLfo[MAX_UPDATE_SAMPLES]{}; + alignas(16) std::array mSampleBufferA{}; + alignas(16) std::array mSampleBufferB{}; + alignas(16) std::array mLfo{}; void deviceUpdate(const DeviceBase *device, const BufferStorage *buffer) override; void update(const ContextBase *context, const EffectSlot *slot, const EffectProps *props, @@ -169,14 +175,12 @@ struct VmorpherState final : public EffectState { void process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) override; - static std::array getFiltersByPhoneme(VMorpherPhenome phoneme, - float frequency, float pitch); - - DEF_NEWDEL(VmorpherState) + static std::array getFiltersByPhoneme(VMorpherPhenome phoneme, + float frequency, float pitch) noexcept; }; -std::array VmorpherState::getFiltersByPhoneme(VMorpherPhenome phoneme, - float frequency, float pitch) +std::array VmorpherState::getFiltersByPhoneme(VMorpherPhenome phoneme, + float frequency, float pitch) noexcept { /* Using soprano formant set of values to * better match mid-range frequency space. @@ -232,44 +236,43 @@ void VmorpherState::deviceUpdate(const DeviceBase*, const BufferStorage*) for(auto &e : mChans) { e.mTargetChannel = InvalidChannelIndex; - std::for_each(std::begin(e.mFormants[VOWEL_A_INDEX]), std::end(e.mFormants[VOWEL_A_INDEX]), + std::for_each(e.mFormants[VowelAIndex].begin(), e.mFormants[VowelAIndex].end(), std::mem_fn(&FormantFilter::clear)); - std::for_each(std::begin(e.mFormants[VOWEL_B_INDEX]), std::end(e.mFormants[VOWEL_B_INDEX]), + std::for_each(e.mFormants[VowelBIndex].begin(), e.mFormants[VowelBIndex].end(), std::mem_fn(&FormantFilter::clear)); e.mCurrentGain = 0.0f; } } void VmorpherState::update(const ContextBase *context, const EffectSlot *slot, - const EffectProps *props, const EffectTarget target) + const EffectProps *props_, const EffectTarget target) { + auto &props = std::get(*props_); const DeviceBase *device{context->mDevice}; - const float frequency{static_cast(device->Frequency)}; - const float step{props->Vmorpher.Rate / frequency}; - mStep = fastf2u(clampf(step*WAVEFORM_FRACONE, 0.0f, float{WAVEFORM_FRACONE-1})); + const float frequency{static_cast(device->mSampleRate)}; + const float step{props.Rate / frequency}; + mStep = fastf2u(std::clamp(step*WaveformFracOne, 0.0f, WaveformFracOne-1.0f)); if(mStep == 0) mGetSamples = Oscillate; - else if(props->Vmorpher.Waveform == VMorpherWaveform::Sinusoid) + else if(props.Waveform == VMorpherWaveform::Sinusoid) mGetSamples = Oscillate; - else if(props->Vmorpher.Waveform == VMorpherWaveform::Triangle) + else if(props.Waveform == VMorpherWaveform::Triangle) mGetSamples = Oscillate; - else /*if(props->Vmorpher.Waveform == VMorpherWaveform::Sawtooth)*/ + else /*if(props.Waveform == VMorpherWaveform::Sawtooth)*/ mGetSamples = Oscillate; - const float pitchA{std::pow(2.0f, - static_cast(props->Vmorpher.PhonemeACoarseTuning) / 12.0f)}; - const float pitchB{std::pow(2.0f, - static_cast(props->Vmorpher.PhonemeBCoarseTuning) / 12.0f)}; + const float pitchA{std::pow(2.0f, static_cast(props.PhonemeACoarseTuning) / 12.0f)}; + const float pitchB{std::pow(2.0f, static_cast(props.PhonemeBCoarseTuning) / 12.0f)}; - auto vowelA = getFiltersByPhoneme(props->Vmorpher.PhonemeA, frequency, pitchA); - auto vowelB = getFiltersByPhoneme(props->Vmorpher.PhonemeB, frequency, pitchB); + auto vowelA = getFiltersByPhoneme(props.PhonemeA, frequency, pitchA); + auto vowelB = getFiltersByPhoneme(props.PhonemeB, frequency, pitchB); /* Copy the filter coefficients to the input channels. */ for(size_t i{0u};i < slot->Wet.Buffer.size();++i) { - std::copy(vowelA.begin(), vowelA.end(), std::begin(mChans[i].mFormants[VOWEL_A_INDEX])); - std::copy(vowelB.begin(), vowelB.end(), std::begin(mChans[i].mFormants[VOWEL_B_INDEX])); + std::copy(vowelA.begin(), vowelA.end(), mChans[i].mFormants[VowelAIndex].begin()); + std::copy(vowelB.begin(), vowelB.end(), mChans[i].mFormants[VowelBIndex].begin()); } mOutTarget = target.Main->Buffer; @@ -283,18 +286,20 @@ void VmorpherState::update(const ContextBase *context, const EffectSlot *slot, void VmorpherState::process(const size_t samplesToDo, const al::span samplesIn, const al::span samplesOut) { + alignas(16) std::array blended{}; + /* Following the EFX specification for a conformant implementation which describes * the effect as a pair of 4-band formant filters blended together using an LFO. */ for(size_t base{0u};base < samplesToDo;) { - const size_t td{minz(MAX_UPDATE_SAMPLES, samplesToDo-base)}; + const size_t td{std::min(MaxUpdateSamples, samplesToDo-base)}; - mGetSamples(mLfo, mIndex, mStep, td); + mGetSamples(al::span{mLfo}.first(td), mIndex, mStep); mIndex += static_cast(mStep * td); - mIndex &= WAVEFORM_FRACMASK; + mIndex &= WaveformFracMask; - auto chandata = std::begin(mChans); + auto chandata = mChans.begin(); for(const auto &input : samplesIn) { const size_t outidx{chandata->mTargetChannel}; @@ -304,30 +309,29 @@ void VmorpherState::process(const size_t samplesToDo, const al::spanmFormants[VOWEL_A_INDEX]; - auto& vowelB = chandata->mFormants[VOWEL_B_INDEX]; + const auto vowelA = al::span{chandata->mFormants[VowelAIndex]}; + const auto vowelB = al::span{chandata->mFormants[VowelBIndex]}; /* Process first vowel. */ - std::fill_n(std::begin(mSampleBufferA), td, 0.0f); - vowelA[0].process(&input[base], mSampleBufferA, td); - vowelA[1].process(&input[base], mSampleBufferA, td); - vowelA[2].process(&input[base], mSampleBufferA, td); - vowelA[3].process(&input[base], mSampleBufferA, td); + std::fill_n(mSampleBufferA.begin(), td, 0.0f); + vowelA[0].process(&input[base], mSampleBufferA.data(), td); + vowelA[1].process(&input[base], mSampleBufferA.data(), td); + vowelA[2].process(&input[base], mSampleBufferA.data(), td); + vowelA[3].process(&input[base], mSampleBufferA.data(), td); /* Process second vowel. */ - std::fill_n(std::begin(mSampleBufferB), td, 0.0f); - vowelB[0].process(&input[base], mSampleBufferB, td); - vowelB[1].process(&input[base], mSampleBufferB, td); - vowelB[2].process(&input[base], mSampleBufferB, td); - vowelB[3].process(&input[base], mSampleBufferB, td); + std::fill_n(mSampleBufferB.begin(), td, 0.0f); + vowelB[0].process(&input[base], mSampleBufferB.data(), td); + vowelB[1].process(&input[base], mSampleBufferB.data(), td); + vowelB[2].process(&input[base], mSampleBufferB.data(), td); + vowelB[3].process(&input[base], mSampleBufferB.data(), td); - alignas(16) float blended[MAX_UPDATE_SAMPLES]; for(size_t i{0u};i < td;i++) blended[i] = lerpf(mSampleBufferA[i], mSampleBufferB[i], mLfo[i]); /* Now, mix the processed sound data to the output. */ - MixSamples({blended, td}, samplesOut[outidx].data()+base, chandata->mCurrentGain, - chandata->mTargetGain, samplesToDo-base); + MixSamples(al::span{blended}.first(td), al::span{samplesOut[outidx]}.subspan(base), + chandata->mCurrentGain, chandata->mTargetGain, samplesToDo-base); ++chandata; } diff --git a/3rdparty/openal/alc/events.cpp b/3rdparty/openal/alc/events.cpp index a80faf8aa075..dfabcf897f0e 100644 --- a/3rdparty/openal/alc/events.cpp +++ b/3rdparty/openal/alc/events.cpp @@ -3,26 +3,15 @@ #include "events.h" -#include - +#include "alnumeric.h" #include "alspan.h" #include "core/logging.h" #include "device.h" +#include "fmt/core.h" namespace { -std::optional GetEventType(ALCenum type) -{ - switch(type) - { - case ALC_EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT: return alc::EventType::DefaultDeviceChanged; - case ALC_EVENT_TYPE_DEVICE_ADDED_SOFT: return alc::EventType::DeviceAdded; - case ALC_EVENT_TYPE_DEVICE_REMOVED_SOFT: return alc::EventType::DeviceRemoved; - } - return std::nullopt; -} - ALCenum EnumFromEventType(const alc::EventType type) { switch(type) @@ -32,13 +21,24 @@ ALCenum EnumFromEventType(const alc::EventType type) case alc::EventType::DeviceRemoved: return ALC_EVENT_TYPE_DEVICE_REMOVED_SOFT; case alc::EventType::Count: break; } - throw std::runtime_error{"Invalid EventType: "+std::to_string(al::to_underlying(type))}; + throw std::runtime_error{fmt::format("Invalid EventType: {}", int{al::to_underlying(type)})}; } } // namespace namespace alc { +std::optional GetEventType(ALCenum type) +{ + switch(type) + { + case ALC_EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT: return alc::EventType::DefaultDeviceChanged; + case ALC_EVENT_TYPE_DEVICE_ADDED_SOFT: return alc::EventType::DeviceAdded; + case ALC_EVENT_TYPE_DEVICE_REMOVED_SOFT: return alc::EventType::DeviceRemoved; + } + return std::nullopt; +} + void Event(EventType eventType, DeviceType deviceType, ALCdevice *device, std::string_view message) noexcept { auto eventlock = std::unique_lock{EventMutex}; @@ -73,10 +73,10 @@ FORCE_ALIGN ALCboolean ALC_APIENTRY alcEventControlSOFT(ALCsizei count, const AL alc::EventBitSet eventSet{0}; for(ALCenum type : al::span{events, static_cast(count)}) { - auto etype = GetEventType(type); + auto etype = alc::GetEventType(type); if(!etype) { - WARN("Invalid event type: 0x%04x\n", type); + WARN("Invalid event type: {:#04x}", as_unsigned(type)); alcSetError(nullptr, ALC_INVALID_ENUM); return ALC_FALSE; } diff --git a/3rdparty/openal/alc/events.h b/3rdparty/openal/alc/events.h index 4acc505df64a..3f53ec76265b 100644 --- a/3rdparty/openal/alc/events.h +++ b/3rdparty/openal/alc/events.h @@ -6,6 +6,7 @@ #include #include +#include #include @@ -19,6 +20,13 @@ enum class EventType : uint8_t { Count }; +std::optional GetEventType(ALCenum type); + +enum class EventSupport : ALCenum { + FullSupport = ALC_EVENT_SUPPORTED_SOFT, + NoSupport = ALC_EVENT_NOT_SUPPORTED_SOFT, +}; + enum class DeviceType : ALCenum { Playback = ALC_PLAYBACK_DEVICE_SOFT, Capture = ALC_CAPTURE_DEVICE_SOFT, diff --git a/3rdparty/openal/alc/export_list.h b/3rdparty/openal/alc/export_list.h index cefe7a099bce..b83f2c38a3fc 100644 --- a/3rdparty/openal/alc/export_list.h +++ b/3rdparty/openal/alc/export_list.h @@ -1,12 +1,14 @@ #ifndef ALC_EXPORT_LIST_H #define ALC_EXPORT_LIST_H +#include "config.h" + #include "AL/alc.h" #include "AL/al.h" #include "AL/alext.h" #include "inprogext.h" -#ifdef ALSOFT_EAX +#if ALSOFT_EAX #include "context.h" #include "al/eax/x_ram.h" #endif @@ -16,7 +18,8 @@ struct FuncExport { const char *funcName; void *address; }; -#define DECL(x) { #x, reinterpret_cast(x) } +#define DECL(x) FuncExport{#x, reinterpret_cast(x)} +/* NOLINTNEXTLINE(*-avoid-c-arrays) Too large for std::array auto-deduction :( */ inline const FuncExport alcFunctions[]{ DECL(alcCreateContext), DECL(alcMakeContextCurrent), @@ -56,6 +59,7 @@ inline const FuncExport alcFunctions[]{ DECL(alcReopenDeviceSOFT), + DECL(alcEventIsSupportedSOFT), DECL(alcEventControlSOFT), DECL(alcEventCallbackSOFT), @@ -200,11 +204,6 @@ inline const FuncExport alcFunctions[]{ DECL(alGetBuffer3PtrSOFT), DECL(alGetBufferPtrvSOFT), - DECL(alAuxiliaryEffectSlotPlaySOFT), - DECL(alAuxiliaryEffectSlotPlayvSOFT), - DECL(alAuxiliaryEffectSlotStopSOFT), - DECL(alAuxiliaryEffectSlotStopvSOFT), - DECL(alSourcePlayAtTimeSOFT), DECL(alSourcePlayAtTimevSOFT), @@ -218,6 +217,10 @@ inline const FuncExport alcFunctions[]{ DECL(alPushDebugGroupEXT), DECL(alPopDebugGroupEXT), DECL(alGetDebugMessageLogEXT), + DECL(alObjectLabelEXT), + DECL(alGetObjectLabelEXT), + DECL(alGetPointerEXT), + DECL(alGetPointervEXT), /* Direct Context functions */ DECL(alcGetProcAddress2), @@ -368,15 +371,16 @@ inline const FuncExport alcFunctions[]{ DECL(alPushDebugGroupDirectEXT), DECL(alPopDebugGroupDirectEXT), DECL(alGetDebugMessageLogDirectEXT), - DECL(alObjectLabelEXT), DECL(alObjectLabelDirectEXT), - DECL(alGetObjectLabelEXT), DECL(alGetObjectLabelDirectEXT), + DECL(alGetPointerDirectEXT), + DECL(alGetPointervDirectEXT), /* Extra functions */ DECL(alsoft_set_log_callback), -#ifdef ALSOFT_EAX -}, eaxFunctions[]{ +}; +#if ALSOFT_EAX +inline const std::array eaxFunctions{ DECL(EAXGet), DECL(EAXSet), DECL(EAXGetBufferMode), @@ -386,15 +390,16 @@ inline const FuncExport alcFunctions[]{ DECL(EAXSetDirect), DECL(EAXGetBufferModeDirect), DECL(EAXSetBufferModeDirect), -#endif }; +#endif #undef DECL struct EnumExport { const char *enumName; int value; }; -#define DECL(x) { #x, (x) } +#define DECL(x) EnumExport{#x, (x)} +/* NOLINTNEXTLINE(*-avoid-c-arrays) Too large for std::array auto-deduction :( */ inline const EnumExport alcEnumerations[]{ DECL(ALC_INVALID), DECL(ALC_FALSE), @@ -598,6 +603,48 @@ inline const EnumExport alcEnumerations[]{ DECL(AL_FORMAT_BFORMAT3D_FLOAT32), DECL(AL_FORMAT_BFORMAT3D_MULAW), + DECL(AL_FORMAT_UHJ2CHN8_SOFT), + DECL(AL_FORMAT_UHJ2CHN16_SOFT), + DECL(AL_FORMAT_UHJ2CHN_FLOAT32_SOFT), + DECL(AL_FORMAT_UHJ3CHN8_SOFT), + DECL(AL_FORMAT_UHJ3CHN16_SOFT), + DECL(AL_FORMAT_UHJ3CHN_FLOAT32_SOFT), + DECL(AL_FORMAT_UHJ4CHN8_SOFT), + DECL(AL_FORMAT_UHJ4CHN16_SOFT), + DECL(AL_FORMAT_UHJ4CHN_FLOAT32_SOFT), + DECL(AL_STEREO_MODE_SOFT), + DECL(AL_NORMAL_SOFT), + DECL(AL_SUPER_STEREO_SOFT), + DECL(AL_SUPER_STEREO_WIDTH_SOFT), + + DECL(AL_FORMAT_UHJ2CHN_MULAW_SOFT), + DECL(AL_FORMAT_UHJ2CHN_ALAW_SOFT), + DECL(AL_FORMAT_UHJ2CHN_IMA4_SOFT), + DECL(AL_FORMAT_UHJ2CHN_MSADPCM_SOFT), + DECL(AL_FORMAT_UHJ3CHN_MULAW_SOFT), + DECL(AL_FORMAT_UHJ3CHN_ALAW_SOFT), + DECL(AL_FORMAT_UHJ4CHN_MULAW_SOFT), + DECL(AL_FORMAT_UHJ4CHN_ALAW_SOFT), + + DECL(AL_FORMAT_MONO_I32), + DECL(AL_FORMAT_STEREO_I32), + DECL(AL_FORMAT_REAR_I32), + DECL(AL_FORMAT_QUAD_I32), + DECL(AL_FORMAT_51CHN_I32), + DECL(AL_FORMAT_61CHN_I32), + DECL(AL_FORMAT_71CHN_I32), + DECL(AL_FORMAT_BFORMAT2D_I32), + DECL(AL_FORMAT_BFORMAT3D_I32), + DECL(AL_FORMAT_UHJ2CHN_I32_SOFT), + DECL(AL_FORMAT_UHJ3CHN_I32_SOFT), + DECL(AL_FORMAT_UHJ4CHN_I32_SOFT), + + DECL(AL_FORMAT_REAR_FLOAT32), + DECL(AL_FORMAT_QUAD_FLOAT32), + DECL(AL_FORMAT_51CHN_FLOAT32), + DECL(AL_FORMAT_61CHN_FLOAT32), + DECL(AL_FORMAT_71CHN_FLOAT32), + DECL(AL_FREQUENCY), DECL(AL_BITS), DECL(AL_CHANNELS), @@ -820,32 +867,9 @@ inline const EnumExport alcEnumerations[]{ DECL(AL_UNPACK_AMBISONIC_ORDER_SOFT), - DECL(AL_EFFECT_CONVOLUTION_REVERB_SOFT), + DECL(AL_EFFECT_CONVOLUTION_SOFT), DECL(AL_EFFECTSLOT_STATE_SOFT), - DECL(AL_FORMAT_UHJ2CHN8_SOFT), - DECL(AL_FORMAT_UHJ2CHN16_SOFT), - DECL(AL_FORMAT_UHJ2CHN_FLOAT32_SOFT), - DECL(AL_FORMAT_UHJ3CHN8_SOFT), - DECL(AL_FORMAT_UHJ3CHN16_SOFT), - DECL(AL_FORMAT_UHJ3CHN_FLOAT32_SOFT), - DECL(AL_FORMAT_UHJ4CHN8_SOFT), - DECL(AL_FORMAT_UHJ4CHN16_SOFT), - DECL(AL_FORMAT_UHJ4CHN_FLOAT32_SOFT), - DECL(AL_STEREO_MODE_SOFT), - DECL(AL_NORMAL_SOFT), - DECL(AL_SUPER_STEREO_SOFT), - DECL(AL_SUPER_STEREO_WIDTH_SOFT), - - DECL(AL_FORMAT_UHJ2CHN_MULAW_SOFT), - DECL(AL_FORMAT_UHJ2CHN_ALAW_SOFT), - DECL(AL_FORMAT_UHJ2CHN_IMA4_SOFT), - DECL(AL_FORMAT_UHJ2CHN_MSADPCM_SOFT), - DECL(AL_FORMAT_UHJ3CHN_MULAW_SOFT), - DECL(AL_FORMAT_UHJ3CHN_ALAW_SOFT), - DECL(AL_FORMAT_UHJ4CHN_MULAW_SOFT), - DECL(AL_FORMAT_UHJ4CHN_ALAW_SOFT), - DECL(AL_DONT_CARE_EXT), DECL(AL_DEBUG_OUTPUT_EXT), DECL(AL_DEBUG_CALLBACK_FUNCTION_EXT), @@ -882,16 +906,20 @@ inline const EnumExport alcEnumerations[]{ DECL(AL_EFFECT_EXT), DECL(AL_AUXILIARY_EFFECT_SLOT_EXT), + DECL(AL_PANNING_ENABLED_SOFT), + DECL(AL_PAN_SOFT), + DECL(AL_STOP_SOURCES_ON_DISCONNECT_SOFT), -#ifdef ALSOFT_EAX -}, eaxEnumerations[]{ +}; +#if ALSOFT_EAX +inline const std::array eaxEnumerations{ DECL(AL_EAX_RAM_SIZE), DECL(AL_EAX_RAM_FREE), DECL(AL_STORAGE_AUTOMATIC), DECL(AL_STORAGE_HARDWARE), DECL(AL_STORAGE_ACCESSIBLE), -#endif // ALSOFT_EAX }; +#endif #undef DECL #endif /* ALC_EXPORT_LIST_H */ diff --git a/3rdparty/openal/alc/inprogext.h b/3rdparty/openal/alc/inprogext.h index 2fa425bbbcb6..eb421fd6350f 100644 --- a/3rdparty/openal/alc/inprogext.h +++ b/3rdparty/openal/alc/inprogext.h @@ -1,6 +1,7 @@ #ifndef INPROGEXT_H #define INPROGEXT_H +/* NOLINTBEGIN */ #include "AL/al.h" #include "AL/alc.h" #include "AL/alext.h" @@ -36,25 +37,11 @@ void AL_APIENTRY alFlushMappedBufferDirectSOFT(ALCcontext *context, ALuint buffe #endif #endif -#ifndef AL_SOFT_bformat_hoa -#define AL_SOFT_bformat_hoa -#define AL_UNPACK_AMBISONIC_ORDER_SOFT 0x199D -#endif - -#ifndef AL_SOFT_convolution_reverb -#define AL_SOFT_convolution_reverb -#define AL_EFFECT_CONVOLUTION_REVERB_SOFT 0xA000 -#define AL_EFFECTSLOT_STATE_SOFT 0x199D -typedef void (AL_APIENTRY*LPALAUXILIARYEFFECTSLOTPLAYSOFT)(ALuint slotid) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY*LPALAUXILIARYEFFECTSLOTPLAYVSOFT)(ALsizei n, const ALuint *slotids) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY*LPALAUXILIARYEFFECTSLOTSTOPSOFT)(ALuint slotid) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY*LPALAUXILIARYEFFECTSLOTSTOPVSOFT)(ALsizei n, const ALuint *slotids) AL_API_NOEXCEPT17; -#ifdef AL_ALEXT_PROTOTYPES -AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlaySOFT(ALuint slotid) AL_API_NOEXCEPT; -AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlayvSOFT(ALsizei n, const ALuint *slotids) AL_API_NOEXCEPT; -AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopSOFT(ALuint slotid) AL_API_NOEXCEPT; -AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopvSOFT(ALsizei n, const ALuint *slotids) AL_API_NOEXCEPT; -#endif +#ifndef AL_SOFT_convolution_effect +#define AL_SOFT_convolution_effect +#define AL_EFFECT_CONVOLUTION_SOFT 0xA000 +#define AL_CONVOLUTION_ORIENTATION_SOFT 0x100F /* same as AL_ORIENTATION */ +#define AL_EFFECTSLOT_STATE_SOFT 0x199E #endif #ifndef AL_SOFT_hold_on_disconnect @@ -63,338 +50,33 @@ AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopvSOFT(ALsizei n, const ALuint * #endif -#ifndef AL_EXT_direct_context -#define AL_EXT_direct_context -typedef ALCvoid* (ALC_APIENTRY *LPALCGETPROCADDRESS2)(ALCdevice *device, const ALCchar *funcname) AL_API_NOEXCEPT17; - -typedef void (AL_APIENTRY *LPALENABLEDIRECT)(ALCcontext *context, ALenum capability) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALDISABLEDIRECT)(ALCcontext *context, ALenum capability) AL_API_NOEXCEPT17; -typedef ALboolean (AL_APIENTRY *LPALISENABLEDDIRECT)(ALCcontext *context, ALenum capability) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALDOPPLERFACTORDIRECT)(ALCcontext *context, ALfloat value) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALSPEEDOFSOUNDDIRECT)(ALCcontext *context, ALfloat value) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALDISTANCEMODELDIRECT)(ALCcontext *context, ALenum distanceModel) AL_API_NOEXCEPT17; -typedef const ALchar* (AL_APIENTRY *LPALGETSTRINGDIRECT)(ALCcontext *context, ALenum param) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETBOOLEANVDIRECT)(ALCcontext *context, ALenum param, ALboolean *values) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETINTEGERVDIRECT)(ALCcontext *context, ALenum param, ALint *values) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETFLOATVDIRECT)(ALCcontext *context, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETDOUBLEVDIRECT)(ALCcontext *context, ALenum param, ALdouble *values) AL_API_NOEXCEPT17; -typedef ALboolean (AL_APIENTRY *LPALGETBOOLEANDIRECT)(ALCcontext *context, ALenum param) AL_API_NOEXCEPT17; -typedef ALint (AL_APIENTRY *LPALGETINTEGERDIRECT)(ALCcontext *context, ALenum param) AL_API_NOEXCEPT17; -typedef ALfloat (AL_APIENTRY *LPALGETFLOATDIRECT)(ALCcontext *context, ALenum param) AL_API_NOEXCEPT17; -typedef ALdouble (AL_APIENTRY *LPALGETDOUBLEDIRECT)(ALCcontext *context, ALenum param) AL_API_NOEXCEPT17; -typedef ALenum (AL_APIENTRY *LPALGETERRORDIRECT)(ALCcontext *context) AL_API_NOEXCEPT17; -typedef ALboolean (AL_APIENTRY *LPALISEXTENSIONPRESENTDIRECT)(ALCcontext *context, const ALchar *extname) AL_API_NOEXCEPT17; -typedef void* (AL_APIENTRY *LPALGETPROCADDRESSDIRECT)(ALCcontext *context, const ALchar *fname) AL_API_NOEXCEPT17; -typedef ALenum (AL_APIENTRY *LPALGETENUMVALUEDIRECT)(ALCcontext *context, const ALchar *ename) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALLISTENERFDIRECT)(ALCcontext *context, ALenum param, ALfloat value) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALLISTENER3FDIRECT)(ALCcontext *context, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALLISTENERFVDIRECT)(ALCcontext *context, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALLISTENERIDIRECT)(ALCcontext *context, ALenum param, ALint value) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALLISTENER3IDIRECT)(ALCcontext *context, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALLISTENERIVDIRECT)(ALCcontext *context, ALenum param, const ALint *values) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETLISTENERFDIRECT)(ALCcontext *context, ALenum param, ALfloat *value) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETLISTENER3FDIRECT)(ALCcontext *context, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETLISTENERFVDIRECT)(ALCcontext *context, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETLISTENERIDIRECT)(ALCcontext *context, ALenum param, ALint *value) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETLISTENER3IDIRECT)(ALCcontext *context, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETLISTENERIVDIRECT)(ALCcontext *context, ALenum param, ALint *values) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGENSOURCESDIRECT)(ALCcontext *context, ALsizei n, ALuint *sources) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALDELETESOURCESDIRECT)(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; -typedef ALboolean (AL_APIENTRY *LPALISSOURCEDIRECT)(ALCcontext *context, ALuint source) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALSOURCEFDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALfloat value) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALSOURCE3FDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALSOURCEFVDIRECT)(ALCcontext *context, ALuint source, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALSOURCEIDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALint value) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALSOURCE3IDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALSOURCEIVDIRECT)(ALCcontext *context, ALuint source, ALenum param, const ALint *values) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETSOURCEFDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALfloat *value) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETSOURCE3FDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETSOURCEFVDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETSOURCEIDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALint *value) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETSOURCE3IDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETSOURCEIVDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALint *values) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALSOURCEPLAYVDIRECT)(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALSOURCESTOPVDIRECT)(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALSOURCEREWINDVDIRECT)(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALSOURCEPAUSEVDIRECT)(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALSOURCEPLAYDIRECT)(ALCcontext *context, ALuint source) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALSOURCESTOPDIRECT)(ALCcontext *context, ALuint source) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALSOURCEREWINDDIRECT)(ALCcontext *context, ALuint source) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALSOURCEPAUSEDIRECT)(ALCcontext *context, ALuint source) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALSOURCEQUEUEBUFFERSDIRECT)(ALCcontext *context, ALuint source, ALsizei nb, const ALuint *buffers) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALSOURCEUNQUEUEBUFFERSDIRECT)(ALCcontext *context, ALuint source, ALsizei nb, ALuint *buffers) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGENBUFFERSDIRECT)(ALCcontext *context, ALsizei n, ALuint *buffers) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALDELETEBUFFERSDIRECT)(ALCcontext *context, ALsizei n, const ALuint *buffers) AL_API_NOEXCEPT17; -typedef ALboolean (AL_APIENTRY *LPALISBUFFERDIRECT)(ALCcontext *context, ALuint buffer) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALBUFFERDATADIRECT)(ALCcontext *context, ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei samplerate) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALBUFFERFDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALfloat value) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALBUFFER3FDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALBUFFERFVDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALBUFFERIDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALint value) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALBUFFER3IDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALBUFFERIVDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, const ALint *values) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETBUFFERFDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *value) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETBUFFER3FDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETBUFFERFVDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETBUFFERIDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALint *value) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETBUFFER3IDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETBUFFERIVDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALint *values) AL_API_NOEXCEPT17; -/* ALC_EXT_EFX */ -typedef void (AL_APIENTRY *LPALGENEFFECTSDIRECT)(ALCcontext *context, ALsizei n, ALuint *effects) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALDELETEEFFECTSDIRECT)(ALCcontext *context, ALsizei n, const ALuint *effects) AL_API_NOEXCEPT17; -typedef ALboolean (AL_APIENTRY *LPALISEFFECTDIRECT)(ALCcontext *context, ALuint effect) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALEFFECTIDIRECT)(ALCcontext *context, ALuint effect, ALenum param, ALint value) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALEFFECTIVDIRECT)(ALCcontext *context, ALuint effect, ALenum param, const ALint *values) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALEFFECTFDIRECT)(ALCcontext *context, ALuint effect, ALenum param, ALfloat value) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALEFFECTFVDIRECT)(ALCcontext *context, ALuint effect, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETEFFECTIDIRECT)(ALCcontext *context, ALuint effect, ALenum param, ALint *value) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETEFFECTIVDIRECT)(ALCcontext *context, ALuint effect, ALenum param, ALint *values) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETEFFECTFDIRECT)(ALCcontext *context, ALuint effect, ALenum param, ALfloat *value) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETEFFECTFVDIRECT)(ALCcontext *context, ALuint effect, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGENFILTERSDIRECT)(ALCcontext *context, ALsizei n, ALuint *filters) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALDELETEFILTERSDIRECT)(ALCcontext *context, ALsizei n, const ALuint *filters) AL_API_NOEXCEPT17; -typedef ALboolean (AL_APIENTRY *LPALISFILTERDIRECT)(ALCcontext *context, ALuint filter) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALFILTERIDIRECT)(ALCcontext *context, ALuint filter, ALenum param, ALint value) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALFILTERIVDIRECT)(ALCcontext *context, ALuint filter, ALenum param, const ALint *values) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALFILTERFDIRECT)(ALCcontext *context, ALuint filter, ALenum param, ALfloat value) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALFILTERFVDIRECT)(ALCcontext *context, ALuint filter, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETFILTERIDIRECT)(ALCcontext *context, ALuint filter, ALenum param, ALint *value) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETFILTERIVDIRECT)(ALCcontext *context, ALuint filter, ALenum param, ALint *values) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETFILTERFDIRECT)(ALCcontext *context, ALuint filter, ALenum param, ALfloat *value) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETFILTERFVDIRECT)(ALCcontext *context, ALuint filter, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGENAUXILIARYEFFECTSLOTSDIRECT)(ALCcontext *context, ALsizei n, ALuint *effectslots) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALDELETEAUXILIARYEFFECTSLOTSDIRECT)(ALCcontext *context, ALsizei n, const ALuint *effectslots) AL_API_NOEXCEPT17; -typedef ALboolean (AL_APIENTRY *LPALISAUXILIARYEFFECTSLOTDIRECT)(ALCcontext *context, ALuint effectslot) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTIDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, ALint value) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTIVDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, const ALint *values) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTFDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat value) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTFVDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTIDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, ALint *value) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTIVDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, ALint *values) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTFDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat *value) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTFVDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; -/* AL_EXT_BUFFER_DATA_STATIC */ -typedef void (AL_APIENTRY *LPALBUFFERDATASTATICDIRECT)(ALCcontext *context, ALuint buffer, ALenum format, ALvoid *data, ALsizei size, ALsizei freq) AL_API_NOEXCEPT17; -/* AL_EXT_debug */ -typedef void (AL_APIENTRY*LPALDEBUGMESSAGECALLBACKDIRECTEXT)(ALCcontext *context, ALDEBUGPROCEXT callback, void *userParam) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY*LPALDEBUGMESSAGEINSERTDIRECTEXT)(ALCcontext *context, ALenum source, ALenum type, ALuint id, ALenum severity, ALsizei length, const ALchar *message) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY*LPALDEBUGMESSAGECONTROLDIRECTEXT)(ALCcontext *context, ALenum source, ALenum type, ALenum severity, ALsizei count, const ALuint *ids, ALboolean enable) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY*LPALPUSHDEBUGGROUPDIRECTEXT)(ALCcontext *context, ALenum source, ALuint id, ALsizei length, const ALchar *message) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY*LPALPOPDEBUGGROUPDIRECTEXT)(ALCcontext *context) AL_API_NOEXCEPT17; -typedef ALuint (AL_APIENTRY*LPALGETDEBUGMESSAGELOGDIRECTEXT)(ALCcontext *context, ALuint count, ALsizei logBufSize, ALenum *sources, ALenum *types, ALuint *ids, ALenum *severities, ALsizei *lengths, ALchar *logBuf) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY*LPALOBJECTLABELDIRECTEXT)(ALCcontext *context, ALenum identifier, ALuint name, ALsizei length, const ALchar *label) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY*LPALGETOBJECTLABELDIRECTEXT)(ALCcontext *context, ALenum identifier, ALuint name, ALsizei bufSize, ALsizei *length, ALchar *label) AL_API_NOEXCEPT17; -/* AL_EXT_FOLDBACK */ -typedef void (AL_APIENTRY *LPALREQUESTFOLDBACKSTARTDIRECT)(ALCcontext *context, ALenum mode, ALsizei count, ALsizei length, ALfloat *mem, LPALFOLDBACKCALLBACK callback) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALREQUESTFOLDBACKSTOPDIRECT)(ALCcontext *context) AL_API_NOEXCEPT17; -/* AL_SOFT_buffer_sub_data */ -typedef void (AL_APIENTRY *LPALBUFFERSUBDATADIRECTSOFT)(ALCcontext *context, ALuint buffer, ALenum format, const ALvoid *data, ALsizei offset, ALsizei length) AL_API_NOEXCEPT17; -/* AL_SOFT_source_latency */ -typedef void (AL_APIENTRY *LPALSOURCEDDIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALdouble) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALSOURCE3DDIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALdouble,ALdouble,ALdouble) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALSOURCEDVDIRECTSOFT)(ALCcontext*,ALuint,ALenum,const ALdouble*) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETSOURCEDDIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALdouble*) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETSOURCE3DDIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALdouble*,ALdouble*,ALdouble*) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETSOURCEDVDIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALdouble*) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALSOURCEI64DIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALint64SOFT) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALSOURCE3I64DIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALint64SOFT,ALint64SOFT,ALint64SOFT) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALSOURCEI64VDIRECTSOFT)(ALCcontext*,ALuint,ALenum,const ALint64SOFT*) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETSOURCEI64DIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALint64SOFT*) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETSOURCE3I64DIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALint64SOFT*,ALint64SOFT*,ALint64SOFT*) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETSOURCEI64VDIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALint64SOFT*) AL_API_NOEXCEPT17; -/* AL_SOFT_deferred_updates */ -typedef void (AL_APIENTRY *LPALDEFERUPDATESDIRECTSOFT)(ALCcontext *context) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALPROCESSUPDATESDIRECTSOFT)(ALCcontext *context) AL_API_NOEXCEPT17; -/* AL_SOFT_source_resampler */ -typedef const ALchar* (AL_APIENTRY *LPALGETSTRINGIDIRECTSOFT)(ALCcontext *context, ALenum pname, ALsizei index) AL_API_NOEXCEPT17; -/* AL_SOFT_events */ -typedef void (AL_APIENTRY *LPALEVENTCONTROLDIRECTSOFT)(ALCcontext *context, ALsizei count, const ALenum *types, ALboolean enable) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALEVENTCALLBACKDIRECTSOFT)(ALCcontext *context, ALEVENTPROCSOFT callback, void *userParam) AL_API_NOEXCEPT17; -typedef void* (AL_APIENTRY *LPALGETPOINTERDIRECTSOFT)(ALCcontext *context, ALenum pname) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETPOINTERVDIRECTSOFT)(ALCcontext *context, ALenum pname, void **values) AL_API_NOEXCEPT17; -/* AL_SOFT_callback_buffer */ -typedef void (AL_APIENTRY *LPALBUFFERCALLBACKDIRECTSOFT)(ALCcontext *context, ALuint buffer, ALenum format, ALsizei freq, ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETBUFFERPTRDIRECTSOFT)(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **value) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETBUFFER3PTRDIRECTSOFT)(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **value1, ALvoid **value2, ALvoid **value3) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALGETBUFFERPTRVDIRECTSOFT)(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **values) AL_API_NOEXCEPT17; -/* AL_SOFT_source_start_delay */ -typedef void (AL_APIENTRY *LPALSOURCEPLAYATTIMEDIRECTSOFT)(ALCcontext *context, ALuint source, ALint64SOFT start_time) AL_API_NOEXCEPT17; -typedef void (AL_APIENTRY *LPALSOURCEPLAYATTIMEVDIRECTSOFT)(ALCcontext *context, ALsizei n, const ALuint *sources, ALint64SOFT start_time) AL_API_NOEXCEPT17; -/* EAX */ -typedef ALenum (AL_APIENTRY *LPEAXSETDIRECT)(ALCcontext *context, const struct _GUID *property_set_id, ALuint property_id, ALuint property_source_id, ALvoid *property_buffer, ALuint property_size) AL_API_NOEXCEPT17; -typedef ALenum (AL_APIENTRY *LPEAXGETDIRECT)(ALCcontext *context, const struct _GUID *property_set_id, ALuint property_id, ALuint property_source_id, ALvoid *property_value, ALuint property_value_size) AL_API_NOEXCEPT17; -typedef ALboolean (AL_APIENTRY *LPEAXSETBUFFERMODEDIRECT)(ALCcontext *context, ALsizei n, const ALuint *buffers, ALint value) AL_API_NOEXCEPT17; -typedef ALenum (AL_APIENTRY *LPEAXGETBUFFERMODEDIRECT)(ALCcontext *context, ALuint buffer, ALint *pReserved) AL_API_NOEXCEPT17; -#ifdef AL_ALEXT_PROTOTYPES -ALCvoid* AL_APIENTRY alcGetProcAddress2(ALCdevice *device, const ALchar *funcName) AL_API_NOEXCEPT; - -void AL_APIENTRY alEnableDirect(ALCcontext *context, ALenum capability) AL_API_NOEXCEPT; -void AL_APIENTRY alDisableDirect(ALCcontext *context, ALenum capability) AL_API_NOEXCEPT; -ALboolean AL_APIENTRY alIsEnabledDirect(ALCcontext *context, ALenum capability) AL_API_NOEXCEPT; - -void AL_APIENTRY alDopplerFactorDirect(ALCcontext *context, ALfloat value) AL_API_NOEXCEPT; -void AL_APIENTRY alSpeedOfSoundDirect(ALCcontext *context, ALfloat value) AL_API_NOEXCEPT; -void AL_APIENTRY alDistanceModelDirect(ALCcontext *context, ALenum distanceModel) AL_API_NOEXCEPT; - -const ALchar* AL_APIENTRY alGetStringDirect(ALCcontext *context, ALenum param) AL_API_NOEXCEPT; -void AL_APIENTRY alGetBooleanvDirect(ALCcontext *context, ALenum param, ALboolean *values) AL_API_NOEXCEPT; -void AL_APIENTRY alGetIntegervDirect(ALCcontext *context, ALenum param, ALint *values) AL_API_NOEXCEPT; -void AL_APIENTRY alGetFloatvDirect(ALCcontext *context, ALenum param, ALfloat *values) AL_API_NOEXCEPT; -void AL_APIENTRY alGetDoublevDirect(ALCcontext *context, ALenum param, ALdouble *values) AL_API_NOEXCEPT; -ALboolean AL_APIENTRY alGetBooleanDirect(ALCcontext *context, ALenum param) AL_API_NOEXCEPT; -ALint AL_APIENTRY alGetIntegerDirect(ALCcontext *context, ALenum param) AL_API_NOEXCEPT; -ALfloat AL_APIENTRY alGetFloatDirect(ALCcontext *context, ALenum param) AL_API_NOEXCEPT; -ALdouble AL_APIENTRY alGetDoubleDirect(ALCcontext *context, ALenum param) AL_API_NOEXCEPT; - -ALenum AL_APIENTRY alGetErrorDirect(ALCcontext *context) AL_API_NOEXCEPT; -ALboolean AL_APIENTRY alIsExtensionPresentDirect(ALCcontext *context, const ALchar *extname) AL_API_NOEXCEPT; -void* AL_APIENTRY alGetProcAddressDirect(ALCcontext *context, const ALchar *fname) AL_API_NOEXCEPT; -ALenum AL_APIENTRY alGetEnumValueDirect(ALCcontext *context, const ALchar *ename) AL_API_NOEXCEPT; - -void AL_APIENTRY alListenerfDirect(ALCcontext *context, ALenum param, ALfloat value) AL_API_NOEXCEPT; -void AL_APIENTRY alListener3fDirect(ALCcontext *context, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT; -void AL_APIENTRY alListenerfvDirect(ALCcontext *context, ALenum param, const ALfloat *values) AL_API_NOEXCEPT; -void AL_APIENTRY alListeneriDirect(ALCcontext *context, ALenum param, ALint value) AL_API_NOEXCEPT; -void AL_APIENTRY alListener3iDirect(ALCcontext *context, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT; -void AL_APIENTRY alListenerivDirect(ALCcontext *context, ALenum param, const ALint *values) AL_API_NOEXCEPT; -void AL_APIENTRY alGetListenerfDirect(ALCcontext *context, ALenum param, ALfloat *value) AL_API_NOEXCEPT; -void AL_APIENTRY alGetListener3fDirect(ALCcontext *context, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT; -void AL_APIENTRY alGetListenerfvDirect(ALCcontext *context, ALenum param, ALfloat *values) AL_API_NOEXCEPT; -void AL_APIENTRY alGetListeneriDirect(ALCcontext *context, ALenum param, ALint *value) AL_API_NOEXCEPT; -void AL_APIENTRY alGetListener3iDirect(ALCcontext *context, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT; -void AL_APIENTRY alGetListenerivDirect(ALCcontext *context, ALenum param, ALint *values) AL_API_NOEXCEPT; - -void AL_APIENTRY alGenSourcesDirect(ALCcontext *context, ALsizei n, ALuint *sources) AL_API_NOEXCEPT; -void AL_APIENTRY alDeleteSourcesDirect(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; -ALboolean AL_APIENTRY alIsSourceDirect(ALCcontext *context, ALuint source) AL_API_NOEXCEPT; -void AL_APIENTRY alSourcefDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat value) AL_API_NOEXCEPT; -void AL_APIENTRY alSource3fDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT; -void AL_APIENTRY alSourcefvDirect(ALCcontext *context, ALuint source, ALenum param, const ALfloat *values) AL_API_NOEXCEPT; -void AL_APIENTRY alSourceiDirect(ALCcontext *context, ALuint source, ALenum param, ALint value) AL_API_NOEXCEPT; -void AL_APIENTRY alSource3iDirect(ALCcontext *context, ALuint source, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT; -void AL_APIENTRY alSourceivDirect(ALCcontext *context, ALuint source, ALenum param, const ALint *values) AL_API_NOEXCEPT; -void AL_APIENTRY alGetSourcefDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat *value) AL_API_NOEXCEPT; -void AL_APIENTRY alGetSource3fDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT; -void AL_APIENTRY alGetSourcefvDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat *values) AL_API_NOEXCEPT; -void AL_APIENTRY alGetSourceiDirect(ALCcontext *context, ALuint source, ALenum param, ALint *value) AL_API_NOEXCEPT; -void AL_APIENTRY alGetSource3iDirect(ALCcontext *context, ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT; -void AL_APIENTRY alGetSourceivDirect(ALCcontext *context, ALuint source, ALenum param, ALint *values) AL_API_NOEXCEPT; -void AL_APIENTRY alSourcePlayDirect(ALCcontext *context, ALuint source) AL_API_NOEXCEPT; -void AL_APIENTRY alSourceStopDirect(ALCcontext *context, ALuint source) AL_API_NOEXCEPT; -void AL_APIENTRY alSourceRewindDirect(ALCcontext *context, ALuint source) AL_API_NOEXCEPT; -void AL_APIENTRY alSourcePauseDirect(ALCcontext *context, ALuint source) AL_API_NOEXCEPT; -void AL_APIENTRY alSourcePlayvDirect(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; -void AL_APIENTRY alSourceStopvDirect(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; -void AL_APIENTRY alSourceRewindvDirect(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; -void AL_APIENTRY alSourcePausevDirect(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; -void AL_APIENTRY alSourceQueueBuffersDirect(ALCcontext *context, ALuint source, ALsizei nb, const ALuint *buffers) AL_API_NOEXCEPT; -void AL_APIENTRY alSourceUnqueueBuffersDirect(ALCcontext *context, ALuint source, ALsizei nb, ALuint *buffers) AL_API_NOEXCEPT; - -void AL_APIENTRY alGenBuffersDirect(ALCcontext *context, ALsizei n, ALuint *buffers) AL_API_NOEXCEPT; -void AL_APIENTRY alDeleteBuffersDirect(ALCcontext *context, ALsizei n, const ALuint *buffers) AL_API_NOEXCEPT; -ALboolean AL_APIENTRY alIsBufferDirect(ALCcontext *context, ALuint buffer) AL_API_NOEXCEPT; -void AL_APIENTRY alBufferDataDirect(ALCcontext *context, ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei samplerate) AL_API_NOEXCEPT; -void AL_APIENTRY alBufferfDirect(ALCcontext *context, ALuint buffer, ALenum param, ALfloat value) AL_API_NOEXCEPT; -void AL_APIENTRY alBuffer3fDirect(ALCcontext *context, ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT; -void AL_APIENTRY alBufferfvDirect(ALCcontext *context, ALuint buffer, ALenum param, const ALfloat *values) AL_API_NOEXCEPT; -void AL_APIENTRY alBufferiDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint value) AL_API_NOEXCEPT; -void AL_APIENTRY alBuffer3iDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT; -void AL_APIENTRY alBufferivDirect(ALCcontext *context, ALuint buffer, ALenum param, const ALint *values) AL_API_NOEXCEPT; -void AL_APIENTRY alGetBufferfDirect(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *value) AL_API_NOEXCEPT; -void AL_APIENTRY alGetBuffer3fDirect(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT; -void AL_APIENTRY alGetBufferfvDirect(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *values) AL_API_NOEXCEPT; -void AL_APIENTRY alGetBufferiDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint *value) AL_API_NOEXCEPT; -void AL_APIENTRY alGetBuffer3iDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT; -void AL_APIENTRY alGetBufferivDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint *values) AL_API_NOEXCEPT; - -void AL_APIENTRY alGenEffectsDirect(ALCcontext *context, ALsizei n, ALuint *effects) AL_API_NOEXCEPT; -void AL_APIENTRY alDeleteEffectsDirect(ALCcontext *context, ALsizei n, const ALuint *effects) AL_API_NOEXCEPT; -ALboolean AL_APIENTRY alIsEffectDirect(ALCcontext *context, ALuint effect) AL_API_NOEXCEPT; -void AL_APIENTRY alEffectiDirect(ALCcontext *context, ALuint effect, ALenum param, ALint iValue) AL_API_NOEXCEPT; -void AL_APIENTRY alEffectivDirect(ALCcontext *context, ALuint effect, ALenum param, const ALint *piValues) AL_API_NOEXCEPT; -void AL_APIENTRY alEffectfDirect(ALCcontext *context, ALuint effect, ALenum param, ALfloat flValue) AL_API_NOEXCEPT; -void AL_APIENTRY alEffectfvDirect(ALCcontext *context, ALuint effect, ALenum param, const ALfloat *pflValues) AL_API_NOEXCEPT; -void AL_APIENTRY alGetEffectiDirect(ALCcontext *context, ALuint effect, ALenum param, ALint *piValue) AL_API_NOEXCEPT; -void AL_APIENTRY alGetEffectivDirect(ALCcontext *context, ALuint effect, ALenum param, ALint *piValues) AL_API_NOEXCEPT; -void AL_APIENTRY alGetEffectfDirect(ALCcontext *context, ALuint effect, ALenum param, ALfloat *pflValue) AL_API_NOEXCEPT; -void AL_APIENTRY alGetEffectfvDirect(ALCcontext *context, ALuint effect, ALenum param, ALfloat *pflValues) AL_API_NOEXCEPT; - -void AL_APIENTRY alGenFiltersDirect(ALCcontext *context, ALsizei n, ALuint *filters) AL_API_NOEXCEPT; -void AL_APIENTRY alDeleteFiltersDirect(ALCcontext *context, ALsizei n, const ALuint *filters) AL_API_NOEXCEPT; -ALboolean AL_APIENTRY alIsFilterDirect(ALCcontext *context, ALuint filter) AL_API_NOEXCEPT; -void AL_APIENTRY alFilteriDirect(ALCcontext *context, ALuint filter, ALenum param, ALint iValue) AL_API_NOEXCEPT; -void AL_APIENTRY alFilterivDirect(ALCcontext *context, ALuint filter, ALenum param, const ALint *piValues) AL_API_NOEXCEPT; -void AL_APIENTRY alFilterfDirect(ALCcontext *context, ALuint filter, ALenum param, ALfloat flValue) AL_API_NOEXCEPT; -void AL_APIENTRY alFilterfvDirect(ALCcontext *context, ALuint filter, ALenum param, const ALfloat *pflValues) AL_API_NOEXCEPT; -void AL_APIENTRY alGetFilteriDirect(ALCcontext *context, ALuint filter, ALenum param, ALint *piValue) AL_API_NOEXCEPT; -void AL_APIENTRY alGetFilterivDirect(ALCcontext *context, ALuint filter, ALenum param, ALint *piValues) AL_API_NOEXCEPT; -void AL_APIENTRY alGetFilterfDirect(ALCcontext *context, ALuint filter, ALenum param, ALfloat *pflValue) AL_API_NOEXCEPT; -void AL_APIENTRY alGetFilterfvDirect(ALCcontext *context, ALuint filter, ALenum param, ALfloat *pflValues) AL_API_NOEXCEPT; - -void AL_APIENTRY alGenAuxiliaryEffectSlotsDirect(ALCcontext *context, ALsizei n, ALuint *effectslots) AL_API_NOEXCEPT; -void AL_APIENTRY alDeleteAuxiliaryEffectSlotsDirect(ALCcontext *context, ALsizei n, const ALuint *effectslots) AL_API_NOEXCEPT; -ALboolean AL_APIENTRY alIsAuxiliaryEffectSlotDirect(ALCcontext *context, ALuint effectslot) AL_API_NOEXCEPT; -void AL_APIENTRY alAuxiliaryEffectSlotiDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALint iValue) AL_API_NOEXCEPT; -void AL_APIENTRY alAuxiliaryEffectSlotivDirect(ALCcontext *context, ALuint effectslot, ALenum param, const ALint *piValues) AL_API_NOEXCEPT; -void AL_APIENTRY alAuxiliaryEffectSlotfDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat flValue) AL_API_NOEXCEPT; -void AL_APIENTRY alAuxiliaryEffectSlotfvDirect(ALCcontext *context, ALuint effectslot, ALenum param, const ALfloat *pflValues) AL_API_NOEXCEPT; -void AL_APIENTRY alGetAuxiliaryEffectSlotiDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALint *piValue) AL_API_NOEXCEPT; -void AL_APIENTRY alGetAuxiliaryEffectSlotivDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALint *piValues) AL_API_NOEXCEPT; -void AL_APIENTRY alGetAuxiliaryEffectSlotfDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat *pflValue) AL_API_NOEXCEPT; -void AL_APIENTRY alGetAuxiliaryEffectSlotfvDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat *pflValues) AL_API_NOEXCEPT; - -void AL_APIENTRY alBufferDataStaticDirect(ALCcontext *context, ALuint buffer, ALenum format, ALvoid *data, ALsizei size, ALsizei freq) AL_API_NOEXCEPT; - -void AL_APIENTRY alDebugMessageCallbackDirectEXT(ALCcontext *context, ALDEBUGPROCEXT callback, void *userParam) AL_API_NOEXCEPT; -void AL_APIENTRY alDebugMessageInsertDirectEXT(ALCcontext *context, ALenum source, ALenum type, ALuint id, ALenum severity, ALsizei length, const ALchar *message) AL_API_NOEXCEPT; -void AL_APIENTRY alDebugMessageControlDirectEXT(ALCcontext *context, ALenum source, ALenum type, ALenum severity, ALsizei count, const ALuint *ids, ALboolean enable) AL_API_NOEXCEPT; -void AL_APIENTRY alPushDebugGroupDirectEXT(ALCcontext *context, ALenum source, ALuint id, ALsizei length, const ALchar *message) AL_API_NOEXCEPT; -void AL_APIENTRY alPopDebugGroupDirectEXT(ALCcontext *context) AL_API_NOEXCEPT; -ALuint AL_APIENTRY alGetDebugMessageLogDirectEXT(ALCcontext *context, ALuint count, ALsizei logBufSize, ALenum *sources, ALenum *types, ALuint *ids, ALenum *severities, ALsizei *lengths, ALchar *logBuf) AL_API_NOEXCEPT; -void AL_APIENTRY alObjectLabelDirectEXT(ALCcontext *context, ALenum identifier, ALuint name, ALsizei length, const ALchar *label) AL_API_NOEXCEPT; -void AL_APIENTRY alGetObjectLabelDirectEXT(ALCcontext *context, ALenum identifier, ALuint name, ALsizei bufSize, ALsizei *length, ALchar *label) AL_API_NOEXCEPT; - -void AL_APIENTRY alRequestFoldbackStartDirect(ALCcontext *context, ALenum mode, ALsizei count, ALsizei length, ALfloat *mem, LPALFOLDBACKCALLBACK callback) AL_API_NOEXCEPT; -void AL_APIENTRY alRequestFoldbackStopDirect(ALCcontext *context) AL_API_NOEXCEPT; - -void AL_APIENTRY alBufferSubDataDirectSOFT(ALCcontext *context, ALuint buffer, ALenum format, const ALvoid *data, ALsizei offset, ALsizei length) AL_API_NOEXCEPT; - -void AL_APIENTRY alSourcedDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble value) AL_API_NOEXCEPT; -void AL_APIENTRY alSource3dDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3) AL_API_NOEXCEPT; -void AL_APIENTRY alSourcedvDirectSOFT(ALCcontext *context, ALuint source, ALenum param, const ALdouble *values) AL_API_NOEXCEPT; -void AL_APIENTRY alGetSourcedDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble *value) AL_API_NOEXCEPT; -void AL_APIENTRY alGetSource3dDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3) AL_API_NOEXCEPT; -void AL_APIENTRY alGetSourcedvDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble *values) AL_API_NOEXCEPT; -void AL_APIENTRY alSourcei64DirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT value) AL_API_NOEXCEPT; -void AL_APIENTRY alSource3i64DirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3) AL_API_NOEXCEPT; -void AL_APIENTRY alSourcei64vDirectSOFT(ALCcontext *context, ALuint source, ALenum param, const ALint64SOFT *values) AL_API_NOEXCEPT; -void AL_APIENTRY alGetSourcei64DirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT *value) AL_API_NOEXCEPT; -void AL_APIENTRY alGetSource3i64DirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3) AL_API_NOEXCEPT; -void AL_APIENTRY alGetSourcei64vDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT *values) AL_API_NOEXCEPT; - -void AL_APIENTRY alDeferUpdatesDirectSOFT(ALCcontext *context) AL_API_NOEXCEPT; -void AL_APIENTRY alProcessUpdatesDirectSOFT(ALCcontext *context) AL_API_NOEXCEPT; - -const ALchar* AL_APIENTRY alGetStringiDirectSOFT(ALCcontext *context, ALenum pname, ALsizei index) AL_API_NOEXCEPT; - -void AL_APIENTRY alEventControlDirectSOFT(ALCcontext *context, ALsizei count, const ALenum *types, ALboolean enable) AL_API_NOEXCEPT; -void AL_APIENTRY alEventCallbackDirectSOFT(ALCcontext *context, ALEVENTPROCSOFT callback, void *userParam) AL_API_NOEXCEPT; -void* AL_APIENTRY alGetPointerDirectSOFT(ALCcontext *context, ALenum pname) AL_API_NOEXCEPT; -void AL_APIENTRY alGetPointervDirectSOFT(ALCcontext *context, ALenum pname, void **values) AL_API_NOEXCEPT; - -void AL_APIENTRY alBufferCallbackDirectSOFT(ALCcontext *context, ALuint buffer, ALenum format, ALsizei freq, ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr) AL_API_NOEXCEPT; -void AL_APIENTRY alGetBufferPtrDirectSOFT(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **ptr) AL_API_NOEXCEPT; -void AL_APIENTRY alGetBuffer3PtrDirectSOFT(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **ptr0, ALvoid **ptr1, ALvoid **ptr2) AL_API_NOEXCEPT; -void AL_APIENTRY alGetBufferPtrvDirectSOFT(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **ptr) AL_API_NOEXCEPT; - -void AL_APIENTRY alSourcePlayAtTimeDirectSOFT(ALCcontext *context, ALuint source, ALint64SOFT start_time) AL_API_NOEXCEPT; -void AL_APIENTRY alSourcePlayAtTimevDirectSOFT(ALCcontext *context, ALsizei n, const ALuint *sources, ALint64SOFT start_time) AL_API_NOEXCEPT; - -ALenum AL_APIENTRY EAXSetDirect(ALCcontext *context, const struct _GUID *property_set_id, ALuint property_id, ALuint property_source_id, ALvoid *property_value, ALuint property_value_size) AL_API_NOEXCEPT; -ALenum AL_APIENTRY EAXGetDirect(ALCcontext *context, const struct _GUID *property_set_id, ALuint property_id, ALuint property_source_id, ALvoid *property_value, ALuint property_value_size) AL_API_NOEXCEPT; -ALboolean AL_APIENTRY EAXSetBufferModeDirect(ALCcontext *context, ALsizei n, const ALuint *buffers, ALint value) AL_API_NOEXCEPT; -ALenum AL_APIENTRY EAXGetBufferModeDirect(ALCcontext *context, ALuint buffer, ALint *pReserved) AL_API_NOEXCEPT; +#ifndef AL_EXT_32bit_formats +#define AL_EXT_32bit_formats +#define AL_FORMAT_MONO_I32 0x19DB +#define AL_FORMAT_STEREO_I32 0x19DC +#define AL_FORMAT_REAR_I32 0x19DD +#define AL_FORMAT_REAR_FLOAT32 0x19DE +#define AL_FORMAT_QUAD_I32 0x19DF +#define AL_FORMAT_QUAD_FLOAT32 0x19E0 +#define AL_FORMAT_51CHN_I32 0x19E1 +#define AL_FORMAT_51CHN_FLOAT32 0x19E2 +#define AL_FORMAT_61CHN_I32 0x19E3 +#define AL_FORMAT_61CHN_FLOAT32 0x19E4 +#define AL_FORMAT_71CHN_I32 0x19E5 +#define AL_FORMAT_71CHN_FLOAT32 0x19E6 + +#define AL_FORMAT_BFORMAT2D_I32 0x19E7 +#define AL_FORMAT_BFORMAT3D_I32 0x19E8 + +#define AL_FORMAT_UHJ2CHN_I32_SOFT 0x19E9 +#define AL_FORMAT_UHJ3CHN_I32_SOFT 0x19EA +#define AL_FORMAT_UHJ4CHN_I32_SOFT 0x19EB #endif + +#ifndef AL_SOFT_source_panning +#define AL_SOFT_source_panning +#define AL_PANNING_ENABLED_SOFT 0x19EC +#define AL_PAN_SOFT 0x19ED #endif /* Non-standard exports. Not part of any extension. */ @@ -407,13 +89,25 @@ void ALC_APIENTRY alsoft_set_log_callback(LPALSOFTLOGCALLBACK callback, void *us AL_API void AL_APIENTRY alSourceQueueBufferLayersSOFT(ALuint src, ALsizei nb, const ALuint *buffers) noexcept; +AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlaySOFT(ALuint slotid) noexcept; +AL_API void AL_APIENTRY alAuxiliaryEffectSlotPlayvSOFT(ALsizei n, const ALuint *slotids) noexcept; +AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopSOFT(ALuint slotid) noexcept; +AL_API void AL_APIENTRY alAuxiliaryEffectSlotStopvSOFT(ALsizei n, const ALuint *slotids) noexcept; + AL_API ALint64SOFT AL_APIENTRY alGetInteger64SOFT(ALenum pname) AL_API_NOEXCEPT; AL_API void AL_APIENTRY alGetInteger64vSOFT(ALenum pname, ALint64SOFT *values) AL_API_NOEXCEPT; ALint64SOFT AL_APIENTRY alGetInteger64DirectSOFT(ALCcontext *context, ALenum pname) AL_API_NOEXCEPT; void AL_APIENTRY alGetInteger64vDirectSOFT(ALCcontext *context, ALenum pname, ALint64SOFT *values) AL_API_NOEXCEPT; +/* Not included in the public headers or export list, as a precaution for apps + * that check these to determine the behavior of the multi-channel *32 formats. + */ +#define AL_FORMAT_MONO32 0x1202 +#define AL_FORMAT_STEREO32 0x1203 + #ifdef __cplusplus } /* extern "C" */ #endif +/* NOLINTEND */ #endif /* INPROGEXT_H */ diff --git a/3rdparty/openal/alc/panning.cpp b/3rdparty/openal/alc/panning.cpp index 871fef65e906..2f160dda9726 100644 --- a/3rdparty/openal/alc/panning.cpp +++ b/3rdparty/openal/alc/panning.cpp @@ -22,29 +22,26 @@ #include #include +#include #include #include #include +#include #include -#include +#include #include -#include #include -#include #include #include #include +#include +#include #include -#include "AL/al.h" #include "AL/alc.h" #include "AL/alext.h" -#include "al/auxeffectslot.h" -#include "albit.h" -#include "alconfig.h" #include "alc/context.h" -#include "almalloc.h" #include "alnumbers.h" #include "alnumeric.h" #include "alspan.h" @@ -53,64 +50,103 @@ #include "core/ambdec.h" #include "core/ambidefs.h" #include "core/bformatdec.h" +#include "core/bufferline.h" #include "core/bs2b.h" +#include "core/context.h" #include "core/devformat.h" +#include "core/device.h" +#include "core/effectslot.h" +#include "core/filters/nfc.h" +#include "core/filters/splitter.h" #include "core/front_stablizer.h" #include "core/hrtf.h" #include "core/logging.h" +#include "core/mixer/hrtfdefs.h" #include "core/uhjfilter.h" #include "device.h" +#include "flexarray.h" +#include "intrusive_ptr.h" #include "opthelpers.h" +#include "vector.h" namespace { -using namespace std::placeholders; +using namespace std::string_view_literals; using std::chrono::seconds; using std::chrono::nanoseconds; -inline const char *GetLabelFromChannel(Channel channel) +[[nodiscard]] +auto GetLabelFromChannel(Channel channel) -> std::string_view { switch(channel) { - case FrontLeft: return "front-left"; - case FrontRight: return "front-right"; - case FrontCenter: return "front-center"; - case LFE: return "lfe"; - case BackLeft: return "back-left"; - case BackRight: return "back-right"; - case BackCenter: return "back-center"; - case SideLeft: return "side-left"; - case SideRight: return "side-right"; - - case TopFrontLeft: return "top-front-left"; - case TopFrontCenter: return "top-front-center"; - case TopFrontRight: return "top-front-right"; - case TopCenter: return "top-center"; - case TopBackLeft: return "top-back-left"; - case TopBackCenter: return "top-back-center"; - case TopBackRight: return "top-back-right"; - - case Aux0: return "Aux0"; - case Aux1: return "Aux1"; - case Aux2: return "Aux2"; - case Aux3: return "Aux3"; - case Aux4: return "Aux4"; - case Aux5: return "Aux5"; - case Aux6: return "Aux6"; - case Aux7: return "Aux7"; - case Aux8: return "Aux8"; - case Aux9: return "Aux9"; - case Aux10: return "Aux10"; - case Aux11: return "Aux11"; - case Aux12: return "Aux12"; - case Aux13: return "Aux13"; - case Aux14: return "Aux14"; - case Aux15: return "Aux15"; - - case MaxChannels: break; + case FrontLeft: return "front-left"sv; + case FrontRight: return "front-right"sv; + case FrontCenter: return "front-center"sv; + case LFE: return "lfe"sv; + case BackLeft: return "back-left"sv; + case BackRight: return "back-right"sv; + case BackCenter: return "back-center"sv; + case SideLeft: return "side-left"sv; + case SideRight: return "side-right"sv; + + case TopFrontLeft: return "top-front-left"sv; + case TopFrontCenter: return "top-front-center"sv; + case TopFrontRight: return "top-front-right"sv; + case TopCenter: return "top-center"sv; + case TopBackLeft: return "top-back-left"sv; + case TopBackCenter: return "top-back-center"sv; + case TopBackRight: return "top-back-right"sv; + + case BottomFrontLeft: return "bottom-front-left"sv; + case BottomFrontRight: return "bottom-front-right"sv; + case BottomBackLeft: return "bottom-back-left"sv; + case BottomBackRight: return "bottom-back-right"sv; + + case Aux0: return "Aux0"sv; + case Aux1: return "Aux1"sv; + case Aux2: return "Aux2"sv; + case Aux3: return "Aux3"sv; + case Aux4: return "Aux4"sv; + case Aux5: return "Aux5"sv; + case Aux6: return "Aux6"sv; + case Aux7: return "Aux7"sv; + case Aux8: return "Aux8"sv; + case Aux9: return "Aux9"sv; + case Aux10: return "Aux10"sv; + case Aux11: return "Aux11"sv; + case Aux12: return "Aux12"sv; + case Aux13: return "Aux13"sv; + case Aux14: return "Aux14"sv; + case Aux15: return "Aux15"sv; + + case MaxChannels: break; } - return "(unknown)"; + return "(unknown)"sv; +} + +[[nodiscard]] +auto GetLayoutName(DevAmbiLayout layout) noexcept -> std::string_view +{ + switch(layout) + { + case DevAmbiLayout::FuMa: return "FuMa"sv; + case DevAmbiLayout::ACN: return "ACN"sv; + } + return ""sv; +} + +[[nodiscard]] +auto GetScalingName(DevAmbiScaling scaling) noexcept -> std::string_view +{ + switch(scaling) + { + case DevAmbiScaling::FuMa: return "FuMa"sv; + case DevAmbiScaling::SN3D: return "SN3D"sv; + case DevAmbiScaling::N3D: return "N3D"sv; + } + return ""sv; } @@ -128,14 +164,14 @@ std::unique_ptr CreateStablizer(const size_t outchans, const uin return stablizer; } -void AllocChannels(ALCdevice *device, const size_t main_chans, const size_t real_chans) +void AllocChannels(al::Device *device, const size_t main_chans, const size_t real_chans) { - TRACE("Channel config, Main: %zu, Real: %zu\n", main_chans, real_chans); + TRACE("Channel config, Main: {}, Real: {}", main_chans, real_chans); /* Allocate extra channels for any post-filter output. */ const size_t num_chans{main_chans + real_chans}; - TRACE("Allocating %zu channels, %zu bytes\n", num_chans, + TRACE("Allocating {} channels, {} bytes", num_chans, num_chans*sizeof(device->MixBuffer[0])); device->MixBuffer.resize(num_chans); al::span buffer{device->MixBuffer}; @@ -227,43 +263,48 @@ struct DecoderConfig { using DecoderView = DecoderConfig; -void InitNearFieldCtrl(ALCdevice *device, float ctrl_dist, uint order, bool is3d) +void InitNearFieldCtrl(al::Device *device, const float ctrl_dist, const uint order, + const bool is3d) { - static const uint chans_per_order2d[MaxAmbiOrder+1]{ 1, 2, 2, 2 }; - static const uint chans_per_order3d[MaxAmbiOrder+1]{ 1, 3, 5, 7 }; + static const std::array chans_per_order2d{{1, 2, 2, 2}}; + static const std::array chans_per_order3d{{1, 3, 5, 7}}; /* NFC is only used when AvgSpeakerDist is greater than 0. */ if(!device->getConfigValueBool("decoder", "nfc", false) || !(ctrl_dist > 0.0f)) return; - device->AvgSpeakerDist = clampf(ctrl_dist, 0.1f, 10.0f); - TRACE("Using near-field reference distance: %.2f meters\n", device->AvgSpeakerDist); + device->AvgSpeakerDist = std::clamp(ctrl_dist, 0.1f, 10.0f); + TRACE("Using near-field reference distance: {:.2f} meters", device->AvgSpeakerDist); const float w1{SpeedOfSoundMetersPerSec / - (device->AvgSpeakerDist * static_cast(device->Frequency))}; + (device->AvgSpeakerDist * static_cast(device->mSampleRate))}; device->mNFCtrlFilter.init(w1); - auto iter = std::copy_n(is3d ? chans_per_order3d : chans_per_order2d, order+1u, - std::begin(device->NumChannelsPerOrder)); - std::fill(iter, std::end(device->NumChannelsPerOrder), 0u); + auto iter = std::copy_n(is3d ? chans_per_order3d.begin() : chans_per_order2d.begin(), order+1u, + device->NumChannelsPerOrder.begin()); + std::fill(iter, device->NumChannelsPerOrder.end(), 0u); } -void InitDistanceComp(ALCdevice *device, const al::span channels, - const al::span dists) +void InitDistanceComp(al::Device *device, const al::span channels, + const al::span dists) { - const float maxdist{std::accumulate(std::begin(dists), std::end(dists), 0.0f, maxf)}; + const float maxdist{std::accumulate(dists.begin(), dists.end(), 0.0f, + [](const float a, const float b) noexcept -> float { return std::max(a, b); })}; if(!device->getConfigValueBool("decoder", "distance-comp", true) || !(maxdist > 0.0f)) return; - const auto distSampleScale = static_cast(device->Frequency) / SpeedOfSoundMetersPerSec; - std::vector ChanDelay; + const auto distSampleScale = static_cast(device->mSampleRate)/SpeedOfSoundMetersPerSec; + + struct DistCoeffs { uint Length{}; float Gain{}; }; + std::vector ChanDelay; ChanDelay.reserve(device->RealOut.Buffer.size()); + size_t total{0u}; for(size_t chidx{0};chidx < channels.size();++chidx) { const Channel ch{channels[chidx]}; - const uint idx{device->RealOut.ChannelIndex[ch]}; + const size_t idx{device->RealOut.ChannelIndex[ch]}; if(idx == InvalidChannelIndex) continue; @@ -278,15 +319,15 @@ void InitDistanceComp(ALCdevice *device, const al::span channels, float delay{std::floor((maxdist - distance)*distSampleScale + 0.5f)}; if(delay > float{DistanceComp::MaxDelay-1}) { - ERR("Delay for channel %u (%s) exceeds buffer length (%f > %d)\n", idx, + ERR("Delay for channel {} ({}) exceeds buffer length ({:f} > {})", idx, GetLabelFromChannel(ch), delay, DistanceComp::MaxDelay-1); delay = float{DistanceComp::MaxDelay-1}; } - ChanDelay.resize(maxz(ChanDelay.size(), idx+1)); + ChanDelay.resize(std::max(ChanDelay.size(), idx+1_uz)); ChanDelay[idx].Length = static_cast(delay); ChanDelay[idx].Gain = distance / maxdist; - TRACE("Channel %s distance comp: %u samples, %f gain\n", GetLabelFromChannel(ch), + TRACE("Channel {} distance comp: {} samples, {:f} gain", GetLabelFromChannel(ch), ChanDelay[idx].Length, ChanDelay[idx].Gain); /* Round up to the next 4th sample, so each channel buffer starts @@ -298,16 +339,17 @@ void InitDistanceComp(ALCdevice *device, const al::span channels, if(total > 0) { auto chandelays = DistanceComp::Create(total); + auto chanbuffer = chandelays->mSamples.begin(); - ChanDelay[0].Buffer = chandelays->mSamples.data(); - auto set_bufptr = [](const DistanceComp::ChanData &last, const DistanceComp::ChanData &cur) - -> DistanceComp::ChanData + auto set_bufptr = [&chanbuffer](const DistCoeffs &data) { - DistanceComp::ChanData ret{cur}; - ret.Buffer = last.Buffer + RoundUp(last.Length, 4); + DistanceComp::ChanData ret{}; + ret.Buffer = al::span{chanbuffer, data.Length}; + ret.Gain = data.Gain; + chanbuffer += ptrdiff_t(RoundUp(data.Length, 4)); return ret; }; - std::partial_sum(ChanDelay.begin(), ChanDelay.end(), chandelays->mChannels.begin(), + std::transform(ChanDelay.begin(), ChanDelay.end(), chandelays->mChannels.begin(), set_bufptr); device->ChannelDelays = std::move(chandelays); } @@ -328,8 +370,8 @@ constexpr auto GetAmbiLayout(DevAmbiLayout layouttype) noexcept } -DecoderView MakeDecoderView(ALCdevice *device, const AmbDecConf *conf, - DecoderConfig &decoder) +auto MakeDecoderView(al::Device *device, const AmbDecConf *conf, + DecoderConfig &decoder) -> DecoderView { DecoderView ret{}; @@ -346,23 +388,20 @@ DecoderView MakeDecoderView(ALCdevice *device, const AmbDecConf *conf, case AmbDecScale::FuMa: decoder.mScaling = DevAmbiScaling::FuMa; break; } - std::copy_n(std::begin(conf->HFOrderGain), - std::min(std::size(conf->HFOrderGain), std::size(decoder.mOrderGain)), - std::begin(decoder.mOrderGain)); - std::copy_n(std::begin(conf->LFOrderGain), - std::min(std::size(conf->LFOrderGain), std::size(decoder.mOrderGainLF)), - std::begin(decoder.mOrderGainLF)); + const auto hfordermin = std::min(conf->HFOrderGain.size(), decoder.mOrderGain.size()); + std::copy_n(conf->HFOrderGain.begin(), hfordermin, decoder.mOrderGain.begin()); + const auto lfordermin = std::min(conf->LFOrderGain.size(), decoder.mOrderGainLF.size()); + std::copy_n(conf->LFOrderGain.begin(), lfordermin, decoder.mOrderGainLF.begin()); const auto num_coeffs = decoder.mIs3D ? AmbiChannelsFromOrder(decoder.mOrder) : Ambi2DChannelsFromOrder(decoder.mOrder); - const auto idx_map = decoder.mIs3D ? AmbiIndex::FromACN.data() - : AmbiIndex::FromACN2D.data(); + const auto idx_map = decoder.mIs3D ? al::span{AmbiIndex::FromACN} + : al::span{AmbiIndex::FromACN2D}; const auto hfmatrix = conf->HFMatrix; const auto lfmatrix = conf->LFMatrix; uint chan_count{0}; - using const_speaker_span = al::span; - for(auto &speaker : const_speaker_span{conf->Speakers.get(), conf->NumSpeakers}) + for(auto &speaker : al::span{std::as_const(conf->Speakers)}) { /* NOTE: AmbDec does not define any standard speaker names, however * for this to work we have to by able to find the output channel @@ -381,44 +420,57 @@ DecoderView MakeDecoderView(ALCdevice *device, const AmbDecConf *conf, * RFT = Top front right * LBT = Top back left * RBT = Top back right + * LFB = Bottom front left + * RFB = Bottom front right + * LBB = Bottom back left + * RBB = Bottom back right * * Additionally, surround51 will acknowledge back speakers for side * channels, to avoid issues with an ambdec expecting 5.1 to use the * back channels. */ Channel ch{}; - if(speaker.Name == "LF") + if(speaker.Name == "LF"sv) ch = FrontLeft; - else if(speaker.Name == "RF") + else if(speaker.Name == "RF"sv) ch = FrontRight; - else if(speaker.Name == "CE") + else if(speaker.Name == "CE"sv) ch = FrontCenter; - else if(speaker.Name == "LS") + else if(speaker.Name == "LS"sv) ch = SideLeft; - else if(speaker.Name == "RS") + else if(speaker.Name == "RS"sv) ch = SideRight; - else if(speaker.Name == "LB") + else if(speaker.Name == "LB"sv) ch = (device->FmtChans == DevFmtX51) ? SideLeft : BackLeft; - else if(speaker.Name == "RB") + else if(speaker.Name == "RB"sv) ch = (device->FmtChans == DevFmtX51) ? SideRight : BackRight; - else if(speaker.Name == "CB") + else if(speaker.Name == "CB"sv) ch = BackCenter; - else if(speaker.Name == "LFT") + else if(speaker.Name == "LFT"sv) ch = TopFrontLeft; - else if(speaker.Name == "RFT") + else if(speaker.Name == "RFT"sv) ch = TopFrontRight; - else if(speaker.Name == "LBT") + else if(speaker.Name == "LBT"sv) ch = TopBackLeft; - else if(speaker.Name == "RBT") + else if(speaker.Name == "RBT"sv) ch = TopBackRight; + else if(speaker.Name == "LFB"sv) + ch = BottomFrontLeft; + else if(speaker.Name == "RFB"sv) + ch = BottomFrontRight; + else if(speaker.Name == "LBB"sv) + ch = BottomBackLeft; + else if(speaker.Name == "RBB"sv) + ch = BottomBackRight; else { int idx{}; char c{}; + /* NOLINTNEXTLINE(cert-err34-c,cppcoreguidelines-pro-type-vararg) */ if(sscanf(speaker.Name.c_str(), "AUX%d%c", &idx, &c) != 1 || idx < 0 || idx >= MaxChannels-Aux0) { - ERR("AmbDec speaker label \"%s\" not recognized\n", speaker.Name.c_str()); + ERR("AmbDec speaker label \"{}\" not recognized", speaker.Name); continue; } ch = static_cast(Aux0+idx); @@ -446,13 +498,13 @@ DecoderView MakeDecoderView(ALCdevice *device, const AmbDecConf *conf, ret.mOrder = decoder.mOrder; ret.mIs3D = decoder.mIs3D; ret.mScaling = decoder.mScaling; - ret.mChannels = {decoder.mChannels.data(), chan_count}; + ret.mChannels = al::span{decoder.mChannels}.first(chan_count); ret.mOrderGain = decoder.mOrderGain; - ret.mCoeffs = {decoder.mCoeffs.data(), chan_count}; + ret.mCoeffs = al::span{decoder.mCoeffs}.first(chan_count); if(conf->FreqBands > 1) { ret.mOrderGainLF = decoder.mOrderGainLF; - ret.mCoeffsLF = {decoder.mCoeffsLF.data(), chan_count}; + ret.mCoeffsLF = al::span{decoder.mCoeffsLF}.first(chan_count); } } return ret; @@ -584,8 +636,46 @@ constexpr DecoderConfig X714Config{ {{8.80892603e-02f, -7.48948724e-02f, 9.08779842e-02f, -6.22480443e-02f}}, }} }; +constexpr DecoderConfig X7144Config{ + 1, true, {{BackLeft, SideLeft, FrontLeft, FrontRight, SideRight, BackRight, TopBackLeft, TopFrontLeft, TopFrontRight, TopBackRight, BottomBackLeft, BottomFrontLeft, BottomFrontRight, BottomBackRight}}, + DevAmbiScaling::N3D, + /*HF*/{{2.64575131e+0f, 1.52752523e+0f}}, + {{ + {{7.14285714e-02f, 5.09426708e-02f, 0.00000000e+00f, -8.82352941e-02f}}, + {{7.14285714e-02f, 1.01885342e-01f, 0.00000000e+00f, 0.00000000e+00f}}, + {{7.14285714e-02f, 5.09426708e-02f, 0.00000000e+00f, 8.82352941e-02f}}, + {{7.14285714e-02f, -5.09426708e-02f, 0.00000000e+00f, 8.82352941e-02f}}, + {{7.14285714e-02f, -1.01885342e-01f, 0.00000000e+00f, 0.00000000e+00f}}, + {{7.14285714e-02f, -5.09426708e-02f, 0.00000000e+00f, -8.82352941e-02f}}, + {{7.14285714e-02f, 5.88235294e-02f, 1.25000000e-01f, -5.88235294e-02f}}, + {{7.14285714e-02f, 5.88235294e-02f, 1.25000000e-01f, 5.88235294e-02f}}, + {{7.14285714e-02f, -5.88235294e-02f, 1.25000000e-01f, 5.88235294e-02f}}, + {{7.14285714e-02f, -5.88235294e-02f, 1.25000000e-01f, -5.88235294e-02f}}, + {{7.14285714e-02f, 5.88235294e-02f, -1.25000000e-01f, -5.88235294e-02f}}, + {{7.14285714e-02f, 5.88235294e-02f, -1.25000000e-01f, 5.88235294e-02f}}, + {{7.14285714e-02f, -5.88235294e-02f, -1.25000000e-01f, 5.88235294e-02f}}, + {{7.14285714e-02f, -5.88235294e-02f, -1.25000000e-01f, -5.88235294e-02f}}, + }}, + /*LF*/{{1.00000000e+0f, 1.00000000e+0f}}, + {{ + {{7.14285714e-02f, 5.09426708e-02f, 0.00000000e+00f, -8.82352941e-02f}}, + {{7.14285714e-02f, 1.01885342e-01f, 0.00000000e+00f, 0.00000000e+00f}}, + {{7.14285714e-02f, 5.09426708e-02f, 0.00000000e+00f, 8.82352941e-02f}}, + {{7.14285714e-02f, -5.09426708e-02f, 0.00000000e+00f, 8.82352941e-02f}}, + {{7.14285714e-02f, -1.01885342e-01f, 0.00000000e+00f, 0.00000000e+00f}}, + {{7.14285714e-02f, -5.09426708e-02f, 0.00000000e+00f, -8.82352941e-02f}}, + {{7.14285714e-02f, 5.88235294e-02f, 1.25000000e-01f, -5.88235294e-02f}}, + {{7.14285714e-02f, 5.88235294e-02f, 1.25000000e-01f, 5.88235294e-02f}}, + {{7.14285714e-02f, -5.88235294e-02f, 1.25000000e-01f, 5.88235294e-02f}}, + {{7.14285714e-02f, -5.88235294e-02f, 1.25000000e-01f, -5.88235294e-02f}}, + {{7.14285714e-02f, 5.88235294e-02f, -1.25000000e-01f, -5.88235294e-02f}}, + {{7.14285714e-02f, 5.88235294e-02f, -1.25000000e-01f, 5.88235294e-02f}}, + {{7.14285714e-02f, -5.88235294e-02f, -1.25000000e-01f, 5.88235294e-02f}}, + {{7.14285714e-02f, -5.88235294e-02f, -1.25000000e-01f, -5.88235294e-02f}}, + }} +}; -void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize=false, +void InitPanning(al::Device *device, const bool hqdec=false, const bool stablize=false, DecoderView decoder={}) { if(!decoder) @@ -599,14 +689,15 @@ void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize= case DevFmtX61: decoder = X61Config; break; case DevFmtX71: decoder = X71Config; break; case DevFmtX714: decoder = X714Config; break; + case DevFmtX7144: decoder = X7144Config; break; case DevFmtX3D71: decoder = X3D71Config; break; case DevFmtAmbi3D: - const auto acnmap = GetAmbiLayout(device->mAmbiLayout); - const auto n3dscale = GetAmbiScales(device->mAmbiScale); - /* For DevFmtAmbi3D, the ambisonic order is already set. */ const size_t count{AmbiChannelsFromOrder(device->mAmbiOrder)}; - std::transform(acnmap.begin(), acnmap.begin()+count, std::begin(device->Dry.AmbiMap), + const auto acnmap = GetAmbiLayout(device->mAmbiLayout).first(count); + const auto n3dscale = GetAmbiScales(device->mAmbiScale); + + std::transform(acnmap.cbegin(), acnmap.cend(), device->Dry.AmbiMap.begin(), [n3dscale](const uint8_t &acn) noexcept -> BFChannelConfig { return BFChannelConfig{1.0f/n3dscale[acn], acn}; }); AllocChannels(device, count, 0); @@ -617,10 +708,13 @@ void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize= avg_dist = *distopt; else if(auto delayopt = device->configValue("decoder", "nfc-ref-delay")) { - WARN("nfc-ref-delay is deprecated, use speaker-dist instead\n"); + WARN("nfc-ref-delay is deprecated, use speaker-dist instead"); avg_dist = *delayopt * SpeedOfSoundMetersPerSec; } + TRACE("{}{} order ambisonic output ({} layout, {} scaling)", device->mAmbiOrder, + GetCounterSuffix(device->mAmbiOrder), GetLayoutName(device->mAmbiLayout), + GetScalingName(device->mAmbiScale)); InitNearFieldCtrl(device, avg_dist, device->mAmbiOrder, true); return; } @@ -632,18 +726,18 @@ void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize= std::vector chancoeffs, chancoeffslf; for(size_t i{0u};i < decoder.mChannels.size();++i) { - const uint idx{device->channelIdxByName(decoder.mChannels[i])}; + const size_t idx{device->channelIdxByName(decoder.mChannels[i])}; if(idx == InvalidChannelIndex) { - ERR("Failed to find %s channel in device\n", + ERR("Failed to find {} channel in device", GetLabelFromChannel(decoder.mChannels[i])); continue; } - auto ordermap = decoder.mIs3D ? AmbiIndex::OrderFromChannel.data() - : AmbiIndex::OrderFrom2DChannel.data(); + auto ordermap = decoder.mIs3D ? al::span{AmbiIndex::OrderFromChannel} + : al::span{AmbiIndex::OrderFrom2DChannel}; - chancoeffs.resize(maxz(chancoeffs.size(), idx+1u), ChannelDec{}); + chancoeffs.resize(std::max(chancoeffs.size(), idx+1_zu), ChannelDec{}); al::span src{decoder.mCoeffs[i]}; al::span dst{chancoeffs[idx]}; for(size_t ambichan{0};ambichan < ambicount;++ambichan) @@ -652,7 +746,7 @@ void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize= if(!dual_band) continue; - chancoeffslf.resize(maxz(chancoeffslf.size(), idx+1u), ChannelDec{}); + chancoeffslf.resize(std::max(chancoeffslf.size(), idx+1_zu), ChannelDec{}); src = decoder.mCoeffsLF[i]; dst = chancoeffslf[idx]; for(size_t ambichan{0};ambichan < ambicount;++ambichan) @@ -663,10 +757,10 @@ void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize= device->mAmbiOrder = decoder.mOrder; device->m2DMixing = !decoder.mIs3D; - const al::span acnmap{decoder.mIs3D ? AmbiIndex::FromACN.data() : - AmbiIndex::FromACN2D.data(), ambicount}; + const auto acnmap = decoder.mIs3D ? al::span{AmbiIndex::FromACN}.first(ambicount) + : al::span{AmbiIndex::FromACN2D}.first(ambicount); const auto coeffscale = GetAmbiScales(decoder.mScaling); - std::transform(acnmap.begin(), acnmap.end(), std::begin(device->Dry.AmbiMap), + std::transform(acnmap.begin(), acnmap.end(), device->Dry.AmbiMap.begin(), [coeffscale](const uint8_t &acn) noexcept { return BFChannelConfig{1.0f/coeffscale[acn], acn}; }); AllocChannels(device, ambicount, device->channelsFromFmt()); @@ -677,7 +771,7 @@ void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize= /* Only enable the stablizer if the decoder does not output to the * front-center channel. */ - const auto cidx = device->RealOut.ChannelIndex[FrontCenter]; + const size_t cidx{device->RealOut.ChannelIndex[FrontCenter]}; bool hasfc{false}; if(cidx < chancoeffs.size()) { @@ -691,137 +785,142 @@ void InitPanning(ALCdevice *device, const bool hqdec=false, const bool stablize= } if(!hasfc) { - stablizer = CreateStablizer(device->channelsFromFmt(), device->Frequency); - TRACE("Front stablizer enabled\n"); + stablizer = CreateStablizer(device->channelsFromFmt(), device->mSampleRate); + TRACE("Front stablizer enabled"); } } - TRACE("Enabling %s-band %s-order%s ambisonic decoder\n", - !dual_band ? "single" : "dual", + TRACE("Enabling {}-band {}-order{} ambisonic decoder", !dual_band ? "single" : "dual", (decoder.mOrder > 3) ? "fourth" : (decoder.mOrder > 2) ? "third" : (decoder.mOrder > 1) ? "second" : "first", decoder.mIs3D ? " periphonic" : ""); device->AmbiDecoder = BFormatDec::Create(ambicount, chancoeffs, chancoeffslf, - device->mXOverFreq/static_cast(device->Frequency), std::move(stablizer)); + device->mXOverFreq/static_cast(device->mSampleRate), std::move(stablizer)); } -void InitHrtfPanning(ALCdevice *device) +void InitHrtfPanning(al::Device *device) { - constexpr float Deg180{al::numbers::pi_v}; - constexpr float Deg_90{Deg180 / 2.0f /* 90 degrees*/}; - constexpr float Deg_45{Deg_90 / 2.0f /* 45 degrees*/}; - constexpr float Deg135{Deg_45 * 3.0f /*135 degrees*/}; - constexpr float Deg_21{3.648638281e-01f /* 20~ 21 degrees*/}; - constexpr float Deg_32{5.535743589e-01f /* 31~ 32 degrees*/}; - constexpr float Deg_35{6.154797087e-01f /* 35~ 36 degrees*/}; - constexpr float Deg_58{1.017221968e+00f /* 58~ 59 degrees*/}; - constexpr float Deg_69{1.205932499e+00f /* 69~ 70 degrees*/}; - constexpr float Deg111{1.935660155e+00f /*110~111 degrees*/}; - constexpr float Deg122{2.124370686e+00f /*121~122 degrees*/}; - static const AngularPoint AmbiPoints1O[]{ - { EvRadians{ Deg_35}, AzRadians{-Deg_45} }, - { EvRadians{ Deg_35}, AzRadians{-Deg135} }, - { EvRadians{ Deg_35}, AzRadians{ Deg_45} }, - { EvRadians{ Deg_35}, AzRadians{ Deg135} }, - { EvRadians{-Deg_35}, AzRadians{-Deg_45} }, - { EvRadians{-Deg_35}, AzRadians{-Deg135} }, - { EvRadians{-Deg_35}, AzRadians{ Deg_45} }, - { EvRadians{-Deg_35}, AzRadians{ Deg135} }, - }, AmbiPoints2O[]{ - { EvRadians{-Deg_32}, AzRadians{ 0.0f} }, - { EvRadians{ 0.0f}, AzRadians{ Deg_58} }, - { EvRadians{ Deg_58}, AzRadians{ Deg_90} }, - { EvRadians{ Deg_32}, AzRadians{ 0.0f} }, - { EvRadians{ 0.0f}, AzRadians{ Deg122} }, - { EvRadians{-Deg_58}, AzRadians{-Deg_90} }, - { EvRadians{-Deg_32}, AzRadians{ Deg180} }, - { EvRadians{ 0.0f}, AzRadians{-Deg122} }, - { EvRadians{ Deg_58}, AzRadians{-Deg_90} }, - { EvRadians{ Deg_32}, AzRadians{ Deg180} }, - { EvRadians{ 0.0f}, AzRadians{-Deg_58} }, - { EvRadians{-Deg_58}, AzRadians{ Deg_90} }, - }, AmbiPoints3O[]{ - { EvRadians{ Deg_69}, AzRadians{-Deg_90} }, - { EvRadians{ Deg_69}, AzRadians{ Deg_90} }, - { EvRadians{-Deg_69}, AzRadians{-Deg_90} }, - { EvRadians{-Deg_69}, AzRadians{ Deg_90} }, - { EvRadians{ 0.0f}, AzRadians{-Deg_69} }, - { EvRadians{ 0.0f}, AzRadians{-Deg111} }, - { EvRadians{ 0.0f}, AzRadians{ Deg_69} }, - { EvRadians{ 0.0f}, AzRadians{ Deg111} }, - { EvRadians{ Deg_21}, AzRadians{ 0.0f} }, - { EvRadians{ Deg_21}, AzRadians{ Deg180} }, - { EvRadians{-Deg_21}, AzRadians{ 0.0f} }, - { EvRadians{-Deg_21}, AzRadians{ Deg180} }, - { EvRadians{ Deg_35}, AzRadians{-Deg_45} }, - { EvRadians{ Deg_35}, AzRadians{-Deg135} }, - { EvRadians{ Deg_35}, AzRadians{ Deg_45} }, - { EvRadians{ Deg_35}, AzRadians{ Deg135} }, - { EvRadians{-Deg_35}, AzRadians{-Deg_45} }, - { EvRadians{-Deg_35}, AzRadians{-Deg135} }, - { EvRadians{-Deg_35}, AzRadians{ Deg_45} }, - { EvRadians{-Deg_35}, AzRadians{ Deg135} }, + static constexpr float Deg180{al::numbers::pi_v}; + static constexpr float Deg_90{Deg180 / 2.0f /* 90 degrees*/}; + static constexpr float Deg_45{Deg_90 / 2.0f /* 45 degrees*/}; + static constexpr float Deg135{Deg_45 * 3.0f /*135 degrees*/}; + static constexpr float Deg_21{3.648638281e-01f /* 20~ 21 degrees*/}; + static constexpr float Deg_32{5.535743589e-01f /* 31~ 32 degrees*/}; + static constexpr float Deg_35{6.154797087e-01f /* 35~ 36 degrees*/}; + static constexpr float Deg_58{1.017221968e+00f /* 58~ 59 degrees*/}; + static constexpr float Deg_69{1.205932499e+00f /* 69~ 70 degrees*/}; + static constexpr float Deg111{1.935660155e+00f /*110~111 degrees*/}; + static constexpr float Deg122{2.124370686e+00f /*121~122 degrees*/}; + static constexpr std::array AmbiPoints1O{ + AngularPoint{EvRadians{ Deg_35}, AzRadians{-Deg_45}}, + AngularPoint{EvRadians{ Deg_35}, AzRadians{-Deg135}}, + AngularPoint{EvRadians{ Deg_35}, AzRadians{ Deg_45}}, + AngularPoint{EvRadians{ Deg_35}, AzRadians{ Deg135}}, + AngularPoint{EvRadians{-Deg_35}, AzRadians{-Deg_45}}, + AngularPoint{EvRadians{-Deg_35}, AzRadians{-Deg135}}, + AngularPoint{EvRadians{-Deg_35}, AzRadians{ Deg_45}}, + AngularPoint{EvRadians{-Deg_35}, AzRadians{ Deg135}}, + }; + static constexpr std::array AmbiPoints2O{ + AngularPoint{EvRadians{-Deg_32}, AzRadians{ 0.0f}}, + AngularPoint{EvRadians{ 0.0f}, AzRadians{ Deg_58}}, + AngularPoint{EvRadians{ Deg_58}, AzRadians{ Deg_90}}, + AngularPoint{EvRadians{ Deg_32}, AzRadians{ 0.0f}}, + AngularPoint{EvRadians{ 0.0f}, AzRadians{ Deg122}}, + AngularPoint{EvRadians{-Deg_58}, AzRadians{-Deg_90}}, + AngularPoint{EvRadians{-Deg_32}, AzRadians{ Deg180}}, + AngularPoint{EvRadians{ 0.0f}, AzRadians{-Deg122}}, + AngularPoint{EvRadians{ Deg_58}, AzRadians{-Deg_90}}, + AngularPoint{EvRadians{ Deg_32}, AzRadians{ Deg180}}, + AngularPoint{EvRadians{ 0.0f}, AzRadians{-Deg_58}}, + AngularPoint{EvRadians{-Deg_58}, AzRadians{ Deg_90}}, }; - static const float AmbiMatrix1O[][MaxAmbiChannels]{ - { 1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f }, - { 1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f }, - { 1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f }, - { 1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f }, - { 1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f }, - { 1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f }, - { 1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f }, - { 1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f }, - }, AmbiMatrix2O[][MaxAmbiChannels]{ - { 8.333333333e-02f, 0.000000000e+00f, -7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, -1.443375673e-01f, 1.167715449e-01f, }, - { 8.333333333e-02f, -1.227808683e-01f, 0.000000000e+00f, 7.588274978e-02f, -1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f, }, - { 8.333333333e-02f, -7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f, }, - { 8.333333333e-02f, 0.000000000e+00f, 7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, 1.443375673e-01f, 1.167715449e-01f, }, - { 8.333333333e-02f, -1.227808683e-01f, 0.000000000e+00f, -7.588274978e-02f, 1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f, }, - { 8.333333333e-02f, 7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f, }, - { 8.333333333e-02f, 0.000000000e+00f, -7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, 1.443375673e-01f, 1.167715449e-01f, }, - { 8.333333333e-02f, 1.227808683e-01f, 0.000000000e+00f, -7.588274978e-02f, -1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f, }, - { 8.333333333e-02f, 7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, 1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f, }, - { 8.333333333e-02f, 0.000000000e+00f, 7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, -1.443375673e-01f, 1.167715449e-01f, }, - { 8.333333333e-02f, 1.227808683e-01f, 0.000000000e+00f, 7.588274978e-02f, 1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f, }, - { 8.333333333e-02f, -7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, 1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f, }, - }, AmbiMatrix3O[][MaxAmbiChannels]{ - { 5.000000000e-02f, 3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, 6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, -1.256118221e-01f, 0.000000000e+00f, 1.126112056e-01f, 7.944389175e-02f, 0.000000000e+00f, 2.421151497e-02f, 0.000000000e+00f, }, - { 5.000000000e-02f, -3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, 1.256118221e-01f, 0.000000000e+00f, -1.126112056e-01f, 7.944389175e-02f, 0.000000000e+00f, 2.421151497e-02f, 0.000000000e+00f, }, - { 5.000000000e-02f, 3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, -1.256118221e-01f, 0.000000000e+00f, 1.126112056e-01f, -7.944389175e-02f, 0.000000000e+00f, -2.421151497e-02f, 0.000000000e+00f, }, - { 5.000000000e-02f, -3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, 6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, 1.256118221e-01f, 0.000000000e+00f, -1.126112056e-01f, -7.944389175e-02f, 0.000000000e+00f, -2.421151497e-02f, 0.000000000e+00f, }, - { 5.000000000e-02f, 8.090169944e-02f, 0.000000000e+00f, 3.090169944e-02f, 6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, -7.763237543e-02f, 0.000000000e+00f, -2.950836627e-02f, 0.000000000e+00f, -1.497759251e-01f, 0.000000000e+00f, -7.763237543e-02f, }, - { 5.000000000e-02f, 8.090169944e-02f, 0.000000000e+00f, -3.090169944e-02f, -6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, -7.763237543e-02f, 0.000000000e+00f, -2.950836627e-02f, 0.000000000e+00f, 1.497759251e-01f, 0.000000000e+00f, 7.763237543e-02f, }, - { 5.000000000e-02f, -8.090169944e-02f, 0.000000000e+00f, 3.090169944e-02f, -6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, 7.763237543e-02f, 0.000000000e+00f, 2.950836627e-02f, 0.000000000e+00f, -1.497759251e-01f, 0.000000000e+00f, -7.763237543e-02f, }, - { 5.000000000e-02f, -8.090169944e-02f, 0.000000000e+00f, -3.090169944e-02f, 6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, 7.763237543e-02f, 0.000000000e+00f, 2.950836627e-02f, 0.000000000e+00f, 1.497759251e-01f, 0.000000000e+00f, 7.763237543e-02f, }, - { 5.000000000e-02f, 0.000000000e+00f, 3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, 6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, 3.034486645e-02f, -6.779013272e-02f, 1.659481923e-01f, 4.797944664e-02f, }, - { 5.000000000e-02f, 0.000000000e+00f, 3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, -6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, 3.034486645e-02f, 6.779013272e-02f, 1.659481923e-01f, -4.797944664e-02f, }, - { 5.000000000e-02f, 0.000000000e+00f, -3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, -6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, -3.034486645e-02f, -6.779013272e-02f, -1.659481923e-01f, 4.797944664e-02f, }, - { 5.000000000e-02f, 0.000000000e+00f, -3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, 6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, -3.034486645e-02f, 6.779013272e-02f, -1.659481923e-01f, -4.797944664e-02f, }, - { 5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, 6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, 6.338656910e-02f, -1.092600649e-02f, -7.364853795e-02f, 1.011266756e-01f, -7.086833869e-02f, -1.482646439e-02f, }, - { 5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, -6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, -6.338656910e-02f, -1.092600649e-02f, -7.364853795e-02f, -1.011266756e-01f, -7.086833869e-02f, 1.482646439e-02f, }, - { 5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, -6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, -6.338656910e-02f, 1.092600649e-02f, -7.364853795e-02f, 1.011266756e-01f, -7.086833869e-02f, -1.482646439e-02f, }, - { 5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, 6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, 6.338656910e-02f, 1.092600649e-02f, -7.364853795e-02f, -1.011266756e-01f, -7.086833869e-02f, 1.482646439e-02f, }, - { 5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, 6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, -6.338656910e-02f, -1.092600649e-02f, 7.364853795e-02f, 1.011266756e-01f, 7.086833869e-02f, -1.482646439e-02f, }, - { 5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, -6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, 6.338656910e-02f, -1.092600649e-02f, 7.364853795e-02f, -1.011266756e-01f, 7.086833869e-02f, 1.482646439e-02f, }, - { 5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, -6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, 6.338656910e-02f, 1.092600649e-02f, 7.364853795e-02f, 1.011266756e-01f, 7.086833869e-02f, -1.482646439e-02f, }, - { 5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, 6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, -6.338656910e-02f, 1.092600649e-02f, 7.364853795e-02f, -1.011266756e-01f, 7.086833869e-02f, 1.482646439e-02f, }, + static constexpr std::array AmbiPoints3O{ + AngularPoint{EvRadians{ Deg_69}, AzRadians{-Deg_90}}, + AngularPoint{EvRadians{ Deg_69}, AzRadians{ Deg_90}}, + AngularPoint{EvRadians{-Deg_69}, AzRadians{-Deg_90}}, + AngularPoint{EvRadians{-Deg_69}, AzRadians{ Deg_90}}, + AngularPoint{EvRadians{ 0.0f}, AzRadians{-Deg_69}}, + AngularPoint{EvRadians{ 0.0f}, AzRadians{-Deg111}}, + AngularPoint{EvRadians{ 0.0f}, AzRadians{ Deg_69}}, + AngularPoint{EvRadians{ 0.0f}, AzRadians{ Deg111}}, + AngularPoint{EvRadians{ Deg_21}, AzRadians{ 0.0f}}, + AngularPoint{EvRadians{ Deg_21}, AzRadians{ Deg180}}, + AngularPoint{EvRadians{-Deg_21}, AzRadians{ 0.0f}}, + AngularPoint{EvRadians{-Deg_21}, AzRadians{ Deg180}}, + AngularPoint{EvRadians{ Deg_35}, AzRadians{-Deg_45}}, + AngularPoint{EvRadians{ Deg_35}, AzRadians{-Deg135}}, + AngularPoint{EvRadians{ Deg_35}, AzRadians{ Deg_45}}, + AngularPoint{EvRadians{ Deg_35}, AzRadians{ Deg135}}, + AngularPoint{EvRadians{-Deg_35}, AzRadians{-Deg_45}}, + AngularPoint{EvRadians{-Deg_35}, AzRadians{-Deg135}}, + AngularPoint{EvRadians{-Deg_35}, AzRadians{ Deg_45}}, + AngularPoint{EvRadians{-Deg_35}, AzRadians{ Deg135}}, }; - static const float AmbiOrderHFGain1O[MaxAmbiOrder+1]{ + static constexpr std::array AmbiMatrix1O{ + ChannelCoeffs{1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f}, + ChannelCoeffs{1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f}, + ChannelCoeffs{1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f}, + ChannelCoeffs{1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f}, + ChannelCoeffs{1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f}, + ChannelCoeffs{1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f}, + ChannelCoeffs{1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f}, + ChannelCoeffs{1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f}, + }; + static constexpr std::array AmbiMatrix2O{ + ChannelCoeffs{8.333333333e-02f, 0.000000000e+00f, -7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, -1.443375673e-01f, 1.167715449e-01f}, + ChannelCoeffs{8.333333333e-02f, -1.227808683e-01f, 0.000000000e+00f, 7.588274978e-02f, -1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f}, + ChannelCoeffs{8.333333333e-02f, -7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f}, + ChannelCoeffs{8.333333333e-02f, 0.000000000e+00f, 7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, 1.443375673e-01f, 1.167715449e-01f}, + ChannelCoeffs{8.333333333e-02f, -1.227808683e-01f, 0.000000000e+00f, -7.588274978e-02f, 1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f}, + ChannelCoeffs{8.333333333e-02f, 7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f}, + ChannelCoeffs{8.333333333e-02f, 0.000000000e+00f, -7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, 1.443375673e-01f, 1.167715449e-01f}, + ChannelCoeffs{8.333333333e-02f, 1.227808683e-01f, 0.000000000e+00f, -7.588274978e-02f, -1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f}, + ChannelCoeffs{8.333333333e-02f, 7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, 1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f}, + ChannelCoeffs{8.333333333e-02f, 0.000000000e+00f, 7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, -1.443375673e-01f, 1.167715449e-01f}, + ChannelCoeffs{8.333333333e-02f, 1.227808683e-01f, 0.000000000e+00f, 7.588274978e-02f, 1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f}, + ChannelCoeffs{8.333333333e-02f, -7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, 1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f}, + }; + static constexpr std::array AmbiMatrix3O{ + ChannelCoeffs{5.000000000e-02f, 3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, 6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, -1.256118221e-01f, 0.000000000e+00f, 1.126112056e-01f, 7.944389175e-02f, 0.000000000e+00f, 2.421151497e-02f, 0.000000000e+00f}, + ChannelCoeffs{5.000000000e-02f, -3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, 1.256118221e-01f, 0.000000000e+00f, -1.126112056e-01f, 7.944389175e-02f, 0.000000000e+00f, 2.421151497e-02f, 0.000000000e+00f}, + ChannelCoeffs{5.000000000e-02f, 3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, -1.256118221e-01f, 0.000000000e+00f, 1.126112056e-01f, -7.944389175e-02f, 0.000000000e+00f, -2.421151497e-02f, 0.000000000e+00f}, + ChannelCoeffs{5.000000000e-02f, -3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, 6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, 1.256118221e-01f, 0.000000000e+00f, -1.126112056e-01f, -7.944389175e-02f, 0.000000000e+00f, -2.421151497e-02f, 0.000000000e+00f}, + ChannelCoeffs{5.000000000e-02f, 8.090169944e-02f, 0.000000000e+00f, 3.090169944e-02f, 6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, -7.763237543e-02f, 0.000000000e+00f, -2.950836627e-02f, 0.000000000e+00f, -1.497759251e-01f, 0.000000000e+00f, -7.763237543e-02f}, + ChannelCoeffs{5.000000000e-02f, 8.090169944e-02f, 0.000000000e+00f, -3.090169944e-02f, -6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, -7.763237543e-02f, 0.000000000e+00f, -2.950836627e-02f, 0.000000000e+00f, 1.497759251e-01f, 0.000000000e+00f, 7.763237543e-02f}, + ChannelCoeffs{5.000000000e-02f, -8.090169944e-02f, 0.000000000e+00f, 3.090169944e-02f, -6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, 7.763237543e-02f, 0.000000000e+00f, 2.950836627e-02f, 0.000000000e+00f, -1.497759251e-01f, 0.000000000e+00f, -7.763237543e-02f}, + ChannelCoeffs{5.000000000e-02f, -8.090169944e-02f, 0.000000000e+00f, -3.090169944e-02f, 6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, 7.763237543e-02f, 0.000000000e+00f, 2.950836627e-02f, 0.000000000e+00f, 1.497759251e-01f, 0.000000000e+00f, 7.763237543e-02f}, + ChannelCoeffs{5.000000000e-02f, 0.000000000e+00f, 3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, 6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, 3.034486645e-02f, -6.779013272e-02f, 1.659481923e-01f, 4.797944664e-02f}, + ChannelCoeffs{5.000000000e-02f, 0.000000000e+00f, 3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, -6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, 3.034486645e-02f, 6.779013272e-02f, 1.659481923e-01f, -4.797944664e-02f}, + ChannelCoeffs{5.000000000e-02f, 0.000000000e+00f, -3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, -6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, -3.034486645e-02f, -6.779013272e-02f, -1.659481923e-01f, 4.797944664e-02f}, + ChannelCoeffs{5.000000000e-02f, 0.000000000e+00f, -3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, 6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, -3.034486645e-02f, 6.779013272e-02f, -1.659481923e-01f, -4.797944664e-02f}, + ChannelCoeffs{5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, 6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, 6.338656910e-02f, -1.092600649e-02f, -7.364853795e-02f, 1.011266756e-01f, -7.086833869e-02f, -1.482646439e-02f}, + ChannelCoeffs{5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, -6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, -6.338656910e-02f, -1.092600649e-02f, -7.364853795e-02f, -1.011266756e-01f, -7.086833869e-02f, 1.482646439e-02f}, + ChannelCoeffs{5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, -6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, -6.338656910e-02f, 1.092600649e-02f, -7.364853795e-02f, 1.011266756e-01f, -7.086833869e-02f, -1.482646439e-02f}, + ChannelCoeffs{5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, 6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, 6.338656910e-02f, 1.092600649e-02f, -7.364853795e-02f, -1.011266756e-01f, -7.086833869e-02f, 1.482646439e-02f}, + ChannelCoeffs{5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, 6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, -6.338656910e-02f, -1.092600649e-02f, 7.364853795e-02f, 1.011266756e-01f, 7.086833869e-02f, -1.482646439e-02f}, + ChannelCoeffs{5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, -6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, 6.338656910e-02f, -1.092600649e-02f, 7.364853795e-02f, -1.011266756e-01f, 7.086833869e-02f, 1.482646439e-02f}, + ChannelCoeffs{5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, -6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, 6.338656910e-02f, 1.092600649e-02f, 7.364853795e-02f, 1.011266756e-01f, 7.086833869e-02f, -1.482646439e-02f}, + ChannelCoeffs{5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, 6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, -6.338656910e-02f, 1.092600649e-02f, 7.364853795e-02f, -1.011266756e-01f, 7.086833869e-02f, 1.482646439e-02f}, + }; + static constexpr std::array AmbiOrderHFGain1O{ /*ENRGY*/ 2.000000000e+00f, 1.154700538e+00f - }, AmbiOrderHFGain2O[MaxAmbiOrder+1]{ + }; + static constexpr std::array AmbiOrderHFGain2O{ /*ENRGY*/ 1.825741858e+00f, 1.414213562e+00f, 7.302967433e-01f /*AMP 1.000000000e+00f, 7.745966692e-01f, 4.000000000e-01f*/ /*RMS 9.128709292e-01f, 7.071067812e-01f, 3.651483717e-01f*/ - }, AmbiOrderHFGain3O[MaxAmbiOrder+1]{ + }; + static constexpr std::array AmbiOrderHFGain3O{ /*ENRGY 1.865086714e+00f, 1.606093894e+00f, 1.142055301e+00f, 5.683795528e-01f*/ /*AMP*/ 1.000000000e+00f, 8.611363116e-01f, 6.123336207e-01f, 3.047469850e-01f /*RMS 8.340921354e-01f, 7.182670250e-01f, 5.107426573e-01f, 2.541870634e-01f*/ }; - static_assert(std::size(AmbiPoints1O) == std::size(AmbiMatrix1O), "First-Order Ambisonic HRTF mismatch"); - static_assert(std::size(AmbiPoints2O) == std::size(AmbiMatrix2O), "Second-Order Ambisonic HRTF mismatch"); - static_assert(std::size(AmbiPoints3O) == std::size(AmbiMatrix3O), "Third-Order Ambisonic HRTF mismatch"); + static_assert(AmbiPoints1O.size() == AmbiMatrix1O.size(), "First-Order Ambisonic HRTF mismatch"); + static_assert(AmbiPoints2O.size() == AmbiMatrix2O.size(), "Second-Order Ambisonic HRTF mismatch"); + static_assert(AmbiPoints3O.size() == AmbiMatrix3O.size(), "Third-Order Ambisonic HRTF mismatch"); /* A 700hz crossover frequency provides tighter sound imaging at the sweet * spot with ambisonic decoding, as the distance between the ears is closer @@ -841,50 +940,46 @@ void InitHrtfPanning(ALCdevice *device) */ device->mRenderMode = RenderMode::Hrtf; uint ambi_order{1}; - if(auto modeopt = device->configValue(nullptr, "hrtf-mode")) + if(auto modeopt = device->configValue({}, "hrtf-mode")) { struct HrtfModeEntry { - char name[8]; + std::string_view name; RenderMode mode; uint order; }; - static const HrtfModeEntry hrtf_modes[]{ - { "full", RenderMode::Hrtf, 1 }, - { "ambi1", RenderMode::Normal, 1 }, - { "ambi2", RenderMode::Normal, 2 }, - { "ambi3", RenderMode::Normal, 3 }, + constexpr std::array hrtf_modes{ + HrtfModeEntry{"full"sv, RenderMode::Hrtf, 1}, + HrtfModeEntry{"ambi1"sv, RenderMode::Normal, 1}, + HrtfModeEntry{"ambi2"sv, RenderMode::Normal, 2}, + HrtfModeEntry{"ambi3"sv, RenderMode::Normal, 3}, }; - const char *mode{modeopt->c_str()}; - if(al::strcasecmp(mode, "basic") == 0) + std::string_view mode{*modeopt}; + if(al::case_compare(mode, "basic"sv) == 0) { - ERR("HRTF mode \"%s\" deprecated, substituting \"%s\"\n", mode, "ambi2"); + ERR("HRTF mode \"{}\" deprecated, substituting \"{}\"", *modeopt, "ambi2"); mode = "ambi2"; } auto match_entry = [mode](const HrtfModeEntry &entry) -> bool - { return al::strcasecmp(mode, entry.name) == 0; }; - auto iter = std::find_if(std::begin(hrtf_modes), std::end(hrtf_modes), match_entry); - if(iter == std::end(hrtf_modes)) - ERR("Unexpected hrtf-mode: %s\n", mode); + { return al::case_compare(mode, entry.name) == 0; }; + auto iter = std::find_if(hrtf_modes.begin(), hrtf_modes.end(), match_entry); + if(iter == hrtf_modes.end()) + ERR("Unexpected hrtf-mode: {}", *modeopt); else { device->mRenderMode = iter->mode; ambi_order = iter->order; } } - TRACE("%u%s order %sHRTF rendering enabled, using \"%s\"\n", ambi_order, - (((ambi_order%100)/10) == 1) ? "th" : - ((ambi_order%10) == 1) ? "st" : - ((ambi_order%10) == 2) ? "nd" : - ((ambi_order%10) == 3) ? "rd" : "th", - (device->mRenderMode == RenderMode::Hrtf) ? "+ Full " : "", - device->mHrtfName.c_str()); + TRACE("{}{} order {}HRTF rendering enabled, using \"{}\"", ambi_order, + GetCounterSuffix(ambi_order), (device->mRenderMode == RenderMode::Hrtf) ? "+ Full " : "", + device->mHrtfName); bool perHrirMin{false}; - al::span AmbiPoints{AmbiPoints1O}; - const float (*AmbiMatrix)[MaxAmbiChannels]{AmbiMatrix1O}; - al::span AmbiOrderHFGain{AmbiOrderHFGain1O}; + auto AmbiPoints = al::span{AmbiPoints1O}.subspan(0); + auto AmbiMatrix = al::span{AmbiMatrix1O}.subspan(0); + auto AmbiOrderHFGain = al::span{AmbiOrderHFGain1O}; if(ambi_order >= 3) { perHrirMin = true; @@ -902,10 +997,9 @@ void InitHrtfPanning(ALCdevice *device) device->m2DMixing = false; const size_t count{AmbiChannelsFromOrder(ambi_order)}; - std::transform(AmbiIndex::FromACN.begin(), AmbiIndex::FromACN.begin()+count, - std::begin(device->Dry.AmbiMap), - [](const uint8_t &index) noexcept { return BFChannelConfig{1.0f, index}; } - ); + const auto acnmap = al::span{AmbiIndex::FromACN}.first(count); + std::transform(acnmap.begin(), acnmap.end(), device->Dry.AmbiMap.begin(), + [](const uint8_t &index) noexcept { return BFChannelConfig{1.0f, index}; }); AllocChannels(device, count, device->channelsFromFmt()); HrtfStore *Hrtf{device->mHrtf.get()}; @@ -917,16 +1011,16 @@ void InitHrtfPanning(ALCdevice *device) InitNearFieldCtrl(device, Hrtf->mFields[0].distance, ambi_order, true); } -void InitUhjPanning(ALCdevice *device) +void InitUhjPanning(al::Device *device) { /* UHJ is always 2D first-order. */ - constexpr size_t count{Ambi2DChannelsFromOrder(1)}; + static constexpr size_t count{Ambi2DChannelsFromOrder(1)}; device->mAmbiOrder = 1; device->m2DMixing = true; - auto acnmap_begin = AmbiIndex::FromFuMa2D.begin(); - std::transform(acnmap_begin, acnmap_begin + count, std::begin(device->Dry.AmbiMap), + const auto acnmap = al::span{AmbiIndex::FromFuMa2D}.first(); + std::transform(acnmap.cbegin(), acnmap.cend(), device->Dry.AmbiMap.begin(), [](const uint8_t &acn) noexcept -> BFChannelConfig { return BFChannelConfig{1.0f/AmbiScale::FromUHJ[acn], acn}; }); AllocChannels(device, count, device->channelsFromFmt()); @@ -934,7 +1028,7 @@ void InitUhjPanning(ALCdevice *device) } // namespace -void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optional stereomode) +void aluInitRenderer(al::Device *device, int hrtf_id, std::optional stereomode) { /* Hold the HRTF the device last used, in case it's used again. */ HrtfStorePtr old_hrtf{std::move(device->mHrtf)}; @@ -961,6 +1055,7 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optional> decoder_store; + std::unique_ptr> decoder_store; DecoderView decoder{}; - float speakerdists[MAX_OUTPUT_CHANNELS]{}; + std::array speakerdists{}; auto load_config = [device,&decoder_store,&decoder,&speakerdists](const char *config) { AmbDecConf conf{}; if(auto err = conf.load(config)) { - ERR("Failed to load layout file %s\n", config); - ERR(" %s\n", err->c_str()); + ERR("Failed to load layout file {}", config); + ERR(" {}", *err); return false; } - else if(conf.NumSpeakers > MAX_OUTPUT_CHANNELS) + if(conf.Speakers.size() > MaxOutputChannels) { - ERR("Unsupported decoder speaker count %zu (max %d)\n", conf.NumSpeakers, - MAX_OUTPUT_CHANNELS); + ERR("Unsupported decoder speaker count {} (max {})", conf.Speakers.size(), + MaxOutputChannels); return false; } - else if(conf.ChanMask > Ambi3OrderMask) + if(conf.ChanMask > Ambi3OrderMask) { - ERR("Unsupported decoder channel mask 0x%04x (max 0x%x)\n", conf.ChanMask, + ERR("Unsupported decoder channel mask {:#x} (max {:#x})", conf.ChanMask, Ambi3OrderMask); return false; } - TRACE("Using %s decoder: \"%s\"\n", DevFmtChannelsString(device->FmtChans), - conf.Description.c_str()); - device->mXOverFreq = clampf(conf.XOverFreq, 100.0f, 1000.0f); + TRACE("Using {} decoder: \"{}\"", DevFmtChannelsString(device->FmtChans), + conf.Description); + device->mXOverFreq = std::clamp(conf.XOverFreq, 100.0f, 1000.0f); - decoder_store = std::make_unique>(); + decoder_store = std::make_unique>(); decoder = MakeDecoderView(device, &conf, *decoder_store); - for(size_t i{0};i < decoder.mChannels.size();++i) - speakerdists[i] = conf.Speakers[i].Distance; + + const auto confspeakers = al::span{std::as_const(conf.Speakers)} + .first(decoder.mChannels.size()); + std::transform(confspeakers.cbegin(), confspeakers.cend(), speakerdists.begin(), + std::mem_fn(&AmbDecConf::SpeakerConf::Distance)); return true; }; bool usingCustom{false}; @@ -1011,7 +1109,7 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optionalc_str()); } if(!usingCustom && device->FmtChans != DevFmtAmbi3D) - TRACE("Using built-in %s decoder\n", DevFmtChannelsString(device->FmtChans)); + TRACE("Using built-in {} decoder", DevFmtChannelsString(device->FmtChans)); /* Enable the stablizer only for formats that have front-left, front- * right, and front-center outputs. @@ -1019,8 +1117,8 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optionalRealOut.ChannelIndex[FrontCenter] != InvalidChannelIndex && device->RealOut.ChannelIndex[FrontLeft] != InvalidChannelIndex && device->RealOut.ChannelIndex[FrontRight] != InvalidChannelIndex - && device->getConfigValueBool(nullptr, "front-stablizer", false) != 0}; - const bool hqdec{device->getConfigValueBool("decoder", "hq-mode", true) != 0}; + && device->getConfigValueBool({}, "front-stablizer", false)}; + const bool hqdec{device->getConfigValueBool("decoder", "hq-mode", true)}; InitPanning(device, hqdec, stablize, decoder); if(decoder) { @@ -1043,8 +1141,8 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optionalAmbiDecoder.get()}) { - device->PostProcess = ambidec->hasStablizer() ? &ALCdevice::ProcessAmbiDecStablized - : &ALCdevice::ProcessAmbiDec; + device->PostProcess = ambidec->hasStablizer() ? &al::Device::ProcessAmbiDecStablized + : &al::Device::ProcessAmbiDec; } return; } @@ -1061,8 +1159,8 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optional= 0 && static_cast(hrtf_id) < device->mHrtfList.size()) { - const std::string &hrtfname = device->mHrtfList[static_cast(hrtf_id)]; - if(HrtfStorePtr hrtf{GetLoadedHrtf(hrtfname, device->Frequency)}) + const std::string_view hrtfname{device->mHrtfList[static_cast(hrtf_id)]}; + if(HrtfStorePtr hrtf{GetLoadedHrtf(hrtfname, device->mSampleRate)}) { device->mHrtf = std::move(hrtf); device->mHrtfName = hrtfname; @@ -1071,9 +1169,9 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optionalmHrtf) { - for(const auto &hrtfname : device->mHrtfList) + for(const std::string_view hrtfname : device->mHrtfList) { - if(HrtfStorePtr hrtf{GetLoadedHrtf(hrtfname, device->Frequency)}) + if(HrtfStorePtr hrtf{GetLoadedHrtf(hrtfname, device->mSampleRate)}) { device->mHrtf = std::move(hrtf); device->mHrtfName = hrtfname; @@ -1088,14 +1186,14 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optionalmHrtf.get()}; device->mIrSize = hrtf->mIrSize; - if(auto hrtfsizeopt = device->configValue(nullptr, "hrtf-size")) + if(auto hrtfsizeopt = device->configValue({}, "hrtf-size")) { if(*hrtfsizeopt > 0 && *hrtfsizeopt < device->mIrSize) - device->mIrSize = maxu(*hrtfsizeopt, MinIrLength); + device->mIrSize = std::max(*hrtfsizeopt, MinIrLength); } InitHrtfPanning(device); - device->PostProcess = &ALCdevice::ProcessHrtf; + device->PostProcess = &al::Device::ProcessHrtf; device->mHrtfStatus = ALC_HRTF_ENABLED_SOFT; return; } @@ -1104,47 +1202,51 @@ void aluInitRenderer(ALCdevice *device, int hrtf_id, std::optionalmUhjEncoder = std::make_unique(); + ftype = "IIR"sv; break; case UhjQualityType::FIR256: device->mUhjEncoder = std::make_unique>(); + ftype = "FIR-256"sv; break; case UhjQualityType::FIR512: device->mUhjEncoder = std::make_unique>(); + ftype = "FIR-512"sv; break; } assert(device->mUhjEncoder != nullptr); - TRACE("UHJ enabled\n"); + TRACE("UHJ enabled ({} encoder)", ftype); InitUhjPanning(device); - device->PostProcess = &ALCdevice::ProcessUhj; + device->PostProcess = &al::Device::ProcessUhj; return; } device->mRenderMode = RenderMode::Pairwise; if(device->Type != DeviceType::Loopback) { - if(auto cflevopt = device->configValue(nullptr, "cf_level")) + if(auto cflevopt = device->configValue({}, "cf_level")) { if(*cflevopt > 0 && *cflevopt <= 6) { - device->Bs2b = std::make_unique(); - bs2b_set_params(device->Bs2b.get(), *cflevopt, - static_cast(device->Frequency)); - TRACE("BS2B enabled\n"); + auto bs2b = std::make_unique(); + bs2b->set_params(*cflevopt, static_cast(device->mSampleRate)); + device->Bs2b = std::move(bs2b); + TRACE("BS2B enabled"); InitPanning(device); - device->PostProcess = &ALCdevice::ProcessBs2b; + device->PostProcess = &al::Device::ProcessBs2b; return; } } } - TRACE("Stereo rendering\n"); + TRACE("Stereo rendering"); InitPanning(device); - device->PostProcess = &ALCdevice::ProcessAmbiDec; + device->PostProcess = &al::Device::ProcessAmbiDec; } @@ -1155,10 +1257,9 @@ void aluInitEffectPanning(EffectSlot *slot, ALCcontext *context) slot->mWetBuffer.resize(count); - auto acnmap_begin = AmbiIndex::FromACN.begin(); - auto iter = std::transform(acnmap_begin, acnmap_begin + count, slot->Wet.AmbiMap.begin(), - [](const uint8_t &acn) noexcept -> BFChannelConfig - { return BFChannelConfig{1.0f, acn}; }); + const auto acnmap = al::span{AmbiIndex::FromACN}.first(count); + const auto iter = std::transform(acnmap.cbegin(), acnmap.cend(), slot->Wet.AmbiMap.begin(), + [](const uint8_t &acn) noexcept -> BFChannelConfig { return BFChannelConfig{1.0f, acn}; }); std::fill(iter, slot->Wet.AmbiMap.end(), BFChannelConfig{}); slot->Wet.Buffer = slot->mWetBuffer; } diff --git a/3rdparty/openal/cmake/FindFFmpeg.cmake b/3rdparty/openal/cmake/FindFFmpeg.cmake index 26ed4d2faf30..190c2c56c139 100644 --- a/3rdparty/openal/cmake/FindFFmpeg.cmake +++ b/3rdparty/openal/cmake/FindFFmpeg.cmake @@ -80,8 +80,37 @@ macro(find_component _component _pkgconfig _library _header) ${PC_LIB${_component}_LIBRARY_DIRS} ) - set(${_component}_VERSION ${PC_${_component}_VERSION} CACHE STRING "The ${_component} version number." FORCE) - set(${_component}_DEFINITIONS ${PC_${_component}_CFLAGS_OTHER} CACHE STRING "The ${_component} CFLAGS." FORCE) + if(DEFINED ${PC_${_component}_VERSION}) + set(${_component}_VERSION ${PC_${_component}_VERSION} CACHE STRING + "The ${_component} version number." FORCE) + else() + if(EXISTS "${${_component}_INCLUDE_DIRS}/${_pkgconfig}/version_major.h") + file(STRINGS "${${_component}_INCLUDE_DIRS}/${_pkgconfig}/version_major.h" majorver + REGEX "^#define[ \t]+LIB${_component}_VERSION_MAJOR[ \t]+[0-9]+$") + else() + file(STRINGS "${${_component}_INCLUDE_DIRS}/${_pkgconfig}/version.h" majorver + REGEX "^#define[ \t]+LIB${_component}_VERSION_MAJOR[ \t]+[0-9]+$") + endif() + file(STRINGS "${${_component}_INCLUDE_DIRS}/${_pkgconfig}/version.h" minorver + REGEX "^#define[ \t]+LIB${_component}_VERSION_MINOR[ \t]+[0-9]+$") + file(STRINGS "${${_component}_INCLUDE_DIRS}/${_pkgconfig}/version.h" microver + REGEX "^#define[ \t]+LIB${_component}_VERSION_MICRO[ \t]+[0-9]+$") + + string(REGEX REPLACE "^#define[ \t]+LIB${_component}_VERSION_MAJOR[ \t]+([0-9]+)$" "\\1" + majorver "${majorver}") + string(REGEX REPLACE "^#define[ \t]+LIB${_component}_VERSION_MINOR[ \t]+([0-9]+)$" "\\1" + minorver "${minorver}") + string(REGEX REPLACE "^#define[ \t]+LIB${_component}_VERSION_MICRO[ \t]+([0-9]+)$" "\\1" + microver "${microver}") + + set(${_component}_VERSION "${majorver}.${minorver}.${microver}" CACHE STRING + "The ${_component} version number." FORCE) + unset(microver) + unset(minorver) + unset(majorver) + endif() + set(${_component}_DEFINITIONS ${PC_${_component}_CFLAGS_OTHER} CACHE STRING + "The ${_component} CFLAGS." FORCE) set_component_found(${_component}) diff --git a/3rdparty/openal/cmake/FindJACK.cmake b/3rdparty/openal/cmake/FindJACK.cmake index b72fe3f9a91e..c85d0643f392 100644 --- a/3rdparty/openal/cmake/FindJACK.cmake +++ b/3rdparty/openal/cmake/FindJACK.cmake @@ -43,7 +43,7 @@ find_path(JACK_INCLUDE_DIR NAMES jack/jack.h DOC "The JACK include directory" ) -find_library(JACK_LIBRARY NAMES jack +find_library(JACK_LIBRARY NAMES jack jack64 DOC "The JACK library" ) diff --git a/3rdparty/openal/cmake/FindSndIO.cmake b/3rdparty/openal/cmake/FindSndIO.cmake new file mode 100644 index 000000000000..eb4a2587be6b --- /dev/null +++ b/3rdparty/openal/cmake/FindSndIO.cmake @@ -0,0 +1,31 @@ +# - Find SndIO includes and libraries +# +# SNDIO_FOUND - True if SNDIO_INCLUDE_DIR & SNDIO_LIBRARY are found +# SNDIO_LIBRARIES - Set when SNDIO_LIBRARY is found +# SNDIO_INCLUDE_DIRS - Set when SNDIO_INCLUDE_DIR is found +# +# SNDIO_INCLUDE_DIR - where to find sndio.h, etc. +# SNDIO_LIBRARY - the sndio library +# + +find_path(SNDIO_INCLUDE_DIR + NAMES sndio.h + DOC "The SndIO include directory" +) + +find_library(SNDIO_LIBRARY + NAMES sndio + DOC "The SndIO library" +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(SndIO + REQUIRED_VARS SNDIO_LIBRARY SNDIO_INCLUDE_DIR +) + +if(SNDIO_FOUND) + set(SNDIO_LIBRARIES ${SNDIO_LIBRARY}) + set(SNDIO_INCLUDE_DIRS ${SNDIO_INCLUDE_DIR}) +endif() + +mark_as_advanced(SNDIO_INCLUDE_DIR SNDIO_LIBRARY) diff --git a/3rdparty/openal/cmake/FindSoundIO.cmake b/3rdparty/openal/cmake/FindSoundIO.cmake deleted file mode 100644 index 10450254d213..000000000000 --- a/3rdparty/openal/cmake/FindSoundIO.cmake +++ /dev/null @@ -1,32 +0,0 @@ -# - Find SoundIO (sndio) includes and libraries -# -# SOUNDIO_FOUND - True if SOUNDIO_INCLUDE_DIR & SOUNDIO_LIBRARY are -# found -# SOUNDIO_LIBRARIES - Set when SOUNDIO_LIBRARY is found -# SOUNDIO_INCLUDE_DIRS - Set when SOUNDIO_INCLUDE_DIR is found -# -# SOUNDIO_INCLUDE_DIR - where to find sndio.h, etc. -# SOUNDIO_LIBRARY - the sndio library -# - -find_path(SOUNDIO_INCLUDE_DIR - NAMES sndio.h - DOC "The SoundIO include directory" -) - -find_library(SOUNDIO_LIBRARY - NAMES sndio - DOC "The SoundIO library" -) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(SoundIO - REQUIRED_VARS SOUNDIO_LIBRARY SOUNDIO_INCLUDE_DIR -) - -if(SOUNDIO_FOUND) - set(SOUNDIO_LIBRARIES ${SOUNDIO_LIBRARY}) - set(SOUNDIO_INCLUDE_DIRS ${SOUNDIO_INCLUDE_DIR}) -endif() - -mark_as_advanced(SOUNDIO_INCLUDE_DIR SOUNDIO_LIBRARY) diff --git a/3rdparty/openal/common/alassert.cpp b/3rdparty/openal/common/alassert.cpp new file mode 100644 index 000000000000..aa44841e428b --- /dev/null +++ b/3rdparty/openal/common/alassert.cpp @@ -0,0 +1,44 @@ + +#include "alassert.h" + +#include +#include + +namespace { + +[[noreturn]] +void throw_error(const std::string &message) +{ + throw std::runtime_error{message}; +} + +} /* namespace */ + +namespace al { + +[[noreturn]] +void do_assert(const char *message, int linenum, const char *filename, const char *funcname) noexcept +{ + /* Throwing an exception that tries to leave a noexcept function will + * hopefully cause the system to provide info about the caught exception in + * an error dialog. At least on Linux, this results in the process printing + * + * terminate called after throwing an instance of 'std::runtime_error' + * what(): + * + * before terminating from a SIGABRT. Hopefully Windows and Mac will do the + * appropriate things with the message to alert the user about an abnormal + * termination. + */ + auto errstr = std::string{filename}; + errstr += ':'; + errstr += std::to_string(linenum); + errstr += ": "; + errstr += funcname; + errstr += ": "; + errstr += message; + + throw_error(errstr); +} + +} /* namespace al */ diff --git a/3rdparty/openal/common/alassert.h b/3rdparty/openal/common/alassert.h new file mode 100644 index 000000000000..3797e96d5bd1 --- /dev/null +++ b/3rdparty/openal/common/alassert.h @@ -0,0 +1,24 @@ +#ifndef AL_ASSERT_H +#define AL_ASSERT_H + +#include + +#include "opthelpers.h" + +namespace al { + +[[noreturn]] +void do_assert(const char *message, int linenum, const char *filename, const char *funcname) noexcept; + +} /* namespace al */ + +/* A custom assert macro that is not compiled out for Release/NDEBUG builds, + * making it an appropriate replacement for assert() checks that must not be + * ignored. + */ +#define alassert(cond) do { \ + if(!(cond)) UNLIKELY \ + al::do_assert("Assertion '" #cond "' failed", __LINE__, __FILE__, std::data(__func__)); \ +} while(0) + +#endif /* AL_ASSERT_H */ diff --git a/3rdparty/openal/common/albit.h b/3rdparty/openal/common/albit.h index 962eb0aae458..b5c10d718066 100644 --- a/3rdparty/openal/common/albit.h +++ b/3rdparty/openal/common/albit.h @@ -1,7 +1,11 @@ #ifndef AL_BIT_H #define AL_BIT_H +#include +#include +#ifndef __GNUC__ #include +#endif #include #include #include @@ -17,9 +21,19 @@ std::enable_if_t, To> bit_cast(const From &src) noexcept { - std::aligned_storage_t dst; - std::memcpy(&dst, &src, sizeof(To)); - return *std::launder(reinterpret_cast(&dst)); + alignas(To) std::array dst; + std::memcpy(dst.data(), &src, sizeof(To)); + return *std::launder(reinterpret_cast(dst.data())); +} + +template +std::enable_if_t, +T> byteswap(T value) noexcept +{ + static_assert(std::has_unique_object_representations_v); + auto bytes = al::bit_cast>(value); + std::reverse(bytes.begin(), bytes.end()); + return al::bit_cast(bytes); } #ifdef __BYTE_ORDER__ diff --git a/3rdparty/openal/common/alcomplex.cpp b/3rdparty/openal/common/alcomplex.cpp index dad62d0a1910..ae8bc41cef4b 100644 --- a/3rdparty/openal/common/alcomplex.cpp +++ b/3rdparty/openal/common/alcomplex.cpp @@ -4,10 +4,11 @@ #include "alcomplex.h" #include +#include #include -#include #include #include +#include #include #include "albit.h" @@ -19,32 +20,33 @@ namespace { using ushort = unsigned short; -using ushort2 = std::pair; +using ushort2 = std::array; +using complex_d = std::complex; -constexpr size_t BitReverseCounter(size_t log2_size) noexcept +constexpr std::size_t BitReverseCounter(std::size_t log2_size) noexcept { /* Some magic math that calculates the number of swaps needed for a * sequence of bit-reversed indices when index < reversed_index. */ - return (1u<<(log2_size-1)) - (1u<<((log2_size-1u)/2u)); + return (1_zu<<(log2_size-1)) - (1_zu<<((log2_size-1_zu)/2_zu)); } -template +template struct BitReverser { static_assert(N <= sizeof(ushort)*8, "Too many bits for the bit-reversal table."); - ushort2 mData[BitReverseCounter(N)]{}; + std::array mData{}; constexpr BitReverser() { - const size_t fftsize{1u << N}; - size_t ret_i{0}; + const std::size_t fftsize{1u << N}; + std::size_t ret_i{0}; /* Bit-reversal permutation applied to a sequence of fftsize items. */ - for(size_t idx{1u};idx < fftsize-1;++idx) + for(std::size_t idx{1u};idx < fftsize-1;++idx) { - size_t revidx{idx}; + std::size_t revidx{idx}; revidx = ((revidx&0xaaaaaaaa) >> 1) | ((revidx&0x55555555) << 1); revidx = ((revidx&0xcccccccc) >> 2) | ((revidx&0x33333333) << 2); revidx = ((revidx&0xf0f0f0f0) >> 4) | ((revidx&0x0f0f0f0f) << 4); @@ -54,8 +56,8 @@ struct BitReverser { if(idx < revidx) { - mData[ret_i].first = static_cast(idx); - mData[ret_i].second = static_cast(revidx); + mData[ret_i][0] = static_cast(idx); + mData[ret_i][1] = static_cast(revidx); ++ret_i; } } @@ -109,43 +111,41 @@ constexpr std::array,gBitReverses.size()-1> gArgAngle{{ } // namespace -template -std::enable_if_t::value> -complex_fft(const al::span> buffer, const al::type_identity_t sign) +void complex_fft(const al::span> buffer, const double sign) { - const size_t fftsize{buffer.size()}; + const std::size_t fftsize{buffer.size()}; /* Get the number of bits used for indexing. Simplifies bit-reversal and * the main loop count. */ - const size_t log2_size{static_cast(al::countr_zero(fftsize))}; + const std::size_t log2_size{static_cast(al::countr_zero(fftsize))}; if(log2_size < gBitReverses.size()) LIKELY { for(auto &rev : gBitReverses[log2_size]) - std::swap(buffer[rev.first], buffer[rev.second]); + std::swap(buffer[rev[0]], buffer[rev[1]]); /* Iterative form of Danielson-Lanczos lemma */ - for(size_t i{0};i < log2_size;++i) + for(std::size_t i{0};i < log2_size;++i) { - const size_t step2{1_uz << i}; - const size_t step{2_uz << i}; + const std::size_t step2{1_uz << i}; + const std::size_t step{2_uz << i}; /* The first iteration of the inner loop would have u=1, which we * can simplify to remove a number of complex multiplies. */ - for(size_t k{0};k < fftsize;k+=step) + for(std::size_t k{0};k < fftsize;k+=step) { - std::complex temp{buffer[k+step2]}; + const complex_d temp{buffer[k+step2]}; buffer[k+step2] = buffer[k] - temp; buffer[k] += temp; } - const std::complex w{gArgAngle[i].real(), gArgAngle[i].imag()*sign}; - std::complex u{w}; - for(size_t j{1};j < step2;j++) + const complex_d w{gArgAngle[i].real(), gArgAngle[i].imag()*sign}; + complex_d u{w}; + for(std::size_t j{1};j < step2;j++) { - for(size_t k{j};k < fftsize;k+=step) + for(std::size_t k{j};k < fftsize;k+=step) { - std::complex temp{buffer[k+step2] * u}; + const complex_d temp{buffer[k+step2] * u}; buffer[k+step2] = buffer[k] - temp; buffer[k] += temp; } @@ -155,9 +155,11 @@ complex_fft(const al::span> buffer, const al::type_identity_t } else { - for(size_t idx{1u};idx < fftsize-1;++idx) + assert(log2_size < 32); + + for(std::size_t idx{1u};idx < fftsize-1;++idx) { - size_t revidx{idx}; + std::size_t revidx{idx}; revidx = ((revidx&0xaaaaaaaa) >> 1) | ((revidx&0x55555555) << 1); revidx = ((revidx&0xcccccccc) >> 2) | ((revidx&0x33333333) << 2); revidx = ((revidx&0xf0f0f0f0) >> 4) | ((revidx&0x0f0f0f0f) << 4); @@ -169,26 +171,26 @@ complex_fft(const al::span> buffer, const al::type_identity_t std::swap(buffer[idx], buffer[revidx]); } - const Real pi{al::numbers::pi_v * sign}; - for(size_t i{0};i < log2_size;++i) + const double pi{al::numbers::pi * sign}; + for(std::size_t i{0};i < log2_size;++i) { - const size_t step2{1_uz << i}; - const size_t step{2_uz << i}; - for(size_t k{0};k < fftsize;k+=step) + const std::size_t step2{1_uz << i}; + const std::size_t step{2_uz << i}; + for(std::size_t k{0};k < fftsize;k+=step) { - std::complex temp{buffer[k+step2]}; + const complex_d temp{buffer[k+step2]}; buffer[k+step2] = buffer[k] - temp; buffer[k] += temp; } - const Real arg{pi / static_cast(step2)}; - const std::complex w{std::polar(Real{1}, arg)}; - std::complex u{w}; - for(size_t j{1};j < step2;j++) + const double arg{pi / static_cast(step2)}; + const complex_d w{std::polar(1.0, arg)}; + complex_d u{w}; + for(std::size_t j{1};j < step2;j++) { - for(size_t k{j};k < fftsize;k+=step) + for(std::size_t k{j};k < fftsize;k+=step) { - std::complex temp{buffer[k+step2] * u}; + const complex_d temp{buffer[k+step2] * u}; buffer[k+step2] = buffer[k] - temp; buffer[k] += temp; } @@ -200,13 +202,11 @@ complex_fft(const al::span> buffer, const al::type_identity_t void complex_hilbert(const al::span> buffer) { - using namespace std::placeholders; - inverse_fft(buffer); const double inverse_size = 1.0/static_cast(buffer.size()); auto bufiter = buffer.begin(); - const auto halfiter = bufiter + (buffer.size()>>1); + const auto halfiter = bufiter + ptrdiff_t(buffer.size()>>1); *bufiter *= inverse_size; ++bufiter; bufiter = std::transform(bufiter, halfiter, bufiter, @@ -217,7 +217,3 @@ void complex_hilbert(const al::span> buffer) forward_fft(buffer); } - - -template void complex_fft<>(const al::span> buffer, const float sign); -template void complex_fft<>(const al::span> buffer, const double sign); diff --git a/3rdparty/openal/common/alcomplex.h b/3rdparty/openal/common/alcomplex.h index 042a323266b5..9f12cef32cd2 100644 --- a/3rdparty/openal/common/alcomplex.h +++ b/3rdparty/openal/common/alcomplex.h @@ -2,7 +2,6 @@ #define ALCOMPLEX_H #include -#include #include "alspan.h" @@ -11,25 +10,21 @@ * FFT and 1 is inverse FFT. Applies the Discrete Fourier Transform (DFT) to * the data supplied in the buffer, which MUST BE power of two. */ -template -std::enable_if_t::value> -complex_fft(const al::span> buffer, const al::type_identity_t sign); +void complex_fft(const al::span> buffer, const double sign); /** * Calculate the frequency-domain response of the time-domain signal in the * provided buffer, which MUST BE power of two. */ -template -void forward_fft(const al::span buffer) -{ complex_fft(al::span{buffer}, -1); } +inline void forward_fft(const al::span> buffer) +{ complex_fft(buffer, -1.0); } /** * Calculate the time-domain signal of the frequency-domain response in the * provided buffer, which MUST BE power of two. */ -template -void inverse_fft(const al::span buffer) -{ complex_fft(al::span{buffer}, 1); } +inline void inverse_fft(const al::span> buffer) +{ complex_fft(buffer, +1.0); } /** * Calculate the complex helical sequence (discrete-time analytical signal) of diff --git a/3rdparty/openal/common/aldeque.h b/3rdparty/openal/common/aldeque.h deleted file mode 100644 index 3f99bf007469..000000000000 --- a/3rdparty/openal/common/aldeque.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef ALDEQUE_H -#define ALDEQUE_H - -#include - -#include "almalloc.h" - - -namespace al { - -template -using deque = std::deque>; - -} // namespace al - -#endif /* ALDEQUE_H */ diff --git a/3rdparty/openal/common/alfstream.cpp b/3rdparty/openal/common/alfstream.cpp deleted file mode 100644 index 8991ce0352a0..000000000000 --- a/3rdparty/openal/common/alfstream.cpp +++ /dev/null @@ -1,26 +0,0 @@ - -#include "config.h" - -#include "alfstream.h" - -#include "strutils.h" - -#ifdef _WIN32 - -namespace al { - -ifstream::ifstream(const char *filename, std::ios_base::openmode mode) - : std::ifstream{utf8_to_wstr(filename).c_str(), mode} -{ } - -void ifstream::open(const char *filename, std::ios_base::openmode mode) -{ - std::wstring wstr{utf8_to_wstr(filename)}; - std::ifstream::open(wstr.c_str(), mode); -} - -ifstream::~ifstream() = default; - -} // namespace al - -#endif diff --git a/3rdparty/openal/common/alfstream.h b/3rdparty/openal/common/alfstream.h deleted file mode 100644 index 62b2e12c7568..000000000000 --- a/3rdparty/openal/common/alfstream.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef AL_FSTREAM_H -#define AL_FSTREAM_H - -#ifdef _WIN32 - -#include -#include - - -namespace al { - -// Inherit from std::ifstream to accept UTF-8 filenames -class ifstream final : public std::ifstream { -public: - explicit ifstream(const char *filename, std::ios_base::openmode mode=std::ios_base::in); - explicit ifstream(const std::string &filename, std::ios_base::openmode mode=std::ios_base::in) - : ifstream{filename.c_str(), mode} { } - - explicit ifstream(const wchar_t *filename, std::ios_base::openmode mode=std::ios_base::in) - : std::ifstream{filename, mode} { } - explicit ifstream(const std::wstring &filename, std::ios_base::openmode mode=std::ios_base::in) - : ifstream{filename.c_str(), mode} { } - - void open(const char *filename, std::ios_base::openmode mode=std::ios_base::in); - void open(const std::string &filename, std::ios_base::openmode mode=std::ios_base::in) - { open(filename.c_str(), mode); } - - ~ifstream() override; -}; - -} // namespace al - -#else /* _WIN32 */ - -#include - -namespace al { - -using ifstream = std::ifstream; - -} // namespace al - -#endif /* _WIN32 */ - -#endif /* AL_FSTREAM_H */ diff --git a/3rdparty/openal/common/almalloc.cpp b/3rdparty/openal/common/almalloc.cpp deleted file mode 100644 index ad1dc6be082e..000000000000 --- a/3rdparty/openal/common/almalloc.cpp +++ /dev/null @@ -1,61 +0,0 @@ - -#include "config.h" - -#include "almalloc.h" - -#include -#include -#include -#include -#include -#ifdef HAVE_MALLOC_H -#include -#endif - - -void *al_malloc(size_t alignment, size_t size) -{ - assert((alignment & (alignment-1)) == 0); - alignment = std::max(alignment, alignof(std::max_align_t)); - -#if defined(HAVE_POSIX_MEMALIGN) - void *ret{}; - if(posix_memalign(&ret, alignment, size) == 0) - return ret; - return nullptr; -#elif defined(HAVE__ALIGNED_MALLOC) - return _aligned_malloc(size, alignment); -#else - size_t total_size{size + alignment-1 + sizeof(void*)}; - void *base{std::malloc(total_size)}; - if(base != nullptr) - { - void *aligned_ptr{static_cast(base) + sizeof(void*)}; - total_size -= sizeof(void*); - - std::align(alignment, size, aligned_ptr, total_size); - *(static_cast(aligned_ptr)-1) = base; - base = aligned_ptr; - } - return base; -#endif -} - -void *al_calloc(size_t alignment, size_t size) -{ - void *ret{al_malloc(alignment, size)}; - if(ret) std::memset(ret, 0, size); - return ret; -} - -void al_free(void *ptr) noexcept -{ -#if defined(HAVE_POSIX_MEMALIGN) - std::free(ptr); -#elif defined(HAVE__ALIGNED_MALLOC) - _aligned_free(ptr); -#else - if(ptr != nullptr) - std::free(*(static_cast(ptr) - 1)); -#endif -} diff --git a/3rdparty/openal/common/almalloc.h b/3rdparty/openal/common/almalloc.h index 873473cacafe..7edda119c9e7 100644 --- a/3rdparty/openal/common/almalloc.h +++ b/3rdparty/openal/common/almalloc.h @@ -3,49 +3,24 @@ #include #include -#include #include -#include #include #include #include +#include -#include "pragmadefs.h" - -void al_free(void *ptr) noexcept; -[[gnu::alloc_align(1), gnu::alloc_size(2), gnu::malloc]] -void *al_malloc(size_t alignment, size_t size); -[[gnu::alloc_align(1), gnu::alloc_size(2), gnu::malloc]] -void *al_calloc(size_t alignment, size_t size); +namespace gsl { +template using owner = T; +} -#define DISABLE_ALLOC() \ +#define DISABLE_ALLOC \ void *operator new(size_t) = delete; \ void *operator new[](size_t) = delete; \ void operator delete(void*) noexcept = delete; \ void operator delete[](void*) noexcept = delete; -#define DEF_NEWDEL(T) \ - void *operator new(size_t size) \ - { \ - static_assert(&operator new == &T::operator new, \ - "Incorrect container type specified"); \ - if(void *ret{al_malloc(alignof(T), size)}) \ - return ret; \ - throw std::bad_alloc(); \ - } \ - void *operator new[](size_t size) { return operator new(size); } \ - void operator delete(void *block) noexcept { al_free(block); } \ - void operator delete[](void *block) noexcept { operator delete(block); } - -#define DEF_PLACE_NEWDEL() \ - void *operator new(size_t /*size*/, void *ptr) noexcept { return ptr; } \ - void *operator new[](size_t /*size*/, void *ptr) noexcept { return ptr; } \ - void operator delete(void *block, void*) noexcept { al_free(block); } \ - void operator delete(void *block) noexcept { al_free(block); } \ - void operator delete[](void *block, void*) noexcept { al_free(block); } \ - void operator delete[](void *block) noexcept { al_free(block); } enum FamCount : size_t { }; @@ -58,56 +33,64 @@ enum FamCount : size_t { }; sizeof(T)); \ } \ \ - void *operator new(size_t /*size*/, FamCount count) \ + gsl::owner operator new(size_t /*size*/, FamCount count) \ { \ - if(void *ret{al_malloc(alignof(T), T::Sizeof(count))}) \ - return ret; \ - throw std::bad_alloc(); \ + const auto alignment = std::align_val_t{alignof(T)}; \ + return ::operator new[](T::Sizeof(count), alignment); \ } \ + void operator delete(gsl::owner block, FamCount) noexcept \ + { ::operator delete[](block, std::align_val_t{alignof(T)}); } \ + void operator delete(gsl::owner block) noexcept \ + { ::operator delete[](block, std::align_val_t{alignof(T)}); } \ void *operator new[](size_t /*size*/) = delete; \ - void operator delete(void *block, FamCount) { al_free(block); } \ - void operator delete(void *block) noexcept { al_free(block); } \ void operator delete[](void* /*block*/) = delete; namespace al { -template +template struct allocator { - static constexpr std::size_t alignment{std::max(Align, alignof(T))}; - - using value_type = T; - using reference = T&; - using const_reference = const T&; - using pointer = T*; - using const_pointer = const T*; + static constexpr auto Alignment = std::max(AlignV, alignof(T)); + static constexpr auto AlignVal = std::align_val_t{Alignment}; + + using value_type = std::remove_cv_t>; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = value_type*; + using const_pointer = const value_type*; using size_type = std::size_t; using difference_type = std::ptrdiff_t; using is_always_equal = std::true_type; - template + template = true> struct rebind { - using other = allocator; + using other = allocator; }; constexpr explicit allocator() noexcept = default; template - constexpr explicit allocator(const allocator&) noexcept { } + constexpr explicit allocator(const allocator&) noexcept + { static_assert(Alignment == allocator::Alignment); } - T *allocate(std::size_t n) + gsl::owner allocate(std::size_t n) { if(n > std::numeric_limits::max()/sizeof(T)) throw std::bad_alloc(); - if(auto p = al_malloc(alignment, n*sizeof(T))) return static_cast(p); - throw std::bad_alloc(); + return static_cast>(::operator new[](n*sizeof(T), AlignVal)); } - void deallocate(T *p, std::size_t) noexcept { al_free(p); } + void deallocate(gsl::owner p, std::size_t) noexcept + { ::operator delete[](gsl::owner{p}, AlignVal); } }; template -constexpr bool operator==(const allocator&, const allocator&) noexcept { return true; } +constexpr bool operator==(const allocator&, const allocator&) noexcept +{ return allocator::Alignment == allocator::Alignment; } template -constexpr bool operator!=(const allocator&, const allocator&) noexcept { return false; } +constexpr bool operator!=(const allocator&, const allocator&) noexcept +{ return allocator::Alignment != allocator::Alignment; } +#ifdef __cpp_lib_to_address +using std::to_address; +#else template constexpr T *to_address(T *p) noexcept { @@ -120,128 +103,52 @@ constexpr auto to_address(const T &p) noexcept { return ::al::to_address(p.operator->()); } - +#endif template constexpr T* construct_at(T *ptr, Args&& ...args) - noexcept(std::is_nothrow_constructible::value) -{ return ::new(static_cast(ptr)) T{std::forward(args)...}; } - - -/* Storage for flexible array data. This is trivially destructible if type T is - * trivially destructible. - */ -template::value> -struct FlexArrayStorage { - const size_t mSize; - union { - char mDummy; - alignas(alignment) T mArray[1]; - }; - - static constexpr size_t Sizeof(size_t count, size_t base=0u) noexcept - { - const size_t len{sizeof(T)*count}; - return std::max(offsetof(FlexArrayStorage,mArray)+len, sizeof(FlexArrayStorage)) + base; - } + noexcept(std::is_nothrow_constructible_v) +{ + /* NOLINTBEGIN(cppcoreguidelines-owning-memory) construct_at doesn't + * necessarily handle the address from an owner, while placement new + * expects to. + */ + return ::new(static_cast(ptr)) T{std::forward(args)...}; + /* NOLINTEND(cppcoreguidelines-owning-memory) */ +} - FlexArrayStorage(size_t size) : mSize{size} - { std::uninitialized_default_construct_n(mArray, mSize); } - ~FlexArrayStorage() = default; - FlexArrayStorage(const FlexArrayStorage&) = delete; - FlexArrayStorage& operator=(const FlexArrayStorage&) = delete; -}; +template +class out_ptr_t { + static_assert(!std::is_same_v); -template -struct FlexArrayStorage { - const size_t mSize; - union { - char mDummy; - alignas(alignment) T mArray[1]; - }; + SP &mRes; + std::variant mPtr{}; - static constexpr size_t Sizeof(size_t count, size_t base) noexcept +public: + explicit out_ptr_t(SP &res) : mRes{res} { } + ~out_ptr_t() { - const size_t len{sizeof(T)*count}; - return std::max(offsetof(FlexArrayStorage,mArray)+len, sizeof(FlexArrayStorage)) + base; + auto set_res = [this](auto &ptr) + { mRes.reset(static_cast(ptr)); }; + std::visit(set_res, mPtr); } + out_ptr_t(const out_ptr_t&) = delete; + out_ptr_t& operator=(const out_ptr_t&) = delete; - FlexArrayStorage(size_t size) : mSize{size} - { std::uninitialized_default_construct_n(mArray, mSize); } - ~FlexArrayStorage() { std::destroy_n(mArray, mSize); } + operator PT*() noexcept /* NOLINT(google-explicit-constructor) */ + { return &std::get(mPtr); } - FlexArrayStorage(const FlexArrayStorage&) = delete; - FlexArrayStorage& operator=(const FlexArrayStorage&) = delete; + operator void**() noexcept /* NOLINT(google-explicit-constructor) */ + { return &mPtr.template emplace(); } }; -/* A flexible array type. Used either standalone or at the end of a parent - * struct, with placement new, to have a run-time-sized array that's embedded - * with its size. - */ -template -struct FlexArray { - using element_type = T; - using value_type = std::remove_cv_t; - using index_type = size_t; - using difference_type = ptrdiff_t; - - using pointer = T*; - using const_pointer = const T*; - using reference = T&; - using const_reference = const T&; - - using iterator = pointer; - using const_iterator = const_pointer; - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; - - using Storage_t_ = FlexArrayStorage; - - Storage_t_ mStore; - - static constexpr index_type Sizeof(index_type count, index_type base=0u) noexcept - { return Storage_t_::Sizeof(count, base); } - static std::unique_ptr Create(index_type count) - { - void *ptr{al_calloc(alignof(FlexArray), Sizeof(count))}; - return std::unique_ptr{al::construct_at(static_cast(ptr), count)}; - } - - FlexArray(index_type size) : mStore{size} { } - ~FlexArray() = default; - - index_type size() const noexcept { return mStore.mSize; } - bool empty() const noexcept { return mStore.mSize == 0; } - - pointer data() noexcept { return mStore.mArray; } - const_pointer data() const noexcept { return mStore.mArray; } - - reference operator[](index_type i) noexcept { return mStore.mArray[i]; } - const_reference operator[](index_type i) const noexcept { return mStore.mArray[i]; } - - reference front() noexcept { return mStore.mArray[0]; } - const_reference front() const noexcept { return mStore.mArray[0]; } - - reference back() noexcept { return mStore.mArray[mStore.mSize-1]; } - const_reference back() const noexcept { return mStore.mArray[mStore.mSize-1]; } - - iterator begin() noexcept { return mStore.mArray; } - const_iterator begin() const noexcept { return mStore.mArray; } - const_iterator cbegin() const noexcept { return mStore.mArray; } - iterator end() noexcept { return mStore.mArray + mStore.mSize; } - const_iterator end() const noexcept { return mStore.mArray + mStore.mSize; } - const_iterator cend() const noexcept { return mStore.mArray + mStore.mSize; } - - reverse_iterator rbegin() noexcept { return end(); } - const_reverse_iterator rbegin() const noexcept { return end(); } - const_reverse_iterator crbegin() const noexcept { return cend(); } - reverse_iterator rend() noexcept { return begin(); } - const_reverse_iterator rend() const noexcept { return begin(); } - const_reverse_iterator crend() const noexcept { return cbegin(); } - - DEF_PLACE_NEWDEL() -}; +template +auto out_ptr(SP &res) +{ + using ptype = typename SP::element_type*; + return out_ptr_t{res}; +} } // namespace al diff --git a/3rdparty/openal/common/alnumbers.h b/3rdparty/openal/common/alnumbers.h index e92d7b871ee5..6d201ad4f50a 100644 --- a/3rdparty/openal/common/alnumbers.h +++ b/3rdparty/openal/common/alnumbers.h @@ -1,11 +1,9 @@ #ifndef COMMON_ALNUMBERS_H #define COMMON_ALNUMBERS_H -#include +#include -namespace al { - -namespace numbers { +namespace al::numbers { namespace detail_ { template @@ -29,8 +27,6 @@ inline constexpr auto inv_pi = inv_pi_v; inline constexpr auto sqrt2 = sqrt2_v; inline constexpr auto sqrt3 = sqrt3_v; -} // namespace numbers - -} // namespace al +} // namespace al::numbers #endif /* COMMON_ALNUMBERS_H */ diff --git a/3rdparty/openal/common/alnumeric.h b/3rdparty/openal/common/alnumeric.h index 759dbc126f0d..cd9c55cfcb8a 100644 --- a/3rdparty/openal/common/alnumeric.h +++ b/3rdparty/openal/common/alnumeric.h @@ -1,15 +1,19 @@ #ifndef AL_NUMERIC_H #define AL_NUMERIC_H +#include "config_simd.h" + #include +#include #include #include #include +#include #include #ifdef HAVE_INTRIN_H #include #endif -#ifdef HAVE_SSE_INTRINSICS +#if HAVE_SSE_INTRINSICS #include #endif @@ -18,78 +22,39 @@ #include "opthelpers.h" -constexpr auto operator "" _i64(unsigned long long n) noexcept { return static_cast(n); } -constexpr auto operator "" _u64(unsigned long long n) noexcept { return static_cast(n); } +constexpr auto operator "" _i64(unsigned long long n) noexcept { return static_cast(n); } +constexpr auto operator "" _u64(unsigned long long n) noexcept { return static_cast(n); } constexpr auto operator "" _z(unsigned long long n) noexcept -{ return static_cast>(n); } -constexpr auto operator "" _uz(unsigned long long n) noexcept { return static_cast(n); } -constexpr auto operator "" _zu(unsigned long long n) noexcept { return static_cast(n); } - - -constexpr inline float minf(float a, float b) noexcept -{ return ((a > b) ? b : a); } -constexpr inline float maxf(float a, float b) noexcept -{ return ((a > b) ? a : b); } -constexpr inline float clampf(float val, float min, float max) noexcept -{ return minf(max, maxf(min, val)); } - -constexpr inline double mind(double a, double b) noexcept -{ return ((a > b) ? b : a); } -constexpr inline double maxd(double a, double b) noexcept -{ return ((a > b) ? a : b); } -constexpr inline double clampd(double val, double min, double max) noexcept -{ return mind(max, maxd(min, val)); } - -constexpr inline unsigned int minu(unsigned int a, unsigned int b) noexcept -{ return ((a > b) ? b : a); } -constexpr inline unsigned int maxu(unsigned int a, unsigned int b) noexcept -{ return ((a > b) ? a : b); } -constexpr inline unsigned int clampu(unsigned int val, unsigned int min, unsigned int max) noexcept -{ return minu(max, maxu(min, val)); } - -constexpr inline int mini(int a, int b) noexcept -{ return ((a > b) ? b : a); } -constexpr inline int maxi(int a, int b) noexcept -{ return ((a > b) ? a : b); } -constexpr inline int clampi(int val, int min, int max) noexcept -{ return mini(max, maxi(min, val)); } - -constexpr inline int64_t mini64(int64_t a, int64_t b) noexcept -{ return ((a > b) ? b : a); } -constexpr inline int64_t maxi64(int64_t a, int64_t b) noexcept -{ return ((a > b) ? a : b); } -constexpr inline int64_t clampi64(int64_t val, int64_t min, int64_t max) noexcept -{ return mini64(max, maxi64(min, val)); } - -constexpr inline uint64_t minu64(uint64_t a, uint64_t b) noexcept -{ return ((a > b) ? b : a); } -constexpr inline uint64_t maxu64(uint64_t a, uint64_t b) noexcept -{ return ((a > b) ? a : b); } -constexpr inline uint64_t clampu64(uint64_t val, uint64_t min, uint64_t max) noexcept -{ return minu64(max, maxu64(min, val)); } - -constexpr inline size_t minz(size_t a, size_t b) noexcept -{ return ((a > b) ? b : a); } -constexpr inline size_t maxz(size_t a, size_t b) noexcept -{ return ((a > b) ? a : b); } -constexpr inline size_t clampz(size_t val, size_t min, size_t max) noexcept -{ return minz(max, maxz(min, val)); } - - -constexpr inline float lerpf(float val1, float val2, float mu) noexcept -{ return val1 + (val2-val1)*mu; } -constexpr inline float cubic(float val1, float val2, float val3, float val4, float mu) noexcept +{ return static_cast>(n); } +constexpr auto operator "" _uz(unsigned long long n) noexcept { return static_cast(n); } +constexpr auto operator "" _zu(unsigned long long n) noexcept { return static_cast(n); } + + +template,bool> = true> +constexpr auto as_unsigned(T value) noexcept { - const float mu2{mu*mu}, mu3{mu2*mu}; - const float a0{-0.5f*mu3 + mu2 + -0.5f*mu}; - const float a1{ 1.5f*mu3 + -2.5f*mu2 + 1.0f}; - const float a2{-1.5f*mu3 + 2.0f*mu2 + 0.5f*mu}; - const float a3{ 0.5f*mu3 + -0.5f*mu2}; - return val1*a0 + val2*a1 + val3*a2 + val4*a3; + using UT = std::make_unsigned_t; + return static_cast(value); } +constexpr auto GetCounterSuffix(size_t count) noexcept -> std::string_view +{ + using namespace std::string_view_literals; + return (((count%100)/10) == 1) ? "th"sv : + ((count%10) == 1) ? "st"sv : + ((count%10) == 2) ? "nd"sv : + ((count%10) == 3) ? "rd"sv : "th"sv; +} + + +constexpr auto lerpf(float val1, float val2, float mu) noexcept -> float +{ return val1 + (val2-val1)*mu; } +constexpr auto lerpd(double val1, double val2, double mu) noexcept -> double +{ return val1 + (val2-val1)*mu; } + + /** Find the next power-of-2 for non-power-of-2 numbers. */ inline uint32_t NextPowerOf2(uint32_t value) noexcept { @@ -129,24 +94,21 @@ constexpr T RoundUp(T value, al::type_identity_t r) noexcept */ inline int fastf2i(float f) noexcept { -#if defined(HAVE_SSE_INTRINSICS) +#if HAVE_SSE_INTRINSICS return _mm_cvt_ss2si(_mm_set_ss(f)); -#elif defined(_MSC_VER) && defined(_M_IX86_FP) +#elif defined(_MSC_VER) && defined(_M_IX86_FP) && _M_IX86_FP == 0 int i; __asm fld f __asm fistp i return i; -#elif (defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__)) +#elif (defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__)) \ + && !defined(__SSE_MATH__) int i; -#ifdef __SSE_MATH__ - __asm__("cvtss2si %1, %0" : "=r"(i) : "x"(f)); -#else __asm__ __volatile__("fistpl %0" : "=m"(i) : "t"(f) : "st"); -#endif return i; #else @@ -160,7 +122,7 @@ inline unsigned int fastf2u(float f) noexcept /** Converts float-to-int using standard behavior (truncation). */ inline int float2int(float f) noexcept { -#if defined(HAVE_SSE_INTRINSICS) +#if HAVE_SSE_INTRINSICS return _mm_cvtt_ss2si(_mm_set_ss(f)); #elif (defined(_MSC_VER) && defined(_M_IX86_FP) && _M_IX86_FP == 0) \ @@ -191,7 +153,7 @@ inline unsigned int float2uint(float f) noexcept /** Converts double-to-int using standard behavior (truncation). */ inline int double2int(double d) noexcept { -#if defined(HAVE_SSE_INTRINSICS) +#if HAVE_SSE_INTRINSICS return _mm_cvttsd_si32(_mm_set_sd(d)); #elif (defined(_MSC_VER) && defined(_M_IX86_FP) && _M_IX86_FP < 2) \ @@ -242,7 +204,7 @@ inline float fast_roundf(float f) noexcept /* Integral limit, where sub-integral precision is not available for * floats. */ - static constexpr float ilim[2]{ + static constexpr std::array ilim{ 8388608.0f /* 0x1.0p+23 */, -8388608.0f /* -0x1.0p+23 */ }; @@ -266,11 +228,15 @@ inline float fast_roundf(float f) noexcept * optimize this out because of non-associative rules on floating-point * math (as long as you don't use -fassociative-math, * -funsafe-math-optimizations, -ffast-math, or -Ofast, in which case this - * may break). + * may break without __builtin_assoc_barrier support). */ +#if HAS_BUILTIN(__builtin_assoc_barrier) + return __builtin_assoc_barrier(f + ilim[sign]) - ilim[sign]; +#else f += ilim[sign]; return f - ilim[sign]; #endif +#endif } @@ -285,9 +251,9 @@ inline float level_mb_to_gain(float x) // Converts gain to level (mB). inline float gain_to_level_mb(float x) { - if (x <= 0.0f) + if(x <= 1e-05f) return -10'000.0f; - return maxf(std::log10(x) * 2'000.0f, -10'000.0f); + return std::max(std::log10(x) * 2'000.0f, -10'000.0f); } #endif /* AL_NUMERIC_H */ diff --git a/3rdparty/openal/common/alsem.cpp b/3rdparty/openal/common/alsem.cpp index 6a92b35cf252..2eeed5929d84 100644 --- a/3rdparty/openal/common/alsem.cpp +++ b/3rdparty/openal/common/alsem.cpp @@ -24,8 +24,6 @@ #include -#include "opthelpers.h" - #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN @@ -39,7 +37,8 @@ semaphore::semaphore(unsigned int initial) { if(initial > static_cast(std::numeric_limits::max())) throw std::system_error(std::make_error_code(std::errc::value_too_large)); - mSem = CreateSemaphore(nullptr, initial, std::numeric_limits::max(), nullptr); + mSem = CreateSemaphoreW(nullptr, static_cast(initial), std::numeric_limits::max(), + nullptr); if(mSem == nullptr) throw std::system_error(std::make_error_code(std::errc::resource_unavailable_try_again)); } diff --git a/3rdparty/openal/common/alsem.h b/3rdparty/openal/common/alsem.h index 9f72d1c6510d..b23a54a1738b 100644 --- a/3rdparty/openal/common/alsem.h +++ b/3rdparty/openal/common/alsem.h @@ -24,10 +24,10 @@ class semaphore { #else using native_type = sem_t; #endif - native_type mSem; + native_type mSem{}; public: - semaphore(unsigned int initial=0); + explicit semaphore(unsigned int initial=0); semaphore(const semaphore&) = delete; ~semaphore(); diff --git a/3rdparty/openal/common/alspan.h b/3rdparty/openal/common/alspan.h index d5e42324b8c6..33587e6e1109 100644 --- a/3rdparty/openal/common/alspan.h +++ b/3rdparty/openal/common/alspan.h @@ -1,157 +1,255 @@ #ifndef AL_SPAN_H #define AL_SPAN_H -#include +#include #include -#include #include +#include +#include #include +#include +#include "alassert.h" #include "almalloc.h" #include "altraits.h" namespace al { -constexpr size_t dynamic_extent{static_cast(-1)}; +/* This is here primarily to help ensure proper behavior for span's iterators, + * being an actual object with member functions instead of a raw pointer (which + * has requirements like + and - working with ptrdiff_t). This also helps + * silence clang-tidy's pointer arithmetic warnings for span and FlexArray + * iterators. It otherwise behaves like a plain pointer and should optimize + * accordingly. + * + * Shouldn't be needed once we use std::span in C++20. + */ +template +class ptr_wrapper { + static_assert(std::is_pointer_v); + T mPointer{}; + +public: + using value_type = std::remove_pointer_t; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using pointer = value_type*; + using reference = value_type&; + using iterator_category = std::random_access_iterator_tag; + + explicit constexpr ptr_wrapper(T ptr) : mPointer{ptr} { } + + /* NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) */ + constexpr auto operator++() noexcept -> ptr_wrapper& { ++mPointer; return *this; } + constexpr auto operator--() noexcept -> ptr_wrapper& { --mPointer; return *this; } + constexpr auto operator++(int) noexcept -> ptr_wrapper + { + auto temp = *this; + ++*this; + return temp; + } + constexpr auto operator--(int) noexcept -> ptr_wrapper + { + auto temp = *this; + --*this; + return temp; + } + + constexpr + auto operator+=(std::ptrdiff_t n) noexcept -> ptr_wrapper& { mPointer += n; return *this; } + constexpr + auto operator-=(std::ptrdiff_t n) noexcept -> ptr_wrapper& { mPointer -= n; return *this; } + + [[nodiscard]] constexpr auto operator*() const noexcept -> value_type& { return *mPointer; } + [[nodiscard]] constexpr auto operator->() const noexcept -> value_type* { return mPointer; } + [[nodiscard]] constexpr + auto operator[](std::size_t idx) const noexcept -> value_type& {return mPointer[idx];} + + [[nodiscard]] friend constexpr + auto operator+(const ptr_wrapper &lhs, std::ptrdiff_t n) noexcept -> ptr_wrapper + { return ptr_wrapper{lhs.mPointer + n}; } + [[nodiscard]] friend constexpr + auto operator+(std::ptrdiff_t n, const ptr_wrapper &rhs) noexcept -> ptr_wrapper + { return ptr_wrapper{n + rhs.mPointer}; } + [[nodiscard]] friend constexpr + auto operator-(const ptr_wrapper &lhs, std::ptrdiff_t n) noexcept -> ptr_wrapper + { return ptr_wrapper{lhs.mPointer - n}; } + + [[nodiscard]] friend constexpr + auto operator-(const ptr_wrapper &lhs, const ptr_wrapper &rhs)noexcept->std::ptrdiff_t + { return lhs.mPointer - rhs.mPointer; } + + [[nodiscard]] friend constexpr + auto operator==(const ptr_wrapper &lhs, const ptr_wrapper &rhs) noexcept -> bool + { return lhs.mPointer == rhs.mPointer; } + [[nodiscard]] friend constexpr + auto operator!=(const ptr_wrapper &lhs, const ptr_wrapper &rhs) noexcept -> bool + { return lhs.mPointer != rhs.mPointer; } + [[nodiscard]] friend constexpr + auto operator<=(const ptr_wrapper &lhs, const ptr_wrapper &rhs) noexcept -> bool + { return lhs.mPointer <= rhs.mPointer; } + [[nodiscard]] friend constexpr + auto operator>=(const ptr_wrapper &lhs, const ptr_wrapper &rhs) noexcept -> bool + { return lhs.mPointer >= rhs.mPointer; } + [[nodiscard]] friend constexpr + auto operator<(const ptr_wrapper &lhs, const ptr_wrapper &rhs) noexcept -> bool + { return lhs.mPointer < rhs.mPointer; } + [[nodiscard]] friend constexpr + auto operator>(const ptr_wrapper &lhs, const ptr_wrapper &rhs) noexcept -> bool + { return lhs.mPointer > rhs.mPointer; } + /* NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) */ +}; + -template +inline constexpr std::size_t dynamic_extent{static_cast(-1)}; + +template class span; namespace detail_ { template struct is_span_ : std::false_type { }; - template + template struct is_span_> : std::true_type { }; template - constexpr bool is_span_v = is_span_>::value; + inline constexpr bool is_span_v = is_span_>::value; template struct is_std_array_ : std::false_type { }; - template + template struct is_std_array_> : std::true_type { }; template - constexpr bool is_std_array_v = is_std_array_>::value; + inline constexpr bool is_std_array_v = is_std_array_>::value; template - constexpr bool has_size_and_data = false; + inline constexpr bool has_size_and_data = false; template - constexpr bool has_size_and_data())),decltype(std::data(std::declval()))>> = true; template - constexpr bool is_valid_container_type = !is_span_v && !is_std_array_v + inline constexpr bool is_valid_container_type = !is_span_v && !is_std_array_v && !std::is_array::value && has_size_and_data; template - constexpr bool is_array_compatible = std::is_convertible::value; + inline constexpr bool is_array_compatible = std::is_convertible::value; /* NOLINT(*-avoid-c-arrays) */ template - constexpr bool is_valid_container = is_valid_container_type + inline constexpr bool is_valid_container = is_valid_container_type && is_array_compatible()))>,T>; } // namespace detail_ #define REQUIRES(...) std::enable_if_t<(__VA_ARGS__),bool> = true -template +/* NOLINTBEGIN(google-explicit-constructor) This largely follows std::span's + * constructor behavior, and should be replaced once C++20 is used. + */ +template class span { public: using element_type = T; using value_type = std::remove_cv_t; - using index_type = size_t; - using difference_type = ptrdiff_t; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; using pointer = T*; using const_pointer = const T*; using reference = T&; using const_reference = const T&; - using iterator = pointer; - using const_iterator = const_pointer; + using iterator = ptr_wrapper; + using const_iterator = ptr_wrapper; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; - static constexpr size_t extent{E}; + static constexpr std::size_t extent{E}; template constexpr span() noexcept { } template - constexpr explicit span(U iter, index_type) : mData{::al::to_address(iter)} { } - template::value)> - constexpr explicit span(U first, V) : mData{::al::to_address(first)} - {} - - constexpr span(type_identity_t (&arr)[E]) noexcept - : span{std::data(arr), std::size(arr)} - { } - constexpr span(std::array &arr) noexcept - : span{std::data(arr), std::size(arr)} - { } - template::value)> - constexpr span(const std::array &arr) noexcept - : span{std::data(arr), std::size(arr)} - { } + constexpr explicit span(U iter, size_type size_) : mData{::al::to_address(iter)} + { alassert(size_ == extent); } + template::value)> + constexpr explicit span(U first, V last) : mData{::al::to_address(first)} + { alassert(static_cast(last-first) == extent); } + + template + constexpr span(type_identity_t (&arr)[N]) noexcept /* NOLINT(*-avoid-c-arrays) */ + : mData{std::data(arr)} + { static_assert(N == extent); } + template + constexpr span(std::array &arr) noexcept : mData{std::data(arr)} + { static_assert(N == extent); } + template::value)> + constexpr span(const std::array &arr) noexcept : mData{std::data(arr)} + { static_assert(N == extent); } template)> constexpr explicit span(U&& cont) : span{std::data(cont), std::size(cont)} { } - template::value + template::value && detail_::is_array_compatible && N == dynamic_extent)> - constexpr explicit span(const span &span_) noexcept - : span{std::data(span_), std::size(span_)} - { } - template::value + constexpr explicit span(const span &span_) noexcept : mData{std::data(span_)} + { alassert(std::size(span_) == extent); } + template::value && detail_::is_array_compatible && N == extent)> - constexpr span(const span &span_) noexcept : span{std::data(span_), std::size(span_)} { } + constexpr span(const span &span_) noexcept : mData{std::data(span_)} { } constexpr span(const span&) noexcept = default; constexpr span& operator=(const span &rhs) noexcept = default; - constexpr reference front() const { return *mData; } - constexpr reference back() const { return *(mData+E-1); } - constexpr reference operator[](index_type idx) const { return mData[idx]; } - constexpr pointer data() const noexcept { return mData; } - - constexpr index_type size() const noexcept { return E; } - constexpr index_type size_bytes() const noexcept { return E * sizeof(value_type); } - constexpr bool empty() const noexcept { return E == 0; } - - constexpr iterator begin() const noexcept { return mData; } - constexpr iterator end() const noexcept { return mData+E; } - constexpr const_iterator cbegin() const noexcept { return mData; } - constexpr const_iterator cend() const noexcept { return mData+E; } - - constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; } - constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; } - constexpr const_reverse_iterator crbegin() const noexcept - { return const_reverse_iterator{cend()}; } - constexpr const_reverse_iterator crend() const noexcept - { return const_reverse_iterator{cbegin()}; } - - template - constexpr span first() const + [[nodiscard]] constexpr auto front() const -> reference { return mData[0]; } + [[nodiscard]] constexpr auto back() const -> reference { return mData[E-1]; } + [[nodiscard]] constexpr auto operator[](size_type idx) const -> reference { return mData[idx]; } + [[nodiscard]] constexpr auto data() const noexcept -> pointer { return mData; } + + [[nodiscard]] constexpr auto size() const noexcept -> size_type { return E; } + [[nodiscard]] constexpr auto size_bytes() const noexcept -> size_type { return E * sizeof(value_type); } + [[nodiscard]] constexpr auto empty() const noexcept -> bool { return E == 0; } + + [[nodiscard]] constexpr auto begin() const noexcept -> iterator { return iterator{mData}; } + [[nodiscard]] constexpr auto end() const noexcept -> iterator { return iterator{mData+E}; } + [[nodiscard]] constexpr + auto cbegin() const noexcept -> const_iterator { return const_iterator{mData}; } + [[nodiscard]] constexpr + auto cend() const noexcept -> const_iterator { return const_iterator{mData+E}; } + + [[nodiscard]] constexpr + auto rbegin() const noexcept -> reverse_iterator { return reverse_iterator{end()}; } + [[nodiscard]] constexpr + auto rend() const noexcept -> reverse_iterator { return reverse_iterator{begin()}; } + [[nodiscard]] constexpr + auto crbegin() const noexcept -> const_reverse_iterator { return cend(); } + [[nodiscard]] constexpr + auto crend() const noexcept -> const_reverse_iterator { return cbegin(); } + + template + [[nodiscard]] constexpr auto first() const noexcept -> span { static_assert(E >= C, "New size exceeds original capacity"); return span{mData, C}; } - template - constexpr span last() const + template + [[nodiscard]] constexpr auto last() const noexcept -> span { static_assert(E >= C, "New size exceeds original capacity"); return span{mData+(E-C), C}; } - template - constexpr auto subspan() const -> std::enable_if_t> + template + [[nodiscard]] constexpr + auto subspan() const noexcept -> std::enable_if_t> { static_assert(E >= O, "Offset exceeds extent"); static_assert(E-O >= C, "New size exceeds original capacity"); return span{mData+O, C}; } - template - constexpr auto subspan() const -> std::enable_if_t> + template + [[nodiscard]] constexpr + auto subspan() const noexcept -> std::enable_if_t> { static_assert(E >= O, "Offset exceeds extent"); return span{mData+O, E-O}; @@ -161,10 +259,13 @@ class span { * defining the specialization. As a result, these methods need to be * defined later. */ - constexpr span first(size_t count) const; - constexpr span last(size_t count) const; - constexpr span subspan(size_t offset, - size_t count=dynamic_extent) const; + [[nodiscard]] constexpr + auto first(std::size_t count) const noexcept -> span; + [[nodiscard]] constexpr + auto last(std::size_t count) const noexcept -> span; + [[nodiscard]] constexpr + auto subspan(std::size_t offset, std::size_t count=dynamic_extent) const noexcept + -> span; private: pointer mData{nullptr}; @@ -175,7 +276,7 @@ class span { public: using element_type = T; using value_type = std::remove_cv_t; - using index_type = size_t; + using size_type = std::size_t; using difference_type = ptrdiff_t; using pointer = T*; @@ -183,129 +284,168 @@ class span { using reference = T&; using const_reference = const T&; - using iterator = pointer; - using const_iterator = const_pointer; + using iterator = ptr_wrapper; + using const_iterator = ptr_wrapper; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; - static constexpr size_t extent{dynamic_extent}; + static constexpr std::size_t extent{dynamic_extent}; constexpr span() noexcept = default; template - constexpr span(U iter, index_type count) : mData{::al::to_address(iter)}, mDataEnd{::al::to_address(iter) + count} + constexpr span(U iter, size_type count) : mData{::al::to_address(iter)}, mDataLength{count} { } - template::value)> - constexpr span(U first, V last) : span{::al::to_address(first), static_cast(last - first)} + template::value)> + constexpr span(U first, V last) + : span{::al::to_address(first), static_cast(last-first)} { } - template - constexpr span(type_identity_t (&arr)[N]) noexcept - : span{std::data(arr), std::size(arr)} + template + constexpr span(type_identity_t (&arr)[N]) noexcept /* NOLINT(*-avoid-c-arrays) */ + : mData{std::data(arr)}, mDataLength{std::size(arr)} { } - template + template constexpr span(std::array &arr) noexcept - : span{std::data(arr), std::size(arr)} + : mData{std::data(arr)}, mDataLength{std::size(arr)} { } - template::value)> + template::value)> constexpr span(const std::array &arr) noexcept - : span{std::data(arr), std::size(arr)} + : mData{std::data(arr)}, mDataLength{std::size(arr)} { } template)> constexpr span(U&& cont) : span{std::data(cont), std::size(cont)} { } - template::value || extent != N) - && detail_::is_array_compatible)> + template + && (!std::is_same::value || extent != N))> constexpr span(const span &span_) noexcept : span{std::data(span_), std::size(span_)} { } constexpr span(const span&) noexcept = default; constexpr span& operator=(const span &rhs) noexcept = default; - constexpr reference front() const { return *mData; } - constexpr reference back() const { return *(mDataEnd-1); } - constexpr reference operator[](index_type idx) const { return mData[idx]; } - constexpr pointer data() const noexcept { return mData; } - - constexpr index_type size() const noexcept { return static_cast(mDataEnd-mData); } - constexpr index_type size_bytes() const noexcept - { return static_cast(mDataEnd-mData) * sizeof(value_type); } - constexpr bool empty() const noexcept { return mData == mDataEnd; } - - constexpr iterator begin() const noexcept { return mData; } - constexpr iterator end() const noexcept { return mDataEnd; } - constexpr const_iterator cbegin() const noexcept { return mData; } - constexpr const_iterator cend() const noexcept { return mDataEnd; } - - constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; } - constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; } - constexpr const_reverse_iterator crbegin() const noexcept - { return const_reverse_iterator{cend()}; } - constexpr const_reverse_iterator crend() const noexcept - { return const_reverse_iterator{cbegin()}; } - - template - constexpr span first() const - { return span{mData, C}; } + [[nodiscard]] constexpr auto front() const -> reference { return mData[0]; } + [[nodiscard]] constexpr auto back() const -> reference { return mData[mDataLength-1]; } + [[nodiscard]] constexpr auto operator[](size_type idx) const -> reference {return mData[idx];} + [[nodiscard]] constexpr auto data() const noexcept -> pointer { return mData; } + + [[nodiscard]] constexpr auto size() const noexcept -> size_type { return mDataLength; } + [[nodiscard]] constexpr + auto size_bytes() const noexcept -> size_type { return mDataLength * sizeof(value_type); } + [[nodiscard]] constexpr auto empty() const noexcept -> bool { return mDataLength == 0; } + + [[nodiscard]] constexpr auto begin() const noexcept -> iterator { return iterator{mData}; } + [[nodiscard]] constexpr + auto end() const noexcept -> iterator { return iterator{mData+mDataLength}; } + [[nodiscard]] constexpr + auto cbegin() const noexcept -> const_iterator { return const_iterator{mData}; } + [[nodiscard]] constexpr + auto cend() const noexcept -> const_iterator { return const_iterator{mData+mDataLength}; } + + [[nodiscard]] constexpr + auto rbegin() const noexcept -> reverse_iterator { return reverse_iterator{end()}; } + [[nodiscard]] constexpr + auto rend() const noexcept -> reverse_iterator { return reverse_iterator{begin()}; } + [[nodiscard]] constexpr + auto crbegin() const noexcept -> const_reverse_iterator { return cend(); } + [[nodiscard]] constexpr + auto crend() const noexcept -> const_reverse_iterator { return cbegin(); } + + template + [[nodiscard]] constexpr auto first() const noexcept -> span + { + assert(C <= mDataLength); + return span{mData, C}; + } - constexpr span first(size_t count) const - { return (count >= size()) ? *this : span{mData, mData+count}; } + [[nodiscard]] constexpr auto first(std::size_t count) const noexcept -> span + { + assert(count <= mDataLength); + return span{mData, count}; + } - template - constexpr span last() const - { return span{mDataEnd-C, C}; } + template + [[nodiscard]] constexpr auto last() const noexcept -> span + { + assert(C <= mDataLength); + return span{mData+mDataLength-C, C}; + } - constexpr span last(size_t count) const - { return (count >= size()) ? *this : span{mDataEnd-count, mDataEnd}; } + [[nodiscard]] constexpr auto last(std::size_t count) const noexcept -> span + { + assert(count <= mDataLength); + return span{mData+mDataLength-count, count}; + } - template - constexpr auto subspan() const -> std::enable_if_t> - { return span{mData+O, C}; } + template + [[nodiscard]] constexpr + auto subspan() const noexcept -> std::enable_if_t> + { + assert(O <= mDataLength); + assert(C <= mDataLength-O); + return span{mData+O, C}; + } - template - constexpr auto subspan() const -> std::enable_if_t> - { return span{mData+O, mDataEnd}; } + template + [[nodiscard]] constexpr + auto subspan() const noexcept -> std::enable_if_t> + { + assert(O <= mDataLength); + return span{mData+O, mDataLength-O}; + } - constexpr span subspan(size_t offset, size_t count=dynamic_extent) const + [[nodiscard]] constexpr + auto subspan(std::size_t offset, std::size_t count=dynamic_extent) const noexcept -> span { - return (offset > size()) ? span{} : - (count >= size()-offset) ? span{mData+offset, mDataEnd} : - span{mData+offset, mData+offset+count}; + assert(offset <= mDataLength); + if(count != dynamic_extent) + { + assert(count <= mDataLength-offset); + return span{mData+offset, count}; + } + return span{mData+offset, mDataLength-offset}; } private: pointer mData{nullptr}; - pointer mDataEnd{nullptr}; + size_type mDataLength{0}; }; -template -constexpr inline auto span::first(size_t count) const -> span +template +[[nodiscard]] constexpr +auto span::first(std::size_t count) const noexcept -> span { - return (count >= size()) ? span{mData, extent} : - span{mData, count}; + assert(count <= size()); + return span{mData, count}; } -template -constexpr inline auto span::last(size_t count) const -> span +template +[[nodiscard]] constexpr +auto span::last(std::size_t count) const noexcept -> span { - return (count >= size()) ? span{mData, extent} : - span{mData+extent-count, count}; + assert(count <= size()); + return span{mData+size()-count, count}; } -template -constexpr inline auto span::subspan(size_t offset, size_t count) const +template +[[nodiscard]] constexpr +auto span::subspan(std::size_t offset, std::size_t count) const noexcept -> span { - return (offset > size()) ? span{} : - (count >= size()-offset) ? span{mData+offset, mData+extent} : - span{mData+offset, mData+offset+count}; + assert(offset <= size()); + if(count != dynamic_extent) + { + assert(count <= size()-offset); + return span{mData+offset, count}; + } + return span{mData+offset, size()-offset}; } - +/* NOLINTEND(google-explicit-constructor) */ template span(T, EndOrSize) -> span())>>; template -span(T (&)[N]) -> span; +span(T (&)[N]) -> span; /* NOLINT(*-avoid-c-arrays) */ template span(std::array&) -> span; @@ -314,7 +454,7 @@ template span(const std::array&) -> span; template)> -span(C&&) -> span()))>>; +span(C&&) -> span()))>>; #undef REQUIRES diff --git a/3rdparty/openal/common/alstring.cpp b/3rdparty/openal/common/alstring.cpp index 4a84be1db24a..d609823dfc48 100644 --- a/3rdparty/openal/common/alstring.cpp +++ b/3rdparty/openal/common/alstring.cpp @@ -3,42 +3,51 @@ #include "alstring.h" +#include #include -#include +#include +#include -namespace { +namespace al { -int to_upper(const char ch) +int case_compare(const std::string_view str0, const std::string_view str1) noexcept { - using char8_traits = std::char_traits; - return std::toupper(char8_traits::to_int_type(ch)); -} - -} // namespace + using Traits = std::string_view::traits_type; -namespace al { + auto ch0 = str0.cbegin(); + auto ch1 = str1.cbegin(); + auto ch1end = ch1 + std::min(str0.size(), str1.size()); + while(ch1 != ch1end) + { + const int u0{std::toupper(Traits::to_int_type(*ch0))}; + const int u1{std::toupper(Traits::to_int_type(*ch1))}; + if(const int diff{u0-u1}) return diff; + ++ch0; ++ch1; + } -int strcasecmp(const char *str0, const char *str1) noexcept -{ - do { - const int diff{to_upper(*str0) - to_upper(*str1)}; - if(diff < 0) return -1; - if(diff > 0) return 1; - } while(*(str0++) && *(str1++)); + if(str0.size() < str1.size()) return -1; + if(str0.size() > str1.size()) return 1; return 0; } -int strncasecmp(const char *str0, const char *str1, std::size_t len) noexcept +int case_compare(const std::wstring_view str0, const std::wstring_view str1) noexcept { - if(len > 0) + using Traits = std::wstring_view::traits_type; + + auto ch0 = str0.cbegin(); + auto ch1 = str1.cbegin(); + auto ch1end = ch1 + std::min(str0.size(), str1.size()); + while(ch1 != ch1end) { - do { - const int diff{to_upper(*str0) - to_upper(*str1)}; - if(diff < 0) return -1; - if(diff > 0) return 1; - } while(--len && *(str0++) && *(str1++)); + const auto u0 = std::towupper(Traits::to_int_type(*ch0)); + const auto u1 = std::towupper(Traits::to_int_type(*ch1)); + if(const auto diff = static_cast(u0-u1)) return diff; + ++ch0; ++ch1; } + + if(str0.size() < str1.size()) return -1; + if(str0.size() > str1.size()) return 1; return 0; } diff --git a/3rdparty/openal/common/alstring.h b/3rdparty/openal/common/alstring.h index 6c5004ee1abe..c03936136724 100644 --- a/3rdparty/openal/common/alstring.h +++ b/3rdparty/openal/common/alstring.h @@ -1,17 +1,58 @@ #ifndef AL_STRING_H #define AL_STRING_H +#include #include -#include +#include +#include +#include namespace al { -/* These would be better served by using a string_view-like span/view with - * case-insensitive char traits. +template +[[nodiscard]] constexpr +auto sizei(const std::basic_string_view str) noexcept -> int +{ return static_cast(std::min(str.size(), std::numeric_limits::max())); } + +template +[[nodiscard]] constexpr +auto sizei(const std::basic_string &str) noexcept -> int +{ return static_cast(std::min(str.size(), std::numeric_limits::max())); } + + +[[nodiscard]] +constexpr bool contains(const std::string_view str0, const std::string_view str1) noexcept +{ return str0.find(str1) != std::string_view::npos; } + +[[nodiscard]] +constexpr bool starts_with(const std::string_view str0, const std::string_view str1) noexcept +{ return str0.substr(0, std::min(str0.size(), str1.size())) == str1; } + +[[nodiscard]] +constexpr bool ends_with(const std::string_view str0, const std::string_view str1) noexcept +{ return str0.substr(str0.size() - std::min(str0.size(), str1.size())) == str1; } + +[[nodiscard]] +int case_compare(const std::string_view str0, const std::string_view str1) noexcept; + +[[nodiscard]] +int case_compare(const std::wstring_view str0, const std::wstring_view str1) noexcept; + +/* C++20 changes path::u8string() to return a string using a new/distinct + * char8_t type for UTF-8 strings. However, support for this with standard + * string functions is totally inadequate, and we already hold UTF-8 with plain + * char strings. So this function is used to reinterpret a char8_t string as a + * char string_view. */ -int strcasecmp(const char *str0, const char *str1) noexcept; -int strncasecmp(const char *str0, const char *str1, std::size_t len) noexcept; +#if defined(__cpp_lib_char8_t) && __cpp_lib_char8_t >= 201907L +inline auto u8_as_char(const std::u8string_view str) -> std::string_view +#else +inline auto u8_as_char(const std::string_view str) -> std::string_view +#endif +{ + return std::string_view{reinterpret_cast(str.data()), str.size()}; +} } // namespace al diff --git a/3rdparty/openal/common/althrd_setname.cpp b/3rdparty/openal/common/althrd_setname.cpp index 22d330923f4f..b92b05d1c91f 100644 --- a/3rdparty/openal/common/althrd_setname.cpp +++ b/3rdparty/openal/common/althrd_setname.cpp @@ -14,13 +14,14 @@ void althrd_setname(const char *name [[maybe_unused]]) #define MS_VC_EXCEPTION 0x406D1388 #pragma pack(push,8) - struct { + struct InfoStruct { DWORD dwType; // Must be 0x1000. LPCSTR szName; // Pointer to name (in user addr space). DWORD dwThreadID; // Thread ID (-1=caller thread). DWORD dwFlags; // Reserved for future use, must be zero. - } info; + }; #pragma pack(pop) + InfoStruct info{}; info.dwType = 0x1000; info.szName = name; info.dwThreadID = ~DWORD{0}; @@ -60,7 +61,7 @@ using setname_t4 = int(*)(pthread_t, const char*, void*); { func(pthread_self(), name); } [[maybe_unused]] void setname_caller(setname_t4 func, const char *name) -{ func(pthread_self(), "%s", static_cast(const_cast(name))); } +{ func(pthread_self(), "%s", const_cast(name)); /* NOLINT(*-const-cast) */ } } // namespace diff --git a/3rdparty/openal/common/althreads.h b/3rdparty/openal/common/althreads.h new file mode 100644 index 000000000000..66e62e555680 --- /dev/null +++ b/3rdparty/openal/common/althreads.h @@ -0,0 +1,143 @@ +#ifndef AL_THREADS_H +#define AL_THREADS_H + +#include +#include +#include + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include + +#elif defined(__STDC_NO_THREADS__) || !__has_include() + +#include + +#else + +#include +#endif + +#include "albit.h" + +namespace al { + +template +class tss { + static_assert(sizeof(T) <= sizeof(void*)); + static_assert(std::is_trivially_destructible_v && std::is_trivially_copy_constructible_v); + + [[nodiscard]] + static auto to_ptr(const T &value) noexcept -> void* + { + if constexpr(std::is_pointer_v) + { + if constexpr(std::is_const_v>) + return const_cast(static_cast(value)); /* NOLINT(*-const-cast) */ + else + return static_cast(value); + } + else if constexpr(sizeof(T) == sizeof(void*)) + return al::bit_cast(value); + else if constexpr(std::is_integral_v) + return al::bit_cast(static_cast(value)); + } + + [[nodiscard]] + static auto from_ptr(void *ptr) noexcept -> T + { + if constexpr(std::is_pointer_v) + return static_cast(ptr); + else if constexpr(sizeof(T) == sizeof(void*)) + return al::bit_cast(ptr); + else if constexpr(std::is_integral_v) + return static_cast(al::bit_cast(ptr)); + } + +#ifdef _WIN32 + DWORD mTss{TLS_OUT_OF_INDEXES}; + +public: + tss() : mTss{TlsAlloc()} + { + if(mTss == TLS_OUT_OF_INDEXES) + throw std::runtime_error{"al::tss::tss()"}; + } + explicit tss(const T &init) : tss{} + { + if(TlsSetValue(mTss, to_ptr(init)) == FALSE) + throw std::runtime_error{"al::tss::tss(T)"}; + } + ~tss() { TlsFree(mTss); } + + void set(const T &value) const + { + if(TlsSetValue(mTss, to_ptr(value)) == FALSE) + throw std::runtime_error{"al::tss::set(T)"}; + } + + [[nodiscard]] + auto get() const noexcept -> T { return from_ptr(TlsGetValue(mTss)); } + +#elif defined(__STDC_NO_THREADS__) || !__has_include() + + pthread_key_t mTss{}; + +public: + tss() + { + if(int res{pthread_key_create(&mTss, nullptr)}; res != 0) + throw std::runtime_error{"al::tss::tss()"}; + } + explicit tss(const T &init) : tss{} + { + if(int res{pthread_setspecific(mTss, to_ptr(init))}; res != 0) + throw std::runtime_error{"al::tss::tss(T)"}; + } + ~tss() { pthread_key_delete(mTss); } + + void set(const T &value) const + { + if(int res{pthread_setspecific(mTss, to_ptr(value))}; res != 0) + throw std::runtime_error{"al::tss::set(T)"}; + } + + [[nodiscard]] + auto get() const noexcept -> T { return from_ptr(pthread_getspecific(mTss)); } + +#else + + tss_t mTss{}; + +public: + tss() + { + if(int res{tss_create(&mTss, nullptr)}; res != thrd_success) + throw std::runtime_error{"al::tss::tss()"}; + } + explicit tss(const T &init) : tss{} + { + if(int res{tss_set(mTss, to_ptr(init))}; res != thrd_success) + throw std::runtime_error{"al::tss::tss(T)"}; + } + ~tss() { tss_delete(mTss); } + + void set(const T &value) const + { + if(int res{tss_set(mTss, to_ptr(value))}; res != thrd_success) + throw std::runtime_error{"al::tss::set(T)"}; + } + + [[nodiscard]] + auto get() const noexcept -> T { return from_ptr(tss_get(mTss)); } +#endif /* _WIN32 */ + + tss(const tss&) = delete; + tss(tss&&) = delete; + void operator=(const tss&) = delete; + void operator=(tss&&) = delete; +}; + +} // namespace al + +#endif /* AL_THREADS_H */ diff --git a/3rdparty/openal/common/atomic.h b/3rdparty/openal/common/atomic.h index 5e9b04c6975f..7075b15892f8 100644 --- a/3rdparty/openal/common/atomic.h +++ b/3rdparty/openal/common/atomic.h @@ -2,17 +2,17 @@ #define AL_ATOMIC_H #include +#include +#include +#include "almalloc.h" -using RefCount = std::atomic; - -inline void InitRef(RefCount &ref, unsigned int value) -{ ref.store(value, std::memory_order_relaxed); } -inline unsigned int ReadRef(RefCount &ref) -{ return ref.load(std::memory_order_acquire); } -inline unsigned int IncrementRef(RefCount &ref) +template +auto IncrementRef(std::atomic &ref) noexcept { return ref.fetch_add(1u, std::memory_order_acq_rel)+1u; } -inline unsigned int DecrementRef(RefCount &ref) + +template +auto DecrementRef(std::atomic &ref) noexcept { return ref.fetch_sub(1u, std::memory_order_acq_rel)-1u; } @@ -30,4 +30,75 @@ inline void AtomicReplaceHead(std::atomic &head, T newhead) std::memory_order_acq_rel, std::memory_order_acquire)); } +namespace al { + +template> +class atomic_unique_ptr { + std::atomic> mPointer{}; + + using unique_ptr_t = std::unique_ptr; + +public: + atomic_unique_ptr() = default; + atomic_unique_ptr(const atomic_unique_ptr&) = delete; + explicit atomic_unique_ptr(std::nullptr_t) noexcept { } + explicit atomic_unique_ptr(gsl::owner ptr) noexcept : mPointer{ptr} { } + explicit atomic_unique_ptr(unique_ptr_t&& rhs) noexcept : mPointer{rhs.release()} { } + ~atomic_unique_ptr() + { + if(auto ptr = mPointer.exchange(nullptr, std::memory_order_relaxed)) + D{}(ptr); + } + + auto operator=(const atomic_unique_ptr&) -> atomic_unique_ptr& = delete; + auto operator=(std::nullptr_t) noexcept -> atomic_unique_ptr& + { + if(auto ptr = mPointer.exchange(nullptr)) + D{}(ptr); + return *this; + } + auto operator=(unique_ptr_t&& rhs) noexcept -> atomic_unique_ptr& + { + if(auto ptr = mPointer.exchange(rhs.release())) + D{}(ptr); + return *this; + } + + [[nodiscard]] + auto load(std::memory_order m=std::memory_order_seq_cst) const noexcept -> T* + { return mPointer.load(m); } + void store(std::nullptr_t, std::memory_order m=std::memory_order_seq_cst) noexcept + { + if(auto oldptr = mPointer.exchange(nullptr, m)) + D{}(oldptr); + } + void store(gsl::owner ptr, std::memory_order m=std::memory_order_seq_cst) noexcept + { + if(auto oldptr = mPointer.exchange(ptr, m)) + D{}(oldptr); + } + void store(unique_ptr_t&& ptr, std::memory_order m=std::memory_order_seq_cst) noexcept + { + if(auto oldptr = mPointer.exchange(ptr.release(), m)) + D{}(oldptr); + } + + [[nodiscard]] + auto exchange(std::nullptr_t, std::memory_order m=std::memory_order_seq_cst) noexcept -> unique_ptr_t + { return unique_ptr_t{mPointer.exchange(nullptr, m)}; } + [[nodiscard]] + auto exchange(gsl::owner ptr, std::memory_order m=std::memory_order_seq_cst) noexcept -> unique_ptr_t + { return unique_ptr_t{mPointer.exchange(ptr, m)}; } + [[nodiscard]] + auto exchange(std::unique_ptr&& ptr, std::memory_order m=std::memory_order_seq_cst) noexcept -> unique_ptr_t + { return unique_ptr_t{mPointer.exchange(ptr.release(), m)}; } + + [[nodiscard]] + auto is_lock_free() const noexcept -> bool { return mPointer.is_lock_free(); } + + static constexpr auto is_always_lock_free = std::atomic>::is_always_lock_free; +}; + +} // namespace al + #endif /* AL_ATOMIC_H */ diff --git a/3rdparty/openal/common/comptr.h b/3rdparty/openal/common/comptr.h index 5a733ea2e824..c592f1d051e1 100644 --- a/3rdparty/openal/common/comptr.h +++ b/3rdparty/openal/common/comptr.h @@ -1,13 +1,50 @@ #ifndef COMMON_COMPTR_H #define COMMON_COMPTR_H +#ifdef _WIN32 #include -#include -#include #include -#include -template +#define WIN32_LEAN_AND_MEAN +#include +#include + +struct ComWrapper { + HRESULT mStatus{}; + + ComWrapper(void *reserved, DWORD coinit) + : mStatus{CoInitializeEx(reserved, coinit)} + { } + explicit ComWrapper(DWORD coinit=COINIT_APARTMENTTHREADED) + : mStatus{CoInitializeEx(nullptr, coinit)} + { } + ComWrapper(ComWrapper&& rhs) { mStatus = std::exchange(rhs.mStatus, E_FAIL); } + ComWrapper(const ComWrapper&) = delete; + ~ComWrapper() { if(SUCCEEDED(mStatus)) CoUninitialize(); } + + ComWrapper& operator=(ComWrapper&& rhs) + { + if(SUCCEEDED(mStatus)) + CoUninitialize(); + mStatus = std::exchange(rhs.mStatus, E_FAIL); + return *this; + } + ComWrapper& operator=(const ComWrapper&) = delete; + + [[nodiscard]] + HRESULT status() const noexcept { return mStatus; } + explicit operator bool() const noexcept { return SUCCEEDED(status()); } + + void uninit() + { + if(SUCCEEDED(mStatus)) + CoUninitialize(); + mStatus = E_FAIL; + } +}; + + +template /* NOLINTNEXTLINE(clazy-rule-of-three) False positive */ struct ComPtr { using element_type = T; @@ -18,10 +55,11 @@ struct ComPtr { ComPtr(const ComPtr &rhs) noexcept(RefIsNoexcept) : mPtr{rhs.mPtr} { if(mPtr) mPtr->AddRef(); } ComPtr(ComPtr&& rhs) noexcept : mPtr{rhs.mPtr} { rhs.mPtr = nullptr; } - ComPtr(std::nullptr_t) noexcept { } + ComPtr(std::nullptr_t) noexcept { } /* NOLINT(google-explicit-constructor) */ explicit ComPtr(T *ptr) noexcept : mPtr{ptr} { } ~ComPtr() { if(mPtr) mPtr->Release(); } + /* NOLINTNEXTLINE(bugprone-unhandled-self-assignment) Yes it is. */ ComPtr& operator=(const ComPtr &rhs) noexcept(RefIsNoexcept) { if constexpr(RefIsNoexcept) @@ -69,43 +107,6 @@ struct ComPtr { private: T *mPtr{nullptr}; }; - - -namespace al { - -template -class out_ptr_t { - static_assert(!std::is_same_v); - - SP &mRes; - std::variant mPtr{}; - -public: - out_ptr_t(SP &res) : mRes{res} { } - ~out_ptr_t() - { - auto set_res = [this](auto &ptr) - { mRes.reset(static_cast(ptr)); }; - std::visit(set_res, mPtr); - } - out_ptr_t(const out_ptr_t&) = delete; - - out_ptr_t& operator=(const out_ptr_t&) = delete; - - operator PT*() noexcept - { return &std::get(mPtr); } - - operator void**() noexcept - { return &mPtr.template emplace(); } -}; - -template -auto out_ptr(SP &res) -{ - using ptype = typename SP::element_type*; - return out_ptr_t{res}; -} - -} // namespace al +#endif /* _WIN32 */ #endif diff --git a/3rdparty/openal/common/dynload.cpp b/3rdparty/openal/common/dynload.cpp index 86c36e003a72..333a9435ee07 100644 --- a/3rdparty/openal/common/dynload.cpp +++ b/3rdparty/openal/common/dynload.cpp @@ -3,13 +3,12 @@ #include "dynload.h" -#include "albit.h" -#include "strutils.h" - #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include +#include "strutils.h" + void *LoadLib(const char *name) { std::wstring wname{utf8_to_wstr(name)}; @@ -18,7 +17,7 @@ void *LoadLib(const char *name) void CloseLib(void *handle) { FreeLibrary(static_cast(handle)); } void *GetSymbol(void *handle, const char *name) -{ return al::bit_cast(GetProcAddress(static_cast(handle), name)); } +{ return reinterpret_cast(GetProcAddress(static_cast(handle), name)); } #elif defined(HAVE_DLFCN_H) diff --git a/3rdparty/openal/common/dynload.h b/3rdparty/openal/common/dynload.h index bd9e86f7907f..ce7c8f030076 100644 --- a/3rdparty/openal/common/dynload.h +++ b/3rdparty/openal/common/dynload.h @@ -3,12 +3,16 @@ #if defined(_WIN32) || defined(HAVE_DLFCN_H) -#define HAVE_DYNLOAD +#define HAVE_DYNLOAD 1 void *LoadLib(const char *name); void CloseLib(void *handle); void *GetSymbol(void *handle, const char *name); +#else + +#define HAVE_DYNLOAD 0 + #endif #endif /* AL_DYNLOAD_H */ diff --git a/3rdparty/openal/common/filesystem.cpp b/3rdparty/openal/common/filesystem.cpp new file mode 100644 index 000000000000..58da22a7daed --- /dev/null +++ b/3rdparty/openal/common/filesystem.cpp @@ -0,0 +1,61 @@ +//--------------------------------------------------------------------------------------- +// +// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14 +// +//--------------------------------------------------------------------------------------- +// +// Copyright (c) 2018, Steffen Schümann +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +//--------------------------------------------------------------------------------------- +// fs_std_impl.hpp - The implementation header for the header/implementation separated usage of +// ghc::filesystem that does nothing if std::filesystem is detected. +// This file can be used to hide the implementation of ghc::filesystem into a single cpp. +// The cpp has to include this before including fs_std_fwd.hpp directly or via a different +// header to work. +//--------------------------------------------------------------------------------------- +#if defined(_MSVC_LANG) && _MSVC_LANG >= 201703L || __cplusplus >= 201703L && defined(__has_include) + // ^ Supports MSVC prior to 15.7 without setting /Zc:__cplusplus to fix __cplusplus + // _MSVC_LANG works regardless. But without the switch, the compiler always reported 199711L: https://blogs.msdn.microsoft.com/vcblog/2018/04/09/msvc-now-correctly-reports-__cplusplus/ + #if __has_include() // Two stage __has_include needed for MSVC 2015 and per https://gcc.gnu.org/onlinedocs/cpp/_005f_005fhas_005finclude.html + #define GHC_USE_STD_FS + + // Old Apple OSs don't support std::filesystem, though the header is available at compile + // time. In particular, std::filesystem is unavailable before macOS 10.15, iOS/tvOS 13.0, + // and watchOS 6.0. + #ifdef __APPLE__ + #include + // Note: This intentionally uses std::filesystem on any new Apple OS, like visionOS + // released after std::filesystem, where std::filesystem is always available. + // (All other ___VERSION_MIN_REQUIREDs will be undefined and thus 0.) + #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 \ + || defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000 \ + || defined(__TV_OS_VERSION_MIN_REQUIRED) && __TV_OS_VERSION_MIN_REQUIRED < 130000 \ + || defined(__WATCH_OS_VERSION_MAX_ALLOWED) && __WATCH_OS_VERSION_MAX_ALLOWED < 60000 + #undef GHC_USE_STD_FS + #endif + #endif + #endif +#endif + +#ifndef GHC_USE_STD_FS + #define GHC_FILESYSTEM_IMPLEMENTATION + #include "ghc_filesystem.h" +#endif diff --git a/3rdparty/openal/common/filesystem.h b/3rdparty/openal/common/filesystem.h new file mode 100644 index 000000000000..8d556e87ffb5 --- /dev/null +++ b/3rdparty/openal/common/filesystem.h @@ -0,0 +1,81 @@ +//--------------------------------------------------------------------------------------- +// +// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14 +// +//--------------------------------------------------------------------------------------- +// +// Copyright (c) 2018, Steffen Schümann +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +//--------------------------------------------------------------------------------------- +// fs_std_fwd.hpp - The forwarding header for the header/implementation separated usage of +// ghc::filesystem that uses std::filesystem if it detects it. +// This file can be include at any place, where fs::filesystem api is needed while +// not bleeding implementation details (e.g. system includes) into the global namespace, +// as long as one cpp includes fs_std_impl.hpp to deliver the matching implementations. +//--------------------------------------------------------------------------------------- +#ifndef GHC_FILESYSTEM_STD_FWD_H +#define GHC_FILESYSTEM_STD_FWD_H + +#if defined(_MSVC_LANG) && _MSVC_LANG >= 201703L || __cplusplus >= 201703L && defined(__has_include) + // ^ Supports MSVC prior to 15.7 without setting /Zc:__cplusplus to fix __cplusplus + // _MSVC_LANG works regardless. But without the switch, the compiler always reported 199711L: https://blogs.msdn.microsoft.com/vcblog/2018/04/09/msvc-now-correctly-reports-__cplusplus/ + #if __has_include() // Two stage __has_include needed for MSVC 2015 and per https://gcc.gnu.org/onlinedocs/cpp/_005f_005fhas_005finclude.html + #define GHC_USE_STD_FS + + // Old Apple OSs don't support std::filesystem, though the header is available at compile + // time. In particular, std::filesystem is unavailable before macOS 10.15, iOS/tvOS 13.0, + // and watchOS 6.0. + #ifdef __APPLE__ + #include + // Note: This intentionally uses std::filesystem on any new Apple OS, like visionOS + // released after std::filesystem, where std::filesystem is always available. + // (All other ___VERSION_MIN_REQUIREDs will be undefined and thus 0.) + #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 \ + || defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000 \ + || defined(__TV_OS_VERSION_MIN_REQUIRED) && __TV_OS_VERSION_MIN_REQUIRED < 130000 \ + || defined(__WATCH_OS_VERSION_MAX_ALLOWED) && __WATCH_OS_VERSION_MAX_ALLOWED < 60000 + #undef GHC_USE_STD_FS + #endif + #endif + #endif +#endif + +#ifdef GHC_USE_STD_FS + #include + namespace fs { + using namespace std::filesystem; + using ifstream = std::ifstream; + using ofstream = std::ofstream; + using fstream = std::fstream; + } +#else + #define GHC_FILESYSTEM_FWD + #include "ghc_filesystem.h" + + namespace fs { + using namespace ghc::filesystem; + using ifstream = ghc::filesystem::ifstream; + using ofstream = ghc::filesystem::ofstream; + using fstream = ghc::filesystem::fstream; + } +#endif + +#endif // GHC_FILESYSTEM_STD_FWD_H diff --git a/3rdparty/openal/common/flexarray.h b/3rdparty/openal/common/flexarray.h new file mode 100644 index 000000000000..b6f09c20dbd5 --- /dev/null +++ b/3rdparty/openal/common/flexarray.h @@ -0,0 +1,140 @@ +#ifndef AL_FLEXARRAY_H +#define AL_FLEXARRAY_H + +#include +#include +#include +#include +#include +#include + +#include "almalloc.h" +#include "alspan.h" + +namespace al { + +/* Storage for flexible array data. This is trivially destructible if type T is + * trivially destructible. + */ +template::value> +struct alignas(alignment) FlexArrayStorage : al::span { + /* NOLINTBEGIN(bugprone-sizeof-expression) clang-tidy warns about the + * sizeof(T) being suspicious when T is a pointer type, which it will be + * for flexible arrays of pointers. + */ + static constexpr size_t Sizeof(size_t count, size_t base=0u) noexcept + { return sizeof(FlexArrayStorage) + sizeof(T)*count + base; } + /* NOLINTEND(bugprone-sizeof-expression) */ + + /* NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) Flexible + * arrays store their payloads after the end of the object, which must be + * the last in the whole parent chain. + */ + explicit FlexArrayStorage(size_t size) noexcept(std::is_nothrow_constructible_v) + : al::span{::new(static_cast(this+1)) T[size], size} + { } + /* NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) */ + ~FlexArrayStorage() = default; + + FlexArrayStorage(const FlexArrayStorage&) = delete; + FlexArrayStorage& operator=(const FlexArrayStorage&) = delete; +}; + +template +struct alignas(alignment) FlexArrayStorage : al::span { + static constexpr size_t Sizeof(size_t count, size_t base=0u) noexcept + { return sizeof(FlexArrayStorage) + sizeof(T)*count + base; } + + /* NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) */ + explicit FlexArrayStorage(size_t size) noexcept(std::is_nothrow_constructible_v) + : al::span{::new(static_cast(this+1)) T[size], size} + { } + /* NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) */ + ~FlexArrayStorage() { std::destroy(this->begin(), this->end()); } + + FlexArrayStorage(const FlexArrayStorage&) = delete; + FlexArrayStorage& operator=(const FlexArrayStorage&) = delete; +}; + +/* A flexible array type. Used either standalone or at the end of a parent + * struct, to have a run-time-sized array that's embedded with its size. Should + * be used delicately, ensuring there's no additional data after the FlexArray + * member. + */ +template +struct FlexArray { + using element_type = T; + using value_type = std::remove_cv_t; + using index_type = size_t; + using difference_type = ptrdiff_t; + + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; + + static constexpr std::size_t StorageAlign{std::max(alignof(T), Align)}; + using Storage_t_ = FlexArrayStorage), StorageAlign)>; + + using iterator = typename Storage_t_::iterator; + using const_iterator = typename Storage_t_::const_iterator; + using reverse_iterator = typename Storage_t_::reverse_iterator; + using const_reverse_iterator = typename Storage_t_::const_reverse_iterator; + + const Storage_t_ mStore; + + static constexpr index_type Sizeof(index_type count, index_type base=0u) noexcept + { return Storage_t_::Sizeof(count, base); } + static std::unique_ptr Create(index_type count) + { return std::unique_ptr{new(FamCount{count}) FlexArray{count}}; } + + explicit FlexArray(index_type size) + noexcept(std::is_nothrow_constructible_v) + : mStore{size} + { } + ~FlexArray() = default; + + [[nodiscard]] auto size() const noexcept -> index_type { return mStore.size(); } + [[nodiscard]] auto empty() const noexcept -> bool { return mStore.empty(); } + + [[nodiscard]] auto data() noexcept -> pointer { return mStore.data(); } + [[nodiscard]] auto data() const noexcept -> const_pointer { return mStore.data(); } + + [[nodiscard]] auto operator[](index_type i) noexcept -> reference { return mStore[i]; } + [[nodiscard]] auto operator[](index_type i) const noexcept -> const_reference { return mStore[i]; } + + [[nodiscard]] auto front() noexcept -> reference { return mStore.front(); } + [[nodiscard]] auto front() const noexcept -> const_reference { return mStore.front(); } + + [[nodiscard]] auto back() noexcept -> reference { return mStore.back(); } + [[nodiscard]] auto back() const noexcept -> const_reference { return mStore.back(); } + + [[nodiscard]] auto begin() noexcept -> iterator { return mStore.begin(); } + [[nodiscard]] auto begin() const noexcept -> const_iterator { return mStore.cbegin(); } + [[nodiscard]] auto cbegin() const noexcept -> const_iterator { return mStore.cbegin(); } + [[nodiscard]] auto end() noexcept -> iterator { return mStore.end(); } + [[nodiscard]] auto end() const noexcept -> const_iterator { return mStore.cend(); } + [[nodiscard]] auto cend() const noexcept -> const_iterator { return mStore.cend(); } + + [[nodiscard]] auto rbegin() noexcept -> reverse_iterator { return mStore.rbegin(); } + [[nodiscard]] auto rbegin() const noexcept -> const_reverse_iterator { return mStore.crbegin(); } + [[nodiscard]] auto crbegin() const noexcept -> const_reverse_iterator { return mStore.crbegin(); } + [[nodiscard]] auto rend() noexcept -> reverse_iterator { return mStore.rend(); } + [[nodiscard]] auto rend() const noexcept -> const_reverse_iterator { return mStore.crend(); } + [[nodiscard]] auto crend() const noexcept -> const_reverse_iterator { return mStore.crend(); } + + gsl::owner operator new(size_t, FamCount count) + { return ::operator new[](Sizeof(count), std::align_val_t{alignof(FlexArray)}); } + void operator delete(gsl::owner block, FamCount) noexcept + { ::operator delete[](block, std::align_val_t{alignof(FlexArray)}); } + void operator delete(gsl::owner block) noexcept + { ::operator delete[](block, std::align_val_t{alignof(FlexArray)}); } + + void *operator new(size_t size) = delete; + void *operator new[](size_t size) = delete; + void operator delete[](void *block) = delete; +}; + +} // namespace al + +#endif /* AL_FLEXARRAY_H */ diff --git a/3rdparty/openal/common/intrusive_ptr.h b/3rdparty/openal/common/intrusive_ptr.h index 27075347998e..1a446f44b87f 100644 --- a/3rdparty/openal/common/intrusive_ptr.h +++ b/3rdparty/openal/common/intrusive_ptr.h @@ -1,6 +1,8 @@ #ifndef INTRUSIVE_PTR_H #define INTRUSIVE_PTR_H +#include +#include #include #include "atomic.h" @@ -11,7 +13,10 @@ namespace al { template class intrusive_ref { - RefCount mRef{1u}; + std::atomic mRef{1u}; + +protected: + ~intrusive_ref() = default; public: unsigned int add_ref() noexcept { return IncrementRef(mRef); } @@ -46,7 +51,7 @@ class intrusive_ref { }; -template +template /* NOLINTNEXTLINE(clazy-rule-of-three) False positive */ class intrusive_ptr { T *mPtr{nullptr}; @@ -56,10 +61,13 @@ class intrusive_ptr { { if(mPtr) mPtr->add_ref(); } intrusive_ptr(intrusive_ptr&& rhs) noexcept : mPtr{rhs.mPtr} { rhs.mPtr = nullptr; } - intrusive_ptr(std::nullptr_t) noexcept { } + intrusive_ptr(std::nullptr_t) noexcept { } /* NOLINT(google-explicit-constructor) */ explicit intrusive_ptr(T *ptr) noexcept : mPtr{ptr} { } ~intrusive_ptr() { if(mPtr) mPtr->dec_ref(); } + /* NOLINTBEGIN(bugprone-unhandled-self-assignment) + * Self-assignment is handled properly here. + */ intrusive_ptr& operator=(const intrusive_ptr &rhs) noexcept { static_assert(noexcept(std::declval()->dec_ref()), "dec_ref must be noexcept"); @@ -69,6 +77,7 @@ class intrusive_ptr { mPtr = rhs.mPtr; return *this; } + /* NOLINTEND(bugprone-unhandled-self-assignment) */ intrusive_ptr& operator=(intrusive_ptr&& rhs) noexcept { if(&rhs != this) LIKELY @@ -81,9 +90,9 @@ class intrusive_ptr { explicit operator bool() const noexcept { return mPtr != nullptr; } - T& operator*() const noexcept { return *mPtr; } - T* operator->() const noexcept { return mPtr; } - T* get() const noexcept { return mPtr; } + [[nodiscard]] auto operator*() const noexcept -> T& { return *mPtr; } + [[nodiscard]] auto operator->() const noexcept -> T* { return mPtr; } + [[nodiscard]] auto get() const noexcept -> T* { return mPtr; } void reset(T *ptr=nullptr) noexcept { @@ -98,23 +107,6 @@ class intrusive_ptr { void swap(intrusive_ptr&& rhs) noexcept { std::swap(mPtr, rhs.mPtr); } }; -#define AL_DECL_OP(op) \ -template \ -inline bool operator op(const intrusive_ptr &lhs, const T *rhs) noexcept \ -{ return lhs.get() op rhs; } \ -template \ -inline bool operator op(const T *lhs, const intrusive_ptr &rhs) noexcept \ -{ return lhs op rhs.get(); } - -AL_DECL_OP(==) -AL_DECL_OP(!=) -AL_DECL_OP(<=) -AL_DECL_OP(>=) -AL_DECL_OP(<) -AL_DECL_OP(>) - -#undef AL_DECL_OP - } // namespace al #endif /* INTRUSIVE_PTR_H */ diff --git a/3rdparty/openal/common/opthelpers.h b/3rdparty/openal/common/opthelpers.h index dc43ccdb5301..e22f220fedbb 100644 --- a/3rdparty/openal/common/opthelpers.h +++ b/3rdparty/openal/common/opthelpers.h @@ -28,6 +28,24 @@ #define NOINLINE #endif +#if defined(__MINGW32__) && defined(__i386__) +/* 32-bit MinGW targets have a bug where __STDCPP_DEFAULT_NEW_ALIGNMENT__ + * reports 16, despite the default operator new calling standard malloc which + * only guarantees 8-byte alignment. As a result, structs that need and specify + * 16-byte alignment only get 8-byte alignment. Explicitly specifying 32-byte + * alignment forces the over-aligned operator new to be called, giving the + * correct (if larger than necessary) alignment. + * + * Technically this bug affects 32-bit GCC more generally, but typically only + * with fairly old glibc versions as newer versions do guarantee the 16-byte + * alignment as specified. MinGW is reliant on msvcrt.dll's malloc however, + * which can't be updated to give that guarantee. + */ +#define SIMDALIGN alignas(32) +#else +#define SIMDALIGN +#endif + /* Unlike the likely attribute, ASSUME requires the condition to be true or * else it invokes undefined behavior. It's essentially an assert without * actually checking the condition at run-time, allowing for stronger @@ -42,7 +60,7 @@ #elif HAS_BUILTIN(__builtin_unreachable) #define ASSUME(x) do { if(x) break; __builtin_unreachable(); } while(0) #else -#define ASSUME(x) ((void)0) +#define ASSUME(x) (static_cast(0)) #endif /* This shouldn't be needed since unknown attributes are ignored, but older @@ -56,6 +74,12 @@ #define UNLIKELY #endif +#if !defined(_WIN32) && HAS_ATTRIBUTE(gnu::visibility) +#define DECL_HIDDEN [[gnu::visibility("hidden")]] +#else +#define DECL_HIDDEN +#endif + namespace al { template diff --git a/3rdparty/openal/common/pffft.cpp b/3rdparty/openal/common/pffft.cpp new file mode 100644 index 000000000000..0d71d3412156 --- /dev/null +++ b/3rdparty/openal/common/pffft.cpp @@ -0,0 +1,2308 @@ +//$ nobt + +/* Copyright (c) 2013 Julien Pommier ( pommier@modartt.com ) + * Copyright (c) 2023 Christopher Robinson + * + * Based on original fortran 77 code from FFTPACKv4 from NETLIB + * (http://www.netlib.org/fftpack), authored by Dr Paul Swarztrauber + * of NCAR, in 1985. + * + * As confirmed by the NCAR fftpack software curators, the following + * FFTPACKv5 license applies to FFTPACKv4 sources. My changes are + * released under the same terms. + * + * FFTPACK license: + * + * http://www.cisl.ucar.edu/css/software/fftpack5/ftpk.html + * + * Copyright (c) 2004 the University Corporation for Atmospheric + * Research ("UCAR"). All rights reserved. Developed by NCAR's + * Computational and Information Systems Laboratory, UCAR, + * www.cisl.ucar.edu. + * + * Redistribution and use of the Software in source and binary forms, + * with or without modification, is permitted provided that the + * following conditions are met: + * + * - Neither the names of NCAR's Computational and Information Systems + * Laboratory, the University Corporation for Atmospheric Research, + * nor the names of its sponsors or contributors may be used to + * endorse or promote products derived from this Software without + * specific prior written permission. + * + * - Redistributions of source code must retain the above copyright + * notices, this list of conditions, and the disclaimer below. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions, and the disclaimer below in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE + * SOFTWARE. + * + * + * PFFFT : a Pretty Fast FFT. + * + * This file is largerly based on the original FFTPACK implementation, modified + * in order to take advantage of SIMD instructions of modern CPUs. + */ + +#include "pffft.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "albit.h" +#include "almalloc.h" +#include "alnumbers.h" +#include "alnumeric.h" +#include "alspan.h" +#include "fmt/core.h" +#include "fmt/ranges.h" +#include "opthelpers.h" + + +using uint = unsigned int; + +namespace { + +#if defined(__GNUC__) || defined(_MSC_VER) +#define RESTRICT __restrict +#else +#define RESTRICT +#endif + +/* Vector support macros: the rest of the code is independent of + * SSE/Altivec/NEON -- adding support for other platforms with 4-element + * vectors should be limited to these macros + */ + +/* Define PFFFT_SIMD_DISABLE if you want to use scalar code instead of SIMD code */ +//#define PFFFT_SIMD_DISABLE + +#ifndef PFFFT_SIMD_DISABLE +/* + * Altivec support macros + */ +#if (defined(__ppc__) || defined(__ppc64__) || defined(__powerpc__) || defined(__powerpc64__)) \ + && (defined(__VEC__) || defined(__ALTIVEC__)) +#include +using v4sf = vector float; +constexpr uint SimdSize{4}; +force_inline v4sf vzero() noexcept { return (vector float)vec_splat_u8(0); } +force_inline v4sf vmul(v4sf a, v4sf b) noexcept { return vec_madd(a, b, vzero()); } +force_inline v4sf vadd(v4sf a, v4sf b) noexcept { return vec_add(a, b); } +force_inline v4sf vmadd(v4sf a, v4sf b, v4sf c) noexcept { return vec_madd(a, b, c); } +force_inline v4sf vsub(v4sf a, v4sf b) noexcept { return vec_sub(a, b); } +force_inline v4sf ld_ps1(float a) noexcept { return vec_splats(a); } + +force_inline v4sf vset4(float a, float b, float c, float d) noexcept +{ + /* There a more efficient way to do this? */ + alignas(16) std::array vals{{a, b, c, d}}; + return vec_ld(0, vals.data()); +} +force_inline v4sf vinsert0(v4sf v, float a) noexcept { return vec_insert(a, v, 0); } +force_inline float vextract0(v4sf v) noexcept { return vec_extract(v, 0); } + +force_inline void interleave2(v4sf in1, v4sf in2, v4sf &out1, v4sf &out2) noexcept +{ + out1 = vec_mergeh(in1, in2); + out2 = vec_mergel(in1, in2); +} +force_inline void uninterleave2(v4sf in1, v4sf in2, v4sf &out1, v4sf &out2) noexcept +{ + out1 = vec_perm(in1, in2, (vector unsigned char){0,1,2,3,8,9,10,11,16,17,18,19,24,25,26,27}); + out2 = vec_perm(in1, in2, (vector unsigned char){4,5,6,7,12,13,14,15,20,21,22,23,28,29,30,31}); +} + +force_inline void vtranspose4(v4sf &x0, v4sf &x1, v4sf &x2, v4sf &x3) noexcept +{ + v4sf y0{vec_mergeh(x0, x2)}; + v4sf y1{vec_mergel(x0, x2)}; + v4sf y2{vec_mergeh(x1, x3)}; + v4sf y3{vec_mergel(x1, x3)}; + x0 = vec_mergeh(y0, y2); + x1 = vec_mergel(y0, y2); + x2 = vec_mergeh(y1, y3); + x3 = vec_mergel(y1, y3); +} + +force_inline v4sf vswaphl(v4sf a, v4sf b) noexcept +{ return vec_perm(a,b, (vector unsigned char){16,17,18,19,20,21,22,23,8,9,10,11,12,13,14,15}); } + +/* + * SSE1 support macros + */ +#elif defined(__x86_64__) || defined(__SSE__) || defined(_M_X64) || \ + (defined(_M_IX86_FP) && _M_IX86_FP >= 1) + +#include +using v4sf = __m128; +/* 4 floats by simd vector -- this is pretty much hardcoded in the preprocess/ + * finalize functions anyway so you will have to work if you want to enable AVX + * with its 256-bit vectors. + */ +constexpr uint SimdSize{4}; +force_inline v4sf vzero() noexcept { return _mm_setzero_ps(); } +force_inline v4sf vmul(v4sf a, v4sf b) noexcept { return _mm_mul_ps(a, b); } +force_inline v4sf vadd(v4sf a, v4sf b) noexcept { return _mm_add_ps(a, b); } +force_inline v4sf vmadd(v4sf a, v4sf b, v4sf c) noexcept { return _mm_add_ps(_mm_mul_ps(a,b), c); } +force_inline v4sf vsub(v4sf a, v4sf b) noexcept { return _mm_sub_ps(a, b); } +force_inline v4sf ld_ps1(float a) noexcept { return _mm_set1_ps(a); } + +force_inline v4sf vset4(float a, float b, float c, float d) noexcept +{ return _mm_setr_ps(a, b, c, d); } +force_inline v4sf vinsert0(const v4sf v, const float a) noexcept +{ return _mm_move_ss(v, _mm_set_ss(a)); } +force_inline float vextract0(v4sf v) noexcept +{ return _mm_cvtss_f32(v); } + +force_inline void interleave2(const v4sf in1, const v4sf in2, v4sf &out1, v4sf &out2) noexcept +{ + out1 = _mm_unpacklo_ps(in1, in2); + out2 = _mm_unpackhi_ps(in1, in2); +} +force_inline void uninterleave2(v4sf in1, v4sf in2, v4sf &out1, v4sf &out2) noexcept +{ + out1 = _mm_shuffle_ps(in1, in2, _MM_SHUFFLE(2,0,2,0)); + out2 = _mm_shuffle_ps(in1, in2, _MM_SHUFFLE(3,1,3,1)); +} + +force_inline void vtranspose4(v4sf &x0, v4sf &x1, v4sf &x2, v4sf &x3) noexcept +{ _MM_TRANSPOSE4_PS(x0, x1, x2, x3); } + +force_inline v4sf vswaphl(v4sf a, v4sf b) noexcept +{ return _mm_shuffle_ps(b, a, _MM_SHUFFLE(3,2,1,0)); } + +/* + * ARM NEON support macros + */ +#elif defined(__ARM_NEON) || defined(__aarch64__) || defined(__arm64) || defined(_M_ARM64) + +#include +using v4sf = float32x4_t; +constexpr uint SimdSize{4}; +force_inline v4sf vzero() noexcept { return vdupq_n_f32(0.0f); } +force_inline v4sf vmul(v4sf a, v4sf b) noexcept { return vmulq_f32(a, b); } +force_inline v4sf vadd(v4sf a, v4sf b) noexcept { return vaddq_f32(a, b); } +force_inline v4sf vmadd(v4sf a, v4sf b, v4sf c) noexcept { return vmlaq_f32(c, a, b); } +force_inline v4sf vsub(v4sf a, v4sf b) noexcept { return vsubq_f32(a, b); } +force_inline v4sf ld_ps1(float a) noexcept { return vdupq_n_f32(a); } + +force_inline v4sf vset4(float a, float b, float c, float d) noexcept +{ + float32x4_t ret{vmovq_n_f32(a)}; + ret = vsetq_lane_f32(b, ret, 1); + ret = vsetq_lane_f32(c, ret, 2); + ret = vsetq_lane_f32(d, ret, 3); + return ret; +} +force_inline v4sf vinsert0(v4sf v, float a) noexcept +{ return vsetq_lane_f32(a, v, 0); } +force_inline float vextract0(v4sf v) noexcept +{ return vgetq_lane_f32(v, 0); } + +force_inline void interleave2(v4sf in1, v4sf in2, v4sf &out1, v4sf &out2) noexcept +{ + float32x4x2_t tmp{vzipq_f32(in1, in2)}; + out1 = tmp.val[0]; + out2 = tmp.val[1]; +} +force_inline void uninterleave2(v4sf in1, v4sf in2, v4sf &out1, v4sf &out2) noexcept +{ + float32x4x2_t tmp{vuzpq_f32(in1, in2)}; + out1 = tmp.val[0]; + out2 = tmp.val[1]; +} + +force_inline void vtranspose4(v4sf &x0, v4sf &x1, v4sf &x2, v4sf &x3) noexcept +{ + /* marginally faster version: + * asm("vtrn.32 %q0, %q1;\n" + * "vtrn.32 %q2, %q3\n + * "vswp %f0, %e2\n + * "vswp %f1, %e3" + * : "+w"(x0), "+w"(x1), "+w"(x2), "+w"(x3)::); + */ + float32x4x2_t t0_{vzipq_f32(x0, x2)}; + float32x4x2_t t1_{vzipq_f32(x1, x3)}; + float32x4x2_t u0_{vzipq_f32(t0_.val[0], t1_.val[0])}; + float32x4x2_t u1_{vzipq_f32(t0_.val[1], t1_.val[1])}; + x0 = u0_.val[0]; + x1 = u0_.val[1]; + x2 = u1_.val[0]; + x3 = u1_.val[1]; +} + +force_inline v4sf vswaphl(v4sf a, v4sf b) noexcept +{ return vcombine_f32(vget_low_f32(b), vget_high_f32(a)); } + +/* + * Generic GCC vector macros + */ +#elif defined(__GNUC__) + +using v4sf [[gnu::vector_size(16), gnu::aligned(16)]] = float; +constexpr uint SimdSize{4}; +force_inline constexpr v4sf vzero() noexcept { return v4sf{0.0f, 0.0f, 0.0f, 0.0f}; } +force_inline constexpr v4sf vmul(v4sf a, v4sf b) noexcept { return a * b; } +force_inline constexpr v4sf vadd(v4sf a, v4sf b) noexcept { return a + b; } +force_inline constexpr v4sf vmadd(v4sf a, v4sf b, v4sf c) noexcept { return a*b + c; } +force_inline constexpr v4sf vsub(v4sf a, v4sf b) noexcept { return a - b; } +force_inline constexpr v4sf ld_ps1(float a) noexcept { return v4sf{a, a, a, a}; } + +force_inline constexpr v4sf vset4(float a, float b, float c, float d) noexcept +{ return v4sf{a, b, c, d}; } +force_inline constexpr v4sf vinsert0(v4sf v, float a) noexcept +{ return v4sf{a, v[1], v[2], v[3]}; } +force_inline float vextract0(v4sf v) noexcept +{ return v[0]; } + +force_inline v4sf unpacklo(v4sf a, v4sf b) noexcept +{ return v4sf{a[0], b[0], a[1], b[1]}; } +force_inline v4sf unpackhi(v4sf a, v4sf b) noexcept +{ return v4sf{a[2], b[2], a[3], b[3]}; } + +force_inline void interleave2(v4sf in1, v4sf in2, v4sf &out1, v4sf &out2) noexcept +{ + out1 = unpacklo(in1, in2); + out2 = unpackhi(in1, in2); +} +force_inline void uninterleave2(v4sf in1, v4sf in2, v4sf &out1, v4sf &out2) noexcept +{ + out1 = v4sf{in1[0], in1[2], in2[0], in2[2]}; + out2 = v4sf{in1[1], in1[3], in2[1], in2[3]}; +} + +force_inline void vtranspose4(v4sf &x0, v4sf &x1, v4sf &x2, v4sf &x3) noexcept +{ + v4sf tmp0{unpacklo(x0, x1)}; + v4sf tmp2{unpacklo(x2, x3)}; + v4sf tmp1{unpackhi(x0, x1)}; + v4sf tmp3{unpackhi(x2, x3)}; + x0 = v4sf{tmp0[0], tmp0[1], tmp2[0], tmp2[1]}; + x1 = v4sf{tmp0[2], tmp0[3], tmp2[2], tmp2[3]}; + x2 = v4sf{tmp1[0], tmp1[1], tmp3[0], tmp3[1]}; + x3 = v4sf{tmp1[2], tmp1[3], tmp3[2], tmp3[3]}; +} + +force_inline v4sf vswaphl(v4sf a, v4sf b) noexcept +{ return v4sf{b[0], b[1], a[2], a[3]}; } + +#else + +#warning "building with simd disabled !\n"; +#define PFFFT_SIMD_DISABLE // fallback to scalar code +#endif + +#endif /* PFFFT_SIMD_DISABLE */ + +// fallback mode for situations where SIMD is not available, use scalar mode instead +#ifdef PFFFT_SIMD_DISABLE +using v4sf = float; +constexpr uint SimdSize{1}; +force_inline constexpr v4sf vmul(v4sf a, v4sf b) noexcept { return a * b; } +force_inline constexpr v4sf vadd(v4sf a, v4sf b) noexcept { return a + b; } +force_inline constexpr v4sf vmadd(v4sf a, v4sf b, v4sf c) noexcept { return a*b + c; } +force_inline constexpr v4sf vsub(v4sf a, v4sf b) noexcept { return a - b; } +force_inline constexpr v4sf ld_ps1(float a) noexcept { return a; } + +#else + +[[maybe_unused]] inline +auto valigned(const float *ptr) noexcept -> bool +{ + static constexpr uintptr_t alignmask{SimdSize*sizeof(float) - 1}; + return (reinterpret_cast(ptr) & alignmask) == 0; +} +#endif + +// shortcuts for complex multiplications +force_inline void vcplxmul(v4sf &ar, v4sf &ai, v4sf br, v4sf bi) noexcept +{ + v4sf tmp{vmul(ar, bi)}; + ar = vsub(vmul(ar, br), vmul(ai, bi)); + ai = vmadd(ai, br, tmp); +} +force_inline void vcplxmulconj(v4sf &ar, v4sf &ai, v4sf br, v4sf bi) noexcept +{ + v4sf tmp{vmul(ar, bi)}; + ar = vmadd(ai, bi, vmul(ar, br)); + ai = vsub(vmul(ai, br), tmp); +} + +#if !defined(PFFFT_SIMD_DISABLE) + +inline void assertv4(const al::span v_f [[maybe_unused]], + const float f0 [[maybe_unused]], const float f1 [[maybe_unused]], + const float f2 [[maybe_unused]], const float f3 [[maybe_unused]]) +{ assert(v_f[0] == f0 && v_f[1] == f1 && v_f[2] == f2 && v_f[3] == f3); } + +template +constexpr auto make_float_array(std::integer_sequence) +{ return std::array{static_cast(N)...}; } + +/* detect bugs with the vector support macros */ +[[maybe_unused]] auto validate_pffft_simd() -> bool +{ + using float4 = std::array; + static constexpr auto f = make_float_array(std::make_index_sequence<16>{}); + + auto a0_v = vset4(f[ 0], f[ 1], f[ 2], f[ 3]); + auto a1_v = vset4(f[ 4], f[ 5], f[ 6], f[ 7]); + auto a2_v = vset4(f[ 8], f[ 9], f[10], f[11]); + auto a3_v = vset4(f[12], f[13], f[14], f[15]); + + auto t_v = vzero(); + auto t_f = al::bit_cast(t_v); + fmt::println("VZERO={}", t_f); + assertv4(t_f, 0, 0, 0, 0); + + t_v = vadd(a1_v, a2_v); + t_f = al::bit_cast(t_v); + fmt::println("VADD(4:7,8:11)={}", t_f); + assertv4(t_f, 12, 14, 16, 18); + + t_v = vmul(a1_v, a2_v); + t_f = al::bit_cast(t_v); + fmt::println("VMUL(4:7,8:11)={}", t_f); + assertv4(t_f, 32, 45, 60, 77); + + t_v = vmadd(a1_v, a2_v, a0_v); + t_f = al::bit_cast(t_v); + fmt::println("VMADD(4:7,8:11,0:3)={}", t_f); + assertv4(t_f, 32, 46, 62, 80); + + auto u_v = v4sf{}; + interleave2(a1_v, a2_v, t_v, u_v); + t_f = al::bit_cast(t_v); + auto u_f = al::bit_cast(u_v); + fmt::println("INTERLEAVE2(4:7,8:11)={} {}", t_f, u_f); + assertv4(t_f, 4, 8, 5, 9); + assertv4(u_f, 6, 10, 7, 11); + + uninterleave2(a1_v, a2_v, t_v, u_v); + t_f = al::bit_cast(t_v); + u_f = al::bit_cast(u_v); + fmt::println("UNINTERLEAVE2(4:7,8:11)={} {}", t_f, u_f); + assertv4(t_f, 4, 6, 8, 10); + assertv4(u_f, 5, 7, 9, 11); + + t_v = ld_ps1(f[15]); + t_f = al::bit_cast(t_v); + fmt::println("LD_PS1(15)={}", t_f); + assertv4(t_f, 15, 15, 15, 15); + + t_v = vswaphl(a1_v, a2_v); + t_f = al::bit_cast(t_v); + fmt::println("VSWAPHL(4:7,8:11)={}", t_f); + assertv4(t_f, 8, 9, 6, 7); + + vtranspose4(a0_v, a1_v, a2_v, a3_v); + auto a0_f = al::bit_cast(a0_v); + auto a1_f = al::bit_cast(a1_v); + auto a2_f = al::bit_cast(a2_v); + auto a3_f = al::bit_cast(a3_v); + fmt::println("VTRANSPOSE4(0:3,4:7,8:11,12:15)={} {} {} {}", a0_f, a1_f, a2_f, a3_f); + assertv4(a0_f, 0, 4, 8, 12); + assertv4(a1_f, 1, 5, 9, 13); + assertv4(a2_f, 2, 6, 10, 14); + assertv4(a3_f, 3, 7, 11, 15); + + return true; +} +#endif //!PFFFT_SIMD_DISABLE + +/* SSE and co like 16-bytes aligned pointers */ +/* with a 64-byte alignment, we are even aligned on L2 cache lines... */ +constexpr auto V4sfAlignment = size_t(64); +constexpr auto V4sfAlignVal = std::align_val_t(V4sfAlignment); + +/* NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) + * FIXME: Converting this from raw pointers to spans or something will probably + * need significant work to maintain performance, given non-sequential range- + * checked accesses and lack of 'restrict' to indicate non-aliased memory. At + * least, some tests should be done to check the impact of using range-checked + * spans here before blindly switching. + */ +/* + passf2 and passb2 has been merged here, fsign = -1 for passf2, +1 for passb2 +*/ +NOINLINE void passf2_ps(const size_t ido, const size_t l1, const v4sf *cc, v4sf *RESTRICT ch, + const float *const wa1, const float fsign) +{ + const size_t l1ido{l1*ido}; + if(ido <= 2) + { + for(size_t k{0};k < l1ido;k += ido, ch += ido, cc += 2*ido) + { + ch[0] = vadd(cc[0], cc[ido+0]); + ch[l1ido] = vsub(cc[0], cc[ido+0]); + ch[1] = vadd(cc[1], cc[ido+1]); + ch[l1ido + 1] = vsub(cc[1], cc[ido+1]); + } + } + else + { + for(size_t k{0};k < l1ido;k += ido, ch += ido, cc += 2*ido) + { + for(size_t i{0};i < ido-1;i += 2) + { + v4sf tr2{vsub(cc[i+0], cc[i+ido+0])}; + v4sf ti2{vsub(cc[i+1], cc[i+ido+1])}; + v4sf wr{ld_ps1(wa1[i])}, wi{ld_ps1(wa1[i+1]*fsign)}; + ch[i] = vadd(cc[i+0], cc[i+ido+0]); + ch[i+1] = vadd(cc[i+1], cc[i+ido+1]); + vcplxmul(tr2, ti2, wr, wi); + ch[i+l1ido] = tr2; + ch[i+l1ido+1] = ti2; + } + } + } +} + +/* + passf3 and passb3 has been merged here, fsign = -1 for passf3, +1 for passb3 +*/ +NOINLINE void passf3_ps(const size_t ido, const size_t l1, const v4sf *cc, v4sf *RESTRICT ch, + const float *const wa1, const float fsign) +{ + assert(ido > 2); + + const v4sf taur{ld_ps1(-0.5f)}; + const v4sf taui{ld_ps1(0.866025403784439f*fsign)}; + const size_t l1ido{l1*ido}; + const auto wa2 = wa1 + ido; + for(size_t k{0};k < l1ido;k += ido, cc += 3*ido, ch +=ido) + { + for(size_t i{0};i < ido-1;i += 2) + { + v4sf tr2{vadd(cc[i+ido], cc[i+2*ido])}; + v4sf cr2{vmadd(taur, tr2, cc[i])}; + ch[i] = vadd(tr2, cc[i]); + v4sf ti2{vadd(cc[i+ido+1], cc[i+2*ido+1])}; + v4sf ci2{vmadd(taur, ti2, cc[i+1])}; + ch[i+1] = vadd(cc[i+1], ti2); + v4sf cr3{vmul(taui, vsub(cc[i+ido], cc[i+2*ido]))}; + v4sf ci3{vmul(taui, vsub(cc[i+ido+1], cc[i+2*ido+1]))}; + v4sf dr2{vsub(cr2, ci3)}; + v4sf dr3{vadd(cr2, ci3)}; + v4sf di2{vadd(ci2, cr3)}; + v4sf di3{vsub(ci2, cr3)}; + float wr1{wa1[i]}, wi1{fsign*wa1[i+1]}, wr2{wa2[i]}, wi2{fsign*wa2[i+1]}; + vcplxmul(dr2, di2, ld_ps1(wr1), ld_ps1(wi1)); + ch[i+l1ido] = dr2; + ch[i+l1ido + 1] = di2; + vcplxmul(dr3, di3, ld_ps1(wr2), ld_ps1(wi2)); + ch[i+2*l1ido] = dr3; + ch[i+2*l1ido+1] = di3; + } + } +} /* passf3 */ + +NOINLINE void passf4_ps(const size_t ido, const size_t l1, const v4sf *cc, v4sf *RESTRICT ch, + const float *const wa1, const float fsign) +{ + /* fsign == -1 for forward transform and +1 for backward transform */ + const v4sf vsign{ld_ps1(fsign)}; + const size_t l1ido{l1*ido}; + if(ido == 2) + { + for(size_t k{0};k < l1ido;k += ido, ch += ido, cc += 4*ido) + { + v4sf tr1{vsub(cc[0], cc[2*ido + 0])}; + v4sf tr2{vadd(cc[0], cc[2*ido + 0])}; + v4sf ti1{vsub(cc[1], cc[2*ido + 1])}; + v4sf ti2{vadd(cc[1], cc[2*ido + 1])}; + v4sf ti4{vmul(vsub(cc[1*ido + 0], cc[3*ido + 0]), vsign)}; + v4sf tr4{vmul(vsub(cc[3*ido + 1], cc[1*ido + 1]), vsign)}; + v4sf tr3{vadd(cc[ido + 0], cc[3*ido + 0])}; + v4sf ti3{vadd(cc[ido + 1], cc[3*ido + 1])}; + + ch[0*l1ido + 0] = vadd(tr2, tr3); + ch[0*l1ido + 1] = vadd(ti2, ti3); + ch[1*l1ido + 0] = vadd(tr1, tr4); + ch[1*l1ido + 1] = vadd(ti1, ti4); + ch[2*l1ido + 0] = vsub(tr2, tr3); + ch[2*l1ido + 1] = vsub(ti2, ti3); + ch[3*l1ido + 0] = vsub(tr1, tr4); + ch[3*l1ido + 1] = vsub(ti1, ti4); + } + } + else + { + const auto wa2 = wa1 + ido; + const auto wa3 = wa2 + ido; + for(size_t k{0};k < l1ido;k += ido, ch+=ido, cc += 4*ido) + { + for(size_t i{0};i < ido-1;i+=2) + { + v4sf tr1{vsub(cc[i + 0], cc[i + 2*ido + 0])}; + v4sf tr2{vadd(cc[i + 0], cc[i + 2*ido + 0])}; + v4sf ti1{vsub(cc[i + 1], cc[i + 2*ido + 1])}; + v4sf ti2{vadd(cc[i + 1], cc[i + 2*ido + 1])}; + v4sf tr4{vmul(vsub(cc[i + 3*ido + 1], cc[i + 1*ido + 1]), vsign)}; + v4sf ti4{vmul(vsub(cc[i + 1*ido + 0], cc[i + 3*ido + 0]), vsign)}; + v4sf tr3{vadd(cc[i + ido + 0], cc[i + 3*ido + 0])}; + v4sf ti3{vadd(cc[i + ido + 1], cc[i + 3*ido + 1])}; + + ch[i] = vadd(tr2, tr3); + v4sf cr3{vsub(tr2, tr3)}; + ch[i + 1] = vadd(ti2, ti3); + v4sf ci3{vsub(ti2, ti3)}; + + v4sf cr2{vadd(tr1, tr4)}; + v4sf cr4{vsub(tr1, tr4)}; + v4sf ci2{vadd(ti1, ti4)}; + v4sf ci4{vsub(ti1, ti4)}; + float wr1{wa1[i]}, wi1{fsign*wa1[i+1]}; + vcplxmul(cr2, ci2, ld_ps1(wr1), ld_ps1(wi1)); + float wr2{wa2[i]}, wi2{fsign*wa2[i+1]}; + ch[i + l1ido] = cr2; + ch[i + l1ido + 1] = ci2; + + vcplxmul(cr3, ci3, ld_ps1(wr2), ld_ps1(wi2)); + float wr3{wa3[i]}, wi3{fsign*wa3[i+1]}; + ch[i + 2*l1ido] = cr3; + ch[i + 2*l1ido + 1] = ci3; + + vcplxmul(cr4, ci4, ld_ps1(wr3), ld_ps1(wi3)); + ch[i + 3*l1ido] = cr4; + ch[i + 3*l1ido + 1] = ci4; + } + } + } +} /* passf4 */ + +/* + * passf5 and passb5 has been merged here, fsign = -1 for passf5, +1 for passb5 + */ +NOINLINE void passf5_ps(const size_t ido, const size_t l1, const v4sf *cc, v4sf *RESTRICT ch, + const float *const wa1, const float fsign) +{ + const v4sf tr11{ld_ps1(0.309016994374947f)}; + const v4sf tr12{ld_ps1(-0.809016994374947f)}; + const v4sf ti11{ld_ps1(0.951056516295154f*fsign)}; + const v4sf ti12{ld_ps1(0.587785252292473f*fsign)}; + + auto cc_ref = [&cc,ido](size_t a_1, size_t a_2) noexcept -> auto& + { return cc[(a_2-1)*ido + a_1 + 1]; }; + auto ch_ref = [&ch,ido,l1](size_t a_1, size_t a_3) noexcept -> auto& + { return ch[(a_3-1)*l1*ido + a_1 + 1]; }; + + assert(ido > 2); + + const auto wa2 = wa1 + ido; + const auto wa3 = wa2 + ido; + const auto wa4 = wa3 + ido; + for(size_t k{0};k < l1;++k, cc += 5*ido, ch += ido) + { + for(size_t i{0};i < ido-1;i += 2) + { + v4sf ti5{vsub(cc_ref(i , 2), cc_ref(i , 5))}; + v4sf ti2{vadd(cc_ref(i , 2), cc_ref(i , 5))}; + v4sf ti4{vsub(cc_ref(i , 3), cc_ref(i , 4))}; + v4sf ti3{vadd(cc_ref(i , 3), cc_ref(i , 4))}; + v4sf tr5{vsub(cc_ref(i-1, 2), cc_ref(i-1, 5))}; + v4sf tr2{vadd(cc_ref(i-1, 2), cc_ref(i-1, 5))}; + v4sf tr4{vsub(cc_ref(i-1, 3), cc_ref(i-1, 4))}; + v4sf tr3{vadd(cc_ref(i-1, 3), cc_ref(i-1, 4))}; + ch_ref(i-1, 1) = vadd(cc_ref(i-1, 1), vadd(tr2, tr3)); + ch_ref(i , 1) = vadd(cc_ref(i , 1), vadd(ti2, ti3)); + v4sf cr2{vadd(cc_ref(i-1, 1), vmadd(tr11, tr2, vmul(tr12, tr3)))}; + v4sf ci2{vadd(cc_ref(i , 1), vmadd(tr11, ti2, vmul(tr12, ti3)))}; + v4sf cr3{vadd(cc_ref(i-1, 1), vmadd(tr12, tr2, vmul(tr11, tr3)))}; + v4sf ci3{vadd(cc_ref(i , 1), vmadd(tr12, ti2, vmul(tr11, ti3)))}; + v4sf cr5{vmadd(ti11, tr5, vmul(ti12, tr4))}; + v4sf ci5{vmadd(ti11, ti5, vmul(ti12, ti4))}; + v4sf cr4{vsub(vmul(ti12, tr5), vmul(ti11, tr4))}; + v4sf ci4{vsub(vmul(ti12, ti5), vmul(ti11, ti4))}; + v4sf dr3{vsub(cr3, ci4)}; + v4sf dr4{vadd(cr3, ci4)}; + v4sf di3{vadd(ci3, cr4)}; + v4sf di4{vsub(ci3, cr4)}; + v4sf dr5{vadd(cr2, ci5)}; + v4sf dr2{vsub(cr2, ci5)}; + v4sf di5{vsub(ci2, cr5)}; + v4sf di2{vadd(ci2, cr5)}; + float wr1{wa1[i]}, wi1{fsign*wa1[i+1]}, wr2{wa2[i]}, wi2{fsign*wa2[i+1]}; + float wr3{wa3[i]}, wi3{fsign*wa3[i+1]}, wr4{wa4[i]}, wi4{fsign*wa4[i+1]}; + vcplxmul(dr2, di2, ld_ps1(wr1), ld_ps1(wi1)); + ch_ref(i - 1, 2) = dr2; + ch_ref(i, 2) = di2; + vcplxmul(dr3, di3, ld_ps1(wr2), ld_ps1(wi2)); + ch_ref(i - 1, 3) = dr3; + ch_ref(i, 3) = di3; + vcplxmul(dr4, di4, ld_ps1(wr3), ld_ps1(wi3)); + ch_ref(i - 1, 4) = dr4; + ch_ref(i, 4) = di4; + vcplxmul(dr5, di5, ld_ps1(wr4), ld_ps1(wi4)); + ch_ref(i - 1, 5) = dr5; + ch_ref(i, 5) = di5; + } + } +} + +NOINLINE void radf2_ps(const size_t ido, const size_t l1, const v4sf *RESTRICT cc, + v4sf *RESTRICT ch, const float *const wa1) +{ + const size_t l1ido{l1*ido}; + for(size_t k{0};k < l1ido;k += ido) + { + v4sf a{cc[k]}, b{cc[k + l1ido]}; + ch[2*k] = vadd(a, b); + ch[2*(k+ido)-1] = vsub(a, b); + } + if(ido < 2) + return; + if(ido != 2) + { + for(size_t k{0};k < l1ido;k += ido) + { + for(size_t i{2};i < ido;i += 2) + { + v4sf tr2{cc[i - 1 + k + l1ido]}, ti2{cc[i + k + l1ido]}; + v4sf br{cc[i - 1 + k]}, bi{cc[i + k]}; + vcplxmulconj(tr2, ti2, ld_ps1(wa1[i - 2]), ld_ps1(wa1[i - 1])); + ch[i + 2*k] = vadd(bi, ti2); + ch[2*(k+ido) - i] = vsub(ti2, bi); + ch[i - 1 + 2*k] = vadd(br, tr2); + ch[2*(k+ido) - i -1] = vsub(br, tr2); + } + } + if((ido&1) == 1) + return; + } + const v4sf minus_one{ld_ps1(-1.0f)}; + for(size_t k{0};k < l1ido;k += ido) + { + ch[2*k + ido] = vmul(minus_one, cc[ido-1 + k + l1ido]); + ch[2*k + ido-1] = cc[k + ido-1]; + } +} /* radf2 */ + + +NOINLINE void radb2_ps(const size_t ido, const size_t l1, const v4sf *cc, v4sf *RESTRICT ch, + const float *const wa1) +{ + const size_t l1ido{l1*ido}; + for(size_t k{0};k < l1ido;k += ido) + { + v4sf a{cc[2*k]}; + v4sf b{cc[2*(k+ido) - 1]}; + ch[k] = vadd(a, b); + ch[k + l1ido] = vsub(a, b); + } + if(ido < 2) + return; + if(ido != 2) + { + for(size_t k{0};k < l1ido;k += ido) + { + for(size_t i{2};i < ido;i += 2) + { + v4sf a{cc[i-1 + 2*k]}; + v4sf b{cc[2*(k + ido) - i - 1]}; + v4sf c{cc[i+0 + 2*k]}; + v4sf d{cc[2*(k + ido) - i + 0]}; + ch[i-1 + k] = vadd(a, b); + v4sf tr2{vsub(a, b)}; + ch[i+0 + k] = vsub(c, d); + v4sf ti2{vadd(c, d)}; + vcplxmul(tr2, ti2, ld_ps1(wa1[i - 2]), ld_ps1(wa1[i - 1])); + ch[i-1 + k + l1ido] = tr2; + ch[i+0 + k + l1ido] = ti2; + } + } + if((ido&1) == 1) + return; + } + const v4sf minus_two{ld_ps1(-2.0f)}; + for(size_t k{0};k < l1ido;k += ido) + { + v4sf a{cc[2*k + ido-1]}; + v4sf b{cc[2*k + ido]}; + ch[k + ido-1] = vadd(a,a); + ch[k + ido-1 + l1ido] = vmul(minus_two, b); + } +} /* radb2 */ + +void radf3_ps(const size_t ido, const size_t l1, const v4sf *RESTRICT cc, v4sf *RESTRICT ch, + const float *const wa1) +{ + const v4sf taur{ld_ps1(-0.5f)}; + const v4sf taui{ld_ps1(0.866025403784439f)}; + for(size_t k{0};k < l1;++k) + { + v4sf cr2{vadd(cc[(k + l1)*ido], cc[(k + 2*l1)*ido])}; + ch[ (3*k )*ido] = vadd(cc[k*ido], cr2); + ch[ (3*k + 2)*ido] = vmul(taui, vsub(cc[(k + l1*2)*ido], cc[(k + l1)*ido])); + ch[ido-1 + (3*k + 1)*ido] = vmadd(taur, cr2, cc[k*ido]); + } + if(ido == 1) + return; + + const auto wa2 = wa1 + ido; + for(size_t k{0};k < l1;++k) + { + for(size_t i{2};i < ido;i += 2) + { + const size_t ic{ido - i}; + v4sf wr1{ld_ps1(wa1[i - 2])}; + v4sf wi1{ld_ps1(wa1[i - 1])}; + v4sf dr2{cc[i - 1 + (k + l1)*ido]}; + v4sf di2{cc[i + (k + l1)*ido]}; + vcplxmulconj(dr2, di2, wr1, wi1); + + v4sf wr2{ld_ps1(wa2[i - 2])}; + v4sf wi2{ld_ps1(wa2[i - 1])}; + v4sf dr3{cc[i - 1 + (k + l1*2)*ido]}; + v4sf di3{cc[i + (k + l1*2)*ido]}; + vcplxmulconj(dr3, di3, wr2, wi2); + + v4sf cr2{vadd(dr2, dr3)}; + v4sf ci2{vadd(di2, di3)}; + ch[i - 1 + 3*k*ido] = vadd(cc[i - 1 + k*ido], cr2); + ch[i + 3*k*ido] = vadd(cc[i + k*ido], ci2); + v4sf tr2{vmadd(taur, cr2, cc[i - 1 + k*ido])}; + v4sf ti2{vmadd(taur, ci2, cc[i + k*ido])}; + v4sf tr3{vmul(taui, vsub(di2, di3))}; + v4sf ti3{vmul(taui, vsub(dr3, dr2))}; + ch[i - 1 + (3*k + 2)*ido] = vadd(tr2, tr3); + ch[ic - 1 + (3*k + 1)*ido] = vsub(tr2, tr3); + ch[i + (3*k + 2)*ido] = vadd(ti2, ti3); + ch[ic + (3*k + 1)*ido] = vsub(ti3, ti2); + } + } +} /* radf3 */ + + +void radb3_ps(const size_t ido, const size_t l1, const v4sf *RESTRICT cc, v4sf *RESTRICT ch, + const float *const wa1) +{ + static constexpr float taur{-0.5f}; + static constexpr float taui{0.866025403784439f}; + static constexpr float taui_2{taui*2.0f}; + + const v4sf vtaur{ld_ps1(taur)}; + const v4sf vtaui_2{ld_ps1(taui_2)}; + for(size_t k{0};k < l1;++k) + { + v4sf tr2 = cc[ido-1 + (3*k + 1)*ido]; + tr2 = vadd(tr2,tr2); + v4sf cr2 = vmadd(vtaur, tr2, cc[3*k*ido]); + ch[k*ido] = vadd(cc[3*k*ido], tr2); + v4sf ci3 = vmul(vtaui_2, cc[(3*k + 2)*ido]); + ch[(k + l1)*ido] = vsub(cr2, ci3); + ch[(k + 2*l1)*ido] = vadd(cr2, ci3); + } + if(ido == 1) + return; + + const auto wa2 = wa1 + ido; + const v4sf vtaui{ld_ps1(taui)}; + for(size_t k{0};k < l1;++k) + { + for(size_t i{2};i < ido;i += 2) + { + const size_t ic{ido - i}; + v4sf tr2{vadd(cc[i - 1 + (3*k + 2)*ido], cc[ic - 1 + (3*k + 1)*ido])}; + v4sf cr2{vmadd(vtaur, tr2, cc[i - 1 + 3*k*ido])}; + ch[i - 1 + k*ido] = vadd(cc[i - 1 + 3*k*ido], tr2); + v4sf ti2{vsub(cc[i + (3*k + 2)*ido], cc[ic + (3*k + 1)*ido])}; + v4sf ci2{vmadd(vtaur, ti2, cc[i + 3*k*ido])}; + ch[i + k*ido] = vadd(cc[i + 3*k*ido], ti2); + v4sf cr3{vmul(vtaui, vsub(cc[i - 1 + (3*k + 2)*ido], cc[ic - 1 + (3*k + 1)*ido]))}; + v4sf ci3{vmul(vtaui, vadd(cc[i + (3*k + 2)*ido], cc[ic + (3*k + 1)*ido]))}; + v4sf dr2{vsub(cr2, ci3)}; + v4sf dr3{vadd(cr2, ci3)}; + v4sf di2{vadd(ci2, cr3)}; + v4sf di3{vsub(ci2, cr3)}; + vcplxmul(dr2, di2, ld_ps1(wa1[i-2]), ld_ps1(wa1[i-1])); + ch[i - 1 + (k + l1)*ido] = dr2; + ch[i + (k + l1)*ido] = di2; + vcplxmul(dr3, di3, ld_ps1(wa2[i-2]), ld_ps1(wa2[i-1])); + ch[i - 1 + (k + 2*l1)*ido] = dr3; + ch[i + (k + 2*l1)*ido] = di3; + } + } +} /* radb3 */ + +NOINLINE void radf4_ps(const size_t ido, const size_t l1, const v4sf *RESTRICT cc, + v4sf *RESTRICT ch, const float *const wa1) +{ + const size_t l1ido{l1*ido}; + { + const v4sf *RESTRICT cc_{cc}, *RESTRICT cc_end{cc + l1ido}; + v4sf *RESTRICT ch_{ch}; + while(cc != cc_end) + { + // this loop represents between 25% and 40% of total radf4_ps cost ! + v4sf a0{cc[0]}, a1{cc[l1ido]}; + v4sf a2{cc[2*l1ido]}, a3{cc[3*l1ido]}; + v4sf tr1{vadd(a1, a3)}; + v4sf tr2{vadd(a0, a2)}; + ch[2*ido-1] = vsub(a0, a2); + ch[2*ido ] = vsub(a3, a1); + ch[0 ] = vadd(tr1, tr2); + ch[4*ido-1] = vsub(tr2, tr1); + cc += ido; ch += 4*ido; + } + cc = cc_; + ch = ch_; + } + if(ido < 2) + return; + if(ido != 2) + { + const auto wa2 = wa1 + ido; + const auto wa3 = wa2 + ido; + + for(size_t k{0};k < l1ido;k += ido) + { + const v4sf *RESTRICT pc{cc + 1 + k}; + for(size_t i{2};i < ido;i += 2, pc += 2) + { + const size_t ic{ido - i}; + + v4sf cr2{pc[1*l1ido+0]}; + v4sf ci2{pc[1*l1ido+1]}; + v4sf wr{ld_ps1(wa1[i - 2])}; + v4sf wi{ld_ps1(wa1[i - 1])}; + vcplxmulconj(cr2,ci2,wr,wi); + + v4sf cr3{pc[2*l1ido+0]}; + v4sf ci3{pc[2*l1ido+1]}; + wr = ld_ps1(wa2[i-2]); + wi = ld_ps1(wa2[i-1]); + vcplxmulconj(cr3, ci3, wr, wi); + + v4sf cr4{pc[3*l1ido]}; + v4sf ci4{pc[3*l1ido+1]}; + wr = ld_ps1(wa3[i-2]); + wi = ld_ps1(wa3[i-1]); + vcplxmulconj(cr4, ci4, wr, wi); + + /* at this point, on SSE, five of "cr2 cr3 cr4 ci2 ci3 ci4" should be loaded in registers */ + + v4sf tr1{vadd(cr2,cr4)}; + v4sf tr4{vsub(cr4,cr2)}; + v4sf tr2{vadd(pc[0],cr3)}; + v4sf tr3{vsub(pc[0],cr3)}; + ch[i - 1 + 4*k ] = vadd(tr2,tr1); + ch[ic - 1 + 4*k + 3*ido] = vsub(tr2,tr1); // at this point tr1 and tr2 can be disposed + v4sf ti1{vadd(ci2,ci4)}; + v4sf ti4{vsub(ci2,ci4)}; + ch[i - 1 + 4*k + 2*ido] = vadd(tr3,ti4); + ch[ic - 1 + 4*k + 1*ido] = vsub(tr3,ti4); // dispose tr3, ti4 + v4sf ti2{vadd(pc[1],ci3)}; + v4sf ti3{vsub(pc[1],ci3)}; + ch[i + 4*k ] = vadd(ti1, ti2); + ch[ic + 4*k + 3*ido] = vsub(ti1, ti2); + ch[i + 4*k + 2*ido] = vadd(tr4, ti3); + ch[ic + 4*k + 1*ido] = vsub(tr4, ti3); + } + } + if((ido&1) == 1) + return; + } + const v4sf minus_hsqt2{ld_ps1(al::numbers::sqrt2_v * -0.5f)}; + for(size_t k{0};k < l1ido;k += ido) + { + v4sf a{cc[ido-1 + k + l1ido]}, b{cc[ido-1 + k + 3*l1ido]}; + v4sf c{cc[ido-1 + k]}, d{cc[ido-1 + k + 2*l1ido]}; + v4sf ti1{vmul(minus_hsqt2, vadd(b, a))}; + v4sf tr1{vmul(minus_hsqt2, vsub(b, a))}; + ch[ido-1 + 4*k ] = vadd(c, tr1); + ch[ido-1 + 4*k + 2*ido] = vsub(c, tr1); + ch[ 4*k + 1*ido] = vsub(ti1, d); + ch[ 4*k + 3*ido] = vadd(ti1, d); + } +} /* radf4 */ + + +NOINLINE void radb4_ps(const size_t ido, const size_t l1, const v4sf *RESTRICT cc, + v4sf *RESTRICT ch, const float *const wa1) +{ + const v4sf two{ld_ps1(2.0f)}; + const size_t l1ido{l1*ido}; + { + const v4sf *RESTRICT cc_{cc}, *RESTRICT ch_end{ch + l1ido}; + v4sf *ch_{ch}; + while(ch != ch_end) + { + v4sf a{cc[0]}, b{cc[4*ido-1]}; + v4sf c{cc[2*ido]}, d{cc[2*ido-1]}; + v4sf tr3{vmul(two,d)}; + v4sf tr2{vadd(a,b)}; + v4sf tr1{vsub(a,b)}; + v4sf tr4{vmul(two,c)}; + ch[0*l1ido] = vadd(tr2, tr3); + ch[2*l1ido] = vsub(tr2, tr3); + ch[1*l1ido] = vsub(tr1, tr4); + ch[3*l1ido] = vadd(tr1, tr4); + + cc += 4*ido; ch += ido; + } + cc = cc_; ch = ch_; + } + if(ido < 2) + return; + if(ido != 2) + { + const auto wa2 = wa1 + ido; + const auto wa3 = wa2 + ido; + + for(size_t k{0};k < l1ido;k += ido) + { + const v4sf *RESTRICT pc{cc - 1 + 4*k}; + v4sf *RESTRICT ph{ch + k + 1}; + for(size_t i{2};i < ido;i += 2) + { + v4sf tr1{vsub(pc[ i], pc[4*ido - i])}; + v4sf tr2{vadd(pc[ i], pc[4*ido - i])}; + v4sf ti4{vsub(pc[2*ido + i], pc[2*ido - i])}; + v4sf tr3{vadd(pc[2*ido + i], pc[2*ido - i])}; + ph[0] = vadd(tr2, tr3); + v4sf cr3{vsub(tr2, tr3)}; + + v4sf ti3{vsub(pc[2*ido + i + 1], pc[2*ido - i + 1])}; + v4sf tr4{vadd(pc[2*ido + i + 1], pc[2*ido - i + 1])}; + v4sf cr2{vsub(tr1, tr4)}; + v4sf cr4{vadd(tr1, tr4)}; + + v4sf ti1{vadd(pc[i + 1], pc[4*ido - i + 1])}; + v4sf ti2{vsub(pc[i + 1], pc[4*ido - i + 1])}; + + ph[1] = vadd(ti2, ti3); ph += l1ido; + v4sf ci3{vsub(ti2, ti3)}; + v4sf ci2{vadd(ti1, ti4)}; + v4sf ci4{vsub(ti1, ti4)}; + vcplxmul(cr2, ci2, ld_ps1(wa1[i-2]), ld_ps1(wa1[i-1])); + ph[0] = cr2; + ph[1] = ci2; ph += l1ido; + vcplxmul(cr3, ci3, ld_ps1(wa2[i-2]), ld_ps1(wa2[i-1])); + ph[0] = cr3; + ph[1] = ci3; ph += l1ido; + vcplxmul(cr4, ci4, ld_ps1(wa3[i-2]), ld_ps1(wa3[i-1])); + ph[0] = cr4; + ph[1] = ci4; ph = ph - 3*l1ido + 2; + } + } + if((ido&1) == 1) + return; + } + const v4sf minus_sqrt2{ld_ps1(-1.414213562373095f)}; + for(size_t k{0};k < l1ido;k += ido) + { + const size_t i0{4*k + ido}; + v4sf c{cc[i0-1]}, d{cc[i0 + 2*ido-1]}; + v4sf a{cc[i0+0]}, b{cc[i0 + 2*ido+0]}; + v4sf tr1{vsub(c,d)}; + v4sf tr2{vadd(c,d)}; + v4sf ti1{vadd(b,a)}; + v4sf ti2{vsub(b,a)}; + ch[ido-1 + k + 0*l1ido] = vadd(tr2,tr2); + ch[ido-1 + k + 1*l1ido] = vmul(minus_sqrt2, vsub(ti1, tr1)); + ch[ido-1 + k + 2*l1ido] = vadd(ti2, ti2); + ch[ido-1 + k + 3*l1ido] = vmul(minus_sqrt2, vadd(ti1, tr1)); + } +} /* radb4 */ + +void radf5_ps(const size_t ido, const size_t l1, const v4sf *RESTRICT cc, v4sf *RESTRICT ch, + const float *const wa1) +{ + const v4sf tr11{ld_ps1(0.309016994374947f)}; + const v4sf ti11{ld_ps1(0.951056516295154f)}; + const v4sf tr12{ld_ps1(-0.809016994374947f)}; + const v4sf ti12{ld_ps1(0.587785252292473f)}; + + auto cc_ref = [&cc,l1,ido](size_t a_1, size_t a_2, size_t a_3) noexcept -> auto& + { return cc[(a_3*l1 + a_2)*ido + a_1]; }; + auto ch_ref = [&ch,ido](size_t a_1, size_t a_2, size_t a_3) noexcept -> auto& + { return ch[(a_3*5 + a_2)*ido + a_1]; }; + + /* Parameter adjustments */ + ch -= 1 + ido * 6; + cc -= 1 + ido * (1 + l1); + + const auto wa2 = wa1 + ido; + const auto wa3 = wa2 + ido; + const auto wa4 = wa3 + ido; + + /* Function Body */ + for(size_t k{1};k <= l1;++k) + { + v4sf cr2{vadd(cc_ref(1, k, 5), cc_ref(1, k, 2))}; + v4sf ci5{vsub(cc_ref(1, k, 5), cc_ref(1, k, 2))}; + v4sf cr3{vadd(cc_ref(1, k, 4), cc_ref(1, k, 3))}; + v4sf ci4{vsub(cc_ref(1, k, 4), cc_ref(1, k, 3))}; + ch_ref(1, 1, k) = vadd(cc_ref(1, k, 1), vadd(cr2, cr3)); + ch_ref(ido, 2, k) = vadd(cc_ref(1, k, 1), vmadd(tr11, cr2, vmul(tr12, cr3))); + ch_ref(1, 3, k) = vmadd(ti11, ci5, vmul(ti12, ci4)); + ch_ref(ido, 4, k) = vadd(cc_ref(1, k, 1), vmadd(tr12, cr2, vmul(tr11, cr3))); + ch_ref(1, 5, k) = vsub(vmul(ti12, ci5), vmul(ti11, ci4)); + //fmt::println("pffft: radf5, k={} ch_ref={:f}, ci4={:f}", k, ch_ref(1, 5, k), ci4); + } + if(ido == 1) + return; + + const size_t idp2{ido + 2}; + for(size_t k{1};k <= l1;++k) + { + for(size_t i{3};i <= ido;i += 2) + { + const size_t ic{idp2 - i}; + v4sf dr2{ld_ps1(wa1[i-3])}; + v4sf di2{ld_ps1(wa1[i-2])}; + v4sf dr3{ld_ps1(wa2[i-3])}; + v4sf di3{ld_ps1(wa2[i-2])}; + v4sf dr4{ld_ps1(wa3[i-3])}; + v4sf di4{ld_ps1(wa3[i-2])}; + v4sf dr5{ld_ps1(wa4[i-3])}; + v4sf di5{ld_ps1(wa4[i-2])}; + vcplxmulconj(dr2, di2, cc_ref(i-1, k, 2), cc_ref(i, k, 2)); + vcplxmulconj(dr3, di3, cc_ref(i-1, k, 3), cc_ref(i, k, 3)); + vcplxmulconj(dr4, di4, cc_ref(i-1, k, 4), cc_ref(i, k, 4)); + vcplxmulconj(dr5, di5, cc_ref(i-1, k, 5), cc_ref(i, k, 5)); + v4sf cr2{vadd(dr2, dr5)}; + v4sf ci5{vsub(dr5, dr2)}; + v4sf cr5{vsub(di2, di5)}; + v4sf ci2{vadd(di2, di5)}; + v4sf cr3{vadd(dr3, dr4)}; + v4sf ci4{vsub(dr4, dr3)}; + v4sf cr4{vsub(di3, di4)}; + v4sf ci3{vadd(di3, di4)}; + ch_ref(i - 1, 1, k) = vadd(cc_ref(i - 1, k, 1), vadd(cr2, cr3)); + ch_ref(i, 1, k) = vsub(cc_ref(i, k, 1), vadd(ci2, ci3)); + v4sf tr2{vadd(cc_ref(i - 1, k, 1), vmadd(tr11, cr2, vmul(tr12, cr3)))}; + v4sf ti2{vsub(cc_ref(i, k, 1), vmadd(tr11, ci2, vmul(tr12, ci3)))}; + v4sf tr3{vadd(cc_ref(i - 1, k, 1), vmadd(tr12, cr2, vmul(tr11, cr3)))}; + v4sf ti3{vsub(cc_ref(i, k, 1), vmadd(tr12, ci2, vmul(tr11, ci3)))}; + v4sf tr5{vmadd(ti11, cr5, vmul(ti12, cr4))}; + v4sf ti5{vmadd(ti11, ci5, vmul(ti12, ci4))}; + v4sf tr4{vsub(vmul(ti12, cr5), vmul(ti11, cr4))}; + v4sf ti4{vsub(vmul(ti12, ci5), vmul(ti11, ci4))}; + ch_ref(i - 1, 3, k) = vsub(tr2, tr5); + ch_ref(ic - 1, 2, k) = vadd(tr2, tr5); + ch_ref(i , 3, k) = vadd(ti5, ti2); + ch_ref(ic , 2, k) = vsub(ti5, ti2); + ch_ref(i - 1, 5, k) = vsub(tr3, tr4); + ch_ref(ic - 1, 4, k) = vadd(tr3, tr4); + ch_ref(i , 5, k) = vadd(ti4, ti3); + ch_ref(ic , 4, k) = vsub(ti4, ti3); + } + } +} /* radf5 */ + +void radb5_ps(const size_t ido, const size_t l1, const v4sf *RESTRICT cc, v4sf *RESTRICT ch, + const float *const wa1) +{ + const v4sf tr11{ld_ps1(0.309016994374947f)}; + const v4sf ti11{ld_ps1(0.951056516295154f)}; + const v4sf tr12{ld_ps1(-0.809016994374947f)}; + const v4sf ti12{ld_ps1(0.587785252292473f)}; + + auto cc_ref = [&cc,ido](size_t a_1, size_t a_2, size_t a_3) noexcept -> auto& + { return cc[(a_3*5 + a_2)*ido + a_1]; }; + auto ch_ref = [&ch,ido,l1](size_t a_1, size_t a_2, size_t a_3) noexcept -> auto& + { return ch[(a_3*l1 + a_2)*ido + a_1]; }; + + /* Parameter adjustments */ + ch -= 1 + ido*(1 + l1); + cc -= 1 + ido*6; + + const auto wa2 = wa1 + ido; + const auto wa3 = wa2 + ido; + const auto wa4 = wa3 + ido; + + /* Function Body */ + for(size_t k{1};k <= l1;++k) + { + v4sf ti5{vadd(cc_ref( 1, 3, k), cc_ref(1, 3, k))}; + v4sf ti4{vadd(cc_ref( 1, 5, k), cc_ref(1, 5, k))}; + v4sf tr2{vadd(cc_ref(ido, 2, k), cc_ref(ido, 2, k))}; + v4sf tr3{vadd(cc_ref(ido, 4, k), cc_ref(ido, 4, k))}; + ch_ref(1, k, 1) = vadd(cc_ref(1, 1, k), vadd(tr2, tr3)); + v4sf cr2{vadd(cc_ref(1, 1, k), vmadd(tr11, tr2, vmul(tr12, tr3)))}; + v4sf cr3{vadd(cc_ref(1, 1, k), vmadd(tr12, tr2, vmul(tr11, tr3)))}; + v4sf ci5{vmadd(ti11, ti5, vmul(ti12, ti4))}; + v4sf ci4{vsub(vmul(ti12, ti5), vmul(ti11, ti4))}; + ch_ref(1, k, 2) = vsub(cr2, ci5); + ch_ref(1, k, 3) = vsub(cr3, ci4); + ch_ref(1, k, 4) = vadd(cr3, ci4); + ch_ref(1, k, 5) = vadd(cr2, ci5); + } + if(ido == 1) + return; + + const size_t idp2{ido + 2}; + for(size_t k{1};k <= l1;++k) + { + for(size_t i{3};i <= ido;i += 2) + { + const size_t ic{idp2 - i}; + v4sf ti5{vadd(cc_ref(i , 3, k), cc_ref(ic , 2, k))}; + v4sf ti2{vsub(cc_ref(i , 3, k), cc_ref(ic , 2, k))}; + v4sf ti4{vadd(cc_ref(i , 5, k), cc_ref(ic , 4, k))}; + v4sf ti3{vsub(cc_ref(i , 5, k), cc_ref(ic , 4, k))}; + v4sf tr5{vsub(cc_ref(i-1, 3, k), cc_ref(ic-1, 2, k))}; + v4sf tr2{vadd(cc_ref(i-1, 3, k), cc_ref(ic-1, 2, k))}; + v4sf tr4{vsub(cc_ref(i-1, 5, k), cc_ref(ic-1, 4, k))}; + v4sf tr3{vadd(cc_ref(i-1, 5, k), cc_ref(ic-1, 4, k))}; + ch_ref(i - 1, k, 1) = vadd(cc_ref(i-1, 1, k), vadd(tr2, tr3)); + ch_ref(i , k, 1) = vadd(cc_ref(i , 1, k), vadd(ti2, ti3)); + v4sf cr2{vadd(cc_ref(i-1, 1, k), vmadd(tr11, tr2, vmul(tr12, tr3)))}; + v4sf ci2{vadd(cc_ref(i , 1, k), vmadd(tr11, ti2, vmul(tr12, ti3)))}; + v4sf cr3{vadd(cc_ref(i-1, 1, k), vmadd(tr12, tr2, vmul(tr11, tr3)))}; + v4sf ci3{vadd(cc_ref(i , 1, k), vmadd(tr12, ti2, vmul(tr11, ti3)))}; + v4sf cr5{vmadd(ti11, tr5, vmul(ti12, tr4))}; + v4sf ci5{vmadd(ti11, ti5, vmul(ti12, ti4))}; + v4sf cr4{vsub(vmul(ti12, tr5), vmul(ti11, tr4))}; + v4sf ci4{vsub(vmul(ti12, ti5), vmul(ti11, ti4))}; + v4sf dr3{vsub(cr3, ci4)}; + v4sf dr4{vadd(cr3, ci4)}; + v4sf di3{vadd(ci3, cr4)}; + v4sf di4{vsub(ci3, cr4)}; + v4sf dr5{vadd(cr2, ci5)}; + v4sf dr2{vsub(cr2, ci5)}; + v4sf di5{vsub(ci2, cr5)}; + v4sf di2{vadd(ci2, cr5)}; + vcplxmul(dr2, di2, ld_ps1(wa1[i-3]), ld_ps1(wa1[i-2])); + vcplxmul(dr3, di3, ld_ps1(wa2[i-3]), ld_ps1(wa2[i-2])); + vcplxmul(dr4, di4, ld_ps1(wa3[i-3]), ld_ps1(wa3[i-2])); + vcplxmul(dr5, di5, ld_ps1(wa4[i-3]), ld_ps1(wa4[i-2])); + + ch_ref(i-1, k, 2) = dr2; ch_ref(i, k, 2) = di2; + ch_ref(i-1, k, 3) = dr3; ch_ref(i, k, 3) = di3; + ch_ref(i-1, k, 4) = dr4; ch_ref(i, k, 4) = di4; + ch_ref(i-1, k, 5) = dr5; ch_ref(i, k, 5) = di5; + } + } +} /* radb5 */ + +NOINLINE v4sf *rfftf1_ps(const size_t n, const v4sf *input_readonly, v4sf *work1, v4sf *work2, + const float *wa, const al::span ifac) +{ + assert(work1 != work2); + + const v4sf *in{input_readonly}; + v4sf *out{in == work2 ? work1 : work2}; + const size_t nf{ifac[1]}; + size_t l2{n}; + size_t iw{n-1}; + size_t k1{1}; + while(true) + { + const size_t kh{nf - k1}; + const size_t ip{ifac[kh + 2]}; + const size_t l1{l2 / ip}; + const size_t ido{n / l2}; + iw -= (ip - 1)*ido; + switch(ip) + { + case 5: + radf5_ps(ido, l1, in, out, &wa[iw]); + break; + case 4: + radf4_ps(ido, l1, in, out, &wa[iw]); + break; + case 3: + radf3_ps(ido, l1, in, out, &wa[iw]); + break; + case 2: + radf2_ps(ido, l1, in, out, &wa[iw]); + break; + default: + assert(0); + } + if(++k1 > nf) + return out; + + l2 = l1; + if(out == work2) + { + out = work1; + in = work2; + } + else + { + out = work2; + in = work1; + } + } +} /* rfftf1 */ + +NOINLINE v4sf *rfftb1_ps(const size_t n, const v4sf *input_readonly, v4sf *work1, v4sf *work2, + const float *wa, const al::span ifac) +{ + assert(work1 != work2); + + const v4sf *in{input_readonly}; + v4sf *out{in == work2 ? work1 : work2}; + const size_t nf{ifac[1]}; + size_t l1{1}; + size_t iw{0}; + size_t k1{1}; + while(true) + { + const size_t ip{ifac[k1 + 1]}; + const size_t l2{ip*l1}; + const size_t ido{n / l2}; + switch(ip) + { + case 5: + radb5_ps(ido, l1, in, out, &wa[iw]); + break; + case 4: + radb4_ps(ido, l1, in, out, &wa[iw]); + break; + case 3: + radb3_ps(ido, l1, in, out, &wa[iw]); + break; + case 2: + radb2_ps(ido, l1, in, out, &wa[iw]); + break; + default: + assert(0); + } + if(++k1 > nf) + return out; + + l1 = l2; + iw += (ip - 1)*ido; + + if(out == work2) + { + out = work1; + in = work2; + } + else + { + out = work2; + in = work1; + } + } +} + +v4sf *cfftf1_ps(const size_t n, const v4sf *input_readonly, v4sf *work1, v4sf *work2, + const float *wa, const al::span ifac, const float fsign) +{ + assert(work1 != work2); + + const v4sf *in{input_readonly}; + v4sf *out{in == work2 ? work1 : work2}; + const size_t nf{ifac[1]}; + size_t l1{1}, iw{0}; + size_t k1{2}; + while(true) + { + const size_t ip{ifac[k1]}; + const size_t l2{ip*l1}; + const size_t ido{n / l2}; + const size_t idot{ido + ido}; + switch(ip) + { + case 5: + passf5_ps(idot, l1, in, out, &wa[iw], fsign); + break; + case 4: + passf4_ps(idot, l1, in, out, &wa[iw], fsign); + break; + case 3: + passf3_ps(idot, l1, in, out, &wa[iw], fsign); + break; + case 2: + passf2_ps(idot, l1, in, out, &wa[iw], fsign); + break; + default: + assert(0); + } + if(++k1 > nf+1) + return out; + + l1 = l2; + iw += (ip - 1)*idot; + if(out == work2) + { + out = work1; + in = work2; + } + else + { + out = work2; + in = work1; + } + } +} + + +uint decompose(const uint n, const al::span ifac, const al::span ntryh) +{ + uint nl{n}, nf{0}; + for(const uint ntry : ntryh) + { + while(nl != 1) + { + const uint nq{nl / ntry}; + const uint nr{nl % ntry}; + if(nr != 0) break; + + ifac[2+nf++] = ntry; + nl = nq; + if(ntry == 2 && nf != 1) + { + for(size_t i{2};i <= nf;++i) + { + size_t ib{nf - i + 2}; + ifac[ib + 1] = ifac[ib]; + } + ifac[2] = 2; + } + } + } + ifac[0] = n; + ifac[1] = nf; + return nf; +} + +void rffti1_ps(const uint n, float *wa, const al::span ifac) +{ + static constexpr std::array ntryh{4u,2u,3u,5u}; + + const uint nf{decompose(n, ifac, ntryh)}; + const double argh{2.0*al::numbers::pi / n}; + size_t is{0}; + size_t nfm1{nf - 1}; + size_t l1{1}; + for(size_t k1{0};k1 < nfm1;++k1) + { + const size_t ip{ifac[k1+2]}; + const size_t l2{l1*ip}; + const size_t ido{n / l2}; + const size_t ipm{ip - 1}; + size_t ld{0}; + for(size_t j{0};j < ipm;++j) + { + size_t i{is}; + ld += l1; + const double argld{static_cast(ld)*argh}; + double fi{0.0}; + for(size_t ii{2};ii < ido;ii += 2) + { + fi += 1.0; + wa[i++] = static_cast(std::cos(fi*argld)); + wa[i++] = static_cast(std::sin(fi*argld)); + } + is += ido; + } + l1 = l2; + } +} /* rffti1 */ + +void cffti1_ps(const uint n, float *wa, const al::span ifac) +{ + static constexpr std::array ntryh{5u,3u,4u,2u}; + + const uint nf{decompose(n, ifac, ntryh)}; + const double argh{2.0*al::numbers::pi / n}; + size_t i{1}; + size_t l1{1}; + for(size_t k1{0};k1 < nf;++k1) + { + const size_t ip{ifac[k1+2]}; + const size_t l2{l1*ip}; + const size_t ido{n / l2}; + const size_t idot{ido + ido + 2}; + const size_t ipm{ip - 1}; + size_t ld{0}; + for(size_t j{0};j < ipm;++j) + { + size_t i1{i}; + wa[i-1] = 1; + wa[i] = 0; + ld += l1; + const double argld{static_cast(ld)*argh}; + double fi{0.0}; + for(size_t ii{3};ii < idot;ii += 2) + { + fi += 1.0; + wa[++i] = static_cast(std::cos(fi*argld)); + wa[++i] = static_cast(std::sin(fi*argld)); + } + if(ip > 5) + { + wa[i1-1] = wa[i-1]; + wa[i1] = wa[i]; + } + } + l1 = l2; + } +} /* cffti1 */ + +} // namespace + +/* NOLINTNEXTLINE(clang-analyzer-optin.performance.Padding) */ +struct PFFFT_Setup { + uint N{}; + uint Ncvec{}; /* nb of complex simd vectors (N/4 if PFFFT_COMPLEX, N/8 if PFFFT_REAL) */ + std::array ifac{}; + pffft_transform_t transform{}; + + float *twiddle{}; /* N/4 elements */ + al::span e; /* N/4*3 elements */ + + alignas(V4sfAlignment) std::byte end; +}; + +PFFFTSetupPtr pffft_new_setup(unsigned int N, pffft_transform_t transform) +{ + assert(transform == PFFFT_REAL || transform == PFFFT_COMPLEX); + assert(N > 0); + /* unfortunately, the fft size must be a multiple of 16 for complex FFTs + * and 32 for real FFTs -- a lot of stuff would need to be rewritten to + * handle other cases (or maybe just switch to a scalar fft, I don't know..) + */ + if(transform == PFFFT_REAL) + assert((N%(2*SimdSize*SimdSize)) == 0); + else + assert((N%(SimdSize*SimdSize)) == 0); + + const uint Ncvec{(transform == PFFFT_REAL ? N/2 : N) / SimdSize}; + + const size_t storelen{std::max(offsetof(PFFFT_Setup, end) + 2_zu*Ncvec*sizeof(v4sf), + sizeof(PFFFT_Setup))}; + auto storage = static_cast>(::operator new[](storelen, V4sfAlignVal)); + al::span extrastore{&storage[offsetof(PFFFT_Setup, end)], 2_zu*Ncvec*sizeof(v4sf)}; + + PFFFTSetupPtr s{::new(storage) PFFFT_Setup{}}; + s->N = N; + s->transform = transform; + s->Ncvec = Ncvec; + + const size_t ecount{2_zu*Ncvec*(SimdSize-1)/SimdSize}; + s->e = {std::launder(reinterpret_cast(extrastore.data())), ecount}; + s->twiddle = std::launder(reinterpret_cast(&extrastore[ecount*sizeof(v4sf)])); + + if constexpr(SimdSize > 1) + { + auto e = std::vector(s->e.size()*SimdSize, 0.0f); + for(size_t k{0};k < s->Ncvec;++k) + { + const size_t i{k / SimdSize}; + const size_t j{k % SimdSize}; + for(size_t m{0};m < SimdSize-1;++m) + { + const double A{-2.0*al::numbers::pi*static_cast((m+1)*k) / N}; + e[((i*3 + m)*2 + 0)*SimdSize + j] = static_cast(std::cos(A)); + e[((i*3 + m)*2 + 1)*SimdSize + j] = static_cast(std::sin(A)); + } + } + std::memcpy(s->e.data(), e.data(), e.size()*sizeof(float)); + } + if(transform == PFFFT_REAL) + rffti1_ps(N/SimdSize, s->twiddle, s->ifac); + else + cffti1_ps(N/SimdSize, s->twiddle, s->ifac); + + /* check that N is decomposable with allowed prime factors */ + size_t m{1}; + for(size_t k{0};k < s->ifac[1];++k) + m *= s->ifac[2+k]; + + if(m != N/SimdSize) + s = nullptr; + + return s; +} + + +void pffft_destroy_setup(gsl::owner s) noexcept +{ + std::destroy_at(s); + ::operator delete[](gsl::owner{s}, V4sfAlignVal); +} + +#if !defined(PFFFT_SIMD_DISABLE) + +namespace { + +/* [0 0 1 2 3 4 5 6 7 8] -> [0 8 7 6 5 4 3 2 1] */ +void reversed_copy(const size_t N, const v4sf *in, const int in_stride, v4sf *RESTRICT out) +{ + v4sf g0, g1; + interleave2(in[0], in[1], g0, g1); + in += in_stride; + + *--out = vswaphl(g0, g1); // [g0l, g0h], [g1l g1h] -> [g1l, g0h] + for(size_t k{1};k < N;++k) + { + v4sf h0, h1; + interleave2(in[0], in[1], h0, h1); + in += in_stride; + *--out = vswaphl(g1, h0); + *--out = vswaphl(h0, h1); + g1 = h1; + } + *--out = vswaphl(g1, g0); +} + +void unreversed_copy(const size_t N, const v4sf *in, v4sf *RESTRICT out, const int out_stride) +{ + v4sf g0{in[0]}, g1{g0}; + ++in; + for(size_t k{1};k < N;++k) + { + v4sf h0{*in++}; v4sf h1{*in++}; + g1 = vswaphl(g1, h0); + h0 = vswaphl(h0, h1); + uninterleave2(h0, g1, out[0], out[1]); + out += out_stride; + g1 = h1; + } + v4sf h0{*in++}, h1{g0}; + g1 = vswaphl(g1, h0); + h0 = vswaphl(h0, h1); + uninterleave2(h0, g1, out[0], out[1]); +} + +void pffft_cplx_finalize(const size_t Ncvec, const v4sf *in, v4sf *RESTRICT out, const v4sf *e) +{ + assert(in != out); + + const size_t dk{Ncvec/SimdSize}; // number of 4x4 matrix blocks + for(size_t k{0};k < dk;++k) + { + v4sf r0{in[8*k+0]}, i0{in[8*k+1]}; + v4sf r1{in[8*k+2]}, i1{in[8*k+3]}; + v4sf r2{in[8*k+4]}, i2{in[8*k+5]}; + v4sf r3{in[8*k+6]}, i3{in[8*k+7]}; + vtranspose4(r0,r1,r2,r3); + vtranspose4(i0,i1,i2,i3); + vcplxmul(r1,i1,e[k*6+0],e[k*6+1]); + vcplxmul(r2,i2,e[k*6+2],e[k*6+3]); + vcplxmul(r3,i3,e[k*6+4],e[k*6+5]); + + v4sf sr0{vadd(r0,r2)}, dr0{vsub(r0, r2)}; + v4sf sr1{vadd(r1,r3)}, dr1{vsub(r1, r3)}; + v4sf si0{vadd(i0,i2)}, di0{vsub(i0, i2)}; + v4sf si1{vadd(i1,i3)}, di1{vsub(i1, i3)}; + + /* transformation for each column is: + * + * [1 1 1 1 0 0 0 0] [r0] + * [1 0 -1 0 0 -1 0 1] [r1] + * [1 -1 1 -1 0 0 0 0] [r2] + * [1 0 -1 0 0 1 0 -1] [r3] + * [0 0 0 0 1 1 1 1] * [i0] + * [0 1 0 -1 1 0 -1 0] [i1] + * [0 0 0 0 1 -1 1 -1] [i2] + * [0 -1 0 1 1 0 -1 0] [i3] + */ + + r0 = vadd(sr0, sr1); i0 = vadd(si0, si1); + r1 = vadd(dr0, di1); i1 = vsub(di0, dr1); + r2 = vsub(sr0, sr1); i2 = vsub(si0, si1); + r3 = vsub(dr0, di1); i3 = vadd(di0, dr1); + + *out++ = r0; *out++ = i0; *out++ = r1; *out++ = i1; + *out++ = r2; *out++ = i2; *out++ = r3; *out++ = i3; + } +} + +void pffft_cplx_preprocess(const size_t Ncvec, const v4sf *in, v4sf *RESTRICT out, const v4sf *e) +{ + assert(in != out); + + const size_t dk{Ncvec/SimdSize}; // number of 4x4 matrix blocks + for(size_t k{0};k < dk;++k) + { + v4sf r0{in[8*k+0]}, i0{in[8*k+1]}; + v4sf r1{in[8*k+2]}, i1{in[8*k+3]}; + v4sf r2{in[8*k+4]}, i2{in[8*k+5]}; + v4sf r3{in[8*k+6]}, i3{in[8*k+7]}; + + v4sf sr0{vadd(r0,r2)}, dr0{vsub(r0, r2)}; + v4sf sr1{vadd(r1,r3)}, dr1{vsub(r1, r3)}; + v4sf si0{vadd(i0,i2)}, di0{vsub(i0, i2)}; + v4sf si1{vadd(i1,i3)}, di1{vsub(i1, i3)}; + + r0 = vadd(sr0, sr1); i0 = vadd(si0, si1); + r1 = vsub(dr0, di1); i1 = vadd(di0, dr1); + r2 = vsub(sr0, sr1); i2 = vsub(si0, si1); + r3 = vadd(dr0, di1); i3 = vsub(di0, dr1); + + vcplxmulconj(r1,i1,e[k*6+0],e[k*6+1]); + vcplxmulconj(r2,i2,e[k*6+2],e[k*6+3]); + vcplxmulconj(r3,i3,e[k*6+4],e[k*6+5]); + + vtranspose4(r0,r1,r2,r3); + vtranspose4(i0,i1,i2,i3); + + *out++ = r0; *out++ = i0; *out++ = r1; *out++ = i1; + *out++ = r2; *out++ = i2; *out++ = r3; *out++ = i3; + } +} + + +force_inline void pffft_real_finalize_4x4(const v4sf *in0, const v4sf *in1, const v4sf *in, + const v4sf *e, v4sf *RESTRICT out) +{ + v4sf r0{*in0}, i0{*in1}; + v4sf r1{*in++}; v4sf i1{*in++}; + v4sf r2{*in++}; v4sf i2{*in++}; + v4sf r3{*in++}; v4sf i3{*in++}; + vtranspose4(r0,r1,r2,r3); + vtranspose4(i0,i1,i2,i3); + + /* transformation for each column is: + * + * [1 1 1 1 0 0 0 0] [r0] + * [1 0 -1 0 0 -1 0 1] [r1] + * [1 0 -1 0 0 1 0 -1] [r2] + * [1 -1 1 -1 0 0 0 0] [r3] + * [0 0 0 0 1 1 1 1] * [i0] + * [0 -1 0 1 -1 0 1 0] [i1] + * [0 -1 0 1 1 0 -1 0] [i2] + * [0 0 0 0 -1 1 -1 1] [i3] + */ + + //cerr << "matrix initial, before e , REAL:\n 1: " << r0 << "\n 1: " << r1 << "\n 1: " << r2 << "\n 1: " << r3 << "\n"; + //cerr << "matrix initial, before e, IMAG :\n 1: " << i0 << "\n 1: " << i1 << "\n 1: " << i2 << "\n 1: " << i3 << "\n"; + + vcplxmul(r1,i1,e[0],e[1]); + vcplxmul(r2,i2,e[2],e[3]); + vcplxmul(r3,i3,e[4],e[5]); + + //cerr << "matrix initial, real part:\n 1: " << r0 << "\n 1: " << r1 << "\n 1: " << r2 << "\n 1: " << r3 << "\n"; + //cerr << "matrix initial, imag part:\n 1: " << i0 << "\n 1: " << i1 << "\n 1: " << i2 << "\n 1: " << i3 << "\n"; + + v4sf sr0{vadd(r0,r2)}, dr0{vsub(r0,r2)}; + v4sf sr1{vadd(r1,r3)}, dr1{vsub(r3,r1)}; + v4sf si0{vadd(i0,i2)}, di0{vsub(i0,i2)}; + v4sf si1{vadd(i1,i3)}, di1{vsub(i3,i1)}; + + r0 = vadd(sr0, sr1); + r3 = vsub(sr0, sr1); + i0 = vadd(si0, si1); + i3 = vsub(si1, si0); + r1 = vadd(dr0, di1); + r2 = vsub(dr0, di1); + i1 = vsub(dr1, di0); + i2 = vadd(dr1, di0); + + *out++ = r0; + *out++ = i0; + *out++ = r1; + *out++ = i1; + *out++ = r2; + *out++ = i2; + *out++ = r3; + *out++ = i3; +} + +NOINLINE void pffft_real_finalize(const size_t Ncvec, const v4sf *in, v4sf *RESTRICT out, + const v4sf *e) +{ + static constexpr float s{al::numbers::sqrt2_v/2.0f}; + + assert(in != out); + const size_t dk{Ncvec/SimdSize}; // number of 4x4 matrix blocks + /* fftpack order is f0r f1r f1i f2r f2i ... f(n-1)r f(n-1)i f(n)r */ + + const v4sf zero{vzero()}; + const auto cr = al::bit_cast>(in[0]); + const auto ci = al::bit_cast>(in[Ncvec*2-1]); + pffft_real_finalize_4x4(&zero, &zero, in+1, e, out); + + /* [cr0 cr1 cr2 cr3 ci0 ci1 ci2 ci3] + * + * [Xr(1)] ] [1 1 1 1 0 0 0 0] + * [Xr(N/4) ] [0 0 0 0 1 s 0 -s] + * [Xr(N/2) ] [1 0 -1 0 0 0 0 0] + * [Xr(3N/4)] [0 0 0 0 1 -s 0 s] + * [Xi(1) ] [1 -1 1 -1 0 0 0 0] + * [Xi(N/4) ] [0 0 0 0 0 -s -1 -s] + * [Xi(N/2) ] [0 -1 0 1 0 0 0 0] + * [Xi(3N/4)] [0 0 0 0 0 -s 1 -s] + */ + + const float xr0{(cr[0]+cr[2]) + (cr[1]+cr[3])}; out[0] = vinsert0(out[0], xr0); + const float xi0{(cr[0]+cr[2]) - (cr[1]+cr[3])}; out[1] = vinsert0(out[1], xi0); + const float xr2{(cr[0]-cr[2])}; out[4] = vinsert0(out[4], xr2); + const float xi2{(cr[3]-cr[1])}; out[5] = vinsert0(out[5], xi2); + const float xr1{ ci[0] + s*(ci[1]-ci[3])}; out[2] = vinsert0(out[2], xr1); + const float xi1{-ci[2] - s*(ci[1]+ci[3])}; out[3] = vinsert0(out[3], xi1); + const float xr3{ ci[0] - s*(ci[1]-ci[3])}; out[6] = vinsert0(out[6], xr3); + const float xi3{ ci[2] - s*(ci[1]+ci[3])}; out[7] = vinsert0(out[7], xi3); + + for(size_t k{1};k < dk;++k) + pffft_real_finalize_4x4(&in[8*k-1], &in[8*k+0], in + 8*k+1, e + k*6, out + k*8); +} + +force_inline void pffft_real_preprocess_4x4(const v4sf *in, const v4sf *e, v4sf *RESTRICT out, + const bool first) +{ + v4sf r0{in[0]}, i0{in[1]}, r1{in[2]}, i1{in[3]}; + v4sf r2{in[4]}, i2{in[5]}, r3{in[6]}, i3{in[7]}; + + /* transformation for each column is: + * + * [1 1 1 1 0 0 0 0] [r0] + * [1 0 0 -1 0 -1 -1 0] [r1] + * [1 -1 -1 1 0 0 0 0] [r2] + * [1 0 0 -1 0 1 1 0] [r3] + * [0 0 0 0 1 -1 1 -1] * [i0] + * [0 -1 1 0 1 0 0 1] [i1] + * [0 0 0 0 1 1 -1 -1] [i2] + * [0 1 -1 0 1 0 0 1] [i3] + */ + + v4sf sr0{vadd(r0,r3)}, dr0{vsub(r0,r3)}; + v4sf sr1{vadd(r1,r2)}, dr1{vsub(r1,r2)}; + v4sf si0{vadd(i0,i3)}, di0{vsub(i0,i3)}; + v4sf si1{vadd(i1,i2)}, di1{vsub(i1,i2)}; + + r0 = vadd(sr0, sr1); + r2 = vsub(sr0, sr1); + r1 = vsub(dr0, si1); + r3 = vadd(dr0, si1); + i0 = vsub(di0, di1); + i2 = vadd(di0, di1); + i1 = vsub(si0, dr1); + i3 = vadd(si0, dr1); + + vcplxmulconj(r1,i1,e[0],e[1]); + vcplxmulconj(r2,i2,e[2],e[3]); + vcplxmulconj(r3,i3,e[4],e[5]); + + vtranspose4(r0,r1,r2,r3); + vtranspose4(i0,i1,i2,i3); + + if(!first) + { + *out++ = r0; + *out++ = i0; + } + *out++ = r1; + *out++ = i1; + *out++ = r2; + *out++ = i2; + *out++ = r3; + *out++ = i3; +} + +NOINLINE void pffft_real_preprocess(const size_t Ncvec, const v4sf *in, v4sf *RESTRICT out, + const v4sf *e) +{ + static constexpr float sqrt2{al::numbers::sqrt2_v}; + + assert(in != out); + const size_t dk{Ncvec/SimdSize}; // number of 4x4 matrix blocks + /* fftpack order is f0r f1r f1i f2r f2i ... f(n-1)r f(n-1)i f(n)r */ + + std::array Xr{}, Xi{}; + for(size_t k{0};k < SimdSize;++k) + { + Xr[k] = vextract0(in[2*k]); + Xi[k] = vextract0(in[2*k + 1]); + } + + pffft_real_preprocess_4x4(in, e, out+1, true); // will write only 6 values + + /* [Xr0 Xr1 Xr2 Xr3 Xi0 Xi1 Xi2 Xi3] + * + * [cr0] [1 0 2 0 1 0 0 0] + * [cr1] [1 0 0 0 -1 0 -2 0] + * [cr2] [1 0 -2 0 1 0 0 0] + * [cr3] [1 0 0 0 -1 0 2 0] + * [ci0] [0 2 0 2 0 0 0 0] + * [ci1] [0 s 0 -s 0 -s 0 -s] + * [ci2] [0 0 0 0 0 -2 0 2] + * [ci3] [0 -s 0 s 0 -s 0 -s] + */ + for(size_t k{1};k < dk;++k) + pffft_real_preprocess_4x4(in+8*k, e + k*6, out-1+k*8, false); + + const float cr0{(Xr[0]+Xi[0]) + 2*Xr[2]}; + const float cr1{(Xr[0]-Xi[0]) - 2*Xi[2]}; + const float cr2{(Xr[0]+Xi[0]) - 2*Xr[2]}; + const float cr3{(Xr[0]-Xi[0]) + 2*Xi[2]}; + out[0] = vset4(cr0, cr1, cr2, cr3); + const float ci0{ 2*(Xr[1]+Xr[3])}; + const float ci1{ sqrt2*(Xr[1]-Xr[3]) - sqrt2*(Xi[1]+Xi[3])}; + const float ci2{ 2*(Xi[3]-Xi[1])}; + const float ci3{-sqrt2*(Xr[1]-Xr[3]) - sqrt2*(Xi[1]+Xi[3])}; + out[2*Ncvec-1] = vset4(ci0, ci1, ci2, ci3); +} + + +void pffft_transform_internal(const PFFFT_Setup *setup, const v4sf *vinput, v4sf *voutput, + v4sf *scratch, const pffft_direction_t direction, const bool ordered) +{ + assert(scratch != nullptr); + assert(voutput != scratch); + + const size_t Ncvec{setup->Ncvec}; + const bool nf_odd{(setup->ifac[1]&1) != 0}; + + std::array buff{voutput, scratch}; + bool ib{nf_odd != ordered}; + if(direction == PFFFT_FORWARD) + { + /* Swap the initial work buffer for forward FFTs, which helps avoid an + * extra copy for output. + */ + ib = !ib; + if(setup->transform == PFFFT_REAL) + { + ib = (rfftf1_ps(Ncvec*2, vinput, buff[ib], buff[!ib], setup->twiddle, setup->ifac) == buff[1]); + pffft_real_finalize(Ncvec, buff[ib], buff[!ib], setup->e.data()); + } + else + { + v4sf *tmp{buff[ib]}; + for(size_t k=0; k < Ncvec; ++k) + uninterleave2(vinput[k*2], vinput[k*2+1], tmp[k*2], tmp[k*2+1]); + + ib = (cfftf1_ps(Ncvec, buff[ib], buff[!ib], buff[ib], setup->twiddle, setup->ifac, -1.0f) == buff[1]); + pffft_cplx_finalize(Ncvec, buff[ib], buff[!ib], setup->e.data()); + } + if(ordered) + pffft_zreorder(setup, reinterpret_cast(buff[!ib]), + reinterpret_cast(buff[ib]), PFFFT_FORWARD); + else + ib = !ib; + } + else + { + if(vinput == buff[ib]) + ib = !ib; // may happen when finput == foutput + + if(ordered) + { + pffft_zreorder(setup, reinterpret_cast(vinput), + reinterpret_cast(buff[ib]), PFFFT_BACKWARD); + vinput = buff[ib]; + ib = !ib; + } + if(setup->transform == PFFFT_REAL) + { + pffft_real_preprocess(Ncvec, vinput, buff[ib], setup->e.data()); + ib = (rfftb1_ps(Ncvec*2, buff[ib], buff[0], buff[1], setup->twiddle, setup->ifac) == buff[1]); + } + else + { + pffft_cplx_preprocess(Ncvec, vinput, buff[ib], setup->e.data()); + ib = (cfftf1_ps(Ncvec, buff[ib], buff[0], buff[1], setup->twiddle, setup->ifac, +1.0f) == buff[1]); + for(size_t k{0};k < Ncvec;++k) + interleave2(buff[ib][k*2], buff[ib][k*2+1], buff[ib][k*2], buff[ib][k*2+1]); + } + } + + if(buff[ib] != voutput) + { + /* extra copy required -- this situation should only happen when finput == foutput */ + assert(vinput==voutput); + for(size_t k{0};k < Ncvec;++k) + { + v4sf a{buff[ib][2*k]}, b{buff[ib][2*k+1]}; + voutput[2*k] = a; voutput[2*k+1] = b; + } + } +} + +} // namespace + +void pffft_zreorder(const PFFFT_Setup *setup, const float *in, float *out, + pffft_direction_t direction) +{ + assert(in != out); + + const size_t N{setup->N}, Ncvec{setup->Ncvec}; + const v4sf *vin{reinterpret_cast(in)}; + v4sf *RESTRICT vout{reinterpret_cast(out)}; + if(setup->transform == PFFFT_REAL) + { + const size_t dk{N/32}; + if(direction == PFFFT_FORWARD) + { + for(size_t k{0};k < dk;++k) + { + interleave2(vin[k*8 + 0], vin[k*8 + 1], vout[2*(0*dk + k) + 0], vout[2*(0*dk + k) + 1]); + interleave2(vin[k*8 + 4], vin[k*8 + 5], vout[2*(2*dk + k) + 0], vout[2*(2*dk + k) + 1]); + } + reversed_copy(dk, vin+2, 8, vout + N/SimdSize/2); + reversed_copy(dk, vin+6, 8, vout + N/SimdSize); + } + else + { + for(size_t k{0};k < dk;++k) + { + uninterleave2(vin[2*(0*dk + k) + 0], vin[2*(0*dk + k) + 1], vout[k*8 + 0], vout[k*8 + 1]); + uninterleave2(vin[2*(2*dk + k) + 0], vin[2*(2*dk + k) + 1], vout[k*8 + 4], vout[k*8 + 5]); + } + unreversed_copy(dk, vin + N/SimdSize/4, vout + N/SimdSize - 6, -8); + unreversed_copy(dk, vin + 3_uz*N/SimdSize/4, vout + N/SimdSize - 2, -8); + } + } + else + { + if(direction == PFFFT_FORWARD) + { + for(size_t k{0};k < Ncvec;++k) + { + size_t kk{(k/4) + (k%4)*(Ncvec/4)}; + interleave2(vin[k*2], vin[k*2+1], vout[kk*2], vout[kk*2+1]); + } + } + else + { + for(size_t k{0};k < Ncvec;++k) + { + size_t kk{(k/4) + (k%4)*(Ncvec/4)}; + uninterleave2(vin[kk*2], vin[kk*2+1], vout[k*2], vout[k*2+1]); + } + } + } +} + +void pffft_zconvolve_scale_accumulate(const PFFFT_Setup *s, const float *a, const float *b, + float *ab, float scaling) +{ + const size_t Ncvec{s->Ncvec}; + const v4sf *RESTRICT va{reinterpret_cast(a)}; + const v4sf *RESTRICT vb{reinterpret_cast(b)}; + v4sf *RESTRICT vab{reinterpret_cast(ab)}; + +#ifdef __arm__ + __builtin_prefetch(va); + __builtin_prefetch(vb); + __builtin_prefetch(vab); + __builtin_prefetch(va+2); + __builtin_prefetch(vb+2); + __builtin_prefetch(vab+2); + __builtin_prefetch(va+4); + __builtin_prefetch(vb+4); + __builtin_prefetch(vab+4); + __builtin_prefetch(va+6); + __builtin_prefetch(vb+6); + __builtin_prefetch(vab+6); +#ifndef __clang__ +#define ZCONVOLVE_USING_INLINE_NEON_ASM +#endif +#endif + + const float ar1{vextract0(va[0])}; + const float ai1{vextract0(va[1])}; + const float br1{vextract0(vb[0])}; + const float bi1{vextract0(vb[1])}; + const float abr1{vextract0(vab[0])}; + const float abi1{vextract0(vab[1])}; + +#ifdef ZCONVOLVE_USING_INLINE_ASM + /* Inline asm version, unfortunately miscompiled by clang 3.2, at least on + * Ubuntu. So this will be restricted to GCC. + * + * Does it still miscompile with Clang? Is it even needed with today's + * optimizers? + */ + const float *a_{a}, *b_{b}; float *ab_{ab}; + size_t N{Ncvec}; + asm volatile("mov r8, %2 \n" + "vdup.f32 q15, %4 \n" + "1: \n" + "pld [%0,#64] \n" + "pld [%1,#64] \n" + "pld [%2,#64] \n" + "pld [%0,#96] \n" + "pld [%1,#96] \n" + "pld [%2,#96] \n" + "vld1.f32 {q0,q1}, [%0,:128]! \n" + "vld1.f32 {q4,q5}, [%1,:128]! \n" + "vld1.f32 {q2,q3}, [%0,:128]! \n" + "vld1.f32 {q6,q7}, [%1,:128]! \n" + "vld1.f32 {q8,q9}, [r8,:128]! \n" + + "vmul.f32 q10, q0, q4 \n" + "vmul.f32 q11, q0, q5 \n" + "vmul.f32 q12, q2, q6 \n" + "vmul.f32 q13, q2, q7 \n" + "vmls.f32 q10, q1, q5 \n" + "vmla.f32 q11, q1, q4 \n" + "vld1.f32 {q0,q1}, [r8,:128]! \n" + "vmls.f32 q12, q3, q7 \n" + "vmla.f32 q13, q3, q6 \n" + "vmla.f32 q8, q10, q15 \n" + "vmla.f32 q9, q11, q15 \n" + "vmla.f32 q0, q12, q15 \n" + "vmla.f32 q1, q13, q15 \n" + "vst1.f32 {q8,q9},[%2,:128]! \n" + "vst1.f32 {q0,q1},[%2,:128]! \n" + "subs %3, #2 \n" + "bne 1b \n" + : "+r"(a_), "+r"(b_), "+r"(ab_), "+r"(N) : "r"(scaling) : "r8", "q0","q1","q2","q3","q4","q5","q6","q7","q8","q9", "q10","q11","q12","q13","q15","memory"); + +#else + + /* Default routine, works fine for non-arm cpus with current compilers. */ + const v4sf vscal{ld_ps1(scaling)}; + for(size_t i{0};i < Ncvec;i += 2) + { + v4sf ar4{va[2*i+0]}, ai4{va[2*i+1]}; + v4sf br4{vb[2*i+0]}, bi4{vb[2*i+1]}; + vcplxmul(ar4, ai4, br4, bi4); + vab[2*i+0] = vmadd(ar4, vscal, vab[2*i+0]); + vab[2*i+1] = vmadd(ai4, vscal, vab[2*i+1]); + ar4 = va[2*i+2]; ai4 = va[2*i+3]; + br4 = vb[2*i+2]; bi4 = vb[2*i+3]; + vcplxmul(ar4, ai4, br4, bi4); + vab[2*i+2] = vmadd(ar4, vscal, vab[2*i+2]); + vab[2*i+3] = vmadd(ai4, vscal, vab[2*i+3]); + } +#endif + + if(s->transform == PFFFT_REAL) + { + vab[0] = vinsert0(vab[0], abr1 + ar1*br1*scaling); + vab[1] = vinsert0(vab[1], abi1 + ai1*bi1*scaling); + } +} + +void pffft_zconvolve_accumulate(const PFFFT_Setup *s, const float *a, const float *b, float *ab) +{ + const size_t Ncvec{s->Ncvec}; + const v4sf *RESTRICT va{reinterpret_cast(a)}; + const v4sf *RESTRICT vb{reinterpret_cast(b)}; + v4sf *RESTRICT vab{reinterpret_cast(ab)}; + +#ifdef __arm__ + __builtin_prefetch(va); + __builtin_prefetch(vb); + __builtin_prefetch(vab); + __builtin_prefetch(va+2); + __builtin_prefetch(vb+2); + __builtin_prefetch(vab+2); + __builtin_prefetch(va+4); + __builtin_prefetch(vb+4); + __builtin_prefetch(vab+4); + __builtin_prefetch(va+6); + __builtin_prefetch(vb+6); + __builtin_prefetch(vab+6); +#endif + + const float ar1{vextract0(va[0])}; + const float ai1{vextract0(va[1])}; + const float br1{vextract0(vb[0])}; + const float bi1{vextract0(vb[1])}; + const float abr1{vextract0(vab[0])}; + const float abi1{vextract0(vab[1])}; + + /* No inline assembly for this version. I'm not familiar enough with NEON + * assembly, and I don't know that it's needed with today's optimizers. + */ + for(size_t i{0};i < Ncvec;i += 2) + { + v4sf ar4{va[2*i+0]}, ai4{va[2*i+1]}; + v4sf br4{vb[2*i+0]}, bi4{vb[2*i+1]}; + vcplxmul(ar4, ai4, br4, bi4); + vab[2*i+0] = vadd(ar4, vab[2*i+0]); + vab[2*i+1] = vadd(ai4, vab[2*i+1]); + ar4 = va[2*i+2]; ai4 = va[2*i+3]; + br4 = vb[2*i+2]; bi4 = vb[2*i+3]; + vcplxmul(ar4, ai4, br4, bi4); + vab[2*i+2] = vadd(ar4, vab[2*i+2]); + vab[2*i+3] = vadd(ai4, vab[2*i+3]); + } + + if(s->transform == PFFFT_REAL) + { + vab[0] = vinsert0(vab[0], abr1 + ar1*br1); + vab[1] = vinsert0(vab[1], abi1 + ai1*bi1); + } +} + + +void pffft_transform(const PFFFT_Setup *setup, const float *input, float *output, float *work, + pffft_direction_t direction) +{ + assert(valigned(input) && valigned(output) && valigned(work)); + pffft_transform_internal(setup, reinterpret_cast(al::assume_aligned<16>(input)), + reinterpret_cast(al::assume_aligned<16>(output)), + reinterpret_cast(al::assume_aligned<16>(work)), direction, false); +} + +void pffft_transform_ordered(const PFFFT_Setup *setup, const float *input, float *output, + float *work, pffft_direction_t direction) +{ + assert(valigned(input) && valigned(output) && valigned(work)); + pffft_transform_internal(setup, reinterpret_cast(al::assume_aligned<16>(input)), + reinterpret_cast(al::assume_aligned<16>(output)), + reinterpret_cast(al::assume_aligned<16>(work)), direction, true); +} + +#else // defined(PFFFT_SIMD_DISABLE) + +// standard routine using scalar floats, without SIMD stuff. + +namespace { + +void pffft_transform_internal(const PFFFT_Setup *setup, const float *input, float *output, + float *scratch, const pffft_direction_t direction, bool ordered) +{ + const size_t Ncvec{setup->Ncvec}; + const bool nf_odd{(setup->ifac[1]&1) != 0}; + + assert(scratch != nullptr); + + /* z-domain data for complex transforms is already ordered without SIMD. */ + if(setup->transform == PFFFT_COMPLEX) + ordered = false; + + float *buff[2]{output, scratch}; + bool ib{nf_odd != ordered}; + if(direction == PFFFT_FORWARD) + { + if(setup->transform == PFFFT_REAL) + ib = (rfftf1_ps(Ncvec*2, input, buff[ib], buff[!ib], setup->twiddle, setup->ifac) == buff[1]); + else + ib = (cfftf1_ps(Ncvec, input, buff[ib], buff[!ib], setup->twiddle, setup->ifac, -1.0f) == buff[1]); + if(ordered) + { + pffft_zreorder(setup, buff[ib], buff[!ib], PFFFT_FORWARD); + ib = !ib; + } + } + else + { + if (input == buff[ib]) + ib = !ib; // may happen when finput == foutput + + if(ordered) + { + pffft_zreorder(setup, input, buff[ib], PFFFT_BACKWARD); + input = buff[ib]; + ib = !ib; + } + if(setup->transform == PFFFT_REAL) + ib = (rfftb1_ps(Ncvec*2, input, buff[ib], buff[!ib], setup->twiddle, setup->ifac) == buff[1]); + else + ib = (cfftf1_ps(Ncvec, input, buff[ib], buff[!ib], setup->twiddle, setup->ifac, +1.0f) == buff[1]); + } + if(buff[ib] != output) + { + // extra copy required -- this situation should happens only when finput == foutput + assert(input==output); + for(size_t k{0};k < Ncvec;++k) + { + float a{buff[ib][2*k]}, b{buff[ib][2*k+1]}; + output[2*k] = a; output[2*k+1] = b; + } + } +} + +} // namespace + +void pffft_zreorder(const PFFFT_Setup *setup, const float *in, float *RESTRICT out, + pffft_direction_t direction) +{ + const size_t N{setup->N}; + if(setup->transform == PFFFT_COMPLEX) + { + for(size_t k{0};k < 2*N;++k) + out[k] = in[k]; + return; + } + else if(direction == PFFFT_FORWARD) + { + float x_N{in[N-1]}; + for(size_t k{N-1};k > 1;--k) + out[k] = in[k-1]; + out[0] = in[0]; + out[1] = x_N; + } + else + { + float x_N{in[1]}; + for(size_t k{1};k < N-1;++k) + out[k] = in[k+1]; + out[0] = in[0]; + out[N-1] = x_N; + } +} + +void pffft_zconvolve_scale_accumulate(const PFFFT_Setup *s, const float *a, const float *b, + float *ab, float scaling) +{ + size_t Ncvec{s->Ncvec}; + + if(s->transform == PFFFT_REAL) + { + // take care of the fftpack ordering + ab[0] += a[0]*b[0]*scaling; + ab[2*Ncvec-1] += a[2*Ncvec-1]*b[2*Ncvec-1]*scaling; + ++ab; ++a; ++b; --Ncvec; + } + for(size_t i{0};i < Ncvec;++i) + { + float ar{a[2*i+0]}, ai{a[2*i+1]}; + const float br{b[2*i+0]}, bi{b[2*i+1]}; + vcplxmul(ar, ai, br, bi); + ab[2*i+0] += ar*scaling; + ab[2*i+1] += ai*scaling; + } +} + +void pffft_zconvolve_accumulate(const PFFFT_Setup *s, const float *a, const float *b, float *ab) +{ + size_t Ncvec{s->Ncvec}; + + if(s->transform == PFFFT_REAL) + { + // take care of the fftpack ordering + ab[0] += a[0]*b[0]; + ab[2*Ncvec-1] += a[2*Ncvec-1]*b[2*Ncvec-1]; + ++ab; ++a; ++b; --Ncvec; + } + for(size_t i{0};i < Ncvec;++i) + { + float ar{a[2*i+0]}, ai{a[2*i+1]}; + const float br{b[2*i+0]}, bi{b[2*i+1]}; + vcplxmul(ar, ai, br, bi); + ab[2*i+0] += ar; + ab[2*i+1] += ai; + } +} + + +void pffft_transform(const PFFFT_Setup *setup, const float *input, float *output, float *work, + pffft_direction_t direction) +{ + pffft_transform_internal(setup, input, output, work, direction, false); +} + +void pffft_transform_ordered(const PFFFT_Setup *setup, const float *input, float *output, + float *work, pffft_direction_t direction) +{ + pffft_transform_internal(setup, input, output, work, direction, true); +} + +#endif /* defined(PFFFT_SIMD_DISABLE) */ +/* NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) */ diff --git a/3rdparty/openal/common/pffft.h b/3rdparty/openal/common/pffft.h new file mode 100644 index 000000000000..ac7e2940ac86 --- /dev/null +++ b/3rdparty/openal/common/pffft.h @@ -0,0 +1,212 @@ +/* Copyright (c) 2013 Julien Pommier ( pommier@modartt.com ) + + Based on original fortran 77 code from FFTPACKv4 from NETLIB, + authored by Dr Paul Swarztrauber of NCAR, in 1985. + + As confirmed by the NCAR fftpack software curators, the following + FFTPACKv5 license applies to FFTPACKv4 sources. My changes are + released under the same terms. + + FFTPACK license: + + http://www.cisl.ucar.edu/css/software/fftpack5/ftpk.html + + Copyright (c) 2004 the University Corporation for Atmospheric + Research ("UCAR"). All rights reserved. Developed by NCAR's + Computational and Information Systems Laboratory, UCAR, + www.cisl.ucar.edu. + + Redistribution and use of the Software in source and binary forms, + with or without modification, is permitted provided that the + following conditions are met: + + - Neither the names of NCAR's Computational and Information Systems + Laboratory, the University Corporation for Atmospheric Research, + nor the names of its sponsors or contributors may be used to + endorse or promote products derived from this Software without + specific prior written permission. + + - Redistributions of source code must retain the above copyright + notices, this list of conditions, and the disclaimer below. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions, and the disclaimer below in the + documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE + SOFTWARE. +*/ + +/* PFFFT : a Pretty Fast FFT. + * + * This is basically an adaptation of the single precision fftpack (v4) as + * found on netlib taking advantage of SIMD instructions found on CPUs such as + * Intel x86 (SSE1), PowerPC (Altivec), and Arm (NEON). + * + * For architectures where SIMD instructions aren't available, the code falls + * back to a scalar version. + * + * Restrictions: + * + * - 1D transforms only, with 32-bit single precision. + * + * - supports only transforms for inputs of length N of the form + * N=(2^a)*(3^b)*(5^c), given a >= 5, b >=0, c >= 0 (32, 48, 64, 96, 128, 144, + * 160, etc are all acceptable lengths). Performance is best for 128<=N<=8192. + * + * - all (float*) pointers for the functions below are expected to have a + * "SIMD-compatible" alignment, that is 16 bytes. + * + * You can allocate such buffers with the pffft_aligned_malloc function, and + * deallocate them with pffft_aligned_free (or with stuff like posix_memalign, + * aligned_alloc, etc). + * + * Note that for the z-domain data of real transforms, when in the canonical + * order (as interleaved complex numbers) both 0-frequency and half-frequency + * components, which are real, are assembled in the first entry as + * F(0)+i*F(n/2+1). The original fftpack placed F(n/2+1) at the end of the + * arrays instead. + */ + +#ifndef PFFFT_H +#define PFFFT_H + +#include +#include + +#include "almalloc.h" + + +/* opaque struct holding internal stuff (precomputed twiddle factors) this + * struct can be shared by many threads as it contains only read-only data. + */ +struct PFFFT_Setup; + +/* direction of the transform */ +enum pffft_direction_t { PFFFT_FORWARD, PFFFT_BACKWARD }; + +/* type of transform */ +enum pffft_transform_t { PFFFT_REAL, PFFFT_COMPLEX }; + +void pffft_destroy_setup(gsl::owner setup) noexcept; +struct PFFFTSetupDeleter { + void operator()(gsl::owner setup) const noexcept { pffft_destroy_setup(setup); } +}; +using PFFFTSetupPtr = std::unique_ptr; + +/** + * Prepare for performing transforms of size N -- the returned PFFFT_Setup + * structure is read-only so it can safely be shared by multiple concurrent + * threads. + */ +PFFFTSetupPtr pffft_new_setup(unsigned int N, pffft_transform_t transform); + +/** + * Perform a Fourier transform. The z-domain data is stored in the most + * efficient order for transforming back or using for convolution, and as + * such, there's no guarantee to the order of the values. If you need to have + * its content sorted in the usual way, that is as an array of interleaved + * complex numbers, either use pffft_transform_ordered, or call pffft_zreorder + * after the forward fft and before the backward fft. + * + * Transforms are not scaled: PFFFT_BACKWARD(PFFFT_FORWARD(x)) = N*x. Typically + * you will want to scale the backward transform by 1/N. + * + * The 'work' pointer must point to an area of N (2*N for complex fft) floats, + * properly aligned. It cannot be NULL. + * + * The input and output parameters may alias. + */ +void pffft_transform(const PFFFT_Setup *setup, const float *input, float *output, float *work, pffft_direction_t direction); + +/** + * Similar to pffft_transform, but handles the complex values in the usual form + * (interleaved complex numbers). This is similar to calling + * pffft_transform(..., PFFFT_FORWARD) followed by + * pffft_zreorder(..., PFFFT_FORWARD), or + * pffft_zreorder(..., PFFFT_BACKWARD) followed by + * pffft_transform(..., PFFFT_BACKWARD), for the given direction. + * + * The input and output parameters may alias. + */ +void pffft_transform_ordered(const PFFFT_Setup *setup, const float *input, float *output, float *work, pffft_direction_t direction); + +/** + * Reorder the z-domain data. For PFFFT_FORWARD, it reorders from the internal + * representation to the "canonical" order (as interleaved complex numbers). + * For PFFFT_BACKWARD, it reorders from the canonical order to the internal + * order suitable for pffft_transform(..., PFFFT_BACKWARD) or + * pffft_zconvolve_accumulate. + * + * The input and output parameters should not alias. + */ +void pffft_zreorder(const PFFFT_Setup *setup, const float *input, float *output, pffft_direction_t direction); + +/** + * Perform a multiplication of the z-domain data in dft_a and dft_b, and scale + * and accumulate into dft_ab. The arrays should have been obtained with + * pffft_transform(..., PFFFT_FORWARD) or pffft_zreorder(..., PFFFT_BACKWARD) + * and should *not* be in the usual order (otherwise just perform the operation + * yourself as the dft coeffs are stored as interleaved complex numbers). + * + * The operation performed is: dft_ab += (dft_a * dft_b)*scaling + * + * The dft_a, dft_b, and dft_ab parameters may alias. + */ +void pffft_zconvolve_scale_accumulate(const PFFFT_Setup *setup, const float *dft_a, const float *dft_b, float *dft_ab, float scaling); + +/** + * Perform a multiplication of the z-domain data in dft_a and dft_b, and + * accumulate into dft_ab. + * + * The operation performed is: dft_ab += dft_a * dft_b + * + * The dft_a, dft_b, and dft_ab parameters may alias. + */ +void pffft_zconvolve_accumulate(const PFFFT_Setup *setup, const float *dft_a, const float *dft_b, float *dft_ab); + + +struct PFFFTSetup { + PFFFTSetupPtr mSetup; + + PFFFTSetup() = default; + PFFFTSetup(const PFFFTSetup&) = delete; + PFFFTSetup(PFFFTSetup&& rhs) noexcept = default; + explicit PFFFTSetup(std::nullptr_t) noexcept { } + explicit PFFFTSetup(unsigned int n, pffft_transform_t transform) + : mSetup{pffft_new_setup(n, transform)} + { } + ~PFFFTSetup() = default; + + PFFFTSetup& operator=(const PFFFTSetup&) = delete; + PFFFTSetup& operator=(PFFFTSetup&& rhs) noexcept = default; + + [[nodiscard]] explicit operator bool() const noexcept { return mSetup != nullptr; } + + void transform(const float *input, float *output, float *work, pffft_direction_t direction) const + { pffft_transform(mSetup.get(), input, output, work, direction); } + + void transform_ordered(const float *input, float *output, float *work, + pffft_direction_t direction) const + { pffft_transform_ordered(mSetup.get(), input, output, work, direction); } + + void zreorder(const float *input, float *output, pffft_direction_t direction) const + { pffft_zreorder(mSetup.get(), input, output, direction); } + + void zconvolve_scale_accumulate(const float *dft_a, const float *dft_b, float *dft_ab, + float scaling) const + { pffft_zconvolve_scale_accumulate(mSetup.get(), dft_a, dft_b, dft_ab, scaling); } + + void zconvolve_accumulate(const float *dft_a, const float *dft_b, float *dft_ab) const + { pffft_zconvolve_accumulate(mSetup.get(), dft_a, dft_b, dft_ab); } +}; + +#endif // PFFFT_H diff --git a/3rdparty/openal/common/phase_shifter.h b/3rdparty/openal/common/phase_shifter.h index 061e91769813..5ff867400121 100644 --- a/3rdparty/openal/common/phase_shifter.h +++ b/3rdparty/openal/common/phase_shifter.h @@ -1,96 +1,60 @@ #ifndef PHASE_SHIFTER_H #define PHASE_SHIFTER_H -#ifdef HAVE_SSE_INTRINSICS +#include "config_simd.h" + +#if HAVE_SSE_INTRINSICS #include -#elif defined(HAVE_NEON) +#elif HAVE_NEON #include #endif #include -#include +#include +#include -#include "alcomplex.h" +#include "alnumbers.h" #include "alspan.h" +#include "opthelpers.h" /* Implements a wide-band +90 degree phase-shift. Note that this should be * given one sample less of a delay (FilterSize/2 - 1) compared to the direct * signal delay (FilterSize/2) to properly align. */ -template -struct PhaseShifterT { +template +struct SIMDALIGN PhaseShifterT { static_assert(FilterSize >= 16, "FilterSize needs to be at least 16"); static_assert((FilterSize&(FilterSize-1)) == 0, "FilterSize needs to be power-of-two"); alignas(16) std::array mCoeffs{}; - /* Some notes on this filter construction. - * - * A wide-band phase-shift filter needs a delay to maintain linearity. A - * dirac impulse in the center of a time-domain buffer represents a filter - * passing all frequencies through as-is with a pure delay. Converting that - * to the frequency domain, adjusting the phase of each frequency bin by - * +90 degrees, then converting back to the time domain, results in a FIR - * filter that applies a +90 degree wide-band phase-shift. - * - * A particularly notable aspect of the time-domain filter response is that - * every other coefficient is 0. This allows doubling the effective size of - * the filter, by storing only the non-0 coefficients and double-stepping - * over the input to apply it. - * - * Additionally, the resulting filter is independent of the sample rate. - * The same filter can be applied regardless of the device's sample rate - * and achieve the same effect. - */ PhaseShifterT() { - using complex_d = std::complex; - constexpr size_t fft_size{FilterSize}; - constexpr size_t half_size{fft_size / 2}; - - auto fftBuffer = std::make_unique(fft_size); - std::fill_n(fftBuffer.get(), fft_size, complex_d{}); - fftBuffer[half_size] = 1.0; - - forward_fft(al::span{fftBuffer.get(), fft_size}); - for(size_t i{0};i < half_size+1;++i) - fftBuffer[i] = complex_d{-fftBuffer[i].imag(), fftBuffer[i].real()}; - for(size_t i{half_size+1};i < fft_size;++i) - fftBuffer[i] = std::conj(fftBuffer[fft_size - i]); - inverse_fft(al::span{fftBuffer.get(), fft_size}); - - auto fftiter = fftBuffer.get() + half_size + (FilterSize/2 - 1); - for(float &coeff : mCoeffs) + /* Every other coefficient is 0, so we only need to calculate and store + * the non-0 terms and double-step over the input to apply it. The + * calculated coefficients are in reverse to make applying in the time- + * domain more efficient. + */ + for(std::size_t i{0};i < FilterSize/2;++i) { - coeff = static_cast(fftiter->real() / double{fft_size}); - fftiter -= 2; + const int k{static_cast(i*2 + 1) - int{FilterSize/2}}; + + /* Calculate the Blackman window value for this coefficient. */ + const double w{2.0*al::numbers::pi * static_cast(i*2 + 1) + / double{FilterSize}}; + const double window{0.3635819 - 0.4891775*std::cos(w) + 0.1365995*std::cos(2.0*w) + - 0.0106411*std::cos(3.0*w)}; + + const double pk{al::numbers::pi * static_cast(k)}; + mCoeffs[i] = static_cast(window * (1.0-std::cos(pk)) / pk); } } - void process(al::span dst, const float *RESTRICT src) const; + void process(const al::span dst, const al::span src) const; private: -#if defined(HAVE_NEON) - /* There doesn't seem to be NEON intrinsics to do this kind of stipple - * shuffling, so there's two custom methods for it. - */ - static auto shuffle_2020(float32x4_t a, float32x4_t b) - { - float32x4_t ret{vmovq_n_f32(vgetq_lane_f32(a, 0))}; - ret = vsetq_lane_f32(vgetq_lane_f32(a, 2), ret, 1); - ret = vsetq_lane_f32(vgetq_lane_f32(b, 0), ret, 2); - ret = vsetq_lane_f32(vgetq_lane_f32(b, 2), ret, 3); - return ret; - } - static auto shuffle_3131(float32x4_t a, float32x4_t b) - { - float32x4_t ret{vmovq_n_f32(vgetq_lane_f32(a, 1))}; - ret = vsetq_lane_f32(vgetq_lane_f32(a, 3), ret, 1); - ret = vsetq_lane_f32(vgetq_lane_f32(b, 1), ret, 2); - ret = vsetq_lane_f32(vgetq_lane_f32(b, 3), ret, 3); - return ret; - } +#if HAVE_NEON static auto unpacklo(float32x4_t a, float32x4_t b) { float32x2x2_t result{vzip_f32(vget_low_f32(a), vget_low_f32(b))}; @@ -109,105 +73,141 @@ struct PhaseShifterT { ret = vsetq_lane_f32(d, ret, 3); return ret; } + static void vtranspose4(float32x4_t &x0, float32x4_t &x1, float32x4_t &x2, float32x4_t &x3) + { + float32x4x2_t t0_{vzipq_f32(x0, x2)}; + float32x4x2_t t1_{vzipq_f32(x1, x3)}; + float32x4x2_t u0_{vzipq_f32(t0_.val[0], t1_.val[0])}; + float32x4x2_t u1_{vzipq_f32(t0_.val[1], t1_.val[1])}; + x0 = u0_.val[0]; + x1 = u0_.val[1]; + x2 = u1_.val[0]; + x3 = u1_.val[1]; + } #endif }; -template -inline void PhaseShifterT::process(al::span dst, const float *RESTRICT src) const +template +NOINLINE inline +void PhaseShifterT::process(const al::span dst, const al::span src) const { -#ifdef HAVE_SSE_INTRINSICS - if(size_t todo{dst.size()>>1}) + auto in = src.begin(); +#if HAVE_SSE_INTRINSICS + if(const std::size_t todo{dst.size()>>2}) { - auto *out = reinterpret_cast<__m64*>(dst.data()); - do { - __m128 r04{_mm_setzero_ps()}; - __m128 r14{_mm_setzero_ps()}; - for(size_t j{0};j < mCoeffs.size();j+=4) + auto out = al::span{reinterpret_cast<__m128*>(dst.data()), todo}; + std::generate(out.begin(), out.end(), [&in,this] + { + __m128 r0{_mm_setzero_ps()}; + __m128 r1{_mm_setzero_ps()}; + __m128 r2{_mm_setzero_ps()}; + __m128 r3{_mm_setzero_ps()}; + for(std::size_t j{0};j < mCoeffs.size();j+=4) { const __m128 coeffs{_mm_load_ps(&mCoeffs[j])}; - const __m128 s0{_mm_loadu_ps(&src[j*2])}; - const __m128 s1{_mm_loadu_ps(&src[j*2 + 4])}; + const __m128 s0{_mm_loadu_ps(&in[j*2])}; + const __m128 s1{_mm_loadu_ps(&in[j*2 + 4])}; + const __m128 s2{_mm_movehl_ps(_mm_movelh_ps(s1, s1), s0)}; + const __m128 s3{_mm_loadh_pi(_mm_movehl_ps(s1, s1), + reinterpret_cast(&in[j*2 + 8]))}; __m128 s{_mm_shuffle_ps(s0, s1, _MM_SHUFFLE(2, 0, 2, 0))}; - r04 = _mm_add_ps(r04, _mm_mul_ps(s, coeffs)); + r0 = _mm_add_ps(r0, _mm_mul_ps(s, coeffs)); s = _mm_shuffle_ps(s0, s1, _MM_SHUFFLE(3, 1, 3, 1)); - r14 = _mm_add_ps(r14, _mm_mul_ps(s, coeffs)); - } - src += 2; + r1 = _mm_add_ps(r1, _mm_mul_ps(s, coeffs)); - __m128 r4{_mm_add_ps(_mm_unpackhi_ps(r04, r14), _mm_unpacklo_ps(r04, r14))}; - r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4)); + s = _mm_shuffle_ps(s2, s3, _MM_SHUFFLE(2, 0, 2, 0)); + r2 = _mm_add_ps(r2, _mm_mul_ps(s, coeffs)); + + s = _mm_shuffle_ps(s2, s3, _MM_SHUFFLE(3, 1, 3, 1)); + r3 = _mm_add_ps(r3, _mm_mul_ps(s, coeffs)); + } + in += 4; - _mm_storel_pi(out, r4); - ++out; - } while(--todo); + _MM_TRANSPOSE4_PS(r0, r1, r2, r3); + return _mm_add_ps(_mm_add_ps(r0, r1), _mm_add_ps(r2, r3)); + }); } - if((dst.size()&1)) + if(const std::size_t todo{dst.size()&3}) { - __m128 r4{_mm_setzero_ps()}; - for(size_t j{0};j < mCoeffs.size();j+=4) + auto out = dst.last(todo); + std::generate(out.begin(), out.end(), [&in,this] { - const __m128 coeffs{_mm_load_ps(&mCoeffs[j])}; - const __m128 s{_mm_setr_ps(src[j*2], src[j*2 + 2], src[j*2 + 4], src[j*2 + 6])}; - r4 = _mm_add_ps(r4, _mm_mul_ps(s, coeffs)); - } - r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3))); - r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4)); - - dst.back() = _mm_cvtss_f32(r4); + __m128 r4{_mm_setzero_ps()}; + for(std::size_t j{0};j < mCoeffs.size();j+=4) + { + const __m128 coeffs{_mm_load_ps(&mCoeffs[j])}; + const __m128 s{_mm_setr_ps(in[j*2], in[j*2 + 2], in[j*2 + 4], in[j*2 + 6])}; + r4 = _mm_add_ps(r4, _mm_mul_ps(s, coeffs)); + } + ++in; + r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3))); + r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4)); + return _mm_cvtss_f32(r4); + }); } -#elif defined(HAVE_NEON) +#elif HAVE_NEON - size_t pos{0}; - if(size_t todo{dst.size()>>1}) + if(const std::size_t todo{dst.size()>>2}) { - do { - float32x4_t r04{vdupq_n_f32(0.0f)}; - float32x4_t r14{vdupq_n_f32(0.0f)}; - for(size_t j{0};j < mCoeffs.size();j+=4) + auto out = al::span{reinterpret_cast(dst.data()), todo}; + std::generate(out.begin(), out.end(), [&in,this] + { + float32x4_t r0{vdupq_n_f32(0.0f)}; + float32x4_t r1{vdupq_n_f32(0.0f)}; + float32x4_t r2{vdupq_n_f32(0.0f)}; + float32x4_t r3{vdupq_n_f32(0.0f)}; + for(std::size_t j{0};j < mCoeffs.size();j+=4) { const float32x4_t coeffs{vld1q_f32(&mCoeffs[j])}; - const float32x4_t s0{vld1q_f32(&src[j*2])}; - const float32x4_t s1{vld1q_f32(&src[j*2 + 4])}; - - r04 = vmlaq_f32(r04, shuffle_2020(s0, s1), coeffs); - r14 = vmlaq_f32(r14, shuffle_3131(s0, s1), coeffs); + const float32x4_t s0{vld1q_f32(&in[j*2])}; + const float32x4_t s1{vld1q_f32(&in[j*2 + 4])}; + const float32x4_t s2{vcombine_f32(vget_high_f32(s0), vget_low_f32(s1))}; + const float32x4_t s3{vcombine_f32(vget_high_f32(s1), vld1_f32(&in[j*2 + 8]))}; + const float32x4x2_t values0{vuzpq_f32(s0, s1)}; + const float32x4x2_t values1{vuzpq_f32(s2, s3)}; + + r0 = vmlaq_f32(r0, values0.val[0], coeffs); + r1 = vmlaq_f32(r1, values0.val[1], coeffs); + r2 = vmlaq_f32(r2, values1.val[0], coeffs); + r3 = vmlaq_f32(r3, values1.val[1], coeffs); } - src += 2; + in += 4; - float32x4_t r4{vaddq_f32(unpackhi(r04, r14), unpacklo(r04, r14))}; - float32x2_t r2{vadd_f32(vget_low_f32(r4), vget_high_f32(r4))}; - - vst1_f32(&dst[pos], r2); - pos += 2; - } while(--todo); + vtranspose4(r0, r1, r2, r3); + return vaddq_f32(vaddq_f32(r0, r1), vaddq_f32(r2, r3)); + }); } - if((dst.size()&1)) + if(const std::size_t todo{dst.size()&3}) { - float32x4_t r4{vdupq_n_f32(0.0f)}; - for(size_t j{0};j < mCoeffs.size();j+=4) + auto out = dst.last(todo); + std::generate(out.begin(), out.end(), [&in,this] { - const float32x4_t coeffs{vld1q_f32(&mCoeffs[j])}; - const float32x4_t s{load4(src[j*2], src[j*2 + 2], src[j*2 + 4], src[j*2 + 6])}; - r4 = vmlaq_f32(r4, s, coeffs); - } - r4 = vaddq_f32(r4, vrev64q_f32(r4)); - dst[pos] = vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0); + float32x4_t r4{vdupq_n_f32(0.0f)}; + for(std::size_t j{0};j < mCoeffs.size();j+=4) + { + const float32x4_t coeffs{vld1q_f32(&mCoeffs[j])}; + const float32x4_t s{load4(in[j*2], in[j*2 + 2], in[j*2 + 4], in[j*2 + 6])}; + r4 = vmlaq_f32(r4, s, coeffs); + } + ++in; + r4 = vaddq_f32(r4, vrev64q_f32(r4)); + return vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0); + }); } #else - for(float &output : dst) + std::generate(dst.begin(), dst.end(), [&in,this] { float ret{0.0f}; - for(size_t j{0};j < mCoeffs.size();++j) - ret += src[j*2] * mCoeffs[j]; - - output = ret; - ++src; - } + for(std::size_t j{0};j < mCoeffs.size();++j) + ret += in[j*2] * mCoeffs[j]; + ++in; + return ret; + }); #endif } diff --git a/3rdparty/openal/common/polyphase_resampler.cpp b/3rdparty/openal/common/polyphase_resampler.cpp index 14f7e40d877a..d74ecdff6910 100644 --- a/3rdparty/openal/common/polyphase_resampler.cpp +++ b/3rdparty/openal/common/polyphase_resampler.cpp @@ -3,45 +3,46 @@ #include #include +#include +#include +#include #include "alnumbers.h" #include "opthelpers.h" +using uint = unsigned int; + namespace { constexpr double Epsilon{1e-9}; -using uint = unsigned int; - -/* This is the normalized cardinal sine (sinc) function. - * - * sinc(x) = { 1, x = 0 - * { sin(pi x) / (pi x), otherwise. - */ -double Sinc(const double x) -{ - if(std::abs(x) < Epsilon) UNLIKELY - return 1.0; - return std::sin(al::numbers::pi*x) / (al::numbers::pi*x); -} /* The zero-order modified Bessel function of the first kind, used for the * Kaiser window. * * I_0(x) = sum_{k=0}^inf (1 / k!)^2 (x / 2)^(2 k) * = sum_{k=0}^inf ((x / 2)^k / k!)^2 + * + * This implementation only handles nu = 0, and isn't the most precise (it + * starts with the largest value and accumulates successively smaller values, + * compounding the rounding and precision error), but it's good enough. */ -constexpr double BesselI_0(const double x) +template +constexpr auto cyl_bessel_i(T nu, U x) -> U { - // Start at k=1 since k=0 is trivial. + if(nu != T{0}) + throw std::runtime_error{"cyl_bessel_i: nu != 0"}; + + /* Start at k=1 since k=0 is trivial. */ const double x2{x/2.0}; double term{1.0}; double sum{1.0}; int k{1}; - // Let the integration converge until the term of the sum is no longer - // significant. + /* Let the integration converge until the term of the sum is no longer + * significant. + */ double last_sum{}; do { const double y{x2 / k}; @@ -50,7 +51,19 @@ constexpr double BesselI_0(const double x) term *= y * y; sum += term; } while(sum != last_sum); - return sum; + return static_cast(sum); +} + +/* This is the normalized cardinal sine (sinc) function. + * + * sinc(x) = { 1, x = 0 + * { sin(pi x) / (pi x), otherwise. + */ +double Sinc(const double x) +{ + if(std::abs(x) < Epsilon) UNLIKELY + return 1.0; + return std::sin(al::numbers::pi*x) / (al::numbers::pi*x); } /* Calculate a Kaiser window from the given beta value and a normalized k @@ -67,23 +80,11 @@ constexpr double BesselI_0(const double x) * * k = 2 i / M - 1, where 0 <= i <= M. */ -double Kaiser(const double b, const double k) +double Kaiser(const double beta, const double k, const double besseli_0_beta) { if(!(k >= -1.0 && k <= 1.0)) return 0.0; - return BesselI_0(b * std::sqrt(1.0 - k*k)) / BesselI_0(b); -} - -// Calculates the greatest common divisor of a and b. -constexpr uint Gcd(uint x, uint y) -{ - while(y > 0) - { - const uint z{y}; - y = x % y; - x = z; - } - return x; + return ::cyl_bessel_i(0, beta * std::sqrt(1.0 - k*k)) / besseli_0_beta; } /* Calculates the size (order) of the Kaiser window. Rejection is in dB and @@ -124,11 +125,11 @@ constexpr double CalcKaiserBeta(const double rejection) * p -- gain compensation factor when sampling * f_t -- normalized center frequency (or cutoff; 0.5 is nyquist) */ -double SincFilter(const uint l, const double b, const double gain, const double cutoff, - const uint i) +double SincFilter(const uint l, const double beta, const double besseli_0_beta, const double gain, + const double cutoff, const uint i) { const double x{static_cast(i) - l}; - return Kaiser(b, x / l) * 2.0 * gain * cutoff * Sinc(2.0 * cutoff * x); + return Kaiser(beta, x/l, besseli_0_beta) * 2.0 * gain * cutoff * Sinc(2.0 * cutoff * x); } } // namespace @@ -137,7 +138,7 @@ double SincFilter(const uint l, const double b, const double gain, const double // that's used to cut frequencies above the destination nyquist. void PPhaseResampler::init(const uint srcRate, const uint dstRate) { - const uint gcd{Gcd(srcRate, dstRate)}; + const uint gcd{std::gcd(srcRate, dstRate)}; mP = dstRate / gcd; mQ = srcRate / gcd; @@ -145,78 +146,70 @@ void PPhaseResampler::init(const uint srcRate, const uint dstRate) * ends before the nyquist (0.5). Both are scaled by the downsampling * factor. */ - double cutoff, width; - if(mP > mQ) - { - cutoff = 0.475 / mP; - width = 0.05 / mP; - } - else - { - cutoff = 0.475 / mQ; - width = 0.05 / mQ; - } + const auto [cutoff, width] = (mP > mQ) ? std::make_tuple(0.475 / mP, 0.05 / mP) + : std::make_tuple(0.475 / mQ, 0.05 / mQ); + // A rejection of -180 dB is used for the stop band. Round up when // calculating the left offset to avoid increasing the transition width. const uint l{(CalcKaiserOrder(180.0, width)+1) / 2}; const double beta{CalcKaiserBeta(180.0)}; + const double besseli_0_beta{::cyl_bessel_i(0, beta)}; mM = l*2 + 1; mL = l; mF.resize(mM); for(uint i{0};i < mM;i++) - mF[i] = SincFilter(l, beta, mP, cutoff, i); + mF[i] = SincFilter(l, beta, besseli_0_beta, mP, cutoff, i); } // Perform the upsample-filter-downsample resampling operation using a // polyphase filter implementation. -void PPhaseResampler::process(const uint inN, const double *in, const uint outN, double *out) +void PPhaseResampler::process(const al::span in, const al::span out) { - if(outN == 0) UNLIKELY + if(out.empty()) UNLIKELY return; // Handle in-place operation. std::vector workspace; - double *work{out}; - if(work == in) UNLIKELY + al::span work{out}; + if(work.data() == in.data()) UNLIKELY { - workspace.resize(outN); - work = workspace.data(); + workspace.resize(out.size()); + work = workspace; } // Resample the input. const uint p{mP}, q{mQ}, m{mM}, l{mL}; - const double *f{mF.data()}; - for(uint i{0};i < outN;i++) + const al::span f{mF}; + for(uint i{0};i < out.size();i++) { // Input starts at l to compensate for the filter delay. This will // drop any build-up from the first half of the filter. - size_t j_f{(l + q*i) % p}; - size_t j_s{(l + q*i) / p}; + std::size_t j_f{(l + q*i) % p}; + std::size_t j_s{(l + q*i) / p}; - // Only take input when 0 <= j_s < inN. + // Only take input when 0 <= j_s < in.size(). double r{0.0}; if(j_f < m) LIKELY { - size_t filt_len{(m-j_f+p-1) / p}; - if(j_s+1 > inN) LIKELY + std::size_t filt_len{(m-j_f+p-1) / p}; + if(j_s+1 > in.size()) LIKELY { - size_t skip{std::min(j_s+1 - inN, filt_len)}; + std::size_t skip{std::min(j_s+1 - in.size(), filt_len)}; j_f += p*skip; j_s -= skip; filt_len -= skip; } - if(size_t todo{std::min(j_s+1, filt_len)}) LIKELY + std::size_t todo{std::min(j_s+1, filt_len)}; + while(todo) { - do { - r += f[j_f] * in[j_s]; - j_f += p; - --j_s; - } while(--todo); + r += f[j_f] * in[j_s]; + j_f += p; --j_s; + --todo; } } work[i] = r; } // Clean up after in-place operation. - if(work != out) - std::copy_n(work, outN, out); + if(work.data() != out.data()) + std::copy(work.cbegin(), work.cend(), out.begin()); } diff --git a/3rdparty/openal/common/polyphase_resampler.h b/3rdparty/openal/common/polyphase_resampler.h index 557485bb2f61..0795c80d897e 100644 --- a/3rdparty/openal/common/polyphase_resampler.h +++ b/3rdparty/openal/common/polyphase_resampler.h @@ -3,6 +3,8 @@ #include +#include "alspan.h" + using uint = unsigned int; @@ -35,12 +37,12 @@ using uint = unsigned int; struct PPhaseResampler { void init(const uint srcRate, const uint dstRate); - void process(const uint inN, const double *in, const uint outN, double *out); + void process(const al::span in, const al::span out); explicit operator bool() const noexcept { return !mF.empty(); } private: - uint mP, mQ, mM, mL; + uint mP{}, mQ{}, mM{}, mL{}; std::vector mF; }; diff --git a/3rdparty/openal/common/ringbuffer.cpp b/3rdparty/openal/common/ringbuffer.cpp index af1f36695e93..23c48806a6cd 100644 --- a/3rdparty/openal/common/ringbuffer.cpp +++ b/3rdparty/openal/common/ringbuffer.cpp @@ -23,201 +23,151 @@ #include "ringbuffer.h" #include -#include +#include +#include +#include #include -#include "almalloc.h" +#include "alnumeric.h" +#include "alspan.h" -RingBufferPtr RingBuffer::Create(std::size_t sz, std::size_t elem_sz, int limit_writes) +auto RingBuffer::Create(std::size_t sz, std::size_t elem_sz, bool limit_writes) -> RingBufferPtr { std::size_t power_of_two{0u}; if(sz > 0) { - power_of_two = sz; + power_of_two = sz - 1; power_of_two |= power_of_two>>1; power_of_two |= power_of_two>>2; power_of_two |= power_of_two>>4; power_of_two |= power_of_two>>8; power_of_two |= power_of_two>>16; - if constexpr(SIZE_MAX > UINT_MAX) + if constexpr(sizeof(size_t) > sizeof(uint32_t)) power_of_two |= power_of_two>>32; } ++power_of_two; - if(power_of_two <= sz || power_of_two > std::numeric_limits::max()/elem_sz) + if(power_of_two < sz || power_of_two > std::numeric_limits::max()>>1 + || power_of_two > std::numeric_limits::max()/elem_sz) throw std::overflow_error{"Ring buffer size overflow"}; const std::size_t bufbytes{power_of_two * elem_sz}; - RingBufferPtr rb{new(FamCount(bufbytes)) RingBuffer{bufbytes}}; - rb->mWriteSize = limit_writes ? sz : (power_of_two-1); - rb->mSizeMask = power_of_two - 1; - rb->mElemSize = elem_sz; + RingBufferPtr rb{new(FamCount(bufbytes)) RingBuffer{limit_writes ? sz : power_of_two, + power_of_two-1, elem_sz, bufbytes}}; return rb; } void RingBuffer::reset() noexcept { - mWritePtr.store(0, std::memory_order_relaxed); - mReadPtr.store(0, std::memory_order_relaxed); + mWriteCount.store(0, std::memory_order_relaxed); + mReadCount.store(0, std::memory_order_relaxed); std::fill_n(mBuffer.begin(), (mSizeMask+1)*mElemSize, std::byte{}); } -std::size_t RingBuffer::read(void *dest, std::size_t cnt) noexcept +auto RingBuffer::read(void *dest, std::size_t count) noexcept -> std::size_t { - const std::size_t free_cnt{readSpace()}; - if(free_cnt == 0) return 0; + const std::size_t w{mWriteCount.load(std::memory_order_acquire)}; + const std::size_t r{mReadCount.load(std::memory_order_relaxed)}; + const std::size_t readable{w - r}; + if(readable == 0) return 0; - const std::size_t to_read{std::min(cnt, free_cnt)}; - std::size_t read_ptr{mReadPtr.load(std::memory_order_relaxed) & mSizeMask}; + const std::size_t to_read{std::min(count, readable)}; + const std::size_t read_idx{r & mSizeMask}; - std::size_t n1, n2; - const std::size_t cnt2{read_ptr + to_read}; - if(cnt2 > mSizeMask+1) - { - n1 = mSizeMask+1 - read_ptr; - n2 = cnt2 & mSizeMask; - } - else - { - n1 = to_read; - n2 = 0; - } + const std::size_t rdend{read_idx + to_read}; + const auto [n1, n2] = (rdend <= mSizeMask+1) ? std::array{to_read, 0_uz} + : std::array{mSizeMask+1 - read_idx, rdend&mSizeMask}; - auto outiter = std::copy_n(mBuffer.begin() + read_ptr*mElemSize, n1*mElemSize, - static_cast(dest)); - read_ptr += n1; + auto dstbytes = al::span{static_cast(dest), count*mElemSize}; + auto outiter = std::copy_n(mBuffer.begin() + ptrdiff_t(read_idx*mElemSize), n1*mElemSize, + dstbytes.begin()); if(n2 > 0) - { std::copy_n(mBuffer.begin(), n2*mElemSize, outiter); - read_ptr += n2; - } - mReadPtr.store(read_ptr, std::memory_order_release); + mReadCount.store(r+n1+n2, std::memory_order_release); return to_read; } -std::size_t RingBuffer::peek(void *dest, std::size_t cnt) const noexcept +auto RingBuffer::peek(void *dest, std::size_t count) const noexcept -> std::size_t { - const std::size_t free_cnt{readSpace()}; - if(free_cnt == 0) return 0; + const std::size_t w{mWriteCount.load(std::memory_order_acquire)}; + const std::size_t r{mReadCount.load(std::memory_order_relaxed)}; + const std::size_t readable{w - r}; + if(readable == 0) return 0; - const std::size_t to_read{std::min(cnt, free_cnt)}; - std::size_t read_ptr{mReadPtr.load(std::memory_order_relaxed) & mSizeMask}; + const std::size_t to_read{std::min(count, readable)}; + const std::size_t read_idx{r & mSizeMask}; - std::size_t n1, n2; - const std::size_t cnt2{read_ptr + to_read}; - if(cnt2 > mSizeMask+1) - { - n1 = mSizeMask+1 - read_ptr; - n2 = cnt2 & mSizeMask; - } - else - { - n1 = to_read; - n2 = 0; - } + const std::size_t rdend{read_idx + to_read}; + const auto [n1, n2] = (rdend <= mSizeMask+1) ? std::array{to_read, 0_uz} + : std::array{mSizeMask+1 - read_idx, rdend&mSizeMask}; - auto outiter = std::copy_n(mBuffer.begin() + read_ptr*mElemSize, n1*mElemSize, - static_cast(dest)); + auto dstbytes = al::span{static_cast(dest), count*mElemSize}; + auto outiter = std::copy_n(mBuffer.begin() + ptrdiff_t(read_idx*mElemSize), n1*mElemSize, + dstbytes.begin()); if(n2 > 0) std::copy_n(mBuffer.begin(), n2*mElemSize, outiter); return to_read; } -std::size_t RingBuffer::write(const void *src, std::size_t cnt) noexcept +auto RingBuffer::write(const void *src, std::size_t count) noexcept -> std::size_t { - const std::size_t free_cnt{writeSpace()}; - if(free_cnt == 0) return 0; + const std::size_t w{mWriteCount.load(std::memory_order_relaxed)}; + const std::size_t r{mReadCount.load(std::memory_order_acquire)}; + const std::size_t writable{mWriteSize - (w - r)}; + if(writable == 0) return 0; - const std::size_t to_write{std::min(cnt, free_cnt)}; - std::size_t write_ptr{mWritePtr.load(std::memory_order_relaxed) & mSizeMask}; + const std::size_t to_write{std::min(count, writable)}; + const std::size_t write_idx{w & mSizeMask}; - std::size_t n1, n2; - const std::size_t cnt2{write_ptr + to_write}; - if(cnt2 > mSizeMask+1) - { - n1 = mSizeMask+1 - write_ptr; - n2 = cnt2 & mSizeMask; - } - else - { - n1 = to_write; - n2 = 0; - } + const std::size_t wrend{write_idx + to_write}; + const auto [n1, n2] = (wrend <= mSizeMask+1) ? std::array{to_write, 0_uz} + : std::array{mSizeMask+1 - write_idx, wrend&mSizeMask}; - auto srcbytes = static_cast(src); - std::copy_n(srcbytes, n1*mElemSize, mBuffer.begin() + write_ptr*mElemSize); - write_ptr += n1; + auto srcbytes = al::span{static_cast(src), count*mElemSize}; + std::copy_n(srcbytes.cbegin(), n1*mElemSize, mBuffer.begin() + ptrdiff_t(write_idx*mElemSize)); if(n2 > 0) - { - std::copy_n(srcbytes + n1*mElemSize, n2*mElemSize, mBuffer.begin()); - write_ptr += n2; - } - mWritePtr.store(write_ptr, std::memory_order_release); + std::copy_n(srcbytes.cbegin() + ptrdiff_t(n1*mElemSize), n2*mElemSize, mBuffer.begin()); + mWriteCount.store(w+n1+n2, std::memory_order_release); return to_write; } -auto RingBuffer::getReadVector() const noexcept -> DataPair +auto RingBuffer::getReadVector() noexcept -> DataPair { - DataPair ret; - - std::size_t w{mWritePtr.load(std::memory_order_acquire)}; - std::size_t r{mReadPtr.load(std::memory_order_acquire)}; - w &= mSizeMask; - r &= mSizeMask; - const std::size_t free_cnt{(w-r) & mSizeMask}; + const std::size_t w{mWriteCount.load(std::memory_order_acquire)}; + const std::size_t r{mReadCount.load(std::memory_order_relaxed)}; + const std::size_t readable{w - r}; + const std::size_t read_idx{r & mSizeMask}; - const std::size_t cnt2{r + free_cnt}; - if(cnt2 > mSizeMask+1) + const std::size_t rdend{read_idx + readable}; + if(rdend > mSizeMask+1) { /* Two part vector: the rest of the buffer after the current read ptr, - * plus some from the start of the buffer. */ - ret.first.buf = const_cast(mBuffer.data() + r*mElemSize); - ret.first.len = mSizeMask+1 - r; - ret.second.buf = const_cast(mBuffer.data()); - ret.second.len = cnt2 & mSizeMask; - } - else - { - /* Single part vector: just the rest of the buffer */ - ret.first.buf = const_cast(mBuffer.data() + r*mElemSize); - ret.first.len = free_cnt; - ret.second.buf = nullptr; - ret.second.len = 0; + * plus some from the start of the buffer. + */ + return DataPair{{{mBuffer.data() + read_idx*mElemSize, mSizeMask+1 - read_idx}, + {mBuffer.data(), rdend&mSizeMask}}}; } - - return ret; + return DataPair{{{mBuffer.data() + read_idx*mElemSize, readable}, {}}}; } -auto RingBuffer::getWriteVector() const noexcept -> DataPair +auto RingBuffer::getWriteVector() noexcept -> DataPair { - DataPair ret; - - std::size_t w{mWritePtr.load(std::memory_order_acquire)}; - std::size_t r{mReadPtr.load(std::memory_order_acquire) + mWriteSize - mSizeMask}; - w &= mSizeMask; - r &= mSizeMask; - const std::size_t free_cnt{(r-w-1) & mSizeMask}; + const std::size_t w{mWriteCount.load(std::memory_order_relaxed)}; + const std::size_t r{mReadCount.load(std::memory_order_acquire)}; + const std::size_t writable{mWriteSize - (w - r)}; + const std::size_t write_idx{w & mSizeMask}; - const std::size_t cnt2{w + free_cnt}; - if(cnt2 > mSizeMask+1) + const std::size_t wrend{write_idx + writable}; + if(wrend > mSizeMask+1) { /* Two part vector: the rest of the buffer after the current write ptr, - * plus some from the start of the buffer. */ - ret.first.buf = const_cast(mBuffer.data() + w*mElemSize); - ret.first.len = mSizeMask+1 - w; - ret.second.buf = const_cast(mBuffer.data()); - ret.second.len = cnt2 & mSizeMask; + * plus some from the start of the buffer. + */ + return DataPair{{{mBuffer.data() + write_idx*mElemSize, mSizeMask+1 - write_idx}, + {mBuffer.data(), wrend&mSizeMask}}}; } - else - { - ret.first.buf = const_cast(mBuffer.data() + w*mElemSize); - ret.first.len = free_cnt; - ret.second.buf = nullptr; - ret.second.len = 0; - } - - return ret; + return DataPair{{{mBuffer.data() + write_idx*mElemSize, writable}, {}}}; } diff --git a/3rdparty/openal/common/ringbuffer.h b/3rdparty/openal/common/ringbuffer.h index 8c65c3af9ac8..59b0a0ac7d86 100644 --- a/3rdparty/openal/common/ringbuffer.h +++ b/3rdparty/openal/common/ringbuffer.h @@ -2,26 +2,35 @@ #define RINGBUFFER_H #include +#include #include #include #include #include "almalloc.h" +#include "flexarray.h" /* NOTE: This lockless ringbuffer implementation is copied from JACK, extended * to include an element size. Consequently, parameters and return values for a - * size or count is in 'elements', not bytes. Additionally, it only supports + * size or count are in 'elements', not bytes. Additionally, it only supports * single-consumer/single-provider operation. */ struct RingBuffer { private: - std::atomic mWritePtr{0u}; - std::atomic mReadPtr{0u}; - std::size_t mWriteSize{0u}; - std::size_t mSizeMask{0u}; - std::size_t mElemSize{0u}; +#if defined(__cpp_lib_hardware_interference_size) && !defined(_LIBCPP_VERSION) + static constexpr std::size_t sCacheAlignment{std::hardware_destructive_interference_size}; +#else + /* Assume a 64-byte cache line, the most common/likely value. */ + static constexpr std::size_t sCacheAlignment{64}; +#endif + alignas(sCacheAlignment) std::atomic mWriteCount{0u}; + alignas(sCacheAlignment) std::atomic mReadCount{0u}; + + alignas(sCacheAlignment) const std::size_t mWriteSize; + const std::size_t mSizeMask; + const std::size_t mElemSize; al::FlexArray mBuffer; @@ -30,82 +39,95 @@ struct RingBuffer { std::byte *buf; std::size_t len; }; - using DataPair = std::pair; + using DataPair = std::array; - - RingBuffer(const std::size_t count) : mBuffer{count} { } + RingBuffer(const std::size_t writesize, const std::size_t mask, const std::size_t elemsize, + const std::size_t numbytes) + : mWriteSize{writesize}, mSizeMask{mask}, mElemSize{elemsize}, mBuffer{numbytes} + { } /** Reset the read and write pointers to zero. This is not thread safe. */ - void reset() noexcept; - - /** - * The non-copying data reader. Returns two ringbuffer data pointers that - * hold the current readable data. If the readable data is in one segment - * the second segment has zero length. - */ - DataPair getReadVector() const noexcept; - /** - * The non-copying data writer. Returns two ringbuffer data pointers that - * hold the current writeable data. If the writeable data is in one segment - * the second segment has zero length. - */ - DataPair getWriteVector() const noexcept; + auto reset() noexcept -> void; /** * Return the number of elements available for reading. This is the number * of elements in front of the read pointer and behind the write pointer. */ - std::size_t readSpace() const noexcept + [[nodiscard]] auto readSpace() const noexcept -> std::size_t { - const size_t w{mWritePtr.load(std::memory_order_acquire)}; - const size_t r{mReadPtr.load(std::memory_order_acquire)}; - return (w-r) & mSizeMask; + const std::size_t w{mWriteCount.load(std::memory_order_acquire)}; + const std::size_t r{mReadCount.load(std::memory_order_acquire)}; + /* mWriteCount is never more than mWriteSize greater than mReadCount. */ + return w - r; } /** - * The copying data reader. Copy at most `cnt' elements into `dest'. + * The copying data reader. Copy at most `count' elements into `dest'. * Returns the actual number of elements copied. */ - std::size_t read(void *dest, std::size_t cnt) noexcept; + [[nodiscard]] auto read(void *dest, std::size_t count) noexcept -> std::size_t; /** - * The copying data reader w/o read pointer advance. Copy at most `cnt' + * The copying data reader w/o read pointer advance. Copy at most `count' * elements into `dest'. Returns the actual number of elements copied. */ - std::size_t peek(void *dest, std::size_t cnt) const noexcept; - /** Advance the read pointer `cnt' places. */ - void readAdvance(std::size_t cnt) noexcept - { mReadPtr.fetch_add(cnt, std::memory_order_acq_rel); } - + [[nodiscard]] auto peek(void *dest, std::size_t count) const noexcept -> std::size_t; /** - * Return the number of elements available for writing. This is the number - * of elements in front of the write pointer and behind the read pointer. + * The non-copying data reader. Returns two ringbuffer data pointers that + * hold the current readable data. If the readable data is in one segment + * the second segment has zero length. */ - std::size_t writeSpace() const noexcept + [[nodiscard]] auto getReadVector() noexcept -> DataPair; + /** Advance the read pointer `count' places. */ + auto readAdvance(std::size_t count) noexcept -> void { - const size_t w{mWritePtr.load(std::memory_order_acquire)}; - const size_t r{mReadPtr.load(std::memory_order_acquire) + mWriteSize - mSizeMask}; - return (r-w-1) & mSizeMask; + const std::size_t w{mWriteCount.load(std::memory_order_acquire)}; + const std::size_t r{mReadCount.load(std::memory_order_relaxed)}; + [[maybe_unused]] const std::size_t readable{w - r}; + assert(readable >= count); + mReadCount.store(r+count, std::memory_order_release); } + /** - * The copying data writer. Copy at most `cnt' elements from `src'. Returns + * Return the number of elements available for writing. This is the total + * number of writable elements excluding what's readable (already written). + */ + [[nodiscard]] auto writeSpace() const noexcept -> std::size_t + { return mWriteSize - readSpace(); } + + /** + * The copying data writer. Copy at most `count' elements from `src'. Returns * the actual number of elements copied. */ - std::size_t write(const void *src, std::size_t cnt) noexcept; - /** Advance the write pointer `cnt' places. */ - void writeAdvance(std::size_t cnt) noexcept - { mWritePtr.fetch_add(cnt, std::memory_order_acq_rel); } + [[nodiscard]] auto write(const void *src, std::size_t count) noexcept -> std::size_t; + + /** + * The non-copying data writer. Returns two ringbuffer data pointers that + * hold the current writeable data. If the writeable data is in one segment + * the second segment has zero length. + */ + [[nodiscard]] auto getWriteVector() noexcept -> DataPair; + /** Advance the write pointer `count' places. */ + auto writeAdvance(std::size_t count) noexcept -> void + { + const std::size_t w{mWriteCount.load(std::memory_order_relaxed)}; + const std::size_t r{mReadCount.load(std::memory_order_acquire)}; + [[maybe_unused]] const std::size_t writable{mWriteSize - (w - r)}; + assert(writable >= count); + mWriteCount.store(w+count, std::memory_order_release); + } - std::size_t getElemSize() const noexcept { return mElemSize; } + [[nodiscard]] auto getElemSize() const noexcept -> std::size_t { return mElemSize; } /** * Create a new ringbuffer to hold at least `sz' elements of `elem_sz' - * bytes. The number of elements is rounded up to the next power of two - * (even if it is already a power of two, to ensure the requested amount - * can be written). + * bytes. The number of elements is rounded up to a power of two. If + * `limit_writes' is true, the writable space will be limited to `sz' + * elements regardless of the rounded size. */ - static std::unique_ptr Create(std::size_t sz, std::size_t elem_sz, int limit_writes); + [[nodiscard]] static + auto Create(std::size_t sz, std::size_t elem_sz, bool limit_writes) -> std::unique_ptr; DEF_FAM_NEWDEL(RingBuffer, mBuffer) }; diff --git a/3rdparty/openal/common/strutils.cpp b/3rdparty/openal/common/strutils.cpp index f7868e2e147b..5b1b6af19073 100644 --- a/3rdparty/openal/common/strutils.cpp +++ b/3rdparty/openal/common/strutils.cpp @@ -5,21 +5,23 @@ #include - #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include +#include "alstring.h" + +/* NOLINTBEGIN(bugprone-suspicious-stringview-data-usage) */ std::string wstr_to_utf8(std::wstring_view wstr) { std::string ret; - int len{WideCharToMultiByte(CP_UTF8, 0, wstr.data(), static_cast(wstr.length()), nullptr, - 0, nullptr, nullptr)}; + const int len{WideCharToMultiByte(CP_UTF8, 0, wstr.data(), al::sizei(wstr), nullptr, 0, + nullptr, nullptr)}; if(len > 0) { - ret.resize(len); - WideCharToMultiByte(CP_UTF8, 0, wstr.data(), static_cast(wstr.length()), &ret[0], len, + ret.resize(static_cast(len)); + WideCharToMultiByte(CP_UTF8, 0, wstr.data(), al::sizei(wstr), ret.data(), len, nullptr, nullptr); } @@ -30,16 +32,16 @@ std::wstring utf8_to_wstr(std::string_view str) { std::wstring ret; - int len{MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast(str.length()), nullptr, - 0)}; + const int len{MultiByteToWideChar(CP_UTF8, 0, str.data(), al::sizei(str), nullptr, 0)}; if(len > 0) { - ret.resize(len); - MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast(str.length()), &ret[0], len); + ret.resize(static_cast(len)); + MultiByteToWideChar(CP_UTF8, 0, str.data(), al::sizei(str), ret.data(), len); } return ret; } +/* NOLINTEND(bugprone-suspicious-stringview-data-usage) */ #endif namespace al { @@ -51,7 +53,7 @@ std::optional getenv(const char *envname) #else const char *str{std::getenv(envname)}; #endif - if(str && str[0] != '\0') + if(str && *str != '\0') return str; return std::nullopt; } @@ -60,7 +62,7 @@ std::optional getenv(const char *envname) std::optional getenv(const WCHAR *envname) { const WCHAR *str{_wgetenv(envname)}; - if(str && str[0] != L'\0') + if(str && *str != L'\0') return str; return std::nullopt; } diff --git a/3rdparty/openal/common/strutils.h b/3rdparty/openal/common/strutils.h index 7eee0c1d1269..3644847c2bca 100644 --- a/3rdparty/openal/common/strutils.h +++ b/3rdparty/openal/common/strutils.h @@ -5,8 +5,8 @@ #include #ifdef _WIN32 +#include #include -#include std::string wstr_to_utf8(std::wstring_view wstr); std::wstring utf8_to_wstr(std::string_view str); diff --git a/3rdparty/openal/common/vecmat.h b/3rdparty/openal/common/vecmat.h index a45f262f6ded..7ac6afe32701 100644 --- a/3rdparty/openal/common/vecmat.h +++ b/3rdparty/openal/common/vecmat.h @@ -1,6 +1,7 @@ #ifndef COMMON_VECMAT_H #define COMMON_VECMAT_H +#include #include #include #include @@ -11,22 +12,24 @@ namespace alu { -template -class VectorR { - static_assert(std::is_floating_point::value, "Must use floating-point types"); - alignas(16) T mVals[4]; +class Vector { + alignas(16) std::array mVals{}; public: - constexpr VectorR() noexcept = default; - constexpr VectorR(const VectorR&) noexcept = default; - constexpr explicit VectorR(T a, T b, T c, T d) noexcept : mVals{a, b, c, d} { } + constexpr Vector() noexcept = default; + constexpr Vector(const Vector&) noexcept = default; + constexpr Vector(Vector&&) noexcept = default; + constexpr explicit Vector(float a, float b, float c, float d) noexcept : mVals{{a,b,c,d}} { } - constexpr VectorR& operator=(const VectorR&) noexcept = default; + constexpr auto operator=(const Vector&) noexcept -> Vector& = default; + constexpr auto operator=(Vector&&) noexcept -> Vector& = default; - constexpr T& operator[](size_t idx) noexcept { return mVals[idx]; } - constexpr const T& operator[](size_t idx) const noexcept { return mVals[idx]; } + [[nodiscard]] constexpr + auto operator[](std::size_t idx) noexcept -> float& { return mVals[idx]; } + [[nodiscard]] constexpr + auto operator[](std::size_t idx) const noexcept -> const float& { return mVals[idx]; } - constexpr VectorR& operator+=(const VectorR &rhs) noexcept + constexpr auto operator+=(const Vector &rhs) noexcept -> Vector& { mVals[0] += rhs.mVals[0]; mVals[1] += rhs.mVals[1]; @@ -35,85 +38,84 @@ class VectorR { return *this; } - constexpr VectorR operator-(const VectorR &rhs) const noexcept + [[nodiscard]] constexpr + auto operator-(const Vector &rhs) const noexcept -> Vector { - return VectorR{mVals[0] - rhs.mVals[0], mVals[1] - rhs.mVals[1], + return Vector{mVals[0] - rhs.mVals[0], mVals[1] - rhs.mVals[1], mVals[2] - rhs.mVals[2], mVals[3] - rhs.mVals[3]}; } - constexpr T normalize(T limit = std::numeric_limits::epsilon()) + constexpr auto normalize() -> float { - limit = std::max(limit, std::numeric_limits::epsilon()); - const T length_sqr{mVals[0]*mVals[0] + mVals[1]*mVals[1] + mVals[2]*mVals[2]}; - if(length_sqr > limit*limit) + const auto length_sqr = float{mVals[0]*mVals[0] + mVals[1]*mVals[1] + mVals[2]*mVals[2]}; + if(length_sqr > std::numeric_limits::epsilon()) { - const T length{std::sqrt(length_sqr)}; - T inv_length{T{1}/length}; + const auto length = std::sqrt(length_sqr); + auto inv_length = float{1.0f / length}; mVals[0] *= inv_length; mVals[1] *= inv_length; mVals[2] *= inv_length; return length; } - mVals[0] = mVals[1] = mVals[2] = T{0}; - return T{0}; + mVals[0] = mVals[1] = mVals[2] = 0.0f; + return 0.0f; } - constexpr VectorR cross_product(const alu::VectorR &rhs) const noexcept + [[nodiscard]] constexpr auto cross_product(const Vector &rhs) const noexcept -> Vector { - return VectorR{ + return Vector{ mVals[1]*rhs.mVals[2] - mVals[2]*rhs.mVals[1], mVals[2]*rhs.mVals[0] - mVals[0]*rhs.mVals[2], mVals[0]*rhs.mVals[1] - mVals[1]*rhs.mVals[0], - T{0}}; + 0.0f}; } - constexpr T dot_product(const alu::VectorR &rhs) const noexcept + [[nodiscard]] constexpr auto dot_product(const Vector &rhs) const noexcept -> float { return mVals[0]*rhs.mVals[0] + mVals[1]*rhs.mVals[1] + mVals[2]*rhs.mVals[2]; } }; -using Vector = VectorR; -template -class MatrixR { - static_assert(std::is_floating_point::value, "Must use floating-point types"); - alignas(16) T mVals[16]; +class Matrix { + alignas(16) std::array mVals{}; public: - constexpr MatrixR() noexcept = default; - constexpr MatrixR(const MatrixR&) noexcept = default; - constexpr explicit MatrixR( - T aa, T ab, T ac, T ad, - T ba, T bb, T bc, T bd, - T ca, T cb, T cc, T cd, - T da, T db, T dc, T dd) noexcept - : mVals{aa,ab,ac,ad, ba,bb,bc,bd, ca,cb,cc,cd, da,db,dc,dd} + constexpr Matrix() noexcept = default; + constexpr Matrix(const Matrix&) noexcept = default; + constexpr Matrix(Matrix&&) noexcept = default; + constexpr explicit Matrix( + float aa, float ab, float ac, float ad, + float ba, float bb, float bc, float bd, + float ca, float cb, float cc, float cd, + float da, float db, float dc, float dd) noexcept + : mVals{{aa,ab,ac,ad, ba,bb,bc,bd, ca,cb,cc,cd, da,db,dc,dd}} { } - constexpr MatrixR& operator=(const MatrixR&) noexcept = default; + constexpr auto operator=(const Matrix&) noexcept -> Matrix& = default; + constexpr auto operator=(Matrix&&) noexcept -> Matrix& = default; - constexpr auto operator[](size_t idx) noexcept { return al::span{&mVals[idx*4], 4}; } - constexpr auto operator[](size_t idx) const noexcept - { return al::span{&mVals[idx*4], 4}; } + [[nodiscard]] constexpr auto operator[](std::size_t idx) noexcept + { return al::span{&mVals[idx*4], 4}; } + [[nodiscard]] constexpr auto operator[](std::size_t idx) const noexcept + { return al::span{&mVals[idx*4], 4}; } - static constexpr MatrixR Identity() noexcept + [[nodiscard]] static constexpr auto Identity() noexcept -> Matrix { - return MatrixR{ - T{1}, T{0}, T{0}, T{0}, - T{0}, T{1}, T{0}, T{0}, - T{0}, T{0}, T{1}, T{0}, - T{0}, T{0}, T{0}, T{1}}; + return Matrix{ + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + } + + [[nodiscard]] friend constexpr + auto operator*(const Matrix &mtx, const Vector &vec) noexcept -> Vector + { + return Vector{ + vec[0]*mtx[0][0] + vec[1]*mtx[1][0] + vec[2]*mtx[2][0] + vec[3]*mtx[3][0], + vec[0]*mtx[0][1] + vec[1]*mtx[1][1] + vec[2]*mtx[2][1] + vec[3]*mtx[3][1], + vec[0]*mtx[0][2] + vec[1]*mtx[1][2] + vec[2]*mtx[2][2] + vec[3]*mtx[3][2], + vec[0]*mtx[0][3] + vec[1]*mtx[1][3] + vec[2]*mtx[2][3] + vec[3]*mtx[3][3]}; } }; -using Matrix = MatrixR; - -template -constexpr VectorR operator*(const MatrixR &mtx, const VectorR &vec) noexcept -{ - return VectorR{ - vec[0]*mtx[0][0] + vec[1]*mtx[1][0] + vec[2]*mtx[2][0] + vec[3]*mtx[3][0], - vec[0]*mtx[0][1] + vec[1]*mtx[1][1] + vec[2]*mtx[2][1] + vec[3]*mtx[3][1], - vec[0]*mtx[0][2] + vec[1]*mtx[1][2] + vec[2]*mtx[2][2] + vec[3]*mtx[3][2], - vec[0]*mtx[0][3] + vec[1]*mtx[1][3] + vec[2]*mtx[2][3] + vec[3]*mtx[3][3]}; -} } // namespace alu diff --git a/3rdparty/openal/common/vector.h b/3rdparty/openal/common/vector.h index 1b69d6a77fea..364735c67da4 100644 --- a/3rdparty/openal/common/vector.h +++ b/3rdparty/openal/common/vector.h @@ -1,13 +1,14 @@ #ifndef AL_VECTOR_H #define AL_VECTOR_H +#include #include #include "almalloc.h" namespace al { -template +template using vector = std::vector>; } // namespace al diff --git a/3rdparty/openal/config.h.in b/3rdparty/openal/config.h.in index 20df5b467079..fb61fcf284ca 100644 --- a/3rdparty/openal/config.h.in +++ b/3rdparty/openal/config.h.in @@ -1,93 +1,18 @@ /* Define the alignment attribute for externally callable functions. */ #define FORCE_ALIGN @ALSOFT_FORCE_ALIGN@ -/* Define if deprecated EAX extensions are enabled */ -#cmakedefine ALSOFT_EAX - /* Define if HRTF data is embedded in the library */ #cmakedefine ALSOFT_EMBED_HRTF_DATA -/* Define if we have the posix_memalign function */ -#cmakedefine HAVE_POSIX_MEMALIGN - -/* Define if we have the _aligned_malloc function */ -#cmakedefine HAVE__ALIGNED_MALLOC - /* Define if we have the proc_pidpath function */ #cmakedefine HAVE_PROC_PIDPATH -/* Define if we have the getopt function */ -#cmakedefine HAVE_GETOPT - -/* Define if we have DBus/RTKit */ -#cmakedefine HAVE_RTKIT - -/* Define if we have SSE CPU extensions */ -#cmakedefine HAVE_SSE -#cmakedefine HAVE_SSE2 -#cmakedefine HAVE_SSE3 -#cmakedefine HAVE_SSE4_1 - -/* Define if we have ARM Neon CPU extensions */ -#cmakedefine HAVE_NEON - -/* Define if we have the ALSA backend */ -#cmakedefine HAVE_ALSA - -/* Define if we have the OSS backend */ -#cmakedefine HAVE_OSS - -/* Define if we have the PipeWire backend */ -#cmakedefine HAVE_PIPEWIRE - -/* Define if we have the Solaris backend */ -#cmakedefine HAVE_SOLARIS - -/* Define if we have the SndIO backend */ -#cmakedefine HAVE_SNDIO - -/* Define if we have the WASAPI backend */ -#cmakedefine HAVE_WASAPI - -/* Define if we have the DSound backend */ -#cmakedefine HAVE_DSOUND - -/* Define if we have the Windows Multimedia backend */ -#cmakedefine HAVE_WINMM - -/* Define if we have the PortAudio backend */ -#cmakedefine HAVE_PORTAUDIO - -/* Define if we have the PulseAudio backend */ -#cmakedefine HAVE_PULSEAUDIO - -/* Define if we have the JACK backend */ -#cmakedefine HAVE_JACK - -/* Define if we have the CoreAudio backend */ -#cmakedefine HAVE_COREAUDIO - -/* Define if we have the OpenSL backend */ -#cmakedefine HAVE_OPENSL - -/* Define if we have the Oboe backend */ -#cmakedefine HAVE_OBOE - -/* Define if we have the Wave Writer backend */ -#cmakedefine HAVE_WAVE - -/* Define if we have the SDL2 backend */ -#cmakedefine HAVE_SDL2 - /* Define if we have dlfcn.h */ #cmakedefine HAVE_DLFCN_H /* Define if we have pthread_np.h */ #cmakedefine HAVE_PTHREAD_NP_H -/* Define if we have malloc.h */ -#cmakedefine HAVE_MALLOC_H - /* Define if we have cpuid.h */ #cmakedefine HAVE_CPUID_H @@ -97,18 +22,12 @@ /* Define if we have guiddef.h */ #cmakedefine HAVE_GUIDDEF_H -/* Define if we have initguid.h */ -#cmakedefine HAVE_INITGUID_H - /* Define if we have GCC's __get_cpuid() */ #cmakedefine HAVE_GCC_GET_CPUID /* Define if we have the __cpuid() intrinsic */ #cmakedefine HAVE_CPUID_INTRINSIC -/* Define if we have SSE intrinsics */ -#cmakedefine HAVE_SSE_INTRINSICS - /* Define if we have pthread_setschedparam() */ #cmakedefine HAVE_PTHREAD_SETSCHEDPARAM @@ -121,5 +40,11 @@ /* Define the installation data directory */ #cmakedefine ALSOFT_INSTALL_DATADIR "@ALSOFT_INSTALL_DATADIR@" -/* Define whether build alsoft for winuwp */ -#cmakedefine ALSOFT_UWP +/* Define to 1 if we have DBus/RTKit, else 0 */ +#cmakedefine01 HAVE_RTKIT + +/* Define to 1 if building for winuwp, else 0 */ +#cmakedefine01 ALSOFT_UWP + +/* Define to 1 if building with legacy EAX API support, else 0 */ +#cmakedefine01 ALSOFT_EAX diff --git a/3rdparty/openal/config_backends.h.in b/3rdparty/openal/config_backends.h.in new file mode 100644 index 000000000000..96eb79ca7c35 --- /dev/null +++ b/3rdparty/openal/config_backends.h.in @@ -0,0 +1,37 @@ +/* Define to 1 if the given backend is enabled, else 0 */ + +#cmakedefine01 HAVE_ALSA + +#cmakedefine01 HAVE_OSS + +#cmakedefine01 HAVE_PIPEWIRE + +#cmakedefine01 HAVE_SOLARIS + +#cmakedefine01 HAVE_SNDIO + +#cmakedefine01 HAVE_WASAPI + +#cmakedefine01 HAVE_DSOUND + +#cmakedefine01 HAVE_WINMM + +#cmakedefine01 HAVE_PORTAUDIO + +#cmakedefine01 HAVE_PULSEAUDIO + +#cmakedefine01 HAVE_JACK + +#cmakedefine01 HAVE_COREAUDIO + +#cmakedefine01 HAVE_OPENSL + +#cmakedefine01 HAVE_OBOE + +#cmakedefine01 HAVE_OTHERIO + +#cmakedefine01 HAVE_WAVE + +#cmakedefine01 HAVE_SDL3 + +#cmakedefine01 HAVE_SDL2 diff --git a/3rdparty/openal/config_simd.h.in b/3rdparty/openal/config_simd.h.in new file mode 100644 index 000000000000..3d284cc7bf1b --- /dev/null +++ b/3rdparty/openal/config_simd.h.in @@ -0,0 +1,10 @@ +/* Define to 1 if we have SSE CPU extensions, else 0 */ +#cmakedefine01 HAVE_SSE +#cmakedefine01 HAVE_SSE2 +#cmakedefine01 HAVE_SSE3 +#cmakedefine01 HAVE_SSE4_1 + +#cmakedefine01 HAVE_SSE_INTRINSICS + +/* Define to 1 if we have ARM Neon CPU extensions, else 0 */ +#cmakedefine01 HAVE_NEON diff --git a/3rdparty/openal/core/ambdec.cpp b/3rdparty/openal/core/ambdec.cpp index f98e1098f012..c1ba4c31b683 100644 --- a/3rdparty/openal/core/ambdec.cpp +++ b/3rdparty/openal/core/ambdec.cpp @@ -8,14 +8,15 @@ #include #include #include +#include #include #include #include #include "albit.h" -#include "alfstream.h" #include "alspan.h" -#include "opthelpers.h" +#include "filesystem.h" +#include "fmt/core.h" namespace { @@ -42,33 +43,13 @@ enum class ReaderScope { HFMatrix, }; -#ifdef __USE_MINGW_ANSI_STDIO -[[gnu::format(gnu_printf,2,3)]] -#else -[[gnu::format(printf,2,3)]] -#endif -std::optional make_error(size_t linenum, const char *fmt, ...) +template +auto make_error(size_t linenum, fmt::format_string fmt, Args&& ...args) + -> std::optional { std::optional ret; - auto &str = ret.emplace(); - - str.resize(256); - int printed{std::snprintf(const_cast(str.data()), str.length(), "Line %zu: ", linenum)}; - if(printed < 0) printed = 0; - auto plen = std::min(static_cast(printed), str.length()); - - std::va_list args, args2; - va_start(args, fmt); - va_copy(args2, args); - const int msglen{std::vsnprintf(&str[plen], str.size()-plen, fmt, args)}; - if(msglen >= 0 && static_cast(msglen) >= str.size()-plen) - { - str.resize(static_cast(msglen) + plen + 1u); - std::vsnprintf(&str[plen], str.size()-plen, fmt, args2); - } - va_end(args2); - va_end(args); - + auto &str = ret.emplace(fmt::format("Line {}: ", linenum)); + str += fmt::format(std::move(fmt), std::forward(args)...); return ret; } @@ -79,7 +60,7 @@ AmbDecConf::~AmbDecConf() = default; std::optional AmbDecConf::load(const char *fname) noexcept { - al::ifstream f{fname}; + fs::ifstream f{fs::u8path(fname)}; if(!f.is_open()) return std::string("Failed to open file \"")+fname+"\""; @@ -102,7 +83,7 @@ std::optional AmbDecConf::load(const char *fname) noexcept if(command == "/}") { if(scope == ReaderScope::Global) - return make_error(linenum, "Unexpected /} in global scope"); + return make_error(linenum, "Unexpected /}} in global scope"); scope = ReaderScope::Global; continue; } @@ -111,7 +92,7 @@ std::optional AmbDecConf::load(const char *fname) noexcept { if(command == "add_spkr") { - if(speaker_pos == NumSpeakers) + if(speaker_pos == Speakers.size()) return make_error(linenum, "Too many speakers specified"); AmbDecConf::SpeakerConf &spkr = Speakers[speaker_pos++]; @@ -122,12 +103,12 @@ std::optional AmbDecConf::load(const char *fname) noexcept istr >> spkr.Connection; } else - return make_error(linenum, "Unexpected speakers command: %s", command.c_str()); + return make_error(linenum, "Unexpected speakers command: {}", command); } else if(scope == ReaderScope::LFMatrix || scope == ReaderScope::HFMatrix) { auto &gains = (scope == ReaderScope::LFMatrix) ? LFOrderGain : HFOrderGain; - auto *matrix = (scope == ReaderScope::LFMatrix) ? LFMatrix : HFMatrix; + auto matrix = (scope == ReaderScope::LFMatrix) ? LFMatrix : HFMatrix; auto &pos = (scope == ReaderScope::LFMatrix) ? lfmatrix_pos : hfmatrix_pos; if(command == "order_gain") @@ -145,7 +126,7 @@ std::optional AmbDecConf::load(const char *fname) noexcept } else if(command == "add_row") { - if(pos == NumSpeakers) + if(pos == Speakers.size()) return make_error(linenum, "Too many matrix rows specified"); unsigned int mask{ChanMask}; @@ -165,7 +146,7 @@ std::optional AmbDecConf::load(const char *fname) noexcept } } else - return make_error(linenum, "Unexpected matrix command: %s", command.c_str()); + return make_error(linenum, "Unexpected matrix command: {}", command); } // Global scope commands else if(command == "/description") @@ -182,7 +163,7 @@ std::optional AmbDecConf::load(const char *fname) noexcept return make_error(linenum, "Duplicate version definition"); istr >> Version; if(Version != 3) - return make_error(linenum, "Unsupported version: %d", Version); + return make_error(linenum, "Unsupported version: {}", Version); } else if(command == "/dec/chan_mask") { @@ -191,7 +172,7 @@ std::optional AmbDecConf::load(const char *fname) noexcept istr >> std::hex >> ChanMask >> std::dec; if(!ChanMask || ChanMask > Ambi4OrderMask) - return make_error(linenum, "Invalid chan_mask: 0x%x", ChanMask); + return make_error(linenum, "Invalid chan_mask: {:#x}", ChanMask); if(ChanMask > Ambi3OrderMask && CoeffScale == AmbDecScale::FuMa) return make_error(linenum, "FuMa not compatible with over third-order"); } @@ -201,16 +182,17 @@ std::optional AmbDecConf::load(const char *fname) noexcept return make_error(linenum, "Duplicate freq_bands"); istr >> FreqBands; if(FreqBands != 1 && FreqBands != 2) - return make_error(linenum, "Invalid freq_bands: %u", FreqBands); + return make_error(linenum, "Invalid freq_bands: {}", FreqBands); } else if(command == "/dec/speakers") { - if(NumSpeakers) + if(!Speakers.empty()) return make_error(linenum, "Duplicate speakers"); - istr >> NumSpeakers; - if(!NumSpeakers) - return make_error(linenum, "Invalid speakers: %zu", NumSpeakers); - Speakers = std::make_unique(NumSpeakers); + size_t numspeakers{}; + istr >> numspeakers; + if(!numspeakers) + return make_error(linenum, "Invalid speakers: {}", numspeakers); + Speakers.resize(numspeakers); } else if(command == "/dec/coeff_scale") { @@ -222,7 +204,7 @@ std::optional AmbDecConf::load(const char *fname) noexcept else if(scale == "sn3d") CoeffScale = AmbDecScale::SN3D; else if(scale == "fuma") CoeffScale = AmbDecScale::FuMa; else - return make_error(linenum, "Unexpected coeff_scale: %s", scale.c_str()); + return make_error(linenum, "Unexpected coeff_scale: {}", scale); if(ChanMask > Ambi3OrderMask && CoeffScale == AmbDecScale::FuMa) return make_error(linenum, "FuMa not compatible with over third-order"); @@ -243,29 +225,29 @@ std::optional AmbDecConf::load(const char *fname) noexcept } else if(command == "/speakers/{") { - if(!NumSpeakers) + if(Speakers.empty()) return make_error(linenum, "Speakers defined without a count"); scope = ReaderScope::Speakers; } else if(command == "/lfmatrix/{" || command == "/hfmatrix/{" || command == "/matrix/{") { - if(!NumSpeakers) + if(Speakers.empty()) return make_error(linenum, "Matrix defined without a speaker count"); if(!ChanMask) return make_error(linenum, "Matrix defined without a channel mask"); - if(!Matrix) + if(Matrix.empty()) { - Matrix = std::make_unique(NumSpeakers * FreqBands); - LFMatrix = Matrix.get(); - HFMatrix = LFMatrix + NumSpeakers*(FreqBands-1); + Matrix.resize(Speakers.size() * FreqBands); + LFMatrix = al::span{Matrix}.first(Speakers.size()); + HFMatrix = al::span{Matrix}.subspan(Speakers.size()*(FreqBands-1)); } if(FreqBands == 1) { if(command != "/matrix/{") - return make_error(linenum, "Unexpected \"%s\" for a single-band decoder", - command.c_str()); + return make_error(linenum, "Unexpected \"{}\" for a single-band decoder", + command); scope = ReaderScope::HFMatrix; } else @@ -275,18 +257,19 @@ std::optional AmbDecConf::load(const char *fname) noexcept else if(command == "/hfmatrix/{") scope = ReaderScope::HFMatrix; else - return make_error(linenum, "Unexpected \"%s\" for a dual-band decoder", - command.c_str()); + return make_error(linenum, "Unexpected \"{}\" for a dual-band decoder", + command); } } else if(command == "/end") { const auto endpos = static_cast(istr.tellg()); if(!is_at_end(buffer, endpos)) - return make_error(linenum, "Extra junk on end: %s", buffer.substr(endpos).c_str()); + return make_error(linenum, "Extra junk on end: {}", + std::string_view{buffer}.substr(endpos)); - if(speaker_pos < NumSpeakers || hfmatrix_pos < NumSpeakers - || (FreqBands == 2 && lfmatrix_pos < NumSpeakers)) + if(speaker_pos < Speakers.size() || hfmatrix_pos < Speakers.size() + || (FreqBands == 2 && lfmatrix_pos < Speakers.size())) return make_error(linenum, "Incomplete decoder definition"); if(CoeffScale == AmbDecScale::Unset) return make_error(linenum, "No coefficient scaling defined"); @@ -294,12 +277,13 @@ std::optional AmbDecConf::load(const char *fname) noexcept return std::nullopt; } else - return make_error(linenum, "Unexpected command: %s", command.c_str()); + return make_error(linenum, "Unexpected command: {}", command); istr.clear(); const auto endpos = static_cast(istr.tellg()); if(!is_at_end(buffer, endpos)) - return make_error(linenum, "Extra junk on line: %s", buffer.substr(endpos).c_str()); + return make_error(linenum, "Extra junk on line: {}", + std::string_view{buffer}.substr(endpos)); buffer.clear(); } return make_error(linenum, "Unexpected end of file"); diff --git a/3rdparty/openal/core/ambdec.h b/3rdparty/openal/core/ambdec.h index 19f68697114c..587a30c666ce 100644 --- a/3rdparty/openal/core/ambdec.h +++ b/3rdparty/openal/core/ambdec.h @@ -5,7 +5,9 @@ #include #include #include +#include +#include "alspan.h" #include "core/ambidefs.h" /* Helpers to read .ambdec configuration files. */ @@ -34,18 +36,17 @@ struct AmbDecConf { float Elevation{0.0f}; std::string Connection; }; - size_t NumSpeakers{0}; - std::unique_ptr Speakers; + std::vector Speakers; using CoeffArray = std::array; - std::unique_ptr Matrix; + std::vector Matrix; /* Unused when FreqBands == 1 */ - float LFOrderGain[MaxAmbiOrder+1]{}; - CoeffArray *LFMatrix; + std::array LFOrderGain{}; + al::span LFMatrix; - float HFOrderGain[MaxAmbiOrder+1]{}; - CoeffArray *HFMatrix; + std::array HFOrderGain{}; + al::span HFMatrix; ~AmbDecConf(); diff --git a/3rdparty/openal/core/ambidefs.cpp b/3rdparty/openal/core/ambidefs.cpp index c1bb5c81e8b6..4a72e34f3808 100644 --- a/3rdparty/openal/core/ambidefs.cpp +++ b/3rdparty/openal/core/ambidefs.cpp @@ -10,7 +10,6 @@ namespace { using AmbiChannelFloatArray = std::array; -constexpr auto inv_sqrt2f = static_cast(1.0/al::numbers::sqrt2); constexpr auto inv_sqrt3f = static_cast(1.0/al::numbers::sqrt3); @@ -21,25 +20,25 @@ constexpr auto inv_sqrt3f = static_cast(1.0/al::numbers::sqrt3); * will result in that channel being subsequently decoded for second-order as * if it was a first-order decoder for that same speaker array. */ -constexpr std::array,MaxAmbiOrder+1> HFScales{{ - {{ 4.000000000e+00f, 2.309401077e+00f, 1.192569588e+00f, 7.189495850e-01f }}, - {{ 4.000000000e+00f, 2.309401077e+00f, 1.192569588e+00f, 7.189495850e-01f }}, - {{ 2.981423970e+00f, 2.309401077e+00f, 1.192569588e+00f, 7.189495850e-01f }}, - {{ 2.359168820e+00f, 2.031565936e+00f, 1.444598386e+00f, 7.189495850e-01f }}, - /* 1.947005434e+00f, 1.764337084e+00f, 1.424707344e+00f, 9.755104127e-01f, 4.784482742e-01f */ -}}; +constexpr std::array HFScales{ + std::array{4.000000000e+00f, 2.309401077e+00f, 1.192569588e+00f, 7.189495850e-01f}, + std::array{4.000000000e+00f, 2.309401077e+00f, 1.192569588e+00f, 7.189495850e-01f}, + std::array{2.981423970e+00f, 2.309401077e+00f, 1.192569588e+00f, 7.189495850e-01f}, + std::array{2.359168820e+00f, 2.031565936e+00f, 1.444598386e+00f, 7.189495850e-01f}, + /*std::array{1.947005434e+00f, 1.764337084e+00f, 1.424707344e+00f, 9.755104127e-01f, 4.784482742e-01f}, */ +}; /* Same as above, but using a 10-point horizontal-only speaker array. Should * only be used when the device is mixing in 2D B-Format for horizontal-only * output. */ -constexpr std::array,MaxAmbiOrder+1> HFScales2D{{ - {{ 2.236067977e+00f, 1.581138830e+00f, 9.128709292e-01f, 6.050756345e-01f }}, - {{ 2.236067977e+00f, 1.581138830e+00f, 9.128709292e-01f, 6.050756345e-01f }}, - {{ 1.825741858e+00f, 1.581138830e+00f, 9.128709292e-01f, 6.050756345e-01f }}, - {{ 1.581138830e+00f, 1.460781803e+00f, 1.118033989e+00f, 6.050756345e-01f }}, - /* 1.414213562e+00f, 1.344997024e+00f, 1.144122806e+00f, 8.312538756e-01f, 4.370160244e-01f */ -}}; +constexpr std::array HFScales2D{ + std::array{2.236067977e+00f, 1.581138830e+00f, 9.128709292e-01f, 6.050756345e-01f}, + std::array{2.236067977e+00f, 1.581138830e+00f, 9.128709292e-01f, 6.050756345e-01f}, + std::array{1.825741858e+00f, 1.581138830e+00f, 9.128709292e-01f, 6.050756345e-01f}, + std::array{1.581138830e+00f, 1.460781803e+00f, 1.118033989e+00f, 6.050756345e-01f}, + /*std::array{1.414213562e+00f, 1.344997024e+00f, 1.144122806e+00f, 8.312538756e-01f, 4.370160244e-01f}, */ +}; /* This calculates a first-order "upsampler" matrix. It combines a first-order @@ -49,17 +48,17 @@ constexpr std::array,MaxAmbiOrder+1> HFScales2D * signal. While not perfect, this should accurately encode a lower-order * signal into a higher-order signal. */ -constexpr std::array,8> FirstOrderDecoder{{ - {{ 1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f, }}, - {{ 1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f, }}, - {{ 1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f, }}, - {{ 1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f, }}, - {{ 1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f, }}, - {{ 1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f, }}, - {{ 1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f, }}, - {{ 1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f, }}, -}}; -constexpr std::array FirstOrderEncoder{{ +constexpr std::array FirstOrderDecoder{ + std::array{1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f}, + std::array{1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f}, + std::array{1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f, 1.250000000e-01f}, + std::array{1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f}, + std::array{1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f}, + std::array{1.250000000e-01f, 1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f}, + std::array{1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f, 1.250000000e-01f}, + std::array{1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f, -1.250000000e-01f}, +}; +constexpr std::array FirstOrderEncoder{ CalcAmbiCoeffs( inv_sqrt3f, inv_sqrt3f, inv_sqrt3f), CalcAmbiCoeffs( inv_sqrt3f, inv_sqrt3f, -inv_sqrt3f), CalcAmbiCoeffs(-inv_sqrt3f, inv_sqrt3f, inv_sqrt3f), @@ -68,25 +67,29 @@ constexpr std::array FirstOrderEncoder{{ CalcAmbiCoeffs( inv_sqrt3f, -inv_sqrt3f, -inv_sqrt3f), CalcAmbiCoeffs(-inv_sqrt3f, -inv_sqrt3f, inv_sqrt3f), CalcAmbiCoeffs(-inv_sqrt3f, -inv_sqrt3f, -inv_sqrt3f), -}}; +}; static_assert(FirstOrderDecoder.size() == FirstOrderEncoder.size(), "First-order mismatch"); /* This calculates a 2D first-order "upsampler" matrix. Same as the first-order * matrix, just using a more optimized speaker array for horizontal-only * content. */ -constexpr std::array,4> FirstOrder2DDecoder{{ - {{ 2.500000000e-01f, 2.041241452e-01f, 0.0f, 2.041241452e-01f, }}, - {{ 2.500000000e-01f, 2.041241452e-01f, 0.0f, -2.041241452e-01f, }}, - {{ 2.500000000e-01f, -2.041241452e-01f, 0.0f, 2.041241452e-01f, }}, - {{ 2.500000000e-01f, -2.041241452e-01f, 0.0f, -2.041241452e-01f, }}, -}}; -constexpr std::array FirstOrder2DEncoder{{ - CalcAmbiCoeffs( inv_sqrt2f, 0.0f, inv_sqrt2f), - CalcAmbiCoeffs( inv_sqrt2f, 0.0f, -inv_sqrt2f), - CalcAmbiCoeffs(-inv_sqrt2f, 0.0f, inv_sqrt2f), - CalcAmbiCoeffs(-inv_sqrt2f, 0.0f, -inv_sqrt2f), -}}; +constexpr std::array FirstOrder2DDecoder{ + std::array{1.666666667e-01f, -9.622504486e-02f, 0.0f, 1.666666667e-01f}, + std::array{1.666666667e-01f, -1.924500897e-01f, 0.0f, 0.000000000e+00f}, + std::array{1.666666667e-01f, -9.622504486e-02f, 0.0f, -1.666666667e-01f}, + std::array{1.666666667e-01f, 9.622504486e-02f, 0.0f, -1.666666667e-01f}, + std::array{1.666666667e-01f, 1.924500897e-01f, 0.0f, 0.000000000e+00f}, + std::array{1.666666667e-01f, 9.622504486e-02f, 0.0f, 1.666666667e-01f}, +}; +constexpr std::array FirstOrder2DEncoder{ + CalcAmbiCoeffs(-0.50000000000f, 0.0f, 0.86602540379f), + CalcAmbiCoeffs(-1.00000000000f, 0.0f, 0.00000000000f), + CalcAmbiCoeffs(-0.50000000000f, 0.0f, -0.86602540379f), + CalcAmbiCoeffs( 0.50000000000f, 0.0f, -0.86602540379f), + CalcAmbiCoeffs( 1.00000000000f, 0.0f, 0.00000000000f), + CalcAmbiCoeffs( 0.50000000000f, 0.0f, 0.86602540379f), +}; static_assert(FirstOrder2DDecoder.size() == FirstOrder2DEncoder.size(), "First-order 2D mismatch"); @@ -94,21 +97,21 @@ static_assert(FirstOrder2DDecoder.size() == FirstOrder2DEncoder.size(), "First-o * matrix, just using a slightly more dense speaker array suitable for second- * order content. */ -constexpr std::array,12> SecondOrderDecoder{{ - {{ 8.333333333e-02f, 0.000000000e+00f, -7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, -1.443375673e-01f, 1.167715449e-01f, }}, - {{ 8.333333333e-02f, -1.227808683e-01f, 0.000000000e+00f, 7.588274978e-02f, -1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f, }}, - {{ 8.333333333e-02f, -7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f, }}, - {{ 8.333333333e-02f, 0.000000000e+00f, 7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, 1.443375673e-01f, 1.167715449e-01f, }}, - {{ 8.333333333e-02f, -1.227808683e-01f, 0.000000000e+00f, -7.588274978e-02f, 1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f, }}, - {{ 8.333333333e-02f, 7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f, }}, - {{ 8.333333333e-02f, 0.000000000e+00f, -7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, 1.443375673e-01f, 1.167715449e-01f, }}, - {{ 8.333333333e-02f, 1.227808683e-01f, 0.000000000e+00f, -7.588274978e-02f, -1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f, }}, - {{ 8.333333333e-02f, 7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, 1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f, }}, - {{ 8.333333333e-02f, 0.000000000e+00f, 7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, -1.443375673e-01f, 1.167715449e-01f, }}, - {{ 8.333333333e-02f, 1.227808683e-01f, 0.000000000e+00f, 7.588274978e-02f, 1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f, }}, - {{ 8.333333333e-02f, -7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, 1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f, }}, -}}; -constexpr std::array SecondOrderEncoder{{ +constexpr std::array SecondOrderDecoder{ + std::array{8.333333333e-02f, 0.000000000e+00f, -7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, -1.443375673e-01f, 1.167715449e-01f}, + std::array{8.333333333e-02f, -1.227808683e-01f, 0.000000000e+00f, 7.588274978e-02f, -1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f}, + std::array{8.333333333e-02f, -7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f}, + std::array{8.333333333e-02f, 0.000000000e+00f, 7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, 1.443375673e-01f, 1.167715449e-01f}, + std::array{8.333333333e-02f, -1.227808683e-01f, 0.000000000e+00f, -7.588274978e-02f, 1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f}, + std::array{8.333333333e-02f, 7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f}, + std::array{8.333333333e-02f, 0.000000000e+00f, -7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, 1.443375673e-01f, 1.167715449e-01f}, + std::array{8.333333333e-02f, 1.227808683e-01f, 0.000000000e+00f, -7.588274978e-02f, -1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f}, + std::array{8.333333333e-02f, 7.588274978e-02f, 1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, 1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f}, + std::array{8.333333333e-02f, 0.000000000e+00f, 7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, -1.591525047e-02f, -1.443375673e-01f, 1.167715449e-01f}, + std::array{8.333333333e-02f, 1.227808683e-01f, 0.000000000e+00f, 7.588274978e-02f, 1.443375673e-01f, 0.000000000e+00f, -9.316949906e-02f, 0.000000000e+00f, -7.216878365e-02f}, + std::array{8.333333333e-02f, -7.588274978e-02f, -1.227808683e-01f, 0.000000000e+00f, 0.000000000e+00f, 1.443375673e-01f, 1.090847495e-01f, 0.000000000e+00f, -4.460276122e-02f}, +}; +constexpr std::array SecondOrderEncoder{ CalcAmbiCoeffs( 0.000000000e+00f, -5.257311121e-01f, 8.506508084e-01f), CalcAmbiCoeffs(-8.506508084e-01f, 0.000000000e+00f, 5.257311121e-01f), CalcAmbiCoeffs(-5.257311121e-01f, 8.506508084e-01f, 0.000000000e+00f), @@ -121,29 +124,29 @@ constexpr std::array SecondOrderEncoder{{ CalcAmbiCoeffs( 0.000000000e+00f, 5.257311121e-01f, -8.506508084e-01f), CalcAmbiCoeffs( 8.506508084e-01f, 0.000000000e+00f, 5.257311121e-01f), CalcAmbiCoeffs(-5.257311121e-01f, -8.506508084e-01f, 0.000000000e+00f), -}}; +}; static_assert(SecondOrderDecoder.size() == SecondOrderEncoder.size(), "Second-order mismatch"); /* This calculates a 2D second-order "upsampler" matrix. Same as the second- * order matrix, just using a more optimized speaker array for horizontal-only * content. */ -constexpr std::array,6> SecondOrder2DDecoder{{ - {{ 1.666666667e-01f, -9.622504486e-02f, 0.0f, 1.666666667e-01f, -1.490711985e-01f, 0.0f, 0.0f, 0.0f, 8.606629658e-02f, }}, - {{ 1.666666667e-01f, -1.924500897e-01f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, -1.721325932e-01f, }}, - {{ 1.666666667e-01f, -9.622504486e-02f, 0.0f, -1.666666667e-01f, 1.490711985e-01f, 0.0f, 0.0f, 0.0f, 8.606629658e-02f, }}, - {{ 1.666666667e-01f, 9.622504486e-02f, 0.0f, -1.666666667e-01f, -1.490711985e-01f, 0.0f, 0.0f, 0.0f, 8.606629658e-02f, }}, - {{ 1.666666667e-01f, 1.924500897e-01f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, -1.721325932e-01f, }}, - {{ 1.666666667e-01f, 9.622504486e-02f, 0.0f, 1.666666667e-01f, 1.490711985e-01f, 0.0f, 0.0f, 0.0f, 8.606629658e-02f, }}, -}}; -constexpr std::array SecondOrder2DEncoder{{ +constexpr std::array SecondOrder2DDecoder{ + std::array{1.666666667e-01f, -9.622504486e-02f, 0.0f, 1.666666667e-01f, -1.490711985e-01f, 0.0f, 0.0f, 0.0f, 8.606629658e-02f}, + std::array{1.666666667e-01f, -1.924500897e-01f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, -1.721325932e-01f}, + std::array{1.666666667e-01f, -9.622504486e-02f, 0.0f, -1.666666667e-01f, 1.490711985e-01f, 0.0f, 0.0f, 0.0f, 8.606629658e-02f}, + std::array{1.666666667e-01f, 9.622504486e-02f, 0.0f, -1.666666667e-01f, -1.490711985e-01f, 0.0f, 0.0f, 0.0f, 8.606629658e-02f}, + std::array{1.666666667e-01f, 1.924500897e-01f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, -1.721325932e-01f}, + std::array{1.666666667e-01f, 9.622504486e-02f, 0.0f, 1.666666667e-01f, 1.490711985e-01f, 0.0f, 0.0f, 0.0f, 8.606629658e-02f}, +}; +constexpr std::array SecondOrder2DEncoder{ CalcAmbiCoeffs(-0.50000000000f, 0.0f, 0.86602540379f), CalcAmbiCoeffs(-1.00000000000f, 0.0f, 0.00000000000f), CalcAmbiCoeffs(-0.50000000000f, 0.0f, -0.86602540379f), CalcAmbiCoeffs( 0.50000000000f, 0.0f, -0.86602540379f), CalcAmbiCoeffs( 1.00000000000f, 0.0f, 0.00000000000f), CalcAmbiCoeffs( 0.50000000000f, 0.0f, 0.86602540379f), -}}; +}; static_assert(SecondOrder2DDecoder.size() == SecondOrder2DEncoder.size(), "Second-order 2D mismatch"); @@ -152,29 +155,29 @@ static_assert(SecondOrder2DDecoder.size() == SecondOrder2DEncoder.size(), * matrix, just using a more dense speaker array suitable for third-order * content. */ -constexpr std::array,20> ThirdOrderDecoder{{ - {{ 5.000000000e-02f, 3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, 6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, -1.256118221e-01f, 0.000000000e+00f, 1.126112056e-01f, 7.944389175e-02f, 0.000000000e+00f, 2.421151497e-02f, 0.000000000e+00f, }}, - {{ 5.000000000e-02f, -3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, 1.256118221e-01f, 0.000000000e+00f, -1.126112056e-01f, 7.944389175e-02f, 0.000000000e+00f, 2.421151497e-02f, 0.000000000e+00f, }}, - {{ 5.000000000e-02f, 3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, -1.256118221e-01f, 0.000000000e+00f, 1.126112056e-01f, -7.944389175e-02f, 0.000000000e+00f, -2.421151497e-02f, 0.000000000e+00f, }}, - {{ 5.000000000e-02f, -3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, 6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, 1.256118221e-01f, 0.000000000e+00f, -1.126112056e-01f, -7.944389175e-02f, 0.000000000e+00f, -2.421151497e-02f, 0.000000000e+00f, }}, - {{ 5.000000000e-02f, 8.090169944e-02f, 0.000000000e+00f, 3.090169944e-02f, 6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, -7.763237543e-02f, 0.000000000e+00f, -2.950836627e-02f, 0.000000000e+00f, -1.497759251e-01f, 0.000000000e+00f, -7.763237543e-02f, }}, - {{ 5.000000000e-02f, 8.090169944e-02f, 0.000000000e+00f, -3.090169944e-02f, -6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, -7.763237543e-02f, 0.000000000e+00f, -2.950836627e-02f, 0.000000000e+00f, 1.497759251e-01f, 0.000000000e+00f, 7.763237543e-02f, }}, - {{ 5.000000000e-02f, -8.090169944e-02f, 0.000000000e+00f, 3.090169944e-02f, -6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, 7.763237543e-02f, 0.000000000e+00f, 2.950836627e-02f, 0.000000000e+00f, -1.497759251e-01f, 0.000000000e+00f, -7.763237543e-02f, }}, - {{ 5.000000000e-02f, -8.090169944e-02f, 0.000000000e+00f, -3.090169944e-02f, 6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, 7.763237543e-02f, 0.000000000e+00f, 2.950836627e-02f, 0.000000000e+00f, 1.497759251e-01f, 0.000000000e+00f, 7.763237543e-02f, }}, - {{ 5.000000000e-02f, 0.000000000e+00f, 3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, 6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, 3.034486645e-02f, -6.779013272e-02f, 1.659481923e-01f, 4.797944664e-02f, }}, - {{ 5.000000000e-02f, 0.000000000e+00f, 3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, -6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, 3.034486645e-02f, 6.779013272e-02f, 1.659481923e-01f, -4.797944664e-02f, }}, - {{ 5.000000000e-02f, 0.000000000e+00f, -3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, -6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, -3.034486645e-02f, -6.779013272e-02f, -1.659481923e-01f, 4.797944664e-02f, }}, - {{ 5.000000000e-02f, 0.000000000e+00f, -3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, 6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, -3.034486645e-02f, 6.779013272e-02f, -1.659481923e-01f, -4.797944664e-02f, }}, - {{ 5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, 6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, 6.338656910e-02f, -1.092600649e-02f, -7.364853795e-02f, 1.011266756e-01f, -7.086833869e-02f, -1.482646439e-02f, }}, - {{ 5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, -6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, -6.338656910e-02f, -1.092600649e-02f, -7.364853795e-02f, -1.011266756e-01f, -7.086833869e-02f, 1.482646439e-02f, }}, - {{ 5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, -6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, -6.338656910e-02f, 1.092600649e-02f, -7.364853795e-02f, 1.011266756e-01f, -7.086833869e-02f, -1.482646439e-02f, }}, - {{ 5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, 6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, 6.338656910e-02f, 1.092600649e-02f, -7.364853795e-02f, -1.011266756e-01f, -7.086833869e-02f, 1.482646439e-02f, }}, - {{ 5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, 6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, -6.338656910e-02f, -1.092600649e-02f, 7.364853795e-02f, 1.011266756e-01f, 7.086833869e-02f, -1.482646439e-02f, }}, - {{ 5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, -6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, 6.338656910e-02f, -1.092600649e-02f, 7.364853795e-02f, -1.011266756e-01f, 7.086833869e-02f, 1.482646439e-02f, }}, - {{ 5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, -6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, 6.338656910e-02f, 1.092600649e-02f, 7.364853795e-02f, 1.011266756e-01f, 7.086833869e-02f, -1.482646439e-02f, }}, - {{ 5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, 6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, -6.338656910e-02f, 1.092600649e-02f, 7.364853795e-02f, -1.011266756e-01f, 7.086833869e-02f, 1.482646439e-02f, }}, -}}; -constexpr std::array ThirdOrderEncoder{{ +constexpr std::array ThirdOrderDecoder{ + std::array{5.000000000e-02f, 3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, 6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, -1.256118221e-01f, 0.000000000e+00f, 1.126112056e-01f, 7.944389175e-02f, 0.000000000e+00f, 2.421151497e-02f, 0.000000000e+00f}, + std::array{5.000000000e-02f, -3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, 1.256118221e-01f, 0.000000000e+00f, -1.126112056e-01f, 7.944389175e-02f, 0.000000000e+00f, 2.421151497e-02f, 0.000000000e+00f}, + std::array{5.000000000e-02f, 3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, -1.256118221e-01f, 0.000000000e+00f, 1.126112056e-01f, -7.944389175e-02f, 0.000000000e+00f, -2.421151497e-02f, 0.000000000e+00f}, + std::array{5.000000000e-02f, -3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, 6.454972244e-02f, 9.045084972e-02f, 0.000000000e+00f, -1.232790000e-02f, 1.256118221e-01f, 0.000000000e+00f, -1.126112056e-01f, -7.944389175e-02f, 0.000000000e+00f, -2.421151497e-02f, 0.000000000e+00f}, + std::array{5.000000000e-02f, 8.090169944e-02f, 0.000000000e+00f, 3.090169944e-02f, 6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, -7.763237543e-02f, 0.000000000e+00f, -2.950836627e-02f, 0.000000000e+00f, -1.497759251e-01f, 0.000000000e+00f, -7.763237543e-02f}, + std::array{5.000000000e-02f, 8.090169944e-02f, 0.000000000e+00f, -3.090169944e-02f, -6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, -7.763237543e-02f, 0.000000000e+00f, -2.950836627e-02f, 0.000000000e+00f, 1.497759251e-01f, 0.000000000e+00f, 7.763237543e-02f}, + std::array{5.000000000e-02f, -8.090169944e-02f, 0.000000000e+00f, 3.090169944e-02f, -6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, 7.763237543e-02f, 0.000000000e+00f, 2.950836627e-02f, 0.000000000e+00f, -1.497759251e-01f, 0.000000000e+00f, -7.763237543e-02f}, + std::array{5.000000000e-02f, -8.090169944e-02f, 0.000000000e+00f, -3.090169944e-02f, 6.454972244e-02f, 0.000000000e+00f, -5.590169944e-02f, 0.000000000e+00f, -7.216878365e-02f, 7.763237543e-02f, 0.000000000e+00f, 2.950836627e-02f, 0.000000000e+00f, 1.497759251e-01f, 0.000000000e+00f, 7.763237543e-02f}, + std::array{5.000000000e-02f, 0.000000000e+00f, 3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, 6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, 3.034486645e-02f, -6.779013272e-02f, 1.659481923e-01f, 4.797944664e-02f}, + std::array{5.000000000e-02f, 0.000000000e+00f, 3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, -6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, 3.034486645e-02f, 6.779013272e-02f, 1.659481923e-01f, -4.797944664e-02f}, + std::array{5.000000000e-02f, 0.000000000e+00f, -3.090169944e-02f, 8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, -6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, -3.034486645e-02f, -6.779013272e-02f, -1.659481923e-01f, 4.797944664e-02f}, + std::array{5.000000000e-02f, 0.000000000e+00f, -3.090169944e-02f, -8.090169944e-02f, 0.000000000e+00f, 0.000000000e+00f, -3.454915028e-02f, 6.454972244e-02f, 8.449668365e-02f, 0.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f, -3.034486645e-02f, 6.779013272e-02f, -1.659481923e-01f, -4.797944664e-02f}, + std::array{5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, 6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, 6.338656910e-02f, -1.092600649e-02f, -7.364853795e-02f, 1.011266756e-01f, -7.086833869e-02f, -1.482646439e-02f}, + std::array{5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, -6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, -6.338656910e-02f, -1.092600649e-02f, -7.364853795e-02f, -1.011266756e-01f, -7.086833869e-02f, 1.482646439e-02f}, + std::array{5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, 5.000000000e-02f, -6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, -6.338656910e-02f, 1.092600649e-02f, -7.364853795e-02f, 1.011266756e-01f, -7.086833869e-02f, -1.482646439e-02f}, + std::array{5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, 6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, 6.338656910e-02f, 1.092600649e-02f, -7.364853795e-02f, -1.011266756e-01f, -7.086833869e-02f, 1.482646439e-02f}, + std::array{5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, 6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, -6.338656910e-02f, -1.092600649e-02f, 7.364853795e-02f, 1.011266756e-01f, 7.086833869e-02f, -1.482646439e-02f}, + std::array{5.000000000e-02f, 5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, -6.454972244e-02f, -6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, 1.016220987e-01f, 6.338656910e-02f, -1.092600649e-02f, 7.364853795e-02f, -1.011266756e-01f, 7.086833869e-02f, 1.482646439e-02f}, + std::array{5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, 5.000000000e-02f, -6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, -6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, 6.338656910e-02f, 1.092600649e-02f, 7.364853795e-02f, 1.011266756e-01f, 7.086833869e-02f, -1.482646439e-02f}, + std::array{5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, -5.000000000e-02f, 6.454972244e-02f, 6.454972244e-02f, 0.000000000e+00f, 6.454972244e-02f, 0.000000000e+00f, -1.016220987e-01f, -6.338656910e-02f, 1.092600649e-02f, 7.364853795e-02f, -1.011266756e-01f, 7.086833869e-02f, 1.482646439e-02f}, +}; +constexpr std::array ThirdOrderEncoder{ CalcAmbiCoeffs( 0.35682208976f, 0.93417235897f, 0.00000000000f), CalcAmbiCoeffs(-0.35682208976f, 0.93417235897f, 0.00000000000f), CalcAmbiCoeffs( 0.35682208976f, -0.93417235897f, 0.00000000000f), @@ -195,24 +198,24 @@ constexpr std::array ThirdOrderEncoder{{ CalcAmbiCoeffs( inv_sqrt3f, -inv_sqrt3f, -inv_sqrt3f), CalcAmbiCoeffs( -inv_sqrt3f, -inv_sqrt3f, inv_sqrt3f), CalcAmbiCoeffs( -inv_sqrt3f, -inv_sqrt3f, -inv_sqrt3f), -}}; +}; static_assert(ThirdOrderDecoder.size() == ThirdOrderEncoder.size(), "Third-order mismatch"); /* This calculates a 2D third-order "upsampler" matrix. Same as the third-order * matrix, just using a more optimized speaker array for horizontal-only * content. */ -constexpr std::array,8> ThirdOrder2DDecoder{{ - {{ 1.250000000e-01f, -5.523559567e-02f, 0.0f, 1.333505242e-01f, -9.128709292e-02f, 0.0f, 0.0f, 0.0f, 9.128709292e-02f, -1.104247249e-01f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 4.573941867e-02f, }}, - {{ 1.250000000e-01f, -1.333505242e-01f, 0.0f, 5.523559567e-02f, -9.128709292e-02f, 0.0f, 0.0f, 0.0f, -9.128709292e-02f, 4.573941867e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.104247249e-01f, }}, - {{ 1.250000000e-01f, -1.333505242e-01f, 0.0f, -5.523559567e-02f, 9.128709292e-02f, 0.0f, 0.0f, 0.0f, -9.128709292e-02f, 4.573941867e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.104247249e-01f, }}, - {{ 1.250000000e-01f, -5.523559567e-02f, 0.0f, -1.333505242e-01f, 9.128709292e-02f, 0.0f, 0.0f, 0.0f, 9.128709292e-02f, -1.104247249e-01f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -4.573941867e-02f, }}, - {{ 1.250000000e-01f, 5.523559567e-02f, 0.0f, -1.333505242e-01f, -9.128709292e-02f, 0.0f, 0.0f, 0.0f, 9.128709292e-02f, 1.104247249e-01f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -4.573941867e-02f, }}, - {{ 1.250000000e-01f, 1.333505242e-01f, 0.0f, -5.523559567e-02f, -9.128709292e-02f, 0.0f, 0.0f, 0.0f, -9.128709292e-02f, -4.573941867e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.104247249e-01f, }}, - {{ 1.250000000e-01f, 1.333505242e-01f, 0.0f, 5.523559567e-02f, 9.128709292e-02f, 0.0f, 0.0f, 0.0f, -9.128709292e-02f, -4.573941867e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.104247249e-01f, }}, - {{ 1.250000000e-01f, 5.523559567e-02f, 0.0f, 1.333505242e-01f, 9.128709292e-02f, 0.0f, 0.0f, 0.0f, 9.128709292e-02f, 1.104247249e-01f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 4.573941867e-02f, }}, -}}; -constexpr std::array ThirdOrder2DEncoder{{ +constexpr std::array ThirdOrder2DDecoder{ + std::array{1.250000000e-01f, -5.523559567e-02f, 0.0f, 1.333505242e-01f, -9.128709292e-02f, 0.0f, 0.0f, 0.0f, 9.128709292e-02f, -1.104247249e-01f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 4.573941867e-02f}, + std::array{1.250000000e-01f, -1.333505242e-01f, 0.0f, 5.523559567e-02f, -9.128709292e-02f, 0.0f, 0.0f, 0.0f, -9.128709292e-02f, 4.573941867e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.104247249e-01f}, + std::array{1.250000000e-01f, -1.333505242e-01f, 0.0f, -5.523559567e-02f, 9.128709292e-02f, 0.0f, 0.0f, 0.0f, -9.128709292e-02f, 4.573941867e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.104247249e-01f}, + std::array{1.250000000e-01f, -5.523559567e-02f, 0.0f, -1.333505242e-01f, 9.128709292e-02f, 0.0f, 0.0f, 0.0f, 9.128709292e-02f, -1.104247249e-01f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -4.573941867e-02f}, + std::array{1.250000000e-01f, 5.523559567e-02f, 0.0f, -1.333505242e-01f, -9.128709292e-02f, 0.0f, 0.0f, 0.0f, 9.128709292e-02f, 1.104247249e-01f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -4.573941867e-02f}, + std::array{1.250000000e-01f, 1.333505242e-01f, 0.0f, -5.523559567e-02f, -9.128709292e-02f, 0.0f, 0.0f, 0.0f, -9.128709292e-02f, -4.573941867e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.104247249e-01f}, + std::array{1.250000000e-01f, 1.333505242e-01f, 0.0f, 5.523559567e-02f, 9.128709292e-02f, 0.0f, 0.0f, 0.0f, -9.128709292e-02f, -4.573941867e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.104247249e-01f}, + std::array{1.250000000e-01f, 5.523559567e-02f, 0.0f, 1.333505242e-01f, 9.128709292e-02f, 0.0f, 0.0f, 0.0f, 9.128709292e-02f, 1.104247249e-01f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 4.573941867e-02f}, +}; +constexpr std::array ThirdOrder2DEncoder{ CalcAmbiCoeffs(-0.38268343237f, 0.0f, 0.92387953251f), CalcAmbiCoeffs(-0.92387953251f, 0.0f, 0.38268343237f), CalcAmbiCoeffs(-0.92387953251f, 0.0f, -0.38268343237f), @@ -221,7 +224,7 @@ constexpr std::array ThirdOrder2DEncoder{{ CalcAmbiCoeffs( 0.92387953251f, 0.0f, -0.38268343237f), CalcAmbiCoeffs( 0.92387953251f, 0.0f, 0.38268343237f), CalcAmbiCoeffs( 0.38268343237f, 0.0f, 0.92387953251f), -}}; +}; static_assert(ThirdOrder2DDecoder.size() == ThirdOrder2DEncoder.size(), "Third-order 2D mismatch"); @@ -230,19 +233,19 @@ static_assert(ThirdOrder2DDecoder.size() == ThirdOrder2DEncoder.size(), "Third-o * the foreseeable future. This is only necessary for mixing horizontal-only * fourth-order content to 3D. */ -constexpr std::array,10> FourthOrder2DDecoder{{ - {{ 1.000000000e-01f, 3.568220898e-02f, 0.0f, 1.098185471e-01f, 6.070619982e-02f, 0.0f, 0.0f, 0.0f, 8.355491589e-02f, 7.735682057e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 5.620301997e-02f, 8.573754253e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.785781628e-02f, }}, - {{ 1.000000000e-01f, 9.341723590e-02f, 0.0f, 6.787159473e-02f, 9.822469464e-02f, 0.0f, 0.0f, 0.0f, -3.191513794e-02f, 2.954767620e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -9.093839659e-02f, -5.298871540e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -7.293270986e-02f, }}, - {{ 1.000000000e-01f, 1.154700538e-01f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, -1.032795559e-01f, -9.561828875e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 9.014978717e-02f, }}, - {{ 1.000000000e-01f, 9.341723590e-02f, 0.0f, -6.787159473e-02f, -9.822469464e-02f, 0.0f, 0.0f, 0.0f, -3.191513794e-02f, 2.954767620e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 9.093839659e-02f, 5.298871540e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -7.293270986e-02f, }}, - {{ 1.000000000e-01f, 3.568220898e-02f, 0.0f, -1.098185471e-01f, -6.070619982e-02f, 0.0f, 0.0f, 0.0f, 8.355491589e-02f, 7.735682057e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -5.620301997e-02f, -8.573754253e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.785781628e-02f, }}, - {{ 1.000000000e-01f, -3.568220898e-02f, 0.0f, -1.098185471e-01f, 6.070619982e-02f, 0.0f, 0.0f, 0.0f, 8.355491589e-02f, -7.735682057e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -5.620301997e-02f, 8.573754253e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.785781628e-02f, }}, - {{ 1.000000000e-01f, -9.341723590e-02f, 0.0f, -6.787159473e-02f, 9.822469464e-02f, 0.0f, 0.0f, 0.0f, -3.191513794e-02f, -2.954767620e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 9.093839659e-02f, -5.298871540e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -7.293270986e-02f, }}, - {{ 1.000000000e-01f, -1.154700538e-01f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, -1.032795559e-01f, 9.561828875e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 9.014978717e-02f, }}, - {{ 1.000000000e-01f, -9.341723590e-02f, 0.0f, 6.787159473e-02f, -9.822469464e-02f, 0.0f, 0.0f, 0.0f, -3.191513794e-02f, -2.954767620e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -9.093839659e-02f, 5.298871540e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -7.293270986e-02f, }}, - {{ 1.000000000e-01f, -3.568220898e-02f, 0.0f, 1.098185471e-01f, -6.070619982e-02f, 0.0f, 0.0f, 0.0f, 8.355491589e-02f, -7.735682057e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 5.620301997e-02f, -8.573754253e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.785781628e-02f, }}, -}}; -constexpr std::array FourthOrder2DEncoder{{ +constexpr std::array FourthOrder2DDecoder{ + std::array{1.000000000e-01f, 3.568220898e-02f, 0.0f, 1.098185471e-01f, 6.070619982e-02f, 0.0f, 0.0f, 0.0f, 8.355491589e-02f, 7.735682057e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 5.620301997e-02f, 8.573754253e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.785781628e-02f}, + std::array{1.000000000e-01f, 9.341723590e-02f, 0.0f, 6.787159473e-02f, 9.822469464e-02f, 0.0f, 0.0f, 0.0f, -3.191513794e-02f, 2.954767620e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -9.093839659e-02f, -5.298871540e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -7.293270986e-02f}, + std::array{1.000000000e-01f, 1.154700538e-01f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, -1.032795559e-01f, -9.561828875e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 9.014978717e-02f}, + std::array{1.000000000e-01f, 9.341723590e-02f, 0.0f, -6.787159473e-02f, -9.822469464e-02f, 0.0f, 0.0f, 0.0f, -3.191513794e-02f, 2.954767620e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 9.093839659e-02f, 5.298871540e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -7.293270986e-02f}, + std::array{1.000000000e-01f, 3.568220898e-02f, 0.0f, -1.098185471e-01f, -6.070619982e-02f, 0.0f, 0.0f, 0.0f, 8.355491589e-02f, 7.735682057e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -5.620301997e-02f, -8.573754253e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.785781628e-02f}, + std::array{1.000000000e-01f, -3.568220898e-02f, 0.0f, -1.098185471e-01f, 6.070619982e-02f, 0.0f, 0.0f, 0.0f, 8.355491589e-02f, -7.735682057e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -5.620301997e-02f, 8.573754253e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.785781628e-02f}, + std::array{1.000000000e-01f, -9.341723590e-02f, 0.0f, -6.787159473e-02f, 9.822469464e-02f, 0.0f, 0.0f, 0.0f, -3.191513794e-02f, -2.954767620e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 9.093839659e-02f, -5.298871540e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -7.293270986e-02f}, + std::array{1.000000000e-01f, -1.154700538e-01f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, -1.032795559e-01f, 9.561828875e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.000000000e+00f, 0.000000000e+00f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 9.014978717e-02f}, + std::array{1.000000000e-01f, -9.341723590e-02f, 0.0f, 6.787159473e-02f, -9.822469464e-02f, 0.0f, 0.0f, 0.0f, -3.191513794e-02f, -2.954767620e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -9.093839659e-02f, 5.298871540e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -7.293270986e-02f}, + std::array{1.000000000e-01f, -3.568220898e-02f, 0.0f, 1.098185471e-01f, -6.070619982e-02f, 0.0f, 0.0f, 0.0f, 8.355491589e-02f, -7.735682057e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 5.620301997e-02f, -8.573754253e-02f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.785781628e-02f}, +}; +constexpr std::array FourthOrder2DEncoder{ CalcAmbiCoeffs( 3.090169944e-01f, 0.000000000e+00f, 9.510565163e-01f), CalcAmbiCoeffs( 8.090169944e-01f, 0.000000000e+00f, 5.877852523e-01f), CalcAmbiCoeffs( 1.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f), @@ -253,7 +256,7 @@ constexpr std::array FourthOrder2DEncoder{{ CalcAmbiCoeffs(-1.000000000e+00f, 0.000000000e+00f, 0.000000000e+00f), CalcAmbiCoeffs(-8.090169944e-01f, 0.000000000e+00f, 5.877852523e-01f), CalcAmbiCoeffs(-3.090169944e-01f, 0.000000000e+00f, 9.510565163e-01f), -}}; +}; static_assert(FourthOrder2DDecoder.size() == FourthOrder2DEncoder.size(), "Fourth-order 2D mismatch"); @@ -279,13 +282,13 @@ constexpr auto CalcAmbiUpsampler(const std::array,M> &decode } // namespace -const std::array AmbiScale::FirstOrderUp{CalcAmbiUpsampler(FirstOrderDecoder, FirstOrderEncoder)}; -const std::array AmbiScale::FirstOrder2DUp{CalcAmbiUpsampler(FirstOrder2DDecoder, FirstOrder2DEncoder)}; -const std::array AmbiScale::SecondOrderUp{CalcAmbiUpsampler(SecondOrderDecoder, SecondOrderEncoder)}; -const std::array AmbiScale::SecondOrder2DUp{CalcAmbiUpsampler(SecondOrder2DDecoder, SecondOrder2DEncoder)}; -const std::array AmbiScale::ThirdOrderUp{CalcAmbiUpsampler(ThirdOrderDecoder, ThirdOrderEncoder)}; -const std::array AmbiScale::ThirdOrder2DUp{CalcAmbiUpsampler(ThirdOrder2DDecoder, ThirdOrder2DEncoder)}; -const std::array AmbiScale::FourthOrder2DUp{CalcAmbiUpsampler(FourthOrder2DDecoder, FourthOrder2DEncoder)}; +const std::array,4> AmbiScale::FirstOrderUp{CalcAmbiUpsampler(FirstOrderDecoder, FirstOrderEncoder)}; +const std::array,4> AmbiScale::FirstOrder2DUp{CalcAmbiUpsampler(FirstOrder2DDecoder, FirstOrder2DEncoder)}; +const std::array,9> AmbiScale::SecondOrderUp{CalcAmbiUpsampler(SecondOrderDecoder, SecondOrderEncoder)}; +const std::array,9> AmbiScale::SecondOrder2DUp{CalcAmbiUpsampler(SecondOrder2DDecoder, SecondOrder2DEncoder)}; +const std::array,16> AmbiScale::ThirdOrderUp{CalcAmbiUpsampler(ThirdOrderDecoder, ThirdOrderEncoder)}; +const std::array,16> AmbiScale::ThirdOrder2DUp{CalcAmbiUpsampler(ThirdOrder2DDecoder, ThirdOrder2DEncoder)}; +const std::array,25> AmbiScale::FourthOrder2DUp{CalcAmbiUpsampler(FourthOrder2DDecoder, FourthOrder2DEncoder)}; std::array AmbiScale::GetHFOrderScales(const uint src_order, diff --git a/3rdparty/openal/core/ambidefs.h b/3rdparty/openal/core/ambidefs.h index bea1a312079b..2e7774a957ba 100644 --- a/3rdparty/openal/core/ambidefs.h +++ b/3rdparty/openal/core/ambidefs.h @@ -2,8 +2,8 @@ #define CORE_AMBIDEFS_H #include -#include -#include +#include +#include #include "alnumbers.h" @@ -14,10 +14,10 @@ using uint = unsigned int; * needed will be (o+1)**2, thus zero-order has 1, first-order has 4, second- * order has 9, third-order has 16, and fourth-order has 25. */ -inline constexpr uint8_t MaxAmbiOrder{3}; -constexpr inline size_t AmbiChannelsFromOrder(size_t order) noexcept +constexpr auto AmbiChannelsFromOrder(std::size_t order) noexcept -> std::size_t { return (order+1) * (order+1); } -inline constexpr size_t MaxAmbiChannels{AmbiChannelsFromOrder(MaxAmbiOrder)}; +inline constexpr auto MaxAmbiOrder = std::uint8_t{3}; +inline constexpr auto MaxAmbiChannels = size_t{AmbiChannelsFromOrder(MaxAmbiOrder)}; /* A bitmask of ambisonic channels for 0 to 4th order. This only specifies up * to 4th order, which is the highest order a 32-bit mask value can specify (a @@ -39,20 +39,20 @@ inline constexpr uint AmbiPeriphonicMask{0xfe7ce4}; * representation. This is 2 per each order above zero-order, plus 1 for zero- * order. Or simply, o*2 + 1. */ -constexpr inline size_t Ambi2DChannelsFromOrder(size_t order) noexcept +constexpr auto Ambi2DChannelsFromOrder(std::size_t order) noexcept -> std::size_t { return order*2 + 1; } -inline constexpr size_t MaxAmbi2DChannels{Ambi2DChannelsFromOrder(MaxAmbiOrder)}; +inline constexpr auto MaxAmbi2DChannels = Ambi2DChannelsFromOrder(MaxAmbiOrder); /* NOTE: These are scale factors as applied to Ambisonics content. Decoder * coefficients should be divided by these values to get proper scalings. */ struct AmbiScale { - static inline constexpr std::array FromN3D{{ + static constexpr auto FromN3D = std::array{ 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f - }}; - static inline constexpr std::array FromSN3D{{ + }; + static constexpr auto FromSN3D = std::array{ 1.000000000f, /* ACN 0, sqrt(1) */ 1.732050808f, /* ACN 1, sqrt(3) */ 1.732050808f, /* ACN 2, sqrt(3) */ @@ -69,8 +69,8 @@ struct AmbiScale { 2.645751311f, /* ACN 13, sqrt(7) */ 2.645751311f, /* ACN 14, sqrt(7) */ 2.645751311f, /* ACN 15, sqrt(7) */ - }}; - static inline constexpr std::array FromFuMa{{ + }; + static constexpr auto FromFuMa = std::array{ 1.414213562f, /* ACN 0 (W), sqrt(2) */ 1.732050808f, /* ACN 1 (Y), sqrt(3) */ 1.732050808f, /* ACN 2 (Z), sqrt(3) */ @@ -87,15 +87,15 @@ struct AmbiScale { 2.231093404f, /* ACN 13 (L), sqrt(224/45) */ 1.972026594f, /* ACN 14 (N), sqrt(35)/3 */ 2.091650066f, /* ACN 15 (P), sqrt(35/8) */ - }}; - static inline constexpr std::array FromUHJ{{ + }; + static constexpr auto FromUHJ = std::array{ 1.000000000f, /* ACN 0 (W), sqrt(1) */ 1.224744871f, /* ACN 1 (Y), sqrt(3/2) */ 1.224744871f, /* ACN 2 (Z), sqrt(3/2) */ 1.224744871f, /* ACN 3 (X), sqrt(3/2) */ /* Higher orders not relevant for UHJ. */ 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, - }}; + }; /* Retrieves per-order HF scaling factors for "upsampling" ambisonic data. */ static std::array GetHFOrderScales(const uint src_order, @@ -111,7 +111,7 @@ struct AmbiScale { }; struct AmbiIndex { - static inline constexpr std::array FromFuMa{{ + static constexpr auto FromFuMa = std::array{ 0, /* W */ 3, /* X */ 1, /* Y */ @@ -128,8 +128,8 @@ struct AmbiIndex { 10, /* O */ 15, /* P */ 9, /* Q */ - }}; - static inline constexpr std::array FromFuMa2D{{ + }; + static constexpr auto FromFuMa2D = std::array{ 0, /* W */ 3, /* X */ 1, /* Y */ @@ -137,23 +137,23 @@ struct AmbiIndex { 4, /* V */ 15, /* P */ 9, /* Q */ - }}; + }; - static inline constexpr std::array FromACN{{ + static constexpr auto FromACN = std::array{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 - }}; - static inline constexpr std::array FromACN2D{{ + }; + static constexpr auto FromACN2D = std::array{ 0, 1,3, 4,8, 9,15 - }}; + }; - static inline constexpr std::array OrderFromChannel{{ + static constexpr auto OrderFromChannel = std::array{ 0, 1,1,1, 2,2,2,2,2, 3,3,3,3,3,3,3, - }}; - static inline constexpr std::array OrderFrom2DChannel{{ + }; + static constexpr auto OrderFrom2DChannel = std::array{ 0, 1,1, 2,2, 3,3, - }}; + }; }; diff --git a/3rdparty/openal/core/async_event.h b/3rdparty/openal/core/async_event.h index f1ca0c7b6251..f865fd8a809b 100644 --- a/3rdparty/openal/core/async_event.h +++ b/3rdparty/openal/core/async_event.h @@ -1,7 +1,9 @@ #ifndef CORE_EVENT_H #define CORE_EVENT_H -#include +#include +#include +#include #include #include "almalloc.h" @@ -11,7 +13,7 @@ struct EffectState; using uint = unsigned int; -enum class AsyncEnableBits : uint8_t { +enum class AsyncEnableBits : std::uint8_t { SourceState, BufferCompleted, Disconnected, @@ -19,7 +21,7 @@ enum class AsyncEnableBits : uint8_t { }; -enum class AsyncSrcState : uint8_t { +enum class AsyncSrcState : std::uint8_t { Reset, Stop, Play, @@ -39,7 +41,7 @@ struct AsyncBufferCompleteEvent { }; struct AsyncDisconnectEvent { - char msg[244]; + std::string msg; }; struct AsyncEffectReleaseEvent { diff --git a/3rdparty/openal/core/bformatdec.cpp b/3rdparty/openal/core/bformatdec.cpp index a308e1856967..1e46e9508ced 100644 --- a/3rdparty/openal/core/bformatdec.cpp +++ b/3rdparty/openal/core/bformatdec.cpp @@ -6,11 +6,13 @@ #include #include #include +#include #include -#include "almalloc.h" #include "alnumbers.h" +#include "bufferline.h" #include "filters/splitter.h" +#include "flexarray.h" #include "front_stablizer.h" #include "mixer.h" #include "opthelpers.h" @@ -36,9 +38,8 @@ BFormatDec::BFormatDec(const size_t inchans, const al::span co auto &decoder = mChannelDec.emplace>(inchans); for(size_t j{0};j < decoder.size();++j) { - float *outcoeffs{decoder[j].mGains}; - for(const ChannelDec &incoeffs : coeffs) - *(outcoeffs++) = incoeffs[j]; + std::transform(coeffs.cbegin(), coeffs.cend(), decoder[j].mGains.begin(), + [j](const ChannelDec &incoeffs) { return incoeffs[j]; }); } } else @@ -50,45 +51,40 @@ BFormatDec::BFormatDec(const size_t inchans, const al::span co for(size_t j{0};j < decoder.size();++j) { - float *outcoeffs{decoder[j].mGains[sHFBand]}; - for(const ChannelDec &incoeffs : coeffs) - *(outcoeffs++) = incoeffs[j]; + std::transform(coeffs.cbegin(), coeffs.cend(), decoder[j].mGains[sHFBand].begin(), + [j](const ChannelDec &incoeffs) { return incoeffs[j]; }); - outcoeffs = decoder[j].mGains[sLFBand]; - for(const ChannelDec &incoeffs : coeffslf) - *(outcoeffs++) = incoeffs[j]; + std::transform(coeffslf.cbegin(), coeffslf.cend(), decoder[j].mGains[sLFBand].begin(), + [j](const ChannelDec &incoeffs) { return incoeffs[j]; }); } } } void BFormatDec::process(const al::span OutBuffer, - const FloatBufferLine *InSamples, const size_t SamplesToDo) + const al::span InSamples, const size_t SamplesToDo) { ASSUME(SamplesToDo > 0); auto decode_dualband = [=](std::vector &decoder) { - auto *input = InSamples; - const al::span hfSamples{mSamples[sHFBand].data(), SamplesToDo}; - const al::span lfSamples{mSamples[sLFBand].data(), SamplesToDo}; + auto input = InSamples.cbegin(); + const auto hfSamples = al::span{mSamples[sHFBand]}.first(SamplesToDo); + const auto lfSamples = al::span{mSamples[sLFBand]}.first(SamplesToDo); for(auto &chandec : decoder) { - chandec.mXOver.process({input->data(), SamplesToDo}, hfSamples.data(), - lfSamples.data()); + chandec.mXOver.process(al::span{*input++}.first(SamplesToDo), hfSamples, lfSamples); MixSamples(hfSamples, OutBuffer, chandec.mGains[sHFBand], chandec.mGains[sHFBand],0,0); MixSamples(lfSamples, OutBuffer, chandec.mGains[sLFBand], chandec.mGains[sLFBand],0,0); - ++input; } }; auto decode_singleband = [=](std::vector &decoder) { - auto *input = InSamples; + auto input = InSamples.cbegin(); for(auto &chandec : decoder) { - MixSamples({input->data(), SamplesToDo}, OutBuffer, chandec.mGains, chandec.mGains, - 0, 0); - ++input; + MixSamples(al::span{*input++}.first(SamplesToDo), OutBuffer, chandec.mGains, + chandec.mGains, 0, 0); } }; @@ -96,38 +92,36 @@ void BFormatDec::process(const al::span OutBuffer, } void BFormatDec::processStablize(const al::span OutBuffer, - const FloatBufferLine *InSamples, const size_t lidx, const size_t ridx, const size_t cidx, - const size_t SamplesToDo) + const al::span InSamples, const size_t lidx, const size_t ridx, + const size_t cidx, const size_t SamplesToDo) { ASSUME(SamplesToDo > 0); /* Move the existing direct L/R signal out so it doesn't get processed by * the stablizer. */ - float *RESTRICT mid{al::assume_aligned<16>(mStablizer->MidDirect.data())}; - float *RESTRICT side{al::assume_aligned<16>(mStablizer->Side.data())}; - for(size_t i{0};i < SamplesToDo;++i) - { - mid[i] = OutBuffer[lidx][i] + OutBuffer[ridx][i]; - side[i] = OutBuffer[lidx][i] - OutBuffer[ridx][i]; - } - std::fill_n(OutBuffer[lidx].begin(), SamplesToDo, 0.0f); - std::fill_n(OutBuffer[ridx].begin(), SamplesToDo, 0.0f); + const auto leftout = al::span{OutBuffer[lidx]}.first(SamplesToDo); + const auto rightout = al::span{OutBuffer[ridx]}.first(SamplesToDo); + const auto mid = al::span{mStablizer->MidDirect}.first(SamplesToDo); + const auto side = al::span{mStablizer->Side}.first(SamplesToDo); + std::transform(leftout.cbegin(), leftout.cend(), rightout.cbegin(), mid.begin(),std::plus{}); + std::transform(leftout.cbegin(), leftout.cend(), rightout.cbegin(), side.begin(),std::minus{}); + std::fill_n(leftout.begin(), leftout.size(), 0.0f); + std::fill_n(rightout.begin(), rightout.size(), 0.0f); /* Decode the B-Format input to OutBuffer. */ process(OutBuffer, InSamples, SamplesToDo); /* Include the decoded side signal with the direct side signal. */ for(size_t i{0};i < SamplesToDo;++i) - side[i] += OutBuffer[lidx][i] - OutBuffer[ridx][i]; + side[i] += leftout[i] - rightout[i]; /* Get the decoded mid signal and band-split it. */ - std::transform(OutBuffer[lidx].cbegin(), OutBuffer[lidx].cbegin()+SamplesToDo, - OutBuffer[ridx].cbegin(), mStablizer->Temp.begin(), - [](const float l, const float r) noexcept { return l + r; }); + const auto tmpsamples = al::span{mStablizer->Temp}.first(SamplesToDo); + std::transform(leftout.cbegin(), leftout.cend(), rightout.cbegin(), tmpsamples.begin(), + std::plus{}); - mStablizer->MidFilter.process({mStablizer->Temp.data(), SamplesToDo}, mStablizer->MidHF.data(), - mStablizer->MidLF.data()); + mStablizer->MidFilter.process(tmpsamples, mStablizer->MidHF, mStablizer->MidLF); /* Apply an all-pass to all channels to match the band-splitter's phase * shift. This is to keep the phase synchronized between the existing @@ -140,9 +134,9 @@ void BFormatDec::processStablize(const al::span OutBuffer, * and substitute the direct mid signal and direct+decoded side signal. */ if(i == lidx) - mStablizer->ChannelFilters[i].processAllPass({mid, SamplesToDo}); + mStablizer->ChannelFilters[i].processAllPass(mid); else if(i == ridx) - mStablizer->ChannelFilters[i].processAllPass({side, SamplesToDo}); + mStablizer->ChannelFilters[i].processAllPass(side); else mStablizer->ChannelFilters[i].processAllPass({OutBuffer[i].data(), SamplesToDo}); } @@ -156,6 +150,7 @@ void BFormatDec::processStablize(const al::span OutBuffer, const float cos_hf{std::cos(1.0f/4.0f * (al::numbers::pi_v*0.5f))}; const float sin_lf{std::sin(1.0f/3.0f * (al::numbers::pi_v*0.5f))}; const float sin_hf{std::sin(1.0f/4.0f * (al::numbers::pi_v*0.5f))}; + const auto centerout = al::span{OutBuffer[cidx]}.first(SamplesToDo); for(size_t i{0};i < SamplesToDo;i++) { /* Add the direct mid signal to the processed mid signal so it can be @@ -168,9 +163,9 @@ void BFormatDec::processStablize(const al::span OutBuffer, /* The generated center channel signal adds to the existing signal, * while the modified left and right channels replace. */ - OutBuffer[lidx][i] = (m + s) * 0.5f; - OutBuffer[ridx][i] = (m - s) * 0.5f; - OutBuffer[cidx][i] += c * 0.5f; + leftout[i] = (m + s) * 0.5f; + rightout[i] = (m - s) * 0.5f; + centerout[i] += c * 0.5f; } } diff --git a/3rdparty/openal/core/bformatdec.h b/3rdparty/openal/core/bformatdec.h index 3bb7f544899b..b86f771da15c 100644 --- a/3rdparty/openal/core/bformatdec.h +++ b/3rdparty/openal/core/bformatdec.h @@ -7,33 +7,32 @@ #include #include -#include "almalloc.h" #include "alspan.h" #include "ambidefs.h" #include "bufferline.h" #include "devformat.h" #include "filters/splitter.h" - -struct FrontStablizer; +#include "front_stablizer.h" +#include "opthelpers.h" using ChannelDec = std::array; -class BFormatDec { +class SIMDALIGN BFormatDec { static constexpr size_t sHFBand{0}; static constexpr size_t sLFBand{1}; static constexpr size_t sNumBands{2}; struct ChannelDecoderSingle { - float mGains[MAX_OUTPUT_CHANNELS]; + std::array mGains{}; }; struct ChannelDecoderDual { BandSplitter mXOver; - float mGains[sNumBands][MAX_OUTPUT_CHANNELS]; + std::array,sNumBands> mGains{}; }; - alignas(16) std::array mSamples; + alignas(16) std::array mSamples{}; const std::unique_ptr mStablizer; @@ -44,22 +43,20 @@ class BFormatDec { const al::span coeffslf, const float xover_f0norm, std::unique_ptr stablizer); - bool hasStablizer() const noexcept { return mStablizer != nullptr; } + [[nodiscard]] auto hasStablizer() const noexcept -> bool { return mStablizer != nullptr; } /* Decodes the ambisonic input to the given output channels. */ - void process(const al::span OutBuffer, const FloatBufferLine *InSamples, - const size_t SamplesToDo); + void process(const al::span OutBuffer, + const al::span InSamples, const size_t SamplesToDo); /* Decodes the ambisonic input to the given output channels with stablization. */ void processStablize(const al::span OutBuffer, - const FloatBufferLine *InSamples, const size_t lidx, const size_t ridx, const size_t cidx, - const size_t SamplesToDo); + const al::span InSamples, const size_t lidx, const size_t ridx, + const size_t cidx, const size_t SamplesToDo); static std::unique_ptr Create(const size_t inchans, const al::span coeffs, const al::span coeffslf, const float xover_f0norm, std::unique_ptr stablizer); - - DEF_NEWDEL(BFormatDec) }; #endif /* CORE_BFORMATDEC_H */ diff --git a/3rdparty/openal/core/bs2b.cpp b/3rdparty/openal/core/bs2b.cpp index 303bf9bd9300..68b0d575fba3 100644 --- a/3rdparty/openal/core/bs2b.cpp +++ b/3rdparty/openal/core/bs2b.cpp @@ -26,72 +26,75 @@ #include #include #include +#include #include "alnumbers.h" +#include "alspan.h" #include "bs2b.h" +namespace { /* Set up all data. */ -static void init(struct bs2b *bs2b) +void init(Bs2b::bs2b *bs2b) { float Fc_lo, Fc_hi; float G_lo, G_hi; - float x, g; switch(bs2b->level) { - case BS2B_LOW_CLEVEL: /* Low crossfeed level */ + case Bs2b::LowCLevel: /* Low crossfeed level */ Fc_lo = 360.0f; Fc_hi = 501.0f; G_lo = 0.398107170553497f; G_hi = 0.205671765275719f; break; - case BS2B_MIDDLE_CLEVEL: /* Middle crossfeed level */ + case Bs2b::MiddleCLevel: /* Middle crossfeed level */ Fc_lo = 500.0f; Fc_hi = 711.0f; G_lo = 0.459726988530872f; G_hi = 0.228208484414988f; break; - case BS2B_HIGH_CLEVEL: /* High crossfeed level (virtual speakers are closer to itself) */ + case Bs2b::HighCLevel: /* High crossfeed level (virtual speakers are closer to itself) */ Fc_lo = 700.0f; Fc_hi = 1021.0f; G_lo = 0.530884444230988f; G_hi = 0.250105790667544f; break; - case BS2B_LOW_ECLEVEL: /* Low easy crossfeed level */ + case Bs2b::LowECLevel: /* Low easy crossfeed level */ Fc_lo = 360.0f; Fc_hi = 494.0f; G_lo = 0.316227766016838f; G_hi = 0.168236228897329f; break; - case BS2B_MIDDLE_ECLEVEL: /* Middle easy crossfeed level */ + case Bs2b::MiddleECLevel: /* Middle easy crossfeed level */ Fc_lo = 500.0f; Fc_hi = 689.0f; G_lo = 0.354813389233575f; G_hi = 0.187169483835901f; break; - default: /* High easy crossfeed level */ - bs2b->level = BS2B_HIGH_ECLEVEL; + case Bs2b::HighECLevel: /* High easy crossfeed level */ + default: + bs2b->level = Bs2b::HighECLevel; Fc_lo = 700.0f; Fc_hi = 975.0f; G_lo = 0.398107170553497f; G_hi = 0.205671765275719f; break; - } /* switch */ + } - g = 1.0f / (1.0f - G_hi + G_lo); + float g{1.0f / (1.0f - G_hi + G_lo)}; /* $fc = $Fc / $s; * $d = 1 / 2 / pi / $fc; * $x = exp(-1 / $d); */ - x = std::exp(-al::numbers::pi_v*2.0f*Fc_lo/static_cast(bs2b->srate)); + float x{ std::exp(-al::numbers::pi_v*2.0f*Fc_lo/static_cast(bs2b->srate))}; bs2b->b1_lo = x; bs2b->a0_lo = G_lo * (1.0f - x) * g; @@ -99,85 +102,86 @@ static void init(struct bs2b *bs2b) bs2b->b1_hi = x; bs2b->a0_hi = (1.0f - G_hi * (1.0f - x)) * g; bs2b->a1_hi = -x * g; -} /* init */ +} +} // namespace /* Exported functions. * See descriptions in "bs2b.h" */ +namespace Bs2b { -void bs2b_set_params(struct bs2b *bs2b, int level, int srate) -{ - if(srate <= 0) srate = 1; - - bs2b->level = level; - bs2b->srate = srate; - init(bs2b); -} /* bs2b_set_params */ - -int bs2b_get_level(struct bs2b *bs2b) +void bs2b::set_params(int level_, int srate_) { - return bs2b->level; -} /* bs2b_get_level */ + if(srate_ < 1) + throw std::runtime_error{"BS2B srate < 1"}; -int bs2b_get_srate(struct bs2b *bs2b) -{ - return bs2b->srate; -} /* bs2b_get_srate */ + level = level_; + srate = srate_; + init(this); +} -void bs2b_clear(struct bs2b *bs2b) +void bs2b::clear() { - std::fill(std::begin(bs2b->history), std::end(bs2b->history), bs2b::t_last_sample{}); -} /* bs2b_clear */ + history.fill(bs2b::t_last_sample{}); +} -void bs2b_cross_feed(struct bs2b *bs2b, float *Left, float *Right, size_t SamplesToDo) +void bs2b::cross_feed(const al::span Left, const al::span Right) { - const float a0_lo{bs2b->a0_lo}; - const float b1_lo{bs2b->b1_lo}; - const float a0_hi{bs2b->a0_hi}; - const float a1_hi{bs2b->a1_hi}; - const float b1_hi{bs2b->b1_hi}; - float lsamples[128][2]; - float rsamples[128][2]; - - for(size_t base{0};base < SamplesToDo;) + const auto a0lo = a0_lo; + const auto b1lo = b1_lo; + const auto a0hi = a0_hi; + const auto a1hi = a1_hi; + const auto b1hi = b1_hi; + auto lsamples = Left.first(std::min(Left.size(), Right.size())); + auto rsamples = Right.first(lsamples.size()); + auto samples = std::array,128>{}; + + auto leftio = lsamples.begin(); + auto rightio = rsamples.begin(); + while(auto rem = std::distance(leftio, lsamples.end())) { - const size_t todo{std::min(128, SamplesToDo-base)}; + const auto todo = std::min(samples.size(), rem); /* Process left input */ - float z_lo{bs2b->history[0].lo}; - float z_hi{bs2b->history[0].hi}; - for(size_t i{0};i < todo;i++) - { - lsamples[i][0] = a0_lo*Left[i] + z_lo; - z_lo = b1_lo*lsamples[i][0]; - - lsamples[i][1] = a0_hi*Left[i] + z_hi; - z_hi = a1_hi*Left[i] + b1_hi*lsamples[i][1]; - } - bs2b->history[0].lo = z_lo; - bs2b->history[0].hi = z_hi; + auto z_lo = history[0].lo; + auto z_hi = history[0].hi; + std::transform(leftio, leftio+todo, samples.begin(), + [a0hi,a1hi,b1hi,a0lo,b1lo,&z_lo,&z_hi](const float x) noexcept + { + const auto y0 = a0hi*x + z_hi; + z_hi = a1hi*x + b1hi*y0; + + const auto y1 = a0lo*x + z_lo; + z_lo = b1lo*y1; + + return std::array{y0, y1}; + }); + history[0].lo = z_lo; + history[0].hi = z_hi; /* Process right input */ - z_lo = bs2b->history[1].lo; - z_hi = bs2b->history[1].hi; - for(size_t i{0};i < todo;i++) - { - rsamples[i][0] = a0_lo*Right[i] + z_lo; - z_lo = b1_lo*rsamples[i][0]; - - rsamples[i][1] = a0_hi*Right[i] + z_hi; - z_hi = a1_hi*Right[i] + b1_hi*rsamples[i][1]; - } - bs2b->history[1].lo = z_lo; - bs2b->history[1].hi = z_hi; - - /* Crossfeed */ - for(size_t i{0};i < todo;i++) - *(Left++) = lsamples[i][1] + rsamples[i][0]; - for(size_t i{0};i < todo;i++) - *(Right++) = rsamples[i][1] + lsamples[i][0]; - - base += todo; + z_lo = history[1].lo; + z_hi = history[1].hi; + std::transform(rightio, rightio+todo, samples.cbegin(), samples.begin(), + [a0hi,a1hi,b1hi,a0lo,b1lo,&z_lo,&z_hi](const float x, const std::array &out) noexcept + { + const auto y0 = a0lo*x + z_lo; + z_lo = b1lo*y0; + + const auto y1 = a0hi*x + z_hi; + z_hi = a1hi*x + b1hi*y1; + + return std::array{out[0]+y0, out[1]+y1}; + }); + history[1].lo = z_lo; + history[1].hi = z_hi; + + leftio = std::transform(samples.cbegin(), samples.cbegin()+todo, leftio, + [](const std::array &in) { return in[0]; }); + rightio = std::transform(samples.cbegin(), samples.cbegin()+todo, rightio, + [](const std::array &in) { return in[1]; }); } -} /* bs2b_cross_feed */ +} + +} // namespace Bs2b diff --git a/3rdparty/openal/core/bs2b.h b/3rdparty/openal/core/bs2b.h index 4d0b9dd8e14e..dd6d3ce24573 100644 --- a/3rdparty/openal/core/bs2b.h +++ b/3rdparty/openal/core/bs2b.h @@ -24,66 +24,68 @@ #ifndef CORE_BS2B_H #define CORE_BS2B_H -#include "almalloc.h" +#include +#include -/* Number of crossfeed levels */ -#define BS2B_CLEVELS 3 +#include "alspan.h" -/* Normal crossfeed levels */ -#define BS2B_HIGH_CLEVEL 3 -#define BS2B_MIDDLE_CLEVEL 2 -#define BS2B_LOW_CLEVEL 1 +namespace Bs2b { -/* Easy crossfeed levels */ -#define BS2B_HIGH_ECLEVEL BS2B_HIGH_CLEVEL + BS2B_CLEVELS -#define BS2B_MIDDLE_ECLEVEL BS2B_MIDDLE_CLEVEL + BS2B_CLEVELS -#define BS2B_LOW_ECLEVEL BS2B_LOW_CLEVEL + BS2B_CLEVELS +enum { + /* Normal crossfeed levels */ + LowCLevel = 1, + MiddleCLevel = 2, + HighCLevel = 3, -/* Default crossfeed levels */ -#define BS2B_DEFAULT_CLEVEL BS2B_HIGH_ECLEVEL -/* Default sample rate (Hz) */ -#define BS2B_DEFAULT_SRATE 44100 + /* Easy crossfeed levels */ + LowECLevel = 4, + MiddleECLevel = 5, + HighECLevel = 6, + + DefaultCLevel = HighECLevel +}; struct bs2b { - int level; /* Crossfeed level */ - int srate; /* Sample rate (Hz) */ + int level{}; /* Crossfeed level */ + int srate{}; /* Sample rate (Hz) */ /* Lowpass IIR filter coefficients */ - float a0_lo; - float b1_lo; + float a0_lo{}; + float b1_lo{}; /* Highboost IIR filter coefficients */ - float a0_hi; - float a1_hi; - float b1_hi; + float a0_hi{}; + float a1_hi{}; + float b1_hi{}; /* Buffer of filter history * [0] - first channel, [1] - second channel */ struct t_last_sample { - float lo; - float hi; - } history[2]; - - DEF_NEWDEL(bs2b) -}; + float lo{}; + float hi{}; + }; + std::array history{}; + + /* Clear buffers and set new coefficients with new crossfeed level and + * sample rate values. + * level - crossfeed level of *Level enum values. + * srate - sample rate by Hz. + */ + void set_params(int level, int srate); -/* Clear buffers and set new coefficients with new crossfeed level and sample - * rate values. - * level - crossfeed level of *LEVEL values. - * srate - sample rate by Hz. - */ -void bs2b_set_params(bs2b *bs2b, int level, int srate); + /* Return current crossfeed level value */ + [[nodiscard]] auto get_level() const noexcept -> int { return level; } -/* Return current crossfeed level value */ -int bs2b_get_level(bs2b *bs2b); + /* Return current sample rate value */ + [[nodiscard]] auto get_srate() const noexcept -> int { return srate; } -/* Return current sample rate value */ -int bs2b_get_srate(bs2b *bs2b); + /* Clear buffer */ + void clear(); -/* Clear buffer */ -void bs2b_clear(bs2b *bs2b); + void cross_feed(const al::span Left, const al::span Right); +}; -void bs2b_cross_feed(bs2b *bs2b, float *Left, float *Right, size_t SamplesToDo); +} // namespace Bs2b #endif /* CORE_BS2B_H */ diff --git a/3rdparty/openal/core/bsinc_tables.cpp b/3rdparty/openal/core/bsinc_tables.cpp index 1b3f57841a2f..9d6b5d8b77e6 100644 --- a/3rdparty/openal/core/bsinc_tables.cpp +++ b/3rdparty/openal/core/bsinc_tables.cpp @@ -5,12 +5,16 @@ #include #include #include +#include #include -#include -#include +#include +#include #include "alnumbers.h" +#include "alnumeric.h" +#include "alspan.h" #include "bsinc_defs.h" +#include "opthelpers.h" #include "resampler_limits.h" @@ -19,37 +23,32 @@ namespace { using uint = unsigned int; -/* This is the normalized cardinal sine (sinc) function. - * - * sinc(x) = { 1, x = 0 - * { sin(pi x) / (pi x), otherwise. - */ -constexpr double Sinc(const double x) -{ - constexpr double epsilon{std::numeric_limits::epsilon()}; - if(!(x > epsilon || x < -epsilon)) - return 1.0; - return std::sin(al::numbers::pi*x) / (al::numbers::pi*x); -} - /* The zero-order modified Bessel function of the first kind, used for the * Kaiser window. * * I_0(x) = sum_{k=0}^inf (1 / k!)^2 (x / 2)^(2 k) * = sum_{k=0}^inf ((x / 2)^k / k!)^2 + * + * This implementation only handles nu = 0, and isn't the most precise (it + * starts with the largest value and accumulates successively smaller values, + * compounding the rounding and precision error), but it's good enough. */ -constexpr double BesselI_0(const double x) noexcept +template +constexpr auto cyl_bessel_i(T nu, U x) -> U { + if(nu != T{0}) + throw std::runtime_error{"cyl_bessel_i: nu != 0"}; + /* Start at k=1 since k=0 is trivial. */ - const double x2{x / 2.0}; + const double x2{x/2.0}; double term{1.0}; double sum{1.0}; - double last_sum{}; int k{1}; /* Let the integration converge until the term of the sum is no longer * significant. */ + double last_sum{}; do { const double y{x2 / k}; ++k; @@ -57,8 +56,20 @@ constexpr double BesselI_0(const double x) noexcept term *= y * y; sum += term; } while(sum != last_sum); + return static_cast(sum); +} - return sum; +/* This is the normalized cardinal sine (sinc) function. + * + * sinc(x) = { 1, x = 0 + * { sin(pi x) / (pi x), otherwise. + */ +constexpr double Sinc(const double x) +{ + constexpr double epsilon{std::numeric_limits::epsilon()}; + if(!(x > epsilon || x < -epsilon)) + return 1.0; + return std::sin(al::numbers::pi*x) / (al::numbers::pi*x); } /* Calculate a Kaiser window from the given beta value and a normalized k @@ -79,7 +90,7 @@ constexpr double Kaiser(const double beta, const double k, const double besseli_ { if(!(k >= -1.0 && k <= 1.0)) return 0.0; - return BesselI_0(beta * std::sqrt(1.0 - k*k)) / besseli_0_beta; + return ::cyl_bessel_i(0, beta * std::sqrt(1.0 - k*k)) / besseli_0_beta; } /* Calculates the (normalized frequency) transition width of the Kaiser window. @@ -98,7 +109,7 @@ constexpr double CalcKaiserBeta(const double rejection) { if(rejection > 50.0) return 0.1102 * (rejection-8.7); - else if(rejection >= 21.0) + if(rejection >= 21.0) return (0.5842 * std::pow(rejection-21.0, 0.4)) + (0.07886 * (rejection-21.0)); return 0.0; } @@ -108,24 +119,18 @@ struct BSincHeader { double width{}; double beta{}; double scaleBase{}; - double scaleRange{}; - double besseli_0_beta{}; - uint a[BSincScaleCount]{}; + std::array a{}; uint total_size{}; constexpr BSincHeader(uint Rejection, uint Order) noexcept + : width{CalcKaiserWidth(Rejection, Order)}, beta{CalcKaiserBeta(Rejection)} + , scaleBase{width / 2.0} { - width = CalcKaiserWidth(Rejection, Order); - beta = CalcKaiserBeta(Rejection); - scaleBase = width / 2.0; - scaleRange = 1.0 - scaleBase; - besseli_0_beta = BesselI_0(beta); - uint num_points{Order+1}; for(uint si{0};si < BSincScaleCount;++si) { - const double scale{scaleBase + (scaleRange * (si+1) / BSincScaleCount)}; + const double scale{lerpd(scaleBase, 1.0, (si+1) / double{BSincScaleCount})}; const uint a_{std::min(static_cast(num_points / 2.0 / scale), num_points)}; const uint m{2 * a_}; @@ -144,16 +149,18 @@ constexpr BSincHeader bsinc24_hdr{60, 23}; template -struct BSincFilterArray { +struct SIMDALIGN BSincFilterArray { alignas(16) std::array mTable{}; BSincFilterArray() { - constexpr uint BSincPointsMax{(hdr.a[0]*2 + 3) & ~3u}; + static constexpr uint BSincPointsMax{(hdr.a[0]*2u + 3u) & ~3u}; static_assert(BSincPointsMax <= MaxResamplerPadding, "MaxResamplerPadding is too small"); - using filter_type = double[BSincPhaseCount+1][BSincPointsMax]; - auto filter = std::make_unique(BSincScaleCount); + using filter_type = std::array,BSincPhaseCount>; + auto filter = std::vector(BSincScaleCount); + + const double besseli_0_beta{::cyl_bessel_i(0, hdr.beta)}; /* Calculate the Kaiser-windowed Sinc filter coefficients for each * scale and phase index. @@ -162,22 +169,19 @@ struct BSincFilterArray { { const uint m{hdr.a[si] * 2}; const size_t o{(BSincPointsMax-m) / 2}; - const double scale{hdr.scaleBase + (hdr.scaleRange * (si+1) / BSincScaleCount)}; + const double scale{lerpd(hdr.scaleBase, 1.0, (si+1) / double{BSincScaleCount})}; const double cutoff{scale - (hdr.scaleBase * std::max(1.0, scale*2.0))}; const auto a = static_cast(hdr.a[si]); const double l{a - 1.0/BSincPhaseCount}; - /* Do one extra phase index so that the phase delta has a proper - * target for its last index. - */ - for(uint pi{0};pi <= BSincPhaseCount;++pi) + for(uint pi{0};pi < BSincPhaseCount;++pi) { const double phase{std::floor(l) + (pi/double{BSincPhaseCount})}; for(uint i{0};i < m;++i) { const double x{i - phase}; - filter[si][pi][o+i] = Kaiser(hdr.beta, x/l, hdr.besseli_0_beta) * cutoff * + filter[si][pi][o+i] = Kaiser(hdr.beta, x/l, besseli_0_beta) * cutoff * Sinc(cutoff*x); } } @@ -200,50 +204,78 @@ struct BSincFilterArray { /* Linear interpolation between phases is simplified by pre- * calculating the delta (b - a) in: x = a + f (b - a) */ - for(size_t i{0};i < m;++i) + if(pi < BSincPhaseCount-1) { - const double phDelta{filter[si][pi+1][o+i] - filter[si][pi][o+i]}; - mTable[idx++] = static_cast(phDelta); + for(size_t i{0};i < m;++i) + { + const double phDelta{filter[si][pi+1][o+i] - filter[si][pi][o+i]}; + mTable[idx++] = static_cast(phDelta); + } } - } - /* Calculate and write out each phase index's filter quality scale - * deltas. The last scale index doesn't have any scale or scale- - * phase deltas. - */ - if(si == BSincScaleCount-1) - { - for(size_t i{0};i < BSincPhaseCount*m*2;++i) - mTable[idx++] = 0.0f; - } - else for(size_t pi{0};pi < BSincPhaseCount;++pi) - { - /* Linear interpolation between scales is also simplified. - * - * Given a difference in the number of points between scales, - * the destination points will be 0, thus: x = a + f (-a) - */ - for(size_t i{0};i < m;++i) + else { - const double scDelta{filter[si+1][pi][o+i] - filter[si][pi][o+i]}; - mTable[idx++] = static_cast(scDelta); + /* The delta target for the last phase index is the first + * phase index with the coefficients offset by one. The + * first delta targets 0, as it represents a coefficient + * for a sample that won't be part of the filter. + */ + mTable[idx++] = static_cast(0.0 - filter[si][pi][o]); + for(size_t i{1};i < m;++i) + { + const double phDelta{filter[si][0][o+i-1] - filter[si][pi][o+i]}; + mTable[idx++] = static_cast(phDelta); + } } + } - /* This last simplification is done to complete the bilinear - * equation for the combination of phase and scale. - */ - for(size_t i{0};i < m;++i) + /* Now write out each phase index's scale and phase+scale deltas, + * to complete the bilinear equation for the combination of phase + * and scale. + */ + if(si < BSincScaleCount-1) + { + for(size_t pi{0};pi < BSincPhaseCount;++pi) { - const double spDelta{(filter[si+1][pi+1][o+i] - filter[si+1][pi][o+i]) - - (filter[si][pi+1][o+i] - filter[si][pi][o+i])}; - mTable[idx++] = static_cast(spDelta); + for(size_t i{0};i < m;++i) + { + const double scDelta{filter[si+1][pi][o+i] - filter[si][pi][o+i]}; + mTable[idx++] = static_cast(scDelta); + } + + if(pi < BSincPhaseCount-1) + { + for(size_t i{0};i < m;++i) + { + const double spDelta{(filter[si+1][pi+1][o+i]-filter[si+1][pi][o+i]) - + (filter[si][pi+1][o+i]-filter[si][pi][o+i])}; + mTable[idx++] = static_cast(spDelta); + } + } + else + { + mTable[idx++] = static_cast((0.0 - filter[si+1][pi][o]) - + (0.0 - filter[si][pi][o])); + for(size_t i{1};i < m;++i) + { + const double spDelta{(filter[si+1][0][o+i-1] - filter[si+1][pi][o+i]) - + (filter[si][0][o+i-1] - filter[si][pi][o+i])}; + mTable[idx++] = static_cast(spDelta); + } + } } } + else + { + /* The last scale index doesn't have scale-related deltas. */ + for(size_t i{0};i < BSincPhaseCount*m*2;++i) + mTable[idx++] = 0.0f; + } } assert(idx == hdr.total_size); } - constexpr const BSincHeader &getHeader() const noexcept { return hdr; } - constexpr const float *getTable() const noexcept { return &mTable.front(); } + [[nodiscard]] constexpr auto getHeader() const noexcept -> const BSincHeader& { return hdr; } + [[nodiscard]] constexpr auto getTable() const noexcept { return al::span{mTable}; } }; const BSincFilterArray bsinc12_filter{}; @@ -255,7 +287,7 @@ constexpr BSincTable GenerateBSincTable(const T &filter) BSincTable ret{}; const BSincHeader &hdr = filter.getHeader(); ret.scaleBase = static_cast(hdr.scaleBase); - ret.scaleRange = static_cast(1.0 / hdr.scaleRange); + ret.scaleRange = static_cast(1.0 / (1.0 - hdr.scaleBase)); for(size_t i{0};i < BSincScaleCount;++i) ret.m[i] = ((hdr.a[i]*2) + 3) & ~3u; ret.filterOffset[0] = 0; diff --git a/3rdparty/openal/core/bsinc_tables.h b/3rdparty/openal/core/bsinc_tables.h index aca4b274420a..59110d62427f 100644 --- a/3rdparty/openal/core/bsinc_tables.h +++ b/3rdparty/openal/core/bsinc_tables.h @@ -1,17 +1,20 @@ #ifndef CORE_BSINC_TABLES_H #define CORE_BSINC_TABLES_H -#include "bsinc_defs.h" +#include +#include "alspan.h" +#include "bsinc_defs.h" +#include "opthelpers.h" struct BSincTable { float scaleBase, scaleRange; - unsigned int m[BSincScaleCount]; - unsigned int filterOffset[BSincScaleCount]; - const float *Tab; + std::array m; + std::array filterOffset; + al::span Tab; }; -extern const BSincTable gBSinc12; -extern const BSincTable gBSinc24; +DECL_HIDDEN extern const BSincTable gBSinc12; +DECL_HIDDEN extern const BSincTable gBSinc24; #endif /* CORE_BSINC_TABLES_H */ diff --git a/3rdparty/openal/core/buffer_storage.cpp b/3rdparty/openal/core/buffer_storage.cpp index 98ca2c1b3fe5..780e388adeb7 100644 --- a/3rdparty/openal/core/buffer_storage.cpp +++ b/3rdparty/openal/core/buffer_storage.cpp @@ -3,79 +3,3 @@ #include "buffer_storage.h" -#include - - -const char *NameFromFormat(FmtType type) noexcept -{ - switch(type) - { - case FmtUByte: return "UInt8"; - case FmtShort: return "Int16"; - case FmtFloat: return "Float"; - case FmtDouble: return "Double"; - case FmtMulaw: return "muLaw"; - case FmtAlaw: return "aLaw"; - case FmtIMA4: return "IMA4 ADPCM"; - case FmtMSADPCM: return "MS ADPCM"; - } - return ""; -} - -const char *NameFromFormat(FmtChannels channels) noexcept -{ - switch(channels) - { - case FmtMono: return "Mono"; - case FmtStereo: return "Stereo"; - case FmtRear: return "Rear"; - case FmtQuad: return "Quadraphonic"; - case FmtX51: return "Surround 5.1"; - case FmtX61: return "Surround 6.1"; - case FmtX71: return "Surround 7.1"; - case FmtBFormat2D: return "B-Format 2D"; - case FmtBFormat3D: return "B-Format 3D"; - case FmtUHJ2: return "UHJ2"; - case FmtUHJ3: return "UHJ3"; - case FmtUHJ4: return "UHJ4"; - case FmtSuperStereo: return "Super Stereo"; - } - return ""; -} - -uint BytesFromFmt(FmtType type) noexcept -{ - switch(type) - { - case FmtUByte: return sizeof(uint8_t); - case FmtShort: return sizeof(int16_t); - case FmtFloat: return sizeof(float); - case FmtDouble: return sizeof(double); - case FmtMulaw: return sizeof(uint8_t); - case FmtAlaw: return sizeof(uint8_t); - case FmtIMA4: break; - case FmtMSADPCM: break; - } - return 0; -} - -uint ChannelsFromFmt(FmtChannels chans, uint ambiorder) noexcept -{ - switch(chans) - { - case FmtMono: return 1; - case FmtStereo: return 2; - case FmtRear: return 2; - case FmtQuad: return 4; - case FmtX51: return 6; - case FmtX61: return 7; - case FmtX71: return 8; - case FmtBFormat2D: return (ambiorder*2) + 1; - case FmtBFormat3D: return (ambiorder+1) * (ambiorder+1); - case FmtUHJ2: return 2; - case FmtUHJ3: return 3; - case FmtUHJ4: return 4; - case FmtSuperStereo: return 2; - } - return 0; -} diff --git a/3rdparty/openal/core/buffer_storage.h b/3rdparty/openal/core/buffer_storage.h index d8ab0b670d02..3ffc1f0be289 100644 --- a/3rdparty/openal/core/buffer_storage.h +++ b/3rdparty/openal/core/buffer_storage.h @@ -1,62 +1,15 @@ #ifndef CORE_BUFFER_STORAGE_H #define CORE_BUFFER_STORAGE_H -#include #include -#include "alnumeric.h" #include "alspan.h" #include "ambidefs.h" +#include "storage_formats.h" using uint = unsigned int; -/* Storable formats */ -enum FmtType : unsigned char { - FmtUByte, - FmtShort, - FmtFloat, - FmtDouble, - FmtMulaw, - FmtAlaw, - FmtIMA4, - FmtMSADPCM, -}; -enum FmtChannels : unsigned char { - FmtMono, - FmtStereo, - FmtRear, - FmtQuad, - FmtX51, /* (WFX order) */ - FmtX61, /* (WFX order) */ - FmtX71, /* (WFX order) */ - FmtBFormat2D, - FmtBFormat3D, - FmtUHJ2, /* 2-channel UHJ, aka "BHJ", stereo-compatible */ - FmtUHJ3, /* 3-channel UHJ, aka "THJ" */ - FmtUHJ4, /* 4-channel UHJ, aka "PHJ" */ - FmtSuperStereo, /* Stereo processed with Super Stereo. */ -}; - -enum class AmbiLayout : unsigned char { - FuMa, - ACN, -}; -enum class AmbiScaling : unsigned char { - FuMa, - SN3D, - N3D, - UHJ, -}; - -const char *NameFromFormat(FmtType type) noexcept; -const char *NameFromFormat(FmtChannels channels) noexcept; - -uint BytesFromFmt(FmtType type) noexcept; -uint ChannelsFromFmt(FmtChannels chans, uint ambiorder) noexcept; -inline uint FrameSizeFromFmt(FmtChannels chans, FmtType type, uint ambiorder) noexcept -{ return ChannelsFromFmt(chans, ambiorder) * BytesFromFmt(type); } - constexpr bool IsBFormat(FmtChannels chans) noexcept { return chans == FmtBFormat2D || chans == FmtBFormat3D; } @@ -79,7 +32,7 @@ constexpr bool Is2DAmbisonic(FmtChannels chans) noexcept } -using CallbackType = int(*)(void*, void*, int); +using CallbackType = int(*)(void*, void*, int) noexcept; struct BufferStorage { CallbackType mCallback{nullptr}; @@ -97,19 +50,20 @@ struct BufferStorage { AmbiScaling mAmbiScaling{AmbiScaling::FuMa}; uint mAmbiOrder{0u}; - inline uint bytesFromFmt() const noexcept { return BytesFromFmt(mType); } - inline uint channelsFromFmt() const noexcept + [[nodiscard]] auto bytesFromFmt() const noexcept -> uint { return BytesFromFmt(mType); } + [[nodiscard]] auto channelsFromFmt() const noexcept -> uint { return ChannelsFromFmt(mChannels, mAmbiOrder); } - inline uint frameSizeFromFmt() const noexcept { return channelsFromFmt() * bytesFromFmt(); } + [[nodiscard]] auto frameSizeFromFmt() const noexcept -> uint + { return channelsFromFmt() * bytesFromFmt(); } - inline uint blockSizeFromFmt() const noexcept + [[nodiscard]] auto blockSizeFromFmt() const noexcept -> uint { if(mType == FmtIMA4) return ((mBlockAlign-1)/2 + 4) * channelsFromFmt(); if(mType == FmtMSADPCM) return ((mBlockAlign-2)/2 + 7) * channelsFromFmt(); return frameSizeFromFmt(); }; - inline bool isBFormat() const noexcept { return IsBFormat(mChannels); } + [[nodiscard]] auto isBFormat() const noexcept -> bool { return IsBFormat(mChannels); } }; #endif /* CORE_BUFFER_STORAGE_H */ diff --git a/3rdparty/openal/core/bufferline.h b/3rdparty/openal/core/bufferline.h index 8b445f3ff58a..309fb778f841 100644 --- a/3rdparty/openal/core/bufferline.h +++ b/3rdparty/openal/core/bufferline.h @@ -9,7 +9,7 @@ * more memory and are harder on cache, while smaller values may need more * iterations for mixing. */ -constexpr int BufferLineSize{1024}; +inline constexpr size_t BufferLineSize{1024}; using FloatBufferLine = std::array; using FloatBufferSpan = al::span; diff --git a/3rdparty/openal/core/context.cpp b/3rdparty/openal/core/context.cpp index 2ebbc7b13ed8..68f6dc38d89e 100644 --- a/3rdparty/openal/core/context.cpp +++ b/3rdparty/openal/core/context.cpp @@ -2,6 +2,7 @@ #include "config.h" #include +#include #include #include #include @@ -26,58 +27,22 @@ ContextBase::ContextBase(DeviceBase *device) : mDevice{device} ContextBase::~ContextBase() { - size_t count{0}; - ContextProps *cprops{mParams.ContextUpdate.exchange(nullptr, std::memory_order_relaxed)}; - if(cprops) - { - ++count; - delete cprops; - } - cprops = mFreeContextProps.exchange(nullptr, std::memory_order_acquire); - while(cprops) - { - std::unique_ptr old{cprops}; - cprops = old->next.load(std::memory_order_relaxed); - ++count; - } - TRACE("Freed %zu context property object%s\n", count, (count==1)?"":"s"); - - count = 0; - EffectSlotProps *eprops{mFreeEffectslotProps.exchange(nullptr, std::memory_order_acquire)}; - while(eprops) - { - std::unique_ptr old{eprops}; - eprops = old->next.load(std::memory_order_relaxed); - ++count; - } - TRACE("Freed %zu AuxiliaryEffectSlot property object%s\n", count, (count==1)?"":"s"); - - if(EffectSlotArray *curarray{mActiveAuxSlots.exchange(nullptr, std::memory_order_relaxed)}) - { - std::destroy_n(curarray->end(), curarray->size()); - delete curarray; - } - - delete mVoices.exchange(nullptr, std::memory_order_relaxed); + mActiveAuxSlots.store(nullptr, std::memory_order_relaxed); + mVoices.store(nullptr, std::memory_order_relaxed); if(mAsyncEvents) { - count = 0; - auto evt_vec = mAsyncEvents->getReadVector(); - if(evt_vec.first.len > 0) + size_t count{0}; + for(auto &evt : mAsyncEvents->getReadVector()) { - std::destroy_n(std::launder(reinterpret_cast(evt_vec.first.buf)), - evt_vec.first.len); - count += evt_vec.first.len; - } - if(evt_vec.second.len > 0) - { - std::destroy_n(std::launder(reinterpret_cast(evt_vec.second.buf)), - evt_vec.second.len); - count += evt_vec.second.len; + if(evt.len > 0) + { + std::destroy_n(std::launder(reinterpret_cast(evt.buf)), evt.len); + count += evt.len; + } } if(count > 0) - TRACE("Destructed %zu orphaned event%s\n", count, (count==1)?"":"s"); + TRACE("Destructed {} orphaned event{}", count, (count==1)?"":"s"); mAsyncEvents->readAdvance(count); } } @@ -85,85 +50,131 @@ ContextBase::~ContextBase() void ContextBase::allocVoiceChanges() { - constexpr size_t clustersize{128}; + static constexpr size_t clustersize{std::tuple_size_v}; + + VoiceChangeCluster clusterptr{std::make_unique()}; + const auto cluster = al::span{*clusterptr}; - VoiceChangeCluster cluster{std::make_unique(clustersize)}; for(size_t i{1};i < clustersize;++i) cluster[i-1].mNext.store(std::addressof(cluster[i]), std::memory_order_relaxed); cluster[clustersize-1].mNext.store(mVoiceChangeTail, std::memory_order_relaxed); - mVoiceChangeClusters.emplace_back(std::move(cluster)); - mVoiceChangeTail = mVoiceChangeClusters.back().get(); + mVoiceChangeClusters.emplace_back(std::move(clusterptr)); + mVoiceChangeTail = mVoiceChangeClusters.back()->data(); } void ContextBase::allocVoiceProps() { - constexpr size_t clustersize{32}; + static constexpr size_t clustersize{std::tuple_size_v}; - TRACE("Increasing allocated voice properties to %zu\n", + TRACE("Increasing allocated voice properties to {}", (mVoicePropClusters.size()+1) * clustersize); - VoicePropsCluster cluster{std::make_unique(clustersize)}; + auto clusterptr = std::make_unique(); + auto cluster = al::span{*clusterptr}; for(size_t i{1};i < clustersize;++i) cluster[i-1].next.store(std::addressof(cluster[i]), std::memory_order_relaxed); - mVoicePropClusters.emplace_back(std::move(cluster)); + mVoicePropClusters.emplace_back(std::move(clusterptr)); VoicePropsItem *oldhead{mFreeVoiceProps.load(std::memory_order_acquire)}; do { - mVoicePropClusters.back()[clustersize-1].next.store(oldhead, std::memory_order_relaxed); - } while(mFreeVoiceProps.compare_exchange_weak(oldhead, mVoicePropClusters.back().get(), + mVoicePropClusters.back()->back().next.store(oldhead, std::memory_order_relaxed); + } while(mFreeVoiceProps.compare_exchange_weak(oldhead, mVoicePropClusters.back()->data(), std::memory_order_acq_rel, std::memory_order_acquire) == false); } void ContextBase::allocVoices(size_t addcount) { - constexpr size_t clustersize{32}; + static constexpr size_t clustersize{std::tuple_size_v}; /* Convert element count to cluster count. */ addcount = (addcount+(clustersize-1)) / clustersize; + if(!addcount) + { + if(!mVoiceClusters.empty()) + return; + ++addcount; + } + if(addcount >= std::numeric_limits::max()/clustersize - mVoiceClusters.size()) throw std::runtime_error{"Allocating too many voices"}; const size_t totalcount{(mVoiceClusters.size()+addcount) * clustersize}; - TRACE("Increasing allocated voices to %zu\n", totalcount); + TRACE("Increasing allocated voices to {}", totalcount); - auto newarray = VoiceArray::Create(totalcount); while(addcount) { - mVoiceClusters.emplace_back(std::make_unique(clustersize)); + mVoiceClusters.emplace_back(std::make_unique()); --addcount; } + auto newarray = VoiceArray::Create(totalcount); auto voice_iter = newarray->begin(); for(VoiceCluster &cluster : mVoiceClusters) - { - for(size_t i{0};i < clustersize;++i) - *(voice_iter++) = &cluster[i]; - } + voice_iter = std::transform(cluster->begin(), cluster->end(), voice_iter, + [](Voice &voice) noexcept -> Voice* { return &voice; }); - if(auto *oldvoices = mVoices.exchange(newarray.release(), std::memory_order_acq_rel)) - { - mDevice->waitForMix(); - delete oldvoices; - } + if(auto oldvoices = mVoices.exchange(std::move(newarray), std::memory_order_acq_rel)) + std::ignore = mDevice->waitForMix(); } +void ContextBase::allocEffectSlotProps() +{ + static constexpr size_t clustersize{std::tuple_size_v}; + + TRACE("Increasing allocated effect slot properties to {}", + (mEffectSlotPropClusters.size()+1) * clustersize); + + auto clusterptr = std::make_unique(); + auto cluster = al::span{*clusterptr}; + for(size_t i{1};i < clustersize;++i) + cluster[i-1].next.store(std::addressof(cluster[i]), std::memory_order_relaxed); + auto *newcluster = mEffectSlotPropClusters.emplace_back(std::move(clusterptr)).get(); + + EffectSlotProps *oldhead{mFreeEffectSlotProps.load(std::memory_order_acquire)}; + do { + newcluster->back().next.store(oldhead, std::memory_order_relaxed); + } while(mFreeEffectSlotProps.compare_exchange_weak(oldhead, newcluster->data(), + std::memory_order_acq_rel, std::memory_order_acquire) == false); +} + EffectSlot *ContextBase::getEffectSlot() { - for(auto& cluster : mEffectSlotClusters) + for(auto& clusterptr : mEffectSlotClusters) { - for(size_t i{0};i < EffectSlotClusterSize;++i) - { - if(!cluster[i].InUse) - return &cluster[i]; - } + const auto cluster = al::span{*clusterptr}; + auto iter = std::find_if_not(cluster.begin(), cluster.end(), + std::mem_fn(&EffectSlot::InUse)); + if(iter != cluster.end()) return al::to_address(iter); } - if(1 >= std::numeric_limits::max()/EffectSlotClusterSize - mEffectSlotClusters.size()) + auto clusterptr = std::make_unique(); + if(1 >= std::numeric_limits::max()/clusterptr->size() - mEffectSlotClusters.size()) throw std::runtime_error{"Allocating too many effect slots"}; - const size_t totalcount{(mEffectSlotClusters.size()+1) * EffectSlotClusterSize}; - TRACE("Increasing allocated effect slots to %zu\n", totalcount); + const size_t totalcount{(mEffectSlotClusters.size()+1) * clusterptr->size()}; + TRACE("Increasing allocated effect slots to {}", totalcount); - mEffectSlotClusters.emplace_back(std::make_unique(EffectSlotClusterSize)); - return getEffectSlot(); + mEffectSlotClusters.emplace_back(std::move(clusterptr)); + return mEffectSlotClusters.back()->data(); +} + + +void ContextBase::allocContextProps() +{ + static constexpr size_t clustersize{std::tuple_size_v}; + + TRACE("Increasing allocated context properties to {}", + (mContextPropClusters.size()+1) * clustersize); + + auto clusterptr = std::make_unique(); + auto cluster = al::span{*clusterptr}; + for(size_t i{1};i < clustersize;++i) + cluster[i-1].next.store(std::addressof(cluster[i]), std::memory_order_relaxed); + auto *newcluster = mContextPropClusters.emplace_back(std::move(clusterptr)).get(); + + ContextProps *oldhead{mFreeContextProps.load(std::memory_order_acquire)}; + do { + newcluster->back().next.store(oldhead, std::memory_order_relaxed); + } while(mFreeContextProps.compare_exchange_weak(oldhead, newcluster->data(), + std::memory_order_acq_rel, std::memory_order_acquire) == false); } diff --git a/3rdparty/openal/core/context.h b/3rdparty/openal/core/context.h index ccb7dd3bfbec..7d98ee8f769c 100644 --- a/3rdparty/openal/core/context.h +++ b/3rdparty/openal/core/context.h @@ -1,6 +1,8 @@ #ifndef CORE_CONTEXT_H #define CORE_CONTEXT_H +#include "config.h" + #include #include #include @@ -9,11 +11,11 @@ #include #include -#include "almalloc.h" #include "alsem.h" #include "alspan.h" #include "async_event.h" #include "atomic.h" +#include "flexarray.h" #include "opthelpers.h" #include "vecmat.h" @@ -26,9 +28,9 @@ struct VoiceChange; struct VoicePropsItem; -constexpr float SpeedOfSoundMetersPerSec{343.3f}; +inline constexpr float SpeedOfSoundMetersPerSec{343.3f}; -constexpr float AirAbsorbGainHF{0.99426f}; /* -0.05dB */ +inline constexpr float AirAbsorbGainHF{0.99426f}; /* -0.05dB */ enum class DistanceModel : unsigned char { Disable, @@ -52,21 +54,22 @@ struct ContextProps { float DopplerFactor; float DopplerVelocity; float SpeedOfSound; +#if ALSOFT_EAX + float DistanceFactor; +#endif bool SourceDistanceModel; DistanceModel mDistanceModel; - std::atomic next; - - DEF_NEWDEL(ContextProps) + std::atomic next{}; }; struct ContextParams { /* Pointer to the most recent property values that are awaiting an update. */ std::atomic ContextUpdate{nullptr}; - alu::Vector Position{}; + alu::Vector Position; alu::Matrix Matrix{alu::Matrix::Identity()}; - alu::Vector Velocity{}; + alu::Vector Velocity; float Gain{1.0f}; float MetersPerUnit{1.0f}; @@ -85,7 +88,7 @@ struct ContextBase { /* Counter for the pre-mixing updates, in 31.1 fixed point (lowest bit * indicates if updates are currently happening). */ - RefCount mUpdateCount{0u}; + std::atomic mUpdateCount{0u}; std::atomic mHoldUpdates{false}; std::atomic mStopVoicesOnDisconnect{true}; @@ -96,7 +99,7 @@ struct ContextBase { */ std::atomic mFreeContextProps{nullptr}; std::atomic mFreeVoiceProps{nullptr}; - std::atomic mFreeEffectslotProps{nullptr}; + std::atomic mFreeEffectSlotProps{nullptr}; /* The voice change tail is the beginning of the "free" elements, up to and * *excluding* the current. If tail==current, there's no free elements and @@ -108,21 +111,22 @@ struct ContextBase { void allocVoiceChanges(); void allocVoiceProps(); - + void allocEffectSlotProps(); + void allocContextProps(); ContextParams mParams; using VoiceArray = al::FlexArray; - std::atomic mVoices{}; + al::atomic_unique_ptr mVoices; std::atomic mActiveVoiceCount{}; void allocVoices(size_t addcount); - al::span getVoicesSpan() const noexcept + [[nodiscard]] auto getVoicesSpan() const noexcept -> al::span { return {mVoices.load(std::memory_order_relaxed)->data(), mActiveVoiceCount.load(std::memory_order_relaxed)}; } - al::span getVoicesSpanAcquired() const noexcept + [[nodiscard]] auto getVoicesSpanAcquired() const noexcept -> al::span { return {mVoices.load(std::memory_order_acquire)->data(), mActiveVoiceCount.load(std::memory_order_acquire)}; @@ -130,7 +134,11 @@ struct ContextBase { using EffectSlotArray = al::FlexArray; - std::atomic mActiveAuxSlots{nullptr}; + /* This array is split in half. The front half is the list of activated + * effect slots as set by the app, and the back half is the same list but + * sorted to ensure later effect slots are fed by earlier ones. + */ + al::atomic_unique_ptr mActiveAuxSlots; std::thread mEventThread; al::semaphore mEventSem; @@ -143,27 +151,35 @@ struct ContextBase { * However, to avoid allocating each object individually, they're allocated * in clusters that are stored in a vector for easy automatic cleanup. */ - using VoiceChangeCluster = std::unique_ptr; + using VoiceChangeCluster = std::unique_ptr>; std::vector mVoiceChangeClusters; - using VoiceCluster = std::unique_ptr; + using VoiceCluster = std::unique_ptr>; std::vector mVoiceClusters; - using VoicePropsCluster = std::unique_ptr; + using VoicePropsCluster = std::unique_ptr>; std::vector mVoicePropClusters; - static constexpr size_t EffectSlotClusterSize{4}; EffectSlot *getEffectSlot(); - using EffectSlotCluster = std::unique_ptr; + using EffectSlotCluster = std::unique_ptr>; std::vector mEffectSlotClusters; + using EffectSlotPropsCluster = std::unique_ptr>; + std::vector mEffectSlotPropClusters; + + /* This could be greater than 2, but there should be no way there can be + * more than two context property updates in use simultaneously. + */ + using ContextPropsCluster = std::unique_ptr>; + std::vector mContextPropClusters; + - ContextBase(DeviceBase *device); + explicit ContextBase(DeviceBase *device); ContextBase(const ContextBase&) = delete; ContextBase& operator=(const ContextBase&) = delete; - ~ContextBase(); + virtual ~ContextBase(); }; #endif /* CORE_CONTEXT_H */ diff --git a/3rdparty/openal/core/converter.cpp b/3rdparty/openal/core/converter.cpp index dea31bd57f6b..97fffc161b82 100644 --- a/3rdparty/openal/core/converter.cpp +++ b/3rdparty/openal/core/converter.cpp @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include "albit.h" #include "alnumeric.h" @@ -24,43 +24,46 @@ static_assert((BufferLineSize-1)/MaxPitch > 0, "MaxPitch is too large for Buffer static_assert((INT_MAX>>MixerFracBits)/MaxPitch > BufferLineSize, "MaxPitch and/or BufferLineSize are too large for MixerFracBits!"); -/* Base template left undefined. Should be marked =delete, but Clang 3.8.1 - * chokes on that given the inline specializations. - */ template -inline float LoadSample(DevFmtType_t val) noexcept; +constexpr float LoadSample(DevFmtType_t val) noexcept = delete; -template<> inline float LoadSample(DevFmtType_t val) noexcept -{ return val * (1.0f/128.0f); } -template<> inline float LoadSample(DevFmtType_t val) noexcept -{ return val * (1.0f/32768.0f); } -template<> inline float LoadSample(DevFmtType_t val) noexcept +template<> constexpr float LoadSample(DevFmtType_t val) noexcept +{ return float(val) * (1.0f/128.0f); } +template<> constexpr float LoadSample(DevFmtType_t val) noexcept +{ return float(val) * (1.0f/32768.0f); } +template<> constexpr float LoadSample(DevFmtType_t val) noexcept { return static_cast(val) * (1.0f/2147483648.0f); } -template<> inline float LoadSample(DevFmtType_t val) noexcept +template<> constexpr float LoadSample(DevFmtType_t val) noexcept { return val; } -template<> inline float LoadSample(DevFmtType_t val) noexcept +template<> constexpr float LoadSample(DevFmtType_t val) noexcept { return LoadSample(static_cast(val - 128)); } -template<> inline float LoadSample(DevFmtType_t val) noexcept +template<> constexpr float LoadSample(DevFmtType_t val) noexcept { return LoadSample(static_cast(val - 32768)); } -template<> inline float LoadSample(DevFmtType_t val) noexcept +template<> constexpr float LoadSample(DevFmtType_t val) noexcept { return LoadSample(static_cast(val - 2147483648u)); } template -inline void LoadSampleArray(float *RESTRICT dst, const void *src, const size_t srcstep, - const size_t samples) noexcept +inline void LoadSampleArray(const al::span dst, const void *src, const size_t channel, + const size_t srcstep) noexcept { - const DevFmtType_t *ssrc = static_cast*>(src); - for(size_t i{0u};i < samples;i++) - dst[i] = LoadSample(ssrc[i*srcstep]); + assert(channel < srcstep); + const auto srcspan = al::span{static_cast*>(src), dst.size()*srcstep}; + auto ssrc = srcspan.cbegin(); + std::generate(dst.begin(), dst.end(), [&ssrc,channel,srcstep] + { + const float ret{LoadSample(ssrc[channel])}; + ssrc += ptrdiff_t(srcstep); + return ret; + }); } -void LoadSamples(float *dst, const void *src, const size_t srcstep, const DevFmtType srctype, - const size_t samples) noexcept +void LoadSamples(const al::span dst, const void *src, const size_t channel, + const size_t srcstep, const DevFmtType srctype) noexcept { #define HANDLE_FMT(T) \ - case T: LoadSampleArray(dst, src, srcstep, samples); break + case T: LoadSampleArray(dst, src, channel, srcstep); break switch(srctype) { HANDLE_FMT(DevFmtByte); @@ -81,11 +84,11 @@ inline DevFmtType_t StoreSample(float) noexcept; template<> inline float StoreSample(float val) noexcept { return val; } template<> inline int32_t StoreSample(float val) noexcept -{ return fastf2i(clampf(val*2147483648.0f, -2147483648.0f, 2147483520.0f)); } +{ return fastf2i(std::clamp(val*2147483648.0f, -2147483648.0f, 2147483520.0f)); } template<> inline int16_t StoreSample(float val) noexcept -{ return static_cast(fastf2i(clampf(val*32768.0f, -32768.0f, 32767.0f))); } +{ return static_cast(fastf2i(std::clamp(val*32768.0f, -32768.0f, 32767.0f))); } template<> inline int8_t StoreSample(float val) noexcept -{ return static_cast(fastf2i(clampf(val*128.0f, -128.0f, 127.0f))); } +{ return static_cast(fastf2i(std::clamp(val*128.0f, -128.0f, 127.0f))); } /* Define unsigned output variations. */ template<> inline uint32_t StoreSample(float val) noexcept @@ -96,20 +99,25 @@ template<> inline uint8_t StoreSample(float val) noexcept { return static_cast(StoreSample(val) + 128); } template -inline void StoreSampleArray(void *dst, const float *RESTRICT src, const size_t dststep, - const size_t samples) noexcept +inline void StoreSampleArray(void *dst, const al::span src, const size_t channel, + const size_t dststep) noexcept { - DevFmtType_t *sdst = static_cast*>(dst); - for(size_t i{0u};i < samples;i++) - sdst[i*dststep] = StoreSample(src[i]); + assert(channel < dststep); + const auto dstspan = al::span{static_cast*>(dst), src.size()*dststep}; + auto sdst = dstspan.begin(); + std::for_each(src.cbegin(), src.cend(), [&sdst,channel,dststep](const float in) + { + sdst[channel] = StoreSample(in); + sdst += ptrdiff_t(dststep); + }); } -void StoreSamples(void *dst, const float *src, const size_t dststep, const DevFmtType dsttype, - const size_t samples) noexcept +void StoreSamples(void *dst, const al::span src, const size_t channel, + const size_t dststep, const DevFmtType dsttype) noexcept { #define HANDLE_FMT(T) \ - case T: StoreSampleArray(dst, src, dststep, samples); break + case T: StoreSampleArray(dst, src, channel, dststep); break switch(dsttype) { HANDLE_FMT(DevFmtByte); @@ -125,30 +133,35 @@ void StoreSamples(void *dst, const float *src, const size_t dststep, const DevFm template -void Mono2Stereo(float *RESTRICT dst, const void *src, const size_t frames) noexcept +void Mono2Stereo(const al::span dst, const void *src) noexcept { - const DevFmtType_t *ssrc = static_cast*>(src); - for(size_t i{0u};i < frames;i++) - dst[i*2 + 1] = dst[i*2 + 0] = LoadSample(ssrc[i]) * 0.707106781187f; + const auto srcspan = al::span{static_cast*>(src), dst.size()>>1}; + auto sdst = dst.begin(); + std::for_each(srcspan.cbegin(), srcspan.cend(), [&sdst](const auto in) + { sdst = std::fill_n(sdst, 2, LoadSample(in)*0.707106781187f); }); } template -void Multi2Mono(uint chanmask, const size_t step, const float scale, float *RESTRICT dst, - const void *src, const size_t frames) noexcept +void Multi2Mono(uint chanmask, const size_t step, const float scale, const al::span dst, + const void *src) noexcept { - const DevFmtType_t *ssrc = static_cast*>(src); - std::fill_n(dst, frames, 0.0f); + const auto srcspan = al::span{static_cast*>(src), step*dst.size()}; + std::fill_n(dst.begin(), dst.size(), 0.0f); for(size_t c{0};chanmask;++c) { if((chanmask&1)) LIKELY { - for(size_t i{0u};i < frames;i++) - dst[i] += LoadSample(ssrc[i*step + c]); + auto ssrc = srcspan.cbegin(); + std::for_each(dst.begin(), dst.end(), [&ssrc,step,c](float &sample) + { + const float s{LoadSample(ssrc[c])}; + ssrc += ptrdiff_t(step); + sample += s; + }); } chanmask >>= 1; } - for(size_t i{0u};i < frames;i++) - dst[i] *= scale; + std::for_each(dst.begin(), dst.end(), [scale](float &sample) noexcept { sample *= scale; }); } } // namespace @@ -156,10 +169,11 @@ void Multi2Mono(uint chanmask, const size_t step, const float scale, float *REST SampleConverterPtr SampleConverter::Create(DevFmtType srcType, DevFmtType dstType, size_t numchans, uint srcRate, uint dstRate, Resampler resampler) { + SampleConverterPtr converter; if(numchans < 1 || srcRate < 1 || dstRate < 1) - return nullptr; + return converter; - SampleConverterPtr converter{new(FamCount(numchans)) SampleConverter{numchans}}; + converter = SampleConverterPtr{new(FamCount(numchans)) SampleConverter{numchans}}; converter->mSrcType = srcType; converter->mDstType = dstType; converter->mSrcTypeSize = BytesFromDevFmt(srcType); @@ -168,19 +182,19 @@ SampleConverterPtr SampleConverter::Create(DevFmtType srcType, DevFmtType dstTyp converter->mSrcPrepCount = MaxResamplerPadding; converter->mFracOffset = 0; for(auto &chan : converter->mChan) - { - const al::span buffer{chan.PrevSamples}; - std::fill(buffer.begin(), buffer.end(), 0.0f); - } + chan.PrevSamples.fill(0.0f); /* Have to set the mixer FPU mode since that's what the resampler code expects. */ FPUCtl mixer_mode{}; - auto step = static_cast( - mind(srcRate*double{MixerFracOne}/dstRate + 0.5, MaxPitch*MixerFracOne)); - converter->mIncrement = maxu(step, 1); + const auto step = std::min(std::round(srcRate*double{MixerFracOne}/dstRate), + MaxPitch*double{MixerFracOne}); + converter->mIncrement = std::max(static_cast(step), 1u); if(converter->mIncrement == MixerFracOne) - converter->mResample = [](const InterpState*, const float *RESTRICT src, uint, const uint, - const al::span dst) { std::copy_n(src, dst.size(), dst.begin()); }; + { + converter->mResample = [](const InterpState*, const al::span src, uint, + const uint, const al::span dst) + { std::copy_n(src.begin()+MaxResamplerEdge, dst.size(), dst.begin()); }; + } else converter->mResample = PrepareResampler(resampler, converter->mIncrement, &converter->mState); @@ -210,24 +224,25 @@ uint SampleConverter::availableOut(uint srcframes) const DataSize64 -= mFracOffset; /* If we have a full prep, we can generate at least one sample. */ - return static_cast(clampu64((DataSize64 + mIncrement-1)/mIncrement, 1, - std::numeric_limits::max())); + return static_cast(std::clamp((DataSize64 + mIncrement-1)/mIncrement, 1_u64, + uint64_t{std::numeric_limits::max()})); } uint SampleConverter::convert(const void **src, uint *srcframes, void *dst, uint dstframes) { - const uint SrcFrameSize{static_cast(mChan.size()) * mSrcTypeSize}; - const uint DstFrameSize{static_cast(mChan.size()) * mDstTypeSize}; + const size_t SrcFrameSize{mChan.size() * mSrcTypeSize}; + const size_t DstFrameSize{mChan.size() * mDstTypeSize}; const uint increment{mIncrement}; - auto SamplesIn = static_cast(*src); uint NumSrcSamples{*srcframes}; + auto SamplesIn = al::span{static_cast(*src), NumSrcSamples*SrcFrameSize}; + auto SamplesOut = al::span{static_cast(dst), dstframes*DstFrameSize}; FPUCtl mixer_mode{}; uint pos{0}; while(pos < dstframes && NumSrcSamples > 0) { const uint prepcount{mSrcPrepCount}; - const uint readable{minu(NumSrcSamples, BufferLineSize - prepcount)}; + const uint readable{std::min(NumSrcSamples, uint{BufferLineSize} - prepcount)}; if(prepcount < MaxResamplerPadding && MaxResamplerPadding-prepcount >= readable) { @@ -235,16 +250,16 @@ uint SampleConverter::convert(const void **src, uint *srcframes, void *dst, uint * what we're given for later. */ for(size_t chan{0u};chan < mChan.size();chan++) - LoadSamples(&mChan[chan].PrevSamples[prepcount], SamplesIn + mSrcTypeSize*chan, - mChan.size(), mSrcType, readable); + LoadSamples(al::span{mChan[chan].PrevSamples}.subspan(prepcount, readable), + SamplesIn.data(), chan, mChan.size(), mSrcType); mSrcPrepCount = prepcount + readable; NumSrcSamples = 0; break; } - float *RESTRICT SrcData{mSrcSamples}; - float *RESTRICT DstData{mDstSamples}; + const auto SrcData = al::span{mSrcSamples}; + const auto DstData = al::span{mDstSamples}; uint DataPosFrac{mFracOffset}; uint64_t DataSize64{prepcount}; DataSize64 += readable; @@ -253,39 +268,36 @@ uint SampleConverter::convert(const void **src, uint *srcframes, void *dst, uint DataSize64 -= DataPosFrac; /* If we have a full prep, we can generate at least one sample. */ - auto DstSize = static_cast( - clampu64((DataSize64 + increment-1)/increment, 1, BufferLineSize)); - DstSize = minu(DstSize, dstframes-pos); + auto DstSize = static_cast(std::clamp((DataSize64 + increment-1)/increment, 1_u64, + uint64_t{BufferLineSize})); + DstSize = std::min(DstSize, dstframes-pos); const uint DataPosEnd{DstSize*increment + DataPosFrac}; const uint SrcDataEnd{DataPosEnd>>MixerFracBits}; assert(prepcount+readable >= SrcDataEnd); - const uint nextprep{minu(prepcount + readable - SrcDataEnd, MaxResamplerPadding)}; + const uint nextprep{std::min(prepcount+readable-SrcDataEnd, MaxResamplerPadding)}; for(size_t chan{0u};chan < mChan.size();chan++) { - const std::byte *SrcSamples{SamplesIn + mSrcTypeSize*chan}; - std::byte *DstSamples = static_cast(dst) + mDstTypeSize*chan; - /* Load the previous samples into the source data first, then the * new samples from the input buffer. */ - std::copy_n(mChan[chan].PrevSamples, prepcount, SrcData); - LoadSamples(SrcData + prepcount, SrcSamples, mChan.size(), mSrcType, readable); + std::copy_n(mChan[chan].PrevSamples.cbegin(), prepcount, SrcData.begin()); + LoadSamples(SrcData.subspan(prepcount, readable), SamplesIn.data(), chan, mChan.size(), + mSrcType); /* Store as many prep samples for next time as possible, given the * number of output samples being generated. */ - std::copy_n(SrcData+SrcDataEnd, nextprep, mChan[chan].PrevSamples); - std::fill(std::begin(mChan[chan].PrevSamples)+nextprep, - std::end(mChan[chan].PrevSamples), 0.0f); + auto previter = std::copy_n(SrcData.begin()+ptrdiff_t(SrcDataEnd), nextprep, + mChan[chan].PrevSamples.begin()); + std::fill(previter, mChan[chan].PrevSamples.end(), 0.0f); /* Now resample, and store the result in the output buffer. */ - mResample(&mState, SrcData+MaxResamplerEdge, DataPosFrac, increment, - {DstData, DstSize}); + mResample(&mState, SrcData, DataPosFrac, increment, DstData.first(DstSize)); - StoreSamples(DstSamples, DstData, mChan.size(), mDstType, DstSize); + StoreSamples(SamplesOut.data(), DstData.first(DstSize), chan, mChan.size(), mDstType); } /* Update the number of prep samples still available, as well as the @@ -295,22 +307,24 @@ uint SampleConverter::convert(const void **src, uint *srcframes, void *dst, uint mFracOffset = DataPosEnd & MixerFracMask; /* Update the src and dst pointers in case there's still more to do. */ - const uint srcread{minu(NumSrcSamples, SrcDataEnd + mSrcPrepCount - prepcount)}; - SamplesIn += SrcFrameSize*srcread; + const uint srcread{std::min(NumSrcSamples, SrcDataEnd + mSrcPrepCount - prepcount)}; + SamplesIn = SamplesIn.subspan(SrcFrameSize*srcread); NumSrcSamples -= srcread; - dst = static_cast(dst) + DstFrameSize*DstSize; + SamplesOut = SamplesOut.subspan(DstFrameSize*DstSize); pos += DstSize; } - *src = SamplesIn; + *src = SamplesIn.data(); *srcframes = NumSrcSamples; return pos; } -uint SampleConverter::convertPlanar(const void **src, uint *srcframes, void **dst, uint dstframes) +uint SampleConverter::convertPlanar(const void **src, uint *srcframes, void *const*dst, uint dstframes) { + const auto srcs = al::span{src, mChan.size()}; + const auto dsts = al::span{dst, mChan.size()}; const uint increment{mIncrement}; uint NumSrcSamples{*srcframes}; @@ -319,7 +333,7 @@ uint SampleConverter::convertPlanar(const void **src, uint *srcframes, void **ds while(pos < dstframes && NumSrcSamples > 0) { const uint prepcount{mSrcPrepCount}; - const uint readable{minu(NumSrcSamples, BufferLineSize - prepcount)}; + const uint readable{std::min(NumSrcSamples, uint{BufferLineSize} - prepcount)}; if(prepcount < MaxResamplerPadding && MaxResamplerPadding-prepcount >= readable) { @@ -328,9 +342,11 @@ uint SampleConverter::convertPlanar(const void **src, uint *srcframes, void **ds */ for(size_t chan{0u};chan < mChan.size();chan++) { - LoadSamples(&mChan[chan].PrevSamples[prepcount], - static_cast(src[chan]), 1, mSrcType, readable); - src[chan] = static_cast(src[chan]) + mSrcTypeSize*readable; + auto samples = al::span{static_cast(srcs[chan]), + NumSrcSamples*size_t{mSrcTypeSize}}; + LoadSamples(al::span{mChan[chan].PrevSamples}.subspan(prepcount, readable), + samples.data(), 0, 1, mSrcType); + srcs[chan] = samples.subspan(size_t{mSrcTypeSize}*readable).data(); } mSrcPrepCount = prepcount + readable; @@ -338,8 +354,8 @@ uint SampleConverter::convertPlanar(const void **src, uint *srcframes, void **ds break; } - float *RESTRICT SrcData{mSrcSamples}; - float *RESTRICT DstData{mDstSamples}; + const auto SrcData = al::span{mSrcSamples}; + const auto DstData = al::span{mDstSamples}; uint DataPosFrac{mFracOffset}; uint64_t DataSize64{prepcount}; DataSize64 += readable; @@ -348,37 +364,37 @@ uint SampleConverter::convertPlanar(const void **src, uint *srcframes, void **ds DataSize64 -= DataPosFrac; /* If we have a full prep, we can generate at least one sample. */ - auto DstSize = static_cast( - clampu64((DataSize64 + increment-1)/increment, 1, BufferLineSize)); - DstSize = minu(DstSize, dstframes-pos); + auto DstSize = static_cast(std::clamp((DataSize64 + increment-1)/increment, 1_u64, + uint64_t{BufferLineSize})); + DstSize = std::min(DstSize, dstframes-pos); const uint DataPosEnd{DstSize*increment + DataPosFrac}; const uint SrcDataEnd{DataPosEnd>>MixerFracBits}; assert(prepcount+readable >= SrcDataEnd); - const uint nextprep{minu(prepcount + readable - SrcDataEnd, MaxResamplerPadding)}; + const uint nextprep{std::min(prepcount+readable-SrcDataEnd, MaxResamplerPadding)}; for(size_t chan{0u};chan < mChan.size();chan++) { /* Load the previous samples into the source data first, then the * new samples from the input buffer. */ - std::copy_n(mChan[chan].PrevSamples, prepcount, SrcData); - LoadSamples(SrcData + prepcount, src[chan], 1, mSrcType, readable); + auto srciter = std::copy_n(mChan[chan].PrevSamples.cbegin(),prepcount,SrcData.begin()); + LoadSamples({srciter, readable}, srcs[chan], 0, 1, mSrcType); /* Store as many prep samples for next time as possible, given the * number of output samples being generated. */ - std::copy_n(SrcData+SrcDataEnd, nextprep, mChan[chan].PrevSamples); - std::fill(std::begin(mChan[chan].PrevSamples)+nextprep, - std::end(mChan[chan].PrevSamples), 0.0f); + auto previter = std::copy_n(SrcData.begin()+ptrdiff_t(SrcDataEnd), nextprep, + mChan[chan].PrevSamples.begin()); + std::fill(previter, mChan[chan].PrevSamples.end(), 0.0f); /* Now resample, and store the result in the output buffer. */ - mResample(&mState, SrcData+MaxResamplerEdge, DataPosFrac, increment, - {DstData, DstSize}); + mResample(&mState, SrcData, DataPosFrac, increment, DstData.first(DstSize)); - std::byte *DstSamples = static_cast(dst[chan]) + pos*mDstTypeSize; - StoreSamples(DstSamples, DstData, 1, mDstType, DstSize); + auto DstSamples = al::span{static_cast(dsts[chan]), + size_t{mDstTypeSize}*dstframes}.subspan(pos*size_t{mDstTypeSize}); + StoreSamples(DstSamples.data(), DstData.first(DstSize), 0, 1, mDstType); } /* Update the number of prep samples still available, as well as the @@ -388,9 +404,13 @@ uint SampleConverter::convertPlanar(const void **src, uint *srcframes, void **ds mFracOffset = DataPosEnd & MixerFracMask; /* Update the src and dst pointers in case there's still more to do. */ - const uint srcread{minu(NumSrcSamples, SrcDataEnd + mSrcPrepCount - prepcount)}; - for(size_t chan{0u};chan < mChan.size();chan++) - src[chan] = static_cast(src[chan]) + mSrcTypeSize*srcread; + const uint srcread{std::min(NumSrcSamples, SrcDataEnd + mSrcPrepCount - prepcount)}; + std::for_each(srcs.begin(), srcs.end(), [this,NumSrcSamples,srcread](const void *&srcref) + { + auto srcspan = al::span{static_cast(srcref), + size_t{mSrcTypeSize}*NumSrcSamples}; + srcref = srcspan.subspan(size_t{mSrcTypeSize}*srcread).data(); + }); NumSrcSamples -= srcread; pos += DstSize; @@ -409,7 +429,7 @@ void ChannelConverter::convert(const void *src, float *dst, uint frames) const const float scale{std::sqrt(1.0f / static_cast(al::popcount(mChanMask)))}; switch(mSrcType) { -#define HANDLE_FMT(T) case T: Multi2Mono(mChanMask, mSrcStep, scale, dst, src, frames); break +#define HANDLE_FMT(T) case T: Multi2Mono(mChanMask, mSrcStep, scale, {dst, frames}, src); break HANDLE_FMT(DevFmtByte); HANDLE_FMT(DevFmtUByte); HANDLE_FMT(DevFmtShort); @@ -424,7 +444,7 @@ void ChannelConverter::convert(const void *src, float *dst, uint frames) const { switch(mSrcType) { -#define HANDLE_FMT(T) case T: Mono2Stereo(dst, src, frames); break +#define HANDLE_FMT(T) case T: Mono2Stereo({dst, frames*2_uz}, src); break HANDLE_FMT(DevFmtByte); HANDLE_FMT(DevFmtUByte); HANDLE_FMT(DevFmtShort); diff --git a/3rdparty/openal/core/converter.h b/3rdparty/openal/core/converter.h index d811b46b42fe..45dc4aa98888 100644 --- a/3rdparty/openal/core/converter.h +++ b/3rdparty/openal/core/converter.h @@ -7,7 +7,9 @@ #include "almalloc.h" #include "devformat.h" +#include "flexarray.h" #include "mixer/defs.h" +#include "resampler_limits.h" using uint = unsigned int; @@ -22,25 +24,25 @@ struct SampleConverter { uint mFracOffset{}; uint mIncrement{}; - InterpState mState{}; + InterpState mState; ResamplerFunc mResample{}; - alignas(16) float mSrcSamples[BufferLineSize]{}; - alignas(16) float mDstSamples[BufferLineSize]{}; + alignas(16) FloatBufferLine mSrcSamples{}; + alignas(16) FloatBufferLine mDstSamples{}; struct ChanSamples { - alignas(16) float PrevSamples[MaxResamplerPadding]; + alignas(16) std::array PrevSamples; }; al::FlexArray mChan; - SampleConverter(size_t numchans) : mChan{numchans} { } + explicit SampleConverter(size_t numchans) : mChan{numchans} { } - uint convert(const void **src, uint *srcframes, void *dst, uint dstframes); - uint convertPlanar(const void **src, uint *srcframes, void **dst, uint dstframes); - uint availableOut(uint srcframes) const; + [[nodiscard]] auto convert(const void **src, uint *srcframes, void *dst, uint dstframes) -> uint; + [[nodiscard]] auto convertPlanar(const void **src, uint *srcframes, void *const*dst, uint dstframes) -> uint; + [[nodiscard]] auto availableOut(uint srcframes) const -> uint; using SampleOffset = std::chrono::duration>; - SampleOffset currentInputDelay() const noexcept + [[nodiscard]] auto currentInputDelay() const noexcept -> SampleOffset { const int64_t prep{int64_t{mSrcPrepCount} - MaxResamplerEdge}; return SampleOffset{(prep< bool { return mChanMask != 0; } void convert(const void *src, float *dst, uint frames) const; }; diff --git a/3rdparty/openal/core/cpu_caps.cpp b/3rdparty/openal/core/cpu_caps.cpp index 1a064cf466a0..6f901d24eac4 100644 --- a/3rdparty/openal/core/cpu_caps.cpp +++ b/3rdparty/openal/core/cpu_caps.cpp @@ -1,5 +1,6 @@ #include "config.h" +#include "config_simd.h" #include "cpu_caps.h" @@ -23,8 +24,6 @@ #include -int CPUCapFlags{0}; - namespace { #if defined(HAVE_GCC_GET_CPUID) \ @@ -111,22 +110,22 @@ std::optional GetCPUInfo() #else /* Assume support for whatever's supported if we can't check for it */ -#if defined(HAVE_SSE4_1) +#if HAVE_SSE4_1 #warning "Assuming SSE 4.1 run-time support!" ret.mCaps |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3 | CPU_CAP_SSE4_1; -#elif defined(HAVE_SSE3) +#elif HAVE_SSE3 #warning "Assuming SSE 3 run-time support!" ret.mCaps |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE3; -#elif defined(HAVE_SSE2) +#elif HAVE_SSE2 #warning "Assuming SSE 2 run-time support!" ret.mCaps |= CPU_CAP_SSE | CPU_CAP_SSE2; -#elif defined(HAVE_SSE) +#elif HAVE_SSE #warning "Assuming SSE run-time support!" ret.mCaps |= CPU_CAP_SSE; #endif #endif /* CAN_GET_CPUID */ -#ifdef HAVE_NEON +#if HAVE_NEON #ifdef __ARM_NEON ret.mCaps |= CPU_CAP_NEON; #elif defined(_WIN32) && (defined(_M_ARM) || defined(_M_ARM64)) diff --git a/3rdparty/openal/core/cpu_caps.h b/3rdparty/openal/core/cpu_caps.h index 0826a49bd12a..31096e015396 100644 --- a/3rdparty/openal/core/cpu_caps.h +++ b/3rdparty/openal/core/cpu_caps.h @@ -5,7 +5,7 @@ #include -extern int CPUCapFlags; +inline int CPUCapFlags{0}; enum { CPU_CAP_SSE = 1<<0, CPU_CAP_SSE2 = 1<<1, diff --git a/3rdparty/openal/core/cubic_defs.h b/3rdparty/openal/core/cubic_defs.h index 33751c9743c6..a382081640f9 100644 --- a/3rdparty/openal/core/cubic_defs.h +++ b/3rdparty/openal/core/cubic_defs.h @@ -1,13 +1,15 @@ #ifndef CORE_CUBIC_DEFS_H #define CORE_CUBIC_DEFS_H +#include + /* The number of distinct phase intervals within the cubic filter tables. */ constexpr unsigned int CubicPhaseBits{5}; constexpr unsigned int CubicPhaseCount{1 << CubicPhaseBits}; struct CubicCoefficients { - float mCoeffs[4]; - float mDeltas[4]; + alignas(16) std::array mCoeffs; + alignas(16) std::array mDeltas; }; #endif /* CORE_CUBIC_DEFS_H */ diff --git a/3rdparty/openal/core/cubic_tables.cpp b/3rdparty/openal/core/cubic_tables.cpp index 5e7aafad6386..305a61eda94c 100644 --- a/3rdparty/openal/core/cubic_tables.cpp +++ b/3rdparty/openal/core/cubic_tables.cpp @@ -2,50 +2,120 @@ #include "cubic_tables.h" #include -#include +#include +#include +#include "alnumbers.h" +#include "alnumeric.h" #include "cubic_defs.h" - +/* These gaussian filter tables are inspired by the gaussian-like filter found + * in the SNES. This is based on the public domain code developed by Near, with + * the help of Ryphecha and nocash, from the nesdev.org forums. + * + * + * + * Additional changes were made here, the most obvious being that it has full + * floating-point precision instead of 11-bit fixed-point, but also an offset + * adjustment for the coefficients to better preserve phase. + */ namespace { -struct SplineFilterArray { - alignas(16) std::array mTable{}; +[[nodiscard]] +auto GetCoeff(double idx) noexcept -> double +{ + const double k{0.5 + idx}; + if(k > 512.0) return 0.0; + const double s{ std::sin(al::numbers::pi*1.280/1024.0 * k)}; + const double t{(std::cos(al::numbers::pi*2.000/1023.0 * k) - 1.0) * 0.50}; + const double u{(std::cos(al::numbers::pi*4.000/1023.0 * k) - 1.0) * 0.08}; + return s * (t + u + 1.0) / k; +} + +} // namespace - constexpr SplineFilterArray() +GaussianTable::GaussianTable() +{ + static constexpr double IndexScale{512.0 / double{CubicPhaseCount*2}}; + /* Fill in the main coefficients. */ + for(std::size_t pi{0};pi < CubicPhaseCount;++pi) { - /* Fill in the main coefficients. */ - for(size_t pi{0};pi < CubicPhaseCount;++pi) - { - const double mu{static_cast(pi) / CubicPhaseCount}; - const double mu2{mu*mu}, mu3{mu2*mu}; - mTable[pi].mCoeffs[0] = static_cast(-0.5*mu3 + mu2 + -0.5*mu); - mTable[pi].mCoeffs[1] = static_cast( 1.5*mu3 + -2.5*mu2 + 1.0); - mTable[pi].mCoeffs[2] = static_cast(-1.5*mu3 + 2.0*mu2 + 0.5*mu); - mTable[pi].mCoeffs[3] = static_cast( 0.5*mu3 + -0.5*mu2); - } - - /* Fill in the coefficient deltas. */ - for(size_t pi{0};pi < CubicPhaseCount-1;++pi) - { - mTable[pi].mDeltas[0] = mTable[pi+1].mCoeffs[0] - mTable[pi].mCoeffs[0]; - mTable[pi].mDeltas[1] = mTable[pi+1].mCoeffs[1] - mTable[pi].mCoeffs[1]; - mTable[pi].mDeltas[2] = mTable[pi+1].mCoeffs[2] - mTable[pi].mCoeffs[2]; - mTable[pi].mDeltas[3] = mTable[pi+1].mCoeffs[3] - mTable[pi].mCoeffs[3]; - } - - const size_t pi{CubicPhaseCount - 1}; - mTable[pi].mDeltas[0] = -mTable[pi].mCoeffs[0]; - mTable[pi].mDeltas[1] = -mTable[pi].mCoeffs[1]; - mTable[pi].mDeltas[2] = 1.0f - mTable[pi].mCoeffs[2]; - mTable[pi].mDeltas[3] = -mTable[pi].mCoeffs[3]; + const double coeff0{GetCoeff(static_cast(CubicPhaseCount + pi)*IndexScale)}; + const double coeff1{GetCoeff(static_cast(pi)*IndexScale)}; + const double coeff2{GetCoeff(static_cast(CubicPhaseCount - pi)*IndexScale)}; + const double coeff3{GetCoeff(static_cast(CubicPhaseCount*2_uz-pi)*IndexScale)}; + + const double scale{1.0 / (coeff0 + coeff1 + coeff2 + coeff3)}; + mTable[pi].mCoeffs[0] = static_cast(coeff0 * scale); + mTable[pi].mCoeffs[1] = static_cast(coeff1 * scale); + mTable[pi].mCoeffs[2] = static_cast(coeff2 * scale); + mTable[pi].mCoeffs[3] = static_cast(coeff3 * scale); } - constexpr auto& getTable() const noexcept { return mTable; } -}; + /* Fill in the coefficient deltas. */ + for(std::size_t pi{0};pi < CubicPhaseCount-1;++pi) + { + mTable[pi].mDeltas[0] = mTable[pi+1].mCoeffs[0] - mTable[pi].mCoeffs[0]; + mTable[pi].mDeltas[1] = mTable[pi+1].mCoeffs[1] - mTable[pi].mCoeffs[1]; + mTable[pi].mDeltas[2] = mTable[pi+1].mCoeffs[2] - mTable[pi].mCoeffs[2]; + mTable[pi].mDeltas[3] = mTable[pi+1].mCoeffs[3] - mTable[pi].mCoeffs[3]; + } -constexpr SplineFilterArray SplineFilter{}; + const std::size_t pi{CubicPhaseCount - 1}; + mTable[pi].mDeltas[0] = 0.0f - mTable[pi].mCoeffs[0]; + mTable[pi].mDeltas[1] = mTable[0].mCoeffs[0] - mTable[pi].mCoeffs[1]; + mTable[pi].mDeltas[2] = mTable[0].mCoeffs[1] - mTable[pi].mCoeffs[2]; + mTable[pi].mDeltas[3] = mTable[0].mCoeffs[2] - mTable[pi].mCoeffs[3]; +} -} // namespace +SplineTable::SplineTable() +{ + /* This filter table is based on a Catmull-Rom spline. It retains more of + * the original high-frequency content, at the cost of increased harmonics. + */ + for(std::size_t pi{0};pi < CubicPhaseCount;++pi) + { + const double mu{static_cast(pi) / double{CubicPhaseCount}}; + const double mu2{mu*mu}, mu3{mu2*mu}; + mTable[pi].mCoeffs[0] = static_cast(-0.5*mu3 + mu2 + -0.5*mu); + mTable[pi].mCoeffs[1] = static_cast( 1.5*mu3 + -2.5*mu2 + 1.0); + mTable[pi].mCoeffs[2] = static_cast(-1.5*mu3 + 2.0*mu2 + 0.5*mu); + mTable[pi].mCoeffs[3] = static_cast( 0.5*mu3 + -0.5*mu2); + } -const CubicTable gCubicSpline{SplineFilter.getTable()}; + for(std::size_t pi{0};pi < CubicPhaseCount-1;++pi) + { + mTable[pi].mDeltas[0] = mTable[pi+1].mCoeffs[0] - mTable[pi].mCoeffs[0]; + mTable[pi].mDeltas[1] = mTable[pi+1].mCoeffs[1] - mTable[pi].mCoeffs[1]; + mTable[pi].mDeltas[2] = mTable[pi+1].mCoeffs[2] - mTable[pi].mCoeffs[2]; + mTable[pi].mDeltas[3] = mTable[pi+1].mCoeffs[3] - mTable[pi].mCoeffs[3]; + } + + const std::size_t pi{CubicPhaseCount - 1}; + mTable[pi].mDeltas[0] = 0.0f - mTable[pi].mCoeffs[0]; + mTable[pi].mDeltas[1] = mTable[0].mCoeffs[0] - mTable[pi].mCoeffs[1]; + mTable[pi].mDeltas[2] = mTable[0].mCoeffs[1] - mTable[pi].mCoeffs[2]; + mTable[pi].mDeltas[3] = mTable[0].mCoeffs[2] - mTable[pi].mCoeffs[3]; +} + + +CubicFilter::CubicFilter() +{ + static constexpr double IndexScale{512.0 / double{sTableSteps*2}}; + /* Only half the coefficients need to be iterated here, since Coeff2 and + * Coeff3 are just Coeff1 and Coeff0 in reverse respectively. + */ + for(size_t i{0};i < sTableSteps/2 + 1;++i) + { + const double coeff0{GetCoeff(static_cast(sTableSteps + i)*IndexScale)}; + const double coeff1{GetCoeff(static_cast(i)*IndexScale)}; + const double coeff2{GetCoeff(static_cast(sTableSteps - i)*IndexScale)}; + const double coeff3{GetCoeff(static_cast(sTableSteps*2_uz - i)*IndexScale)}; + + const double scale{1.0 / (coeff0 + coeff1 + coeff2 + coeff3)}; + mFilter[sTableSteps + i] = static_cast(coeff0 * scale); + mFilter[i] = static_cast(coeff1 * scale); + mFilter[sTableSteps - i] = static_cast(coeff2 * scale); + mFilter[sTableSteps*2 - i] = static_cast(coeff3 * scale); + } +} diff --git a/3rdparty/openal/core/cubic_tables.h b/3rdparty/openal/core/cubic_tables.h index 88097ae2f153..b32764451737 100644 --- a/3rdparty/openal/core/cubic_tables.h +++ b/3rdparty/openal/core/cubic_tables.h @@ -1,17 +1,42 @@ #ifndef CORE_CUBIC_TABLES_H #define CORE_CUBIC_TABLES_H -#include "alspan.h" +#include +#include + #include "cubic_defs.h" +#include "opthelpers.h" -struct CubicTable { - al::span Tab; +struct SIMDALIGN CubicTable { + std::array mTable{}; }; -/* A Catmull-Rom spline. The spline passes through the center two samples, - * ensuring no discontinuity while moving through a series of samples. - */ -extern const CubicTable gCubicSpline; +struct GaussianTable : CubicTable { GaussianTable(); }; +inline const GaussianTable gGaussianFilter; + +struct SplineTable : CubicTable { SplineTable(); }; +inline const SplineTable gSplineFilter; + + +struct CubicFilter { + static constexpr std::size_t sTableBits{8}; + static constexpr std::size_t sTableSteps{1 << sTableBits}; + static constexpr std::size_t sTableMask{sTableSteps - 1}; + + std::array mFilter{}; + + CubicFilter(); + + [[nodiscard]] constexpr + auto getCoeff0(std::size_t i) const noexcept -> float { return mFilter[sTableSteps+i]; } + [[nodiscard]] constexpr + auto getCoeff1(std::size_t i) const noexcept -> float { return mFilter[i]; } + [[nodiscard]] constexpr + auto getCoeff2(std::size_t i) const noexcept -> float { return mFilter[sTableSteps-i]; } + [[nodiscard]] constexpr + auto getCoeff3(std::size_t i) const noexcept -> float { return mFilter[sTableSteps*2-i]; } +}; +inline const CubicFilter gCubicTable; #endif /* CORE_CUBIC_TABLES_H */ diff --git a/3rdparty/openal/core/dbus_wrap.cpp b/3rdparty/openal/core/dbus_wrap.cpp index 4841956686b6..eccfb2c0b716 100644 --- a/3rdparty/openal/core/dbus_wrap.cpp +++ b/3rdparty/openal/core/dbus_wrap.cpp @@ -3,40 +3,40 @@ #include "dbus_wrap.h" -#ifdef HAVE_DYNLOAD +#if HAVE_DYNLOAD #include #include -#include "albit.h" #include "logging.h" void PrepareDBus() { - static constexpr char libname[] = "libdbus-1.so.3"; + const char *libname{"libdbus-1.so.3"}; + + dbus_handle = LoadLib(libname); + if(!dbus_handle) + { + WARN("Failed to load {}", libname); + return; + } auto load_func = [](auto &f, const char *name) -> void - { f = al::bit_cast>(GetSymbol(dbus_handle, name)); }; + { f = reinterpret_cast>(GetSymbol(dbus_handle, name)); }; #define LOAD_FUNC(x) do { \ load_func(p##x, #x); \ if(!p##x) \ { \ - WARN("Failed to load function %s\n", #x); \ + WARN("Failed to load function {}", #x); \ CloseLib(dbus_handle); \ dbus_handle = nullptr; \ return; \ } \ } while(0); - dbus_handle = LoadLib(libname); - if(!dbus_handle) - { - WARN("Failed to load %s\n", libname); - return; - } + DBUS_FUNCTIONS(LOAD_FUNC) -DBUS_FUNCTIONS(LOAD_FUNC) #undef LOAD_FUNC } #endif diff --git a/3rdparty/openal/core/dbus_wrap.h b/3rdparty/openal/core/dbus_wrap.h index 65f08942409e..85a996eb375e 100644 --- a/3rdparty/openal/core/dbus_wrap.h +++ b/3rdparty/openal/core/dbus_wrap.h @@ -7,7 +7,7 @@ #include "dynload.h" -#ifdef HAVE_DYNLOAD +#if HAVE_DYNLOAD #include @@ -63,16 +63,23 @@ inline auto HasDBus() #else constexpr bool HasDBus() noexcept { return true; } -#endif /* HAVE_DYNLOAD */ +#endif namespace dbus { struct Error { Error() { dbus_error_init(&mError); } + Error(const Error&) = delete; + Error(Error&&) = delete; ~Error() { dbus_error_free(&mError); } + + void operator=(const Error&) = delete; + void operator=(Error&&) = delete; + DBusError* operator->() { return &mError; } DBusError &get() { return mError; } + private: DBusError mError{}; }; diff --git a/3rdparty/openal/core/devformat.cpp b/3rdparty/openal/core/devformat.cpp index acdabc4f1f43..fc4a490aeec4 100644 --- a/3rdparty/openal/core/devformat.cpp +++ b/3rdparty/openal/core/devformat.cpp @@ -3,6 +3,11 @@ #include "devformat.h" +#include + +namespace { +using namespace std::string_view_literals; +} // namespace uint BytesFromDevFmt(DevFmtType type) noexcept { @@ -29,39 +34,41 @@ uint ChannelsFromDevFmt(DevFmtChannels chans, uint ambiorder) noexcept case DevFmtX61: return 7; case DevFmtX71: return 8; case DevFmtX714: return 12; + case DevFmtX7144: return 16; case DevFmtX3D71: return 8; case DevFmtAmbi3D: return (ambiorder+1) * (ambiorder+1); } return 0; } -const char *DevFmtTypeString(DevFmtType type) noexcept +auto DevFmtTypeString(DevFmtType type) noexcept -> std::string_view { switch(type) { - case DevFmtByte: return "Int8"; - case DevFmtUByte: return "UInt8"; - case DevFmtShort: return "Int16"; - case DevFmtUShort: return "UInt16"; - case DevFmtInt: return "Int32"; - case DevFmtUInt: return "UInt32"; - case DevFmtFloat: return "Float32"; + case DevFmtByte: return "Int8"sv; + case DevFmtUByte: return "UInt8"sv; + case DevFmtShort: return "Int16"sv; + case DevFmtUShort: return "UInt16"sv; + case DevFmtInt: return "Int32"sv; + case DevFmtUInt: return "UInt32"sv; + case DevFmtFloat: return "Float32"sv; } - return "(unknown type)"; + return "(unknown type)"sv; } -const char *DevFmtChannelsString(DevFmtChannels chans) noexcept +auto DevFmtChannelsString(DevFmtChannels chans) noexcept -> std::string_view { switch(chans) { - case DevFmtMono: return "Mono"; - case DevFmtStereo: return "Stereo"; - case DevFmtQuad: return "Quadraphonic"; - case DevFmtX51: return "5.1 Surround"; - case DevFmtX61: return "6.1 Surround"; - case DevFmtX71: return "7.1 Surround"; - case DevFmtX714: return "7.1.4 Surround"; - case DevFmtX3D71: return "3D7.1 Surround"; - case DevFmtAmbi3D: return "Ambisonic 3D"; + case DevFmtMono: return "Mono"sv; + case DevFmtStereo: return "Stereo"sv; + case DevFmtQuad: return "Quadraphonic"sv; + case DevFmtX51: return "5.1 Surround"sv; + case DevFmtX61: return "6.1 Surround"sv; + case DevFmtX71: return "7.1 Surround"sv; + case DevFmtX714: return "7.1.4 Surround"sv; + case DevFmtX7144: return "7.1.4.4 Surround"sv; + case DevFmtX3D71: return "3D7.1 Surround"sv; + case DevFmtAmbi3D: return "Ambisonic 3D"sv; } - return "(unknown channels)"; + return "(unknown channels)"sv; } diff --git a/3rdparty/openal/core/devformat.h b/3rdparty/openal/core/devformat.h index 485826a397e7..beff40de347a 100644 --- a/3rdparty/openal/core/devformat.h +++ b/3rdparty/openal/core/devformat.h @@ -2,6 +2,8 @@ #define CORE_DEVFORMAT_H #include +#include +#include using uint = unsigned int; @@ -25,6 +27,11 @@ enum Channel : unsigned char { TopBackCenter, TopBackRight, + BottomFrontLeft, + BottomFrontRight, + BottomBackLeft, + BottomBackRight, + Aux0, Aux1, Aux2, @@ -66,12 +73,13 @@ enum DevFmtChannels : unsigned char { DevFmtX61, DevFmtX71, DevFmtX714, + DevFmtX7144, DevFmtX3D71, DevFmtAmbi3D, DevFmtChannelsDefault = DevFmtStereo }; -#define MAX_OUTPUT_CHANNELS 16 +inline constexpr std::size_t MaxOutputChannels{16}; /* DevFmtType traits, providing the type, etc given a DevFmtType. */ template @@ -101,8 +109,8 @@ uint ChannelsFromDevFmt(DevFmtChannels chans, uint ambiorder) noexcept; inline uint FrameSizeFromDevFmt(DevFmtChannels chans, DevFmtType type, uint ambiorder) noexcept { return ChannelsFromDevFmt(chans, ambiorder) * BytesFromDevFmt(type); } -const char *DevFmtTypeString(DevFmtType type) noexcept; -const char *DevFmtChannelsString(DevFmtChannels chans) noexcept; +auto DevFmtTypeString(DevFmtType type) noexcept -> std::string_view; +auto DevFmtChannelsString(DevFmtChannels chans) noexcept -> std::string_view; enum class DevAmbiLayout : bool { FuMa, diff --git a/3rdparty/openal/core/device.cpp b/3rdparty/openal/core/device.cpp index 2766c5e4ea7e..4c2dc2a38864 100644 --- a/3rdparty/openal/core/device.cpp +++ b/3rdparty/openal/core/device.cpp @@ -9,15 +9,9 @@ #include "mastering.h" -al::FlexArray DeviceBase::sEmptyContextArray{0u}; - - -DeviceBase::DeviceBase(DeviceType type) : Type{type}, mContexts{&sEmptyContextArray} +DeviceBase::DeviceBase(DeviceType type) + : Type{type}, mContexts{al::FlexArray::Create(0)} { } -DeviceBase::~DeviceBase() -{ - auto *oldarray = mContexts.exchange(nullptr, std::memory_order_relaxed); - if(oldarray != &sEmptyContextArray) delete oldarray; -} +DeviceBase::~DeviceBase() = default; diff --git a/3rdparty/openal/core/device.h b/3rdparty/openal/core/device.h index b088e130ef04..c01888c8eff3 100644 --- a/3rdparty/openal/core/device.h +++ b/3rdparty/openal/core/device.h @@ -5,9 +5,9 @@ #include #include #include +#include +#include #include -#include -#include #include #include "almalloc.h" @@ -17,6 +17,8 @@ #include "bufferline.h" #include "devformat.h" #include "filters/nfc.h" +#include "flexarray.h" +#include "fmt/core.h" #include "intrusive_ptr.h" #include "mixer/hrtfdefs.h" #include "opthelpers.h" @@ -25,8 +27,10 @@ #include "vector.h" class BFormatDec; +namespace Bs2b { struct bs2b; -struct Compressor; +} // namespace Bs2b +class Compressor; struct ContextBase; struct DirectHrtfState; struct HrtfStore; @@ -34,28 +38,28 @@ struct HrtfStore; using uint = unsigned int; -#define MIN_OUTPUT_RATE 8000 -#define MAX_OUTPUT_RATE 192000 -#define DEFAULT_OUTPUT_RATE 48000 +inline constexpr std::size_t MinOutputRate{8000}; +inline constexpr std::size_t MaxOutputRate{192000}; +inline constexpr std::size_t DefaultOutputRate{48000}; -#define DEFAULT_UPDATE_SIZE 960 /* 20ms */ -#define DEFAULT_NUM_UPDATES 3 +inline constexpr std::size_t DefaultUpdateSize{960}; /* 20ms */ +inline constexpr std::size_t DefaultNumUpdates{3}; -enum class DeviceType : uint8_t { +enum class DeviceType : std::uint8_t { Playback, Capture, Loopback }; -enum class RenderMode : uint8_t { +enum class RenderMode : std::uint8_t { Normal, Pairwise, Hrtf }; -enum class StereoEncoding : uint8_t { +enum class StereoEncoding : std::uint8_t { Basic, Uhj, Hrtf, @@ -77,24 +81,23 @@ struct DistanceComp { static constexpr uint MaxDelay{1024}; struct ChanData { + al::span Buffer; /* Valid size is [0...MaxDelay). */ float Gain{1.0f}; - uint Length{0u}; /* Valid range is [0...MaxDelay). */ - float *Buffer{nullptr}; }; - std::array mChannels; + std::array mChannels; al::FlexArray mSamples; - DistanceComp(size_t count) : mSamples{count} { } + explicit DistanceComp(std::size_t count) : mSamples{count} { } - static std::unique_ptr Create(size_t numsamples) + static std::unique_ptr Create(std::size_t numsamples) { return std::unique_ptr{new(FamCount(numsamples)) DistanceComp{numsamples}}; } DEF_FAM_NEWDEL(DistanceComp, mSamples) }; -constexpr uint InvalidChannelIndex{~0u}; +constexpr auto InvalidChannelIndex = static_cast(~0u); struct BFChannelConfig { float Scale; @@ -112,24 +115,24 @@ struct MixParams { * source is expected to be a 3D ACN/N3D ambisonic buffer, and for each * channel [0...count), the given functor is called with the source channel * index, destination channel index, and the gain for that channel. If the - * destination channel is INVALID_CHANNEL_INDEX, the given source channel - * is not used for output. + * destination channel is InvalidChannelIndex, the given source channel is + * not used for output. */ template void setAmbiMixParams(const MixParams &inmix, const float gainbase, F func) const { - const size_t numIn{inmix.Buffer.size()}; - const size_t numOut{Buffer.size()}; - for(size_t i{0};i < numIn;++i) + const std::size_t numIn{inmix.Buffer.size()}; + const std::size_t numOut{Buffer.size()}; + for(std::size_t i{0};i < numIn;++i) { - auto idx = InvalidChannelIndex; - auto gain = 0.0f; + std::uint8_t idx{InvalidChannelIndex}; + float gain{0.0f}; - for(size_t j{0};j < numOut;++j) + for(std::size_t j{0};j < numOut;++j) { if(AmbiMap[j].Index == inmix.AmbiMap[i].Index) { - idx = static_cast(j); + idx = static_cast(j); gain = AmbiMap[j].Scale * gainbase; break; } @@ -141,7 +144,7 @@ struct MixParams { struct RealMixParams { al::span RemixMap; - std::array ChannelIndex{}; + std::array ChannelIndex{}; al::span Buffer; }; @@ -158,8 +161,6 @@ enum { // Specifies if the DSP is paused at user request DevicePaused, - // Specifies if the device is currently running - DeviceRunning, // Specifies if the output plays directly on/in ears (headphones, headset, // ear buds, etc). @@ -173,18 +174,22 @@ enum { DeviceFlagsCount }; -struct DeviceBase { - /* To avoid extraneous allocations, a 0-sized FlexArray is - * defined globally as a sharable object. - */ - static al::FlexArray sEmptyContextArray; +enum class DeviceState : std::uint8_t { + Unprepared, + Configured, + Playing +}; +/* NOLINTNEXTLINE(clang-analyzer-optin.performance.Padding) */ +struct SIMDALIGN DeviceBase { std::atomic Connected{true}; const DeviceType Type{}; - uint Frequency{}; - uint UpdateSize{}; - uint BufferSize{}; + std::string mDeviceName; + + uint mSampleRate{}; + uint mUpdateSize{}; + uint mBufferSize{}; DevFmtChannels FmtChans{}; DevFmtType FmtType{}; @@ -198,10 +203,9 @@ struct DeviceBase { DevAmbiLayout mAmbiLayout{DevAmbiLayout::Default}; DevAmbiScaling mAmbiScale{DevAmbiScaling::Default}; - std::string DeviceName; - // Device flags - std::bitset Flags{}; + std::bitset Flags; + DeviceState mDeviceState{DeviceState::Unprepared}; uint NumAuxSends{}; @@ -218,35 +222,36 @@ struct DeviceBase { */ NfcFilter mNFCtrlFilter{}; - uint SamplesDone{0u}; - std::chrono::nanoseconds ClockBase{0}; + using seconds32 = std::chrono::duration; + using nanoseconds32 = std::chrono::duration; + + std::atomic mSamplesDone{0u}; + /* Split the clock to avoid a 64-bit atomic for certain 32-bit targets. */ + std::atomic mClockBaseSec{seconds32{}}; + std::atomic mClockBaseNSec{nanoseconds32{}}; std::chrono::nanoseconds FixedLatency{0}; AmbiRotateMatrix mAmbiRotateMatrix{}; AmbiRotateMatrix mAmbiRotateMatrix2{}; /* Temp storage used for mixer processing. */ - static constexpr size_t MixerLineSize{BufferLineSize + DecoderBase::sMaxPadding}; - static constexpr size_t MixerChannelsMax{16}; - using MixerBufferLine = std::array; - alignas(16) std::array mSampleData; - alignas(16) std::array mResampleData; - - alignas(16) float FilteredData[BufferLineSize]; - union { - alignas(16) float HrtfSourceData[BufferLineSize + HrtfHistoryLength]; - alignas(16) float NfcSampleData[BufferLineSize]; - }; + static constexpr std::size_t MixerLineSize{BufferLineSize + DecoderBase::sMaxPadding}; + static constexpr std::size_t MixerChannelsMax{16}; + alignas(16) std::array mSampleData{}; + alignas(16) std::array mResampleData{}; + + alignas(16) std::array FilteredData{}; + alignas(16) std::array ExtraSampleData{}; /* Persistent storage for HRTF mixing. */ - alignas(16) float2 HrtfAccumData[BufferLineSize + HrirLength]; + alignas(16) std::array HrtfAccumData{}; /* Mixing buffer used by the Dry mix and Real output. */ al::vector MixBuffer; /* The "dry" path corresponds to the main output. */ MixParams Dry; - uint NumChannelsPerOrder[MaxAmbiOrder+1]{}; + std::array NumChannelsPerOrder{}; /* "Real" output, which will be written to the device buffer. May alias the * dry buffer. @@ -265,7 +270,7 @@ struct DeviceBase { std::unique_ptr AmbiDecoder; /* Stereo-to-binaural filter */ - std::unique_ptr Bs2b; + std::unique_ptr Bs2b; using PostProc = void(DeviceBase::*)(const size_t SamplesToDo); PostProc PostProcess{nullptr}; @@ -284,66 +289,104 @@ struct DeviceBase { * the end, so the bottom bit indicates if the device is currently mixing * and the upper bits indicates how many mixes have been done. */ - RefCount MixCount{0u}; + std::atomic mMixCount{0u}; // Contexts created on this device - std::atomic*> mContexts{nullptr}; + al::atomic_unique_ptr> mContexts; - DeviceBase(DeviceType type); - DeviceBase(const DeviceBase&) = delete; - DeviceBase& operator=(const DeviceBase&) = delete; - ~DeviceBase(); + [[nodiscard]] auto bytesFromFmt() const noexcept -> uint { return BytesFromDevFmt(FmtType); } + [[nodiscard]] auto channelsFromFmt() const noexcept -> uint { return ChannelsFromDevFmt(FmtChans, mAmbiOrder); } + [[nodiscard]] auto frameSizeFromFmt() const noexcept -> uint { return bytesFromFmt() * channelsFromFmt(); } - uint bytesFromFmt() const noexcept { return BytesFromDevFmt(FmtType); } - uint channelsFromFmt() const noexcept { return ChannelsFromDevFmt(FmtChans, mAmbiOrder); } - uint frameSizeFromFmt() const noexcept { return bytesFromFmt() * channelsFromFmt(); } + struct MixLock { + DeviceBase *const self; + const uint mEndVal; - uint waitForMix() const noexcept + MixLock(DeviceBase *device, const uint endval) noexcept : self{device}, mEndVal{endval} { } + MixLock(const MixLock&) = delete; + void operator=(const MixLock&) = delete; + /* Update the mix count when the lock goes out of scope to "release" it + * (lsb should be 0). + */ + ~MixLock() { self->mMixCount.store(mEndVal, std::memory_order_release); } + }; + auto getWriteMixLock() noexcept -> MixLock { - uint refcount; - while((refcount=MixCount.load(std::memory_order_acquire))&1) { - } + /* Increment the mix count at the start of mixing and writing clock + * info (lsb should be 1). + */ + const auto oldCount = mMixCount.fetch_add(1u, std::memory_order_acq_rel); + return MixLock{this, oldCount+2}; + } + + /** Waits for the mixer to not be mixing or updating the clock. */ + [[nodiscard]] auto waitForMix() const noexcept -> uint + { + uint refcount{mMixCount.load(std::memory_order_acquire)}; + while((refcount&1)) refcount = mMixCount.load(std::memory_order_acquire); return refcount; } - void ProcessHrtf(const size_t SamplesToDo); - void ProcessAmbiDec(const size_t SamplesToDo); - void ProcessAmbiDecStablized(const size_t SamplesToDo); - void ProcessUhj(const size_t SamplesToDo); - void ProcessBs2b(const size_t SamplesToDo); + /** + * Helper to get the current clock time from the device's ClockBase, and + * SamplesDone converted from the sample rate. Should only be called while + * watching the MixCount. + */ + [[nodiscard]] auto getClockTime() const noexcept -> std::chrono::nanoseconds + { + using std::chrono::seconds; + using std::chrono::nanoseconds; + + auto ns = nanoseconds{seconds{mSamplesDone.load(std::memory_order_relaxed)}} / mSampleRate; + return nanoseconds{mClockBaseNSec.load(std::memory_order_relaxed)} + + mClockBaseSec.load(std::memory_order_relaxed) + ns; + } + + void ProcessHrtf(const std::size_t SamplesToDo); + void ProcessAmbiDec(const std::size_t SamplesToDo); + void ProcessAmbiDecStablized(const std::size_t SamplesToDo); + void ProcessUhj(const std::size_t SamplesToDo); + void ProcessBs2b(const std::size_t SamplesToDo); - inline void postProcess(const size_t SamplesToDo) + void postProcess(const std::size_t SamplesToDo) { if(PostProcess) LIKELY (this->*PostProcess)(SamplesToDo); } - void renderSamples(const al::span outBuffers, const uint numSamples); - void renderSamples(void *outBuffer, const uint numSamples, const size_t frameStep); + void renderSamples(const al::span outBuffers, const uint numSamples); + void renderSamples(void *outBuffer, const uint numSamples, const std::size_t frameStep); /* Caller must lock the device state, and the mixer must not be running. */ -#ifdef __USE_MINGW_ANSI_STDIO - [[gnu::format(gnu_printf,2,3)]] -#else - [[gnu::format(printf,2,3)]] -#endif - void handleDisconnect(const char *msg, ...); + void doDisconnect(std::string msg); + + template + void handleDisconnect(fmt::format_string fmt, Args&& ...args) + { doDisconnect(fmt::format(std::move(fmt), std::forward(args)...)); } /** * Returns the index for the given channel name (e.g. FrontCenter), or - * INVALID_CHANNEL_INDEX if it doesn't exist. + * InvalidChannelIndex if it doesn't exist. */ - uint channelIdxByName(Channel chan) const noexcept + [[nodiscard]] auto channelIdxByName(Channel chan) const noexcept -> std::uint8_t { return RealOut.ChannelIndex[chan]; } - DISABLE_ALLOC() - private: uint renderSamples(const uint numSamples); + +protected: + explicit DeviceBase(DeviceType type); + ~DeviceBase(); + +public: + DeviceBase(const DeviceBase&) = delete; + DeviceBase& operator=(const DeviceBase&) = delete; }; /* Must be less than 15 characters (16 including terminating null) for * compatibility with pthread_setname_np limitations. */ -#define MIXER_THREAD_NAME "alsoft-mixer" +[[nodiscard]] constexpr +auto GetMixerThreadName() noexcept -> const char* { return "alsoft-mixer"; } -#define RECORD_THREAD_NAME "alsoft-record" +[[nodiscard]] constexpr +auto GetRecordThreadName() noexcept -> const char* { return "alsoft-record"; } #endif /* CORE_DEVICE_H */ diff --git a/3rdparty/openal/core/effects/base.h b/3rdparty/openal/core/effects/base.h index b02d33b7b1ae..879f83900549 100644 --- a/3rdparty/openal/core/effects/base.h +++ b/3rdparty/openal/core/effects/base.h @@ -1,13 +1,14 @@ #ifndef CORE_EFFECTS_BASE_H #define CORE_EFFECTS_BASE_H -#include +#include +#include +#include -#include "almalloc.h" #include "alspan.h" -#include "atomic.h" #include "core/bufferline.h" #include "intrusive_ptr.h" +#include "opthelpers.h" struct BufferStorage; struct ContextBase; @@ -18,21 +19,21 @@ struct RealMixParams; /** Target gain for the reverb decay feedback reaching the decay time. */ -constexpr float ReverbDecayGain{0.001f}; /* -60 dB */ +inline constexpr float ReverbDecayGain{0.001f}; /* -60 dB */ -constexpr float ReverbMaxReflectionsDelay{0.3f}; -constexpr float ReverbMaxLateReverbDelay{0.1f}; +inline constexpr float ReverbMaxReflectionsDelay{0.3f}; +inline constexpr float ReverbMaxLateReverbDelay{0.1f}; enum class ChorusWaveform { Sinusoid, Triangle }; -constexpr float ChorusMaxDelay{0.016f}; -constexpr float FlangerMaxDelay{0.004f}; +inline constexpr float ChorusMaxDelay{0.016f}; +inline constexpr float FlangerMaxDelay{0.004f}; -constexpr float EchoMaxDelay{0.207f}; -constexpr float EchoMaxLRDelay{0.404f}; +inline constexpr float EchoMaxDelay{0.207f}; +inline constexpr float EchoMaxLRDelay{0.404f}; enum class FShifterDirection { Down, @@ -58,122 +59,142 @@ enum class VMorpherWaveform { Sawtooth }; -union EffectProps { - struct { - float Density; - float Diffusion; - float Gain; - float GainHF; - float GainLF; - float DecayTime; - float DecayHFRatio; - float DecayLFRatio; - float ReflectionsGain; - float ReflectionsDelay; - float ReflectionsPan[3]; - float LateReverbGain; - float LateReverbDelay; - float LateReverbPan[3]; - float EchoTime; - float EchoDepth; - float ModulationTime; - float ModulationDepth; - float AirAbsorptionGainHF; - float HFReference; - float LFReference; - float RoomRolloffFactor; - bool DecayHFLimit; - } Reverb; - - struct { - float AttackTime; - float ReleaseTime; - float Resonance; - float PeakGain; - } Autowah; - - struct { - ChorusWaveform Waveform; - int Phase; - float Rate; - float Depth; - float Feedback; - float Delay; - } Chorus; /* Also Flanger */ - - struct { - bool OnOff; - } Compressor; - - struct { - float Edge; - float Gain; - float LowpassCutoff; - float EQCenter; - float EQBandwidth; - } Distortion; - - struct { - float Delay; - float LRDelay; - - float Damping; - float Feedback; - - float Spread; - } Echo; - - struct { - float LowCutoff; - float LowGain; - float Mid1Center; - float Mid1Gain; - float Mid1Width; - float Mid2Center; - float Mid2Gain; - float Mid2Width; - float HighCutoff; - float HighGain; - } Equalizer; - - struct { - float Frequency; - FShifterDirection LeftDirection; - FShifterDirection RightDirection; - } Fshifter; - - struct { - float Frequency; - float HighPassCutoff; - ModulatorWaveform Waveform; - } Modulator; - - struct { - int CoarseTune; - int FineTune; - } Pshifter; - - struct { - float Rate; - VMorpherPhenome PhonemeA; - VMorpherPhenome PhonemeB; - int PhonemeACoarseTuning; - int PhonemeBCoarseTuning; - VMorpherWaveform Waveform; - } Vmorpher; - - struct { - float Gain; - } Dedicated; +struct ReverbProps { + float Density; + float Diffusion; + float Gain; + float GainHF; + float GainLF; + float DecayTime; + float DecayHFRatio; + float DecayLFRatio; + float ReflectionsGain; + float ReflectionsDelay; + std::array ReflectionsPan; + float LateReverbGain; + float LateReverbDelay; + std::array LateReverbPan; + float EchoTime; + float EchoDepth; + float ModulationTime; + float ModulationDepth; + float AirAbsorptionGainHF; + float HFReference; + float LFReference; + float RoomRolloffFactor; + bool DecayHFLimit; }; +struct AutowahProps { + float AttackTime; + float ReleaseTime; + float Resonance; + float PeakGain; +}; + +struct ChorusProps { + ChorusWaveform Waveform; + int Phase; + float Rate; + float Depth; + float Feedback; + float Delay; +}; + +struct CompressorProps { + bool OnOff; +}; + +struct DistortionProps { + float Edge; + float Gain; + float LowpassCutoff; + float EQCenter; + float EQBandwidth; +}; + +struct EchoProps { + float Delay; + float LRDelay; + + float Damping; + float Feedback; + + float Spread; +}; + +struct EqualizerProps { + float LowCutoff; + float LowGain; + float Mid1Center; + float Mid1Gain; + float Mid1Width; + float Mid2Center; + float Mid2Gain; + float Mid2Width; + float HighCutoff; + float HighGain; +}; + +struct FshifterProps { + float Frequency; + FShifterDirection LeftDirection; + FShifterDirection RightDirection; +}; + +struct ModulatorProps { + float Frequency; + float HighPassCutoff; + ModulatorWaveform Waveform; +}; + +struct PshifterProps { + int CoarseTune; + int FineTune; +}; + +struct VmorpherProps { + float Rate; + VMorpherPhenome PhonemeA; + VMorpherPhenome PhonemeB; + int PhonemeACoarseTuning; + int PhonemeBCoarseTuning; + VMorpherWaveform Waveform; +}; + +struct DedicatedProps { + enum TargetType : bool { Dialog, Lfe }; + TargetType Target; + float Gain; +}; + +struct ConvolutionProps { + std::array OrientAt; + std::array OrientUp; +}; + +using EffectProps = std::variant; + struct EffectTarget { MixParams *Main; RealMixParams *RealOut; }; -struct EffectState : public al::intrusive_ref { +struct SIMDALIGN EffectState : public al::intrusive_ref { al::span mOutTarget; @@ -188,8 +209,14 @@ struct EffectState : public al::intrusive_ref { struct EffectStateFactory { + EffectStateFactory() = default; + EffectStateFactory(const EffectStateFactory&) = delete; + EffectStateFactory(EffectStateFactory&&) = delete; virtual ~EffectStateFactory() = default; + void operator=(const EffectStateFactory&) = delete; + void operator=(EffectStateFactory&&) = delete; + virtual al::intrusive_ptr create() = 0; }; diff --git a/3rdparty/openal/core/effectslot.cpp b/3rdparty/openal/core/effectslot.cpp index db8aa078c223..d07c79a546a0 100644 --- a/3rdparty/openal/core/effectslot.cpp +++ b/3rdparty/openal/core/effectslot.cpp @@ -3,17 +3,13 @@ #include "effectslot.h" -#include +#include #include "almalloc.h" #include "context.h" -EffectSlotArray *EffectSlot::CreatePtrArray(size_t count) noexcept +std::unique_ptr EffectSlot::CreatePtrArray(size_t count) { - /* Allocate space for twice as many pointers, so the mixer has scratch - * space to store a sorted list during mixing. - */ - void *ptr{al_calloc(alignof(EffectSlotArray), EffectSlotArray::Sizeof(count*2))}; - return al::construct_at(static_cast(ptr), count); + return std::unique_ptr{new(FamCount{count}) EffectSlotArray(count)}; } diff --git a/3rdparty/openal/core/effectslot.h b/3rdparty/openal/core/effectslot.h index 2624ae5fd306..4825670a5d7c 100644 --- a/3rdparty/openal/core/effectslot.h +++ b/3rdparty/openal/core/effectslot.h @@ -2,10 +2,11 @@ #define CORE_EFFECTSLOT_H #include +#include -#include "almalloc.h" #include "device.h" #include "effects/base.h" +#include "flexarray.h" #include "intrusive_ptr.h" struct EffectSlot; @@ -18,20 +19,18 @@ enum class EffectSlotType : unsigned char { None, Reverb, Chorus, + Autowah, + Compressor, + Convolution, + Dedicated, Distortion, Echo, + Equalizer, Flanger, FrequencyShifter, - VocalMorpher, PitchShifter, RingModulator, - Autowah, - Compressor, - Equalizer, - EAXReverb, - DedicatedLFE, - DedicatedDialog, - Convolution + VocalMorpher, }; struct EffectSlotProps { @@ -44,9 +43,7 @@ struct EffectSlotProps { al::intrusive_ptr State; - std::atomic next; - - DEF_NEWDEL(EffectSlotProps) + std::atomic next{}; }; @@ -67,7 +64,7 @@ struct EffectSlot { EffectSlot *Target{nullptr}; EffectSlotType EffectType{EffectSlotType::None}; - EffectProps mEffectProps{}; + EffectProps mEffectProps; al::intrusive_ptr mEffectState; float RoomRolloff{0.0f}; /* Added to the source's room rolloff, not multiplied. */ @@ -81,9 +78,7 @@ struct EffectSlot { al::vector mWetBuffer; - static EffectSlotArray *CreatePtrArray(size_t count) noexcept; - - DEF_NEWDEL(EffectSlot) + static std::unique_ptr CreatePtrArray(size_t count); }; #endif /* CORE_EFFECTSLOT_H */ diff --git a/3rdparty/openal/core/except.cpp b/3rdparty/openal/core/except.cpp index 45fd4eb5f7a0..50c085e5573e 100644 --- a/3rdparty/openal/core/except.cpp +++ b/3rdparty/openal/core/except.cpp @@ -3,28 +3,9 @@ #include "except.h" -#include -#include - -#include "opthelpers.h" - namespace al { base_exception::~base_exception() = default; -void base_exception::setMessage(const char* msg, std::va_list args) -{ - std::va_list args2; - va_copy(args2, args); - int msglen{std::vsnprintf(nullptr, 0, msg, args)}; - if(msglen > 0) LIKELY - { - mMessage.resize(static_cast(msglen)+1); - std::vsnprintf(const_cast(mMessage.data()), mMessage.length(), msg, args2); - mMessage.pop_back(); - } - va_end(args2); -} - } // namespace al diff --git a/3rdparty/openal/core/except.h b/3rdparty/openal/core/except.h index 0e28e9dfede6..53e6a1c9f926 100644 --- a/3rdparty/openal/core/except.h +++ b/3rdparty/openal/core/except.h @@ -1,10 +1,9 @@ #ifndef CORE_EXCEPT_H #define CORE_EXCEPT_H -#include #include #include -#include +#include namespace al { @@ -12,20 +11,20 @@ namespace al { class base_exception : public std::exception { std::string mMessage; -protected: +public: base_exception() = default; - virtual ~base_exception(); + template,bool> = true> + explicit base_exception(T&& msg) : mMessage{std::forward(msg)} { } + base_exception(const base_exception&) = default; + base_exception(base_exception&&) = default; + ~base_exception() override; - void setMessage(const char *msg, std::va_list args); + auto operator=(const base_exception&) -> base_exception& = default; + auto operator=(base_exception&&) -> base_exception& = default; -public: - const char *what() const noexcept override { return mMessage.c_str(); } + [[nodiscard]] auto what() const noexcept -> const char* override { return mMessage.c_str(); } }; } // namespace al -#define START_API_FUNC try - -#define END_API_FUNC catch(...) { std::terminate(); } - #endif /* CORE_EXCEPT_H */ diff --git a/3rdparty/openal/core/filters/biquad.cpp b/3rdparty/openal/core/filters/biquad.cpp index a0a62eb8bde4..c1faf5e9fe05 100644 --- a/3rdparty/openal/core/filters/biquad.cpp +++ b/3rdparty/openal/core/filters/biquad.cpp @@ -3,6 +3,7 @@ #include "biquad.h" +#include #include #include #include @@ -27,8 +28,8 @@ void BiquadFilterR::setParams(BiquadType type, Real f0norm, Real gain, Rea const Real alpha{sin_w0/2.0f * rcpQ}; Real sqrtgain_alpha_2; - Real a[3]{ 1.0f, 0.0f, 0.0f }; - Real b[3]{ 1.0f, 0.0f, 0.0f }; + std::array a{{1.0f, 0.0f, 0.0f}}; + std::array b{{1.0f, 0.0f, 0.0f}}; /* Calculate filter coefficients depending on filter type */ switch(type) @@ -94,7 +95,7 @@ void BiquadFilterR::setParams(BiquadType type, Real f0norm, Real gain, Rea } template -void BiquadFilterR::process(const al::span src, Real *dst) +void BiquadFilterR::process(const al::span src, const al::span dst) { const Real b0{mB0}; const Real b1{mB1}; @@ -119,7 +120,7 @@ void BiquadFilterR::process(const al::span src, Real *dst) z2 = input*b2 - output*a2; return output; }; - std::transform(src.cbegin(), src.cend(), dst, proc_sample); + std::transform(src.cbegin(), src.cend(), dst.begin(), proc_sample); mZ1 = z1; mZ2 = z2; @@ -127,7 +128,7 @@ void BiquadFilterR::process(const al::span src, Real *dst) template void BiquadFilterR::dualProcess(BiquadFilterR &other, const al::span src, - Real *dst) + const al::span dst) { const Real b00{mB0}; const Real b01{mB1}; @@ -156,7 +157,7 @@ void BiquadFilterR::dualProcess(BiquadFilterR &other, const al::span src, Real *dst); + void process(const al::span src, const al::span dst); /** Processes this filter and the other at the same time. */ - void dualProcess(BiquadFilterR &other, const al::span src, Real *dst); + void dualProcess(BiquadFilterR &other, const al::span src, + const al::span dst); /* Rather hacky. It's just here to support "manual" processing. */ - std::pair getComponents() const noexcept { return {mZ1, mZ2}; } + [[nodiscard]] auto getComponents() const noexcept -> std::array { return {{mZ1,mZ2}}; } void setComponents(Real z1, Real z2) noexcept { mZ1 = z1; mZ2 = z2; } - Real processOne(const Real in, Real &z1, Real &z2) const noexcept + [[nodiscard]] auto processOne(const Real in, Real &z1, Real &z2) const noexcept -> Real { const Real out{in*mB0 + z1}; z1 = in*mB1 - out*mA1 + z2; @@ -134,7 +135,7 @@ template struct DualBiquadR { BiquadFilterR &f0, &f1; - void process(const al::span src, Real *dst) + void process(const al::span src, const al::span dst) { f0.dualProcess(f1, src, dst); } }; diff --git a/3rdparty/openal/core/filters/nfc.cpp b/3rdparty/openal/core/filters/nfc.cpp index aa64c6130728..15a1a7894b90 100644 --- a/3rdparty/openal/core/filters/nfc.cpp +++ b/3rdparty/openal/core/filters/nfc.cpp @@ -5,8 +5,6 @@ #include -#include "opthelpers.h" - /* Near-field control filters are the basis for handling the near-field effect. * The near-field effect is a bass-boost present in the directional components @@ -48,24 +46,22 @@ namespace { -constexpr float B[5][4] = { - { 0.0f }, - { 1.0f }, - { 3.0f, 3.0f }, - { 3.6778f, 6.4595f, 2.3222f }, - { 4.2076f, 11.4877f, 5.7924f, 9.1401f } +constexpr std::array B{ + std::array{ 0.0f, 0.0f, 0.0f, 0.0f}, + std::array{ 1.0f, 0.0f, 0.0f, 0.0f}, + std::array{ 3.0f, 3.0f, 0.0f, 0.0f}, + std::array{3.6778f, 6.4595f, 2.3222f, 0.0f}, + std::array{4.2076f, 11.4877f, 5.7924f, 9.1401f} }; NfcFilter1 NfcFilterCreate1(const float w0, const float w1) noexcept { - NfcFilter1 nfc{}; - float b_00, g_0; - float r; + auto nfc = NfcFilter1{}; /* Calculate bass-cut coefficients. */ - r = 0.5f * w1; - b_00 = B[1][0] * r; - g_0 = 1.0f + b_00; + auto r = 0.5f * w1; + auto b_00 = B[1][0] * r; + auto g_0 = 1.0f + b_00; nfc.base_gain = 1.0f / g_0; nfc.a1 = 2.0f * b_00 / g_0; @@ -83,9 +79,9 @@ NfcFilter1 NfcFilterCreate1(const float w0, const float w1) noexcept void NfcFilterAdjust1(NfcFilter1 *nfc, const float w0) noexcept { - const float r{0.5f * w0}; - const float b_00{B[1][0] * r}; - const float g_0{1.0f + b_00}; + const auto r = 0.5f * w0; + const auto b_00 = B[1][0] * r; + const auto g_0 = 1.0f + b_00; nfc->gain = nfc->base_gain * g_0; nfc->b1 = 2.0f * b_00 / g_0; @@ -94,15 +90,13 @@ void NfcFilterAdjust1(NfcFilter1 *nfc, const float w0) noexcept NfcFilter2 NfcFilterCreate2(const float w0, const float w1) noexcept { - NfcFilter2 nfc{}; - float b_10, b_11, g_1; - float r; + auto nfc = NfcFilter2{}; /* Calculate bass-cut coefficients. */ - r = 0.5f * w1; - b_10 = B[2][0] * r; - b_11 = B[2][1] * r * r; - g_1 = 1.0f + b_10 + b_11; + auto r = 0.5f * w1; + auto b_10 = B[2][0] * r; + auto b_11 = B[2][1] * (r*r); + auto g_1 = 1.0f + b_10 + b_11; nfc.base_gain = 1.0f / g_1; nfc.a1 = (2.0f*b_10 + 4.0f*b_11) / g_1; @@ -123,10 +117,10 @@ NfcFilter2 NfcFilterCreate2(const float w0, const float w1) noexcept void NfcFilterAdjust2(NfcFilter2 *nfc, const float w0) noexcept { - const float r{0.5f * w0}; - const float b_10{B[2][0] * r}; - const float b_11{B[2][1] * r * r}; - const float g_1{1.0f + b_10 + b_11}; + const auto r = 0.5f * w0; + const auto b_10 = B[2][0] * r; + const auto b_11 = B[2][1] * (r*r); + const auto g_1 = 1.0f + b_10 + b_11; nfc->gain = nfc->base_gain * g_1; nfc->b1 = (2.0f*b_10 + 4.0f*b_11) / g_1; @@ -136,18 +130,15 @@ void NfcFilterAdjust2(NfcFilter2 *nfc, const float w0) noexcept NfcFilter3 NfcFilterCreate3(const float w0, const float w1) noexcept { - NfcFilter3 nfc{}; - float b_10, b_11, g_1; - float b_00, g_0; - float r; + auto nfc = NfcFilter3{}; /* Calculate bass-cut coefficients. */ - r = 0.5f * w1; - b_10 = B[3][0] * r; - b_11 = B[3][1] * r * r; - b_00 = B[3][2] * r; - g_1 = 1.0f + b_10 + b_11; - g_0 = 1.0f + b_00; + auto r = 0.5f * w1; + auto b_10 = B[3][0] * r; + auto b_11 = B[3][1] * (r*r); + auto b_00 = B[3][2] * r; + auto g_1 = 1.0f + b_10 + b_11; + auto g_0 = 1.0f + b_00; nfc.base_gain = 1.0f / (g_1 * g_0); nfc.a1 = (2.0f*b_10 + 4.0f*b_11) / g_1; @@ -157,7 +148,7 @@ NfcFilter3 NfcFilterCreate3(const float w0, const float w1) noexcept /* Calculate bass-boost coefficients. */ r = 0.5f * w0; b_10 = B[3][0] * r; - b_11 = B[3][1] * r * r; + b_11 = B[3][1] * (r*r); b_00 = B[3][2] * r; g_1 = 1.0f + b_10 + b_11; g_0 = 1.0f + b_00; @@ -172,12 +163,12 @@ NfcFilter3 NfcFilterCreate3(const float w0, const float w1) noexcept void NfcFilterAdjust3(NfcFilter3 *nfc, const float w0) noexcept { - const float r{0.5f * w0}; - const float b_10{B[3][0] * r}; - const float b_11{B[3][1] * r * r}; - const float b_00{B[3][2] * r}; - const float g_1{1.0f + b_10 + b_11}; - const float g_0{1.0f + b_00}; + const auto r = 0.5f * w0; + const auto b_10 = B[3][0] * r; + const auto b_11 = B[3][1] * (r*r); + const auto b_00 = B[3][2] * r; + const auto g_1 = 1.0f + b_10 + b_11; + const auto g_0 = 1.0f + b_00; nfc->gain = nfc->base_gain * (g_1 * g_0); nfc->b1 = (2.0f*b_10 + 4.0f*b_11) / g_1; @@ -188,19 +179,16 @@ void NfcFilterAdjust3(NfcFilter3 *nfc, const float w0) noexcept NfcFilter4 NfcFilterCreate4(const float w0, const float w1) noexcept { - NfcFilter4 nfc{}; - float b_10, b_11, g_1; - float b_00, b_01, g_0; - float r; + auto nfc = NfcFilter4{}; /* Calculate bass-cut coefficients. */ - r = 0.5f * w1; - b_10 = B[4][0] * r; - b_11 = B[4][1] * r * r; - b_00 = B[4][2] * r; - b_01 = B[4][3] * r * r; - g_1 = 1.0f + b_10 + b_11; - g_0 = 1.0f + b_00 + b_01; + auto r = 0.5f * w1; + auto b_10 = B[4][0] * r; + auto b_11 = B[4][1] * (r*r); + auto b_00 = B[4][2] * r; + auto b_01 = B[4][3] * (r*r); + auto g_1 = 1.0f + b_10 + b_11; + auto g_0 = 1.0f + b_00 + b_01; nfc.base_gain = 1.0f / (g_1 * g_0); nfc.a1 = (2.0f*b_10 + 4.0f*b_11) / g_1; @@ -211,9 +199,9 @@ NfcFilter4 NfcFilterCreate4(const float w0, const float w1) noexcept /* Calculate bass-boost coefficients. */ r = 0.5f * w0; b_10 = B[4][0] * r; - b_11 = B[4][1] * r * r; + b_11 = B[4][1] * (r*r); b_00 = B[4][2] * r; - b_01 = B[4][3] * r * r; + b_01 = B[4][3] * (r*r); g_1 = 1.0f + b_10 + b_11; g_0 = 1.0f + b_00 + b_01; @@ -228,13 +216,13 @@ NfcFilter4 NfcFilterCreate4(const float w0, const float w1) noexcept void NfcFilterAdjust4(NfcFilter4 *nfc, const float w0) noexcept { - const float r{0.5f * w0}; - const float b_10{B[4][0] * r}; - const float b_11{B[4][1] * r * r}; - const float b_00{B[4][2] * r}; - const float b_01{B[4][3] * r * r}; - const float g_1{1.0f + b_10 + b_11}; - const float g_0{1.0f + b_00 + b_01}; + const auto r = 0.5f * w0; + const auto b_10 = B[4][0] * r; + const auto b_11 = B[4][1] * (r*r); + const auto b_00 = B[4][2] * r; + const auto b_01 = B[4][3] * (r*r); + const auto g_1 = 1.0f + b_10 + b_11; + const auto g_0 = 1.0f + b_00 + b_01; nfc->gain = nfc->base_gain * (g_1 * g_0); nfc->b1 = (2.0f*b_10 + 4.0f*b_11) / g_1; @@ -262,7 +250,7 @@ void NfcFilter::adjust(const float w0) noexcept } -void NfcFilter::process1(const al::span src, float *RESTRICT dst) +void NfcFilter::process1(const al::span src, const al::span dst) { const float gain{first.gain}; const float b1{first.b1}; @@ -275,11 +263,11 @@ void NfcFilter::process1(const al::span src, float *RESTRICT dst) z1 += y; return out; }; - std::transform(src.cbegin(), src.cend(), dst, proc_sample); + std::transform(src.cbegin(), src.cend(), dst.begin(), proc_sample); first.z[0] = z1; } -void NfcFilter::process2(const al::span src, float *RESTRICT dst) +void NfcFilter::process2(const al::span src, const al::span dst) { const float gain{second.gain}; const float b1{second.b1}; @@ -296,12 +284,12 @@ void NfcFilter::process2(const al::span src, float *RESTRICT dst) z1 += y; return out; }; - std::transform(src.cbegin(), src.cend(), dst, proc_sample); + std::transform(src.cbegin(), src.cend(), dst.begin(), proc_sample); second.z[0] = z1; second.z[1] = z2; } -void NfcFilter::process3(const al::span src, float *RESTRICT dst) +void NfcFilter::process3(const al::span src, const al::span dst) { const float gain{third.gain}; const float b1{third.b1}; @@ -325,13 +313,13 @@ void NfcFilter::process3(const al::span src, float *RESTRICT dst) z3 += y; return out; }; - std::transform(src.cbegin(), src.cend(), dst, proc_sample); + std::transform(src.cbegin(), src.cend(), dst.begin(), proc_sample); third.z[0] = z1; third.z[1] = z2; third.z[2] = z3; } -void NfcFilter::process4(const al::span src, float *RESTRICT dst) +void NfcFilter::process4(const al::span src, const al::span dst) { const float gain{fourth.gain}; const float b1{fourth.b1}; @@ -359,7 +347,7 @@ void NfcFilter::process4(const al::span src, float *RESTRICT dst) z3 += y; return out; }; - std::transform(src.cbegin(), src.cend(), dst, proc_sample); + std::transform(src.cbegin(), src.cend(), dst.begin(), proc_sample); fourth.z[0] = z1; fourth.z[1] = z2; fourth.z[2] = z3; diff --git a/3rdparty/openal/core/filters/nfc.h b/3rdparty/openal/core/filters/nfc.h index 4b8e68b5dc0f..00ab4dd76540 100644 --- a/3rdparty/openal/core/filters/nfc.h +++ b/3rdparty/openal/core/filters/nfc.h @@ -1,30 +1,31 @@ #ifndef CORE_FILTERS_NFC_H #define CORE_FILTERS_NFC_H +#include #include #include "alspan.h" struct NfcFilter1 { - float base_gain, gain; - float b1, a1; - float z[1]; + float base_gain{1.0f}, gain{1.0f}; + float b1{}, a1{}; + std::array z{}; }; struct NfcFilter2 { - float base_gain, gain; - float b1, b2, a1, a2; - float z[2]; + float base_gain{1.0f}, gain{1.0f}; + float b1{}, b2{}, a1{}, a2{}; + std::array z{}; }; struct NfcFilter3 { - float base_gain, gain; - float b1, b2, b3, a1, a2, a3; - float z[3]; + float base_gain{1.0f}, gain{1.0f}; + float b1{}, b2{}, b3{}, a1{}, a2{}, a3{}; + std::array z{}; }; struct NfcFilter4 { - float base_gain, gain; - float b1, b2, b3, b4, a1, a2, a3, a4; - float z[4]; + float base_gain{1.0f}, gain{1.0f}; + float b1{}, b2{}, b3{}, b4{}, a1{}, a2{}, a3{}, a4{}; + std::array z{}; }; class NfcFilter { @@ -48,16 +49,16 @@ class NfcFilter { void adjust(const float w0) noexcept; /* Near-field control filter for first-order ambisonic channels (1-3). */ - void process1(const al::span src, float *RESTRICT dst); + void process1(const al::span src, const al::span dst); /* Near-field control filter for second-order ambisonic channels (4-8). */ - void process2(const al::span src, float *RESTRICT dst); + void process2(const al::span src, const al::span dst); /* Near-field control filter for third-order ambisonic channels (9-15). */ - void process3(const al::span src, float *RESTRICT dst); + void process3(const al::span src, const al::span dst); /* Near-field control filter for fourth-order ambisonic channels (16-24). */ - void process4(const al::span src, float *RESTRICT dst); + void process4(const al::span src, const al::span dst); }; #endif /* CORE_FILTERS_NFC_H */ diff --git a/3rdparty/openal/core/filters/splitter.cpp b/3rdparty/openal/core/filters/splitter.cpp index 983ba36f15dd..fbb6b2b7ef00 100644 --- a/3rdparty/openal/core/filters/splitter.cpp +++ b/3rdparty/openal/core/filters/splitter.cpp @@ -4,6 +4,7 @@ #include "splitter.h" #include +#include #include #include @@ -27,14 +28,17 @@ void BandSplitterR::init(Real f0norm) } template -void BandSplitterR::process(const al::span input, Real *hpout, Real *lpout) +void BandSplitterR::process(const al::span input, const al::span hpout, + const al::span lpout) { const Real ap_coeff{mCoeff}; const Real lp_coeff{mCoeff*0.5f + 0.5f}; Real lp_z1{mLpZ1}; Real lp_z2{mLpZ2}; Real ap_z1{mApZ1}; - auto proc_sample = [ap_coeff,lp_coeff,&lp_z1,&lp_z2,&ap_z1,&lpout](const Real in) noexcept -> Real + assert(lpout.size() <= input.size()); + auto lpiter = lpout.begin(); + auto proc_sample = [ap_coeff,lp_coeff,&lp_z1,&lp_z2,&ap_z1,&lpiter](const Real in) noexcept -> Real { /* Low-pass sample processing. */ Real d{(in - lp_z1) * lp_coeff}; @@ -45,7 +49,7 @@ void BandSplitterR::process(const al::span input, Real *hpout, lp_y = lp_z2 + d; lp_z2 = lp_y + d; - *(lpout++) = lp_y; + *(lpiter++) = lp_y; /* All-pass sample processing. */ Real ap_y{in*ap_coeff + ap_z1}; @@ -54,15 +58,15 @@ void BandSplitterR::process(const al::span input, Real *hpout, /* High-pass generated from removing low-passed output. */ return ap_y - lp_y; }; - std::transform(input.cbegin(), input.cend(), hpout, proc_sample); + std::transform(input.cbegin(), input.cend(), hpout.begin(), proc_sample); mLpZ1 = lp_z1; mLpZ2 = lp_z2; mApZ1 = ap_z1; } template -void BandSplitterR::processHfScale(const al::span input, Real *RESTRICT output, - const Real hfscale) +void BandSplitterR::processHfScale(const al::span input, + const al::span output, const Real hfscale) { const Real ap_coeff{mCoeff}; const Real lp_coeff{mCoeff*0.5f + 0.5f}; @@ -89,7 +93,7 @@ void BandSplitterR::processHfScale(const al::span input, Real */ return (ap_y-lp_y)*hfscale + lp_y; }; - std::transform(input.begin(), input.end(), output, proc_sample); + std::transform(input.cbegin(), input.cend(), output.begin(), proc_sample); mLpZ1 = lp_z1; mLpZ2 = lp_z2; mApZ1 = ap_z1; diff --git a/3rdparty/openal/core/filters/splitter.h b/3rdparty/openal/core/filters/splitter.h index e853eb385b8d..fdbf07f7459d 100644 --- a/3rdparty/openal/core/filters/splitter.h +++ b/3rdparty/openal/core/filters/splitter.h @@ -17,14 +17,16 @@ class BandSplitterR { public: BandSplitterR() = default; BandSplitterR(const BandSplitterR&) = default; - BandSplitterR(Real f0norm) { init(f0norm); } + explicit BandSplitterR(Real f0norm) { init(f0norm); } BandSplitterR& operator=(const BandSplitterR&) = default; void init(Real f0norm); void clear() noexcept { mLpZ1 = mLpZ2 = mApZ1 = 0.0f; } - void process(const al::span input, Real *hpout, Real *lpout); + void process(const al::span input, const al::span hpout, + const al::span lpout); - void processHfScale(const al::span input, Real *output, const Real hfscale); + void processHfScale(const al::span input, const al::span output, + const Real hfscale); void processHfScale(const al::span samples, const Real hfscale); void processScale(const al::span samples, const Real hfscale, const Real lfscale); diff --git a/3rdparty/openal/core/fmt_traits.cpp b/3rdparty/openal/core/fmt_traits.cpp deleted file mode 100644 index 054d87669f0f..000000000000 --- a/3rdparty/openal/core/fmt_traits.cpp +++ /dev/null @@ -1,79 +0,0 @@ - -#include "config.h" - -#include "fmt_traits.h" - - -namespace al { - -const int16_t muLawDecompressionTable[256] = { - -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956, - -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764, - -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412, - -11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316, - -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, - -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092, - -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004, - -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980, - -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436, - -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, - -876, -844, -812, -780, -748, -716, -684, -652, - -620, -588, -556, -524, -492, -460, -428, -396, - -372, -356, -340, -324, -308, -292, -276, -260, - -244, -228, -212, -196, -180, -164, -148, -132, - -120, -112, -104, -96, -88, -80, -72, -64, - -56, -48, -40, -32, -24, -16, -8, 0, - 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956, - 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764, - 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412, - 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316, - 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, - 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092, - 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004, - 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980, - 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, - 1372, 1308, 1244, 1180, 1116, 1052, 988, 924, - 876, 844, 812, 780, 748, 716, 684, 652, - 620, 588, 556, 524, 492, 460, 428, 396, - 372, 356, 340, 324, 308, 292, 276, 260, - 244, 228, 212, 196, 180, 164, 148, 132, - 120, 112, 104, 96, 88, 80, 72, 64, - 56, 48, 40, 32, 24, 16, 8, 0 -}; - -const int16_t aLawDecompressionTable[256] = { - -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736, - -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, - -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368, - -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392, - -22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944, - -30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136, - -11008,-10496,-12032,-11520, -8960, -8448, -9984, -9472, - -15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568, - -344, -328, -376, -360, -280, -264, -312, -296, - -472, -456, -504, -488, -408, -392, -440, -424, - -88, -72, -120, -104, -24, -8, -56, -40, - -216, -200, -248, -232, -152, -136, -184, -168, - -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184, - -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696, - -688, -656, -752, -720, -560, -528, -624, -592, - -944, -912, -1008, -976, -816, -784, -880, -848, - 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736, - 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784, - 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368, - 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392, - 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944, - 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136, - 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472, - 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568, - 344, 328, 376, 360, 280, 264, 312, 296, - 472, 456, 504, 488, 408, 392, 440, 424, - 88, 72, 120, 104, 24, 8, 56, 40, - 216, 200, 248, 232, 152, 136, 184, 168, - 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184, - 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696, - 688, 656, 752, 720, 560, 528, 624, 592, - 944, 912, 1008, 976, 816, 784, 880, 848 -}; - -} // namespace al diff --git a/3rdparty/openal/core/fmt_traits.h b/3rdparty/openal/core/fmt_traits.h index 1879c81b1187..b16f4a3d13e4 100644 --- a/3rdparty/openal/core/fmt_traits.h +++ b/3rdparty/openal/core/fmt_traits.h @@ -1,16 +1,83 @@ #ifndef CORE_FMT_TRAITS_H #define CORE_FMT_TRAITS_H -#include -#include +#include +#include -#include "buffer_storage.h" +#include "storage_formats.h" namespace al { -extern const int16_t muLawDecompressionTable[256]; -extern const int16_t aLawDecompressionTable[256]; +inline constexpr auto muLawDecompressionTable = std::array{{ + -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956, + -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764, + -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412, + -11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316, + -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, + -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092, + -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004, + -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980, + -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436, + -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, + -876, -844, -812, -780, -748, -716, -684, -652, + -620, -588, -556, -524, -492, -460, -428, -396, + -372, -356, -340, -324, -308, -292, -276, -260, + -244, -228, -212, -196, -180, -164, -148, -132, + -120, -112, -104, -96, -88, -80, -72, -64, + -56, -48, -40, -32, -24, -16, -8, 0, + 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956, + 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764, + 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412, + 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316, + 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, + 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092, + 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004, + 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980, + 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, + 1372, 1308, 1244, 1180, 1116, 1052, 988, 924, + 876, 844, 812, 780, 748, 716, 684, 652, + 620, 588, 556, 524, 492, 460, 428, 396, + 372, 356, 340, 324, 308, 292, 276, 260, + 244, 228, 212, 196, 180, 164, 148, 132, + 120, 112, 104, 96, 88, 80, 72, 64, + 56, 48, 40, 32, 24, 16, 8, 0 +}}; + +inline constexpr auto aLawDecompressionTable = std::array{{ + -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736, + -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, + -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368, + -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392, + -22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944, + -30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136, + -11008,-10496,-12032,-11520, -8960, -8448, -9984, -9472, + -15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568, + -344, -328, -376, -360, -280, -264, -312, -296, + -472, -456, -504, -488, -408, -392, -440, -424, + -88, -72, -120, -104, -24, -8, -56, -40, + -216, -200, -248, -232, -152, -136, -184, -168, + -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184, + -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696, + -688, -656, -752, -720, -560, -528, -624, -592, + -944, -912, -1008, -976, -816, -784, -880, -848, + 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736, + 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784, + 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368, + 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392, + 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944, + 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136, + 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472, + 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568, + 344, 328, 376, 360, 280, 264, 312, 296, + 472, 456, 504, 488, 408, 392, 440, 424, + 88, 72, 120, 104, 24, 8, 56, 40, + 216, 200, 248, 232, 152, 136, 184, 168, + 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184, + 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696, + 688, 656, 752, 720, 560, 528, 624, 592, + 944, 912, 1008, 976, 816, 784, 880, 848 +}}; template @@ -18,62 +85,52 @@ struct FmtTypeTraits { }; template<> struct FmtTypeTraits { - using Type = uint8_t; + using Type = std::uint8_t; - template - static constexpr OutT to(const Type val) noexcept { return val*OutT{1.0/128.0} - OutT{1.0}; } + constexpr float operator()(const Type val) const noexcept + { return float(val)*(1.0f/128.0f) - 1.0f; } }; template<> struct FmtTypeTraits { - using Type = int16_t; + using Type = std::int16_t; - template - static constexpr OutT to(const Type val) noexcept { return val*OutT{1.0/32768.0}; } + constexpr float operator()(const Type val) const noexcept + { return float(val) * (1.0f/32768.0f); } +}; +template<> +struct FmtTypeTraits { + using Type = std::int32_t; + + constexpr float operator()(const Type val) const noexcept + { return static_cast(val)*(1.0f/2147483648.0f); } }; template<> struct FmtTypeTraits { using Type = float; - template - static constexpr OutT to(const Type val) noexcept { return val; } + constexpr float operator()(const Type val) const noexcept { return val; } }; template<> struct FmtTypeTraits { using Type = double; - template - static constexpr OutT to(const Type val) noexcept { return static_cast(val); } + constexpr float operator()(const Type val) const noexcept { return static_cast(val); } }; template<> struct FmtTypeTraits { - using Type = uint8_t; + using Type = std::uint8_t; - template - static constexpr OutT to(const Type val) noexcept - { return muLawDecompressionTable[val] * OutT{1.0/32768.0}; } + constexpr float operator()(const Type val) const noexcept + { return float(muLawDecompressionTable[val]) * (1.0f/32768.0f); } }; template<> struct FmtTypeTraits { - using Type = uint8_t; + using Type = std::uint8_t; - template - static constexpr OutT to(const Type val) noexcept - { return aLawDecompressionTable[val] * OutT{1.0/32768.0}; } + constexpr float operator()(const Type val) const noexcept + { return float(aLawDecompressionTable[val]) * (1.0f/32768.0f); } }; - -template -inline void LoadSampleArray(DstT *RESTRICT dst, const std::byte *src, const std::size_t srcstep, - const std::size_t samples) noexcept -{ - using TypeTraits = FmtTypeTraits; - using SampleType = typename TypeTraits::Type; - - const SampleType *RESTRICT ssrc{reinterpret_cast(src)}; - for(size_t i{0u};i < samples;i++) - dst[i] = TypeTraits::template to(ssrc[i*srcstep]); -} - } // namespace al #endif /* CORE_FMT_TRAITS_H */ diff --git a/3rdparty/openal/core/fpu_ctrl.cpp b/3rdparty/openal/core/fpu_ctrl.cpp index 0cf0d6e72d10..7a4f147fa30d 100644 --- a/3rdparty/openal/core/fpu_ctrl.cpp +++ b/3rdparty/openal/core/fpu_ctrl.cpp @@ -1,61 +1,90 @@ #include "config.h" +#include "config_simd.h" #include "fpu_ctrl.h" #ifdef HAVE_INTRIN_H #include #endif -#ifdef HAVE_SSE_INTRINSICS +#if HAVE_SSE_INTRINSICS #include -#ifndef _MM_DENORMALS_ZERO_MASK +#elif HAVE_SSE +#include +#endif + +#if HAVE_SSE && !defined(_MM_DENORMALS_ZERO_MASK) /* Some headers seem to be missing these? */ #define _MM_DENORMALS_ZERO_MASK 0x0040u #define _MM_DENORMALS_ZERO_ON 0x0040u #endif -#endif +#if !HAVE_SSE_INTRINSICS && HAVE_SSE #include "cpu_caps.h" +#endif +namespace { -void FPUCtl::enter() noexcept +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +[[gnu::target("sse")]] +#endif +[[maybe_unused]] +void disable_denormals(unsigned int *state [[maybe_unused]]) { - if(this->in_mode) return; - -#if defined(HAVE_SSE_INTRINSICS) - this->sse_state = _mm_getcsr(); - unsigned int sseState{this->sse_state}; +#if HAVE_SSE_INTRINSICS + *state = _mm_getcsr(); + unsigned int sseState{*state}; sseState &= ~(_MM_FLUSH_ZERO_MASK | _MM_DENORMALS_ZERO_MASK); sseState |= _MM_FLUSH_ZERO_ON | _MM_DENORMALS_ZERO_ON; _mm_setcsr(sseState); -#elif defined(__GNUC__) && defined(HAVE_SSE) +#elif HAVE_SSE - if((CPUCapFlags&CPU_CAP_SSE)) + *state = _mm_getcsr(); + unsigned int sseState{*state}; + sseState &= ~_MM_FLUSH_ZERO_MASK; + sseState |= _MM_FLUSH_ZERO_ON; + if((CPUCapFlags&CPU_CAP_SSE2)) { - __asm__ __volatile__("stmxcsr %0" : "=m" (*&this->sse_state)); - unsigned int sseState{this->sse_state}; - sseState |= 0x8000; /* set flush-to-zero */ - if((CPUCapFlags&CPU_CAP_SSE2)) - sseState |= 0x0040; /* set denormals-are-zero */ - __asm__ __volatile__("ldmxcsr %0" : : "m" (*&sseState)); + sseState &= ~_MM_DENORMALS_ZERO_MASK; + sseState |= _MM_DENORMALS_ZERO_ON; } + _mm_setcsr(sseState); #endif - - this->in_mode = true; } -void FPUCtl::leave() noexcept +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +[[gnu::target("sse")]] +#endif +[[maybe_unused]] +void reset_fpu(unsigned int state [[maybe_unused]]) { - if(!this->in_mode) return; +#if HAVE_SSE_INTRINSICS || HAVE_SSE + _mm_setcsr(state); +#endif +} -#if defined(HAVE_SSE_INTRINSICS) - _mm_setcsr(this->sse_state); +} // namespace -#elif defined(__GNUC__) && defined(HAVE_SSE) +unsigned int FPUCtl::Set() noexcept +{ + unsigned int state{}; +#if HAVE_SSE_INTRINSICS + disable_denormals(&state); +#elif HAVE_SSE + if((CPUCapFlags&CPU_CAP_SSE)) + disable_denormals(&state); +#endif + return state; +} + +void FPUCtl::Reset(unsigned int state [[maybe_unused]]) noexcept +{ +#if HAVE_SSE_INTRINSICS + reset_fpu(state); +#elif HAVE_SSE if((CPUCapFlags&CPU_CAP_SSE)) - __asm__ __volatile__("ldmxcsr %0" : : "m" (*&this->sse_state)); + reset_fpu(state); #endif - this->in_mode = false; } diff --git a/3rdparty/openal/core/fpu_ctrl.h b/3rdparty/openal/core/fpu_ctrl.h index 9554313ae047..d4f75ec3146c 100644 --- a/3rdparty/openal/core/fpu_ctrl.h +++ b/3rdparty/openal/core/fpu_ctrl.h @@ -2,20 +2,31 @@ #define CORE_FPU_CTRL_H class FPUCtl { -#if defined(HAVE_SSE_INTRINSICS) || (defined(__GNUC__) && defined(HAVE_SSE)) unsigned int sse_state{}; -#endif bool in_mode{}; + static unsigned int Set() noexcept; + static void Reset(unsigned int state) noexcept; + public: - FPUCtl() noexcept { enter(); in_mode = true; } - ~FPUCtl() { if(in_mode) leave(); } + FPUCtl() noexcept : sse_state{Set()}, in_mode{true} { } + ~FPUCtl() { if(in_mode) Reset(sse_state); } FPUCtl(const FPUCtl&) = delete; FPUCtl& operator=(const FPUCtl&) = delete; - void enter() noexcept; - void leave() noexcept; + void enter() noexcept + { + if(!in_mode) + sse_state = Set(); + in_mode = true; + } + void leave() noexcept + { + if(in_mode) + Reset(sse_state); + in_mode = false; + } }; #endif /* CORE_FPU_CTRL_H */ diff --git a/3rdparty/openal/core/front_stablizer.h b/3rdparty/openal/core/front_stablizer.h index 6825111a7c6a..62652699da9c 100644 --- a/3rdparty/openal/core/front_stablizer.h +++ b/3rdparty/openal/core/front_stablizer.h @@ -7,10 +7,11 @@ #include "almalloc.h" #include "bufferline.h" #include "filters/splitter.h" +#include "flexarray.h" struct FrontStablizer { - FrontStablizer(size_t numchans) : ChannelFilters{numchans} { } + explicit FrontStablizer(size_t numchans) : ChannelFilters{numchans} { } alignas(16) std::array MidDirect{}; alignas(16) std::array Side{}; diff --git a/3rdparty/openal/core/helpers.cpp b/3rdparty/openal/core/helpers.cpp index b353da2e1012..cff864c5e79e 100644 --- a/3rdparty/openal/core/helpers.cpp +++ b/3rdparty/openal/core/helpers.cpp @@ -15,20 +15,59 @@ #include #include #include +#include +#include +#include "almalloc.h" #include "alnumeric.h" #include "alspan.h" #include "alstring.h" +#include "filesystem.h" #include "logging.h" #include "strutils.h" -/* Mixing thread priority level */ -int RTPrioLevel{1}; +namespace { + +using namespace std::string_view_literals; + +std::mutex gSearchLock; -/* Allow reducing the process's RTTime limit for RTKit. */ -bool AllowRTTimeLimit{true}; +void DirectorySearch(const fs::path &path, const std::string_view ext, + std::vector *const results) +{ + const auto base = results->size(); + try { + auto fpath = path.lexically_normal(); + if(!fs::exists(fpath)) + return; + + TRACE("Searching {} for *{}", al::u8_as_char(fpath.u8string()), ext); + for(auto&& dirent : fs::directory_iterator{fpath}) + { + auto&& entrypath = dirent.path(); + if(!entrypath.has_extension()) + continue; + + if(fs::status(entrypath).type() != fs::file_type::regular) + continue; + const auto u8ext = entrypath.extension().u8string(); + if(al::case_compare(al::u8_as_char(u8ext), ext) == 0) + results->emplace_back(al::u8_as_char(entrypath.u8string())); + } + } + catch(std::exception& e) { + ERR("Exception enumerating files: {}", e.what()); + } + + const auto newlist = al::span{*results}.subspan(base); + std::sort(newlist.begin(), newlist.end()); + for(const auto &name : newlist) + TRACE(" got {}", name); +} + +} // namespace #ifdef _WIN32 @@ -37,153 +76,120 @@ bool AllowRTTimeLimit{true}; const PathNamePair &GetProcBinary() { - static std::optional procbin; - if(procbin) return *procbin; -#if !defined(ALSOFT_UWP) - auto fullpath = std::vector(256); - DWORD len{GetModuleFileNameW(nullptr, fullpath.data(), static_cast(fullpath.size()))}; - while(len == fullpath.size()) + auto get_procbin = [] { - fullpath.resize(fullpath.size() << 1); - len = GetModuleFileNameW(nullptr, fullpath.data(), static_cast(fullpath.size())); - } - if(len == 0) - { - ERR("Failed to get process name: error %lu\n", GetLastError()); - procbin.emplace(); - return *procbin; - } +#if !ALSOFT_UWP + DWORD pathlen{256}; + auto fullpath = std::wstring(pathlen, L'\0'); + DWORD len{GetModuleFileNameW(nullptr, fullpath.data(), pathlen)}; + while(len == fullpath.size()) + { + pathlen <<= 1; + if(pathlen == 0) + { + /* pathlen overflow (more than 4 billion characters??) */ + len = 0; + break; + } + fullpath.resize(pathlen); + len = GetModuleFileNameW(nullptr, fullpath.data(), pathlen); + } + if(len == 0) + { + ERR("Failed to get process name: error {}", GetLastError()); + return PathNamePair{}; + } - fullpath.resize(len); - if(fullpath.back() != 0) - fullpath.push_back(0); + fullpath.resize(len); #else - auto exePath = __wargv[0]; - if (!exePath) - { - ERR("Failed to get process name: error %lu\n", GetLastError()); - procbin.emplace(); - return *procbin; - } - std::vector fullpath{exePath, exePath + wcslen(exePath) + 1}; + const WCHAR *exePath{__wargv[0]}; + if(!exePath) + { + ERR("Failed to get process name: __wargv[0] == nullptr"); + return PathNamePair{}; + } + std::wstring fullpath{exePath}; #endif - std::replace(fullpath.begin(), fullpath.end(), '/', '\\'); - auto sep = std::find(fullpath.rbegin()+1, fullpath.rend(), '\\'); - if(sep != fullpath.rend()) - { - *sep = 0; - procbin.emplace(wstr_to_utf8(fullpath.data()), wstr_to_utf8(al::to_address(sep.base()))); - } - else - procbin.emplace(std::string{}, wstr_to_utf8(fullpath.data())); + std::replace(fullpath.begin(), fullpath.end(), L'/', L'\\'); - TRACE("Got binary: %s, %s\n", procbin->path.c_str(), procbin->fname.c_str()); - return *procbin; + PathNamePair res{}; + if(auto seppos = fullpath.rfind(L'\\'); seppos < fullpath.size()) + { + res.path = wstr_to_utf8(std::wstring_view{fullpath}.substr(0, seppos)); + res.fname = wstr_to_utf8(std::wstring_view{fullpath}.substr(seppos+1)); + } + else + res.fname = wstr_to_utf8(fullpath); + + TRACE("Got binary: {}, {}", res.path, res.fname); + return res; + }; + static const PathNamePair procbin{get_procbin()}; + return procbin; } namespace { -void DirectorySearch(const char *path, const char *ext, std::vector *const results) -{ - std::string pathstr{path}; - pathstr += "\\*"; - pathstr += ext; - TRACE("Searching %s\n", pathstr.c_str()); +#if !ALSOFT_UWP && !defined(_GAMING_XBOX) +struct CoTaskMemDeleter { + void operator()(void *mem) const { CoTaskMemFree(mem); } +}; +#endif - std::wstring wpath{utf8_to_wstr(pathstr.c_str())}; - WIN32_FIND_DATAW fdata; - HANDLE hdl{FindFirstFileExW(wpath.c_str(), FindExInfoStandard, &fdata, FindExSearchNameMatch, NULL, 0)}; - if(hdl == INVALID_HANDLE_VALUE) return; +} // namespace - const auto base = results->size(); +auto SearchDataFiles(const std::string_view ext) -> std::vector +{ + auto srchlock = std::lock_guard{gSearchLock}; - do { - results->emplace_back(); - std::string &str = results->back(); - str = path; - str += '\\'; - str += wstr_to_utf8(fdata.cFileName); - } while(FindNextFileW(hdl, &fdata)); - FindClose(hdl); - const al::span newlist{results->data()+base, results->size()-base}; - std::sort(newlist.begin(), newlist.end()); - for(const auto &name : newlist) - TRACE(" got %s\n", name.c_str()); -} + /* Search the app-local directory. */ + auto results = std::vector{}; + if(auto localpath = al::getenv(L"ALSOFT_LOCAL_PATH")) + DirectorySearch(*localpath, ext, &results); + else if(auto curpath = fs::current_path(); !curpath.empty()) + DirectorySearch(curpath, ext, &results); -} // namespace + return results; +} -std::vector SearchDataFiles(const char *ext, const char *subdir) +auto SearchDataFiles(const std::string_view ext, const std::string_view subdir) + -> std::vector { - auto is_slash = [](int c) noexcept { return (c == '\\' || c == '/'); }; - - static std::mutex search_lock; - std::lock_guard _{search_lock}; + std::lock_guard srchlock{gSearchLock}; /* If the path is absolute, use it directly. */ std::vector results; - if(std::isalpha(subdir[0]) && subdir[1] == ':' && is_slash(subdir[2])) - { - std::string path{subdir}; - std::replace(path.begin(), path.end(), '/', '\\'); - DirectorySearch(path.c_str(), ext, &results); - return results; - } - if(subdir[0] == '\\' && subdir[1] == '\\' && subdir[2] == '?' && subdir[3] == '\\') + auto path = fs::u8path(subdir); + if(path.is_absolute()) { - DirectorySearch(subdir, ext, &results); + DirectorySearch(path, ext, &results); return results; } - std::string path; - - /* Search the app-local directory. */ - if(auto localpath = al::getenv(L"ALSOFT_LOCAL_PATH")) - { - path = wstr_to_utf8(localpath->c_str()); - if(is_slash(path.back())) - path.pop_back(); - } - else if(WCHAR *cwdbuf{_wgetcwd(nullptr, 0)}) - { - path = wstr_to_utf8(cwdbuf); - if(is_slash(path.back())) - path.pop_back(); - free(cwdbuf); - } - else - path = "."; - std::replace(path.begin(), path.end(), '/', '\\'); - DirectorySearch(path.c_str(), ext, &results); - -#if !defined(ALSOFT_UWP) && !defined(_GAMING_XBOX) +#if !ALSOFT_UWP && !defined(_GAMING_XBOX) /* Search the local and global data dirs. */ - for(auto id : std::array{CSIDL_APPDATA, CSIDL_COMMON_APPDATA}) + for(const auto &folderid : std::array{FOLDERID_RoamingAppData, FOLDERID_ProgramData}) { - WCHAR buffer[MAX_PATH]; - if(SHGetSpecialFolderPathW(nullptr, buffer, id, FALSE) == FALSE) + std::unique_ptr buffer; + const HRESULT hr{SHGetKnownFolderPath(folderid, KF_FLAG_DONT_UNEXPAND, nullptr, + al::out_ptr(buffer))}; + if(FAILED(hr) || !buffer || !*buffer) continue; - path = wstr_to_utf8(buffer); - if(!is_slash(path.back())) - path += '\\'; - path += subdir; - std::replace(path.begin(), path.end(), '/', '\\'); - - DirectorySearch(path.c_str(), ext, &results); + DirectorySearch(fs::path{buffer.get()}/path, ext, &results); } #endif return results; } -void SetRTPriority(void) +void SetRTPriority() { -#if !defined(ALSOFT_UWP) +#if !ALSOFT_UWP if(RTPrioLevel > 0) { if(!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL)) - ERR("Failed to set priority level for thread\n"); + ERR("Failed to set priority level for thread"); } #endif } @@ -206,7 +212,7 @@ void SetRTPriority(void) #include #include #endif -#ifdef HAVE_RTKIT +#if HAVE_RTKIT #include #include "dbus_wrap.h" @@ -218,184 +224,120 @@ void SetRTPriority(void) const PathNamePair &GetProcBinary() { - static std::optional procbin; - if(procbin) return *procbin; - - std::vector pathname; -#ifdef __FreeBSD__ - size_t pathlen; - int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; - if(sysctl(mib, 4, nullptr, &pathlen, nullptr, 0) == -1) - WARN("Failed to sysctl kern.proc.pathname: %s\n", strerror(errno)); - else + auto get_procbin = [] { - pathname.resize(pathlen + 1); - sysctl(mib, 4, pathname.data(), &pathlen, nullptr, 0); - pathname.resize(pathlen); - } + std::string pathname; +#ifdef __FreeBSD__ + size_t pathlen{}; + std::array mib{{CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1}}; + if(sysctl(mib.data(), mib.size(), nullptr, &pathlen, nullptr, 0) == -1) + WARN("Failed to sysctl kern.proc.pathname: {}", + std::generic_category().message(errno)); + else + { + auto procpath = std::vector(pathlen+1, '\0'); + sysctl(mib.data(), mib.size(), procpath.data(), &pathlen, nullptr, 0); + pathname = procpath.data(); + } #endif #ifdef HAVE_PROC_PIDPATH - if(pathname.empty()) - { - char procpath[PROC_PIDPATHINFO_MAXSIZE]{}; - const pid_t pid{getpid()}; - if(proc_pidpath(pid, procpath, sizeof(procpath)) < 1) - ERR("proc_pidpath(%d, ...) failed: %s\n", pid, strerror(errno)); - else - pathname.insert(pathname.end(), procpath, procpath+strlen(procpath)); - } + if(pathname.empty()) + { + std::array procpath{}; + const pid_t pid{getpid()}; + if(proc_pidpath(pid, procpath.data(), procpath.size()) < 1) + ERR("proc_pidpath({}, ...) failed: {}", pid, + std::generic_category().message(errno)); + else + pathname = procpath.data(); + } #endif #ifdef __HAIKU__ - if(pathname.empty()) - { - char procpath[PATH_MAX]; - if(find_path(B_APP_IMAGE_SYMBOL, B_FIND_PATH_IMAGE_PATH, NULL, procpath, sizeof(procpath)) == B_OK) - pathname.insert(pathname.end(), procpath, procpath+strlen(procpath)); - } + if(pathname.empty()) + { + std::array procpath{}; + if(find_path(B_APP_IMAGE_SYMBOL, B_FIND_PATH_IMAGE_PATH, NULL, procpath.data(), procpath.size()) == B_OK) + pathname = procpath.data(); + } #endif #ifndef __SWITCH__ - if(pathname.empty()) - { - const char *SelfLinkNames[]{ - "/proc/self/exe", - "/proc/self/file", - "/proc/curproc/exe", - "/proc/curproc/file" - }; - - pathname.resize(256); - - const char *selfname{}; - ssize_t len{}; - for(const char *name : SelfLinkNames) + if(pathname.empty()) { - selfname = name; - len = readlink(selfname, pathname.data(), pathname.size()); - if(len >= 0 || errno != ENOENT) break; + const std::array SelfLinkNames{ + "/proc/self/exe"sv, + "/proc/self/file"sv, + "/proc/curproc/exe"sv, + "/proc/curproc/file"sv, + }; + + for(const std::string_view name : SelfLinkNames) + { + try { + if(!fs::exists(name)) + continue; + if(auto path = fs::read_symlink(name); !path.empty()) + { + pathname = al::u8_as_char(path.u8string()); + break; + } + } + catch(std::exception& e) { + WARN("Exception getting symlink {}: {}", name, e.what()); + } + } } +#endif - while(len > 0 && static_cast(len) == pathname.size()) - { - pathname.resize(pathname.size() << 1); - len = readlink(selfname, pathname.data(), pathname.size()); - } - if(len <= 0) + PathNamePair res{}; + if(auto seppos = pathname.rfind('/'); seppos < pathname.size()) { - WARN("Failed to readlink %s: %s\n", selfname, strerror(errno)); - len = 0; + res.path = std::string_view{pathname}.substr(0, seppos); + res.fname = std::string_view{pathname}.substr(seppos+1); } + else + res.fname = pathname; - pathname.resize(static_cast(len)); - } -#endif - while(!pathname.empty() && pathname.back() == 0) - pathname.pop_back(); - - auto sep = std::find(pathname.crbegin(), pathname.crend(), '/'); - if(sep != pathname.crend()) - procbin.emplace(std::string(pathname.cbegin(), sep.base()-1), - std::string(sep.base(), pathname.cend())); - else - procbin.emplace(std::string{}, std::string(pathname.cbegin(), pathname.cend())); - - TRACE("Got binary: \"%s\", \"%s\"\n", procbin->path.c_str(), procbin->fname.c_str()); - return *procbin; + TRACE("Got binary: \"{}\", \"{}\"", res.path, res.fname); + return res; + }; + static const PathNamePair procbin{get_procbin()}; + return procbin; } -namespace { - -void DirectorySearch(const char *path, const char *ext, std::vector *const results) +auto SearchDataFiles(const std::string_view ext) -> std::vector { - TRACE("Searching %s for *%s\n", path, ext); - DIR *dir{opendir(path)}; - if(!dir) return; - - const auto base = results->size(); - const size_t extlen{strlen(ext)}; - - while(struct dirent *dirent{readdir(dir)}) - { - if(strcmp(dirent->d_name, ".") == 0 || strcmp(dirent->d_name, "..") == 0) - continue; + auto srchlock = std::lock_guard{gSearchLock}; - const size_t len{strlen(dirent->d_name)}; - if(len <= extlen) continue; - if(al::strcasecmp(dirent->d_name+len-extlen, ext) != 0) - continue; - - results->emplace_back(); - std::string &str = results->back(); - str = path; - if(str.back() != '/') - str.push_back('/'); - str += dirent->d_name; - } - closedir(dir); + /* Search the app-local directory. */ + auto results = std::vector{}; + if(auto localpath = al::getenv("ALSOFT_LOCAL_PATH")) + DirectorySearch(*localpath, ext, &results); + else if(auto curpath = fs::current_path(); !curpath.empty()) + DirectorySearch(curpath, ext, &results); - const al::span newlist{results->data()+base, results->size()-base}; - std::sort(newlist.begin(), newlist.end()); - for(const auto &name : newlist) - TRACE(" got %s\n", name.c_str()); + return results; } -} // namespace - -std::vector SearchDataFiles(const char *ext, const char *subdir) +auto SearchDataFiles(const std::string_view ext, const std::string_view subdir) + -> std::vector { - static std::mutex search_lock; - std::lock_guard _{search_lock}; + std::lock_guard srchlock{gSearchLock}; std::vector results; - if(subdir[0] == '/') + auto path = fs::u8path(subdir); + if(path.is_absolute()) { - DirectorySearch(subdir, ext, &results); + DirectorySearch(path, ext, &results); return results; } - /* Search the app-local directory. */ - if(auto localpath = al::getenv("ALSOFT_LOCAL_PATH")) - DirectorySearch(localpath->c_str(), ext, &results); - else - { - std::vector cwdbuf(256); - while(!getcwd(cwdbuf.data(), cwdbuf.size())) - { - if(errno != ERANGE) - { - cwdbuf.clear(); - break; - } - cwdbuf.resize(cwdbuf.size() << 1); - } - if(cwdbuf.empty()) - DirectorySearch(".", ext, &results); - else - { - DirectorySearch(cwdbuf.data(), ext, &results); - cwdbuf.clear(); - } - } - - // Search local data dir + /* Search local data dir */ if(auto datapath = al::getenv("XDG_DATA_HOME")) - { - std::string &path = *datapath; - if(path.back() != '/') - path += '/'; - path += subdir; - DirectorySearch(path.c_str(), ext, &results); - } + DirectorySearch(fs::path{*datapath}/path, ext, &results); else if(auto homepath = al::getenv("HOME")) - { - std::string &path = *homepath; - if(path.back() == '/') - path.pop_back(); - path += "/.local/share/"; - path += subdir; - DirectorySearch(path.c_str(), ext, &results); - } + DirectorySearch(fs::path{*homepath}/".local/share"/path, ext, &results); - // Search global data dirs + /* Search global data dirs */ std::string datadirs{al::getenv("XDG_DATA_DIRS").value_or("/usr/local/share/:/usr/share/")}; size_t curpos{0u}; @@ -403,30 +345,19 @@ std::vector SearchDataFiles(const char *ext, const char *subdir) { size_t nextpos{datadirs.find(':', curpos)}; - std::string path{(nextpos != std::string::npos) ? - datadirs.substr(curpos, nextpos++ - curpos) : datadirs.substr(curpos)}; + std::string_view pathname{(nextpos != std::string::npos) + ? std::string_view{datadirs}.substr(curpos, nextpos++ - curpos) + : std::string_view{datadirs}.substr(curpos)}; curpos = nextpos; - if(path.empty()) continue; - if(path.back() != '/') - path += '/'; - path += subdir; - - DirectorySearch(path.c_str(), ext, &results); + if(!pathname.empty()) + DirectorySearch(fs::path{pathname}/path, ext, &results); } #ifdef ALSOFT_INSTALL_DATADIR - // Search the installation data directory - { - std::string path{ALSOFT_INSTALL_DATADIR}; - if(!path.empty()) - { - if(path.back() != '/') - path += '/'; - path += subdir; - DirectorySearch(path.c_str(), ext, &results); - } - } + /* Search the installation data directory */ + if(auto instpath = fs::path{ALSOFT_INSTALL_DATADIR}; !instpath.empty()) + DirectorySearch(instpath/path, ext, &results); #endif return results; @@ -447,7 +378,7 @@ bool SetRTPriorityPthread(int prio [[maybe_unused]]) rtmax = (rtmax-rtmin)/2 + rtmin; struct sched_param param{}; - param.sched_priority = clampi(prio, rtmin, rtmax); + param.sched_priority = std::clamp(prio, rtmin, rtmax); #ifdef SCHED_RESET_ON_FORK err = pthread_setschedparam(pthread_self(), SCHED_RR|SCHED_RESET_ON_FORK, ¶m); if(err == EINVAL) @@ -455,23 +386,23 @@ bool SetRTPriorityPthread(int prio [[maybe_unused]]) err = pthread_setschedparam(pthread_self(), SCHED_RR, ¶m); if(err == 0) return true; #endif - WARN("pthread_setschedparam failed: %s (%d)\n", std::strerror(err), err); + WARN("pthread_setschedparam failed: {} ({})", std::generic_category().message(err), err); return false; } bool SetRTPriorityRTKit(int prio [[maybe_unused]]) { -#ifdef HAVE_RTKIT +#if HAVE_RTKIT if(!HasDBus()) { - WARN("D-Bus not available\n"); + WARN("D-Bus not available"); return false; } dbus::Error error; dbus::ConnectionPtr conn{dbus_bus_get(DBUS_BUS_SYSTEM, &error.get())}; if(!conn) { - WARN("D-Bus connection failed with %s: %s\n", error->name, error->message); + WARN("D-Bus connection failed with {}: {}", error->name, error->message); return false; } @@ -483,11 +414,11 @@ bool SetRTPriorityRTKit(int prio [[maybe_unused]]) if(err == -ENOENT) { err = std::abs(err); - ERR("Could not query RTKit: %s (%d)\n", std::strerror(err), err); + ERR("Could not query RTKit: {} ({})", std::generic_category().message(err), err); return false; } int rtmax{rtkit_get_max_realtime_priority(conn.get())}; - TRACE("Maximum real-time priority: %d, minimum niceness: %d\n", rtmax, nicemin); + TRACE("Maximum real-time priority: {}, minimum niceness: {}", rtmax, nicemin); auto limit_rttime = [](DBusConnection *c) -> int { @@ -500,8 +431,7 @@ bool SetRTPriorityRTKit(int prio [[maybe_unused]]) if(getrlimit(RLIMIT_RTTIME, &rlim) != 0) return errno; - TRACE("RTTime max: %llu (hard: %llu, soft: %llu)\n", umaxtime, - static_cast(rlim.rlim_max), static_cast(rlim.rlim_cur)); + TRACE("RTTime max: {} (hard: {}, soft: {})", umaxtime, rlim.rlim_max, rlim.rlim_cur); if(rlim.rlim_max > umaxtime) { rlim.rlim_max = static_cast(std::min(umaxtime, @@ -518,20 +448,21 @@ bool SetRTPriorityRTKit(int prio [[maybe_unused]]) { err = limit_rttime(conn.get()); if(err != 0) - WARN("Failed to set RLIMIT_RTTIME for RTKit: %s (%d)\n", - std::strerror(err), err); + WARN("Failed to set RLIMIT_RTTIME for RTKit: {} ({})", + std::generic_category().message(err), err); } /* Limit the maximum real-time priority to half. */ rtmax = (rtmax+1)/2; - prio = clampi(prio, 1, rtmax); + prio = std::clamp(prio, 1, rtmax); - TRACE("Making real-time with priority %d (max: %d)\n", prio, rtmax); + TRACE("Making real-time with priority {} (max: {})", prio, rtmax); err = rtkit_make_realtime(conn.get(), 0, prio); if(err == 0) return true; err = std::abs(err); - WARN("Failed to set real-time priority: %s (%d)\n", std::strerror(err), err); + WARN("Failed to set real-time priority: {} ({})", + std::generic_category().message(err), err); } /* Don't try to set the niceness for non-Linux systems. Standard POSIX has * niceness as a per-process attribute, while the intent here is for the @@ -541,18 +472,18 @@ bool SetRTPriorityRTKit(int prio [[maybe_unused]]) #ifdef __linux__ if(nicemin < 0) { - TRACE("Making high priority with niceness %d\n", nicemin); + TRACE("Making high priority with niceness {}", nicemin); err = rtkit_make_high_priority(conn.get(), 0, nicemin); if(err == 0) return true; err = std::abs(err); - WARN("Failed to set high priority: %s (%d)\n", std::strerror(err), err); + WARN("Failed to set high priority: {} ({})", std::generic_category().message(err), err); } #endif /* __linux__ */ #else - WARN("D-Bus not supported\n"); + WARN("D-Bus not supported"); #endif return false; } diff --git a/3rdparty/openal/core/helpers.h b/3rdparty/openal/core/helpers.h index df51c1161de4..96987a2e8743 100644 --- a/3rdparty/openal/core/helpers.h +++ b/3rdparty/openal/core/helpers.h @@ -1,26 +1,26 @@ #ifndef CORE_HELPERS_H #define CORE_HELPERS_H -#include #include +#include #include struct PathNamePair { std::string path, fname; - - PathNamePair() = default; - template - PathNamePair(T&& path_, U&& fname_) - : path{std::forward(path_)}, fname{std::forward(fname_)} - { } }; -const PathNamePair &GetProcBinary(void); +const PathNamePair &GetProcBinary(); + +/* Mixing thread priority level */ +inline int RTPrioLevel{1}; + +/* Allow reducing the process's RTTime limit for RTKit. */ +inline bool AllowRTTimeLimit{true}; -extern int RTPrioLevel; -extern bool AllowRTTimeLimit; -void SetRTPriority(void); +void SetRTPriority(); -std::vector SearchDataFiles(const char *match, const char *subdir); +auto SearchDataFiles(const std::string_view ext) -> std::vector; +auto SearchDataFiles(const std::string_view ext, const std::string_view subdir) + -> std::vector; #endif /* CORE_HELPERS_H */ diff --git a/3rdparty/openal/core/hrtf.cpp b/3rdparty/openal/core/hrtf.cpp index 9a13a0045056..ccd0a11a6d65 100644 --- a/3rdparty/openal/core/hrtf.cpp +++ b/3rdparty/openal/core/hrtf.cpp @@ -18,18 +18,21 @@ #include #include #include +#include #include #include #include #include "albit.h" -#include "alfstream.h" #include "almalloc.h" #include "alnumbers.h" #include "alnumeric.h" #include "alspan.h" +#include "alstring.h" #include "ambidefs.h" +#include "filesystem.h" #include "filters/splitter.h" +#include "fmt/core.h" #include "helpers.h" #include "logging.h" #include "mixer/hrtfdefs.h" @@ -39,10 +42,16 @@ namespace { +using namespace std::string_view_literals; + struct HrtfEntry { std::string mDispName; std::string mFilename; + template + HrtfEntry(T&& dispname, U&& fname) + : mDispName{std::forward(dispname)}, mFilename{std::forward(fname)} + { } /* GCC warns when it tries to inline this. */ ~HrtfEntry(); }; @@ -50,11 +59,12 @@ HrtfEntry::~HrtfEntry() = default; struct LoadedHrtf { std::string mFilename; + uint mSampleRate{}; std::unique_ptr mEntry; template - LoadedHrtf(T&& name, U&& entry) - : mFilename{std::forward(name)}, mEntry{std::forward(entry)} + LoadedHrtf(T&& name, uint srate, U&& entry) + : mFilename{std::forward(name)}, mSampleRate{srate}, mEntry{std::forward(entry)} { } LoadedHrtf(LoadedHrtf&&) = default; /* GCC warns when it tries to inline this. */ @@ -86,12 +96,24 @@ constexpr uint HrirDelayFracBits{2}; constexpr uint HrirDelayFracOne{1 << HrirDelayFracBits}; constexpr uint HrirDelayFracHalf{HrirDelayFracOne >> 1}; +/* The sample rate is stored as a 24-bit integer, so 16MHz is the largest + * supported. + */ +constexpr uint MaxSampleRate{0xff'ff'ff}; + static_assert(MaxHrirDelay*HrirDelayFracOne < 256, "MAX_HRIR_DELAY or DELAY_FRAC too large"); -constexpr char magicMarker00[8]{'M','i','n','P','H','R','0','0'}; -constexpr char magicMarker01[8]{'M','i','n','P','H','R','0','1'}; -constexpr char magicMarker02[8]{'M','i','n','P','H','R','0','2'}; -constexpr char magicMarker03[8]{'M','i','n','P','H','R','0','3'}; + +constexpr auto HeaderMarkerSize = 8_uz; +[[nodiscard]] constexpr auto GetMarker00Name() noexcept { return "MinPHR00"sv; } +[[nodiscard]] constexpr auto GetMarker01Name() noexcept { return "MinPHR01"sv; } +[[nodiscard]] constexpr auto GetMarker02Name() noexcept { return "MinPHR02"sv; } +[[nodiscard]] constexpr auto GetMarker03Name() noexcept { return "MinPHR03"sv; } + +static_assert(GetMarker00Name().size() == HeaderMarkerSize); +static_assert(GetMarker01Name().size() == HeaderMarkerSize); +static_assert(GetMarker02Name().size() == HeaderMarkerSize); +static_assert(GetMarker03Name().size() == HeaderMarkerSize); /* First value for pass-through coefficients (remaining are 0), used for omni- * directional sounds. */ @@ -104,6 +126,11 @@ std::mutex EnumeratedHrtfLock; std::vector EnumeratedHrtfs; +/* NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) + * To access a memory buffer through the std::istream interface, a custom + * std::streambuf implementation is needed that has to do pointer manipulation + * for seeking. With C++23, we may be able to use std::spanstream instead. + */ class databuf final : public std::streambuf { int_type underflow() override { return traits_type::eof(); } @@ -113,34 +140,32 @@ class databuf final : public std::streambuf { if((mode&std::ios_base::out) || !(mode&std::ios_base::in)) return traits_type::eof(); - char_type *cur; switch(whence) { - case std::ios_base::beg: - if(offset < 0 || offset > egptr()-eback()) - return traits_type::eof(); - cur = eback() + offset; - break; - - case std::ios_base::cur: - if((offset >= 0 && offset > egptr()-gptr()) || - (offset < 0 && -offset > gptr()-eback())) - return traits_type::eof(); - cur = gptr() + offset; - break; + case std::ios_base::beg: + if(offset < 0 || offset > egptr()-eback()) + return traits_type::eof(); + setg(eback(), eback()+offset, egptr()); + break; - case std::ios_base::end: - if(offset > 0 || -offset > egptr()-eback()) - return traits_type::eof(); - cur = egptr() + offset; - break; + case std::ios_base::cur: + if((offset >= 0 && offset > egptr()-gptr()) || + (offset < 0 && -offset > gptr()-eback())) + return traits_type::eof(); + setg(eback(), gptr()+offset, egptr()); + break; - default: + case std::ios_base::end: + if(offset > 0 || -offset > egptr()-eback()) return traits_type::eof(); + setg(eback(), egptr()+offset, egptr()); + break; + + default: + return traits_type::eof(); } - setg(eback(), cur, egptr()); - return cur - eback(); + return gptr() - eback(); } pos_type seekpos(pos_type pos, std::ios_base::openmode mode) override @@ -152,24 +177,23 @@ class databuf final : public std::streambuf { if(pos < 0 || pos > egptr()-eback()) return traits_type::eof(); - setg(eback(), eback() + static_cast(pos), egptr()); + setg(eback(), eback()+static_cast(pos), egptr()); return pos; } public: - databuf(const char_type *start_, const char_type *end_) noexcept + explicit databuf(const al::span data) noexcept { - setg(const_cast(start_), const_cast(start_), - const_cast(end_)); + setg(data.data(), data.data(), al::to_address(data.end())); } }; +/* NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) */ class idstream final : public std::istream { databuf mStreamBuf; public: - idstream(const char *start_, const char *end_) - : std::istream{nullptr}, mStreamBuf{start_, end_} + explicit idstream(const al::span data) : std::istream{nullptr}, mStreamBuf{data} { init(&mStreamBuf); } }; @@ -180,11 +204,10 @@ struct IdxBlend { uint idx; float blend; }; */ IdxBlend CalcEvIndex(uint evcount, float ev) { - ev = (al::numbers::pi_v*0.5f + ev) * static_cast(evcount-1) * - al::numbers::inv_pi_v; - uint idx{float2uint(ev)}; + ev = (al::numbers::inv_pi_v*ev + 0.5f) * static_cast(evcount-1); - return IdxBlend{minu(idx, evcount-1), ev-static_cast(idx)}; + const auto idx = float2uint(ev); + return IdxBlend{std::min(idx, evcount-1u), ev-static_cast(idx)}; } /* Calculate the azimuth index given the polar azimuth in radians. This will @@ -192,10 +215,9 @@ IdxBlend CalcEvIndex(uint evcount, float ev) */ IdxBlend CalcAzIndex(uint azcount, float az) { - az = (al::numbers::pi_v*2.0f + az) * static_cast(azcount) * - (al::numbers::inv_pi_v*0.5f); - uint idx{float2uint(az)}; + az = (al::numbers::inv_pi_v*0.5f*az + 1.0f) * static_cast(azcount); + const auto idx = float2uint(az); return IdxBlend{idx%azcount, az-static_cast(idx)}; } @@ -206,7 +228,7 @@ IdxBlend CalcAzIndex(uint azcount, float az) * and azimuth in radians. The coefficients are normalized. */ void HrtfStore::getCoeffs(float elevation, float azimuth, float distance, float spread, - HrirArray &coeffs, const al::span delays) + const HrirSpan coeffs, const al::span delays) const { const float dirfact{1.0f - (al::numbers::inv_pi_v/2.0f * spread)}; @@ -222,7 +244,7 @@ void HrtfStore::getCoeffs(float elevation, float azimuth, float distance, float /* Calculate the elevation indices. */ const auto elev0 = CalcEvIndex(field->evCount, elevation); - const size_t elev1_idx{minu(elev0.idx+1, field->evCount-1)}; + const size_t elev1_idx{std::min(elev0.idx+1u, field->evCount-1u)}; const size_t ir0offset{mElev[ebase + elev0.idx].irOffset}; const size_t ir1offset{mElev[ebase + elev1_idx].irOffset}; @@ -231,43 +253,43 @@ void HrtfStore::getCoeffs(float elevation, float azimuth, float distance, float const auto az1 = CalcAzIndex(mElev[ebase + elev1_idx].azCount, azimuth); /* Calculate the HRIR indices to blend. */ - const size_t idx[4]{ + const std::array idx{{ ir0offset + az0.idx, ir0offset + ((az0.idx+1) % mElev[ebase + elev0.idx].azCount), ir1offset + az1.idx, ir1offset + ((az1.idx+1) % mElev[ebase + elev1_idx].azCount) - }; + }}; /* Calculate bilinear blending weights, attenuated according to the * directional panning factor. */ - const float blend[4]{ + const std::array blend{{ (1.0f-elev0.blend) * (1.0f-az0.blend) * dirfact, (1.0f-elev0.blend) * ( az0.blend) * dirfact, ( elev0.blend) * (1.0f-az1.blend) * dirfact, ( elev0.blend) * ( az1.blend) * dirfact - }; + }}; /* Calculate the blended HRIR delays. */ - float d{mDelays[idx[0]][0]*blend[0] + mDelays[idx[1]][0]*blend[1] + mDelays[idx[2]][0]*blend[2] - + mDelays[idx[3]][0]*blend[3]}; + float d{float(mDelays[idx[0]][0])*blend[0] + float(mDelays[idx[1]][0])*blend[1] + + float(mDelays[idx[2]][0])*blend[2] + float(mDelays[idx[3]][0])*blend[3]}; delays[0] = fastf2u(d * float{1.0f/HrirDelayFracOne}); - d = mDelays[idx[0]][1]*blend[0] + mDelays[idx[1]][1]*blend[1] + mDelays[idx[2]][1]*blend[2] - + mDelays[idx[3]][1]*blend[3]; + d = float(mDelays[idx[0]][1])*blend[0] + float(mDelays[idx[1]][1])*blend[1] + + float(mDelays[idx[2]][1])*blend[2] + float(mDelays[idx[3]][1])*blend[3]; delays[1] = fastf2u(d * float{1.0f/HrirDelayFracOne}); /* Calculate the blended HRIR coefficients. */ - float *coeffout{al::assume_aligned<16>(coeffs[0].data())}; - coeffout[0] = PassthruCoeff * (1.0f-dirfact); - coeffout[1] = PassthruCoeff * (1.0f-dirfact); - std::fill_n(coeffout+2, size_t{HrirLength-1}*2, 0.0f); + auto coeffout = coeffs.begin(); + coeffout[0][0] = PassthruCoeff * (1.0f-dirfact); + coeffout[0][1] = PassthruCoeff * (1.0f-dirfact); + std::fill_n(coeffout+1, size_t{HrirLength-1}, std::array{0.0f, 0.0f}); for(size_t c{0};c < 4;c++) { - const float *srccoeffs{al::assume_aligned<16>(mCoeffs[idx[c]][0].data())}; const float mult{blend[c]}; - auto blend_coeffs = [mult](const float src, const float coeff) noexcept -> float - { return src*mult + coeff; }; - std::transform(srccoeffs, srccoeffs + HrirLength*2, coeffout, coeffout, blend_coeffs); + auto blend_coeffs = [mult](const float2 &src, const float2 &coeff) noexcept -> float2 + { return float2{{src[0]*mult + coeff[0], src[1]*mult + coeff[1]}}; }; + std::transform(mCoeffs[idx[c]].cbegin(), mCoeffs[idx[c]].cend(), coeffout, coeffout, + blend_coeffs); } } @@ -276,7 +298,8 @@ std::unique_ptr DirectHrtfState::Create(size_t num_chans) { return std::unique_ptr{new(FamCount(num_chans)) DirectHrtfState{num_chans}}; } void DirectHrtfState::build(const HrtfStore *Hrtf, const uint irSize, const bool perHrirMin, - const al::span AmbiPoints, const float (*AmbiMatrix)[MaxAmbiChannels], + const al::span AmbiPoints, + const al::span> AmbiMatrix, const float XOverFreq, const al::span AmbiOrderHFGain) { using double2 = std::array; @@ -287,7 +310,8 @@ void DirectHrtfState::build(const HrtfStore *Hrtf, const uint irSize, const bool const double xover_norm{double{XOverFreq} / Hrtf->mSampleRate}; mChannels[0].mSplitter.init(static_cast(xover_norm)); - for(size_t i{0};i < mChannels.size();++i) + mChannels[0].mHfScale = AmbiOrderHFGain[0]; + for(size_t i{1};i < mChannels.size();++i) { const size_t order{AmbiIndex::OrderFromChannel[i]}; mChannels[i].mSplitter = mChannels[0].mSplitter; @@ -300,14 +324,14 @@ void DirectHrtfState::build(const HrtfStore *Hrtf, const uint irSize, const bool { auto &field = Hrtf->mFields[0]; const auto elev0 = CalcEvIndex(field.evCount, pt.Elev.value); - const size_t elev1_idx{minu(elev0.idx+1, field.evCount-1)}; + const size_t elev1_idx{std::min(elev0.idx+1u, field.evCount-1u)}; const size_t ir0offset{Hrtf->mElev[elev0.idx].irOffset}; const size_t ir1offset{Hrtf->mElev[elev1_idx].irOffset}; const auto az0 = CalcAzIndex(Hrtf->mElev[elev0.idx].azCount, pt.Azim.value); const auto az1 = CalcAzIndex(Hrtf->mElev[elev1_idx].azCount, pt.Azim.value); - const size_t idx[4]{ + const std::array idx{ ir0offset + az0.idx, ir0offset + ((az0.idx+1) % Hrtf->mElev[elev0.idx].azCount), ir1offset + az1.idx, @@ -319,8 +343,8 @@ void DirectHrtfState::build(const HrtfStore *Hrtf, const uint irSize, const bool ImpulseResponse res{Hrtf->mCoeffs[irOffset], Hrtf->mDelays[irOffset][0], Hrtf->mDelays[irOffset][1]}; - min_delay = minu(min_delay, minu(res.ldelay, res.rdelay)); - max_delay = maxu(max_delay, maxu(res.ldelay, res.rdelay)); + min_delay = std::min(min_delay, std::min(res.ldelay, res.rdelay)); + max_delay = std::max(max_delay, std::max(res.ldelay, res.rdelay)); return res; }; @@ -328,44 +352,48 @@ void DirectHrtfState::build(const HrtfStore *Hrtf, const uint irSize, const bool auto hrir_delay_round = [](const uint d) noexcept -> uint { return (d+HrirDelayFracHalf) >> HrirDelayFracBits; }; - TRACE("Min delay: %.2f, max delay: %.2f, FIR length: %u\n", + TRACE("Min delay: {:.2f}, max delay: {:.2f}, FIR length: {}", min_delay/double{HrirDelayFracOne}, max_delay/double{HrirDelayFracOne}, irSize); auto tmpres = std::vector>(mChannels.size()); max_delay = 0; - for(size_t c{0u};c < AmbiPoints.size();++c) + auto matrixline = AmbiMatrix.cbegin(); + for(auto &impulse : impres) { - const ConstHrirSpan hrir{impres[c].hrir}; - const uint base_delay{perHrirMin ? minu(impres[c].ldelay, impres[c].rdelay) : min_delay}; - const uint ldelay{hrir_delay_round(impres[c].ldelay - base_delay)}; - const uint rdelay{hrir_delay_round(impres[c].rdelay - base_delay)}; - max_delay = maxu(max_delay, maxu(impres[c].ldelay, impres[c].rdelay) - base_delay); - - for(size_t i{0u};i < mChannels.size();++i) + const ConstHrirSpan hrir{impulse.hrir}; + const uint base_delay{perHrirMin ? std::min(impulse.ldelay, impulse.rdelay) : min_delay}; + const uint ldelay{hrir_delay_round(impulse.ldelay - base_delay)}; + const uint rdelay{hrir_delay_round(impulse.rdelay - base_delay)}; + max_delay = std::max(max_delay, std::max(impulse.ldelay, impulse.rdelay) - base_delay); + + auto gains = matrixline->cbegin(); + ++matrixline; + for(auto &result : tmpres) { - const double mult{AmbiMatrix[c][i]}; - const size_t numirs{HrirLength - maxz(ldelay, rdelay)}; + const double mult{*(gains++)}; + const size_t numirs{HrirLength - std::max(ldelay, rdelay)}; size_t lidx{ldelay}, ridx{rdelay}; for(size_t j{0};j < numirs;++j) { - tmpres[i][lidx++][0] += hrir[j][0] * mult; - tmpres[i][ridx++][1] += hrir[j][1] * mult; + result[lidx++][0] += hrir[j][0] * mult; + result[ridx++][1] += hrir[j][1] * mult; } } } impres.clear(); - for(size_t i{0u};i < mChannels.size();++i) + auto output = mChannels.begin(); + for(auto &result : tmpres) { - auto copy_arr = [](const double2 &in) noexcept -> float2 + auto cast_array2 = [](const double2 &in) noexcept -> float2 { return float2{{static_cast(in[0]), static_cast(in[1])}}; }; - std::transform(tmpres[i].cbegin(), tmpres[i].cend(), mChannels[i].mCoeffs.begin(), - copy_arr); + std::transform(result.cbegin(), result.cend(), output->mCoeffs.begin(), cast_array2); + ++output; } tmpres.clear(); - const uint max_length{minu(hrir_delay_round(max_delay) + irSize, HrirLength)}; - TRACE("New max delay: %.2f, FIR length: %u\n", max_delay/double{HrirDelayFracOne}, + const uint max_length{std::min(hrir_delay_round(max_delay) + irSize, HrirLength)}; + TRACE("New max delay: {:.2f}, FIR length: {}", max_delay/double{HrirDelayFracOne}, max_length); mIrSize = max_length; } @@ -376,8 +404,15 @@ namespace { std::unique_ptr CreateHrtfStore(uint rate, uint8_t irSize, const al::span fields, const al::span elevs, const HrirArray *coeffs, - const ubyte2 *delays, const char *filename) + const ubyte2 *delays) { + static_assert(alignof(HrtfStore::Field) <= alignof(HrtfStore)); + static_assert(alignof(HrtfStore::Elevation) <= alignof(HrtfStore)); + static_assert(16 <= alignof(HrtfStore)); + + if(rate > MaxSampleRate) + throw std::runtime_error{"Sample rate is too large (max: "+std::to_string(MaxSampleRate)+"hz)"}; + const size_t irCount{size_t{elevs.back().azCount} + elevs.back().irOffset}; size_t total{sizeof(HrtfStore)}; total = RoundUp(total, alignof(HrtfStore::Field)); /* Align for field infos */ @@ -388,56 +423,54 @@ std::unique_ptr CreateHrtfStore(uint rate, uint8_t irSize, total += sizeof(std::declval().mCoeffs[0])*irCount; total += sizeof(std::declval().mDelays[0])*irCount; - std::unique_ptr Hrtf{}; - if(void *ptr{al_calloc(16, total)}) - { - Hrtf.reset(al::construct_at(static_cast(ptr))); - InitRef(Hrtf->mRef, 1u); - Hrtf->mSampleRate = rate & 0xff'ff'ff; - Hrtf->mIrSize = irSize; - - /* Set up pointers to storage following the main HRTF struct. */ - char *base = reinterpret_cast(Hrtf.get()); - size_t offset{sizeof(HrtfStore)}; - - offset = RoundUp(offset, alignof(HrtfStore::Field)); /* Align for field infos */ - auto field_ = reinterpret_cast(base + offset); - offset += sizeof(field_[0])*fields.size(); - - offset = RoundUp(offset, alignof(HrtfStore::Elevation)); /* Align for elevation infos */ - auto elev_ = reinterpret_cast(base + offset); - offset += sizeof(elev_[0])*elevs.size(); - - offset = RoundUp(offset, 16); /* Align for coefficients using SIMD */ - auto coeffs_ = reinterpret_cast(base + offset); - offset += sizeof(coeffs_[0])*irCount; - - auto delays_ = reinterpret_cast(base + offset); - offset += sizeof(delays_[0])*irCount; - - if(offset != total) - throw std::runtime_error{"HrtfStore allocation size mismatch"}; - - /* Copy input data to storage. */ - std::uninitialized_copy(fields.cbegin(), fields.cend(), field_); - std::uninitialized_copy(elevs.cbegin(), elevs.cend(), elev_); - std::uninitialized_copy_n(coeffs, irCount, coeffs_); - std::uninitialized_copy_n(delays, irCount, delays_); - - /* Finally, assign the storage pointers. */ - Hrtf->mFields = {field_, fields.size()}; - Hrtf->mElev = elev_; - Hrtf->mCoeffs = coeffs_; - Hrtf->mDelays = delays_; - } - else - ERR("Out of memory allocating storage for %s.\n", filename); + static constexpr auto AlignVal = std::align_val_t{alignof(HrtfStore)}; + std::unique_ptr Hrtf{::new(::operator new[](total, AlignVal)) HrtfStore{}}; + Hrtf->mRef.store(1u, std::memory_order_relaxed); + Hrtf->mSampleRate = rate & 0xff'ff'ff; + Hrtf->mIrSize = irSize; + + /* Set up pointers to storage following the main HRTF struct. */ + auto storage = al::span{reinterpret_cast(Hrtf.get()), total}; + auto base = storage.begin(); + ptrdiff_t offset{sizeof(HrtfStore)}; + + offset = RoundUp(offset, alignof(HrtfStore::Field)); /* Align for field infos */ + auto field_ = al::span{reinterpret_cast(al::to_address(base + offset)), + fields.size()}; + offset += ptrdiff_t(sizeof(field_[0])*fields.size()); + + offset = RoundUp(offset, alignof(HrtfStore::Elevation)); /* Align for elevation infos */ + auto elev_ = al::span{reinterpret_cast(al::to_address(base + offset)), + elevs.size()}; + offset += ptrdiff_t(sizeof(elev_[0])*elevs.size()); + + offset = RoundUp(offset, 16); /* Align for coefficients using SIMD */ + auto coeffs_ = al::span{reinterpret_cast(al::to_address(base + offset)), irCount}; + offset += ptrdiff_t(sizeof(coeffs_[0])*irCount); + + auto delays_ = al::span{reinterpret_cast(al::to_address(base + offset)), irCount}; + offset += ptrdiff_t(sizeof(delays_[0])*irCount); + + if(size_t(offset) != total) + throw std::runtime_error{"HrtfStore allocation size mismatch"}; + + /* Copy input data to storage. */ + std::uninitialized_copy(fields.cbegin(), fields.cend(), field_.begin()); + std::uninitialized_copy(elevs.cbegin(), elevs.cend(), elev_.begin()); + std::uninitialized_copy_n(coeffs, irCount, coeffs_.begin()); + std::uninitialized_copy_n(delays, irCount, delays_.begin()); + + /* Finally, assign the storage pointers. */ + Hrtf->mFields = field_; + Hrtf->mElev = elev_; + Hrtf->mCoeffs = coeffs_; + Hrtf->mDelays = delays_; return Hrtf; } -void MirrorLeftHrirs(const al::span elevs, HrirArray *coeffs, - ubyte2 *delays) +void MirrorLeftHrirs(const al::span elevs, al::span coeffs, + al::span delays) { for(const auto &elev : elevs) { @@ -477,11 +510,11 @@ T> readle(std::istream &data) static_assert((num_bits&7) == 0, "num_bits must be a multiple of 8"); static_assert(num_bits <= sizeof(T)*8, "num_bits is too large for the type"); - T ret{}; - if(!data.read(reinterpret_cast(&ret), num_bits/8)) + alignas(T) std::array ret{}; + if(!data.read(ret.data(), num_bits/8)) return static_cast(EOF); - return fixsign(ret); + return fixsign(al::bit_cast(ret)); } template @@ -491,13 +524,12 @@ T> readle(std::istream &data) static_assert((num_bits&7) == 0, "num_bits must be a multiple of 8"); static_assert(num_bits <= sizeof(T)*8, "num_bits is too large for the type"); - T ret{}; - std::byte b[sizeof(T)]{}; - if(!data.read(reinterpret_cast(b), num_bits/8)) + alignas(T) std::array ret{}; + if(!data.read(ret.data(), num_bits/8)) return static_cast(EOF); - std::reverse_copy(std::begin(b), std::end(b), reinterpret_cast(&ret)); + std::reverse(ret.begin(), ret.end()); - return fixsign(ret); + return fixsign(al::bit_cast(ret)); } template<> @@ -505,27 +537,24 @@ inline uint8_t readle(std::istream &data) { return static_cast(data.get()); } -std::unique_ptr LoadHrtf00(std::istream &data, const char *filename) +std::unique_ptr LoadHrtf00(std::istream &data) { uint rate{readle(data)}; ushort irCount{readle(data)}; ushort irSize{readle(data)}; ubyte evCount{readle(data)}; if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } + throw std::runtime_error{"Premature end of file"}; if(irSize < MinIrLength || irSize > HrirLength) { - ERR("Unsupported HRIR size, irSize=%d (%d to %d)\n", irSize, MinIrLength, HrirLength); + ERR("Unsupported HRIR size, irSize={} ({} to {})", irSize, MinIrLength, HrirLength); return nullptr; } if(evCount < MinEvCount || evCount > MaxEvCount) { - ERR("Unsupported elevation count: evCount=%d (%d to %d)\n", - evCount, MinEvCount, MaxEvCount); + ERR("Unsupported elevation count: evCount={} ({} to {})", evCount, MinEvCount, + MaxEvCount); return nullptr; } @@ -533,23 +562,21 @@ std::unique_ptr LoadHrtf00(std::istream &data, const char *filename) for(auto &elev : elevs) elev.irOffset = readle(data); if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } + throw std::runtime_error{"Premature end of file"}; + for(size_t i{1};i < evCount;i++) { if(elevs[i].irOffset <= elevs[i-1].irOffset) { - ERR("Invalid evOffset: evOffset[%zu]=%d (last=%d)\n", i, elevs[i].irOffset, + ERR("Invalid evOffset: evOffset[{}]={} (last={})", i, elevs[i].irOffset, elevs[i-1].irOffset); return nullptr; } } if(irCount <= elevs.back().irOffset) { - ERR("Invalid evOffset: evOffset[%zu]=%d (irCount=%d)\n", - elevs.size()-1, elevs.back().irOffset, irCount); + ERR("Invalid evOffset: evOffset[{}]={} (irCount={})", elevs.size()-1, + elevs.back().irOffset, irCount); return nullptr; } @@ -558,16 +585,16 @@ std::unique_ptr LoadHrtf00(std::istream &data, const char *filename) elevs[i-1].azCount = static_cast(elevs[i].irOffset - elevs[i-1].irOffset); if(elevs[i-1].azCount < MinAzCount || elevs[i-1].azCount > MaxAzCount) { - ERR("Unsupported azimuth count: azCount[%zd]=%d (%d to %d)\n", - i-1, elevs[i-1].azCount, MinAzCount, MaxAzCount); + ERR("Unsupported azimuth count: azCount[{}]={} ({} to {})", i-1, elevs[i-1].azCount, + MinAzCount, MaxAzCount); return nullptr; } } elevs.back().azCount = static_cast(irCount - elevs.back().irOffset); if(elevs.back().azCount < MinAzCount || elevs.back().azCount > MaxAzCount) { - ERR("Unsupported azimuth count: azCount[%zu]=%d (%d to %d)\n", - elevs.size()-1, elevs.back().azCount, MinAzCount, MaxAzCount); + ERR("Unsupported azimuth count: azCount[{}]={} ({} to {})", elevs.size()-1, + elevs.back().azCount, MinAzCount, MaxAzCount); return nullptr; } @@ -575,54 +602,49 @@ std::unique_ptr LoadHrtf00(std::istream &data, const char *filename) auto delays = std::vector(irCount); for(auto &hrir : coeffs) { - for(auto &val : al::span{hrir.data(), irSize}) - val[0] = readle(data) / 32768.0f; + for(auto &val : al::span{hrir}.first(irSize)) + val[0] = float(readle(data)) / 32768.0f; } for(auto &val : delays) val[0] = readle(data); if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } + throw std::runtime_error{"Premature end of file"}; + for(size_t i{0};i < irCount;i++) { if(delays[i][0] > MaxHrirDelay) { - ERR("Invalid delays[%zd]: %d (%d)\n", i, delays[i][0], MaxHrirDelay); + ERR("Invalid delays[{}]: {} ({})", i, delays[i][0], MaxHrirDelay); return nullptr; } delays[i][0] <<= HrirDelayFracBits; } /* Mirror the left ear responses to the right ear. */ - MirrorLeftHrirs({elevs.data(), elevs.size()}, coeffs.data(), delays.data()); + MirrorLeftHrirs(elevs, coeffs, delays); - const HrtfStore::Field field[1]{{0.0f, evCount}}; - return CreateHrtfStore(rate, static_cast(irSize), field, {elevs.data(), elevs.size()}, - coeffs.data(), delays.data(), filename); + const std::array field{HrtfStore::Field{0.0f, evCount}}; + return CreateHrtfStore(rate, static_cast(irSize), field, elevs, coeffs.data(), + delays.data()); } -std::unique_ptr LoadHrtf01(std::istream &data, const char *filename) +std::unique_ptr LoadHrtf01(std::istream &data) { uint rate{readle(data)}; uint8_t irSize{readle(data)}; ubyte evCount{readle(data)}; if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } + throw std::runtime_error{"Premature end of file"}; if(irSize < MinIrLength || irSize > HrirLength) { - ERR("Unsupported HRIR size, irSize=%d (%d to %d)\n", irSize, MinIrLength, HrirLength); + ERR("Unsupported HRIR size, irSize={} ({} to {})", irSize, MinIrLength, HrirLength); return nullptr; } if(evCount < MinEvCount || evCount > MaxEvCount) { - ERR("Unsupported elevation count: evCount=%d (%d to %d)\n", - evCount, MinEvCount, MaxEvCount); + ERR("Unsupported elevation count: evCount={} ({} to {})", evCount, MinEvCount, + MaxEvCount); return nullptr; } @@ -630,15 +652,13 @@ std::unique_ptr LoadHrtf01(std::istream &data, const char *filename) for(auto &elev : elevs) elev.azCount = readle(data); if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } + throw std::runtime_error{"Premature end of file"}; + for(size_t i{0};i < evCount;++i) { if(elevs[i].azCount < MinAzCount || elevs[i].azCount > MaxAzCount) { - ERR("Unsupported azimuth count: azCount[%zd]=%d (%d to %d)\n", i, elevs[i].azCount, + ERR("Unsupported azimuth count: azCount[{}]={} ({} to {})", i, elevs[i].azCount, MinAzCount, MaxAzCount); return nullptr; } @@ -653,40 +673,37 @@ std::unique_ptr LoadHrtf01(std::istream &data, const char *filename) auto delays = std::vector(irCount); for(auto &hrir : coeffs) { - for(auto &val : al::span{hrir.data(), irSize}) - val[0] = readle(data) / 32768.0f; + for(auto &val : al::span{hrir}.first(irSize)) + val[0] = float(readle(data)) / 32768.0f; } for(auto &val : delays) val[0] = readle(data); if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } + throw std::runtime_error{"Premature end of file"}; + for(size_t i{0};i < irCount;i++) { if(delays[i][0] > MaxHrirDelay) { - ERR("Invalid delays[%zd]: %d (%d)\n", i, delays[i][0], MaxHrirDelay); + ERR("Invalid delays[{}]: {} ({})", i, delays[i][0], MaxHrirDelay); return nullptr; } delays[i][0] <<= HrirDelayFracBits; } /* Mirror the left ear responses to the right ear. */ - MirrorLeftHrirs({elevs.data(), elevs.size()}, coeffs.data(), delays.data()); + MirrorLeftHrirs(elevs, coeffs, delays); - const HrtfStore::Field field[1]{{0.0f, evCount}}; - return CreateHrtfStore(rate, irSize, field, {elevs.data(), elevs.size()}, coeffs.data(), - delays.data(), filename); + const std::array field{HrtfStore::Field{0.0f, evCount}}; + return CreateHrtfStore(rate, irSize, field, elevs, coeffs.data(), delays.data()); } -std::unique_ptr LoadHrtf02(std::istream &data, const char *filename) +std::unique_ptr LoadHrtf02(std::istream &data) { - constexpr ubyte SampleType_S16{0}; - constexpr ubyte SampleType_S24{1}; - constexpr ubyte ChanType_LeftOnly{0}; - constexpr ubyte ChanType_LeftRight{1}; + static constexpr ubyte SampleType_S16{0}; + static constexpr ubyte SampleType_S24{1}; + static constexpr ubyte ChanType_LeftOnly{0}; + static constexpr ubyte ChanType_LeftRight{1}; uint rate{readle(data)}; ubyte sampleType{readle(data)}; @@ -694,30 +711,27 @@ std::unique_ptr LoadHrtf02(std::istream &data, const char *filename) uint8_t irSize{readle(data)}; ubyte fdCount{readle(data)}; if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } + throw std::runtime_error{"Premature end of file"}; if(sampleType > SampleType_S24) { - ERR("Unsupported sample type: %d\n", sampleType); + ERR("Unsupported sample type: {}", sampleType); return nullptr; } if(channelType > ChanType_LeftRight) { - ERR("Unsupported channel type: %d\n", channelType); + ERR("Unsupported channel type: {}", channelType); return nullptr; } if(irSize < MinIrLength || irSize > HrirLength) { - ERR("Unsupported HRIR size, irSize=%d (%d to %d)\n", irSize, MinIrLength, HrirLength); + ERR("Unsupported HRIR size, irSize={} ({} to {})", irSize, MinIrLength, HrirLength); return nullptr; } if(fdCount < 1 || fdCount > MaxFdCount) { - ERR("Unsupported number of field-depths: fdCount=%d (%d to %d)\n", fdCount, MinFdCount, + ERR("Unsupported number of field-depths: fdCount={} ({} to {})", fdCount, MinFdCount, MaxFdCount); return nullptr; } @@ -729,48 +743,42 @@ std::unique_ptr LoadHrtf02(std::istream &data, const char *filename) const ushort distance{readle(data)}; const ubyte evCount{readle(data)}; if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } + throw std::runtime_error{"Premature end of file"}; if(distance < MinFdDistance || distance > MaxFdDistance) { - ERR("Unsupported field distance[%zu]=%d (%d to %d millimeters)\n", f, distance, + ERR("Unsupported field distance[{}]={} ({} to {} millimeters)", f, distance, MinFdDistance, MaxFdDistance); return nullptr; } if(evCount < MinEvCount || evCount > MaxEvCount) { - ERR("Unsupported elevation count: evCount[%zu]=%d (%d to %d)\n", f, evCount, + ERR("Unsupported elevation count: evCount[{}]={} ({} to {})", f, evCount, MinEvCount, MaxEvCount); return nullptr; } - fields[f].distance = distance / 1000.0f; + fields[f].distance = float(distance) / 1000.0f; fields[f].evCount = evCount; if(f > 0 && fields[f].distance <= fields[f-1].distance) { - ERR("Field distance[%zu] is not after previous (%f > %f)\n", f, fields[f].distance, + ERR("Field distance[{}] is not after previous ({:f} > {:f})", f, fields[f].distance, fields[f-1].distance); return nullptr; } const size_t ebase{elevs.size()}; elevs.resize(ebase + evCount); - for(auto &elev : al::span(elevs.data()+ebase, evCount)) + for(auto &elev : al::span{elevs}.subspan(ebase, evCount)) elev.azCount = readle(data); if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } + throw std::runtime_error{"Premature end of file"}; for(size_t e{0};e < evCount;e++) { if(elevs[ebase+e].azCount < MinAzCount || elevs[ebase+e].azCount > MaxAzCount) { - ERR("Unsupported azimuth count: azCount[%zu][%zu]=%d (%d to %d)\n", f, e, + ERR("Unsupported azimuth count: azCount[{}][{}]={} ({} to {})", f, e, elevs[ebase+e].azCount, MinAzCount, MaxAzCount); return nullptr; } @@ -795,37 +803,35 @@ std::unique_ptr LoadHrtf02(std::istream &data, const char *filename) { for(auto &hrir : coeffs) { - for(auto &val : al::span{hrir.data(), irSize}) - val[0] = readle(data) / 32768.0f; + for(auto &val : al::span{hrir}.first(irSize)) + val[0] = float(readle(data)) / 32768.0f; } } else if(sampleType == SampleType_S24) { for(auto &hrir : coeffs) { - for(auto &val : al::span{hrir.data(), irSize}) + for(auto &val : al::span{hrir}.first(irSize)) val[0] = static_cast(readle(data)) / 8388608.0f; } } for(auto &val : delays) val[0] = readle(data); if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } + throw std::runtime_error{"Premature end of file"}; + for(size_t i{0};i < irTotal;++i) { if(delays[i][0] > MaxHrirDelay) { - ERR("Invalid delays[%zu][0]: %d (%d)\n", i, delays[i][0], MaxHrirDelay); + ERR("Invalid delays[{}][0]: {} ({})", i, delays[i][0], MaxHrirDelay); return nullptr; } delays[i][0] <<= HrirDelayFracBits; } /* Mirror the left ear responses to the right ear. */ - MirrorLeftHrirs({elevs.data(), elevs.size()}, coeffs.data(), delays.data()); + MirrorLeftHrirs(elevs, coeffs, delays); } else if(channelType == ChanType_LeftRight) { @@ -833,10 +839,10 @@ std::unique_ptr LoadHrtf02(std::istream &data, const char *filename) { for(auto &hrir : coeffs) { - for(auto &val : al::span{hrir.data(), irSize}) + for(auto &val : al::span{hrir}.first(irSize)) { - val[0] = readle(data) / 32768.0f; - val[1] = readle(data) / 32768.0f; + val[0] = float(readle(data)) / 32768.0f; + val[1] = float(readle(data)) / 32768.0f; } } } @@ -844,7 +850,7 @@ std::unique_ptr LoadHrtf02(std::istream &data, const char *filename) { for(auto &hrir : coeffs) { - for(auto &val : al::span{hrir.data(), irSize}) + for(auto &val : al::span{hrir}.first(irSize)) { val[0] = static_cast(readle(data)) / 8388608.0f; val[1] = static_cast(readle(data)) / 8388608.0f; @@ -857,21 +863,18 @@ std::unique_ptr LoadHrtf02(std::istream &data, const char *filename) val[1] = readle(data); } if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } + throw std::runtime_error{"Premature end of file"}; for(size_t i{0};i < irTotal;++i) { if(delays[i][0] > MaxHrirDelay) { - ERR("Invalid delays[%zu][0]: %d (%d)\n", i, delays[i][0], MaxHrirDelay); + ERR("Invalid delays[{}][0]: {} ({})", i, delays[i][0], MaxHrirDelay); return nullptr; } if(delays[i][1] > MaxHrirDelay) { - ERR("Invalid delays[%zu][1]: %d (%d)\n", i, delays[i][1], MaxHrirDelay); + ERR("Invalid delays[{}][1]: {} ({})", i, delays[i][1], MaxHrirDelay); return nullptr; } delays[i][0] <<= HrirDelayFracBits; @@ -893,16 +896,16 @@ std::unique_ptr LoadHrtf02(std::istream &data, const char *filename) * count. Reverse the order of the groups, keeping the relative order * of per-group azimuth counts. */ - auto elevs__end = elevs_.end(); - auto copy_azs = [&elevs,&elevs__end](const ptrdiff_t ebase, const HrtfStore::Field &field) + auto elevs_end = elevs_.end(); + auto copy_azs = [&elevs,&elevs_end](const ptrdiff_t ebase, const HrtfStore::Field &field) -> ptrdiff_t { auto elevs_src = elevs.begin()+ebase; - elevs__end = std::copy_backward(elevs_src, elevs_src+field.evCount, elevs__end); + elevs_end = std::copy_backward(elevs_src, elevs_src+field.evCount, elevs_end); return ebase + field.evCount; }; - (void)std::accumulate(fields.cbegin(), fields.cend(), ptrdiff_t{0}, copy_azs); - assert(elevs_.begin() == elevs__end); + std::ignore = std::accumulate(fields.cbegin(), fields.cend(), ptrdiff_t{0}, copy_azs); + assert(elevs_.begin() == elevs_end); /* Reestablish the IR offset for each elevation index, given the new * ordering of elevations. @@ -922,12 +925,13 @@ std::unique_ptr LoadHrtf02(std::istream &data, const char *filename) auto copy_irs = [&elevs,&coeffs,&delays,&coeffs_end,&delays_end]( const ptrdiff_t ebase, const HrtfStore::Field &field) -> ptrdiff_t { - auto accum_az = [](int count, const HrtfStore::Elevation &elev) noexcept -> int + auto accum_az = [](const ptrdiff_t count, const HrtfStore::Elevation &elev) noexcept + -> ptrdiff_t { return count + elev.azCount; }; - const auto elevs_mid = elevs.cbegin() + ebase; - const auto elevs_end = elevs_mid + field.evCount; - const int abase{std::accumulate(elevs.cbegin(), elevs_mid, 0, accum_az)}; - const int num_azs{std::accumulate(elevs_mid, elevs_end, 0, accum_az)}; + const auto elev_mid = elevs.cbegin() + ebase; + const auto abase = std::accumulate(elevs.cbegin(), elev_mid, ptrdiff_t{0}, accum_az); + const auto num_azs = std::accumulate(elev_mid, elev_mid + field.evCount, ptrdiff_t{0}, + accum_az); coeffs_end = std::copy_backward(coeffs.cbegin() + abase, coeffs.cbegin() + (abase+num_azs), coeffs_end); @@ -936,7 +940,7 @@ std::unique_ptr LoadHrtf02(std::istream &data, const char *filename) return ebase + field.evCount; }; - (void)std::accumulate(fields.cbegin(), fields.cend(), ptrdiff_t{0}, copy_irs); + std::ignore = std::accumulate(fields.cbegin(), fields.cend(), ptrdiff_t{0}, copy_irs); assert(coeffs_.begin() == coeffs_end); assert(delays_.begin() == delays_end); @@ -946,39 +950,35 @@ std::unique_ptr LoadHrtf02(std::istream &data, const char *filename) delays = std::move(delays_); } - return CreateHrtfStore(rate, irSize, {fields.data(), fields.size()}, - {elevs.data(), elevs.size()}, coeffs.data(), delays.data(), filename); + return CreateHrtfStore(rate, irSize, fields, elevs, coeffs.data(), delays.data()); } -std::unique_ptr LoadHrtf03(std::istream &data, const char *filename) +std::unique_ptr LoadHrtf03(std::istream &data) { - constexpr ubyte ChanType_LeftOnly{0}; - constexpr ubyte ChanType_LeftRight{1}; + static constexpr ubyte ChanType_LeftOnly{0}; + static constexpr ubyte ChanType_LeftRight{1}; uint rate{readle(data)}; ubyte channelType{readle(data)}; uint8_t irSize{readle(data)}; ubyte fdCount{readle(data)}; if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } + throw std::runtime_error{"Premature end of file"}; if(channelType > ChanType_LeftRight) { - ERR("Unsupported channel type: %d\n", channelType); + ERR("Unsupported channel type: {}", channelType); return nullptr; } if(irSize < MinIrLength || irSize > HrirLength) { - ERR("Unsupported HRIR size, irSize=%d (%d to %d)\n", irSize, MinIrLength, HrirLength); + ERR("Unsupported HRIR size, irSize={} ({} to {})", irSize, MinIrLength, HrirLength); return nullptr; } if(fdCount < 1 || fdCount > MaxFdCount) { - ERR("Unsupported number of field-depths: fdCount=%d (%d to %d)\n", fdCount, MinFdCount, + ERR("Unsupported number of field-depths: fdCount={} ({} to {})", fdCount, MinFdCount, MaxFdCount); return nullptr; } @@ -990,48 +990,42 @@ std::unique_ptr LoadHrtf03(std::istream &data, const char *filename) const ushort distance{readle(data)}; const ubyte evCount{readle(data)}; if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } + throw std::runtime_error{"Premature end of file"}; if(distance < MinFdDistance || distance > MaxFdDistance) { - ERR("Unsupported field distance[%zu]=%d (%d to %d millimeters)\n", f, distance, + ERR("Unsupported field distance[{}]={} ({} to {} millimeters)", f, distance, MinFdDistance, MaxFdDistance); return nullptr; } if(evCount < MinEvCount || evCount > MaxEvCount) { - ERR("Unsupported elevation count: evCount[%zu]=%d (%d to %d)\n", f, evCount, + ERR("Unsupported elevation count: evCount[{}]={} ({} to {})", f, evCount, MinEvCount, MaxEvCount); return nullptr; } - fields[f].distance = distance / 1000.0f; + fields[f].distance = float(distance) / 1000.0f; fields[f].evCount = evCount; if(f > 0 && fields[f].distance > fields[f-1].distance) { - ERR("Field distance[%zu] is not before previous (%f <= %f)\n", f, fields[f].distance, - fields[f-1].distance); + ERR("Field distance[{}] is not before previous ({:f} <= {:f})", f, + fields[f].distance, fields[f-1].distance); return nullptr; } const size_t ebase{elevs.size()}; elevs.resize(ebase + evCount); - for(auto &elev : al::span(elevs.data()+ebase, evCount)) + for(auto &elev : al::span{elevs}.subspan(ebase, evCount)) elev.azCount = readle(data); if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } + throw std::runtime_error{"Premature end of file"}; for(size_t e{0};e < evCount;e++) { if(elevs[ebase+e].azCount < MinAzCount || elevs[ebase+e].azCount > MaxAzCount) { - ERR("Unsupported azimuth count: azCount[%zu][%zu]=%d (%d to %d)\n", f, e, + ERR("Unsupported azimuth count: azCount[{}][{}]={} ({} to {})", f, e, elevs[ebase+e].azCount, MinAzCount, MaxAzCount); return nullptr; } @@ -1054,34 +1048,32 @@ std::unique_ptr LoadHrtf03(std::istream &data, const char *filename) { for(auto &hrir : coeffs) { - for(auto &val : al::span{hrir.data(), irSize}) + for(auto &val : al::span{hrir}.first(irSize)) val[0] = static_cast(readle(data)) / 8388608.0f; } for(auto &val : delays) val[0] = readle(data); if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } + throw std::runtime_error{"Premature end of file"}; + for(size_t i{0};i < irTotal;++i) { if(delays[i][0] > MaxHrirDelay<{hrir.data(), irSize}) + for(auto &val : al::span{hrir}.first(irSize)) { val[0] = static_cast(readle(data)) / 8388608.0f; val[1] = static_cast(readle(data)) / 8388608.0f; @@ -1093,107 +1085,90 @@ std::unique_ptr LoadHrtf03(std::istream &data, const char *filename) val[1] = readle(data); } if(!data || data.eof()) - { - ERR("Failed reading %s\n", filename); - return nullptr; - } + throw std::runtime_error{"Premature end of file"}; for(size_t i{0};i < irTotal;++i) { if(delays[i][0] > MaxHrirDelay< MaxHrirDelay< bool { return name == entry.mDispName; }; + auto match_name = [name](const HrtfEntry &entry) -> bool { return name == entry.mDispName; }; auto &enum_names = EnumeratedHrtfs; return std::find_if(enum_names.cbegin(), enum_names.cend(), match_name) != enum_names.cend(); } -void AddFileEntry(const std::string &filename) +void AddFileEntry(const std::string_view filename) { /* Check if this file has already been enumerated. */ auto enum_iter = std::find_if(EnumeratedHrtfs.cbegin(), EnumeratedHrtfs.cend(), - [&filename](const HrtfEntry &entry) -> bool - { return entry.mFilename == filename; }); + [filename](const HrtfEntry &entry) -> bool { return entry.mFilename == filename; }); if(enum_iter != EnumeratedHrtfs.cend()) { - TRACE("Skipping duplicate file entry %s\n", filename.c_str()); + TRACE("Skipping duplicate file entry {}", filename); return; } /* TODO: Get a human-readable name from the HRTF data (possibly coming in a - * format update). */ - size_t namepos{filename.find_last_of('/')+1}; - if(!namepos) namepos = filename.find_last_of('\\')+1; + * format update). + */ + const auto namepos = std::max(filename.rfind('/')+1, filename.rfind('\\')+1); + const auto extpos = filename.substr(namepos).rfind('.'); - size_t extpos{filename.find_last_of('.')}; - if(extpos <= namepos) extpos = std::string::npos; + const auto basename = (extpos == std::string::npos) ? + filename.substr(namepos) : filename.substr(namepos, extpos); - const std::string basename{(extpos == std::string::npos) ? - filename.substr(namepos) : filename.substr(namepos, extpos-namepos)}; - std::string newname{basename}; - int count{1}; + auto count = 1; + auto newname = std::string{basename}; while(checkName(newname)) - { - newname = basename; - newname += " #"; - newname += std::to_string(++count); - } - EnumeratedHrtfs.emplace_back(HrtfEntry{newname, filename}); - const HrtfEntry &entry = EnumeratedHrtfs.back(); + newname = fmt::format("{} #{}", basename, ++count); - TRACE("Adding file entry \"%s\"\n", entry.mFilename.c_str()); + const auto &entry = EnumeratedHrtfs.emplace_back(newname, filename); + TRACE("Adding file entry \"{}\"", entry.mFilename); } /* Unfortunate that we have to duplicate AddFileEntry to take a memory buffer * for input instead of opening the given filename. */ -void AddBuiltInEntry(const std::string &dispname, uint residx) +void AddBuiltInEntry(const std::string_view dispname, uint residx) { - const std::string filename{'!'+std::to_string(residx)+'_'+dispname}; + auto filename = fmt::format("!{}_{}", residx, dispname); auto enum_iter = std::find_if(EnumeratedHrtfs.cbegin(), EnumeratedHrtfs.cend(), - [&filename](const HrtfEntry &entry) -> bool - { return entry.mFilename == filename; }); + [&filename](const HrtfEntry &entry) -> bool { return entry.mFilename == filename; }); if(enum_iter != EnumeratedHrtfs.cend()) { - TRACE("Skipping duplicate file entry %s\n", filename.c_str()); + TRACE("Skipping duplicate file entry {}", filename); return; } /* TODO: Get a human-readable name from the HRTF data (possibly coming in a * format update). */ - std::string newname{dispname}; - int count{1}; + auto count = 1; + auto newname = std::string{dispname}; while(checkName(newname)) - { - newname = dispname; - newname += " #"; - newname += std::to_string(++count); - } - EnumeratedHrtfs.emplace_back(HrtfEntry{newname, filename}); - const HrtfEntry &entry = EnumeratedHrtfs.back(); + newname = fmt::format("{} #{}", dispname, ++count); - TRACE("Adding built-in entry \"%s\"\n", entry.mFilename.c_str()); + const auto &entry = EnumeratedHrtfs.emplace_back(std::move(newname), std::move(filename)); + TRACE("Adding built-in entry \"{}\"", entry.mFilename); } @@ -1206,6 +1181,7 @@ al::span GetResource(int /*name*/) #else +/* NOLINTNEXTLINE(*-avoid-c-arrays) */ constexpr unsigned char hrtf_default[]{ #include "default_hrtf.txt" }; @@ -1223,47 +1199,46 @@ al::span GetResource(int name) std::vector EnumerateHrtf(std::optional pathopt) { - std::lock_guard _{EnumeratedHrtfLock}; + std::lock_guard enumlock{EnumeratedHrtfLock}; EnumeratedHrtfs.clear(); + for(const auto &fname : SearchDataFiles(".mhr"sv)) + AddFileEntry(fname); + bool usedefaults{true}; if(pathopt) { - const char *pathlist{pathopt->c_str()}; - while(pathlist && *pathlist) + std::string_view pathlist{*pathopt}; + while(!pathlist.empty()) { - const char *next, *end; - - while(isspace(*pathlist) || *pathlist == ',') - pathlist++; - if(*pathlist == '\0') - continue; + while(!pathlist.empty() && (std::isspace(pathlist.front()) || pathlist.front() == ',')) + pathlist.remove_prefix(1); + if(pathlist.empty()) + break; - next = strchr(pathlist, ','); - if(next) - end = next++; + auto endpos = std::min(pathlist.find(','), pathlist.size()); + auto entry = pathlist.substr(0, endpos); + if(endpos < pathlist.size()) + pathlist.remove_prefix(++endpos); else { - end = pathlist + strlen(pathlist); + pathlist.remove_prefix(endpos); usedefaults = false; } - while(end != pathlist && isspace(*(end-1))) - --end; - if(end != pathlist) + while(!entry.empty() && std::isspace(entry.back())) + entry.remove_suffix(1); + if(!entry.empty()) { - const std::string pname{pathlist, end}; - for(const auto &fname : SearchDataFiles(".mhr", pname.c_str())) + for(const auto &fname : SearchDataFiles(".mhr"sv, entry)) AddFileEntry(fname); } - - pathlist = next; } } if(usedefaults) { - for(const auto &fname : SearchDataFiles(".mhr", "openal/hrtf")) + for(const auto &fname : SearchDataFiles(".mhr"sv, "openal/hrtf"sv)) AddFileEntry(fname); if(!GetResource(IDR_DEFAULT_HRTF_MHR).empty()) @@ -1278,94 +1253,101 @@ std::vector EnumerateHrtf(std::optional pathopt) return list; } -HrtfStorePtr GetLoadedHrtf(const std::string &name, const uint devrate) -{ - std::lock_guard _{EnumeratedHrtfLock}; +HrtfStorePtr GetLoadedHrtf(const std::string_view name, const uint devrate) +try { + if(devrate > MaxSampleRate) + { + WARN("Device sample rate too large for HRTF ({}hz > {}hz)", devrate, MaxSampleRate); + return nullptr; + } + std::lock_guard enumlock{EnumeratedHrtfLock}; auto entry_iter = std::find_if(EnumeratedHrtfs.cbegin(), EnumeratedHrtfs.cend(), - [&name](const HrtfEntry &entry) -> bool { return entry.mDispName == name; }); + [name](const HrtfEntry &entry) -> bool { return entry.mDispName == name; }); if(entry_iter == EnumeratedHrtfs.cend()) return nullptr; const std::string &fname = entry_iter->mFilename; - std::lock_guard __{LoadedHrtfLock}; - auto hrtf_lt_fname = [](LoadedHrtf &hrtf, const std::string &filename) -> bool - { return hrtf.mFilename < filename; }; + std::lock_guard loadlock{LoadedHrtfLock}; + auto hrtf_lt_fname = [devrate](LoadedHrtf &hrtf, const std::string_view filename) -> bool + { + return hrtf.mSampleRate < devrate + || (hrtf.mSampleRate == devrate && hrtf.mFilename < filename); + }; auto handle = std::lower_bound(LoadedHrtfs.begin(), LoadedHrtfs.end(), fname, hrtf_lt_fname); - while(handle != LoadedHrtfs.end() && handle->mFilename == fname) + if(handle != LoadedHrtfs.end() && handle->mSampleRate == devrate && handle->mFilename == fname) { - HrtfStore *hrtf{handle->mEntry.get()}; - if(hrtf && hrtf->mSampleRate == devrate) + if(HrtfStore *hrtf{handle->mEntry.get()}) { + assert(hrtf->mSampleRate == devrate); hrtf->add_ref(); return HrtfStorePtr{hrtf}; } - ++handle; } std::unique_ptr stream; int residx{}; char ch{}; + /* NOLINTNEXTLINE(cert-err34-c,cppcoreguidelines-pro-type-vararg) */ if(sscanf(fname.c_str(), "!%d%c", &residx, &ch) == 2 && ch == '_') { - TRACE("Loading %s...\n", fname.c_str()); + TRACE("Loading {}...", fname); al::span res{GetResource(residx)}; if(res.empty()) { - ERR("Could not get resource %u, %s\n", residx, name.c_str()); + ERR("Could not get resource {}, {}", residx, name); return nullptr; } - stream = std::make_unique(res.begin(), res.end()); + /* NOLINTNEXTLINE(*-const-cast) */ + stream = std::make_unique(al::span{const_cast(res.data()), res.size()}); } else { - TRACE("Loading %s...\n", fname.c_str()); - auto fstr = std::make_unique(fname.c_str(), std::ios::binary); + TRACE("Loading {}...", fname); + auto fstr = std::make_unique(fs::u8path(fname), + std::ios::binary); if(!fstr->is_open()) { - ERR("Could not open %s\n", fname.c_str()); + ERR("Could not open {}", fname); return nullptr; } stream = std::move(fstr); } - std::unique_ptr hrtf; - char magic[sizeof(magicMarker03)]; - stream->read(magic, sizeof(magic)); - if(stream->gcount() < static_cast(sizeof(magicMarker03))) - ERR("%s data is too short (%zu bytes)\n", name.c_str(), stream->gcount()); - else if(memcmp(magic, magicMarker03, sizeof(magicMarker03)) == 0) + auto hrtf = std::unique_ptr{}; + auto magic = std::array{}; + stream->read(magic.data(), magic.size()); + if(stream->gcount() < std::streamsize{magic.size()}) + ERR("{} data is too short ({} bytes)", name, stream->gcount()); + else if(GetMarker03Name() == std::string_view{magic.data(), magic.size()}) { - TRACE("Detected data set format v3\n"); - hrtf = LoadHrtf03(*stream, name.c_str()); + TRACE("Detected data set format v3"); + hrtf = LoadHrtf03(*stream); } - else if(memcmp(magic, magicMarker02, sizeof(magicMarker02)) == 0) + else if(GetMarker02Name() == std::string_view{magic.data(), magic.size()}) { - TRACE("Detected data set format v2\n"); - hrtf = LoadHrtf02(*stream, name.c_str()); + TRACE("Detected data set format v2"); + hrtf = LoadHrtf02(*stream); } - else if(memcmp(magic, magicMarker01, sizeof(magicMarker01)) == 0) + else if(GetMarker01Name() == std::string_view{magic.data(), magic.size()}) { - TRACE("Detected data set format v1\n"); - hrtf = LoadHrtf01(*stream, name.c_str()); + TRACE("Detected data set format v1"); + hrtf = LoadHrtf01(*stream); } - else if(memcmp(magic, magicMarker00, sizeof(magicMarker00)) == 0) + else if(GetMarker00Name() == std::string_view{magic.data(), magic.size()}) { - TRACE("Detected data set format v0\n"); - hrtf = LoadHrtf00(*stream, name.c_str()); + TRACE("Detected data set format v0"); + hrtf = LoadHrtf00(*stream); } else - ERR("Invalid header in %s: \"%.8s\"\n", name.c_str(), magic); + ERR("Invalid header in {}: \"{}\"", name, std::string_view{magic.data(), magic.size()}); stream.reset(); if(!hrtf) - { - ERR("Failed to load %s\n", name.c_str()); return nullptr; - } if(hrtf->mSampleRate != devrate) { - TRACE("Resampling HRTF %s (%uhz -> %uhz)\n", name.c_str(), hrtf->mSampleRate, devrate); + TRACE("Resampling HRTF {} ({}hz -> {}hz)", name, uint{hrtf->mSampleRate}, devrate); /* Calculate the last elevation's index and get the total IR count. */ const size_t lastEv{std::accumulate(hrtf->mFields.begin(), hrtf->mFields.end(), 0_uz, @@ -1375,17 +1357,18 @@ HrtfStorePtr GetLoadedHrtf(const std::string &name, const uint devrate) const size_t irCount{size_t{hrtf->mElev[lastEv].irOffset} + hrtf->mElev[lastEv].azCount}; /* Resample all the IRs. */ - std::array,2> inout; + std::array,2> inout{}; PPhaseResampler rs; rs.init(hrtf->mSampleRate, devrate); for(size_t i{0};i < irCount;++i) { - HrirArray &coeffs = const_cast(hrtf->mCoeffs[i]); + /* NOLINTNEXTLINE(*-const-cast) */ + auto coeffs = al::span{const_cast(hrtf->mCoeffs[i])}; for(size_t j{0};j < 2;++j) { std::transform(coeffs.cbegin(), coeffs.cend(), inout[0].begin(), [j](const float2 &in) noexcept -> double { return in[j]; }); - rs.process(HrirLength, inout[0].data(), HrirLength, inout[1].data()); + rs.process(inout[0], inout[1]); for(size_t k{0};k < HrirLength;++k) coeffs[k][j] = static_cast(inout[1][k]); } @@ -1400,9 +1383,9 @@ HrtfStorePtr GetLoadedHrtf(const std::string &name, const uint devrate) { for(size_t j{0};j < 2;++j) { - const float new_delay{std::round(hrtf->mDelays[i][j] * rate_scale) / + const float new_delay{std::round(float(hrtf->mDelays[i][j]) * rate_scale) / float{HrirDelayFracOne}}; - max_delay = maxf(max_delay, new_delay); + max_delay = std::max(max_delay, new_delay); new_delays[i][j] = new_delay; } } @@ -1414,54 +1397,60 @@ HrtfStorePtr GetLoadedHrtf(const std::string &name, const uint devrate) float delay_scale{HrirDelayFracOne}; if(max_delay > MaxHrirDelay) { - WARN("Resampled delay exceeds max (%.2f > %d)\n", max_delay, MaxHrirDelay); + WARN("Resampled delay exceeds max ({:.2f} > {})", max_delay, MaxHrirDelay); delay_scale *= float{MaxHrirDelay} / max_delay; } for(size_t i{0};i < irCount;++i) { - ubyte2 &delays = const_cast(hrtf->mDelays[i]); - for(size_t j{0};j < 2;++j) - delays[j] = static_cast(float2int(new_delays[i][j]*delay_scale + 0.5f)); + /* NOLINTNEXTLINE(*-const-cast) */ + auto delays = al::span{const_cast(hrtf->mDelays[i])}; + std::transform(new_delays[i].cbegin(), new_delays[i].cend(), delays.begin(), + [delay_scale](const float delay) + { return static_cast(float2int(delay*delay_scale + 0.5f)); }); } /* Scale the IR size for the new sample rate and update the stored * sample rate. */ const float newIrSize{std::round(static_cast(hrtf->mIrSize) * rate_scale)}; - hrtf->mIrSize = static_cast(minf(HrirLength, newIrSize)); + hrtf->mIrSize = static_cast(std::min(float{HrirLength}, newIrSize)); hrtf->mSampleRate = devrate & 0xff'ff'ff; } - TRACE("Loaded HRTF %s for sample rate %uhz, %u-sample filter\n", name.c_str(), - hrtf->mSampleRate, hrtf->mIrSize); - handle = LoadedHrtfs.emplace(handle, fname, std::move(hrtf)); + handle = LoadedHrtfs.emplace(handle, fname, devrate, std::move(hrtf)); + TRACE("Loaded HRTF {} for sample rate {}hz, {}-sample filter", name, + uint{handle->mEntry->mSampleRate}, uint{handle->mEntry->mIrSize}); return HrtfStorePtr{handle->mEntry.get()}; } +catch(std::exception& e) { + ERR("Failed to load {}: {}", name, e.what()); + return nullptr; +} void HrtfStore::add_ref() { auto ref = IncrementRef(mRef); - TRACE("HrtfStore %p increasing refcount to %u\n", decltype(std::declval()){this}, ref); + TRACE("HrtfStore {} increasing refcount to {}", decltype(std::declval()){this}, ref); } void HrtfStore::dec_ref() { auto ref = DecrementRef(mRef); - TRACE("HrtfStore %p decreasing refcount to %u\n", decltype(std::declval()){this}, ref); + TRACE("HrtfStore {} decreasing refcount to {}", decltype(std::declval()){this}, ref); if(ref == 0) { - std::lock_guard _{LoadedHrtfLock}; + std::lock_guard loadlock{LoadedHrtfLock}; /* Go through and remove all unused HRTFs. */ auto remove_unused = [](LoadedHrtf &hrtf) -> bool { HrtfStore *entry{hrtf.mEntry.get()}; - if(entry && ReadRef(entry->mRef) == 0) + if(entry && entry->mRef.load() == 0) { - TRACE("Unloading unused HRTF %s\n", hrtf.mFilename.data()); + TRACE("Unloading unused HRTF {}", hrtf.mFilename); hrtf.mEntry = nullptr; return true; } diff --git a/3rdparty/openal/core/hrtf.h b/3rdparty/openal/core/hrtf.h index 5e6e09a8c3bd..020e3077a11a 100644 --- a/3rdparty/openal/core/hrtf.h +++ b/3rdparty/openal/core/hrtf.h @@ -6,19 +6,20 @@ #include #include #include +#include #include #include "almalloc.h" #include "alspan.h" -#include "atomic.h" #include "ambidefs.h" #include "bufferline.h" -#include "mixer/hrtfdefs.h" +#include "flexarray.h" #include "intrusive_ptr.h" +#include "mixer/hrtfdefs.h" -struct HrtfStore { - RefCount mRef; +struct alignas(16) HrtfStore { + std::atomic mRef{}; uint mSampleRate : 24; uint mIrSize : 8; @@ -36,17 +37,24 @@ struct HrtfStore { ushort azCount; ushort irOffset; }; - Elevation *mElev; - const HrirArray *mCoeffs; - const ubyte2 *mDelays; + al::span mElev; + al::span mCoeffs; + al::span mDelays; - void getCoeffs(float elevation, float azimuth, float distance, float spread, HrirArray &coeffs, - const al::span delays); + void getCoeffs(float elevation, float azimuth, float distance, float spread, + const HrirSpan coeffs, const al::span delays) const; void add_ref(); void dec_ref(); - DEF_PLACE_NEWDEL() + void *operator new(size_t) = delete; + void *operator new[](size_t) = delete; + void operator delete[](void*) noexcept = delete; + + void operator delete(gsl::owner block, void*) noexcept + { ::operator delete[](block, std::align_val_t{alignof(HrtfStore)}); } + void operator delete(gsl::owner block) noexcept + { ::operator delete[](block, std::align_val_t{alignof(HrtfStore)}); } }; using HrtfStorePtr = al::intrusive_ptr; @@ -60,13 +68,13 @@ struct AngularPoint { struct DirectHrtfState { - std::array mTemp; + std::array mTemp{}; /* HRTF filter state for dry buffer content */ uint mIrSize{0}; al::FlexArray mChannels; - DirectHrtfState(size_t numchans) : mChannels{numchans} { } + explicit DirectHrtfState(size_t numchans) : mChannels{numchans} { } /** * Produces HRTF filter coefficients for decoding B-Format, given a set of * virtual speaker positions, a matching decoding matrix, and per-order @@ -74,7 +82,8 @@ struct DirectHrtfState { * are ordered and scaled according to the matrix input. */ void build(const HrtfStore *Hrtf, const uint irSize, const bool perHrirMin, - const al::span AmbiPoints, const float (*AmbiMatrix)[MaxAmbiChannels], + const al::span AmbiPoints, + const al::span> AmbiMatrix, const float XOverFreq, const al::span AmbiOrderHFGain); static std::unique_ptr Create(size_t num_chans); @@ -84,6 +93,6 @@ struct DirectHrtfState { std::vector EnumerateHrtf(std::optional pathopt); -HrtfStorePtr GetLoadedHrtf(const std::string &name, const uint devrate); +HrtfStorePtr GetLoadedHrtf(const std::string_view name, const uint devrate); #endif /* CORE_HRTF_H */ diff --git a/3rdparty/openal/core/logging.cpp b/3rdparty/openal/core/logging.cpp index 56ad0a0dba45..ba40b36865d0 100644 --- a/3rdparty/openal/core/logging.cpp +++ b/3rdparty/openal/core/logging.cpp @@ -10,9 +10,10 @@ #include #include #include -#include +#include +#include -#include "alspan.h" +#include "alstring.h" #include "strutils.h" @@ -34,6 +35,8 @@ LogLevel gLogLevel{LogLevel::Error}; namespace { +using namespace std::string_view_literals; + enum class LogState : uint8_t { FirstRun, Ready, @@ -46,7 +49,7 @@ LogState gLogState{LogState::FirstRun}; LogCallbackFunc gLogCallback{}; void *gLogCallbackPtr{}; -constexpr std::optional GetLevelCode(LogLevel level) +constexpr auto GetLevelCode(LogLevel level) noexcept -> std::optional { switch(level) { @@ -75,55 +78,23 @@ void al_set_log_callback(LogCallbackFunc callback, void *userptr) } } -void al_print(LogLevel level, const char *fmt, ...) +void al_print_impl(LogLevel level, const fmt::string_view fmt, fmt::format_args args) { - /* Kind of ugly since string literals are const char arrays with a size - * that includes the null terminator, which we want to exclude from the - * span. - */ - auto prefix = al::span{"[ALSOFT] (--) "}.first<14>(); + const auto msg = fmt::vformat(fmt, std::move(args)); + + auto prefix = "[ALSOFT] (--) "sv; switch(level) { case LogLevel::Disable: break; - case LogLevel::Error: prefix = al::span{"[ALSOFT] (EE) "}.first<14>(); break; - case LogLevel::Warning: prefix = al::span{"[ALSOFT] (WW) "}.first<14>(); break; - case LogLevel::Trace: prefix = al::span{"[ALSOFT] (II) "}.first<14>(); break; - } - - std::vector dynmsg; - std::array stcmsg{}; - - char *str{stcmsg.data()}; - auto prefend1 = std::copy_n(prefix.begin(), prefix.size(), stcmsg.begin()); - al::span msg{prefend1, stcmsg.end()}; - - std::va_list args, args2; - va_start(args, fmt); - va_copy(args2, args); - const int msglen{std::vsnprintf(msg.data(), msg.size(), fmt, args)}; - if(msglen >= 0) - { - if(static_cast(msglen) >= msg.size()) UNLIKELY - { - dynmsg.resize(static_cast(msglen)+prefix.size() + 1u); - - str = dynmsg.data(); - auto prefend2 = std::copy_n(prefix.begin(), prefix.size(), dynmsg.begin()); - msg = {prefend2, dynmsg.end()}; - - std::vsnprintf(msg.data(), msg.size(), fmt, args2); - } - msg = msg.first(static_cast(msglen)); + case LogLevel::Error: prefix = "[ALSOFT] (EE) "sv; break; + case LogLevel::Warning: prefix = "[ALSOFT] (WW) "sv; break; + case LogLevel::Trace: prefix = "[ALSOFT] (II) "sv; break; } - else - msg = {msg.data(), std::strlen(msg.data())}; - va_end(args2); - va_end(args); if(gLogLevel >= level) { auto logfile = gLogFile; - fputs(str, logfile); + fmt::println(logfile, "{}{}", prefix, msg); fflush(logfile); } #if defined(_WIN32) && !defined(NDEBUG) @@ -131,8 +102,7 @@ void al_print(LogLevel level, const char *fmt, ...) * informational, warning, or error debug messages. So only print them for * non-Release builds. */ - std::wstring wstr{utf8_to_wstr(str)}; - OutputDebugStringW(wstr.c_str()); + OutputDebugStringW(utf8_to_wstr(fmt::format("{}{}\n", prefix, msg)).c_str()); #elif defined(__ANDROID__) auto android_severity = [](LogLevel l) noexcept { @@ -147,21 +117,18 @@ void al_print(LogLevel level, const char *fmt, ...) } return ANDROID_LOG_ERROR; }; - __android_log_print(android_severity(level), "openal", "%s", str); + /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) */ + __android_log_print(android_severity(level), "openal", "%.*s%s", al::sizei(prefix), + prefix.data(), msg.c_str()); #endif auto cblock = std::lock_guard{LogCallbackMutex}; if(gLogState != LogState::Disable) { - while(!msg.empty() && std::isspace(msg.back())) - { - msg.back() = '\0'; - msg = msg.first(msg.size()-1); - } - if(auto logcode = GetLevelCode(level); logcode && !msg.empty()) + if(auto logcode = GetLevelCode(level)) { if(gLogCallback) - gLogCallback(gLogCallbackPtr, *logcode, msg.data(), static_cast(msg.size())); + gLogCallback(gLogCallbackPtr, *logcode, msg.data(), al::sizei(msg)); else if(gLogState == LogState::FirstRun) gLogState = LogState::Disable; } diff --git a/3rdparty/openal/core/logging.h b/3rdparty/openal/core/logging.h index 06b7cddea316..f32fd43e8aac 100644 --- a/3rdparty/openal/core/logging.h +++ b/3rdparty/openal/core/logging.h @@ -1,8 +1,9 @@ #ifndef CORE_LOGGING_H #define CORE_LOGGING_H -#include +#include +#include "fmt/core.h" #include "opthelpers.h" @@ -12,9 +13,9 @@ enum class LogLevel { Warning, Trace }; -extern LogLevel gLogLevel; +DECL_HIDDEN extern LogLevel gLogLevel; -extern FILE *gLogFile; +DECL_HIDDEN extern FILE *gLogFile; using LogCallbackFunc = void(*)(void *userptr, char level, const char *message, int length) noexcept; @@ -22,12 +23,13 @@ using LogCallbackFunc = void(*)(void *userptr, char level, const char *message, void al_set_log_callback(LogCallbackFunc callback, void *userptr); -#ifdef __USE_MINGW_ANSI_STDIO -[[gnu::format(gnu_printf,2,3)]] -#else -[[gnu::format(printf,2,3)]] -#endif -void al_print(LogLevel level, const char *fmt, ...); +void al_print_impl(LogLevel level, const fmt::string_view fmt, fmt::format_args args); + +template +void al_print(LogLevel level, fmt::format_string fmt, Args&& ...args) noexcept +try { + al_print_impl(level, fmt, fmt::make_format_args(args...)); +} catch(...) { } #define TRACE(...) al_print(LogLevel::Trace, __VA_ARGS__) diff --git a/3rdparty/openal/core/mastering.cpp b/3rdparty/openal/core/mastering.cpp index 4445719b4216..4da708ab86cd 100644 --- a/3rdparty/openal/core/mastering.cpp +++ b/3rdparty/openal/core/mastering.cpp @@ -11,7 +11,6 @@ #include #include -#include "almalloc.h" #include "alnumeric.h" #include "alspan.h" #include "opthelpers.h" @@ -20,9 +19,9 @@ /* These structures assume BufferLineSize is a power of 2. */ static_assert((BufferLineSize & (BufferLineSize-1)) == 0, "BufferLineSize is not a power of 2"); -struct SlidingHold { - alignas(16) float mValues[BufferLineSize]; - uint mExpiries[BufferLineSize]; +struct SIMDALIGN SlidingHold { + alignas(16) FloatBufferLine mValues; + std::array mExpiries; uint mLowerIndex; uint mUpperIndex; uint mLength; @@ -31,7 +30,9 @@ struct SlidingHold { namespace { -using namespace std::placeholders; +template +constexpr auto assume_aligned_span(const al::span s) noexcept -> al::span +{ return al::span{al::assume_aligned(s.data()), s.size()}; } /* This sliding hold follows the input level with an instant attack and a * fixed duration hold before an instant release to the next highest level. @@ -44,8 +45,8 @@ float UpdateSlidingHold(SlidingHold *Hold, const uint i, const float in) { static constexpr uint mask{BufferLineSize - 1}; const uint length{Hold->mLength}; - float (&values)[BufferLineSize] = Hold->mValues; - uint (&expiries)[BufferLineSize] = Hold->mExpiries; + const al::span values{Hold->mValues}; + const al::span expiries{Hold->mExpiries}; uint lowerIndex{Hold->mLowerIndex}; uint upperIndex{Hold->mUpperIndex}; @@ -60,14 +61,16 @@ float UpdateSlidingHold(SlidingHold *Hold, const uint i, const float in) } else { - do { + auto findLowerIndex = [&lowerIndex,in,values]() noexcept -> bool + { do { if(!(in >= values[lowerIndex])) - goto found_place; + return true; } while(lowerIndex--); + return false; + }; + while(!findLowerIndex()) lowerIndex = mask; - } while(true); - found_place: lowerIndex = (lowerIndex + 1) & mask; values[lowerIndex] = in; @@ -82,38 +85,42 @@ float UpdateSlidingHold(SlidingHold *Hold, const uint i, const float in) void ShiftSlidingHold(SlidingHold *Hold, const uint n) { - auto exp_begin = std::begin(Hold->mExpiries) + Hold->mUpperIndex; - auto exp_last = std::begin(Hold->mExpiries) + Hold->mLowerIndex; - if(exp_last-exp_begin < 0) + auto exp_upper = Hold->mExpiries.begin() + Hold->mUpperIndex; + if(Hold->mLowerIndex < Hold->mUpperIndex) { - std::transform(exp_begin, std::end(Hold->mExpiries), exp_begin, - [n](uint e){ return e - n; }); - exp_begin = std::begin(Hold->mExpiries); + std::transform(exp_upper, Hold->mExpiries.end(), exp_upper, + [n](const uint e) noexcept { return e - n; }); + exp_upper = Hold->mExpiries.begin(); } - std::transform(exp_begin, exp_last+1, exp_begin, [n](uint e){ return e - n; }); + const auto exp_lower = Hold->mExpiries.begin() + Hold->mLowerIndex; + std::transform(exp_upper, exp_lower+1, exp_upper, + [n](const uint e) noexcept { return e - n; }); } +} // namespace /* Multichannel compression is linked via the absolute maximum of all * channels. */ -void LinkChannels(Compressor *Comp, const uint SamplesToDo, const FloatBufferLine *OutBuffer) +void Compressor::linkChannels(const uint SamplesToDo, + const al::span OutBuffer) { - const size_t numChans{Comp->mNumChans}; - ASSUME(SamplesToDo > 0); - ASSUME(numChans > 0); + ASSUME(SamplesToDo <= BufferLineSize); - auto side_begin = std::begin(Comp->mSideChain) + Comp->mLookAhead; - std::fill(side_begin, side_begin+SamplesToDo, 0.0f); + const auto sideChain = al::span{mSideChain}.subspan(mLookAhead, SamplesToDo); + std::fill_n(sideChain.begin(), sideChain.size(), 0.0f); - auto fill_max = [SamplesToDo,side_begin](const FloatBufferLine &input) -> void + auto fill_max = [sideChain](const FloatBufferLine &input) -> void { - const float *RESTRICT buffer{al::assume_aligned<16>(input.data())}; - auto max_abs = std::bind(maxf, _1, std::bind(static_cast(std::fabs), _2)); - std::transform(side_begin, side_begin+SamplesToDo, buffer, side_begin, max_abs); + const auto buffer = assume_aligned_span<16>(al::span{input}); + auto max_abs = [](const float s0, const float s1) noexcept -> float + { return std::max(s0, std::fabs(s1)); }; + std::transform(sideChain.begin(), sideChain.end(), buffer.begin(), sideChain.begin(), + max_abs); }; - std::for_each(OutBuffer, OutBuffer+numChans, fill_max); + for(const FloatBufferLine &input : OutBuffer) + fill_max(input); } /* This calculates the squared crest factor of the control signal for the @@ -121,60 +128,63 @@ void LinkChannels(Compressor *Comp, const uint SamplesToDo, const FloatBufferLin * it uses an instantaneous squared peak detector and a squared RMS detector * both with 200ms release times. */ -void CrestDetector(Compressor *Comp, const uint SamplesToDo) +void Compressor::crestDetector(const uint SamplesToDo) { - const float a_crest{Comp->mCrestCoeff}; - float y2_peak{Comp->mLastPeakSq}; - float y2_rms{Comp->mLastRmsSq}; + const float a_crest{mCrestCoeff}; + float y2_peak{mLastPeakSq}; + float y2_rms{mLastRmsSq}; ASSUME(SamplesToDo > 0); + ASSUME(SamplesToDo <= BufferLineSize); auto calc_crest = [&y2_rms,&y2_peak,a_crest](const float x_abs) noexcept -> float { - const float x2{clampf(x_abs * x_abs, 0.000001f, 1000000.0f)}; + const float x2{std::clamp(x_abs*x_abs, 0.000001f, 1000000.0f)}; - y2_peak = maxf(x2, lerpf(x2, y2_peak, a_crest)); + y2_peak = std::max(x2, lerpf(x2, y2_peak, a_crest)); y2_rms = lerpf(x2, y2_rms, a_crest); return y2_peak / y2_rms; }; - auto side_begin = std::begin(Comp->mSideChain) + Comp->mLookAhead; - std::transform(side_begin, side_begin+SamplesToDo, std::begin(Comp->mCrestFactor), calc_crest); + const auto sideChain = al::span{mSideChain}.subspan(mLookAhead, SamplesToDo); + std::transform(sideChain.cbegin(), sideChain.cend(), mCrestFactor.begin(), calc_crest); - Comp->mLastPeakSq = y2_peak; - Comp->mLastRmsSq = y2_rms; + mLastPeakSq = y2_peak; + mLastRmsSq = y2_rms; } /* The side-chain starts with a simple peak detector (based on the absolute * value of the incoming signal) and performs most of its operations in the * log domain. */ -void PeakDetector(Compressor *Comp, const uint SamplesToDo) +void Compressor::peakDetector(const uint SamplesToDo) { ASSUME(SamplesToDo > 0); + ASSUME(SamplesToDo <= BufferLineSize); - /* Clamp the minimum amplitude to near-zero and convert to logarithm. */ - auto side_begin = std::begin(Comp->mSideChain) + Comp->mLookAhead; - std::transform(side_begin, side_begin+SamplesToDo, side_begin, - [](float s) { return std::log(maxf(0.000001f, s)); }); + /* Clamp the minimum amplitude to near-zero and convert to logarithmic. */ + const auto sideChain = al::span{mSideChain}.subspan(mLookAhead, SamplesToDo); + std::transform(sideChain.cbegin(), sideChain.cend(), sideChain.begin(), + [](float s) { return std::log(std::max(0.000001f, s)); }); } /* An optional hold can be used to extend the peak detector so it can more * solidly detect fast transients. This is best used when operating as a * limiter. */ -void PeakHoldDetector(Compressor *Comp, const uint SamplesToDo) +void Compressor::peakHoldDetector(const uint SamplesToDo) { ASSUME(SamplesToDo > 0); + ASSUME(SamplesToDo <= BufferLineSize); - SlidingHold *hold{Comp->mHold}; + SlidingHold *hold{mHold.get()}; uint i{0}; auto detect_peak = [&i,hold](const float x_abs) -> float { - const float x_G{std::log(maxf(0.000001f, x_abs))}; + const float x_G{std::log(std::max(0.000001f, x_abs))}; return UpdateSlidingHold(hold, i++, x_G); }; - auto side_begin = std::begin(Comp->mSideChain) + Comp->mLookAhead; - std::transform(side_begin, side_begin+SamplesToDo, side_begin, detect_peak); + auto sideChain = al::span{mSideChain}.subspan(mLookAhead, SamplesToDo); + std::transform(sideChain.cbegin(), sideChain.cend(), sideChain.begin(), detect_peak); ShiftSlidingHold(hold, SamplesToDo); } @@ -184,46 +194,46 @@ void PeakHoldDetector(Compressor *Comp, const uint SamplesToDo) * to knee width, attack/release times, make-up/post gain, and clipping * reduction. */ -void GainCompressor(Compressor *Comp, const uint SamplesToDo) +void Compressor::gainCompressor(const uint SamplesToDo) { - const bool autoKnee{Comp->mAuto.Knee}; - const bool autoAttack{Comp->mAuto.Attack}; - const bool autoRelease{Comp->mAuto.Release}; - const bool autoPostGain{Comp->mAuto.PostGain}; - const bool autoDeclip{Comp->mAuto.Declip}; - const uint lookAhead{Comp->mLookAhead}; - const float threshold{Comp->mThreshold}; - const float slope{Comp->mSlope}; - const float attack{Comp->mAttack}; - const float release{Comp->mRelease}; - const float c_est{Comp->mGainEstimate}; - const float a_adp{Comp->mAdaptCoeff}; - const float *crestFactor{Comp->mCrestFactor}; - float postGain{Comp->mPostGain}; - float knee{Comp->mKnee}; + const bool autoKnee{mAuto.Knee}; + const bool autoAttack{mAuto.Attack}; + const bool autoRelease{mAuto.Release}; + const bool autoPostGain{mAuto.PostGain}; + const bool autoDeclip{mAuto.Declip}; + const float threshold{mThreshold}; + const float slope{mSlope}; + const float attack{mAttack}; + const float release{mRelease}; + const float c_est{mGainEstimate}; + const float a_adp{mAdaptCoeff}; + auto lookAhead = mSideChain.cbegin() + mLookAhead; + auto crestFactor = mCrestFactor.cbegin(); + float postGain{mPostGain}; + float knee{mKnee}; float t_att{attack}; float t_rel{release - attack}; float a_att{std::exp(-1.0f / t_att)}; float a_rel{std::exp(-1.0f / t_rel)}; - float y_1{Comp->mLastRelease}; - float y_L{Comp->mLastAttack}; - float c_dev{Comp->mLastGainDev}; + float y_1{mLastRelease}; + float y_L{mLastAttack}; + float c_dev{mLastGainDev}; ASSUME(SamplesToDo > 0); - for(float &sideChain : al::span{Comp->mSideChain, SamplesToDo}) + auto process = [&](const float input) -> float { if(autoKnee) - knee = maxf(0.0f, 2.5f * (c_dev + c_est)); + knee = std::max(0.0f, 2.5f * (c_dev + c_est)); const float knee_h{0.5f * knee}; /* This is the gain computer. It applies a static compression curve * to the control signal. */ - const float x_over{std::addressof(sideChain)[lookAhead] - threshold}; + const float x_over{*(lookAhead++) - threshold}; const float y_G{ (x_over <= -knee_h) ? 0.0f : - (std::fabs(x_over) < knee_h) ? (x_over + knee_h) * (x_over + knee_h) / (2.0f * knee) : + (std::fabs(x_over) < knee_h) ? (x_over+knee_h) * (x_over+knee_h) / (2.0f * knee) : x_over}; const float y2_crest{*(crestFactor++)}; @@ -243,7 +253,7 @@ void GainCompressor(Compressor *Comp, const uint SamplesToDo) * above to compensate for the chained operating mode. */ const float x_L{-slope * y_G}; - y_1 = maxf(x_L, lerpf(x_L, y_1, a_rel)); + y_1 = std::max(x_L, lerpf(x_L, y_1, a_rel)); y_L = lerpf(y_1, y_L, a_att); /* Knee width and make-up gain automation make use of a smoothed @@ -262,17 +272,19 @@ void GainCompressor(Compressor *Comp, const uint SamplesToDo) * same output level. */ if(autoDeclip) - c_dev = maxf(c_dev, sideChain - y_L - threshold - c_est); + c_dev = std::max(c_dev, input - y_L - threshold - c_est); postGain = -(c_dev + c_est); } - sideChain = std::exp(postGain - y_L); - } + return std::exp(postGain - y_L); + }; + auto sideChain = al::span{mSideChain}.first(SamplesToDo); + std::transform(sideChain.begin(), sideChain.end(), sideChain.begin(), process); - Comp->mLastRelease = y_1; - Comp->mLastAttack = y_L; - Comp->mLastGainDev = c_dev; + mLastRelease = y_1; + mLastAttack = y_L; + mLastGainDev = c_dev; } /* Combined with the hold time, a look-ahead delay can improve handling of @@ -280,75 +292,60 @@ void GainCompressor(Compressor *Comp, const uint SamplesToDo) * reaching the offending impulse. This is best used when operating as a * limiter. */ -void SignalDelay(Compressor *Comp, const uint SamplesToDo, FloatBufferLine *OutBuffer) +void Compressor::signalDelay(const uint SamplesToDo, const al::span OutBuffer) { - const size_t numChans{Comp->mNumChans}; - const uint lookAhead{Comp->mLookAhead}; + const auto lookAhead = mLookAhead; ASSUME(SamplesToDo > 0); - ASSUME(numChans > 0); + ASSUME(SamplesToDo <= BufferLineSize); ASSUME(lookAhead > 0); + ASSUME(lookAhead < BufferLineSize); - for(size_t c{0};c < numChans;c++) + auto delays = mDelay.begin(); + for(auto &buffer : OutBuffer) { - float *inout{al::assume_aligned<16>(OutBuffer[c].data())}; - float *delaybuf{al::assume_aligned<16>(Comp->mDelay[c].data())}; + const auto inout = al::span{buffer}.first(SamplesToDo); + const auto delaybuf = al::span{*(delays++)}.first(lookAhead); - auto inout_end = inout + SamplesToDo; - if(SamplesToDo >= lookAhead) LIKELY + if(SamplesToDo >= delaybuf.size()) LIKELY { - auto delay_end = std::rotate(inout, inout_end - lookAhead, inout_end); - std::swap_ranges(inout, delay_end, delaybuf); + const auto inout_start = inout.end() - ptrdiff_t(delaybuf.size()); + const auto delay_end = std::rotate(inout.begin(), inout_start, inout.end()); + std::swap_ranges(inout.begin(), delay_end, delaybuf.begin()); } else { - auto delay_start = std::swap_ranges(inout, inout_end, delaybuf); - std::rotate(delaybuf, delay_start, delaybuf + lookAhead); + auto delay_start = std::swap_ranges(inout.begin(), inout.end(), delaybuf.begin()); + std::rotate(delaybuf.begin(), delay_start, delaybuf.end()); } } } -} // namespace - std::unique_ptr Compressor::Create(const size_t NumChans, const float SampleRate, - const bool AutoKnee, const bool AutoAttack, const bool AutoRelease, const bool AutoPostGain, - const bool AutoDeclip, const float LookAheadTime, const float HoldTime, const float PreGainDb, - const float PostGainDb, const float ThresholdDb, const float Ratio, const float KneeDb, - const float AttackTime, const float ReleaseTime) + const FlagBits autoflags, const float LookAheadTime, const float HoldTime, + const float PreGainDb, const float PostGainDb, const float ThresholdDb, const float Ratio, + const float KneeDb, const float AttackTime, const float ReleaseTime) { - const auto lookAhead = static_cast( - clampf(std::round(LookAheadTime*SampleRate), 0.0f, BufferLineSize-1)); - const auto hold = static_cast( - clampf(std::round(HoldTime*SampleRate), 0.0f, BufferLineSize-1)); - - size_t size{sizeof(Compressor)}; - if(lookAhead > 0) - { - size += sizeof(*Compressor::mDelay) * NumChans; - /* The sliding hold implementation doesn't handle a length of 1. A 1- - * sample hold is useless anyway, it would only ever give back what was - * just given to it. - */ - if(hold > 1) - size += sizeof(*Compressor::mHold); - } - - auto Comp = CompressorPtr{al::construct_at(static_cast(al_calloc(16, size)))}; - Comp->mNumChans = NumChans; - Comp->mAuto.Knee = AutoKnee; - Comp->mAuto.Attack = AutoAttack; - Comp->mAuto.Release = AutoRelease; - Comp->mAuto.PostGain = AutoPostGain; - Comp->mAuto.Declip = AutoPostGain && AutoDeclip; + const auto lookAhead = static_cast(std::clamp(std::round(LookAheadTime*SampleRate), 0.0f, + BufferLineSize-1.0f)); + const auto hold = static_cast(std::clamp(std::round(HoldTime*SampleRate), 0.0f, + BufferLineSize-1.0f)); + + auto Comp = CompressorPtr{new Compressor{}}; + Comp->mAuto.Knee = autoflags.test(AutoKnee); + Comp->mAuto.Attack = autoflags.test(AutoAttack); + Comp->mAuto.Release = autoflags.test(AutoRelease); + Comp->mAuto.PostGain = autoflags.test(AutoPostGain); + Comp->mAuto.Declip = autoflags.test(AutoPostGain) && autoflags.test(AutoDeclip); Comp->mLookAhead = lookAhead; Comp->mPreGain = std::pow(10.0f, PreGainDb / 20.0f); - Comp->mPostGain = PostGainDb * std::log(10.0f) / 20.0f; - Comp->mThreshold = ThresholdDb * std::log(10.0f) / 20.0f; - Comp->mSlope = 1.0f / maxf(1.0f, Ratio) - 1.0f; - Comp->mKnee = maxf(0.0f, KneeDb * std::log(10.0f) / 20.0f); - Comp->mAttack = maxf(1.0f, AttackTime * SampleRate); - Comp->mRelease = maxf(1.0f, ReleaseTime * SampleRate); + Comp->mPostGain = std::log(10.0f)/20.0f * PostGainDb; + Comp->mThreshold = std::log(10.0f)/20.0f * ThresholdDb; + Comp->mSlope = 1.0f / std::max(1.0f, Ratio) - 1.0f; + Comp->mKnee = std::max(0.0f, std::log(10.0f)/20.0f * KneeDb); + Comp->mAttack = std::max(1.0f, AttackTime * SampleRate); + Comp->mRelease = std::max(1.0f, ReleaseTime * SampleRate); /* Knee width automation actually treats the compressor as a limiter. By * varying the knee width, it can effectively be seen as applying @@ -359,17 +356,18 @@ std::unique_ptr Compressor::Create(const size_t NumChans, const floa if(lookAhead > 0) { + /* The sliding hold implementation doesn't handle a length of 1. A 1- + * sample hold is useless anyway, it would only ever give back what was + * just given to it. + */ if(hold > 1) { - Comp->mHold = al::construct_at(reinterpret_cast(Comp.get() + 1)); + Comp->mHold = std::make_unique(); Comp->mHold->mValues[0] = -std::numeric_limits::infinity(); Comp->mHold->mExpiries[0] = hold; Comp->mHold->mLength = hold; - Comp->mDelay = reinterpret_cast(Comp->mHold + 1); } - else - Comp->mDelay = reinterpret_cast(Comp.get() + 1); - std::uninitialized_fill_n(Comp->mDelay, NumChans, FloatBufferLine{}); + Comp->mDelay.resize(NumChans, FloatBufferLine{}); } Comp->mCrestCoeff = std::exp(-1.0f / (0.200f * SampleRate)); // 200ms @@ -379,61 +377,51 @@ std::unique_ptr Compressor::Create(const size_t NumChans, const floa return Comp; } -Compressor::~Compressor() -{ - if(mHold) - std::destroy_at(mHold); - mHold = nullptr; - if(mDelay) - std::destroy_n(mDelay, mNumChans); - mDelay = nullptr; -} +Compressor::~Compressor() = default; -void Compressor::process(const uint SamplesToDo, FloatBufferLine *OutBuffer) +void Compressor::process(const uint SamplesToDo, const al::span InOut) { - const size_t numChans{mNumChans}; - ASSUME(SamplesToDo > 0); - ASSUME(numChans > 0); + ASSUME(SamplesToDo <= BufferLineSize); const float preGain{mPreGain}; if(preGain != 1.0f) { auto apply_gain = [SamplesToDo,preGain](FloatBufferLine &input) noexcept -> void { - float *buffer{al::assume_aligned<16>(input.data())}; - std::transform(buffer, buffer+SamplesToDo, buffer, - [preGain](float s) { return s * preGain; }); + const auto buffer = assume_aligned_span<16>(al::span{input}.first(SamplesToDo)); + std::transform(buffer.cbegin(), buffer.cend(), buffer.begin(), + [preGain](const float s) noexcept { return s * preGain; }); }; - std::for_each(OutBuffer, OutBuffer+numChans, apply_gain); + std::for_each(InOut.begin(), InOut.end(), apply_gain); } - LinkChannels(this, SamplesToDo, OutBuffer); + linkChannels(SamplesToDo, InOut); if(mAuto.Attack || mAuto.Release) - CrestDetector(this, SamplesToDo); + crestDetector(SamplesToDo); if(mHold) - PeakHoldDetector(this, SamplesToDo); + peakHoldDetector(SamplesToDo); else - PeakDetector(this, SamplesToDo); + peakDetector(SamplesToDo); - GainCompressor(this, SamplesToDo); + gainCompressor(SamplesToDo); - if(mDelay) - SignalDelay(this, SamplesToDo, OutBuffer); + if(!mDelay.empty()) + signalDelay(SamplesToDo, InOut); - const float (&sideChain)[BufferLineSize*2] = mSideChain; - auto apply_comp = [SamplesToDo,&sideChain](FloatBufferLine &input) noexcept -> void + const auto gains = assume_aligned_span<16>(al::span{mSideChain}.first(SamplesToDo)); + auto apply_comp = [gains](const FloatBufferSpan inout) noexcept -> void { - float *buffer{al::assume_aligned<16>(input.data())}; - const float *gains{al::assume_aligned<16>(&sideChain[0])}; - std::transform(gains, gains+SamplesToDo, buffer, buffer, - [](float g, float s) { return g * s; }); + const auto buffer = assume_aligned_span<16>(inout); + std::transform(gains.cbegin(), gains.cend(), buffer.cbegin(), buffer.begin(), + std::multiplies{}); }; - std::for_each(OutBuffer, OutBuffer+numChans, apply_comp); + for(const FloatBufferSpan inout : InOut) + apply_comp(inout); - auto side_begin = std::begin(mSideChain) + SamplesToDo; - std::copy(side_begin, side_begin+mLookAhead, std::begin(mSideChain)); + const auto delayedGains = al::span{mSideChain}.subspan(SamplesToDo, mLookAhead); + std::copy(delayedGains.begin(), delayedGains.end(), mSideChain.begin()); } diff --git a/3rdparty/openal/core/mastering.h b/3rdparty/openal/core/mastering.h index 1a36937ca484..32f581dbc486 100644 --- a/3rdparty/openal/core/mastering.h +++ b/3rdparty/openal/core/mastering.h @@ -1,10 +1,15 @@ #ifndef CORE_MASTERING_H #define CORE_MASTERING_H +#include +#include #include -#include "almalloc.h" +#include "alnumeric.h" +#include "alspan.h" #include "bufferline.h" +#include "opthelpers.h" +#include "vector.h" struct SlidingHold; @@ -21,16 +26,15 @@ using uint = unsigned int; * * http://c4dm.eecs.qmul.ac.uk/audioengineering/compressors/ */ -struct Compressor { - size_t mNumChans{0u}; - - struct { +class SIMDALIGN Compressor { + struct AutoFlags { bool Knee : 1; bool Attack : 1; bool Release : 1; bool PostGain : 1; bool Declip : 1; - } mAuto{}; + }; + AutoFlags mAuto{}; uint mLookAhead{0}; @@ -44,11 +48,11 @@ struct Compressor { float mAttack{0.0f}; float mRelease{0.0f}; - alignas(16) float mSideChain[2*BufferLineSize]{}; - alignas(16) float mCrestFactor[BufferLineSize]{}; + alignas(16) std::array mSideChain{}; + alignas(16) std::array mCrestFactor{}; - SlidingHold *mHold{nullptr}; - FloatBufferLine *mDelay{nullptr}; + std::unique_ptr mHold; + al::vector mDelay; float mCrestCoeff{0.0f}; float mGainEstimate{0.0f}; @@ -60,12 +64,24 @@ struct Compressor { float mLastAttack{0.0f}; float mLastGainDev{0.0f}; + Compressor() = default; - ~Compressor(); - void process(const uint SamplesToDo, FloatBufferLine *OutBuffer); - int getLookAhead() const noexcept { return static_cast(mLookAhead); } + void linkChannels(const uint SamplesToDo, const al::span OutBuffer); + void crestDetector(const uint SamplesToDo); + void peakDetector(const uint SamplesToDo); + void peakHoldDetector(const uint SamplesToDo); + void gainCompressor(const uint SamplesToDo); + void signalDelay(const uint SamplesToDo, const al::span OutBuffer); - DEF_PLACE_NEWDEL() +public: + enum { + AutoKnee, AutoAttack, AutoRelease, AutoPostGain, AutoDeclip, FlagsCount + }; + using FlagBits = std::bitset; + + ~Compressor(); + void process(const uint SamplesToDo, al::span InOut); + [[nodiscard]] auto getLookAhead() const noexcept -> uint { return mLookAhead; } /** * The compressor is initialized with the following settings: @@ -94,11 +110,9 @@ struct Compressor { * automating release time. */ static std::unique_ptr Create(const size_t NumChans, const float SampleRate, - const bool AutoKnee, const bool AutoAttack, const bool AutoRelease, - const bool AutoPostGain, const bool AutoDeclip, const float LookAheadTime, - const float HoldTime, const float PreGainDb, const float PostGainDb, - const float ThresholdDb, const float Ratio, const float KneeDb, const float AttackTime, - const float ReleaseTime); + const FlagBits autoflags, const float LookAheadTime, const float HoldTime, + const float PreGainDb, const float PostGainDb, const float ThresholdDb, const float Ratio, + const float KneeDb, const float AttackTime, const float ReleaseTime); }; using CompressorPtr = std::unique_ptr; diff --git a/3rdparty/openal/core/mixer.cpp b/3rdparty/openal/core/mixer.cpp index 066c57bd765e..bba7ae2061f9 100644 --- a/3rdparty/openal/core/mixer.cpp +++ b/3rdparty/openal/core/mixer.cpp @@ -3,10 +3,12 @@ #include "mixer.h" +#include #include +#include #include "alnumbers.h" -#include "devformat.h" +#include "core/ambidefs.h" #include "device.h" #include "mixer/defs.h" @@ -82,14 +84,13 @@ std::array CalcAmbiCoeffs(const float y, const float z, c return coeffs; } -void ComputePanGains(const MixParams *mix, const float*RESTRICT coeffs, const float ingain, - const al::span gains) +void ComputePanGains(const MixParams *mix, const al::span coeffs, + const float ingain, const al::span gains) { - auto ambimap = mix->AmbiMap.cbegin(); + auto ambimap = al::span{std::as_const(mix->AmbiMap)}.first(mix->Buffer.size()); - auto iter = std::transform(ambimap, ambimap+mix->Buffer.size(), gains.begin(), + auto iter = std::transform(ambimap.begin(), ambimap.end(), gains.begin(), [coeffs,ingain](const BFChannelConfig &chanmap) noexcept -> float - { return chanmap.Scale * coeffs[chanmap.Index] * ingain; } - ); + { return chanmap.Scale * coeffs[chanmap.Index] * ingain; }); std::fill(iter, gains.end(), 0.0f); } diff --git a/3rdparty/openal/core/mixer.h b/3rdparty/openal/core/mixer.h index a9c1f931ca9f..ec6c3271d578 100644 --- a/3rdparty/openal/core/mixer.h +++ b/3rdparty/openal/core/mixer.h @@ -3,34 +3,33 @@ #include #include -#include -#include +#include #include "alspan.h" #include "ambidefs.h" #include "bufferline.h" -#include "devformat.h" +#include "opthelpers.h" struct MixParams; /* Mixer functions that handle one input and multiple output channels. */ using MixerOutFunc = void(*)(const al::span InSamples, - const al::span OutBuffer, float *CurrentGains, const float *TargetGains, - const size_t Counter, const size_t OutPos); + const al::span OutBuffer, const al::span CurrentGains, + const al::span TargetGains, const std::size_t Counter, const std::size_t OutPos); -extern MixerOutFunc MixSamplesOut; +DECL_HIDDEN extern MixerOutFunc MixSamplesOut; inline void MixSamples(const al::span InSamples, - const al::span OutBuffer, float *CurrentGains, const float *TargetGains, - const size_t Counter, const size_t OutPos) + const al::span OutBuffer, const al::span CurrentGains, + const al::span TargetGains, const std::size_t Counter, const std::size_t OutPos) { MixSamplesOut(InSamples, OutBuffer, CurrentGains, TargetGains, Counter, OutPos); } /* Mixer functions that handle one input and one output channel. */ -using MixerOneFunc = void(*)(const al::span InSamples, float *OutBuffer, - float &CurrentGain, const float TargetGain, const size_t Counter); +using MixerOneFunc = void(*)(const al::span InSamples,const al::span OutBuffer, + float &CurrentGain, const float TargetGain, const std::size_t Counter); -extern MixerOneFunc MixSamplesOne; -inline void MixSamples(const al::span InSamples, float *OutBuffer, float &CurrentGain, - const float TargetGain, const size_t Counter) +DECL_HIDDEN extern MixerOneFunc MixSamplesOne; +inline void MixSamples(const al::span InSamples, const al::span OutBuffer, + float &CurrentGain, const float TargetGain, const std::size_t Counter) { MixSamplesOne(InSamples, OutBuffer, CurrentGain, TargetGain, Counter); } @@ -103,7 +102,7 @@ inline std::array CalcAngleCoeffs(const float azimuth, * coeffs are a 'slice' of a transform matrix for the input channel, used to * scale and orient the sound samples. */ -void ComputePanGains(const MixParams *mix, const float*RESTRICT coeffs, const float ingain, - const al::span gains); +void ComputePanGains(const MixParams *mix, const al::span coeffs, + const float ingain, const al::span gains); #endif /* CORE_MIXER_H */ diff --git a/3rdparty/openal/core/mixer/defs.h b/3rdparty/openal/core/mixer/defs.h index 48daca9b95c2..d4d8910303ac 100644 --- a/3rdparty/openal/core/mixer/defs.h +++ b/3rdparty/openal/core/mixer/defs.h @@ -2,13 +2,15 @@ #define CORE_MIXER_DEFS_H #include -#include +#include +#include +#include +#include #include "alspan.h" #include "core/bufferline.h" -#include "core/resampler_limits.h" +#include "core/cubic_defs.h" -struct CubicCoefficients; struct HrtfChannelState; struct HrtfFilter; struct MixHrtfFilter; @@ -17,18 +19,19 @@ using uint = unsigned int; using float2 = std::array; -constexpr int MixerFracBits{16}; -constexpr int MixerFracOne{1 << MixerFracBits}; -constexpr int MixerFracMask{MixerFracOne - 1}; -constexpr int MixerFracHalf{MixerFracOne >> 1}; +inline constexpr int MixerFracBits{16}; +inline constexpr int MixerFracOne{1 << MixerFracBits}; +inline constexpr int MixerFracMask{MixerFracOne - 1}; +inline constexpr int MixerFracHalf{MixerFracOne >> 1}; -constexpr float GainSilenceThreshold{0.00001f}; /* -100dB */ +inline constexpr float GainSilenceThreshold{0.00001f}; /* -100dB */ -enum class Resampler : uint8_t { +enum class Resampler : std::uint8_t { Point, Linear, - Cubic, + Spline, + Gaussian, FastBSinc12, BSinc12, FastBSinc24, @@ -49,56 +52,59 @@ struct BsincState { * delta coefficients. Starting at phase index 0, each subsequent phase * index follows contiguously. */ - const float *filter; + al::span filter; }; struct CubicState { /* Filter coefficients, and coefficient deltas. Starting at phase index 0, * each subsequent phase index follows contiguously. */ - const CubicCoefficients *filter; + al::span filter; + explicit CubicState(al::span f) : filter{f} { } }; -union InterpState { - CubicState cubic; - BsincState bsinc; -}; +using InterpState = std::variant; -using ResamplerFunc = void(*)(const InterpState *state, const float *RESTRICT src, uint frac, +using ResamplerFunc = void(*)(const InterpState *state, const al::span src, uint frac, const uint increment, const al::span dst); ResamplerFunc PrepareResampler(Resampler resampler, uint increment, InterpState *state); template -void Resample_(const InterpState *state, const float *RESTRICT src, uint frac, +void Resample_(const InterpState *state, const al::span src, uint frac, const uint increment, const al::span dst); template void Mix_(const al::span InSamples, const al::span OutBuffer, - float *CurrentGains, const float *TargetGains, const size_t Counter, const size_t OutPos); + const al::span CurrentGains, const al::span TargetGains, + const size_t Counter, const size_t OutPos); template -void Mix_(const al::span InSamples, float *OutBuffer, float &CurrentGain, - const float TargetGain, const size_t Counter); +void Mix_(const al::span InSamples, const al::span OutBuffer, + float &CurrentGain, const float TargetGain, const size_t Counter); template -void MixHrtf_(const float *InSamples, float2 *AccumSamples, const uint IrSize, - const MixHrtfFilter *hrtfparams, const size_t BufferSize); +void MixHrtf_(const al::span InSamples, const al::span AccumSamples, + const uint IrSize, const MixHrtfFilter *hrtfparams, const size_t SamplesToDo); template -void MixHrtfBlend_(const float *InSamples, float2 *AccumSamples, const uint IrSize, - const HrtfFilter *oldparams, const MixHrtfFilter *newparams, const size_t BufferSize); +void MixHrtfBlend_(const al::span InSamples, const al::span AccumSamples, + const uint IrSize, const HrtfFilter *oldparams, const MixHrtfFilter *newparams, + const size_t SamplesToDo); template void MixDirectHrtf_(const FloatBufferSpan LeftOut, const FloatBufferSpan RightOut, - const al::span InSamples, float2 *AccumSamples, - float *TempBuf, HrtfChannelState *ChanState, const size_t IrSize, const size_t BufferSize); + const al::span InSamples, const al::span AccumSamples, + const al::span TempBuf, const al::span ChanState, + const size_t IrSize, const size_t SamplesToDo); /* Vectorized resampler helpers */ template -inline void InitPosArrays(uint frac, uint increment, uint (&frac_arr)[N], uint (&pos_arr)[N]) +constexpr void InitPosArrays(uint pos, uint frac, const uint increment, + const al::span frac_arr, const al::span pos_arr) { - pos_arr[0] = 0; + static_assert(pos_arr.size() == frac_arr.size()); + pos_arr[0] = pos; frac_arr[0] = frac; - for(size_t i{1};i < N;i++) + for(size_t i{1};i < pos_arr.size();++i) { const uint frac_tmp{frac_arr[i-1] + increment}; pos_arr[i] = pos_arr[i-1] + (frac_tmp>>MixerFracBits); diff --git a/3rdparty/openal/core/mixer/hrtfbase.h b/3rdparty/openal/core/mixer/hrtfbase.h index 36f88e4967ce..703bfab9d649 100644 --- a/3rdparty/openal/core/mixer/hrtfbase.h +++ b/3rdparty/openal/core/mixer/hrtfbase.h @@ -4,21 +4,23 @@ #include #include -#include "almalloc.h" +#include "defs.h" #include "hrtfdefs.h" #include "opthelpers.h" using uint = unsigned int; -using ApplyCoeffsT = void(&)(float2 *RESTRICT Values, const size_t irSize, +using ApplyCoeffsT = void(const al::span Values, const size_t irSize, const ConstHrirSpan Coeffs, const float left, const float right); template -inline void MixHrtfBase(const float *InSamples, float2 *RESTRICT AccumSamples, const size_t IrSize, - const MixHrtfFilter *hrtfparams, const size_t BufferSize) +inline void MixHrtfBase(const al::span InSamples, const al::span AccumSamples, + const size_t IrSize, const MixHrtfFilter *hrtfparams, const size_t SamplesToDo) { - ASSUME(BufferSize > 0); + ASSUME(SamplesToDo > 0); + ASSUME(SamplesToDo <= BufferLineSize); + ASSUME(IrSize <= HrirLength); const ConstHrirSpan Coeffs{hrtfparams->Coeffs}; const float gainstep{hrtfparams->GainStep}; @@ -27,26 +29,28 @@ inline void MixHrtfBase(const float *InSamples, float2 *RESTRICT AccumSamples, c size_t ldelay{HrtfHistoryLength - hrtfparams->Delay[0]}; size_t rdelay{HrtfHistoryLength - hrtfparams->Delay[1]}; float stepcount{0.0f}; - for(size_t i{0u};i < BufferSize;++i) + for(size_t i{0u};i < SamplesToDo;++i) { const float g{gain + gainstep*stepcount}; const float left{InSamples[ldelay++] * g}; const float right{InSamples[rdelay++] * g}; - ApplyCoeffs(AccumSamples+i, IrSize, Coeffs, left, right); + ApplyCoeffs(AccumSamples.subspan(i), IrSize, Coeffs, left, right); stepcount += 1.0f; } } template -inline void MixHrtfBlendBase(const float *InSamples, float2 *RESTRICT AccumSamples, - const size_t IrSize, const HrtfFilter *oldparams, const MixHrtfFilter *newparams, - const size_t BufferSize) +inline void MixHrtfBlendBase(const al::span InSamples, + const al::span AccumSamples, const size_t IrSize, const HrtfFilter *oldparams, + const MixHrtfFilter *newparams, const size_t SamplesToDo) { - ASSUME(BufferSize > 0); + ASSUME(SamplesToDo > 0); + ASSUME(SamplesToDo <= BufferLineSize); + ASSUME(IrSize <= HrirLength); const ConstHrirSpan OldCoeffs{oldparams->Coeffs}; - const float oldGainStep{oldparams->Gain / static_cast(BufferSize)}; + const float oldGainStep{oldparams->Gain / static_cast(SamplesToDo)}; const ConstHrirSpan NewCoeffs{newparams->Coeffs}; const float newGainStep{newparams->GainStep}; @@ -54,29 +58,29 @@ inline void MixHrtfBlendBase(const float *InSamples, float2 *RESTRICT AccumSampl { size_t ldelay{HrtfHistoryLength - oldparams->Delay[0]}; size_t rdelay{HrtfHistoryLength - oldparams->Delay[1]}; - auto stepcount = static_cast(BufferSize); - for(size_t i{0u};i < BufferSize;++i) + auto stepcount = static_cast(SamplesToDo); + for(size_t i{0u};i < SamplesToDo;++i) { const float g{oldGainStep*stepcount}; const float left{InSamples[ldelay++] * g}; const float right{InSamples[rdelay++] * g}; - ApplyCoeffs(AccumSamples+i, IrSize, OldCoeffs, left, right); + ApplyCoeffs(AccumSamples.subspan(i), IrSize, OldCoeffs, left, right); stepcount -= 1.0f; } } - if(newGainStep*static_cast(BufferSize) > GainSilenceThreshold) LIKELY + if(newGainStep*static_cast(SamplesToDo) > GainSilenceThreshold) LIKELY { size_t ldelay{HrtfHistoryLength+1 - newparams->Delay[0]}; size_t rdelay{HrtfHistoryLength+1 - newparams->Delay[1]}; float stepcount{1.0f}; - for(size_t i{1u};i < BufferSize;++i) + for(size_t i{1u};i < SamplesToDo;++i) { const float g{newGainStep*stepcount}; const float left{InSamples[ldelay++] * g}; const float right{InSamples[rdelay++] * g}; - ApplyCoeffs(AccumSamples+i, IrSize, NewCoeffs, left, right); + ApplyCoeffs(AccumSamples.subspan(i), IrSize, NewCoeffs, left, right); stepcount += 1.0f; } @@ -85,45 +89,52 @@ inline void MixHrtfBlendBase(const float *InSamples, float2 *RESTRICT AccumSampl template inline void MixDirectHrtfBase(const FloatBufferSpan LeftOut, const FloatBufferSpan RightOut, - const al::span InSamples, float2 *RESTRICT AccumSamples, - float *TempBuf, HrtfChannelState *ChanState, const size_t IrSize, const size_t BufferSize) + const al::span InSamples, const al::span AccumSamples, + const al::span TempBuf, const al::span ChannelState, + const size_t IrSize, const size_t SamplesToDo) { - ASSUME(BufferSize > 0); + ASSUME(SamplesToDo > 0); + ASSUME(SamplesToDo <= BufferLineSize); + ASSUME(IrSize <= HrirLength); + assert(ChannelState.size() == InSamples.size()); + auto ChanState = ChannelState.begin(); for(const FloatBufferLine &input : InSamples) { /* For dual-band processing, the signal needs extra scaling applied to * the high frequency response. The band-splitter applies this scaling * with a consistent phase shift regardless of the scale amount. */ - ChanState->mSplitter.processHfScale({input.data(), BufferSize}, TempBuf, + ChanState->mSplitter.processHfScale(al::span{input}.first(SamplesToDo), TempBuf, ChanState->mHfScale); /* Now apply the HRIR coefficients to this channel. */ - const float *RESTRICT tempbuf{al::assume_aligned<16>(TempBuf)}; const ConstHrirSpan Coeffs{ChanState->mCoeffs}; - for(size_t i{0u};i < BufferSize;++i) + for(size_t i{0u};i < SamplesToDo;++i) { - const float insample{tempbuf[i]}; - ApplyCoeffs(AccumSamples+i, IrSize, Coeffs, insample, insample); + const float insample{TempBuf[i]}; + ApplyCoeffs(AccumSamples.subspan(i), IrSize, Coeffs, insample, insample); } ++ChanState; } /* Add the HRTF signal to the existing "direct" signal. */ - float *RESTRICT left{al::assume_aligned<16>(LeftOut.data())}; - float *RESTRICT right{al::assume_aligned<16>(RightOut.data())}; - for(size_t i{0u};i < BufferSize;++i) - left[i] += AccumSamples[i][0]; - for(size_t i{0u};i < BufferSize;++i) - right[i] += AccumSamples[i][1]; + const auto left = al::span{al::assume_aligned<16>(LeftOut.data()), SamplesToDo}; + std::transform(left.cbegin(), left.cend(), AccumSamples.cbegin(), left.begin(), + [](const float sample, const float2 &accum) noexcept -> float + { return sample + accum[0]; }); + const auto right = al::span{al::assume_aligned<16>(RightOut.data()), SamplesToDo}; + std::transform(right.cbegin(), right.cend(), AccumSamples.cbegin(), right.begin(), + [](const float sample, const float2 &accum) noexcept -> float + { return sample + accum[1]; }); /* Copy the new in-progress accumulation values to the front and clear the * following samples for the next mix. */ - auto accum_iter = std::copy_n(AccumSamples+BufferSize, HrirLength, AccumSamples); - std::fill_n(accum_iter, BufferSize, float2{}); + const auto accum_inprog = AccumSamples.subspan(SamplesToDo, HrirLength); + auto accum_iter = std::copy(accum_inprog.cbegin(), accum_inprog.cend(), AccumSamples.begin()); + std::fill_n(accum_iter, SamplesToDo, float2{}); } #endif /* CORE_MIXER_HRTFBASE_H */ diff --git a/3rdparty/openal/core/mixer/mixer_c.cpp b/3rdparty/openal/core/mixer/mixer_c.cpp index 28a92ef7e637..e14454d26d84 100644 --- a/3rdparty/openal/core/mixer/mixer_c.cpp +++ b/3rdparty/openal/core/mixer/mixer_c.cpp @@ -1,14 +1,21 @@ #include "config.h" -#include -#include +#include +#include +#include #include +#include #include "alnumeric.h" +#include "alspan.h" #include "core/bsinc_defs.h" +#include "core/bufferline.h" #include "core/cubic_defs.h" +#include "core/mixer/hrtfdefs.h" +#include "core/resampler_limits.h" #include "defs.h" #include "hrtfbase.h" +#include "opthelpers.h" struct CTag; struct PointTag; @@ -28,191 +35,246 @@ constexpr uint CubicPhaseDiffBits{MixerFracBits - CubicPhaseBits}; constexpr uint CubicPhaseDiffOne{1 << CubicPhaseDiffBits}; constexpr uint CubicPhaseDiffMask{CubicPhaseDiffOne - 1u}; -inline float do_point(const InterpState&, const float *RESTRICT vals, const uint) -{ return vals[0]; } -inline float do_lerp(const InterpState&, const float *RESTRICT vals, const uint frac) -{ return lerpf(vals[0], vals[1], static_cast(frac)*(1.0f/MixerFracOne)); } -inline float do_cubic(const InterpState &istate, const float *RESTRICT vals, const uint frac) +using SamplerNST = float(const al::span, const size_t, const uint) noexcept; + +template +using SamplerT = float(const T&,const al::span,const size_t,const uint) noexcept; + +[[nodiscard]] constexpr +auto do_point(const al::span vals, const size_t pos, const uint) noexcept -> float +{ return vals[pos]; } +[[nodiscard]] constexpr +auto do_lerp(const al::span vals, const size_t pos, const uint frac) noexcept -> float +{ return lerpf(vals[pos+0], vals[pos+1], static_cast(frac)*(1.0f/MixerFracOne)); } +[[nodiscard]] constexpr +auto do_cubic(const CubicState &istate, const al::span vals, const size_t pos, + const uint frac) noexcept -> float { /* Calculate the phase index and factor. */ - const uint pi{frac >> CubicPhaseDiffBits}; + const uint pi{frac >> CubicPhaseDiffBits}; ASSUME(pi < CubicPhaseCount); const float pf{static_cast(frac&CubicPhaseDiffMask) * (1.0f/CubicPhaseDiffOne)}; - const float *RESTRICT fil{al::assume_aligned<16>(istate.cubic.filter[pi].mCoeffs)}; - const float *RESTRICT phd{al::assume_aligned<16>(istate.cubic.filter[pi].mDeltas)}; + const auto fil = al::span{istate.filter[pi].mCoeffs}; + const auto phd = al::span{istate.filter[pi].mDeltas}; /* Apply the phase interpolated filter. */ - return (fil[0] + pf*phd[0])*vals[0] + (fil[1] + pf*phd[1])*vals[1] - + (fil[2] + pf*phd[2])*vals[2] + (fil[3] + pf*phd[3])*vals[3]; + return (fil[0] + pf*phd[0])*vals[pos+0] + (fil[1] + pf*phd[1])*vals[pos+1] + + (fil[2] + pf*phd[2])*vals[pos+2] + (fil[3] + pf*phd[3])*vals[pos+3]; } -inline float do_bsinc(const InterpState &istate, const float *RESTRICT vals, const uint frac) +[[nodiscard]] constexpr +auto do_fastbsinc(const BsincState &bsinc, const al::span vals, const size_t pos, + const uint frac) noexcept -> float { - const size_t m{istate.bsinc.m}; + const size_t m{bsinc.m}; ASSUME(m > 0); + ASSUME(m <= MaxResamplerPadding); /* Calculate the phase index and factor. */ - const uint pi{frac >> BsincPhaseDiffBits}; + const uint pi{frac >> BsincPhaseDiffBits}; ASSUME(pi < BSincPhaseCount); const float pf{static_cast(frac&BsincPhaseDiffMask) * (1.0f/BsincPhaseDiffOne)}; - const float *RESTRICT fil{istate.bsinc.filter + m*pi*2}; - const float *RESTRICT phd{fil + m}; - const float *RESTRICT scd{fil + BSincPhaseCount*2*m}; - const float *RESTRICT spd{scd + m}; + const auto fil = bsinc.filter.subspan(2_uz*pi*m); + const auto phd = fil.subspan(m); - /* Apply the scale and phase interpolated filter. */ + /* Apply the phase interpolated filter. */ float r{0.0f}; - for(size_t j_f{0};j_f < m;j_f++) - r += (fil[j_f] + istate.bsinc.sf*scd[j_f] + pf*(phd[j_f] + istate.bsinc.sf*spd[j_f])) * vals[j_f]; + for(size_t j_f{0};j_f < m;++j_f) + r += (fil[j_f] + pf*phd[j_f]) * vals[pos+j_f]; return r; } -inline float do_fastbsinc(const InterpState &istate, const float *RESTRICT vals, const uint frac) +[[nodiscard]] constexpr +auto do_bsinc(const BsincState &bsinc, const al::span vals, const size_t pos, + const uint frac) noexcept -> float { - const size_t m{istate.bsinc.m}; + const size_t m{bsinc.m}; ASSUME(m > 0); + ASSUME(m <= MaxResamplerPadding); /* Calculate the phase index and factor. */ - const uint pi{frac >> BsincPhaseDiffBits}; + const uint pi{frac >> BsincPhaseDiffBits}; ASSUME(pi < BSincPhaseCount); const float pf{static_cast(frac&BsincPhaseDiffMask) * (1.0f/BsincPhaseDiffOne)}; - const float *RESTRICT fil{istate.bsinc.filter + m*pi*2}; - const float *RESTRICT phd{fil + m}; + const auto fil = bsinc.filter.subspan(2_uz*pi*m); + const auto phd = fil.subspan(m); + const auto scd = fil.subspan(BSincPhaseCount*2_uz*m); + const auto spd = scd.subspan(m); - /* Apply the phase interpolated filter. */ + /* Apply the scale and phase interpolated filter. */ float r{0.0f}; - for(size_t j_f{0};j_f < m;j_f++) - r += (fil[j_f] + pf*phd[j_f]) * vals[j_f]; + for(size_t j_f{0};j_f < m;++j_f) + r += (fil[j_f] + bsinc.sf*scd[j_f] + pf*(phd[j_f] + bsinc.sf*spd[j_f])) * vals[pos+j_f]; return r; } -using SamplerT = float(&)(const InterpState&, const float*RESTRICT, const uint); -template -void DoResample(const InterpState *state, const float *RESTRICT src, uint frac, - const uint increment, const al::span dst) +template +void DoResample(const al::span src, uint frac, const uint increment, + const al::span dst) { - const InterpState istate{*state}; ASSUME(frac < MixerFracOne); - for(float &out : dst) + size_t pos{0}; + std::generate(dst.begin(), dst.end(), [&pos,&frac,src,increment]() -> float { - out = Sampler(istate, src, frac); + const float output{Sampler(src, pos, frac)}; + frac += increment; + pos += frac>>MixerFracBits; + frac &= MixerFracMask; + return output; + }); +} +template Sampler> +void DoResample(const U istate, const al::span src, uint frac, const uint increment, + const al::span dst) +{ + ASSUME(frac < MixerFracOne); + size_t pos{0}; + std::generate(dst.begin(), dst.end(), [istate,src,&pos,&frac,increment]() -> float + { + const float output{Sampler(istate, src, pos, frac)}; frac += increment; - src += frac>>MixerFracBits; + pos += frac>>MixerFracBits; frac &= MixerFracMask; - } + return output; + }); } -inline void ApplyCoeffs(float2 *RESTRICT Values, const size_t IrSize, const ConstHrirSpan Coeffs, - const float left, const float right) +inline void ApplyCoeffs(const al::span Values, const size_t IrSize, + const ConstHrirSpan Coeffs, const float left, const float right) noexcept { ASSUME(IrSize >= MinIrLength); - for(size_t c{0};c < IrSize;++c) - { - Values[c][0] += Coeffs[c][0] * left; - Values[c][1] += Coeffs[c][1] * right; - } + ASSUME(IrSize <= HrirLength); + + auto mix_impulse = [left,right](const float2 &value, const float2 &coeff) noexcept -> float2 + { return float2{{value[0] + coeff[0]*left, value[1] + coeff[1]*right}}; }; + std::transform(Values.cbegin(), Values.cbegin()+ptrdiff_t(IrSize), Coeffs.cbegin(), + Values.begin(), mix_impulse); } -force_inline void MixLine(const al::span InSamples, float *RESTRICT dst, - float &CurrentGain, const float TargetGain, const float delta, const size_t min_len, +force_inline void MixLine(al::span InSamples, const al::span dst, + float &CurrentGain, const float TargetGain, const float delta, const size_t fade_len, size_t Counter) { - float gain{CurrentGain}; - const float step{(TargetGain-gain) * delta}; + const float step{(TargetGain-CurrentGain) * delta}; - size_t pos{0}; - if(!(std::abs(step) > std::numeric_limits::epsilon())) - gain = TargetGain; - else + auto output = dst.begin(); + if(std::abs(step) > std::numeric_limits::epsilon()) { + auto input = InSamples.first(fade_len); + InSamples = InSamples.subspan(fade_len); + + const float gain{CurrentGain}; float step_count{0.0f}; - for(;pos != min_len;++pos) + output = std::transform(input.begin(), input.end(), output, output, + [gain,step,&step_count](const float in, float out) noexcept -> float + { + out += in * (gain + step*step_count); + step_count += 1.0f; + return out; + }); + + if(fade_len < Counter) { - dst[pos] += InSamples[pos] * (gain + step*step_count); - step_count += 1.0f; + CurrentGain = gain + step*step_count; + return; } - if(pos == Counter) - gain = TargetGain; - else - gain += step*step_count; } - CurrentGain = gain; + CurrentGain = TargetGain; - if(!(std::abs(gain) > GainSilenceThreshold)) + if(!(std::abs(TargetGain) > GainSilenceThreshold)) return; - for(;pos != InSamples.size();++pos) - dst[pos] += InSamples[pos] * gain; + + std::transform(InSamples.begin(), InSamples.end(), output, output, + [TargetGain](const float in, const float out) noexcept -> float + { return out + in*TargetGain; }); } } // namespace template<> -void Resample_(const InterpState *state, const float *RESTRICT src, uint frac, +void Resample_(const InterpState*, const al::span src, uint frac, const uint increment, const al::span dst) -{ DoResample(state, src, frac, increment, dst); } +{ DoResample(src.subspan(MaxResamplerEdge), frac, increment, dst); } template<> -void Resample_(const InterpState *state, const float *RESTRICT src, uint frac, +void Resample_(const InterpState*, const al::span src, uint frac, const uint increment, const al::span dst) -{ DoResample(state, src, frac, increment, dst); } +{ DoResample(src.subspan(MaxResamplerEdge), frac, increment, dst); } template<> -void Resample_(const InterpState *state, const float *RESTRICT src, uint frac, +void Resample_(const InterpState *state, const al::span src, uint frac, const uint increment, const al::span dst) -{ DoResample(state, src-1, frac, increment, dst); } +{ + DoResample(std::get(*state), src.subspan(MaxResamplerEdge-1), + frac, increment, dst); +} template<> -void Resample_(const InterpState *state, const float *RESTRICT src, uint frac, - const uint increment, const al::span dst) -{ DoResample(state, src-state->bsinc.l, frac, increment, dst); } +void Resample_(const InterpState *state, const al::span src, + uint frac, const uint increment, const al::span dst) +{ + const auto istate = std::get(*state); + ASSUME(istate.l <= MaxResamplerEdge); + DoResample(istate, src.subspan(MaxResamplerEdge-istate.l), frac, + increment, dst); +} template<> -void Resample_(const InterpState *state, const float *RESTRICT src, uint frac, +void Resample_(const InterpState *state, const al::span src, uint frac, const uint increment, const al::span dst) -{ DoResample(state, src-state->bsinc.l, frac, increment, dst); } +{ + const auto istate = std::get(*state); + ASSUME(istate.l <= MaxResamplerEdge); + DoResample(istate, src.subspan(MaxResamplerEdge-istate.l), frac, + increment, dst); +} template<> -void MixHrtf_(const float *InSamples, float2 *AccumSamples, const uint IrSize, - const MixHrtfFilter *hrtfparams, const size_t BufferSize) -{ MixHrtfBase(InSamples, AccumSamples, IrSize, hrtfparams, BufferSize); } +void MixHrtf_(const al::span InSamples, const al::span AccumSamples, + const uint IrSize, const MixHrtfFilter *hrtfparams, const size_t SamplesToDo) +{ MixHrtfBase(InSamples, AccumSamples, IrSize, hrtfparams, SamplesToDo); } template<> -void MixHrtfBlend_(const float *InSamples, float2 *AccumSamples, const uint IrSize, - const HrtfFilter *oldparams, const MixHrtfFilter *newparams, const size_t BufferSize) +void MixHrtfBlend_(const al::span InSamples,const al::span AccumSamples, + const uint IrSize, const HrtfFilter *oldparams, const MixHrtfFilter *newparams, + const size_t SamplesToDo) { MixHrtfBlendBase(InSamples, AccumSamples, IrSize, oldparams, newparams, - BufferSize); + SamplesToDo); } template<> void MixDirectHrtf_(const FloatBufferSpan LeftOut, const FloatBufferSpan RightOut, - const al::span InSamples, float2 *AccumSamples, - float *TempBuf, HrtfChannelState *ChanState, const size_t IrSize, const size_t BufferSize) + const al::span InSamples, const al::span AccumSamples, + const al::span TempBuf, const al::span ChanState, + const size_t IrSize, const size_t SamplesToDo) { MixDirectHrtfBase(LeftOut, RightOut, InSamples, AccumSamples, TempBuf, ChanState, - IrSize, BufferSize); + IrSize, SamplesToDo); } template<> void Mix_(const al::span InSamples, const al::span OutBuffer, - float *CurrentGains, const float *TargetGains, const size_t Counter, const size_t OutPos) + const al::span CurrentGains, const al::span TargetGains, + const size_t Counter, const size_t OutPos) { const float delta{(Counter > 0) ? 1.0f / static_cast(Counter) : 0.0f}; - const auto min_len = minz(Counter, InSamples.size()); + const auto fade_len = std::min(Counter, InSamples.size()); + auto curgains = CurrentGains.begin(); + auto targetgains = TargetGains.cbegin(); for(FloatBufferLine &output : OutBuffer) - MixLine(InSamples, al::assume_aligned<16>(output.data()+OutPos), *CurrentGains++, - *TargetGains++, delta, min_len, Counter); + MixLine(InSamples, al::span{output}.subspan(OutPos), *curgains++, *targetgains++, delta, + fade_len, Counter); } template<> -void Mix_(const al::span InSamples, float *OutBuffer, float &CurrentGain, - const float TargetGain, const size_t Counter) +void Mix_(const al::span InSamples, const al::span OutBuffer, + float &CurrentGain, const float TargetGain, const size_t Counter) { const float delta{(Counter > 0) ? 1.0f / static_cast(Counter) : 0.0f}; - const auto min_len = minz(Counter, InSamples.size()); + const auto fade_len = std::min(Counter, InSamples.size()); - MixLine(InSamples, al::assume_aligned<16>(OutBuffer), CurrentGain, - TargetGain, delta, min_len, Counter); + MixLine(InSamples, OutBuffer, CurrentGain, TargetGain, delta, fade_len, Counter); } diff --git a/3rdparty/openal/core/mixer/mixer_neon.cpp b/3rdparty/openal/core/mixer/mixer_neon.cpp index ead775af1ebf..600c014b7ab9 100644 --- a/3rdparty/openal/core/mixer/mixer_neon.cpp +++ b/3rdparty/openal/core/mixer/mixer_neon.cpp @@ -2,15 +2,24 @@ #include -#include +#include +#include +#include #include +#include #include "alnumeric.h" +#include "alspan.h" #include "core/bsinc_defs.h" +#include "core/bufferline.h" #include "core/cubic_defs.h" +#include "core/mixer/hrtfdefs.h" +#include "core/resampler_limits.h" #include "defs.h" #include "hrtfbase.h" +#include "opthelpers.h" +struct CTag; struct NEONTag; struct LerpTag; struct CubicTag; @@ -22,6 +31,8 @@ struct FastBSincTag; #pragma GCC target("fpu=neon") #endif +using uint = unsigned int; + namespace { constexpr uint BSincPhaseDiffBits{MixerFracBits - BSincPhaseBits}; @@ -32,6 +43,19 @@ constexpr uint CubicPhaseDiffBits{MixerFracBits - CubicPhaseBits}; constexpr uint CubicPhaseDiffOne{1 << CubicPhaseDiffBits}; constexpr uint CubicPhaseDiffMask{CubicPhaseDiffOne - 1u}; +force_inline +void vtranspose4(float32x4_t &x0, float32x4_t &x1, float32x4_t &x2, float32x4_t &x3) noexcept +{ + float32x4x2_t t0_{vzipq_f32(x0, x2)}; + float32x4x2_t t1_{vzipq_f32(x1, x3)}; + float32x4x2_t u0_{vzipq_f32(t0_.val[0], t1_.val[0])}; + float32x4x2_t u1_{vzipq_f32(t0_.val[1], t1_.val[1])}; + x0 = u0_.val[0]; + x1 = u0_.val[1]; + x2 = u1_.val[0]; + x3 = u1_.val[1]; +} + inline float32x4_t set_f4(float l0, float l1, float l2, float l3) { float32x4_t ret{vmovq_n_f32(l0)}; @@ -41,60 +65,62 @@ inline float32x4_t set_f4(float l0, float l1, float l2, float l3) return ret; } -inline void ApplyCoeffs(float2 *RESTRICT Values, const size_t IrSize, const ConstHrirSpan Coeffs, - const float left, const float right) +inline void ApplyCoeffs(const al::span Values, const size_t IrSize, + const ConstHrirSpan Coeffs, const float left, const float right) { - float32x4_t leftright4; - { - float32x2_t leftright2{vmov_n_f32(left)}; - leftright2 = vset_lane_f32(right, leftright2, 1); - leftright4 = vcombine_f32(leftright2, leftright2); - } - ASSUME(IrSize >= MinIrLength); + ASSUME(IrSize <= HrirLength); + + auto dup_samples = [left,right]() -> float32x4_t + { + float32x2_t leftright2{vset_lane_f32(right, vmov_n_f32(left), 1)}; + return vcombine_f32(leftright2, leftright2); + }; + const auto leftright4 = dup_samples(); + + /* Using a loop here instead of std::transform since some builds seem to + * have an issue with accessing an array/span of float32x4_t. + */ for(size_t c{0};c < IrSize;c += 2) { - float32x4_t vals = vld1q_f32(&Values[c][0]); - float32x4_t coefs = vld1q_f32(&Coeffs[c][0]); - - vals = vmlaq_f32(vals, coefs, leftright4); - + auto vals = vld1q_f32(&Values[c][0]); + vals = vmlaq_f32(vals, vld1q_f32(&Coeffs[c][0]), leftright4); vst1q_f32(&Values[c][0], vals); } } -force_inline void MixLine(const al::span InSamples, float *RESTRICT dst, - float &CurrentGain, const float TargetGain, const float delta, const size_t min_len, - const size_t aligned_len, size_t Counter) +force_inline void MixLine(const al::span InSamples, const al::span dst, + float &CurrentGain, const float TargetGain, const float delta, const size_t fade_len, + const size_t realign_len, size_t Counter) { - float gain{CurrentGain}; - const float step{(TargetGain-gain) * delta}; + const auto step = float{(TargetGain-CurrentGain) * delta}; - size_t pos{0}; - if(!(std::abs(step) > std::numeric_limits::epsilon())) - gain = TargetGain; - else + auto pos = size_t{0}; + if(std::abs(step) > std::numeric_limits::epsilon()) { - float step_count{0.0f}; + const auto gain = float{CurrentGain}; + auto step_count = float{0.0f}; /* Mix with applying gain steps in aligned multiples of 4. */ - if(size_t todo{min_len >> 2}) + if(const size_t todo{fade_len >> 2}) { - const float32x4_t four4{vdupq_n_f32(4.0f)}; - const float32x4_t step4{vdupq_n_f32(step)}; - const float32x4_t gain4{vdupq_n_f32(gain)}; - float32x4_t step_count4{vdupq_n_f32(0.0f)}; - step_count4 = vsetq_lane_f32(1.0f, step_count4, 1); - step_count4 = vsetq_lane_f32(2.0f, step_count4, 2); - step_count4 = vsetq_lane_f32(3.0f, step_count4, 3); + const auto four4 = vdupq_n_f32(4.0f); + const auto step4 = vdupq_n_f32(step); + const auto gain4 = vdupq_n_f32(gain); + auto step_count4 = set_f4(0.0f, 1.0f, 2.0f, 3.0f); + + const auto in4 = al::span{reinterpret_cast(InSamples.data()), + InSamples.size()/4}.first(todo); + const auto out4 = al::span{reinterpret_cast(dst.data()), dst.size()/4}; + std::transform(in4.begin(), in4.end(), out4.begin(), out4.begin(), + [gain4,step4,four4,&step_count4](const float32x4_t val4, float32x4_t dry4) + { + /* dry += val * (gain + step*step_count) */ + dry4 = vmlaq_f32(dry4, val4, vmlaq_f32(gain4, step4, step_count4)); + step_count4 = vaddq_f32(step_count4, four4); + return dry4; + }); + pos += in4.size()*4; - do { - const float32x4_t val4 = vld1q_f32(&InSamples[pos]); - float32x4_t dry4 = vld1q_f32(&dst[pos]); - dry4 = vmlaq_f32(dry4, val4, vmlaq_f32(gain4, step4, step_count4)); - step_count4 = vaddq_f32(step_count4, four4); - vst1q_f32(&dst[pos], dry4); - pos += 4; - } while(--todo); /* NOTE: step_count4 now represents the next four counts after the * last four mixed samples, so the lowest element represents the * next step count to apply. @@ -102,152 +128,242 @@ force_inline void MixLine(const al::span InSamples, float *RESTRICT step_count = vgetq_lane_f32(step_count4, 0); } /* Mix with applying left over gain steps that aren't aligned multiples of 4. */ - for(size_t leftover{min_len&3};leftover;++pos,--leftover) + if(const size_t leftover{fade_len&3}) + { + const auto in = InSamples.subspan(pos, leftover); + const auto out = dst.subspan(pos); + + std::transform(in.begin(), in.end(), out.begin(), out.begin(), + [gain,step,&step_count](const float val, float dry) noexcept -> float + { + dry += val * (gain + step*step_count); + step_count += 1.0f; + return dry; + }); + pos += leftover; + } + if(pos < Counter) { - dst[pos] += InSamples[pos] * (gain + step*step_count); - step_count += 1.0f; + CurrentGain = gain + step*step_count; + return; } - if(pos == Counter) - gain = TargetGain; - else - gain += step*step_count; /* Mix until pos is aligned with 4 or the mix is done. */ - for(size_t leftover{aligned_len&3};leftover;++pos,--leftover) - dst[pos] += InSamples[pos] * gain; + if(const size_t leftover{realign_len&3}) + { + const auto in = InSamples.subspan(pos, leftover); + const auto out = dst.subspan(pos); + + std::transform(in.begin(), in.end(), out.begin(), out.begin(), + [TargetGain](const float val, const float dry) noexcept -> float + { return dry + val*TargetGain; }); + pos += leftover; + } } - CurrentGain = gain; + CurrentGain = TargetGain; - if(!(std::abs(gain) > GainSilenceThreshold)) + if(!(std::abs(TargetGain) > GainSilenceThreshold)) return; - if(size_t todo{(InSamples.size()-pos) >> 2}) + if(const size_t todo{(InSamples.size()-pos) >> 2}) { - const float32x4_t gain4 = vdupq_n_f32(gain); - do { - const float32x4_t val4 = vld1q_f32(&InSamples[pos]); - float32x4_t dry4 = vld1q_f32(&dst[pos]); - dry4 = vmlaq_f32(dry4, val4, gain4); - vst1q_f32(&dst[pos], dry4); - pos += 4; - } while(--todo); + const auto in4 = al::span{reinterpret_cast(InSamples.data()), + InSamples.size()/4}.last(todo); + const auto out = dst.subspan(pos); + const auto out4 = al::span{reinterpret_cast(out.data()), out.size()/4}; + + const auto gain4 = vdupq_n_f32(TargetGain); + std::transform(in4.begin(), in4.end(), out4.begin(), out4.begin(), + [gain4](const float32x4_t val4, const float32x4_t dry4) -> float32x4_t + { return vmlaq_f32(dry4, val4, gain4); }); + pos += in4.size()*4; + } + if(const size_t leftover{(InSamples.size()-pos)&3}) + { + const auto in = InSamples.last(leftover); + const auto out = dst.subspan(pos); + + std::transform(in.begin(), in.end(), out.begin(), out.begin(), + [TargetGain](const float val, const float dry) noexcept -> float + { return dry + val*TargetGain; }); } - for(size_t leftover{(InSamples.size()-pos)&3};leftover;++pos,--leftover) - dst[pos] += InSamples[pos] * gain; } } // namespace template<> -void Resample_(const InterpState*, const float *RESTRICT src, uint frac, +void Resample_(const InterpState*, const al::span src, uint frac, const uint increment, const al::span dst) { ASSUME(frac < MixerFracOne); - const int32x4_t increment4 = vdupq_n_s32(static_cast(increment*4)); + const uint32x4_t increment4 = vdupq_n_u32(increment*4u); const float32x4_t fracOne4 = vdupq_n_f32(1.0f/MixerFracOne); - const int32x4_t fracMask4 = vdupq_n_s32(MixerFracMask); - alignas(16) uint pos_[4], frac_[4]; - int32x4_t pos4, frac4; + const uint32x4_t fracMask4 = vdupq_n_u32(MixerFracMask); - InitPosArrays(frac, increment, frac_, pos_); - frac4 = vld1q_s32(reinterpret_cast(frac_)); - pos4 = vld1q_s32(reinterpret_cast(pos_)); + alignas(16) std::array pos_{}, frac_{}; + InitPosArrays(MaxResamplerEdge, frac, increment, al::span{frac_}, al::span{pos_}); + uint32x4_t frac4 = vld1q_u32(frac_.data()); + uint32x4_t pos4 = vld1q_u32(pos_.data()); - auto dst_iter = dst.begin(); - for(size_t todo{dst.size()>>2};todo;--todo) + auto vecout = al::span{reinterpret_cast(dst.data()), dst.size()/4}; + std::generate(vecout.begin(), vecout.end(), [=,&pos4,&frac4]() -> float32x4_t { - const int pos0{vgetq_lane_s32(pos4, 0)}; - const int pos1{vgetq_lane_s32(pos4, 1)}; - const int pos2{vgetq_lane_s32(pos4, 2)}; - const int pos3{vgetq_lane_s32(pos4, 3)}; + const uint pos0{vgetq_lane_u32(pos4, 0)}; + const uint pos1{vgetq_lane_u32(pos4, 1)}; + const uint pos2{vgetq_lane_u32(pos4, 2)}; + const uint pos3{vgetq_lane_u32(pos4, 3)}; + ASSUME(pos0 <= pos1); ASSUME(pos1 <= pos2); ASSUME(pos2 <= pos3); const float32x4_t val1{set_f4(src[pos0], src[pos1], src[pos2], src[pos3])}; - const float32x4_t val2{set_f4(src[pos0+1], src[pos1+1], src[pos2+1], src[pos3+1])}; + const float32x4_t val2{set_f4(src[pos0+1_uz], src[pos1+1_uz], src[pos2+1_uz], src[pos3+1_uz])}; /* val1 + (val2-val1)*mu */ const float32x4_t r0{vsubq_f32(val2, val1)}; - const float32x4_t mu{vmulq_f32(vcvtq_f32_s32(frac4), fracOne4)}; + const float32x4_t mu{vmulq_f32(vcvtq_f32_u32(frac4), fracOne4)}; const float32x4_t out{vmlaq_f32(val1, mu, r0)}; - vst1q_f32(dst_iter, out); - dst_iter += 4; - - frac4 = vaddq_s32(frac4, increment4); - pos4 = vaddq_s32(pos4, vshrq_n_s32(frac4, MixerFracBits)); - frac4 = vandq_s32(frac4, fracMask4); - } + frac4 = vaddq_u32(frac4, increment4); + pos4 = vaddq_u32(pos4, vshrq_n_u32(frac4, MixerFracBits)); + frac4 = vandq_u32(frac4, fracMask4); + return out; + }); if(size_t todo{dst.size()&3}) { - src += static_cast(vgetq_lane_s32(pos4, 0)); - frac = static_cast(vgetq_lane_s32(frac4, 0)); + auto pos = size_t{vgetq_lane_u32(pos4, 0)}; + frac = vgetq_lane_u32(frac4, 0); - do { - *(dst_iter++) = lerpf(src[0], src[1], static_cast(frac) * (1.0f/MixerFracOne)); + const auto out = dst.last(todo); + std::generate(out.begin(), out.end(), [&pos,&frac,src,increment] + { + const float output{lerpf(src[pos+0], src[pos+1], + static_cast(frac) * (1.0f/MixerFracOne))}; frac += increment; - src += frac>>MixerFracBits; + pos += frac>>MixerFracBits; frac &= MixerFracMask; - } while(--todo); + return output; + }); } } template<> -void Resample_(const InterpState *state, const float *RESTRICT src, uint frac, - const uint increment, const al::span dst) +void Resample_(const InterpState *state, const al::span src, + uint frac, const uint increment, const al::span dst) { ASSUME(frac < MixerFracOne); - const CubicCoefficients *RESTRICT filter = al::assume_aligned<16>(state->cubic.filter); + const auto filter = std::get(*state).filter; - src -= 1; - for(float &out_sample : dst) + const uint32x4_t increment4{vdupq_n_u32(increment*4u)}; + const uint32x4_t fracMask4{vdupq_n_u32(MixerFracMask)}; + const float32x4_t fracDiffOne4{vdupq_n_f32(1.0f/CubicPhaseDiffOne)}; + const uint32x4_t fracDiffMask4{vdupq_n_u32(CubicPhaseDiffMask)}; + + alignas(16) std::array pos_{}, frac_{}; + InitPosArrays(MaxResamplerEdge-1, frac, increment, al::span{frac_}, al::span{pos_}); + uint32x4_t frac4{vld1q_u32(frac_.data())}; + uint32x4_t pos4{vld1q_u32(pos_.data())}; + + auto vecout = al::span{reinterpret_cast(dst.data()), dst.size()/4}; + std::generate(vecout.begin(), vecout.end(), [=,&pos4,&frac4] + { + const uint pos0{vgetq_lane_u32(pos4, 0)}; + const uint pos1{vgetq_lane_u32(pos4, 1)}; + const uint pos2{vgetq_lane_u32(pos4, 2)}; + const uint pos3{vgetq_lane_u32(pos4, 3)}; + ASSUME(pos0 <= pos1); ASSUME(pos1 <= pos2); ASSUME(pos2 <= pos3); + const float32x4_t val0{vld1q_f32(&src[pos0])}; + const float32x4_t val1{vld1q_f32(&src[pos1])}; + const float32x4_t val2{vld1q_f32(&src[pos2])}; + const float32x4_t val3{vld1q_f32(&src[pos3])}; + + const uint32x4_t pi4{vshrq_n_u32(frac4, CubicPhaseDiffBits)}; + const uint pi0{vgetq_lane_u32(pi4, 0)}; ASSUME(pi0 < CubicPhaseCount); + const uint pi1{vgetq_lane_u32(pi4, 1)}; ASSUME(pi1 < CubicPhaseCount); + const uint pi2{vgetq_lane_u32(pi4, 2)}; ASSUME(pi2 < CubicPhaseCount); + const uint pi3{vgetq_lane_u32(pi4, 3)}; ASSUME(pi3 < CubicPhaseCount); + + const float32x4_t pf4{vmulq_f32(vcvtq_f32_u32(vandq_u32(frac4, fracDiffMask4)), + fracDiffOne4)}; + + float32x4_t r0{vmulq_f32(val0, + vmlaq_f32(vld1q_f32(filter[pi0].mCoeffs.data()), vdupq_lane_f32(vget_low_f32(pf4), 0), + vld1q_f32(filter[pi0].mDeltas.data())))}; + float32x4_t r1{vmulq_f32(val1, + vmlaq_f32(vld1q_f32(filter[pi1].mCoeffs.data()), vdupq_lane_f32(vget_low_f32(pf4), 1), + vld1q_f32(filter[pi1].mDeltas.data())))}; + float32x4_t r2{vmulq_f32(val2, + vmlaq_f32(vld1q_f32(filter[pi2].mCoeffs.data()), vdupq_lane_f32(vget_high_f32(pf4), 0), + vld1q_f32(filter[pi2].mDeltas.data())))}; + float32x4_t r3{vmulq_f32(val3, + vmlaq_f32(vld1q_f32(filter[pi3].mCoeffs.data()), vdupq_lane_f32(vget_high_f32(pf4), 1), + vld1q_f32(filter[pi3].mDeltas.data())))}; + + vtranspose4(r0, r1, r2, r3); + r0 = vaddq_f32(vaddq_f32(r0, r1), vaddq_f32(r2, r3)); + + frac4 = vaddq_u32(frac4, increment4); + pos4 = vaddq_u32(pos4, vshrq_n_u32(frac4, MixerFracBits)); + frac4 = vandq_u32(frac4, fracMask4); + return r0; + }); + + if(const size_t todo{dst.size()&3}) { - const uint pi{frac >> CubicPhaseDiffBits}; - const float pf{static_cast(frac&CubicPhaseDiffMask) * (1.0f/CubicPhaseDiffOne)}; - const float32x4_t pf4{vdupq_n_f32(pf)}; + auto pos = size_t{vgetq_lane_u32(pos4, 0)}; + frac = vgetq_lane_u32(frac4, 0); - /* Apply the phase interpolated filter. */ + auto out = dst.last(todo); + std::generate(out.begin(), out.end(), [&pos,&frac,src,increment,filter] + { + const uint pi{frac >> CubicPhaseDiffBits}; ASSUME(pi < CubicPhaseCount); + const float pf{static_cast(frac&CubicPhaseDiffMask) * (1.0f/CubicPhaseDiffOne)}; + const float32x4_t pf4{vdupq_n_f32(pf)}; - /* f = fil + pf*phd */ - const float32x4_t f4 = vmlaq_f32(vld1q_f32(filter[pi].mCoeffs), pf4, - vld1q_f32(filter[pi].mDeltas)); - /* r = f*src */ - float32x4_t r4{vmulq_f32(f4, vld1q_f32(src))}; + const float32x4_t f4{vmlaq_f32(vld1q_f32(filter[pi].mCoeffs.data()), pf4, + vld1q_f32(filter[pi].mDeltas.data()))}; + float32x4_t r4{vmulq_f32(f4, vld1q_f32(&src[pos]))}; - r4 = vaddq_f32(r4, vrev64q_f32(r4)); - out_sample = vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0); + r4 = vaddq_f32(r4, vrev64q_f32(r4)); + const float output{vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0)}; - frac += increment; - src += frac>>MixerFracBits; - frac &= MixerFracMask; + frac += increment; + pos += frac>>MixerFracBits; + frac &= MixerFracMask; + return output; + }); } } template<> -void Resample_(const InterpState *state, const float *RESTRICT src, uint frac, - const uint increment, const al::span dst) +void Resample_(const InterpState *state, const al::span src, + uint frac, const uint increment, const al::span dst) { - const float *const filter{state->bsinc.filter}; - const float32x4_t sf4{vdupq_n_f32(state->bsinc.sf)}; - const size_t m{state->bsinc.m}; + const auto &bsinc = std::get(*state); + const auto sf4 = vdupq_n_f32(bsinc.sf); + const auto m = size_t{bsinc.m}; ASSUME(m > 0); + ASSUME(m <= MaxResamplerPadding); ASSUME(frac < MixerFracOne); - src -= state->bsinc.l; - for(float &out_sample : dst) + const auto filter = bsinc.filter.first(4_uz*BSincPhaseCount*m); + + ASSUME(bsinc.l <= MaxResamplerEdge); + auto pos = size_t{MaxResamplerEdge-bsinc.l}; + std::generate(dst.begin(), dst.end(), [&pos,&frac,src,increment,sf4,m,filter]() -> float { // Calculate the phase index and factor. - const uint pi{frac >> BSincPhaseDiffBits}; + const uint pi{frac >> BSincPhaseDiffBits}; ASSUME(pi < BSincPhaseCount); const float pf{static_cast(frac&BSincPhaseDiffMask) * (1.0f/BSincPhaseDiffOne)}; // Apply the scale and phase interpolated filter. float32x4_t r4{vdupq_n_f32(0.0f)}; { const float32x4_t pf4{vdupq_n_f32(pf)}; - const float *RESTRICT fil{filter + m*pi*2}; - const float *RESTRICT phd{fil + m}; - const float *RESTRICT scd{fil + BSincPhaseCount*2*m}; - const float *RESTRICT spd{scd + m}; + const auto fil = filter.subspan(2_uz*pi*m); + const auto phd = fil.subspan(m); + const auto scd = fil.subspan(2_uz*BSincPhaseCount*m); + const auto spd = scd.subspan(m); size_t td{m >> 2}; size_t j{0u}; @@ -257,41 +373,46 @@ void Resample_(const InterpState *state, const float *RESTRICT vmlaq_f32(vld1q_f32(&fil[j]), sf4, vld1q_f32(&scd[j])), pf4, vmlaq_f32(vld1q_f32(&phd[j]), sf4, vld1q_f32(&spd[j]))); /* r += f*src */ - r4 = vmlaq_f32(r4, f4, vld1q_f32(&src[j])); + r4 = vmlaq_f32(r4, f4, vld1q_f32(&src[pos+j])); j += 4; } while(--td); } r4 = vaddq_f32(r4, vrev64q_f32(r4)); - out_sample = vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0); + const float output{vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0)}; frac += increment; - src += frac>>MixerFracBits; + pos += frac>>MixerFracBits; frac &= MixerFracMask; - } + return output; + }); } template<> -void Resample_(const InterpState *state, const float *RESTRICT src, uint frac, - const uint increment, const al::span dst) +void Resample_(const InterpState *state, const al::span src, + uint frac, const uint increment, const al::span dst) { - const float *const filter{state->bsinc.filter}; - const size_t m{state->bsinc.m}; + const auto &bsinc = std::get(*state); + const auto m = size_t{bsinc.m}; ASSUME(m > 0); + ASSUME(m <= MaxResamplerPadding); ASSUME(frac < MixerFracOne); - src -= state->bsinc.l; - for(float &out_sample : dst) + const auto filter = bsinc.filter.first(2_uz*BSincPhaseCount*m); + + ASSUME(bsinc.l <= MaxResamplerEdge); + auto pos = size_t{MaxResamplerEdge-bsinc.l}; + std::generate(dst.begin(), dst.end(), [&pos,&frac,src,increment,m,filter]() -> float { // Calculate the phase index and factor. - const uint pi{frac >> BSincPhaseDiffBits}; + const uint pi{frac >> BSincPhaseDiffBits}; ASSUME(pi < BSincPhaseCount); const float pf{static_cast(frac&BSincPhaseDiffMask) * (1.0f/BSincPhaseDiffOne)}; // Apply the phase interpolated filter. float32x4_t r4{vdupq_n_f32(0.0f)}; { const float32x4_t pf4{vdupq_n_f32(pf)}; - const float *RESTRICT fil{filter + m*pi*2}; - const float *RESTRICT phd{fil + m}; + const auto fil = filter.subspan(2_uz*pi*m); + const auto phd = fil.subspan(m); size_t td{m >> 2}; size_t j{0u}; @@ -299,64 +420,75 @@ void Resample_(const InterpState *state, const float *REST /* f = fil + pf*phd */ const float32x4_t f4 = vmlaq_f32(vld1q_f32(&fil[j]), pf4, vld1q_f32(&phd[j])); /* r += f*src */ - r4 = vmlaq_f32(r4, f4, vld1q_f32(&src[j])); + r4 = vmlaq_f32(r4, f4, vld1q_f32(&src[pos+j])); j += 4; } while(--td); } r4 = vaddq_f32(r4, vrev64q_f32(r4)); - out_sample = vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0); + const float output{vget_lane_f32(vadd_f32(vget_low_f32(r4), vget_high_f32(r4)), 0)}; frac += increment; - src += frac>>MixerFracBits; + pos += frac>>MixerFracBits; frac &= MixerFracMask; - } + return output; + }); } template<> -void MixHrtf_(const float *InSamples, float2 *AccumSamples, const uint IrSize, - const MixHrtfFilter *hrtfparams, const size_t BufferSize) -{ MixHrtfBase(InSamples, AccumSamples, IrSize, hrtfparams, BufferSize); } +void MixHrtf_(const al::span InSamples, const al::span AccumSamples, + const uint IrSize, const MixHrtfFilter *hrtfparams, const size_t SamplesToDo) +{ MixHrtfBase(InSamples, AccumSamples, IrSize, hrtfparams, SamplesToDo); } template<> -void MixHrtfBlend_(const float *InSamples, float2 *AccumSamples, const uint IrSize, - const HrtfFilter *oldparams, const MixHrtfFilter *newparams, const size_t BufferSize) +void MixHrtfBlend_(const al::span InSamples, + const al::span AccumSamples, const uint IrSize, const HrtfFilter *oldparams, + const MixHrtfFilter *newparams, const size_t SamplesToDo) { MixHrtfBlendBase(InSamples, AccumSamples, IrSize, oldparams, newparams, - BufferSize); + SamplesToDo); } template<> void MixDirectHrtf_(const FloatBufferSpan LeftOut, const FloatBufferSpan RightOut, - const al::span InSamples, float2 *AccumSamples, - float *TempBuf, HrtfChannelState *ChanState, const size_t IrSize, const size_t BufferSize) + const al::span InSamples, const al::span AccumSamples, + const al::span TempBuf, const al::span ChanState, + const size_t IrSize, const size_t SamplesToDo) { MixDirectHrtfBase(LeftOut, RightOut, InSamples, AccumSamples, TempBuf, ChanState, - IrSize, BufferSize); + IrSize, SamplesToDo); } template<> -void Mix_(const al::span InSamples, const al::span OutBuffer, - float *CurrentGains, const float *TargetGains, const size_t Counter, const size_t OutPos) +void Mix_(const al::span InSamples,const al::span OutBuffer, + const al::span CurrentGains, const al::span TargetGains, + const size_t Counter, const size_t OutPos) { + if((OutPos&3) != 0) UNLIKELY + return Mix_(InSamples, OutBuffer, CurrentGains, TargetGains, Counter, OutPos); + const float delta{(Counter > 0) ? 1.0f / static_cast(Counter) : 0.0f}; - const auto min_len = minz(Counter, InSamples.size()); - const auto aligned_len = minz((min_len+3) & ~3_uz, InSamples.size()) - min_len; + const auto fade_len = std::min(Counter, InSamples.size()); + const auto realign_len = std::min((fade_len+3_uz) & ~3_uz, InSamples.size()) - fade_len; + auto curgains = CurrentGains.begin(); + auto targetgains = TargetGains.cbegin(); for(FloatBufferLine &output : OutBuffer) - MixLine(InSamples, al::assume_aligned<16>(output.data()+OutPos), *CurrentGains++, - *TargetGains++, delta, min_len, aligned_len, Counter); + MixLine(InSamples, al::span{output}.subspan(OutPos), *curgains++, *targetgains++, delta, + fade_len, realign_len, Counter); } template<> -void Mix_(const al::span InSamples, float *OutBuffer, float &CurrentGain, - const float TargetGain, const size_t Counter) +void Mix_(const al::span InSamples, const al::span OutBuffer, + float &CurrentGain, const float TargetGain, const size_t Counter) { + if((reinterpret_cast(OutBuffer.data())&15) != 0) UNLIKELY + return Mix_(InSamples, OutBuffer, CurrentGain, TargetGain, Counter); + const float delta{(Counter > 0) ? 1.0f / static_cast(Counter) : 0.0f}; - const auto min_len = minz(Counter, InSamples.size()); - const auto aligned_len = minz((min_len+3) & ~3_uz, InSamples.size()) - min_len; + const auto fade_len = std::min(Counter, InSamples.size()); + const auto realign_len = std::min((fade_len+3_uz) & ~3_uz, InSamples.size()) - fade_len; - MixLine(InSamples, al::assume_aligned<16>(OutBuffer), CurrentGain, TargetGain, delta, min_len, - aligned_len, Counter); + MixLine(InSamples, OutBuffer, CurrentGain, TargetGain, delta, fade_len, realign_len, Counter); } diff --git a/3rdparty/openal/core/mixer/mixer_sse.cpp b/3rdparty/openal/core/mixer/mixer_sse.cpp index 70f77c14cf91..097cd9333087 100644 --- a/3rdparty/openal/core/mixer/mixer_sse.cpp +++ b/3rdparty/openal/core/mixer/mixer_sse.cpp @@ -1,16 +1,27 @@ #include "config.h" +#include #include -#include +#include +#include +#include +#include #include +#include #include "alnumeric.h" +#include "alspan.h" #include "core/bsinc_defs.h" +#include "core/bufferline.h" #include "core/cubic_defs.h" +#include "core/mixer/hrtfdefs.h" +#include "core/resampler_limits.h" #include "defs.h" #include "hrtfbase.h" +#include "opthelpers.h" +struct CTag; struct SSETag; struct CubicTag; struct BSincTag; @@ -31,42 +42,48 @@ constexpr uint CubicPhaseDiffBits{MixerFracBits - CubicPhaseBits}; constexpr uint CubicPhaseDiffOne{1 << CubicPhaseDiffBits}; constexpr uint CubicPhaseDiffMask{CubicPhaseDiffOne - 1u}; -#define MLA4(x, y, z) _mm_add_ps(x, _mm_mul_ps(y, z)) +force_inline __m128 vmadd(const __m128 x, const __m128 y, const __m128 z) noexcept +{ return _mm_add_ps(x, _mm_mul_ps(y, z)); } -inline void ApplyCoeffs(float2 *RESTRICT Values, const size_t IrSize, const ConstHrirSpan Coeffs, - const float left, const float right) +inline void ApplyCoeffs(const al::span Values, const size_t IrSize, + const ConstHrirSpan Coeffs, const float left, const float right) { - const __m128 lrlr{_mm_setr_ps(left, right, left, right)}; - ASSUME(IrSize >= MinIrLength); + ASSUME(IrSize <= HrirLength); + const auto lrlr = _mm_setr_ps(left, right, left, right); + /* Round up the IR size to a multiple of 2 for SIMD (2 IRs for 2 channels + * is 4 floats), to avoid cutting the last sample for odd IR counts. The + * underlying HRIR is a fixed-size multiple of 2, any extra samples are + * either 0 (silence) or more IR samples that get applied for "free". + */ + const auto count4 = size_t{(IrSize+1) >> 1}; + /* This isn't technically correct to test alignment, but it's true for * systems that support SSE, which is the only one that needs to know the * alignment of Values (which alternates between 8- and 16-byte aligned). */ - if(!(reinterpret_cast(Values)&15)) + if(!(reinterpret_cast(Values.data())&15)) { - for(size_t i{0};i < IrSize;i += 2) - { - const __m128 coeffs{_mm_load_ps(Coeffs[i].data())}; - __m128 vals{_mm_load_ps(Values[i].data())}; - vals = MLA4(vals, lrlr, coeffs); - _mm_store_ps(Values[i].data(), vals); - } + const auto vals4 = al::span{reinterpret_cast<__m128*>(Values[0].data()), count4}; + const auto coeffs4 = al::span{reinterpret_cast(Coeffs[0].data()), count4}; + + std::transform(vals4.cbegin(), vals4.cend(), coeffs4.cbegin(), vals4.begin(), + [lrlr](const __m128 &val, const __m128 &coeff) -> __m128 + { return vmadd(val, coeff, lrlr); }); } else { - __m128 imp0, imp1; - __m128 coeffs{_mm_load_ps(Coeffs[0].data())}; - __m128 vals{_mm_loadl_pi(_mm_setzero_ps(), reinterpret_cast<__m64*>(Values[0].data()))}; - imp0 = _mm_mul_ps(lrlr, coeffs); + auto coeffs = _mm_load_ps(Coeffs[0].data()); + auto vals = _mm_loadl_pi(_mm_setzero_ps(), reinterpret_cast<__m64*>(Values[0].data())); + auto imp0 = _mm_mul_ps(lrlr, coeffs); vals = _mm_add_ps(imp0, vals); _mm_storel_pi(reinterpret_cast<__m64*>(Values[0].data()), vals); - size_t td{((IrSize+1)>>1) - 1}; + size_t td{count4 - 1}; size_t i{1}; do { coeffs = _mm_load_ps(Coeffs[i+1].data()); vals = _mm_load_ps(Values[i].data()); - imp1 = _mm_mul_ps(lrlr, coeffs); + const auto imp1 = _mm_mul_ps(lrlr, coeffs); imp0 = _mm_shuffle_ps(imp0, imp1, _MM_SHUFFLE(1, 0, 3, 2)); vals = _mm_add_ps(imp0, vals); _mm_store_ps(Values[i].data(), vals); @@ -80,37 +97,38 @@ inline void ApplyCoeffs(float2 *RESTRICT Values, const size_t IrSize, const Cons } } -force_inline void MixLine(const al::span InSamples, float *RESTRICT dst, - float &CurrentGain, const float TargetGain, const float delta, const size_t min_len, - const size_t aligned_len, size_t Counter) +force_inline void MixLine(const al::span InSamples, const al::span dst, + float &CurrentGain, const float TargetGain, const float delta, const size_t fade_len, + const size_t realign_len, size_t Counter) { - float gain{CurrentGain}; - const float step{(TargetGain-gain) * delta}; + const auto step = float{(TargetGain-CurrentGain) * delta}; size_t pos{0}; - if(!(std::abs(step) > std::numeric_limits::epsilon())) - gain = TargetGain; - else + if(std::abs(step) > std::numeric_limits::epsilon()) { - float step_count{0.0f}; + const auto gain = CurrentGain; + auto step_count = 0.0f; /* Mix with applying gain steps in aligned multiples of 4. */ - if(size_t todo{min_len >> 2}) + if(const size_t todo{fade_len >> 2}) { - const __m128 four4{_mm_set1_ps(4.0f)}; - const __m128 step4{_mm_set1_ps(step)}; - const __m128 gain4{_mm_set1_ps(gain)}; - __m128 step_count4{_mm_setr_ps(0.0f, 1.0f, 2.0f, 3.0f)}; - do { - const __m128 val4{_mm_load_ps(&InSamples[pos])}; - __m128 dry4{_mm_load_ps(&dst[pos])}; - - /* dry += val * (gain + step*step_count) */ - dry4 = MLA4(dry4, val4, MLA4(gain4, step4, step_count4)); + const auto four4 = _mm_set1_ps(4.0f); + const auto step4 = _mm_set1_ps(step); + const auto gain4 = _mm_set1_ps(gain); + auto step_count4 = _mm_setr_ps(0.0f, 1.0f, 2.0f, 3.0f); + + const auto in4 = al::span{reinterpret_cast(InSamples.data()), + InSamples.size()/4}.first(todo); + const auto out4 = al::span{reinterpret_cast<__m128*>(dst.data()), dst.size()/4}; + std::transform(in4.begin(), in4.end(), out4.begin(), out4.begin(), + [gain4,step4,four4,&step_count4](const __m128 val4, __m128 dry4) -> __m128 + { + /* dry += val * (gain + step*step_count) */ + dry4 = vmadd(dry4, val4, vmadd(gain4, step4, step_count4)); + step_count4 = _mm_add_ps(step_count4, four4); + return dry4; + }); + pos += in4.size()*4; - _mm_store_ps(&dst[pos], dry4); - step_count4 = _mm_add_ps(step_count4, four4); - pos += 4; - } while(--todo); /* NOTE: step_count4 now represents the next four counts after the * last four mixed samples, so the lowest element represents the * next step count to apply. @@ -118,210 +136,258 @@ force_inline void MixLine(const al::span InSamples, float *RESTRICT step_count = _mm_cvtss_f32(step_count4); } /* Mix with applying left over gain steps that aren't aligned multiples of 4. */ - for(size_t leftover{min_len&3};leftover;++pos,--leftover) + if(const size_t leftover{fade_len&3}) { - dst[pos] += InSamples[pos] * (gain + step*step_count); - step_count += 1.0f; + const auto in = InSamples.subspan(pos, leftover); + const auto out = dst.subspan(pos); + + std::transform(in.begin(), in.end(), out.begin(), out.begin(), + [gain,step,&step_count](const float val, float dry) noexcept -> float + { + dry += val * (gain + step*step_count); + step_count += 1.0f; + return dry; + }); + pos += leftover; + } + if(pos < Counter) + { + CurrentGain = gain + step*step_count; + return; } - if(pos == Counter) - gain = TargetGain; - else - gain += step*step_count; /* Mix until pos is aligned with 4 or the mix is done. */ - for(size_t leftover{aligned_len&3};leftover;++pos,--leftover) - dst[pos] += InSamples[pos] * gain; + if(const size_t leftover{realign_len&3}) + { + const auto in = InSamples.subspan(pos, leftover); + const auto out = dst.subspan(pos); + + std::transform(in.begin(), in.end(), out.begin(), out.begin(), + [TargetGain](const float val, const float dry) noexcept -> float + { return dry + val*TargetGain; }); + pos += leftover; + } } - CurrentGain = gain; + CurrentGain = TargetGain; - if(!(std::abs(gain) > GainSilenceThreshold)) + if(!(std::abs(TargetGain) > GainSilenceThreshold)) return; if(size_t todo{(InSamples.size()-pos) >> 2}) { - const __m128 gain4{_mm_set1_ps(gain)}; - do { - const __m128 val4{_mm_load_ps(&InSamples[pos])}; - __m128 dry4{_mm_load_ps(&dst[pos])}; - dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4)); - _mm_store_ps(&dst[pos], dry4); - pos += 4; - } while(--todo); + const auto in4 = al::span{reinterpret_cast(InSamples.data()), + InSamples.size()/4}.last(todo); + const auto out = dst.subspan(pos); + const auto out4 = al::span{reinterpret_cast<__m128*>(out.data()), out.size()/4}; + + const auto gain4 = _mm_set1_ps(TargetGain); + std::transform(in4.begin(), in4.end(), out4.begin(), out4.begin(), + [gain4](const __m128 val4, const __m128 dry4) -> __m128 + { return vmadd(dry4, val4, gain4); }); + pos += in4.size()*4; + } + if(const size_t leftover{(InSamples.size()-pos)&3}) + { + const auto in = InSamples.last(leftover); + const auto out = dst.subspan(pos); + + std::transform(in.begin(), in.end(), out.begin(), out.begin(), + [TargetGain](const float val, const float dry) noexcept -> float + { return dry + val*TargetGain; }); } - for(size_t leftover{(InSamples.size()-pos)&3};leftover;++pos,--leftover) - dst[pos] += InSamples[pos] * gain; } } // namespace template<> -void Resample_(const InterpState *state, const float *RESTRICT src, uint frac, - const uint increment, const al::span dst) +void Resample_(const InterpState *state, const al::span src, + uint frac, const uint increment, const al::span dst) { ASSUME(frac < MixerFracOne); - const CubicCoefficients *RESTRICT filter = al::assume_aligned<16>(state->cubic.filter); + const auto filter = std::get(*state).filter; - src -= 1; - for(float &out_sample : dst) + size_t pos{MaxResamplerEdge-1}; + std::generate(dst.begin(), dst.end(), [&pos,&frac,src,increment,filter]() -> float { - const uint pi{frac >> CubicPhaseDiffBits}; + const uint pi{frac >> CubicPhaseDiffBits}; ASSUME(pi < CubicPhaseCount); const float pf{static_cast(frac&CubicPhaseDiffMask) * (1.0f/CubicPhaseDiffOne)}; const __m128 pf4{_mm_set1_ps(pf)}; /* Apply the phase interpolated filter. */ /* f = fil + pf*phd */ - const __m128 f4 = MLA4(_mm_load_ps(filter[pi].mCoeffs), pf4, - _mm_load_ps(filter[pi].mDeltas)); + const __m128 f4 = vmadd(_mm_load_ps(filter[pi].mCoeffs.data()), pf4, + _mm_load_ps(filter[pi].mDeltas.data())); /* r = f*src */ - __m128 r4{_mm_mul_ps(f4, _mm_loadu_ps(src))}; + __m128 r4{_mm_mul_ps(f4, _mm_loadu_ps(&src[pos]))}; r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3))); r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4)); - out_sample = _mm_cvtss_f32(r4); + const float output{_mm_cvtss_f32(r4)}; frac += increment; - src += frac>>MixerFracBits; + pos += frac>>MixerFracBits; frac &= MixerFracMask; - } + return output; + }); } template<> -void Resample_(const InterpState *state, const float *RESTRICT src, uint frac, - const uint increment, const al::span dst) +void Resample_(const InterpState *state, const al::span src, + uint frac, const uint increment, const al::span dst) { - const float *const filter{state->bsinc.filter}; - const __m128 sf4{_mm_set1_ps(state->bsinc.sf)}; - const size_t m{state->bsinc.m}; + const auto &bsinc = std::get(*state); + const auto sf4 = _mm_set1_ps(bsinc.sf); + const auto m = size_t{bsinc.m}; ASSUME(m > 0); + ASSUME(m <= MaxResamplerPadding); ASSUME(frac < MixerFracOne); - src -= state->bsinc.l; - for(float &out_sample : dst) + const auto filter = bsinc.filter.first(4_uz*BSincPhaseCount*m); + + ASSUME(bsinc.l <= MaxResamplerEdge); + auto pos = size_t{MaxResamplerEdge-bsinc.l}; + std::generate(dst.begin(), dst.end(), [&pos,&frac,src,increment,sf4,m,filter]() -> float { // Calculate the phase index and factor. - const uint pi{frac >> BSincPhaseDiffBits}; + const size_t pi{frac >> BSincPhaseDiffBits}; ASSUME(pi < BSincPhaseCount); const float pf{static_cast(frac&BSincPhaseDiffMask) * (1.0f/BSincPhaseDiffOne)}; // Apply the scale and phase interpolated filter. - __m128 r4{_mm_setzero_ps()}; + auto r4 = _mm_setzero_ps(); { - const __m128 pf4{_mm_set1_ps(pf)}; - const float *RESTRICT fil{filter + m*pi*2}; - const float *RESTRICT phd{fil + m}; - const float *RESTRICT scd{fil + BSincPhaseCount*2*m}; - const float *RESTRICT spd{scd + m}; - size_t td{m >> 2}; - size_t j{0u}; + const auto pf4 = _mm_set1_ps(pf); + const auto fil = filter.subspan(2_uz*pi*m); + const auto phd = fil.subspan(m); + const auto scd = fil.subspan(2_uz*BSincPhaseCount*m); + const auto spd = scd.subspan(m); + auto td = size_t{m >> 2}; + auto j = size_t{0}; do { /* f = ((fil + sf*scd) + pf*(phd + sf*spd)) */ - const __m128 f4 = MLA4( - MLA4(_mm_load_ps(&fil[j]), sf4, _mm_load_ps(&scd[j])), - pf4, MLA4(_mm_load_ps(&phd[j]), sf4, _mm_load_ps(&spd[j]))); + const __m128 f4 = vmadd( + vmadd(_mm_load_ps(&fil[j]), sf4, _mm_load_ps(&scd[j])), + pf4, vmadd(_mm_load_ps(&phd[j]), sf4, _mm_load_ps(&spd[j]))); /* r += f*src */ - r4 = MLA4(r4, f4, _mm_loadu_ps(&src[j])); + r4 = vmadd(r4, f4, _mm_loadu_ps(&src[pos+j])); j += 4; } while(--td); } r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3))); r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4)); - out_sample = _mm_cvtss_f32(r4); + const auto output = _mm_cvtss_f32(r4); frac += increment; - src += frac>>MixerFracBits; + pos += frac>>MixerFracBits; frac &= MixerFracMask; - } + return output; + }); } template<> -void Resample_(const InterpState *state, const float *RESTRICT src, uint frac, - const uint increment, const al::span dst) +void Resample_(const InterpState *state, const al::span src, + uint frac, const uint increment, const al::span dst) { - const float *const filter{state->bsinc.filter}; - const size_t m{state->bsinc.m}; + const auto &bsinc = std::get(*state); + const auto m = size_t{bsinc.m}; ASSUME(m > 0); + ASSUME(m <= MaxResamplerPadding); ASSUME(frac < MixerFracOne); - src -= state->bsinc.l; - for(float &out_sample : dst) + const auto filter = bsinc.filter.first(2_uz*m*BSincPhaseCount); + + ASSUME(bsinc.l <= MaxResamplerEdge); + size_t pos{MaxResamplerEdge-bsinc.l}; + std::generate(dst.begin(), dst.end(), [&pos,&frac,src,increment,filter,m]() -> float { // Calculate the phase index and factor. - const uint pi{frac >> BSincPhaseDiffBits}; + const size_t pi{frac >> BSincPhaseDiffBits}; ASSUME(pi < BSincPhaseCount); const float pf{static_cast(frac&BSincPhaseDiffMask) * (1.0f/BSincPhaseDiffOne)}; // Apply the phase interpolated filter. - __m128 r4{_mm_setzero_ps()}; + auto r4 = _mm_setzero_ps(); { - const __m128 pf4{_mm_set1_ps(pf)}; - const float *RESTRICT fil{filter + m*pi*2}; - const float *RESTRICT phd{fil + m}; - size_t td{m >> 2}; - size_t j{0u}; + const auto pf4 = _mm_set1_ps(pf); + const auto fil = filter.subspan(2_uz*m*pi); + const auto phd = fil.subspan(m); + auto td = size_t{m >> 2}; + auto j = size_t{0}; do { /* f = fil + pf*phd */ - const __m128 f4 = MLA4(_mm_load_ps(&fil[j]), pf4, _mm_load_ps(&phd[j])); + const auto f4 = vmadd(_mm_load_ps(&fil[j]), pf4, _mm_load_ps(&phd[j])); /* r += f*src */ - r4 = MLA4(r4, f4, _mm_loadu_ps(&src[j])); + r4 = vmadd(r4, f4, _mm_loadu_ps(&src[pos+j])); j += 4; } while(--td); } r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3))); r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4)); - out_sample = _mm_cvtss_f32(r4); + const auto output = _mm_cvtss_f32(r4); frac += increment; - src += frac>>MixerFracBits; + pos += frac>>MixerFracBits; frac &= MixerFracMask; - } + return output; + }); } template<> -void MixHrtf_(const float *InSamples, float2 *AccumSamples, const uint IrSize, - const MixHrtfFilter *hrtfparams, const size_t BufferSize) -{ MixHrtfBase(InSamples, AccumSamples, IrSize, hrtfparams, BufferSize); } +void MixHrtf_(const al::span InSamples, const al::span AccumSamples, + const uint IrSize, const MixHrtfFilter *hrtfparams, const size_t SamplesToDo) +{ MixHrtfBase(InSamples, AccumSamples, IrSize, hrtfparams, SamplesToDo); } template<> -void MixHrtfBlend_(const float *InSamples, float2 *AccumSamples, const uint IrSize, - const HrtfFilter *oldparams, const MixHrtfFilter *newparams, const size_t BufferSize) +void MixHrtfBlend_(const al::span InSamples, + const al::span AccumSamples, const uint IrSize, const HrtfFilter *oldparams, + const MixHrtfFilter *newparams, const size_t SamplesToDo) { MixHrtfBlendBase(InSamples, AccumSamples, IrSize, oldparams, newparams, - BufferSize); + SamplesToDo); } template<> void MixDirectHrtf_(const FloatBufferSpan LeftOut, const FloatBufferSpan RightOut, - const al::span InSamples, float2 *AccumSamples, - float *TempBuf, HrtfChannelState *ChanState, const size_t IrSize, const size_t BufferSize) + const al::span InSamples, const al::span AccumSamples, + const al::span TempBuf, const al::span ChanState, + const size_t IrSize, const size_t SamplesToDo) { MixDirectHrtfBase(LeftOut, RightOut, InSamples, AccumSamples, TempBuf, ChanState, - IrSize, BufferSize); + IrSize, SamplesToDo); } template<> void Mix_(const al::span InSamples, const al::span OutBuffer, - float *CurrentGains, const float *TargetGains, const size_t Counter, const size_t OutPos) + const al::span CurrentGains, const al::span TargetGains, + const size_t Counter, const size_t OutPos) { + if((OutPos&3) != 0) UNLIKELY + return Mix_(InSamples, OutBuffer, CurrentGains, TargetGains, Counter, OutPos); + const float delta{(Counter > 0) ? 1.0f / static_cast(Counter) : 0.0f}; - const auto min_len = minz(Counter, InSamples.size()); - const auto aligned_len = minz((min_len+3) & ~3_uz, InSamples.size()) - min_len; + const auto fade_len = std::min(Counter, InSamples.size()); + const auto realign_len = std::min((fade_len+3_uz) & ~3_uz, InSamples.size()) - fade_len; + auto curgains = CurrentGains.begin(); + auto targetgains = TargetGains.cbegin(); for(FloatBufferLine &output : OutBuffer) - MixLine(InSamples, al::assume_aligned<16>(output.data()+OutPos), *CurrentGains++, - *TargetGains++, delta, min_len, aligned_len, Counter); + MixLine(InSamples, al::span{output}.subspan(OutPos), *curgains++, *targetgains++, delta, + fade_len, realign_len, Counter); } template<> -void Mix_(const al::span InSamples, float *OutBuffer, float &CurrentGain, - const float TargetGain, const size_t Counter) +void Mix_(const al::span InSamples, const al::span OutBuffer, + float &CurrentGain, const float TargetGain, const size_t Counter) { + if((reinterpret_cast(OutBuffer.data())&15) != 0) UNLIKELY + return Mix_(InSamples, OutBuffer, CurrentGain, TargetGain, Counter); + const float delta{(Counter > 0) ? 1.0f / static_cast(Counter) : 0.0f}; - const auto min_len = minz(Counter, InSamples.size()); - const auto aligned_len = minz((min_len+3) & ~3_uz, InSamples.size()) - min_len; + const auto fade_len = std::min(Counter, InSamples.size()); + const auto realign_len = std::min((fade_len+3_uz) & ~3_uz, InSamples.size()) - fade_len; - MixLine(InSamples, al::assume_aligned<16>(OutBuffer), CurrentGain, TargetGain, delta, min_len, - aligned_len, Counter); + MixLine(InSamples, OutBuffer, CurrentGain, TargetGain, delta, fade_len, realign_len, Counter); } diff --git a/3rdparty/openal/core/mixer/mixer_sse2.cpp b/3rdparty/openal/core/mixer/mixer_sse2.cpp index edaaf7a1a1fe..c79d50cabecf 100644 --- a/3rdparty/openal/core/mixer/mixer_sse2.cpp +++ b/3rdparty/openal/core/mixer/mixer_sse2.cpp @@ -23,19 +23,42 @@ #include #include +#include +#include +#include +#include + #include "alnumeric.h" +#include "alspan.h" +#include "core/cubic_defs.h" +#include "core/resampler_limits.h" #include "defs.h" +#include "opthelpers.h" struct SSE2Tag; struct LerpTag; +struct CubicTag; #if defined(__GNUC__) && !defined(__clang__) && !defined(__SSE2__) #pragma GCC target("sse2") #endif +using uint = unsigned int; + +namespace { + +constexpr uint CubicPhaseDiffBits{MixerFracBits - CubicPhaseBits}; +constexpr uint CubicPhaseDiffOne{1 << CubicPhaseDiffBits}; +constexpr uint CubicPhaseDiffMask{CubicPhaseDiffOne - 1u}; + +force_inline __m128 vmadd(const __m128 x, const __m128 y, const __m128 z) noexcept +{ return _mm_add_ps(x, _mm_mul_ps(y, z)); } + +} // namespace + template<> -void Resample_(const InterpState*, const float *RESTRICT src, uint frac, +void Resample_(const InterpState*, const al::span src, uint frac, const uint increment, const al::span dst) { ASSUME(frac < MixerFracOne); @@ -44,47 +67,148 @@ void Resample_(const InterpState*, const float *RESTRICT src, u const __m128 fracOne4{_mm_set1_ps(1.0f/MixerFracOne)}; const __m128i fracMask4{_mm_set1_epi32(MixerFracMask)}; - alignas(16) uint pos_[4], frac_[4]; - InitPosArrays(frac, increment, frac_, pos_); + std::array pos_{}, frac_{}; + InitPosArrays(MaxResamplerEdge, frac, increment, al::span{frac_}, al::span{pos_}); __m128i frac4{_mm_setr_epi32(static_cast(frac_[0]), static_cast(frac_[1]), static_cast(frac_[2]), static_cast(frac_[3]))}; __m128i pos4{_mm_setr_epi32(static_cast(pos_[0]), static_cast(pos_[1]), static_cast(pos_[2]), static_cast(pos_[3]))}; - auto dst_iter = dst.begin(); - for(size_t todo{dst.size()>>2};todo;--todo) + auto vecout = al::span{reinterpret_cast<__m128*>(dst.data()), dst.size()/4}; + std::generate(vecout.begin(), vecout.end(), [=,&pos4,&frac4]() -> __m128 { - const int pos0{_mm_cvtsi128_si32(pos4)}; - const int pos1{_mm_cvtsi128_si32(_mm_srli_si128(pos4, 4))}; - const int pos2{_mm_cvtsi128_si32(_mm_srli_si128(pos4, 8))}; - const int pos3{_mm_cvtsi128_si32(_mm_srli_si128(pos4, 12))}; - const __m128 val1{_mm_setr_ps(src[pos0 ], src[pos1 ], src[pos2 ], src[pos3 ])}; - const __m128 val2{_mm_setr_ps(src[pos0+1], src[pos1+1], src[pos2+1], src[pos3+1])}; + const auto pos0 = static_cast(_mm_cvtsi128_si32(pos4)); + const auto pos1 = static_cast(_mm_cvtsi128_si32(_mm_srli_si128(pos4, 4))); + const auto pos2 = static_cast(_mm_cvtsi128_si32(_mm_srli_si128(pos4, 8))); + const auto pos3 = static_cast(_mm_cvtsi128_si32(_mm_srli_si128(pos4, 12))); + ASSUME(pos0 <= pos1); ASSUME(pos1 <= pos2); ASSUME(pos2 <= pos3); + const __m128 val1{_mm_setr_ps(src[pos0], src[pos1], src[pos2], src[pos3])}; + const __m128 val2{_mm_setr_ps(src[pos0+1_uz], src[pos1+1_uz], src[pos2+1_uz], src[pos3+1_uz])}; /* val1 + (val2-val1)*mu */ const __m128 r0{_mm_sub_ps(val2, val1)}; const __m128 mu{_mm_mul_ps(_mm_cvtepi32_ps(frac4), fracOne4)}; const __m128 out{_mm_add_ps(val1, _mm_mul_ps(mu, r0))}; - _mm_store_ps(dst_iter, out); - dst_iter += 4; - frac4 = _mm_add_epi32(frac4, increment4); pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, MixerFracBits)); frac4 = _mm_and_si128(frac4, fracMask4); - } + return out; + }); if(size_t todo{dst.size()&3}) { - src += static_cast(_mm_cvtsi128_si32(pos4)); + auto pos = size_t{static_cast(_mm_cvtsi128_si32(pos4))}; + frac = static_cast(_mm_cvtsi128_si32(frac4)); + + const auto out = dst.last(todo); + std::generate(out.begin(), out.end(), [&pos,&frac,src,increment]() + { + const float smp{lerpf(src[pos+0], src[pos+1], + static_cast(frac) * (1.0f/MixerFracOne))}; + + frac += increment; + pos += frac>>MixerFracBits; + frac &= MixerFracMask; + return smp; + }); + } +} + +template<> +void Resample_(const InterpState *state, const al::span src, + uint frac, const uint increment, const al::span dst) +{ + ASSUME(frac < MixerFracOne); + + const auto filter = std::get(*state).filter; + + const __m128i increment4{_mm_set1_epi32(static_cast(increment*4))}; + const __m128i fracMask4{_mm_set1_epi32(MixerFracMask)}; + const __m128 fracDiffOne4{_mm_set1_ps(1.0f/CubicPhaseDiffOne)}; + const __m128i fracDiffMask4{_mm_set1_epi32(CubicPhaseDiffMask)}; + + std::array pos_{}, frac_{}; + InitPosArrays(MaxResamplerEdge-1, frac, increment, al::span{frac_}, al::span{pos_}); + __m128i frac4{_mm_setr_epi32(static_cast(frac_[0]), static_cast(frac_[1]), + static_cast(frac_[2]), static_cast(frac_[3]))}; + __m128i pos4{_mm_setr_epi32(static_cast(pos_[0]), static_cast(pos_[1]), + static_cast(pos_[2]), static_cast(pos_[3]))}; + + auto vecout = al::span{reinterpret_cast<__m128*>(dst.data()), dst.size()/4}; + std::generate(vecout.begin(), vecout.end(), [=,&pos4,&frac4] + { + const auto pos0 = static_cast(_mm_cvtsi128_si32(pos4)); + const auto pos1 = static_cast(_mm_cvtsi128_si32(_mm_srli_si128(pos4, 4))); + const auto pos2 = static_cast(_mm_cvtsi128_si32(_mm_srli_si128(pos4, 8))); + const auto pos3 = static_cast(_mm_cvtsi128_si32(_mm_srli_si128(pos4, 12))); + ASSUME(pos0 <= pos1); ASSUME(pos1 <= pos2); ASSUME(pos2 <= pos3); + const __m128 val0{_mm_loadu_ps(&src[pos0])}; + const __m128 val1{_mm_loadu_ps(&src[pos1])}; + const __m128 val2{_mm_loadu_ps(&src[pos2])}; + const __m128 val3{_mm_loadu_ps(&src[pos3])}; + + const __m128i pi4{_mm_srli_epi32(frac4, CubicPhaseDiffBits)}; + const auto pi0 = static_cast(_mm_cvtsi128_si32(pi4)); + const auto pi1 = static_cast(_mm_cvtsi128_si32(_mm_srli_si128(pi4, 4))); + const auto pi2 = static_cast(_mm_cvtsi128_si32(_mm_srli_si128(pi4, 8))); + const auto pi3 = static_cast(_mm_cvtsi128_si32(_mm_srli_si128(pi4, 12))); + ASSUME(pi0 < CubicPhaseCount); ASSUME(pi1 < CubicPhaseCount); + ASSUME(pi2 < CubicPhaseCount); ASSUME(pi3 < CubicPhaseCount); + + const __m128 pf4{_mm_mul_ps(_mm_cvtepi32_ps(_mm_and_si128(frac4, fracDiffMask4)), + fracDiffOne4)}; + + __m128 r0{_mm_mul_ps(val0, + vmadd(_mm_load_ps(filter[pi0].mCoeffs.data()), + _mm_shuffle_ps(pf4, pf4, _MM_SHUFFLE(0, 0, 0, 0)), + _mm_load_ps(filter[pi0].mDeltas.data())))}; + __m128 r1{_mm_mul_ps(val1, + vmadd(_mm_load_ps(filter[pi1].mCoeffs.data()), + _mm_shuffle_ps(pf4, pf4, _MM_SHUFFLE(1, 1, 1, 1)), + _mm_load_ps(filter[pi1].mDeltas.data())))}; + __m128 r2{_mm_mul_ps(val2, + vmadd(_mm_load_ps(filter[pi2].mCoeffs.data()), + _mm_shuffle_ps(pf4, pf4, _MM_SHUFFLE(2, 2, 2, 2)), + _mm_load_ps(filter[pi2].mDeltas.data())))}; + __m128 r3{_mm_mul_ps(val3, + vmadd(_mm_load_ps(filter[pi3].mCoeffs.data()), + _mm_shuffle_ps(pf4, pf4, _MM_SHUFFLE(3, 3, 3, 3)), + _mm_load_ps(filter[pi3].mDeltas.data())))}; + + _MM_TRANSPOSE4_PS(r0, r1, r2, r3); + r0 = _mm_add_ps(_mm_add_ps(r0, r1), _mm_add_ps(r2, r3)); + + frac4 = _mm_add_epi32(frac4, increment4); + pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, MixerFracBits)); + frac4 = _mm_and_si128(frac4, fracMask4); + return r0; + }); + + if(const size_t todo{dst.size()&3}) + { + auto pos = size_t{static_cast(_mm_cvtsi128_si32(pos4))}; frac = static_cast(_mm_cvtsi128_si32(frac4)); - do { - *(dst_iter++) = lerpf(src[0], src[1], static_cast(frac) * (1.0f/MixerFracOne)); + auto out = dst.last(todo); + std::generate(out.begin(), out.end(), [&pos,&frac,src,increment,filter] + { + const uint pi{frac >> CubicPhaseDiffBits}; ASSUME(pi < CubicPhaseCount); + const float pf{static_cast(frac&CubicPhaseDiffMask) * (1.0f/CubicPhaseDiffOne)}; + const __m128 pf4{_mm_set1_ps(pf)}; + + const __m128 f4 = vmadd(_mm_load_ps(filter[pi].mCoeffs.data()), pf4, + _mm_load_ps(filter[pi].mDeltas.data())); + __m128 r4{_mm_mul_ps(f4, _mm_loadu_ps(&src[pos]))}; + + r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3))); + r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4)); + const float output{_mm_cvtss_f32(r4)}; frac += increment; - src += frac>>MixerFracBits; + pos += frac>>MixerFracBits; frac &= MixerFracMask; - } while(--todo); + return output; + }); } } diff --git a/3rdparty/openal/core/mixer/mixer_sse41.cpp b/3rdparty/openal/core/mixer/mixer_sse41.cpp index 8ccd9fd3a862..345330454413 100644 --- a/3rdparty/openal/core/mixer/mixer_sse41.cpp +++ b/3rdparty/openal/core/mixer/mixer_sse41.cpp @@ -24,19 +24,42 @@ #include #include +#include +#include +#include +#include + #include "alnumeric.h" +#include "alspan.h" +#include "core/cubic_defs.h" +#include "core/resampler_limits.h" #include "defs.h" +#include "opthelpers.h" struct SSE4Tag; struct LerpTag; +struct CubicTag; #if defined(__GNUC__) && !defined(__clang__) && !defined(__SSE4_1__) #pragma GCC target("sse4.1") #endif +using uint = unsigned int; + +namespace { + +constexpr uint CubicPhaseDiffBits{MixerFracBits - CubicPhaseBits}; +constexpr uint CubicPhaseDiffOne{1 << CubicPhaseDiffBits}; +constexpr uint CubicPhaseDiffMask{CubicPhaseDiffOne - 1u}; + +force_inline __m128 vmadd(const __m128 x, const __m128 y, const __m128 z) noexcept +{ return _mm_add_ps(x, _mm_mul_ps(y, z)); } + +} // namespace + template<> -void Resample_(const InterpState*, const float *RESTRICT src, uint frac, +void Resample_(const InterpState*, const al::span src, uint frac, const uint increment, const al::span dst) { ASSUME(frac < MixerFracOne); @@ -45,35 +68,34 @@ void Resample_(const InterpState*, const float *RESTRICT src, u const __m128 fracOne4{_mm_set1_ps(1.0f/MixerFracOne)}; const __m128i fracMask4{_mm_set1_epi32(MixerFracMask)}; - alignas(16) uint pos_[4], frac_[4]; - InitPosArrays(frac, increment, frac_, pos_); + std::array pos_{}, frac_{}; + InitPosArrays(MaxResamplerEdge, frac, increment, al::span{frac_}, al::span{pos_}); __m128i frac4{_mm_setr_epi32(static_cast(frac_[0]), static_cast(frac_[1]), static_cast(frac_[2]), static_cast(frac_[3]))}; __m128i pos4{_mm_setr_epi32(static_cast(pos_[0]), static_cast(pos_[1]), static_cast(pos_[2]), static_cast(pos_[3]))}; - auto dst_iter = dst.begin(); - for(size_t todo{dst.size()>>2};todo;--todo) + auto vecout = al::span{reinterpret_cast<__m128*>(dst.data()), dst.size()/4}; + std::generate(vecout.begin(), vecout.end(), [=,&pos4,&frac4] { - const int pos0{_mm_extract_epi32(pos4, 0)}; - const int pos1{_mm_extract_epi32(pos4, 1)}; - const int pos2{_mm_extract_epi32(pos4, 2)}; - const int pos3{_mm_extract_epi32(pos4, 3)}; - const __m128 val1{_mm_setr_ps(src[pos0 ], src[pos1 ], src[pos2 ], src[pos3 ])}; - const __m128 val2{_mm_setr_ps(src[pos0+1], src[pos1+1], src[pos2+1], src[pos3+1])}; + const auto pos0 = static_cast(_mm_extract_epi32(pos4, 0)); + const auto pos1 = static_cast(_mm_extract_epi32(pos4, 1)); + const auto pos2 = static_cast(_mm_extract_epi32(pos4, 2)); + const auto pos3 = static_cast(_mm_extract_epi32(pos4, 3)); + ASSUME(pos0 <= pos1); ASSUME(pos1 <= pos2); ASSUME(pos2 <= pos3); + const __m128 val1{_mm_setr_ps(src[pos0], src[pos1], src[pos2], src[pos3])}; + const __m128 val2{_mm_setr_ps(src[pos0+1_uz], src[pos1+1_uz], src[pos2+1_uz], src[pos3+1_uz])}; /* val1 + (val2-val1)*mu */ const __m128 r0{_mm_sub_ps(val2, val1)}; const __m128 mu{_mm_mul_ps(_mm_cvtepi32_ps(frac4), fracOne4)}; const __m128 out{_mm_add_ps(val1, _mm_mul_ps(mu, r0))}; - _mm_store_ps(dst_iter, out); - dst_iter += 4; - frac4 = _mm_add_epi32(frac4, increment4); pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, MixerFracBits)); frac4 = _mm_and_si128(frac4, fracMask4); - } + return out; + }); if(size_t todo{dst.size()&3}) { @@ -81,15 +103,117 @@ void Resample_(const InterpState*, const float *RESTRICT src, u * four samples, so the lowest element is the next position to * resample. */ - src += static_cast(_mm_cvtsi128_si32(pos4)); + auto pos = size_t{static_cast(_mm_cvtsi128_si32(pos4))}; + frac = static_cast(_mm_cvtsi128_si32(frac4)); + + auto out = dst.last(todo); + std::generate(out.begin(), out.end(), [&pos,&frac,src,increment] + { + const float smp{lerpf(src[pos+0], src[pos+1], + static_cast(frac) * (1.0f/MixerFracOne))}; + + frac += increment; + pos += frac>>MixerFracBits; + frac &= MixerFracMask; + return smp; + }); + } +} + +template<> +void Resample_(const InterpState *state, const al::span src, + uint frac, const uint increment, const al::span dst) +{ + ASSUME(frac < MixerFracOne); + + const auto filter = std::get(*state).filter; + + const __m128i increment4{_mm_set1_epi32(static_cast(increment*4))}; + const __m128i fracMask4{_mm_set1_epi32(MixerFracMask)}; + const __m128 fracDiffOne4{_mm_set1_ps(1.0f/CubicPhaseDiffOne)}; + const __m128i fracDiffMask4{_mm_set1_epi32(CubicPhaseDiffMask)}; + + std::array pos_{}, frac_{}; + InitPosArrays(MaxResamplerEdge-1, frac, increment, al::span{frac_}, al::span{pos_}); + __m128i frac4{_mm_setr_epi32(static_cast(frac_[0]), static_cast(frac_[1]), + static_cast(frac_[2]), static_cast(frac_[3]))}; + __m128i pos4{_mm_setr_epi32(static_cast(pos_[0]), static_cast(pos_[1]), + static_cast(pos_[2]), static_cast(pos_[3]))}; + + auto vecout = al::span{reinterpret_cast<__m128*>(dst.data()), dst.size()/4}; + std::generate(vecout.begin(), vecout.end(), [=,&pos4,&frac4] + { + const auto pos0 = static_cast(_mm_extract_epi32(pos4, 0)); + const auto pos1 = static_cast(_mm_extract_epi32(pos4, 1)); + const auto pos2 = static_cast(_mm_extract_epi32(pos4, 2)); + const auto pos3 = static_cast(_mm_extract_epi32(pos4, 3)); + ASSUME(pos0 <= pos1); ASSUME(pos1 <= pos2); ASSUME(pos2 <= pos3); + const __m128 val0{_mm_loadu_ps(&src[pos0])}; + const __m128 val1{_mm_loadu_ps(&src[pos1])}; + const __m128 val2{_mm_loadu_ps(&src[pos2])}; + const __m128 val3{_mm_loadu_ps(&src[pos3])}; + + const __m128i pi4{_mm_srli_epi32(frac4, CubicPhaseDiffBits)}; + const auto pi0 = static_cast(_mm_extract_epi32(pi4, 0)); + const auto pi1 = static_cast(_mm_extract_epi32(pi4, 1)); + const auto pi2 = static_cast(_mm_extract_epi32(pi4, 2)); + const auto pi3 = static_cast(_mm_extract_epi32(pi4, 3)); + ASSUME(pi0 < CubicPhaseCount); ASSUME(pi1 < CubicPhaseCount); + ASSUME(pi2 < CubicPhaseCount); ASSUME(pi3 < CubicPhaseCount); + + const __m128 pf4{_mm_mul_ps(_mm_cvtepi32_ps(_mm_and_si128(frac4, fracDiffMask4)), + fracDiffOne4)}; + + __m128 r0{_mm_mul_ps(val0, + vmadd(_mm_load_ps(filter[pi0].mCoeffs.data()), + _mm_shuffle_ps(pf4, pf4, _MM_SHUFFLE(0, 0, 0, 0)), + _mm_load_ps(filter[pi0].mDeltas.data())))}; + __m128 r1{_mm_mul_ps(val1, + vmadd(_mm_load_ps(filter[pi1].mCoeffs.data()), + _mm_shuffle_ps(pf4, pf4, _MM_SHUFFLE(1, 1, 1, 1)), + _mm_load_ps(filter[pi1].mDeltas.data())))}; + __m128 r2{_mm_mul_ps(val2, + vmadd(_mm_load_ps(filter[pi2].mCoeffs.data()), + _mm_shuffle_ps(pf4, pf4, _MM_SHUFFLE(2, 2, 2, 2)), + _mm_load_ps(filter[pi2].mDeltas.data())))}; + __m128 r3{_mm_mul_ps(val3, + vmadd(_mm_load_ps(filter[pi3].mCoeffs.data()), + _mm_shuffle_ps(pf4, pf4, _MM_SHUFFLE(3, 3, 3, 3)), + _mm_load_ps(filter[pi3].mDeltas.data())))}; + + _MM_TRANSPOSE4_PS(r0, r1, r2, r3); + r0 = _mm_add_ps(_mm_add_ps(r0, r1), _mm_add_ps(r2, r3)); + + frac4 = _mm_add_epi32(frac4, increment4); + pos4 = _mm_add_epi32(pos4, _mm_srli_epi32(frac4, MixerFracBits)); + frac4 = _mm_and_si128(frac4, fracMask4); + return r0; + }); + + if(const size_t todo{dst.size()&3}) + { + auto pos = size_t{static_cast(_mm_cvtsi128_si32(pos4))}; frac = static_cast(_mm_cvtsi128_si32(frac4)); - do { - *(dst_iter++) = lerpf(src[0], src[1], static_cast(frac) * (1.0f/MixerFracOne)); + auto out = dst.last(todo); + std::generate(out.begin(), out.end(), [&pos,&frac,src,increment,filter] + { + const uint pi{frac >> CubicPhaseDiffBits}; ASSUME(pi < CubicPhaseCount); + const float pf{static_cast(frac&CubicPhaseDiffMask) * (1.0f/CubicPhaseDiffOne)}; + const __m128 pf4{_mm_set1_ps(pf)}; + + const __m128 f4 = vmadd(_mm_load_ps(filter[pi].mCoeffs.data()), pf4, + _mm_load_ps(filter[pi].mDeltas.data())); + __m128 r4{_mm_mul_ps(f4, _mm_loadu_ps(&src[pos]))}; + + r4 = _mm_add_ps(r4, _mm_shuffle_ps(r4, r4, _MM_SHUFFLE(0, 1, 2, 3))); + r4 = _mm_add_ps(r4, _mm_movehl_ps(r4, r4)); + const float output{_mm_cvtss_f32(r4)}; frac += increment; - src += frac>>MixerFracBits; + pos += frac>>MixerFracBits; frac &= MixerFracMask; - } while(--todo); + return output; + }); } } diff --git a/3rdparty/openal/core/resampler_limits.h b/3rdparty/openal/core/resampler_limits.h index 9d4cefdae251..a32807e8e0e1 100644 --- a/3rdparty/openal/core/resampler_limits.h +++ b/3rdparty/openal/core/resampler_limits.h @@ -5,8 +5,8 @@ * Note that the padding is symmetric (half at the beginning and half at the * end)! */ -constexpr int MaxResamplerPadding{48}; +constexpr unsigned int MaxResamplerPadding{48}; -constexpr int MaxResamplerEdge{MaxResamplerPadding >> 1}; +constexpr unsigned int MaxResamplerEdge{MaxResamplerPadding >> 1}; #endif /* CORE_RESAMPLER_LIMITS_H */ diff --git a/3rdparty/openal/core/rtkit.cpp b/3rdparty/openal/core/rtkit.cpp index ff944ebf2b4f..717059ab88af 100644 --- a/3rdparty/openal/core/rtkit.cpp +++ b/3rdparty/openal/core/rtkit.cpp @@ -30,14 +30,14 @@ #include "rtkit.h" -#include +#include #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include -#include +#include #include #include #ifdef __linux__ @@ -69,6 +69,7 @@ namespace { inline pid_t _gettid() { #ifdef __linux__ + /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) */ return static_cast(syscall(SYS_gettid)); #elif defined(__FreeBSD__) long pid{}; @@ -153,29 +154,29 @@ int rtkit_get_int_property(DBusConnection *connection, const char *propname, lon } // namespace -int rtkit_get_max_realtime_priority(DBusConnection *connection) +int rtkit_get_max_realtime_priority(DBusConnection *system_bus) { long long retval{}; - int err{rtkit_get_int_property(connection, "MaxRealtimePriority", &retval)}; + int err{rtkit_get_int_property(system_bus, "MaxRealtimePriority", &retval)}; return err < 0 ? err : static_cast(retval); } -int rtkit_get_min_nice_level(DBusConnection *connection, int *min_nice_level) +int rtkit_get_min_nice_level(DBusConnection *system_bus, int *min_nice_level) { long long retval{}; - int err{rtkit_get_int_property(connection, "MinNiceLevel", &retval)}; + int err{rtkit_get_int_property(system_bus, "MinNiceLevel", &retval)}; if(err >= 0) *min_nice_level = static_cast(retval); return err; } -long long rtkit_get_rttime_usec_max(DBusConnection *connection) +long long rtkit_get_rttime_usec_max(DBusConnection *system_bus) { long long retval{}; - int err{rtkit_get_int_property(connection, "RTTimeUSecMax", &retval)}; + int err{rtkit_get_int_property(system_bus, "RTTimeUSecMax", &retval)}; return err < 0 ? err : retval; } -int rtkit_make_realtime(DBusConnection *connection, pid_t thread, int priority) +int rtkit_make_realtime(DBusConnection *system_bus, pid_t thread, int priority) { if(thread == 0) thread = _gettid(); @@ -195,7 +196,7 @@ int rtkit_make_realtime(DBusConnection *connection, pid_t thread, int priority) if(!ready) return -ENOMEM; dbus::Error error; - dbus::MessagePtr r{dbus_connection_send_with_reply_and_block(connection, m.get(), -1, + dbus::MessagePtr r{dbus_connection_send_with_reply_and_block(system_bus, m.get(), -1, &error.get())}; if(!r) return translate_error(error->name); @@ -205,7 +206,7 @@ int rtkit_make_realtime(DBusConnection *connection, pid_t thread, int priority) return 0; } -int rtkit_make_high_priority(DBusConnection *connection, pid_t thread, int nice_level) +int rtkit_make_high_priority(DBusConnection *system_bus, pid_t thread, int nice_level) { if(thread == 0) thread = _gettid(); @@ -225,7 +226,7 @@ int rtkit_make_high_priority(DBusConnection *connection, pid_t thread, int nice_ if(!ready) return -ENOMEM; dbus::Error error; - dbus::MessagePtr r{dbus_connection_send_with_reply_and_block(connection, m.get(), -1, + dbus::MessagePtr r{dbus_connection_send_with_reply_and_block(system_bus, m.get(), -1, &error.get())}; if(!r) return translate_error(error->name); diff --git a/3rdparty/openal/core/storage_formats.cpp b/3rdparty/openal/core/storage_formats.cpp new file mode 100644 index 000000000000..b62f91a0a4d8 --- /dev/null +++ b/3rdparty/openal/core/storage_formats.cpp @@ -0,0 +1,89 @@ + +#include "config.h" + +#include "storage_formats.h" + +#include +#include + +namespace { +using namespace std::string_view_literals; +} // namespace + +auto NameFromFormat(FmtType type) noexcept -> std::string_view +{ + switch(type) + { + case FmtUByte: return "UInt8"sv; + case FmtShort: return "Int16"sv; + case FmtInt: return "Int32"sv; + case FmtFloat: return "Float"sv; + case FmtDouble: return "Double"sv; + case FmtMulaw: return "muLaw"sv; + case FmtAlaw: return "aLaw"sv; + case FmtIMA4: return "IMA4 ADPCM"sv; + case FmtMSADPCM: return "MS ADPCM"sv; + } + return ""sv; +} + +auto NameFromFormat(FmtChannels channels) noexcept -> std::string_view +{ + switch(channels) + { + case FmtMono: return "Mono"sv; + case FmtStereo: return "Stereo"sv; + case FmtRear: return "Rear"sv; + case FmtQuad: return "Quadraphonic"sv; + case FmtX51: return "Surround 5.1"sv; + case FmtX61: return "Surround 6.1"sv; + case FmtX71: return "Surround 7.1"sv; + case FmtBFormat2D: return "B-Format 2D"sv; + case FmtBFormat3D: return "B-Format 3D"sv; + case FmtUHJ2: return "UHJ2"sv; + case FmtUHJ3: return "UHJ3"sv; + case FmtUHJ4: return "UHJ4"sv; + case FmtSuperStereo: return "Super Stereo"sv; + case FmtMonoDup: return "Mono (dup)"sv; + } + return ""sv; +} + +uint BytesFromFmt(FmtType type) noexcept +{ + switch(type) + { + case FmtUByte: return sizeof(std::uint8_t); + case FmtShort: return sizeof(std::int16_t); + case FmtInt: return sizeof(std::int32_t); + case FmtFloat: return sizeof(float); + case FmtDouble: return sizeof(double); + case FmtMulaw: return sizeof(std::uint8_t); + case FmtAlaw: return sizeof(std::uint8_t); + case FmtIMA4: break; + case FmtMSADPCM: break; + } + return 0; +} + +uint ChannelsFromFmt(FmtChannels chans, uint ambiorder) noexcept +{ + switch(chans) + { + case FmtMono: return 1; + case FmtStereo: return 2; + case FmtRear: return 2; + case FmtQuad: return 4; + case FmtX51: return 6; + case FmtX61: return 7; + case FmtX71: return 8; + case FmtBFormat2D: return (ambiorder*2) + 1; + case FmtBFormat3D: return (ambiorder+1) * (ambiorder+1); + case FmtUHJ2: return 2; + case FmtUHJ3: return 3; + case FmtUHJ4: return 4; + case FmtSuperStereo: return 2; + case FmtMonoDup: return 1; + } + return 0; +} diff --git a/3rdparty/openal/core/storage_formats.h b/3rdparty/openal/core/storage_formats.h new file mode 100644 index 000000000000..fab0a04ce28d --- /dev/null +++ b/3rdparty/openal/core/storage_formats.h @@ -0,0 +1,56 @@ +#ifndef CORE_STORAGE_FORMATS_H +#define CORE_STORAGE_FORMATS_H + +#include + +using uint = unsigned int; + +/* Storable formats */ +enum FmtType : unsigned char { + FmtUByte, + FmtShort, + FmtInt, + FmtFloat, + FmtDouble, + FmtMulaw, + FmtAlaw, + FmtIMA4, + FmtMSADPCM, +}; +enum FmtChannels : unsigned char { + FmtMono, + FmtStereo, + FmtRear, + FmtQuad, + FmtX51, /* (WFX order) */ + FmtX61, /* (WFX order) */ + FmtX71, /* (WFX order) */ + FmtBFormat2D, + FmtBFormat3D, + FmtUHJ2, /* 2-channel UHJ, aka "BHJ", stereo-compatible */ + FmtUHJ3, /* 3-channel UHJ, aka "THJ" */ + FmtUHJ4, /* 4-channel UHJ, aka "PHJ" */ + FmtSuperStereo, /* Stereo processed with Super Stereo. */ + FmtMonoDup, /* Mono duplicated for left/right separation */ +}; + +enum class AmbiLayout : unsigned char { + FuMa, + ACN, +}; +enum class AmbiScaling : unsigned char { + FuMa, + SN3D, + N3D, + UHJ, +}; + +auto NameFromFormat(FmtType type) noexcept -> std::string_view; +auto NameFromFormat(FmtChannels channels) noexcept -> std::string_view; + +uint BytesFromFmt(FmtType type) noexcept; +uint ChannelsFromFmt(FmtChannels chans, uint ambiorder) noexcept; +inline uint FrameSizeFromFmt(FmtChannels chans, FmtType type, uint ambiorder) noexcept +{ return ChannelsFromFmt(chans, ambiorder) * BytesFromFmt(type); } + +#endif /* CORE_STORAGE_FORMATS_H */ diff --git a/3rdparty/openal/core/uhjfilter.cpp b/3rdparty/openal/core/uhjfilter.cpp index 4e4e99a54800..25517d689804 100644 --- a/3rdparty/openal/core/uhjfilter.cpp +++ b/3rdparty/openal/core/uhjfilter.cpp @@ -4,29 +4,117 @@ #include "uhjfilter.h" #include -#include +#include +#include +#include +#include #include "alcomplex.h" -#include "alnumeric.h" +#include "almalloc.h" +#include "alnumbers.h" +#include "core/bufferline.h" #include "opthelpers.h" +#include "pffft.h" #include "phase_shifter.h" +#include "vector.h" -UhjQualityType UhjDecodeQuality{UhjQualityType::Default}; -UhjQualityType UhjEncodeQuality{UhjQualityType::Default}; +namespace { +template +constexpr auto assume_aligned_span(const al::span s) noexcept -> al::span +{ return al::span{al::assume_aligned(s.data()), s.size()}; } -namespace { +/* Convolution is implemented using a segmented overlap-add method. The filter + * response is broken up into multiple segments of 128 samples, and each + * segment has an FFT applied with a 256-sample buffer (the latter half left + * silent) to get its frequency-domain response. + * + * Input samples are similarly broken up into 128-sample segments, with a 256- + * sample FFT applied to each new incoming segment to get its frequency-domain + * response. A history of FFT'd input segments is maintained, equal to the + * number of filter response segments. + * + * To apply the convolution, each filter response segment is convolved with its + * paired input segment (using complex multiplies, far cheaper than time-domain + * FIRs), accumulating into an FFT buffer. The input history is then shifted to + * align with later filter response segments for the next input segment. + * + * An inverse FFT is then applied to the accumulated FFT buffer to get a 256- + * sample time-domain response for output, which is split in two halves. The + * first half is the 128-sample output, and the second half is a 128-sample + * (really, 127) delayed extension, which gets added to the output next time. + * Convolving two time-domain responses of length N results in a time-domain + * signal of length N*2 - 1, and this holds true regardless of the convolution + * being applied in the frequency domain, so these "overflow" samples need to + * be accounted for. + */ +template +struct SegmentedFilter { + static constexpr size_t sFftLength{256}; + static constexpr size_t sSampleLength{sFftLength / 2}; + static constexpr size_t sNumSegments{N/sSampleLength}; + static_assert(N >= sFftLength); + static_assert((N % sSampleLength) == 0); + + PFFFTSetup mFft; + alignas(16) std::array mFilterData; + + SegmentedFilter() : mFft{sFftLength, PFFFT_REAL} + { + static constexpr size_t fft_size{N}; + + /* To set up the filter, we first need to generate the desired + * response (not reversed). + */ + auto tmpBuffer = std::vector(fft_size, 0.0); + for(std::size_t i{0};i < fft_size/2;++i) + { + const int k{int{fft_size/2} - static_cast(i*2 + 1)}; + + const double w{2.0*al::numbers::pi * static_cast(i*2 + 1) + / double{fft_size}}; + const double window{0.3635819 - 0.4891775*std::cos(w) + 0.1365995*std::cos(2.0*w) + - 0.0106411*std::cos(3.0*w)}; + + const double pk{al::numbers::pi * static_cast(k)}; + tmpBuffer[i*2 + 1] = window * (1.0-std::cos(pk)) / pk; + } + + /* The segments of the filter are converted back to the frequency + * domain, each on their own (0 stuffed). + */ + using complex_d = std::complex; + auto fftBuffer = std::vector(sFftLength); + auto fftTmp = al::vector(sFftLength); + auto filter = mFilterData.begin(); + for(size_t s{0};s < sNumSegments;++s) + { + const auto tmpspan = al::span{tmpBuffer}.subspan(sSampleLength*s, sSampleLength); + auto iter = std::copy_n(tmpspan.cbegin(), tmpspan.size(), fftBuffer.begin()); + std::fill(iter, fftBuffer.end(), complex_d{}); + forward_fft(fftBuffer); + + /* Convert to zdomain data for PFFFT, scaled by the FFT length so + * the iFFT result will be normalized. + */ + for(size_t i{0};i < sSampleLength;++i) + { + fftTmp[i*2 + 0] = static_cast(fftBuffer[i].real()) / float{sFftLength}; + fftTmp[i*2 + 1] = static_cast((i == 0) ? fftBuffer[sSampleLength].real() + : fftBuffer[i].imag()) / float{sFftLength}; + } + mFft.zreorder(fftTmp.data(), al::to_address(filter), PFFFT_BACKWARD); + filter += sFftLength; + } + } +}; -const PhaseShifterT PShiftLq{}; -const PhaseShifterT PShiftHq{}; +template +const SegmentedFilter gSegmentedFilter; template -struct GetPhaseShifter; -template<> -struct GetPhaseShifter { static auto& Get() noexcept { return PShiftLq; } }; -template<> -struct GetPhaseShifter { static auto& Get() noexcept { return PShiftHq; } }; +const PhaseShifterT PShifter; /* Filter coefficients for the 'base' all-pass IIR, which applies a frequency- @@ -43,12 +131,24 @@ constexpr std::array Filter2Coeff{{ 0.161758498368f, 0.733028932341f, 0.945349700329f, 0.990599156684f }}; -} // namespace -void UhjAllPassFilter::process(const al::span coeffs, - const al::span src, const bool updateState, float *RESTRICT dst) +void processOne(UhjAllPassFilter &self, const al::span coeffs, float x) +{ + auto state = self.mState; + for(size_t i{0};i < 4;++i) + { + const float y{x*coeffs[i] + state[i].z[0]}; + state[i].z[0] = state[i].z[1]; + state[i].z[1] = y*coeffs[i] - x; + x = y; + } + self.mState = state; +} + +void process(UhjAllPassFilter &self, const al::span coeffs, + const al::span src, const bool updateState, const al::span dst) { - auto state = mState; + auto state = self.mState; auto proc_sample = [&state,coeffs](float x) noexcept -> float { @@ -61,10 +161,11 @@ void UhjAllPassFilter::process(const al::span coeffs, } return x; }; - std::transform(src.begin(), src.end(), dst, proc_sample); - if(updateState) LIKELY mState = state; + std::transform(src.begin(), src.end(), dst.begin(), proc_sample); + if(updateState) LIKELY self.mState = state; } +} // namespace /* Encoding UHJ from B-Format is done as: * @@ -87,68 +188,140 @@ template void UhjEncoder::encode(float *LeftOut, float *RightOut, const al::span InSamples, const size_t SamplesToDo) { - const auto &PShift = GetPhaseShifter::Get(); + static constexpr auto &Filter = gSegmentedFilter; + static_assert(sFftLength == Filter.sFftLength); + static_assert(sSegmentSize == Filter.sSampleLength); + static_assert(sNumSegments == Filter.sNumSegments); ASSUME(SamplesToDo > 0); + ASSUME(SamplesToDo <= BufferLineSize); - const float *RESTRICT winput{al::assume_aligned<16>(InSamples[0])}; - const float *RESTRICT xinput{al::assume_aligned<16>(InSamples[1])}; - const float *RESTRICT yinput{al::assume_aligned<16>(InSamples[2])}; + const auto winput = al::span{al::assume_aligned<16>(InSamples[0]), SamplesToDo}; + const auto xinput = al::span{al::assume_aligned<16>(InSamples[1]), SamplesToDo}; + const auto yinput = al::span{al::assume_aligned<16>(InSamples[2]), SamplesToDo}; - std::copy_n(winput, SamplesToDo, mW.begin()+sFilterDelay); - std::copy_n(xinput, SamplesToDo, mX.begin()+sFilterDelay); - std::copy_n(yinput, SamplesToDo, mY.begin()+sFilterDelay); + std::copy_n(winput.begin(), SamplesToDo, mW.begin()+sFilterDelay); + std::copy_n(xinput.begin(), SamplesToDo, mX.begin()+sFilterDelay); + std::copy_n(yinput.begin(), SamplesToDo, mY.begin()+sFilterDelay); /* S = 0.9396926*W + 0.1855740*X */ - for(size_t i{0};i < SamplesToDo;++i) - mS[i] = 0.9396926f*mW[i] + 0.1855740f*mX[i]; + std::transform(mW.begin(), mW.begin()+SamplesToDo, mX.begin(), mS.begin(), + [](const float w, const float x) noexcept { return 0.9396926f*w + 0.1855740f*x; }); /* Precompute j(-0.3420201*W + 0.5098604*X) and store in mD. */ - std::transform(winput, winput+SamplesToDo, xinput, mWX.begin() + sWXInOffset, - [](const float w, const float x) noexcept -> float - { return -0.3420201f*w + 0.5098604f*x; }); - PShift.process({mD.data(), SamplesToDo}, mWX.data()); + auto dstore = mD.begin(); + size_t curseg{mCurrentSegment}; + for(size_t base{0};base < SamplesToDo;) + { + const size_t todo{std::min(sSegmentSize-mFifoPos, SamplesToDo-base)}; + auto wseg = winput.subspan(base, todo); + auto xseg = xinput.subspan(base, todo); + /* Some Clang versions don't like calling subspan on an rvalue here. */ + const auto wxio_ = al::span{mWXInOut}; + auto wxio = wxio_.subspan(mFifoPos, todo); + + /* Copy out the samples that were previously processed by the FFT. */ + dstore = std::copy_n(wxio.begin(), todo, dstore); + + /* Transform the non-delayed input and store in the front half of the + * filter input. + */ + std::transform(wseg.begin(), wseg.end(), xseg.begin(), wxio.begin(), + [](const float w, const float x) noexcept -> float + { return -0.3420201f*w + 0.5098604f*x; }); + + mFifoPos += todo; + base += todo; + + /* Check whether the input buffer is filled with new samples. */ + if(mFifoPos < sSegmentSize) break; + mFifoPos = 0; + + /* Copy the new input to the next history segment, clearing the back + * half of the segment, and convert to the frequency domain. + */ + auto input = mWXHistory.begin() + curseg*sFftLength; + std::copy_n(mWXInOut.begin(), sSegmentSize, input); + std::fill_n(input+sSegmentSize, sSegmentSize, 0.0f); + + Filter.mFft.transform(al::to_address(input), al::to_address(input), mWorkData.data(), + PFFFT_FORWARD); + + /* Convolve each input segment with its IR filter counterpart (aligned + * in time, from newest to oldest). + */ + mFftBuffer.fill(0.0f); + auto filter = Filter.mFilterData.begin(); + for(size_t s{curseg};s < sNumSegments;++s) + { + Filter.mFft.zconvolve_accumulate(al::to_address(input), al::to_address(filter), + mFftBuffer.data()); + input += sFftLength; + filter += sFftLength; + } + input = mWXHistory.begin(); + for(size_t s{0};s < curseg;++s) + { + Filter.mFft.zconvolve_accumulate(al::to_address(input), al::to_address(filter), + mFftBuffer.data()); + input += sFftLength; + filter += sFftLength; + } + + /* Convert back to samples, writing to the output and storing the extra + * for next time. + */ + Filter.mFft.transform(mFftBuffer.data(), mFftBuffer.data(), mWorkData.data(), + PFFFT_BACKWARD); + + std::transform(mFftBuffer.begin(), mFftBuffer.begin()+sSegmentSize, + mWXInOut.begin()+sSegmentSize, mWXInOut.begin(), std::plus{}); + std::copy_n(mFftBuffer.begin()+sSegmentSize, sSegmentSize, mWXInOut.begin()+sSegmentSize); + + /* Shift the input history. */ + curseg = curseg ? (curseg-1) : (sNumSegments-1); + } + mCurrentSegment = curseg; /* D = j(-0.3420201*W + 0.5098604*X) + 0.6554516*Y */ - for(size_t i{0};i < SamplesToDo;++i) - mD[i] = mD[i] + 0.6554516f*mY[i]; + std::transform(mD.begin(), mD.begin()+SamplesToDo, mY.begin(), mD.begin(), + [](const float jwx, const float y) noexcept { return jwx + 0.6554516f*y; }); /* Copy the future samples to the front for next time. */ std::copy(mW.cbegin()+SamplesToDo, mW.cbegin()+SamplesToDo+sFilterDelay, mW.begin()); std::copy(mX.cbegin()+SamplesToDo, mX.cbegin()+SamplesToDo+sFilterDelay, mX.begin()); std::copy(mY.cbegin()+SamplesToDo, mY.cbegin()+SamplesToDo+sFilterDelay, mY.begin()); - std::copy(mWX.cbegin()+SamplesToDo, mWX.cbegin()+SamplesToDo+sWXInOffset, mWX.begin()); /* Apply a delay to the existing output to align with the input delay. */ - auto *delayBuffer = mDirectDelay.data(); + auto delayBuffer = mDirectDelay.begin(); for(float *buffer : {LeftOut, RightOut}) { - float *distbuf{al::assume_aligned<16>(delayBuffer->data())}; + const auto distbuf = assume_aligned_span<16>(al::span{*delayBuffer}); ++delayBuffer; - float *inout{al::assume_aligned<16>(buffer)}; - auto inout_end = inout + SamplesToDo; - if(SamplesToDo >= sFilterDelay) LIKELY + const auto inout = al::span{al::assume_aligned<16>(buffer), SamplesToDo}; + if(SamplesToDo >= sFilterDelay) { - auto delay_end = std::rotate(inout, inout_end - sFilterDelay, inout_end); - std::swap_ranges(inout, delay_end, distbuf); + auto delay_end = std::rotate(inout.begin(), inout.end() - sFilterDelay, inout.end()); + std::swap_ranges(inout.begin(), delay_end, distbuf.begin()); } else { - auto delay_start = std::swap_ranges(inout, inout_end, distbuf); - std::rotate(distbuf, delay_start, distbuf + sFilterDelay); + auto delay_start = std::swap_ranges(inout.begin(), inout.end(), distbuf.begin()); + std::rotate(distbuf.begin(), delay_start, distbuf.begin() + sFilterDelay); } } /* Combine the direct signal with the produced output. */ /* Left = (S + D)/2.0 */ - float *RESTRICT left{al::assume_aligned<16>(LeftOut)}; - for(size_t i{0};i < SamplesToDo;i++) + const auto left = al::span{al::assume_aligned<16>(LeftOut), SamplesToDo}; + for(size_t i{0};i < SamplesToDo;++i) left[i] += (mS[i] + mD[i]) * 0.5f; + /* Right = (S - D)/2.0 */ - float *RESTRICT right{al::assume_aligned<16>(RightOut)}; - for(size_t i{0};i < SamplesToDo;i++) + const auto right = al::span{al::assume_aligned<16>(RightOut), SamplesToDo}; + for(size_t i{0};i < SamplesToDo;++i) right[i] += (mS[i] - mD[i]) * 0.5f; } @@ -171,47 +344,49 @@ void UhjEncoderIIR::encode(float *LeftOut, float *RightOut, const al::span InSamples, const size_t SamplesToDo) { ASSUME(SamplesToDo > 0); + ASSUME(SamplesToDo <= BufferLineSize); - const float *RESTRICT winput{al::assume_aligned<16>(InSamples[0])}; - const float *RESTRICT xinput{al::assume_aligned<16>(InSamples[1])}; - const float *RESTRICT yinput{al::assume_aligned<16>(InSamples[2])}; + const auto winput = al::span{al::assume_aligned<16>(InSamples[0]), SamplesToDo}; + const auto xinput = al::span{al::assume_aligned<16>(InSamples[1]), SamplesToDo}; + const auto yinput = al::span{al::assume_aligned<16>(InSamples[2]), SamplesToDo}; /* S = 0.9396926*W + 0.1855740*X */ - std::transform(winput, winput+SamplesToDo, xinput, mTemp.begin(), + std::transform(winput.begin(), winput.end(), xinput.begin(), mTemp.begin(), [](const float w, const float x) noexcept { return 0.9396926f*w + 0.1855740f*x; }); - mFilter1WX.process(Filter1Coeff, {mTemp.data(), SamplesToDo}, true, mS.data()+1); + process(mFilter1WX, Filter1Coeff, al::span{mTemp}.first(SamplesToDo), true, + al::span{mS}.subspan(1)); mS[0] = mDelayWX; mDelayWX = mS[SamplesToDo]; /* Precompute j(-0.3420201*W + 0.5098604*X) and store in mWX. */ - std::transform(winput, winput+SamplesToDo, xinput, mTemp.begin(), + std::transform(winput.begin(), winput.end(), xinput.begin(), mTemp.begin(), [](const float w, const float x) noexcept { return -0.3420201f*w + 0.5098604f*x; }); - mFilter2WX.process(Filter2Coeff, {mTemp.data(), SamplesToDo}, true, mWX.data()); + process(mFilter2WX, Filter2Coeff, al::span{mTemp}.first(SamplesToDo), true, mWX); /* Apply filter1 to Y and store in mD. */ - mFilter1Y.process(Filter1Coeff, {yinput, SamplesToDo}, SamplesToDo, mD.data()+1); + process(mFilter1Y, Filter1Coeff, yinput, true, al::span{mD}.subspan(1)); mD[0] = mDelayY; mDelayY = mD[SamplesToDo]; /* D = j(-0.3420201*W + 0.5098604*X) + 0.6554516*Y */ - for(size_t i{0};i < SamplesToDo;++i) - mD[i] = mWX[i] + 0.6554516f*mD[i]; + std::transform(mWX.begin(), mWX.begin()+SamplesToDo, mD.begin(), mD.begin(), + [](const float jwx, const float y) noexcept { return jwx + 0.6554516f*y; }); /* Apply the base filter to the existing output to align with the processed * signal. */ - mFilter1Direct[0].process(Filter1Coeff, {LeftOut, SamplesToDo}, true, mTemp.data()+1); + const auto left = al::span{al::assume_aligned<16>(LeftOut), SamplesToDo}; + process(mFilter1Direct[0], Filter1Coeff, left, true, al::span{mTemp}.subspan(1)); mTemp[0] = mDirectDelay[0]; mDirectDelay[0] = mTemp[SamplesToDo]; /* Left = (S + D)/2.0 */ - float *RESTRICT left{al::assume_aligned<16>(LeftOut)}; - for(size_t i{0};i < SamplesToDo;i++) + for(size_t i{0};i < SamplesToDo;++i) left[i] = (mS[i] + mD[i])*0.5f + mTemp[i]; - mFilter1Direct[1].process(Filter1Coeff, {RightOut, SamplesToDo}, true, mTemp.data()+1); + const auto right = al::span{al::assume_aligned<16>(RightOut), SamplesToDo}; + process(mFilter1Direct[1], Filter1Coeff, right, true, al::span{mTemp}.subspan(1)); mTemp[0] = mDirectDelay[1]; mDirectDelay[1] = mTemp[SamplesToDo]; /* Right = (S - D)/2.0 */ - float *RESTRICT right{al::assume_aligned<16>(RightOut)}; - for(size_t i{0};i < SamplesToDo;i++) + for(size_t i{0};i < SamplesToDo;++i) right[i] = (mS[i] - mD[i])*0.5f + mTemp[i]; } @@ -235,31 +410,29 @@ void UhjDecoder::decode(const al::span samples, const size_t samplesT { static_assert(sInputPadding <= sMaxPadding, "Filter padding is too large"); - const auto &PShift = GetPhaseShifter::Get(); + constexpr auto &PShift = PShifter; ASSUME(samplesToDo > 0); + ASSUME(samplesToDo <= BufferLineSize); { - const float *RESTRICT left{al::assume_aligned<16>(samples[0])}; - const float *RESTRICT right{al::assume_aligned<16>(samples[1])}; - const float *RESTRICT t{al::assume_aligned<16>(samples[2])}; + const auto left = al::span{al::assume_aligned<16>(samples[0]), samplesToDo+sInputPadding}; + const auto right = al::span{al::assume_aligned<16>(samples[1]), samplesToDo+sInputPadding}; + const auto t = al::span{al::assume_aligned<16>(samples[2]), samplesToDo+sInputPadding}; /* S = Left + Right */ - for(size_t i{0};i < samplesToDo+sInputPadding;++i) - mS[i] = left[i] + right[i]; + std::transform(left.begin(), left.end(), right.begin(), mS.begin(), std::plus{}); /* D = Left - Right */ - for(size_t i{0};i < samplesToDo+sInputPadding;++i) - mD[i] = left[i] - right[i]; + std::transform(left.begin(), left.end(), right.begin(), mD.begin(), std::minus{}); /* T */ - for(size_t i{0};i < samplesToDo+sInputPadding;++i) - mT[i] = t[i]; + std::copy(t.begin(), t.end(), mT.begin()); } - float *RESTRICT woutput{al::assume_aligned<16>(samples[0])}; - float *RESTRICT xoutput{al::assume_aligned<16>(samples[1])}; - float *RESTRICT youtput{al::assume_aligned<16>(samples[2])}; + const auto woutput = al::span{al::assume_aligned<16>(samples[0]), samplesToDo}; + const auto xoutput = al::span{al::assume_aligned<16>(samples[1]), samplesToDo}; + const auto youtput = al::span{al::assume_aligned<16>(samples[2]), samplesToDo}; /* Precompute j(0.828331*D + 0.767820*T) and store in xoutput. */ auto tmpiter = std::copy(mDTHistory.cbegin(), mDTHistory.cend(), mTemp.begin()); @@ -267,21 +440,22 @@ void UhjDecoder::decode(const al::span samples, const size_t samplesT [](const float d, const float t) noexcept { return 0.828331f*d + 0.767820f*t; }); if(updateState) LIKELY std::copy_n(mTemp.cbegin()+samplesToDo, mDTHistory.size(), mDTHistory.begin()); - PShift.process({xoutput, samplesToDo}, mTemp.data()); + PShift.process(xoutput, mTemp); /* W = 0.981532*S + 0.197484*j(0.828331*D + 0.767820*T) */ - for(size_t i{0};i < samplesToDo;++i) - woutput[i] = 0.981532f*mS[i] + 0.197484f*xoutput[i]; + std::transform(mS.begin(), mS.begin()+samplesToDo, xoutput.begin(), woutput.begin(), + [](const float s, const float jdt) noexcept { return 0.981532f*s + 0.197484f*jdt; }); + /* X = 0.418496*S - j(0.828331*D + 0.767820*T) */ - for(size_t i{0};i < samplesToDo;++i) - xoutput[i] = 0.418496f*mS[i] - xoutput[i]; + std::transform(mS.begin(), mS.begin()+samplesToDo, xoutput.begin(), xoutput.begin(), + [](const float s, const float jdt) noexcept { return 0.418496f*s - jdt; }); /* Precompute j*S and store in youtput. */ tmpiter = std::copy(mSHistory.cbegin(), mSHistory.cend(), mTemp.begin()); std::copy_n(mS.cbegin(), samplesToDo+sInputPadding, tmpiter); if(updateState) LIKELY std::copy_n(mTemp.cbegin()+samplesToDo, mSHistory.size(), mSHistory.begin()); - PShift.process({youtput, samplesToDo}, mTemp.data()); + PShift.process(youtput, mTemp); /* Y = 0.795968*D - 0.676392*T + j(0.186633*S) */ for(size_t i{0};i < samplesToDo;++i) @@ -289,10 +463,10 @@ void UhjDecoder::decode(const al::span samples, const size_t samplesT if(samples.size() > 3) { - float *RESTRICT zoutput{al::assume_aligned<16>(samples[3])}; + const auto zoutput = al::span{al::assume_aligned<16>(samples[3]), samplesToDo}; /* Z = 1.023332*Q */ - for(size_t i{0};i < samplesToDo;++i) - zoutput[i] = 1.023332f*zoutput[i]; + std::transform(zoutput.begin(), zoutput.end(), zoutput.begin(), + [](const float q) noexcept { return 1.023332f*q; }); } } @@ -302,70 +476,68 @@ void UhjDecoderIIR::decode(const al::span samples, const size_t samplesT static_assert(sInputPadding <= sMaxPadding, "Filter padding is too large"); ASSUME(samplesToDo > 0); + ASSUME(samplesToDo <= BufferLineSize); { - const float *RESTRICT left{al::assume_aligned<16>(samples[0])}; - const float *RESTRICT right{al::assume_aligned<16>(samples[1])}; + const auto left = al::span{al::assume_aligned<16>(samples[0]), samplesToDo+sInputPadding}; + const auto right = al::span{al::assume_aligned<16>(samples[1]), samplesToDo+sInputPadding}; /* S = Left + Right */ - for(size_t i{0};i < samplesToDo;++i) - mS[i] = left[i] + right[i]; + std::transform(left.begin(), left.end(), right.begin(), mS.begin(), std::plus{}); /* D = Left - Right */ - for(size_t i{0};i < samplesToDo;++i) - mD[i] = left[i] - right[i]; + std::transform(left.begin(), left.end(), right.begin(), mD.begin(), std::minus{}); } - float *RESTRICT woutput{al::assume_aligned<16>(samples[0])}; - float *RESTRICT xoutput{al::assume_aligned<16>(samples[1])}; - float *RESTRICT youtput{al::assume_aligned<16>(samples[2])}; + const auto woutput = al::span{al::assume_aligned<16>(samples[0]), samplesToDo}; + const auto xoutput = al::span{al::assume_aligned<16>(samples[1]), samplesToDo}; + const auto youtput = al::span{al::assume_aligned<16>(samples[2]), samplesToDo+sInputPadding}; /* Precompute j(0.828331*D + 0.767820*T) and store in xoutput. */ - std::transform(mD.cbegin(), mD.cbegin()+samplesToDo, youtput, mTemp.begin(), + std::transform(mD.cbegin(), mD.cbegin()+sInputPadding+samplesToDo, youtput.begin(), + mTemp.begin(), [](const float d, const float t) noexcept { return 0.828331f*d + 0.767820f*t; }); - mFilter2DT.process(Filter2Coeff, {mTemp.data(), samplesToDo}, updateState, xoutput); + if(mFirstRun) processOne(mFilter2DT, Filter2Coeff, mTemp[0]); + process(mFilter2DT, Filter2Coeff, al::span{mTemp}.subspan(1, samplesToDo), updateState, + xoutput); /* Apply filter1 to S and store in mTemp. */ - mTemp[0] = mDelayS; - mFilter1S.process(Filter1Coeff, {mS.data(), samplesToDo}, updateState, mTemp.data()+1); - if(updateState) LIKELY mDelayS = mTemp[samplesToDo]; + process(mFilter1S, Filter1Coeff, al::span{mS}.first(samplesToDo), updateState, mTemp); /* W = 0.981532*S + 0.197484*j(0.828331*D + 0.767820*T) */ - for(size_t i{0};i < samplesToDo;++i) - woutput[i] = 0.981532f*mTemp[i] + 0.197484f*xoutput[i]; + std::transform(mTemp.begin(), mTemp.begin()+samplesToDo, xoutput.begin(), woutput.begin(), + [](const float s, const float jdt) noexcept { return 0.981532f*s + 0.197484f*jdt; }); /* X = 0.418496*S - j(0.828331*D + 0.767820*T) */ - for(size_t i{0};i < samplesToDo;++i) - xoutput[i] = 0.418496f*mTemp[i] - xoutput[i]; + std::transform(mTemp.begin(), mTemp.begin()+samplesToDo, xoutput.begin(), xoutput.begin(), + [](const float s, const float jdt) noexcept { return 0.418496f*s - jdt; }); /* Apply filter1 to (0.795968*D - 0.676392*T) and store in mTemp. */ - std::transform(mD.cbegin(), mD.cbegin()+samplesToDo, youtput, youtput, + std::transform(mD.cbegin(), mD.cbegin()+samplesToDo, youtput.begin(), youtput.begin(), [](const float d, const float t) noexcept { return 0.795968f*d - 0.676392f*t; }); - mTemp[0] = mDelayDT; - mFilter1DT.process(Filter1Coeff, {youtput, samplesToDo}, updateState, mTemp.data()+1); - if(updateState) LIKELY mDelayDT = mTemp[samplesToDo]; + process(mFilter1DT, Filter1Coeff, youtput.first(samplesToDo), updateState, mTemp); /* Precompute j*S and store in youtput. */ - mFilter2S.process(Filter2Coeff, {mS.data(), samplesToDo}, updateState, youtput); + if(mFirstRun) processOne(mFilter2S, Filter2Coeff, mS[0]); + process(mFilter2S, Filter2Coeff, al::span{mS}.subspan(1, samplesToDo), updateState, youtput); /* Y = 0.795968*D - 0.676392*T + j(0.186633*S) */ - for(size_t i{0};i < samplesToDo;++i) - youtput[i] = mTemp[i] + 0.186633f*youtput[i]; - + std::transform(mTemp.begin(), mTemp.begin()+samplesToDo, youtput.begin(), youtput.begin(), + [](const float dt, const float js) noexcept { return dt + 0.186633f*js; }); if(samples.size() > 3) { - float *RESTRICT zoutput{al::assume_aligned<16>(samples[3])}; + const auto zoutput = al::span{al::assume_aligned<16>(samples[3]), samplesToDo}; /* Apply filter1 to Q and store in mTemp. */ - mTemp[0] = mDelayQ; - mFilter1Q.process(Filter1Coeff, {zoutput, samplesToDo}, updateState, mTemp.data()+1); - if(updateState) LIKELY mDelayQ = mTemp[samplesToDo]; + process(mFilter1Q, Filter1Coeff, zoutput, updateState, mTemp); /* Z = 1.023332*Q */ - for(size_t i{0};i < samplesToDo;++i) - zoutput[i] = 1.023332f*mTemp[i]; + std::transform(mTemp.begin(), mTemp.end(), zoutput.begin(), + [](const float q) noexcept { return 1.023332f*q; }); } + + mFirstRun = false; } @@ -374,9 +546,9 @@ void UhjDecoderIIR::decode(const al::span samples, const size_t samplesT * S = Left + Right * D = Left - Right * - * W = 0.6098637*S - 0.6896511*j*w*D - * X = 0.8624776*S + 0.7626955*j*w*D - * Y = 1.6822415*w*D - 0.2156194*j*S + * W = 0.6098637*S + 0.6896511*j*w*D + * X = 0.8624776*S - 0.7626955*j*w*D + * Y = 1.6822415*w*D + 0.2156194*j*S * * where j is a +90 degree phase shift. w is a variable control for the * resulting stereo width, with the range 0 <= w <= 0.7. @@ -387,16 +559,16 @@ void UhjStereoDecoder::decode(const al::span samples, const size_t sa { static_assert(sInputPadding <= sMaxPadding, "Filter padding is too large"); - const auto &PShift = GetPhaseShifter::Get(); + constexpr auto &PShift = PShifter; ASSUME(samplesToDo > 0); + ASSUME(samplesToDo <= BufferLineSize); { - const float *RESTRICT left{al::assume_aligned<16>(samples[0])}; - const float *RESTRICT right{al::assume_aligned<16>(samples[1])}; + const auto left = al::span{al::assume_aligned<16>(samples[0]), samplesToDo+sInputPadding}; + const auto right = al::span{al::assume_aligned<16>(samples[1]), samplesToDo+sInputPadding}; - for(size_t i{0};i < samplesToDo+sInputPadding;++i) - mS[i] = left[i] + right[i]; + std::transform(left.begin(), left.end(), right.begin(), mS.begin(), std::plus{}); /* Pre-apply the width factor to the difference signal D. Smoothly * interpolate when it changes. @@ -405,53 +577,60 @@ void UhjStereoDecoder::decode(const al::span samples, const size_t sa const float wcurrent{(mCurrentWidth < 0.0f) ? wtarget : mCurrentWidth}; if(wtarget == wcurrent || !updateState) { - for(size_t i{0};i < samplesToDo+sInputPadding;++i) - mD[i] = (left[i] - right[i]) * wcurrent; + std::transform(left.begin(), left.end(), right.begin(), mD.begin(), + [wcurrent](const float l, const float r) noexcept { return (l-r) * wcurrent; }); mCurrentWidth = wcurrent; } else { const float wstep{(wtarget - wcurrent) / static_cast(samplesToDo)}; float fi{0.0f}; - for(size_t i{0};i < samplesToDo;++i) - { - mD[i] = (left[i] - right[i]) * (wcurrent + wstep*fi); - fi += 1.0f; - } - for(size_t i{samplesToDo};i < samplesToDo+sInputPadding;++i) - mD[i] = (left[i] - right[i]) * wtarget; + + const auto lfade = left.first(samplesToDo); + auto dstore = std::transform(lfade.begin(), lfade.begin(), right.begin(), mD.begin(), + [wcurrent,wstep,&fi](const float l, const float r) noexcept + { + const float ret{(l-r) * (wcurrent + wstep*fi)}; + fi += 1.0f; + return ret; + }); + + const auto lend = left.subspan(samplesToDo); + const auto rend = right.subspan(samplesToDo); + std::transform(lend.begin(), lend.end(), rend.begin(), dstore, + [wtarget](const float l, const float r) noexcept { return (l-r) * wtarget; }); mCurrentWidth = wtarget; } } - float *RESTRICT woutput{al::assume_aligned<16>(samples[0])}; - float *RESTRICT xoutput{al::assume_aligned<16>(samples[1])}; - float *RESTRICT youtput{al::assume_aligned<16>(samples[2])}; + const auto woutput = al::span{al::assume_aligned<16>(samples[0]), samplesToDo}; + const auto xoutput = al::span{al::assume_aligned<16>(samples[1]), samplesToDo}; + const auto youtput = al::span{al::assume_aligned<16>(samples[2]), samplesToDo}; /* Precompute j*D and store in xoutput. */ auto tmpiter = std::copy(mDTHistory.cbegin(), mDTHistory.cend(), mTemp.begin()); std::copy_n(mD.cbegin(), samplesToDo+sInputPadding, tmpiter); if(updateState) LIKELY std::copy_n(mTemp.cbegin()+samplesToDo, mDTHistory.size(), mDTHistory.begin()); - PShift.process({xoutput, samplesToDo}, mTemp.data()); + PShift.process(xoutput, mTemp); - /* W = 0.6098637*S - 0.6896511*j*w*D */ - for(size_t i{0};i < samplesToDo;++i) - woutput[i] = 0.6098637f*mS[i] - 0.6896511f*xoutput[i]; - /* X = 0.8624776*S + 0.7626955*j*w*D */ - for(size_t i{0};i < samplesToDo;++i) - xoutput[i] = 0.8624776f*mS[i] + 0.7626955f*xoutput[i]; + /* W = 0.6098637*S + 0.6896511*j*w*D */ + std::transform(mS.begin(), mS.begin()+samplesToDo, xoutput.begin(), woutput.begin(), + [](const float s, const float jd) noexcept { return 0.6098637f*s + 0.6896511f*jd; }); + /* X = 0.8624776*S - 0.7626955*j*w*D */ + std::transform(mS.begin(), mS.begin()+samplesToDo, xoutput.begin(), xoutput.begin(), + [](const float s, const float jd) noexcept { return 0.8624776f*s - 0.7626955f*jd; }); /* Precompute j*S and store in youtput. */ tmpiter = std::copy(mSHistory.cbegin(), mSHistory.cend(), mTemp.begin()); std::copy_n(mS.cbegin(), samplesToDo+sInputPadding, tmpiter); if(updateState) LIKELY std::copy_n(mTemp.cbegin()+samplesToDo, mSHistory.size(), mSHistory.begin()); - PShift.process({youtput, samplesToDo}, mTemp.data()); + PShift.process(youtput, mTemp); - /* Y = 1.6822415*w*D - 0.2156194*j*S */ - for(size_t i{0};i < samplesToDo;++i) - youtput[i] = 1.6822415f*mD[i] - 0.2156194f*youtput[i]; + /* Y = 1.6822415*w*D + 0.2156194*j*S */ + std::transform(mD.begin(), mD.begin()+samplesToDo, youtput.begin(), youtput.begin(), + [](const float d, const float js) noexcept { return 1.6822415f*d + 0.2156194f*js; }); } void UhjStereoDecoderIIR::decode(const al::span samples, const size_t samplesToDo, @@ -460,13 +639,13 @@ void UhjStereoDecoderIIR::decode(const al::span samples, const size_t sa static_assert(sInputPadding <= sMaxPadding, "Filter padding is too large"); ASSUME(samplesToDo > 0); + ASSUME(samplesToDo <= BufferLineSize); { - const float *RESTRICT left{al::assume_aligned<16>(samples[0])}; - const float *RESTRICT right{al::assume_aligned<16>(samples[1])}; + const auto left = al::span{al::assume_aligned<16>(samples[0]), samplesToDo+sInputPadding}; + const auto right = al::span{al::assume_aligned<16>(samples[1]), samplesToDo+sInputPadding}; - for(size_t i{0};i < samplesToDo;++i) - mS[i] = left[i] + right[i]; + std::transform(left.begin(), left.end(), right.begin(), mS.begin(), std::plus{}); /* Pre-apply the width factor to the difference signal D. Smoothly * interpolate when it changes. @@ -475,53 +654,63 @@ void UhjStereoDecoderIIR::decode(const al::span samples, const size_t sa const float wcurrent{(mCurrentWidth < 0.0f) ? wtarget : mCurrentWidth}; if(wtarget == wcurrent || !updateState) { - for(size_t i{0};i < samplesToDo;++i) - mD[i] = (left[i] - right[i]) * wcurrent; + std::transform(left.begin(), left.end(), right.begin(), mD.begin(), + [wcurrent](const float l, const float r) noexcept + { return (l-r) * wcurrent; }); mCurrentWidth = wcurrent; } else { const float wstep{(wtarget - wcurrent) / static_cast(samplesToDo)}; float fi{0.0f}; - for(size_t i{0};i < samplesToDo;++i) - { - mD[i] = (left[i] - right[i]) * (wcurrent + wstep*fi); - fi += 1.0f; - } + + const auto lfade = left.first(samplesToDo); + auto dstore = std::transform(lfade.begin(), lfade.begin(), right.begin(), mD.begin(), + [wcurrent,wstep,&fi](const float l, const float r) noexcept + { + const float ret{(l-r) * (wcurrent + wstep*fi)}; + fi += 1.0f; + return ret; + }); + + const auto lend = left.subspan(samplesToDo); + const auto rend = right.subspan(samplesToDo); + std::transform(lend.begin(), lend.end(), rend.begin(), dstore, + [wtarget](const float l, const float r) noexcept { return (l-r) * wtarget; }); mCurrentWidth = wtarget; } } - float *RESTRICT woutput{al::assume_aligned<16>(samples[0])}; - float *RESTRICT xoutput{al::assume_aligned<16>(samples[1])}; - float *RESTRICT youtput{al::assume_aligned<16>(samples[2])}; + const auto woutput = al::span{al::assume_aligned<16>(samples[0]), samplesToDo}; + const auto xoutput = al::span{al::assume_aligned<16>(samples[1]), samplesToDo}; + const auto youtput = al::span{al::assume_aligned<16>(samples[2]), samplesToDo}; /* Apply filter1 to S and store in mTemp. */ - mTemp[0] = mDelayS; - mFilter1S.process(Filter1Coeff, {mS.data(), samplesToDo}, updateState, mTemp.data()+1); - if(updateState) LIKELY mDelayS = mTemp[samplesToDo]; + process(mFilter1S, Filter1Coeff, al::span{mS}.first(samplesToDo), updateState, mTemp); /* Precompute j*D and store in xoutput. */ - mFilter2D.process(Filter2Coeff, {mD.data(), samplesToDo}, updateState, xoutput); + if(mFirstRun) processOne(mFilter2D, Filter2Coeff, mD[0]); + process(mFilter2D, Filter2Coeff, al::span{mD}.subspan(1, samplesToDo), updateState, xoutput); - /* W = 0.6098637*S - 0.6896511*j*w*D */ - for(size_t i{0};i < samplesToDo;++i) - woutput[i] = 0.6098637f*mTemp[i] - 0.6896511f*xoutput[i]; - /* X = 0.8624776*S + 0.7626955*j*w*D */ - for(size_t i{0};i < samplesToDo;++i) - xoutput[i] = 0.8624776f*mTemp[i] + 0.7626955f*xoutput[i]; + /* W = 0.6098637*S + 0.6896511*j*w*D */ + std::transform(mTemp.begin(), mTemp.begin()+samplesToDo, xoutput.begin(), woutput.begin(), + [](const float s, const float jd) noexcept { return 0.6098637f*s + 0.6896511f*jd; }); + /* X = 0.8624776*S - 0.7626955*j*w*D */ + std::transform(mTemp.begin(), mTemp.begin()+samplesToDo, xoutput.begin(), xoutput.begin(), + [](const float s, const float jd) noexcept { return 0.8624776f*s - 0.7626955f*jd; }); /* Precompute j*S and store in youtput. */ - mFilter2S.process(Filter2Coeff, {mS.data(), samplesToDo}, updateState, youtput); + if(mFirstRun) processOne(mFilter2S, Filter2Coeff, mS[0]); + process(mFilter2S, Filter2Coeff, al::span{mS}.subspan(1, samplesToDo), updateState, youtput); /* Apply filter1 to D and store in mTemp. */ - mTemp[0] = mDelayD; - mFilter1D.process(Filter1Coeff, {mD.data(), samplesToDo}, updateState, mTemp.data()+1); - if(updateState) LIKELY mDelayD = mTemp[samplesToDo]; + process(mFilter1D, Filter1Coeff, al::span{mD}.first(samplesToDo), updateState, mTemp); - /* Y = 1.6822415*w*D - 0.2156194*j*S */ - for(size_t i{0};i < samplesToDo;++i) - youtput[i] = 1.6822415f*mTemp[i] - 0.2156194f*youtput[i]; + /* Y = 1.6822415*w*D + 0.2156194*j*S */ + std::transform(mTemp.begin(), mTemp.begin()+samplesToDo, youtput.begin(), youtput.begin(), + [](const float d, const float js) noexcept { return 1.6822415f*d + 0.2156194f*js; }); + + mFirstRun = false; } diff --git a/3rdparty/openal/core/uhjfilter.h b/3rdparty/openal/core/uhjfilter.h index df30809444b7..34ea1dfa09c1 100644 --- a/3rdparty/openal/core/uhjfilter.h +++ b/3rdparty/openal/core/uhjfilter.h @@ -2,42 +2,47 @@ #define CORE_UHJFILTER_H #include +#include +#include -#include "almalloc.h" #include "alspan.h" #include "bufferline.h" +#include "opthelpers.h" -static constexpr size_t UhjLength256{256}; -static constexpr size_t UhjLength512{512}; +inline constexpr std::size_t UhjLength256{256}; +inline constexpr std::size_t UhjLength512{512}; -enum class UhjQualityType : uint8_t { +enum class UhjQualityType : std::uint8_t { IIR = 0, FIR256, FIR512, Default = IIR }; -extern UhjQualityType UhjDecodeQuality; -extern UhjQualityType UhjEncodeQuality; +inline UhjQualityType UhjDecodeQuality{UhjQualityType::Default}; +inline UhjQualityType UhjEncodeQuality{UhjQualityType::Default}; struct UhjAllPassFilter { struct AllPassState { /* Last two delayed components for direct form II. */ - float z[2]; + std::array z{}; }; std::array mState; - - void process(const al::span coeffs, const al::span src, - const bool update, float *RESTRICT dst); }; -struct UhjEncoderBase { +struct SIMDALIGN UhjEncoderBase { + UhjEncoderBase() = default; + UhjEncoderBase(const UhjEncoderBase&) = delete; + UhjEncoderBase(UhjEncoderBase&&) = delete; virtual ~UhjEncoderBase() = default; - virtual size_t getDelay() noexcept = 0; + void operator=(const UhjEncoderBase&) = delete; + void operator=(UhjEncoderBase&&) = delete; + + virtual std::size_t getDelay() noexcept = 0; /** * Encodes a 2-channel UHJ (stereo-compatible) signal from a B-Format input @@ -45,12 +50,15 @@ struct UhjEncoderBase { * with an additional +3dB boost). */ virtual void encode(float *LeftOut, float *RightOut, - const al::span InSamples, const size_t SamplesToDo) = 0; + const al::span InSamples, const std::size_t SamplesToDo) = 0; }; -template +template struct UhjEncoder final : public UhjEncoderBase { - static constexpr size_t sFilterDelay{N/2}; + static constexpr std::size_t sFftLength{256}; + static constexpr std::size_t sSegmentSize{sFftLength/2}; + static constexpr std::size_t sNumSegments{N/sSegmentSize}; + static constexpr std::size_t sFilterDelay{N/2 + sSegmentSize}; /* Delays and processing storage for the input signal. */ alignas(16) std::array mW{}; @@ -60,15 +68,16 @@ struct UhjEncoder final : public UhjEncoderBase { alignas(16) std::array mS{}; alignas(16) std::array mD{}; - /* History and temp storage for the FIR filter. New samples should be - * written to index sFilterDelay*2 - 1. - */ - static constexpr size_t sWXInOffset{sFilterDelay*2 - 1}; - alignas(16) std::array mWX{}; + /* History and temp storage for the convolution filter. */ + std::size_t mFifoPos{}, mCurrentSegment{}; + alignas(16) std::array mWXInOut{}; + alignas(16) std::array mFftBuffer{}; + alignas(16) std::array mWorkData{}; + alignas(16) std::array mWXHistory{}; alignas(16) std::array,2> mDirectDelay{}; - size_t getDelay() noexcept override { return sFilterDelay; } + std::size_t getDelay() noexcept override { return sFilterDelay; } /** * Encodes a 2-channel UHJ (stereo-compatible) signal from a B-Format input @@ -76,13 +85,11 @@ struct UhjEncoder final : public UhjEncoderBase { * with an additional +3dB boost). */ void encode(float *LeftOut, float *RightOut, const al::span InSamples, - const size_t SamplesToDo) override; - - DEF_NEWDEL(UhjEncoder) + const std::size_t SamplesToDo) final; }; struct UhjEncoderIIR final : public UhjEncoderBase { - static constexpr size_t sFilterDelay{1}; + static constexpr std::size_t sFilterDelay{1}; /* Processing storage for the input signal. */ alignas(16) std::array mS{}; @@ -98,7 +105,7 @@ struct UhjEncoderIIR final : public UhjEncoderBase { std::array mFilter1Direct; std::array mDirectDelay{}; - size_t getDelay() noexcept override { return sFilterDelay; } + std::size_t getDelay() noexcept override { return sFilterDelay; } /** * Encodes a 2-channel UHJ (stereo-compatible) signal from a B-Format input @@ -106,22 +113,26 @@ struct UhjEncoderIIR final : public UhjEncoderBase { * with an additional +3dB boost). */ void encode(float *LeftOut, float *RightOut, const al::span InSamples, - const size_t SamplesToDo) override; - - DEF_NEWDEL(UhjEncoderIIR) + const std::size_t SamplesToDo) final; }; -struct DecoderBase { - static constexpr size_t sMaxPadding{256}; +struct SIMDALIGN DecoderBase { + static constexpr std::size_t sMaxPadding{256}; /* For 2-channel UHJ, shelf filters should use these LF responses. */ static constexpr float sWLFScale{0.661f}; static constexpr float sXYLFScale{1.293f}; + DecoderBase() = default; + DecoderBase(const DecoderBase&) = delete; + DecoderBase(DecoderBase&&) = delete; virtual ~DecoderBase() = default; - virtual void decode(const al::span samples, const size_t samplesToDo, + void operator=(const DecoderBase&) = delete; + void operator=(DecoderBase&&) = delete; + + virtual void decode(const al::span samples, const std::size_t samplesToDo, const bool updateState) = 0; /** @@ -131,10 +142,10 @@ struct DecoderBase { float mWidthControl{0.593f}; }; -template +template struct UhjDecoder final : public DecoderBase { /* The number of extra sample frames needed for input. */ - static constexpr size_t sInputPadding{N/2}; + static constexpr std::size_t sInputPadding{N/2}; alignas(16) std::array mS{}; alignas(16) std::array mD{}; @@ -153,24 +164,23 @@ struct UhjDecoder final : public DecoderBase { * reconstructed from 2-channel UHJ should not be run through a normal * B-Format decoder, as it needs different shelf filters. */ - void decode(const al::span samples, const size_t samplesToDo, - const bool updateState) override; - - DEF_NEWDEL(UhjDecoder) + void decode(const al::span samples, const std::size_t samplesToDo, + const bool updateState) final; }; struct UhjDecoderIIR final : public DecoderBase { - /* FIXME: These IIR decoder filters actually have a 1-sample delay on the - * non-filtered components, which is not reflected in the source latency - * value. sInputPadding is 0, however, because it doesn't need any extra - * input samples. + /* These IIR decoder filters normally have a 1-sample delay on the non- + * filtered components. However, the filtered components are made to skip + * the first output sample and take one future sample, which puts it ahead + * by one sample. The first filtered output sample is cut to align it with + * the first non-filtered sample, similar to the FIR filters. */ - static constexpr size_t sInputPadding{0}; + static constexpr std::size_t sInputPadding{1}; - alignas(16) std::array mS{}; - alignas(16) std::array mD{}; - alignas(16) std::array mTemp{}; - float mDelayS{}, mDelayDT{}, mDelayQ{}; + bool mFirstRun{true}; + alignas(16) std::array mS{}; + alignas(16) std::array mD{}; + alignas(16) std::array mTemp{}; UhjAllPassFilter mFilter1S; UhjAllPassFilter mFilter2DT; @@ -178,15 +188,13 @@ struct UhjDecoderIIR final : public DecoderBase { UhjAllPassFilter mFilter2S; UhjAllPassFilter mFilter1Q; - void decode(const al::span samples, const size_t samplesToDo, - const bool updateState) override; - - DEF_NEWDEL(UhjDecoderIIR) + void decode(const al::span samples, const std::size_t samplesToDo, + const bool updateState) final; }; -template +template struct UhjStereoDecoder final : public DecoderBase { - static constexpr size_t sInputPadding{N/2}; + static constexpr std::size_t sInputPadding{N/2}; float mCurrentWidth{-1.0f}; @@ -204,31 +212,27 @@ struct UhjStereoDecoder final : public DecoderBase { * should contain 3 channels, the first two being the left and right stereo * channels, and the third left empty. */ - void decode(const al::span samples, const size_t samplesToDo, - const bool updateState) override; - - DEF_NEWDEL(UhjStereoDecoder) + void decode(const al::span samples, const std::size_t samplesToDo, + const bool updateState) final; }; struct UhjStereoDecoderIIR final : public DecoderBase { - static constexpr size_t sInputPadding{0}; + static constexpr std::size_t sInputPadding{1}; + bool mFirstRun{true}; float mCurrentWidth{-1.0f}; - alignas(16) std::array mS{}; - alignas(16) std::array mD{}; - alignas(16) std::array mTemp{}; - float mDelayS{}, mDelayD{}; + alignas(16) std::array mS{}; + alignas(16) std::array mD{}; + alignas(16) std::array mTemp{}; UhjAllPassFilter mFilter1S; UhjAllPassFilter mFilter2D; UhjAllPassFilter mFilter1D; UhjAllPassFilter mFilter2S; - void decode(const al::span samples, const size_t samplesToDo, - const bool updateState) override; - - DEF_NEWDEL(UhjStereoDecoderIIR) + void decode(const al::span samples, const std::size_t samplesToDo, + const bool updateState) final; }; #endif /* CORE_UHJFILTER_H */ diff --git a/3rdparty/openal/core/uiddefs.cpp b/3rdparty/openal/core/uiddefs.cpp index 9471bba5cfbe..e52a9ae33c88 100644 --- a/3rdparty/openal/core/uiddefs.cpp +++ b/3rdparty/openal/core/uiddefs.cpp @@ -1,17 +1,13 @@ #include "config.h" - +#include "config_backends.h" #ifndef AL_NO_UID_DEFS -#if defined(HAVE_GUIDDEF_H) || defined(HAVE_INITGUID_H) +#if defined(HAVE_GUIDDEF_H) #define INITGUID #include -#ifdef HAVE_GUIDDEF_H #include -#else -#include -#endif DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80,0x00, 0x00,0xaa,0x00,0x38,0x9b,0x71); DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80,0x00, 0x00,0xaa,0x00,0x38,0x9b,0x71); @@ -20,7 +16,7 @@ DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf,0x08, 0x0 DEFINE_GUID(CLSID_MMDeviceEnumerator, 0xbcde0395, 0xe52f, 0x467c, 0x8e,0x3d, 0xc4,0x57,0x92,0x91,0x69,0x2e); -#if defined(HAVE_WASAPI) && !defined(ALSOFT_UWP) +#if HAVE_WASAPI && !ALSOFT_UWP #include #include #include diff --git a/3rdparty/openal/core/voice.cpp b/3rdparty/openal/core/voice.cpp index b8acc7a66ef8..692e42a16371 100644 --- a/3rdparty/openal/core/voice.cpp +++ b/3rdparty/openal/core/voice.cpp @@ -1,5 +1,6 @@ #include "config.h" +#include "config_simd.h" #include "voice.h" @@ -9,11 +10,11 @@ #include #include #include +#include #include #include #include #include -#include #include #include @@ -42,45 +43,44 @@ #include "voice_change.h" struct CTag; -#ifdef HAVE_SSE +#if HAVE_SSE struct SSETag; #endif -#ifdef HAVE_NEON +#if HAVE_NEON struct NEONTag; #endif -static_assert(!(sizeof(DeviceBase::MixerBufferLine)&15), - "DeviceBase::MixerBufferLine must be a multiple of 16 bytes"); +static_assert(!(DeviceBase::MixerLineSize&3), "MixerLineSize must be a multiple of 4"); static_assert(!(MaxResamplerEdge&3), "MaxResamplerEdge is not a multiple of 4"); static_assert((BufferLineSize-1)/MaxPitch > 0, "MaxPitch is too large for BufferLineSize!"); static_assert((INT_MAX>>MixerFracBits)/MaxPitch > BufferLineSize, "MaxPitch and/or BufferLineSize are too large for MixerFracBits!"); -Resampler ResamplerDefault{Resampler::Cubic}; - namespace { using uint = unsigned int; using namespace std::chrono; +using namespace std::string_view_literals; -using HrtfMixerFunc = void(*)(const float *InSamples, float2 *AccumSamples, const uint IrSize, - const MixHrtfFilter *hrtfparams, const size_t BufferSize); -using HrtfMixerBlendFunc = void(*)(const float *InSamples, float2 *AccumSamples, - const uint IrSize, const HrtfFilter *oldparams, const MixHrtfFilter *newparams, - const size_t BufferSize); +using HrtfMixerFunc = void(*)(const al::span InSamples, + const al::span AccumSamples, const uint IrSize, const MixHrtfFilter *hrtfparams, + const size_t SamplesToDo); +using HrtfMixerBlendFunc = void(*)(const al::span InSamples, + const al::span AccumSamples, const uint IrSize, const HrtfFilter *oldparams, + const MixHrtfFilter *newparams, const size_t SamplesToDo); HrtfMixerFunc MixHrtfSamples{MixHrtf_}; HrtfMixerBlendFunc MixHrtfBlendSamples{MixHrtfBlend_}; inline MixerOutFunc SelectMixer() { -#ifdef HAVE_NEON +#if HAVE_NEON if((CPUCapFlags&CPU_CAP_NEON)) return Mix_; #endif -#ifdef HAVE_SSE +#if HAVE_SSE if((CPUCapFlags&CPU_CAP_SSE)) return Mix_; #endif @@ -89,11 +89,11 @@ inline MixerOutFunc SelectMixer() inline MixerOneFunc SelectMixerOne() { -#ifdef HAVE_NEON +#if HAVE_NEON if((CPUCapFlags&CPU_CAP_NEON)) return Mix_; #endif -#ifdef HAVE_SSE +#if HAVE_SSE if((CPUCapFlags&CPU_CAP_SSE)) return Mix_; #endif @@ -102,11 +102,11 @@ inline MixerOneFunc SelectMixerOne() inline HrtfMixerFunc SelectHrtfMixer() { -#ifdef HAVE_NEON +#if HAVE_NEON if((CPUCapFlags&CPU_CAP_NEON)) return MixHrtf_; #endif -#ifdef HAVE_SSE +#if HAVE_SSE if((CPUCapFlags&CPU_CAP_SSE)) return MixHrtf_; #endif @@ -115,11 +115,11 @@ inline HrtfMixerFunc SelectHrtfMixer() inline HrtfMixerBlendFunc SelectHrtfBlendMixer() { -#ifdef HAVE_NEON +#if HAVE_NEON if((CPUCapFlags&CPU_CAP_NEON)) return MixHrtfBlend_; #endif -#ifdef HAVE_SSE +#if HAVE_SSE if((CPUCapFlags&CPU_CAP_SSE)) return MixHrtfBlend_; #endif @@ -128,42 +128,50 @@ inline HrtfMixerBlendFunc SelectHrtfBlendMixer() } // namespace -void Voice::InitMixer(std::optional resampler) +void Voice::InitMixer(std::optional resopt) { - if(resampler) + if(resopt) { struct ResamplerEntry { - const char name[16]; + const std::string_view name; const Resampler resampler; }; - constexpr ResamplerEntry ResamplerList[]{ - { "none", Resampler::Point }, - { "point", Resampler::Point }, - { "linear", Resampler::Linear }, - { "cubic", Resampler::Cubic }, - { "bsinc12", Resampler::BSinc12 }, - { "fast_bsinc12", Resampler::FastBSinc12 }, - { "bsinc24", Resampler::BSinc24 }, - { "fast_bsinc24", Resampler::FastBSinc24 }, + constexpr std::array ResamplerList{ + ResamplerEntry{"none"sv, Resampler::Point}, + ResamplerEntry{"point"sv, Resampler::Point}, + ResamplerEntry{"linear"sv, Resampler::Linear}, + ResamplerEntry{"spline"sv, Resampler::Spline}, + ResamplerEntry{"gaussian"sv, Resampler::Gaussian}, + ResamplerEntry{"bsinc12"sv, Resampler::BSinc12}, + ResamplerEntry{"fast_bsinc12"sv, Resampler::FastBSinc12}, + ResamplerEntry{"bsinc24"sv, Resampler::BSinc24}, + ResamplerEntry{"fast_bsinc24"sv, Resampler::FastBSinc24}, }; - const char *str{resampler->c_str()}; - if(al::strcasecmp(str, "bsinc") == 0) + std::string_view resampler{*resopt}; + + if (al::case_compare(resampler, "cubic"sv) == 0) + { + WARN("Resampler option \"{}\" is deprecated, using spline", *resopt); + resampler = "spline"sv; + } + else if(al::case_compare(resampler, "sinc4"sv) == 0 + || al::case_compare(resampler, "sinc8"sv) == 0) { - WARN("Resampler option \"%s\" is deprecated, using bsinc12\n", str); - str = "bsinc12"; + WARN("Resampler option \"{}\" is deprecated, using gaussian", *resopt); + resampler = "gaussian"sv; } - else if(al::strcasecmp(str, "sinc4") == 0 || al::strcasecmp(str, "sinc8") == 0) + else if(al::case_compare(resampler, "bsinc"sv) == 0) { - WARN("Resampler option \"%s\" is deprecated, using cubic\n", str); - str = "cubic"; + WARN("Resampler option \"{}\" is deprecated, using bsinc12", *resopt); + resampler = "bsinc12"sv; } - auto iter = std::find_if(std::begin(ResamplerList), std::end(ResamplerList), - [str](const ResamplerEntry &entry) -> bool - { return al::strcasecmp(str, entry.name) == 0; }); - if(iter == std::end(ResamplerList)) - ERR("Invalid resampler: %s\n", str); + auto iter = std::find_if(ResamplerList.begin(), ResamplerList.end(), + [resampler](const ResamplerEntry &entry) -> bool + { return al::case_compare(resampler, entry.name) == 0; }); + if(iter == ResamplerList.end()) + ERR("Invalid resampler: {}", *resopt); else ResamplerDefault = iter->resampler; } @@ -178,7 +186,7 @@ void Voice::InitMixer(std::optional resampler) namespace { /* IMA ADPCM Stepsize table */ -constexpr int IMAStep_size[89] = { +constexpr std::array IMAStep_size{{ 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, @@ -188,35 +196,35 @@ constexpr int IMAStep_size[89] = { 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493,10442, 11487,12635,13899,15289,16818,18500,20350,22358,24633,27086,29794, 32767 -}; +}}; /* IMA4 ADPCM Codeword decode table */ -constexpr int IMA4Codeword[16] = { +constexpr std::array IMA4Codeword{{ 1, 3, 5, 7, 9, 11, 13, 15, -1,-3,-5,-7,-9,-11,-13,-15, -}; +}}; /* IMA4 ADPCM Step index adjust decode table */ -constexpr int IMA4Index_adjust[16] = { +constexpr std::arrayIMA4Index_adjust{{ -1,-1,-1,-1, 2, 4, 6, 8, -1,-1,-1,-1, 2, 4, 6, 8 -}; +}}; /* MSADPCM Adaption table */ -constexpr int MSADPCMAdaption[16] = { +constexpr std::array MSADPCMAdaption{{ 230, 230, 230, 230, 307, 409, 512, 614, 768, 614, 512, 409, 307, 230, 230, 230 -}; +}}; /* MSADPCM Adaption Coefficient tables */ -constexpr int MSADPCMAdaptionCoeff[7][2] = { - { 256, 0 }, - { 512, -256 }, - { 0, 0 }, - { 192, 64 }, - { 240, 0 }, - { 460, -208 }, - { 392, -232 } +constexpr std::array MSADPCMAdaptionCoeff{ + std::array{256, 0}, + std::array{512, -256}, + std::array{ 0, 0}, + std::array{192, 64}, + std::array{240, 0}, + std::array{460, -208}, + std::array{392, -232} }; @@ -224,9 +232,9 @@ void SendSourceStoppedEvent(ContextBase *context, uint id) { RingBuffer *ring{context->mAsyncEvents.get()}; auto evt_vec = ring->getWriteVector(); - if(evt_vec.first.len < 1) return; + if(evt_vec[0].len < 1) return; - auto &evt = InitAsyncEvent(evt_vec.first.buf); + auto &evt = InitAsyncEvent(evt_vec[0].buf); evt.mId = id; evt.mState = AsyncSrcState::Stop; @@ -234,8 +242,8 @@ void SendSourceStoppedEvent(ContextBase *context, uint id) } -const float *DoFilters(BiquadFilter &lpfilter, BiquadFilter &hpfilter, float *dst, - const al::span src, int type) +al::span DoFilters(BiquadFilter &lpfilter, BiquadFilter &hpfilter, + const al::span dst, const al::span src, int type) { switch(type) { @@ -247,71 +255,89 @@ const float *DoFilters(BiquadFilter &lpfilter, BiquadFilter &hpfilter, float *ds case AF_LowPass: lpfilter.process(src, dst); hpfilter.clear(); - return dst; + return dst.first(src.size()); case AF_HighPass: lpfilter.clear(); hpfilter.process(src, dst); - return dst; + return dst.first(src.size()); case AF_BandPass: DualBiquad{lpfilter, hpfilter}.process(src, dst); - return dst; + return dst.first(src.size()); } - return src.data(); + return src; } template -inline void LoadSamples(float *RESTRICT dstSamples, const std::byte *src, const size_t srcChan, - const size_t srcOffset, const size_t srcStep, const size_t /*samplesPerBlock*/, - const size_t samplesToLoad) noexcept +inline void LoadSamples(const al::span dstSamples, const al::span srcData, + const size_t srcChan, const size_t srcOffset, const size_t srcStep, + const size_t samplesPerBlock [[maybe_unused]]) noexcept { - constexpr size_t sampleSize{sizeof(typename al::FmtTypeTraits::Type)}; - auto s = src + (srcOffset*srcStep + srcChan)*sampleSize; - - al::LoadSampleArray(dstSamples, s, srcStep, samplesToLoad); + using TypeTraits = al::FmtTypeTraits; + using SampleType = typename TypeTraits::Type; + static constexpr size_t sampleSize{sizeof(SampleType)}; + assert(srcChan < srcStep); + auto converter = TypeTraits{}; + + al::span src{reinterpret_cast(srcData.data()), + srcData.size()/sampleSize}; + auto ssrc = src.cbegin() + ptrdiff_t(srcOffset*srcStep); + std::generate(dstSamples.begin(), dstSamples.end(), [&ssrc,srcChan,srcStep,converter] + { + auto ret = converter(ssrc[srcChan]); + ssrc += ptrdiff_t(srcStep); + return ret; + }); } template<> -inline void LoadSamples(float *RESTRICT dstSamples, const std::byte *src, +inline void LoadSamples(al::span dstSamples, al::span src, const size_t srcChan, const size_t srcOffset, const size_t srcStep, - const size_t samplesPerBlock, const size_t samplesToLoad) noexcept + const size_t samplesPerBlock) noexcept { + static constexpr int MaxStepIndex{static_cast(IMAStep_size.size()) - 1}; + + assert(srcStep > 0 || srcStep <= 2); + assert(srcChan < srcStep); + assert(samplesPerBlock > 1); const size_t blockBytes{((samplesPerBlock-1)/2 + 4)*srcStep}; /* Skip to the ADPCM block containing the srcOffset sample. */ - src += srcOffset/samplesPerBlock*blockBytes; + src = src.subspan(srcOffset/samplesPerBlock*blockBytes); /* Calculate how many samples need to be skipped in the block. */ size_t skip{srcOffset % samplesPerBlock}; /* NOTE: This could probably be optimized better. */ - size_t wrote{0}; - do { - static constexpr int MaxStepIndex{static_cast(std::size(IMAStep_size)) - 1}; + auto dst = dstSamples.begin(); + while(dst != dstSamples.end()) + { /* Each IMA4 block starts with a signed 16-bit sample, and a signed * 16-bit table index. The table index needs to be clamped. */ - int sample{int(src[srcChan*4]) | (int(src[srcChan*4 + 1]) << 8)}; + int sample{int(src[srcChan*4 + 0]) | (int(src[srcChan*4 + 1]) << 8)}; int index{int(src[srcChan*4 + 2]) | (int(src[srcChan*4 + 3]) << 8)}; + auto nibbleData = src.subspan((srcStep+srcChan)*4); + src = src.subspan(blockBytes); sample = (sample^0x8000) - 32768; - index = clampi((index^0x8000) - 32768, 0, MaxStepIndex); + index = std::clamp((index^0x8000) - 32768, 0, MaxStepIndex); if(skip == 0) { - dstSamples[wrote++] = static_cast(sample) / 32768.0f; - if(wrote == samplesToLoad) return; + *dst = static_cast(sample) / 32768.0f; + if(++dst == dstSamples.end()) return; } else --skip; - auto decode_sample = [&sample,&index](const uint nibble) + auto decode_sample = [&sample,&index](const uint8_t nibble) { - sample += IMA4Codeword[nibble] * IMAStep_size[index] / 8; - sample = clampi(sample, -32768, 32767); + sample += IMA4Codeword[nibble] * IMAStep_size[static_cast(index)] / 8; + sample = std::clamp(sample, -32768, 32767); index += IMA4Index_adjust[nibble]; - index = clampi(index, 0, MaxStepIndex); + index = std::clamp(index, 0, MaxStepIndex); return sample; }; @@ -324,7 +350,7 @@ inline void LoadSamples(float *RESTRICT dstSamples, const std::byte *sr * always be less than the block size). They need to be decoded despite * being ignored for proper state on the remaining samples. */ - const std::byte *nibbleData{src + (srcStep+srcChan)*4}; + static constexpr auto NibbleMask = std::byte{0xf}; size_t nibbleOffset{0}; const size_t startOffset{skip + 1}; for(;skip;--skip) @@ -334,61 +360,59 @@ inline void LoadSamples(float *RESTRICT dstSamples, const std::byte *sr const size_t byteOffset{wordOffset*srcStep + ((nibbleOffset>>1)&3u)}; ++nibbleOffset; - std::ignore = decode_sample(uint(nibbleData[byteOffset]>>byteShift) & 15u); + const auto nval = (nibbleData[byteOffset]>>byteShift) & NibbleMask; + std::ignore = decode_sample(al::to_underlying(nval)); } /* Second, decode the rest of the block and write to the output, until * the end of the block or the end of output. */ - const size_t todo{minz(samplesPerBlock-startOffset, samplesToLoad-wrote)}; - for(size_t i{0};i < todo;++i) + const size_t todo{std::min(samplesPerBlock-startOffset, size_t(dstSamples.end()-dst))}; + dst = std::generate_n(dst, todo, [&] { const size_t byteShift{(nibbleOffset&1) * 4}; const size_t wordOffset{(nibbleOffset>>1) & ~3_uz}; const size_t byteOffset{wordOffset*srcStep + ((nibbleOffset>>1)&3u)}; ++nibbleOffset; - const int result{decode_sample(uint(nibbleData[byteOffset]>>byteShift) & 15u)}; - dstSamples[wrote++] = static_cast(result) / 32768.0f; - } - if(wrote == samplesToLoad) - return; - - src += blockBytes; - } while(true); + const auto nval = (nibbleData[byteOffset]>>byteShift) & NibbleMask; + return static_cast(decode_sample(al::to_underlying(nval))) / 32768.0f; + }); + } } template<> -inline void LoadSamples(float *RESTRICT dstSamples, const std::byte *src, +inline void LoadSamples(al::span dstSamples, al::span src, const size_t srcChan, const size_t srcOffset, const size_t srcStep, - const size_t samplesPerBlock, const size_t samplesToLoad) noexcept + const size_t samplesPerBlock) noexcept { + assert(srcStep > 0 || srcStep <= 2); + assert(srcChan < srcStep); + assert(samplesPerBlock > 2); const size_t blockBytes{((samplesPerBlock-2)/2 + 7)*srcStep}; - src += srcOffset/samplesPerBlock*blockBytes; + src = src.subspan(srcOffset/samplesPerBlock*blockBytes); size_t skip{srcOffset % samplesPerBlock}; - size_t wrote{0}; - do { + auto dst = dstSamples.begin(); + while(dst != dstSamples.end()) + { /* Each MS ADPCM block starts with an 8-bit block predictor, used to * dictate how the two sample history values are mixed with the decoded * sample, and an initial signed 16-bit delta value which scales the * nibble sample value. This is followed by the two initial 16-bit * sample history values. */ - const std::byte *input{src}; - const uint8_t blockpred{std::min(uint8_t(input[srcChan]), uint8_t{6})}; - input += srcStep; - int delta{int(input[2*srcChan + 0]) | (int(input[2*srcChan + 1]) << 8)}; - input += srcStep*2; - - int sampleHistory[2]{}; - sampleHistory[0] = int(input[2*srcChan + 0]) | (int(input[2*srcChan + 1])<<8); - input += srcStep*2; - sampleHistory[1] = int(input[2*srcChan + 0]) | (int(input[2*srcChan + 1])<<8); - input += srcStep*2; - - const al::span coeffs{MSADPCMAdaptionCoeff[blockpred]}; + const uint8_t blockpred{std::min(uint8_t(src[srcChan]), uint8_t{6})}; + int delta{int(src[srcStep + 2*srcChan + 0]) | (int(src[srcStep + 2*srcChan + 1]) << 8)}; + + auto sampleHistory = std::array{ + int(src[3*srcStep + 2*srcChan + 0]) | (int(src[3*srcStep + 2*srcChan + 1])<<8), + int(src[5*srcStep + 2*srcChan + 0]) | (int(src[5*srcStep + 2*srcChan + 1])<<8)}; + const auto input = src.subspan(7*srcStep); + src = src.subspan(blockBytes); + + const auto coeffs = al::span{MSADPCMAdaptionCoeff[blockpred]}; delta = (delta^0x8000) - 32768; sampleHistory[0] = (sampleHistory[0]^0x8000) - 32768; sampleHistory[1] = (sampleHistory[1]^0x8000) - 32768; @@ -398,31 +422,31 @@ inline void LoadSamples(float *RESTRICT dstSamples, const std::byte */ if(skip == 0) { - dstSamples[wrote++] = static_cast(sampleHistory[1]) / 32768.0f; - if(wrote == samplesToLoad) return; - dstSamples[wrote++] = static_cast(sampleHistory[0]) / 32768.0f; - if(wrote == samplesToLoad) return; + *dst = static_cast(sampleHistory[1]) / 32768.0f; + if(++dst == dstSamples.end()) return; + *dst = static_cast(sampleHistory[0]) / 32768.0f; + if(++dst == dstSamples.end()) return; } else if(skip == 1) { --skip; - dstSamples[wrote++] = static_cast(sampleHistory[0]) / 32768.0f; - if(wrote == samplesToLoad) return; + *dst = static_cast(sampleHistory[0]) / 32768.0f; + if(++dst == dstSamples.end()) return; } else skip -= 2; - auto decode_sample = [&sampleHistory,&delta,coeffs](const int nibble) + auto decode_sample = [&sampleHistory,&delta,coeffs](const uint8_t nibble) { int pred{(sampleHistory[0]*coeffs[0] + sampleHistory[1]*coeffs[1]) / 256}; pred += ((nibble^0x08) - 0x08) * delta; - pred = clampi(pred, -32768, 32767); + pred = std::clamp(pred, -32768, 32767); sampleHistory[1] = sampleHistory[0]; sampleHistory[0] = pred; delta = (MSADPCMAdaption[nibble] * delta) / 256; - delta = maxi(16, delta); + delta = std::max(16, delta); return pred; }; @@ -430,6 +454,7 @@ inline void LoadSamples(float *RESTRICT dstSamples, const std::byte /* The rest of the block is a series of nibbles, interleaved per- * channel. First, skip samples. */ + static constexpr auto NibbleMask = std::byte{0xf}; const size_t startOffset{skip + 2}; size_t nibbleOffset{srcChan}; for(;skip;--skip) @@ -438,42 +463,40 @@ inline void LoadSamples(float *RESTRICT dstSamples, const std::byte const size_t byteShift{((nibbleOffset&1)^1) * 4}; nibbleOffset += srcStep; - std::ignore = decode_sample(int(input[byteOffset]>>byteShift) & 15); + const auto nval = (input[byteOffset]>>byteShift) & NibbleMask; + std::ignore = decode_sample(al::to_underlying(nval)); } /* Now decode the rest of the block, until the end of the block or the * dst buffer is filled. */ - const size_t todo{minz(samplesPerBlock-startOffset, samplesToLoad-wrote)}; - for(size_t j{0};j < todo;++j) + const size_t todo{std::min(samplesPerBlock-startOffset, size_t(dstSamples.end()-dst))}; + dst = std::generate_n(dst, todo, [&] { const size_t byteOffset{nibbleOffset>>1}; const size_t byteShift{((nibbleOffset&1)^1) * 4}; nibbleOffset += srcStep; - const int sample{decode_sample(int(input[byteOffset]>>byteShift) & 15)}; - dstSamples[wrote++] = static_cast(sample) / 32768.0f; - } - if(wrote == samplesToLoad) - return; - - src += blockBytes; - } while(true); + const auto nval = (input[byteOffset]>>byteShift) & NibbleMask; + return static_cast(decode_sample(al::to_underlying(nval))) / 32768.0f; + }); + } } -void LoadSamples(float *dstSamples, const std::byte *src, const size_t srcChan, - const size_t srcOffset, const FmtType srcType, const size_t srcStep, - const size_t samplesPerBlock, const size_t samplesToLoad) noexcept +void LoadSamples(const al::span dstSamples, const al::span src, + const size_t srcChan, const size_t srcOffset, const FmtType srcType, const size_t srcStep, + const size_t samplesPerBlock) noexcept { #define HANDLE_FMT(T) case T: \ LoadSamples(dstSamples, src, srcChan, srcOffset, srcStep, \ - samplesPerBlock, samplesToLoad); \ + samplesPerBlock); \ break switch(srcType) { HANDLE_FMT(FmtUByte); HANDLE_FMT(FmtShort); + HANDLE_FMT(FmtInt); HANDLE_FMT(FmtFloat); HANDLE_FMT(FmtDouble); HANDLE_FMT(FmtMulaw); @@ -486,26 +509,24 @@ void LoadSamples(float *dstSamples, const std::byte *src, const size_t srcChan, void LoadBufferStatic(VoiceBufferItem *buffer, VoiceBufferItem *bufferLoopItem, const size_t dataPosInt, const FmtType sampleType, const size_t srcChannel, - const size_t srcStep, size_t samplesLoaded, const size_t samplesToLoad, - float *voiceSamples) + const size_t srcStep, al::span voiceSamples) { if(!bufferLoopItem) { + float lastSample{0.0f}; /* Load what's left to play from the buffer */ if(buffer->mSampleLen > dataPosInt) LIKELY { const size_t buffer_remaining{buffer->mSampleLen - dataPosInt}; - const size_t remaining{minz(samplesToLoad-samplesLoaded, buffer_remaining)}; - LoadSamples(voiceSamples+samplesLoaded, buffer->mSamples, srcChannel, dataPosInt, - sampleType, srcStep, buffer->mBlockAlign, remaining); - samplesLoaded += remaining; + const size_t remaining{std::min(voiceSamples.size(), buffer_remaining)}; + LoadSamples(voiceSamples.first(remaining), buffer->mSamples, srcChannel, dataPosInt, + sampleType, srcStep, buffer->mBlockAlign); + lastSample = voiceSamples[remaining-1]; + voiceSamples = voiceSamples.subspan(remaining); } - if(const size_t toFill{samplesToLoad - samplesLoaded}) - { - auto srcsamples = voiceSamples + samplesLoaded; - std::fill_n(srcsamples, toFill, *(srcsamples-1)); - } + if(const size_t toFill{voiceSamples.size()}) + std::fill_n(voiceSamples.begin(), toFill, lastSample); } else { @@ -517,49 +538,47 @@ void LoadBufferStatic(VoiceBufferItem *buffer, VoiceBufferItem *bufferLoopItem, : (((dataPosInt-loopStart)%(loopEnd-loopStart)) + loopStart)}; /* Load what's left of this loop iteration */ - const size_t remaining{minz(samplesToLoad-samplesLoaded, loopEnd-dataPosInt)}; - LoadSamples(voiceSamples+samplesLoaded, buffer->mSamples, srcChannel, intPos, sampleType, - srcStep, buffer->mBlockAlign, remaining); - samplesLoaded += remaining; + const size_t remaining{std::min(voiceSamples.size(), loopEnd-dataPosInt)}; + LoadSamples(voiceSamples.first(remaining), buffer->mSamples, srcChannel, intPos, + sampleType, srcStep, buffer->mBlockAlign); + voiceSamples = voiceSamples.subspan(remaining); /* Load repeats of the loop to fill the buffer. */ const size_t loopSize{loopEnd - loopStart}; - while(const size_t toFill{minz(samplesToLoad - samplesLoaded, loopSize)}) + while(const size_t toFill{std::min(voiceSamples.size(), loopSize)}) { - LoadSamples(voiceSamples+samplesLoaded, buffer->mSamples, srcChannel, loopStart, - sampleType, srcStep, buffer->mBlockAlign, toFill); - samplesLoaded += toFill; + LoadSamples(voiceSamples.first(toFill), buffer->mSamples, srcChannel, loopStart, + sampleType, srcStep, buffer->mBlockAlign); + voiceSamples = voiceSamples.subspan(toFill); } } } void LoadBufferCallback(VoiceBufferItem *buffer, const size_t dataPosInt, const size_t numCallbackSamples, const FmtType sampleType, const size_t srcChannel, - const size_t srcStep, size_t samplesLoaded, const size_t samplesToLoad, float *voiceSamples) + const size_t srcStep, al::span voiceSamples) { - /* Load what's left to play from the buffer */ + float lastSample{0.0f}; if(numCallbackSamples > dataPosInt) LIKELY { - const size_t remaining{minz(samplesToLoad-samplesLoaded, numCallbackSamples-dataPosInt)}; - LoadSamples(voiceSamples+samplesLoaded, buffer->mSamples, srcChannel, dataPosInt, - sampleType, srcStep, buffer->mBlockAlign, remaining); - samplesLoaded += remaining; + const size_t remaining{std::min(voiceSamples.size(), numCallbackSamples-dataPosInt)}; + LoadSamples(voiceSamples.first(remaining), buffer->mSamples, srcChannel, dataPosInt, + sampleType, srcStep, buffer->mBlockAlign); + lastSample = voiceSamples[remaining-1]; + voiceSamples = voiceSamples.subspan(remaining); } - if(const size_t toFill{samplesToLoad - samplesLoaded}) - { - auto srcsamples = voiceSamples + samplesLoaded; - std::fill_n(srcsamples, toFill, *(srcsamples-1)); - } + if(const size_t toFill{voiceSamples.size()}) + std::fill_n(voiceSamples.begin(), toFill, lastSample); } void LoadBufferQueue(VoiceBufferItem *buffer, VoiceBufferItem *bufferLoopItem, size_t dataPosInt, const FmtType sampleType, const size_t srcChannel, - const size_t srcStep, size_t samplesLoaded, const size_t samplesToLoad, - float *voiceSamples) + const size_t srcStep, al::span voiceSamples) { + float lastSample{0.0f}; /* Crawl the buffer queue to fill in the temp buffer */ - while(buffer && samplesLoaded != samplesToLoad) + while(buffer && !voiceSamples.empty()) { if(dataPosInt >= buffer->mSampleLen) { @@ -569,48 +588,47 @@ void LoadBufferQueue(VoiceBufferItem *buffer, VoiceBufferItem *bufferLoopItem, continue; } - const size_t remaining{minz(samplesToLoad-samplesLoaded, buffer->mSampleLen-dataPosInt)}; - LoadSamples(voiceSamples+samplesLoaded, buffer->mSamples, srcChannel, dataPosInt, - sampleType, srcStep, buffer->mBlockAlign, remaining); + const size_t remaining{std::min(voiceSamples.size(), buffer->mSampleLen-dataPosInt)}; + LoadSamples(voiceSamples.first(remaining), buffer->mSamples, srcChannel, dataPosInt, + sampleType, srcStep, buffer->mBlockAlign); - samplesLoaded += remaining; - if(samplesLoaded == samplesToLoad) + lastSample = voiceSamples[remaining-1]; + voiceSamples = voiceSamples.subspan(remaining); + if(voiceSamples.empty()) break; dataPosInt = 0; buffer = buffer->mNext.load(std::memory_order_acquire); if(!buffer) buffer = bufferLoopItem; } - if(const size_t toFill{samplesToLoad - samplesLoaded}) - { - auto srcsamples = voiceSamples + samplesLoaded; - std::fill_n(srcsamples, toFill, *(srcsamples-1)); - } + if(const size_t toFill{voiceSamples.size()}) + std::fill_n(voiceSamples.begin(), toFill, lastSample); } -void DoHrtfMix(const float *samples, const uint DstBufferSize, DirectParams &parms, - const float TargetGain, const uint Counter, uint OutPos, const bool IsPlaying, - DeviceBase *Device) +void DoHrtfMix(const al::span samples, DirectParams &parms, const float TargetGain, + const size_t Counter, size_t OutPos, const bool IsPlaying, DeviceBase *Device) { const uint IrSize{Device->mIrSize}; - auto &HrtfSamples = Device->HrtfSourceData; - auto &AccumSamples = Device->HrtfAccumData; + const auto HrtfSamples = al::span{Device->ExtraSampleData}; + const auto AccumSamples = al::span{Device->HrtfAccumData}; /* Copy the HRTF history and new input samples into a temp buffer. */ auto src_iter = std::copy(parms.Hrtf.History.begin(), parms.Hrtf.History.end(), - std::begin(HrtfSamples)); - std::copy_n(samples, DstBufferSize, src_iter); + HrtfSamples.begin()); + std::copy_n(samples.begin(), samples.size(), src_iter); /* Copy the last used samples back into the history buffer for later. */ if(IsPlaying) LIKELY - std::copy_n(std::begin(HrtfSamples) + DstBufferSize, parms.Hrtf.History.size(), - parms.Hrtf.History.begin()); + { + const auto endsamples = HrtfSamples.subspan(samples.size(), parms.Hrtf.History.size()); + std::copy_n(endsamples.cbegin(), endsamples.size(), parms.Hrtf.History.begin()); + } /* If fading and this is the first mixing pass, fade between the IRs. */ - uint fademix{0u}; + size_t fademix{0}; if(Counter && OutPos == 0) { - fademix = minu(DstBufferSize, Counter); + fademix = std::min(samples.size(), Counter); float gain{TargetGain}; @@ -629,8 +647,8 @@ void DoHrtfMix(const float *samples, const uint DstBufferSize, DirectParams &par parms.Hrtf.Target.Coeffs, parms.Hrtf.Target.Delay, 0.0f, gain / static_cast(fademix)}; - MixHrtfBlendSamples(HrtfSamples, AccumSamples+OutPos, IrSize, &parms.Hrtf.Old, &hrtfparams, - fademix); + MixHrtfBlendSamples(HrtfSamples, AccumSamples.subspan(OutPos), IrSize, &parms.Hrtf.Old, + &hrtfparams, fademix); /* Update the old parameters with the result. */ parms.Hrtf.Old = parms.Hrtf.Target; @@ -638,15 +656,15 @@ void DoHrtfMix(const float *samples, const uint DstBufferSize, DirectParams &par OutPos += fademix; } - if(fademix < DstBufferSize) + if(fademix < samples.size()) { - const uint todo{DstBufferSize - fademix}; + const size_t todo{samples.size() - fademix}; float gain{TargetGain}; /* Interpolate the target gain if the gain fading lasts longer than * this mix. */ - if(Counter > DstBufferSize) + if(Counter > samples.size()) { const float a{static_cast(todo) / static_cast(Counter-fademix)}; gain = lerpf(parms.Hrtf.Old.Gain, TargetGain, a); @@ -657,37 +675,40 @@ void DoHrtfMix(const float *samples, const uint DstBufferSize, DirectParams &par parms.Hrtf.Target.Delay, parms.Hrtf.Old.Gain, (gain - parms.Hrtf.Old.Gain) / static_cast(todo)}; - MixHrtfSamples(HrtfSamples+fademix, AccumSamples+OutPos, IrSize, &hrtfparams, todo); + MixHrtfSamples(HrtfSamples.subspan(fademix), AccumSamples.subspan(OutPos), IrSize, + &hrtfparams, todo); /* Store the now-current gain for next time. */ parms.Hrtf.Old.Gain = gain; } } -void DoNfcMix(const al::span samples, FloatBufferLine *OutBuffer, DirectParams &parms, - const float *TargetGains, const uint Counter, const uint OutPos, DeviceBase *Device) +void DoNfcMix(const al::span samples, al::span OutBuffer, + DirectParams &parms, const al::span OutGains, + const uint Counter, const uint OutPos, DeviceBase *Device) { - using FilterProc = void (NfcFilter::*)(const al::span, float*); - static constexpr FilterProc NfcProcess[MaxAmbiOrder+1]{ - nullptr, &NfcFilter::process1, &NfcFilter::process2, &NfcFilter::process3}; + using FilterProc = void (NfcFilter::*)(const al::span, const al::span); + static constexpr std::array NfcProcess{{ + nullptr, &NfcFilter::process1, &NfcFilter::process2, &NfcFilter::process3}}; - float *CurrentGains{parms.Gains.Current.data()}; - MixSamples(samples, {OutBuffer, 1u}, CurrentGains, TargetGains, Counter, OutPos); - ++OutBuffer; - ++CurrentGains; - ++TargetGains; + MixSamples(samples, al::span{OutBuffer[0]}.subspan(OutPos), parms.Gains.Current[0], + OutGains[0], Counter); + OutBuffer = OutBuffer.subspan(1); + auto CurrentGains = al::span{parms.Gains.Current}.subspan(1); + auto TargetGains = OutGains.subspan(1); - const al::span nfcsamples{Device->NfcSampleData, samples.size()}; + const auto nfcsamples = al::span{Device->ExtraSampleData}.first(samples.size()); size_t order{1}; while(const size_t chancount{Device->NumChannelsPerOrder[order]}) { - (parms.NFCtrlFilter.*NfcProcess[order])(samples, nfcsamples.data()); - MixSamples(nfcsamples, {OutBuffer, chancount}, CurrentGains, TargetGains, Counter, OutPos); - OutBuffer += chancount; - CurrentGains += chancount; - TargetGains += chancount; + (parms.NFCtrlFilter.*NfcProcess[order])(samples, nfcsamples); + MixSamples(nfcsamples, OutBuffer.first(chancount), CurrentGains, TargetGains, Counter, + OutPos); if(++order == MaxAmbiOrder+1) break; + OutBuffer = OutBuffer.subspan(chancount); + CurrentGains = CurrentGains.subspan(chancount); + TargetGains = TargetGains.subspan(chancount); } } @@ -696,7 +717,7 @@ void DoNfcMix(const al::span samples, FloatBufferLine *OutBuffer, D void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds deviceTime, const uint SamplesToDo) { - static constexpr std::array SilentTarget{}; + static constexpr std::array SilentTarget{}; ASSUME(SamplesToDo > 0); @@ -750,17 +771,9 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi /* Get the number of samples ahead of the current time that output * should start at. Skip this update if it's beyond the output sample * count. - * - * Round the start position to a multiple of 4, which some mixers want. - * This makes the start time accurate to 4 samples. This could be made - * sample-accurate by forcing non-SIMD functions on the first run. */ - seconds::rep sampleOffset{duration_cast(diff * Device->Frequency).count()}; - sampleOffset = (sampleOffset+2) & ~seconds::rep{3}; - if(sampleOffset >= SamplesToDo) - return; - - OutPos = static_cast(sampleOffset); + OutPos = static_cast(round(diff * Device->mSampleRate).count()); + if(OutPos >= SamplesToDo) return; } /* Calculate the number of samples to mix, and the number of (resampled) @@ -772,34 +785,34 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi /* Get a span of pointers to hold the floating point, deinterlaced, * resampled buffer data to be mixed. */ - std::array SamplePointers; - const al::span MixingSamples{SamplePointers.data(), mChans.size()}; - auto get_bufferline = [](DeviceBase::MixerBufferLine &bufline) noexcept -> float* - { return bufline.data(); }; - std::transform(Device->mSampleData.end() - mChans.size(), Device->mSampleData.end(), - MixingSamples.begin(), get_bufferline); - - /* If there's a matching sample step and no phase offset, use a simple copy - * for resampling. - */ - const ResamplerFunc Resample{(increment == MixerFracOne && DataPosFrac == 0) - ? ResamplerFunc{[](const InterpState*, const float *RESTRICT src, uint, const uint, - const al::span dst) { std::copy_n(src, dst.size(), dst.begin()); }} - : mResampler}; + auto SamplePointers = std::array{}; + const auto MixingSamples = al::span{SamplePointers}.first(mChans.size()); + { + const uint channelStep{(samplesToLoad+3u)&~3u}; + auto base = Device->mSampleData.end() - MixingSamples.size()*channelStep; + std::generate(MixingSamples.begin(), MixingSamples.end(), [&base,channelStep] + { + const auto ret = base; + base += channelStep; + return al::to_address(ret); + }); + } /* UHJ2 and SuperStereo only have 2 buffer channels, but 3 mixing channels - * (3rd channel is generated from decoding). + * (3rd channel is generated from decoding). MonoDup only has 1 buffer + * channel, but 2 mixing channels (2nd channel is just duplicated). */ - const size_t realChannels{(mFmtChannels == FmtUHJ2 || mFmtChannels == FmtSuperStereo) ? 2u + const size_t realChannels{(mFmtChannels == FmtMonoDup) ? 1u + : (mFmtChannels == FmtUHJ2 || mFmtChannels == FmtSuperStereo) ? 2u : MixingSamples.size()}; for(size_t chan{0};chan < realChannels;++chan) { - using ResBufType = decltype(DeviceBase::mResampleData); - static constexpr uint srcSizeMax{static_cast(ResBufType{}.size()-MaxResamplerEdge)}; + static constexpr uint ResBufSize{std::tuple_size_v}; + static constexpr uint srcSizeMax{ResBufSize - MaxResamplerEdge}; const al::span prevSamples{mPrevSamples[chan]}; - const auto resampleBuffer = std::copy(prevSamples.cbegin(), prevSamples.cend(), - Device->mResampleData.begin()) - MaxResamplerEdge; + std::copy(prevSamples.cbegin(), prevSamples.cend(), Device->mResampleData.begin()); + const auto resampleBuffer = al::span{Device->mResampleData}.subspan(); int intPos{DataPosInt}; uint fracPos{DataPosFrac}; @@ -831,7 +844,7 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi dataSize64 += ext + MaxResamplerEdge; if(dataSize64 <= srcSizeMax) - return std::make_pair(dstBufferSize, static_cast(dataSize64)); + return std::array{dstBufferSize, static_cast(dataSize64)}; /* If the source size got saturated, we can't fill the desired * dst size. Figure out how many dst samples we can fill. @@ -846,88 +859,103 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi */ dstBufferSize = static_cast(dataSize64) & ~3u; } - return std::make_pair(dstBufferSize, srcSizeMax); + return std::array{dstBufferSize, srcSizeMax}; }; - const auto bufferSizes = calc_buffer_sizes(samplesToLoad - samplesLoaded); - const auto dstBufferSize = bufferSizes.first; - const auto srcBufferSize = bufferSizes.second; + const auto [dstBufferSize, srcBufferSize] = calc_buffer_sizes( + samplesToLoad - samplesLoaded); + + size_t srcSampleDelay{0}; + if(intPos < 0) UNLIKELY + { + /* If the current position is negative, there's that many + * silent samples to load before using the buffer. + */ + srcSampleDelay = static_cast(-intPos); + if(srcSampleDelay >= srcBufferSize) + { + /* If the number of silent source samples exceeds the + * number to load, the output will be silent. + */ + std::fill_n(MixingSamples[chan]+samplesLoaded, dstBufferSize, 0.0f); + std::fill_n(resampleBuffer.begin(), srcBufferSize, 0.0f); + goto skip_resample; + } + + std::fill_n(resampleBuffer.begin(), srcSampleDelay, 0.0f); + } /* Load the necessary samples from the given buffer(s). */ - if(!BufferListItem) + if(!BufferListItem) UNLIKELY { - const uint avail{minu(srcBufferSize, MaxResamplerEdge)}; - const uint tofill{maxu(srcBufferSize, MaxResamplerEdge)}; + const uint avail{std::min(srcBufferSize, MaxResamplerEdge)}; + const uint tofill{std::max(srcBufferSize, MaxResamplerEdge)}; + const auto srcbuf = resampleBuffer.first(tofill); /* When loading from a voice that ended prematurely, only take * the samples that get closest to 0 amplitude. This helps * certain sounds fade out better. */ - auto abs_lt = [](const float lhs, const float rhs) noexcept -> bool - { return std::abs(lhs) < std::abs(rhs); }; - auto srciter = std::min_element(resampleBuffer, resampleBuffer+avail, abs_lt); + auto srciter = std::min_element(srcbuf.begin(), srcbuf.begin()+ptrdiff_t(avail), + [](const float l, const float r) { return std::abs(l) < std::abs(r); }); - std::fill(srciter+1, resampleBuffer+tofill, *srciter); + std::fill(srciter+1, srcbuf.end(), *srciter); } - else + else if(mFlags.test(VoiceIsStatic)) { - size_t srcSampleDelay{0}; - if(intPos < 0) UNLIKELY - { - /* If the current position is negative, there's that many - * silent samples to load before using the buffer. - */ - srcSampleDelay = static_cast(-intPos); - if(srcSampleDelay >= srcBufferSize) - { - /* If the number of silent source samples exceeds the - * number to load, the output will be silent. - */ - std::fill_n(MixingSamples[chan]+samplesLoaded, dstBufferSize, 0.0f); - std::fill_n(resampleBuffer, srcBufferSize, 0.0f); - goto skip_resample; - } - - std::fill_n(resampleBuffer, srcSampleDelay, 0.0f); - } - const uint uintPos{static_cast(maxi(intPos, 0))}; - - if(mFlags.test(VoiceIsStatic)) - LoadBufferStatic(BufferListItem, BufferLoopItem, uintPos, mFmtType, chan, - mFrameStep, srcSampleDelay, srcBufferSize, al::to_address(resampleBuffer)); - else if(mFlags.test(VoiceIsCallback)) + const auto uintPos = static_cast(std::max(intPos, 0)); + const auto bufferSamples = resampleBuffer.subspan(srcSampleDelay, + srcBufferSize-srcSampleDelay); + LoadBufferStatic(BufferListItem, BufferLoopItem, uintPos, mFmtType, chan, + mFrameStep, bufferSamples); + } + else if(mFlags.test(VoiceIsCallback)) + { + const auto uintPos = static_cast(std::max(intPos, 0)); + const uint callbackBase{mCallbackBlockBase * mSamplesPerBlock}; + const size_t bufferOffset{uintPos - callbackBase}; + const size_t needSamples{bufferOffset + srcBufferSize - srcSampleDelay}; + const size_t needBlocks{(needSamples + mSamplesPerBlock-1) / mSamplesPerBlock}; + if(!mFlags.test(VoiceCallbackStopped) && needBlocks > mNumCallbackBlocks) { - const uint callbackBase{mCallbackBlockBase * mSamplesPerBlock}; - const size_t bufferOffset{uintPos - callbackBase}; - const size_t needSamples{bufferOffset + srcBufferSize - srcSampleDelay}; - const size_t needBlocks{(needSamples + mSamplesPerBlock-1) / mSamplesPerBlock}; - if(!mFlags.test(VoiceCallbackStopped) && needBlocks > mNumCallbackBlocks) + const size_t byteOffset{mNumCallbackBlocks*size_t{mBytesPerBlock}}; + const size_t needBytes{(needBlocks-mNumCallbackBlocks)*size_t{mBytesPerBlock}}; + + const int gotBytes{BufferListItem->mCallback(BufferListItem->mUserData, + &BufferListItem->mSamples[byteOffset], static_cast(needBytes))}; + if(gotBytes < 0) + mFlags.set(VoiceCallbackStopped); + else if(static_cast(gotBytes) < needBytes) { - const size_t byteOffset{mNumCallbackBlocks*mBytesPerBlock}; - const size_t needBytes{(needBlocks-mNumCallbackBlocks)*mBytesPerBlock}; - - const int gotBytes{BufferListItem->mCallback(BufferListItem->mUserData, - &BufferListItem->mSamples[byteOffset], static_cast(needBytes))}; - if(gotBytes < 0) - mFlags.set(VoiceCallbackStopped); - else if(static_cast(gotBytes) < needBytes) - { - mFlags.set(VoiceCallbackStopped); - mNumCallbackBlocks += static_cast(gotBytes) / mBytesPerBlock; - } - else - mNumCallbackBlocks = static_cast(needBlocks); + mFlags.set(VoiceCallbackStopped); + mNumCallbackBlocks += static_cast(gotBytes) / mBytesPerBlock; } - const size_t numSamples{uint{mNumCallbackBlocks} * mSamplesPerBlock}; - LoadBufferCallback(BufferListItem, bufferOffset, numSamples, mFmtType, chan, - mFrameStep, srcSampleDelay, srcBufferSize, al::to_address(resampleBuffer)); + else + mNumCallbackBlocks = static_cast(needBlocks); } - else - LoadBufferQueue(BufferListItem, BufferLoopItem, uintPos, mFmtType, chan, - mFrameStep, srcSampleDelay, srcBufferSize, al::to_address(resampleBuffer)); + const size_t numSamples{size_t{mNumCallbackBlocks} * mSamplesPerBlock}; + const auto bufferSamples = resampleBuffer.subspan(srcSampleDelay, + srcBufferSize-srcSampleDelay); + LoadBufferCallback(BufferListItem, bufferOffset, numSamples, mFmtType, chan, + mFrameStep, bufferSamples); + } + else + { + const auto uintPos = static_cast(std::max(intPos, 0)); + const auto bufferSamples = resampleBuffer.subspan(srcSampleDelay, + srcBufferSize-srcSampleDelay); + LoadBufferQueue(BufferListItem, BufferLoopItem, uintPos, mFmtType, chan, + mFrameStep, bufferSamples); } - Resample(&mResampleState, al::to_address(resampleBuffer), fracPos, increment, - {MixingSamples[chan]+samplesLoaded, dstBufferSize}); + /* If there's a matching sample step and no phase offset, use a + * simple copy for resampling. + */ + if(increment == MixerFracOne && fracPos == 0) + std::copy_n(resampleBuffer.cbegin(), dstBufferSize, + MixingSamples[chan]+samplesLoaded); + else + mResampler(&mResampleState, Device->mResampleData, fracPos, increment, + {MixingSamples[chan]+samplesLoaded, dstBufferSize}); /* Store the last source samples used for next time. */ if(vstate == Playing) LIKELY @@ -940,7 +968,7 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi { const size_t dstOffset{samplesToMix - samplesLoaded}; const size_t srcOffset{(dstOffset*increment + fracPos) >> MixerFracBits}; - std::copy_n(resampleBuffer-MaxResamplerEdge+srcOffset, prevSamples.size(), + std::copy_n(Device->mResampleData.cbegin()+srcOffset, prevSamples.size(), prevSamples.begin()); } } @@ -952,18 +980,26 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi fracPos += dstBufferSize*increment; const uint srcOffset{fracPos >> MixerFracBits}; fracPos &= MixerFracMask; - intPos += srcOffset; + intPos += static_cast(srcOffset); /* If more samples need to be loaded, copy the back of the * resampleBuffer to the front to reuse it. prevSamples isn't * reliable since it's only updated for the end of the mix. */ - std::copy(resampleBuffer-MaxResamplerEdge+srcOffset, - resampleBuffer+MaxResamplerEdge+srcOffset, resampleBuffer-MaxResamplerEdge); + std::copy_n(Device->mResampleData.cbegin()+srcOffset, MaxResamplerPadding, + Device->mResampleData.begin()); } } } - for(auto &samples : MixingSamples.subspan(realChannels)) + if(mFmtChannels == FmtMonoDup) + { + /* NOTE: a mono source shouldn't have a decoder or the VoiceIsAmbisonic + * flag, so aliasing instead of copying to the second channel shouldn't + * be a problem. + */ + MixingSamples[1] = MixingSamples[0]; + } + else for(auto &samples : MixingSamples.subspan(realChannels)) std::fill_n(samples, samplesToLoad, 0.0f); if(mDecoder) @@ -980,7 +1016,7 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi } } - const uint Counter{mFlags.test(VoiceIsFading) ? minu(samplesToMix, 64u) : 0u}; + const uint Counter{mFlags.test(VoiceIsFading) ? std::min(samplesToMix, 64u) : 0u}; if(!Counter) { /* No fading, just overwrite the old/current params. */ @@ -1011,25 +1047,24 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi const al::span FilterBuf{Device->FilteredData}; { DirectParams &parms = chandata.mDryParams; - const float *samples{DoFilters(parms.LowPass, parms.HighPass, FilterBuf.data(), - {*voiceSamples, samplesToMix}, mDirect.FilterType)}; + const auto samples = DoFilters(parms.LowPass, parms.HighPass, FilterBuf, + {*voiceSamples, samplesToMix}, mDirect.FilterType); if(mFlags.test(VoiceHasHrtf)) { - const float TargetGain{parms.Hrtf.Target.Gain * (vstate == Playing)}; - DoHrtfMix(samples, samplesToMix, parms, TargetGain, Counter, OutPos, - (vstate == Playing), Device); + const float TargetGain{parms.Hrtf.Target.Gain * float(vstate == Playing)}; + DoHrtfMix(samples, parms, TargetGain, Counter, OutPos, (vstate == Playing), + Device); } else { - const float *TargetGains{(vstate == Playing) ? parms.Gains.Target.data() - : SilentTarget.data()}; + const auto TargetGains = (vstate == Playing) ? al::span{parms.Gains.Target} + : al::span{SilentTarget}; if(mFlags.test(VoiceHasNfc)) - DoNfcMix({samples, samplesToMix}, mDirect.Buffer.data(), parms, - TargetGains, Counter, OutPos, Device); + DoNfcMix(samples, mDirect.Buffer, parms, TargetGains, Counter, OutPos, Device); else - MixSamples({samples, samplesToMix}, mDirect.Buffer, - parms.Gains.Current.data(), TargetGains, Counter, OutPos); + MixSamples(samples, mDirect.Buffer, parms.Gains.Current, TargetGains, Counter, + OutPos); } } @@ -1039,13 +1074,13 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi continue; SendParams &parms = chandata.mWetParams[send]; - const float *samples{DoFilters(parms.LowPass, parms.HighPass, FilterBuf.data(), - {*voiceSamples, samplesToMix}, mSend[send].FilterType)}; + const auto samples = DoFilters(parms.LowPass, parms.HighPass, FilterBuf, + {*voiceSamples, samplesToMix}, mSend[send].FilterType); - const float *TargetGains{(vstate == Playing) ? parms.Gains.Target.data() - : SilentTarget.data()}; - MixSamples({samples, samplesToMix}, mSend[send].Buffer, - parms.Gains.Current.data(), TargetGains, Counter, OutPos); + const auto TargetGains = (vstate == Playing) ? al::span{parms.Gains.Target} + : al::span{SilentTarget}; + MixSamples(samples, mSend[send].Buffer, parms.Gains.Current, TargetGains, Counter, + OutPos); } ++voiceSamples; @@ -1062,12 +1097,11 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi /* Update voice positions and buffers as needed. */ DataPosFrac += increment*samplesToMix; - const uint SrcSamplesDone{DataPosFrac>>MixerFracBits}; - DataPosInt += SrcSamplesDone; + DataPosInt += static_cast(DataPosFrac>>MixerFracBits); DataPosFrac &= MixerFracMask; uint buffers_done{0u}; - if(BufferListItem && DataPosInt >= 0) LIKELY + if(BufferListItem && DataPosInt > 0) LIKELY { if(mFlags.test(VoiceIsStatic)) { @@ -1098,10 +1132,11 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi const uint blocksDone{currentBlock - mCallbackBlockBase}; if(blocksDone < mNumCallbackBlocks) { - const size_t byteOffset{blocksDone*mBytesPerBlock}; - const size_t byteEnd{mNumCallbackBlocks*mBytesPerBlock}; - std::byte *data{BufferListItem->mSamples}; - std::copy(data+byteOffset, data+byteEnd, data); + const size_t byteOffset{blocksDone*size_t{mBytesPerBlock}}; + const size_t byteEnd{mNumCallbackBlocks*size_t{mBytesPerBlock}}; + const al::span data{BufferListItem->mSamples}; + std::copy(data.cbegin()+ptrdiff_t(byteOffset), data.cbegin()+ptrdiff_t(byteEnd), + data.begin()); mNumCallbackBlocks -= blocksDone; mCallbackBlockBase += blocksDone; } @@ -1119,7 +1154,7 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi if(BufferListItem->mSampleLen > static_cast(DataPosInt)) break; - DataPosInt -= BufferListItem->mSampleLen; + DataPosInt -= static_cast(BufferListItem->mSampleLen); ++buffers_done; BufferListItem = BufferListItem->mNext.load(std::memory_order_relaxed); @@ -1148,9 +1183,9 @@ void Voice::mix(const State vstate, ContextBase *Context, const nanoseconds devi { RingBuffer *ring{Context->mAsyncEvents.get()}; auto evt_vec = ring->getWriteVector(); - if(evt_vec.first.len > 0) + if(evt_vec[0].len > 0) { - auto &evt = InitAsyncEvent(evt_vec.first.buf); + auto &evt = InitAsyncEvent(evt_vec[0].buf); evt.mId = SourceID; evt.mCount = buffers_done; ring->writeAdvance(1); @@ -1173,22 +1208,23 @@ void Voice::prepare(DeviceBase *device) /* Even if storing really high order ambisonics, we only mix channels for * orders up to the device order. The rest are simply dropped. */ - uint num_channels{(mFmtChannels == FmtUHJ2 || mFmtChannels == FmtSuperStereo) ? 3 : - ChannelsFromFmt(mFmtChannels, minu(mAmbiOrder, device->mAmbiOrder))}; - if(num_channels > device->mSampleData.size()) UNLIKELY + uint num_channels{(mFmtChannels == FmtMonoDup) ? 2 + : (mFmtChannels == FmtUHJ2 || mFmtChannels == FmtSuperStereo) ? 3 + : ChannelsFromFmt(mFmtChannels, std::min(mAmbiOrder, device->mAmbiOrder))}; + if(num_channels > device->MixerChannelsMax) UNLIKELY { - ERR("Unexpected channel count: %u (limit: %zu, %d:%d)\n", num_channels, - device->mSampleData.size(), mFmtChannels, mAmbiOrder); - num_channels = static_cast(device->mSampleData.size()); + ERR("Unexpected channel count: {} (limit: {}, {} : {})", num_channels, + device->MixerChannelsMax, NameFromFormat(mFmtChannels), mAmbiOrder); + num_channels = device->MixerChannelsMax; } if(mChans.capacity() > 2 && num_channels < mChans.capacity()) { decltype(mChans){}.swap(mChans); decltype(mPrevSamples){}.swap(mPrevSamples); } - mChans.reserve(maxu(2, num_channels)); + mChans.reserve(std::max(2u, num_channels)); mChans.resize(num_channels); - mPrevSamples.reserve(maxu(2, num_channels)); + mPrevSamples.reserve(std::max(2u, num_channels)); mPrevSamples.resize(num_channels); mDecoder = nullptr; @@ -1252,7 +1288,7 @@ void Voice::prepare(DeviceBase *device) * Note this isn't needed with UHJ output (UHJ2->B-Format->UHJ2 is * identity, so don't mess with it). */ - const BandSplitter splitter{device->mXOverFreq / static_cast(device->Frequency)}; + const BandSplitter splitter{device->mXOverFreq / static_cast(device->mSampleRate)}; for(auto &chandata : mChans) { chandata.mAmbiHFScale = 1.0f; @@ -1272,12 +1308,14 @@ void Voice::prepare(DeviceBase *device) */ else if(mAmbiOrder && device->mAmbiOrder > mAmbiOrder) { - const uint8_t *OrderFromChan{Is2DAmbisonic(mFmtChannels) ? - AmbiIndex::OrderFrom2DChannel.data() : AmbiIndex::OrderFromChannel.data()}; + auto OrdersSpan = Is2DAmbisonic(mFmtChannels) + ? al::span{AmbiIndex::OrderFrom2DChannel} + : al::span{AmbiIndex::OrderFromChannel}; + auto OrderFromChan = OrdersSpan.cbegin(); const auto scales = AmbiScale::GetHFOrderScales(mAmbiOrder, device->mAmbiOrder, device->m2DMixing); - const BandSplitter splitter{device->mXOverFreq / static_cast(device->Frequency)}; + const BandSplitter splitter{device->mXOverFreq / static_cast(device->mSampleRate)}; for(auto &chandata : mChans) { chandata.mAmbiHFScale = scales[*(OrderFromChan++)]; diff --git a/3rdparty/openal/core/voice.h b/3rdparty/openal/core/voice.h index a599eda8f8ec..bb7c6e249844 100644 --- a/3rdparty/openal/core/voice.h +++ b/3rdparty/openal/core/voice.h @@ -10,7 +10,6 @@ #include #include -#include "almalloc.h" #include "alspan.h" #include "bufferline.h" #include "buffer_storage.h" @@ -20,6 +19,7 @@ #include "filters/splitter.h" #include "mixer/defs.h" #include "mixer/hrtfdefs.h" +#include "opthelpers.h" #include "resampler_limits.h" #include "uhjfilter.h" #include "vector.h" @@ -32,7 +32,7 @@ enum class DistanceModel : unsigned char; using uint = unsigned int; -#define MAX_SENDS 6 +inline constexpr size_t MaxSendCount{6}; enum class SpatializeMode : unsigned char { @@ -48,7 +48,7 @@ enum class DirectMode : unsigned char { }; -constexpr uint MaxPitch{10}; +inline constexpr uint MaxPitch{10}; enum { @@ -65,26 +65,29 @@ struct DirectParams { NfcFilter NFCtrlFilter; - struct { - HrtfFilter Old; - HrtfFilter Target; - alignas(16) std::array History; - } Hrtf; + struct HrtfParams { + HrtfFilter Old{}; + HrtfFilter Target{}; + alignas(16) std::array History{}; + }; + HrtfParams Hrtf; - struct { - std::array Current; - std::array Target; - } Gains; + struct GainParams { + std::array Current{}; + std::array Target{}; + }; + GainParams Gains; }; struct SendParams { BiquadFilter LowPass; BiquadFilter HighPass; - struct { - std::array Current; - std::array Target; - } Gains; + struct GainParams { + std::array Current{}; + std::array Target{}; + }; + GainParams Gains; }; @@ -99,7 +102,10 @@ struct VoiceBufferItem { uint mLoopStart{0u}; uint mLoopEnd{0u}; - std::byte *mSamples{nullptr}; + al::span mSamples; + +protected: + ~VoiceBufferItem() = default; }; @@ -138,15 +144,18 @@ struct VoiceProps { float Radius; float EnhWidth; + float Panning; /** Direct filter and auxiliary send info. */ - struct { + struct DirectData { float Gain; float GainHF; float HFReference; float GainLF; float LFReference; - } Direct; + }; + DirectData Direct; + struct SendData { EffectSlot *Slot; float Gain; @@ -154,13 +163,12 @@ struct VoiceProps { float HFReference; float GainLF; float LFReference; - } Send[MAX_SENDS]; + }; + std::array Send; }; struct VoicePropsItem : public VoiceProps { std::atomic next{nullptr}; - - DEF_NEWDEL(VoicePropsItem) }; enum : uint { @@ -175,7 +183,7 @@ enum : uint { VoiceFlagCount }; -struct Voice { +struct SIMDALIGN Voice { enum State { Stopped, Playing, @@ -185,7 +193,7 @@ struct Voice { std::atomic mUpdate{nullptr}; - VoiceProps mProps; + VoiceProps mProps{}; std::atomic mSourceID{0u}; std::atomic mPlayState{Stopped}; @@ -195,30 +203,30 @@ struct Voice { * Source offset in samples, relative to the currently playing buffer, NOT * the whole queue. */ - std::atomic mPosition; + std::atomic mPosition{}; /** Fractional (fixed-point) offset to the next sample. */ - std::atomic mPositionFrac; + std::atomic mPositionFrac{}; /* Current buffer queue item being played. */ - std::atomic mCurrentBuffer; + std::atomic mCurrentBuffer{}; /* Buffer queue item to loop to at end of queue (will be NULL for non- * looping voices). */ - std::atomic mLoopBuffer; + std::atomic mLoopBuffer{}; std::chrono::nanoseconds mStartTime{}; /* Properties for the attached buffer(s). */ - FmtChannels mFmtChannels; - FmtType mFmtType; - uint mFrequency; - uint mFrameStep; /**< In steps of the sample type size. */ - uint mBytesPerBlock; /**< Or for PCM formats, BytesPerFrame. */ - uint mSamplesPerBlock; /**< Always 1 for PCM formats. */ - AmbiLayout mAmbiLayout; - AmbiScaling mAmbiScaling; - uint mAmbiOrder; + FmtChannels mFmtChannels{}; + FmtType mFmtType{}; + uint mFrequency{}; + uint mFrameStep{}; /**< In steps of the sample type size. */ + uint mBytesPerBlock{}; /**< Or for PCM formats, BytesPerFrame. */ + uint mSamplesPerBlock{}; /**< Always 1 for PCM formats. */ + AmbiLayout mAmbiLayout{}; + AmbiScaling mAmbiScaling{}; + uint mAmbiOrder{}; std::unique_ptr mDecoder; uint mDecoderPadding{}; @@ -226,20 +234,20 @@ struct Voice { /** Current target parameters used for mixing. */ uint mStep{0}; - ResamplerFunc mResampler; + ResamplerFunc mResampler{}; InterpState mResampleState; - std::bitset mFlags{}; + std::bitset mFlags; uint mNumCallbackBlocks{0}; uint mCallbackBlockBase{0}; struct TargetData { - int FilterType; + int FilterType{}; al::span Buffer; }; TargetData mDirect; - std::array mSend; + std::array mSend; /* The first MaxResamplerPadding/2 elements are the sample history from the * previous mix, with an additional MaxResamplerPadding/2 elements that are @@ -250,11 +258,11 @@ struct Voice { al::vector mPrevSamples{2}; struct ChannelData { - float mAmbiHFScale, mAmbiLFScale; + float mAmbiHFScale{}, mAmbiLFScale{}; BandSplitter mAmbiSplitter; DirectParams mDryParams; - std::array mWetParams; + std::array mWetParams; }; al::vector mChans{2}; @@ -269,11 +277,9 @@ struct Voice { void prepare(DeviceBase *device); - static void InitMixer(std::optional resampler); - - DEF_NEWDEL(Voice) + static void InitMixer(std::optional resopt); }; -extern Resampler ResamplerDefault; +inline Resampler ResamplerDefault{Resampler::Spline}; #endif /* CORE_VOICE_H */ diff --git a/3rdparty/openal/core/voice_change.h b/3rdparty/openal/core/voice_change.h index ddc6186f5da2..e97c48f33110 100644 --- a/3rdparty/openal/core/voice_change.h +++ b/3rdparty/openal/core/voice_change.h @@ -3,8 +3,6 @@ #include -#include "almalloc.h" - struct Voice; using uint = unsigned int; @@ -24,8 +22,6 @@ struct VoiceChange { VChangeState mState{}; std::atomic mNext{nullptr}; - - DEF_NEWDEL(VoiceChange) }; #endif /* VOICE_CHANGE_H */ diff --git a/3rdparty/openal/examples/common/alhelpers.c b/3rdparty/openal/examples/common/alhelpers.c deleted file mode 100644 index 6627f7042056..000000000000 --- a/3rdparty/openal/examples/common/alhelpers.c +++ /dev/null @@ -1,228 +0,0 @@ -/* - * OpenAL Helpers - * - * Copyright (c) 2011 by Chris Robinson - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -/* This file contains routines to help with some menial OpenAL-related tasks, - * such as opening a device and setting up a context, closing the device and - * destroying its context, converting between frame counts and byte lengths, - * finding an appropriate buffer format, and getting readable strings for - * channel configs and sample types. */ - -#include "alhelpers.h" - -#include -#include -#include - -#include "AL/al.h" -#include "AL/alc.h" -#include "AL/alext.h" - - -/* InitAL opens a device and sets up a context using default attributes, making - * the program ready to call OpenAL functions. */ -int InitAL(char ***argv, int *argc) -{ - const ALCchar *name; - ALCdevice *device; - ALCcontext *ctx; - - /* Open and initialize a device */ - device = NULL; - if(argc && argv && *argc > 1 && strcmp((*argv)[0], "-device") == 0) - { - device = alcOpenDevice((*argv)[1]); - if(!device) - fprintf(stderr, "Failed to open \"%s\", trying default\n", (*argv)[1]); - (*argv) += 2; - (*argc) -= 2; - } - if(!device) - device = alcOpenDevice(NULL); - if(!device) - { - fprintf(stderr, "Could not open a device!\n"); - return 1; - } - - ctx = alcCreateContext(device, NULL); - if(ctx == NULL || alcMakeContextCurrent(ctx) == ALC_FALSE) - { - if(ctx != NULL) - alcDestroyContext(ctx); - alcCloseDevice(device); - fprintf(stderr, "Could not set a context!\n"); - return 1; - } - - name = NULL; - if(alcIsExtensionPresent(device, "ALC_ENUMERATE_ALL_EXT")) - name = alcGetString(device, ALC_ALL_DEVICES_SPECIFIER); - if(!name || alcGetError(device) != AL_NO_ERROR) - name = alcGetString(device, ALC_DEVICE_SPECIFIER); - printf("Opened \"%s\"\n", name); - - return 0; -} - -/* CloseAL closes the device belonging to the current context, and destroys the - * context. */ -void CloseAL(void) -{ - ALCdevice *device; - ALCcontext *ctx; - - ctx = alcGetCurrentContext(); - if(ctx == NULL) - return; - - device = alcGetContextsDevice(ctx); - - alcMakeContextCurrent(NULL); - alcDestroyContext(ctx); - alcCloseDevice(device); -} - - -const char *FormatName(ALenum format) -{ - switch(format) - { - case AL_FORMAT_MONO8: return "Mono, U8"; - case AL_FORMAT_MONO16: return "Mono, S16"; - case AL_FORMAT_MONO_FLOAT32: return "Mono, Float32"; - case AL_FORMAT_MONO_MULAW: return "Mono, muLaw"; - case AL_FORMAT_MONO_ALAW_EXT: return "Mono, aLaw"; - case AL_FORMAT_MONO_IMA4: return "Mono, IMA4 ADPCM"; - case AL_FORMAT_MONO_MSADPCM_SOFT: return "Mono, MS ADPCM"; - case AL_FORMAT_STEREO8: return "Stereo, U8"; - case AL_FORMAT_STEREO16: return "Stereo, S16"; - case AL_FORMAT_STEREO_FLOAT32: return "Stereo, Float32"; - case AL_FORMAT_STEREO_MULAW: return "Stereo, muLaw"; - case AL_FORMAT_STEREO_ALAW_EXT: return "Stereo, aLaw"; - case AL_FORMAT_STEREO_IMA4: return "Stereo, IMA4 ADPCM"; - case AL_FORMAT_STEREO_MSADPCM_SOFT: return "Stereo, MS ADPCM"; - case AL_FORMAT_QUAD8: return "Quadraphonic, U8"; - case AL_FORMAT_QUAD16: return "Quadraphonic, S16"; - case AL_FORMAT_QUAD32: return "Quadraphonic, Float32"; - case AL_FORMAT_QUAD_MULAW: return "Quadraphonic, muLaw"; - case AL_FORMAT_51CHN8: return "5.1 Surround, U8"; - case AL_FORMAT_51CHN16: return "5.1 Surround, S16"; - case AL_FORMAT_51CHN32: return "5.1 Surround, Float32"; - case AL_FORMAT_51CHN_MULAW: return "5.1 Surround, muLaw"; - case AL_FORMAT_61CHN8: return "6.1 Surround, U8"; - case AL_FORMAT_61CHN16: return "6.1 Surround, S16"; - case AL_FORMAT_61CHN32: return "6.1 Surround, Float32"; - case AL_FORMAT_61CHN_MULAW: return "6.1 Surround, muLaw"; - case AL_FORMAT_71CHN8: return "7.1 Surround, U8"; - case AL_FORMAT_71CHN16: return "7.1 Surround, S16"; - case AL_FORMAT_71CHN32: return "7.1 Surround, Float32"; - case AL_FORMAT_71CHN_MULAW: return "7.1 Surround, muLaw"; - case AL_FORMAT_BFORMAT2D_8: return "B-Format 2D, U8"; - case AL_FORMAT_BFORMAT2D_16: return "B-Format 2D, S16"; - case AL_FORMAT_BFORMAT2D_FLOAT32: return "B-Format 2D, Float32"; - case AL_FORMAT_BFORMAT2D_MULAW: return "B-Format 2D, muLaw"; - case AL_FORMAT_BFORMAT3D_8: return "B-Format 3D, U8"; - case AL_FORMAT_BFORMAT3D_16: return "B-Format 3D, S16"; - case AL_FORMAT_BFORMAT3D_FLOAT32: return "B-Format 3D, Float32"; - case AL_FORMAT_BFORMAT3D_MULAW: return "B-Format 3D, muLaw"; - case AL_FORMAT_UHJ2CHN8_SOFT: return "UHJ 2-channel, U8"; - case AL_FORMAT_UHJ2CHN16_SOFT: return "UHJ 2-channel, S16"; - case AL_FORMAT_UHJ2CHN_FLOAT32_SOFT: return "UHJ 2-channel, Float32"; - case AL_FORMAT_UHJ3CHN8_SOFT: return "UHJ 3-channel, U8"; - case AL_FORMAT_UHJ3CHN16_SOFT: return "UHJ 3-channel, S16"; - case AL_FORMAT_UHJ3CHN_FLOAT32_SOFT: return "UHJ 3-channel, Float32"; - case AL_FORMAT_UHJ4CHN8_SOFT: return "UHJ 4-channel, U8"; - case AL_FORMAT_UHJ4CHN16_SOFT: return "UHJ 4-channel, S16"; - case AL_FORMAT_UHJ4CHN_FLOAT32_SOFT: return "UHJ 4-channel, Float32"; - } - return "Unknown Format"; -} - - -#ifdef _WIN32 - -#define WIN32_LEAN_AND_MEAN -#include -#include - -int altime_get(void) -{ - static int start_time = 0; - int cur_time; - union { - FILETIME ftime; - ULARGE_INTEGER ulint; - } systime; - GetSystemTimeAsFileTime(&systime.ftime); - /* FILETIME is in 100-nanosecond units, or 1/10th of a microsecond. */ - cur_time = (int)(systime.ulint.QuadPart/10000); - - if(!start_time) - start_time = cur_time; - return cur_time - start_time; -} - -void al_nssleep(unsigned long nsec) -{ - Sleep(nsec / 1000000); -} - -#else - -#include -#include -#include - -int altime_get(void) -{ - static int start_time = 0u; - int cur_time; - -#if _POSIX_TIMERS > 0 - struct timespec ts; - int ret = clock_gettime(CLOCK_REALTIME, &ts); - if(ret != 0) return 0; - cur_time = (int)(ts.tv_sec*1000 + ts.tv_nsec/1000000); -#else /* _POSIX_TIMERS > 0 */ - struct timeval tv; - int ret = gettimeofday(&tv, NULL); - if(ret != 0) return 0; - cur_time = (int)(tv.tv_sec*1000 + tv.tv_usec/1000); -#endif - - if(!start_time) - start_time = cur_time; - return cur_time - start_time; -} - -void al_nssleep(unsigned long nsec) -{ - struct timespec ts, rem; - ts.tv_sec = (time_t)(nsec / 1000000000ul); - ts.tv_nsec = (long)(nsec % 1000000000ul); - while(nanosleep(&ts, &rem) == -1 && errno == EINTR) - ts = rem; -} - -#endif diff --git a/3rdparty/openal/examples/common/alhelpers.h b/3rdparty/openal/examples/common/alhelpers.h deleted file mode 100644 index 34f738647668..000000000000 --- a/3rdparty/openal/examples/common/alhelpers.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef ALHELPERS_H -#define ALHELPERS_H - -#include "AL/al.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* Some helper functions to get the name from the format enums. */ -const char *FormatName(ALenum type); - -/* Easy device init/deinit functions. InitAL returns 0 on success. */ -int InitAL(char ***argv, int *argc); -void CloseAL(void); - -/* Cross-platform timeget and sleep functions. */ -int altime_get(void); -void al_nssleep(unsigned long nsec); - -/* C doesn't allow casting between function and non-function pointer types, so - * with C99 we need to use a union to reinterpret the pointer type. Pre-C99 - * still needs to use a normal cast and live with the warning (C++ is fine with - * a regular reinterpret_cast). - */ -#if __STDC_VERSION__ >= 199901L -#define FUNCTION_CAST(T, ptr) (union{void *p; T f;}){ptr}.f -#elif defined(__cplusplus) -#define FUNCTION_CAST(T, ptr) reinterpret_cast(ptr) -#else -#define FUNCTION_CAST(T, ptr) (T)(ptr) -#endif - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif /* ALHELPERS_H */ diff --git a/3rdparty/openal/include/AL/al.h b/3rdparty/openal/include/AL/al.h index 87274184b2db..a4e3ad5159f1 100644 --- a/3rdparty/openal/include/AL/al.h +++ b/3rdparty/openal/include/AL/al.h @@ -1,12 +1,23 @@ #ifndef AL_AL_H #define AL_AL_H +/* NOLINTBEGIN */ #ifdef __cplusplus extern "C" { +#ifdef _MSVC_LANG +#define AL_CPLUSPLUS _MSVC_LANG +#else +#define AL_CPLUSPLUS __cplusplus +#endif + #ifndef AL_DISABLE_NOEXCEPT +#if AL_CPLUSPLUS >= 201103L #define AL_API_NOEXCEPT noexcept -#if __cplusplus >= 201703L +#else +#define AL_API_NOEXCEPT +#endif +#if AL_CPLUSPLUS >= 201703L #define AL_API_NOEXCEPT17 noexcept #else #define AL_API_NOEXCEPT17 @@ -18,6 +29,8 @@ extern "C" { #define AL_API_NOEXCEPT17 #endif +#undef AL_CPLUSPLUS + #else /* __cplusplus */ #define AL_API_NOEXCEPT @@ -689,5 +702,6 @@ typedef void (AL_APIENTRY *LPALDISTANCEMODEL)(ALenum distanceModel) AL_ #ifdef __cplusplus } /* extern "C" */ #endif +/* NOLINTEND */ #endif /* AL_AL_H */ diff --git a/3rdparty/openal/include/AL/alc.h b/3rdparty/openal/include/AL/alc.h index 73dcf08f043d..d048ca04dbd0 100644 --- a/3rdparty/openal/include/AL/alc.h +++ b/3rdparty/openal/include/AL/alc.h @@ -1,12 +1,23 @@ #ifndef AL_ALC_H #define AL_ALC_H +/* NOLINTBEGIN */ #ifdef __cplusplus extern "C" { +#ifdef _MSVC_LANG +#define ALC_CPLUSPLUS _MSVC_LANG +#else +#define ALC_CPLUSPLUS __cplusplus +#endif + #ifndef AL_DISABLE_NOEXCEPT +#if ALC_CPLUSPLUS >= 201103L #define ALC_API_NOEXCEPT noexcept -#if __cplusplus >= 201703L +#else +#define ALC_API_NOEXCEPT +#endif +#if ALC_CPLUSPLUS >= 201703L #define ALC_API_NOEXCEPT17 noexcept #else #define ALC_API_NOEXCEPT17 @@ -18,6 +29,8 @@ extern "C" { #define ALC_API_NOEXCEPT17 #endif +#undef ALC_CPLUSPLUS + #else /* __cplusplus */ #define ALC_API_NOEXCEPT @@ -289,5 +302,6 @@ typedef void (ALC_APIENTRY *LPALCCAPTURESAMPLES)(ALCdevice *device, AL #ifdef __cplusplus } /* extern "C" */ #endif +/* NOLINTEND */ #endif /* AL_ALC_H */ diff --git a/3rdparty/openal/include/AL/alext.h b/3rdparty/openal/include/AL/alext.h index b99d6aacb83b..3908e19404c7 100644 --- a/3rdparty/openal/include/AL/alext.h +++ b/3rdparty/openal/include/AL/alext.h @@ -1,6 +1,7 @@ #ifndef AL_ALEXT_H #define AL_ALEXT_H +/* NOLINTBEGIN */ #include /* Define int64 and uint64 types */ #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || \ @@ -25,6 +26,8 @@ typedef uint64_t _alsoft_uint64_t; extern "C" { #endif +struct _GUID; + #ifndef AL_LOKI_IMA_ADPCM_format #define AL_LOKI_IMA_ADPCM_format 1 #define AL_FORMAT_IMA_ADPCM_MONO16_EXT 0x10000 @@ -701,15 +704,19 @@ typedef void (AL_APIENTRY*LPALPOPDEBUGGROUPEXT)(void) AL_API_NOEXCEPT17; typedef ALuint (AL_APIENTRY*LPALGETDEBUGMESSAGELOGEXT)(ALuint count, ALsizei logBufSize, ALenum *sources, ALenum *types, ALuint *ids, ALenum *severities, ALsizei *lengths, ALchar *logBuf) AL_API_NOEXCEPT17; typedef void (AL_APIENTRY*LPALOBJECTLABELEXT)(ALenum identifier, ALuint name, ALsizei length, const ALchar *label) AL_API_NOEXCEPT17; typedef void (AL_APIENTRY*LPALGETOBJECTLABELEXT)(ALenum identifier, ALuint name, ALsizei bufSize, ALsizei *length, ALchar *label) AL_API_NOEXCEPT17; +typedef void* (AL_APIENTRY*LPALGETPOINTEREXT)(ALenum pname) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETPOINTERVEXT)(ALenum pname, void **values) AL_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES void AL_APIENTRY alDebugMessageCallbackEXT(ALDEBUGPROCEXT callback, void *userParam) AL_API_NOEXCEPT; void AL_APIENTRY alDebugMessageInsertEXT(ALenum source, ALenum type, ALuint id, ALenum severity, ALsizei length, const ALchar *message) AL_API_NOEXCEPT; -void AL_APIENTRY alDebugMessageControlEXT(ALenum source, ALenum type, ALenum severity, ALsizei count, const ALuint *ids, ALboolean enable) AL_API_NOEXCEPT; -void AL_APIENTRY alPushDebugGroupEXT(ALenum source, ALuint id, ALsizei length, const ALchar *message) AL_API_NOEXCEPT; -void AL_APIENTRY alPopDebugGroupEXT(void) AL_API_NOEXCEPT; -ALuint AL_APIENTRY alGetDebugMessageLogEXT(ALuint count, ALsizei logBufSize, ALenum *sources, ALenum *types, ALuint *ids, ALenum *severities, ALsizei *lengths, ALchar *logBuf) AL_API_NOEXCEPT; +void AL_APIENTRY alDebugMessageControlEXT(ALenum source, ALenum type, ALenum severity, ALsizei count, const ALuint *ids, ALboolean enable) AL_API_NOEXCEPT; +void AL_APIENTRY alPushDebugGroupEXT(ALenum source, ALuint id, ALsizei length, const ALchar *message) AL_API_NOEXCEPT; +void AL_APIENTRY alPopDebugGroupEXT(void) AL_API_NOEXCEPT; +ALuint AL_APIENTRY alGetDebugMessageLogEXT(ALuint count, ALsizei logBufSize, ALenum *sources, ALenum *types, ALuint *ids, ALenum *severities, ALsizei *lengths, ALchar *logBuf) AL_API_NOEXCEPT; void AL_APIENTRY alObjectLabelEXT(ALenum identifier, ALuint name, ALsizei length, const ALchar *label) AL_API_NOEXCEPT; void AL_APIENTRY alGetObjectLabelEXT(ALenum identifier, ALuint name, ALsizei bufSize, ALsizei *length, ALchar *label) AL_API_NOEXCEPT; +void* AL_APIENTRY alGetPointerEXT(ALenum pname) AL_API_NOEXCEPT; +void AL_APIENTRY alGetPointervEXT(ALenum pname, void **values) AL_API_NOEXCEPT; #endif #endif @@ -720,18 +727,366 @@ void AL_APIENTRY alGetObjectLabelEXT(ALenum identifier, ALuint name, ALsizei buf #define ALC_EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT 0x19D6 #define ALC_EVENT_TYPE_DEVICE_ADDED_SOFT 0x19D7 #define ALC_EVENT_TYPE_DEVICE_REMOVED_SOFT 0x19D8 +#define ALC_EVENT_SUPPORTED_SOFT 0x19D9 +#define ALC_EVENT_NOT_SUPPORTED_SOFT 0x19DA typedef void (ALC_APIENTRY*ALCEVENTPROCTYPESOFT)(ALCenum eventType, ALCenum deviceType, ALCdevice *device, ALCsizei length, const ALCchar *message, void *userParam) ALC_API_NOEXCEPT17; +typedef ALCenum (ALC_APIENTRY*LPALCEVENTISSUPPORTEDSOFT)(ALCenum eventType, ALCenum deviceType) ALC_API_NOEXCEPT17; typedef ALCboolean (ALC_APIENTRY*LPALCEVENTCONTROLSOFT)(ALCsizei count, const ALCenum *events, ALCboolean enable) ALC_API_NOEXCEPT17; typedef void (ALC_APIENTRY*LPALCEVENTCALLBACKSOFT)(ALCEVENTPROCTYPESOFT callback, void *userParam) ALC_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES +ALCenum ALC_APIENTRY alcEventIsSupportedSOFT(ALCenum eventType, ALCenum deviceType) ALC_API_NOEXCEPT; ALCboolean ALC_APIENTRY alcEventControlSOFT(ALCsizei count, const ALCenum *events, ALCboolean enable) ALC_API_NOEXCEPT; void ALC_APIENTRY alcEventCallbackSOFT(ALCEVENTPROCTYPESOFT callback, void *userParam) ALC_API_NOEXCEPT; #endif #endif +#ifndef AL_EXT_direct_context +#define AL_EXT_direct_context +typedef ALCvoid* (ALC_APIENTRY *LPALCGETPROCADDRESS2)(ALCdevice *device, const ALCchar *funcname) AL_API_NOEXCEPT17; + +typedef void (AL_APIENTRY *LPALENABLEDIRECT)(ALCcontext *context, ALenum capability) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDISABLEDIRECT)(ALCcontext *context, ALenum capability) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISENABLEDDIRECT)(ALCcontext *context, ALenum capability) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDOPPLERFACTORDIRECT)(ALCcontext *context, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSPEEDOFSOUNDDIRECT)(ALCcontext *context, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDISTANCEMODELDIRECT)(ALCcontext *context, ALenum distanceModel) AL_API_NOEXCEPT17; +typedef const ALchar* (AL_APIENTRY *LPALGETSTRINGDIRECT)(ALCcontext *context, ALenum param) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBOOLEANVDIRECT)(ALCcontext *context, ALenum param, ALboolean *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETINTEGERVDIRECT)(ALCcontext *context, ALenum param, ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETFLOATVDIRECT)(ALCcontext *context, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETDOUBLEVDIRECT)(ALCcontext *context, ALenum param, ALdouble *values) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALGETBOOLEANDIRECT)(ALCcontext *context, ALenum param) AL_API_NOEXCEPT17; +typedef ALint (AL_APIENTRY *LPALGETINTEGERDIRECT)(ALCcontext *context, ALenum param) AL_API_NOEXCEPT17; +typedef ALfloat (AL_APIENTRY *LPALGETFLOATDIRECT)(ALCcontext *context, ALenum param) AL_API_NOEXCEPT17; +typedef ALdouble (AL_APIENTRY *LPALGETDOUBLEDIRECT)(ALCcontext *context, ALenum param) AL_API_NOEXCEPT17; +typedef ALenum (AL_APIENTRY *LPALGETERRORDIRECT)(ALCcontext *context) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISEXTENSIONPRESENTDIRECT)(ALCcontext *context, const ALchar *extname) AL_API_NOEXCEPT17; +typedef void* (AL_APIENTRY *LPALGETPROCADDRESSDIRECT)(ALCcontext *context, const ALchar *fname) AL_API_NOEXCEPT17; +typedef ALenum (AL_APIENTRY *LPALGETENUMVALUEDIRECT)(ALCcontext *context, const ALchar *ename) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENERFDIRECT)(ALCcontext *context, ALenum param, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENER3FDIRECT)(ALCcontext *context, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENERFVDIRECT)(ALCcontext *context, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENERIDIRECT)(ALCcontext *context, ALenum param, ALint value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENER3IDIRECT)(ALCcontext *context, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENERIVDIRECT)(ALCcontext *context, ALenum param, const ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENERFDIRECT)(ALCcontext *context, ALenum param, ALfloat *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENER3FDIRECT)(ALCcontext *context, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENERFVDIRECT)(ALCcontext *context, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENERIDIRECT)(ALCcontext *context, ALenum param, ALint *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENER3IDIRECT)(ALCcontext *context, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENERIVDIRECT)(ALCcontext *context, ALenum param, ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGENSOURCESDIRECT)(ALCcontext *context, ALsizei n, ALuint *sources) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDELETESOURCESDIRECT)(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISSOURCEDIRECT)(ALCcontext *context, ALuint source) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEFDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCE3FDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEFVDIRECT)(ALCcontext *context, ALuint source, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEIDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALint value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCE3IDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEIVDIRECT)(ALCcontext *context, ALuint source, ALenum param, const ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEFDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALfloat *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCE3FDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEFVDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEIDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALint *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCE3IDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEIVDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEPLAYDIRECT)(ALCcontext *context, ALuint source) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCESTOPDIRECT)(ALCcontext *context, ALuint source) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEREWINDDIRECT)(ALCcontext *context, ALuint source) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEPAUSEDIRECT)(ALCcontext *context, ALuint source) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEPLAYVDIRECT)(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCESTOPVDIRECT)(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEREWINDVDIRECT)(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEPAUSEVDIRECT)(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEQUEUEBUFFERSDIRECT)(ALCcontext *context, ALuint source, ALsizei nb, const ALuint *buffers) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEUNQUEUEBUFFERSDIRECT)(ALCcontext *context, ALuint source, ALsizei nb, ALuint *buffers) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGENBUFFERSDIRECT)(ALCcontext *context, ALsizei n, ALuint *buffers) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDELETEBUFFERSDIRECT)(ALCcontext *context, ALsizei n, const ALuint *buffers) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISBUFFERDIRECT)(ALCcontext *context, ALuint buffer) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFERDATADIRECT)(ALCcontext *context, ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei samplerate) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFERFDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFER3FDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFERFVDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFERIDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALint value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFER3IDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFERIVDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, const ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFERFDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFER3FDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFERFVDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFERIDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALint *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFER3IDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFERIVDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALint *values) AL_API_NOEXCEPT17; +/* ALC_EXT_EFX */ +typedef void (AL_APIENTRY *LPALGENEFFECTSDIRECT)(ALCcontext *context, ALsizei n, ALuint *effects) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDELETEEFFECTSDIRECT)(ALCcontext *context, ALsizei n, const ALuint *effects) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISEFFECTDIRECT)(ALCcontext *context, ALuint effect) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALEFFECTIDIRECT)(ALCcontext *context, ALuint effect, ALenum param, ALint value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALEFFECTIVDIRECT)(ALCcontext *context, ALuint effect, ALenum param, const ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALEFFECTFDIRECT)(ALCcontext *context, ALuint effect, ALenum param, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALEFFECTFVDIRECT)(ALCcontext *context, ALuint effect, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETEFFECTIDIRECT)(ALCcontext *context, ALuint effect, ALenum param, ALint *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETEFFECTIVDIRECT)(ALCcontext *context, ALuint effect, ALenum param, ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETEFFECTFDIRECT)(ALCcontext *context, ALuint effect, ALenum param, ALfloat *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETEFFECTFVDIRECT)(ALCcontext *context, ALuint effect, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGENFILTERSDIRECT)(ALCcontext *context, ALsizei n, ALuint *filters) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDELETEFILTERSDIRECT)(ALCcontext *context, ALsizei n, const ALuint *filters) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISFILTERDIRECT)(ALCcontext *context, ALuint filter) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALFILTERIDIRECT)(ALCcontext *context, ALuint filter, ALenum param, ALint value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALFILTERIVDIRECT)(ALCcontext *context, ALuint filter, ALenum param, const ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALFILTERFDIRECT)(ALCcontext *context, ALuint filter, ALenum param, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALFILTERFVDIRECT)(ALCcontext *context, ALuint filter, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETFILTERIDIRECT)(ALCcontext *context, ALuint filter, ALenum param, ALint *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETFILTERIVDIRECT)(ALCcontext *context, ALuint filter, ALenum param, ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETFILTERFDIRECT)(ALCcontext *context, ALuint filter, ALenum param, ALfloat *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETFILTERFVDIRECT)(ALCcontext *context, ALuint filter, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGENAUXILIARYEFFECTSLOTSDIRECT)(ALCcontext *context, ALsizei n, ALuint *effectslots) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDELETEAUXILIARYEFFECTSLOTSDIRECT)(ALCcontext *context, ALsizei n, const ALuint *effectslots) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISAUXILIARYEFFECTSLOTDIRECT)(ALCcontext *context, ALuint effectslot) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTIDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, ALint value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTIVDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, const ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTFDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTFVDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTIDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, ALint *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTIVDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTFDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTFVDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +/* AL_EXT_BUFFER_DATA_STATIC */ +typedef void (AL_APIENTRY *LPALBUFFERDATASTATICDIRECT)(ALCcontext *context, ALuint buffer, ALenum format, ALvoid *data, ALsizei size, ALsizei freq) AL_API_NOEXCEPT17; +/* AL_EXT_debug */ +typedef void (AL_APIENTRY*LPALDEBUGMESSAGECALLBACKDIRECTEXT)(ALCcontext *context, ALDEBUGPROCEXT callback, void *userParam) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALDEBUGMESSAGEINSERTDIRECTEXT)(ALCcontext *context, ALenum source, ALenum type, ALuint id, ALenum severity, ALsizei length, const ALchar *message) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALDEBUGMESSAGECONTROLDIRECTEXT)(ALCcontext *context, ALenum source, ALenum type, ALenum severity, ALsizei count, const ALuint *ids, ALboolean enable) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALPUSHDEBUGGROUPDIRECTEXT)(ALCcontext *context, ALenum source, ALuint id, ALsizei length, const ALchar *message) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALPOPDEBUGGROUPDIRECTEXT)(ALCcontext *context) AL_API_NOEXCEPT17; +typedef ALuint (AL_APIENTRY*LPALGETDEBUGMESSAGELOGDIRECTEXT)(ALCcontext *context, ALuint count, ALsizei logBufSize, ALenum *sources, ALenum *types, ALuint *ids, ALenum *severities, ALsizei *lengths, ALchar *logBuf) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALOBJECTLABELDIRECTEXT)(ALCcontext *context, ALenum identifier, ALuint name, ALsizei length, const ALchar *label) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETOBJECTLABELDIRECTEXT)(ALCcontext *context, ALenum identifier, ALuint name, ALsizei bufSize, ALsizei *length, ALchar *label) AL_API_NOEXCEPT17; +typedef void* (AL_APIENTRY*LPALGETPOINTERDIRECTEXT)(ALCcontext *context, ALenum pname) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETPOINTERVDIRECTEXT)(ALCcontext *context, ALenum pname, void **values) AL_API_NOEXCEPT17; +/* AL_EXT_FOLDBACK */ +typedef void (AL_APIENTRY *LPALREQUESTFOLDBACKSTARTDIRECT)(ALCcontext *context, ALenum mode, ALsizei count, ALsizei length, ALfloat *mem, LPALFOLDBACKCALLBACK callback) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALREQUESTFOLDBACKSTOPDIRECT)(ALCcontext *context) AL_API_NOEXCEPT17; +/* AL_SOFT_buffer_sub_data */ +typedef void (AL_APIENTRY *LPALBUFFERSUBDATADIRECTSOFT)(ALCcontext *context, ALuint buffer, ALenum format, const ALvoid *data, ALsizei offset, ALsizei length) AL_API_NOEXCEPT17; +/* AL_SOFT_source_latency */ +typedef void (AL_APIENTRY *LPALSOURCEDDIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALdouble) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCE3DDIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALdouble,ALdouble,ALdouble) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEDVDIRECTSOFT)(ALCcontext*,ALuint,ALenum,const ALdouble*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEDDIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALdouble*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCE3DDIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALdouble*,ALdouble*,ALdouble*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEDVDIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALdouble*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEI64DIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALint64SOFT) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCE3I64DIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALint64SOFT,ALint64SOFT,ALint64SOFT) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEI64VDIRECTSOFT)(ALCcontext*,ALuint,ALenum,const ALint64SOFT*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEI64DIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALint64SOFT*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCE3I64DIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALint64SOFT*,ALint64SOFT*,ALint64SOFT*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEI64VDIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALint64SOFT*) AL_API_NOEXCEPT17; +/* AL_SOFT_deferred_updates */ +typedef void (AL_APIENTRY *LPALDEFERUPDATESDIRECTSOFT)(ALCcontext *context) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALPROCESSUPDATESDIRECTSOFT)(ALCcontext *context) AL_API_NOEXCEPT17; +/* AL_SOFT_source_resampler */ +typedef const ALchar* (AL_APIENTRY *LPALGETSTRINGIDIRECTSOFT)(ALCcontext *context, ALenum pname, ALsizei index) AL_API_NOEXCEPT17; +/* AL_SOFT_events */ +typedef void (AL_APIENTRY *LPALEVENTCONTROLDIRECTSOFT)(ALCcontext *context, ALsizei count, const ALenum *types, ALboolean enable) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALEVENTCALLBACKDIRECTSOFT)(ALCcontext *context, ALEVENTPROCSOFT callback, void *userParam) AL_API_NOEXCEPT17; +typedef void* (AL_APIENTRY *LPALGETPOINTERDIRECTSOFT)(ALCcontext *context, ALenum pname) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETPOINTERVDIRECTSOFT)(ALCcontext *context, ALenum pname, void **values) AL_API_NOEXCEPT17; +/* AL_SOFT_callback_buffer */ +typedef void (AL_APIENTRY *LPALBUFFERCALLBACKDIRECTSOFT)(ALCcontext *context, ALuint buffer, ALenum format, ALsizei freq, ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFERPTRDIRECTSOFT)(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFER3PTRDIRECTSOFT)(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **value1, ALvoid **value2, ALvoid **value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFERPTRVDIRECTSOFT)(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **values) AL_API_NOEXCEPT17; +/* AL_SOFT_source_start_delay */ +typedef void (AL_APIENTRY *LPALSOURCEPLAYATTIMEDIRECTSOFT)(ALCcontext *context, ALuint source, ALint64SOFT start_time) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEPLAYATTIMEVDIRECTSOFT)(ALCcontext *context, ALsizei n, const ALuint *sources, ALint64SOFT start_time) AL_API_NOEXCEPT17; +/* EAX */ +typedef ALenum (AL_APIENTRY *LPEAXSETDIRECT)(ALCcontext *context, const struct _GUID *property_set_id, ALuint property_id, ALuint source_id, ALvoid *value, ALuint value_size) AL_API_NOEXCEPT17; +typedef ALenum (AL_APIENTRY *LPEAXGETDIRECT)(ALCcontext *context, const struct _GUID *property_set_id, ALuint property_id, ALuint source_id, ALvoid *value, ALuint value_size) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPEAXSETBUFFERMODEDIRECT)(ALCcontext *context, ALsizei n, const ALuint *buffers, ALint value) AL_API_NOEXCEPT17; +typedef ALenum (AL_APIENTRY *LPEAXGETBUFFERMODEDIRECT)(ALCcontext *context, ALuint buffer, ALint *pReserved) AL_API_NOEXCEPT17; +#ifdef AL_ALEXT_PROTOTYPES +ALCvoid* ALC_APIENTRY alcGetProcAddress2(ALCdevice *device, const ALCchar *funcName) AL_API_NOEXCEPT; + +void AL_APIENTRY alEnableDirect(ALCcontext *context, ALenum capability) AL_API_NOEXCEPT; +void AL_APIENTRY alDisableDirect(ALCcontext *context, ALenum capability) AL_API_NOEXCEPT; +ALboolean AL_APIENTRY alIsEnabledDirect(ALCcontext *context, ALenum capability) AL_API_NOEXCEPT; + +void AL_APIENTRY alDopplerFactorDirect(ALCcontext *context, ALfloat value) AL_API_NOEXCEPT; +void AL_APIENTRY alSpeedOfSoundDirect(ALCcontext *context, ALfloat value) AL_API_NOEXCEPT; +void AL_APIENTRY alDistanceModelDirect(ALCcontext *context, ALenum distanceModel) AL_API_NOEXCEPT; + +const ALchar* AL_APIENTRY alGetStringDirect(ALCcontext *context, ALenum param) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBooleanvDirect(ALCcontext *context, ALenum param, ALboolean *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetIntegervDirect(ALCcontext *context, ALenum param, ALint *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetFloatvDirect(ALCcontext *context, ALenum param, ALfloat *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetDoublevDirect(ALCcontext *context, ALenum param, ALdouble *values) AL_API_NOEXCEPT; +ALboolean AL_APIENTRY alGetBooleanDirect(ALCcontext *context, ALenum param) AL_API_NOEXCEPT; +ALint AL_APIENTRY alGetIntegerDirect(ALCcontext *context, ALenum param) AL_API_NOEXCEPT; +ALfloat AL_APIENTRY alGetFloatDirect(ALCcontext *context, ALenum param) AL_API_NOEXCEPT; +ALdouble AL_APIENTRY alGetDoubleDirect(ALCcontext *context, ALenum param) AL_API_NOEXCEPT; + +ALenum AL_APIENTRY alGetErrorDirect(ALCcontext *context) AL_API_NOEXCEPT; +ALboolean AL_APIENTRY alIsExtensionPresentDirect(ALCcontext *context, const ALchar *extname) AL_API_NOEXCEPT; +void* AL_APIENTRY alGetProcAddressDirect(ALCcontext *context, const ALchar *fname) AL_API_NOEXCEPT; +ALenum AL_APIENTRY alGetEnumValueDirect(ALCcontext *context, const ALchar *ename) AL_API_NOEXCEPT; + +void AL_APIENTRY alListenerfDirect(ALCcontext *context, ALenum param, ALfloat value) AL_API_NOEXCEPT; +void AL_APIENTRY alListener3fDirect(ALCcontext *context, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT; +void AL_APIENTRY alListenerfvDirect(ALCcontext *context, ALenum param, const ALfloat *values) AL_API_NOEXCEPT; +void AL_APIENTRY alListeneriDirect(ALCcontext *context, ALenum param, ALint value) AL_API_NOEXCEPT; +void AL_APIENTRY alListener3iDirect(ALCcontext *context, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT; +void AL_APIENTRY alListenerivDirect(ALCcontext *context, ALenum param, const ALint *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetListenerfDirect(ALCcontext *context, ALenum param, ALfloat *value) AL_API_NOEXCEPT; +void AL_APIENTRY alGetListener3fDirect(ALCcontext *context, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT; +void AL_APIENTRY alGetListenerfvDirect(ALCcontext *context, ALenum param, ALfloat *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetListeneriDirect(ALCcontext *context, ALenum param, ALint *value) AL_API_NOEXCEPT; +void AL_APIENTRY alGetListener3iDirect(ALCcontext *context, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT; +void AL_APIENTRY alGetListenerivDirect(ALCcontext *context, ALenum param, ALint *values) AL_API_NOEXCEPT; + +void AL_APIENTRY alGenSourcesDirect(ALCcontext *context, ALsizei n, ALuint *sources) AL_API_NOEXCEPT; +void AL_APIENTRY alDeleteSourcesDirect(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; +ALboolean AL_APIENTRY alIsSourceDirect(ALCcontext *context, ALuint source) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcefDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat value) AL_API_NOEXCEPT; +void AL_APIENTRY alSource3fDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcefvDirect(ALCcontext *context, ALuint source, ALenum param, const ALfloat *values) AL_API_NOEXCEPT; +void AL_APIENTRY alSourceiDirect(ALCcontext *context, ALuint source, ALenum param, ALint value) AL_API_NOEXCEPT; +void AL_APIENTRY alSource3iDirect(ALCcontext *context, ALuint source, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT; +void AL_APIENTRY alSourceivDirect(ALCcontext *context, ALuint source, ALenum param, const ALint *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSourcefDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat *value) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSource3fDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSourcefvDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSourceiDirect(ALCcontext *context, ALuint source, ALenum param, ALint *value) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSource3iDirect(ALCcontext *context, ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSourceivDirect(ALCcontext *context, ALuint source, ALenum param, ALint *values) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcePlayDirect(ALCcontext *context, ALuint source) AL_API_NOEXCEPT; +void AL_APIENTRY alSourceStopDirect(ALCcontext *context, ALuint source) AL_API_NOEXCEPT; +void AL_APIENTRY alSourceRewindDirect(ALCcontext *context, ALuint source) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcePauseDirect(ALCcontext *context, ALuint source) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcePlayvDirect(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; +void AL_APIENTRY alSourceStopvDirect(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; +void AL_APIENTRY alSourceRewindvDirect(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcePausevDirect(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; +void AL_APIENTRY alSourceQueueBuffersDirect(ALCcontext *context, ALuint source, ALsizei nb, const ALuint *buffers) AL_API_NOEXCEPT; +void AL_APIENTRY alSourceUnqueueBuffersDirect(ALCcontext *context, ALuint source, ALsizei nb, ALuint *buffers) AL_API_NOEXCEPT; + +void AL_APIENTRY alGenBuffersDirect(ALCcontext *context, ALsizei n, ALuint *buffers) AL_API_NOEXCEPT; +void AL_APIENTRY alDeleteBuffersDirect(ALCcontext *context, ALsizei n, const ALuint *buffers) AL_API_NOEXCEPT; +ALboolean AL_APIENTRY alIsBufferDirect(ALCcontext *context, ALuint buffer) AL_API_NOEXCEPT; +void AL_APIENTRY alBufferDataDirect(ALCcontext *context, ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei samplerate) AL_API_NOEXCEPT; +void AL_APIENTRY alBufferfDirect(ALCcontext *context, ALuint buffer, ALenum param, ALfloat value) AL_API_NOEXCEPT; +void AL_APIENTRY alBuffer3fDirect(ALCcontext *context, ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT; +void AL_APIENTRY alBufferfvDirect(ALCcontext *context, ALuint buffer, ALenum param, const ALfloat *values) AL_API_NOEXCEPT; +void AL_APIENTRY alBufferiDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint value) AL_API_NOEXCEPT; +void AL_APIENTRY alBuffer3iDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT; +void AL_APIENTRY alBufferivDirect(ALCcontext *context, ALuint buffer, ALenum param, const ALint *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBufferfDirect(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *value) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBuffer3fDirect(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBufferfvDirect(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBufferiDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint *value) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBuffer3iDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBufferivDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint *values) AL_API_NOEXCEPT; + +void AL_APIENTRY alGenEffectsDirect(ALCcontext *context, ALsizei n, ALuint *effects) AL_API_NOEXCEPT; +void AL_APIENTRY alDeleteEffectsDirect(ALCcontext *context, ALsizei n, const ALuint *effects) AL_API_NOEXCEPT; +ALboolean AL_APIENTRY alIsEffectDirect(ALCcontext *context, ALuint effect) AL_API_NOEXCEPT; +void AL_APIENTRY alEffectiDirect(ALCcontext *context, ALuint effect, ALenum param, ALint iValue) AL_API_NOEXCEPT; +void AL_APIENTRY alEffectivDirect(ALCcontext *context, ALuint effect, ALenum param, const ALint *piValues) AL_API_NOEXCEPT; +void AL_APIENTRY alEffectfDirect(ALCcontext *context, ALuint effect, ALenum param, ALfloat flValue) AL_API_NOEXCEPT; +void AL_APIENTRY alEffectfvDirect(ALCcontext *context, ALuint effect, ALenum param, const ALfloat *pflValues) AL_API_NOEXCEPT; +void AL_APIENTRY alGetEffectiDirect(ALCcontext *context, ALuint effect, ALenum param, ALint *piValue) AL_API_NOEXCEPT; +void AL_APIENTRY alGetEffectivDirect(ALCcontext *context, ALuint effect, ALenum param, ALint *piValues) AL_API_NOEXCEPT; +void AL_APIENTRY alGetEffectfDirect(ALCcontext *context, ALuint effect, ALenum param, ALfloat *pflValue) AL_API_NOEXCEPT; +void AL_APIENTRY alGetEffectfvDirect(ALCcontext *context, ALuint effect, ALenum param, ALfloat *pflValues) AL_API_NOEXCEPT; + +void AL_APIENTRY alGenFiltersDirect(ALCcontext *context, ALsizei n, ALuint *filters) AL_API_NOEXCEPT; +void AL_APIENTRY alDeleteFiltersDirect(ALCcontext *context, ALsizei n, const ALuint *filters) AL_API_NOEXCEPT; +ALboolean AL_APIENTRY alIsFilterDirect(ALCcontext *context, ALuint filter) AL_API_NOEXCEPT; +void AL_APIENTRY alFilteriDirect(ALCcontext *context, ALuint filter, ALenum param, ALint iValue) AL_API_NOEXCEPT; +void AL_APIENTRY alFilterivDirect(ALCcontext *context, ALuint filter, ALenum param, const ALint *piValues) AL_API_NOEXCEPT; +void AL_APIENTRY alFilterfDirect(ALCcontext *context, ALuint filter, ALenum param, ALfloat flValue) AL_API_NOEXCEPT; +void AL_APIENTRY alFilterfvDirect(ALCcontext *context, ALuint filter, ALenum param, const ALfloat *pflValues) AL_API_NOEXCEPT; +void AL_APIENTRY alGetFilteriDirect(ALCcontext *context, ALuint filter, ALenum param, ALint *piValue) AL_API_NOEXCEPT; +void AL_APIENTRY alGetFilterivDirect(ALCcontext *context, ALuint filter, ALenum param, ALint *piValues) AL_API_NOEXCEPT; +void AL_APIENTRY alGetFilterfDirect(ALCcontext *context, ALuint filter, ALenum param, ALfloat *pflValue) AL_API_NOEXCEPT; +void AL_APIENTRY alGetFilterfvDirect(ALCcontext *context, ALuint filter, ALenum param, ALfloat *pflValues) AL_API_NOEXCEPT; + +void AL_APIENTRY alGenAuxiliaryEffectSlotsDirect(ALCcontext *context, ALsizei n, ALuint *effectslots) AL_API_NOEXCEPT; +void AL_APIENTRY alDeleteAuxiliaryEffectSlotsDirect(ALCcontext *context, ALsizei n, const ALuint *effectslots) AL_API_NOEXCEPT; +ALboolean AL_APIENTRY alIsAuxiliaryEffectSlotDirect(ALCcontext *context, ALuint effectslot) AL_API_NOEXCEPT; +void AL_APIENTRY alAuxiliaryEffectSlotiDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALint iValue) AL_API_NOEXCEPT; +void AL_APIENTRY alAuxiliaryEffectSlotivDirect(ALCcontext *context, ALuint effectslot, ALenum param, const ALint *piValues) AL_API_NOEXCEPT; +void AL_APIENTRY alAuxiliaryEffectSlotfDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat flValue) AL_API_NOEXCEPT; +void AL_APIENTRY alAuxiliaryEffectSlotfvDirect(ALCcontext *context, ALuint effectslot, ALenum param, const ALfloat *pflValues) AL_API_NOEXCEPT; +void AL_APIENTRY alGetAuxiliaryEffectSlotiDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALint *piValue) AL_API_NOEXCEPT; +void AL_APIENTRY alGetAuxiliaryEffectSlotivDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALint *piValues) AL_API_NOEXCEPT; +void AL_APIENTRY alGetAuxiliaryEffectSlotfDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat *pflValue) AL_API_NOEXCEPT; +void AL_APIENTRY alGetAuxiliaryEffectSlotfvDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat *pflValues) AL_API_NOEXCEPT; + +void AL_APIENTRY alBufferDataStaticDirect(ALCcontext *context, ALuint buffer, ALenum format, ALvoid *data, ALsizei size, ALsizei freq) AL_API_NOEXCEPT; + +void AL_APIENTRY alDebugMessageCallbackDirectEXT(ALCcontext *context, ALDEBUGPROCEXT callback, void *userParam) AL_API_NOEXCEPT; +void AL_APIENTRY alDebugMessageInsertDirectEXT(ALCcontext *context, ALenum source, ALenum type, ALuint id, ALenum severity, ALsizei length, const ALchar *message) AL_API_NOEXCEPT; +void AL_APIENTRY alDebugMessageControlDirectEXT(ALCcontext *context, ALenum source, ALenum type, ALenum severity, ALsizei count, const ALuint *ids, ALboolean enable) AL_API_NOEXCEPT; +void AL_APIENTRY alPushDebugGroupDirectEXT(ALCcontext *context, ALenum source, ALuint id, ALsizei length, const ALchar *message) AL_API_NOEXCEPT; +void AL_APIENTRY alPopDebugGroupDirectEXT(ALCcontext *context) AL_API_NOEXCEPT; +ALuint AL_APIENTRY alGetDebugMessageLogDirectEXT(ALCcontext *context, ALuint count, ALsizei logBufSize, ALenum *sources, ALenum *types, ALuint *ids, ALenum *severities, ALsizei *lengths, ALchar *logBuf) AL_API_NOEXCEPT; +void AL_APIENTRY alObjectLabelDirectEXT(ALCcontext *context, ALenum identifier, ALuint name, ALsizei length, const ALchar *label) AL_API_NOEXCEPT; +void AL_APIENTRY alGetObjectLabelDirectEXT(ALCcontext *context, ALenum identifier, ALuint name, ALsizei bufSize, ALsizei *length, ALchar *label) AL_API_NOEXCEPT; +void* AL_APIENTRY alGetPointerDirectEXT(ALCcontext *context, ALenum pname) AL_API_NOEXCEPT; +void AL_APIENTRY alGetPointervDirectEXT(ALCcontext *context, ALenum pname, void **values) AL_API_NOEXCEPT; + +void AL_APIENTRY alRequestFoldbackStartDirect(ALCcontext *context, ALenum mode, ALsizei count, ALsizei length, ALfloat *mem, LPALFOLDBACKCALLBACK callback) AL_API_NOEXCEPT; +void AL_APIENTRY alRequestFoldbackStopDirect(ALCcontext *context) AL_API_NOEXCEPT; + +void AL_APIENTRY alBufferSubDataDirectSOFT(ALCcontext *context, ALuint buffer, ALenum format, const ALvoid *data, ALsizei offset, ALsizei length) AL_API_NOEXCEPT; + +void AL_APIENTRY alSourcedDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble value) AL_API_NOEXCEPT; +void AL_APIENTRY alSource3dDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcedvDirectSOFT(ALCcontext *context, ALuint source, ALenum param, const ALdouble *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSourcedDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble *value) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSource3dDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSourcedvDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble *values) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcei64DirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT value) AL_API_NOEXCEPT; +void AL_APIENTRY alSource3i64DirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcei64vDirectSOFT(ALCcontext *context, ALuint source, ALenum param, const ALint64SOFT *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSourcei64DirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT *value) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSource3i64DirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSourcei64vDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT *values) AL_API_NOEXCEPT; + +void AL_APIENTRY alDeferUpdatesDirectSOFT(ALCcontext *context) AL_API_NOEXCEPT; +void AL_APIENTRY alProcessUpdatesDirectSOFT(ALCcontext *context) AL_API_NOEXCEPT; + +const ALchar* AL_APIENTRY alGetStringiDirectSOFT(ALCcontext *context, ALenum pname, ALsizei index) AL_API_NOEXCEPT; + +void AL_APIENTRY alEventControlDirectSOFT(ALCcontext *context, ALsizei count, const ALenum *types, ALboolean enable) AL_API_NOEXCEPT; +void AL_APIENTRY alEventCallbackDirectSOFT(ALCcontext *context, ALEVENTPROCSOFT callback, void *userParam) AL_API_NOEXCEPT; +void* AL_APIENTRY alGetPointerDirectSOFT(ALCcontext *context, ALenum pname) AL_API_NOEXCEPT; +void AL_APIENTRY alGetPointervDirectSOFT(ALCcontext *context, ALenum pname, void **values) AL_API_NOEXCEPT; + +void AL_APIENTRY alBufferCallbackDirectSOFT(ALCcontext *context, ALuint buffer, ALenum format, ALsizei freq, ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBufferPtrDirectSOFT(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **ptr) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBuffer3PtrDirectSOFT(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **ptr0, ALvoid **ptr1, ALvoid **ptr2) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBufferPtrvDirectSOFT(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **ptr) AL_API_NOEXCEPT; + +void AL_APIENTRY alSourcePlayAtTimeDirectSOFT(ALCcontext *context, ALuint source, ALint64SOFT start_time) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcePlayAtTimevDirectSOFT(ALCcontext *context, ALsizei n, const ALuint *sources, ALint64SOFT start_time) AL_API_NOEXCEPT; + +ALenum AL_APIENTRY EAXSetDirect(ALCcontext *context, const struct _GUID *property_set_id, ALuint property_id, ALuint source_id, ALvoid *value, ALuint value_size) AL_API_NOEXCEPT; +ALenum AL_APIENTRY EAXGetDirect(ALCcontext *context, const struct _GUID *property_set_id, ALuint property_id, ALuint source_id, ALvoid *value, ALuint value_size) AL_API_NOEXCEPT; +ALboolean AL_APIENTRY EAXSetBufferModeDirect(ALCcontext *context, ALsizei n, const ALuint *buffers, ALint value) AL_API_NOEXCEPT; +ALenum AL_APIENTRY EAXGetBufferModeDirect(ALCcontext *context, ALuint buffer, ALint *pReserved) AL_API_NOEXCEPT; +#endif +#endif + +#ifndef AL_SOFT_bformat_hoa +#define AL_SOFT_bformat_hoa +#define AL_UNPACK_AMBISONIC_ORDER_SOFT 0x199D +#endif + #ifdef __cplusplus } #endif +/* NOLINTEND */ #endif diff --git a/3rdparty/openal/include/AL/efx-presets.h b/3rdparty/openal/include/AL/efx-presets.h index 8539fd51789f..acd5bf398522 100644 --- a/3rdparty/openal/include/AL/efx-presets.h +++ b/3rdparty/openal/include/AL/efx-presets.h @@ -2,6 +2,7 @@ #ifndef EFX_PRESETS_H #define EFX_PRESETS_H +/* NOLINTBEGIN */ #ifndef EFXEAXREVERBPROPERTIES_DEFINED #define EFXEAXREVERBPROPERTIES_DEFINED @@ -399,4 +400,5 @@ typedef struct { #define EFX_REVERB_PRESET_SMALLWATERROOM \ { 1.0000f, 0.7000f, 0.3162f, 0.4477f, 1.0000f, 1.5100f, 1.2500f, 1.1400f, 0.8913f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.1790f, 0.1500f, 0.8950f, 0.1900f, 0.9920f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } +/* NOLINTEND */ #endif /* EFX_PRESETS_H */ diff --git a/3rdparty/openal/include/AL/efx.h b/3rdparty/openal/include/AL/efx.h index f24222c353db..1e93bf2222b4 100644 --- a/3rdparty/openal/include/AL/efx.h +++ b/3rdparty/openal/include/AL/efx.h @@ -1,6 +1,7 @@ #ifndef AL_EFX_H #define AL_EFX_H +/* NOLINTBEGIN */ #include #include "alc.h" @@ -758,5 +759,6 @@ AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum par #ifdef __cplusplus } /* extern "C" */ #endif +/* NOLINTEND */ #endif /* AL_EFX_H */ diff --git a/3rdparty/simdjson/simdjson.cpp b/3rdparty/simdjson/simdjson.cpp index d79fc703a815..a36c044941fc 100644 --- a/3rdparty/simdjson/simdjson.cpp +++ b/3rdparty/simdjson/simdjson.cpp @@ -1,4 +1,4 @@ -/* auto-generated on 2024-08-26 09:37:03 -0400. Do not edit! */ +/* auto-generated on 2025-01-27 20:34:35 -0500. Do not edit! */ /* including simdjson.cpp: */ /* begin file simdjson.cpp */ #define SIMDJSON_SRC_SIMDJSON_CPP @@ -77,6 +77,30 @@ #endif #endif +#ifdef __has_include +#if __has_include() +#include +#endif +#endif + +#if defined(__apple_build_version__) +#if __apple_build_version__ < 14000000 +#define SIMDJSON_CONCEPT_DISABLED 1 // apple-clang/13 doesn't support std::convertible_to +#endif +#endif + + +#if defined(__cpp_concepts) && !defined(SIMDJSON_CONCEPT_DISABLED) +#if __cpp_concepts >= 201907L +#include +#define SIMDJSON_SUPPORTS_DESERIALIZATION 1 +#else +#define SIMDJSON_SUPPORTS_DESERIALIZATION 0 +#endif +#else // defined(__cpp_concepts) && !defined(SIMDJSON_CONCEPT_DISABLED) +#define SIMDJSON_SUPPORTS_DESERIALIZATION 0 +#endif // defined(__cpp_concepts) && !defined(SIMDJSON_CONCEPT_DISABLED) + #endif // SIMDJSON_COMPILER_CHECK_H /* end file simdjson/compiler_check.h */ /* including simdjson/portability.h: #include "simdjson/portability.h" */ @@ -89,11 +113,15 @@ #include #include #include +#include #ifndef _WIN32 // strcasecmp, strncasecmp #include #endif +static_assert(CHAR_BIT == 8, "simdjson requires 8-bit bytes"); + + // We are using size_t without namespace std:: throughout the project using std::size_t; @@ -127,6 +155,7 @@ using std::size_t; #elif defined(__loongarch_lp64) #define SIMDJSON_IS_LOONGARCH64 1 #elif defined(__PPC64__) || defined(_M_PPC64) +#define SIMDJSON_IS_PPC64 1 #if defined(__ALTIVEC__) #define SIMDJSON_IS_PPC64_VMX 1 #endif // defined(__ALTIVEC__) @@ -284,6 +313,45 @@ using std::size_t; #endif + + +#if defined __BYTE_ORDER__ && defined __ORDER_BIG_ENDIAN__ +#define SIMDJSON_IS_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +#elif defined _WIN32 +#define SIMDJSON_IS_BIG_ENDIAN 0 +#else +#if defined(__APPLE__) || defined(__FreeBSD__) +#include +#elif defined(sun) || defined(__sun) +#include +#elif defined(__MVS__) +#include +#else +#ifdef __has_include +#if __has_include() +#include +#endif //__has_include() +#endif //__has_include +#endif +# +#ifndef __BYTE_ORDER__ +// safe choice +#define SIMDJSON_IS_BIG_ENDIAN 0 +#endif +# +#ifndef __ORDER_LITTLE_ENDIAN__ +// safe choice +#define SIMDJSON_IS_BIG_ENDIAN 0 +#endif +# +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define SIMDJSON_IS_BIG_ENDIAN 0 +#else +#define SIMDJSON_IS_BIG_ENDIAN 1 +#endif +#endif + + #endif // SIMDJSON_PORTABILITY_H /* end file simdjson/portability.h */ @@ -549,7 +617,6 @@ SIMDJSON_PUSH_DISABLE_ALL_WARNINGS // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -#pragma once #ifndef NONSTD_SV_LITE_H_INCLUDED #define NONSTD_SV_LITE_H_INCLUDED @@ -2430,7 +2497,7 @@ struct simdjson_error : public std::exception { */ simdjson_error(error_code error) noexcept : _error{error} { } /** The error message */ - const char *what() const noexcept { return error_message(error()); } + const char *what() const noexcept override { return error_message(error()); } /** The error code */ error_code error() const noexcept { return _error; } private: @@ -2662,6 +2729,122 @@ inline const std::string error_message(int error) noexcept; #endif // SIMDJSON_ERROR_H /* end file simdjson/error.h */ /* skipped duplicate #include "simdjson/portability.h" */ +/* including simdjson/concepts.h: #include "simdjson/concepts.h" */ +/* begin file simdjson/concepts.h */ +#ifndef SIMDJSON_CONCEPTS_H +#define SIMDJSON_CONCEPTS_H +#if SIMDJSON_SUPPORTS_DESERIALIZATION + +#include +#include + +namespace simdjson { +namespace concepts { + +namespace details { +#define SIMDJSON_IMPL_CONCEPT(name, method) \ + template \ + concept supports_##name = !std::is_const_v && requires { \ + typename std::remove_cvref_t::value_type; \ + requires requires(typename std::remove_cvref_t::value_type &&val, \ + T obj) { \ + obj.method(std::move(val)); \ + requires !requires { obj = std::move(val); }; \ + }; \ + }; + +SIMDJSON_IMPL_CONCEPT(emplace_back, emplace_back) +SIMDJSON_IMPL_CONCEPT(emplace, emplace) +SIMDJSON_IMPL_CONCEPT(push_back, push_back) +SIMDJSON_IMPL_CONCEPT(add, add) +SIMDJSON_IMPL_CONCEPT(push, push) +SIMDJSON_IMPL_CONCEPT(append, append) +SIMDJSON_IMPL_CONCEPT(insert, insert) +SIMDJSON_IMPL_CONCEPT(op_append, operator+=) + +#undef SIMDJSON_IMPL_CONCEPT +} // namespace details + +/// Check if T is a container that we can append to, including: +/// std::vector, std::deque, std::list, std::string, ... +template +concept appendable_containers = + details::supports_emplace_back || details::supports_emplace || + details::supports_push_back || details::supports_push || + details::supports_add || details::supports_append || + details::supports_insert; + +/// Insert into the container however possible +template +constexpr decltype(auto) emplace_one(T &vec, Args &&...args) { + if constexpr (details::supports_emplace_back) { + return vec.emplace_back(std::forward(args)...); + } else if constexpr (details::supports_emplace) { + return vec.emplace(std::forward(args)...); + } else if constexpr (details::supports_push_back) { + return vec.push_back(std::forward(args)...); + } else if constexpr (details::supports_push) { + return vec.push(std::forward(args)...); + } else if constexpr (details::supports_add) { + return vec.add(std::forward(args)...); + } else if constexpr (details::supports_append) { + return vec.append(std::forward(args)...); + } else if constexpr (details::supports_insert) { + return vec.insert(std::forward(args)...); + } else if constexpr (details::supports_op_append && sizeof...(Args) == 1) { + return vec.operator+=(std::forward(args)...); + } else { + static_assert(!sizeof(T *), + "We don't know how to add things to this container"); + } +} + +/// This checks if the container will return a reference to the newly added +/// element after an insert which for example `std::vector::emplace_back` does +/// since C++17; this will allow some optimizations. +template +concept returns_reference = appendable_containers && requires { + typename std::remove_cvref_t::reference; + requires requires(typename std::remove_cvref_t::value_type &&val, T obj) { + { + emplace_one(obj, std::move(val)) + } -> std::same_as::reference>; + }; +}; + +template +concept smart_pointer = requires(std::remove_cvref_t ptr) { + // Check if T has a member type named element_type + typename std::remove_cvref_t::element_type; + + // Check if T has a get() member function + { + ptr.get() + } -> std::same_as::element_type *>; + + // Check if T can be dereferenced + { *ptr } -> std::same_as::element_type &>; +}; + +template +concept optional_type = requires(std::remove_cvref_t obj) { + typename std::remove_cvref_t::value_type; + { obj.value() } -> std::same_as::value_type&>; + requires requires(typename std::remove_cvref_t::value_type &&val) { + obj.emplace(std::move(val)); + obj = std::move(val); + { + obj.value_or(val) + } -> std::convertible_to::value_type>; + }; + { static_cast(obj) } -> std::same_as; // convertible to bool +}; + +} // namespace concepts +} // namespace simdjson +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +#endif // SIMDJSON_CONCEPTS_H +/* end file simdjson/concepts.h */ /** * @brief The top level simdjson namespace, containing everything the library provides. @@ -6447,7 +6630,6 @@ extern SIMDJSON_DLLIMPORTEXPORT const uint64_t thintable_epi8[256]; #endif // SIMDJSON_INTERNAL_SIMDPRUNE_TABLES_H /* end file simdjson/internal/simdprune_tables.h */ - #endif // SIMDJSON_GENERIC_DEPENDENCIES_H /* end file simdjson/generic/dependencies.h */ /* including generic/dependencies.h: #include */ @@ -7623,7 +7805,7 @@ SIMDJSON_NO_SANITIZE_UNDEFINED // See issue https://github.com/simdjson/simdjson/issues/1965 SIMDJSON_NO_SANITIZE_MEMORY simdjson_inline int trailing_zeroes(uint64_t input_num) { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO unsigned long ret; // Search the mask data from least significant bit (LSB) // to the most significant bit (MSB) for a set bit (1). @@ -7639,9 +7821,15 @@ simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) { return input_num & (input_num-1); } +// We sometimes call leading_zeroes on inputs that are zero, +// but the algorithms do not end up using the returned value. +// Sadly, sanitizers are not smart enough to figure it out. +// Applies only when SIMDJSON_PREFER_REVERSE_BITS is defined and true. +// (See below.) +SIMDJSON_NO_SANITIZE_UNDEFINED /* result might be undefined when input_num is zero */ simdjson_inline int leading_zeroes(uint64_t input_num) { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO unsigned long leading_zero = 0; // Search the mask data from most significant bit (MSB) // to least significant bit (LSB) for a set bit (1). @@ -7694,7 +7882,7 @@ simdjson_inline uint64_t zero_leading_bit(uint64_t rev_bits, int leading_zeroes) #endif simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t *result) { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO *result = value1 + value2; return *result < value1; #else @@ -7811,7 +7999,13 @@ simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t } // namespace arm64 } // namespace simdjson +#ifndef SIMDJSON_SWAR_NUMBER_PARSING +#if SIMDJSON_IS_BIG_ENDIAN +#define SIMDJSON_SWAR_NUMBER_PARSING 0 +#else #define SIMDJSON_SWAR_NUMBER_PARSING 1 +#endif +#endif #endif // SIMDJSON_ARM64_NUMBERPARSING_DEFS_H /* end file simdjson/arm64/numberparsing_defs.h */ @@ -7831,7 +8025,7 @@ namespace arm64 { namespace { namespace simd { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO namespace { // Start of private section with Visual Studio workaround @@ -7940,7 +8134,7 @@ namespace { // We return uint32_t instead of uint16_t because that seems to be more efficient for most // purposes (cutting it down to uint16_t costs performance in some compilers). simdjson_inline uint32_t to_bitmask() const { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO const uint8x16_t bit_mask = simdjson_make_uint8x16_t(0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80); #else @@ -7971,7 +8165,7 @@ namespace { // Splat constructor simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {} // Member-by-member initialization -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO simdjson_inline simd8( uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7, uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15 @@ -8065,7 +8259,7 @@ namespace { uint64x2_t shufmask64 = {thintable_epi8[mask1], thintable_epi8[mask2]}; uint8x16_t shufmask = vreinterpretq_u8_u64(shufmask64); // we increment by 0x08 the second half of the mask -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO uint8x16_t inc = simdjson_make_uint8x16_t(0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08); #else uint8x16_t inc = {0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}; @@ -8095,7 +8289,7 @@ namespace { uint8x8_t compactmask1 = vcreate_u8(thintable_epi8[mask1]); uint8x8_t compactmask2 = vcreate_u8(thintable_epi8[mask2]); // we increment by 0x08 the second half of the mask -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO uint8x8_t inc = simdjson_make_uint8x8_t(0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08); #else uint8x8_t inc = {0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}; @@ -8147,7 +8341,7 @@ namespace { // Array constructor simdjson_inline simd8(const int8_t* values) : simd8(load(values)) {} // Member-by-member initialization -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO simdjson_inline simd8( int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7, int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15 @@ -8268,7 +8462,7 @@ namespace { } simdjson_inline uint64_t to_bitmask() const { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO const uint8x16_t bit_mask = simdjson_make_uint8x16_t( 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80 @@ -9428,7 +9622,6 @@ simdjson_unused simdjson_inline simdjson_result get_number_type(con // Our objective is accurate parsing (ULP of 0) at high speed. template simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { - // // Check for minus sign // @@ -10379,7 +10572,7 @@ SIMDJSON_NO_SANITIZE_UNDEFINED // See issue https://github.com/simdjson/simdjson/issues/1965 SIMDJSON_NO_SANITIZE_MEMORY simdjson_inline int trailing_zeroes(uint64_t input_num) { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO unsigned long ret; // Search the mask data from least significant bit (LSB) // to the most significant bit (MSB) for a set bit (1). @@ -10395,9 +10588,15 @@ simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) { return input_num & (input_num-1); } +// We sometimes call leading_zeroes on inputs that are zero, +// but the algorithms do not end up using the returned value. +// Sadly, sanitizers are not smart enough to figure it out. +// Applies only when SIMDJSON_PREFER_REVERSE_BITS is defined and true. +// (See below.) +SIMDJSON_NO_SANITIZE_UNDEFINED /* result might be undefined when input_num is zero */ simdjson_inline int leading_zeroes(uint64_t input_num) { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO unsigned long leading_zero = 0; // Search the mask data from most significant bit (MSB) // to least significant bit (LSB) for a set bit (1). @@ -10450,7 +10649,7 @@ simdjson_inline uint64_t zero_leading_bit(uint64_t rev_bits, int leading_zeroes) #endif simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t *result) { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO *result = value1 + value2; return *result < value1; #else @@ -10567,7 +10766,13 @@ simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t } // namespace arm64 } // namespace simdjson +#ifndef SIMDJSON_SWAR_NUMBER_PARSING +#if SIMDJSON_IS_BIG_ENDIAN +#define SIMDJSON_SWAR_NUMBER_PARSING 0 +#else #define SIMDJSON_SWAR_NUMBER_PARSING 1 +#endif +#endif #endif // SIMDJSON_ARM64_NUMBERPARSING_DEFS_H /* end file simdjson/arm64/numberparsing_defs.h */ @@ -10587,7 +10792,7 @@ namespace arm64 { namespace { namespace simd { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO namespace { // Start of private section with Visual Studio workaround @@ -10696,7 +10901,7 @@ namespace { // We return uint32_t instead of uint16_t because that seems to be more efficient for most // purposes (cutting it down to uint16_t costs performance in some compilers). simdjson_inline uint32_t to_bitmask() const { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO const uint8x16_t bit_mask = simdjson_make_uint8x16_t(0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80); #else @@ -10727,7 +10932,7 @@ namespace { // Splat constructor simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {} // Member-by-member initialization -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO simdjson_inline simd8( uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7, uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15 @@ -10821,7 +11026,7 @@ namespace { uint64x2_t shufmask64 = {thintable_epi8[mask1], thintable_epi8[mask2]}; uint8x16_t shufmask = vreinterpretq_u8_u64(shufmask64); // we increment by 0x08 the second half of the mask -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO uint8x16_t inc = simdjson_make_uint8x16_t(0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08); #else uint8x16_t inc = {0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}; @@ -10851,7 +11056,7 @@ namespace { uint8x8_t compactmask1 = vcreate_u8(thintable_epi8[mask1]); uint8x8_t compactmask2 = vcreate_u8(thintable_epi8[mask2]); // we increment by 0x08 the second half of the mask -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO uint8x8_t inc = simdjson_make_uint8x8_t(0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08); #else uint8x8_t inc = {0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}; @@ -10903,7 +11108,7 @@ namespace { // Array constructor simdjson_inline simd8(const int8_t* values) : simd8(load(values)) {} // Member-by-member initialization -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO simdjson_inline simd8( int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7, int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15 @@ -11024,7 +11229,7 @@ namespace { } simdjson_inline uint64_t to_bitmask() const { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO const uint8x16_t bit_mask = simdjson_make_uint8x16_t( 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80 @@ -13977,6 +14182,7 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::docu return stage2::tape_builder::parse_document(*this, _doc); } +SIMDJSON_NO_SANITIZE_MEMORY simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept { return arm64::stringparsing::parse_string(src, dst, allow_replacement); } @@ -14115,8 +14321,14 @@ static_assert(sizeof(__m256i) <= simdjson::SIMDJSON_PADDING, "insufficient paddi /* end file simdjson/haswell/intrinsics.h */ #if !SIMDJSON_CAN_ALWAYS_RUN_HASWELL +// We enable bmi2 only if LLVM/clang is used, because GCC may not +// make good use of it. See https://github.com/simdjson/simdjson/pull/2243 +#if defined(__clang__) +SIMDJSON_TARGET_REGION("avx2,bmi,bmi2,pclmul,lzcnt,popcnt") +#else SIMDJSON_TARGET_REGION("avx2,bmi,pclmul,lzcnt,popcnt") #endif +#endif /* including simdjson/haswell/bitmanipulation.h: #include "simdjson/haswell/bitmanipulation.h" */ /* begin file simdjson/haswell/bitmanipulation.h */ @@ -15770,7 +15982,6 @@ simdjson_unused simdjson_inline simdjson_result get_number_type(con // Our objective is accurate parsing (ULP of 0) at high speed. template simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { - // // Check for minus sign // @@ -16748,8 +16959,14 @@ static_assert(sizeof(__m256i) <= simdjson::SIMDJSON_PADDING, "insufficient paddi /* end file simdjson/haswell/intrinsics.h */ #if !SIMDJSON_CAN_ALWAYS_RUN_HASWELL +// We enable bmi2 only if LLVM/clang is used, because GCC may not +// make good use of it. See https://github.com/simdjson/simdjson/pull/2243 +#if defined(__clang__) +SIMDJSON_TARGET_REGION("avx2,bmi,bmi2,pclmul,lzcnt,popcnt") +#else SIMDJSON_TARGET_REGION("avx2,bmi,pclmul,lzcnt,popcnt") #endif +#endif /* including simdjson/haswell/bitmanipulation.h: #include "simdjson/haswell/bitmanipulation.h" */ /* begin file simdjson/haswell/bitmanipulation.h */ @@ -20193,6 +20410,7 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::docu return stage2::tape_builder::parse_document(*this, _doc); } +SIMDJSON_NO_SANITIZE_MEMORY simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst, bool replacement_char) const noexcept { return haswell::stringparsing::parse_string(src, dst, replacement_char); } @@ -21984,7 +22202,6 @@ simdjson_unused simdjson_inline simdjson_result get_number_type(con // Our objective is accurate parsing (ULP of 0) at high speed. template simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { - // // Check for minus sign // @@ -26448,6 +26665,7 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::docu return stage2::tape_builder::parse_document(*this, _doc); } +SIMDJSON_NO_SANITIZE_MEMORY simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst, bool replacement_char) const noexcept { return icelake::stringparsing::parse_string(src, dst, replacement_char); } @@ -26750,7 +26968,13 @@ simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t } // namespace ppc64 } // namespace simdjson +#ifndef SIMDJSON_SWAR_NUMBER_PARSING +#if SIMDJSON_IS_BIG_ENDIAN +#define SIMDJSON_SWAR_NUMBER_PARSING 0 +#else #define SIMDJSON_SWAR_NUMBER_PARSING 1 +#endif +#endif #endif // SIMDJSON_PPC64_NUMBERPARSING_DEFS_H /* end file simdjson/ppc64/numberparsing_defs.h */ @@ -28354,7 +28578,6 @@ simdjson_unused simdjson_inline simdjson_result get_number_type(con // Our objective is accurate parsing (ULP of 0) at high speed. template simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { - // // Check for minus sign // @@ -29494,7 +29717,13 @@ simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t } // namespace ppc64 } // namespace simdjson +#ifndef SIMDJSON_SWAR_NUMBER_PARSING +#if SIMDJSON_IS_BIG_ENDIAN +#define SIMDJSON_SWAR_NUMBER_PARSING 0 +#else #define SIMDJSON_SWAR_NUMBER_PARSING 1 +#endif +#endif #endif // SIMDJSON_PPC64_NUMBERPARSING_DEFS_H /* end file simdjson/ppc64/numberparsing_defs.h */ @@ -32861,6 +33090,7 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::docu return stage2::tape_builder::parse_document(*this, _doc); } +SIMDJSON_NO_SANITIZE_MEMORY simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst, bool replacement_char) const noexcept { return ppc64::stringparsing::parse_string(src, dst, replacement_char); } @@ -35090,7 +35320,6 @@ simdjson_unused simdjson_inline simdjson_result get_number_type(con // Our objective is accurate parsing (ULP of 0) at high speed. template simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { - // // Check for minus sign // @@ -39950,6 +40179,7 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::docu return stage2::tape_builder::parse_document(*this, _doc); } +SIMDJSON_NO_SANITIZE_MEMORY simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst, bool replacement_char) const noexcept { return westmere::stringparsing::parse_string(src, dst, replacement_char); } @@ -40176,7 +40406,13 @@ simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t } // namespace lsx } // namespace simdjson +#ifndef SIMDJSON_SWAR_NUMBER_PARSING +#if SIMDJSON_IS_BIG_ENDIAN +#define SIMDJSON_SWAR_NUMBER_PARSING 0 +#else #define SIMDJSON_SWAR_NUMBER_PARSING 1 +#endif +#endif #endif // SIMDJSON_LSX_NUMBERPARSING_DEFS_H /* end file simdjson/lsx/numberparsing_defs.h */ @@ -41650,7 +41886,6 @@ simdjson_unused simdjson_inline simdjson_result get_number_type(con // Our objective is accurate parsing (ULP of 0) at high speed. template simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { - // // Check for minus sign // @@ -42705,7 +42940,13 @@ simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t } // namespace lsx } // namespace simdjson +#ifndef SIMDJSON_SWAR_NUMBER_PARSING +#if SIMDJSON_IS_BIG_ENDIAN +#define SIMDJSON_SWAR_NUMBER_PARSING 0 +#else #define SIMDJSON_SWAR_NUMBER_PARSING 1 +#endif +#endif #endif // SIMDJSON_LSX_NUMBERPARSING_DEFS_H /* end file simdjson/lsx/numberparsing_defs.h */ @@ -45936,6 +46177,7 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::docu return stage2::tape_builder::parse_document(*this, _doc); } +SIMDJSON_NO_SANITIZE_MEMORY simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept { return lsx::stringparsing::parse_string(src, dst, allow_replacement); } @@ -46159,7 +46401,13 @@ simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t } // namespace lasx } // namespace simdjson +#ifndef SIMDJSON_SWAR_NUMBER_PARSING +#if SIMDJSON_IS_BIG_ENDIAN +#define SIMDJSON_SWAR_NUMBER_PARSING 0 +#else #define SIMDJSON_SWAR_NUMBER_PARSING 1 +#endif +#endif #endif // SIMDJSON_LASX_NUMBERPARSING_DEFS_H /* end file simdjson/lasx/numberparsing_defs.h */ @@ -47649,7 +47897,6 @@ simdjson_unused simdjson_inline simdjson_result get_number_type(con // Our objective is accurate parsing (ULP of 0) at high speed. template simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { - // // Check for minus sign // @@ -48704,7 +48951,13 @@ simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t } // namespace lasx } // namespace simdjson +#ifndef SIMDJSON_SWAR_NUMBER_PARSING +#if SIMDJSON_IS_BIG_ENDIAN +#define SIMDJSON_SWAR_NUMBER_PARSING 0 +#else #define SIMDJSON_SWAR_NUMBER_PARSING 1 +#endif +#endif #endif // SIMDJSON_LASX_NUMBERPARSING_DEFS_H /* end file simdjson/lasx/numberparsing_defs.h */ @@ -51947,6 +52200,7 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::docu return stage2::tape_builder::parse_document(*this, _doc); } +SIMDJSON_NO_SANITIZE_MEMORY simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst, bool allow_replacement) const noexcept { return lasx::stringparsing::parse_string(src, dst, allow_replacement); } @@ -52188,7 +52442,13 @@ simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t } // namespace fallback } // namespace simdjson +#ifndef SIMDJSON_SWAR_NUMBER_PARSING +#if SIMDJSON_IS_BIG_ENDIAN +#define SIMDJSON_SWAR_NUMBER_PARSING 0 +#else #define SIMDJSON_SWAR_NUMBER_PARSING 1 +#endif +#endif #endif // SIMDJSON_FALLBACK_NUMBERPARSING_DEFS_H /* end file simdjson/fallback/numberparsing_defs.h */ @@ -53247,7 +53507,6 @@ simdjson_unused simdjson_inline simdjson_result get_number_type(con // Our objective is accurate parsing (ULP of 0) at high speed. template simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { - // // Check for minus sign // @@ -54322,7 +54581,13 @@ simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t } // namespace fallback } // namespace simdjson +#ifndef SIMDJSON_SWAR_NUMBER_PARSING +#if SIMDJSON_IS_BIG_ENDIAN +#define SIMDJSON_SWAR_NUMBER_PARSING 0 +#else #define SIMDJSON_SWAR_NUMBER_PARSING 1 +#endif +#endif #endif // SIMDJSON_FALLBACK_NUMBERPARSING_DEFS_H /* end file simdjson/fallback/numberparsing_defs.h */ @@ -55909,6 +56174,7 @@ simdjson_warn_unused error_code dom_parser_implementation::stage2_next(dom::docu return stage2::tape_builder::parse_document(*this, _doc); } +SIMDJSON_NO_SANITIZE_MEMORY simdjson_warn_unused uint8_t *dom_parser_implementation::parse_string(const uint8_t *src, uint8_t *dst, bool replacement_char) const noexcept { return fallback::stringparsing::parse_string(src, dst, replacement_char); } diff --git a/3rdparty/simdjson/simdjson.h b/3rdparty/simdjson/simdjson.h index f21cd9381eef..fb82b032bd0c 100644 --- a/3rdparty/simdjson/simdjson.h +++ b/3rdparty/simdjson/simdjson.h @@ -1,4 +1,4 @@ -/* auto-generated on 2024-08-26 09:37:03 -0400. Do not edit! */ +/* auto-generated on 2025-01-27 20:34:35 -0500. Do not edit! */ /* including simdjson.h: */ /* begin file simdjson.h */ #ifndef SIMDJSON_H @@ -97,6 +97,30 @@ #endif #endif +#ifdef __has_include +#if __has_include() +#include +#endif +#endif + +#if defined(__apple_build_version__) +#if __apple_build_version__ < 14000000 +#define SIMDJSON_CONCEPT_DISABLED 1 // apple-clang/13 doesn't support std::convertible_to +#endif +#endif + + +#if defined(__cpp_concepts) && !defined(SIMDJSON_CONCEPT_DISABLED) +#if __cpp_concepts >= 201907L +#include +#define SIMDJSON_SUPPORTS_DESERIALIZATION 1 +#else +#define SIMDJSON_SUPPORTS_DESERIALIZATION 0 +#endif +#else // defined(__cpp_concepts) && !defined(SIMDJSON_CONCEPT_DISABLED) +#define SIMDJSON_SUPPORTS_DESERIALIZATION 0 +#endif // defined(__cpp_concepts) && !defined(SIMDJSON_CONCEPT_DISABLED) + #endif // SIMDJSON_COMPILER_CHECK_H /* end file simdjson/compiler_check.h */ /* including simdjson/portability.h: #include "simdjson/portability.h" */ @@ -109,11 +133,15 @@ #include #include #include +#include #ifndef _WIN32 // strcasecmp, strncasecmp #include #endif +static_assert(CHAR_BIT == 8, "simdjson requires 8-bit bytes"); + + // We are using size_t without namespace std:: throughout the project using std::size_t; @@ -147,6 +175,7 @@ using std::size_t; #elif defined(__loongarch_lp64) #define SIMDJSON_IS_LOONGARCH64 1 #elif defined(__PPC64__) || defined(_M_PPC64) +#define SIMDJSON_IS_PPC64 1 #if defined(__ALTIVEC__) #define SIMDJSON_IS_PPC64_VMX 1 #endif // defined(__ALTIVEC__) @@ -304,6 +333,45 @@ using std::size_t; #endif + + +#if defined __BYTE_ORDER__ && defined __ORDER_BIG_ENDIAN__ +#define SIMDJSON_IS_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +#elif defined _WIN32 +#define SIMDJSON_IS_BIG_ENDIAN 0 +#else +#if defined(__APPLE__) || defined(__FreeBSD__) +#include +#elif defined(sun) || defined(__sun) +#include +#elif defined(__MVS__) +#include +#else +#ifdef __has_include +#if __has_include() +#include +#endif //__has_include() +#endif //__has_include +#endif +# +#ifndef __BYTE_ORDER__ +// safe choice +#define SIMDJSON_IS_BIG_ENDIAN 0 +#endif +# +#ifndef __ORDER_LITTLE_ENDIAN__ +// safe choice +#define SIMDJSON_IS_BIG_ENDIAN 0 +#endif +# +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define SIMDJSON_IS_BIG_ENDIAN 0 +#else +#define SIMDJSON_IS_BIG_ENDIAN 1 +#endif +#endif + + #endif // SIMDJSON_PORTABILITY_H /* end file simdjson/portability.h */ @@ -569,7 +637,6 @@ SIMDJSON_PUSH_DISABLE_ALL_WARNINGS // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -#pragma once #ifndef NONSTD_SV_LITE_H_INCLUDED #define NONSTD_SV_LITE_H_INCLUDED @@ -2370,7 +2437,7 @@ namespace std { #define SIMDJSON_SIMDJSON_VERSION_H /** The version of simdjson being used (major.minor.revision) */ -#define SIMDJSON_VERSION "3.10.1" +#define SIMDJSON_VERSION "3.12.0" namespace simdjson { enum { @@ -2381,11 +2448,11 @@ enum { /** * The minor version (major.MINOR.revision) of simdjson being used. */ - SIMDJSON_VERSION_MINOR = 10, + SIMDJSON_VERSION_MINOR = 12, /** * The revision (major.minor.REVISION) of simdjson being used. */ - SIMDJSON_VERSION_REVISION = 1 + SIMDJSON_VERSION_REVISION = 0 }; } // namespace simdjson @@ -2493,7 +2560,7 @@ struct simdjson_error : public std::exception { */ simdjson_error(error_code error) noexcept : _error{error} { } /** The error message */ - const char *what() const noexcept { return error_message(error()); } + const char *what() const noexcept override { return error_message(error()); } /** The error code */ error_code error() const noexcept { return _error; } private: @@ -2725,6 +2792,122 @@ inline const std::string error_message(int error) noexcept; #endif // SIMDJSON_ERROR_H /* end file simdjson/error.h */ /* skipped duplicate #include "simdjson/portability.h" */ +/* including simdjson/concepts.h: #include "simdjson/concepts.h" */ +/* begin file simdjson/concepts.h */ +#ifndef SIMDJSON_CONCEPTS_H +#define SIMDJSON_CONCEPTS_H +#if SIMDJSON_SUPPORTS_DESERIALIZATION + +#include +#include + +namespace simdjson { +namespace concepts { + +namespace details { +#define SIMDJSON_IMPL_CONCEPT(name, method) \ + template \ + concept supports_##name = !std::is_const_v && requires { \ + typename std::remove_cvref_t::value_type; \ + requires requires(typename std::remove_cvref_t::value_type &&val, \ + T obj) { \ + obj.method(std::move(val)); \ + requires !requires { obj = std::move(val); }; \ + }; \ + }; + +SIMDJSON_IMPL_CONCEPT(emplace_back, emplace_back) +SIMDJSON_IMPL_CONCEPT(emplace, emplace) +SIMDJSON_IMPL_CONCEPT(push_back, push_back) +SIMDJSON_IMPL_CONCEPT(add, add) +SIMDJSON_IMPL_CONCEPT(push, push) +SIMDJSON_IMPL_CONCEPT(append, append) +SIMDJSON_IMPL_CONCEPT(insert, insert) +SIMDJSON_IMPL_CONCEPT(op_append, operator+=) + +#undef SIMDJSON_IMPL_CONCEPT +} // namespace details + +/// Check if T is a container that we can append to, including: +/// std::vector, std::deque, std::list, std::string, ... +template +concept appendable_containers = + details::supports_emplace_back || details::supports_emplace || + details::supports_push_back || details::supports_push || + details::supports_add || details::supports_append || + details::supports_insert; + +/// Insert into the container however possible +template +constexpr decltype(auto) emplace_one(T &vec, Args &&...args) { + if constexpr (details::supports_emplace_back) { + return vec.emplace_back(std::forward(args)...); + } else if constexpr (details::supports_emplace) { + return vec.emplace(std::forward(args)...); + } else if constexpr (details::supports_push_back) { + return vec.push_back(std::forward(args)...); + } else if constexpr (details::supports_push) { + return vec.push(std::forward(args)...); + } else if constexpr (details::supports_add) { + return vec.add(std::forward(args)...); + } else if constexpr (details::supports_append) { + return vec.append(std::forward(args)...); + } else if constexpr (details::supports_insert) { + return vec.insert(std::forward(args)...); + } else if constexpr (details::supports_op_append && sizeof...(Args) == 1) { + return vec.operator+=(std::forward(args)...); + } else { + static_assert(!sizeof(T *), + "We don't know how to add things to this container"); + } +} + +/// This checks if the container will return a reference to the newly added +/// element after an insert which for example `std::vector::emplace_back` does +/// since C++17; this will allow some optimizations. +template +concept returns_reference = appendable_containers && requires { + typename std::remove_cvref_t::reference; + requires requires(typename std::remove_cvref_t::value_type &&val, T obj) { + { + emplace_one(obj, std::move(val)) + } -> std::same_as::reference>; + }; +}; + +template +concept smart_pointer = requires(std::remove_cvref_t ptr) { + // Check if T has a member type named element_type + typename std::remove_cvref_t::element_type; + + // Check if T has a get() member function + { + ptr.get() + } -> std::same_as::element_type *>; + + // Check if T can be dereferenced + { *ptr } -> std::same_as::element_type &>; +}; + +template +concept optional_type = requires(std::remove_cvref_t obj) { + typename std::remove_cvref_t::value_type; + { obj.value() } -> std::same_as::value_type&>; + requires requires(typename std::remove_cvref_t::value_type &&val) { + obj.emplace(std::move(val)); + obj = std::move(val); + { + obj.value_or(val) + } -> std::convertible_to::value_type>; + }; + { static_cast(obj) } -> std::same_as; // convertible to bool +}; + +} // namespace concepts +} // namespace simdjson +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +#endif // SIMDJSON_CONCEPTS_H +/* end file simdjson/concepts.h */ /** * @brief The top level simdjson namespace, containing everything the library provides. @@ -3655,9 +3838,9 @@ inline std::ostream& operator<<(std::ostream& out, simdjson_result &s) noexcept(false); #endif +/** + * Create a padded_string_view from a string. The string will be padded with SIMDJSON_PADDING + * space characters. The resulting padded_string_view will have a length equal to the original + * string. + * + * @param s The string. + * @return The padded string. + */ +inline padded_string_view pad(std::string& s) noexcept; } // namespace simdjson #endif // SIMDJSON_PADDED_STRING_VIEW_H @@ -3859,6 +4051,11 @@ inline bool padded_string_view::remove_utf8_bom() noexcept { inline std::ostream& operator<<(std::ostream& out, simdjson_result &s) noexcept(false) { return out << s.value(); } #endif +inline padded_string_view pad(std::string& s) noexcept { + const auto len = s.size(); + s.append(SIMDJSON_PADDING, ' '); + return padded_string_view(s.data(), len, s.size()); +} } // namespace simdjson @@ -3911,6 +4108,9 @@ inline padded_string::padded_string(const char *data, size_t length) noexcept if ((data != nullptr) && (data_ptr != nullptr)) { std::memcpy(data_ptr, data, length); } + if (data_ptr == nullptr) { + viable_size = 0; + } } #ifdef __cpp_char8_t inline padded_string::padded_string(const char8_t *data, size_t length) noexcept @@ -3918,12 +4118,17 @@ inline padded_string::padded_string(const char8_t *data, size_t length) noexcept if ((data != nullptr) && (data_ptr != nullptr)) { std::memcpy(data_ptr, reinterpret_cast(data), length); } + if (data_ptr == nullptr) { + viable_size = 0; + } } #endif // note: do not pass std::string arguments by value inline padded_string::padded_string(const std::string & str_ ) noexcept : viable_size(str_.size()), data_ptr(internal::allocate_padded_buffer(str_.size())) { - if (data_ptr != nullptr) { + if (data_ptr == nullptr) { + viable_size = 0; + } else { std::memcpy(data_ptr, str_.data(), str_.size()); } } @@ -4037,11 +4242,11 @@ inline simdjson_result padded_string::load(std::string_view filen } // namespace simdjson -inline simdjson::padded_string operator "" _padded(const char *str, size_t len) { +inline simdjson::padded_string operator ""_padded(const char *str, size_t len) { return simdjson::padded_string(str, len); } #ifdef __cpp_char8_t -inline simdjson::padded_string operator "" _padded(const char8_t *str, size_t len) { +inline simdjson::padded_string operator ""_padded(const char8_t *str, size_t len) { return simdjson::padded_string(reinterpret_cast(str), len); } #endif @@ -4275,6 +4480,21 @@ class array { */ inline simdjson_result at_pointer(std::string_view json_pointer) const noexcept; + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00 + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + inline simdjson_result at_path(std::string_view json_path) const noexcept; + /** * Get the value at the given index. This function has linear-time complexity and * is equivalent to the following: @@ -4319,6 +4539,7 @@ struct simdjson_result : public internal::simdjson_result_base at_pointer(std::string_view json_pointer) const noexcept; + inline simdjson_result at_path(std::string_view json_path) const noexcept; inline simdjson_result at(size_t index) const noexcept; #if SIMDJSON_EXCEPTIONS @@ -4654,6 +4875,22 @@ class parser { * simdjson::dom::parser parser; * simdjson::dom::element element = parser.parse(padded_json_copy.get(), json_len, false); * + * ### std::string references + * + * If you pass a mutable std::string reference (std::string&), the parser will seek to extend + * its capacity to SIMDJSON_PADDING bytes beyond the end of the string. + * + * Whenever you pass an std::string reference, the parser will access the bytes beyond the end of + * the string but before the end of the allocated memory (std::string::capacity()). + * If you are using a sanitizer that checks for reading uninitialized bytes or std::string's + * container-overflow checks, you may encounter sanitizer warnings. + * You can safely ignore these warnings. Or you can call simdjson::pad(std::string&) to pad the + * string with SIMDJSON_PADDING spaces: this function returns a simdjson::padding_string_view + * which can be be passed to the parser's parse function: + * + * std::string json = R"({ "foo": 1 } { "foo": 2 } { "foo": 3 } )"; + * element doc = parser.parse(simdjson::pad(json)); + * * ### Parser Capacity * * If the parser's current capacity is less than len, it will allocate enough capacity @@ -5001,9 +5238,14 @@ class parser { /** * The parser instance can use threads when they are available to speed up some * operations. It is enabled by default. Changing this attribute will change the - * behavior of the parser for future operations. + * behavior of the parser for future operations. Set to true by default. */ bool threaded{true}; +#else + /** + * When SIMDJSON_THREADS_ENABLED is not defined, the parser instance cannot use threads. + */ + bool threaded{false}; #endif /** @private Use the new DOM API instead */ class Iterator; @@ -5796,6 +6038,8 @@ class element { * - INCORRECT_TYPE if this is not an object */ inline simdjson_result operator[](const char *key) const noexcept; + simdjson_result operator[](int) const noexcept = delete; + /** * Get the value associated with the given JSON pointer. We use the RFC 6901 @@ -5821,6 +6065,21 @@ class element { */ inline simdjson_result at_pointer(const std::string_view json_pointer) const noexcept; + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00 + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + inline simdjson_result at_path(std::string_view json_path) const noexcept; + #ifndef SIMDJSON_DISABLE_DEPRECATED_API /** * @@ -5949,7 +6208,9 @@ struct simdjson_result : public internal::simdjson_result_base operator[](std::string_view key) const noexcept; simdjson_inline simdjson_result operator[](const char *key) const noexcept; + simdjson_result operator[](int) const noexcept = delete; simdjson_inline simdjson_result at_pointer(const std::string_view json_pointer) const noexcept; + simdjson_inline simdjson_result at_path(const std::string_view json_path) const noexcept; [[deprecated("For standard compliance, use at_pointer instead, and prefix your pointers with a slash '/', see RFC6901 ")]] simdjson_inline simdjson_result at(const std::string_view json_pointer) const noexcept; simdjson_inline simdjson_result at(size_t index) const noexcept; @@ -6124,6 +6385,7 @@ class object { * - INCORRECT_TYPE if this is not an object */ inline simdjson_result operator[](const char *key) const noexcept; + simdjson_result operator[](int) const noexcept = delete; /** * Get the value associated with the given JSON pointer. We use the RFC 6901 @@ -6150,6 +6412,21 @@ class object { */ inline simdjson_result at_pointer(std::string_view json_pointer) const noexcept; + /** + * Get the value associated with the given JSONPath expression. We only support + * JSONPath queries that trivially convertible to JSON Pointer queries: key + * names and array indices. + * + * https://datatracker.ietf.org/doc/html/draft-normington-jsonpath-00 + * + * @return The value associated with the given JSONPath expression, or: + * - INVALID_JSON_POINTER if the JSONPath to JSON Pointer conversion fails + * - NO_SUCH_FIELD if a field does not exist in an object + * - INDEX_OUT_OF_BOUNDS if an array index is larger than an array length + * - INCORRECT_TYPE if a non-integer is used to access an array + */ + inline simdjson_result at_path(std::string_view json_path) const noexcept; + /** * Get the value associated with the given key. * @@ -6222,7 +6499,9 @@ struct simdjson_result : public internal::simdjson_result_base operator[](std::string_view key) const noexcept; inline simdjson_result operator[](const char *key) const noexcept; + simdjson_result operator[](int) const noexcept = delete; inline simdjson_result at_pointer(std::string_view json_pointer) const noexcept; + inline simdjson_result at_path(std::string_view json_path) const noexcept; inline simdjson_result at_key(std::string_view key) const noexcept; inline simdjson_result at_key_case_insensitive(std::string_view key) const noexcept; @@ -6529,6 +6808,73 @@ std::string prettify(simdjson_result x) { /* skipped duplicate #include "simdjson/dom/array.h" */ /* skipped duplicate #include "simdjson/dom/element.h" */ /* skipped duplicate #include "simdjson/error-inl.h" */ +/* including simdjson/jsonpathutil.h: #include "simdjson/jsonpathutil.h" */ +/* begin file simdjson/jsonpathutil.h */ +#ifndef SIMDJSON_JSONPATHUTIL_H +#define SIMDJSON_JSONPATHUTIL_H + +#include +#include + +namespace simdjson { +/** + * Converts JSONPath to JSON Pointer. + * @param json_path The JSONPath string to be converted. + * @return A string containing the equivalent JSON Pointer. + */ +inline std::string json_path_to_pointer_conversion(std::string_view json_path) { + size_t i = 0; + + // if JSONPath starts with $, skip it + if (!json_path.empty() && json_path.front() == '$') { + i = 1; + } + if (json_path.empty() || (json_path[i] != '.' && + json_path[i] != '[')) { + return "-1"; // This is just a sentinel value, the caller should check for this and return an error. + } + + std::string result; + // Reserve space to reduce allocations, adjusting for potential increases due + // to escaping. + result.reserve(json_path.size() * 2); + + while (i < json_path.length()) { + if (json_path[i] == '.') { + result += '/'; + } else if (json_path[i] == '[') { + result += '/'; + ++i; // Move past the '[' + while (i < json_path.length() && json_path[i] != ']') { + if (json_path[i] == '~') { + result += "~0"; + } else if (json_path[i] == '/') { + result += "~1"; + } else { + result += json_path[i]; + } + ++i; + } + if (i == json_path.length() || json_path[i] != ']') { + return "-1"; // Using sentinel value that will be handled as an error by the caller. + } + } else { + if (json_path[i] == '~') { + result += "~0"; + } else if (json_path[i] == '/') { + result += "~1"; + } else { + result += json_path[i]; + } + } + ++i; + } + + return result; +} +} // namespace simdjson +#endif // SIMDJSON_JSONPATHUTIL_H +/* end file simdjson/jsonpathutil.h */ /* including simdjson/internal/tape_ref-inl.h: #include "simdjson/internal/tape_ref-inl.h" */ /* begin file simdjson/internal/tape_ref-inl.h */ #ifndef SIMDJSON_TAPE_REF_INL_H @@ -6716,6 +7062,13 @@ inline simdjson_result simdjson_result::at_pointer(std if (error()) { return error(); } return first.at_pointer(json_pointer); } + + inline simdjson_result simdjson_result::at_path(std::string_view json_path) const noexcept { + auto json_pointer = json_path_to_pointer_conversion(json_path); + if (json_pointer == "-1") { return INVALID_JSON_POINTER; } + return at_pointer(json_pointer); + } + inline simdjson_result simdjson_result::at(size_t index) const noexcept { if (error()) { return error(); } return first.at(index); @@ -6785,6 +7138,12 @@ inline simdjson_result array::at_pointer(std::string_view json_pointer) return child; } +inline simdjson_result array::at_path(std::string_view json_path) const noexcept { + auto json_pointer = json_path_to_pointer_conversion(json_path); + if (json_pointer == "-1") { return INVALID_JSON_POINTER; } + return at_pointer(json_pointer); +} + inline simdjson_result array::at(size_t index) const noexcept { SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 size_t i=0; @@ -6861,6 +7220,7 @@ inline bool array::iterator::operator>(const array::iterator& other) const noexc /* skipped duplicate #include "simdjson/dom/element-inl.h" */ /* skipped duplicate #include "simdjson/error-inl.h" */ +/* skipped duplicate #include "simdjson/jsonpathutil.h" */ #include @@ -6888,6 +7248,11 @@ inline simdjson_result simdjson_result::at_pointer(st if (error()) { return error(); } return first.at_pointer(json_pointer); } +inline simdjson_result simdjson_result::at_path(std::string_view json_path) const noexcept { + auto json_pointer = json_path_to_pointer_conversion(json_path); + if (json_pointer == "-1") { return INVALID_JSON_POINTER; } + return at_pointer(json_pointer); +} inline simdjson_result simdjson_result::at_key(std::string_view key) const noexcept { if (error()) { return error(); } return first.at_key(key); @@ -6985,6 +7350,12 @@ inline simdjson_result object::at_pointer(std::string_view json_pointer return child; } +inline simdjson_result object::at_path(std::string_view json_path) const noexcept { + auto json_pointer = json_path_to_pointer_conversion(json_path); + if (json_pointer == "-1") { return INVALID_JSON_POINTER; } + return at_pointer(json_pointer); +} + inline simdjson_result object::at_key(std::string_view key) const noexcept { iterator end_field = end(); for (iterator field = begin(); field != end_field; ++field) { @@ -7117,6 +7488,7 @@ static_assert(std::ranges::sized_range #include @@ -7230,6 +7602,11 @@ simdjson_inline simdjson_result simdjson_result::at_ if (error()) { return error(); } return first.at_pointer(json_pointer); } +simdjson_inline simdjson_result simdjson_result::at_path(const std::string_view json_path) const noexcept { + auto json_pointer = json_path_to_pointer_conversion(json_path); + if (json_pointer == "-1") { return INVALID_JSON_POINTER; } + return at_pointer(json_pointer); +} #ifndef SIMDJSON_DISABLE_DEPRECATED_API [[deprecated("For standard compliance, use at_pointer instead, and prefix your pointers with a slash '/', see RFC6901 ")]] simdjson_inline simdjson_result simdjson_result::at(const std::string_view json_pointer) const noexcept { @@ -7520,6 +7897,11 @@ inline simdjson_result element::at_pointer(std::string_view json_pointe } } } +inline simdjson_result element::at_path(std::string_view json_path) const noexcept { + auto json_pointer = json_path_to_pointer_conversion(json_path); + if (json_pointer == "-1") { return INVALID_JSON_POINTER; } + return at_pointer(json_pointer); +} #ifndef SIMDJSON_DISABLE_DEPRECATED_API [[deprecated("For standard compliance, use at_pointer instead, and prefix your pointers with a slash '/', see RFC6901 ")]] inline simdjson_result element::at(std::string_view json_pointer) const noexcept { @@ -9424,7 +9806,6 @@ extern SIMDJSON_DLLIMPORTEXPORT const uint64_t thintable_epi8[256]; #endif // SIMDJSON_INTERNAL_SIMDPRUNE_TABLES_H /* end file simdjson/internal/simdprune_tables.h */ - #endif // SIMDJSON_GENERIC_DEPENDENCIES_H /* end file simdjson/generic/dependencies.h */ @@ -9838,7 +10219,7 @@ SIMDJSON_NO_SANITIZE_UNDEFINED // See issue https://github.com/simdjson/simdjson/issues/1965 SIMDJSON_NO_SANITIZE_MEMORY simdjson_inline int trailing_zeroes(uint64_t input_num) { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO unsigned long ret; // Search the mask data from least significant bit (LSB) // to the most significant bit (MSB) for a set bit (1). @@ -9854,9 +10235,15 @@ simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) { return input_num & (input_num-1); } +// We sometimes call leading_zeroes on inputs that are zero, +// but the algorithms do not end up using the returned value. +// Sadly, sanitizers are not smart enough to figure it out. +// Applies only when SIMDJSON_PREFER_REVERSE_BITS is defined and true. +// (See below.) +SIMDJSON_NO_SANITIZE_UNDEFINED /* result might be undefined when input_num is zero */ simdjson_inline int leading_zeroes(uint64_t input_num) { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO unsigned long leading_zero = 0; // Search the mask data from most significant bit (MSB) // to least significant bit (LSB) for a set bit (1). @@ -9909,7 +10296,7 @@ simdjson_inline uint64_t zero_leading_bit(uint64_t rev_bits, int leading_zeroes) #endif simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t *result) { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO *result = value1 + value2; return *result < value1; #else @@ -10026,7 +10413,13 @@ simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t } // namespace arm64 } // namespace simdjson +#ifndef SIMDJSON_SWAR_NUMBER_PARSING +#if SIMDJSON_IS_BIG_ENDIAN +#define SIMDJSON_SWAR_NUMBER_PARSING 0 +#else #define SIMDJSON_SWAR_NUMBER_PARSING 1 +#endif +#endif #endif // SIMDJSON_ARM64_NUMBERPARSING_DEFS_H /* end file simdjson/arm64/numberparsing_defs.h */ @@ -10046,7 +10439,7 @@ namespace arm64 { namespace { namespace simd { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO namespace { // Start of private section with Visual Studio workaround @@ -10155,7 +10548,7 @@ namespace { // We return uint32_t instead of uint16_t because that seems to be more efficient for most // purposes (cutting it down to uint16_t costs performance in some compilers). simdjson_inline uint32_t to_bitmask() const { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO const uint8x16_t bit_mask = simdjson_make_uint8x16_t(0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80); #else @@ -10186,7 +10579,7 @@ namespace { // Splat constructor simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {} // Member-by-member initialization -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO simdjson_inline simd8( uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7, uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15 @@ -10280,7 +10673,7 @@ namespace { uint64x2_t shufmask64 = {thintable_epi8[mask1], thintable_epi8[mask2]}; uint8x16_t shufmask = vreinterpretq_u8_u64(shufmask64); // we increment by 0x08 the second half of the mask -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO uint8x16_t inc = simdjson_make_uint8x16_t(0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08); #else uint8x16_t inc = {0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}; @@ -10310,7 +10703,7 @@ namespace { uint8x8_t compactmask1 = vcreate_u8(thintable_epi8[mask1]); uint8x8_t compactmask2 = vcreate_u8(thintable_epi8[mask2]); // we increment by 0x08 the second half of the mask -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO uint8x8_t inc = simdjson_make_uint8x8_t(0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08); #else uint8x8_t inc = {0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}; @@ -10362,7 +10755,7 @@ namespace { // Array constructor simdjson_inline simd8(const int8_t* values) : simd8(load(values)) {} // Member-by-member initialization -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO simdjson_inline simd8( int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7, int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15 @@ -10483,7 +10876,7 @@ namespace { } simdjson_inline uint64_t to_bitmask() const { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO const uint8x16_t bit_mask = simdjson_make_uint8x16_t( 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80 @@ -11643,7 +12036,6 @@ simdjson_unused simdjson_inline simdjson_result get_number_type(con // Our objective is accurate parsing (ULP of 0) at high speed. template simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { - // // Check for minus sign // @@ -12687,7 +13079,13 @@ simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t } // namespace fallback } // namespace simdjson +#ifndef SIMDJSON_SWAR_NUMBER_PARSING +#if SIMDJSON_IS_BIG_ENDIAN +#define SIMDJSON_SWAR_NUMBER_PARSING 0 +#else #define SIMDJSON_SWAR_NUMBER_PARSING 1 +#endif +#endif #endif // SIMDJSON_FALLBACK_NUMBERPARSING_DEFS_H /* end file simdjson/fallback/numberparsing_defs.h */ @@ -13746,7 +14144,6 @@ simdjson_unused simdjson_inline simdjson_result get_number_type(con // Our objective is accurate parsing (ULP of 0) at high speed. template simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { - // // Check for minus sign // @@ -14686,8 +15083,14 @@ static_assert(sizeof(__m256i) <= simdjson::SIMDJSON_PADDING, "insufficient paddi /* end file simdjson/haswell/intrinsics.h */ #if !SIMDJSON_CAN_ALWAYS_RUN_HASWELL +// We enable bmi2 only if LLVM/clang is used, because GCC may not +// make good use of it. See https://github.com/simdjson/simdjson/pull/2243 +#if defined(__clang__) +SIMDJSON_TARGET_REGION("avx2,bmi,bmi2,pclmul,lzcnt,popcnt") +#else SIMDJSON_TARGET_REGION("avx2,bmi,pclmul,lzcnt,popcnt") #endif +#endif /* including simdjson/haswell/bitmanipulation.h: #include "simdjson/haswell/bitmanipulation.h" */ /* begin file simdjson/haswell/bitmanipulation.h */ @@ -16341,7 +16744,6 @@ simdjson_unused simdjson_inline simdjson_result get_number_type(con // Our objective is accurate parsing (ULP of 0) at high speed. template simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { - // // Check for minus sign // @@ -18935,7 +19337,6 @@ simdjson_unused simdjson_inline simdjson_result get_number_type(con // Our objective is accurate parsing (ULP of 0) at high speed. template simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { - // // Check for minus sign // @@ -20040,7 +20441,13 @@ simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t } // namespace ppc64 } // namespace simdjson +#ifndef SIMDJSON_SWAR_NUMBER_PARSING +#if SIMDJSON_IS_BIG_ENDIAN +#define SIMDJSON_SWAR_NUMBER_PARSING 0 +#else #define SIMDJSON_SWAR_NUMBER_PARSING 1 +#endif +#endif #endif // SIMDJSON_PPC64_NUMBERPARSING_DEFS_H /* end file simdjson/ppc64/numberparsing_defs.h */ @@ -21644,7 +22051,6 @@ simdjson_unused simdjson_inline simdjson_result get_number_type(con // Our objective is accurate parsing (ULP of 0) at high speed. template simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { - // // Check for minus sign // @@ -24676,7 +25082,6 @@ simdjson_unused simdjson_inline simdjson_result get_number_type(con // Our objective is accurate parsing (ULP of 0) at high speed. template simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { - // // Check for minus sign // @@ -25705,7 +26110,13 @@ simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t } // namespace lsx } // namespace simdjson +#ifndef SIMDJSON_SWAR_NUMBER_PARSING +#if SIMDJSON_IS_BIG_ENDIAN +#define SIMDJSON_SWAR_NUMBER_PARSING 0 +#else #define SIMDJSON_SWAR_NUMBER_PARSING 1 +#endif +#endif #endif // SIMDJSON_LSX_NUMBERPARSING_DEFS_H /* end file simdjson/lsx/numberparsing_defs.h */ @@ -27179,7 +27590,6 @@ simdjson_unused simdjson_inline simdjson_result get_number_type(con // Our objective is accurate parsing (ULP of 0) at high speed. template simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { - // // Check for minus sign // @@ -28205,7 +28615,13 @@ simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t } // namespace lasx } // namespace simdjson +#ifndef SIMDJSON_SWAR_NUMBER_PARSING +#if SIMDJSON_IS_BIG_ENDIAN +#define SIMDJSON_SWAR_NUMBER_PARSING 0 +#else #define SIMDJSON_SWAR_NUMBER_PARSING 1 +#endif +#endif #endif // SIMDJSON_LASX_NUMBERPARSING_DEFS_H /* end file simdjson/lasx/numberparsing_defs.h */ @@ -29695,7 +30111,6 @@ simdjson_unused simdjson_inline simdjson_result get_number_type(con // Our objective is accurate parsing (ULP of 0) at high speed. template simdjson_inline error_code parse_number(const uint8_t *const src, W &writer) { - // // Check for minus sign // @@ -30566,6 +30981,7 @@ simdjson_inline implementation_simdjson_result_base::implementation_simdjson_ /* skipped duplicate #include "simdjson/padded_string.h" */ /* skipped duplicate #include "simdjson/padded_string_view.h" */ /* skipped duplicate #include "simdjson/internal/dom_parser_implementation.h" */ +/* skipped duplicate #include "simdjson/jsonpathutil.h" */ #endif // SIMDJSON_GENERIC_ONDEMAND_DEPENDENCIES_H /* end file simdjson/generic/ondemand/dependencies.h */ @@ -30652,7 +31068,7 @@ SIMDJSON_NO_SANITIZE_UNDEFINED // See issue https://github.com/simdjson/simdjson/issues/1965 SIMDJSON_NO_SANITIZE_MEMORY simdjson_inline int trailing_zeroes(uint64_t input_num) { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO unsigned long ret; // Search the mask data from least significant bit (LSB) // to the most significant bit (MSB) for a set bit (1). @@ -30668,9 +31084,15 @@ simdjson_inline uint64_t clear_lowest_bit(uint64_t input_num) { return input_num & (input_num-1); } +// We sometimes call leading_zeroes on inputs that are zero, +// but the algorithms do not end up using the returned value. +// Sadly, sanitizers are not smart enough to figure it out. +// Applies only when SIMDJSON_PREFER_REVERSE_BITS is defined and true. +// (See below.) +SIMDJSON_NO_SANITIZE_UNDEFINED /* result might be undefined when input_num is zero */ simdjson_inline int leading_zeroes(uint64_t input_num) { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO unsigned long leading_zero = 0; // Search the mask data from most significant bit (MSB) // to least significant bit (LSB) for a set bit (1). @@ -30723,7 +31145,7 @@ simdjson_inline uint64_t zero_leading_bit(uint64_t rev_bits, int leading_zeroes) #endif simdjson_inline bool add_overflow(uint64_t value1, uint64_t value2, uint64_t *result) { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO *result = value1 + value2; return *result < value1; #else @@ -30840,7 +31262,13 @@ simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t } // namespace arm64 } // namespace simdjson +#ifndef SIMDJSON_SWAR_NUMBER_PARSING +#if SIMDJSON_IS_BIG_ENDIAN +#define SIMDJSON_SWAR_NUMBER_PARSING 0 +#else #define SIMDJSON_SWAR_NUMBER_PARSING 1 +#endif +#endif #endif // SIMDJSON_ARM64_NUMBERPARSING_DEFS_H /* end file simdjson/arm64/numberparsing_defs.h */ @@ -30860,7 +31288,7 @@ namespace arm64 { namespace { namespace simd { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO namespace { // Start of private section with Visual Studio workaround @@ -30969,7 +31397,7 @@ namespace { // We return uint32_t instead of uint16_t because that seems to be more efficient for most // purposes (cutting it down to uint16_t costs performance in some compilers). simdjson_inline uint32_t to_bitmask() const { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO const uint8x16_t bit_mask = simdjson_make_uint8x16_t(0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80); #else @@ -31000,7 +31428,7 @@ namespace { // Splat constructor simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {} // Member-by-member initialization -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO simdjson_inline simd8( uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7, uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15 @@ -31094,7 +31522,7 @@ namespace { uint64x2_t shufmask64 = {thintable_epi8[mask1], thintable_epi8[mask2]}; uint8x16_t shufmask = vreinterpretq_u8_u64(shufmask64); // we increment by 0x08 the second half of the mask -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO uint8x16_t inc = simdjson_make_uint8x16_t(0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08); #else uint8x16_t inc = {0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}; @@ -31124,7 +31552,7 @@ namespace { uint8x8_t compactmask1 = vcreate_u8(thintable_epi8[mask1]); uint8x8_t compactmask2 = vcreate_u8(thintable_epi8[mask2]); // we increment by 0x08 the second half of the mask -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO uint8x8_t inc = simdjson_make_uint8x8_t(0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08); #else uint8x8_t inc = {0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}; @@ -31176,7 +31604,7 @@ namespace { // Array constructor simdjson_inline simd8(const int8_t* values) : simd8(load(values)) {} // Member-by-member initialization -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO simdjson_inline simd8( int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7, int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15 @@ -31297,7 +31725,7 @@ namespace { } simdjson_inline uint64_t to_bitmask() const { -#ifdef SIMDJSON_REGULAR_VISUAL_STUDIO +#if SIMDJSON_REGULAR_VISUAL_STUDIO const uint8x16_t bit_mask = simdjson_make_uint8x16_t( 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80 @@ -31460,6 +31888,132 @@ class value_iterator; #endif // SIMDJSON_GENERIC_ONDEMAND_BASE_H /* end file simdjson/generic/ondemand/base.h for arm64 */ +/* including simdjson/generic/ondemand/deserialize.h for arm64: #include "simdjson/generic/ondemand/deserialize.h" */ +/* begin file simdjson/generic/ondemand/deserialize.h for arm64 */ +#if SIMDJSON_SUPPORTS_DESERIALIZATION + +#ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_ONDEMAND_DESERIALIZE_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +namespace simdjson { + +namespace tag_invoke_fn_ns { +void tag_invoke(); + +struct tag_invoke_fn { + template + requires requires(Tag tag, Args &&...args) { + tag_invoke(std::forward(tag), std::forward(args)...); + } + constexpr auto operator()(Tag tag, Args &&...args) const + noexcept(noexcept(tag_invoke(std::forward(tag), + std::forward(args)...))) + -> decltype(tag_invoke(std::forward(tag), + std::forward(args)...)) { + return tag_invoke(std::forward(tag), std::forward(args)...); + } +}; +} // namespace tag_invoke_fn_ns + +inline namespace tag_invoke_ns { +inline constexpr tag_invoke_fn_ns::tag_invoke_fn tag_invoke = {}; +} // namespace tag_invoke_ns + +template +concept tag_invocable = requires(Tag tag, Args... args) { + tag_invoke(std::forward(tag), std::forward(args)...); +}; + +template +concept nothrow_tag_invocable = + tag_invocable && requires(Tag tag, Args... args) { + { + tag_invoke(std::forward(tag), std::forward(args)...) + } noexcept; + }; + +template +using tag_invoke_result = + std::invoke_result; + +template +using tag_invoke_result_t = + std::invoke_result_t; + +template using tag_t = std::decay_t; + + +struct deserialize_tag; + +/// These types are deserializable in a built-in way +template struct is_builtin_deserializable : std::false_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; + +template +concept is_builtin_deserializable_v = is_builtin_deserializable::value; + +template +concept custom_deserializable = tag_invocable; + +template +concept deserializable = custom_deserializable || is_builtin_deserializable_v; + +template +concept nothrow_custom_deserializable = nothrow_tag_invocable; + +// built-in types are noexcept and if an error happens, the value simply gets ignored and the error is returned. +template +concept nothrow_deserializable = nothrow_custom_deserializable || is_builtin_deserializable_v; + +/// Deserialize Tag +inline constexpr struct deserialize_tag { + using value_type = arm64::ondemand::value; + using document_type = arm64::ondemand::document; + using document_reference_type = arm64::ondemand::document_reference; + + // Customization Point for value + template + requires custom_deserializable + [[nodiscard]] constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for document + template + requires custom_deserializable + [[nodiscard]] constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for document reference + template + requires custom_deserializable + [[nodiscard]] constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + +} deserialize{}; + +} // namespace simdjson + +#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION + +/* end file simdjson/generic/ondemand/deserialize.h for arm64 */ /* including simdjson/generic/ondemand/value_iterator.h for arm64: #include "simdjson/generic/ondemand/value_iterator.h" */ /* begin file simdjson/generic/ondemand/value_iterator.h for arm64 */ #ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H @@ -31964,12 +32518,15 @@ struct simdjson_result : public arm64::implemen /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ +#include + namespace simdjson { + namespace arm64 { namespace ondemand { - /** * An ephemeral JSON value returned during iteration. It is only valid for as long as you do * not access more data in the JSON document. @@ -31994,16 +32551,21 @@ class value { * @returns A value of the given type, parsed from the JSON. * @returns INCORRECT_TYPE If the JSON value is not the given type. */ - template simdjson_inline simdjson_result get() noexcept { - // Unless the simdjson library or the user provides an inline implementation, calling this method should - // immediately fail. - static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " - "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " - "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." - " You may also add support for custom types, see our documentation."); + template + simdjson_inline simdjson_result get() +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; } + /** * Get this value as the given type. * @@ -32013,7 +32575,32 @@ class value { * @returns INCORRECT_TYPE If the JSON value is not an object. * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ - template simdjson_inline error_code get(T &out) noexcept; + template + simdjson_inline error_code get(T &out) +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_DESERIALIZATION + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION + // Unless the simdjson library or the user provides an inline implementation, calling this method should + // immediately fail. + static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " + "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " + "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." + " You may also add support for custom types, see our documentation."); + static_cast(out); // to get rid of unused errors + return UNINITIALIZED; +#if SIMDJSON_SUPPORTS_DESERIALIZATION + } +#endif + } /** * Cast this JSON value to an array. @@ -32089,6 +32676,17 @@ class value { * Important: a value should be consumed once. Calling get_string() twice on the same value * is an error. * + * In some instances, you may want to allow replacement of invalid Unicode sequences. + * You may do so by passing the allow_replacement parameter as true. In the following + * example, the string "431924697b\udff0L\u0001Y" is not valid Unicode. By passing true + * to get_string, we allow the replacement of the invalid Unicode sequences with the Unicode + * replacement character (U+FFFD). + * + * simdjson::ondemand::parser parser; + * auto json = R"({"deviceId":"431924697b\udff0L\u0001Y"})"_padded; + * simdjson::ondemand::document doc = parser.iterate(json); + * auto view = doc["deviceId"].get_string(true); + * * @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next * time it parses a document or when it is destroyed. * @returns INCORRECT_TYPE if the JSON value is not a string. @@ -32340,6 +32938,7 @@ class value { simdjson_inline simdjson_result operator[](std::string_view key) noexcept; /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ simdjson_inline simdjson_result operator[](const char *key) noexcept; + simdjson_result operator[](int) noexcept = delete; /** * Get the type of this JSON value. It does not validate or consume the value. @@ -32707,6 +33306,7 @@ struct simdjson_result : public arm64::implementation_si simdjson_inline simdjson_result operator[](std::string_view key) noexcept; /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ simdjson_inline simdjson_result operator[](const char *key) noexcept; + simdjson_result operator[](int) noexcept = delete; /** * Get the type of this JSON value. @@ -33762,6 +34362,22 @@ class parser { * using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the * SIMDJSON_PADDING bytes to avoid runtime warnings. * + * ### std::string references + * + * If you pass a mutable std::string reference (std::string&), the parser will seek to extend + * its capacity to SIMDJSON_PADDING bytes beyond the end of the string. + * + * Whenever you pass an std::string reference, the parser will access the bytes beyond the end of + * the string but before the end of the allocated memory (std::string::capacity()). + * If you are using a sanitizer that checks for reading uninitialized bytes or std::string's + * container-overflow checks, you may encounter sanitizer warnings. + * You can safely ignore these warnings. Or you can call simdjson::pad(std::string&) to pad the + * string with SIMDJSON_PADDING spaces: this function returns a simdjson::padding_string_view + * which can be be passed to the parser's iterate function: + * + * std::string json = R"({ "foo": 1 } { "foo": 2 } { "foo": 3 } )"; + * document doc = parser.iterate(simdjson::pad(json)); + * * @param json The JSON to parse. * @param len The length of the JSON. * @param capacity The number of bytes allocated in the JSON (must be at least len+SIMDJSON_PADDING). @@ -33956,8 +34572,12 @@ class parser { * behavior of the parser for future operations. */ bool threaded{true}; + #else + /** + * When SIMDJSON_THREADS_ENABLED is not defined, the parser instance cannot use threads. + */ + bool threaded{false}; #endif - /** * Unescape this JSON string, replacing \\ with \, \n with newline, etc. to a user-provided buffer. * The result must be valid UTF-8. @@ -34099,7 +34719,8 @@ class array { * calling this function, if successful, the array is 'rewinded' at its * beginning as if it had never been accessed. If the JSON is malformed (e.g., * there is a missing comma), then an error is returned and it is no longer - * safe to continue. + * safe to continue. Note that count_elements() does not validate the JSON values, + * only the structure of the array. * * To check that an array is empty, it is more performant to use * the is_empty() method. @@ -34377,8 +34998,11 @@ struct simdjson_result : public arm64::implemen /* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + namespace simdjson { namespace arm64 { namespace ondemand { @@ -34551,24 +35175,39 @@ class document { * @returns A value of the given type, parsed from the JSON. * @returns INCORRECT_TYPE If the JSON value is not the given type. */ - template simdjson_inline simdjson_result get() & noexcept { - // Unless the simdjson library or the user provides an inline implementation, calling this method should - // immediately fail. - static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " - "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " - "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." - " You may also add support for custom types, see our documentation."); - } - /** @overload template simdjson_result get() & noexcept */ - template simdjson_deprecated simdjson_inline simdjson_result get() && noexcept { - // Unless the simdjson library or the user provides an inline implementation, calling this method should - // immediately fail. - static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " - "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " - "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." - " You may also add support for custom types, see our documentation."); + template + simdjson_inline simdjson_result get() & +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "Cannot initialize the specified type."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + /** + * @overload template simdjson_result get() & noexcept + * + * We disallow the use tag_invoke CPO on a moved document; it may create UB + * if user uses `ondemand::array` or `ondemand::object` in their custom type. + * + * The member function is still remains specialize-able for compatibility + * reasons, but we completely disallow its use when a tag_invoke customization + * is provided. + */ + template + simdjson_inline simdjson_result get() && +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(!std::is_same::value && !std::is_same::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document being alive; that would be Undefined Behaviour."); + return static_cast(*this).get(); } /** @@ -34582,7 +35221,32 @@ class document { * @returns INCORRECT_TYPE If the JSON value is not an object. * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ - template simdjson_inline error_code get(T &out) & noexcept; + template + simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_DESERIALIZATION + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION + // Unless the simdjson library or the user provides an inline implementation, calling this method should + // immediately fail. + static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " + "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " + "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." + " You may also add support for custom types, see our documentation."); + static_cast(out); // to get rid of unused errors + return UNINITIALIZED; +#if SIMDJSON_SUPPORTS_DESERIALIZATION + } +#endif + } /** @overload template error_code get(T &out) & noexcept */ template simdjson_deprecated simdjson_inline error_code get(T &out) && noexcept; @@ -34684,7 +35348,8 @@ class document { * calling this function, if successful, the array is 'rewinded' at its * beginning as if it had never been accessed. If the JSON is malformed (e.g., * there is a missing comma), then an error is returned and it is no longer - * safe to continue. + * safe to continue. Note that count_elements() does not validate the JSON values, + * only the structure of the array. */ simdjson_inline simdjson_result count_elements() & noexcept; /** @@ -34796,6 +35461,7 @@ class document { simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; /** * Get the type of this JSON value. It does not validate or consume the value. @@ -35064,6 +35730,11 @@ class document { /** * A document_reference is a thin wrapper around a document reference instance. + * The document_reference instances are used primarily/solely for streams of JSON + * documents. They differ from document instances when parsing a scalar value + * (a document that is not an array or an object). In the case of a document, + * we expect the document to be fully consumed. In the case of a document_reference, + * we allow trailing content. */ class document_reference { public: @@ -35089,7 +35760,70 @@ class document_reference { simdjson_inline simdjson_result get_value() noexcept; simdjson_inline simdjson_result is_null() noexcept; - template simdjson_inline simdjson_result get() & noexcept; + template + simdjson_inline simdjson_result get() & +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "Cannot initialize the specified type."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + template + simdjson_inline simdjson_result get() && +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(!std::is_same::value && !std::is_same::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document_reference being alive; that would be Undefined Behaviour."); + return static_cast(*this).get(); + } + + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool, value + * + * Be mindful that the document instance must remain in scope while you are accessing object, array and value instances. + * + * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. + * @returns INCORRECT_TYPE If the JSON value is not an object. + * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. + */ + template + simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_DESERIALIZATION + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION + // Unless the simdjson library or the user provides an inline implementation, calling this method should + // immediately fail. + static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " + "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " + "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." + " You may also add support for custom types, see our documentation."); + static_cast(out); // to get rid of unused errors + return UNINITIALIZED; +#if SIMDJSON_SUPPORTS_DESERIALIZATION + } +#endif + } + /** @overload template error_code get(T &out) & noexcept */ + template simdjson_inline error_code get(T &out) && noexcept; simdjson_inline simdjson_result raw_json() noexcept; simdjson_inline operator document&() const noexcept; #if SIMDJSON_EXCEPTIONS @@ -35114,6 +35848,7 @@ class document_reference { simdjson_inline simdjson_result find_field(const char *key) & noexcept; simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; @@ -35192,6 +35927,7 @@ struct simdjson_result : public arm64::implementation simdjson_inline simdjson_result find_field(const char *key) & noexcept; simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; simdjson_inline simdjson_result type() noexcept; @@ -35241,8 +35977,14 @@ struct simdjson_result : public arm64::impl simdjson_inline simdjson_result get_bool() noexcept; simdjson_inline simdjson_result get_value() noexcept; simdjson_inline simdjson_result is_null() noexcept; + + template simdjson_inline simdjson_result get() & noexcept; + template simdjson_inline simdjson_result get() && noexcept; + + template simdjson_inline error_code get(T &out) & noexcept; + template simdjson_inline error_code get(T &out) && noexcept; #if SIMDJSON_EXCEPTIONS - template ::value == false>::type> + template explicit simdjson_inline operator T() noexcept(false); simdjson_inline operator arm64::ondemand::array() & noexcept(false); simdjson_inline operator arm64::ondemand::object() & noexcept(false); @@ -35263,6 +36005,7 @@ struct simdjson_result : public arm64::impl simdjson_inline simdjson_result find_field(const char *key) & noexcept; simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; simdjson_inline simdjson_result type() noexcept; @@ -36193,6 +36936,177 @@ inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result +#include + +namespace simdjson { +template +constexpr bool require_custom_serialization = false; + +////////////////////////////// +// Number deserialization +////////////////////////////// + +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + using limits = std::numeric_limits; + + uint64_t x; + SIMDJSON_TRY(val.get_uint64().get(x)); + if (x > (limits::max)()) { + return NUMBER_OUT_OF_RANGE; + } + out = static_cast(x); + return SUCCESS; +} + +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + double x; + SIMDJSON_TRY(val.get_double().get(x)); + out = static_cast(x); + return SUCCESS; +} + +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + using limits = std::numeric_limits; + + int64_t x; + SIMDJSON_TRY(val.get_int64().get(x)); + if (x > (limits::max)() || x < (limits::min)()) { + return NUMBER_OUT_OF_RANGE; + } + out = static_cast(x); + return SUCCESS; +} + +/** + * STL containers have several constructors including one that takes a single + * size argument. Thus, some compilers (Visual Studio) will not be able to + * disambiguate between the size and container constructor. Users should + * explicitly specify the type of the container as needed: e.g., + * doc.get>(). + */ +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { + using value_type = typename std::remove_cvref_t::value_type; + static_assert( + deserializable, + "The specified type inside the container must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the container must default constructible."); + + arm64::ondemand::array arr; + SIMDJSON_TRY(val.get_array().get(arr)); + for (auto v : arr) { + if constexpr (concepts::returns_reference) { + if (auto const err = v.get().get(concepts::emplace_one(out)); + err) { + // If an error occurs, the empty element that we just inserted gets + // removed. We're not using a temp variable because if T is a heavy + // type, we want the valid path to be the fast path and the slow path be + // the path that has errors in it. + if constexpr (requires { out.pop_back(); }) { + static_cast(out.pop_back()); + } + return err; + } + } else { + value_type temp; + if (auto const err = v.get().get(temp); err) { + return err; + } + concepts::emplace_one(out, std::move(temp)); + } + } + return SUCCESS; +} + + + +/** + * This CPO (Customization Point Object) will help deserialize into + * smart pointers. + * + * If constructing T is nothrow, this conversion should be nothrow as well since + * we return MEMALLOC if we're not able to allocate memory instead of throwing + * the error message. + * + * @tparam T The type inside the smart pointer + * @tparam ValT document/value type + * @param val document/value + * @param out a reference to the smart pointer + * @return status of the conversion + */ +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::element_type, ValT>) { + using element_type = typename std::remove_cvref_t::element_type; + + // For better error messages, don't use these as constraints on + // the tag_invoke CPO. + static_assert( + deserializable, + "The specified type inside the unique_ptr must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the unique_ptr must default constructible."); + + auto ptr = new (std::nothrow) element_type(); + if (ptr == nullptr) { + return MEMALLOC; + } + SIMDJSON_TRY(val.template get(*ptr)); + out.reset(ptr); + return SUCCESS; +} + +/** + * This CPO (Customization Point Object) will help deserialize into optional types. + */ +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::value_type, ValT>) { + using value_type = typename std::remove_cvref_t::value_type; + + static_assert( + deserializable, + "The specified type inside the unique_ptr must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the unique_ptr must default constructible."); + + if (!out) { + out.emplace(); + } + SIMDJSON_TRY(val.template get(out.value())); + return SUCCESS; +} + +} // namespace simdjson + +#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +/* end file simdjson/generic/ondemand/std_deserialize.h for arm64 */ + // Inline definitions /* including simdjson/generic/ondemand/array-inl.h for arm64: #include "simdjson/generic/ondemand/array-inl.h" */ /* begin file simdjson/generic/ondemand/array-inl.h for arm64 */ @@ -36200,6 +37114,7 @@ inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result array::at_pointer(std::string_view json_pointer) n return child; } -inline std::string json_path_to_pointer_conversion(std::string_view json_path) { - if (json_path.empty() || (json_path.front() != '.' && - json_path.front() != '[')) { - return "-1"; // This is just a sentinel value, the caller should check for this and return an error. - } - - std::string result; - // Reserve space to reduce allocations, adjusting for potential increases due - // to escaping. - result.reserve(json_path.size() * 2); - - size_t i = 0; - - while (i < json_path.length()) { - if (json_path[i] == '.') { - result += '/'; - } else if (json_path[i] == '[') { - result += '/'; - ++i; // Move past the '[' - while (i < json_path.length() && json_path[i] != ']') { - if (json_path[i] == '~') { - result += "~0"; - } else if (json_path[i] == '/') { - result += "~1"; - } else { - result += json_path[i]; - } - ++i; - } - if (i == json_path.length() || json_path[i] != ']') { - return "-1"; // Using sentinel value that will be handled as an error by the caller. - } - } else { - if (json_path[i] == '~') { - result += "~0"; - } else if (json_path[i] == '/') { - result += "~1"; - } else { - result += json_path[i]; - } - } - ++i; - } - - return result; -} - inline simdjson_result array::at_path(std::string_view json_path) noexcept { auto json_pointer = json_path_to_pointer_conversion(json_path); if (json_pointer == "-1") { return INVALID_JSON_POINTER; } @@ -36561,313 +37429,276 @@ simdjson_inline simdjson_result &simdjson_resul #endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H /* end file simdjson/generic/ondemand/array_iterator-inl.h for arm64 */ -/* including simdjson/generic/ondemand/document-inl.h for arm64: #include "simdjson/generic/ondemand/document-inl.h" */ -/* begin file simdjson/generic/ondemand/document-inl.h for arm64 */ -#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H +/* including simdjson/generic/ondemand/value-inl.h for arm64: #include "simdjson/generic/ondemand/value-inl.h" */ +/* begin file simdjson/generic/ondemand/value-inl.h for arm64 */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ -/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { namespace arm64 { namespace ondemand { -simdjson_inline document::document(ondemand::json_iterator &&_iter) noexcept - : iter{std::forward(_iter)} +simdjson_inline value::value(const value_iterator &_iter) noexcept + : iter{_iter} { - logger::log_start_value(iter, "document"); } - -simdjson_inline document document::start(json_iterator &&iter) noexcept { - return document(std::forward(iter)); +simdjson_inline value value::start(const value_iterator &iter) noexcept { + return iter; } - -inline void document::rewind() noexcept { - iter.rewind(); +simdjson_inline value value::resume(const value_iterator &iter) noexcept { + return iter; } -inline std::string document::to_debug_string() noexcept { - return iter.to_string(); +simdjson_inline simdjson_result value::get_array() noexcept { + return array::start(iter); } - -inline simdjson_result document::current_location() const noexcept { - return iter.current_location(); +simdjson_inline simdjson_result value::get_object() noexcept { + return object::start(iter); } - -inline int32_t document::current_depth() const noexcept { - return iter.depth(); +simdjson_inline simdjson_result value::start_or_resume_object() noexcept { + if (iter.at_start()) { + return get_object(); + } else { + return object::resume(iter); + } } -inline bool document::at_end() const noexcept { - return iter.at_end(); +simdjson_inline simdjson_result value::get_raw_json_string() noexcept { + return iter.get_raw_json_string(); } - - -inline bool document::is_alive() noexcept { - return iter.is_alive(); +simdjson_inline simdjson_result value::get_string(bool allow_replacement) noexcept { + return iter.get_string(allow_replacement); } -simdjson_inline value_iterator document::resume_value_iterator() noexcept { - return value_iterator(&iter, 1, iter.root_position()); +template +simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept { + return iter.get_string(receiver, allow_replacement); } -simdjson_inline value_iterator document::get_root_value_iterator() noexcept { - return resume_value_iterator(); +simdjson_inline simdjson_result value::get_wobbly_string() noexcept { + return iter.get_wobbly_string(); } -simdjson_inline simdjson_result document::start_or_resume_object() noexcept { - if (iter.at_root()) { - return get_object(); - } else { - return object::resume(resume_value_iterator()); - } +simdjson_inline simdjson_result value::get_double() noexcept { + return iter.get_double(); } -simdjson_inline simdjson_result document::get_value() noexcept { - // Make sure we start any arrays or objects before returning, so that start_root_() - // gets called. - - // It is the convention throughout the code that the macro `SIMDJSON_DEVELOPMENT_CHECKS` determines whether - // we check for OUT_OF_ORDER_ITERATION. Proper on::demand code should never trigger this error. -#if SIMDJSON_DEVELOPMENT_CHECKS - if (!iter.at_root()) { return OUT_OF_ORDER_ITERATION; } -#endif - // assert_at_root() serves two purposes: in Debug mode, whether or not - // SIMDJSON_DEVELOPMENT_CHECKS is set or not, it checks that we are at the root of - // the document (this will typically be redundant). In release mode, it generates - // SIMDJSON_ASSUME statements to allow the compiler to make assumptions. - iter.assert_at_root(); - switch (*iter.peek()) { - case '[': { - // The following lines check that the document ends with ]. - auto value_iterator = get_root_value_iterator(); - auto error = value_iterator.check_root_array(); - if(error) { return error; } - return value(get_root_value_iterator()); - } - case '{': { - // The following lines would check that the document ends with }. - auto value_iterator = get_root_value_iterator(); - auto error = value_iterator.check_root_object(); - if(error) { return error; } - return value(get_root_value_iterator()); - } - default: - // Unfortunately, scalar documents are a special case in simdjson and they cannot - // be safely converted to value instances. - return SCALAR_DOCUMENT_AS_VALUE; - } +simdjson_inline simdjson_result value::get_double_in_string() noexcept { + return iter.get_double_in_string(); } -simdjson_inline simdjson_result document::get_array() & noexcept { - auto value = get_root_value_iterator(); - return array::start_root(value); +simdjson_inline simdjson_result value::get_uint64() noexcept { + return iter.get_uint64(); } -simdjson_inline simdjson_result document::get_object() & noexcept { - auto value = get_root_value_iterator(); - return object::start_root(value); +simdjson_inline simdjson_result value::get_uint64_in_string() noexcept { + return iter.get_uint64_in_string(); } - -/** - * We decided that calling 'get_double()' on the JSON document '1.233 blabla' should - * give an error, so we check for trailing content. We want to disallow trailing - * content. - * Thus, in several implementations below, we pass a 'true' parameter value to - * a get_root_value_iterator() method: this indicates that we disallow trailing content. - */ - -simdjson_inline simdjson_result document::get_uint64() noexcept { - return get_root_value_iterator().get_root_uint64(true); +simdjson_inline simdjson_result value::get_int64() noexcept { + return iter.get_int64(); } -simdjson_inline simdjson_result document::get_uint64_in_string() noexcept { - return get_root_value_iterator().get_root_uint64_in_string(true); +simdjson_inline simdjson_result value::get_int64_in_string() noexcept { + return iter.get_int64_in_string(); } -simdjson_inline simdjson_result document::get_int64() noexcept { - return get_root_value_iterator().get_root_int64(true); +simdjson_inline simdjson_result value::get_bool() noexcept { + return iter.get_bool(); } -simdjson_inline simdjson_result document::get_int64_in_string() noexcept { - return get_root_value_iterator().get_root_int64_in_string(true); +simdjson_inline simdjson_result value::is_null() noexcept { + return iter.is_null(); } -simdjson_inline simdjson_result document::get_double() noexcept { - return get_root_value_iterator().get_root_double(true); + +template<> simdjson_inline simdjson_result value::get() noexcept { return get_array(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_object(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_number(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_double(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_bool(); } + + +template<> simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); } +template<> simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); } +template<> simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); } +template<> simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); } +template<> simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); } +template<> simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); } +template<> simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); } +template<> simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); } +template<> simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); } + +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline value::operator T() noexcept(false) { + return get(); } -simdjson_inline simdjson_result document::get_double_in_string() noexcept { - return get_root_value_iterator().get_root_double_in_string(true); +simdjson_inline value::operator array() noexcept(false) { + return get_array(); } -simdjson_inline simdjson_result document::get_string(bool allow_replacement) noexcept { - return get_root_value_iterator().get_root_string(true, allow_replacement); +simdjson_inline value::operator object() noexcept(false) { + return get_object(); } -template -simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept { - return get_root_value_iterator().get_root_string(receiver, true, allow_replacement); +simdjson_inline value::operator uint64_t() noexcept(false) { + return get_uint64(); } -simdjson_inline simdjson_result document::get_wobbly_string() noexcept { - return get_root_value_iterator().get_root_wobbly_string(true); +simdjson_inline value::operator int64_t() noexcept(false) { + return get_int64(); } -simdjson_inline simdjson_result document::get_raw_json_string() noexcept { - return get_root_value_iterator().get_root_raw_json_string(true); +simdjson_inline value::operator double() noexcept(false) { + return get_double(); } -simdjson_inline simdjson_result document::get_bool() noexcept { - return get_root_value_iterator().get_root_bool(true); +simdjson_inline value::operator std::string_view() noexcept(false) { + return get_string(false); } -simdjson_inline simdjson_result document::is_null() noexcept { - return get_root_value_iterator().is_root_null(true); +simdjson_inline value::operator raw_json_string() noexcept(false) { + return get_raw_json_string(); } +simdjson_inline value::operator bool() noexcept(false) { + return get_bool(); +} +#endif -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_array(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_object(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_raw_json_string(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_string(false); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_double(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_uint64(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_int64(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_value(); } - -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_raw_json_string(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_value(); } - -template simdjson_inline error_code document::get(T &out) & noexcept { - return get().get(out); +simdjson_inline simdjson_result value::begin() & noexcept { + return get_array().begin(); } -template simdjson_deprecated simdjson_inline error_code document::get(T &out) && noexcept { - return std::forward(*this).get().get(out); +simdjson_inline simdjson_result value::end() & noexcept { + return {}; } - -#if SIMDJSON_EXCEPTIONS -template -simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get(); } -template -simdjson_inline document::operator T() & noexcept(false) { return get(); } -simdjson_inline document::operator array() & noexcept(false) { return get_array(); } -simdjson_inline document::operator object() & noexcept(false) { return get_object(); } -simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); } -simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); } -simdjson_inline document::operator double() noexcept(false) { return get_double(); } -simdjson_inline document::operator std::string_view() noexcept(false) { return get_string(false); } -simdjson_inline document::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } -simdjson_inline document::operator bool() noexcept(false) { return get_bool(); } -simdjson_inline document::operator value() noexcept(false) { return get_value(); } - -#endif -simdjson_inline simdjson_result document::count_elements() & noexcept { +simdjson_inline simdjson_result value::count_elements() & noexcept { + simdjson_result answer; auto a = get_array(); - simdjson_result answer = a.count_elements(); - /* If there was an array, we are now left pointing at its first element. */ - if(answer.error() == SUCCESS) { rewind(); } + answer = a.count_elements(); + // count_elements leaves you pointing inside the array, at the first element. + // We need to move back so that the user can create a new array (which requires that + // we point at '['). + iter.move_at_start(); return answer; } -simdjson_inline simdjson_result document::count_fields() & noexcept { +simdjson_inline simdjson_result value::count_fields() & noexcept { + simdjson_result answer; auto a = get_object(); - simdjson_result answer = a.count_fields(); - /* If there was an object, we are now left pointing at its first element. */ - if(answer.error() == SUCCESS) { rewind(); } + answer = a.count_fields(); + iter.move_at_start(); return answer; } -simdjson_inline simdjson_result document::at(size_t index) & noexcept { +simdjson_inline simdjson_result value::at(size_t index) noexcept { auto a = get_array(); return a.at(index); } -simdjson_inline simdjson_result document::begin() & noexcept { - return get_array().begin(); -} -simdjson_inline simdjson_result document::end() & noexcept { - return {}; -} -simdjson_inline simdjson_result document::find_field(std::string_view key) & noexcept { +simdjson_inline simdjson_result value::find_field(std::string_view key) noexcept { return start_or_resume_object().find_field(key); } -simdjson_inline simdjson_result document::find_field(const char *key) & noexcept { +simdjson_inline simdjson_result value::find_field(const char *key) noexcept { return start_or_resume_object().find_field(key); } -simdjson_inline simdjson_result document::find_field_unordered(std::string_view key) & noexcept { + +simdjson_inline simdjson_result value::find_field_unordered(std::string_view key) noexcept { return start_or_resume_object().find_field_unordered(key); } -simdjson_inline simdjson_result document::find_field_unordered(const char *key) & noexcept { +simdjson_inline simdjson_result value::find_field_unordered(const char *key) noexcept { return start_or_resume_object().find_field_unordered(key); } -simdjson_inline simdjson_result document::operator[](std::string_view key) & noexcept { + +simdjson_inline simdjson_result value::operator[](std::string_view key) noexcept { return start_or_resume_object()[key]; } -simdjson_inline simdjson_result document::operator[](const char *key) & noexcept { +simdjson_inline simdjson_result value::operator[](const char *key) noexcept { return start_or_resume_object()[key]; } -simdjson_inline error_code document::consume() noexcept { - auto error = iter.skip_child(0); - if(error) { iter.abandon(); } - return error; -} - -simdjson_inline simdjson_result document::raw_json() noexcept { - auto _iter = get_root_value_iterator(); - const uint8_t * starting_point{_iter.peek_start()}; - auto error = consume(); - if(error) { return error; } - // After 'consume()', we could be left pointing just beyond the document, but that - // is ok because we are not going to dereference the final pointer position, we just - // use it to compute the length in bytes. - const uint8_t * final_point{iter.unsafe_pointer()}; - return std::string_view(reinterpret_cast(starting_point), size_t(final_point - starting_point)); -} - -simdjson_inline simdjson_result document::type() noexcept { - return get_root_value_iterator().type(); +simdjson_inline simdjson_result value::type() noexcept { + return iter.type(); } -simdjson_inline simdjson_result document::is_scalar() noexcept { +simdjson_inline simdjson_result value::is_scalar() noexcept { json_type this_type; auto error = type().get(this_type); if(error) { return error; } return ! ((this_type == json_type::array) || (this_type == json_type::object)); } -simdjson_inline simdjson_result document::is_string() noexcept { +simdjson_inline simdjson_result value::is_string() noexcept { json_type this_type; auto error = type().get(this_type); if(error) { return error; } return (this_type == json_type::string); } -simdjson_inline bool document::is_negative() noexcept { - return get_root_value_iterator().is_root_negative(); -} -simdjson_inline simdjson_result document::is_integer() noexcept { - return get_root_value_iterator().is_root_integer(true); +simdjson_inline bool value::is_negative() noexcept { + return iter.is_negative(); } -simdjson_inline simdjson_result document::get_number_type() noexcept { - return get_root_value_iterator().get_root_number_type(true); +simdjson_inline simdjson_result value::is_integer() noexcept { + return iter.is_integer(); +} +simdjson_warn_unused simdjson_inline simdjson_result value::get_number_type() noexcept { + return iter.get_number_type(); +} +simdjson_warn_unused simdjson_inline simdjson_result value::get_number() noexcept { + return iter.get_number(); } -simdjson_inline simdjson_result document::get_number() noexcept { - return get_root_value_iterator().get_root_number(true); +simdjson_inline std::string_view value::raw_json_token() noexcept { + return std::string_view(reinterpret_cast(iter.peek_start()), iter.peek_start_length()); } +simdjson_inline simdjson_result value::raw_json() noexcept { + json_type t; + SIMDJSON_TRY(type().get(t)); + switch (t) + { + case json_type::array: { + ondemand::array array; + SIMDJSON_TRY(get_array().get(array)); + return array.raw_json(); + } + case json_type::object: { + ondemand::object object; + SIMDJSON_TRY(get_object().get(object)); + return object.raw_json(); + } + default: + return raw_json_token(); + } +} -simdjson_inline simdjson_result document::raw_json_token() noexcept { - auto _iter = get_root_value_iterator(); - return std::string_view(reinterpret_cast(_iter.peek_start()), _iter.peek_root_length()); +simdjson_inline simdjson_result value::current_location() noexcept { + return iter.json_iter().current_location(); } -simdjson_inline simdjson_result document::at_pointer(std::string_view json_pointer) noexcept { - rewind(); // Rewind the document each time at_pointer is called - if (json_pointer.empty()) { - return this->get_value(); +simdjson_inline int32_t value::current_depth() const noexcept{ + return iter.json_iter().depth(); +} + +inline bool is_pointer_well_formed(std::string_view json_pointer) noexcept { + if (simdjson_unlikely(json_pointer.empty())) { // can't be + return false; + } + if (simdjson_unlikely(json_pointer[0] != '/')) { + return false; } + size_t escape = json_pointer.find('~'); + if (escape == std::string_view::npos) { + return true; + } + if (escape == json_pointer.size() - 1) { + return false; + } + if (json_pointer[escape + 1] != '0' && json_pointer[escape + 1] != '1') { + return false; + } + return true; +} + +simdjson_inline simdjson_result value::at_pointer(std::string_view json_pointer) noexcept { json_type t; SIMDJSON_TRY(type().get(t)); switch (t) @@ -36877,15 +37708,15 @@ simdjson_inline simdjson_result document::at_pointer(std::string_view jso case json_type::object: return (*this).get_object().at_pointer(json_pointer); default: + // a non-empty string can be invalid, or accessing a primitive (issue 2154) + if (is_pointer_well_formed(json_pointer)) { + return NO_SUCH_FIELD; + } return INVALID_JSON_POINTER; } } -simdjson_inline simdjson_result document::at_path(std::string_view json_path) noexcept { - rewind(); // Rewind the document each time at_pointer is called - if (json_path.empty()) { - return this->get_value(); - } +simdjson_inline simdjson_result value::at_path(std::string_view json_path) noexcept { json_type t; SIMDJSON_TRY(type().get(t)); switch (t) { @@ -36904,614 +37735,1253 @@ simdjson_inline simdjson_result document::at_path(std::string_view json_p namespace simdjson { -simdjson_inline simdjson_result::simdjson_result( - arm64::ondemand::document &&value +simdjson_inline simdjson_result::simdjson_result( + arm64::ondemand::value &&value ) noexcept : - implementation_simdjson_result_base( - std::forward(value) + implementation_simdjson_result_base( + std::forward(value) ) { } -simdjson_inline simdjson_result::simdjson_result( +simdjson_inline simdjson_result::simdjson_result( error_code error ) noexcept : - implementation_simdjson_result_base( - error - ) + implementation_simdjson_result_base(error) { } -simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { if (error()) { return error(); } return first.count_elements(); } -simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { if (error()) { return error(); } return first.count_fields(); } -simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { +simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { if (error()) { return error(); } return first.at(index); } -simdjson_inline error_code simdjson_result::rewind() noexcept { - if (error()) { return error(); } - first.rewind(); - return SUCCESS; -} -simdjson_inline simdjson_result simdjson_result::begin() & noexcept { +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { if (error()) { return error(); } return first.begin(); } -simdjson_inline simdjson_result simdjson_result::end() & noexcept { +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + if (error()) { return error(); } return {}; } -simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) noexcept { if (error()) { return error(); } - return first.find_field_unordered(key); + return first.find_field(key); } -simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) noexcept { if (error()) { return error(); } - return first.find_field_unordered(key); + return first.find_field(key); } -simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) noexcept { if (error()) { return error(); } - return first[key]; + return first.find_field_unordered(key); } -simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) noexcept { if (error()) { return error(); } - return first[key]; + return first.find_field_unordered(key); } -simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) noexcept { if (error()) { return error(); } - return first.find_field(key); + return first[key]; } -simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) noexcept { if (error()) { return error(); } - return first.find_field(key); + return first[key]; } -simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { + +simdjson_inline simdjson_result simdjson_result::get_array() noexcept { if (error()) { return error(); } return first.get_array(); } -simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { +simdjson_inline simdjson_result simdjson_result::get_object() noexcept { if (error()) { return error(); } return first.get_object(); } -simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { if (error()) { return error(); } return first.get_uint64(); } -simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { if (error()) { return error(); } return first.get_uint64_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { if (error()) { return error(); } return first.get_int64(); } -simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { if (error()) { return error(); } return first.get_int64_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_double() noexcept { +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { if (error()) { return error(); } return first.get_double(); } -simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { if (error()) { return error(); } return first.get_double_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(allow_replacement); } template -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(receiver, allow_replacement); } -simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { if (error()) { return error(); } return first.get_wobbly_string(); } -simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { if (error()) { return error(); } return first.get_raw_json_string(); } -simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { if (error()) { return error(); } return first.get_bool(); } -simdjson_inline simdjson_result simdjson_result::get_value() noexcept { - if (error()) { return error(); } - return first.get_value(); -} -simdjson_inline simdjson_result simdjson_result::is_null() noexcept { +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { if (error()) { return error(); } return first.is_null(); } -template -simdjson_inline simdjson_result simdjson_result::get() & noexcept { +template<> simdjson_inline error_code simdjson_result::get(arm64::ondemand::value &out) noexcept { if (error()) { return error(); } - return first.get(); + out = first; + return SUCCESS; } -template -simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + +template simdjson_inline simdjson_result simdjson_result::get() noexcept { if (error()) { return error(); } - return std::forward(first).get(); + return first.get(); } -template -simdjson_inline error_code simdjson_result::get(T &out) & noexcept { +template simdjson_inline error_code simdjson_result::get(T &out) noexcept { if (error()) { return error(); } return first.get(out); } -template -simdjson_inline error_code simdjson_result::get(T &out) && noexcept { - if (error()) { return error(); } - return std::forward(first).get(out); -} -template<> simdjson_inline simdjson_result simdjson_result::get() & noexcept = delete; -template<> simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { - if (error()) { return error(); } - return std::forward(first); -} -template<> simdjson_inline error_code simdjson_result::get(arm64::ondemand::document &out) & noexcept = delete; -template<> simdjson_inline error_code simdjson_result::get(arm64::ondemand::document &out) && noexcept { +template<> simdjson_inline simdjson_result simdjson_result::get() noexcept { if (error()) { return error(); } - out = std::forward(first); - return SUCCESS; + return std::move(first); } -simdjson_inline simdjson_result simdjson_result::type() noexcept { +simdjson_inline simdjson_result simdjson_result::type() noexcept { if (error()) { return error(); } return first.type(); } - -simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { if (error()) { return error(); } return first.is_scalar(); } - -simdjson_inline simdjson_result simdjson_result::is_string() noexcept { +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { if (error()) { return error(); } return first.is_string(); } - -simdjson_inline bool simdjson_result::is_negative() noexcept { +simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { if (error()) { return error(); } return first.is_negative(); } - -simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { if (error()) { return error(); } return first.is_integer(); } - -simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { if (error()) { return error(); } return first.get_number_type(); } - -simdjson_inline simdjson_result simdjson_result::get_number() noexcept { +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { if (error()) { return error(); } return first.get_number(); } - - #if SIMDJSON_EXCEPTIONS -template ::value == false>::type> -simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator arm64::ondemand::array() & noexcept(false) { +template +simdjson_inline simdjson_result::operator T() noexcept(false) { if (error()) { throw simdjson_error(error()); } - return first; + return first.get(); } -simdjson_inline simdjson_result::operator arm64::ondemand::object() & noexcept(false) { +simdjson_inline simdjson_result::operator arm64::ondemand::array() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { +simdjson_inline simdjson_result::operator arm64::ondemand::object() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator int64_t() noexcept(false) { +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator double() noexcept(false) { +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { +simdjson_inline simdjson_result::operator double() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator arm64::ondemand::raw_json_string() noexcept(false) { +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator bool() noexcept(false) { +simdjson_inline simdjson_result::operator arm64::ondemand::raw_json_string() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator arm64::ondemand::value() noexcept(false) { +simdjson_inline simdjson_result::operator bool() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } #endif - -simdjson_inline simdjson_result simdjson_result::current_location() noexcept { +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { if (error()) { return error(); } - return first.current_location(); + return first.raw_json_token(); } -simdjson_inline bool simdjson_result::at_end() const noexcept { +simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { if (error()) { return error(); } - return first.at_end(); + return first.raw_json(); } - -simdjson_inline int32_t simdjson_result::current_depth() const noexcept { +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { if (error()) { return error(); } - return first.current_depth(); + return first.current_location(); } -simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { +simdjson_inline simdjson_result simdjson_result::current_depth() const noexcept { if (error()) { return error(); } - return first.raw_json_token(); + return first.current_depth(); } -simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } +simdjson_inline simdjson_result simdjson_result::at_pointer( + std::string_view json_pointer) noexcept { + if (error()) { + return error(); + } return first.at_pointer(json_pointer); } -simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { return error(); } +simdjson_inline simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } return first.at_path(json_path); } } // namespace simdjson +#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H +/* end file simdjson/generic/ondemand/value-inl.h for arm64 */ +/* including simdjson/generic/ondemand/document-inl.h for arm64: #include "simdjson/generic/ondemand/document-inl.h" */ +/* begin file simdjson/generic/ondemand/document-inl.h for arm64 */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { namespace arm64 { namespace ondemand { -simdjson_inline document_reference::document_reference() noexcept : doc{nullptr} {} -simdjson_inline document_reference::document_reference(document &d) noexcept : doc(&d) {} -simdjson_inline void document_reference::rewind() noexcept { doc->rewind(); } -simdjson_inline simdjson_result document_reference::get_array() & noexcept { return doc->get_array(); } -simdjson_inline simdjson_result document_reference::get_object() & noexcept { return doc->get_object(); } -/** - * The document_reference instances are used primarily/solely for streams of JSON - * documents. - * We decided that calling 'get_double()' on the JSON document '1.233 blabla' should - * give an error, so we check for trailing content. - * - * However, for streams of JSON documents, we want to be able to start from - * "321" "321" "321" - * and parse it successfully as a stream of JSON documents, calling get_uint64_in_string() - * successfully each time. - * - * To achieve this result, we pass a 'false' to a get_root_value_iterator() method: - * this indicates that we allow trailing content. - */ -simdjson_inline simdjson_result document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); } -simdjson_inline simdjson_result document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); } -simdjson_inline simdjson_result document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); } -simdjson_inline simdjson_result document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); } -simdjson_inline simdjson_result document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); } -simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } -simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } -template -simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); } -simdjson_inline simdjson_result document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } -simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } -simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } -simdjson_inline simdjson_result document_reference::get_value() noexcept { return doc->get_value(); } -simdjson_inline simdjson_result document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_array(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_object(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_raw_json_string(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_string(false); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_double(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_uint64(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_int64(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_bool(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_value(); } -#if SIMDJSON_EXCEPTIONS -template -simdjson_inline document_reference::operator T() noexcept(false) { return get(); } -simdjson_inline document_reference::operator array() & noexcept(false) { return array(*doc); } -simdjson_inline document_reference::operator object() & noexcept(false) { return object(*doc); } -simdjson_inline document_reference::operator uint64_t() noexcept(false) { return get_uint64(); } -simdjson_inline document_reference::operator int64_t() noexcept(false) { return get_int64(); } -simdjson_inline document_reference::operator double() noexcept(false) { return get_double(); } -simdjson_inline document_reference::operator std::string_view() noexcept(false) { return std::string_view(*doc); } -simdjson_inline document_reference::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } -simdjson_inline document_reference::operator bool() noexcept(false) { return get_bool(); } -simdjson_inline document_reference::operator value() noexcept(false) { return value(*doc); } -#endif -simdjson_inline simdjson_result document_reference::count_elements() & noexcept { return doc->count_elements(); } -simdjson_inline simdjson_result document_reference::count_fields() & noexcept { return doc->count_fields(); } -simdjson_inline simdjson_result document_reference::at(size_t index) & noexcept { return doc->at(index); } -simdjson_inline simdjson_result document_reference::begin() & noexcept { return doc->begin(); } -simdjson_inline simdjson_result document_reference::end() & noexcept { return doc->end(); } -simdjson_inline simdjson_result document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); } -simdjson_inline simdjson_result document_reference::find_field(const char *key) & noexcept { return doc->find_field(key); } -simdjson_inline simdjson_result document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; } -simdjson_inline simdjson_result document_reference::operator[](const char *key) & noexcept { return (*doc)[key]; } -simdjson_inline simdjson_result document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); } -simdjson_inline simdjson_result document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); } -simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } -simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } -simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } -simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } -simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } -simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } -simdjson_inline simdjson_result document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); } -simdjson_inline simdjson_result document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); } -simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } -simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } -simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } -simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } -simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} -simdjson_inline document_reference::operator document&() const noexcept { return *doc; } - -} // namespace ondemand -} // namespace arm64 -} // namespace simdjson +simdjson_inline document::document(ondemand::json_iterator &&_iter) noexcept + : iter{std::forward(_iter)} +{ + logger::log_start_value(iter, "document"); +} +simdjson_inline document document::start(json_iterator &&iter) noexcept { + return document(std::forward(iter)); +} +inline void document::rewind() noexcept { + iter.rewind(); +} -namespace simdjson { -simdjson_inline simdjson_result::simdjson_result(arm64::ondemand::document_reference value, error_code error) - noexcept : implementation_simdjson_result_base(std::forward(value), error) {} +inline std::string document::to_debug_string() noexcept { + return iter.to_string(); +} +inline simdjson_result document::current_location() const noexcept { + return iter.current_location(); +} -simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); +inline int32_t document::current_depth() const noexcept { + return iter.depth(); } -simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); + +inline bool document::at_end() const noexcept { + return iter.at_end(); } -simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { - if (error()) { return error(); } - return first.at(index); + + +inline bool document::is_alive() noexcept { + return iter.is_alive(); } -simdjson_inline error_code simdjson_result::rewind() noexcept { - if (error()) { return error(); } - first.rewind(); - return SUCCESS; +simdjson_inline value_iterator document::resume_value_iterator() noexcept { + return value_iterator(&iter, 1, iter.root_position()); } -simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); +simdjson_inline value_iterator document::get_root_value_iterator() noexcept { + return resume_value_iterator(); } -simdjson_inline simdjson_result simdjson_result::end() & noexcept { - return {}; +simdjson_inline simdjson_result document::start_or_resume_object() noexcept { + if (iter.at_root()) { + return get_object(); + } else { + return object::resume(resume_value_iterator()); + } } -simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); +simdjson_inline simdjson_result document::get_value() noexcept { + // Make sure we start any arrays or objects before returning, so that start_root_() + // gets called. + + // It is the convention throughout the code that the macro `SIMDJSON_DEVELOPMENT_CHECKS` determines whether + // we check for OUT_OF_ORDER_ITERATION. Proper on::demand code should never trigger this error. +#if SIMDJSON_DEVELOPMENT_CHECKS + if (!iter.at_root()) { return OUT_OF_ORDER_ITERATION; } +#endif + // assert_at_root() serves two purposes: in Debug mode, whether or not + // SIMDJSON_DEVELOPMENT_CHECKS is set or not, it checks that we are at the root of + // the document (this will typically be redundant). In release mode, it generates + // SIMDJSON_ASSUME statements to allow the compiler to make assumptions. + iter.assert_at_root(); + switch (*iter.peek()) { + case '[': { + // The following lines check that the document ends with ]. + auto value_iterator = get_root_value_iterator(); + auto error = value_iterator.check_root_array(); + if(error) { return error; } + return value(get_root_value_iterator()); + } + case '{': { + // The following lines would check that the document ends with }. + auto value_iterator = get_root_value_iterator(); + auto error = value_iterator.check_root_object(); + if(error) { return error; } + return value(get_root_value_iterator()); + } + default: + // Unfortunately, scalar documents are a special case in simdjson and they cannot + // be safely converted to value instances. + return SCALAR_DOCUMENT_AS_VALUE; + } } -simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); +simdjson_inline simdjson_result document::get_array() & noexcept { + auto value = get_root_value_iterator(); + return array::start_root(value); } -simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { - if (error()) { return error(); } - return first[key]; +simdjson_inline simdjson_result document::get_object() & noexcept { + auto value = get_root_value_iterator(); + return object::start_root(value); } -simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { - if (error()) { return error(); } - return first[key]; + +/** + * We decided that calling 'get_double()' on the JSON document '1.233 blabla' should + * give an error, so we check for trailing content. We want to disallow trailing + * content. + * Thus, in several implementations below, we pass a 'true' parameter value to + * a get_root_value_iterator() method: this indicates that we disallow trailing content. + */ + +simdjson_inline simdjson_result document::get_uint64() noexcept { + return get_root_value_iterator().get_root_uint64(true); } -simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); +simdjson_inline simdjson_result document::get_uint64_in_string() noexcept { + return get_root_value_iterator().get_root_uint64_in_string(true); } -simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { - if (error()) { return error(); } - return first.find_field(key); +simdjson_inline simdjson_result document::get_int64() noexcept { + return get_root_value_iterator().get_root_int64(true); } -simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { - if (error()) { return error(); } - return first.get_array(); +simdjson_inline simdjson_result document::get_int64_in_string() noexcept { + return get_root_value_iterator().get_root_int64_in_string(true); } -simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { +simdjson_inline simdjson_result document::get_double() noexcept { + return get_root_value_iterator().get_root_double(true); +} +simdjson_inline simdjson_result document::get_double_in_string() noexcept { + return get_root_value_iterator().get_root_double_in_string(true); +} +simdjson_inline simdjson_result document::get_string(bool allow_replacement) noexcept { + return get_root_value_iterator().get_root_string(true, allow_replacement); +} +template +simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept { + return get_root_value_iterator().get_root_string(receiver, true, allow_replacement); +} +simdjson_inline simdjson_result document::get_wobbly_string() noexcept { + return get_root_value_iterator().get_root_wobbly_string(true); +} +simdjson_inline simdjson_result document::get_raw_json_string() noexcept { + return get_root_value_iterator().get_root_raw_json_string(true); +} +simdjson_inline simdjson_result document::get_bool() noexcept { + return get_root_value_iterator().get_root_bool(true); +} +simdjson_inline simdjson_result document::is_null() noexcept { + return get_root_value_iterator().is_root_null(true); +} + +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_array(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_object(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_double(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_value(); } + +template<> simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); } +template<> simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); } +template<> simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); } +template<> simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); } +template<> simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); } +template<> simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); } +template<> simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); } +template<> simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); } +template<> simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); } + +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_raw_json_string(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_value(); } + +#if SIMDJSON_EXCEPTIONS +template +simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get(); } +template +simdjson_inline document::operator T() & noexcept(false) { return get(); } +simdjson_inline document::operator array() & noexcept(false) { return get_array(); } +simdjson_inline document::operator object() & noexcept(false) { return get_object(); } +simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); } +simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); } +simdjson_inline document::operator double() noexcept(false) { return get_double(); } +simdjson_inline document::operator std::string_view() noexcept(false) { return get_string(false); } +simdjson_inline document::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } +simdjson_inline document::operator bool() noexcept(false) { return get_bool(); } +simdjson_inline document::operator value() noexcept(false) { return get_value(); } + +#endif +simdjson_inline simdjson_result document::count_elements() & noexcept { + auto a = get_array(); + simdjson_result answer = a.count_elements(); + /* If there was an array, we are now left pointing at its first element. */ + if(answer.error() == SUCCESS) { rewind(); } + return answer; +} +simdjson_inline simdjson_result document::count_fields() & noexcept { + auto a = get_object(); + simdjson_result answer = a.count_fields(); + /* If there was an object, we are now left pointing at its first element. */ + if(answer.error() == SUCCESS) { rewind(); } + return answer; +} +simdjson_inline simdjson_result document::at(size_t index) & noexcept { + auto a = get_array(); + return a.at(index); +} +simdjson_inline simdjson_result document::begin() & noexcept { + return get_array().begin(); +} +simdjson_inline simdjson_result document::end() & noexcept { + return {}; +} + +simdjson_inline simdjson_result document::find_field(std::string_view key) & noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result document::find_field(const char *key) & noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result document::find_field_unordered(std::string_view key) & noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result document::find_field_unordered(const char *key) & noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result document::operator[](std::string_view key) & noexcept { + return start_or_resume_object()[key]; +} +simdjson_inline simdjson_result document::operator[](const char *key) & noexcept { + return start_or_resume_object()[key]; +} + +simdjson_inline error_code document::consume() noexcept { + bool scalar = false; + auto error = is_scalar().get(scalar); + if(error) { return error; } + if(scalar) { + iter.return_current_and_advance(); + return SUCCESS; + } + error = iter.skip_child(0); + if(error) { iter.abandon(); } + return error; +} + +simdjson_inline simdjson_result document::raw_json() noexcept { + auto _iter = get_root_value_iterator(); + const uint8_t * starting_point{_iter.peek_start()}; + auto error = consume(); + if(error) { return error; } + // After 'consume()', we could be left pointing just beyond the document, but that + // is ok because we are not going to dereference the final pointer position, we just + // use it to compute the length in bytes. + const uint8_t * final_point{iter.unsafe_pointer()}; + return std::string_view(reinterpret_cast(starting_point), size_t(final_point - starting_point)); +} + +simdjson_inline simdjson_result document::type() noexcept { + return get_root_value_iterator().type(); +} + +simdjson_inline simdjson_result document::is_scalar() noexcept { + // For more speed, we could do: + // return iter.is_single_token(); + json_type this_type; + auto error = type().get(this_type); + if(error) { return error; } + return ! ((this_type == json_type::array) || (this_type == json_type::object)); +} + +simdjson_inline simdjson_result document::is_string() noexcept { + json_type this_type; + auto error = type().get(this_type); + if(error) { return error; } + return (this_type == json_type::string); +} + +simdjson_inline bool document::is_negative() noexcept { + return get_root_value_iterator().is_root_negative(); +} + +simdjson_inline simdjson_result document::is_integer() noexcept { + return get_root_value_iterator().is_root_integer(true); +} + +simdjson_inline simdjson_result document::get_number_type() noexcept { + return get_root_value_iterator().get_root_number_type(true); +} + +simdjson_inline simdjson_result document::get_number() noexcept { + return get_root_value_iterator().get_root_number(true); +} + + +simdjson_inline simdjson_result document::raw_json_token() noexcept { + auto _iter = get_root_value_iterator(); + return std::string_view(reinterpret_cast(_iter.peek_start()), _iter.peek_root_length()); +} + +simdjson_inline simdjson_result document::at_pointer(std::string_view json_pointer) noexcept { + rewind(); // Rewind the document each time at_pointer is called + if (json_pointer.empty()) { + return this->get_value(); + } + json_type t; + SIMDJSON_TRY(type().get(t)); + switch (t) + { + case json_type::array: + return (*this).get_array().at_pointer(json_pointer); + case json_type::object: + return (*this).get_object().at_pointer(json_pointer); + default: + return INVALID_JSON_POINTER; + } +} + +simdjson_inline simdjson_result document::at_path(std::string_view json_path) noexcept { + rewind(); // Rewind the document each time at_pointer is called + if (json_path.empty()) { + return this->get_value(); + } + json_type t; + SIMDJSON_TRY(type().get(t)); + switch (t) { + case json_type::array: + return (*this).get_array().at_path(json_path); + case json_type::object: + return (*this).get_object().at_path(json_path); + default: + return INVALID_JSON_POINTER; + } +} + +} // namespace ondemand +} // namespace arm64 +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result( + arm64::ondemand::document &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base( + error + ) +{ +} +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline error_code simdjson_result::rewind() noexcept { + if (error()) { return error(); } + first.rewind(); + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { + if (error()) { return error(); } + return first.get_array(); +} +simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { if (error()) { return error(); } return first.get_object(); } -simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { if (error()) { return error(); } return first.get_uint64(); } -simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { if (error()) { return error(); } return first.get_uint64_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { if (error()) { return error(); } return first.get_int64(); } -simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { if (error()) { return error(); } return first.get_int64_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_double() noexcept { +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { if (error()) { return error(); } return first.get_double(); } -simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { if (error()) { return error(); } return first.get_double_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(allow_replacement); } template -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(receiver, allow_replacement); } -simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { if (error()) { return error(); } return first.get_wobbly_string(); } -simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { if (error()) { return error(); } return first.get_raw_json_string(); } -simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { if (error()) { return error(); } return first.get_bool(); } -simdjson_inline simdjson_result simdjson_result::get_value() noexcept { +simdjson_inline simdjson_result simdjson_result::get_value() noexcept { if (error()) { return error(); } return first.get_value(); } -simdjson_inline simdjson_result simdjson_result::is_null() noexcept { +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { if (error()) { return error(); } return first.is_null(); } -simdjson_inline simdjson_result simdjson_result::type() noexcept { + +template +simdjson_inline simdjson_result simdjson_result::get() & noexcept { + if (error()) { return error(); } + return first.get(); +} +template +simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first).get(); +} +template +simdjson_inline error_code simdjson_result::get(T &out) & noexcept { + if (error()) { return error(); } + return first.get(out); +} +template +simdjson_inline error_code simdjson_result::get(T &out) && noexcept { + if (error()) { return error(); } + return std::forward(first).get(out); +} + +template<> simdjson_inline simdjson_result simdjson_result::get() & noexcept = delete; +template<> simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first); +} +template<> simdjson_inline error_code simdjson_result::get(arm64::ondemand::document &out) & noexcept = delete; +template<> simdjson_inline error_code simdjson_result::get(arm64::ondemand::document &out) && noexcept { + if (error()) { return error(); } + out = std::forward(first); + return SUCCESS; +} + +simdjson_inline simdjson_result simdjson_result::type() noexcept { if (error()) { return error(); } return first.type(); } -simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { if (error()) { return error(); } return first.is_scalar(); } -simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { if (error()) { return error(); } return first.is_string(); } -simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { + +simdjson_inline bool simdjson_result::is_negative() noexcept { if (error()) { return error(); } return first.is_negative(); } -simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { if (error()) { return error(); } return first.is_integer(); } -simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { if (error()) { return error(); } return first.get_number_type(); } -simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { if (error()) { return error(); } return first.get_number(); } + + #if SIMDJSON_EXCEPTIONS -template ::value == false>::type> -simdjson_inline simdjson_result::operator T() noexcept(false) { +template ::value == false>::type> +simdjson_inline simdjson_result::operator T() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator arm64::ondemand::array() & noexcept(false) { +simdjson_inline simdjson_result::operator arm64::ondemand::array() & noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator arm64::ondemand::object() & noexcept(false) { +simdjson_inline simdjson_result::operator arm64::ondemand::object() & noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator int64_t() noexcept(false) { +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator double() noexcept(false) { +simdjson_inline simdjson_result::operator double() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator arm64::ondemand::raw_json_string() noexcept(false) { +simdjson_inline simdjson_result::operator arm64::ondemand::raw_json_string() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator bool() noexcept(false) { +simdjson_inline simdjson_result::operator bool() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator arm64::ondemand::value() noexcept(false) { +simdjson_inline simdjson_result::operator arm64::ondemand::value() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } #endif -simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { if (error()) { return error(); } return first.current_location(); } -simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { +simdjson_inline bool simdjson_result::at_end() const noexcept { + if (error()) { return error(); } + return first.at_end(); +} + + +simdjson_inline int32_t simdjson_result::current_depth() const noexcept { + if (error()) { return error(); } + return first.current_depth(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { if (error()) { return error(); } return first.raw_json_token(); } -simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { if (error()) { return error(); } return first.at_pointer(json_pointer); } -simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { - return error(); - } +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } return first.at_path(json_path); } } // namespace simdjson -#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H -/* end file simdjson/generic/ondemand/document-inl.h for arm64 */ -/* including simdjson/generic/ondemand/document_stream-inl.h for arm64: #include "simdjson/generic/ondemand/document_stream-inl.h" */ -/* begin file simdjson/generic/ondemand/document_stream-inl.h for arm64 */ -#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H - -/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ -/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document_stream.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */ -/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -#include -#include namespace simdjson { namespace arm64 { namespace ondemand { -#ifdef SIMDJSON_THREADS_ENABLED +simdjson_inline document_reference::document_reference() noexcept : doc{nullptr} {} +simdjson_inline document_reference::document_reference(document &d) noexcept : doc(&d) {} +simdjson_inline void document_reference::rewind() noexcept { doc->rewind(); } +simdjson_inline simdjson_result document_reference::get_array() & noexcept { return doc->get_array(); } +simdjson_inline simdjson_result document_reference::get_object() & noexcept { return doc->get_object(); } +/** + * The document_reference instances are used primarily/solely for streams of JSON + * documents. + * We decided that calling 'get_double()' on the JSON document '1.233 blabla' should + * give an error, so we check for trailing content. + * + * However, for streams of JSON documents, we want to be able to start from + * "321" "321" "321" + * and parse it successfully as a stream of JSON documents, calling get_uint64_in_string() + * successfully each time. + * + * To achieve this result, we pass a 'false' to a get_root_value_iterator() method: + * this indicates that we allow trailing content. + */ +simdjson_inline simdjson_result document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); } +simdjson_inline simdjson_result document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); } +simdjson_inline simdjson_result document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); } +simdjson_inline simdjson_result document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); } +simdjson_inline simdjson_result document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); } +simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } +simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } +template +simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); } +simdjson_inline simdjson_result document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } +simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } +simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } +simdjson_inline simdjson_result document_reference::get_value() noexcept { return doc->get_value(); } +simdjson_inline simdjson_result document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_array(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_object(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_double(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_bool(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_value(); } +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline document_reference::operator T() noexcept(false) { return get(); } +simdjson_inline document_reference::operator array() & noexcept(false) { return array(*doc); } +simdjson_inline document_reference::operator object() & noexcept(false) { return object(*doc); } +simdjson_inline document_reference::operator uint64_t() noexcept(false) { return get_uint64(); } +simdjson_inline document_reference::operator int64_t() noexcept(false) { return get_int64(); } +simdjson_inline document_reference::operator double() noexcept(false) { return get_double(); } +simdjson_inline document_reference::operator std::string_view() noexcept(false) { return std::string_view(*doc); } +simdjson_inline document_reference::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } +simdjson_inline document_reference::operator bool() noexcept(false) { return get_bool(); } +simdjson_inline document_reference::operator value() noexcept(false) { return value(*doc); } +#endif +simdjson_inline simdjson_result document_reference::count_elements() & noexcept { return doc->count_elements(); } +simdjson_inline simdjson_result document_reference::count_fields() & noexcept { return doc->count_fields(); } +simdjson_inline simdjson_result document_reference::at(size_t index) & noexcept { return doc->at(index); } +simdjson_inline simdjson_result document_reference::begin() & noexcept { return doc->begin(); } +simdjson_inline simdjson_result document_reference::end() & noexcept { return doc->end(); } +simdjson_inline simdjson_result document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); } +simdjson_inline simdjson_result document_reference::find_field(const char *key) & noexcept { return doc->find_field(key); } +simdjson_inline simdjson_result document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; } +simdjson_inline simdjson_result document_reference::operator[](const char *key) & noexcept { return (*doc)[key]; } +simdjson_inline simdjson_result document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); } +simdjson_inline simdjson_result document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); } +simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } +simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } +simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } +simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } +simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } +simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } +simdjson_inline simdjson_result document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); } +simdjson_inline simdjson_result document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); } +simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } +simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } +simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } +simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } +simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} +simdjson_inline document_reference::operator document&() const noexcept { return *doc; } -inline void stage1_worker::finish() { - // After calling "run" someone would call finish() to wait - // for the end of the processing. - // This function will wait until either the thread has done - // the processing or, else, the destructor has been called. - std::unique_lock lock(locking_mutex); - cond_var.wait(lock, [this]{return has_work == false;}); -} +} // namespace ondemand +} // namespace arm64 +} // namespace simdjson -inline stage1_worker::~stage1_worker() { + + +namespace simdjson { +simdjson_inline simdjson_result::simdjson_result(arm64::ondemand::document_reference value, error_code error) + noexcept : implementation_simdjson_result_base(std::forward(value), error) {} + + +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline error_code simdjson_result::rewind() noexcept { + if (error()) { return error(); } + first.rewind(); + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { + if (error()) { return error(); } + return first.get_array(); +} +simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { + if (error()) { return error(); } + return first.get_object(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { + if (error()) { return error(); } + return first.get_uint64(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { + if (error()) { return error(); } + return first.get_uint64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { + if (error()) { return error(); } + return first.get_int64(); +} +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { + if (error()) { return error(); } + return first.get_int64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { + if (error()) { return error(); } + return first.get_double(); +} +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { + if (error()) { return error(); } + return first.get_double_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(allow_replacement); +} +template +simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(receiver, allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { + if (error()) { return error(); } + return first.get_wobbly_string(); +} +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { + if (error()) { return error(); } + return first.get_raw_json_string(); +} +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { + if (error()) { return error(); } + return first.get_bool(); +} +simdjson_inline simdjson_result simdjson_result::get_value() noexcept { + if (error()) { return error(); } + return first.get_value(); +} +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { + if (error()) { return error(); } + return first.is_null(); +} +template +simdjson_inline simdjson_result simdjson_result::get() & noexcept { + if (error()) { return error(); } + return first.get(); +} +template +simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first).get(); +} +template +simdjson_inline error_code simdjson_result::get(T &out) & noexcept { + if (error()) { return error(); } + return first.get(out); +} +template +simdjson_inline error_code simdjson_result::get(T &out) && noexcept { + if (error()) { return error(); } + return std::forward(first).get(out); +} +simdjson_inline simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); +} +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + if (error()) { return error(); } + return first.is_scalar(); +} +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} +template <> +simdjson_inline error_code simdjson_result::get(arm64::ondemand::document_reference &out) & noexcept { + if (error()) { return error(); } + out = first; + return SUCCESS; +} +template <> +simdjson_inline error_code simdjson_result::get(arm64::ondemand::document_reference &out) && noexcept { + if (error()) { return error(); } + out = first; + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { + if (error()) { return error(); } + return first.is_negative(); +} +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + if (error()) { return error(); } + return first.is_integer(); +} +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + if (error()) { return error(); } + return first.get_number_type(); +} +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + if (error()) { return error(); } + return first.get_number(); +} +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline simdjson_result::operator T() noexcept(false) { + static_assert(std::is_same::value == false, "You should not call get when T is a document"); + static_assert(std::is_same::value == false, "You should not call get when T is a document"); + if (error()) { throw simdjson_error(error()); } + return first.get(); +} +simdjson_inline simdjson_result::operator arm64::ondemand::array() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator arm64::ondemand::object() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator double() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator arm64::ondemand::raw_json_string() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator bool() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator arm64::ondemand::value() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +#endif + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} + +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H +/* end file simdjson/generic/ondemand/document-inl.h for arm64 */ +/* including simdjson/generic/ondemand/document_stream-inl.h for arm64: #include "simdjson/generic/ondemand/document_stream-inl.h" */ +/* begin file simdjson/generic/ondemand/document_stream-inl.h for arm64 */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document_stream.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +#include + +namespace simdjson { +namespace arm64 { +namespace ondemand { + +#ifdef SIMDJSON_THREADS_ENABLED + +inline void stage1_worker::finish() { + // After calling "run" someone would call finish() to wait + // for the end of the processing. + // This function will wait until either the thread has done + // the processing or, else, the destructor has been called. + std::unique_lock lock(locking_mutex); + cond_var.wait(lock, [this]{return has_work == false;}); +} + +inline stage1_worker::~stage1_worker() { // The thread may never outlive the stage1_worker instance // and will always be stopped/joined before the stage1_worker // instance is gone. @@ -37637,7 +39107,6 @@ simdjson_inline document_stream::iterator::iterator(document_stream* _stream, bo } simdjson_inline simdjson_result document_stream::iterator::operator*() noexcept { - //if(stream->error) { return stream->error; } return simdjson_result(stream->doc, stream->error); } @@ -39496,36 +40965,39 @@ simdjson_inline const char * raw_json_string::raw() const noexcept { return rein simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(std::string_view target) noexcept { size_t pos{0}; - // if the content has no escape character, just scan through it quickly! - for(;pos < target.size() && target[pos] != '\\';pos++) {} - // slow path may begin. - bool escaping{false}; - for(;pos < target.size();pos++) { - if((target[pos] == '"') && !escaping) { - return false; - } else if(target[pos] == '\\') { - escaping = !escaping; - } else { - escaping = false; + while(pos < target.size()) { + pos = target.find('"', pos); + if(pos == std::string_view::npos) { return true; } + if(pos != 0 && target[pos-1] != '\\') { return false; } + if(pos > 1 && target[pos-2] == '\\') { + size_t backslash_count{2}; + for(size_t i = 3; i <= pos; i++) { + if(target[pos-i] == '\\') { backslash_count++; } + else { break; } + } + if(backslash_count % 2 == 0) { return false; } } + pos++; } return true; } simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(const char* target) noexcept { size_t pos{0}; - // if the content has no escape character, just scan through it quickly! - for(;target[pos] && target[pos] != '\\';pos++) {} - // slow path may begin. - bool escaping{false}; - for(;target[pos];pos++) { - if((target[pos] == '"') && !escaping) { - return false; - } else if(target[pos] == '\\') { - escaping = !escaping; - } else { - escaping = false; + while(target[pos]) { + const char * result = strchr(target+pos, '"'); + if(result == nullptr) { return true; } + pos = result - target; + if(pos != 0 && target[pos-1] != '\\') { return false; } + if(pos > 1 && target[pos-2] == '\\') { + size_t backslash_count{2}; + for(size_t i = 3; i <= pos; i++) { + if(target[pos-i] == '\\') { backslash_count++; } + else { break; } + } + if(backslash_count % 2 == 0) { return false; } } + pos++; } return true; } @@ -39537,7 +41009,7 @@ simdjson_inline bool raw_json_string::unsafe_is_equal(size_t length, std::string } simdjson_inline bool raw_json_string::unsafe_is_equal(std::string_view target) const noexcept { - // Assumptions: does not contain unescaped quote characters, and + // Assumptions: does not contain unescaped quote characters("), and // the raw content is quote terminated within a valid JSON string. if(target.size() <= SIMDJSON_PADDING) { return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size()); @@ -40011,775 +41483,230 @@ simdjson_inline simdjson_result::simdjson_resul #endif // SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H /* end file simdjson/generic/ondemand/token_iterator-inl.h for arm64 */ -/* including simdjson/generic/ondemand/value-inl.h for arm64: #include "simdjson/generic/ondemand/value-inl.h" */ -/* begin file simdjson/generic/ondemand/value-inl.h for arm64 */ -#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H +/* including simdjson/generic/ondemand/value_iterator-inl.h for arm64: #include "simdjson/generic/ondemand/value_iterator-inl.h" */ +/* begin file simdjson/generic/ondemand/value_iterator-inl.h for arm64 */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ -/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/atomparsing.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/numberparsing.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string-inl.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { namespace arm64 { namespace ondemand { -simdjson_inline value::value(const value_iterator &_iter) noexcept - : iter{_iter} +simdjson_inline value_iterator::value_iterator( + json_iterator *json_iter, + depth_t depth, + token_position start_position +) noexcept : _json_iter{json_iter}, _depth{depth}, _start_position{start_position} { } -simdjson_inline value value::start(const value_iterator &iter) noexcept { - return iter; -} -simdjson_inline value value::resume(const value_iterator &iter) noexcept { - return iter; -} - -simdjson_inline simdjson_result value::get_array() noexcept { - return array::start(iter); -} -simdjson_inline simdjson_result value::get_object() noexcept { - return object::start(iter); -} -simdjson_inline simdjson_result value::start_or_resume_object() noexcept { - if (iter.at_start()) { - return get_object(); - } else { - return object::resume(iter); - } -} -simdjson_inline simdjson_result value::get_raw_json_string() noexcept { - return iter.get_raw_json_string(); -} -simdjson_inline simdjson_result value::get_string(bool allow_replacement) noexcept { - return iter.get_string(allow_replacement); -} -template -simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept { - return iter.get_string(receiver, allow_replacement); -} -simdjson_inline simdjson_result value::get_wobbly_string() noexcept { - return iter.get_wobbly_string(); -} -simdjson_inline simdjson_result value::get_double() noexcept { - return iter.get_double(); -} -simdjson_inline simdjson_result value::get_double_in_string() noexcept { - return iter.get_double_in_string(); -} -simdjson_inline simdjson_result value::get_uint64() noexcept { - return iter.get_uint64(); -} -simdjson_inline simdjson_result value::get_uint64_in_string() noexcept { - return iter.get_uint64_in_string(); -} -simdjson_inline simdjson_result value::get_int64() noexcept { - return iter.get_int64(); -} -simdjson_inline simdjson_result value::get_int64_in_string() noexcept { - return iter.get_int64_in_string(); -} -simdjson_inline simdjson_result value::get_bool() noexcept { - return iter.get_bool(); -} -simdjson_inline simdjson_result value::is_null() noexcept { - return iter.is_null(); +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_object() noexcept { + SIMDJSON_TRY( start_container('{', "Not an object", "object") ); + return started_object(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_array(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_object(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_raw_json_string(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_string(false); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_number(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_double(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_uint64(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_int64(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_bool(); } -template simdjson_inline error_code value::get(T &out) noexcept { - return get().get(out); +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_root_object() noexcept { + SIMDJSON_TRY( start_container('{', "Not an object", "object") ); + return started_root_object(); } -#if SIMDJSON_EXCEPTIONS -template -simdjson_inline value::operator T() noexcept(false) { - return get(); -} -simdjson_inline value::operator array() noexcept(false) { - return get_array(); -} -simdjson_inline value::operator object() noexcept(false) { - return get_object(); -} -simdjson_inline value::operator uint64_t() noexcept(false) { - return get_uint64(); -} -simdjson_inline value::operator int64_t() noexcept(false) { - return get_int64(); -} -simdjson_inline value::operator double() noexcept(false) { - return get_double(); -} -simdjson_inline value::operator std::string_view() noexcept(false) { - return get_string(false); -} -simdjson_inline value::operator raw_json_string() noexcept(false) { - return get_raw_json_string(); -} -simdjson_inline value::operator bool() noexcept(false) { - return get_bool(); -} +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::started_object() noexcept { + assert_at_container_start(); +#if SIMDJSON_DEVELOPMENT_CHECKS + _json_iter->set_start_position(_depth, start_position()); #endif - -simdjson_inline simdjson_result value::begin() & noexcept { - return get_array().begin(); -} -simdjson_inline simdjson_result value::end() & noexcept { - return {}; -} -simdjson_inline simdjson_result value::count_elements() & noexcept { - simdjson_result answer; - auto a = get_array(); - answer = a.count_elements(); - // count_elements leaves you pointing inside the array, at the first element. - // We need to move back so that the user can create a new array (which requires that - // we point at '['). - iter.move_at_start(); - return answer; -} -simdjson_inline simdjson_result value::count_fields() & noexcept { - simdjson_result answer; - auto a = get_object(); - answer = a.count_fields(); - iter.move_at_start(); - return answer; -} -simdjson_inline simdjson_result value::at(size_t index) noexcept { - auto a = get_array(); - return a.at(index); -} - -simdjson_inline simdjson_result value::find_field(std::string_view key) noexcept { - return start_or_resume_object().find_field(key); -} -simdjson_inline simdjson_result value::find_field(const char *key) noexcept { - return start_or_resume_object().find_field(key); -} - -simdjson_inline simdjson_result value::find_field_unordered(std::string_view key) noexcept { - return start_or_resume_object().find_field_unordered(key); -} -simdjson_inline simdjson_result value::find_field_unordered(const char *key) noexcept { - return start_or_resume_object().find_field_unordered(key); -} - -simdjson_inline simdjson_result value::operator[](std::string_view key) noexcept { - return start_or_resume_object()[key]; -} -simdjson_inline simdjson_result value::operator[](const char *key) noexcept { - return start_or_resume_object()[key]; -} - -simdjson_inline simdjson_result value::type() noexcept { - return iter.type(); + if (*_json_iter->peek() == '}') { + logger::log_value(*_json_iter, "empty object"); + _json_iter->return_current_and_advance(); + end_container(); + return false; + } + return true; } -simdjson_inline simdjson_result value::is_scalar() noexcept { - json_type this_type; - auto error = type().get(this_type); - if(error) { return error; } - return ! ((this_type == json_type::array) || (this_type == json_type::object)); +simdjson_warn_unused simdjson_inline error_code value_iterator::check_root_object() noexcept { + // When in streaming mode, we cannot expect peek_last() to be the last structural element of the + // current document. It only works in the normal mode where we have indexed a single document. + // Note that adding a check for 'streaming' is not expensive since we only have at most + // one root element. + if ( ! _json_iter->streaming() ) { + // The following lines do not fully protect against garbage content within the + // object: e.g., `{"a":2} foo }`. Users concerned with garbage content should + // call `at_end()` on the document instance at the end of the processing to + // ensure that the processing has finished at the end. + // + if (*_json_iter->peek_last() != '}') { + _json_iter->abandon(); + return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing } at end"); + } + // If the last character is } *and* the first gibberish character is also '}' + // then on-demand could accidentally go over. So we need additional checks. + // https://github.com/simdjson/simdjson/issues/1834 + // Checking that the document is balanced requires a full scan which is potentially + // expensive, but it only happens in edge cases where the first padding character is + // a closing bracket. + if ((*_json_iter->peek(_json_iter->end_position()) == '}') && (!_json_iter->balanced())) { + _json_iter->abandon(); + // The exact error would require more work. It will typically be an unclosed object. + return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "the document is unbalanced"); + } + } + return SUCCESS; } -simdjson_inline simdjson_result value::is_string() noexcept { - json_type this_type; - auto error = type().get(this_type); +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::started_root_object() noexcept { + auto error = check_root_object(); if(error) { return error; } - return (this_type == json_type::string); -} - - -simdjson_inline bool value::is_negative() noexcept { - return iter.is_negative(); + return started_object(); } -simdjson_inline simdjson_result value::is_integer() noexcept { - return iter.is_integer(); -} -simdjson_warn_unused simdjson_inline simdjson_result value::get_number_type() noexcept { - return iter.get_number_type(); -} -simdjson_warn_unused simdjson_inline simdjson_result value::get_number() noexcept { - return iter.get_number(); +simdjson_warn_unused simdjson_inline error_code value_iterator::end_container() noexcept { +#if SIMDJSON_CHECK_EOF + if (depth() > 1 && at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing parent ] or }"); } + // if (depth() <= 1 && !at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing [ or { at start"); } +#endif // SIMDJSON_CHECK_EOF + _json_iter->ascend_to(depth()-1); + return SUCCESS; } -simdjson_inline std::string_view value::raw_json_token() noexcept { - return std::string_view(reinterpret_cast(iter.peek_start()), iter.peek_start_length()); -} +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::has_next_field() noexcept { + assert_at_next(); -simdjson_inline simdjson_result value::raw_json() noexcept { - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) - { - case json_type::array: { - ondemand::array array; - SIMDJSON_TRY(get_array().get(array)); - return array.raw_json(); - } - case json_type::object: { - ondemand::object object; - SIMDJSON_TRY(get_object().get(object)); - return object.raw_json(); - } + // It's illegal to call this unless there are more tokens: anything that ends in } or ] is + // obligated to verify there are more tokens if they are not the top level. + switch (*_json_iter->return_current_and_advance()) { + case '}': + logger::log_end_value(*_json_iter, "object"); + SIMDJSON_TRY( end_container() ); + return false; + case ',': + return true; default: - return raw_json_token(); + return report_error(TAPE_ERROR, "Missing comma between object fields"); } } -simdjson_inline simdjson_result value::current_location() noexcept { - return iter.json_iter().current_location(); -} - -simdjson_inline int32_t value::current_depth() const noexcept{ - return iter.json_iter().depth(); -} +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::find_field_raw(const std::string_view key) noexcept { + error_code error; + bool has_value; + // + // Initially, the object can be in one of a few different places: + // + // 1. The start of the object, at the first field: + // + // ``` + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 2, index 1) + // ``` + if (at_first_field()) { + has_value = true; -inline bool is_pointer_well_formed(std::string_view json_pointer) noexcept { - if (simdjson_unlikely(json_pointer.empty())) { // can't be - return false; - } - if (simdjson_unlikely(json_pointer[0] != '/')) { - return false; - } - size_t escape = json_pointer.find('~'); - if (escape == std::string_view::npos) { - return true; - } - if (escape == json_pointer.size() - 1) { - return false; - } - if (json_pointer[escape + 1] != '0' && json_pointer[escape + 1] != '1') { + // + // 2. When a previous search did not yield a value or the object is empty: + // + // ``` + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 0) + // { } + // ^ (depth 0, index 2) + // ``` + // + } else if (!is_open()) { +#if SIMDJSON_DEVELOPMENT_CHECKS + // If we're past the end of the object, we're being iterated out of order. + // Note: this is not perfect detection. It's possible the user is inside some other object; if so, + // this object iterator will blithely scan that object for fields. + if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; } +#endif return false; - } - return true; -} -simdjson_inline simdjson_result value::at_pointer(std::string_view json_pointer) noexcept { - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) - { - case json_type::array: - return (*this).get_array().at_pointer(json_pointer); - case json_type::object: - return (*this).get_object().at_pointer(json_pointer); - default: - // a non-empty string can be invalid, or accessing a primitive (issue 2154) - if (is_pointer_well_formed(json_pointer)) { - return NO_SUCH_FIELD; - } - return INVALID_JSON_POINTER; + // 3. When a previous search found a field or an iterator yielded a value: + // + // ``` + // // When a field was not fully consumed (or not even touched at all) + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 2) + // // When a field was fully consumed + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 1) + // // When the last field was fully consumed + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 1) + // ``` + // + } else { + if ((error = skip_child() )) { abandon(); return error; } + if ((error = has_next_field().get(has_value) )) { abandon(); return error; } +#if SIMDJSON_DEVELOPMENT_CHECKS + if (_json_iter->start_position(_depth) != start_position()) { return OUT_OF_ORDER_ITERATION; } +#endif } -} + while (has_value) { + // Get the key and colon, stopping at the value. + raw_json_string actual_key; + // size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes + // Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2. + // field_key() advances the pointer and checks that '"' is found (corresponding to a key). + // The depth is left unchanged by field_key(). + if ((error = field_key().get(actual_key) )) { abandon(); return error; }; + // field_value() will advance and check that we find a ':' separating the + // key and the value. It will also increment the depth by one. + if ((error = field_value() )) { abandon(); return error; } + // If it matches, stop and return + // We could do it this way if we wanted to allow arbitrary + // key content (including escaped quotes). + //if (actual_key.unsafe_is_equal(max_key_length, key)) { + // Instead we do the following which may trigger buffer overruns if the + // user provides an adversarial key (containing a well placed unescaped quote + // character and being longer than the number of bytes remaining in the JSON + // input). + if (actual_key.unsafe_is_equal(key)) { + logger::log_event(*this, "match", key, -2); + // If we return here, then we return while pointing at the ':' that we just checked. + return true; + } -simdjson_inline simdjson_result value::at_path(std::string_view json_path) noexcept { - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) { - case json_type::array: - return (*this).get_array().at_path(json_path); - case json_type::object: - return (*this).get_object().at_path(json_path); - default: - return INVALID_JSON_POINTER; + // No match: skip the value and see if , or } is next + logger::log_event(*this, "no match", key, -2); + // The call to skip_child is meant to skip over the value corresponding to the key. + // After skip_child(), we are right before the next comma (',') or the final brace ('}'). + SIMDJSON_TRY( skip_child() ); // Skip the value entirely + // The has_next_field() advances the pointer and check that either ',' or '}' is found. + // It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found, + // then we are in error and we abort. + if ((error = has_next_field().get(has_value) )) { abandon(); return error; } } -} - -} // namespace ondemand -} // namespace arm64 -} // namespace simdjson - -namespace simdjson { -simdjson_inline simdjson_result::simdjson_result( - arm64::ondemand::value &&value -) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) -{ -} -simdjson_inline simdjson_result::simdjson_result( - error_code error -) noexcept : - implementation_simdjson_result_base(error) -{ -} -simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); -} -simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); -} -simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { - if (error()) { return error(); } - return first.at(index); -} -simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); -} -simdjson_inline simdjson_result simdjson_result::end() & noexcept { - if (error()) { return error(); } - return {}; + // If the loop ended, we're out of fields to look at. + return false; } -simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) noexcept { - if (error()) { return error(); } - return first.find_field(key); -} -simdjson_inline simdjson_result simdjson_result::find_field(const char *key) noexcept { - if (error()) { return error(); } - return first.find_field(key); -} +SIMDJSON_PUSH_DISABLE_WARNINGS +SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::find_field_unordered_raw(const std::string_view key) noexcept { + /** + * When find_field_unordered_raw is called, we can either be pointing at the + * first key, pointing outside (at the closing brace) or if a key was matched + * we can be either pointing right afterthe ':' right before the value (that we need skip), + * or we may have consumed the value and we might be at a comma or at the + * final brace (ready for a call to has_next_field()). + */ + error_code error; + bool has_value; -simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); -} -simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); -} - -simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) noexcept { - if (error()) { return error(); } - return first[key]; -} -simdjson_inline simdjson_result simdjson_result::operator[](const char *key) noexcept { - if (error()) { return error(); } - return first[key]; -} - -simdjson_inline simdjson_result simdjson_result::get_array() noexcept { - if (error()) { return error(); } - return first.get_array(); -} -simdjson_inline simdjson_result simdjson_result::get_object() noexcept { - if (error()) { return error(); } - return first.get_object(); -} -simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { - if (error()) { return error(); } - return first.get_uint64(); -} -simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { - if (error()) { return error(); } - return first.get_uint64_in_string(); -} -simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { - if (error()) { return error(); } - return first.get_int64(); -} -simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { - if (error()) { return error(); } - return first.get_int64_in_string(); -} -simdjson_inline simdjson_result simdjson_result::get_double() noexcept { - if (error()) { return error(); } - return first.get_double(); -} -simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { - if (error()) { return error(); } - return first.get_double_in_string(); -} -simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(allow_replacement); -} -template -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(receiver, allow_replacement); -} -simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { - if (error()) { return error(); } - return first.get_wobbly_string(); -} -simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { - if (error()) { return error(); } - return first.get_raw_json_string(); -} -simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { - if (error()) { return error(); } - return first.get_bool(); -} -simdjson_inline simdjson_result simdjson_result::is_null() noexcept { - if (error()) { return error(); } - return first.is_null(); -} - -template simdjson_inline simdjson_result simdjson_result::get() noexcept { - if (error()) { return error(); } - return first.get(); -} -template simdjson_inline error_code simdjson_result::get(T &out) noexcept { - if (error()) { return error(); } - return first.get(out); -} - -template<> simdjson_inline simdjson_result simdjson_result::get() noexcept { - if (error()) { return error(); } - return std::move(first); -} -template<> simdjson_inline error_code simdjson_result::get(arm64::ondemand::value &out) noexcept { - if (error()) { return error(); } - out = first; - return SUCCESS; -} - -simdjson_inline simdjson_result simdjson_result::type() noexcept { - if (error()) { return error(); } - return first.type(); -} -simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { - if (error()) { return error(); } - return first.is_scalar(); -} -simdjson_inline simdjson_result simdjson_result::is_string() noexcept { - if (error()) { return error(); } - return first.is_string(); -} -simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { - if (error()) { return error(); } - return first.is_negative(); -} -simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { - if (error()) { return error(); } - return first.is_integer(); -} -simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { - if (error()) { return error(); } - return first.get_number_type(); -} -simdjson_inline simdjson_result simdjson_result::get_number() noexcept { - if (error()) { return error(); } - return first.get_number(); -} -#if SIMDJSON_EXCEPTIONS -template -simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return static_cast(first); -} -simdjson_inline simdjson_result::operator arm64::ondemand::array() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator arm64::ondemand::object() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator int64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator double() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator arm64::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator bool() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -#endif - -simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { - if (error()) { return error(); } - return first.raw_json_token(); -} - -simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { - if (error()) { return error(); } - return first.raw_json(); -} - -simdjson_inline simdjson_result simdjson_result::current_location() noexcept { - if (error()) { return error(); } - return first.current_location(); -} - -simdjson_inline simdjson_result simdjson_result::current_depth() const noexcept { - if (error()) { return error(); } - return first.current_depth(); -} - -simdjson_inline simdjson_result simdjson_result::at_pointer( - std::string_view json_pointer) noexcept { - if (error()) { - return error(); - } - return first.at_pointer(json_pointer); -} - -simdjson_inline simdjson_result simdjson_result::at_path( - std::string_view json_path) noexcept { - if (error()) { - return error(); - } - return first.at_path(json_path); -} - -} // namespace simdjson - -#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H -/* end file simdjson/generic/ondemand/value-inl.h for arm64 */ -/* including simdjson/generic/ondemand/value_iterator-inl.h for arm64: #include "simdjson/generic/ondemand/value_iterator-inl.h" */ -/* begin file simdjson/generic/ondemand/value_iterator-inl.h for arm64 */ -#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H - -/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ -/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/atomparsing.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/numberparsing.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string-inl.h" */ -/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -namespace simdjson { -namespace arm64 { -namespace ondemand { - -simdjson_inline value_iterator::value_iterator( - json_iterator *json_iter, - depth_t depth, - token_position start_position -) noexcept : _json_iter{json_iter}, _depth{depth}, _start_position{start_position} -{ -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_object() noexcept { - SIMDJSON_TRY( start_container('{', "Not an object", "object") ); - return started_object(); -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_root_object() noexcept { - SIMDJSON_TRY( start_container('{', "Not an object", "object") ); - return started_root_object(); -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::started_object() noexcept { - assert_at_container_start(); -#if SIMDJSON_DEVELOPMENT_CHECKS - _json_iter->set_start_position(_depth, start_position()); -#endif - if (*_json_iter->peek() == '}') { - logger::log_value(*_json_iter, "empty object"); - _json_iter->return_current_and_advance(); - end_container(); - return false; - } - return true; -} - -simdjson_warn_unused simdjson_inline error_code value_iterator::check_root_object() noexcept { - // When in streaming mode, we cannot expect peek_last() to be the last structural element of the - // current document. It only works in the normal mode where we have indexed a single document. - // Note that adding a check for 'streaming' is not expensive since we only have at most - // one root element. - if ( ! _json_iter->streaming() ) { - // The following lines do not fully protect against garbage content within the - // object: e.g., `{"a":2} foo }`. Users concerned with garbage content should - // call `at_end()` on the document instance at the end of the processing to - // ensure that the processing has finished at the end. - // - if (*_json_iter->peek_last() != '}') { - _json_iter->abandon(); - return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing } at end"); - } - // If the last character is } *and* the first gibberish character is also '}' - // then on-demand could accidentally go over. So we need additional checks. - // https://github.com/simdjson/simdjson/issues/1834 - // Checking that the document is balanced requires a full scan which is potentially - // expensive, but it only happens in edge cases where the first padding character is - // a closing bracket. - if ((*_json_iter->peek(_json_iter->end_position()) == '}') && (!_json_iter->balanced())) { - _json_iter->abandon(); - // The exact error would require more work. It will typically be an unclosed object. - return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "the document is unbalanced"); - } - } - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::started_root_object() noexcept { - auto error = check_root_object(); - if(error) { return error; } - return started_object(); -} - -simdjson_warn_unused simdjson_inline error_code value_iterator::end_container() noexcept { -#if SIMDJSON_CHECK_EOF - if (depth() > 1 && at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing parent ] or }"); } - // if (depth() <= 1 && !at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing [ or { at start"); } -#endif // SIMDJSON_CHECK_EOF - _json_iter->ascend_to(depth()-1); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::has_next_field() noexcept { - assert_at_next(); - - // It's illegal to call this unless there are more tokens: anything that ends in } or ] is - // obligated to verify there are more tokens if they are not the top level. - switch (*_json_iter->return_current_and_advance()) { - case '}': - logger::log_end_value(*_json_iter, "object"); - SIMDJSON_TRY( end_container() ); - return false; - case ',': - return true; - default: - return report_error(TAPE_ERROR, "Missing comma between object fields"); - } -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::find_field_raw(const std::string_view key) noexcept { - error_code error; - bool has_value; - // - // Initially, the object can be in one of a few different places: - // - // 1. The start of the object, at the first field: - // - // ``` - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 2, index 1) - // ``` - if (at_first_field()) { - has_value = true; - - // - // 2. When a previous search did not yield a value or the object is empty: - // - // ``` - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 0) - // { } - // ^ (depth 0, index 2) - // ``` - // - } else if (!is_open()) { -#if SIMDJSON_DEVELOPMENT_CHECKS - // If we're past the end of the object, we're being iterated out of order. - // Note: this is not perfect detection. It's possible the user is inside some other object; if so, - // this object iterator will blithely scan that object for fields. - if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; } -#endif - return false; - - // 3. When a previous search found a field or an iterator yielded a value: - // - // ``` - // // When a field was not fully consumed (or not even touched at all) - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 2) - // // When a field was fully consumed - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 1) - // // When the last field was fully consumed - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 1) - // ``` - // - } else { - if ((error = skip_child() )) { abandon(); return error; } - if ((error = has_next_field().get(has_value) )) { abandon(); return error; } -#if SIMDJSON_DEVELOPMENT_CHECKS - if (_json_iter->start_position(_depth) != start_position()) { return OUT_OF_ORDER_ITERATION; } -#endif - } - while (has_value) { - // Get the key and colon, stopping at the value. - raw_json_string actual_key; - // size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes - // Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2. - // field_key() advances the pointer and checks that '"' is found (corresponding to a key). - // The depth is left unchanged by field_key(). - if ((error = field_key().get(actual_key) )) { abandon(); return error; }; - // field_value() will advance and check that we find a ':' separating the - // key and the value. It will also increment the depth by one. - if ((error = field_value() )) { abandon(); return error; } - // If it matches, stop and return - // We could do it this way if we wanted to allow arbitrary - // key content (including escaped quotes). - //if (actual_key.unsafe_is_equal(max_key_length, key)) { - // Instead we do the following which may trigger buffer overruns if the - // user provides an adversarial key (containing a well placed unescaped quote - // character and being longer than the number of bytes remaining in the JSON - // input). - if (actual_key.unsafe_is_equal(key)) { - logger::log_event(*this, "match", key, -2); - // If we return here, then we return while pointing at the ':' that we just checked. - return true; - } - - // No match: skip the value and see if , or } is next - logger::log_event(*this, "no match", key, -2); - // The call to skip_child is meant to skip over the value corresponding to the key. - // After skip_child(), we are right before the next comma (',') or the final brace ('}'). - SIMDJSON_TRY( skip_child() ); // Skip the value entirely - // The has_next_field() advances the pointer and check that either ',' or '}' is found. - // It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found, - // then we are in error and we abort. - if ((error = has_next_field().get(has_value) )) { abandon(); return error; } - } - - // If the loop ended, we're out of fields to look at. - return false; -} - -SIMDJSON_PUSH_DISABLE_WARNINGS -SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::find_field_unordered_raw(const std::string_view key) noexcept { - /** - * When find_field_unordered_raw is called, we can either be pointing at the - * first key, pointing outside (at the closing brace) or if a key was matched - * we can be either pointing right afterthe ':' right before the value (that we need skip), - * or we may have consumed the value and we might be at a comma or at the - * final brace (ready for a call to has_next_field()). - */ - error_code error; - bool has_value; - - // First, we scan from that point to the end. - // If we don't find a match, we may loop back around, and scan from the beginning to that point. - token_position search_start = _json_iter->position(); + // First, we scan from that point to the end. + // If we don't find a match, we may loop back around, and scan from the beginning to that point. + token_position search_start = _json_iter->position(); // We want to know whether we need to go back to the beginning. bool at_first = at_first_field(); @@ -41359,6 +42286,8 @@ simdjson_inline simdjson_result value_iterator::is_root_null(bool check_tr if(result) { // we have something that looks like a null. if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; } advance_root_scalar("null"); + } else if (json[0] == 'n') { + return incorrect_type_error("Not a null but starts with n"); } return result; } @@ -41650,6 +42579,8 @@ simdjson_inline simdjson_result::simdjson_resul #endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H /* end file simdjson/generic/ondemand/value_iterator-inl.h for arm64 */ + + /* end file simdjson/generic/ondemand/amalgamated.h for arm64 */ /* including simdjson/arm64/end.h: #include "simdjson/arm64/end.h" */ /* begin file simdjson/arm64/end.h */ @@ -41865,7 +42796,13 @@ simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t } // namespace fallback } // namespace simdjson +#ifndef SIMDJSON_SWAR_NUMBER_PARSING +#if SIMDJSON_IS_BIG_ENDIAN +#define SIMDJSON_SWAR_NUMBER_PARSING 0 +#else #define SIMDJSON_SWAR_NUMBER_PARSING 1 +#endif +#endif #endif // SIMDJSON_FALLBACK_NUMBERPARSING_DEFS_H /* end file simdjson/fallback/numberparsing_defs.h */ @@ -41927,6 +42864,132 @@ class value_iterator; #endif // SIMDJSON_GENERIC_ONDEMAND_BASE_H /* end file simdjson/generic/ondemand/base.h for fallback */ +/* including simdjson/generic/ondemand/deserialize.h for fallback: #include "simdjson/generic/ondemand/deserialize.h" */ +/* begin file simdjson/generic/ondemand/deserialize.h for fallback */ +#if SIMDJSON_SUPPORTS_DESERIALIZATION + +#ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_ONDEMAND_DESERIALIZE_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +namespace simdjson { + +namespace tag_invoke_fn_ns { +void tag_invoke(); + +struct tag_invoke_fn { + template + requires requires(Tag tag, Args &&...args) { + tag_invoke(std::forward(tag), std::forward(args)...); + } + constexpr auto operator()(Tag tag, Args &&...args) const + noexcept(noexcept(tag_invoke(std::forward(tag), + std::forward(args)...))) + -> decltype(tag_invoke(std::forward(tag), + std::forward(args)...)) { + return tag_invoke(std::forward(tag), std::forward(args)...); + } +}; +} // namespace tag_invoke_fn_ns + +inline namespace tag_invoke_ns { +inline constexpr tag_invoke_fn_ns::tag_invoke_fn tag_invoke = {}; +} // namespace tag_invoke_ns + +template +concept tag_invocable = requires(Tag tag, Args... args) { + tag_invoke(std::forward(tag), std::forward(args)...); +}; + +template +concept nothrow_tag_invocable = + tag_invocable && requires(Tag tag, Args... args) { + { + tag_invoke(std::forward(tag), std::forward(args)...) + } noexcept; + }; + +template +using tag_invoke_result = + std::invoke_result; + +template +using tag_invoke_result_t = + std::invoke_result_t; + +template using tag_t = std::decay_t; + + +struct deserialize_tag; + +/// These types are deserializable in a built-in way +template struct is_builtin_deserializable : std::false_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; + +template +concept is_builtin_deserializable_v = is_builtin_deserializable::value; + +template +concept custom_deserializable = tag_invocable; + +template +concept deserializable = custom_deserializable || is_builtin_deserializable_v; + +template +concept nothrow_custom_deserializable = nothrow_tag_invocable; + +// built-in types are noexcept and if an error happens, the value simply gets ignored and the error is returned. +template +concept nothrow_deserializable = nothrow_custom_deserializable || is_builtin_deserializable_v; + +/// Deserialize Tag +inline constexpr struct deserialize_tag { + using value_type = fallback::ondemand::value; + using document_type = fallback::ondemand::document; + using document_reference_type = fallback::ondemand::document_reference; + + // Customization Point for value + template + requires custom_deserializable + [[nodiscard]] constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for document + template + requires custom_deserializable + [[nodiscard]] constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for document reference + template + requires custom_deserializable + [[nodiscard]] constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + +} deserialize{}; + +} // namespace simdjson + +#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION + +/* end file simdjson/generic/ondemand/deserialize.h for fallback */ /* including simdjson/generic/ondemand/value_iterator.h for fallback: #include "simdjson/generic/ondemand/value_iterator.h" */ /* begin file simdjson/generic/ondemand/value_iterator.h for fallback */ #ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H @@ -42431,12 +43494,15 @@ struct simdjson_result : public fallback::im /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ +#include + namespace simdjson { + namespace fallback { namespace ondemand { - /** * An ephemeral JSON value returned during iteration. It is only valid for as long as you do * not access more data in the JSON document. @@ -42461,16 +43527,21 @@ class value { * @returns A value of the given type, parsed from the JSON. * @returns INCORRECT_TYPE If the JSON value is not the given type. */ - template simdjson_inline simdjson_result get() noexcept { - // Unless the simdjson library or the user provides an inline implementation, calling this method should - // immediately fail. - static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " - "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " - "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." - " You may also add support for custom types, see our documentation."); + template + simdjson_inline simdjson_result get() +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; } + /** * Get this value as the given type. * @@ -42480,7 +43551,32 @@ class value { * @returns INCORRECT_TYPE If the JSON value is not an object. * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ - template simdjson_inline error_code get(T &out) noexcept; + template + simdjson_inline error_code get(T &out) +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_DESERIALIZATION + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION + // Unless the simdjson library or the user provides an inline implementation, calling this method should + // immediately fail. + static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " + "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " + "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." + " You may also add support for custom types, see our documentation."); + static_cast(out); // to get rid of unused errors + return UNINITIALIZED; +#if SIMDJSON_SUPPORTS_DESERIALIZATION + } +#endif + } /** * Cast this JSON value to an array. @@ -42556,6 +43652,17 @@ class value { * Important: a value should be consumed once. Calling get_string() twice on the same value * is an error. * + * In some instances, you may want to allow replacement of invalid Unicode sequences. + * You may do so by passing the allow_replacement parameter as true. In the following + * example, the string "431924697b\udff0L\u0001Y" is not valid Unicode. By passing true + * to get_string, we allow the replacement of the invalid Unicode sequences with the Unicode + * replacement character (U+FFFD). + * + * simdjson::ondemand::parser parser; + * auto json = R"({"deviceId":"431924697b\udff0L\u0001Y"})"_padded; + * simdjson::ondemand::document doc = parser.iterate(json); + * auto view = doc["deviceId"].get_string(true); + * * @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next * time it parses a document or when it is destroyed. * @returns INCORRECT_TYPE if the JSON value is not a string. @@ -42807,6 +43914,7 @@ class value { simdjson_inline simdjson_result operator[](std::string_view key) noexcept; /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ simdjson_inline simdjson_result operator[](const char *key) noexcept; + simdjson_result operator[](int) noexcept = delete; /** * Get the type of this JSON value. It does not validate or consume the value. @@ -43174,6 +44282,7 @@ struct simdjson_result : public fallback::implementat simdjson_inline simdjson_result operator[](std::string_view key) noexcept; /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ simdjson_inline simdjson_result operator[](const char *key) noexcept; + simdjson_result operator[](int) noexcept = delete; /** * Get the type of this JSON value. @@ -44229,6 +45338,22 @@ class parser { * using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the * SIMDJSON_PADDING bytes to avoid runtime warnings. * + * ### std::string references + * + * If you pass a mutable std::string reference (std::string&), the parser will seek to extend + * its capacity to SIMDJSON_PADDING bytes beyond the end of the string. + * + * Whenever you pass an std::string reference, the parser will access the bytes beyond the end of + * the string but before the end of the allocated memory (std::string::capacity()). + * If you are using a sanitizer that checks for reading uninitialized bytes or std::string's + * container-overflow checks, you may encounter sanitizer warnings. + * You can safely ignore these warnings. Or you can call simdjson::pad(std::string&) to pad the + * string with SIMDJSON_PADDING spaces: this function returns a simdjson::padding_string_view + * which can be be passed to the parser's iterate function: + * + * std::string json = R"({ "foo": 1 } { "foo": 2 } { "foo": 3 } )"; + * document doc = parser.iterate(simdjson::pad(json)); + * * @param json The JSON to parse. * @param len The length of the JSON. * @param capacity The number of bytes allocated in the JSON (must be at least len+SIMDJSON_PADDING). @@ -44423,8 +45548,12 @@ class parser { * behavior of the parser for future operations. */ bool threaded{true}; + #else + /** + * When SIMDJSON_THREADS_ENABLED is not defined, the parser instance cannot use threads. + */ + bool threaded{false}; #endif - /** * Unescape this JSON string, replacing \\ with \, \n with newline, etc. to a user-provided buffer. * The result must be valid UTF-8. @@ -44566,7 +45695,8 @@ class array { * calling this function, if successful, the array is 'rewinded' at its * beginning as if it had never been accessed. If the JSON is malformed (e.g., * there is a missing comma), then an error is returned and it is no longer - * safe to continue. + * safe to continue. Note that count_elements() does not validate the JSON values, + * only the structure of the array. * * To check that an array is empty, it is more performant to use * the is_empty() method. @@ -44844,8 +45974,11 @@ struct simdjson_result : public fallback::im /* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + namespace simdjson { namespace fallback { namespace ondemand { @@ -45018,24 +46151,39 @@ class document { * @returns A value of the given type, parsed from the JSON. * @returns INCORRECT_TYPE If the JSON value is not the given type. */ - template simdjson_inline simdjson_result get() & noexcept { - // Unless the simdjson library or the user provides an inline implementation, calling this method should - // immediately fail. - static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " - "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " - "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." - " You may also add support for custom types, see our documentation."); - } - /** @overload template simdjson_result get() & noexcept */ - template simdjson_deprecated simdjson_inline simdjson_result get() && noexcept { - // Unless the simdjson library or the user provides an inline implementation, calling this method should - // immediately fail. - static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " - "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " - "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." - " You may also add support for custom types, see our documentation."); + template + simdjson_inline simdjson_result get() & +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "Cannot initialize the specified type."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + /** + * @overload template simdjson_result get() & noexcept + * + * We disallow the use tag_invoke CPO on a moved document; it may create UB + * if user uses `ondemand::array` or `ondemand::object` in their custom type. + * + * The member function is still remains specialize-able for compatibility + * reasons, but we completely disallow its use when a tag_invoke customization + * is provided. + */ + template + simdjson_inline simdjson_result get() && +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(!std::is_same::value && !std::is_same::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document being alive; that would be Undefined Behaviour."); + return static_cast(*this).get(); } /** @@ -45049,7 +46197,32 @@ class document { * @returns INCORRECT_TYPE If the JSON value is not an object. * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ - template simdjson_inline error_code get(T &out) & noexcept; + template + simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_DESERIALIZATION + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION + // Unless the simdjson library or the user provides an inline implementation, calling this method should + // immediately fail. + static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " + "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " + "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." + " You may also add support for custom types, see our documentation."); + static_cast(out); // to get rid of unused errors + return UNINITIALIZED; +#if SIMDJSON_SUPPORTS_DESERIALIZATION + } +#endif + } /** @overload template error_code get(T &out) & noexcept */ template simdjson_deprecated simdjson_inline error_code get(T &out) && noexcept; @@ -45151,7 +46324,8 @@ class document { * calling this function, if successful, the array is 'rewinded' at its * beginning as if it had never been accessed. If the JSON is malformed (e.g., * there is a missing comma), then an error is returned and it is no longer - * safe to continue. + * safe to continue. Note that count_elements() does not validate the JSON values, + * only the structure of the array. */ simdjson_inline simdjson_result count_elements() & noexcept; /** @@ -45263,6 +46437,7 @@ class document { simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; /** * Get the type of this JSON value. It does not validate or consume the value. @@ -45531,6 +46706,11 @@ class document { /** * A document_reference is a thin wrapper around a document reference instance. + * The document_reference instances are used primarily/solely for streams of JSON + * documents. They differ from document instances when parsing a scalar value + * (a document that is not an array or an object). In the case of a document, + * we expect the document to be fully consumed. In the case of a document_reference, + * we allow trailing content. */ class document_reference { public: @@ -45556,7 +46736,70 @@ class document_reference { simdjson_inline simdjson_result get_value() noexcept; simdjson_inline simdjson_result is_null() noexcept; - template simdjson_inline simdjson_result get() & noexcept; + template + simdjson_inline simdjson_result get() & +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "Cannot initialize the specified type."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + template + simdjson_inline simdjson_result get() && +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(!std::is_same::value && !std::is_same::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document_reference being alive; that would be Undefined Behaviour."); + return static_cast(*this).get(); + } + + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool, value + * + * Be mindful that the document instance must remain in scope while you are accessing object, array and value instances. + * + * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. + * @returns INCORRECT_TYPE If the JSON value is not an object. + * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. + */ + template + simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_DESERIALIZATION + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION + // Unless the simdjson library or the user provides an inline implementation, calling this method should + // immediately fail. + static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " + "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " + "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." + " You may also add support for custom types, see our documentation."); + static_cast(out); // to get rid of unused errors + return UNINITIALIZED; +#if SIMDJSON_SUPPORTS_DESERIALIZATION + } +#endif + } + /** @overload template error_code get(T &out) & noexcept */ + template simdjson_inline error_code get(T &out) && noexcept; simdjson_inline simdjson_result raw_json() noexcept; simdjson_inline operator document&() const noexcept; #if SIMDJSON_EXCEPTIONS @@ -45581,6 +46824,7 @@ class document_reference { simdjson_inline simdjson_result find_field(const char *key) & noexcept; simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; @@ -45659,6 +46903,7 @@ struct simdjson_result : public fallback::implemen simdjson_inline simdjson_result find_field(const char *key) & noexcept; simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; simdjson_inline simdjson_result type() noexcept; @@ -45708,8 +46953,14 @@ struct simdjson_result : public fallback simdjson_inline simdjson_result get_bool() noexcept; simdjson_inline simdjson_result get_value() noexcept; simdjson_inline simdjson_result is_null() noexcept; + + template simdjson_inline simdjson_result get() & noexcept; + template simdjson_inline simdjson_result get() && noexcept; + + template simdjson_inline error_code get(T &out) & noexcept; + template simdjson_inline error_code get(T &out) && noexcept; #if SIMDJSON_EXCEPTIONS - template ::value == false>::type> + template explicit simdjson_inline operator T() noexcept(false); simdjson_inline operator fallback::ondemand::array() & noexcept(false); simdjson_inline operator fallback::ondemand::object() & noexcept(false); @@ -45730,6 +46981,7 @@ struct simdjson_result : public fallback simdjson_inline simdjson_result find_field(const char *key) & noexcept; simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; simdjson_inline simdjson_result type() noexcept; @@ -46660,6 +47912,177 @@ inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result +#include + +namespace simdjson { +template +constexpr bool require_custom_serialization = false; + +////////////////////////////// +// Number deserialization +////////////////////////////// + +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + using limits = std::numeric_limits; + + uint64_t x; + SIMDJSON_TRY(val.get_uint64().get(x)); + if (x > (limits::max)()) { + return NUMBER_OUT_OF_RANGE; + } + out = static_cast(x); + return SUCCESS; +} + +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + double x; + SIMDJSON_TRY(val.get_double().get(x)); + out = static_cast(x); + return SUCCESS; +} + +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + using limits = std::numeric_limits; + + int64_t x; + SIMDJSON_TRY(val.get_int64().get(x)); + if (x > (limits::max)() || x < (limits::min)()) { + return NUMBER_OUT_OF_RANGE; + } + out = static_cast(x); + return SUCCESS; +} + +/** + * STL containers have several constructors including one that takes a single + * size argument. Thus, some compilers (Visual Studio) will not be able to + * disambiguate between the size and container constructor. Users should + * explicitly specify the type of the container as needed: e.g., + * doc.get>(). + */ +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { + using value_type = typename std::remove_cvref_t::value_type; + static_assert( + deserializable, + "The specified type inside the container must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the container must default constructible."); + + fallback::ondemand::array arr; + SIMDJSON_TRY(val.get_array().get(arr)); + for (auto v : arr) { + if constexpr (concepts::returns_reference) { + if (auto const err = v.get().get(concepts::emplace_one(out)); + err) { + // If an error occurs, the empty element that we just inserted gets + // removed. We're not using a temp variable because if T is a heavy + // type, we want the valid path to be the fast path and the slow path be + // the path that has errors in it. + if constexpr (requires { out.pop_back(); }) { + static_cast(out.pop_back()); + } + return err; + } + } else { + value_type temp; + if (auto const err = v.get().get(temp); err) { + return err; + } + concepts::emplace_one(out, std::move(temp)); + } + } + return SUCCESS; +} + + + +/** + * This CPO (Customization Point Object) will help deserialize into + * smart pointers. + * + * If constructing T is nothrow, this conversion should be nothrow as well since + * we return MEMALLOC if we're not able to allocate memory instead of throwing + * the error message. + * + * @tparam T The type inside the smart pointer + * @tparam ValT document/value type + * @param val document/value + * @param out a reference to the smart pointer + * @return status of the conversion + */ +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::element_type, ValT>) { + using element_type = typename std::remove_cvref_t::element_type; + + // For better error messages, don't use these as constraints on + // the tag_invoke CPO. + static_assert( + deserializable, + "The specified type inside the unique_ptr must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the unique_ptr must default constructible."); + + auto ptr = new (std::nothrow) element_type(); + if (ptr == nullptr) { + return MEMALLOC; + } + SIMDJSON_TRY(val.template get(*ptr)); + out.reset(ptr); + return SUCCESS; +} + +/** + * This CPO (Customization Point Object) will help deserialize into optional types. + */ +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::value_type, ValT>) { + using value_type = typename std::remove_cvref_t::value_type; + + static_assert( + deserializable, + "The specified type inside the unique_ptr must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the unique_ptr must default constructible."); + + if (!out) { + out.emplace(); + } + SIMDJSON_TRY(val.template get(out.value())); + return SUCCESS; +} + +} // namespace simdjson + +#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +/* end file simdjson/generic/ondemand/std_deserialize.h for fallback */ + // Inline definitions /* including simdjson/generic/ondemand/array-inl.h for fallback: #include "simdjson/generic/ondemand/array-inl.h" */ /* begin file simdjson/generic/ondemand/array-inl.h for fallback */ @@ -46667,6 +48090,7 @@ inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result array::at_pointer(std::string_view json_pointer) n return child; } -inline std::string json_path_to_pointer_conversion(std::string_view json_path) { - if (json_path.empty() || (json_path.front() != '.' && - json_path.front() != '[')) { - return "-1"; // This is just a sentinel value, the caller should check for this and return an error. - } - - std::string result; - // Reserve space to reduce allocations, adjusting for potential increases due - // to escaping. - result.reserve(json_path.size() * 2); - - size_t i = 0; - - while (i < json_path.length()) { - if (json_path[i] == '.') { - result += '/'; - } else if (json_path[i] == '[') { - result += '/'; - ++i; // Move past the '[' - while (i < json_path.length() && json_path[i] != ']') { - if (json_path[i] == '~') { - result += "~0"; - } else if (json_path[i] == '/') { - result += "~1"; - } else { - result += json_path[i]; - } - ++i; - } - if (i == json_path.length() || json_path[i] != ']') { - return "-1"; // Using sentinel value that will be handled as an error by the caller. - } - } else { - if (json_path[i] == '~') { - result += "~0"; - } else if (json_path[i] == '/') { - result += "~1"; - } else { - result += json_path[i]; - } - } - ++i; - } - - return result; -} - inline simdjson_result array::at_path(std::string_view json_path) noexcept { auto json_pointer = json_path_to_pointer_conversion(json_path); if (json_pointer == "-1") { return INVALID_JSON_POINTER; } @@ -47028,313 +48405,276 @@ simdjson_inline simdjson_result &simdjson_re #endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H /* end file simdjson/generic/ondemand/array_iterator-inl.h for fallback */ -/* including simdjson/generic/ondemand/document-inl.h for fallback: #include "simdjson/generic/ondemand/document-inl.h" */ -/* begin file simdjson/generic/ondemand/document-inl.h for fallback */ -#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H +/* including simdjson/generic/ondemand/value-inl.h for fallback: #include "simdjson/generic/ondemand/value-inl.h" */ +/* begin file simdjson/generic/ondemand/value-inl.h for fallback */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ -/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { namespace fallback { namespace ondemand { -simdjson_inline document::document(ondemand::json_iterator &&_iter) noexcept - : iter{std::forward(_iter)} +simdjson_inline value::value(const value_iterator &_iter) noexcept + : iter{_iter} { - logger::log_start_value(iter, "document"); } - -simdjson_inline document document::start(json_iterator &&iter) noexcept { - return document(std::forward(iter)); +simdjson_inline value value::start(const value_iterator &iter) noexcept { + return iter; } - -inline void document::rewind() noexcept { - iter.rewind(); +simdjson_inline value value::resume(const value_iterator &iter) noexcept { + return iter; } -inline std::string document::to_debug_string() noexcept { - return iter.to_string(); +simdjson_inline simdjson_result value::get_array() noexcept { + return array::start(iter); } - -inline simdjson_result document::current_location() const noexcept { - return iter.current_location(); +simdjson_inline simdjson_result value::get_object() noexcept { + return object::start(iter); } - -inline int32_t document::current_depth() const noexcept { - return iter.depth(); +simdjson_inline simdjson_result value::start_or_resume_object() noexcept { + if (iter.at_start()) { + return get_object(); + } else { + return object::resume(iter); + } } -inline bool document::at_end() const noexcept { - return iter.at_end(); +simdjson_inline simdjson_result value::get_raw_json_string() noexcept { + return iter.get_raw_json_string(); } - - -inline bool document::is_alive() noexcept { - return iter.is_alive(); +simdjson_inline simdjson_result value::get_string(bool allow_replacement) noexcept { + return iter.get_string(allow_replacement); } -simdjson_inline value_iterator document::resume_value_iterator() noexcept { - return value_iterator(&iter, 1, iter.root_position()); +template +simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept { + return iter.get_string(receiver, allow_replacement); } -simdjson_inline value_iterator document::get_root_value_iterator() noexcept { - return resume_value_iterator(); +simdjson_inline simdjson_result value::get_wobbly_string() noexcept { + return iter.get_wobbly_string(); } -simdjson_inline simdjson_result document::start_or_resume_object() noexcept { - if (iter.at_root()) { - return get_object(); - } else { - return object::resume(resume_value_iterator()); - } +simdjson_inline simdjson_result value::get_double() noexcept { + return iter.get_double(); } -simdjson_inline simdjson_result document::get_value() noexcept { - // Make sure we start any arrays or objects before returning, so that start_root_() - // gets called. - - // It is the convention throughout the code that the macro `SIMDJSON_DEVELOPMENT_CHECKS` determines whether - // we check for OUT_OF_ORDER_ITERATION. Proper on::demand code should never trigger this error. -#if SIMDJSON_DEVELOPMENT_CHECKS - if (!iter.at_root()) { return OUT_OF_ORDER_ITERATION; } -#endif - // assert_at_root() serves two purposes: in Debug mode, whether or not - // SIMDJSON_DEVELOPMENT_CHECKS is set or not, it checks that we are at the root of - // the document (this will typically be redundant). In release mode, it generates - // SIMDJSON_ASSUME statements to allow the compiler to make assumptions. - iter.assert_at_root(); - switch (*iter.peek()) { - case '[': { - // The following lines check that the document ends with ]. - auto value_iterator = get_root_value_iterator(); - auto error = value_iterator.check_root_array(); - if(error) { return error; } - return value(get_root_value_iterator()); - } - case '{': { - // The following lines would check that the document ends with }. - auto value_iterator = get_root_value_iterator(); - auto error = value_iterator.check_root_object(); - if(error) { return error; } - return value(get_root_value_iterator()); - } - default: - // Unfortunately, scalar documents are a special case in simdjson and they cannot - // be safely converted to value instances. - return SCALAR_DOCUMENT_AS_VALUE; - } +simdjson_inline simdjson_result value::get_double_in_string() noexcept { + return iter.get_double_in_string(); } -simdjson_inline simdjson_result document::get_array() & noexcept { - auto value = get_root_value_iterator(); - return array::start_root(value); +simdjson_inline simdjson_result value::get_uint64() noexcept { + return iter.get_uint64(); } -simdjson_inline simdjson_result document::get_object() & noexcept { - auto value = get_root_value_iterator(); - return object::start_root(value); +simdjson_inline simdjson_result value::get_uint64_in_string() noexcept { + return iter.get_uint64_in_string(); } - -/** - * We decided that calling 'get_double()' on the JSON document '1.233 blabla' should - * give an error, so we check for trailing content. We want to disallow trailing - * content. - * Thus, in several implementations below, we pass a 'true' parameter value to - * a get_root_value_iterator() method: this indicates that we disallow trailing content. - */ - -simdjson_inline simdjson_result document::get_uint64() noexcept { - return get_root_value_iterator().get_root_uint64(true); +simdjson_inline simdjson_result value::get_int64() noexcept { + return iter.get_int64(); } -simdjson_inline simdjson_result document::get_uint64_in_string() noexcept { - return get_root_value_iterator().get_root_uint64_in_string(true); +simdjson_inline simdjson_result value::get_int64_in_string() noexcept { + return iter.get_int64_in_string(); } -simdjson_inline simdjson_result document::get_int64() noexcept { - return get_root_value_iterator().get_root_int64(true); +simdjson_inline simdjson_result value::get_bool() noexcept { + return iter.get_bool(); } -simdjson_inline simdjson_result document::get_int64_in_string() noexcept { - return get_root_value_iterator().get_root_int64_in_string(true); +simdjson_inline simdjson_result value::is_null() noexcept { + return iter.is_null(); } -simdjson_inline simdjson_result document::get_double() noexcept { - return get_root_value_iterator().get_root_double(true); + +template<> simdjson_inline simdjson_result value::get() noexcept { return get_array(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_object(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_number(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_double(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_bool(); } + + +template<> simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); } +template<> simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); } +template<> simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); } +template<> simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); } +template<> simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); } +template<> simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); } +template<> simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); } +template<> simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); } +template<> simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); } + +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline value::operator T() noexcept(false) { + return get(); } -simdjson_inline simdjson_result document::get_double_in_string() noexcept { - return get_root_value_iterator().get_root_double_in_string(true); +simdjson_inline value::operator array() noexcept(false) { + return get_array(); } -simdjson_inline simdjson_result document::get_string(bool allow_replacement) noexcept { - return get_root_value_iterator().get_root_string(true, allow_replacement); +simdjson_inline value::operator object() noexcept(false) { + return get_object(); } -template -simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept { - return get_root_value_iterator().get_root_string(receiver, true, allow_replacement); +simdjson_inline value::operator uint64_t() noexcept(false) { + return get_uint64(); } -simdjson_inline simdjson_result document::get_wobbly_string() noexcept { - return get_root_value_iterator().get_root_wobbly_string(true); +simdjson_inline value::operator int64_t() noexcept(false) { + return get_int64(); } -simdjson_inline simdjson_result document::get_raw_json_string() noexcept { - return get_root_value_iterator().get_root_raw_json_string(true); +simdjson_inline value::operator double() noexcept(false) { + return get_double(); } -simdjson_inline simdjson_result document::get_bool() noexcept { - return get_root_value_iterator().get_root_bool(true); +simdjson_inline value::operator std::string_view() noexcept(false) { + return get_string(false); } -simdjson_inline simdjson_result document::is_null() noexcept { - return get_root_value_iterator().is_root_null(true); +simdjson_inline value::operator raw_json_string() noexcept(false) { + return get_raw_json_string(); } +simdjson_inline value::operator bool() noexcept(false) { + return get_bool(); +} +#endif -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_array(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_object(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_raw_json_string(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_string(false); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_double(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_uint64(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_int64(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_value(); } - -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_raw_json_string(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_value(); } - -template simdjson_inline error_code document::get(T &out) & noexcept { - return get().get(out); +simdjson_inline simdjson_result value::begin() & noexcept { + return get_array().begin(); } -template simdjson_deprecated simdjson_inline error_code document::get(T &out) && noexcept { - return std::forward(*this).get().get(out); +simdjson_inline simdjson_result value::end() & noexcept { + return {}; } - -#if SIMDJSON_EXCEPTIONS -template -simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get(); } -template -simdjson_inline document::operator T() & noexcept(false) { return get(); } -simdjson_inline document::operator array() & noexcept(false) { return get_array(); } -simdjson_inline document::operator object() & noexcept(false) { return get_object(); } -simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); } -simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); } -simdjson_inline document::operator double() noexcept(false) { return get_double(); } -simdjson_inline document::operator std::string_view() noexcept(false) { return get_string(false); } -simdjson_inline document::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } -simdjson_inline document::operator bool() noexcept(false) { return get_bool(); } -simdjson_inline document::operator value() noexcept(false) { return get_value(); } - -#endif -simdjson_inline simdjson_result document::count_elements() & noexcept { +simdjson_inline simdjson_result value::count_elements() & noexcept { + simdjson_result answer; auto a = get_array(); - simdjson_result answer = a.count_elements(); - /* If there was an array, we are now left pointing at its first element. */ - if(answer.error() == SUCCESS) { rewind(); } + answer = a.count_elements(); + // count_elements leaves you pointing inside the array, at the first element. + // We need to move back so that the user can create a new array (which requires that + // we point at '['). + iter.move_at_start(); return answer; } -simdjson_inline simdjson_result document::count_fields() & noexcept { +simdjson_inline simdjson_result value::count_fields() & noexcept { + simdjson_result answer; auto a = get_object(); - simdjson_result answer = a.count_fields(); - /* If there was an object, we are now left pointing at its first element. */ - if(answer.error() == SUCCESS) { rewind(); } + answer = a.count_fields(); + iter.move_at_start(); return answer; } -simdjson_inline simdjson_result document::at(size_t index) & noexcept { +simdjson_inline simdjson_result value::at(size_t index) noexcept { auto a = get_array(); return a.at(index); } -simdjson_inline simdjson_result document::begin() & noexcept { - return get_array().begin(); -} -simdjson_inline simdjson_result document::end() & noexcept { - return {}; -} -simdjson_inline simdjson_result document::find_field(std::string_view key) & noexcept { +simdjson_inline simdjson_result value::find_field(std::string_view key) noexcept { return start_or_resume_object().find_field(key); } -simdjson_inline simdjson_result document::find_field(const char *key) & noexcept { +simdjson_inline simdjson_result value::find_field(const char *key) noexcept { return start_or_resume_object().find_field(key); } -simdjson_inline simdjson_result document::find_field_unordered(std::string_view key) & noexcept { + +simdjson_inline simdjson_result value::find_field_unordered(std::string_view key) noexcept { return start_or_resume_object().find_field_unordered(key); } -simdjson_inline simdjson_result document::find_field_unordered(const char *key) & noexcept { +simdjson_inline simdjson_result value::find_field_unordered(const char *key) noexcept { return start_or_resume_object().find_field_unordered(key); } -simdjson_inline simdjson_result document::operator[](std::string_view key) & noexcept { + +simdjson_inline simdjson_result value::operator[](std::string_view key) noexcept { return start_or_resume_object()[key]; } -simdjson_inline simdjson_result document::operator[](const char *key) & noexcept { +simdjson_inline simdjson_result value::operator[](const char *key) noexcept { return start_or_resume_object()[key]; } -simdjson_inline error_code document::consume() noexcept { - auto error = iter.skip_child(0); - if(error) { iter.abandon(); } - return error; -} - -simdjson_inline simdjson_result document::raw_json() noexcept { - auto _iter = get_root_value_iterator(); - const uint8_t * starting_point{_iter.peek_start()}; - auto error = consume(); - if(error) { return error; } - // After 'consume()', we could be left pointing just beyond the document, but that - // is ok because we are not going to dereference the final pointer position, we just - // use it to compute the length in bytes. - const uint8_t * final_point{iter.unsafe_pointer()}; - return std::string_view(reinterpret_cast(starting_point), size_t(final_point - starting_point)); -} - -simdjson_inline simdjson_result document::type() noexcept { - return get_root_value_iterator().type(); +simdjson_inline simdjson_result value::type() noexcept { + return iter.type(); } -simdjson_inline simdjson_result document::is_scalar() noexcept { +simdjson_inline simdjson_result value::is_scalar() noexcept { json_type this_type; auto error = type().get(this_type); if(error) { return error; } return ! ((this_type == json_type::array) || (this_type == json_type::object)); } -simdjson_inline simdjson_result document::is_string() noexcept { +simdjson_inline simdjson_result value::is_string() noexcept { json_type this_type; auto error = type().get(this_type); if(error) { return error; } return (this_type == json_type::string); } -simdjson_inline bool document::is_negative() noexcept { - return get_root_value_iterator().is_root_negative(); + +simdjson_inline bool value::is_negative() noexcept { + return iter.is_negative(); } -simdjson_inline simdjson_result document::is_integer() noexcept { - return get_root_value_iterator().is_root_integer(true); +simdjson_inline simdjson_result value::is_integer() noexcept { + return iter.is_integer(); +} +simdjson_warn_unused simdjson_inline simdjson_result value::get_number_type() noexcept { + return iter.get_number_type(); +} +simdjson_warn_unused simdjson_inline simdjson_result value::get_number() noexcept { + return iter.get_number(); } -simdjson_inline simdjson_result document::get_number_type() noexcept { - return get_root_value_iterator().get_root_number_type(true); +simdjson_inline std::string_view value::raw_json_token() noexcept { + return std::string_view(reinterpret_cast(iter.peek_start()), iter.peek_start_length()); } -simdjson_inline simdjson_result document::get_number() noexcept { - return get_root_value_iterator().get_root_number(true); +simdjson_inline simdjson_result value::raw_json() noexcept { + json_type t; + SIMDJSON_TRY(type().get(t)); + switch (t) + { + case json_type::array: { + ondemand::array array; + SIMDJSON_TRY(get_array().get(array)); + return array.raw_json(); + } + case json_type::object: { + ondemand::object object; + SIMDJSON_TRY(get_object().get(object)); + return object.raw_json(); + } + default: + return raw_json_token(); + } } +simdjson_inline simdjson_result value::current_location() noexcept { + return iter.json_iter().current_location(); +} -simdjson_inline simdjson_result document::raw_json_token() noexcept { - auto _iter = get_root_value_iterator(); - return std::string_view(reinterpret_cast(_iter.peek_start()), _iter.peek_root_length()); +simdjson_inline int32_t value::current_depth() const noexcept{ + return iter.json_iter().depth(); } -simdjson_inline simdjson_result document::at_pointer(std::string_view json_pointer) noexcept { - rewind(); // Rewind the document each time at_pointer is called - if (json_pointer.empty()) { - return this->get_value(); +inline bool is_pointer_well_formed(std::string_view json_pointer) noexcept { + if (simdjson_unlikely(json_pointer.empty())) { // can't be + return false; + } + if (simdjson_unlikely(json_pointer[0] != '/')) { + return false; + } + size_t escape = json_pointer.find('~'); + if (escape == std::string_view::npos) { + return true; + } + if (escape == json_pointer.size() - 1) { + return false; } + if (json_pointer[escape + 1] != '0' && json_pointer[escape + 1] != '1') { + return false; + } + return true; +} + +simdjson_inline simdjson_result value::at_pointer(std::string_view json_pointer) noexcept { json_type t; SIMDJSON_TRY(type().get(t)); switch (t) @@ -47344,15 +48684,15 @@ simdjson_inline simdjson_result document::at_pointer(std::string_view jso case json_type::object: return (*this).get_object().at_pointer(json_pointer); default: + // a non-empty string can be invalid, or accessing a primitive (issue 2154) + if (is_pointer_well_formed(json_pointer)) { + return NO_SUCH_FIELD; + } return INVALID_JSON_POINTER; } } -simdjson_inline simdjson_result document::at_path(std::string_view json_path) noexcept { - rewind(); // Rewind the document each time at_pointer is called - if (json_path.empty()) { - return this->get_value(); - } +simdjson_inline simdjson_result value::at_path(std::string_view json_path) noexcept { json_type t; SIMDJSON_TRY(type().get(t)); switch (t) { @@ -47371,680 +48711,1319 @@ simdjson_inline simdjson_result document::at_path(std::string_view json_p namespace simdjson { -simdjson_inline simdjson_result::simdjson_result( - fallback::ondemand::document &&value +simdjson_inline simdjson_result::simdjson_result( + fallback::ondemand::value &&value ) noexcept : - implementation_simdjson_result_base( - std::forward(value) + implementation_simdjson_result_base( + std::forward(value) ) { } -simdjson_inline simdjson_result::simdjson_result( +simdjson_inline simdjson_result::simdjson_result( error_code error ) noexcept : - implementation_simdjson_result_base( - error - ) + implementation_simdjson_result_base(error) { } -simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { if (error()) { return error(); } return first.count_elements(); } -simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { if (error()) { return error(); } return first.count_fields(); } -simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { +simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { if (error()) { return error(); } return first.at(index); } -simdjson_inline error_code simdjson_result::rewind() noexcept { - if (error()) { return error(); } - first.rewind(); - return SUCCESS; -} -simdjson_inline simdjson_result simdjson_result::begin() & noexcept { +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { if (error()) { return error(); } return first.begin(); } -simdjson_inline simdjson_result simdjson_result::end() & noexcept { +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + if (error()) { return error(); } return {}; } -simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) noexcept { if (error()) { return error(); } - return first.find_field_unordered(key); + return first.find_field(key); } -simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) noexcept { if (error()) { return error(); } - return first.find_field_unordered(key); + return first.find_field(key); } -simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) noexcept { if (error()) { return error(); } - return first[key]; + return first.find_field_unordered(key); } -simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) noexcept { if (error()) { return error(); } - return first[key]; + return first.find_field_unordered(key); } -simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) noexcept { if (error()) { return error(); } - return first.find_field(key); + return first[key]; } -simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) noexcept { if (error()) { return error(); } - return first.find_field(key); + return first[key]; } -simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { + +simdjson_inline simdjson_result simdjson_result::get_array() noexcept { if (error()) { return error(); } return first.get_array(); } -simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { +simdjson_inline simdjson_result simdjson_result::get_object() noexcept { if (error()) { return error(); } return first.get_object(); } -simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { if (error()) { return error(); } return first.get_uint64(); } -simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { if (error()) { return error(); } return first.get_uint64_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { if (error()) { return error(); } return first.get_int64(); } -simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { if (error()) { return error(); } return first.get_int64_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_double() noexcept { +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { if (error()) { return error(); } return first.get_double(); } -simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { if (error()) { return error(); } return first.get_double_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(allow_replacement); } template -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(receiver, allow_replacement); } -simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { if (error()) { return error(); } return first.get_wobbly_string(); } -simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { if (error()) { return error(); } return first.get_raw_json_string(); } -simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { if (error()) { return error(); } return first.get_bool(); } -simdjson_inline simdjson_result simdjson_result::get_value() noexcept { - if (error()) { return error(); } - return first.get_value(); -} -simdjson_inline simdjson_result simdjson_result::is_null() noexcept { +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { if (error()) { return error(); } return first.is_null(); } -template -simdjson_inline simdjson_result simdjson_result::get() & noexcept { +template<> simdjson_inline error_code simdjson_result::get(fallback::ondemand::value &out) noexcept { if (error()) { return error(); } - return first.get(); + out = first; + return SUCCESS; } -template -simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + +template simdjson_inline simdjson_result simdjson_result::get() noexcept { if (error()) { return error(); } - return std::forward(first).get(); + return first.get(); } -template -simdjson_inline error_code simdjson_result::get(T &out) & noexcept { +template simdjson_inline error_code simdjson_result::get(T &out) noexcept { if (error()) { return error(); } return first.get(out); } -template -simdjson_inline error_code simdjson_result::get(T &out) && noexcept { - if (error()) { return error(); } - return std::forward(first).get(out); -} -template<> simdjson_inline simdjson_result simdjson_result::get() & noexcept = delete; -template<> simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { - if (error()) { return error(); } - return std::forward(first); -} -template<> simdjson_inline error_code simdjson_result::get(fallback::ondemand::document &out) & noexcept = delete; -template<> simdjson_inline error_code simdjson_result::get(fallback::ondemand::document &out) && noexcept { +template<> simdjson_inline simdjson_result simdjson_result::get() noexcept { if (error()) { return error(); } - out = std::forward(first); - return SUCCESS; + return std::move(first); } -simdjson_inline simdjson_result simdjson_result::type() noexcept { +simdjson_inline simdjson_result simdjson_result::type() noexcept { if (error()) { return error(); } return first.type(); } - -simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { if (error()) { return error(); } return first.is_scalar(); } - -simdjson_inline simdjson_result simdjson_result::is_string() noexcept { +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { if (error()) { return error(); } return first.is_string(); } - -simdjson_inline bool simdjson_result::is_negative() noexcept { +simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { if (error()) { return error(); } return first.is_negative(); } - -simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { if (error()) { return error(); } return first.is_integer(); } - -simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { if (error()) { return error(); } return first.get_number_type(); } - -simdjson_inline simdjson_result simdjson_result::get_number() noexcept { +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { if (error()) { return error(); } return first.get_number(); } - - #if SIMDJSON_EXCEPTIONS -template ::value == false>::type> -simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator fallback::ondemand::array() & noexcept(false) { +template +simdjson_inline simdjson_result::operator T() noexcept(false) { if (error()) { throw simdjson_error(error()); } - return first; + return first.get(); } -simdjson_inline simdjson_result::operator fallback::ondemand::object() & noexcept(false) { +simdjson_inline simdjson_result::operator fallback::ondemand::array() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { +simdjson_inline simdjson_result::operator fallback::ondemand::object() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator int64_t() noexcept(false) { +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator double() noexcept(false) { +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { +simdjson_inline simdjson_result::operator double() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator fallback::ondemand::raw_json_string() noexcept(false) { +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator bool() noexcept(false) { +simdjson_inline simdjson_result::operator fallback::ondemand::raw_json_string() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator fallback::ondemand::value() noexcept(false) { +simdjson_inline simdjson_result::operator bool() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } #endif - -simdjson_inline simdjson_result simdjson_result::current_location() noexcept { +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { if (error()) { return error(); } - return first.current_location(); + return first.raw_json_token(); } -simdjson_inline bool simdjson_result::at_end() const noexcept { +simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { if (error()) { return error(); } - return first.at_end(); + return first.raw_json(); } - -simdjson_inline int32_t simdjson_result::current_depth() const noexcept { +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { if (error()) { return error(); } - return first.current_depth(); + return first.current_location(); } -simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { +simdjson_inline simdjson_result simdjson_result::current_depth() const noexcept { if (error()) { return error(); } - return first.raw_json_token(); + return first.current_depth(); } -simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } +simdjson_inline simdjson_result simdjson_result::at_pointer( + std::string_view json_pointer) noexcept { + if (error()) { + return error(); + } return first.at_pointer(json_pointer); } -simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { return error(); } +simdjson_inline simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } return first.at_path(json_path); } } // namespace simdjson +#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H +/* end file simdjson/generic/ondemand/value-inl.h for fallback */ +/* including simdjson/generic/ondemand/document-inl.h for fallback: #include "simdjson/generic/ondemand/document-inl.h" */ +/* begin file simdjson/generic/ondemand/document-inl.h for fallback */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { namespace fallback { namespace ondemand { -simdjson_inline document_reference::document_reference() noexcept : doc{nullptr} {} -simdjson_inline document_reference::document_reference(document &d) noexcept : doc(&d) {} -simdjson_inline void document_reference::rewind() noexcept { doc->rewind(); } -simdjson_inline simdjson_result document_reference::get_array() & noexcept { return doc->get_array(); } -simdjson_inline simdjson_result document_reference::get_object() & noexcept { return doc->get_object(); } -/** - * The document_reference instances are used primarily/solely for streams of JSON - * documents. - * We decided that calling 'get_double()' on the JSON document '1.233 blabla' should - * give an error, so we check for trailing content. - * - * However, for streams of JSON documents, we want to be able to start from - * "321" "321" "321" - * and parse it successfully as a stream of JSON documents, calling get_uint64_in_string() - * successfully each time. - * - * To achieve this result, we pass a 'false' to a get_root_value_iterator() method: - * this indicates that we allow trailing content. - */ -simdjson_inline simdjson_result document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); } -simdjson_inline simdjson_result document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); } -simdjson_inline simdjson_result document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); } -simdjson_inline simdjson_result document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); } -simdjson_inline simdjson_result document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); } -simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } -simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } -template -simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); } -simdjson_inline simdjson_result document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } -simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } -simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } -simdjson_inline simdjson_result document_reference::get_value() noexcept { return doc->get_value(); } -simdjson_inline simdjson_result document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_array(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_object(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_raw_json_string(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_string(false); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_double(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_uint64(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_int64(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_bool(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_value(); } -#if SIMDJSON_EXCEPTIONS -template -simdjson_inline document_reference::operator T() noexcept(false) { return get(); } -simdjson_inline document_reference::operator array() & noexcept(false) { return array(*doc); } -simdjson_inline document_reference::operator object() & noexcept(false) { return object(*doc); } -simdjson_inline document_reference::operator uint64_t() noexcept(false) { return get_uint64(); } -simdjson_inline document_reference::operator int64_t() noexcept(false) { return get_int64(); } -simdjson_inline document_reference::operator double() noexcept(false) { return get_double(); } -simdjson_inline document_reference::operator std::string_view() noexcept(false) { return std::string_view(*doc); } -simdjson_inline document_reference::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } -simdjson_inline document_reference::operator bool() noexcept(false) { return get_bool(); } -simdjson_inline document_reference::operator value() noexcept(false) { return value(*doc); } -#endif -simdjson_inline simdjson_result document_reference::count_elements() & noexcept { return doc->count_elements(); } -simdjson_inline simdjson_result document_reference::count_fields() & noexcept { return doc->count_fields(); } -simdjson_inline simdjson_result document_reference::at(size_t index) & noexcept { return doc->at(index); } -simdjson_inline simdjson_result document_reference::begin() & noexcept { return doc->begin(); } -simdjson_inline simdjson_result document_reference::end() & noexcept { return doc->end(); } -simdjson_inline simdjson_result document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); } -simdjson_inline simdjson_result document_reference::find_field(const char *key) & noexcept { return doc->find_field(key); } -simdjson_inline simdjson_result document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; } -simdjson_inline simdjson_result document_reference::operator[](const char *key) & noexcept { return (*doc)[key]; } -simdjson_inline simdjson_result document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); } -simdjson_inline simdjson_result document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); } -simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } -simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } -simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } -simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } -simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } -simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } -simdjson_inline simdjson_result document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); } -simdjson_inline simdjson_result document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); } -simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } -simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } -simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } -simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } -simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} -simdjson_inline document_reference::operator document&() const noexcept { return *doc; } +simdjson_inline document::document(ondemand::json_iterator &&_iter) noexcept + : iter{std::forward(_iter)} +{ + logger::log_start_value(iter, "document"); +} -} // namespace ondemand -} // namespace fallback -} // namespace simdjson +simdjson_inline document document::start(json_iterator &&iter) noexcept { + return document(std::forward(iter)); +} +inline void document::rewind() noexcept { + iter.rewind(); +} +inline std::string document::to_debug_string() noexcept { + return iter.to_string(); +} -namespace simdjson { -simdjson_inline simdjson_result::simdjson_result(fallback::ondemand::document_reference value, error_code error) - noexcept : implementation_simdjson_result_base(std::forward(value), error) {} +inline simdjson_result document::current_location() const noexcept { + return iter.current_location(); +} +inline int32_t document::current_depth() const noexcept { + return iter.depth(); +} -simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); +inline bool document::at_end() const noexcept { + return iter.at_end(); } -simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); + + +inline bool document::is_alive() noexcept { + return iter.is_alive(); } -simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { - if (error()) { return error(); } - return first.at(index); +simdjson_inline value_iterator document::resume_value_iterator() noexcept { + return value_iterator(&iter, 1, iter.root_position()); } -simdjson_inline error_code simdjson_result::rewind() noexcept { - if (error()) { return error(); } - first.rewind(); - return SUCCESS; +simdjson_inline value_iterator document::get_root_value_iterator() noexcept { + return resume_value_iterator(); } -simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); +simdjson_inline simdjson_result document::start_or_resume_object() noexcept { + if (iter.at_root()) { + return get_object(); + } else { + return object::resume(resume_value_iterator()); + } } -simdjson_inline simdjson_result simdjson_result::end() & noexcept { - return {}; +simdjson_inline simdjson_result document::get_value() noexcept { + // Make sure we start any arrays or objects before returning, so that start_root_() + // gets called. + + // It is the convention throughout the code that the macro `SIMDJSON_DEVELOPMENT_CHECKS` determines whether + // we check for OUT_OF_ORDER_ITERATION. Proper on::demand code should never trigger this error. +#if SIMDJSON_DEVELOPMENT_CHECKS + if (!iter.at_root()) { return OUT_OF_ORDER_ITERATION; } +#endif + // assert_at_root() serves two purposes: in Debug mode, whether or not + // SIMDJSON_DEVELOPMENT_CHECKS is set or not, it checks that we are at the root of + // the document (this will typically be redundant). In release mode, it generates + // SIMDJSON_ASSUME statements to allow the compiler to make assumptions. + iter.assert_at_root(); + switch (*iter.peek()) { + case '[': { + // The following lines check that the document ends with ]. + auto value_iterator = get_root_value_iterator(); + auto error = value_iterator.check_root_array(); + if(error) { return error; } + return value(get_root_value_iterator()); + } + case '{': { + // The following lines would check that the document ends with }. + auto value_iterator = get_root_value_iterator(); + auto error = value_iterator.check_root_object(); + if(error) { return error; } + return value(get_root_value_iterator()); + } + default: + // Unfortunately, scalar documents are a special case in simdjson and they cannot + // be safely converted to value instances. + return SCALAR_DOCUMENT_AS_VALUE; + } } -simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { +simdjson_inline simdjson_result document::get_array() & noexcept { + auto value = get_root_value_iterator(); + return array::start_root(value); +} +simdjson_inline simdjson_result document::get_object() & noexcept { + auto value = get_root_value_iterator(); + return object::start_root(value); +} + +/** + * We decided that calling 'get_double()' on the JSON document '1.233 blabla' should + * give an error, so we check for trailing content. We want to disallow trailing + * content. + * Thus, in several implementations below, we pass a 'true' parameter value to + * a get_root_value_iterator() method: this indicates that we disallow trailing content. + */ + +simdjson_inline simdjson_result document::get_uint64() noexcept { + return get_root_value_iterator().get_root_uint64(true); +} +simdjson_inline simdjson_result document::get_uint64_in_string() noexcept { + return get_root_value_iterator().get_root_uint64_in_string(true); +} +simdjson_inline simdjson_result document::get_int64() noexcept { + return get_root_value_iterator().get_root_int64(true); +} +simdjson_inline simdjson_result document::get_int64_in_string() noexcept { + return get_root_value_iterator().get_root_int64_in_string(true); +} +simdjson_inline simdjson_result document::get_double() noexcept { + return get_root_value_iterator().get_root_double(true); +} +simdjson_inline simdjson_result document::get_double_in_string() noexcept { + return get_root_value_iterator().get_root_double_in_string(true); +} +simdjson_inline simdjson_result document::get_string(bool allow_replacement) noexcept { + return get_root_value_iterator().get_root_string(true, allow_replacement); +} +template +simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept { + return get_root_value_iterator().get_root_string(receiver, true, allow_replacement); +} +simdjson_inline simdjson_result document::get_wobbly_string() noexcept { + return get_root_value_iterator().get_root_wobbly_string(true); +} +simdjson_inline simdjson_result document::get_raw_json_string() noexcept { + return get_root_value_iterator().get_root_raw_json_string(true); +} +simdjson_inline simdjson_result document::get_bool() noexcept { + return get_root_value_iterator().get_root_bool(true); +} +simdjson_inline simdjson_result document::is_null() noexcept { + return get_root_value_iterator().is_root_null(true); +} + +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_array(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_object(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_double(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_value(); } + +template<> simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); } +template<> simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); } +template<> simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); } +template<> simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); } +template<> simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); } +template<> simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); } +template<> simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); } +template<> simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); } +template<> simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); } + +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_raw_json_string(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_value(); } + +#if SIMDJSON_EXCEPTIONS +template +simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get(); } +template +simdjson_inline document::operator T() & noexcept(false) { return get(); } +simdjson_inline document::operator array() & noexcept(false) { return get_array(); } +simdjson_inline document::operator object() & noexcept(false) { return get_object(); } +simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); } +simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); } +simdjson_inline document::operator double() noexcept(false) { return get_double(); } +simdjson_inline document::operator std::string_view() noexcept(false) { return get_string(false); } +simdjson_inline document::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } +simdjson_inline document::operator bool() noexcept(false) { return get_bool(); } +simdjson_inline document::operator value() noexcept(false) { return get_value(); } + +#endif +simdjson_inline simdjson_result document::count_elements() & noexcept { + auto a = get_array(); + simdjson_result answer = a.count_elements(); + /* If there was an array, we are now left pointing at its first element. */ + if(answer.error() == SUCCESS) { rewind(); } + return answer; +} +simdjson_inline simdjson_result document::count_fields() & noexcept { + auto a = get_object(); + simdjson_result answer = a.count_fields(); + /* If there was an object, we are now left pointing at its first element. */ + if(answer.error() == SUCCESS) { rewind(); } + return answer; +} +simdjson_inline simdjson_result document::at(size_t index) & noexcept { + auto a = get_array(); + return a.at(index); +} +simdjson_inline simdjson_result document::begin() & noexcept { + return get_array().begin(); +} +simdjson_inline simdjson_result document::end() & noexcept { + return {}; +} + +simdjson_inline simdjson_result document::find_field(std::string_view key) & noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result document::find_field(const char *key) & noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result document::find_field_unordered(std::string_view key) & noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result document::find_field_unordered(const char *key) & noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result document::operator[](std::string_view key) & noexcept { + return start_or_resume_object()[key]; +} +simdjson_inline simdjson_result document::operator[](const char *key) & noexcept { + return start_or_resume_object()[key]; +} + +simdjson_inline error_code document::consume() noexcept { + bool scalar = false; + auto error = is_scalar().get(scalar); + if(error) { return error; } + if(scalar) { + iter.return_current_and_advance(); + return SUCCESS; + } + error = iter.skip_child(0); + if(error) { iter.abandon(); } + return error; +} + +simdjson_inline simdjson_result document::raw_json() noexcept { + auto _iter = get_root_value_iterator(); + const uint8_t * starting_point{_iter.peek_start()}; + auto error = consume(); + if(error) { return error; } + // After 'consume()', we could be left pointing just beyond the document, but that + // is ok because we are not going to dereference the final pointer position, we just + // use it to compute the length in bytes. + const uint8_t * final_point{iter.unsafe_pointer()}; + return std::string_view(reinterpret_cast(starting_point), size_t(final_point - starting_point)); +} + +simdjson_inline simdjson_result document::type() noexcept { + return get_root_value_iterator().type(); +} + +simdjson_inline simdjson_result document::is_scalar() noexcept { + // For more speed, we could do: + // return iter.is_single_token(); + json_type this_type; + auto error = type().get(this_type); + if(error) { return error; } + return ! ((this_type == json_type::array) || (this_type == json_type::object)); +} + +simdjson_inline simdjson_result document::is_string() noexcept { + json_type this_type; + auto error = type().get(this_type); + if(error) { return error; } + return (this_type == json_type::string); +} + +simdjson_inline bool document::is_negative() noexcept { + return get_root_value_iterator().is_root_negative(); +} + +simdjson_inline simdjson_result document::is_integer() noexcept { + return get_root_value_iterator().is_root_integer(true); +} + +simdjson_inline simdjson_result document::get_number_type() noexcept { + return get_root_value_iterator().get_root_number_type(true); +} + +simdjson_inline simdjson_result document::get_number() noexcept { + return get_root_value_iterator().get_root_number(true); +} + + +simdjson_inline simdjson_result document::raw_json_token() noexcept { + auto _iter = get_root_value_iterator(); + return std::string_view(reinterpret_cast(_iter.peek_start()), _iter.peek_root_length()); +} + +simdjson_inline simdjson_result document::at_pointer(std::string_view json_pointer) noexcept { + rewind(); // Rewind the document each time at_pointer is called + if (json_pointer.empty()) { + return this->get_value(); + } + json_type t; + SIMDJSON_TRY(type().get(t)); + switch (t) + { + case json_type::array: + return (*this).get_array().at_pointer(json_pointer); + case json_type::object: + return (*this).get_object().at_pointer(json_pointer); + default: + return INVALID_JSON_POINTER; + } +} + +simdjson_inline simdjson_result document::at_path(std::string_view json_path) noexcept { + rewind(); // Rewind the document each time at_pointer is called + if (json_path.empty()) { + return this->get_value(); + } + json_type t; + SIMDJSON_TRY(type().get(t)); + switch (t) { + case json_type::array: + return (*this).get_array().at_path(json_path); + case json_type::object: + return (*this).get_object().at_path(json_path); + default: + return INVALID_JSON_POINTER; + } +} + +} // namespace ondemand +} // namespace fallback +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result( + fallback::ondemand::document &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base( + error + ) +{ +} +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline error_code simdjson_result::rewind() noexcept { + if (error()) { return error(); } + first.rewind(); + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { if (error()) { return error(); } return first.find_field_unordered(key); } -simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { if (error()) { return error(); } return first.find_field_unordered(key); } -simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { if (error()) { return error(); } return first[key]; } -simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { if (error()) { return error(); } return first[key]; } -simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { if (error()) { return error(); } return first.find_field(key); } -simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { if (error()) { return error(); } return first.find_field(key); } -simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { +simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { if (error()) { return error(); } return first.get_array(); } -simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { +simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { if (error()) { return error(); } return first.get_object(); } -simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { if (error()) { return error(); } return first.get_uint64(); } -simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { if (error()) { return error(); } return first.get_uint64_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { if (error()) { return error(); } return first.get_int64(); } -simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { if (error()) { return error(); } return first.get_int64_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_double() noexcept { +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { if (error()) { return error(); } return first.get_double(); } -simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { if (error()) { return error(); } return first.get_double_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(allow_replacement); } template -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(receiver, allow_replacement); } -simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { if (error()) { return error(); } return first.get_wobbly_string(); } -simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { if (error()) { return error(); } return first.get_raw_json_string(); } -simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { if (error()) { return error(); } return first.get_bool(); } -simdjson_inline simdjson_result simdjson_result::get_value() noexcept { +simdjson_inline simdjson_result simdjson_result::get_value() noexcept { if (error()) { return error(); } return first.get_value(); } -simdjson_inline simdjson_result simdjson_result::is_null() noexcept { +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { if (error()) { return error(); } return first.is_null(); } -simdjson_inline simdjson_result simdjson_result::type() noexcept { + +template +simdjson_inline simdjson_result simdjson_result::get() & noexcept { + if (error()) { return error(); } + return first.get(); +} +template +simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first).get(); +} +template +simdjson_inline error_code simdjson_result::get(T &out) & noexcept { + if (error()) { return error(); } + return first.get(out); +} +template +simdjson_inline error_code simdjson_result::get(T &out) && noexcept { + if (error()) { return error(); } + return std::forward(first).get(out); +} + +template<> simdjson_inline simdjson_result simdjson_result::get() & noexcept = delete; +template<> simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first); +} +template<> simdjson_inline error_code simdjson_result::get(fallback::ondemand::document &out) & noexcept = delete; +template<> simdjson_inline error_code simdjson_result::get(fallback::ondemand::document &out) && noexcept { + if (error()) { return error(); } + out = std::forward(first); + return SUCCESS; +} + +simdjson_inline simdjson_result simdjson_result::type() noexcept { if (error()) { return error(); } return first.type(); } -simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { if (error()) { return error(); } return first.is_scalar(); } -simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { if (error()) { return error(); } return first.is_string(); } -simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { + +simdjson_inline bool simdjson_result::is_negative() noexcept { if (error()) { return error(); } return first.is_negative(); } -simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { if (error()) { return error(); } return first.is_integer(); } -simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { if (error()) { return error(); } return first.get_number_type(); } -simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { if (error()) { return error(); } return first.get_number(); } + + #if SIMDJSON_EXCEPTIONS -template ::value == false>::type> -simdjson_inline simdjson_result::operator T() noexcept(false) { +template ::value == false>::type> +simdjson_inline simdjson_result::operator T() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator fallback::ondemand::array() & noexcept(false) { +simdjson_inline simdjson_result::operator fallback::ondemand::array() & noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator fallback::ondemand::object() & noexcept(false) { +simdjson_inline simdjson_result::operator fallback::ondemand::object() & noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator int64_t() noexcept(false) { +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator double() noexcept(false) { +simdjson_inline simdjson_result::operator double() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator fallback::ondemand::raw_json_string() noexcept(false) { +simdjson_inline simdjson_result::operator fallback::ondemand::raw_json_string() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator bool() noexcept(false) { +simdjson_inline simdjson_result::operator bool() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator fallback::ondemand::value() noexcept(false) { +simdjson_inline simdjson_result::operator fallback::ondemand::value() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } #endif -simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { if (error()) { return error(); } return first.current_location(); } -simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { +simdjson_inline bool simdjson_result::at_end() const noexcept { + if (error()) { return error(); } + return first.at_end(); +} + + +simdjson_inline int32_t simdjson_result::current_depth() const noexcept { + if (error()) { return error(); } + return first.current_depth(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { if (error()) { return error(); } return first.raw_json_token(); } -simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { if (error()) { return error(); } return first.at_pointer(json_pointer); } -simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { - return error(); - } +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } return first.at_path(json_path); } } // namespace simdjson -#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H -/* end file simdjson/generic/ondemand/document-inl.h for fallback */ -/* including simdjson/generic/ondemand/document_stream-inl.h for fallback: #include "simdjson/generic/ondemand/document_stream-inl.h" */ -/* begin file simdjson/generic/ondemand/document_stream-inl.h for fallback */ -#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H - -/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ -/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document_stream.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */ -/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -#include -#include namespace simdjson { namespace fallback { namespace ondemand { -#ifdef SIMDJSON_THREADS_ENABLED - -inline void stage1_worker::finish() { - // After calling "run" someone would call finish() to wait - // for the end of the processing. - // This function will wait until either the thread has done - // the processing or, else, the destructor has been called. - std::unique_lock lock(locking_mutex); - cond_var.wait(lock, [this]{return has_work == false;}); -} - -inline stage1_worker::~stage1_worker() { - // The thread may never outlive the stage1_worker instance - // and will always be stopped/joined before the stage1_worker - // instance is gone. - stop_thread(); -} - -inline void stage1_worker::start_thread() { - std::unique_lock lock(locking_mutex); - if(thread.joinable()) { - return; // This should never happen but we never want to create more than one thread. - } - thread = std::thread([this]{ - while(true) { - std::unique_lock thread_lock(locking_mutex); - // We wait for either "run" or "stop_thread" to be called. - cond_var.wait(thread_lock, [this]{return has_work || !can_work;}); - // If, for some reason, the stop_thread() method was called (i.e., the - // destructor of stage1_worker is called, then we want to immediately destroy - // the thread (and not do any more processing). - if(!can_work) { - break; - } - this->owner->stage1_thread_error = this->owner->run_stage1(*this->stage1_thread_parser, - this->_next_batch_start); - this->has_work = false; - // The condition variable call should be moved after thread_lock.unlock() for performance - // reasons but thread sanitizers may report it as a data race if we do. - // See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock - cond_var.notify_one(); // will notify "finish" - thread_lock.unlock(); - } - } - ); -} - - -inline void stage1_worker::stop_thread() { - std::unique_lock lock(locking_mutex); - // We have to make sure that all locks can be released. - can_work = false; - has_work = false; - cond_var.notify_all(); - lock.unlock(); - if(thread.joinable()) { - thread.join(); - } -} - -inline void stage1_worker::run(document_stream * ds, parser * stage1, size_t next_batch_start) { - std::unique_lock lock(locking_mutex); - owner = ds; - _next_batch_start = next_batch_start; - stage1_thread_parser = stage1; - has_work = true; - // The condition variable call should be moved after thread_lock.unlock() for performance - // reasons but thread sanitizers may report it as a data race if we do. - // See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock - cond_var.notify_one(); // will notify the thread lock that we have work - lock.unlock(); -} - -#endif // SIMDJSON_THREADS_ENABLED - -simdjson_inline document_stream::document_stream( - ondemand::parser &_parser, - const uint8_t *_buf, +simdjson_inline document_reference::document_reference() noexcept : doc{nullptr} {} +simdjson_inline document_reference::document_reference(document &d) noexcept : doc(&d) {} +simdjson_inline void document_reference::rewind() noexcept { doc->rewind(); } +simdjson_inline simdjson_result document_reference::get_array() & noexcept { return doc->get_array(); } +simdjson_inline simdjson_result document_reference::get_object() & noexcept { return doc->get_object(); } +/** + * The document_reference instances are used primarily/solely for streams of JSON + * documents. + * We decided that calling 'get_double()' on the JSON document '1.233 blabla' should + * give an error, so we check for trailing content. + * + * However, for streams of JSON documents, we want to be able to start from + * "321" "321" "321" + * and parse it successfully as a stream of JSON documents, calling get_uint64_in_string() + * successfully each time. + * + * To achieve this result, we pass a 'false' to a get_root_value_iterator() method: + * this indicates that we allow trailing content. + */ +simdjson_inline simdjson_result document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); } +simdjson_inline simdjson_result document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); } +simdjson_inline simdjson_result document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); } +simdjson_inline simdjson_result document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); } +simdjson_inline simdjson_result document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); } +simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } +simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } +template +simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); } +simdjson_inline simdjson_result document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } +simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } +simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } +simdjson_inline simdjson_result document_reference::get_value() noexcept { return doc->get_value(); } +simdjson_inline simdjson_result document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_array(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_object(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_double(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_bool(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_value(); } +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline document_reference::operator T() noexcept(false) { return get(); } +simdjson_inline document_reference::operator array() & noexcept(false) { return array(*doc); } +simdjson_inline document_reference::operator object() & noexcept(false) { return object(*doc); } +simdjson_inline document_reference::operator uint64_t() noexcept(false) { return get_uint64(); } +simdjson_inline document_reference::operator int64_t() noexcept(false) { return get_int64(); } +simdjson_inline document_reference::operator double() noexcept(false) { return get_double(); } +simdjson_inline document_reference::operator std::string_view() noexcept(false) { return std::string_view(*doc); } +simdjson_inline document_reference::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } +simdjson_inline document_reference::operator bool() noexcept(false) { return get_bool(); } +simdjson_inline document_reference::operator value() noexcept(false) { return value(*doc); } +#endif +simdjson_inline simdjson_result document_reference::count_elements() & noexcept { return doc->count_elements(); } +simdjson_inline simdjson_result document_reference::count_fields() & noexcept { return doc->count_fields(); } +simdjson_inline simdjson_result document_reference::at(size_t index) & noexcept { return doc->at(index); } +simdjson_inline simdjson_result document_reference::begin() & noexcept { return doc->begin(); } +simdjson_inline simdjson_result document_reference::end() & noexcept { return doc->end(); } +simdjson_inline simdjson_result document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); } +simdjson_inline simdjson_result document_reference::find_field(const char *key) & noexcept { return doc->find_field(key); } +simdjson_inline simdjson_result document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; } +simdjson_inline simdjson_result document_reference::operator[](const char *key) & noexcept { return (*doc)[key]; } +simdjson_inline simdjson_result document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); } +simdjson_inline simdjson_result document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); } +simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } +simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } +simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } +simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } +simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } +simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } +simdjson_inline simdjson_result document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); } +simdjson_inline simdjson_result document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); } +simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } +simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } +simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } +simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } +simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} +simdjson_inline document_reference::operator document&() const noexcept { return *doc; } + +} // namespace ondemand +} // namespace fallback +} // namespace simdjson + + + +namespace simdjson { +simdjson_inline simdjson_result::simdjson_result(fallback::ondemand::document_reference value, error_code error) + noexcept : implementation_simdjson_result_base(std::forward(value), error) {} + + +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline error_code simdjson_result::rewind() noexcept { + if (error()) { return error(); } + first.rewind(); + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { + if (error()) { return error(); } + return first.get_array(); +} +simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { + if (error()) { return error(); } + return first.get_object(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { + if (error()) { return error(); } + return first.get_uint64(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { + if (error()) { return error(); } + return first.get_uint64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { + if (error()) { return error(); } + return first.get_int64(); +} +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { + if (error()) { return error(); } + return first.get_int64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { + if (error()) { return error(); } + return first.get_double(); +} +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { + if (error()) { return error(); } + return first.get_double_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(allow_replacement); +} +template +simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(receiver, allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { + if (error()) { return error(); } + return first.get_wobbly_string(); +} +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { + if (error()) { return error(); } + return first.get_raw_json_string(); +} +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { + if (error()) { return error(); } + return first.get_bool(); +} +simdjson_inline simdjson_result simdjson_result::get_value() noexcept { + if (error()) { return error(); } + return first.get_value(); +} +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { + if (error()) { return error(); } + return first.is_null(); +} +template +simdjson_inline simdjson_result simdjson_result::get() & noexcept { + if (error()) { return error(); } + return first.get(); +} +template +simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first).get(); +} +template +simdjson_inline error_code simdjson_result::get(T &out) & noexcept { + if (error()) { return error(); } + return first.get(out); +} +template +simdjson_inline error_code simdjson_result::get(T &out) && noexcept { + if (error()) { return error(); } + return std::forward(first).get(out); +} +simdjson_inline simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); +} +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + if (error()) { return error(); } + return first.is_scalar(); +} +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} +template <> +simdjson_inline error_code simdjson_result::get(fallback::ondemand::document_reference &out) & noexcept { + if (error()) { return error(); } + out = first; + return SUCCESS; +} +template <> +simdjson_inline error_code simdjson_result::get(fallback::ondemand::document_reference &out) && noexcept { + if (error()) { return error(); } + out = first; + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { + if (error()) { return error(); } + return first.is_negative(); +} +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + if (error()) { return error(); } + return first.is_integer(); +} +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + if (error()) { return error(); } + return first.get_number_type(); +} +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + if (error()) { return error(); } + return first.get_number(); +} +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline simdjson_result::operator T() noexcept(false) { + static_assert(std::is_same::value == false, "You should not call get when T is a document"); + static_assert(std::is_same::value == false, "You should not call get when T is a document"); + if (error()) { throw simdjson_error(error()); } + return first.get(); +} +simdjson_inline simdjson_result::operator fallback::ondemand::array() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator fallback::ondemand::object() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator double() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator fallback::ondemand::raw_json_string() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator bool() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator fallback::ondemand::value() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +#endif + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} + +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H +/* end file simdjson/generic/ondemand/document-inl.h for fallback */ +/* including simdjson/generic/ondemand/document_stream-inl.h for fallback: #include "simdjson/generic/ondemand/document_stream-inl.h" */ +/* begin file simdjson/generic/ondemand/document_stream-inl.h for fallback */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document_stream.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +#include + +namespace simdjson { +namespace fallback { +namespace ondemand { + +#ifdef SIMDJSON_THREADS_ENABLED + +inline void stage1_worker::finish() { + // After calling "run" someone would call finish() to wait + // for the end of the processing. + // This function will wait until either the thread has done + // the processing or, else, the destructor has been called. + std::unique_lock lock(locking_mutex); + cond_var.wait(lock, [this]{return has_work == false;}); +} + +inline stage1_worker::~stage1_worker() { + // The thread may never outlive the stage1_worker instance + // and will always be stopped/joined before the stage1_worker + // instance is gone. + stop_thread(); +} + +inline void stage1_worker::start_thread() { + std::unique_lock lock(locking_mutex); + if(thread.joinable()) { + return; // This should never happen but we never want to create more than one thread. + } + thread = std::thread([this]{ + while(true) { + std::unique_lock thread_lock(locking_mutex); + // We wait for either "run" or "stop_thread" to be called. + cond_var.wait(thread_lock, [this]{return has_work || !can_work;}); + // If, for some reason, the stop_thread() method was called (i.e., the + // destructor of stage1_worker is called, then we want to immediately destroy + // the thread (and not do any more processing). + if(!can_work) { + break; + } + this->owner->stage1_thread_error = this->owner->run_stage1(*this->stage1_thread_parser, + this->_next_batch_start); + this->has_work = false; + // The condition variable call should be moved after thread_lock.unlock() for performance + // reasons but thread sanitizers may report it as a data race if we do. + // See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock + cond_var.notify_one(); // will notify "finish" + thread_lock.unlock(); + } + } + ); +} + + +inline void stage1_worker::stop_thread() { + std::unique_lock lock(locking_mutex); + // We have to make sure that all locks can be released. + can_work = false; + has_work = false; + cond_var.notify_all(); + lock.unlock(); + if(thread.joinable()) { + thread.join(); + } +} + +inline void stage1_worker::run(document_stream * ds, parser * stage1, size_t next_batch_start) { + std::unique_lock lock(locking_mutex); + owner = ds; + _next_batch_start = next_batch_start; + stage1_thread_parser = stage1; + has_work = true; + // The condition variable call should be moved after thread_lock.unlock() for performance + // reasons but thread sanitizers may report it as a data race if we do. + // See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock + cond_var.notify_one(); // will notify the thread lock that we have work + lock.unlock(); +} + +#endif // SIMDJSON_THREADS_ENABLED + +simdjson_inline document_stream::document_stream( + ondemand::parser &_parser, + const uint8_t *_buf, size_t _len, size_t _batch_size, bool _allow_comma_separated @@ -48104,7 +50083,6 @@ simdjson_inline document_stream::iterator::iterator(document_stream* _stream, bo } simdjson_inline simdjson_result document_stream::iterator::operator*() noexcept { - //if(stream->error) { return stream->error; } return simdjson_result(stream->doc, stream->error); } @@ -49963,36 +51941,39 @@ simdjson_inline const char * raw_json_string::raw() const noexcept { return rein simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(std::string_view target) noexcept { size_t pos{0}; - // if the content has no escape character, just scan through it quickly! - for(;pos < target.size() && target[pos] != '\\';pos++) {} - // slow path may begin. - bool escaping{false}; - for(;pos < target.size();pos++) { - if((target[pos] == '"') && !escaping) { - return false; - } else if(target[pos] == '\\') { - escaping = !escaping; - } else { - escaping = false; + while(pos < target.size()) { + pos = target.find('"', pos); + if(pos == std::string_view::npos) { return true; } + if(pos != 0 && target[pos-1] != '\\') { return false; } + if(pos > 1 && target[pos-2] == '\\') { + size_t backslash_count{2}; + for(size_t i = 3; i <= pos; i++) { + if(target[pos-i] == '\\') { backslash_count++; } + else { break; } + } + if(backslash_count % 2 == 0) { return false; } } + pos++; } return true; } simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(const char* target) noexcept { size_t pos{0}; - // if the content has no escape character, just scan through it quickly! - for(;target[pos] && target[pos] != '\\';pos++) {} - // slow path may begin. - bool escaping{false}; - for(;target[pos];pos++) { - if((target[pos] == '"') && !escaping) { - return false; - } else if(target[pos] == '\\') { - escaping = !escaping; - } else { - escaping = false; + while(target[pos]) { + const char * result = strchr(target+pos, '"'); + if(result == nullptr) { return true; } + pos = result - target; + if(pos != 0 && target[pos-1] != '\\') { return false; } + if(pos > 1 && target[pos-2] == '\\') { + size_t backslash_count{2}; + for(size_t i = 3; i <= pos; i++) { + if(target[pos-i] == '\\') { backslash_count++; } + else { break; } + } + if(backslash_count % 2 == 0) { return false; } } + pos++; } return true; } @@ -50004,7 +51985,7 @@ simdjson_inline bool raw_json_string::unsafe_is_equal(size_t length, std::string } simdjson_inline bool raw_json_string::unsafe_is_equal(std::string_view target) const noexcept { - // Assumptions: does not contain unescaped quote characters, and + // Assumptions: does not contain unescaped quote characters("), and // the raw content is quote terminated within a valid JSON string. if(target.size() <= SIMDJSON_PADDING) { return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size()); @@ -50478,775 +52459,230 @@ simdjson_inline simdjson_result::simdjson_re #endif // SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H /* end file simdjson/generic/ondemand/token_iterator-inl.h for fallback */ -/* including simdjson/generic/ondemand/value-inl.h for fallback: #include "simdjson/generic/ondemand/value-inl.h" */ -/* begin file simdjson/generic/ondemand/value-inl.h for fallback */ -#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H +/* including simdjson/generic/ondemand/value_iterator-inl.h for fallback: #include "simdjson/generic/ondemand/value_iterator-inl.h" */ +/* begin file simdjson/generic/ondemand/value_iterator-inl.h for fallback */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ -/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/atomparsing.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/numberparsing.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string-inl.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { namespace fallback { namespace ondemand { -simdjson_inline value::value(const value_iterator &_iter) noexcept - : iter{_iter} +simdjson_inline value_iterator::value_iterator( + json_iterator *json_iter, + depth_t depth, + token_position start_position +) noexcept : _json_iter{json_iter}, _depth{depth}, _start_position{start_position} { } -simdjson_inline value value::start(const value_iterator &iter) noexcept { - return iter; -} -simdjson_inline value value::resume(const value_iterator &iter) noexcept { - return iter; -} - -simdjson_inline simdjson_result value::get_array() noexcept { - return array::start(iter); -} -simdjson_inline simdjson_result value::get_object() noexcept { - return object::start(iter); -} -simdjson_inline simdjson_result value::start_or_resume_object() noexcept { - if (iter.at_start()) { - return get_object(); - } else { - return object::resume(iter); - } -} -simdjson_inline simdjson_result value::get_raw_json_string() noexcept { - return iter.get_raw_json_string(); -} -simdjson_inline simdjson_result value::get_string(bool allow_replacement) noexcept { - return iter.get_string(allow_replacement); -} -template -simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept { - return iter.get_string(receiver, allow_replacement); -} -simdjson_inline simdjson_result value::get_wobbly_string() noexcept { - return iter.get_wobbly_string(); -} -simdjson_inline simdjson_result value::get_double() noexcept { - return iter.get_double(); -} -simdjson_inline simdjson_result value::get_double_in_string() noexcept { - return iter.get_double_in_string(); -} -simdjson_inline simdjson_result value::get_uint64() noexcept { - return iter.get_uint64(); -} -simdjson_inline simdjson_result value::get_uint64_in_string() noexcept { - return iter.get_uint64_in_string(); -} -simdjson_inline simdjson_result value::get_int64() noexcept { - return iter.get_int64(); -} -simdjson_inline simdjson_result value::get_int64_in_string() noexcept { - return iter.get_int64_in_string(); -} -simdjson_inline simdjson_result value::get_bool() noexcept { - return iter.get_bool(); -} -simdjson_inline simdjson_result value::is_null() noexcept { - return iter.is_null(); +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_object() noexcept { + SIMDJSON_TRY( start_container('{', "Not an object", "object") ); + return started_object(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_array(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_object(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_raw_json_string(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_string(false); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_number(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_double(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_uint64(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_int64(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_bool(); } -template simdjson_inline error_code value::get(T &out) noexcept { - return get().get(out); +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_root_object() noexcept { + SIMDJSON_TRY( start_container('{', "Not an object", "object") ); + return started_root_object(); } -#if SIMDJSON_EXCEPTIONS -template -simdjson_inline value::operator T() noexcept(false) { - return get(); -} -simdjson_inline value::operator array() noexcept(false) { - return get_array(); -} -simdjson_inline value::operator object() noexcept(false) { - return get_object(); -} -simdjson_inline value::operator uint64_t() noexcept(false) { - return get_uint64(); -} -simdjson_inline value::operator int64_t() noexcept(false) { - return get_int64(); -} -simdjson_inline value::operator double() noexcept(false) { - return get_double(); -} -simdjson_inline value::operator std::string_view() noexcept(false) { - return get_string(false); -} -simdjson_inline value::operator raw_json_string() noexcept(false) { - return get_raw_json_string(); -} -simdjson_inline value::operator bool() noexcept(false) { - return get_bool(); -} +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::started_object() noexcept { + assert_at_container_start(); +#if SIMDJSON_DEVELOPMENT_CHECKS + _json_iter->set_start_position(_depth, start_position()); #endif - -simdjson_inline simdjson_result value::begin() & noexcept { - return get_array().begin(); -} -simdjson_inline simdjson_result value::end() & noexcept { - return {}; -} -simdjson_inline simdjson_result value::count_elements() & noexcept { - simdjson_result answer; - auto a = get_array(); - answer = a.count_elements(); - // count_elements leaves you pointing inside the array, at the first element. - // We need to move back so that the user can create a new array (which requires that - // we point at '['). - iter.move_at_start(); - return answer; -} -simdjson_inline simdjson_result value::count_fields() & noexcept { - simdjson_result answer; - auto a = get_object(); - answer = a.count_fields(); - iter.move_at_start(); - return answer; -} -simdjson_inline simdjson_result value::at(size_t index) noexcept { - auto a = get_array(); - return a.at(index); -} - -simdjson_inline simdjson_result value::find_field(std::string_view key) noexcept { - return start_or_resume_object().find_field(key); -} -simdjson_inline simdjson_result value::find_field(const char *key) noexcept { - return start_or_resume_object().find_field(key); -} - -simdjson_inline simdjson_result value::find_field_unordered(std::string_view key) noexcept { - return start_or_resume_object().find_field_unordered(key); -} -simdjson_inline simdjson_result value::find_field_unordered(const char *key) noexcept { - return start_or_resume_object().find_field_unordered(key); -} - -simdjson_inline simdjson_result value::operator[](std::string_view key) noexcept { - return start_or_resume_object()[key]; -} -simdjson_inline simdjson_result value::operator[](const char *key) noexcept { - return start_or_resume_object()[key]; -} - -simdjson_inline simdjson_result value::type() noexcept { - return iter.type(); + if (*_json_iter->peek() == '}') { + logger::log_value(*_json_iter, "empty object"); + _json_iter->return_current_and_advance(); + end_container(); + return false; + } + return true; } -simdjson_inline simdjson_result value::is_scalar() noexcept { - json_type this_type; - auto error = type().get(this_type); - if(error) { return error; } - return ! ((this_type == json_type::array) || (this_type == json_type::object)); +simdjson_warn_unused simdjson_inline error_code value_iterator::check_root_object() noexcept { + // When in streaming mode, we cannot expect peek_last() to be the last structural element of the + // current document. It only works in the normal mode where we have indexed a single document. + // Note that adding a check for 'streaming' is not expensive since we only have at most + // one root element. + if ( ! _json_iter->streaming() ) { + // The following lines do not fully protect against garbage content within the + // object: e.g., `{"a":2} foo }`. Users concerned with garbage content should + // call `at_end()` on the document instance at the end of the processing to + // ensure that the processing has finished at the end. + // + if (*_json_iter->peek_last() != '}') { + _json_iter->abandon(); + return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing } at end"); + } + // If the last character is } *and* the first gibberish character is also '}' + // then on-demand could accidentally go over. So we need additional checks. + // https://github.com/simdjson/simdjson/issues/1834 + // Checking that the document is balanced requires a full scan which is potentially + // expensive, but it only happens in edge cases where the first padding character is + // a closing bracket. + if ((*_json_iter->peek(_json_iter->end_position()) == '}') && (!_json_iter->balanced())) { + _json_iter->abandon(); + // The exact error would require more work. It will typically be an unclosed object. + return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "the document is unbalanced"); + } + } + return SUCCESS; } -simdjson_inline simdjson_result value::is_string() noexcept { - json_type this_type; - auto error = type().get(this_type); +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::started_root_object() noexcept { + auto error = check_root_object(); if(error) { return error; } - return (this_type == json_type::string); -} - - -simdjson_inline bool value::is_negative() noexcept { - return iter.is_negative(); + return started_object(); } -simdjson_inline simdjson_result value::is_integer() noexcept { - return iter.is_integer(); -} -simdjson_warn_unused simdjson_inline simdjson_result value::get_number_type() noexcept { - return iter.get_number_type(); -} -simdjson_warn_unused simdjson_inline simdjson_result value::get_number() noexcept { - return iter.get_number(); +simdjson_warn_unused simdjson_inline error_code value_iterator::end_container() noexcept { +#if SIMDJSON_CHECK_EOF + if (depth() > 1 && at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing parent ] or }"); } + // if (depth() <= 1 && !at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing [ or { at start"); } +#endif // SIMDJSON_CHECK_EOF + _json_iter->ascend_to(depth()-1); + return SUCCESS; } -simdjson_inline std::string_view value::raw_json_token() noexcept { - return std::string_view(reinterpret_cast(iter.peek_start()), iter.peek_start_length()); -} +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::has_next_field() noexcept { + assert_at_next(); -simdjson_inline simdjson_result value::raw_json() noexcept { - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) - { - case json_type::array: { - ondemand::array array; - SIMDJSON_TRY(get_array().get(array)); - return array.raw_json(); - } - case json_type::object: { - ondemand::object object; - SIMDJSON_TRY(get_object().get(object)); - return object.raw_json(); - } + // It's illegal to call this unless there are more tokens: anything that ends in } or ] is + // obligated to verify there are more tokens if they are not the top level. + switch (*_json_iter->return_current_and_advance()) { + case '}': + logger::log_end_value(*_json_iter, "object"); + SIMDJSON_TRY( end_container() ); + return false; + case ',': + return true; default: - return raw_json_token(); + return report_error(TAPE_ERROR, "Missing comma between object fields"); } } -simdjson_inline simdjson_result value::current_location() noexcept { - return iter.json_iter().current_location(); -} - -simdjson_inline int32_t value::current_depth() const noexcept{ - return iter.json_iter().depth(); -} +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::find_field_raw(const std::string_view key) noexcept { + error_code error; + bool has_value; + // + // Initially, the object can be in one of a few different places: + // + // 1. The start of the object, at the first field: + // + // ``` + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 2, index 1) + // ``` + if (at_first_field()) { + has_value = true; -inline bool is_pointer_well_formed(std::string_view json_pointer) noexcept { - if (simdjson_unlikely(json_pointer.empty())) { // can't be - return false; - } - if (simdjson_unlikely(json_pointer[0] != '/')) { - return false; - } - size_t escape = json_pointer.find('~'); - if (escape == std::string_view::npos) { - return true; - } - if (escape == json_pointer.size() - 1) { - return false; - } - if (json_pointer[escape + 1] != '0' && json_pointer[escape + 1] != '1') { + // + // 2. When a previous search did not yield a value or the object is empty: + // + // ``` + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 0) + // { } + // ^ (depth 0, index 2) + // ``` + // + } else if (!is_open()) { +#if SIMDJSON_DEVELOPMENT_CHECKS + // If we're past the end of the object, we're being iterated out of order. + // Note: this is not perfect detection. It's possible the user is inside some other object; if so, + // this object iterator will blithely scan that object for fields. + if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; } +#endif return false; - } - return true; -} -simdjson_inline simdjson_result value::at_pointer(std::string_view json_pointer) noexcept { - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) - { - case json_type::array: - return (*this).get_array().at_pointer(json_pointer); - case json_type::object: - return (*this).get_object().at_pointer(json_pointer); - default: - // a non-empty string can be invalid, or accessing a primitive (issue 2154) - if (is_pointer_well_formed(json_pointer)) { - return NO_SUCH_FIELD; - } - return INVALID_JSON_POINTER; + // 3. When a previous search found a field or an iterator yielded a value: + // + // ``` + // // When a field was not fully consumed (or not even touched at all) + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 2) + // // When a field was fully consumed + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 1) + // // When the last field was fully consumed + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 1) + // ``` + // + } else { + if ((error = skip_child() )) { abandon(); return error; } + if ((error = has_next_field().get(has_value) )) { abandon(); return error; } +#if SIMDJSON_DEVELOPMENT_CHECKS + if (_json_iter->start_position(_depth) != start_position()) { return OUT_OF_ORDER_ITERATION; } +#endif } -} + while (has_value) { + // Get the key and colon, stopping at the value. + raw_json_string actual_key; + // size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes + // Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2. + // field_key() advances the pointer and checks that '"' is found (corresponding to a key). + // The depth is left unchanged by field_key(). + if ((error = field_key().get(actual_key) )) { abandon(); return error; }; + // field_value() will advance and check that we find a ':' separating the + // key and the value. It will also increment the depth by one. + if ((error = field_value() )) { abandon(); return error; } + // If it matches, stop and return + // We could do it this way if we wanted to allow arbitrary + // key content (including escaped quotes). + //if (actual_key.unsafe_is_equal(max_key_length, key)) { + // Instead we do the following which may trigger buffer overruns if the + // user provides an adversarial key (containing a well placed unescaped quote + // character and being longer than the number of bytes remaining in the JSON + // input). + if (actual_key.unsafe_is_equal(key)) { + logger::log_event(*this, "match", key, -2); + // If we return here, then we return while pointing at the ':' that we just checked. + return true; + } -simdjson_inline simdjson_result value::at_path(std::string_view json_path) noexcept { - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) { - case json_type::array: - return (*this).get_array().at_path(json_path); - case json_type::object: - return (*this).get_object().at_path(json_path); - default: - return INVALID_JSON_POINTER; + // No match: skip the value and see if , or } is next + logger::log_event(*this, "no match", key, -2); + // The call to skip_child is meant to skip over the value corresponding to the key. + // After skip_child(), we are right before the next comma (',') or the final brace ('}'). + SIMDJSON_TRY( skip_child() ); // Skip the value entirely + // The has_next_field() advances the pointer and check that either ',' or '}' is found. + // It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found, + // then we are in error and we abort. + if ((error = has_next_field().get(has_value) )) { abandon(); return error; } } -} - -} // namespace ondemand -} // namespace fallback -} // namespace simdjson - -namespace simdjson { -simdjson_inline simdjson_result::simdjson_result( - fallback::ondemand::value &&value -) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) -{ -} -simdjson_inline simdjson_result::simdjson_result( - error_code error -) noexcept : - implementation_simdjson_result_base(error) -{ -} -simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); -} -simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); -} -simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { - if (error()) { return error(); } - return first.at(index); -} -simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); -} -simdjson_inline simdjson_result simdjson_result::end() & noexcept { - if (error()) { return error(); } - return {}; + // If the loop ended, we're out of fields to look at. + return false; } -simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) noexcept { - if (error()) { return error(); } - return first.find_field(key); -} -simdjson_inline simdjson_result simdjson_result::find_field(const char *key) noexcept { - if (error()) { return error(); } - return first.find_field(key); -} +SIMDJSON_PUSH_DISABLE_WARNINGS +SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::find_field_unordered_raw(const std::string_view key) noexcept { + /** + * When find_field_unordered_raw is called, we can either be pointing at the + * first key, pointing outside (at the closing brace) or if a key was matched + * we can be either pointing right afterthe ':' right before the value (that we need skip), + * or we may have consumed the value and we might be at a comma or at the + * final brace (ready for a call to has_next_field()). + */ + error_code error; + bool has_value; -simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); -} -simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); -} - -simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) noexcept { - if (error()) { return error(); } - return first[key]; -} -simdjson_inline simdjson_result simdjson_result::operator[](const char *key) noexcept { - if (error()) { return error(); } - return first[key]; -} - -simdjson_inline simdjson_result simdjson_result::get_array() noexcept { - if (error()) { return error(); } - return first.get_array(); -} -simdjson_inline simdjson_result simdjson_result::get_object() noexcept { - if (error()) { return error(); } - return first.get_object(); -} -simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { - if (error()) { return error(); } - return first.get_uint64(); -} -simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { - if (error()) { return error(); } - return first.get_uint64_in_string(); -} -simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { - if (error()) { return error(); } - return first.get_int64(); -} -simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { - if (error()) { return error(); } - return first.get_int64_in_string(); -} -simdjson_inline simdjson_result simdjson_result::get_double() noexcept { - if (error()) { return error(); } - return first.get_double(); -} -simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { - if (error()) { return error(); } - return first.get_double_in_string(); -} -simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(allow_replacement); -} -template -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(receiver, allow_replacement); -} -simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { - if (error()) { return error(); } - return first.get_wobbly_string(); -} -simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { - if (error()) { return error(); } - return first.get_raw_json_string(); -} -simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { - if (error()) { return error(); } - return first.get_bool(); -} -simdjson_inline simdjson_result simdjson_result::is_null() noexcept { - if (error()) { return error(); } - return first.is_null(); -} - -template simdjson_inline simdjson_result simdjson_result::get() noexcept { - if (error()) { return error(); } - return first.get(); -} -template simdjson_inline error_code simdjson_result::get(T &out) noexcept { - if (error()) { return error(); } - return first.get(out); -} - -template<> simdjson_inline simdjson_result simdjson_result::get() noexcept { - if (error()) { return error(); } - return std::move(first); -} -template<> simdjson_inline error_code simdjson_result::get(fallback::ondemand::value &out) noexcept { - if (error()) { return error(); } - out = first; - return SUCCESS; -} - -simdjson_inline simdjson_result simdjson_result::type() noexcept { - if (error()) { return error(); } - return first.type(); -} -simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { - if (error()) { return error(); } - return first.is_scalar(); -} -simdjson_inline simdjson_result simdjson_result::is_string() noexcept { - if (error()) { return error(); } - return first.is_string(); -} -simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { - if (error()) { return error(); } - return first.is_negative(); -} -simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { - if (error()) { return error(); } - return first.is_integer(); -} -simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { - if (error()) { return error(); } - return first.get_number_type(); -} -simdjson_inline simdjson_result simdjson_result::get_number() noexcept { - if (error()) { return error(); } - return first.get_number(); -} -#if SIMDJSON_EXCEPTIONS -template -simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return static_cast(first); -} -simdjson_inline simdjson_result::operator fallback::ondemand::array() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator fallback::ondemand::object() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator int64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator double() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator fallback::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator bool() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -#endif - -simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { - if (error()) { return error(); } - return first.raw_json_token(); -} - -simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { - if (error()) { return error(); } - return first.raw_json(); -} - -simdjson_inline simdjson_result simdjson_result::current_location() noexcept { - if (error()) { return error(); } - return first.current_location(); -} - -simdjson_inline simdjson_result simdjson_result::current_depth() const noexcept { - if (error()) { return error(); } - return first.current_depth(); -} - -simdjson_inline simdjson_result simdjson_result::at_pointer( - std::string_view json_pointer) noexcept { - if (error()) { - return error(); - } - return first.at_pointer(json_pointer); -} - -simdjson_inline simdjson_result simdjson_result::at_path( - std::string_view json_path) noexcept { - if (error()) { - return error(); - } - return first.at_path(json_path); -} - -} // namespace simdjson - -#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H -/* end file simdjson/generic/ondemand/value-inl.h for fallback */ -/* including simdjson/generic/ondemand/value_iterator-inl.h for fallback: #include "simdjson/generic/ondemand/value_iterator-inl.h" */ -/* begin file simdjson/generic/ondemand/value_iterator-inl.h for fallback */ -#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H - -/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ -/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/atomparsing.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/numberparsing.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string-inl.h" */ -/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -namespace simdjson { -namespace fallback { -namespace ondemand { - -simdjson_inline value_iterator::value_iterator( - json_iterator *json_iter, - depth_t depth, - token_position start_position -) noexcept : _json_iter{json_iter}, _depth{depth}, _start_position{start_position} -{ -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_object() noexcept { - SIMDJSON_TRY( start_container('{', "Not an object", "object") ); - return started_object(); -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_root_object() noexcept { - SIMDJSON_TRY( start_container('{', "Not an object", "object") ); - return started_root_object(); -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::started_object() noexcept { - assert_at_container_start(); -#if SIMDJSON_DEVELOPMENT_CHECKS - _json_iter->set_start_position(_depth, start_position()); -#endif - if (*_json_iter->peek() == '}') { - logger::log_value(*_json_iter, "empty object"); - _json_iter->return_current_and_advance(); - end_container(); - return false; - } - return true; -} - -simdjson_warn_unused simdjson_inline error_code value_iterator::check_root_object() noexcept { - // When in streaming mode, we cannot expect peek_last() to be the last structural element of the - // current document. It only works in the normal mode where we have indexed a single document. - // Note that adding a check for 'streaming' is not expensive since we only have at most - // one root element. - if ( ! _json_iter->streaming() ) { - // The following lines do not fully protect against garbage content within the - // object: e.g., `{"a":2} foo }`. Users concerned with garbage content should - // call `at_end()` on the document instance at the end of the processing to - // ensure that the processing has finished at the end. - // - if (*_json_iter->peek_last() != '}') { - _json_iter->abandon(); - return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing } at end"); - } - // If the last character is } *and* the first gibberish character is also '}' - // then on-demand could accidentally go over. So we need additional checks. - // https://github.com/simdjson/simdjson/issues/1834 - // Checking that the document is balanced requires a full scan which is potentially - // expensive, but it only happens in edge cases where the first padding character is - // a closing bracket. - if ((*_json_iter->peek(_json_iter->end_position()) == '}') && (!_json_iter->balanced())) { - _json_iter->abandon(); - // The exact error would require more work. It will typically be an unclosed object. - return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "the document is unbalanced"); - } - } - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::started_root_object() noexcept { - auto error = check_root_object(); - if(error) { return error; } - return started_object(); -} - -simdjson_warn_unused simdjson_inline error_code value_iterator::end_container() noexcept { -#if SIMDJSON_CHECK_EOF - if (depth() > 1 && at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing parent ] or }"); } - // if (depth() <= 1 && !at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing [ or { at start"); } -#endif // SIMDJSON_CHECK_EOF - _json_iter->ascend_to(depth()-1); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::has_next_field() noexcept { - assert_at_next(); - - // It's illegal to call this unless there are more tokens: anything that ends in } or ] is - // obligated to verify there are more tokens if they are not the top level. - switch (*_json_iter->return_current_and_advance()) { - case '}': - logger::log_end_value(*_json_iter, "object"); - SIMDJSON_TRY( end_container() ); - return false; - case ',': - return true; - default: - return report_error(TAPE_ERROR, "Missing comma between object fields"); - } -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::find_field_raw(const std::string_view key) noexcept { - error_code error; - bool has_value; - // - // Initially, the object can be in one of a few different places: - // - // 1. The start of the object, at the first field: - // - // ``` - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 2, index 1) - // ``` - if (at_first_field()) { - has_value = true; - - // - // 2. When a previous search did not yield a value or the object is empty: - // - // ``` - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 0) - // { } - // ^ (depth 0, index 2) - // ``` - // - } else if (!is_open()) { -#if SIMDJSON_DEVELOPMENT_CHECKS - // If we're past the end of the object, we're being iterated out of order. - // Note: this is not perfect detection. It's possible the user is inside some other object; if so, - // this object iterator will blithely scan that object for fields. - if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; } -#endif - return false; - - // 3. When a previous search found a field or an iterator yielded a value: - // - // ``` - // // When a field was not fully consumed (or not even touched at all) - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 2) - // // When a field was fully consumed - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 1) - // // When the last field was fully consumed - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 1) - // ``` - // - } else { - if ((error = skip_child() )) { abandon(); return error; } - if ((error = has_next_field().get(has_value) )) { abandon(); return error; } -#if SIMDJSON_DEVELOPMENT_CHECKS - if (_json_iter->start_position(_depth) != start_position()) { return OUT_OF_ORDER_ITERATION; } -#endif - } - while (has_value) { - // Get the key and colon, stopping at the value. - raw_json_string actual_key; - // size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes - // Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2. - // field_key() advances the pointer and checks that '"' is found (corresponding to a key). - // The depth is left unchanged by field_key(). - if ((error = field_key().get(actual_key) )) { abandon(); return error; }; - // field_value() will advance and check that we find a ':' separating the - // key and the value. It will also increment the depth by one. - if ((error = field_value() )) { abandon(); return error; } - // If it matches, stop and return - // We could do it this way if we wanted to allow arbitrary - // key content (including escaped quotes). - //if (actual_key.unsafe_is_equal(max_key_length, key)) { - // Instead we do the following which may trigger buffer overruns if the - // user provides an adversarial key (containing a well placed unescaped quote - // character and being longer than the number of bytes remaining in the JSON - // input). - if (actual_key.unsafe_is_equal(key)) { - logger::log_event(*this, "match", key, -2); - // If we return here, then we return while pointing at the ':' that we just checked. - return true; - } - - // No match: skip the value and see if , or } is next - logger::log_event(*this, "no match", key, -2); - // The call to skip_child is meant to skip over the value corresponding to the key. - // After skip_child(), we are right before the next comma (',') or the final brace ('}'). - SIMDJSON_TRY( skip_child() ); // Skip the value entirely - // The has_next_field() advances the pointer and check that either ',' or '}' is found. - // It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found, - // then we are in error and we abort. - if ((error = has_next_field().get(has_value) )) { abandon(); return error; } - } - - // If the loop ended, we're out of fields to look at. - return false; -} - -SIMDJSON_PUSH_DISABLE_WARNINGS -SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::find_field_unordered_raw(const std::string_view key) noexcept { - /** - * When find_field_unordered_raw is called, we can either be pointing at the - * first key, pointing outside (at the closing brace) or if a key was matched - * we can be either pointing right afterthe ':' right before the value (that we need skip), - * or we may have consumed the value and we might be at a comma or at the - * final brace (ready for a call to has_next_field()). - */ - error_code error; - bool has_value; - - // First, we scan from that point to the end. - // If we don't find a match, we may loop back around, and scan from the beginning to that point. - token_position search_start = _json_iter->position(); + // First, we scan from that point to the end. + // If we don't find a match, we may loop back around, and scan from the beginning to that point. + token_position search_start = _json_iter->position(); // We want to know whether we need to go back to the beginning. bool at_first = at_first_field(); @@ -51826,6 +53262,8 @@ simdjson_inline simdjson_result value_iterator::is_root_null(bool check_tr if(result) { // we have something that looks like a null. if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; } advance_root_scalar("null"); + } else if (json[0] == 'n') { + return incorrect_type_error("Not a null but starts with n"); } return result; } @@ -52117,6 +53555,8 @@ simdjson_inline simdjson_result::simdjson_re #endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H /* end file simdjson/generic/ondemand/value_iterator-inl.h for fallback */ + + /* end file simdjson/generic/ondemand/amalgamated.h for fallback */ /* including simdjson/fallback/end.h: #include "simdjson/fallback/end.h" */ /* begin file simdjson/fallback/end.h */ @@ -52228,8 +53668,14 @@ static_assert(sizeof(__m256i) <= simdjson::SIMDJSON_PADDING, "insufficient paddi /* end file simdjson/haswell/intrinsics.h */ #if !SIMDJSON_CAN_ALWAYS_RUN_HASWELL +// We enable bmi2 only if LLVM/clang is used, because GCC may not +// make good use of it. See https://github.com/simdjson/simdjson/pull/2243 +#if defined(__clang__) +SIMDJSON_TARGET_REGION("avx2,bmi,bmi2,pclmul,lzcnt,popcnt") +#else SIMDJSON_TARGET_REGION("avx2,bmi,pclmul,lzcnt,popcnt") #endif +#endif /* including simdjson/haswell/bitmanipulation.h: #include "simdjson/haswell/bitmanipulation.h" */ /* begin file simdjson/haswell/bitmanipulation.h */ @@ -52886,6 +54332,132 @@ class value_iterator; #endif // SIMDJSON_GENERIC_ONDEMAND_BASE_H /* end file simdjson/generic/ondemand/base.h for haswell */ +/* including simdjson/generic/ondemand/deserialize.h for haswell: #include "simdjson/generic/ondemand/deserialize.h" */ +/* begin file simdjson/generic/ondemand/deserialize.h for haswell */ +#if SIMDJSON_SUPPORTS_DESERIALIZATION + +#ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_ONDEMAND_DESERIALIZE_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +namespace simdjson { + +namespace tag_invoke_fn_ns { +void tag_invoke(); + +struct tag_invoke_fn { + template + requires requires(Tag tag, Args &&...args) { + tag_invoke(std::forward(tag), std::forward(args)...); + } + constexpr auto operator()(Tag tag, Args &&...args) const + noexcept(noexcept(tag_invoke(std::forward(tag), + std::forward(args)...))) + -> decltype(tag_invoke(std::forward(tag), + std::forward(args)...)) { + return tag_invoke(std::forward(tag), std::forward(args)...); + } +}; +} // namespace tag_invoke_fn_ns + +inline namespace tag_invoke_ns { +inline constexpr tag_invoke_fn_ns::tag_invoke_fn tag_invoke = {}; +} // namespace tag_invoke_ns + +template +concept tag_invocable = requires(Tag tag, Args... args) { + tag_invoke(std::forward(tag), std::forward(args)...); +}; + +template +concept nothrow_tag_invocable = + tag_invocable && requires(Tag tag, Args... args) { + { + tag_invoke(std::forward(tag), std::forward(args)...) + } noexcept; + }; + +template +using tag_invoke_result = + std::invoke_result; + +template +using tag_invoke_result_t = + std::invoke_result_t; + +template using tag_t = std::decay_t; + + +struct deserialize_tag; + +/// These types are deserializable in a built-in way +template struct is_builtin_deserializable : std::false_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; + +template +concept is_builtin_deserializable_v = is_builtin_deserializable::value; + +template +concept custom_deserializable = tag_invocable; + +template +concept deserializable = custom_deserializable || is_builtin_deserializable_v; + +template +concept nothrow_custom_deserializable = nothrow_tag_invocable; + +// built-in types are noexcept and if an error happens, the value simply gets ignored and the error is returned. +template +concept nothrow_deserializable = nothrow_custom_deserializable || is_builtin_deserializable_v; + +/// Deserialize Tag +inline constexpr struct deserialize_tag { + using value_type = haswell::ondemand::value; + using document_type = haswell::ondemand::document; + using document_reference_type = haswell::ondemand::document_reference; + + // Customization Point for value + template + requires custom_deserializable + [[nodiscard]] constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for document + template + requires custom_deserializable + [[nodiscard]] constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for document reference + template + requires custom_deserializable + [[nodiscard]] constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + +} deserialize{}; + +} // namespace simdjson + +#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION + +/* end file simdjson/generic/ondemand/deserialize.h for haswell */ /* including simdjson/generic/ondemand/value_iterator.h for haswell: #include "simdjson/generic/ondemand/value_iterator.h" */ /* begin file simdjson/generic/ondemand/value_iterator.h for haswell */ #ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H @@ -53390,12 +54962,15 @@ struct simdjson_result : public haswell::impl /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ +#include + namespace simdjson { + namespace haswell { namespace ondemand { - /** * An ephemeral JSON value returned during iteration. It is only valid for as long as you do * not access more data in the JSON document. @@ -53420,16 +54995,21 @@ class value { * @returns A value of the given type, parsed from the JSON. * @returns INCORRECT_TYPE If the JSON value is not the given type. */ - template simdjson_inline simdjson_result get() noexcept { - // Unless the simdjson library or the user provides an inline implementation, calling this method should - // immediately fail. - static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " - "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " - "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." - " You may also add support for custom types, see our documentation."); + template + simdjson_inline simdjson_result get() +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; } + /** * Get this value as the given type. * @@ -53439,7 +55019,32 @@ class value { * @returns INCORRECT_TYPE If the JSON value is not an object. * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ - template simdjson_inline error_code get(T &out) noexcept; + template + simdjson_inline error_code get(T &out) +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_DESERIALIZATION + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION + // Unless the simdjson library or the user provides an inline implementation, calling this method should + // immediately fail. + static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " + "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " + "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." + " You may also add support for custom types, see our documentation."); + static_cast(out); // to get rid of unused errors + return UNINITIALIZED; +#if SIMDJSON_SUPPORTS_DESERIALIZATION + } +#endif + } /** * Cast this JSON value to an array. @@ -53515,6 +55120,17 @@ class value { * Important: a value should be consumed once. Calling get_string() twice on the same value * is an error. * + * In some instances, you may want to allow replacement of invalid Unicode sequences. + * You may do so by passing the allow_replacement parameter as true. In the following + * example, the string "431924697b\udff0L\u0001Y" is not valid Unicode. By passing true + * to get_string, we allow the replacement of the invalid Unicode sequences with the Unicode + * replacement character (U+FFFD). + * + * simdjson::ondemand::parser parser; + * auto json = R"({"deviceId":"431924697b\udff0L\u0001Y"})"_padded; + * simdjson::ondemand::document doc = parser.iterate(json); + * auto view = doc["deviceId"].get_string(true); + * * @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next * time it parses a document or when it is destroyed. * @returns INCORRECT_TYPE if the JSON value is not a string. @@ -53766,6 +55382,7 @@ class value { simdjson_inline simdjson_result operator[](std::string_view key) noexcept; /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ simdjson_inline simdjson_result operator[](const char *key) noexcept; + simdjson_result operator[](int) noexcept = delete; /** * Get the type of this JSON value. It does not validate or consume the value. @@ -54133,6 +55750,7 @@ struct simdjson_result : public haswell::implementatio simdjson_inline simdjson_result operator[](std::string_view key) noexcept; /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ simdjson_inline simdjson_result operator[](const char *key) noexcept; + simdjson_result operator[](int) noexcept = delete; /** * Get the type of this JSON value. @@ -55188,6 +56806,22 @@ class parser { * using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the * SIMDJSON_PADDING bytes to avoid runtime warnings. * + * ### std::string references + * + * If you pass a mutable std::string reference (std::string&), the parser will seek to extend + * its capacity to SIMDJSON_PADDING bytes beyond the end of the string. + * + * Whenever you pass an std::string reference, the parser will access the bytes beyond the end of + * the string but before the end of the allocated memory (std::string::capacity()). + * If you are using a sanitizer that checks for reading uninitialized bytes or std::string's + * container-overflow checks, you may encounter sanitizer warnings. + * You can safely ignore these warnings. Or you can call simdjson::pad(std::string&) to pad the + * string with SIMDJSON_PADDING spaces: this function returns a simdjson::padding_string_view + * which can be be passed to the parser's iterate function: + * + * std::string json = R"({ "foo": 1 } { "foo": 2 } { "foo": 3 } )"; + * document doc = parser.iterate(simdjson::pad(json)); + * * @param json The JSON to parse. * @param len The length of the JSON. * @param capacity The number of bytes allocated in the JSON (must be at least len+SIMDJSON_PADDING). @@ -55382,8 +57016,12 @@ class parser { * behavior of the parser for future operations. */ bool threaded{true}; + #else + /** + * When SIMDJSON_THREADS_ENABLED is not defined, the parser instance cannot use threads. + */ + bool threaded{false}; #endif - /** * Unescape this JSON string, replacing \\ with \, \n with newline, etc. to a user-provided buffer. * The result must be valid UTF-8. @@ -55525,7 +57163,8 @@ class array { * calling this function, if successful, the array is 'rewinded' at its * beginning as if it had never been accessed. If the JSON is malformed (e.g., * there is a missing comma), then an error is returned and it is no longer - * safe to continue. + * safe to continue. Note that count_elements() does not validate the JSON values, + * only the structure of the array. * * To check that an array is empty, it is more performant to use * the is_empty() method. @@ -55803,8 +57442,11 @@ struct simdjson_result : public haswell::impl /* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + namespace simdjson { namespace haswell { namespace ondemand { @@ -55977,24 +57619,39 @@ class document { * @returns A value of the given type, parsed from the JSON. * @returns INCORRECT_TYPE If the JSON value is not the given type. */ - template simdjson_inline simdjson_result get() & noexcept { - // Unless the simdjson library or the user provides an inline implementation, calling this method should - // immediately fail. - static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " - "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " - "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." - " You may also add support for custom types, see our documentation."); - } - /** @overload template simdjson_result get() & noexcept */ - template simdjson_deprecated simdjson_inline simdjson_result get() && noexcept { - // Unless the simdjson library or the user provides an inline implementation, calling this method should - // immediately fail. - static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " - "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " - "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." - " You may also add support for custom types, see our documentation."); + template + simdjson_inline simdjson_result get() & +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "Cannot initialize the specified type."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + /** + * @overload template simdjson_result get() & noexcept + * + * We disallow the use tag_invoke CPO on a moved document; it may create UB + * if user uses `ondemand::array` or `ondemand::object` in their custom type. + * + * The member function is still remains specialize-able for compatibility + * reasons, but we completely disallow its use when a tag_invoke customization + * is provided. + */ + template + simdjson_inline simdjson_result get() && +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(!std::is_same::value && !std::is_same::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document being alive; that would be Undefined Behaviour."); + return static_cast(*this).get(); } /** @@ -56008,7 +57665,32 @@ class document { * @returns INCORRECT_TYPE If the JSON value is not an object. * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ - template simdjson_inline error_code get(T &out) & noexcept; + template + simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_DESERIALIZATION + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION + // Unless the simdjson library or the user provides an inline implementation, calling this method should + // immediately fail. + static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " + "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " + "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." + " You may also add support for custom types, see our documentation."); + static_cast(out); // to get rid of unused errors + return UNINITIALIZED; +#if SIMDJSON_SUPPORTS_DESERIALIZATION + } +#endif + } /** @overload template error_code get(T &out) & noexcept */ template simdjson_deprecated simdjson_inline error_code get(T &out) && noexcept; @@ -56110,7 +57792,8 @@ class document { * calling this function, if successful, the array is 'rewinded' at its * beginning as if it had never been accessed. If the JSON is malformed (e.g., * there is a missing comma), then an error is returned and it is no longer - * safe to continue. + * safe to continue. Note that count_elements() does not validate the JSON values, + * only the structure of the array. */ simdjson_inline simdjson_result count_elements() & noexcept; /** @@ -56222,6 +57905,7 @@ class document { simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; /** * Get the type of this JSON value. It does not validate or consume the value. @@ -56490,6 +58174,11 @@ class document { /** * A document_reference is a thin wrapper around a document reference instance. + * The document_reference instances are used primarily/solely for streams of JSON + * documents. They differ from document instances when parsing a scalar value + * (a document that is not an array or an object). In the case of a document, + * we expect the document to be fully consumed. In the case of a document_reference, + * we allow trailing content. */ class document_reference { public: @@ -56515,7 +58204,70 @@ class document_reference { simdjson_inline simdjson_result get_value() noexcept; simdjson_inline simdjson_result is_null() noexcept; - template simdjson_inline simdjson_result get() & noexcept; + template + simdjson_inline simdjson_result get() & +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "Cannot initialize the specified type."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + template + simdjson_inline simdjson_result get() && +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(!std::is_same::value && !std::is_same::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document_reference being alive; that would be Undefined Behaviour."); + return static_cast(*this).get(); + } + + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool, value + * + * Be mindful that the document instance must remain in scope while you are accessing object, array and value instances. + * + * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. + * @returns INCORRECT_TYPE If the JSON value is not an object. + * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. + */ + template + simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_DESERIALIZATION + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION + // Unless the simdjson library or the user provides an inline implementation, calling this method should + // immediately fail. + static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " + "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " + "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." + " You may also add support for custom types, see our documentation."); + static_cast(out); // to get rid of unused errors + return UNINITIALIZED; +#if SIMDJSON_SUPPORTS_DESERIALIZATION + } +#endif + } + /** @overload template error_code get(T &out) & noexcept */ + template simdjson_inline error_code get(T &out) && noexcept; simdjson_inline simdjson_result raw_json() noexcept; simdjson_inline operator document&() const noexcept; #if SIMDJSON_EXCEPTIONS @@ -56540,6 +58292,7 @@ class document_reference { simdjson_inline simdjson_result find_field(const char *key) & noexcept; simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; @@ -56618,6 +58371,7 @@ struct simdjson_result : public haswell::implementa simdjson_inline simdjson_result find_field(const char *key) & noexcept; simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; simdjson_inline simdjson_result type() noexcept; @@ -56667,8 +58421,14 @@ struct simdjson_result : public haswell:: simdjson_inline simdjson_result get_bool() noexcept; simdjson_inline simdjson_result get_value() noexcept; simdjson_inline simdjson_result is_null() noexcept; + + template simdjson_inline simdjson_result get() & noexcept; + template simdjson_inline simdjson_result get() && noexcept; + + template simdjson_inline error_code get(T &out) & noexcept; + template simdjson_inline error_code get(T &out) && noexcept; #if SIMDJSON_EXCEPTIONS - template ::value == false>::type> + template explicit simdjson_inline operator T() noexcept(false); simdjson_inline operator haswell::ondemand::array() & noexcept(false); simdjson_inline operator haswell::ondemand::object() & noexcept(false); @@ -56689,6 +58449,7 @@ struct simdjson_result : public haswell:: simdjson_inline simdjson_result find_field(const char *key) & noexcept; simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; simdjson_inline simdjson_result type() noexcept; @@ -57619,6 +59380,177 @@ inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result +#include + +namespace simdjson { +template +constexpr bool require_custom_serialization = false; + +////////////////////////////// +// Number deserialization +////////////////////////////// + +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + using limits = std::numeric_limits; + + uint64_t x; + SIMDJSON_TRY(val.get_uint64().get(x)); + if (x > (limits::max)()) { + return NUMBER_OUT_OF_RANGE; + } + out = static_cast(x); + return SUCCESS; +} + +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + double x; + SIMDJSON_TRY(val.get_double().get(x)); + out = static_cast(x); + return SUCCESS; +} + +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + using limits = std::numeric_limits; + + int64_t x; + SIMDJSON_TRY(val.get_int64().get(x)); + if (x > (limits::max)() || x < (limits::min)()) { + return NUMBER_OUT_OF_RANGE; + } + out = static_cast(x); + return SUCCESS; +} + +/** + * STL containers have several constructors including one that takes a single + * size argument. Thus, some compilers (Visual Studio) will not be able to + * disambiguate between the size and container constructor. Users should + * explicitly specify the type of the container as needed: e.g., + * doc.get>(). + */ +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { + using value_type = typename std::remove_cvref_t::value_type; + static_assert( + deserializable, + "The specified type inside the container must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the container must default constructible."); + + haswell::ondemand::array arr; + SIMDJSON_TRY(val.get_array().get(arr)); + for (auto v : arr) { + if constexpr (concepts::returns_reference) { + if (auto const err = v.get().get(concepts::emplace_one(out)); + err) { + // If an error occurs, the empty element that we just inserted gets + // removed. We're not using a temp variable because if T is a heavy + // type, we want the valid path to be the fast path and the slow path be + // the path that has errors in it. + if constexpr (requires { out.pop_back(); }) { + static_cast(out.pop_back()); + } + return err; + } + } else { + value_type temp; + if (auto const err = v.get().get(temp); err) { + return err; + } + concepts::emplace_one(out, std::move(temp)); + } + } + return SUCCESS; +} + + + +/** + * This CPO (Customization Point Object) will help deserialize into + * smart pointers. + * + * If constructing T is nothrow, this conversion should be nothrow as well since + * we return MEMALLOC if we're not able to allocate memory instead of throwing + * the error message. + * + * @tparam T The type inside the smart pointer + * @tparam ValT document/value type + * @param val document/value + * @param out a reference to the smart pointer + * @return status of the conversion + */ +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::element_type, ValT>) { + using element_type = typename std::remove_cvref_t::element_type; + + // For better error messages, don't use these as constraints on + // the tag_invoke CPO. + static_assert( + deserializable, + "The specified type inside the unique_ptr must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the unique_ptr must default constructible."); + + auto ptr = new (std::nothrow) element_type(); + if (ptr == nullptr) { + return MEMALLOC; + } + SIMDJSON_TRY(val.template get(*ptr)); + out.reset(ptr); + return SUCCESS; +} + +/** + * This CPO (Customization Point Object) will help deserialize into optional types. + */ +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::value_type, ValT>) { + using value_type = typename std::remove_cvref_t::value_type; + + static_assert( + deserializable, + "The specified type inside the unique_ptr must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the unique_ptr must default constructible."); + + if (!out) { + out.emplace(); + } + SIMDJSON_TRY(val.template get(out.value())); + return SUCCESS; +} + +} // namespace simdjson + +#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +/* end file simdjson/generic/ondemand/std_deserialize.h for haswell */ + // Inline definitions /* including simdjson/generic/ondemand/array-inl.h for haswell: #include "simdjson/generic/ondemand/array-inl.h" */ /* begin file simdjson/generic/ondemand/array-inl.h for haswell */ @@ -57626,6 +59558,7 @@ inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result array::at_pointer(std::string_view json_pointer) n return child; } -inline std::string json_path_to_pointer_conversion(std::string_view json_path) { - if (json_path.empty() || (json_path.front() != '.' && - json_path.front() != '[')) { - return "-1"; // This is just a sentinel value, the caller should check for this and return an error. - } - - std::string result; - // Reserve space to reduce allocations, adjusting for potential increases due - // to escaping. - result.reserve(json_path.size() * 2); - - size_t i = 0; - - while (i < json_path.length()) { - if (json_path[i] == '.') { - result += '/'; - } else if (json_path[i] == '[') { - result += '/'; - ++i; // Move past the '[' - while (i < json_path.length() && json_path[i] != ']') { - if (json_path[i] == '~') { - result += "~0"; - } else if (json_path[i] == '/') { - result += "~1"; - } else { - result += json_path[i]; - } - ++i; - } - if (i == json_path.length() || json_path[i] != ']') { - return "-1"; // Using sentinel value that will be handled as an error by the caller. - } - } else { - if (json_path[i] == '~') { - result += "~0"; - } else if (json_path[i] == '/') { - result += "~1"; - } else { - result += json_path[i]; - } - } - ++i; - } - - return result; -} - inline simdjson_result array::at_path(std::string_view json_path) noexcept { auto json_pointer = json_path_to_pointer_conversion(json_path); if (json_pointer == "-1") { return INVALID_JSON_POINTER; } @@ -57987,313 +59873,276 @@ simdjson_inline simdjson_result &simdjson_res #endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H /* end file simdjson/generic/ondemand/array_iterator-inl.h for haswell */ -/* including simdjson/generic/ondemand/document-inl.h for haswell: #include "simdjson/generic/ondemand/document-inl.h" */ -/* begin file simdjson/generic/ondemand/document-inl.h for haswell */ -#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H +/* including simdjson/generic/ondemand/value-inl.h for haswell: #include "simdjson/generic/ondemand/value-inl.h" */ +/* begin file simdjson/generic/ondemand/value-inl.h for haswell */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ -/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { namespace haswell { namespace ondemand { -simdjson_inline document::document(ondemand::json_iterator &&_iter) noexcept - : iter{std::forward(_iter)} +simdjson_inline value::value(const value_iterator &_iter) noexcept + : iter{_iter} { - logger::log_start_value(iter, "document"); } - -simdjson_inline document document::start(json_iterator &&iter) noexcept { - return document(std::forward(iter)); +simdjson_inline value value::start(const value_iterator &iter) noexcept { + return iter; } - -inline void document::rewind() noexcept { - iter.rewind(); +simdjson_inline value value::resume(const value_iterator &iter) noexcept { + return iter; } -inline std::string document::to_debug_string() noexcept { - return iter.to_string(); +simdjson_inline simdjson_result value::get_array() noexcept { + return array::start(iter); } - -inline simdjson_result document::current_location() const noexcept { - return iter.current_location(); +simdjson_inline simdjson_result value::get_object() noexcept { + return object::start(iter); } - -inline int32_t document::current_depth() const noexcept { - return iter.depth(); +simdjson_inline simdjson_result value::start_or_resume_object() noexcept { + if (iter.at_start()) { + return get_object(); + } else { + return object::resume(iter); + } } -inline bool document::at_end() const noexcept { - return iter.at_end(); +simdjson_inline simdjson_result value::get_raw_json_string() noexcept { + return iter.get_raw_json_string(); } - - -inline bool document::is_alive() noexcept { - return iter.is_alive(); +simdjson_inline simdjson_result value::get_string(bool allow_replacement) noexcept { + return iter.get_string(allow_replacement); } -simdjson_inline value_iterator document::resume_value_iterator() noexcept { - return value_iterator(&iter, 1, iter.root_position()); +template +simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept { + return iter.get_string(receiver, allow_replacement); } -simdjson_inline value_iterator document::get_root_value_iterator() noexcept { - return resume_value_iterator(); +simdjson_inline simdjson_result value::get_wobbly_string() noexcept { + return iter.get_wobbly_string(); } -simdjson_inline simdjson_result document::start_or_resume_object() noexcept { - if (iter.at_root()) { - return get_object(); - } else { - return object::resume(resume_value_iterator()); - } +simdjson_inline simdjson_result value::get_double() noexcept { + return iter.get_double(); } -simdjson_inline simdjson_result document::get_value() noexcept { - // Make sure we start any arrays or objects before returning, so that start_root_() - // gets called. - - // It is the convention throughout the code that the macro `SIMDJSON_DEVELOPMENT_CHECKS` determines whether - // we check for OUT_OF_ORDER_ITERATION. Proper on::demand code should never trigger this error. -#if SIMDJSON_DEVELOPMENT_CHECKS - if (!iter.at_root()) { return OUT_OF_ORDER_ITERATION; } -#endif - // assert_at_root() serves two purposes: in Debug mode, whether or not - // SIMDJSON_DEVELOPMENT_CHECKS is set or not, it checks that we are at the root of - // the document (this will typically be redundant). In release mode, it generates - // SIMDJSON_ASSUME statements to allow the compiler to make assumptions. - iter.assert_at_root(); - switch (*iter.peek()) { - case '[': { - // The following lines check that the document ends with ]. - auto value_iterator = get_root_value_iterator(); - auto error = value_iterator.check_root_array(); - if(error) { return error; } - return value(get_root_value_iterator()); - } - case '{': { - // The following lines would check that the document ends with }. - auto value_iterator = get_root_value_iterator(); - auto error = value_iterator.check_root_object(); - if(error) { return error; } - return value(get_root_value_iterator()); - } - default: - // Unfortunately, scalar documents are a special case in simdjson and they cannot - // be safely converted to value instances. - return SCALAR_DOCUMENT_AS_VALUE; - } +simdjson_inline simdjson_result value::get_double_in_string() noexcept { + return iter.get_double_in_string(); } -simdjson_inline simdjson_result document::get_array() & noexcept { - auto value = get_root_value_iterator(); - return array::start_root(value); +simdjson_inline simdjson_result value::get_uint64() noexcept { + return iter.get_uint64(); } -simdjson_inline simdjson_result document::get_object() & noexcept { - auto value = get_root_value_iterator(); - return object::start_root(value); +simdjson_inline simdjson_result value::get_uint64_in_string() noexcept { + return iter.get_uint64_in_string(); } - -/** - * We decided that calling 'get_double()' on the JSON document '1.233 blabla' should - * give an error, so we check for trailing content. We want to disallow trailing - * content. - * Thus, in several implementations below, we pass a 'true' parameter value to - * a get_root_value_iterator() method: this indicates that we disallow trailing content. - */ - -simdjson_inline simdjson_result document::get_uint64() noexcept { - return get_root_value_iterator().get_root_uint64(true); +simdjson_inline simdjson_result value::get_int64() noexcept { + return iter.get_int64(); } -simdjson_inline simdjson_result document::get_uint64_in_string() noexcept { - return get_root_value_iterator().get_root_uint64_in_string(true); +simdjson_inline simdjson_result value::get_int64_in_string() noexcept { + return iter.get_int64_in_string(); } -simdjson_inline simdjson_result document::get_int64() noexcept { - return get_root_value_iterator().get_root_int64(true); +simdjson_inline simdjson_result value::get_bool() noexcept { + return iter.get_bool(); } -simdjson_inline simdjson_result document::get_int64_in_string() noexcept { - return get_root_value_iterator().get_root_int64_in_string(true); +simdjson_inline simdjson_result value::is_null() noexcept { + return iter.is_null(); } -simdjson_inline simdjson_result document::get_double() noexcept { - return get_root_value_iterator().get_root_double(true); + +template<> simdjson_inline simdjson_result value::get() noexcept { return get_array(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_object(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_number(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_double(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_bool(); } + + +template<> simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); } +template<> simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); } +template<> simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); } +template<> simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); } +template<> simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); } +template<> simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); } +template<> simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); } +template<> simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); } +template<> simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); } + +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline value::operator T() noexcept(false) { + return get(); } -simdjson_inline simdjson_result document::get_double_in_string() noexcept { - return get_root_value_iterator().get_root_double_in_string(true); +simdjson_inline value::operator array() noexcept(false) { + return get_array(); } -simdjson_inline simdjson_result document::get_string(bool allow_replacement) noexcept { - return get_root_value_iterator().get_root_string(true, allow_replacement); +simdjson_inline value::operator object() noexcept(false) { + return get_object(); } -template -simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept { - return get_root_value_iterator().get_root_string(receiver, true, allow_replacement); +simdjson_inline value::operator uint64_t() noexcept(false) { + return get_uint64(); } -simdjson_inline simdjson_result document::get_wobbly_string() noexcept { - return get_root_value_iterator().get_root_wobbly_string(true); +simdjson_inline value::operator int64_t() noexcept(false) { + return get_int64(); } -simdjson_inline simdjson_result document::get_raw_json_string() noexcept { - return get_root_value_iterator().get_root_raw_json_string(true); +simdjson_inline value::operator double() noexcept(false) { + return get_double(); } -simdjson_inline simdjson_result document::get_bool() noexcept { - return get_root_value_iterator().get_root_bool(true); +simdjson_inline value::operator std::string_view() noexcept(false) { + return get_string(false); } -simdjson_inline simdjson_result document::is_null() noexcept { - return get_root_value_iterator().is_root_null(true); +simdjson_inline value::operator raw_json_string() noexcept(false) { + return get_raw_json_string(); } +simdjson_inline value::operator bool() noexcept(false) { + return get_bool(); +} +#endif -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_array(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_object(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_raw_json_string(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_string(false); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_double(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_uint64(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_int64(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_value(); } - -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_raw_json_string(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_value(); } - -template simdjson_inline error_code document::get(T &out) & noexcept { - return get().get(out); +simdjson_inline simdjson_result value::begin() & noexcept { + return get_array().begin(); } -template simdjson_deprecated simdjson_inline error_code document::get(T &out) && noexcept { - return std::forward(*this).get().get(out); +simdjson_inline simdjson_result value::end() & noexcept { + return {}; } - -#if SIMDJSON_EXCEPTIONS -template -simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get(); } -template -simdjson_inline document::operator T() & noexcept(false) { return get(); } -simdjson_inline document::operator array() & noexcept(false) { return get_array(); } -simdjson_inline document::operator object() & noexcept(false) { return get_object(); } -simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); } -simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); } -simdjson_inline document::operator double() noexcept(false) { return get_double(); } -simdjson_inline document::operator std::string_view() noexcept(false) { return get_string(false); } -simdjson_inline document::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } -simdjson_inline document::operator bool() noexcept(false) { return get_bool(); } -simdjson_inline document::operator value() noexcept(false) { return get_value(); } - -#endif -simdjson_inline simdjson_result document::count_elements() & noexcept { +simdjson_inline simdjson_result value::count_elements() & noexcept { + simdjson_result answer; auto a = get_array(); - simdjson_result answer = a.count_elements(); - /* If there was an array, we are now left pointing at its first element. */ - if(answer.error() == SUCCESS) { rewind(); } + answer = a.count_elements(); + // count_elements leaves you pointing inside the array, at the first element. + // We need to move back so that the user can create a new array (which requires that + // we point at '['). + iter.move_at_start(); return answer; } -simdjson_inline simdjson_result document::count_fields() & noexcept { +simdjson_inline simdjson_result value::count_fields() & noexcept { + simdjson_result answer; auto a = get_object(); - simdjson_result answer = a.count_fields(); - /* If there was an object, we are now left pointing at its first element. */ - if(answer.error() == SUCCESS) { rewind(); } + answer = a.count_fields(); + iter.move_at_start(); return answer; } -simdjson_inline simdjson_result document::at(size_t index) & noexcept { +simdjson_inline simdjson_result value::at(size_t index) noexcept { auto a = get_array(); return a.at(index); } -simdjson_inline simdjson_result document::begin() & noexcept { - return get_array().begin(); -} -simdjson_inline simdjson_result document::end() & noexcept { - return {}; -} -simdjson_inline simdjson_result document::find_field(std::string_view key) & noexcept { +simdjson_inline simdjson_result value::find_field(std::string_view key) noexcept { return start_or_resume_object().find_field(key); } -simdjson_inline simdjson_result document::find_field(const char *key) & noexcept { +simdjson_inline simdjson_result value::find_field(const char *key) noexcept { return start_or_resume_object().find_field(key); } -simdjson_inline simdjson_result document::find_field_unordered(std::string_view key) & noexcept { + +simdjson_inline simdjson_result value::find_field_unordered(std::string_view key) noexcept { return start_or_resume_object().find_field_unordered(key); } -simdjson_inline simdjson_result document::find_field_unordered(const char *key) & noexcept { +simdjson_inline simdjson_result value::find_field_unordered(const char *key) noexcept { return start_or_resume_object().find_field_unordered(key); } -simdjson_inline simdjson_result document::operator[](std::string_view key) & noexcept { + +simdjson_inline simdjson_result value::operator[](std::string_view key) noexcept { return start_or_resume_object()[key]; } -simdjson_inline simdjson_result document::operator[](const char *key) & noexcept { +simdjson_inline simdjson_result value::operator[](const char *key) noexcept { return start_or_resume_object()[key]; } -simdjson_inline error_code document::consume() noexcept { - auto error = iter.skip_child(0); - if(error) { iter.abandon(); } - return error; -} - -simdjson_inline simdjson_result document::raw_json() noexcept { - auto _iter = get_root_value_iterator(); - const uint8_t * starting_point{_iter.peek_start()}; - auto error = consume(); - if(error) { return error; } - // After 'consume()', we could be left pointing just beyond the document, but that - // is ok because we are not going to dereference the final pointer position, we just - // use it to compute the length in bytes. - const uint8_t * final_point{iter.unsafe_pointer()}; - return std::string_view(reinterpret_cast(starting_point), size_t(final_point - starting_point)); -} - -simdjson_inline simdjson_result document::type() noexcept { - return get_root_value_iterator().type(); +simdjson_inline simdjson_result value::type() noexcept { + return iter.type(); } -simdjson_inline simdjson_result document::is_scalar() noexcept { +simdjson_inline simdjson_result value::is_scalar() noexcept { json_type this_type; auto error = type().get(this_type); if(error) { return error; } return ! ((this_type == json_type::array) || (this_type == json_type::object)); } -simdjson_inline simdjson_result document::is_string() noexcept { +simdjson_inline simdjson_result value::is_string() noexcept { json_type this_type; auto error = type().get(this_type); if(error) { return error; } return (this_type == json_type::string); } -simdjson_inline bool document::is_negative() noexcept { - return get_root_value_iterator().is_root_negative(); + +simdjson_inline bool value::is_negative() noexcept { + return iter.is_negative(); } -simdjson_inline simdjson_result document::is_integer() noexcept { - return get_root_value_iterator().is_root_integer(true); +simdjson_inline simdjson_result value::is_integer() noexcept { + return iter.is_integer(); +} +simdjson_warn_unused simdjson_inline simdjson_result value::get_number_type() noexcept { + return iter.get_number_type(); +} +simdjson_warn_unused simdjson_inline simdjson_result value::get_number() noexcept { + return iter.get_number(); } -simdjson_inline simdjson_result document::get_number_type() noexcept { - return get_root_value_iterator().get_root_number_type(true); +simdjson_inline std::string_view value::raw_json_token() noexcept { + return std::string_view(reinterpret_cast(iter.peek_start()), iter.peek_start_length()); } -simdjson_inline simdjson_result document::get_number() noexcept { - return get_root_value_iterator().get_root_number(true); +simdjson_inline simdjson_result value::raw_json() noexcept { + json_type t; + SIMDJSON_TRY(type().get(t)); + switch (t) + { + case json_type::array: { + ondemand::array array; + SIMDJSON_TRY(get_array().get(array)); + return array.raw_json(); + } + case json_type::object: { + ondemand::object object; + SIMDJSON_TRY(get_object().get(object)); + return object.raw_json(); + } + default: + return raw_json_token(); + } } +simdjson_inline simdjson_result value::current_location() noexcept { + return iter.json_iter().current_location(); +} -simdjson_inline simdjson_result document::raw_json_token() noexcept { - auto _iter = get_root_value_iterator(); - return std::string_view(reinterpret_cast(_iter.peek_start()), _iter.peek_root_length()); +simdjson_inline int32_t value::current_depth() const noexcept{ + return iter.json_iter().depth(); } -simdjson_inline simdjson_result document::at_pointer(std::string_view json_pointer) noexcept { - rewind(); // Rewind the document each time at_pointer is called - if (json_pointer.empty()) { - return this->get_value(); +inline bool is_pointer_well_formed(std::string_view json_pointer) noexcept { + if (simdjson_unlikely(json_pointer.empty())) { // can't be + return false; + } + if (simdjson_unlikely(json_pointer[0] != '/')) { + return false; + } + size_t escape = json_pointer.find('~'); + if (escape == std::string_view::npos) { + return true; + } + if (escape == json_pointer.size() - 1) { + return false; } + if (json_pointer[escape + 1] != '0' && json_pointer[escape + 1] != '1') { + return false; + } + return true; +} + +simdjson_inline simdjson_result value::at_pointer(std::string_view json_pointer) noexcept { json_type t; SIMDJSON_TRY(type().get(t)); switch (t) @@ -58303,15 +60152,15 @@ simdjson_inline simdjson_result document::at_pointer(std::string_view jso case json_type::object: return (*this).get_object().at_pointer(json_pointer); default: + // a non-empty string can be invalid, or accessing a primitive (issue 2154) + if (is_pointer_well_formed(json_pointer)) { + return NO_SUCH_FIELD; + } return INVALID_JSON_POINTER; } } -simdjson_inline simdjson_result document::at_path(std::string_view json_path) noexcept { - rewind(); // Rewind the document each time at_pointer is called - if (json_path.empty()) { - return this->get_value(); - } +simdjson_inline simdjson_result value::at_path(std::string_view json_path) noexcept { json_type t; SIMDJSON_TRY(type().get(t)); switch (t) { @@ -58330,680 +60179,1319 @@ simdjson_inline simdjson_result document::at_path(std::string_view json_p namespace simdjson { -simdjson_inline simdjson_result::simdjson_result( - haswell::ondemand::document &&value +simdjson_inline simdjson_result::simdjson_result( + haswell::ondemand::value &&value ) noexcept : - implementation_simdjson_result_base( - std::forward(value) + implementation_simdjson_result_base( + std::forward(value) ) { } -simdjson_inline simdjson_result::simdjson_result( +simdjson_inline simdjson_result::simdjson_result( error_code error ) noexcept : - implementation_simdjson_result_base( - error - ) + implementation_simdjson_result_base(error) { } -simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { if (error()) { return error(); } return first.count_elements(); } -simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { if (error()) { return error(); } return first.count_fields(); } -simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { +simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { if (error()) { return error(); } return first.at(index); } -simdjson_inline error_code simdjson_result::rewind() noexcept { - if (error()) { return error(); } - first.rewind(); - return SUCCESS; -} -simdjson_inline simdjson_result simdjson_result::begin() & noexcept { +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { if (error()) { return error(); } return first.begin(); } -simdjson_inline simdjson_result simdjson_result::end() & noexcept { +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + if (error()) { return error(); } return {}; } -simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) noexcept { if (error()) { return error(); } - return first.find_field_unordered(key); + return first.find_field(key); } -simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) noexcept { if (error()) { return error(); } - return first.find_field_unordered(key); + return first.find_field(key); } -simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) noexcept { if (error()) { return error(); } - return first[key]; + return first.find_field_unordered(key); } -simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) noexcept { if (error()) { return error(); } - return first[key]; + return first.find_field_unordered(key); } -simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) noexcept { if (error()) { return error(); } - return first.find_field(key); + return first[key]; } -simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) noexcept { if (error()) { return error(); } - return first.find_field(key); + return first[key]; } -simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { + +simdjson_inline simdjson_result simdjson_result::get_array() noexcept { if (error()) { return error(); } return first.get_array(); } -simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { +simdjson_inline simdjson_result simdjson_result::get_object() noexcept { if (error()) { return error(); } return first.get_object(); } -simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { if (error()) { return error(); } return first.get_uint64(); } -simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { if (error()) { return error(); } return first.get_uint64_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { if (error()) { return error(); } return first.get_int64(); } -simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { if (error()) { return error(); } return first.get_int64_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_double() noexcept { +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { if (error()) { return error(); } return first.get_double(); } -simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { if (error()) { return error(); } return first.get_double_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(allow_replacement); } template -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(receiver, allow_replacement); } -simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { if (error()) { return error(); } return first.get_wobbly_string(); } -simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { if (error()) { return error(); } return first.get_raw_json_string(); } -simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { if (error()) { return error(); } return first.get_bool(); } -simdjson_inline simdjson_result simdjson_result::get_value() noexcept { - if (error()) { return error(); } - return first.get_value(); -} -simdjson_inline simdjson_result simdjson_result::is_null() noexcept { +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { if (error()) { return error(); } return first.is_null(); } -template -simdjson_inline simdjson_result simdjson_result::get() & noexcept { +template<> simdjson_inline error_code simdjson_result::get(haswell::ondemand::value &out) noexcept { if (error()) { return error(); } - return first.get(); + out = first; + return SUCCESS; } -template -simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + +template simdjson_inline simdjson_result simdjson_result::get() noexcept { if (error()) { return error(); } - return std::forward(first).get(); + return first.get(); } -template -simdjson_inline error_code simdjson_result::get(T &out) & noexcept { +template simdjson_inline error_code simdjson_result::get(T &out) noexcept { if (error()) { return error(); } return first.get(out); } -template -simdjson_inline error_code simdjson_result::get(T &out) && noexcept { - if (error()) { return error(); } - return std::forward(first).get(out); -} -template<> simdjson_inline simdjson_result simdjson_result::get() & noexcept = delete; -template<> simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { - if (error()) { return error(); } - return std::forward(first); -} -template<> simdjson_inline error_code simdjson_result::get(haswell::ondemand::document &out) & noexcept = delete; -template<> simdjson_inline error_code simdjson_result::get(haswell::ondemand::document &out) && noexcept { +template<> simdjson_inline simdjson_result simdjson_result::get() noexcept { if (error()) { return error(); } - out = std::forward(first); - return SUCCESS; + return std::move(first); } -simdjson_inline simdjson_result simdjson_result::type() noexcept { +simdjson_inline simdjson_result simdjson_result::type() noexcept { if (error()) { return error(); } return first.type(); } - -simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { if (error()) { return error(); } return first.is_scalar(); } - -simdjson_inline simdjson_result simdjson_result::is_string() noexcept { +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { if (error()) { return error(); } return first.is_string(); } - -simdjson_inline bool simdjson_result::is_negative() noexcept { +simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { if (error()) { return error(); } return first.is_negative(); } - -simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { if (error()) { return error(); } return first.is_integer(); } - -simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { if (error()) { return error(); } return first.get_number_type(); } - -simdjson_inline simdjson_result simdjson_result::get_number() noexcept { +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { if (error()) { return error(); } return first.get_number(); } - - #if SIMDJSON_EXCEPTIONS -template ::value == false>::type> -simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator haswell::ondemand::array() & noexcept(false) { +template +simdjson_inline simdjson_result::operator T() noexcept(false) { if (error()) { throw simdjson_error(error()); } - return first; + return first.get(); } -simdjson_inline simdjson_result::operator haswell::ondemand::object() & noexcept(false) { +simdjson_inline simdjson_result::operator haswell::ondemand::array() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { +simdjson_inline simdjson_result::operator haswell::ondemand::object() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator int64_t() noexcept(false) { +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator double() noexcept(false) { +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { +simdjson_inline simdjson_result::operator double() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator haswell::ondemand::raw_json_string() noexcept(false) { +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator bool() noexcept(false) { +simdjson_inline simdjson_result::operator haswell::ondemand::raw_json_string() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator haswell::ondemand::value() noexcept(false) { +simdjson_inline simdjson_result::operator bool() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } #endif - -simdjson_inline simdjson_result simdjson_result::current_location() noexcept { +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { if (error()) { return error(); } - return first.current_location(); + return first.raw_json_token(); } -simdjson_inline bool simdjson_result::at_end() const noexcept { +simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { if (error()) { return error(); } - return first.at_end(); + return first.raw_json(); } - -simdjson_inline int32_t simdjson_result::current_depth() const noexcept { +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { if (error()) { return error(); } - return first.current_depth(); + return first.current_location(); } -simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { +simdjson_inline simdjson_result simdjson_result::current_depth() const noexcept { if (error()) { return error(); } - return first.raw_json_token(); + return first.current_depth(); } -simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } +simdjson_inline simdjson_result simdjson_result::at_pointer( + std::string_view json_pointer) noexcept { + if (error()) { + return error(); + } return first.at_pointer(json_pointer); } -simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { return error(); } +simdjson_inline simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } return first.at_path(json_path); } } // namespace simdjson +#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H +/* end file simdjson/generic/ondemand/value-inl.h for haswell */ +/* including simdjson/generic/ondemand/document-inl.h for haswell: #include "simdjson/generic/ondemand/document-inl.h" */ +/* begin file simdjson/generic/ondemand/document-inl.h for haswell */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { namespace haswell { namespace ondemand { -simdjson_inline document_reference::document_reference() noexcept : doc{nullptr} {} -simdjson_inline document_reference::document_reference(document &d) noexcept : doc(&d) {} -simdjson_inline void document_reference::rewind() noexcept { doc->rewind(); } -simdjson_inline simdjson_result document_reference::get_array() & noexcept { return doc->get_array(); } -simdjson_inline simdjson_result document_reference::get_object() & noexcept { return doc->get_object(); } -/** - * The document_reference instances are used primarily/solely for streams of JSON - * documents. - * We decided that calling 'get_double()' on the JSON document '1.233 blabla' should - * give an error, so we check for trailing content. - * - * However, for streams of JSON documents, we want to be able to start from - * "321" "321" "321" - * and parse it successfully as a stream of JSON documents, calling get_uint64_in_string() - * successfully each time. - * - * To achieve this result, we pass a 'false' to a get_root_value_iterator() method: - * this indicates that we allow trailing content. - */ -simdjson_inline simdjson_result document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); } -simdjson_inline simdjson_result document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); } -simdjson_inline simdjson_result document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); } -simdjson_inline simdjson_result document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); } -simdjson_inline simdjson_result document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); } -simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } -simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } -template -simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); } -simdjson_inline simdjson_result document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } -simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } -simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } -simdjson_inline simdjson_result document_reference::get_value() noexcept { return doc->get_value(); } -simdjson_inline simdjson_result document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_array(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_object(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_raw_json_string(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_string(false); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_double(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_uint64(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_int64(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_bool(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_value(); } -#if SIMDJSON_EXCEPTIONS -template -simdjson_inline document_reference::operator T() noexcept(false) { return get(); } -simdjson_inline document_reference::operator array() & noexcept(false) { return array(*doc); } -simdjson_inline document_reference::operator object() & noexcept(false) { return object(*doc); } -simdjson_inline document_reference::operator uint64_t() noexcept(false) { return get_uint64(); } -simdjson_inline document_reference::operator int64_t() noexcept(false) { return get_int64(); } -simdjson_inline document_reference::operator double() noexcept(false) { return get_double(); } -simdjson_inline document_reference::operator std::string_view() noexcept(false) { return std::string_view(*doc); } -simdjson_inline document_reference::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } -simdjson_inline document_reference::operator bool() noexcept(false) { return get_bool(); } -simdjson_inline document_reference::operator value() noexcept(false) { return value(*doc); } -#endif -simdjson_inline simdjson_result document_reference::count_elements() & noexcept { return doc->count_elements(); } -simdjson_inline simdjson_result document_reference::count_fields() & noexcept { return doc->count_fields(); } -simdjson_inline simdjson_result document_reference::at(size_t index) & noexcept { return doc->at(index); } -simdjson_inline simdjson_result document_reference::begin() & noexcept { return doc->begin(); } -simdjson_inline simdjson_result document_reference::end() & noexcept { return doc->end(); } -simdjson_inline simdjson_result document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); } -simdjson_inline simdjson_result document_reference::find_field(const char *key) & noexcept { return doc->find_field(key); } -simdjson_inline simdjson_result document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; } -simdjson_inline simdjson_result document_reference::operator[](const char *key) & noexcept { return (*doc)[key]; } -simdjson_inline simdjson_result document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); } -simdjson_inline simdjson_result document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); } -simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } -simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } -simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } -simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } -simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } -simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } -simdjson_inline simdjson_result document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); } -simdjson_inline simdjson_result document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); } -simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } -simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } -simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } -simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } -simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} -simdjson_inline document_reference::operator document&() const noexcept { return *doc; } +simdjson_inline document::document(ondemand::json_iterator &&_iter) noexcept + : iter{std::forward(_iter)} +{ + logger::log_start_value(iter, "document"); +} -} // namespace ondemand -} // namespace haswell -} // namespace simdjson +simdjson_inline document document::start(json_iterator &&iter) noexcept { + return document(std::forward(iter)); +} +inline void document::rewind() noexcept { + iter.rewind(); +} +inline std::string document::to_debug_string() noexcept { + return iter.to_string(); +} -namespace simdjson { -simdjson_inline simdjson_result::simdjson_result(haswell::ondemand::document_reference value, error_code error) - noexcept : implementation_simdjson_result_base(std::forward(value), error) {} +inline simdjson_result document::current_location() const noexcept { + return iter.current_location(); +} +inline int32_t document::current_depth() const noexcept { + return iter.depth(); +} -simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); +inline bool document::at_end() const noexcept { + return iter.at_end(); } -simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); + + +inline bool document::is_alive() noexcept { + return iter.is_alive(); } -simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { - if (error()) { return error(); } - return first.at(index); +simdjson_inline value_iterator document::resume_value_iterator() noexcept { + return value_iterator(&iter, 1, iter.root_position()); } -simdjson_inline error_code simdjson_result::rewind() noexcept { - if (error()) { return error(); } - first.rewind(); - return SUCCESS; +simdjson_inline value_iterator document::get_root_value_iterator() noexcept { + return resume_value_iterator(); } -simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); +simdjson_inline simdjson_result document::start_or_resume_object() noexcept { + if (iter.at_root()) { + return get_object(); + } else { + return object::resume(resume_value_iterator()); + } } -simdjson_inline simdjson_result simdjson_result::end() & noexcept { - return {}; +simdjson_inline simdjson_result document::get_value() noexcept { + // Make sure we start any arrays or objects before returning, so that start_root_() + // gets called. + + // It is the convention throughout the code that the macro `SIMDJSON_DEVELOPMENT_CHECKS` determines whether + // we check for OUT_OF_ORDER_ITERATION. Proper on::demand code should never trigger this error. +#if SIMDJSON_DEVELOPMENT_CHECKS + if (!iter.at_root()) { return OUT_OF_ORDER_ITERATION; } +#endif + // assert_at_root() serves two purposes: in Debug mode, whether or not + // SIMDJSON_DEVELOPMENT_CHECKS is set or not, it checks that we are at the root of + // the document (this will typically be redundant). In release mode, it generates + // SIMDJSON_ASSUME statements to allow the compiler to make assumptions. + iter.assert_at_root(); + switch (*iter.peek()) { + case '[': { + // The following lines check that the document ends with ]. + auto value_iterator = get_root_value_iterator(); + auto error = value_iterator.check_root_array(); + if(error) { return error; } + return value(get_root_value_iterator()); + } + case '{': { + // The following lines would check that the document ends with }. + auto value_iterator = get_root_value_iterator(); + auto error = value_iterator.check_root_object(); + if(error) { return error; } + return value(get_root_value_iterator()); + } + default: + // Unfortunately, scalar documents are a special case in simdjson and they cannot + // be safely converted to value instances. + return SCALAR_DOCUMENT_AS_VALUE; + } } -simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { +simdjson_inline simdjson_result document::get_array() & noexcept { + auto value = get_root_value_iterator(); + return array::start_root(value); +} +simdjson_inline simdjson_result document::get_object() & noexcept { + auto value = get_root_value_iterator(); + return object::start_root(value); +} + +/** + * We decided that calling 'get_double()' on the JSON document '1.233 blabla' should + * give an error, so we check for trailing content. We want to disallow trailing + * content. + * Thus, in several implementations below, we pass a 'true' parameter value to + * a get_root_value_iterator() method: this indicates that we disallow trailing content. + */ + +simdjson_inline simdjson_result document::get_uint64() noexcept { + return get_root_value_iterator().get_root_uint64(true); +} +simdjson_inline simdjson_result document::get_uint64_in_string() noexcept { + return get_root_value_iterator().get_root_uint64_in_string(true); +} +simdjson_inline simdjson_result document::get_int64() noexcept { + return get_root_value_iterator().get_root_int64(true); +} +simdjson_inline simdjson_result document::get_int64_in_string() noexcept { + return get_root_value_iterator().get_root_int64_in_string(true); +} +simdjson_inline simdjson_result document::get_double() noexcept { + return get_root_value_iterator().get_root_double(true); +} +simdjson_inline simdjson_result document::get_double_in_string() noexcept { + return get_root_value_iterator().get_root_double_in_string(true); +} +simdjson_inline simdjson_result document::get_string(bool allow_replacement) noexcept { + return get_root_value_iterator().get_root_string(true, allow_replacement); +} +template +simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept { + return get_root_value_iterator().get_root_string(receiver, true, allow_replacement); +} +simdjson_inline simdjson_result document::get_wobbly_string() noexcept { + return get_root_value_iterator().get_root_wobbly_string(true); +} +simdjson_inline simdjson_result document::get_raw_json_string() noexcept { + return get_root_value_iterator().get_root_raw_json_string(true); +} +simdjson_inline simdjson_result document::get_bool() noexcept { + return get_root_value_iterator().get_root_bool(true); +} +simdjson_inline simdjson_result document::is_null() noexcept { + return get_root_value_iterator().is_root_null(true); +} + +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_array(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_object(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_double(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_value(); } + +template<> simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); } +template<> simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); } +template<> simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); } +template<> simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); } +template<> simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); } +template<> simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); } +template<> simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); } +template<> simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); } +template<> simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); } + +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_raw_json_string(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_value(); } + +#if SIMDJSON_EXCEPTIONS +template +simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get(); } +template +simdjson_inline document::operator T() & noexcept(false) { return get(); } +simdjson_inline document::operator array() & noexcept(false) { return get_array(); } +simdjson_inline document::operator object() & noexcept(false) { return get_object(); } +simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); } +simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); } +simdjson_inline document::operator double() noexcept(false) { return get_double(); } +simdjson_inline document::operator std::string_view() noexcept(false) { return get_string(false); } +simdjson_inline document::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } +simdjson_inline document::operator bool() noexcept(false) { return get_bool(); } +simdjson_inline document::operator value() noexcept(false) { return get_value(); } + +#endif +simdjson_inline simdjson_result document::count_elements() & noexcept { + auto a = get_array(); + simdjson_result answer = a.count_elements(); + /* If there was an array, we are now left pointing at its first element. */ + if(answer.error() == SUCCESS) { rewind(); } + return answer; +} +simdjson_inline simdjson_result document::count_fields() & noexcept { + auto a = get_object(); + simdjson_result answer = a.count_fields(); + /* If there was an object, we are now left pointing at its first element. */ + if(answer.error() == SUCCESS) { rewind(); } + return answer; +} +simdjson_inline simdjson_result document::at(size_t index) & noexcept { + auto a = get_array(); + return a.at(index); +} +simdjson_inline simdjson_result document::begin() & noexcept { + return get_array().begin(); +} +simdjson_inline simdjson_result document::end() & noexcept { + return {}; +} + +simdjson_inline simdjson_result document::find_field(std::string_view key) & noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result document::find_field(const char *key) & noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result document::find_field_unordered(std::string_view key) & noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result document::find_field_unordered(const char *key) & noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result document::operator[](std::string_view key) & noexcept { + return start_or_resume_object()[key]; +} +simdjson_inline simdjson_result document::operator[](const char *key) & noexcept { + return start_or_resume_object()[key]; +} + +simdjson_inline error_code document::consume() noexcept { + bool scalar = false; + auto error = is_scalar().get(scalar); + if(error) { return error; } + if(scalar) { + iter.return_current_and_advance(); + return SUCCESS; + } + error = iter.skip_child(0); + if(error) { iter.abandon(); } + return error; +} + +simdjson_inline simdjson_result document::raw_json() noexcept { + auto _iter = get_root_value_iterator(); + const uint8_t * starting_point{_iter.peek_start()}; + auto error = consume(); + if(error) { return error; } + // After 'consume()', we could be left pointing just beyond the document, but that + // is ok because we are not going to dereference the final pointer position, we just + // use it to compute the length in bytes. + const uint8_t * final_point{iter.unsafe_pointer()}; + return std::string_view(reinterpret_cast(starting_point), size_t(final_point - starting_point)); +} + +simdjson_inline simdjson_result document::type() noexcept { + return get_root_value_iterator().type(); +} + +simdjson_inline simdjson_result document::is_scalar() noexcept { + // For more speed, we could do: + // return iter.is_single_token(); + json_type this_type; + auto error = type().get(this_type); + if(error) { return error; } + return ! ((this_type == json_type::array) || (this_type == json_type::object)); +} + +simdjson_inline simdjson_result document::is_string() noexcept { + json_type this_type; + auto error = type().get(this_type); + if(error) { return error; } + return (this_type == json_type::string); +} + +simdjson_inline bool document::is_negative() noexcept { + return get_root_value_iterator().is_root_negative(); +} + +simdjson_inline simdjson_result document::is_integer() noexcept { + return get_root_value_iterator().is_root_integer(true); +} + +simdjson_inline simdjson_result document::get_number_type() noexcept { + return get_root_value_iterator().get_root_number_type(true); +} + +simdjson_inline simdjson_result document::get_number() noexcept { + return get_root_value_iterator().get_root_number(true); +} + + +simdjson_inline simdjson_result document::raw_json_token() noexcept { + auto _iter = get_root_value_iterator(); + return std::string_view(reinterpret_cast(_iter.peek_start()), _iter.peek_root_length()); +} + +simdjson_inline simdjson_result document::at_pointer(std::string_view json_pointer) noexcept { + rewind(); // Rewind the document each time at_pointer is called + if (json_pointer.empty()) { + return this->get_value(); + } + json_type t; + SIMDJSON_TRY(type().get(t)); + switch (t) + { + case json_type::array: + return (*this).get_array().at_pointer(json_pointer); + case json_type::object: + return (*this).get_object().at_pointer(json_pointer); + default: + return INVALID_JSON_POINTER; + } +} + +simdjson_inline simdjson_result document::at_path(std::string_view json_path) noexcept { + rewind(); // Rewind the document each time at_pointer is called + if (json_path.empty()) { + return this->get_value(); + } + json_type t; + SIMDJSON_TRY(type().get(t)); + switch (t) { + case json_type::array: + return (*this).get_array().at_path(json_path); + case json_type::object: + return (*this).get_object().at_path(json_path); + default: + return INVALID_JSON_POINTER; + } +} + +} // namespace ondemand +} // namespace haswell +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result( + haswell::ondemand::document &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base( + error + ) +{ +} +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline error_code simdjson_result::rewind() noexcept { + if (error()) { return error(); } + first.rewind(); + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { if (error()) { return error(); } return first.find_field_unordered(key); } -simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { if (error()) { return error(); } return first.find_field_unordered(key); } -simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { if (error()) { return error(); } return first[key]; } -simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { if (error()) { return error(); } return first[key]; } -simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { if (error()) { return error(); } return first.find_field(key); } -simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { if (error()) { return error(); } return first.find_field(key); } -simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { +simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { if (error()) { return error(); } return first.get_array(); } -simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { +simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { if (error()) { return error(); } return first.get_object(); } -simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { if (error()) { return error(); } return first.get_uint64(); } -simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { if (error()) { return error(); } return first.get_uint64_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { if (error()) { return error(); } return first.get_int64(); } -simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { if (error()) { return error(); } return first.get_int64_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_double() noexcept { +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { if (error()) { return error(); } return first.get_double(); } -simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { if (error()) { return error(); } return first.get_double_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(allow_replacement); } template -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(receiver, allow_replacement); } -simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { if (error()) { return error(); } return first.get_wobbly_string(); } -simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { if (error()) { return error(); } return first.get_raw_json_string(); } -simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { if (error()) { return error(); } return first.get_bool(); } -simdjson_inline simdjson_result simdjson_result::get_value() noexcept { +simdjson_inline simdjson_result simdjson_result::get_value() noexcept { if (error()) { return error(); } return first.get_value(); } -simdjson_inline simdjson_result simdjson_result::is_null() noexcept { +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { if (error()) { return error(); } return first.is_null(); } -simdjson_inline simdjson_result simdjson_result::type() noexcept { + +template +simdjson_inline simdjson_result simdjson_result::get() & noexcept { + if (error()) { return error(); } + return first.get(); +} +template +simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first).get(); +} +template +simdjson_inline error_code simdjson_result::get(T &out) & noexcept { + if (error()) { return error(); } + return first.get(out); +} +template +simdjson_inline error_code simdjson_result::get(T &out) && noexcept { + if (error()) { return error(); } + return std::forward(first).get(out); +} + +template<> simdjson_inline simdjson_result simdjson_result::get() & noexcept = delete; +template<> simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first); +} +template<> simdjson_inline error_code simdjson_result::get(haswell::ondemand::document &out) & noexcept = delete; +template<> simdjson_inline error_code simdjson_result::get(haswell::ondemand::document &out) && noexcept { + if (error()) { return error(); } + out = std::forward(first); + return SUCCESS; +} + +simdjson_inline simdjson_result simdjson_result::type() noexcept { if (error()) { return error(); } return first.type(); } -simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { if (error()) { return error(); } return first.is_scalar(); } -simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { if (error()) { return error(); } return first.is_string(); } -simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { + +simdjson_inline bool simdjson_result::is_negative() noexcept { if (error()) { return error(); } return first.is_negative(); } -simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { if (error()) { return error(); } return first.is_integer(); } -simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { if (error()) { return error(); } return first.get_number_type(); } -simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { if (error()) { return error(); } return first.get_number(); } + + #if SIMDJSON_EXCEPTIONS -template ::value == false>::type> -simdjson_inline simdjson_result::operator T() noexcept(false) { +template ::value == false>::type> +simdjson_inline simdjson_result::operator T() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator haswell::ondemand::array() & noexcept(false) { +simdjson_inline simdjson_result::operator haswell::ondemand::array() & noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator haswell::ondemand::object() & noexcept(false) { +simdjson_inline simdjson_result::operator haswell::ondemand::object() & noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator int64_t() noexcept(false) { +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator double() noexcept(false) { +simdjson_inline simdjson_result::operator double() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator haswell::ondemand::raw_json_string() noexcept(false) { +simdjson_inline simdjson_result::operator haswell::ondemand::raw_json_string() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator bool() noexcept(false) { +simdjson_inline simdjson_result::operator bool() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator haswell::ondemand::value() noexcept(false) { +simdjson_inline simdjson_result::operator haswell::ondemand::value() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } #endif -simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { if (error()) { return error(); } return first.current_location(); } -simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { +simdjson_inline bool simdjson_result::at_end() const noexcept { + if (error()) { return error(); } + return first.at_end(); +} + + +simdjson_inline int32_t simdjson_result::current_depth() const noexcept { + if (error()) { return error(); } + return first.current_depth(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { if (error()) { return error(); } return first.raw_json_token(); } -simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { if (error()) { return error(); } return first.at_pointer(json_pointer); } -simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { - return error(); - } +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } return first.at_path(json_path); } } // namespace simdjson -#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H -/* end file simdjson/generic/ondemand/document-inl.h for haswell */ -/* including simdjson/generic/ondemand/document_stream-inl.h for haswell: #include "simdjson/generic/ondemand/document_stream-inl.h" */ -/* begin file simdjson/generic/ondemand/document_stream-inl.h for haswell */ -#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H - -/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ -/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document_stream.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */ -/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -#include -#include namespace simdjson { namespace haswell { namespace ondemand { -#ifdef SIMDJSON_THREADS_ENABLED - -inline void stage1_worker::finish() { - // After calling "run" someone would call finish() to wait - // for the end of the processing. - // This function will wait until either the thread has done - // the processing or, else, the destructor has been called. - std::unique_lock lock(locking_mutex); - cond_var.wait(lock, [this]{return has_work == false;}); -} - -inline stage1_worker::~stage1_worker() { - // The thread may never outlive the stage1_worker instance - // and will always be stopped/joined before the stage1_worker - // instance is gone. - stop_thread(); -} - -inline void stage1_worker::start_thread() { - std::unique_lock lock(locking_mutex); - if(thread.joinable()) { - return; // This should never happen but we never want to create more than one thread. - } - thread = std::thread([this]{ - while(true) { - std::unique_lock thread_lock(locking_mutex); - // We wait for either "run" or "stop_thread" to be called. - cond_var.wait(thread_lock, [this]{return has_work || !can_work;}); - // If, for some reason, the stop_thread() method was called (i.e., the - // destructor of stage1_worker is called, then we want to immediately destroy - // the thread (and not do any more processing). - if(!can_work) { - break; - } - this->owner->stage1_thread_error = this->owner->run_stage1(*this->stage1_thread_parser, - this->_next_batch_start); - this->has_work = false; - // The condition variable call should be moved after thread_lock.unlock() for performance - // reasons but thread sanitizers may report it as a data race if we do. - // See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock - cond_var.notify_one(); // will notify "finish" - thread_lock.unlock(); - } - } - ); -} - - -inline void stage1_worker::stop_thread() { - std::unique_lock lock(locking_mutex); - // We have to make sure that all locks can be released. - can_work = false; - has_work = false; - cond_var.notify_all(); - lock.unlock(); - if(thread.joinable()) { - thread.join(); - } -} - -inline void stage1_worker::run(document_stream * ds, parser * stage1, size_t next_batch_start) { - std::unique_lock lock(locking_mutex); - owner = ds; - _next_batch_start = next_batch_start; - stage1_thread_parser = stage1; - has_work = true; - // The condition variable call should be moved after thread_lock.unlock() for performance - // reasons but thread sanitizers may report it as a data race if we do. - // See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock - cond_var.notify_one(); // will notify the thread lock that we have work - lock.unlock(); -} - -#endif // SIMDJSON_THREADS_ENABLED - -simdjson_inline document_stream::document_stream( - ondemand::parser &_parser, - const uint8_t *_buf, +simdjson_inline document_reference::document_reference() noexcept : doc{nullptr} {} +simdjson_inline document_reference::document_reference(document &d) noexcept : doc(&d) {} +simdjson_inline void document_reference::rewind() noexcept { doc->rewind(); } +simdjson_inline simdjson_result document_reference::get_array() & noexcept { return doc->get_array(); } +simdjson_inline simdjson_result document_reference::get_object() & noexcept { return doc->get_object(); } +/** + * The document_reference instances are used primarily/solely for streams of JSON + * documents. + * We decided that calling 'get_double()' on the JSON document '1.233 blabla' should + * give an error, so we check for trailing content. + * + * However, for streams of JSON documents, we want to be able to start from + * "321" "321" "321" + * and parse it successfully as a stream of JSON documents, calling get_uint64_in_string() + * successfully each time. + * + * To achieve this result, we pass a 'false' to a get_root_value_iterator() method: + * this indicates that we allow trailing content. + */ +simdjson_inline simdjson_result document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); } +simdjson_inline simdjson_result document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); } +simdjson_inline simdjson_result document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); } +simdjson_inline simdjson_result document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); } +simdjson_inline simdjson_result document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); } +simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } +simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } +template +simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); } +simdjson_inline simdjson_result document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } +simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } +simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } +simdjson_inline simdjson_result document_reference::get_value() noexcept { return doc->get_value(); } +simdjson_inline simdjson_result document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_array(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_object(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_double(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_bool(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_value(); } +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline document_reference::operator T() noexcept(false) { return get(); } +simdjson_inline document_reference::operator array() & noexcept(false) { return array(*doc); } +simdjson_inline document_reference::operator object() & noexcept(false) { return object(*doc); } +simdjson_inline document_reference::operator uint64_t() noexcept(false) { return get_uint64(); } +simdjson_inline document_reference::operator int64_t() noexcept(false) { return get_int64(); } +simdjson_inline document_reference::operator double() noexcept(false) { return get_double(); } +simdjson_inline document_reference::operator std::string_view() noexcept(false) { return std::string_view(*doc); } +simdjson_inline document_reference::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } +simdjson_inline document_reference::operator bool() noexcept(false) { return get_bool(); } +simdjson_inline document_reference::operator value() noexcept(false) { return value(*doc); } +#endif +simdjson_inline simdjson_result document_reference::count_elements() & noexcept { return doc->count_elements(); } +simdjson_inline simdjson_result document_reference::count_fields() & noexcept { return doc->count_fields(); } +simdjson_inline simdjson_result document_reference::at(size_t index) & noexcept { return doc->at(index); } +simdjson_inline simdjson_result document_reference::begin() & noexcept { return doc->begin(); } +simdjson_inline simdjson_result document_reference::end() & noexcept { return doc->end(); } +simdjson_inline simdjson_result document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); } +simdjson_inline simdjson_result document_reference::find_field(const char *key) & noexcept { return doc->find_field(key); } +simdjson_inline simdjson_result document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; } +simdjson_inline simdjson_result document_reference::operator[](const char *key) & noexcept { return (*doc)[key]; } +simdjson_inline simdjson_result document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); } +simdjson_inline simdjson_result document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); } +simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } +simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } +simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } +simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } +simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } +simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } +simdjson_inline simdjson_result document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); } +simdjson_inline simdjson_result document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); } +simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } +simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } +simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } +simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } +simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} +simdjson_inline document_reference::operator document&() const noexcept { return *doc; } + +} // namespace ondemand +} // namespace haswell +} // namespace simdjson + + + +namespace simdjson { +simdjson_inline simdjson_result::simdjson_result(haswell::ondemand::document_reference value, error_code error) + noexcept : implementation_simdjson_result_base(std::forward(value), error) {} + + +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline error_code simdjson_result::rewind() noexcept { + if (error()) { return error(); } + first.rewind(); + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { + if (error()) { return error(); } + return first.get_array(); +} +simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { + if (error()) { return error(); } + return first.get_object(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { + if (error()) { return error(); } + return first.get_uint64(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { + if (error()) { return error(); } + return first.get_uint64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { + if (error()) { return error(); } + return first.get_int64(); +} +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { + if (error()) { return error(); } + return first.get_int64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { + if (error()) { return error(); } + return first.get_double(); +} +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { + if (error()) { return error(); } + return first.get_double_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(allow_replacement); +} +template +simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(receiver, allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { + if (error()) { return error(); } + return first.get_wobbly_string(); +} +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { + if (error()) { return error(); } + return first.get_raw_json_string(); +} +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { + if (error()) { return error(); } + return first.get_bool(); +} +simdjson_inline simdjson_result simdjson_result::get_value() noexcept { + if (error()) { return error(); } + return first.get_value(); +} +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { + if (error()) { return error(); } + return first.is_null(); +} +template +simdjson_inline simdjson_result simdjson_result::get() & noexcept { + if (error()) { return error(); } + return first.get(); +} +template +simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first).get(); +} +template +simdjson_inline error_code simdjson_result::get(T &out) & noexcept { + if (error()) { return error(); } + return first.get(out); +} +template +simdjson_inline error_code simdjson_result::get(T &out) && noexcept { + if (error()) { return error(); } + return std::forward(first).get(out); +} +simdjson_inline simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); +} +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + if (error()) { return error(); } + return first.is_scalar(); +} +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} +template <> +simdjson_inline error_code simdjson_result::get(haswell::ondemand::document_reference &out) & noexcept { + if (error()) { return error(); } + out = first; + return SUCCESS; +} +template <> +simdjson_inline error_code simdjson_result::get(haswell::ondemand::document_reference &out) && noexcept { + if (error()) { return error(); } + out = first; + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { + if (error()) { return error(); } + return first.is_negative(); +} +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + if (error()) { return error(); } + return first.is_integer(); +} +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + if (error()) { return error(); } + return first.get_number_type(); +} +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + if (error()) { return error(); } + return first.get_number(); +} +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline simdjson_result::operator T() noexcept(false) { + static_assert(std::is_same::value == false, "You should not call get when T is a document"); + static_assert(std::is_same::value == false, "You should not call get when T is a document"); + if (error()) { throw simdjson_error(error()); } + return first.get(); +} +simdjson_inline simdjson_result::operator haswell::ondemand::array() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator haswell::ondemand::object() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator double() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator haswell::ondemand::raw_json_string() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator bool() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator haswell::ondemand::value() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +#endif + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} + +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H +/* end file simdjson/generic/ondemand/document-inl.h for haswell */ +/* including simdjson/generic/ondemand/document_stream-inl.h for haswell: #include "simdjson/generic/ondemand/document_stream-inl.h" */ +/* begin file simdjson/generic/ondemand/document_stream-inl.h for haswell */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document_stream.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +#include + +namespace simdjson { +namespace haswell { +namespace ondemand { + +#ifdef SIMDJSON_THREADS_ENABLED + +inline void stage1_worker::finish() { + // After calling "run" someone would call finish() to wait + // for the end of the processing. + // This function will wait until either the thread has done + // the processing or, else, the destructor has been called. + std::unique_lock lock(locking_mutex); + cond_var.wait(lock, [this]{return has_work == false;}); +} + +inline stage1_worker::~stage1_worker() { + // The thread may never outlive the stage1_worker instance + // and will always be stopped/joined before the stage1_worker + // instance is gone. + stop_thread(); +} + +inline void stage1_worker::start_thread() { + std::unique_lock lock(locking_mutex); + if(thread.joinable()) { + return; // This should never happen but we never want to create more than one thread. + } + thread = std::thread([this]{ + while(true) { + std::unique_lock thread_lock(locking_mutex); + // We wait for either "run" or "stop_thread" to be called. + cond_var.wait(thread_lock, [this]{return has_work || !can_work;}); + // If, for some reason, the stop_thread() method was called (i.e., the + // destructor of stage1_worker is called, then we want to immediately destroy + // the thread (and not do any more processing). + if(!can_work) { + break; + } + this->owner->stage1_thread_error = this->owner->run_stage1(*this->stage1_thread_parser, + this->_next_batch_start); + this->has_work = false; + // The condition variable call should be moved after thread_lock.unlock() for performance + // reasons but thread sanitizers may report it as a data race if we do. + // See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock + cond_var.notify_one(); // will notify "finish" + thread_lock.unlock(); + } + } + ); +} + + +inline void stage1_worker::stop_thread() { + std::unique_lock lock(locking_mutex); + // We have to make sure that all locks can be released. + can_work = false; + has_work = false; + cond_var.notify_all(); + lock.unlock(); + if(thread.joinable()) { + thread.join(); + } +} + +inline void stage1_worker::run(document_stream * ds, parser * stage1, size_t next_batch_start) { + std::unique_lock lock(locking_mutex); + owner = ds; + _next_batch_start = next_batch_start; + stage1_thread_parser = stage1; + has_work = true; + // The condition variable call should be moved after thread_lock.unlock() for performance + // reasons but thread sanitizers may report it as a data race if we do. + // See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock + cond_var.notify_one(); // will notify the thread lock that we have work + lock.unlock(); +} + +#endif // SIMDJSON_THREADS_ENABLED + +simdjson_inline document_stream::document_stream( + ondemand::parser &_parser, + const uint8_t *_buf, size_t _len, size_t _batch_size, bool _allow_comma_separated @@ -59063,7 +61551,6 @@ simdjson_inline document_stream::iterator::iterator(document_stream* _stream, bo } simdjson_inline simdjson_result document_stream::iterator::operator*() noexcept { - //if(stream->error) { return stream->error; } return simdjson_result(stream->doc, stream->error); } @@ -60922,36 +63409,39 @@ simdjson_inline const char * raw_json_string::raw() const noexcept { return rein simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(std::string_view target) noexcept { size_t pos{0}; - // if the content has no escape character, just scan through it quickly! - for(;pos < target.size() && target[pos] != '\\';pos++) {} - // slow path may begin. - bool escaping{false}; - for(;pos < target.size();pos++) { - if((target[pos] == '"') && !escaping) { - return false; - } else if(target[pos] == '\\') { - escaping = !escaping; - } else { - escaping = false; + while(pos < target.size()) { + pos = target.find('"', pos); + if(pos == std::string_view::npos) { return true; } + if(pos != 0 && target[pos-1] != '\\') { return false; } + if(pos > 1 && target[pos-2] == '\\') { + size_t backslash_count{2}; + for(size_t i = 3; i <= pos; i++) { + if(target[pos-i] == '\\') { backslash_count++; } + else { break; } + } + if(backslash_count % 2 == 0) { return false; } } + pos++; } return true; } simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(const char* target) noexcept { size_t pos{0}; - // if the content has no escape character, just scan through it quickly! - for(;target[pos] && target[pos] != '\\';pos++) {} - // slow path may begin. - bool escaping{false}; - for(;target[pos];pos++) { - if((target[pos] == '"') && !escaping) { - return false; - } else if(target[pos] == '\\') { - escaping = !escaping; - } else { - escaping = false; + while(target[pos]) { + const char * result = strchr(target+pos, '"'); + if(result == nullptr) { return true; } + pos = result - target; + if(pos != 0 && target[pos-1] != '\\') { return false; } + if(pos > 1 && target[pos-2] == '\\') { + size_t backslash_count{2}; + for(size_t i = 3; i <= pos; i++) { + if(target[pos-i] == '\\') { backslash_count++; } + else { break; } + } + if(backslash_count % 2 == 0) { return false; } } + pos++; } return true; } @@ -60963,7 +63453,7 @@ simdjson_inline bool raw_json_string::unsafe_is_equal(size_t length, std::string } simdjson_inline bool raw_json_string::unsafe_is_equal(std::string_view target) const noexcept { - // Assumptions: does not contain unescaped quote characters, and + // Assumptions: does not contain unescaped quote characters("), and // the raw content is quote terminated within a valid JSON string. if(target.size() <= SIMDJSON_PADDING) { return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size()); @@ -61437,775 +63927,230 @@ simdjson_inline simdjson_result::simdjson_res #endif // SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H /* end file simdjson/generic/ondemand/token_iterator-inl.h for haswell */ -/* including simdjson/generic/ondemand/value-inl.h for haswell: #include "simdjson/generic/ondemand/value-inl.h" */ -/* begin file simdjson/generic/ondemand/value-inl.h for haswell */ -#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H +/* including simdjson/generic/ondemand/value_iterator-inl.h for haswell: #include "simdjson/generic/ondemand/value_iterator-inl.h" */ +/* begin file simdjson/generic/ondemand/value_iterator-inl.h for haswell */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ -/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/atomparsing.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/numberparsing.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string-inl.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { namespace haswell { namespace ondemand { -simdjson_inline value::value(const value_iterator &_iter) noexcept - : iter{_iter} +simdjson_inline value_iterator::value_iterator( + json_iterator *json_iter, + depth_t depth, + token_position start_position +) noexcept : _json_iter{json_iter}, _depth{depth}, _start_position{start_position} { } -simdjson_inline value value::start(const value_iterator &iter) noexcept { - return iter; -} -simdjson_inline value value::resume(const value_iterator &iter) noexcept { - return iter; -} - -simdjson_inline simdjson_result value::get_array() noexcept { - return array::start(iter); -} -simdjson_inline simdjson_result value::get_object() noexcept { - return object::start(iter); -} -simdjson_inline simdjson_result value::start_or_resume_object() noexcept { - if (iter.at_start()) { - return get_object(); - } else { - return object::resume(iter); - } -} -simdjson_inline simdjson_result value::get_raw_json_string() noexcept { - return iter.get_raw_json_string(); -} -simdjson_inline simdjson_result value::get_string(bool allow_replacement) noexcept { - return iter.get_string(allow_replacement); -} -template -simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept { - return iter.get_string(receiver, allow_replacement); -} -simdjson_inline simdjson_result value::get_wobbly_string() noexcept { - return iter.get_wobbly_string(); -} -simdjson_inline simdjson_result value::get_double() noexcept { - return iter.get_double(); -} -simdjson_inline simdjson_result value::get_double_in_string() noexcept { - return iter.get_double_in_string(); -} -simdjson_inline simdjson_result value::get_uint64() noexcept { - return iter.get_uint64(); -} -simdjson_inline simdjson_result value::get_uint64_in_string() noexcept { - return iter.get_uint64_in_string(); -} -simdjson_inline simdjson_result value::get_int64() noexcept { - return iter.get_int64(); -} -simdjson_inline simdjson_result value::get_int64_in_string() noexcept { - return iter.get_int64_in_string(); -} -simdjson_inline simdjson_result value::get_bool() noexcept { - return iter.get_bool(); -} -simdjson_inline simdjson_result value::is_null() noexcept { - return iter.is_null(); +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_object() noexcept { + SIMDJSON_TRY( start_container('{', "Not an object", "object") ); + return started_object(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_array(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_object(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_raw_json_string(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_string(false); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_number(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_double(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_uint64(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_int64(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_bool(); } -template simdjson_inline error_code value::get(T &out) noexcept { - return get().get(out); +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_root_object() noexcept { + SIMDJSON_TRY( start_container('{', "Not an object", "object") ); + return started_root_object(); } -#if SIMDJSON_EXCEPTIONS -template -simdjson_inline value::operator T() noexcept(false) { - return get(); -} -simdjson_inline value::operator array() noexcept(false) { - return get_array(); -} -simdjson_inline value::operator object() noexcept(false) { - return get_object(); -} -simdjson_inline value::operator uint64_t() noexcept(false) { - return get_uint64(); -} -simdjson_inline value::operator int64_t() noexcept(false) { - return get_int64(); -} -simdjson_inline value::operator double() noexcept(false) { - return get_double(); -} -simdjson_inline value::operator std::string_view() noexcept(false) { - return get_string(false); -} -simdjson_inline value::operator raw_json_string() noexcept(false) { - return get_raw_json_string(); -} -simdjson_inline value::operator bool() noexcept(false) { - return get_bool(); -} +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::started_object() noexcept { + assert_at_container_start(); +#if SIMDJSON_DEVELOPMENT_CHECKS + _json_iter->set_start_position(_depth, start_position()); #endif - -simdjson_inline simdjson_result value::begin() & noexcept { - return get_array().begin(); -} -simdjson_inline simdjson_result value::end() & noexcept { - return {}; -} -simdjson_inline simdjson_result value::count_elements() & noexcept { - simdjson_result answer; - auto a = get_array(); - answer = a.count_elements(); - // count_elements leaves you pointing inside the array, at the first element. - // We need to move back so that the user can create a new array (which requires that - // we point at '['). - iter.move_at_start(); - return answer; -} -simdjson_inline simdjson_result value::count_fields() & noexcept { - simdjson_result answer; - auto a = get_object(); - answer = a.count_fields(); - iter.move_at_start(); - return answer; -} -simdjson_inline simdjson_result value::at(size_t index) noexcept { - auto a = get_array(); - return a.at(index); -} - -simdjson_inline simdjson_result value::find_field(std::string_view key) noexcept { - return start_or_resume_object().find_field(key); -} -simdjson_inline simdjson_result value::find_field(const char *key) noexcept { - return start_or_resume_object().find_field(key); -} - -simdjson_inline simdjson_result value::find_field_unordered(std::string_view key) noexcept { - return start_or_resume_object().find_field_unordered(key); -} -simdjson_inline simdjson_result value::find_field_unordered(const char *key) noexcept { - return start_or_resume_object().find_field_unordered(key); -} - -simdjson_inline simdjson_result value::operator[](std::string_view key) noexcept { - return start_or_resume_object()[key]; -} -simdjson_inline simdjson_result value::operator[](const char *key) noexcept { - return start_or_resume_object()[key]; -} - -simdjson_inline simdjson_result value::type() noexcept { - return iter.type(); + if (*_json_iter->peek() == '}') { + logger::log_value(*_json_iter, "empty object"); + _json_iter->return_current_and_advance(); + end_container(); + return false; + } + return true; } -simdjson_inline simdjson_result value::is_scalar() noexcept { - json_type this_type; - auto error = type().get(this_type); - if(error) { return error; } - return ! ((this_type == json_type::array) || (this_type == json_type::object)); +simdjson_warn_unused simdjson_inline error_code value_iterator::check_root_object() noexcept { + // When in streaming mode, we cannot expect peek_last() to be the last structural element of the + // current document. It only works in the normal mode where we have indexed a single document. + // Note that adding a check for 'streaming' is not expensive since we only have at most + // one root element. + if ( ! _json_iter->streaming() ) { + // The following lines do not fully protect against garbage content within the + // object: e.g., `{"a":2} foo }`. Users concerned with garbage content should + // call `at_end()` on the document instance at the end of the processing to + // ensure that the processing has finished at the end. + // + if (*_json_iter->peek_last() != '}') { + _json_iter->abandon(); + return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing } at end"); + } + // If the last character is } *and* the first gibberish character is also '}' + // then on-demand could accidentally go over. So we need additional checks. + // https://github.com/simdjson/simdjson/issues/1834 + // Checking that the document is balanced requires a full scan which is potentially + // expensive, but it only happens in edge cases where the first padding character is + // a closing bracket. + if ((*_json_iter->peek(_json_iter->end_position()) == '}') && (!_json_iter->balanced())) { + _json_iter->abandon(); + // The exact error would require more work. It will typically be an unclosed object. + return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "the document is unbalanced"); + } + } + return SUCCESS; } -simdjson_inline simdjson_result value::is_string() noexcept { - json_type this_type; - auto error = type().get(this_type); +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::started_root_object() noexcept { + auto error = check_root_object(); if(error) { return error; } - return (this_type == json_type::string); -} - - -simdjson_inline bool value::is_negative() noexcept { - return iter.is_negative(); + return started_object(); } -simdjson_inline simdjson_result value::is_integer() noexcept { - return iter.is_integer(); -} -simdjson_warn_unused simdjson_inline simdjson_result value::get_number_type() noexcept { - return iter.get_number_type(); -} -simdjson_warn_unused simdjson_inline simdjson_result value::get_number() noexcept { - return iter.get_number(); +simdjson_warn_unused simdjson_inline error_code value_iterator::end_container() noexcept { +#if SIMDJSON_CHECK_EOF + if (depth() > 1 && at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing parent ] or }"); } + // if (depth() <= 1 && !at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing [ or { at start"); } +#endif // SIMDJSON_CHECK_EOF + _json_iter->ascend_to(depth()-1); + return SUCCESS; } -simdjson_inline std::string_view value::raw_json_token() noexcept { - return std::string_view(reinterpret_cast(iter.peek_start()), iter.peek_start_length()); -} +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::has_next_field() noexcept { + assert_at_next(); -simdjson_inline simdjson_result value::raw_json() noexcept { - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) - { - case json_type::array: { - ondemand::array array; - SIMDJSON_TRY(get_array().get(array)); - return array.raw_json(); - } - case json_type::object: { - ondemand::object object; - SIMDJSON_TRY(get_object().get(object)); - return object.raw_json(); - } + // It's illegal to call this unless there are more tokens: anything that ends in } or ] is + // obligated to verify there are more tokens if they are not the top level. + switch (*_json_iter->return_current_and_advance()) { + case '}': + logger::log_end_value(*_json_iter, "object"); + SIMDJSON_TRY( end_container() ); + return false; + case ',': + return true; default: - return raw_json_token(); + return report_error(TAPE_ERROR, "Missing comma between object fields"); } } -simdjson_inline simdjson_result value::current_location() noexcept { - return iter.json_iter().current_location(); -} - -simdjson_inline int32_t value::current_depth() const noexcept{ - return iter.json_iter().depth(); -} +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::find_field_raw(const std::string_view key) noexcept { + error_code error; + bool has_value; + // + // Initially, the object can be in one of a few different places: + // + // 1. The start of the object, at the first field: + // + // ``` + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 2, index 1) + // ``` + if (at_first_field()) { + has_value = true; -inline bool is_pointer_well_formed(std::string_view json_pointer) noexcept { - if (simdjson_unlikely(json_pointer.empty())) { // can't be - return false; - } - if (simdjson_unlikely(json_pointer[0] != '/')) { - return false; - } - size_t escape = json_pointer.find('~'); - if (escape == std::string_view::npos) { - return true; - } - if (escape == json_pointer.size() - 1) { - return false; - } - if (json_pointer[escape + 1] != '0' && json_pointer[escape + 1] != '1') { + // + // 2. When a previous search did not yield a value or the object is empty: + // + // ``` + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 0) + // { } + // ^ (depth 0, index 2) + // ``` + // + } else if (!is_open()) { +#if SIMDJSON_DEVELOPMENT_CHECKS + // If we're past the end of the object, we're being iterated out of order. + // Note: this is not perfect detection. It's possible the user is inside some other object; if so, + // this object iterator will blithely scan that object for fields. + if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; } +#endif return false; - } - return true; -} -simdjson_inline simdjson_result value::at_pointer(std::string_view json_pointer) noexcept { - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) - { - case json_type::array: - return (*this).get_array().at_pointer(json_pointer); - case json_type::object: - return (*this).get_object().at_pointer(json_pointer); - default: - // a non-empty string can be invalid, or accessing a primitive (issue 2154) - if (is_pointer_well_formed(json_pointer)) { - return NO_SUCH_FIELD; - } - return INVALID_JSON_POINTER; + // 3. When a previous search found a field or an iterator yielded a value: + // + // ``` + // // When a field was not fully consumed (or not even touched at all) + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 2) + // // When a field was fully consumed + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 1) + // // When the last field was fully consumed + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 1) + // ``` + // + } else { + if ((error = skip_child() )) { abandon(); return error; } + if ((error = has_next_field().get(has_value) )) { abandon(); return error; } +#if SIMDJSON_DEVELOPMENT_CHECKS + if (_json_iter->start_position(_depth) != start_position()) { return OUT_OF_ORDER_ITERATION; } +#endif } -} + while (has_value) { + // Get the key and colon, stopping at the value. + raw_json_string actual_key; + // size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes + // Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2. + // field_key() advances the pointer and checks that '"' is found (corresponding to a key). + // The depth is left unchanged by field_key(). + if ((error = field_key().get(actual_key) )) { abandon(); return error; }; + // field_value() will advance and check that we find a ':' separating the + // key and the value. It will also increment the depth by one. + if ((error = field_value() )) { abandon(); return error; } + // If it matches, stop and return + // We could do it this way if we wanted to allow arbitrary + // key content (including escaped quotes). + //if (actual_key.unsafe_is_equal(max_key_length, key)) { + // Instead we do the following which may trigger buffer overruns if the + // user provides an adversarial key (containing a well placed unescaped quote + // character and being longer than the number of bytes remaining in the JSON + // input). + if (actual_key.unsafe_is_equal(key)) { + logger::log_event(*this, "match", key, -2); + // If we return here, then we return while pointing at the ':' that we just checked. + return true; + } -simdjson_inline simdjson_result value::at_path(std::string_view json_path) noexcept { - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) { - case json_type::array: - return (*this).get_array().at_path(json_path); - case json_type::object: - return (*this).get_object().at_path(json_path); - default: - return INVALID_JSON_POINTER; + // No match: skip the value and see if , or } is next + logger::log_event(*this, "no match", key, -2); + // The call to skip_child is meant to skip over the value corresponding to the key. + // After skip_child(), we are right before the next comma (',') or the final brace ('}'). + SIMDJSON_TRY( skip_child() ); // Skip the value entirely + // The has_next_field() advances the pointer and check that either ',' or '}' is found. + // It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found, + // then we are in error and we abort. + if ((error = has_next_field().get(has_value) )) { abandon(); return error; } } -} - -} // namespace ondemand -} // namespace haswell -} // namespace simdjson - -namespace simdjson { -simdjson_inline simdjson_result::simdjson_result( - haswell::ondemand::value &&value -) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) -{ -} -simdjson_inline simdjson_result::simdjson_result( - error_code error -) noexcept : - implementation_simdjson_result_base(error) -{ -} -simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); -} -simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); -} -simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { - if (error()) { return error(); } - return first.at(index); -} -simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); -} -simdjson_inline simdjson_result simdjson_result::end() & noexcept { - if (error()) { return error(); } - return {}; + // If the loop ended, we're out of fields to look at. + return false; } -simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) noexcept { - if (error()) { return error(); } - return first.find_field(key); -} -simdjson_inline simdjson_result simdjson_result::find_field(const char *key) noexcept { - if (error()) { return error(); } - return first.find_field(key); -} +SIMDJSON_PUSH_DISABLE_WARNINGS +SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::find_field_unordered_raw(const std::string_view key) noexcept { + /** + * When find_field_unordered_raw is called, we can either be pointing at the + * first key, pointing outside (at the closing brace) or if a key was matched + * we can be either pointing right afterthe ':' right before the value (that we need skip), + * or we may have consumed the value and we might be at a comma or at the + * final brace (ready for a call to has_next_field()). + */ + error_code error; + bool has_value; -simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); -} -simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); -} - -simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) noexcept { - if (error()) { return error(); } - return first[key]; -} -simdjson_inline simdjson_result simdjson_result::operator[](const char *key) noexcept { - if (error()) { return error(); } - return first[key]; -} - -simdjson_inline simdjson_result simdjson_result::get_array() noexcept { - if (error()) { return error(); } - return first.get_array(); -} -simdjson_inline simdjson_result simdjson_result::get_object() noexcept { - if (error()) { return error(); } - return first.get_object(); -} -simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { - if (error()) { return error(); } - return first.get_uint64(); -} -simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { - if (error()) { return error(); } - return first.get_uint64_in_string(); -} -simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { - if (error()) { return error(); } - return first.get_int64(); -} -simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { - if (error()) { return error(); } - return first.get_int64_in_string(); -} -simdjson_inline simdjson_result simdjson_result::get_double() noexcept { - if (error()) { return error(); } - return first.get_double(); -} -simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { - if (error()) { return error(); } - return first.get_double_in_string(); -} -simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(allow_replacement); -} -template -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(receiver, allow_replacement); -} -simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { - if (error()) { return error(); } - return first.get_wobbly_string(); -} -simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { - if (error()) { return error(); } - return first.get_raw_json_string(); -} -simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { - if (error()) { return error(); } - return first.get_bool(); -} -simdjson_inline simdjson_result simdjson_result::is_null() noexcept { - if (error()) { return error(); } - return first.is_null(); -} - -template simdjson_inline simdjson_result simdjson_result::get() noexcept { - if (error()) { return error(); } - return first.get(); -} -template simdjson_inline error_code simdjson_result::get(T &out) noexcept { - if (error()) { return error(); } - return first.get(out); -} - -template<> simdjson_inline simdjson_result simdjson_result::get() noexcept { - if (error()) { return error(); } - return std::move(first); -} -template<> simdjson_inline error_code simdjson_result::get(haswell::ondemand::value &out) noexcept { - if (error()) { return error(); } - out = first; - return SUCCESS; -} - -simdjson_inline simdjson_result simdjson_result::type() noexcept { - if (error()) { return error(); } - return first.type(); -} -simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { - if (error()) { return error(); } - return first.is_scalar(); -} -simdjson_inline simdjson_result simdjson_result::is_string() noexcept { - if (error()) { return error(); } - return first.is_string(); -} -simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { - if (error()) { return error(); } - return first.is_negative(); -} -simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { - if (error()) { return error(); } - return first.is_integer(); -} -simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { - if (error()) { return error(); } - return first.get_number_type(); -} -simdjson_inline simdjson_result simdjson_result::get_number() noexcept { - if (error()) { return error(); } - return first.get_number(); -} -#if SIMDJSON_EXCEPTIONS -template -simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return static_cast(first); -} -simdjson_inline simdjson_result::operator haswell::ondemand::array() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator haswell::ondemand::object() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator int64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator double() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator haswell::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator bool() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -#endif - -simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { - if (error()) { return error(); } - return first.raw_json_token(); -} - -simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { - if (error()) { return error(); } - return first.raw_json(); -} - -simdjson_inline simdjson_result simdjson_result::current_location() noexcept { - if (error()) { return error(); } - return first.current_location(); -} - -simdjson_inline simdjson_result simdjson_result::current_depth() const noexcept { - if (error()) { return error(); } - return first.current_depth(); -} - -simdjson_inline simdjson_result simdjson_result::at_pointer( - std::string_view json_pointer) noexcept { - if (error()) { - return error(); - } - return first.at_pointer(json_pointer); -} - -simdjson_inline simdjson_result simdjson_result::at_path( - std::string_view json_path) noexcept { - if (error()) { - return error(); - } - return first.at_path(json_path); -} - -} // namespace simdjson - -#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H -/* end file simdjson/generic/ondemand/value-inl.h for haswell */ -/* including simdjson/generic/ondemand/value_iterator-inl.h for haswell: #include "simdjson/generic/ondemand/value_iterator-inl.h" */ -/* begin file simdjson/generic/ondemand/value_iterator-inl.h for haswell */ -#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H - -/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ -/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/atomparsing.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/numberparsing.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string-inl.h" */ -/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -namespace simdjson { -namespace haswell { -namespace ondemand { - -simdjson_inline value_iterator::value_iterator( - json_iterator *json_iter, - depth_t depth, - token_position start_position -) noexcept : _json_iter{json_iter}, _depth{depth}, _start_position{start_position} -{ -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_object() noexcept { - SIMDJSON_TRY( start_container('{', "Not an object", "object") ); - return started_object(); -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_root_object() noexcept { - SIMDJSON_TRY( start_container('{', "Not an object", "object") ); - return started_root_object(); -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::started_object() noexcept { - assert_at_container_start(); -#if SIMDJSON_DEVELOPMENT_CHECKS - _json_iter->set_start_position(_depth, start_position()); -#endif - if (*_json_iter->peek() == '}') { - logger::log_value(*_json_iter, "empty object"); - _json_iter->return_current_and_advance(); - end_container(); - return false; - } - return true; -} - -simdjson_warn_unused simdjson_inline error_code value_iterator::check_root_object() noexcept { - // When in streaming mode, we cannot expect peek_last() to be the last structural element of the - // current document. It only works in the normal mode where we have indexed a single document. - // Note that adding a check for 'streaming' is not expensive since we only have at most - // one root element. - if ( ! _json_iter->streaming() ) { - // The following lines do not fully protect against garbage content within the - // object: e.g., `{"a":2} foo }`. Users concerned with garbage content should - // call `at_end()` on the document instance at the end of the processing to - // ensure that the processing has finished at the end. - // - if (*_json_iter->peek_last() != '}') { - _json_iter->abandon(); - return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing } at end"); - } - // If the last character is } *and* the first gibberish character is also '}' - // then on-demand could accidentally go over. So we need additional checks. - // https://github.com/simdjson/simdjson/issues/1834 - // Checking that the document is balanced requires a full scan which is potentially - // expensive, but it only happens in edge cases where the first padding character is - // a closing bracket. - if ((*_json_iter->peek(_json_iter->end_position()) == '}') && (!_json_iter->balanced())) { - _json_iter->abandon(); - // The exact error would require more work. It will typically be an unclosed object. - return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "the document is unbalanced"); - } - } - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::started_root_object() noexcept { - auto error = check_root_object(); - if(error) { return error; } - return started_object(); -} - -simdjson_warn_unused simdjson_inline error_code value_iterator::end_container() noexcept { -#if SIMDJSON_CHECK_EOF - if (depth() > 1 && at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing parent ] or }"); } - // if (depth() <= 1 && !at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing [ or { at start"); } -#endif // SIMDJSON_CHECK_EOF - _json_iter->ascend_to(depth()-1); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::has_next_field() noexcept { - assert_at_next(); - - // It's illegal to call this unless there are more tokens: anything that ends in } or ] is - // obligated to verify there are more tokens if they are not the top level. - switch (*_json_iter->return_current_and_advance()) { - case '}': - logger::log_end_value(*_json_iter, "object"); - SIMDJSON_TRY( end_container() ); - return false; - case ',': - return true; - default: - return report_error(TAPE_ERROR, "Missing comma between object fields"); - } -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::find_field_raw(const std::string_view key) noexcept { - error_code error; - bool has_value; - // - // Initially, the object can be in one of a few different places: - // - // 1. The start of the object, at the first field: - // - // ``` - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 2, index 1) - // ``` - if (at_first_field()) { - has_value = true; - - // - // 2. When a previous search did not yield a value or the object is empty: - // - // ``` - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 0) - // { } - // ^ (depth 0, index 2) - // ``` - // - } else if (!is_open()) { -#if SIMDJSON_DEVELOPMENT_CHECKS - // If we're past the end of the object, we're being iterated out of order. - // Note: this is not perfect detection. It's possible the user is inside some other object; if so, - // this object iterator will blithely scan that object for fields. - if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; } -#endif - return false; - - // 3. When a previous search found a field or an iterator yielded a value: - // - // ``` - // // When a field was not fully consumed (or not even touched at all) - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 2) - // // When a field was fully consumed - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 1) - // // When the last field was fully consumed - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 1) - // ``` - // - } else { - if ((error = skip_child() )) { abandon(); return error; } - if ((error = has_next_field().get(has_value) )) { abandon(); return error; } -#if SIMDJSON_DEVELOPMENT_CHECKS - if (_json_iter->start_position(_depth) != start_position()) { return OUT_OF_ORDER_ITERATION; } -#endif - } - while (has_value) { - // Get the key and colon, stopping at the value. - raw_json_string actual_key; - // size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes - // Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2. - // field_key() advances the pointer and checks that '"' is found (corresponding to a key). - // The depth is left unchanged by field_key(). - if ((error = field_key().get(actual_key) )) { abandon(); return error; }; - // field_value() will advance and check that we find a ':' separating the - // key and the value. It will also increment the depth by one. - if ((error = field_value() )) { abandon(); return error; } - // If it matches, stop and return - // We could do it this way if we wanted to allow arbitrary - // key content (including escaped quotes). - //if (actual_key.unsafe_is_equal(max_key_length, key)) { - // Instead we do the following which may trigger buffer overruns if the - // user provides an adversarial key (containing a well placed unescaped quote - // character and being longer than the number of bytes remaining in the JSON - // input). - if (actual_key.unsafe_is_equal(key)) { - logger::log_event(*this, "match", key, -2); - // If we return here, then we return while pointing at the ':' that we just checked. - return true; - } - - // No match: skip the value and see if , or } is next - logger::log_event(*this, "no match", key, -2); - // The call to skip_child is meant to skip over the value corresponding to the key. - // After skip_child(), we are right before the next comma (',') or the final brace ('}'). - SIMDJSON_TRY( skip_child() ); // Skip the value entirely - // The has_next_field() advances the pointer and check that either ',' or '}' is found. - // It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found, - // then we are in error and we abort. - if ((error = has_next_field().get(has_value) )) { abandon(); return error; } - } - - // If the loop ended, we're out of fields to look at. - return false; -} - -SIMDJSON_PUSH_DISABLE_WARNINGS -SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::find_field_unordered_raw(const std::string_view key) noexcept { - /** - * When find_field_unordered_raw is called, we can either be pointing at the - * first key, pointing outside (at the closing brace) or if a key was matched - * we can be either pointing right afterthe ':' right before the value (that we need skip), - * or we may have consumed the value and we might be at a comma or at the - * final brace (ready for a call to has_next_field()). - */ - error_code error; - bool has_value; - - // First, we scan from that point to the end. - // If we don't find a match, we may loop back around, and scan from the beginning to that point. - token_position search_start = _json_iter->position(); + // First, we scan from that point to the end. + // If we don't find a match, we may loop back around, and scan from the beginning to that point. + token_position search_start = _json_iter->position(); // We want to know whether we need to go back to the beginning. bool at_first = at_first_field(); @@ -62785,6 +64730,8 @@ simdjson_inline simdjson_result value_iterator::is_root_null(bool check_tr if(result) { // we have something that looks like a null. if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; } advance_root_scalar("null"); + } else if (json[0] == 'n') { + return incorrect_type_error("Not a null but starts with n"); } return result; } @@ -63076,6 +65023,8 @@ simdjson_inline simdjson_result::simdjson_res #endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H /* end file simdjson/generic/ondemand/value_iterator-inl.h for haswell */ + + /* end file simdjson/generic/ondemand/amalgamated.h for haswell */ /* including simdjson/haswell/end.h: #include "simdjson/haswell/end.h" */ /* begin file simdjson/haswell/end.h */ @@ -63844,6 +65793,132 @@ class value_iterator; #endif // SIMDJSON_GENERIC_ONDEMAND_BASE_H /* end file simdjson/generic/ondemand/base.h for icelake */ +/* including simdjson/generic/ondemand/deserialize.h for icelake: #include "simdjson/generic/ondemand/deserialize.h" */ +/* begin file simdjson/generic/ondemand/deserialize.h for icelake */ +#if SIMDJSON_SUPPORTS_DESERIALIZATION + +#ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_ONDEMAND_DESERIALIZE_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +namespace simdjson { + +namespace tag_invoke_fn_ns { +void tag_invoke(); + +struct tag_invoke_fn { + template + requires requires(Tag tag, Args &&...args) { + tag_invoke(std::forward(tag), std::forward(args)...); + } + constexpr auto operator()(Tag tag, Args &&...args) const + noexcept(noexcept(tag_invoke(std::forward(tag), + std::forward(args)...))) + -> decltype(tag_invoke(std::forward(tag), + std::forward(args)...)) { + return tag_invoke(std::forward(tag), std::forward(args)...); + } +}; +} // namespace tag_invoke_fn_ns + +inline namespace tag_invoke_ns { +inline constexpr tag_invoke_fn_ns::tag_invoke_fn tag_invoke = {}; +} // namespace tag_invoke_ns + +template +concept tag_invocable = requires(Tag tag, Args... args) { + tag_invoke(std::forward(tag), std::forward(args)...); +}; + +template +concept nothrow_tag_invocable = + tag_invocable && requires(Tag tag, Args... args) { + { + tag_invoke(std::forward(tag), std::forward(args)...) + } noexcept; + }; + +template +using tag_invoke_result = + std::invoke_result; + +template +using tag_invoke_result_t = + std::invoke_result_t; + +template using tag_t = std::decay_t; + + +struct deserialize_tag; + +/// These types are deserializable in a built-in way +template struct is_builtin_deserializable : std::false_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; + +template +concept is_builtin_deserializable_v = is_builtin_deserializable::value; + +template +concept custom_deserializable = tag_invocable; + +template +concept deserializable = custom_deserializable || is_builtin_deserializable_v; + +template +concept nothrow_custom_deserializable = nothrow_tag_invocable; + +// built-in types are noexcept and if an error happens, the value simply gets ignored and the error is returned. +template +concept nothrow_deserializable = nothrow_custom_deserializable || is_builtin_deserializable_v; + +/// Deserialize Tag +inline constexpr struct deserialize_tag { + using value_type = icelake::ondemand::value; + using document_type = icelake::ondemand::document; + using document_reference_type = icelake::ondemand::document_reference; + + // Customization Point for value + template + requires custom_deserializable + [[nodiscard]] constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for document + template + requires custom_deserializable + [[nodiscard]] constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for document reference + template + requires custom_deserializable + [[nodiscard]] constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + +} deserialize{}; + +} // namespace simdjson + +#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION + +/* end file simdjson/generic/ondemand/deserialize.h for icelake */ /* including simdjson/generic/ondemand/value_iterator.h for icelake: #include "simdjson/generic/ondemand/value_iterator.h" */ /* begin file simdjson/generic/ondemand/value_iterator.h for icelake */ #ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H @@ -64348,12 +66423,15 @@ struct simdjson_result : public icelake::impl /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ +#include + namespace simdjson { + namespace icelake { namespace ondemand { - /** * An ephemeral JSON value returned during iteration. It is only valid for as long as you do * not access more data in the JSON document. @@ -64378,16 +66456,21 @@ class value { * @returns A value of the given type, parsed from the JSON. * @returns INCORRECT_TYPE If the JSON value is not the given type. */ - template simdjson_inline simdjson_result get() noexcept { - // Unless the simdjson library or the user provides an inline implementation, calling this method should - // immediately fail. - static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " - "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " - "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." - " You may also add support for custom types, see our documentation."); + template + simdjson_inline simdjson_result get() +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; } + /** * Get this value as the given type. * @@ -64397,7 +66480,32 @@ class value { * @returns INCORRECT_TYPE If the JSON value is not an object. * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ - template simdjson_inline error_code get(T &out) noexcept; + template + simdjson_inline error_code get(T &out) +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_DESERIALIZATION + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION + // Unless the simdjson library or the user provides an inline implementation, calling this method should + // immediately fail. + static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " + "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " + "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." + " You may also add support for custom types, see our documentation."); + static_cast(out); // to get rid of unused errors + return UNINITIALIZED; +#if SIMDJSON_SUPPORTS_DESERIALIZATION + } +#endif + } /** * Cast this JSON value to an array. @@ -64473,6 +66581,17 @@ class value { * Important: a value should be consumed once. Calling get_string() twice on the same value * is an error. * + * In some instances, you may want to allow replacement of invalid Unicode sequences. + * You may do so by passing the allow_replacement parameter as true. In the following + * example, the string "431924697b\udff0L\u0001Y" is not valid Unicode. By passing true + * to get_string, we allow the replacement of the invalid Unicode sequences with the Unicode + * replacement character (U+FFFD). + * + * simdjson::ondemand::parser parser; + * auto json = R"({"deviceId":"431924697b\udff0L\u0001Y"})"_padded; + * simdjson::ondemand::document doc = parser.iterate(json); + * auto view = doc["deviceId"].get_string(true); + * * @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next * time it parses a document or when it is destroyed. * @returns INCORRECT_TYPE if the JSON value is not a string. @@ -64724,6 +66843,7 @@ class value { simdjson_inline simdjson_result operator[](std::string_view key) noexcept; /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ simdjson_inline simdjson_result operator[](const char *key) noexcept; + simdjson_result operator[](int) noexcept = delete; /** * Get the type of this JSON value. It does not validate or consume the value. @@ -65091,6 +67211,7 @@ struct simdjson_result : public icelake::implementatio simdjson_inline simdjson_result operator[](std::string_view key) noexcept; /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ simdjson_inline simdjson_result operator[](const char *key) noexcept; + simdjson_result operator[](int) noexcept = delete; /** * Get the type of this JSON value. @@ -66146,6 +68267,22 @@ class parser { * using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the * SIMDJSON_PADDING bytes to avoid runtime warnings. * + * ### std::string references + * + * If you pass a mutable std::string reference (std::string&), the parser will seek to extend + * its capacity to SIMDJSON_PADDING bytes beyond the end of the string. + * + * Whenever you pass an std::string reference, the parser will access the bytes beyond the end of + * the string but before the end of the allocated memory (std::string::capacity()). + * If you are using a sanitizer that checks for reading uninitialized bytes or std::string's + * container-overflow checks, you may encounter sanitizer warnings. + * You can safely ignore these warnings. Or you can call simdjson::pad(std::string&) to pad the + * string with SIMDJSON_PADDING spaces: this function returns a simdjson::padding_string_view + * which can be be passed to the parser's iterate function: + * + * std::string json = R"({ "foo": 1 } { "foo": 2 } { "foo": 3 } )"; + * document doc = parser.iterate(simdjson::pad(json)); + * * @param json The JSON to parse. * @param len The length of the JSON. * @param capacity The number of bytes allocated in the JSON (must be at least len+SIMDJSON_PADDING). @@ -66340,8 +68477,12 @@ class parser { * behavior of the parser for future operations. */ bool threaded{true}; + #else + /** + * When SIMDJSON_THREADS_ENABLED is not defined, the parser instance cannot use threads. + */ + bool threaded{false}; #endif - /** * Unescape this JSON string, replacing \\ with \, \n with newline, etc. to a user-provided buffer. * The result must be valid UTF-8. @@ -66483,7 +68624,8 @@ class array { * calling this function, if successful, the array is 'rewinded' at its * beginning as if it had never been accessed. If the JSON is malformed (e.g., * there is a missing comma), then an error is returned and it is no longer - * safe to continue. + * safe to continue. Note that count_elements() does not validate the JSON values, + * only the structure of the array. * * To check that an array is empty, it is more performant to use * the is_empty() method. @@ -66761,8 +68903,11 @@ struct simdjson_result : public icelake::impl /* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + namespace simdjson { namespace icelake { namespace ondemand { @@ -66935,24 +69080,39 @@ class document { * @returns A value of the given type, parsed from the JSON. * @returns INCORRECT_TYPE If the JSON value is not the given type. */ - template simdjson_inline simdjson_result get() & noexcept { - // Unless the simdjson library or the user provides an inline implementation, calling this method should - // immediately fail. - static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " - "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " - "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." - " You may also add support for custom types, see our documentation."); - } - /** @overload template simdjson_result get() & noexcept */ - template simdjson_deprecated simdjson_inline simdjson_result get() && noexcept { - // Unless the simdjson library or the user provides an inline implementation, calling this method should - // immediately fail. - static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " - "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " - "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." - " You may also add support for custom types, see our documentation."); + template + simdjson_inline simdjson_result get() & +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "Cannot initialize the specified type."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + /** + * @overload template simdjson_result get() & noexcept + * + * We disallow the use tag_invoke CPO on a moved document; it may create UB + * if user uses `ondemand::array` or `ondemand::object` in their custom type. + * + * The member function is still remains specialize-able for compatibility + * reasons, but we completely disallow its use when a tag_invoke customization + * is provided. + */ + template + simdjson_inline simdjson_result get() && +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(!std::is_same::value && !std::is_same::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document being alive; that would be Undefined Behaviour."); + return static_cast(*this).get(); } /** @@ -66966,7 +69126,32 @@ class document { * @returns INCORRECT_TYPE If the JSON value is not an object. * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ - template simdjson_inline error_code get(T &out) & noexcept; + template + simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_DESERIALIZATION + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION + // Unless the simdjson library or the user provides an inline implementation, calling this method should + // immediately fail. + static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " + "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " + "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." + " You may also add support for custom types, see our documentation."); + static_cast(out); // to get rid of unused errors + return UNINITIALIZED; +#if SIMDJSON_SUPPORTS_DESERIALIZATION + } +#endif + } /** @overload template error_code get(T &out) & noexcept */ template simdjson_deprecated simdjson_inline error_code get(T &out) && noexcept; @@ -67068,7 +69253,8 @@ class document { * calling this function, if successful, the array is 'rewinded' at its * beginning as if it had never been accessed. If the JSON is malformed (e.g., * there is a missing comma), then an error is returned and it is no longer - * safe to continue. + * safe to continue. Note that count_elements() does not validate the JSON values, + * only the structure of the array. */ simdjson_inline simdjson_result count_elements() & noexcept; /** @@ -67180,6 +69366,7 @@ class document { simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; /** * Get the type of this JSON value. It does not validate or consume the value. @@ -67448,6 +69635,11 @@ class document { /** * A document_reference is a thin wrapper around a document reference instance. + * The document_reference instances are used primarily/solely for streams of JSON + * documents. They differ from document instances when parsing a scalar value + * (a document that is not an array or an object). In the case of a document, + * we expect the document to be fully consumed. In the case of a document_reference, + * we allow trailing content. */ class document_reference { public: @@ -67473,7 +69665,70 @@ class document_reference { simdjson_inline simdjson_result get_value() noexcept; simdjson_inline simdjson_result is_null() noexcept; - template simdjson_inline simdjson_result get() & noexcept; + template + simdjson_inline simdjson_result get() & +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "Cannot initialize the specified type."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + template + simdjson_inline simdjson_result get() && +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(!std::is_same::value && !std::is_same::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document_reference being alive; that would be Undefined Behaviour."); + return static_cast(*this).get(); + } + + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool, value + * + * Be mindful that the document instance must remain in scope while you are accessing object, array and value instances. + * + * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. + * @returns INCORRECT_TYPE If the JSON value is not an object. + * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. + */ + template + simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_DESERIALIZATION + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION + // Unless the simdjson library or the user provides an inline implementation, calling this method should + // immediately fail. + static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " + "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " + "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." + " You may also add support for custom types, see our documentation."); + static_cast(out); // to get rid of unused errors + return UNINITIALIZED; +#if SIMDJSON_SUPPORTS_DESERIALIZATION + } +#endif + } + /** @overload template error_code get(T &out) & noexcept */ + template simdjson_inline error_code get(T &out) && noexcept; simdjson_inline simdjson_result raw_json() noexcept; simdjson_inline operator document&() const noexcept; #if SIMDJSON_EXCEPTIONS @@ -67498,6 +69753,7 @@ class document_reference { simdjson_inline simdjson_result find_field(const char *key) & noexcept; simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; @@ -67576,6 +69832,7 @@ struct simdjson_result : public icelake::implementa simdjson_inline simdjson_result find_field(const char *key) & noexcept; simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; simdjson_inline simdjson_result type() noexcept; @@ -67625,8 +69882,14 @@ struct simdjson_result : public icelake:: simdjson_inline simdjson_result get_bool() noexcept; simdjson_inline simdjson_result get_value() noexcept; simdjson_inline simdjson_result is_null() noexcept; + + template simdjson_inline simdjson_result get() & noexcept; + template simdjson_inline simdjson_result get() && noexcept; + + template simdjson_inline error_code get(T &out) & noexcept; + template simdjson_inline error_code get(T &out) && noexcept; #if SIMDJSON_EXCEPTIONS - template ::value == false>::type> + template explicit simdjson_inline operator T() noexcept(false); simdjson_inline operator icelake::ondemand::array() & noexcept(false); simdjson_inline operator icelake::ondemand::object() & noexcept(false); @@ -67647,6 +69910,7 @@ struct simdjson_result : public icelake:: simdjson_inline simdjson_result find_field(const char *key) & noexcept; simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; simdjson_inline simdjson_result type() noexcept; @@ -68577,6 +70841,177 @@ inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result +#include + +namespace simdjson { +template +constexpr bool require_custom_serialization = false; + +////////////////////////////// +// Number deserialization +////////////////////////////// + +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + using limits = std::numeric_limits; + + uint64_t x; + SIMDJSON_TRY(val.get_uint64().get(x)); + if (x > (limits::max)()) { + return NUMBER_OUT_OF_RANGE; + } + out = static_cast(x); + return SUCCESS; +} + +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + double x; + SIMDJSON_TRY(val.get_double().get(x)); + out = static_cast(x); + return SUCCESS; +} + +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + using limits = std::numeric_limits; + + int64_t x; + SIMDJSON_TRY(val.get_int64().get(x)); + if (x > (limits::max)() || x < (limits::min)()) { + return NUMBER_OUT_OF_RANGE; + } + out = static_cast(x); + return SUCCESS; +} + +/** + * STL containers have several constructors including one that takes a single + * size argument. Thus, some compilers (Visual Studio) will not be able to + * disambiguate between the size and container constructor. Users should + * explicitly specify the type of the container as needed: e.g., + * doc.get>(). + */ +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { + using value_type = typename std::remove_cvref_t::value_type; + static_assert( + deserializable, + "The specified type inside the container must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the container must default constructible."); + + icelake::ondemand::array arr; + SIMDJSON_TRY(val.get_array().get(arr)); + for (auto v : arr) { + if constexpr (concepts::returns_reference) { + if (auto const err = v.get().get(concepts::emplace_one(out)); + err) { + // If an error occurs, the empty element that we just inserted gets + // removed. We're not using a temp variable because if T is a heavy + // type, we want the valid path to be the fast path and the slow path be + // the path that has errors in it. + if constexpr (requires { out.pop_back(); }) { + static_cast(out.pop_back()); + } + return err; + } + } else { + value_type temp; + if (auto const err = v.get().get(temp); err) { + return err; + } + concepts::emplace_one(out, std::move(temp)); + } + } + return SUCCESS; +} + + + +/** + * This CPO (Customization Point Object) will help deserialize into + * smart pointers. + * + * If constructing T is nothrow, this conversion should be nothrow as well since + * we return MEMALLOC if we're not able to allocate memory instead of throwing + * the error message. + * + * @tparam T The type inside the smart pointer + * @tparam ValT document/value type + * @param val document/value + * @param out a reference to the smart pointer + * @return status of the conversion + */ +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::element_type, ValT>) { + using element_type = typename std::remove_cvref_t::element_type; + + // For better error messages, don't use these as constraints on + // the tag_invoke CPO. + static_assert( + deserializable, + "The specified type inside the unique_ptr must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the unique_ptr must default constructible."); + + auto ptr = new (std::nothrow) element_type(); + if (ptr == nullptr) { + return MEMALLOC; + } + SIMDJSON_TRY(val.template get(*ptr)); + out.reset(ptr); + return SUCCESS; +} + +/** + * This CPO (Customization Point Object) will help deserialize into optional types. + */ +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::value_type, ValT>) { + using value_type = typename std::remove_cvref_t::value_type; + + static_assert( + deserializable, + "The specified type inside the unique_ptr must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the unique_ptr must default constructible."); + + if (!out) { + out.emplace(); + } + SIMDJSON_TRY(val.template get(out.value())); + return SUCCESS; +} + +} // namespace simdjson + +#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +/* end file simdjson/generic/ondemand/std_deserialize.h for icelake */ + // Inline definitions /* including simdjson/generic/ondemand/array-inl.h for icelake: #include "simdjson/generic/ondemand/array-inl.h" */ /* begin file simdjson/generic/ondemand/array-inl.h for icelake */ @@ -68584,6 +71019,7 @@ inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result array::at_pointer(std::string_view json_pointer) n return child; } -inline std::string json_path_to_pointer_conversion(std::string_view json_path) { - if (json_path.empty() || (json_path.front() != '.' && - json_path.front() != '[')) { - return "-1"; // This is just a sentinel value, the caller should check for this and return an error. - } - - std::string result; - // Reserve space to reduce allocations, adjusting for potential increases due - // to escaping. - result.reserve(json_path.size() * 2); - - size_t i = 0; - - while (i < json_path.length()) { - if (json_path[i] == '.') { - result += '/'; - } else if (json_path[i] == '[') { - result += '/'; - ++i; // Move past the '[' - while (i < json_path.length() && json_path[i] != ']') { - if (json_path[i] == '~') { - result += "~0"; - } else if (json_path[i] == '/') { - result += "~1"; - } else { - result += json_path[i]; - } - ++i; - } - if (i == json_path.length() || json_path[i] != ']') { - return "-1"; // Using sentinel value that will be handled as an error by the caller. - } - } else { - if (json_path[i] == '~') { - result += "~0"; - } else if (json_path[i] == '/') { - result += "~1"; - } else { - result += json_path[i]; - } - } - ++i; - } - - return result; -} - inline simdjson_result array::at_path(std::string_view json_path) noexcept { auto json_pointer = json_path_to_pointer_conversion(json_path); if (json_pointer == "-1") { return INVALID_JSON_POINTER; } @@ -68945,313 +71334,276 @@ simdjson_inline simdjson_result &simdjson_res #endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H /* end file simdjson/generic/ondemand/array_iterator-inl.h for icelake */ -/* including simdjson/generic/ondemand/document-inl.h for icelake: #include "simdjson/generic/ondemand/document-inl.h" */ -/* begin file simdjson/generic/ondemand/document-inl.h for icelake */ -#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H +/* including simdjson/generic/ondemand/value-inl.h for icelake: #include "simdjson/generic/ondemand/value-inl.h" */ +/* begin file simdjson/generic/ondemand/value-inl.h for icelake */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ -/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { namespace icelake { namespace ondemand { -simdjson_inline document::document(ondemand::json_iterator &&_iter) noexcept - : iter{std::forward(_iter)} +simdjson_inline value::value(const value_iterator &_iter) noexcept + : iter{_iter} { - logger::log_start_value(iter, "document"); } - -simdjson_inline document document::start(json_iterator &&iter) noexcept { - return document(std::forward(iter)); +simdjson_inline value value::start(const value_iterator &iter) noexcept { + return iter; } - -inline void document::rewind() noexcept { - iter.rewind(); +simdjson_inline value value::resume(const value_iterator &iter) noexcept { + return iter; } -inline std::string document::to_debug_string() noexcept { - return iter.to_string(); +simdjson_inline simdjson_result value::get_array() noexcept { + return array::start(iter); } - -inline simdjson_result document::current_location() const noexcept { - return iter.current_location(); +simdjson_inline simdjson_result value::get_object() noexcept { + return object::start(iter); } - -inline int32_t document::current_depth() const noexcept { - return iter.depth(); +simdjson_inline simdjson_result value::start_or_resume_object() noexcept { + if (iter.at_start()) { + return get_object(); + } else { + return object::resume(iter); + } } -inline bool document::at_end() const noexcept { - return iter.at_end(); +simdjson_inline simdjson_result value::get_raw_json_string() noexcept { + return iter.get_raw_json_string(); } - - -inline bool document::is_alive() noexcept { - return iter.is_alive(); +simdjson_inline simdjson_result value::get_string(bool allow_replacement) noexcept { + return iter.get_string(allow_replacement); } -simdjson_inline value_iterator document::resume_value_iterator() noexcept { - return value_iterator(&iter, 1, iter.root_position()); +template +simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept { + return iter.get_string(receiver, allow_replacement); } -simdjson_inline value_iterator document::get_root_value_iterator() noexcept { - return resume_value_iterator(); +simdjson_inline simdjson_result value::get_wobbly_string() noexcept { + return iter.get_wobbly_string(); } -simdjson_inline simdjson_result document::start_or_resume_object() noexcept { - if (iter.at_root()) { - return get_object(); - } else { - return object::resume(resume_value_iterator()); - } +simdjson_inline simdjson_result value::get_double() noexcept { + return iter.get_double(); } -simdjson_inline simdjson_result document::get_value() noexcept { - // Make sure we start any arrays or objects before returning, so that start_root_() - // gets called. - - // It is the convention throughout the code that the macro `SIMDJSON_DEVELOPMENT_CHECKS` determines whether - // we check for OUT_OF_ORDER_ITERATION. Proper on::demand code should never trigger this error. -#if SIMDJSON_DEVELOPMENT_CHECKS - if (!iter.at_root()) { return OUT_OF_ORDER_ITERATION; } -#endif - // assert_at_root() serves two purposes: in Debug mode, whether or not - // SIMDJSON_DEVELOPMENT_CHECKS is set or not, it checks that we are at the root of - // the document (this will typically be redundant). In release mode, it generates - // SIMDJSON_ASSUME statements to allow the compiler to make assumptions. - iter.assert_at_root(); - switch (*iter.peek()) { - case '[': { - // The following lines check that the document ends with ]. - auto value_iterator = get_root_value_iterator(); - auto error = value_iterator.check_root_array(); - if(error) { return error; } - return value(get_root_value_iterator()); - } - case '{': { - // The following lines would check that the document ends with }. - auto value_iterator = get_root_value_iterator(); - auto error = value_iterator.check_root_object(); - if(error) { return error; } - return value(get_root_value_iterator()); - } - default: - // Unfortunately, scalar documents are a special case in simdjson and they cannot - // be safely converted to value instances. - return SCALAR_DOCUMENT_AS_VALUE; - } +simdjson_inline simdjson_result value::get_double_in_string() noexcept { + return iter.get_double_in_string(); } -simdjson_inline simdjson_result document::get_array() & noexcept { - auto value = get_root_value_iterator(); - return array::start_root(value); +simdjson_inline simdjson_result value::get_uint64() noexcept { + return iter.get_uint64(); } -simdjson_inline simdjson_result document::get_object() & noexcept { - auto value = get_root_value_iterator(); - return object::start_root(value); +simdjson_inline simdjson_result value::get_uint64_in_string() noexcept { + return iter.get_uint64_in_string(); } - -/** - * We decided that calling 'get_double()' on the JSON document '1.233 blabla' should - * give an error, so we check for trailing content. We want to disallow trailing - * content. - * Thus, in several implementations below, we pass a 'true' parameter value to - * a get_root_value_iterator() method: this indicates that we disallow trailing content. - */ - -simdjson_inline simdjson_result document::get_uint64() noexcept { - return get_root_value_iterator().get_root_uint64(true); +simdjson_inline simdjson_result value::get_int64() noexcept { + return iter.get_int64(); } -simdjson_inline simdjson_result document::get_uint64_in_string() noexcept { - return get_root_value_iterator().get_root_uint64_in_string(true); +simdjson_inline simdjson_result value::get_int64_in_string() noexcept { + return iter.get_int64_in_string(); } -simdjson_inline simdjson_result document::get_int64() noexcept { - return get_root_value_iterator().get_root_int64(true); +simdjson_inline simdjson_result value::get_bool() noexcept { + return iter.get_bool(); } -simdjson_inline simdjson_result document::get_int64_in_string() noexcept { - return get_root_value_iterator().get_root_int64_in_string(true); +simdjson_inline simdjson_result value::is_null() noexcept { + return iter.is_null(); } -simdjson_inline simdjson_result document::get_double() noexcept { - return get_root_value_iterator().get_root_double(true); + +template<> simdjson_inline simdjson_result value::get() noexcept { return get_array(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_object(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_number(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_double(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_bool(); } + + +template<> simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); } +template<> simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); } +template<> simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); } +template<> simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); } +template<> simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); } +template<> simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); } +template<> simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); } +template<> simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); } +template<> simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); } + +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline value::operator T() noexcept(false) { + return get(); } -simdjson_inline simdjson_result document::get_double_in_string() noexcept { - return get_root_value_iterator().get_root_double_in_string(true); +simdjson_inline value::operator array() noexcept(false) { + return get_array(); } -simdjson_inline simdjson_result document::get_string(bool allow_replacement) noexcept { - return get_root_value_iterator().get_root_string(true, allow_replacement); +simdjson_inline value::operator object() noexcept(false) { + return get_object(); } -template -simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept { - return get_root_value_iterator().get_root_string(receiver, true, allow_replacement); +simdjson_inline value::operator uint64_t() noexcept(false) { + return get_uint64(); } -simdjson_inline simdjson_result document::get_wobbly_string() noexcept { - return get_root_value_iterator().get_root_wobbly_string(true); +simdjson_inline value::operator int64_t() noexcept(false) { + return get_int64(); } -simdjson_inline simdjson_result document::get_raw_json_string() noexcept { - return get_root_value_iterator().get_root_raw_json_string(true); +simdjson_inline value::operator double() noexcept(false) { + return get_double(); } -simdjson_inline simdjson_result document::get_bool() noexcept { - return get_root_value_iterator().get_root_bool(true); +simdjson_inline value::operator std::string_view() noexcept(false) { + return get_string(false); } -simdjson_inline simdjson_result document::is_null() noexcept { - return get_root_value_iterator().is_root_null(true); +simdjson_inline value::operator raw_json_string() noexcept(false) { + return get_raw_json_string(); } +simdjson_inline value::operator bool() noexcept(false) { + return get_bool(); +} +#endif -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_array(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_object(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_raw_json_string(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_string(false); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_double(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_uint64(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_int64(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_value(); } - -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_raw_json_string(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_value(); } - -template simdjson_inline error_code document::get(T &out) & noexcept { - return get().get(out); +simdjson_inline simdjson_result value::begin() & noexcept { + return get_array().begin(); } -template simdjson_deprecated simdjson_inline error_code document::get(T &out) && noexcept { - return std::forward(*this).get().get(out); +simdjson_inline simdjson_result value::end() & noexcept { + return {}; } - -#if SIMDJSON_EXCEPTIONS -template -simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get(); } -template -simdjson_inline document::operator T() & noexcept(false) { return get(); } -simdjson_inline document::operator array() & noexcept(false) { return get_array(); } -simdjson_inline document::operator object() & noexcept(false) { return get_object(); } -simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); } -simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); } -simdjson_inline document::operator double() noexcept(false) { return get_double(); } -simdjson_inline document::operator std::string_view() noexcept(false) { return get_string(false); } -simdjson_inline document::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } -simdjson_inline document::operator bool() noexcept(false) { return get_bool(); } -simdjson_inline document::operator value() noexcept(false) { return get_value(); } - -#endif -simdjson_inline simdjson_result document::count_elements() & noexcept { +simdjson_inline simdjson_result value::count_elements() & noexcept { + simdjson_result answer; auto a = get_array(); - simdjson_result answer = a.count_elements(); - /* If there was an array, we are now left pointing at its first element. */ - if(answer.error() == SUCCESS) { rewind(); } + answer = a.count_elements(); + // count_elements leaves you pointing inside the array, at the first element. + // We need to move back so that the user can create a new array (which requires that + // we point at '['). + iter.move_at_start(); return answer; } -simdjson_inline simdjson_result document::count_fields() & noexcept { +simdjson_inline simdjson_result value::count_fields() & noexcept { + simdjson_result answer; auto a = get_object(); - simdjson_result answer = a.count_fields(); - /* If there was an object, we are now left pointing at its first element. */ - if(answer.error() == SUCCESS) { rewind(); } + answer = a.count_fields(); + iter.move_at_start(); return answer; } -simdjson_inline simdjson_result document::at(size_t index) & noexcept { +simdjson_inline simdjson_result value::at(size_t index) noexcept { auto a = get_array(); return a.at(index); } -simdjson_inline simdjson_result document::begin() & noexcept { - return get_array().begin(); -} -simdjson_inline simdjson_result document::end() & noexcept { - return {}; -} -simdjson_inline simdjson_result document::find_field(std::string_view key) & noexcept { +simdjson_inline simdjson_result value::find_field(std::string_view key) noexcept { return start_or_resume_object().find_field(key); } -simdjson_inline simdjson_result document::find_field(const char *key) & noexcept { +simdjson_inline simdjson_result value::find_field(const char *key) noexcept { return start_or_resume_object().find_field(key); } -simdjson_inline simdjson_result document::find_field_unordered(std::string_view key) & noexcept { - return start_or_resume_object().find_field_unordered(key); + +simdjson_inline simdjson_result value::find_field_unordered(std::string_view key) noexcept { + return start_or_resume_object().find_field_unordered(key); } -simdjson_inline simdjson_result document::find_field_unordered(const char *key) & noexcept { +simdjson_inline simdjson_result value::find_field_unordered(const char *key) noexcept { return start_or_resume_object().find_field_unordered(key); } -simdjson_inline simdjson_result document::operator[](std::string_view key) & noexcept { + +simdjson_inline simdjson_result value::operator[](std::string_view key) noexcept { return start_or_resume_object()[key]; } -simdjson_inline simdjson_result document::operator[](const char *key) & noexcept { +simdjson_inline simdjson_result value::operator[](const char *key) noexcept { return start_or_resume_object()[key]; } -simdjson_inline error_code document::consume() noexcept { - auto error = iter.skip_child(0); - if(error) { iter.abandon(); } - return error; -} - -simdjson_inline simdjson_result document::raw_json() noexcept { - auto _iter = get_root_value_iterator(); - const uint8_t * starting_point{_iter.peek_start()}; - auto error = consume(); - if(error) { return error; } - // After 'consume()', we could be left pointing just beyond the document, but that - // is ok because we are not going to dereference the final pointer position, we just - // use it to compute the length in bytes. - const uint8_t * final_point{iter.unsafe_pointer()}; - return std::string_view(reinterpret_cast(starting_point), size_t(final_point - starting_point)); -} - -simdjson_inline simdjson_result document::type() noexcept { - return get_root_value_iterator().type(); +simdjson_inline simdjson_result value::type() noexcept { + return iter.type(); } -simdjson_inline simdjson_result document::is_scalar() noexcept { +simdjson_inline simdjson_result value::is_scalar() noexcept { json_type this_type; auto error = type().get(this_type); if(error) { return error; } return ! ((this_type == json_type::array) || (this_type == json_type::object)); } -simdjson_inline simdjson_result document::is_string() noexcept { +simdjson_inline simdjson_result value::is_string() noexcept { json_type this_type; auto error = type().get(this_type); if(error) { return error; } return (this_type == json_type::string); } -simdjson_inline bool document::is_negative() noexcept { - return get_root_value_iterator().is_root_negative(); + +simdjson_inline bool value::is_negative() noexcept { + return iter.is_negative(); } -simdjson_inline simdjson_result document::is_integer() noexcept { - return get_root_value_iterator().is_root_integer(true); +simdjson_inline simdjson_result value::is_integer() noexcept { + return iter.is_integer(); +} +simdjson_warn_unused simdjson_inline simdjson_result value::get_number_type() noexcept { + return iter.get_number_type(); +} +simdjson_warn_unused simdjson_inline simdjson_result value::get_number() noexcept { + return iter.get_number(); } -simdjson_inline simdjson_result document::get_number_type() noexcept { - return get_root_value_iterator().get_root_number_type(true); +simdjson_inline std::string_view value::raw_json_token() noexcept { + return std::string_view(reinterpret_cast(iter.peek_start()), iter.peek_start_length()); } -simdjson_inline simdjson_result document::get_number() noexcept { - return get_root_value_iterator().get_root_number(true); +simdjson_inline simdjson_result value::raw_json() noexcept { + json_type t; + SIMDJSON_TRY(type().get(t)); + switch (t) + { + case json_type::array: { + ondemand::array array; + SIMDJSON_TRY(get_array().get(array)); + return array.raw_json(); + } + case json_type::object: { + ondemand::object object; + SIMDJSON_TRY(get_object().get(object)); + return object.raw_json(); + } + default: + return raw_json_token(); + } } +simdjson_inline simdjson_result value::current_location() noexcept { + return iter.json_iter().current_location(); +} -simdjson_inline simdjson_result document::raw_json_token() noexcept { - auto _iter = get_root_value_iterator(); - return std::string_view(reinterpret_cast(_iter.peek_start()), _iter.peek_root_length()); +simdjson_inline int32_t value::current_depth() const noexcept{ + return iter.json_iter().depth(); } -simdjson_inline simdjson_result document::at_pointer(std::string_view json_pointer) noexcept { - rewind(); // Rewind the document each time at_pointer is called - if (json_pointer.empty()) { - return this->get_value(); +inline bool is_pointer_well_formed(std::string_view json_pointer) noexcept { + if (simdjson_unlikely(json_pointer.empty())) { // can't be + return false; + } + if (simdjson_unlikely(json_pointer[0] != '/')) { + return false; + } + size_t escape = json_pointer.find('~'); + if (escape == std::string_view::npos) { + return true; } + if (escape == json_pointer.size() - 1) { + return false; + } + if (json_pointer[escape + 1] != '0' && json_pointer[escape + 1] != '1') { + return false; + } + return true; +} + +simdjson_inline simdjson_result value::at_pointer(std::string_view json_pointer) noexcept { json_type t; SIMDJSON_TRY(type().get(t)); switch (t) @@ -69261,15 +71613,15 @@ simdjson_inline simdjson_result document::at_pointer(std::string_view jso case json_type::object: return (*this).get_object().at_pointer(json_pointer); default: + // a non-empty string can be invalid, or accessing a primitive (issue 2154) + if (is_pointer_well_formed(json_pointer)) { + return NO_SUCH_FIELD; + } return INVALID_JSON_POINTER; } } -simdjson_inline simdjson_result document::at_path(std::string_view json_path) noexcept { - rewind(); // Rewind the document each time at_pointer is called - if (json_path.empty()) { - return this->get_value(); - } +simdjson_inline simdjson_result value::at_path(std::string_view json_path) noexcept { json_type t; SIMDJSON_TRY(type().get(t)); switch (t) { @@ -69288,681 +71640,1320 @@ simdjson_inline simdjson_result document::at_path(std::string_view json_p namespace simdjson { -simdjson_inline simdjson_result::simdjson_result( - icelake::ondemand::document &&value +simdjson_inline simdjson_result::simdjson_result( + icelake::ondemand::value &&value ) noexcept : - implementation_simdjson_result_base( - std::forward(value) + implementation_simdjson_result_base( + std::forward(value) ) { } -simdjson_inline simdjson_result::simdjson_result( +simdjson_inline simdjson_result::simdjson_result( error_code error ) noexcept : - implementation_simdjson_result_base( - error - ) + implementation_simdjson_result_base(error) { } -simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { if (error()) { return error(); } return first.count_elements(); } -simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { if (error()) { return error(); } return first.count_fields(); } -simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { +simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { if (error()) { return error(); } return first.at(index); } -simdjson_inline error_code simdjson_result::rewind() noexcept { - if (error()) { return error(); } - first.rewind(); - return SUCCESS; -} -simdjson_inline simdjson_result simdjson_result::begin() & noexcept { +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { if (error()) { return error(); } return first.begin(); } -simdjson_inline simdjson_result simdjson_result::end() & noexcept { +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + if (error()) { return error(); } return {}; } -simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) noexcept { if (error()) { return error(); } - return first.find_field_unordered(key); + return first.find_field(key); } -simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) noexcept { if (error()) { return error(); } - return first.find_field_unordered(key); + return first.find_field(key); } -simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) noexcept { if (error()) { return error(); } - return first[key]; + return first.find_field_unordered(key); } -simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) noexcept { if (error()) { return error(); } - return first[key]; + return first.find_field_unordered(key); } -simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) noexcept { if (error()) { return error(); } - return first.find_field(key); + return first[key]; } -simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) noexcept { if (error()) { return error(); } - return first.find_field(key); + return first[key]; } -simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { + +simdjson_inline simdjson_result simdjson_result::get_array() noexcept { if (error()) { return error(); } return first.get_array(); } -simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { +simdjson_inline simdjson_result simdjson_result::get_object() noexcept { if (error()) { return error(); } return first.get_object(); } -simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { if (error()) { return error(); } return first.get_uint64(); } -simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { if (error()) { return error(); } return first.get_uint64_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { if (error()) { return error(); } return first.get_int64(); } -simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { if (error()) { return error(); } return first.get_int64_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_double() noexcept { +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { if (error()) { return error(); } return first.get_double(); } -simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { if (error()) { return error(); } return first.get_double_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(allow_replacement); } template -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(receiver, allow_replacement); } -simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { if (error()) { return error(); } return first.get_wobbly_string(); } -simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { if (error()) { return error(); } return first.get_raw_json_string(); } -simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { if (error()) { return error(); } return first.get_bool(); } -simdjson_inline simdjson_result simdjson_result::get_value() noexcept { - if (error()) { return error(); } - return first.get_value(); -} -simdjson_inline simdjson_result simdjson_result::is_null() noexcept { +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { if (error()) { return error(); } return first.is_null(); } -template -simdjson_inline simdjson_result simdjson_result::get() & noexcept { +template<> simdjson_inline error_code simdjson_result::get(icelake::ondemand::value &out) noexcept { if (error()) { return error(); } - return first.get(); + out = first; + return SUCCESS; } -template -simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + +template simdjson_inline simdjson_result simdjson_result::get() noexcept { if (error()) { return error(); } - return std::forward(first).get(); + return first.get(); } -template -simdjson_inline error_code simdjson_result::get(T &out) & noexcept { +template simdjson_inline error_code simdjson_result::get(T &out) noexcept { if (error()) { return error(); } return first.get(out); } -template -simdjson_inline error_code simdjson_result::get(T &out) && noexcept { - if (error()) { return error(); } - return std::forward(first).get(out); -} -template<> simdjson_inline simdjson_result simdjson_result::get() & noexcept = delete; -template<> simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { - if (error()) { return error(); } - return std::forward(first); -} -template<> simdjson_inline error_code simdjson_result::get(icelake::ondemand::document &out) & noexcept = delete; -template<> simdjson_inline error_code simdjson_result::get(icelake::ondemand::document &out) && noexcept { +template<> simdjson_inline simdjson_result simdjson_result::get() noexcept { if (error()) { return error(); } - out = std::forward(first); - return SUCCESS; + return std::move(first); } -simdjson_inline simdjson_result simdjson_result::type() noexcept { +simdjson_inline simdjson_result simdjson_result::type() noexcept { if (error()) { return error(); } return first.type(); } - -simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { if (error()) { return error(); } return first.is_scalar(); } - -simdjson_inline simdjson_result simdjson_result::is_string() noexcept { +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { if (error()) { return error(); } return first.is_string(); } - -simdjson_inline bool simdjson_result::is_negative() noexcept { +simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { if (error()) { return error(); } return first.is_negative(); } - -simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { if (error()) { return error(); } return first.is_integer(); } - -simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { if (error()) { return error(); } return first.get_number_type(); } - -simdjson_inline simdjson_result simdjson_result::get_number() noexcept { +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { if (error()) { return error(); } return first.get_number(); } - - #if SIMDJSON_EXCEPTIONS -template ::value == false>::type> -simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator icelake::ondemand::array() & noexcept(false) { +template +simdjson_inline simdjson_result::operator T() noexcept(false) { if (error()) { throw simdjson_error(error()); } - return first; + return first.get(); } -simdjson_inline simdjson_result::operator icelake::ondemand::object() & noexcept(false) { +simdjson_inline simdjson_result::operator icelake::ondemand::array() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { +simdjson_inline simdjson_result::operator icelake::ondemand::object() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator int64_t() noexcept(false) { +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator double() noexcept(false) { +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { +simdjson_inline simdjson_result::operator double() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator icelake::ondemand::raw_json_string() noexcept(false) { +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator bool() noexcept(false) { +simdjson_inline simdjson_result::operator icelake::ondemand::raw_json_string() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator icelake::ondemand::value() noexcept(false) { +simdjson_inline simdjson_result::operator bool() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } #endif - -simdjson_inline simdjson_result simdjson_result::current_location() noexcept { +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { if (error()) { return error(); } - return first.current_location(); + return first.raw_json_token(); } -simdjson_inline bool simdjson_result::at_end() const noexcept { +simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { if (error()) { return error(); } - return first.at_end(); + return first.raw_json(); } - -simdjson_inline int32_t simdjson_result::current_depth() const noexcept { +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { if (error()) { return error(); } - return first.current_depth(); + return first.current_location(); } -simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { +simdjson_inline simdjson_result simdjson_result::current_depth() const noexcept { if (error()) { return error(); } - return first.raw_json_token(); + return first.current_depth(); } -simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } +simdjson_inline simdjson_result simdjson_result::at_pointer( + std::string_view json_pointer) noexcept { + if (error()) { + return error(); + } return first.at_pointer(json_pointer); } -simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { return error(); } +simdjson_inline simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } return first.at_path(json_path); } } // namespace simdjson +#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H +/* end file simdjson/generic/ondemand/value-inl.h for icelake */ +/* including simdjson/generic/ondemand/document-inl.h for icelake: #include "simdjson/generic/ondemand/document-inl.h" */ +/* begin file simdjson/generic/ondemand/document-inl.h for icelake */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { namespace icelake { namespace ondemand { -simdjson_inline document_reference::document_reference() noexcept : doc{nullptr} {} -simdjson_inline document_reference::document_reference(document &d) noexcept : doc(&d) {} -simdjson_inline void document_reference::rewind() noexcept { doc->rewind(); } -simdjson_inline simdjson_result document_reference::get_array() & noexcept { return doc->get_array(); } -simdjson_inline simdjson_result document_reference::get_object() & noexcept { return doc->get_object(); } -/** - * The document_reference instances are used primarily/solely for streams of JSON - * documents. - * We decided that calling 'get_double()' on the JSON document '1.233 blabla' should - * give an error, so we check for trailing content. - * - * However, for streams of JSON documents, we want to be able to start from - * "321" "321" "321" - * and parse it successfully as a stream of JSON documents, calling get_uint64_in_string() - * successfully each time. - * - * To achieve this result, we pass a 'false' to a get_root_value_iterator() method: - * this indicates that we allow trailing content. - */ -simdjson_inline simdjson_result document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); } -simdjson_inline simdjson_result document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); } -simdjson_inline simdjson_result document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); } -simdjson_inline simdjson_result document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); } -simdjson_inline simdjson_result document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); } -simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } -simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } -template -simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); } -simdjson_inline simdjson_result document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } -simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } -simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } -simdjson_inline simdjson_result document_reference::get_value() noexcept { return doc->get_value(); } -simdjson_inline simdjson_result document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_array(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_object(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_raw_json_string(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_string(false); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_double(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_uint64(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_int64(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_bool(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_value(); } -#if SIMDJSON_EXCEPTIONS -template -simdjson_inline document_reference::operator T() noexcept(false) { return get(); } -simdjson_inline document_reference::operator array() & noexcept(false) { return array(*doc); } -simdjson_inline document_reference::operator object() & noexcept(false) { return object(*doc); } -simdjson_inline document_reference::operator uint64_t() noexcept(false) { return get_uint64(); } -simdjson_inline document_reference::operator int64_t() noexcept(false) { return get_int64(); } -simdjson_inline document_reference::operator double() noexcept(false) { return get_double(); } -simdjson_inline document_reference::operator std::string_view() noexcept(false) { return std::string_view(*doc); } -simdjson_inline document_reference::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } -simdjson_inline document_reference::operator bool() noexcept(false) { return get_bool(); } -simdjson_inline document_reference::operator value() noexcept(false) { return value(*doc); } -#endif -simdjson_inline simdjson_result document_reference::count_elements() & noexcept { return doc->count_elements(); } -simdjson_inline simdjson_result document_reference::count_fields() & noexcept { return doc->count_fields(); } -simdjson_inline simdjson_result document_reference::at(size_t index) & noexcept { return doc->at(index); } -simdjson_inline simdjson_result document_reference::begin() & noexcept { return doc->begin(); } -simdjson_inline simdjson_result document_reference::end() & noexcept { return doc->end(); } -simdjson_inline simdjson_result document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); } -simdjson_inline simdjson_result document_reference::find_field(const char *key) & noexcept { return doc->find_field(key); } -simdjson_inline simdjson_result document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; } -simdjson_inline simdjson_result document_reference::operator[](const char *key) & noexcept { return (*doc)[key]; } -simdjson_inline simdjson_result document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); } -simdjson_inline simdjson_result document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); } -simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } -simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } -simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } -simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } -simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } -simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } -simdjson_inline simdjson_result document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); } -simdjson_inline simdjson_result document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); } -simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } -simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } -simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } -simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } -simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} -simdjson_inline document_reference::operator document&() const noexcept { return *doc; } +simdjson_inline document::document(ondemand::json_iterator &&_iter) noexcept + : iter{std::forward(_iter)} +{ + logger::log_start_value(iter, "document"); +} -} // namespace ondemand -} // namespace icelake -} // namespace simdjson +simdjson_inline document document::start(json_iterator &&iter) noexcept { + return document(std::forward(iter)); +} +inline void document::rewind() noexcept { + iter.rewind(); +} +inline std::string document::to_debug_string() noexcept { + return iter.to_string(); +} -namespace simdjson { -simdjson_inline simdjson_result::simdjson_result(icelake::ondemand::document_reference value, error_code error) - noexcept : implementation_simdjson_result_base(std::forward(value), error) {} +inline simdjson_result document::current_location() const noexcept { + return iter.current_location(); +} +inline int32_t document::current_depth() const noexcept { + return iter.depth(); +} -simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); +inline bool document::at_end() const noexcept { + return iter.at_end(); } -simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); + + +inline bool document::is_alive() noexcept { + return iter.is_alive(); } -simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { - if (error()) { return error(); } - return first.at(index); +simdjson_inline value_iterator document::resume_value_iterator() noexcept { + return value_iterator(&iter, 1, iter.root_position()); } -simdjson_inline error_code simdjson_result::rewind() noexcept { - if (error()) { return error(); } - first.rewind(); - return SUCCESS; +simdjson_inline value_iterator document::get_root_value_iterator() noexcept { + return resume_value_iterator(); } -simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); +simdjson_inline simdjson_result document::start_or_resume_object() noexcept { + if (iter.at_root()) { + return get_object(); + } else { + return object::resume(resume_value_iterator()); + } } -simdjson_inline simdjson_result simdjson_result::end() & noexcept { - return {}; +simdjson_inline simdjson_result document::get_value() noexcept { + // Make sure we start any arrays or objects before returning, so that start_root_() + // gets called. + + // It is the convention throughout the code that the macro `SIMDJSON_DEVELOPMENT_CHECKS` determines whether + // we check for OUT_OF_ORDER_ITERATION. Proper on::demand code should never trigger this error. +#if SIMDJSON_DEVELOPMENT_CHECKS + if (!iter.at_root()) { return OUT_OF_ORDER_ITERATION; } +#endif + // assert_at_root() serves two purposes: in Debug mode, whether or not + // SIMDJSON_DEVELOPMENT_CHECKS is set or not, it checks that we are at the root of + // the document (this will typically be redundant). In release mode, it generates + // SIMDJSON_ASSUME statements to allow the compiler to make assumptions. + iter.assert_at_root(); + switch (*iter.peek()) { + case '[': { + // The following lines check that the document ends with ]. + auto value_iterator = get_root_value_iterator(); + auto error = value_iterator.check_root_array(); + if(error) { return error; } + return value(get_root_value_iterator()); + } + case '{': { + // The following lines would check that the document ends with }. + auto value_iterator = get_root_value_iterator(); + auto error = value_iterator.check_root_object(); + if(error) { return error; } + return value(get_root_value_iterator()); + } + default: + // Unfortunately, scalar documents are a special case in simdjson and they cannot + // be safely converted to value instances. + return SCALAR_DOCUMENT_AS_VALUE; + } } -simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { +simdjson_inline simdjson_result document::get_array() & noexcept { + auto value = get_root_value_iterator(); + return array::start_root(value); +} +simdjson_inline simdjson_result document::get_object() & noexcept { + auto value = get_root_value_iterator(); + return object::start_root(value); +} + +/** + * We decided that calling 'get_double()' on the JSON document '1.233 blabla' should + * give an error, so we check for trailing content. We want to disallow trailing + * content. + * Thus, in several implementations below, we pass a 'true' parameter value to + * a get_root_value_iterator() method: this indicates that we disallow trailing content. + */ + +simdjson_inline simdjson_result document::get_uint64() noexcept { + return get_root_value_iterator().get_root_uint64(true); +} +simdjson_inline simdjson_result document::get_uint64_in_string() noexcept { + return get_root_value_iterator().get_root_uint64_in_string(true); +} +simdjson_inline simdjson_result document::get_int64() noexcept { + return get_root_value_iterator().get_root_int64(true); +} +simdjson_inline simdjson_result document::get_int64_in_string() noexcept { + return get_root_value_iterator().get_root_int64_in_string(true); +} +simdjson_inline simdjson_result document::get_double() noexcept { + return get_root_value_iterator().get_root_double(true); +} +simdjson_inline simdjson_result document::get_double_in_string() noexcept { + return get_root_value_iterator().get_root_double_in_string(true); +} +simdjson_inline simdjson_result document::get_string(bool allow_replacement) noexcept { + return get_root_value_iterator().get_root_string(true, allow_replacement); +} +template +simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept { + return get_root_value_iterator().get_root_string(receiver, true, allow_replacement); +} +simdjson_inline simdjson_result document::get_wobbly_string() noexcept { + return get_root_value_iterator().get_root_wobbly_string(true); +} +simdjson_inline simdjson_result document::get_raw_json_string() noexcept { + return get_root_value_iterator().get_root_raw_json_string(true); +} +simdjson_inline simdjson_result document::get_bool() noexcept { + return get_root_value_iterator().get_root_bool(true); +} +simdjson_inline simdjson_result document::is_null() noexcept { + return get_root_value_iterator().is_root_null(true); +} + +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_array(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_object(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_double(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_value(); } + +template<> simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); } +template<> simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); } +template<> simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); } +template<> simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); } +template<> simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); } +template<> simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); } +template<> simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); } +template<> simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); } +template<> simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); } + +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_raw_json_string(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_value(); } + +#if SIMDJSON_EXCEPTIONS +template +simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get(); } +template +simdjson_inline document::operator T() & noexcept(false) { return get(); } +simdjson_inline document::operator array() & noexcept(false) { return get_array(); } +simdjson_inline document::operator object() & noexcept(false) { return get_object(); } +simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); } +simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); } +simdjson_inline document::operator double() noexcept(false) { return get_double(); } +simdjson_inline document::operator std::string_view() noexcept(false) { return get_string(false); } +simdjson_inline document::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } +simdjson_inline document::operator bool() noexcept(false) { return get_bool(); } +simdjson_inline document::operator value() noexcept(false) { return get_value(); } + +#endif +simdjson_inline simdjson_result document::count_elements() & noexcept { + auto a = get_array(); + simdjson_result answer = a.count_elements(); + /* If there was an array, we are now left pointing at its first element. */ + if(answer.error() == SUCCESS) { rewind(); } + return answer; +} +simdjson_inline simdjson_result document::count_fields() & noexcept { + auto a = get_object(); + simdjson_result answer = a.count_fields(); + /* If there was an object, we are now left pointing at its first element. */ + if(answer.error() == SUCCESS) { rewind(); } + return answer; +} +simdjson_inline simdjson_result document::at(size_t index) & noexcept { + auto a = get_array(); + return a.at(index); +} +simdjson_inline simdjson_result document::begin() & noexcept { + return get_array().begin(); +} +simdjson_inline simdjson_result document::end() & noexcept { + return {}; +} + +simdjson_inline simdjson_result document::find_field(std::string_view key) & noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result document::find_field(const char *key) & noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result document::find_field_unordered(std::string_view key) & noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result document::find_field_unordered(const char *key) & noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result document::operator[](std::string_view key) & noexcept { + return start_or_resume_object()[key]; +} +simdjson_inline simdjson_result document::operator[](const char *key) & noexcept { + return start_or_resume_object()[key]; +} + +simdjson_inline error_code document::consume() noexcept { + bool scalar = false; + auto error = is_scalar().get(scalar); + if(error) { return error; } + if(scalar) { + iter.return_current_and_advance(); + return SUCCESS; + } + error = iter.skip_child(0); + if(error) { iter.abandon(); } + return error; +} + +simdjson_inline simdjson_result document::raw_json() noexcept { + auto _iter = get_root_value_iterator(); + const uint8_t * starting_point{_iter.peek_start()}; + auto error = consume(); + if(error) { return error; } + // After 'consume()', we could be left pointing just beyond the document, but that + // is ok because we are not going to dereference the final pointer position, we just + // use it to compute the length in bytes. + const uint8_t * final_point{iter.unsafe_pointer()}; + return std::string_view(reinterpret_cast(starting_point), size_t(final_point - starting_point)); +} + +simdjson_inline simdjson_result document::type() noexcept { + return get_root_value_iterator().type(); +} + +simdjson_inline simdjson_result document::is_scalar() noexcept { + // For more speed, we could do: + // return iter.is_single_token(); + json_type this_type; + auto error = type().get(this_type); + if(error) { return error; } + return ! ((this_type == json_type::array) || (this_type == json_type::object)); +} + +simdjson_inline simdjson_result document::is_string() noexcept { + json_type this_type; + auto error = type().get(this_type); + if(error) { return error; } + return (this_type == json_type::string); +} + +simdjson_inline bool document::is_negative() noexcept { + return get_root_value_iterator().is_root_negative(); +} + +simdjson_inline simdjson_result document::is_integer() noexcept { + return get_root_value_iterator().is_root_integer(true); +} + +simdjson_inline simdjson_result document::get_number_type() noexcept { + return get_root_value_iterator().get_root_number_type(true); +} + +simdjson_inline simdjson_result document::get_number() noexcept { + return get_root_value_iterator().get_root_number(true); +} + + +simdjson_inline simdjson_result document::raw_json_token() noexcept { + auto _iter = get_root_value_iterator(); + return std::string_view(reinterpret_cast(_iter.peek_start()), _iter.peek_root_length()); +} + +simdjson_inline simdjson_result document::at_pointer(std::string_view json_pointer) noexcept { + rewind(); // Rewind the document each time at_pointer is called + if (json_pointer.empty()) { + return this->get_value(); + } + json_type t; + SIMDJSON_TRY(type().get(t)); + switch (t) + { + case json_type::array: + return (*this).get_array().at_pointer(json_pointer); + case json_type::object: + return (*this).get_object().at_pointer(json_pointer); + default: + return INVALID_JSON_POINTER; + } +} + +simdjson_inline simdjson_result document::at_path(std::string_view json_path) noexcept { + rewind(); // Rewind the document each time at_pointer is called + if (json_path.empty()) { + return this->get_value(); + } + json_type t; + SIMDJSON_TRY(type().get(t)); + switch (t) { + case json_type::array: + return (*this).get_array().at_path(json_path); + case json_type::object: + return (*this).get_object().at_path(json_path); + default: + return INVALID_JSON_POINTER; + } +} + +} // namespace ondemand +} // namespace icelake +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result( + icelake::ondemand::document &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base( + error + ) +{ +} +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline error_code simdjson_result::rewind() noexcept { + if (error()) { return error(); } + first.rewind(); + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { if (error()) { return error(); } return first.find_field_unordered(key); } -simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { if (error()) { return error(); } return first.find_field_unordered(key); } -simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { if (error()) { return error(); } return first[key]; } -simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { if (error()) { return error(); } return first[key]; } -simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { if (error()) { return error(); } return first.find_field(key); } -simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { if (error()) { return error(); } return first.find_field(key); } -simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { +simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { if (error()) { return error(); } return first.get_array(); } -simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { +simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { if (error()) { return error(); } return first.get_object(); } -simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { if (error()) { return error(); } return first.get_uint64(); } -simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { if (error()) { return error(); } return first.get_uint64_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { if (error()) { return error(); } return first.get_int64(); } -simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { if (error()) { return error(); } return first.get_int64_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_double() noexcept { +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { if (error()) { return error(); } return first.get_double(); } -simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { if (error()) { return error(); } return first.get_double_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(allow_replacement); } template -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(receiver, allow_replacement); } -simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { if (error()) { return error(); } return first.get_wobbly_string(); } -simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { if (error()) { return error(); } return first.get_raw_json_string(); } -simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { if (error()) { return error(); } return first.get_bool(); } -simdjson_inline simdjson_result simdjson_result::get_value() noexcept { +simdjson_inline simdjson_result simdjson_result::get_value() noexcept { if (error()) { return error(); } return first.get_value(); } -simdjson_inline simdjson_result simdjson_result::is_null() noexcept { +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { if (error()) { return error(); } return first.is_null(); } -simdjson_inline simdjson_result simdjson_result::type() noexcept { + +template +simdjson_inline simdjson_result simdjson_result::get() & noexcept { + if (error()) { return error(); } + return first.get(); +} +template +simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first).get(); +} +template +simdjson_inline error_code simdjson_result::get(T &out) & noexcept { + if (error()) { return error(); } + return first.get(out); +} +template +simdjson_inline error_code simdjson_result::get(T &out) && noexcept { + if (error()) { return error(); } + return std::forward(first).get(out); +} + +template<> simdjson_inline simdjson_result simdjson_result::get() & noexcept = delete; +template<> simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first); +} +template<> simdjson_inline error_code simdjson_result::get(icelake::ondemand::document &out) & noexcept = delete; +template<> simdjson_inline error_code simdjson_result::get(icelake::ondemand::document &out) && noexcept { + if (error()) { return error(); } + out = std::forward(first); + return SUCCESS; +} + +simdjson_inline simdjson_result simdjson_result::type() noexcept { if (error()) { return error(); } return first.type(); } -simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { if (error()) { return error(); } return first.is_scalar(); } -simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { if (error()) { return error(); } return first.is_string(); } -simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { + +simdjson_inline bool simdjson_result::is_negative() noexcept { if (error()) { return error(); } return first.is_negative(); } -simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { if (error()) { return error(); } return first.is_integer(); } -simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { if (error()) { return error(); } return first.get_number_type(); } -simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { if (error()) { return error(); } return first.get_number(); } + + #if SIMDJSON_EXCEPTIONS -template ::value == false>::type> -simdjson_inline simdjson_result::operator T() noexcept(false) { +template ::value == false>::type> +simdjson_inline simdjson_result::operator T() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator icelake::ondemand::array() & noexcept(false) { +simdjson_inline simdjson_result::operator icelake::ondemand::array() & noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator icelake::ondemand::object() & noexcept(false) { +simdjson_inline simdjson_result::operator icelake::ondemand::object() & noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator int64_t() noexcept(false) { +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator double() noexcept(false) { +simdjson_inline simdjson_result::operator double() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator icelake::ondemand::raw_json_string() noexcept(false) { +simdjson_inline simdjson_result::operator icelake::ondemand::raw_json_string() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator bool() noexcept(false) { +simdjson_inline simdjson_result::operator bool() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator icelake::ondemand::value() noexcept(false) { +simdjson_inline simdjson_result::operator icelake::ondemand::value() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } #endif -simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { if (error()) { return error(); } return first.current_location(); } -simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { +simdjson_inline bool simdjson_result::at_end() const noexcept { + if (error()) { return error(); } + return first.at_end(); +} + + +simdjson_inline int32_t simdjson_result::current_depth() const noexcept { + if (error()) { return error(); } + return first.current_depth(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { if (error()) { return error(); } return first.raw_json_token(); } -simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { if (error()) { return error(); } return first.at_pointer(json_pointer); } -simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { - return error(); - } +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } return first.at_path(json_path); } } // namespace simdjson -#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H -/* end file simdjson/generic/ondemand/document-inl.h for icelake */ -/* including simdjson/generic/ondemand/document_stream-inl.h for icelake: #include "simdjson/generic/ondemand/document_stream-inl.h" */ -/* begin file simdjson/generic/ondemand/document_stream-inl.h for icelake */ -#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H - -/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ -/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document_stream.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */ -/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -#include -#include namespace simdjson { namespace icelake { namespace ondemand { -#ifdef SIMDJSON_THREADS_ENABLED - -inline void stage1_worker::finish() { - // After calling "run" someone would call finish() to wait - // for the end of the processing. - // This function will wait until either the thread has done - // the processing or, else, the destructor has been called. - std::unique_lock lock(locking_mutex); - cond_var.wait(lock, [this]{return has_work == false;}); -} - -inline stage1_worker::~stage1_worker() { - // The thread may never outlive the stage1_worker instance - // and will always be stopped/joined before the stage1_worker - // instance is gone. - stop_thread(); -} - -inline void stage1_worker::start_thread() { - std::unique_lock lock(locking_mutex); - if(thread.joinable()) { - return; // This should never happen but we never want to create more than one thread. - } - thread = std::thread([this]{ - while(true) { - std::unique_lock thread_lock(locking_mutex); - // We wait for either "run" or "stop_thread" to be called. - cond_var.wait(thread_lock, [this]{return has_work || !can_work;}); - // If, for some reason, the stop_thread() method was called (i.e., the - // destructor of stage1_worker is called, then we want to immediately destroy - // the thread (and not do any more processing). - if(!can_work) { - break; - } - this->owner->stage1_thread_error = this->owner->run_stage1(*this->stage1_thread_parser, - this->_next_batch_start); - this->has_work = false; - // The condition variable call should be moved after thread_lock.unlock() for performance - // reasons but thread sanitizers may report it as a data race if we do. - // See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock - cond_var.notify_one(); // will notify "finish" - thread_lock.unlock(); - } - } - ); -} - - -inline void stage1_worker::stop_thread() { - std::unique_lock lock(locking_mutex); - // We have to make sure that all locks can be released. - can_work = false; - has_work = false; - cond_var.notify_all(); - lock.unlock(); - if(thread.joinable()) { - thread.join(); - } -} - -inline void stage1_worker::run(document_stream * ds, parser * stage1, size_t next_batch_start) { - std::unique_lock lock(locking_mutex); - owner = ds; - _next_batch_start = next_batch_start; - stage1_thread_parser = stage1; - has_work = true; - // The condition variable call should be moved after thread_lock.unlock() for performance - // reasons but thread sanitizers may report it as a data race if we do. - // See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock - cond_var.notify_one(); // will notify the thread lock that we have work - lock.unlock(); -} - -#endif // SIMDJSON_THREADS_ENABLED - -simdjson_inline document_stream::document_stream( - ondemand::parser &_parser, - const uint8_t *_buf, - size_t _len, +simdjson_inline document_reference::document_reference() noexcept : doc{nullptr} {} +simdjson_inline document_reference::document_reference(document &d) noexcept : doc(&d) {} +simdjson_inline void document_reference::rewind() noexcept { doc->rewind(); } +simdjson_inline simdjson_result document_reference::get_array() & noexcept { return doc->get_array(); } +simdjson_inline simdjson_result document_reference::get_object() & noexcept { return doc->get_object(); } +/** + * The document_reference instances are used primarily/solely for streams of JSON + * documents. + * We decided that calling 'get_double()' on the JSON document '1.233 blabla' should + * give an error, so we check for trailing content. + * + * However, for streams of JSON documents, we want to be able to start from + * "321" "321" "321" + * and parse it successfully as a stream of JSON documents, calling get_uint64_in_string() + * successfully each time. + * + * To achieve this result, we pass a 'false' to a get_root_value_iterator() method: + * this indicates that we allow trailing content. + */ +simdjson_inline simdjson_result document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); } +simdjson_inline simdjson_result document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); } +simdjson_inline simdjson_result document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); } +simdjson_inline simdjson_result document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); } +simdjson_inline simdjson_result document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); } +simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } +simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } +template +simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); } +simdjson_inline simdjson_result document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } +simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } +simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } +simdjson_inline simdjson_result document_reference::get_value() noexcept { return doc->get_value(); } +simdjson_inline simdjson_result document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_array(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_object(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_double(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_bool(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_value(); } +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline document_reference::operator T() noexcept(false) { return get(); } +simdjson_inline document_reference::operator array() & noexcept(false) { return array(*doc); } +simdjson_inline document_reference::operator object() & noexcept(false) { return object(*doc); } +simdjson_inline document_reference::operator uint64_t() noexcept(false) { return get_uint64(); } +simdjson_inline document_reference::operator int64_t() noexcept(false) { return get_int64(); } +simdjson_inline document_reference::operator double() noexcept(false) { return get_double(); } +simdjson_inline document_reference::operator std::string_view() noexcept(false) { return std::string_view(*doc); } +simdjson_inline document_reference::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } +simdjson_inline document_reference::operator bool() noexcept(false) { return get_bool(); } +simdjson_inline document_reference::operator value() noexcept(false) { return value(*doc); } +#endif +simdjson_inline simdjson_result document_reference::count_elements() & noexcept { return doc->count_elements(); } +simdjson_inline simdjson_result document_reference::count_fields() & noexcept { return doc->count_fields(); } +simdjson_inline simdjson_result document_reference::at(size_t index) & noexcept { return doc->at(index); } +simdjson_inline simdjson_result document_reference::begin() & noexcept { return doc->begin(); } +simdjson_inline simdjson_result document_reference::end() & noexcept { return doc->end(); } +simdjson_inline simdjson_result document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); } +simdjson_inline simdjson_result document_reference::find_field(const char *key) & noexcept { return doc->find_field(key); } +simdjson_inline simdjson_result document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; } +simdjson_inline simdjson_result document_reference::operator[](const char *key) & noexcept { return (*doc)[key]; } +simdjson_inline simdjson_result document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); } +simdjson_inline simdjson_result document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); } +simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } +simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } +simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } +simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } +simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } +simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } +simdjson_inline simdjson_result document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); } +simdjson_inline simdjson_result document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); } +simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } +simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } +simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } +simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } +simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} +simdjson_inline document_reference::operator document&() const noexcept { return *doc; } + +} // namespace ondemand +} // namespace icelake +} // namespace simdjson + + + +namespace simdjson { +simdjson_inline simdjson_result::simdjson_result(icelake::ondemand::document_reference value, error_code error) + noexcept : implementation_simdjson_result_base(std::forward(value), error) {} + + +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline error_code simdjson_result::rewind() noexcept { + if (error()) { return error(); } + first.rewind(); + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { + if (error()) { return error(); } + return first.get_array(); +} +simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { + if (error()) { return error(); } + return first.get_object(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { + if (error()) { return error(); } + return first.get_uint64(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { + if (error()) { return error(); } + return first.get_uint64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { + if (error()) { return error(); } + return first.get_int64(); +} +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { + if (error()) { return error(); } + return first.get_int64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { + if (error()) { return error(); } + return first.get_double(); +} +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { + if (error()) { return error(); } + return first.get_double_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(allow_replacement); +} +template +simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(receiver, allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { + if (error()) { return error(); } + return first.get_wobbly_string(); +} +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { + if (error()) { return error(); } + return first.get_raw_json_string(); +} +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { + if (error()) { return error(); } + return first.get_bool(); +} +simdjson_inline simdjson_result simdjson_result::get_value() noexcept { + if (error()) { return error(); } + return first.get_value(); +} +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { + if (error()) { return error(); } + return first.is_null(); +} +template +simdjson_inline simdjson_result simdjson_result::get() & noexcept { + if (error()) { return error(); } + return first.get(); +} +template +simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first).get(); +} +template +simdjson_inline error_code simdjson_result::get(T &out) & noexcept { + if (error()) { return error(); } + return first.get(out); +} +template +simdjson_inline error_code simdjson_result::get(T &out) && noexcept { + if (error()) { return error(); } + return std::forward(first).get(out); +} +simdjson_inline simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); +} +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + if (error()) { return error(); } + return first.is_scalar(); +} +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} +template <> +simdjson_inline error_code simdjson_result::get(icelake::ondemand::document_reference &out) & noexcept { + if (error()) { return error(); } + out = first; + return SUCCESS; +} +template <> +simdjson_inline error_code simdjson_result::get(icelake::ondemand::document_reference &out) && noexcept { + if (error()) { return error(); } + out = first; + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { + if (error()) { return error(); } + return first.is_negative(); +} +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + if (error()) { return error(); } + return first.is_integer(); +} +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + if (error()) { return error(); } + return first.get_number_type(); +} +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + if (error()) { return error(); } + return first.get_number(); +} +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline simdjson_result::operator T() noexcept(false) { + static_assert(std::is_same::value == false, "You should not call get when T is a document"); + static_assert(std::is_same::value == false, "You should not call get when T is a document"); + if (error()) { throw simdjson_error(error()); } + return first.get(); +} +simdjson_inline simdjson_result::operator icelake::ondemand::array() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator icelake::ondemand::object() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator double() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator icelake::ondemand::raw_json_string() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator bool() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator icelake::ondemand::value() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +#endif + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} + +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H +/* end file simdjson/generic/ondemand/document-inl.h for icelake */ +/* including simdjson/generic/ondemand/document_stream-inl.h for icelake: #include "simdjson/generic/ondemand/document_stream-inl.h" */ +/* begin file simdjson/generic/ondemand/document_stream-inl.h for icelake */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document_stream.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +#include + +namespace simdjson { +namespace icelake { +namespace ondemand { + +#ifdef SIMDJSON_THREADS_ENABLED + +inline void stage1_worker::finish() { + // After calling "run" someone would call finish() to wait + // for the end of the processing. + // This function will wait until either the thread has done + // the processing or, else, the destructor has been called. + std::unique_lock lock(locking_mutex); + cond_var.wait(lock, [this]{return has_work == false;}); +} + +inline stage1_worker::~stage1_worker() { + // The thread may never outlive the stage1_worker instance + // and will always be stopped/joined before the stage1_worker + // instance is gone. + stop_thread(); +} + +inline void stage1_worker::start_thread() { + std::unique_lock lock(locking_mutex); + if(thread.joinable()) { + return; // This should never happen but we never want to create more than one thread. + } + thread = std::thread([this]{ + while(true) { + std::unique_lock thread_lock(locking_mutex); + // We wait for either "run" or "stop_thread" to be called. + cond_var.wait(thread_lock, [this]{return has_work || !can_work;}); + // If, for some reason, the stop_thread() method was called (i.e., the + // destructor of stage1_worker is called, then we want to immediately destroy + // the thread (and not do any more processing). + if(!can_work) { + break; + } + this->owner->stage1_thread_error = this->owner->run_stage1(*this->stage1_thread_parser, + this->_next_batch_start); + this->has_work = false; + // The condition variable call should be moved after thread_lock.unlock() for performance + // reasons but thread sanitizers may report it as a data race if we do. + // See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock + cond_var.notify_one(); // will notify "finish" + thread_lock.unlock(); + } + } + ); +} + + +inline void stage1_worker::stop_thread() { + std::unique_lock lock(locking_mutex); + // We have to make sure that all locks can be released. + can_work = false; + has_work = false; + cond_var.notify_all(); + lock.unlock(); + if(thread.joinable()) { + thread.join(); + } +} + +inline void stage1_worker::run(document_stream * ds, parser * stage1, size_t next_batch_start) { + std::unique_lock lock(locking_mutex); + owner = ds; + _next_batch_start = next_batch_start; + stage1_thread_parser = stage1; + has_work = true; + // The condition variable call should be moved after thread_lock.unlock() for performance + // reasons but thread sanitizers may report it as a data race if we do. + // See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock + cond_var.notify_one(); // will notify the thread lock that we have work + lock.unlock(); +} + +#endif // SIMDJSON_THREADS_ENABLED + +simdjson_inline document_stream::document_stream( + ondemand::parser &_parser, + const uint8_t *_buf, + size_t _len, size_t _batch_size, bool _allow_comma_separated ) noexcept @@ -70021,7 +73012,6 @@ simdjson_inline document_stream::iterator::iterator(document_stream* _stream, bo } simdjson_inline simdjson_result document_stream::iterator::operator*() noexcept { - //if(stream->error) { return stream->error; } return simdjson_result(stream->doc, stream->error); } @@ -71880,36 +74870,39 @@ simdjson_inline const char * raw_json_string::raw() const noexcept { return rein simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(std::string_view target) noexcept { size_t pos{0}; - // if the content has no escape character, just scan through it quickly! - for(;pos < target.size() && target[pos] != '\\';pos++) {} - // slow path may begin. - bool escaping{false}; - for(;pos < target.size();pos++) { - if((target[pos] == '"') && !escaping) { - return false; - } else if(target[pos] == '\\') { - escaping = !escaping; - } else { - escaping = false; + while(pos < target.size()) { + pos = target.find('"', pos); + if(pos == std::string_view::npos) { return true; } + if(pos != 0 && target[pos-1] != '\\') { return false; } + if(pos > 1 && target[pos-2] == '\\') { + size_t backslash_count{2}; + for(size_t i = 3; i <= pos; i++) { + if(target[pos-i] == '\\') { backslash_count++; } + else { break; } + } + if(backslash_count % 2 == 0) { return false; } } + pos++; } return true; } simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(const char* target) noexcept { size_t pos{0}; - // if the content has no escape character, just scan through it quickly! - for(;target[pos] && target[pos] != '\\';pos++) {} - // slow path may begin. - bool escaping{false}; - for(;target[pos];pos++) { - if((target[pos] == '"') && !escaping) { - return false; - } else if(target[pos] == '\\') { - escaping = !escaping; - } else { - escaping = false; + while(target[pos]) { + const char * result = strchr(target+pos, '"'); + if(result == nullptr) { return true; } + pos = result - target; + if(pos != 0 && target[pos-1] != '\\') { return false; } + if(pos > 1 && target[pos-2] == '\\') { + size_t backslash_count{2}; + for(size_t i = 3; i <= pos; i++) { + if(target[pos-i] == '\\') { backslash_count++; } + else { break; } + } + if(backslash_count % 2 == 0) { return false; } } + pos++; } return true; } @@ -71921,7 +74914,7 @@ simdjson_inline bool raw_json_string::unsafe_is_equal(size_t length, std::string } simdjson_inline bool raw_json_string::unsafe_is_equal(std::string_view target) const noexcept { - // Assumptions: does not contain unescaped quote characters, and + // Assumptions: does not contain unescaped quote characters("), and // the raw content is quote terminated within a valid JSON string. if(target.size() <= SIMDJSON_PADDING) { return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size()); @@ -72395,775 +75388,230 @@ simdjson_inline simdjson_result::simdjson_res #endif // SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H /* end file simdjson/generic/ondemand/token_iterator-inl.h for icelake */ -/* including simdjson/generic/ondemand/value-inl.h for icelake: #include "simdjson/generic/ondemand/value-inl.h" */ -/* begin file simdjson/generic/ondemand/value-inl.h for icelake */ -#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H +/* including simdjson/generic/ondemand/value_iterator-inl.h for icelake: #include "simdjson/generic/ondemand/value_iterator-inl.h" */ +/* begin file simdjson/generic/ondemand/value_iterator-inl.h for icelake */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ -/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/atomparsing.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/numberparsing.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string-inl.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { namespace icelake { namespace ondemand { -simdjson_inline value::value(const value_iterator &_iter) noexcept - : iter{_iter} +simdjson_inline value_iterator::value_iterator( + json_iterator *json_iter, + depth_t depth, + token_position start_position +) noexcept : _json_iter{json_iter}, _depth{depth}, _start_position{start_position} { } -simdjson_inline value value::start(const value_iterator &iter) noexcept { - return iter; -} -simdjson_inline value value::resume(const value_iterator &iter) noexcept { - return iter; -} - -simdjson_inline simdjson_result value::get_array() noexcept { - return array::start(iter); -} -simdjson_inline simdjson_result value::get_object() noexcept { - return object::start(iter); -} -simdjson_inline simdjson_result value::start_or_resume_object() noexcept { - if (iter.at_start()) { - return get_object(); - } else { - return object::resume(iter); - } -} -simdjson_inline simdjson_result value::get_raw_json_string() noexcept { - return iter.get_raw_json_string(); -} -simdjson_inline simdjson_result value::get_string(bool allow_replacement) noexcept { - return iter.get_string(allow_replacement); -} -template -simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept { - return iter.get_string(receiver, allow_replacement); -} -simdjson_inline simdjson_result value::get_wobbly_string() noexcept { - return iter.get_wobbly_string(); -} -simdjson_inline simdjson_result value::get_double() noexcept { - return iter.get_double(); -} -simdjson_inline simdjson_result value::get_double_in_string() noexcept { - return iter.get_double_in_string(); -} -simdjson_inline simdjson_result value::get_uint64() noexcept { - return iter.get_uint64(); -} -simdjson_inline simdjson_result value::get_uint64_in_string() noexcept { - return iter.get_uint64_in_string(); -} -simdjson_inline simdjson_result value::get_int64() noexcept { - return iter.get_int64(); -} -simdjson_inline simdjson_result value::get_int64_in_string() noexcept { - return iter.get_int64_in_string(); -} -simdjson_inline simdjson_result value::get_bool() noexcept { - return iter.get_bool(); -} -simdjson_inline simdjson_result value::is_null() noexcept { - return iter.is_null(); +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_object() noexcept { + SIMDJSON_TRY( start_container('{', "Not an object", "object") ); + return started_object(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_array(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_object(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_raw_json_string(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_string(false); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_number(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_double(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_uint64(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_int64(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_bool(); } -template simdjson_inline error_code value::get(T &out) noexcept { - return get().get(out); +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_root_object() noexcept { + SIMDJSON_TRY( start_container('{', "Not an object", "object") ); + return started_root_object(); } -#if SIMDJSON_EXCEPTIONS -template -simdjson_inline value::operator T() noexcept(false) { - return get(); -} -simdjson_inline value::operator array() noexcept(false) { - return get_array(); -} -simdjson_inline value::operator object() noexcept(false) { - return get_object(); -} -simdjson_inline value::operator uint64_t() noexcept(false) { - return get_uint64(); -} -simdjson_inline value::operator int64_t() noexcept(false) { - return get_int64(); -} -simdjson_inline value::operator double() noexcept(false) { - return get_double(); -} -simdjson_inline value::operator std::string_view() noexcept(false) { - return get_string(false); -} -simdjson_inline value::operator raw_json_string() noexcept(false) { - return get_raw_json_string(); -} -simdjson_inline value::operator bool() noexcept(false) { - return get_bool(); -} +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::started_object() noexcept { + assert_at_container_start(); +#if SIMDJSON_DEVELOPMENT_CHECKS + _json_iter->set_start_position(_depth, start_position()); #endif - -simdjson_inline simdjson_result value::begin() & noexcept { - return get_array().begin(); -} -simdjson_inline simdjson_result value::end() & noexcept { - return {}; -} -simdjson_inline simdjson_result value::count_elements() & noexcept { - simdjson_result answer; - auto a = get_array(); - answer = a.count_elements(); - // count_elements leaves you pointing inside the array, at the first element. - // We need to move back so that the user can create a new array (which requires that - // we point at '['). - iter.move_at_start(); - return answer; -} -simdjson_inline simdjson_result value::count_fields() & noexcept { - simdjson_result answer; - auto a = get_object(); - answer = a.count_fields(); - iter.move_at_start(); - return answer; -} -simdjson_inline simdjson_result value::at(size_t index) noexcept { - auto a = get_array(); - return a.at(index); -} - -simdjson_inline simdjson_result value::find_field(std::string_view key) noexcept { - return start_or_resume_object().find_field(key); -} -simdjson_inline simdjson_result value::find_field(const char *key) noexcept { - return start_or_resume_object().find_field(key); -} - -simdjson_inline simdjson_result value::find_field_unordered(std::string_view key) noexcept { - return start_or_resume_object().find_field_unordered(key); -} -simdjson_inline simdjson_result value::find_field_unordered(const char *key) noexcept { - return start_or_resume_object().find_field_unordered(key); -} - -simdjson_inline simdjson_result value::operator[](std::string_view key) noexcept { - return start_or_resume_object()[key]; -} -simdjson_inline simdjson_result value::operator[](const char *key) noexcept { - return start_or_resume_object()[key]; -} - -simdjson_inline simdjson_result value::type() noexcept { - return iter.type(); + if (*_json_iter->peek() == '}') { + logger::log_value(*_json_iter, "empty object"); + _json_iter->return_current_and_advance(); + end_container(); + return false; + } + return true; } -simdjson_inline simdjson_result value::is_scalar() noexcept { - json_type this_type; - auto error = type().get(this_type); - if(error) { return error; } - return ! ((this_type == json_type::array) || (this_type == json_type::object)); +simdjson_warn_unused simdjson_inline error_code value_iterator::check_root_object() noexcept { + // When in streaming mode, we cannot expect peek_last() to be the last structural element of the + // current document. It only works in the normal mode where we have indexed a single document. + // Note that adding a check for 'streaming' is not expensive since we only have at most + // one root element. + if ( ! _json_iter->streaming() ) { + // The following lines do not fully protect against garbage content within the + // object: e.g., `{"a":2} foo }`. Users concerned with garbage content should + // call `at_end()` on the document instance at the end of the processing to + // ensure that the processing has finished at the end. + // + if (*_json_iter->peek_last() != '}') { + _json_iter->abandon(); + return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing } at end"); + } + // If the last character is } *and* the first gibberish character is also '}' + // then on-demand could accidentally go over. So we need additional checks. + // https://github.com/simdjson/simdjson/issues/1834 + // Checking that the document is balanced requires a full scan which is potentially + // expensive, but it only happens in edge cases where the first padding character is + // a closing bracket. + if ((*_json_iter->peek(_json_iter->end_position()) == '}') && (!_json_iter->balanced())) { + _json_iter->abandon(); + // The exact error would require more work. It will typically be an unclosed object. + return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "the document is unbalanced"); + } + } + return SUCCESS; } -simdjson_inline simdjson_result value::is_string() noexcept { - json_type this_type; - auto error = type().get(this_type); +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::started_root_object() noexcept { + auto error = check_root_object(); if(error) { return error; } - return (this_type == json_type::string); -} - - -simdjson_inline bool value::is_negative() noexcept { - return iter.is_negative(); + return started_object(); } -simdjson_inline simdjson_result value::is_integer() noexcept { - return iter.is_integer(); -} -simdjson_warn_unused simdjson_inline simdjson_result value::get_number_type() noexcept { - return iter.get_number_type(); -} -simdjson_warn_unused simdjson_inline simdjson_result value::get_number() noexcept { - return iter.get_number(); +simdjson_warn_unused simdjson_inline error_code value_iterator::end_container() noexcept { +#if SIMDJSON_CHECK_EOF + if (depth() > 1 && at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing parent ] or }"); } + // if (depth() <= 1 && !at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing [ or { at start"); } +#endif // SIMDJSON_CHECK_EOF + _json_iter->ascend_to(depth()-1); + return SUCCESS; } -simdjson_inline std::string_view value::raw_json_token() noexcept { - return std::string_view(reinterpret_cast(iter.peek_start()), iter.peek_start_length()); -} +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::has_next_field() noexcept { + assert_at_next(); -simdjson_inline simdjson_result value::raw_json() noexcept { - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) - { - case json_type::array: { - ondemand::array array; - SIMDJSON_TRY(get_array().get(array)); - return array.raw_json(); - } - case json_type::object: { - ondemand::object object; - SIMDJSON_TRY(get_object().get(object)); - return object.raw_json(); - } + // It's illegal to call this unless there are more tokens: anything that ends in } or ] is + // obligated to verify there are more tokens if they are not the top level. + switch (*_json_iter->return_current_and_advance()) { + case '}': + logger::log_end_value(*_json_iter, "object"); + SIMDJSON_TRY( end_container() ); + return false; + case ',': + return true; default: - return raw_json_token(); + return report_error(TAPE_ERROR, "Missing comma between object fields"); } } -simdjson_inline simdjson_result value::current_location() noexcept { - return iter.json_iter().current_location(); -} - -simdjson_inline int32_t value::current_depth() const noexcept{ - return iter.json_iter().depth(); -} +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::find_field_raw(const std::string_view key) noexcept { + error_code error; + bool has_value; + // + // Initially, the object can be in one of a few different places: + // + // 1. The start of the object, at the first field: + // + // ``` + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 2, index 1) + // ``` + if (at_first_field()) { + has_value = true; -inline bool is_pointer_well_formed(std::string_view json_pointer) noexcept { - if (simdjson_unlikely(json_pointer.empty())) { // can't be - return false; - } - if (simdjson_unlikely(json_pointer[0] != '/')) { - return false; - } - size_t escape = json_pointer.find('~'); - if (escape == std::string_view::npos) { - return true; - } - if (escape == json_pointer.size() - 1) { - return false; - } - if (json_pointer[escape + 1] != '0' && json_pointer[escape + 1] != '1') { + // + // 2. When a previous search did not yield a value or the object is empty: + // + // ``` + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 0) + // { } + // ^ (depth 0, index 2) + // ``` + // + } else if (!is_open()) { +#if SIMDJSON_DEVELOPMENT_CHECKS + // If we're past the end of the object, we're being iterated out of order. + // Note: this is not perfect detection. It's possible the user is inside some other object; if so, + // this object iterator will blithely scan that object for fields. + if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; } +#endif return false; - } - return true; -} -simdjson_inline simdjson_result value::at_pointer(std::string_view json_pointer) noexcept { - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) - { - case json_type::array: - return (*this).get_array().at_pointer(json_pointer); - case json_type::object: - return (*this).get_object().at_pointer(json_pointer); - default: - // a non-empty string can be invalid, or accessing a primitive (issue 2154) - if (is_pointer_well_formed(json_pointer)) { - return NO_SUCH_FIELD; - } - return INVALID_JSON_POINTER; + // 3. When a previous search found a field or an iterator yielded a value: + // + // ``` + // // When a field was not fully consumed (or not even touched at all) + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 2) + // // When a field was fully consumed + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 1) + // // When the last field was fully consumed + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 1) + // ``` + // + } else { + if ((error = skip_child() )) { abandon(); return error; } + if ((error = has_next_field().get(has_value) )) { abandon(); return error; } +#if SIMDJSON_DEVELOPMENT_CHECKS + if (_json_iter->start_position(_depth) != start_position()) { return OUT_OF_ORDER_ITERATION; } +#endif } -} + while (has_value) { + // Get the key and colon, stopping at the value. + raw_json_string actual_key; + // size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes + // Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2. + // field_key() advances the pointer and checks that '"' is found (corresponding to a key). + // The depth is left unchanged by field_key(). + if ((error = field_key().get(actual_key) )) { abandon(); return error; }; + // field_value() will advance and check that we find a ':' separating the + // key and the value. It will also increment the depth by one. + if ((error = field_value() )) { abandon(); return error; } + // If it matches, stop and return + // We could do it this way if we wanted to allow arbitrary + // key content (including escaped quotes). + //if (actual_key.unsafe_is_equal(max_key_length, key)) { + // Instead we do the following which may trigger buffer overruns if the + // user provides an adversarial key (containing a well placed unescaped quote + // character and being longer than the number of bytes remaining in the JSON + // input). + if (actual_key.unsafe_is_equal(key)) { + logger::log_event(*this, "match", key, -2); + // If we return here, then we return while pointing at the ':' that we just checked. + return true; + } -simdjson_inline simdjson_result value::at_path(std::string_view json_path) noexcept { - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) { - case json_type::array: - return (*this).get_array().at_path(json_path); - case json_type::object: - return (*this).get_object().at_path(json_path); - default: - return INVALID_JSON_POINTER; + // No match: skip the value and see if , or } is next + logger::log_event(*this, "no match", key, -2); + // The call to skip_child is meant to skip over the value corresponding to the key. + // After skip_child(), we are right before the next comma (',') or the final brace ('}'). + SIMDJSON_TRY( skip_child() ); // Skip the value entirely + // The has_next_field() advances the pointer and check that either ',' or '}' is found. + // It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found, + // then we are in error and we abort. + if ((error = has_next_field().get(has_value) )) { abandon(); return error; } } -} - -} // namespace ondemand -} // namespace icelake -} // namespace simdjson - -namespace simdjson { -simdjson_inline simdjson_result::simdjson_result( - icelake::ondemand::value &&value -) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) -{ -} -simdjson_inline simdjson_result::simdjson_result( - error_code error -) noexcept : - implementation_simdjson_result_base(error) -{ -} -simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); -} -simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); -} -simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { - if (error()) { return error(); } - return first.at(index); -} -simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); -} -simdjson_inline simdjson_result simdjson_result::end() & noexcept { - if (error()) { return error(); } - return {}; + // If the loop ended, we're out of fields to look at. + return false; } -simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) noexcept { - if (error()) { return error(); } - return first.find_field(key); -} -simdjson_inline simdjson_result simdjson_result::find_field(const char *key) noexcept { - if (error()) { return error(); } - return first.find_field(key); -} +SIMDJSON_PUSH_DISABLE_WARNINGS +SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::find_field_unordered_raw(const std::string_view key) noexcept { + /** + * When find_field_unordered_raw is called, we can either be pointing at the + * first key, pointing outside (at the closing brace) or if a key was matched + * we can be either pointing right afterthe ':' right before the value (that we need skip), + * or we may have consumed the value and we might be at a comma or at the + * final brace (ready for a call to has_next_field()). + */ + error_code error; + bool has_value; -simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); -} -simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); -} - -simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) noexcept { - if (error()) { return error(); } - return first[key]; -} -simdjson_inline simdjson_result simdjson_result::operator[](const char *key) noexcept { - if (error()) { return error(); } - return first[key]; -} - -simdjson_inline simdjson_result simdjson_result::get_array() noexcept { - if (error()) { return error(); } - return first.get_array(); -} -simdjson_inline simdjson_result simdjson_result::get_object() noexcept { - if (error()) { return error(); } - return first.get_object(); -} -simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { - if (error()) { return error(); } - return first.get_uint64(); -} -simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { - if (error()) { return error(); } - return first.get_uint64_in_string(); -} -simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { - if (error()) { return error(); } - return first.get_int64(); -} -simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { - if (error()) { return error(); } - return first.get_int64_in_string(); -} -simdjson_inline simdjson_result simdjson_result::get_double() noexcept { - if (error()) { return error(); } - return first.get_double(); -} -simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { - if (error()) { return error(); } - return first.get_double_in_string(); -} -simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(allow_replacement); -} -template -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(receiver, allow_replacement); -} -simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { - if (error()) { return error(); } - return first.get_wobbly_string(); -} -simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { - if (error()) { return error(); } - return first.get_raw_json_string(); -} -simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { - if (error()) { return error(); } - return first.get_bool(); -} -simdjson_inline simdjson_result simdjson_result::is_null() noexcept { - if (error()) { return error(); } - return first.is_null(); -} - -template simdjson_inline simdjson_result simdjson_result::get() noexcept { - if (error()) { return error(); } - return first.get(); -} -template simdjson_inline error_code simdjson_result::get(T &out) noexcept { - if (error()) { return error(); } - return first.get(out); -} - -template<> simdjson_inline simdjson_result simdjson_result::get() noexcept { - if (error()) { return error(); } - return std::move(first); -} -template<> simdjson_inline error_code simdjson_result::get(icelake::ondemand::value &out) noexcept { - if (error()) { return error(); } - out = first; - return SUCCESS; -} - -simdjson_inline simdjson_result simdjson_result::type() noexcept { - if (error()) { return error(); } - return first.type(); -} -simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { - if (error()) { return error(); } - return first.is_scalar(); -} -simdjson_inline simdjson_result simdjson_result::is_string() noexcept { - if (error()) { return error(); } - return first.is_string(); -} -simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { - if (error()) { return error(); } - return first.is_negative(); -} -simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { - if (error()) { return error(); } - return first.is_integer(); -} -simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { - if (error()) { return error(); } - return first.get_number_type(); -} -simdjson_inline simdjson_result simdjson_result::get_number() noexcept { - if (error()) { return error(); } - return first.get_number(); -} -#if SIMDJSON_EXCEPTIONS -template -simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return static_cast(first); -} -simdjson_inline simdjson_result::operator icelake::ondemand::array() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator icelake::ondemand::object() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator int64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator double() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator icelake::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator bool() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -#endif - -simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { - if (error()) { return error(); } - return first.raw_json_token(); -} - -simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { - if (error()) { return error(); } - return first.raw_json(); -} - -simdjson_inline simdjson_result simdjson_result::current_location() noexcept { - if (error()) { return error(); } - return first.current_location(); -} - -simdjson_inline simdjson_result simdjson_result::current_depth() const noexcept { - if (error()) { return error(); } - return first.current_depth(); -} - -simdjson_inline simdjson_result simdjson_result::at_pointer( - std::string_view json_pointer) noexcept { - if (error()) { - return error(); - } - return first.at_pointer(json_pointer); -} - -simdjson_inline simdjson_result simdjson_result::at_path( - std::string_view json_path) noexcept { - if (error()) { - return error(); - } - return first.at_path(json_path); -} - -} // namespace simdjson - -#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H -/* end file simdjson/generic/ondemand/value-inl.h for icelake */ -/* including simdjson/generic/ondemand/value_iterator-inl.h for icelake: #include "simdjson/generic/ondemand/value_iterator-inl.h" */ -/* begin file simdjson/generic/ondemand/value_iterator-inl.h for icelake */ -#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H - -/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ -/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/atomparsing.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/numberparsing.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string-inl.h" */ -/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -namespace simdjson { -namespace icelake { -namespace ondemand { - -simdjson_inline value_iterator::value_iterator( - json_iterator *json_iter, - depth_t depth, - token_position start_position -) noexcept : _json_iter{json_iter}, _depth{depth}, _start_position{start_position} -{ -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_object() noexcept { - SIMDJSON_TRY( start_container('{', "Not an object", "object") ); - return started_object(); -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_root_object() noexcept { - SIMDJSON_TRY( start_container('{', "Not an object", "object") ); - return started_root_object(); -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::started_object() noexcept { - assert_at_container_start(); -#if SIMDJSON_DEVELOPMENT_CHECKS - _json_iter->set_start_position(_depth, start_position()); -#endif - if (*_json_iter->peek() == '}') { - logger::log_value(*_json_iter, "empty object"); - _json_iter->return_current_and_advance(); - end_container(); - return false; - } - return true; -} - -simdjson_warn_unused simdjson_inline error_code value_iterator::check_root_object() noexcept { - // When in streaming mode, we cannot expect peek_last() to be the last structural element of the - // current document. It only works in the normal mode where we have indexed a single document. - // Note that adding a check for 'streaming' is not expensive since we only have at most - // one root element. - if ( ! _json_iter->streaming() ) { - // The following lines do not fully protect against garbage content within the - // object: e.g., `{"a":2} foo }`. Users concerned with garbage content should - // call `at_end()` on the document instance at the end of the processing to - // ensure that the processing has finished at the end. - // - if (*_json_iter->peek_last() != '}') { - _json_iter->abandon(); - return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing } at end"); - } - // If the last character is } *and* the first gibberish character is also '}' - // then on-demand could accidentally go over. So we need additional checks. - // https://github.com/simdjson/simdjson/issues/1834 - // Checking that the document is balanced requires a full scan which is potentially - // expensive, but it only happens in edge cases where the first padding character is - // a closing bracket. - if ((*_json_iter->peek(_json_iter->end_position()) == '}') && (!_json_iter->balanced())) { - _json_iter->abandon(); - // The exact error would require more work. It will typically be an unclosed object. - return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "the document is unbalanced"); - } - } - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::started_root_object() noexcept { - auto error = check_root_object(); - if(error) { return error; } - return started_object(); -} - -simdjson_warn_unused simdjson_inline error_code value_iterator::end_container() noexcept { -#if SIMDJSON_CHECK_EOF - if (depth() > 1 && at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing parent ] or }"); } - // if (depth() <= 1 && !at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing [ or { at start"); } -#endif // SIMDJSON_CHECK_EOF - _json_iter->ascend_to(depth()-1); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::has_next_field() noexcept { - assert_at_next(); - - // It's illegal to call this unless there are more tokens: anything that ends in } or ] is - // obligated to verify there are more tokens if they are not the top level. - switch (*_json_iter->return_current_and_advance()) { - case '}': - logger::log_end_value(*_json_iter, "object"); - SIMDJSON_TRY( end_container() ); - return false; - case ',': - return true; - default: - return report_error(TAPE_ERROR, "Missing comma between object fields"); - } -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::find_field_raw(const std::string_view key) noexcept { - error_code error; - bool has_value; - // - // Initially, the object can be in one of a few different places: - // - // 1. The start of the object, at the first field: - // - // ``` - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 2, index 1) - // ``` - if (at_first_field()) { - has_value = true; - - // - // 2. When a previous search did not yield a value or the object is empty: - // - // ``` - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 0) - // { } - // ^ (depth 0, index 2) - // ``` - // - } else if (!is_open()) { -#if SIMDJSON_DEVELOPMENT_CHECKS - // If we're past the end of the object, we're being iterated out of order. - // Note: this is not perfect detection. It's possible the user is inside some other object; if so, - // this object iterator will blithely scan that object for fields. - if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; } -#endif - return false; - - // 3. When a previous search found a field or an iterator yielded a value: - // - // ``` - // // When a field was not fully consumed (or not even touched at all) - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 2) - // // When a field was fully consumed - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 1) - // // When the last field was fully consumed - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 1) - // ``` - // - } else { - if ((error = skip_child() )) { abandon(); return error; } - if ((error = has_next_field().get(has_value) )) { abandon(); return error; } -#if SIMDJSON_DEVELOPMENT_CHECKS - if (_json_iter->start_position(_depth) != start_position()) { return OUT_OF_ORDER_ITERATION; } -#endif - } - while (has_value) { - // Get the key and colon, stopping at the value. - raw_json_string actual_key; - // size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes - // Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2. - // field_key() advances the pointer and checks that '"' is found (corresponding to a key). - // The depth is left unchanged by field_key(). - if ((error = field_key().get(actual_key) )) { abandon(); return error; }; - // field_value() will advance and check that we find a ':' separating the - // key and the value. It will also increment the depth by one. - if ((error = field_value() )) { abandon(); return error; } - // If it matches, stop and return - // We could do it this way if we wanted to allow arbitrary - // key content (including escaped quotes). - //if (actual_key.unsafe_is_equal(max_key_length, key)) { - // Instead we do the following which may trigger buffer overruns if the - // user provides an adversarial key (containing a well placed unescaped quote - // character and being longer than the number of bytes remaining in the JSON - // input). - if (actual_key.unsafe_is_equal(key)) { - logger::log_event(*this, "match", key, -2); - // If we return here, then we return while pointing at the ':' that we just checked. - return true; - } - - // No match: skip the value and see if , or } is next - logger::log_event(*this, "no match", key, -2); - // The call to skip_child is meant to skip over the value corresponding to the key. - // After skip_child(), we are right before the next comma (',') or the final brace ('}'). - SIMDJSON_TRY( skip_child() ); // Skip the value entirely - // The has_next_field() advances the pointer and check that either ',' or '}' is found. - // It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found, - // then we are in error and we abort. - if ((error = has_next_field().get(has_value) )) { abandon(); return error; } - } - - // If the loop ended, we're out of fields to look at. - return false; -} - -SIMDJSON_PUSH_DISABLE_WARNINGS -SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::find_field_unordered_raw(const std::string_view key) noexcept { - /** - * When find_field_unordered_raw is called, we can either be pointing at the - * first key, pointing outside (at the closing brace) or if a key was matched - * we can be either pointing right afterthe ':' right before the value (that we need skip), - * or we may have consumed the value and we might be at a comma or at the - * final brace (ready for a call to has_next_field()). - */ - error_code error; - bool has_value; - - // First, we scan from that point to the end. - // If we don't find a match, we may loop back around, and scan from the beginning to that point. - token_position search_start = _json_iter->position(); + // First, we scan from that point to the end. + // If we don't find a match, we may loop back around, and scan from the beginning to that point. + token_position search_start = _json_iter->position(); // We want to know whether we need to go back to the beginning. bool at_first = at_first_field(); @@ -73743,6 +76191,8 @@ simdjson_inline simdjson_result value_iterator::is_root_null(bool check_tr if(result) { // we have something that looks like a null. if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; } advance_root_scalar("null"); + } else if (json[0] == 'n') { + return incorrect_type_error("Not a null but starts with n"); } return result; } @@ -74034,6 +76484,8 @@ simdjson_inline simdjson_result::simdjson_res #endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H /* end file simdjson/generic/ondemand/value_iterator-inl.h for icelake */ + + /* end file simdjson/generic/ondemand/amalgamated.h for icelake */ /* including simdjson/icelake/end.h: #include "simdjson/icelake/end.h" */ /* begin file simdjson/icelake/end.h */ @@ -74310,7 +76762,13 @@ simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t } // namespace ppc64 } // namespace simdjson +#ifndef SIMDJSON_SWAR_NUMBER_PARSING +#if SIMDJSON_IS_BIG_ENDIAN +#define SIMDJSON_SWAR_NUMBER_PARSING 0 +#else #define SIMDJSON_SWAR_NUMBER_PARSING 1 +#endif +#endif #endif // SIMDJSON_PPC64_NUMBERPARSING_DEFS_H /* end file simdjson/ppc64/numberparsing_defs.h */ @@ -74917,6 +77375,132 @@ class value_iterator; #endif // SIMDJSON_GENERIC_ONDEMAND_BASE_H /* end file simdjson/generic/ondemand/base.h for ppc64 */ +/* including simdjson/generic/ondemand/deserialize.h for ppc64: #include "simdjson/generic/ondemand/deserialize.h" */ +/* begin file simdjson/generic/ondemand/deserialize.h for ppc64 */ +#if SIMDJSON_SUPPORTS_DESERIALIZATION + +#ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_ONDEMAND_DESERIALIZE_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +namespace simdjson { + +namespace tag_invoke_fn_ns { +void tag_invoke(); + +struct tag_invoke_fn { + template + requires requires(Tag tag, Args &&...args) { + tag_invoke(std::forward(tag), std::forward(args)...); + } + constexpr auto operator()(Tag tag, Args &&...args) const + noexcept(noexcept(tag_invoke(std::forward(tag), + std::forward(args)...))) + -> decltype(tag_invoke(std::forward(tag), + std::forward(args)...)) { + return tag_invoke(std::forward(tag), std::forward(args)...); + } +}; +} // namespace tag_invoke_fn_ns + +inline namespace tag_invoke_ns { +inline constexpr tag_invoke_fn_ns::tag_invoke_fn tag_invoke = {}; +} // namespace tag_invoke_ns + +template +concept tag_invocable = requires(Tag tag, Args... args) { + tag_invoke(std::forward(tag), std::forward(args)...); +}; + +template +concept nothrow_tag_invocable = + tag_invocable && requires(Tag tag, Args... args) { + { + tag_invoke(std::forward(tag), std::forward(args)...) + } noexcept; + }; + +template +using tag_invoke_result = + std::invoke_result; + +template +using tag_invoke_result_t = + std::invoke_result_t; + +template using tag_t = std::decay_t; + + +struct deserialize_tag; + +/// These types are deserializable in a built-in way +template struct is_builtin_deserializable : std::false_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; + +template +concept is_builtin_deserializable_v = is_builtin_deserializable::value; + +template +concept custom_deserializable = tag_invocable; + +template +concept deserializable = custom_deserializable || is_builtin_deserializable_v; + +template +concept nothrow_custom_deserializable = nothrow_tag_invocable; + +// built-in types are noexcept and if an error happens, the value simply gets ignored and the error is returned. +template +concept nothrow_deserializable = nothrow_custom_deserializable || is_builtin_deserializable_v; + +/// Deserialize Tag +inline constexpr struct deserialize_tag { + using value_type = ppc64::ondemand::value; + using document_type = ppc64::ondemand::document; + using document_reference_type = ppc64::ondemand::document_reference; + + // Customization Point for value + template + requires custom_deserializable + [[nodiscard]] constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for document + template + requires custom_deserializable + [[nodiscard]] constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for document reference + template + requires custom_deserializable + [[nodiscard]] constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + +} deserialize{}; + +} // namespace simdjson + +#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION + +/* end file simdjson/generic/ondemand/deserialize.h for ppc64 */ /* including simdjson/generic/ondemand/value_iterator.h for ppc64: #include "simdjson/generic/ondemand/value_iterator.h" */ /* begin file simdjson/generic/ondemand/value_iterator.h for ppc64 */ #ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H @@ -75421,12 +78005,15 @@ struct simdjson_result : public ppc64::implemen /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ +#include + namespace simdjson { + namespace ppc64 { namespace ondemand { - /** * An ephemeral JSON value returned during iteration. It is only valid for as long as you do * not access more data in the JSON document. @@ -75451,16 +78038,21 @@ class value { * @returns A value of the given type, parsed from the JSON. * @returns INCORRECT_TYPE If the JSON value is not the given type. */ - template simdjson_inline simdjson_result get() noexcept { - // Unless the simdjson library or the user provides an inline implementation, calling this method should - // immediately fail. - static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " - "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " - "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." - " You may also add support for custom types, see our documentation."); + template + simdjson_inline simdjson_result get() +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; } + /** * Get this value as the given type. * @@ -75470,7 +78062,32 @@ class value { * @returns INCORRECT_TYPE If the JSON value is not an object. * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ - template simdjson_inline error_code get(T &out) noexcept; + template + simdjson_inline error_code get(T &out) +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_DESERIALIZATION + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION + // Unless the simdjson library or the user provides an inline implementation, calling this method should + // immediately fail. + static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " + "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " + "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." + " You may also add support for custom types, see our documentation."); + static_cast(out); // to get rid of unused errors + return UNINITIALIZED; +#if SIMDJSON_SUPPORTS_DESERIALIZATION + } +#endif + } /** * Cast this JSON value to an array. @@ -75546,6 +78163,17 @@ class value { * Important: a value should be consumed once. Calling get_string() twice on the same value * is an error. * + * In some instances, you may want to allow replacement of invalid Unicode sequences. + * You may do so by passing the allow_replacement parameter as true. In the following + * example, the string "431924697b\udff0L\u0001Y" is not valid Unicode. By passing true + * to get_string, we allow the replacement of the invalid Unicode sequences with the Unicode + * replacement character (U+FFFD). + * + * simdjson::ondemand::parser parser; + * auto json = R"({"deviceId":"431924697b\udff0L\u0001Y"})"_padded; + * simdjson::ondemand::document doc = parser.iterate(json); + * auto view = doc["deviceId"].get_string(true); + * * @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next * time it parses a document or when it is destroyed. * @returns INCORRECT_TYPE if the JSON value is not a string. @@ -75797,6 +78425,7 @@ class value { simdjson_inline simdjson_result operator[](std::string_view key) noexcept; /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ simdjson_inline simdjson_result operator[](const char *key) noexcept; + simdjson_result operator[](int) noexcept = delete; /** * Get the type of this JSON value. It does not validate or consume the value. @@ -76164,6 +78793,7 @@ struct simdjson_result : public ppc64::implementation_si simdjson_inline simdjson_result operator[](std::string_view key) noexcept; /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ simdjson_inline simdjson_result operator[](const char *key) noexcept; + simdjson_result operator[](int) noexcept = delete; /** * Get the type of this JSON value. @@ -77219,6 +79849,22 @@ class parser { * using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the * SIMDJSON_PADDING bytes to avoid runtime warnings. * + * ### std::string references + * + * If you pass a mutable std::string reference (std::string&), the parser will seek to extend + * its capacity to SIMDJSON_PADDING bytes beyond the end of the string. + * + * Whenever you pass an std::string reference, the parser will access the bytes beyond the end of + * the string but before the end of the allocated memory (std::string::capacity()). + * If you are using a sanitizer that checks for reading uninitialized bytes or std::string's + * container-overflow checks, you may encounter sanitizer warnings. + * You can safely ignore these warnings. Or you can call simdjson::pad(std::string&) to pad the + * string with SIMDJSON_PADDING spaces: this function returns a simdjson::padding_string_view + * which can be be passed to the parser's iterate function: + * + * std::string json = R"({ "foo": 1 } { "foo": 2 } { "foo": 3 } )"; + * document doc = parser.iterate(simdjson::pad(json)); + * * @param json The JSON to parse. * @param len The length of the JSON. * @param capacity The number of bytes allocated in the JSON (must be at least len+SIMDJSON_PADDING). @@ -77413,8 +80059,12 @@ class parser { * behavior of the parser for future operations. */ bool threaded{true}; + #else + /** + * When SIMDJSON_THREADS_ENABLED is not defined, the parser instance cannot use threads. + */ + bool threaded{false}; #endif - /** * Unescape this JSON string, replacing \\ with \, \n with newline, etc. to a user-provided buffer. * The result must be valid UTF-8. @@ -77556,7 +80206,8 @@ class array { * calling this function, if successful, the array is 'rewinded' at its * beginning as if it had never been accessed. If the JSON is malformed (e.g., * there is a missing comma), then an error is returned and it is no longer - * safe to continue. + * safe to continue. Note that count_elements() does not validate the JSON values, + * only the structure of the array. * * To check that an array is empty, it is more performant to use * the is_empty() method. @@ -77834,8 +80485,11 @@ struct simdjson_result : public ppc64::implemen /* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + namespace simdjson { namespace ppc64 { namespace ondemand { @@ -78008,24 +80662,39 @@ class document { * @returns A value of the given type, parsed from the JSON. * @returns INCORRECT_TYPE If the JSON value is not the given type. */ - template simdjson_inline simdjson_result get() & noexcept { - // Unless the simdjson library or the user provides an inline implementation, calling this method should - // immediately fail. - static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " - "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " - "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." - " You may also add support for custom types, see our documentation."); - } - /** @overload template simdjson_result get() & noexcept */ - template simdjson_deprecated simdjson_inline simdjson_result get() && noexcept { - // Unless the simdjson library or the user provides an inline implementation, calling this method should - // immediately fail. - static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " - "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " - "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." - " You may also add support for custom types, see our documentation."); + template + simdjson_inline simdjson_result get() & +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "Cannot initialize the specified type."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + /** + * @overload template simdjson_result get() & noexcept + * + * We disallow the use tag_invoke CPO on a moved document; it may create UB + * if user uses `ondemand::array` or `ondemand::object` in their custom type. + * + * The member function is still remains specialize-able for compatibility + * reasons, but we completely disallow its use when a tag_invoke customization + * is provided. + */ + template + simdjson_inline simdjson_result get() && +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(!std::is_same::value && !std::is_same::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document being alive; that would be Undefined Behaviour."); + return static_cast(*this).get(); } /** @@ -78039,7 +80708,32 @@ class document { * @returns INCORRECT_TYPE If the JSON value is not an object. * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ - template simdjson_inline error_code get(T &out) & noexcept; + template + simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_DESERIALIZATION + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION + // Unless the simdjson library or the user provides an inline implementation, calling this method should + // immediately fail. + static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " + "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " + "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." + " You may also add support for custom types, see our documentation."); + static_cast(out); // to get rid of unused errors + return UNINITIALIZED; +#if SIMDJSON_SUPPORTS_DESERIALIZATION + } +#endif + } /** @overload template error_code get(T &out) & noexcept */ template simdjson_deprecated simdjson_inline error_code get(T &out) && noexcept; @@ -78141,7 +80835,8 @@ class document { * calling this function, if successful, the array is 'rewinded' at its * beginning as if it had never been accessed. If the JSON is malformed (e.g., * there is a missing comma), then an error is returned and it is no longer - * safe to continue. + * safe to continue. Note that count_elements() does not validate the JSON values, + * only the structure of the array. */ simdjson_inline simdjson_result count_elements() & noexcept; /** @@ -78253,6 +80948,7 @@ class document { simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; /** * Get the type of this JSON value. It does not validate or consume the value. @@ -78521,6 +81217,11 @@ class document { /** * A document_reference is a thin wrapper around a document reference instance. + * The document_reference instances are used primarily/solely for streams of JSON + * documents. They differ from document instances when parsing a scalar value + * (a document that is not an array or an object). In the case of a document, + * we expect the document to be fully consumed. In the case of a document_reference, + * we allow trailing content. */ class document_reference { public: @@ -78546,7 +81247,70 @@ class document_reference { simdjson_inline simdjson_result get_value() noexcept; simdjson_inline simdjson_result is_null() noexcept; - template simdjson_inline simdjson_result get() & noexcept; + template + simdjson_inline simdjson_result get() & +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "Cannot initialize the specified type."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + template + simdjson_inline simdjson_result get() && +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(!std::is_same::value && !std::is_same::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document_reference being alive; that would be Undefined Behaviour."); + return static_cast(*this).get(); + } + + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool, value + * + * Be mindful that the document instance must remain in scope while you are accessing object, array and value instances. + * + * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. + * @returns INCORRECT_TYPE If the JSON value is not an object. + * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. + */ + template + simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_DESERIALIZATION + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION + // Unless the simdjson library or the user provides an inline implementation, calling this method should + // immediately fail. + static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " + "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " + "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." + " You may also add support for custom types, see our documentation."); + static_cast(out); // to get rid of unused errors + return UNINITIALIZED; +#if SIMDJSON_SUPPORTS_DESERIALIZATION + } +#endif + } + /** @overload template error_code get(T &out) & noexcept */ + template simdjson_inline error_code get(T &out) && noexcept; simdjson_inline simdjson_result raw_json() noexcept; simdjson_inline operator document&() const noexcept; #if SIMDJSON_EXCEPTIONS @@ -78571,6 +81335,7 @@ class document_reference { simdjson_inline simdjson_result find_field(const char *key) & noexcept; simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; @@ -78649,6 +81414,7 @@ struct simdjson_result : public ppc64::implementation simdjson_inline simdjson_result find_field(const char *key) & noexcept; simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; simdjson_inline simdjson_result type() noexcept; @@ -78698,8 +81464,14 @@ struct simdjson_result : public ppc64::impl simdjson_inline simdjson_result get_bool() noexcept; simdjson_inline simdjson_result get_value() noexcept; simdjson_inline simdjson_result is_null() noexcept; + + template simdjson_inline simdjson_result get() & noexcept; + template simdjson_inline simdjson_result get() && noexcept; + + template simdjson_inline error_code get(T &out) & noexcept; + template simdjson_inline error_code get(T &out) && noexcept; #if SIMDJSON_EXCEPTIONS - template ::value == false>::type> + template explicit simdjson_inline operator T() noexcept(false); simdjson_inline operator ppc64::ondemand::array() & noexcept(false); simdjson_inline operator ppc64::ondemand::object() & noexcept(false); @@ -78720,6 +81492,7 @@ struct simdjson_result : public ppc64::impl simdjson_inline simdjson_result find_field(const char *key) & noexcept; simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; simdjson_inline simdjson_result type() noexcept; @@ -79650,6 +82423,177 @@ inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result +#include + +namespace simdjson { +template +constexpr bool require_custom_serialization = false; + +////////////////////////////// +// Number deserialization +////////////////////////////// + +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + using limits = std::numeric_limits; + + uint64_t x; + SIMDJSON_TRY(val.get_uint64().get(x)); + if (x > (limits::max)()) { + return NUMBER_OUT_OF_RANGE; + } + out = static_cast(x); + return SUCCESS; +} + +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + double x; + SIMDJSON_TRY(val.get_double().get(x)); + out = static_cast(x); + return SUCCESS; +} + +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + using limits = std::numeric_limits; + + int64_t x; + SIMDJSON_TRY(val.get_int64().get(x)); + if (x > (limits::max)() || x < (limits::min)()) { + return NUMBER_OUT_OF_RANGE; + } + out = static_cast(x); + return SUCCESS; +} + +/** + * STL containers have several constructors including one that takes a single + * size argument. Thus, some compilers (Visual Studio) will not be able to + * disambiguate between the size and container constructor. Users should + * explicitly specify the type of the container as needed: e.g., + * doc.get>(). + */ +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { + using value_type = typename std::remove_cvref_t::value_type; + static_assert( + deserializable, + "The specified type inside the container must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the container must default constructible."); + + ppc64::ondemand::array arr; + SIMDJSON_TRY(val.get_array().get(arr)); + for (auto v : arr) { + if constexpr (concepts::returns_reference) { + if (auto const err = v.get().get(concepts::emplace_one(out)); + err) { + // If an error occurs, the empty element that we just inserted gets + // removed. We're not using a temp variable because if T is a heavy + // type, we want the valid path to be the fast path and the slow path be + // the path that has errors in it. + if constexpr (requires { out.pop_back(); }) { + static_cast(out.pop_back()); + } + return err; + } + } else { + value_type temp; + if (auto const err = v.get().get(temp); err) { + return err; + } + concepts::emplace_one(out, std::move(temp)); + } + } + return SUCCESS; +} + + + +/** + * This CPO (Customization Point Object) will help deserialize into + * smart pointers. + * + * If constructing T is nothrow, this conversion should be nothrow as well since + * we return MEMALLOC if we're not able to allocate memory instead of throwing + * the error message. + * + * @tparam T The type inside the smart pointer + * @tparam ValT document/value type + * @param val document/value + * @param out a reference to the smart pointer + * @return status of the conversion + */ +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::element_type, ValT>) { + using element_type = typename std::remove_cvref_t::element_type; + + // For better error messages, don't use these as constraints on + // the tag_invoke CPO. + static_assert( + deserializable, + "The specified type inside the unique_ptr must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the unique_ptr must default constructible."); + + auto ptr = new (std::nothrow) element_type(); + if (ptr == nullptr) { + return MEMALLOC; + } + SIMDJSON_TRY(val.template get(*ptr)); + out.reset(ptr); + return SUCCESS; +} + +/** + * This CPO (Customization Point Object) will help deserialize into optional types. + */ +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::value_type, ValT>) { + using value_type = typename std::remove_cvref_t::value_type; + + static_assert( + deserializable, + "The specified type inside the unique_ptr must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the unique_ptr must default constructible."); + + if (!out) { + out.emplace(); + } + SIMDJSON_TRY(val.template get(out.value())); + return SUCCESS; +} + +} // namespace simdjson + +#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +/* end file simdjson/generic/ondemand/std_deserialize.h for ppc64 */ + // Inline definitions /* including simdjson/generic/ondemand/array-inl.h for ppc64: #include "simdjson/generic/ondemand/array-inl.h" */ /* begin file simdjson/generic/ondemand/array-inl.h for ppc64 */ @@ -79657,6 +82601,7 @@ inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result array::at_pointer(std::string_view json_pointer) n return child; } -inline std::string json_path_to_pointer_conversion(std::string_view json_path) { - if (json_path.empty() || (json_path.front() != '.' && - json_path.front() != '[')) { - return "-1"; // This is just a sentinel value, the caller should check for this and return an error. - } - - std::string result; - // Reserve space to reduce allocations, adjusting for potential increases due - // to escaping. - result.reserve(json_path.size() * 2); - - size_t i = 0; - - while (i < json_path.length()) { - if (json_path[i] == '.') { - result += '/'; - } else if (json_path[i] == '[') { - result += '/'; - ++i; // Move past the '[' - while (i < json_path.length() && json_path[i] != ']') { - if (json_path[i] == '~') { - result += "~0"; - } else if (json_path[i] == '/') { - result += "~1"; - } else { - result += json_path[i]; - } - ++i; - } - if (i == json_path.length() || json_path[i] != ']') { - return "-1"; // Using sentinel value that will be handled as an error by the caller. - } - } else { - if (json_path[i] == '~') { - result += "~0"; - } else if (json_path[i] == '/') { - result += "~1"; - } else { - result += json_path[i]; - } - } - ++i; - } - - return result; -} - inline simdjson_result array::at_path(std::string_view json_path) noexcept { auto json_pointer = json_path_to_pointer_conversion(json_path); if (json_pointer == "-1") { return INVALID_JSON_POINTER; } @@ -80018,313 +82916,276 @@ simdjson_inline simdjson_result &simdjson_resul #endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H /* end file simdjson/generic/ondemand/array_iterator-inl.h for ppc64 */ -/* including simdjson/generic/ondemand/document-inl.h for ppc64: #include "simdjson/generic/ondemand/document-inl.h" */ -/* begin file simdjson/generic/ondemand/document-inl.h for ppc64 */ -#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H +/* including simdjson/generic/ondemand/value-inl.h for ppc64: #include "simdjson/generic/ondemand/value-inl.h" */ +/* begin file simdjson/generic/ondemand/value-inl.h for ppc64 */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ -/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { namespace ppc64 { namespace ondemand { -simdjson_inline document::document(ondemand::json_iterator &&_iter) noexcept - : iter{std::forward(_iter)} +simdjson_inline value::value(const value_iterator &_iter) noexcept + : iter{_iter} { - logger::log_start_value(iter, "document"); } - -simdjson_inline document document::start(json_iterator &&iter) noexcept { - return document(std::forward(iter)); +simdjson_inline value value::start(const value_iterator &iter) noexcept { + return iter; } - -inline void document::rewind() noexcept { - iter.rewind(); +simdjson_inline value value::resume(const value_iterator &iter) noexcept { + return iter; } -inline std::string document::to_debug_string() noexcept { - return iter.to_string(); +simdjson_inline simdjson_result value::get_array() noexcept { + return array::start(iter); } - -inline simdjson_result document::current_location() const noexcept { - return iter.current_location(); +simdjson_inline simdjson_result value::get_object() noexcept { + return object::start(iter); } - -inline int32_t document::current_depth() const noexcept { - return iter.depth(); +simdjson_inline simdjson_result value::start_or_resume_object() noexcept { + if (iter.at_start()) { + return get_object(); + } else { + return object::resume(iter); + } } -inline bool document::at_end() const noexcept { - return iter.at_end(); +simdjson_inline simdjson_result value::get_raw_json_string() noexcept { + return iter.get_raw_json_string(); } - - -inline bool document::is_alive() noexcept { - return iter.is_alive(); +simdjson_inline simdjson_result value::get_string(bool allow_replacement) noexcept { + return iter.get_string(allow_replacement); } -simdjson_inline value_iterator document::resume_value_iterator() noexcept { - return value_iterator(&iter, 1, iter.root_position()); +template +simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept { + return iter.get_string(receiver, allow_replacement); } -simdjson_inline value_iterator document::get_root_value_iterator() noexcept { - return resume_value_iterator(); +simdjson_inline simdjson_result value::get_wobbly_string() noexcept { + return iter.get_wobbly_string(); } -simdjson_inline simdjson_result document::start_or_resume_object() noexcept { - if (iter.at_root()) { - return get_object(); - } else { - return object::resume(resume_value_iterator()); - } +simdjson_inline simdjson_result value::get_double() noexcept { + return iter.get_double(); } -simdjson_inline simdjson_result document::get_value() noexcept { - // Make sure we start any arrays or objects before returning, so that start_root_() - // gets called. - - // It is the convention throughout the code that the macro `SIMDJSON_DEVELOPMENT_CHECKS` determines whether - // we check for OUT_OF_ORDER_ITERATION. Proper on::demand code should never trigger this error. -#if SIMDJSON_DEVELOPMENT_CHECKS - if (!iter.at_root()) { return OUT_OF_ORDER_ITERATION; } -#endif - // assert_at_root() serves two purposes: in Debug mode, whether or not - // SIMDJSON_DEVELOPMENT_CHECKS is set or not, it checks that we are at the root of - // the document (this will typically be redundant). In release mode, it generates - // SIMDJSON_ASSUME statements to allow the compiler to make assumptions. - iter.assert_at_root(); - switch (*iter.peek()) { - case '[': { - // The following lines check that the document ends with ]. - auto value_iterator = get_root_value_iterator(); - auto error = value_iterator.check_root_array(); - if(error) { return error; } - return value(get_root_value_iterator()); - } - case '{': { - // The following lines would check that the document ends with }. - auto value_iterator = get_root_value_iterator(); - auto error = value_iterator.check_root_object(); - if(error) { return error; } - return value(get_root_value_iterator()); - } - default: - // Unfortunately, scalar documents are a special case in simdjson and they cannot - // be safely converted to value instances. - return SCALAR_DOCUMENT_AS_VALUE; - } +simdjson_inline simdjson_result value::get_double_in_string() noexcept { + return iter.get_double_in_string(); } -simdjson_inline simdjson_result document::get_array() & noexcept { - auto value = get_root_value_iterator(); - return array::start_root(value); +simdjson_inline simdjson_result value::get_uint64() noexcept { + return iter.get_uint64(); } -simdjson_inline simdjson_result document::get_object() & noexcept { - auto value = get_root_value_iterator(); - return object::start_root(value); +simdjson_inline simdjson_result value::get_uint64_in_string() noexcept { + return iter.get_uint64_in_string(); } - -/** - * We decided that calling 'get_double()' on the JSON document '1.233 blabla' should - * give an error, so we check for trailing content. We want to disallow trailing - * content. - * Thus, in several implementations below, we pass a 'true' parameter value to - * a get_root_value_iterator() method: this indicates that we disallow trailing content. - */ - -simdjson_inline simdjson_result document::get_uint64() noexcept { - return get_root_value_iterator().get_root_uint64(true); +simdjson_inline simdjson_result value::get_int64() noexcept { + return iter.get_int64(); } -simdjson_inline simdjson_result document::get_uint64_in_string() noexcept { - return get_root_value_iterator().get_root_uint64_in_string(true); +simdjson_inline simdjson_result value::get_int64_in_string() noexcept { + return iter.get_int64_in_string(); } -simdjson_inline simdjson_result document::get_int64() noexcept { - return get_root_value_iterator().get_root_int64(true); +simdjson_inline simdjson_result value::get_bool() noexcept { + return iter.get_bool(); } -simdjson_inline simdjson_result document::get_int64_in_string() noexcept { - return get_root_value_iterator().get_root_int64_in_string(true); +simdjson_inline simdjson_result value::is_null() noexcept { + return iter.is_null(); } -simdjson_inline simdjson_result document::get_double() noexcept { - return get_root_value_iterator().get_root_double(true); + +template<> simdjson_inline simdjson_result value::get() noexcept { return get_array(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_object(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_number(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_double(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_bool(); } + + +template<> simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); } +template<> simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); } +template<> simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); } +template<> simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); } +template<> simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); } +template<> simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); } +template<> simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); } +template<> simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); } +template<> simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); } + +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline value::operator T() noexcept(false) { + return get(); } -simdjson_inline simdjson_result document::get_double_in_string() noexcept { - return get_root_value_iterator().get_root_double_in_string(true); +simdjson_inline value::operator array() noexcept(false) { + return get_array(); } -simdjson_inline simdjson_result document::get_string(bool allow_replacement) noexcept { - return get_root_value_iterator().get_root_string(true, allow_replacement); +simdjson_inline value::operator object() noexcept(false) { + return get_object(); } -template -simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept { - return get_root_value_iterator().get_root_string(receiver, true, allow_replacement); +simdjson_inline value::operator uint64_t() noexcept(false) { + return get_uint64(); } -simdjson_inline simdjson_result document::get_wobbly_string() noexcept { - return get_root_value_iterator().get_root_wobbly_string(true); +simdjson_inline value::operator int64_t() noexcept(false) { + return get_int64(); } -simdjson_inline simdjson_result document::get_raw_json_string() noexcept { - return get_root_value_iterator().get_root_raw_json_string(true); +simdjson_inline value::operator double() noexcept(false) { + return get_double(); } -simdjson_inline simdjson_result document::get_bool() noexcept { - return get_root_value_iterator().get_root_bool(true); +simdjson_inline value::operator std::string_view() noexcept(false) { + return get_string(false); } -simdjson_inline simdjson_result document::is_null() noexcept { - return get_root_value_iterator().is_root_null(true); +simdjson_inline value::operator raw_json_string() noexcept(false) { + return get_raw_json_string(); } +simdjson_inline value::operator bool() noexcept(false) { + return get_bool(); +} +#endif -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_array(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_object(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_raw_json_string(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_string(false); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_double(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_uint64(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_int64(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_value(); } - -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_raw_json_string(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_value(); } - -template simdjson_inline error_code document::get(T &out) & noexcept { - return get().get(out); +simdjson_inline simdjson_result value::begin() & noexcept { + return get_array().begin(); } -template simdjson_deprecated simdjson_inline error_code document::get(T &out) && noexcept { - return std::forward(*this).get().get(out); +simdjson_inline simdjson_result value::end() & noexcept { + return {}; } - -#if SIMDJSON_EXCEPTIONS -template -simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get(); } -template -simdjson_inline document::operator T() & noexcept(false) { return get(); } -simdjson_inline document::operator array() & noexcept(false) { return get_array(); } -simdjson_inline document::operator object() & noexcept(false) { return get_object(); } -simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); } -simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); } -simdjson_inline document::operator double() noexcept(false) { return get_double(); } -simdjson_inline document::operator std::string_view() noexcept(false) { return get_string(false); } -simdjson_inline document::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } -simdjson_inline document::operator bool() noexcept(false) { return get_bool(); } -simdjson_inline document::operator value() noexcept(false) { return get_value(); } - -#endif -simdjson_inline simdjson_result document::count_elements() & noexcept { +simdjson_inline simdjson_result value::count_elements() & noexcept { + simdjson_result answer; auto a = get_array(); - simdjson_result answer = a.count_elements(); - /* If there was an array, we are now left pointing at its first element. */ - if(answer.error() == SUCCESS) { rewind(); } + answer = a.count_elements(); + // count_elements leaves you pointing inside the array, at the first element. + // We need to move back so that the user can create a new array (which requires that + // we point at '['). + iter.move_at_start(); return answer; } -simdjson_inline simdjson_result document::count_fields() & noexcept { +simdjson_inline simdjson_result value::count_fields() & noexcept { + simdjson_result answer; auto a = get_object(); - simdjson_result answer = a.count_fields(); - /* If there was an object, we are now left pointing at its first element. */ - if(answer.error() == SUCCESS) { rewind(); } + answer = a.count_fields(); + iter.move_at_start(); return answer; } -simdjson_inline simdjson_result document::at(size_t index) & noexcept { +simdjson_inline simdjson_result value::at(size_t index) noexcept { auto a = get_array(); return a.at(index); } -simdjson_inline simdjson_result document::begin() & noexcept { - return get_array().begin(); -} -simdjson_inline simdjson_result document::end() & noexcept { - return {}; -} -simdjson_inline simdjson_result document::find_field(std::string_view key) & noexcept { +simdjson_inline simdjson_result value::find_field(std::string_view key) noexcept { return start_or_resume_object().find_field(key); } -simdjson_inline simdjson_result document::find_field(const char *key) & noexcept { +simdjson_inline simdjson_result value::find_field(const char *key) noexcept { return start_or_resume_object().find_field(key); } -simdjson_inline simdjson_result document::find_field_unordered(std::string_view key) & noexcept { + +simdjson_inline simdjson_result value::find_field_unordered(std::string_view key) noexcept { return start_or_resume_object().find_field_unordered(key); } -simdjson_inline simdjson_result document::find_field_unordered(const char *key) & noexcept { +simdjson_inline simdjson_result value::find_field_unordered(const char *key) noexcept { return start_or_resume_object().find_field_unordered(key); } -simdjson_inline simdjson_result document::operator[](std::string_view key) & noexcept { + +simdjson_inline simdjson_result value::operator[](std::string_view key) noexcept { return start_or_resume_object()[key]; } -simdjson_inline simdjson_result document::operator[](const char *key) & noexcept { +simdjson_inline simdjson_result value::operator[](const char *key) noexcept { return start_or_resume_object()[key]; } -simdjson_inline error_code document::consume() noexcept { - auto error = iter.skip_child(0); - if(error) { iter.abandon(); } - return error; -} - -simdjson_inline simdjson_result document::raw_json() noexcept { - auto _iter = get_root_value_iterator(); - const uint8_t * starting_point{_iter.peek_start()}; - auto error = consume(); - if(error) { return error; } - // After 'consume()', we could be left pointing just beyond the document, but that - // is ok because we are not going to dereference the final pointer position, we just - // use it to compute the length in bytes. - const uint8_t * final_point{iter.unsafe_pointer()}; - return std::string_view(reinterpret_cast(starting_point), size_t(final_point - starting_point)); -} - -simdjson_inline simdjson_result document::type() noexcept { - return get_root_value_iterator().type(); +simdjson_inline simdjson_result value::type() noexcept { + return iter.type(); } -simdjson_inline simdjson_result document::is_scalar() noexcept { +simdjson_inline simdjson_result value::is_scalar() noexcept { json_type this_type; auto error = type().get(this_type); if(error) { return error; } return ! ((this_type == json_type::array) || (this_type == json_type::object)); } -simdjson_inline simdjson_result document::is_string() noexcept { +simdjson_inline simdjson_result value::is_string() noexcept { json_type this_type; auto error = type().get(this_type); if(error) { return error; } return (this_type == json_type::string); } -simdjson_inline bool document::is_negative() noexcept { - return get_root_value_iterator().is_root_negative(); + +simdjson_inline bool value::is_negative() noexcept { + return iter.is_negative(); } -simdjson_inline simdjson_result document::is_integer() noexcept { - return get_root_value_iterator().is_root_integer(true); +simdjson_inline simdjson_result value::is_integer() noexcept { + return iter.is_integer(); +} +simdjson_warn_unused simdjson_inline simdjson_result value::get_number_type() noexcept { + return iter.get_number_type(); +} +simdjson_warn_unused simdjson_inline simdjson_result value::get_number() noexcept { + return iter.get_number(); } -simdjson_inline simdjson_result document::get_number_type() noexcept { - return get_root_value_iterator().get_root_number_type(true); +simdjson_inline std::string_view value::raw_json_token() noexcept { + return std::string_view(reinterpret_cast(iter.peek_start()), iter.peek_start_length()); } -simdjson_inline simdjson_result document::get_number() noexcept { - return get_root_value_iterator().get_root_number(true); +simdjson_inline simdjson_result value::raw_json() noexcept { + json_type t; + SIMDJSON_TRY(type().get(t)); + switch (t) + { + case json_type::array: { + ondemand::array array; + SIMDJSON_TRY(get_array().get(array)); + return array.raw_json(); + } + case json_type::object: { + ondemand::object object; + SIMDJSON_TRY(get_object().get(object)); + return object.raw_json(); + } + default: + return raw_json_token(); + } } +simdjson_inline simdjson_result value::current_location() noexcept { + return iter.json_iter().current_location(); +} -simdjson_inline simdjson_result document::raw_json_token() noexcept { - auto _iter = get_root_value_iterator(); - return std::string_view(reinterpret_cast(_iter.peek_start()), _iter.peek_root_length()); +simdjson_inline int32_t value::current_depth() const noexcept{ + return iter.json_iter().depth(); } -simdjson_inline simdjson_result document::at_pointer(std::string_view json_pointer) noexcept { - rewind(); // Rewind the document each time at_pointer is called - if (json_pointer.empty()) { - return this->get_value(); +inline bool is_pointer_well_formed(std::string_view json_pointer) noexcept { + if (simdjson_unlikely(json_pointer.empty())) { // can't be + return false; + } + if (simdjson_unlikely(json_pointer[0] != '/')) { + return false; + } + size_t escape = json_pointer.find('~'); + if (escape == std::string_view::npos) { + return true; + } + if (escape == json_pointer.size() - 1) { + return false; } + if (json_pointer[escape + 1] != '0' && json_pointer[escape + 1] != '1') { + return false; + } + return true; +} + +simdjson_inline simdjson_result value::at_pointer(std::string_view json_pointer) noexcept { json_type t; SIMDJSON_TRY(type().get(t)); switch (t) @@ -80334,15 +83195,15 @@ simdjson_inline simdjson_result document::at_pointer(std::string_view jso case json_type::object: return (*this).get_object().at_pointer(json_pointer); default: + // a non-empty string can be invalid, or accessing a primitive (issue 2154) + if (is_pointer_well_formed(json_pointer)) { + return NO_SUCH_FIELD; + } return INVALID_JSON_POINTER; } } -simdjson_inline simdjson_result document::at_path(std::string_view json_path) noexcept { - rewind(); // Rewind the document each time at_pointer is called - if (json_path.empty()) { - return this->get_value(); - } +simdjson_inline simdjson_result value::at_path(std::string_view json_path) noexcept { json_type t; SIMDJSON_TRY(type().get(t)); switch (t) { @@ -80361,680 +83222,1319 @@ simdjson_inline simdjson_result document::at_path(std::string_view json_p namespace simdjson { -simdjson_inline simdjson_result::simdjson_result( - ppc64::ondemand::document &&value +simdjson_inline simdjson_result::simdjson_result( + ppc64::ondemand::value &&value ) noexcept : - implementation_simdjson_result_base( - std::forward(value) + implementation_simdjson_result_base( + std::forward(value) ) { } -simdjson_inline simdjson_result::simdjson_result( +simdjson_inline simdjson_result::simdjson_result( error_code error ) noexcept : - implementation_simdjson_result_base( - error - ) + implementation_simdjson_result_base(error) { } -simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { if (error()) { return error(); } return first.count_elements(); } -simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { if (error()) { return error(); } return first.count_fields(); } -simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { +simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { if (error()) { return error(); } return first.at(index); } -simdjson_inline error_code simdjson_result::rewind() noexcept { - if (error()) { return error(); } - first.rewind(); - return SUCCESS; -} -simdjson_inline simdjson_result simdjson_result::begin() & noexcept { +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { if (error()) { return error(); } return first.begin(); } -simdjson_inline simdjson_result simdjson_result::end() & noexcept { +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + if (error()) { return error(); } return {}; } -simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) noexcept { if (error()) { return error(); } - return first.find_field_unordered(key); + return first.find_field(key); } -simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) noexcept { if (error()) { return error(); } - return first.find_field_unordered(key); + return first.find_field(key); } -simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) noexcept { if (error()) { return error(); } - return first[key]; + return first.find_field_unordered(key); } -simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) noexcept { if (error()) { return error(); } - return first[key]; + return first.find_field_unordered(key); } -simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) noexcept { if (error()) { return error(); } - return first.find_field(key); + return first[key]; } -simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) noexcept { if (error()) { return error(); } - return first.find_field(key); + return first[key]; } -simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { + +simdjson_inline simdjson_result simdjson_result::get_array() noexcept { if (error()) { return error(); } return first.get_array(); } -simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { +simdjson_inline simdjson_result simdjson_result::get_object() noexcept { if (error()) { return error(); } return first.get_object(); } -simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { if (error()) { return error(); } return first.get_uint64(); } -simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { if (error()) { return error(); } return first.get_uint64_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { if (error()) { return error(); } return first.get_int64(); } -simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { if (error()) { return error(); } return first.get_int64_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_double() noexcept { +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { if (error()) { return error(); } return first.get_double(); } -simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { if (error()) { return error(); } return first.get_double_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(allow_replacement); } template -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(receiver, allow_replacement); } -simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { if (error()) { return error(); } return first.get_wobbly_string(); } -simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { if (error()) { return error(); } return first.get_raw_json_string(); } -simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { if (error()) { return error(); } return first.get_bool(); } -simdjson_inline simdjson_result simdjson_result::get_value() noexcept { - if (error()) { return error(); } - return first.get_value(); -} -simdjson_inline simdjson_result simdjson_result::is_null() noexcept { +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { if (error()) { return error(); } return first.is_null(); } -template -simdjson_inline simdjson_result simdjson_result::get() & noexcept { +template<> simdjson_inline error_code simdjson_result::get(ppc64::ondemand::value &out) noexcept { if (error()) { return error(); } - return first.get(); + out = first; + return SUCCESS; } -template -simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + +template simdjson_inline simdjson_result simdjson_result::get() noexcept { if (error()) { return error(); } - return std::forward(first).get(); + return first.get(); } -template -simdjson_inline error_code simdjson_result::get(T &out) & noexcept { +template simdjson_inline error_code simdjson_result::get(T &out) noexcept { if (error()) { return error(); } return first.get(out); } -template -simdjson_inline error_code simdjson_result::get(T &out) && noexcept { - if (error()) { return error(); } - return std::forward(first).get(out); -} -template<> simdjson_inline simdjson_result simdjson_result::get() & noexcept = delete; -template<> simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { - if (error()) { return error(); } - return std::forward(first); -} -template<> simdjson_inline error_code simdjson_result::get(ppc64::ondemand::document &out) & noexcept = delete; -template<> simdjson_inline error_code simdjson_result::get(ppc64::ondemand::document &out) && noexcept { +template<> simdjson_inline simdjson_result simdjson_result::get() noexcept { if (error()) { return error(); } - out = std::forward(first); - return SUCCESS; + return std::move(first); } -simdjson_inline simdjson_result simdjson_result::type() noexcept { +simdjson_inline simdjson_result simdjson_result::type() noexcept { if (error()) { return error(); } return first.type(); } - -simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { if (error()) { return error(); } return first.is_scalar(); } - -simdjson_inline simdjson_result simdjson_result::is_string() noexcept { +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { if (error()) { return error(); } return first.is_string(); } - -simdjson_inline bool simdjson_result::is_negative() noexcept { +simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { if (error()) { return error(); } return first.is_negative(); } - -simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { if (error()) { return error(); } return first.is_integer(); } - -simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { if (error()) { return error(); } return first.get_number_type(); } - -simdjson_inline simdjson_result simdjson_result::get_number() noexcept { +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { if (error()) { return error(); } return first.get_number(); } - - #if SIMDJSON_EXCEPTIONS -template ::value == false>::type> -simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator ppc64::ondemand::array() & noexcept(false) { +template +simdjson_inline simdjson_result::operator T() noexcept(false) { if (error()) { throw simdjson_error(error()); } - return first; + return first.get(); } -simdjson_inline simdjson_result::operator ppc64::ondemand::object() & noexcept(false) { +simdjson_inline simdjson_result::operator ppc64::ondemand::array() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { +simdjson_inline simdjson_result::operator ppc64::ondemand::object() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator int64_t() noexcept(false) { +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator double() noexcept(false) { +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { +simdjson_inline simdjson_result::operator double() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator ppc64::ondemand::raw_json_string() noexcept(false) { +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator bool() noexcept(false) { +simdjson_inline simdjson_result::operator ppc64::ondemand::raw_json_string() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator ppc64::ondemand::value() noexcept(false) { +simdjson_inline simdjson_result::operator bool() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } #endif - -simdjson_inline simdjson_result simdjson_result::current_location() noexcept { +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { if (error()) { return error(); } - return first.current_location(); + return first.raw_json_token(); } -simdjson_inline bool simdjson_result::at_end() const noexcept { +simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { if (error()) { return error(); } - return first.at_end(); + return first.raw_json(); } - -simdjson_inline int32_t simdjson_result::current_depth() const noexcept { +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { if (error()) { return error(); } - return first.current_depth(); + return first.current_location(); } -simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { +simdjson_inline simdjson_result simdjson_result::current_depth() const noexcept { if (error()) { return error(); } - return first.raw_json_token(); + return first.current_depth(); } -simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } +simdjson_inline simdjson_result simdjson_result::at_pointer( + std::string_view json_pointer) noexcept { + if (error()) { + return error(); + } return first.at_pointer(json_pointer); } -simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { return error(); } +simdjson_inline simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } return first.at_path(json_path); } } // namespace simdjson +#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H +/* end file simdjson/generic/ondemand/value-inl.h for ppc64 */ +/* including simdjson/generic/ondemand/document-inl.h for ppc64: #include "simdjson/generic/ondemand/document-inl.h" */ +/* begin file simdjson/generic/ondemand/document-inl.h for ppc64 */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { namespace ppc64 { namespace ondemand { -simdjson_inline document_reference::document_reference() noexcept : doc{nullptr} {} -simdjson_inline document_reference::document_reference(document &d) noexcept : doc(&d) {} -simdjson_inline void document_reference::rewind() noexcept { doc->rewind(); } -simdjson_inline simdjson_result document_reference::get_array() & noexcept { return doc->get_array(); } -simdjson_inline simdjson_result document_reference::get_object() & noexcept { return doc->get_object(); } -/** - * The document_reference instances are used primarily/solely for streams of JSON - * documents. - * We decided that calling 'get_double()' on the JSON document '1.233 blabla' should - * give an error, so we check for trailing content. - * - * However, for streams of JSON documents, we want to be able to start from - * "321" "321" "321" - * and parse it successfully as a stream of JSON documents, calling get_uint64_in_string() - * successfully each time. - * - * To achieve this result, we pass a 'false' to a get_root_value_iterator() method: - * this indicates that we allow trailing content. - */ -simdjson_inline simdjson_result document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); } -simdjson_inline simdjson_result document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); } -simdjson_inline simdjson_result document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); } -simdjson_inline simdjson_result document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); } -simdjson_inline simdjson_result document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); } -simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } -simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } -template -simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); } -simdjson_inline simdjson_result document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } -simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } -simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } -simdjson_inline simdjson_result document_reference::get_value() noexcept { return doc->get_value(); } -simdjson_inline simdjson_result document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_array(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_object(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_raw_json_string(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_string(false); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_double(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_uint64(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_int64(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_bool(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_value(); } -#if SIMDJSON_EXCEPTIONS -template -simdjson_inline document_reference::operator T() noexcept(false) { return get(); } -simdjson_inline document_reference::operator array() & noexcept(false) { return array(*doc); } -simdjson_inline document_reference::operator object() & noexcept(false) { return object(*doc); } -simdjson_inline document_reference::operator uint64_t() noexcept(false) { return get_uint64(); } -simdjson_inline document_reference::operator int64_t() noexcept(false) { return get_int64(); } -simdjson_inline document_reference::operator double() noexcept(false) { return get_double(); } -simdjson_inline document_reference::operator std::string_view() noexcept(false) { return std::string_view(*doc); } -simdjson_inline document_reference::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } -simdjson_inline document_reference::operator bool() noexcept(false) { return get_bool(); } -simdjson_inline document_reference::operator value() noexcept(false) { return value(*doc); } -#endif -simdjson_inline simdjson_result document_reference::count_elements() & noexcept { return doc->count_elements(); } -simdjson_inline simdjson_result document_reference::count_fields() & noexcept { return doc->count_fields(); } -simdjson_inline simdjson_result document_reference::at(size_t index) & noexcept { return doc->at(index); } -simdjson_inline simdjson_result document_reference::begin() & noexcept { return doc->begin(); } -simdjson_inline simdjson_result document_reference::end() & noexcept { return doc->end(); } -simdjson_inline simdjson_result document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); } -simdjson_inline simdjson_result document_reference::find_field(const char *key) & noexcept { return doc->find_field(key); } -simdjson_inline simdjson_result document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; } -simdjson_inline simdjson_result document_reference::operator[](const char *key) & noexcept { return (*doc)[key]; } -simdjson_inline simdjson_result document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); } -simdjson_inline simdjson_result document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); } -simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } -simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } -simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } -simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } -simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } -simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } -simdjson_inline simdjson_result document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); } -simdjson_inline simdjson_result document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); } -simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } -simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } -simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } -simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } -simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} -simdjson_inline document_reference::operator document&() const noexcept { return *doc; } +simdjson_inline document::document(ondemand::json_iterator &&_iter) noexcept + : iter{std::forward(_iter)} +{ + logger::log_start_value(iter, "document"); +} -} // namespace ondemand -} // namespace ppc64 -} // namespace simdjson +simdjson_inline document document::start(json_iterator &&iter) noexcept { + return document(std::forward(iter)); +} +inline void document::rewind() noexcept { + iter.rewind(); +} +inline std::string document::to_debug_string() noexcept { + return iter.to_string(); +} -namespace simdjson { -simdjson_inline simdjson_result::simdjson_result(ppc64::ondemand::document_reference value, error_code error) - noexcept : implementation_simdjson_result_base(std::forward(value), error) {} +inline simdjson_result document::current_location() const noexcept { + return iter.current_location(); +} +inline int32_t document::current_depth() const noexcept { + return iter.depth(); +} -simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); +inline bool document::at_end() const noexcept { + return iter.at_end(); } -simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); + + +inline bool document::is_alive() noexcept { + return iter.is_alive(); } -simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { - if (error()) { return error(); } - return first.at(index); +simdjson_inline value_iterator document::resume_value_iterator() noexcept { + return value_iterator(&iter, 1, iter.root_position()); } -simdjson_inline error_code simdjson_result::rewind() noexcept { - if (error()) { return error(); } - first.rewind(); - return SUCCESS; +simdjson_inline value_iterator document::get_root_value_iterator() noexcept { + return resume_value_iterator(); } -simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); +simdjson_inline simdjson_result document::start_or_resume_object() noexcept { + if (iter.at_root()) { + return get_object(); + } else { + return object::resume(resume_value_iterator()); + } } -simdjson_inline simdjson_result simdjson_result::end() & noexcept { - return {}; +simdjson_inline simdjson_result document::get_value() noexcept { + // Make sure we start any arrays or objects before returning, so that start_root_() + // gets called. + + // It is the convention throughout the code that the macro `SIMDJSON_DEVELOPMENT_CHECKS` determines whether + // we check for OUT_OF_ORDER_ITERATION. Proper on::demand code should never trigger this error. +#if SIMDJSON_DEVELOPMENT_CHECKS + if (!iter.at_root()) { return OUT_OF_ORDER_ITERATION; } +#endif + // assert_at_root() serves two purposes: in Debug mode, whether or not + // SIMDJSON_DEVELOPMENT_CHECKS is set or not, it checks that we are at the root of + // the document (this will typically be redundant). In release mode, it generates + // SIMDJSON_ASSUME statements to allow the compiler to make assumptions. + iter.assert_at_root(); + switch (*iter.peek()) { + case '[': { + // The following lines check that the document ends with ]. + auto value_iterator = get_root_value_iterator(); + auto error = value_iterator.check_root_array(); + if(error) { return error; } + return value(get_root_value_iterator()); + } + case '{': { + // The following lines would check that the document ends with }. + auto value_iterator = get_root_value_iterator(); + auto error = value_iterator.check_root_object(); + if(error) { return error; } + return value(get_root_value_iterator()); + } + default: + // Unfortunately, scalar documents are a special case in simdjson and they cannot + // be safely converted to value instances. + return SCALAR_DOCUMENT_AS_VALUE; + } } -simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { +simdjson_inline simdjson_result document::get_array() & noexcept { + auto value = get_root_value_iterator(); + return array::start_root(value); +} +simdjson_inline simdjson_result document::get_object() & noexcept { + auto value = get_root_value_iterator(); + return object::start_root(value); +} + +/** + * We decided that calling 'get_double()' on the JSON document '1.233 blabla' should + * give an error, so we check for trailing content. We want to disallow trailing + * content. + * Thus, in several implementations below, we pass a 'true' parameter value to + * a get_root_value_iterator() method: this indicates that we disallow trailing content. + */ + +simdjson_inline simdjson_result document::get_uint64() noexcept { + return get_root_value_iterator().get_root_uint64(true); +} +simdjson_inline simdjson_result document::get_uint64_in_string() noexcept { + return get_root_value_iterator().get_root_uint64_in_string(true); +} +simdjson_inline simdjson_result document::get_int64() noexcept { + return get_root_value_iterator().get_root_int64(true); +} +simdjson_inline simdjson_result document::get_int64_in_string() noexcept { + return get_root_value_iterator().get_root_int64_in_string(true); +} +simdjson_inline simdjson_result document::get_double() noexcept { + return get_root_value_iterator().get_root_double(true); +} +simdjson_inline simdjson_result document::get_double_in_string() noexcept { + return get_root_value_iterator().get_root_double_in_string(true); +} +simdjson_inline simdjson_result document::get_string(bool allow_replacement) noexcept { + return get_root_value_iterator().get_root_string(true, allow_replacement); +} +template +simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept { + return get_root_value_iterator().get_root_string(receiver, true, allow_replacement); +} +simdjson_inline simdjson_result document::get_wobbly_string() noexcept { + return get_root_value_iterator().get_root_wobbly_string(true); +} +simdjson_inline simdjson_result document::get_raw_json_string() noexcept { + return get_root_value_iterator().get_root_raw_json_string(true); +} +simdjson_inline simdjson_result document::get_bool() noexcept { + return get_root_value_iterator().get_root_bool(true); +} +simdjson_inline simdjson_result document::is_null() noexcept { + return get_root_value_iterator().is_root_null(true); +} + +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_array(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_object(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_double(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_value(); } + +template<> simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); } +template<> simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); } +template<> simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); } +template<> simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); } +template<> simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); } +template<> simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); } +template<> simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); } +template<> simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); } +template<> simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); } + +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_raw_json_string(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_value(); } + +#if SIMDJSON_EXCEPTIONS +template +simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get(); } +template +simdjson_inline document::operator T() & noexcept(false) { return get(); } +simdjson_inline document::operator array() & noexcept(false) { return get_array(); } +simdjson_inline document::operator object() & noexcept(false) { return get_object(); } +simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); } +simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); } +simdjson_inline document::operator double() noexcept(false) { return get_double(); } +simdjson_inline document::operator std::string_view() noexcept(false) { return get_string(false); } +simdjson_inline document::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } +simdjson_inline document::operator bool() noexcept(false) { return get_bool(); } +simdjson_inline document::operator value() noexcept(false) { return get_value(); } + +#endif +simdjson_inline simdjson_result document::count_elements() & noexcept { + auto a = get_array(); + simdjson_result answer = a.count_elements(); + /* If there was an array, we are now left pointing at its first element. */ + if(answer.error() == SUCCESS) { rewind(); } + return answer; +} +simdjson_inline simdjson_result document::count_fields() & noexcept { + auto a = get_object(); + simdjson_result answer = a.count_fields(); + /* If there was an object, we are now left pointing at its first element. */ + if(answer.error() == SUCCESS) { rewind(); } + return answer; +} +simdjson_inline simdjson_result document::at(size_t index) & noexcept { + auto a = get_array(); + return a.at(index); +} +simdjson_inline simdjson_result document::begin() & noexcept { + return get_array().begin(); +} +simdjson_inline simdjson_result document::end() & noexcept { + return {}; +} + +simdjson_inline simdjson_result document::find_field(std::string_view key) & noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result document::find_field(const char *key) & noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result document::find_field_unordered(std::string_view key) & noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result document::find_field_unordered(const char *key) & noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result document::operator[](std::string_view key) & noexcept { + return start_or_resume_object()[key]; +} +simdjson_inline simdjson_result document::operator[](const char *key) & noexcept { + return start_or_resume_object()[key]; +} + +simdjson_inline error_code document::consume() noexcept { + bool scalar = false; + auto error = is_scalar().get(scalar); + if(error) { return error; } + if(scalar) { + iter.return_current_and_advance(); + return SUCCESS; + } + error = iter.skip_child(0); + if(error) { iter.abandon(); } + return error; +} + +simdjson_inline simdjson_result document::raw_json() noexcept { + auto _iter = get_root_value_iterator(); + const uint8_t * starting_point{_iter.peek_start()}; + auto error = consume(); + if(error) { return error; } + // After 'consume()', we could be left pointing just beyond the document, but that + // is ok because we are not going to dereference the final pointer position, we just + // use it to compute the length in bytes. + const uint8_t * final_point{iter.unsafe_pointer()}; + return std::string_view(reinterpret_cast(starting_point), size_t(final_point - starting_point)); +} + +simdjson_inline simdjson_result document::type() noexcept { + return get_root_value_iterator().type(); +} + +simdjson_inline simdjson_result document::is_scalar() noexcept { + // For more speed, we could do: + // return iter.is_single_token(); + json_type this_type; + auto error = type().get(this_type); + if(error) { return error; } + return ! ((this_type == json_type::array) || (this_type == json_type::object)); +} + +simdjson_inline simdjson_result document::is_string() noexcept { + json_type this_type; + auto error = type().get(this_type); + if(error) { return error; } + return (this_type == json_type::string); +} + +simdjson_inline bool document::is_negative() noexcept { + return get_root_value_iterator().is_root_negative(); +} + +simdjson_inline simdjson_result document::is_integer() noexcept { + return get_root_value_iterator().is_root_integer(true); +} + +simdjson_inline simdjson_result document::get_number_type() noexcept { + return get_root_value_iterator().get_root_number_type(true); +} + +simdjson_inline simdjson_result document::get_number() noexcept { + return get_root_value_iterator().get_root_number(true); +} + + +simdjson_inline simdjson_result document::raw_json_token() noexcept { + auto _iter = get_root_value_iterator(); + return std::string_view(reinterpret_cast(_iter.peek_start()), _iter.peek_root_length()); +} + +simdjson_inline simdjson_result document::at_pointer(std::string_view json_pointer) noexcept { + rewind(); // Rewind the document each time at_pointer is called + if (json_pointer.empty()) { + return this->get_value(); + } + json_type t; + SIMDJSON_TRY(type().get(t)); + switch (t) + { + case json_type::array: + return (*this).get_array().at_pointer(json_pointer); + case json_type::object: + return (*this).get_object().at_pointer(json_pointer); + default: + return INVALID_JSON_POINTER; + } +} + +simdjson_inline simdjson_result document::at_path(std::string_view json_path) noexcept { + rewind(); // Rewind the document each time at_pointer is called + if (json_path.empty()) { + return this->get_value(); + } + json_type t; + SIMDJSON_TRY(type().get(t)); + switch (t) { + case json_type::array: + return (*this).get_array().at_path(json_path); + case json_type::object: + return (*this).get_object().at_path(json_path); + default: + return INVALID_JSON_POINTER; + } +} + +} // namespace ondemand +} // namespace ppc64 +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result( + ppc64::ondemand::document &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base( + error + ) +{ +} +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline error_code simdjson_result::rewind() noexcept { + if (error()) { return error(); } + first.rewind(); + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { if (error()) { return error(); } return first.find_field_unordered(key); } -simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { if (error()) { return error(); } return first.find_field_unordered(key); } -simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { if (error()) { return error(); } return first[key]; } -simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { if (error()) { return error(); } return first[key]; } -simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { if (error()) { return error(); } return first.find_field(key); } -simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { if (error()) { return error(); } return first.find_field(key); } -simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { +simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { if (error()) { return error(); } return first.get_array(); } -simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { +simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { if (error()) { return error(); } return first.get_object(); } -simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { if (error()) { return error(); } return first.get_uint64(); } -simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { if (error()) { return error(); } return first.get_uint64_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { if (error()) { return error(); } return first.get_int64(); } -simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { if (error()) { return error(); } return first.get_int64_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_double() noexcept { +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { if (error()) { return error(); } return first.get_double(); } -simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { if (error()) { return error(); } return first.get_double_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(allow_replacement); } template -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(receiver, allow_replacement); } -simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { if (error()) { return error(); } return first.get_wobbly_string(); } -simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { if (error()) { return error(); } return first.get_raw_json_string(); } -simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { if (error()) { return error(); } return first.get_bool(); } -simdjson_inline simdjson_result simdjson_result::get_value() noexcept { +simdjson_inline simdjson_result simdjson_result::get_value() noexcept { if (error()) { return error(); } return first.get_value(); } -simdjson_inline simdjson_result simdjson_result::is_null() noexcept { +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { if (error()) { return error(); } return first.is_null(); } -simdjson_inline simdjson_result simdjson_result::type() noexcept { + +template +simdjson_inline simdjson_result simdjson_result::get() & noexcept { + if (error()) { return error(); } + return first.get(); +} +template +simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first).get(); +} +template +simdjson_inline error_code simdjson_result::get(T &out) & noexcept { + if (error()) { return error(); } + return first.get(out); +} +template +simdjson_inline error_code simdjson_result::get(T &out) && noexcept { + if (error()) { return error(); } + return std::forward(first).get(out); +} + +template<> simdjson_inline simdjson_result simdjson_result::get() & noexcept = delete; +template<> simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first); +} +template<> simdjson_inline error_code simdjson_result::get(ppc64::ondemand::document &out) & noexcept = delete; +template<> simdjson_inline error_code simdjson_result::get(ppc64::ondemand::document &out) && noexcept { + if (error()) { return error(); } + out = std::forward(first); + return SUCCESS; +} + +simdjson_inline simdjson_result simdjson_result::type() noexcept { if (error()) { return error(); } return first.type(); } -simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { if (error()) { return error(); } return first.is_scalar(); } -simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { if (error()) { return error(); } return first.is_string(); } -simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { + +simdjson_inline bool simdjson_result::is_negative() noexcept { if (error()) { return error(); } return first.is_negative(); } -simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { if (error()) { return error(); } return first.is_integer(); } -simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { if (error()) { return error(); } return first.get_number_type(); } -simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { if (error()) { return error(); } return first.get_number(); } + + #if SIMDJSON_EXCEPTIONS -template ::value == false>::type> -simdjson_inline simdjson_result::operator T() noexcept(false) { +template ::value == false>::type> +simdjson_inline simdjson_result::operator T() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator ppc64::ondemand::array() & noexcept(false) { +simdjson_inline simdjson_result::operator ppc64::ondemand::array() & noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator ppc64::ondemand::object() & noexcept(false) { +simdjson_inline simdjson_result::operator ppc64::ondemand::object() & noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator int64_t() noexcept(false) { +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator double() noexcept(false) { +simdjson_inline simdjson_result::operator double() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator ppc64::ondemand::raw_json_string() noexcept(false) { +simdjson_inline simdjson_result::operator ppc64::ondemand::raw_json_string() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator bool() noexcept(false) { +simdjson_inline simdjson_result::operator bool() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator ppc64::ondemand::value() noexcept(false) { +simdjson_inline simdjson_result::operator ppc64::ondemand::value() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } #endif -simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { if (error()) { return error(); } return first.current_location(); } -simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { +simdjson_inline bool simdjson_result::at_end() const noexcept { + if (error()) { return error(); } + return first.at_end(); +} + + +simdjson_inline int32_t simdjson_result::current_depth() const noexcept { + if (error()) { return error(); } + return first.current_depth(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { if (error()) { return error(); } return first.raw_json_token(); } -simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { if (error()) { return error(); } return first.at_pointer(json_pointer); } -simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { - return error(); - } +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } return first.at_path(json_path); } } // namespace simdjson -#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H -/* end file simdjson/generic/ondemand/document-inl.h for ppc64 */ -/* including simdjson/generic/ondemand/document_stream-inl.h for ppc64: #include "simdjson/generic/ondemand/document_stream-inl.h" */ -/* begin file simdjson/generic/ondemand/document_stream-inl.h for ppc64 */ -#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H - -/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ -/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document_stream.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */ -/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -#include -#include namespace simdjson { namespace ppc64 { namespace ondemand { -#ifdef SIMDJSON_THREADS_ENABLED - -inline void stage1_worker::finish() { - // After calling "run" someone would call finish() to wait - // for the end of the processing. - // This function will wait until either the thread has done - // the processing or, else, the destructor has been called. - std::unique_lock lock(locking_mutex); - cond_var.wait(lock, [this]{return has_work == false;}); -} - -inline stage1_worker::~stage1_worker() { - // The thread may never outlive the stage1_worker instance - // and will always be stopped/joined before the stage1_worker - // instance is gone. - stop_thread(); -} - -inline void stage1_worker::start_thread() { - std::unique_lock lock(locking_mutex); - if(thread.joinable()) { - return; // This should never happen but we never want to create more than one thread. - } - thread = std::thread([this]{ - while(true) { - std::unique_lock thread_lock(locking_mutex); - // We wait for either "run" or "stop_thread" to be called. - cond_var.wait(thread_lock, [this]{return has_work || !can_work;}); - // If, for some reason, the stop_thread() method was called (i.e., the - // destructor of stage1_worker is called, then we want to immediately destroy - // the thread (and not do any more processing). - if(!can_work) { - break; - } - this->owner->stage1_thread_error = this->owner->run_stage1(*this->stage1_thread_parser, - this->_next_batch_start); - this->has_work = false; - // The condition variable call should be moved after thread_lock.unlock() for performance - // reasons but thread sanitizers may report it as a data race if we do. - // See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock - cond_var.notify_one(); // will notify "finish" - thread_lock.unlock(); - } - } - ); -} - - -inline void stage1_worker::stop_thread() { - std::unique_lock lock(locking_mutex); - // We have to make sure that all locks can be released. - can_work = false; - has_work = false; - cond_var.notify_all(); - lock.unlock(); - if(thread.joinable()) { - thread.join(); - } -} - -inline void stage1_worker::run(document_stream * ds, parser * stage1, size_t next_batch_start) { - std::unique_lock lock(locking_mutex); - owner = ds; - _next_batch_start = next_batch_start; - stage1_thread_parser = stage1; - has_work = true; - // The condition variable call should be moved after thread_lock.unlock() for performance - // reasons but thread sanitizers may report it as a data race if we do. - // See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock - cond_var.notify_one(); // will notify the thread lock that we have work - lock.unlock(); -} - -#endif // SIMDJSON_THREADS_ENABLED - -simdjson_inline document_stream::document_stream( - ondemand::parser &_parser, - const uint8_t *_buf, +simdjson_inline document_reference::document_reference() noexcept : doc{nullptr} {} +simdjson_inline document_reference::document_reference(document &d) noexcept : doc(&d) {} +simdjson_inline void document_reference::rewind() noexcept { doc->rewind(); } +simdjson_inline simdjson_result document_reference::get_array() & noexcept { return doc->get_array(); } +simdjson_inline simdjson_result document_reference::get_object() & noexcept { return doc->get_object(); } +/** + * The document_reference instances are used primarily/solely for streams of JSON + * documents. + * We decided that calling 'get_double()' on the JSON document '1.233 blabla' should + * give an error, so we check for trailing content. + * + * However, for streams of JSON documents, we want to be able to start from + * "321" "321" "321" + * and parse it successfully as a stream of JSON documents, calling get_uint64_in_string() + * successfully each time. + * + * To achieve this result, we pass a 'false' to a get_root_value_iterator() method: + * this indicates that we allow trailing content. + */ +simdjson_inline simdjson_result document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); } +simdjson_inline simdjson_result document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); } +simdjson_inline simdjson_result document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); } +simdjson_inline simdjson_result document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); } +simdjson_inline simdjson_result document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); } +simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } +simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } +template +simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); } +simdjson_inline simdjson_result document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } +simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } +simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } +simdjson_inline simdjson_result document_reference::get_value() noexcept { return doc->get_value(); } +simdjson_inline simdjson_result document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_array(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_object(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_double(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_bool(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_value(); } +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline document_reference::operator T() noexcept(false) { return get(); } +simdjson_inline document_reference::operator array() & noexcept(false) { return array(*doc); } +simdjson_inline document_reference::operator object() & noexcept(false) { return object(*doc); } +simdjson_inline document_reference::operator uint64_t() noexcept(false) { return get_uint64(); } +simdjson_inline document_reference::operator int64_t() noexcept(false) { return get_int64(); } +simdjson_inline document_reference::operator double() noexcept(false) { return get_double(); } +simdjson_inline document_reference::operator std::string_view() noexcept(false) { return std::string_view(*doc); } +simdjson_inline document_reference::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } +simdjson_inline document_reference::operator bool() noexcept(false) { return get_bool(); } +simdjson_inline document_reference::operator value() noexcept(false) { return value(*doc); } +#endif +simdjson_inline simdjson_result document_reference::count_elements() & noexcept { return doc->count_elements(); } +simdjson_inline simdjson_result document_reference::count_fields() & noexcept { return doc->count_fields(); } +simdjson_inline simdjson_result document_reference::at(size_t index) & noexcept { return doc->at(index); } +simdjson_inline simdjson_result document_reference::begin() & noexcept { return doc->begin(); } +simdjson_inline simdjson_result document_reference::end() & noexcept { return doc->end(); } +simdjson_inline simdjson_result document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); } +simdjson_inline simdjson_result document_reference::find_field(const char *key) & noexcept { return doc->find_field(key); } +simdjson_inline simdjson_result document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; } +simdjson_inline simdjson_result document_reference::operator[](const char *key) & noexcept { return (*doc)[key]; } +simdjson_inline simdjson_result document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); } +simdjson_inline simdjson_result document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); } +simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } +simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } +simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } +simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } +simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } +simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } +simdjson_inline simdjson_result document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); } +simdjson_inline simdjson_result document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); } +simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } +simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } +simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } +simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } +simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} +simdjson_inline document_reference::operator document&() const noexcept { return *doc; } + +} // namespace ondemand +} // namespace ppc64 +} // namespace simdjson + + + +namespace simdjson { +simdjson_inline simdjson_result::simdjson_result(ppc64::ondemand::document_reference value, error_code error) + noexcept : implementation_simdjson_result_base(std::forward(value), error) {} + + +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline error_code simdjson_result::rewind() noexcept { + if (error()) { return error(); } + first.rewind(); + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { + if (error()) { return error(); } + return first.get_array(); +} +simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { + if (error()) { return error(); } + return first.get_object(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { + if (error()) { return error(); } + return first.get_uint64(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { + if (error()) { return error(); } + return first.get_uint64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { + if (error()) { return error(); } + return first.get_int64(); +} +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { + if (error()) { return error(); } + return first.get_int64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { + if (error()) { return error(); } + return first.get_double(); +} +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { + if (error()) { return error(); } + return first.get_double_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(allow_replacement); +} +template +simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(receiver, allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { + if (error()) { return error(); } + return first.get_wobbly_string(); +} +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { + if (error()) { return error(); } + return first.get_raw_json_string(); +} +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { + if (error()) { return error(); } + return first.get_bool(); +} +simdjson_inline simdjson_result simdjson_result::get_value() noexcept { + if (error()) { return error(); } + return first.get_value(); +} +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { + if (error()) { return error(); } + return first.is_null(); +} +template +simdjson_inline simdjson_result simdjson_result::get() & noexcept { + if (error()) { return error(); } + return first.get(); +} +template +simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first).get(); +} +template +simdjson_inline error_code simdjson_result::get(T &out) & noexcept { + if (error()) { return error(); } + return first.get(out); +} +template +simdjson_inline error_code simdjson_result::get(T &out) && noexcept { + if (error()) { return error(); } + return std::forward(first).get(out); +} +simdjson_inline simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); +} +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + if (error()) { return error(); } + return first.is_scalar(); +} +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} +template <> +simdjson_inline error_code simdjson_result::get(ppc64::ondemand::document_reference &out) & noexcept { + if (error()) { return error(); } + out = first; + return SUCCESS; +} +template <> +simdjson_inline error_code simdjson_result::get(ppc64::ondemand::document_reference &out) && noexcept { + if (error()) { return error(); } + out = first; + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { + if (error()) { return error(); } + return first.is_negative(); +} +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + if (error()) { return error(); } + return first.is_integer(); +} +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + if (error()) { return error(); } + return first.get_number_type(); +} +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + if (error()) { return error(); } + return first.get_number(); +} +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline simdjson_result::operator T() noexcept(false) { + static_assert(std::is_same::value == false, "You should not call get when T is a document"); + static_assert(std::is_same::value == false, "You should not call get when T is a document"); + if (error()) { throw simdjson_error(error()); } + return first.get(); +} +simdjson_inline simdjson_result::operator ppc64::ondemand::array() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator ppc64::ondemand::object() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator double() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator ppc64::ondemand::raw_json_string() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator bool() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator ppc64::ondemand::value() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +#endif + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} + +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H +/* end file simdjson/generic/ondemand/document-inl.h for ppc64 */ +/* including simdjson/generic/ondemand/document_stream-inl.h for ppc64: #include "simdjson/generic/ondemand/document_stream-inl.h" */ +/* begin file simdjson/generic/ondemand/document_stream-inl.h for ppc64 */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document_stream.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +#include + +namespace simdjson { +namespace ppc64 { +namespace ondemand { + +#ifdef SIMDJSON_THREADS_ENABLED + +inline void stage1_worker::finish() { + // After calling "run" someone would call finish() to wait + // for the end of the processing. + // This function will wait until either the thread has done + // the processing or, else, the destructor has been called. + std::unique_lock lock(locking_mutex); + cond_var.wait(lock, [this]{return has_work == false;}); +} + +inline stage1_worker::~stage1_worker() { + // The thread may never outlive the stage1_worker instance + // and will always be stopped/joined before the stage1_worker + // instance is gone. + stop_thread(); +} + +inline void stage1_worker::start_thread() { + std::unique_lock lock(locking_mutex); + if(thread.joinable()) { + return; // This should never happen but we never want to create more than one thread. + } + thread = std::thread([this]{ + while(true) { + std::unique_lock thread_lock(locking_mutex); + // We wait for either "run" or "stop_thread" to be called. + cond_var.wait(thread_lock, [this]{return has_work || !can_work;}); + // If, for some reason, the stop_thread() method was called (i.e., the + // destructor of stage1_worker is called, then we want to immediately destroy + // the thread (and not do any more processing). + if(!can_work) { + break; + } + this->owner->stage1_thread_error = this->owner->run_stage1(*this->stage1_thread_parser, + this->_next_batch_start); + this->has_work = false; + // The condition variable call should be moved after thread_lock.unlock() for performance + // reasons but thread sanitizers may report it as a data race if we do. + // See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock + cond_var.notify_one(); // will notify "finish" + thread_lock.unlock(); + } + } + ); +} + + +inline void stage1_worker::stop_thread() { + std::unique_lock lock(locking_mutex); + // We have to make sure that all locks can be released. + can_work = false; + has_work = false; + cond_var.notify_all(); + lock.unlock(); + if(thread.joinable()) { + thread.join(); + } +} + +inline void stage1_worker::run(document_stream * ds, parser * stage1, size_t next_batch_start) { + std::unique_lock lock(locking_mutex); + owner = ds; + _next_batch_start = next_batch_start; + stage1_thread_parser = stage1; + has_work = true; + // The condition variable call should be moved after thread_lock.unlock() for performance + // reasons but thread sanitizers may report it as a data race if we do. + // See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock + cond_var.notify_one(); // will notify the thread lock that we have work + lock.unlock(); +} + +#endif // SIMDJSON_THREADS_ENABLED + +simdjson_inline document_stream::document_stream( + ondemand::parser &_parser, + const uint8_t *_buf, size_t _len, size_t _batch_size, bool _allow_comma_separated @@ -81094,7 +84594,6 @@ simdjson_inline document_stream::iterator::iterator(document_stream* _stream, bo } simdjson_inline simdjson_result document_stream::iterator::operator*() noexcept { - //if(stream->error) { return stream->error; } return simdjson_result(stream->doc, stream->error); } @@ -82953,36 +86452,39 @@ simdjson_inline const char * raw_json_string::raw() const noexcept { return rein simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(std::string_view target) noexcept { size_t pos{0}; - // if the content has no escape character, just scan through it quickly! - for(;pos < target.size() && target[pos] != '\\';pos++) {} - // slow path may begin. - bool escaping{false}; - for(;pos < target.size();pos++) { - if((target[pos] == '"') && !escaping) { - return false; - } else if(target[pos] == '\\') { - escaping = !escaping; - } else { - escaping = false; + while(pos < target.size()) { + pos = target.find('"', pos); + if(pos == std::string_view::npos) { return true; } + if(pos != 0 && target[pos-1] != '\\') { return false; } + if(pos > 1 && target[pos-2] == '\\') { + size_t backslash_count{2}; + for(size_t i = 3; i <= pos; i++) { + if(target[pos-i] == '\\') { backslash_count++; } + else { break; } + } + if(backslash_count % 2 == 0) { return false; } } + pos++; } return true; } simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(const char* target) noexcept { size_t pos{0}; - // if the content has no escape character, just scan through it quickly! - for(;target[pos] && target[pos] != '\\';pos++) {} - // slow path may begin. - bool escaping{false}; - for(;target[pos];pos++) { - if((target[pos] == '"') && !escaping) { - return false; - } else if(target[pos] == '\\') { - escaping = !escaping; - } else { - escaping = false; + while(target[pos]) { + const char * result = strchr(target+pos, '"'); + if(result == nullptr) { return true; } + pos = result - target; + if(pos != 0 && target[pos-1] != '\\') { return false; } + if(pos > 1 && target[pos-2] == '\\') { + size_t backslash_count{2}; + for(size_t i = 3; i <= pos; i++) { + if(target[pos-i] == '\\') { backslash_count++; } + else { break; } + } + if(backslash_count % 2 == 0) { return false; } } + pos++; } return true; } @@ -82994,7 +86496,7 @@ simdjson_inline bool raw_json_string::unsafe_is_equal(size_t length, std::string } simdjson_inline bool raw_json_string::unsafe_is_equal(std::string_view target) const noexcept { - // Assumptions: does not contain unescaped quote characters, and + // Assumptions: does not contain unescaped quote characters("), and // the raw content is quote terminated within a valid JSON string. if(target.size() <= SIMDJSON_PADDING) { return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size()); @@ -83468,775 +86970,230 @@ simdjson_inline simdjson_result::simdjson_resul #endif // SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H /* end file simdjson/generic/ondemand/token_iterator-inl.h for ppc64 */ -/* including simdjson/generic/ondemand/value-inl.h for ppc64: #include "simdjson/generic/ondemand/value-inl.h" */ -/* begin file simdjson/generic/ondemand/value-inl.h for ppc64 */ -#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H +/* including simdjson/generic/ondemand/value_iterator-inl.h for ppc64: #include "simdjson/generic/ondemand/value_iterator-inl.h" */ +/* begin file simdjson/generic/ondemand/value_iterator-inl.h for ppc64 */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ -/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/atomparsing.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/numberparsing.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string-inl.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { namespace ppc64 { namespace ondemand { -simdjson_inline value::value(const value_iterator &_iter) noexcept - : iter{_iter} +simdjson_inline value_iterator::value_iterator( + json_iterator *json_iter, + depth_t depth, + token_position start_position +) noexcept : _json_iter{json_iter}, _depth{depth}, _start_position{start_position} { } -simdjson_inline value value::start(const value_iterator &iter) noexcept { - return iter; -} -simdjson_inline value value::resume(const value_iterator &iter) noexcept { - return iter; -} - -simdjson_inline simdjson_result value::get_array() noexcept { - return array::start(iter); -} -simdjson_inline simdjson_result value::get_object() noexcept { - return object::start(iter); -} -simdjson_inline simdjson_result value::start_or_resume_object() noexcept { - if (iter.at_start()) { - return get_object(); - } else { - return object::resume(iter); - } -} -simdjson_inline simdjson_result value::get_raw_json_string() noexcept { - return iter.get_raw_json_string(); -} -simdjson_inline simdjson_result value::get_string(bool allow_replacement) noexcept { - return iter.get_string(allow_replacement); -} -template -simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept { - return iter.get_string(receiver, allow_replacement); -} -simdjson_inline simdjson_result value::get_wobbly_string() noexcept { - return iter.get_wobbly_string(); -} -simdjson_inline simdjson_result value::get_double() noexcept { - return iter.get_double(); -} -simdjson_inline simdjson_result value::get_double_in_string() noexcept { - return iter.get_double_in_string(); -} -simdjson_inline simdjson_result value::get_uint64() noexcept { - return iter.get_uint64(); -} -simdjson_inline simdjson_result value::get_uint64_in_string() noexcept { - return iter.get_uint64_in_string(); -} -simdjson_inline simdjson_result value::get_int64() noexcept { - return iter.get_int64(); -} -simdjson_inline simdjson_result value::get_int64_in_string() noexcept { - return iter.get_int64_in_string(); -} -simdjson_inline simdjson_result value::get_bool() noexcept { - return iter.get_bool(); -} -simdjson_inline simdjson_result value::is_null() noexcept { - return iter.is_null(); +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_object() noexcept { + SIMDJSON_TRY( start_container('{', "Not an object", "object") ); + return started_object(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_array(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_object(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_raw_json_string(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_string(false); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_number(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_double(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_uint64(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_int64(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_bool(); } -template simdjson_inline error_code value::get(T &out) noexcept { - return get().get(out); +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_root_object() noexcept { + SIMDJSON_TRY( start_container('{', "Not an object", "object") ); + return started_root_object(); } -#if SIMDJSON_EXCEPTIONS -template -simdjson_inline value::operator T() noexcept(false) { - return get(); -} -simdjson_inline value::operator array() noexcept(false) { - return get_array(); -} -simdjson_inline value::operator object() noexcept(false) { - return get_object(); -} -simdjson_inline value::operator uint64_t() noexcept(false) { - return get_uint64(); -} -simdjson_inline value::operator int64_t() noexcept(false) { - return get_int64(); -} -simdjson_inline value::operator double() noexcept(false) { - return get_double(); -} -simdjson_inline value::operator std::string_view() noexcept(false) { - return get_string(false); -} -simdjson_inline value::operator raw_json_string() noexcept(false) { - return get_raw_json_string(); -} -simdjson_inline value::operator bool() noexcept(false) { - return get_bool(); -} +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::started_object() noexcept { + assert_at_container_start(); +#if SIMDJSON_DEVELOPMENT_CHECKS + _json_iter->set_start_position(_depth, start_position()); #endif - -simdjson_inline simdjson_result value::begin() & noexcept { - return get_array().begin(); -} -simdjson_inline simdjson_result value::end() & noexcept { - return {}; -} -simdjson_inline simdjson_result value::count_elements() & noexcept { - simdjson_result answer; - auto a = get_array(); - answer = a.count_elements(); - // count_elements leaves you pointing inside the array, at the first element. - // We need to move back so that the user can create a new array (which requires that - // we point at '['). - iter.move_at_start(); - return answer; -} -simdjson_inline simdjson_result value::count_fields() & noexcept { - simdjson_result answer; - auto a = get_object(); - answer = a.count_fields(); - iter.move_at_start(); - return answer; -} -simdjson_inline simdjson_result value::at(size_t index) noexcept { - auto a = get_array(); - return a.at(index); -} - -simdjson_inline simdjson_result value::find_field(std::string_view key) noexcept { - return start_or_resume_object().find_field(key); -} -simdjson_inline simdjson_result value::find_field(const char *key) noexcept { - return start_or_resume_object().find_field(key); -} - -simdjson_inline simdjson_result value::find_field_unordered(std::string_view key) noexcept { - return start_or_resume_object().find_field_unordered(key); -} -simdjson_inline simdjson_result value::find_field_unordered(const char *key) noexcept { - return start_or_resume_object().find_field_unordered(key); -} - -simdjson_inline simdjson_result value::operator[](std::string_view key) noexcept { - return start_or_resume_object()[key]; -} -simdjson_inline simdjson_result value::operator[](const char *key) noexcept { - return start_or_resume_object()[key]; -} - -simdjson_inline simdjson_result value::type() noexcept { - return iter.type(); + if (*_json_iter->peek() == '}') { + logger::log_value(*_json_iter, "empty object"); + _json_iter->return_current_and_advance(); + end_container(); + return false; + } + return true; } -simdjson_inline simdjson_result value::is_scalar() noexcept { - json_type this_type; - auto error = type().get(this_type); - if(error) { return error; } - return ! ((this_type == json_type::array) || (this_type == json_type::object)); +simdjson_warn_unused simdjson_inline error_code value_iterator::check_root_object() noexcept { + // When in streaming mode, we cannot expect peek_last() to be the last structural element of the + // current document. It only works in the normal mode where we have indexed a single document. + // Note that adding a check for 'streaming' is not expensive since we only have at most + // one root element. + if ( ! _json_iter->streaming() ) { + // The following lines do not fully protect against garbage content within the + // object: e.g., `{"a":2} foo }`. Users concerned with garbage content should + // call `at_end()` on the document instance at the end of the processing to + // ensure that the processing has finished at the end. + // + if (*_json_iter->peek_last() != '}') { + _json_iter->abandon(); + return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing } at end"); + } + // If the last character is } *and* the first gibberish character is also '}' + // then on-demand could accidentally go over. So we need additional checks. + // https://github.com/simdjson/simdjson/issues/1834 + // Checking that the document is balanced requires a full scan which is potentially + // expensive, but it only happens in edge cases where the first padding character is + // a closing bracket. + if ((*_json_iter->peek(_json_iter->end_position()) == '}') && (!_json_iter->balanced())) { + _json_iter->abandon(); + // The exact error would require more work. It will typically be an unclosed object. + return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "the document is unbalanced"); + } + } + return SUCCESS; } -simdjson_inline simdjson_result value::is_string() noexcept { - json_type this_type; - auto error = type().get(this_type); +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::started_root_object() noexcept { + auto error = check_root_object(); if(error) { return error; } - return (this_type == json_type::string); -} - - -simdjson_inline bool value::is_negative() noexcept { - return iter.is_negative(); + return started_object(); } -simdjson_inline simdjson_result value::is_integer() noexcept { - return iter.is_integer(); -} -simdjson_warn_unused simdjson_inline simdjson_result value::get_number_type() noexcept { - return iter.get_number_type(); -} -simdjson_warn_unused simdjson_inline simdjson_result value::get_number() noexcept { - return iter.get_number(); +simdjson_warn_unused simdjson_inline error_code value_iterator::end_container() noexcept { +#if SIMDJSON_CHECK_EOF + if (depth() > 1 && at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing parent ] or }"); } + // if (depth() <= 1 && !at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing [ or { at start"); } +#endif // SIMDJSON_CHECK_EOF + _json_iter->ascend_to(depth()-1); + return SUCCESS; } -simdjson_inline std::string_view value::raw_json_token() noexcept { - return std::string_view(reinterpret_cast(iter.peek_start()), iter.peek_start_length()); -} +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::has_next_field() noexcept { + assert_at_next(); -simdjson_inline simdjson_result value::raw_json() noexcept { - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) - { - case json_type::array: { - ondemand::array array; - SIMDJSON_TRY(get_array().get(array)); - return array.raw_json(); - } - case json_type::object: { - ondemand::object object; - SIMDJSON_TRY(get_object().get(object)); - return object.raw_json(); - } + // It's illegal to call this unless there are more tokens: anything that ends in } or ] is + // obligated to verify there are more tokens if they are not the top level. + switch (*_json_iter->return_current_and_advance()) { + case '}': + logger::log_end_value(*_json_iter, "object"); + SIMDJSON_TRY( end_container() ); + return false; + case ',': + return true; default: - return raw_json_token(); + return report_error(TAPE_ERROR, "Missing comma between object fields"); } } -simdjson_inline simdjson_result value::current_location() noexcept { - return iter.json_iter().current_location(); -} - -simdjson_inline int32_t value::current_depth() const noexcept{ - return iter.json_iter().depth(); -} +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::find_field_raw(const std::string_view key) noexcept { + error_code error; + bool has_value; + // + // Initially, the object can be in one of a few different places: + // + // 1. The start of the object, at the first field: + // + // ``` + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 2, index 1) + // ``` + if (at_first_field()) { + has_value = true; -inline bool is_pointer_well_formed(std::string_view json_pointer) noexcept { - if (simdjson_unlikely(json_pointer.empty())) { // can't be - return false; - } - if (simdjson_unlikely(json_pointer[0] != '/')) { - return false; - } - size_t escape = json_pointer.find('~'); - if (escape == std::string_view::npos) { - return true; - } - if (escape == json_pointer.size() - 1) { - return false; - } - if (json_pointer[escape + 1] != '0' && json_pointer[escape + 1] != '1') { + // + // 2. When a previous search did not yield a value or the object is empty: + // + // ``` + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 0) + // { } + // ^ (depth 0, index 2) + // ``` + // + } else if (!is_open()) { +#if SIMDJSON_DEVELOPMENT_CHECKS + // If we're past the end of the object, we're being iterated out of order. + // Note: this is not perfect detection. It's possible the user is inside some other object; if so, + // this object iterator will blithely scan that object for fields. + if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; } +#endif return false; - } - return true; -} -simdjson_inline simdjson_result value::at_pointer(std::string_view json_pointer) noexcept { - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) - { - case json_type::array: - return (*this).get_array().at_pointer(json_pointer); - case json_type::object: - return (*this).get_object().at_pointer(json_pointer); - default: - // a non-empty string can be invalid, or accessing a primitive (issue 2154) - if (is_pointer_well_formed(json_pointer)) { - return NO_SUCH_FIELD; - } - return INVALID_JSON_POINTER; + // 3. When a previous search found a field or an iterator yielded a value: + // + // ``` + // // When a field was not fully consumed (or not even touched at all) + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 2) + // // When a field was fully consumed + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 1) + // // When the last field was fully consumed + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 1) + // ``` + // + } else { + if ((error = skip_child() )) { abandon(); return error; } + if ((error = has_next_field().get(has_value) )) { abandon(); return error; } +#if SIMDJSON_DEVELOPMENT_CHECKS + if (_json_iter->start_position(_depth) != start_position()) { return OUT_OF_ORDER_ITERATION; } +#endif } -} + while (has_value) { + // Get the key and colon, stopping at the value. + raw_json_string actual_key; + // size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes + // Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2. + // field_key() advances the pointer and checks that '"' is found (corresponding to a key). + // The depth is left unchanged by field_key(). + if ((error = field_key().get(actual_key) )) { abandon(); return error; }; + // field_value() will advance and check that we find a ':' separating the + // key and the value. It will also increment the depth by one. + if ((error = field_value() )) { abandon(); return error; } + // If it matches, stop and return + // We could do it this way if we wanted to allow arbitrary + // key content (including escaped quotes). + //if (actual_key.unsafe_is_equal(max_key_length, key)) { + // Instead we do the following which may trigger buffer overruns if the + // user provides an adversarial key (containing a well placed unescaped quote + // character and being longer than the number of bytes remaining in the JSON + // input). + if (actual_key.unsafe_is_equal(key)) { + logger::log_event(*this, "match", key, -2); + // If we return here, then we return while pointing at the ':' that we just checked. + return true; + } -simdjson_inline simdjson_result value::at_path(std::string_view json_path) noexcept { - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) { - case json_type::array: - return (*this).get_array().at_path(json_path); - case json_type::object: - return (*this).get_object().at_path(json_path); - default: - return INVALID_JSON_POINTER; + // No match: skip the value and see if , or } is next + logger::log_event(*this, "no match", key, -2); + // The call to skip_child is meant to skip over the value corresponding to the key. + // After skip_child(), we are right before the next comma (',') or the final brace ('}'). + SIMDJSON_TRY( skip_child() ); // Skip the value entirely + // The has_next_field() advances the pointer and check that either ',' or '}' is found. + // It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found, + // then we are in error and we abort. + if ((error = has_next_field().get(has_value) )) { abandon(); return error; } } -} - -} // namespace ondemand -} // namespace ppc64 -} // namespace simdjson - -namespace simdjson { -simdjson_inline simdjson_result::simdjson_result( - ppc64::ondemand::value &&value -) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) -{ -} -simdjson_inline simdjson_result::simdjson_result( - error_code error -) noexcept : - implementation_simdjson_result_base(error) -{ -} -simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); -} -simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); -} -simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { - if (error()) { return error(); } - return first.at(index); -} -simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); -} -simdjson_inline simdjson_result simdjson_result::end() & noexcept { - if (error()) { return error(); } - return {}; + // If the loop ended, we're out of fields to look at. + return false; } -simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) noexcept { - if (error()) { return error(); } - return first.find_field(key); -} -simdjson_inline simdjson_result simdjson_result::find_field(const char *key) noexcept { - if (error()) { return error(); } - return first.find_field(key); -} +SIMDJSON_PUSH_DISABLE_WARNINGS +SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::find_field_unordered_raw(const std::string_view key) noexcept { + /** + * When find_field_unordered_raw is called, we can either be pointing at the + * first key, pointing outside (at the closing brace) or if a key was matched + * we can be either pointing right afterthe ':' right before the value (that we need skip), + * or we may have consumed the value and we might be at a comma or at the + * final brace (ready for a call to has_next_field()). + */ + error_code error; + bool has_value; -simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); -} -simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); -} - -simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) noexcept { - if (error()) { return error(); } - return first[key]; -} -simdjson_inline simdjson_result simdjson_result::operator[](const char *key) noexcept { - if (error()) { return error(); } - return first[key]; -} - -simdjson_inline simdjson_result simdjson_result::get_array() noexcept { - if (error()) { return error(); } - return first.get_array(); -} -simdjson_inline simdjson_result simdjson_result::get_object() noexcept { - if (error()) { return error(); } - return first.get_object(); -} -simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { - if (error()) { return error(); } - return first.get_uint64(); -} -simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { - if (error()) { return error(); } - return first.get_uint64_in_string(); -} -simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { - if (error()) { return error(); } - return first.get_int64(); -} -simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { - if (error()) { return error(); } - return first.get_int64_in_string(); -} -simdjson_inline simdjson_result simdjson_result::get_double() noexcept { - if (error()) { return error(); } - return first.get_double(); -} -simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { - if (error()) { return error(); } - return first.get_double_in_string(); -} -simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(allow_replacement); -} -template -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(receiver, allow_replacement); -} -simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { - if (error()) { return error(); } - return first.get_wobbly_string(); -} -simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { - if (error()) { return error(); } - return first.get_raw_json_string(); -} -simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { - if (error()) { return error(); } - return first.get_bool(); -} -simdjson_inline simdjson_result simdjson_result::is_null() noexcept { - if (error()) { return error(); } - return first.is_null(); -} - -template simdjson_inline simdjson_result simdjson_result::get() noexcept { - if (error()) { return error(); } - return first.get(); -} -template simdjson_inline error_code simdjson_result::get(T &out) noexcept { - if (error()) { return error(); } - return first.get(out); -} - -template<> simdjson_inline simdjson_result simdjson_result::get() noexcept { - if (error()) { return error(); } - return std::move(first); -} -template<> simdjson_inline error_code simdjson_result::get(ppc64::ondemand::value &out) noexcept { - if (error()) { return error(); } - out = first; - return SUCCESS; -} - -simdjson_inline simdjson_result simdjson_result::type() noexcept { - if (error()) { return error(); } - return first.type(); -} -simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { - if (error()) { return error(); } - return first.is_scalar(); -} -simdjson_inline simdjson_result simdjson_result::is_string() noexcept { - if (error()) { return error(); } - return first.is_string(); -} -simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { - if (error()) { return error(); } - return first.is_negative(); -} -simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { - if (error()) { return error(); } - return first.is_integer(); -} -simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { - if (error()) { return error(); } - return first.get_number_type(); -} -simdjson_inline simdjson_result simdjson_result::get_number() noexcept { - if (error()) { return error(); } - return first.get_number(); -} -#if SIMDJSON_EXCEPTIONS -template -simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return static_cast(first); -} -simdjson_inline simdjson_result::operator ppc64::ondemand::array() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator ppc64::ondemand::object() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator int64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator double() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator ppc64::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator bool() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -#endif - -simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { - if (error()) { return error(); } - return first.raw_json_token(); -} - -simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { - if (error()) { return error(); } - return first.raw_json(); -} - -simdjson_inline simdjson_result simdjson_result::current_location() noexcept { - if (error()) { return error(); } - return first.current_location(); -} - -simdjson_inline simdjson_result simdjson_result::current_depth() const noexcept { - if (error()) { return error(); } - return first.current_depth(); -} - -simdjson_inline simdjson_result simdjson_result::at_pointer( - std::string_view json_pointer) noexcept { - if (error()) { - return error(); - } - return first.at_pointer(json_pointer); -} - -simdjson_inline simdjson_result simdjson_result::at_path( - std::string_view json_path) noexcept { - if (error()) { - return error(); - } - return first.at_path(json_path); -} - -} // namespace simdjson - -#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H -/* end file simdjson/generic/ondemand/value-inl.h for ppc64 */ -/* including simdjson/generic/ondemand/value_iterator-inl.h for ppc64: #include "simdjson/generic/ondemand/value_iterator-inl.h" */ -/* begin file simdjson/generic/ondemand/value_iterator-inl.h for ppc64 */ -#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H - -/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ -/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/atomparsing.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/numberparsing.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string-inl.h" */ -/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -namespace simdjson { -namespace ppc64 { -namespace ondemand { - -simdjson_inline value_iterator::value_iterator( - json_iterator *json_iter, - depth_t depth, - token_position start_position -) noexcept : _json_iter{json_iter}, _depth{depth}, _start_position{start_position} -{ -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_object() noexcept { - SIMDJSON_TRY( start_container('{', "Not an object", "object") ); - return started_object(); -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_root_object() noexcept { - SIMDJSON_TRY( start_container('{', "Not an object", "object") ); - return started_root_object(); -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::started_object() noexcept { - assert_at_container_start(); -#if SIMDJSON_DEVELOPMENT_CHECKS - _json_iter->set_start_position(_depth, start_position()); -#endif - if (*_json_iter->peek() == '}') { - logger::log_value(*_json_iter, "empty object"); - _json_iter->return_current_and_advance(); - end_container(); - return false; - } - return true; -} - -simdjson_warn_unused simdjson_inline error_code value_iterator::check_root_object() noexcept { - // When in streaming mode, we cannot expect peek_last() to be the last structural element of the - // current document. It only works in the normal mode where we have indexed a single document. - // Note that adding a check for 'streaming' is not expensive since we only have at most - // one root element. - if ( ! _json_iter->streaming() ) { - // The following lines do not fully protect against garbage content within the - // object: e.g., `{"a":2} foo }`. Users concerned with garbage content should - // call `at_end()` on the document instance at the end of the processing to - // ensure that the processing has finished at the end. - // - if (*_json_iter->peek_last() != '}') { - _json_iter->abandon(); - return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing } at end"); - } - // If the last character is } *and* the first gibberish character is also '}' - // then on-demand could accidentally go over. So we need additional checks. - // https://github.com/simdjson/simdjson/issues/1834 - // Checking that the document is balanced requires a full scan which is potentially - // expensive, but it only happens in edge cases where the first padding character is - // a closing bracket. - if ((*_json_iter->peek(_json_iter->end_position()) == '}') && (!_json_iter->balanced())) { - _json_iter->abandon(); - // The exact error would require more work. It will typically be an unclosed object. - return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "the document is unbalanced"); - } - } - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::started_root_object() noexcept { - auto error = check_root_object(); - if(error) { return error; } - return started_object(); -} - -simdjson_warn_unused simdjson_inline error_code value_iterator::end_container() noexcept { -#if SIMDJSON_CHECK_EOF - if (depth() > 1 && at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing parent ] or }"); } - // if (depth() <= 1 && !at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing [ or { at start"); } -#endif // SIMDJSON_CHECK_EOF - _json_iter->ascend_to(depth()-1); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::has_next_field() noexcept { - assert_at_next(); - - // It's illegal to call this unless there are more tokens: anything that ends in } or ] is - // obligated to verify there are more tokens if they are not the top level. - switch (*_json_iter->return_current_and_advance()) { - case '}': - logger::log_end_value(*_json_iter, "object"); - SIMDJSON_TRY( end_container() ); - return false; - case ',': - return true; - default: - return report_error(TAPE_ERROR, "Missing comma between object fields"); - } -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::find_field_raw(const std::string_view key) noexcept { - error_code error; - bool has_value; - // - // Initially, the object can be in one of a few different places: - // - // 1. The start of the object, at the first field: - // - // ``` - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 2, index 1) - // ``` - if (at_first_field()) { - has_value = true; - - // - // 2. When a previous search did not yield a value or the object is empty: - // - // ``` - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 0) - // { } - // ^ (depth 0, index 2) - // ``` - // - } else if (!is_open()) { -#if SIMDJSON_DEVELOPMENT_CHECKS - // If we're past the end of the object, we're being iterated out of order. - // Note: this is not perfect detection. It's possible the user is inside some other object; if so, - // this object iterator will blithely scan that object for fields. - if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; } -#endif - return false; - - // 3. When a previous search found a field or an iterator yielded a value: - // - // ``` - // // When a field was not fully consumed (or not even touched at all) - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 2) - // // When a field was fully consumed - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 1) - // // When the last field was fully consumed - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 1) - // ``` - // - } else { - if ((error = skip_child() )) { abandon(); return error; } - if ((error = has_next_field().get(has_value) )) { abandon(); return error; } -#if SIMDJSON_DEVELOPMENT_CHECKS - if (_json_iter->start_position(_depth) != start_position()) { return OUT_OF_ORDER_ITERATION; } -#endif - } - while (has_value) { - // Get the key and colon, stopping at the value. - raw_json_string actual_key; - // size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes - // Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2. - // field_key() advances the pointer and checks that '"' is found (corresponding to a key). - // The depth is left unchanged by field_key(). - if ((error = field_key().get(actual_key) )) { abandon(); return error; }; - // field_value() will advance and check that we find a ':' separating the - // key and the value. It will also increment the depth by one. - if ((error = field_value() )) { abandon(); return error; } - // If it matches, stop and return - // We could do it this way if we wanted to allow arbitrary - // key content (including escaped quotes). - //if (actual_key.unsafe_is_equal(max_key_length, key)) { - // Instead we do the following which may trigger buffer overruns if the - // user provides an adversarial key (containing a well placed unescaped quote - // character and being longer than the number of bytes remaining in the JSON - // input). - if (actual_key.unsafe_is_equal(key)) { - logger::log_event(*this, "match", key, -2); - // If we return here, then we return while pointing at the ':' that we just checked. - return true; - } - - // No match: skip the value and see if , or } is next - logger::log_event(*this, "no match", key, -2); - // The call to skip_child is meant to skip over the value corresponding to the key. - // After skip_child(), we are right before the next comma (',') or the final brace ('}'). - SIMDJSON_TRY( skip_child() ); // Skip the value entirely - // The has_next_field() advances the pointer and check that either ',' or '}' is found. - // It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found, - // then we are in error and we abort. - if ((error = has_next_field().get(has_value) )) { abandon(); return error; } - } - - // If the loop ended, we're out of fields to look at. - return false; -} - -SIMDJSON_PUSH_DISABLE_WARNINGS -SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::find_field_unordered_raw(const std::string_view key) noexcept { - /** - * When find_field_unordered_raw is called, we can either be pointing at the - * first key, pointing outside (at the closing brace) or if a key was matched - * we can be either pointing right afterthe ':' right before the value (that we need skip), - * or we may have consumed the value and we might be at a comma or at the - * final brace (ready for a call to has_next_field()). - */ - error_code error; - bool has_value; - - // First, we scan from that point to the end. - // If we don't find a match, we may loop back around, and scan from the beginning to that point. - token_position search_start = _json_iter->position(); + // First, we scan from that point to the end. + // If we don't find a match, we may loop back around, and scan from the beginning to that point. + token_position search_start = _json_iter->position(); // We want to know whether we need to go back to the beginning. bool at_first = at_first_field(); @@ -84816,6 +87773,8 @@ simdjson_inline simdjson_result value_iterator::is_root_null(bool check_tr if(result) { // we have something that looks like a null. if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; } advance_root_scalar("null"); + } else if (json[0] == 'n') { + return incorrect_type_error("Not a null but starts with n"); } return result; } @@ -85107,6 +88066,8 @@ simdjson_inline simdjson_result::simdjson_resul #endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H /* end file simdjson/generic/ondemand/value_iterator-inl.h for ppc64 */ + + /* end file simdjson/generic/ondemand/amalgamated.h for ppc64 */ /* including simdjson/ppc64/end.h: #include "simdjson/ppc64/end.h" */ /* begin file simdjson/ppc64/end.h */ @@ -86313,6 +89274,132 @@ class value_iterator; #endif // SIMDJSON_GENERIC_ONDEMAND_BASE_H /* end file simdjson/generic/ondemand/base.h for westmere */ +/* including simdjson/generic/ondemand/deserialize.h for westmere: #include "simdjson/generic/ondemand/deserialize.h" */ +/* begin file simdjson/generic/ondemand/deserialize.h for westmere */ +#if SIMDJSON_SUPPORTS_DESERIALIZATION + +#ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_ONDEMAND_DESERIALIZE_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +namespace simdjson { + +namespace tag_invoke_fn_ns { +void tag_invoke(); + +struct tag_invoke_fn { + template + requires requires(Tag tag, Args &&...args) { + tag_invoke(std::forward(tag), std::forward(args)...); + } + constexpr auto operator()(Tag tag, Args &&...args) const + noexcept(noexcept(tag_invoke(std::forward(tag), + std::forward(args)...))) + -> decltype(tag_invoke(std::forward(tag), + std::forward(args)...)) { + return tag_invoke(std::forward(tag), std::forward(args)...); + } +}; +} // namespace tag_invoke_fn_ns + +inline namespace tag_invoke_ns { +inline constexpr tag_invoke_fn_ns::tag_invoke_fn tag_invoke = {}; +} // namespace tag_invoke_ns + +template +concept tag_invocable = requires(Tag tag, Args... args) { + tag_invoke(std::forward(tag), std::forward(args)...); +}; + +template +concept nothrow_tag_invocable = + tag_invocable && requires(Tag tag, Args... args) { + { + tag_invoke(std::forward(tag), std::forward(args)...) + } noexcept; + }; + +template +using tag_invoke_result = + std::invoke_result; + +template +using tag_invoke_result_t = + std::invoke_result_t; + +template using tag_t = std::decay_t; + + +struct deserialize_tag; + +/// These types are deserializable in a built-in way +template struct is_builtin_deserializable : std::false_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; + +template +concept is_builtin_deserializable_v = is_builtin_deserializable::value; + +template +concept custom_deserializable = tag_invocable; + +template +concept deserializable = custom_deserializable || is_builtin_deserializable_v; + +template +concept nothrow_custom_deserializable = nothrow_tag_invocable; + +// built-in types are noexcept and if an error happens, the value simply gets ignored and the error is returned. +template +concept nothrow_deserializable = nothrow_custom_deserializable || is_builtin_deserializable_v; + +/// Deserialize Tag +inline constexpr struct deserialize_tag { + using value_type = westmere::ondemand::value; + using document_type = westmere::ondemand::document; + using document_reference_type = westmere::ondemand::document_reference; + + // Customization Point for value + template + requires custom_deserializable + [[nodiscard]] constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for document + template + requires custom_deserializable + [[nodiscard]] constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for document reference + template + requires custom_deserializable + [[nodiscard]] constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + +} deserialize{}; + +} // namespace simdjson + +#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION + +/* end file simdjson/generic/ondemand/deserialize.h for westmere */ /* including simdjson/generic/ondemand/value_iterator.h for westmere: #include "simdjson/generic/ondemand/value_iterator.h" */ /* begin file simdjson/generic/ondemand/value_iterator.h for westmere */ #ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H @@ -86817,12 +89904,15 @@ struct simdjson_result : public westmere::im /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ +#include + namespace simdjson { + namespace westmere { namespace ondemand { - /** * An ephemeral JSON value returned during iteration. It is only valid for as long as you do * not access more data in the JSON document. @@ -86847,16 +89937,21 @@ class value { * @returns A value of the given type, parsed from the JSON. * @returns INCORRECT_TYPE If the JSON value is not the given type. */ - template simdjson_inline simdjson_result get() noexcept { - // Unless the simdjson library or the user provides an inline implementation, calling this method should - // immediately fail. - static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " - "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " - "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." - " You may also add support for custom types, see our documentation."); + template + simdjson_inline simdjson_result get() +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; } + /** * Get this value as the given type. * @@ -86866,7 +89961,32 @@ class value { * @returns INCORRECT_TYPE If the JSON value is not an object. * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ - template simdjson_inline error_code get(T &out) noexcept; + template + simdjson_inline error_code get(T &out) +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_DESERIALIZATION + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION + // Unless the simdjson library or the user provides an inline implementation, calling this method should + // immediately fail. + static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " + "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " + "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." + " You may also add support for custom types, see our documentation."); + static_cast(out); // to get rid of unused errors + return UNINITIALIZED; +#if SIMDJSON_SUPPORTS_DESERIALIZATION + } +#endif + } /** * Cast this JSON value to an array. @@ -86942,6 +90062,17 @@ class value { * Important: a value should be consumed once. Calling get_string() twice on the same value * is an error. * + * In some instances, you may want to allow replacement of invalid Unicode sequences. + * You may do so by passing the allow_replacement parameter as true. In the following + * example, the string "431924697b\udff0L\u0001Y" is not valid Unicode. By passing true + * to get_string, we allow the replacement of the invalid Unicode sequences with the Unicode + * replacement character (U+FFFD). + * + * simdjson::ondemand::parser parser; + * auto json = R"({"deviceId":"431924697b\udff0L\u0001Y"})"_padded; + * simdjson::ondemand::document doc = parser.iterate(json); + * auto view = doc["deviceId"].get_string(true); + * * @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next * time it parses a document or when it is destroyed. * @returns INCORRECT_TYPE if the JSON value is not a string. @@ -87193,6 +90324,7 @@ class value { simdjson_inline simdjson_result operator[](std::string_view key) noexcept; /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ simdjson_inline simdjson_result operator[](const char *key) noexcept; + simdjson_result operator[](int) noexcept = delete; /** * Get the type of this JSON value. It does not validate or consume the value. @@ -87560,6 +90692,7 @@ struct simdjson_result : public westmere::implementat simdjson_inline simdjson_result operator[](std::string_view key) noexcept; /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ simdjson_inline simdjson_result operator[](const char *key) noexcept; + simdjson_result operator[](int) noexcept = delete; /** * Get the type of this JSON value. @@ -88615,6 +91748,22 @@ class parser { * using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the * SIMDJSON_PADDING bytes to avoid runtime warnings. * + * ### std::string references + * + * If you pass a mutable std::string reference (std::string&), the parser will seek to extend + * its capacity to SIMDJSON_PADDING bytes beyond the end of the string. + * + * Whenever you pass an std::string reference, the parser will access the bytes beyond the end of + * the string but before the end of the allocated memory (std::string::capacity()). + * If you are using a sanitizer that checks for reading uninitialized bytes or std::string's + * container-overflow checks, you may encounter sanitizer warnings. + * You can safely ignore these warnings. Or you can call simdjson::pad(std::string&) to pad the + * string with SIMDJSON_PADDING spaces: this function returns a simdjson::padding_string_view + * which can be be passed to the parser's iterate function: + * + * std::string json = R"({ "foo": 1 } { "foo": 2 } { "foo": 3 } )"; + * document doc = parser.iterate(simdjson::pad(json)); + * * @param json The JSON to parse. * @param len The length of the JSON. * @param capacity The number of bytes allocated in the JSON (must be at least len+SIMDJSON_PADDING). @@ -88809,8 +91958,12 @@ class parser { * behavior of the parser for future operations. */ bool threaded{true}; + #else + /** + * When SIMDJSON_THREADS_ENABLED is not defined, the parser instance cannot use threads. + */ + bool threaded{false}; #endif - /** * Unescape this JSON string, replacing \\ with \, \n with newline, etc. to a user-provided buffer. * The result must be valid UTF-8. @@ -88952,7 +92105,8 @@ class array { * calling this function, if successful, the array is 'rewinded' at its * beginning as if it had never been accessed. If the JSON is malformed (e.g., * there is a missing comma), then an error is returned and it is no longer - * safe to continue. + * safe to continue. Note that count_elements() does not validate the JSON values, + * only the structure of the array. * * To check that an array is empty, it is more performant to use * the is_empty() method. @@ -89230,8 +92384,11 @@ struct simdjson_result : public westmere::im /* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + namespace simdjson { namespace westmere { namespace ondemand { @@ -89404,24 +92561,39 @@ class document { * @returns A value of the given type, parsed from the JSON. * @returns INCORRECT_TYPE If the JSON value is not the given type. */ - template simdjson_inline simdjson_result get() & noexcept { - // Unless the simdjson library or the user provides an inline implementation, calling this method should - // immediately fail. - static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " - "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " - "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." - " You may also add support for custom types, see our documentation."); - } - /** @overload template simdjson_result get() & noexcept */ - template simdjson_deprecated simdjson_inline simdjson_result get() && noexcept { - // Unless the simdjson library or the user provides an inline implementation, calling this method should - // immediately fail. - static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " - "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " - "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." - " You may also add support for custom types, see our documentation."); + template + simdjson_inline simdjson_result get() & +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "Cannot initialize the specified type."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + /** + * @overload template simdjson_result get() & noexcept + * + * We disallow the use tag_invoke CPO on a moved document; it may create UB + * if user uses `ondemand::array` or `ondemand::object` in their custom type. + * + * The member function is still remains specialize-able for compatibility + * reasons, but we completely disallow its use when a tag_invoke customization + * is provided. + */ + template + simdjson_inline simdjson_result get() && +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(!std::is_same::value && !std::is_same::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document being alive; that would be Undefined Behaviour."); + return static_cast(*this).get(); } /** @@ -89435,7 +92607,32 @@ class document { * @returns INCORRECT_TYPE If the JSON value is not an object. * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ - template simdjson_inline error_code get(T &out) & noexcept; + template + simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_DESERIALIZATION + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION + // Unless the simdjson library or the user provides an inline implementation, calling this method should + // immediately fail. + static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " + "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " + "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." + " You may also add support for custom types, see our documentation."); + static_cast(out); // to get rid of unused errors + return UNINITIALIZED; +#if SIMDJSON_SUPPORTS_DESERIALIZATION + } +#endif + } /** @overload template error_code get(T &out) & noexcept */ template simdjson_deprecated simdjson_inline error_code get(T &out) && noexcept; @@ -89537,7 +92734,8 @@ class document { * calling this function, if successful, the array is 'rewinded' at its * beginning as if it had never been accessed. If the JSON is malformed (e.g., * there is a missing comma), then an error is returned and it is no longer - * safe to continue. + * safe to continue. Note that count_elements() does not validate the JSON values, + * only the structure of the array. */ simdjson_inline simdjson_result count_elements() & noexcept; /** @@ -89649,6 +92847,7 @@ class document { simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; /** * Get the type of this JSON value. It does not validate or consume the value. @@ -89917,6 +93116,11 @@ class document { /** * A document_reference is a thin wrapper around a document reference instance. + * The document_reference instances are used primarily/solely for streams of JSON + * documents. They differ from document instances when parsing a scalar value + * (a document that is not an array or an object). In the case of a document, + * we expect the document to be fully consumed. In the case of a document_reference, + * we allow trailing content. */ class document_reference { public: @@ -89942,7 +93146,70 @@ class document_reference { simdjson_inline simdjson_result get_value() noexcept; simdjson_inline simdjson_result is_null() noexcept; - template simdjson_inline simdjson_result get() & noexcept; + template + simdjson_inline simdjson_result get() & +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "Cannot initialize the specified type."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + template + simdjson_inline simdjson_result get() && +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(!std::is_same::value && !std::is_same::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document_reference being alive; that would be Undefined Behaviour."); + return static_cast(*this).get(); + } + + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool, value + * + * Be mindful that the document instance must remain in scope while you are accessing object, array and value instances. + * + * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. + * @returns INCORRECT_TYPE If the JSON value is not an object. + * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. + */ + template + simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_DESERIALIZATION + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION + // Unless the simdjson library or the user provides an inline implementation, calling this method should + // immediately fail. + static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " + "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " + "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." + " You may also add support for custom types, see our documentation."); + static_cast(out); // to get rid of unused errors + return UNINITIALIZED; +#if SIMDJSON_SUPPORTS_DESERIALIZATION + } +#endif + } + /** @overload template error_code get(T &out) & noexcept */ + template simdjson_inline error_code get(T &out) && noexcept; simdjson_inline simdjson_result raw_json() noexcept; simdjson_inline operator document&() const noexcept; #if SIMDJSON_EXCEPTIONS @@ -89967,6 +93234,7 @@ class document_reference { simdjson_inline simdjson_result find_field(const char *key) & noexcept; simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; @@ -90045,6 +93313,7 @@ struct simdjson_result : public westmere::implemen simdjson_inline simdjson_result find_field(const char *key) & noexcept; simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; simdjson_inline simdjson_result type() noexcept; @@ -90094,8 +93363,14 @@ struct simdjson_result : public westmere simdjson_inline simdjson_result get_bool() noexcept; simdjson_inline simdjson_result get_value() noexcept; simdjson_inline simdjson_result is_null() noexcept; + + template simdjson_inline simdjson_result get() & noexcept; + template simdjson_inline simdjson_result get() && noexcept; + + template simdjson_inline error_code get(T &out) & noexcept; + template simdjson_inline error_code get(T &out) && noexcept; #if SIMDJSON_EXCEPTIONS - template ::value == false>::type> + template explicit simdjson_inline operator T() noexcept(false); simdjson_inline operator westmere::ondemand::array() & noexcept(false); simdjson_inline operator westmere::ondemand::object() & noexcept(false); @@ -90116,6 +93391,7 @@ struct simdjson_result : public westmere simdjson_inline simdjson_result find_field(const char *key) & noexcept; simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; simdjson_inline simdjson_result type() noexcept; @@ -91046,6 +94322,177 @@ inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result +#include + +namespace simdjson { +template +constexpr bool require_custom_serialization = false; + +////////////////////////////// +// Number deserialization +////////////////////////////// + +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + using limits = std::numeric_limits; + + uint64_t x; + SIMDJSON_TRY(val.get_uint64().get(x)); + if (x > (limits::max)()) { + return NUMBER_OUT_OF_RANGE; + } + out = static_cast(x); + return SUCCESS; +} + +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + double x; + SIMDJSON_TRY(val.get_double().get(x)); + out = static_cast(x); + return SUCCESS; +} + +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + using limits = std::numeric_limits; + + int64_t x; + SIMDJSON_TRY(val.get_int64().get(x)); + if (x > (limits::max)() || x < (limits::min)()) { + return NUMBER_OUT_OF_RANGE; + } + out = static_cast(x); + return SUCCESS; +} + +/** + * STL containers have several constructors including one that takes a single + * size argument. Thus, some compilers (Visual Studio) will not be able to + * disambiguate between the size and container constructor. Users should + * explicitly specify the type of the container as needed: e.g., + * doc.get>(). + */ +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { + using value_type = typename std::remove_cvref_t::value_type; + static_assert( + deserializable, + "The specified type inside the container must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the container must default constructible."); + + westmere::ondemand::array arr; + SIMDJSON_TRY(val.get_array().get(arr)); + for (auto v : arr) { + if constexpr (concepts::returns_reference) { + if (auto const err = v.get().get(concepts::emplace_one(out)); + err) { + // If an error occurs, the empty element that we just inserted gets + // removed. We're not using a temp variable because if T is a heavy + // type, we want the valid path to be the fast path and the slow path be + // the path that has errors in it. + if constexpr (requires { out.pop_back(); }) { + static_cast(out.pop_back()); + } + return err; + } + } else { + value_type temp; + if (auto const err = v.get().get(temp); err) { + return err; + } + concepts::emplace_one(out, std::move(temp)); + } + } + return SUCCESS; +} + + + +/** + * This CPO (Customization Point Object) will help deserialize into + * smart pointers. + * + * If constructing T is nothrow, this conversion should be nothrow as well since + * we return MEMALLOC if we're not able to allocate memory instead of throwing + * the error message. + * + * @tparam T The type inside the smart pointer + * @tparam ValT document/value type + * @param val document/value + * @param out a reference to the smart pointer + * @return status of the conversion + */ +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::element_type, ValT>) { + using element_type = typename std::remove_cvref_t::element_type; + + // For better error messages, don't use these as constraints on + // the tag_invoke CPO. + static_assert( + deserializable, + "The specified type inside the unique_ptr must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the unique_ptr must default constructible."); + + auto ptr = new (std::nothrow) element_type(); + if (ptr == nullptr) { + return MEMALLOC; + } + SIMDJSON_TRY(val.template get(*ptr)); + out.reset(ptr); + return SUCCESS; +} + +/** + * This CPO (Customization Point Object) will help deserialize into optional types. + */ +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::value_type, ValT>) { + using value_type = typename std::remove_cvref_t::value_type; + + static_assert( + deserializable, + "The specified type inside the unique_ptr must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the unique_ptr must default constructible."); + + if (!out) { + out.emplace(); + } + SIMDJSON_TRY(val.template get(out.value())); + return SUCCESS; +} + +} // namespace simdjson + +#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +/* end file simdjson/generic/ondemand/std_deserialize.h for westmere */ + // Inline definitions /* including simdjson/generic/ondemand/array-inl.h for westmere: #include "simdjson/generic/ondemand/array-inl.h" */ /* begin file simdjson/generic/ondemand/array-inl.h for westmere */ @@ -91053,6 +94500,7 @@ inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result array::at_pointer(std::string_view json_pointer) n return child; } -inline std::string json_path_to_pointer_conversion(std::string_view json_path) { - if (json_path.empty() || (json_path.front() != '.' && - json_path.front() != '[')) { - return "-1"; // This is just a sentinel value, the caller should check for this and return an error. - } - - std::string result; - // Reserve space to reduce allocations, adjusting for potential increases due - // to escaping. - result.reserve(json_path.size() * 2); - - size_t i = 0; - - while (i < json_path.length()) { - if (json_path[i] == '.') { - result += '/'; - } else if (json_path[i] == '[') { - result += '/'; - ++i; // Move past the '[' - while (i < json_path.length() && json_path[i] != ']') { - if (json_path[i] == '~') { - result += "~0"; - } else if (json_path[i] == '/') { - result += "~1"; - } else { - result += json_path[i]; - } - ++i; - } - if (i == json_path.length() || json_path[i] != ']') { - return "-1"; // Using sentinel value that will be handled as an error by the caller. - } - } else { - if (json_path[i] == '~') { - result += "~0"; - } else if (json_path[i] == '/') { - result += "~1"; - } else { - result += json_path[i]; - } - } - ++i; - } - - return result; -} - inline simdjson_result array::at_path(std::string_view json_path) noexcept { auto json_pointer = json_path_to_pointer_conversion(json_path); if (json_pointer == "-1") { return INVALID_JSON_POINTER; } @@ -91414,313 +94815,276 @@ simdjson_inline simdjson_result &simdjson_re #endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H /* end file simdjson/generic/ondemand/array_iterator-inl.h for westmere */ -/* including simdjson/generic/ondemand/document-inl.h for westmere: #include "simdjson/generic/ondemand/document-inl.h" */ -/* begin file simdjson/generic/ondemand/document-inl.h for westmere */ -#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H +/* including simdjson/generic/ondemand/value-inl.h for westmere: #include "simdjson/generic/ondemand/value-inl.h" */ +/* begin file simdjson/generic/ondemand/value-inl.h for westmere */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ -/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { namespace westmere { namespace ondemand { -simdjson_inline document::document(ondemand::json_iterator &&_iter) noexcept - : iter{std::forward(_iter)} +simdjson_inline value::value(const value_iterator &_iter) noexcept + : iter{_iter} { - logger::log_start_value(iter, "document"); } - -simdjson_inline document document::start(json_iterator &&iter) noexcept { - return document(std::forward(iter)); +simdjson_inline value value::start(const value_iterator &iter) noexcept { + return iter; } - -inline void document::rewind() noexcept { - iter.rewind(); +simdjson_inline value value::resume(const value_iterator &iter) noexcept { + return iter; } -inline std::string document::to_debug_string() noexcept { - return iter.to_string(); +simdjson_inline simdjson_result value::get_array() noexcept { + return array::start(iter); } - -inline simdjson_result document::current_location() const noexcept { - return iter.current_location(); +simdjson_inline simdjson_result value::get_object() noexcept { + return object::start(iter); } - -inline int32_t document::current_depth() const noexcept { - return iter.depth(); +simdjson_inline simdjson_result value::start_or_resume_object() noexcept { + if (iter.at_start()) { + return get_object(); + } else { + return object::resume(iter); + } } -inline bool document::at_end() const noexcept { - return iter.at_end(); +simdjson_inline simdjson_result value::get_raw_json_string() noexcept { + return iter.get_raw_json_string(); } - - -inline bool document::is_alive() noexcept { - return iter.is_alive(); +simdjson_inline simdjson_result value::get_string(bool allow_replacement) noexcept { + return iter.get_string(allow_replacement); } -simdjson_inline value_iterator document::resume_value_iterator() noexcept { - return value_iterator(&iter, 1, iter.root_position()); +template +simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept { + return iter.get_string(receiver, allow_replacement); } -simdjson_inline value_iterator document::get_root_value_iterator() noexcept { - return resume_value_iterator(); +simdjson_inline simdjson_result value::get_wobbly_string() noexcept { + return iter.get_wobbly_string(); } -simdjson_inline simdjson_result document::start_or_resume_object() noexcept { - if (iter.at_root()) { - return get_object(); - } else { - return object::resume(resume_value_iterator()); - } +simdjson_inline simdjson_result value::get_double() noexcept { + return iter.get_double(); } -simdjson_inline simdjson_result document::get_value() noexcept { - // Make sure we start any arrays or objects before returning, so that start_root_() - // gets called. - - // It is the convention throughout the code that the macro `SIMDJSON_DEVELOPMENT_CHECKS` determines whether - // we check for OUT_OF_ORDER_ITERATION. Proper on::demand code should never trigger this error. -#if SIMDJSON_DEVELOPMENT_CHECKS - if (!iter.at_root()) { return OUT_OF_ORDER_ITERATION; } -#endif - // assert_at_root() serves two purposes: in Debug mode, whether or not - // SIMDJSON_DEVELOPMENT_CHECKS is set or not, it checks that we are at the root of - // the document (this will typically be redundant). In release mode, it generates - // SIMDJSON_ASSUME statements to allow the compiler to make assumptions. - iter.assert_at_root(); - switch (*iter.peek()) { - case '[': { - // The following lines check that the document ends with ]. - auto value_iterator = get_root_value_iterator(); - auto error = value_iterator.check_root_array(); - if(error) { return error; } - return value(get_root_value_iterator()); - } - case '{': { - // The following lines would check that the document ends with }. - auto value_iterator = get_root_value_iterator(); - auto error = value_iterator.check_root_object(); - if(error) { return error; } - return value(get_root_value_iterator()); - } - default: - // Unfortunately, scalar documents are a special case in simdjson and they cannot - // be safely converted to value instances. - return SCALAR_DOCUMENT_AS_VALUE; - } +simdjson_inline simdjson_result value::get_double_in_string() noexcept { + return iter.get_double_in_string(); } -simdjson_inline simdjson_result document::get_array() & noexcept { - auto value = get_root_value_iterator(); - return array::start_root(value); +simdjson_inline simdjson_result value::get_uint64() noexcept { + return iter.get_uint64(); } -simdjson_inline simdjson_result document::get_object() & noexcept { - auto value = get_root_value_iterator(); - return object::start_root(value); +simdjson_inline simdjson_result value::get_uint64_in_string() noexcept { + return iter.get_uint64_in_string(); } - -/** - * We decided that calling 'get_double()' on the JSON document '1.233 blabla' should - * give an error, so we check for trailing content. We want to disallow trailing - * content. - * Thus, in several implementations below, we pass a 'true' parameter value to - * a get_root_value_iterator() method: this indicates that we disallow trailing content. - */ - -simdjson_inline simdjson_result document::get_uint64() noexcept { - return get_root_value_iterator().get_root_uint64(true); +simdjson_inline simdjson_result value::get_int64() noexcept { + return iter.get_int64(); } -simdjson_inline simdjson_result document::get_uint64_in_string() noexcept { - return get_root_value_iterator().get_root_uint64_in_string(true); +simdjson_inline simdjson_result value::get_int64_in_string() noexcept { + return iter.get_int64_in_string(); } -simdjson_inline simdjson_result document::get_int64() noexcept { - return get_root_value_iterator().get_root_int64(true); +simdjson_inline simdjson_result value::get_bool() noexcept { + return iter.get_bool(); } -simdjson_inline simdjson_result document::get_int64_in_string() noexcept { - return get_root_value_iterator().get_root_int64_in_string(true); +simdjson_inline simdjson_result value::is_null() noexcept { + return iter.is_null(); } -simdjson_inline simdjson_result document::get_double() noexcept { - return get_root_value_iterator().get_root_double(true); + +template<> simdjson_inline simdjson_result value::get() noexcept { return get_array(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_object(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_number(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_double(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_bool(); } + + +template<> simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); } +template<> simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); } +template<> simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); } +template<> simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); } +template<> simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); } +template<> simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); } +template<> simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); } +template<> simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); } +template<> simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); } + +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline value::operator T() noexcept(false) { + return get(); } -simdjson_inline simdjson_result document::get_double_in_string() noexcept { - return get_root_value_iterator().get_root_double_in_string(true); +simdjson_inline value::operator array() noexcept(false) { + return get_array(); } -simdjson_inline simdjson_result document::get_string(bool allow_replacement) noexcept { - return get_root_value_iterator().get_root_string(true, allow_replacement); +simdjson_inline value::operator object() noexcept(false) { + return get_object(); } -template -simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept { - return get_root_value_iterator().get_root_string(receiver, true, allow_replacement); +simdjson_inline value::operator uint64_t() noexcept(false) { + return get_uint64(); } -simdjson_inline simdjson_result document::get_wobbly_string() noexcept { - return get_root_value_iterator().get_root_wobbly_string(true); +simdjson_inline value::operator int64_t() noexcept(false) { + return get_int64(); } -simdjson_inline simdjson_result document::get_raw_json_string() noexcept { - return get_root_value_iterator().get_root_raw_json_string(true); +simdjson_inline value::operator double() noexcept(false) { + return get_double(); } -simdjson_inline simdjson_result document::get_bool() noexcept { - return get_root_value_iterator().get_root_bool(true); +simdjson_inline value::operator std::string_view() noexcept(false) { + return get_string(false); } -simdjson_inline simdjson_result document::is_null() noexcept { - return get_root_value_iterator().is_root_null(true); +simdjson_inline value::operator raw_json_string() noexcept(false) { + return get_raw_json_string(); } +simdjson_inline value::operator bool() noexcept(false) { + return get_bool(); +} +#endif -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_array(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_object(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_raw_json_string(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_string(false); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_double(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_uint64(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_int64(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_value(); } - -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_raw_json_string(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_value(); } - -template simdjson_inline error_code document::get(T &out) & noexcept { - return get().get(out); +simdjson_inline simdjson_result value::begin() & noexcept { + return get_array().begin(); } -template simdjson_deprecated simdjson_inline error_code document::get(T &out) && noexcept { - return std::forward(*this).get().get(out); +simdjson_inline simdjson_result value::end() & noexcept { + return {}; } - -#if SIMDJSON_EXCEPTIONS -template -simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get(); } -template -simdjson_inline document::operator T() & noexcept(false) { return get(); } -simdjson_inline document::operator array() & noexcept(false) { return get_array(); } -simdjson_inline document::operator object() & noexcept(false) { return get_object(); } -simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); } -simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); } -simdjson_inline document::operator double() noexcept(false) { return get_double(); } -simdjson_inline document::operator std::string_view() noexcept(false) { return get_string(false); } -simdjson_inline document::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } -simdjson_inline document::operator bool() noexcept(false) { return get_bool(); } -simdjson_inline document::operator value() noexcept(false) { return get_value(); } - -#endif -simdjson_inline simdjson_result document::count_elements() & noexcept { +simdjson_inline simdjson_result value::count_elements() & noexcept { + simdjson_result answer; auto a = get_array(); - simdjson_result answer = a.count_elements(); - /* If there was an array, we are now left pointing at its first element. */ - if(answer.error() == SUCCESS) { rewind(); } + answer = a.count_elements(); + // count_elements leaves you pointing inside the array, at the first element. + // We need to move back so that the user can create a new array (which requires that + // we point at '['). + iter.move_at_start(); return answer; } -simdjson_inline simdjson_result document::count_fields() & noexcept { +simdjson_inline simdjson_result value::count_fields() & noexcept { + simdjson_result answer; auto a = get_object(); - simdjson_result answer = a.count_fields(); - /* If there was an object, we are now left pointing at its first element. */ - if(answer.error() == SUCCESS) { rewind(); } + answer = a.count_fields(); + iter.move_at_start(); return answer; } -simdjson_inline simdjson_result document::at(size_t index) & noexcept { +simdjson_inline simdjson_result value::at(size_t index) noexcept { auto a = get_array(); return a.at(index); } -simdjson_inline simdjson_result document::begin() & noexcept { - return get_array().begin(); -} -simdjson_inline simdjson_result document::end() & noexcept { - return {}; -} -simdjson_inline simdjson_result document::find_field(std::string_view key) & noexcept { +simdjson_inline simdjson_result value::find_field(std::string_view key) noexcept { return start_or_resume_object().find_field(key); } -simdjson_inline simdjson_result document::find_field(const char *key) & noexcept { +simdjson_inline simdjson_result value::find_field(const char *key) noexcept { return start_or_resume_object().find_field(key); } -simdjson_inline simdjson_result document::find_field_unordered(std::string_view key) & noexcept { - return start_or_resume_object().find_field_unordered(key); + +simdjson_inline simdjson_result value::find_field_unordered(std::string_view key) noexcept { + return start_or_resume_object().find_field_unordered(key); } -simdjson_inline simdjson_result document::find_field_unordered(const char *key) & noexcept { +simdjson_inline simdjson_result value::find_field_unordered(const char *key) noexcept { return start_or_resume_object().find_field_unordered(key); } -simdjson_inline simdjson_result document::operator[](std::string_view key) & noexcept { + +simdjson_inline simdjson_result value::operator[](std::string_view key) noexcept { return start_or_resume_object()[key]; } -simdjson_inline simdjson_result document::operator[](const char *key) & noexcept { +simdjson_inline simdjson_result value::operator[](const char *key) noexcept { return start_or_resume_object()[key]; } -simdjson_inline error_code document::consume() noexcept { - auto error = iter.skip_child(0); - if(error) { iter.abandon(); } - return error; -} - -simdjson_inline simdjson_result document::raw_json() noexcept { - auto _iter = get_root_value_iterator(); - const uint8_t * starting_point{_iter.peek_start()}; - auto error = consume(); - if(error) { return error; } - // After 'consume()', we could be left pointing just beyond the document, but that - // is ok because we are not going to dereference the final pointer position, we just - // use it to compute the length in bytes. - const uint8_t * final_point{iter.unsafe_pointer()}; - return std::string_view(reinterpret_cast(starting_point), size_t(final_point - starting_point)); -} - -simdjson_inline simdjson_result document::type() noexcept { - return get_root_value_iterator().type(); +simdjson_inline simdjson_result value::type() noexcept { + return iter.type(); } -simdjson_inline simdjson_result document::is_scalar() noexcept { +simdjson_inline simdjson_result value::is_scalar() noexcept { json_type this_type; auto error = type().get(this_type); if(error) { return error; } return ! ((this_type == json_type::array) || (this_type == json_type::object)); } -simdjson_inline simdjson_result document::is_string() noexcept { +simdjson_inline simdjson_result value::is_string() noexcept { json_type this_type; auto error = type().get(this_type); if(error) { return error; } return (this_type == json_type::string); } -simdjson_inline bool document::is_negative() noexcept { - return get_root_value_iterator().is_root_negative(); + +simdjson_inline bool value::is_negative() noexcept { + return iter.is_negative(); } -simdjson_inline simdjson_result document::is_integer() noexcept { - return get_root_value_iterator().is_root_integer(true); +simdjson_inline simdjson_result value::is_integer() noexcept { + return iter.is_integer(); +} +simdjson_warn_unused simdjson_inline simdjson_result value::get_number_type() noexcept { + return iter.get_number_type(); +} +simdjson_warn_unused simdjson_inline simdjson_result value::get_number() noexcept { + return iter.get_number(); } -simdjson_inline simdjson_result document::get_number_type() noexcept { - return get_root_value_iterator().get_root_number_type(true); +simdjson_inline std::string_view value::raw_json_token() noexcept { + return std::string_view(reinterpret_cast(iter.peek_start()), iter.peek_start_length()); } -simdjson_inline simdjson_result document::get_number() noexcept { - return get_root_value_iterator().get_root_number(true); +simdjson_inline simdjson_result value::raw_json() noexcept { + json_type t; + SIMDJSON_TRY(type().get(t)); + switch (t) + { + case json_type::array: { + ondemand::array array; + SIMDJSON_TRY(get_array().get(array)); + return array.raw_json(); + } + case json_type::object: { + ondemand::object object; + SIMDJSON_TRY(get_object().get(object)); + return object.raw_json(); + } + default: + return raw_json_token(); + } } +simdjson_inline simdjson_result value::current_location() noexcept { + return iter.json_iter().current_location(); +} -simdjson_inline simdjson_result document::raw_json_token() noexcept { - auto _iter = get_root_value_iterator(); - return std::string_view(reinterpret_cast(_iter.peek_start()), _iter.peek_root_length()); +simdjson_inline int32_t value::current_depth() const noexcept{ + return iter.json_iter().depth(); } -simdjson_inline simdjson_result document::at_pointer(std::string_view json_pointer) noexcept { - rewind(); // Rewind the document each time at_pointer is called - if (json_pointer.empty()) { - return this->get_value(); +inline bool is_pointer_well_formed(std::string_view json_pointer) noexcept { + if (simdjson_unlikely(json_pointer.empty())) { // can't be + return false; + } + if (simdjson_unlikely(json_pointer[0] != '/')) { + return false; + } + size_t escape = json_pointer.find('~'); + if (escape == std::string_view::npos) { + return true; } + if (escape == json_pointer.size() - 1) { + return false; + } + if (json_pointer[escape + 1] != '0' && json_pointer[escape + 1] != '1') { + return false; + } + return true; +} + +simdjson_inline simdjson_result value::at_pointer(std::string_view json_pointer) noexcept { json_type t; SIMDJSON_TRY(type().get(t)); switch (t) @@ -91730,15 +95094,15 @@ simdjson_inline simdjson_result document::at_pointer(std::string_view jso case json_type::object: return (*this).get_object().at_pointer(json_pointer); default: + // a non-empty string can be invalid, or accessing a primitive (issue 2154) + if (is_pointer_well_formed(json_pointer)) { + return NO_SUCH_FIELD; + } return INVALID_JSON_POINTER; } } -simdjson_inline simdjson_result document::at_path(std::string_view json_path) noexcept { - rewind(); // Rewind the document each time at_pointer is called - if (json_path.empty()) { - return this->get_value(); - } +simdjson_inline simdjson_result value::at_path(std::string_view json_path) noexcept { json_type t; SIMDJSON_TRY(type().get(t)); switch (t) { @@ -91757,681 +95121,1320 @@ simdjson_inline simdjson_result document::at_path(std::string_view json_p namespace simdjson { -simdjson_inline simdjson_result::simdjson_result( - westmere::ondemand::document &&value +simdjson_inline simdjson_result::simdjson_result( + westmere::ondemand::value &&value ) noexcept : - implementation_simdjson_result_base( - std::forward(value) + implementation_simdjson_result_base( + std::forward(value) ) { } -simdjson_inline simdjson_result::simdjson_result( +simdjson_inline simdjson_result::simdjson_result( error_code error ) noexcept : - implementation_simdjson_result_base( - error - ) + implementation_simdjson_result_base(error) { } -simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { if (error()) { return error(); } return first.count_elements(); } -simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { if (error()) { return error(); } return first.count_fields(); } -simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { +simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { if (error()) { return error(); } return first.at(index); } -simdjson_inline error_code simdjson_result::rewind() noexcept { - if (error()) { return error(); } - first.rewind(); - return SUCCESS; -} -simdjson_inline simdjson_result simdjson_result::begin() & noexcept { +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { if (error()) { return error(); } return first.begin(); } -simdjson_inline simdjson_result simdjson_result::end() & noexcept { +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + if (error()) { return error(); } return {}; } -simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) noexcept { if (error()) { return error(); } - return first.find_field_unordered(key); + return first.find_field(key); } -simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) noexcept { if (error()) { return error(); } - return first.find_field_unordered(key); + return first.find_field(key); } -simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) noexcept { if (error()) { return error(); } - return first[key]; + return first.find_field_unordered(key); } -simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) noexcept { if (error()) { return error(); } - return first[key]; + return first.find_field_unordered(key); } -simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) noexcept { if (error()) { return error(); } - return first.find_field(key); + return first[key]; } -simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) noexcept { if (error()) { return error(); } - return first.find_field(key); + return first[key]; } -simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { + +simdjson_inline simdjson_result simdjson_result::get_array() noexcept { if (error()) { return error(); } return first.get_array(); } -simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { +simdjson_inline simdjson_result simdjson_result::get_object() noexcept { if (error()) { return error(); } return first.get_object(); } -simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { if (error()) { return error(); } return first.get_uint64(); } -simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { if (error()) { return error(); } return first.get_uint64_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { if (error()) { return error(); } return first.get_int64(); } -simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { if (error()) { return error(); } return first.get_int64_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_double() noexcept { +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { if (error()) { return error(); } return first.get_double(); } -simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { if (error()) { return error(); } return first.get_double_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(allow_replacement); } template -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(receiver, allow_replacement); } -simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { if (error()) { return error(); } return first.get_wobbly_string(); } -simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { if (error()) { return error(); } return first.get_raw_json_string(); } -simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { if (error()) { return error(); } return first.get_bool(); } -simdjson_inline simdjson_result simdjson_result::get_value() noexcept { - if (error()) { return error(); } - return first.get_value(); -} -simdjson_inline simdjson_result simdjson_result::is_null() noexcept { +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { if (error()) { return error(); } return first.is_null(); } -template -simdjson_inline simdjson_result simdjson_result::get() & noexcept { +template<> simdjson_inline error_code simdjson_result::get(westmere::ondemand::value &out) noexcept { if (error()) { return error(); } - return first.get(); + out = first; + return SUCCESS; } -template -simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + +template simdjson_inline simdjson_result simdjson_result::get() noexcept { if (error()) { return error(); } - return std::forward(first).get(); + return first.get(); } -template -simdjson_inline error_code simdjson_result::get(T &out) & noexcept { +template simdjson_inline error_code simdjson_result::get(T &out) noexcept { if (error()) { return error(); } return first.get(out); } -template -simdjson_inline error_code simdjson_result::get(T &out) && noexcept { - if (error()) { return error(); } - return std::forward(first).get(out); -} -template<> simdjson_inline simdjson_result simdjson_result::get() & noexcept = delete; -template<> simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { - if (error()) { return error(); } - return std::forward(first); -} -template<> simdjson_inline error_code simdjson_result::get(westmere::ondemand::document &out) & noexcept = delete; -template<> simdjson_inline error_code simdjson_result::get(westmere::ondemand::document &out) && noexcept { +template<> simdjson_inline simdjson_result simdjson_result::get() noexcept { if (error()) { return error(); } - out = std::forward(first); - return SUCCESS; + return std::move(first); } -simdjson_inline simdjson_result simdjson_result::type() noexcept { +simdjson_inline simdjson_result simdjson_result::type() noexcept { if (error()) { return error(); } return first.type(); } - -simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { if (error()) { return error(); } return first.is_scalar(); } - -simdjson_inline simdjson_result simdjson_result::is_string() noexcept { +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { if (error()) { return error(); } return first.is_string(); } - -simdjson_inline bool simdjson_result::is_negative() noexcept { +simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { if (error()) { return error(); } return first.is_negative(); } - -simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { if (error()) { return error(); } return first.is_integer(); } - -simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { if (error()) { return error(); } return first.get_number_type(); } - -simdjson_inline simdjson_result simdjson_result::get_number() noexcept { +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { if (error()) { return error(); } return first.get_number(); } - - #if SIMDJSON_EXCEPTIONS -template ::value == false>::type> -simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator westmere::ondemand::array() & noexcept(false) { +template +simdjson_inline simdjson_result::operator T() noexcept(false) { if (error()) { throw simdjson_error(error()); } - return first; + return first.get(); } -simdjson_inline simdjson_result::operator westmere::ondemand::object() & noexcept(false) { +simdjson_inline simdjson_result::operator westmere::ondemand::array() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { +simdjson_inline simdjson_result::operator westmere::ondemand::object() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator int64_t() noexcept(false) { +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator double() noexcept(false) { +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { +simdjson_inline simdjson_result::operator double() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator westmere::ondemand::raw_json_string() noexcept(false) { +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator bool() noexcept(false) { +simdjson_inline simdjson_result::operator westmere::ondemand::raw_json_string() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator westmere::ondemand::value() noexcept(false) { +simdjson_inline simdjson_result::operator bool() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } #endif - -simdjson_inline simdjson_result simdjson_result::current_location() noexcept { +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { if (error()) { return error(); } - return first.current_location(); + return first.raw_json_token(); } -simdjson_inline bool simdjson_result::at_end() const noexcept { +simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { if (error()) { return error(); } - return first.at_end(); + return first.raw_json(); } - -simdjson_inline int32_t simdjson_result::current_depth() const noexcept { +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { if (error()) { return error(); } - return first.current_depth(); + return first.current_location(); } -simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { +simdjson_inline simdjson_result simdjson_result::current_depth() const noexcept { if (error()) { return error(); } - return first.raw_json_token(); + return first.current_depth(); } -simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } +simdjson_inline simdjson_result simdjson_result::at_pointer( + std::string_view json_pointer) noexcept { + if (error()) { + return error(); + } return first.at_pointer(json_pointer); } -simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { return error(); } +simdjson_inline simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } return first.at_path(json_path); } } // namespace simdjson +#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H +/* end file simdjson/generic/ondemand/value-inl.h for westmere */ +/* including simdjson/generic/ondemand/document-inl.h for westmere: #include "simdjson/generic/ondemand/document-inl.h" */ +/* begin file simdjson/generic/ondemand/document-inl.h for westmere */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { namespace westmere { namespace ondemand { -simdjson_inline document_reference::document_reference() noexcept : doc{nullptr} {} -simdjson_inline document_reference::document_reference(document &d) noexcept : doc(&d) {} -simdjson_inline void document_reference::rewind() noexcept { doc->rewind(); } -simdjson_inline simdjson_result document_reference::get_array() & noexcept { return doc->get_array(); } -simdjson_inline simdjson_result document_reference::get_object() & noexcept { return doc->get_object(); } -/** - * The document_reference instances are used primarily/solely for streams of JSON - * documents. - * We decided that calling 'get_double()' on the JSON document '1.233 blabla' should - * give an error, so we check for trailing content. - * - * However, for streams of JSON documents, we want to be able to start from - * "321" "321" "321" - * and parse it successfully as a stream of JSON documents, calling get_uint64_in_string() - * successfully each time. - * - * To achieve this result, we pass a 'false' to a get_root_value_iterator() method: - * this indicates that we allow trailing content. - */ -simdjson_inline simdjson_result document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); } -simdjson_inline simdjson_result document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); } -simdjson_inline simdjson_result document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); } -simdjson_inline simdjson_result document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); } -simdjson_inline simdjson_result document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); } -simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } -simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } -template -simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); } -simdjson_inline simdjson_result document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } -simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } -simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } -simdjson_inline simdjson_result document_reference::get_value() noexcept { return doc->get_value(); } -simdjson_inline simdjson_result document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_array(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_object(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_raw_json_string(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_string(false); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_double(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_uint64(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_int64(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_bool(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_value(); } -#if SIMDJSON_EXCEPTIONS -template -simdjson_inline document_reference::operator T() noexcept(false) { return get(); } -simdjson_inline document_reference::operator array() & noexcept(false) { return array(*doc); } -simdjson_inline document_reference::operator object() & noexcept(false) { return object(*doc); } -simdjson_inline document_reference::operator uint64_t() noexcept(false) { return get_uint64(); } -simdjson_inline document_reference::operator int64_t() noexcept(false) { return get_int64(); } -simdjson_inline document_reference::operator double() noexcept(false) { return get_double(); } -simdjson_inline document_reference::operator std::string_view() noexcept(false) { return std::string_view(*doc); } -simdjson_inline document_reference::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } -simdjson_inline document_reference::operator bool() noexcept(false) { return get_bool(); } -simdjson_inline document_reference::operator value() noexcept(false) { return value(*doc); } -#endif -simdjson_inline simdjson_result document_reference::count_elements() & noexcept { return doc->count_elements(); } -simdjson_inline simdjson_result document_reference::count_fields() & noexcept { return doc->count_fields(); } -simdjson_inline simdjson_result document_reference::at(size_t index) & noexcept { return doc->at(index); } -simdjson_inline simdjson_result document_reference::begin() & noexcept { return doc->begin(); } -simdjson_inline simdjson_result document_reference::end() & noexcept { return doc->end(); } -simdjson_inline simdjson_result document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); } -simdjson_inline simdjson_result document_reference::find_field(const char *key) & noexcept { return doc->find_field(key); } -simdjson_inline simdjson_result document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; } -simdjson_inline simdjson_result document_reference::operator[](const char *key) & noexcept { return (*doc)[key]; } -simdjson_inline simdjson_result document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); } -simdjson_inline simdjson_result document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); } -simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } -simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } -simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } -simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } -simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } -simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } -simdjson_inline simdjson_result document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); } -simdjson_inline simdjson_result document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); } -simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } -simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } -simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } -simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } -simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} -simdjson_inline document_reference::operator document&() const noexcept { return *doc; } +simdjson_inline document::document(ondemand::json_iterator &&_iter) noexcept + : iter{std::forward(_iter)} +{ + logger::log_start_value(iter, "document"); +} -} // namespace ondemand -} // namespace westmere -} // namespace simdjson +simdjson_inline document document::start(json_iterator &&iter) noexcept { + return document(std::forward(iter)); +} +inline void document::rewind() noexcept { + iter.rewind(); +} +inline std::string document::to_debug_string() noexcept { + return iter.to_string(); +} -namespace simdjson { -simdjson_inline simdjson_result::simdjson_result(westmere::ondemand::document_reference value, error_code error) - noexcept : implementation_simdjson_result_base(std::forward(value), error) {} +inline simdjson_result document::current_location() const noexcept { + return iter.current_location(); +} +inline int32_t document::current_depth() const noexcept { + return iter.depth(); +} -simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); +inline bool document::at_end() const noexcept { + return iter.at_end(); } -simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); + + +inline bool document::is_alive() noexcept { + return iter.is_alive(); } -simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { - if (error()) { return error(); } - return first.at(index); +simdjson_inline value_iterator document::resume_value_iterator() noexcept { + return value_iterator(&iter, 1, iter.root_position()); } -simdjson_inline error_code simdjson_result::rewind() noexcept { - if (error()) { return error(); } - first.rewind(); - return SUCCESS; +simdjson_inline value_iterator document::get_root_value_iterator() noexcept { + return resume_value_iterator(); } -simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); +simdjson_inline simdjson_result document::start_or_resume_object() noexcept { + if (iter.at_root()) { + return get_object(); + } else { + return object::resume(resume_value_iterator()); + } } -simdjson_inline simdjson_result simdjson_result::end() & noexcept { - return {}; +simdjson_inline simdjson_result document::get_value() noexcept { + // Make sure we start any arrays or objects before returning, so that start_root_() + // gets called. + + // It is the convention throughout the code that the macro `SIMDJSON_DEVELOPMENT_CHECKS` determines whether + // we check for OUT_OF_ORDER_ITERATION. Proper on::demand code should never trigger this error. +#if SIMDJSON_DEVELOPMENT_CHECKS + if (!iter.at_root()) { return OUT_OF_ORDER_ITERATION; } +#endif + // assert_at_root() serves two purposes: in Debug mode, whether or not + // SIMDJSON_DEVELOPMENT_CHECKS is set or not, it checks that we are at the root of + // the document (this will typically be redundant). In release mode, it generates + // SIMDJSON_ASSUME statements to allow the compiler to make assumptions. + iter.assert_at_root(); + switch (*iter.peek()) { + case '[': { + // The following lines check that the document ends with ]. + auto value_iterator = get_root_value_iterator(); + auto error = value_iterator.check_root_array(); + if(error) { return error; } + return value(get_root_value_iterator()); + } + case '{': { + // The following lines would check that the document ends with }. + auto value_iterator = get_root_value_iterator(); + auto error = value_iterator.check_root_object(); + if(error) { return error; } + return value(get_root_value_iterator()); + } + default: + // Unfortunately, scalar documents are a special case in simdjson and they cannot + // be safely converted to value instances. + return SCALAR_DOCUMENT_AS_VALUE; + } } -simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { +simdjson_inline simdjson_result document::get_array() & noexcept { + auto value = get_root_value_iterator(); + return array::start_root(value); +} +simdjson_inline simdjson_result document::get_object() & noexcept { + auto value = get_root_value_iterator(); + return object::start_root(value); +} + +/** + * We decided that calling 'get_double()' on the JSON document '1.233 blabla' should + * give an error, so we check for trailing content. We want to disallow trailing + * content. + * Thus, in several implementations below, we pass a 'true' parameter value to + * a get_root_value_iterator() method: this indicates that we disallow trailing content. + */ + +simdjson_inline simdjson_result document::get_uint64() noexcept { + return get_root_value_iterator().get_root_uint64(true); +} +simdjson_inline simdjson_result document::get_uint64_in_string() noexcept { + return get_root_value_iterator().get_root_uint64_in_string(true); +} +simdjson_inline simdjson_result document::get_int64() noexcept { + return get_root_value_iterator().get_root_int64(true); +} +simdjson_inline simdjson_result document::get_int64_in_string() noexcept { + return get_root_value_iterator().get_root_int64_in_string(true); +} +simdjson_inline simdjson_result document::get_double() noexcept { + return get_root_value_iterator().get_root_double(true); +} +simdjson_inline simdjson_result document::get_double_in_string() noexcept { + return get_root_value_iterator().get_root_double_in_string(true); +} +simdjson_inline simdjson_result document::get_string(bool allow_replacement) noexcept { + return get_root_value_iterator().get_root_string(true, allow_replacement); +} +template +simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept { + return get_root_value_iterator().get_root_string(receiver, true, allow_replacement); +} +simdjson_inline simdjson_result document::get_wobbly_string() noexcept { + return get_root_value_iterator().get_root_wobbly_string(true); +} +simdjson_inline simdjson_result document::get_raw_json_string() noexcept { + return get_root_value_iterator().get_root_raw_json_string(true); +} +simdjson_inline simdjson_result document::get_bool() noexcept { + return get_root_value_iterator().get_root_bool(true); +} +simdjson_inline simdjson_result document::is_null() noexcept { + return get_root_value_iterator().is_root_null(true); +} + +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_array(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_object(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_double(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_value(); } + +template<> simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); } +template<> simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); } +template<> simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); } +template<> simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); } +template<> simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); } +template<> simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); } +template<> simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); } +template<> simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); } +template<> simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); } + +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_raw_json_string(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_value(); } + +#if SIMDJSON_EXCEPTIONS +template +simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get(); } +template +simdjson_inline document::operator T() & noexcept(false) { return get(); } +simdjson_inline document::operator array() & noexcept(false) { return get_array(); } +simdjson_inline document::operator object() & noexcept(false) { return get_object(); } +simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); } +simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); } +simdjson_inline document::operator double() noexcept(false) { return get_double(); } +simdjson_inline document::operator std::string_view() noexcept(false) { return get_string(false); } +simdjson_inline document::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } +simdjson_inline document::operator bool() noexcept(false) { return get_bool(); } +simdjson_inline document::operator value() noexcept(false) { return get_value(); } + +#endif +simdjson_inline simdjson_result document::count_elements() & noexcept { + auto a = get_array(); + simdjson_result answer = a.count_elements(); + /* If there was an array, we are now left pointing at its first element. */ + if(answer.error() == SUCCESS) { rewind(); } + return answer; +} +simdjson_inline simdjson_result document::count_fields() & noexcept { + auto a = get_object(); + simdjson_result answer = a.count_fields(); + /* If there was an object, we are now left pointing at its first element. */ + if(answer.error() == SUCCESS) { rewind(); } + return answer; +} +simdjson_inline simdjson_result document::at(size_t index) & noexcept { + auto a = get_array(); + return a.at(index); +} +simdjson_inline simdjson_result document::begin() & noexcept { + return get_array().begin(); +} +simdjson_inline simdjson_result document::end() & noexcept { + return {}; +} + +simdjson_inline simdjson_result document::find_field(std::string_view key) & noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result document::find_field(const char *key) & noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result document::find_field_unordered(std::string_view key) & noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result document::find_field_unordered(const char *key) & noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result document::operator[](std::string_view key) & noexcept { + return start_or_resume_object()[key]; +} +simdjson_inline simdjson_result document::operator[](const char *key) & noexcept { + return start_or_resume_object()[key]; +} + +simdjson_inline error_code document::consume() noexcept { + bool scalar = false; + auto error = is_scalar().get(scalar); + if(error) { return error; } + if(scalar) { + iter.return_current_and_advance(); + return SUCCESS; + } + error = iter.skip_child(0); + if(error) { iter.abandon(); } + return error; +} + +simdjson_inline simdjson_result document::raw_json() noexcept { + auto _iter = get_root_value_iterator(); + const uint8_t * starting_point{_iter.peek_start()}; + auto error = consume(); + if(error) { return error; } + // After 'consume()', we could be left pointing just beyond the document, but that + // is ok because we are not going to dereference the final pointer position, we just + // use it to compute the length in bytes. + const uint8_t * final_point{iter.unsafe_pointer()}; + return std::string_view(reinterpret_cast(starting_point), size_t(final_point - starting_point)); +} + +simdjson_inline simdjson_result document::type() noexcept { + return get_root_value_iterator().type(); +} + +simdjson_inline simdjson_result document::is_scalar() noexcept { + // For more speed, we could do: + // return iter.is_single_token(); + json_type this_type; + auto error = type().get(this_type); + if(error) { return error; } + return ! ((this_type == json_type::array) || (this_type == json_type::object)); +} + +simdjson_inline simdjson_result document::is_string() noexcept { + json_type this_type; + auto error = type().get(this_type); + if(error) { return error; } + return (this_type == json_type::string); +} + +simdjson_inline bool document::is_negative() noexcept { + return get_root_value_iterator().is_root_negative(); +} + +simdjson_inline simdjson_result document::is_integer() noexcept { + return get_root_value_iterator().is_root_integer(true); +} + +simdjson_inline simdjson_result document::get_number_type() noexcept { + return get_root_value_iterator().get_root_number_type(true); +} + +simdjson_inline simdjson_result document::get_number() noexcept { + return get_root_value_iterator().get_root_number(true); +} + + +simdjson_inline simdjson_result document::raw_json_token() noexcept { + auto _iter = get_root_value_iterator(); + return std::string_view(reinterpret_cast(_iter.peek_start()), _iter.peek_root_length()); +} + +simdjson_inline simdjson_result document::at_pointer(std::string_view json_pointer) noexcept { + rewind(); // Rewind the document each time at_pointer is called + if (json_pointer.empty()) { + return this->get_value(); + } + json_type t; + SIMDJSON_TRY(type().get(t)); + switch (t) + { + case json_type::array: + return (*this).get_array().at_pointer(json_pointer); + case json_type::object: + return (*this).get_object().at_pointer(json_pointer); + default: + return INVALID_JSON_POINTER; + } +} + +simdjson_inline simdjson_result document::at_path(std::string_view json_path) noexcept { + rewind(); // Rewind the document each time at_pointer is called + if (json_path.empty()) { + return this->get_value(); + } + json_type t; + SIMDJSON_TRY(type().get(t)); + switch (t) { + case json_type::array: + return (*this).get_array().at_path(json_path); + case json_type::object: + return (*this).get_object().at_path(json_path); + default: + return INVALID_JSON_POINTER; + } +} + +} // namespace ondemand +} // namespace westmere +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result( + westmere::ondemand::document &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base( + error + ) +{ +} +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline error_code simdjson_result::rewind() noexcept { + if (error()) { return error(); } + first.rewind(); + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { if (error()) { return error(); } return first.find_field_unordered(key); } -simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { if (error()) { return error(); } return first.find_field_unordered(key); } -simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { if (error()) { return error(); } return first[key]; } -simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { if (error()) { return error(); } return first[key]; } -simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { if (error()) { return error(); } return first.find_field(key); } -simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { if (error()) { return error(); } return first.find_field(key); } -simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { +simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { if (error()) { return error(); } return first.get_array(); } -simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { +simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { if (error()) { return error(); } return first.get_object(); } -simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { if (error()) { return error(); } return first.get_uint64(); } -simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { if (error()) { return error(); } return first.get_uint64_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { if (error()) { return error(); } return first.get_int64(); } -simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { if (error()) { return error(); } return first.get_int64_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_double() noexcept { +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { if (error()) { return error(); } return first.get_double(); } -simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { if (error()) { return error(); } return first.get_double_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(allow_replacement); } template -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(receiver, allow_replacement); } -simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { if (error()) { return error(); } return first.get_wobbly_string(); } -simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { if (error()) { return error(); } return first.get_raw_json_string(); } -simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { if (error()) { return error(); } return first.get_bool(); } -simdjson_inline simdjson_result simdjson_result::get_value() noexcept { +simdjson_inline simdjson_result simdjson_result::get_value() noexcept { if (error()) { return error(); } return first.get_value(); } -simdjson_inline simdjson_result simdjson_result::is_null() noexcept { +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { if (error()) { return error(); } return first.is_null(); } -simdjson_inline simdjson_result simdjson_result::type() noexcept { + +template +simdjson_inline simdjson_result simdjson_result::get() & noexcept { + if (error()) { return error(); } + return first.get(); +} +template +simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first).get(); +} +template +simdjson_inline error_code simdjson_result::get(T &out) & noexcept { + if (error()) { return error(); } + return first.get(out); +} +template +simdjson_inline error_code simdjson_result::get(T &out) && noexcept { + if (error()) { return error(); } + return std::forward(first).get(out); +} + +template<> simdjson_inline simdjson_result simdjson_result::get() & noexcept = delete; +template<> simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first); +} +template<> simdjson_inline error_code simdjson_result::get(westmere::ondemand::document &out) & noexcept = delete; +template<> simdjson_inline error_code simdjson_result::get(westmere::ondemand::document &out) && noexcept { + if (error()) { return error(); } + out = std::forward(first); + return SUCCESS; +} + +simdjson_inline simdjson_result simdjson_result::type() noexcept { if (error()) { return error(); } return first.type(); } -simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { if (error()) { return error(); } return first.is_scalar(); } -simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { if (error()) { return error(); } return first.is_string(); } -simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { + +simdjson_inline bool simdjson_result::is_negative() noexcept { if (error()) { return error(); } return first.is_negative(); } -simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { if (error()) { return error(); } return first.is_integer(); } -simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { if (error()) { return error(); } return first.get_number_type(); } -simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { if (error()) { return error(); } return first.get_number(); } + + #if SIMDJSON_EXCEPTIONS -template ::value == false>::type> -simdjson_inline simdjson_result::operator T() noexcept(false) { +template ::value == false>::type> +simdjson_inline simdjson_result::operator T() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator westmere::ondemand::array() & noexcept(false) { +simdjson_inline simdjson_result::operator westmere::ondemand::array() & noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator westmere::ondemand::object() & noexcept(false) { +simdjson_inline simdjson_result::operator westmere::ondemand::object() & noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator int64_t() noexcept(false) { +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator double() noexcept(false) { +simdjson_inline simdjson_result::operator double() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator westmere::ondemand::raw_json_string() noexcept(false) { +simdjson_inline simdjson_result::operator westmere::ondemand::raw_json_string() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator bool() noexcept(false) { +simdjson_inline simdjson_result::operator bool() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator westmere::ondemand::value() noexcept(false) { +simdjson_inline simdjson_result::operator westmere::ondemand::value() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } #endif -simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { if (error()) { return error(); } return first.current_location(); } -simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { +simdjson_inline bool simdjson_result::at_end() const noexcept { + if (error()) { return error(); } + return first.at_end(); +} + + +simdjson_inline int32_t simdjson_result::current_depth() const noexcept { + if (error()) { return error(); } + return first.current_depth(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { if (error()) { return error(); } return first.raw_json_token(); } -simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { if (error()) { return error(); } return first.at_pointer(json_pointer); } -simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { - return error(); - } +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } return first.at_path(json_path); } } // namespace simdjson -#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H -/* end file simdjson/generic/ondemand/document-inl.h for westmere */ -/* including simdjson/generic/ondemand/document_stream-inl.h for westmere: #include "simdjson/generic/ondemand/document_stream-inl.h" */ -/* begin file simdjson/generic/ondemand/document_stream-inl.h for westmere */ -#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H - -/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ -/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document_stream.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */ -/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -#include -#include namespace simdjson { namespace westmere { namespace ondemand { -#ifdef SIMDJSON_THREADS_ENABLED - -inline void stage1_worker::finish() { - // After calling "run" someone would call finish() to wait - // for the end of the processing. - // This function will wait until either the thread has done - // the processing or, else, the destructor has been called. - std::unique_lock lock(locking_mutex); - cond_var.wait(lock, [this]{return has_work == false;}); -} - -inline stage1_worker::~stage1_worker() { - // The thread may never outlive the stage1_worker instance - // and will always be stopped/joined before the stage1_worker - // instance is gone. - stop_thread(); -} - -inline void stage1_worker::start_thread() { - std::unique_lock lock(locking_mutex); - if(thread.joinable()) { - return; // This should never happen but we never want to create more than one thread. - } - thread = std::thread([this]{ - while(true) { - std::unique_lock thread_lock(locking_mutex); - // We wait for either "run" or "stop_thread" to be called. - cond_var.wait(thread_lock, [this]{return has_work || !can_work;}); - // If, for some reason, the stop_thread() method was called (i.e., the - // destructor of stage1_worker is called, then we want to immediately destroy - // the thread (and not do any more processing). - if(!can_work) { - break; - } - this->owner->stage1_thread_error = this->owner->run_stage1(*this->stage1_thread_parser, - this->_next_batch_start); - this->has_work = false; - // The condition variable call should be moved after thread_lock.unlock() for performance - // reasons but thread sanitizers may report it as a data race if we do. - // See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock - cond_var.notify_one(); // will notify "finish" - thread_lock.unlock(); - } - } - ); -} - - -inline void stage1_worker::stop_thread() { - std::unique_lock lock(locking_mutex); - // We have to make sure that all locks can be released. - can_work = false; - has_work = false; - cond_var.notify_all(); - lock.unlock(); - if(thread.joinable()) { - thread.join(); - } -} - -inline void stage1_worker::run(document_stream * ds, parser * stage1, size_t next_batch_start) { - std::unique_lock lock(locking_mutex); - owner = ds; - _next_batch_start = next_batch_start; - stage1_thread_parser = stage1; - has_work = true; - // The condition variable call should be moved after thread_lock.unlock() for performance - // reasons but thread sanitizers may report it as a data race if we do. - // See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock - cond_var.notify_one(); // will notify the thread lock that we have work - lock.unlock(); -} - -#endif // SIMDJSON_THREADS_ENABLED - -simdjson_inline document_stream::document_stream( - ondemand::parser &_parser, - const uint8_t *_buf, - size_t _len, +simdjson_inline document_reference::document_reference() noexcept : doc{nullptr} {} +simdjson_inline document_reference::document_reference(document &d) noexcept : doc(&d) {} +simdjson_inline void document_reference::rewind() noexcept { doc->rewind(); } +simdjson_inline simdjson_result document_reference::get_array() & noexcept { return doc->get_array(); } +simdjson_inline simdjson_result document_reference::get_object() & noexcept { return doc->get_object(); } +/** + * The document_reference instances are used primarily/solely for streams of JSON + * documents. + * We decided that calling 'get_double()' on the JSON document '1.233 blabla' should + * give an error, so we check for trailing content. + * + * However, for streams of JSON documents, we want to be able to start from + * "321" "321" "321" + * and parse it successfully as a stream of JSON documents, calling get_uint64_in_string() + * successfully each time. + * + * To achieve this result, we pass a 'false' to a get_root_value_iterator() method: + * this indicates that we allow trailing content. + */ +simdjson_inline simdjson_result document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); } +simdjson_inline simdjson_result document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); } +simdjson_inline simdjson_result document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); } +simdjson_inline simdjson_result document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); } +simdjson_inline simdjson_result document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); } +simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } +simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } +template +simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); } +simdjson_inline simdjson_result document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } +simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } +simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } +simdjson_inline simdjson_result document_reference::get_value() noexcept { return doc->get_value(); } +simdjson_inline simdjson_result document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_array(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_object(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_double(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_bool(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_value(); } +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline document_reference::operator T() noexcept(false) { return get(); } +simdjson_inline document_reference::operator array() & noexcept(false) { return array(*doc); } +simdjson_inline document_reference::operator object() & noexcept(false) { return object(*doc); } +simdjson_inline document_reference::operator uint64_t() noexcept(false) { return get_uint64(); } +simdjson_inline document_reference::operator int64_t() noexcept(false) { return get_int64(); } +simdjson_inline document_reference::operator double() noexcept(false) { return get_double(); } +simdjson_inline document_reference::operator std::string_view() noexcept(false) { return std::string_view(*doc); } +simdjson_inline document_reference::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } +simdjson_inline document_reference::operator bool() noexcept(false) { return get_bool(); } +simdjson_inline document_reference::operator value() noexcept(false) { return value(*doc); } +#endif +simdjson_inline simdjson_result document_reference::count_elements() & noexcept { return doc->count_elements(); } +simdjson_inline simdjson_result document_reference::count_fields() & noexcept { return doc->count_fields(); } +simdjson_inline simdjson_result document_reference::at(size_t index) & noexcept { return doc->at(index); } +simdjson_inline simdjson_result document_reference::begin() & noexcept { return doc->begin(); } +simdjson_inline simdjson_result document_reference::end() & noexcept { return doc->end(); } +simdjson_inline simdjson_result document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); } +simdjson_inline simdjson_result document_reference::find_field(const char *key) & noexcept { return doc->find_field(key); } +simdjson_inline simdjson_result document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; } +simdjson_inline simdjson_result document_reference::operator[](const char *key) & noexcept { return (*doc)[key]; } +simdjson_inline simdjson_result document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); } +simdjson_inline simdjson_result document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); } +simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } +simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } +simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } +simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } +simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } +simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } +simdjson_inline simdjson_result document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); } +simdjson_inline simdjson_result document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); } +simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } +simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } +simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } +simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } +simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} +simdjson_inline document_reference::operator document&() const noexcept { return *doc; } + +} // namespace ondemand +} // namespace westmere +} // namespace simdjson + + + +namespace simdjson { +simdjson_inline simdjson_result::simdjson_result(westmere::ondemand::document_reference value, error_code error) + noexcept : implementation_simdjson_result_base(std::forward(value), error) {} + + +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline error_code simdjson_result::rewind() noexcept { + if (error()) { return error(); } + first.rewind(); + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { + if (error()) { return error(); } + return first.get_array(); +} +simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { + if (error()) { return error(); } + return first.get_object(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { + if (error()) { return error(); } + return first.get_uint64(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { + if (error()) { return error(); } + return first.get_uint64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { + if (error()) { return error(); } + return first.get_int64(); +} +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { + if (error()) { return error(); } + return first.get_int64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { + if (error()) { return error(); } + return first.get_double(); +} +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { + if (error()) { return error(); } + return first.get_double_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(allow_replacement); +} +template +simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(receiver, allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { + if (error()) { return error(); } + return first.get_wobbly_string(); +} +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { + if (error()) { return error(); } + return first.get_raw_json_string(); +} +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { + if (error()) { return error(); } + return first.get_bool(); +} +simdjson_inline simdjson_result simdjson_result::get_value() noexcept { + if (error()) { return error(); } + return first.get_value(); +} +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { + if (error()) { return error(); } + return first.is_null(); +} +template +simdjson_inline simdjson_result simdjson_result::get() & noexcept { + if (error()) { return error(); } + return first.get(); +} +template +simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first).get(); +} +template +simdjson_inline error_code simdjson_result::get(T &out) & noexcept { + if (error()) { return error(); } + return first.get(out); +} +template +simdjson_inline error_code simdjson_result::get(T &out) && noexcept { + if (error()) { return error(); } + return std::forward(first).get(out); +} +simdjson_inline simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); +} +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + if (error()) { return error(); } + return first.is_scalar(); +} +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} +template <> +simdjson_inline error_code simdjson_result::get(westmere::ondemand::document_reference &out) & noexcept { + if (error()) { return error(); } + out = first; + return SUCCESS; +} +template <> +simdjson_inline error_code simdjson_result::get(westmere::ondemand::document_reference &out) && noexcept { + if (error()) { return error(); } + out = first; + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { + if (error()) { return error(); } + return first.is_negative(); +} +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + if (error()) { return error(); } + return first.is_integer(); +} +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + if (error()) { return error(); } + return first.get_number_type(); +} +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + if (error()) { return error(); } + return first.get_number(); +} +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline simdjson_result::operator T() noexcept(false) { + static_assert(std::is_same::value == false, "You should not call get when T is a document"); + static_assert(std::is_same::value == false, "You should not call get when T is a document"); + if (error()) { throw simdjson_error(error()); } + return first.get(); +} +simdjson_inline simdjson_result::operator westmere::ondemand::array() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator westmere::ondemand::object() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator double() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator westmere::ondemand::raw_json_string() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator bool() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator westmere::ondemand::value() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +#endif + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} + +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H +/* end file simdjson/generic/ondemand/document-inl.h for westmere */ +/* including simdjson/generic/ondemand/document_stream-inl.h for westmere: #include "simdjson/generic/ondemand/document_stream-inl.h" */ +/* begin file simdjson/generic/ondemand/document_stream-inl.h for westmere */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document_stream.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +#include + +namespace simdjson { +namespace westmere { +namespace ondemand { + +#ifdef SIMDJSON_THREADS_ENABLED + +inline void stage1_worker::finish() { + // After calling "run" someone would call finish() to wait + // for the end of the processing. + // This function will wait until either the thread has done + // the processing or, else, the destructor has been called. + std::unique_lock lock(locking_mutex); + cond_var.wait(lock, [this]{return has_work == false;}); +} + +inline stage1_worker::~stage1_worker() { + // The thread may never outlive the stage1_worker instance + // and will always be stopped/joined before the stage1_worker + // instance is gone. + stop_thread(); +} + +inline void stage1_worker::start_thread() { + std::unique_lock lock(locking_mutex); + if(thread.joinable()) { + return; // This should never happen but we never want to create more than one thread. + } + thread = std::thread([this]{ + while(true) { + std::unique_lock thread_lock(locking_mutex); + // We wait for either "run" or "stop_thread" to be called. + cond_var.wait(thread_lock, [this]{return has_work || !can_work;}); + // If, for some reason, the stop_thread() method was called (i.e., the + // destructor of stage1_worker is called, then we want to immediately destroy + // the thread (and not do any more processing). + if(!can_work) { + break; + } + this->owner->stage1_thread_error = this->owner->run_stage1(*this->stage1_thread_parser, + this->_next_batch_start); + this->has_work = false; + // The condition variable call should be moved after thread_lock.unlock() for performance + // reasons but thread sanitizers may report it as a data race if we do. + // See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock + cond_var.notify_one(); // will notify "finish" + thread_lock.unlock(); + } + } + ); +} + + +inline void stage1_worker::stop_thread() { + std::unique_lock lock(locking_mutex); + // We have to make sure that all locks can be released. + can_work = false; + has_work = false; + cond_var.notify_all(); + lock.unlock(); + if(thread.joinable()) { + thread.join(); + } +} + +inline void stage1_worker::run(document_stream * ds, parser * stage1, size_t next_batch_start) { + std::unique_lock lock(locking_mutex); + owner = ds; + _next_batch_start = next_batch_start; + stage1_thread_parser = stage1; + has_work = true; + // The condition variable call should be moved after thread_lock.unlock() for performance + // reasons but thread sanitizers may report it as a data race if we do. + // See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock + cond_var.notify_one(); // will notify the thread lock that we have work + lock.unlock(); +} + +#endif // SIMDJSON_THREADS_ENABLED + +simdjson_inline document_stream::document_stream( + ondemand::parser &_parser, + const uint8_t *_buf, + size_t _len, size_t _batch_size, bool _allow_comma_separated ) noexcept @@ -92490,7 +96493,6 @@ simdjson_inline document_stream::iterator::iterator(document_stream* _stream, bo } simdjson_inline simdjson_result document_stream::iterator::operator*() noexcept { - //if(stream->error) { return stream->error; } return simdjson_result(stream->doc, stream->error); } @@ -94349,36 +98351,39 @@ simdjson_inline const char * raw_json_string::raw() const noexcept { return rein simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(std::string_view target) noexcept { size_t pos{0}; - // if the content has no escape character, just scan through it quickly! - for(;pos < target.size() && target[pos] != '\\';pos++) {} - // slow path may begin. - bool escaping{false}; - for(;pos < target.size();pos++) { - if((target[pos] == '"') && !escaping) { - return false; - } else if(target[pos] == '\\') { - escaping = !escaping; - } else { - escaping = false; + while(pos < target.size()) { + pos = target.find('"', pos); + if(pos == std::string_view::npos) { return true; } + if(pos != 0 && target[pos-1] != '\\') { return false; } + if(pos > 1 && target[pos-2] == '\\') { + size_t backslash_count{2}; + for(size_t i = 3; i <= pos; i++) { + if(target[pos-i] == '\\') { backslash_count++; } + else { break; } + } + if(backslash_count % 2 == 0) { return false; } } + pos++; } return true; } simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(const char* target) noexcept { size_t pos{0}; - // if the content has no escape character, just scan through it quickly! - for(;target[pos] && target[pos] != '\\';pos++) {} - // slow path may begin. - bool escaping{false}; - for(;target[pos];pos++) { - if((target[pos] == '"') && !escaping) { - return false; - } else if(target[pos] == '\\') { - escaping = !escaping; - } else { - escaping = false; + while(target[pos]) { + const char * result = strchr(target+pos, '"'); + if(result == nullptr) { return true; } + pos = result - target; + if(pos != 0 && target[pos-1] != '\\') { return false; } + if(pos > 1 && target[pos-2] == '\\') { + size_t backslash_count{2}; + for(size_t i = 3; i <= pos; i++) { + if(target[pos-i] == '\\') { backslash_count++; } + else { break; } + } + if(backslash_count % 2 == 0) { return false; } } + pos++; } return true; } @@ -94390,7 +98395,7 @@ simdjson_inline bool raw_json_string::unsafe_is_equal(size_t length, std::string } simdjson_inline bool raw_json_string::unsafe_is_equal(std::string_view target) const noexcept { - // Assumptions: does not contain unescaped quote characters, and + // Assumptions: does not contain unescaped quote characters("), and // the raw content is quote terminated within a valid JSON string. if(target.size() <= SIMDJSON_PADDING) { return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size()); @@ -94864,775 +98869,230 @@ simdjson_inline simdjson_result::simdjson_re #endif // SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H /* end file simdjson/generic/ondemand/token_iterator-inl.h for westmere */ -/* including simdjson/generic/ondemand/value-inl.h for westmere: #include "simdjson/generic/ondemand/value-inl.h" */ -/* begin file simdjson/generic/ondemand/value-inl.h for westmere */ -#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H +/* including simdjson/generic/ondemand/value_iterator-inl.h for westmere: #include "simdjson/generic/ondemand/value_iterator-inl.h" */ +/* begin file simdjson/generic/ondemand/value_iterator-inl.h for westmere */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ -/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/atomparsing.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/numberparsing.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string-inl.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { namespace westmere { namespace ondemand { -simdjson_inline value::value(const value_iterator &_iter) noexcept - : iter{_iter} +simdjson_inline value_iterator::value_iterator( + json_iterator *json_iter, + depth_t depth, + token_position start_position +) noexcept : _json_iter{json_iter}, _depth{depth}, _start_position{start_position} { } -simdjson_inline value value::start(const value_iterator &iter) noexcept { - return iter; -} -simdjson_inline value value::resume(const value_iterator &iter) noexcept { - return iter; -} - -simdjson_inline simdjson_result value::get_array() noexcept { - return array::start(iter); -} -simdjson_inline simdjson_result value::get_object() noexcept { - return object::start(iter); -} -simdjson_inline simdjson_result value::start_or_resume_object() noexcept { - if (iter.at_start()) { - return get_object(); - } else { - return object::resume(iter); - } -} -simdjson_inline simdjson_result value::get_raw_json_string() noexcept { - return iter.get_raw_json_string(); -} -simdjson_inline simdjson_result value::get_string(bool allow_replacement) noexcept { - return iter.get_string(allow_replacement); -} -template -simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept { - return iter.get_string(receiver, allow_replacement); -} -simdjson_inline simdjson_result value::get_wobbly_string() noexcept { - return iter.get_wobbly_string(); -} -simdjson_inline simdjson_result value::get_double() noexcept { - return iter.get_double(); -} -simdjson_inline simdjson_result value::get_double_in_string() noexcept { - return iter.get_double_in_string(); -} -simdjson_inline simdjson_result value::get_uint64() noexcept { - return iter.get_uint64(); -} -simdjson_inline simdjson_result value::get_uint64_in_string() noexcept { - return iter.get_uint64_in_string(); -} -simdjson_inline simdjson_result value::get_int64() noexcept { - return iter.get_int64(); -} -simdjson_inline simdjson_result value::get_int64_in_string() noexcept { - return iter.get_int64_in_string(); -} -simdjson_inline simdjson_result value::get_bool() noexcept { - return iter.get_bool(); -} -simdjson_inline simdjson_result value::is_null() noexcept { - return iter.is_null(); +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_object() noexcept { + SIMDJSON_TRY( start_container('{', "Not an object", "object") ); + return started_object(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_array(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_object(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_raw_json_string(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_string(false); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_number(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_double(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_uint64(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_int64(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_bool(); } -template simdjson_inline error_code value::get(T &out) noexcept { - return get().get(out); +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_root_object() noexcept { + SIMDJSON_TRY( start_container('{', "Not an object", "object") ); + return started_root_object(); } -#if SIMDJSON_EXCEPTIONS -template -simdjson_inline value::operator T() noexcept(false) { - return get(); -} -simdjson_inline value::operator array() noexcept(false) { - return get_array(); -} -simdjson_inline value::operator object() noexcept(false) { - return get_object(); -} -simdjson_inline value::operator uint64_t() noexcept(false) { - return get_uint64(); -} -simdjson_inline value::operator int64_t() noexcept(false) { - return get_int64(); -} -simdjson_inline value::operator double() noexcept(false) { - return get_double(); -} -simdjson_inline value::operator std::string_view() noexcept(false) { - return get_string(false); -} -simdjson_inline value::operator raw_json_string() noexcept(false) { - return get_raw_json_string(); -} -simdjson_inline value::operator bool() noexcept(false) { - return get_bool(); -} +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::started_object() noexcept { + assert_at_container_start(); +#if SIMDJSON_DEVELOPMENT_CHECKS + _json_iter->set_start_position(_depth, start_position()); #endif - -simdjson_inline simdjson_result value::begin() & noexcept { - return get_array().begin(); -} -simdjson_inline simdjson_result value::end() & noexcept { - return {}; -} -simdjson_inline simdjson_result value::count_elements() & noexcept { - simdjson_result answer; - auto a = get_array(); - answer = a.count_elements(); - // count_elements leaves you pointing inside the array, at the first element. - // We need to move back so that the user can create a new array (which requires that - // we point at '['). - iter.move_at_start(); - return answer; -} -simdjson_inline simdjson_result value::count_fields() & noexcept { - simdjson_result answer; - auto a = get_object(); - answer = a.count_fields(); - iter.move_at_start(); - return answer; -} -simdjson_inline simdjson_result value::at(size_t index) noexcept { - auto a = get_array(); - return a.at(index); -} - -simdjson_inline simdjson_result value::find_field(std::string_view key) noexcept { - return start_or_resume_object().find_field(key); -} -simdjson_inline simdjson_result value::find_field(const char *key) noexcept { - return start_or_resume_object().find_field(key); -} - -simdjson_inline simdjson_result value::find_field_unordered(std::string_view key) noexcept { - return start_or_resume_object().find_field_unordered(key); -} -simdjson_inline simdjson_result value::find_field_unordered(const char *key) noexcept { - return start_or_resume_object().find_field_unordered(key); -} - -simdjson_inline simdjson_result value::operator[](std::string_view key) noexcept { - return start_or_resume_object()[key]; -} -simdjson_inline simdjson_result value::operator[](const char *key) noexcept { - return start_or_resume_object()[key]; -} - -simdjson_inline simdjson_result value::type() noexcept { - return iter.type(); + if (*_json_iter->peek() == '}') { + logger::log_value(*_json_iter, "empty object"); + _json_iter->return_current_and_advance(); + end_container(); + return false; + } + return true; } -simdjson_inline simdjson_result value::is_scalar() noexcept { - json_type this_type; - auto error = type().get(this_type); - if(error) { return error; } - return ! ((this_type == json_type::array) || (this_type == json_type::object)); +simdjson_warn_unused simdjson_inline error_code value_iterator::check_root_object() noexcept { + // When in streaming mode, we cannot expect peek_last() to be the last structural element of the + // current document. It only works in the normal mode where we have indexed a single document. + // Note that adding a check for 'streaming' is not expensive since we only have at most + // one root element. + if ( ! _json_iter->streaming() ) { + // The following lines do not fully protect against garbage content within the + // object: e.g., `{"a":2} foo }`. Users concerned with garbage content should + // call `at_end()` on the document instance at the end of the processing to + // ensure that the processing has finished at the end. + // + if (*_json_iter->peek_last() != '}') { + _json_iter->abandon(); + return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing } at end"); + } + // If the last character is } *and* the first gibberish character is also '}' + // then on-demand could accidentally go over. So we need additional checks. + // https://github.com/simdjson/simdjson/issues/1834 + // Checking that the document is balanced requires a full scan which is potentially + // expensive, but it only happens in edge cases where the first padding character is + // a closing bracket. + if ((*_json_iter->peek(_json_iter->end_position()) == '}') && (!_json_iter->balanced())) { + _json_iter->abandon(); + // The exact error would require more work. It will typically be an unclosed object. + return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "the document is unbalanced"); + } + } + return SUCCESS; } -simdjson_inline simdjson_result value::is_string() noexcept { - json_type this_type; - auto error = type().get(this_type); +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::started_root_object() noexcept { + auto error = check_root_object(); if(error) { return error; } - return (this_type == json_type::string); -} - - -simdjson_inline bool value::is_negative() noexcept { - return iter.is_negative(); + return started_object(); } -simdjson_inline simdjson_result value::is_integer() noexcept { - return iter.is_integer(); -} -simdjson_warn_unused simdjson_inline simdjson_result value::get_number_type() noexcept { - return iter.get_number_type(); -} -simdjson_warn_unused simdjson_inline simdjson_result value::get_number() noexcept { - return iter.get_number(); +simdjson_warn_unused simdjson_inline error_code value_iterator::end_container() noexcept { +#if SIMDJSON_CHECK_EOF + if (depth() > 1 && at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing parent ] or }"); } + // if (depth() <= 1 && !at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing [ or { at start"); } +#endif // SIMDJSON_CHECK_EOF + _json_iter->ascend_to(depth()-1); + return SUCCESS; } -simdjson_inline std::string_view value::raw_json_token() noexcept { - return std::string_view(reinterpret_cast(iter.peek_start()), iter.peek_start_length()); -} +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::has_next_field() noexcept { + assert_at_next(); -simdjson_inline simdjson_result value::raw_json() noexcept { - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) - { - case json_type::array: { - ondemand::array array; - SIMDJSON_TRY(get_array().get(array)); - return array.raw_json(); - } - case json_type::object: { - ondemand::object object; - SIMDJSON_TRY(get_object().get(object)); - return object.raw_json(); - } + // It's illegal to call this unless there are more tokens: anything that ends in } or ] is + // obligated to verify there are more tokens if they are not the top level. + switch (*_json_iter->return_current_and_advance()) { + case '}': + logger::log_end_value(*_json_iter, "object"); + SIMDJSON_TRY( end_container() ); + return false; + case ',': + return true; default: - return raw_json_token(); + return report_error(TAPE_ERROR, "Missing comma between object fields"); } } -simdjson_inline simdjson_result value::current_location() noexcept { - return iter.json_iter().current_location(); -} - -simdjson_inline int32_t value::current_depth() const noexcept{ - return iter.json_iter().depth(); -} +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::find_field_raw(const std::string_view key) noexcept { + error_code error; + bool has_value; + // + // Initially, the object can be in one of a few different places: + // + // 1. The start of the object, at the first field: + // + // ``` + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 2, index 1) + // ``` + if (at_first_field()) { + has_value = true; -inline bool is_pointer_well_formed(std::string_view json_pointer) noexcept { - if (simdjson_unlikely(json_pointer.empty())) { // can't be - return false; - } - if (simdjson_unlikely(json_pointer[0] != '/')) { - return false; - } - size_t escape = json_pointer.find('~'); - if (escape == std::string_view::npos) { - return true; - } - if (escape == json_pointer.size() - 1) { - return false; - } - if (json_pointer[escape + 1] != '0' && json_pointer[escape + 1] != '1') { + // + // 2. When a previous search did not yield a value or the object is empty: + // + // ``` + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 0) + // { } + // ^ (depth 0, index 2) + // ``` + // + } else if (!is_open()) { +#if SIMDJSON_DEVELOPMENT_CHECKS + // If we're past the end of the object, we're being iterated out of order. + // Note: this is not perfect detection. It's possible the user is inside some other object; if so, + // this object iterator will blithely scan that object for fields. + if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; } +#endif return false; - } - return true; -} -simdjson_inline simdjson_result value::at_pointer(std::string_view json_pointer) noexcept { - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) - { - case json_type::array: - return (*this).get_array().at_pointer(json_pointer); - case json_type::object: - return (*this).get_object().at_pointer(json_pointer); - default: - // a non-empty string can be invalid, or accessing a primitive (issue 2154) - if (is_pointer_well_formed(json_pointer)) { - return NO_SUCH_FIELD; - } - return INVALID_JSON_POINTER; + // 3. When a previous search found a field or an iterator yielded a value: + // + // ``` + // // When a field was not fully consumed (or not even touched at all) + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 2) + // // When a field was fully consumed + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 1) + // // When the last field was fully consumed + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 1) + // ``` + // + } else { + if ((error = skip_child() )) { abandon(); return error; } + if ((error = has_next_field().get(has_value) )) { abandon(); return error; } +#if SIMDJSON_DEVELOPMENT_CHECKS + if (_json_iter->start_position(_depth) != start_position()) { return OUT_OF_ORDER_ITERATION; } +#endif } -} + while (has_value) { + // Get the key and colon, stopping at the value. + raw_json_string actual_key; + // size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes + // Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2. + // field_key() advances the pointer and checks that '"' is found (corresponding to a key). + // The depth is left unchanged by field_key(). + if ((error = field_key().get(actual_key) )) { abandon(); return error; }; + // field_value() will advance and check that we find a ':' separating the + // key and the value. It will also increment the depth by one. + if ((error = field_value() )) { abandon(); return error; } + // If it matches, stop and return + // We could do it this way if we wanted to allow arbitrary + // key content (including escaped quotes). + //if (actual_key.unsafe_is_equal(max_key_length, key)) { + // Instead we do the following which may trigger buffer overruns if the + // user provides an adversarial key (containing a well placed unescaped quote + // character and being longer than the number of bytes remaining in the JSON + // input). + if (actual_key.unsafe_is_equal(key)) { + logger::log_event(*this, "match", key, -2); + // If we return here, then we return while pointing at the ':' that we just checked. + return true; + } -simdjson_inline simdjson_result value::at_path(std::string_view json_path) noexcept { - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) { - case json_type::array: - return (*this).get_array().at_path(json_path); - case json_type::object: - return (*this).get_object().at_path(json_path); - default: - return INVALID_JSON_POINTER; + // No match: skip the value and see if , or } is next + logger::log_event(*this, "no match", key, -2); + // The call to skip_child is meant to skip over the value corresponding to the key. + // After skip_child(), we are right before the next comma (',') or the final brace ('}'). + SIMDJSON_TRY( skip_child() ); // Skip the value entirely + // The has_next_field() advances the pointer and check that either ',' or '}' is found. + // It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found, + // then we are in error and we abort. + if ((error = has_next_field().get(has_value) )) { abandon(); return error; } } -} - -} // namespace ondemand -} // namespace westmere -} // namespace simdjson - -namespace simdjson { -simdjson_inline simdjson_result::simdjson_result( - westmere::ondemand::value &&value -) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) -{ -} -simdjson_inline simdjson_result::simdjson_result( - error_code error -) noexcept : - implementation_simdjson_result_base(error) -{ -} -simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); -} -simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); -} -simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { - if (error()) { return error(); } - return first.at(index); -} -simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); -} -simdjson_inline simdjson_result simdjson_result::end() & noexcept { - if (error()) { return error(); } - return {}; + // If the loop ended, we're out of fields to look at. + return false; } -simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) noexcept { - if (error()) { return error(); } - return first.find_field(key); -} -simdjson_inline simdjson_result simdjson_result::find_field(const char *key) noexcept { - if (error()) { return error(); } - return first.find_field(key); -} +SIMDJSON_PUSH_DISABLE_WARNINGS +SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::find_field_unordered_raw(const std::string_view key) noexcept { + /** + * When find_field_unordered_raw is called, we can either be pointing at the + * first key, pointing outside (at the closing brace) or if a key was matched + * we can be either pointing right afterthe ':' right before the value (that we need skip), + * or we may have consumed the value and we might be at a comma or at the + * final brace (ready for a call to has_next_field()). + */ + error_code error; + bool has_value; -simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); -} -simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); -} - -simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) noexcept { - if (error()) { return error(); } - return first[key]; -} -simdjson_inline simdjson_result simdjson_result::operator[](const char *key) noexcept { - if (error()) { return error(); } - return first[key]; -} - -simdjson_inline simdjson_result simdjson_result::get_array() noexcept { - if (error()) { return error(); } - return first.get_array(); -} -simdjson_inline simdjson_result simdjson_result::get_object() noexcept { - if (error()) { return error(); } - return first.get_object(); -} -simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { - if (error()) { return error(); } - return first.get_uint64(); -} -simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { - if (error()) { return error(); } - return first.get_uint64_in_string(); -} -simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { - if (error()) { return error(); } - return first.get_int64(); -} -simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { - if (error()) { return error(); } - return first.get_int64_in_string(); -} -simdjson_inline simdjson_result simdjson_result::get_double() noexcept { - if (error()) { return error(); } - return first.get_double(); -} -simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { - if (error()) { return error(); } - return first.get_double_in_string(); -} -simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(allow_replacement); -} -template -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(receiver, allow_replacement); -} -simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { - if (error()) { return error(); } - return first.get_wobbly_string(); -} -simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { - if (error()) { return error(); } - return first.get_raw_json_string(); -} -simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { - if (error()) { return error(); } - return first.get_bool(); -} -simdjson_inline simdjson_result simdjson_result::is_null() noexcept { - if (error()) { return error(); } - return first.is_null(); -} - -template simdjson_inline simdjson_result simdjson_result::get() noexcept { - if (error()) { return error(); } - return first.get(); -} -template simdjson_inline error_code simdjson_result::get(T &out) noexcept { - if (error()) { return error(); } - return first.get(out); -} - -template<> simdjson_inline simdjson_result simdjson_result::get() noexcept { - if (error()) { return error(); } - return std::move(first); -} -template<> simdjson_inline error_code simdjson_result::get(westmere::ondemand::value &out) noexcept { - if (error()) { return error(); } - out = first; - return SUCCESS; -} - -simdjson_inline simdjson_result simdjson_result::type() noexcept { - if (error()) { return error(); } - return first.type(); -} -simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { - if (error()) { return error(); } - return first.is_scalar(); -} -simdjson_inline simdjson_result simdjson_result::is_string() noexcept { - if (error()) { return error(); } - return first.is_string(); -} -simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { - if (error()) { return error(); } - return first.is_negative(); -} -simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { - if (error()) { return error(); } - return first.is_integer(); -} -simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { - if (error()) { return error(); } - return first.get_number_type(); -} -simdjson_inline simdjson_result simdjson_result::get_number() noexcept { - if (error()) { return error(); } - return first.get_number(); -} -#if SIMDJSON_EXCEPTIONS -template -simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return static_cast(first); -} -simdjson_inline simdjson_result::operator westmere::ondemand::array() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator westmere::ondemand::object() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator int64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator double() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator westmere::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator bool() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -#endif - -simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { - if (error()) { return error(); } - return first.raw_json_token(); -} - -simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { - if (error()) { return error(); } - return first.raw_json(); -} - -simdjson_inline simdjson_result simdjson_result::current_location() noexcept { - if (error()) { return error(); } - return first.current_location(); -} - -simdjson_inline simdjson_result simdjson_result::current_depth() const noexcept { - if (error()) { return error(); } - return first.current_depth(); -} - -simdjson_inline simdjson_result simdjson_result::at_pointer( - std::string_view json_pointer) noexcept { - if (error()) { - return error(); - } - return first.at_pointer(json_pointer); -} - -simdjson_inline simdjson_result simdjson_result::at_path( - std::string_view json_path) noexcept { - if (error()) { - return error(); - } - return first.at_path(json_path); -} - -} // namespace simdjson - -#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H -/* end file simdjson/generic/ondemand/value-inl.h for westmere */ -/* including simdjson/generic/ondemand/value_iterator-inl.h for westmere: #include "simdjson/generic/ondemand/value_iterator-inl.h" */ -/* begin file simdjson/generic/ondemand/value_iterator-inl.h for westmere */ -#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H - -/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ -/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/atomparsing.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/numberparsing.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string-inl.h" */ -/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -namespace simdjson { -namespace westmere { -namespace ondemand { - -simdjson_inline value_iterator::value_iterator( - json_iterator *json_iter, - depth_t depth, - token_position start_position -) noexcept : _json_iter{json_iter}, _depth{depth}, _start_position{start_position} -{ -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_object() noexcept { - SIMDJSON_TRY( start_container('{', "Not an object", "object") ); - return started_object(); -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_root_object() noexcept { - SIMDJSON_TRY( start_container('{', "Not an object", "object") ); - return started_root_object(); -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::started_object() noexcept { - assert_at_container_start(); -#if SIMDJSON_DEVELOPMENT_CHECKS - _json_iter->set_start_position(_depth, start_position()); -#endif - if (*_json_iter->peek() == '}') { - logger::log_value(*_json_iter, "empty object"); - _json_iter->return_current_and_advance(); - end_container(); - return false; - } - return true; -} - -simdjson_warn_unused simdjson_inline error_code value_iterator::check_root_object() noexcept { - // When in streaming mode, we cannot expect peek_last() to be the last structural element of the - // current document. It only works in the normal mode where we have indexed a single document. - // Note that adding a check for 'streaming' is not expensive since we only have at most - // one root element. - if ( ! _json_iter->streaming() ) { - // The following lines do not fully protect against garbage content within the - // object: e.g., `{"a":2} foo }`. Users concerned with garbage content should - // call `at_end()` on the document instance at the end of the processing to - // ensure that the processing has finished at the end. - // - if (*_json_iter->peek_last() != '}') { - _json_iter->abandon(); - return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing } at end"); - } - // If the last character is } *and* the first gibberish character is also '}' - // then on-demand could accidentally go over. So we need additional checks. - // https://github.com/simdjson/simdjson/issues/1834 - // Checking that the document is balanced requires a full scan which is potentially - // expensive, but it only happens in edge cases where the first padding character is - // a closing bracket. - if ((*_json_iter->peek(_json_iter->end_position()) == '}') && (!_json_iter->balanced())) { - _json_iter->abandon(); - // The exact error would require more work. It will typically be an unclosed object. - return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "the document is unbalanced"); - } - } - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::started_root_object() noexcept { - auto error = check_root_object(); - if(error) { return error; } - return started_object(); -} - -simdjson_warn_unused simdjson_inline error_code value_iterator::end_container() noexcept { -#if SIMDJSON_CHECK_EOF - if (depth() > 1 && at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing parent ] or }"); } - // if (depth() <= 1 && !at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing [ or { at start"); } -#endif // SIMDJSON_CHECK_EOF - _json_iter->ascend_to(depth()-1); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::has_next_field() noexcept { - assert_at_next(); - - // It's illegal to call this unless there are more tokens: anything that ends in } or ] is - // obligated to verify there are more tokens if they are not the top level. - switch (*_json_iter->return_current_and_advance()) { - case '}': - logger::log_end_value(*_json_iter, "object"); - SIMDJSON_TRY( end_container() ); - return false; - case ',': - return true; - default: - return report_error(TAPE_ERROR, "Missing comma between object fields"); - } -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::find_field_raw(const std::string_view key) noexcept { - error_code error; - bool has_value; - // - // Initially, the object can be in one of a few different places: - // - // 1. The start of the object, at the first field: - // - // ``` - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 2, index 1) - // ``` - if (at_first_field()) { - has_value = true; - - // - // 2. When a previous search did not yield a value or the object is empty: - // - // ``` - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 0) - // { } - // ^ (depth 0, index 2) - // ``` - // - } else if (!is_open()) { -#if SIMDJSON_DEVELOPMENT_CHECKS - // If we're past the end of the object, we're being iterated out of order. - // Note: this is not perfect detection. It's possible the user is inside some other object; if so, - // this object iterator will blithely scan that object for fields. - if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; } -#endif - return false; - - // 3. When a previous search found a field or an iterator yielded a value: - // - // ``` - // // When a field was not fully consumed (or not even touched at all) - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 2) - // // When a field was fully consumed - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 1) - // // When the last field was fully consumed - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 1) - // ``` - // - } else { - if ((error = skip_child() )) { abandon(); return error; } - if ((error = has_next_field().get(has_value) )) { abandon(); return error; } -#if SIMDJSON_DEVELOPMENT_CHECKS - if (_json_iter->start_position(_depth) != start_position()) { return OUT_OF_ORDER_ITERATION; } -#endif - } - while (has_value) { - // Get the key and colon, stopping at the value. - raw_json_string actual_key; - // size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes - // Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2. - // field_key() advances the pointer and checks that '"' is found (corresponding to a key). - // The depth is left unchanged by field_key(). - if ((error = field_key().get(actual_key) )) { abandon(); return error; }; - // field_value() will advance and check that we find a ':' separating the - // key and the value. It will also increment the depth by one. - if ((error = field_value() )) { abandon(); return error; } - // If it matches, stop and return - // We could do it this way if we wanted to allow arbitrary - // key content (including escaped quotes). - //if (actual_key.unsafe_is_equal(max_key_length, key)) { - // Instead we do the following which may trigger buffer overruns if the - // user provides an adversarial key (containing a well placed unescaped quote - // character and being longer than the number of bytes remaining in the JSON - // input). - if (actual_key.unsafe_is_equal(key)) { - logger::log_event(*this, "match", key, -2); - // If we return here, then we return while pointing at the ':' that we just checked. - return true; - } - - // No match: skip the value and see if , or } is next - logger::log_event(*this, "no match", key, -2); - // The call to skip_child is meant to skip over the value corresponding to the key. - // After skip_child(), we are right before the next comma (',') or the final brace ('}'). - SIMDJSON_TRY( skip_child() ); // Skip the value entirely - // The has_next_field() advances the pointer and check that either ',' or '}' is found. - // It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found, - // then we are in error and we abort. - if ((error = has_next_field().get(has_value) )) { abandon(); return error; } - } - - // If the loop ended, we're out of fields to look at. - return false; -} - -SIMDJSON_PUSH_DISABLE_WARNINGS -SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::find_field_unordered_raw(const std::string_view key) noexcept { - /** - * When find_field_unordered_raw is called, we can either be pointing at the - * first key, pointing outside (at the closing brace) or if a key was matched - * we can be either pointing right afterthe ':' right before the value (that we need skip), - * or we may have consumed the value and we might be at a comma or at the - * final brace (ready for a call to has_next_field()). - */ - error_code error; - bool has_value; - - // First, we scan from that point to the end. - // If we don't find a match, we may loop back around, and scan from the beginning to that point. - token_position search_start = _json_iter->position(); + // First, we scan from that point to the end. + // If we don't find a match, we may loop back around, and scan from the beginning to that point. + token_position search_start = _json_iter->position(); // We want to know whether we need to go back to the beginning. bool at_first = at_first_field(); @@ -96212,6 +99672,8 @@ simdjson_inline simdjson_result value_iterator::is_root_null(bool check_tr if(result) { // we have something that looks like a null. if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; } advance_root_scalar("null"); + } else if (json[0] == 'n') { + return incorrect_type_error("Not a null but starts with n"); } return result; } @@ -96503,6 +99965,8 @@ simdjson_inline simdjson_result::simdjson_re #endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H /* end file simdjson/generic/ondemand/value_iterator-inl.h for westmere */ + + /* end file simdjson/generic/ondemand/amalgamated.h for westmere */ /* including simdjson/westmere/end.h: #include "simdjson/westmere/end.h" */ /* begin file simdjson/westmere/end.h */ @@ -96703,7 +100167,13 @@ simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t } // namespace lsx } // namespace simdjson +#ifndef SIMDJSON_SWAR_NUMBER_PARSING +#if SIMDJSON_IS_BIG_ENDIAN +#define SIMDJSON_SWAR_NUMBER_PARSING 0 +#else #define SIMDJSON_SWAR_NUMBER_PARSING 1 +#endif +#endif #endif // SIMDJSON_LSX_NUMBERPARSING_DEFS_H /* end file simdjson/lsx/numberparsing_defs.h */ @@ -97180,6 +100650,132 @@ class value_iterator; #endif // SIMDJSON_GENERIC_ONDEMAND_BASE_H /* end file simdjson/generic/ondemand/base.h for lsx */ +/* including simdjson/generic/ondemand/deserialize.h for lsx: #include "simdjson/generic/ondemand/deserialize.h" */ +/* begin file simdjson/generic/ondemand/deserialize.h for lsx */ +#if SIMDJSON_SUPPORTS_DESERIALIZATION + +#ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_ONDEMAND_DESERIALIZE_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +namespace simdjson { + +namespace tag_invoke_fn_ns { +void tag_invoke(); + +struct tag_invoke_fn { + template + requires requires(Tag tag, Args &&...args) { + tag_invoke(std::forward(tag), std::forward(args)...); + } + constexpr auto operator()(Tag tag, Args &&...args) const + noexcept(noexcept(tag_invoke(std::forward(tag), + std::forward(args)...))) + -> decltype(tag_invoke(std::forward(tag), + std::forward(args)...)) { + return tag_invoke(std::forward(tag), std::forward(args)...); + } +}; +} // namespace tag_invoke_fn_ns + +inline namespace tag_invoke_ns { +inline constexpr tag_invoke_fn_ns::tag_invoke_fn tag_invoke = {}; +} // namespace tag_invoke_ns + +template +concept tag_invocable = requires(Tag tag, Args... args) { + tag_invoke(std::forward(tag), std::forward(args)...); +}; + +template +concept nothrow_tag_invocable = + tag_invocable && requires(Tag tag, Args... args) { + { + tag_invoke(std::forward(tag), std::forward(args)...) + } noexcept; + }; + +template +using tag_invoke_result = + std::invoke_result; + +template +using tag_invoke_result_t = + std::invoke_result_t; + +template using tag_t = std::decay_t; + + +struct deserialize_tag; + +/// These types are deserializable in a built-in way +template struct is_builtin_deserializable : std::false_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; + +template +concept is_builtin_deserializable_v = is_builtin_deserializable::value; + +template +concept custom_deserializable = tag_invocable; + +template +concept deserializable = custom_deserializable || is_builtin_deserializable_v; + +template +concept nothrow_custom_deserializable = nothrow_tag_invocable; + +// built-in types are noexcept and if an error happens, the value simply gets ignored and the error is returned. +template +concept nothrow_deserializable = nothrow_custom_deserializable || is_builtin_deserializable_v; + +/// Deserialize Tag +inline constexpr struct deserialize_tag { + using value_type = lsx::ondemand::value; + using document_type = lsx::ondemand::document; + using document_reference_type = lsx::ondemand::document_reference; + + // Customization Point for value + template + requires custom_deserializable + [[nodiscard]] constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for document + template + requires custom_deserializable + [[nodiscard]] constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for document reference + template + requires custom_deserializable + [[nodiscard]] constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + +} deserialize{}; + +} // namespace simdjson + +#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION + +/* end file simdjson/generic/ondemand/deserialize.h for lsx */ /* including simdjson/generic/ondemand/value_iterator.h for lsx: #include "simdjson/generic/ondemand/value_iterator.h" */ /* begin file simdjson/generic/ondemand/value_iterator.h for lsx */ #ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H @@ -97684,12 +101280,15 @@ struct simdjson_result : public lsx::implementati /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ +#include + namespace simdjson { + namespace lsx { namespace ondemand { - /** * An ephemeral JSON value returned during iteration. It is only valid for as long as you do * not access more data in the JSON document. @@ -97714,16 +101313,21 @@ class value { * @returns A value of the given type, parsed from the JSON. * @returns INCORRECT_TYPE If the JSON value is not the given type. */ - template simdjson_inline simdjson_result get() noexcept { - // Unless the simdjson library or the user provides an inline implementation, calling this method should - // immediately fail. - static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " - "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " - "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." - " You may also add support for custom types, see our documentation."); + template + simdjson_inline simdjson_result get() +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; } + /** * Get this value as the given type. * @@ -97733,7 +101337,32 @@ class value { * @returns INCORRECT_TYPE If the JSON value is not an object. * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ - template simdjson_inline error_code get(T &out) noexcept; + template + simdjson_inline error_code get(T &out) +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_DESERIALIZATION + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION + // Unless the simdjson library or the user provides an inline implementation, calling this method should + // immediately fail. + static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " + "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " + "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." + " You may also add support for custom types, see our documentation."); + static_cast(out); // to get rid of unused errors + return UNINITIALIZED; +#if SIMDJSON_SUPPORTS_DESERIALIZATION + } +#endif + } /** * Cast this JSON value to an array. @@ -97809,6 +101438,17 @@ class value { * Important: a value should be consumed once. Calling get_string() twice on the same value * is an error. * + * In some instances, you may want to allow replacement of invalid Unicode sequences. + * You may do so by passing the allow_replacement parameter as true. In the following + * example, the string "431924697b\udff0L\u0001Y" is not valid Unicode. By passing true + * to get_string, we allow the replacement of the invalid Unicode sequences with the Unicode + * replacement character (U+FFFD). + * + * simdjson::ondemand::parser parser; + * auto json = R"({"deviceId":"431924697b\udff0L\u0001Y"})"_padded; + * simdjson::ondemand::document doc = parser.iterate(json); + * auto view = doc["deviceId"].get_string(true); + * * @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next * time it parses a document or when it is destroyed. * @returns INCORRECT_TYPE if the JSON value is not a string. @@ -98060,6 +101700,7 @@ class value { simdjson_inline simdjson_result operator[](std::string_view key) noexcept; /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ simdjson_inline simdjson_result operator[](const char *key) noexcept; + simdjson_result operator[](int) noexcept = delete; /** * Get the type of this JSON value. It does not validate or consume the value. @@ -98427,6 +102068,7 @@ struct simdjson_result : public lsx::implementation_simdjs simdjson_inline simdjson_result operator[](std::string_view key) noexcept; /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ simdjson_inline simdjson_result operator[](const char *key) noexcept; + simdjson_result operator[](int) noexcept = delete; /** * Get the type of this JSON value. @@ -99482,6 +103124,22 @@ class parser { * using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the * SIMDJSON_PADDING bytes to avoid runtime warnings. * + * ### std::string references + * + * If you pass a mutable std::string reference (std::string&), the parser will seek to extend + * its capacity to SIMDJSON_PADDING bytes beyond the end of the string. + * + * Whenever you pass an std::string reference, the parser will access the bytes beyond the end of + * the string but before the end of the allocated memory (std::string::capacity()). + * If you are using a sanitizer that checks for reading uninitialized bytes or std::string's + * container-overflow checks, you may encounter sanitizer warnings. + * You can safely ignore these warnings. Or you can call simdjson::pad(std::string&) to pad the + * string with SIMDJSON_PADDING spaces: this function returns a simdjson::padding_string_view + * which can be be passed to the parser's iterate function: + * + * std::string json = R"({ "foo": 1 } { "foo": 2 } { "foo": 3 } )"; + * document doc = parser.iterate(simdjson::pad(json)); + * * @param json The JSON to parse. * @param len The length of the JSON. * @param capacity The number of bytes allocated in the JSON (must be at least len+SIMDJSON_PADDING). @@ -99676,8 +103334,12 @@ class parser { * behavior of the parser for future operations. */ bool threaded{true}; + #else + /** + * When SIMDJSON_THREADS_ENABLED is not defined, the parser instance cannot use threads. + */ + bool threaded{false}; #endif - /** * Unescape this JSON string, replacing \\ with \, \n with newline, etc. to a user-provided buffer. * The result must be valid UTF-8. @@ -99819,7 +103481,8 @@ class array { * calling this function, if successful, the array is 'rewinded' at its * beginning as if it had never been accessed. If the JSON is malformed (e.g., * there is a missing comma), then an error is returned and it is no longer - * safe to continue. + * safe to continue. Note that count_elements() does not validate the JSON values, + * only the structure of the array. * * To check that an array is empty, it is more performant to use * the is_empty() method. @@ -100097,8 +103760,11 @@ struct simdjson_result : public lsx::implementati /* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + namespace simdjson { namespace lsx { namespace ondemand { @@ -100271,24 +103937,39 @@ class document { * @returns A value of the given type, parsed from the JSON. * @returns INCORRECT_TYPE If the JSON value is not the given type. */ - template simdjson_inline simdjson_result get() & noexcept { - // Unless the simdjson library or the user provides an inline implementation, calling this method should - // immediately fail. - static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " - "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " - "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." - " You may also add support for custom types, see our documentation."); - } - /** @overload template simdjson_result get() & noexcept */ - template simdjson_deprecated simdjson_inline simdjson_result get() && noexcept { - // Unless the simdjson library or the user provides an inline implementation, calling this method should - // immediately fail. - static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " - "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " - "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." - " You may also add support for custom types, see our documentation."); + template + simdjson_inline simdjson_result get() & +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "Cannot initialize the specified type."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + /** + * @overload template simdjson_result get() & noexcept + * + * We disallow the use tag_invoke CPO on a moved document; it may create UB + * if user uses `ondemand::array` or `ondemand::object` in their custom type. + * + * The member function is still remains specialize-able for compatibility + * reasons, but we completely disallow its use when a tag_invoke customization + * is provided. + */ + template + simdjson_inline simdjson_result get() && +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(!std::is_same::value && !std::is_same::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document being alive; that would be Undefined Behaviour."); + return static_cast(*this).get(); } /** @@ -100302,7 +103983,32 @@ class document { * @returns INCORRECT_TYPE If the JSON value is not an object. * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ - template simdjson_inline error_code get(T &out) & noexcept; + template + simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_DESERIALIZATION + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION + // Unless the simdjson library or the user provides an inline implementation, calling this method should + // immediately fail. + static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " + "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " + "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." + " You may also add support for custom types, see our documentation."); + static_cast(out); // to get rid of unused errors + return UNINITIALIZED; +#if SIMDJSON_SUPPORTS_DESERIALIZATION + } +#endif + } /** @overload template error_code get(T &out) & noexcept */ template simdjson_deprecated simdjson_inline error_code get(T &out) && noexcept; @@ -100404,7 +104110,8 @@ class document { * calling this function, if successful, the array is 'rewinded' at its * beginning as if it had never been accessed. If the JSON is malformed (e.g., * there is a missing comma), then an error is returned and it is no longer - * safe to continue. + * safe to continue. Note that count_elements() does not validate the JSON values, + * only the structure of the array. */ simdjson_inline simdjson_result count_elements() & noexcept; /** @@ -100516,6 +104223,7 @@ class document { simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; /** * Get the type of this JSON value. It does not validate or consume the value. @@ -100784,6 +104492,11 @@ class document { /** * A document_reference is a thin wrapper around a document reference instance. + * The document_reference instances are used primarily/solely for streams of JSON + * documents. They differ from document instances when parsing a scalar value + * (a document that is not an array or an object). In the case of a document, + * we expect the document to be fully consumed. In the case of a document_reference, + * we allow trailing content. */ class document_reference { public: @@ -100809,7 +104522,70 @@ class document_reference { simdjson_inline simdjson_result get_value() noexcept; simdjson_inline simdjson_result is_null() noexcept; - template simdjson_inline simdjson_result get() & noexcept; + template + simdjson_inline simdjson_result get() & +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "Cannot initialize the specified type."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + template + simdjson_inline simdjson_result get() && +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(!std::is_same::value && !std::is_same::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document_reference being alive; that would be Undefined Behaviour."); + return static_cast(*this).get(); + } + + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool, value + * + * Be mindful that the document instance must remain in scope while you are accessing object, array and value instances. + * + * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. + * @returns INCORRECT_TYPE If the JSON value is not an object. + * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. + */ + template + simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_DESERIALIZATION + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION + // Unless the simdjson library or the user provides an inline implementation, calling this method should + // immediately fail. + static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " + "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " + "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." + " You may also add support for custom types, see our documentation."); + static_cast(out); // to get rid of unused errors + return UNINITIALIZED; +#if SIMDJSON_SUPPORTS_DESERIALIZATION + } +#endif + } + /** @overload template error_code get(T &out) & noexcept */ + template simdjson_inline error_code get(T &out) && noexcept; simdjson_inline simdjson_result raw_json() noexcept; simdjson_inline operator document&() const noexcept; #if SIMDJSON_EXCEPTIONS @@ -100834,6 +104610,7 @@ class document_reference { simdjson_inline simdjson_result find_field(const char *key) & noexcept; simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; @@ -100912,6 +104689,7 @@ struct simdjson_result : public lsx::implementation_sim simdjson_inline simdjson_result find_field(const char *key) & noexcept; simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; simdjson_inline simdjson_result type() noexcept; @@ -100961,8 +104739,14 @@ struct simdjson_result : public lsx::implemen simdjson_inline simdjson_result get_bool() noexcept; simdjson_inline simdjson_result get_value() noexcept; simdjson_inline simdjson_result is_null() noexcept; + + template simdjson_inline simdjson_result get() & noexcept; + template simdjson_inline simdjson_result get() && noexcept; + + template simdjson_inline error_code get(T &out) & noexcept; + template simdjson_inline error_code get(T &out) && noexcept; #if SIMDJSON_EXCEPTIONS - template ::value == false>::type> + template explicit simdjson_inline operator T() noexcept(false); simdjson_inline operator lsx::ondemand::array() & noexcept(false); simdjson_inline operator lsx::ondemand::object() & noexcept(false); @@ -100983,6 +104767,7 @@ struct simdjson_result : public lsx::implemen simdjson_inline simdjson_result find_field(const char *key) & noexcept; simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; simdjson_inline simdjson_result type() noexcept; @@ -101913,6 +105698,177 @@ inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result +#include + +namespace simdjson { +template +constexpr bool require_custom_serialization = false; + +////////////////////////////// +// Number deserialization +////////////////////////////// + +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + using limits = std::numeric_limits; + + uint64_t x; + SIMDJSON_TRY(val.get_uint64().get(x)); + if (x > (limits::max)()) { + return NUMBER_OUT_OF_RANGE; + } + out = static_cast(x); + return SUCCESS; +} + +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + double x; + SIMDJSON_TRY(val.get_double().get(x)); + out = static_cast(x); + return SUCCESS; +} + +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + using limits = std::numeric_limits; + + int64_t x; + SIMDJSON_TRY(val.get_int64().get(x)); + if (x > (limits::max)() || x < (limits::min)()) { + return NUMBER_OUT_OF_RANGE; + } + out = static_cast(x); + return SUCCESS; +} + +/** + * STL containers have several constructors including one that takes a single + * size argument. Thus, some compilers (Visual Studio) will not be able to + * disambiguate between the size and container constructor. Users should + * explicitly specify the type of the container as needed: e.g., + * doc.get>(). + */ +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { + using value_type = typename std::remove_cvref_t::value_type; + static_assert( + deserializable, + "The specified type inside the container must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the container must default constructible."); + + lsx::ondemand::array arr; + SIMDJSON_TRY(val.get_array().get(arr)); + for (auto v : arr) { + if constexpr (concepts::returns_reference) { + if (auto const err = v.get().get(concepts::emplace_one(out)); + err) { + // If an error occurs, the empty element that we just inserted gets + // removed. We're not using a temp variable because if T is a heavy + // type, we want the valid path to be the fast path and the slow path be + // the path that has errors in it. + if constexpr (requires { out.pop_back(); }) { + static_cast(out.pop_back()); + } + return err; + } + } else { + value_type temp; + if (auto const err = v.get().get(temp); err) { + return err; + } + concepts::emplace_one(out, std::move(temp)); + } + } + return SUCCESS; +} + + + +/** + * This CPO (Customization Point Object) will help deserialize into + * smart pointers. + * + * If constructing T is nothrow, this conversion should be nothrow as well since + * we return MEMALLOC if we're not able to allocate memory instead of throwing + * the error message. + * + * @tparam T The type inside the smart pointer + * @tparam ValT document/value type + * @param val document/value + * @param out a reference to the smart pointer + * @return status of the conversion + */ +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::element_type, ValT>) { + using element_type = typename std::remove_cvref_t::element_type; + + // For better error messages, don't use these as constraints on + // the tag_invoke CPO. + static_assert( + deserializable, + "The specified type inside the unique_ptr must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the unique_ptr must default constructible."); + + auto ptr = new (std::nothrow) element_type(); + if (ptr == nullptr) { + return MEMALLOC; + } + SIMDJSON_TRY(val.template get(*ptr)); + out.reset(ptr); + return SUCCESS; +} + +/** + * This CPO (Customization Point Object) will help deserialize into optional types. + */ +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::value_type, ValT>) { + using value_type = typename std::remove_cvref_t::value_type; + + static_assert( + deserializable, + "The specified type inside the unique_ptr must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the unique_ptr must default constructible."); + + if (!out) { + out.emplace(); + } + SIMDJSON_TRY(val.template get(out.value())); + return SUCCESS; +} + +} // namespace simdjson + +#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +/* end file simdjson/generic/ondemand/std_deserialize.h for lsx */ + // Inline definitions /* including simdjson/generic/ondemand/array-inl.h for lsx: #include "simdjson/generic/ondemand/array-inl.h" */ /* begin file simdjson/generic/ondemand/array-inl.h for lsx */ @@ -101920,6 +105876,7 @@ inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result array::at_pointer(std::string_view json_pointer) n return child; } -inline std::string json_path_to_pointer_conversion(std::string_view json_path) { - if (json_path.empty() || (json_path.front() != '.' && - json_path.front() != '[')) { - return "-1"; // This is just a sentinel value, the caller should check for this and return an error. - } - - std::string result; - // Reserve space to reduce allocations, adjusting for potential increases due - // to escaping. - result.reserve(json_path.size() * 2); - - size_t i = 0; - - while (i < json_path.length()) { - if (json_path[i] == '.') { - result += '/'; - } else if (json_path[i] == '[') { - result += '/'; - ++i; // Move past the '[' - while (i < json_path.length() && json_path[i] != ']') { - if (json_path[i] == '~') { - result += "~0"; - } else if (json_path[i] == '/') { - result += "~1"; - } else { - result += json_path[i]; - } - ++i; - } - if (i == json_path.length() || json_path[i] != ']') { - return "-1"; // Using sentinel value that will be handled as an error by the caller. - } - } else { - if (json_path[i] == '~') { - result += "~0"; - } else if (json_path[i] == '/') { - result += "~1"; - } else { - result += json_path[i]; - } - } - ++i; - } - - return result; -} - inline simdjson_result array::at_path(std::string_view json_path) noexcept { auto json_pointer = json_path_to_pointer_conversion(json_path); if (json_pointer == "-1") { return INVALID_JSON_POINTER; } @@ -102281,313 +106191,276 @@ simdjson_inline simdjson_result &simdjson_result< #endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H /* end file simdjson/generic/ondemand/array_iterator-inl.h for lsx */ -/* including simdjson/generic/ondemand/document-inl.h for lsx: #include "simdjson/generic/ondemand/document-inl.h" */ -/* begin file simdjson/generic/ondemand/document-inl.h for lsx */ -#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H +/* including simdjson/generic/ondemand/value-inl.h for lsx: #include "simdjson/generic/ondemand/value-inl.h" */ +/* begin file simdjson/generic/ondemand/value-inl.h for lsx */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ -/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { namespace lsx { namespace ondemand { -simdjson_inline document::document(ondemand::json_iterator &&_iter) noexcept - : iter{std::forward(_iter)} +simdjson_inline value::value(const value_iterator &_iter) noexcept + : iter{_iter} { - logger::log_start_value(iter, "document"); } - -simdjson_inline document document::start(json_iterator &&iter) noexcept { - return document(std::forward(iter)); +simdjson_inline value value::start(const value_iterator &iter) noexcept { + return iter; } - -inline void document::rewind() noexcept { - iter.rewind(); +simdjson_inline value value::resume(const value_iterator &iter) noexcept { + return iter; } -inline std::string document::to_debug_string() noexcept { - return iter.to_string(); +simdjson_inline simdjson_result value::get_array() noexcept { + return array::start(iter); } - -inline simdjson_result document::current_location() const noexcept { - return iter.current_location(); +simdjson_inline simdjson_result value::get_object() noexcept { + return object::start(iter); } - -inline int32_t document::current_depth() const noexcept { - return iter.depth(); +simdjson_inline simdjson_result value::start_or_resume_object() noexcept { + if (iter.at_start()) { + return get_object(); + } else { + return object::resume(iter); + } } -inline bool document::at_end() const noexcept { - return iter.at_end(); +simdjson_inline simdjson_result value::get_raw_json_string() noexcept { + return iter.get_raw_json_string(); } - - -inline bool document::is_alive() noexcept { - return iter.is_alive(); +simdjson_inline simdjson_result value::get_string(bool allow_replacement) noexcept { + return iter.get_string(allow_replacement); } -simdjson_inline value_iterator document::resume_value_iterator() noexcept { - return value_iterator(&iter, 1, iter.root_position()); +template +simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept { + return iter.get_string(receiver, allow_replacement); } -simdjson_inline value_iterator document::get_root_value_iterator() noexcept { - return resume_value_iterator(); +simdjson_inline simdjson_result value::get_wobbly_string() noexcept { + return iter.get_wobbly_string(); } -simdjson_inline simdjson_result document::start_or_resume_object() noexcept { - if (iter.at_root()) { - return get_object(); - } else { - return object::resume(resume_value_iterator()); - } +simdjson_inline simdjson_result value::get_double() noexcept { + return iter.get_double(); } -simdjson_inline simdjson_result document::get_value() noexcept { - // Make sure we start any arrays or objects before returning, so that start_root_() - // gets called. - - // It is the convention throughout the code that the macro `SIMDJSON_DEVELOPMENT_CHECKS` determines whether - // we check for OUT_OF_ORDER_ITERATION. Proper on::demand code should never trigger this error. -#if SIMDJSON_DEVELOPMENT_CHECKS - if (!iter.at_root()) { return OUT_OF_ORDER_ITERATION; } -#endif - // assert_at_root() serves two purposes: in Debug mode, whether or not - // SIMDJSON_DEVELOPMENT_CHECKS is set or not, it checks that we are at the root of - // the document (this will typically be redundant). In release mode, it generates - // SIMDJSON_ASSUME statements to allow the compiler to make assumptions. - iter.assert_at_root(); - switch (*iter.peek()) { - case '[': { - // The following lines check that the document ends with ]. - auto value_iterator = get_root_value_iterator(); - auto error = value_iterator.check_root_array(); - if(error) { return error; } - return value(get_root_value_iterator()); - } - case '{': { - // The following lines would check that the document ends with }. - auto value_iterator = get_root_value_iterator(); - auto error = value_iterator.check_root_object(); - if(error) { return error; } - return value(get_root_value_iterator()); - } - default: - // Unfortunately, scalar documents are a special case in simdjson and they cannot - // be safely converted to value instances. - return SCALAR_DOCUMENT_AS_VALUE; - } +simdjson_inline simdjson_result value::get_double_in_string() noexcept { + return iter.get_double_in_string(); } -simdjson_inline simdjson_result document::get_array() & noexcept { - auto value = get_root_value_iterator(); - return array::start_root(value); +simdjson_inline simdjson_result value::get_uint64() noexcept { + return iter.get_uint64(); } -simdjson_inline simdjson_result document::get_object() & noexcept { - auto value = get_root_value_iterator(); - return object::start_root(value); +simdjson_inline simdjson_result value::get_uint64_in_string() noexcept { + return iter.get_uint64_in_string(); } - -/** - * We decided that calling 'get_double()' on the JSON document '1.233 blabla' should - * give an error, so we check for trailing content. We want to disallow trailing - * content. - * Thus, in several implementations below, we pass a 'true' parameter value to - * a get_root_value_iterator() method: this indicates that we disallow trailing content. - */ - -simdjson_inline simdjson_result document::get_uint64() noexcept { - return get_root_value_iterator().get_root_uint64(true); +simdjson_inline simdjson_result value::get_int64() noexcept { + return iter.get_int64(); } -simdjson_inline simdjson_result document::get_uint64_in_string() noexcept { - return get_root_value_iterator().get_root_uint64_in_string(true); +simdjson_inline simdjson_result value::get_int64_in_string() noexcept { + return iter.get_int64_in_string(); } -simdjson_inline simdjson_result document::get_int64() noexcept { - return get_root_value_iterator().get_root_int64(true); +simdjson_inline simdjson_result value::get_bool() noexcept { + return iter.get_bool(); } -simdjson_inline simdjson_result document::get_int64_in_string() noexcept { - return get_root_value_iterator().get_root_int64_in_string(true); +simdjson_inline simdjson_result value::is_null() noexcept { + return iter.is_null(); } -simdjson_inline simdjson_result document::get_double() noexcept { - return get_root_value_iterator().get_root_double(true); + +template<> simdjson_inline simdjson_result value::get() noexcept { return get_array(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_object(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_number(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_double(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_bool(); } + + +template<> simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); } +template<> simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); } +template<> simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); } +template<> simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); } +template<> simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); } +template<> simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); } +template<> simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); } +template<> simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); } +template<> simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); } + +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline value::operator T() noexcept(false) { + return get(); } -simdjson_inline simdjson_result document::get_double_in_string() noexcept { - return get_root_value_iterator().get_root_double_in_string(true); +simdjson_inline value::operator array() noexcept(false) { + return get_array(); } -simdjson_inline simdjson_result document::get_string(bool allow_replacement) noexcept { - return get_root_value_iterator().get_root_string(true, allow_replacement); +simdjson_inline value::operator object() noexcept(false) { + return get_object(); } -template -simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept { - return get_root_value_iterator().get_root_string(receiver, true, allow_replacement); +simdjson_inline value::operator uint64_t() noexcept(false) { + return get_uint64(); } -simdjson_inline simdjson_result document::get_wobbly_string() noexcept { - return get_root_value_iterator().get_root_wobbly_string(true); +simdjson_inline value::operator int64_t() noexcept(false) { + return get_int64(); } -simdjson_inline simdjson_result document::get_raw_json_string() noexcept { - return get_root_value_iterator().get_root_raw_json_string(true); +simdjson_inline value::operator double() noexcept(false) { + return get_double(); } -simdjson_inline simdjson_result document::get_bool() noexcept { - return get_root_value_iterator().get_root_bool(true); +simdjson_inline value::operator std::string_view() noexcept(false) { + return get_string(false); } -simdjson_inline simdjson_result document::is_null() noexcept { - return get_root_value_iterator().is_root_null(true); +simdjson_inline value::operator raw_json_string() noexcept(false) { + return get_raw_json_string(); } +simdjson_inline value::operator bool() noexcept(false) { + return get_bool(); +} +#endif -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_array(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_object(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_raw_json_string(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_string(false); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_double(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_uint64(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_int64(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_value(); } - -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_raw_json_string(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_value(); } - -template simdjson_inline error_code document::get(T &out) & noexcept { - return get().get(out); +simdjson_inline simdjson_result value::begin() & noexcept { + return get_array().begin(); } -template simdjson_deprecated simdjson_inline error_code document::get(T &out) && noexcept { - return std::forward(*this).get().get(out); +simdjson_inline simdjson_result value::end() & noexcept { + return {}; } - -#if SIMDJSON_EXCEPTIONS -template -simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get(); } -template -simdjson_inline document::operator T() & noexcept(false) { return get(); } -simdjson_inline document::operator array() & noexcept(false) { return get_array(); } -simdjson_inline document::operator object() & noexcept(false) { return get_object(); } -simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); } -simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); } -simdjson_inline document::operator double() noexcept(false) { return get_double(); } -simdjson_inline document::operator std::string_view() noexcept(false) { return get_string(false); } -simdjson_inline document::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } -simdjson_inline document::operator bool() noexcept(false) { return get_bool(); } -simdjson_inline document::operator value() noexcept(false) { return get_value(); } - -#endif -simdjson_inline simdjson_result document::count_elements() & noexcept { +simdjson_inline simdjson_result value::count_elements() & noexcept { + simdjson_result answer; auto a = get_array(); - simdjson_result answer = a.count_elements(); - /* If there was an array, we are now left pointing at its first element. */ - if(answer.error() == SUCCESS) { rewind(); } + answer = a.count_elements(); + // count_elements leaves you pointing inside the array, at the first element. + // We need to move back so that the user can create a new array (which requires that + // we point at '['). + iter.move_at_start(); return answer; } -simdjson_inline simdjson_result document::count_fields() & noexcept { +simdjson_inline simdjson_result value::count_fields() & noexcept { + simdjson_result answer; auto a = get_object(); - simdjson_result answer = a.count_fields(); - /* If there was an object, we are now left pointing at its first element. */ - if(answer.error() == SUCCESS) { rewind(); } + answer = a.count_fields(); + iter.move_at_start(); return answer; } -simdjson_inline simdjson_result document::at(size_t index) & noexcept { +simdjson_inline simdjson_result value::at(size_t index) noexcept { auto a = get_array(); return a.at(index); } -simdjson_inline simdjson_result document::begin() & noexcept { - return get_array().begin(); -} -simdjson_inline simdjson_result document::end() & noexcept { - return {}; -} -simdjson_inline simdjson_result document::find_field(std::string_view key) & noexcept { +simdjson_inline simdjson_result value::find_field(std::string_view key) noexcept { return start_or_resume_object().find_field(key); } -simdjson_inline simdjson_result document::find_field(const char *key) & noexcept { +simdjson_inline simdjson_result value::find_field(const char *key) noexcept { return start_or_resume_object().find_field(key); } -simdjson_inline simdjson_result document::find_field_unordered(std::string_view key) & noexcept { + +simdjson_inline simdjson_result value::find_field_unordered(std::string_view key) noexcept { return start_or_resume_object().find_field_unordered(key); } -simdjson_inline simdjson_result document::find_field_unordered(const char *key) & noexcept { +simdjson_inline simdjson_result value::find_field_unordered(const char *key) noexcept { return start_or_resume_object().find_field_unordered(key); } -simdjson_inline simdjson_result document::operator[](std::string_view key) & noexcept { + +simdjson_inline simdjson_result value::operator[](std::string_view key) noexcept { return start_or_resume_object()[key]; } -simdjson_inline simdjson_result document::operator[](const char *key) & noexcept { +simdjson_inline simdjson_result value::operator[](const char *key) noexcept { return start_or_resume_object()[key]; } -simdjson_inline error_code document::consume() noexcept { - auto error = iter.skip_child(0); - if(error) { iter.abandon(); } - return error; -} - -simdjson_inline simdjson_result document::raw_json() noexcept { - auto _iter = get_root_value_iterator(); - const uint8_t * starting_point{_iter.peek_start()}; - auto error = consume(); - if(error) { return error; } - // After 'consume()', we could be left pointing just beyond the document, but that - // is ok because we are not going to dereference the final pointer position, we just - // use it to compute the length in bytes. - const uint8_t * final_point{iter.unsafe_pointer()}; - return std::string_view(reinterpret_cast(starting_point), size_t(final_point - starting_point)); -} - -simdjson_inline simdjson_result document::type() noexcept { - return get_root_value_iterator().type(); +simdjson_inline simdjson_result value::type() noexcept { + return iter.type(); } -simdjson_inline simdjson_result document::is_scalar() noexcept { +simdjson_inline simdjson_result value::is_scalar() noexcept { json_type this_type; auto error = type().get(this_type); if(error) { return error; } return ! ((this_type == json_type::array) || (this_type == json_type::object)); } -simdjson_inline simdjson_result document::is_string() noexcept { +simdjson_inline simdjson_result value::is_string() noexcept { json_type this_type; auto error = type().get(this_type); if(error) { return error; } return (this_type == json_type::string); } -simdjson_inline bool document::is_negative() noexcept { - return get_root_value_iterator().is_root_negative(); + +simdjson_inline bool value::is_negative() noexcept { + return iter.is_negative(); } -simdjson_inline simdjson_result document::is_integer() noexcept { - return get_root_value_iterator().is_root_integer(true); +simdjson_inline simdjson_result value::is_integer() noexcept { + return iter.is_integer(); +} +simdjson_warn_unused simdjson_inline simdjson_result value::get_number_type() noexcept { + return iter.get_number_type(); +} +simdjson_warn_unused simdjson_inline simdjson_result value::get_number() noexcept { + return iter.get_number(); } -simdjson_inline simdjson_result document::get_number_type() noexcept { - return get_root_value_iterator().get_root_number_type(true); +simdjson_inline std::string_view value::raw_json_token() noexcept { + return std::string_view(reinterpret_cast(iter.peek_start()), iter.peek_start_length()); } -simdjson_inline simdjson_result document::get_number() noexcept { - return get_root_value_iterator().get_root_number(true); +simdjson_inline simdjson_result value::raw_json() noexcept { + json_type t; + SIMDJSON_TRY(type().get(t)); + switch (t) + { + case json_type::array: { + ondemand::array array; + SIMDJSON_TRY(get_array().get(array)); + return array.raw_json(); + } + case json_type::object: { + ondemand::object object; + SIMDJSON_TRY(get_object().get(object)); + return object.raw_json(); + } + default: + return raw_json_token(); + } } +simdjson_inline simdjson_result value::current_location() noexcept { + return iter.json_iter().current_location(); +} -simdjson_inline simdjson_result document::raw_json_token() noexcept { - auto _iter = get_root_value_iterator(); - return std::string_view(reinterpret_cast(_iter.peek_start()), _iter.peek_root_length()); +simdjson_inline int32_t value::current_depth() const noexcept{ + return iter.json_iter().depth(); } -simdjson_inline simdjson_result document::at_pointer(std::string_view json_pointer) noexcept { - rewind(); // Rewind the document each time at_pointer is called - if (json_pointer.empty()) { - return this->get_value(); +inline bool is_pointer_well_formed(std::string_view json_pointer) noexcept { + if (simdjson_unlikely(json_pointer.empty())) { // can't be + return false; + } + if (simdjson_unlikely(json_pointer[0] != '/')) { + return false; + } + size_t escape = json_pointer.find('~'); + if (escape == std::string_view::npos) { + return true; + } + if (escape == json_pointer.size() - 1) { + return false; } + if (json_pointer[escape + 1] != '0' && json_pointer[escape + 1] != '1') { + return false; + } + return true; +} + +simdjson_inline simdjson_result value::at_pointer(std::string_view json_pointer) noexcept { json_type t; SIMDJSON_TRY(type().get(t)); switch (t) @@ -102597,15 +106470,15 @@ simdjson_inline simdjson_result document::at_pointer(std::string_view jso case json_type::object: return (*this).get_object().at_pointer(json_pointer); default: + // a non-empty string can be invalid, or accessing a primitive (issue 2154) + if (is_pointer_well_formed(json_pointer)) { + return NO_SUCH_FIELD; + } return INVALID_JSON_POINTER; } } -simdjson_inline simdjson_result document::at_path(std::string_view json_path) noexcept { - rewind(); // Rewind the document each time at_pointer is called - if (json_path.empty()) { - return this->get_value(); - } +simdjson_inline simdjson_result value::at_path(std::string_view json_path) noexcept { json_type t; SIMDJSON_TRY(type().get(t)); switch (t) { @@ -102624,680 +106497,1319 @@ simdjson_inline simdjson_result document::at_path(std::string_view json_p namespace simdjson { -simdjson_inline simdjson_result::simdjson_result( - lsx::ondemand::document &&value +simdjson_inline simdjson_result::simdjson_result( + lsx::ondemand::value &&value ) noexcept : - implementation_simdjson_result_base( - std::forward(value) + implementation_simdjson_result_base( + std::forward(value) ) { } -simdjson_inline simdjson_result::simdjson_result( +simdjson_inline simdjson_result::simdjson_result( error_code error ) noexcept : - implementation_simdjson_result_base( - error - ) + implementation_simdjson_result_base(error) { } -simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { if (error()) { return error(); } return first.count_elements(); } -simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { if (error()) { return error(); } return first.count_fields(); } -simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { +simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { if (error()) { return error(); } return first.at(index); } -simdjson_inline error_code simdjson_result::rewind() noexcept { - if (error()) { return error(); } - first.rewind(); - return SUCCESS; -} -simdjson_inline simdjson_result simdjson_result::begin() & noexcept { +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { if (error()) { return error(); } return first.begin(); } -simdjson_inline simdjson_result simdjson_result::end() & noexcept { +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + if (error()) { return error(); } return {}; } -simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) noexcept { if (error()) { return error(); } - return first.find_field_unordered(key); + return first.find_field(key); } -simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) noexcept { if (error()) { return error(); } - return first.find_field_unordered(key); + return first.find_field(key); } -simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) noexcept { if (error()) { return error(); } - return first[key]; + return first.find_field_unordered(key); } -simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) noexcept { if (error()) { return error(); } - return first[key]; + return first.find_field_unordered(key); } -simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) noexcept { if (error()) { return error(); } - return first.find_field(key); + return first[key]; } -simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) noexcept { if (error()) { return error(); } - return first.find_field(key); + return first[key]; } -simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { + +simdjson_inline simdjson_result simdjson_result::get_array() noexcept { if (error()) { return error(); } return first.get_array(); } -simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { +simdjson_inline simdjson_result simdjson_result::get_object() noexcept { if (error()) { return error(); } return first.get_object(); } -simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { if (error()) { return error(); } return first.get_uint64(); } -simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { if (error()) { return error(); } return first.get_uint64_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { if (error()) { return error(); } return first.get_int64(); } -simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { if (error()) { return error(); } return first.get_int64_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_double() noexcept { +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { if (error()) { return error(); } return first.get_double(); } -simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { if (error()) { return error(); } return first.get_double_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(allow_replacement); } template -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(receiver, allow_replacement); } -simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { if (error()) { return error(); } return first.get_wobbly_string(); } -simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { if (error()) { return error(); } return first.get_raw_json_string(); } -simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { if (error()) { return error(); } return first.get_bool(); } -simdjson_inline simdjson_result simdjson_result::get_value() noexcept { - if (error()) { return error(); } - return first.get_value(); -} -simdjson_inline simdjson_result simdjson_result::is_null() noexcept { +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { if (error()) { return error(); } return first.is_null(); } -template -simdjson_inline simdjson_result simdjson_result::get() & noexcept { +template<> simdjson_inline error_code simdjson_result::get(lsx::ondemand::value &out) noexcept { if (error()) { return error(); } - return first.get(); + out = first; + return SUCCESS; } -template -simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + +template simdjson_inline simdjson_result simdjson_result::get() noexcept { if (error()) { return error(); } - return std::forward(first).get(); + return first.get(); } -template -simdjson_inline error_code simdjson_result::get(T &out) & noexcept { +template simdjson_inline error_code simdjson_result::get(T &out) noexcept { if (error()) { return error(); } return first.get(out); } -template -simdjson_inline error_code simdjson_result::get(T &out) && noexcept { - if (error()) { return error(); } - return std::forward(first).get(out); -} -template<> simdjson_inline simdjson_result simdjson_result::get() & noexcept = delete; -template<> simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { - if (error()) { return error(); } - return std::forward(first); -} -template<> simdjson_inline error_code simdjson_result::get(lsx::ondemand::document &out) & noexcept = delete; -template<> simdjson_inline error_code simdjson_result::get(lsx::ondemand::document &out) && noexcept { +template<> simdjson_inline simdjson_result simdjson_result::get() noexcept { if (error()) { return error(); } - out = std::forward(first); - return SUCCESS; + return std::move(first); } -simdjson_inline simdjson_result simdjson_result::type() noexcept { +simdjson_inline simdjson_result simdjson_result::type() noexcept { if (error()) { return error(); } return first.type(); } - -simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { if (error()) { return error(); } return first.is_scalar(); } - -simdjson_inline simdjson_result simdjson_result::is_string() noexcept { +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { if (error()) { return error(); } return first.is_string(); } - -simdjson_inline bool simdjson_result::is_negative() noexcept { +simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { if (error()) { return error(); } return first.is_negative(); } - -simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { if (error()) { return error(); } return first.is_integer(); } - -simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { if (error()) { return error(); } return first.get_number_type(); } - -simdjson_inline simdjson_result simdjson_result::get_number() noexcept { +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { if (error()) { return error(); } return first.get_number(); } - - #if SIMDJSON_EXCEPTIONS -template ::value == false>::type> -simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator lsx::ondemand::array() & noexcept(false) { +template +simdjson_inline simdjson_result::operator T() noexcept(false) { if (error()) { throw simdjson_error(error()); } - return first; + return first.get(); } -simdjson_inline simdjson_result::operator lsx::ondemand::object() & noexcept(false) { +simdjson_inline simdjson_result::operator lsx::ondemand::array() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { +simdjson_inline simdjson_result::operator lsx::ondemand::object() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator int64_t() noexcept(false) { +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator double() noexcept(false) { +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { +simdjson_inline simdjson_result::operator double() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator lsx::ondemand::raw_json_string() noexcept(false) { +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator bool() noexcept(false) { +simdjson_inline simdjson_result::operator lsx::ondemand::raw_json_string() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator lsx::ondemand::value() noexcept(false) { +simdjson_inline simdjson_result::operator bool() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } #endif - -simdjson_inline simdjson_result simdjson_result::current_location() noexcept { +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { if (error()) { return error(); } - return first.current_location(); + return first.raw_json_token(); } -simdjson_inline bool simdjson_result::at_end() const noexcept { +simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { if (error()) { return error(); } - return first.at_end(); + return first.raw_json(); } - -simdjson_inline int32_t simdjson_result::current_depth() const noexcept { +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { if (error()) { return error(); } - return first.current_depth(); + return first.current_location(); } -simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { +simdjson_inline simdjson_result simdjson_result::current_depth() const noexcept { if (error()) { return error(); } - return first.raw_json_token(); + return first.current_depth(); } -simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } +simdjson_inline simdjson_result simdjson_result::at_pointer( + std::string_view json_pointer) noexcept { + if (error()) { + return error(); + } return first.at_pointer(json_pointer); } -simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { return error(); } +simdjson_inline simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } return first.at_path(json_path); } } // namespace simdjson +#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H +/* end file simdjson/generic/ondemand/value-inl.h for lsx */ +/* including simdjson/generic/ondemand/document-inl.h for lsx: #include "simdjson/generic/ondemand/document-inl.h" */ +/* begin file simdjson/generic/ondemand/document-inl.h for lsx */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { namespace lsx { namespace ondemand { -simdjson_inline document_reference::document_reference() noexcept : doc{nullptr} {} -simdjson_inline document_reference::document_reference(document &d) noexcept : doc(&d) {} -simdjson_inline void document_reference::rewind() noexcept { doc->rewind(); } -simdjson_inline simdjson_result document_reference::get_array() & noexcept { return doc->get_array(); } -simdjson_inline simdjson_result document_reference::get_object() & noexcept { return doc->get_object(); } -/** - * The document_reference instances are used primarily/solely for streams of JSON - * documents. - * We decided that calling 'get_double()' on the JSON document '1.233 blabla' should - * give an error, so we check for trailing content. - * - * However, for streams of JSON documents, we want to be able to start from - * "321" "321" "321" - * and parse it successfully as a stream of JSON documents, calling get_uint64_in_string() - * successfully each time. - * - * To achieve this result, we pass a 'false' to a get_root_value_iterator() method: - * this indicates that we allow trailing content. - */ -simdjson_inline simdjson_result document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); } -simdjson_inline simdjson_result document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); } -simdjson_inline simdjson_result document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); } -simdjson_inline simdjson_result document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); } -simdjson_inline simdjson_result document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); } -simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } -simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } -template -simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); } -simdjson_inline simdjson_result document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } -simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } -simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } -simdjson_inline simdjson_result document_reference::get_value() noexcept { return doc->get_value(); } -simdjson_inline simdjson_result document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_array(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_object(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_raw_json_string(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_string(false); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_double(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_uint64(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_int64(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_bool(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_value(); } -#if SIMDJSON_EXCEPTIONS -template -simdjson_inline document_reference::operator T() noexcept(false) { return get(); } -simdjson_inline document_reference::operator array() & noexcept(false) { return array(*doc); } -simdjson_inline document_reference::operator object() & noexcept(false) { return object(*doc); } -simdjson_inline document_reference::operator uint64_t() noexcept(false) { return get_uint64(); } -simdjson_inline document_reference::operator int64_t() noexcept(false) { return get_int64(); } -simdjson_inline document_reference::operator double() noexcept(false) { return get_double(); } -simdjson_inline document_reference::operator std::string_view() noexcept(false) { return std::string_view(*doc); } -simdjson_inline document_reference::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } -simdjson_inline document_reference::operator bool() noexcept(false) { return get_bool(); } -simdjson_inline document_reference::operator value() noexcept(false) { return value(*doc); } -#endif -simdjson_inline simdjson_result document_reference::count_elements() & noexcept { return doc->count_elements(); } -simdjson_inline simdjson_result document_reference::count_fields() & noexcept { return doc->count_fields(); } -simdjson_inline simdjson_result document_reference::at(size_t index) & noexcept { return doc->at(index); } -simdjson_inline simdjson_result document_reference::begin() & noexcept { return doc->begin(); } -simdjson_inline simdjson_result document_reference::end() & noexcept { return doc->end(); } -simdjson_inline simdjson_result document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); } -simdjson_inline simdjson_result document_reference::find_field(const char *key) & noexcept { return doc->find_field(key); } -simdjson_inline simdjson_result document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; } -simdjson_inline simdjson_result document_reference::operator[](const char *key) & noexcept { return (*doc)[key]; } -simdjson_inline simdjson_result document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); } -simdjson_inline simdjson_result document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); } -simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } -simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } -simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } -simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } -simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } -simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } -simdjson_inline simdjson_result document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); } -simdjson_inline simdjson_result document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); } -simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } -simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } -simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } -simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } -simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} -simdjson_inline document_reference::operator document&() const noexcept { return *doc; } +simdjson_inline document::document(ondemand::json_iterator &&_iter) noexcept + : iter{std::forward(_iter)} +{ + logger::log_start_value(iter, "document"); +} -} // namespace ondemand -} // namespace lsx -} // namespace simdjson +simdjson_inline document document::start(json_iterator &&iter) noexcept { + return document(std::forward(iter)); +} +inline void document::rewind() noexcept { + iter.rewind(); +} +inline std::string document::to_debug_string() noexcept { + return iter.to_string(); +} -namespace simdjson { -simdjson_inline simdjson_result::simdjson_result(lsx::ondemand::document_reference value, error_code error) - noexcept : implementation_simdjson_result_base(std::forward(value), error) {} +inline simdjson_result document::current_location() const noexcept { + return iter.current_location(); +} +inline int32_t document::current_depth() const noexcept { + return iter.depth(); +} -simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); +inline bool document::at_end() const noexcept { + return iter.at_end(); } -simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); + + +inline bool document::is_alive() noexcept { + return iter.is_alive(); } -simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { - if (error()) { return error(); } - return first.at(index); +simdjson_inline value_iterator document::resume_value_iterator() noexcept { + return value_iterator(&iter, 1, iter.root_position()); } -simdjson_inline error_code simdjson_result::rewind() noexcept { - if (error()) { return error(); } - first.rewind(); - return SUCCESS; +simdjson_inline value_iterator document::get_root_value_iterator() noexcept { + return resume_value_iterator(); } -simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); +simdjson_inline simdjson_result document::start_or_resume_object() noexcept { + if (iter.at_root()) { + return get_object(); + } else { + return object::resume(resume_value_iterator()); + } } -simdjson_inline simdjson_result simdjson_result::end() & noexcept { - return {}; +simdjson_inline simdjson_result document::get_value() noexcept { + // Make sure we start any arrays or objects before returning, so that start_root_() + // gets called. + + // It is the convention throughout the code that the macro `SIMDJSON_DEVELOPMENT_CHECKS` determines whether + // we check for OUT_OF_ORDER_ITERATION. Proper on::demand code should never trigger this error. +#if SIMDJSON_DEVELOPMENT_CHECKS + if (!iter.at_root()) { return OUT_OF_ORDER_ITERATION; } +#endif + // assert_at_root() serves two purposes: in Debug mode, whether or not + // SIMDJSON_DEVELOPMENT_CHECKS is set or not, it checks that we are at the root of + // the document (this will typically be redundant). In release mode, it generates + // SIMDJSON_ASSUME statements to allow the compiler to make assumptions. + iter.assert_at_root(); + switch (*iter.peek()) { + case '[': { + // The following lines check that the document ends with ]. + auto value_iterator = get_root_value_iterator(); + auto error = value_iterator.check_root_array(); + if(error) { return error; } + return value(get_root_value_iterator()); + } + case '{': { + // The following lines would check that the document ends with }. + auto value_iterator = get_root_value_iterator(); + auto error = value_iterator.check_root_object(); + if(error) { return error; } + return value(get_root_value_iterator()); + } + default: + // Unfortunately, scalar documents are a special case in simdjson and they cannot + // be safely converted to value instances. + return SCALAR_DOCUMENT_AS_VALUE; + } } -simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { +simdjson_inline simdjson_result document::get_array() & noexcept { + auto value = get_root_value_iterator(); + return array::start_root(value); +} +simdjson_inline simdjson_result document::get_object() & noexcept { + auto value = get_root_value_iterator(); + return object::start_root(value); +} + +/** + * We decided that calling 'get_double()' on the JSON document '1.233 blabla' should + * give an error, so we check for trailing content. We want to disallow trailing + * content. + * Thus, in several implementations below, we pass a 'true' parameter value to + * a get_root_value_iterator() method: this indicates that we disallow trailing content. + */ + +simdjson_inline simdjson_result document::get_uint64() noexcept { + return get_root_value_iterator().get_root_uint64(true); +} +simdjson_inline simdjson_result document::get_uint64_in_string() noexcept { + return get_root_value_iterator().get_root_uint64_in_string(true); +} +simdjson_inline simdjson_result document::get_int64() noexcept { + return get_root_value_iterator().get_root_int64(true); +} +simdjson_inline simdjson_result document::get_int64_in_string() noexcept { + return get_root_value_iterator().get_root_int64_in_string(true); +} +simdjson_inline simdjson_result document::get_double() noexcept { + return get_root_value_iterator().get_root_double(true); +} +simdjson_inline simdjson_result document::get_double_in_string() noexcept { + return get_root_value_iterator().get_root_double_in_string(true); +} +simdjson_inline simdjson_result document::get_string(bool allow_replacement) noexcept { + return get_root_value_iterator().get_root_string(true, allow_replacement); +} +template +simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept { + return get_root_value_iterator().get_root_string(receiver, true, allow_replacement); +} +simdjson_inline simdjson_result document::get_wobbly_string() noexcept { + return get_root_value_iterator().get_root_wobbly_string(true); +} +simdjson_inline simdjson_result document::get_raw_json_string() noexcept { + return get_root_value_iterator().get_root_raw_json_string(true); +} +simdjson_inline simdjson_result document::get_bool() noexcept { + return get_root_value_iterator().get_root_bool(true); +} +simdjson_inline simdjson_result document::is_null() noexcept { + return get_root_value_iterator().is_root_null(true); +} + +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_array(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_object(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_double(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_value(); } + +template<> simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); } +template<> simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); } +template<> simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); } +template<> simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); } +template<> simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); } +template<> simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); } +template<> simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); } +template<> simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); } +template<> simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); } + +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_raw_json_string(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_value(); } + +#if SIMDJSON_EXCEPTIONS +template +simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get(); } +template +simdjson_inline document::operator T() & noexcept(false) { return get(); } +simdjson_inline document::operator array() & noexcept(false) { return get_array(); } +simdjson_inline document::operator object() & noexcept(false) { return get_object(); } +simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); } +simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); } +simdjson_inline document::operator double() noexcept(false) { return get_double(); } +simdjson_inline document::operator std::string_view() noexcept(false) { return get_string(false); } +simdjson_inline document::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } +simdjson_inline document::operator bool() noexcept(false) { return get_bool(); } +simdjson_inline document::operator value() noexcept(false) { return get_value(); } + +#endif +simdjson_inline simdjson_result document::count_elements() & noexcept { + auto a = get_array(); + simdjson_result answer = a.count_elements(); + /* If there was an array, we are now left pointing at its first element. */ + if(answer.error() == SUCCESS) { rewind(); } + return answer; +} +simdjson_inline simdjson_result document::count_fields() & noexcept { + auto a = get_object(); + simdjson_result answer = a.count_fields(); + /* If there was an object, we are now left pointing at its first element. */ + if(answer.error() == SUCCESS) { rewind(); } + return answer; +} +simdjson_inline simdjson_result document::at(size_t index) & noexcept { + auto a = get_array(); + return a.at(index); +} +simdjson_inline simdjson_result document::begin() & noexcept { + return get_array().begin(); +} +simdjson_inline simdjson_result document::end() & noexcept { + return {}; +} + +simdjson_inline simdjson_result document::find_field(std::string_view key) & noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result document::find_field(const char *key) & noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result document::find_field_unordered(std::string_view key) & noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result document::find_field_unordered(const char *key) & noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result document::operator[](std::string_view key) & noexcept { + return start_or_resume_object()[key]; +} +simdjson_inline simdjson_result document::operator[](const char *key) & noexcept { + return start_or_resume_object()[key]; +} + +simdjson_inline error_code document::consume() noexcept { + bool scalar = false; + auto error = is_scalar().get(scalar); + if(error) { return error; } + if(scalar) { + iter.return_current_and_advance(); + return SUCCESS; + } + error = iter.skip_child(0); + if(error) { iter.abandon(); } + return error; +} + +simdjson_inline simdjson_result document::raw_json() noexcept { + auto _iter = get_root_value_iterator(); + const uint8_t * starting_point{_iter.peek_start()}; + auto error = consume(); + if(error) { return error; } + // After 'consume()', we could be left pointing just beyond the document, but that + // is ok because we are not going to dereference the final pointer position, we just + // use it to compute the length in bytes. + const uint8_t * final_point{iter.unsafe_pointer()}; + return std::string_view(reinterpret_cast(starting_point), size_t(final_point - starting_point)); +} + +simdjson_inline simdjson_result document::type() noexcept { + return get_root_value_iterator().type(); +} + +simdjson_inline simdjson_result document::is_scalar() noexcept { + // For more speed, we could do: + // return iter.is_single_token(); + json_type this_type; + auto error = type().get(this_type); + if(error) { return error; } + return ! ((this_type == json_type::array) || (this_type == json_type::object)); +} + +simdjson_inline simdjson_result document::is_string() noexcept { + json_type this_type; + auto error = type().get(this_type); + if(error) { return error; } + return (this_type == json_type::string); +} + +simdjson_inline bool document::is_negative() noexcept { + return get_root_value_iterator().is_root_negative(); +} + +simdjson_inline simdjson_result document::is_integer() noexcept { + return get_root_value_iterator().is_root_integer(true); +} + +simdjson_inline simdjson_result document::get_number_type() noexcept { + return get_root_value_iterator().get_root_number_type(true); +} + +simdjson_inline simdjson_result document::get_number() noexcept { + return get_root_value_iterator().get_root_number(true); +} + + +simdjson_inline simdjson_result document::raw_json_token() noexcept { + auto _iter = get_root_value_iterator(); + return std::string_view(reinterpret_cast(_iter.peek_start()), _iter.peek_root_length()); +} + +simdjson_inline simdjson_result document::at_pointer(std::string_view json_pointer) noexcept { + rewind(); // Rewind the document each time at_pointer is called + if (json_pointer.empty()) { + return this->get_value(); + } + json_type t; + SIMDJSON_TRY(type().get(t)); + switch (t) + { + case json_type::array: + return (*this).get_array().at_pointer(json_pointer); + case json_type::object: + return (*this).get_object().at_pointer(json_pointer); + default: + return INVALID_JSON_POINTER; + } +} + +simdjson_inline simdjson_result document::at_path(std::string_view json_path) noexcept { + rewind(); // Rewind the document each time at_pointer is called + if (json_path.empty()) { + return this->get_value(); + } + json_type t; + SIMDJSON_TRY(type().get(t)); + switch (t) { + case json_type::array: + return (*this).get_array().at_path(json_path); + case json_type::object: + return (*this).get_object().at_path(json_path); + default: + return INVALID_JSON_POINTER; + } +} + +} // namespace ondemand +} // namespace lsx +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result( + lsx::ondemand::document &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base( + error + ) +{ +} +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline error_code simdjson_result::rewind() noexcept { + if (error()) { return error(); } + first.rewind(); + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { if (error()) { return error(); } return first.find_field_unordered(key); } -simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { if (error()) { return error(); } return first.find_field_unordered(key); } -simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { if (error()) { return error(); } return first[key]; } -simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { if (error()) { return error(); } return first[key]; } -simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { if (error()) { return error(); } return first.find_field(key); } -simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { if (error()) { return error(); } return first.find_field(key); } -simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { +simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { if (error()) { return error(); } return first.get_array(); } -simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { +simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { if (error()) { return error(); } return first.get_object(); } -simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { if (error()) { return error(); } return first.get_uint64(); } -simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { if (error()) { return error(); } return first.get_uint64_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { if (error()) { return error(); } return first.get_int64(); } -simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { if (error()) { return error(); } return first.get_int64_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_double() noexcept { +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { if (error()) { return error(); } return first.get_double(); } -simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { if (error()) { return error(); } return first.get_double_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(allow_replacement); } template -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(receiver, allow_replacement); } -simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { if (error()) { return error(); } return first.get_wobbly_string(); } -simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { if (error()) { return error(); } return first.get_raw_json_string(); } -simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { if (error()) { return error(); } return first.get_bool(); } -simdjson_inline simdjson_result simdjson_result::get_value() noexcept { +simdjson_inline simdjson_result simdjson_result::get_value() noexcept { if (error()) { return error(); } return first.get_value(); } -simdjson_inline simdjson_result simdjson_result::is_null() noexcept { +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { if (error()) { return error(); } return first.is_null(); } -simdjson_inline simdjson_result simdjson_result::type() noexcept { + +template +simdjson_inline simdjson_result simdjson_result::get() & noexcept { + if (error()) { return error(); } + return first.get(); +} +template +simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first).get(); +} +template +simdjson_inline error_code simdjson_result::get(T &out) & noexcept { + if (error()) { return error(); } + return first.get(out); +} +template +simdjson_inline error_code simdjson_result::get(T &out) && noexcept { + if (error()) { return error(); } + return std::forward(first).get(out); +} + +template<> simdjson_inline simdjson_result simdjson_result::get() & noexcept = delete; +template<> simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first); +} +template<> simdjson_inline error_code simdjson_result::get(lsx::ondemand::document &out) & noexcept = delete; +template<> simdjson_inline error_code simdjson_result::get(lsx::ondemand::document &out) && noexcept { + if (error()) { return error(); } + out = std::forward(first); + return SUCCESS; +} + +simdjson_inline simdjson_result simdjson_result::type() noexcept { if (error()) { return error(); } return first.type(); } -simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { if (error()) { return error(); } return first.is_scalar(); } -simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { if (error()) { return error(); } return first.is_string(); } -simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { + +simdjson_inline bool simdjson_result::is_negative() noexcept { if (error()) { return error(); } return first.is_negative(); } -simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { if (error()) { return error(); } return first.is_integer(); } -simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { if (error()) { return error(); } return first.get_number_type(); } -simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { if (error()) { return error(); } return first.get_number(); } + + #if SIMDJSON_EXCEPTIONS -template ::value == false>::type> -simdjson_inline simdjson_result::operator T() noexcept(false) { +template ::value == false>::type> +simdjson_inline simdjson_result::operator T() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator lsx::ondemand::array() & noexcept(false) { +simdjson_inline simdjson_result::operator lsx::ondemand::array() & noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator lsx::ondemand::object() & noexcept(false) { +simdjson_inline simdjson_result::operator lsx::ondemand::object() & noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator int64_t() noexcept(false) { +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator double() noexcept(false) { +simdjson_inline simdjson_result::operator double() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator lsx::ondemand::raw_json_string() noexcept(false) { +simdjson_inline simdjson_result::operator lsx::ondemand::raw_json_string() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator bool() noexcept(false) { +simdjson_inline simdjson_result::operator bool() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator lsx::ondemand::value() noexcept(false) { +simdjson_inline simdjson_result::operator lsx::ondemand::value() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } #endif -simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { if (error()) { return error(); } return first.current_location(); } -simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { +simdjson_inline bool simdjson_result::at_end() const noexcept { + if (error()) { return error(); } + return first.at_end(); +} + + +simdjson_inline int32_t simdjson_result::current_depth() const noexcept { + if (error()) { return error(); } + return first.current_depth(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { if (error()) { return error(); } return first.raw_json_token(); } -simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { if (error()) { return error(); } return first.at_pointer(json_pointer); } -simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { - return error(); - } +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } return first.at_path(json_path); } } // namespace simdjson -#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H -/* end file simdjson/generic/ondemand/document-inl.h for lsx */ -/* including simdjson/generic/ondemand/document_stream-inl.h for lsx: #include "simdjson/generic/ondemand/document_stream-inl.h" */ -/* begin file simdjson/generic/ondemand/document_stream-inl.h for lsx */ -#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H - -/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ -/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document_stream.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */ -/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -#include -#include namespace simdjson { namespace lsx { namespace ondemand { -#ifdef SIMDJSON_THREADS_ENABLED - -inline void stage1_worker::finish() { - // After calling "run" someone would call finish() to wait - // for the end of the processing. - // This function will wait until either the thread has done - // the processing or, else, the destructor has been called. - std::unique_lock lock(locking_mutex); - cond_var.wait(lock, [this]{return has_work == false;}); -} - -inline stage1_worker::~stage1_worker() { - // The thread may never outlive the stage1_worker instance - // and will always be stopped/joined before the stage1_worker - // instance is gone. - stop_thread(); -} - -inline void stage1_worker::start_thread() { - std::unique_lock lock(locking_mutex); - if(thread.joinable()) { - return; // This should never happen but we never want to create more than one thread. - } - thread = std::thread([this]{ - while(true) { - std::unique_lock thread_lock(locking_mutex); - // We wait for either "run" or "stop_thread" to be called. - cond_var.wait(thread_lock, [this]{return has_work || !can_work;}); - // If, for some reason, the stop_thread() method was called (i.e., the - // destructor of stage1_worker is called, then we want to immediately destroy - // the thread (and not do any more processing). - if(!can_work) { - break; - } - this->owner->stage1_thread_error = this->owner->run_stage1(*this->stage1_thread_parser, - this->_next_batch_start); - this->has_work = false; - // The condition variable call should be moved after thread_lock.unlock() for performance - // reasons but thread sanitizers may report it as a data race if we do. - // See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock - cond_var.notify_one(); // will notify "finish" - thread_lock.unlock(); - } - } - ); -} - - -inline void stage1_worker::stop_thread() { - std::unique_lock lock(locking_mutex); - // We have to make sure that all locks can be released. - can_work = false; - has_work = false; - cond_var.notify_all(); - lock.unlock(); - if(thread.joinable()) { - thread.join(); - } -} - -inline void stage1_worker::run(document_stream * ds, parser * stage1, size_t next_batch_start) { - std::unique_lock lock(locking_mutex); - owner = ds; - _next_batch_start = next_batch_start; - stage1_thread_parser = stage1; - has_work = true; - // The condition variable call should be moved after thread_lock.unlock() for performance - // reasons but thread sanitizers may report it as a data race if we do. - // See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock - cond_var.notify_one(); // will notify the thread lock that we have work - lock.unlock(); -} - -#endif // SIMDJSON_THREADS_ENABLED - -simdjson_inline document_stream::document_stream( - ondemand::parser &_parser, - const uint8_t *_buf, +simdjson_inline document_reference::document_reference() noexcept : doc{nullptr} {} +simdjson_inline document_reference::document_reference(document &d) noexcept : doc(&d) {} +simdjson_inline void document_reference::rewind() noexcept { doc->rewind(); } +simdjson_inline simdjson_result document_reference::get_array() & noexcept { return doc->get_array(); } +simdjson_inline simdjson_result document_reference::get_object() & noexcept { return doc->get_object(); } +/** + * The document_reference instances are used primarily/solely for streams of JSON + * documents. + * We decided that calling 'get_double()' on the JSON document '1.233 blabla' should + * give an error, so we check for trailing content. + * + * However, for streams of JSON documents, we want to be able to start from + * "321" "321" "321" + * and parse it successfully as a stream of JSON documents, calling get_uint64_in_string() + * successfully each time. + * + * To achieve this result, we pass a 'false' to a get_root_value_iterator() method: + * this indicates that we allow trailing content. + */ +simdjson_inline simdjson_result document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); } +simdjson_inline simdjson_result document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); } +simdjson_inline simdjson_result document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); } +simdjson_inline simdjson_result document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); } +simdjson_inline simdjson_result document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); } +simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } +simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } +template +simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); } +simdjson_inline simdjson_result document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } +simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } +simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } +simdjson_inline simdjson_result document_reference::get_value() noexcept { return doc->get_value(); } +simdjson_inline simdjson_result document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_array(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_object(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_double(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_bool(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_value(); } +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline document_reference::operator T() noexcept(false) { return get(); } +simdjson_inline document_reference::operator array() & noexcept(false) { return array(*doc); } +simdjson_inline document_reference::operator object() & noexcept(false) { return object(*doc); } +simdjson_inline document_reference::operator uint64_t() noexcept(false) { return get_uint64(); } +simdjson_inline document_reference::operator int64_t() noexcept(false) { return get_int64(); } +simdjson_inline document_reference::operator double() noexcept(false) { return get_double(); } +simdjson_inline document_reference::operator std::string_view() noexcept(false) { return std::string_view(*doc); } +simdjson_inline document_reference::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } +simdjson_inline document_reference::operator bool() noexcept(false) { return get_bool(); } +simdjson_inline document_reference::operator value() noexcept(false) { return value(*doc); } +#endif +simdjson_inline simdjson_result document_reference::count_elements() & noexcept { return doc->count_elements(); } +simdjson_inline simdjson_result document_reference::count_fields() & noexcept { return doc->count_fields(); } +simdjson_inline simdjson_result document_reference::at(size_t index) & noexcept { return doc->at(index); } +simdjson_inline simdjson_result document_reference::begin() & noexcept { return doc->begin(); } +simdjson_inline simdjson_result document_reference::end() & noexcept { return doc->end(); } +simdjson_inline simdjson_result document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); } +simdjson_inline simdjson_result document_reference::find_field(const char *key) & noexcept { return doc->find_field(key); } +simdjson_inline simdjson_result document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; } +simdjson_inline simdjson_result document_reference::operator[](const char *key) & noexcept { return (*doc)[key]; } +simdjson_inline simdjson_result document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); } +simdjson_inline simdjson_result document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); } +simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } +simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } +simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } +simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } +simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } +simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } +simdjson_inline simdjson_result document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); } +simdjson_inline simdjson_result document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); } +simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } +simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } +simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } +simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } +simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} +simdjson_inline document_reference::operator document&() const noexcept { return *doc; } + +} // namespace ondemand +} // namespace lsx +} // namespace simdjson + + + +namespace simdjson { +simdjson_inline simdjson_result::simdjson_result(lsx::ondemand::document_reference value, error_code error) + noexcept : implementation_simdjson_result_base(std::forward(value), error) {} + + +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline error_code simdjson_result::rewind() noexcept { + if (error()) { return error(); } + first.rewind(); + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { + if (error()) { return error(); } + return first.get_array(); +} +simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { + if (error()) { return error(); } + return first.get_object(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { + if (error()) { return error(); } + return first.get_uint64(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { + if (error()) { return error(); } + return first.get_uint64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { + if (error()) { return error(); } + return first.get_int64(); +} +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { + if (error()) { return error(); } + return first.get_int64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { + if (error()) { return error(); } + return first.get_double(); +} +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { + if (error()) { return error(); } + return first.get_double_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(allow_replacement); +} +template +simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(receiver, allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { + if (error()) { return error(); } + return first.get_wobbly_string(); +} +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { + if (error()) { return error(); } + return first.get_raw_json_string(); +} +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { + if (error()) { return error(); } + return first.get_bool(); +} +simdjson_inline simdjson_result simdjson_result::get_value() noexcept { + if (error()) { return error(); } + return first.get_value(); +} +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { + if (error()) { return error(); } + return first.is_null(); +} +template +simdjson_inline simdjson_result simdjson_result::get() & noexcept { + if (error()) { return error(); } + return first.get(); +} +template +simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first).get(); +} +template +simdjson_inline error_code simdjson_result::get(T &out) & noexcept { + if (error()) { return error(); } + return first.get(out); +} +template +simdjson_inline error_code simdjson_result::get(T &out) && noexcept { + if (error()) { return error(); } + return std::forward(first).get(out); +} +simdjson_inline simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); +} +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + if (error()) { return error(); } + return first.is_scalar(); +} +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} +template <> +simdjson_inline error_code simdjson_result::get(lsx::ondemand::document_reference &out) & noexcept { + if (error()) { return error(); } + out = first; + return SUCCESS; +} +template <> +simdjson_inline error_code simdjson_result::get(lsx::ondemand::document_reference &out) && noexcept { + if (error()) { return error(); } + out = first; + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { + if (error()) { return error(); } + return first.is_negative(); +} +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + if (error()) { return error(); } + return first.is_integer(); +} +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + if (error()) { return error(); } + return first.get_number_type(); +} +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + if (error()) { return error(); } + return first.get_number(); +} +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline simdjson_result::operator T() noexcept(false) { + static_assert(std::is_same::value == false, "You should not call get when T is a document"); + static_assert(std::is_same::value == false, "You should not call get when T is a document"); + if (error()) { throw simdjson_error(error()); } + return first.get(); +} +simdjson_inline simdjson_result::operator lsx::ondemand::array() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator lsx::ondemand::object() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator double() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator lsx::ondemand::raw_json_string() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator bool() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator lsx::ondemand::value() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +#endif + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} + +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H +/* end file simdjson/generic/ondemand/document-inl.h for lsx */ +/* including simdjson/generic/ondemand/document_stream-inl.h for lsx: #include "simdjson/generic/ondemand/document_stream-inl.h" */ +/* begin file simdjson/generic/ondemand/document_stream-inl.h for lsx */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document_stream.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +#include + +namespace simdjson { +namespace lsx { +namespace ondemand { + +#ifdef SIMDJSON_THREADS_ENABLED + +inline void stage1_worker::finish() { + // After calling "run" someone would call finish() to wait + // for the end of the processing. + // This function will wait until either the thread has done + // the processing or, else, the destructor has been called. + std::unique_lock lock(locking_mutex); + cond_var.wait(lock, [this]{return has_work == false;}); +} + +inline stage1_worker::~stage1_worker() { + // The thread may never outlive the stage1_worker instance + // and will always be stopped/joined before the stage1_worker + // instance is gone. + stop_thread(); +} + +inline void stage1_worker::start_thread() { + std::unique_lock lock(locking_mutex); + if(thread.joinable()) { + return; // This should never happen but we never want to create more than one thread. + } + thread = std::thread([this]{ + while(true) { + std::unique_lock thread_lock(locking_mutex); + // We wait for either "run" or "stop_thread" to be called. + cond_var.wait(thread_lock, [this]{return has_work || !can_work;}); + // If, for some reason, the stop_thread() method was called (i.e., the + // destructor of stage1_worker is called, then we want to immediately destroy + // the thread (and not do any more processing). + if(!can_work) { + break; + } + this->owner->stage1_thread_error = this->owner->run_stage1(*this->stage1_thread_parser, + this->_next_batch_start); + this->has_work = false; + // The condition variable call should be moved after thread_lock.unlock() for performance + // reasons but thread sanitizers may report it as a data race if we do. + // See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock + cond_var.notify_one(); // will notify "finish" + thread_lock.unlock(); + } + } + ); +} + + +inline void stage1_worker::stop_thread() { + std::unique_lock lock(locking_mutex); + // We have to make sure that all locks can be released. + can_work = false; + has_work = false; + cond_var.notify_all(); + lock.unlock(); + if(thread.joinable()) { + thread.join(); + } +} + +inline void stage1_worker::run(document_stream * ds, parser * stage1, size_t next_batch_start) { + std::unique_lock lock(locking_mutex); + owner = ds; + _next_batch_start = next_batch_start; + stage1_thread_parser = stage1; + has_work = true; + // The condition variable call should be moved after thread_lock.unlock() for performance + // reasons but thread sanitizers may report it as a data race if we do. + // See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock + cond_var.notify_one(); // will notify the thread lock that we have work + lock.unlock(); +} + +#endif // SIMDJSON_THREADS_ENABLED + +simdjson_inline document_stream::document_stream( + ondemand::parser &_parser, + const uint8_t *_buf, size_t _len, size_t _batch_size, bool _allow_comma_separated @@ -103357,7 +107869,6 @@ simdjson_inline document_stream::iterator::iterator(document_stream* _stream, bo } simdjson_inline simdjson_result document_stream::iterator::operator*() noexcept { - //if(stream->error) { return stream->error; } return simdjson_result(stream->doc, stream->error); } @@ -105216,36 +109727,39 @@ simdjson_inline const char * raw_json_string::raw() const noexcept { return rein simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(std::string_view target) noexcept { size_t pos{0}; - // if the content has no escape character, just scan through it quickly! - for(;pos < target.size() && target[pos] != '\\';pos++) {} - // slow path may begin. - bool escaping{false}; - for(;pos < target.size();pos++) { - if((target[pos] == '"') && !escaping) { - return false; - } else if(target[pos] == '\\') { - escaping = !escaping; - } else { - escaping = false; + while(pos < target.size()) { + pos = target.find('"', pos); + if(pos == std::string_view::npos) { return true; } + if(pos != 0 && target[pos-1] != '\\') { return false; } + if(pos > 1 && target[pos-2] == '\\') { + size_t backslash_count{2}; + for(size_t i = 3; i <= pos; i++) { + if(target[pos-i] == '\\') { backslash_count++; } + else { break; } + } + if(backslash_count % 2 == 0) { return false; } } + pos++; } return true; } simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(const char* target) noexcept { size_t pos{0}; - // if the content has no escape character, just scan through it quickly! - for(;target[pos] && target[pos] != '\\';pos++) {} - // slow path may begin. - bool escaping{false}; - for(;target[pos];pos++) { - if((target[pos] == '"') && !escaping) { - return false; - } else if(target[pos] == '\\') { - escaping = !escaping; - } else { - escaping = false; + while(target[pos]) { + const char * result = strchr(target+pos, '"'); + if(result == nullptr) { return true; } + pos = result - target; + if(pos != 0 && target[pos-1] != '\\') { return false; } + if(pos > 1 && target[pos-2] == '\\') { + size_t backslash_count{2}; + for(size_t i = 3; i <= pos; i++) { + if(target[pos-i] == '\\') { backslash_count++; } + else { break; } + } + if(backslash_count % 2 == 0) { return false; } } + pos++; } return true; } @@ -105257,7 +109771,7 @@ simdjson_inline bool raw_json_string::unsafe_is_equal(size_t length, std::string } simdjson_inline bool raw_json_string::unsafe_is_equal(std::string_view target) const noexcept { - // Assumptions: does not contain unescaped quote characters, and + // Assumptions: does not contain unescaped quote characters("), and // the raw content is quote terminated within a valid JSON string. if(target.size() <= SIMDJSON_PADDING) { return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size()); @@ -105731,775 +110245,230 @@ simdjson_inline simdjson_result::simdjson_result( #endif // SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H /* end file simdjson/generic/ondemand/token_iterator-inl.h for lsx */ -/* including simdjson/generic/ondemand/value-inl.h for lsx: #include "simdjson/generic/ondemand/value-inl.h" */ -/* begin file simdjson/generic/ondemand/value-inl.h for lsx */ -#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H +/* including simdjson/generic/ondemand/value_iterator-inl.h for lsx: #include "simdjson/generic/ondemand/value_iterator-inl.h" */ +/* begin file simdjson/generic/ondemand/value_iterator-inl.h for lsx */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ -/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/atomparsing.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/numberparsing.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string-inl.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { namespace lsx { namespace ondemand { -simdjson_inline value::value(const value_iterator &_iter) noexcept - : iter{_iter} +simdjson_inline value_iterator::value_iterator( + json_iterator *json_iter, + depth_t depth, + token_position start_position +) noexcept : _json_iter{json_iter}, _depth{depth}, _start_position{start_position} { } -simdjson_inline value value::start(const value_iterator &iter) noexcept { - return iter; -} -simdjson_inline value value::resume(const value_iterator &iter) noexcept { - return iter; -} - -simdjson_inline simdjson_result value::get_array() noexcept { - return array::start(iter); -} -simdjson_inline simdjson_result value::get_object() noexcept { - return object::start(iter); -} -simdjson_inline simdjson_result value::start_or_resume_object() noexcept { - if (iter.at_start()) { - return get_object(); - } else { - return object::resume(iter); - } -} -simdjson_inline simdjson_result value::get_raw_json_string() noexcept { - return iter.get_raw_json_string(); -} -simdjson_inline simdjson_result value::get_string(bool allow_replacement) noexcept { - return iter.get_string(allow_replacement); -} -template -simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept { - return iter.get_string(receiver, allow_replacement); -} -simdjson_inline simdjson_result value::get_wobbly_string() noexcept { - return iter.get_wobbly_string(); -} -simdjson_inline simdjson_result value::get_double() noexcept { - return iter.get_double(); -} -simdjson_inline simdjson_result value::get_double_in_string() noexcept { - return iter.get_double_in_string(); -} -simdjson_inline simdjson_result value::get_uint64() noexcept { - return iter.get_uint64(); -} -simdjson_inline simdjson_result value::get_uint64_in_string() noexcept { - return iter.get_uint64_in_string(); -} -simdjson_inline simdjson_result value::get_int64() noexcept { - return iter.get_int64(); -} -simdjson_inline simdjson_result value::get_int64_in_string() noexcept { - return iter.get_int64_in_string(); -} -simdjson_inline simdjson_result value::get_bool() noexcept { - return iter.get_bool(); -} -simdjson_inline simdjson_result value::is_null() noexcept { - return iter.is_null(); +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_object() noexcept { + SIMDJSON_TRY( start_container('{', "Not an object", "object") ); + return started_object(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_array(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_object(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_raw_json_string(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_string(false); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_number(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_double(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_uint64(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_int64(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_bool(); } -template simdjson_inline error_code value::get(T &out) noexcept { - return get().get(out); +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_root_object() noexcept { + SIMDJSON_TRY( start_container('{', "Not an object", "object") ); + return started_root_object(); } -#if SIMDJSON_EXCEPTIONS -template -simdjson_inline value::operator T() noexcept(false) { - return get(); -} -simdjson_inline value::operator array() noexcept(false) { - return get_array(); -} -simdjson_inline value::operator object() noexcept(false) { - return get_object(); -} -simdjson_inline value::operator uint64_t() noexcept(false) { - return get_uint64(); -} -simdjson_inline value::operator int64_t() noexcept(false) { - return get_int64(); -} -simdjson_inline value::operator double() noexcept(false) { - return get_double(); -} -simdjson_inline value::operator std::string_view() noexcept(false) { - return get_string(false); -} -simdjson_inline value::operator raw_json_string() noexcept(false) { - return get_raw_json_string(); -} -simdjson_inline value::operator bool() noexcept(false) { - return get_bool(); -} +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::started_object() noexcept { + assert_at_container_start(); +#if SIMDJSON_DEVELOPMENT_CHECKS + _json_iter->set_start_position(_depth, start_position()); #endif - -simdjson_inline simdjson_result value::begin() & noexcept { - return get_array().begin(); -} -simdjson_inline simdjson_result value::end() & noexcept { - return {}; -} -simdjson_inline simdjson_result value::count_elements() & noexcept { - simdjson_result answer; - auto a = get_array(); - answer = a.count_elements(); - // count_elements leaves you pointing inside the array, at the first element. - // We need to move back so that the user can create a new array (which requires that - // we point at '['). - iter.move_at_start(); - return answer; -} -simdjson_inline simdjson_result value::count_fields() & noexcept { - simdjson_result answer; - auto a = get_object(); - answer = a.count_fields(); - iter.move_at_start(); - return answer; -} -simdjson_inline simdjson_result value::at(size_t index) noexcept { - auto a = get_array(); - return a.at(index); -} - -simdjson_inline simdjson_result value::find_field(std::string_view key) noexcept { - return start_or_resume_object().find_field(key); -} -simdjson_inline simdjson_result value::find_field(const char *key) noexcept { - return start_or_resume_object().find_field(key); -} - -simdjson_inline simdjson_result value::find_field_unordered(std::string_view key) noexcept { - return start_or_resume_object().find_field_unordered(key); -} -simdjson_inline simdjson_result value::find_field_unordered(const char *key) noexcept { - return start_or_resume_object().find_field_unordered(key); -} - -simdjson_inline simdjson_result value::operator[](std::string_view key) noexcept { - return start_or_resume_object()[key]; -} -simdjson_inline simdjson_result value::operator[](const char *key) noexcept { - return start_or_resume_object()[key]; -} - -simdjson_inline simdjson_result value::type() noexcept { - return iter.type(); + if (*_json_iter->peek() == '}') { + logger::log_value(*_json_iter, "empty object"); + _json_iter->return_current_and_advance(); + end_container(); + return false; + } + return true; } -simdjson_inline simdjson_result value::is_scalar() noexcept { - json_type this_type; - auto error = type().get(this_type); - if(error) { return error; } - return ! ((this_type == json_type::array) || (this_type == json_type::object)); +simdjson_warn_unused simdjson_inline error_code value_iterator::check_root_object() noexcept { + // When in streaming mode, we cannot expect peek_last() to be the last structural element of the + // current document. It only works in the normal mode where we have indexed a single document. + // Note that adding a check for 'streaming' is not expensive since we only have at most + // one root element. + if ( ! _json_iter->streaming() ) { + // The following lines do not fully protect against garbage content within the + // object: e.g., `{"a":2} foo }`. Users concerned with garbage content should + // call `at_end()` on the document instance at the end of the processing to + // ensure that the processing has finished at the end. + // + if (*_json_iter->peek_last() != '}') { + _json_iter->abandon(); + return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing } at end"); + } + // If the last character is } *and* the first gibberish character is also '}' + // then on-demand could accidentally go over. So we need additional checks. + // https://github.com/simdjson/simdjson/issues/1834 + // Checking that the document is balanced requires a full scan which is potentially + // expensive, but it only happens in edge cases where the first padding character is + // a closing bracket. + if ((*_json_iter->peek(_json_iter->end_position()) == '}') && (!_json_iter->balanced())) { + _json_iter->abandon(); + // The exact error would require more work. It will typically be an unclosed object. + return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "the document is unbalanced"); + } + } + return SUCCESS; } -simdjson_inline simdjson_result value::is_string() noexcept { - json_type this_type; - auto error = type().get(this_type); +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::started_root_object() noexcept { + auto error = check_root_object(); if(error) { return error; } - return (this_type == json_type::string); -} - - -simdjson_inline bool value::is_negative() noexcept { - return iter.is_negative(); + return started_object(); } -simdjson_inline simdjson_result value::is_integer() noexcept { - return iter.is_integer(); -} -simdjson_warn_unused simdjson_inline simdjson_result value::get_number_type() noexcept { - return iter.get_number_type(); -} -simdjson_warn_unused simdjson_inline simdjson_result value::get_number() noexcept { - return iter.get_number(); +simdjson_warn_unused simdjson_inline error_code value_iterator::end_container() noexcept { +#if SIMDJSON_CHECK_EOF + if (depth() > 1 && at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing parent ] or }"); } + // if (depth() <= 1 && !at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing [ or { at start"); } +#endif // SIMDJSON_CHECK_EOF + _json_iter->ascend_to(depth()-1); + return SUCCESS; } -simdjson_inline std::string_view value::raw_json_token() noexcept { - return std::string_view(reinterpret_cast(iter.peek_start()), iter.peek_start_length()); -} +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::has_next_field() noexcept { + assert_at_next(); -simdjson_inline simdjson_result value::raw_json() noexcept { - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) - { - case json_type::array: { - ondemand::array array; - SIMDJSON_TRY(get_array().get(array)); - return array.raw_json(); - } - case json_type::object: { - ondemand::object object; - SIMDJSON_TRY(get_object().get(object)); - return object.raw_json(); - } + // It's illegal to call this unless there are more tokens: anything that ends in } or ] is + // obligated to verify there are more tokens if they are not the top level. + switch (*_json_iter->return_current_and_advance()) { + case '}': + logger::log_end_value(*_json_iter, "object"); + SIMDJSON_TRY( end_container() ); + return false; + case ',': + return true; default: - return raw_json_token(); + return report_error(TAPE_ERROR, "Missing comma between object fields"); } } -simdjson_inline simdjson_result value::current_location() noexcept { - return iter.json_iter().current_location(); -} - -simdjson_inline int32_t value::current_depth() const noexcept{ - return iter.json_iter().depth(); -} +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::find_field_raw(const std::string_view key) noexcept { + error_code error; + bool has_value; + // + // Initially, the object can be in one of a few different places: + // + // 1. The start of the object, at the first field: + // + // ``` + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 2, index 1) + // ``` + if (at_first_field()) { + has_value = true; -inline bool is_pointer_well_formed(std::string_view json_pointer) noexcept { - if (simdjson_unlikely(json_pointer.empty())) { // can't be - return false; - } - if (simdjson_unlikely(json_pointer[0] != '/')) { - return false; - } - size_t escape = json_pointer.find('~'); - if (escape == std::string_view::npos) { - return true; - } - if (escape == json_pointer.size() - 1) { - return false; - } - if (json_pointer[escape + 1] != '0' && json_pointer[escape + 1] != '1') { + // + // 2. When a previous search did not yield a value or the object is empty: + // + // ``` + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 0) + // { } + // ^ (depth 0, index 2) + // ``` + // + } else if (!is_open()) { +#if SIMDJSON_DEVELOPMENT_CHECKS + // If we're past the end of the object, we're being iterated out of order. + // Note: this is not perfect detection. It's possible the user is inside some other object; if so, + // this object iterator will blithely scan that object for fields. + if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; } +#endif return false; - } - return true; -} -simdjson_inline simdjson_result value::at_pointer(std::string_view json_pointer) noexcept { - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) - { - case json_type::array: - return (*this).get_array().at_pointer(json_pointer); - case json_type::object: - return (*this).get_object().at_pointer(json_pointer); - default: - // a non-empty string can be invalid, or accessing a primitive (issue 2154) - if (is_pointer_well_formed(json_pointer)) { - return NO_SUCH_FIELD; - } - return INVALID_JSON_POINTER; + // 3. When a previous search found a field or an iterator yielded a value: + // + // ``` + // // When a field was not fully consumed (or not even touched at all) + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 2) + // // When a field was fully consumed + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 1) + // // When the last field was fully consumed + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 1) + // ``` + // + } else { + if ((error = skip_child() )) { abandon(); return error; } + if ((error = has_next_field().get(has_value) )) { abandon(); return error; } +#if SIMDJSON_DEVELOPMENT_CHECKS + if (_json_iter->start_position(_depth) != start_position()) { return OUT_OF_ORDER_ITERATION; } +#endif } -} + while (has_value) { + // Get the key and colon, stopping at the value. + raw_json_string actual_key; + // size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes + // Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2. + // field_key() advances the pointer and checks that '"' is found (corresponding to a key). + // The depth is left unchanged by field_key(). + if ((error = field_key().get(actual_key) )) { abandon(); return error; }; + // field_value() will advance and check that we find a ':' separating the + // key and the value. It will also increment the depth by one. + if ((error = field_value() )) { abandon(); return error; } + // If it matches, stop and return + // We could do it this way if we wanted to allow arbitrary + // key content (including escaped quotes). + //if (actual_key.unsafe_is_equal(max_key_length, key)) { + // Instead we do the following which may trigger buffer overruns if the + // user provides an adversarial key (containing a well placed unescaped quote + // character and being longer than the number of bytes remaining in the JSON + // input). + if (actual_key.unsafe_is_equal(key)) { + logger::log_event(*this, "match", key, -2); + // If we return here, then we return while pointing at the ':' that we just checked. + return true; + } -simdjson_inline simdjson_result value::at_path(std::string_view json_path) noexcept { - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) { - case json_type::array: - return (*this).get_array().at_path(json_path); - case json_type::object: - return (*this).get_object().at_path(json_path); - default: - return INVALID_JSON_POINTER; + // No match: skip the value and see if , or } is next + logger::log_event(*this, "no match", key, -2); + // The call to skip_child is meant to skip over the value corresponding to the key. + // After skip_child(), we are right before the next comma (',') or the final brace ('}'). + SIMDJSON_TRY( skip_child() ); // Skip the value entirely + // The has_next_field() advances the pointer and check that either ',' or '}' is found. + // It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found, + // then we are in error and we abort. + if ((error = has_next_field().get(has_value) )) { abandon(); return error; } } -} - -} // namespace ondemand -} // namespace lsx -} // namespace simdjson - -namespace simdjson { -simdjson_inline simdjson_result::simdjson_result( - lsx::ondemand::value &&value -) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) -{ -} -simdjson_inline simdjson_result::simdjson_result( - error_code error -) noexcept : - implementation_simdjson_result_base(error) -{ -} -simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); -} -simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); -} -simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { - if (error()) { return error(); } - return first.at(index); -} -simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); -} -simdjson_inline simdjson_result simdjson_result::end() & noexcept { - if (error()) { return error(); } - return {}; + // If the loop ended, we're out of fields to look at. + return false; } -simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) noexcept { - if (error()) { return error(); } - return first.find_field(key); -} -simdjson_inline simdjson_result simdjson_result::find_field(const char *key) noexcept { - if (error()) { return error(); } - return first.find_field(key); -} +SIMDJSON_PUSH_DISABLE_WARNINGS +SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING +simdjson_warn_unused simdjson_inline simdjson_result value_iterator::find_field_unordered_raw(const std::string_view key) noexcept { + /** + * When find_field_unordered_raw is called, we can either be pointing at the + * first key, pointing outside (at the closing brace) or if a key was matched + * we can be either pointing right afterthe ':' right before the value (that we need skip), + * or we may have consumed the value and we might be at a comma or at the + * final brace (ready for a call to has_next_field()). + */ + error_code error; + bool has_value; -simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); -} -simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); -} - -simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) noexcept { - if (error()) { return error(); } - return first[key]; -} -simdjson_inline simdjson_result simdjson_result::operator[](const char *key) noexcept { - if (error()) { return error(); } - return first[key]; -} - -simdjson_inline simdjson_result simdjson_result::get_array() noexcept { - if (error()) { return error(); } - return first.get_array(); -} -simdjson_inline simdjson_result simdjson_result::get_object() noexcept { - if (error()) { return error(); } - return first.get_object(); -} -simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { - if (error()) { return error(); } - return first.get_uint64(); -} -simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { - if (error()) { return error(); } - return first.get_uint64_in_string(); -} -simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { - if (error()) { return error(); } - return first.get_int64(); -} -simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { - if (error()) { return error(); } - return first.get_int64_in_string(); -} -simdjson_inline simdjson_result simdjson_result::get_double() noexcept { - if (error()) { return error(); } - return first.get_double(); -} -simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { - if (error()) { return error(); } - return first.get_double_in_string(); -} -simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(allow_replacement); -} -template -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(receiver, allow_replacement); -} -simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { - if (error()) { return error(); } - return first.get_wobbly_string(); -} -simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { - if (error()) { return error(); } - return first.get_raw_json_string(); -} -simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { - if (error()) { return error(); } - return first.get_bool(); -} -simdjson_inline simdjson_result simdjson_result::is_null() noexcept { - if (error()) { return error(); } - return first.is_null(); -} - -template simdjson_inline simdjson_result simdjson_result::get() noexcept { - if (error()) { return error(); } - return first.get(); -} -template simdjson_inline error_code simdjson_result::get(T &out) noexcept { - if (error()) { return error(); } - return first.get(out); -} - -template<> simdjson_inline simdjson_result simdjson_result::get() noexcept { - if (error()) { return error(); } - return std::move(first); -} -template<> simdjson_inline error_code simdjson_result::get(lsx::ondemand::value &out) noexcept { - if (error()) { return error(); } - out = first; - return SUCCESS; -} - -simdjson_inline simdjson_result simdjson_result::type() noexcept { - if (error()) { return error(); } - return first.type(); -} -simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { - if (error()) { return error(); } - return first.is_scalar(); -} -simdjson_inline simdjson_result simdjson_result::is_string() noexcept { - if (error()) { return error(); } - return first.is_string(); -} -simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { - if (error()) { return error(); } - return first.is_negative(); -} -simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { - if (error()) { return error(); } - return first.is_integer(); -} -simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { - if (error()) { return error(); } - return first.get_number_type(); -} -simdjson_inline simdjson_result simdjson_result::get_number() noexcept { - if (error()) { return error(); } - return first.get_number(); -} -#if SIMDJSON_EXCEPTIONS -template -simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return static_cast(first); -} -simdjson_inline simdjson_result::operator lsx::ondemand::array() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator lsx::ondemand::object() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator int64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator double() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator lsx::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator bool() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -#endif - -simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { - if (error()) { return error(); } - return first.raw_json_token(); -} - -simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { - if (error()) { return error(); } - return first.raw_json(); -} - -simdjson_inline simdjson_result simdjson_result::current_location() noexcept { - if (error()) { return error(); } - return first.current_location(); -} - -simdjson_inline simdjson_result simdjson_result::current_depth() const noexcept { - if (error()) { return error(); } - return first.current_depth(); -} - -simdjson_inline simdjson_result simdjson_result::at_pointer( - std::string_view json_pointer) noexcept { - if (error()) { - return error(); - } - return first.at_pointer(json_pointer); -} - -simdjson_inline simdjson_result simdjson_result::at_path( - std::string_view json_path) noexcept { - if (error()) { - return error(); - } - return first.at_path(json_path); -} - -} // namespace simdjson - -#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H -/* end file simdjson/generic/ondemand/value-inl.h for lsx */ -/* including simdjson/generic/ondemand/value_iterator-inl.h for lsx: #include "simdjson/generic/ondemand/value_iterator-inl.h" */ -/* begin file simdjson/generic/ondemand/value_iterator-inl.h for lsx */ -#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H - -/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ -/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/atomparsing.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/numberparsing.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string-inl.h" */ -/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -namespace simdjson { -namespace lsx { -namespace ondemand { - -simdjson_inline value_iterator::value_iterator( - json_iterator *json_iter, - depth_t depth, - token_position start_position -) noexcept : _json_iter{json_iter}, _depth{depth}, _start_position{start_position} -{ -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_object() noexcept { - SIMDJSON_TRY( start_container('{', "Not an object", "object") ); - return started_object(); -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::start_root_object() noexcept { - SIMDJSON_TRY( start_container('{', "Not an object", "object") ); - return started_root_object(); -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::started_object() noexcept { - assert_at_container_start(); -#if SIMDJSON_DEVELOPMENT_CHECKS - _json_iter->set_start_position(_depth, start_position()); -#endif - if (*_json_iter->peek() == '}') { - logger::log_value(*_json_iter, "empty object"); - _json_iter->return_current_and_advance(); - end_container(); - return false; - } - return true; -} - -simdjson_warn_unused simdjson_inline error_code value_iterator::check_root_object() noexcept { - // When in streaming mode, we cannot expect peek_last() to be the last structural element of the - // current document. It only works in the normal mode where we have indexed a single document. - // Note that adding a check for 'streaming' is not expensive since we only have at most - // one root element. - if ( ! _json_iter->streaming() ) { - // The following lines do not fully protect against garbage content within the - // object: e.g., `{"a":2} foo }`. Users concerned with garbage content should - // call `at_end()` on the document instance at the end of the processing to - // ensure that the processing has finished at the end. - // - if (*_json_iter->peek_last() != '}') { - _json_iter->abandon(); - return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing } at end"); - } - // If the last character is } *and* the first gibberish character is also '}' - // then on-demand could accidentally go over. So we need additional checks. - // https://github.com/simdjson/simdjson/issues/1834 - // Checking that the document is balanced requires a full scan which is potentially - // expensive, but it only happens in edge cases where the first padding character is - // a closing bracket. - if ((*_json_iter->peek(_json_iter->end_position()) == '}') && (!_json_iter->balanced())) { - _json_iter->abandon(); - // The exact error would require more work. It will typically be an unclosed object. - return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "the document is unbalanced"); - } - } - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::started_root_object() noexcept { - auto error = check_root_object(); - if(error) { return error; } - return started_object(); -} - -simdjson_warn_unused simdjson_inline error_code value_iterator::end_container() noexcept { -#if SIMDJSON_CHECK_EOF - if (depth() > 1 && at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing parent ] or }"); } - // if (depth() <= 1 && !at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing [ or { at start"); } -#endif // SIMDJSON_CHECK_EOF - _json_iter->ascend_to(depth()-1); - return SUCCESS; -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::has_next_field() noexcept { - assert_at_next(); - - // It's illegal to call this unless there are more tokens: anything that ends in } or ] is - // obligated to verify there are more tokens if they are not the top level. - switch (*_json_iter->return_current_and_advance()) { - case '}': - logger::log_end_value(*_json_iter, "object"); - SIMDJSON_TRY( end_container() ); - return false; - case ',': - return true; - default: - return report_error(TAPE_ERROR, "Missing comma between object fields"); - } -} - -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::find_field_raw(const std::string_view key) noexcept { - error_code error; - bool has_value; - // - // Initially, the object can be in one of a few different places: - // - // 1. The start of the object, at the first field: - // - // ``` - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 2, index 1) - // ``` - if (at_first_field()) { - has_value = true; - - // - // 2. When a previous search did not yield a value or the object is empty: - // - // ``` - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 0) - // { } - // ^ (depth 0, index 2) - // ``` - // - } else if (!is_open()) { -#if SIMDJSON_DEVELOPMENT_CHECKS - // If we're past the end of the object, we're being iterated out of order. - // Note: this is not perfect detection. It's possible the user is inside some other object; if so, - // this object iterator will blithely scan that object for fields. - if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; } -#endif - return false; - - // 3. When a previous search found a field or an iterator yielded a value: - // - // ``` - // // When a field was not fully consumed (or not even touched at all) - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 2) - // // When a field was fully consumed - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 1) - // // When the last field was fully consumed - // { "a": [ 1, 2 ], "b": [ 3, 4 ] } - // ^ (depth 1) - // ``` - // - } else { - if ((error = skip_child() )) { abandon(); return error; } - if ((error = has_next_field().get(has_value) )) { abandon(); return error; } -#if SIMDJSON_DEVELOPMENT_CHECKS - if (_json_iter->start_position(_depth) != start_position()) { return OUT_OF_ORDER_ITERATION; } -#endif - } - while (has_value) { - // Get the key and colon, stopping at the value. - raw_json_string actual_key; - // size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes - // Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2. - // field_key() advances the pointer and checks that '"' is found (corresponding to a key). - // The depth is left unchanged by field_key(). - if ((error = field_key().get(actual_key) )) { abandon(); return error; }; - // field_value() will advance and check that we find a ':' separating the - // key and the value. It will also increment the depth by one. - if ((error = field_value() )) { abandon(); return error; } - // If it matches, stop and return - // We could do it this way if we wanted to allow arbitrary - // key content (including escaped quotes). - //if (actual_key.unsafe_is_equal(max_key_length, key)) { - // Instead we do the following which may trigger buffer overruns if the - // user provides an adversarial key (containing a well placed unescaped quote - // character and being longer than the number of bytes remaining in the JSON - // input). - if (actual_key.unsafe_is_equal(key)) { - logger::log_event(*this, "match", key, -2); - // If we return here, then we return while pointing at the ':' that we just checked. - return true; - } - - // No match: skip the value and see if , or } is next - logger::log_event(*this, "no match", key, -2); - // The call to skip_child is meant to skip over the value corresponding to the key. - // After skip_child(), we are right before the next comma (',') or the final brace ('}'). - SIMDJSON_TRY( skip_child() ); // Skip the value entirely - // The has_next_field() advances the pointer and check that either ',' or '}' is found. - // It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found, - // then we are in error and we abort. - if ((error = has_next_field().get(has_value) )) { abandon(); return error; } - } - - // If the loop ended, we're out of fields to look at. - return false; -} - -SIMDJSON_PUSH_DISABLE_WARNINGS -SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING -simdjson_warn_unused simdjson_inline simdjson_result value_iterator::find_field_unordered_raw(const std::string_view key) noexcept { - /** - * When find_field_unordered_raw is called, we can either be pointing at the - * first key, pointing outside (at the closing brace) or if a key was matched - * we can be either pointing right afterthe ':' right before the value (that we need skip), - * or we may have consumed the value and we might be at a comma or at the - * final brace (ready for a call to has_next_field()). - */ - error_code error; - bool has_value; - - // First, we scan from that point to the end. - // If we don't find a match, we may loop back around, and scan from the beginning to that point. - token_position search_start = _json_iter->position(); + // First, we scan from that point to the end. + // If we don't find a match, we may loop back around, and scan from the beginning to that point. + token_position search_start = _json_iter->position(); // We want to know whether we need to go back to the beginning. bool at_first = at_first_field(); @@ -107079,6 +111048,8 @@ simdjson_inline simdjson_result value_iterator::is_root_null(bool check_tr if(result) { // we have something that looks like a null. if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; } advance_root_scalar("null"); + } else if (json[0] == 'n') { + return incorrect_type_error("Not a null but starts with n"); } return result; } @@ -107370,6 +111341,8 @@ simdjson_inline simdjson_result::simdjson_result( #endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H /* end file simdjson/generic/ondemand/value_iterator-inl.h for lsx */ + + /* end file simdjson/generic/ondemand/amalgamated.h for lsx */ /* including simdjson/lsx/end.h: #include "simdjson/lsx/end.h" */ /* begin file simdjson/lsx/end.h */ @@ -107567,7 +111540,13 @@ simdjson_inline internal::value128 full_multiplication(uint64_t value1, uint64_t } // namespace lasx } // namespace simdjson +#ifndef SIMDJSON_SWAR_NUMBER_PARSING +#if SIMDJSON_IS_BIG_ENDIAN +#define SIMDJSON_SWAR_NUMBER_PARSING 0 +#else #define SIMDJSON_SWAR_NUMBER_PARSING 1 +#endif +#endif #endif // SIMDJSON_LASX_NUMBERPARSING_DEFS_H /* end file simdjson/lasx/numberparsing_defs.h */ @@ -108060,6 +112039,132 @@ class value_iterator; #endif // SIMDJSON_GENERIC_ONDEMAND_BASE_H /* end file simdjson/generic/ondemand/base.h for lasx */ +/* including simdjson/generic/ondemand/deserialize.h for lasx: #include "simdjson/generic/ondemand/deserialize.h" */ +/* begin file simdjson/generic/ondemand/deserialize.h for lasx */ +#if SIMDJSON_SUPPORTS_DESERIALIZATION + +#ifndef SIMDJSON_ONDEMAND_DESERIALIZE_H +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_ONDEMAND_DESERIALIZE_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +namespace simdjson { + +namespace tag_invoke_fn_ns { +void tag_invoke(); + +struct tag_invoke_fn { + template + requires requires(Tag tag, Args &&...args) { + tag_invoke(std::forward(tag), std::forward(args)...); + } + constexpr auto operator()(Tag tag, Args &&...args) const + noexcept(noexcept(tag_invoke(std::forward(tag), + std::forward(args)...))) + -> decltype(tag_invoke(std::forward(tag), + std::forward(args)...)) { + return tag_invoke(std::forward(tag), std::forward(args)...); + } +}; +} // namespace tag_invoke_fn_ns + +inline namespace tag_invoke_ns { +inline constexpr tag_invoke_fn_ns::tag_invoke_fn tag_invoke = {}; +} // namespace tag_invoke_ns + +template +concept tag_invocable = requires(Tag tag, Args... args) { + tag_invoke(std::forward(tag), std::forward(args)...); +}; + +template +concept nothrow_tag_invocable = + tag_invocable && requires(Tag tag, Args... args) { + { + tag_invoke(std::forward(tag), std::forward(args)...) + } noexcept; + }; + +template +using tag_invoke_result = + std::invoke_result; + +template +using tag_invoke_result_t = + std::invoke_result_t; + +template using tag_t = std::decay_t; + + +struct deserialize_tag; + +/// These types are deserializable in a built-in way +template struct is_builtin_deserializable : std::false_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; +template <> struct is_builtin_deserializable : std::true_type {}; + +template +concept is_builtin_deserializable_v = is_builtin_deserializable::value; + +template +concept custom_deserializable = tag_invocable; + +template +concept deserializable = custom_deserializable || is_builtin_deserializable_v; + +template +concept nothrow_custom_deserializable = nothrow_tag_invocable; + +// built-in types are noexcept and if an error happens, the value simply gets ignored and the error is returned. +template +concept nothrow_deserializable = nothrow_custom_deserializable || is_builtin_deserializable_v; + +/// Deserialize Tag +inline constexpr struct deserialize_tag { + using value_type = lasx::ondemand::value; + using document_type = lasx::ondemand::document; + using document_reference_type = lasx::ondemand::document_reference; + + // Customization Point for value + template + requires custom_deserializable + [[nodiscard]] constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for document + template + requires custom_deserializable + [[nodiscard]] constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + // Customization Point for document reference + template + requires custom_deserializable + [[nodiscard]] constexpr /* error_code */ auto operator()(document_reference_type &object, T& output) const noexcept(nothrow_custom_deserializable) { + return tag_invoke(*this, object, output); + } + + +} deserialize{}; + +} // namespace simdjson + +#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION + +/* end file simdjson/generic/ondemand/deserialize.h for lasx */ /* including simdjson/generic/ondemand/value_iterator.h for lasx: #include "simdjson/generic/ondemand/value_iterator.h" */ /* begin file simdjson/generic/ondemand/value_iterator.h for lasx */ #ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H @@ -108564,12 +112669,15 @@ struct simdjson_result : public lasx::implementa /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ +#include + namespace simdjson { + namespace lasx { namespace ondemand { - /** * An ephemeral JSON value returned during iteration. It is only valid for as long as you do * not access more data in the JSON document. @@ -108594,16 +112702,21 @@ class value { * @returns A value of the given type, parsed from the JSON. * @returns INCORRECT_TYPE If the JSON value is not the given type. */ - template simdjson_inline simdjson_result get() noexcept { - // Unless the simdjson library or the user provides an inline implementation, calling this method should - // immediately fail. - static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " - "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " - "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." - " You may also add support for custom types, see our documentation."); + template + simdjson_inline simdjson_result get() +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; } + /** * Get this value as the given type. * @@ -108613,7 +112726,32 @@ class value { * @returns INCORRECT_TYPE If the JSON value is not an object. * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ - template simdjson_inline error_code get(T &out) noexcept; + template + simdjson_inline error_code get(T &out) +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_DESERIALIZATION + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION + // Unless the simdjson library or the user provides an inline implementation, calling this method should + // immediately fail. + static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " + "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " + "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." + " You may also add support for custom types, see our documentation."); + static_cast(out); // to get rid of unused errors + return UNINITIALIZED; +#if SIMDJSON_SUPPORTS_DESERIALIZATION + } +#endif + } /** * Cast this JSON value to an array. @@ -108689,6 +112827,17 @@ class value { * Important: a value should be consumed once. Calling get_string() twice on the same value * is an error. * + * In some instances, you may want to allow replacement of invalid Unicode sequences. + * You may do so by passing the allow_replacement parameter as true. In the following + * example, the string "431924697b\udff0L\u0001Y" is not valid Unicode. By passing true + * to get_string, we allow the replacement of the invalid Unicode sequences with the Unicode + * replacement character (U+FFFD). + * + * simdjson::ondemand::parser parser; + * auto json = R"({"deviceId":"431924697b\udff0L\u0001Y"})"_padded; + * simdjson::ondemand::document doc = parser.iterate(json); + * auto view = doc["deviceId"].get_string(true); + * * @returns An UTF-8 string. The string is stored in the parser and will be invalidated the next * time it parses a document or when it is destroyed. * @returns INCORRECT_TYPE if the JSON value is not a string. @@ -108940,6 +113089,7 @@ class value { simdjson_inline simdjson_result operator[](std::string_view key) noexcept; /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ simdjson_inline simdjson_result operator[](const char *key) noexcept; + simdjson_result operator[](int) noexcept = delete; /** * Get the type of this JSON value. It does not validate or consume the value. @@ -109307,6 +113457,7 @@ struct simdjson_result : public lasx::implementation_simd simdjson_inline simdjson_result operator[](std::string_view key) noexcept; /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) noexcept; */ simdjson_inline simdjson_result operator[](const char *key) noexcept; + simdjson_result operator[](int) noexcept = delete; /** * Get the type of this JSON value. @@ -110362,6 +114513,22 @@ class parser { * using a sanitizer that verifies that no uninitialized byte is read, then you should initialize the * SIMDJSON_PADDING bytes to avoid runtime warnings. * + * ### std::string references + * + * If you pass a mutable std::string reference (std::string&), the parser will seek to extend + * its capacity to SIMDJSON_PADDING bytes beyond the end of the string. + * + * Whenever you pass an std::string reference, the parser will access the bytes beyond the end of + * the string but before the end of the allocated memory (std::string::capacity()). + * If you are using a sanitizer that checks for reading uninitialized bytes or std::string's + * container-overflow checks, you may encounter sanitizer warnings. + * You can safely ignore these warnings. Or you can call simdjson::pad(std::string&) to pad the + * string with SIMDJSON_PADDING spaces: this function returns a simdjson::padding_string_view + * which can be be passed to the parser's iterate function: + * + * std::string json = R"({ "foo": 1 } { "foo": 2 } { "foo": 3 } )"; + * document doc = parser.iterate(simdjson::pad(json)); + * * @param json The JSON to parse. * @param len The length of the JSON. * @param capacity The number of bytes allocated in the JSON (must be at least len+SIMDJSON_PADDING). @@ -110556,8 +114723,12 @@ class parser { * behavior of the parser for future operations. */ bool threaded{true}; + #else + /** + * When SIMDJSON_THREADS_ENABLED is not defined, the parser instance cannot use threads. + */ + bool threaded{false}; #endif - /** * Unescape this JSON string, replacing \\ with \, \n with newline, etc. to a user-provided buffer. * The result must be valid UTF-8. @@ -110699,7 +114870,8 @@ class array { * calling this function, if successful, the array is 'rewinded' at its * beginning as if it had never been accessed. If the JSON is malformed (e.g., * there is a missing comma), then an error is returned and it is no longer - * safe to continue. + * safe to continue. Note that count_elements() does not validate the JSON values, + * only the structure of the array. * * To check that an array is empty, it is more performant to use * the is_empty() method. @@ -110977,8 +115149,11 @@ struct simdjson_result : public lasx::implementa /* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_H */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + namespace simdjson { namespace lasx { namespace ondemand { @@ -111151,24 +115326,39 @@ class document { * @returns A value of the given type, parsed from the JSON. * @returns INCORRECT_TYPE If the JSON value is not the given type. */ - template simdjson_inline simdjson_result get() & noexcept { - // Unless the simdjson library or the user provides an inline implementation, calling this method should - // immediately fail. - static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " - "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " - "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." - " You may also add support for custom types, see our documentation."); - } - /** @overload template simdjson_result get() & noexcept */ - template simdjson_deprecated simdjson_inline simdjson_result get() && noexcept { - // Unless the simdjson library or the user provides an inline implementation, calling this method should - // immediately fail. - static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " - "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " - "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." - " You may also add support for custom types, see our documentation."); + template + simdjson_inline simdjson_result get() & +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "Cannot initialize the specified type."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + /** + * @overload template simdjson_result get() & noexcept + * + * We disallow the use tag_invoke CPO on a moved document; it may create UB + * if user uses `ondemand::array` or `ondemand::object` in their custom type. + * + * The member function is still remains specialize-able for compatibility + * reasons, but we completely disallow its use when a tag_invoke customization + * is provided. + */ + template + simdjson_inline simdjson_result get() && +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(!std::is_same::value && !std::is_same::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document being alive; that would be Undefined Behaviour."); + return static_cast(*this).get(); } /** @@ -111182,7 +115372,32 @@ class document { * @returns INCORRECT_TYPE If the JSON value is not an object. * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ - template simdjson_inline error_code get(T &out) & noexcept; + template + simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_DESERIALIZATION + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION + // Unless the simdjson library or the user provides an inline implementation, calling this method should + // immediately fail. + static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " + "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " + "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." + " You may also add support for custom types, see our documentation."); + static_cast(out); // to get rid of unused errors + return UNINITIALIZED; +#if SIMDJSON_SUPPORTS_DESERIALIZATION + } +#endif + } /** @overload template error_code get(T &out) & noexcept */ template simdjson_deprecated simdjson_inline error_code get(T &out) && noexcept; @@ -111284,7 +115499,8 @@ class document { * calling this function, if successful, the array is 'rewinded' at its * beginning as if it had never been accessed. If the JSON is malformed (e.g., * there is a missing comma), then an error is returned and it is no longer - * safe to continue. + * safe to continue. Note that count_elements() does not validate the JSON values, + * only the structure of the array. */ simdjson_inline simdjson_result count_elements() & noexcept; /** @@ -111396,6 +115612,7 @@ class document { simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; /** @overload simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; /** * Get the type of this JSON value. It does not validate or consume the value. @@ -111664,6 +115881,11 @@ class document { /** * A document_reference is a thin wrapper around a document reference instance. + * The document_reference instances are used primarily/solely for streams of JSON + * documents. They differ from document instances when parsing a scalar value + * (a document that is not an array or an object). In the case of a document, + * we expect the document to be fully consumed. In the case of a document_reference, + * we allow trailing content. */ class document_reference { public: @@ -111689,7 +115911,70 @@ class document_reference { simdjson_inline simdjson_result get_value() noexcept; simdjson_inline simdjson_result is_null() noexcept; - template simdjson_inline simdjson_result get() & noexcept; + template + simdjson_inline simdjson_result get() & +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(std::is_default_constructible::value, "Cannot initialize the specified type."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; + } + template + simdjson_inline simdjson_result get() && +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { + static_assert(!std::is_same::value && !std::is_same::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document_reference being alive; that would be Undefined Behaviour."); + return static_cast(*this).get(); + } + + /** + * Get this value as the given type. + * + * Supported types: object, array, raw_json_string, string_view, uint64_t, int64_t, double, bool, value + * + * Be mindful that the document instance must remain in scope while you are accessing object, array and value instances. + * + * @param out This is set to a value of the given type, parsed from the JSON. If there is an error, this may not be initialized. + * @returns INCORRECT_TYPE If the JSON value is not an object. + * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. + */ + template + simdjson_inline error_code get(T &out) & +#if SIMDJSON_SUPPORTS_DESERIALIZATION + noexcept(custom_deserializable ? nothrow_custom_deserializable : true) +#else + noexcept +#endif + { +#if SIMDJSON_SUPPORTS_DESERIALIZATION + if constexpr (custom_deserializable) { + return deserialize(*this, out); + } else { +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION + // Unless the simdjson library or the user provides an inline implementation, calling this method should + // immediately fail. + static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " + "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " + "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." + " You may also add support for custom types, see our documentation."); + static_cast(out); // to get rid of unused errors + return UNINITIALIZED; +#if SIMDJSON_SUPPORTS_DESERIALIZATION + } +#endif + } + /** @overload template error_code get(T &out) & noexcept */ + template simdjson_inline error_code get(T &out) && noexcept; simdjson_inline simdjson_result raw_json() noexcept; simdjson_inline operator document&() const noexcept; #if SIMDJSON_EXCEPTIONS @@ -111714,6 +115999,7 @@ class document_reference { simdjson_inline simdjson_result find_field(const char *key) & noexcept; simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; @@ -111792,6 +116078,7 @@ struct simdjson_result : public lasx::implementation_s simdjson_inline simdjson_result find_field(const char *key) & noexcept; simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; simdjson_inline simdjson_result type() noexcept; @@ -111841,8 +116128,14 @@ struct simdjson_result : public lasx::implem simdjson_inline simdjson_result get_bool() noexcept; simdjson_inline simdjson_result get_value() noexcept; simdjson_inline simdjson_result is_null() noexcept; + + template simdjson_inline simdjson_result get() & noexcept; + template simdjson_inline simdjson_result get() && noexcept; + + template simdjson_inline error_code get(T &out) & noexcept; + template simdjson_inline error_code get(T &out) && noexcept; #if SIMDJSON_EXCEPTIONS - template ::value == false>::type> + template explicit simdjson_inline operator T() noexcept(false); simdjson_inline operator lasx::ondemand::array() & noexcept(false); simdjson_inline operator lasx::ondemand::object() & noexcept(false); @@ -111863,6 +116156,7 @@ struct simdjson_result : public lasx::implem simdjson_inline simdjson_result find_field(const char *key) & noexcept; simdjson_inline simdjson_result operator[](std::string_view key) & noexcept; simdjson_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_result operator[](int) & noexcept = delete; simdjson_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; simdjson_inline simdjson_result find_field_unordered(const char *key) & noexcept; simdjson_inline simdjson_result type() noexcept; @@ -112793,6 +117087,177 @@ inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result +#include + +namespace simdjson { +template +constexpr bool require_custom_serialization = false; + +////////////////////////////// +// Number deserialization +////////////////////////////// + +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + using limits = std::numeric_limits; + + uint64_t x; + SIMDJSON_TRY(val.get_uint64().get(x)); + if (x > (limits::max)()) { + return NUMBER_OUT_OF_RANGE; + } + out = static_cast(x); + return SUCCESS; +} + +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + double x; + SIMDJSON_TRY(val.get_double().get(x)); + out = static_cast(x); + return SUCCESS; +} + +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, auto &val, T &out) noexcept { + using limits = std::numeric_limits; + + int64_t x; + SIMDJSON_TRY(val.get_int64().get(x)); + if (x > (limits::max)() || x < (limits::min)()) { + return NUMBER_OUT_OF_RANGE; + } + out = static_cast(x); + return SUCCESS; +} + +/** + * STL containers have several constructors including one that takes a single + * size argument. Thus, some compilers (Visual Studio) will not be able to + * disambiguate between the size and container constructor. Users should + * explicitly specify the type of the container as needed: e.g., + * doc.get>(). + */ +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(false) { + using value_type = typename std::remove_cvref_t::value_type; + static_assert( + deserializable, + "The specified type inside the container must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the container must default constructible."); + + lasx::ondemand::array arr; + SIMDJSON_TRY(val.get_array().get(arr)); + for (auto v : arr) { + if constexpr (concepts::returns_reference) { + if (auto const err = v.get().get(concepts::emplace_one(out)); + err) { + // If an error occurs, the empty element that we just inserted gets + // removed. We're not using a temp variable because if T is a heavy + // type, we want the valid path to be the fast path and the slow path be + // the path that has errors in it. + if constexpr (requires { out.pop_back(); }) { + static_cast(out.pop_back()); + } + return err; + } + } else { + value_type temp; + if (auto const err = v.get().get(temp); err) { + return err; + } + concepts::emplace_one(out, std::move(temp)); + } + } + return SUCCESS; +} + + + +/** + * This CPO (Customization Point Object) will help deserialize into + * smart pointers. + * + * If constructing T is nothrow, this conversion should be nothrow as well since + * we return MEMALLOC if we're not able to allocate memory instead of throwing + * the error message. + * + * @tparam T The type inside the smart pointer + * @tparam ValT document/value type + * @param val document/value + * @param out a reference to the smart pointer + * @return status of the conversion + */ +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::element_type, ValT>) { + using element_type = typename std::remove_cvref_t::element_type; + + // For better error messages, don't use these as constraints on + // the tag_invoke CPO. + static_assert( + deserializable, + "The specified type inside the unique_ptr must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the unique_ptr must default constructible."); + + auto ptr = new (std::nothrow) element_type(); + if (ptr == nullptr) { + return MEMALLOC; + } + SIMDJSON_TRY(val.template get(*ptr)); + out.reset(ptr); + return SUCCESS; +} + +/** + * This CPO (Customization Point Object) will help deserialize into optional types. + */ +template + requires(!require_custom_serialization) +error_code tag_invoke(deserialize_tag, ValT &val, T &out) noexcept(nothrow_deserializable::value_type, ValT>) { + using value_type = typename std::remove_cvref_t::value_type; + + static_assert( + deserializable, + "The specified type inside the unique_ptr must itself be deserializable"); + static_assert( + std::is_default_constructible_v, + "The specified type inside the unique_ptr must default constructible."); + + if (!out) { + out.emplace(); + } + SIMDJSON_TRY(val.template get(out.value())); + return SUCCESS; +} + +} // namespace simdjson + +#endif // SIMDJSON_ONDEMAND_DESERIALIZE_H +#endif // SIMDJSON_SUPPORTS_DESERIALIZATION +/* end file simdjson/generic/ondemand/std_deserialize.h for lasx */ + // Inline definitions /* including simdjson/generic/ondemand/array-inl.h for lasx: #include "simdjson/generic/ondemand/array-inl.h" */ /* begin file simdjson/generic/ondemand/array-inl.h for lasx */ @@ -112800,6 +117265,7 @@ inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result array::at_pointer(std::string_view json_pointer) n return child; } -inline std::string json_path_to_pointer_conversion(std::string_view json_path) { - if (json_path.empty() || (json_path.front() != '.' && - json_path.front() != '[')) { - return "-1"; // This is just a sentinel value, the caller should check for this and return an error. - } - - std::string result; - // Reserve space to reduce allocations, adjusting for potential increases due - // to escaping. - result.reserve(json_path.size() * 2); - - size_t i = 0; - - while (i < json_path.length()) { - if (json_path[i] == '.') { - result += '/'; - } else if (json_path[i] == '[') { - result += '/'; - ++i; // Move past the '[' - while (i < json_path.length() && json_path[i] != ']') { - if (json_path[i] == '~') { - result += "~0"; - } else if (json_path[i] == '/') { - result += "~1"; - } else { - result += json_path[i]; - } - ++i; - } - if (i == json_path.length() || json_path[i] != ']') { - return "-1"; // Using sentinel value that will be handled as an error by the caller. - } - } else { - if (json_path[i] == '~') { - result += "~0"; - } else if (json_path[i] == '/') { - result += "~1"; - } else { - result += json_path[i]; - } - } - ++i; - } - - return result; -} - inline simdjson_result array::at_path(std::string_view json_path) noexcept { auto json_pointer = json_path_to_pointer_conversion(json_path); if (json_pointer == "-1") { return INVALID_JSON_POINTER; } @@ -113161,313 +117580,276 @@ simdjson_inline simdjson_result &simdjson_result #endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_ITERATOR_INL_H /* end file simdjson/generic/ondemand/array_iterator-inl.h for lasx */ -/* including simdjson/generic/ondemand/document-inl.h for lasx: #include "simdjson/generic/ondemand/document-inl.h" */ -/* begin file simdjson/generic/ondemand/document-inl.h for lasx */ -#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H +/* including simdjson/generic/ondemand/value-inl.h for lasx: #include "simdjson/generic/ondemand/value-inl.h" */ +/* begin file simdjson/generic/ondemand/value-inl.h for lasx */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H /* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ -/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ /* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */ /* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { namespace lasx { namespace ondemand { -simdjson_inline document::document(ondemand::json_iterator &&_iter) noexcept - : iter{std::forward(_iter)} +simdjson_inline value::value(const value_iterator &_iter) noexcept + : iter{_iter} { - logger::log_start_value(iter, "document"); } - -simdjson_inline document document::start(json_iterator &&iter) noexcept { - return document(std::forward(iter)); +simdjson_inline value value::start(const value_iterator &iter) noexcept { + return iter; } - -inline void document::rewind() noexcept { - iter.rewind(); +simdjson_inline value value::resume(const value_iterator &iter) noexcept { + return iter; } -inline std::string document::to_debug_string() noexcept { - return iter.to_string(); +simdjson_inline simdjson_result value::get_array() noexcept { + return array::start(iter); } - -inline simdjson_result document::current_location() const noexcept { - return iter.current_location(); +simdjson_inline simdjson_result value::get_object() noexcept { + return object::start(iter); } - -inline int32_t document::current_depth() const noexcept { - return iter.depth(); +simdjson_inline simdjson_result value::start_or_resume_object() noexcept { + if (iter.at_start()) { + return get_object(); + } else { + return object::resume(iter); + } } -inline bool document::at_end() const noexcept { - return iter.at_end(); +simdjson_inline simdjson_result value::get_raw_json_string() noexcept { + return iter.get_raw_json_string(); } - - -inline bool document::is_alive() noexcept { - return iter.is_alive(); +simdjson_inline simdjson_result value::get_string(bool allow_replacement) noexcept { + return iter.get_string(allow_replacement); } -simdjson_inline value_iterator document::resume_value_iterator() noexcept { - return value_iterator(&iter, 1, iter.root_position()); +template +simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept { + return iter.get_string(receiver, allow_replacement); } -simdjson_inline value_iterator document::get_root_value_iterator() noexcept { - return resume_value_iterator(); +simdjson_inline simdjson_result value::get_wobbly_string() noexcept { + return iter.get_wobbly_string(); } -simdjson_inline simdjson_result document::start_or_resume_object() noexcept { - if (iter.at_root()) { - return get_object(); - } else { - return object::resume(resume_value_iterator()); - } +simdjson_inline simdjson_result value::get_double() noexcept { + return iter.get_double(); } -simdjson_inline simdjson_result document::get_value() noexcept { - // Make sure we start any arrays or objects before returning, so that start_root_() - // gets called. - - // It is the convention throughout the code that the macro `SIMDJSON_DEVELOPMENT_CHECKS` determines whether - // we check for OUT_OF_ORDER_ITERATION. Proper on::demand code should never trigger this error. -#if SIMDJSON_DEVELOPMENT_CHECKS - if (!iter.at_root()) { return OUT_OF_ORDER_ITERATION; } -#endif - // assert_at_root() serves two purposes: in Debug mode, whether or not - // SIMDJSON_DEVELOPMENT_CHECKS is set or not, it checks that we are at the root of - // the document (this will typically be redundant). In release mode, it generates - // SIMDJSON_ASSUME statements to allow the compiler to make assumptions. - iter.assert_at_root(); - switch (*iter.peek()) { - case '[': { - // The following lines check that the document ends with ]. - auto value_iterator = get_root_value_iterator(); - auto error = value_iterator.check_root_array(); - if(error) { return error; } - return value(get_root_value_iterator()); - } - case '{': { - // The following lines would check that the document ends with }. - auto value_iterator = get_root_value_iterator(); - auto error = value_iterator.check_root_object(); - if(error) { return error; } - return value(get_root_value_iterator()); - } - default: - // Unfortunately, scalar documents are a special case in simdjson and they cannot - // be safely converted to value instances. - return SCALAR_DOCUMENT_AS_VALUE; - } +simdjson_inline simdjson_result value::get_double_in_string() noexcept { + return iter.get_double_in_string(); } -simdjson_inline simdjson_result document::get_array() & noexcept { - auto value = get_root_value_iterator(); - return array::start_root(value); +simdjson_inline simdjson_result value::get_uint64() noexcept { + return iter.get_uint64(); } -simdjson_inline simdjson_result document::get_object() & noexcept { - auto value = get_root_value_iterator(); - return object::start_root(value); +simdjson_inline simdjson_result value::get_uint64_in_string() noexcept { + return iter.get_uint64_in_string(); } - -/** - * We decided that calling 'get_double()' on the JSON document '1.233 blabla' should - * give an error, so we check for trailing content. We want to disallow trailing - * content. - * Thus, in several implementations below, we pass a 'true' parameter value to - * a get_root_value_iterator() method: this indicates that we disallow trailing content. - */ - -simdjson_inline simdjson_result document::get_uint64() noexcept { - return get_root_value_iterator().get_root_uint64(true); +simdjson_inline simdjson_result value::get_int64() noexcept { + return iter.get_int64(); } -simdjson_inline simdjson_result document::get_uint64_in_string() noexcept { - return get_root_value_iterator().get_root_uint64_in_string(true); +simdjson_inline simdjson_result value::get_int64_in_string() noexcept { + return iter.get_int64_in_string(); } -simdjson_inline simdjson_result document::get_int64() noexcept { - return get_root_value_iterator().get_root_int64(true); +simdjson_inline simdjson_result value::get_bool() noexcept { + return iter.get_bool(); } -simdjson_inline simdjson_result document::get_int64_in_string() noexcept { - return get_root_value_iterator().get_root_int64_in_string(true); +simdjson_inline simdjson_result value::is_null() noexcept { + return iter.is_null(); } -simdjson_inline simdjson_result document::get_double() noexcept { - return get_root_value_iterator().get_root_double(true); + +template<> simdjson_inline simdjson_result value::get() noexcept { return get_array(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_object(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_number(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_double(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result value::get() noexcept { return get_bool(); } + + +template<> simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); } +template<> simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); } +template<> simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); } +template<> simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); } +template<> simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); } +template<> simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); } +template<> simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); } +template<> simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); } +template<> simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); } + +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline value::operator T() noexcept(false) { + return get(); } -simdjson_inline simdjson_result document::get_double_in_string() noexcept { - return get_root_value_iterator().get_root_double_in_string(true); +simdjson_inline value::operator array() noexcept(false) { + return get_array(); } -simdjson_inline simdjson_result document::get_string(bool allow_replacement) noexcept { - return get_root_value_iterator().get_root_string(true, allow_replacement); +simdjson_inline value::operator object() noexcept(false) { + return get_object(); } -template -simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept { - return get_root_value_iterator().get_root_string(receiver, true, allow_replacement); +simdjson_inline value::operator uint64_t() noexcept(false) { + return get_uint64(); } -simdjson_inline simdjson_result document::get_wobbly_string() noexcept { - return get_root_value_iterator().get_root_wobbly_string(true); +simdjson_inline value::operator int64_t() noexcept(false) { + return get_int64(); } -simdjson_inline simdjson_result document::get_raw_json_string() noexcept { - return get_root_value_iterator().get_root_raw_json_string(true); +simdjson_inline value::operator double() noexcept(false) { + return get_double(); } -simdjson_inline simdjson_result document::get_bool() noexcept { - return get_root_value_iterator().get_root_bool(true); +simdjson_inline value::operator std::string_view() noexcept(false) { + return get_string(false); } -simdjson_inline simdjson_result document::is_null() noexcept { - return get_root_value_iterator().is_root_null(true); +simdjson_inline value::operator raw_json_string() noexcept(false) { + return get_raw_json_string(); } +simdjson_inline value::operator bool() noexcept(false) { + return get_bool(); +} +#endif -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_array(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_object(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_raw_json_string(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_string(false); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_double(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_uint64(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_int64(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } -template<> simdjson_inline simdjson_result document::get() & noexcept { return get_value(); } - -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_raw_json_string(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } -template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_value(); } - -template simdjson_inline error_code document::get(T &out) & noexcept { - return get().get(out); +simdjson_inline simdjson_result value::begin() & noexcept { + return get_array().begin(); } -template simdjson_deprecated simdjson_inline error_code document::get(T &out) && noexcept { - return std::forward(*this).get().get(out); +simdjson_inline simdjson_result value::end() & noexcept { + return {}; } - -#if SIMDJSON_EXCEPTIONS -template -simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get(); } -template -simdjson_inline document::operator T() & noexcept(false) { return get(); } -simdjson_inline document::operator array() & noexcept(false) { return get_array(); } -simdjson_inline document::operator object() & noexcept(false) { return get_object(); } -simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); } -simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); } -simdjson_inline document::operator double() noexcept(false) { return get_double(); } -simdjson_inline document::operator std::string_view() noexcept(false) { return get_string(false); } -simdjson_inline document::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } -simdjson_inline document::operator bool() noexcept(false) { return get_bool(); } -simdjson_inline document::operator value() noexcept(false) { return get_value(); } - -#endif -simdjson_inline simdjson_result document::count_elements() & noexcept { +simdjson_inline simdjson_result value::count_elements() & noexcept { + simdjson_result answer; auto a = get_array(); - simdjson_result answer = a.count_elements(); - /* If there was an array, we are now left pointing at its first element. */ - if(answer.error() == SUCCESS) { rewind(); } + answer = a.count_elements(); + // count_elements leaves you pointing inside the array, at the first element. + // We need to move back so that the user can create a new array (which requires that + // we point at '['). + iter.move_at_start(); return answer; } -simdjson_inline simdjson_result document::count_fields() & noexcept { +simdjson_inline simdjson_result value::count_fields() & noexcept { + simdjson_result answer; auto a = get_object(); - simdjson_result answer = a.count_fields(); - /* If there was an object, we are now left pointing at its first element. */ - if(answer.error() == SUCCESS) { rewind(); } + answer = a.count_fields(); + iter.move_at_start(); return answer; } -simdjson_inline simdjson_result document::at(size_t index) & noexcept { +simdjson_inline simdjson_result value::at(size_t index) noexcept { auto a = get_array(); return a.at(index); } -simdjson_inline simdjson_result document::begin() & noexcept { - return get_array().begin(); -} -simdjson_inline simdjson_result document::end() & noexcept { - return {}; -} -simdjson_inline simdjson_result document::find_field(std::string_view key) & noexcept { +simdjson_inline simdjson_result value::find_field(std::string_view key) noexcept { return start_or_resume_object().find_field(key); } -simdjson_inline simdjson_result document::find_field(const char *key) & noexcept { +simdjson_inline simdjson_result value::find_field(const char *key) noexcept { return start_or_resume_object().find_field(key); } -simdjson_inline simdjson_result document::find_field_unordered(std::string_view key) & noexcept { + +simdjson_inline simdjson_result value::find_field_unordered(std::string_view key) noexcept { return start_or_resume_object().find_field_unordered(key); } -simdjson_inline simdjson_result document::find_field_unordered(const char *key) & noexcept { +simdjson_inline simdjson_result value::find_field_unordered(const char *key) noexcept { return start_or_resume_object().find_field_unordered(key); } -simdjson_inline simdjson_result document::operator[](std::string_view key) & noexcept { + +simdjson_inline simdjson_result value::operator[](std::string_view key) noexcept { return start_or_resume_object()[key]; } -simdjson_inline simdjson_result document::operator[](const char *key) & noexcept { +simdjson_inline simdjson_result value::operator[](const char *key) noexcept { return start_or_resume_object()[key]; } -simdjson_inline error_code document::consume() noexcept { - auto error = iter.skip_child(0); - if(error) { iter.abandon(); } - return error; -} - -simdjson_inline simdjson_result document::raw_json() noexcept { - auto _iter = get_root_value_iterator(); - const uint8_t * starting_point{_iter.peek_start()}; - auto error = consume(); - if(error) { return error; } - // After 'consume()', we could be left pointing just beyond the document, but that - // is ok because we are not going to dereference the final pointer position, we just - // use it to compute the length in bytes. - const uint8_t * final_point{iter.unsafe_pointer()}; - return std::string_view(reinterpret_cast(starting_point), size_t(final_point - starting_point)); -} - -simdjson_inline simdjson_result document::type() noexcept { - return get_root_value_iterator().type(); +simdjson_inline simdjson_result value::type() noexcept { + return iter.type(); } -simdjson_inline simdjson_result document::is_scalar() noexcept { +simdjson_inline simdjson_result value::is_scalar() noexcept { json_type this_type; auto error = type().get(this_type); if(error) { return error; } return ! ((this_type == json_type::array) || (this_type == json_type::object)); } -simdjson_inline simdjson_result document::is_string() noexcept { +simdjson_inline simdjson_result value::is_string() noexcept { json_type this_type; auto error = type().get(this_type); if(error) { return error; } return (this_type == json_type::string); } -simdjson_inline bool document::is_negative() noexcept { - return get_root_value_iterator().is_root_negative(); + +simdjson_inline bool value::is_negative() noexcept { + return iter.is_negative(); } -simdjson_inline simdjson_result document::is_integer() noexcept { - return get_root_value_iterator().is_root_integer(true); +simdjson_inline simdjson_result value::is_integer() noexcept { + return iter.is_integer(); +} +simdjson_warn_unused simdjson_inline simdjson_result value::get_number_type() noexcept { + return iter.get_number_type(); +} +simdjson_warn_unused simdjson_inline simdjson_result value::get_number() noexcept { + return iter.get_number(); } -simdjson_inline simdjson_result document::get_number_type() noexcept { - return get_root_value_iterator().get_root_number_type(true); +simdjson_inline std::string_view value::raw_json_token() noexcept { + return std::string_view(reinterpret_cast(iter.peek_start()), iter.peek_start_length()); } -simdjson_inline simdjson_result document::get_number() noexcept { - return get_root_value_iterator().get_root_number(true); +simdjson_inline simdjson_result value::raw_json() noexcept { + json_type t; + SIMDJSON_TRY(type().get(t)); + switch (t) + { + case json_type::array: { + ondemand::array array; + SIMDJSON_TRY(get_array().get(array)); + return array.raw_json(); + } + case json_type::object: { + ondemand::object object; + SIMDJSON_TRY(get_object().get(object)); + return object.raw_json(); + } + default: + return raw_json_token(); + } } +simdjson_inline simdjson_result value::current_location() noexcept { + return iter.json_iter().current_location(); +} -simdjson_inline simdjson_result document::raw_json_token() noexcept { - auto _iter = get_root_value_iterator(); - return std::string_view(reinterpret_cast(_iter.peek_start()), _iter.peek_root_length()); +simdjson_inline int32_t value::current_depth() const noexcept{ + return iter.json_iter().depth(); } -simdjson_inline simdjson_result document::at_pointer(std::string_view json_pointer) noexcept { - rewind(); // Rewind the document each time at_pointer is called - if (json_pointer.empty()) { - return this->get_value(); +inline bool is_pointer_well_formed(std::string_view json_pointer) noexcept { + if (simdjson_unlikely(json_pointer.empty())) { // can't be + return false; + } + if (simdjson_unlikely(json_pointer[0] != '/')) { + return false; + } + size_t escape = json_pointer.find('~'); + if (escape == std::string_view::npos) { + return true; + } + if (escape == json_pointer.size() - 1) { + return false; } + if (json_pointer[escape + 1] != '0' && json_pointer[escape + 1] != '1') { + return false; + } + return true; +} + +simdjson_inline simdjson_result value::at_pointer(std::string_view json_pointer) noexcept { json_type t; SIMDJSON_TRY(type().get(t)); switch (t) @@ -113477,15 +117859,15 @@ simdjson_inline simdjson_result document::at_pointer(std::string_view jso case json_type::object: return (*this).get_object().at_pointer(json_pointer); default: + // a non-empty string can be invalid, or accessing a primitive (issue 2154) + if (is_pointer_well_formed(json_pointer)) { + return NO_SUCH_FIELD; + } return INVALID_JSON_POINTER; } } -simdjson_inline simdjson_result document::at_path(std::string_view json_path) noexcept { - rewind(); // Rewind the document each time at_pointer is called - if (json_path.empty()) { - return this->get_value(); - } +simdjson_inline simdjson_result value::at_path(std::string_view json_path) noexcept { json_type t; SIMDJSON_TRY(type().get(t)); switch (t) { @@ -113504,680 +117886,1319 @@ simdjson_inline simdjson_result document::at_path(std::string_view json_p namespace simdjson { -simdjson_inline simdjson_result::simdjson_result( - lasx::ondemand::document &&value +simdjson_inline simdjson_result::simdjson_result( + lasx::ondemand::value &&value ) noexcept : - implementation_simdjson_result_base( - std::forward(value) + implementation_simdjson_result_base( + std::forward(value) ) { } -simdjson_inline simdjson_result::simdjson_result( +simdjson_inline simdjson_result::simdjson_result( error_code error ) noexcept : - implementation_simdjson_result_base( - error - ) + implementation_simdjson_result_base(error) { } -simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { if (error()) { return error(); } return first.count_elements(); } -simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { if (error()) { return error(); } return first.count_fields(); } -simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { +simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { if (error()) { return error(); } return first.at(index); } -simdjson_inline error_code simdjson_result::rewind() noexcept { - if (error()) { return error(); } - first.rewind(); - return SUCCESS; -} -simdjson_inline simdjson_result simdjson_result::begin() & noexcept { +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { if (error()) { return error(); } return first.begin(); } -simdjson_inline simdjson_result simdjson_result::end() & noexcept { +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + if (error()) { return error(); } return {}; } -simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) noexcept { if (error()) { return error(); } - return first.find_field_unordered(key); + return first.find_field(key); } -simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) noexcept { if (error()) { return error(); } - return first.find_field_unordered(key); + return first.find_field(key); } -simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) noexcept { if (error()) { return error(); } - return first[key]; + return first.find_field_unordered(key); } -simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) noexcept { if (error()) { return error(); } - return first[key]; + return first.find_field_unordered(key); } -simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) noexcept { if (error()) { return error(); } - return first.find_field(key); + return first[key]; } -simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) noexcept { if (error()) { return error(); } - return first.find_field(key); + return first[key]; } -simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { + +simdjson_inline simdjson_result simdjson_result::get_array() noexcept { if (error()) { return error(); } return first.get_array(); } -simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { +simdjson_inline simdjson_result simdjson_result::get_object() noexcept { if (error()) { return error(); } return first.get_object(); } -simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { if (error()) { return error(); } return first.get_uint64(); } -simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { if (error()) { return error(); } return first.get_uint64_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { if (error()) { return error(); } return first.get_int64(); } -simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { if (error()) { return error(); } return first.get_int64_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_double() noexcept { +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { if (error()) { return error(); } return first.get_double(); } -simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { if (error()) { return error(); } return first.get_double_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(allow_replacement); } template -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(receiver, allow_replacement); } -simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { if (error()) { return error(); } return first.get_wobbly_string(); } -simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { if (error()) { return error(); } return first.get_raw_json_string(); } -simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { if (error()) { return error(); } return first.get_bool(); } -simdjson_inline simdjson_result simdjson_result::get_value() noexcept { - if (error()) { return error(); } - return first.get_value(); -} -simdjson_inline simdjson_result simdjson_result::is_null() noexcept { +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { if (error()) { return error(); } return first.is_null(); } -template -simdjson_inline simdjson_result simdjson_result::get() & noexcept { +template<> simdjson_inline error_code simdjson_result::get(lasx::ondemand::value &out) noexcept { if (error()) { return error(); } - return first.get(); + out = first; + return SUCCESS; } -template -simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + +template simdjson_inline simdjson_result simdjson_result::get() noexcept { if (error()) { return error(); } - return std::forward(first).get(); + return first.get(); } -template -simdjson_inline error_code simdjson_result::get(T &out) & noexcept { +template simdjson_inline error_code simdjson_result::get(T &out) noexcept { if (error()) { return error(); } return first.get(out); } -template -simdjson_inline error_code simdjson_result::get(T &out) && noexcept { - if (error()) { return error(); } - return std::forward(first).get(out); -} -template<> simdjson_inline simdjson_result simdjson_result::get() & noexcept = delete; -template<> simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { - if (error()) { return error(); } - return std::forward(first); -} -template<> simdjson_inline error_code simdjson_result::get(lasx::ondemand::document &out) & noexcept = delete; -template<> simdjson_inline error_code simdjson_result::get(lasx::ondemand::document &out) && noexcept { +template<> simdjson_inline simdjson_result simdjson_result::get() noexcept { if (error()) { return error(); } - out = std::forward(first); - return SUCCESS; + return std::move(first); } -simdjson_inline simdjson_result simdjson_result::type() noexcept { +simdjson_inline simdjson_result simdjson_result::type() noexcept { if (error()) { return error(); } return first.type(); } - -simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { if (error()) { return error(); } return first.is_scalar(); } - -simdjson_inline simdjson_result simdjson_result::is_string() noexcept { +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { if (error()) { return error(); } return first.is_string(); } - -simdjson_inline bool simdjson_result::is_negative() noexcept { +simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { if (error()) { return error(); } return first.is_negative(); } - -simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { if (error()) { return error(); } return first.is_integer(); } - -simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { if (error()) { return error(); } return first.get_number_type(); } - -simdjson_inline simdjson_result simdjson_result::get_number() noexcept { +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { if (error()) { return error(); } return first.get_number(); } - - #if SIMDJSON_EXCEPTIONS -template ::value == false>::type> -simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; -} -simdjson_inline simdjson_result::operator lasx::ondemand::array() & noexcept(false) { +template +simdjson_inline simdjson_result::operator T() noexcept(false) { if (error()) { throw simdjson_error(error()); } - return first; + return first.get(); } -simdjson_inline simdjson_result::operator lasx::ondemand::object() & noexcept(false) { +simdjson_inline simdjson_result::operator lasx::ondemand::array() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { +simdjson_inline simdjson_result::operator lasx::ondemand::object() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator int64_t() noexcept(false) { +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator double() noexcept(false) { +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { +simdjson_inline simdjson_result::operator double() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator lasx::ondemand::raw_json_string() noexcept(false) { +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator bool() noexcept(false) { +simdjson_inline simdjson_result::operator lasx::ondemand::raw_json_string() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator lasx::ondemand::value() noexcept(false) { +simdjson_inline simdjson_result::operator bool() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } #endif - -simdjson_inline simdjson_result simdjson_result::current_location() noexcept { +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { if (error()) { return error(); } - return first.current_location(); + return first.raw_json_token(); } -simdjson_inline bool simdjson_result::at_end() const noexcept { +simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { if (error()) { return error(); } - return first.at_end(); + return first.raw_json(); } - -simdjson_inline int32_t simdjson_result::current_depth() const noexcept { +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { if (error()) { return error(); } - return first.current_depth(); + return first.current_location(); } -simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { +simdjson_inline simdjson_result simdjson_result::current_depth() const noexcept { if (error()) { return error(); } - return first.raw_json_token(); + return first.current_depth(); } -simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { - if (error()) { return error(); } +simdjson_inline simdjson_result simdjson_result::at_pointer( + std::string_view json_pointer) noexcept { + if (error()) { + return error(); + } return first.at_pointer(json_pointer); } -simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { return error(); } +simdjson_inline simdjson_result simdjson_result::at_path( + std::string_view json_path) noexcept { + if (error()) { + return error(); + } return first.at_path(json_path); } } // namespace simdjson +#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H +/* end file simdjson/generic/ondemand/value-inl.h for lasx */ +/* including simdjson/generic/ondemand/document-inl.h for lasx: #include "simdjson/generic/ondemand/document-inl.h" */ +/* begin file simdjson/generic/ondemand/document-inl.h for lasx */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value_iterator-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/deserialize.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ namespace simdjson { namespace lasx { namespace ondemand { -simdjson_inline document_reference::document_reference() noexcept : doc{nullptr} {} -simdjson_inline document_reference::document_reference(document &d) noexcept : doc(&d) {} -simdjson_inline void document_reference::rewind() noexcept { doc->rewind(); } -simdjson_inline simdjson_result document_reference::get_array() & noexcept { return doc->get_array(); } -simdjson_inline simdjson_result document_reference::get_object() & noexcept { return doc->get_object(); } -/** - * The document_reference instances are used primarily/solely for streams of JSON - * documents. - * We decided that calling 'get_double()' on the JSON document '1.233 blabla' should - * give an error, so we check for trailing content. - * - * However, for streams of JSON documents, we want to be able to start from - * "321" "321" "321" - * and parse it successfully as a stream of JSON documents, calling get_uint64_in_string() - * successfully each time. - * - * To achieve this result, we pass a 'false' to a get_root_value_iterator() method: - * this indicates that we allow trailing content. - */ -simdjson_inline simdjson_result document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); } -simdjson_inline simdjson_result document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); } -simdjson_inline simdjson_result document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); } -simdjson_inline simdjson_result document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); } -simdjson_inline simdjson_result document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); } -simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } -simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } -template -simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); } -simdjson_inline simdjson_result document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } -simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } -simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } -simdjson_inline simdjson_result document_reference::get_value() noexcept { return doc->get_value(); } -simdjson_inline simdjson_result document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_array(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_object(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_raw_json_string(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_string(false); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_double(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_uint64(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_int64(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_bool(); } -template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_value(); } -#if SIMDJSON_EXCEPTIONS -template -simdjson_inline document_reference::operator T() noexcept(false) { return get(); } -simdjson_inline document_reference::operator array() & noexcept(false) { return array(*doc); } -simdjson_inline document_reference::operator object() & noexcept(false) { return object(*doc); } -simdjson_inline document_reference::operator uint64_t() noexcept(false) { return get_uint64(); } -simdjson_inline document_reference::operator int64_t() noexcept(false) { return get_int64(); } -simdjson_inline document_reference::operator double() noexcept(false) { return get_double(); } -simdjson_inline document_reference::operator std::string_view() noexcept(false) { return std::string_view(*doc); } -simdjson_inline document_reference::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } -simdjson_inline document_reference::operator bool() noexcept(false) { return get_bool(); } -simdjson_inline document_reference::operator value() noexcept(false) { return value(*doc); } -#endif -simdjson_inline simdjson_result document_reference::count_elements() & noexcept { return doc->count_elements(); } -simdjson_inline simdjson_result document_reference::count_fields() & noexcept { return doc->count_fields(); } -simdjson_inline simdjson_result document_reference::at(size_t index) & noexcept { return doc->at(index); } -simdjson_inline simdjson_result document_reference::begin() & noexcept { return doc->begin(); } -simdjson_inline simdjson_result document_reference::end() & noexcept { return doc->end(); } -simdjson_inline simdjson_result document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); } -simdjson_inline simdjson_result document_reference::find_field(const char *key) & noexcept { return doc->find_field(key); } -simdjson_inline simdjson_result document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; } -simdjson_inline simdjson_result document_reference::operator[](const char *key) & noexcept { return (*doc)[key]; } -simdjson_inline simdjson_result document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); } -simdjson_inline simdjson_result document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); } -simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } -simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } -simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } -simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } -simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } -simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } -simdjson_inline simdjson_result document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); } -simdjson_inline simdjson_result document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); } -simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } -simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } -simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } -simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } -simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} -simdjson_inline document_reference::operator document&() const noexcept { return *doc; } +simdjson_inline document::document(ondemand::json_iterator &&_iter) noexcept + : iter{std::forward(_iter)} +{ + logger::log_start_value(iter, "document"); +} -} // namespace ondemand -} // namespace lasx -} // namespace simdjson +simdjson_inline document document::start(json_iterator &&iter) noexcept { + return document(std::forward(iter)); +} +inline void document::rewind() noexcept { + iter.rewind(); +} +inline std::string document::to_debug_string() noexcept { + return iter.to_string(); +} -namespace simdjson { -simdjson_inline simdjson_result::simdjson_result(lasx::ondemand::document_reference value, error_code error) - noexcept : implementation_simdjson_result_base(std::forward(value), error) {} +inline simdjson_result document::current_location() const noexcept { + return iter.current_location(); +} +inline int32_t document::current_depth() const noexcept { + return iter.depth(); +} -simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { - if (error()) { return error(); } - return first.count_elements(); +inline bool document::at_end() const noexcept { + return iter.at_end(); } -simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { - if (error()) { return error(); } - return first.count_fields(); + + +inline bool document::is_alive() noexcept { + return iter.is_alive(); } -simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { - if (error()) { return error(); } - return first.at(index); +simdjson_inline value_iterator document::resume_value_iterator() noexcept { + return value_iterator(&iter, 1, iter.root_position()); } -simdjson_inline error_code simdjson_result::rewind() noexcept { - if (error()) { return error(); } - first.rewind(); - return SUCCESS; +simdjson_inline value_iterator document::get_root_value_iterator() noexcept { + return resume_value_iterator(); } -simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); +simdjson_inline simdjson_result document::start_or_resume_object() noexcept { + if (iter.at_root()) { + return get_object(); + } else { + return object::resume(resume_value_iterator()); + } } -simdjson_inline simdjson_result simdjson_result::end() & noexcept { - return {}; +simdjson_inline simdjson_result document::get_value() noexcept { + // Make sure we start any arrays or objects before returning, so that start_root_() + // gets called. + + // It is the convention throughout the code that the macro `SIMDJSON_DEVELOPMENT_CHECKS` determines whether + // we check for OUT_OF_ORDER_ITERATION. Proper on::demand code should never trigger this error. +#if SIMDJSON_DEVELOPMENT_CHECKS + if (!iter.at_root()) { return OUT_OF_ORDER_ITERATION; } +#endif + // assert_at_root() serves two purposes: in Debug mode, whether or not + // SIMDJSON_DEVELOPMENT_CHECKS is set or not, it checks that we are at the root of + // the document (this will typically be redundant). In release mode, it generates + // SIMDJSON_ASSUME statements to allow the compiler to make assumptions. + iter.assert_at_root(); + switch (*iter.peek()) { + case '[': { + // The following lines check that the document ends with ]. + auto value_iterator = get_root_value_iterator(); + auto error = value_iterator.check_root_array(); + if(error) { return error; } + return value(get_root_value_iterator()); + } + case '{': { + // The following lines would check that the document ends with }. + auto value_iterator = get_root_value_iterator(); + auto error = value_iterator.check_root_object(); + if(error) { return error; } + return value(get_root_value_iterator()); + } + default: + // Unfortunately, scalar documents are a special case in simdjson and they cannot + // be safely converted to value instances. + return SCALAR_DOCUMENT_AS_VALUE; + } } -simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { +simdjson_inline simdjson_result document::get_array() & noexcept { + auto value = get_root_value_iterator(); + return array::start_root(value); +} +simdjson_inline simdjson_result document::get_object() & noexcept { + auto value = get_root_value_iterator(); + return object::start_root(value); +} + +/** + * We decided that calling 'get_double()' on the JSON document '1.233 blabla' should + * give an error, so we check for trailing content. We want to disallow trailing + * content. + * Thus, in several implementations below, we pass a 'true' parameter value to + * a get_root_value_iterator() method: this indicates that we disallow trailing content. + */ + +simdjson_inline simdjson_result document::get_uint64() noexcept { + return get_root_value_iterator().get_root_uint64(true); +} +simdjson_inline simdjson_result document::get_uint64_in_string() noexcept { + return get_root_value_iterator().get_root_uint64_in_string(true); +} +simdjson_inline simdjson_result document::get_int64() noexcept { + return get_root_value_iterator().get_root_int64(true); +} +simdjson_inline simdjson_result document::get_int64_in_string() noexcept { + return get_root_value_iterator().get_root_int64_in_string(true); +} +simdjson_inline simdjson_result document::get_double() noexcept { + return get_root_value_iterator().get_root_double(true); +} +simdjson_inline simdjson_result document::get_double_in_string() noexcept { + return get_root_value_iterator().get_root_double_in_string(true); +} +simdjson_inline simdjson_result document::get_string(bool allow_replacement) noexcept { + return get_root_value_iterator().get_root_string(true, allow_replacement); +} +template +simdjson_inline error_code document::get_string(string_type& receiver, bool allow_replacement) noexcept { + return get_root_value_iterator().get_root_string(receiver, true, allow_replacement); +} +simdjson_inline simdjson_result document::get_wobbly_string() noexcept { + return get_root_value_iterator().get_root_wobbly_string(true); +} +simdjson_inline simdjson_result document::get_raw_json_string() noexcept { + return get_root_value_iterator().get_root_raw_json_string(true); +} +simdjson_inline simdjson_result document::get_bool() noexcept { + return get_root_value_iterator().get_root_bool(true); +} +simdjson_inline simdjson_result document::is_null() noexcept { + return get_root_value_iterator().is_root_null(true); +} + +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_array(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_object(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_double(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } +template<> simdjson_inline simdjson_result document::get() & noexcept { return get_value(); } + +template<> simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); } +template<> simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); } +template<> simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); } +template<> simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); } +template<> simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); } +template<> simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); } +template<> simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); } +template<> simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); } +template<> simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); } + +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_raw_json_string(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_string(false); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_double(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_uint64(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_int64(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return std::forward(*this).get_bool(); } +template<> simdjson_deprecated simdjson_inline simdjson_result document::get() && noexcept { return get_value(); } + +#if SIMDJSON_EXCEPTIONS +template +simdjson_deprecated simdjson_inline document::operator T() && noexcept(false) { return get(); } +template +simdjson_inline document::operator T() & noexcept(false) { return get(); } +simdjson_inline document::operator array() & noexcept(false) { return get_array(); } +simdjson_inline document::operator object() & noexcept(false) { return get_object(); } +simdjson_inline document::operator uint64_t() noexcept(false) { return get_uint64(); } +simdjson_inline document::operator int64_t() noexcept(false) { return get_int64(); } +simdjson_inline document::operator double() noexcept(false) { return get_double(); } +simdjson_inline document::operator std::string_view() noexcept(false) { return get_string(false); } +simdjson_inline document::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } +simdjson_inline document::operator bool() noexcept(false) { return get_bool(); } +simdjson_inline document::operator value() noexcept(false) { return get_value(); } + +#endif +simdjson_inline simdjson_result document::count_elements() & noexcept { + auto a = get_array(); + simdjson_result answer = a.count_elements(); + /* If there was an array, we are now left pointing at its first element. */ + if(answer.error() == SUCCESS) { rewind(); } + return answer; +} +simdjson_inline simdjson_result document::count_fields() & noexcept { + auto a = get_object(); + simdjson_result answer = a.count_fields(); + /* If there was an object, we are now left pointing at its first element. */ + if(answer.error() == SUCCESS) { rewind(); } + return answer; +} +simdjson_inline simdjson_result document::at(size_t index) & noexcept { + auto a = get_array(); + return a.at(index); +} +simdjson_inline simdjson_result document::begin() & noexcept { + return get_array().begin(); +} +simdjson_inline simdjson_result document::end() & noexcept { + return {}; +} + +simdjson_inline simdjson_result document::find_field(std::string_view key) & noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result document::find_field(const char *key) & noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_inline simdjson_result document::find_field_unordered(std::string_view key) & noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result document::find_field_unordered(const char *key) & noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_inline simdjson_result document::operator[](std::string_view key) & noexcept { + return start_or_resume_object()[key]; +} +simdjson_inline simdjson_result document::operator[](const char *key) & noexcept { + return start_or_resume_object()[key]; +} + +simdjson_inline error_code document::consume() noexcept { + bool scalar = false; + auto error = is_scalar().get(scalar); + if(error) { return error; } + if(scalar) { + iter.return_current_and_advance(); + return SUCCESS; + } + error = iter.skip_child(0); + if(error) { iter.abandon(); } + return error; +} + +simdjson_inline simdjson_result document::raw_json() noexcept { + auto _iter = get_root_value_iterator(); + const uint8_t * starting_point{_iter.peek_start()}; + auto error = consume(); + if(error) { return error; } + // After 'consume()', we could be left pointing just beyond the document, but that + // is ok because we are not going to dereference the final pointer position, we just + // use it to compute the length in bytes. + const uint8_t * final_point{iter.unsafe_pointer()}; + return std::string_view(reinterpret_cast(starting_point), size_t(final_point - starting_point)); +} + +simdjson_inline simdjson_result document::type() noexcept { + return get_root_value_iterator().type(); +} + +simdjson_inline simdjson_result document::is_scalar() noexcept { + // For more speed, we could do: + // return iter.is_single_token(); + json_type this_type; + auto error = type().get(this_type); + if(error) { return error; } + return ! ((this_type == json_type::array) || (this_type == json_type::object)); +} + +simdjson_inline simdjson_result document::is_string() noexcept { + json_type this_type; + auto error = type().get(this_type); + if(error) { return error; } + return (this_type == json_type::string); +} + +simdjson_inline bool document::is_negative() noexcept { + return get_root_value_iterator().is_root_negative(); +} + +simdjson_inline simdjson_result document::is_integer() noexcept { + return get_root_value_iterator().is_root_integer(true); +} + +simdjson_inline simdjson_result document::get_number_type() noexcept { + return get_root_value_iterator().get_root_number_type(true); +} + +simdjson_inline simdjson_result document::get_number() noexcept { + return get_root_value_iterator().get_root_number(true); +} + + +simdjson_inline simdjson_result document::raw_json_token() noexcept { + auto _iter = get_root_value_iterator(); + return std::string_view(reinterpret_cast(_iter.peek_start()), _iter.peek_root_length()); +} + +simdjson_inline simdjson_result document::at_pointer(std::string_view json_pointer) noexcept { + rewind(); // Rewind the document each time at_pointer is called + if (json_pointer.empty()) { + return this->get_value(); + } + json_type t; + SIMDJSON_TRY(type().get(t)); + switch (t) + { + case json_type::array: + return (*this).get_array().at_pointer(json_pointer); + case json_type::object: + return (*this).get_object().at_pointer(json_pointer); + default: + return INVALID_JSON_POINTER; + } +} + +simdjson_inline simdjson_result document::at_path(std::string_view json_path) noexcept { + rewind(); // Rewind the document each time at_pointer is called + if (json_path.empty()) { + return this->get_value(); + } + json_type t; + SIMDJSON_TRY(type().get(t)); + switch (t) { + case json_type::array: + return (*this).get_array().at_path(json_path); + case json_type::object: + return (*this).get_object().at_path(json_path); + default: + return INVALID_JSON_POINTER; + } +} + +} // namespace ondemand +} // namespace lasx +} // namespace simdjson + +namespace simdjson { + +simdjson_inline simdjson_result::simdjson_result( + lasx::ondemand::document &&value +) noexcept : + implementation_simdjson_result_base( + std::forward(value) + ) +{ +} +simdjson_inline simdjson_result::simdjson_result( + error_code error +) noexcept : + implementation_simdjson_result_base( + error + ) +{ +} +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline error_code simdjson_result::rewind() noexcept { + if (error()) { return error(); } + first.rewind(); + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { if (error()) { return error(); } return first.find_field_unordered(key); } -simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { if (error()) { return error(); } return first.find_field_unordered(key); } -simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { if (error()) { return error(); } return first[key]; } -simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { if (error()) { return error(); } return first[key]; } -simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { if (error()) { return error(); } return first.find_field(key); } -simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { if (error()) { return error(); } return first.find_field(key); } -simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { +simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { if (error()) { return error(); } return first.get_array(); } -simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { +simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { if (error()) { return error(); } return first.get_object(); } -simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { if (error()) { return error(); } return first.get_uint64(); } -simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { if (error()) { return error(); } return first.get_uint64_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { if (error()) { return error(); } return first.get_int64(); } -simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { if (error()) { return error(); } return first.get_int64_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_double() noexcept { +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { if (error()) { return error(); } return first.get_double(); } -simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { if (error()) { return error(); } return first.get_double_in_string(); } -simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(allow_replacement); } template -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { +simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { if (error()) { return error(); } return first.get_string(receiver, allow_replacement); } -simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { if (error()) { return error(); } return first.get_wobbly_string(); } -simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { if (error()) { return error(); } return first.get_raw_json_string(); } -simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { if (error()) { return error(); } return first.get_bool(); } -simdjson_inline simdjson_result simdjson_result::get_value() noexcept { +simdjson_inline simdjson_result simdjson_result::get_value() noexcept { if (error()) { return error(); } return first.get_value(); } -simdjson_inline simdjson_result simdjson_result::is_null() noexcept { +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { if (error()) { return error(); } return first.is_null(); } -simdjson_inline simdjson_result simdjson_result::type() noexcept { + +template +simdjson_inline simdjson_result simdjson_result::get() & noexcept { + if (error()) { return error(); } + return first.get(); +} +template +simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first).get(); +} +template +simdjson_inline error_code simdjson_result::get(T &out) & noexcept { + if (error()) { return error(); } + return first.get(out); +} +template +simdjson_inline error_code simdjson_result::get(T &out) && noexcept { + if (error()) { return error(); } + return std::forward(first).get(out); +} + +template<> simdjson_inline simdjson_result simdjson_result::get() & noexcept = delete; +template<> simdjson_deprecated simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first); +} +template<> simdjson_inline error_code simdjson_result::get(lasx::ondemand::document &out) & noexcept = delete; +template<> simdjson_inline error_code simdjson_result::get(lasx::ondemand::document &out) && noexcept { + if (error()) { return error(); } + out = std::forward(first); + return SUCCESS; +} + +simdjson_inline simdjson_result simdjson_result::type() noexcept { if (error()) { return error(); } return first.type(); } -simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { if (error()) { return error(); } return first.is_scalar(); } -simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { if (error()) { return error(); } return first.is_string(); } -simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { + +simdjson_inline bool simdjson_result::is_negative() noexcept { if (error()) { return error(); } return first.is_negative(); } -simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { if (error()) { return error(); } return first.is_integer(); } -simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { if (error()) { return error(); } return first.get_number_type(); } -simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { if (error()) { return error(); } return first.get_number(); } + + #if SIMDJSON_EXCEPTIONS -template ::value == false>::type> -simdjson_inline simdjson_result::operator T() noexcept(false) { +template ::value == false>::type> +simdjson_inline simdjson_result::operator T() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator lasx::ondemand::array() & noexcept(false) { +simdjson_inline simdjson_result::operator lasx::ondemand::array() & noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator lasx::ondemand::object() & noexcept(false) { +simdjson_inline simdjson_result::operator lasx::ondemand::object() & noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator int64_t() noexcept(false) { +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator double() noexcept(false) { +simdjson_inline simdjson_result::operator double() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator lasx::ondemand::raw_json_string() noexcept(false) { +simdjson_inline simdjson_result::operator lasx::ondemand::raw_json_string() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator bool() noexcept(false) { +simdjson_inline simdjson_result::operator bool() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } -simdjson_inline simdjson_result::operator lasx::ondemand::value() noexcept(false) { +simdjson_inline simdjson_result::operator lasx::ondemand::value() noexcept(false) { if (error()) { throw simdjson_error(error()); } return first; } #endif -simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { if (error()) { return error(); } return first.current_location(); } -simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { +simdjson_inline bool simdjson_result::at_end() const noexcept { + if (error()) { return error(); } + return first.at_end(); +} + + +simdjson_inline int32_t simdjson_result::current_depth() const noexcept { + if (error()) { return error(); } + return first.current_depth(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { if (error()) { return error(); } return first.raw_json_token(); } -simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { if (error()) { return error(); } return first.at_pointer(json_pointer); } -simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { - if (error()) { - return error(); - } +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { return error(); } return first.at_path(json_path); } } // namespace simdjson -#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H -/* end file simdjson/generic/ondemand/document-inl.h for lasx */ -/* including simdjson/generic/ondemand/document_stream-inl.h for lasx: #include "simdjson/generic/ondemand/document_stream-inl.h" */ -/* begin file simdjson/generic/ondemand/document_stream-inl.h for lasx */ -#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H - -/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ -/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document_stream.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */ -/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -#include -#include namespace simdjson { namespace lasx { namespace ondemand { -#ifdef SIMDJSON_THREADS_ENABLED - -inline void stage1_worker::finish() { - // After calling "run" someone would call finish() to wait - // for the end of the processing. - // This function will wait until either the thread has done - // the processing or, else, the destructor has been called. - std::unique_lock lock(locking_mutex); - cond_var.wait(lock, [this]{return has_work == false;}); -} - -inline stage1_worker::~stage1_worker() { - // The thread may never outlive the stage1_worker instance - // and will always be stopped/joined before the stage1_worker - // instance is gone. - stop_thread(); -} - -inline void stage1_worker::start_thread() { - std::unique_lock lock(locking_mutex); - if(thread.joinable()) { - return; // This should never happen but we never want to create more than one thread. - } - thread = std::thread([this]{ - while(true) { - std::unique_lock thread_lock(locking_mutex); - // We wait for either "run" or "stop_thread" to be called. - cond_var.wait(thread_lock, [this]{return has_work || !can_work;}); - // If, for some reason, the stop_thread() method was called (i.e., the - // destructor of stage1_worker is called, then we want to immediately destroy - // the thread (and not do any more processing). - if(!can_work) { - break; - } - this->owner->stage1_thread_error = this->owner->run_stage1(*this->stage1_thread_parser, - this->_next_batch_start); - this->has_work = false; - // The condition variable call should be moved after thread_lock.unlock() for performance - // reasons but thread sanitizers may report it as a data race if we do. - // See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock - cond_var.notify_one(); // will notify "finish" - thread_lock.unlock(); - } - } - ); -} - - -inline void stage1_worker::stop_thread() { - std::unique_lock lock(locking_mutex); - // We have to make sure that all locks can be released. - can_work = false; - has_work = false; - cond_var.notify_all(); - lock.unlock(); - if(thread.joinable()) { - thread.join(); - } -} - -inline void stage1_worker::run(document_stream * ds, parser * stage1, size_t next_batch_start) { - std::unique_lock lock(locking_mutex); - owner = ds; - _next_batch_start = next_batch_start; - stage1_thread_parser = stage1; - has_work = true; - // The condition variable call should be moved after thread_lock.unlock() for performance - // reasons but thread sanitizers may report it as a data race if we do. - // See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock - cond_var.notify_one(); // will notify the thread lock that we have work - lock.unlock(); -} - -#endif // SIMDJSON_THREADS_ENABLED - -simdjson_inline document_stream::document_stream( - ondemand::parser &_parser, - const uint8_t *_buf, +simdjson_inline document_reference::document_reference() noexcept : doc{nullptr} {} +simdjson_inline document_reference::document_reference(document &d) noexcept : doc(&d) {} +simdjson_inline void document_reference::rewind() noexcept { doc->rewind(); } +simdjson_inline simdjson_result document_reference::get_array() & noexcept { return doc->get_array(); } +simdjson_inline simdjson_result document_reference::get_object() & noexcept { return doc->get_object(); } +/** + * The document_reference instances are used primarily/solely for streams of JSON + * documents. + * We decided that calling 'get_double()' on the JSON document '1.233 blabla' should + * give an error, so we check for trailing content. + * + * However, for streams of JSON documents, we want to be able to start from + * "321" "321" "321" + * and parse it successfully as a stream of JSON documents, calling get_uint64_in_string() + * successfully each time. + * + * To achieve this result, we pass a 'false' to a get_root_value_iterator() method: + * this indicates that we allow trailing content. + */ +simdjson_inline simdjson_result document_reference::get_uint64() noexcept { return doc->get_root_value_iterator().get_root_uint64(false); } +simdjson_inline simdjson_result document_reference::get_uint64_in_string() noexcept { return doc->get_root_value_iterator().get_root_uint64_in_string(false); } +simdjson_inline simdjson_result document_reference::get_int64() noexcept { return doc->get_root_value_iterator().get_root_int64(false); } +simdjson_inline simdjson_result document_reference::get_int64_in_string() noexcept { return doc->get_root_value_iterator().get_root_int64_in_string(false); } +simdjson_inline simdjson_result document_reference::get_double() noexcept { return doc->get_root_value_iterator().get_root_double(false); } +simdjson_inline simdjson_result document_reference::get_double_in_string() noexcept { return doc->get_root_value_iterator().get_root_double(false); } +simdjson_inline simdjson_result document_reference::get_string(bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(false, allow_replacement); } +template +simdjson_inline error_code document_reference::get_string(string_type& receiver, bool allow_replacement) noexcept { return doc->get_root_value_iterator().get_root_string(receiver, false, allow_replacement); } +simdjson_inline simdjson_result document_reference::get_wobbly_string() noexcept { return doc->get_root_value_iterator().get_root_wobbly_string(false); } +simdjson_inline simdjson_result document_reference::get_raw_json_string() noexcept { return doc->get_root_value_iterator().get_root_raw_json_string(false); } +simdjson_inline simdjson_result document_reference::get_bool() noexcept { return doc->get_root_value_iterator().get_root_bool(false); } +simdjson_inline simdjson_result document_reference::get_value() noexcept { return doc->get_value(); } +simdjson_inline simdjson_result document_reference::is_null() noexcept { return doc->get_root_value_iterator().is_root_null(false); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_array(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_object(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_raw_json_string(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_string(false); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_double(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_uint64(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_int64(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_bool(); } +template<> simdjson_inline simdjson_result document_reference::get() & noexcept { return get_value(); } +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline document_reference::operator T() noexcept(false) { return get(); } +simdjson_inline document_reference::operator array() & noexcept(false) { return array(*doc); } +simdjson_inline document_reference::operator object() & noexcept(false) { return object(*doc); } +simdjson_inline document_reference::operator uint64_t() noexcept(false) { return get_uint64(); } +simdjson_inline document_reference::operator int64_t() noexcept(false) { return get_int64(); } +simdjson_inline document_reference::operator double() noexcept(false) { return get_double(); } +simdjson_inline document_reference::operator std::string_view() noexcept(false) { return std::string_view(*doc); } +simdjson_inline document_reference::operator raw_json_string() noexcept(false) { return get_raw_json_string(); } +simdjson_inline document_reference::operator bool() noexcept(false) { return get_bool(); } +simdjson_inline document_reference::operator value() noexcept(false) { return value(*doc); } +#endif +simdjson_inline simdjson_result document_reference::count_elements() & noexcept { return doc->count_elements(); } +simdjson_inline simdjson_result document_reference::count_fields() & noexcept { return doc->count_fields(); } +simdjson_inline simdjson_result document_reference::at(size_t index) & noexcept { return doc->at(index); } +simdjson_inline simdjson_result document_reference::begin() & noexcept { return doc->begin(); } +simdjson_inline simdjson_result document_reference::end() & noexcept { return doc->end(); } +simdjson_inline simdjson_result document_reference::find_field(std::string_view key) & noexcept { return doc->find_field(key); } +simdjson_inline simdjson_result document_reference::find_field(const char *key) & noexcept { return doc->find_field(key); } +simdjson_inline simdjson_result document_reference::operator[](std::string_view key) & noexcept { return (*doc)[key]; } +simdjson_inline simdjson_result document_reference::operator[](const char *key) & noexcept { return (*doc)[key]; } +simdjson_inline simdjson_result document_reference::find_field_unordered(std::string_view key) & noexcept { return doc->find_field_unordered(key); } +simdjson_inline simdjson_result document_reference::find_field_unordered(const char *key) & noexcept { return doc->find_field_unordered(key); } +simdjson_inline simdjson_result document_reference::type() noexcept { return doc->type(); } +simdjson_inline simdjson_result document_reference::is_scalar() noexcept { return doc->is_scalar(); } +simdjson_inline simdjson_result document_reference::is_string() noexcept { return doc->is_string(); } +simdjson_inline simdjson_result document_reference::current_location() noexcept { return doc->current_location(); } +simdjson_inline int32_t document_reference::current_depth() const noexcept { return doc->current_depth(); } +simdjson_inline bool document_reference::is_negative() noexcept { return doc->is_negative(); } +simdjson_inline simdjson_result document_reference::is_integer() noexcept { return doc->get_root_value_iterator().is_root_integer(false); } +simdjson_inline simdjson_result document_reference::get_number_type() noexcept { return doc->get_root_value_iterator().get_root_number_type(false); } +simdjson_inline simdjson_result document_reference::get_number() noexcept { return doc->get_root_value_iterator().get_root_number(false); } +simdjson_inline simdjson_result document_reference::raw_json_token() noexcept { return doc->raw_json_token(); } +simdjson_inline simdjson_result document_reference::at_pointer(std::string_view json_pointer) noexcept { return doc->at_pointer(json_pointer); } +simdjson_inline simdjson_result document_reference::at_path(std::string_view json_path) noexcept { return doc->at_path(json_path); } +simdjson_inline simdjson_result document_reference::raw_json() noexcept { return doc->raw_json();} +simdjson_inline document_reference::operator document&() const noexcept { return *doc; } + +} // namespace ondemand +} // namespace lasx +} // namespace simdjson + + + +namespace simdjson { +simdjson_inline simdjson_result::simdjson_result(lasx::ondemand::document_reference value, error_code error) + noexcept : implementation_simdjson_result_base(std::forward(value), error) {} + + +simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { + if (error()) { return error(); } + return first.count_elements(); +} +simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { + if (error()) { return error(); } + return first.count_fields(); +} +simdjson_inline simdjson_result simdjson_result::at(size_t index) & noexcept { + if (error()) { return error(); } + return first.at(index); +} +simdjson_inline error_code simdjson_result::rewind() noexcept { + if (error()) { return error(); } + first.rewind(); + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_inline simdjson_result simdjson_result::end() & noexcept { + return {}; +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::operator[](const char *key) & noexcept { + if (error()) { return error(); } + return first[key]; +} +simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_inline simdjson_result simdjson_result::get_array() & noexcept { + if (error()) { return error(); } + return first.get_array(); +} +simdjson_inline simdjson_result simdjson_result::get_object() & noexcept { + if (error()) { return error(); } + return first.get_object(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { + if (error()) { return error(); } + return first.get_uint64(); +} +simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { + if (error()) { return error(); } + return first.get_uint64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { + if (error()) { return error(); } + return first.get_int64(); +} +simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { + if (error()) { return error(); } + return first.get_int64_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_double() noexcept { + if (error()) { return error(); } + return first.get_double(); +} +simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { + if (error()) { return error(); } + return first.get_double_in_string(); +} +simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(allow_replacement); +} +template +simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { + if (error()) { return error(); } + return first.get_string(receiver, allow_replacement); +} +simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { + if (error()) { return error(); } + return first.get_wobbly_string(); +} +simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { + if (error()) { return error(); } + return first.get_raw_json_string(); +} +simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { + if (error()) { return error(); } + return first.get_bool(); +} +simdjson_inline simdjson_result simdjson_result::get_value() noexcept { + if (error()) { return error(); } + return first.get_value(); +} +simdjson_inline simdjson_result simdjson_result::is_null() noexcept { + if (error()) { return error(); } + return first.is_null(); +} +template +simdjson_inline simdjson_result simdjson_result::get() & noexcept { + if (error()) { return error(); } + return first.get(); +} +template +simdjson_inline simdjson_result simdjson_result::get() && noexcept { + if (error()) { return error(); } + return std::forward(first).get(); +} +template +simdjson_inline error_code simdjson_result::get(T &out) & noexcept { + if (error()) { return error(); } + return first.get(out); +} +template +simdjson_inline error_code simdjson_result::get(T &out) && noexcept { + if (error()) { return error(); } + return std::forward(first).get(out); +} +simdjson_inline simdjson_result simdjson_result::type() noexcept { + if (error()) { return error(); } + return first.type(); +} +simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { + if (error()) { return error(); } + return first.is_scalar(); +} +simdjson_inline simdjson_result simdjson_result::is_string() noexcept { + if (error()) { return error(); } + return first.is_string(); +} +template <> +simdjson_inline error_code simdjson_result::get(lasx::ondemand::document_reference &out) & noexcept { + if (error()) { return error(); } + out = first; + return SUCCESS; +} +template <> +simdjson_inline error_code simdjson_result::get(lasx::ondemand::document_reference &out) && noexcept { + if (error()) { return error(); } + out = first; + return SUCCESS; +} +simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { + if (error()) { return error(); } + return first.is_negative(); +} +simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { + if (error()) { return error(); } + return first.is_integer(); +} +simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { + if (error()) { return error(); } + return first.get_number_type(); +} +simdjson_inline simdjson_result simdjson_result::get_number() noexcept { + if (error()) { return error(); } + return first.get_number(); +} +#if SIMDJSON_EXCEPTIONS +template +simdjson_inline simdjson_result::operator T() noexcept(false) { + static_assert(std::is_same::value == false, "You should not call get when T is a document"); + static_assert(std::is_same::value == false, "You should not call get when T is a document"); + if (error()) { throw simdjson_error(error()); } + return first.get(); +} +simdjson_inline simdjson_result::operator lasx::ondemand::array() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator lasx::ondemand::object() & noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator int64_t() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator double() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator lasx::ondemand::raw_json_string() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator bool() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +simdjson_inline simdjson_result::operator lasx::ondemand::value() noexcept(false) { + if (error()) { throw simdjson_error(error()); } + return first; +} +#endif + +simdjson_inline simdjson_result simdjson_result::current_location() noexcept { + if (error()) { return error(); } + return first.current_location(); +} + +simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { + if (error()) { return error(); } + return first.raw_json_token(); +} + +simdjson_inline simdjson_result simdjson_result::at_pointer(std::string_view json_pointer) noexcept { + if (error()) { return error(); } + return first.at_pointer(json_pointer); +} + +simdjson_inline simdjson_result simdjson_result::at_path(std::string_view json_path) noexcept { + if (error()) { + return error(); + } + return first.at_path(json_path); +} + +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_INL_H +/* end file simdjson/generic/ondemand/document-inl.h for lasx */ +/* including simdjson/generic/ondemand/document_stream-inl.h for lasx: #include "simdjson/generic/ondemand/document_stream-inl.h" */ +/* begin file simdjson/generic/ondemand/document_stream-inl.h for lasx */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_DOCUMENT_STREAM_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document_stream.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +#include +#include + +namespace simdjson { +namespace lasx { +namespace ondemand { + +#ifdef SIMDJSON_THREADS_ENABLED + +inline void stage1_worker::finish() { + // After calling "run" someone would call finish() to wait + // for the end of the processing. + // This function will wait until either the thread has done + // the processing or, else, the destructor has been called. + std::unique_lock lock(locking_mutex); + cond_var.wait(lock, [this]{return has_work == false;}); +} + +inline stage1_worker::~stage1_worker() { + // The thread may never outlive the stage1_worker instance + // and will always be stopped/joined before the stage1_worker + // instance is gone. + stop_thread(); +} + +inline void stage1_worker::start_thread() { + std::unique_lock lock(locking_mutex); + if(thread.joinable()) { + return; // This should never happen but we never want to create more than one thread. + } + thread = std::thread([this]{ + while(true) { + std::unique_lock thread_lock(locking_mutex); + // We wait for either "run" or "stop_thread" to be called. + cond_var.wait(thread_lock, [this]{return has_work || !can_work;}); + // If, for some reason, the stop_thread() method was called (i.e., the + // destructor of stage1_worker is called, then we want to immediately destroy + // the thread (and not do any more processing). + if(!can_work) { + break; + } + this->owner->stage1_thread_error = this->owner->run_stage1(*this->stage1_thread_parser, + this->_next_batch_start); + this->has_work = false; + // The condition variable call should be moved after thread_lock.unlock() for performance + // reasons but thread sanitizers may report it as a data race if we do. + // See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock + cond_var.notify_one(); // will notify "finish" + thread_lock.unlock(); + } + } + ); +} + + +inline void stage1_worker::stop_thread() { + std::unique_lock lock(locking_mutex); + // We have to make sure that all locks can be released. + can_work = false; + has_work = false; + cond_var.notify_all(); + lock.unlock(); + if(thread.joinable()) { + thread.join(); + } +} + +inline void stage1_worker::run(document_stream * ds, parser * stage1, size_t next_batch_start) { + std::unique_lock lock(locking_mutex); + owner = ds; + _next_batch_start = next_batch_start; + stage1_thread_parser = stage1; + has_work = true; + // The condition variable call should be moved after thread_lock.unlock() for performance + // reasons but thread sanitizers may report it as a data race if we do. + // See https://stackoverflow.com/questions/35775501/c-should-condition-variable-be-notified-under-lock + cond_var.notify_one(); // will notify the thread lock that we have work + lock.unlock(); +} + +#endif // SIMDJSON_THREADS_ENABLED + +simdjson_inline document_stream::document_stream( + ondemand::parser &_parser, + const uint8_t *_buf, size_t _len, size_t _batch_size, bool _allow_comma_separated @@ -114237,7 +119258,6 @@ simdjson_inline document_stream::iterator::iterator(document_stream* _stream, bo } simdjson_inline simdjson_result document_stream::iterator::operator*() noexcept { - //if(stream->error) { return stream->error; } return simdjson_result(stream->doc, stream->error); } @@ -116096,36 +121116,39 @@ simdjson_inline const char * raw_json_string::raw() const noexcept { return rein simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(std::string_view target) noexcept { size_t pos{0}; - // if the content has no escape character, just scan through it quickly! - for(;pos < target.size() && target[pos] != '\\';pos++) {} - // slow path may begin. - bool escaping{false}; - for(;pos < target.size();pos++) { - if((target[pos] == '"') && !escaping) { - return false; - } else if(target[pos] == '\\') { - escaping = !escaping; - } else { - escaping = false; + while(pos < target.size()) { + pos = target.find('"', pos); + if(pos == std::string_view::npos) { return true; } + if(pos != 0 && target[pos-1] != '\\') { return false; } + if(pos > 1 && target[pos-2] == '\\') { + size_t backslash_count{2}; + for(size_t i = 3; i <= pos; i++) { + if(target[pos-i] == '\\') { backslash_count++; } + else { break; } + } + if(backslash_count % 2 == 0) { return false; } } + pos++; } return true; } simdjson_inline bool raw_json_string::is_free_from_unescaped_quote(const char* target) noexcept { size_t pos{0}; - // if the content has no escape character, just scan through it quickly! - for(;target[pos] && target[pos] != '\\';pos++) {} - // slow path may begin. - bool escaping{false}; - for(;target[pos];pos++) { - if((target[pos] == '"') && !escaping) { - return false; - } else if(target[pos] == '\\') { - escaping = !escaping; - } else { - escaping = false; + while(target[pos]) { + const char * result = strchr(target+pos, '"'); + if(result == nullptr) { return true; } + pos = result - target; + if(pos != 0 && target[pos-1] != '\\') { return false; } + if(pos > 1 && target[pos-2] == '\\') { + size_t backslash_count{2}; + for(size_t i = 3; i <= pos; i++) { + if(target[pos-i] == '\\') { backslash_count++; } + else { break; } + } + if(backslash_count % 2 == 0) { return false; } } + pos++; } return true; } @@ -116137,7 +121160,7 @@ simdjson_inline bool raw_json_string::unsafe_is_equal(size_t length, std::string } simdjson_inline bool raw_json_string::unsafe_is_equal(std::string_view target) const noexcept { - // Assumptions: does not contain unescaped quote characters, and + // Assumptions: does not contain unescaped quote characters("), and // the raw content is quote terminated within a valid JSON string. if(target.size() <= SIMDJSON_PADDING) { return (raw()[target.size()] == '"') && !memcmp(raw(), target.data(), target.size()); @@ -116225,681 +121248,29 @@ simdjson_unused simdjson_inline bool operator!=(const raw_json_string &a, std::s } simdjson_unused simdjson_inline bool operator!=(std::string_view c, const raw_json_string &a) noexcept { - return !(a == c); -} - - -simdjson_inline simdjson_warn_unused simdjson_result raw_json_string::unescape(json_iterator &iter, bool allow_replacement) const noexcept { - return iter.unescape(*this, allow_replacement); -} - -simdjson_inline simdjson_warn_unused simdjson_result raw_json_string::unescape_wobbly(json_iterator &iter) const noexcept { - return iter.unescape_wobbly(*this); -} - -simdjson_unused simdjson_inline std::ostream &operator<<(std::ostream &out, const raw_json_string &str) noexcept { - bool in_escape = false; - const char *s = str.raw(); - while (true) { - switch (*s) { - case '\\': in_escape = !in_escape; break; - case '"': if (in_escape) { in_escape = false; } else { return out; } break; - default: if (in_escape) { in_escape = false; } - } - out << *s; - s++; - } -} - -} // namespace ondemand -} // namespace lasx -} // namespace simdjson - -namespace simdjson { - -simdjson_inline simdjson_result::simdjson_result(lasx::ondemand::raw_json_string &&value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} -simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} - -simdjson_inline simdjson_result simdjson_result::raw() const noexcept { - if (error()) { return error(); } - return first.raw(); -} -simdjson_inline simdjson_warn_unused simdjson_result simdjson_result::unescape(lasx::ondemand::json_iterator &iter, bool allow_replacement) const noexcept { - if (error()) { return error(); } - return first.unescape(iter, allow_replacement); -} -simdjson_inline simdjson_warn_unused simdjson_result simdjson_result::unescape_wobbly(lasx::ondemand::json_iterator &iter) const noexcept { - if (error()) { return error(); } - return first.unescape_wobbly(iter); -} -} // namespace simdjson - -#endif // SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_INL_H -/* end file simdjson/generic/ondemand/raw_json_string-inl.h for lasx */ -/* including simdjson/generic/ondemand/serialization-inl.h for lasx: #include "simdjson/generic/ondemand/serialization-inl.h" */ -/* begin file simdjson/generic/ondemand/serialization-inl.h for lasx */ -#ifndef SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_INL_H - -/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ -/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_INL_H */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document-inl.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/serialization.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ -/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -namespace simdjson { - -inline std::string_view trim(const std::string_view str) noexcept { - // We can almost surely do better by rolling our own find_first_not_of function. - size_t first = str.find_first_not_of(" \t\n\r"); - // If we have the empty string (just white space), then no trimming is possible, and - // we return the empty string_view. - if (std::string_view::npos == first) { return std::string_view(); } - size_t last = str.find_last_not_of(" \t\n\r"); - return str.substr(first, (last - first + 1)); -} - - -inline simdjson_result to_json_string(lasx::ondemand::document& x) noexcept { - std::string_view v; - auto error = x.raw_json().get(v); - if(error) {return error; } - return trim(v); -} - -inline simdjson_result to_json_string(lasx::ondemand::document_reference& x) noexcept { - std::string_view v; - auto error = x.raw_json().get(v); - if(error) {return error; } - return trim(v); -} - -inline simdjson_result to_json_string(lasx::ondemand::value& x) noexcept { - /** - * If we somehow receive a value that has already been consumed, - * then the following code could be in trouble. E.g., we create - * an array as needed, but if an array was already created, then - * it could be bad. - */ - using namespace lasx::ondemand; - lasx::ondemand::json_type t; - auto error = x.type().get(t); - if(error != SUCCESS) { return error; } - switch (t) - { - case json_type::array: - { - lasx::ondemand::array array; - error = x.get_array().get(array); - if(error) { return error; } - return to_json_string(array); - } - case json_type::object: - { - lasx::ondemand::object object; - error = x.get_object().get(object); - if(error) { return error; } - return to_json_string(object); - } - default: - return trim(x.raw_json_token()); - } -} - -inline simdjson_result to_json_string(lasx::ondemand::object& x) noexcept { - std::string_view v; - auto error = x.raw_json().get(v); - if(error) {return error; } - return trim(v); -} - -inline simdjson_result to_json_string(lasx::ondemand::array& x) noexcept { - std::string_view v; - auto error = x.raw_json().get(v); - if(error) {return error; } - return trim(v); -} - -inline simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); -} - -inline simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); -} - -inline simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); -} - -inline simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); -} - -inline simdjson_result to_json_string(simdjson_result x) { - if (x.error()) { return x.error(); } - return to_json_string(x.value_unsafe()); -} -} // namespace simdjson - -namespace simdjson { namespace lasx { namespace ondemand { - -#if SIMDJSON_EXCEPTIONS -inline std::ostream& operator<<(std::ostream& out, simdjson::lasx::ondemand::value x) { - std::string_view v; - auto error = simdjson::to_json_string(x).get(v); - if(error == simdjson::SUCCESS) { - return (out << v); - } else { - throw simdjson::simdjson_error(error); - } -} -inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result x) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); -} -#else -inline std::ostream& operator<<(std::ostream& out, simdjson::lasx::ondemand::value x) { - std::string_view v; - auto error = simdjson::to_json_string(x).get(v); - if(error == simdjson::SUCCESS) { - return (out << v); - } else { - return (out << error); - } -} -#endif - -#if SIMDJSON_EXCEPTIONS -inline std::ostream& operator<<(std::ostream& out, simdjson::lasx::ondemand::array value) { - std::string_view v; - auto error = simdjson::to_json_string(value).get(v); - if(error == simdjson::SUCCESS) { - return (out << v); - } else { - throw simdjson::simdjson_error(error); - } -} -inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result x) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); -} -#else -inline std::ostream& operator<<(std::ostream& out, simdjson::lasx::ondemand::array value) { - std::string_view v; - auto error = simdjson::to_json_string(value).get(v); - if(error == simdjson::SUCCESS) { - return (out << v); - } else { - return (out << error); - } -} -#endif - -#if SIMDJSON_EXCEPTIONS -inline std::ostream& operator<<(std::ostream& out, simdjson::lasx::ondemand::document& value) { - std::string_view v; - auto error = simdjson::to_json_string(value).get(v); - if(error == simdjson::SUCCESS) { - return (out << v); - } else { - throw simdjson::simdjson_error(error); - } -} -inline std::ostream& operator<<(std::ostream& out, simdjson::lasx::ondemand::document_reference& value) { - std::string_view v; - auto error = simdjson::to_json_string(value).get(v); - if(error == simdjson::SUCCESS) { - return (out << v); - } else { - throw simdjson::simdjson_error(error); - } -} -inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result&& x) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); -} -inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result&& x) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); -} -#else -inline std::ostream& operator<<(std::ostream& out, simdjson::lasx::ondemand::document& value) { - std::string_view v; - auto error = simdjson::to_json_string(value).get(v); - if(error == simdjson::SUCCESS) { - return (out << v); - } else { - return (out << error); - } -} -#endif - -#if SIMDJSON_EXCEPTIONS -inline std::ostream& operator<<(std::ostream& out, simdjson::lasx::ondemand::object value) { - std::string_view v; - auto error = simdjson::to_json_string(value).get(v); - if(error == simdjson::SUCCESS) { - return (out << v); - } else { - throw simdjson::simdjson_error(error); - } -} -inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result x) { - if (x.error()) { throw simdjson::simdjson_error(x.error()); } - return (out << x.value()); -} -#else -inline std::ostream& operator<<(std::ostream& out, simdjson::lasx::ondemand::object value) { - std::string_view v; - auto error = simdjson::to_json_string(value).get(v); - if(error == simdjson::SUCCESS) { - return (out << v); - } else { - return (out << error); - } -} -#endif -}}} // namespace simdjson::lasx::ondemand - -#endif // SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_INL_H -/* end file simdjson/generic/ondemand/serialization-inl.h for lasx */ -/* including simdjson/generic/ondemand/token_iterator-inl.h for lasx: #include "simdjson/generic/ondemand/token_iterator-inl.h" */ -/* begin file simdjson/generic/ondemand/token_iterator-inl.h for lasx */ -#ifndef SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H - -/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ -/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/token_iterator.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */ -/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -namespace simdjson { -namespace lasx { -namespace ondemand { - -simdjson_inline token_iterator::token_iterator( - const uint8_t *_buf, - token_position position -) noexcept : buf{_buf}, _position{position} -{ -} - -simdjson_inline uint32_t token_iterator::current_offset() const noexcept { - return *(_position); -} - - -simdjson_inline const uint8_t *token_iterator::return_current_and_advance() noexcept { - return &buf[*(_position++)]; -} - -simdjson_inline const uint8_t *token_iterator::peek(token_position position) const noexcept { - return &buf[*position]; -} -simdjson_inline uint32_t token_iterator::peek_index(token_position position) const noexcept { - return *position; -} -simdjson_inline uint32_t token_iterator::peek_length(token_position position) const noexcept { - return *(position+1) - *position; -} - -simdjson_inline uint32_t token_iterator::peek_root_length(token_position position) const noexcept { - return *(position+2) - *(position) > *(position+1) - *(position) ? - *(position+1) - *(position) - : *(position+2) - *(position); -} -simdjson_inline const uint8_t *token_iterator::peek(int32_t delta) const noexcept { - return &buf[*(_position+delta)]; -} -simdjson_inline uint32_t token_iterator::peek_index(int32_t delta) const noexcept { - return *(_position+delta); -} -simdjson_inline uint32_t token_iterator::peek_length(int32_t delta) const noexcept { - return *(_position+delta+1) - *(_position+delta); -} - -simdjson_inline token_position token_iterator::position() const noexcept { - return _position; -} -simdjson_inline void token_iterator::set_position(token_position target_position) noexcept { - _position = target_position; -} - -simdjson_inline bool token_iterator::operator==(const token_iterator &other) const noexcept { - return _position == other._position; -} -simdjson_inline bool token_iterator::operator!=(const token_iterator &other) const noexcept { - return _position != other._position; -} -simdjson_inline bool token_iterator::operator>(const token_iterator &other) const noexcept { - return _position > other._position; -} -simdjson_inline bool token_iterator::operator>=(const token_iterator &other) const noexcept { - return _position >= other._position; -} -simdjson_inline bool token_iterator::operator<(const token_iterator &other) const noexcept { - return _position < other._position; -} -simdjson_inline bool token_iterator::operator<=(const token_iterator &other) const noexcept { - return _position <= other._position; -} - -} // namespace ondemand -} // namespace lasx -} // namespace simdjson - -namespace simdjson { - -simdjson_inline simdjson_result::simdjson_result(lasx::ondemand::token_iterator &&value) noexcept - : implementation_simdjson_result_base(std::forward(value)) {} -simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept - : implementation_simdjson_result_base(error) {} - -} // namespace simdjson - -#endif // SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H -/* end file simdjson/generic/ondemand/token_iterator-inl.h for lasx */ -/* including simdjson/generic/ondemand/value-inl.h for lasx: #include "simdjson/generic/ondemand/value-inl.h" */ -/* begin file simdjson/generic/ondemand/value-inl.h for lasx */ -#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H - -/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ -/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array_iterator.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_iterator.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/raw_json_string.h" */ -/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ -/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ - -namespace simdjson { -namespace lasx { -namespace ondemand { - -simdjson_inline value::value(const value_iterator &_iter) noexcept - : iter{_iter} -{ -} -simdjson_inline value value::start(const value_iterator &iter) noexcept { - return iter; -} -simdjson_inline value value::resume(const value_iterator &iter) noexcept { - return iter; -} - -simdjson_inline simdjson_result value::get_array() noexcept { - return array::start(iter); -} -simdjson_inline simdjson_result value::get_object() noexcept { - return object::start(iter); -} -simdjson_inline simdjson_result value::start_or_resume_object() noexcept { - if (iter.at_start()) { - return get_object(); - } else { - return object::resume(iter); - } -} - -simdjson_inline simdjson_result value::get_raw_json_string() noexcept { - return iter.get_raw_json_string(); -} -simdjson_inline simdjson_result value::get_string(bool allow_replacement) noexcept { - return iter.get_string(allow_replacement); -} -template -simdjson_inline error_code value::get_string(string_type& receiver, bool allow_replacement) noexcept { - return iter.get_string(receiver, allow_replacement); -} -simdjson_inline simdjson_result value::get_wobbly_string() noexcept { - return iter.get_wobbly_string(); -} -simdjson_inline simdjson_result value::get_double() noexcept { - return iter.get_double(); -} -simdjson_inline simdjson_result value::get_double_in_string() noexcept { - return iter.get_double_in_string(); -} -simdjson_inline simdjson_result value::get_uint64() noexcept { - return iter.get_uint64(); -} -simdjson_inline simdjson_result value::get_uint64_in_string() noexcept { - return iter.get_uint64_in_string(); -} -simdjson_inline simdjson_result value::get_int64() noexcept { - return iter.get_int64(); -} -simdjson_inline simdjson_result value::get_int64_in_string() noexcept { - return iter.get_int64_in_string(); -} -simdjson_inline simdjson_result value::get_bool() noexcept { - return iter.get_bool(); -} -simdjson_inline simdjson_result value::is_null() noexcept { - return iter.is_null(); -} -template<> simdjson_inline simdjson_result value::get() noexcept { return get_array(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_object(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_raw_json_string(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_string(false); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_number(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_double(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_uint64(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_int64(); } -template<> simdjson_inline simdjson_result value::get() noexcept { return get_bool(); } - -template simdjson_inline error_code value::get(T &out) noexcept { - return get().get(out); -} - -#if SIMDJSON_EXCEPTIONS -template -simdjson_inline value::operator T() noexcept(false) { - return get(); -} -simdjson_inline value::operator array() noexcept(false) { - return get_array(); -} -simdjson_inline value::operator object() noexcept(false) { - return get_object(); -} -simdjson_inline value::operator uint64_t() noexcept(false) { - return get_uint64(); -} -simdjson_inline value::operator int64_t() noexcept(false) { - return get_int64(); -} -simdjson_inline value::operator double() noexcept(false) { - return get_double(); -} -simdjson_inline value::operator std::string_view() noexcept(false) { - return get_string(false); -} -simdjson_inline value::operator raw_json_string() noexcept(false) { - return get_raw_json_string(); -} -simdjson_inline value::operator bool() noexcept(false) { - return get_bool(); -} -#endif - -simdjson_inline simdjson_result value::begin() & noexcept { - return get_array().begin(); -} -simdjson_inline simdjson_result value::end() & noexcept { - return {}; -} -simdjson_inline simdjson_result value::count_elements() & noexcept { - simdjson_result answer; - auto a = get_array(); - answer = a.count_elements(); - // count_elements leaves you pointing inside the array, at the first element. - // We need to move back so that the user can create a new array (which requires that - // we point at '['). - iter.move_at_start(); - return answer; -} -simdjson_inline simdjson_result value::count_fields() & noexcept { - simdjson_result answer; - auto a = get_object(); - answer = a.count_fields(); - iter.move_at_start(); - return answer; -} -simdjson_inline simdjson_result value::at(size_t index) noexcept { - auto a = get_array(); - return a.at(index); -} - -simdjson_inline simdjson_result value::find_field(std::string_view key) noexcept { - return start_or_resume_object().find_field(key); -} -simdjson_inline simdjson_result value::find_field(const char *key) noexcept { - return start_or_resume_object().find_field(key); -} - -simdjson_inline simdjson_result value::find_field_unordered(std::string_view key) noexcept { - return start_or_resume_object().find_field_unordered(key); -} -simdjson_inline simdjson_result value::find_field_unordered(const char *key) noexcept { - return start_or_resume_object().find_field_unordered(key); -} - -simdjson_inline simdjson_result value::operator[](std::string_view key) noexcept { - return start_or_resume_object()[key]; -} -simdjson_inline simdjson_result value::operator[](const char *key) noexcept { - return start_or_resume_object()[key]; -} - -simdjson_inline simdjson_result value::type() noexcept { - return iter.type(); -} - -simdjson_inline simdjson_result value::is_scalar() noexcept { - json_type this_type; - auto error = type().get(this_type); - if(error) { return error; } - return ! ((this_type == json_type::array) || (this_type == json_type::object)); -} - -simdjson_inline simdjson_result value::is_string() noexcept { - json_type this_type; - auto error = type().get(this_type); - if(error) { return error; } - return (this_type == json_type::string); -} - - -simdjson_inline bool value::is_negative() noexcept { - return iter.is_negative(); -} - -simdjson_inline simdjson_result value::is_integer() noexcept { - return iter.is_integer(); -} -simdjson_warn_unused simdjson_inline simdjson_result value::get_number_type() noexcept { - return iter.get_number_type(); -} -simdjson_warn_unused simdjson_inline simdjson_result value::get_number() noexcept { - return iter.get_number(); -} - -simdjson_inline std::string_view value::raw_json_token() noexcept { - return std::string_view(reinterpret_cast(iter.peek_start()), iter.peek_start_length()); -} - -simdjson_inline simdjson_result value::raw_json() noexcept { - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) - { - case json_type::array: { - ondemand::array array; - SIMDJSON_TRY(get_array().get(array)); - return array.raw_json(); - } - case json_type::object: { - ondemand::object object; - SIMDJSON_TRY(get_object().get(object)); - return object.raw_json(); - } - default: - return raw_json_token(); - } -} - -simdjson_inline simdjson_result value::current_location() noexcept { - return iter.json_iter().current_location(); -} - -simdjson_inline int32_t value::current_depth() const noexcept{ - return iter.json_iter().depth(); -} - -inline bool is_pointer_well_formed(std::string_view json_pointer) noexcept { - if (simdjson_unlikely(json_pointer.empty())) { // can't be - return false; - } - if (simdjson_unlikely(json_pointer[0] != '/')) { - return false; - } - size_t escape = json_pointer.find('~'); - if (escape == std::string_view::npos) { - return true; - } - if (escape == json_pointer.size() - 1) { - return false; - } - if (json_pointer[escape + 1] != '0' && json_pointer[escape + 1] != '1') { - return false; - } - return true; -} - -simdjson_inline simdjson_result value::at_pointer(std::string_view json_pointer) noexcept { - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) - { - case json_type::array: - return (*this).get_array().at_pointer(json_pointer); - case json_type::object: - return (*this).get_object().at_pointer(json_pointer); - default: - // a non-empty string can be invalid, or accessing a primitive (issue 2154) - if (is_pointer_well_formed(json_pointer)) { - return NO_SUCH_FIELD; - } - return INVALID_JSON_POINTER; - } + return !(a == c); } -simdjson_inline simdjson_result value::at_path(std::string_view json_path) noexcept { - json_type t; - SIMDJSON_TRY(type().get(t)); - switch (t) { - case json_type::array: - return (*this).get_array().at_path(json_path); - case json_type::object: - return (*this).get_object().at_path(json_path); - default: - return INVALID_JSON_POINTER; + +simdjson_inline simdjson_warn_unused simdjson_result raw_json_string::unescape(json_iterator &iter, bool allow_replacement) const noexcept { + return iter.unescape(*this, allow_replacement); +} + +simdjson_inline simdjson_warn_unused simdjson_result raw_json_string::unescape_wobbly(json_iterator &iter) const noexcept { + return iter.unescape_wobbly(*this); +} + +simdjson_unused simdjson_inline std::ostream &operator<<(std::ostream &out, const raw_json_string &str) noexcept { + bool in_escape = false; + const char *s = str.raw(); + while (true) { + switch (*s) { + case '\\': in_escape = !in_escape; break; + case '"': if (in_escape) { in_escape = false; } else { return out; } break; + default: if (in_escape) { in_escape = false; } + } + out << *s; + s++; } } @@ -116909,253 +121280,360 @@ simdjson_inline simdjson_result value::at_path(std::string_view json_path namespace simdjson { -simdjson_inline simdjson_result::simdjson_result( - lasx::ondemand::value &&value -) noexcept : - implementation_simdjson_result_base( - std::forward(value) - ) -{ -} -simdjson_inline simdjson_result::simdjson_result( - error_code error -) noexcept : - implementation_simdjson_result_base(error) -{ -} -simdjson_inline simdjson_result simdjson_result::count_elements() & noexcept { +simdjson_inline simdjson_result::simdjson_result(lasx::ondemand::raw_json_string &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(error) {} + +simdjson_inline simdjson_result simdjson_result::raw() const noexcept { if (error()) { return error(); } - return first.count_elements(); + return first.raw(); } -simdjson_inline simdjson_result simdjson_result::count_fields() & noexcept { +simdjson_inline simdjson_warn_unused simdjson_result simdjson_result::unescape(lasx::ondemand::json_iterator &iter, bool allow_replacement) const noexcept { if (error()) { return error(); } - return first.count_fields(); + return first.unescape(iter, allow_replacement); } -simdjson_inline simdjson_result simdjson_result::at(size_t index) noexcept { +simdjson_inline simdjson_warn_unused simdjson_result simdjson_result::unescape_wobbly(lasx::ondemand::json_iterator &iter) const noexcept { if (error()) { return error(); } - return first.at(index); + return first.unescape_wobbly(iter); } -simdjson_inline simdjson_result simdjson_result::begin() & noexcept { - if (error()) { return error(); } - return first.begin(); +} // namespace simdjson + +#endif // SIMDJSON_GENERIC_ONDEMAND_RAW_JSON_STRING_INL_H +/* end file simdjson/generic/ondemand/raw_json_string-inl.h for lasx */ +/* including simdjson/generic/ondemand/serialization-inl.h for lasx: #include "simdjson/generic/ondemand/serialization-inl.h" */ +/* begin file simdjson/generic/ondemand/serialization-inl.h for lasx */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/array.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/document-inl.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/json_type.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/object.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/serialization.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/value.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { + +inline std::string_view trim(const std::string_view str) noexcept { + // We can almost surely do better by rolling our own find_first_not_of function. + size_t first = str.find_first_not_of(" \t\n\r"); + // If we have the empty string (just white space), then no trimming is possible, and + // we return the empty string_view. + if (std::string_view::npos == first) { return std::string_view(); } + size_t last = str.find_last_not_of(" \t\n\r"); + return str.substr(first, (last - first + 1)); } -simdjson_inline simdjson_result simdjson_result::end() & noexcept { - if (error()) { return error(); } - return {}; + + +inline simdjson_result to_json_string(lasx::ondemand::document& x) noexcept { + std::string_view v; + auto error = x.raw_json().get(v); + if(error) {return error; } + return trim(v); } -simdjson_inline simdjson_result simdjson_result::find_field(std::string_view key) noexcept { - if (error()) { return error(); } - return first.find_field(key); +inline simdjson_result to_json_string(lasx::ondemand::document_reference& x) noexcept { + std::string_view v; + auto error = x.raw_json().get(v); + if(error) {return error; } + return trim(v); } -simdjson_inline simdjson_result simdjson_result::find_field(const char *key) noexcept { - if (error()) { return error(); } - return first.find_field(key); + +inline simdjson_result to_json_string(lasx::ondemand::value& x) noexcept { + /** + * If we somehow receive a value that has already been consumed, + * then the following code could be in trouble. E.g., we create + * an array as needed, but if an array was already created, then + * it could be bad. + */ + using namespace lasx::ondemand; + lasx::ondemand::json_type t; + auto error = x.type().get(t); + if(error != SUCCESS) { return error; } + switch (t) + { + case json_type::array: + { + lasx::ondemand::array array; + error = x.get_array().get(array); + if(error) { return error; } + return to_json_string(array); + } + case json_type::object: + { + lasx::ondemand::object object; + error = x.get_object().get(object); + if(error) { return error; } + return to_json_string(object); + } + default: + return trim(x.raw_json_token()); + } } -simdjson_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); +inline simdjson_result to_json_string(lasx::ondemand::object& x) noexcept { + std::string_view v; + auto error = x.raw_json().get(v); + if(error) {return error; } + return trim(v); } -simdjson_inline simdjson_result simdjson_result::find_field_unordered(const char *key) noexcept { - if (error()) { return error(); } - return first.find_field_unordered(key); + +inline simdjson_result to_json_string(lasx::ondemand::array& x) noexcept { + std::string_view v; + auto error = x.raw_json().get(v); + if(error) {return error; } + return trim(v); } -simdjson_inline simdjson_result simdjson_result::operator[](std::string_view key) noexcept { - if (error()) { return error(); } - return first[key]; +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); } -simdjson_inline simdjson_result simdjson_result::operator[](const char *key) noexcept { - if (error()) { return error(); } - return first[key]; + +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); } -simdjson_inline simdjson_result simdjson_result::get_array() noexcept { - if (error()) { return error(); } - return first.get_array(); +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); } -simdjson_inline simdjson_result simdjson_result::get_object() noexcept { - if (error()) { return error(); } - return first.get_object(); + +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); } -simdjson_inline simdjson_result simdjson_result::get_uint64() noexcept { - if (error()) { return error(); } - return first.get_uint64(); + +inline simdjson_result to_json_string(simdjson_result x) { + if (x.error()) { return x.error(); } + return to_json_string(x.value_unsafe()); } -simdjson_inline simdjson_result simdjson_result::get_uint64_in_string() noexcept { - if (error()) { return error(); } - return first.get_uint64_in_string(); +} // namespace simdjson + +namespace simdjson { namespace lasx { namespace ondemand { + +#if SIMDJSON_EXCEPTIONS +inline std::ostream& operator<<(std::ostream& out, simdjson::lasx::ondemand::value x) { + std::string_view v; + auto error = simdjson::to_json_string(x).get(v); + if(error == simdjson::SUCCESS) { + return (out << v); + } else { + throw simdjson::simdjson_error(error); + } } -simdjson_inline simdjson_result simdjson_result::get_int64() noexcept { - if (error()) { return error(); } - return first.get_int64(); +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result x) { + if (x.error()) { throw simdjson::simdjson_error(x.error()); } + return (out << x.value()); } -simdjson_inline simdjson_result simdjson_result::get_int64_in_string() noexcept { - if (error()) { return error(); } - return first.get_int64_in_string(); +#else +inline std::ostream& operator<<(std::ostream& out, simdjson::lasx::ondemand::value x) { + std::string_view v; + auto error = simdjson::to_json_string(x).get(v); + if(error == simdjson::SUCCESS) { + return (out << v); + } else { + return (out << error); + } } -simdjson_inline simdjson_result simdjson_result::get_double() noexcept { - if (error()) { return error(); } - return first.get_double(); +#endif + +#if SIMDJSON_EXCEPTIONS +inline std::ostream& operator<<(std::ostream& out, simdjson::lasx::ondemand::array value) { + std::string_view v; + auto error = simdjson::to_json_string(value).get(v); + if(error == simdjson::SUCCESS) { + return (out << v); + } else { + throw simdjson::simdjson_error(error); + } } -simdjson_inline simdjson_result simdjson_result::get_double_in_string() noexcept { - if (error()) { return error(); } - return first.get_double_in_string(); +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result x) { + if (x.error()) { throw simdjson::simdjson_error(x.error()); } + return (out << x.value()); } -simdjson_inline simdjson_result simdjson_result::get_string(bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(allow_replacement); +#else +inline std::ostream& operator<<(std::ostream& out, simdjson::lasx::ondemand::array value) { + std::string_view v; + auto error = simdjson::to_json_string(value).get(v); + if(error == simdjson::SUCCESS) { + return (out << v); + } else { + return (out << error); + } } -template -simdjson_inline error_code simdjson_result::get_string(string_type& receiver, bool allow_replacement) noexcept { - if (error()) { return error(); } - return first.get_string(receiver, allow_replacement); +#endif + +#if SIMDJSON_EXCEPTIONS +inline std::ostream& operator<<(std::ostream& out, simdjson::lasx::ondemand::document& value) { + std::string_view v; + auto error = simdjson::to_json_string(value).get(v); + if(error == simdjson::SUCCESS) { + return (out << v); + } else { + throw simdjson::simdjson_error(error); + } } -simdjson_inline simdjson_result simdjson_result::get_wobbly_string() noexcept { - if (error()) { return error(); } - return first.get_wobbly_string(); +inline std::ostream& operator<<(std::ostream& out, simdjson::lasx::ondemand::document_reference& value) { + std::string_view v; + auto error = simdjson::to_json_string(value).get(v); + if(error == simdjson::SUCCESS) { + return (out << v); + } else { + throw simdjson::simdjson_error(error); + } } -simdjson_inline simdjson_result simdjson_result::get_raw_json_string() noexcept { - if (error()) { return error(); } - return first.get_raw_json_string(); +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result&& x) { + if (x.error()) { throw simdjson::simdjson_error(x.error()); } + return (out << x.value()); } -simdjson_inline simdjson_result simdjson_result::get_bool() noexcept { - if (error()) { return error(); } - return first.get_bool(); +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result&& x) { + if (x.error()) { throw simdjson::simdjson_error(x.error()); } + return (out << x.value()); } -simdjson_inline simdjson_result simdjson_result::is_null() noexcept { - if (error()) { return error(); } - return first.is_null(); +#else +inline std::ostream& operator<<(std::ostream& out, simdjson::lasx::ondemand::document& value) { + std::string_view v; + auto error = simdjson::to_json_string(value).get(v); + if(error == simdjson::SUCCESS) { + return (out << v); + } else { + return (out << error); + } } +#endif -template simdjson_inline simdjson_result simdjson_result::get() noexcept { - if (error()) { return error(); } - return first.get(); -} -template simdjson_inline error_code simdjson_result::get(T &out) noexcept { - if (error()) { return error(); } - return first.get(out); +#if SIMDJSON_EXCEPTIONS +inline std::ostream& operator<<(std::ostream& out, simdjson::lasx::ondemand::object value) { + std::string_view v; + auto error = simdjson::to_json_string(value).get(v); + if(error == simdjson::SUCCESS) { + return (out << v); + } else { + throw simdjson::simdjson_error(error); + } } - -template<> simdjson_inline simdjson_result simdjson_result::get() noexcept { - if (error()) { return error(); } - return std::move(first); +inline std::ostream& operator<<(std::ostream& out, simdjson::simdjson_result x) { + if (x.error()) { throw simdjson::simdjson_error(x.error()); } + return (out << x.value()); } -template<> simdjson_inline error_code simdjson_result::get(lasx::ondemand::value &out) noexcept { - if (error()) { return error(); } - out = first; - return SUCCESS; +#else +inline std::ostream& operator<<(std::ostream& out, simdjson::lasx::ondemand::object value) { + std::string_view v; + auto error = simdjson::to_json_string(value).get(v); + if(error == simdjson::SUCCESS) { + return (out << v); + } else { + return (out << error); + } } +#endif +}}} // namespace simdjson::lasx::ondemand -simdjson_inline simdjson_result simdjson_result::type() noexcept { - if (error()) { return error(); } - return first.type(); -} -simdjson_inline simdjson_result simdjson_result::is_scalar() noexcept { - if (error()) { return error(); } - return first.is_scalar(); +#endif // SIMDJSON_GENERIC_ONDEMAND_SERIALIZATION_INL_H +/* end file simdjson/generic/ondemand/serialization-inl.h for lasx */ +/* including simdjson/generic/ondemand/token_iterator-inl.h for lasx: #include "simdjson/generic/ondemand/token_iterator-inl.h" */ +/* begin file simdjson/generic/ondemand/token_iterator-inl.h for lasx */ +#ifndef SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H + +/* amalgamation skipped (editor-only): #ifndef SIMDJSON_CONDITIONAL_INCLUDE */ +/* amalgamation skipped (editor-only): #define SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/base.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/ondemand/token_iterator.h" */ +/* amalgamation skipped (editor-only): #include "simdjson/generic/implementation_simdjson_result_base-inl.h" */ +/* amalgamation skipped (editor-only): #endif // SIMDJSON_CONDITIONAL_INCLUDE */ + +namespace simdjson { +namespace lasx { +namespace ondemand { + +simdjson_inline token_iterator::token_iterator( + const uint8_t *_buf, + token_position position +) noexcept : buf{_buf}, _position{position} +{ } -simdjson_inline simdjson_result simdjson_result::is_string() noexcept { - if (error()) { return error(); } - return first.is_string(); + +simdjson_inline uint32_t token_iterator::current_offset() const noexcept { + return *(_position); } -simdjson_inline simdjson_result simdjson_result::is_negative() noexcept { - if (error()) { return error(); } - return first.is_negative(); + + +simdjson_inline const uint8_t *token_iterator::return_current_and_advance() noexcept { + return &buf[*(_position++)]; } -simdjson_inline simdjson_result simdjson_result::is_integer() noexcept { - if (error()) { return error(); } - return first.is_integer(); + +simdjson_inline const uint8_t *token_iterator::peek(token_position position) const noexcept { + return &buf[*position]; } -simdjson_inline simdjson_result simdjson_result::get_number_type() noexcept { - if (error()) { return error(); } - return first.get_number_type(); +simdjson_inline uint32_t token_iterator::peek_index(token_position position) const noexcept { + return *position; } -simdjson_inline simdjson_result simdjson_result::get_number() noexcept { - if (error()) { return error(); } - return first.get_number(); +simdjson_inline uint32_t token_iterator::peek_length(token_position position) const noexcept { + return *(position+1) - *position; } -#if SIMDJSON_EXCEPTIONS -template -simdjson_inline simdjson_result::operator T() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return static_cast(first); + +simdjson_inline uint32_t token_iterator::peek_root_length(token_position position) const noexcept { + return *(position+2) - *(position) > *(position+1) - *(position) ? + *(position+1) - *(position) + : *(position+2) - *(position); } -simdjson_inline simdjson_result::operator lasx::ondemand::array() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; +simdjson_inline const uint8_t *token_iterator::peek(int32_t delta) const noexcept { + return &buf[*(_position+delta)]; } -simdjson_inline simdjson_result::operator lasx::ondemand::object() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; +simdjson_inline uint32_t token_iterator::peek_index(int32_t delta) const noexcept { + return *(_position+delta); } -simdjson_inline simdjson_result::operator uint64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; +simdjson_inline uint32_t token_iterator::peek_length(int32_t delta) const noexcept { + return *(_position+delta+1) - *(_position+delta); } -simdjson_inline simdjson_result::operator int64_t() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; + +simdjson_inline token_position token_iterator::position() const noexcept { + return _position; } -simdjson_inline simdjson_result::operator double() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; +simdjson_inline void token_iterator::set_position(token_position target_position) noexcept { + _position = target_position; } -simdjson_inline simdjson_result::operator std::string_view() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; + +simdjson_inline bool token_iterator::operator==(const token_iterator &other) const noexcept { + return _position == other._position; } -simdjson_inline simdjson_result::operator lasx::ondemand::raw_json_string() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; +simdjson_inline bool token_iterator::operator!=(const token_iterator &other) const noexcept { + return _position != other._position; } -simdjson_inline simdjson_result::operator bool() noexcept(false) { - if (error()) { throw simdjson_error(error()); } - return first; +simdjson_inline bool token_iterator::operator>(const token_iterator &other) const noexcept { + return _position > other._position; } -#endif - -simdjson_inline simdjson_result simdjson_result::raw_json_token() noexcept { - if (error()) { return error(); } - return first.raw_json_token(); +simdjson_inline bool token_iterator::operator>=(const token_iterator &other) const noexcept { + return _position >= other._position; } - -simdjson_inline simdjson_result simdjson_result::raw_json() noexcept { - if (error()) { return error(); } - return first.raw_json(); +simdjson_inline bool token_iterator::operator<(const token_iterator &other) const noexcept { + return _position < other._position; } - -simdjson_inline simdjson_result simdjson_result::current_location() noexcept { - if (error()) { return error(); } - return first.current_location(); +simdjson_inline bool token_iterator::operator<=(const token_iterator &other) const noexcept { + return _position <= other._position; } -simdjson_inline simdjson_result simdjson_result::current_depth() const noexcept { - if (error()) { return error(); } - return first.current_depth(); -} +} // namespace ondemand +} // namespace lasx +} // namespace simdjson -simdjson_inline simdjson_result simdjson_result::at_pointer( - std::string_view json_pointer) noexcept { - if (error()) { - return error(); - } - return first.at_pointer(json_pointer); -} +namespace simdjson { -simdjson_inline simdjson_result simdjson_result::at_path( - std::string_view json_path) noexcept { - if (error()) { - return error(); - } - return first.at_path(json_path); -} +simdjson_inline simdjson_result::simdjson_result(lasx::ondemand::token_iterator &&value) noexcept + : implementation_simdjson_result_base(std::forward(value)) {} +simdjson_inline simdjson_result::simdjson_result(error_code error) noexcept + : implementation_simdjson_result_base(error) {} } // namespace simdjson -#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_INL_H -/* end file simdjson/generic/ondemand/value-inl.h for lasx */ +#endif // SIMDJSON_GENERIC_ONDEMAND_TOKEN_ITERATOR_INL_H +/* end file simdjson/generic/ondemand/token_iterator-inl.h for lasx */ /* including simdjson/generic/ondemand/value_iterator-inl.h for lasx: #include "simdjson/generic/ondemand/value_iterator-inl.h" */ /* begin file simdjson/generic/ondemand/value_iterator-inl.h for lasx */ #ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H @@ -117959,6 +122437,8 @@ simdjson_inline simdjson_result value_iterator::is_root_null(bool check_tr if(result) { // we have something that looks like a null. if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; } advance_root_scalar("null"); + } else if (json[0] == 'n') { + return incorrect_type_error("Not a null but starts with n"); } return result; } @@ -118250,6 +122730,8 @@ simdjson_inline simdjson_result::simdjson_result #endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H /* end file simdjson/generic/ondemand/value_iterator-inl.h for lasx */ + + /* end file simdjson/generic/ondemand/amalgamated.h for lasx */ /* including simdjson/lasx/end.h: #include "simdjson/lasx/end.h" */ /* begin file simdjson/lasx/end.h */ @@ -118290,6 +122772,5 @@ namespace simdjson { #endif // SIMDJSON_ONDEMAND_H /* end file simdjson/ondemand.h */ - #endif // SIMDJSON_H /* end file simdjson.h */ diff --git a/3rdparty/yasio/yasio/ibstream.hpp b/3rdparty/yasio/yasio/ibstream.hpp index b166f62fa449..7df39ac16506 100644 --- a/3rdparty/yasio/yasio/ibstream.hpp +++ b/3rdparty/yasio/yasio/ibstream.hpp @@ -152,7 +152,7 @@ class binary_reader_impl { } binary_reader_impl& operator=(const binary_reader_impl&) = delete; - binary_reader_impl& operator=(binary_reader_impl&&) = delete; + binary_reader_impl& operator=(binary_reader_impl&&) = delete; /* read 7bit encoded variant integer value ** @dotnet BinaryReader.Read7BitEncodedInt(64) @@ -189,6 +189,21 @@ class binary_reader_impl { read_bytes(&oav.front(), len); } } + template + size_t read_blob(_Ty& out) + { + return read_blob(&out, static_cast(sizeof(_Ty)), 1); + } + template + size_t read_blob(_Ty (&out)[_N]) + { + return read_blob(out, static_cast(sizeof(_Ty)), static_cast(_N)); + } + size_t read_blob(void* out, int size, int count) + { + read_bytes(out, size * count); + return count; + } void read_bytes(void* oav, int len) { if (len > 0) diff --git a/3rdparty/yasio/yasio/pod_vector.hpp b/3rdparty/yasio/yasio/pod_vector.hpp index 33ca2374702b..beddf30114a2 100644 --- a/3rdparty/yasio/yasio/pod_vector.hpp +++ b/3rdparty/yasio/yasio/pod_vector.hpp @@ -224,6 +224,11 @@ class pod_vector { } void push_back(value_type&& val) { push_back(val); } void push_back(const value_type& val) { emplace_back(val); } + void pop_back() + { + if (!empty()) + _Eos(size() - 1); + } template inline value_type& emplace_back(_Valty&&... val) { diff --git a/cmake/Modules/AXBuildSet.cmake b/cmake/Modules/AXBuildSet.cmake index 14bfd96dcd6f..956d443ec8c5 100644 --- a/cmake/Modules/AXBuildSet.cmake +++ b/cmake/Modules/AXBuildSet.cmake @@ -32,7 +32,7 @@ endif() # import minimal axslcc.cmake for shader compiler support # the function: ax_target_compile_shaders avaiable from it -set(AXSLCC_FIND_PROG_ROOT "${_AX_ROOT}/tools/external/axslcc" "$ENV{AX_ROOT}/tools/external/axslcc") +set(AXSLCC_FIND_PROG_ROOT "${_AX_ROOT}/tools/external/axslcc") include(AXSLCC) # include helper functions diff --git a/cmake/Modules/AXConfigDefine.cmake b/cmake/Modules/AXConfigDefine.cmake index 507286326b4a..d9a7813f5322 100644 --- a/cmake/Modules/AXConfigDefine.cmake +++ b/cmake/Modules/AXConfigDefine.cmake @@ -18,6 +18,11 @@ if (WINRT) set(CMAKE_CXX_FLAGS "/sdl- ${CMAKE_CXX_FLAGS}") endif() +if (ANDROID OR LINUX) + set(CMAKE_C_FLAGS "-fPIC ${CMAKE_C_FLAGS}") + set(CMAKE_CXX_FLAGS "-fPIC ${CMAKE_CXX_FLAGS}") +endif() + # config c standard if (WINDOWS) message(STATUS "CMAKE_HOST_SYSTEM_VERSION: ${CMAKE_HOST_SYSTEM_VERSION}") diff --git a/cmake/Modules/AXLinkHelpers.cmake b/cmake/Modules/AXLinkHelpers.cmake index f5b524ae5074..ae75683e90b0 100644 --- a/cmake/Modules/AXLinkHelpers.cmake +++ b/cmake/Modules/AXLinkHelpers.cmake @@ -52,8 +52,6 @@ function(ax_link_cxx_prebuilt APP_NAME AX_ROOT_DIR AX_PREBUILT_DIR) PRIVATE NOUNCRYPT=1 PRIVATE P2T_STATIC_EXPORTS=1 PRIVATE BT_USE_SSE_IN_API=1 - PRIVATE CP_USE_DOUBLES=0 - PRIVATE CP_USE_CGTYPES=0 ) ax_config_pred(${APP_NAME} AX_USE_ALSOFT) @@ -91,7 +89,6 @@ function(ax_link_cxx_prebuilt APP_NAME AX_ROOT_DIR AX_PREBUILT_DIR) PRIVATE ${AX_ROOT_DIR}/3rdparty/freetype/include PRIVATE ${AX_ROOT_DIR}/3rdparty/glfw/include/GLFW PRIVATE ${AX_ROOT_DIR}/3rdparty/box2d/include - PRIVATE ${AX_ROOT_DIR}/3rdparty/chipmunk/include PRIVATE ${AX_ROOT_DIR}/${AX_PREBUILT_DIR}/engine/3rdparty/freetype/include PRIVATE ${AX_ROOT_DIR}/3rdparty/webp/src/webp PRIVATE ${AX_ROOT_DIR}/3rdparty/pugixml @@ -136,7 +133,6 @@ function(ax_link_cxx_prebuilt APP_NAME AX_ROOT_DIR AX_PREBUILT_DIR) set(LIBS axmol box2d - chipmunk freetype webp pugixml diff --git a/core/2d/AnchoredSprite.cpp b/core/2d/AnchoredSprite.cpp index faa4eb8272c3..62bf49cd8aba 100644 --- a/core/2d/AnchoredSprite.cpp +++ b/core/2d/AnchoredSprite.cpp @@ -126,7 +126,7 @@ AnchoredSprite* AnchoredSprite::createWithSpriteFrameName(std::string_view sprit { SpriteFrame* frame = SpriteFrameCache::getInstance()->getSpriteFrameByName(spriteFrameName); -#if _AX_DEBUG > 0 +#if defined(_AX_DEBUG) && _AX_DEBUG > 0 char msg[256] = {0}; snprintf(msg, sizeof(msg), "Invalid spriteFrameName: %s", spriteFrameName.data()); AXASSERT(frame != nullptr, msg); @@ -147,7 +147,7 @@ AnchoredSprite* AnchoredSprite::create() return nullptr; } -void AnchoredSprite::setVertexCoords(const Rect& rect, V3F_C4B_T2F_Quad* outQuad) +void AnchoredSprite::setVertexCoords(const Rect& rect, V3F_T2F_C4B_Quad* outQuad) { float relativeOffsetX = _unflippedOffsetPositionFromCenter.x - getContentSize().x * _spriteVertexAnchor.x; float relativeOffsetY = _unflippedOffsetPositionFromCenter.y - getContentSize().y * _spriteVertexAnchor.y; @@ -186,10 +186,10 @@ void AnchoredSprite::setVertexCoords(const Rect& rect, V3F_C4B_T2F_Quad* outQuad const float y2 = y1 + rect.size.height; // Don't update Z. - outQuad->bl.vertices.set(x1, y1, 0.0f); - outQuad->br.vertices.set(x2, y1, 0.0f); - outQuad->tl.vertices.set(x1, y2, 0.0f); - outQuad->tr.vertices.set(x2, y2, 0.0f); + outQuad->bl.position.set(x1, y1, 0.0f); + outQuad->br.position.set(x2, y1, 0.0f); + outQuad->tl.position.set(x1, y2, 0.0f); + outQuad->tr.position.set(x2, y2, 0.0f); } } diff --git a/core/2d/AnchoredSprite.h b/core/2d/AnchoredSprite.h index f749fe489942..458a7a12477a 100644 --- a/core/2d/AnchoredSprite.h +++ b/core/2d/AnchoredSprite.h @@ -143,7 +143,7 @@ class AX_DLL AnchoredSprite : public Sprite virtual Rect getTouchRect(); protected: - virtual void setVertexCoords(const Rect& rect, V3F_C4B_T2F_Quad* outQuad) override; + virtual void setVertexCoords(const Rect& rect, V3F_T2F_C4B_Quad* outQuad) override; Vec2 _spriteVertexAnchor = Vec2::ANCHOR_MIDDLE; }; diff --git a/core/2d/AutoPolygon.cpp b/core/2d/AutoPolygon.cpp index a77ae4a20ca3..9329ccc2524e 100644 --- a/core/2d/AutoPolygon.cpp +++ b/core/2d/AutoPolygon.cpp @@ -63,7 +63,7 @@ PolygonInfo::PolygonInfo(const PolygonInfo& other) : triangles(), _isVertsOwner( _filename = other._filename; _isVertsOwner = true; _rect = other._rect; - triangles.verts = new V3F_C4B_T2F[other.triangles.vertCount]; + triangles.verts = new V3F_T2F_C4B[other.triangles.vertCount]; triangles.indices = new unsigned short[other.triangles.indexCount]; AXASSERT(triangles.verts && triangles.indices, "not enough memory"); triangles.vertCount = other.triangles.vertCount; @@ -80,7 +80,7 @@ PolygonInfo& PolygonInfo::operator=(const PolygonInfo& other) _filename = other._filename; _isVertsOwner = true; _rect = other._rect; - triangles.verts = new V3F_C4B_T2F[other.triangles.vertCount]; + triangles.verts = new V3F_T2F_C4B[other.triangles.vertCount]; triangles.indices = new unsigned short[other.triangles.indexCount]; AXASSERT(triangles.verts && triangles.indices, "not enough memory"); triangles.vertCount = other.triangles.vertCount; @@ -97,17 +97,17 @@ PolygonInfo::~PolygonInfo() releaseVertsAndIndices(); } -void PolygonInfo::setQuad(V3F_C4B_T2F_Quad* quad) +void PolygonInfo::setQuad(V3F_T2F_C4B_Quad* quad) { releaseVertsAndIndices(); _isVertsOwner = false; triangles.indices = quadIndices9; triangles.vertCount = 4; triangles.indexCount = 6; - triangles.verts = (V3F_C4B_T2F*)quad; + triangles.verts = (V3F_T2F_C4B*)quad; } -void PolygonInfo::setQuads(V3F_C4B_T2F_Quad* quad, int numberOfQuads) +void PolygonInfo::setQuads(V3F_T2F_C4B_Quad* quad, int numberOfQuads) { AXASSERT(numberOfQuads >= 1 && numberOfQuads <= 9, "Invalid number of Quads"); @@ -116,7 +116,7 @@ void PolygonInfo::setQuads(V3F_C4B_T2F_Quad* quad, int numberOfQuads) triangles.indices = quadIndices9; triangles.vertCount = 4 * numberOfQuads; triangles.indexCount = 6 * numberOfQuads; - triangles.verts = (V3F_C4B_T2F*)quad; + triangles.verts = (V3F_T2F_C4B*)quad; } void PolygonInfo::setTriangles(const TrianglesCommand::Triangles& other) @@ -159,13 +159,13 @@ unsigned int PolygonInfo::getTrianglesCount() const float PolygonInfo::getArea() const { float area = 0; - V3F_C4B_T2F* verts = triangles.verts; + V3F_T2F_C4B* verts = triangles.verts; unsigned short* indices = triangles.indices; for (unsigned int i = 0; i < triangles.indexCount; i += 3) { - auto A = verts[indices[i]].vertices; - auto B = verts[indices[i + 1]].vertices; - auto C = verts[indices[i + 2]].vertices; + auto A = verts[indices[i]].position; + auto B = verts[indices[i + 1]].position; + auto C = verts[indices[i + 2]].position; area += (A.x * (B.y - C.y) + B.x * (C.y - A.y) + C.x * (A.y - B.y)) / 2; } return area; @@ -610,7 +610,7 @@ TrianglesCommand::Triangles AutoPolygon::triangulate(const std::vector& po std::vector tris = cdt.GetTriangles(); axstd::pod_vector indices(tris.size() * 3); - axstd::pod_vector verts; + axstd::pod_vector verts; verts.reserve(indices.size() / 2); // we won't know the size of verts until we process all of the triangles! unsigned short idx = 0; @@ -627,7 +627,7 @@ TrianglesCommand::Triangles AutoPolygon::triangulate(const std::vector& po auto length = vdx; for (j = 0; j < length; j++) { - if (verts[j].vertices == v3) + if (verts[j].position == v3) { found = true; break; @@ -640,10 +640,8 @@ TrianglesCommand::Triangles AutoPolygon::triangulate(const std::vector& po } else { - // vert does not exist yet, so we need to create a new one, - auto c4b = Color4B::WHITE; - auto t2f = Tex2F(0, 0); // don't worry about tex coords now, we calculate that later - verts.push_back(V3F_C4B_T2F{v3, c4b, t2f}); + // vert does not exist yet, so we need to create a new one + verts.emplace_back(v3, Vec2::ZERO, Color::WHITE); indices[idx++] = vdx++;; } } @@ -656,7 +654,7 @@ TrianglesCommand::Triangles AutoPolygon::triangulate(const std::vector& po return triangles; } -void AutoPolygon::calculateUV(const Rect& rect, V3F_C4B_T2F* verts, ssize_t count) +void AutoPolygon::calculateUV(const Rect& rect, V3F_T2F_C4B* verts, ssize_t count) { /* whole texture UV coordination @@ -683,10 +681,10 @@ void AutoPolygon::calculateUV(const Rect& rect, V3F_C4B_T2F* verts, ssize_t coun for (auto i = verts; i != end; ++i) { // for every point, offset with the center point - float u = (i->vertices.x * _scaleFactor + rect.origin.x) / texWidth; - float v = (rect.origin.y + rect.size.height - i->vertices.y * _scaleFactor) / texHeight; - i->texCoords.u = u; - i->texCoords.v = v; + float u = (i->position.x * _scaleFactor + rect.origin.x) / texWidth; + float v = (rect.origin.y + rect.size.height - i->position.y * _scaleFactor) / texHeight; + i->texCoord.u = u; + i->texCoord.v = v; } } diff --git a/core/2d/AutoPolygon.h b/core/2d/AutoPolygon.h index 0d53ea380d77..660d3fc5c33d 100644 --- a/core/2d/AutoPolygon.h +++ b/core/2d/AutoPolygon.h @@ -80,17 +80,17 @@ class AX_DLL PolygonInfo * set the data to be a pointer to a quad * the member verts will not be released when this PolygonInfo destructs * as the verts memory are managed by other objects - * @param quad a pointer to the V3F_C4B_T2F_Quad object + * @param quad a pointer to the V3F_T2F_C4B_Quad object */ - void setQuad(V3F_C4B_T2F_Quad* quad); + void setQuad(V3F_T2F_C4B_Quad* quad); /** * set the data to be a pointer to a number of Quads * the member verts will not be released when this PolygonInfo destructs * as the verts memory are managed by other objects - * @param quad a pointer to the V3F_C4B_T2F_Quad quads + * @param quad a pointer to the V3F_T2F_C4B_Quad quads */ - void setQuads(V3F_C4B_T2F_Quad* quads, int numberOfQuads); + void setQuads(V3F_T2F_C4B_Quad* quads, int numberOfQuads); /** * set the data to be a pointer to a triangles @@ -225,7 +225,7 @@ class AX_DLL AutoPolygon * ap.calculateUV(rect, myPolygons.verts, 20); * @endcode */ - void calculateUV(const Rect& rect, V3F_C4B_T2F* verts, ssize_t count); + void calculateUV(const Rect& rect, V3F_T2F_C4B* verts, ssize_t count); /** * a helper function, packing trace, reduce, expand, triangulate and calculate uv in one function diff --git a/core/2d/CameraBackgroundBrush.cpp b/core/2d/CameraBackgroundBrush.cpp index f69e652ed240..b253077e7948 100644 --- a/core/2d/CameraBackgroundBrush.cpp +++ b/core/2d/CameraBackgroundBrush.cpp @@ -62,7 +62,7 @@ CameraBackgroundBrush* CameraBackgroundBrush::createNoneBrush() return ret; } -CameraBackgroundColorBrush* CameraBackgroundBrush::createColorBrush(const Color4F& color, float depth) +CameraBackgroundColorBrush* CameraBackgroundBrush::createColorBrush(const Color& color, float depth) { return CameraBackgroundColorBrush::create(color, depth); } @@ -133,17 +133,17 @@ bool CameraBackgroundDepthBrush::init() pipelineDescriptor.programState = _programState; _vertices.resize(4); - _vertices[0].vertices = Vec3(-1, -1, 0); - _vertices[1].vertices = Vec3(1, -1, 0); - _vertices[2].vertices = Vec3(1, 1, 0); - _vertices[3].vertices = Vec3(-1, 1, 0); + _vertices[0].position = Vec3(-1, -1, 0); + _vertices[1].position = Vec3(1, -1, 0); + _vertices[2].position = Vec3(1, 1, 0); + _vertices[3].position = Vec3(-1, 1, 0); - _vertices[0].colors = _vertices[1].colors = _vertices[2].colors = _vertices[3].colors = Color4B(0, 0, 0, 1); + _vertices[0].color = _vertices[1].color = _vertices[2].color = _vertices[3].color = Color(0, 0, 0, 1); - _vertices[0].texCoords = Tex2F(0, 0); - _vertices[1].texCoords = Tex2F(1, 0); - _vertices[2].texCoords = Tex2F(1, 1); - _vertices[3].texCoords = Tex2F(0, 1); + _vertices[0].texCoord = Tex2F(0, 0); + _vertices[1].texCoord = Tex2F(1, 0); + _vertices[2].texCoord = Tex2F(1, 1); + _vertices[3].texCoord = Tex2F(0, 1); _customCommand.setBeforeCallback(AX_CALLBACK_0(CameraBackgroundDepthBrush::onBeforeDraw, this)); _customCommand.setAfterCallback(AX_CALLBACK_0(CameraBackgroundDepthBrush::onAfterDraw, this)); @@ -231,16 +231,16 @@ void CameraBackgroundColorBrush::drawBackground(Camera* camera) CameraBackgroundDepthBrush::drawBackground(camera); } -void CameraBackgroundColorBrush::setColor(const Color4F& color) +void CameraBackgroundColorBrush::setColor(const Color& color) { for (auto&& vert : _vertices) { - vert.colors = Color4B(color); + vert.color = color; } _customCommand.updateVertexBuffer(_vertices.data(), sizeof(_vertices[0]) * _vertices.size()); } -CameraBackgroundColorBrush* CameraBackgroundColorBrush::create(const Color4F& color, float depth) +CameraBackgroundColorBrush* CameraBackgroundColorBrush::create(const Color& color, float depth) { auto ret = new CameraBackgroundColorBrush(); diff --git a/core/2d/CameraBackgroundBrush.h b/core/2d/CameraBackgroundBrush.h index 3e5c4bd462f5..a75d780990de 100644 --- a/core/2d/CameraBackgroundBrush.h +++ b/core/2d/CameraBackgroundBrush.h @@ -95,7 +95,7 @@ class AX_DLL CameraBackgroundBrush : public Object * @param depth Depth used to clear depth buffer * @return Created brush */ - static CameraBackgroundColorBrush* createColorBrush(const Color4F& color, float depth); + static CameraBackgroundColorBrush* createColorBrush(const Color& color, float depth); /** Creates a Skybox brush with 6 textures. @param positive_x texture for the right side of the texture cube face. @@ -179,7 +179,7 @@ class AX_DLL CameraBackgroundDepthBrush : public CameraBackgroundBrush CustomCommand _customCommand; bool _clearColor; - std::vector _vertices; + std::vector _vertices; struct { uint32_t stencilWriteMask = 0; @@ -206,7 +206,7 @@ class AX_DLL CameraBackgroundColorBrush : public CameraBackgroundDepthBrush * @param depth Depth used to clear the depth buffer * @return Created brush */ - static CameraBackgroundColorBrush* create(const Color4F& color, float depth); + static CameraBackgroundColorBrush* create(const Color& color, float depth); /** * Draw background @@ -217,7 +217,7 @@ class AX_DLL CameraBackgroundColorBrush : public CameraBackgroundDepthBrush * Set clear color * @param color Color used to clear the color buffer */ - void setColor(const Color4F& color); + void setColor(const Color& color); CameraBackgroundColorBrush(); virtual ~CameraBackgroundColorBrush(); @@ -225,7 +225,7 @@ class AX_DLL CameraBackgroundColorBrush : public CameraBackgroundDepthBrush virtual bool init() override; protected: - Color4F _color; + Color _color; }; class TextureCube; diff --git a/core/2d/DrawNode.cpp b/core/2d/DrawNode.cpp index 40fdf661dd82..5249d9ec21aa 100644 --- a/core/2d/DrawNode.cpp +++ b/core/2d/DrawNode.cpp @@ -72,7 +72,7 @@ static bool isConvex(const Vec2* verts, int count) return true; // is convex } -static V2F_C4B_T2F* expandBufferAndGetPointer(axstd::pod_vector& buffer, size_t count) +static V2F_T2F_C4F* expandBufferAndGetPointer(axstd::pod_vector& buffer, size_t count) { size_t oldSize = buffer.size(); buffer.expand(count); @@ -235,7 +235,7 @@ void DrawNode::draw(Renderer* renderer, const Mat4& transform, uint32_t flags) } } -static void udpateCommand(CustomCommand& cmd, const axstd::pod_vector& buffer) +static void udpateCommand(CustomCommand& cmd, const axstd::pod_vector& buffer) { if (buffer.empty()) { @@ -243,8 +243,9 @@ static void udpateCommand(CustomCommand& cmd, const axstd::pod_vector triangleList; + axstd::pod_vector triangleList; int vertex_count = 0; @@ -857,10 +858,10 @@ void DrawNode::_drawPolygon(const Vec2* verts, p2t::Point* vec2 = t->GetPoint(1); p2t::Point* vec3 = t->GetPoint(2); - V2F_C4B_T2F_Triangle triangle = { - {Vec2(vec1->x, vec1->y), fillColor, Vec2::ZERO}, - {Vec2(vec2->x, vec2->y), fillColor, Vec2::ZERO}, - {Vec2(vec3->x, vec3->y), fillColor, Vec2::ZERO}, + V2F_T2F_C4F_Triangle triangle = { + {Vec2(vec1->x, vec1->y), Vec2::ZERO, fillColor}, + {Vec2(vec2->x, vec2->y), Vec2::ZERO, fillColor}, + {Vec2(vec3->x, vec3->y), Vec2::ZERO, fillColor}, }; triangleList.emplace_back(triangle); // use it for drawing later } @@ -884,7 +885,7 @@ void DrawNode::_drawPolygon(const Vec2* verts, vertex_count *= 3; - auto triangles = reinterpret_cast(expandBufferAndGetPointer(_triangles, vertex_count)); + auto triangles = reinterpret_cast(expandBufferAndGetPointer(_triangles, vertex_count)); _trianglesDirty = true; // start drawing... @@ -901,9 +902,9 @@ void DrawNode::_drawPolygon(const Vec2* verts, for (unsigned int i = 0; i < count - 2; i++) { triangles[ii++] = { - {_vertices[0], fillColor, Vec2::ZERO}, - {_vertices[i + 1], fillColor, Vec2::ZERO}, - {_vertices[i + 2], fillColor, Vec2::ZERO}, + {_vertices[0], Vec2::ZERO, fillColor}, + {_vertices[i + 1], Vec2::ZERO, fillColor}, + {_vertices[i + 2], Vec2::ZERO, fillColor}, }; } } @@ -931,38 +932,38 @@ void DrawNode::_drawPolygon(const Vec2* verts, { triangles[ii++] = { - {v0, borderColor, -(n + t)}, - {v1, borderColor, n - t}, - {v2, borderColor, -n}, + {v0, -(n + t), borderColor}, + {v1, n - t, borderColor}, + {v2, -n, borderColor}, }; triangles[ii++] = { - {v3, borderColor, n}, - {v1, borderColor, n - t}, - {v2, borderColor, -n}, + {v3, n, borderColor}, + {v1, n - t, borderColor}, + {v2, -n, borderColor}, }; } triangles[ii++] = { - {v3, borderColor, n}, - {v4, borderColor, -n}, - {v2, borderColor, -n}, + {v3, n, borderColor}, + {v4, -n, borderColor}, + {v2, -n, borderColor}, }; triangles[ii++] = { - {v3, borderColor, n}, - {v4, borderColor, -n}, - {v5, borderColor, n}, + {v3, n, borderColor}, + {v4, -n, borderColor}, + {v5, n, borderColor}, }; { triangles[ii++] = { - {v6, borderColor, t - n}, - {v4, borderColor, -n}, - {v5, borderColor, n}, + {v6, t - n, borderColor}, + {v4, -n, borderColor}, + {v5, n, borderColor}, }; triangles[ii++] = { - {v6, borderColor, t - n}, - {v7, borderColor, t + n}, - {v5, borderColor, n}, + {v6, t - n, borderColor}, + {v7, t + n, borderColor}, + {v5, n, borderColor}, }; } } @@ -1005,9 +1006,9 @@ void DrawNode::_drawPolygon(const Vec2* verts, Vec2 outer0 = v0 + offset0 * width; Vec2 outer1 = v1 + offset1 * width; - triangles[ii++] = {{inner0, borderColor, -n0}, {inner1, borderColor, -n0}, {outer1, borderColor, n0}}; + triangles[ii++] = {{inner0, -n0, borderColor}, {inner1, -n0, borderColor}, {outer1, n0, borderColor}}; - triangles[ii++] = {{inner0, borderColor, -n0}, {outer0, borderColor, n0}, {outer1, borderColor, n0}}; + triangles[ii++] = {{inner0, -n0, borderColor}, {outer0, n0, borderColor}, {outer1, n0, borderColor}}; } } } @@ -1016,7 +1017,7 @@ void DrawNode::_drawPolygon(const Vec2* verts, void DrawNode::_drawPoly(const Vec2* verts, unsigned int count, bool closedPolygon, - const Color4B& color, + const Color& color, float thickness, bool isconvex) { @@ -1032,24 +1033,24 @@ void DrawNode::_drawPoly(const Vec2* verts, int ii = 0; for (unsigned int i = 0; i < count - 1; i++) { - line[ii++] = {_vertices[i], color, Vec2::ZERO}; - line[ii++] = {_vertices[i + 1], color, Vec2::ZERO}; + line[ii++] = {_vertices[i], Vec2::ZERO, color}; + line[ii++] = {_vertices[i + 1], Vec2::ZERO, color}; } if (closedPolygon) { - line[ii++] = {_vertices[count - 1], color, Vec2::ZERO}; + line[ii++] = {_vertices[count - 1], Vec2::ZERO, color}; line[ii++] = line[0]; } } else { - _drawPolygon(verts, count, Color4B::TRANSPARENT, color, closedPolygon, thickness, isconvex); + _drawPolygon(verts, count, Color::TRANSPARENT, color, closedPolygon, thickness, isconvex); } } void DrawNode::_drawSegment(const Vec2& from, const Vec2& to, - const Color4B& color, + const Color& color, float thickness, DrawNode::EndType etStart, DrawNode::EndType etEnd) @@ -1085,7 +1086,7 @@ void DrawNode::_drawSegment(const Vec2& from, unsigned int vertex_count = 3 * ((etStart != DrawNode::EndType::Butt) ? 2 : 0) + 3 * 2 + 3 * ((etEnd != DrawNode::EndType::Butt) ? 2 : 0); - auto triangles = reinterpret_cast(expandBufferAndGetPointer(_triangles, vertex_count)); + auto triangles = reinterpret_cast(expandBufferAndGetPointer(_triangles, vertex_count)); _trianglesDirty = true; int ii = 0; @@ -1096,29 +1097,29 @@ void DrawNode::_drawSegment(const Vec2& from, case DrawNode::EndType::Square: triangles[ii++] = { - {v0, color, Vec2::ZERO}, - {v1, color, -n}, - {v2, color, n}, + {v0, Vec2::ZERO, color}, + {v1, -n, color}, + {v2, n, color}, }; triangles[ii++] = { - {v3, color, n}, - {v1, color, Vec2::ZERO}, - {v2, color, -n}, + {v3, n, color}, + {v1, Vec2::ZERO, color}, + {v2, -n, color}, }; break; case DrawNode::EndType::Round: triangles[ii++] = { - {v0, color, -(n + t)}, - {v1, color, n - t}, - {v2, color, -n}, + {v0, -(n + t), color}, + {v1, n - t, color}, + {v2, -n, color}, }; triangles[ii++] = { - {v3, color, n}, - {v1, color, n - t}, - {v2, color, -n}, + {v3, n, color}, + {v1, n - t, color}, + {v2, -n, color}, }; break; @@ -1128,15 +1129,15 @@ void DrawNode::_drawSegment(const Vec2& from, // BODY triangles[ii++] = { - {v3, color, n}, - {v4, color, -n}, - {v2, color, -n}, + {v3, n, color}, + {v4, -n, color}, + {v2, -n, color}, }; triangles[ii++] = { - {v3, color, n}, - {v4, color, -n}, - {v5, color, n}, + {v3, n, color}, + {v4, -n, color}, + {v5, n, color}, }; switch (etStart) @@ -1146,29 +1147,29 @@ void DrawNode::_drawSegment(const Vec2& from, case DrawNode::EndType::Square: triangles[ii++] = { - {v6, color, Vec2::ZERO}, - {v4, color, -n}, - {v5, color, n}, + {v6, Vec2::ZERO, color}, + {v4, -n, color}, + {v5, n, color}, }; triangles[ii++] = { - {v6, color, -n}, - {v7, color, Vec2::ZERO}, - {v5, color, n}, + {v6, -n, color}, + {v7, Vec2::ZERO, color}, + {v5, n, color}, }; break; case DrawNode::EndType::Round: triangles[ii++] = { - {v6, color, t - n}, - {v4, color, -n}, - {v5, color, n}, + {v6, t - n, color}, + {v4, -n, color}, + {v5, n, color}, }; triangles[ii++] = { - {v6, color, t - n}, - {v7, color, t + n}, - {v5, color, n}, + {v6, t - n, color}, + {v7, t + n, color}, + {v5, n, color}, }; break; @@ -1178,29 +1179,28 @@ void DrawNode::_drawSegment(const Vec2& from, } } // Internal function _drawLine => thickness is always 1 (fastes way to draw a line) -void DrawNode::_drawLine(const Vec2& from, const Vec2& to, const Color4B& color) +void DrawNode::_drawLine(const Vec2& from, const Vec2& to, const Color& color) { Vec2 vertices[2] = {from, to}; applyTransform(vertices, vertices, 2); - - auto line = expandBufferAndGetPointer(_lines, 2); + auto line = expandBufferAndGetPointer(_lines, 2); _linesDirty = true; - line[0] = {vertices[0], color, Vec2::ZERO}; - line[1] = {vertices[1], color, Vec2::ZERO}; + line[0] = {vertices[0], Vec2::ZERO, color}; + line[1] = {vertices[1], Vec2::ZERO, color}; } -void DrawNode::_drawDot(const Vec2& pos, float radius, const Color4B& color) +void DrawNode::_drawDot(const Vec2& pos, float radius, const Color& color) { unsigned int vertex_count = 2 * 3; - auto triangles = reinterpret_cast(expandBufferAndGetPointer(_triangles, vertex_count)); + auto triangles = reinterpret_cast(expandBufferAndGetPointer(_triangles, vertex_count)); _trianglesDirty = true; - V2F_C4B_T2F a = {Vec2(pos.x - radius, pos.y - radius), color, Vec2(-1.0f, -1.0f)}; - V2F_C4B_T2F b = {Vec2(pos.x - radius, pos.y + radius), color, Vec2(-1.0f, 1.0f)}; - V2F_C4B_T2F c = {Vec2(pos.x + radius, pos.y + radius), color, Vec2(1.0f, 1.0f)}; - V2F_C4B_T2F d = {Vec2(pos.x + radius, pos.y - radius), color, Vec2(1.0f, -1.0f)}; + V2F_T2F_C4F a = {Vec2(pos.x - radius, pos.y - radius), Vec2(-1.0f, -1.0f), color}; + V2F_T2F_C4F b = {Vec2(pos.x - radius, pos.y + radius), Vec2(-1.0f, 1.0f), color}; + V2F_T2F_C4F c = {Vec2(pos.x + radius, pos.y + radius), Vec2(1.0f, 1.0f), color}; + V2F_T2F_C4F d = {Vec2(pos.x + radius, pos.y - radius), Vec2(1.0f, -1.0f), color}; triangles[0] = {a, b, c}; triangles[1] = {a, c, d}; @@ -1213,8 +1213,8 @@ void DrawNode::_drawCircle(const Vec2& center, bool drawLineToCenter, float scaleX, float scaleY, - const Color4B& borderColor, - const Color4B& fillColor, + const Color& borderColor, + const Color& fillColor, bool solid, float thickness) { @@ -1245,24 +1245,23 @@ void DrawNode::_drawCircle(const Vec2& center, } void DrawNode::_drawColoredTriangle(Vec2* vertices3, - const Color4B* color3) + const Color* color3) { unsigned int vertex_count = 3; applyTransform(vertices3, vertices3, vertex_count); - auto triangles = reinterpret_cast(expandBufferAndGetPointer(_triangles, vertex_count)); + auto triangles = reinterpret_cast(expandBufferAndGetPointer(_triangles, vertex_count)); _trianglesDirty = true; - triangles[0] = {{vertices3[0], color3[0], Vec2::ZERO}, - {vertices3[1], color3[1], Vec2::ZERO}, - {vertices3[2], color3[2], Vec2::ZERO}}; + triangles[0] = {{vertices3[0], Vec2::ZERO, color3[0]}, + {vertices3[1], Vec2::ZERO, color3[1]}, + {vertices3[2], Vec2::ZERO, color3[2]}}; } - void DrawNode::_drawTriangle(Vec2* vertices3, - const Color4B& borderColor, - const Color4B& fillColor, + const Color& borderColor, + const Color& fillColor, bool solid, float thickness) { @@ -1276,12 +1275,12 @@ void DrawNode::_drawTriangle(Vec2* vertices3, { applyTransform(vertices3, vertices3, vertex_count); - auto triangles = reinterpret_cast(expandBufferAndGetPointer(_triangles, vertex_count)); + auto triangles = reinterpret_cast(expandBufferAndGetPointer(_triangles, vertex_count)); _trianglesDirty = true; - triangles[0] = {{vertices3[0], fillColor, Vec2::ZERO}, - {vertices3[1], fillColor, Vec2::ZERO}, - {vertices3[2], fillColor, Vec2::ZERO}}; + triangles[0] = {{vertices3[0], Vec2::ZERO, fillColor}, + {vertices3[1], Vec2::ZERO, fillColor}, + {vertices3[2], Vec2::ZERO, fillColor}}; } } @@ -1289,8 +1288,8 @@ void DrawNode::_drawAStar(const Vec2& center, float radiusI, // inner float radiusO, // outer unsigned int segments, - const Color4B& color, - const Color4B& filledColor, + const Color& color, + const Color& filledColor, float thickness, bool solid) { @@ -1321,7 +1320,7 @@ void DrawNode::_drawAStar(const Vec2& center, void DrawNode::_drawPoints(const Vec2* position, unsigned int numberOfPoints, const float pointSize, - const Color4B& color, + const Color& color, const DrawNode::PointType pointType) { if (properties.drawOrder == true) @@ -1334,7 +1333,7 @@ void DrawNode::_drawPoints(const Vec2* position, { case PointType::Circle: { - _drawCircle(position[i], pointSize4, 90, 32, false, 1.0f, 1.0f, Color4B(), color, true); + _drawCircle(position[i], pointSize4, 90, 32, false, 1.0f, 1.0f, Color(), color, true); break; } case PointType::Rect: @@ -1358,13 +1357,13 @@ void DrawNode::_drawPoints(const Vec2* position, for (unsigned int i = 0; i < numberOfPoints; i++) { - *(point + i) = {position[i], color, Vec2(pointSize, 0.0f)}; + *(point + i) = {position[i], Vec2(pointSize, 0.0f), color}; } } void DrawNode::_drawPoint(const Vec2& position, const float pointSize, - const Color4B& color, + const Color& color, const DrawNode::PointType pointType) { if (properties.drawOrder == true) @@ -1376,7 +1375,7 @@ void DrawNode::_drawPoint(const Vec2& position, { case PointType::Circle: { - _drawCircle(position, pointSize4, 90, 32, false, 1.0f, 1.0f, Color4B(), color, true); + _drawCircle(position, pointSize4, 90, 32, false, 1.0f, 1.0f, Color(), color, true); break; } case PointType::Rect: @@ -1408,7 +1407,7 @@ void DrawNode::_drawPoint(const Vec2& position, auto point = expandBufferAndGetPointer(_points, 1); _pointsDirty = true; - *point = {position, color, Vec2(pointSize, 0.0f)}; + *point = {position, Vec2(pointSize, 0.0f), color}; } } @@ -1419,8 +1418,8 @@ void DrawNode::_drawPie(const Vec2& center, int endAngle, float scaleX, float scaleY, - const Color4B& fillColor, - const Color4B& borderColor, + const Color& fillColor, + const Color& borderColor, DrawMode drawMode, float thickness) { @@ -1439,11 +1438,11 @@ void DrawNode::_drawPie(const Vec2& center, _drawCircle(center, radius, 0.0f, 360, false, scaleX, scaleY, borderColor, fillColor, true, thickness); break; case DrawMode::Outline: - _drawCircle(center, radius, 0.0f, 360, false, scaleX, scaleY, borderColor, Color4B::TRANSPARENT, true, + _drawCircle(center, radius, 0.0f, 360, false, scaleX, scaleY, borderColor, Color::TRANSPARENT, true, thickness); break; case DrawMode::Line: - _drawCircle(center, radius, 0.0f, 360, false, scaleX, scaleY, borderColor, Color4B::TRANSPARENT, true, + _drawCircle(center, radius, 0.0f, 360, false, scaleX, scaleY, borderColor, Color::TRANSPARENT, true, thickness); break; case DrawMode::Semi: @@ -1488,7 +1487,7 @@ void DrawNode::_drawPie(const Vec2& center, case DrawMode::Fill: _vertices[n++] = center; _vertices[n++] = _vertices[0]; - _drawPolygon(_vertices.data(), n, fillColor, Color4B::TRANSPARENT, true, 0, false); + _drawPolygon(_vertices.data(), n, fillColor, Color::TRANSPARENT, true, 0, false); _drawPoly(_vertices.data(), n, false, borderColor, thickness, true); break; case DrawMode::Outline: @@ -1500,7 +1499,7 @@ void DrawNode::_drawPie(const Vec2& center, _drawPoly(_vertices.data(), n, false, borderColor, thickness, true); break; case DrawMode::Semi: - if (fillColor != Color4B::TRANSPARENT) + if (fillColor != Color::TRANSPARENT) _drawPolygon(_vertices.data(), n, fillColor, borderColor, true, 0, false); _drawPoly(_vertices.data(), n, true, borderColor, thickness, true); break; diff --git a/core/2d/DrawNode.h b/core/2d/DrawNode.h index a2c9ccd2310f..1f3b6e521f9c 100644 --- a/core/2d/DrawNode.h +++ b/core/2d/DrawNode.h @@ -101,7 +101,7 @@ class AX_DLL DrawNode : public Node */ void drawPoint(const Vec2& point, const float pointSize, - const Color4B& color, + const Color& color, DrawNode::PointType pointType = DrawNode::PointType::Rect); /** Draw a group point. @@ -113,7 +113,7 @@ class AX_DLL DrawNode : public Node */ void drawPoints(const Vec2* position, unsigned int numberOfPoints, - const Color4B& color, + const Color& color, DrawNode::PointType pointType = DrawNode::PointType::Rect); /** Draw a group point. @@ -127,7 +127,7 @@ class AX_DLL DrawNode : public Node void drawPoints(const Vec2* position, unsigned int numberOfPoints, const float pointSize, - const Color4B& color, + const Color& color, DrawNode::PointType pointType = DrawNode::PointType::Rect); /** Draw an line from origin to destination with color. @@ -139,7 +139,7 @@ class AX_DLL DrawNode : public Node */ void drawLine(const Vec2& origin, const Vec2& destination, - const Color4B& color, + const Color& color, float thickness = 1.0f, DrawNode::EndType etStart = DrawNode::EndType::Round, DrawNode::EndType etEnd = DrawNode::EndType::Round); @@ -151,7 +151,7 @@ class AX_DLL DrawNode : public Node * @param destination The rectangle destination. * @param color The rectangle color. */ - void drawRect(const Vec2& origin, const Vec2& destination, const Color4B& color, float thickness = 1.0f); + void drawRect(const Vec2& origin, const Vec2& destination, const Color& color, float thickness = 1.0f); /** Draws a polygon given a pointer to point coordinates and the number of vertices measured in points. * The polygon can be closed or open. @@ -164,7 +164,7 @@ class AX_DLL DrawNode : public Node void drawPoly(const Vec2* poli, unsigned int numberOfPoints, bool closedPolygon, - const Color4B& color, + const Color& color, float thickness = 1.0f); /** Draws a circle given the center, radius and number of segments. @@ -186,7 +186,7 @@ class AX_DLL DrawNode : public Node bool drawLineToCenter, float scaleX, float scaleY, - const Color4B& color, + const Color& color, float thickness = 1.0f); /** Draws a circle given the center, radius and number of segments. @@ -204,7 +204,7 @@ class AX_DLL DrawNode : public Node float angle, unsigned int segments, bool drawLineToCenter, - const Color4B& color, + const Color& color, float thickness = 1.0f); /** Draws a star given the center, radiusI, radiusO and number of segments. @@ -220,7 +220,7 @@ class AX_DLL DrawNode : public Node float radiusI, float radiusO, unsigned int segments, - const Color4B& color, + const Color& color, float thickness = 1.0f); /** Draws a solid star given the center, radiusI, radiusO and number of segments. @@ -236,8 +236,8 @@ class AX_DLL DrawNode : public Node float radiusI, float radiusO, unsigned int segments, - const Color4B& color, - const Color4B& filledColor, + const Color& color, + const Color& filledColor, float thickness = 1.0f); /** Draws a quad bezier path. @@ -252,7 +252,7 @@ class AX_DLL DrawNode : public Node const Vec2& control, const Vec2& destination, unsigned int segments, - const Color4B& color, + const Color& color, float thickness = 1.0f); /** Draw a cubic bezier curve with color and number of segments @@ -269,7 +269,7 @@ class AX_DLL DrawNode : public Node const Vec2& control2, const Vec2& destination, unsigned int segments, - const Color4B& color, + const Color& color, float thickness = 1.0f); /** Draws a Cardinal Spline path. @@ -282,7 +282,7 @@ class AX_DLL DrawNode : public Node void drawCardinalSpline(PointArray* config, float tension, unsigned int segments, - const Color4B& color, + const Color& color, float thickness = 1.0f); /** Draws a Catmull Rom path. @@ -291,7 +291,7 @@ class AX_DLL DrawNode : public Node * @param segments The number of segments. * @param color The Catmull Rom color. */ - void drawCatmullRom(PointArray* points, unsigned int segments, const Color4B& color, float thickness = 1.0f); + void drawCatmullRom(PointArray* points, unsigned int segments, const Color& color, float thickness = 1.0f); /** draw a dot at a position, with a given radius and color. * @@ -299,7 +299,7 @@ class AX_DLL DrawNode : public Node * @param radius The dot radius. * @param color The dot color. */ - void drawDot(const Vec2& pos, float radius, const Color4B& color); + void drawDot(const Vec2& pos, float radius, const Color& color); /** Draws a rectangle with 4 points. * @@ -313,7 +313,7 @@ class AX_DLL DrawNode : public Node const Vec2& p2, const Vec2& p3, const Vec2& p4, - const Color4B& color, + const Color& color, float thickness = 1.0f); /** Draws a solid rectangle given the origin and destination point measured in points. @@ -326,9 +326,9 @@ class AX_DLL DrawNode : public Node */ void drawSolidRect(const Vec2& origin, const Vec2& destination, - const Color4B& color, + const Color& color, float thickness = 0, - const Color4B& borderColor = Color4B(0, 0, 0, 0)); + const Color& borderColor = Color(0, 0, 0, 0)); /** Draws a solid polygon given a pointer to CGPoint coordinates, the number of vertices measured in points, and a * color. @@ -340,9 +340,9 @@ class AX_DLL DrawNode : public Node */ void drawSolidPoly(const Vec2* poli, unsigned int numberOfPoints, - const Color4B& color, + const Color& color, float thickness = 0, - const Color4B& borderColor = Color4B(0, 0, 0, 0), + const Color& borderColor = Color(0, 0, 0, 0), bool isconvex = false); /** Draws a solid circle given the center, radius and number of segments. @@ -364,9 +364,9 @@ class AX_DLL DrawNode : public Node unsigned int segments, float scaleX, float scaleY, - const Color4B& fillColor, + const Color& fillColor, float thickness, - const Color4B& borderColor, + const Color& borderColor, bool drawLineToCenter = false); /** Draws a solid circle given the center, radius and number of segments. @@ -385,7 +385,7 @@ class AX_DLL DrawNode : public Node unsigned int segments, float scaleX, float scaleY, - const Color4B& color); + const Color& color); /** Draws a solid circle given the center, radius and number of segments. * @param center The circle center point. @@ -395,7 +395,7 @@ class AX_DLL DrawNode : public Node * @param color The solid circle color. * @js NA */ - void drawSolidCircle(const Vec2& center, float radius, float angle, unsigned int segments, const Color4B& color); + void drawSolidCircle(const Vec2& center, float radius, float angle, unsigned int segments, const Color& color); /** Draws a pie given the center, radius, angle, start angle, end angle and number of segments. * @param center The circle center point. @@ -417,8 +417,8 @@ class AX_DLL DrawNode : public Node int endAngle, float scaleX, float scaleY, - const Color4B& fillColor, - const Color4B& borderColor, + const Color& fillColor, + const Color& borderColor, DrawMode drawMode = DrawMode::Outline, float thickness = 1.0f); @@ -442,14 +442,9 @@ class AX_DLL DrawNode : public Node int endAngle, float scaleX, float scaleY, - const Color4B& color, + const Color& color, DrawMode drawMode = DrawMode::Outline); - void setIsConvex(bool isConvex) - { - AXLOGW("'setIsConvex()' No longer supported. Use the new drawPolygon API."); - }; - /** draw a segment with a radius and color. * @@ -461,7 +456,7 @@ class AX_DLL DrawNode : public Node void drawSegment(const Vec2& from, const Vec2& to, float radius, - const Color4B& color, + const Color& color, DrawNode::EndType etStart = DrawNode::EndType::Round, DrawNode::EndType etEnd = DrawNode::EndType::Round); @@ -480,18 +475,18 @@ class AX_DLL DrawNode : public Node */ void drawPolygon(const Vec2* verts, int count, - const Color4B& fillColor, + const Color& fillColor, float thickness, - const Color4B& borderColor, + const Color& borderColor, bool isconvex = false); - void drawPolygon(const Vec2* verts, int count, float thickness, const Color4B& borderColor, bool isconvex = false); + void drawPolygon(const Vec2* verts, int count, float thickness, const Color& borderColor, bool isconvex = false); void drawSolidPolygon(const Vec2* verts, int count, - const Color4B& fillColor, + const Color& fillColor, float thickness, - const Color4B& borderColor, + const Color& borderColor, bool isconvex = false); /** draw a triangle with color. @@ -503,25 +498,25 @@ class AX_DLL DrawNode : public Node * @js NA */ - void drawColoredTriangle(const Vec2* vertices3, const Color4B* color3); - void drawTriangle(const Vec2* vertices3, const Color4B& color); + void drawColoredTriangle(const Vec2* vertices3, const Color* color3); + void drawTriangle(const Vec2* vertices3, const Color& color); - void drawTriangle(const Vec2& p1, const Vec2& p2, const Vec2& p3, const Color4B& color); + void drawTriangle(const Vec2& p1, const Vec2& p2, const Vec2& p3, const Color& color); void drawSolidTriangle(const Vec2* vertices3, - const Color4B& fillColor, - const Color4B& borderColor, + const Color& fillColor, + const Color& borderColor, float thickness = 1.0f); void drawSolidTriangle(const Vec2& p1, const Vec2& p2, const Vec2& p3, - const Color4B& fillColor, - const Color4B& borderColor, + const Color& fillColor, + const Color& borderColor, float thickness = 1.0f); /** Clear the geometry in the node's buffer. */ - void clear(); + virtual void clear(); /** Get the color mixed mode. * @lua NA */ @@ -578,46 +573,46 @@ class AX_DLL DrawNode : public Node CustomCommand _customCommandPoint; CustomCommand _customCommandLine; - axstd::pod_vector _triangles; - axstd::pod_vector _points; - axstd::pod_vector _lines; + axstd::pod_vector _triangles; + axstd::pod_vector _points; + axstd::pod_vector _lines; private: // Internal function _drawPoint void _drawPoint(const Vec2& position, const float pointSize, - const Color4B& color, + const Color& color, const DrawNode::PointType pointType); // Internal function _drawPoints void _drawPoints(const Vec2* position, unsigned int numberOfPoints, const float pointSize, - const Color4B& color, + const Color& color, const DrawNode::PointType pointType); // Internal function _drawDot - void _drawDot(const Vec2& pos, float radius, const Color4B& color); + void _drawDot(const Vec2& pos, float radius, const Color& color); // Internal function _drawTriangle // Note: modifies supplied vertex array void _drawTriangle(Vec2* vertices3, - const Color4B& borderColor, - const Color4B& fillColor, + const Color& borderColor, + const Color& fillColor, bool solid = true, float thickness = 0.0f); void _drawColoredTriangle(Vec2* vertices3, - const Color4B* color3); + const Color* color3); // Internal function _drawAStar void _drawAStar(const Vec2& center, float radiusI, float radiusO, unsigned int segments, - const Color4B& color, - const Color4B& filledColor, + const Color& color, + const Color& filledColor, float thickness = 1.0f, bool solid = false); @@ -625,26 +620,26 @@ class AX_DLL DrawNode : public Node void _drawPoly(const Vec2* poli, unsigned int numberOfPoints, bool closedPolygon, - const Color4B& color, + const Color& color, float thickness = 1.0f, bool isconvex = true); // Internal function _drawPolygon void _drawPolygon(const Vec2* verts, unsigned int count, - const Color4B& fillColor, - const Color4B& borderColor, + const Color& fillColor, + const Color& borderColor, bool closedPolygon = true, float thickness = 1.0f, bool isconvex = true); // Internal function _drawLine - void _drawLine(const Vec2& from, const Vec2& to, const Color4B& color); + void _drawLine(const Vec2& from, const Vec2& to, const Color& color); // Internal function _drawSegment void _drawSegment(const Vec2& origin, const Vec2& destination, - const Color4B& color, + const Color& color, float thickness = 1.0f, DrawNode::EndType etStart = DrawNode::EndType::Square, DrawNode::EndType etEnd = DrawNode::EndType::Square); @@ -657,8 +652,8 @@ class AX_DLL DrawNode : public Node bool drawLineToCenter, float scaleX, float scaleY, - const Color4B& borderColor, - const Color4B& fillColor, + const Color& borderColor, + const Color& fillColor, bool solid, float thickness = 1.0f); @@ -670,8 +665,8 @@ class AX_DLL DrawNode : public Node int endAngle, float scaleX, float scaleY, - const Color4B& fillColor, - const Color4B& borderColor, + const Color& fillColor, + const Color& borderColor, DrawMode drawMode, float thickness = 1.0f); diff --git a/core/2d/FastTMXLayer.cpp b/core/2d/FastTMXLayer.cpp index b96eddd8d2a6..58b31866444c 100644 --- a/core/2d/FastTMXLayer.cpp +++ b/core/2d/FastTMXLayer.cpp @@ -275,7 +275,7 @@ void FastTMXLayer::updateTiles(const Rect& culledRect) void FastTMXLayer::updateVertexBuffer() { - unsigned int vertexBufferSize = (unsigned int)(sizeof(V3F_C4B_T2F) * _totalQuads.size() * 4); + unsigned int vertexBufferSize = (unsigned int)(sizeof(V3F_T2F_C4B) * _totalQuads.size() * 4); if (!_vertexBuffer) { _vertexBuffer = backend::DriverBase::getInstance()->newBuffer(vertexBufferSize, backend::BufferType::VERTEX, backend::BufferUsage::STATIC); @@ -503,7 +503,7 @@ void FastTMXLayer::updateTotalQuads() _tileToQuadIndex.resize(int(_layerSize.width * _layerSize.height), -1); _indicesVertexZOffsets.clear(); - auto color = Color4B::WHITE; + auto color = Color32::WHITE; color.a = getDisplayedOpacity(); if (_texture->hasPremultipliedAlpha()) @@ -569,33 +569,33 @@ void FastTMXLayer::updateTotalQuads() if (tileGID & kTMXTileDiagonalFlag) { // FIXME: not working correctly - quad.bl.vertices.x = left; - quad.bl.vertices.y = bottom; - quad.bl.vertices.z = z; - quad.br.vertices.x = left; - quad.br.vertices.y = top; - quad.br.vertices.z = z; - quad.tl.vertices.x = right; - quad.tl.vertices.y = bottom; - quad.tl.vertices.z = z; - quad.tr.vertices.x = right; - quad.tr.vertices.y = top; - quad.tr.vertices.z = z; + quad.bl.position.x = left; + quad.bl.position.y = bottom; + quad.bl.position.z = z; + quad.br.position.x = left; + quad.br.position.y = top; + quad.br.position.z = z; + quad.tl.position.x = right; + quad.tl.position.y = bottom; + quad.tl.position.z = z; + quad.tr.position.x = right; + quad.tr.position.y = top; + quad.tr.position.z = z; } else { - quad.bl.vertices.x = left; - quad.bl.vertices.y = bottom; - quad.bl.vertices.z = z; - quad.br.vertices.x = right; - quad.br.vertices.y = bottom; - quad.br.vertices.z = z; - quad.tl.vertices.x = left; - quad.tl.vertices.y = top; - quad.tl.vertices.z = z; - quad.tr.vertices.x = right; - quad.tr.vertices.y = top; - quad.tr.vertices.z = z; + quad.bl.position.x = left; + quad.bl.position.y = bottom; + quad.bl.position.z = z; + quad.br.position.x = right; + quad.br.position.y = bottom; + quad.br.position.z = z; + quad.tl.position.x = left; + quad.tl.position.y = top; + quad.tl.position.z = z; + quad.tr.position.x = right; + quad.tr.position.y = top; + quad.tr.position.z = z; } // texcoords @@ -609,19 +609,19 @@ void FastTMXLayer::updateTotalQuads() float ptx = 1.0 / (_tileSet->_imageSize.x * tileSize.x); float pty = 1.0 / (_tileSet->_imageSize.y * tileSize.y); - quad.bl.texCoords.u = left + ptx; - quad.bl.texCoords.v = bottom + pty; - quad.br.texCoords.u = right - ptx; - quad.br.texCoords.v = bottom + pty; - quad.tl.texCoords.u = left + ptx; - quad.tl.texCoords.v = top - pty; - quad.tr.texCoords.u = right - ptx; - quad.tr.texCoords.v = top - pty; - - quad.bl.colors = color; - quad.br.colors = color; - quad.tl.colors = color; - quad.tr.colors = color; + quad.bl.texCoord.u = left + ptx; + quad.bl.texCoord.v = bottom + pty; + quad.br.texCoord.u = right - ptx; + quad.br.texCoord.v = bottom + pty; + quad.tl.texCoord.u = left + ptx; + quad.tl.texCoord.v = top - pty; + quad.tr.texCoord.u = right - ptx; + quad.tr.texCoord.v = top - pty; + + quad.bl.color = color; + quad.br.color = color; + quad.tl.color = color; + quad.tr.color = color; ++quadIndex; } diff --git a/core/2d/FastTMXLayer.h b/core/2d/FastTMXLayer.h index 09b597c2e394..6a7a0c99f179 100644 --- a/core/2d/FastTMXLayer.h +++ b/core/2d/FastTMXLayer.h @@ -371,7 +371,7 @@ class AX_DLL FastTMXLayer : public Node float _cameraZoomDirty; std::vector _tileToQuadIndex; - std::vector _totalQuads; + std::vector _totalQuads; #ifdef AX_FAST_TILEMAP_32_BIT_INDICES std::vector _indices; #else diff --git a/core/2d/FastTMXTiledMap.h b/core/2d/FastTMXTiledMap.h index e14843baac90..d586e9ac8273 100644 --- a/core/2d/FastTMXTiledMap.h +++ b/core/2d/FastTMXTiledMap.h @@ -207,9 +207,7 @@ class AX_DLL FastTMXTiledMap : public Node * animations are not enabled by default */ void setTileAnimEnabled(bool enabled); -#ifndef AX_CORE_PROFILE - AX_DEPRECATED(2.1) int getLayerNum() const { return getLayerCount(); } -#endif + int getLayerCount() const { return _layerCount; } std::string_view getResourceFile() const { return _tmxFile; } diff --git a/core/2d/FontAtlas.cpp b/core/2d/FontAtlas.cpp index a7969ecb6f9d..835a0688044f 100644 --- a/core/2d/FontAtlas.cpp +++ b/core/2d/FontAtlas.cpp @@ -43,11 +43,10 @@ #include "fmt/format.h" #include "base/ZipUtils.h" -#include "base/PaddedString.h" +#include "base/json.h" namespace ax { - const int FontAtlas::CacheTextureWidth = 512; const int FontAtlas::CacheTextureHeight = 512; const char* FontAtlas::CMD_PURGE_FONTATLAS = "__ax_PURGE_FONTATLAS"; @@ -55,11 +54,12 @@ const char* FontAtlas::CMD_RESET_FONTATLAS = "__ax_RESET_FONTATLAS"; void FontAtlas::loadFontAtlas(std::string_view fontatlasFile, hlookup::string_map& outAtlasMap) { - using namespace simdjson; + using namespace ::simdjson; try { - auto strJson = PaddedString::load(fontatlasFile); + simdjson::PaddedString strJson; + FileUtils::getInstance()->getContents(fontatlasFile, &strJson); ondemand::parser parser; ondemand::document settings = parser.iterate(strJson); std::string_view type = settings["type"]; diff --git a/core/2d/FontAtlasCache.cpp b/core/2d/FontAtlasCache.cpp index 36a9d01245ef..8af5cd862d8d 100644 --- a/core/2d/FontAtlasCache.cpp +++ b/core/2d/FontAtlasCache.cpp @@ -144,12 +144,7 @@ FontAtlas* FontAtlasCache::getFontAtlasFNT(std::string_view fontFileName, const return nullptr; } -#ifndef AX_CORE_PROFILE -FontAtlas* FontAtlasCache::getFontAtlasFNT(std::string_view fontFileName, const Vec2& imageOffset) -{ - return getFontAtlasFNT(fontFileName, Rect(imageOffset.x, imageOffset.y, 0, 0), false); -} -#endif + FontAtlas* FontAtlasCache::getFontAtlasCharMap(std::string_view plistFile) { std::string_view atlasName = plistFile; @@ -260,12 +255,6 @@ void FontAtlasCache::reloadFontAtlasFNT(std::string_view fontFileName, const Rec _atlasMap.emplace(std::move(atlasName), tempAtlas); } } -#ifndef AX_CORE_PROFILE -void FontAtlasCache::reloadFontAtlasFNT(std::string_view fontFileName, const Vec2& imageOffset) -{ - reloadFontAtlasFNT(fontFileName, Rect(imageOffset.x, imageOffset.y, 0, 0), false); -} -#endif void FontAtlasCache::unloadFontAtlasTTF(std::string_view fontFileName) { diff --git a/core/2d/FontAtlasCache.h b/core/2d/FontAtlasCache.h index 964d52c30904..b4862887bbd9 100644 --- a/core/2d/FontAtlasCache.h +++ b/core/2d/FontAtlasCache.h @@ -52,9 +52,7 @@ class AX_DLL FontAtlasCache static FontAtlas* getFontAtlasFNT(std::string_view fontFileName); static FontAtlas* getFontAtlasFNT(std::string_view fontFileName, std::string_view subTextureKey); static FontAtlas* getFontAtlasFNT(std::string_view fontFileName, const Rect& imageRect, bool imageRotated); -#ifndef AX_CORE_PROFILE - AX_DEPRECATED(2.1) static FontAtlas* getFontAtlasFNT(std::string_view fontFileName, const Vec2& imageOffset); -#endif + static FontAtlas* getFontAtlasCharMap(std::string_view charMapFile, int itemWidth, int itemHeight, @@ -74,10 +72,7 @@ class AX_DLL FontAtlasCache otherwise, it will cause program crash! */ static void reloadFontAtlasFNT(std::string_view fontFileName, const Rect& imageRect, bool imageRotated); -#ifndef AX_CORE_PROFILE - AX_DEPRECATED(2.1) static void reloadFontAtlasFNT(std::string_view fontFileName, - const Vec2& imageOffset = Vec2::ZERO); -#endif + /** Unload all texture atlas texture create by special file name. CAUTION : All component use this font texture should be reset font name, though the file name is same! otherwise, it will cause program crash! diff --git a/core/2d/FontFNT.cpp b/core/2d/FontFNT.cpp index 1160e6702be0..a38708336664 100644 --- a/core/2d/FontFNT.cpp +++ b/core/2d/FontFNT.cpp @@ -444,7 +444,7 @@ void BMFontConfiguration::parseCommonArguments(const char* line) auto tmp = strstr(line, "lineHeight=") + 11; sscanf(tmp, "%d", &_commonHeight); -#if _AX_DEBUG > 0 +#if defined(_AX_DEBUG) && _AX_DEBUG > 0 // scaleW. sanity check int value; tmp = strstr(tmp, "scaleW=") + 7; @@ -590,12 +590,7 @@ FontFNT* FontFNT::create(std::string_view fntFilePath) tempFont->autorelease(); return tempFont; } -#ifndef AX_CORE_PROFILE -FontFNT* FontFNT::create(std::string_view fntFilePath, const Vec2& imageOffset) -{ - return create(fntFilePath, Rect(imageOffset.x, imageOffset.y, 0, 0), false); -} -#endif + FontFNT::FontFNT(BMFontConfiguration* theContfig, const Rect& imageRect, bool imageRotated) : _configuration(theContfig), _imageRectInPoints(AX_RECT_PIXELS_TO_POINTS(imageRect)), _imageRotated(imageRotated) { diff --git a/core/2d/FontFNT.h b/core/2d/FontFNT.h index 1df12c5673dd..4bf621a8f510 100644 --- a/core/2d/FontFNT.h +++ b/core/2d/FontFNT.h @@ -149,9 +149,7 @@ class AX_DLL FontFNT : public Font static FontFNT* create(std::string_view fntFilePath, const Rect& imageRect, bool imageRotated); static FontFNT* create(std::string_view fntFilePath, std::string_view subTextureKey); static FontFNT* create(std::string_view fntFilePath); -#ifndef AX_CORE_PROFILE - AX_DEPRECATED(2.1) static FontFNT* create(std::string_view fntFilePath, const Vec2& imageOffset = Vec2::ZERO); -#endif + /** Purges the cached data. Removes from memory the cached configurations and the atlas name dictionary. */ diff --git a/core/2d/Grid.h b/core/2d/Grid.h index 3b17f7e18e55..a1a49882f3ef 100644 --- a/core/2d/Grid.h +++ b/core/2d/Grid.h @@ -152,7 +152,7 @@ class AX_DLL GridBase : public Object Director::Projection _directorProjection = Director::Projection::_2D; Rect _gridRect; - Color4F _clearColor = {0, 0, 0, 0}; + Color _clearColor = {0, 0, 0, 0}; CustomCommand _drawCommand; //CallbackCommand _beforeDrawCommand; diff --git a/core/2d/Label.cpp b/core/2d/Label.cpp index f0cb218762c2..c13200f6d784 100644 --- a/core/2d/Label.cpp +++ b/core/2d/Label.cpp @@ -136,10 +136,10 @@ class LabelLetter : public Sprite float dx = x1 * cr - y2 * sr2 + x; float dy = x1 * sr + y2 * cr2 + y; - _quad.bl.vertices.set(SPRITE_RENDER_IN_SUBPIXEL(ax), SPRITE_RENDER_IN_SUBPIXEL(ay), _positionZ); - _quad.br.vertices.set(SPRITE_RENDER_IN_SUBPIXEL(bx), SPRITE_RENDER_IN_SUBPIXEL(by), _positionZ); - _quad.tl.vertices.set(SPRITE_RENDER_IN_SUBPIXEL(dx), SPRITE_RENDER_IN_SUBPIXEL(dy), _positionZ); - _quad.tr.vertices.set(SPRITE_RENDER_IN_SUBPIXEL(cx), SPRITE_RENDER_IN_SUBPIXEL(cy), _positionZ); + _quad.bl.position.set(SPRITE_RENDER_IN_SUBPIXEL(ax), SPRITE_RENDER_IN_SUBPIXEL(ay), _positionZ); + _quad.br.position.set(SPRITE_RENDER_IN_SUBPIXEL(bx), SPRITE_RENDER_IN_SUBPIXEL(by), _positionZ); + _quad.tl.position.set(SPRITE_RENDER_IN_SUBPIXEL(dx), SPRITE_RENDER_IN_SUBPIXEL(dy), _positionZ); + _quad.tr.position.set(SPRITE_RENDER_IN_SUBPIXEL(cx), SPRITE_RENDER_IN_SUBPIXEL(cy), _positionZ); if (_textureAtlas) { @@ -165,18 +165,15 @@ class LabelLetter : public Sprite { displayedOpacity = 0.0f; } - Color4B color4(_displayedColor.r, _displayedColor.g, _displayedColor.b, displayedOpacity); + Color color(_displayedColor, displayedOpacity / 255.0f); // special opacity for premultiplied textures if (_opacityModifyRGB) - { - color4.r *= displayedOpacity / 255.0f; - color4.g *= displayedOpacity / 255.0f; - color4.b *= displayedOpacity / 255.0f; - } - _quad.bl.colors = color4; - _quad.br.colors = color4; - _quad.tl.colors = color4; - _quad.tr.colors = color4; + color.premultiplyAlpha(); + + _quad.bl.color = color; + _quad.br.color = color; + _quad.tl.color = color; + _quad.tr.color = color; _textureAtlas->updateQuad(_quad, _atlasIndex); } @@ -360,17 +357,6 @@ Label* Label::createWithBMFont(std::string_view bmfontPath, return nullptr; } -#ifndef AX_CORE_PROFILE -Label* Label::createWithBMFont(std::string_view bmfontPath, - std::string_view text, - const TextHAlignment& hAlignment, - int maxLineWidth, - const Vec2& imageOffset) -{ - return createWithBMFont(bmfontPath, text, hAlignment, maxLineWidth, Rect(imageOffset.x, imageOffset.y, 0, 0), - false); -} -#endif Label* Label::createWithCharMap(std::string_view plistFile) { auto ret = new Label(); @@ -631,9 +617,9 @@ void Label::reset() _hAlignment = TextHAlignment::LEFT; _vAlignment = TextVAlignment::TOP; - _effectColorF = Color4F::BLACK; - _textColor = Color4B::WHITE; - _textColorF = Color4F::WHITE; + _effectColor = Color::BLACK; + _textColor = Color32::WHITE; + _textColorF = Color::WHITE; setColor(Color3B::WHITE); _shadowDirty = false; @@ -930,12 +916,7 @@ bool Label::setBMFontFilePath(std::string_view bmfontFilePath, std::string_view return true; } -#ifndef AX_CORE_PROFILE -bool Label::setBMFontFilePath(std::string_view bmfontFilePath, const Vec2& imageOffset, float fontSize) -{ - return setBMFontFilePath(bmfontFilePath, Rect(imageOffset.x, imageOffset.y, 0, 0), false); -} -#endif + void Label::setString(std::string_view text) { if (text.compare(_utf8Text)) @@ -1362,7 +1343,7 @@ void Label::scaleFontSize(float fontSize) } } -void Label::enableGlow(const Color4B& glowColor) +void Label::enableGlow(const Color32& glowColor) { if (_currentLabelType == LabelType::TTF) { @@ -1375,15 +1356,15 @@ void Label::enableGlow(const Color4B& glowColor) _contentDirty = true; } _currLabelEffect = LabelEffect::GLOW; - _effectColorF.r = glowColor.r / 255.0f; - _effectColorF.g = glowColor.g / 255.0f; - _effectColorF.b = glowColor.b / 255.0f; - _effectColorF.a = glowColor.a / 255.0f; + _effectColor.r = glowColor.r / 255.0f; + _effectColor.g = glowColor.g / 255.0f; + _effectColor.b = glowColor.b / 255.0f; + _effectColor.a = glowColor.a / 255.0f; updateShaderProgram(); } } -void Label::enableOutline(const Color4B& outlineColor, int outlineSize /* = -1 */) +void Label::enableOutline(const Color32& outlineColor, int outlineSize /* = -1 */) { AXASSERT(_currentLabelType == LabelType::STRING_TEXTURE || _currentLabelType == LabelType::TTF, "Only supported system font and TTF!"); @@ -1392,10 +1373,10 @@ void Label::enableOutline(const Color4B& outlineColor, int outlineSize /* = -1 * { if (_currentLabelType == LabelType::TTF) { - _effectColorF.r = outlineColor.r / 255.0f; - _effectColorF.g = outlineColor.g / 255.0f; - _effectColorF.b = outlineColor.b / 255.0f; - _effectColorF.a = outlineColor.a / 255.0f; + _effectColor.r = outlineColor.r / 255.0f; + _effectColor.g = outlineColor.g / 255.0f; + _effectColor.b = outlineColor.b / 255.0f; + _effectColor.a = outlineColor.a / 255.0f; if (!_useDistanceField) { // not SDF, request font atlas from feetype @@ -1412,12 +1393,12 @@ void Label::enableOutline(const Color4B& outlineColor, int outlineSize /* = -1 * updateShaderProgram(); } } - else if (_effectColorF != outlineColor || _outlineSize != outlineSize) + else if (_effectColor != outlineColor || _outlineSize != outlineSize) { - _effectColorF.r = outlineColor.r / 255.f; - _effectColorF.g = outlineColor.g / 255.f; - _effectColorF.b = outlineColor.b / 255.f; - _effectColorF.a = outlineColor.a / 255.f; + _effectColor.r = outlineColor.r / 255.f; + _effectColor.g = outlineColor.g / 255.f; + _effectColor.b = outlineColor.b / 255.f; + _effectColor.a = outlineColor.a / 255.f; _currLabelEffect = LabelEffect::OUTLINE; _contentDirty = true; } @@ -1425,7 +1406,7 @@ void Label::enableOutline(const Color4B& outlineColor, int outlineSize /* = -1 * } } -void Label::enableShadow(const Color4B& shadowColor /* = Color4B::BLACK */, +void Label::enableShadow(const Color32& shadowColor /* = Color32::BLACK */, const Vec2& offset /* = Vec2(2 ,-2)*/, int /* blurRadius = 0 */) { @@ -1446,7 +1427,7 @@ void Label::enableShadow(const Color4B& shadowColor /* = Color4B::BLACK */, auto fontDef = _getFontDefinition(); if (_shadowNode) { - if (shadowColor != _shadowColor4F) + if (shadowColor != _shadowColor) { _shadowNode->release(); _shadowNode = nullptr; @@ -1463,10 +1444,10 @@ void Label::enableShadow(const Color4B& shadowColor /* = Color4B::BLACK */, } } - _shadowColor4F.r = shadowColor.r / 255.0f; - _shadowColor4F.g = shadowColor.g / 255.0f; - _shadowColor4F.b = shadowColor.b / 255.0f; - _shadowColor4F.a = shadowColor.a / 255.0f; + _shadowColor.r = shadowColor.r / 255.0f; + _shadowColor.g = shadowColor.g / 255.0f; + _shadowColor.b = shadowColor.b / 255.0f; + _shadowColor.a = shadowColor.a / 255.0f; if (_currentLabelType == LabelType::BMFONT || _currentLabelType == LabelType::CHARMAP) { @@ -1484,7 +1465,7 @@ void Label::enableBold() if (!_boldEnabled) { // bold is implemented with outline - enableShadow(Color4B::WHITE, Vec2(0.9f, 0), 0); + enableShadow(Color32::WHITE, Vec2(0.9f, 0), 0); // add one to kerning setAdditionalKerning(_additionalKerning + 1); _boldEnabled = true; @@ -1747,8 +1728,8 @@ void Label::updateContent() if (_lineDrawNode) { - Color4B lineColor = Color4B(_displayedColor); - if (_textColor != Color4B::WHITE && _textColor != lineColor) + Color32 lineColor = Color32(_displayedColor); + if (_textColor != Color32::WHITE && _textColor != lineColor) lineColor = _textColor; _lineDrawNode->clear(); @@ -1769,14 +1750,14 @@ void Label::updateContent() { auto y = nextY - lineHeight / 2; _lineDrawNode->drawLine(Vec2(_linesOffsetX[i], y), Vec2(_linesWidth[i] + _linesOffsetX[i], y), - Color4F(lineColor), thickness); + Color(lineColor), thickness); } if (_underlineEnabled) { auto y = nextY - lineHeight; _lineDrawNode->drawLine(Vec2(_linesOffsetX[i], y), Vec2(_linesWidth[i] + _linesOffsetX[i], y), - Color4F(lineColor), thickness); + Color(lineColor), thickness); } nextY -= lineHeight + lineSpacing; @@ -1798,7 +1779,7 @@ void Label::updateContent() for (int i = 0; i < _numberOfLines; ++i) { float y = baseY + lineSize * i; - _lineDrawNode->drawLine(Vec2(0.0f, y), Vec2(spriteSize.width, y), Color4F(lineColor), thickness); + _lineDrawNode->drawLine(Vec2(0.0f, y), Vec2(spriteSize.width, y), Color(lineColor), thickness); } } @@ -1809,7 +1790,7 @@ void Label::updateContent() for (int i = 0; i < _numberOfLines; ++i) { float y = baseY + lineSize * i; - _lineDrawNode->drawLine(Vec2(0.0f, y), Vec2(spriteSize.width, y), Color4F(lineColor), thickness); + _lineDrawNode->drawLine(Vec2(0.0f, y), Vec2(spriteSize.width, y), Color(lineColor), thickness); } } } @@ -1824,7 +1805,7 @@ void Label::updateContent() _debugDrawNode->clear(); Vec2 vertices[4] = {Vec2::ZERO, Vec2(_contentSize.width, 0.0f), Vec2(_contentSize.width, _contentSize.height), Vec2(0.0f, _contentSize.height)}; - _debugDrawNode->drawPoly(vertices, 4, true, Color4B::WHITE); + _debugDrawNode->drawPoly(vertices, 4, true, Color::WHITE); #endif } @@ -1857,7 +1838,7 @@ void Label::updateBuffer(TextureAtlas* textureAtlas, CustomCommand& customComman { if (textureAtlas->getTotalQuads() > customCommand.getVertexCapacity()) { - customCommand.createVertexBuffer((unsigned int)sizeof(V3F_C4B_T2F_Quad), + customCommand.createVertexBuffer((unsigned int)sizeof(V3F_T2F_C4B_Quad), (unsigned int)textureAtlas->getTotalQuads(), CustomCommand::BufferUsage::DYNAMIC); customCommand.createIndexBuffer(CustomCommand::IndexFormat::U_SHORT, @@ -1865,7 +1846,7 @@ void Label::updateBuffer(TextureAtlas* textureAtlas, CustomCommand& customComman CustomCommand::BufferUsage::DYNAMIC); } customCommand.updateVertexBuffer(textureAtlas->getQuads(), - (unsigned int)(textureAtlas->getTotalQuads() * sizeof(V3F_C4B_T2F_Quad))); + (unsigned int)(textureAtlas->getTotalQuads() * sizeof(V3F_T2F_C4B_Quad))); customCommand.updateIndexBuffer(textureAtlas->getIndices(), (unsigned int)(textureAtlas->getTotalQuads() * 6 * sizeof(unsigned short))); customCommand.setIndexDrawInfo(0, (unsigned int)(textureAtlas->getTotalQuads() * 6)); @@ -1895,14 +1876,13 @@ void Label::updateEffectUniforms(BatchCommand& batch, case LabelEffect::OUTLINE: { int effectType = 0; - Vec4 effectColor(_effectColorF.r, _effectColorF.g, _effectColorF.b, _effectColorF.a); + Vec4 effectColor(_effectColor.r, _effectColor.g, _effectColor.b, _effectColor.a); // draw shadow if (_shadowEnabled) { effectType = 2; - Vec4 shadowColor = Vec4(_shadowColor4F.r, _shadowColor4F.g, _shadowColor4F.b, _shadowColor4F.a); auto* programStateShadow = batch.shadowCommand.getPipelineDescriptor().programState; - programStateShadow->setUniform(_effectColorLocation, &shadowColor, sizeof(Vec4)); + programStateShadow->setUniform(_effectColorLocation, &_shadowColor, sizeof(ax::Color)); programStateShadow->setUniform(_effectTypeLocation, &effectType, sizeof(effectType)); batch.shadowCommand.init(_globalZOrder); renderer->addCommand(&batch.shadowCommand); @@ -1912,7 +1892,7 @@ void Label::updateEffectUniforms(BatchCommand& batch, { // distance outline effectColor.w = _outlineSize > 0 ? _outlineSize : _fontConfig.outlineSize; batch.textCommand.getPipelineDescriptor().programState->setUniform(_effectColorLocation, &effectColor, - sizeof(Vec4)); + sizeof(ax::Color)); } else { @@ -1942,9 +1922,8 @@ void Label::updateEffectUniforms(BatchCommand& batch, { if (_shadowEnabled) { - Vec4 shadowColor = Vec4(_shadowColor4F.r, _shadowColor4F.g, _shadowColor4F.b, _shadowColor4F.a); auto* programStateShadow = batch.shadowCommand.getPipelineDescriptor().programState; - programStateShadow->setUniform(_textColorLocation, &shadowColor, sizeof(Vec4)); + programStateShadow->setUniform(_textColorLocation, &_shadowColor, sizeof(Vec4)); batch.shadowCommand.init(_globalZOrder); renderer->addCommand(&batch.shadowCommand); } @@ -1955,16 +1934,14 @@ void Label::updateEffectUniforms(BatchCommand& batch, // draw shadow if (_shadowEnabled) { - Vec4 shadowColor = Vec4(_shadowColor4F.r, _shadowColor4F.g, _shadowColor4F.b, _shadowColor4F.a); auto* programStateShadow = batch.shadowCommand.getPipelineDescriptor().programState; - programStateShadow->setUniform(_textColorLocation, &shadowColor, sizeof(Vec4)); - programStateShadow->setUniform(_effectColorLocation, &shadowColor, sizeof(Vec4)); + programStateShadow->setUniform(_textColorLocation, &_shadowColor, sizeof(Vec4)); + programStateShadow->setUniform(_effectColorLocation, &_shadowColor, sizeof(Vec4)); batch.shadowCommand.init(_globalZOrder); renderer->addCommand(&batch.shadowCommand); } - Vec4 effectColor(_effectColorF.r, _effectColorF.g, _effectColorF.b, _effectColorF.a); - batch.textCommand.getPipelineDescriptor().programState->setUniform(_effectColorLocation, &effectColor, + batch.textCommand.getPipelineDescriptor().programState->setUniform(_effectColorLocation, &_effectColor, sizeof(Vec4)); } break; @@ -1978,10 +1955,10 @@ void Label::updateEffectUniforms(BatchCommand& batch, { Color3B oldColor = _realColor; uint8_t oldOPacity = _displayedOpacity; - _displayedOpacity = _shadowColor4F.a * (oldOPacity / 255.0f) * 255; - setColor(Color3B(_shadowColor4F)); + _displayedOpacity = _shadowColor.a * (oldOPacity / 255.0f) * 255; + setColor(Color3B(_shadowColor)); batch.shadowCommand.updateVertexBuffer( - textureAtlas->getQuads(), (unsigned int)(textureAtlas->getTotalQuads() * sizeof(V3F_C4B_T2F_Quad))); + textureAtlas->getQuads(), (unsigned int)(textureAtlas->getTotalQuads() * sizeof(V3F_T2F_C4B_Quad))); batch.shadowCommand.init(_globalZOrder); renderer->addCommand(&batch.shadowCommand); @@ -2446,7 +2423,7 @@ void Label::updateDisplayedOpacity(uint8_t parentOpacity) // FIXME: it is not clear what is the difference between setTextColor() and setColor() // if setTextColor() only changes the text and nothing but the text (no glow, no outline, not underline) // that's fine but it should be documented -void Label::setTextColor(const Color4B& color) +void Label::setTextColor(const Color32& color) { if (_textColor != color) { @@ -2477,30 +2454,28 @@ void Label::updateColor() return; } - Color4B color4(_displayedColor.r, _displayedColor.g, _displayedColor.b, _displayedOpacity); + Color color(_displayedColor, _displayedOpacity / 255.0f); // special opacity for premultiplied textures if (_isOpacityModifyRGB) { - color4.r *= _displayedOpacity / 255.0f; - color4.g *= _displayedOpacity / 255.0f; - color4.b *= _displayedOpacity / 255.0f; + color.r *= color.a; + color.g *= color.a; + color.b *= color.a; } - ax::TextureAtlas* textureAtlas; - V3F_C4B_T2F_Quad* quads; for (auto&& batchNode : _batchNodes) { - textureAtlas = batchNode->getTextureAtlas(); - quads = textureAtlas->getQuads(); - auto count = textureAtlas->getTotalQuads(); + auto textureAtlas = batchNode->getTextureAtlas(); + auto quads = textureAtlas->getQuads(); + auto count = textureAtlas->getTotalQuads(); for (int index = 0; index < count; ++index) { - quads[index].bl.colors = color4; - quads[index].br.colors = color4; - quads[index].tl.colors = color4; - quads[index].tr.colors = color4; + quads[index].bl.color = color; + quads[index].br.color = color; + quads[index].tl.color = color; + quads[index].tr.color = color; textureAtlas->updateQuad(quads[index], index); } } @@ -2591,10 +2566,10 @@ FontDefinition Label::_getFontDefinition() const { systemFontDef._stroke._strokeEnabled = true; systemFontDef._stroke._strokeSize = _outlineSize; - systemFontDef._stroke._strokeColor.r = _effectColorF.r * 255; - systemFontDef._stroke._strokeColor.g = _effectColorF.g * 255; - systemFontDef._stroke._strokeColor.b = _effectColorF.b * 255; - systemFontDef._stroke._strokeAlpha = _effectColorF.a * 255; + systemFontDef._stroke._strokeColor.r = _effectColor.r * 255.f; + systemFontDef._stroke._strokeColor.g = _effectColor.g * 255.f; + systemFontDef._stroke._strokeColor.b = _effectColor.b * 255.f; + systemFontDef._stroke._strokeAlpha = _effectColor.a * 255.f; } else { @@ -2915,7 +2890,7 @@ bool Label::multilineTextWrap(const std::function(character)); + static_cast(character)); continue; } @@ -3131,11 +3106,11 @@ void Label::recordLetterInfo(const ax::Vec2& point, char32_t utf32Char, int lett LetterInfo tmpInfo; _lettersInfo.emplace_back(tmpInfo); } - _lettersInfo[letterIndex].lineIndex = lineIndex; - _lettersInfo[letterIndex].utf32Char = utf32Char; - _lettersInfo[letterIndex].valid = _fontAtlas->_letterDefinitions[utf32Char].validDefinition && utf32Char != ' '; - _lettersInfo[letterIndex].positionX = point.x; - _lettersInfo[letterIndex].positionY = point.y; + _lettersInfo[letterIndex].lineIndex = lineIndex; + _lettersInfo[letterIndex].utf32Char = utf32Char; + _lettersInfo[letterIndex].valid = _fontAtlas->_letterDefinitions[utf32Char].validDefinition && utf32Char != ' '; + _lettersInfo[letterIndex].positionX = point.x; + _lettersInfo[letterIndex].positionY = point.y; _lettersInfo[letterIndex].atlasIndex = -1; } @@ -3150,4 +3125,4 @@ void Label::recordPlaceholderInfo(int letterIndex, char32_t utf32Char) _lettersInfo[letterIndex].valid = false; } -} +} // namespace ax diff --git a/core/2d/Label.h b/core/2d/Label.h index dfc08491e811..7ed822945b34 100644 --- a/core/2d/Label.h +++ b/core/2d/Label.h @@ -261,25 +261,6 @@ class AX_DLL Label : public Node, public LabelProtocol, public BlendProtocol int maxLineWidth, std::string_view subTextureKey); - /** - * Allocates and initializes a Label, with a bitmap font file. - * - * @param bmfontPath A bitmap font file, it's a FNT format. - * @param text The initial text. - * @param hAlignment Text horizontal alignment. - * @param maxLineWidth The max line width. - * @param imageOffset Offset into larger texture - * - * @return An automatically released Label object. - * @see setBMFontFilePath setMaxLineWidth - */ -#ifndef AX_CORE_PROFILE - AX_DEPRECATED(2.1) static Label* createWithBMFont(std::string_view bmfontPath, - std::string_view text, - const TextHAlignment& hAlignment, - int maxLineWidth, - const Vec2& imageOffset); -#endif /** * Allocates and initializes a Label, with char map configuration. * @@ -342,12 +323,7 @@ class AX_DLL Label : public Node, public LabelProtocol, public BlendProtocol /** Sets a new bitmap font to Label */ virtual bool setBMFontFilePath(std::string_view bmfontFilePath, std::string_view subTextureKey, float fontSize = 0); -#ifndef AX_CORE_PROFILE - /** Sets a new bitmap font to Label */ - AX_DEPRECATED(2.1) virtual bool setBMFontFilePath(std::string_view bmfontFilePath, - const Vec2& imageOffset, - float fontSize = 0); -#endif + /** Returns the bitmap font used by the Label.*/ std::string_view getBMFontFilePath() const { return _bmFontPath; } @@ -420,17 +396,17 @@ class AX_DLL Label : public Node, public LabelProtocol, public BlendProtocol * * @warning Limiting use to only when the Label created with true type font or system font. */ - virtual void setTextColor(const Color4B& color); + virtual void setTextColor(const Color32& color); /** Returns the text color of the Label.*/ - const Color4B& getTextColor() const { return _textColor; } + const Color32& getTextColor() const { return _textColor; } /** * Enable shadow effect to Label. * * @todo Support blur for shadow effect. */ - virtual void enableShadow(const Color4B& shadowColor = Color4B::BLACK, + virtual void enableShadow(const Color32& shadowColor = Color32::BLACK, const Vec2& offset = Vec2(2, -2), int blurRadius = 0); @@ -438,13 +414,13 @@ class AX_DLL Label : public Node, public LabelProtocol, public BlendProtocol * Enable outline effect to Label. * @warning Limiting use to only when the Label created with true type font or system font. */ - virtual void enableOutline(const Color4B& outlineColor, int outlineSize = -1); + virtual void enableOutline(const Color32& outlineColor, int outlineSize = -1); /** * Enable glow effect to Label. * @warning Limiting use to only when the Label created with true type font. */ - virtual void enableGlow(const Color4B& glowColor); + virtual void enableGlow(const Color32& glowColor); /** * Enable italics rendering @@ -498,7 +474,7 @@ class AX_DLL Label : public Node, public LabelProtocol, public BlendProtocol /** * Return the shadow effect color value. */ - Color4F getShadowColor() const { return _shadowColor4F; } + Color getShadowColor() const { return _shadowColor; } /** * Return the outline effect size value. @@ -513,7 +489,7 @@ class AX_DLL Label : public Node, public LabelProtocol, public BlendProtocol /** * Return current effect color value. */ - Color4F getEffectColor() const { return _effectColorF; } + Color getEffectColor() const { return _effectColor; } /** Sets the Label's text horizontal alignment.*/ void setAlignment(TextHAlignment hAlignment) { setAlignment(hAlignment, _vAlignment); } @@ -795,10 +771,7 @@ class AX_DLL Label : public Node, public LabelProtocol, public BlendProtocol virtual void updateShaderProgram(); virtual void updateFontScale(); -#ifndef AX_CORE_PROFILE - /* DEPRECATED: use updateFontScale instead */ - AX_DEPRECATED(2.1) virtual void updateBMFontScale() { updateFontScale(); } -#endif + void scaleFontSize(float fontSize); bool setTTFConfigInternal(const TTFConfig& ttfConfig); bool updateTTFConfigInternal(); @@ -883,7 +856,7 @@ class AX_DLL Label : public Node, public LabelProtocol, public BlendProtocol Overflow _overflow; float _originalFontSize; - Color4B _textColor; + Color32 _textColor; BlendFunc _blendFunc; @@ -902,9 +875,9 @@ class AX_DLL Label : public Node, public LabelProtocol, public BlendProtocol Rect _bmRect; Rect _reusedRect; - Color4F _effectColorF; - Color4F _textColorF; - Color4F _shadowColor4F; + Color _effectColor; + Color _textColorF; + Color _shadowColor; Mat4 _shadowTransform; std::u32string _utf32Text; diff --git a/core/2d/LabelAtlas.cpp b/core/2d/LabelAtlas.cpp index a045c23947f0..a552033e13c4 100644 --- a/core/2d/LabelAtlas.cpp +++ b/core/2d/LabelAtlas.cpp @@ -161,7 +161,7 @@ void LabelAtlas::updateAtlasValues() } AXASSERT(n <= _textureAtlas->getCapacity(), "updateAtlasValues: Invalid String length"); - V3F_C4B_T2F_Quad* quads = _textureAtlas->getQuads(); + auto quads = _textureAtlas->getQuads(); for (ssize_t i = 0; i < n; i++) { @@ -182,32 +182,32 @@ void LabelAtlas::updateAtlasValues() float bottom = top + itemHeightInPixels / textureHigh; #endif // ! AX_FIX_ARTIFACTS_BY_STRECHING_TEXEL - quads[i].tl.texCoords.u = left; - quads[i].tl.texCoords.v = top; - quads[i].tr.texCoords.u = right; - quads[i].tr.texCoords.v = top; - quads[i].bl.texCoords.u = left; - quads[i].bl.texCoords.v = bottom; - quads[i].br.texCoords.u = right; - quads[i].br.texCoords.v = bottom; - - quads[i].bl.vertices.x = (float)(i * _itemWidth); - quads[i].bl.vertices.y = 0; - quads[i].bl.vertices.z = 0.0f; - quads[i].br.vertices.x = (float)(i * _itemWidth + _itemWidth); - quads[i].br.vertices.y = 0; - quads[i].br.vertices.z = 0.0f; - quads[i].tl.vertices.x = (float)(i * _itemWidth); - quads[i].tl.vertices.y = (float)(_itemHeight); - quads[i].tl.vertices.z = 0.0f; - quads[i].tr.vertices.x = (float)(i * _itemWidth + _itemWidth); - quads[i].tr.vertices.y = (float)(_itemHeight); - quads[i].tr.vertices.z = 0.0f; - Color4B c(_displayedColor.r, _displayedColor.g, _displayedColor.b, _displayedOpacity); - quads[i].tl.colors = c; - quads[i].tr.colors = c; - quads[i].bl.colors = c; - quads[i].br.colors = c; + quads[i].tl.texCoord.u = left; + quads[i].tl.texCoord.v = top; + quads[i].tr.texCoord.u = right; + quads[i].tr.texCoord.v = top; + quads[i].bl.texCoord.u = left; + quads[i].bl.texCoord.v = bottom; + quads[i].br.texCoord.u = right; + quads[i].br.texCoord.v = bottom; + + quads[i].bl.position.x = (float)(i * _itemWidth); + quads[i].bl.position.y = 0; + quads[i].bl.position.z = 0.0f; + quads[i].br.position.x = (float)(i * _itemWidth + _itemWidth); + quads[i].br.position.y = 0; + quads[i].br.position.z = 0.0f; + quads[i].tl.position.x = (float)(i * _itemWidth); + quads[i].tl.position.y = (float)(_itemHeight); + quads[i].tl.position.z = 0.0f; + quads[i].tr.position.x = (float)(i * _itemWidth + _itemWidth); + quads[i].tr.position.y = (float)(_itemHeight); + quads[i].tr.position.z = 0.0f; + Color32 c(_displayedColor, _displayedOpacity); + quads[i].tl.color = c; + quads[i].tr.color = c; + quads[i].bl.color = c; + quads[i].br.color = c; } if (n > 0) { @@ -248,21 +248,21 @@ void LabelAtlas::updateColor() { if (_textureAtlas) { - Color4B color4(_displayedColor.r, _displayedColor.g, _displayedColor.b, _displayedOpacity); + Color color(_displayedColor, _displayedOpacity / 255.0f); if (_isOpacityModifyRGB) { - color4.r *= _displayedOpacity / 255.0f; - color4.g *= _displayedOpacity / 255.0f; - color4.b *= _displayedOpacity / 255.0f; + color.r *= color.a; + color.g *= color.a; + color.b *= color.a; } auto quads = _textureAtlas->getQuads(); ssize_t length = _string.length(); for (int index = 0; index < length; index++) { - quads[index].bl.colors = color4; - quads[index].br.colors = color4; - quads[index].tl.colors = color4; - quads[index].tr.colors = color4; + quads[index].bl.color = color; + quads[index].br.color = color; + quads[index].tl.color = color; + quads[index].tr.color = color; _textureAtlas->updateQuad(quads[index], index); } } @@ -277,7 +277,7 @@ void LabelAtlas::draw(Renderer* renderer, const Mat4& transform, uint32_t flags) _debugDrawNode->clear(); auto size = getContentSize(); Vec2 vertices[4] = {Vec2::ZERO, Vec2(size.width, 0), Vec2(size.width, size.height), Vec2(0.0f, size.height)}; - _debugDrawNode->drawPoly(vertices, 4, true, Color4B::WHITE); + _debugDrawNode->drawPoly(vertices, 4, true, Color32::WHITE); } #endif diff --git a/core/2d/Layer.cpp b/core/2d/Layer.cpp index 87da0ec1607f..05d7212d57d5 100644 --- a/core/2d/Layer.cpp +++ b/core/2d/Layer.cpp @@ -74,7 +74,7 @@ LayerColor* LayerColor::create() return ret; } -LayerColor* LayerColor::create(const Color4B& color, float width, float height) +LayerColor* LayerColor::create(const Color32& color, float width, float height) { LayerColor* layer = new LayerColor(); if (layer->initWithColor(color, width, height)) @@ -86,7 +86,7 @@ LayerColor* LayerColor::create(const Color4B& color, float width, float height) return nullptr; } -LayerColor* LayerColor::create(const Color4B& color) +LayerColor* LayerColor::create(const Color32& color) { LayerColor* layer = new LayerColor(); if (layer->initWithColor(color)) @@ -101,10 +101,10 @@ LayerColor* LayerColor::create(const Color4B& color) bool LayerColor::init() { Size s = _director->getWinSize(); - return initWithColor(Color4B(0, 0, 0, 0), s.width, s.height); + return initWithColor(Color32(0, 0, 0, 0), s.width, s.height); } -bool LayerColor::initWithColor(const Color4B& color, float w, float h) +bool LayerColor::initWithColor(const Color32& color, float w, float h) { if (Sprite::init()) { @@ -123,7 +123,7 @@ bool LayerColor::initWithColor(const Color4B& color, float w, float h) return false; } -bool LayerColor::initWithColor(const Color4B& color) +bool LayerColor::initWithColor(const Color32& color) { Size s = _director->getWinSize(); return initWithColor(color, s.width, s.height); @@ -151,7 +151,7 @@ LayerGradient::LayerGradient() {} LayerGradient::~LayerGradient() {} -LayerGradient* LayerGradient::create(const Color4B& start, const Color4B& end) +LayerGradient* LayerGradient::create(const Color32& start, const Color32& end) { LayerGradient* layer = new LayerGradient(); if (layer->initWithColor(start, end)) @@ -163,7 +163,7 @@ LayerGradient* LayerGradient::create(const Color4B& start, const Color4B& end) return nullptr; } -LayerGradient* LayerGradient::create(const Color4B& start, const Color4B& end, const Vec2& v) +LayerGradient* LayerGradient::create(const Color32& start, const Color32& end, const Vec2& v) { LayerGradient* layer = new LayerGradient(); if (layer->initWithColor(start, end, v)) @@ -191,15 +191,15 @@ LayerGradient* LayerGradient::create() bool LayerGradient::init() { - return initWithColor(Color4B(0, 0, 0, 255), Color4B(0, 0, 0, 255)); + return initWithColor(Color32(0, 0, 0, 255), Color32(0, 0, 0, 255)); } -bool LayerGradient::initWithColor(const Color4B& start, const Color4B& end) +bool LayerGradient::initWithColor(const Color32& start, const Color32& end) { return initWithColor(start, end, Vec2(0, -1)); } -bool LayerGradient::initWithColor(const Color4B& start, const Color4B& end, const Vec2& v) +bool LayerGradient::initWithColor(const Color32& start, const Color32& end, const Vec2& v) { _endColor.r = end.r; _endColor.g = end.g; @@ -211,7 +211,7 @@ bool LayerGradient::initWithColor(const Color4B& start, const Color4B& end, cons _compressedInterpolation = true; - return LayerColor::initWithColor(Color4B(start.r, start.g, start.b, 255)); + return LayerColor::initWithColor(Color32(start.r, start.g, start.b, 255)); } void LayerGradient::updateColor() @@ -232,31 +232,31 @@ void LayerGradient::updateColor() float opacityf = (float)_displayedOpacity / 255.0f; - Color4F S(_displayedColor.r / 255.0f, _displayedColor.g / 255.0f, _displayedColor.b / 255.0f, + Color S(_displayedColor, _startOpacity * opacityf / 255.0f); - Color4F E(_endColor.r / 255.0f, _endColor.g / 255.0f, _endColor.b / 255.0f, _endOpacity * opacityf / 255.0f); + Color E(_endColor, _endOpacity * opacityf / 255.0f); // (-1, -1) - _quad.bl.colors.r = (E.r + (S.r - E.r) * ((c + u.x + u.y) / (2.0f * c))) * 255; - _quad.bl.colors.g = (E.g + (S.g - E.g) * ((c + u.x + u.y) / (2.0f * c))) * 255; - _quad.bl.colors.b = (E.b + (S.b - E.b) * ((c + u.x + u.y) / (2.0f * c))) * 255; - _quad.bl.colors.a = (E.a + (S.a - E.a) * ((c + u.x + u.y) / (2.0f * c))) * 255; + _quad.bl.color.r = (E.r + (S.r - E.r) * ((c + u.x + u.y) / (2.0f * c))); + _quad.bl.color.g = (E.g + (S.g - E.g) * ((c + u.x + u.y) / (2.0f * c))); + _quad.bl.color.b = (E.b + (S.b - E.b) * ((c + u.x + u.y) / (2.0f * c))); + _quad.bl.color.a = (E.a + (S.a - E.a) * ((c + u.x + u.y) / (2.0f * c))); // (1, -1) - _quad.br.colors.r = (E.r + (S.r - E.r) * ((c - u.x + u.y) / (2.0f * c))) * 255; - _quad.br.colors.g = (E.g + (S.g - E.g) * ((c - u.x + u.y) / (2.0f * c))) * 255; - _quad.br.colors.b = (E.b + (S.b - E.b) * ((c - u.x + u.y) / (2.0f * c))) * 255; - _quad.br.colors.a = (E.a + (S.a - E.a) * ((c - u.x + u.y) / (2.0f * c))) * 255; + _quad.br.color.r = (E.r + (S.r - E.r) * ((c - u.x + u.y) / (2.0f * c))); + _quad.br.color.g = (E.g + (S.g - E.g) * ((c - u.x + u.y) / (2.0f * c))); + _quad.br.color.b = (E.b + (S.b - E.b) * ((c - u.x + u.y) / (2.0f * c))); + _quad.br.color.a = (E.a + (S.a - E.a) * ((c - u.x + u.y) / (2.0f * c))); // (-1, 1) - _quad.tl.colors.r = (E.r + (S.r - E.r) * ((c + u.x - u.y) / (2.0f * c))) * 255; - _quad.tl.colors.g = (E.g + (S.g - E.g) * ((c + u.x - u.y) / (2.0f * c))) * 255; - _quad.tl.colors.b = (E.b + (S.b - E.b) * ((c + u.x - u.y) / (2.0f * c))) * 255; - _quad.tl.colors.a = (E.a + (S.a - E.a) * ((c + u.x - u.y) / (2.0f * c))) * 255; + _quad.tl.color.r = (E.r + (S.r - E.r) * ((c + u.x - u.y) / (2.0f * c))); + _quad.tl.color.g = (E.g + (S.g - E.g) * ((c + u.x - u.y) / (2.0f * c))); + _quad.tl.color.b = (E.b + (S.b - E.b) * ((c + u.x - u.y) / (2.0f * c))); + _quad.tl.color.a = (E.a + (S.a - E.a) * ((c + u.x - u.y) / (2.0f * c))); // (1, 1) - _quad.tr.colors.r = (E.r + (S.r - E.r) * ((c - u.x - u.y) / (2.0f * c))) * 255; - _quad.tr.colors.g = (E.g + (S.g - E.g) * ((c - u.x - u.y) / (2.0f * c))) * 255; - _quad.tr.colors.b = (E.b + (S.b - E.b) * ((c - u.x - u.y) / (2.0f * c))) * 255; - _quad.tr.colors.a = (E.a + (S.a - E.a) * ((c - u.x - u.y) / (2.0f * c))) * 255; + _quad.tr.color.r = (E.r + (S.r - E.r) * ((c - u.x - u.y) / (2.0f * c))); + _quad.tr.color.g = (E.g + (S.g - E.g) * ((c - u.x - u.y) / (2.0f * c))); + _quad.tr.color.b = (E.b + (S.b - E.b) * ((c - u.x - u.y) / (2.0f * c))); + _quad.tr.color.a = (E.a + (S.a - E.a) * ((c - u.x - u.y) / (2.0f * c))); // renders using batch node if (_renderMode == RenderMode::QUAD_BATCHNODE) @@ -343,8 +343,8 @@ std::string LayerGradient::getDescription() const /** * LayerRadialGradient */ -LayerRadialGradient* LayerRadialGradient::create(const Color4B& startColor, - const Color4B& endColor, +LayerRadialGradient* LayerRadialGradient::create(const Color32& startColor, + const Color32& endColor, float radius, const Vec2& center, float expand) @@ -363,7 +363,7 @@ LayerRadialGradient* LayerRadialGradient::create(const Color4B& startColor, LayerRadialGradient* LayerRadialGradient::create() { auto layerGradient = new LayerRadialGradient(); - if (layerGradient && layerGradient->initWithColor(Color4B::BLACK, Color4B::BLACK, 0, Vec2(0, 0), 0)) + if (layerGradient && layerGradient->initWithColor(Color32::BLACK, Color32::BLACK, 0, Vec2(0, 0), 0)) { layerGradient->autorelease(); return layerGradient; @@ -397,8 +397,8 @@ LayerRadialGradient::~LayerRadialGradient() AX_SAFE_RELEASE_NULL(_customCommand.getPipelineDescriptor().programState); } -bool LayerRadialGradient::initWithColor(const ax::Color4B& startColor, - const ax::Color4B& endColor, +bool LayerRadialGradient::initWithColor(const ax::Color32& startColor, + const ax::Color32& endColor, float radius, const Vec2& center, float expand) @@ -409,10 +409,10 @@ bool LayerRadialGradient::initWithColor(const ax::Color4B& startColor, if (Node::initLayer()) { - convertColor4B24F(_startColorRend, startColor); + _startColorRend = static_cast(startColor); _startColor = startColor; - convertColor4B24F(_endColorRend, endColor); + _endColorRend = static_cast(endColor); _endColor = endColor; _expand = expand; @@ -508,16 +508,16 @@ float LayerRadialGradient::getExpand() const void LayerRadialGradient::setStartColor(const Color3B& color) { - setStartColor(Color4B(color)); + setStartColor(Color32(color)); } -void LayerRadialGradient::setStartColor(const ax::Color4B& color) +void LayerRadialGradient::setStartColor(const ax::Color32& color) { _startColor = color; - convertColor4B24F(_startColorRend, _startColor); + _startColorRend = static_cast(color); } -Color4B LayerRadialGradient::getStartColor() const +Color32 LayerRadialGradient::getStartColor() const { return _startColor; } @@ -529,16 +529,16 @@ Color3B LayerRadialGradient::getStartColor3B() const void LayerRadialGradient::setEndColor(const Color3B& color) { - setEndColor(Color4B(color)); + setEndColor(Color32(color)); } -void LayerRadialGradient::setEndColor(const ax::Color4B& color) +void LayerRadialGradient::setEndColor(const ax::Color32& color) { _endColor = color; - convertColor4B24F(_endColorRend, _endColor); + _endColorRend = static_cast(color); } -Color4B LayerRadialGradient::getEndColor() const +Color32 LayerRadialGradient::getEndColor() const { return _endColor; } @@ -558,14 +558,6 @@ const BlendFunc& LayerRadialGradient::getBlendFunc() const return _blendFunc; } -void LayerRadialGradient::convertColor4B24F(Color4F& outColor, const Color4B& inColor) -{ - outColor.r = inColor.r / 255.0f; - outColor.g = inColor.g / 255.0f; - outColor.b = inColor.b / 255.0f; - outColor.a = inColor.a / 255.0f; -} - /// MultiplexLayer LayerMultiplex::LayerMultiplex() : _enabledLayer(0) {} diff --git a/core/2d/Layer.h b/core/2d/Layer.h index 735d0288eac5..40cca722a4e2 100644 --- a/core/2d/Layer.h +++ b/core/2d/Layer.h @@ -73,13 +73,13 @@ class AX_DLL LayerColor : public Sprite * @param height The height of layer. * @return An autoreleased LayerColor object. */ - static LayerColor * create(const Color4B& color, float width, float height); + static LayerColor * create(const Color32& color, float width, float height); /** Creates a Layer with color. Width and height are the window size. * * @param color The color of layer. * @return An autoreleased LayerColor object. */ - static LayerColor * create(const Color4B& color); + static LayerColor * create(const Color32& color); /** Change width in Points. * @@ -100,8 +100,8 @@ class AX_DLL LayerColor : public Sprite void changeWidthAndHeight(float w, float h); LayerColor(); bool init() override; - bool initWithColor(const Color4B& color, float width, float height); - bool initWithColor(const Color4B& color); + bool initWithColor(const Color32& color, float width, float height); + bool initWithColor(const Color32& color); private: AX_DISALLOW_COPY_AND_ASSIGN(LayerColor); @@ -146,7 +146,7 @@ class AX_DLL LayerGradient : public LayerColor * @param end The end color. * @return An autoreleased LayerGradient object. */ - static LayerGradient* create(const Color4B& start, const Color4B& end); + static LayerGradient* create(const Color32& start, const Color32& end); /** Creates a full-screen Layer with a gradient between start and end in the direction of v. * @@ -155,7 +155,7 @@ class AX_DLL LayerGradient : public LayerColor * @param v The direction of gradient color. * @return An autoreleased LayerGradient object. */ - static LayerGradient* create(const Color4B& start, const Color4B& end, const Vec2& v); + static LayerGradient* create(const Color32& start, const Color32& end, const Vec2& v); /** Whether or not the interpolation will be compressed in order to display all the colors of the gradient both in canonical and non canonical vectors. Default: true. @@ -235,13 +235,13 @@ class AX_DLL LayerGradient : public LayerColor * @js init * @lua init */ - bool initWithColor(const Color4B& start, const Color4B& end); + bool initWithColor(const Color32& start, const Color32& end); /** Initializes the Layer with a gradient between start and end in the direction of v. * @js init * @lua init */ - bool initWithColor(const Color4B& start, const Color4B& end, const Vec2& v); + bool initWithColor(const Color32& start, const Color32& end, const Vec2& v); protected: virtual void updateColor() override; @@ -269,8 +269,8 @@ class AX_DLL LayerRadialGradient : public Node, BlendProtocol * @param expand an alpha value(0.f-1.f) that specifies how much of that radius in only inner color(the gradient starts outside of that amount) */ - static LayerRadialGradient* create(const Color4B& startColor, - const Color4B& endColor, + static LayerRadialGradient* create(const Color32& startColor, + const Color32& endColor, float radius, const Vec2& center, float expand); @@ -298,13 +298,13 @@ class AX_DLL LayerRadialGradient : public Node, BlendProtocol float getExpand() const; void setStartColor(const Color3B& color); - void setStartColor(const Color4B& color); - Color4B getStartColor() const; + void setStartColor(const Color32& color); + Color32 getStartColor() const; Color3B getStartColor3B() const; void setEndColor(const Color3B& color); - void setEndColor(const Color4B& color); - Color4B getEndColor() const; + void setEndColor(const Color32& color); + Color32 getEndColor() const; Color3B getEndColor3B() const; void setBlendFunc(const BlendFunc& blendFunc) override; @@ -313,20 +313,18 @@ class AX_DLL LayerRadialGradient : public Node, BlendProtocol LayerRadialGradient(); virtual ~LayerRadialGradient(); - bool initWithColor(const Color4B& startColor, - const Color4B& endColor, + bool initWithColor(const Color32& startColor, + const Color32& endColor, float radius, const Vec2& center, float expand); private: - void convertColor4B24F(Color4F& outColor, const Color4B& inColor); + Color32 _startColor = Color32::BLACK; + Color _startColorRend = Color::BLACK; // start color used in shader - Color4B _startColor = Color4B::BLACK; - Color4F _startColorRend = Color4F::BLACK; // start color used in shader - - Color4B _endColor = Color4B::BLACK; - Color4F _endColorRend = Color4F::BLACK; // end color used in shader + Color32 _endColor = Color32::BLACK; + Color _endColorRend = Color::BLACK; // end color used in shader Vec2 _vertices[4]; Vec2 _center; diff --git a/core/2d/Node.cpp b/core/2d/Node.cpp index 674bcfa09e72..3ec2a65eecaf 100644 --- a/core/2d/Node.cpp +++ b/core/2d/Node.cpp @@ -125,7 +125,7 @@ Node::Node() , _onExitCallback(nullptr) , _onEnterTransitionDidFinishCallback(nullptr) , _onExitTransitionDidStartCallback(nullptr) -#if defined(AX_ENABLE_PHYSICS) +#if defined(AX_ENABLE_PHYSICS) && 0 , _physicsBody(nullptr) #endif { diff --git a/core/2d/Node.h b/core/2d/Node.h index 008251a0cd9b..a95b50e464d2 100644 --- a/core/2d/Node.h +++ b/core/2d/Node.h @@ -41,7 +41,7 @@ #include "2d/ComponentContainer.h" #include "2d/Component.h" -#if defined(AX_ENABLE_PHYSICS) +#if defined(AX_ENABLE_PHYSICS) && 0 # include "physics/PhysicsBody.h" #endif @@ -2026,7 +2026,7 @@ class AX_DLL Node : public Object backend::ProgramState* _programState = nullptr; // Physics:remaining backwardly compatible -#if defined(AX_ENABLE_PHYSICS) +#if defined(AX_ENABLE_PHYSICS) && 0 PhysicsBody* _physicsBody; public: diff --git a/core/2d/ParticleBatchNode.cpp b/core/2d/ParticleBatchNode.cpp index f5afe8eaa0bc..47918776cc9d 100644 --- a/core/2d/ParticleBatchNode.cpp +++ b/core/2d/ParticleBatchNode.cpp @@ -474,9 +474,9 @@ void ParticleBatchNode::increaseAtlasCapacityTo(ssize_t quantity) // sets a 0'd quad into the quads array void ParticleBatchNode::disableParticle(int particleIndex) { - V3F_C4B_T2F_Quad* quad = &((_textureAtlas->getQuads())[particleIndex]); - quad->br.vertices.x = quad->br.vertices.y = quad->tr.vertices.x = quad->tr.vertices.y = quad->tl.vertices.x = - quad->tl.vertices.y = quad->bl.vertices.x = quad->bl.vertices.y = 0.0f; + auto quad = &((_textureAtlas->getQuads())[particleIndex]); + quad->br.position.x = quad->br.position.y = quad->tr.position.x = quad->tr.position.y = quad->tl.position.x = + quad->tl.position.y = quad->bl.position.x = quad->bl.position.y = 0.0f; } // ParticleBatchNode - add / remove / reorder helper methods diff --git a/core/2d/ParticleSystem.h b/core/2d/ParticleSystem.h index e10941a7a1e1..6834583c18bd 100644 --- a/core/2d/ParticleSystem.h +++ b/core/2d/ParticleSystem.h @@ -818,45 +818,45 @@ class AX_DLL ParticleSystem : public Node, public TextureProtocol, public Playab * * @return The start color of each particle. */ - const Color4F& getStartColor() const { return _startColor; } + const Color& getStartColor() const { return _startColor; } /** Sets the start color of each particle. * * @param color The start color of each particle. */ - void setStartColor(const Color4F& color) { _startColor = color; } + void setStartColor(const Color& color) { _startColor = color; } /** Gets the start color variance of each particle. * * @return The start color variance of each particle. */ - const Color4F& getStartColorVar() const { return _startColorVar; } + const Color& getStartColorVar() const { return _startColorVar; } /** Sets the start color variance of each particle. * * @param color The start color variance of each particle. */ - void setStartColorVar(const Color4F& color) { _startColorVar = color; } + void setStartColorVar(const Color& color) { _startColorVar = color; } /** Gets the end color and end color variation of each particle. * * @return The end color and end color variation of each particle. */ - const Color4F& getEndColor() const { return _endColor; } + const Color& getEndColor() const { return _endColor; } /** Sets the end color and end color variation of each particle. * * @param color The end color and end color variation of each particle. */ - void setEndColor(const Color4F& color) { _endColor = color; } + void setEndColor(const Color& color) { _endColor = color; } /** Gets the end color variance of each particle. * * @return The end color variance of each particle. */ - const Color4F& getEndColorVar() const { return _endColorVar; } + const Color& getEndColorVar() const { return _endColorVar; } /** Sets the end color variance of each particle. * * @param color The end color variance of each particle. */ - void setEndColorVar(const Color4F& color) { _endColorVar = color; } + void setEndColorVar(const Color& color) { _endColorVar = color; } /** Sets wether to use HSV color system. * WARNING: becareful when using HSV with too many particles because it's expensive. @@ -1588,13 +1588,13 @@ class AX_DLL ParticleSystem : public Node, public TextureProtocol, public Playab /** end size variance in pixels of each particle */ float _endSizeVar; /** start color of each particle */ - Color4F _startColor; + Color _startColor; /** start color variance of each particle */ - Color4F _startColorVar; + Color _startColorVar; /** end color and end color variation of each particle */ - Color4F _endColor; + Color _endColor; /** end color variance of each particle */ - Color4F _endColorVar; + Color _endColorVar; /** hsv color of each particle */ HSV _hsv; /** hsv color variance of each particle */ diff --git a/core/2d/ParticleSystemQuad.cpp b/core/2d/ParticleSystemQuad.cpp index 10f1e0cc7973..6365bf81f956 100644 --- a/core/2d/ParticleSystemQuad.cpp +++ b/core/2d/ParticleSystemQuad.cpp @@ -176,7 +176,7 @@ void ParticleSystemQuad::initTexCoordsWithRect(const Rect& pointRect) // Important. Texture in cocos2d are inverted, so the Y component should be inverted std::swap(top, bottom); - V3F_C4B_T2F_Quad* quads = nullptr; + V3F_T2F_C4B_Quad* quads = nullptr; unsigned int start = 0, end = 0; if (_batchNode) { @@ -194,17 +194,17 @@ void ParticleSystemQuad::initTexCoordsWithRect(const Rect& pointRect) for (unsigned int i = start; i < end; i++) { // bottom-left vertex: - quads[i].bl.texCoords.u = left; - quads[i].bl.texCoords.v = bottom; + quads[i].bl.texCoord.u = left; + quads[i].bl.texCoord.v = bottom; // bottom-right vertex: - quads[i].br.texCoords.u = right; - quads[i].br.texCoords.v = bottom; + quads[i].br.texCoord.u = right; + quads[i].br.texCoord.v = bottom; // top-left vertex: - quads[i].tl.texCoords.u = left; - quads[i].tl.texCoords.v = top; + quads[i].tl.texCoord.u = left; + quads[i].tl.texCoord.v = top; // top-right vertex: - quads[i].tr.texCoords.u = right; - quads[i].tr.texCoords.v = top; + quads[i].tr.texCoord.u = right; + quads[i].tr.texCoord.v = top; } } @@ -260,7 +260,7 @@ void ParticleSystemQuad::initIndices() } } -inline void updatePosWithParticle(V3F_C4B_T2F_Quad* quad, +static void updatePosWithParticle(V3F_T2F_C4B_Quad* quad, const Vec2& newPosition, float size, float scaleInSize, @@ -290,20 +290,20 @@ inline void updatePosWithParticle(V3F_C4B_T2F_Quad* quad, float dy = x1 * sr + y2 * cr + y; // bottom-left - quad->bl.vertices.x = ax; - quad->bl.vertices.y = ay; + quad->bl.position.x = ax; + quad->bl.position.y = ay; // bottom-right vertex: - quad->br.vertices.x = bx; - quad->br.vertices.y = by; + quad->br.position.x = bx; + quad->br.position.y = by; // top-left vertex: - quad->tl.vertices.x = dx; - quad->tl.vertices.y = dy; + quad->tl.position.x = dx; + quad->tl.position.y = dy; // top-right vertex: - quad->tr.vertices.x = cx; - quad->tr.vertices.y = cy; + quad->tr.position.x = cx; + quad->tr.position.y = cy; } void ParticleSystemQuad::updateParticleQuads() @@ -323,11 +323,11 @@ void ParticleSystemQuad::updateParticleQuads() currentPosition = _position; } - V3F_C4B_T2F_Quad* startQuad; + V3F_T2F_C4B_Quad* startQuad; Vec2 pos = Vec2::ZERO; if (_batchNode) { - V3F_C4B_T2F_Quad* batchQuads = _batchNode->getTextureAtlas()->getQuads(); + V3F_T2F_C4B_Quad* batchQuads = _batchNode->getTextureAtlas()->getQuads(); startQuad = &(batchQuads[_atlasIndex]); pos = _position; } @@ -352,7 +352,7 @@ void ParticleSystemQuad::updateParticleQuads() float* sr = _particleData.staticRotation; float* sid = _particleData.scaleInDelta; float* sil = _particleData.scaleInLength; - V3F_C4B_T2F_Quad* quadStart = startQuad; + V3F_T2F_C4B_Quad* quadStart = startQuad; if (_isScaleInAllocated) { for (int i = 0; i < _particleCount; @@ -393,7 +393,7 @@ void ParticleSystemQuad::updateParticleQuads() float* sr = _particleData.staticRotation; float* sid = _particleData.scaleInDelta; float* sil = _particleData.scaleInLength; - V3F_C4B_T2F_Quad* quadStart = startQuad; + V3F_T2F_C4B_Quad* quadStart = startQuad; if (_isScaleInAllocated) { for (int i = 0; i < _particleCount; @@ -430,7 +430,7 @@ void ParticleSystemQuad::updateParticleQuads() float* sr = _particleData.staticRotation; float* sid = _particleData.scaleInDelta; float* sil = _particleData.scaleInLength; - V3F_C4B_T2F_Quad* quadStart = startQuad; + V3F_T2F_C4B_Quad* quadStart = startQuad; if (_isScaleInAllocated) { for (int i = 0; i < _particleCount; @@ -450,7 +450,7 @@ void ParticleSystemQuad::updateParticleQuads() } } - V3F_C4B_T2F_Quad* quad = startQuad; + auto quad = startQuad; float* r = _particleData.colorR; float* g = _particleData.colorG; float* b = _particleData.colorB; @@ -478,15 +478,12 @@ void ParticleSystemQuad::updateParticleQuads() hsv.h += *hue; hsv.s = abs(*sat); hsv.v = abs(*val); - auto colF = hsv.toColor4F(); - quad->bl.colors.set(colF.r * colF.a * 255.0F, colF.g * colF.a * 255.0F, colF.b * colF.a * 255.0F, - colF.a * 255.0F); - quad->br.colors.set(colF.r * colF.a * 255.0F, colF.g * colF.a * 255.0F, colF.b * colF.a * 255.0F, - colF.a * 255.0F); - quad->tl.colors.set(colF.r * colF.a * 255.0F, colF.g * colF.a * 255.0F, colF.b * colF.a * 255.0F, - colF.a * 255.0F); - quad->tr.colors.set(colF.r * colF.a * 255.0F, colF.g * colF.a * 255.0F, colF.b * colF.a * 255.0F, - colF.a * 255.0F); + auto colF = hsv.toRgba(); + Color32 col = colF.premultiplyAlpha(); + quad->bl.color = col; + quad->br.color = col; + quad->tl.color = col; + quad->tr.color = col; } } else @@ -499,11 +496,11 @@ void ParticleSystemQuad::updateParticleQuads() hsv.h += *hue; hsv.s = abs(*sat); hsv.v = abs(*val); - auto col = hsv.toColor4B(); - quad->bl.colors.set(col.r, col.g, col.b, col.a); - quad->br.colors.set(col.r, col.g, col.b, col.a); - quad->tl.colors.set(col.r, col.g, col.b, col.a); - quad->tr.colors.set(col.r, col.g, col.b, col.a); + auto col = hsv.toColor32(); + quad->bl.color = col; + quad->br.color = col; + quad->tl.color = col; + quad->tr.color = col; } } } @@ -514,28 +511,22 @@ void ParticleSystemQuad::updateParticleQuads() { for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a, ++fadeDt, ++fadeLn) { - uint8_t colorR = *r * *a * 255; - uint8_t colorG = *g * *a * 255; - uint8_t colorB = *b * *a * 255; - uint8_t colorA = *a * (*fadeDt / *fadeLn) * 255; - quad->bl.colors.set(colorR, colorG, colorB, colorA); - quad->br.colors.set(colorR, colorG, colorB, colorA); - quad->tl.colors.set(colorR, colorG, colorB, colorA); - quad->tr.colors.set(colorR, colorG, colorB, colorA); + Color32 col = Color{*r * *a, *g * *a, *b * *a, *a * (*fadeDt / *fadeLn)}; + quad->bl.color = col; + quad->br.color = col; + quad->tl.color = col; + quad->tr.color = col; } } else { for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a, ++fadeDt, ++fadeLn) { - uint8_t colorR = *r * 255; - uint8_t colorG = *g * 255; - uint8_t colorB = *b * 255; - uint8_t colorA = *a * (*fadeDt / *fadeLn) * 255; - quad->bl.colors.set(colorR, colorG, colorB, colorA); - quad->br.colors.set(colorR, colorG, colorB, colorA); - quad->tl.colors.set(colorR, colorG, colorB, colorA); - quad->tr.colors.set(colorR, colorG, colorB, colorA); + Color32 col = Color{*r, *g, *b, *a * (*fadeDt / *fadeLn)}; + quad->bl.color = col; + quad->br.color = col; + quad->tl.color = col; + quad->tr.color = col; } } } @@ -558,15 +549,13 @@ void ParticleSystemQuad::updateParticleQuads() hsv.h += *hue; hsv.s = abs(*sat); hsv.v = abs(*val); - auto colF = hsv.toColor4F(); - quad->bl.colors.set(colF.r * colF.a * 255.0F, colF.g * colF.a * 255.0F, colF.b * colF.a * 255.0F, - colF.a * 255.0F); - quad->br.colors.set(colF.r * colF.a * 255.0F, colF.g * colF.a * 255.0F, colF.b * colF.a * 255.0F, - colF.a * 255.0F); - quad->tl.colors.set(colF.r * colF.a * 255.0F, colF.g * colF.a * 255.0F, colF.b * colF.a * 255.0F, - colF.a * 255.0F); - quad->tr.colors.set(colF.r * colF.a * 255.0F, colF.g * colF.a * 255.0F, colF.b * colF.a * 255.0F, - colF.a * 255.0F); + auto colF = hsv.toRgba(); + Color32 col = colF.premultiplyAlpha(); + quad->bl.color = col; + quad->br.color = col; + quad->tl.color = col; + quad->tr.color = col; + } } else @@ -578,11 +567,11 @@ void ParticleSystemQuad::updateParticleQuads() hsv.h += *hue; hsv.s = abs(*sat); hsv.v = abs(*val); - auto col = hsv.toColor4B(); - quad->bl.colors.set(col.r, col.g, col.b, col.a); - quad->br.colors.set(col.r, col.g, col.b, col.a); - quad->tl.colors.set(col.r, col.g, col.b, col.a); - quad->tr.colors.set(col.r, col.g, col.b, col.a); + auto col = hsv.toColor32(); + quad->bl.color = col; + quad->br.color = col; + quad->tl.color = col; + quad->tr.color = col; } } } @@ -593,28 +582,22 @@ void ParticleSystemQuad::updateParticleQuads() { for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a) { - uint8_t colorR = *r * *a * 255; - uint8_t colorG = *g * *a * 255; - uint8_t colorB = *b * *a * 255; - uint8_t colorA = *a * 255; - quad->bl.colors.set(colorR, colorG, colorB, colorA); - quad->br.colors.set(colorR, colorG, colorB, colorA); - quad->tl.colors.set(colorR, colorG, colorB, colorA); - quad->tr.colors.set(colorR, colorG, colorB, colorA); + Color32 col = Color{*r * *a, *g * *a, *b * *a, *a}; + quad->bl.color = col; + quad->br.color = col; + quad->tl.color = col; + quad->tr.color = col; } } else { for (int i = 0; i < _particleCount; ++i, ++quad, ++r, ++g, ++b, ++a) { - uint8_t colorR = *r * 255; - uint8_t colorG = *g * 255; - uint8_t colorB = *b * 255; - uint8_t colorA = *a * 255; - quad->bl.colors.set(colorR, colorG, colorB, colorA); - quad->br.colors.set(colorR, colorG, colorB, colorA); - quad->tl.colors.set(colorR, colorG, colorB, colorA); - quad->tr.colors.set(colorR, colorG, colorB, colorA); + Color32 col = Color{*r, *g, *b, *a}; + quad->bl.color = col; + quad->br.color = col; + quad->tl.color = col; + quad->tr.color = col; } } } @@ -628,7 +611,7 @@ void ParticleSystemQuad::updateParticleQuads() // It was proved to be effective especially for low-end devices. if ((_isLifeAnimated || _isEmitterAnimated || _isLoopAnimated) && _isAnimAllocated) { - V3F_C4B_T2F_Quad* quad = startQuad; + V3F_T2F_C4B_Quad* quad = startQuad; unsigned short* cellIndex = _particleData.animCellIndex; ParticleFrameDescriptor index; @@ -653,17 +636,17 @@ void ParticleSystemQuad::updateParticleQuads() top = index.rect.origin.y / texHeight; bottom = (index.rect.origin.y + index.rect.size.y) / texHeight; - quad->bl.texCoords.u = left; - quad->bl.texCoords.v = bottom; + quad->bl.texCoord.u = left; + quad->bl.texCoord.v = bottom; - quad->br.texCoords.u = right; - quad->br.texCoords.v = bottom; + quad->br.texCoord.u = right; + quad->br.texCoord.v = bottom; - quad->tl.texCoords.u = left; - quad->tl.texCoords.v = top; + quad->tl.texCoord.u = left; + quad->tl.texCoord.v = top; - quad->tr.texCoords.u = right; - quad->tr.texCoords.v = top; + quad->tr.texCoord.u = right; + quad->tr.texCoord.v = top; } } } @@ -700,7 +683,7 @@ void ParticleSystemQuad::setTotalParticles(int tp) AXLOGW("Particle system: not enough memory"); return; } - V3F_C4B_T2F_Quad* quadsNew = (V3F_C4B_T2F_Quad*)realloc(_quads, quadsSize); + auto quadsNew = (V3F_T2F_C4B_Quad*)realloc(_quads, quadsSize); unsigned short* indicesNew = (unsigned short*)realloc(_indices, indicesSize); if (quadsNew && indicesNew) @@ -780,7 +763,7 @@ bool ParticleSystemQuad::allocMemory() AX_SAFE_FREE(_quads); AX_SAFE_FREE(_indices); - _quads = (V3F_C4B_T2F_Quad*)malloc(_totalParticles * sizeof(V3F_C4B_T2F_Quad)); + _quads = (V3F_T2F_C4B_Quad*)malloc(_totalParticles * sizeof(V3F_T2F_C4B_Quad)); _indices = (unsigned short*)malloc(_totalParticles * 6 * sizeof(unsigned short)); if (!_quads || !_indices) @@ -792,7 +775,7 @@ bool ParticleSystemQuad::allocMemory() return false; } - memset(_quads, 0, _totalParticles * sizeof(V3F_C4B_T2F_Quad)); + memset(_quads, 0, _totalParticles * sizeof(V3F_T2F_C4B_Quad)); memset(_indices, 0, _totalParticles * 6 * sizeof(unsigned short)); return true; @@ -818,8 +801,8 @@ void ParticleSystemQuad::setBatchNode(ParticleBatchNode* batchNode) else if (!oldBatch) { // copy current state to batch - V3F_C4B_T2F_Quad* batchQuads = _batchNode->getTextureAtlas()->getQuads(); - V3F_C4B_T2F_Quad* quad = &(batchQuads[_atlasIndex]); + auto batchQuads = _batchNode->getTextureAtlas()->getQuads(); + auto quad = &(batchQuads[_atlasIndex]); memcpy(quad, _quads, _totalParticles * sizeof(_quads[0])); AX_SAFE_FREE(_quads); diff --git a/core/2d/ParticleSystemQuad.h b/core/2d/ParticleSystemQuad.h index a62c24252308..fea6fbf6e632 100644 --- a/core/2d/ParticleSystemQuad.h +++ b/core/2d/ParticleSystemQuad.h @@ -169,7 +169,7 @@ class AX_DLL ParticleSystemQuad : public ParticleSystem bool allocMemory(); - V3F_C4B_T2F_Quad* _quads = nullptr; // quads to be rendered + V3F_T2F_C4B_Quad* _quads = nullptr; // quads to be rendered unsigned short* _indices = nullptr; // indices QuadCommand _quadCommand; // quad command diff --git a/core/2d/ProgressTimer.cpp b/core/2d/ProgressTimer.cpp index a4d9df0810e7..234e5b9d2896 100644 --- a/core/2d/ProgressTimer.cpp +++ b/core/2d/ProgressTimer.cpp @@ -57,17 +57,17 @@ backend::ProgramState* initPipelineDescriptor(ax::CustomCommand& command, AX_SAFE_RELEASE(pipelieDescriptor.programState); pipelieDescriptor.programState = programState; - // set custom vertexLayout according to V2F_C4B_T2F structure + // set custom vertexLayout according to V2F_T2F_C4F structure auto vertexLayout = programState->getMutableVertexLayout(); vertexLayout->setAttrib("a_position", program->getAttributeLocation(backend::Attribute::POSITION), backend::VertexFormat::FLOAT2, 0, false); vertexLayout->setAttrib("a_texCoord", program->getAttributeLocation(backend::Attribute::TEXCOORD), backend::VertexFormat::FLOAT2, - offsetof(V2F_C4B_T2F, texCoords), false); + offsetof(V2F_T2F_C4F, texCoord), false); vertexLayout->setAttrib("a_color", program->getAttributeLocation(backend::Attribute::COLOR), - backend::VertexFormat::UBYTE4, - offsetof(V2F_C4B_T2F, colors), true); - vertexLayout->setStride(sizeof(V2F_C4B_T2F)); + backend::VertexFormat::FLOAT4, + offsetof(V2F_T2F_C4F, color), false); + vertexLayout->setStride(sizeof(V2F_T2F_C4F)); if (ridal) { @@ -202,9 +202,9 @@ Tex2F ProgressTimer::textureCoordFromAlphaPoint(Vec2 alpha) { return ret; } - V3F_C4B_T2F_Quad quad = _sprite->getQuad(); - Vec2 min(quad.bl.texCoords.u, quad.bl.texCoords.v); - Vec2 max(quad.tr.texCoords.u, quad.tr.texCoords.v); + auto& quad = _sprite->getQuad(); + Vec2 min(quad.bl.texCoord.u, quad.bl.texCoord.v); + Vec2 max(quad.tr.texCoord.u, quad.tr.texCoord.v); // Fix bug #1303 so that progress timer handles sprite frame texture rotation if (_sprite->isTextureRectRotated()) { @@ -220,9 +220,9 @@ Vec2 ProgressTimer::vertexFromAlphaPoint(Vec2 alpha) { return ret; } - V3F_C4B_T2F_Quad quad = _sprite->getQuad(); - Vec2 min(quad.bl.vertices.x, quad.bl.vertices.y); - Vec2 max(quad.tr.vertices.x, quad.tr.vertices.y); + auto& quad = _sprite->getQuad(); + Vec2 min(quad.bl.position.x, quad.bl.position.y); + Vec2 max(quad.tr.position.x, quad.tr.position.y); ret.x = min.x * (1.f - alpha.x) + max.x * alpha.x; ret.y = min.y * (1.f - alpha.y) + max.y * alpha.y; return ret; @@ -256,14 +256,14 @@ void ProgressTimer::updateColor() if (!_vertexData.empty()) { - auto sc = _sprite->getQuad().tl.colors; + auto sc = _sprite->getQuad().tl.color; sc.r = sc.r * _sprite->getOpacity() / 255.0f; sc.g = sc.g * _sprite->getOpacity() / 255.0f; sc.b = sc.b * _sprite->getOpacity() / 255.0f; sc.a = sc.a * _sprite->getOpacity() / 255.0f; for (auto& d : _vertexData) { - d.colors = sc; + d.color = ax::Color{sc}; } } } @@ -454,17 +454,17 @@ void ProgressTimer::updateRadial() { // First we populate the array with the _midpoint, then all // vertices/texcoords/colors of the 12 'o clock start and edges and the hitpoint - _vertexData[0].texCoords = textureCoordFromAlphaPoint(_midpoint); - _vertexData[0].vertices = vertexFromAlphaPoint(_midpoint); + _vertexData[0].texCoord = textureCoordFromAlphaPoint(_midpoint); + _vertexData[0].position = vertexFromAlphaPoint(_midpoint); - _vertexData[1].texCoords = textureCoordFromAlphaPoint(topMid); - _vertexData[1].vertices = vertexFromAlphaPoint(topMid); + _vertexData[1].texCoord = textureCoordFromAlphaPoint(topMid); + _vertexData[1].position = vertexFromAlphaPoint(topMid); for (int i = 0; i < index; ++i) { Vec2 alphaPoint = boundaryTexCoord(i); - _vertexData[i + 2].texCoords = textureCoordFromAlphaPoint(alphaPoint); - _vertexData[i + 2].vertices = vertexFromAlphaPoint(alphaPoint); + _vertexData[i + 2].texCoord = textureCoordFromAlphaPoint(alphaPoint); + _vertexData[i + 2].position = vertexFromAlphaPoint(alphaPoint); } for (int i = 0; i < index + 1; i++) @@ -478,8 +478,8 @@ void ProgressTimer::updateRadial() } // hitpoint will go last - _vertexData[_vertexData.size() - 1].texCoords = textureCoordFromAlphaPoint(hit); - _vertexData[_vertexData.size() - 1].vertices = vertexFromAlphaPoint(hit); + _vertexData[_vertexData.size() - 1].texCoord = textureCoordFromAlphaPoint(hit); + _vertexData[_vertexData.size() - 1].position = vertexFromAlphaPoint(hit); updateColor(); _customCommand.updateVertexBuffer(_vertexData.data(), (unsigned int)(sizeof(_vertexData[0]) * _vertexData.size())); @@ -541,20 +541,20 @@ void ProgressTimer::updateBar() } // TOPLEFT - _vertexData[0].texCoords = textureCoordFromAlphaPoint(Vec2(min.x, max.y)); - _vertexData[0].vertices = vertexFromAlphaPoint(Vec2(min.x, max.y)); + _vertexData[0].texCoord = textureCoordFromAlphaPoint(Vec2(min.x, max.y)); + _vertexData[0].position = vertexFromAlphaPoint(Vec2(min.x, max.y)); // BOTLEFT - _vertexData[1].texCoords = textureCoordFromAlphaPoint(Vec2(min.x, min.y)); - _vertexData[1].vertices = vertexFromAlphaPoint(Vec2(min.x, min.y)); + _vertexData[1].texCoord = textureCoordFromAlphaPoint(Vec2(min.x, min.y)); + _vertexData[1].position = vertexFromAlphaPoint(Vec2(min.x, min.y)); // TOPRIGHT - _vertexData[2].texCoords = textureCoordFromAlphaPoint(Vec2(max.x, max.y)); - _vertexData[2].vertices = vertexFromAlphaPoint(Vec2(max.x, max.y)); + _vertexData[2].texCoord = textureCoordFromAlphaPoint(Vec2(max.x, max.y)); + _vertexData[2].position = vertexFromAlphaPoint(Vec2(max.x, max.y)); // BOTRIGHT - _vertexData[3].texCoords = textureCoordFromAlphaPoint(Vec2(max.x, min.y)); - _vertexData[3].vertices = vertexFromAlphaPoint(Vec2(max.x, min.y)); + _vertexData[3].texCoord = textureCoordFromAlphaPoint(Vec2(max.x, min.y)); + _vertexData[3].position = vertexFromAlphaPoint(Vec2(max.x, min.y)); updateColor(); @@ -571,37 +571,37 @@ void ProgressTimer::updateBar() _customCommand2.createVertexBuffer(sizeof(_vertexData[0]), (unsigned int)(_vertexData.size() / 2), CustomCommand::BufferUsage::DYNAMIC); // TOPLEFT 1 - _vertexData[0].texCoords = textureCoordFromAlphaPoint(Vec2(0, 1)); - _vertexData[0].vertices = vertexFromAlphaPoint(Vec2(0, 1)); + _vertexData[0].texCoord = textureCoordFromAlphaPoint(Vec2(0, 1)); + _vertexData[0].position = vertexFromAlphaPoint(Vec2(0, 1)); // BOTLEFT 1 - _vertexData[1].texCoords = textureCoordFromAlphaPoint(Vec2(0, 0)); - _vertexData[1].vertices = vertexFromAlphaPoint(Vec2(0, 0)); + _vertexData[1].texCoord = textureCoordFromAlphaPoint(Vec2(0, 0)); + _vertexData[1].position = vertexFromAlphaPoint(Vec2(0, 0)); // TOPRIGHT 2 - _vertexData[6].texCoords = textureCoordFromAlphaPoint(Vec2(1, 1)); - _vertexData[6].vertices = vertexFromAlphaPoint(Vec2(1, 1)); + _vertexData[6].texCoord = textureCoordFromAlphaPoint(Vec2(1, 1)); + _vertexData[6].position = vertexFromAlphaPoint(Vec2(1, 1)); // BOTRIGHT 2 - _vertexData[7].texCoords = textureCoordFromAlphaPoint(Vec2(1, 0)); - _vertexData[7].vertices = vertexFromAlphaPoint(Vec2(1, 0)); + _vertexData[7].texCoord = textureCoordFromAlphaPoint(Vec2(1, 0)); + _vertexData[7].position = vertexFromAlphaPoint(Vec2(1, 0)); } // TOPRIGHT 1 - _vertexData[2].texCoords = textureCoordFromAlphaPoint(Vec2(min.x, max.y)); - _vertexData[2].vertices = vertexFromAlphaPoint(Vec2(min.x, max.y)); + _vertexData[2].texCoord = textureCoordFromAlphaPoint(Vec2(min.x, max.y)); + _vertexData[2].position = vertexFromAlphaPoint(Vec2(min.x, max.y)); // BOTRIGHT 1 - _vertexData[3].texCoords = textureCoordFromAlphaPoint(Vec2(min.x, min.y)); - _vertexData[3].vertices = vertexFromAlphaPoint(Vec2(min.x, min.y)); + _vertexData[3].texCoord = textureCoordFromAlphaPoint(Vec2(min.x, min.y)); + _vertexData[3].position = vertexFromAlphaPoint(Vec2(min.x, min.y)); // TOPLEFT 2 - _vertexData[4].texCoords = textureCoordFromAlphaPoint(Vec2(max.x, max.y)); - _vertexData[4].vertices = vertexFromAlphaPoint(Vec2(max.x, max.y)); + _vertexData[4].texCoord = textureCoordFromAlphaPoint(Vec2(max.x, max.y)); + _vertexData[4].position = vertexFromAlphaPoint(Vec2(max.x, max.y)); // BOTLEFT 2 - _vertexData[5].texCoords = textureCoordFromAlphaPoint(Vec2(max.x, min.y)); - _vertexData[5].vertices = vertexFromAlphaPoint(Vec2(max.x, min.y)); + _vertexData[5].texCoord = textureCoordFromAlphaPoint(Vec2(max.x, min.y)); + _vertexData[5].position = vertexFromAlphaPoint(Vec2(max.x, min.y)); updateColor(); diff --git a/core/2d/ProgressTimer.h b/core/2d/ProgressTimer.h index 1a40e53a0939..c6b0706085a6 100644 --- a/core/2d/ProgressTimer.h +++ b/core/2d/ProgressTimer.h @@ -31,7 +31,7 @@ THE SOFTWARE. #include "2d/Node.h" #include "renderer/PipelineDescriptor.h" -#include +#include "base/axstd.h" namespace ax { @@ -187,8 +187,8 @@ class AX_DLL ProgressTimer : public Node Vec2 _barChangeRate; float _percentage = 0.0f; Sprite* _sprite = nullptr; - std::vector _vertexData; - std::vector _indexData; + axstd::pod_vector _vertexData; + axstd::pod_vector _indexData; bool _reverseDirection = false; CustomCommand _customCommand; diff --git a/core/2d/RenderTexture.cpp b/core/2d/RenderTexture.cpp index 0d348e736830..2e9c3e7abf64 100644 --- a/core/2d/RenderTexture.cpp +++ b/core/2d/RenderTexture.cpp @@ -339,7 +339,7 @@ void RenderTexture::beginWithClear(float r, int stencilValue, ClearFlag flags) { - setClearColor(Color4F(r, g, b, a)); + setClearColor(Color(r, g, b, a)); setClearDepth(depthValue); setClearStencil(stencilValue); setClearFlags(flags); @@ -725,7 +725,7 @@ void RenderTexture::clearColorAttachment() }; renderer->addCommand(beforeClearAttachmentCommand); - Color4F color(0.f, 0.f, 0.f, 0.f); + Color color(0.f, 0.f, 0.f, 0.f); renderer->clear(ClearFlag::COLOR, color, 1, 0, _globalZOrder); // auto renderer = _director->getRenderer(); diff --git a/core/2d/RenderTexture.h b/core/2d/RenderTexture.h index 1e8756da2f0e..8a6096723773 100644 --- a/core/2d/RenderTexture.h +++ b/core/2d/RenderTexture.h @@ -261,13 +261,13 @@ class AX_DLL RenderTexture : public Node * * @return Color value. */ - inline const Color4F& getClearColor() const { return _clearColor; } + inline const Color& getClearColor() const { return _clearColor; } /** Set color value. * * @param clearColor Color value. */ - inline void setClearColor(const Color4F& clearColor) { _clearColor = clearColor; } + inline void setClearColor(const Color& clearColor) { _clearColor = clearColor; } /** Value for clearDepth. Valid only when "autoDraw" is true. * @@ -425,7 +425,7 @@ class AX_DLL RenderTexture : public Node RefPtr _UITextureImage = nullptr; backend::PixelFormat _pixelFormat = backend::PixelFormat::RGBA8; - Color4F _clearColor; + Color _clearColor; float _clearDepth = 1.f; int _clearStencil = 0; bool _autoDraw = false; diff --git a/core/2d/Scene.cpp b/core/2d/Scene.cpp index 1f80eff0782a..631f171dcb71 100644 --- a/core/2d/Scene.cpp +++ b/core/2d/Scene.cpp @@ -35,11 +35,11 @@ THE SOFTWARE. #include "base/UTF8.h" #include "renderer/Renderer.h" -#if defined(AX_ENABLE_PHYSICS) +#if defined(AX_ENABLE_PHYSICS) && 0 # include "physics/PhysicsWorld.h" #endif -#if defined(AX_ENABLE_3D_PHYSICS) && AX_ENABLE_BULLET_INTEGRATION +#if defined(AX_ENABLE_3D_PHYSICS) # include "physics3d/Physics3DWorld.h" # include "physics3d/Physics3DComponent.h" #endif @@ -66,7 +66,7 @@ Scene::Scene() Scene::~Scene() { -#if defined(AX_ENABLE_3D_PHYSICS) && AX_ENABLE_BULLET_INTEGRATION +#if defined(AX_ENABLE_3D_PHYSICS) AX_SAFE_RELEASE(_physics3DWorld); AX_SAFE_RELEASE(_physics3dDebugCamera); #endif @@ -233,7 +233,7 @@ void Scene::render(Renderer* renderer, const Mat4& eyeTransform, const Mat4* eye // camera->setNodeToParentTransform(eyeCopy); } -#if defined(AX_ENABLE_3D_PHYSICS) && AX_ENABLE_BULLET_INTEGRATION +#if defined(AX_ENABLE_3D_PHYSICS) if (_physics3DWorld && _physics3DWorld->isDebugDrawEnabled()) { Camera* physics3dDebugCamera = _physics3dDebugCamera != nullptr ? _physics3dDebugCamera : defaultCamera; @@ -307,7 +307,7 @@ void Scene::removeAllChildren() } } -#if defined(AX_ENABLE_3D_PHYSICS) && AX_ENABLE_BULLET_INTEGRATION +#if defined(AX_ENABLE_3D_PHYSICS) void Scene::setPhysics3DDebugCamera(Camera* camera) { AX_SAFE_RETAIN(camera); @@ -326,7 +326,7 @@ void Scene::setNavMeshDebugCamera(Camera* camera) #endif -#if (defined(AX_ENABLE_PHYSICS) || (defined(AX_ENABLE_3D_PHYSICS) && AX_ENABLE_BULLET_INTEGRATION)) +#if defined(AX_ENABLE_PHYSICS) || defined(AX_ENABLE_3D_PHYSICS) Scene* Scene::createWithPhysics() { @@ -351,7 +351,7 @@ bool Scene::initWithPhysics() bool Scene::initPhysicsWorld() { -# if defined(AX_ENABLE_PHYSICS) +# if defined(AX_ENABLE_PHYSICS) && 0 _physicsWorld = PhysicsWorld::construct(this); # endif @@ -360,7 +360,7 @@ bool Scene::initPhysicsWorld() { this->setContentSize(_director->getWinSize()); -# if defined(AX_ENABLE_3D_PHYSICS) && AX_ENABLE_BULLET_INTEGRATION +# if defined(AX_ENABLE_3D_PHYSICS) Physics3DWorldDes info; AX_BREAK_IF(!(_physics3DWorld = Physics3DWorld::create(&info))); _physics3DWorld->retain(); @@ -374,15 +374,15 @@ bool Scene::initPhysicsWorld() #endif -#if (defined(AX_ENABLE_PHYSICS) || (defined(AX_ENABLE_3D_PHYSICS) && AX_ENABLE_BULLET_INTEGRATION) || defined(AX_ENABLE_NAVMESH)) +#if defined(AX_ENABLE_PHYSICS) || defined(AX_ENABLE_3D_PHYSICS) || defined(AX_ENABLE_NAVMESH) void Scene::stepPhysicsAndNavigation(float deltaTime) { -# if defined(AX_ENABLE_PHYSICS) +# if defined(AX_ENABLE_PHYSICS) && 0 if (_physicsWorld && _physicsWorld->isAutoStep()) _physicsWorld->update(deltaTime); # endif -# if defined(AX_ENABLE_3D_PHYSICS) && AX_ENABLE_BULLET_INTEGRATION +# if defined(AX_ENABLE_3D_PHYSICS) if (_physics3DWorld) { _physics3DWorld->stepSimulate(deltaTime); diff --git a/core/2d/Scene.h b/core/2d/Scene.h index 573bae39723e..b69e7c84fe56 100644 --- a/core/2d/Scene.h +++ b/core/2d/Scene.h @@ -44,7 +44,7 @@ class EventCustom; #if defined(AX_ENABLE_PHYSICS) class PhysicsWorld; #endif -#if defined(AX_ENABLE_3D_PHYSICS) && AX_ENABLE_BULLET_INTEGRATION +#if defined(AX_ENABLE_3D_PHYSICS) class Physics3DWorld; #endif #if defined(AX_ENABLE_NAVMESH) @@ -157,7 +157,7 @@ class AX_DLL Scene : public Node private: AX_DISALLOW_COPY_AND_ASSIGN(Scene); -#if (AX_ENABLE_PHYSICS || (defined(AX_ENABLE_3D_PHYSICS) && AX_ENABLE_BULLET_INTEGRATION)) +#if AX_ENABLE_PHYSICS || defined(AX_ENABLE_3D_PHYSICS) public: # if defined(AX_ENABLE_PHYSICS) /** Get the physics world of the scene. @@ -167,7 +167,7 @@ class AX_DLL Scene : public Node PhysicsWorld* getPhysicsWorld() const { return _physicsWorld; } # endif -# if defined(AX_ENABLE_3D_PHYSICS) && AX_ENABLE_BULLET_INTEGRATION +# if defined(AX_ENABLE_3D_PHYSICS) /** Get the 3d physics world of the scene. * @return The 3d physics world of the scene. * @js NA @@ -197,7 +197,7 @@ class AX_DLL Scene : public Node PhysicsWorld* _physicsWorld = nullptr; # endif -# if defined(AX_ENABLE_3D_PHYSICS) && AX_ENABLE_BULLET_INTEGRATION +# if defined(AX_ENABLE_3D_PHYSICS) Physics3DWorld* _physics3DWorld = nullptr; Camera* _physics3dDebugCamera = nullptr; # endif @@ -219,7 +219,7 @@ class AX_DLL Scene : public Node Camera* _navMeshDebugCamera = nullptr; #endif -#if (defined(AX_ENABLE_PHYSICS) || (defined(AX_ENABLE_3D_PHYSICS) && AX_ENABLE_BULLET_INTEGRATION) || defined(AX_ENABLE_NAVMESH)) +#if defined(AX_ENABLE_PHYSICS) || defined(AX_ENABLE_3D_PHYSICS) || defined(AX_ENABLE_NAVMESH) public: void stepPhysicsAndNavigation(float deltaTime); #endif diff --git a/core/2d/Sprite.cpp b/core/2d/Sprite.cpp index 62b67e9d78b5..f09eb0c7517b 100644 --- a/core/2d/Sprite.cpp +++ b/core/2d/Sprite.cpp @@ -142,7 +142,7 @@ Sprite* Sprite::createWithSpriteFrameName(std::string_view spriteFrameName) { SpriteFrame* frame = SpriteFrameCache::getInstance()->getSpriteFrameByName(spriteFrameName); -#if _AX_DEBUG > 0 +#if defined(_AX_DEBUG) && _AX_DEBUG > 0 char msg[256] = {0}; snprintf(msg, sizeof(msg), "Invalid spriteFrameName: %s", spriteFrameName.data()); AXASSERT(frame != nullptr, msg); @@ -296,10 +296,10 @@ bool Sprite::initWithTexture(Texture2D* texture, const Rect& rect, bool rotated) memset(&_quad, 0, sizeof(_quad)); // Atlas: Color - _quad.bl.colors = Color4B::WHITE; - _quad.br.colors = Color4B::WHITE; - _quad.tl.colors = Color4B::WHITE; - _quad.tr.colors = Color4B::WHITE; + _quad.bl.color = Color::WHITE; + _quad.br.color = Color::WHITE; + _quad.tl.color = Color::WHITE; + _quad.tr.color = Color::WHITE; // update texture (calls updateBlendFunc) setTexture(texture); @@ -683,7 +683,7 @@ void Sprite::updatePoly() }; // needed in order to get color from "_quad" - V3F_C4B_T2F_Quad tmpQuad = _quad; + V3F_T2F_C4B_Quad tmpQuad = _quad; for (int i = 0; i < 9; ++i) { @@ -736,7 +736,7 @@ void Sprite::setCenterRectNormalized(const ax::Rect& rectTopLeft) { _renderMode = RenderMode::SLICE9; // 9 quads + 7 exterior points = 16 - _trianglesVertex = (V3F_C4B_T2F*)malloc(sizeof(*_trianglesVertex) * (9 + 3 + 4)); + _trianglesVertex = (V3F_T2F_C4B*)malloc(sizeof(*_trianglesVertex) * (9 + 3 + 4)); // 9 quads, each needs 6 vertices = 54 _trianglesIndex = (unsigned short*)malloc(sizeof(*_trianglesIndex) * 6 * 9); @@ -808,7 +808,7 @@ void Sprite::setTextureCoords(const Rect& rectInPoints) setTextureCoords(rectInPoints, &_quad); } -void Sprite::setTextureCoords(const Rect& rectInPoints, V3F_C4B_T2F_Quad* outQuad) +void Sprite::setTextureCoords(const Rect& rectInPoints, V3F_T2F_C4B_Quad* outQuad) { Texture2D* tex = (_renderMode == RenderMode::QUAD_BATCHNODE) ? _textureAtlas->getTexture() : _texture; @@ -856,29 +856,29 @@ void Sprite::setTextureCoords(const Rect& rectInPoints, V3F_C4B_T2F_Quad* outQua if (_rectRotated) { - outQuad->bl.texCoords.u = left; - outQuad->bl.texCoords.v = top; - outQuad->br.texCoords.u = left; - outQuad->br.texCoords.v = bottom; - outQuad->tl.texCoords.u = right; - outQuad->tl.texCoords.v = top; - outQuad->tr.texCoords.u = right; - outQuad->tr.texCoords.v = bottom; + outQuad->bl.texCoord.u = left; + outQuad->bl.texCoord.v = top; + outQuad->br.texCoord.u = left; + outQuad->br.texCoord.v = bottom; + outQuad->tl.texCoord.u = right; + outQuad->tl.texCoord.v = top; + outQuad->tr.texCoord.u = right; + outQuad->tr.texCoord.v = bottom; } else { - outQuad->bl.texCoords.u = left; - outQuad->bl.texCoords.v = bottom; - outQuad->br.texCoords.u = right; - outQuad->br.texCoords.v = bottom; - outQuad->tl.texCoords.u = left; - outQuad->tl.texCoords.v = top; - outQuad->tr.texCoords.u = right; - outQuad->tr.texCoords.v = top; + outQuad->bl.texCoord.u = left; + outQuad->bl.texCoord.v = bottom; + outQuad->br.texCoord.u = right; + outQuad->br.texCoord.v = bottom; + outQuad->tl.texCoord.u = left; + outQuad->tl.texCoord.v = top; + outQuad->tr.texCoord.u = right; + outQuad->tr.texCoord.v = top; } } -void Sprite::setVertexCoords(const Rect& rect, V3F_C4B_T2F_Quad* outQuad) +void Sprite::setVertexCoords(const Rect& rect, V3F_T2F_C4B_Quad* outQuad) { float relativeOffsetX = _unflippedOffsetPositionFromCenter.x; float relativeOffsetY = _unflippedOffsetPositionFromCenter.y; @@ -917,14 +917,14 @@ void Sprite::setVertexCoords(const Rect& rect, V3F_C4B_T2F_Quad* outQuad) const float y2 = y1 + rect.size.height; // Don't update Z. - outQuad->bl.vertices.set(x1, y1, 0.0f); - outQuad->br.vertices.set(x2, y1, 0.0f); - outQuad->tl.vertices.set(x1, y2, 0.0f); - outQuad->tr.vertices.set(x2, y2, 0.0f); + outQuad->bl.position.set(x1, y1, 0.0f); + outQuad->br.position.set(x2, y1, 0.0f); + outQuad->tl.position.set(x1, y2, 0.0f); + outQuad->tr.position.set(x2, y2, 0.0f); } } -void Sprite::populateTriangle(int quadIndex, const V3F_C4B_T2F_Quad& quad) +void Sprite::populateTriangle(int quadIndex, const V3F_T2F_C4B_Quad& quad) { AXASSERT(quadIndex < 9, "Invalid quadIndex"); // convert Quad intro Triangle since it takes less memory @@ -993,10 +993,10 @@ void Sprite::updateTransform() // If it is not visible, or one of its ancestors is not visible, then do nothing: if (!_visible || (_parent && _parent != _batchNode && static_cast(_parent)->_shouldBeHidden)) { - _quad.br.vertices.setZero(); - _quad.tl.vertices.setZero(); - _quad.tr.vertices.setZero(); - _quad.bl.vertices.setZero(); + _quad.br.position.setZero(); + _quad.tl.position.setZero(); + _quad.tr.position.setZero(); + _quad.bl.position.setZero(); _shouldBeHidden = true; } else @@ -1044,10 +1044,10 @@ void Sprite::updateTransform() float dx = x1 * cr - y2 * sr2 + x; float dy = x1 * sr + y2 * cr2 + y; - _quad.bl.vertices.set(SPRITE_RENDER_IN_SUBPIXEL(ax), SPRITE_RENDER_IN_SUBPIXEL(ay), _positionZ); - _quad.br.vertices.set(SPRITE_RENDER_IN_SUBPIXEL(bx), SPRITE_RENDER_IN_SUBPIXEL(by), _positionZ); - _quad.tl.vertices.set(SPRITE_RENDER_IN_SUBPIXEL(dx), SPRITE_RENDER_IN_SUBPIXEL(dy), _positionZ); - _quad.tr.vertices.set(SPRITE_RENDER_IN_SUBPIXEL(cx), SPRITE_RENDER_IN_SUBPIXEL(cy), _positionZ); + _quad.bl.position.set(SPRITE_RENDER_IN_SUBPIXEL(ax), SPRITE_RENDER_IN_SUBPIXEL(ay), _positionZ); + _quad.br.position.set(SPRITE_RENDER_IN_SUBPIXEL(bx), SPRITE_RENDER_IN_SUBPIXEL(by), _positionZ); + _quad.tl.position.set(SPRITE_RENDER_IN_SUBPIXEL(dx), SPRITE_RENDER_IN_SUBPIXEL(dy), _positionZ); + _quad.tr.position.set(SPRITE_RENDER_IN_SUBPIXEL(cx), SPRITE_RENDER_IN_SUBPIXEL(cy), _positionZ); setTextureCoords(_rect); } @@ -1099,17 +1099,17 @@ void Sprite::draw(Renderer* renderer, const Mat4& transform, uint32_t flags) for (unsigned int i = 0; i < count; i++) { // draw 3 lines - Vec3 from = verts[indices[i * 3]].vertices; - Vec3 to = verts[indices[i * 3 + 1]].vertices; - _debugDrawNode->drawLine(Vec2(from.x, from.y), Vec2(to.x, to.y), Color4B::WHITE); + Vec3 from = verts[indices[i * 3]].position; + Vec3 to = verts[indices[i * 3 + 1]].position; + _debugDrawNode->drawLine(Vec2(from.x, from.y), Vec2(to.x, to.y), Color32::WHITE); - from = verts[indices[i * 3 + 1]].vertices; - to = verts[indices[i * 3 + 2]].vertices; - _debugDrawNode->drawLine(Vec2(from.x, from.y), Vec2(to.x, to.y), Color4B::WHITE); + from = verts[indices[i * 3 + 1]].position; + to = verts[indices[i * 3 + 2]].position; + _debugDrawNode->drawLine(Vec2(from.x, from.y), Vec2(to.x, to.y), Color32::WHITE); - from = verts[indices[i * 3 + 2]].vertices; - to = verts[indices[i * 3]].vertices; - _debugDrawNode->drawLine(Vec2(from.x, from.y), Vec2(to.x, to.y), Color4B::WHITE); + from = verts[indices[i * 3 + 2]].position; + to = verts[indices[i * 3]].position; + _debugDrawNode->drawLine(Vec2(from.x, from.y), Vec2(to.x, to.y), Color32::WHITE); } #endif // AX_SPRITE_DEBUG_DRAW } @@ -1467,7 +1467,7 @@ void Sprite::flipX() { for (unsigned int i = 0; i < _polyInfo.triangles.vertCount; i++) { - auto& v = _polyInfo.triangles.verts[i].vertices; + auto& v = _polyInfo.triangles.verts[i].position; v.x = _contentSize.width - v.x; } } @@ -1484,7 +1484,7 @@ void Sprite::flipY() { for (unsigned int i = 0; i < _polyInfo.triangles.vertCount; i++) { - auto& v = _polyInfo.triangles.verts[i].vertices; + auto& v = _polyInfo.triangles.verts[i].position; v.y = _contentSize.height - v.y; } } @@ -1499,23 +1499,23 @@ void Sprite::flipY() void Sprite::updateColor() { - Color4B color4(_displayedColor.r, _displayedColor.g, _displayedColor.b, _displayedOpacity); + Color color(_displayedColor, _displayedOpacity / 255.0f); // special opacity for premultiplied textures if (_opacityModifyRGB) { - color4.r *= _displayedOpacity / 255.0f; - color4.g *= _displayedOpacity / 255.0f; - color4.b *= _displayedOpacity / 255.0f; + color.r *= color.a; + color.g *= color.a; + color.b *= color.a; } for (unsigned int i = 0; i < _polyInfo.triangles.vertCount; i++) - _polyInfo.triangles.verts[i].colors = color4; + _polyInfo.triangles.verts[i].color = color; // related to issue #17116 // when switching from Quad to Slice9, the color will be obtained from _quad // so it is important to update _quad colors as well. - _quad.bl.colors = _quad.tl.colors = _quad.br.colors = _quad.tr.colors = color4; + _quad.bl.color = _quad.tl.color = _quad.br.color = _quad.tr.color = color; // renders using batch node if (_renderMode == RenderMode::QUAD_BATCHNODE) @@ -1658,10 +1658,10 @@ void Sprite::setBatchNode(SpriteBatchNode* spriteBatchNode) float y1 = _offsetPosition.y; float x2 = x1 + _rect.size.width; float y2 = y1 + _rect.size.height; - _quad.bl.vertices.set(x1, y1, 0); - _quad.br.vertices.set(x2, y1, 0); - _quad.tl.vertices.set(x1, y2, 0); - _quad.tr.vertices.set(x2, y2, 0); + _quad.bl.position.set(x1, y1, 0); + _quad.br.position.set(x2, y1, 0); + _quad.tl.position.set(x1, y2, 0); + _quad.tr.position.set(x2, y2, 0); } else { diff --git a/core/2d/Sprite.h b/core/2d/Sprite.h index 453cc01a2410..68ed8836c165 100644 --- a/core/2d/Sprite.h +++ b/core/2d/Sprite.h @@ -402,7 +402,7 @@ class AX_DLL Sprite : public Node, public TextureProtocol * @js NA * @lua NA */ - const V3F_C4B_T2F_Quad& getQuad() const { return _quad; } + const V3F_T2F_C4B_Quad& getQuad() const { return _quad; } /** * Returns whether or not the texture rectangle is rotated. @@ -660,8 +660,8 @@ class AX_DLL Sprite : public Node, public TextureProtocol protected: virtual void updateColor() override; virtual void setTextureCoords(const Rect& rect); - virtual void setTextureCoords(const Rect& rect, V3F_C4B_T2F_Quad* outQuad); - virtual void setVertexCoords(const Rect& rect, V3F_C4B_T2F_Quad* outQuad); + virtual void setTextureCoords(const Rect& rect, V3F_T2F_C4B_Quad* outQuad); + virtual void setVertexCoords(const Rect& rect, V3F_T2F_C4B_Quad* outQuad); virtual void updateBlendFunc(); virtual void setReorderChildDirtyRecursively(); virtual void setDirtyRecursively(bool value); @@ -670,7 +670,7 @@ class AX_DLL Sprite : public Node, public TextureProtocol void updatePoly(); void updateStretchFactor(); - void populateTriangle(int quadIndex, const V3F_C4B_T2F_Quad& quad); + void populateTriangle(int quadIndex, const V3F_T2F_C4B_Quad& quad); void setMVPMatrixUniform(); // // Data used when the sprite is rendered using a SpriteSheet @@ -715,8 +715,8 @@ class AX_DLL Sprite : public Node, public TextureProtocol Vec2 _unflippedOffsetPositionFromCenter; // vertex coords, texture coords and color info - V3F_C4B_T2F_Quad _quad; - V3F_C4B_T2F* _trianglesVertex = nullptr; + V3F_T2F_C4B_Quad _quad; + V3F_T2F_C4B* _trianglesVertex = nullptr; unsigned short* _trianglesIndex = nullptr; PolygonInfo _polyInfo; diff --git a/core/2d/SpriteBatchNode.cpp b/core/2d/SpriteBatchNode.cpp index a05657e83466..dc4d2f12d0a3 100644 --- a/core/2d/SpriteBatchNode.cpp +++ b/core/2d/SpriteBatchNode.cpp @@ -397,7 +397,7 @@ void SpriteBatchNode::swap(ssize_t oldIndex, ssize_t newIndex) oldIndex >= 0 && oldIndex < (int)_descendants.size() && newIndex >= 0 && newIndex < (int)_descendants.size(), "Invalid index"); - V3F_C4B_T2F_Quad* quads = _textureAtlas->getQuads(); + auto quads = _textureAtlas->getQuads(); std::swap(quads[oldIndex], quads[newIndex]); // update the index of other swapped item diff --git a/core/2d/SpriteSheetLoader.cpp b/core/2d/SpriteSheetLoader.cpp index a1bbba484333..9a4f549857fc 100644 --- a/core/2d/SpriteSheetLoader.cpp +++ b/core/2d/SpriteSheetLoader.cpp @@ -19,13 +19,13 @@ void SpriteSheetLoader::initializePolygonInfo(const Vec2& textureSize, const auto scaleFactor = AX_CONTENT_SCALE_FACTOR(); - auto* vertexData = new V3F_C4B_T2F[vertexCount]; + auto* vertexData = new V3F_T2F_C4B[vertexCount]; for (size_t i = 0; i < vertexCount / 2; i++) { - vertexData[i].colors = Color4B::WHITE; - vertexData[i].vertices = + vertexData[i].color = Color32::WHITE; + vertexData[i].position = Vec3(vertices[i * 2] / scaleFactor, (spriteSize.height - vertices[i * 2 + 1]) / scaleFactor, 0); - vertexData[i].texCoords = + vertexData[i].texCoord = Tex2F(verticesUV[i * 2] / textureSize.width, verticesUV[i * 2 + 1] / textureSize.height); } diff --git a/core/2d/TMXXMLParser.cpp b/core/2d/TMXXMLParser.cpp index 3d7d5c903245..97f5f4f8a042 100644 --- a/core/2d/TMXXMLParser.cpp +++ b/core/2d/TMXXMLParser.cpp @@ -32,13 +32,13 @@ THE SOFTWARE. #include #include #include -// #include "2d/TMXTiledMap.h" + #include "base/ZipUtils.h" #include "base/Director.h" #include "base/Utils.h" #include "platform/FileUtils.h" - -// using namespace std; +#include +#include namespace ax { @@ -452,10 +452,9 @@ void TMXMapInfo::startElement(void* /*ctx*/, const char* name, const char** atts TMXLayerInfo* layer = tmxMapInfo->getLayers().back(); Vec2 layerSize = layer->_layerSize; - auto tilesAmount = static_cast(layerSize.width * layerSize.height); + auto tilesAmount = static_cast(layerSize.width * layerSize.height); - layer->_tiles = - (uint32_t*)axstd::pod_vector(tilesAmount, 0U).release_pointer(); + layer->_tiles = (uint32_t*)axstd::pod_vector(tilesAmount, 0U).release_pointer(); } else if (encoding == "base64") { @@ -534,7 +533,7 @@ void TMXMapInfo::startElement(void* /*ctx*/, const char* name, const char** atts if (tmxMapInfo->getParentElement() == TMXPropertyNone) { AXLOGD("TMX tile map: Parent element is unsupported. Cannot add property named '{}' with value '{}'", - attributeDict["name"].asString(), attributeDict["value"].asString()); + attributeDict["name"].asString(), attributeDict["value"].asString()); tmxMapInfo->setStoringCharacters(false); } else if (tmxMapInfo->getParentElement() == TMXPropertyMap) @@ -596,36 +595,36 @@ void TMXMapInfo::startElement(void* /*ctx*/, const char* name, const char** atts ValueVector pointsArray; pointsArray.reserve(10); - // parse points string into a space-separated set of points - std::stringstream pointsStream(value); - std::string pointPair; - while (std::getline(pointsStream, pointPair, ' ')) + const auto offsetX = static_cast(objectGroup->getPositionOffset().x); + const auto offsetY = static_cast(objectGroup->getPositionOffset().y); + // std::views::split 2~3x faster than std::getline + for (auto pt : std::views::split(value, ' ')) { - // parse each point combo into a comma-separated x,y point - std::stringstream pointStream(pointPair); - std::string xStr, yStr; - + std::string_view citem{pt.data(), pt.size()}; + int idx = 0; ValueMap pointDict; - - // set x - if (std::getline(pointStream, xStr, ',')) - { - int x = atoi(xStr.c_str()) + (int)objectGroup->getPositionOffset().x; - pointDict["x"] = Value(x); - } - - // set y - if (std::getline(pointStream, yStr, ',')) + for (auto subrgn : std::views::split(pt, ',')) { - int y = atoi(yStr.c_str()) + (int)objectGroup->getPositionOffset().y; - pointDict["y"] = Value(y); + int axisVal = 0; + std::string_view word(subrgn.data()); + std::from_chars(word.data(), word.data() + word.length(), axisVal, 10); + switch (idx++) + { + case 0: + pointDict["x"] = Value(axisVal + offsetX); + break; + case 1: + pointDict["y"] = Value(axisVal + offsetY); + break; + } + if (idx == 2) + break; } - // add to points array - pointsArray.emplace_back(Value(pointDict)); + pointsArray.emplace_back(Value(std::move(pointDict))); } - dict["points"] = Value(pointsArray); + dict["points"] = Value(std::move(pointsArray)); } } else if (elementName == "polyline") @@ -641,36 +640,32 @@ void TMXMapInfo::startElement(void* /*ctx*/, const char* name, const char** atts ValueVector pointsArray; pointsArray.reserve(10); - // parse points string into a space-separated set of points - std::stringstream pointsStream(value); - std::string pointPair; - while (std::getline(pointsStream, pointPair, ' ')) + const auto offsetX = static_cast(objectGroup->getPositionOffset().x); + const auto offsetY = static_cast(objectGroup->getPositionOffset().y); + for (auto pt : std::views::split(value, ' ')) { - // parse each point combo into a comma-separated x,y point - std::stringstream pointStream(pointPair); - std::string xStr, yStr; - + int idx = 0; ValueMap pointDict; - - // set x - if (std::getline(pointStream, xStr, ',')) - { - int x = atoi(xStr.c_str()) + (int)objectGroup->getPositionOffset().x; - pointDict["x"] = Value(x); - } - - // set y - if (std::getline(pointStream, yStr, ',')) + for (auto pt_axis : std::views::split(pt, ',')) { - int y = atoi(yStr.c_str()) + (int)objectGroup->getPositionOffset().y; - pointDict["y"] = Value(y); + int axisVal = 0; + std::from_chars(pt_axis.data(), pt_axis.data() + pt_axis.size(), axisVal, 10); + switch (idx++) + { + case 0: + pointDict["x"] = Value(axisVal + offsetX); + break; + case 1: + pointDict["y"] = Value(axisVal + offsetY); + break; + } + if (idx == 2) + break; } - // add to points array - pointsArray.emplace_back(Value(pointDict)); + pointsArray.emplace_back(Value(std::move(pointDict))); } - - dict["polylinePoints"] = Value(pointsArray); + dict["polylinePoints"] = Value(std::move(pointsArray)); } } else if (elementName == "animation") @@ -701,7 +696,7 @@ void TMXMapInfo::endElement(void* /*ctx*/, const char* name) tmxMapInfo->setStoringCharacters(false); TMXLayerInfo* layer = tmxMapInfo->getLayers().back(); - auto currentString = tmxMapInfo->getCurrentString(); + auto currentString = tmxMapInfo->getCurrentString(); auto buffer = utils::base64Decode(currentString); if (buffer.empty()) @@ -712,7 +707,7 @@ void TMXMapInfo::endElement(void* /*ctx*/, const char* name) if (tmxMapInfo->getLayerAttribs() & (TMXLayerAttribGzip | TMXLayerAttribZlib)) { - Vec2 s = layer->_layerSize; + Vec2 s = layer->_layerSize; // int sizeHint = s.width * s.height * sizeof(uint32_t); ssize_t sizeHint = s.width * s.height * sizeof(unsigned int); @@ -741,31 +736,17 @@ void TMXMapInfo::endElement(void* /*ctx*/, const char* name) tmxMapInfo->setStoringCharacters(false); auto currentString = tmxMapInfo->getCurrentString(); - std::vector gidTokens; - std::stringstream filestr; - filestr << currentString; - std::string sRow; - while (std::getline(filestr, sRow, '\n')) - { - std::string sGID; - std::istringstream rowstr(sRow); - while (std::getline(rowstr, sGID, ',')) - { - gidTokens.emplace_back(sGID); - } - } - - // 32-bits per gid - axstd::pod_vector buffer(gidTokens.size()); - uint32_t* bufferPtr = buffer.data(); - for (const auto& gidToken : gidTokens) - { - auto tileGid = (uint32_t)strtoul(gidToken.c_str(), nullptr, 10); - *bufferPtr = tileGid; - bufferPtr++; - } + axstd::pod_vector tileGids; + axstd::split_cb(currentString, '\n', [&tileGids](const char* first, const char* last) { + axstd::split_cb(std::string_view{first, static_cast(last - first)}, ',', + [&tileGids](const char* _first, const char* _last) { + unsigned int gid{0}; + std::from_chars(_first, _last, gid); + tileGids.push_back(gid); + }); + }); - layer->_tiles = buffer.release_pointer(); + layer->_tiles = tileGids.release_pointer(); tmxMapInfo->setCurrentString(""); } @@ -884,4 +865,4 @@ TMXTileAnimInfo* TMXTileAnimInfo::create(uint32_t tileID) return ret; } -} +} // namespace ax diff --git a/core/2d/TextFieldTTF.cpp b/core/2d/TextFieldTTF.cpp index 971b259209e5..9ec60148dc2a 100644 --- a/core/2d/TextFieldTTF.cpp +++ b/core/2d/TextFieldTTF.cpp @@ -93,7 +93,7 @@ TextFieldTTF::TextFieldTTF() , _charCount(0) , _inputText("") , _placeHolder("") // prevent Label initWithString assertion - , _colorText(Color4B::WHITE) + , _colorText(Color32::WHITE) , _secureTextEntry(false) , _passwordStyleText(PASSWORD_STYLE_TEXT_DEFAULT) , _cursorEnabled(false) @@ -449,7 +449,7 @@ void TextFieldTTF::setAttachWithIME(bool isAttachWithIME) } } -void TextFieldTTF::setTextColorInternally(const Color4B& color) +void TextFieldTTF::setTextColorInternally(const Color32& color) { if (_currentLabelType == LabelType::BMFONT) { @@ -460,7 +460,7 @@ void TextFieldTTF::setTextColorInternally(const Color4B& color) Label::setTextColor(color); } -void TextFieldTTF::setTextColor(const Color4B& color) +void TextFieldTTF::setTextColor(const Color32& color) { _colorText = color; if (!_inputText.empty()) @@ -509,17 +509,17 @@ void TextFieldTTF::update(float delta) } } -const Color4B& TextFieldTTF::getColorSpaceHolder() +const Color32& TextFieldTTF::getColorSpaceHolder() { return _colorSpaceHolder; } void TextFieldTTF::setColorSpaceHolder(const Color3B& color) { - setColorSpaceHolder(Color4B(color)); + setColorSpaceHolder(Color32(color)); } -void TextFieldTTF::setColorSpaceHolder(const Color4B& color) +void TextFieldTTF::setColorSpaceHolder(const Color32& color) { _colorSpaceHolder = color; if (_inputText.empty()) diff --git a/core/2d/TextFieldTTF.h b/core/2d/TextFieldTTF.h index a66328d1f024..d7e3bcb02724 100644 --- a/core/2d/TextFieldTTF.h +++ b/core/2d/TextFieldTTF.h @@ -155,7 +155,7 @@ class AX_DLL TextFieldTTF : public Label, public IMEDelegate * Query the color of place holder. *@return The place holder color. */ - virtual const Color4B& getColorSpaceHolder(); + virtual const Color32& getColorSpaceHolder(); /** *@brief Change input placeholder color. @@ -165,15 +165,15 @@ class AX_DLL TextFieldTTF : public Label, public IMEDelegate /** * Change the placeholder color. - *@param color The placeholder color in Color4B. + *@param color The placeholder color in Color32. */ - virtual void setColorSpaceHolder(const Color4B& color); + virtual void setColorSpaceHolder(const Color32& color); /** * Change the color of input text. - *@param textColor The text color in Color4B. + *@param textColor The text color in Color32. */ - virtual void setTextColor(const Color4B& textColor) override; + virtual void setTextColor(const Color32& textColor) override; /** * Change input text of TextField. @@ -271,8 +271,8 @@ class AX_DLL TextFieldTTF : public Label, public IMEDelegate std::string _inputText; std::string _placeHolder; - Color4B _colorSpaceHolder; - Color4B _colorText; + Color32 _colorSpaceHolder; + Color32 _colorText; bool _secureTextEntry; std::string _passwordStyleText; @@ -291,7 +291,7 @@ class AX_DLL TextFieldTTF : public Label, public IMEDelegate void makeStringSupportCursor(std::string& displayText); void updateCursorDisplayText(); void setAttachWithIME(bool isAttachWithIME); - void setTextColorInternally(const Color4B& color); + void setTextColorInternally(const Color32& color); private: class LengthStack; diff --git a/core/2d/TileMapAtlas.cpp b/core/2d/TileMapAtlas.cpp index d4b9ce6abbaf..857aa30af997 100644 --- a/core/2d/TileMapAtlas.cpp +++ b/core/2d/TileMapAtlas.cpp @@ -163,7 +163,7 @@ void TileMapAtlas::updateAtlasValueAt(const Vec2& pos, const Color3B& value, int { AXASSERT(index >= 0 && index < _textureAtlas->getCapacity(), "updateAtlasValueAt: Invalid index"); - V3F_C4B_T2F_Quad* quad = &((_textureAtlas->getQuads())[index]); + auto quad = &((_textureAtlas->getQuads())[index]); int x = pos.x; int y = pos.y; @@ -188,33 +188,33 @@ void TileMapAtlas::updateAtlasValueAt(const Vec2& pos, const Color3B& value, int float bottom = top + itemHeightInPixels / textureHigh; #endif - quad->tl.texCoords.u = left; - quad->tl.texCoords.v = top; - quad->tr.texCoords.u = right; - quad->tr.texCoords.v = top; - quad->bl.texCoords.u = left; - quad->bl.texCoords.v = bottom; - quad->br.texCoords.u = right; - quad->br.texCoords.v = bottom; - - quad->bl.vertices.x = (float)(x * _itemWidth); - quad->bl.vertices.y = (float)(y * _itemHeight); - quad->bl.vertices.z = 0.0f; - quad->br.vertices.x = (float)(x * _itemWidth + _itemWidth); - quad->br.vertices.y = (float)(y * _itemHeight); - quad->br.vertices.z = 0.0f; - quad->tl.vertices.x = (float)(x * _itemWidth); - quad->tl.vertices.y = (float)(y * _itemHeight + _itemHeight); - quad->tl.vertices.z = 0.0f; - quad->tr.vertices.x = (float)(x * _itemWidth + _itemWidth); - quad->tr.vertices.y = (float)(y * _itemHeight + _itemHeight); - quad->tr.vertices.z = 0.0f; - - Color4B color(_displayedColor.r, _displayedColor.g, _displayedColor.b, _displayedOpacity); - quad->tr.colors = color; - quad->tl.colors = color; - quad->br.colors = color; - quad->bl.colors = color; + quad->tl.texCoord.u = left; + quad->tl.texCoord.v = top; + quad->tr.texCoord.u = right; + quad->tr.texCoord.v = top; + quad->bl.texCoord.u = left; + quad->bl.texCoord.v = bottom; + quad->br.texCoord.u = right; + quad->br.texCoord.v = bottom; + + quad->bl.position.x = (float)(x * _itemWidth); + quad->bl.position.y = (float)(y * _itemHeight); + quad->bl.position.z = 0.0f; + quad->br.position.x = (float)(x * _itemWidth + _itemWidth); + quad->br.position.y = (float)(y * _itemHeight); + quad->br.position.z = 0.0f; + quad->tl.position.x = (float)(x * _itemWidth); + quad->tl.position.y = (float)(y * _itemHeight + _itemHeight); + quad->tl.position.z = 0.0f; + quad->tr.position.x = (float)(x * _itemWidth + _itemWidth); + quad->tr.position.y = (float)(y * _itemHeight + _itemHeight); + quad->tr.position.z = 0.0f; + + Color32 color(_displayedColor, _displayedOpacity); + quad->tr.color = color; + quad->tl.color = color; + quad->br.color = color; + quad->bl.color = color; _textureAtlas->setDirty(true); ssize_t totalQuads = _textureAtlas->getTotalQuads(); @@ -243,7 +243,7 @@ void TileMapAtlas::updateAtlasValues() { this->updateAtlasValueAt(Vec2(x, y), value, total); - std::string key = StringUtils::toString(x) + "," + StringUtils::toString(y); + std::string key = fmt::format("{},{}", x, y); _posToAtlasIndex[key] = total; total++; diff --git a/core/2d/Transition.cpp b/core/2d/Transition.cpp index 6df70fbd40e2..b3df8714fe07 100644 --- a/core/2d/Transition.cpp +++ b/core/2d/Transition.cpp @@ -1116,7 +1116,7 @@ void TransitionCrossFade::onEnter() // create a transparent color layer // in which we are going to add our rendertextures - Color4B color(0, 0, 0, 0); + Color32 color(0, 0, 0, 0); Vec2 size = _director->getWinSize(); LayerColor* layer = LayerColor::create(color); diff --git a/core/2d/Transition.h b/core/2d/Transition.h index 9841f57a2066..cd3a4510a064 100644 --- a/core/2d/Transition.h +++ b/core/2d/Transition.h @@ -777,7 +777,7 @@ class AX_DLL TransitionFade : public TransitionScene bool initWithDuration(float t, Scene* scene); protected: - Color4B _color; + Color32 _color; private: AX_DISALLOW_COPY_AND_ASSIGN(TransitionFade); diff --git a/core/3d/Bundle3D.cpp b/core/3d/Bundle3D.cpp index bbc4d0ac5859..a87a653dd186 100644 --- a/core/3d/Bundle3D.cpp +++ b/core/3d/Bundle3D.cpp @@ -29,22 +29,21 @@ THE SOFTWARE. #include "base/Macros.h" #include "platform/FileUtils.h" -#include "3d/BundleReader.h" #include "base/Data.h" -#define BUNDLE_TYPE_SCENE 1 -#define BUNDLE_TYPE_NODE 2 -#define BUNDLE_TYPE_ANIMATIONS 3 -#define BUNDLE_TYPE_ANIMATION 4 +#define BUNDLE_TYPE_SCENE 1 +#define BUNDLE_TYPE_NODE 2 +#define BUNDLE_TYPE_ANIMATIONS 3 +#define BUNDLE_TYPE_ANIMATION 4 #define BUNDLE_TYPE_ANIMATION_CHANNEL 5 -#define BUNDLE_TYPE_MODEL 10 -#define BUNDLE_TYPE_MATERIAL 16 -#define BUNDLE_TYPE_EFFECT 18 -#define BUNDLE_TYPE_CAMERA 32 -#define BUNDLE_TYPE_LIGHT 33 -#define BUNDLE_TYPE_MESH 34 -#define BUNDLE_TYPE_MESHPART 35 -#define BUNDLE_TYPE_MESHSKIN 36 +#define BUNDLE_TYPE_MODEL 10 +#define BUNDLE_TYPE_MATERIAL 16 +#define BUNDLE_TYPE_EFFECT 18 +#define BUNDLE_TYPE_CAMERA 32 +#define BUNDLE_TYPE_LIGHT 33 +#define BUNDLE_TYPE_MESH 34 +#define BUNDLE_TYPE_MESHPART 35 +#define BUNDLE_TYPE_MESHSKIN 36 static const char* VERSION = "version"; static const char* ID = "id"; @@ -91,23 +90,26 @@ static const char* AABBS = "aabb"; namespace ax { - -void getChildMap(std::map>& map, SkinData* skinData, const rapidjson::Value& val) +using namespace simdjson; +void getChildMap(std::map>& map, + SkinData* skinData, + simdjson::simdjson_result& val) { if (!skinData) return; // get transform matrix Mat4 transform; - const rapidjson::Value& parent_transform = val[OLDTRANSFORM]; - for (rapidjson::SizeType j = 0, size = parent_transform.Size(); j < size; ++j) + auto parent_transform = val.get_object()[OLDTRANSFORM]; + int j = 0; + for (auto vv : val) { - transform.m[j] = parent_transform[j].GetFloat(); + transform.m[j++] = static_cast(vv); } // set origin matrices - std::string parent_name = val[ID].GetString(); - int parent_name_index = skinData->getSkinBoneNameIndex(parent_name); + std::string_view parent_name = val[ID]; + int parent_name_index = skinData->getSkinBoneNameIndex(parent_name); if (parent_name_index < 0) { skinData->addNodeBoneNames(parent_name); @@ -123,17 +125,13 @@ void getChildMap(std::map>& map, SkinData* skinData, const if (skinData->rootBoneIndex < 0) skinData->rootBoneIndex = parent_name_index; - if (!val.HasMember(CHILDREN)) + auto children = val[CHILDREN]; + if (children.is_null()) return; - - const rapidjson::Value& children = val[CHILDREN]; - for (rapidjson::SizeType i = 0, size = children.Size(); i < size; ++i) + for (auto child : children) { - // get child bone name - const rapidjson::Value& child = children[i]; - - std::string child_name = child[ID].GetString(); - int child_name_index = skinData->getSkinBoneNameIndex(child_name); + std::string_view child_name = child[ID]; + int child_name_index = skinData->getSkinBoneNameIndex(child_name); if (child_name_index < 0) { skinData->addNodeBoneNames(child_name); @@ -394,100 +392,79 @@ bool Bundle3D::loadMeshDatasBinary(MeshDatas& meshdatas) { if (!seekToFirstType(BUNDLE_TYPE_MESH)) return false; - unsigned int meshSize = 0; - if (_binaryReader.read(&meshSize, 4, 1) != 1) - { - AXLOGW("warning: Failed to read meshdata: attribCount '{}'.", _path); - return false; - } MeshData* meshData = nullptr; - for (unsigned int i = 0; i < meshSize; ++i) + try { - unsigned int attribSize = 0; - // read mesh data - if (_binaryReader.read(&attribSize, 4, 1) != 1 || attribSize < 1) + unsigned int meshSize = _binaryReader.read(); + for (unsigned int i = 0; i < meshSize; ++i) { - AXLOGW("warning: Failed to read meshdata: attribCount '{}'.", _path); - goto FAILED; - } - meshData = new MeshData(); - meshData->attribCount = attribSize; - meshData->attribs.resize(meshData->attribCount); - for (ssize_t j = 0; j < meshData->attribCount; ++j) - { - std::string attribute = ""; - unsigned int vSize; - if (_binaryReader.read(&vSize, 4, 1) != 1) + unsigned int attribSize = _binaryReader.read(); + + // read mesh data + if (attribSize < 1) { - AXLOGW("warning: Failed to read meshdata: usage or size '{}'.", _path); + AXLOGW("warning: Failed to read meshdata: attribCount '{}'.", _path); goto FAILED; } - std::string type = _binaryReader.readString(); - attribute = _binaryReader.readString(); - meshData->attribs[j].type = parseGLDataType(type, vSize); - meshData->attribs[j].vertexAttrib = parseGLProgramAttribute(attribute); - } - unsigned int vertexSizeInFloat = 0; - // Read vertex data - if (_binaryReader.read(&vertexSizeInFloat, 4, 1) != 1 || vertexSizeInFloat == 0) - { - AXLOGW("warning: Failed to read meshdata: vertexSizeInFloat '{}'.", _path); - goto FAILED; - } - - meshData->vertex.resize(vertexSizeInFloat); - if (_binaryReader.read(&meshData->vertex[0], 4, vertexSizeInFloat) != vertexSizeInFloat) - { - AXLOGW("warning: Failed to read meshdata: vertex element '{}'.", _path); - goto FAILED; - } - - // Read index data - unsigned int meshPartCount = 1; - _binaryReader.read(&meshPartCount, 4, 1); - - for (unsigned int k = 0; k < meshPartCount; ++k) - { - IndexArray indexArray{}; - std::string meshPartid = _binaryReader.readString(); - meshData->subMeshIds.emplace_back(meshPartid); - unsigned int nIndexCount; - if (_binaryReader.read(&nIndexCount, 4, 1) != 1) + meshData = new MeshData(); + meshData->attribCount = attribSize; + meshData->attribs.resize(meshData->attribCount); + for (ssize_t j = 0; j < meshData->attribCount; ++j) { - AXLOGW("warning: Failed to read meshdata: nIndexCount '{}'.", _path); - goto FAILED; + unsigned int vSize = _binaryReader.read(); + std::string_view type = _binaryReader.read_v32(); + std::string_view attribute = _binaryReader.read_v32(); + meshData->attribs[j].type = parseGLDataType(type, vSize); + meshData->attribs[j].vertexAttrib = parseGLProgramAttribute(attribute); } - indexArray.resize(nIndexCount); - if (_binaryReader.read(indexArray.data(), 2, nIndexCount) != nIndexCount) + unsigned int vertexSizeInFloat = _binaryReader.read(); + // Read vertex data + if (vertexSizeInFloat == 0) { - AXLOGW("warning: Failed to read meshdata: indices '{}'.", _path); + AXLOGW("warning: Failed to read meshdata: vertexSizeInFloat '{}'.", _path); goto FAILED; } - auto& storedIndices = meshData->subMeshIndices.emplace_back(std::move(indexArray)); - meshData->numIndex = (int)meshData->subMeshIndices.size(); - // meshData->subMeshAABB.emplace_back(calculateAABB(meshData->vertex, meshData->getPerVertexSize(), - // indexArray)); - if (_version != "0.3" && _version != "0.4" && _version != "0.5") + + meshData->vertex.resize(vertexSizeInFloat); + + _binaryReader.read_blob(&meshData->vertex[0], 4, vertexSizeInFloat); + + // Read index data + unsigned int meshPartCount = _binaryReader.read(); + for (unsigned int k = 0; k < meshPartCount; ++k) { - // read mesh aabb - float aabb[6]; - if (_binaryReader.read(aabb, 4, 6) != 6) + IndexArray indexArray{}; + std::string_view meshPartid = _binaryReader.read_v32(); + meshData->subMeshIds.emplace_back(meshPartid); + unsigned int nIndexCount = _binaryReader.read(); + indexArray.resize(nIndexCount); + _binaryReader.read_blob(indexArray.data(), 2, nIndexCount); + auto& storedIndices = meshData->subMeshIndices.emplace_back(std::move(indexArray)); + meshData->numIndex = (int)meshData->subMeshIndices.size(); + // meshData->subMeshAABB.emplace_back(calculateAABB(meshData->vertex, meshData->getPerVertexSize(), + // indexArray)); + if (_version != "0.3" && _version != "0.4" && _version != "0.5") { - AXLOGW("warning: Failed to read meshdata: aabb '{}'.", _path); - goto FAILED; + // read mesh aabb + Vec3 aabb[2]; + _binaryReader.read_blob(aabb); + meshData->subMeshAABB.emplace_back(AABB(aabb[0], aabb[1])); + } + else + { + meshData->subMeshAABB.emplace_back( + calculateAABB(meshData->vertex, meshData->getPerVertexSize(), storedIndices)); } - meshData->subMeshAABB.emplace_back(AABB(Vec3(aabb[0], aabb[1], aabb[2]), Vec3(aabb[3], aabb[4], aabb[5]))); - } - else - { - meshData->subMeshAABB.emplace_back( - calculateAABB(meshData->vertex, meshData->getPerVertexSize(), storedIndices)); } + meshdatas.meshDatas.emplace_back(meshData); } - meshdatas.meshDatas.emplace_back(meshData); - } - return true; + return true; + } + catch (const std::exception& ex) + { + AXLOGW("exception occurred: {}", ex.what()); + } FAILED: { AX_SAFE_DELETE(meshData); @@ -508,113 +485,103 @@ bool Bundle3D::loadMeshDatasBinary_0_1(MeshDatas& meshdatas) MeshData* meshdata = new MeshData(); - // read mesh data - unsigned int attribSize = 0; - if (_binaryReader.read(&attribSize, 4, 1) != 1 || attribSize < 1) - { - AXLOGW("warning: Failed to read meshdata: attribCount '{}'.", _path); - AX_SAFE_DELETE(meshdata); - return false; - } - enum - { - VERTEX_ATTRIB_POSITION, - VERTEX_ATTRIB_COLOR, - VERTEX_ATTRIB_TEX_COORD, - VERTEX_ATTRIB_NORMAL, - VERTEX_ATTRIB_BLEND_WEIGHT, - VERTEX_ATTRIB_BLEND_INDEX, - - VERTEX_ATTRIB_MAX, - - // backward compatibility - VERTEX_ATTRIB_TEX_COORDS = VERTEX_ATTRIB_TEX_COORD, - }; - for (unsigned int i = 0; i < attribSize; ++i) + try { - unsigned int vUsage, vSize; - shaderinfos::VertexKey usage; - if (_binaryReader.read(&vUsage, 4, 1) != 1 || _binaryReader.read(&vSize, 4, 1) != 1) + // read mesh data + unsigned int attribSize = _binaryReader.read(); + if (attribSize < 1) { - AXLOGW("warning: Failed to read meshdata: usage or size '{}'.", _path); + AXLOGW("warning: Failed to read meshdata: attribCount '{}'.", _path); AX_SAFE_DELETE(meshdata); return false; } - - MeshVertexAttrib meshVertexAttribute; - meshVertexAttribute.type = parseGLDataType("GL_FLOAT", vSize); - if (vUsage == VERTEX_ATTRIB_NORMAL) - { - usage = shaderinfos::VertexKey::VERTEX_ATTRIB_NORMAL; - } - else if (vUsage == VERTEX_ATTRIB_BLEND_WEIGHT) + enum { - usage = shaderinfos::VertexKey::VERTEX_ATTRIB_BLEND_WEIGHT; - } - else if (vUsage == VERTEX_ATTRIB_BLEND_INDEX) - { - usage = shaderinfos::VertexKey::VERTEX_ATTRIB_BLEND_INDEX; - } - else if (vUsage == VERTEX_ATTRIB_POSITION) - { - usage = shaderinfos::VertexKey::VERTEX_ATTRIB_POSITION; - } - else if (vUsage == VERTEX_ATTRIB_TEX_COORD) - { - usage = shaderinfos::VertexKey::VERTEX_ATTRIB_TEX_COORD; - } - else - { - AXASSERT(false, "invalidate usage value"); - } - meshVertexAttribute.vertexAttrib = usage; + VERTEX_ATTRIB_POSITION, + VERTEX_ATTRIB_COLOR, + VERTEX_ATTRIB_TEX_COORD, + VERTEX_ATTRIB_NORMAL, + VERTEX_ATTRIB_BLEND_WEIGHT, + VERTEX_ATTRIB_BLEND_INDEX, - meshdata->attribs.emplace_back(meshVertexAttribute); - } + VERTEX_ATTRIB_MAX, - // Read vertex data - if (_binaryReader.read(&meshdata->vertexSizeInFloat, 4, 1) != 1 || meshdata->vertexSizeInFloat == 0) - { - AXLOGW("warning: Failed to read meshdata: vertexSizeInFloat '{}'.", _path); - AX_SAFE_DELETE(meshdata); - return false; - } + // backward compatibility + VERTEX_ATTRIB_TEX_COORDS = VERTEX_ATTRIB_TEX_COORD, + }; + for (unsigned int i = 0; i < attribSize; ++i) + { + auto vUsage = _binaryReader.read(); + auto vSize = _binaryReader.read(); + shaderinfos::VertexKey usage; - meshdata->vertex.resize(meshdata->vertexSizeInFloat); - if (_binaryReader.read(&meshdata->vertex[0], 4, meshdata->vertexSizeInFloat) != meshdata->vertexSizeInFloat) - { - AXLOGW("warning: Failed to read meshdata: vertex element '{}'.", _path); - AX_SAFE_DELETE(meshdata); - return false; - } + MeshVertexAttrib meshVertexAttribute; + meshVertexAttribute.type = parseGLDataType("GL_FLOAT", vSize); + if (vUsage == VERTEX_ATTRIB_NORMAL) + { + usage = shaderinfos::VertexKey::VERTEX_ATTRIB_NORMAL; + } + else if (vUsage == VERTEX_ATTRIB_BLEND_WEIGHT) + { + usage = shaderinfos::VertexKey::VERTEX_ATTRIB_BLEND_WEIGHT; + } + else if (vUsage == VERTEX_ATTRIB_BLEND_INDEX) + { + usage = shaderinfos::VertexKey::VERTEX_ATTRIB_BLEND_INDEX; + } + else if (vUsage == VERTEX_ATTRIB_POSITION) + { + usage = shaderinfos::VertexKey::VERTEX_ATTRIB_POSITION; + } + else if (vUsage == VERTEX_ATTRIB_TEX_COORD) + { + usage = shaderinfos::VertexKey::VERTEX_ATTRIB_TEX_COORD; + } + else + { + AXASSERT(false, "invalidate usage value"); + } + meshVertexAttribute.vertexAttrib = usage; - // Read index data - unsigned int meshPartCount = 1; - for (unsigned int i = 0; i < meshPartCount; ++i) - { - unsigned int nIndexCount; - if (_binaryReader.read(&nIndexCount, 4, 1) != 1) + meshdata->attribs.emplace_back(meshVertexAttribute); + } + + // Read vertex data + meshdata->vertexSizeInFloat = _binaryReader.read(); + if (meshdata->vertexSizeInFloat == 0) { - AXLOGW("warning: Failed to read meshdata: nIndexCount '{}'.", _path); + AXLOGW("warning: Failed to read meshdata: vertexSizeInFloat '{}'.", _path); AX_SAFE_DELETE(meshdata); return false; } - IndexArray indices{}; - indices.resize(nIndexCount); - if (_binaryReader.read(indices.data(), 2, nIndexCount) != nIndexCount) + meshdata->vertex.resize(meshdata->vertexSizeInFloat); + _binaryReader.read_blob(&meshdata->vertex[0], 4, meshdata->vertexSizeInFloat); + + // Read index data + unsigned int meshPartCount = 1; + for (unsigned int i = 0; i < meshPartCount; ++i) { - AXLOGW("warning: Failed to read meshdata: indices '{}'.", _path); - AX_SAFE_DELETE(meshdata); - return false; + unsigned int nIndexCount = _binaryReader.read(); + + IndexArray indices{}; + indices.resize(nIndexCount); + _binaryReader.read_blob(indices.data(), 2, nIndexCount); + + auto& storedIndices = meshdata->subMeshIndices.emplace_back(std::move(indices)); + meshdata->subMeshAABB.emplace_back( + calculateAABB(meshdata->vertex, meshdata->getPerVertexSize(), storedIndices)); } - auto& storedIndices = meshdata->subMeshIndices.emplace_back(std::move(indices)); - meshdata->subMeshAABB.emplace_back(calculateAABB(meshdata->vertex, meshdata->getPerVertexSize(), storedIndices)); - } + meshdatas.meshDatas.emplace_back(meshdata); - meshdatas.meshDatas.emplace_back(meshdata); - return true; + return true; + } + catch (const std::exception& ex) + { + AXLOGW("exception occurred: {}", ex.what()); + } + return false; } bool Bundle3D::loadMeshDatasBinary_0_2(MeshDatas& meshdatas) @@ -626,182 +593,183 @@ bool Bundle3D::loadMeshDatasBinary_0_2(MeshDatas& meshdatas) MeshData* meshdata = new MeshData(); - // read mesh data - unsigned int attribSize = 0; - if (_binaryReader.read(&attribSize, 4, 1) != 1 || attribSize < 1) - { - AXLOGW("warning: Failed to read meshdata: attribCount '{}'.", _path); - AX_SAFE_DELETE(meshdata); - return false; - } - enum - { - VERTEX_ATTRIB_POSITION, - VERTEX_ATTRIB_COLOR, - VERTEX_ATTRIB_TEX_COORD, - VERTEX_ATTRIB_NORMAL, - VERTEX_ATTRIB_BLEND_WEIGHT, - VERTEX_ATTRIB_BLEND_INDEX, - - VERTEX_ATTRIB_MAX, - - // backward compatibility - VERTEX_ATTRIB_TEX_COORDS = VERTEX_ATTRIB_TEX_COORD, - }; - for (unsigned int i = 0; i < attribSize; ++i) + try { - unsigned int vUsage, vSize; - shaderinfos::VertexKey usage = shaderinfos::VertexKey::VERTEX_ATTRIB_ERROR; - if (_binaryReader.read(&vUsage, 4, 1) != 1 || _binaryReader.read(&vSize, 4, 1) != 1) + // read mesh data + unsigned int attribSize = 0; + if (_binaryReader.read_blob(attribSize) != 1 || attribSize < 1) { - AXLOGW("warning: Failed to read meshdata: usage or size '{}'.", _path); + AXLOGW("warning: Failed to read meshdata: attribCount '{}'.", _path); AX_SAFE_DELETE(meshdata); return false; } - - MeshVertexAttrib meshVertexAttribute; - meshVertexAttribute.type = parseGLDataType("GL_FLOAT", vSize); - if (vUsage == VERTEX_ATTRIB_NORMAL) - { - usage = shaderinfos::VertexKey::VERTEX_ATTRIB_NORMAL; - } - else if (vUsage == VERTEX_ATTRIB_BLEND_WEIGHT) - { - usage = shaderinfos::VertexKey::VERTEX_ATTRIB_BLEND_WEIGHT; - } - else if (vUsage == VERTEX_ATTRIB_BLEND_INDEX) - { - usage = shaderinfos::VertexKey::VERTEX_ATTRIB_BLEND_INDEX; - } - else if (vUsage == VERTEX_ATTRIB_POSITION) - { - usage = shaderinfos::VertexKey::VERTEX_ATTRIB_POSITION; - } - else if (vUsage == VERTEX_ATTRIB_TEX_COORD) + enum { - usage = shaderinfos::VertexKey::VERTEX_ATTRIB_TEX_COORD; - } - meshVertexAttribute.vertexAttrib = usage; + VERTEX_ATTRIB_POSITION, + VERTEX_ATTRIB_COLOR, + VERTEX_ATTRIB_TEX_COORD, + VERTEX_ATTRIB_NORMAL, + VERTEX_ATTRIB_BLEND_WEIGHT, + VERTEX_ATTRIB_BLEND_INDEX, - meshdata->attribs.emplace_back(meshVertexAttribute); - } + VERTEX_ATTRIB_MAX, - // Read vertex data - if (_binaryReader.read(&meshdata->vertexSizeInFloat, 4, 1) != 1 || meshdata->vertexSizeInFloat == 0) - { - AXLOGW("warning: Failed to read meshdata: vertexSizeInFloat '{}'.", _path); - AX_SAFE_DELETE(meshdata); - return false; - } + // backward compatibility + VERTEX_ATTRIB_TEX_COORDS = VERTEX_ATTRIB_TEX_COORD, + }; + for (unsigned int i = 0; i < attribSize; ++i) + { + unsigned int vUsage, vSize; + shaderinfos::VertexKey usage = shaderinfos::VertexKey::VERTEX_ATTRIB_ERROR; + if (_binaryReader.read_blob(vUsage) != 1 || _binaryReader.read_blob(vSize) != 1) + { + AXLOGW("warning: Failed to read meshdata: usage or size '{}'.", _path); + AX_SAFE_DELETE(meshdata); + return false; + } - meshdata->vertex.resize(meshdata->vertexSizeInFloat); - if (_binaryReader.read(&meshdata->vertex[0], 4, meshdata->vertexSizeInFloat) != meshdata->vertexSizeInFloat) - { - AXLOGW("warning: Failed to read meshdata: vertex element '{}'.", _path); - AX_SAFE_DELETE(meshdata); - return false; - } + MeshVertexAttrib meshVertexAttribute; + meshVertexAttribute.type = parseGLDataType("GL_FLOAT", vSize); + if (vUsage == VERTEX_ATTRIB_NORMAL) + { + usage = shaderinfos::VertexKey::VERTEX_ATTRIB_NORMAL; + } + else if (vUsage == VERTEX_ATTRIB_BLEND_WEIGHT) + { + usage = shaderinfos::VertexKey::VERTEX_ATTRIB_BLEND_WEIGHT; + } + else if (vUsage == VERTEX_ATTRIB_BLEND_INDEX) + { + usage = shaderinfos::VertexKey::VERTEX_ATTRIB_BLEND_INDEX; + } + else if (vUsage == VERTEX_ATTRIB_POSITION) + { + usage = shaderinfos::VertexKey::VERTEX_ATTRIB_POSITION; + } + else if (vUsage == VERTEX_ATTRIB_TEX_COORD) + { + usage = shaderinfos::VertexKey::VERTEX_ATTRIB_TEX_COORD; + } + meshVertexAttribute.vertexAttrib = usage; - // read submesh - unsigned int submeshCount; - if (_binaryReader.read(&submeshCount, 4, 1) != 1) - { - AXLOGW("warning: Failed to read meshdata: submeshCount '{}'.", _path); - AX_SAFE_DELETE(meshdata); - return false; - } + meshdata->attribs.emplace_back(meshVertexAttribute); + } - for (unsigned int i = 0; i < submeshCount; ++i) - { - unsigned int nIndexCount; - if (_binaryReader.read(&nIndexCount, 4, 1) != 1) + // Read vertex data + if (_binaryReader.read_blob(&meshdata->vertexSizeInFloat, 4, 1) != 1 || meshdata->vertexSizeInFloat == 0) { - AXLOGW("warning: Failed to read meshdata: nIndexCount '{}'.", _path); + AXLOGW("warning: Failed to read meshdata: vertexSizeInFloat '{}'.", _path); AX_SAFE_DELETE(meshdata); return false; } - IndexArray indices{}; /* TODO: _version == 1.3 use U_INT?*/ - indices.resize(nIndexCount); - if (_binaryReader.read(indices.data(), 2, nIndexCount) != nIndexCount) + meshdata->vertex.resize(meshdata->vertexSizeInFloat); + if (_binaryReader.read_blob(&meshdata->vertex[0], 4, meshdata->vertexSizeInFloat) != meshdata->vertexSizeInFloat) { - AXLOGW("warning: Failed to read meshdata: indices '{}'.", _path); + AXLOGW("warning: Failed to read meshdata: vertex element '{}'.", _path); AX_SAFE_DELETE(meshdata); return false; } - auto& storedIndices = meshdata->subMeshIndices.emplace_back(std::move(indices)); - meshdata->subMeshAABB.emplace_back(calculateAABB(meshdata->vertex, meshdata->getPerVertexSize(), storedIndices)); - } + // read submesh + unsigned int submeshCount; + if (_binaryReader.read_blob(submeshCount) != 1) + { + AXLOGW("warning: Failed to read meshdata: submeshCount '{}'.", _path); + AX_SAFE_DELETE(meshdata); + return false; + } - meshdatas.meshDatas.emplace_back(meshdata); + for (unsigned int i = 0; i < submeshCount; ++i) + { + unsigned int nIndexCount; + if (_binaryReader.read_blob(nIndexCount) != 1) + { + AXLOGW("warning: Failed to read meshdata: nIndexCount '{}'.", _path); + AX_SAFE_DELETE(meshdata); + return false; + } - return true; + IndexArray indices{}; /* TODO: _version == 1.3 use U_INT?*/ + indices.resize(nIndexCount); + if (_binaryReader.read_blob(indices.data(), 2, nIndexCount) != nIndexCount) + { + AXLOGW("warning: Failed to read meshdata: indices '{}'.", _path); + AX_SAFE_DELETE(meshdata); + return false; + } + + auto& storedIndices = meshdata->subMeshIndices.emplace_back(std::move(indices)); + meshdata->subMeshAABB.emplace_back( + calculateAABB(meshdata->vertex, meshdata->getPerVertexSize(), storedIndices)); + } + + meshdatas.meshDatas.emplace_back(meshdata); + return true; + } + catch (const std::exception& ex) + { + AXLOGW("exception occurred: {}", ex.what()); + } + + return false; } bool Bundle3D::loadMeshDatasJson(MeshDatas& meshdatas) { - const rapidjson::Value& mesh_data_array = _jsonReader[MESHES]; - for (rapidjson::SizeType index = 0, mesh_data_array_size = mesh_data_array.Size(); index < mesh_data_array_size; - ++index) + auto mesh_data_array = _jsonReader[MESHES]; + for (auto&& mesh_data : mesh_data_array) { - MeshData* meshData = new MeshData(); - const rapidjson::Value& mesh_data = mesh_data_array[index]; + MeshData* meshData = new MeshData(); // mesh_vertex_attribute - const rapidjson::Value& mesh_vertex_attribute = mesh_data[ATTRIBUTES]; - MeshVertexAttrib tempAttrib; - meshData->attribCount = mesh_vertex_attribute.Size(); - meshData->attribs.resize(meshData->attribCount); - for (rapidjson::SizeType i = 0, mesh_vertex_attribute_size = mesh_vertex_attribute.Size(); - i < mesh_vertex_attribute_size; ++i) - { - const rapidjson::Value& mesh_vertex_attribute_val = mesh_vertex_attribute[i]; + auto&& mesh_vertex_attribute = mesh_data[ATTRIBUTES].get_array(); - int size = mesh_vertex_attribute_val[ATTRIBUTESIZE].GetInt(); - std::string type = mesh_vertex_attribute_val[TYPE].GetString(); - std::string attribute = mesh_vertex_attribute_val[ATTRIBUTE].GetString(); + meshData->attribCount = mesh_vertex_attribute.count_elements(); + meshData->attribs.reserve(meshData->attribCount); + for (auto&& mesh_vertex_attribute_val : mesh_vertex_attribute) + { + int size = static_cast(mesh_vertex_attribute_val[ATTRIBUTESIZE]); + std::string_view type = mesh_vertex_attribute_val[TYPE]; + std::string_view attribute = mesh_vertex_attribute_val[ATTRIBUTE]; - tempAttrib.type = parseGLDataType(type, size); - tempAttrib.vertexAttrib = parseGLProgramAttribute(attribute); - meshData->attribs[i] = tempAttrib; + meshData->attribs.emplace_back(parseGLDataType(type, size), parseGLProgramAttribute(attribute)); } // mesh vertices //////////////////////////////////////////////////////////////////////////////////////////////// - const rapidjson::Value& mesh_data_vertex_array = mesh_data[VERTICES]; - auto mesh_data_vertex_array_size = mesh_data_vertex_array.Size(); - meshData->vertexSizeInFloat = mesh_data_vertex_array_size; - for (rapidjson::SizeType i = 0; i < mesh_data_vertex_array_size; ++i) + auto&& mesh_data_vertex_array = mesh_data[VERTICES].get_array(); + for (auto&& vertex_val : mesh_data_vertex_array) { - meshData->vertex.emplace_back(mesh_data_vertex_array[i].GetFloat()); + meshData->vertex.emplace_back(static_cast(vertex_val)); } + meshData->vertexSizeInFloat = meshData->vertex.size(); // mesh part //////////////////////////////////////////////////////////////////////////////////////////////// - const rapidjson::Value& mesh_part_array = mesh_data[PARTS]; - for (rapidjson::SizeType i = 0, mesh_part_array_size = mesh_part_array.Size(); i < mesh_part_array_size; ++i) + auto&& mesh_part_array = mesh_data[PARTS].get_array(); + for (auto&& mesh_part : mesh_part_array) { IndexArray indexArray{}; - const rapidjson::Value& mesh_part = mesh_part_array[i]; - meshData->subMeshIds.emplace_back(mesh_part[ID].GetString()); + meshData->subMeshIds.emplace_back(static_cast(mesh_part[ID])); // index_number - const rapidjson::Value& indices_val_array = mesh_part[INDICES]; - for (rapidjson::SizeType j = 0, indices_val_array_size = indices_val_array.Size(); - j < indices_val_array_size; ++j) - indexArray.emplace_back((unsigned short)indices_val_array[j].GetUint()); + auto&& indices_val_array = mesh_part[INDICES]; + for (auto&& indices_val : indices_val_array) + indexArray.emplace_back(static_cast(indices_val)); auto& storedIndices = meshData->subMeshIndices.emplace_back(std::move(indexArray)); - meshData->numIndex = (int)meshData->subMeshIndices.size(); + meshData->numIndex = (int)meshData->subMeshIndices.size(); - if (mesh_data.HasMember(AABBS)) + auto&& mesh_part_aabb = mesh_part[AABBS]; + if (!mesh_part_aabb.error()) { - const rapidjson::Value& mesh_part_aabb = mesh_part[AABBS]; - if (mesh_part.HasMember(AABBS) && mesh_part_aabb.Size() == 6) + if (mesh_part_aabb.count_elements() == 6) { - Vec3 min(mesh_part_aabb[(rapidjson::SizeType)0].GetFloat(), - mesh_part_aabb[(rapidjson::SizeType)1].GetFloat(), - mesh_part_aabb[(rapidjson::SizeType)2].GetFloat()); - Vec3 max(mesh_part_aabb[(rapidjson::SizeType)3].GetFloat(), - mesh_part_aabb[(rapidjson::SizeType)4].GetFloat(), - mesh_part_aabb[(rapidjson::SizeType)5].GetFloat()); - meshData->subMeshAABB.emplace_back(AABB(min, max)); + Vec3 aabb_vals[2]; + int aabb_idx = 0; + for (auto aabb_val : mesh_part_aabb) + { + aabb_vals->comps[aabb_idx++] = static_cast(aabb_val); + if (aabb_idx >= 6) + break; + } + meshData->subMeshAABB.emplace_back(AABB(aabb_vals[0], aabb_vals[1])); } else { @@ -811,7 +779,8 @@ bool Bundle3D::loadMeshDatasJson(MeshDatas& meshdatas) } else { - meshData->subMeshAABB.emplace_back(calculateAABB(meshData->vertex, meshData->getPerVertexSize(), storedIndices)); + meshData->subMeshAABB.emplace_back( + calculateAABB(meshData->vertex, meshData->getPerVertexSize(), storedIndices)); } } meshdatas.meshDatas.emplace_back(meshData); @@ -926,45 +895,52 @@ bool Bundle3D::loadMaterialsBinary(MaterialDatas& materialdatas) if (!seekToFirstType(BUNDLE_TYPE_MATERIAL)) return false; unsigned int materialnum = 1; - _binaryReader.read(&materialnum, 4, 1); - for (unsigned int i = 0; i < materialnum; ++i) + try { - NMaterialData materialData; - materialData.id = _binaryReader.readString(); + _binaryReader.read_blob(materialnum); + for (unsigned int i = 0; i < materialnum; ++i) + { + NMaterialData materialData; + materialData.id = _binaryReader.read_v32(); - // skip: diffuse(3), ambient(3), emissive(3), opacity(1), specular(3), shininess(1) - float data[14]; - _binaryReader.read(&data, sizeof(float), 14); + // skip: diffuse(3), ambient(3), emissive(3), opacity(1), specular(3), shininess(1) + float data[14]; + _binaryReader.read_blob(data); - unsigned int textureNum = 1; - _binaryReader.read(&textureNum, 4, 1); - for (unsigned int j = 0; j < textureNum; ++j) - { - NTextureData textureData; - textureData.id = _binaryReader.readString(); - if (textureData.id.empty()) - { - AXLOGW("warning: Failed to read Materialdata: texturePath is empty '{}'.", textureData.id); - return false; - } - std::string texturePath = _binaryReader.readString(); - if (texturePath.empty()) + unsigned int textureNum = 1; + _binaryReader.read_blob(textureNum); + for (unsigned int j = 0; j < textureNum; ++j) { - AXLOGW("warning: Failed to read Materialdata: texturePath is empty '{}'.", _path); - return false; + NTextureData textureData; + textureData.id = _binaryReader.read_v32(); + if (textureData.id.empty()) + { + AXLOGW("warning: Failed to read Materialdata: texturePath is empty '{}'.", textureData.id); + return false; + } + std::string_view texturePath = _binaryReader.read_v32(); + if (texturePath.empty()) + { + AXLOGW("warning: Failed to read Materialdata: texturePath is empty '{}'.", _path); + return false; + } + (textureData.filename = _modelPath) += texturePath; + float uvdata[4]; + _binaryReader.read_blob(uvdata); + textureData.type = parseGLTextureType(_binaryReader.read_v32()); + textureData.wrapS = parseSamplerAddressMode(_binaryReader.read_v32()); + textureData.wrapT = parseSamplerAddressMode(_binaryReader.read_v32()); + materialData.textures.emplace_back(textureData); } - - textureData.filename = texturePath.empty() ? texturePath : _modelPath + texturePath; - float uvdata[4]; - _binaryReader.read(&uvdata, sizeof(float), 4); - textureData.type = parseGLTextureType(_binaryReader.readString()); - textureData.wrapS = parseSamplerAddressMode(_binaryReader.readString()); - textureData.wrapT = parseSamplerAddressMode(_binaryReader.readString()); - materialData.textures.emplace_back(textureData); + materialdatas.materials.emplace_back(materialData); } - materialdatas.materials.emplace_back(materialData); + return true; } - return true; + catch (const std::exception& ex) + { + AXLOGW("exception occurred: {}", ex.what()); + } + return false; } bool Bundle3D::loadMaterialsBinary_0_1(MaterialDatas& materialdatas) { @@ -973,7 +949,7 @@ bool Bundle3D::loadMaterialsBinary_0_1(MaterialDatas& materialdatas) NMaterialData materialData; - std::string texturePath = _binaryReader.readString(); + std::string_view texturePath = _binaryReader.read_v32(); if (texturePath.empty()) { AXLOGW("warning: Failed to read Materialdata: texturePath is empty '{}'.", _path); @@ -981,9 +957,9 @@ bool Bundle3D::loadMaterialsBinary_0_1(MaterialDatas& materialdatas) } NTextureData textureData; - textureData.filename = texturePath.empty() ? texturePath : _modelPath + texturePath; - textureData.type = NTextureData::Usage::Diffuse; - textureData.id = ""; + (textureData.filename = _modelPath) += texturePath; + textureData.type = NTextureData::Usage::Diffuse; + textureData.id = ""; materialData.textures.emplace_back(textureData); materialdatas.materials.emplace_back(materialData); return true; @@ -994,28 +970,36 @@ bool Bundle3D::loadMaterialsBinary_0_2(MaterialDatas& materialdatas) if (!seekToFirstType(BUNDLE_TYPE_MATERIAL)) return false; - unsigned int materialnum = 1; - _binaryReader.read(&materialnum, 4, 1); - - for (unsigned int i = 0; i < materialnum; ++i) + try { - NMaterialData materialData; + unsigned int materialnum = 1; + _binaryReader.read_blob(materialnum); - std::string texturePath = _binaryReader.readString(); - if (texturePath.empty()) + for (unsigned int i = 0; i < materialnum; ++i) { - AXLOGW("warning: Failed to read Materialdata: texturePath is empty '{}'.", _path); - return true; - } + NMaterialData materialData; - NTextureData textureData; - textureData.filename = texturePath.empty() ? texturePath : _modelPath + texturePath; - textureData.type = NTextureData::Usage::Diffuse; - textureData.id = ""; - materialData.textures.emplace_back(textureData); - materialdatas.materials.emplace_back(materialData); + std::string_view texturePath = _binaryReader.read_v32(); + if (texturePath.empty()) + { + AXLOGW("warning: Failed to read Materialdata: texturePath is empty '{}'.", _path); + return true; + } + + NTextureData textureData; + (textureData.filename = _modelPath) += texturePath; + textureData.type = NTextureData::Usage::Diffuse; + textureData.id = ""; + materialData.textures.emplace_back(textureData); + materialdatas.materials.emplace_back(materialData); + } + return true; } - return true; + catch (const std::exception& ex) + { + AXLOGW("exception occurred: {}", ex.what()); + } + return false; } bool loadMeshDataJson(MeshData* /*meshdata*/) @@ -1035,26 +1019,25 @@ bool loadMeshDataJson_0_2(MeshData* /*meshdata*/) bool Bundle3D::loadMaterialsJson(MaterialDatas& materialdatas) { - if (!_jsonReader.HasMember(MATERIALS)) + auto material_array = _jsonReader[MATERIALS].get_array(); + if (material_array.error()) return false; - const rapidjson::Value& material_array = _jsonReader[MATERIALS]; - for (rapidjson::SizeType i = 0; i < material_array.Size(); ++i) + for (auto&& material_val : material_array) { NMaterialData materialData; - const rapidjson::Value& material_val = material_array[i]; - materialData.id = material_val[ID].GetString(); - if (material_val.HasMember(TEXTURES)) + materialData.id = static_cast(material_val[ID]); + auto&& texture_array = material_val[TEXTURES]; + if (!texture_array.error()) { - const rapidjson::Value& texture_array = material_val[TEXTURES]; - for (rapidjson::SizeType j = 0; j < texture_array.Size(); ++j) + for (auto&& texture_val : texture_array.get_array()) { NTextureData textureData; - const rapidjson::Value& texture_val = texture_array[j]; - std::string filename = texture_val[FILENAME].GetString(); - textureData.filename = filename.empty() ? filename : _modelPath + filename; - textureData.type = parseGLTextureType(texture_val["type"].GetString()); - textureData.wrapS = parseSamplerAddressMode(texture_val["wrapModeU"].GetString()); - textureData.wrapT = parseSamplerAddressMode(texture_val["wrapModeV"].GetString()); + std::string_view filename = texture_val[FILENAME]; + if (!filename.empty()) + (textureData.filename = _modelPath) += filename; + textureData.type = parseGLTextureType(texture_val["type"]); + textureData.wrapS = parseSamplerAddressMode(texture_val["wrapModeU"]); + textureData.wrapT = parseSamplerAddressMode(texture_val["wrapModeV"]); materialData.textures.emplace_back(textureData); } } @@ -1066,20 +1049,21 @@ bool Bundle3D::loadJson(std::string_view path) { clear(); - _jsonBuffer = FileUtils::getInstance()->getStringFromFile(path); + FileUtils::getInstance()->getContents(path, &_jsonBuffer); - if (_jsonReader.ParseInsitu<0>((char*)_jsonBuffer.c_str()).HasParseError()) + _jsonReader = _jsonParser.iterate(_jsonBuffer); + if (_jsonReader.is_null()) { clear(); AXLOGW("Parse json failed in Bundle3D::loadJson function"); return false; } - const rapidjson::Value& mash_data_array = _jsonReader[VERSION]; - if (mash_data_array.IsArray()) // Compatible with the old version + auto mash_data_array = _jsonReader[VERSION]; + if (mash_data_array.type() == ondemand::json_type::array) // Compatible with the old version _version = "1.2"; else - _version = mash_data_array.GetString(); + _version = static_cast(mash_data_array); return true; } @@ -1099,98 +1083,108 @@ bool Bundle3D::loadBinary(std::string_view path) } // Initialise bundle reader - _binaryReader.init((char*)_binaryBuffer.getBytes(), _binaryBuffer.getSize()); + _binaryReader.reset((char*)_binaryBuffer.getBytes(), _binaryBuffer.getSize()); - // Read identifier info - char identifier[] = {'C', '3', 'B', '\0'}; - char sig[4]; - if (_binaryReader.read(sig, 1, 4) != 4 || memcmp(sig, identifier, 4) != 0) + try { - clear(); - AXLOGW("warning: Invalid identifier: {}", path); - return false; - } - - // Read version - unsigned char ver[2]; - if (_binaryReader.read(ver, 1, 2) != 2) - { - AXLOGW("warning: Failed to read version:"); - return false; - } + // Read identifier info + char identifier[] = {'C', '3', 'B', '\0'}; + char sig[4]; + if (_binaryReader.read_blob(sig) != 4 || memcmp(sig, identifier, 4) != 0) + { + clear(); + AXLOGW("warning: Invalid identifier: {}", path); + return false; + } - char version[20] = {0}; - snprintf(version, sizeof(version), "%d.%d", ver[0], ver[1]); - _version = version; + // Read version + unsigned char ver[2]; + if (_binaryReader.read_blob(ver) != 2) + { + AXLOGW("warning: Failed to read version:"); + return false; + } - // Read ref table size - if (_binaryReader.read(&_referenceCount, 4, 1) != 1) - { - clear(); - AXLOGW("warning: Failed to read ref table size '{}'.", path); - return false; - } + char version[20] = {0}; + snprintf(version, sizeof(version), "%d.%d", ver[0], ver[1]); + _version = version; - // Read all refs - AX_SAFE_DELETE_ARRAY(_references); - _references = new Reference[_referenceCount]; - for (unsigned int i = 0; i < _referenceCount; ++i) - { - if ((_references[i].id = _binaryReader.readString()).empty() || - _binaryReader.read(&_references[i].type, 4, 1) != 1 || - _binaryReader.read(&_references[i].offset, 4, 1) != 1) + // Read ref table size + if (_binaryReader.read_blob(_referenceCount) != 1) { clear(); - AXLOGW("warning: Failed to read ref number {} for bundle '{}'.", i, path); - AX_SAFE_DELETE_ARRAY(_references); + AXLOGW("warning: Failed to read ref table size '{}'.", path); return false; } - } - return true; + // Read all refs + AX_SAFE_DELETE_ARRAY(_references); + _references = new Reference[_referenceCount]; + for (unsigned int i = 0; i < _referenceCount; ++i) + { + if ((_references[i].id = _binaryReader.read_v32()).empty() || + _binaryReader.read_blob(_references[i].type) != 1 || _binaryReader.read_blob(_references[i].offset) != 1) + { + clear(); + AXLOGW("warning: Failed to read ref number {} for bundle '{}'.", i, path); + AX_SAFE_DELETE_ARRAY(_references); + return false; + } + } + + return true; + } + catch (const std::exception& ex) + { + AXLOGW("exception occurred: {}", ex.what()); + } + return false; } bool Bundle3D::loadMeshDataJson_0_1(MeshDatas& meshdatas) { - const rapidjson::Value& mesh_data_array = _jsonReader[MESH]; - MeshData* meshdata = new MeshData(); - const rapidjson::Value& mesh_data_val = mesh_data_array[(rapidjson::SizeType)0]; + auto&& mesh_data_array = _jsonReader[MESH]; + MeshData* meshdata = new MeshData(); + auto&& mesh_data_val = mesh_data_array.at(0); - const rapidjson::Value& mesh_data_body_array = mesh_data_val[DEFAULTPART]; + auto&& mesh_data_body_array = mesh_data_val[DEFAULTPART]; - const rapidjson::Value& mesh_data_body_array_0 = mesh_data_body_array[(rapidjson::SizeType)0]; + auto&& mesh_data_body_array_0 = mesh_data_body_array.at(0); // mesh_vertex_attribute - const rapidjson::Value& mesh_vertex_attribute = mesh_data_val[ATTRIBUTES]; - meshdata->attribCount = mesh_vertex_attribute.Size(); - meshdata->attribs.resize(meshdata->attribCount); - for (rapidjson::SizeType i = 0; i < mesh_vertex_attribute.Size(); ++i) + auto&& mesh_vertex_attribute = mesh_data_val[ATTRIBUTES].get_array(); + meshdata->attribCount = mesh_vertex_attribute.count_elements(); + meshdata->attribs.reserve(meshdata->attribCount); + for (auto&& mesh_vertex_attribute_val : mesh_vertex_attribute) { - const rapidjson::Value& mesh_vertex_attribute_val = mesh_vertex_attribute[i]; - - int size = mesh_vertex_attribute_val[ATTRIBUTESIZE].GetUint(); - meshdata->attribs[i].type = parseGLDataType(mesh_vertex_attribute_val[TYPE].GetString(), size); - meshdata->attribs[i].vertexAttrib = parseGLProgramAttribute(mesh_vertex_attribute_val[ATTRIBUTE].GetString()); + int size = mesh_vertex_attribute_val[ATTRIBUTESIZE]; + meshdata->attribs.emplace_back(parseGLDataType(mesh_vertex_attribute_val[TYPE], size), + parseGLProgramAttribute(mesh_vertex_attribute_val[ATTRIBUTE])); + /*meshdata->attribs[i].type = parseGLDataType(mesh_vertex_attribute_val[TYPE], size); + meshdata->attribs[i].vertexAttrib = parseGLProgramAttribute(mesh_vertex_attribute_val[ATTRIBUTE]);*/ } + // meshdata->attribCount = meshdata->attribs.size(); // vertices - meshdata->vertexSizeInFloat = mesh_data_body_array_0[VERTEXSIZE].GetInt(); + meshdata->vertexSizeInFloat = mesh_data_body_array_0[VERTEXSIZE]; meshdata->vertex.resize(meshdata->vertexSizeInFloat); - const rapidjson::Value& mesh_data_body_vertices = mesh_data_body_array_0[VERTICES]; - for (rapidjson::SizeType i = 0; i < mesh_data_body_vertices.Size(); ++i) - meshdata->vertex[i] = mesh_data_body_vertices[i].GetFloat(); + auto mesh_data_body_vertices = mesh_data_body_array_0[VERTICES]; + int i = 0; + for (auto&& vertex_val : mesh_data_body_vertices) + meshdata->vertex[i++] = static_cast(vertex_val); // index_number - unsigned int indexnum = mesh_data_body_array_0[INDEXNUM].GetUint(); + int indexnum = mesh_data_body_array_0[INDEXNUM]; // indices IndexArray indices{}; indices.resize(indexnum); - const rapidjson::Value& indices_val_array = mesh_data_body_array_0[INDICES]; - for (rapidjson::SizeType i = 0; i < indices_val_array.Size(); ++i) - indices.at(i) = (unsigned short)indices_val_array[i].GetUint(); + auto&& indices_val_array = mesh_data_body_array_0[INDICES]; + i = 0; + for (auto&& index_val : indices_val_array.get_array()) + indices.at(i++) = static_cast(indices_val_array); auto& storedIndices = meshdata->subMeshIndices.emplace_back(std::move(indices)); meshdata->subMeshAABB.emplace_back(calculateAABB(meshdata->vertex, meshdata->getPerVertexSize(), storedIndices)); @@ -1200,54 +1194,55 @@ bool Bundle3D::loadMeshDataJson_0_1(MeshDatas& meshdatas) bool Bundle3D::loadMeshDataJson_0_2(MeshDatas& meshdatas) { - MeshData* meshdata = new MeshData(); - const rapidjson::Value& mesh_array = _jsonReader[MESH]; - const rapidjson::Value& mesh_array_0 = mesh_array[(rapidjson::SizeType)0]; + MeshData* meshdata = new MeshData(); + auto&& mesh_array = _jsonReader[MESH]; + auto&& mesh_array_0 = mesh_array.at(0); // mesh_vertex_attribute - const rapidjson::Value& mesh_vertex_attribute = mesh_array_0[ATTRIBUTES]; - meshdata->attribCount = mesh_vertex_attribute.Size(); + auto&& mesh_vertex_attribute = mesh_array_0[ATTRIBUTES]; + meshdata->attribCount = mesh_vertex_attribute.count_elements(); meshdata->attribs.resize(meshdata->attribCount); - for (rapidjson::SizeType i = 0; i < mesh_vertex_attribute.Size(); ++i) + int i = 0; + for (auto&& mesh_vertex_attribute_val : mesh_vertex_attribute.get_array()) { - const rapidjson::Value& mesh_vertex_attribute_val = mesh_vertex_attribute[i]; - - auto size = mesh_vertex_attribute_val[ATTRIBUTESIZE].GetUint(); - meshdata->attribs[i].type = parseGLDataType(mesh_vertex_attribute_val[TYPE].GetString(), size); - meshdata->attribs[i].vertexAttrib = parseGLProgramAttribute(mesh_vertex_attribute_val[ATTRIBUTE].GetString()); + auto size = static_cast(mesh_vertex_attribute_val[ATTRIBUTESIZE]); + meshdata->attribs[i].type = parseGLDataType(mesh_vertex_attribute_val[TYPE], size); + meshdata->attribs[i].vertexAttrib = parseGLProgramAttribute(mesh_vertex_attribute_val[ATTRIBUTE]); } // vertices - const rapidjson::Value& mesh_data_vertex = mesh_array_0[VERTEX]; - const rapidjson::Value& mesh_data_vertex_0 = mesh_data_vertex[(rapidjson::SizeType)0]; + auto&& mesh_data_vertex = mesh_array_0[VERTEX]; + auto&& mesh_data_vertex_0 = mesh_data_vertex.at(0); - meshdata->vertexSizeInFloat = mesh_data_vertex_0[VERTEXSIZE].GetInt(); + meshdata->vertexSizeInFloat = static_cast(mesh_data_vertex_0[VERTEXSIZE]); meshdata->vertex.resize(meshdata->vertexSizeInFloat); - const rapidjson::Value& mesh_data_body_vertices = mesh_data_vertex_0[VERTICES]; - for (rapidjson::SizeType i = 0; i < mesh_data_body_vertices.Size(); ++i) - meshdata->vertex[i] = mesh_data_body_vertices[i].GetFloat(); + auto&& mesh_data_body_vertices = mesh_data_vertex_0[VERTICES]; + i = 0; + for (auto&& vertex_val : mesh_data_body_vertices) + meshdata->vertex[i++] = static_cast(vertex_val); // submesh - const rapidjson::Value& mesh_submesh_array = mesh_array_0[SUBMESH]; - for (rapidjson::SizeType i = 0; i < mesh_submesh_array.Size(); ++i) + auto&& mesh_submesh_array = mesh_array_0[SUBMESH]; + for (auto&& mesh_submesh_val : mesh_submesh_array.get_array()) { - const rapidjson::Value& mesh_submesh_val = mesh_submesh_array[i]; // std::string id = mesh_submesh_val[ID].GetString(); // index_number - unsigned int indexnum = mesh_submesh_val[INDEXNUM].GetUint(); + auto indexnum = static_cast(mesh_submesh_val[INDEXNUM]); // indices IndexArray indices{}; indices.resize(indexnum); - const rapidjson::Value& indices_val_array = mesh_submesh_val[INDICES]; - for (rapidjson::SizeType j = 0; j < indices_val_array.Size(); ++j) - indices.at(j) = (unsigned short)indices_val_array[j].GetUint(); + auto indices_val_array = mesh_submesh_val[INDICES]; + int j = 0; + for (auto&& index_val : indices_val_array) + indices.at(j) = static_cast(indices_val_array); auto& storedIndices = meshdata->subMeshIndices.emplace_back(std::move(indices)); - meshdata->subMeshAABB.emplace_back(calculateAABB(meshdata->vertex, meshdata->getPerVertexSize(), storedIndices)); + meshdata->subMeshAABB.emplace_back( + calculateAABB(meshdata->vertex, meshdata->getPerVertexSize(), storedIndices)); } meshdatas.meshDatas.emplace_back(meshdata); return true; @@ -1255,35 +1250,33 @@ bool Bundle3D::loadMeshDataJson_0_2(MeshDatas& meshdatas) bool Bundle3D::loadSkinDataJson(SkinData* skindata) { - if (!_jsonReader.HasMember(SKIN)) + auto&& skin_data_array = _jsonReader[SKIN].get_array(); + if (skin_data_array.error()) return false; - const rapidjson::Value& skin_data_array = _jsonReader[SKIN]; - - AXASSERT(skin_data_array.IsArray(), "skin data is not an array"); - const rapidjson::Value& skin_data_array_val_0 = skin_data_array[(rapidjson::SizeType)0]; + // AXASSERT(skin_data_array.(), "skin data is not an array"); + auto&& skin_data_array_val_0 = skin_data_array.at(0); - if (!skin_data_array_val_0.HasMember(BONES)) + auto&& skin_data_bones = skin_data_array_val_0[BONES].get_array(); + if (skin_data_bones.error()) return false; - - const rapidjson::Value& skin_data_bones = skin_data_array_val_0[BONES]; - for (rapidjson::SizeType i = 0; i < skin_data_bones.Size(); ++i) + for (auto&& skin_data_bone : skin_data_bones) { - const rapidjson::Value& skin_data_bone = skin_data_bones[i]; - std::string name = skin_data_bone[NODE].GetString(); + std::string_view name = skin_data_bone[NODE]; skindata->addSkinBoneNames(name); Mat4 mat_bind_pos; - const rapidjson::Value& bind_pos = skin_data_bone[BINDSHAPE]; - for (rapidjson::SizeType j = 0; j < bind_pos.Size(); ++j) + auto&& bind_pos_array = skin_data_bone[BINDSHAPE]; + int j = 0; + for (auto&& pos : bind_pos_array.get_array()) { - mat_bind_pos.m[j] = bind_pos[j].GetFloat(); + mat_bind_pos.m[j++] = static_cast(pos); } skindata->inverseBindPoseMatrices.emplace_back(mat_bind_pos); } // set root bone information - const rapidjson::Value& skin_data_1 = skin_data_array[1]; + auto&& skin_data_1 = skin_data_array.at(1); // parent and child relationship map skindata->skinBoneOriginMatrices.resize(skindata->skinBoneNames.size()); @@ -1296,127 +1289,132 @@ bool Bundle3D::loadSkinDataBinary(SkinData* skindata) if (!seekToFirstType(BUNDLE_TYPE_MESHSKIN)) return false; - std::string boneName = _binaryReader.readString(); - - // transform - float bindShape[16]; - if (!_binaryReader.readMatrix(bindShape)) - { - AXLOGW("warning: Failed to read SkinData: bindShape matrix '{}'.", _path); - return false; - } - - // bone count - unsigned int boneNum; - if (!_binaryReader.read(&boneNum)) - { - AXLOGW("warning: Failed to read SkinData: boneNum '{}'.", _path); - return false; - } - - // Fix bug: check if the bone number is 0. - if (boneNum == 0) - return false; + std::string_view boneName = _binaryReader.read_v32(); - // bone names and bind pos - float bindpos[16]; - for (unsigned int i = 0; i < boneNum; ++i) + try { - std::string skinBoneName = _binaryReader.readString(); - skindata->skinBoneNames.emplace_back(skinBoneName); - if (!_binaryReader.readMatrix(bindpos)) + // transform + float bindShape[16]; + if (!_binaryReader.read_blob(bindShape)) { - AXLOGW("warning: Failed to load SkinData: bindpos '{}'.", _path); + AXLOGW("warning: Failed to read SkinData: bindShape matrix '{}'.", _path); return false; } - skindata->inverseBindPoseMatrices.emplace_back(bindpos); - } - - skindata->skinBoneOriginMatrices.resize(boneNum); - boneName = _binaryReader.readString(); - - // bind shape - _binaryReader.readMatrix(bindShape); - int rootIndex = skindata->getSkinBoneNameIndex(boneName); - if (rootIndex < 0) - { - skindata->addNodeBoneNames(boneName); - rootIndex = skindata->getBoneNameIndex(boneName); - skindata->nodeBoneOriginMatrices.emplace_back(bindShape); - } - else - { - skindata->skinBoneOriginMatrices[rootIndex] = bindShape; - } - - // set root bone index - skindata->rootBoneIndex = rootIndex; - - // read parent and child relationship map - float transform[16]; - unsigned int linkNum; - _binaryReader.read(&linkNum); - for (unsigned int i = 0; i < linkNum; ++i) - { - std::string id = _binaryReader.readString(); - int index = skindata->getSkinBoneNameIndex(id); + // bone count + unsigned int boneNum; + if (!_binaryReader.read_blob(boneNum)) + { + AXLOGW("warning: Failed to read SkinData: boneNum '{}'.", _path); + return false; + } - std::string parentid = _binaryReader.readString(); + // Fix bug: check if the bone number is 0. + if (boneNum == 0) + return false; - if (!_binaryReader.readMatrix(transform)) + // bone names and bind pos + float bindpos[16]; + for (unsigned int i = 0; i < boneNum; ++i) { - AXLOGW("warning: Failed to load SkinData: transform '{}'.", _path); - return false; + std::string_view skinBoneName = _binaryReader.read_v32(); + skindata->skinBoneNames.emplace_back(skinBoneName); + if (!_binaryReader.read_blob(bindpos)) + { + AXLOGW("warning: Failed to load SkinData: bindpos '{}'.", _path); + return false; + } + skindata->inverseBindPoseMatrices.emplace_back(bindpos); } - if (index < 0) + skindata->skinBoneOriginMatrices.resize(boneNum); + + boneName = _binaryReader.read_v32(); + + // bind shape + _binaryReader.read_blob(bindShape); + int rootIndex = skindata->getSkinBoneNameIndex(boneName); + if (rootIndex < 0) { - skindata->addNodeBoneNames(id); - index = skindata->getBoneNameIndex(id); - skindata->nodeBoneOriginMatrices.emplace_back(transform); + skindata->addNodeBoneNames(boneName); + rootIndex = skindata->getBoneNameIndex(boneName); + skindata->nodeBoneOriginMatrices.emplace_back(bindShape); } else { - skindata->skinBoneOriginMatrices[index] = transform; + skindata->skinBoneOriginMatrices[rootIndex] = bindShape; } - int parentIndex = skindata->getSkinBoneNameIndex(parentid); - if (parentIndex < 0) + // set root bone index + skindata->rootBoneIndex = rootIndex; + + // read parent and child relationship map + float transform[16]; + unsigned int linkNum; + _binaryReader.read_blob(linkNum); + for (unsigned int i = 0; i < linkNum; ++i) { - skindata->addNodeBoneNames(parentid); - parentIndex = skindata->getBoneNameIndex(parentid); + std::string_view id = _binaryReader.read_v32(); + int index = skindata->getSkinBoneNameIndex(id); + + std::string_view parentid = _binaryReader.read_v32(); + if (!_binaryReader.read_blob(transform)) + { + AXLOGW("warning: Failed to load SkinData: transform '{}'.", _path); + return false; + } + + if (index < 0) + { + skindata->addNodeBoneNames(id); + index = skindata->getBoneNameIndex(id); + skindata->nodeBoneOriginMatrices.emplace_back(transform); + } + else + { + skindata->skinBoneOriginMatrices[index] = transform; + } + + int parentIndex = skindata->getSkinBoneNameIndex(parentid); + if (parentIndex < 0) + { + skindata->addNodeBoneNames(parentid); + parentIndex = skindata->getBoneNameIndex(parentid); + } + + skindata->boneChild[parentIndex].emplace_back(index); } - skindata->boneChild[parentIndex].emplace_back(index); + return true; } - - return true; + catch (const std::exception& ex) + { + AXLOGW("exception occurred: {}", ex.what()); + } + return false; } bool Bundle3D::loadMaterialDataJson_0_1(MaterialDatas& materialdatas) { - if (!_jsonReader.HasMember(MATERIAL)) - return false; NMaterialData materialData; - const rapidjson::Value& material_data_array = _jsonReader[MATERIAL]; + auto&& material_data_array = _jsonReader[MATERIAL].get_array(); + if (material_data_array.error()) + return false; + + auto&& material_data_array_0 = material_data_array.at(0); - if (material_data_array.Size() > 0) + auto&& material_data_base_array = material_data_array_0[BASE].get_array(); + if (!material_data_base_array.error()) { - const rapidjson::Value& material_data_array_0 = material_data_array[(rapidjson::SizeType)0]; - if (material_data_array_0.HasMember(BASE)) - { - const rapidjson::Value& material_data_base_array = material_data_array_0[BASE]; - const rapidjson::Value& material_data_base_array_0 = material_data_base_array[(rapidjson::SizeType)0]; - NTextureData textureData; - // set texture - std::string filename = material_data_base_array_0[FILENAME].GetString(); - textureData.filename = filename.empty() ? filename : _modelPath + filename; - textureData.type = NTextureData::Usage::Diffuse; - textureData.id = ""; - materialData.textures.emplace_back(textureData); - materialdatas.materials.emplace_back(materialData); - } + auto&& material_data_base_array_0 = material_data_base_array.at(0); + NTextureData textureData; + // set texture + std::string_view filename = material_data_base_array_0[FILENAME]; + if (!filename.empty()) + (textureData.filename = _modelPath) += filename; + textureData.type = NTextureData::Usage::Diffuse; + materialData.textures.emplace_back(textureData); + materialdatas.materials.emplace_back(materialData); } return true; @@ -1424,21 +1422,20 @@ bool Bundle3D::loadMaterialDataJson_0_1(MaterialDatas& materialdatas) bool Bundle3D::loadMaterialDataJson_0_2(MaterialDatas& materialdatas) { - if (!_jsonReader.HasMember(MATERIAL)) - return false; NMaterialData materialData; - const rapidjson::Value& material_array = _jsonReader[MATERIAL]; + auto&& material_array = _jsonReader[MATERIAL]; + if (material_array.error()) + return false; - for (rapidjson::SizeType i = 0; i < material_array.Size(); ++i) + for (auto&& material_val : material_array.get_array()) { NTextureData textureData; - const rapidjson::Value& material_val = material_array[i]; // set texture - std::string filename = material_val[TEXTURES].GetString(); - textureData.filename = filename.empty() ? filename : _modelPath + filename; - textureData.type = NTextureData::Usage::Diffuse; - textureData.id = ""; + std::string_view filename = material_val[TEXTURES]; + if (!filename.empty()) + (textureData.filename = _modelPath) += filename; + textureData.type = NTextureData::Usage::Diffuse; materialData.textures.emplace_back(textureData); } materialdatas.materials.emplace_back(materialData); @@ -1468,22 +1465,22 @@ bool Bundle3D::loadAnimationDataJson(std::string_view id, Animation3DData* anima else anim = ANIMATIONS; - if (!_jsonReader.HasMember(anim.c_str())) - return false; - int the_index = -1; - const rapidjson::Value& animation_data_array = _jsonReader[anim.c_str()]; - - if (animation_data_array.Size() == 0) + int the_index = -1; + auto&& animation_data_array = _jsonReader[anim]; + if (animation_data_array.error()) return false; if (!id.empty()) { - for (rapidjson::SizeType i = 0; i < animation_data_array.Size(); ++i) + int i = 0; + for (auto&& animation_data : animation_data_array.get_array()) { - if (animation_data_array[i][ID].GetString() == id) + std::string_view keyid = animation_data[ID]; + if (keyid == id) { the_index = static_cast(i); } + ++i; } if (the_index < 0) return false; @@ -1493,55 +1490,63 @@ bool Bundle3D::loadAnimationDataJson(std::string_view id, Animation3DData* anima the_index = 0; } - const rapidjson::Value& animation_data_array_val_0 = animation_data_array[(rapidjson::SizeType)the_index]; + auto&& animation_data_array_val_0 = animation_data_array.at(the_index); - animationdata->_totalTime = animation_data_array_val_0[LENGTH].GetFloat(); + animationdata->_totalTime = static_cast(animation_data_array_val_0[LENGTH]); - const rapidjson::Value& bones = animation_data_array_val_0[BONES]; - for (rapidjson::SizeType i = 0; i < bones.Size(); ++i) + auto&& bones = animation_data_array_val_0[BONES]; + for (auto&& bone : bones.get_array()) { - const rapidjson::Value& bone = bones[i]; - std::string bone_name = bone[BONEID].GetString(); + std::string_view bone_name = bone[BONEID]; + auto&& bone_keyframes = bone[KEYFRAMES]; + if (bone_keyframes.error()) + continue; + size_t keyframe_size = bone_keyframes.count_elements(); + animationdata->_rotationKeys[bone_name].reserve(keyframe_size); + animationdata->_scaleKeys[bone_name].reserve(keyframe_size); + animationdata->_translationKeys[bone_name].reserve(keyframe_size); - if (bone.HasMember(KEYFRAMES)) + for (auto&& bone_keyframe : bone_keyframes) { - const rapidjson::Value& bone_keyframes = bone[KEYFRAMES]; - rapidjson::SizeType keyframe_size = bone_keyframes.Size(); - animationdata->_rotationKeys[bone_name].reserve(keyframe_size); - animationdata->_scaleKeys[bone_name].reserve(keyframe_size); - animationdata->_translationKeys[bone_name].reserve(keyframe_size); - - for (rapidjson::SizeType j = 0; j < keyframe_size; ++j) + float keytime = static_cast(bone_keyframe[KEYTIME]); + auto&& bone_keyframe_translation = bone_keyframe[TRANSLATION]; + if (!bone_keyframe_translation.error()) { - const rapidjson::Value& bone_keyframe = bone_keyframes[j]; - - if (bone_keyframe.HasMember(TRANSLATION)) + Vec3 val; + int i = 0; + for (auto&& axis_val : bone_keyframe_translation) { - const rapidjson::Value& bone_keyframe_translation = bone_keyframe[TRANSLATION]; - float keytime = bone_keyframe[KEYTIME].GetFloat(); - Vec3 val(bone_keyframe_translation[(rapidjson::SizeType)0].GetFloat(), - bone_keyframe_translation[1].GetFloat(), bone_keyframe_translation[2].GetFloat()); - animationdata->_translationKeys[bone_name].emplace_back(Animation3DData::Vec3Key(keytime, val)); + val.comps[i++] = static_cast(axis_val); + if (i >= 3) + break; } - - if (bone_keyframe.HasMember(ROTATION)) + animationdata->_translationKeys[bone_name].emplace_back(Animation3DData::Vec3Key(keytime, Vec3{val})); + } + auto&& bone_keyframe_rotation = bone_keyframe[ROTATION]; + if (!bone_keyframe_rotation.error()) + { + Quaternion val; + int i = 0; + for (auto&& axis_val : bone_keyframe_rotation) { - const rapidjson::Value& bone_keyframe_rotation = bone_keyframe[ROTATION]; - float keytime = bone_keyframe[KEYTIME].GetFloat(); - Quaternion val = Quaternion( - bone_keyframe_rotation[(rapidjson::SizeType)0].GetFloat(), bone_keyframe_rotation[1].GetFloat(), - bone_keyframe_rotation[2].GetFloat(), bone_keyframe_rotation[3].GetFloat()); - animationdata->_rotationKeys[bone_name].emplace_back(Animation3DData::QuatKey(keytime, val)); + val.comps[i++] = static_cast(axis_val); + if (i >= 4) + break; } - - if (bone_keyframe.HasMember(SCALE)) + animationdata->_rotationKeys[bone_name].emplace_back(Animation3DData::QuatKey(keytime, val)); + } + auto&& bone_keyframe_scale = bone_keyframe[SCALE]; + if (!bone_keyframe_scale.error()) + { + Vec3 val; + int i = 0; + for (auto&& axis_val : bone_keyframe_scale) { - const rapidjson::Value& bone_keyframe_scale = bone_keyframe[SCALE]; - float keytime = bone_keyframe[KEYTIME].GetFloat(); - Vec3 val(bone_keyframe_scale[(rapidjson::SizeType)0].GetFloat(), bone_keyframe_scale[1].GetFloat(), - bone_keyframe_scale[2].GetFloat()); - animationdata->_scaleKeys[bone_name].emplace_back(Animation3DData::Vec3Key(keytime, val)); + val.comps[i++] = static_cast(axis_val); + if (i >= 3) + break; } + animationdata->_scaleKeys[bone_name].emplace_back(Animation3DData::Vec3Key(keytime, val)); } } } @@ -1568,147 +1573,154 @@ bool Bundle3D::loadAnimationDataBinary(std::string_view id, Animation3DData* ani return false; } - unsigned int animNum = 1; - if (_version == "0.3" || _version == "0.4") + try { - if (!_binaryReader.read(&animNum)) + unsigned int animNum = 1; + if (_version == "0.3" || _version == "0.4") { - AXLOGW("warning: Failed to read AnimationData: animNum '{}'.", _path); - return false; + if (!_binaryReader.read_blob(animNum)) + { + AXLOGW("warning: Failed to read AnimationData: animNum '{}'.", _path); + return false; + } } - } - - bool has_found = false; - for (unsigned int k = 0; k < animNum; ++k) - { - animationdata->resetData(); - std::string animId = _binaryReader.readString(); - if (!_binaryReader.read(&animationdata->_totalTime)) + bool has_found = false; + for (unsigned int k = 0; k < animNum; ++k) { - AXLOGW("warning: Failed to read AnimationData: totalTime '{}'.", _path); - return false; - } + animationdata->resetData(); + std::string_view animId = _binaryReader.read_v32(); - unsigned int nodeAnimationNum; - if (!_binaryReader.read(&nodeAnimationNum)) - { - AXLOGW("warning: Failed to read AnimationData: animNum '{}'.", _path); - return false; - } - for (unsigned int i = 0; i < nodeAnimationNum; ++i) - { - std::string boneName = _binaryReader.readString(); - unsigned int keyframeNum; - if (!_binaryReader.read(&keyframeNum)) + if (!_binaryReader.read_blob(animationdata->_totalTime)) { - AXLOGW("warning: Failed to read AnimationData: keyframeNum '{}'.", _path); + AXLOGW("warning: Failed to read AnimationData: totalTime '{}'.", _path); return false; } - animationdata->_rotationKeys[boneName].reserve(keyframeNum); - animationdata->_scaleKeys[boneName].reserve(keyframeNum); - animationdata->_translationKeys[boneName].reserve(keyframeNum); - - for (unsigned int j = 0; j < keyframeNum; ++j) + unsigned int nodeAnimationNum; + if (!_binaryReader.read_blob(nodeAnimationNum)) + { + AXLOGW("warning: Failed to read AnimationData: animNum '{}'.", _path); + return false; + } + for (unsigned int i = 0; i < nodeAnimationNum; ++i) { - float keytime; - if (!_binaryReader.read(&keytime)) + std::string_view boneName = _binaryReader.read_v32(); + unsigned int keyframeNum; + if (!_binaryReader.read_blob(keyframeNum)) { - AXLOGW("warning: Failed to read AnimationData: keytime '{}'.", _path); + AXLOGW("warning: Failed to read AnimationData: keyframeNum '{}'.", _path); return false; } - // transform flag - unsigned char transformFlag(0); - if (_version != "0.1" && _version != "0.2" && _version != "0.3") + animationdata->_rotationKeys[boneName].reserve(keyframeNum); + animationdata->_scaleKeys[boneName].reserve(keyframeNum); + animationdata->_translationKeys[boneName].reserve(keyframeNum); + + for (unsigned int j = 0; j < keyframeNum; ++j) { - if (!_binaryReader.read(&transformFlag)) + float keytime; + if (!_binaryReader.read_blob(keytime)) { - AXLOGW("warning: Failed to read AnimationData: transformFlag '{}'.", _path); + AXLOGW("warning: Failed to read AnimationData: keytime '{}'.", _path); return false; } - } - // rotation - bool hasRotate = true; - if (_version != "0.1" && _version != "0.2" && _version != "0.3") - hasRotate = transformFlag & 0x01; + // transform flag + unsigned char transformFlag(0); + if (_version != "0.1" && _version != "0.2" && _version != "0.3") + { + if (!_binaryReader.read_blob(transformFlag)) + { + AXLOGW("warning: Failed to read AnimationData: transformFlag '{}'.", _path); + return false; + } + } + + // rotation + bool hasRotate = true; + if (_version != "0.1" && _version != "0.2" && _version != "0.3") + hasRotate = transformFlag & 0x01; - if (hasRotate) - { - Quaternion rotate; - if (_binaryReader.read(&rotate, 4, 4) != 4) + if (hasRotate) { - AXLOGW("warning: Failed to read AnimationData: rotate '{}'.", _path); - return false; + Quaternion rotate; + if (_binaryReader.read_blob(rotate.comps) != 4) + { + AXLOGW("warning: Failed to read AnimationData: rotate '{}'.", _path); + return false; + } + animationdata->_rotationKeys[boneName].emplace_back(Animation3DData::QuatKey(keytime, rotate)); } - animationdata->_rotationKeys[boneName].emplace_back(Animation3DData::QuatKey(keytime, rotate)); - } - // scale - bool hasScale = true; - if (_version != "0.1" && _version != "0.2" && _version != "0.3") - hasScale = (transformFlag >> 1) & 0x01; + // scale + bool hasScale = true; + if (_version != "0.1" && _version != "0.2" && _version != "0.3") + hasScale = (transformFlag >> 1) & 0x01; - if (hasScale) - { - Vec3 scale; - if (_binaryReader.read(&scale, 4, 3) != 3) + if (hasScale) { - AXLOGW("warning: Failed to read AnimationData: scale '{}'.", _path); - return false; + Vec3 scale; + if (_binaryReader.read_blob(scale.comps) != 3) + { + AXLOGW("warning: Failed to read AnimationData: scale '{}'.", _path); + return false; + } + animationdata->_scaleKeys[boneName].emplace_back(Animation3DData::Vec3Key(keytime, scale)); } - animationdata->_scaleKeys[boneName].emplace_back(Animation3DData::Vec3Key(keytime, scale)); - } - // translation - bool hasTranslation = true; - if (_version != "0.1" && _version != "0.2" && _version != "0.3") - hasTranslation = (transformFlag >> 2) & 0x01; + // translation + bool hasTranslation = true; + if (_version != "0.1" && _version != "0.2" && _version != "0.3") + hasTranslation = (transformFlag >> 2) & 0x01; - if (hasTranslation) - { - Vec3 position; - if (_binaryReader.read(&position, 4, 3) != 3) + if (hasTranslation) { - AXLOGW("warning: Failed to read AnimationData: position '{}'.", _path); - return false; + Vec3 position; + if (_binaryReader.read_blob(position.comps) != 3) + { + AXLOGW("warning: Failed to read AnimationData: position '{}'.", _path); + return false; + } + animationdata->_translationKeys[boneName].emplace_back( + Animation3DData::Vec3Key(keytime, position)); } - animationdata->_translationKeys[boneName].emplace_back(Animation3DData::Vec3Key(keytime, position)); } } + if (id == animId || id.empty()) + { + has_found = true; + break; + } } - if (id == animId || id.empty()) + if (!has_found) { - has_found = true; - break; + animationdata->resetData(); + return false; } + return true; } - if (!has_found) + catch (const std::exception& ex) { - animationdata->resetData(); - return false; + AXLOGW("exception occurred: {}", ex.what()); } - return true; + return false; } bool Bundle3D::loadNodesJson(NodeDatas& nodedatas) { - if (!_jsonReader.HasMember(NODES)) - return false; - const rapidjson::Value& nodes = _jsonReader[NODES]; - if (!nodes.IsArray()) + auto&& nodes = _jsonReader[NODES]; + if (nodes.error()) return false; // traverse the nodes again - for (rapidjson::SizeType i = 0; i < nodes.Size(); ++i) + int node_count = nodes.count_elements(); + for (auto&& jnode : nodes.get_array()) { - const rapidjson::Value& jnode = nodes[i]; - std::string id = jnode[ID].GetString(); - NodeData* nodedata = parseNodesRecursivelyJson(jnode, nodes.Size() == 1); - bool isSkeleton = jnode[SKELETON].GetBool(); + NodeData* nodedata = parseNodesRecursivelyJson(jnode, node_count == 1); + + bool isSkeleton = jnode[SKELETON]; if (isSkeleton) nodedatas.skeleton.emplace_back(nodedata); else @@ -1716,19 +1728,20 @@ bool Bundle3D::loadNodesJson(NodeDatas& nodedatas) } return true; } -NodeData* Bundle3D::parseNodesRecursivelyJson(const rapidjson::Value& jvalue, bool singleSprite) +NodeData* Bundle3D::parseNodesRecursivelyJson(simdjson::simdjson_result jvalue, + bool singleSprite) { NodeData* nodedata = new NodeData(); // id - nodedata->id = jvalue[ID].GetString(); + nodedata->id = static_cast(jvalue[ID]); // transform Mat4 transform; - const rapidjson::Value& jtransform = jvalue[TRANSFORM]; - - for (rapidjson::SizeType j = 0; j < jtransform.Size(); ++j) + auto&& jtransform = jvalue[TRANSFORM]; + int j = 0; + for (auto&& val : jtransform.get_array()) { - transform.m[j] = jtransform[j].GetFloat(); + transform.m[j++] = static_cast(val); } nodedata->transform = transform; @@ -1736,16 +1749,14 @@ NodeData* Bundle3D::parseNodesRecursivelyJson(const rapidjson::Value& jvalue, bo bool isSkin = false; // parts - if (jvalue.HasMember(PARTS)) + auto&& parts = jvalue[PARTS]; + if (!parts.error()) { - const rapidjson::Value& parts = jvalue[PARTS]; - - for (rapidjson::SizeType i = 0; i < parts.Size(); ++i) + for (auto&& part : parts.get_array()) { - auto modelnodedata = new ModelData(); - const rapidjson::Value& part = parts[i]; - modelnodedata->subMeshId = part[MESHPARTID].GetString(); - modelnodedata->materialId = part[MATERIALID].GetString(); + auto modelnodedata = new ModelData(); + modelnodedata->subMeshId = static_cast(part[MESHPARTID]); + modelnodedata->materialId = static_cast(part[MATERIALID]); if (modelnodedata->subMeshId == "" || modelnodedata->materialId == "") { @@ -1754,17 +1765,15 @@ NodeData* Bundle3D::parseNodesRecursivelyJson(const rapidjson::Value& jvalue, bo AX_SAFE_DELETE(nodedata); return nullptr; } - - if (part.HasMember(BONES)) + auto&& bones = part[BONES]; + if (!bones.error()) { - const rapidjson::Value& bones = part[BONES]; - - for (rapidjson::SizeType j = 0; j < bones.Size(); ++j) + unsigned int bones_count = 0; + for (auto&& bone : bones.get_array()) { - const rapidjson::Value& bone = bones[j]; - // node - if (!bone.HasMember(NODE)) + auto&& node_name = bone[NODE]; + if (node_name.error()) { AXLOGW("warning: Bone node ID missing"); AX_SAFE_DELETE(modelnodedata); @@ -1772,21 +1781,22 @@ NodeData* Bundle3D::parseNodesRecursivelyJson(const rapidjson::Value& jvalue, bo return nullptr; } - modelnodedata->bones.emplace_back(bone[NODE].GetString()); + ++bones_count; + modelnodedata->bones.emplace_back(static_cast(node_name)); Mat4 invbindpos; - const rapidjson::Value& jinvbindpos = bone[TRANSFORM]; - - for (rapidjson::SizeType k = 0; k < jinvbindpos.Size(); ++k) + auto&& jinvbindpos = bone[TRANSFORM]; + int k = 0; + for (auto&& val : jinvbindpos) { - invbindpos.m[k] = jinvbindpos[k].GetFloat(); + invbindpos.m[k] = static_cast(val); } // invbindpos.inverse(); modelnodedata->invBindPose.emplace_back(invbindpos); } - if (bones.Size() > 0) + if (bones_count) isSkin = true; } nodedata->modelNodeDatas.emplace_back(modelnodedata); @@ -1811,13 +1821,11 @@ NodeData* Bundle3D::parseNodesRecursivelyJson(const rapidjson::Value& jvalue, bo nodedata->transform = transform; } - if (jvalue.HasMember(CHILDREN)) + auto&& children = jvalue[CHILDREN].get_array(); + if (!children.error()) { - const rapidjson::Value& children = jvalue[CHILDREN]; - for (rapidjson::SizeType i = 0; i < children.Size(); ++i) + for (auto&& child : children) { - const rapidjson::Value& child = children[i]; - NodeData* tempdata = parseNodesRecursivelyJson(child, singleSprite); nodedata->children.emplace_back(tempdata); } @@ -1831,14 +1839,14 @@ bool Bundle3D::loadNodesBinary(NodeDatas& nodedatas) return false; unsigned int nodeSize = 0; - if (_binaryReader.read(&nodeSize, 4, 1) != 1) + if (_binaryReader.read_blob(nodeSize) != 1) { AXLOGW("warning: Failed to read nodes"); return false; } // traverse the nodes again - for (rapidjson::SizeType i = 0; i < nodeSize; ++i) + for (unsigned int i = 0; i < nodeSize; ++i) { bool skeleton = false; NodeData* nodedata = parseNodesRecursivelyBinary(skeleton, nodeSize == 1); @@ -1852,150 +1860,158 @@ bool Bundle3D::loadNodesBinary(NodeDatas& nodedatas) } NodeData* Bundle3D::parseNodesRecursivelyBinary(bool& skeleton, bool singleSprite) { - // id - std::string id = _binaryReader.readString(); - // is skeleton - bool skeleton_; - if (_binaryReader.read(&skeleton_, 1, 1) != 1) + try { - AXLOGW("warning: Failed to read is skeleton"); - return nullptr; - } - if (skeleton_) - skeleton = true; + // id + std::string_view id = _binaryReader.read_v32(); + // is skeleton + bool skeleton_; + if (_binaryReader.read_blob(skeleton_) != 1) + { + AXLOGW("warning: Failed to read is skeleton"); + return nullptr; + } + if (skeleton_) + skeleton = true; - // transform - Mat4 transform; - if (!_binaryReader.readMatrix(transform.m)) - { - AXLOGW("warning: Failed to read transform matrix"); - return nullptr; - } - // parts - unsigned int partsSize = 0; - if (_binaryReader.read(&partsSize, 4, 1) != 1) - { - AXLOGW("warning: Failed to read meshdata: attribCount '{}'.", _path); - return nullptr; - } + // transform + Mat4 transform; + if (!_binaryReader.read_blob(transform.m)) + { + AXLOGW("warning: Failed to read transform matrix"); + return nullptr; + } + // parts + unsigned int partsSize = 0; + if (_binaryReader.read_blob(partsSize) != 1) + { + AXLOGW("warning: Failed to read meshdata: attribCount '{}'.", _path); + return nullptr; + } - NodeData* nodedata = new NodeData(); - nodedata->id = id; + NodeData* nodedata = new NodeData(); + nodedata->id = id; - bool isSkin = false; + bool isSkin = false; - if (partsSize > 0) - { - for (unsigned int i = 0; i < partsSize; ++i) + if (partsSize > 0) { - auto modelnodedata = new ModelData(); - modelnodedata->subMeshId = _binaryReader.readString(); - modelnodedata->materialId = _binaryReader.readString(); - - if (modelnodedata->subMeshId.empty() || modelnodedata->materialId.empty()) + for (unsigned int i = 0; i < partsSize; ++i) { - AXLOGW("Node {} part is missing meshPartId or materialId", nodedata->id); - AX_SAFE_DELETE(modelnodedata); - AX_SAFE_DELETE(nodedata); - return nullptr; - } + auto modelnodedata = new ModelData(); + modelnodedata->subMeshId = _binaryReader.read_v32(); + modelnodedata->materialId = _binaryReader.read_v32(); - // read bone - unsigned int bonesSize = 0; - if (_binaryReader.read(&bonesSize, 4, 1) != 1) - { - AXLOGW("warning: Failed to read meshdata: attribCount '{}'.", _path); - AX_SAFE_DELETE(modelnodedata); - AX_SAFE_DELETE(nodedata); - return nullptr; - } + if (modelnodedata->subMeshId.empty() || modelnodedata->materialId.empty()) + { + AXLOGW("Node {} part is missing meshPartId or materialId", nodedata->id); + AX_SAFE_DELETE(modelnodedata); + AX_SAFE_DELETE(nodedata); + return nullptr; + } - if (bonesSize > 0) - { - for (unsigned int j = 0; j < bonesSize; ++j) + // read bone + unsigned int bonesSize = 0; + if (_binaryReader.read_blob(bonesSize) != 1) { - std::string name = _binaryReader.readString(); - modelnodedata->bones.emplace_back(name); + AXLOGW("warning: Failed to read meshdata: attribCount '{}'.", _path); + AX_SAFE_DELETE(modelnodedata); + AX_SAFE_DELETE(nodedata); + return nullptr; + } - Mat4 invbindpos; - if (!_binaryReader.readMatrix(invbindpos.m)) + if (bonesSize > 0) + { + for (unsigned int j = 0; j < bonesSize; ++j) { - AX_SAFE_DELETE(modelnodedata); - AX_SAFE_DELETE(nodedata); - return nullptr; + std::string_view name = _binaryReader.read_v32(); + modelnodedata->bones.emplace_back(name); + + Mat4 invbindpos; + if (!_binaryReader.read_blob(invbindpos.m)) + { + AX_SAFE_DELETE(modelnodedata); + AX_SAFE_DELETE(nodedata); + return nullptr; + } + + modelnodedata->invBindPose.emplace_back(invbindpos); } - - modelnodedata->invBindPose.emplace_back(invbindpos); + isSkin = true; } - isSkin = true; - } - unsigned int uvMapping = 0; - if (_binaryReader.read(&uvMapping, 4, 1) != 1) - { - AXLOGW("warning: Failed to read nodedata: uvMapping '{}'.", _path); - AX_SAFE_DELETE(modelnodedata); - AX_SAFE_DELETE(nodedata); - return nullptr; - } - for (unsigned int j = 0; j < uvMapping; ++j) - { - unsigned int textureIndexSize = 0; - if (_binaryReader.read(&textureIndexSize, 4, 1) != 1) + unsigned int uvMapping = 0; + if (_binaryReader.read_blob(uvMapping) != 1) { - AXLOGW("warning: Failed to read meshdata: attribCount '{}'.", _path); + AXLOGW("warning: Failed to read nodedata: uvMapping '{}'.", _path); AX_SAFE_DELETE(modelnodedata); AX_SAFE_DELETE(nodedata); return nullptr; } - for (unsigned int k = 0; k < textureIndexSize; ++k) + for (unsigned int j = 0; j < uvMapping; ++j) { - unsigned int index = 0; - if (_binaryReader.read(&index, 4, 1) != 1) + unsigned int textureIndexSize = 0; + if (_binaryReader.read_blob(textureIndexSize) != 1) { + AXLOGW("warning: Failed to read meshdata: attribCount '{}'.", _path); AX_SAFE_DELETE(modelnodedata); AX_SAFE_DELETE(nodedata); return nullptr; } + for (unsigned int k = 0; k < textureIndexSize; ++k) + { + unsigned int index = 0; + if (_binaryReader.read_blob(index) != 1) + { + AX_SAFE_DELETE(modelnodedata); + AX_SAFE_DELETE(nodedata); + return nullptr; + } + } } + nodedata->modelNodeDatas.emplace_back(modelnodedata); } - nodedata->modelNodeDatas.emplace_back(modelnodedata); } - } - // set transform - if (_version == "0.1" || _version == "0.2" || _version == "0.3" || _version == "0.4" || _version == "0.5" || - _version == "0.6") - { - if (isSkin || singleSprite) + // set transform + if (_version == "0.1" || _version == "0.2" || _version == "0.3" || _version == "0.4" || _version == "0.5" || + _version == "0.6") { - nodedata->transform = Mat4::IDENTITY; + if (isSkin || singleSprite) + { + nodedata->transform = Mat4::IDENTITY; + } + else + { + nodedata->transform = transform; + } } else { nodedata->transform = transform; } - } - else - { - nodedata->transform = transform; - } - unsigned int childrenSize = 0; - if (_binaryReader.read(&childrenSize, 4, 1) != 1) - { - AXLOGW("warning: Failed to read meshdata: attribCount '{}'.", _path); - AX_SAFE_DELETE(nodedata); - return nullptr; - } - if (childrenSize > 0) - { - for (unsigned int i = 0; i < childrenSize; ++i) + unsigned int childrenSize = 0; + if (_binaryReader.read_blob(childrenSize) != 1) { - NodeData* tempdata = parseNodesRecursivelyBinary(skeleton, singleSprite); - nodedata->children.emplace_back(tempdata); + AXLOGW("warning: Failed to read meshdata: attribCount '{}'.", _path); + AX_SAFE_DELETE(nodedata); + return nullptr; } + if (childrenSize > 0) + { + for (unsigned int i = 0; i < childrenSize; ++i) + { + NodeData* tempdata = parseNodesRecursivelyBinary(skeleton, singleSprite); + nodedata->children.emplace_back(tempdata); + } + } + return nodedata; } - return nodedata; + catch (const std::exception& ex) + { + AXLOGW("exception occurred: {}", ex.what()); + } + return nullptr; } backend::VertexFormat Bundle3D::parseGLDataType(std::string_view str, int size) @@ -2304,8 +2320,9 @@ std::vector Bundle3D::getTrianglesList(std::string_view path) for (const auto& indices : iter->subMeshIndices) { indices.for_each([&](unsigned int ind) { - trianglesList.emplace_back(Vec3(iter->vertex[ind * preVertexSize], iter->vertex[ind * preVertexSize + 1], - iter->vertex[ind * preVertexSize + 2])); + trianglesList.emplace_back(Vec3(iter->vertex[ind * preVertexSize], + iter->vertex[ind * preVertexSize + 1], + iter->vertex[ind * preVertexSize + 2])); }); } } @@ -2321,14 +2338,12 @@ Bundle3D::~Bundle3D() clear(); } -ax::AABB Bundle3D::calculateAABB(const std::vector& vertex, - int stride, - const IndexArray& indices) +ax::AABB Bundle3D::calculateAABB(const std::vector& vertex, int stride, const IndexArray& indices) { AABB aabb; stride /= 4; - indices.for_each ([&](uint32_t i) { + indices.for_each([&](uint32_t i) { Vec3 point(vertex[i * stride], vertex[i * stride + 1], vertex[i * stride + 2]); aabb.updateMinMax(&point, 1); }); @@ -2336,4 +2351,4 @@ ax::AABB Bundle3D::calculateAABB(const std::vector& vertex, return aabb; } -} +} // namespace ax diff --git a/core/3d/Bundle3D.h b/core/3d/Bundle3D.h index a972c6831aff..aa18308973c6 100644 --- a/core/3d/Bundle3D.h +++ b/core/3d/Bundle3D.h @@ -29,10 +29,8 @@ #include "base/Data.h" #include "3d/Bundle3DData.h" -#include "3d/BundleReader.h" -#include "rapidjson/rapidjson.h" -#include "rapidjson/document.h" - +#include "base/json.h" +#include "yasio/ibstream.hpp" namespace ax { @@ -151,7 +149,7 @@ class AX_DLL Bundle3D * load nodes of json */ bool loadNodesJson(NodeDatas& nodedatas); - NodeData* parseNodesRecursivelyJson(const rapidjson::Value& jvalue, bool singleSprite); + NodeData* parseNodesRecursivelyJson(simdjson::simdjson_result jvalue, bool singleSprite); /** * load nodes of binary @@ -190,12 +188,13 @@ class AX_DLL Bundle3D std::string _version; // the c3b or c3t version // for json reading - std::string _jsonBuffer; - rapidjson::Document _jsonReader; + simdjson::PaddedString _jsonBuffer; + simdjson::ondemand::parser _jsonParser; + simdjson::ondemand::document _jsonReader; // for binary reading Data _binaryBuffer; - BundleReader _binaryReader; + yasio::fast_ibstream_view _binaryReader; unsigned int _referenceCount; Reference* _references; bool _isBinary; diff --git a/core/3d/Bundle3DData.h b/core/3d/Bundle3DData.h index e54744ecd2cd..fe6633d189f4 100644 --- a/core/3d/Bundle3DData.h +++ b/core/3d/Bundle3DData.h @@ -39,8 +39,7 @@ #include #include "3d/3DProgramInfo.h" - -#include "yasio/byte_buffer.hpp" +#include "base/axstd.h" namespace ax { @@ -319,7 +318,7 @@ struct MeshData std::vector subMeshIds; // subMesh Names (since 3.3) std::vector subMeshAABB; int numIndex; - std::vector attribs; + axstd::pod_vector attribs; int attribCount; public: @@ -539,9 +538,9 @@ struct Animation3DData }; public: - std::map> _translationKeys; - std::map> _rotationKeys; - std::map> _scaleKeys; + hlookup::string_map> _translationKeys; + hlookup::string_map> _rotationKeys; + hlookup::string_map> _scaleKeys; float _totalTime; diff --git a/core/3d/BundleReader.cpp b/core/3d/BundleReader.cpp deleted file mode 100644 index 31e7102deea0..000000000000 --- a/core/3d/BundleReader.cpp +++ /dev/null @@ -1,192 +0,0 @@ -/**************************************************************************** - Copyright (c) 2014-2016 Chukong Technologies Inc. - Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - Copyright (c) 2019-present Axmol Engine contributors (see AUTHORS.md). - - https://axmol.dev/ - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - ****************************************************************************/ - -#include "3d/BundleReader.h" -#include "platform/FileUtils.h" - -namespace ax -{ - -BundleReader::BundleReader() -{ - _buffer = nullptr; - _position = 0; - _length = 0; -}; - -BundleReader::~BundleReader(){ - -}; - -void BundleReader::init(char* buffer, ssize_t length) -{ - _position = 0; - _buffer = buffer; - _length = length; -} - -ssize_t BundleReader::read(void* ptr, ssize_t size, ssize_t count) -{ - if (!_buffer || eof()) - { - AXLOGW("warning: bundle reader out of range"); - return 0; - } - - ssize_t validCount; - ssize_t validLength = _length - _position; - ssize_t needLength = size * count; - char* ptr1 = (char*)ptr; - if (validLength < needLength) - { - validCount = validLength / size; - ssize_t readLength = size * validCount; - memcpy(ptr1, (char*)_buffer + _position, readLength); - ptr1 += readLength; - _position += readLength; - readLength = validLength - readLength; - if (readLength > 0) - { - memcpy(ptr1, (char*)_buffer + _position, readLength); - _position += readLength; - validCount += 1; - } - AXLOGW("warning: bundle reader out of range"); - } - else - { - memcpy(ptr1, (char*)_buffer + _position, needLength); - _position += needLength; - validCount = count; - } - - return validCount; -} - -char* BundleReader::readLine(int num, char* line) -{ - if (!_buffer) - return nullptr; - - char* buffer = (char*)_buffer + _position; - char* p = line; - char c; - ssize_t readNum = 0; - while ((c = *buffer) != 10 && readNum < (ssize_t)num && _position < _length) - { - *p = c; - p++; - buffer++; - _position++; - readNum++; - } - *p = '\0'; - - return line; -} - -bool BundleReader::eof() -{ - if (!_buffer) - return true; - - return ((ssize_t)tell()) >= length(); -} - -ssize_t BundleReader::length() -{ - return _length; -} - -ssize_t BundleReader::tell() -{ - if (!_buffer) - return -1; - return _position; -} - -bool BundleReader::seek(int32_t offset, int origin) -{ - if (!_buffer) - return false; - - if (origin == SEEK_CUR) - { - _position += offset; - } - else if (origin == SEEK_SET) - { - _position = offset; - } - else if (origin == SEEK_END) - { - _position = _length + offset; - } - else - return false; - - return true; -} - -bool BundleReader::rewind() -{ - if (_buffer != nullptr) - { - _position = 0; - return true; - } - return false; -} - -std::string BundleReader::readString() -{ - unsigned int length; - if (read(&length, 4, 1) != 1) - { - return std::string(); - } - - std::string str; - - ssize_t validLength = _length - _position; - if (length > 0 && static_cast(length) <= validLength) - { - str.resize(length); - if (read(&str[0], 1, length) != length) - { - return std::string(); - } - } - - return str; -} - -bool BundleReader::readMatrix(float* m) -{ - return (read(m, sizeof(float), 16) == 16); -} - -} diff --git a/core/3d/BundleReader.h b/core/3d/BundleReader.h deleted file mode 100644 index 4d247f2f1da3..000000000000 --- a/core/3d/BundleReader.h +++ /dev/null @@ -1,225 +0,0 @@ -/**************************************************************************** - Copyright (c) 2014-2016 Chukong Technologies Inc. - Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - Copyright (c) 2019-present Axmol Engine contributors (see AUTHORS.md). - - https://axmol.dev/ - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - ****************************************************************************/ - -#ifndef __AX_BUNDLE_READER_H__ -#define __AX_BUNDLE_READER_H__ - -#include -#include - -#include "base/Object.h" -#include "platform/PlatformMacros.h" -#include "base/Logging.h" - -namespace ax -{ - -/** - * @addtogroup _3d - * @{ - */ - -/** - * @brief BundleReader is an interface for reading sequence of bytes. - * @js NA - * @lua NA - */ -class BundleReader : public ax::Object -{ -public: - /** - * Constructor - */ - BundleReader(); - - /** - * Destructor - */ - ~BundleReader(); - - /** - * initialise - * @param buffer The data buffer pointer - * @param length The data buffer size - */ - void init(char* buffer, ssize_t length); - - /** - * Reads an array of elements. - * - * @param ptr The pointer to the memory to copy into. - * The available size should be at least bytes. - * @param size The size of each element to be read, in bytes. - * @param count The number of elements to read. - * - * @return The number of elements read. - */ - ssize_t read(void* ptr, ssize_t size, ssize_t count); - - /** - * Reads a line from the buffer. - */ - char* readLine(int num, char* line); - - /** - * Returns true if the end of the buffer has been reached. - */ - bool eof(); - - /** - * Returns the length of the buffer in bytes. - */ - ssize_t length(); - - /** - * Returns the position of the file pointer. - */ - ssize_t tell(); - - /** - * Sets the position of the file pointer. - */ - bool seek(int32_t offset, int origin); - - /** - * Sets the file pointer at the start of the file. - */ - bool rewind(); - - /** - * read binary typed value. - */ - template - bool read(T* ptr); - template - bool readArray(unsigned int* length, std::vector* values); - - /** - * first read length, then read string text - */ - std::string readString(); - - /** - * Read the matrix. - * @note the matrix type must be the 4*4 float matrix - */ - bool readMatrix(float* m); - -private: - ssize_t _position; - ssize_t _length; - char* _buffer; -}; - -/// @cond - -/** - * template read routines - */ -template -inline bool BundleReader::read(T* ptr) -{ - return (read(ptr, sizeof(T), 1) == 1); -} - -/** - * template function to read array of value. - */ -template -inline bool BundleReader::readArray(unsigned int* length, std::vector* values) -{ - if (!read(length)) - { - return false; - } - - if (*length > 0 && values) - { - values->resize(*length); - if (read(&(*values)[0], sizeof(T), *length) != *length) - { - return false; - } - } - return true; -} - -/** - * specialization for char - */ -template <> -inline bool BundleReader::read(char* ptr) -{ - if (read(ptr, sizeof(char), 1) == 1) - { - return true; - } - else - { - *ptr = -1; - return false; - } -} - -/** - * specialization for std::string - */ -template <> -inline bool BundleReader::read(std::string* /*ptr*/) -{ - AXLOGW("can not read std::string, use readString() instead"); - return false; -} - -/** - * template function to read array of value. - */ -template <> -inline bool BundleReader::readArray(unsigned int* length, std::vector* values) -{ - if (!read(length)) - { - return false; - } - values->clear(); - if (*length > 0 && values) - { - for (int i = 0; i < (int)*length; ++i) - { - values->emplace_back(readString()); - } - } - return true; -} - -/// @endcond - -// end of 3d group -/// @} - -} - -#endif diff --git a/core/3d/CMakeLists.txt b/core/3d/CMakeLists.txt index 08c3bfabdea9..5cf47b224afc 100644 --- a/core/3d/CMakeLists.txt +++ b/core/3d/CMakeLists.txt @@ -16,13 +16,11 @@ set(_AX_3D_HEADER 3d/MotionStreak3D.h 3d/Skybox.h 3d/MeshSkin.h - 3d/cocos3d.h 3d/AABB.h 3d/Bundle3D.h 3d/ObjLoader.h 3d/Bundle3DData.h 3d/Skeleton3D.h - 3d/BundleReader.h 3d/AttachNode.h 3d/VertexAttribBinding.h 3d/3DProgramInfo.h @@ -37,7 +35,6 @@ set(_AX_3D_SRC 3d/BillBoard.cpp 3d/Bundle3D.cpp 3d/Bundle3DData.cpp - 3d/BundleReader.cpp 3d/Frustum.cpp 3d/Mesh.cpp 3d/MeshSkin.cpp diff --git a/core/3d/Mesh.cpp b/core/3d/Mesh.cpp index a431944a5c70..2efeddd45d10 100644 --- a/core/3d/Mesh.cpp +++ b/core/3d/Mesh.cpp @@ -199,7 +199,7 @@ Mesh* Mesh::create(const std::vector& positions, { int perVertexSizeInFloat = 0; std::vector vertices; - std::vector attribs; + axstd::pod_vector attribs; MeshVertexAttrib att; att.type = backend::VertexFormat::FLOAT3; @@ -258,7 +258,7 @@ Mesh* Mesh::create(const std::vector& positions, Mesh* Mesh::create(const std::vector& vertices, int /*perVertexSizeInFloat*/, const IndexArray& indices, - const std::vector& attribs) + const axstd::pod_vector& attribs) { MeshData meshdata; meshdata.attribs = attribs; @@ -537,7 +537,7 @@ void Mesh::draw(Renderer* renderer, command.setWireframe(wireframe); if (_instancing && _instances.size() > 0) { - command.setDrawType(CustomCommand::DrawType::ELEMENT_INSTANCE); + command.setDrawType(CustomCommand::DrawType::ELEMENT_INSTANCED); command.setInstanceBuffer(_instanceTransformBuffer, _instances.size()); } else if (_instancing) diff --git a/core/3d/Mesh.h b/core/3d/Mesh.h index 58d7f303a396..407f630d4883 100644 --- a/core/3d/Mesh.h +++ b/core/3d/Mesh.h @@ -80,7 +80,7 @@ class AX_DLL Mesh : public Object static Mesh* create(const std::vector& vertices, int perVertexSizeInFloat, const IndexArray& indices, - const std::vector& attribs); + const axstd::pod_vector& attribs); /** * create mesh diff --git a/core/3d/MeshRenderer.cpp b/core/3d/MeshRenderer.cpp index badafaf50c4e..b102947769bf 100644 --- a/core/3d/MeshRenderer.cpp +++ b/core/3d/MeshRenderer.cpp @@ -869,7 +869,7 @@ void MeshRenderer::draw(Renderer* renderer, const Mat4& transform, uint32_t flag if (_skeleton) _skeleton->updateBoneMatrix(); - Color4F color(getDisplayedColor()); + Color color(getDisplayedColor()); color.a = getDisplayedOpacity() / 255.0f; // check light and determine the shader used diff --git a/core/3d/MeshVertexIndexData.h b/core/3d/MeshVertexIndexData.h index 2048a498d6a2..17dcd03b5af6 100644 --- a/core/3d/MeshVertexIndexData.h +++ b/core/3d/MeshVertexIndexData.h @@ -146,7 +146,7 @@ class AX_DLL MeshVertexData : public Object backend::Buffer* _vertexBuffer = nullptr; // vertex buffer ssize_t _sizePerVertex = -1; Vector _indices; // index data - std::vector _attribs; // vertex attributes + axstd::pod_vector _attribs; // vertex attributes int _vertexCount = 0; // vertex count std::vector _vertexData; diff --git a/core/3d/MotionStreak3D.cpp b/core/3d/MotionStreak3D.cpp index 5e79a8e2cfe8..234068c8e96d 100644 --- a/core/3d/MotionStreak3D.cpp +++ b/core/3d/MotionStreak3D.cpp @@ -362,8 +362,8 @@ void MotionStreak3D::update(float delta) _pointState[_nuPoints] = 1.0f; // Color assignment - _vertexData[_nuPoints * 2].color = Color4B(_displayedColor, 255); - _vertexData[_nuPoints * 2 + 1].color = Color4B(_displayedColor, 255); + _vertexData[_nuPoints * 2].color = Color32(_displayedColor, 255); + _vertexData[_nuPoints * 2 + 1].color = Color32(_displayedColor, 255); // Generate polygon { diff --git a/core/3d/MotionStreak3D.h b/core/3d/MotionStreak3D.h index 24506b246333..42e597a03c35 100644 --- a/core/3d/MotionStreak3D.h +++ b/core/3d/MotionStreak3D.h @@ -174,7 +174,7 @@ class AX_DLL MotionStreak3D : public Node, public TextureProtocol struct VertexData { Vec3 pos; - Color4B color; + Color32 color; Tex2F texPos; }; diff --git a/core/3d/Skybox.cpp b/core/3d/Skybox.cpp index bc155b41c7d4..03558c846ec1 100644 --- a/core/3d/Skybox.cpp +++ b/core/3d/Skybox.cpp @@ -149,7 +149,7 @@ void Skybox::draw(Renderer* renderer, const Mat4& transform, uint32_t flags) // prescale the matrix to account for the camera fov cameraModelMat.scale(1 / projectionMat.m[0], 1 / projectionMat.m[5], 1.0); - Vec4 color(_displayedColor.r / 255.f, _displayedColor.g / 255.f, _displayedColor.b / 255.f, 1.f); + Color color(_displayedColor, 1.f); _programState->setUniform(_uniformColorLoc, &color, sizeof(color)); _programState->setUniform(_uniformCameraRotLoc, cameraModelMat.m, sizeof(cameraModelMat.m)); diff --git a/core/3d/cocos3d.h b/core/3d/cocos3d.h deleted file mode 100644 index 65d3cf97217d..000000000000 --- a/core/3d/cocos3d.h +++ /dev/null @@ -1,32 +0,0 @@ -/**************************************************************************** - Copyright (c) 2014-2016 Chukong Technologies Inc. - Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - - https://axmol.dev/ - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - ****************************************************************************/ - -#ifndef __COCOS_3D_COCOS3D_H__ -#define __COCOS_3D_COCOS3D_H__ - -// DON'T ADD FILES HERE -// ADD THEM IN THE COCOS2D.H HEADER FILE - -#endif diff --git a/core/audio/AudioDecoderWav.cpp b/core/audio/AudioDecoderWav.cpp index 22bb8beb06af..be4c5ad949d0 100644 --- a/core/audio/AudioDecoderWav.cpp +++ b/core/audio/AudioDecoderWav.cpp @@ -185,7 +185,7 @@ static int wav_close(WAV_FILE* wavf) AudioDecoderWav::AudioDecoderWav() { - memset(&_wavf, 0, offsetof(WAV_FILE, Stream)); + memset((void*)&_wavf, 0, offsetof(WAV_FILE, Stream)); } AudioDecoderWav::~AudioDecoderWav() diff --git a/core/axmol.cpp b/core/axmol.cpp index 207555e63c6d..8b5ee54c645a 100644 --- a/core/axmol.cpp +++ b/core/axmol.cpp @@ -36,12 +36,7 @@ namespace ax AX_DLL const char* axmolVersion() { - return "axmol-" AX_VERSION_STR_FULL; -} - -AX_DLL const char* cocos2dVersion() -{ - return axmolVersion(); + return AX_VERSION_STR_FULL; } } diff --git a/core/axmol.h b/core/axmol.h index 3108206a58c4..db2277fc96eb 100644 --- a/core/axmol.h +++ b/core/axmol.h @@ -38,9 +38,6 @@ THE SOFTWARE. #include "base/Config.h" // base -#ifndef AX_CORE_PROFILE -# include "base/AsyncTaskPool.h" -#endif #include "base/AutoreleasePool.h" #include "base/Configuration.h" #include "base/Logging.h" @@ -168,11 +165,11 @@ THE SOFTWARE. #include "renderer/Shaders.h" // physics -#include "physics/PhysicsBody.h" -#include "physics/PhysicsContact.h" -#include "physics/PhysicsJoint.h" -#include "physics/PhysicsShape.h" -#include "physics/PhysicsWorld.h" +// #include "physics/PhysicsBody.h" +// #include "physics/PhysicsContact.h" +// #include "physics/PhysicsJoint.h" +// #include "physics/PhysicsCollider.h" +// #include "physics/PhysicsWorld.h" // platform #include "platform/Common.h" diff --git a/core/axmolver.h.in b/core/axmolver.h.in index aee134c66ec5..4edc597b45af 100644 --- a/core/axmolver.h.in +++ b/core/axmolver.h.in @@ -3,9 +3,9 @@ /* Define the axmol version */ // 0x00 HI ME LO // 00 03 08 00 -#define AX_VERSION_MAJOR 2 -#define AX_VERSION_MINOR 3 -#define AX_VERSION_PATCH 3 +#define AX_VERSION_MAJOR 3 +#define AX_VERSION_MINOR 0 +#define AX_VERSION_PATCH 0 /* Define axmol version helper macros */ #define AX_VERSION_MAKE(a, b, c) ((a << 16) | (b << 8) | (c & 0xff)) diff --git a/core/base/AsyncTaskPool.cpp b/core/base/AsyncTaskPool.cpp deleted file mode 100644 index 5ee32e08fd68..000000000000 --- a/core/base/AsyncTaskPool.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/**************************************************************************** -Copyright (c) 2010 cocos2d-x.org -Copyright (c) 2013-2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - -https://axmol.dev/ - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -****************************************************************************/ - -#include "base/AsyncTaskPool.h" - -namespace ax -{ - -AsyncTaskPool* AsyncTaskPool::s_asyncTaskPool = nullptr; - -AsyncTaskPool* AsyncTaskPool::getInstance() -{ - if (s_asyncTaskPool == nullptr) - { - s_asyncTaskPool = new AsyncTaskPool(); - } - return s_asyncTaskPool; -} - -void AsyncTaskPool::destroyInstance() -{ - delete s_asyncTaskPool; - s_asyncTaskPool = nullptr; -} - -AsyncTaskPool::AsyncTaskPool() {} - -AsyncTaskPool::~AsyncTaskPool() {} - -} diff --git a/core/base/AsyncTaskPool.h b/core/base/AsyncTaskPool.h deleted file mode 100644 index dc189799b8c5..000000000000 --- a/core/base/AsyncTaskPool.h +++ /dev/null @@ -1,232 +0,0 @@ -/**************************************************************************** -Copyright (c) 2013-2016 Chukong Technologies Inc. -Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - -https://axmol.dev/ - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -****************************************************************************/ - -#ifndef __CCSYNC_TASK_POOL_H_ -#define __CCSYNC_TASK_POOL_H_ - -#include "platform/PlatformMacros.h" -#include "base/Director.h" -#include "base/Scheduler.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/** - * @addtogroup base - * @{ - */ -namespace ax -{ - -/** - * @class AsyncTaskPool - * @brief This class allows to perform background operations without having to manipulate threads. - * @js NA - */ -class AX_DLL AsyncTaskPool -{ -public: - typedef std::function TaskCallBack; - - enum class TaskType - { - TASK_IO, - TASK_NETWORK, - TASK_OTHER, - TASK_MAX_TYPE, - }; - - /** - * Returns the shared instance of the async task pool. - */ - static AsyncTaskPool* getInstance(); - - /** - * Destroys the async task pool. - */ - static void destroyInstance(); - - /** - * Stop tasks. - * - * @param type Task type you want to stop. - */ - void stopTasks(TaskType type); - - /** - * Enqueue a asynchronous task. - * - * @param type task type is io task, network task or others, each type of task has a thread to deal with it. - * @param callback callback when the task is finished. The callback is called in the main thread instead of task - * thread. - * @param callbackParam parameter used by the callback. - * @param task: task can be lambda function to be performed off thread. - * @lua NA - */ - void enqueue(TaskType type, TaskCallBack callback, void* callbackParam, std::function task); - - /** - * Enqueue a asynchronous task. - * - * @param type task type is io task, network task or others, each type of task has a thread to deal with it. - * @param task: task can be lambda function to be performed off thread. - * @lua NA - */ - void enqueue(AsyncTaskPool::TaskType type, std::function task); - - AsyncTaskPool(); - ~AsyncTaskPool(); - -protected: - // thread tasks internally used - class ThreadTasks - { - struct AsyncTaskCallBack - { - TaskCallBack callback; - void* callbackParam; - }; - - public: - ThreadTasks() : _stop(false) - { - _thread = std::thread([this] { - for (;;) - { - std::function task; - AsyncTaskCallBack callback; - { - std::unique_lock lock(this->_queueMutex); - this->_condition.wait(lock, [this] { return this->_stop || !this->_tasks.empty(); }); - if (this->_stop && this->_tasks.empty()) - return; - task = std::move(this->_tasks.front()); - callback = std::move(this->_taskCallBacks.front()); - this->_tasks.pop(); - this->_taskCallBacks.pop(); - } - - task(); - Director::getInstance()->getScheduler()->runOnAxmolThread( - std::bind(callback.callback, callback.callbackParam)); - } - }); - } - ~ThreadTasks() - { - { - std::unique_lock lock(_queueMutex); - _stop = true; - - while (_tasks.size()) - _tasks.pop(); - while (_taskCallBacks.size()) - _taskCallBacks.pop(); - } - _condition.notify_all(); - _thread.join(); - } - void clear() - { - std::unique_lock lock(_queueMutex); - while (_tasks.size()) - _tasks.pop(); - while (_taskCallBacks.size()) - _taskCallBacks.pop(); - } - - void enqueue(TaskCallBack callback, void* callbackParam, std::function task) - { - AsyncTaskCallBack taskCallBack; - taskCallBack.callback = std::move(callback); - taskCallBack.callbackParam = callbackParam; - - { - std::unique_lock lock(_queueMutex); - - // don't allow enqueueing after stopping the pool - if (_stop) - { - AX_ASSERT(0 && "already stop"); - return; - } - - _tasks.push(std::move(task)); - _taskCallBacks.push(std::move(taskCallBack)); - } - _condition.notify_one(); - } - - private: - // need to keep track of thread so we can join them - std::thread _thread; - // the task queue - std::queue> _tasks; - std::queue _taskCallBacks; - - // synchronization - std::mutex _queueMutex; - std::condition_variable _condition; - bool _stop; - }; - - // tasks - ThreadTasks _threadTasks[int(TaskType::TASK_MAX_TYPE)]; - - static AsyncTaskPool* s_asyncTaskPool; -}; - -inline void AsyncTaskPool::stopTasks(TaskType type) -{ - auto& threadTask = _threadTasks[(int)type]; - threadTask.clear(); -} - -inline void AsyncTaskPool::enqueue(AsyncTaskPool::TaskType type, - TaskCallBack callback, - void* callbackParam, - std::function task) -{ - auto& threadTask = _threadTasks[(int)type]; - - threadTask.enqueue(std::move(callback), callbackParam, std::move(task)); -} - -inline void AsyncTaskPool::enqueue(AsyncTaskPool::TaskType type, std::function task) -{ - enqueue( - type, [](void*) {}, nullptr, std::move(task)); -} - -} -// end group -/// @} -#endif //__CCSYNC_TASK_POOL_H_ diff --git a/core/base/CMakeLists.txt b/core/base/CMakeLists.txt index 371dc8592400..b8592e1ea61a 100644 --- a/core/base/CMakeLists.txt +++ b/core/base/CMakeLists.txt @@ -78,7 +78,6 @@ set(_AX_BASE_HEADER base/Scheduler.h base/EventType.h base/IMEDispatcher.h - base/PaddedString.h base/JsonWriter.h base/JobSystem.h ) @@ -139,11 +138,6 @@ set(_AX_BASE_SRC ${_AX_BASE_SPECIFIC_SRC} ) -if(NOT AX_CORE_PROFILE) - set(_AX_BASE_HEADER ${_AX_BASE_HEADER} base/AsyncTaskPool.h) - set(_AX_BASE_SRC ${_AX_BASE_SRC} base/AsyncTaskPool.cpp) -endif() - if(AX_ENABLE_CONSOLE) set(_AX_BASE_HEADER ${_AX_BASE_HEADER} base/Console.h) set(_AX_BASE_SRC ${_AX_BASE_SRC} base/Console.cpp) diff --git a/core/base/Config.h b/core/base/Config.h index f2ce9105e13f..d51fb74be64b 100644 --- a/core/base/Config.h +++ b/core/base/Config.h @@ -252,27 +252,6 @@ THE SOFTWARE. # define AX_LUA_ENGINE_DEBUG 0 #endif -/** Use physics integration API. */ -// It works with: -// Chipmunk2D or Box2D -#if defined(AX_ENABLE_PHYSICS) -/** Use Chipmunk2D physics 2d engine on physics integration API. */ -# ifndef AX_ENABLE_CHIPMUNK_INTEGRATION -# define AX_ENABLE_CHIPMUNK_INTEGRATION 0 -# endif -/** or use Box2D physics 2d engine on physics integration API. */ -# ifndef AX_ENABLE_BOX2D_INTEGRATION -# define AX_ENABLE_BOX2D_INTEGRATION 1 -# endif -#endif // defined(AX_ENABLE_PHYSICS) - -#if defined(AX_ENABLE_3D_PHYSICS) -/** Use bullet physics engine. */ -# ifndef AX_ENABLE_BULLET_INTEGRATION -# define AX_ENABLE_BULLET_INTEGRATION 1 -# endif -#endif - /** Use culling or not. */ #ifndef AX_USE_CULLING # define AX_USE_CULLING 1 diff --git a/core/base/Configuration.cpp b/core/base/Configuration.cpp index 5b304c2e4b61..5f4e9a34ed6e 100644 --- a/core/base/Configuration.cpp +++ b/core/base/Configuration.cpp @@ -81,7 +81,7 @@ bool Configuration::init() _valueDict["axmol.compiled_with_gl_state_cache"] = Value(true); #endif -#if _AX_DEBUG +#if defined(_AX_DEBUG) && _AX_DEBUG _valueDict["axmol.build_type"] = Value("DEBUG"); #else _valueDict["axmol.build_type"] = Value("RELEASE"); diff --git a/core/base/Console.cpp b/core/base/Console.cpp index b28de3a014fc..eec8047aa608 100644 --- a/core/base/Console.cpp +++ b/core/base/Console.cpp @@ -37,6 +37,8 @@ #include #include #include +#include +#include #if defined(_MSC_VER) || defined(__MINGW32__) # include @@ -71,51 +73,6 @@ static const size_t SEND_BUFSIZ = 512; std::string Console::Utility::_prompt(PROMPT); -// TODO: these general utils should be in a separate class -// -// Trimming functions were taken from: http://stackoverflow.com/a/217605 -// Since c++17, some parts of the standard library were removed, include "ptr_fun". - -// trim from start - -std::string& Console::Utility::ltrim(std::string& s) -{ - s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) { return !std::isspace(ch); })); - return s; -} - -// trim from end -std::string& Console::Utility::rtrim(std::string& s) -{ - s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { return !std::isspace(ch); }).base(), s.end()); - return s; -} - -// trim from both ends -std::string& Console::Utility::trim(std::string& s) -{ - return Console::Utility::ltrim(Console::Utility::rtrim(s)); -} - -std::vector& Console::Utility::split(std::string_view s, char delim, std::vector& elems) -{ - std::stringstream ss; - ss << s; - std::string item; - while (std::getline(ss, item, delim)) - { - elems.emplace_back(item); - } - return elems; -} - -std::vector Console::Utility::split(std::string_view s, char delim) -{ - std::vector elems; - Console::Utility::split(s, delim, elems); - return elems; -} - // isFloat taken from http://stackoverflow.com/questions/447206/c-isfloat-function bool Console::Utility::isFloat(std::string_view myString) { @@ -756,14 +713,13 @@ bool Console::parseCommand(socket_native_type fd) return false; } } - std::string cmdLine; - cmdLine = std::string(buf); - auto commands = Console::Utility::split(cmdLine, _commandSeparator); try { - for (auto&& command : commands) + std::string_view cmdLine(buf); + for (auto rgn : std::views::split(cmdLine, _commandSeparator)) { - performCommand(fd, Console::Utility::trim(command)); + std::string_view command{&*rgn.begin(), static_cast(std::ranges::distance(rgn))}; + performCommand(fd, command); } } catch (const std::runtime_error& e) @@ -778,30 +734,39 @@ bool Console::parseCommand(socket_native_type fd) void Console::performCommand(socket_native_type fd, std::string_view command) { - std::vector args = Console::Utility::split(command, ' '); - if (args.empty()) + if (command.empty()) { - throw std::runtime_error("Unknown command. Type 'help' for options\n"); + return; } - auto it = _commands.find(Console::Utility::trim(args[0])); - if (it != _commands.end()) + int index = 0; + hlookup::string_map::iterator it; + std::string cmd_args; + for (auto rgn : std::views::split(command, ' ')) { - std::string args2; - for (size_t i = 1; i < args.size(); ++i) + std::string_view cmd_arg{&*rgn.begin(), static_cast(std::ranges::distance(rgn))}; + if (index == 0) { - if (i > 1) + it = _commands.find(StringUtils::trim(cmd_arg)); + if (it == _commands.end()) { - args2 += ' '; + AXLOGW("Unknown command {} . Type 'help' for options", command); + break; } - args2 += Console::Utility::trim(args[i]); } - auto cmd = it->second; - cmd->commandGeneric(fd, args2); + else + { + if (index > 1) + cmd_args += ' '; + cmd_args += cmd_arg; + } + ++index; } - else + + if (it != _commands.end()) { - throw std::runtime_error(std::string{"Unknown command "}.append(command).append(". Type 'help' for options\n")); + auto cmd = it->second; + cmd->commandGeneric(fd, cmd_args); } } @@ -1163,14 +1128,28 @@ void Console::commandTexturesSubCommandFlush(socket_native_type /*fd*/, std::str void Console::commandTouchSubCommandTap(socket_native_type fd, std::string_view args) { - auto argv = Console::Utility::split(args, ' '); - - if ((argv.size() == 3) && (Console::Utility::isFloat(argv[1]) && Console::Utility::isFloat(argv[2]))) + int argi = 0; + float x, y; + for (auto rgn : std::views::split(args, ' ')) { + std::string_view argv{&*rgn.begin(), static_cast(std::ranges::distance(rgn))}; + switch (argi++) + { + case 1: + axstd::from_chars(argv.data(), argv.data() + argv.length(), x); + break; + case 2: + axstd::from_chars(argv.data(), argv.data() + argv.length(), x); + break; + } + if (argi == 3) + { + break; + } + } - float x = (float)utils::atof(argv[1].c_str()); - float y = (float)utils::atof(argv[2].c_str()); - + if (argi == 3) + { std::srand((unsigned)time(nullptr)); _touchId = rand(); Scheduler* sched = Director::getInstance()->getScheduler(); @@ -1188,16 +1167,34 @@ void Console::commandTouchSubCommandTap(socket_native_type fd, std::string_view void Console::commandTouchSubCommandSwipe(socket_native_type fd, std::string_view args) { - auto argv = Console::Utility::split(args, ' '); + std::vector argv; + for (auto rgn : std::views::split(args, ' ')) + { + argv.emplace_back(&*rgn.begin(), static_cast(std::ranges::distance(rgn))); + } if ((argv.size() == 5) && (Console::Utility::isFloat(argv[1])) && (Console::Utility::isFloat(argv[2])) && (Console::Utility::isFloat(argv[3])) && (Console::Utility::isFloat(argv[4]))) { + float points[4]; + + for (int i = 0; i < 4; ++i) + { + const auto& val = argv[i + 1]; + // const auto [_, ec] = std::from_chars(val.data(), val.data() + val.size(), points[i]); + // if (!!(int)ec) + // { + // AXLOGW("invalid float number: {}", val); + // return; + // } + char* endptr = nullptr; + points[i] = std::strtod(val.data(), &endptr); + } - float x1 = (float)utils::atof(argv[1].c_str()); - float y1 = (float)utils::atof(argv[2].c_str()); - float x2 = (float)utils::atof(argv[3].c_str()); - float y2 = (float)utils::atof(argv[4].c_str()); + float& x1 = points[0]; + float& y1 = points[1]; + float& x2 = points[2]; + float& y2 = points[3]; std::srand((unsigned)time(nullptr)); _touchId = rand(); @@ -1430,4 +1427,4 @@ void Console::sendHelp(socket_native_type fd, const hlookup::string_map& split(std::string_view s, char delim, std::vector& elems); - static std::vector split(std::string_view s, char delim); - /** Checks myString is a floating-point type. */ static bool isFloat(std::string_view myString); diff --git a/core/base/Director.cpp b/core/base/Director.cpp index dbeefd96476b..bc0b3bb8791f 100644 --- a/core/base/Director.cpp +++ b/core/base/Director.cpp @@ -57,9 +57,6 @@ THE SOFTWARE. #include "base/Logging.h" #include "base/AutoreleasePool.h" #include "base/Configuration.h" -#ifndef AX_CORE_PROFILE -# include "base/AsyncTaskPool.h" -#endif #include "base/ObjectFactory.h" #include "platform/Application.h" #if defined(AX_ENABLE_AUDIO) @@ -308,8 +305,8 @@ void Director::drawScene() if (_runningScene) { -#if (defined(AX_ENABLE_PHYSICS) || (defined(AX_ENABLE_3D_PHYSICS) && AX_ENABLE_BULLET_INTEGRATION) || \ - defined(AX_ENABLE_NAVMESH)) +#if defined(AX_ENABLE_PHYSICS) || defined(AX_ENABLE_3D_PHYSICS) || \ + defined(AX_ENABLE_NAVMESH) _runningScene->stepPhysicsAndNavigation(_deltaTime); #endif // clear draw stats @@ -382,7 +379,7 @@ void Director::calculateDeltaTime() _deltaTime = MAX(0, _deltaTime); } -#if _AX_DEBUG +#if defined(_AX_DEBUG) && _AX_DEBUG // If we are debugging our code, prevent big delta time if (_deltaTime > 0.2f) { @@ -693,7 +690,7 @@ float Director::getZEye() const return (_winSizeInPoints.height / 1.154700538379252f); //(2 * tanf(M_PI/6)) } -void Director::setClearColor(const Color4F& clearColor) +void Director::setClearColor(const Color& clearColor) { _clearColor = clearColor; } @@ -1059,9 +1056,7 @@ void Director::reset() AnimationCache::destroyInstance(); SpriteFrameCache::destroyInstance(); FileUtils::destroyInstance(); -#ifndef AX_CORE_PROFILE - AsyncTaskPool::destroyInstance(); -#endif + backend::ProgramStateRegistry::destroyInstance(); backend::ProgramManager::destroyInstance(); diff --git a/core/base/Director.h b/core/base/Director.h index b5d017bfb4c9..b1797c1d1965 100644 --- a/core/base/Director.h +++ b/core/base/Director.h @@ -379,7 +379,7 @@ class AX_DLL Director * value range of each element is [0.0, 1.0]. * @js NA */ - void setClearColor(const Color4F& clearColor); + void setClearColor(const Color& clearColor); void mainLoop(); /** Invoke main loop with delta time. Then `calculateDeltaTime` can just use the delta time directly. @@ -662,7 +662,7 @@ class AX_DLL Director /* Renderer for the Director */ Renderer* _renderer = nullptr; - Color4F _clearColor = {0, 0, 0, 1}; + Color _clearColor = {0, 0, 0, 1}; #ifdef AX_ENABLE_CONSOLE /* Console for the director */ Console* _console = nullptr; diff --git a/core/base/JsonWriter.h b/core/base/JsonWriter.h index 3fbf020fd0ed..cf65ed176014 100644 --- a/core/base/JsonWriter.h +++ b/core/base/JsonWriter.h @@ -22,7 +22,7 @@ Copyright (c) 2019-present Axmol Engine contributors (see AUTHORS.md). OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - A simple wrapper dotnet Utf8JsonWriter like API style of rapidjson + A simple JsonWriter implementation like dotnet Utf8JsonWriter API. ****************************************************************************/ @@ -30,73 +30,46 @@ Copyright (c) 2019-present Axmol Engine contributors (see AUTHORS.md). #include -#include "rapidjson/writer.h" -#include "rapidjson/prettywriter.h" +#include "yasio/byte_buffer.hpp" +#include "base/format.h" namespace ax { struct JsonWriterOptions { - char indentChar = ' '; - int indentCharCount = 2; - rapidjson::PrettyFormatOptions formatOptions = rapidjson::kFormatDefault; -}; - -template -struct JsonWriterImpl -{ - using type = rapidjson::PrettyWriter; -}; - -template <> -struct JsonWriterImpl -{ - using type = rapidjson::Writer; + char indentChar = ' '; + int indentCharCount = 2; + int initialBufferSize = 128; }; template class JsonWriter { - using writer_type = typename JsonWriterImpl<_Pretty>::type; - public: JsonWriter() { - new (static_cast(_writerHold)) writer_type(_buffer); setOptions(JsonWriterOptions{}); + _buffer.reserve(_options.initialBufferSize); } - ~JsonWriter() { writer().~writer_type(); } + ~JsonWriter() {} explicit JsonWriter(const JsonWriterOptions& options) { setOptions(options); } - void setOptions(const JsonWriterOptions& options) - { - if constexpr (_Pretty) - { - prettyWriter().SetIndent(options.indentChar, options.indentCharCount); - prettyWriter().SetFormatOptions(options.formatOptions); - } - } + void setOptions(const JsonWriterOptions& options) { _options = options; } - void writePropertyName(std::string_view propertyName) - { - writer().Key(propertyName.data(), static_cast(propertyName.length()), false); - } + explicit operator std::string_view() const { return std::string_view{_buffer.data(), _buffer.size()}; } #pragma region write values - void writeBoolValue(bool value) { writer().Bool(value); } - void writeNumberValue(int value) { writer().Int(value); } - void writeNumberValue(long long value) { writer().Int64(value); } - void writeNumberValue(double value) { writer().Double(value); } - void writeStringValue(std::string_view value) - { - writer().String(value.data(), static_cast(value.length())); - } + void writeBoolValue(bool value) { writeUnquoteValue(value); } + void writeNumberValue(int value) { writeUnquoteValue(value); } + void writeNumberValue(long long value) { writeUnquoteValue(value); } + void writeNumberValue(double value) { writeUnquoteValue(value); } + void writeNullValue() { writeUnquoteValue("null"sv); } - void writeNullValue() { writer().Null(); } + void writeStringValue(std::string_view value) { writeQuoteValue(value); } - template - void writeNumberValues(Intty (&values)[_N]) + template + void writeNumberValues(_Nty (&values)[_N]) { for (auto v : values) writeNumberValue(v); @@ -154,21 +127,108 @@ class JsonWriter writePropertyName(propertyName); writeStartObject(); } - void writeStartArray() { writer().StartArray(); } - void writeEndArray() { writer().EndArray(); } - void writeStartObject() { writer().StartObject(); } - void writeEndObject() { writer().EndObject(); } + void writeStartArray() { writeStartCollection('['); } + void writeEndArray() { writeEndCollection(']'); } + + void writeStartObject() { writeStartCollection('{'); } + void writeEndObject() { writeEndCollection('}'); } #pragma endregion - explicit operator std::string_view() { return std::string_view{_buffer.GetString(), _buffer.GetLength()}; } + void writePropertyName(std::string_view propertyName) + { + if constexpr (_Pretty) + { + fillIndentChars(); + _pendingValue = true; + } + _buffer += '"'; + _buffer += propertyName; + _buffer += "\":"sv; + if constexpr (_Pretty) + _buffer += _options.indentChar; + } protected: - inline writer_type& writer() { return *reinterpret_cast(&_writerHold[0]); } - inline JsonWriterImpl::type& prettyWriter() + void writeQuoteValue(const std::string_view& value) { - return *reinterpret_cast::type*>(&_writerHold[0]); + if constexpr (_Pretty) + if (!_pendingValue) + fillIndentChars(); + + _buffer += '"'; + _buffer += value; + _buffer += "\","sv; + if constexpr (_Pretty) + _buffer += '\n'; + + _pendingValue = false; } - rapidjson::StringBuffer _buffer; - uint8_t _writerHold[sizeof(writer_type)]; + template + void writeUnquoteValue(const _Ty& value) + { + if constexpr (_Pretty) + if (!_pendingValue) + fillIndentChars(); + + fmt::vformat_to(std::back_inserter(_buffer), "{},", fmt::make_format_args(value)); + if constexpr (_Pretty) + _buffer += '\n'; + + _pendingValue = false; + } + + void writeStartCollection(const char startChar) + { + if constexpr (_Pretty) + if (!_pendingValue) + fillIndentChars(); + + ++_level; + + _buffer += startChar; + if constexpr (_Pretty) + _buffer += '\n'; + + _pendingValue = false; + } + + void writeEndCollection(const char termChar) + { + if (_buffer.empty()) + return; + + --_level; + + if constexpr (_Pretty) + { + if (_buffer.back() == '\n') + _buffer.pop_back(); // pop '\n' + } + if (_buffer.back() == ',') + _buffer.pop_back(); + if constexpr (_Pretty) + { + _buffer += '\n'; + fillIndentChars(); + } + _buffer += termChar; + + if (_level != 0) + _buffer += ','; + if constexpr (_Pretty) + _buffer += '\n'; + } + + void fillIndentChars() + { + if constexpr (_Pretty) + if (_level) + _buffer.expand(_level * _options.indentCharCount, _options.indentChar); + } + + yasio::sbyte_buffer _buffer; + uint16_t _level{0}; + bool _pendingValue{false}; + JsonWriterOptions _options; }; } // namespace ax diff --git a/core/base/Logging.cpp b/core/base/Logging.cpp index 73500b6936ee..5da30fdd5879 100644 --- a/core/base/Logging.cpp +++ b/core/base/Logging.cpp @@ -230,18 +230,4 @@ AX_DLL void writeLog(LogItem& item, const char* tag) # endif #endif } -#ifndef AX_CORE_PROFILE -AX_API void print(const char* format, ...) -{ - va_list args; - - va_start(args, format); - auto message = StringUtils::vformat(format, args); - va_end(args); - - if (!message.empty()) - outputLog(LogItem::vformat(FMT_COMPILE("{}{}\n"), preprocessLog(LogItem{LogLevel::Silent}), message), - "axmol debug info"); -} -#endif } diff --git a/core/base/Logging.h b/core/base/Logging.h index e1a41e20d7d0..c1f71465dd2e 100644 --- a/core/base/Logging.h +++ b/core/base/Logging.h @@ -159,11 +159,4 @@ inline void printLogT(_FmtType&& fmt, LogItem& item, _Types&&... args) #define AXLOGE(fmtOrMsg, ...) AXLOG_WITH_LEVEL(ax::LogLevel::Error, fmtOrMsg, ##__VA_ARGS__) #define AXLOGT AXLOGV - -#ifndef AX_CORE_PROFILE -/** - @brief Output Debug message. - */ -/* AX_DEPRECATED(2.1)*/ AX_API void print(const char* format, ...) AX_FORMAT_PRINTF(1, 2); // use AXLOGD instead -#endif } diff --git a/core/base/Macros.h b/core/base/Macros.h index 3b2e90463f24..f47afaf3187e 100644 --- a/core/base/Macros.h +++ b/core/base/Macros.h @@ -38,7 +38,7 @@ THE SOFTWARE. #include "platform/StdC.h" #ifndef AXASSERT -# if _AX_DEBUG > 0 +# if defined(_AX_DEBUG) && _AX_DEBUG > 0 # if AX_ENABLE_SCRIPT_BINDING extern bool AX_DLL ax_assert_script_compatible(const char* msg); # define AXASSERT(cond, msg) \ diff --git a/core/base/PaddedString.h b/core/base/PaddedString.h deleted file mode 100644 index 63dac7b65f04..000000000000 --- a/core/base/PaddedString.h +++ /dev/null @@ -1,105 +0,0 @@ -/**************************************************************************** - -Copyright (c) 2019-present Axmol Engine contributors (see AUTHORS.md). - - https://axmol.dev/ - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - - - ****************************************************************************/ - -#pragma once - -#include "simdjson/simdjson.h" -#include "platform/FileUtils.h" - -namespace ax -{ - -/** - * @addtogroup base - * @{ - */ - -/** - @brief A dedicated class for easy load padded string for simdjson parser. - - The only legal use case is: - - auto strJson = PaddedString::load(fontatlasFile); - ondemand::parser parser; - ondemand::document settings = parser.iterate(strJson); - */ -class PaddedString final -{ -public: - static PaddedString load(std::string_view filename) - { - PaddedString ret; - FileUtils::getInstance()->getContents(filename, &ret); - return ret; - } - - PaddedString() = default; - ~PaddedString() { delete[] data_ptr; } - - PaddedString(PaddedString&& o) noexcept : viable_size(o.viable_size), data_ptr(o.data_ptr) - { - o.data_ptr = nullptr; // we take ownership - } - PaddedString& operator=(PaddedString&& o) noexcept - { - swap(o); - return *this; - } - void swap(PaddedString& o) noexcept - { - std::swap(viable_size, o.viable_size); - std::swap(data_ptr, o.data_ptr); - } - - using value_type = char; - size_t size() const { return viable_size; } - void resize(size_t size) - { - assert(!data_ptr || size <= viable_size); // not allow enlarge - if (!data_ptr) - data_ptr = simdjson::internal::allocate_padded_buffer(size); - viable_size = size; - } - - char* data() { return data_ptr; } - const char* data() const { return data_ptr; } - - operator simdjson::padded_string_view() const noexcept - { - return simdjson::padded_string_view(data(), size(), size() + simdjson::SIMDJSON_PADDING); - } - -private: - PaddedString(const PaddedString&) = delete; - size_t viable_size{0}; - char* data_ptr{nullptr}; -}; - -// end of base group -/** @} */ - -} diff --git a/core/base/Properties.cpp b/core/base/Properties.cpp index d10081b1e064..ab7037833a66 100644 --- a/core/base/Properties.cpp +++ b/core/base/Properties.cpp @@ -955,12 +955,7 @@ bool Properties::getQuaternionFromAxisAngle(const char* name, Quaternion* out) c return parseAxisAngle(getString(name), out); } -bool Properties::getColor(const char* name, Vec3* out) const -{ - return parseColor(getString(name), out); -} - -bool Properties::getColor(const char* name, Vec4* out) const +bool Properties::getColor(const char* name, Color* out) const { return parseColor(getString(name), out); } @@ -1261,39 +1256,7 @@ bool Properties::parseAxisAngle(const char* str, Quaternion* out) return false; } -bool Properties::parseColor(const char* str, Vec3* out) -{ - if (str) - { - if (strlen(str) == 7 && str[0] == '#') - { - // Read the string into an int as hex. - unsigned int color; - if (sscanf(str + 1, "%x", &color) == 1) - { - if (out) - out->set(Vec3::fromColor(color)); - return true; - } - else - { - // Invalid format - AXLOGW("Error attempting to parse property as an RGB color: {}", str); - } - } - else - { - // Not a color string. - AXLOGW("Error attempting to parse property as an RGB color (not specified as a color string): {}", str); - } - } - - if (out) - out->set(0.0f, 0.0f, 0.0f); - return false; -} - -bool Properties::parseColor(const char* str, Vec4* out) +bool Properties::parseColor(const char* str, Color* out) { if (str) { @@ -1304,7 +1267,7 @@ bool Properties::parseColor(const char* str, Vec4* out) if (sscanf(str + 1, "%x", &color) == 1) { if (out) - out->set(Vec4::fromColor(color)); + out->set(Color::fromHex(color)); return true; } else diff --git a/core/base/Properties.h b/core/base/Properties.h index 9f521b2e4aba..01c9e480bcf3 100644 --- a/core/base/Properties.h +++ b/core/base/Properties.h @@ -392,20 +392,6 @@ class AX_DLL Properties */ bool getQuaternionFromAxisAngle(const char* name, Quaternion* out) const; - /** - * Interpret the value of the given property as an RGB color in hex and write this color to a Vector3. - * E.g. 0xff0000 represents red and sets the vector to (1, 0, 0). - * If the property does not exist, out will be set to Vector3(0.0f, 0.0f, 0.0f). - * If the property exists but could not be scanned, an error will be logged and out will be set - * to Vector3(0.0f, 0.0f, 0.0f). - * - * @param name The name of the property to interpret, or NULL to return the current property's value. - * @param out The vector to set to this property's interpreted value. - * - * @return True on success, false if the property does not exist or could not be scanned. - */ - bool getColor(const char* name, Vec3* out) const; - /** * Interpret the value of the given property as an RGBA color in hex and write this color to a Vector4. * E.g. 0xff0000ff represents opaque red and sets the vector to (1, 0, 0, 1). @@ -418,7 +404,7 @@ class AX_DLL Properties * * @return True on success, false if the property does not exist or could not be scanned. */ - bool getColor(const char* name, Vec4* out) const; + bool getColor(const char* name, Color* out) const; /** * Gets the file path for the given property if the file exists. @@ -507,16 +493,6 @@ class AX_DLL Properties */ static bool parseAxisAngle(const char* str, Quaternion* out); - /** - * Attempts to parse the specified string as an RGB color value. - * - * @param str The string to parse. - * @param out The value to populate if successful. - * - * @return True if a valid RGB color was parsed, false otherwise. - */ - static bool parseColor(const char* str, Vec3* out); - /** * Attempts to parse the specified string as an RGBA color value. * @@ -525,7 +501,7 @@ class AX_DLL Properties * * @return True if a valid RGBA color was parsed, false otherwise. */ - static bool parseColor(const char* str, Vec4* out); + static bool parseColor(const char* str, Color* out); private: /** diff --git a/core/base/Scheduler.h b/core/base/Scheduler.h index c00b84a3688a..b1381d11fd89 100644 --- a/core/base/Scheduler.h +++ b/core/base/Scheduler.h @@ -471,12 +471,7 @@ class AX_DLL Scheduler : public Object @js NA */ void runOnAxmolThread(std::function action); -#ifndef AX_CORE_PROFILE - AX_DEPRECATED(2.1) void performFunctionInCocosThread(std::function action) - { - runOnAxmolThread(std::move(action)); - } -#endif + /** * Remove all pending functions queued to be performed with Scheduler::runOnAxmolThread * Functions unscheduled in this manner will not be executed @@ -485,9 +480,6 @@ class AX_DLL Scheduler : public Object * @js NA */ void removeAllPendingActions(); -#ifndef AX_CORE_PROFILE - AX_DEPRECATED(2.1) void removeAllFunctionsToBePerformedInCocosThread() { removeAllPendingActions(); } -#endif protected: /** Schedules the 'callback' function for a given target with a given priority. The 'callback' selector will be called every frame. diff --git a/core/base/StencilStateManager.cpp b/core/base/StencilStateManager.cpp index 542663725aa7..9fe19c451031 100644 --- a/core/base/StencilStateManager.cpp +++ b/core/base/StencilStateManager.cpp @@ -52,7 +52,7 @@ StencilStateManager::StencilStateManager() _customCommand.createIndexBuffer(CustomCommand::IndexFormat::U_SHORT, 6, CustomCommand::BufferUsage::STATIC); _customCommand.updateIndexBuffer(indices, sizeof(indices)); - Color4F color(1, 1, 1, 1); + Color color(1, 1, 1, 1); pipelineDescriptor.programState->setUniform(_colorUniformLocation, &color, sizeof(color)); } diff --git a/core/base/Types.h b/core/base/Types.h index 6f9b88607cbe..ccbeebeb4e0e 100644 --- a/core/base/Types.h +++ b/core/base/Types.h @@ -50,20 +50,10 @@ namespace ax */ typedef Vec2 Tex2F; -/** @struct PointSprite - * Vec2 Sprite component. - */ -struct AX_DLL PointSprite -{ - Vec2 pos; // 8 bytes - Color4B color; // 4 bytes - float size = 0.f; // 4 bytes -}; - /** @struct Quad2 * A 2D Quad. 4 * 2 floats. */ -struct AX_DLL Quad2 +struct Quad2 { Vec2 tl; Vec2 tr; @@ -74,7 +64,7 @@ struct AX_DLL Quad2 /** @struct Quad3 * A 3D Quad. 4 * 3 floats. */ -struct AX_DLL Quad3 +struct Quad3 { Vec3 bl; Vec3 br; @@ -82,93 +72,69 @@ struct AX_DLL Quad3 Vec3 tr; }; -/** @struct V2F_C4B_T2F - * A Vec2 with a vertex point, a tex coord point and a color 4B. +/** @struct V2F_T2F_C4F + * A Vec2 with a vertex point, a tex coord point and a color 4F. */ -struct V2F_C4B_T2F +struct V2F_T2F_C4F { - /// vertices (2F) - Vec2 vertices; - /// colors (4B) - Color4B colors; + /// position (2F) + Vec2 position; /// tex coords (2F) - Tex2F texCoords; + Tex2F texCoord; + /// color (4F) + Color color; }; -/** @struct V2F_C4B_PF - * +/** @struct V3F_T2F_C4B + * A Vec2 with a vertex point, a tex coord point and a color 4B. */ -struct V2F_C4B_PF +struct V3F_T2F_C4B { - /// vertices (2F) - Vec2 vertices; - /// colors (4B) - Color4B colors; - /// pointsize - float pointSize = 0.f; -}; + /// position (3F) + Vec3 position; // 12 bytes -/** @struct V2F_C4F_T2F - * A Vec2 with a vertex point, a tex coord point and a color 4F. - */ -struct AX_DLL V2F_C4F_T2F -{ - /// vertices (2F) - Vec2 vertices; - /// colors (4F) - Color4F colors; - /// tex coords (2F) - Tex2F texCoords; + // tex coords (2F) + Tex2F texCoord; // 8 bytes + + /// color (4B) + Color32 color; // 4 bytes }; -/** @struct V3F_C4B_T2F - * A Vec2 with a vertex point, a tex coord point and a color 4B. +/** @struct V3F_T2F_C4F + * A Vec2 with a vertex point, a tex coord point and a color 4F. */ -struct AX_DLL V3F_C4B_T2F +struct V3F_T2F_C4F { - /// vertices (3F) - Vec3 vertices; // 12 bytes - - /// colors (4B) - Color4B colors; // 4 bytes + /// position (3F) + Vec3 position; // 12 bytes // tex coords (2F) - Tex2F texCoords; // 8 bytes + Tex2F texCoord; // 8 bytes + + /// color (4F) + Color color; // 16 bytes }; /** @struct V3F_T2F * A Vec2 with a vertex point, a tex coord point. */ -struct AX_DLL V3F_T2F +struct V3F_T2F { - /// vertices (2F) - Vec3 vertices; + /// position (2F) + Vec3 position; /// tex coords (2F) - Tex2F texCoords; + Tex2F texCoord; }; /** @struct V3F_C4F * A Vec3 with a vertex point, a color. */ -struct AX_DLL V3F_C4F -{ - /// vertices (3F) - Vec3 vertices; - /// vertices (4F) - Color4F colors; -}; - -struct V3F_C4B -{ - Vec3 vertices; - Color4B colors; -}; - -struct V3F_T2F_C4F +struct V3F_C4F { + /// position (3F) Vec3 position; - Vec2 uv; - Vec4 color; + /// color (4F) + Color color; }; struct V3F_T2F_N3F @@ -178,65 +144,47 @@ struct V3F_T2F_N3F Vec3 normal; }; -/** @struct V2F_C4B_T2F_Triangle - * A Triangle of V2F_C4B_T2F. - */ -struct AX_DLL V2F_C4B_T2F_Triangle -{ - V2F_C4B_T2F a; - V2F_C4B_T2F b; - V2F_C4B_T2F c; -}; - -/** @struct V2F_C4B_T2F_Quad - * A Quad of V2F_C4B_T2F. - */ -struct AX_DLL V2F_C4B_T2F_Quad +struct V2F_T2F_C4F_Triangle { - /// bottom left - V2F_C4B_T2F bl; - /// bottom right - V2F_C4B_T2F br; - /// top left - V2F_C4B_T2F tl; - /// top right - V2F_C4B_T2F tr; + V2F_T2F_C4F a; + V2F_T2F_C4F b; + V2F_T2F_C4F c; }; -/** @struct V3F_C4B_T2F_Quad - * 4 Vertex3FTex2FColor4B. +/** @struct V3F_T2F_C4F_Quad + * 4 Vertex3FTex2FColor32. */ -struct AX_DLL V3F_C4B_T2F_Quad +struct V3F_T2F_C4B_Quad { /// top left - V3F_C4B_T2F tl; + V3F_T2F_C4B tl; /// bottom left - V3F_C4B_T2F bl; + V3F_T2F_C4B bl; /// top right - V3F_C4B_T2F tr; + V3F_T2F_C4B tr; /// bottom right - V3F_C4B_T2F br; + V3F_T2F_C4B br; }; -/** @struct V2F_C4F_T2F_Quad - * 4 Vertex2FTex2FColor4F Quad. +/** @struct V3F_T2F_C4F_Quad + * 4 Vertex3FTex2FColor4F. */ -struct AX_DLL V2F_C4F_T2F_Quad +struct V3F_T2F_C4F_Quad { - /// bottom left - V2F_C4F_T2F bl; - /// bottom right - V2F_C4F_T2F br; /// top left - V2F_C4F_T2F tl; + V3F_T2F_C4F tl; + /// bottom left + V3F_T2F_C4F bl; /// top right - V2F_C4F_T2F tr; + V3F_T2F_C4F tr; + /// bottom right + V3F_T2F_C4F br; }; /** @struct V3F_T2F_Quad * */ -struct AX_DLL V3F_T2F_Quad +struct V3F_T2F_Quad { /// bottom left V3F_T2F bl; diff --git a/core/base/UTF8.cpp b/core/base/UTF8.cpp index 76ecbd13bc98..c903a2baa852 100644 --- a/core/base/UTF8.cpp +++ b/core/base/UTF8.cpp @@ -117,6 +117,35 @@ std::string vformat(const char* format, va_list ap) return buf; } //#endif + + +std::string_view ltrim(std::string_view s) +{ + if(!s.empty()) { + auto first = s.data(); + auto last = first + s.length(); + while(first < last && std::isspace(*first)) { + ++first; + } + return std::string_view{first, static_cast(last - first)}; + } + return s; +} + +std::string_view rtrim(std::string_view s) +{ + if(!s.empty()) { + auto first = s.data(); + auto last = first + s.length(); + auto rfirst = last - 1; + while(rfirst > first && std::isspace(*rfirst)) { + --rfirst; + } + return std::string_view{first, static_cast(rfirst + 1 - first)}; + } + return s; +} + /* * @str: the string to search through. * @c: the character to not look for. diff --git a/core/base/UTF8.h b/core/base/UTF8.h index f4dc22ee7799..54536e8bef55 100644 --- a/core/base/UTF8.h +++ b/core/base/UTF8.h @@ -68,10 +68,17 @@ inline std::string toString(T arg) } //#ifndef AX_CORE_PROFILE // DEPRECATED since axmol-2.1.4, use fmt::format instead -std::string AX_DLL format(const char* format, ...) AX_FORMAT_PRINTF(1, 2); -std::string AX_DLL vformat(const char* format, va_list ap); +AX_DLL std::string format(const char* format, ...) AX_FORMAT_PRINTF(1, 2); +AX_DLL std::string vformat(const char* format, va_list ap); //#endif +AX_DLL std::string_view ltrim(std::string_view s); +AX_DLL std::string_view rtrim(std::string_view s); +inline std::string_view trim(std::string_view s) +{ + return ltrim(rtrim(s)); +} + /** * @brief Converts from UTF8 string to UTF16 string. * diff --git a/core/base/ZipUtils.cpp b/core/base/ZipUtils.cpp index c8119621ac90..79dd977aa125 100644 --- a/core/base/ZipUtils.cpp +++ b/core/base/ZipUtils.cpp @@ -452,7 +452,7 @@ int ZipUtils::inflateCCZBuffer(const unsigned char* buffer, ssize_t bufferLen, u decodeEncodedPvr(ints, enclen); -#if _AX_DEBUG > 0 +#if defined(_AX_DEBUG) && _AX_DEBUG > 0 // verify checksum in debug mode unsigned int calculated = checksumPvr(ints, enclen); unsigned int required = AX_SWAP_INT32_BIG_TO_HOST(header->reserved); diff --git a/core/base/charconv.h b/core/base/charconv.h new file mode 100644 index 000000000000..7cb028ce91c7 --- /dev/null +++ b/core/base/charconv.h @@ -0,0 +1,18 @@ +#pragma once + +// llvm clang and Apple clang std::from_chars not support floating-point +// use fast_float instead: https://github.com/fastfloat/fast_float +#if !defined(_WIN32) && defined(__clang__) +#include "fast_float/fast_float.h" +namespace axstd { + using fast_float::from_chars; + using from_chars_result = fast_float::from_chars_result; +} + +#else +#include +namespace axstd { + using std::from_chars; + using std::from_chars_result; +} +#endif diff --git a/core/base/filesystem.h b/core/base/filesystem.h index 8a806488a7c9..d806fe4808a3 100644 --- a/core/base/filesystem.h +++ b/core/base/filesystem.h @@ -1,12 +1,5 @@ -// The stdfs workaround header, if not avaiable on current system, use ghc::filesystem +// The filesystem works on ndk r23+ and ios 13+ #pragma once -/* ios < 13 or ndk < 22, ghc as workaround */ -#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000) || \ - (defined(__NDK_MAJOR__) && __NDK_MAJOR__ < 22) -# include "ghc/filesystem.hpp" -namespace stdfs = ghc::filesystem; -#else -# include +#include namespace stdfs = std::filesystem; -#endif diff --git a/core/base/json.h b/core/base/json.h index 6de168546d28..51457f19eb47 100644 --- a/core/base/json.h +++ b/core/base/json.h @@ -1,4 +1,76 @@ #pragma once -#include "rapidjson/rapidjson.h" -#include "rapidjson/document.h" +#include "simdjson/simdjson.h" + +namespace simdjson +{ + +/** + * @addtogroup base + * @{ + */ +/** + @brief A dedicated class for easy load padded string for simdjson parser. + + The only legal use case is: + + PaddedString strJson; + FileUtils::getInstance()->getContents(filePath, &strJson); + ondemand::parser parser; + ondemand::document settings = parser.iterate(strJson); + */ +class PaddedString final +{ +public: + PaddedString() = default; + ~PaddedString() { delete[] data_ptr; } + + PaddedString(PaddedString&& o) noexcept : viable_size(o.viable_size), data_ptr(o.data_ptr) + { + o.data_ptr = nullptr; // we take ownership + } + PaddedString& operator=(PaddedString&& o) noexcept + { + swap(o); + return *this; + } + void swap(PaddedString& o) noexcept + { + std::swap(viable_size, o.viable_size); + std::swap(data_ptr, o.data_ptr); + } + + using value_type = char; + size_t size() const { return viable_size; } + void resize(size_t size) + { + assert(!data_ptr || size <= capacity); // not allow enlarge + if (!data_ptr && size > 0) + { + capacity = size; + data_ptr = internal::allocate_padded_buffer(size); + } + viable_size = size; + } + + char* data() { return data_ptr; } + const char* data() const { return data_ptr; } + + void clear() { resize(0); } + + operator padded_string_view() const noexcept + { + return padded_string_view(data(), size(), size() + SIMDJSON_PADDING); + } + +private: + PaddedString(const PaddedString&) = delete; + size_t viable_size{0}; + size_t capacity{0}; + char* data_ptr{nullptr}; +}; + +// end of base group +/** @} */ + +} // namespace ax::simdjson diff --git a/core/cocos2d.h b/core/cocos2d.h index 75d469bb46e9..091e7b0dc0a7 100644 --- a/core/cocos2d.h +++ b/core/cocos2d.h @@ -40,9 +40,6 @@ extensions use it, i.g fairygui, live2d ... namespace ax { - -AX_DLL const char* cocos2dVersion(); - /** Backward compatibility with old axmol projects */ using Sprite3D = MeshRenderer; @@ -63,12 +60,6 @@ namespace cocos2d = ax; #define CCASSERT AXASSERT #define CC_ASSERT AX_ASSERT #define CC_CONSTRUCTOR_ACCESS public -#ifndef AX_CORE_PROFILE -# define CCLOG AXLOG -# define CCLOGINFO AXLOGINFO -# define CCLOGWARN AXLOGWARN -# define CCLOGERROR AXLOGERROR -#endif #define CC_SAFE_RETAIN AX_SAFE_RETAIN diff --git a/core/math/Color.cpp b/core/math/Color.cpp index c337b6ea5cba..a46f4c856a2b 100644 --- a/core/math/Color.cpp +++ b/core/math/Color.cpp @@ -41,14 +41,14 @@ bool Color3B::operator==(const Color3B& right) const return (r == right.r && g == right.g && b == right.b); } -bool Color3B::operator==(const Color4B& right) const +bool Color3B::operator==(const Color32& right) const { return (r == right.r && g == right.g && b == right.b && 255 == right.a); } -bool Color3B::operator==(const Color4F& right) const +bool Color3B::operator==(const Color& right) const { - return (right.a == 1.0f && Color4F(*this) == right); + return (right.a == 1.0f && Color(*this) == right); } bool Color3B::operator!=(const Color3B& right) const @@ -56,70 +56,70 @@ bool Color3B::operator!=(const Color3B& right) const return !(*this == right); } -bool Color3B::operator!=(const Color4B& right) const +bool Color3B::operator!=(const Color32& right) const { return !(*this == right); } -bool Color3B::operator!=(const Color4F& right) const +bool Color3B::operator!=(const Color& right) const { return !(*this == right); } /** - * Color4B + * Color32 */ -bool Color4B::operator==(const Color4B& right) const +bool Color32::operator==(const Color32& right) const { return (r == right.r && g == right.g && b == right.b && a == right.a); } -bool Color4B::operator==(const Color3B& right) const +bool Color32::operator==(const Color3B& right) const { return (r == right.r && g == right.g && b == right.b && a == 255); } -bool Color4B::operator==(const Color4F& right) const +bool Color32::operator==(const Color& right) const { - return (*this == Color4B(right)); + return (*this == Color32(right)); } -bool Color4B::operator!=(const Color4B& right) const +bool Color32::operator!=(const Color32& right) const { return !(*this == right); } -bool Color4B::operator!=(const Color3B& right) const +bool Color32::operator!=(const Color3B& right) const { return !(*this == right); } -bool Color4B::operator!=(const Color4F& right) const +bool Color32::operator!=(const Color& right) const { return !(*this == right); } /** - * Color4F + * Color */ -bool Color4F::operator==(const Color3B& right) const +bool Color::operator==(const Color3B& right) const { return (a == 1.0f && Color3B(*this) == right); } -bool Color4F::operator==(const Color4B& right) const +bool Color::operator==(const Color32& right) const { - return (*this == Color4F(right)); + return (*this == Color(right)); } -bool Color4F::operator!=(const Color3B& right) const +bool Color::operator!=(const Color3B& right) const { return !(*this == right); } -bool Color4F::operator!=(const Color4B& right) const +bool Color::operator!=(const Color32& right) const { return !(*this == right); } @@ -138,48 +138,48 @@ const Color3B Color3B::BLACK(0, 0, 0); const Color3B Color3B::ORANGE(255, 127, 0); const Color3B Color3B::GRAY(166, 166, 166); -const Color4B Color4B::WHITE(255, 255, 255, 255); -const Color4B Color4B::YELLOW(255, 255, 0, 255); -const Color4B Color4B::GREEN(0, 255, 0, 255); -const Color4B Color4B::BLUE(0, 0, 255, 255); -const Color4B Color4B::RED(255, 0, 0, 255); -const Color4B Color4B::MAGENTA(255, 0, 255, 255); -const Color4B Color4B::BLACK(0, 0, 0, 255); -const Color4B Color4B::ORANGE(255, 127, 0, 255); -const Color4B Color4B::GRAY(166, 166, 166, 255); -const Color4B Color4B::TRANSPARENT(0, 0, 0, 0); - -const Color4F Color4F::WHITE(1, 1, 1, 1); -const Color4F Color4F::YELLOW(1, 1, 0, 1); -const Color4F Color4F::GREEN(0, 1, 0, 1); -const Color4F Color4F::BLUE(0, 0, 1, 1); -const Color4F Color4F::RED(1, 0, 0, 1); -const Color4F Color4F::MAGENTA(1, 0, 1, 1); -const Color4F Color4F::BLACK(0, 0, 0, 1); -const Color4F Color4F::ORANGE(1, 0.5f, 0, 1); -const Color4F Color4F::GRAY(0.65f, 0.65f, 0.65f, 1); -const Color4F Color4F::TRANSPARENT(0, 0, 0, 0); +const Color32 Color32::WHITE(255, 255, 255, 255); +const Color32 Color32::YELLOW(255, 255, 0, 255); +const Color32 Color32::GREEN(0, 255, 0, 255); +const Color32 Color32::BLUE(0, 0, 255, 255); +const Color32 Color32::RED(255, 0, 0, 255); +const Color32 Color32::MAGENTA(255, 0, 255, 255); +const Color32 Color32::BLACK(0, 0, 0, 255); +const Color32 Color32::ORANGE(255, 127, 0, 255); +const Color32 Color32::GRAY(166, 166, 166, 255); +const Color32 Color32::TRANSPARENT(0, 0, 0, 0); + +const Color Color::WHITE(1, 1, 1, 1); +const Color Color::YELLOW(1, 1, 0, 1); +const Color Color::GREEN(0, 1, 0, 1); +const Color Color::BLUE(0, 0, 1, 1); +const Color Color::RED(1, 0, 0, 1); +const Color Color::MAGENTA(1, 0, 1, 1); +const Color Color::BLACK(0, 0, 0, 1); +const Color Color::ORANGE(1, 0.5f, 0, 1); +const Color Color::GRAY(0.65f, 0.65f, 0.65f, 1); +const Color Color::TRANSPARENT(0, 0, 0, 0); HSV::HSV() {} -HSV::HSV(float _h, float _s, float _v, float _a) : Vec4Base(_h, _s, _v, _a) {} +HSV::HSV(float _h, float _s, float _v, float _a) : Vec4Adapter(_h, _s, _v, _a) {} HSV::HSV(const Color3B& c) { - fromRgba(Color4F(c)); + fromRgba(Color(c)); }; -HSV::HSV(const Color4B& c) +HSV::HSV(const Color32& c) { - fromRgba(Color4F(c)); + fromRgba(Color(c)); } -HSV::HSV(const Color4F& c) +HSV::HSV(const Color& c) { fromRgba(c); } -void HSV::fromRgba(const Color4F& rgba) +void HSV::fromRgba(const Color& rgba) { float fcmax = MAX(MAX(rgba.r, rgba.g), rgba.b); float fcmin = MIN(MIN(rgba.r, rgba.g), rgba.b); @@ -226,9 +226,9 @@ void HSV::fromRgba(const Color4F& rgba) this->a = rgba.a; } -Color4F HSV::toRgba() const +Color HSV::toRgba() const { - auto rgba = Color4F(0, 0, 0, a); + auto rgba = Color(0, 0, 0, a); float hue = remainder(std::fabs(h), 360); hue += 360; @@ -293,35 +293,30 @@ Color3B HSV::toColor3B() const return Color3B(toRgba()); } -Color4B HSV::toColor4B() const +Color32 HSV::toColor32() const { - return Color4B(toRgba()); -} - -Color4F HSV::toColor4F() const -{ - return toRgba(); + return Color32(toRgba()); } HSL::HSL() {} -HSL::HSL(float _h, float _s, float _l, float _a) : Vec4Base(_h, _s, _l, _a) {} +HSL::HSL(float _h, float _s, float _l, float _a) : Vec4Adapter(_h, _s, _l, _a) {} HSL::HSL(const Color3B& c) { - fromRgba(Color4F(c)); + fromRgba(Color(c)); } -HSL::HSL(const Color4B& c) +HSL::HSL(const Color32& c) { - fromRgba(Color4F(c)); + fromRgba(Color(c)); } -HSL::HSL(const Color4F& c) +HSL::HSL(const Color& c) { fromRgba(c); } -void HSL::fromRgba(const Color4F& rgba) +void HSL::fromRgba(const Color& rgba) { float max = MAX(MAX(rgba.r, rgba.g), rgba.b); float min = MIN(MIN(rgba.r, rgba.g), rgba.b); @@ -372,9 +367,9 @@ float HSL::hue2rgb(float p, float q, float t) return p; } -Color4F HSL::toRgba() const +Color HSL::toRgba() const { - auto rgba = Color4F(0, 0, 0, a); + auto rgba = Color(0, 0, 0, a); float hue = remainder(std::fabs(h), 360); hue += 360; @@ -401,14 +396,9 @@ Color3B HSL::toColor3B() const return Color3B(toRgba()); } -Color4B HSL::toColor4B() const -{ - return Color4B(toRgba()); -} - -Color4F HSL::toColor4F() const +Color32 HSL::toColor32() const { - return Color4F(toRgba()); + return Color32(toRgba()); } NS_AX_MATH_END diff --git a/core/math/Color.h b/core/math/Color.h index 3bcdd35eb47b..e98932cbb75e 100644 --- a/core/math/Color.h +++ b/core/math/Color.h @@ -34,8 +34,8 @@ THE SOFTWARE. NS_AX_MATH_BEGIN -struct Color4B; -struct Color4F; +struct Color32; +struct Color; struct HSV; /** @@ -46,15 +46,15 @@ struct AX_DLL Color3B { Color3B() {}; Color3B(uint8_t _r, uint8_t _g, uint8_t _b) : r(_r), g(_g), b(_b) {} - explicit Color3B(const Color4B& color); - explicit Color3B(const Color4F& color); + explicit Color3B(const Color32& color); + explicit Color3B(const Color& color); bool operator==(const Color3B& right) const; - bool operator==(const Color4B& right) const; - bool operator==(const Color4F& right) const; + bool operator==(const Color32& right) const; + bool operator==(const Color& right) const; bool operator!=(const Color3B& right) const; - bool operator!=(const Color4B& right) const; - bool operator!=(const Color4F& right) const; + bool operator!=(const Color32& right) const; + bool operator!=(const Color& right) const; bool equals(const Color3B& other) const { return (*this == other); } @@ -77,12 +77,12 @@ struct AX_DLL Color3B * RGBA color composed of 4 bytes. * @since v3.0 */ -struct AX_DLL Color4B +struct AX_DLL Color32 { - Color4B() {} - Color4B(uint8_t _r, uint8_t _g, uint8_t _b, uint8_t _a) : r(_r), g(_g), b(_b), a(_a) {} - explicit Color4B(const Color3B& color, uint8_t _a = 255) : r(color.r), g(color.g), b(color.b), a(_a) {} - Color4B(const Color4F& color); + Color32() {} + Color32(uint8_t _r, uint8_t _g, uint8_t _b, uint8_t _a) : r(_r), g(_g), b(_b), a(_a) {} + explicit Color32(const Color3B& color, uint8_t _a = 255) : r(color.r), g(color.g), b(color.b), a(_a) {} + Color32(const Color& color); inline void set(uint8_t _r, uint8_t _g, uint8_t _b, uint8_t _a) { @@ -92,64 +92,82 @@ struct AX_DLL Color4B a = _a; } - bool operator==(const Color4B& right) const; + bool operator==(const Color32& right) const; bool operator==(const Color3B& right) const; - bool operator==(const Color4F& right) const; - bool operator!=(const Color4B& right) const; + bool operator==(const Color& right) const; + bool operator!=(const Color32& right) const; bool operator!=(const Color3B& right) const; - bool operator!=(const Color4F& right) const; + bool operator!=(const Color& right) const; uint8_t r = 0; uint8_t g = 0; uint8_t b = 0; uint8_t a = 0; - static const Color4B WHITE; - static const Color4B YELLOW; - static const Color4B BLUE; - static const Color4B GREEN; - static const Color4B RED; - static const Color4B MAGENTA; - static const Color4B BLACK; - static const Color4B ORANGE; - static const Color4B GRAY; - static const Color4B TRANSPARENT; // TRANSPARENT is defined on wingdi.h /*Background Modes*/ + static const Color32 WHITE; + static const Color32 YELLOW; + static const Color32 BLUE; + static const Color32 GREEN; + static const Color32 RED; + static const Color32 MAGENTA; + static const Color32 BLACK; + static const Color32 ORANGE; + static const Color32 GRAY; + static const Color32 TRANSPARENT; // TRANSPARENT is defined on wingdi.h /*Background Modes*/ }; /** * RGBA color composed of 4 floats. - * @since v3.0 + * @since v3.0, renamed from Color4F */ -struct AX_DLL Color4F : public Vec4Base +struct AX_DLL Color : public Vec4Adapter { - using Vec4Base = Vec4Base; - - Color4F() {} - Color4F(float _r, float _g, float _b, float _a) : Vec4Base(_r, _g, _b, _a) {} - explicit Color4F(const Color3B& color, float _a = 1.0f) - : Vec4Base(color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, _a) + Color() {} + Color(float _r, float _g, float _b, float _a) : Vec4Adapter(_r, _g, _b, _a) {} + explicit Color(const Color3B& color, float _a = 1.0f) + : Vec4Adapter(color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, _a) + {} + explicit Color(const Color32& color) + : Vec4Adapter(color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f) {} - explicit Color4F(const Color4B& color) - : Vec4Base(color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f) + template + explicit Color(const _Other& color) : Vec4Adapter(color.r, color.g, color.b, color.a) {} + static Color fromHex(unsigned int v) + { + auto r = (v >> 24) & 0xff; + auto g = (v >> 16) & 0xff; + auto b = (v >> 8) & 0xff; + auto a = v & 0xff; + return Color{r / 255.f, g / 255.f, b / 255.f, a / 255.f}; + } + + inline Color& premultiplyAlpha() + { + r *= a; + g *= a; + b *= a; + return *this; + } + bool operator==(const Color3B& rhs) const; - bool operator==(const Color4B& rhs) const; + bool operator==(const Color32& rhs) const; bool operator!=(const Color3B& rhs) const; - bool operator!=(const Color4B& rhs) const; - - bool equals(const Color4F& other) const { return (*this == other); } - - static const Color4F WHITE; - static const Color4F YELLOW; - static const Color4F BLUE; - static const Color4F GREEN; - static const Color4F RED; - static const Color4F MAGENTA; - static const Color4F BLACK; - static const Color4F ORANGE; - static const Color4F GRAY; - static const Color4F TRANSPARENT; // TRANSPARENT is defined on wingdi.h /*Background Modes*/ + bool operator!=(const Color32& rhs) const; + + bool equals(const Color& other) const { return (*this == other); } + + static const Color WHITE; + static const Color YELLOW; + static const Color BLUE; + static const Color GREEN; + static const Color RED; + static const Color MAGENTA; + static const Color BLACK; + static const Color ORANGE; + static const Color GRAY; + static const Color TRANSPARENT; // TRANSPARENT is defined on wingdi.h /*Background Modes*/ }; /** @@ -158,23 +176,22 @@ struct AX_DLL Color4F : public Vec4Base * * Implementation source: https://gist.github.com/fairlight1337/4935ae72bcbcc1ba5c72 */ -struct AX_DLL HSV : public Vec4Base +struct AX_DLL HSV : public Vec4Adapter { HSV(); HSV(float _h, float _s, float _v, float _a = 1.0F); explicit HSV(const Color3B& c); - explicit HSV(const Color4B& c); - explicit HSV(const Color4F& c); + explicit HSV(const Color32& c); + explicit HSV(const Color& c); bool equals(const HSV& other) const { return (*this == other); } - void fromRgba(const Color4F& rgba); - Color4F toRgba() const; + void fromRgba(const Color& rgba); + Color toRgba() const; Color3B toColor3B() const; - Color4B toColor4B() const; - Color4F toColor4F() const; + Color32 toColor32() const; }; /** @@ -183,31 +200,30 @@ struct AX_DLL HSV : public Vec4Base * * Implementation source: https://gist.github.com/ciembor/1494530 */ -struct AX_DLL HSL : public Vec4Base +struct AX_DLL HSL : public Vec4Adapter { HSL(); HSL(float _h, float _s, float _l, float _a = 1.0F); explicit HSL(const Color3B& c); - explicit HSL(const Color4B& c); - explicit HSL(const Color4F& c); + explicit HSL(const Color32& c); + explicit HSL(const Color& c); bool equals(const HSL& other) const { return (*this == other); } - void fromRgba(const Color4F& rgba); - Color4F toRgba() const; + void fromRgba(const Color& rgba); + Color toRgba() const; static float hue2rgb(float p, float q, float t); Color3B toColor3B() const; - Color4B toColor4B() const; - Color4F toColor4F() const; + Color32 toColor32() const; }; -inline Color3B::Color3B(const Color4B& color) : r(color.r), g(color.g), b(color.b) {} -inline Color3B::Color3B(const Color4F& color) : r(color.r * 255.0f), g(color.g * 255.0f), b(color.b * 255.0f) {} +inline Color3B::Color3B(const Color32& color) : r(color.r), g(color.g), b(color.b) {} +inline Color3B::Color3B(const Color& color) : r(color.r * 255.0f), g(color.g * 255.0f), b(color.b * 255.0f) {} -inline Color4B::Color4B(const Color4F& color) : r(color.r * 255), g(color.g * 255), b(color.b * 255), a(color.a * 255) {} +inline Color32::Color32(const Color& color) : r(color.r * 255), g(color.g * 255), b(color.b * 255), a(color.a * 255) {} NS_AX_MATH_END diff --git a/core/math/MathUtil.cpp b/core/math/MathUtil.cpp index f2ca2ce8dd1d..b5e4f7a2cdf4 100644 --- a/core/math/MathUtil.cpp +++ b/core/math/MathUtil.cpp @@ -283,13 +283,13 @@ void MathUtil::crossVec3(const float* v1, const float* v2, float* dst) #endif } -void MathUtil::transformVertices(V3F_C4B_T2F* dst, const V3F_C4B_T2F* src, size_t count, const Mat4& transform) +void MathUtil::transformVertices(V3F_T2F_C4B* dst, const V3F_T2F_C4B* src, size_t count, const Mat4& transform) { // Check some assumptions made by optimizations - static_assert(sizeof(V3F_C4B_T2F) == 24); - static_assert(offsetof(V3F_C4B_T2F, vertices) == 0); - static_assert(offsetof(V3F_C4B_T2F, colors) == 12); - static_assert(offsetof(V3F_C4B_T2F, texCoords) == 16); + static_assert(sizeof(V3F_T2F_C4B) == 24); + static_assert(offsetof(V3F_T2F_C4B, position) == 0); + static_assert(offsetof(V3F_T2F_C4B, texCoord) == 12); + static_assert(offsetof(V3F_T2F_C4B, color) == 20); #if defined(AX_SSE_INTRINSICS) MathUtilSSE::transformVertices(dst, src, count, transform); #elif defined(AX_NEON_INTRINSICS) diff --git a/core/math/MathUtil.h b/core/math/MathUtil.h index a01f67227e3c..50e1c9eb6b66 100644 --- a/core/math/MathUtil.h +++ b/core/math/MathUtil.h @@ -29,7 +29,7 @@ namespace ax { - struct V3F_C4B_T2F; + struct V3F_T2F_C4B; } /** @@ -119,7 +119,7 @@ class AX_DLL MathUtil static void crossVec3(const float* v1, const float* v2, float* dst); - static void transformVertices(V3F_C4B_T2F* dst, const V3F_C4B_T2F* src, size_t count, const Mat4& transform); + static void transformVertices(V3F_T2F_C4B* dst, const V3F_T2F_C4B* src, size_t count, const Mat4& transform); static void transformIndices(uint16_t* dst, const uint16_t* src, size_t count, uint16_t offset); }; diff --git a/core/math/MathUtil.inl b/core/math/MathUtil.inl index 649cb0426786..d1971cbd11e1 100644 --- a/core/math/MathUtil.inl +++ b/core/math/MathUtil.inl @@ -191,7 +191,7 @@ public: dst[2] = z; } - inline static void transformVertices(V3F_C4B_T2F* dst, const V3F_C4B_T2F* src, size_t count, const Mat4& transform) + inline static void transformVertices(V3F_T2F_C4B* dst, const V3F_T2F_C4B* src, size_t count, const Mat4& transform) { auto end = dst + count; auto& t = transform; // Make copy for better aliasing inference @@ -199,11 +199,12 @@ public: while (dst < end) { - auto pos = src->vertices; - dst->vertices.x = pos.x * m[0] + pos.y * m[4] + pos.z * m[8] + m[12]; - dst->vertices.y = pos.x * m[1] + pos.y * m[5] + pos.z * m[9] + m[13]; - dst->vertices.z = pos.x * m[2] + pos.y * m[6] + pos.z * m[10] + m[14]; - memcpy(&dst->colors, &src->colors, sizeof(V3F_C4B_T2F::colors) + sizeof(V3F_C4B_T2F::texCoords)); + auto pos = src->position; + dst->position.x = pos.x * m[0] + pos.y * m[4] + pos.z * m[8] + m[12]; + dst->position.y = pos.x * m[1] + pos.y * m[5] + pos.z * m[9] + m[13]; + dst->position.z = pos.x * m[2] + pos.y * m[6] + pos.z * m[10] + m[14]; + dst->texCoord = src->texCoord; + dst->color = src->color; ++dst; ++src; } diff --git a/core/math/MathUtilNeon.inl b/core/math/MathUtilNeon.inl index d85324ddc541..1db8ea891bd9 100644 --- a/core/math/MathUtilNeon.inl +++ b/core/math/MathUtilNeon.inl @@ -147,7 +147,7 @@ struct MathUtilNeon } #if AX_64BITS - inline static void transformVertices(V3F_C4B_T2F* dst, const V3F_C4B_T2F* src, size_t count, const Mat4& transform) + inline static void transformVertices(V3F_T2F_C4B* dst, const V3F_T2F_C4B* src, size_t count, const Mat4& transform) { auto end = dst + count; @@ -159,26 +159,26 @@ struct MathUtilNeon while (dst < end4) { // Do this for each vertex - // dst->vertices.x = pos.x * m[0] + pos.y * m[4] + pos.z * m[8] + m[12]; - // dst->vertices.y = pos.x * m[1] + pos.y * m[5] + pos.z * m[9] + m[13]; - // dst->vertices.z = pos.x * m[2] + pos.y * m[6] + pos.z * m[10] + m[14]; + // dst->position.x = pos.x * m[0] + pos.y * m[4] + pos.z * m[8] + m[12]; + // dst->position.y = pos.x * m[1] + pos.y * m[5] + pos.z * m[9] + m[13]; + // dst->position.z = pos.x * m[2] + pos.y * m[6] + pos.z * m[10] + m[14]; // First, load each vertex, multiply x by column 0 and add to column 3 - // Note: since we're reading 4 floats it will load color bytes into v.w - float32x4_t v0 = vld1q_f32(&src[0].vertices.x); + // Note: since we're reading 4 floats it will load uv.u into v.w + float32x4_t v0 = vld1q_f32(&src[0].position.x); float32x4_t r0 = vmlaq_laneq_f32(m.val[3], m.val[0], v0, 0); - float32x4_t v1 = vld1q_f32(&src[1].vertices.x); + float32x4_t v1 = vld1q_f32(&src[1].position.x); float32x4_t r1 = vmlaq_laneq_f32(m.val[3], m.val[0], v1, 0); - float32x4_t v2 = vld1q_f32(&src[2].vertices.x); + float32x4_t v2 = vld1q_f32(&src[2].position.x); float32x4_t r2 = vmlaq_laneq_f32(m.val[3], m.val[0], v2, 0); - float32x4_t v3 = vld1q_f32(&src[3].vertices.x); + float32x4_t v3 = vld1q_f32(&src[3].position.x); float32x4_t r3 = vmlaq_laneq_f32(m.val[3], m.val[0], v3, 0); - // Load texCoords - float32x2_t uv0 = vld1_f32(&src[0].texCoords.u); - float32x2_t uv1 = vld1_f32(&src[1].texCoords.u); - float32x2_t uv2 = vld1_f32(&src[2].texCoords.u); - float32x2_t uv3 = vld1_f32(&src[3].texCoords.u); + // Load texCoord.v and color32 + float32x2_t vc0 = vld1_f32(&src[0].texCoord.v); + float32x2_t vc1 = vld1_f32(&src[1].texCoord.v); + float32x2_t vc2 = vld1_f32(&src[2].texCoord.v); + float32x2_t vc3 = vld1_f32(&src[3].texCoord.v); // Multiply y by column 1 and add to result r0 = vmlaq_laneq_f32(r0, m.val[1], v0, 1); @@ -192,21 +192,21 @@ struct MathUtilNeon r2 = vmlaq_laneq_f32(r2, m.val[2], v2, 2); r3 = vmlaq_laneq_f32(r3, m.val[2], v3, 2); - // Set w to loaded color + // Set w to loaded uv.u r0 = vsetq_lane_f32(vgetq_lane_f32(v0, 3), r0, 3); r1 = vsetq_lane_f32(vgetq_lane_f32(v1, 3), r1, 3); r2 = vsetq_lane_f32(vgetq_lane_f32(v2, 3), r2, 3); r3 = vsetq_lane_f32(vgetq_lane_f32(v3, 3), r3, 3); // Store result - vst1q_f32(&dst[0].vertices.x, r0); - vst1_f32(&dst[0].texCoords.u, uv0); - vst1q_f32(&dst[1].vertices.x, r1); - vst1_f32(&dst[1].texCoords.u, uv1); - vst1q_f32(&dst[2].vertices.x, r2); - vst1_f32(&dst[2].texCoords.u, uv2); - vst1q_f32(&dst[3].vertices.x, r3); - vst1_f32(&dst[3].texCoords.u, uv3); + vst1q_f32(&dst[0].position.x, r0); + vst1_f32(&dst[0].texCoord.v, vc0); + vst1q_f32(&dst[1].position.x, r1); + vst1_f32(&dst[1].texCoord.v, vc1); + vst1q_f32(&dst[2].position.x, r2); + vst1_f32(&dst[2].texCoord.v, vc2); + vst1q_f32(&dst[3].position.x, r3); + vst1_f32(&dst[3].texCoord.v, vc3); dst += 4; src += 4; @@ -215,14 +215,14 @@ struct MathUtilNeon // Process remaining vertices one by one while (dst < end) { - float32x4_t v = vld1q_f32(&src->vertices.x); + float32x4_t v = vld1q_f32(&src->position.x); float32x4_t r = vmlaq_laneq_f32(m.val[3], m.val[0], v, 0); r = vmlaq_laneq_f32(r, m.val[1], v, 1); r = vmlaq_laneq_f32(r, m.val[2], v, 2); r = vsetq_lane_f32(vgetq_lane_f32(v, 3), r, 3); - float32x2_t uv = vld1_f32(&src->texCoords.u); - vst1q_f32(&dst->vertices.x, r); - vst1_f32(&dst->texCoords.u, uv); + float32x2_t vc = vld1_f32(&src->texCoord.v); + vst1q_f32(&dst->position.x, r); + vst1_f32(&dst->texCoord.v, vc); ++dst; ++src; @@ -280,8 +280,8 @@ struct MathUtilNeon } } #else - inline static void transformVertices(ax::V3F_C4B_T2F* dst, - const ax::V3F_C4B_T2F* src, + inline static void transformVertices(ax::V3F_T2F_C4B* dst, + const ax::V3F_T2F_C4B* src, size_t count, const ax::Mat4& transform) { @@ -297,19 +297,19 @@ struct MathUtilNeon auto end4 = dst + count / 4 * 4; while (dst < end4) { - // Load 4 vertices. Note that color will also get loaded into w - float32x2_t xy0 = vld1_f32(&src[0].vertices.x); - float32x2_t zw0 = vld1_f32(&src[0].vertices.z); - float32x2_t uv0 = vld1_f32(&src[0].texCoords.u); - float32x2_t xy1 = vld1_f32(&src[1].vertices.x); - float32x2_t zw1 = vld1_f32(&src[1].vertices.z); - float32x2_t uv1 = vld1_f32(&src[1].texCoords.u); - float32x2_t xy2 = vld1_f32(&src[2].vertices.x); - float32x2_t zw2 = vld1_f32(&src[2].vertices.z); - float32x2_t uv2 = vld1_f32(&src[2].texCoords.u); - float32x2_t xy3 = vld1_f32(&src[3].vertices.x); - float32x2_t zw3 = vld1_f32(&src[3].vertices.z); - float32x2_t uv3 = vld1_f32(&src[3].texCoords.u); + // Load 4 vertices. Note that texCoord.u will also get loaded into w + float32x2_t xy0 = vld1_f32(&src[0].position.x); + float32x2_t zw0 = vld1_f32(&src[0].position.z); + //float32x2_t uv0 = vld1_f32(&src[0].texCoord.u); + float32x2_t xy1 = vld1_f32(&src[1].position.x); + float32x2_t zw1 = vld1_f32(&src[1].position.z); + //float32x2_t uv1 = vld1_f32(&src[1].texCoord.u); + float32x2_t xy2 = vld1_f32(&src[2].position.x); + float32x2_t zw2 = vld1_f32(&src[2].position.z); + //float32x2_t uv2 = vld1_f32(&src[2].texCoord.u); + float32x2_t xy3 = vld1_f32(&src[3].position.x); + float32x2_t zw3 = vld1_f32(&src[3].position.z); + //float32x2_t uv3 = vld1_f32(&src[3].texCoord.u); // Multiply x by column 0 float32x4_t r0 = vmulq_lane_f32(mc0, xy0, 0); @@ -335,21 +335,27 @@ struct MathUtilNeon r2 = vaddq_f32(r2, mc3); r3 = vaddq_f32(r3, mc3); - // Set color + // Set uv.u r0 = vsetq_lane_f32(vget_lane_f32(zw0, 1), r0, 3); r1 = vsetq_lane_f32(vget_lane_f32(zw1, 1), r1, 3); r2 = vsetq_lane_f32(vget_lane_f32(zw2, 1), r2, 3); r3 = vsetq_lane_f32(vget_lane_f32(zw3, 1), r3, 3); + // Load texCoords.v and color32 + float32x2_t vc0 = vld1_f32(&src[0].texCoord.v); + float32x2_t vc1 = vld1_f32(&src[1].texCoord.v); + float32x2_t vc2 = vld1_f32(&src[2].texCoord.v); + float32x2_t vc3 = vld1_f32(&src[3].texCoord.v); + // Store result - vst1q_f32(&dst[0].vertices.x, r0); - vst1_f32(&dst[0].texCoords.u, uv0); - vst1q_f32(&dst[1].vertices.x, r1); - vst1_f32(&dst[1].texCoords.u, uv1); - vst1q_f32(&dst[2].vertices.x, r2); - vst1_f32(&dst[2].texCoords.u, uv2); - vst1q_f32(&dst[3].vertices.x, r3); - vst1_f32(&dst[3].texCoords.u, uv3); + vst1q_f32(&dst[0].position.x, r0); + vst1_f32(&dst[0].texCoord.v, vc0); + vst1q_f32(&dst[1].position.x, r1); + vst1_f32(&dst[1].texCoord.v, vc1); + vst1q_f32(&dst[2].position.x, r2); + vst1_f32(&dst[2].texCoord.v, vc2); + vst1q_f32(&dst[3].position.x, r3); + vst1_f32(&dst[3].texCoord.v, vc3); dst += 4; src += 4; @@ -359,9 +365,9 @@ struct MathUtilNeon while (dst < end) { // Load vertex - float32x2_t xy = vld1_f32(&src->vertices.x); - float32x2_t zw = vld1_f32(&src->vertices.z); - float32x2_t uv = vld1_f32(&src->texCoords.u); + float32x2_t xy = vld1_f32(&src->position.x); + float32x2_t zw = vld1_f32(&src->position.z); + float32x2_t vc = vld1_f32(&src->texCoord.v); // Multiply x by column 0 float32x4_t r = vmulq_lane_f32(mc0, xy, 0); @@ -376,8 +382,8 @@ struct MathUtilNeon r = vsetq_lane_f32(vget_lane_f32(zw, 1), r, 3); // Store result - vst1q_f32(&dst->vertices.x, r); - vst1_f32(&dst->texCoords.u, uv); + vst1q_f32(&dst->position.x, r); + vst1_f32(&dst->texCoord.v, vc); ++dst; ++src; diff --git a/core/math/MathUtilSSE.inl b/core/math/MathUtilSSE.inl index 4869fe98b1de..26dc9d5cf051 100644 --- a/core/math/MathUtilSSE.inl +++ b/core/math/MathUtilSSE.inl @@ -230,23 +230,27 @@ struct MathUtilSSE # endif } - static void transformVertices(V3F_C4B_T2F* dst, const V3F_C4B_T2F* src, size_t count, const Mat4& transform) + static void transformVertices(V3F_T2F_C4B* dst, const V3F_T2F_C4B* src, size_t count, const Mat4& transform) { auto& m = transform.col; for (size_t i = 0; i < count; ++i) { - auto& vert = src[i].vertices; + auto& vert = src[i].position; __m128 v = _mm_set_ps(1.0f, vert.z, vert.y, vert.x); v = _mm_add_ps( _mm_add_ps(_mm_mul_ps(m[0], _mm_shuffle_ps(v, v, 0)), _mm_mul_ps(m[1], _mm_shuffle_ps(v, v, 0x55))), _mm_add_ps(_mm_mul_ps(m[2], _mm_shuffle_ps(v, v, 0xaa)), _mm_mul_ps(m[3], _mm_shuffle_ps(v, v, 0xff)))); - _mm_storeu_ps((float*)&dst[i].vertices, v); - - // Copy tex coords and colors - // dst[i].texCoords = src[i].texCoords; - // dst[i].colors = src[i].colors; - memcpy(&dst[i].colors, &src[i].colors, sizeof(V3F_C4B_T2F::colors) + sizeof(V3F_C4B_T2F::texCoords)); + _mm_storeu_ps((float*)&dst[i].position, v); + + // Copy tex coord and color (12 bytes) + // memcpy(&dst[i].texCoord, &src[i].texCoord, sizeof(V3F_T2F_C4B::color) + sizeof(V3F_T2F_C4B::texCoord)); + dst[i].texCoord.u = src[i].texCoord.u; + dst[i].texCoord.v = src[i].texCoord.v; + dst[i].color.r = src[i].color.r; + dst[i].color.g = src[i].color.g; + dst[i].color.b = src[i].color.b; + dst[i].color.a = src[i].color.a; } } diff --git a/core/math/Quaternion.h b/core/math/Quaternion.h index 21349a807100..9268f0b5b48d 100644 --- a/core/math/Quaternion.h +++ b/core/math/Quaternion.h @@ -78,22 +78,14 @@ class AX_DLL Quaternion friend class Transform; public: - /** - * The x-value of the quaternion's vector component. - */ - float x; - /** - * The y-value of the quaternion's vector component. - */ - float y; - /** - * The z-value of the quaternion's vector component. - */ - float z; - /** - * The scalar component of the quaternion. - */ - float w; + union + { + struct + { + float x, y, z, w; + }; + float comps[4]; + }; /** * Constructs a quaternion initialized to (0, 0, 0, 1). diff --git a/core/math/Vec3.cpp b/core/math/Vec3.cpp index 60c9235eda99..47293d01ba04 100644 --- a/core/math/Vec3.cpp +++ b/core/math/Vec3.cpp @@ -33,22 +33,6 @@ NS_AX_MATH_BEGIN const Vec3 Vec3::UNIT_Z(0.0f, 0.0f, 1.0f); #endif - -Vec3 Vec3::fromColor(unsigned int color) -{ - float components[3]; - int componentIndex = 0; - for (int i = 2; i >= 0; --i) - { - int component = (color >> i * 8) & 0x0000ff; - - components[componentIndex++] = static_cast(component) / 255.0f; - } - - Vec3 value(components); - return value; -} - float Vec3::angle(const Vec3& v1, const Vec3& v2) { float dx = v1.y * v2.z - v1.z * v2.y; diff --git a/core/math/Vec3.h b/core/math/Vec3.h index 014c96f05c06..0366b985f5d7 100644 --- a/core/math/Vec3.h +++ b/core/math/Vec3.h @@ -49,20 +49,18 @@ class Quaternion; class AX_DLL Vec3 { public: - /** - * The x-coordinate. - */ - float x; + union + { + float comps[3]; - /** - * The y-coordinate. - */ - float y; - - /** - * The z-coordinate. - */ - float z; + // The coord alias + struct + { + float x; + float y; + float z; + }; + }; /** * Constructs a new vector initialized to all zeros. @@ -93,16 +91,6 @@ class AX_DLL Vec3 */ constexpr Vec3(const Vec3& p1, const Vec3& p2) { set(p1, p2); } - /** - * Creates a new vector from an integer interpreted as an RGB value. - * E.g. 0xff0000 represents red or the vector (1, 0, 0). - * - * @param color The integer to interpret as an RGB value. - * - * @return A vector corresponding to the interpreted RGB color. - */ - static Vec3 fromColor(unsigned int color); - /** * Indicates whether this vector contains all zeros. * diff --git a/core/math/Vec4.cpp b/core/math/Vec4.cpp index 3d26db646e3c..464c3805312d 100644 --- a/core/math/Vec4.cpp +++ b/core/math/Vec4.cpp @@ -37,52 +37,8 @@ NS_AX_MATH_BEGIN const Vec4 Vec4::UNIT_W = Vec4(0.0f, 0.0f, 0.0f, 1.0f); #endif - -Vec4 Vec4::fromColor(unsigned int color) -{ - float components[4]; - int componentIndex = 0; - for (int i = 3; i >= 0; --i) - { - int component = (color >> i * 8) & 0x000000ff; - - components[componentIndex++] = static_cast(component) / 255.0f; - } - - Vec4 value(components); - return value; -} - -bool Vec4::isZero() const -{ - return x == 0.0f && y == 0.0f && z == 0.0f && w == 0.0f; -} - -bool Vec4::isOne() const -{ - return x == 1.0f && y == 1.0f && z == 1.0f && w == 1.0f; -} - -float Vec4::angle(const Vec4& v1, const Vec4& v2) -{ - float dx = v1.w * v2.x - v1.x * v2.w - v1.y * v2.z + v1.z * v2.y; - float dy = v1.w * v2.y - v1.y * v2.w - v1.z * v2.x + v1.x * v2.z; - float dz = v1.w * v2.z - v1.z * v2.w - v1.x * v2.y + v1.y * v2.x; - - return std::atan2(std::sqrt(dx * dx + dy * dy + dz * dz) + MATH_FLOAT_SMALL, dot(v1, v2)); -} - -void Vec4::add(const Vec4& v1, const Vec4& v2, Vec4* dst) -{ - AX_ASSERT(dst); - - dst->x = v1.x + v2.x; - dst->y = v1.y + v2.y; - dst->z = v1.z + v2.z; - dst->w = v1.w + v2.w; -} - -void Vec4::clamp(const Vec4& min, const Vec4& max) + +void Vec4Base::clamp(const Vec4Base& min, const Vec4Base& max) { AX_ASSERT(!(min.x > max.x || min.y > max.y || min.z > max.z || min.w > max.w)); @@ -111,7 +67,7 @@ void Vec4::clamp(const Vec4& min, const Vec4& max) w = max.w; } -void Vec4::clamp(const Vec4& v, const Vec4& min, const Vec4& max, Vec4* dst) +void Vec4Base::clamp(const Vec4Base& v, const Vec4Base& min, const Vec4Base& max, Vec4Base* dst) { AX_ASSERT(dst); AX_ASSERT(!(min.x > max.x || min.y > max.y || min.z > max.z || min.w > max.w)); @@ -145,6 +101,35 @@ void Vec4::clamp(const Vec4& v, const Vec4& min, const Vec4& max, Vec4* dst) dst->w = max.w; } +bool Vec4::isZero() const +{ + return x == 0.0f && y == 0.0f && z == 0.0f && w == 0.0f; +} + +bool Vec4::isOne() const +{ + return x == 1.0f && y == 1.0f && z == 1.0f && w == 1.0f; +} + +float Vec4::angle(const Vec4& v1, const Vec4& v2) +{ + float dx = v1.w * v2.x - v1.x * v2.w - v1.y * v2.z + v1.z * v2.y; + float dy = v1.w * v2.y - v1.y * v2.w - v1.z * v2.x + v1.x * v2.z; + float dz = v1.w * v2.z - v1.z * v2.w - v1.x * v2.y + v1.y * v2.x; + + return std::atan2(std::sqrt(dx * dx + dy * dy + dz * dz) + MATH_FLOAT_SMALL, dot(v1, v2)); +} + +void Vec4::add(const Vec4& v1, const Vec4& v2, Vec4* dst) +{ + AX_ASSERT(dst); + + dst->x = v1.x + v2.x; + dst->y = v1.y + v2.y; + dst->z = v1.z + v2.z; + dst->w = v1.w + v2.w; +} + float Vec4::distance(const Vec4& v) const { float dx = v.x - x; diff --git a/core/math/Vec4.h b/core/math/Vec4.h index 7541fb59eeb1..5069e3e5966d 100644 --- a/core/math/Vec4.h +++ b/core/math/Vec4.h @@ -36,18 +36,73 @@ NS_AX_MATH_BEGIN class Mat4; -/* - * @brief A 4-floats class template with base math operators - */ -template -class Vec4Base +struct AX_DLL Vec4Base { -public: - using impl_type = _ImplType; - constexpr Vec4Base() : x(0.0f), y(0.0f), z(0.0f), w(0.0f) {} constexpr Vec4Base(float xx, float yy, float zz, float ww) : x(xx), y(yy), z(zz), w(ww) {} - constexpr explicit Vec4Base(const float* src) { this->set(src); } + explicit Vec4Base(const float* src) { this->set(src); } + + /** + * Sets the elements of this vector to those in the specified vector. + * + * @param v The vector to copy. + */ + void set(const Vec4Base& v) + { + this->x = v.x; + this->y = v.y; + this->z = v.z; + this->w = v.w; + } + + /** + * Sets the elements of this vector to the specified values. + * + * @param xx The new x coordinate. + * @param yy The new y coordinate. + * @param zz The new z coordinate. + * @param ww The new w coordinate. + */ + constexpr void set(float xx, float yy, float zz, float ww) + { + this->x = xx; + this->y = yy; + this->z = zz; + this->w = ww; + } + + /** + * Sets the elements of this vector from the values in the specified array. + * + * @param array An array containing the elements of the vector in the order x, y, z, w. + */ + void set(const float* array) + { + AX_ASSERT(array); + + this->x = array[0]; + this->y = array[1]; + this->z = array[2]; + this->w = array[3]; + } + + /** + * Clamps this vector within the specified range. + * + * @param min The minimum value. + * @param max The maximum value. + */ + void clamp(const Vec4Base& min, const Vec4Base& max); + + /** + * Clamps the specified vector within the specified range and returns it in dst. + * + * @param v The vector to clamp. + * @param min The minimum value. + * @param max The maximum value. + * @param dst A vector to store the result in. + */ + static void clamp(const Vec4Base& v, const Vec4Base& min, const Vec4Base& max, Vec4Base* dst); union { @@ -69,6 +124,20 @@ class Vec4Base }; float comps[4]; }; +}; + +/* + * @brief A 4-floats class template with base math operators + */ +template +struct Vec4Adapter : public Vec4Base +{ +public: + using impl_type = _ImplType; + + constexpr Vec4Adapter() = default; + constexpr Vec4Adapter(float xx, float yy, float zz, float ww) : Vec4Base(xx, yy, zz, ww) {} + constexpr explicit Vec4Adapter(const float* src) : Vec4Base(src) {} impl_type& negate() { @@ -112,50 +181,6 @@ class Vec4Base return *static_cast(this); } - /** - * Sets the elements of this vector to the specified values. - * - * @param xx The new x coordinate. - * @param yy The new y coordinate. - * @param zz The new z coordinate. - * @param ww The new w coordinate. - */ - constexpr void set(float xx, float yy, float zz, float ww) - { - this->x = xx; - this->y = yy; - this->z = zz; - this->w = ww; - } - - /** - * Sets the elements of this vector from the values in the specified array. - * - * @param array An array containing the elements of the vector in the order x, y, z, w. - */ - void set(const float* array) - { - AX_ASSERT(array); - - this->x = array[0]; - this->y = array[1]; - this->z = array[2]; - this->w = array[3]; - } - - /** - * Sets the elements of this vector to those in the specified vector. - * - * @param v The vector to copy. - */ - void set(const impl_type& v) - { - this->x = v.x; - this->y = v.y; - this->z = v.z; - this->w = v.w; - } - inline impl_type& operator-() { return impl_type{*static_cast(this)}.negate(); } /** @@ -269,9 +294,9 @@ class Vec4Base /** * Defines 4-element floating point vector. */ -class AX_DLL Vec4 : public Vec4Base +class AX_DLL Vec4 : public Vec4Adapter { - using Vec4Base = Vec4Base; + using MyBase = Vec4Adapter; public: /** @@ -287,14 +312,14 @@ class AX_DLL Vec4 : public Vec4Base * @param zz The z coordinate. * @param ww The w coordinate. */ - constexpr Vec4(float xx, float yy, float zz, float ww) : Vec4Base(xx, yy, zz, ww) {} + constexpr Vec4(float xx, float yy, float zz, float ww) : MyBase(xx, yy, zz, ww) {} /** * Constructs a new vector from the values in the specified array. * * @param array An array containing the elements of the vector in the order x, y, z, w. */ - constexpr explicit Vec4(const float* array) : Vec4Base(array) {} + constexpr explicit Vec4(const float* array) : MyBase(array) {} /** * Constructs a vector that describes the direction between the specified points. @@ -304,16 +329,6 @@ class AX_DLL Vec4 : public Vec4Base */ constexpr Vec4(const Vec4& p1, const Vec4& p2) { setDirection(p1, p2); } - /** - * Creates a new vector from an integer interpreted as an RGBA value. - * E.g. 0xff0000ff represents opaque red or the vector (1, 0, 0, 1). - * - * @param color The integer to interpret as an RGBA value. - * - * @return A vector corresponding to the interpreted RGBA color. - */ - static Vec4 fromColor(unsigned int color); - /** * Indicates whether this vector contains all zeros. * @@ -347,24 +362,6 @@ class AX_DLL Vec4 : public Vec4Base */ static void add(const Vec4& v1, const Vec4& v2, Vec4* dst); - /** - * Clamps this vector within the specified range. - * - * @param min The minimum value. - * @param max The maximum value. - */ - void clamp(const Vec4& min, const Vec4& max); - - /** - * Clamps the specified vector within the specified range and returns it in dst. - * - * @param v The vector to clamp. - * @param min The minimum value. - * @param max The maximum value. - * @param dst A vector to store the result in. - */ - static void clamp(const Vec4& v, const Vec4& min, const Vec4& max, Vec4* dst); - /** * Returns the distance between this vector and v. * @@ -491,12 +488,12 @@ class AX_DLL Vec4 : public Vec4Base }; #if !(defined(AX_DLLEXPORT) || defined(AX_DLLIMPORT)) - inline constexpr Vec4 Vec4::ZERO = Vec4(0.0f, 0.0f, 0.0f, 0.0f); - inline constexpr Vec4 Vec4::ONE = Vec4(1.0f, 1.0f, 1.0f, 1.0f); - inline constexpr Vec4 Vec4::UNIT_X = Vec4(1.0f, 0.0f, 0.0f, 0.0f); - inline constexpr Vec4 Vec4::UNIT_Y = Vec4(0.0f, 1.0f, 0.0f, 0.0f); - inline constexpr Vec4 Vec4::UNIT_Z = Vec4(0.0f, 0.0f, 1.0f, 0.0f); - inline constexpr Vec4 Vec4::UNIT_W = Vec4(0.0f, 0.0f, 0.0f, 1.0f); +inline constexpr Vec4 Vec4::ZERO = Vec4(0.0f, 0.0f, 0.0f, 0.0f); +inline constexpr Vec4 Vec4::ONE = Vec4(1.0f, 1.0f, 1.0f, 1.0f); +inline constexpr Vec4 Vec4::UNIT_X = Vec4(1.0f, 0.0f, 0.0f, 0.0f); +inline constexpr Vec4 Vec4::UNIT_Y = Vec4(0.0f, 1.0f, 0.0f, 0.0f); +inline constexpr Vec4 Vec4::UNIT_Z = Vec4(0.0f, 0.0f, 1.0f, 0.0f); +inline constexpr Vec4 Vec4::UNIT_W = Vec4(0.0f, 0.0f, 0.0f, 1.0f); #endif NS_AX_MATH_END diff --git a/core/navmesh/NavMeshDebugDraw.cpp b/core/navmesh/NavMeshDebugDraw.cpp index b863eb9178f9..53ab4538d4ce 100644 --- a/core/navmesh/NavMeshDebugDraw.cpp +++ b/core/navmesh/NavMeshDebugDraw.cpp @@ -44,7 +44,7 @@ NavMeshDebugDraw::NavMeshDebugDraw() _programState = new backend::ProgramState(program); _locMVP = _programState->getUniformLocation("u_MVPMatrix"); - // the POSITION_COLOR default vertex layout is: V3F_C4B, so we need modify it + // the POSITION_COLOR default vertex layout is: V3F_C4F, so we need modify it auto vertexLayout = _programState->getMutableVertexLayout(); vertexLayout->setAttrib("a_position", _programState->getAttributeLocation(backend::Attribute::POSITION), diff --git a/core/physics/CMakeLists.txt b/core/physics/CMakeLists.txt index 12df34ee9d20..41e69b84df46 100644 --- a/core/physics/CMakeLists.txt +++ b/core/physics/CMakeLists.txt @@ -1,17 +1 @@ -set(_AX_PHYSICS_HEADER - physics/PhysicsContact.h - physics/PhysicsWorld.h - physics/PhysicsBody.h - physics/cpCompat62.h - physics/PhysicsShape.h - physics/PhysicsHelper.h - physics/PhysicsJoint.h - ) - -set(_AX_PHYSICS_SRC - physics/PhysicsBody.cpp - physics/PhysicsContact.cpp - physics/PhysicsJoint.cpp - physics/PhysicsShape.cpp - physics/PhysicsWorld.cpp - ) +# place holder for builtin physics based on box2d v3 \ No newline at end of file diff --git a/core/physics/PhysicsBody.cpp b/core/physics/PhysicsBody.cpp deleted file mode 100644 index bf0f21d7aca8..000000000000 --- a/core/physics/PhysicsBody.cpp +++ /dev/null @@ -1,1027 +0,0 @@ -/**************************************************************************** - Copyright (c) 2013-2016 Chukong Technologies Inc. - Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - Copyright (c) 2019-present Axmol Engine contributors (see AUTHORS.md). - - https://axmol.dev/ - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - ****************************************************************************/ -#include "physics/PhysicsBody.h" -#if defined(AX_ENABLE_PHYSICS) - -# include -# include -# include - -# include "chipmunk/chipmunk_private.h" - -# include "2d/Scene.h" -# include "physics/PhysicsShape.h" -# include "physics/PhysicsJoint.h" -# include "physics/PhysicsWorld.h" -# include "physics/PhysicsHelper.h" - -static void internalBodySetMass(cpBody* body, cpFloat mass) -{ - cpBodyActivate(body); - body->m = mass; - body->m_inv = 1.0f / mass; - // cpAssertSaneBody(body); -} - -static void internalBodyUpdateVelocity(cpBody* body, cpVect gravity, cpFloat damping, cpFloat dt) -{ - cpBodyUpdateVelocity(body, cpvzero, damping, dt); - // Skip kinematic bodies. - if (cpBodyGetType(body) == CP_BODY_TYPE_KINEMATIC) - return; - - cpAssertSoft(body->m > 0.0f && body->i > 0.0f, - "Body's mass and moment must be positive to simulate. (Mass: %f Moment: f)", body->m, body->i); - - ax::PhysicsBody* physicsBody = static_cast(body->userData); - - if (physicsBody->isGravityEnabled()) - body->v = - cpvclamp(cpvadd(cpvmult(body->v, damping), cpvmult(cpvadd(gravity, cpvmult(body->f, body->m_inv)), dt)), - physicsBody->getVelocityLimit()); - else - body->v = cpvclamp(cpvadd(cpvmult(body->v, damping), cpvmult(cpvmult(body->f, body->m_inv), dt)), - physicsBody->getVelocityLimit()); - cpFloat w_limit = physicsBody->getAngularVelocityLimit(); - body->w = cpfclamp(body->w * damping + body->t * body->i_inv * dt, -w_limit, w_limit); - - // Reset forces. - body->f = cpvzero; - // to check body sanity - cpBodySetTorque(body, 0.0f); -} - -namespace ax -{ -extern const float PHYSICS_INFINITY; - -const std::string PhysicsBody::COMPONENT_NAME = "PhysicsBody"; - -namespace -{ -static const float MASS_DEFAULT = 1.0; -static const float MOMENT_DEFAULT = 200; -} // namespace - -PhysicsBody::PhysicsBody() - : _world(nullptr) - , _cpBody(nullptr) - , _dynamic(true) - , _rotationEnabled(true) - , _gravityEnabled(true) - , _massDefault(true) - , _momentDefault(true) - , _mass(MASS_DEFAULT) - , _area(0.0f) - , _density(0.0f) - , _moment(MOMENT_DEFAULT) - , _velocityLimit(PHYSICS_INFINITY) - , _angularVelocityLimit(PHYSICS_INFINITY) - , _isDamping(false) - , _linearDamping(0.0f) - , _angularDamping(0.0f) - , _tag(0) - , _massSetByUser(false) - , _momentSetByUser(false) - , _rotationOffset(0) - , _recordedRotation(0.0f) - , _recordedAngle(0.0) - , _recordScaleX(1.f) - , _recordScaleY(1.f) - , _fixedUpdate(false) -{ - _name = COMPONENT_NAME; -} - -PhysicsBody::~PhysicsBody() -{ - for (auto&& joint : _joints) - { - PhysicsBody* other = joint->getBodyA() == this ? joint->getBodyB() : joint->getBodyA(); - other->removeJoint(joint); - delete joint; - } - - if (_cpBody) - { - cpBodyFree(_cpBody); - } -} - -PhysicsBody* PhysicsBody::create() -{ - PhysicsBody* body = new PhysicsBody(); - if (body->init()) - { - body->autorelease(); - return body; - } - - AX_SAFE_DELETE(body); - return nullptr; -} - -PhysicsBody* PhysicsBody::create(float mass) -{ - PhysicsBody* body = new PhysicsBody(); - if (body) - { - body->_mass = mass; - body->_massDefault = false; - if (body->init()) - { - body->autorelease(); - return body; - } - } - - AX_SAFE_DELETE(body); - return nullptr; -} - -PhysicsBody* PhysicsBody::create(float mass, float moment) -{ - PhysicsBody* body = new PhysicsBody(); - if (body) - { - body->_mass = mass; - body->_massDefault = false; - body->_moment = moment; - body->_momentDefault = false; - if (body->init()) - { - body->autorelease(); - return body; - } - } - - AX_SAFE_DELETE(body); - return nullptr; -} - -PhysicsBody* PhysicsBody::createCircle(float radius, const PhysicsMaterial& material, const Vec2& offset) -{ - PhysicsBody* body = new PhysicsBody(); - if (body->init()) - { - body->addShape(PhysicsShapeCircle::create(radius, material, offset)); - body->autorelease(); - return body; - } - - AX_SAFE_DELETE(body); - return nullptr; -} - -PhysicsBody* PhysicsBody::createBox(const Vec2& size, const PhysicsMaterial& material, const Vec2& offset) -{ - PhysicsBody* body = new PhysicsBody(); - if (body->init()) - { - body->addShape(PhysicsShapeBox::create(size, material, offset)); - body->autorelease(); - return body; - } - - AX_SAFE_DELETE(body); - return nullptr; -} - -PhysicsBody* PhysicsBody::createPolygon(const Vec2* points, - int count, - const PhysicsMaterial& material, - const Vec2& offset) -{ - PhysicsBody* body = new PhysicsBody(); - if (body->init()) - { - body->addShape(PhysicsShapePolygon::create(points, count, material, offset)); - body->autorelease(); - return body; - } - - AX_SAFE_DELETE(body); - return nullptr; -} - -PhysicsBody* PhysicsBody::createEdgeSegment(const Vec2& a, - const Vec2& b, - const PhysicsMaterial& material, - float border /* = 1*/) -{ - PhysicsBody* body = new PhysicsBody(); - if (body->init()) - { - body->addShape(PhysicsShapeEdgeSegment::create(a, b, material, border)); - body->setDynamic(false); - body->autorelease(); - return body; - } - - AX_SAFE_DELETE(body); - return nullptr; -} - -PhysicsBody* PhysicsBody::createEdgeBox(const Vec2& size, - const PhysicsMaterial& material, - float border /* = 1*/, - const Vec2& offset) -{ - PhysicsBody* body = new PhysicsBody(); - if (body->init()) - { - body->addShape(PhysicsShapeEdgeBox::create(size, material, border, offset)); - body->setDynamic(false); - body->autorelease(); - return body; - } - - AX_SAFE_DELETE(body); - - return nullptr; -} - -PhysicsBody* PhysicsBody::createEdgePolygon(const Vec2* points, - int count, - const PhysicsMaterial& material, - float border /* = 1*/) -{ - PhysicsBody* body = new PhysicsBody(); - if (body->init()) - { - body->addShape(PhysicsShapeEdgePolygon::create(points, count, material, border)); - body->setDynamic(false); - body->autorelease(); - return body; - } - - AX_SAFE_DELETE(body); - - return nullptr; -} - -PhysicsBody* PhysicsBody::createEdgeChain(const Vec2* points, - int count, - const PhysicsMaterial& material, - float border /* = 1*/) -{ - PhysicsBody* body = new PhysicsBody(); - if (body->init()) - { - body->addShape(PhysicsShapeEdgeChain::create(points, count, material, border)); - body->setDynamic(false); - body->autorelease(); - return body; - } - - AX_SAFE_DELETE(body); - - return nullptr; -} - -bool PhysicsBody::init() -{ - do - { - _cpBody = cpBodyNew(_mass, _moment); - internalBodySetMass(_cpBody, _mass); - cpBodySetUserData(_cpBody, this); - cpBodySetVelocityUpdateFunc(_cpBody, internalBodyUpdateVelocity); - - AX_BREAK_IF(_cpBody == nullptr); - - return true; - } while (false); - - return false; -} - -void PhysicsBody::removeJoint(PhysicsJoint* joint) -{ - auto it = std::find(_joints.begin(), _joints.end(), joint); - - if (it != _joints.end()) - { - _joints.erase(it); - } -} - -void PhysicsBody::setDynamic(bool dynamic) -{ - if (dynamic != _dynamic) - { - _dynamic = dynamic; - if (dynamic) - { - cpBodySetType(_cpBody, CP_BODY_TYPE_DYNAMIC); - internalBodySetMass(_cpBody, _mass); - cpBodySetMoment(_cpBody, _moment); - } - else - { - cpBodySetType(_cpBody, CP_BODY_TYPE_KINEMATIC); - } - } -} - -void PhysicsBody::setRotationEnable(bool enable) -{ - if (_rotationEnabled != enable) - { - cpBodySetMoment(_cpBody, enable ? _moment : PHYSICS_INFINITY); - _rotationEnabled = enable; - } -} - -void PhysicsBody::setGravityEnable(bool enable) -{ - _gravityEnabled = enable; -} - -void PhysicsBody::setRotation(float rotation) -{ - _recordedRotation = rotation; - _recordedAngle = -(rotation + _rotationOffset) * (M_PI / 180.0); - cpBodySetAngle(_cpBody, _recordedAngle); -} - -void PhysicsBody::setScale(float scaleX, float scaleY) -{ - for (auto&& shape : _shapes) - { - _area -= shape->getArea(); - if (!_massSetByUser) - addMass(-shape->getMass()); - if (!_momentSetByUser) - addMoment(-shape->getMoment()); - - shape->setScale(scaleX, scaleY); - - _area += shape->getArea(); - if (!_massSetByUser) - addMass(shape->getMass()); - if (!_momentSetByUser) - addMoment(shape->getMoment()); - } -} - -void PhysicsBody::setPosition(float positionX, float positionY) -{ - cpVect tt; - - tt.x = positionX + _positionOffset.x; - tt.y = positionY + _positionOffset.y; - - cpBodySetPosition(_cpBody, tt); -} - -Vec2 PhysicsBody::getPosition() const -{ - cpVect tt = cpBodyGetPosition(_cpBody); - return Vec2(tt.x - _positionOffset.x, tt.y - _positionOffset.y); -} - -void PhysicsBody::setPositionOffset(const Vec2& position) -{ - if (!_positionOffset.equals(position)) - { - Vec2 pos = getPosition(); - _positionOffset = position; - setPosition(pos.x, pos.y); - } -} - -float PhysicsBody::getRotation() -{ - if (_recordedAngle != cpBodyGetAngle(_cpBody)) - { - _recordedAngle = cpBodyGetAngle(_cpBody); - _recordedRotation = -_recordedAngle * 180.0 / M_PI - _rotationOffset; - } - return _recordedRotation; -} - -PhysicsShape* PhysicsBody::addShape(PhysicsShape* shape, bool addMassAndMoment /* = true*/) -{ - if (shape == nullptr) - return nullptr; - - // add shape to body - if (_shapes.getIndex(shape) == -1) - { - shape->setBody(this); - - // calculate the area, mass, and density - // area must update before mass, because the density changes depend on it. - if (addMassAndMoment) - { - _area += shape->getArea(); - addMass(shape->getMass()); - addMoment(shape->getMoment()); - } - - if (_world && cpBodyGetSpace(_cpBody)) - { - _world->addShape(shape); - } - - _shapes.pushBack(shape); - } - - return shape; -} - -void PhysicsBody::applyForce(const Vec2& force, const Vec2& offset) -{ - if (_dynamic && _mass != PHYSICS_INFINITY) - { - cpBodyApplyForceAtLocalPoint(_cpBody, PhysicsHelper::vec22cpv(force), PhysicsHelper::vec22cpv(offset)); - } -} - -void PhysicsBody::resetForces() -{ - cpBodySetForce(_cpBody, PhysicsHelper::vec22cpv(Vec2(0, 0))); -} - -void PhysicsBody::applyImpulse(const Vec2& impulse, const Vec2& offset) -{ - cpBodyApplyImpulseAtLocalPoint(_cpBody, PhysicsHelper::vec22cpv(impulse), PhysicsHelper::vec22cpv(offset)); -} - -void PhysicsBody::applyTorque(float torque) -{ - cpBodySetTorque(_cpBody, torque); -} - -void PhysicsBody::setMass(float mass) -{ - if (mass <= 0) - { - return; - } - _mass = mass; - _massDefault = false; - _massSetByUser = true; - - // update density - if (_mass == PHYSICS_INFINITY) - { - _density = PHYSICS_INFINITY; - } - else - { - if (_area > 0) - { - _density = _mass / _area; - } - else - { - _density = 0; - } - } - - // the static body's mass and moment is always infinity - if (_dynamic) - { - internalBodySetMass(_cpBody, _mass); - } -} - -void PhysicsBody::addMass(float mass) -{ - if (mass == PHYSICS_INFINITY) - { - _mass = PHYSICS_INFINITY; - _massDefault = false; - _density = PHYSICS_INFINITY; - } - else if (mass == -PHYSICS_INFINITY) - { - return; - } - else - { - if (_massDefault) - { - _mass = 0; - _massDefault = false; - } - - if (_mass + mass > 0) - { - _mass += mass; - } - else - { - _mass = MASS_DEFAULT; - _massDefault = true; - } - - if (_area > 0) - { - _density = _mass / _area; - } - else - { - _density = 0; - } - } - - // the static body's mass and moment is always infinity - if (_dynamic) - { - internalBodySetMass(_cpBody, _mass); - } -} - -void PhysicsBody::addMoment(float moment) -{ - if (moment == PHYSICS_INFINITY) - { - // if moment is PHYSICS_INFINITY, the moment of the body will become PHYSICS_INFINITY - _moment = PHYSICS_INFINITY; - _momentDefault = false; - } - else if (moment == -PHYSICS_INFINITY) - { - return; - } - else - { - // if moment of the body is PHYSICS_INFINITY is has no effect - if (_moment != PHYSICS_INFINITY) - { - if (_momentDefault) - { - _moment = 0; - _momentDefault = false; - } - - if (_moment + moment > 0) - { - _moment += moment; - } - else - { - _moment = MOMENT_DEFAULT; - _momentDefault = true; - } - } - } - - // the static body's mass and moment is always infinity - if (_rotationEnabled && _dynamic) - { - cpBodySetMoment(_cpBody, _moment); - } -} - -void PhysicsBody::setVelocity(const Vec2& velocity) -{ - if (cpBodyGetType(_cpBody) == CP_BODY_TYPE_STATIC) - { - AXLOGD("physics warning: you can't set velocity for a static body."); - return; - } - - cpBodySetVelocity(_cpBody, PhysicsHelper::vec22cpv(velocity)); -} - -Vec2 PhysicsBody::getVelocity() -{ - return PhysicsHelper::cpv2vec2(cpBodyGetVelocity(_cpBody)); -} - -Vec2 PhysicsBody::getVelocityAtLocalPoint(const Vec2& point) -{ - return PhysicsHelper::cpv2vec2(cpBodyGetVelocityAtLocalPoint(_cpBody, PhysicsHelper::vec22cpv(point))); -} - -Vec2 PhysicsBody::getVelocityAtWorldPoint(const Vec2& point) -{ - return PhysicsHelper::cpv2vec2(cpBodyGetVelocityAtWorldPoint(_cpBody, PhysicsHelper::vec22cpv(point))); -} - -void PhysicsBody::setAngularVelocity(float velocity) -{ - if (cpBodyGetType(_cpBody) == CP_BODY_TYPE_STATIC) - { - AXLOGD("physics warning: you can't set angular velocity for a static body."); - return; - } - - cpBodySetAngularVelocity(_cpBody, velocity); -} - -float PhysicsBody::getAngularVelocity() -{ - return PhysicsHelper::cpfloat2float(cpBodyGetAngularVelocity(_cpBody)); -} - -void PhysicsBody::setVelocityLimit(float limit) -{ - _velocityLimit = limit; -} - -float PhysicsBody::getVelocityLimit() -{ - return _velocityLimit; -} - -void PhysicsBody::setAngularVelocityLimit(float limit) -{ - _angularVelocityLimit = limit; -} - -float PhysicsBody::getAngularVelocityLimit() -{ - return _angularVelocityLimit; -} - -void PhysicsBody::setMoment(float moment) -{ - _moment = moment; - _momentDefault = false; - _momentSetByUser = true; - - // the static body's mass and moment is always infinity - if (_rotationEnabled && _dynamic) - { - cpBodySetMoment(_cpBody, _moment); - } -} - -PhysicsShape* PhysicsBody::getShape(int tag) const -{ - for (auto&& shape : _shapes) - { - if (shape->getTag() == tag) - { - return shape; - } - } - - return nullptr; -} - -void PhysicsBody::removeShape(int tag, bool reduceMassAndMoment /* = true*/) -{ - for (auto&& shape : _shapes) - { - if (shape->getTag() == tag) - { - removeShape(shape, reduceMassAndMoment); - return; - } - } -} - -void PhysicsBody::removeShape(PhysicsShape* shape, bool reduceMassAndMoment /* = true*/) -{ - if (_shapes.getIndex(shape) != -1) - { - // deduce the area, mass and moment - // area must update before mass, because the density changes depend on it. - if (reduceMassAndMoment) - { - _area -= shape->getArea(); - addMass(-shape->getMass()); - addMoment(-shape->getMoment()); - } - - // remove - if (_world) - { - _world->removeShape(shape); - } - - // set shape->_body = nullptr make the shape->setBody will not trigger the _body->removeShape function call. - shape->_body = nullptr; - shape->setBody(nullptr); - _shapes.eraseObject(shape); - } -} - -void PhysicsBody::removeAllShapes(bool reduceMassAndMoment /* = true*/) -{ - for (auto&& child : _shapes) - { - PhysicsShape* shape = dynamic_cast(child); - - // deduce the area, mass and moment - // area must update before mass, because the density changes depend on it. - if (reduceMassAndMoment) - { - _area -= shape->getArea(); - addMass(-shape->getMass()); - addMoment(-shape->getMoment()); - } - - if (_world) - { - _world->removeShape(shape); - } - - // set shape->_body = nullptr make the shape->setBody will not trigger the _body->removeShape function call. - shape->_body = nullptr; - shape->setBody(nullptr); - } - - _shapes.clear(); -} - -void PhysicsBody::removeFromWorld() -{ - removeFromPhysicsWorld(); -} - -void PhysicsBody::setEnabled(bool enable) -{ - if (_enabled != enable) - { - _enabled = enable; - - if (_world) - { - if (enable) - { - _world->addBodyOrDelay(this); - } - else - { - _world->removeBodyOrDelay(this); - } - } - } -} - -bool PhysicsBody::isResting() const -{ - return cpBodyIsSleeping(_cpBody) != cpFalse; -} - -void PhysicsBody::setResting(bool rest) const -{ - if (rest && !isResting()) - { - cpBodySleep(_cpBody); - } - else if (!rest && isResting()) - { - cpBodyActivate(_cpBody); - } -} - -void PhysicsBody::update(float delta) -{ - // damping compute - if (!_fixedUpdate && _isDamping && _dynamic && !isResting()) - { - _cpBody->v.x *= cpfclamp(1.0f - delta * _linearDamping, 0.0f, 1.0f); - _cpBody->v.y *= cpfclamp(1.0f - delta * _linearDamping, 0.0f, 1.0f); - _cpBody->w *= cpfclamp(1.0f - delta * _angularDamping, 0.0f, 1.0f); - } -} - -void PhysicsBody::fixedUpdate(float delta) -{ - if (_fixedUpdate && _isDamping && _dynamic && !isResting()) - { - _cpBody->v.x *= cpfclamp(1.0f - delta * _linearDamping, 0.0f, 1.0f); - _cpBody->v.y *= cpfclamp(1.0f - delta * _linearDamping, 0.0f, 1.0f); - _cpBody->w *= cpfclamp(1.0f - delta * _angularDamping, 0.0f, 1.0f); - } -} - -void PhysicsBody::setCategoryBitmask(int bitmask) -{ - for (auto&& shape : _shapes) - { - shape->setCategoryBitmask(bitmask); - } -} - -int PhysicsBody::getCategoryBitmask() const -{ - if (!_shapes.empty()) - { - return _shapes.front()->getCategoryBitmask(); - } - else - { - return UINT_MAX; - } -} - -void PhysicsBody::setContactTestBitmask(int bitmask) -{ - for (auto&& shape : _shapes) - { - shape->setContactTestBitmask(bitmask); - } -} - -int PhysicsBody::getContactTestBitmask() const -{ - if (!_shapes.empty()) - { - return _shapes.front()->getContactTestBitmask(); - } - else - { - return 0x00000000; - } -} - -void PhysicsBody::setCollisionBitmask(int bitmask) -{ - for (auto&& shape : _shapes) - { - shape->setCollisionBitmask(bitmask); - } -} - -int PhysicsBody::getCollisionBitmask() const -{ - if (!_shapes.empty()) - { - return _shapes.front()->getCollisionBitmask(); - } - else - { - return UINT_MAX; - } -} - -void PhysicsBody::setGroup(int group) -{ - for (auto&& shape : _shapes) - { - shape->setGroup(group); - } -} - -int PhysicsBody::getGroup() const -{ - if (!_shapes.empty()) - { - return _shapes.front()->getGroup(); - } - else - { - return 0; - } -} - -void PhysicsBody::setRotationOffset(float rotation) -{ - if (std::abs(_rotationOffset - rotation) > 0.5f) - { - float rot = getRotation(); - _rotationOffset = rotation; - setRotation(rot); - } -} - -Vec2 PhysicsBody::world2Local(const Vec2& point) -{ - return PhysicsHelper::cpv2vec2(cpBodyWorldToLocal(_cpBody, PhysicsHelper::vec22cpv(point))); -} - -Vec2 PhysicsBody::local2World(const Vec2& point) -{ - return PhysicsHelper::cpv2vec2(cpBodyLocalToWorld(_cpBody, PhysicsHelper::vec22cpv(point))); -} - -void PhysicsBody::beforeSimulation(const Mat4& parentToWorldTransform, - const Mat4& nodeToWorldTransform, - float scaleX, - float scaleY, - float rotation) -{ - if (_recordScaleX != scaleX || _recordScaleY != scaleY) - { - _recordScaleX = scaleX; - _recordScaleY = scaleY; - setScale(scaleX, scaleY); - } - - // set rotation - if (_recordedRotation != rotation) - { - setRotation(rotation); - } - - // set position - auto worldPosition = _ownerCenterOffset; - nodeToWorldTransform.transformVector(worldPosition.x, worldPosition.y, worldPosition.z, 1.f, &worldPosition); - setPosition(worldPosition.x, worldPosition.y); - - _recordPosX = worldPosition.x; - _recordPosY = worldPosition.y; - - if (_owner->getAnchorPoint() != Vec2::ANCHOR_MIDDLE) - { - parentToWorldTransform.getInversed().transformVector(worldPosition.x, worldPosition.y, worldPosition.z, 1.f, - &worldPosition); - _offset.x = worldPosition.x - _owner->getPositionX(); - _offset.y = worldPosition.y - _owner->getPositionY(); - } -} - -void PhysicsBody::afterSimulation(const Mat4& parentToWorldTransform, float parentRotation) -{ - // set Node position - auto tmp = getPosition(); - Vec3 positionInParent(tmp.x, tmp.y, 0.f); - if (_recordPosX != positionInParent.x || _recordPosY != positionInParent.y) - { - parentToWorldTransform.getInversed().transformVector(positionInParent.x, positionInParent.y, positionInParent.z, - 1.f, &positionInParent); - _owner->setPosition(positionInParent.x - _offset.x, positionInParent.y - _offset.y); - } - - // set Node rotation - _owner->setRotation(getRotation() - parentRotation); -} - -void PhysicsBody::onEnter() -{ - addToPhysicsWorld(); -} - -void PhysicsBody::onExit() -{ - removeFromPhysicsWorld(); -} - -void PhysicsBody::onAdd() -{ - _owner->_physicsBody = this; - auto contentSize = _owner->getContentSize(); - _ownerCenterOffset.x = 0.5f * contentSize.width; - _ownerCenterOffset.y = 0.5f * contentSize.height; - - setRotationOffset(_owner->getRotation()); - - // component may be added after onEnter() has been invoked, so we should add - // this line to make sure physics body is added to physics world - addToPhysicsWorld(); -} - -void PhysicsBody::onRemove() -{ - AXASSERT(_owner != nullptr, "_owner can't be nullptr"); - - removeFromPhysicsWorld(); - - _owner->_physicsBody = nullptr; -} - -void PhysicsBody::addToPhysicsWorld() -{ - if (_owner) - { - auto scene = _owner->getScene(); - if (scene) - scene->getPhysicsWorld()->addBody(this); - } -} - -void PhysicsBody::removeFromPhysicsWorld() -{ - if (_owner) - { - auto scene = _owner->getScene(); - if (scene) - scene->getPhysicsWorld()->removeBody(this); - } -} - -} - -#endif // defined(AX_ENABLE_PHYSICS) diff --git a/core/physics/PhysicsBody.h b/core/physics/PhysicsBody.h deleted file mode 100644 index 528f169eb8ef..000000000000 --- a/core/physics/PhysicsBody.h +++ /dev/null @@ -1,630 +0,0 @@ -/**************************************************************************** - Copyright (c) 2013-2016 Chukong Technologies Inc. - Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - Copyright (c) 2019-present Axmol Engine contributors (see AUTHORS.md). - - https://axmol.dev/ - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - ****************************************************************************/ - -#ifndef __CCPHYSICS_BODY_H__ -#define __CCPHYSICS_BODY_H__ - -#include "base/Config.h" -#if defined(AX_ENABLE_PHYSICS) - -# include "2d/Component.h" -# include "math/Math.h" -# include "physics/PhysicsShape.h" -# include "base/Vector.h" - -struct cpBody; - -namespace ax -{ - -class Node; -class PhysicsWorld; -class PhysicsJoint; - -const PhysicsMaterial PHYSICSBODY_MATERIAL_DEFAULT(0.1f, 0.5f, 0.5f); - -/** - * @addtogroup physics - * @{ - * @addtogroup physics_2d - * @{ - */ - -/** - * A body affect by physics. - * - * It can attach one or more shapes. - * If you create body with createXXX, it will automatically compute mass and moment with density your specified(which is - * PHYSICSBODY_MATERIAL_DEFAULT by default, and the density value is 0.1f), and it based on the formula: mass = density - * * area. If you create body with createEdgeXXX, the mass and moment will be PHYSICS_INFINITY by default. And it's a - * static body. You can change mass and moment with setMass() and setMoment(). And you can change the body to be dynamic - * or static by use function setDynamic(). - */ -class AX_DLL PhysicsBody : public Component -{ -public: - const static std::string COMPONENT_NAME; - - /** - * Create a body with default mass and moment. - * - * This default mass value is 1.0. - * This default moment value is 200. - * @return An autoreleased PhysicsBody object pointer. - */ - static PhysicsBody* create(); - - /** - * Create a body with mass and default moment. - * - * @param mass This body's mass. - * @return An autoreleased PhysicsBody object pointer. - */ - static PhysicsBody* create(float mass); - - /** - * Create a body with mass and moment. - * - * @param mass This body's mass. - * @param moment This body's moment. - * @return An autoreleased PhysicsBody object pointer. - */ - static PhysicsBody* create(float mass, float moment); - - /** - * Create a body contains a circle. - * - * @param radius A float number, it is the circle's radius. - * @param material A PhysicsMaterial object, the default value is PHYSICSSHAPE_MATERIAL_DEFAULT. - * @param offset A Vec2 object, it is the offset from the body's center of gravity in body local coordinates. - * @return An autoreleased PhysicsBody object pointer. - */ - static PhysicsBody* createCircle(float radius, - const PhysicsMaterial& material = PHYSICSBODY_MATERIAL_DEFAULT, - const Vec2& offset = Vec2::ZERO); - /** - * Create a body contains a box shape. - * - * @param size Size contains this box's width and height. - * @param material A PhysicsMaterial object, the default value is PHYSICSSHAPE_MATERIAL_DEFAULT. - * @param offset A Vec2 object, it is the offset from the body's center of gravity in body local coordinates. - * @return An autoreleased PhysicsBody object pointer. - */ - static PhysicsBody* createBox(const Vec2& size, - const PhysicsMaterial& material = PHYSICSBODY_MATERIAL_DEFAULT, - const Vec2& offset = Vec2::ZERO); - - /** - * @brief Create a body contains a polygon shape. - * - * @param points Points is an array of Vec2 structs defining a convex hull with a clockwise winding. - * @param count An integer number, contains the count of the points array. - * @param material A PhysicsMaterial object, the default value is PHYSICSSHAPE_MATERIAL_DEFAULT. - * @param offset A Vec2 object, it is the offset from the body's center of gravity in body local coordinates. - * @return An autoreleased PhysicsBody object pointer. - */ - static PhysicsBody* createPolygon(const Vec2* points, - int count, - const PhysicsMaterial& material = PHYSICSBODY_MATERIAL_DEFAULT, - const Vec2& offset = Vec2::ZERO); - - /** - * Create a body contains a EdgeSegment shape. - * - * @param a It's the edge's begin position. - * @param b It's the edge's end position. - * @param material A PhysicsMaterial object, the default value is PHYSICSSHAPE_MATERIAL_DEFAULT. - * @param border It's a edge's border width. - * @return An autoreleased PhysicsBody object pointer. - */ - static PhysicsBody* createEdgeSegment(const Vec2& a, - const Vec2& b, - const PhysicsMaterial& material = PHYSICSBODY_MATERIAL_DEFAULT, - float border = 1); - - /** - * Create a body contains a EdgeBox shape. - * @param size The size contains this box's width and height. - * @param material A PhysicsMaterial object, the default value is PHYSICSSHAPE_MATERIAL_DEFAULT. - * @param border It's a edge's border width. - * @param offset A Vec2 object, it is the offset from the body's center of gravity in body local coordinates. - * @return An autoreleased PhysicsBody object pointer. - */ - static PhysicsBody* createEdgeBox(const Vec2& size, - const PhysicsMaterial& material = PHYSICSBODY_MATERIAL_DEFAULT, - float border = 1, - const Vec2& offset = Vec2::ZERO); - - /** - * Create a body contains a EdgePolygon shape. - * - * @param points Points is an array of Vec2 structs defining a convex hull with a clockwise winding. - * @param count An integer number, contains the count of the points array. - * @param material A PhysicsMaterial object, the default value is PHYSICSSHAPE_MATERIAL_DEFAULT. - * @param border It's a edge's border width. - * @return An autoreleased PhysicsBody object pointer. - */ - static PhysicsBody* createEdgePolygon(const Vec2* points, - int count, - const PhysicsMaterial& material = PHYSICSBODY_MATERIAL_DEFAULT, - float border = 1); - - /** - * Create a body contains a EdgeChain shape. - * - * @param points A Vec2 object pointer, it contains an array of points. - * @param count An integer number, contains the count of the points array. - * @param material A PhysicsMaterial object, the default value is PHYSICSSHAPE_MATERIAL_DEFAULT. - * @param border It's a edge's border width. - * @return An autoreleased PhysicsBody object pointer. - */ - static PhysicsBody* createEdgeChain(const Vec2* points, - int count, - const PhysicsMaterial& material = PHYSICSBODY_MATERIAL_DEFAULT, - float border = 1); - - /** - * @brief Add a shape to body. - * @param shape The shape to be added. - * @param addMassAndMoment If this is true, the shape's mass and moment will be added to body. The default is true. - * @return This shape's pointer if added success or nullptr if failed. - */ - virtual PhysicsShape* addShape(PhysicsShape* shape, bool addMassAndMoment = true); - - /** - * @brief Remove a shape from body. - * @param shape Shape the shape to be removed. - * @param reduceMassAndMoment If this is true, the body mass and moment will be reduced by shape. The default is - * true. - */ - void removeShape(PhysicsShape* shape, bool reduceMassAndMoment = true); - - /** - * @brief Remove a shape from body. - * @param tag The tag of the shape to be removed. - * @param reduceMassAndMoment If this is true, the body mass and moment will be reduced by shape. The default is - * true. - */ - void removeShape(int tag, bool reduceMassAndMoment = true); - - /** - * Remove all shapes. - * - * @param reduceMassAndMoment If this is true, the body mass and moment will be reduced by shape. The default is - * true. - */ - void removeAllShapes(bool reduceMassAndMoment = true); - - /** - * Get the body shapes. - * - * @return A Vector object contains PhysicsShape pointer. - */ - const Vector& getShapes() const { return _shapes; } - - /** - * Get the first shape of the body shapes. - * - * @return The first shape in this body. - */ - PhysicsShape* getFirstShape() const { return _shapes.size() >= 1 ? _shapes.at(0) : nullptr; } - - /** - * get the shape of the body. - * - * @param tag An integer number that identifies a PhysicsShape object. - * @return A PhysicsShape object pointer or nullptr if no shapes were found. - */ - PhysicsShape* getShape(int tag) const; - - /** - * Applies a continuous force to body. - * - * @param force The force is applies to this body. - * @param offset A Vec2 object, it is the offset from the body's center of gravity in world coordinates. - */ - virtual void applyForce(const Vec2& force, const Vec2& offset = Vec2::ZERO); - - /** - * reset all the force applied to body. - */ - virtual void resetForces(); - - /** - * Applies a immediate force to body. - * - * @param impulse The impulse is applies to this body. - * @param offset A Vec2 object, it is the offset from the body's center of gravity in world coordinates. - */ - virtual void applyImpulse(const Vec2& impulse, const Vec2& offset = Vec2::ZERO); - - /** - * Applies a torque force to body. - * - * @param torque The torque is applies to this body. - */ - virtual void applyTorque(float torque); - - /** - * Set the velocity of a body. - * - * @param velocity The velocity is set to this body. - */ - virtual void setVelocity(const Vec2& velocity); - - /** Get the velocity of a body. */ - virtual Vec2 getVelocity(); - - /** - * Set the angular velocity of a body. - * - * @param velocity The angular velocity is set to this body. - */ - virtual void setAngularVelocity(float velocity); - - /** Get the angular velocity of a body at a local point.*/ - virtual Vec2 getVelocityAtLocalPoint(const Vec2& point); - - /** get the angular velocity of a body at a world point */ - virtual Vec2 getVelocityAtWorldPoint(const Vec2& point); - - /** get the angular velocity of a body */ - virtual float getAngularVelocity(); - - /** set the max of velocity */ - virtual void setVelocityLimit(float limit); - - /** get the max of velocity */ - virtual float getVelocityLimit(); - - /** set the max of angular velocity */ - virtual void setAngularVelocityLimit(float limit); - - /** get the max of angular velocity */ - virtual float getAngularVelocityLimit(); - - /** remove the body from the world it added to */ - void removeFromWorld(); - - /** get the world body added to. */ - PhysicsWorld* getWorld() const { return _world; } - - /** get all joints the body have */ - const std::vector& getJoints() const { return _joints; } - - /** get the node the body set to. */ - Node* getNode() const { return _owner; } - - /** - * A mask that defines which categories this physics body belongs to. - * - * Every physics body in a scene can be assigned to up to 32 different categories, each corresponding to a bit in - * the bit mask. You define the mask values used in your game. In conjunction with the collisionBitMask and - * contactTestBitMask properties, you define which physics bodies interact with each other and when your game is - * notified of these interactions. - * @param bitmask An integer number, the default value is 0xFFFFFFFF (all bits set). - */ - void setCategoryBitmask(int bitmask); - - /** - * A mask that defines which categories of bodies cause intersection notifications with this physics body. - * - * When two bodies share the same space, each body's category mask is tested against the other body's contact mask - * by performing a logical AND operation. If either comparison results in a non-zero value, an PhysicsContact object - * is created and passed to the physics world’s delegate. For best performance, only set bits in the contacts mask - * for interactions you are interested in. - * @param bitmask An integer number, the default value is 0x00000000 (all bits cleared). - */ - void setContactTestBitmask(int bitmask); - - /** - * A mask that defines which categories of physics bodies can collide with this physics body. - * - * When two physics bodies contact each other, a collision may occur. This body's collision mask is compared to the - * other body's category mask by performing a logical AND operation. If the result is a non-zero value, then this - * body is affected by the collision. Each body independently chooses whether it wants to be affected by the other - * body. For example, you might use this to avoid collision calculations that would make negligible changes to a - * body's velocity. - * @param bitmask An integer number, the default value is 0xFFFFFFFF (all bits set). - */ - void setCollisionBitmask(int bitmask); - - /** - * Return bitmask of first shape. - * - * @return If there is no shape in body, return default value.(0xFFFFFFFF) - */ - int getCategoryBitmask() const; - - /** - * Return bitmask of first shape. - * - * @return If there is no shape in body, return default value.(0x00000000) - */ - int getContactTestBitmask() const; - - /** - * Return bitmask of first shape. - * - * @return If there is no shape in body, return default value.(0xFFFFFFFF) - */ - int getCollisionBitmask() const; - - /** - * Set the group of body. - * - * Collision groups let you specify an integral group index. You can have all fixtures with the same group index - * always collide (positive index) or never collide (negative index). It have high priority than bit masks. - */ - void setGroup(int group); - - /** - * Return group of first shape. - * - * @return If there is no shape in body, return default value.(0) - */ - int getGroup() const; - - /** get the body position. */ - Vec2 getPosition() const; - - /** get the body rotation. */ - float getRotation(); - - /** set body position offset, it's the position witch relative to node */ - void setPositionOffset(const Vec2& position); - - /** get body position offset. */ - const Vec2& getPositionOffset() const { return _positionOffset; } - - /** set body rotation offset, it's the rotation witch relative to node */ - void setRotationOffset(float rotation); - - /** set the body rotation offset */ - float getRotationOffset() const { return _rotationOffset; } - - /** - * @brief Test the body is dynamic or not. - * - * A dynamic body will effect with gravity. - */ - bool isDynamic() const { return _dynamic; } - /** - * @brief Set dynamic to body. - * - * A dynamic body will effect with gravity. - */ - void setDynamic(bool dynamic); - - /** - * @brief Set the body mass. - * - * @attention If you need add/subtract mass to body, don't use setMass(getMass() +/- mass), because the mass of body - * may be equal to PHYSICS_INFINITY, it will cause some unexpected result, please use addMass() instead. - */ - void setMass(float mass); - - /** Get the body mass. */ - float getMass() const { return _mass; } - - /** - * @brief Add mass to body. - * - * @param mass If _mass(mass of the body) == PHYSICS_INFINITY, it remains. - * if mass == PHYSICS_INFINITY, _mass will be PHYSICS_INFINITY. - * if mass == -PHYSICS_INFINITY, _mass will not change. - * if mass + _mass <= 0, _mass will equal to MASS_DEFAULT(1.0) - * other wise, mass = mass + _mass; - */ - void addMass(float mass); - - /** - * @brief Set the body moment of inertia. - * - * @note If you need add/subtract moment to body, don't use setMoment(getMoment() +/- moment), because the moment of - * body may be equal to PHYSICS_INFINITY, it will cause some unexpected result, please use addMoment() instead. - */ - void setMoment(float moment); - - /** Get the body moment of inertia. */ - float getMoment() const { return _moment; } - - /** - * @brief Add moment of inertia to body. - * - * @param moment If _moment(moment of the body) == PHYSICS_INFINITY, it remains. - * if moment == PHYSICS_INFINITY, _moment will be PHYSICS_INFINITY. - * if moment == -PHYSICS_INFINITY, _moment will not change. - * if moment + _moment <= 0, _moment will equal to MASS_DEFAULT(1.0) - * other wise, moment = moment + _moment; - */ - void addMoment(float moment); - - /** get linear damping. */ - float getLinearDamping() const { return _linearDamping; } - - /** - * Set linear damping. - * - * it is used to simulate fluid or air friction forces on the body. - * @param damping The value is 0.0f to 1.0f. - */ - void setLinearDamping(float damping) - { - _linearDamping = damping; - updateDamping(); - } - - /** Get angular damping. */ - float getAngularDamping() const { return _angularDamping; } - - /** - * Set angular damping. - * - * It is used to simulate fluid or air friction forces on the body. - * @param damping The value is 0.0f to 1.0f. - */ - void setAngularDamping(float damping) - { - _angularDamping = damping; - updateDamping(); - } - - /** Whether the body is at rest. */ - bool isResting() const; - - /** set body to rest */ - void setResting(bool rest) const; - - /** - * Set the enable value. - * - * If the body it isn't enabled, it will not has simulation by world. - */ - virtual void setEnabled(bool enable) override; - - /** Whether the body can rotation. */ - bool isRotationEnabled() const { return _rotationEnabled; } - - /** Set the body is allow rotation or not */ - void setRotationEnable(bool enable); - - /** Whether this physics body is affected by the physics world's gravitational force. */ - bool isGravityEnabled() const { return _gravityEnabled; } - - /** Set the body is affected by the physics world's gravitational force or not. */ - void setGravityEnable(bool enable); - - /** Get the body's tag. */ - int getTag() const { return _tag; } - - /** set the body's tag. */ - void setTag(int tag) { _tag = tag; } - - /** Convert the world point to local. */ - Vec2 world2Local(const Vec2& point); - - /** Convert the local point to world. */ - Vec2 local2World(const Vec2& point); - - /** Get the rigid body of chipmunk. */ - cpBody* getCPBody() const { return _cpBody; } - - /** Set fixed update state */ - void setFixedUpdate(bool fixedUpdate) { _fixedUpdate = fixedUpdate; } - - virtual void onEnter() override; - virtual void onExit() override; - virtual void onAdd() override; - virtual void onRemove() override; - -protected: - PhysicsBody(); - virtual ~PhysicsBody(); - - virtual bool init() override; - - virtual void setPosition(float positionX, float positionY); - - virtual void setRotation(float rotation); - - virtual void setScale(float scaleX, float scaleY); - - void update(float delta) override; - void fixedUpdate(float delta); - - void removeJoint(PhysicsJoint* joint); - - void updateDamping() { _isDamping = _linearDamping != 0.0f || _angularDamping != 0.0f; } - - void addToPhysicsWorld(); - void removeFromPhysicsWorld(); - - void beforeSimulation(const Mat4& parentToWorldTransform, - const Mat4& nodeToWorldTransform, - float scaleX, - float scaleY, - float rotation); - void afterSimulation(const Mat4& parentToWorldTransform, float parentRotation); - -protected: - std::vector _joints; - Vector _shapes; - PhysicsWorld* _world; - - cpBody* _cpBody; - bool _dynamic; - bool _rotationEnabled; - bool _gravityEnabled; - bool _massDefault; - bool _momentDefault; - float _mass; - float _area; - float _density; - float _moment; - float _velocityLimit; - float _angularVelocityLimit; - bool _isDamping; - float _linearDamping; - float _angularDamping; - - int _tag; - - // when setMass() is invoked, it means body's mass is not calculated by shapes - bool _massSetByUser; - // when setMoment() is invoked, it means body's moment is not calculated by shapes - bool _momentSetByUser; - - Vec2 _positionOffset; - float _rotationOffset; - float _recordedRotation; - double _recordedAngle; - - // offset between owner's center point and down left point - Vec3 _ownerCenterOffset; - // offset of owner's center point and anchor point in parent coordinate - Vec2 _offset; - float _recordScaleX; - float _recordScaleY; - - float _recordPosX; - float _recordPosY; - - // fixed update state - bool _fixedUpdate; - - friend class PhysicsWorld; - friend class PhysicsShape; - friend class PhysicsJoint; -}; - -/** @} */ -/** @} */ - -} - -#endif // defined(AX_ENABLE_PHYSICS) -#endif // __CCPHYSICS_BODY_H__ diff --git a/core/physics/PhysicsContact.cpp b/core/physics/PhysicsContact.cpp deleted file mode 100644 index 267ff043be21..000000000000 --- a/core/physics/PhysicsContact.cpp +++ /dev/null @@ -1,435 +0,0 @@ -/**************************************************************************** - Copyright (c) 2013-2016 Chukong Technologies Inc. - Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - Copyright (c) 2019-present Axmol Engine contributors (see AUTHORS.md). - - https://axmol.dev/ - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - ****************************************************************************/ -#include "physics/PhysicsContact.h" -#if defined(AX_ENABLE_PHYSICS) -# include "chipmunk/chipmunk.h" - -# include "physics/PhysicsBody.h" -# include "physics/PhysicsHelper.h" -# include "base/EventCustom.h" - -namespace ax -{ - -const char* PHYSICSCONTACT_EVENT_NAME = "PhysicsContactEvent"; - -PhysicsContact::PhysicsContact() - : EventCustom(PHYSICSCONTACT_EVENT_NAME) - , _world(nullptr) - , _shapeA(nullptr) - , _shapeB(nullptr) - , _eventCode(EventCode::NONE) - , _notificationEnable(true) - , _result(true) - , _data(nullptr) - , _contactInfo(nullptr) - , _contactData(nullptr) - , _preContactData(nullptr) -{} - -PhysicsContact::~PhysicsContact() -{ - AX_SAFE_DELETE(_contactData); - AX_SAFE_DELETE(_preContactData); -} - -PhysicsContact* PhysicsContact::construct(PhysicsShape* a, PhysicsShape* b) -{ - PhysicsContact* contact = new PhysicsContact(); - if (contact->init(a, b)) - { - return contact; - } - - AX_SAFE_DELETE(contact); - return nullptr; -} - -bool PhysicsContact::init(PhysicsShape* a, PhysicsShape* b) -{ - do - { - AX_BREAK_IF(a == nullptr || b == nullptr); - - _shapeA = a; - _shapeB = b; - - return true; - } while (false); - - return false; -} - -void PhysicsContact::generateContactData() -{ - if (_contactInfo == nullptr) - { - return; - } - - cpArbiter* arb = static_cast(_contactInfo); - AX_SAFE_DELETE(_preContactData); - _preContactData = _contactData; - _contactData = new PhysicsContactData(); - _contactData->count = cpArbiterGetCount(arb); - for (int i = 0; i < _contactData->count && i < PhysicsContactData::POINT_MAX; ++i) - { - _contactData->points[i] = PhysicsHelper::cpv2vec2(cpArbiterGetPointA(arb, i)); - } - - _contactData->normal = _contactData->count > 0 ? PhysicsHelper::cpv2vec2(cpArbiterGetNormal(arb)) : Vec2::ZERO; -} - -// PhysicsContactPreSolve implementation -PhysicsContactPreSolve::PhysicsContactPreSolve(void* contactInfo) : _contactInfo(contactInfo) {} - -PhysicsContactPreSolve::~PhysicsContactPreSolve() {} - -float PhysicsContactPreSolve::getRestitution() const -{ - return cpArbiterGetRestitution(static_cast(_contactInfo)); -} - -float PhysicsContactPreSolve::getFriction() const -{ - return cpArbiterGetFriction(static_cast(_contactInfo)); -} - -Vec2 PhysicsContactPreSolve::getSurfaceVelocity() const -{ - return PhysicsHelper::cpv2vec2(cpArbiterGetSurfaceVelocity(static_cast(_contactInfo))); -} - -void PhysicsContactPreSolve::setRestitution(float restitution) -{ - cpArbiterSetRestitution(static_cast(_contactInfo), restitution); -} - -void PhysicsContactPreSolve::setFriction(float friction) -{ - cpArbiterSetFriction(static_cast(_contactInfo), friction); -} - -void PhysicsContactPreSolve::setSurfaceVelocity(const Vec2& velocity) -{ - cpArbiterSetSurfaceVelocity(static_cast(_contactInfo), PhysicsHelper::vec22cpv(velocity)); -} - -void PhysicsContactPreSolve::ignore() -{ - cpArbiterIgnore(static_cast(_contactInfo)); -} - -// PhysicsContactPostSolve implementation -PhysicsContactPostSolve::PhysicsContactPostSolve(void* contactInfo) : _contactInfo(contactInfo) {} - -PhysicsContactPostSolve::~PhysicsContactPostSolve() {} - -float PhysicsContactPostSolve::getRestitution() const -{ - return cpArbiterGetRestitution(static_cast(_contactInfo)); -} - -float PhysicsContactPostSolve::getFriction() const -{ - return cpArbiterGetFriction(static_cast(_contactInfo)); -} - -Vec2 PhysicsContactPostSolve::getSurfaceVelocity() const -{ - return PhysicsHelper::cpv2vec2(cpArbiterGetSurfaceVelocity(static_cast(_contactInfo))); -} - -EventListenerPhysicsContact::EventListenerPhysicsContact() - : onContactBegin(nullptr), onContactPreSolve(nullptr), onContactPostSolve(nullptr), onContactSeparate(nullptr) -{} - -bool EventListenerPhysicsContact::init() -{ - auto func = [this](EventCustom* event) -> void { onEvent(event); }; - - return EventListenerCustom::init(PHYSICSCONTACT_EVENT_NAME, func); -} - -void EventListenerPhysicsContact::onEvent(EventCustom* event) -{ - PhysicsContact* contact = dynamic_cast(event); - - if (contact == nullptr) - { - return; - } - - switch (contact->getEventCode()) - { - case PhysicsContact::EventCode::BEGIN: - { - bool ret = true; - - if (onContactBegin != nullptr && hitTest(contact->getShapeA(), contact->getShapeB())) - { - contact->generateContactData(); - ret = onContactBegin(*contact); - } - - contact->setResult(ret); - break; - } - case PhysicsContact::EventCode::PRESOLVE: - { - bool ret = true; - - if (onContactPreSolve != nullptr && hitTest(contact->getShapeA(), contact->getShapeB())) - { - PhysicsContactPreSolve solve(contact->_contactInfo); - contact->generateContactData(); - - ret = onContactPreSolve(*contact, solve); - } - - contact->setResult(ret); - break; - } - case PhysicsContact::EventCode::POSTSOLVE: - { - if (onContactPostSolve != nullptr && hitTest(contact->getShapeA(), contact->getShapeB())) - { - PhysicsContactPostSolve solve(contact->_contactInfo); - onContactPostSolve(*contact, solve); - } - break; - } - case PhysicsContact::EventCode::SEPARATE: - { - if (onContactSeparate != nullptr && hitTest(contact->getShapeA(), contact->getShapeB())) - { - onContactSeparate(*contact); - } - break; - } - default: - break; - } -} - -EventListenerPhysicsContact::~EventListenerPhysicsContact() {} - -EventListenerPhysicsContact* EventListenerPhysicsContact::create() -{ - EventListenerPhysicsContact* obj = new EventListenerPhysicsContact(); - - if (obj->init()) - { - obj->autorelease(); - return obj; - } - - AX_SAFE_DELETE(obj); - return nullptr; -} - -bool EventListenerPhysicsContact::hitTest(PhysicsShape* /*shapeA*/, PhysicsShape* /*shapeB*/) -{ - return true; -} - -bool EventListenerPhysicsContact::checkAvailable() -{ - if (onContactBegin == nullptr && onContactPreSolve == nullptr && onContactPostSolve == nullptr && - onContactSeparate == nullptr) - { - AXASSERT(false, "Invalid PhysicsContactListener."); - return false; - } - - return true; -} - -EventListenerPhysicsContact* EventListenerPhysicsContact::clone() -{ - EventListenerPhysicsContact* obj = EventListenerPhysicsContact::create(); - - if (obj != nullptr) - { - obj->onContactBegin = onContactBegin; - obj->onContactPreSolve = onContactPreSolve; - obj->onContactPostSolve = onContactPostSolve; - obj->onContactSeparate = onContactSeparate; - - return obj; - } - - AX_SAFE_DELETE(obj); - return nullptr; -} - -EventListenerPhysicsContactWithBodies* EventListenerPhysicsContactWithBodies::create(PhysicsBody* bodyA, - PhysicsBody* bodyB) -{ - EventListenerPhysicsContactWithBodies* obj = new EventListenerPhysicsContactWithBodies(); - - if (obj->init()) - { - obj->_a = bodyA; - obj->_b = bodyB; - obj->autorelease(); - return obj; - } - - AX_SAFE_DELETE(obj); - return nullptr; -} - -EventListenerPhysicsContactWithBodies::EventListenerPhysicsContactWithBodies() : _a(nullptr), _b(nullptr) {} - -EventListenerPhysicsContactWithBodies::~EventListenerPhysicsContactWithBodies() {} - -bool EventListenerPhysicsContactWithBodies::hitTest(PhysicsShape* shapeA, PhysicsShape* shapeB) -{ - if ((shapeA->getBody() == _a && shapeB->getBody() == _b) || (shapeA->getBody() == _b && shapeB->getBody() == _a)) - { - return true; - } - - return false; -} - -EventListenerPhysicsContactWithBodies* EventListenerPhysicsContactWithBodies::clone() -{ - EventListenerPhysicsContactWithBodies* obj = EventListenerPhysicsContactWithBodies::create(_a, _b); - - if (obj != nullptr) - { - obj->onContactBegin = onContactBegin; - obj->onContactPreSolve = onContactPreSolve; - obj->onContactPostSolve = onContactPostSolve; - obj->onContactSeparate = onContactSeparate; - - return obj; - } - - AX_SAFE_DELETE(obj); - return nullptr; -} - -EventListenerPhysicsContactWithShapes::EventListenerPhysicsContactWithShapes() : _a(nullptr), _b(nullptr) {} - -EventListenerPhysicsContactWithShapes::~EventListenerPhysicsContactWithShapes() {} - -EventListenerPhysicsContactWithShapes* EventListenerPhysicsContactWithShapes::create(PhysicsShape* shapeA, - PhysicsShape* shapeB) -{ - EventListenerPhysicsContactWithShapes* obj = new EventListenerPhysicsContactWithShapes(); - - if (obj->init()) - { - obj->_a = shapeA; - obj->_b = shapeB; - obj->autorelease(); - return obj; - } - - AX_SAFE_DELETE(obj); - return nullptr; -} - -bool EventListenerPhysicsContactWithShapes::hitTest(PhysicsShape* shapeA, PhysicsShape* shapeB) -{ - if ((shapeA == _a && shapeB == _b) || (shapeA == _b && shapeB == _a)) - { - return true; - } - - return false; -} - -EventListenerPhysicsContactWithShapes* EventListenerPhysicsContactWithShapes::clone() -{ - EventListenerPhysicsContactWithShapes* obj = EventListenerPhysicsContactWithShapes::create(_a, _b); - - if (obj != nullptr) - { - obj->onContactBegin = onContactBegin; - obj->onContactPreSolve = onContactPreSolve; - obj->onContactPostSolve = onContactPostSolve; - obj->onContactSeparate = onContactSeparate; - - return obj; - } - - AX_SAFE_DELETE(obj); - return nullptr; -} - -EventListenerPhysicsContactWithGroup::EventListenerPhysicsContactWithGroup() : _group(CP_NO_GROUP) {} - -EventListenerPhysicsContactWithGroup::~EventListenerPhysicsContactWithGroup() {} - -EventListenerPhysicsContactWithGroup* EventListenerPhysicsContactWithGroup::create(int group) -{ - EventListenerPhysicsContactWithGroup* obj = new EventListenerPhysicsContactWithGroup(); - - if (obj->init()) - { - obj->_group = group; - obj->autorelease(); - return obj; - } - - AX_SAFE_DELETE(obj); - return nullptr; -} - -bool EventListenerPhysicsContactWithGroup::hitTest(PhysicsShape* shapeA, PhysicsShape* shapeB) -{ - if (shapeA->getGroup() == _group || shapeB->getGroup() == _group) - { - return true; - } - - return false; -} - -EventListenerPhysicsContactWithGroup* EventListenerPhysicsContactWithGroup::clone() -{ - EventListenerPhysicsContactWithGroup* obj = EventListenerPhysicsContactWithGroup::create(_group); - - if (obj != nullptr) - { - obj->onContactBegin = onContactBegin; - obj->onContactPreSolve = onContactPreSolve; - obj->onContactPostSolve = onContactPostSolve; - obj->onContactSeparate = onContactSeparate; - - return obj; - } - - AX_SAFE_DELETE(obj); - return nullptr; -} - -} -#endif // defined(AX_ENABLE_PHYSICS) diff --git a/core/physics/PhysicsContact.h b/core/physics/PhysicsContact.h deleted file mode 100644 index fe125bafe4ff..000000000000 --- a/core/physics/PhysicsContact.h +++ /dev/null @@ -1,327 +0,0 @@ -/**************************************************************************** - Copyright (c) 2013-2016 Chukong Technologies Inc. - Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - Copyright (c) 2019-present Axmol Engine contributors (see AUTHORS.md). - - https://axmol.dev/ - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - ****************************************************************************/ - -#ifndef __CCPHYSICS_CONTACT_H__ -#define __CCPHYSICS_CONTACT_H__ - -#include "base/Config.h" -#if defined(AX_ENABLE_PHYSICS) - -# include "base/Object.h" -# include "math/Math.h" -# include "base/EventListenerCustom.h" -# include "base/Event.h" -# include "base/EventCustom.h" - -namespace ax -{ - -class PhysicsShape; -class PhysicsBody; -class PhysicsWorld; - -typedef struct AX_DLL PhysicsContactData -{ - static const int POINT_MAX = 4; - Vec2 points[POINT_MAX]; - int count; - Vec2 normal; - - PhysicsContactData() : count(0) {} -} PhysicsContactData; - -/** - * @addtogroup physics - * @{ - * @addtogroup physics_2d - * @{ - */ - -/** - * @brief Contact information. - - * It will created automatically when two shape contact with each other. And it will destroyed automatically when two - shape separated. - */ -class AX_DLL PhysicsContact : public EventCustom -{ -public: - enum class EventCode - { - NONE, - BEGIN, - PRESOLVE, - POSTSOLVE, - SEPARATE - }; - - /** Get contact shape A. */ - PhysicsShape* getShapeA() const { return _shapeA; } - - /** Get contact shape B. */ - PhysicsShape* getShapeB() const { return _shapeB; } - - /** Get contact data. */ - const PhysicsContactData* getContactData() const { return _contactData; } - - /** Get previous contact data */ - const PhysicsContactData* getPreContactData() const { return _preContactData; } - - /** - * Get data. - * @lua NA - */ - void* getData() const { return _data; } - - /** - * @brief Set data to contact. - - * You must manage the memory yourself, Generally you can set data at contact begin, and destroy it at contact - separate. - * - * @lua NA - */ - void setData(void* data) { _data = data; } - - /** Get the event code */ - EventCode getEventCode() const { return _eventCode; }; - -private: - static PhysicsContact* construct(PhysicsShape* a, PhysicsShape* b); - bool init(PhysicsShape* a, PhysicsShape* b); - - void setEventCode(EventCode eventCode) { _eventCode = eventCode; }; - bool isNotificationEnabled() const { return _notificationEnable; } - void setNotificationEnable(bool enable) { _notificationEnable = enable; } - PhysicsWorld* getWorld() const { return _world; } - void setWorld(PhysicsWorld* world) { _world = world; } - void setResult(bool result) { _result = result; } - bool resetResult() - { - bool ret = _result; - _result = true; - return ret; - } - - void generateContactData(); - -private: - PhysicsContact(); - ~PhysicsContact(); - -private: - PhysicsWorld* _world; - PhysicsShape* _shapeA; - PhysicsShape* _shapeB; - EventCode _eventCode; - bool _notificationEnable; - bool _result; - - void* _data; - void* _contactInfo; - PhysicsContactData* _contactData; - PhysicsContactData* _preContactData; - - friend class EventListenerPhysicsContact; - friend class PhysicsWorldCallback; - friend class PhysicsWorld; -}; - -/** - * @brief Presolve value generated when onContactPreSolve called. - */ -class AX_DLL PhysicsContactPreSolve -{ -public: - /** Get restitution between two bodies.*/ - float getRestitution() const; - /** Get friction between two bodies.*/ - float getFriction() const; - /** Get surface velocity between two bodies.*/ - Vec2 getSurfaceVelocity() const; - /** Set the restitution.*/ - void setRestitution(float restitution); - /** Set the friction.*/ - void setFriction(float friction); - /** Set the surface velocity.*/ - void setSurfaceVelocity(const Vec2& velocity); - /** Ignore the rest of the contact presolve and postsolve callbacks. */ - void ignore(); - -private: - PhysicsContactPreSolve(void* contactInfo); - ~PhysicsContactPreSolve(); - -private: - void* _contactInfo; - - friend class EventListenerPhysicsContact; -}; - -/** - * @brief Postsolve value generated when onContactPostSolve called. - */ -class AX_DLL PhysicsContactPostSolve -{ -public: - /** Get restitution between two bodies.*/ - float getRestitution() const; - /** Get friction between two bodies.*/ - float getFriction() const; - /** Get surface velocity between two bodies.*/ - Vec2 getSurfaceVelocity() const; - -private: - PhysicsContactPostSolve(void* contactInfo); - ~PhysicsContactPostSolve(); - -private: - void* _contactInfo; - - friend class EventListenerPhysicsContact; -}; - -/** Contact listener. It will receive all the contact callbacks. */ -class AX_DLL EventListenerPhysicsContact : public EventListenerCustom -{ -public: - /** Create the listener. */ - static EventListenerPhysicsContact* create(); - - /** Check the listener is available. - - * @return True if there's one available callback function at least, false if there's no one. - */ - virtual bool checkAvailable() override; - - /** Clone an object from this listener.*/ - virtual EventListenerPhysicsContact* clone() override; - -protected: - /** - * It will be call when two body have contact. - * if return false, it will not invoke callbacks. - */ - virtual bool hitTest(PhysicsShape* shapeA, PhysicsShape* shapeB); - -public: - /** - * @brief It will called at two shapes start to contact, and only call it once. - */ - std::function onContactBegin; - /** - * @brief Two shapes are touching during this step. Return false from the callback to make world ignore the - * collision this step or true to process it normally. Additionally, you may override collision values, restitution, - * or surface velocity values. - */ - std::function onContactPreSolve; - /** - * @brief Two shapes are touching and their collision response has been processed. You can retrieve the collision - * impulse or kinetic energy at this time if you want to use it to calculate sound volumes or damage amounts. See - * cpArbiter for more info - */ - std::function onContactPostSolve; - /** - * @brief It will called at two shapes separated, and only call it once. - * onContactBegin and onContactSeparate will called in pairs. - */ - std::function onContactSeparate; - -protected: - bool init(); - void onEvent(EventCustom* event); - -protected: - EventListenerPhysicsContact(); - virtual ~EventListenerPhysicsContact(); - - friend class PhysicsWorld; -}; - -/** This event listener only be called when bodyA and bodyB have contacts. */ -class AX_DLL EventListenerPhysicsContactWithBodies : public EventListenerPhysicsContact -{ -public: - /** Create the listener. */ - static EventListenerPhysicsContactWithBodies* create(PhysicsBody* bodyA, PhysicsBody* bodyB); - - virtual bool hitTest(PhysicsShape* shapeA, PhysicsShape* shapeB) override; - - virtual EventListenerPhysicsContactWithBodies* clone() override; - -protected: - PhysicsBody* _a; - PhysicsBody* _b; - -protected: - EventListenerPhysicsContactWithBodies(); - virtual ~EventListenerPhysicsContactWithBodies(); -}; - -/** This event listener only be called when shapeA and shapeB have contacts. */ -class AX_DLL EventListenerPhysicsContactWithShapes : public EventListenerPhysicsContact -{ -public: - /** Create the listener. */ - static EventListenerPhysicsContactWithShapes* create(PhysicsShape* shapeA, PhysicsShape* shapeB); - - virtual bool hitTest(PhysicsShape* shapeA, PhysicsShape* shapeB) override; - virtual EventListenerPhysicsContactWithShapes* clone() override; - -protected: - PhysicsShape* _a; - PhysicsShape* _b; - -protected: - EventListenerPhysicsContactWithShapes(); - virtual ~EventListenerPhysicsContactWithShapes(); -}; - -/** This event listener only be called when shapeA or shapeB is in the group your specified */ -class AX_DLL EventListenerPhysicsContactWithGroup : public EventListenerPhysicsContact -{ -public: - /** Create the listener. */ - static EventListenerPhysicsContactWithGroup* create(int group); - - virtual bool hitTest(PhysicsShape* shapeA, PhysicsShape* shapeB) override; - virtual EventListenerPhysicsContactWithGroup* clone() override; - -protected: - int _group; - -protected: - EventListenerPhysicsContactWithGroup(); - virtual ~EventListenerPhysicsContactWithGroup(); -}; - -/** @} */ -/** @} */ - -} - -#endif // defined(AX_ENABLE_PHYSICS) -#endif //__CCPHYSICS_CONTACT_H__ diff --git a/core/physics/PhysicsHelper.h b/core/physics/PhysicsHelper.h index 80ccedc734a9..3cc10e83f07d 100644 --- a/core/physics/PhysicsHelper.h +++ b/core/physics/PhysicsHelper.h @@ -1,117 +1,30 @@ -/**************************************************************************** - Copyright (c) 2013 cocos2d-x.org - Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - Copyright (c) 2019-present Axmol Engine contributors (see AUTHORS.md). +#pragma once - https://axmol.dev/ - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - ****************************************************************************/ - -#ifndef __CCPHYSICS_HELPER_H__ -#define __CCPHYSICS_HELPER_H__ - -#include "base/Config.h" -#if defined(AX_ENABLE_PHYSICS) - -# include "chipmunk/chipmunk.h" -# include "platform/PlatformMacros.h" -# include "math/Math.h" +#include "math/Math.h" +#include "box2d/box2d.h" namespace ax { - -/** - * @addtogroup physics - * @{ - * @addtogroup physics_2d - * @{ - */ - -/** - * A physics helper class. - * - * Support for conversion between the chipmunk types and cocos types, eg: cpVect to Vec2, Vec2 to cpVect, cpFloat to - * float. - */ -class PhysicsHelper +struct PhysicsHelper { -public: - /** Make cpVect type convert to Vec2 type. */ - static Vec2 cpv2vec2(const cpVect& vec) { return Vec2(vec.x, vec.y); } + static Vec2 toVec2(const b2Vec2& v) { return Vec2{v.x, v.y}; } - /** Make Vec2 type convert to cpVect type. */ - static cpVect vec22cpv(const Vec2& point) { return cpv(point.x, point.y); } + static b2Vec2 tob2Vec2(const Vec2& v) { return b2Vec2{v.x, v.y}; } - /** Make cpFloat type convert to float type. */ - static float cpfloat2float(cpFloat f) { return f; } - - /** Make Rect type convert to cpBB type. */ - static cpBB rect2cpbb(const Rect& rect) + static Color toColor(b2HexColor color) { - return cpBBNew(rect.origin.x, rect.origin.y, rect.origin.x + rect.size.width, rect.origin.y + rect.size.height); + unsigned int r = ((unsigned int)color >> 16) & 0xff; + unsigned int g = ((unsigned int)color >> 8) & 0xff; + unsigned int b = ((unsigned int)color) & 0xff; + return ax::Color{r / 255.f, g / 255.f, b / 255.f, 1.0f}; } - /** Make cpBB type convert to Rect type. */ - static Rect cpbb2rect(const cpBB& bb) { return Rect(bb.l, bb.b, bb.r - bb.l, bb.t - bb.b); } - - /** - Make cpVect array convert to Vec2 array. - - @param cpvs The be converted object, it's a cpVect array. - @param out The converted object, it's a Vec2 array. - @param count It's cpvs array length. - @return The out object's pointer. - */ - static Vec2* cpvs2points(const cpVect* cpvs, Vec2* out, int count) + static b2HexColor tob2HexColor(Color color) { - for (int i = 0; i < count; ++i) - { - out[i] = cpv2vec2(cpvs[i]); - } - - return out; - } - - /** - Make Vec2 array convert to cpVect array. - - @param points The be converted object, it's a Vec2 array. - @param out The converted object, it's a cpVect array. - @param count It's points array length. - @return The out object's pointer. - */ - static cpVect* points2cpvs(const Vec2* points, cpVect* out, int count) - { - for (int i = 0; i < count; ++i) - { - out[i] = vec22cpv(points[i]); - } - - return out; + Color3B ret(color); + return (b2HexColor)(static_cast(ret.r) << 16 | static_cast(ret.g) << 8 | + static_cast(ret.b)); } }; -/** @} */ -/** @} */ - -} - -#endif // AX_ENABLE_PHYSICS -#endif // __CCPHYSICS_HELPER_H__ +} // namespace ax diff --git a/core/physics/PhysicsJoint.cpp b/core/physics/PhysicsJoint.cpp deleted file mode 100644 index 33063b046c8e..000000000000 --- a/core/physics/PhysicsJoint.cpp +++ /dev/null @@ -1,936 +0,0 @@ -/**************************************************************************** - Copyright (c) 2013-2016 Chukong Technologies Inc. - Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - Copyright (c) 2019-present Axmol Engine contributors (see AUTHORS.md). - - https://axmol.dev/ - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - ****************************************************************************/ - -#include "physics/PhysicsJoint.h" -#if defined(AX_ENABLE_PHYSICS) -# include "chipmunk/chipmunk.h" - -# include "physics/PhysicsBody.h" -# include "physics/PhysicsWorld.h" -# include "physics/PhysicsHelper.h" -# include "2d/Node.h" - -namespace ax -{ - -template -class Optional -{ - -public: - Optional() {} - Optional(T d) : _isSet(true), _data(d) {} - Optional(const Optional& t) : _isSet(t._isSet), _data(t._data) {} - - // bool isNull() const { return !_isSet; } - // bool isDefineded() const { return _isSet; } - // bool isEmpty() const { return !_isSet; } - - T get() const - { - AXASSERT(_isSet, "data should be set!"); - return _data; - } - void set(T d) - { - _isSet = true; - _data = d; - } - -private: - bool _isSet = false; - T _data; -}; - -class WriteCache -{ -public: - Optional _grooveA; - Optional _grooveB; - Optional _anchr1; - Optional _anchr2; - Optional _min; - Optional _max; - Optional _distance; - Optional _restLength; - Optional _restAngle; - Optional _stiffness; - Optional _damping; - Optional _angle; - Optional _phase; - Optional _ratchet; - Optional _ratio; - Optional _rate; -}; - -# if (defined(__GNUC__) && __GNUC__ >= 4) || defined(__clang__) -# define LIKELY(x) (__builtin_expect((x), 1)) -# define UNLIKELY(x) (__builtin_expect((x), 0)) -# else -# define LIKELY(x) (x) -# define UNLIKELY(x) (x) -# endif - -# define AX_PJOINT_CACHE_READ(field) \ - do \ - { \ - if (UNLIKELY(_initDirty)) \ - { \ - return _writeCache->field.get(); \ - } \ - } while (0) - -# define AX_PJOINT_CACHE_WRITE2(field, method, arg, convertedArg) \ - do \ - { \ - if (UNLIKELY(_initDirty)) \ - { \ - _writeCache->field.set(arg); \ - delay([this, arg]() { method(_cpConstraints.front(), convertedArg); }); \ - } \ - else \ - { \ - method(_cpConstraints.front(), convertedArg); \ - } \ - } while (0) - -# define AX_PJOINT_CACHE_WRITE(field, method, arg) AX_PJOINT_CACHE_WRITE2(field, method, arg, arg) - -PhysicsJoint::PhysicsJoint() - : _bodyA(nullptr) - , _bodyB(nullptr) - , _world(nullptr) - , _enable(false) - , _collisionEnable(true) - , _destroyMark(false) - , _tag(0) - , _maxForce(PHYSICS_INFINITY) - , _initDirty(true) -{ - _writeCache = new WriteCache(); -} - -PhysicsJoint::~PhysicsJoint() -{ - // reset the shapes collision group - setCollisionEnable(true); - - for (cpConstraint* joint : _cpConstraints) - { - cpConstraintFree(joint); - } - _cpConstraints.clear(); - - delete _writeCache; -} - -bool PhysicsJoint::init(ax::PhysicsBody* a, ax::PhysicsBody* b) -{ - do - { - AXASSERT(a != nullptr && b != nullptr, "the body passed in is nil"); - AXASSERT(a != b, "the two bodies are equal"); - - _bodyA = a; - _bodyB = b; - _bodyA->_joints.emplace_back(this); - _bodyB->_joints.emplace_back(this); - - return true; - } while (false); - - return false; -} - -bool PhysicsJoint::initJoint() -{ - bool ret = !_initDirty; - while (_initDirty) - { - ret = createConstraints(); - AX_BREAK_IF(!ret); - - for (auto&& subjoint : _cpConstraints) - { - cpConstraintSetMaxForce(subjoint, _maxForce); - cpConstraintSetErrorBias(subjoint, cpfpow(1.0f - 0.15f, 60.0f)); - cpSpaceAddConstraint(_world->_cpSpace, subjoint); - } - _initDirty = false; - ret = true; - } - - return ret; -} - -void PhysicsJoint::flushDelayTasks() -{ - for (const auto& tsk : _delayTasks) - { - tsk(); - } - _delayTasks.clear(); -} - -void PhysicsJoint::setEnable(bool enable) -{ - if (_enable != enable) - { - _enable = enable; - - if (_world) - { - if (enable) - { - _world->addJoint(this); - } - else - { - _world->removeJoint(this, false); - } - } - } -} - -void PhysicsJoint::setCollisionEnable(bool enable) -{ - if (_collisionEnable != enable) - { - _collisionEnable = enable; - } -} - -void PhysicsJoint::removeFormWorld() -{ - if (_world) - { - _world->removeJoint(this, false); - } -} - -void PhysicsJoint::setMaxForce(float force) -{ - if (_initDirty) - { - delay([this, force]() { - _maxForce = force; - for (auto&& joint : _cpConstraints) - { - cpConstraintSetMaxForce(joint, force); - } - }); - } - else - { - _maxForce = force; - for (auto&& joint : _cpConstraints) - { - cpConstraintSetMaxForce(joint, force); - } - } -} - -PhysicsJointFixed* PhysicsJointFixed::construct(PhysicsBody* a, PhysicsBody* b, const Vec2& anchr) -{ - auto joint = new PhysicsJointFixed(); - - if (joint->init(a, b)) - { - joint->_anchr = anchr; - return joint; - } - - AX_SAFE_DELETE(joint); - return nullptr; -} - -bool PhysicsJointFixed::createConstraints() -{ - do - { - _bodyA->getNode()->setPosition(_anchr); - _bodyB->getNode()->setPosition(_anchr); - - // add a pivot joint to fixed two body together - auto joint = cpPivotJointNew(_bodyA->getCPBody(), _bodyB->getCPBody(), PhysicsHelper::vec22cpv(_anchr)); - AX_BREAK_IF(joint == nullptr); - _cpConstraints.emplace_back(joint); - - // add a gear joint to make two body have the same rotation. - joint = cpGearJointNew(_bodyA->getCPBody(), _bodyB->getCPBody(), 0, 1); - AX_BREAK_IF(joint == nullptr); - _cpConstraints.emplace_back(joint); - - _collisionEnable = false; - - return true; - } while (false); - - return false; -} - -PhysicsJointPin* PhysicsJointPin::construct(PhysicsBody* a, PhysicsBody* b, const Vec2& pivot) -{ - auto joint = new PhysicsJointPin(); - - if (joint->init(a, b)) - { - joint->_anchr1 = pivot; - joint->_useSpecificAnchr = false; - return joint; - } - - AX_SAFE_DELETE(joint); - return nullptr; -} - -PhysicsJointPin* PhysicsJointPin::construct(PhysicsBody* a, PhysicsBody* b, const Vec2& anchr1, const Vec2& anchr2) -{ - auto joint = new PhysicsJointPin(); - - if (joint->init(a, b)) - { - joint->_anchr1 = anchr1; - joint->_anchr2 = anchr2; - joint->_useSpecificAnchr = true; - - return joint; - } - - AX_SAFE_DELETE(joint); - return nullptr; -} - -bool PhysicsJointPin::createConstraints() -{ - do - { - cpConstraint* joint = nullptr; - if (_useSpecificAnchr) - { - joint = cpPivotJointNew2(_bodyA->getCPBody(), _bodyB->getCPBody(), PhysicsHelper::vec22cpv(_anchr1), - PhysicsHelper::vec22cpv(_anchr2)); - } - else - { - joint = cpPivotJointNew(_bodyA->getCPBody(), _bodyB->getCPBody(), PhysicsHelper::vec22cpv(_anchr1)); - } - - AX_BREAK_IF(joint == nullptr); - _cpConstraints.emplace_back(joint); - - return true; - } while (false); - - return false; -} - -PhysicsJointLimit* PhysicsJointLimit::construct(PhysicsBody* a, - PhysicsBody* b, - const Vec2& anchr1, - const Vec2& anchr2, - float min, - float max) -{ - auto joint = new PhysicsJointLimit(); - - if (joint->init(a, b)) - { - joint->_anchr1 = anchr1; - joint->_anchr2 = anchr2; - joint->_min = min; - joint->_max = max; - - return joint; - } - - AX_SAFE_DELETE(joint); - return nullptr; -} - -PhysicsJointLimit* PhysicsJointLimit::construct(PhysicsBody* a, PhysicsBody* b, const Vec2& anchr1, const Vec2& anchr2) -{ - return construct(a, b, anchr1, anchr2, 0, b->local2World(anchr1).getDistance(a->local2World(anchr2))); -} - -bool PhysicsJointLimit::createConstraints() -{ - do - { - auto joint = cpSlideJointNew(_bodyA->getCPBody(), _bodyB->getCPBody(), PhysicsHelper::vec22cpv(_anchr1), - PhysicsHelper::vec22cpv(_anchr2), _min, _max); - - AX_BREAK_IF(joint == nullptr); - _cpConstraints.emplace_back(joint); - - return true; - } while (false); - - return false; -} - -float PhysicsJointLimit::getMin() const -{ - AX_PJOINT_CACHE_READ(_min); - return PhysicsHelper::cpfloat2float(cpSlideJointGetMin(_cpConstraints.front())); -} - -void PhysicsJointLimit::setMin(float min) -{ - AX_PJOINT_CACHE_WRITE(_min, cpSlideJointSetMin, min); -} - -float PhysicsJointLimit::getMax() const -{ - AX_PJOINT_CACHE_READ(_max); - return PhysicsHelper::cpfloat2float(cpSlideJointGetMax(_cpConstraints.front())); -} - -void PhysicsJointLimit::setMax(float max) -{ - AX_PJOINT_CACHE_WRITE(_max, cpSlideJointSetMax, max); -} - -Vec2 PhysicsJointLimit::getAnchr1() const -{ - AX_PJOINT_CACHE_READ(_anchr1); - return PhysicsHelper::cpv2vec2(cpSlideJointGetAnchorA(_cpConstraints.front())); -} - -void PhysicsJointLimit::setAnchr1(const Vec2& anchr) -{ - AX_PJOINT_CACHE_WRITE2(_anchr1, cpSlideJointSetAnchorA, anchr, PhysicsHelper::vec22cpv(anchr)); -} - -Vec2 PhysicsJointLimit::getAnchr2() const -{ - AX_PJOINT_CACHE_READ(_anchr2); - return PhysicsHelper::cpv2vec2(cpSlideJointGetAnchorB(_cpConstraints.front())); -} - -void PhysicsJointLimit::setAnchr2(const Vec2& anchr) -{ - AX_PJOINT_CACHE_WRITE2(_anchr2, cpSlideJointSetAnchorB, anchr, PhysicsHelper::vec22cpv(anchr)); -} - -PhysicsJointDistance* PhysicsJointDistance::construct(PhysicsBody* a, - PhysicsBody* b, - const Vec2& anchr1, - const Vec2& anchr2) -{ - auto joint = new PhysicsJointDistance(); - - if (joint->init(a, b)) - { - joint->_anchr1 = anchr1; - joint->_anchr2 = anchr2; - - return joint; - } - - AX_SAFE_DELETE(joint); - return nullptr; -} - -bool PhysicsJointDistance::createConstraints() -{ - do - { - auto joint = cpPinJointNew(_bodyA->getCPBody(), _bodyB->getCPBody(), PhysicsHelper::vec22cpv(_anchr1), - PhysicsHelper::vec22cpv(_anchr2)); - AX_BREAK_IF(joint == nullptr); - _cpConstraints.emplace_back(joint); - - return true; - } while (false); - - return false; -} - -float PhysicsJointDistance::getDistance() const -{ - AX_PJOINT_CACHE_READ(_distance); - return PhysicsHelper::cpfloat2float(cpPinJointGetDist(_cpConstraints.front())); -} - -void PhysicsJointDistance::setDistance(float distance) -{ - AX_PJOINT_CACHE_WRITE(_distance, cpPinJointSetDist, distance); -} - -PhysicsJointSpring* PhysicsJointSpring::construct(PhysicsBody* a, - PhysicsBody* b, - const Vec2& anchr1, - const Vec2& anchr2, - float stiffness, - float damping) -{ - auto joint = new PhysicsJointSpring(); - - if (joint->init(a, b)) - { - joint->_anchr1 = anchr1; - joint->_anchr2 = anchr2; - joint->_stiffness = stiffness; - joint->_damping = damping; - - return joint; - } - - AX_SAFE_DELETE(joint); - return nullptr; -} - -bool PhysicsJointSpring::createConstraints() -{ - do - { - auto joint = cpDampedSpringNew(_bodyA->getCPBody(), _bodyB->getCPBody(), PhysicsHelper::vec22cpv(_anchr1), - PhysicsHelper::vec22cpv(_anchr2), - _bodyB->local2World(_anchr1).getDistance(_bodyA->local2World(_anchr2)), - _stiffness, _damping); - - AX_BREAK_IF(joint == nullptr); - _cpConstraints.emplace_back(joint); - - return true; - } while (false); - - return false; -} - -Vec2 PhysicsJointSpring::getAnchr1() const -{ - AX_PJOINT_CACHE_READ(_anchr1); - return PhysicsHelper::cpv2vec2(cpDampedSpringGetAnchorA(_cpConstraints.front())); -} - -void PhysicsJointSpring::setAnchr1(const Vec2& anchr) -{ - AX_PJOINT_CACHE_WRITE2(_anchr1, cpDampedSpringSetAnchorA, anchr, PhysicsHelper::vec22cpv(anchr)); -} - -Vec2 PhysicsJointSpring::getAnchr2() const -{ - AX_PJOINT_CACHE_READ(_anchr2); - return PhysicsHelper::cpv2vec2(cpDampedSpringGetAnchorB(_cpConstraints.front())); -} - -void PhysicsJointSpring::setAnchr2(const Vec2& anchr) -{ - AX_PJOINT_CACHE_WRITE2(_anchr2, cpDampedSpringSetAnchorB, anchr, PhysicsHelper::vec22cpv(anchr)); -} - -float PhysicsJointSpring::getRestLength() const -{ - AX_PJOINT_CACHE_READ(_restLength); - return PhysicsHelper::cpfloat2float(cpDampedSpringGetRestLength(_cpConstraints.front())); -} - -void PhysicsJointSpring::setRestLength(float restLength) -{ - AX_PJOINT_CACHE_WRITE(_restLength, cpDampedSpringSetRestLength, restLength); -} - -float PhysicsJointSpring::getStiffness() const -{ - AX_PJOINT_CACHE_READ(_stiffness); - return PhysicsHelper::cpfloat2float(cpDampedSpringGetStiffness(_cpConstraints.front())); -} - -void PhysicsJointSpring::setStiffness(float stiffness) -{ - AX_PJOINT_CACHE_WRITE(_stiffness, cpDampedSpringSetStiffness, stiffness); -} - -float PhysicsJointSpring::getDamping() const -{ - AX_PJOINT_CACHE_READ(_damping); - return PhysicsHelper::cpfloat2float(cpDampedSpringGetDamping(_cpConstraints.front())); -} - -void PhysicsJointSpring::setDamping(float damping) -{ - AX_PJOINT_CACHE_WRITE(_damping, cpDampedSpringSetDamping, damping); -} - -PhysicsJointGroove* PhysicsJointGroove::construct(PhysicsBody* a, - PhysicsBody* b, - const Vec2& grooveA, - const Vec2& grooveB, - const Vec2& anchr2) -{ - auto joint = new PhysicsJointGroove(); - - if (joint->init(a, b)) - { - joint->_grooveA = grooveA; - joint->_grooveB = grooveB; - joint->_anchr2 = anchr2; - - return joint; - } - - AX_SAFE_DELETE(joint); - return nullptr; -} - -bool PhysicsJointGroove::createConstraints() -{ - do - { - auto joint = cpGrooveJointNew(_bodyA->getCPBody(), _bodyB->getCPBody(), PhysicsHelper::vec22cpv(_grooveA), - PhysicsHelper::vec22cpv(_grooveB), PhysicsHelper::vec22cpv(_anchr2)); - - AX_BREAK_IF(joint == nullptr); - _cpConstraints.emplace_back(joint); - - return true; - } while (false); - - return false; -} - -Vec2 PhysicsJointGroove::getGrooveA() const -{ - AX_PJOINT_CACHE_READ(_grooveA); - return PhysicsHelper::cpv2vec2(cpGrooveJointGetGrooveA(_cpConstraints.front())); -} - -void PhysicsJointGroove::setGrooveA(const Vec2& grooveA) -{ - AX_PJOINT_CACHE_WRITE2(_grooveA, cpGrooveJointSetGrooveA, grooveA, PhysicsHelper::vec22cpv(grooveA)); -} - -Vec2 PhysicsJointGroove::getGrooveB() const -{ - AX_PJOINT_CACHE_READ(_grooveB); - return PhysicsHelper::cpv2vec2(cpGrooveJointGetGrooveB(_cpConstraints.front())); -} - -void PhysicsJointGroove::setGrooveB(const Vec2& grooveB) -{ - AX_PJOINT_CACHE_WRITE2(_grooveB, cpGrooveJointSetGrooveB, grooveB, PhysicsHelper::vec22cpv(grooveB)); -} - -Vec2 PhysicsJointGroove::getAnchr2() const -{ - AX_PJOINT_CACHE_READ(_anchr2); - return PhysicsHelper::cpv2vec2(cpGrooveJointGetAnchorB(_cpConstraints.front())); -} - -void PhysicsJointGroove::setAnchr2(const Vec2& anchr2) -{ - AX_PJOINT_CACHE_WRITE2(_anchr2, cpGrooveJointSetAnchorB, anchr2, PhysicsHelper::vec22cpv(anchr2)); -} - -PhysicsJointRotarySpring* PhysicsJointRotarySpring::construct(PhysicsBody* a, - PhysicsBody* b, - float stiffness, - float damping) -{ - auto joint = new PhysicsJointRotarySpring(); - - if (joint->init(a, b)) - { - joint->_stiffness = stiffness; - joint->_damping = damping; - - return joint; - } - - AX_SAFE_DELETE(joint); - return nullptr; -} - -bool PhysicsJointRotarySpring::createConstraints() -{ - do - { - auto joint = cpDampedRotarySpringNew(_bodyA->getCPBody(), _bodyB->getCPBody(), - _bodyB->getRotation() - _bodyA->getRotation(), _stiffness, _damping); - - AX_BREAK_IF(joint == nullptr); - _cpConstraints.emplace_back(joint); - - return true; - } while (false); - - return false; -} - -float PhysicsJointRotarySpring::getRestAngle() const -{ - AX_PJOINT_CACHE_READ(_restAngle); - return PhysicsHelper::cpfloat2float(cpDampedRotarySpringGetRestAngle(_cpConstraints.front())); -} - -void PhysicsJointRotarySpring::setRestAngle(float restAngle) -{ - AX_PJOINT_CACHE_WRITE(_restAngle, cpDampedRotarySpringSetRestAngle, restAngle); -} - -float PhysicsJointRotarySpring::getStiffness() const -{ - AX_PJOINT_CACHE_READ(_stiffness); - return PhysicsHelper::cpfloat2float(cpDampedRotarySpringGetStiffness(_cpConstraints.front())); -} - -void PhysicsJointRotarySpring::setStiffness(float stiffness) -{ - AX_PJOINT_CACHE_WRITE(_stiffness, cpDampedRotarySpringSetStiffness, stiffness); -} - -float PhysicsJointRotarySpring::getDamping() const -{ - AX_PJOINT_CACHE_READ(_damping); - return PhysicsHelper::cpfloat2float(cpDampedRotarySpringGetDamping(_cpConstraints.front())); -} - -void PhysicsJointRotarySpring::setDamping(float damping) -{ - AX_PJOINT_CACHE_WRITE(_damping, cpDampedRotarySpringSetDamping, damping); -} - -PhysicsJointRotaryLimit* PhysicsJointRotaryLimit::construct(PhysicsBody* a, PhysicsBody* b, float min, float max) -{ - auto joint = new PhysicsJointRotaryLimit(); - - if (joint->init(a, b)) - { - joint->_min = min; - joint->_max = max; - - return joint; - } - - AX_SAFE_DELETE(joint); - return nullptr; -} - -PhysicsJointRotaryLimit* PhysicsJointRotaryLimit::construct(PhysicsBody* a, PhysicsBody* b) -{ - return construct(a, b, 0.0f, 0.0f); -} - -bool PhysicsJointRotaryLimit::createConstraints() -{ - do - { - auto joint = cpRotaryLimitJointNew(_bodyA->getCPBody(), _bodyB->getCPBody(), _min, _max); - - AX_BREAK_IF(joint == nullptr); - _cpConstraints.emplace_back(joint); - - return true; - } while (false); - - return false; -} - -float PhysicsJointRotaryLimit::getMin() const -{ - AX_PJOINT_CACHE_READ(_min); - return PhysicsHelper::cpfloat2float(cpRotaryLimitJointGetMin(_cpConstraints.front())); -} - -void PhysicsJointRotaryLimit::setMin(float min) -{ - AX_PJOINT_CACHE_WRITE(_min, cpRotaryLimitJointSetMin, min); -} - -float PhysicsJointRotaryLimit::getMax() const -{ - AX_PJOINT_CACHE_READ(_max); - return PhysicsHelper::cpfloat2float(cpRotaryLimitJointGetMax(_cpConstraints.front())); -} - -void PhysicsJointRotaryLimit::setMax(float max) -{ - AX_PJOINT_CACHE_WRITE(_max, cpRotaryLimitJointSetMax, max); -} - -PhysicsJointRatchet* PhysicsJointRatchet::construct(PhysicsBody* a, PhysicsBody* b, float phase, float ratchet) -{ - auto joint = new PhysicsJointRatchet(); - - if (joint->init(a, b)) - { - joint->_phase = phase; - joint->_ratchet = ratchet; - - return joint; - } - - AX_SAFE_DELETE(joint); - return nullptr; -} - -bool PhysicsJointRatchet::createConstraints() -{ - do - { - auto joint = - cpRatchetJointNew(_bodyA->getCPBody(), _bodyB->getCPBody(), _phase, PhysicsHelper::cpfloat2float(_ratchet)); - - AX_BREAK_IF(joint == nullptr); - _cpConstraints.emplace_back(joint); - - return true; - } while (false); - - return false; -} - -float PhysicsJointRatchet::getAngle() const -{ - AX_PJOINT_CACHE_READ(_angle); - return PhysicsHelper::cpfloat2float(cpRatchetJointGetAngle(_cpConstraints.front())); -} - -void PhysicsJointRatchet::setAngle(float angle) -{ - AX_PJOINT_CACHE_WRITE(_angle, cpRatchetJointSetAngle, angle); -} - -float PhysicsJointRatchet::getPhase() const -{ - AX_PJOINT_CACHE_READ(_phase); - return PhysicsHelper::cpfloat2float(cpRatchetJointGetPhase(_cpConstraints.front())); -} - -void PhysicsJointRatchet::setPhase(float phase) -{ - AX_PJOINT_CACHE_WRITE(_phase, cpRatchetJointSetPhase, phase); -} - -float PhysicsJointRatchet::getRatchet() const -{ - AX_PJOINT_CACHE_READ(_ratchet); - return PhysicsHelper::cpfloat2float(cpRatchetJointGetRatchet(_cpConstraints.front())); -} - -void PhysicsJointRatchet::setRatchet(float ratchet) -{ - AX_PJOINT_CACHE_WRITE(_ratchet, cpRatchetJointSetRatchet, ratchet); -} - -PhysicsJointGear* PhysicsJointGear::construct(PhysicsBody* a, PhysicsBody* b, float phase, float ratio) -{ - auto joint = new PhysicsJointGear(); - - if (joint->init(a, b)) - { - joint->_phase = phase; - joint->_ratio = ratio; - - return joint; - } - - AX_SAFE_DELETE(joint); - return nullptr; -} - -bool PhysicsJointGear::createConstraints() -{ - do - { - auto joint = cpGearJointNew(_bodyA->getCPBody(), _bodyB->getCPBody(), _phase, _ratio); - - AX_BREAK_IF(joint == nullptr); - _cpConstraints.emplace_back(joint); - - return true; - } while (false); - - return false; -} - -float PhysicsJointGear::getPhase() const -{ - AX_PJOINT_CACHE_READ(_phase); - return PhysicsHelper::cpfloat2float(cpGearJointGetPhase(_cpConstraints.front())); -} - -void PhysicsJointGear::setPhase(float phase) -{ - AX_PJOINT_CACHE_WRITE(_phase, cpGearJointSetPhase, phase); -} - -float PhysicsJointGear::getRatio() const -{ - AX_PJOINT_CACHE_READ(_ratio); - return PhysicsHelper::cpfloat2float(cpGearJointGetRatio(_cpConstraints.front())); -} - -void PhysicsJointGear::setRatio(float ratio) -{ - AX_PJOINT_CACHE_WRITE(_ratio, cpGearJointSetRatio, ratio); -} - -PhysicsJointMotor* PhysicsJointMotor::construct(PhysicsBody* a, PhysicsBody* b, float rate) -{ - auto joint = new PhysicsJointMotor(); - - if (joint->init(a, b)) - { - joint->_rate = rate; - - return joint; - } - - AX_SAFE_DELETE(joint); - return nullptr; -} - -bool PhysicsJointMotor::createConstraints() -{ - do - { - auto joint = cpSimpleMotorNew(_bodyA->getCPBody(), _bodyB->getCPBody(), _rate); - - AX_BREAK_IF(joint == nullptr); - _cpConstraints.emplace_back(joint); - - return true; - } while (false); - - return false; -} - -float PhysicsJointMotor::getRate() const -{ - AX_PJOINT_CACHE_READ(_rate); - return PhysicsHelper::cpfloat2float(cpSimpleMotorGetRate(_cpConstraints.front())); -} - -void PhysicsJointMotor::setRate(float rate) -{ - AX_PJOINT_CACHE_WRITE(_rate, cpSimpleMotorSetRate, rate); -} - -} -#endif // AX_ENABLE_PHYSICS diff --git a/core/physics/PhysicsJoint.h b/core/physics/PhysicsJoint.h deleted file mode 100644 index 148108a1e417..000000000000 --- a/core/physics/PhysicsJoint.h +++ /dev/null @@ -1,609 +0,0 @@ -/**************************************************************************** - Copyright (c) 2013-2016 Chukong Technologies Inc. - Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - Copyright (c) 2019-present Axmol Engine contributors (see AUTHORS.md). - - https://axmol.dev/ - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - ****************************************************************************/ - -#ifndef __CCPHYSICS_JOINT_H__ -#define __CCPHYSICS_JOINT_H__ - -#include - -#include "base/Config.h" -#if defined(AX_ENABLE_PHYSICS) - -# include "base/Object.h" -# include "math/Math.h" - -struct cpConstraint; - -namespace ax -{ - -class Node; -class PhysicsBody; -class PhysicsWorld; - -class WriteCache; - -/** - * @addtogroup physics - * @{ - * @addtogroup physics_2d - * @{ - */ - -/** - * @brief An PhysicsJoint object connects two physics bodies together. - */ -class AX_DLL PhysicsJoint -{ -protected: - typedef std::function DelayTask; - -protected: - PhysicsJoint(); - virtual ~PhysicsJoint() = 0; - -public: - /**Get physics body a connected to this joint.*/ - PhysicsBody* getBodyA() const { return _bodyA; } - - /**Get physics body b connected to this joint.*/ - PhysicsBody* getBodyB() const { return _bodyB; } - - /**Get the physics world.*/ - PhysicsWorld* getWorld() const { return _world; } - - /** - * Get this joint's tag. - * - * @return An integer number. - */ - int getTag() const { return _tag; } - - /** - * Set this joint's tag. - * - * @param tag An integer number that identifies a PhysicsJoint. - */ - void setTag(int tag) { _tag = tag; } - - /** Determines if the joint is enable. */ - bool isEnabled() const { return _enable; } - - /** Enable/Disable the joint. */ - void setEnable(bool enable); - - /** Determines if the collision is enable. */ - bool isCollisionEnabled() const { return _collisionEnable; } - - /** Enable/disable the collision between two bodies. */ - void setCollisionEnable(bool enable); - - /** Remove the joint from the world. */ - void removeFormWorld(); - - /** Set the max force between two bodies. */ - void setMaxForce(float force); - - /** Get the max force setting. */ - float getMaxForce() const { return _maxForce; } - -protected: - bool init(PhysicsBody* a, PhysicsBody* b); - - bool initJoint(); - - void delay(const DelayTask& task) { _delayTasks.emplace_back(task); } - - void flushDelayTasks(); - - /** Create constraints for this type joint */ - virtual bool createConstraints() { return false; } - - std::vector _cpConstraints; - std::vector _delayTasks; - PhysicsBody* _bodyA; - PhysicsBody* _bodyB; - PhysicsWorld* _world; - - WriteCache* _writeCache = nullptr; - - bool _enable; - bool _collisionEnable; - bool _destroyMark; - int _tag; - float _maxForce; - - bool _initDirty; - - friend class PhysicsBody; - friend class PhysicsWorld; - friend class PhysicsDebugDraw; -}; - -/** - * @brief A fixed joint fuses the two bodies together at a reference point. Fixed joints are useful for creating complex - * shapes that can be broken apart later. - */ -class AX_DLL PhysicsJointFixed : public PhysicsJoint -{ -public: - /** Create a fixed joint. - - @param a A is the body to connect. - @param b B is the body to connect. - @param anchr It's the pivot position. - @return A object pointer. - */ - static PhysicsJointFixed* construct(PhysicsBody* a, PhysicsBody* b, const Vec2& anchr); - - virtual bool createConstraints() override; - -protected: - PhysicsJointFixed() {} - virtual ~PhysicsJointFixed() {} - - Vec2 _anchr; -}; - -/** - * @brief A limit joint imposes a maximum distance between the two bodies, as if they were connected by a rope. - */ -class AX_DLL PhysicsJointLimit : public PhysicsJoint -{ -public: - /** Create a limit joint. - - @param a A is the body to connect. - @param b B is the body to connect. - @param anchr1 Anchr1 is the anchor point on body a. - @param anchr2 Anchr2 is the anchor point on body b. - @return A object pointer. - */ - static PhysicsJointLimit* construct(PhysicsBody* a, PhysicsBody* b, const Vec2& anchr1, const Vec2& anchr2); - - /** Create a limit joint. - - @param a A is the body to connect. - @param b B is the body to connect. - @param anchr1 Anchr1 is the anchor point on body a. - @param anchr2 Anchr2 is the anchor point on body b. - @param min Define the allowed min distance of the anchor points. - @param max Define the allowed max distance of the anchor points. - @return A object pointer. - */ - static PhysicsJointLimit* construct(PhysicsBody* a, - PhysicsBody* b, - const Vec2& anchr1, - const Vec2& anchr2, - float min, - float max); - - /** Get the anchor point on body a.*/ - Vec2 getAnchr1() const; - - /** Set the anchor point on body a.*/ - void setAnchr1(const Vec2& anchr1); - - /** Get the anchor point on body b.*/ - Vec2 getAnchr2() const; - - /** Set the anchor point on body b.*/ - void setAnchr2(const Vec2& anchr2); - - /** Get the allowed min distance of the anchor points.*/ - float getMin() const; - /** Set the min distance of the anchor points.*/ - void setMin(float min); - - /** Get the allowed max distance of the anchor points.*/ - float getMax() const; - /** Set the max distance of the anchor points.*/ - void setMax(float max); - - virtual bool createConstraints() override; - -protected: - PhysicsJointLimit() {} - virtual ~PhysicsJointLimit() {} - - Vec2 _anchr1; - Vec2 _anchr2; - float _min; - float _max; -}; - -/** - * @brief A pin joint allows the two bodies to independently rotate around the anchor point as if pinned together. - */ -class AX_DLL PhysicsJointPin : public PhysicsJoint -{ -public: - /** Create a pin joint. - - @param a A is the body to connect. - @param b B is the body to connect. - @param pivot It's the pivot position. - @return A object pointer. - */ - static PhysicsJointPin* construct(PhysicsBody* a, PhysicsBody* b, const Vec2& pivot); - - /** Create a pin joint. - - @param a A is the body to connect. - @param b B is the body to connect. - @param anchr1 Anchr1 is the anchor point on body a. - @param anchr2 Anchr2 is the anchor point on body b. - @return A object pointer. - */ - static PhysicsJointPin* construct(PhysicsBody* a, PhysicsBody* b, const Vec2& anchr1, const Vec2& anchr2); - - virtual bool createConstraints() override; - -protected: - PhysicsJointPin() {} - virtual ~PhysicsJointPin() {} - - bool _useSpecificAnchr; - Vec2 _anchr1; - Vec2 _anchr2; -}; - -/** Set the fixed distance with two bodies */ -class AX_DLL PhysicsJointDistance : public PhysicsJoint -{ -public: - /** Create a fixed distance joint. - - @param a A is the body to connect. - @param b B is the body to connect. - @param anchr1 Anchr1 is the anchor point on body a. - @param anchr2 Anchr2 is the anchor point on body b. - @return A object pointer. - */ - static PhysicsJointDistance* construct(PhysicsBody* a, PhysicsBody* b, const Vec2& anchr1, const Vec2& anchr2); - - /** Get the distance of the anchor points.*/ - float getDistance() const; - /** Set the distance of the anchor points.*/ - void setDistance(float distance); - virtual bool createConstraints() override; - -protected: - PhysicsJointDistance() {} - virtual ~PhysicsJointDistance() {} - - Vec2 _anchr1; - Vec2 _anchr2; -}; - -/** Connecting two physics bodies together with a spring. */ -class AX_DLL PhysicsJointSpring : public PhysicsJoint -{ -public: - /** Create a fixed distance joint. - - @param a A is the body to connect. - @param b B is the body to connect. - @param anchr1 Anchr1 is the anchor point on body a. - @param anchr2 Anchr2 is the anchor point on body b. - @param stiffness It's the spring constant. - @param damping It's how soft to make the damping of the spring. - @return A object pointer. - */ - static PhysicsJointSpring* construct(PhysicsBody* a, - PhysicsBody* b, - const Vec2& anchr1, - const Vec2& anchr2, - float stiffness, - float damping); - - /** Get the anchor point on body a.*/ - Vec2 getAnchr1() const; - - /** Set the anchor point on body a.*/ - void setAnchr1(const Vec2& anchr1); - - /** Get the anchor point on body b.*/ - Vec2 getAnchr2() const; - - /** Set the anchor point on body b.*/ - void setAnchr2(const Vec2& anchr2); - - /** Get the distance of the anchor points.*/ - float getRestLength() const; - - /** Set the distance of the anchor points.*/ - void setRestLength(float restLength); - - /** Get the spring constant.*/ - float getStiffness() const; - - /** Set the spring constant.*/ - void setStiffness(float stiffness); - - /** Get the spring soft constant.*/ - float getDamping() const; - - /** Set the spring soft constant.*/ - void setDamping(float damping); - - virtual bool createConstraints() override; - -protected: - PhysicsJointSpring() {} - virtual ~PhysicsJointSpring() {} - - Vec2 _anchr1; - Vec2 _anchr2; - float _stiffness; - float _damping; -}; - -/** Attach body a to a line, and attach body b to a dot. */ -class AX_DLL PhysicsJointGroove : public PhysicsJoint -{ -public: - /** Create a groove joint. - - @param a A is the body to connect. - @param b B is the body to connect. - @param grooveA The line begin position. - @param grooveB The line end position. - @param anchr2 Anchr2 is the anchor point on body b. - @return A object pointer. - */ - static PhysicsJointGroove* construct(PhysicsBody* a, - PhysicsBody* b, - const Vec2& grooveA, - const Vec2& grooveB, - const Vec2& anchr2); - - /** Get the line begin position*/ - Vec2 getGrooveA() const; - - /** Set the line begin position*/ - void setGrooveA(const Vec2& grooveA); - - /** Get the line end position*/ - Vec2 getGrooveB() const; - - /** Set the line end position*/ - void setGrooveB(const Vec2& grooveB); - - /** Get the anchor point on body b.*/ - Vec2 getAnchr2() const; - - /** Set the anchor point on body b.*/ - void setAnchr2(const Vec2& anchr2); - - virtual bool createConstraints() override; - -protected: - PhysicsJointGroove() {} - virtual ~PhysicsJointGroove() {} - - Vec2 _grooveA; - Vec2 _grooveB; - Vec2 _anchr2; -}; - -/** Likes a spring joint, but works with rotary. */ -class AX_DLL PhysicsJointRotarySpring : public PhysicsJoint -{ -public: - /** Create a damped rotary spring joint. - - @param a A is the body to connect. - @param b B is the body to connect. - @param stiffness It's the spring constant. - @param damping It's how soft to make the damping of the spring. - @return A object pointer. - */ - static PhysicsJointRotarySpring* construct(PhysicsBody* a, PhysicsBody* b, float stiffness, float damping); - - /** Get the relative angle in radians from the body a to b.*/ - float getRestAngle() const; - - /** Set the relative angle in radians from the body a to b.*/ - void setRestAngle(float restAngle); - - /** Get the spring constant.*/ - float getStiffness() const; - - /** Set the spring constant.*/ - void setStiffness(float stiffness); - - /** Get the spring soft constant.*/ - float getDamping() const; - - /** Set the spring soft constant.*/ - void setDamping(float damping); - - virtual bool createConstraints() override; - -protected: - PhysicsJointRotarySpring() {} - virtual ~PhysicsJointRotarySpring() {} - - float _stiffness; - float _damping; -}; - -/** Likes a limit joint, but works with rotary. */ -class AX_DLL PhysicsJointRotaryLimit : public PhysicsJoint -{ -public: - /** Create a limit rotary joint. - - @param a A is the body to connect. - @param b B is the body to connect. - @param min It's min rotation limit in radians. - @param max It's max rotation limit in radians. - @return A object pointer. - */ - static PhysicsJointRotaryLimit* construct(PhysicsBody* a, PhysicsBody* b, float min, float max); - - /** Create a limit rotary joint. - - @param a A is the body to connect. - @param b B is the body to connect. - @return A object pointer. - */ - static PhysicsJointRotaryLimit* construct(PhysicsBody* a, PhysicsBody* b); - - /** Get the min rotation limit.*/ - float getMin() const; - - /** Set the min rotation limit.*/ - void setMin(float min); - - /** Get the max rotation limit.*/ - float getMax() const; - - /** Set the max rotation limit.*/ - void setMax(float max); - - virtual bool createConstraints() override; - -protected: - PhysicsJointRotaryLimit() {} - virtual ~PhysicsJointRotaryLimit() {} - - float _min; - float _max; -}; - -/** Works like a socket wrench. */ -class AX_DLL PhysicsJointRatchet : public PhysicsJoint -{ -public: - /** Create a ratchet joint. - - @param a A is the body to connect. - @param b B is the body to connect. - @param phase Phase is the initial offset to use when deciding where the ratchet angles are. - @param ratchet Ratchet is the distance between "clicks". - @return A object pointer. - */ - static PhysicsJointRatchet* construct(PhysicsBody* a, PhysicsBody* b, float phase, float ratchet); - - /** Get the ratchet angle.*/ - float getAngle() const; - - /** Set the ratchet angle.*/ - void setAngle(float angle); - - /** Get the initial offset.*/ - float getPhase() const; - - /** Set the initial offset.*/ - void setPhase(float phase); - - /** Get the distance between "clicks".*/ - float getRatchet() const; - - /** Set the distance between "clicks".*/ - void setRatchet(float ratchet); - virtual bool createConstraints() override; - -protected: - PhysicsJointRatchet() {} - virtual ~PhysicsJointRatchet() {} - - float _phase; - float _ratchet; -}; - -/** Keeps the angular velocity ratio of a pair of bodies constant. */ -class AX_DLL PhysicsJointGear : public PhysicsJoint -{ -public: - /** Create a gear joint. - - @param a A is the body to connect. - @param b B is the body to connect. - @param phase Phase is the initial angular offset of the two bodies. - @param ratio Ratio is always measured in absolute terms. - @return A object pointer. - */ - static PhysicsJointGear* construct(PhysicsBody* a, PhysicsBody* b, float phase, float ratio); - - /** Get the angular offset of the two bodies.*/ - float getPhase() const; - - /** Set the angular offset of the two bodies.*/ - void setPhase(float phase); - - /** Get the ratio.*/ - float getRatio() const; - - /** Set the ratio.*/ - void setRatio(float ratchet); - - virtual bool createConstraints() override; - -protected: - PhysicsJointGear() {} - virtual ~PhysicsJointGear() {} - - float _phase; - float _ratio; -}; - -/** Keeps the relative angular velocity of a pair of bodies constant. */ -class AX_DLL PhysicsJointMotor : public PhysicsJoint -{ -public: - /** Create a motor joint. - - @param a A is the body to connect. - @param b B is the body to connect. - @param rate Rate is the desired relative angular velocity. - @return A object pointer. - */ - static PhysicsJointMotor* construct(PhysicsBody* a, PhysicsBody* b, float rate); - - /** Get the relative angular velocity.*/ - float getRate() const; - - /** Set the relative angular velocity.*/ - void setRate(float rate); - virtual bool createConstraints() override; - -protected: - PhysicsJointMotor() {} - virtual ~PhysicsJointMotor() {} - - float _rate; -}; - -/** @} */ -/** @} */ - -} - -#endif // defined(AX_ENABLE_PHYSICS) -#endif // __CCPHYSICS_JOINT_H__ diff --git a/core/physics/PhysicsShape.cpp b/core/physics/PhysicsShape.cpp deleted file mode 100644 index 6abaf180e61b..000000000000 --- a/core/physics/PhysicsShape.cpp +++ /dev/null @@ -1,980 +0,0 @@ -/**************************************************************************** - Copyright (c) 2013-2016 Chukong Technologies Inc. - Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - Copyright (c) 2019-present Axmol Engine contributors (see AUTHORS.md). - - https://axmol.dev/ - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - ****************************************************************************/ - -#include "physics/PhysicsShape.h" -#if defined(AX_ENABLE_PHYSICS) - -# include -# include -# include - -# include "chipmunk/chipmunk.h" -# include "chipmunk/chipmunk_unsafe.h" - -# include "physics/PhysicsBody.h" -# include "physics/PhysicsWorld.h" -# include "physics/PhysicsHelper.h" - -namespace ax -{ -extern const float PHYSICS_INFINITY; -static cpBody* s_sharedBody = nullptr; - -PhysicsShape::PhysicsShape() - : _body(nullptr) - , _type(Type::UNKNOWN) - , _area(0.0f) - , _mass(0.0f) - , _moment(0.0f) - , _sensor(false) - , _scaleX(1.0f) - , _scaleY(1.0f) - , _newScaleX(1.0f) - , _newScaleY(1.0f) - , _tag(0) - , _categoryBitmask(UINT_MAX) - , _collisionBitmask(UINT_MAX) - , _contactTestBitmask(0) - , _group(0) -{ - if (s_sharedBody == nullptr) - { - s_sharedBody = cpBodyNewStatic(); - } -} - -PhysicsShape::~PhysicsShape() -{ - for (auto&& shape : _cpShapes) - { - cpShapeFree(shape); - } -} - -void PhysicsShape::setMass(float mass) -{ - if (mass < 0) - { - return; - } - - if (_body) - { - _body->addMass(-_mass); - _body->addMass(mass); - }; - - _mass = mass; -} - -void PhysicsShape::setMoment(float moment) -{ - if (moment < 0) - { - return; - } - - if (_body) - { - _body->addMoment(-_moment); - _body->addMoment(moment); - }; - - _moment = moment; -} - -void PhysicsShape::setMaterial(const PhysicsMaterial& material) -{ - setDensity(material.density); - setRestitution(material.restitution); - setFriction(material.friction); -} - -void PhysicsShape::setScale(float scaleX, float scaleY) -{ - if (std::abs(_scaleX - scaleX) > FLT_EPSILON || std::abs(_scaleY - scaleY) > FLT_EPSILON) - { - if (_type == Type::CIRCLE && scaleX != scaleY) - { - AXLOGD("PhysicsShapeCircle WARNING: CANNOT support setScale with different x and y"); - return; - } - _newScaleX = scaleX; - _newScaleY = scaleY; - - updateScale(); - - // re-calculate area and mass - _area = calculateArea(); - _mass = _material.density * _area; - _moment = calculateDefaultMoment(); - } -} - -void PhysicsShape::updateScale() -{ - _scaleX = _newScaleX; - _scaleY = _newScaleY; -} - -void PhysicsShape::addShape(cpShape* shape) -{ - if (shape) - { - cpShapeSetUserData(shape, this); - cpShapeSetFilter(shape, cpShapeFilterNew(_group, CP_ALL_CATEGORIES, CP_ALL_CATEGORIES)); - _cpShapes.emplace_back(shape); - } -} - -PhysicsShapeCircle::PhysicsShapeCircle() {} - -PhysicsShapeCircle::~PhysicsShapeCircle() {} - -PhysicsShapeBox::PhysicsShapeBox() {} - -PhysicsShapeBox::~PhysicsShapeBox() {} - -PhysicsShapePolygon::PhysicsShapePolygon() {} - -PhysicsShapePolygon::~PhysicsShapePolygon() {} - -PhysicsShapeEdgeBox::PhysicsShapeEdgeBox() {} - -PhysicsShapeEdgeBox::~PhysicsShapeEdgeBox() {} - -PhysicsShapeEdgeChain::PhysicsShapeEdgeChain() {} - -PhysicsShapeEdgeChain::~PhysicsShapeEdgeChain() {} - -PhysicsShapeEdgePolygon::PhysicsShapeEdgePolygon() {} - -PhysicsShapeEdgePolygon::~PhysicsShapeEdgePolygon() {} - -PhysicsShapeEdgeSegment::PhysicsShapeEdgeSegment() {} - -PhysicsShapeEdgeSegment::~PhysicsShapeEdgeSegment() {} - -void PhysicsShape::setDensity(float density) -{ - if (density < 0) - { - return; - } - - _material.density = density; - - if (_material.density == PHYSICS_INFINITY) - { - setMass(PHYSICS_INFINITY); - } - else if (_area > 0) - { - setMass(_material.density * _area); - } -} - -void PhysicsShape::setRestitution(float restitution) -{ - _material.restitution = restitution; - - for (cpShape* shape : _cpShapes) - { - cpShapeSetElasticity(shape, restitution); - } -} - -void PhysicsShape::setFriction(float friction) -{ - _material.friction = friction; - - for (cpShape* shape : _cpShapes) - { - cpShapeSetFriction(shape, friction); - } -} - -void PhysicsShape::setSensor(bool sensor) -{ - if (sensor != _sensor) - { - for (cpShape* shape : _cpShapes) - { - cpShapeSetSensor(shape, sensor); - } - _sensor = sensor; - } -} - -void PhysicsShape::recenterPoints(Vec2* points, int count, const Vec2& center) -{ - cpVect* cpvs = new cpVect[count]; - cpVect centroid = cpCentroidForPoly(count, cpvs); - for (int i = 0; i < count; i++) - { - cpvs[i] = cpvsub(cpvs[i], centroid); - } - PhysicsHelper::cpvs2points(cpvs, points, count); - delete[] cpvs; - - if (center != Vec2::ZERO) - { - for (int i = 0; i < count; ++i) - { - points[i] += center; - } - } -} - -Vec2 PhysicsShape::getPolygonCenter(const Vec2* points, int count) -{ - cpVect* cpvs = new cpVect[count]; - cpVect center = cpCentroidForPoly(count, PhysicsHelper::points2cpvs(points, cpvs, count)); - delete[] cpvs; - - return PhysicsHelper::cpv2vec2(center); -} - -void PhysicsShape::setBody(PhysicsBody* body) -{ - // already added - if (body && _body == body) - { - return; - } - - if (_body) - { - _body->removeShape(this); - } - - for (auto&& shape : _cpShapes) - { - cpShapeSetBody(shape, body == nullptr ? s_sharedBody : body->_cpBody); - } - _body = body; -} - -// PhysicsShapeCircle -PhysicsShapeCircle* PhysicsShapeCircle::create(float radius, - const PhysicsMaterial& material /* = MaterialDefault*/, - const Vec2& offset /* = Vec2(0, 0)*/) -{ - PhysicsShapeCircle* shape = new PhysicsShapeCircle(); - if (shape->init(radius, material, offset)) - { - shape->autorelease(); - return shape; - } - - AX_SAFE_DELETE(shape); - return nullptr; -} - -bool PhysicsShapeCircle::init(float radius, - const PhysicsMaterial& material /* = MaterialDefault*/, - const Vec2& offset /*= Vec2(0, 0)*/) -{ - do - { - _type = Type::CIRCLE; - - auto shape = cpCircleShapeNew(s_sharedBody, radius, PhysicsHelper::vec22cpv(offset)); - AX_BREAK_IF(shape == nullptr); - cpShapeSetUserData(shape, this); - - addShape(shape); - - _area = calculateArea(); - _mass = material.density == PHYSICS_INFINITY ? PHYSICS_INFINITY : material.density * _area; - _moment = calculateDefaultMoment(); - - setMaterial(material); - return true; - } while (false); - - return false; -} - -float PhysicsShapeCircle::calculateArea(float radius) -{ - return PhysicsHelper::cpfloat2float(cpAreaForCircle(0, radius)); -} - -float PhysicsShapeCircle::calculateMoment(float mass, float radius, const Vec2& offset) -{ - return mass == PHYSICS_INFINITY - ? PHYSICS_INFINITY - : PhysicsHelper::cpfloat2float(cpMomentForCircle(mass, 0, radius, PhysicsHelper::vec22cpv(offset))); -} - -float PhysicsShapeCircle::calculateArea() -{ - return PhysicsHelper::cpfloat2float(cpAreaForCircle(0, cpCircleShapeGetRadius(_cpShapes.front()))); -} - -float PhysicsShapeCircle::calculateDefaultMoment() -{ - auto shape = _cpShapes.front(); - - return _mass == PHYSICS_INFINITY ? PHYSICS_INFINITY - : PhysicsHelper::cpfloat2float(cpMomentForCircle( - _mass, 0, cpCircleShapeGetRadius(shape), cpCircleShapeGetOffset(shape))); -} - -float PhysicsShapeCircle::getRadius() const -{ - return PhysicsHelper::cpfloat2float(cpCircleShapeGetRadius(_cpShapes.front())); -} - -Vec2 PhysicsShapeCircle::getOffset() -{ - return PhysicsHelper::cpv2vec2(cpCircleShapeGetOffset(_cpShapes.front())); -} - -void PhysicsShapeCircle::updateScale() -{ - cpFloat factor = std::abs(_newScaleX / _scaleX); - - cpShape* shape = _cpShapes.front(); - cpVect v = cpCircleShapeGetOffset(shape); - v = cpvmult(v, factor); - cpCircleShapeSetOffset(shape, v); - - cpCircleShapeSetRadius(shape, cpCircleShapeGetRadius(shape) * factor); - - PhysicsShape::updateScale(); -} - -// PhysicsShapeEdgeSegment -PhysicsShapeEdgeSegment* PhysicsShapeEdgeSegment::create(const Vec2& a, - const Vec2& b, - const PhysicsMaterial& material /* = MaterialDefault*/, - float border /* = 1*/) -{ - PhysicsShapeEdgeSegment* shape = new PhysicsShapeEdgeSegment(); - if (shape->init(a, b, material, border)) - { - shape->autorelease(); - return shape; - } - - AX_SAFE_DELETE(shape); - return nullptr; -} - -bool PhysicsShapeEdgeSegment::init(const Vec2& a, - const Vec2& b, - const PhysicsMaterial& material /* = MaterialDefault*/, - float border /* = 1*/) -{ - do - { - _type = Type::EDGESEGMENT; - - auto shape = cpSegmentShapeNew(s_sharedBody, PhysicsHelper::vec22cpv(a), PhysicsHelper::vec22cpv(b), border); - AX_BREAK_IF(shape == nullptr); - cpShapeSetUserData(shape, this); - - addShape(shape); - - _mass = PHYSICS_INFINITY; - _moment = PHYSICS_INFINITY; - - setMaterial(material); - - return true; - } while (false); - - return false; -} - -Vec2 PhysicsShapeEdgeSegment::getPointA() const -{ - return PhysicsHelper::cpv2vec2(cpSegmentShapeGetA(_cpShapes.front())); -} - -Vec2 PhysicsShapeEdgeSegment::getPointB() const -{ - return PhysicsHelper::cpv2vec2(cpSegmentShapeGetB(_cpShapes.front())); -} - -Vec2 PhysicsShapeEdgeSegment::getCenter() -{ - auto a = PhysicsHelper::cpv2vec2(cpSegmentShapeGetA(_cpShapes.front())); - auto b = PhysicsHelper::cpv2vec2(cpSegmentShapeGetB(_cpShapes.front())); - return (a + b) / 2; -} - -void PhysicsShapeEdgeSegment::updateScale() -{ - cpFloat factorX = _newScaleX / _scaleX; - cpFloat factorY = _newScaleY / _scaleY; - - cpShape* shape = _cpShapes.front(); - cpVect a = cpSegmentShapeGetA(shape); - a.x *= factorX; - a.y *= factorY; - cpVect b = cpSegmentShapeGetB(shape); - b.x *= factorX; - b.y *= factorY; - cpSegmentShapeSetEndpoints(shape, a, b); - - PhysicsShape::updateScale(); -} - -// PhysicsShapeBox -PhysicsShapeBox* PhysicsShapeBox::create(const Vec2& size, - const PhysicsMaterial& material /* = MaterialDefault*/, - const Vec2& offset /* = Vec2(0, 0)*/, - float radius /* = 0.0f*/) -{ - PhysicsShapeBox* shape = new PhysicsShapeBox(); - if (shape->init(size, material, offset, radius)) - { - shape->autorelease(); - return shape; - } - - AX_SAFE_DELETE(shape); - return nullptr; -} - -bool PhysicsShapeBox::init(const Vec2& size, - const PhysicsMaterial& material /* = MaterialDefault*/, - const Vec2& offset /*= Vec2(0, 0)*/, - float radius /* = 0.0f*/) -{ - do - { - _type = Type::BOX; - - auto wh = PhysicsHelper::vec22cpv(size); - cpVect vec[4] = {{-wh.x / 2.0f, -wh.y / 2.0f}, - {-wh.x / 2.0f, wh.y / 2.0f}, - {wh.x / 2.0f, wh.y / 2.0f}, - {wh.x / 2.0f, -wh.y / 2.0f}}; - - cpTransform transform = cpTransformTranslate(PhysicsHelper::vec22cpv(offset)); - - auto shape = cpPolyShapeNew(s_sharedBody, 4, vec, transform, radius); - AX_BREAK_IF(shape == nullptr); - cpShapeSetUserData(shape, this); - - addShape(shape); - - _area = calculateArea(); - _mass = material.density == PHYSICS_INFINITY ? PHYSICS_INFINITY : material.density * _area; - _moment = calculateDefaultMoment(); - - setMaterial(material); - - return true; - } while (false); - - return false; -} - -Vec2 PhysicsShapeBox::getSize() const -{ - cpShape* shape = _cpShapes.front(); - return PhysicsHelper::cpv2vec2(cpv(cpvdist(cpPolyShapeGetVert(shape, 1), cpPolyShapeGetVert(shape, 2)), - cpvdist(cpPolyShapeGetVert(shape, 0), cpPolyShapeGetVert(shape, 1)))); -} - -// PhysicsShapePolygon -PhysicsShapePolygon* PhysicsShapePolygon::create(const Vec2* points, - int count, - const PhysicsMaterial& material /* = MaterialDefault*/, - const Vec2& offset /* = Vec2(0, 0)*/, - float radius /* = 0.0f*/) -{ - PhysicsShapePolygon* shape = new PhysicsShapePolygon(); - if (shape->init(points, count, material, offset, radius)) - { - shape->autorelease(); - return shape; - } - - AX_SAFE_DELETE(shape); - return nullptr; -} - -bool PhysicsShapePolygon::init(const Vec2* points, - int count, - const PhysicsMaterial& material /* = MaterialDefault*/, - const Vec2& offset /* = Vec2(0, 0)*/, - float radius /* = 0.0f*/) -{ - do - { - _type = Type::POLYGON; - - auto vecs = new cpVect[count]; - PhysicsHelper::points2cpvs(points, vecs, count); // count = cpConvexHull((int)count, vecs, nullptr, nullptr, - // 0); - cpTransform transform = cpTransformTranslate(PhysicsHelper::vec22cpv(offset)); - auto shape = cpPolyShapeNew(s_sharedBody, count, vecs, transform, radius); - AX_SAFE_DELETE_ARRAY(vecs); - - AX_BREAK_IF(shape == nullptr); - cpShapeSetUserData(shape, this); - - addShape(shape); - - _area = calculateArea(); - _mass = material.density == PHYSICS_INFINITY ? PHYSICS_INFINITY : material.density * _area; - _moment = calculateDefaultMoment(); - - setMaterial(material); - - return true; - } while (false); - - return false; -} - -float PhysicsShapePolygon::calculateArea(const Vec2* points, int count) -{ - cpVect* vecs = new cpVect[count]; - PhysicsHelper::points2cpvs(points, vecs, count); - float area = PhysicsHelper::cpfloat2float(cpAreaForPoly(count, vecs, 0.0f)); - AX_SAFE_DELETE_ARRAY(vecs); - - return area; -} - -float PhysicsShapePolygon::calculateMoment(float mass, const Vec2* points, int count, const Vec2& offset, float radius) -{ - cpVect* vecs = new cpVect[count]; - PhysicsHelper::points2cpvs(points, vecs, count); - float moment = - mass == PHYSICS_INFINITY - ? PHYSICS_INFINITY - : PhysicsHelper::cpfloat2float(cpMomentForPoly(mass, count, vecs, PhysicsHelper::vec22cpv(offset), radius)); - AX_SAFE_DELETE_ARRAY(vecs); - - return moment; -} - -float PhysicsShapePolygon::calculateArea() -{ - auto shape = _cpShapes.front(); - int count = cpPolyShapeGetCount(shape); - cpVect* vecs = new cpVect[count]; - for (int i = 0; i < count; ++i) - vecs[i] = cpPolyShapeGetVert(shape, i); - float area = PhysicsHelper::cpfloat2float(cpAreaForPoly(count, vecs, cpPolyShapeGetRadius(shape))); - AX_SAFE_DELETE_ARRAY(vecs); - return area; -} - -float PhysicsShapePolygon::calculateDefaultMoment() -{ - if (_mass == PHYSICS_INFINITY) - { - return PHYSICS_INFINITY; - } - else - { - auto shape = _cpShapes.front(); - int count = cpPolyShapeGetCount(shape); - cpVect* vecs = new cpVect[count]; - for (int i = 0; i < count; ++i) - vecs[i] = cpPolyShapeGetVert(shape, i); - float moment = - PhysicsHelper::cpfloat2float(cpMomentForPoly(_mass, count, vecs, cpvzero, cpPolyShapeGetRadius(shape))); - AX_SAFE_DELETE_ARRAY(vecs); - return moment; - } -} - -Vec2 PhysicsShapePolygon::getPoint(int i) const -{ - return PhysicsHelper::cpv2vec2(cpPolyShapeGetVert(_cpShapes.front(), i)); -} - -void PhysicsShapePolygon::getPoints(Vec2* outPoints) const -{ - auto shape = _cpShapes.front(); - int count = cpPolyShapeGetCount(shape); - cpVect* vecs = new cpVect[count]; - for (int i = 0; i < count; ++i) - vecs[i] = cpPolyShapeGetVert(shape, i); - PhysicsHelper::cpvs2points(vecs, outPoints, count); - AX_SAFE_DELETE_ARRAY(vecs); -} - -int PhysicsShapePolygon::getPointsCount() const -{ - return cpPolyShapeGetCount(_cpShapes.front()); -} - -Vec2 PhysicsShapePolygon::getCenter() -{ - auto shape = _cpShapes.front(); - int count = cpPolyShapeGetCount(shape); - cpVect* vecs = new cpVect[count]; - for (int i = 0; i < count; ++i) - vecs[i] = cpPolyShapeGetVert(shape, i); - - Vec2 center = PhysicsHelper::cpv2vec2(cpCentroidForPoly(count, vecs)); - AX_SAFE_DELETE_ARRAY(vecs); - - return center; -} - -void PhysicsShapePolygon::updateScale() -{ - cpFloat factorX = _newScaleX / _scaleX; - cpFloat factorY = _newScaleY / _scaleY; - - auto shape = _cpShapes.front(); - int count = cpPolyShapeGetCount(shape); - cpVect* vects = new cpVect[count]; - for (int i = 0; i < count; ++i) - vects[i] = cpPolyShapeGetVert(shape, i); - - for (int i = 0; i < count; ++i) - { - vects[i].x *= factorX; - vects[i].y *= factorY; - } - - // convert hole to clockwise - if (factorX * factorY < 0) - { - for (int i = 0; i < count / 2; ++i) - { - cpVect v = vects[i]; - vects[i] = vects[count - i - 1]; - vects[count - i - 1] = v; - } - } - - cpPolyShapeSetVertsRaw(shape, count, vects); - AX_SAFE_DELETE_ARRAY(vects); - - PhysicsShape::updateScale(); -} - -// PhysicsShapeEdgeBox -PhysicsShapeEdgeBox* PhysicsShapeEdgeBox::create(const Vec2& size, - const PhysicsMaterial& material /* = MaterialDefault*/, - float border /* = 1*/, - const Vec2& offset /* = Vec2(0, 0)*/) -{ - PhysicsShapeEdgeBox* shape = new PhysicsShapeEdgeBox(); - if (shape->init(size, material, border, offset)) - { - shape->autorelease(); - return shape; - } - - AX_SAFE_DELETE(shape); - return nullptr; -} - -bool PhysicsShapeEdgeBox::init(const Vec2& size, - const PhysicsMaterial& material /* = MaterialDefault*/, - float border /* = 1*/, - const Vec2& offset /*= Vec2(0, 0)*/) -{ - do - { - _type = Type::EDGEBOX; - - cpVect vec[4] = {}; - vec[0] = PhysicsHelper::vec22cpv(Vec2(-size.width / 2 + offset.x, -size.height / 2 + offset.y)); - vec[1] = PhysicsHelper::vec22cpv(Vec2(+size.width / 2 + offset.x, -size.height / 2 + offset.y)); - vec[2] = PhysicsHelper::vec22cpv(Vec2(+size.width / 2 + offset.x, +size.height / 2 + offset.y)); - vec[3] = PhysicsHelper::vec22cpv(Vec2(-size.width / 2 + offset.x, +size.height / 2 + offset.y)); - - int i = 0; - for (; i < 4; ++i) - { - auto shape = cpSegmentShapeNew(s_sharedBody, vec[i], vec[(i + 1) % 4], border); - AX_BREAK_IF(shape == nullptr); - cpShapeSetUserData(shape, this); - addShape(shape); - } - AX_BREAK_IF(i < 4); - - _mass = PHYSICS_INFINITY; - _moment = PHYSICS_INFINITY; - - setMaterial(material); - - return true; - } while (false); - - return false; -} - -// PhysicsShapeEdgeBox -PhysicsShapeEdgePolygon* PhysicsShapeEdgePolygon::create(const Vec2* points, - int count, - const PhysicsMaterial& material /* = MaterialDefault*/, - float border /* = 1*/) -{ - PhysicsShapeEdgePolygon* shape = new PhysicsShapeEdgePolygon(); - if (shape->init(points, count, material, border)) - { - shape->autorelease(); - return shape; - } - - AX_SAFE_DELETE(shape); - return nullptr; -} - -bool PhysicsShapeEdgePolygon::init(const Vec2* points, - int count, - const PhysicsMaterial& material /* = MaterialDefault*/, - float border /* = 1*/) -{ - cpVect* vec = nullptr; - do - { - _type = Type::EDGEPOLYGON; - - vec = new cpVect[count]; - PhysicsHelper::points2cpvs(points, vec, count); - - int i = 0; - for (; i < count; ++i) - { - auto shape = cpSegmentShapeNew(s_sharedBody, vec[i], vec[(i + 1) % count], border); - AX_BREAK_IF(shape == nullptr); - cpShapeSetUserData(shape, this); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - addShape(shape); - } - AX_SAFE_DELETE_ARRAY(vec); - - AX_BREAK_IF(i < count); - - _mass = PHYSICS_INFINITY; - _moment = PHYSICS_INFINITY; - - setMaterial(material); - - return true; - } while (false); - - AX_SAFE_DELETE_ARRAY(vec); - - return false; -} - -Vec2 PhysicsShapeEdgePolygon::getCenter() -{ - int count = (int)_cpShapes.size(); - cpVect* points = new cpVect[count]; - int i = 0; - for (auto&& shape : _cpShapes) - { - points[i++] = cpSegmentShapeGetA(shape); - } - - Vec2 center = PhysicsHelper::cpv2vec2(cpCentroidForPoly(count, points)); - delete[] points; - - return center; -} - -void PhysicsShapeEdgePolygon::getPoints(ax::Vec2* outPoints) const -{ - int i = 0; - for (auto&& shape : _cpShapes) - { - outPoints[i++] = PhysicsHelper::cpv2vec2(cpSegmentShapeGetA(shape)); - } -} - -int PhysicsShapeEdgePolygon::getPointsCount() const -{ - return static_cast(_cpShapes.size()); -} - -// PhysicsShapeEdgeChain -PhysicsShapeEdgeChain* PhysicsShapeEdgeChain::create(const Vec2* points, - int count, - const PhysicsMaterial& material /* = MaterialDefault*/, - float border /* = 1*/) -{ - PhysicsShapeEdgeChain* shape = new PhysicsShapeEdgeChain(); - if (shape->init(points, count, material, border)) - { - shape->autorelease(); - return shape; - } - - AX_SAFE_DELETE(shape); - return nullptr; -} - -void PhysicsShapeEdgePolygon::updateScale() -{ - cpFloat factorX = _newScaleX / _scaleX; - cpFloat factorY = _newScaleY / _scaleY; - - for (auto&& shape : _cpShapes) - { - cpVect a = cpSegmentShapeGetA(shape); - a.x *= factorX; - a.y *= factorY; - cpVect b = cpSegmentShapeGetB(shape); - b.x *= factorX; - b.y *= factorY; - cpSegmentShapeSetEndpoints(shape, a, b); - } - - PhysicsShape::updateScale(); -} - -bool PhysicsShapeEdgeChain::init(const Vec2* points, - int count, - const PhysicsMaterial& material /* = MaterialDefault*/, - float border /* = 1*/) -{ - cpVect* vec = nullptr; - do - { - _type = Type::EDGECHAIN; - - vec = new cpVect[count]; - PhysicsHelper::points2cpvs(points, vec, count); - - int i = 0; - for (; i < count - 1; ++i) - { - auto shape = cpSegmentShapeNew(s_sharedBody, vec[i], vec[i + 1], border); - AX_BREAK_IF(shape == nullptr); - cpShapeSetUserData(shape, this); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - addShape(shape); - } - AX_SAFE_DELETE_ARRAY(vec); - AX_BREAK_IF(i < count - 1); - - _mass = PHYSICS_INFINITY; - _moment = PHYSICS_INFINITY; - - setMaterial(material); - - return true; - } while (false); - - AX_SAFE_DELETE_ARRAY(vec); - - return false; -} - -Vec2 PhysicsShapeEdgeChain::getCenter() -{ - int count = (int)_cpShapes.size() + 1; - cpVect* points = new cpVect[count]; - int i = 0; - for (auto&& shape : _cpShapes) - { - points[i++] = cpSegmentShapeGetA(shape); - } - - points[i++] = cpSegmentShapeGetB(_cpShapes.back()); - - Vec2 center = PhysicsHelper::cpv2vec2(cpCentroidForPoly(count, points)); - delete[] points; - - return center; -} - -void PhysicsShapeEdgeChain::getPoints(Vec2* outPoints) const -{ - int i = 0; - for (auto&& shape : _cpShapes) - { - outPoints[i++] = PhysicsHelper::cpv2vec2(cpSegmentShapeGetA(shape)); - } - - outPoints[i++] = PhysicsHelper::cpv2vec2(cpSegmentShapeGetB(_cpShapes.back())); -} - -int PhysicsShapeEdgeChain::getPointsCount() const -{ - return static_cast(_cpShapes.size() + 1); -} - -void PhysicsShapeEdgeChain::updateScale() -{ - cpFloat factorX = _newScaleX / _scaleX; - cpFloat factorY = _newScaleY / _scaleY; - - for (auto&& shape : _cpShapes) - { - cpVect a = cpSegmentShapeGetA(shape); - a.x *= factorX; - a.y *= factorY; - cpVect b = cpSegmentShapeGetB(shape); - b.x *= factorX; - b.y *= factorY; - cpSegmentShapeSetEndpoints(shape, a, b); - } - - PhysicsShape::updateScale(); -} - -void PhysicsShape::setGroup(int group) -{ - if (group < 0) - { - for (auto&& shape : _cpShapes) - { - cpShapeSetFilter(shape, cpShapeFilterNew(group, CP_ALL_CATEGORIES, CP_ALL_CATEGORIES)); - } - } - - _group = group; -} - -bool PhysicsShape::containsPoint(const Vec2& point) const -{ - for (auto&& shape : _cpShapes) - { - if (cpShapePointQuery(shape, PhysicsHelper::vec22cpv(point), nullptr) < 0) - { - return true; - } - } - - return false; -} - -} - -#endif // defined(AX_ENABLE_PHYSICS) diff --git a/core/physics/PhysicsShape.h b/core/physics/PhysicsShape.h deleted file mode 100644 index e37d2cb116fc..000000000000 --- a/core/physics/PhysicsShape.h +++ /dev/null @@ -1,791 +0,0 @@ -/**************************************************************************** - Copyright (c) 2013-2016 Chukong Technologies Inc. - Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - Copyright (c) 2019-present Axmol Engine contributors (see AUTHORS.md). - - https://axmol.dev/ - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - ****************************************************************************/ - -#ifndef __CCPHYSICS_SHAPE_H__ -#define __CCPHYSICS_SHAPE_H__ - -#include "base/Config.h" -#if defined(AX_ENABLE_PHYSICS) - -# include "base/Object.h" -# include "math/Math.h" - -struct cpShape; - -namespace ax -{ - -class PhysicsBody; - -typedef struct AX_DLL PhysicsMaterial -{ - float density; ///< The density of the object. - float restitution; ///< The bounciness of the physics body. - float friction; ///< The roughness of the surface of a shape. - - PhysicsMaterial() : density(0.0f), restitution(0.0f), friction(0.0f) {} - - PhysicsMaterial(float aDensity, float aRestitution, float aFriction) - : density(aDensity), restitution(aRestitution), friction(aFriction) - {} -} PhysicsMaterial; - -const PhysicsMaterial PHYSICSSHAPE_MATERIAL_DEFAULT; - -/** - * @addtogroup physics - * @{ - * @addtogroup physics_2d - * @{ - - */ - -/** - * @brief A shape for body. You do not create PhysicsWorld objects directly, instead, you can view PhysicsBody to see - * how to create it. - */ -class AX_DLL PhysicsShape : public Object -{ -public: - enum class Type - { - UNKNOWN, - CIRCLE, - BOX, - POLYGON, - EDGESEGMENT, - EDGEBOX, - EDGEPOLYGON, - EDGECHAIN, - - /** @deprecated Use Type::POLYGON instead. */ - POLYGEN = POLYGON, - - /** @deprecated Use Type::EDGEPOLYGON instead. */ - EDGEPOLYGEN = EDGEPOLYGON, - }; - -public: - /** - * Get the body that this shape attaches. - * - * @return A PhysicsBody object pointer. - */ - PhysicsBody* getBody() const { return _body; } - - /** - * Return this shape's type. - * - * @return A Type object. - */ - Type getType() const { return _type; } - - /** - * Return this shape's area. - * - * @return A float number. - */ - float getArea() const { return _area; } - - /** - * Get this shape's moment. - * - * @return A float number. - */ - float getMoment() const { return _moment; } - - /** - * Set this shape's moment. - * - * It will change the body's moment this shape attaches. - * - * @param moment A float number. - */ - void setMoment(float moment); - - /** - * Set this shape's tag. - * - * @param tag An integer number that identifies a shape object. - */ - void setTag(int tag) { _tag = tag; } - - /** - * Get this shape's tag. - * - * @return An integer number. - */ - int getTag() const { return _tag; } - - /** - * Get the mass of this shape. - * - * @return A float number. - */ - float getMass() const { return _mass; } - - /** - * Set this shape's mass. - * - * It will change the body's mass this shape attaches. - * - * @param mass A float number. - */ - void setMass(float mass); - - /** - * Get this shape's density. - * - * @return A float number. - */ - float getDensity() const { return _material.density; } - - /** - * Set this shape's density. - * - * It will change the body's mass this shape attaches. - * - * @param density A float number. - */ - void setDensity(float density); - - /** - * Get this shape's restitution. - * - * @return A float number. - */ - float getRestitution() const { return _material.restitution; } - - /** - * Set this shape's restitution. - * - * It will change the shape's elasticity. - * - * @param restitution A float number. - */ - void setRestitution(float restitution); - - /** - * Get this shape's friction. - * - * @return A float number. - */ - float getFriction() const { return _material.friction; } - - /** - * Set this shape's friction. - * - * It will change the shape's friction. - * - * @param friction A float number. - */ - void setFriction(float friction); - - /** - * Get this shape's PhysicsMaterial object. - * - * @return A PhysicsMaterial object reference. - */ - const PhysicsMaterial& getMaterial() const { return _material; } - - /** - * Set this shape's material. - * - * It will change the shape's mass, elasticity and friction. - * - * @param material A PhysicsMaterial object. - */ - void setMaterial(const PhysicsMaterial& material); - bool isSensor() const { return _sensor; } - void setSensor(bool sensor); - - /** - * Calculate the default moment value. - * - * This function should be overridden in inherit classes. - * @return A float number, equals 0.0. - */ - virtual float calculateDefaultMoment() { return 0.0f; } - - /** - * Get this shape's position offset. - * - * This function should be overridden in inherit classes. - * @return A Vec2 object. - */ - virtual Vec2 getOffset() { return Vec2::ZERO; } - - /** - * Get this shape's center position. - * - * This function should be overridden in inherit classes. - * @return A Vec2 object. - */ - virtual Vec2 getCenter() { return getOffset(); } - - /** - * Test point is inside this shape or not. - * - * @param point A Vec2 object. - * @return A bool object. - */ - bool containsPoint(const Vec2& point) const; - - /** - * Move the points to the center. - * - * @param points A Vec2 object pointer. - * @param count An integer number. - * @param center A Vec2 object, default value is Vec2(0,0). - */ - static void recenterPoints(Vec2* points, int count, const Vec2& center = Vec2::ZERO); - - /** - * Get center of the polygon points. - * - * @param points A Vec2 object pointer. - * @param count An integer number. - * @return A Vec2 object. - */ - static Vec2 getPolygonCenter(const Vec2* points, int count); - - /** - * Set a mask that defines which categories this physics body belongs to. - * - * Every physics body in a scene can be assigned to up to 32 different categories, each corresponding to a bit in - * the bit mask. You define the mask values used in your game. In conjunction with the collisionBitMask and - * contactTestBitMask properties, you define which physics bodies interact with each other and when your game is - * notified of these interactions. - * @param bitmask An integer number, the default value is 0xFFFFFFFF (all bits set). - */ - void setCategoryBitmask(int bitmask) { _categoryBitmask = bitmask; } - - /** - * Get a mask that defines which categories this physics body belongs to. - * - * @return An integer number. - */ - int getCategoryBitmask() const { return _categoryBitmask; } - - /** - * A mask that defines which categories of bodies cause intersection notifications with this physics body. - * - * When two bodies share the same space, each body's category mask is tested against the other body's contact mask - * by performing a logical AND operation. If either comparison results in a non-zero value, an PhysicsContact object - * is created and passed to the physics world’s delegate. For best performance, only set bits in the contacts mask - * for interactions you are interested in. - * @param bitmask An integer number, the default value is 0x00000000 (all bits cleared). - */ - void setContactTestBitmask(int bitmask) { _contactTestBitmask = bitmask; } - - /** - * Get a mask that defines which categories of bodies cause intersection notifications with this physics body. - * - * @return An integer number. - */ - int getContactTestBitmask() const { return _contactTestBitmask; } - - /** - * A mask that defines which categories of physics bodies can collide with this physics body. - * - * When two physics bodies contact each other, a collision may occur. This body's collision mask is compared to the - * other body's category mask by performing a logical AND operation. If the result is a non-zero value, then this - * body is affected by the collision. Each body independently chooses whether it wants to be affected by the other - * body. For example, you might use this to avoid collision calculations that would make negligible changes to a - * body's velocity. - * @param bitmask An integer number, the default value is 0xFFFFFFFF (all bits set). - */ - void setCollisionBitmask(int bitmask) { _collisionBitmask = bitmask; } - - /** - * Get a mask that defines which categories of physics bodies can collide with this physics body. - * - * @return An integer number. - */ - int getCollisionBitmask() const { return _collisionBitmask; } - - /** - * Set the group of body. - * - * Collision groups let you specify an integral group index. You can have all fixtures with the same group index - * always collide (positive index) or never collide (negative index). - * @param group An integer number, it have high priority than bit masks. - */ - void setGroup(int group); - - /** - * Get the group of body. - * - * @return An integer number. - */ - int getGroup() { return _group; } - -protected: - void setBody(PhysicsBody* body); - - /** calculate the area of this shape */ - virtual float calculateArea() { return 0.0f; } - - virtual void setScale(float scaleX, float scaleY); - virtual void updateScale(); - void addShape(cpShape* shape); - -protected: - PhysicsShape(); - virtual ~PhysicsShape() = 0; - -protected: - PhysicsBody* _body; - std::vector _cpShapes; - - Type _type; - float _area; - float _mass; - float _moment; - bool _sensor; - float _scaleX; - float _scaleY; - float _newScaleX; - float _newScaleY; - PhysicsMaterial _material; - int _tag; - int _categoryBitmask; - int _collisionBitmask; - int _contactTestBitmask; - int _group; - - friend class PhysicsWorld; - friend class PhysicsBody; - friend class PhysicsJoint; - friend class PhysicsDebugDraw; -}; - -/** A circle shape. */ -class AX_DLL PhysicsShapeCircle : public PhysicsShape -{ -public: - /** - * Creates a PhysicsShapeCircle with specified value. - * - * @param radius A float number, it is the circle's radius. - * @param material A PhysicsMaterial object, the default value is PHYSICSSHAPE_MATERIAL_DEFAULT. - * @param offset A Vec2 object, it is the offset from the body's center of gravity in body local coordinates. - * @return An autoreleased PhysicsShapeCircle object pointer. - */ - static PhysicsShapeCircle* create(float radius, - const PhysicsMaterial& material = PHYSICSSHAPE_MATERIAL_DEFAULT, - const Vec2& offset = Vec2(0.0f, 0.0f)); - - /** - * Calculate the area of a circle with specified radius. - * - * @param radius A float number - * @return A float number - */ - static float calculateArea(float radius); - - /** - * Calculate the moment of a circle with specified value. - * - * @param mass A float number - * @param radius A float number - * @param offset A Vec2 object, it is the offset from the body's center of gravity in body local coordinates. - * @return A float number - */ - static float calculateMoment(float mass, float radius, const Vec2& offset = Vec2::ZERO); - - /** - * Calculate the moment for a circle. - * - * @return A float number. - */ - virtual float calculateDefaultMoment() override; - - /** - * Get the circle's radius. - * - * @return A float number. - */ - float getRadius() const; - - /** - * Get this circle's position offset. - * - * @return A Vec2 object. - */ - virtual Vec2 getOffset() override; - -protected: - bool init(float radius, - const PhysicsMaterial& material = PHYSICSSHAPE_MATERIAL_DEFAULT, - const Vec2& offset = Vec2::ZERO); - virtual float calculateArea() override; - virtual void updateScale() override; - -protected: - PhysicsShapeCircle(); - virtual ~PhysicsShapeCircle(); -}; - -/** A polygon shape. */ -class AX_DLL PhysicsShapePolygon : public PhysicsShape -{ -public: - /** - * Creates a PhysicsShapePolygon with specified value. - * - * @param points A Vec2 object pointer, it is an array of Vec2. - * @param count An integer number, contains the count of the points array. - * @param material A PhysicsMaterial object, the default value is PHYSICSSHAPE_MATERIAL_DEFAULT. - * @param offset A Vec2 object, it is the offset from the body's center of gravity in body local coordinates. - * @return An autoreleased PhysicsShapePolygon object pointer. - */ - static PhysicsShapePolygon* create(const Vec2* points, - int count, - const PhysicsMaterial& material = PHYSICSSHAPE_MATERIAL_DEFAULT, - const Vec2& offset = Vec2::ZERO, - float radius = 0.0f); - - /** - * Calculate the area of a polygon with specified value. - * - * @param points A Vec2 object pointer, it is an array of Vec2. - * @param count An integer number, contains the count of the points array. - * @return A float number. - */ - static float calculateArea(const Vec2* points, int count); - - /** - * Calculate the moment of a polygon with specified value. - * - * @param mass A float number - * @param points A Vec2 object pointer, it is an array of Vec2. - * @param count An integer number, contains the count of the points array. - * @param offset A Vec2 object, it is the offset from the body's center of gravity in body local coordinates. - * @return A float number - */ - static float calculateMoment(float mass, - const Vec2* points, - int count, - const Vec2& offset = Vec2::ZERO, - float radius = 0.0f); - - /** - * Calculate the moment for a polygon. - * - * @return A float number. - */ - float calculateDefaultMoment() override; - - /** - * Get a point of this polygon's points array. - * - * @param i A index of this polygon's points array. - * @return A point value. - */ - Vec2 getPoint(int i) const; - - /** - * Get this polygon's points array. - * - * @param outPoints A Vec2 array pointer. - */ - void getPoints(Vec2* outPoints) const; - - /** - * Get this polygon's points array count. - * - * @return An integer number. - */ - int getPointsCount() const; - - /** - * Get this polygon's center position. - * - * @return A Vec2 object. - */ - virtual Vec2 getCenter() override; - -protected: - bool init(const Vec2* points, - int count, - const PhysicsMaterial& material = PHYSICSSHAPE_MATERIAL_DEFAULT, - const Vec2& offset = Vec2::ZERO, - float radius = 0.0f); - float calculateArea() override; - virtual void updateScale() override; - -protected: - PhysicsShapePolygon(); - virtual ~PhysicsShapePolygon(); -}; - -/** A box shape. */ -class AX_DLL PhysicsShapeBox : public PhysicsShapePolygon -{ -public: - /** - * Creates a PhysicsShapeBox with specified value. - * - * @param size The size contains this box's width and height. - * @param material A PhysicsMaterial object, the default value is PHYSICSSHAPE_MATERIAL_DEFAULT. - * @param offset A Vec2 object, it is the offset from the body's center of gravity in body local coordinates. - * @return An autoreleased PhysicsShapeBox object pointer. - */ - static PhysicsShapeBox* create(const Vec2& size, - const PhysicsMaterial& material = PHYSICSSHAPE_MATERIAL_DEFAULT, - const Vec2& offset = Vec2::ZERO, - float radius = 0.0f); - - /** - * Get this box's width and height. - * - * @return An Vec2 object. - */ - Vec2 getSize() const; - - /** - * Get this box's position offset. - * - * @return A Vec2 object. - */ - virtual Vec2 getOffset() override { return getCenter(); } - -protected: - bool init(const Vec2& size, - const PhysicsMaterial& material = PHYSICSSHAPE_MATERIAL_DEFAULT, - const Vec2& offset = Vec2::ZERO, - float radius = 0.0f); - -protected: - PhysicsShapeBox(); - virtual ~PhysicsShapeBox(); -}; - -/** A segment shape. */ -class AX_DLL PhysicsShapeEdgeSegment : public PhysicsShape -{ -public: - /** - * Creates a PhysicsShapeEdgeSegment with specified value. - * - * @param a It's the edge's begin position. - * @param b It's the edge's end position. - * @param material A PhysicsMaterial object, the default value is PHYSICSSHAPE_MATERIAL_DEFAULT. - * @param border It's a edge's border width. - * @return An autoreleased PhysicsShapeEdgeSegment object pointer. - */ - static PhysicsShapeEdgeSegment* create(const Vec2& a, - const Vec2& b, - const PhysicsMaterial& material = PHYSICSSHAPE_MATERIAL_DEFAULT, - float border = 1); - - /** - * Get this edge's begin position. - * - * @return A Vec2 object. - */ - Vec2 getPointA() const; - - /** - * Get this edge's end position. - * - * @return A Vec2 object. - */ - Vec2 getPointB() const; - - /** - * Get this edge's center position. - * - * @return A Vec2 object. - */ - virtual Vec2 getCenter() override; - -protected: - bool init(const Vec2& a, - const Vec2& b, - const PhysicsMaterial& material = PHYSICSSHAPE_MATERIAL_DEFAULT, - float border = 1); - virtual void updateScale() override; - -protected: - PhysicsShapeEdgeSegment(); - virtual ~PhysicsShapeEdgeSegment(); - - friend class PhysicsBody; -}; - -/** An edge polygon shape. */ -class AX_DLL PhysicsShapeEdgePolygon : public PhysicsShape -{ -public: - /** - * Creates a PhysicsShapeEdgePolygon with specified value. - * - * @param points A Vec2 object pointer, it contains an array of points. - * @param count An integer number, contains the count of the points array. - * @param material A PhysicsMaterial object, the default value is PHYSICSSHAPE_MATERIAL_DEFAULT. - * @param border It's a edge's border width. - * @return An autoreleased PhysicsShapeEdgePolygon object pointer. - */ - static PhysicsShapeEdgePolygon* create(const Vec2* points, - int count, - const PhysicsMaterial& material = PHYSICSSHAPE_MATERIAL_DEFAULT, - float border = 1); - - /** - * Get this polygon's center position. - * - * @return A Vec2 object. - */ - virtual Vec2 getCenter() override; - - /** - * Get this polygon's points array. - * - * @param outPoints A Vec2 array pointer. - */ - void getPoints(Vec2* outPoints) const; - - /** - * Get this polygon's points array count. - * - * @return An integer number. - */ - int getPointsCount() const; - -protected: - bool init(const Vec2* points, - int count, - const PhysicsMaterial& material = PHYSICSSHAPE_MATERIAL_DEFAULT, - float border = 1); - virtual void updateScale() override; - -protected: - PhysicsShapeEdgePolygon(); - virtual ~PhysicsShapeEdgePolygon(); - - friend class PhysicsBody; -}; - -/** An edge box shape. */ -class AX_DLL PhysicsShapeEdgeBox : public PhysicsShapeEdgePolygon -{ -public: - /** - * Creates a PhysicsShapeEdgeBox with specified value. - * - * @param size The size contains this box's width and height. - * @param material A PhysicsMaterial object, the default value is PHYSICSSHAPE_MATERIAL_DEFAULT. - * @param border It's a edge's border width. - * @param offset A Vec2 object, it is the offset from the body's center of gravity in body local coordinates. - * @return An autoreleased PhysicsShapeEdgeBox object pointer. - */ - static PhysicsShapeEdgeBox* create(const Vec2& size, - const PhysicsMaterial& material = PHYSICSSHAPE_MATERIAL_DEFAULT, - float border = 0, - const Vec2& offset = Vec2::ZERO); - - /** - * Get this box's position offset. - * - * @return A Vec2 object. - */ - virtual Vec2 getOffset() override { return getCenter(); } - -protected: - bool init(const Vec2& size, - const PhysicsMaterial& material = PHYSICSSHAPE_MATERIAL_DEFAULT, - float border = 1, - const Vec2& offset = Vec2::ZERO); - -protected: - PhysicsShapeEdgeBox(); - virtual ~PhysicsShapeEdgeBox(); - - friend class PhysicsBody; -}; - -/** A chain shape. */ -class AX_DLL PhysicsShapeEdgeChain : public PhysicsShape -{ -public: - /** - * Creates a PhysicsShapeEdgeChain with specified value. - * - * @param points A Vec2 object pointer, it contains an array of points. - * @param count An integer number, contains the count of the points array. - * @param material A PhysicsMaterial object, the default value is PHYSICSSHAPE_MATERIAL_DEFAULT. - * @param border It's a edge's border width. - * @return An autoreleased PhysicsShapeEdgeChain object pointer. - */ - static PhysicsShapeEdgeChain* create(const Vec2* points, - int count, - const PhysicsMaterial& material = PHYSICSSHAPE_MATERIAL_DEFAULT, - float border = 1); - - /** - * Get this chain's center position. - * - * @return A Vec2 object. - */ - virtual Vec2 getCenter() override; - - /** - * Get this chain's points array. - * - * @param outPoints A Vec2 array pointer. - */ - void getPoints(Vec2* outPoints) const; - - /** - * Get this chain's points array count. - * - * @return An integer number. - */ - int getPointsCount() const; - -protected: - bool init(const Vec2* points, - int count, - const PhysicsMaterial& material = PHYSICSSHAPE_MATERIAL_DEFAULT, - float border = 1); - virtual void updateScale() override; - -protected: - PhysicsShapeEdgeChain(); - virtual ~PhysicsShapeEdgeChain(); - - friend class PhysicsBody; -}; - -/** @} */ -/** @} */ - -} - -#endif // defined(AX_ENABLE_PHYSICS) -#endif // __CCPHYSICS_FIXTURE_H__ diff --git a/core/physics/PhysicsWorld.cpp b/core/physics/PhysicsWorld.cpp deleted file mode 100644 index 85389f1ad7e9..000000000000 --- a/core/physics/PhysicsWorld.cpp +++ /dev/null @@ -1,1118 +0,0 @@ -/**************************************************************************** - Copyright (c) 2013-2016 Chukong Technologies Inc. - Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - Copyright (c) 2019-present Axmol Engine contributors (see AUTHORS.md). - - https://axmol.dev/ - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - ****************************************************************************/ - -#include "physics/PhysicsWorld.h" -#if defined(AX_ENABLE_PHYSICS) -# include -# include - -# include "chipmunk/chipmunk_private.h" -# include "physics/PhysicsBody.h" -# include "physics/PhysicsShape.h" -# include "physics/PhysicsContact.h" -# include "physics/PhysicsJoint.h" -# include "physics/PhysicsHelper.h" - -# include "2d/DrawNode.h" -# include "2d/Scene.h" -# include "base/Director.h" -# include "base/EventDispatcher.h" -# include "base/EventCustom.h" - -namespace ax -{ -const float PHYSICS_INFINITY = FLT_MAX; -extern const char* PHYSICSCONTACT_EVENT_NAME; - -const int PhysicsWorld::DEBUGDRAW_NONE = 0x00; -const int PhysicsWorld::DEBUGDRAW_SHAPE = 0x01; -const int PhysicsWorld::DEBUGDRAW_JOINT = 0x02; -const int PhysicsWorld::DEBUGDRAW_CONTACT = 0x04; -const int PhysicsWorld::DEBUGDRAW_ALL = DEBUGDRAW_SHAPE | DEBUGDRAW_JOINT | DEBUGDRAW_CONTACT; - -const float _debugDrawThickness = 0.5f; // thickness of the DebugDraw lines, circles, dots, polygons - -namespace -{ -typedef struct RayCastCallbackInfo -{ - PhysicsWorld* world; - PhysicsRayCastCallbackFunc func; - Vec2 p1; - Vec2 p2; - void* data; -} RayCastCallbackInfo; - -typedef struct RectQueryCallbackInfo -{ - PhysicsWorld* world; - PhysicsQueryRectCallbackFunc func; - void* data; -} RectQueryCallbackInfo; - -typedef struct PointQueryCallbackInfo -{ - PhysicsWorld* world; - PhysicsQueryPointCallbackFunc func; - void* data; -} PointQueryCallbackInfo; -} // namespace - -class PhysicsWorldCallback -{ -public: - static cpBool collisionBeginCallbackFunc(cpArbiter* arb, struct cpSpace* space, PhysicsWorld* world); - static cpBool collisionPreSolveCallbackFunc(cpArbiter* arb, cpSpace* space, PhysicsWorld* world); - static void collisionPostSolveCallbackFunc(cpArbiter* arb, cpSpace* space, PhysicsWorld* world); - static void collisionSeparateCallbackFunc(cpArbiter* arb, cpSpace* space, PhysicsWorld* world); - static void rayCastCallbackFunc(cpShape* shape, - cpVect point, - cpVect normal, - cpFloat alpha, - RayCastCallbackInfo* info); - static void queryRectCallbackFunc(cpShape* shape, RectQueryCallbackInfo* info); - static void queryPointFunc(cpShape* shape, - cpVect point, - cpFloat distance, - cpVect gradient, - PointQueryCallbackInfo* info); - static void getShapesAtPointFunc(cpShape* shape, - cpVect point, - cpFloat distance, - cpVect gradient, - Vector* arr); - -public: - static bool continues; -}; - -bool PhysicsWorldCallback::continues = true; - -cpBool PhysicsWorldCallback::collisionBeginCallbackFunc(cpArbiter* arb, struct cpSpace* /*space*/, PhysicsWorld* world) -{ - CP_ARBITER_GET_SHAPES(arb, a, b); - - PhysicsShape* shapeA = static_cast(cpShapeGetUserData(a)); - PhysicsShape* shapeB = static_cast(cpShapeGetUserData(b)); - AX_ASSERT(shapeA != nullptr && shapeB != nullptr); - - auto contact = PhysicsContact::construct(shapeA, shapeB); - cpArbiterSetUserData(arb, contact); - contact->_contactInfo = arb; - - return world->collisionBeginCallback(*contact); -} - -cpBool PhysicsWorldCallback::collisionPreSolveCallbackFunc(cpArbiter* arb, cpSpace* /*space*/, PhysicsWorld* world) -{ - return world->collisionPreSolveCallback(*static_cast(cpArbiterGetUserData(arb))); -} - -void PhysicsWorldCallback::collisionPostSolveCallbackFunc(cpArbiter* arb, cpSpace* /*space*/, PhysicsWorld* world) -{ - world->collisionPostSolveCallback(*static_cast(cpArbiterGetUserData(arb))); -} - -void PhysicsWorldCallback::collisionSeparateCallbackFunc(cpArbiter* arb, cpSpace* /*space*/, PhysicsWorld* world) -{ - PhysicsContact* contact = static_cast(cpArbiterGetUserData(arb)); - - world->collisionSeparateCallback(*contact); - - delete contact; -} - -void PhysicsWorldCallback::rayCastCallbackFunc(cpShape* shape, - cpVect point, - cpVect normal, - cpFloat alpha, - RayCastCallbackInfo* info) -{ - if (!PhysicsWorldCallback::continues) - { - return; - } - - PhysicsShape* physicsShape = static_cast(cpShapeGetUserData(shape)); - AX_ASSERT(physicsShape != nullptr); - - PhysicsRayCastInfo callbackInfo = { - physicsShape, - info->p1, - info->p2, - PhysicsHelper::cpv2vec2(point), - PhysicsHelper::cpv2vec2(normal), - static_cast(alpha), - }; - - PhysicsWorldCallback::continues = info->func(*info->world, callbackInfo, info->data); -} - -void PhysicsWorldCallback::queryRectCallbackFunc(cpShape* shape, RectQueryCallbackInfo* info) -{ - PhysicsShape* physicsShape = static_cast(cpShapeGetUserData(shape)); - AX_ASSERT(physicsShape != nullptr); - - if (!PhysicsWorldCallback::continues) - { - return; - } - - PhysicsWorldCallback::continues = info->func(*info->world, *physicsShape, info->data); -} - -void PhysicsWorldCallback::getShapesAtPointFunc(cpShape* shape, - cpVect /*point*/, - cpFloat /*distance*/, - cpVect /*gradient*/, - Vector* arr) -{ - PhysicsShape* physicsShape = static_cast(cpShapeGetUserData(shape)); - AX_ASSERT(physicsShape != nullptr); - arr->pushBack(physicsShape); -} - -void PhysicsWorldCallback::queryPointFunc(cpShape* shape, - cpVect /*point*/, - cpFloat /*distance*/, - cpVect /*gradient*/, - PointQueryCallbackInfo* info) -{ - PhysicsShape* physicsShape = static_cast(cpShapeGetUserData(shape)); - AX_ASSERT(physicsShape != nullptr); - PhysicsWorldCallback::continues = info->func(*info->world, *physicsShape, info->data); -} - -static inline cpSpaceDebugColor RGBAColor(float r, float g, float b, float a) -{ - cpSpaceDebugColor color = {r, g, b, a}; - return color; -} - -static inline cpSpaceDebugColor LAColor(float l, float a) -{ - cpSpaceDebugColor color = {l, l, l, a}; - return color; -} - -static void DrawCircle(cpVect p, - cpFloat /*a*/, - cpFloat r, - cpSpaceDebugColor outline, - cpSpaceDebugColor fill, - cpDataPointer data) -{ - const Color4F fillColor(fill.r, fill.g, fill.b, fill.a); - const Color4F outlineColor(outline.r, outline.g, outline.b, outline.a); - DrawNode* drawNode = static_cast(data); - float radius = PhysicsHelper::cpfloat2float(r); - Vec2 centre = PhysicsHelper::cpv2vec2(p); - - static const int CIRCLE_SEG_NUM = 12; - Vec2 seg[CIRCLE_SEG_NUM] = {}; - - for (int i = 0; i < CIRCLE_SEG_NUM; ++i) - { - float angle = (float)i * M_PI / (float)CIRCLE_SEG_NUM * 2.0f; - Vec2 d(radius * cosf(angle), radius * sinf(angle)); - seg[i] = centre + d; - } - drawNode->drawPolygon(seg, CIRCLE_SEG_NUM, fillColor, _debugDrawThickness, outlineColor); -} - -static void DrawFatSegment(cpVect a, - cpVect b, - cpFloat r, - cpSpaceDebugColor outline, - cpSpaceDebugColor /*fill*/, - cpDataPointer data) -{ - const Color4F outlineColor(outline.r, outline.g, outline.b, outline.a); - DrawNode* drawNode = static_cast(data); - drawNode->drawSegment(PhysicsHelper::cpv2vec2(a), PhysicsHelper::cpv2vec2(b), - PhysicsHelper::cpfloat2float(r == 0 ? _debugDrawThickness : r), outlineColor); -} - -static void DrawSegment(cpVect a, cpVect b, cpSpaceDebugColor color, cpDataPointer data) -{ - DrawFatSegment(a, b, 0.0, color, color, data); -} - -static void DrawPolygon(int count, - const cpVect* verts, - cpFloat /*r*/, - cpSpaceDebugColor outline, - cpSpaceDebugColor fill, - cpDataPointer data) -{ - const Color4F fillColor(fill.r, fill.g, fill.b, fill.a); - const Color4F outlineColor(outline.r, outline.g, outline.b, outline.a); - DrawNode* drawNode = static_cast(data); - int num = count; - Vec2* seg = new Vec2[num]; - for (int i = 0; i < num; ++i) - seg[i] = PhysicsHelper::cpv2vec2(verts[i]); - - drawNode->drawPolygon(seg, num, fillColor, _debugDrawThickness, outlineColor); - - delete[] seg; -} - -static void DrawDot(cpFloat /*size*/, cpVect pos, cpSpaceDebugColor color, cpDataPointer data) -{ - const Color4F dotColor(color.r, color.g, color.b, color.a); - DrawNode* drawNode = static_cast(data); - drawNode->drawDot(PhysicsHelper::cpv2vec2(pos), _debugDrawThickness, dotColor); -} - -static cpSpaceDebugColor ColorForShape(cpShape* shape, cpDataPointer /*data*/) -{ - if (cpShapeGetSensor(shape)) - { - return LAColor(1.0f, 0.3f); - } - else - { - cpBody* body = cpShapeGetBody(shape); - - if (cpBodyIsSleeping(body)) - { - return LAColor(0.2f, 0.3f); - } - else if (body->sleeping.idleTime > shape->space->sleepTimeThreshold) - { - return LAColor(0.66f, 0.3f); - } - else - { - - float intensity = (cpBodyGetType(body) == CP_BODY_TYPE_STATIC ? 0.15f : 0.75f); - return RGBAColor(intensity, 0.0f, 0.0f, 0.3f); - } - } -} - -void PhysicsWorld::debugDraw() -{ - if (!_debugDraw) - { - return; - } - - if (!_debugDraw->getParent()) - { - Director::getInstance()->getRunningScene()->addChild(_debugDraw); - } - - cpSpaceDebugDrawOptions drawOptions = { - DrawCircle, - DrawSegment, - DrawFatSegment, - DrawPolygon, - DrawDot, - - (cpSpaceDebugDrawFlags)(_debugDrawMask), - - {1.0f, 0.0f, 0.0f, 1.0f}, - ColorForShape, - {0.0f, 0.75f, 0.0f, 1.0f}, - {0.0f, 0.0f, 1.0f, 1.0f}, - _debugDraw, - }; - if (_debugDraw) - { - _debugDraw->clear(); - cpSpaceDebugDraw(_cpSpace, &drawOptions); - } -} - -bool PhysicsWorld::collisionBeginCallback(PhysicsContact& contact) -{ - bool ret = true; - - PhysicsShape* shapeA = contact.getShapeA(); - PhysicsShape* shapeB = contact.getShapeB(); - PhysicsBody* bodyA = shapeA->getBody(); - PhysicsBody* bodyB = shapeB->getBody(); - auto&& jointsA = bodyA->getJoints(); - - // check the joint is collision enable or not - for (PhysicsJoint* joint : jointsA) - { - if (std::find(_joints.begin(), _joints.end(), joint) == _joints.end()) - { - continue; - } - - if (!joint->isCollisionEnabled()) - { - PhysicsBody* body = joint->getBodyA() == bodyA ? joint->getBodyB() : joint->getBodyA(); - - if (body == bodyB) - { - contact.setNotificationEnable(false); - return false; - } - } - } - - // bitmask check - if ((shapeA->getCategoryBitmask() & shapeB->getContactTestBitmask()) == 0 || - (shapeA->getContactTestBitmask() & shapeB->getCategoryBitmask()) == 0) - { - contact.setNotificationEnable(false); - } - - if (shapeA->getGroup() != 0 && shapeA->getGroup() == shapeB->getGroup()) - { - ret = shapeA->getGroup() > 0; - } - else - { - if ((shapeA->getCategoryBitmask() & shapeB->getCollisionBitmask()) == 0 || - (shapeB->getCategoryBitmask() & shapeA->getCollisionBitmask()) == 0) - { - ret = false; - } - } - - if (contact.isNotificationEnabled()) - { - contact.setEventCode(PhysicsContact::EventCode::BEGIN); - contact.setWorld(this); - _eventDispatcher->dispatchEvent(&contact); - } - - return ret ? contact.resetResult() : false; -} - -bool PhysicsWorld::collisionPreSolveCallback(PhysicsContact& contact) -{ - if (!contact.isNotificationEnabled()) - { - return true; - } - - contact.setEventCode(PhysicsContact::EventCode::PRESOLVE); - contact.setWorld(this); - _eventDispatcher->dispatchEvent(&contact); - - return contact.resetResult(); -} - -void PhysicsWorld::collisionPostSolveCallback(PhysicsContact& contact) -{ - if (!contact.isNotificationEnabled()) - { - return; - } - - contact.setEventCode(PhysicsContact::EventCode::POSTSOLVE); - contact.setWorld(this); - _eventDispatcher->dispatchEvent(&contact); -} - -void PhysicsWorld::collisionSeparateCallback(PhysicsContact& contact) -{ - if (!contact.isNotificationEnabled()) - { - return; - } - - contact.setEventCode(PhysicsContact::EventCode::SEPARATE); - contact.setWorld(this); - _eventDispatcher->dispatchEvent(&contact); -} - -void PhysicsWorld::rayCast(PhysicsRayCastCallbackFunc func, const Vec2& point1, const Vec2& point2, void* data) -{ - AXASSERT(func != nullptr, "func shouldn't be nullptr"); - - if (func != nullptr) - { - if (!_delayAddBodies.empty() || !_delayRemoveBodies.empty()) - { - updateBodies(); - } - RayCastCallbackInfo info = {this, func, point1, point2, data}; - - PhysicsWorldCallback::continues = true; - cpSpaceSegmentQuery(_cpSpace, PhysicsHelper::vec22cpv(point1), PhysicsHelper::vec22cpv(point2), 0.0f, - CP_SHAPE_FILTER_ALL, (cpSpaceSegmentQueryFunc)PhysicsWorldCallback::rayCastCallbackFunc, - &info); - } -} - -void PhysicsWorld::queryRect(PhysicsQueryRectCallbackFunc func, const Rect& rect, void* data) -{ - AXASSERT(func != nullptr, "func shouldn't be nullptr"); - - if (func != nullptr) - { - if (!_delayAddBodies.empty() || !_delayRemoveBodies.empty()) - { - updateBodies(); - } - RectQueryCallbackInfo info = {this, func, data}; - - PhysicsWorldCallback::continues = true; - cpSpaceBBQuery(_cpSpace, PhysicsHelper::rect2cpbb(rect), CP_SHAPE_FILTER_ALL, - (cpSpaceBBQueryFunc)PhysicsWorldCallback::queryRectCallbackFunc, &info); - } -} - -void PhysicsWorld::queryPoint(PhysicsQueryPointCallbackFunc func, const Vec2& point, void* data) -{ - AXASSERT(func != nullptr, "func shouldn't be nullptr"); - - if (func != nullptr) - { - if (!_delayAddBodies.empty() || !_delayRemoveBodies.empty()) - { - updateBodies(); - } - PointQueryCallbackInfo info = {this, func, data}; - - PhysicsWorldCallback::continues = true; - cpSpacePointQuery(_cpSpace, PhysicsHelper::vec22cpv(point), 0, CP_SHAPE_FILTER_ALL, - (cpSpacePointQueryFunc)PhysicsWorldCallback::queryPointFunc, &info); - } -} - -Vector PhysicsWorld::getShapes(const Vec2& point) const -{ - Vector arr; - cpSpacePointQuery(_cpSpace, PhysicsHelper::vec22cpv(point), 0, CP_SHAPE_FILTER_ALL, - (cpSpacePointQueryFunc)PhysicsWorldCallback::getShapesAtPointFunc, &arr); - - return arr; -} - -PhysicsShape* PhysicsWorld::getShape(const Vec2& point) const -{ - cpShape* shape = - cpSpacePointQueryNearest(_cpSpace, PhysicsHelper::vec22cpv(point), 0, CP_SHAPE_FILTER_ALL, nullptr); - return shape == nullptr ? nullptr : static_cast(cpShapeGetUserData(shape)); -} - -bool PhysicsWorld::init() -{ - do - { -# if AX_TARGET_PLATFORM == AX_PLATFORM_WIN32 - _cpSpace = cpSpaceNew(); -# else - _cpSpace = cpHastySpaceNew(); - cpHastySpaceSetThreads(_cpSpace, 0); -# endif - AX_BREAK_IF(_cpSpace == nullptr); - - cpSpaceSetGravity(_cpSpace, PhysicsHelper::vec22cpv(_gravity)); - - cpCollisionHandler* handler = cpSpaceAddDefaultCollisionHandler(_cpSpace); - handler->userData = this; - handler->beginFunc = (cpCollisionBeginFunc)PhysicsWorldCallback::collisionBeginCallbackFunc; - handler->preSolveFunc = (cpCollisionPreSolveFunc)PhysicsWorldCallback::collisionPreSolveCallbackFunc; - handler->postSolveFunc = (cpCollisionPostSolveFunc)PhysicsWorldCallback::collisionPostSolveCallbackFunc; - handler->separateFunc = (cpCollisionSeparateFunc)PhysicsWorldCallback::collisionSeparateCallbackFunc; - - return true; - } while (false); - - return false; -} - -void PhysicsWorld::addBody(PhysicsBody* body) -{ - AXASSERT(body != nullptr, "the body can not be nullptr"); - - if (body->getWorld() == this) - { - return; - } - - if (body->getWorld() != nullptr) - { - body->removeFromWorld(); - } - - addBodyOrDelay(body); - _bodies.pushBack(body); - body->_world = this; - body->setFixedUpdate(_fixedRate > 0); -} - -void PhysicsWorld::doAddBody(PhysicsBody* body) -{ - if (body->isEnabled()) - { - // add body to space - if (!cpSpaceContainsBody(_cpSpace, body->_cpBody)) - { - cpSpaceAddBody(_cpSpace, body->_cpBody); - } - - // add shapes to space - for (auto&& shape : body->getShapes()) - { - addShape(dynamic_cast(shape)); - } - } -} - -void PhysicsWorld::addBodyOrDelay(PhysicsBody* body) -{ - auto removeBodyIter = _delayRemoveBodies.find(body); - if (removeBodyIter != _delayRemoveBodies.end()) - { - _delayRemoveBodies.erase(removeBodyIter); - return; - } - - if (_delayAddBodies.find(body) == _delayAddBodies.end()) - { - _delayAddBodies.pushBack(body); - } -} - -void PhysicsWorld::updateBodies() -{ - if (cpSpaceIsLocked(_cpSpace)) - { - return; - } - - // issue #4944, contact callback will be invoked when add/remove body, _delayAddBodies maybe changed, so we need - // make a copy. - auto addCopy = _delayAddBodies; - _delayAddBodies.clear(); - for (auto&& body : addCopy) - { - doAddBody(body); - } - - auto removeCopy = _delayRemoveBodies; - _delayRemoveBodies.clear(); - for (auto&& body : removeCopy) - { - doRemoveBody(body); - } -} - -void PhysicsWorld::removeBody(int tag) -{ - for (auto&& body : _bodies) - { - if (body->getTag() == tag) - { - removeBody(body); - return; - } - } -} - -void PhysicsWorld::removeBody(PhysicsBody* body) -{ - if (body->getWorld() != this) - { - AXLOGD("Physics Warning: this body doesn't belong to this world"); - return; - } - - // destroy the body's joints - auto removeCopy = body->_joints; - for (auto&& joint : removeCopy) - { - removeJoint(joint, true); - } - body->_joints.clear(); - - removeBodyOrDelay(body); - _bodies.eraseObject(body); - body->_world = nullptr; -} - -void PhysicsWorld::removeBodyOrDelay(PhysicsBody* body) -{ - if (_delayAddBodies.getIndex(body) != AX_INVALID_INDEX) - { - _delayAddBodies.eraseObject(body); - return; - } - - if (cpSpaceIsLocked(_cpSpace)) - { - if (_delayRemoveBodies.getIndex(body) == AX_INVALID_INDEX) - { - _delayRemoveBodies.pushBack(body); - } - } - else - { - doRemoveBody(body); - } -} - -void PhysicsWorld::removeJoint(PhysicsJoint* joint, bool destroy) -{ - if (joint) - { - if (joint->getWorld() != this && destroy) - { - AXLOGD( - "physics warning: the joint is not in this world, it won't be destroyed until the body it connects is " - "destroyed"); - return; - } - - joint->_destroyMark = destroy; - - bool removedFromDelayAdd = false; - auto it = std::find(_delayAddJoints.begin(), _delayAddJoints.end(), joint); - if (it != _delayAddJoints.end()) - { - _delayAddJoints.erase(it); - removedFromDelayAdd = true; - } - - if (cpSpaceIsLocked(_cpSpace)) - { - if (removedFromDelayAdd) - return; - if (std::find(_delayRemoveJoints.rbegin(), _delayRemoveJoints.rend(), joint) == _delayRemoveJoints.rend()) - { - _delayRemoveJoints.emplace_back(joint); - } - } - else - { - doRemoveJoint(joint); - } - } -} - -void PhysicsWorld::updateJoints() -{ - if (cpSpaceIsLocked(_cpSpace)) - { - return; - } - - for (auto&& joint : _delayAddJoints) - { - joint->_world = this; - if (joint->initJoint()) - { - _joints.emplace_back(joint); - } - else - { - delete joint; - } - } - _delayAddJoints.clear(); - - for (auto&& joint : _delayRemoveJoints) - { - doRemoveJoint(joint); - } - _delayRemoveJoints.clear(); - - for (auto&& joint : _joints) - { - joint->flushDelayTasks(); - } -} - -void PhysicsWorld::removeShape(PhysicsShape* shape) -{ - if (shape) - { - for (auto&& cps : shape->_cpShapes) - { - if (cpSpaceContainsShape(_cpSpace, cps)) - { - cpSpaceRemoveShape(_cpSpace, cps); - } - } - } -} - -void PhysicsWorld::addJoint(PhysicsJoint* joint) -{ - if (joint) - { - AXASSERT(joint->getWorld() == nullptr, "Can not add joint already add to other world!"); - - joint->_world = this; - auto it = std::find(_delayRemoveJoints.begin(), _delayRemoveJoints.end(), joint); - if (it != _delayRemoveJoints.end()) - { - _delayRemoveJoints.erase(it); - return; - } - - if (std::find(_delayAddJoints.begin(), _delayAddJoints.end(), joint) == _delayAddJoints.end()) - { - _delayAddJoints.emplace_back(joint); - } - } -} - -void PhysicsWorld::removeAllJoints(bool destroy) -{ - auto removeCopy = _joints; - for (auto&& joint : removeCopy) - { - removeJoint(joint, destroy); - } -} - -void PhysicsWorld::addShape(PhysicsShape* physicsShape) -{ - if (physicsShape) - { - for (auto&& shape : physicsShape->_cpShapes) - { - cpSpaceAddShape(_cpSpace, shape); - } - } -} - -void PhysicsWorld::doRemoveBody(PhysicsBody* body) -{ - AXASSERT(body != nullptr, "the body can not be nullptr"); - - // remove shapes - for (auto&& shape : body->getShapes()) - { - removeShape(shape); - } - - // remove body - if (cpSpaceContainsBody(_cpSpace, body->_cpBody)) - { - cpSpaceRemoveBody(_cpSpace, body->_cpBody); - } -} - -void PhysicsWorld::doRemoveJoint(PhysicsJoint* joint) -{ - for (auto&& constraint : joint->_cpConstraints) - { - cpSpaceRemoveConstraint(_cpSpace, constraint); - } - _joints.remove(joint); - joint->_world = nullptr; - - if (joint->getBodyA()) - { - joint->getBodyA()->removeJoint(joint); - } - - if (joint->getBodyB()) - { - joint->getBodyB()->removeJoint(joint); - } - - if (joint->_destroyMark) - { - delete joint; - } -} - -void PhysicsWorld::removeAllBodies() -{ - for (auto&& child : _bodies) - { - removeBodyOrDelay(child); - child->_world = nullptr; - } - - _bodies.clear(); -} - -void PhysicsWorld::setDebugDrawMask(int mask) -{ - if (mask == DEBUGDRAW_NONE) - { - if (_debugDraw) - { - _debugDraw->removeFromParent(); - AX_SAFE_RELEASE_NULL(_debugDraw); - } - } - else if (!_debugDraw) - { - _debugDraw = DrawNode::create(); - _debugDraw->setIsolated(true); - _debugDraw->retain(); - } - - _debugDrawMask = mask; -} - -const Vector& PhysicsWorld::getAllBodies() const -{ - return _bodies; -} - -PhysicsBody* PhysicsWorld::getBody(int tag) const -{ - for (auto&& body : _bodies) - { - if (body->getTag() == tag) - { - return body; - } - } - - return nullptr; -} - -void PhysicsWorld::setGravity(const Vec2& gravity) -{ - _gravity = gravity; - cpSpaceSetGravity(_cpSpace, PhysicsHelper::vec22cpv(gravity)); -} - -void PhysicsWorld::setSlopBias(float slop, float bias) -{ - cpSpaceSetCollisionSlop(_cpSpace, slop); - cpSpaceSetCollisionBias(_cpSpace, bias); -} - -void PhysicsWorld::setSubsteps(int steps) -{ - if (steps > 0) - { - _substeps = steps; - if (steps > 1) - { - _updateRate = 1; - } - } -} - -void PhysicsWorld::step(float delta) -{ - if (_autoStep) - { - AXLOGD("Physics Warning: You need to close auto step( setAutoStep(false) ) first"); - } - else - { - update(delta, true); - } -} - -void PhysicsWorld::update(float delta, bool userCall /* = false*/) -{ - - if (_preUpdateCallback) - _preUpdateCallback(); // fix #11154 - - if (!_delayAddBodies.empty()) - { - updateBodies(); - } - else if (!_delayRemoveBodies.empty()) - { - updateBodies(); - } - - auto sceneToWorldTransform = _scene->getNodeToParentTransform(); - beforeSimulation(_scene, sceneToWorldTransform, 1.f, 1.f, 0.f); - - if (!_delayAddJoints.empty() || !_delayRemoveJoints.empty()) - { - updateJoints(); - } - - if (delta < FLT_EPSILON) - { - return; - } - - if (userCall) - { -# if AX_TARGET_PLATFORM == AX_PLATFORM_WIN32 - cpSpaceStep(_cpSpace, delta); -# else - cpHastySpaceStep(_cpSpace, delta); -# endif - } - else - { - _updateTime += delta; - if (_fixedRate) - { - const float step = 1.0f / _fixedRate; - const float dt = step * _speed; - while (_updateTime > step) - { - _updateTime -= step; - for (auto&& body : _bodies) - { - body->fixedUpdate(dt); - } - _scene->fixedUpdate(dt); - -# if AX_TARGET_PLATFORM == AX_PLATFORM_WIN32 - cpSpaceStep(_cpSpace, dt); -# else - cpHastySpaceStep(_cpSpace, dt); -# endif - } - } - else - { - if (++_updateRateCount >= _updateRate) - { - const float dt = _updateTime * _speed / _substeps; - for (int i = 0; i < _substeps; ++i) - { -# if AX_TARGET_PLATFORM == AX_PLATFORM_WIN32 - cpSpaceStep(_cpSpace, dt); -# else - cpHastySpaceStep(_cpSpace, dt); -# endif - } - _updateRateCount = 0; - _updateTime = 0.0f; - } - } - } - - if (_debugDrawMask != DEBUGDRAW_NONE) - { - debugDraw(); - } - - // Update physics position, should loop as the same sequence as node tree. - // PhysicsWorld::afterSimulation() will depend on the sequence. - afterSimulation(_scene, sceneToWorldTransform, 0.f); - - if (_postUpdateCallback) - _postUpdateCallback(); // fix #11154 -} - -PhysicsWorld* PhysicsWorld::construct(Scene* scene) -{ - PhysicsWorld* world = new PhysicsWorld(); - if (world->init()) - { - world->_scene = scene; - world->_eventDispatcher = scene->getEventDispatcher(); - return world; - } - - AX_SAFE_DELETE(world); - return nullptr; -} - -PhysicsWorld::PhysicsWorld() - : _gravity(Vec2(0.0f, -98.0f)) - , _speed(1.0f) - , _updateRate(1) - , _updateRateCount(0) - , _updateTime(0.0f) - , _substeps(1) - , _fixedRate(0) - , _cpSpace(nullptr) - , _updateBodyTransform(false) - , _scene(nullptr) - , _autoStep(true) - , _debugDraw(nullptr) - , _debugDrawMask(DEBUGDRAW_NONE) - , _eventDispatcher(nullptr) -{} - -PhysicsWorld::~PhysicsWorld() -{ - removeAllJoints(true); - removeAllBodies(); - if (_cpSpace) - { -# if AX_TARGET_PLATFORM == AX_PLATFORM_WIN32 - cpSpaceFree(_cpSpace); -# else - cpHastySpaceFree(_cpSpace); -# endif - } - AX_SAFE_RELEASE_NULL(_debugDraw); -} - -void PhysicsWorld::beforeSimulation(Node* node, - const Mat4& parentToWorldTransform, - float nodeParentScaleX, - float nodeParentScaleY, - float parentRotation) -{ - auto scaleX = nodeParentScaleX * node->getScaleX(); - auto scaleY = nodeParentScaleY * node->getScaleY(); - auto rotation = parentRotation + node->getRotation(); - - auto nodeToWorldTransform = parentToWorldTransform * node->getNodeToParentTransform(); - - auto physicsBody = node->getPhysicsBody(); - if (physicsBody) - { - physicsBody->beforeSimulation(parentToWorldTransform, nodeToWorldTransform, scaleX, scaleY, rotation); - } - - for (auto&& child : node->getChildren()) - beforeSimulation(child, nodeToWorldTransform, scaleX, scaleY, rotation); -} - -void PhysicsWorld::afterSimulation(Node* node, const Mat4& parentToWorldTransform, float parentRotation) -{ - auto nodeToWorldTransform = parentToWorldTransform * node->getNodeToParentTransform(); - auto nodeRotation = parentRotation + node->getRotation(); - - auto physicsBody = node->getPhysicsBody(); - if (physicsBody) - { - physicsBody->afterSimulation(parentToWorldTransform, parentRotation); - } - - for (auto&& child : node->getChildren()) - afterSimulation(child, nodeToWorldTransform, nodeRotation); -} - -void PhysicsWorld::setPostUpdateCallback(const std::function& callback) -{ - _postUpdateCallback = callback; -} - -void PhysicsWorld::setPreUpdateCallback(const std::function& callback) -{ - _preUpdateCallback = callback; -} - -} - -#endif // defined(AX_ENABLE_PHYSICS) diff --git a/core/physics/PhysicsWorld.h b/core/physics/PhysicsWorld.h deleted file mode 100644 index 90fab7eef708..000000000000 --- a/core/physics/PhysicsWorld.h +++ /dev/null @@ -1,488 +0,0 @@ -/**************************************************************************** - Copyright (c) 2013-2016 Chukong Technologies Inc. - Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - Copyright (c) 2019-present Axmol Engine contributors (see AUTHORS.md). - - https://axmol.dev/ - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - ****************************************************************************/ - -#ifndef __CCPHYSICS_WORLD_H__ -#define __CCPHYSICS_WORLD_H__ - -#include "base/Config.h" -#if defined(AX_ENABLE_PHYSICS) - -# include -# include "base/Vector.h" -# include "math/Math.h" -# include "physics/PhysicsBody.h" - -struct cpSpace; - -namespace ax -{ - -class PhysicsBody; -class PhysicsJoint; -class PhysicsShape; -class PhysicsContact; - -class Director; -class Node; -class Sprite; -class Scene; -class DrawNode; -class PhysicsDebugDraw; -class EventDispatcher; - -class PhysicsWorld; - -typedef struct PhysicsRayCastInfo -{ - PhysicsShape* shape; - Vec2 start; - Vec2 end; ///< in lua, it's name is "ended" - Vec2 contact; - Vec2 normal; - - // FIXME: correct thing to do is use `cpFloat` instead of float. - // but in order to do so, we should include "chipmunk_types.h" - // in Chipmunk v7.0, chipmunk_types includes all the mac types that - // conflicts with cocos2d Vec2, Point,... etc types. And all the CocosStudio - // lib will need to use the `ax::` namespace prefix. And it is easier to do this - // than change all the cocosstudio library (and also users code) - float fraction; - void* data; -} PhysicsRayCastInfo; - -/** - * @brief Called for each fixture found in the query. You control how the ray cast - * proceeds by returning a float: - * return true: continue - * return false: terminate the ray cast - * @param fixture the fixture hit by the ray - * @param point the point of initial intersection - * @param normal the normal vector at the point of intersection - * @return true to continue, false to terminate - */ -typedef std::function PhysicsRayCastCallbackFunc; -typedef std::function PhysicsQueryRectCallbackFunc; -typedef PhysicsQueryRectCallbackFunc PhysicsQueryPointCallbackFunc; - -/** - * @addtogroup physics - * @{ - * @addtogroup physics_2d - * @{ - */ - -/** - * @class PhysicsWorld CCPhysicsWorld.h - * @brief An PhysicsWorld object simulates collisions and other physical properties. You do not create PhysicsWorld - * objects directly; instead, you can get it from an Scene object. - */ -class AX_DLL PhysicsWorld -{ -public: - static const int DEBUGDRAW_NONE; ///< draw nothing - static const int DEBUGDRAW_SHAPE; ///< draw shapes - static const int DEBUGDRAW_JOINT; ///< draw joints - static const int DEBUGDRAW_CONTACT; ///< draw contact - static const int DEBUGDRAW_ALL; ///< draw all - -public: - /** - * Adds a joint to this physics world. - * - * This joint will be added to this physics world at next frame. - * @attention If this joint is already added to another physics world, it will be removed from that world first and - * then add to this world. - * @param joint A pointer to an existing PhysicsJoint object. - */ - virtual void addJoint(PhysicsJoint* joint); - - /** - * Remove a joint from this physics world. - * - * If this world is not locked, the joint is removed immediately, otherwise at next frame. - * If this joint is connected with a body, it will be removed from the body also. - * @param joint A pointer to an existing PhysicsJoint object. - * @param destroy true this joint will be destroyed after remove from this world, false otherwise. - */ - virtual void removeJoint(PhysicsJoint* joint, bool destroy = true); - - /** - * Remove all joints from this physics world. - * - * @attention This function is invoked in the destructor of this physics world, you do not use this api in common. - * @param destroy true all joints will be destroyed after remove from this world, false otherwise. - */ - virtual void removeAllJoints(bool destroy = true); - - /** - * Remove a body from this physics world. - * - * If this world is not locked, the body is removed immediately, otherwise at next frame. - * @attention If this body has joints, those joints will be removed also. - * @param body A pointer to an existing PhysicsBody object. - */ - virtual void removeBody(PhysicsBody* body); - - /** - * Remove body by tag. - * - * If this world is not locked, the object is removed immediately, otherwise at next frame. - * @attention If this body has joints, those joints will be removed also. - * @param tag An integer number that identifies a PhysicsBody object. - */ - virtual void removeBody(int tag); - - /** - * Remove all bodies from physics world. - * - * If this world is not locked, those body are removed immediately, otherwise at next frame. - */ - virtual void removeAllBodies(); - - /** - * Searches for physics shapes that intersects the ray. - * - * Query this physics world along the line segment from start to end. - * @param func Func is called for each shape found. - * @param start A Vec2 object contains the begin position of the ray. - * @param end A Vec2 object contains the end position of the ray. - * @param data User defined data, it is passed to func. - */ - void rayCast(PhysicsRayCastCallbackFunc func, const Vec2& start, const Vec2& end, void* data); - - /** - * Searches for physics shapes that contains in the rect. - * - * Query this physics world to find all shapes overlap rect. - * @param func Func is called for each shape whose bounding box overlaps rect. - * @param rect A Rect object contains a rectangle's x, y, width and height. - * @param data User defined data, it is passed to func. - */ - void queryRect(PhysicsQueryRectCallbackFunc func, const Rect& rect, void* data); - - /** - * Searches for physics shapes that contains the point. - * - * @attention The point must lie inside a shape. - * @param func Func is called for each shape contains the point. - * @param point A Vec2 object contains the position of the point. - * @param data User defined data, it is passed to func. - */ - void queryPoint(PhysicsQueryPointCallbackFunc func, const Vec2& point, void* data); - - /** - * Get physics shapes that contains the point. - * - * All shapes contains the point will be pushed in a Vector object. - * @attention The point must lie inside a shape. - * @param point A Vec2 object contains the position of the point. - * @return A Vector object contains all found PhysicsShape pointer. - */ - Vector getShapes(const Vec2& point) const; - - /** - * Get the nearest physics shape that contains the point. - * - * Query this physics world at point and return the closest shape. - * @param point A Vec2 object contains the position of the point. - * @return A PhysicsShape object pointer or nullptr if no shapes were found - */ - PhysicsShape* getShape(const Vec2& point) const; - - /** - * Get all the bodies that in this physics world. - * - * @return A Vector& object contains all bodies in this physics world. - */ - const Vector& getAllBodies() const; - - /** - * Get a body by tag. - * - * @param tag An integer number that identifies a PhysicsBody object. - * @return A PhysicsBody object pointer or nullptr if no shapes were found. - */ - PhysicsBody* getBody(int tag) const; - - /** - * Get a scene contain this physics world. - * - * @attention This value is initialized in constructor - * @return A Scene object reference. - */ - Scene& getScene() const { return *_scene; } - - /** - * Get the gravity value of this physics world. - * - * @return A Vec2 object. - */ - Vec2 getGravity() const { return _gravity; } - - /** - * set the gravity value of this physics world. - * - * @param gravity A gravity value of this physics world. - */ - void setGravity(const Vec2& gravity); - - /** - * set the slop and bias value of this physics world. - * - * @param slop Amount of encouraged penetration between colliding shapes. Used to reduce oscillating contacts and keep the collision cache warm. - * @param bias Determines how fast overlapping shapes are pushed apart. Expressed as a fraction of the error remaining after each second. Defaults to pow(1.0 - 0.1, 60.0) meaning that Chipmunk fixes 10% of overlap each frame at 60Hz. - */ - void setSlopBias(float slop, float bias); - - /** - * Set the speed of this physics world. - * - * @attention if you setAutoStep(false), this won't work. - * @param speed A float number. Speed is the rate at which the simulation executes. default value is 1.0. - */ - void setSpeed(float speed) - { - if (speed >= 0.0f) - { - _speed = speed; - } - } - - /** - * Get the speed of this physics world. - * - * @return A float number. - */ - float getSpeed() { return _speed; } - - /** - * Set the update rate of this physics world - * - * Update rate is the value of EngineUpdateTimes/PhysicsWorldUpdateTimes. - * Set it higher can improve performance, set it lower can improve accuracy of physics world simulation. - * @attention if you setAutoStep(false), this won't work. - * @param rate An integer number, default value is 1.0. - */ - void setUpdateRate(int rate) - { - if (rate > 0) - { - _updateRate = rate; - } - } - - /** - * Get the update rate of this physics world. - * - * @return An integer number. - */ - int getUpdateRate() { return _updateRate; } - - /** - * set the number of substeps in an update of the physics world. - * - * One physics update will be divided into several substeps to increase its accuracy. - * @param steps An integer number, default value is 1. - */ - void setSubsteps(int steps); - - /** - * Get the number of substeps of this physics world. - * - * @return An integer number. - */ - int getSubsteps() const { return _substeps; } - - /** - * set the number of update of the physics world in a second. - * 0 - disable fixed step system - * default value is 0 - */ - void setFixedUpdateRate(int updatesPerSecond) - { - if (updatesPerSecond > 0) - { - _fixedRate = updatesPerSecond; - for (auto body : _bodies) - { - body->setFixedUpdate(true); - } - } - else - { - for (auto body : _bodies) - { - body->setFixedUpdate(false); - } - } - } - /** get the number of substeps */ - int getFixedUpdateRate() const { return _fixedRate; } - - /** - * Set the debug draw mask of this physics world. - * - * This physics world will draw shapes and joints by DrawNode according to mask. - * @param mask Mask has four value:DEBUGDRAW_NONE, DEBUGDRAW_SHAPE, DEBUGDRAW_JOINT, DEBUGDRAW_CONTACT and - * DEBUGDRAW_ALL, default is DEBUGDRAW_NONE - */ - void setDebugDrawMask(int mask); - - /** - * set the callback which invoked before update of each object in physics world. - */ - void setPreUpdateCallback(const std::function& callback); - - /** - * set the callback which invoked after update of each object in physics world. - */ - void setPostUpdateCallback(const std::function& callback); - - /** - * Get the debug draw mask. - * - * @return An integer number. - */ - int getDebugDrawMask() const { return _debugDrawMask; } - - /** - * Get the debug draw node - * - * @return Pointer to draw node, which may be nullptr - */ - DrawNode* getDebugDraw() const { return _debugDraw; } - - /** - * To control the step of physics. - * - * If you want control it by yourself( fixed-timestep for example ), you can set this to false and call step by - * yourself. - * @attention If you set auto step to false, setSpeed setSubsteps and setUpdateRate won't work, you need to control - * the time step by yourself. - * @param autoStep A bool object, default value is true. - */ - void setAutoStep(bool autoStep) { _autoStep = autoStep; } - - /** - * Get the auto step of this physics world. - * - * @return A bool object. - */ - bool isAutoStep() { return _autoStep; } - - /** - * The step for physics world. - * - * The times passing for simulate the physics. - * @attention You need to setAutoStep(false) first before it can work. - * @param delta A float number. - */ - void step(float delta); - -protected: - static PhysicsWorld* construct(Scene* scene); - bool init(); - - virtual void addBody(PhysicsBody* body); - virtual void addShape(PhysicsShape* shape); - virtual void removeShape(PhysicsShape* shape); - virtual void update(float delta, bool userCall = false); - - virtual void debugDraw(); - - virtual bool collisionBeginCallback(PhysicsContact& contact); - virtual bool collisionPreSolveCallback(PhysicsContact& contact); - virtual void collisionPostSolveCallback(PhysicsContact& contact); - virtual void collisionSeparateCallback(PhysicsContact& contact); - - virtual void doAddBody(PhysicsBody* body); - virtual void doRemoveBody(PhysicsBody* body); - virtual void doRemoveJoint(PhysicsJoint* joint); - virtual void addBodyOrDelay(PhysicsBody* body); - virtual void removeBodyOrDelay(PhysicsBody* body); - virtual void updateBodies(); - virtual void updateJoints(); - -protected: - Vec2 _gravity; - float _speed; - int _updateRate; - int _updateRateCount; - float _updateTime; - int _substeps; - int _fixedRate; - cpSpace* _cpSpace; - - bool _updateBodyTransform; - Vector _bodies; - std::list _joints; - Scene* _scene; - - bool _autoStep; - DrawNode* _debugDraw; - int _debugDrawMask; - - EventDispatcher* _eventDispatcher; - - Vector _delayAddBodies; - Vector _delayRemoveBodies; - std::vector _delayAddJoints; - std::vector _delayRemoveJoints; - - std::function _preUpdateCallback; - std::function _postUpdateCallback; - -protected: - PhysicsWorld(); - virtual ~PhysicsWorld(); - - void beforeSimulation(Node* node, - const Mat4& parentToWorldTransform, - float nodeParentScaleX, - float nodeParentScaleY, - float parentRotation); - void afterSimulation(Node* node, const Mat4& parentToWorldTransform, float parentRotation); - - friend class Node; - friend class Sprite; - friend class Scene; - friend class Director; - friend class PhysicsBody; - friend class PhysicsShape; - friend class PhysicsJoint; - friend class PhysicsWorldCallback; - friend class PhysicsDebugDraw; -}; - -extern const float AX_DLL PHYSICS_INFINITY; - -/** @} */ -/** @} */ - -} - -#endif // defined(AX_ENABLE_PHYSICS) -#endif // __CCPHYSICS_WORLD_H__ diff --git a/core/physics/cpCompat62.h b/core/physics/cpCompat62.h deleted file mode 100644 index 1d0e6ecb94ed..000000000000 --- a/core/physics/cpCompat62.h +++ /dev/null @@ -1,122 +0,0 @@ -/* Copyright (c) 2013 Scott Lembcke and Howling Moon Software - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef CHIPMUNK_COMPAT_62_H -#define CHIPMUNK_COMPAT_62_H - -#include "chipmunk/chipmunk.h" - -// -// Body -// -inline cpVect cpBodyGetVelAtWorldPoint(const cpBody* body) -{ - return cpBodyGetVelocityAtWorldPoint(body); -} -inline cpVect cpBodyGetVelAtLocalPoint(const cpBody* body) -{ - return cpBodyGetVelocityAtLocalPoint(body); -} -inline cpVect cpBodyGetVel(const cpBody* body) -{ - return cpBodyGetVelocity(body); -} -inline void cpBodySetVel(cpBody* body, cpVect velocity) -{ - cpBodySetVelocity(body, velocity); -} -inline cpVect cpBodyGetPos(const cpBody* body) -{ - return cpBodyGetPosition(body); -} -inline void cpBodySetPos(cpBody* body, cpVect pos) -{ - cpBodySetPosition(body, pos); -} -inline cpVect cpBodyGetRot(const cpBody* body) -{ - return cpBodyGetRotation(body); -} -inline cpFloat cpBodyGetAngVel(const cpBody* body) -{ - return cpBodyGetAngularVelocity(body); -} -inline void cpBodySetAngVel(cpBody* body, cpFloat angularVelocity) -{ - cpBodySetAngularVelocity(body, angularVelocity); -} -inline cpVect cpBodyLocal2World(const cpBody* body, const cpVect point) -{ - return cpBodyLocalToWorld(body, point); -} -inline cpVect cpBodyWorld2Local(const cpBody* body, const cpVect point) -{ - return cpBodyWorldToLocal(body, point); -} -inline void cpBodyApplyImpulse(cpBody* body, const cpVect j, const cpVect r) -{ - cpBodyApplyImpulseAtWorldPoint(body, j, r); -} - -// -// Shapes -// -inline void cpShapeSetLayers(cpShape* shape, unsigned int layer) -{ - cpShapeFilter filter = cpShapeGetFilter(shape); - filter.mask = layer; - filter.categories = layer; - cpShapeSetFilter(shape, filter); -} -inline unsigned int cpShapeGetLayers(cpShape* shape) -{ - cpShapeFilter filter = cpShapeGetFilter(shape); - return filter.mask; -} -inline void cpShapeSetGroup(cpShape* shape, uintptr_t group) -{ - cpShapeFilter filter = cpShapeGetFilter(shape); - filter.group = group; - cpShapeSetFilter(shape, filter); -} -inline uintptr_t cpShapeGetGroup(cpShape* shape) -{ - cpShapeFilter filter = cpShapeGetFilter(shape); - return filter.group; -} -inline int cpPolyShapeGetNumVerts(const cpShape* shape) -{ - return cpPolyShapeGetCount(shape); -} -inline cpFloat cpShapeNearestPointQuery(cpShape* shape, cpVect p, cpPointQueryInfo* out) -{ - return cpShapePointQuery(shape, p, out); -} - -// -// Space -// -inline cpShape* cpSpaceAddStaticShape(cpSpace* space, cpShape* shape) -{ - return cpSpaceAddShape(space, shape); -} - -#endif // CHIPMUNK_COMPAT_62_H diff --git a/core/physics3d/Physics3D.cpp b/core/physics3d/Physics3D.cpp index 9c20b6be10fc..5f9b1cac43a1 100644 --- a/core/physics3d/Physics3D.cpp +++ b/core/physics3d/Physics3D.cpp @@ -28,16 +28,12 @@ #if defined(AX_ENABLE_3D_PHYSICS) -# if (AX_ENABLE_BULLET_INTEGRATION) - namespace ax { AX_DLL const char* physics3dVersion() { -# if AX_ENABLE_BULLET_INTEGRATION return "bullet2.82"; -# endif } } @@ -93,6 +89,4 @@ btQuaternion convertQuatTobtQuat(const ax::Quaternion& quat) return btQuaternion(quat.x, quat.y, quat.z, quat.w); } -# endif // AX_ENABLE_BULLET_INTEGRATION - #endif // defined(AX_ENABLE_3D_PHYSICS) diff --git a/core/physics3d/Physics3D.h b/core/physics3d/Physics3D.h index 767f14e864c7..728725db3710 100644 --- a/core/physics3d/Physics3D.h +++ b/core/physics3d/Physics3D.h @@ -46,8 +46,6 @@ AX_DLL const char* physics3dVersion(); } -# if (AX_ENABLE_BULLET_INTEGRATION) - // include bullet header files # include "bullet/LinearMath/btTransform.h" # include "bullet/LinearMath/btVector3.h" @@ -65,8 +63,6 @@ btTransform convertMat4TobtTransform(const ax::Mat4& mat4); ax::Quaternion convertbtQuatToQuat(const btQuaternion& btQuat); btQuaternion convertQuatTobtQuat(const ax::Quaternion& quat); -# endif // AX_ENABLE_BULLET_INTEGRATION - #endif // defined(AX_ENABLE_3D_PHYSICS) #endif // __PHYSICS_3D_H__ diff --git a/core/physics3d/Physics3DComponent.cpp b/core/physics3d/Physics3DComponent.cpp index 27e3e12cacb5..eec30b50b786 100644 --- a/core/physics3d/Physics3DComponent.cpp +++ b/core/physics3d/Physics3DComponent.cpp @@ -30,8 +30,6 @@ THE SOFTWARE. #if defined(AX_ENABLE_3D_PHYSICS) -# if (AX_ENABLE_BULLET_INTEGRATION) - namespace ax { @@ -252,6 +250,4 @@ void Physics3DComponent::syncNodeToPhysics() } -# endif // AX_ENABLE_BULLET_INTEGRATION - #endif // defined(AX_ENABLE_3D_PHYSICS) diff --git a/core/physics3d/Physics3DComponent.h b/core/physics3d/Physics3DComponent.h index 219804227429..d2fccbb1fed1 100644 --- a/core/physics3d/Physics3DComponent.h +++ b/core/physics3d/Physics3DComponent.h @@ -34,8 +34,6 @@ #if defined(AX_ENABLE_3D_PHYSICS) -# if (AX_ENABLE_BULLET_INTEGRATION) - namespace ax { @@ -145,8 +143,6 @@ class AX_DLL Physics3DComponent : public ax::Component /// @} } -# endif // AX_ENABLE_BULLET_INTEGRATION - #endif // defined(AX_ENABLE_3D_PHYSICS) #endif // __PHYSICS_3D_COMPONENT_H__ diff --git a/core/physics3d/Physics3DConstraint.cpp b/core/physics3d/Physics3DConstraint.cpp index c20aa3c4f1ae..31886b0c9163 100644 --- a/core/physics3d/Physics3DConstraint.cpp +++ b/core/physics3d/Physics3DConstraint.cpp @@ -28,8 +28,6 @@ #if defined(AX_ENABLE_3D_PHYSICS) -# if (AX_ENABLE_BULLET_INTEGRATION) - namespace ax { @@ -882,6 +880,4 @@ void Physics3D6DofConstraint::setUseFrameOffset(bool frameOffsetOnOff) const } -# endif // AX_ENABLE_BULLET_INTEGRATION - #endif // defined(AX_ENABLE_3D_PHYSICS) diff --git a/core/physics3d/Physics3DConstraint.h b/core/physics3d/Physics3DConstraint.h index 3ee27417e463..05e0c12fd1e9 100644 --- a/core/physics3d/Physics3DConstraint.h +++ b/core/physics3d/Physics3DConstraint.h @@ -33,8 +33,6 @@ #if defined(AX_ENABLE_3D_PHYSICS) -# if (AX_ENABLE_BULLET_INTEGRATION) - class btTypedConstraint; namespace ax @@ -117,9 +115,7 @@ class AX_DLL Physics3DConstraint : public Object */ void setOverrideNumSolverIterations(int overrideNumIterations); -# if (AX_ENABLE_BULLET_INTEGRATION) btTypedConstraint* getbtContraint() { return _constraint; } -# endif protected: Physics3DConstraint(); @@ -617,8 +613,6 @@ class AX_DLL Physics3D6DofConstraint : public Physics3DConstraint } -# endif // AX_ENABLE_BULLET_INTEGRATION - #endif // defined(AX_ENABLE_3D_PHYSICS) #endif // __PHYSICS_3D_CONSTRAINT_H__ diff --git a/core/physics3d/Physics3DDebugDrawer.cpp b/core/physics3d/Physics3DDebugDrawer.cpp index 85ba1964cae2..1d165dc265a6 100644 --- a/core/physics3d/Physics3DDebugDrawer.cpp +++ b/core/physics3d/Physics3DDebugDrawer.cpp @@ -37,8 +37,6 @@ #if defined(AX_ENABLE_3D_PHYSICS) -# if (AX_ENABLE_BULLET_INTEGRATION) - namespace ax { @@ -47,10 +45,10 @@ void Physics3DDebugDrawer::drawLine(const btVector3& from, const btVector3& to, Vec3 col = convertbtVector3ToVec3(color); V3F_C4F a, b; - a.vertices = convertbtVector3ToVec3(from); - a.colors = Color4F(col.x, col.y, col.z, 1.0f); - b.vertices = convertbtVector3ToVec3(to); - b.colors = Color4F(col.x, col.y, col.z, 1.0f); + a.position = convertbtVector3ToVec3(from); + a.color = Color(col.x, col.y, col.z, 1.0f); + b.position = convertbtVector3ToVec3(to); + b.color = Color(col.x, col.y, col.z, 1.0f); _buffer.emplace_back(a); _buffer.emplace_back(b); @@ -163,6 +161,4 @@ void Physics3DDebugDrawer::clear() } -# endif // AX_ENABLE_BULLET_INTEGRATION - #endif // defined(AX_ENABLE_3D_PHYSICS) diff --git a/core/physics3d/Physics3DDebugDrawer.h b/core/physics3d/Physics3DDebugDrawer.h index 105fce3de232..7386899dfe90 100644 --- a/core/physics3d/Physics3DDebugDrawer.h +++ b/core/physics3d/Physics3DDebugDrawer.h @@ -37,7 +37,6 @@ #if defined(AX_ENABLE_3D_PHYSICS) -# if (AX_ENABLE_BULLET_INTEGRATION) # include "bullet/LinearMath/btIDebugDraw.h" namespace ax @@ -98,8 +97,6 @@ class Physics3DDebugDrawer : public btIDebugDraw } -# endif // AX_ENABLE_BULLET_INTEGRATION - #endif // defined(AX_ENABLE_3D_PHYSICS) #endif // __PHYSICS_3D_VIEWER_H__ diff --git a/core/physics3d/Physics3DObject.cpp b/core/physics3d/Physics3DObject.cpp index 869f23009914..815f6271ec74 100644 --- a/core/physics3d/Physics3DObject.cpp +++ b/core/physics3d/Physics3DObject.cpp @@ -29,8 +29,6 @@ #if defined(AX_ENABLE_3D_PHYSICS) -# if (AX_ENABLE_BULLET_INTEGRATION) - # include "bullet/btBulletCollisionCommon.h" # include "bullet/btBulletDynamicsCommon.h" @@ -553,6 +551,4 @@ ax::Mat4 Physics3DCollider::getWorldTransform() const } -# endif // AX_ENABLE_BULLET_INTEGRATION - #endif // defined(AX_ENABLE_3D_PHYSICS) diff --git a/core/physics3d/Physics3DObject.h b/core/physics3d/Physics3DObject.h index 0ab62b520970..2f04b1715fbc 100644 --- a/core/physics3d/Physics3DObject.h +++ b/core/physics3d/Physics3DObject.h @@ -35,8 +35,6 @@ #if defined(AX_ENABLE_3D_PHYSICS) -# if (AX_ENABLE_BULLET_INTEGRATION) - class btCollisionShape; class btRigidBody; class btPersistentManifold; @@ -497,8 +495,6 @@ class AX_DLL Physics3DCollider : public Physics3DObject } -# endif // AX_ENABLE_BULLET_INTEGRATION - #endif // defined(AX_ENABLE_3D_PHYSICS) #endif // __PHYSICS_3D_OBJECT_H__ diff --git a/core/physics3d/Physics3DShape.cpp b/core/physics3d/Physics3DShape.cpp index 561301748cad..a5bd8d31ad90 100644 --- a/core/physics3d/Physics3DShape.cpp +++ b/core/physics3d/Physics3DShape.cpp @@ -28,7 +28,6 @@ #if defined(AX_ENABLE_3D_PHYSICS) -# if (AX_ENABLE_BULLET_INTEGRATION) # include "bullet/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h" namespace ax @@ -41,14 +40,11 @@ Physics3DShape::ShapeType Physics3DShape::getShapeType() const Physics3DShape::Physics3DShape() : _shapeType(ShapeType::UNKNOWN) { -# if (AX_ENABLE_BULLET_INTEGRATION) _btShape = nullptr; _heightfieldData = nullptr; -# endif } Physics3DShape::~Physics3DShape() { -# if (AX_ENABLE_BULLET_INTEGRATION) AX_SAFE_DELETE(_btShape); AX_SAFE_DELETE_ARRAY(_heightfieldData); for (auto&& iter : _compoundChildShapes) @@ -56,7 +52,6 @@ Physics3DShape::~Physics3DShape() AX_SAFE_RELEASE(iter); } _compoundChildShapes.clear(); -# endif } Physics3DShape* Physics3DShape::createBox(const ax::Vec3& extent) @@ -220,6 +215,4 @@ bool Physics3DShape::initCompoundShape(const std::vector>& shapes); -# if AX_ENABLE_BULLET_INTEGRATION btCollisionShape* getbtShape() const { return _btShape; } -# endif Physics3DShape(); ~Physics3DShape(); @@ -160,11 +156,9 @@ class AX_DLL Physics3DShape : public Object protected: ShapeType _shapeType; // shape type -# if (AX_ENABLE_BULLET_INTEGRATION) btCollisionShape* _btShape; unsigned char* _heightfieldData; std::vector _compoundChildShapes; -# endif }; // end of 3d group @@ -172,8 +166,6 @@ class AX_DLL Physics3DShape : public Object } -# endif // AX_ENABLE_BULLET_INTEGRATION - #endif // defined(AX_ENABLE_3D_PHYSICS) #endif // __PHYSICS_3D_SHAPE_H__ diff --git a/core/physics3d/Physics3DWorld.cpp b/core/physics3d/Physics3DWorld.cpp index 2c4ead8ffe0e..6a1a3240d9e9 100644 --- a/core/physics3d/Physics3DWorld.cpp +++ b/core/physics3d/Physics3DWorld.cpp @@ -29,8 +29,6 @@ #if defined(AX_ENABLE_3D_PHYSICS) -# if (AX_ENABLE_BULLET_INTEGRATION) - namespace ax { @@ -425,6 +423,4 @@ void Physics3DWorld::setGhostPairCallback() } -# endif // AX_ENABLE_BULLET_INTEGRATION - #endif // defined(AX_ENABLE_3D_PHYSICS) diff --git a/core/physics3d/Physics3DWorld.h b/core/physics3d/Physics3DWorld.h index e40f3c6cc05f..07de7d601686 100644 --- a/core/physics3d/Physics3DWorld.h +++ b/core/physics3d/Physics3DWorld.h @@ -33,8 +33,6 @@ #if defined(AX_ENABLE_3D_PHYSICS) -# if (AX_ENABLE_BULLET_INTEGRATION) - class btDynamicsWorld; class btDefaultCollisionConfiguration; class btCollisionDispatcher; @@ -169,7 +167,6 @@ class AX_DLL Physics3DWorld : public Object bool _collisionCheckingFlag; bool _needGhostPairCallbackChecking; -# if (AX_ENABLE_BULLET_INTEGRATION) btDynamicsWorld* _btPhyiscsWorld; btDefaultCollisionConfiguration* _collisionConfiguration; btCollisionDispatcher* _dispatcher; @@ -177,15 +174,12 @@ class AX_DLL Physics3DWorld : public Object btSequentialImpulseConstraintSolver* _solver; btGhostPairCallback* _ghostCallback; Physics3DDebugDrawer* _debugDrawer; -# endif // AX_ENABLE_BULLET_INTEGRATION }; // end of 3d group /// @} } -# endif - #endif // defined(AX_ENABLE_3D_PHYSICS) #endif // __PHYSICS_3D_WORLD_H__ diff --git a/core/physics3d/PhysicsMeshRenderer.cpp b/core/physics3d/PhysicsMeshRenderer.cpp index 816366026179..664eeef11bc4 100644 --- a/core/physics3d/PhysicsMeshRenderer.cpp +++ b/core/physics3d/PhysicsMeshRenderer.cpp @@ -28,8 +28,6 @@ #if defined(AX_ENABLE_3D_PHYSICS) -# if (AX_ENABLE_BULLET_INTEGRATION) - namespace ax { @@ -101,6 +99,4 @@ PhysicsMeshRenderer::~PhysicsMeshRenderer() {} } -# endif // AX_ENABLE_BULLET_INTEGRATION - #endif // defined(AX_ENABLE_3D_PHYSICS) diff --git a/core/physics3d/PhysicsMeshRenderer.h b/core/physics3d/PhysicsMeshRenderer.h index ef763cb2e4c7..d82916766717 100644 --- a/core/physics3d/PhysicsMeshRenderer.h +++ b/core/physics3d/PhysicsMeshRenderer.h @@ -34,8 +34,6 @@ #if defined(AX_ENABLE_3D_PHYSICS) -# if (AX_ENABLE_BULLET_INTEGRATION) - namespace ax { /** @@ -84,8 +82,6 @@ class AX_DLL PhysicsMeshRenderer : public ax::MeshRenderer /// @} } -# endif // AX_ENABLE_BULLET_INTEGRATION - #endif // defined(AX_ENABLE_3D_PHYSICS) #endif // __PHYSICS_MESH_RENDERER_H__ diff --git a/core/platform/FileUtils.cpp b/core/platform/FileUtils.cpp index 83c61f0bd47e..5726bbd49cbe 100644 --- a/core/platform/FileUtils.cpp +++ b/core/platform/FileUtils.cpp @@ -487,32 +487,12 @@ bool FileUtils::writeStringToFile(std::string_view dataStr, std::string_view ful { return FileUtils::writeBinaryToFile(dataStr.data(), dataStr.size(), fullPath); } -#ifndef AX_CORE_PROFILE -void FileUtils::writeStringToFile(std::string dataStr, - std::string_view fullPath, - std::function callback) const -{ - performOperationOffthread( - [path = std::string{fullPath}](std::string_view dataStrIn) -> bool { - return FileUtils::getInstance()->writeStringToFile(dataStrIn, path); - }, - std::move(callback), std::move(dataStr)); -} -#endif + bool FileUtils::writeDataToFile(const Data& data, std::string_view fullPath) const { return FileUtils::writeBinaryToFile(data.getBytes(), data.getSize(), fullPath); } -#ifndef AX_CORE_PROFILE -void FileUtils::writeDataToFile(Data data, std::string_view fullPath, std::function callback) const -{ - performOperationOffthread( - [path = std::string{fullPath}](const Data& dataIn) -> bool { - return FileUtils::getInstance()->writeDataToFile(dataIn, path); - }, - std::move(callback), std::move(data)); -} -#endif + bool FileUtils::writeBinaryToFile(const void* data, size_t dataSize, std::string_view fullPath) { AXASSERT(!fullPath.empty() && dataSize > 0, "Invalid parameters."); @@ -549,32 +529,14 @@ std::string FileUtils::getStringFromFile(std::string_view filename) const getContents(filename, &s); return s; } -#ifndef AX_CORE_PROFILE -void FileUtils::getStringFromFile(std::string_view path, std::function callback) const -{ - // Get the full path on the main thread, to avoid the issue that FileUtil's is not - // thread safe, and accessing the fullPath cache and searching the search paths is not thread safe - auto fullPath = fullPathForFilename(path); - performOperationOffthread( - [path = std::string{fullPath}]() -> std::string { return FileUtils::getInstance()->getStringFromFile(path); }, - std::move(callback)); -} -#endif + Data FileUtils::getDataFromFile(std::string_view filename) const { Data d; getContents(filename, &d); return d; } -#ifndef AX_CORE_PROFILE -void FileUtils::getDataFromFile(std::string_view filename, std::function callback) const -{ - auto fullPath = fullPathForFilename(filename); - performOperationOffthread( - [path = std::string{fullPath}]() -> Data { return FileUtils::getInstance()->getDataFromFile(path); }, - std::move(callback)); -} -#endif + FileUtils::Status FileUtils::getContents(std::string_view filename, ResizableBuffer* buffer) const { if (filename.empty()) @@ -610,28 +572,7 @@ FileUtils::Status FileUtils::getContents(std::string_view filename, ResizableBuf return Status::OK; } -#ifndef AX_CORE_PROFILE -void FileUtils::writeValueMapToFile(ValueMap dict, std::string_view fullPath, std::function callback) const -{ - - performOperationOffthread( - [path = std::string{fullPath}](const ValueMap& dictIn) -> bool { - return FileUtils::getInstance()->writeValueMapToFile(dictIn, path); - }, - std::move(callback), std::move(dict)); -} -void FileUtils::writeValueVectorToFile(ValueVector vecData, - std::string_view fullPath, - std::function callback) const -{ - performOperationOffthread( - [path = std::string{fullPath}](const ValueVector& vecDataIn) -> bool { - return FileUtils::getInstance()->writeValueVectorToFile(vecDataIn, path); - }, - std::move(callback), std::move(vecData)); -} -#endif std::string FileUtils::getPathForFilename(std::string_view filename, std::string_view searchPath) const { auto file = filename; @@ -907,15 +848,7 @@ bool FileUtils::isFileExist(std::string_view filename) const return !fullpath.empty(); } } -#ifndef AX_CORE_PROFILE -void FileUtils::isFileExist(std::string_view filename, std::function callback) const -{ - auto fullPath = fullPathForFilename(filename); - performOperationOffthread( - [path = std::string{fullPath}]() -> bool { return FileUtils::getInstance()->isFileExist(path); }, - std::move(callback)); -} -#endif + bool FileUtils::isAbsolutePath(std::string_view path) const { return isAbsolutePathInternal(path); @@ -952,89 +885,6 @@ bool FileUtils::isDirectoryExist(std::string_view dirPath) const } } -#ifndef AX_CORE_PROFILE - -void FileUtils::isDirectoryExist(std::string_view fullPath, std::function callback) const -{ - AXASSERT(isAbsolutePath(fullPath), "Async isDirectoryExist only accepts absolute file paths"); - performOperationOffthread( - [path = std::string{fullPath}]() -> bool { return FileUtils::getInstance()->isDirectoryExist(path); }, - std::move(callback)); -} - -void FileUtils::createDirectory(std::string_view dirPath, std::function callback) const -{ - performOperationOffthread( - [path = std::string{dirPath}]() -> bool { return FileUtils::getInstance()->createDirectories(path); }, - std::move(callback)); -} - -void FileUtils::removeDirectory(std::string_view dirPath, std::function callback) const -{ - performOperationOffthread( - [path = std::string{dirPath}]() -> bool { return FileUtils::getInstance()->removeDirectory(path); }, - std::move(callback)); -} - -void FileUtils::removeFile(std::string_view filepath, std::function callback) const -{ - auto fullPath = fullPathForFilename(filepath); - performOperationOffthread( - [path = std::string{fullPath}]() -> bool { return FileUtils::getInstance()->removeFile(path); }, - std::move(callback)); -} - -void FileUtils::renameFile(std::string_view path, - std::string_view oldname, - std::string_view name, - std::function callback) const -{ - performOperationOffthread( - [path = std::string{path}, oldname = std::string{oldname}, name = std::string{name}]() -> bool { - return FileUtils::getInstance()->renameFile(path, oldname, name); - }, - std::move(callback)); -} - -void FileUtils::renameFile(std::string_view oldfullpath, - std::string_view newfullpath, - std::function callback) const -{ - performOperationOffthread( - [oldpath = std::string{oldfullpath}, newpath = std::string{newfullpath}]() { - return FileUtils::getInstance()->renameFile(oldpath, newpath); - }, - std::move(callback)); -} - -void FileUtils::getFileSize(std::string_view filepath, std::function callback) const -{ - auto fullPath = fullPathForFilename(filepath); - performOperationOffthread([path = std::string{fullPath}]() { return FileUtils::getInstance()->getFileSize(path); }, - std::move(callback)); -} - -void FileUtils::listFilesAsync(std::string_view dirPath, std::function)> callback) const -{ - auto fullPath = fullPathForDirectory(dirPath); - performOperationOffthread([path = std::string{fullPath}]() { return FileUtils::getInstance()->listFiles(path); }, - std::move(callback)); -} - -void FileUtils::listFilesRecursivelyAsync(std::string_view dirPath, - std::function)> callback) const -{ - auto fullPath = fullPathForDirectory(dirPath); - performOperationOffthread( - [path = std::string{fullPath}]() { - std::vector retval; - FileUtils::getInstance()->listFilesRecursively(path, &retval); - return retval; - }, - std::move(callback)); -} -#endif - std::unique_ptr FileUtils::openFileStream(std::string_view filePath, IFileStream::Mode mode) const { FileStream fs; diff --git a/core/platform/FileUtils.h b/core/platform/FileUtils.h index 6713daad10c6..2ee6c0613719 100644 --- a/core/platform/FileUtils.h +++ b/core/platform/FileUtils.h @@ -572,200 +572,6 @@ class AX_DLL FileUtils */ virtual void listFilesRecursively(std::string_view dirPath, std::vector* files) const; - -#ifndef AX_CORE_PROFILE - /** - * Gets string from a file, async off the main cocos thread - * - * @param path filepath for the string to be read. Can be relative or absolute path - * @param callback Function that will be called when file is read. Will be called - * on the main cocos thread. - */ - AX_DEPRECATED(2.1) virtual void getStringFromFile(std::string_view path, std::function callback) const; - /** - * Gets a binary data object from a file, async off the main cocos thread. - * - * @param filename filepath for the data to be read. Can be relative or absolute path - * @param callback Function that will be called when file is read. Will be called - * on the main cocos thread. - */ - AX_DEPRECATED(2.1) virtual void getDataFromFile(std::string_view filename, std::function callback) const; - /** - * Write a string to a file, done async off the main cocos thread - * Use this function if you need file access without blocking the main thread. - * - * This function takes a std::string by value on purpose, to leverage move sematics. - * If you want to avoid a copy of your datastr, use std::move/std::forward if appropriate - * - * @param dataStr the string want to save - * @param fullPath The full path to the file you want to save a string - * @param callback The function called once the string has been written to a file. This - * function will be executed on the main cocos thread. It will have on boolean argument - * signifying if the write was successful. - */ - AX_DEPRECATED(2.1) virtual void writeStringToFile(std::string dataStr, - std::string_view fullPath, - std::function callback) const; - /** - * Write Data into a file, done async off the main cocos thread. - * - * Use this function if you need to write Data while not blocking the main cocos thread. - * - * This function takes Data by value on purpose, to leverage move sematics. - * If you want to avoid a copy of your data, use std::move/std::forward if appropriate - * - *@param data The data that will be written to disk - *@param fullPath The absolute file path that the data will be written to - *@param callback The function that will be called when data is written to disk. This - * function will be executed on the main cocos thread. It will have on boolean argument - * signifying if the write was successful. - */ - AX_DEPRECATED(2.1) virtual void writeDataToFile(Data data, std::string_view fullPath, std::function callback) const; - /** - * Write a ValueMap into a file, done async off the main cocos thread. - * - * Use this function if you need to write a ValueMap while not blocking the main cocos thread. - * - * This function takes ValueMap by value on purpose, to leverage move sematics. - * If you want to avoid a copy of your dict, use std::move/std::forward if appropriate - * - *@param dict The ValueMap that will be written to disk - *@param fullPath The absolute file path that the data will be written to - *@param callback The function that will be called when dict is written to disk. This - * function will be executed on the main cocos thread. It will have on boolean argument - * signifying if the write was successful. - */ - AX_DEPRECATED(2.1) virtual void writeValueMapToFile(ValueMap dict, - std::string_view fullPath, - std::function callback) const; - /** - * Write a ValueVector into a file, done async off the main cocos thread. - * - * Use this function if you need to write a ValueVector while not blocking the main cocos thread. - * - * This function takes ValueVector by value on purpose, to leverage move sematics. - * If you want to avoid a copy of your dict, use std::move/std::forward if appropriate - * - *@param vecData The ValueVector that will be written to disk - *@param fullPath The absolute file path that the data will be written to - *@param callback The function that will be called when vecData is written to disk. This - * function will be executed on the main cocos thread. It will have on boolean argument - * signifying if the write was successful. - */ - AX_DEPRECATED(2.1) virtual void writeValueVectorToFile(ValueVector vecData, - std::string_view fullPath, - std::function callback) const; - /** - * Checks if a file exists, done async off the main cocos thread. - * - * Use this function if you need to check if a file exists while not blocking the main cocos thread. - * - * @note If a relative path was passed in, it will be inserted a default root path at the beginning. - * @param filename The path of the file, it could be a relative or absolute path. - * @param callback The function that will be called when the operation is complete. Will have one boolean - * argument, true if the file exists, false otherwise. - */ - AX_DEPRECATED(2.1) virtual void isFileExist(std::string_view filename, std::function callback) const; - - AX_DEPRECATED(2.1) static std::string getFileExtension(std::string_view filePath) { return getPathExtension(filePath); } - AX_DEPRECATED(2.1) static std::string getFileShortName(std::string_view filePath) { return getPathBaseName(filePath); } - /** - * Checks whether the absoulate path is a directory, async off of the main cocos thread. - * - * @param dirPath The path of the directory, it must be an absolute path - * @param callback that will accept a boolean, true if the file exists, false otherwise. - * Callback will happen on the main cocos thread. - */ - AX_DEPRECATED(2.1) virtual void isDirectoryExist(std::string_view fullPath, std::function callback) const; - - AX_DEPRECATED(2.1) bool createDirectory(std::string_view dirPath) const { return createDirectories(dirPath); } - - /** - * Create a directory, async off the main cocos thread. - * - * @param dirPath the path of the directory, it must be an absolute path - * @param callback The function that will be called when the operation is complete. Will have one boolean - * argument, true if the directory was successfully, false otherwise. - */ - AX_DEPRECATED(2.1) void createDirectory(std::string_view dirPath, std::function callback) const; - - /** - * Removes a directory, async off the main cocos thread. - * - * @param dirPath the path of the directory, it must be an absolute path - * @param callback The function that will be called when the operation is complete. Will have one boolean - * argument, true if the directory was successfully removed, false otherwise. - */ - AX_DEPRECATED(2.1) void removeDirectory(std::string_view dirPath, std::function callback) const; - - /** - * Removes a file, async off the main cocos thread. - * - * @param filepath the path of the file to remove, it must be an absolute path - * @param callback The function that will be called when the operation is complete. Will have one boolean - * argument, true if the file was successfully removed, false otherwise. - */ - AX_DEPRECATED(2.1) virtual void removeFile(std::string_view filepath, std::function callback) const; - - /** - * Renames a file under the given directory, async off the main cocos thread. - * - * @param path The parent directory path of the file, it must be an absolute path. - * @param oldname The current name of the file. - * @param name The new name of the file. - * @param callback The function that will be called when the operation is complete. Will have one boolean - * argument, true if the file was successfully renamed, false otherwise. - */ - AX_DEPRECATED(2.1) virtual void renameFile(std::string_view path, - std::string_view oldname, - std::string_view name, - std::function callback) const; - - /** - * Renames a file under the given directory, async off the main cocos thread. - * - * @param oldfullpath The current fullpath of the file. Includes path and name. - * @param newfullpath The new fullpath of the file. Includes path and name. - * @param callback The function that will be called when the operation is complete. Will have one boolean - * argument, true if the file was successfully renamed, false otherwise. - */ - AX_DEPRECATED(2.1) void renameFile(std::string_view oldfullpath, - std::string_view newfullpath, - std::function callback) const; - - /** - * Retrieve the file size, async off the main cocos thread. - * - * @note If a relative path was passed in, it will be inserted a default root path at the beginning. - * @param filepath The path of the file, it could be a relative or absolute path. - * @param callback The function that will be called when the operation is complete. Will have one long - * argument, the file size. - */ - AX_DEPRECATED(2.1) void getFileSize(std::string_view filepath, std::function callback) const; - - /** - * List all files in a directory async, off of the main cocos thread. - * - * @param dirPath The path of the directory, it could be a relative or an absolute path. - * @param callback The callback to be called once the list operation is complete. Will be called on the main cocos - * thread. - * @js NA - * @lua NA - */ - AX_DEPRECATED(2.1) void listFilesAsync(std::string_view dirPath, std::function)> callback) const; - - /** - * List all files recursively in a directory, async off the main cocos thread. - * - * @param dirPath The path of the directory, it could be a relative or an absolute path. - * @param callback The callback to be called once the list operation is complete. - * Will be called on the main cocos thread. - * @js NA - * @lua NA - */ - AX_DEPRECATED(2.1) void listFilesRecursivelyAsync(std::string_view dirPath, - std::function)> callback) const; -#endif /** Returns the full path cache. */ const hlookup::string_map getFullPathCache() const { return _fullPathCache; } diff --git a/core/platform/PlatformMacros.h b/core/platform/PlatformMacros.h index 8b628579e49f..0a17f529f00c 100644 --- a/core/platform/PlatformMacros.h +++ b/core/platform/PlatformMacros.h @@ -314,52 +314,6 @@ public: \ #define AX_BREAK_IF(cond) \ if (cond) \ break -#ifndef AX_CORE_PROFILE -# define __AXLOGWITHFUNCTION(s, ...) \ - ax::print("%s : %s", __FUNCTION__, ax::StringUtils::format(s, ##__VA_ARGS__).c_str()) -/// @name legacy log macros, deprecated since axmol 2.1.4, use AXLOGD, AXLOGI, AXLOGW, ... instead -/// @{ -# if !defined(_AX_DEBUG) || _AX_DEBUG == 0 -# define AXLOG(...) \ - do \ - { \ - } while (0) -# define AXLOGINFO(...) \ - do \ - { \ - } while (0) -# define AXLOGERROR(...) \ - do \ - { \ - } while (0) -# define AXLOGWARN(...) \ - do \ - { \ - } while (0) - -# elif _AX_DEBUG == 1 -# define AXLOG(format, ...) ax::print(format, ##__VA_ARGS__) -# define AXLOGERROR(format, ...) ax::print(format, ##__VA_ARGS__) -# define AXLOGINFO(format, ...) \ - do \ - { \ - } while (0) -# define AXLOGWARN(...) __AXLOGWITHFUNCTION(__VA_ARGS__) - -# elif _AX_DEBUG > 1 -# define AXLOG(format, ...) ax::print(format, ##__VA_ARGS__) -# define AXLOGERROR(format, ...) ax::print(format, ##__VA_ARGS__) -# define AXLOGINFO(format, ...) ax::print(format, ##__VA_ARGS__) -# define AXLOGWARN(...) __AXLOGWITHFUNCTION(__VA_ARGS__) -# endif // _AX_DEBUG - -/** Lua engine debug */ -# if !defined(_AX_DEBUG) || _AX_DEBUG == 0 || AX_LUA_ENGINE_DEBUG == 0 -# define LUALOG(...) -# else -# define LUALOG(format, ...) ax::print(format, ##__VA_ARGS__) -# endif // Lua engine debug -#endif // end of debug group /// @} diff --git a/core/platform/android/Application-android.cpp b/core/platform/android/Application-android.cpp index 813ef6123ba8..c8657fcb7a1f 100644 --- a/core/platform/android/Application-android.cpp +++ b/core/platform/android/Application-android.cpp @@ -88,12 +88,6 @@ Application* Application::getInstance() return sm_pSharedApplication; } -// @deprecated Use getInstance() instead -Application* Application::sharedApplication() -{ - return Application::getInstance(); -} - const char* Application::getCurrentLanguageCode() { static char code[3] = {0}; diff --git a/core/platform/android/Application-android.h b/core/platform/android/Application-android.h index 11a57b9b5321..d287b4beea43 100644 --- a/core/platform/android/Application-android.h +++ b/core/platform/android/Application-android.h @@ -61,10 +61,7 @@ class AX_DLL Application : public ApplicationBase @return Current application instance pointer. */ static Application* getInstance(); -#ifndef AX_CORE_PROFILE - /** @deprecated Use getInstance() instead */ - AX_DEPRECATED(2.1) static Application* sharedApplication(); -#endif + /** @brief Get current language config @return Current language config diff --git a/core/platform/android/java/src/dev/axmol/lib/AxmolActivity.java b/core/platform/android/java/src/dev/axmol/lib/AxmolActivity.java index dd1d49138a45..ab4ab95bc063 100644 --- a/core/platform/android/java/src/dev/axmol/lib/AxmolActivity.java +++ b/core/platform/android/java/src/dev/axmol/lib/AxmolActivity.java @@ -25,13 +25,11 @@ of this software and associated documentation files (the "Software"), to deal ****************************************************************************/ package dev.axmol.lib; -import android.app.Activity; import android.app.KeyguardManager; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; -import android.graphics.PixelFormat; import android.media.AudioManager; import android.opengl.GLSurfaceView; import android.os.Build; @@ -46,14 +44,15 @@ of this software and associated documentation files (the "Software"), to deal import android.view.Window; import android.view.WindowManager; +import androidx.appcompat.app.AppCompatActivity; + import dev.axmol.lib.AxmolEngine.AxmolEngineListener; import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLDisplay; -import javax.microedition.khronos.egl.EGLContext; -public abstract class AxmolActivity extends Activity implements AxmolEngineListener { +public abstract class AxmolActivity extends AppCompatActivity implements AxmolEngineListener { // =========================================================== // Constants // =========================================================== diff --git a/core/platform/android/java/src/dev/axmol/lib/AxmolEngine.java b/core/platform/android/java/src/dev/axmol/lib/AxmolEngine.java index 500fcbac4103..da2e056b661d 100644 --- a/core/platform/android/java/src/dev/axmol/lib/AxmolEngine.java +++ b/core/platform/android/java/src/dev/axmol/lib/AxmolEngine.java @@ -30,7 +30,6 @@ of this software and associated documentation files (the "Software"), to deal import android.content.pm.PackageManager; import android.graphics.Rect; import android.media.AudioManager; -import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -59,6 +58,8 @@ of this software and associated documentation files (the "Software"), to deal import android.view.WindowInsets; import android.view.WindowManager; +import androidx.appcompat.app.AppCompatActivity; + import com.android.vending.expansion.zipfile.APKExpansionSupport; import com.android.vending.expansion.zipfile.ZipResourceFile; @@ -95,7 +96,7 @@ public class AxmolEngine { private static boolean sCompassEnabled; private static boolean sActivityVisible; private static String sPackageName; - private static Activity sActivity = null; + private static AppCompatActivity sActivity = null; private static AxmolEngineListener sAxmolEngineListener; private static Set onActivityResultListeners = new LinkedHashSet(); private static Vibrator sVibrateService = null; @@ -128,7 +129,7 @@ public void run() { } private static boolean sInited = false; - public static void init(final Activity activity) { + public static void init(final AppCompatActivity activity) { sActivity = activity; AxmolEngine.sAxmolEngineListener = (AxmolEngineListener)activity; if (!sInited) { @@ -202,7 +203,7 @@ public static ZipResourceFile getObbFile() { return sOBBFile; } - public static Activity getActivity() { + public static AppCompatActivity getActivity() { return sActivity; } diff --git a/core/platform/android/java/src/dev/axmol/lib/AxmolMediaEngine.java b/core/platform/android/java/src/dev/axmol/lib/AxmolMediaEngine.java index 58018a14c7eb..d4318a3dd9df 100644 --- a/core/platform/android/java/src/dev/axmol/lib/AxmolMediaEngine.java +++ b/core/platform/android/java/src/dev/axmol/lib/AxmolMediaEngine.java @@ -23,7 +23,6 @@ of this software and associated documentation files (the "Software"), to deal ****************************************************************************/ package dev.axmol.lib; -import android.app.Activity; import android.content.Context; import android.media.MediaCodecInfo; import android.media.MediaFormat; @@ -32,6 +31,7 @@ of this software and associated documentation files (the "Software"), to deal import android.util.Log; import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; import androidx.media3.common.Format; import androidx.media3.common.MediaItem; import androidx.media3.common.PlaybackException; @@ -127,7 +127,7 @@ public class AxmolMediaEngine extends DefaultRenderersFactory implements Player. public static native void nativeStoreCurrentTime(long nativeObj, double currentTime); - public static void setContext(Activity activity) { + public static void setContext(AppCompatActivity activity) { sContext = activity.getApplicationContext(); } diff --git a/core/platform/android/libaxmol/axutils.gradle b/core/platform/android/libaxmol/axutils.gradle deleted file mode 100644 index e876ef9790a5..000000000000 --- a/core/platform/android/libaxmol/axutils.gradle +++ /dev/null @@ -1,3 +0,0 @@ -// this file only for compatible purpose -def scriptDir = project(':libaxmol').projectDir -apply from: "${scriptDir}/axmol.gradle" \ No newline at end of file diff --git a/core/platform/android/libaxmol/build.gradle b/core/platform/android/libaxmol/build.gradle index 0d26ca018418..7ac3437f4bbf 100644 --- a/core/platform/android/libaxmol/build.gradle +++ b/core/platform/android/libaxmol/build.gradle @@ -55,6 +55,7 @@ dependencies { //api "androidx.media3:media3-exoplayer-ima:$media3_version" implementation "androidx.annotation:annotation:$annotation_ver" + implementation "androidx.appcompat:appcompat:$appcompat_ver" api ("com.google.guava:guava:$guava_ver-android") { // Exclude dependencies that are only used by Guava at compile time diff --git a/core/platform/linux/Application-linux.cpp b/core/platform/linux/Application-linux.cpp index 8716f476fa7a..c11736d31b05 100644 --- a/core/platform/linux/Application-linux.cpp +++ b/core/platform/linux/Application-linux.cpp @@ -132,12 +132,6 @@ Application* Application::getInstance() return sm_pSharedApplication; } -// @deprecated Use getInstance() instead -Application* Application::sharedApplication() -{ - return Application::getInstance(); -} - const char* Application::getCurrentLanguageCode() { static char code[3] = {0}; diff --git a/core/platform/linux/Application-linux.h b/core/platform/linux/Application-linux.h index 9066d1ec27d7..bfe998f33510 100644 --- a/core/platform/linux/Application-linux.h +++ b/core/platform/linux/Application-linux.h @@ -64,10 +64,7 @@ class Application : public ApplicationBase @return Current application instance pointer. */ static Application* getInstance(); -#ifndef AX_CORE_PROFILE - /** @deprecated Use getInstance() instead */ - AX_DEPRECATED(2.1) static Application* sharedApplication(); -#endif + /* override functions */ virtual LanguageType getCurrentLanguage() override; diff --git a/core/platform/wasm/Application-wasm.cpp b/core/platform/wasm/Application-wasm.cpp index eca761430ae2..332bfd861698 100644 --- a/core/platform/wasm/Application-wasm.cpp +++ b/core/platform/wasm/Application-wasm.cpp @@ -206,24 +206,6 @@ void Application::setAnimationInterval(float interval) s_animationInterval = static_cast(interval * NANOSECONDSPERSECOND); } -void Application::setResourceRootPath(const std::string& rootResDir) -{ - _resourceRootPath = rootResDir; - if (_resourceRootPath[_resourceRootPath.length() - 1] != '/') - { - _resourceRootPath += '/'; - } - FileUtils* pFileUtils = FileUtils::getInstance(); - std::vector searchPaths = pFileUtils->getSearchPaths(); - searchPaths.insert(searchPaths.begin(), _resourceRootPath); - pFileUtils->setSearchPaths(searchPaths); -} - -const std::string& Application::getResourceRootPath() -{ - return _resourceRootPath; -} - Application::Platform Application::getTargetPlatform() { return Platform::Wasm; @@ -250,12 +232,6 @@ Application* Application::getInstance() return sm_pSharedApplication; } -// @deprecated Use getInstance() instead -Application* Application::sharedApplication() -{ - return Application::getInstance(); -} - const char* Application::getCurrentLanguageCode() { static char code[3] = {0}; diff --git a/core/platform/wasm/Application-wasm.h b/core/platform/wasm/Application-wasm.h index a8e0f0603933..3ec9481d00f0 100644 --- a/core/platform/wasm/Application-wasm.h +++ b/core/platform/wasm/Application-wasm.h @@ -69,10 +69,7 @@ class Application : public ApplicationBase @return Current application instance pointer. */ static Application* getInstance(); -#ifndef AX_CORE_PROFILE - /** @deprecated Use getInstance() instead */ - AX_DEPRECATED(2.1) static Application* sharedApplication(); -#endif + /* override functions */ virtual LanguageType getCurrentLanguage() override; @@ -94,19 +91,6 @@ class Application : public ApplicationBase */ virtual bool openURL(std::string_view url) override; -#ifndef AX_CORE_PROFILE - /** - * Sets the Resource root path. - * @deprecated Please use FileUtils::getInstance()->setSearchPaths() instead. - */ - AX_DEPRECATED(2.1) void setResourceRootPath(const std::string& rootResDir); - - /** - * Gets the Resource root path. - * @deprecated Please use FileUtils::getInstance()->getSearchPaths() instead. - */ - AX_DEPRECATED(2.1) const std::string& getResourceRootPath(); -#endif /** @brief Get target platform */ diff --git a/core/renderer/CustomCommand.cpp b/core/renderer/CustomCommand.cpp index 0fc957218446..feea02fee987 100644 --- a/core/renderer/CustomCommand.cpp +++ b/core/renderer/CustomCommand.cpp @@ -40,6 +40,7 @@ CustomCommand::CustomCommand() CustomCommand::~CustomCommand() { AX_SAFE_RELEASE(_vertexBuffer); + AX_SAFE_RELEASE(_instanceBuffer); AX_SAFE_RELEASE(_indexBuffer); } @@ -142,7 +143,29 @@ void CustomCommand::createVertexBuffer(std::size_t vertexSize, std::size_t capac _vertexCapacity = capacity; _vertexDrawCount = capacity; - _vertexBuffer = backend::DriverBase::getInstance()->newBuffer(vertexSize * capacity, backend::BufferType::VERTEX, usage); + _vertexBuffer = + backend::DriverBase::getInstance()->newBuffer(vertexSize * capacity, backend::BufferType::VERTEX, usage); +} + +void CustomCommand::createInstanceBuffer(std::size_t vertexSize, int capacity, BufferUsage usage) +{ + AX_SAFE_RELEASE(_instanceBuffer); + _instanceBuffer = + backend::DriverBase::getInstance()->newBuffer(vertexSize * capacity, backend::BufferType::VERTEX, usage); + _instanceCapacity = capacity; + _instanceCount = capacity; +} + +void CustomCommand::setInstanceBuffer(backend::Buffer* instanceBuffer, int count) +{ + if (_instanceBuffer != instanceBuffer) + { + AX_SAFE_RELEASE(_instanceBuffer); + _instanceBuffer = instanceBuffer; + _instanceCount = count; + _instanceCapacity = count; + AX_SAFE_RETAIN(_instanceBuffer); + } } void CustomCommand::createIndexBuffer(IndexFormat format, std::size_t capacity, BufferUsage usage) @@ -154,7 +177,8 @@ void CustomCommand::createIndexBuffer(IndexFormat format, std::size_t capacity, _indexCapacity = capacity; _indexDrawCount = capacity; - _indexBuffer = backend::DriverBase::getInstance()->newBuffer(_indexSize * capacity, backend::BufferType::INDEX, usage); + _indexBuffer = + backend::DriverBase::getInstance()->newBuffer(_indexSize * capacity, backend::BufferType::INDEX, usage); } void CustomCommand::updateVertexBuffer(const void* data, std::size_t offset, std::size_t length) @@ -177,6 +201,9 @@ void CustomCommand::setVertexBuffer(backend::Buffer* vertexBuffer) AX_SAFE_RELEASE(_vertexBuffer); _vertexBuffer = vertexBuffer; AX_SAFE_RETAIN(_vertexBuffer); + + if (!_vertexBuffer) + _vertexCapacity = _vertexDrawCount = 0; } void CustomCommand::setIndexBuffer(backend::Buffer* indexBuffer, IndexFormat format) @@ -190,6 +217,9 @@ void CustomCommand::setIndexBuffer(backend::Buffer* indexBuffer, IndexFormat for _indexFormat = format; _indexSize = computeIndexSize(); + + if(!_indexBuffer) + _indexCapacity = _indexDrawCount = 0; } void CustomCommand::updateVertexBuffer(const void* data, std::size_t length) @@ -204,6 +234,12 @@ void CustomCommand::updateIndexBuffer(const void* data, std::size_t length) _indexBuffer->updateData(data, length); } +void CustomCommand::updateInstanceBuffer(const void* data, std::size_t length) +{ + assert(_instanceBuffer); + _instanceBuffer->updateData(data, length); +} + std::size_t CustomCommand::computeIndexSize() const { if (IndexFormat::U_SHORT == _indexFormat) @@ -212,4 +248,4 @@ std::size_t CustomCommand::computeIndexSize() const return sizeof(unsigned int); } -} +} // namespace ax diff --git a/core/renderer/CustomCommand.h b/core/renderer/CustomCommand.h index 81233ce9f28b..4ce40912caef 100644 --- a/core/renderer/CustomCommand.h +++ b/core/renderer/CustomCommand.h @@ -49,8 +49,9 @@ class AX_DLL CustomCommand : public RenderCommand enum class DrawType { ARRAY, + ARRAY_INSTANCED, ELEMENT, - ELEMENT_INSTANCE + ELEMENT_INSTANCED }; using PrimitiveType = backend::PrimitiveType; @@ -112,6 +113,8 @@ TODO: should remove it. every frame, otherwise use DYNAMIC. */ void createVertexBuffer(std::size_t vertexSize, std::size_t capacity, BufferUsage usage); + void createInstanceBuffer(std::size_t vertexSize, int capacity, BufferUsage usage); + /** Create an index buffer of the custom command. The buffer size is (indexSize * capacity). Index size is determined by format. If the buffer already exists, then it will delete the @@ -136,6 +139,9 @@ TODO: should remove it. @param length Specifies the size in bytes of the data store region being replaced. */ void updateIndexBuffer(const void* data, std::size_t length); + + void updateInstanceBuffer(const void* data, std::size_t length); + /** Update some or all contents of vertex buffer. @param data Specifies a pointer to the new data that will be copied into the data store. @@ -162,6 +168,8 @@ TODO: should remove it. */ inline std::size_t getIndexCapacity() const { return _indexCapacity; } + int getInstanceCapacity() const { return _instanceCapacity; } + inline void setDrawType(DrawType drawType) { _drawType = drawType; } inline DrawType getDrawType() const { return _drawType; } @@ -204,6 +212,8 @@ TODO: should remove it. _indexDrawCount = count; } + void setInstanceDrawInfo(int count) { _instanceCount = count; } + inline std::size_t getIndexDrawOffset() const { return _indexDrawOffset; } inline std::size_t getIndexDrawCount() const { return _indexDrawCount; } @@ -221,11 +231,8 @@ TODO: should remove it. */ void setAfterCallback(const CallBackFunc& after) { _afterCallback = after; } - void setInstanceBuffer(backend::Buffer* transformBuffer, int count) - { - _InstanceTransformBuffer = transformBuffer, _instanceCount = count; - } - backend::Buffer* getInstanceBuffer() const { return _InstanceTransformBuffer; } + void setInstanceBuffer(backend::Buffer* instanceBuffer, int count); + backend::Buffer* getInstanceBuffer() const { return _instanceBuffer; } int getInstanceCount() const { return _instanceCount; } const CallBackFunc& getBeforeCallback() { return _beforeCallback; } @@ -238,8 +245,9 @@ TODO: should remove it. backend::Buffer* _vertexBuffer = nullptr; backend::Buffer* _indexBuffer = nullptr; - backend::Buffer* _InstanceTransformBuffer = nullptr; - int _instanceCount = 0; + backend::Buffer* _instanceBuffer = nullptr; + int _instanceCount = 0; + int _instanceCapacity = 0; std::size_t _vertexDrawStart = 0; std::size_t _vertexDrawCount = 0; @@ -262,7 +270,7 @@ TODO: should remove it. CallBackFunc _afterCallback = nullptr; }; -} +} // namespace ax /** end of support group @} diff --git a/core/renderer/QuadCommand.cpp b/core/renderer/QuadCommand.cpp index a60af606eccc..e986f724b1f9 100644 --- a/core/renderer/QuadCommand.cpp +++ b/core/renderer/QuadCommand.cpp @@ -92,7 +92,7 @@ void QuadCommand::reIndex(int indicesCount) void QuadCommand::init(float globalOrder, Texture2D* texture, const BlendFunc& blendType, - V3F_C4B_T2F_Quad* quads, + V3F_T2F_C4B_Quad* quads, ssize_t quadCount, const Mat4& mv, uint32_t flags) diff --git a/core/renderer/QuadCommand.h b/core/renderer/QuadCommand.h index 2e69659731fd..be6419b91e93 100644 --- a/core/renderer/QuadCommand.h +++ b/core/renderer/QuadCommand.h @@ -64,7 +64,7 @@ class AX_DLL QuadCommand : public TrianglesCommand void init(float globalOrder, Texture2D* texture, const BlendFunc& blendType, - V3F_C4B_T2F_Quad* quads, + V3F_T2F_C4B_Quad* quads, ssize_t quadCount, const Mat4& mv, uint32_t flags); diff --git a/core/renderer/Renderer.cpp b/core/renderer/Renderer.cpp index bca8f55d8f0d..012723c6f275 100644 --- a/core/renderer/Renderer.cpp +++ b/core/renderer/Renderer.cpp @@ -21,7 +21,7 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. + THE SOFTWARE. ****************************************************************************/ #include "renderer/Renderer.h" @@ -729,26 +729,34 @@ void Renderer::drawCustomCommand(RenderCommand* command) _commandBuffer->setProgramState(cmd->getPipelineDescriptor().programState); auto drawType = cmd->getDrawType(); - if (CustomCommand::DrawType::ELEMENT == drawType) + switch (drawType) { + case CustomCommand::DrawType::ELEMENT: _commandBuffer->setIndexBuffer(cmd->getIndexBuffer()); _commandBuffer->drawElements(cmd->getPrimitiveType(), cmd->getIndexFormat(), cmd->getIndexDrawCount(), cmd->getIndexDrawOffset(), cmd->isWireframe()); _drawnVertices += cmd->getIndexDrawCount(); - } - else if (CustomCommand::DrawType::ELEMENT_INSTANCE == drawType) - { + break; + case CustomCommand::DrawType::ELEMENT_INSTANCED: _commandBuffer->setIndexBuffer(cmd->getIndexBuffer()); _commandBuffer->setInstanceBuffer(cmd->getInstanceBuffer()); _commandBuffer->drawElementsInstanced(cmd->getPrimitiveType(), cmd->getIndexFormat(), cmd->getIndexDrawCount(), cmd->getIndexDrawOffset(), cmd->getInstanceCount(), cmd->isWireframe()); _drawnVertices += cmd->getIndexDrawCount() * cmd->getInstanceCount(); - } - else - { + break; + case CustomCommand::DrawType::ARRAY: _commandBuffer->drawArrays(cmd->getPrimitiveType(), cmd->getVertexDrawStart(), cmd->getVertexDrawCount(), cmd->isWireframe()); _drawnVertices += cmd->getVertexDrawCount(); + break; + case CustomCommand::DrawType::ARRAY_INSTANCED: + _commandBuffer->setInstanceBuffer(cmd->getInstanceBuffer()); + _commandBuffer->drawArraysInstanced(cmd->getPrimitiveType(), cmd->getVertexDrawStart(), + cmd->getVertexDrawCount(), cmd->getInstanceCount(), + cmd->isWireframe()); + _drawnVertices += cmd->getVertexDrawCount() * cmd->getInstanceCount(); + break; + default:; } _drawnBatches++; endRenderPass(); @@ -860,7 +868,7 @@ void Renderer::endRenderPass() _commandBuffer->endRenderPass(); } -void Renderer::clear(ClearFlag flags, const Color4F& color, float depth, unsigned int stencil, float globalOrder) +void Renderer::clear(ClearFlag flags, const Color& color, float depth, unsigned int stencil, float globalOrder) { _clearFlag = flags; @@ -905,7 +913,7 @@ CallbackCommand* Renderer::nextCallbackCommand() return cmd; } -const Color4F& Renderer::getClearColor() const +const Color& Renderer::getClearColor() const { return _clearColor; } diff --git a/core/renderer/Renderer.h b/core/renderer/Renderer.h index 0e09f1609d40..087467efde57 100644 --- a/core/renderer/Renderer.h +++ b/core/renderer/Renderer.h @@ -209,13 +209,13 @@ class AX_DLL Renderer @depth The clear depth value. @stencil The clear stencil value. */ - void clear(ClearFlag flags, const Color4F& color, float depth, unsigned int stencil, float globalOrder); + void clear(ClearFlag flags, const Color& color, float depth, unsigned int stencil, float globalOrder); /** * Get color clear value. * @return Color clear value. */ - const Color4F& getClearColor() const; + const Color& getClearColor() const; /** * Get depth clear value. @@ -509,7 +509,7 @@ class AX_DLL Renderer std::vector _groupCommandPool; // for TrianglesCommand - V3F_C4B_T2F _verts[VBO_SIZE]; + V3F_T2F_C4B _verts[VBO_SIZE]; unsigned short _indices[INDEX_VBO_SIZE]; backend::Buffer* _vertexBuffer = nullptr; backend::Buffer* _indexBuffer = nullptr; @@ -556,7 +556,7 @@ class AX_DLL Renderer backend::RenderTarget* _offscreenRT = nullptr; - Color4F _clearColor = Color4F::BLACK; + Color _clearColor = Color::BLACK; ClearFlag _clearFlag; struct ScissorState diff --git a/core/renderer/TextureAtlas.cpp b/core/renderer/TextureAtlas.cpp index 074566e44c1a..0179ad1d8e56 100644 --- a/core/renderer/TextureAtlas.cpp +++ b/core/renderer/TextureAtlas.cpp @@ -87,14 +87,14 @@ void TextureAtlas::setTexture(Texture2D* var) _texture = var; } -V3F_C4B_T2F_Quad* TextureAtlas::getQuads() +V3F_T2F_C4B_Quad* TextureAtlas::getQuads() { // if someone accesses the quads directly, presume that changes will be made _dirty = true; return _quads; } -void TextureAtlas::setQuads(V3F_C4B_T2F_Quad* quads) +void TextureAtlas::setQuads(V3F_T2F_C4B_Quad* quads) { _quads = quads; } @@ -156,7 +156,7 @@ bool TextureAtlas::initWithTexture(Texture2D* texture, ssize_t capacity) // Re-initialization is not allowed AXASSERT(_quads == nullptr && _indices == nullptr, "_quads and _indices should be nullptr."); - _quads = (V3F_C4B_T2F_Quad*)malloc(_capacity * sizeof(V3F_C4B_T2F_Quad)); + _quads = (V3F_T2F_C4B_Quad*)malloc(_capacity * sizeof(V3F_T2F_C4B_Quad)); _indices = (uint16_t*)malloc(_capacity * 6 * sizeof(uint16_t)); if (!(_quads && _indices) && _capacity > 0) @@ -171,7 +171,7 @@ bool TextureAtlas::initWithTexture(Texture2D* texture, ssize_t capacity) return false; } - memset(_quads, 0, _capacity * sizeof(V3F_C4B_T2F_Quad)); + memset(_quads, 0, _capacity * sizeof(V3F_T2F_C4B_Quad)); memset(_indices, 0, _capacity * 6 * sizeof(uint16_t)); this->setupIndices(); @@ -206,7 +206,7 @@ void TextureAtlas::setupIndices() // TextureAtlas - Update, Insert, Move & Remove -void TextureAtlas::updateQuad(const V3F_C4B_T2F_Quad& quad, ssize_t index) +void TextureAtlas::updateQuad(const V3F_T2F_C4B_Quad& quad, ssize_t index) { AXASSERT(index >= 0 && index < _capacity, "updateQuadWithTexture: Invalid index"); @@ -217,7 +217,7 @@ void TextureAtlas::updateQuad(const V3F_C4B_T2F_Quad& quad, ssize_t index) _dirty = true; } -void TextureAtlas::insertQuad(const V3F_C4B_T2F_Quad& quad, ssize_t index) +void TextureAtlas::insertQuad(const V3F_T2F_C4B_Quad& quad, ssize_t index) { AXASSERT(index >= 0 && index < _capacity, "insertQuadWithTexture: Invalid index"); @@ -239,7 +239,7 @@ void TextureAtlas::insertQuad(const V3F_C4B_T2F_Quad& quad, ssize_t index) _dirty = true; } -void TextureAtlas::insertQuads(V3F_C4B_T2F_Quad* quads, ssize_t index, ssize_t amount) +void TextureAtlas::insertQuads(V3F_T2F_C4B_Quad* quads, ssize_t index, ssize_t amount) { AXASSERT(index >= 0 && amount >= 0 && index + amount <= _capacity, "insertQuadWithTexture: Invalid index + amount"); @@ -290,7 +290,7 @@ void TextureAtlas::insertQuadFromIndex(ssize_t oldIndex, ssize_t newIndex) } // texture coordinates - V3F_C4B_T2F_Quad quadsBackup = _quads[oldIndex]; + V3F_T2F_C4B_Quad quadsBackup = _quads[oldIndex]; memmove(&_quads[dst], &_quads[src], sizeof(_quads[0]) * howMany); _quads[newIndex] = quadsBackup; @@ -351,7 +351,7 @@ bool TextureAtlas::resizeCapacity(ssize_t newCapacity) _totalQuads = MIN(_totalQuads, newCapacity); _capacity = newCapacity; - V3F_C4B_T2F_Quad* tmpQuads = nullptr; + V3F_T2F_C4B_Quad* tmpQuads = nullptr; uint16_t* tmpIndices = nullptr; // when calling initWithTexture(fileName, 0) on bada device, calloc(0, 1) will fail and return nullptr, @@ -361,7 +361,7 @@ bool TextureAtlas::resizeCapacity(ssize_t newCapacity) ssize_t new_quads_size = _capacity * _quads_size; if (_quads == nullptr) { - tmpQuads = (V3F_C4B_T2F_Quad*)malloc(new_quads_size); + tmpQuads = (V3F_T2F_C4B_Quad*)malloc(new_quads_size); if (tmpQuads != nullptr) { memset(tmpQuads, 0, new_quads_size); @@ -369,7 +369,7 @@ bool TextureAtlas::resizeCapacity(ssize_t newCapacity) } else { - tmpQuads = (V3F_C4B_T2F_Quad*)realloc(_quads, new_quads_size); + tmpQuads = (V3F_T2F_C4B_Quad*)realloc(_quads, new_quads_size); if (tmpQuads != nullptr && _capacity > oldCapacity) { memset(tmpQuads + oldCapacity, 0, (_capacity - oldCapacity) * _quads_size); @@ -436,8 +436,8 @@ void TextureAtlas::moveQuadsFromIndex(ssize_t oldIndex, ssize_t amount, ssize_t return; } // create buffer - size_t quadSize = sizeof(V3F_C4B_T2F_Quad); - V3F_C4B_T2F_Quad* tempQuads = (V3F_C4B_T2F_Quad*)malloc(quadSize * amount); + size_t quadSize = sizeof(V3F_T2F_C4B_Quad); + V3F_T2F_C4B_Quad* tempQuads = (V3F_T2F_C4B_Quad*)malloc(quadSize * amount); memcpy(tempQuads, &_quads[oldIndex], quadSize * amount); if (newIndex < oldIndex) @@ -468,7 +468,7 @@ void TextureAtlas::moveQuadsFromIndex(ssize_t index, ssize_t newIndex) void TextureAtlas::fillWithEmptyQuadsFromIndex(ssize_t index, ssize_t amount) { AXASSERT(index >= 0 && amount >= 0, "values must be >= 0"); - V3F_C4B_T2F_Quad quad; + V3F_T2F_C4B_Quad quad; memset(&quad, 0, sizeof(quad)); auto to = index + amount; diff --git a/core/renderer/TextureAtlas.h b/core/renderer/TextureAtlas.h index b5bfd32d4d7a..ff287e3da01b 100644 --- a/core/renderer/TextureAtlas.h +++ b/core/renderer/TextureAtlas.h @@ -113,14 +113,14 @@ class AX_DLL TextureAtlas : public Object @param index Index must be between 0 and the atlas capacity - 1. @since v0.8 */ - void updateQuad(const V3F_C4B_T2F_Quad& quad, ssize_t index); + void updateQuad(const V3F_T2F_C4B_Quad& quad, ssize_t index); /** Inserts a Quad (texture, vertex and color) at a certain index. @param quad Quad that are going to be rendered. @param index Index must be between 0 and the atlas capacity - 1. @since v0.8 */ - void insertQuad(const V3F_C4B_T2F_Quad& quad, ssize_t index); + void insertQuad(const V3F_T2F_C4B_Quad& quad, ssize_t index); /** Inserts a c array of quads at a given index. @param quads Quad that are going to be rendered. @@ -129,7 +129,7 @@ class AX_DLL TextureAtlas : public Object @attention This method doesn't enlarge the array when amount + index > totalQuads. @since v1.1 */ - void insertQuads(V3F_C4B_T2F_Quad* quads, ssize_t index, ssize_t amount); + void insertQuads(V3F_T2F_C4B_Quad* quads, ssize_t index, ssize_t amount); /** Removes the quad that is located at a certain index and inserts it at a new index. This operation is faster than removing and inserting in a quad in 2 different steps. @@ -214,10 +214,10 @@ class AX_DLL TextureAtlas : public Object void setTexture(Texture2D* texture); /** Gets the quads that are going to be rendered. */ - V3F_C4B_T2F_Quad* getQuads(); + V3F_T2F_C4B_Quad* getQuads(); /** Sets the quads that are going to be rendered. */ - void setQuads(V3F_C4B_T2F_Quad* quads); + void setQuads(V3F_T2F_C4B_Quad* quads); inline unsigned short* getIndices() { return _indices; } @@ -239,7 +239,7 @@ class AX_DLL TextureAtlas : public Object /** Texture of the texture atlas */ Texture2D* _texture = nullptr; /** Quads that are going to be rendered */ - V3F_C4B_T2F_Quad* _quads = nullptr; + V3F_T2F_C4B_Quad* _quads = nullptr; #if AX_ENABLE_CACHE_TEXTURE_DATA EventListenerCustom* _rendererRecreatedListener = nullptr; diff --git a/core/renderer/TrianglesCommand.h b/core/renderer/TrianglesCommand.h index 7f297b2e95ad..21f6d95779e2 100644 --- a/core/renderer/TrianglesCommand.h +++ b/core/renderer/TrianglesCommand.h @@ -54,14 +54,14 @@ class AX_DLL TrianglesCommand : public RenderCommand /**The structure of Triangles. */ struct Triangles { - Triangles(V3F_C4B_T2F* _verts, unsigned short* _indices, unsigned int _vertCount, unsigned int _indexCount) + Triangles(V3F_T2F_C4B* _verts, unsigned short* _indices, unsigned int _vertCount, unsigned int _indexCount) : verts(_verts), indices(_indices), vertCount(_vertCount), indexCount(_indexCount) {} Triangles() {} /**Vertex data pointer.*/ - V3F_C4B_T2F* verts = nullptr; + V3F_T2F_C4B* verts = nullptr; /**Index data pointer.*/ unsigned short* indices = nullptr; /**The number of vertices.*/ @@ -98,7 +98,7 @@ class AX_DLL TrianglesCommand : public RenderCommand /**Get the index count of the triangles.*/ size_t getIndexCount() const { return _triangles.indexCount; } /**Get the vertex data pointer.*/ - const V3F_C4B_T2F* getVertices() const { return _triangles.verts; } + const V3F_T2F_C4B* getVertices() const { return _triangles.verts; } /**Get the index data pointer.*/ const unsigned short* getIndices() const { return _triangles.indices; } /**Get the model view matrix.*/ diff --git a/core/renderer/backend/CommandBuffer.h b/core/renderer/backend/CommandBuffer.h index 166a4d533176..096730de0324 100644 --- a/core/renderer/backend/CommandBuffer.h +++ b/core/renderer/backend/CommandBuffer.h @@ -165,6 +165,12 @@ class CommandBuffer : public ax::Object std::size_t count, bool wireframe = false) = 0; + virtual void drawArraysInstanced(PrimitiveType primitiveType, + std::size_t start, + std::size_t count, + int instanceCount, + bool wireframe = false) = 0; + /** * Draw primitives with an index list. * @param primitiveType The type of primitives that elements are assembled into. diff --git a/core/renderer/backend/Enums.h b/core/renderer/backend/Enums.h index 7cfea6b09483..e85abb56086c 100644 --- a/core/renderer/backend/Enums.h +++ b/core/renderer/backend/Enums.h @@ -71,7 +71,8 @@ enum class VertexFormat : uint32_t INT, USHORT4, USHORT2, - UBYTE4 + UBYTE4, + MAT4, }; /** @typedef backend::PixelFormat Possible texture pixel formats diff --git a/core/renderer/backend/Program.cpp b/core/renderer/backend/Program.cpp index 9b9f6c0172f6..a63b7fbebb40 100644 --- a/core/renderer/backend/Program.cpp +++ b/core/renderer/backend/Program.cpp @@ -30,12 +30,12 @@ NS_AX_BACKEND_BEGIN /* - * shader vertex layout setup functions + * shader vertex layout define functions */ struct VertexLayoutHelper { - static void setupDummy(Program*) {} - static void setupTexture(Program* program) + static void defineDummy(Program*) {} + static void defineTexture(Program* program) { auto vertexLayout = program->getVertexLayout(); @@ -51,7 +51,7 @@ struct VertexLayoutHelper vertexLayout->setStride(4 * sizeof(float)); } - static void setupSprite(Program* program) + static void definePosUvColor(Program* program) { auto vertexLayout = program->getVertexLayout(); @@ -62,74 +62,74 @@ struct VertexLayoutHelper /// a_texCoord vertexLayout->setAttrib(backend::ATTRIBUTE_NAME_TEXCOORD, program->getAttributeLocation(backend::Attribute::TEXCOORD), - backend::VertexFormat::FLOAT2, offsetof(V3F_C4B_T2F, texCoords), false); + backend::VertexFormat::FLOAT2, offsetof(V3F_T2F_C4F, texCoord), false); /// a_color vertexLayout->setAttrib(backend::ATTRIBUTE_NAME_COLOR, program->getAttributeLocation(backend::Attribute::COLOR), - backend::VertexFormat::UBYTE4, offsetof(V3F_C4B_T2F, colors), true); - vertexLayout->setStride(sizeof(V3F_C4B_T2F)); + backend::VertexFormat::FLOAT4, offsetof(V3F_T2F_C4F, color), false); + vertexLayout->setStride(sizeof(V3F_T2F_C4F)); } - static void setupDrawNode(Program* program) + static void defineSprite(Program* program) { auto vertexLayout = program->getVertexLayout(); + /// a_position vertexLayout->setAttrib(backend::ATTRIBUTE_NAME_POSITION, program->getAttributeLocation(backend::Attribute::POSITION), - backend::VertexFormat::FLOAT2, 0, false); - + backend::VertexFormat::FLOAT3, 0, false); + /// a_texCoord vertexLayout->setAttrib(backend::ATTRIBUTE_NAME_TEXCOORD, program->getAttributeLocation(backend::Attribute::TEXCOORD), - backend::VertexFormat::FLOAT2, offsetof(V2F_C4B_T2F, texCoords), false); + backend::VertexFormat::FLOAT2, offsetof(V3F_T2F_C4B, texCoord), false); + /// a_color vertexLayout->setAttrib(backend::ATTRIBUTE_NAME_COLOR, program->getAttributeLocation(backend::Attribute::COLOR), - backend::VertexFormat::UBYTE4, offsetof(V2F_C4B_T2F, colors), true); - - vertexLayout->setStride(sizeof(V2F_C4B_T2F)); + backend::VertexFormat::UBYTE4, offsetof(V3F_T2F_C4B, color), true); + vertexLayout->setStride(sizeof(V3F_T2F_C4B)); } - static void setupDrawNode3D(Program* program) + static void defineDrawNode(Program* program) { auto vertexLayout = program->getVertexLayout(); vertexLayout->setAttrib(backend::ATTRIBUTE_NAME_POSITION, program->getAttributeLocation(backend::Attribute::POSITION), - backend::VertexFormat::FLOAT3, 0, false); + backend::VertexFormat::FLOAT2, 0, false); + + vertexLayout->setAttrib(backend::ATTRIBUTE_NAME_TEXCOORD, + program->getAttributeLocation(backend::Attribute::TEXCOORD), + backend::VertexFormat::FLOAT2, offsetof(V2F_T2F_C4F, texCoord), false); vertexLayout->setAttrib(backend::ATTRIBUTE_NAME_COLOR, program->getAttributeLocation(backend::Attribute::COLOR), - backend::VertexFormat::UBYTE4, offsetof(V3F_C4B, colors), true); + backend::VertexFormat::FLOAT4, offsetof(V2F_T2F_C4F, color), true); - vertexLayout->setStride(sizeof(V3F_C4B)); + vertexLayout->setStride(sizeof(V2F_T2F_C4F)); } - static void setupSkyBox(Program* program) - { - auto vertexLayout = program->getVertexLayout(); - auto attrNameLoc = program->getAttributeLocation(backend::ATTRIBUTE_NAME_POSITION); - vertexLayout->setAttrib(backend::ATTRIBUTE_NAME_POSITION, attrNameLoc, - backend::VertexFormat::FLOAT3, 0, false); - vertexLayout->setStride(sizeof(Vec3)); - } - - static void setupPU3D(Program* program) + static void defineDrawNode3D(Program* program) { auto vertexLayout = program->getVertexLayout(); vertexLayout->setAttrib(backend::ATTRIBUTE_NAME_POSITION, program->getAttributeLocation(backend::Attribute::POSITION), - backend::VertexFormat::FLOAT3, offsetof(V3F_T2F_C4F, position), false); - - vertexLayout->setAttrib(backend::ATTRIBUTE_NAME_TEXCOORD, - program->getAttributeLocation(backend::Attribute::TEXCOORD), - backend::VertexFormat::FLOAT2, offsetof(V3F_T2F_C4F, uv), false); + backend::VertexFormat::FLOAT3, 0, false); vertexLayout->setAttrib(backend::ATTRIBUTE_NAME_COLOR, program->getAttributeLocation(backend::Attribute::COLOR), - backend::VertexFormat::FLOAT4, offsetof(V3F_T2F_C4F, color), false); + backend::VertexFormat::FLOAT4, offsetof(V3F_C4F, color), true); - vertexLayout->setStride(sizeof(V3F_T2F_C4F)); + vertexLayout->setStride(sizeof(V3F_C4F)); } - static void setupPos(Program* program) + static void defineSkyBox(Program* program) + { + auto vertexLayout = program->getVertexLayout(); + auto attrNameLoc = program->getAttributeLocation(backend::ATTRIBUTE_NAME_POSITION); + vertexLayout->setAttrib(backend::ATTRIBUTE_NAME_POSITION, attrNameLoc, backend::VertexFormat::FLOAT3, 0, false); + vertexLayout->setStride(sizeof(Vec3)); + } + + static void definePos(Program* program) { auto vertexLayout = program->getVertexLayout(); vertexLayout->setAttrib(backend::ATTRIBUTE_NAME_POSITION, @@ -138,18 +138,18 @@ struct VertexLayoutHelper vertexLayout->setStride(sizeof(Vec2)); } - static void setupPosColor(Program* program) + static void definePosColor(Program* program) { auto vertexLayout = program->getVertexLayout(); vertexLayout->setAttrib(backend::ATTRIBUTE_NAME_POSITION, program->getAttributeLocation(backend::Attribute::POSITION), backend::VertexFormat::FLOAT3, 0, false); vertexLayout->setAttrib(backend::ATTRIBUTE_NAME_COLOR, program->getAttributeLocation(backend::Attribute::COLOR), - backend::VertexFormat::FLOAT4, offsetof(V3F_C4F, colors), false); + backend::VertexFormat::FLOAT4, offsetof(V3F_C4F, color), false); vertexLayout->setStride(sizeof(V3F_C4F)); } - static void setupTerrain3D(Program* program) + static void defineTerrain3D(Program* program) { auto vertexLayout = program->getVertexLayout(); vertexLayout->setAttrib(backend::ATTRIBUTE_NAME_POSITION, @@ -163,26 +163,38 @@ struct VertexLayoutHelper backend::VertexFormat::FLOAT3, offsetof(V3F_T2F_N3F, normal), false); vertexLayout->setStride(sizeof(V3F_T2F_N3F)); } + + static void defineInstanced(Program* program) + { + auto vertexLayout = program->getVertexLayout(true); + vertexLayout->setAttrib(backend::ATTRIBUTE_NAME_INSTANCE, + program->getAttributeLocation(backend::Attribute::INSTANCE), + backend::VertexFormat::MAT4, 0, false); + vertexLayout->setStride(sizeof(Mat4)); + } }; -std::function Program::s_vertexLayoutSetupList[static_cast(VertexLayoutType::Count)] = { - VertexLayoutHelper::setupDummy, VertexLayoutHelper::setupPos, VertexLayoutHelper::setupTexture, - VertexLayoutHelper::setupSprite, VertexLayoutHelper::setupDrawNode, VertexLayoutHelper::setupDrawNode3D, - VertexLayoutHelper::setupSkyBox, VertexLayoutHelper::setupPU3D, VertexLayoutHelper::setupPosColor, - VertexLayoutHelper::setupTerrain3D}; +std::function Program::s_vertexLayoutDefineList[static_cast(VertexLayoutType::Count)] = { + VertexLayoutHelper::defineDummy, VertexLayoutHelper::definePos, VertexLayoutHelper::defineTexture, + VertexLayoutHelper::definePosUvColor, VertexLayoutHelper::defineSprite, VertexLayoutHelper::defineDrawNode, + VertexLayoutHelper::defineDrawNode3D, VertexLayoutHelper::defineSkyBox, VertexLayoutHelper::definePosColor, + VertexLayoutHelper::defineTerrain3D, VertexLayoutHelper::defineInstanced}; -Program::Program(std::string_view vs, std::string_view fs) - : _vertexShader(vs), _fragmentShader(fs), _vertexLayout(new VertexLayout()) -{} +Program::Program(std::string_view vs, std::string_view fs) : _vertexShader(vs), _fragmentShader(fs) +{ + _vertexLayout[0] = new VertexLayout(); + _vertexLayout[1] = new VertexLayout(); // instanced draw +} Program::~Program() { - delete _vertexLayout; + delete _vertexLayout[0]; + delete _vertexLayout[1]; } -void Program::setupVertexLayout(VertexLayoutType vlt) +void Program::defineVertexLayout(VertexLayoutType vlt) { if (vlt < VertexLayoutType::Count) - s_vertexLayoutSetupList[static_cast(vlt)](this); + s_vertexLayoutDefineList[static_cast(vlt)](this); } Program* Program::getBuiltinProgram(uint32_t type) @@ -192,7 +204,7 @@ Program* Program::getBuiltinProgram(uint32_t type) void Program::setProgramIds(uint32_t progType, uint64_t progId) { - _programType = progType; - _programId = progId; + _programType = progType; + _programId = progId; } NS_AX_BACKEND_END diff --git a/core/renderer/backend/Program.h b/core/renderer/backend/Program.h index 8a75abcbe833..41c6f875f8b6 100644 --- a/core/renderer/backend/Program.h +++ b/core/renderer/backend/Program.h @@ -51,13 +51,14 @@ enum class VertexLayoutType Unspec, // needs binding after program load Pos, // V2F Texture, // T2F - Sprite, // V3F_C4B_T2F posTexColor - DrawNode, // V2F_C4B_T2F - DrawNode3D, // V3F_C4B + PosUvColor, // V3F_T2F_C4F + Sprite, // V3F_T2F_C4B + DrawNode, // V2F_T2F_C4F + DrawNode3D, // V3F_C4F SkyBox, // V3F - PU3D, // V3F_C4B_T2F // same with sprite, TODO: reuse spriete - posColor, // V3F_C4B + posColor, // V3F_C4F Terrain3D, // V3F_T2F_V3F + Instanced, // builtin instanced vertex format for 3D transform Count }; @@ -138,9 +139,9 @@ class AX_DLL Program : public Object std::string_view getFragmentShader() const { return _fragmentShader; } /** - * Sets the program shared vertex layout type, see: VertexLayoutType + * Define the program shared vertex layout type, see: VertexLayoutType */ - void setupVertexLayout(VertexLayoutType vlt); + void defineVertexLayout(VertexLayoutType vlt); /** * Get engine built-in program type. @@ -167,7 +168,7 @@ class AX_DLL Program : public Object */ virtual const hlookup::string_map& getAllActiveUniformInfo(ShaderStage stage) const = 0; - inline VertexLayout* getVertexLayout() const { return _vertexLayout; } + inline VertexLayout* getVertexLayout(bool instanced = false) const { return !instanced ? _vertexLayout[0] : _vertexLayout[1]; } protected: @@ -211,13 +212,13 @@ class AX_DLL Program : public Object std::string _vertexShader; ///< Vertex shader. std::string _fragmentShader; ///< Fragment shader. - VertexLayout* _vertexLayout = nullptr; + VertexLayout* _vertexLayout[2] = {}; uint32_t _programType = ProgramType::CUSTOM_PROGRAM; ///< built-in program type, initial value is CUSTOM_PROGRAM. uint64_t _programId = 0; using VERTEX_LAYOUT_SETUP_FUNC = std::function; - static std::function s_vertexLayoutSetupList[static_cast(VertexLayoutType::Count)]; + static std::function s_vertexLayoutDefineList[static_cast(VertexLayoutType::Count)]; }; // end of _backend group diff --git a/core/renderer/backend/ProgramManager.cpp b/core/renderer/backend/ProgramManager.cpp index a7abce33cf10..a7ae12bb9148 100644 --- a/core/renderer/backend/ProgramManager.cpp +++ b/core/renderer/backend/ProgramManager.cpp @@ -131,7 +131,7 @@ bool ProgramManager::init() registerProgram(ProgramType::POSITION_TEXTURE_3D, positionTexture3D_vert, colorTexture_frag, VertexLayoutType::Unspec); registerProgram(ProgramType::POSITION_TEXTURE_3D_INSTANCE, positionTextureInstance_vert, colorTexture_frag, - VertexLayoutType::Unspec); + VertexLayoutType::Instanced); registerProgram(ProgramType::POSITION_3D, position_vert, color_frag, VertexLayoutType::Unspec); registerProgram(ProgramType::POSITION_NORMAL_3D, positionNormalTexture_vert, colorNormal_frag, VertexLayoutType::Unspec); @@ -140,8 +140,9 @@ bool ProgramManager::init() registerProgram(ProgramType::SKINPOSITION_BUMPEDNORMAL_TEXTURE_3D, skinPositionNormalTexture_vert_1, colorNormalTexture_frag_1, VertexLayoutType::Unspec); registerProgram(ProgramType::TERRAIN_3D, terrain_vert, terrain_frag, VertexLayoutType::Terrain3D); - registerProgram(ProgramType::PARTICLE_TEXTURE_3D, particle_vert, particleTexture_frag, VertexLayoutType::PU3D); - registerProgram(ProgramType::PARTICLE_COLOR_3D, particle_vert, particleColor_frag, VertexLayoutType::PU3D); + registerProgram(ProgramType::PARTICLE_TEXTURE_3D, particle_vert, particleTexture_frag, + VertexLayoutType::PosUvColor); + registerProgram(ProgramType::PARTICLE_COLOR_3D, particle_vert, particleColor_frag, VertexLayoutType::PosUvColor); registerProgram(ProgramType::QUAD_COLOR_2D, quadColor_vert, quadColor_frag, VertexLayoutType::Unspec); registerProgram(ProgramType::QUAD_TEXTURE_2D, quadTexture_vert, quadTexture_frag, VertexLayoutType::Unspec); registerProgram(ProgramType::HSV, positionTextureColor_vert, hsv_frag, VertexLayoutType::Sprite); @@ -216,7 +217,7 @@ Program* ProgramManager::loadProgram(std::string_view vsName, { program->setProgramIds(progType, progId); if (vlt < VertexLayoutType::Count) - program->setupVertexLayout(vlt); + program->defineVertexLayout(vlt); _cachedPrograms.emplace(progId, program); } return program; diff --git a/core/renderer/backend/ProgramManager.h b/core/renderer/backend/ProgramManager.h index e40a4273fa0f..a6aeefc7035f 100644 --- a/core/renderer/backend/ProgramManager.h +++ b/core/renderer/backend/ProgramManager.h @@ -103,23 +103,6 @@ class AX_DLL ProgramManager * Unload all program objects from cache. */ void unloadAllPrograms(); -#ifndef AX_CORE_PROFILE - /** - * Remove a program object from cache. - * @param program Specifies the program object to move. - */ - AX_DEPRECATED(2.1) void removeProgram(Program* prog) { unloadProgram(prog); } - - /** - * Remove all unused program objects from cache. - */ - AX_DEPRECATED(2.1) void removeUnusedProgram() { unloadUnusedPrograms(); } - - /** - * Remove all program objects from cache. - */ - AX_DEPRECATED(2.1) void removeAllPrograms() { unloadAllPrograms(); } -#endif protected: ProgramManager(); virtual ~ProgramManager(); diff --git a/core/renderer/backend/ProgramState.cpp b/core/renderer/backend/ProgramState.cpp index e3376aedfc86..4fda546c85ac 100644 --- a/core/renderer/backend/ProgramState.cpp +++ b/core/renderer/backend/ProgramState.cpp @@ -157,9 +157,11 @@ bool ProgramState::init(Program* program) AX_SAFE_RETAIN(program); _program = program; - _vertexLayout = program->getVertexLayout(); - _ownVertexLayout = false; - _vertexUniformBufferSize = _program->getUniformBufferSize(ShaderStage::VERTEX); + _vertexLayout = program->getVertexLayout(); + _vertexLayoutInstanced = program->getVertexLayout(true); + _ownVertexLayout = false; + _ownVertexLayoutInstanced = false; + _vertexUniformBufferSize = _program->getUniformBufferSize(ShaderStage::VERTEX); #ifdef AX_USE_METAL _fragmentUniformBufferSize = _program->getUniformBufferSize(ShaderStage::FRAGMENT); @@ -220,6 +222,9 @@ ProgramState::~ProgramState() if (_ownVertexLayout) AX_SAFE_DELETE(_vertexLayout); + + if (_ownVertexLayoutInstanced) + AX_SAFE_DELETE(_vertexLayoutInstanced); } ProgramState* ProgramState::clone() const @@ -232,6 +237,9 @@ ProgramState* ProgramState::clone() const cp->_ownVertexLayout = _ownVertexLayout; cp->_vertexLayout = !_ownVertexLayout ? _vertexLayout : new VertexLayout(*_vertexLayout); + cp->_ownVertexLayoutInstanced = _ownVertexLayout; + cp->_vertexLayoutInstanced = !_ownVertexLayout ? _vertexLayoutInstanced : new VertexLayout(*_vertexLayoutInstanced); + cp->_batchId = this->_batchId; cp->_isBatchable = this->_isBatchable; return cp; @@ -285,28 +293,10 @@ void ProgramState::setFragmentUniform(int location, const void* data, std::size_ } #endif -#ifndef AX_CORE_PROFILE -void ProgramState::setVertexAttrib(std::string_view name, - std::size_t index, - VertexFormat format, - std::size_t offset, - bool needToBeNormallized) -{ - ensureVertexLayoutMutable(); - - _vertexLayout->setAttrib(name, index, format, offset, needToBeNormallized); -} - -void ProgramState::setVertexStride(uint32_t stride) -{ - ensureVertexLayoutMutable(); - _vertexLayout->setStride(stride); -} -#endif void ProgramState::validateSharedVertexLayout(VertexLayoutType vlt) { if (!_ownVertexLayout && !_vertexLayout->isValid()) - _program->setupVertexLayout(vlt); + _program->defineVertexLayout(vlt); } void ProgramState::ensureVertexLayoutMutable() @@ -318,13 +308,24 @@ void ProgramState::ensureVertexLayoutMutable() } } -VertexLayout* ProgramState::getMutableVertexLayout() +VertexLayout* ProgramState::getMutableVertexLayout(bool instanced) { - if (_ownVertexLayout || !_vertexLayout->isValid()) - return _vertexLayout; + if (!instanced) + { + if (_ownVertexLayout || !_vertexLayout->isValid()) + return _vertexLayout; - _ownVertexLayout = true; - return _vertexLayout = new VertexLayout(); + _ownVertexLayout = true; + return _vertexLayout = new VertexLayout(); + } + else + { + if (_ownVertexLayoutInstanced || !_vertexLayoutInstanced->isValid()) + return _vertexLayoutInstanced; + + _ownVertexLayoutInstanced = true; + return _vertexLayoutInstanced = new VertexLayout(); + } } void ProgramState::setSharedVertexLayout(VertexLayout* vertexLayout) @@ -372,7 +373,8 @@ void ProgramState::setTextureArray(const backend::UniformLocation& uniformLocati setTextureArray(uniformLocation.vertStage.location, std::move(slots), std::move(textures), _vertexTextureInfos); #ifdef AX_USE_METAL if (uniformLocation.fragStage) - setTextureArray(uniformLocation.fragStage.location, std::move(slots), std::move(textures), _fragmentTextureInfos); + setTextureArray(uniformLocation.fragStage.location, std::move(slots), std::move(textures), + _fragmentTextureInfos); #endif } diff --git a/core/renderer/backend/ProgramState.h b/core/renderer/backend/ProgramState.h index 9fe88c9c6154..bb7a39f67cf2 100644 --- a/core/renderer/backend/ProgramState.h +++ b/core/renderer/backend/ProgramState.h @@ -290,15 +290,18 @@ class AX_DLL ProgramState : public Object */ void setParameterAutoBinding(std::string_view uniformName, std::string_view autoBinding); - inline const VertexLayout* getVertexLayout() const { return _vertexLayout; } + inline const VertexLayout* getVertexLayout(bool instanced = false) const + { + return !instanced ? _vertexLayout : _vertexLayoutInstanced; + } - VertexLayout* getMutableVertexLayout(); + VertexLayout* getMutableVertexLayout(bool instanced = false); void setSharedVertexLayout(VertexLayout* vertexLayout); /* - * Gets batch id of current program state, part of batch draw materialID - */ + * Gets batch id of current program state, part of batch draw materialID + */ uint64_t getBatchId() const { return _batchId; }; /* @@ -311,17 +314,7 @@ class AX_DLL ProgramState : public Object * so batch ID was set to -1 indicate batch was disabled */ void updateBatchId(); -#ifndef AX_CORE_PROFILE - /* - * Follow API is deprecated, use getMutableVertexLayout instead - */ - AX_DEPRECATED(2.1) void setVertexAttrib(std::string_view name, - std::size_t index, - VertexFormat format, - std::size_t offset, - bool needToBeNormallized); - AX_DEPRECATED(2.1) void setVertexStride(uint32_t stride); -#endif + /** Custom shader program's vertex layout maybe not setup * so engine specific render node(such as Sprite) should invoke this API when ProgramState changed */ @@ -401,8 +394,10 @@ class AX_DLL ProgramState : public Object std::unordered_map _autoBindings; static std::vector _customAutoBindingResolvers; - VertexLayout* _vertexLayout = nullptr; - bool _ownVertexLayout = false; + VertexLayout* _vertexLayout = nullptr; + VertexLayout* _vertexLayoutInstanced = nullptr; + bool _ownVertexLayout = false; + bool _ownVertexLayoutInstanced = false; uint64_t _batchId = -1; bool _isBatchable = false; diff --git a/core/renderer/backend/Types.cpp b/core/renderer/backend/Types.cpp index d13e554552b0..d20bc6130179 100644 --- a/core/renderer/backend/Types.cpp +++ b/core/renderer/backend/Types.cpp @@ -37,7 +37,7 @@ bool UniformLocation::operator==(const UniformLocation& other) const return vertStage == other.vertStage && fragStage == other.fragStage; } -std::size_t UniformLocation::operator()(const UniformLocation& uniform) const +std::size_t UniformLocation::operator()(const UniformLocation&) const { return size_t(vertStage.location) ^ size_t(fragStage.location << 16); } diff --git a/core/renderer/backend/Types.h b/core/renderer/backend/Types.h index f1433922e7e0..772f9e1880ae 100644 --- a/core/renderer/backend/Types.h +++ b/core/renderer/backend/Types.h @@ -95,7 +95,7 @@ struct UniformLocation operator bool() const { return vertStage or fragStage; } void reset() { vertStage = {}; fragStage = {}; } bool operator==(const UniformLocation& other) const; - std::size_t operator()(const UniformLocation& uniform) const; // used as a hash function + std::size_t operator()(const UniformLocation&) const; // used as a hash function }; struct AttributeBindInfo diff --git a/core/renderer/backend/VertexLayout.h b/core/renderer/backend/VertexLayout.h index ff2ed5658e62..1ff2afe3cb51 100644 --- a/core/renderer/backend/VertexLayout.h +++ b/core/renderer/backend/VertexLayout.h @@ -81,16 +81,6 @@ class AX_DLL VertexLayout VertexFormat format, std::size_t offset, bool needNormalized); -#ifndef AX_CORE_PROFILE - AX_DEPRECATED(2.1) void setAttribute(std::string_view name, - std::size_t index, - VertexFormat format, - std::size_t offset, - bool needNormalized) - { - setAttrib(name, index, format, offset, needNormalized); - } -#endif /** * Set stride of vertices. * @param stride Specifies the distance between the data of two vertices, in bytes. diff --git a/core/renderer/backend/metal/CommandBufferMTL.h b/core/renderer/backend/metal/CommandBufferMTL.h index 242c0aee9252..e5ca6480e0a5 100644 --- a/core/renderer/backend/metal/CommandBufferMTL.h +++ b/core/renderer/backend/metal/CommandBufferMTL.h @@ -150,6 +150,11 @@ class CommandBufferMTL : public CommandBuffer * TODO: Implement a wireframe mode for METAL devices. Refer to: https://forums.ogre3d.org/viewtopic.php?t=95089 */ void drawArrays(PrimitiveType primitiveType, std::size_t start, std::size_t count, bool wireframe) override; + void drawArraysInstanced(PrimitiveType primitiveType, + std::size_t start, + std::size_t count, + int instanceCount, + bool wireframe = false) override; /** * Draw primitives with an index list. diff --git a/core/renderer/backend/metal/CommandBufferMTL.mm b/core/renderer/backend/metal/CommandBufferMTL.mm index 0520cbb32e5d..2bc96fdee88f 100644 --- a/core/renderer/backend/metal/CommandBufferMTL.mm +++ b/core/renderer/backend/metal/CommandBufferMTL.mm @@ -300,6 +300,12 @@ inline int clamp(int value, int min, int max) [_mtlRenderEncoder drawPrimitives:toMTLPrimitive(primitiveType) vertexStart:start vertexCount:count]; } +void CommandBufferMTL::drawArraysInstanced(PrimitiveType primitiveType, std::size_t start, std::size_t count, int instanceCount, bool wireframe /* unused */) +{ + prepareDrawing(); + [_mtlRenderEncoder drawPrimitives:toMTLPrimitive(primitiveType) vertexStart:start vertexCount:count instanceCount:instanceCount]; +} + void CommandBufferMTL::drawElements(PrimitiveType primitiveType, IndexFormat indexType, std::size_t count, @@ -308,10 +314,10 @@ inline int clamp(int value, int min, int max) { prepareDrawing(); [_mtlRenderEncoder drawIndexedPrimitives:toMTLPrimitive(primitiveType) - indexCount:count - indexType:toMTLIndexType(indexType) - indexBuffer:_mtlIndexBuffer - indexBufferOffset:offset]; + indexCount:count + indexType:toMTLIndexType(indexType) + indexBuffer:_mtlIndexBuffer + indexBufferOffset:offset]; } void CommandBufferMTL::drawElementsInstanced(PrimitiveType primitiveType, @@ -323,11 +329,11 @@ inline int clamp(int value, int min, int max) { prepareDrawing(); [_mtlRenderEncoder drawIndexedPrimitives:toMTLPrimitive(primitiveType) - indexCount:count - indexType:toMTLIndexType(indexType) - indexBuffer:_mtlIndexBuffer - indexBufferOffset:offset - instanceCount:instanceCount]; + indexCount:count + indexType:toMTLIndexType(indexType) + indexBuffer:_mtlIndexBuffer + indexBufferOffset:offset + instanceCount:instanceCount]; } void CommandBufferMTL::endRenderPass() diff --git a/core/renderer/backend/opengl/CommandBufferGL.cpp b/core/renderer/backend/opengl/CommandBufferGL.cpp index 5a715651c300..f70c2647e8a8 100644 --- a/core/renderer/backend/opengl/CommandBufferGL.cpp +++ b/core/renderer/backend/opengl/CommandBufferGL.cpp @@ -194,12 +194,12 @@ void CommandBufferGL::setIndexBuffer(Buffer* buffer) void CommandBufferGL::setInstanceBuffer(Buffer* buffer) { assert(buffer != nullptr); - if (buffer == nullptr || _instanceTransformBuffer == buffer) + if (buffer == nullptr || _instanceBuffer == buffer) return; buffer->retain(); - AX_SAFE_RELEASE(_instanceTransformBuffer); - _instanceTransformBuffer = static_cast(buffer); + AX_SAFE_RELEASE(_instanceBuffer); + _instanceBuffer = static_cast(buffer); } void CommandBufferGL::setVertexBuffer(Buffer* buffer) @@ -238,6 +238,24 @@ void CommandBufferGL::drawArrays(PrimitiveType primitiveType, std::size_t start, cleanResources(); } +void CommandBufferGL::drawArraysInstanced(PrimitiveType primitiveType, std::size_t start, std::size_t count, int instanceCount, bool wireframe) +{ + prepareDrawing(); +#if !AX_GLES_PROFILE // glPolygonMode is only supported in Desktop OpenGL + if (wireframe) + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); +#else + if (wireframe) + primitiveType = PrimitiveType::LINE; +#endif + glDrawArraysInstanced(UtilsGL::toGLPrimitiveType(primitiveType), start, count, instanceCount); +#if !AX_GLES_PROFILE // glPolygonMode is only supported in Desktop OpenGL + if (wireframe) + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); +#endif + cleanResources(); +} + void CommandBufferGL::drawElements(PrimitiveType primitiveType, IndexFormat indexType, std::size_t count, @@ -293,7 +311,7 @@ void CommandBufferGL::endRenderPass() { AX_SAFE_RELEASE_NULL(_indexBuffer); AX_SAFE_RELEASE_NULL(_vertexBuffer); - AX_SAFE_RELEASE_NULL(_instanceTransformBuffer); + AX_SAFE_RELEASE_NULL(_instanceBuffer); } void CommandBufferGL::endFrame() {} @@ -306,7 +324,6 @@ void CommandBufferGL::prepareDrawing() const uint32_t usedBits{0}; bindVertexBuffer(usedBits); - bindInstanceBuffer(program, usedBits); __gl->disableUnusedVertexAttribs(usedBits); bindUniforms(program); @@ -327,47 +344,64 @@ void CommandBufferGL::prepareDrawing() const void CommandBufferGL::bindVertexBuffer(uint32_t& usedBits) const { // Bind vertex buffers and set the attributes. - auto vertexLayout = _programState->getVertexLayout(); + { + auto vertexLayout = _programState->getVertexLayout(); - const auto& attributes = vertexLayout->getAttributes(); - if (!vertexLayout->isValid()) - return; + const auto& attributes = vertexLayout->getAttributes(); + if (!vertexLayout->isValid()) + return; - // Bind VAO, engine share 1 VAO for all vertexLayouts aka vfmts - // optimize proposal: create VAO per vertexLayout, just need bind VAO - __gl->bindBuffer(BufferType::ARRAY_BUFFER, _vertexBuffer->getHandler()); + // Bind VAO, engine share 1 VAO for all vertexLayouts aka vfmts + // optimize proposal: create VAO per vertexLayout, just need bind VAO + __gl->bindBuffer(BufferType::ARRAY_BUFFER, _vertexBuffer->getHandler()); - for (const auto& attributeInfo : attributes) - { - const auto& attribute = attributeInfo.second; - __gl->enableVertexAttribArray(attribute.index); - glVertexAttribPointer(attribute.index, UtilsGL::getGLAttributeSize(attribute.format), - UtilsGL::toGLAttributeType(attribute.format), attribute.needToBeNormallized, - vertexLayout->getStride(), (GLvoid*)attribute.offset); - // non-instance attrib not use divisor, so clear to 0 - __gl->clearVertexAttribDivisor(attribute.index); - usedBits |= (1 << attribute.index); + for (const auto& attributeInfo : attributes) + { + const auto& attribute = attributeInfo.second; + __gl->enableVertexAttribArray(attribute.index); + glVertexAttribPointer(attribute.index, UtilsGL::getGLAttributeSize(attribute.format), + UtilsGL::toGLAttributeType(attribute.format), attribute.needToBeNormallized, + vertexLayout->getStride(), (GLvoid*)attribute.offset); + // non-instance attrib not use divisor, so clear to 0 + __gl->clearVertexAttribDivisor(attribute.index); + usedBits |= (1 << attribute.index); + } } -} -void CommandBufferGL::bindInstanceBuffer(ProgramGL* program, uint32_t& usedBits) const -{ - // if we have an instance transform buffer pointer then we must be rendering in instance mode. - if (_instanceTransformBuffer) + // Bind instanced vertex buffer + // if we have an instance buffer pointer then we must be rendering in instance mode. + if (_instanceBuffer) { - auto instanceLoc = program->getAttributeLocation(Attribute::INSTANCE); - if (instanceLoc != -1) - { - __gl->bindBuffer(BufferType::ARRAY_BUFFER, _instanceTransformBuffer->getHandler()); + auto vertexLayout = _programState->getVertexLayout(true); + const auto& attributes = vertexLayout->getAttributes(); + if (!vertexLayout->isValid()) + return; + + __gl->bindBuffer(BufferType::ARRAY_BUFFER, _instanceBuffer->getHandler()); - for (auto i = 0; i < 4; ++i) + for (const auto& attributeInfo : attributes) + { + const auto& attribute = attributeInfo.second; + switch (attribute.format) { - auto elementLoc = instanceLoc + i; - __gl->enableVertexAttribArray(elementLoc); - glVertexAttribPointer(elementLoc, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 16, - (void*)(sizeof(float) * 4 * i)); - __gl->setVertexAttribDivisor(elementLoc); - usedBits |= (1 << elementLoc); + case VertexFormat::MAT4: + for (auto i = 0; i < 4; ++i) + { + auto elementLoc = attribute.index + i; + __gl->enableVertexAttribArray(elementLoc); + glVertexAttribPointer(elementLoc, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 16, + (void*)(sizeof(float) * 4 * i)); + __gl->setVertexAttribDivisor(elementLoc); + usedBits |= (1 << elementLoc); + } + break; + default: + __gl->enableVertexAttribArray(attribute.index); + glVertexAttribPointer(attribute.index, UtilsGL::getGLAttributeSize(attribute.format), + UtilsGL::toGLAttributeType(attribute.format), attribute.needToBeNormallized, + vertexLayout->getStride(), (GLvoid*)attribute.offset); + __gl->setVertexAttribDivisor(attribute.index); + usedBits |= (1 << attribute.index); } } } diff --git a/core/renderer/backend/opengl/CommandBufferGL.h b/core/renderer/backend/opengl/CommandBufferGL.h index 6e4773727fb5..1d66b536b9ef 100644 --- a/core/renderer/backend/opengl/CommandBufferGL.h +++ b/core/renderer/backend/opengl/CommandBufferGL.h @@ -147,6 +147,12 @@ class CommandBufferGL : public CommandBuffer */ void drawArrays(PrimitiveType primitiveType, std::size_t start, std::size_t count, bool wireframe = false) override; + void drawArraysInstanced(PrimitiveType primitiveType, + std::size_t start, + std::size_t count, + int instanceCount, + bool wireframe = false) override; + /** * Draw primitives with an index list. * @param primitiveType The type of primitives that elements are assembled into. @@ -157,10 +163,10 @@ class CommandBufferGL : public CommandBuffer * @see `drawArrays(PrimitiveType primitiveType, unsigned int start, unsigned int count)` */ void drawElements(PrimitiveType primitiveType, - IndexFormat indexType, - std::size_t count, - std::size_t offset, - bool wireframe = false) override; + IndexFormat indexType, + std::size_t count, + std::size_t offset, + bool wireframe = false) override; /** * Draw primitives with an index list instanced. @@ -174,11 +180,11 @@ class CommandBufferGL : public CommandBuffer * @see `drawArrays(PrimitiveType primitiveType, unsigned int start, unsigned int count)` */ void drawElementsInstanced(PrimitiveType primitiveType, - IndexFormat indexType, - std::size_t count, - std::size_t offset, - int instanceCount, - bool wireframe = false) override; + IndexFormat indexType, + std::size_t count, + std::size_t offset, + int instanceCount, + bool wireframe = false) override; /** * Do some resources release. @@ -204,10 +210,9 @@ class CommandBufferGL : public CommandBuffer */ void readPixels(RenderTarget* rt, std::function callback) override; - /** - * For internal use only - */ + * For internal use only + */ void readPixels(RenderTarget* rt, int x, int y, @@ -218,22 +223,20 @@ class CommandBufferGL : public CommandBuffer PixelBufferDescriptor& pbd); protected: - void prepareDrawing() const; void bindVertexBuffer(uint32_t& usedBits) const; - virtual void bindInstanceBuffer(ProgramGL* program, uint32_t& usedBits) const; void bindUniforms(ProgramGL* program) const; void cleanResources(); BufferGL* _vertexBuffer = nullptr; ProgramState* _programState = nullptr; BufferGL* _indexBuffer = nullptr; - BufferGL* _instanceTransformBuffer = nullptr; + BufferGL* _instanceBuffer = nullptr; RenderPipelineGL* _renderPipeline = nullptr; CullMode _cullMode = CullMode::NONE; DepthStencilStateGL* _depthStencilStateGL = nullptr; Viewport _viewPort; - GLboolean _alphaTestEnabled = false; + GLboolean _alphaTestEnabled = false; #if AX_ENABLE_CACHE_TEXTURE_DATA EventListenerCustom* _backToForegroundListener = nullptr; diff --git a/core/renderer/backend/opengl/CommandBufferGLES2.cpp b/core/renderer/backend/opengl/CommandBufferGLES2.cpp index de756044bbef..0b614d68c10b 100644 --- a/core/renderer/backend/opengl/CommandBufferGLES2.cpp +++ b/core/renderer/backend/opengl/CommandBufferGLES2.cpp @@ -59,14 +59,6 @@ void CommandBufferGLES2::drawElementsInstanced(PrimitiveType primitiveType, CommandBufferGL::drawElementsInstanced(primitiveType, indexType, count, offset, instanceCount, wireframe); } -void CommandBufferGLES2::bindInstanceBuffer(ProgramGL* program, uint32_t& usedBits) const -{ - - if (!glDrawElementsInstanced) - return; - CommandBufferGL::bindInstanceBuffer(program, usedBits); -} - NS_AX_BACKEND_END #endif diff --git a/core/renderer/backend/opengl/CommandBufferGLES2.h b/core/renderer/backend/opengl/CommandBufferGLES2.h index 0afcb3243d5f..b5778cb345c7 100644 --- a/core/renderer/backend/opengl/CommandBufferGLES2.h +++ b/core/renderer/backend/opengl/CommandBufferGLES2.h @@ -50,9 +50,6 @@ class CommandBufferGLES2 : public CommandBufferGL std::size_t offset, int instanceCount, bool wireframe = false) override; - - void bindInstanceBuffer(ProgramGL* program, uint32_t& usedBits) const override; - }; // end of _opengl group diff --git a/core/ui/UIButton.cpp b/core/ui/UIButton.cpp index 16024400a6ad..77fcd8a9a55e 100644 --- a/core/ui/UIButton.cpp +++ b/core/ui/UIButton.cpp @@ -752,7 +752,7 @@ std::string_view Button::getTitleText() const void Button::setTitleColor(const Color3B& color) { createTitleRendererIfNull(); - _titleRenderer->setTextColor(Color4B(color)); + _titleRenderer->setTextColor(Color32(color)); } Color3B Button::getTitleColor() const diff --git a/core/ui/UIEditBox/UIEditBox.cpp b/core/ui/UIEditBox/UIEditBox.cpp index 56ad3e0704d7..751546688f5e 100644 --- a/core/ui/UIEditBox/UIEditBox.cpp +++ b/core/ui/UIEditBox/UIEditBox.cpp @@ -505,10 +505,10 @@ int EditBox::getFontSize() const } void EditBox::setFontColor(const Color3B& color) { - setFontColor(Color4B(color)); + setFontColor(Color32(color)); } -void EditBox::setFontColor(const Color4B& color) +void EditBox::setFontColor(const Color32& color) { if (_editBoxImpl != nullptr) { @@ -516,13 +516,13 @@ void EditBox::setFontColor(const Color4B& color) } } -const Color4B& EditBox::getFontColor() const +const Color32& EditBox::getFontColor() const { if (_editBoxImpl != nullptr) { return _editBoxImpl->getFontColor(); } - return Color4B::WHITE; + return Color32::WHITE; } void EditBox::setPlaceholderFont(const char* pFontName, int fontSize) @@ -574,10 +574,10 @@ int EditBox::getPlaceholderFontSize() const void EditBox::setPlaceholderFontColor(const Color3B& color) { - setPlaceholderFontColor(Color4B(color)); + setPlaceholderFontColor(Color32(color)); } -void EditBox::setPlaceholderFontColor(const Color4B& color) +void EditBox::setPlaceholderFontColor(const Color32& color) { if (_editBoxImpl != nullptr) { @@ -585,13 +585,13 @@ void EditBox::setPlaceholderFontColor(const Color4B& color) } } -const Color4B& EditBox::getPlaceholderFontColor() const +const Color32& EditBox::getPlaceholderFontColor() const { if (_editBoxImpl != nullptr) { return _editBoxImpl->getPlaceholderFontColor(); } - return Color4B::GRAY; + return Color32::GRAY; } void EditBox::setPlaceHolder(const char* pText) diff --git a/core/ui/UIEditBox/UIEditBox.h b/core/ui/UIEditBox/UIEditBox.h index c53898b09873..9a08e8c807ec 100644 --- a/core/ui/UIEditBox/UIEditBox.h +++ b/core/ui/UIEditBox/UIEditBox.h @@ -474,12 +474,12 @@ class AX_GUI_DLL EditBox : public Widget, public IMEDelegate * Set the font color of the widget's text. */ void setFontColor(const Color3B& color); - void setFontColor(const Color4B& color); + void setFontColor(const Color32& color); /** * Get the font color of the widget's text. */ - const Color4B& getFontColor() const; + const Color32& getFontColor() const; /** * Set the placeholder's font. Only system font is allowed. @@ -520,12 +520,12 @@ class AX_GUI_DLL EditBox : public Widget, public IMEDelegate /** * Set the font color of the placeholder text when the edit box is empty. */ - void setPlaceholderFontColor(const Color4B& color); + void setPlaceholderFontColor(const Color32& color); /** * Get the font color of the placeholder text when the edit box is empty. */ - const Color4B& getPlaceholderFontColor() const; + const Color32& getPlaceholderFontColor() const; /** * Set a text in the edit box that acts as a placeholder when an diff --git a/core/ui/UIEditBox/UIEditBoxImpl-android.cpp b/core/ui/UIEditBox/UIEditBoxImpl-android.cpp index 8d9f4c6e4202..3620d73619ae 100644 --- a/core/ui/UIEditBox/UIEditBoxImpl-android.cpp +++ b/core/ui/UIEditBox/UIEditBoxImpl-android.cpp @@ -135,7 +135,7 @@ void EditBoxImplAndroid::setNativeFont(const char* pFontName, int fontSize) (float)fontSize * glView->getScaleX()); } -void EditBoxImplAndroid::setNativeFontColor(const Color4B& color) +void EditBoxImplAndroid::setNativeFontColor(const Color32& color) { JniHelper::callStaticVoidMethod(editBoxClassName, "setFontColor", _editBoxIndex, (int)color.r, (int)color.g, (int)color.b, (int)color.a); @@ -146,7 +146,7 @@ void EditBoxImplAndroid::setNativePlaceholderFont(const char* pFontName, int fon AXLOGD("Warning! You can't change Android Hint fontName and fontSize"); } -void EditBoxImplAndroid::setNativePlaceholderFontColor(const Color4B& color) +void EditBoxImplAndroid::setNativePlaceholderFontColor(const Color32& color) { JniHelper::callStaticVoidMethod(editBoxClassName, "setPlaceHolderTextColor", _editBoxIndex, (int)color.r, (int)color.g, (int)color.b, (int)color.a); diff --git a/core/ui/UIEditBox/UIEditBoxImpl-android.h b/core/ui/UIEditBox/UIEditBoxImpl-android.h index 49aeef883ae1..52547e58e789 100644 --- a/core/ui/UIEditBox/UIEditBoxImpl-android.h +++ b/core/ui/UIEditBox/UIEditBoxImpl-android.h @@ -60,9 +60,9 @@ class EditBoxImplAndroid : public EditBoxImplCommon virtual bool isEditing() override; virtual void createNativeControl(const Rect& frame) override; virtual void setNativeFont(const char* pFontName, int fontSize) override; - virtual void setNativeFontColor(const Color4B& color) override; + virtual void setNativeFontColor(const Color32& color) override; virtual void setNativePlaceholderFont(const char* pFontName, int fontSize) override; - virtual void setNativePlaceholderFontColor(const Color4B& color) override; + virtual void setNativePlaceholderFontColor(const Color32& color) override; virtual void setNativeInputMode(EditBox::InputMode inputMode) override; virtual void setNativeInputFlag(EditBox::InputFlag inputFlag) override; virtual void setNativeReturnType(EditBox::KeyboardReturnType returnType) override; diff --git a/core/ui/UIEditBox/UIEditBoxImpl-common.cpp b/core/ui/UIEditBox/UIEditBoxImpl-common.cpp index 488468147d91..b353edcce43f 100644 --- a/core/ui/UIEditBox/UIEditBoxImpl-common.cpp +++ b/core/ui/UIEditBox/UIEditBoxImpl-common.cpp @@ -101,7 +101,7 @@ void EditBoxImplCommon::initInactiveLabels(const Vec2& size) _labelPlaceHolder = Label::create(); _labelPlaceHolder->setAnchorPoint(Vec2(0.0f, 1.0f)); - _labelPlaceHolder->setTextColor(Color4B::GRAY); + _labelPlaceHolder->setTextColor(Color32::GRAY); _labelPlaceHolder->enableWrap(false); _labelPlaceHolder->setGlobalZOrder(_editBox->getGlobalZOrder()); _editBox->addChild(_labelPlaceHolder, kLabelZOrder); @@ -182,7 +182,7 @@ void EditBoxImplCommon::setFont(const char* pFontName, int fontSize) } } -void EditBoxImplCommon::setFontColor(const Color4B& color) +void EditBoxImplCommon::setFontColor(const Color32& color) { _colText = color; this->setNativeFontColor(color); @@ -213,7 +213,7 @@ void EditBoxImplCommon::setPlaceholderFont(const char* pFontName, int fontSize) } } -void EditBoxImplCommon::setPlaceholderFontColor(const Color4B& color) +void EditBoxImplCommon::setPlaceholderFontColor(const Color32& color) { _colPlaceHolder = color; this->setNativePlaceholderFontColor(color); diff --git a/core/ui/UIEditBox/UIEditBoxImpl-common.h b/core/ui/UIEditBox/UIEditBoxImpl-common.h index de38ac86805f..b19f01a20852 100644 --- a/core/ui/UIEditBox/UIEditBoxImpl-common.h +++ b/core/ui/UIEditBox/UIEditBoxImpl-common.h @@ -58,9 +58,9 @@ class AX_GUI_DLL EditBoxImplCommon : public EditBoxImpl virtual bool initWithSize(const Size& size) override; virtual void setFont(const char* pFontName, int fontSize) override; - virtual void setFontColor(const Color4B& color) override; + virtual void setFontColor(const Color32& color) override; virtual void setPlaceholderFont(const char* pFontName, int fontSize) override; - virtual void setPlaceholderFontColor(const Color4B& color) override; + virtual void setPlaceholderFontColor(const Color32& color) override; virtual void setInputMode(EditBox::InputMode inputMode) override; virtual void setInputFlag(EditBox::InputFlag inputFlag) override; virtual void setReturnType(EditBox::KeyboardReturnType returnType) override; @@ -77,11 +77,11 @@ class AX_GUI_DLL EditBoxImplCommon : public EditBoxImpl virtual const char* getFontName() override { return _fontName.c_str(); } virtual int getFontSize() override { return _fontSize; } - virtual const Color4B& getFontColor() override { return _colText; } + virtual const Color32& getFontColor() override { return _colText; } virtual const char* getPlaceholderFontName() override { return _placeholderFontName.c_str(); } virtual int getPlaceholderFontSize() override { return _placeholderFontSize; } - virtual const Color4B& getPlaceholderFontColor() override { return _colPlaceHolder; } + virtual const Color32& getPlaceholderFontColor() override { return _colPlaceHolder; } virtual EditBox::InputMode getInputMode() override { return _editBoxInputMode; } virtual EditBox::InputFlag getInputFlag() override { return _editBoxInputFlag; } @@ -119,9 +119,9 @@ class AX_GUI_DLL EditBoxImplCommon : public EditBoxImpl virtual bool isEditing() override = 0; virtual void createNativeControl(const Rect& frame) = 0; virtual void setNativeFont(const char* pFontName, int fontSize) = 0; - virtual void setNativeFontColor(const Color4B& color) = 0; + virtual void setNativeFontColor(const Color32& color) = 0; virtual void setNativePlaceholderFont(const char* pFontName, int fontSize) = 0; - virtual void setNativePlaceholderFontColor(const Color4B& color) = 0; + virtual void setNativePlaceholderFontColor(const Color32& color) = 0; virtual void setNativeInputMode(EditBox::InputMode inputMode) = 0; virtual void setNativeInputFlag(EditBox::InputFlag inputFlag) = 0; virtual void setNativeReturnType(EditBox::KeyboardReturnType returnType) = 0; @@ -158,8 +158,8 @@ class AX_GUI_DLL EditBoxImplCommon : public EditBoxImpl int _fontSize; int _placeholderFontSize; - Color4B _colText; - Color4B _colPlaceHolder; + Color32 _colText; + Color32 _colPlaceHolder; int _maxLength; Size _contentSize; diff --git a/core/ui/UIEditBox/UIEditBoxImpl-ios.h b/core/ui/UIEditBox/UIEditBoxImpl-ios.h index 43a7c5f537d8..4fe150bef277 100644 --- a/core/ui/UIEditBox/UIEditBoxImpl-ios.h +++ b/core/ui/UIEditBox/UIEditBoxImpl-ios.h @@ -59,9 +59,9 @@ class EditBoxImplIOS : public EditBoxImplCommon virtual bool isEditing() override; virtual void createNativeControl(const Rect& frame) override; virtual void setNativeFont(const char* pFontName, int fontSize) override; - virtual void setNativeFontColor(const Color4B& color) override; + virtual void setNativeFontColor(const Color32& color) override; virtual void setNativePlaceholderFont(const char* pFontName, int fontSize) override; - virtual void setNativePlaceholderFontColor(const Color4B& color) override; + virtual void setNativePlaceholderFontColor(const Color32& color) override; virtual void setNativeInputMode(EditBox::InputMode inputMode) override; virtual void setNativeInputFlag(EditBox::InputFlag inputFlag) override; virtual void setNativeReturnType(EditBox::KeyboardReturnType returnType) override; diff --git a/core/ui/UIEditBox/UIEditBoxImpl-ios.mm b/core/ui/UIEditBox/UIEditBoxImpl-ios.mm index d06f4e8f1d75..30e4682d000f 100644 --- a/core/ui/UIEditBox/UIEditBoxImpl-ios.mm +++ b/core/ui/UIEditBox/UIEditBoxImpl-ios.mm @@ -100,7 +100,7 @@ of this software and associated documentation files (the "Software"), to deal } } -void EditBoxImplIOS::setNativeFontColor(const Color4B& color) +void EditBoxImplIOS::setNativeFontColor(const Color32& color) { _systemControl.textColor = [UIColor colorWithRed:color.r / 255.0f green:color.g / 255.0f @@ -117,7 +117,7 @@ of this software and associated documentation files (the "Software"), to deal } } -void EditBoxImplIOS::setNativePlaceholderFontColor(const Color4B& color) +void EditBoxImplIOS::setNativePlaceholderFontColor(const Color32& color) { [_systemControl setPlaceholderTextColor:[UIColor colorWithRed:color.r / 255.0f green:color.g / 255.0f diff --git a/core/ui/UIEditBox/UIEditBoxImpl-linux.h b/core/ui/UIEditBox/UIEditBoxImpl-linux.h index 501199af7bd6..e910fcb87297 100644 --- a/core/ui/UIEditBox/UIEditBoxImpl-linux.h +++ b/core/ui/UIEditBox/UIEditBoxImpl-linux.h @@ -60,9 +60,9 @@ class EditBoxImplLinux : public EditBoxImplCommon virtual bool isEditing() override; virtual void createNativeControl(const Rect& frame) override{}; virtual void setNativeFont(const char* pFontName, int fontSize) override{}; - virtual void setNativeFontColor(const Color4B& color) override{}; + virtual void setNativeFontColor(const Color32& color) override{}; virtual void setNativePlaceholderFont(const char* pFontName, int fontSize) override{}; - virtual void setNativePlaceholderFontColor(const Color4B& color) override{}; + virtual void setNativePlaceholderFontColor(const Color32& color) override{}; virtual void setNativeInputMode(EditBox::InputMode inputMode) override{}; virtual void setNativeInputFlag(EditBox::InputFlag inputFlag) override{}; virtual void setNativeReturnType(EditBox::KeyboardReturnType returnType) override{}; diff --git a/core/ui/UIEditBox/UIEditBoxImpl-mac.h b/core/ui/UIEditBox/UIEditBoxImpl-mac.h index bfcd18a928bc..be96d050d58e 100644 --- a/core/ui/UIEditBox/UIEditBoxImpl-mac.h +++ b/core/ui/UIEditBox/UIEditBoxImpl-mac.h @@ -61,9 +61,9 @@ class EditBoxImplMac : public EditBoxImplCommon virtual bool isEditing() override; virtual void createNativeControl(const Rect& frame) override; virtual void setNativeFont(const char* pFontName, int fontSize) override; - virtual void setNativeFontColor(const Color4B& color) override; + virtual void setNativeFontColor(const Color32& color) override; virtual void setNativePlaceholderFont(const char* pFontName, int fontSize) override; - virtual void setNativePlaceholderFontColor(const Color4B& color) override; + virtual void setNativePlaceholderFontColor(const Color32& color) override; virtual void setNativeInputMode(EditBox::InputMode inputMode) override; virtual void setNativeInputFlag(EditBox::InputFlag inputFlag) override; virtual void setNativeReturnType(EditBox::KeyboardReturnType returnType) override; diff --git a/core/ui/UIEditBox/UIEditBoxImpl-mac.mm b/core/ui/UIEditBox/UIEditBoxImpl-mac.mm index e4eb412ebe34..f6bffb478bf6 100644 --- a/core/ui/UIEditBox/UIEditBoxImpl-mac.mm +++ b/core/ui/UIEditBox/UIEditBoxImpl-mac.mm @@ -125,7 +125,7 @@ of this software and associated documentation files (the "Software"), to deal [_sysEdit setPlaceholderFont:textFont]; } -void EditBoxImplMac::setNativeFontColor(const ax::Color4B& color) +void EditBoxImplMac::setNativeFontColor(const ax::Color32& color) { NSColor* newColor = [NSColor colorWithCalibratedRed:color.r / 255.0f green:color.g / 255.0f @@ -135,7 +135,7 @@ of this software and associated documentation files (the "Software"), to deal [_sysEdit setTextColor:newColor]; } -void EditBoxImplMac::setNativePlaceholderFontColor(const ax::Color4B& color) +void EditBoxImplMac::setNativePlaceholderFontColor(const ax::Color32& color) { NSColor* newColor = [NSColor colorWithCalibratedRed:color.r / 255.f green:color.g / 255.f diff --git a/core/ui/UIEditBox/UIEditBoxImpl-wasm.cpp b/core/ui/UIEditBox/UIEditBoxImpl-wasm.cpp index b86b5eeff47f..61325db16988 100644 --- a/core/ui/UIEditBox/UIEditBoxImpl-wasm.cpp +++ b/core/ui/UIEditBox/UIEditBoxImpl-wasm.cpp @@ -42,11 +42,12 @@ extern "C" { EMSCRIPTEN_KEEPALIVE void getInputOver(char* dataPtr, int dataLength) { - AXLOGD("text {} ", dataPtr); + std::string_view text{ dataPtr, static_cast(dataLength) }; + AXLOGD("text {} ", text); if (_activeEditBox) { if (_activeEditBox->isEditingMode()) - _activeEditBox->editBoxEditingDidEnd(std::string_view{(char*)dataPtr}, + _activeEditBox->editBoxEditingDidEnd(text, EditBoxDelegate::EditBoxEndAction::RETURN); _activeEditBox = nullptr; } @@ -57,10 +58,11 @@ EMSCRIPTEN_KEEPALIVE void getInputChange(char* dataPtr, int dataLength) { - AXLOGD("text {} ", dataPtr); + std::string_view text{ dataPtr, static_cast(dataLength) }; + AXLOGD("text {} ", text); if (_activeEditBox && _activeEditBox->isEditingMode()) { - _activeEditBox->editBoxEditingChanged(std::string_view{(char*)dataPtr}); + _activeEditBox->editBoxEditingChanged(text); } free(dataPtr); } @@ -109,7 +111,7 @@ void EditBoxImplWasm::setNativeFont(const char* pFontName, int fontSize) fontSize); } -void EditBoxImplWasm::setNativeFontColor(const Color4B& color) +void EditBoxImplWasm::setNativeFontColor(const Color32& color) { // not implemented yet } @@ -119,7 +121,7 @@ void EditBoxImplWasm::setNativePlaceholderFont(const char* pFontName, int fontSi // not implemented yet } -void EditBoxImplWasm::setNativePlaceholderFontColor(const Color4B& color) +void EditBoxImplWasm::setNativePlaceholderFontColor(const Color32& color) { // not implemented yet } diff --git a/core/ui/UIEditBox/UIEditBoxImpl-wasm.h b/core/ui/UIEditBox/UIEditBoxImpl-wasm.h index 6902e6590767..812a7250779b 100644 --- a/core/ui/UIEditBox/UIEditBoxImpl-wasm.h +++ b/core/ui/UIEditBox/UIEditBoxImpl-wasm.h @@ -48,9 +48,9 @@ class AX_API EditBoxImplWasm : public EditBoxImplCommon virtual bool isEditing() override; virtual void createNativeControl(const Rect& frame) override; virtual void setNativeFont(const char* pFontName, int fontSize) override; - virtual void setNativeFontColor(const Color4B& color) override; + virtual void setNativeFontColor(const Color32& color) override; virtual void setNativePlaceholderFont(const char* pFontName, int fontSize) override; - virtual void setNativePlaceholderFontColor(const Color4B& color) override; + virtual void setNativePlaceholderFontColor(const Color32& color) override; virtual void setNativeInputMode(EditBox::InputMode inputMode) override; virtual void setNativeInputFlag(EditBox::InputFlag inputFlag) override; virtual void setNativeReturnType(EditBox::KeyboardReturnType returnType) override; diff --git a/core/ui/UIEditBox/UIEditBoxImpl-win32.cpp b/core/ui/UIEditBox/UIEditBoxImpl-win32.cpp index 4a3a12920451..3b8d1bb0d4f3 100644 --- a/core/ui/UIEditBox/UIEditBoxImpl-win32.cpp +++ b/core/ui/UIEditBox/UIEditBoxImpl-win32.cpp @@ -154,7 +154,7 @@ void EditBoxImplWin::setNativeFont(const char* pFontName, int fontSize) ); } -void EditBoxImplWin::setNativeFontColor(const Color4B& color) +void EditBoxImplWin::setNativeFontColor(const Color32& color) { // not implemented yet } @@ -164,7 +164,7 @@ void EditBoxImplWin::setNativePlaceholderFont(const char* pFontName, int fontSiz // not implemented yet } -void EditBoxImplWin::setNativePlaceholderFontColor(const Color4B& color) +void EditBoxImplWin::setNativePlaceholderFontColor(const Color32& color) { // not implemented yet } diff --git a/core/ui/UIEditBox/UIEditBoxImpl-win32.h b/core/ui/UIEditBox/UIEditBoxImpl-win32.h index 5d81e090f279..6ef72554b287 100644 --- a/core/ui/UIEditBox/UIEditBoxImpl-win32.h +++ b/core/ui/UIEditBox/UIEditBoxImpl-win32.h @@ -50,9 +50,9 @@ class AX_GUI_DLL EditBoxImplWin : public EditBoxImplCommon virtual bool isEditing() override; virtual void createNativeControl(const Rect& frame) override; virtual void setNativeFont(const char* pFontName, int fontSize) override; - virtual void setNativeFontColor(const Color4B& color) override; + virtual void setNativeFontColor(const Color32& color) override; virtual void setNativePlaceholderFont(const char* pFontName, int fontSize) override; - virtual void setNativePlaceholderFontColor(const Color4B& color) override; + virtual void setNativePlaceholderFontColor(const Color32& color) override; virtual void setNativeInputMode(EditBox::InputMode inputMode) override; virtual void setNativeInputFlag(EditBox::InputFlag inputFlag) override; virtual void setNativeReturnType(EditBox::KeyboardReturnType returnType) override; diff --git a/core/ui/UIEditBox/UIEditBoxImpl-winrt.cpp b/core/ui/UIEditBox/UIEditBoxImpl-winrt.cpp index 714ca1ae83eb..51911dda2af2 100644 --- a/core/ui/UIEditBox/UIEditBoxImpl-winrt.cpp +++ b/core/ui/UIEditBox/UIEditBoxImpl-winrt.cpp @@ -470,7 +470,7 @@ void UIEditBoxImplWinrt::setNativeFont(const char* pFontName, int fontSize) } } -void UIEditBoxImplWinrt::setNativeFontColor(const Color4B& color) +void UIEditBoxImplWinrt::setNativeFontColor(const Color32& color) { Windows::UI::Color win_color = {0xFF, color.r, color.g, color.b}; _system_control->setFontColor(win_color); diff --git a/core/ui/UIEditBox/UIEditBoxImpl-winrt.h b/core/ui/UIEditBox/UIEditBoxImpl-winrt.h index d3359a493e32..b8148d1295f0 100644 --- a/core/ui/UIEditBox/UIEditBoxImpl-winrt.h +++ b/core/ui/UIEditBox/UIEditBoxImpl-winrt.h @@ -133,9 +133,9 @@ namespace ui { virtual bool isEditing() override { return _system_control.get()->isEditing(); } virtual void createNativeControl(const Rect& frame) override { } virtual void setNativeFont(const char* pFontName, int fontSize) override; - virtual void setNativeFontColor(const Color4B& color) override; + virtual void setNativeFontColor(const Color32& color) override; virtual void setNativePlaceholderFont(const char* pFontName, int fontSize) override { AXLOGD("Warning! You can't change WinRT placeholder font"); } - virtual void setNativePlaceholderFontColor(const Color4B& color) override { AXLOGD("Warning! You can't change WinRT placeholder font color"); } + virtual void setNativePlaceholderFontColor(const Color32& color) override { AXLOGD("Warning! You can't change WinRT placeholder font color"); } virtual void setNativeInputMode(EditBox::InputMode inputMode) override; virtual void setNativeInputFlag(EditBox::InputFlag inputFlag) override; virtual void setNativeReturnType(EditBox::KeyboardReturnType returnType) override { AXLOGD("Warning! You can't change WinRT return type"); } diff --git a/core/ui/UIEditBox/UIEditBoxImpl.h b/core/ui/UIEditBox/UIEditBoxImpl.h index 7a3dc6ebb8f3..df04d7b52629 100644 --- a/core/ui/UIEditBox/UIEditBoxImpl.h +++ b/core/ui/UIEditBox/UIEditBoxImpl.h @@ -51,9 +51,9 @@ class AX_GUI_DLL EditBoxImpl virtual bool initWithSize(const Size& size) = 0; virtual void setFont(const char* pFontName, int fontSize) = 0; - virtual void setFontColor(const Color4B& color) = 0; + virtual void setFontColor(const Color32& color) = 0; virtual void setPlaceholderFont(const char* pFontName, int fontSize) = 0; - virtual void setPlaceholderFontColor(const Color4B& color) = 0; + virtual void setPlaceholderFontColor(const Color32& color) = 0; virtual void setInputMode(EditBox::InputMode inputMode) = 0; virtual void setInputFlag(EditBox::InputFlag inputFlag) = 0; virtual void setMaxLength(int maxLength) = 0; @@ -69,11 +69,11 @@ class AX_GUI_DLL EditBoxImpl virtual const char* getFontName() = 0; virtual int getFontSize() = 0; - virtual const Color4B& getFontColor() = 0; + virtual const Color32& getFontColor() = 0; virtual const char* getPlaceholderFontName() = 0; virtual int getPlaceholderFontSize() = 0; - virtual const Color4B& getPlaceholderFontColor() = 0; + virtual const Color32& getPlaceholderFontColor() = 0; virtual EditBox::InputMode getInputMode() = 0; virtual EditBox::InputFlag getInputFlag() = 0; diff --git a/core/ui/UILayout.cpp b/core/ui/UILayout.cpp index 3cd3262a7361..78b3b2972dcd 100644 --- a/core/ui/UILayout.cpp +++ b/core/ui/UILayout.cpp @@ -449,7 +449,7 @@ void Layout::setStencilClippingSize(const Vec2& /*size*/) if (_clippingEnabled && _clippingType == ClippingType::STENCIL) { _clippingStencil->clear(); - _clippingStencil->drawSolidRect(Vec2::ZERO, _contentSize, Color4B::GREEN); // Fix issue #1546 + _clippingStencil->drawSolidRect(Vec2::ZERO, _contentSize, Color::GREEN); // Fix issue #1546 } } diff --git a/core/ui/UIMediaPlayer.cpp b/core/ui/UIMediaPlayer.cpp index 333d126adf8b..5fc48968c647 100644 --- a/core/ui/UIMediaPlayer.cpp +++ b/core/ui/UIMediaPlayer.cpp @@ -220,7 +220,7 @@ void createMediaControlTexture() auto DrawStop = [&](const Vec2& middle) -> void { auto s = Vec2(middle.x - iconW / 2.f, middle.y + iconH / 2.f); - drawNode->drawSolidRect(s, s + Vec2(iconW, -iconH), Color4B::WHITE); + drawNode->drawSolidRect(s, s + Vec2(iconW, -iconH), Color::WHITE); }; auto DrawPlay = [&](const Vec2& middle) -> void { @@ -228,15 +228,15 @@ void createMediaControlTexture() auto p2 = Vec2(middle.x + iconW / 2.f, middle.y); auto p3 = Vec2(middle.x - iconW / 2.f, middle.y - iconH / 2.f); - drawNode->drawTriangle(p1, p2, p3, Color4B::WHITE); + drawNode->drawTriangle(p1, p2, p3, Color::WHITE); }; auto DrawPause = [&](const Vec2& middle) -> void { auto start = Vec2(middle.x - 3, middle.y + iconH / 2.f); - drawNode->drawSolidRect(start, start + Vec2(-6, -iconH), Color4B::WHITE); + drawNode->drawSolidRect(start, start + Vec2(-6, -iconH), Color::WHITE); start = Vec2(middle.x + 3, middle.y + iconH / 2.f); - drawNode->drawSolidRect(start, start + Vec2(6, -iconH), Color4B::WHITE); + drawNode->drawSolidRect(start, start + Vec2(6, -iconH), Color::WHITE); }; auto DrawEnterFullscreen = [&](const Vec2& middle) -> void { @@ -246,20 +246,20 @@ void createMediaControlTexture() auto bottomRight = Vec2(middle.x + panelW / 2.f - 6, middle.y - panelH / 2.f + 6); // Top left - drawNode->drawSolidRect(topLeft, topLeft + Vec2(20, -6), Color4B::WHITE); - drawNode->drawSolidRect(topLeft, topLeft + Vec2(6, -20), Color4B::WHITE); + drawNode->drawSolidRect(topLeft, topLeft + Vec2(20, -6), Color::WHITE); + drawNode->drawSolidRect(topLeft, topLeft + Vec2(6, -20), Color::WHITE); // Top right - drawNode->drawSolidRect(topRight, topRight + Vec2(-20, -6), Color4B::WHITE); - drawNode->drawSolidRect(topRight, topRight + Vec2(-6, -20), Color4B::WHITE); + drawNode->drawSolidRect(topRight, topRight + Vec2(-20, -6), Color::WHITE); + drawNode->drawSolidRect(topRight, topRight + Vec2(-6, -20), Color::WHITE); // Bottom left - drawNode->drawSolidRect(bottomLeft, bottomLeft + Vec2(20, 6), Color4B::WHITE); - drawNode->drawSolidRect(bottomLeft, bottomLeft + Vec2(6, 20), Color4B::WHITE); + drawNode->drawSolidRect(bottomLeft, bottomLeft + Vec2(20, 6), Color::WHITE); + drawNode->drawSolidRect(bottomLeft, bottomLeft + Vec2(6, 20), Color::WHITE); // Bottom right - drawNode->drawSolidRect(bottomRight, bottomRight + Vec2(-20, 6), Color4B::WHITE); - drawNode->drawSolidRect(bottomRight, bottomRight + Vec2(-6, 20), Color4B::WHITE); + drawNode->drawSolidRect(bottomRight, bottomRight + Vec2(-20, 6), Color::WHITE); + drawNode->drawSolidRect(bottomRight, bottomRight + Vec2(-6, 20), Color::WHITE); }; auto DrawExitFullScreen = [&](const Vec2& middle) -> void { @@ -269,24 +269,24 @@ void createMediaControlTexture() auto bottomRight = Vec2(middle.x + 4, middle.y - 4); // Top left - drawNode->drawSolidRect(topLeft, topLeft + Vec2(-20, 6), Color4B::WHITE); - drawNode->drawSolidRect(topLeft, topLeft + Vec2(-6, 20), Color4B::WHITE); + drawNode->drawSolidRect(topLeft, topLeft + Vec2(-20, 6), Color::WHITE); + drawNode->drawSolidRect(topLeft, topLeft + Vec2(-6, 20), Color::WHITE); // Top right - drawNode->drawSolidRect(topRight, topRight + Vec2(20, 6), Color4B::WHITE); - drawNode->drawSolidRect(topRight, topRight + Vec2(6, 20), Color4B::WHITE); + drawNode->drawSolidRect(topRight, topRight + Vec2(20, 6), Color::WHITE); + drawNode->drawSolidRect(topRight, topRight + Vec2(6, 20), Color::WHITE); // Bottom left - drawNode->drawSolidRect(bottomLeft, bottomLeft + Vec2(-20, -6), Color4B::WHITE); - drawNode->drawSolidRect(bottomLeft, bottomLeft + Vec2(-6, -20), Color4B::WHITE); + drawNode->drawSolidRect(bottomLeft, bottomLeft + Vec2(-20, -6), Color::WHITE); + drawNode->drawSolidRect(bottomLeft, bottomLeft + Vec2(-6, -20), Color::WHITE); // Bottom right - drawNode->drawSolidRect(bottomRight, bottomRight + Vec2(20, -6), Color4B::WHITE); - drawNode->drawSolidRect(bottomRight, bottomRight + Vec2(6, -20), Color4B::WHITE); + drawNode->drawSolidRect(bottomRight, bottomRight + Vec2(20, -6), Color::WHITE); + drawNode->drawSolidRect(bottomRight, bottomRight + Vec2(6, -20), Color::WHITE); }; auto DrawSliderControlButton = [&](const Vec2& middle) -> void { - drawNode->drawSolidCircle(middle, panelW / 2, 0, 180, Color4B::WHITE); + drawNode->drawSolidCircle(middle, panelW / 2, 0, 180, Color::WHITE); }; std::map> items = { @@ -1142,7 +1142,7 @@ void MediaPlayer::draw(Renderer* renderer, const Mat4& transform, uint32_t flags _debugDrawNode->clear(); auto size = getContentSize(); Point vertices[4] = {Point::ZERO, Point(size.width, 0), Point(size.width, size.height), Point(0, size.height)}; - _debugDrawNode->drawPoly(vertices, 4, true, Color4B::WHITE); + _debugDrawNode->drawPoly(vertices, 4, true, Color32::WHITE); # endif } diff --git a/core/ui/UIRichText.cpp b/core/ui/UIRichText.cpp index 5aa3b45521bc..de244f1a59b3 100644 --- a/core/ui/UIRichText.cpp +++ b/core/ui/UIRichText.cpp @@ -1723,12 +1723,12 @@ std::string RichText::stringWithColor3B(const ax::Color3B& color3b) return std::string(buf, 7); } -std::string RichText::stringWithColor4B(const ax::Color4B& color4b) +std::string RichText::stringWithColor32(const ax::Color32& Color32) { - int r = color4b.r; - int g = color4b.g; - int b = color4b.b; - int a = color4b.a; + int r = Color32.r; + int g = Color32.g; + int b = Color32.b; + int a = Color32.a; char buf[10]; snprintf(buf, sizeof(buf), "#%02x%02x%02x%02x", r, g, b, a); return std::string(buf, 9); @@ -1805,18 +1805,18 @@ void RichText::formatText(bool force) label, elmtText->_url, [this](std::string_view url) { openUrl(url); })); if (elmtText->_flags & RichElementText::OUTLINE_FLAG) { - label->enableOutline(Color4B(elmtText->_outlineColor), elmtText->_outlineSize); + label->enableOutline(Color32(elmtText->_outlineColor), elmtText->_outlineSize); } if (elmtText->_flags & RichElementText::SHADOW_FLAG) { - label->enableShadow(Color4B(elmtText->_shadowColor), elmtText->_shadowOffset, + label->enableShadow(Color32(elmtText->_shadowColor), elmtText->_shadowOffset, elmtText->_shadowBlurRadius); } if (elmtText->_flags & RichElementText::GLOW_FLAG) { - label->enableGlow(Color4B(elmtText->_glowColor)); + label->enableGlow(Color32(elmtText->_glowColor)); } - label->setTextColor(Color4B(elmtText->_color)); + label->setTextColor(Color32(elmtText->_color)); label->setName(elmtText->_id); @@ -2138,13 +2138,13 @@ void RichText::handleTextRenderer(std::string_view text, textRenderer->addComponent(UrlTouchListenerComponent::create( textRenderer, url, [this](std::string_view url) { openUrl(url); })); if (flags & RichElementText::OUTLINE_FLAG) - textRenderer->enableOutline(Color4B(outlineColor), outlineSize); + textRenderer->enableOutline(Color32(outlineColor), outlineSize); if (flags & RichElementText::SHADOW_FLAG) - textRenderer->enableShadow(Color4B(shadowColor), shadowOffset, shadowBlurRadius); + textRenderer->enableShadow(Color32(shadowColor), shadowOffset, shadowBlurRadius); if (flags & RichElementText::GLOW_FLAG) - textRenderer->enableGlow(Color4B(glowColor)); + textRenderer->enableGlow(Color32(glowColor)); - textRenderer->setTextColor(Color4B(color)); + textRenderer->setTextColor(Color32(color)); textRenderer->setOpacity(opacity); if (isFirstLabel && !id.empty()) diff --git a/core/ui/UIRichText.h b/core/ui/UIRichText.h index 08c370fe34fe..098a40e5b198 100644 --- a/core/ui/UIRichText.h +++ b/core/ui/UIRichText.h @@ -578,7 +578,7 @@ class AX_GUI_DLL RichText : public Widget ax::Color3B color3BWithString(std::string_view color); /*!< convert a color string into a Color3B. */ std::string stringWithColor3B(const ax::Color3B& color3b); /*!< convert a Color3B into a color string. */ - std::string stringWithColor4B(const ax::Color4B& color4b); /*!< convert a Color4B into a color string. */ + std::string stringWithColor32(const ax::Color32& Color32); /*!< convert a Color32 into a color string. */ /** * @brief add a callback to own tag. diff --git a/core/ui/UITabControl.cpp b/core/ui/UITabControl.cpp index ed59739a0e90..066bdf39855d 100644 --- a/core/ui/UITabControl.cpp +++ b/core/ui/UITabControl.cpp @@ -566,16 +566,16 @@ std::string_view TabHeader::getTitleText() const return _tabLabelRender->getString(); } -void TabHeader::setTitleColor(const Color4B& color) +void TabHeader::setTitleColor(const Color32& color) { _tabLabelRender->setTextColor(color); } -const Color4B& TabHeader::getTitleColor() const +const Color32& TabHeader::getTitleColor() const { if (nullptr == _tabLabelRender) { - return Color4B::WHITE; + return Color32::WHITE; } return _tabLabelRender->getTextColor(); } diff --git a/core/ui/UITabControl.h b/core/ui/UITabControl.h index 622ecb7b0fb2..a9291242856e 100644 --- a/core/ui/UITabControl.h +++ b/core/ui/UITabControl.h @@ -116,15 +116,15 @@ class AX_GUI_DLL TabHeader : public AbstractCheckButton /** * Change the color of he TabHeader text - *@param color The he TabHeader text's color in Color4B. + *@param color The he TabHeader text's color in Color32. */ - void setTitleColor(const Color4B& color); + void setTitleColor(const Color32& color); /** * get the TabHeader text color. - *@return Color4B of TabHeader text. + *@return Color32 of TabHeader text. */ - const Color4B& getTitleColor() const; + const Color32& getTitleColor() const; /** * Change the font size of TabHeader text diff --git a/core/ui/UIText.cpp b/core/ui/UIText.cpp index 03c7ad8d2f1b..05fe960e5e36 100644 --- a/core/ui/UIText.cpp +++ b/core/ui/UIText.cpp @@ -221,12 +221,12 @@ TextVAlignment Text::getTextVerticalAlignment() const return _labelRenderer->getVerticalAlignment(); } -void Text::setTextColor(const Color4B color) +void Text::setTextColor(const Color32 color) { _labelRenderer->setTextColor(color); } -const Color4B& Text::getTextColor() const +const Color32& Text::getTextColor() const { return _labelRenderer->getTextColor(); } @@ -332,19 +332,19 @@ std::string Text::getDescription() const return "Label"; } -void Text::enableShadow(const Color4B& shadowColor, const Vec2& offset, int blurRadius) +void Text::enableShadow(const Color32& shadowColor, const Vec2& offset, int blurRadius) { _labelRenderer->enableShadow(shadowColor, offset, blurRadius); } -void Text::enableOutline(const Color4B& outlineColor, int outlineSize) +void Text::enableOutline(const Color32& outlineColor, int outlineSize) { _labelRenderer->enableOutline(outlineColor, outlineSize); updateContentSizeWithTextureSize(_labelRenderer->getContentSize()); _labelRendererAdaptDirty = true; } -void Text::enableGlow(const Color4B& glowColor) +void Text::enableGlow(const Color32& glowColor) { if (_type == Type::TTF) _labelRenderer->enableGlow(glowColor); @@ -380,10 +380,10 @@ float Text::getShadowBlurRadius() const { return _labelRenderer->getShadowBlurRadius(); } -Color4B Text::getShadowColor() const +Color32 Text::getShadowColor() const { - Color4F effect = _labelRenderer->getShadowColor(); - return Color4B(effect.r * 255, effect.g * 255, effect.b * 255, effect.a * 255); + Color effect = _labelRenderer->getShadowColor(); + return Color32(effect.r * 255, effect.g * 255, effect.b * 255, effect.a * 255); } int Text::getOutlineSize() const @@ -394,10 +394,10 @@ LabelEffect Text::getLabelEffectType() const { return _labelRenderer->getLabelEffectType(); } -Color4B Text::getEffectColor() const +Color32 Text::getEffectColor() const { - Color4F effect = _labelRenderer->getEffectColor(); - return Color4B(effect.r * 255, effect.g * 255, effect.b * 255, effect.a * 255); + Color effect = _labelRenderer->getEffectColor(); + return Color32(effect.r * 255, effect.g * 255, effect.b * 255, effect.a * 255); } Sprite* Text::getLetter(int lettetIndex) diff --git a/core/ui/UIText.h b/core/ui/UIText.h index f73f5ad463f9..821538ed0389 100644 --- a/core/ui/UIText.h +++ b/core/ui/UIText.h @@ -241,13 +241,13 @@ class AX_GUI_DLL Text : public Widget, public ax::BlendProtocol * * @param color Text color. */ - void setTextColor(const Color4B color); + void setTextColor(const Color32 color); /** Gets text color. * * @return Text color. */ - const Color4B& getTextColor() const; + const Color32& getTextColor() const; /** * Enable shadow for the label. @@ -258,7 +258,7 @@ class AX_GUI_DLL Text : public Widget, public ax::BlendProtocol * @param offset The offset of shadow effect. * @param blurRadius The blur radius of shadow effect. */ - void enableShadow(const Color4B& shadowColor = Color4B::BLACK, + void enableShadow(const Color32& shadowColor = Color32::BLACK, const Vec2& offset = Vec2(2, -2), int blurRadius = 0); @@ -269,13 +269,13 @@ class AX_GUI_DLL Text : public Widget, public ax::BlendProtocol * @param outlineColor The color of outline. * @param outlineSize The size of outline. */ - void enableOutline(const Color4B& outlineColor, int outlineSize = 1); + void enableOutline(const Color32& outlineColor, int outlineSize = 1); /** Only support for TTF. * * @param glowColor The color of glow. */ - void enableGlow(const Color4B& glowColor); + void enableGlow(const Color32& glowColor); /** Disable all text effects, including shadow, outline and glow. */ @@ -304,7 +304,7 @@ class AX_GUI_DLL Text : public Widget, public ax::BlendProtocol /** * Return the shadow effect color value. */ - Color4B getShadowColor() const; + Color32 getShadowColor() const; /** * Return the outline effect size value. */ @@ -316,7 +316,7 @@ class AX_GUI_DLL Text : public Widget, public ax::BlendProtocol /** * Return current effect color value. */ - Color4B getEffectColor() const; + Color32 getEffectColor() const; /** * Provides a way to treat each character like a Sprite. diff --git a/core/ui/UITextField.cpp b/core/ui/UITextField.cpp index 1d59d5f56c9f..10f0d9b86417 100644 --- a/core/ui/UITextField.cpp +++ b/core/ui/UITextField.cpp @@ -400,7 +400,7 @@ std::string_view TextField::getPlaceHolder() const return _textFieldRenderer->getPlaceHolder(); } -const Color4B& TextField::getPlaceHolderColor() const +const Color32& TextField::getPlaceHolderColor() const { return _textFieldRenderer->getColorSpaceHolder(); } @@ -410,17 +410,17 @@ void TextField::setPlaceHolderColor(const ax::Color3B& color) _textFieldRenderer->setColorSpaceHolder(color); } -void TextField::setPlaceHolderColor(const ax::Color4B& color) +void TextField::setPlaceHolderColor(const ax::Color32& color) { _textFieldRenderer->setColorSpaceHolder(color); } -const Color4B& TextField::getTextColor() const +const Color32& TextField::getTextColor() const { return _textFieldRenderer->getTextColor(); } -void TextField::setTextColor(const ax::Color4B& textColor) +void TextField::setTextColor(const ax::Color32& textColor) { _textFieldRenderer->setTextColor(textColor); } diff --git a/core/ui/UITextField.h b/core/ui/UITextField.h index 6b15782f20c3..d7da7e328d68 100644 --- a/core/ui/UITextField.h +++ b/core/ui/UITextField.h @@ -317,7 +317,7 @@ class AX_GUI_DLL TextField : public Widget * * @return The color of placeholder. */ - const Color4B& getPlaceHolderColor() const; + const Color32& getPlaceHolderColor() const; /** * @brief Change the placeholder color. @@ -329,23 +329,23 @@ class AX_GUI_DLL TextField : public Widget /** * @brief Change the placeholder color. * - * @param color A color value in `Color4B`. + * @param color A color value in `Color32`. */ - void setPlaceHolderColor(const Color4B& color); + void setPlaceHolderColor(const Color32& color); /** * @brief Query the text string color. * * @return The color of the text. */ - const Color4B& getTextColor() const; + const Color32& getTextColor() const; /** * @brief Change the text color. * - * @param textColor The color value in `Color4B`. + * @param textColor The color value in `Color32`. */ - void setTextColor(const Color4B& textColor); + void setTextColor(const Color32& textColor); /** * @brief Change font size of TextField. diff --git a/core/ui/UITextFieldEx.cpp b/core/ui/UITextFieldEx.cpp index 52873d107d66..3987b7ea5d32 100644 --- a/core/ui/UITextFieldEx.cpp +++ b/core/ui/UITextFieldEx.cpp @@ -90,19 +90,19 @@ static bool engine_inj_containsPoint(ax::Node* target, const ax::Vec2& worldPoin return contains; } -static uint32_t engine_inj_c4b2dw(const Color4B& value) +static uint32_t engine_inj_c4b2dw(const Color32& value) { auto rvalue = (uint32_t)value.a << 24 | (uint32_t)value.b << 16 | (uint32_t)value.g << 8 | (uint32_t)value.r; return rvalue; } -static Sprite* engine_inj_create_lump(const Color4B& color, int height, int width) +static Sprite* engine_inj_create_lump(const Color32& color, int height, int width) { unsigned int* pixels((unsigned int*)malloc(height * width * sizeof(unsigned int))); // Fill Pixels uint32_t* ptr = pixels; - const Color4B fillColor = Color4B::WHITE; + const Color32 fillColor = Color32::WHITE; for (int i = 0; i < height * width; ++i) { ptr[i] = engine_inj_c4b2dw(fillColor); // 0xffffffff; @@ -241,8 +241,8 @@ TextFieldEx::TextFieldEx() , _charCount(0) , _inputText("") , _placeHolder("") - , _colorText(Color4B::WHITE) - , _colorSpaceHolder(Color4B::GRAY) + , _colorText(Color32::WHITE) + , _colorSpaceHolder(Color32::GRAY) , _secureTextEntry(false) , _cursor(nullptr) , _touchListener(nullptr) @@ -277,7 +277,7 @@ TextFieldEx* TextFieldEx::create(std::string_view placeholder, std::string_view fontName, float fontSize, float cursorWidth, - const Color4B& cursorColor) + const Color32& cursorColor) { TextFieldEx* ret = new TextFieldEx(); if (ret && ret->initWithPlaceHolder("", fontName, fontSize, cursorWidth, cursorColor)) @@ -300,7 +300,7 @@ bool TextFieldEx::initWithPlaceHolder(std::string_view placeholder, std::string_view fontName, float fontSize, float cursorWidth, - const Color4B& cursorColor) + const Color32& cursorColor) { _placeHolder = placeholder; @@ -759,14 +759,14 @@ std::string_view TextFieldEx::getContentText() return _inputText; } -void TextFieldEx::setTextColor(const Color4B& color) +void TextFieldEx::setTextColor(const Color32& color) { _colorText = color; if (!_inputText.empty()) _renderLabel->setTextColor(_colorText); } -const Color4B& TextFieldEx::getTextColor(void) const +const Color32& TextFieldEx::getTextColor(void) const { return _colorText; } @@ -781,12 +781,12 @@ const Color3B& TextFieldEx::getCursorColor(void) const return _cursor->getColor(); } -const Color4B& TextFieldEx::getPlaceholderColor() const +const Color32& TextFieldEx::getPlaceholderColor() const { return _colorSpaceHolder; } -void TextFieldEx::setPlaceholderColor(const Color4B& color) +void TextFieldEx::setPlaceholderColor(const Color32& color) { _colorSpaceHolder = color; if (_inputText.empty()) @@ -906,9 +906,9 @@ int TextFieldEx::getFontType() const return _fontType; } -void TextFieldEx::__initCursor(int height, int width, const Color4B& color) +void TextFieldEx::__initCursor(int height, int width, const Color32& color) { - _cursor = engine_inj_create_lump(Color4B(color), height, width); + _cursor = engine_inj_create_lump(Color32(color), height, width); this->addChild(_cursor); diff --git a/core/ui/UITextFieldEx.h b/core/ui/UITextFieldEx.h index 7cff67f0080e..a58368ad607b 100644 --- a/core/ui/UITextFieldEx.h +++ b/core/ui/UITextFieldEx.h @@ -58,13 +58,13 @@ class AX_DLL TextFieldEx : public Widget, public IMEDelegate std::string_view fontName, float fontSize, float cursorWidth = 2, - const Color4B& color = Color4B::WHITE); + const Color32& color = Color32::WHITE); bool initWithPlaceHolder(std::string_view placeholder, std::string_view fontName, float fontSize, float cursorWidth = 2, - const Color4B& color = Color4B::WHITE); + const Color32& color = Color32::WHITE); void enableIME(Node* control); void disableIME(void); @@ -73,11 +73,11 @@ class AX_DLL TextFieldEx : public Widget, public IMEDelegate inline int getCharCount() const { return static_cast(_charCount); }; - virtual void setPlaceholderColor(const Color4B& color); - virtual const Color4B& getPlaceholderColor() const; + virtual void setPlaceholderColor(const Color32& color); + virtual const Color32& getPlaceholderColor() const; - virtual void setTextColor(const Color4B& textColor); - virtual const Color4B& getTextColor(void) const; + virtual void setTextColor(const Color32& textColor); + virtual const Color32& getTextColor(void) const; void setCursorColor(const Color3B& color); const Color3B& getCursorColor(void) const; @@ -160,7 +160,7 @@ class AX_DLL TextFieldEx : public Widget, public IMEDelegate void updateContentSize(void); - void __initCursor(int height, int width = 6, const Color4B& color = Color4B::WHITE); + void __initCursor(int height, int width = 6, const Color32& color = Color32::WHITE); void __showCursor(void); void __hideCursor(void); void __updateCursorPosition(void); @@ -182,8 +182,8 @@ class AX_DLL TextFieldEx : public Widget, public IMEDelegate std::string _inputText; std::string _placeHolder; - Color4B _colorSpaceHolder; - Color4B _colorText; + Color32 _colorSpaceHolder; + Color32 _colorText; bool _secureTextEntry; diff --git a/core/ui/UIWebView/UIWebViewImpl-win32.cpp b/core/ui/UIWebView/UIWebViewImpl-win32.cpp index 6a775d24213b..40b71a2ccb46 100644 --- a/core/ui/UIWebView/UIWebViewImpl-win32.cpp +++ b/core/ui/UIWebView/UIWebViewImpl-win32.cpp @@ -34,9 +34,6 @@ # include "platform/FileUtils.h" # include "platform/GLView.h" # include "ui/UIHelper.h" -# include "rapidjson/document.h" -# include "rapidjson/stringbuffer.h" -# include "rapidjson/writer.h" # include "base/Utils.h" # define WIN32_LEAN_AND_MEAN diff --git a/extensions/DragonBones/src/DragonBones/CCArmatureDisplay.cpp b/extensions/DragonBones/src/DragonBones/CCArmatureDisplay.cpp index 72c2e5b5f831..cc2250b72ad9 100644 --- a/extensions/DragonBones/src/DragonBones/CCArmatureDisplay.cpp +++ b/extensions/DragonBones/src/DragonBones/CCArmatureDisplay.cpp @@ -208,17 +208,17 @@ void DBCCSprite::draw(ax::Renderer* renderer, const ax::Mat4& transform, uint32_ for (ssize_t i = 0; i < count; i++) { // draw 3 lines - auto from = verts[indices[i * 3]].vertices; - auto to = verts[indices[i * 3 + 1]].vertices; - _debugDrawNode->drawLine(ax::Vec2(from.x, from.y), ax::Vec2(to.x, to.y), ax::Color4B::WHITE); + auto from = verts[indices[i * 3]].position; + auto to = verts[indices[i * 3 + 1]].position; + _debugDrawNode->drawLine(ax::Vec2(from.x, from.y), ax::Vec2(to.x, to.y), ax::Color32::WHITE); - from = verts[indices[i * 3 + 1]].vertices; - to = verts[indices[i * 3 + 2]].vertices; - _debugDrawNode->drawLine(ax::Vec2(from.x, from.y), ax::Vec2(to.x, to.y), ax::Color4B::WHITE); + from = verts[indices[i * 3 + 1]].position; + to = verts[indices[i * 3 + 2]].position; + _debugDrawNode->drawLine(ax::Vec2(from.x, from.y), ax::Vec2(to.x, to.y), ax::Color32::WHITE); - from = verts[indices[i * 3 + 2]].vertices; - to = verts[indices[i * 3]].vertices; - _debugDrawNode->drawLine(ax::Vec2(from.x, from.y), ax::Vec2(to.x, to.y), ax::Color4B::WHITE); + from = verts[indices[i * 3 + 2]].position; + to = verts[indices[i * 3]].position; + _debugDrawNode->drawLine(ax::Vec2(from.x, from.y), ax::Vec2(to.x, to.y), ax::Color32::WHITE); } #endif // AX_SPRITE_DEBUG_DRAW } diff --git a/extensions/DragonBones/src/DragonBones/CCSlot.cpp b/extensions/DragonBones/src/DragonBones/CCSlot.cpp index 300508e69756..a96c7fcbf981 100644 --- a/extensions/DragonBones/src/DragonBones/CCSlot.cpp +++ b/extensions/DragonBones/src/DragonBones/CCSlot.cpp @@ -1,4 +1,4 @@ -#include "CCSlot.h" +#include "CCSlot.h" #include "CCTextureAtlasData.h" #include "CCArmatureDisplay.h" @@ -92,7 +92,7 @@ void CCSlot::_updateFrame() const auto& region = currentTextureData->region; const auto& textureAtlasSize = currentTextureData->spriteFrame->getTexture()->getContentSizeInPixels(); - auto vertices = new ax::V3F_C4B_T2F[vertexCount]; // does cocos2dx release it? + auto vertices = new ax::V3F_T2F_C4B[vertexCount]; // does cocos2dx release it? auto vertexIndices = new unsigned short[triangleCount * 3]; // does cocos2dx release it? ax::Rect boundsRect(999999.0f, 999999.0f, -999999.0f, -999999.0f); @@ -103,21 +103,21 @@ void CCSlot::_updateFrame() const auto y = floatArray[vertexOffset + i + 1]; auto u = floatArray[uvOffset + i]; auto v = floatArray[uvOffset + i + 1]; - ax::V3F_C4B_T2F vertexData; - vertexData.vertices.set(x, -y, 0.0f); + ax::V3F_T2F_C4B vertexData; + vertexData.position.set(x, -y, 0.0f); if (currentTextureData->rotated) { - vertexData.texCoords.u = (region.x + (1.0f - v) * region.width) / textureAtlasSize.width; - vertexData.texCoords.v = (region.y + u * region.height) / textureAtlasSize.height; + vertexData.texCoord.u = (region.x + (1.0f - v) * region.width) / textureAtlasSize.width; + vertexData.texCoord.v = (region.y + u * region.height) / textureAtlasSize.height; } else { - vertexData.texCoords.u = (region.x + u * region.width) / textureAtlasSize.width; - vertexData.texCoords.v = (region.y + v * region.height) / textureAtlasSize.height; + vertexData.texCoord.u = (region.x + u * region.width) / textureAtlasSize.width; + vertexData.texCoord.v = (region.y + v * region.height) / textureAtlasSize.height; } - vertexData.colors = ax::Color4B::WHITE; + vertexData.color = ax::Color32::WHITE; vertices[iH] = vertexData; if (boundsRect.origin.x > x) @@ -262,7 +262,7 @@ void CCSlot::_updateMesh() } auto& vertex = vertices[i]; - auto& vertexPosition = vertex.vertices; + auto& vertexPosition = vertex.position; vertexPosition.set(xG, -yG, 0.0f); @@ -307,7 +307,7 @@ void CCSlot::_updateMesh() const auto yG = floatArray[vertexOffset + i + 1] * scale + deformVertices[i + 1]; auto& vertex = vertices[iH]; - auto& vertexPosition = vertex.vertices; + auto& vertexPosition = vertex.position; vertexPosition.set(xG, -yG, 0.0f); diff --git a/extensions/Effekseer/Effekseer/Effekseer/Model/ProceduralModelGenerator.cpp b/extensions/Effekseer/Effekseer/Effekseer/Model/ProceduralModelGenerator.cpp index c566c117fffb..b625edd09439 100644 --- a/extensions/Effekseer/Effekseer/Effekseer/Model/ProceduralModelGenerator.cpp +++ b/extensions/Effekseer/Effekseer/Effekseer/Model/ProceduralModelGenerator.cpp @@ -923,4 +923,4 @@ void ProceduralModelGenerator::Ungenerate(ModelRef model) { } -} // namespace Effekseer \ No newline at end of file +} // namespace Effekseer diff --git a/extensions/Effekseer/Effekseer/Effekseer/Model/ProceduralModelParameter.h b/extensions/Effekseer/Effekseer/Effekseer/Model/ProceduralModelParameter.h index 4829044f58a3..914e8c548cd2 100644 --- a/extensions/Effekseer/Effekseer/Effekseer/Model/ProceduralModelParameter.h +++ b/extensions/Effekseer/Effekseer/Effekseer/Model/ProceduralModelParameter.h @@ -471,4 +471,4 @@ struct ProceduralModelParameter } // namespace Effekseer -#endif \ No newline at end of file +#endif diff --git a/extensions/Effekseer/EffekseerForCocos2d-x/EffekseerForCocos2d-x.cpp b/extensions/Effekseer/EffekseerForCocos2d-x/EffekseerForCocos2d-x.cpp index fc17ce99c88e..0fa685cf4d2e 100644 --- a/extensions/Effekseer/EffekseerForCocos2d-x/EffekseerForCocos2d-x.cpp +++ b/extensions/Effekseer/EffekseerForCocos2d-x/EffekseerForCocos2d-x.cpp @@ -598,7 +598,7 @@ bool EffectEmitter::getRemoveOnStop() { return removeOnStop; } void EffectEmitter::setRemoveOnStop(bool value) { removeOnStop = value; } -void EffectEmitter::setColor(cocos2d::Color4B color) +void EffectEmitter::setColor(cocos2d::Color32 color) { color_ = color; Effekseer::Color col; diff --git a/extensions/Effekseer/EffekseerForCocos2d-x/EffekseerForCocos2d-x.h b/extensions/Effekseer/EffekseerForCocos2d-x/EffekseerForCocos2d-x.h index 6f46e328a23f..6b9b06db5d31 100644 --- a/extensions/Effekseer/EffekseerForCocos2d-x/EffekseerForCocos2d-x.h +++ b/extensions/Effekseer/EffekseerForCocos2d-x/EffekseerForCocos2d-x.h @@ -77,7 +77,7 @@ class EffectEmitter : public cocos2d::Node cocos2d::Vec3 targetPosition_; float speed_ = 1.0f; - cocos2d::Color4B color_ = cocos2d::Color4B(255, 255, 255, 255); + cocos2d::Color32 color_ = cocos2d::Color32(255, 255, 255, 255); std::array dynamicInputs_; EffectManager* manager = nullptr; @@ -240,7 +240,7 @@ class EffectEmitter : public cocos2d::Node \~English Set the color which affects whole particles. \~Japanese 全てのパーティクルに影響する色を設定する。 */ - void setColor(cocos2d::Color4B color); + void setColor(cocos2d::Color32 color); /** @brief diff --git a/extensions/ImGui/src/ImGui/ImGuiPresenter.cpp b/extensions/ImGui/src/ImGui/ImGuiPresenter.cpp index 87f71cdd597f..7543262d2051 100644 --- a/extensions/ImGui/src/ImGui/ImGuiPresenter.cpp +++ b/extensions/ImGui/src/ImGui/ImGuiPresenter.cpp @@ -185,7 +185,7 @@ class ImGuiSceneEventTracker : public ImGuiEventTracker } }; auto mouseListener = EventListenerMouse::create(); - mouseListener->onMouseDown = mouseListener->onMouseUp = stopAnyMouse; + mouseListener->onMouseDown = mouseListener->onMouseUp = mouseListener->onMouseMove = mouseListener->onMouseScroll = stopAnyMouse; _trackLayer->getEventDispatcher()->addEventListenerWithSceneGraphPriority(mouseListener, _trackLayer); scene->addChild(_trackLayer, INT_MAX); #endif diff --git a/extensions/ImGui/src/ImGui/imgui_impl_ax.cpp b/extensions/ImGui/src/ImGui/imgui_impl_ax.cpp index bbcacf8021ed..767f77b9dde0 100644 --- a/extensions/ImGui/src/ImGui/imgui_impl_ax.cpp +++ b/extensions/ImGui/src/ImGui/imgui_impl_ax.cpp @@ -674,6 +674,14 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw // GLFW will emit an error which will often be printed by the app, so we temporarily disable error reporting. // Missing cursors will return nullptr and our _UpdateMouseCursor() function will use the Arrow cursor instead.) GLFWerrorfun prev_error_callback = glfwSetErrorCallback(nullptr); + + // axmol spec: Fix wasm64 javascript exception + // Note: The WASM actually not support glfwCreateStandardCursor until emsdk-4.0.1 Jan.17 2025 + // Uncaught TypeError: Cannot convert undefined to a BigInt + // at BigInt () + // at _glfwCreateStandardCursor (http://localhost:6931/cpp-tests.js:16617:12) + // at cpp-tests.wasm.ImGui_ImplGlfw_Init(GLFWwindow*, bool, GlfwClientApi) +#ifndef __EMSCRIPTEN__ bd->MouseCursors[ImGuiMouseCursor_Arrow] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); bd->MouseCursors[ImGuiMouseCursor_TextInput] = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR); bd->MouseCursors[ImGuiMouseCursor_ResizeNS] = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR); @@ -689,6 +697,7 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); +#endif #endif glfwSetErrorCallback(prev_error_callback); #if GLFW_HAS_GETERROR && !defined(__EMSCRIPTEN__) // Eat errors (see #5908) @@ -1098,7 +1107,7 @@ struct ImGui_ImplGlfw_ViewportData WNDPROC PrevWndProc; #endif - ImGui_ImplGlfw_ViewportData() { memset(this, 0, sizeof(*this)); IgnoreWindowSizeEventFrame = IgnoreWindowPosEventFrame = -1; } + ImGui_ImplGlfw_ViewportData() { memset((void*)this, 0, sizeof(*this)); IgnoreWindowSizeEventFrame = IgnoreWindowPosEventFrame = -1; } ~ImGui_ImplGlfw_ViewportData() { IM_ASSERT(Window == nullptr); } }; diff --git a/extensions/Live2D/Framework/src/Rendering/axmol/CubismCommandBuffer_Cocos2dx.cpp b/extensions/Live2D/Framework/src/Rendering/axmol/CubismCommandBuffer_Cocos2dx.cpp index ac7b03acd22a..45bb5fd0da7d 100644 --- a/extensions/Live2D/Framework/src/Rendering/axmol/CubismCommandBuffer_Cocos2dx.cpp +++ b/extensions/Live2D/Framework/src/Rendering/axmol/CubismCommandBuffer_Cocos2dx.cpp @@ -210,7 +210,7 @@ void CubismCommandBuffer_Cocos2dx::SetWindingMode(WindingType windingType) void CubismCommandBuffer_Cocos2dx::Clear(csmFloat32 r, csmFloat32 g, csmFloat32 b, csmFloat32 a) { // Add the callback command internally. - GetCocos2dRenderer()->clear(ax::ClearFlag::COLOR, ax::Color4F(r, g, b, a), 0.0f, 0, 0.0f); + GetCocos2dRenderer()->clear(ax::ClearFlag::COLOR, ax::Color(r, g, b, a), 0.0f, 0, 0.0f); } void CubismCommandBuffer_Cocos2dx::Viewport(csmFloat32 x, csmFloat32 y, csmFloat32 w, csmFloat32 h) diff --git a/extensions/Particle3D/src/Particle3D/PU/PUBeamRender.cpp b/extensions/Particle3D/src/Particle3D/PU/PUBeamRender.cpp index e6b7018d6eec..17500220c219 100644 --- a/extensions/Particle3D/src/Particle3D/PU/PUBeamRender.cpp +++ b/extensions/Particle3D/src/Particle3D/PU/PUBeamRender.cpp @@ -269,7 +269,7 @@ void PUBeamRender::prepare() PUBillboardChain::Element element; element = PUBillboardChain::Element( Vec3::ZERO, _rendererScale.x * static_cast(_particleSystem)->getDefaultWidth(), - 0.0f, Vec4::ONE, Quaternion::identity()); // V1.51 + 0.0f, Color::WHITE, Quaternion::identity()); // V1.51 _billboardChain->addChainElement(i, element); } diff --git a/extensions/Particle3D/src/Particle3D/PU/PUBillboardChain.cpp b/extensions/Particle3D/src/Particle3D/PU/PUBillboardChain.cpp index 2769431f15be..963d8241fee7 100644 --- a/extensions/Particle3D/src/Particle3D/PU/PUBillboardChain.cpp +++ b/extensions/Particle3D/src/Particle3D/PU/PUBillboardChain.cpp @@ -45,7 +45,7 @@ const size_t PUBillboardChain::SEGMENT_EMPTY = std::numeric_limits::max( //----------------------------------------------------------------------- PUBillboardChain::Element::Element() {} //----------------------------------------------------------------------- -PUBillboardChain::Element::Element(const Vec3& pos, float w, float tex, const Vec4& col, const Quaternion& ori) +PUBillboardChain::Element::Element(const Vec3& pos, float w, float tex, const Color& col, const Quaternion& ori) : position(pos), width(w), texCoord(tex), color(col), orientation(ori) {} //----------------------------------------------------------------------- @@ -157,10 +157,10 @@ void PUBillboardChain::setupBuffers() AX_SAFE_RELEASE_NULL(_vertexBuffer); AX_SAFE_RELEASE_NULL(_indexBuffer); - size_t stride = sizeof(VertexInfo); + size_t stride = sizeof(V3F_T2F_C4F); _vertexBuffer = backend::DriverBase::getInstance()->newBuffer( stride * _chainElementList.size() * 2, backend::BufferType::VERTEX, backend::BufferUsage::DYNAMIC); - VertexInfo vi = {Vec3(0.0f, 0.0f, 0.0f), Vec2(0.0f, 0.0f), Vec4::ONE}; + V3F_T2F_C4F vi = {Vec3(0.0f, 0.0f, 0.0f), Vec2(0.0f, 0.0f), Color::WHITE}; _vertices.resize(_chainElementList.size() * 2, vi); _indexBuffer = @@ -404,7 +404,7 @@ void PUBillboardChain::updateVertexBuffer(const Mat4& camMat) if (!_vertexContentDirty) return; - VertexInfo vi = {Vec3(0.0f, 0.0f, 0.0f), Vec2(0.0f, 0.0f), Vec4::ONE}; + V3F_T2F_C4F vi = {Vec3(0.0f, 0.0f, 0.0f), Vec2(0.0f, 0.0f), Color::WHITE}; _vertices.assign(_vertices.size(), vi); // HardwareVertexBufferSharedPtr pBuffer = // _vertexData->vertexBufferBinding->getBuffer(0); @@ -501,15 +501,15 @@ void PUBillboardChain::updateVertexBuffer(const Mat4& camMat) { //*pFloat++ = elem.texCoord; //*pFloat++ = _otherTexCoordRange[0]; - _vertices[vertexIndex + 0].uv.x = elem.texCoord; - _vertices[vertexIndex + 0].uv.y = _otherTexCoordRange[0]; + _vertices[vertexIndex + 0].texCoord.x = elem.texCoord; + _vertices[vertexIndex + 0].texCoord.y = _otherTexCoordRange[0]; } else { //*pFloat++ = _otherTexCoordRange[0]; //*pFloat++ = elem.texCoord; - _vertices[vertexIndex + 0].uv.x = _otherTexCoordRange[0]; - _vertices[vertexIndex + 0].uv.y = elem.texCoord; + _vertices[vertexIndex + 0].texCoord.x = _otherTexCoordRange[0]; + _vertices[vertexIndex + 0].texCoord.y = elem.texCoord; } // pBase = static_cast(pFloat); } @@ -538,15 +538,15 @@ void PUBillboardChain::updateVertexBuffer(const Mat4& camMat) { //*pFloat++ = elem.texCoord; //*pFloat++ = _otherTexCoordRange[1]; - _vertices[vertexIndex + 1].uv.x = elem.texCoord; - _vertices[vertexIndex + 1].uv.y = _otherTexCoordRange[1]; + _vertices[vertexIndex + 1].texCoord.x = elem.texCoord; + _vertices[vertexIndex + 1].texCoord.y = _otherTexCoordRange[1]; } else { //*pFloat++ = _otherTexCoordRange[1]; //*pFloat++ = elem.texCoord; - _vertices[vertexIndex + 1].uv.x = _otherTexCoordRange[1]; - _vertices[vertexIndex + 1].uv.y = elem.texCoord; + _vertices[vertexIndex + 1].texCoord.x = _otherTexCoordRange[1]; + _vertices[vertexIndex + 1].texCoord.y = elem.texCoord; } } diff --git a/extensions/Particle3D/src/Particle3D/PU/PUBillboardChain.h b/extensions/Particle3D/src/Particle3D/PU/PUBillboardChain.h index c9179aca02e3..67b0a5629034 100644 --- a/extensions/Particle3D/src/Particle3D/PU/PUBillboardChain.h +++ b/extensions/Particle3D/src/Particle3D/PU/PUBillboardChain.h @@ -57,13 +57,13 @@ class PUBillboardChain public: Element(); - Element(const Vec3& position, float width, float texCoord, const Vec4& colour, const Quaternion& orientation); + Element(const Vec3& position, float width, float texCoord, const Color& colour, const Quaternion& orientation); Vec3 position; float width; /// U or V texture coord depending on options float texCoord; - Vec4 color; + Color color; // Only used when mFaceCamera == false Quaternion orientation; @@ -310,12 +310,6 @@ class PUBillboardChain /// Chain segment has no elements static const size_t SEGMENT_EMPTY; - struct VertexInfo - { - Vec3 position; - Vec2 uv; - Vec4 color; - }; MeshCommand _meshCommand; RenderState::StateBlock _stateBlock; Texture2D* _texture = nullptr; @@ -323,7 +317,7 @@ class PUBillboardChain backend::Buffer* _indexBuffer = nullptr; // index buffer backend::Buffer* _vertexBuffer = nullptr; // vertex buffer - std::vector _vertices; + std::vector _vertices; std::vector _indices; std::string _texFile; diff --git a/extensions/Particle3D/src/Particle3D/PU/PUColorAffector.cpp b/extensions/Particle3D/src/Particle3D/PU/PUColorAffector.cpp index 5e04e3eb097d..a84d679b475a 100644 --- a/extensions/Particle3D/src/Particle3D/PU/PUColorAffector.cpp +++ b/extensions/Particle3D/src/Particle3D/PU/PUColorAffector.cpp @@ -48,7 +48,7 @@ void PUColorAffector::setColorOperation(const PUColorAffector::ColorOperation& c _colorOperation = colorOperation; } //----------------------------------------------------------------------- -void PUColorAffector::addColor(float timeFraction, const Vec4& color) +void PUColorAffector::addColor(float timeFraction, const Color& color) { _colorMap[timeFraction] = color; } @@ -91,7 +91,7 @@ void PUColorAffector::updatePUAffector(PUParticle3D* particle, float /*deltaTime { // PUParticle3D *particle = iter; // Linear interpolation of the colour - Vec4 color = Vec4::ONE; + Color color = Color::WHITE; float timeFraction = (particle->totalTimeToLive - particle->timeToLive) / particle->totalTimeToLive; ColorMapIterator it1 = findNearestColorMapIterator(timeFraction); ColorMapIterator it2 = it1; @@ -117,7 +117,7 @@ void PUColorAffector::updatePUAffector(PUParticle3D* particle, float /*deltaTime else { // Multiply - particle->color = Vec4(color.x * particle->originalColor.x, color.y * particle->originalColor.y, + particle->color = Color(color.x * particle->originalColor.x, color.y * particle->originalColor.y, color.z * particle->originalColor.z, color.w * particle->originalColor.w); } } diff --git a/extensions/Particle3D/src/Particle3D/PU/PUColorAffector.h b/extensions/Particle3D/src/Particle3D/PU/PUColorAffector.h index 0e87f1283801..adc7121020c1 100644 --- a/extensions/Particle3D/src/Particle3D/PU/PUColorAffector.h +++ b/extensions/Particle3D/src/Particle3D/PU/PUColorAffector.h @@ -37,8 +37,8 @@ namespace ax class AX_EX_DLL PUColorAffector : public PUAffector { public: - typedef std::map ColorMap; - typedef std::map::iterator ColorMapIterator; + typedef std::map ColorMap; + typedef std::map::iterator ColorMapIterator; enum ColorOperation { CAO_MULTIPLY, @@ -54,7 +54,7 @@ class AX_EX_DLL PUColorAffector : public PUAffector /** */ - void addColor(float timeFraction, const Vec4& color); + void addColor(float timeFraction, const Color& color); /** */ @@ -88,4 +88,4 @@ class AX_EX_DLL PUColorAffector : public PUAffector }; } -#endif \ No newline at end of file +#endif diff --git a/extensions/Particle3D/src/Particle3D/PU/PUColorAffectorTranslator.cpp b/extensions/Particle3D/src/Particle3D/PU/PUColorAffectorTranslator.cpp index 6c7ee4fc4c83..d841da253b3d 100644 --- a/extensions/Particle3D/src/Particle3D/PU/PUColorAffectorTranslator.cpp +++ b/extensions/Particle3D/src/Particle3D/PU/PUColorAffectorTranslator.cpp @@ -46,7 +46,7 @@ bool PUColorAffectorTranslator::translateChildProperty(PUScriptCompiler* compile { int n = 0; float time; - Vec4 colour; + Color color; PUAbstractNodeList::const_iterator i = prop->values.begin(); PUAbstractNodeList::const_iterator end = prop->values.end(); while (i != end) @@ -60,23 +60,23 @@ bool PUColorAffectorTranslator::translateChildProperty(PUScriptCompiler* compile time = v; break; case 1: - colour.x = v; + color.x = v; break; case 2: - colour.y = v; + color.y = v; break; case 3: - colour.z = v; + color.z = v; break; case 4: - colour.w = v; + color.w = v; break; } } ++n; ++i; } - affector->addColor(time, colour); + affector->addColor(time, color); return true; } } diff --git a/extensions/Particle3D/src/Particle3D/PU/PUEmitter.cpp b/extensions/Particle3D/src/Particle3D/PU/PUEmitter.cpp index c7e19bee9ca2..1f6351514e78 100644 --- a/extensions/Particle3D/src/Particle3D/PU/PUEmitter.cpp +++ b/extensions/Particle3D/src/Particle3D/PU/PUEmitter.cpp @@ -433,33 +433,33 @@ bool PUEmitter::makeParticleLocal(PUParticle3D* particle) } //----------------------------------------------------------------------- -const Vec4& PUEmitter::getParticleColor() const +const Color& PUEmitter::getParticleColor() const { return _particleColor; } //----------------------------------------------------------------------- -void PUEmitter::setParticleColor(const Vec4& particleColor) +void PUEmitter::setParticleColor(const Color& particleColor) { _particleColor = particleColor; } //----------------------------------------------------------------------- -const Vec4& PUEmitter::getParticleColorRangeStart() const +const Color& PUEmitter::getParticleColorRangeStart() const { return _particleColorRangeStart; } //----------------------------------------------------------------------- -void PUEmitter::setParticleColorRangeStart(const Vec4& particleColorRangeStart) +void PUEmitter::setParticleColorRangeStart(const Color& particleColorRangeStart) { _particleColorRangeStart = particleColorRangeStart; _particleColorRangeSet = true; } //----------------------------------------------------------------------- -const Vec4& PUEmitter::getParticleColorRangeEnd() const +const Color& PUEmitter::getParticleColorRangeEnd() const { return _particleColorRangeEnd; } //----------------------------------------------------------------------- -void PUEmitter::setParticleColorRangeEnd(const Vec4& particleColorRangeEnd) +void PUEmitter::setParticleColorRangeEnd(const Color& particleColorRangeEnd) { _particleColorRangeEnd = particleColorRangeEnd; _particleColorRangeSet = true; diff --git a/extensions/Particle3D/src/Particle3D/PU/PUEmitter.h b/extensions/Particle3D/src/Particle3D/PU/PUEmitter.h index f3b149a50ed6..9034805c73f3 100644 --- a/extensions/Particle3D/src/Particle3D/PU/PUEmitter.h +++ b/extensions/Particle3D/src/Particle3D/PU/PUEmitter.h @@ -262,27 +262,27 @@ class AX_EX_DLL PUEmitter : public Particle3DEmitter /** Get the colour of a particle that will be emitted. */ - const Vec4& getParticleColor() const; + const Color& getParticleColor() const; /** Set the colour of an emitted particle. */ - void setParticleColor(const Vec4& particleColour); + void setParticleColor(const Color& particleColour); /** Get the colour range start of an emitted particle. */ - const Vec4& getParticleColorRangeStart() const; + const Color& getParticleColorRangeStart() const; /** Set the colour range start of an emitted particle. This is the lower value used to generate a random colour. */ - void setParticleColorRangeStart(const Vec4& particleColourRangeStart); + void setParticleColorRangeStart(const Color& particleColourRangeStart); /** Get the colour range end of an emitted particle. */ - const Vec4& getParticleColorRangeEnd() const; + const Color& getParticleColorRangeEnd() const; /** Set the colour range end of an emitted particle. This is the upper value used to generate a random colour. */ - void setParticleColorRangeEnd(const Vec4& particleColourRangeEnd); + void setParticleColorRangeEnd(const Color& particleColourRangeEnd); /** Get the texture coords of an emitted particle. */ @@ -534,15 +534,15 @@ class AX_EX_DLL PUEmitter : public Particle3DEmitter /** Colour that is assigned to an emitted particle. */ - Vec4 _particleColor; + Color _particleColor; /** Used to randomize the colour of an emitted particle. */ - Vec4 _particleColorRangeStart; + Color _particleColorRangeStart; /** Used to randomize the colour of an emitted particle. */ - Vec4 _particleColorRangeEnd; + Color _particleColorRangeEnd; /** Used to determine whether the colour range has been set. */ diff --git a/extensions/Particle3D/src/Particle3D/PU/PUEmitterTranslator.cpp b/extensions/Particle3D/src/Particle3D/PU/PUEmitterTranslator.cpp index 307da57cad8a..a175d2e97ca9 100644 --- a/extensions/Particle3D/src/Particle3D/PU/PUEmitterTranslator.cpp +++ b/extensions/Particle3D/src/Particle3D/PU/PUEmitterTranslator.cpp @@ -303,7 +303,7 @@ void PUEmitterTranslator::translate(PUScriptCompiler* compiler, PUAbstractNode* // Property: start_colour_range if (passValidateProperty(compiler, prop, token[TOKEN_EMITTER_START_COLOUR_RANGE], VAL_COLOURVALUE)) { - Vec4 val; + Color val; if (getVector4(prop->values.begin(), prop->values.end(), &val)) { _emitter->setParticleColorRangeStart(val); @@ -315,7 +315,7 @@ void PUEmitterTranslator::translate(PUScriptCompiler* compiler, PUAbstractNode* // Property: end_colour_range if (passValidateProperty(compiler, prop, token[TOKEN_EMITTER_END_COLOUR_RANGE], VAL_COLOURVALUE)) { - Vec4 val; + Color val; if (getVector4(prop->values.begin(), prop->values.end(), &val)) { _emitter->setParticleColorRangeEnd(val); @@ -327,7 +327,7 @@ void PUEmitterTranslator::translate(PUScriptCompiler* compiler, PUAbstractNode* // Property: colour if (passValidateProperty(compiler, prop, token[TOKEN_EMITTER_COLOUR], VAL_COLOURVALUE)) { - Vec4 val; + Color val; if (getVector4(prop->values.begin(), prop->values.end(), &val)) { _emitter->setParticleColor(val); diff --git a/extensions/Particle3D/src/Particle3D/PU/PUMaterialManager.h b/extensions/Particle3D/src/Particle3D/PU/PUMaterialManager.h index 299fa240462d..9f52cc9e315f 100644 --- a/extensions/Particle3D/src/Particle3D/PU/PUMaterialManager.h +++ b/extensions/Particle3D/src/Particle3D/PU/PUMaterialManager.h @@ -42,10 +42,10 @@ class AX_EX_DLL PUMaterial : public Object std::string fileName; std::string name; bool isEnabledLight; - Vec4 ambientColor; - Vec4 diffuseColor; - Vec4 specularColor; - Vec4 emissiveColor; + Color ambientColor; + Color diffuseColor; + Color specularColor; + Color emissiveColor; float shininess; BlendFunc blendFunc; diff --git a/extensions/Particle3D/src/Particle3D/PU/PUMaterialTranslator.cpp b/extensions/Particle3D/src/Particle3D/PU/PUMaterialTranslator.cpp index 703af72f3e3f..76fe5482c2ad 100644 --- a/extensions/Particle3D/src/Particle3D/PU/PUMaterialTranslator.cpp +++ b/extensions/Particle3D/src/Particle3D/PU/PUMaterialTranslator.cpp @@ -198,7 +198,7 @@ void PUMaterialPassTranslator::translate(PUScriptCompiler* compiler, PUAbstractN { if (passValidateProperty(compiler, prop, matToken[TOKEN_MAT_AMIBIENT], VAL_VECTOR4)) { - Vec4 val; + Color val; if (getVector4(prop->values.begin(), prop->values.end(), &val)) { material->ambientColor = val; @@ -209,7 +209,7 @@ void PUMaterialPassTranslator::translate(PUScriptCompiler* compiler, PUAbstractN { if (passValidateProperty(compiler, prop, matToken[TOKEN_MAT_AMIBIENT], VAL_VECTOR4)) { - Vec4 val; + Color val; if (getVector4(prop->values.begin(), prop->values.end(), &val)) { material->diffuseColor = val; @@ -221,7 +221,7 @@ void PUMaterialPassTranslator::translate(PUScriptCompiler* compiler, PUAbstractN PUAbstractNodeList::const_iterator it = prop->values.begin(); PUAbstractNodeList::const_iterator end = prop->values.end(); unsigned int n = 0; - Vec4 color; + Color color; float shininess = 0.0f; while (it != end) { @@ -258,7 +258,7 @@ void PUMaterialPassTranslator::translate(PUScriptCompiler* compiler, PUAbstractN { if (passValidateProperty(compiler, prop, matToken[TOKEN_MAT_AMIBIENT], VAL_VECTOR4)) { - Vec4 val; + Color val; if (getVector4(prop->values.begin(), prop->values.end(), &val)) { material->emissiveColor = val; diff --git a/extensions/Particle3D/src/Particle3D/PU/PUParticleSystem3D.h b/extensions/Particle3D/src/Particle3D/PU/PUParticleSystem3D.h index 68c2842b54e6..8fb7c4baa357 100644 --- a/extensions/Particle3D/src/Particle3D/PU/PUParticleSystem3D.h +++ b/extensions/Particle3D/src/Particle3D/PU/PUParticleSystem3D.h @@ -108,7 +108,7 @@ struct AX_EX_DLL PUParticle3D : public Particle3D Vec3 rotationAxis; /** Current and original colour */ - Vec4 originalColor; + Color originalColor; /** The zRotationSpeed is used in combination with zRotation and defines tha actual rotationspeed in 2D. */ diff --git a/extensions/Particle3D/src/Particle3D/PU/PURender.cpp b/extensions/Particle3D/src/Particle3D/PU/PURender.cpp index 4a4ceecf1e07..9b9a52c421c0 100644 --- a/extensions/Particle3D/src/Particle3D/PU/PURender.cpp +++ b/extensions/Particle3D/src/Particle3D/PU/PURender.cpp @@ -81,7 +81,7 @@ void PUParticle3DQuadRender::render(Renderer* renderer, const Mat4& transform, P if (_vertexBuffer == nullptr) { - size_t stride = sizeof(VertexInfo); + size_t stride = sizeof(V3F_T2F_C4F); _vertexBuffer = backend::DriverBase::getInstance()->newBuffer(stride * 4 * particleSystem->getParticleQuota(), backend::BufferType::VERTEX, backend::BufferUsage::DYNAMIC); @@ -441,11 +441,11 @@ void PUParticle3DQuadRender::determineUVCoords(PUParticle3D* particle) particle->rt_uv = particle->lb_uv + Vec2(_textureCoordsColStep, _textureCoordsRowStep); } -void PUParticle3DQuadRender::fillVertex(unsigned short index, const Vec3& pos, const Vec4& color, const Vec2& uv) +void PUParticle3DQuadRender::fillVertex(unsigned short index, const Vec3& pos, const Color& color, const Vec2& uv) { _vertices[index].position = pos; _vertices[index].color = color; - _vertices[index].uv = uv; + _vertices[index].texCoord = uv; } void PUParticle3DQuadRender::fillTriangle(unsigned short index, unsigned short v0, unsigned short v1, unsigned short v2) @@ -699,7 +699,7 @@ void PUParticle3DBoxRender::render(Renderer* renderer, const Mat4& transform, Pa if (_vertexBuffer == nullptr && _indexBuffer == nullptr) { - size_t stride = sizeof(VertexInfo); + size_t stride = sizeof(V3F_T2F_C4F); _vertexBuffer = backend::DriverBase::getInstance()->newBuffer(stride * 8 * particleSystem->getParticleQuota(), backend::BufferType::VERTEX, backend::BufferUsage::DYNAMIC); @@ -736,44 +736,44 @@ void PUParticle3DBoxRender::render(Renderer* renderer, const Mat4& transform, Pa val = texRot * Vec3(0.0f, 0.75f, 0.0); _vertices[vertexindex + 0].position = particle->position + Vec3(-halfWidth, -halfHeight, halfDepth); _vertices[vertexindex + 0].color = particle->color; - _vertices[vertexindex + 0].uv.x = val.x; - _vertices[vertexindex + 0].uv.y = val.y; + _vertices[vertexindex + 0].texCoord.x = val.x; + _vertices[vertexindex + 0].texCoord.y = val.y; val = texRot * Vec3(0.0f, 0.25f, 0.0); _vertices[vertexindex + 1].position = particle->position + Vec3(halfWidth, -halfHeight, halfDepth); _vertices[vertexindex + 1].color = particle->color; - _vertices[vertexindex + 1].uv.x = val.x; - _vertices[vertexindex + 1].uv.y = val.y; + _vertices[vertexindex + 1].texCoord.x = val.x; + _vertices[vertexindex + 1].texCoord.y = val.y; val = texRot * Vec3(0.5f, 0.25f, 0.0); _vertices[vertexindex + 2].position = particle->position + Vec3(halfWidth, halfHeight, halfDepth); _vertices[vertexindex + 2].color = particle->color; - _vertices[vertexindex + 2].uv.x = val.x; - _vertices[vertexindex + 2].uv.y = val.y; + _vertices[vertexindex + 2].texCoord.x = val.x; + _vertices[vertexindex + 2].texCoord.y = val.y; val = texRot * Vec3(0.5f, 0.75f, 0.0); _vertices[vertexindex + 3].position = particle->position + Vec3(-halfWidth, halfHeight, halfDepth); _vertices[vertexindex + 3].color = particle->color; - _vertices[vertexindex + 3].uv.x = val.x; - _vertices[vertexindex + 3].uv.y = val.y; + _vertices[vertexindex + 3].texCoord.x = val.x; + _vertices[vertexindex + 3].texCoord.y = val.y; val = texRot * Vec3(0.0f, 0.0f, 0.0); _vertices[vertexindex + 4].position = particle->position + Vec3(halfWidth, -halfHeight, -halfDepth); _vertices[vertexindex + 4].color = particle->color; - _vertices[vertexindex + 4].uv.x = val.x; - _vertices[vertexindex + 4].uv.y = val.y; + _vertices[vertexindex + 4].texCoord.x = val.x; + _vertices[vertexindex + 4].texCoord.y = val.y; val = texRot * Vec3(0.0f, 1.0f, 0.0); _vertices[vertexindex + 5].position = particle->position + Vec3(-halfWidth, -halfHeight, -halfDepth); _vertices[vertexindex + 5].color = particle->color; - _vertices[vertexindex + 5].uv.x = val.x; - _vertices[vertexindex + 5].uv.y = val.y; + _vertices[vertexindex + 5].texCoord.x = val.x; + _vertices[vertexindex + 5].texCoord.y = val.y; val = texRot * Vec3(0.5f, 1.0f, 0.0); _vertices[vertexindex + 6].position = particle->position + Vec3(-halfWidth, halfHeight, -halfDepth); _vertices[vertexindex + 6].color = particle->color; - _vertices[vertexindex + 6].uv.x = val.x; - _vertices[vertexindex + 6].uv.y = val.y; + _vertices[vertexindex + 6].texCoord.x = val.x; + _vertices[vertexindex + 6].texCoord.y = val.y; val = texRot * Vec3(0.5f, 0.0f, 0.0); _vertices[vertexindex + 7].position = particle->position + Vec3(halfWidth, halfHeight, -halfDepth); _vertices[vertexindex + 7].color = particle->color; - _vertices[vertexindex + 7].uv.x = val.x; - _vertices[vertexindex + 7].uv.y = val.y; + _vertices[vertexindex + 7].texCoord.x = val.x; + _vertices[vertexindex + 7].texCoord.y = val.y; vertexindex += 8; index += 36; @@ -891,7 +891,7 @@ void PUSphereRender::render(Renderer* renderer, const Mat4& transform, ParticleS unsigned int indexCount = 6 * _numberOfRings * (_numberOfSegments + 1); if (_vertexBuffer == nullptr && _indexBuffer == nullptr) { - size_t stride = sizeof(VertexInfo); + size_t stride = sizeof(V3F_T2F_C4F); _vertexBuffer = backend::DriverBase::getInstance()->newBuffer(stride * vertexCount * particleSystem->getParticleQuota(), backend::BufferType::VERTEX, backend::BufferUsage::DYNAMIC); @@ -937,11 +937,11 @@ void PUSphereRender::render(Renderer* renderer, const Mat4& transform, ParticleS for (unsigned int i = 0; i < vertexCount; ++i) { - val = texRot * Vec3(_vertexTemplate[vertexindex + i].uv.x, _vertexTemplate[vertexindex + i].uv.y, 0.0f); + val = texRot * Vec3(_vertexTemplate[vertexindex + i].texCoord.x, _vertexTemplate[vertexindex + i].texCoord.y, 0.0f); mat.transformPoint(_vertexTemplate[vertexindex + i].position, &_vertices[vertexindex + i].position); _vertices[vertexindex + i].color = particle->color; - _vertices[vertexindex + i].uv.x = val.x; - _vertices[vertexindex + i].uv.y = val.y; + _vertices[vertexindex + i].texCoord.x = val.x; + _vertices[vertexindex + i].texCoord.y = val.y; } vertexindex += vertexCount; index += indexCount; @@ -990,7 +990,7 @@ void PUSphereRender::buildBuffers(unsigned short count) for (unsigned int segment = 0; segment <= _numberOfSegments; segment++) { - VertexInfo vi; + V3F_T2F_C4F vi; float x0 = r0 * sinf(segment * stepSegmentAngle); float z0 = r0 * cosf(segment * stepSegmentAngle); @@ -998,11 +998,11 @@ void PUSphereRender::buildBuffers(unsigned short count) vi.position.set(x0, y0, z0); // Colour - vi.color = Vec4::ONE; + vi.color = Color::WHITE; // Texture Coordinates - vi.uv.x = (float)segment / (float)_numberOfSegments; - vi.uv.y = 1.0f - (float)ring / (float)_numberOfRings; + vi.texCoord.x = (float)segment / (float)_numberOfSegments; + vi.texCoord.y = 1.0f - (float)ring / (float)_numberOfRings; if (ring != _numberOfRings) { diff --git a/extensions/Particle3D/src/Particle3D/PU/PURender.h b/extensions/Particle3D/src/Particle3D/PU/PURender.h index c906d3230851..cb6639042bb3 100644 --- a/extensions/Particle3D/src/Particle3D/PU/PURender.h +++ b/extensions/Particle3D/src/Particle3D/PU/PURender.h @@ -78,13 +78,6 @@ class AX_EX_DLL PUParticle3DEntityRender : public PURender void onAfterDraw(); protected: - struct VertexInfo - { - Vec3 position; - Vec2 uv; - Vec4 color; - }; - MeshCommand _meshCommand; RenderState::StateBlock _stateBlock; @@ -93,7 +86,7 @@ class AX_EX_DLL PUParticle3DEntityRender : public PURender backend::Buffer* _indexBuffer = nullptr; // index buffer backend::Buffer* _vertexBuffer = nullptr; // vertex buffer - std::vector _vertices; + std::vector _vertices; std::vector _indices; std::string _texFile; @@ -172,7 +165,7 @@ class AX_EX_DLL PUParticle3DQuadRender : public PUParticle3DEntityRender protected: void getOriginOffset(int& offsetX, int& offsetY); void determineUVCoords(PUParticle3D* particle); - void fillVertex(unsigned short index, const Vec3& pos, const Vec4& color, const Vec2& uv); + void fillVertex(unsigned short index, const Vec3& pos, const Color& color, const Vec2& uv); void fillTriangle(unsigned short index, unsigned short v0, unsigned short v1, unsigned short v2); protected: @@ -245,7 +238,7 @@ class AX_EX_DLL PUSphereRender : public PUParticle3DEntityRender protected: unsigned short _numberOfRings; unsigned short _numberOfSegments; - std::vector _vertexTemplate; + std::vector _vertexTemplate; }; } diff --git a/extensions/Particle3D/src/Particle3D/PU/PURendererTranslator.cpp b/extensions/Particle3D/src/Particle3D/PU/PURendererTranslator.cpp index 013782ce7a50..7e2f30539b3b 100644 --- a/extensions/Particle3D/src/Particle3D/PU/PURendererTranslator.cpp +++ b/extensions/Particle3D/src/Particle3D/PU/PURendererTranslator.cpp @@ -580,7 +580,7 @@ void PURendererTranslator::translate(PUScriptCompiler* compiler, PUAbstractNode* // Property: initial_colour if (passValidateProperty(compiler, prop, token[TOKEN_INITIAL_COLOUR], VAL_COLOURVALUE)) { - Vec4 val; + Color val; if (getVector4(prop->values.begin(), prop->values.end(), &val)) { static_cast(_renderer)->setInitialColor(val); @@ -593,7 +593,7 @@ void PURendererTranslator::translate(PUScriptCompiler* compiler, PUAbstractNode* if (passValidateProperty(compiler, prop, token[TOKEN_RIBBONTRAIL_INITIAL_COLOUR], VAL_COLOURVALUE)) { - Vec4 val; + Color val; if (getVector4(prop->values.begin(), prop->values.end(), &val)) { static_cast(_renderer)->setInitialColor(val); @@ -605,7 +605,7 @@ void PURendererTranslator::translate(PUScriptCompiler* compiler, PUAbstractNode* // Property: colour_change if (passValidateProperty(compiler, prop, token[TOKEN_COLOUR_CHANGE], VAL_COLOURVALUE)) { - Vec4 val; + Color val; if (getVector4(prop->values.begin(), prop->values.end(), &val)) { static_cast(_renderer)->setColorChange(val); @@ -618,7 +618,7 @@ void PURendererTranslator::translate(PUScriptCompiler* compiler, PUAbstractNode* if (passValidateProperty(compiler, prop, token[TOKEN_RIBBONTRAIL_COLOUR_CHANGE], VAL_COLOURVALUE)) { - Vec4 val; + Color val; if (getVector4(prop->values.begin(), prop->values.end(), &val)) { static_cast(_renderer)->setColorChange(val); diff --git a/extensions/Particle3D/src/Particle3D/PU/PURibbonTrail.cpp b/extensions/Particle3D/src/Particle3D/PU/PURibbonTrail.cpp index 98ddfa5b593e..b569e5bf9103 100644 --- a/extensions/Particle3D/src/Particle3D/PU/PURibbonTrail.cpp +++ b/extensions/Particle3D/src/Particle3D/PU/PURibbonTrail.cpp @@ -34,6 +34,11 @@ #include "2d/Camera.h" #include "3d/MeshRenderer.h" +#if defined(_WIN32) +# pragma push_macro("TRANSPARENT") +# undef TRANSPARENT +#endif + namespace ax { @@ -137,8 +142,8 @@ void PURibbonTrail::setNumberOfChains(size_t numChains) PUBillboardChain::setNumberOfChains(numChains); - _initialColor.resize(numChains, Vec4::ONE); - _deltaColor.resize(numChains, Vec4::ZERO); + _initialColor.resize(numChains, Color::WHITE); + _deltaColor.resize(numChains, Color::TRANSPARENT); _initialWidth.resize(numChains, 10); _deltaWidth.resize(numChains, 0); @@ -175,7 +180,7 @@ void PURibbonTrail::clearChain(size_t chainIndex) } } //----------------------------------------------------------------------- -void PURibbonTrail::setInitialColour(size_t chainIndex, const Vec4& col) +void PURibbonTrail::setInitialColour(size_t chainIndex, const Color& col) { setInitialColour(chainIndex, col.x, col.y, col.z, col.w); } @@ -189,7 +194,7 @@ void PURibbonTrail::setInitialColour(size_t chainIndex, float r, float g, float _initialColor[chainIndex].w = a; } //----------------------------------------------------------------------- -const Vec4& PURibbonTrail::getInitialColour(size_t chainIndex) const +const Color& PURibbonTrail::getInitialColour(size_t chainIndex) const { AXASSERT(chainIndex < _chainCount, "chainIndex out of bounds"); return _initialColor[chainIndex]; @@ -207,7 +212,7 @@ float PURibbonTrail::getInitialWidth(size_t chainIndex) const return _initialWidth[chainIndex]; } //----------------------------------------------------------------------- -void PURibbonTrail::setColourChange(size_t chainIndex, const Vec4& valuePerSecond) +void PURibbonTrail::setColourChange(size_t chainIndex, const Color& valuePerSecond) { setColourChange(chainIndex, valuePerSecond.x, valuePerSecond.y, valuePerSecond.z, valuePerSecond.w); } @@ -223,7 +228,7 @@ void PURibbonTrail::setColourChange(size_t chainIndex, float r, float g, float b manageController(); } //----------------------------------------------------------------------- -const Vec4& PURibbonTrail::getColourChange(size_t chainIndex) const +const Color& PURibbonTrail::getColourChange(size_t chainIndex) const { AXASSERT(chainIndex < _chainCount, "chainIndex out of bounds"); return _deltaColor[chainIndex]; @@ -247,7 +252,7 @@ void PURibbonTrail::manageController() _needTimeUpdate = false; for (size_t i = 0; i < _chainCount; ++i) { - if (_deltaWidth[i] != 0 || _deltaColor[i] != Vec4::ZERO) + if (_deltaWidth[i] != 0 || !_deltaColor[i].equals(Color::TRANSPARENT)) { _needTimeUpdate = true; break; @@ -421,3 +426,7 @@ void PURibbonTrail::update(float deltaTime) } } + +#if defined(_WIN32) +# pragma pop_macro("TRANSPARENT") +#endif diff --git a/extensions/Particle3D/src/Particle3D/PU/PURibbonTrail.h b/extensions/Particle3D/src/Particle3D/PU/PURibbonTrail.h index 88d2d917ee7d..3799eacb360e 100644 --- a/extensions/Particle3D/src/Particle3D/PU/PURibbonTrail.h +++ b/extensions/Particle3D/src/Particle3D/PU/PURibbonTrail.h @@ -94,7 +94,7 @@ class PURibbonTrail : public PUBillboardChain @note Only used if this instance is using vertex colours. */ - virtual void setInitialColour(size_t chainIndex, const Vec4& col); + virtual void setInitialColour(size_t chainIndex, const Color& col); /** Set the starting ribbon colour. @param chainIndex The index of the chain @param r,b,g,a The initial colour @@ -103,13 +103,13 @@ class PURibbonTrail : public PUBillboardChain */ virtual void setInitialColour(size_t chainIndex, float r, float g, float b, float a = 1.0); /** Get the starting ribbon colour. */ - virtual const Vec4& getInitialColour(size_t chainIndex) const; + virtual const Color& getInitialColour(size_t chainIndex) const; /** Enables / disables fading the trail using colour. @param chainIndex The index of the chain @param valuePerSecond The amount to subtract from colour each second */ - virtual void setColourChange(size_t chainIndex, const Vec4& valuePerSecond); + virtual void setColourChange(size_t chainIndex, const Color& valuePerSecond); /** Set the starting ribbon width in world units. @param chainIndex The index of the chain @@ -134,7 +134,7 @@ class PURibbonTrail : public PUBillboardChain virtual void setColourChange(size_t chainIndex, float r, float g, float b, float a); /** Get the per-second fading amount */ - virtual const Vec4& getColourChange(size_t chainIndex) const; + virtual const Color& getColourChange(size_t chainIndex) const; void update(float deltaTime); @@ -177,7 +177,7 @@ class PURibbonTrail : public PUBillboardChain float _elemLength; /// Squared length of each element float _squaredElemLength; - typedef std::vector ColorValueList; + typedef std::vector ColorValueList; typedef std::vector RealList; /// Initial colour of the ribbon ColorValueList _initialColor; diff --git a/extensions/Particle3D/src/Particle3D/PU/PURibbonTrailRender.cpp b/extensions/Particle3D/src/Particle3D/PU/PURibbonTrailRender.cpp index b5af038f3587..d23edd03926b 100644 --- a/extensions/Particle3D/src/Particle3D/PU/PURibbonTrailRender.cpp +++ b/extensions/Particle3D/src/Particle3D/PU/PURibbonTrailRender.cpp @@ -192,22 +192,22 @@ void PURibbonTrailRender::setRandomInitialColor(bool randomInitialColour) _randomInitialColor = randomInitialColour; } //----------------------------------------------------------------------- -const Vec4& PURibbonTrailRender::getInitialColor() const +const Color& PURibbonTrailRender::getInitialColor() const { return _initialColor; } //----------------------------------------------------------------------- -void PURibbonTrailRender::setInitialColor(const Vec4& initialColour) +void PURibbonTrailRender::setInitialColor(const Color& initialColour) { _initialColor = initialColour; } //----------------------------------------------------------------------- -const Vec4& PURibbonTrailRender::getColorChange() const +const Color& PURibbonTrailRender::getColorChange() const { return _colorChange; } //----------------------------------------------------------------------- -void PURibbonTrailRender::setColorChange(const Vec4& colourChange) +void PURibbonTrailRender::setColorChange(const Color& colourChange) { _colorChange = colourChange; } diff --git a/extensions/Particle3D/src/Particle3D/PU/PURibbonTrailRender.h b/extensions/Particle3D/src/Particle3D/PU/PURibbonTrailRender.h index 7ac35b86599f..be7f47d23e7a 100644 --- a/extensions/Particle3D/src/Particle3D/PU/PURibbonTrailRender.h +++ b/extensions/Particle3D/src/Particle3D/PU/PURibbonTrailRender.h @@ -112,11 +112,11 @@ class AX_EX_DLL PURibbonTrailRender : public PURender, public PUListener bool isRandomInitialColor() const; void setRandomInitialColor(bool randomInitialColour); - const Vec4& getInitialColor() const; - void setInitialColor(const Vec4& initialColour); + const Color& getInitialColor() const; + void setInitialColor(const Color& initialColour); - const Vec4& getColorChange() const; - void setColorChange(const Vec4& colourChange); + const Color& getColorChange() const; + void setColorChange(const Color& colourChange); /** Deletes all ChildSceneNodes en Entities. */ @@ -143,8 +143,8 @@ class AX_EX_DLL PURibbonTrailRender : public PURender, public PUListener bool _randomInitialColor; bool _setLength; bool _setWidth; - Vec4 _initialColor; - Vec4 _colorChange; + Color _initialColor; + Color _colorChange; Node* _childNode; std::string _texFile; }; diff --git a/extensions/Particle3D/src/Particle3D/PU/PUScriptTranslator.cpp b/extensions/Particle3D/src/Particle3D/PU/PUScriptTranslator.cpp index e73431be83ad..f3e979b8f279 100644 --- a/extensions/Particle3D/src/Particle3D/PU/PUScriptTranslator.cpp +++ b/extensions/Particle3D/src/Particle3D/PU/PUScriptTranslator.cpp @@ -444,7 +444,7 @@ bool PUScriptTranslator::getVector3(PUAbstractNodeList::const_iterator i, //------------------------------------------------------------------------- bool PUScriptTranslator::getVector4(PUAbstractNodeList::const_iterator i, PUAbstractNodeList::const_iterator end, - Vec4* result, + Color* result, int maxEntries) { int n = 0; @@ -715,7 +715,7 @@ bool PUScriptTranslator::passValidatePropertyValidVector3(PUScriptCompiler* /*co //------------------------------------------------------------------------- bool PUScriptTranslator::passValidatePropertyValidVector4(PUScriptCompiler* /*compiler*/, PUPropertyAbstractNode* prop) { - Vec4 val; + Color val; if (getVector4(prop->values.begin(), prop->values.end(), &val)) { return true; diff --git a/extensions/Particle3D/src/Particle3D/PU/PUScriptTranslator.h b/extensions/Particle3D/src/Particle3D/PU/PUScriptTranslator.h index 9287f3ac31ef..74bfe567c50e 100644 --- a/extensions/Particle3D/src/Particle3D/PU/PUScriptTranslator.h +++ b/extensions/Particle3D/src/Particle3D/PU/PUScriptTranslator.h @@ -666,7 +666,7 @@ class PUScriptTranslator */ bool getVector4(PUAbstractNodeList::const_iterator i, PUAbstractNodeList::const_iterator end, - Vec4* result, + Color* result, int maxEntries = 4); /** Parse Quaternion diff --git a/extensions/Particle3D/src/Particle3D/Particle3DRender.cpp b/extensions/Particle3D/src/Particle3D/Particle3DRender.cpp index d30404b021d8..8453e935589b 100644 --- a/extensions/Particle3D/src/Particle3D/Particle3DRender.cpp +++ b/extensions/Particle3D/src/Particle3D/Particle3DRender.cpp @@ -77,7 +77,7 @@ void Particle3DQuadRender::render(Renderer* renderer, const Mat4& transform, Par if (_vertexBuffer == nullptr) { - size_t stride = sizeof(Particle3DQuadRender::posuvcolor); + size_t stride = sizeof(V3F_T2F_C4F); _vertexBuffer = backend::DriverBase::getInstance()->newBuffer(stride * 4 * particleSystem->getParticleQuota(), backend::BufferType::VERTEX, backend::BufferUsage::DYNAMIC); @@ -127,19 +127,19 @@ void Particle3DQuadRender::render(Renderer* renderer, const Mat4& transform, Par position = particle->position; _posuvcolors[vertexindex].position = (position + (-halfwidth - halfheight)); _posuvcolors[vertexindex].color = particle->color; - _posuvcolors[vertexindex].uv.set(particle->lb_uv); + _posuvcolors[vertexindex].texCoord.set(particle->lb_uv); _posuvcolors[vertexindex + 1].position = (position + (halfwidth - halfheight)); _posuvcolors[vertexindex + 1].color = particle->color; - _posuvcolors[vertexindex + 1].uv.set(particle->rt_uv.x, particle->lb_uv.y); + _posuvcolors[vertexindex + 1].texCoord.set(particle->rt_uv.x, particle->lb_uv.y); _posuvcolors[vertexindex + 2].position = (position + (-halfwidth + halfheight)); _posuvcolors[vertexindex + 2].color = particle->color; - _posuvcolors[vertexindex + 2].uv.set(particle->lb_uv.x, particle->rt_uv.y); + _posuvcolors[vertexindex + 2].texCoord.set(particle->lb_uv.x, particle->rt_uv.y); _posuvcolors[vertexindex + 3].position = (position + (halfwidth + halfheight)); _posuvcolors[vertexindex + 3].color = particle->color; - _posuvcolors[vertexindex + 3].uv.set(particle->rt_uv); + _posuvcolors[vertexindex + 3].texCoord.set(particle->rt_uv); _indexData[index] = vertexindex; _indexData[index + 1] = vertexindex + 1; diff --git a/extensions/Particle3D/src/Particle3D/Particle3DRender.h b/extensions/Particle3D/src/Particle3D/Particle3DRender.h index b52b87cc5741..7e4e00e3cf6d 100644 --- a/extensions/Particle3D/src/Particle3D/Particle3DRender.h +++ b/extensions/Particle3D/src/Particle3D/Particle3DRender.h @@ -119,14 +119,7 @@ class AX_EX_DLL Particle3DQuadRender : public Particle3DRender backend::Buffer* _indexBuffer = nullptr; // index buffer backend::Buffer* _vertexBuffer = nullptr; // vertex buffer - struct posuvcolor - { - Vec3 position; - Vec2 uv; - Vec4 color; - }; - - std::vector _posuvcolors; // vertex data + std::vector _posuvcolors; // vertex data std::vector _indexData; // index data std::string _texFile; diff --git a/extensions/Particle3D/src/Particle3D/ParticleSystem3D.h b/extensions/Particle3D/src/Particle3D/ParticleSystem3D.h index 9896fb518876..6ca41d37ddb3 100644 --- a/extensions/Particle3D/src/Particle3D/ParticleSystem3D.h +++ b/extensions/Particle3D/src/Particle3D/ParticleSystem3D.h @@ -50,7 +50,7 @@ struct AX_EX_DLL Particle3D // property of particles Vec3 position; // position Quaternion orientation; // Orientation of the particle. - Vec4 color; // particle color + Color color; // particle color Vec2 lb_uv; // left bottom uv Vec2 rt_uv; // right top uv float width; // Own width diff --git a/extensions/README.md b/extensions/README.md index 8569d12ec018..f84a01df3baa 100644 --- a/extensions/README.md +++ b/extensions/README.md @@ -65,7 +65,7 @@ ## physics-nodes - Upstream: https://github.com/axmolengine/axmol -- Version: axmol-1.0 +- Version: axmol-3.0 - License: MIT ## scripting/lua diff --git a/extensions/axmol-ext.h b/extensions/axmol-ext.h index 7ae623f9e81a..fe7262051285 100644 --- a/extensions/axmol-ext.h +++ b/extensions/axmol-ext.h @@ -32,11 +32,7 @@ // Physics integration #include "physics-nodes/src/physics-nodes/PhysicsDebugNode.h" -#include "physics-nodes/src/physics-nodes/PhysicsDebugNodeBox2D.h" -#include "physics-nodes/src/physics-nodes/PhysicsDebugNodeChipmunk2D.h" #include "physics-nodes/src/physics-nodes/PhysicsSprite.h" -#include "physics-nodes/src/physics-nodes/PhysicsSpriteBox2D.h" -#include "physics-nodes/src/physics-nodes/PhysicsSpriteChipmunk2D.h" #include "assets-manager/src/assets-manager/AssetsManager.h" #include "assets-manager/src/assets-manager/AssetsManagerEx.h" diff --git a/extensions/cocostudio/src/cocostudio/ActionTimeline/BoneNode.cpp b/extensions/cocostudio/src/cocostudio/ActionTimeline/BoneNode.cpp index 65160e2aca20..37ca81de8fc9 100644 --- a/extensions/cocostudio/src/cocostudio/ActionTimeline/BoneNode.cpp +++ b/extensions/cocostudio/src/cocostudio/ActionTimeline/BoneNode.cpp @@ -334,7 +334,7 @@ void BoneNode::setDebugDrawEnabled(bool isDebugDraw) _isRackShow = isDebugDraw; } -void BoneNode::setDebugDrawColor(const ax::Color4F& color) +void BoneNode::setDebugDrawColor(const ax::Color& color) { _rackColor = color; updateColor(); diff --git a/extensions/cocostudio/src/cocostudio/ActionTimeline/BoneNode.h b/extensions/cocostudio/src/cocostudio/ActionTimeline/BoneNode.h index 10aa80f30421..71bad0a1351e 100644 --- a/extensions/cocostudio/src/cocostudio/ActionTimeline/BoneNode.h +++ b/extensions/cocostudio/src/cocostudio/ActionTimeline/BoneNode.h @@ -128,8 +128,8 @@ class CCS_DLL BoneNode : public ax::Node, public ax::BlendProtocol virtual float getDebugDrawWidth() const { return _rackWidth; } // bone's debug draw's width - virtual void setDebugDrawColor(const ax::Color4F& color); - virtual ax::Color4F getDebugDrawColor() const { return _rackColor; } + virtual void setDebugDrawColor(const ax::Color& color); + virtual ax::Color getDebugDrawColor() const { return _rackColor; } // get bone's bounding box, depends on getVisibleSkinsRect, apply on node to parent's transform ax::Rect getBoundingBox() const override; @@ -218,7 +218,7 @@ class CCS_DLL BoneNode : public ax::Node, public ax::BlendProtocol ax::BlendFunc _blendFunc = ax::BlendFunc::ALPHA_NON_PREMULTIPLIED; bool _isRackShow = false; - ax::Color4F _rackColor = ax::Color4F::WHITE; + ax::Color _rackColor = ax::Color::WHITE; float _rackLength = 50.0f; float _rackWidth = 20.0f; @@ -229,7 +229,7 @@ class CCS_DLL BoneNode : public ax::Node, public ax::BlendProtocol private: struct VertexData { - ax::Color4F squareColor; + ax::Color squareColor; ax::Vec3 noMVPVertices; }; diff --git a/extensions/cocostudio/src/cocostudio/ActionTimeline/SkeletonNode.h b/extensions/cocostudio/src/cocostudio/ActionTimeline/SkeletonNode.h index eb93aac5eed2..b2f2ad150c97 100644 --- a/extensions/cocostudio/src/cocostudio/ActionTimeline/SkeletonNode.h +++ b/extensions/cocostudio/src/cocostudio/ActionTimeline/SkeletonNode.h @@ -92,7 +92,7 @@ class CCS_DLL SkeletonNode : public BoneNode struct VertexData { ax::Vec3 vertex; - ax::Color4F color; + ax::Color color; }; ax::Vec2 _squareVertices[8]; diff --git a/extensions/cocostudio/src/cocostudio/Datas.cpp b/extensions/cocostudio/src/cocostudio/Datas.cpp index 5a51fa5de0cf..fc411cffa436 100644 --- a/extensions/cocostudio/src/cocostudio/Datas.cpp +++ b/extensions/cocostudio/src/cocostudio/Datas.cpp @@ -123,7 +123,7 @@ void BaseData::subtract(BaseData* from, BaseData* to, bool limit) } } -void BaseData::setColor(const Color4B& color) +void BaseData::setColor(const Color32& color) { r = color.r; g = color.g; @@ -131,9 +131,9 @@ void BaseData::setColor(const Color4B& color) a = color.a; } -Color4B BaseData::getColor() +Color32 BaseData::getColor() { - return Color4B(r, g, b, a); + return Color32(r, g, b, a); } std::string DisplayData::changeDisplayToTexture(std::string_view displayName) diff --git a/extensions/cocostudio/src/cocostudio/Datas.h b/extensions/cocostudio/src/cocostudio/Datas.h index 1fb296f28328..0c01849d9e89 100644 --- a/extensions/cocostudio/src/cocostudio/Datas.h +++ b/extensions/cocostudio/src/cocostudio/Datas.h @@ -95,8 +95,8 @@ class CCS_DLL BaseData : public ax::Object */ virtual void subtract(BaseData* from, BaseData* to, bool limit); - virtual void setColor(const ax::Color4B& color); - virtual ax::Color4B getColor(); + virtual void setColor(const ax::Color32& color); + virtual ax::Color32 getColor(); public: float x; //! position x attribute diff --git a/extensions/cocostudio/src/cocostudio/Skin.cpp b/extensions/cocostudio/src/cocostudio/Skin.cpp index 25e481f67628..cabc892fdc02 100644 --- a/extensions/cocostudio/src/cocostudio/Skin.cpp +++ b/extensions/cocostudio/src/cocostudio/Skin.cpp @@ -145,10 +145,10 @@ void Skin::updateTransform() // If it is not visible, or one of its ancestors is not visible, then do nothing: if (!_visible) { - _quad.br.vertices.setZero(); - _quad.tl.vertices.setZero(); - _quad.tr.vertices.setZero(); - _quad.bl.vertices.setZero(); + _quad.br.position.setZero(); + _quad.tl.position.setZero(); + _quad.tr.position.setZero(); + _quad.bl.position.setZero(); } else { @@ -193,10 +193,10 @@ void Skin::updateTransform() float dx = x1 * cr - y2 * sr2 + x; float dy = x1 * sr + y2 * cr2 + y; - _quad.bl.vertices.set(RENDER_IN_SUBPIXEL(ax), RENDER_IN_SUBPIXEL(ay), _positionZ); - _quad.br.vertices.set(RENDER_IN_SUBPIXEL(bx), RENDER_IN_SUBPIXEL(by), _positionZ); - _quad.tl.vertices.set(RENDER_IN_SUBPIXEL(dx), RENDER_IN_SUBPIXEL(dy), _positionZ); - _quad.tr.vertices.set(RENDER_IN_SUBPIXEL(cx), RENDER_IN_SUBPIXEL(cy), _positionZ); + _quad.bl.position.set(RENDER_IN_SUBPIXEL(ax), RENDER_IN_SUBPIXEL(ay), _positionZ); + _quad.br.position.set(RENDER_IN_SUBPIXEL(bx), RENDER_IN_SUBPIXEL(by), _positionZ); + _quad.tl.position.set(RENDER_IN_SUBPIXEL(dx), RENDER_IN_SUBPIXEL(dy), _positionZ); + _quad.tr.position.set(RENDER_IN_SUBPIXEL(cx), RENDER_IN_SUBPIXEL(cy), _positionZ); } // MARMALADE CHANGE: ADDED CHECK FOR nullptr, TO PERMIT SPRITES WITH NO BATCH NODE / TEXTURE ATLAS diff --git a/extensions/cocostudio/src/cocostudio/WidgetReader/ButtonReader/ButtonReader.cpp b/extensions/cocostudio/src/cocostudio/WidgetReader/ButtonReader/ButtonReader.cpp index 60bee16ff977..69e9082fd658 100644 --- a/extensions/cocostudio/src/cocostudio/WidgetReader/ButtonReader/ButtonReader.cpp +++ b/extensions/cocostudio/src/cocostudio/WidgetReader/ButtonReader/ButtonReader.cpp @@ -237,7 +237,7 @@ Offset ButtonReader::createOptionsWithFlatBuffers(pugi::xml_node objectDa int fontSize = 14; std::string fontName; ax::Size scale9Size; - Color4B textColor(255, 255, 255, 255); + Color32 textColor(255, 255, 255, 255); std::string normalPath; std::string normalPlistFile; @@ -256,15 +256,15 @@ Offset
ButtonReader::createOptionsWithFlatBuffers(pugi::xml_node objectDa int fontResourceResourceType = 0; bool outlineEnabled = false; - Color4B outlineColor = Color4B::BLACK; + Color32 outlineColor = Color32::BLACK; int outlineSize = 1; bool shadowEnabled = false; - Color4B shadowColor = Color4B::BLACK; + Color32 shadowColor = Color32::BLACK; Size shadowOffset = Size(2, -2); int shadowBlurRadius = 0; bool glowEnabled = false; - Color4B glowColor = Color4B::BLACK; + Color32 glowColor = Color32::BLACK; bool boldEnabled = false, underlineEnabled = false, italicsEnabled = false, strikethroughEnabled = false; @@ -892,7 +892,7 @@ void ButtonReader::setPropsWithFlatBuffers(ax::Node* node, const flatbuffers::Ta auto f_outlineColor = options->outlineColor(); if (f_outlineColor) { - Color4B outlineColor(f_outlineColor->r(), f_outlineColor->g(), f_outlineColor->b(), f_outlineColor->a()); + Color32 outlineColor(f_outlineColor->r(), f_outlineColor->g(), f_outlineColor->b(), f_outlineColor->a()); auto label = button->getTitleRenderer(); label->enableOutline(outlineColor, options->outlineSize()); } @@ -904,7 +904,7 @@ void ButtonReader::setPropsWithFlatBuffers(ax::Node* node, const flatbuffers::Ta auto f_shadowColor = options->shadowColor(); if (f_shadowColor) { - Color4B shadowColor(f_shadowColor->r(), f_shadowColor->g(), f_shadowColor->b(), f_shadowColor->a()); + Color32 shadowColor(f_shadowColor->r(), f_shadowColor->g(), f_shadowColor->b(), f_shadowColor->a()); auto label = button->getTitleRenderer(); label->enableShadow(shadowColor, Size(options->shadowOffsetX(), options->shadowOffsetY()), options->shadowBlurRadius()); @@ -916,7 +916,7 @@ void ButtonReader::setPropsWithFlatBuffers(ax::Node* node, const flatbuffers::Ta auto f_glowColor = options->glowColor(); if (f_glowColor) { - Color4B glowColor(f_glowColor->r(), f_glowColor->g(), f_glowColor->b(), f_glowColor->a()); + Color32 glowColor(f_glowColor->r(), f_glowColor->g(), f_glowColor->b(), f_glowColor->a()); auto label = button->getTitleRenderer(); label->enableGlow(glowColor); } diff --git a/extensions/cocostudio/src/cocostudio/WidgetReader/Node3DReader/Node3DReader.cpp b/extensions/cocostudio/src/cocostudio/WidgetReader/Node3DReader/Node3DReader.cpp index 9ebc6c12abbc..4502cb5ddbbd 100644 --- a/extensions/cocostudio/src/cocostudio/WidgetReader/Node3DReader/Node3DReader.cpp +++ b/extensions/cocostudio/src/cocostudio/WidgetReader/Node3DReader/Node3DReader.cpp @@ -105,7 +105,7 @@ Offset
Node3DReader::createOptionsWithFlatBuffersForNode(pugi::xml_node o Vec2 position = Vec2::ZERO; Vec2 scale = Vec2(1.0f, 1.0f); Vec2 anchorPoint = Vec2::ZERO; - Color4B color(255, 255, 255, 255); + Color32 color(255, 255, 255, 255); Vec2 size = Vec2::ZERO; bool flipX = false; diff --git a/extensions/cocostudio/src/cocostudio/WidgetReader/NodeReader/NodeReader.cpp b/extensions/cocostudio/src/cocostudio/WidgetReader/NodeReader/NodeReader.cpp index f929f262a0ef..ec5b0743a1f6 100644 --- a/extensions/cocostudio/src/cocostudio/WidgetReader/NodeReader/NodeReader.cpp +++ b/extensions/cocostudio/src/cocostudio/WidgetReader/NodeReader/NodeReader.cpp @@ -90,7 +90,7 @@ Offset
NodeReader::createOptionsWithFlatBuffers(pugi::xml_node objectData Vec2 position; Vec2 scale(1.0f, 1.0f); Vec2 anchorPoint; - Color4B color(255, 255, 255, 255); + Color32 color(255, 255, 255, 255); Vec2 size; bool flipX = false; diff --git a/extensions/cocostudio/src/cocostudio/WidgetReader/TabControlReader/TabControlReader.cpp b/extensions/cocostudio/src/cocostudio/WidgetReader/TabControlReader/TabControlReader.cpp index 0d699e750482..0055b45351c8 100644 --- a/extensions/cocostudio/src/cocostudio/WidgetReader/TabControlReader/TabControlReader.cpp +++ b/extensions/cocostudio/src/cocostudio/WidgetReader/TabControlReader/TabControlReader.cpp @@ -229,7 +229,7 @@ flatbuffers::Offset TabHeaderReader::createOptionsWithFlatBu int fontsize = 12; std::string text; - ax::Color4B textColor(255, 255, 255, 255); + ax::Color32 textColor(255, 255, 255, 255); std::string fontName; int backgroundboxResourceType = 0; @@ -533,7 +533,7 @@ void TabHeaderReader::setPropsWithFlatBuffers(ax::Node* node, const flatbuffers: header->setTitleFontSize(options->fontSize()); header->setTitleText(options->titleText()->c_str()); auto textColor = options->textColor(); - Color4B titleColor(textColor->r(), textColor->g(), textColor->b(), textColor->a()); + Color32 titleColor(textColor->r(), textColor->g(), textColor->b(), textColor->a()); header->setTitleColor(titleColor); auto resourceData = options->fontRes(); diff --git a/extensions/cocostudio/src/cocostudio/WidgetReader/TextFieldReader/TextFieldExReader.cpp b/extensions/cocostudio/src/cocostudio/WidgetReader/TextFieldReader/TextFieldExReader.cpp index 48234ab18540..7188cfe10906 100644 --- a/extensions/cocostudio/src/cocostudio/WidgetReader/TextFieldReader/TextFieldExReader.cpp +++ b/extensions/cocostudio/src/cocostudio/WidgetReader/TextFieldReader/TextFieldExReader.cpp @@ -16,9 +16,9 @@ using namespace ax; using namespace ui; using namespace flatbuffers; -inline Color4B Color4BFromFb(const FColor* pColor) +inline Color32 Color32FromFb(const FColor* pColor) { - return Color4B(pColor->r(), pColor->g(), pColor->b(), pColor->a()); + return Color32(pColor->r(), pColor->g(), pColor->b(), pColor->a()); } inline Color3B Color3BFromFb(const FColor* pColor) @@ -82,9 +82,9 @@ Offset
TextFieldExReader::createOptionsWithFlatBuffers(pugi::xml_node obj int maxLength = 10; bool isEnabled = true; bool isEditable = true; - Color4B textColor; - Color4B placeholderColor; - Color4B cursorColor; + Color32 textColor; + Color32 placeholderColor; + Color32 cursorColor; // attributes auto attribute = objectData.first_attribute(); @@ -335,8 +335,8 @@ void TextFieldExReader::setPropsWithFlatBuffers(ax::Node* node, const flatbuffer }*/ textField->setString(text); - textField->setTextColor(Color4BFromFb(options->textColor())); - textField->setPlaceholderColor(Color4BFromFb(options->placeholderColor())); + textField->setTextColor(Color32FromFb(options->textColor())); + textField->setPlaceholderColor(Color32FromFb(options->placeholderColor())); textField->setCursorColor(Color3BFromFb(options->cursorColor())); auto widgetReader = NodeReader::getInstance(); diff --git a/extensions/cocostudio/src/cocostudio/WidgetReader/TextReader/TextReader.cpp b/extensions/cocostudio/src/cocostudio/WidgetReader/TextReader/TextReader.cpp index ad096b02d489..efd0452df8e5 100644 --- a/extensions/cocostudio/src/cocostudio/WidgetReader/TextReader/TextReader.cpp +++ b/extensions/cocostudio/src/cocostudio/WidgetReader/TextReader/TextReader.cpp @@ -157,16 +157,16 @@ Offset
TextReader::createOptionsWithFlatBuffers(pugi::xml_node objectData int h_alignment = 0; int v_alignment = 0; bool outlineEnabled = false; - Color4B outlineColor = Color4B::BLACK; + Color32 outlineColor = Color32::BLACK; int outlineSize = 1; bool shadowEnabled = false; - Color4B shadowColor = Color4B::BLACK; + Color32 shadowColor = Color32::BLACK; Size shadowOffset = Size(2, -2); int shadowBlurRadius = 0; // since x-studio reader 10.0.593.0 bool glowEnabled = false; - Color4B glowColor = Color4B::BLACK; + Color32 glowColor = Color32::BLACK; bool boldEnabled = false, underlineEnabled = false, italicsEnabled = false, strikethroughEnabled = false; @@ -469,7 +469,7 @@ void TextReader::setPropsWithFlatBuffers(ax::Node* node, const flatbuffers::Tabl auto f_outlineColor = options->outlineColor(); if (f_outlineColor) { - Color4B outlineColor(f_outlineColor->r(), f_outlineColor->g(), f_outlineColor->b(), f_outlineColor->a()); + Color32 outlineColor(f_outlineColor->r(), f_outlineColor->g(), f_outlineColor->b(), f_outlineColor->a()); label->enableOutline(outlineColor, options->outlineSize()); } } @@ -480,7 +480,7 @@ void TextReader::setPropsWithFlatBuffers(ax::Node* node, const flatbuffers::Tabl auto f_shadowColor = options->shadowColor(); if (f_shadowColor) { - Color4B shadowColor(f_shadowColor->r(), f_shadowColor->g(), f_shadowColor->b(), f_shadowColor->a()); + Color32 shadowColor(f_shadowColor->r(), f_shadowColor->g(), f_shadowColor->b(), f_shadowColor->a()); label->enableShadow(shadowColor, Size(options->shadowOffsetX(), options->shadowOffsetY()), options->shadowBlurRadius()); } @@ -491,7 +491,7 @@ void TextReader::setPropsWithFlatBuffers(ax::Node* node, const flatbuffers::Tabl auto f_glowColor = options->glowColor(); if (f_glowColor) { - Color4B glowColor(f_glowColor->r(), f_glowColor->g(), f_glowColor->b(), f_glowColor->a()); + Color32 glowColor(f_glowColor->r(), f_glowColor->g(), f_glowColor->b(), f_glowColor->a()); label->enableGlow(glowColor); } } @@ -518,7 +518,7 @@ void TextReader::setPropsWithFlatBuffers(ax::Node* node, const flatbuffers::Tabl node->setColor(oldColor); auto optionsWidget = (WidgetOptions*)options->widgetOptions(); auto f_color = optionsWidget->color(); - Color4B color(f_color->r(), f_color->g(), f_color->b(), f_color->a()); + Color32 color(f_color->r(), f_color->g(), f_color->b(), f_color->a()); ((Text*)node)->setTextColor(color); label->setUnifySizeEnabled(false); diff --git a/extensions/cocostudio/src/cocostudio/WidgetReader/WidgetReader.cpp b/extensions/cocostudio/src/cocostudio/WidgetReader/WidgetReader.cpp index 41dcb3066eb2..4bade8d84664 100644 --- a/extensions/cocostudio/src/cocostudio/WidgetReader/WidgetReader.cpp +++ b/extensions/cocostudio/src/cocostudio/WidgetReader/WidgetReader.cpp @@ -417,7 +417,7 @@ Offset
WidgetReader::createOptionsWithFlatBuffers(pugi::xml_node objectDa Vec2 position; Vec2 scale(1.0f, 1.0f); Vec2 anchorPoint; - Color4B color(255, 255, 255, 255); + Color32 color(255, 255, 255, 255); Vec2 size; bool flipX = false; bool flipY = false; diff --git a/extensions/fairygui/src/fairygui/GGraph.cpp b/extensions/fairygui/src/fairygui/GGraph.cpp index d2223dbc188b..98dfd68ec0da 100644 --- a/extensions/fairygui/src/fairygui/GGraph.cpp +++ b/extensions/fairygui/src/fairygui/GGraph.cpp @@ -5,7 +5,7 @@ NS_FGUI_BEGIN using namespace ax; -static void drawVertRect(ax::DrawNode* shape, float x, float y, float width, float height, const ax::Color4B& color) +static void drawVertRect(ax::DrawNode* shape, float x, float y, float width, float height, const ax::Color& color) { float mx = x + width; float my = y + height; @@ -16,8 +16,8 @@ static void drawVertRect(ax::DrawNode* shape, float x, float y, float width, flo GGraph::GGraph() : _shape(nullptr), _type(0), _lineSize(1), - _lineColor(Color4B::BLACK), - _fillColor(Color4B::WHITE), + _lineColor(Color32::BLACK), + _fillColor(Color32::WHITE), _cornerRadius(nullptr), _polygonPoints(nullptr), _distances(nullptr) @@ -40,7 +40,7 @@ void GGraph::handleInit() _displayObject = _shape; } -void GGraph::drawRect(float aWidth, float aHeight, int lineSize, const ax::Color4B& lineColor, const ax::Color4B& fillColor) +void GGraph::drawRect(float aWidth, float aHeight, int lineSize, const ax::Color& lineColor, const ax::Color& fillColor) { _type = 0; //avoid updateshape call in handleSizeChange setSize(aWidth, aHeight); @@ -51,7 +51,7 @@ void GGraph::drawRect(float aWidth, float aHeight, int lineSize, const ax::Color updateShape(); } -void GGraph::drawEllipse(float aWidth, float aHeight, int lineSize, const ax::Color4B& lineColor, const ax::Color4B& fillColor) +void GGraph::drawEllipse(float aWidth, float aHeight, int lineSize, const ax::Color& lineColor, const ax::Color& fillColor) { _type = 0; //avoid updateshape call in handleSizeChange setSize(aWidth, aHeight); @@ -62,7 +62,7 @@ void GGraph::drawEllipse(float aWidth, float aHeight, int lineSize, const ax::Co updateShape(); } -void GGraph::drawPolygon(int lineSize, const ax::Color4B& lineColor, const ax::Color4B& fillColor, const ax::Vec2* points, int count) +void GGraph::drawPolygon(int lineSize, const ax::Color& lineColor, const ax::Color& fillColor, const ax::Vec2* points, int count) { _type = 3; _lineSize = lineSize; @@ -83,7 +83,7 @@ void GGraph::drawPolygon(int lineSize, const ax::Color4B& lineColor, const ax::C updateShape(); } -void GGraph::drawRegularPolygon(int lineSize, const ax::Color4B& lineColor, const ax::Color4B& fillColor, +void GGraph::drawRegularPolygon(int lineSize, const ax::Color& lineColor, const ax::Color& fillColor, int sides, float startAngle, const float* distances, int count) { _type = 4; @@ -189,7 +189,7 @@ ax::Color3B GGraph::getColor() const void GGraph::setColor(const ax::Color3B& value) { - _fillColor = Color4B(value, _fillColor.a); + _fillColor = ax::Color{value, _fillColor.a}; updateShape(); } @@ -247,8 +247,8 @@ void GGraph::setup_beforeAdd(ByteBuffer* buffer, int beginPos) if (_type != 0) { _lineSize = buffer->readInt(); - _lineColor = (Color4B)buffer->readColor(); - _fillColor = (Color4B)buffer->readColor(); + _lineColor = ax::Color{buffer->readColor()}; + _fillColor = ax::Color{buffer->readColor()}; if (buffer->readBool()) { _cornerRadius = new float[4]; diff --git a/extensions/fairygui/src/fairygui/GGraph.h b/extensions/fairygui/src/fairygui/GGraph.h index d2dc512690be..4906d18b0ec4 100644 --- a/extensions/fairygui/src/fairygui/GGraph.h +++ b/extensions/fairygui/src/fairygui/GGraph.h @@ -15,10 +15,10 @@ class GGraph : public GObject CREATE_FUNC(GGraph); - void drawRect(float aWidth, float aHeight, int lineSize, const ax::Color4B& lineColor, const ax::Color4B& fillColor); - void drawEllipse(float aWidth, float aHeight, int lineSize, const ax::Color4B& lineColor, const ax::Color4B& fillColor); - void drawPolygon(int lineSize, const ax::Color4B& lineColor, const ax::Color4B& fillColor, const ax::Vec2* points, int count); - void drawRegularPolygon(int lineSize, const ax::Color4B& lineColor, const ax::Color4B& fillColor, int sides, float startAngle = 0, const float* distances = nullptr, int distanceCount = 0); + void drawRect(float aWidth, float aHeight, int lineSize, const ax::Color& lineColor, const ax::Color& fillColor); + void drawEllipse(float aWidth, float aHeight, int lineSize, const ax::Color& lineColor, const ax::Color& fillColor); + void drawPolygon(int lineSize, const ax::Color& lineColor, const ax::Color& fillColor, const ax::Vec2* points, int count); + void drawRegularPolygon(int lineSize, const ax::Color& lineColor, const ax::Color& fillColor, int sides, float startAngle = 0, const float* distances = nullptr, int distanceCount = 0); bool isEmpty() const { return _type == 0; } ax::Color3B getColor() const; @@ -36,8 +36,8 @@ class GGraph : public GObject void updateShape(); int _type; - ax::Color4B _lineColor; - ax::Color4B _fillColor; + ax::Color _lineColor; + ax::Color _fillColor; int _lineSize; float* _cornerRadius; std::vector* _polygonPoints; diff --git a/extensions/fairygui/src/fairygui/GRoot.cpp b/extensions/fairygui/src/fairygui/GRoot.cpp index 71232cd2e906..c50033366617 100644 --- a/extensions/fairygui/src/fairygui/GRoot.cpp +++ b/extensions/fairygui/src/fairygui/GRoot.cpp @@ -167,7 +167,7 @@ void GRoot::createModalLayer() { _modalLayer = GGraph::create(); _modalLayer->retain(); - _modalLayer->drawRect(getWidth(), getHeight(), 0, Color4F::WHITE, UIConfig::modalLayerColor); + _modalLayer->drawRect(getWidth(), getHeight(), 0, ax::Color::WHITE, UIConfig::modalLayerColor); _modalLayer->addRelation(this, RelationType::Size); } diff --git a/extensions/fairygui/src/fairygui/Transition.cpp b/extensions/fairygui/src/fairygui/Transition.cpp index 2915549ce4b5..1e9bdfc05411 100644 --- a/extensions/fairygui/src/fairygui/Transition.cpp +++ b/extensions/fairygui/src/fairygui/Transition.cpp @@ -79,8 +79,8 @@ class TValue : public TValueBase void setVec2(const ax::Vec2& value); ax::Vec4 getVec4() const; void setVec4(const ax::Vec4& value); - ax::Color4B getColor() const; - void setColor(const ax::Color4B& value); + ax::Color32 getColor() const; + void setColor(const ax::Color32& value); }; TValue::TValue() @@ -114,12 +114,12 @@ void TValue::setVec4(const ax::Vec4& value) f4 = value.w; } -ax::Color4B TValue::getColor() const +ax::Color32 TValue::getColor() const { - return ax::Color4B(f1, f2, f3, f4); + return ax::Color32(f1, f2, f3, f4); } -void TValue::setColor(const ax::Color4B& value) +void TValue::setColor(const ax::Color32& value) { f1 = value.r; f2 = value.g; @@ -551,7 +551,7 @@ void Transition::setValue(const std::string& label, const ValueVector& values) case TransitionActionType::Color: { uint32_t v = values[0].asUnsignedInt(); - ((TValue*)value)->setColor(Color4B((v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF, (v >> 24) & 0xFF)); + ((TValue*)value)->setColor(Color32((v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF, (v >> 24) & 0xFF)); break; } diff --git a/extensions/fairygui/src/fairygui/UIConfig.cpp b/extensions/fairygui/src/fairygui/UIConfig.cpp index 99a9559835b8..fd27f0882f4d 100644 --- a/extensions/fairygui/src/fairygui/UIConfig.cpp +++ b/extensions/fairygui/src/fairygui/UIConfig.cpp @@ -19,7 +19,7 @@ int UIConfig::touchScrollSensitivity = 20; int UIConfig::defaultComboBoxVisibleItemCount = 10; std::string UIConfig::globalModalWaiting = ""; std::string UIConfig::tooltipsWin = ""; -Color4F UIConfig::modalLayerColor = Color4F(0, 0, 0, 0.4f); +ax::Color UIConfig::modalLayerColor = ax::Color(0, 0, 0, 0.4f); bool UIConfig::bringWindowToFrontOnClick = true; std::string UIConfig::windowModalWaiting = ""; std::string UIConfig::popupMenu = ""; diff --git a/extensions/fairygui/src/fairygui/UIConfig.h b/extensions/fairygui/src/fairygui/UIConfig.h index c9b0956d68ba..beaf72310d5c 100644 --- a/extensions/fairygui/src/fairygui/UIConfig.h +++ b/extensions/fairygui/src/fairygui/UIConfig.h @@ -24,7 +24,7 @@ class UIConfig static int touchScrollSensitivity; static int defaultComboBoxVisibleItemCount; static std::string globalModalWaiting; - static ax::Color4F modalLayerColor; + static ax::Color modalLayerColor; static std::string tooltipsWin; static bool bringWindowToFrontOnClick; static std::string windowModalWaiting; diff --git a/extensions/fairygui/src/fairygui/Window.cpp b/extensions/fairygui/src/fairygui/Window.cpp index 0bfa3d08030a..9d5df7faac57 100644 --- a/extensions/fairygui/src/fairygui/Window.cpp +++ b/extensions/fairygui/src/fairygui/Window.cpp @@ -102,7 +102,7 @@ void Window::setDragArea(GObject * value) { _dragArea->retain(); if (dynamic_cast(_dragArea) && ((GGraph*)_dragArea)->isEmpty()) - ((GGraph*)_dragArea)->drawRect(_dragArea->getWidth(), _dragArea->getHeight(), 0, Color4F(0, 0, 0, 0), Color4F(0, 0, 0, 0)); + ((GGraph*)_dragArea)->drawRect(_dragArea->getWidth(), _dragArea->getHeight(), 0, ax::Color(0, 0, 0, 0), ax::Color(0, 0, 0, 0)); _dragArea->setDraggable(true); _dragArea->addEventListener(UIEventType::DragStart, AX_CALLBACK_1(Window::onDragStart, this), EventTag(this)); } diff --git a/extensions/fairygui/src/fairygui/display/FUILabel.cpp b/extensions/fairygui/src/fairygui/display/FUILabel.cpp index 2fcc28b2f752..5ba4bffcf4b5 100644 --- a/extensions/fairygui/src/fairygui/display/FUILabel.cpp +++ b/extensions/fairygui/src/fairygui/display/FUILabel.cpp @@ -60,7 +60,7 @@ void FUILabel::applyTextFormat() } if (oldType == LabelType::BMFONT) - setTextColor((Color4B)_textFormat->color); + setTextColor((Color32)_textFormat->color); } } @@ -86,7 +86,7 @@ void FUILabel::applyTextFormat() if (_currentLabelType != LabelType::BMFONT || _bmFontCanTint) { - //setTextColor((Color4B)(_grayed ? toGrayed(_textFormat->color) : _textFormat->color)); + //setTextColor((Color32)(_grayed ? toGrayed(_textFormat->color) : _textFormat->color)); setColor(_grayed ? toGrayed(_textFormat->color) : _textFormat->color); } @@ -110,12 +110,12 @@ void FUILabel::applyTextFormat() setVerticalAlignment(_textFormat->verticalAlign); if (_textFormat->hasEffect(TextFormat::OUTLINE)) - enableOutline((Color4B)(_grayed ? toGrayed(_textFormat->outlineColor) : _textFormat->outlineColor), _textFormat->outlineSize); + enableOutline((Color32)(_grayed ? toGrayed(_textFormat->outlineColor) : _textFormat->outlineColor), _textFormat->outlineSize); else disableEffect(LabelEffect::OUTLINE); if (_textFormat->hasEffect(TextFormat::SHADOW)) - enableShadow((Color4B)(_grayed ? toGrayed(_textFormat->shadowColor) : _textFormat->shadowColor), _textFormat->shadowOffset); + enableShadow((Color32)(_grayed ? toGrayed(_textFormat->shadowColor) : _textFormat->shadowColor), _textFormat->shadowOffset); else if (!_textFormat->bold) disableEffect(LabelEffect::SHADOW); } @@ -164,15 +164,15 @@ void FUILabel::setGrayed(bool value) _grayed = value; if (_currentLabelType != LabelType::BMFONT) - setTextColor((Color4B)(_grayed ? toGrayed(_textFormat->color) : _textFormat->color)); + setTextColor((Color32)(_grayed ? toGrayed(_textFormat->color) : _textFormat->color)); else if (_bmFontCanTint) setColor(_grayed ? toGrayed(_textFormat->color) : _textFormat->color); if (_textFormat->hasEffect(TextFormat::OUTLINE)) - enableOutline((Color4B)(_grayed ? toGrayed(_textFormat->outlineColor) : _textFormat->outlineColor), _textFormat->outlineSize); + enableOutline((Color32)(_grayed ? toGrayed(_textFormat->outlineColor) : _textFormat->outlineColor), _textFormat->outlineSize); if (_textFormat->hasEffect(TextFormat::SHADOW)) - enableShadow((Color4B)(_grayed ? toGrayed(_textFormat->shadowColor) : _textFormat->shadowColor), _textFormat->shadowOffset); + enableShadow((Color32)(_grayed ? toGrayed(_textFormat->shadowColor) : _textFormat->shadowColor), _textFormat->shadowOffset); } } diff --git a/extensions/fairygui/src/fairygui/display/FUISprite.cpp b/extensions/fairygui/src/fairygui/display/FUISprite.cpp index e7b2badeadd9..16c8786f33cf 100644 --- a/extensions/fairygui/src/fairygui/display/FUISprite.cpp +++ b/extensions/fairygui/src/fairygui/display/FUISprite.cpp @@ -193,9 +193,9 @@ Tex2F FUISprite::textureCoordFromAlphaPoint(Vec2 alpha) { Tex2F ret(0.0f, 0.0f); - V3F_C4B_T2F_Quad quad = getQuad(); - Vec2 min(quad.bl.texCoords.u, quad.bl.texCoords.v); - Vec2 max(quad.tr.texCoords.u, quad.tr.texCoords.v); + auto& quad = getQuad(); + Vec2 min(quad.bl.texCoord.u, quad.bl.texCoord.v); + Vec2 max(quad.tr.texCoord.u, quad.tr.texCoord.v); // Fix bug #1303 so that progress timer handles sprite frame texture rotation if (isTextureRectRotated()) { @@ -208,9 +208,9 @@ Vec3 FUISprite::vertexFromAlphaPoint(Vec2 alpha) { Vec3 ret(0.0f, 0.0f, 0.0f); - V3F_C4B_T2F_Quad quad = getQuad(); - Vec2 min(quad.bl.vertices.x, quad.bl.vertices.y); - Vec2 max(quad.tr.vertices.x, quad.tr.vertices.y); + auto& quad = getQuad(); + Vec2 min(quad.bl.position.x, quad.bl.position.y); + Vec2 max(quad.tr.position.x, quad.tr.position.y); ret.x = min.x * (1.f - alpha.x) + max.x * alpha.x; ret.y = min.y * (1.f - alpha.y) + max.y * alpha.y; return ret; @@ -222,10 +222,10 @@ void FUISprite::updateColor(void) if (_vertexData) { - Color4B sc = getQuad().tl.colors; + auto& sc = getQuad().tl.color; for (int i = 0; i < _vertexDataCount; ++i) { - _vertexData[i].colors = sc; + _vertexData[i].color = sc; } } } @@ -344,7 +344,7 @@ void FUISprite::updateRadial(void) { _vertexDataCount = index + 3; triangleCount = _vertexDataCount - 2; - _vertexData = (V3F_C4B_T2F*)malloc(_vertexDataCount * sizeof(*_vertexData)); + _vertexData = (V3F_T2F_C4B*)malloc(_vertexDataCount * sizeof(*_vertexData)); _vertexIndex = (unsigned short *)malloc(triangleCount * 3 * sizeof(*_vertexIndex)); AXASSERT(_vertexData, "FUISprite. Not enough memory"); } @@ -360,23 +360,23 @@ void FUISprite::updateRadial(void) // First we populate the array with the _midpoint, then all // vertices/texcoords/colors of the 12 'o clock start and edges and the hitpoint - _vertexData[0].texCoords = textureCoordFromAlphaPoint(midpoint); - _vertexData[0].vertices = vertexFromAlphaPoint(midpoint); + _vertexData[0].texCoord = textureCoordFromAlphaPoint(midpoint); + _vertexData[0].position = vertexFromAlphaPoint(midpoint); - _vertexData[1].texCoords = textureCoordFromAlphaPoint(topMid); - _vertexData[1].vertices = vertexFromAlphaPoint(topMid); + _vertexData[1].texCoord = textureCoordFromAlphaPoint(topMid); + _vertexData[1].position = vertexFromAlphaPoint(topMid); for (int i = 0; i < index; ++i) { Vec2 alphaPoint = boundaryTexCoord(i); - _vertexData[i + 2].texCoords = textureCoordFromAlphaPoint(alphaPoint); - _vertexData[i + 2].vertices = vertexFromAlphaPoint(alphaPoint); + _vertexData[i + 2].texCoord = textureCoordFromAlphaPoint(alphaPoint); + _vertexData[i + 2].position = vertexFromAlphaPoint(alphaPoint); } } // hitpoint will go last - _vertexData[_vertexDataCount - 1].texCoords = textureCoordFromAlphaPoint(hit); - _vertexData[_vertexDataCount - 1].vertices = vertexFromAlphaPoint(hit); + _vertexData[_vertexDataCount - 1].texCoord = textureCoordFromAlphaPoint(hit); + _vertexData[_vertexDataCount - 1].position = vertexFromAlphaPoint(hit); for (int i = 0; i < triangleCount; i++) { _vertexIndex[i * 3] = 0; @@ -433,25 +433,25 @@ void FUISprite::updateBar(void) if (!_vertexData) { _vertexDataCount = 4; - _vertexData = (V3F_C4B_T2F*)malloc(_vertexDataCount * sizeof(*_vertexData)); + _vertexData = (V3F_T2F_C4B*)malloc(_vertexDataCount * sizeof(*_vertexData)); _vertexIndex = (unsigned short*)malloc(6 * sizeof(*_vertexIndex)); AXASSERT(_vertexData, "FUISprite. Not enough memory"); } // TOPLEFT - _vertexData[0].texCoords = textureCoordFromAlphaPoint(Vec2(min.x, max.y)); - _vertexData[0].vertices = vertexFromAlphaPoint(Vec2(min.x, max.y)); + _vertexData[0].texCoord = textureCoordFromAlphaPoint(Vec2(min.x, max.y)); + _vertexData[0].position = vertexFromAlphaPoint(Vec2(min.x, max.y)); // BOTLEFT - _vertexData[1].texCoords = textureCoordFromAlphaPoint(Vec2(min.x, min.y)); - _vertexData[1].vertices = vertexFromAlphaPoint(Vec2(min.x, min.y)); + _vertexData[1].texCoord = textureCoordFromAlphaPoint(Vec2(min.x, min.y)); + _vertexData[1].position = vertexFromAlphaPoint(Vec2(min.x, min.y)); // TOPRIGHT - _vertexData[2].texCoords = textureCoordFromAlphaPoint(Vec2(max.x, max.y)); - _vertexData[2].vertices = vertexFromAlphaPoint(Vec2(max.x, max.y)); + _vertexData[2].texCoord = textureCoordFromAlphaPoint(Vec2(max.x, max.y)); + _vertexData[2].position = vertexFromAlphaPoint(Vec2(max.x, max.y)); // BOTRIGHT - _vertexData[3].texCoords = textureCoordFromAlphaPoint(Vec2(max.x, min.y)); - _vertexData[3].vertices = vertexFromAlphaPoint(Vec2(max.x, min.y)); + _vertexData[3].texCoord = textureCoordFromAlphaPoint(Vec2(max.x, min.y)); + _vertexData[3].position = vertexFromAlphaPoint(Vec2(max.x, min.y)); _vertexIndex[0] = 0; _vertexIndex[1] = 1; diff --git a/extensions/fairygui/src/fairygui/display/FUISprite.h b/extensions/fairygui/src/fairygui/display/FUISprite.h index 6755c070309a..72887bdea33f 100644 --- a/extensions/fairygui/src/fairygui/display/FUISprite.h +++ b/extensions/fairygui/src/fairygui/display/FUISprite.h @@ -55,7 +55,7 @@ class FUISprite : public ax::Sprite bool _scaleByTile; int _vertexDataCount; ax::TrianglesCommand::Triangles _fillTriangles; - ax::V3F_C4B_T2F *_vertexData; + ax::V3F_T2F_C4B *_vertexData; unsigned short *_vertexIndex; static ax::Texture2D* _empty; diff --git a/extensions/fairygui/src/fairygui/gears/GearColor.cpp b/extensions/fairygui/src/fairygui/gears/GearColor.cpp index bb2bbf7f42d4..aed9646be381 100644 --- a/extensions/fairygui/src/fairygui/gears/GearColor.cpp +++ b/extensions/fairygui/src/fairygui/gears/GearColor.cpp @@ -82,7 +82,7 @@ void GearColor::apply() if (_owner->checkGearController(0, _controller)) _tweenConfig->_displayLockToken = _owner->addDisplayLock(); - _tweenConfig->_tweener = GTween::to((Color4B)curColor, (Color4B)gv.color, _tweenConfig->duration) + _tweenConfig->_tweener = GTween::to((Color32)curColor, (Color32)gv.color, _tweenConfig->duration) ->setDelay(_tweenConfig->delay) ->setEase(_tweenConfig->easeType) ->setTargetAny(this) diff --git a/extensions/fairygui/src/fairygui/tween/GTween.cpp b/extensions/fairygui/src/fairygui/tween/GTween.cpp index 1b8e93a0a245..0b28e4a89018 100644 --- a/extensions/fairygui/src/fairygui/tween/GTween.cpp +++ b/extensions/fairygui/src/fairygui/tween/GTween.cpp @@ -24,7 +24,7 @@ GTweener* GTween::to(const ax::Vec4& startValue, const ax::Vec4 & endValue, floa return TweenManager::createTween()->_to(startValue, endValue, duration); } -GTweener* GTween::to(const ax::Color4B& startValue, const ax::Color4B & endValue, float duration) +GTweener* GTween::to(const ax::Color32& startValue, const ax::Color32 & endValue, float duration) { return TweenManager::createTween()->_to(startValue, endValue, duration); } diff --git a/extensions/fairygui/src/fairygui/tween/GTween.h b/extensions/fairygui/src/fairygui/tween/GTween.h index feb89288f209..48ad8eb06a35 100644 --- a/extensions/fairygui/src/fairygui/tween/GTween.h +++ b/extensions/fairygui/src/fairygui/tween/GTween.h @@ -17,12 +17,12 @@ class GTween static GTweener* to(const ax::Vec2& startValue, const ax::Vec2& endValue, float duration); static GTweener* to(const ax::Vec3& startValue, const ax::Vec3& endValue, float duration); static GTweener* to(const ax::Vec4& startValue, const ax::Vec4& endValue, float duration); - static GTweener* to(const ax::Color4B& startValue, const ax::Color4B& endValue, float duration); + static GTweener* to(const ax::Color32& startValue, const ax::Color32& endValue, float duration); static GTweener* toFloat(float startValue, float endValue, float duration) { return to(startValue, endValue, duration); } static GTweener* toVec2(const ax::Vec2& startValue, const ax::Vec2& endValue, float duration) { return to(startValue, endValue, duration); } static GTweener* toVec3(const ax::Vec3& startValue, const ax::Vec3& endValue, float duration) { return to(startValue, endValue, duration); } static GTweener* toVec4(const ax::Vec4& startValue, const ax::Vec4& endValue, float duration) { return to(startValue, endValue, duration); } - static GTweener* toColor4B(const ax::Color4B& startValue, const ax::Color4B& endValue, float duration) { return to(startValue, endValue, duration); } + static GTweener* toColor32(const ax::Color32& startValue, const ax::Color32& endValue, float duration) { return to(startValue, endValue, duration); } static GTweener* toDouble(double startValue, double endValue, float duration); static GTweener* delayedCall(float delay); static GTweener* shake(const ax::Vec2& startValue, float amplitude, float duration); diff --git a/extensions/fairygui/src/fairygui/tween/GTweener.cpp b/extensions/fairygui/src/fairygui/tween/GTweener.cpp index ebf0d8ff6a7c..ab7ec485977a 100644 --- a/extensions/fairygui/src/fairygui/tween/GTweener.cpp +++ b/extensions/fairygui/src/fairygui/tween/GTweener.cpp @@ -216,7 +216,7 @@ GTweener* GTweener::_to(const ax::Vec4& start, const ax::Vec4& end, float durati return this; } -GTweener* GTweener::_to(const ax::Color4B& start, const ax::Color4B& end, float duration) +GTweener* GTweener::_to(const ax::Color32& start, const ax::Color32& end, float duration) { _valueSize = 4; startValue.setColor(start); diff --git a/extensions/fairygui/src/fairygui/tween/GTweener.h b/extensions/fairygui/src/fairygui/tween/GTweener.h index c4691e82c739..34979f4cd0e8 100644 --- a/extensions/fairygui/src/fairygui/tween/GTweener.h +++ b/extensions/fairygui/src/fairygui/tween/GTweener.h @@ -59,7 +59,7 @@ class GTweener : public ax::Object GTweener* _to(const ax::Vec2& start, const ax::Vec2& end, float duration); GTweener* _to(const ax::Vec3& start, const ax::Vec3& end, float duration); GTweener* _to(const ax::Vec4& start, const ax::Vec4& end, float duration); - GTweener* _to(const ax::Color4B& start, const ax::Color4B& end, float duration); + GTweener* _to(const ax::Color32& start, const ax::Color32& end, float duration); GTweener* _to(double start, double end, float duration); GTweener* _shake(const ax::Vec2& start, float amplitude, float duration); void _init(); diff --git a/extensions/fairygui/src/fairygui/tween/TweenValue.cpp b/extensions/fairygui/src/fairygui/tween/TweenValue.cpp index 6b258d4982d9..14be5ea34d59 100644 --- a/extensions/fairygui/src/fairygui/tween/TweenValue.cpp +++ b/extensions/fairygui/src/fairygui/tween/TweenValue.cpp @@ -44,12 +44,12 @@ void TweenValue::setVec4(const ax::Vec4 & value) w = value.w; } -ax::Color4B TweenValue::getColor() const +ax::Color32 TweenValue::getColor() const { - return ax::Color4B(x, y, z, w); + return ax::Color32(x, y, z, w); } -void TweenValue::setColor(const ax::Color4B & value) +void TweenValue::setColor(const ax::Color32 & value) { x = value.r; y = value.g; diff --git a/extensions/fairygui/src/fairygui/tween/TweenValue.h b/extensions/fairygui/src/fairygui/tween/TweenValue.h index b081bdac3e4d..0ab2ee30dca2 100644 --- a/extensions/fairygui/src/fairygui/tween/TweenValue.h +++ b/extensions/fairygui/src/fairygui/tween/TweenValue.h @@ -23,8 +23,8 @@ class TweenValue void setVec3(const ax::Vec3& value); ax::Vec4 getVec4() const; void setVec4(const ax::Vec4& value); - ax::Color4B getColor() const; - void setColor(const ax::Color4B& value); + ax::Color32 getColor() const; + void setColor(const ax::Color32& value); float operator[] (int index) const; float& operator[] (int index); void setZero(); diff --git a/extensions/fairygui/src/fairygui/utils/ByteBuffer.cpp b/extensions/fairygui/src/fairygui/utils/ByteBuffer.cpp index 8ddb8b029b47..5fbc7db88da6 100644 --- a/extensions/fairygui/src/fairygui/utils/ByteBuffer.cpp +++ b/extensions/fairygui/src/fairygui/utils/ByteBuffer.cpp @@ -156,7 +156,7 @@ void ByteBuffer::writeS(const std::string& value) (*_stringTable)[index] = value; } -ax::Color4B ByteBuffer::readColor() +ax::Color32 ByteBuffer::readColor() { int startIndex = _offset + _position; #if COCOS2D_VERSION >= 0x00040000 @@ -172,7 +172,7 @@ ax::Color4B ByteBuffer::readColor() #endif _position += 4; - return ax::Color4B(r, g, b, a); + return ax::Color32(r, g, b, a); } ByteBuffer* ByteBuffer::readBuffer() diff --git a/extensions/fairygui/src/fairygui/utils/ByteBuffer.h b/extensions/fairygui/src/fairygui/utils/ByteBuffer.h index b1b46b5c30af..3870929339e8 100644 --- a/extensions/fairygui/src/fairygui/utils/ByteBuffer.h +++ b/extensions/fairygui/src/fairygui/utils/ByteBuffer.h @@ -39,7 +39,7 @@ class ByteBuffer bool readS(std::string& result); const std::string* readSP(); void writeS(const std::string& value); - ax::Color4B readColor(); + ax::Color32 readColor(); ByteBuffer* readBuffer(); bool seek(int indexTablePos, int blockIndex); diff --git a/extensions/fairygui/src/fairygui/utils/ToolSet.cpp b/extensions/fairygui/src/fairygui/utils/ToolSet.cpp index 5b25b11e7e78..abe9ea3cbc48 100644 --- a/extensions/fairygui/src/fairygui/utils/ToolSet.cpp +++ b/extensions/fairygui/src/fairygui/utils/ToolSet.cpp @@ -4,25 +4,25 @@ NS_FGUI_BEGIN using namespace ax; using namespace std; -Color4B ToolSet::hexToColor(const char* str) +Color32 ToolSet::hexToColor(const char* str) { ssize_t len = strlen(str); if (len < 7 || str[0] != '#') - return Color4B::BLACK; + return Color32::BLACK; char temp[3]; memset(temp, 0, 3); if (len == 9) { - return Color4B(strtol(strncpy(temp, str + 3, 2), NULL, 16), + return Color32(strtol(strncpy(temp, str + 3, 2), NULL, 16), strtol(strncpy(temp, str + 5, 2), NULL, 16), strtol(strncpy(temp, str + 7, 2), NULL, 16), strtol(strncpy(temp, str + 1, 2), NULL, 16)); } else { - return Color4B(strtol(strncpy(temp, str + 1, 2), NULL, 16), + return Color32(strtol(strncpy(temp, str + 1, 2), NULL, 16), strtol(strncpy(temp, str + 3, 2), NULL, 16), strtol(strncpy(temp, str + 5, 2), NULL, 16), 255); diff --git a/extensions/fairygui/src/fairygui/utils/ToolSet.h b/extensions/fairygui/src/fairygui/utils/ToolSet.h index 76a311e7016d..59356c62da09 100644 --- a/extensions/fairygui/src/fairygui/utils/ToolSet.h +++ b/extensions/fairygui/src/fairygui/utils/ToolSet.h @@ -9,7 +9,7 @@ NS_FGUI_BEGIN class ToolSet { public: - static ax::Color4B hexToColor(const char* str); + static ax::Color32 hexToColor(const char* str); static ax::Color3B intToColor(unsigned int rgb); static unsigned int colorToInt(const ax::Color3B& color); diff --git a/extensions/physics-nodes/CMakeLists.txt b/extensions/physics-nodes/CMakeLists.txt index 80040ca28c33..08e82ba0a800 100644 --- a/extensions/physics-nodes/CMakeLists.txt +++ b/extensions/physics-nodes/CMakeLists.txt @@ -6,4 +6,5 @@ add_library(${target_name} ${PHYSICS_NODES_SOURCES}) target_include_directories( ${target_name} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src ) + setup_ax_extension_config(${target_name}) diff --git a/extensions/physics-nodes/src/physics-nodes/PhysicsDebugNode.cpp b/extensions/physics-nodes/src/physics-nodes/PhysicsDebugNode.cpp index 6e903917f550..cd0a922bcc11 100644 --- a/extensions/physics-nodes/src/physics-nodes/PhysicsDebugNode.cpp +++ b/extensions/physics-nodes/src/physics-nodes/PhysicsDebugNode.cpp @@ -1,252 +1,168 @@ -/* Copyright (c) 2012 Scott Lembcke and Howling Moon Software - * Copyright (c) 2012 cocos2d-x.org - * Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. +/* * Copyright (c) 2021 @aismann; Peter Eismann, Germany; dreifrankensoft * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. */ -#include "PhysicsDebugNode.h" -#include "chipmunk/chipmunk_private.h" - -#include "base/Types.h" -#include "math/Math.h" +#include "PhysicsDebugNode.h" +#include "physics/PhysicsHelper.h" -#include -#include -#include -#include -#include +#if defined(_WIN32) +# pragma push_macro("TRANSPARENT") +# undef TRANSPARENT +#endif NS_AX_EXT_BEGIN -/* - IMPORTANT - READ ME! - - This file sets pokes around in the private API a lot to provide efficient - debug rendering given nothing more than reference to a Chipmunk space. - It is not recommended to write rendering code like this in your own games - as the private API may change with little or no warning. - */ - -static const cpVect spring_verts[] = { - {0.00f, 0.0f}, {0.20f, 0.0f}, {0.25f, 3.0f}, {0.30f, -6.0f}, {0.35f, 6.0f}, - {0.40f, -6.0f}, {0.45f, 6.0f}, {0.50f, -6.0f}, {0.55f, 6.0f}, {0.60f, -6.0f}, - {0.65f, 6.0f}, {0.70f, -3.0f}, {0.75f, 6.0f}, {0.80f, 0.0f}, {1.00f, 0.0f}, -}; -static const int spring_count = sizeof(spring_verts) / sizeof(cpVect); - -static Color4F ColorForBody(cpBody* body) +/// Draw a closed polygon provided in CCW order. +// void (*DrawPolygon)(const b2Vec2* vertices, int vertexCount, b2HexColor color, void* context); +static void b2DrawPolygon(const b2Vec2* verts, int vertexCount, b2HexColor color, PhysicsDebugNode* dn) { - if (CP_BODY_TYPE_STATIC == cpBodyGetType(body) || cpBodyIsSleeping(body)) + Vec2* vec = new Vec2[vertexCount]; + for (size_t i = 0; i < vertexCount; i++) { - return Color4F(0.5f, 0.5f, 0.5f, 0.5f); + vec[i] = Vec2(verts[i].x * dn->getPTMRatio(), verts[i].y * dn->getPTMRatio()) + dn->getWorldOffset(); } - else if (body->sleeping.idleTime > cpBodyGetSpace(body)->sleepTimeThreshold) - { - return Color4F(0.33f, 0.33f, 0.33f, 0.5f); - } - else + dn->drawPolygon(vec, vertexCount, ax::Color::BLACK, 0.4f, PhysicsHelper::toColor(color)); +} + +/// Draw a solid closed polygon provided in CCW order. +// void (*DrawSolidPolygon)(b2Transform, +// const b2Vec2* vertices, +// int vertexCount, +// float radius, +// b2HexColor color, +// void* context); +static void b2DrawSolidPolygon(b2Transform t, + const b2Vec2* verts, + int vertexCount, + float radius, + b2HexColor color, + PhysicsDebugNode* dn) +{ + axstd::pod_vector vec(vertexCount); + for (size_t i = 0; i < vertexCount; i++) { - return Color4F(1.0f, 0.0f, 0.0f, 0.5f); + auto pt = b2TransformPoint(t, verts[i]); + vec[i] = Vec2(pt.x * dn->getPTMRatio(), pt.y * dn->getPTMRatio()) + dn->getWorldOffset(); } + auto color4f = PhysicsHelper::toColor(color); + dn->drawPolygon(vec.data(), vertexCount, ax::Color(color4f.r / 2, color4f.g / 2, color4f.b / 2, color4f.a), 0.5f, + color4f); } -static Vec2 cpVert2Point(const cpVect& vert) +/// Draw a circle. +// void (*DrawCircle)(b2Vec2 center, float radius, b2HexColor color, void* context); +static void b2DrawCircle(b2Vec2 center, float radius, b2HexColor color, PhysicsDebugNode* dn) { - return (Vec2(vert.x, vert.y)); + dn->drawCircle(Vec2(center.x * dn->getPTMRatio(), center.y * dn->getPTMRatio()) + dn->getWorldOffset(), + radius * dn->getPTMRatio(), AX_DEGREES_TO_RADIANS(0), 30, true, 1.0f, 1.0f, + PhysicsHelper::toColor(color)); } -static void DrawShape(cpShape* shape, DrawNode* renderer) +/// Draw a solid circle. +// void (*DrawSolidCircle)(b2Transform, float radius, b2HexColor color, void* context); +static void b2DrawSolidCircle(b2Transform t, float radius, b2HexColor color, PhysicsDebugNode* dn) { - cpBody* body = cpShapeGetBody(shape); - Color4F color = ColorForBody(body); - - switch (shape->klass->type) - { - case CP_CIRCLE_SHAPE: - { - cpCircleShape* circle = (cpCircleShape*)shape; - cpVect center = circle->tc; - cpFloat radius = circle->r; - renderer->drawDot(cpVert2Point(center), cpfmax(radius, 1.0), color); - renderer->drawSegment(cpVert2Point(center), - cpVert2Point(cpvadd(center, cpvmult(cpBodyGetRotation(body), radius))), 1.0, color); - } - break; - case CP_SEGMENT_SHAPE: - { - cpSegmentShape* seg = (cpSegmentShape*)shape; - renderer->drawSegment(cpVert2Point(seg->ta), cpVert2Point(seg->tb), cpfmax(seg->r, 1.0), color); - } - break; - case CP_POLY_SHAPE: - { - cpPolyShape* poly = (cpPolyShape*)shape; - Color4F line = color; - line.a = cpflerp(color.a, 1.0, 0.5); - int num = poly->count; - Vec2* pPoints = new Vec2[num]; - for (int i = 0; i < num; ++i) - pPoints[i] = cpVert2Point(poly->planes[i].v0); - if (cpfmax(poly->r, 1.0) > 1.0) - { - renderer->drawPolygon(pPoints, num, Color4F(0.5f, 0.5f, 0.5f, 0.0f), poly->r, color); - } - else - { - renderer->drawPolygon(pPoints, num, color, 1.0, line); - } - - AX_SAFE_DELETE_ARRAY(pPoints); - } - break; - default: - cpAssertHard(false, "Bad assertion in DrawShape()"); - } + auto center = b2TransformPoint(t, b2Vec2_zero); + Vec2 c = {Vec2(center.x * dn->getPTMRatio(), center.y * dn->getPTMRatio()) + dn->getWorldOffset()}; + auto color4f = PhysicsHelper::toColor(color); + + dn->drawSolidCircle(c, radius * dn->getPTMRatio(), AX_DEGREES_TO_RADIANS(0), 20, 1.0f, 1.0f, + ax::Color(color4f.r / 2, color4f.g / 2, color4f.b / 2, color4f.a), 0.4f, color4f); + // Draw a line fixed in the circle to animate rotation. + b2Vec2 pp = {(center + radius * b2Rot_GetXAxis(t.q))}; + Vec2 cp = {Vec2(pp.x * dn->getPTMRatio(), pp.y * dn->getPTMRatio()) + dn->getWorldOffset()}; + dn->drawLine(c, cp, color4f); } -static Color4F CONSTRAINT_COLOR(0, 1, 0, 0.5); +/// Draw a solid capsule. +// void (*DrawSolidCapsule)(b2Vec2 p1, b2Vec2 p2, float radius, b2HexColor color, void* context); -static void DrawConstraint(cpConstraint* constraint, DrawNode* renderer) +/// Draw a line segment. +// void (*DrawSegment)(b2Vec2 p1, b2Vec2 p2, b2HexColor color, void* context); +static void b2DrawSegment(b2Vec2 p1, b2Vec2 p2, b2HexColor color, PhysicsDebugNode* dn) { - cpBody* body_a = cpConstraintGetBodyA(constraint); - cpBody* body_b = cpConstraintGetBodyB(constraint); - - if (cpConstraintIsPinJoint(constraint)) - { - cpVect a = - cpvadd(cpBodyGetPosition(body_a), cpvrotate(cpPinJointGetAnchorA(constraint), cpBodyGetRotation(body_a))); - cpVect b = - cpvadd(cpBodyGetPosition(body_b), cpvrotate(cpPinJointGetAnchorB(constraint), cpBodyGetRotation(body_b))); - - renderer->drawDot(cpVert2Point(a), 3.0, CONSTRAINT_COLOR); - renderer->drawDot(cpVert2Point(b), 3.0, CONSTRAINT_COLOR); - renderer->drawSegment(cpVert2Point(a), cpVert2Point(b), 1.0, CONSTRAINT_COLOR); - } - else if (cpConstraintIsSlideJoint(constraint)) - { - cpVect a = - cpvadd(cpBodyGetPosition(body_a), cpvrotate(cpSlideJointGetAnchorA(constraint), cpBodyGetRotation(body_a))); - cpVect b = - cpvadd(cpBodyGetPosition(body_b), cpvrotate(cpSlideJointGetAnchorB(constraint), cpBodyGetRotation(body_b))); - - renderer->drawDot(cpVert2Point(a), 3.0, CONSTRAINT_COLOR); - renderer->drawDot(cpVert2Point(b), 3.0, CONSTRAINT_COLOR); - renderer->drawSegment(cpVert2Point(a), cpVert2Point(b), 1.0, CONSTRAINT_COLOR); - } - else if (cpConstraintIsPivotJoint(constraint)) - { - cpVect a = - cpvadd(cpBodyGetPosition(body_a), cpvrotate(cpPivotJointGetAnchorA(constraint), cpBodyGetRotation(body_a))); - cpVect b = - cpvadd(cpBodyGetPosition(body_b), cpvrotate(cpPivotJointGetAnchorB(constraint), cpBodyGetRotation(body_b))); - - renderer->drawDot(cpVert2Point(a), 3.0, CONSTRAINT_COLOR); - renderer->drawDot(cpVert2Point(b), 3.0, CONSTRAINT_COLOR); - } - else if (cpConstraintIsGrooveJoint(constraint)) - { - cpVect a = cpvadd(cpBodyGetPosition(body_a), - cpvrotate(cpGrooveJointGetGrooveA(constraint), cpBodyGetRotation(body_a))); - cpVect b = cpvadd(cpBodyGetPosition(body_a), - cpvrotate(cpGrooveJointGetGrooveB(constraint), cpBodyGetRotation(body_a))); - cpVect c = cpvadd(cpBodyGetPosition(body_b), - cpvrotate(cpGrooveJointGetAnchorB(constraint), cpBodyGetRotation(body_b))); - - renderer->drawDot(cpVert2Point(c), 3.0, CONSTRAINT_COLOR); - renderer->drawSegment(cpVert2Point(a), cpVert2Point(b), 1.0, CONSTRAINT_COLOR); - } - else if (cpConstraintIsDampedSpring(constraint)) - { - cpDampedSpring* spring = (cpDampedSpring*)constraint; + dn->drawLine(Vec2(p1.x * dn->getPTMRatio(), p1.y * dn->getPTMRatio()) + dn->getWorldOffset(), + Vec2(p2.x * dn->getPTMRatio(), p2.y * dn->getPTMRatio()) + dn->getWorldOffset(), + PhysicsHelper::toColor(color)); +} - cpVect a = cpTransformPoint(body_a->transform, spring->anchorA); - cpVect b = cpTransformPoint(body_b->transform, spring->anchorB); +/// Draw a transform. Choose your own length scale. +// void (*DrawTransform)(b2Transform transform, void* context); +static void b2DrawTransform(b2Transform t, PhysicsDebugNode* dn) +{ + b2Vec2 p1 = t.p, p2; + const float k_axisScale = 0.4f; - renderer->drawDot(cpVert2Point(a), 3.0, CONSTRAINT_COLOR); - renderer->drawDot(cpVert2Point(b), 3.0, CONSTRAINT_COLOR); + p2 = p1 + k_axisScale * b2Rot_GetXAxis(t.q); + b2DrawSegment(p1, p2, b2HexColor::b2_colorRed, dn); - cpVect delta = cpvsub(b, a); - cpFloat cos = delta.x; - cpFloat sin = delta.y; - cpFloat s = 1.0f / cpvlength(delta); + p2 = p1 + k_axisScale * b2Rot_GetYAxis(t.q); + b2DrawSegment(p1, p2, b2HexColor::b2_colorGreen, dn); +} - cpVect r1 = cpv(cos, -sin * s); - cpVect r2 = cpv(sin, cos * s); +/// Draw a point. +// void (*DrawPoint)(b2Vec2 p, float size, b2HexColor color, void* context); +static void b2DrawPoint(b2Vec2 p, float size, b2HexColor color, PhysicsDebugNode* dn) +{ + dn->drawPoint(Vec2(p.x * dn->getPTMRatio(), p.y * dn->getPTMRatio()) + dn->getWorldOffset(), size, + PhysicsHelper::toColor(color)); +} - cpVect* verts = (cpVect*)alloca(spring_count * sizeof(cpVect)); - for (int i = 0; i < spring_count; i++) - { - cpVect v = spring_verts[i]; - verts[i] = cpv(cpvdot(v, r1) + a.x, cpvdot(v, r2) + a.y); - } +bool PhysicsDebugNode::initWithWorld(b2WorldId worldId) +{ + bool ret = DrawNode::init(); - for (int i = 0; i < spring_count - 1; i++) - { - renderer->drawSegment(cpVert2Point(verts[i]), cpVert2Point(verts[i + 1]), 1.0, CONSTRAINT_COLOR); - } - } - else - { - AXLOGD("Cannot draw constraint"); - } + _world = worldId; + return ret; } -// implementation of PhysicsDebugNode - void PhysicsDebugNode::draw(Renderer* renderer, const Mat4& transform, uint32_t flags) { - if (!_spacePtr) + if (!b2World_IsValid(_world)) { return; } - // clear the shapes information before draw current shapes. - DrawNode::clear(); - cpSpaceEachShape(_spacePtr, (cpSpaceShapeIteratorFunc)DrawShape, this); - cpSpaceEachConstraint(_spacePtr, (cpSpaceConstraintIteratorFunc)DrawConstraint, this); + if (_autoDraw) + { + // clear the shapes information before draw current shapes. + clear(); + b2World_Draw(_world, &_debugDraw); + } DrawNode::draw(renderer, transform, flags); } -PhysicsDebugNode::PhysicsDebugNode() : _spacePtr(nullptr) {} - -PhysicsDebugNode* PhysicsDebugNode::create(cpSpace* space) -{ - PhysicsDebugNode* node = new PhysicsDebugNode(); - node->init(); - node->_spacePtr = space; - node->autorelease(); - return node; -} - -PhysicsDebugNode::~PhysicsDebugNode() {} - -cpSpace* PhysicsDebugNode::getSpace() const +PhysicsDebugNode::PhysicsDebugNode() { - return _spacePtr; -} - -void PhysicsDebugNode::setSpace(cpSpace* space) -{ - _spacePtr = space; + _debugDraw.context = this; +#define __b2_setfun(f) _debugDraw.f = reinterpret_cast(b2##f); + __b2_setfun(DrawPolygon); + __b2_setfun(DrawSolidPolygon); + __b2_setfun(DrawCircle); + __b2_setfun(DrawSolidCircle); + __b2_setfun(DrawSegment); + __b2_setfun(DrawTransform); + __b2_setfun(DrawPoint); +#undef __b2_setfun } NS_AX_EXT_END + +#if defined(_WIN32) +# pragma pop_macro("TRANSPARENT") +#endif diff --git a/extensions/physics-nodes/src/physics-nodes/PhysicsDebugNode.h b/extensions/physics-nodes/src/physics-nodes/PhysicsDebugNode.h index ae751ab5a54e..4556f14958d7 100644 --- a/extensions/physics-nodes/src/physics-nodes/PhysicsDebugNode.h +++ b/extensions/physics-nodes/src/physics-nodes/PhysicsDebugNode.h @@ -1,73 +1,68 @@ -/* Copyright (c) 2012 Scott Lembcke and Howling Moon Software - * Copyright (c) 2012 cocos2d-x.org - * Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. +/* + * Copyright (c) 2019 Erin Catto * Copyright (c) 2021 @aismann; Peter Eismann, Germany; dreifrankensoft * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. */ -#ifndef __PHYSICSNODES_DEBUGNODE_H__ -#define __PHYSICSNODES_DEBUGNODE_H__ +#ifndef __PHYSICSNODES_DEBUGNODE_BOX2D_H__ +#define __PHYSICSNODES_DEBUGNODE_BOX2D_H__ #include "extensions/ExtensionMacros.h" -#include "2d/DrawNode.h" #include "extensions/ExtensionExport.h" - -struct cpSpace; +#include "2d/DrawNode.h" +#include "box2d/box2d.h" +#include "axmol.h" NS_AX_EXT_BEGIN -/** - * A BaseData that draws the components of a physics engine. - - * Supported physics engines: - * - Chipmunk - * - Objective-Chipmunk - * @since v2.1 - * @lua NA - */ - +// This class implements debug drawing callbacks that are invoked inside b2World::Step. class AX_EX_DLL PhysicsDebugNode : public DrawNode { - public: - /** Create a debug node for a regular Chipmunk space. */ - static PhysicsDebugNode* create(cpSpace* space); - /** - * @js ctor - */ PhysicsDebugNode(); - /** - * @js NA - */ - virtual ~PhysicsDebugNode(); + virtual bool initWithWorld(b2WorldId worldId); + + void setAutoDraw(bool bval) { _autoDraw = bval; } + bool isAutoDraw() const { return _autoDraw; } + + // control border thinkness + void setThinkness(float fval) { _thinkness = fval; } + float getThinkness() const { return _thinkness; } - cpSpace* getSpace() const; - void setSpace(cpSpace* space); + void setWorldOffset(const Vec2& offset) { _worldOffset = offset; } + const Vec2& getWorldOffset() const { return _worldOffset; } + + void setPTMRatio(float ratio) { _ratio = ratio; } + float getPTMRatio() const { return _ratio; } // Overrides virtual void draw(Renderer* renderer, const Mat4& transform, uint32_t flags) override; + b2DebugDraw& getB2DebugDraw() { return _debugDraw; } + protected: - cpSpace* _spacePtr; + b2WorldId _world{}; + b2DebugDraw _debugDraw{b2DefaultDebugDraw()}; + bool _autoDraw{true}; + float _thinkness{0.5f}; + + ax::Vec2 _worldOffset{}; + float _ratio{1.0f}; }; NS_AX_EXT_END -#endif // __PHYSICSNODES_DEBUGNODE_H__ +#endif //__PHYSICSNODES_DEBUGNODE_BOX2D_H__ diff --git a/extensions/physics-nodes/src/physics-nodes/PhysicsDebugNodeBox2D.cpp b/extensions/physics-nodes/src/physics-nodes/PhysicsDebugNodeBox2D.cpp deleted file mode 100644 index b63fd556ba49..000000000000 --- a/extensions/physics-nodes/src/physics-nodes/PhysicsDebugNodeBox2D.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (c) 2021 @aismann; Peter Eismann, Germany; dreifrankensoft - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -#include "PhysicsDebugNodeBox2D.h" - -NS_AX_EXT_BEGIN - -PhysicsDebugNodeBox2D::PhysicsDebugNodeBox2D() -{ - drawBP = DrawNode::create(); - debugNodeOffset = {40.0f, 0.0f}; - mRatio = 1.0f; -} - -PhysicsDebugNodeBox2D::~PhysicsDebugNodeBox2D() {} - -ax::DrawNode* PhysicsDebugNodeBox2D::GetDrawNode() -{ - return drawBP; -} - -void PhysicsDebugNodeBox2D::SetDrawNode(ax::DrawNode* drawNode) -{ - drawBP = drawNode; -} - -ax::Vec2& PhysicsDebugNodeBox2D::GetDebugNodeOffset() -{ - return debugNodeOffset; -} - -void PhysicsDebugNodeBox2D::DrawPolygon(const b2Vec2* verts, int vertexCount, const b2Color& color) -{ - Vec2* vec = new Vec2[vertexCount]; - for (size_t i = 0; i < vertexCount; i++) - { - vec[i] = Vec2(verts[i].x * mRatio, verts[i].y * mRatio) + debugNodeOffset; - } - drawBP->drawPolygon(vec, vertexCount, Color4F::BLACK, 0.4f, Color4F(color.r, color.g, color.b, color.a)); -} - -void PhysicsDebugNodeBox2D::DrawSolidPolygon(const b2Vec2* verts, int vertexCount, const b2Color& color) -{ - Vec2* vec = new Vec2[vertexCount]; - for (size_t i = 0; i < vertexCount; i++) - { - vec[i] = Vec2(verts[i].x * mRatio, verts[i].y * mRatio) + debugNodeOffset; - } - drawBP->drawPolygon(vec, vertexCount, Color4F(color.r / 2, color.g / 2, color.b / 2, color.a), 0.4f, - Color4F(color.r, color.g, color.b, color.a)); -} - -void PhysicsDebugNodeBox2D::DrawCircle(const b2Vec2& center, float radius, const b2Color& color) -{ - drawBP->drawCircle(Vec2(center.x * mRatio, center.y * mRatio) + debugNodeOffset, radius * mRatio, - AX_DEGREES_TO_RADIANS(0), 30, true, 1.0f, 1.0f, Color4F(color.r, color.g, color.b, color.a)); -} - -void PhysicsDebugNodeBox2D::DrawSolidCircle(const b2Vec2& center, - float radius, - const b2Vec2& axis, - const b2Color& color) -{ - Vec2 c = {Vec2(center.x * mRatio, center.y * mRatio) + debugNodeOffset}; - drawBP->drawSolidCircle(c, radius * mRatio, AX_DEGREES_TO_RADIANS(0), 20, 1.0f, 1.0f, - Color4F(color.r / 2, color.g / 2, color.b / 2, color.a), 0.4f, - Color4F(color.r, color.g, color.b, color.a)); - - // Draw a line fixed in the circle to animate rotation. - b2Vec2 pp = {(center + radius * axis)}; - Vec2 cp = {Vec2(pp.x * mRatio, pp.y * mRatio) + debugNodeOffset}; - drawBP->drawLine(c, cp, Color4F(color.r, color.g, color.b, color.a)); -} - -void PhysicsDebugNodeBox2D::DrawSegment(const b2Vec2& p1, const b2Vec2& p2, const b2Color& color) -{ - drawBP->drawLine(Vec2(p1.x * mRatio, p1.y * mRatio) + debugNodeOffset, - Vec2(p2.x * mRatio, p2.y * mRatio) + debugNodeOffset, Color4F(color.r, color.g, color.b, color.a)); -} - -void PhysicsDebugNodeBox2D::DrawTransform(const b2Transform& xf) -{ - b2Vec2 p1 = xf.p, p2; - const float k_axisScale = 0.4f; - p2 = p1 + k_axisScale * xf.q.GetXAxis(); - DrawSegment(p1, p2, b2Color(1.0f, 0.0f, 0.0f)); - - p2 = p1 + k_axisScale * xf.q.GetYAxis(); - DrawSegment(p1, p2, b2Color(0.0f, 1.0f, 0.0f)); -} - -void PhysicsDebugNodeBox2D::DrawPoint(const b2Vec2& p, float size, const b2Color& color) -{ - drawBP->drawPoint(Vec2(p.x * mRatio, p.y * mRatio) + debugNodeOffset, size, - Color4F(color.r, color.g, color.b, color.a)); -} - -NS_AX_EXT_END \ No newline at end of file diff --git a/extensions/physics-nodes/src/physics-nodes/PhysicsDebugNodeBox2D.h b/extensions/physics-nodes/src/physics-nodes/PhysicsDebugNodeBox2D.h deleted file mode 100644 index 98edb644a532..000000000000 --- a/extensions/physics-nodes/src/physics-nodes/PhysicsDebugNodeBox2D.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2019 Erin Catto - * Copyright (c) 2021 @aismann; Peter Eismann, Germany; dreifrankensoft - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -#ifndef __PHYSICSNODES_DEBUGNODE_BOX2D_H__ -#define __PHYSICSNODES_DEBUGNODE_BOX2D_H__ - -#include "extensions/ExtensionMacros.h" -#include "extensions/ExtensionExport.h" -#include "2d/DrawNode.h" -#include "box2d/box2d.h" -#include "axmol.h" - -NS_AX_EXT_BEGIN - -// This class implements debug drawing callbacks that are invoked inside b2World::Step. -class AX_EX_DLL PhysicsDebugNodeBox2D : public b2Draw -{ -public: - PhysicsDebugNodeBox2D(); - ~PhysicsDebugNodeBox2D(); - - void Create(); - void Destroy(); - - void DrawPolygon(const b2Vec2* vertices, int vertexCount, const b2Color& color) override; - - void DrawSolidPolygon(const b2Vec2* vertices, int vertexCount, const b2Color& color) override; - - void DrawCircle(const b2Vec2& center, float radius, const b2Color& color) override; - - void DrawSolidCircle(const b2Vec2& center, float radius, const b2Vec2& axis, const b2Color& color) override; - - void DrawSegment(const b2Vec2& p1, const b2Vec2& p2, const b2Color& color) override; - - void DrawTransform(const b2Transform& xf) override; - - void DrawPoint(const b2Vec2& p, float size, const b2Color& color) override; - - // axis stuffs - ax::DrawNode* GetDrawNode(); - void SetDrawNode(ax::DrawNode* drawNode); - ax::Vec2& GetDebugNodeOffset(); - - ax::DrawNode* drawBP = NULL; // axis "interface"! - ax::Vec2 debugNodeOffset; - float mRatio; - -private: -}; - -NS_AX_EXT_END - -#endif //__PHYSICSNODES_DEBUGNODE_BOX2D_H__ diff --git a/extensions/physics-nodes/src/physics-nodes/PhysicsDebugNodeChipmunk2D.cpp b/extensions/physics-nodes/src/physics-nodes/PhysicsDebugNodeChipmunk2D.cpp deleted file mode 100644 index bd313debc5e2..000000000000 --- a/extensions/physics-nodes/src/physics-nodes/PhysicsDebugNodeChipmunk2D.cpp +++ /dev/null @@ -1,254 +0,0 @@ -/* Copyright (c) 2012 Scott Lembcke and Howling Moon Software - * Copyright (c) 2012 cocos2d-x.org - * Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - * Copyright (c) 2021 @aismann; Peter Eismann, Germany; dreifrankensoft - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "PhysicsDebugNodeChipmunk2D.h" - -#include "chipmunk/chipmunk_private.h" - -#include "base/Types.h" -#include "math/Math.h" - -#include -#include -#include -#include -#include - -NS_AX_EXT_BEGIN - -Vec2 physicsDebugNodeOffset; - -/* - IMPORTANT - READ ME! - - This file sets pokes around in the private API a lot to provide efficient - debug rendering given nothing more than reference to a Chipmunk space. - It is not recommended to write rendering code like this in your own games - as the private API may change with little or no warning. - */ - -static const cpVect spring_verts[] = { - {0.00f, 0.0f}, {0.20f, 0.0f}, {0.25f, 3.0f}, {0.30f, -6.0f}, {0.35f, 6.0f}, - {0.40f, -6.0f}, {0.45f, 6.0f}, {0.50f, -6.0f}, {0.55f, 6.0f}, {0.60f, -6.0f}, - {0.65f, 6.0f}, {0.70f, -3.0f}, {0.75f, 6.0f}, {0.80f, 0.0f}, {1.00f, 0.0f}, -}; -static const int spring_count = sizeof(spring_verts) / sizeof(cpVect); - -static Color4B ColorForBody(cpBody* body) -{ - if (CP_BODY_TYPE_STATIC == cpBodyGetType(body) || cpBodyIsSleeping(body)) - { - return Color4B(127, 127, 127, 127); - } - else if (body->sleeping.idleTime > cpBodyGetSpace(body)->sleepTimeThreshold) - { - return Color4B(85, 85, 85, 127); - } - else - { - return Color4B(255, 0, 0, 127); - } -} - -static Vec2 cpVert2Point(const cpVect& vert) -{ - return (Vec2(vert.x, vert.y) + physicsDebugNodeOffset); -} - -static void DrawShape(cpShape* shape, DrawNode* renderer) -{ - cpBody* body = cpShapeGetBody(shape); - Color4B color = ColorForBody(body); - - switch (shape->klass->type) - { - case CP_CIRCLE_SHAPE: - { - cpCircleShape* circle = (cpCircleShape*)shape; - cpVect center = circle->tc; - cpFloat radius = circle->r; - renderer->drawDot(cpVert2Point(center), cpfmax(radius, 1.0), color); - renderer->drawSegment(cpVert2Point(center), - cpVert2Point(cpvadd(center, cpvmult(cpBodyGetRotation(body), radius))), 1.0, color); - } - break; - case CP_SEGMENT_SHAPE: - { - cpSegmentShape* seg = (cpSegmentShape*)shape; - renderer->drawSegment(cpVert2Point(seg->ta), cpVert2Point(seg->tb), cpfmax(seg->r, 1.0), color); - } - break; - case CP_POLY_SHAPE: - { - cpPolyShape* poly = (cpPolyShape*)shape; - Color4B line = color; - line.a = cpflerp(color.a, 1.0, 0.5); - int num = poly->count; - Vec2* pPoints = new Vec2[num]; - for (int i = 0; i < num; ++i) - pPoints[i] = cpVert2Point(poly->planes[i].v0); - if (cpfmax(poly->r, 1.0) > 1.0) - { - renderer->drawPolygon(pPoints, num, Color4B(127, 127, 127,0), poly->r, color); - } - else - { - renderer->drawPolygon(pPoints, num, color, 1.0, line); - } - - AX_SAFE_DELETE_ARRAY(pPoints); - } - break; - default: - cpAssertHard(false, "Bad assertion in DrawShape()"); - } -} - -static Color4B CONSTRAINT_COLOR(0, 255, 0, 127); - -static void DrawConstraint(cpConstraint* constraint, DrawNode* renderer) -{ - cpBody* body_a = cpConstraintGetBodyA(constraint); - cpBody* body_b = cpConstraintGetBodyB(constraint); - - if (cpConstraintIsPinJoint(constraint)) - { - cpVect a = - cpvadd(cpBodyGetPosition(body_a), cpvrotate(cpPinJointGetAnchorA(constraint), cpBodyGetRotation(body_a))); - cpVect b = - cpvadd(cpBodyGetPosition(body_b), cpvrotate(cpPinJointGetAnchorB(constraint), cpBodyGetRotation(body_b))); - - renderer->drawDot(cpVert2Point(a), 3.0, CONSTRAINT_COLOR); - renderer->drawDot(cpVert2Point(b), 3.0, CONSTRAINT_COLOR); - renderer->drawSegment(cpVert2Point(a), cpVert2Point(b), 1.0, CONSTRAINT_COLOR); - } - else if (cpConstraintIsSlideJoint(constraint)) - { - cpVect a = - cpvadd(cpBodyGetPosition(body_a), cpvrotate(cpSlideJointGetAnchorA(constraint), cpBodyGetRotation(body_a))); - cpVect b = - cpvadd(cpBodyGetPosition(body_b), cpvrotate(cpSlideJointGetAnchorB(constraint), cpBodyGetRotation(body_b))); - - renderer->drawDot(cpVert2Point(a), 3.0, CONSTRAINT_COLOR); - renderer->drawDot(cpVert2Point(b), 3.0, CONSTRAINT_COLOR); - renderer->drawSegment(cpVert2Point(a), cpVert2Point(b), 1.0, CONSTRAINT_COLOR); - } - else if (cpConstraintIsPivotJoint(constraint)) - { - cpVect a = - cpvadd(cpBodyGetPosition(body_a), cpvrotate(cpPivotJointGetAnchorA(constraint), cpBodyGetRotation(body_a))); - cpVect b = - cpvadd(cpBodyGetPosition(body_b), cpvrotate(cpPivotJointGetAnchorB(constraint), cpBodyGetRotation(body_b))); - - renderer->drawDot(cpVert2Point(a), 3.0, CONSTRAINT_COLOR); - renderer->drawDot(cpVert2Point(b), 3.0, CONSTRAINT_COLOR); - } - else if (cpConstraintIsGrooveJoint(constraint)) - { - cpVect a = cpvadd(cpBodyGetPosition(body_a), - cpvrotate(cpGrooveJointGetGrooveA(constraint), cpBodyGetRotation(body_a))); - cpVect b = cpvadd(cpBodyGetPosition(body_a), - cpvrotate(cpGrooveJointGetGrooveB(constraint), cpBodyGetRotation(body_a))); - cpVect c = cpvadd(cpBodyGetPosition(body_b), - cpvrotate(cpGrooveJointGetAnchorB(constraint), cpBodyGetRotation(body_b))); - - renderer->drawDot(cpVert2Point(c), 3.0, CONSTRAINT_COLOR); - renderer->drawSegment(cpVert2Point(a), cpVert2Point(b), 1.0, CONSTRAINT_COLOR); - } - else if (cpConstraintIsDampedSpring(constraint)) - { - cpDampedSpring* spring = (cpDampedSpring*)constraint; - - cpVect a = cpTransformPoint(body_a->transform, spring->anchorA); - cpVect b = cpTransformPoint(body_b->transform, spring->anchorB); - - renderer->drawDot(cpVert2Point(a), 3.0, CONSTRAINT_COLOR); - renderer->drawDot(cpVert2Point(b), 3.0, CONSTRAINT_COLOR); - - cpVect delta = cpvsub(b, a); - cpFloat cos = delta.x; - cpFloat sin = delta.y; - cpFloat s = 1.0f / cpvlength(delta); - - cpVect r1 = cpv(cos, -sin * s); - cpVect r2 = cpv(sin, cos * s); - - cpVect* verts = (cpVect*)alloca(spring_count * sizeof(cpVect)); - for (int i = 0; i < spring_count; i++) - { - cpVect v = spring_verts[i]; - verts[i] = cpv(cpvdot(v, r1) + a.x, cpvdot(v, r2) + a.y); - } - - for (int i = 0; i < spring_count - 1; i++) - { - renderer->drawSegment(cpVert2Point(verts[i]), cpVert2Point(verts[i + 1]), 1.0, CONSTRAINT_COLOR); - } - } - else - { - AXLOGD("Cannot draw constraint"); - } -} - -// implementation of PhysicsDebugNode - -void PhysicsDebugNodeChipmunk2D::draw(Renderer* renderer, const Mat4& transform, uint32_t flags) -{ - if (!_spacePtr) - { - return; - } - // clear the shapes information before draw current shapes. - DrawNode::clear(); - - cpSpaceEachShape(_spacePtr, (cpSpaceShapeIteratorFunc)DrawShape, this); - cpSpaceEachConstraint(_spacePtr, (cpSpaceConstraintIteratorFunc)DrawConstraint, this); - - DrawNode::draw(renderer, transform, flags); -} - -PhysicsDebugNodeChipmunk2D::PhysicsDebugNodeChipmunk2D() : _spacePtr(nullptr) {} - -PhysicsDebugNodeChipmunk2D* PhysicsDebugNodeChipmunk2D::create(cpSpace* space) -{ - PhysicsDebugNodeChipmunk2D* node = new PhysicsDebugNodeChipmunk2D(); - node->init(); - node->_spacePtr = space; - node->autorelease(); - return node; -} - -PhysicsDebugNodeChipmunk2D::~PhysicsDebugNodeChipmunk2D() {} - -cpSpace* PhysicsDebugNodeChipmunk2D::getSpace() const -{ - return _spacePtr; -} - -void PhysicsDebugNodeChipmunk2D::setSpace(cpSpace* space) -{ - _spacePtr = space; -} - -NS_AX_EXT_END diff --git a/extensions/physics-nodes/src/physics-nodes/PhysicsDebugNodeChipmunk2D.h b/extensions/physics-nodes/src/physics-nodes/PhysicsDebugNodeChipmunk2D.h deleted file mode 100644 index 435615817ece..000000000000 --- a/extensions/physics-nodes/src/physics-nodes/PhysicsDebugNodeChipmunk2D.h +++ /dev/null @@ -1,75 +0,0 @@ -/* Copyright (c) 2012 Scott Lembcke and Howling Moon Software - * Copyright (c) 2012 cocos2d-x.org - * Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - * Copyright (c) 2021 @aismann; Peter Eismann, Germany; dreifrankensoft - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef __PHYSICSNODES_DEBUGNODE_CHIPMUNK2D_H__ -#define __PHYSICSNODES_DEBUGNODE_CHIPMUNK2D_H__ - -#include "extensions/ExtensionMacros.h" -#include "2d/DrawNode.h" -#include "extensions/ExtensionExport.h" - -struct cpSpace; - -NS_AX_EXT_BEGIN - -extern AX_EX_DLL Vec2 physicsDebugNodeOffset; - -/** - * A BaseData that draws the components of a physics engine. - - * Supported physics engines: - * - Chipmunk - * - Objective-Chipmunk - * @since v2.1 - * @lua NA - */ - -class AX_EX_DLL PhysicsDebugNodeChipmunk2D : public DrawNode -{ - -public: - /** Create a debug node for a regular Chipmunk space. */ - static PhysicsDebugNodeChipmunk2D* create(cpSpace* space); - /** - * @js ctor - */ - PhysicsDebugNodeChipmunk2D(); - /** - * @js NA - */ - virtual ~PhysicsDebugNodeChipmunk2D(); - - cpSpace* getSpace() const; - void setSpace(cpSpace* space); - - // Overrides - virtual void draw(Renderer* renderer, const Mat4& transform, uint32_t flags) override; - -protected: - cpSpace* _spacePtr; -}; - -NS_AX_EXT_END - -#endif // __PHYSICSNODES_DEBUGNODE_CHIPMUNK2D_H__ diff --git a/extensions/physics-nodes/src/physics-nodes/PhysicsSprite.cpp b/extensions/physics-nodes/src/physics-nodes/PhysicsSprite.cpp index e26033fe5053..3b024c85544a 100644 --- a/extensions/physics-nodes/src/physics-nodes/PhysicsSprite.cpp +++ b/extensions/physics-nodes/src/physics-nodes/PhysicsSprite.cpp @@ -26,18 +26,10 @@ #include "base/Director.h" #include "base/EventDispatcher.h" -#if (AX_ENABLE_CHIPMUNK_INTEGRATION || AX_ENABLE_BOX2D_INTEGRATION) - -# if AX_ENABLE_CHIPMUNK_INTEGRATION -# include "chipmunk/chipmunk.h" -# elif AX_ENABLE_BOX2D_INTEGRATION -# include "box2d/box2d.h" -# endif - NS_AX_EXT_BEGIN PhysicsSprite::PhysicsSprite() - : _ignoreBodyRotation(false), _CPBody(nullptr), _pB2Body(nullptr), _PTMRatio(0.0f), _syncTransform(nullptr) + : _ignoreBodyRotation(false), _PTMRatio(0.0f), _syncTransform(nullptr) {} PhysicsSprite* PhysicsSprite::create() @@ -196,67 +188,24 @@ Vec3 PhysicsSprite::getPosition3D() const return Vec3(pos.x, pos.y, 0); } -// -// Chipmunk only -// - -cpBody* PhysicsSprite::getCPBody() const +b2BodyId PhysicsSprite::getB2Body() const { -# if AX_ENABLE_CHIPMUNK_INTEGRATION - return _CPBody; -# else - AXASSERT(false, "Can't call chipmunk methods when Chipmunk is disabled"); - return nullptr; -# endif + return _bodyId; } -void PhysicsSprite::setCPBody(cpBody* pBody) +void PhysicsSprite::setB2Body(b2BodyId pBody) { -# if AX_ENABLE_CHIPMUNK_INTEGRATION - _CPBody = pBody; -# else - AXASSERT(false, "Can't call chipmunk methods when Chipmunk is disabled"); -# endif -} - -b2Body* PhysicsSprite::getB2Body() const -{ -# if AX_ENABLE_BOX2D_INTEGRATION - return _pB2Body; -# else - AXASSERT(false, "Can't call box2d methods when Box2d is disabled"); - return nullptr; -# endif -} - -void PhysicsSprite::setB2Body(b2Body* pBody) -{ -# if AX_ENABLE_BOX2D_INTEGRATION - _pB2Body = pBody; -# else - AX_UNUSED_PARAM(pBody); - AXASSERT(false, "Can't call box2d methods when Box2d is disabled"); -# endif + _bodyId = pBody; } float PhysicsSprite::getPTMRatio() const { -# if AX_ENABLE_BOX2D_INTEGRATION return _PTMRatio; -# else - AXASSERT(false, "Can't call box2d methods when Box2d is disabled"); - return 0; -# endif } void PhysicsSprite::setPTMRatio(float fRatio) { -# if AX_ENABLE_BOX2D_INTEGRATION _PTMRatio = fRatio; -# else - AX_UNUSED_PARAM(fRatio); - AXASSERT(false, "Can't call box2d methods when Box2d is disabled"); -# endif } // @@ -266,33 +215,18 @@ void PhysicsSprite::setPTMRatio(float fRatio) const Vec2& PhysicsSprite::getPosFromPhysics() const { static Vec2 s_physicPosion; -# if AX_ENABLE_CHIPMUNK_INTEGRATION - - cpVect cpPos = cpBodyGetPosition(_CPBody); - s_physicPosion = Vec2(cpPos.x, cpPos.y); - -# elif AX_ENABLE_BOX2D_INTEGRATION - - b2Vec2 pos = _pB2Body->GetPosition(); + b2Vec2 pos = b2Body_GetPosition(_bodyId); float x = pos.x * _PTMRatio; float y = pos.y * _PTMRatio; s_physicPosion.set(x, y); -# endif + return s_physicPosion; } void PhysicsSprite::setPosition(float x, float y) { -# if AX_ENABLE_CHIPMUNK_INTEGRATION - - cpVect cpPos = cpv(x, y); - cpBodySetPosition(_CPBody, cpPos); - -# elif AX_ENABLE_BOX2D_INTEGRATION - - float angle = _pB2Body->GetAngle(); - _pB2Body->SetTransform(b2Vec2(x / _PTMRatio, y / _PTMRatio), angle); -# endif + auto rot = b2Body_GetRotation(_bodyId); + b2Body_SetTransform(_bodyId, b2Vec2{x / _PTMRatio, y / _PTMRatio}, rot); } void PhysicsSprite::setPosition(const Vec2& pos) @@ -317,16 +251,8 @@ void PhysicsSprite::setPosition3D(const Vec3& position) float PhysicsSprite::getRotation() const { -# if AX_ENABLE_CHIPMUNK_INTEGRATION - - return (_ignoreBodyRotation ? Sprite::getRotation() : -AX_RADIANS_TO_DEGREES(cpBodyGetAngle(_CPBody))); - -# elif AX_ENABLE_BOX2D_INTEGRATION - - return (_ignoreBodyRotation ? Sprite::getRotation() : AX_RADIANS_TO_DEGREES(_pB2Body->GetAngle())); -# else - return 0.0f; -# endif + auto angle = b2Rot_GetAngle(b2Body_GetRotation(_bodyId)); + return (_ignoreBodyRotation ? Sprite::getRotation() : AX_RADIANS_TO_DEGREES(angle)); } void PhysicsSprite::setRotation(float fRotation) @@ -336,20 +262,13 @@ void PhysicsSprite::setRotation(float fRotation) Sprite::setRotation(fRotation); } -# if AX_ENABLE_CHIPMUNK_INTEGRATION - else - { - cpBodySetAngle(_CPBody, -AX_DEGREES_TO_RADIANS(fRotation)); - } - -# elif AX_ENABLE_BOX2D_INTEGRATION else { - b2Vec2 p = _pB2Body->GetPosition(); + b2Vec2 p = b2Body_GetPosition(_bodyId); float radians = AX_DEGREES_TO_RADIANS(fRotation); - _pB2Body->SetTransform(p, radians); + auto rot = b2MakeRot(radians); + b2Body_SetTransform(_bodyId, p, rot); } -# endif } void PhysicsSprite::syncPhysicsTransform() const @@ -358,42 +277,7 @@ void PhysicsSprite::syncPhysicsTransform() const // the sprite is animated (scaled up/down) using actions. // For more info see: http://www.cocos2d-iphone.org/forum/topic/68990 -# if AX_ENABLE_CHIPMUNK_INTEGRATION - - cpVect rot = (_ignoreBodyRotation ? cpvforangle(-AX_DEGREES_TO_RADIANS(_rotationX)) : cpBodyGetRotation(_CPBody)); - float x = cpBodyGetPosition(_CPBody).x + rot.x * -_anchorPointInPoints.x * _scaleX - - rot.y * -_anchorPointInPoints.y * _scaleY; - float y = cpBodyGetPosition(_CPBody).y + rot.y * -_anchorPointInPoints.x * _scaleX + - rot.x * -_anchorPointInPoints.y * _scaleY; - - if (_ignoreAnchorPointForPosition) - { - x += _anchorPointInPoints.x; - y += _anchorPointInPoints.y; - } - - float mat[] = {(float)rot.x * _scaleX, - (float)rot.y * _scaleX, - 0, - 0, - (float)-rot.y * _scaleY, - (float)rot.x * _scaleY, - 0, - 0, - 0, - 0, - 1, - 0, - x, - y, - 0, - 1}; - - _transform.set(mat); - -# elif AX_ENABLE_BOX2D_INTEGRATION - - b2Vec2 pos = _pB2Body->GetPosition(); + b2Vec2 pos = b2Body_GetPosition(_bodyId); float x = pos.x * _PTMRatio; float y = pos.y * _PTMRatio; @@ -405,7 +289,8 @@ void PhysicsSprite::syncPhysicsTransform() const } // Make matrix - float radians = _pB2Body->GetAngle(); + auto rot = b2Body_GetRotation(_bodyId); + float radians = b2Rot_GetAngle(rot); float c = cosf(radians); float s = sinf(radians); @@ -435,7 +320,6 @@ void PhysicsSprite::syncPhysicsTransform() const 1}; _transform.set(mat); -# endif } void PhysicsSprite::onEnter() @@ -466,5 +350,3 @@ void PhysicsSprite::afterUpdate(EventCustom* /*event*/) } NS_AX_EXT_END - -#endif // AX_ENABLE_CHIPMUNK_INTEGRATION || AX_ENABLE_BOX2D_INTEGRATION diff --git a/extensions/physics-nodes/src/physics-nodes/PhysicsSprite.h b/extensions/physics-nodes/src/physics-nodes/PhysicsSprite.h index 12b4781889b3..a58d035358ae 100644 --- a/extensions/physics-nodes/src/physics-nodes/PhysicsSprite.h +++ b/extensions/physics-nodes/src/physics-nodes/PhysicsSprite.h @@ -22,26 +22,21 @@ * SOFTWARE. */ -#ifndef __PHYSICSNODES_CCPHYSICSSPRITE_H__ -#define __PHYSICSNODES_CCPHYSICSSPRITE_H__ +#ifndef __AX_PHYSICSNODES_PHYSICSSPRITE_H__ +#define __AX_PHYSICSNODES_PHYSICSSPRITE_H__ #include "2d/Sprite.h" #include "extensions/ExtensionMacros.h" #include "extensions/ExtensionExport.h" #include "base/EventListenerCustom.h" -#if (AX_ENABLE_CHIPMUNK_INTEGRATION || AX_ENABLE_BOX2D_INTEGRATION) - -struct cpBody; -class b2Body; +#include "box2d/box2d.h" NS_AX_EXT_BEGIN /** A Sprite subclass that is bound to a physics body. It works with: - - Chipmunk: Preprocessor macro AX_ENABLE_CHIPMUNK_INTEGRATION should be defined - - Objective-Chipmunk: Preprocessor macro AX_ENABLE_CHIPMUNK_INTEGRATION should be defined - - Box2d: Preprocessor macro AX_ENABLE_BOX2D_INTEGRATION should be defined + - Box2D Features and Limitations: - Scale and Skew properties are ignored. @@ -94,19 +89,12 @@ class AX_EX_DLL PhysicsSprite : public Sprite bool isIgnoreBodyRotation() const; void setIgnoreBodyRotation(bool bIgnoreBodyRotation); - // - // Chipmunk specific - // - /** Body accessor when using regular Chipmunk */ - cpBody* getCPBody() const; - void setCPBody(cpBody* pBody); - // // Box2d specific // /** Body accessor when using box2d */ - b2Body* getB2Body() const; - void setB2Body(b2Body* pBody); + b2BodyId getB2Body() const; + void setB2Body(b2BodyId pBody); float getPTMRatio() const; void setPTMRatio(float fPTMRatio); @@ -136,11 +124,8 @@ class AX_EX_DLL PhysicsSprite : public Sprite protected: bool _ignoreBodyRotation; - // chipmunk specific - cpBody* _CPBody; - // box2d specific - b2Body* _pB2Body; + b2BodyId _bodyId{}; float _PTMRatio; // Event for update synchronise physic transform @@ -149,6 +134,4 @@ class AX_EX_DLL PhysicsSprite : public Sprite NS_AX_EXT_END -#endif // AX_ENABLE_CHIPMUNK_INTEGRATION || AX_ENABLE_BOX2D_INTEGRATION - #endif // __PHYSICSNODES_CCPHYSICSSPRITE_H__ diff --git a/extensions/physics-nodes/src/physics-nodes/PhysicsSpriteBox2D.cpp b/extensions/physics-nodes/src/physics-nodes/PhysicsSpriteBox2D.cpp deleted file mode 100644 index 352d5b8fb7cf..000000000000 --- a/extensions/physics-nodes/src/physics-nodes/PhysicsSpriteBox2D.cpp +++ /dev/null @@ -1,352 +0,0 @@ -/* Copyright (c) 2012 Scott Lembcke and Howling Moon Software - * Copyright (c) 2012 cocos2d-x.org - * Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - * Copyright (c) 2021 @aismann; Peter Eismann, Germany; dreifrankensoft - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "PhysicsSpriteBox2D.h" -#include "base/Director.h" -#include "base/EventDispatcher.h" - -#include "box2d/box2d.h" - -NS_AX_EXT_BEGIN - -PhysicsSpriteBox2D::PhysicsSpriteBox2D() - : _ignoreBodyRotation(false), _pB2Body(nullptr), _PTMRatio(0.0f), _syncTransform(nullptr) -{} - -PhysicsSpriteBox2D* PhysicsSpriteBox2D::create() -{ - PhysicsSpriteBox2D* pRet = new PhysicsSpriteBox2D(); - if (pRet->init()) - { - pRet->autorelease(); - } - else - { - AX_SAFE_DELETE(pRet); - } - - return pRet; -} - -PhysicsSpriteBox2D* PhysicsSpriteBox2D::createWithTexture(Texture2D* pTexture) -{ - PhysicsSpriteBox2D* pRet = new PhysicsSpriteBox2D(); - if (pRet->initWithTexture(pTexture)) - { - pRet->autorelease(); - } - else - { - AX_SAFE_DELETE(pRet); - } - - return pRet; -} - -PhysicsSpriteBox2D* PhysicsSpriteBox2D::createWithTexture(Texture2D* pTexture, const Rect& rect) -{ - PhysicsSpriteBox2D* pRet = new PhysicsSpriteBox2D(); - if (pRet->initWithTexture(pTexture, rect)) - { - pRet->autorelease(); - } - else - { - AX_SAFE_DELETE(pRet); - } - - return pRet; -} - -PhysicsSpriteBox2D* PhysicsSpriteBox2D::createWithSpriteFrame(SpriteFrame* pSpriteFrame) -{ - PhysicsSpriteBox2D* pRet = new PhysicsSpriteBox2D(); - if (pRet->initWithSpriteFrame(pSpriteFrame)) - { - pRet->autorelease(); - } - else - { - AX_SAFE_DELETE(pRet); - } - - return pRet; -} - -PhysicsSpriteBox2D* PhysicsSpriteBox2D::createWithSpriteFrameName(const char* pszSpriteFrameName) -{ - PhysicsSpriteBox2D* pRet = new PhysicsSpriteBox2D(); - if (pRet->initWithSpriteFrameName(pszSpriteFrameName)) - { - pRet->autorelease(); - } - else - { - AX_SAFE_DELETE(pRet); - } - - return pRet; -} - -PhysicsSpriteBox2D* PhysicsSpriteBox2D::create(const char* pszFileName) -{ - PhysicsSpriteBox2D* pRet = new PhysicsSpriteBox2D(); - if (pRet->initWithFile(pszFileName)) - { - pRet->autorelease(); - } - else - { - AX_SAFE_DELETE(pRet); - } - - return pRet; -} - -PhysicsSpriteBox2D* PhysicsSpriteBox2D::create(const char* pszFileName, const Rect& rect) -{ - PhysicsSpriteBox2D* pRet = new PhysicsSpriteBox2D(); - if (pRet->initWithFile(pszFileName, rect)) - { - pRet->autorelease(); - } - else - { - AX_SAFE_DELETE(pRet); - } - - return pRet; -} - -// this method will only get called if the sprite is batched. -// return YES if the physic's values (angles, position ) changed. -// If you return NO, then getNodeToParentTransform won't be called. -bool PhysicsSpriteBox2D::isDirty() const -{ - return true; -} - -bool PhysicsSpriteBox2D::isIgnoreBodyRotation() const -{ - return _ignoreBodyRotation; -} - -void PhysicsSpriteBox2D::setIgnoreBodyRotation(bool bIgnoreBodyRotation) -{ - _ignoreBodyRotation = bIgnoreBodyRotation; -} - -// Override the setters and getters to always reflect the body's properties. -const Vec2& PhysicsSpriteBox2D::getPosition() const -{ - return getPosFromPhysics(); -} - -void PhysicsSpriteBox2D::getPosition(float* x, float* y) const -{ - if (x == nullptr || y == nullptr) - { - return; - } - const Vec2& pos = getPosFromPhysics(); - *x = pos.x; - *y = pos.y; -} - -float PhysicsSpriteBox2D::getPositionX() const -{ - return getPosFromPhysics().x; -} - -float PhysicsSpriteBox2D::getPositionY() const -{ - return getPosFromPhysics().y; -} - -Vec3 PhysicsSpriteBox2D::getPosition3D() const -{ - Vec2 pos = getPosFromPhysics(); - return Vec3(pos.x, pos.y, 0); -} - -b2Body* PhysicsSpriteBox2D::getB2Body() const -{ - return _pB2Body; -} - -void PhysicsSpriteBox2D::setB2Body(b2Body* pBody) -{ - _pB2Body = pBody; -} - -float PhysicsSpriteBox2D::getPTMRatio() const -{ - return _PTMRatio; -} - -void PhysicsSpriteBox2D::setPTMRatio(float fRatio) -{ - _PTMRatio = fRatio; -} - -// -// Common to Box2d and Chipmunk -// - -const Vec2& PhysicsSpriteBox2D::getPosFromPhysics() const -{ - static Vec2 s_physicPosion; - - b2Vec2 pos = _pB2Body->GetPosition(); - float x = pos.x * _PTMRatio; - float y = pos.y * _PTMRatio; - s_physicPosion.set(x, y); - - return s_physicPosion; -} - -void PhysicsSpriteBox2D::setPosition(float x, float y) -{ - float angle = _pB2Body->GetAngle(); - _pB2Body->SetTransform(b2Vec2(x / _PTMRatio, y / _PTMRatio), angle); -} - -void PhysicsSpriteBox2D::setPosition(const Vec2& pos) -{ - setPosition(pos.x, pos.y); -} - -void PhysicsSpriteBox2D::setPositionX(float x) -{ - setPosition(x, getPositionY()); -} - -void PhysicsSpriteBox2D::setPositionY(float y) -{ - setPosition(getPositionX(), y); -} - -void PhysicsSpriteBox2D::setPosition3D(const Vec3& position) -{ - setPosition(position.x, position.y); -} - -float PhysicsSpriteBox2D::getRotation() const -{ - return (_ignoreBodyRotation ? Sprite::getRotation() : AX_RADIANS_TO_DEGREES(_pB2Body->GetAngle())); -} - -void PhysicsSpriteBox2D::setRotation(float fRotation) -{ - if (_ignoreBodyRotation) - { - Sprite::setRotation(fRotation); - } - - else - { - b2Vec2 p = _pB2Body->GetPosition(); - float radians = AX_DEGREES_TO_RADIANS(fRotation); - _pB2Body->SetTransform(p, radians); - } -} - -void PhysicsSpriteBox2D::syncPhysicsTransform() const -{ - // Although scale is not used by physics engines, it is calculated just in case - // the sprite is animated (scaled up/down) using actions. - // For more info see: http://www.cocos2d-iphone.org/forum/topic/68990 - - b2Vec2 pos = _pB2Body->GetPosition(); - - float x = pos.x * _PTMRatio; - float y = pos.y * _PTMRatio; - - if (_ignoreAnchorPointForPosition) - { - x += _anchorPointInPoints.x; - y += _anchorPointInPoints.y; - } - - // Make matrix - float radians = _pB2Body->GetAngle(); - float c = cosf(radians); - float s = sinf(radians); - - if (!_anchorPointInPoints.isZero()) - { - x += ((c * -_anchorPointInPoints.x * _scaleX) + (-s * -_anchorPointInPoints.y * _scaleY)); - y += ((s * -_anchorPointInPoints.x * _scaleX) + (c * -_anchorPointInPoints.y * _scaleY)); - } - - // Rot, Translate Matrix - - float mat[] = {(float)c * _scaleX, - (float)s * _scaleX, - 0, - 0, - (float)-s * _scaleY, - (float)c * _scaleY, - 0, - 0, - 0, - 0, - 1, - 0, - x, - y, - 0, - 1}; - - _transform.set(mat); -} - -void PhysicsSpriteBox2D::onEnter() -{ - Node::onEnter(); - _syncTransform = Director::getInstance()->getEventDispatcher()->addCustomEventListener( - Director::EVENT_AFTER_UPDATE, std::bind(&PhysicsSpriteBox2D::afterUpdate, this, std::placeholders::_1)); - _syncTransform->retain(); -} - -void PhysicsSpriteBox2D::onExit() -{ - if (_syncTransform != nullptr) - { - Director::getInstance()->getEventDispatcher()->removeEventListener(_syncTransform); - _syncTransform->release(); - } - Node::onExit(); -} - -void PhysicsSpriteBox2D::afterUpdate(EventCustom* /*event*/) -{ - syncPhysicsTransform(); - - _transformDirty = false; - _transformUpdated = true; - setDirtyRecursively(true); -} - -NS_AX_EXT_END \ No newline at end of file diff --git a/extensions/physics-nodes/src/physics-nodes/PhysicsSpriteBox2D.h b/extensions/physics-nodes/src/physics-nodes/PhysicsSpriteBox2D.h deleted file mode 100644 index 3ded174c1c3a..000000000000 --- a/extensions/physics-nodes/src/physics-nodes/PhysicsSpriteBox2D.h +++ /dev/null @@ -1,137 +0,0 @@ -/* Copyright (c) 2012 Scott Lembcke and Howling Moon Software - * Copyright (c) 2012 cocos2d-x.org - * Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - * Copyright (c) 2021 @aismann; Peter Eismann, Germany; dreifrankensoft - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef __PHYSICSNODES_CCPHYSICSSPRITEBOX2D_H__ -#define __PHYSICSNODES_CCPHYSICSSPRITEBOX2D_H__ - -#include "2d/Sprite.h" -#include "extensions/ExtensionMacros.h" -#include "extensions/ExtensionExport.h" -#include "base/EventListenerCustom.h" - -class b2Body; - -NS_AX_EXT_BEGIN - -/** A Sprite subclass that is bound to a physics body. - It works with: - - Box2D - - Features and Limitations: - - Scale and Skew properties are ignored. - - Position and rotation are going to updated from the physics body - - If you update the rotation or position manually, the physics body will be updated - - You can't enble both Chipmunk support and Box2d support at the same time. Only one can be enabled at compile time - * @lua NA - */ -class AX_EX_DLL PhysicsSpriteBox2D : public Sprite -{ -public: - static PhysicsSpriteBox2D* create(); - /** Creates an sprite with a texture. - The rect used will be the size of the texture. - The offset will be (0,0). - */ - static PhysicsSpriteBox2D* createWithTexture(Texture2D* pTexture); - - /** Creates an sprite with a texture and a rect. - The offset will be (0,0). - */ - static PhysicsSpriteBox2D* createWithTexture(Texture2D* pTexture, const Rect& rect); - - /** Creates an sprite with an sprite frame. */ - static PhysicsSpriteBox2D* createWithSpriteFrame(SpriteFrame* pSpriteFrame); - - /** Creates an sprite with an sprite frame name. - An SpriteFrame will be fetched from the SpriteFrameCache by name. - If the SpriteFrame doesn't exist it will raise an exception. - @since v0.9 - */ - static PhysicsSpriteBox2D* createWithSpriteFrameName(const char* pszSpriteFrameName); - - /** Creates an sprite with an image filename. - The rect used will be the size of the image. - The offset will be (0,0). - */ - static PhysicsSpriteBox2D* create(const char* pszFileName); - - /** Creates an sprite with an image filename and a rect. - The offset will be (0,0). - */ - static PhysicsSpriteBox2D* create(const char* pszFileName, const Rect& rect); - - PhysicsSpriteBox2D(); - - virtual bool isDirty() const override; - - /** Keep the sprite's rotation separate from the body. */ - bool isIgnoreBodyRotation() const; - void setIgnoreBodyRotation(bool bIgnoreBodyRotation); - - // - // Box2d specific - // - /** Body accessor when using box2d */ - b2Body* getB2Body() const; - void setB2Body(b2Body* pBody); - - float getPTMRatio() const; - void setPTMRatio(float fPTMRatio); - virtual void syncPhysicsTransform() const; - - // overrides - virtual const Vec2& getPosition() const override; - virtual void getPosition(float* x, float* y) const override; - virtual float getPositionX() const override; - virtual float getPositionY() const override; - virtual Vec3 getPosition3D() const override; - virtual void setPosition(const Vec2& position) override; - virtual void setPosition(float x, float y) override; - virtual void setPositionX(float x) override; - virtual void setPositionY(float y) override; - virtual void setPosition3D(const Vec3& position) override; - virtual float getRotation() const override; - virtual void setRotation(float fRotation) override; - - virtual void onEnter() override; - virtual void onExit() override; - -protected: - const Vec2& getPosFromPhysics() const; - void afterUpdate(EventCustom* event); - -protected: - bool _ignoreBodyRotation; - - // box2d specific - b2Body* _pB2Body; - float _PTMRatio; - - // Event for update synchronise physic transform - ax::EventListenerCustom* _syncTransform; -}; - -NS_AX_EXT_END - -#endif // __PHYSICSNODES_CCPHYSICSSPRITEBOX2D_H__ diff --git a/extensions/physics-nodes/src/physics-nodes/PhysicsSpriteChipmunk2D.cpp b/extensions/physics-nodes/src/physics-nodes/PhysicsSpriteChipmunk2D.cpp deleted file mode 100644 index 56f6377868b5..000000000000 --- a/extensions/physics-nodes/src/physics-nodes/PhysicsSpriteChipmunk2D.cpp +++ /dev/null @@ -1,329 +0,0 @@ -/* Copyright (c) 2012 Scott Lembcke and Howling Moon Software - * Copyright (c) 2012 cocos2d-x.org - * Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - * Copyright (c) 2021 @aismann; Peter Eismann, Germany; dreifrankensoft - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "PhysicsSpriteChipmunk2D.h" -#include "base/Director.h" -#include "base/EventDispatcher.h" - -#include "chipmunk/chipmunk.h" - -NS_AX_EXT_BEGIN - -PhysicsSpriteChipmunk2D::PhysicsSpriteChipmunk2D() - : _ignoreBodyRotation(false), _CPBody(nullptr), _syncTransform(nullptr) -{} - -PhysicsSpriteChipmunk2D* PhysicsSpriteChipmunk2D::create() -{ - PhysicsSpriteChipmunk2D* pRet = new PhysicsSpriteChipmunk2D(); - if (pRet->init()) - { - pRet->autorelease(); - } - else - { - AX_SAFE_DELETE(pRet); - } - - return pRet; -} - -PhysicsSpriteChipmunk2D* PhysicsSpriteChipmunk2D::createWithTexture(Texture2D* pTexture) -{ - PhysicsSpriteChipmunk2D* pRet = new PhysicsSpriteChipmunk2D(); - if (pRet->initWithTexture(pTexture)) - { - pRet->autorelease(); - } - else - { - AX_SAFE_DELETE(pRet); - } - - return pRet; -} - -PhysicsSpriteChipmunk2D* PhysicsSpriteChipmunk2D::createWithTexture(Texture2D* pTexture, const Rect& rect) -{ - PhysicsSpriteChipmunk2D* pRet = new PhysicsSpriteChipmunk2D(); - if (pRet->initWithTexture(pTexture, rect)) - { - pRet->autorelease(); - } - else - { - AX_SAFE_DELETE(pRet); - } - - return pRet; -} - -PhysicsSpriteChipmunk2D* PhysicsSpriteChipmunk2D::createWithSpriteFrame(SpriteFrame* pSpriteFrame) -{ - PhysicsSpriteChipmunk2D* pRet = new PhysicsSpriteChipmunk2D(); - if (pRet->initWithSpriteFrame(pSpriteFrame)) - { - pRet->autorelease(); - } - else - { - AX_SAFE_DELETE(pRet); - } - - return pRet; -} - -PhysicsSpriteChipmunk2D* PhysicsSpriteChipmunk2D::createWithSpriteFrameName(const char* pszSpriteFrameName) -{ - PhysicsSpriteChipmunk2D* pRet = new PhysicsSpriteChipmunk2D(); - if (pRet->initWithSpriteFrameName(pszSpriteFrameName)) - { - pRet->autorelease(); - } - else - { - AX_SAFE_DELETE(pRet); - } - - return pRet; -} - -PhysicsSpriteChipmunk2D* PhysicsSpriteChipmunk2D::create(const char* pszFileName) -{ - PhysicsSpriteChipmunk2D* pRet = new PhysicsSpriteChipmunk2D(); - if (pRet->initWithFile(pszFileName)) - { - pRet->autorelease(); - } - else - { - AX_SAFE_DELETE(pRet); - } - - return pRet; -} - -PhysicsSpriteChipmunk2D* PhysicsSpriteChipmunk2D::create(const char* pszFileName, const Rect& rect) -{ - PhysicsSpriteChipmunk2D* pRet = new PhysicsSpriteChipmunk2D(); - if (pRet->initWithFile(pszFileName, rect)) - { - pRet->autorelease(); - } - else - { - AX_SAFE_DELETE(pRet); - } - - return pRet; -} - -// this method will only get called if the sprite is batched. -// return YES if the physic's values (angles, position ) changed. -// If you return NO, then getNodeToParentTransform won't be called. -bool PhysicsSpriteChipmunk2D::isDirty() const -{ - return true; -} - -bool PhysicsSpriteChipmunk2D::isIgnoreBodyRotation() const -{ - return _ignoreBodyRotation; -} - -void PhysicsSpriteChipmunk2D::setIgnoreBodyRotation(bool bIgnoreBodyRotation) -{ - _ignoreBodyRotation = bIgnoreBodyRotation; -} - -// Override the setters and getters to always reflect the body's properties. -const Vec2& PhysicsSpriteChipmunk2D::getPosition() const -{ - return getPosFromPhysics(); -} - -void PhysicsSpriteChipmunk2D::getPosition(float* x, float* y) const -{ - if (x == nullptr || y == nullptr) - { - return; - } - const Vec2& pos = getPosFromPhysics(); - *x = pos.x; - *y = pos.y; -} - -float PhysicsSpriteChipmunk2D::getPositionX() const -{ - return getPosFromPhysics().x; -} - -float PhysicsSpriteChipmunk2D::getPositionY() const -{ - return getPosFromPhysics().y; -} - -Vec3 PhysicsSpriteChipmunk2D::getPosition3D() const -{ - Vec2 pos = getPosFromPhysics(); - return Vec3(pos.x, pos.y, 0); -} - -// -// Chipmunk only -// - -cpBody* PhysicsSpriteChipmunk2D::getCPBody() const -{ - return _CPBody; -} - -void PhysicsSpriteChipmunk2D::setCPBody(cpBody* pBody) -{ - _CPBody = pBody; -} - -// -// Common to Box2d and Chipmunk -// - -const Vec2& PhysicsSpriteChipmunk2D::getPosFromPhysics() const -{ - static Vec2 s_physicPosion; - - cpVect cpPos = cpBodyGetPosition(_CPBody); - s_physicPosion = Vec2(cpPos.x, cpPos.y); - - return s_physicPosion; -} - -void PhysicsSpriteChipmunk2D::setPosition(float x, float y) -{ - cpVect cpPos = cpv(x, y); - cpBodySetPosition(_CPBody, cpPos); -} - -void PhysicsSpriteChipmunk2D::setPosition(const Vec2& pos) -{ - setPosition(pos.x, pos.y); -} - -void PhysicsSpriteChipmunk2D::setPositionX(float x) -{ - setPosition(x, getPositionY()); -} - -void PhysicsSpriteChipmunk2D::setPositionY(float y) -{ - setPosition(getPositionX(), y); -} - -void PhysicsSpriteChipmunk2D::setPosition3D(const Vec3& position) -{ - setPosition(position.x, position.y); -} - -float PhysicsSpriteChipmunk2D::getRotation() const -{ - return (_ignoreBodyRotation ? Sprite::getRotation() : -AX_RADIANS_TO_DEGREES(cpBodyGetAngle(_CPBody))); -} - -void PhysicsSpriteChipmunk2D::setRotation(float fRotation) -{ - if (_ignoreBodyRotation) - { - Sprite::setRotation(fRotation); - } - else - { - cpBodySetAngle(_CPBody, -AX_DEGREES_TO_RADIANS(fRotation)); - } -} - -void PhysicsSpriteChipmunk2D::syncPhysicsTransform() const -{ - // Although scale is not used by physics engines, it is calculated just in case - // the sprite is animated (scaled up/down) using actions. - // For more info see: http://www.cocos2d-iphone.org/forum/topic/68990 - - cpVect rot = (_ignoreBodyRotation ? cpvforangle(-AX_DEGREES_TO_RADIANS(_rotationX)) : cpBodyGetRotation(_CPBody)); - float x = cpBodyGetPosition(_CPBody).x + rot.x * -_anchorPointInPoints.x * _scaleX - - rot.y * -_anchorPointInPoints.y * _scaleY; - float y = cpBodyGetPosition(_CPBody).y + rot.y * -_anchorPointInPoints.x * _scaleX + - rot.x * -_anchorPointInPoints.y * _scaleY; - - if (_ignoreAnchorPointForPosition) - { - x += _anchorPointInPoints.x; - y += _anchorPointInPoints.y; - } - - float mat[] = {(float)rot.x * _scaleX, - (float)rot.y * _scaleX, - 0, - 0, - (float)-rot.y * _scaleY, - (float)rot.x * _scaleY, - 0, - 0, - 0, - 0, - 1, - 0, - x, - y, - 0, - 1}; - - _transform.set(mat); -} - -void PhysicsSpriteChipmunk2D::onEnter() -{ - Node::onEnter(); - _syncTransform = Director::getInstance()->getEventDispatcher()->addCustomEventListener( - Director::EVENT_AFTER_UPDATE, std::bind(&PhysicsSpriteChipmunk2D::afterUpdate, this, std::placeholders::_1)); - _syncTransform->retain(); -} - -void PhysicsSpriteChipmunk2D::onExit() -{ - if (_syncTransform != nullptr) - { - Director::getInstance()->getEventDispatcher()->removeEventListener(_syncTransform); - _syncTransform->release(); - } - Node::onExit(); -} - -void PhysicsSpriteChipmunk2D::afterUpdate(EventCustom* /*event*/) -{ - syncPhysicsTransform(); - - _transformDirty = false; - _transformUpdated = true; - setDirtyRecursively(true); -} - -NS_AX_EXT_END \ No newline at end of file diff --git a/extensions/physics-nodes/src/physics-nodes/PhysicsSpriteChipmunk2D.h b/extensions/physics-nodes/src/physics-nodes/PhysicsSpriteChipmunk2D.h deleted file mode 100644 index b2be1bd709c4..000000000000 --- a/extensions/physics-nodes/src/physics-nodes/PhysicsSpriteChipmunk2D.h +++ /dev/null @@ -1,136 +0,0 @@ -/* Copyright (c) 2012 Scott Lembcke and Howling Moon Software - * Copyright (c) 2012 cocos2d-x.org - * Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - * Copyright (c) 2021 @aismann; Peter Eismann, Germany; dreifrankensoft - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef __PHYSICSNODES_CCPhysicsSpriteChipmunk2DCHIPMUNK2D_H__ -#define __PHYSICSNODES_CCPhysicsSpriteChipmunk2DCHIPMUNK2D_H__ - -#include "2d/Sprite.h" -#include "extensions/ExtensionMacros.h" -#include "extensions/ExtensionExport.h" -#include "base/EventListenerCustom.h" - -struct cpBody; - -NS_AX_EXT_BEGIN - -/** A Sprite subclass that is bound to a physics body. - It works with: - - Chipmunk2D: - - Features and Limitations: - - Scale and Skew properties are ignored. - - Position and rotation are going to updated from the physics body - - If you update the rotation or position manually, the physics body will be updated - - You can't enble both Chipmunk support and Box2d support at the same time. Only one can be enabled at compile time - * @lua NA - */ -class AX_EX_DLL PhysicsSpriteChipmunk2D : public Sprite -{ -public: - static PhysicsSpriteChipmunk2D* create(); - /** Creates an sprite with a texture. - The rect used will be the size of the texture. - The offset will be (0,0). - */ - static PhysicsSpriteChipmunk2D* createWithTexture(Texture2D* pTexture); - - /** Creates an sprite with a texture and a rect. - The offset will be (0,0). - */ - static PhysicsSpriteChipmunk2D* createWithTexture(Texture2D* pTexture, const Rect& rect); - - /** Creates an sprite with an sprite frame. */ - static PhysicsSpriteChipmunk2D* createWithSpriteFrame(SpriteFrame* pSpriteFrame); - - /** Creates an sprite with an sprite frame name. - An SpriteFrame will be fetched from the SpriteFrameCache by name. - If the SpriteFrame doesn't exist it will raise an exception. - @since v0.9 - */ - static PhysicsSpriteChipmunk2D* createWithSpriteFrameName(const char* pszSpriteFrameName); - - /** Creates an sprite with an image filename. - The rect used will be the size of the image. - The offset will be (0,0). - */ - static PhysicsSpriteChipmunk2D* create(const char* pszFileName); - - /** Creates an sprite with an image filename and a rect. - The offset will be (0,0). - */ - static PhysicsSpriteChipmunk2D* create(const char* pszFileName, const Rect& rect); - - PhysicsSpriteChipmunk2D(); - - virtual bool isDirty() const override; - - /** Keep the sprite's rotation separate from the body. */ - bool isIgnoreBodyRotation() const; - void setIgnoreBodyRotation(bool bIgnoreBodyRotation); - - // - // Chipmunk specific - // - /** Body accessor when using regular Chipmunk */ - cpBody* getCPBody() const; - void setCPBody(cpBody* pBody); - - float getPTMRatio() const; - void setPTMRatio(float fPTMRatio); - virtual void syncPhysicsTransform() const; - - // overrides - virtual const Vec2& getPosition() const override; - virtual void getPosition(float* x, float* y) const override; - virtual float getPositionX() const override; - virtual float getPositionY() const override; - virtual Vec3 getPosition3D() const override; - virtual void setPosition(const Vec2& position) override; - virtual void setPosition(float x, float y) override; - virtual void setPositionX(float x) override; - virtual void setPositionY(float y) override; - virtual void setPosition3D(const Vec3& position) override; - virtual float getRotation() const override; - virtual void setRotation(float fRotation) override; - - virtual void onEnter() override; - virtual void onExit() override; - -protected: - const Vec2& getPosFromPhysics() const; - void afterUpdate(EventCustom* event); - -protected: - bool _ignoreBodyRotation; - - // chipmunk specific - cpBody* _CPBody; - - // Event for update synchronise physic transform - ax::EventListenerCustom* _syncTransform; -}; - -NS_AX_EXT_END - -#endif // __PHYSICSNODES_CCPhysicsSpriteChipmunk2DCHIPMUNK2D_H__ diff --git a/extensions/scripting/lua-bindings/auto/axlua_backend_auto.cpp b/extensions/scripting/lua-bindings/auto/axlua_backend_auto.cpp index 09647419e849..e7c16a8511ec 100644 --- a/extensions/scripting/lua-bindings/auto/axlua_backend_auto.cpp +++ b/extensions/scripting/lua-bindings/auto/axlua_backend_auto.cpp @@ -77,6 +77,7 @@ int lua_register_ax_backend_VertexFormat(lua_State* tolua_S) tolua_constant(tolua_S, "USHORT4", 8); tolua_constant(tolua_S, "USHORT2", 9); tolua_constant(tolua_S, "UBYTE4", 10); + tolua_constant(tolua_S, "MAT4", 11); tolua_endmodule(tolua_S); auto typeName = typeid(ax::backend::VertexFormat).name(); // rtti is literal storage @@ -748,7 +749,7 @@ int lua_ax_backend_Program_getFragmentShader(lua_State* tolua_S) return 0; } -int lua_ax_backend_Program_setupVertexLayout(lua_State* tolua_S) +int lua_ax_backend_Program_defineVertexLayout(lua_State* tolua_S) { int argc = 0; ax::backend::Program* cobj = nullptr; @@ -768,7 +769,7 @@ int lua_ax_backend_Program_setupVertexLayout(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_backend_Program_setupVertexLayout'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_backend_Program_defineVertexLayout'", nullptr); return 0; } #endif @@ -778,22 +779,22 @@ int lua_ax_backend_Program_setupVertexLayout(lua_State* tolua_S) { ax::backend::VertexLayoutType arg0; - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "axb.Program:setupVertexLayout"); + ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "axb.Program:defineVertexLayout"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_backend_Program_setupVertexLayout'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_backend_Program_defineVertexLayout'", nullptr); return 0; } - cobj->setupVertexLayout(arg0); + cobj->defineVertexLayout(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "axb.Program:setupVertexLayout",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "axb.Program:defineVertexLayout",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_backend_Program_setupVertexLayout'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_backend_Program_defineVertexLayout'.",&tolua_err); #endif return 0; @@ -979,6 +980,20 @@ int lua_ax_backend_Program_getVertexLayout(lua_State* tolua_S) object_to_luaval(tolua_S, "axb.VertexLayout",(ax::backend::VertexLayout*)ret); return 1; } + if (argc == 1) + { + bool arg0; + + ok &= luaval_to_boolean(tolua_S, 2,&arg0, "axb.Program:getVertexLayout"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_backend_Program_getVertexLayout'", nullptr); + return 0; + } + auto&& ret = cobj->getVertexLayout(arg0); + object_to_luaval(tolua_S, "axb.VertexLayout",(ax::backend::VertexLayout*)ret); + return 1; + } luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "axb.Program:getVertexLayout",argc, 0); return 0; @@ -1044,7 +1059,7 @@ int lua_register_ax_backend_Program(lua_State* tolua_S) tolua_function(tolua_S,"getActiveAttributes",lua_ax_backend_Program_getActiveAttributes); tolua_function(tolua_S,"getVertexShader",lua_ax_backend_Program_getVertexShader); tolua_function(tolua_S,"getFragmentShader",lua_ax_backend_Program_getFragmentShader); - tolua_function(tolua_S,"setupVertexLayout",lua_ax_backend_Program_setupVertexLayout); + tolua_function(tolua_S,"defineVertexLayout",lua_ax_backend_Program_defineVertexLayout); tolua_function(tolua_S,"getProgramType",lua_ax_backend_Program_getProgramType); tolua_function(tolua_S,"getProgramId",lua_ax_backend_Program_getProgramId); tolua_function(tolua_S,"getUniformBufferSize",lua_ax_backend_Program_getUniformBufferSize); @@ -1582,6 +1597,20 @@ int lua_ax_backend_ProgramState_getMutableVertexLayout(lua_State* tolua_S) object_to_luaval(tolua_S, "axb.VertexLayout",(ax::backend::VertexLayout*)ret); return 1; } + if (argc == 1) + { + bool arg0; + + ok &= luaval_to_boolean(tolua_S, 2,&arg0, "axb.ProgramState:getMutableVertexLayout"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_backend_ProgramState_getMutableVertexLayout'", nullptr); + return 0; + } + auto&& ret = cobj->getMutableVertexLayout(arg0); + object_to_luaval(tolua_S, "axb.VertexLayout",(ax::backend::VertexLayout*)ret); + return 1; + } luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "axb.ProgramState:getMutableVertexLayout",argc, 0); return 0; diff --git a/extensions/scripting/lua-bindings/auto/axlua_base_auto.cpp b/extensions/scripting/lua-bindings/auto/axlua_base_auto.cpp index e64451b9cb6d..6181c0f11330 100644 --- a/extensions/scripting/lua-bindings/auto/axlua_base_auto.cpp +++ b/extensions/scripting/lua-bindings/auto/axlua_base_auto.cpp @@ -171,10 +171,10 @@ int lua_register_ax_base_Object(lua_State* tolua_S) return 1; } -int lua_ax_base_EventListener_checkAvailable(lua_State* tolua_S) +int lua_ax_base_Touch_getLocation(lua_State* tolua_S) { int argc = 0; - ax::EventListener* cobj = nullptr; + ax::Touch* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -183,15 +183,15 @@ int lua_ax_base_EventListener_checkAvailable(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.EventListener",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Touch",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::EventListener*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Touch*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_EventListener_checkAvailable'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Touch_getLocation'", nullptr); return 0; } #endif @@ -201,27 +201,27 @@ int lua_ax_base_EventListener_checkAvailable(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_EventListener_checkAvailable'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Touch_getLocation'", nullptr); return 0; } - auto&& ret = cobj->checkAvailable(); - tolua_pushboolean(tolua_S,(bool)ret); + auto&& ret = cobj->getLocation(); + vec2_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.EventListener:checkAvailable",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Touch:getLocation",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_EventListener_checkAvailable'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Touch_getLocation'.",&tolua_err); #endif return 0; } -int lua_ax_base_EventListener_clone(lua_State* tolua_S) +int lua_ax_base_Touch_getPreviousLocation(lua_State* tolua_S) { int argc = 0; - ax::EventListener* cobj = nullptr; + ax::Touch* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -230,15 +230,15 @@ int lua_ax_base_EventListener_clone(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.EventListener",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Touch",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::EventListener*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Touch*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_EventListener_clone'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Touch_getPreviousLocation'", nullptr); return 0; } #endif @@ -248,27 +248,27 @@ int lua_ax_base_EventListener_clone(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_EventListener_clone'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Touch_getPreviousLocation'", nullptr); return 0; } - auto&& ret = cobj->clone(); - object_to_luaval(tolua_S, "ax.EventListener",(ax::EventListener*)ret); + auto&& ret = cobj->getPreviousLocation(); + vec2_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.EventListener:clone",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Touch:getPreviousLocation",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_EventListener_clone'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Touch_getPreviousLocation'.",&tolua_err); #endif return 0; } -int lua_ax_base_EventListener_setEnabled(lua_State* tolua_S) +int lua_ax_base_Touch_getStartLocation(lua_State* tolua_S) { int argc = 0; - ax::EventListener* cobj = nullptr; + ax::Touch* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -277,48 +277,45 @@ int lua_ax_base_EventListener_setEnabled(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.EventListener",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Touch",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::EventListener*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Touch*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_EventListener_setEnabled'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Touch_getStartLocation'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - bool arg0; - - ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.EventListener:setEnabled"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_EventListener_setEnabled'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Touch_getStartLocation'", nullptr); return 0; } - cobj->setEnabled(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getStartLocation(); + vec2_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.EventListener:setEnabled",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Touch:getStartLocation",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_EventListener_setEnabled'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Touch_getStartLocation'.",&tolua_err); #endif return 0; } -int lua_ax_base_EventListener_isEnabled(lua_State* tolua_S) +int lua_ax_base_Touch_getDelta(lua_State* tolua_S) { int argc = 0; - ax::EventListener* cobj = nullptr; + ax::Touch* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -327,15 +324,15 @@ int lua_ax_base_EventListener_isEnabled(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.EventListener",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Touch",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::EventListener*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Touch*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_EventListener_isEnabled'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Touch_getDelta'", nullptr); return 0; } #endif @@ -345,50 +342,27 @@ int lua_ax_base_EventListener_isEnabled(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_EventListener_isEnabled'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Touch_getDelta'", nullptr); return 0; } - auto&& ret = cobj->isEnabled(); - tolua_pushboolean(tolua_S,(bool)ret); + auto&& ret = cobj->getDelta(); + vec2_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.EventListener:isEnabled",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Touch:getDelta",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_EventListener_isEnabled'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Touch_getDelta'.",&tolua_err); #endif return 0; } -static int lua_ax_base_EventListener_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (EventListener)"); - return 0; -} - -int lua_register_ax_base_EventListener(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.EventListener"); - tolua_cclass(tolua_S,"EventListener","ax.EventListener","ax.Object",nullptr); - - tolua_beginmodule(tolua_S,"EventListener"); - tolua_function(tolua_S,"checkAvailable",lua_ax_base_EventListener_checkAvailable); - tolua_function(tolua_S,"clone",lua_ax_base_EventListener_clone); - tolua_function(tolua_S,"setEnabled",lua_ax_base_EventListener_setEnabled); - tolua_function(tolua_S,"isEnabled",lua_ax_base_EventListener_isEnabled); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::EventListener).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.EventListener"; - g_typeCast[typeName] = "ax.EventListener"; - return 1; -} - -int lua_ax_base_EventListenerCustom_constructor(lua_State* tolua_S) +int lua_ax_base_Touch_getLocationInView(lua_State* tolua_S) { int argc = 0; - ax::EventListenerCustom* cobj = nullptr; + ax::Touch* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -396,56 +370,46 @@ int lua_ax_base_EventListenerCustom_constructor(lua_State* tolua_S) #endif +#if _AX_DEBUG >= 1 + if (!tolua_isusertype(tolua_S,1,"ax.Touch",0,&tolua_err)) goto tolua_lerror; +#endif + + cobj = (ax::Touch*)tolua_tousertype(tolua_S,1,0); + +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Touch_getLocationInView'", nullptr); + return 0; + } +#endif argc = lua_gettop(tolua_S)-1; if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_EventListenerCustom_constructor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Touch_getLocationInView'", nullptr); return 0; } - cobj = new ax::EventListenerCustom(); - cobj->autorelease(); - int ID = (int)cobj->_ID ; - int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.EventListenerCustom"); + auto&& ret = cobj->getLocationInView(); + vec2_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.EventListenerCustom:EventListenerCustom",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Touch:getLocationInView",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_EventListenerCustom_constructor'.",&tolua_err); + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Touch_getLocationInView'.",&tolua_err); #endif return 0; } - -static int lua_ax_base_EventListenerCustom_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (EventListenerCustom)"); - return 0; -} - -int lua_register_ax_base_EventListenerCustom(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.EventListenerCustom"); - tolua_cclass(tolua_S,"EventListenerCustom","ax.EventListenerCustom","ax.EventListener",nullptr); - - tolua_beginmodule(tolua_S,"EventListenerCustom"); - tolua_function(tolua_S,"new",lua_ax_base_EventListenerCustom_constructor); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::EventListenerCustom).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.EventListenerCustom"; - g_typeCast[typeName] = "ax.EventListenerCustom"; - return 1; -} - -int lua_ax_base_ShaderCache_purge(lua_State* tolua_S) +int lua_ax_base_Touch_getPreviousLocationInView(lua_State* tolua_S) { int argc = 0; - ax::backend::ShaderCache* cobj = nullptr; + ax::Touch* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -454,15 +418,15 @@ int lua_ax_base_ShaderCache_purge(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"axb.ShaderCache",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Touch",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::backend::ShaderCache*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Touch*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_ShaderCache_purge'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Touch_getPreviousLocationInView'", nullptr); return 0; } #endif @@ -472,27 +436,27 @@ int lua_ax_base_ShaderCache_purge(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_ShaderCache_purge'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Touch_getPreviousLocationInView'", nullptr); return 0; } - cobj->purge(); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getPreviousLocationInView(); + vec2_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "axb.ShaderCache:purge",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Touch:getPreviousLocationInView",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_ShaderCache_purge'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Touch_getPreviousLocationInView'.",&tolua_err); #endif return 0; } -int lua_ax_base_ShaderCache_newVertexShaderModule(lua_State* tolua_S) +int lua_ax_base_Touch_getStartLocationInView(lua_State* tolua_S) { int argc = 0; - ax::backend::ShaderCache* cobj = nullptr; + ax::Touch* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -501,48 +465,124 @@ int lua_ax_base_ShaderCache_newVertexShaderModule(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"axb.ShaderCache",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Touch",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::backend::ShaderCache*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Touch*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_ShaderCache_newVertexShaderModule'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Touch_getStartLocationInView'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - std::string_view arg0; - - ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "axb.ShaderCache:newVertexShaderModule"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_ShaderCache_newVertexShaderModule'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Touch_getStartLocationInView'", nullptr); return 0; } - auto&& ret = cobj->newVertexShaderModule(arg0); - object_to_luaval(tolua_S, "axb.ShaderModule",(ax::backend::ShaderModule*)ret); + auto&& ret = cobj->getStartLocationInView(); + vec2_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "axb.ShaderCache:newVertexShaderModule",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Touch:getStartLocationInView",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_ShaderCache_newVertexShaderModule'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Touch_getStartLocationInView'.",&tolua_err); #endif return 0; } -int lua_ax_base_ShaderCache_newFragmentShaderModule(lua_State* tolua_S) +int lua_ax_base_Touch_setTouchInfo(lua_State* tolua_S) { int argc = 0; - ax::backend::ShaderCache* cobj = nullptr; + ax::Touch* cobj = nullptr; + bool ok = true; +#if _AX_DEBUG >= 1 + tolua_Error tolua_err; +#endif + +#if _AX_DEBUG >= 1 + if (!tolua_isusertype(tolua_S,1,"ax.Touch",0,&tolua_err)) goto tolua_lerror; +#endif + cobj = (ax::Touch*)tolua_tousertype(tolua_S,1,0); +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Touch_setTouchInfo'", nullptr); + return 0; + } +#endif + argc = lua_gettop(tolua_S)-1; + do{ + if (argc == 5) { + int arg0; + ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Touch:setTouchInfo"); + + if (!ok) { break; } + double arg1; + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Touch:setTouchInfo"); + + if (!ok) { break; } + double arg2; + ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.Touch:setTouchInfo"); + + if (!ok) { break; } + double arg3; + ok &= luaval_to_number(tolua_S, 5,&arg3, "ax.Touch:setTouchInfo"); + + if (!ok) { break; } + double arg4; + ok &= luaval_to_number(tolua_S, 6,&arg4, "ax.Touch:setTouchInfo"); + + if (!ok) { break; } + cobj->setTouchInfo(arg0, arg1, arg2, arg3, arg4); + lua_settop(tolua_S, 1); + return 1; + } + }while(0); + ok = true; + do{ + if (argc == 3) { + int arg0; + ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Touch:setTouchInfo"); + + if (!ok) { break; } + double arg1; + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Touch:setTouchInfo"); + + if (!ok) { break; } + double arg2; + ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.Touch:setTouchInfo"); + + if (!ok) { break; } + cobj->setTouchInfo(arg0, arg1, arg2); + lua_settop(tolua_S, 1); + return 1; + } + }while(0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Touch:setTouchInfo",argc, 3); + return 0; + +#if _AX_DEBUG >= 1 + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Touch_setTouchInfo'.",&tolua_err); +#endif + + return 0; +} +int lua_ax_base_Touch_getID(lua_State* tolua_S) +{ + int argc = 0; + ax::Touch* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -551,48 +591,45 @@ int lua_ax_base_ShaderCache_newFragmentShaderModule(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"axb.ShaderCache",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Touch",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::backend::ShaderCache*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Touch*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_ShaderCache_newFragmentShaderModule'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Touch_getID'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - std::string_view arg0; - - ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "axb.ShaderCache:newFragmentShaderModule"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_ShaderCache_newFragmentShaderModule'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Touch_getID'", nullptr); return 0; } - auto&& ret = cobj->newFragmentShaderModule(arg0); - object_to_luaval(tolua_S, "axb.ShaderModule",(ax::backend::ShaderModule*)ret); + auto&& ret = cobj->getID(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "axb.ShaderCache:newFragmentShaderModule",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Touch:getID",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_ShaderCache_newFragmentShaderModule'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Touch_getID'.",&tolua_err); #endif return 0; } -int lua_ax_base_ShaderCache_removeUnusedShader(lua_State* tolua_S) +int lua_ax_base_Touch_getCurrentForce(lua_State* tolua_S) { int argc = 0; - ax::backend::ShaderCache* cobj = nullptr; + ax::Touch* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -601,15 +638,15 @@ int lua_ax_base_ShaderCache_removeUnusedShader(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"axb.ShaderCache",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Touch",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::backend::ShaderCache*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Touch*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_ShaderCache_removeUnusedShader'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Touch_getCurrentForce'", nullptr); return 0; } #endif @@ -619,120 +656,142 @@ int lua_ax_base_ShaderCache_removeUnusedShader(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_ShaderCache_removeUnusedShader'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Touch_getCurrentForce'", nullptr); return 0; } - cobj->removeUnusedShader(); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getCurrentForce(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "axb.ShaderCache:removeUnusedShader",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Touch:getCurrentForce",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_ShaderCache_removeUnusedShader'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Touch_getCurrentForce'.",&tolua_err); #endif return 0; } -int lua_ax_base_ShaderCache_getInstance(lua_State* tolua_S) +int lua_ax_base_Touch_getMaxForce(lua_State* tolua_S) { int argc = 0; + ax::Touch* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"axb.ShaderCache",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Touch",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S) - 1; + cobj = (ax::Touch*)tolua_tousertype(tolua_S,1,0); - if (argc == 0) +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Touch_getMaxForce'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_ShaderCache_getInstance'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Touch_getMaxForce'", nullptr); return 0; } - auto&& ret = ax::backend::ShaderCache::getInstance(); - object_to_luaval(tolua_S, "axb.ShaderCache",(ax::backend::ShaderCache*)ret); + auto&& ret = cobj->getMaxForce(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "axb.ShaderCache:getInstance",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Touch:getMaxForce",argc, 0); return 0; + #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_ShaderCache_getInstance'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Touch_getMaxForce'.",&tolua_err); #endif + return 0; } -int lua_ax_base_ShaderCache_destroyInstance(lua_State* tolua_S) +int lua_ax_base_Touch_constructor(lua_State* tolua_S) { int argc = 0; + ax::Touch* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif -#if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"axb.ShaderCache",0,&tolua_err)) goto tolua_lerror; -#endif - argc = lua_gettop(tolua_S) - 1; - if (argc == 0) + argc = lua_gettop(tolua_S)-1; + if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_ShaderCache_destroyInstance'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Touch_constructor'", nullptr); return 0; } - ax::backend::ShaderCache::destroyInstance(); - lua_settop(tolua_S, 1); + cobj = new ax::Touch(); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.Touch"); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "axb.ShaderCache:destroyInstance",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Touch:Touch",argc, 0); return 0; + #if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_ShaderCache_destroyInstance'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Touch_constructor'.",&tolua_err); #endif + return 0; } -static int lua_ax_base_ShaderCache_finalize(lua_State* tolua_S) + +static int lua_ax_base_Touch_finalize(lua_State* tolua_S) { - AXLOGV("luabindings: finalizing LUA object (ShaderCache)"); + AXLOGV("luabindings: finalizing LUA object (Touch)"); return 0; } -int lua_register_ax_base_ShaderCache(lua_State* tolua_S) +int lua_register_ax_base_Touch(lua_State* tolua_S) { - tolua_usertype(tolua_S,"axb.ShaderCache"); - tolua_cclass(tolua_S,"ShaderCache","axb.ShaderCache","",nullptr); + tolua_usertype(tolua_S,"ax.Touch"); + tolua_cclass(tolua_S,"Touch","ax.Touch","ax.Object",nullptr); - tolua_beginmodule(tolua_S,"ShaderCache"); - tolua_function(tolua_S,"purge",lua_ax_base_ShaderCache_purge); - tolua_function(tolua_S,"newVertexShaderModule",lua_ax_base_ShaderCache_newVertexShaderModule); - tolua_function(tolua_S,"newFragmentShaderModule",lua_ax_base_ShaderCache_newFragmentShaderModule); - tolua_function(tolua_S,"removeUnusedShader",lua_ax_base_ShaderCache_removeUnusedShader); - tolua_function(tolua_S,"getInstance", lua_ax_base_ShaderCache_getInstance); - tolua_function(tolua_S,"destroyInstance", lua_ax_base_ShaderCache_destroyInstance); + tolua_beginmodule(tolua_S,"Touch"); + tolua_function(tolua_S,"new",lua_ax_base_Touch_constructor); + tolua_function(tolua_S,"getLocation",lua_ax_base_Touch_getLocation); + tolua_function(tolua_S,"getPreviousLocation",lua_ax_base_Touch_getPreviousLocation); + tolua_function(tolua_S,"getStartLocation",lua_ax_base_Touch_getStartLocation); + tolua_function(tolua_S,"getDelta",lua_ax_base_Touch_getDelta); + tolua_function(tolua_S,"getLocationInView",lua_ax_base_Touch_getLocationInView); + tolua_function(tolua_S,"getPreviousLocationInView",lua_ax_base_Touch_getPreviousLocationInView); + tolua_function(tolua_S,"getStartLocationInView",lua_ax_base_Touch_getStartLocationInView); + tolua_function(tolua_S,"setTouchInfo",lua_ax_base_Touch_setTouchInfo); + tolua_function(tolua_S,"getId",lua_ax_base_Touch_getID); + tolua_function(tolua_S,"getCurrentForce",lua_ax_base_Touch_getCurrentForce); + tolua_function(tolua_S,"getMaxForce",lua_ax_base_Touch_getMaxForce); tolua_endmodule(tolua_S); - auto typeName = typeid(ax::backend::ShaderCache).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "axb.ShaderCache"; - g_typeCast[typeName] = "axb.ShaderCache"; + auto typeName = typeid(ax::Touch).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.Touch"; + g_typeCast[typeName] = "ax.Touch"; return 1; } -int lua_ax_base_Texture2D_updateWithImage(lua_State* tolua_S) +int lua_ax_base_Event_getType(lua_State* tolua_S) { int argc = 0; - ax::Texture2D* cobj = nullptr; + ax::Event* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -741,71 +800,92 @@ int lua_ax_base_Texture2D_updateWithImage(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Event",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Event*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_updateWithImage'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Event_getType'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 2) + if (argc == 0) { - ax::Image* arg0; - ax::backend::PixelFormat arg1; - - ok &= luaval_to_object(tolua_S, 2, "ax.Image",&arg0, "ax.Texture2D:updateWithImage"); - - ok &= luaval_to_int32(tolua_S, 3,(int *)&arg1, "ax.Texture2D:updateWithImage"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_updateWithImage'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Event_getType'", nullptr); return 0; } - auto&& ret = cobj->updateWithImage(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); + int ret = (int)cobj->getType(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - if (argc == 3) - { - ax::Image* arg0; - ax::backend::PixelFormat arg1; - int arg2; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Event:getType",argc, 0); + return 0; - ok &= luaval_to_object(tolua_S, 2, "ax.Image",&arg0, "ax.Texture2D:updateWithImage"); +#if _AX_DEBUG >= 1 + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Event_getType'.",&tolua_err); +#endif - ok &= luaval_to_int32(tolua_S, 3,(int *)&arg1, "ax.Texture2D:updateWithImage"); + return 0; +} +int lua_ax_base_Event_stopPropagation(lua_State* tolua_S) +{ + int argc = 0; + ax::Event* cobj = nullptr; + bool ok = true; - ok &= luaval_to_int32(tolua_S, 4,(int *)&arg2, "ax.Texture2D:updateWithImage"); +#if _AX_DEBUG >= 1 + tolua_Error tolua_err; +#endif + + +#if _AX_DEBUG >= 1 + if (!tolua_isusertype(tolua_S,1,"ax.Event",0,&tolua_err)) goto tolua_lerror; +#endif + + cobj = (ax::Event*)tolua_tousertype(tolua_S,1,0); + +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Event_stopPropagation'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 0) + { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_updateWithImage'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Event_stopPropagation'", nullptr); return 0; } - auto&& ret = cobj->updateWithImage(arg0, arg1, arg2); - tolua_pushboolean(tolua_S,(bool)ret); + cobj->stopPropagation(); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:updateWithImage",argc, 2); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Event:stopPropagation",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_updateWithImage'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Event_stopPropagation'.",&tolua_err); #endif return 0; } -int lua_ax_base_Texture2D_updateWithMipmaps(lua_State* tolua_S) +int lua_ax_base_Event_isStopped(lua_State* tolua_S) { int argc = 0; - ax::Texture2D* cobj = nullptr; + ax::Event* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -814,133 +894,203 @@ int lua_ax_base_Texture2D_updateWithMipmaps(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Event",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Event*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_updateWithMipmaps'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Event_isStopped'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 6) + if (argc == 0) { - ax::_MipmapInfo* arg0; - int arg1; - ax::backend::PixelFormat arg2; - ax::backend::PixelFormat arg3; - int arg4; - int arg5; + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Event_isStopped'", nullptr); + return 0; + } + auto&& ret = cobj->isStopped(); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Event:isStopped",argc, 0); + return 0; - #pragma warning NO CONVERSION TO NATIVE FOR _MipmapInfo* - ok = false; +#if _AX_DEBUG >= 1 + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Event_isStopped'.",&tolua_err); +#endif - ok &= luaval_to_int32(tolua_S, 3,(int *)&arg1, "ax.Texture2D:updateWithMipmaps"); + return 0; +} +int lua_ax_base_Event_getCurrentTarget(lua_State* tolua_S) +{ + int argc = 0; + ax::Event* cobj = nullptr; + bool ok = true; - ok &= luaval_to_int32(tolua_S, 4,(int *)&arg2, "ax.Texture2D:updateWithMipmaps"); +#if _AX_DEBUG >= 1 + tolua_Error tolua_err; +#endif - ok &= luaval_to_int32(tolua_S, 5,(int *)&arg3, "ax.Texture2D:updateWithMipmaps"); - ok &= luaval_to_int32(tolua_S, 6,(int *)&arg4, "ax.Texture2D:updateWithMipmaps"); +#if _AX_DEBUG >= 1 + if (!tolua_isusertype(tolua_S,1,"ax.Event",0,&tolua_err)) goto tolua_lerror; +#endif - ok &= luaval_to_int32(tolua_S, 7,(int *)&arg5, "ax.Texture2D:updateWithMipmaps"); + cobj = (ax::Event*)tolua_tousertype(tolua_S,1,0); + +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Event_getCurrentTarget'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 0) + { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_updateWithMipmaps'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Event_getCurrentTarget'", nullptr); return 0; } - auto&& ret = cobj->updateWithMipmaps(arg0, arg1, arg2, arg3, arg4, arg5); - tolua_pushboolean(tolua_S,(bool)ret); + auto&& ret = cobj->getCurrentTarget(); + object_to_luaval(tolua_S, "ax.Node",(ax::Node*)ret); return 1; } - if (argc == 7) - { - ax::_MipmapInfo* arg0; - int arg1; - ax::backend::PixelFormat arg2; - ax::backend::PixelFormat arg3; - int arg4; - int arg5; - bool arg6; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Event:getCurrentTarget",argc, 0); + return 0; - #pragma warning NO CONVERSION TO NATIVE FOR _MipmapInfo* - ok = false; +#if _AX_DEBUG >= 1 + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Event_getCurrentTarget'.",&tolua_err); +#endif - ok &= luaval_to_int32(tolua_S, 3,(int *)&arg1, "ax.Texture2D:updateWithMipmaps"); + return 0; +} +int lua_ax_base_Event_constructor(lua_State* tolua_S) +{ + int argc = 0; + ax::Event* cobj = nullptr; + bool ok = true; - ok &= luaval_to_int32(tolua_S, 4,(int *)&arg2, "ax.Texture2D:updateWithMipmaps"); +#if _AX_DEBUG >= 1 + tolua_Error tolua_err; +#endif - ok &= luaval_to_int32(tolua_S, 5,(int *)&arg3, "ax.Texture2D:updateWithMipmaps"); - ok &= luaval_to_int32(tolua_S, 6,(int *)&arg4, "ax.Texture2D:updateWithMipmaps"); - ok &= luaval_to_int32(tolua_S, 7,(int *)&arg5, "ax.Texture2D:updateWithMipmaps"); + argc = lua_gettop(tolua_S)-1; + if (argc == 1) + { + ax::Event::Type arg0; - ok &= luaval_to_boolean(tolua_S, 8,&arg6, "ax.Texture2D:updateWithMipmaps"); + ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Event:Event"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_updateWithMipmaps'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Event_constructor'", nullptr); return 0; } - auto&& ret = cobj->updateWithMipmaps(arg0, arg1, arg2, arg3, arg4, arg5, arg6); - tolua_pushboolean(tolua_S,(bool)ret); + cobj = new ax::Event(arg0); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.Event"); return 1; } - if (argc == 8) - { - ax::_MipmapInfo* arg0; - int arg1; - ax::backend::PixelFormat arg2; - ax::backend::PixelFormat arg3; - int arg4; - int arg5; - bool arg6; - int arg7; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Event:Event",argc, 1); + return 0; - #pragma warning NO CONVERSION TO NATIVE FOR _MipmapInfo* - ok = false; +#if _AX_DEBUG >= 1 + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Event_constructor'.",&tolua_err); +#endif - ok &= luaval_to_int32(tolua_S, 3,(int *)&arg1, "ax.Texture2D:updateWithMipmaps"); + return 0; +} - ok &= luaval_to_int32(tolua_S, 4,(int *)&arg2, "ax.Texture2D:updateWithMipmaps"); +static int lua_ax_base_Event_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (Event)"); + return 0; +} - ok &= luaval_to_int32(tolua_S, 5,(int *)&arg3, "ax.Texture2D:updateWithMipmaps"); +int lua_register_ax_base_Event(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.Event"); + tolua_cclass(tolua_S,"Event","ax.Event","ax.Object",nullptr); - ok &= luaval_to_int32(tolua_S, 6,(int *)&arg4, "ax.Texture2D:updateWithMipmaps"); + tolua_beginmodule(tolua_S,"Event"); + tolua_function(tolua_S,"new",lua_ax_base_Event_constructor); + tolua_function(tolua_S,"getType",lua_ax_base_Event_getType); + tolua_function(tolua_S,"stopPropagation",lua_ax_base_Event_stopPropagation); + tolua_function(tolua_S,"isStopped",lua_ax_base_Event_isStopped); + tolua_function(tolua_S,"getCurrentTarget",lua_ax_base_Event_getCurrentTarget); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::Event).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.Event"; + g_typeCast[typeName] = "ax.Event"; + return 1; +} - ok &= luaval_to_int32(tolua_S, 7,(int *)&arg5, "ax.Texture2D:updateWithMipmaps"); +int lua_ax_base_EventTouch_getEventCode(lua_State* tolua_S) +{ + int argc = 0; + ax::EventTouch* cobj = nullptr; + bool ok = true; - ok &= luaval_to_boolean(tolua_S, 8,&arg6, "ax.Texture2D:updateWithMipmaps"); +#if _AX_DEBUG >= 1 + tolua_Error tolua_err; +#endif - ok &= luaval_to_int32(tolua_S, 9,(int *)&arg7, "ax.Texture2D:updateWithMipmaps"); + +#if _AX_DEBUG >= 1 + if (!tolua_isusertype(tolua_S,1,"ax.EventTouch",0,&tolua_err)) goto tolua_lerror; +#endif + + cobj = (ax::EventTouch*)tolua_tousertype(tolua_S,1,0); + +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_EventTouch_getEventCode'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 0) + { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_updateWithMipmaps'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_EventTouch_getEventCode'", nullptr); return 0; } - auto&& ret = cobj->updateWithMipmaps(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7); - tolua_pushboolean(tolua_S,(bool)ret); + int ret = (int)cobj->getEventCode(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:updateWithMipmaps",argc, 6); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.EventTouch:getEventCode",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_updateWithMipmaps'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_EventTouch_getEventCode'.",&tolua_err); #endif return 0; } -int lua_ax_base_Texture2D_updateWithSubData(lua_State* tolua_S) +int lua_ax_base_EventTouch_setEventCode(lua_State* tolua_S) { int argc = 0; - ax::Texture2D* cobj = nullptr; + ax::EventTouch* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -949,91 +1099,170 @@ int lua_ax_base_Texture2D_updateWithSubData(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.EventTouch",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::EventTouch*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_updateWithSubData'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_EventTouch_setEventCode'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 5) + if (argc == 1) { - void* arg0; - int arg1; - int arg2; - int arg3; - int arg4; + ax::EventTouch::EventCode arg0; - #pragma warning NO CONVERSION TO NATIVE FOR void* - ok = false; + ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.EventTouch:setEventCode"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_EventTouch_setEventCode'", nullptr); + return 0; + } + cobj->setEventCode(arg0); + lua_settop(tolua_S, 1); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.EventTouch:setEventCode",argc, 1); + return 0; - ok &= luaval_to_int32(tolua_S, 3,(int *)&arg1, "ax.Texture2D:updateWithSubData"); +#if _AX_DEBUG >= 1 + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_EventTouch_setEventCode'.",&tolua_err); +#endif - ok &= luaval_to_int32(tolua_S, 4,(int *)&arg2, "ax.Texture2D:updateWithSubData"); + return 0; +} +int lua_ax_base_EventTouch_constructor(lua_State* tolua_S) +{ + int argc = 0; + ax::EventTouch* cobj = nullptr; + bool ok = true; - ok &= luaval_to_int32(tolua_S, 5,(int *)&arg3, "ax.Texture2D:updateWithSubData"); +#if _AX_DEBUG >= 1 + tolua_Error tolua_err; +#endif - ok &= luaval_to_int32(tolua_S, 6,(int *)&arg4, "ax.Texture2D:updateWithSubData"); + + + argc = lua_gettop(tolua_S)-1; + if (argc == 0) + { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_updateWithSubData'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_EventTouch_constructor'", nullptr); return 0; } - auto&& ret = cobj->updateWithSubData(arg0, arg1, arg2, arg3, arg4); - tolua_pushboolean(tolua_S,(bool)ret); + cobj = new ax::EventTouch(); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.EventTouch"); return 1; } - if (argc == 6) - { - void* arg0; - int arg1; - int arg2; - int arg3; - int arg4; - int arg5; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.EventTouch:EventTouch",argc, 0); + return 0; - #pragma warning NO CONVERSION TO NATIVE FOR void* - ok = false; +#if _AX_DEBUG >= 1 + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_EventTouch_constructor'.",&tolua_err); +#endif - ok &= luaval_to_int32(tolua_S, 3,(int *)&arg1, "ax.Texture2D:updateWithSubData"); + return 0; +} - ok &= luaval_to_int32(tolua_S, 4,(int *)&arg2, "ax.Texture2D:updateWithSubData"); +static int lua_ax_base_EventTouch_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (EventTouch)"); + return 0; +} - ok &= luaval_to_int32(tolua_S, 5,(int *)&arg3, "ax.Texture2D:updateWithSubData"); +int lua_register_ax_base_EventTouch(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.EventTouch"); + tolua_cclass(tolua_S,"EventTouch","ax.EventTouch","ax.Event",nullptr); - ok &= luaval_to_int32(tolua_S, 6,(int *)&arg4, "ax.Texture2D:updateWithSubData"); + tolua_beginmodule(tolua_S,"EventTouch"); + tolua_function(tolua_S,"new",lua_ax_base_EventTouch_constructor); + tolua_function(tolua_S,"getEventCode",lua_ax_base_EventTouch_getEventCode); + tolua_function(tolua_S,"setEventCode",lua_ax_base_EventTouch_setEventCode); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::EventTouch).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.EventTouch"; + g_typeCast[typeName] = "ax.EventTouch"; + return 1; +} - ok &= luaval_to_int32(tolua_S, 7,(int *)&arg5, "ax.Texture2D:updateWithSubData"); +int lua_ax_base_EventKeyboard_constructor(lua_State* tolua_S) +{ + int argc = 0; + ax::EventKeyboard* cobj = nullptr; + bool ok = true; + +#if _AX_DEBUG >= 1 + tolua_Error tolua_err; +#endif + + + + argc = lua_gettop(tolua_S)-1; + if (argc == 2) + { + ax::EventKeyboard::KeyCode arg0; + bool arg1; + + ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.EventKeyboard:EventKeyboard"); + + ok &= luaval_to_boolean(tolua_S, 3,&arg1, "ax.EventKeyboard:EventKeyboard"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_updateWithSubData'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_EventKeyboard_constructor'", nullptr); return 0; } - auto&& ret = cobj->updateWithSubData(arg0, arg1, arg2, arg3, arg4, arg5); - tolua_pushboolean(tolua_S,(bool)ret); + cobj = new ax::EventKeyboard(arg0, arg1); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.EventKeyboard"); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:updateWithSubData",argc, 5); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.EventKeyboard:EventKeyboard",argc, 2); return 0; #if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_updateWithSubData'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_EventKeyboard_constructor'.",&tolua_err); #endif return 0; } -int lua_ax_base_Texture2D_drawAtPoint(lua_State* tolua_S) + +static int lua_ax_base_EventKeyboard_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (EventKeyboard)"); + return 0; +} + +int lua_register_ax_base_EventKeyboard(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.EventKeyboard"); + tolua_cclass(tolua_S,"EventKeyboard","ax.EventKeyboard","ax.Event",nullptr); + + tolua_beginmodule(tolua_S,"EventKeyboard"); + tolua_function(tolua_S,"new",lua_ax_base_EventKeyboard_constructor); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::EventKeyboard).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.EventKeyboard"; + g_typeCast[typeName] = "ax.EventKeyboard"; + return 1; +} + +int lua_ax_base_Action_clone(lua_State* tolua_S) { int argc = 0; - ax::Texture2D* cobj = nullptr; + ax::Action* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -1042,51 +1271,45 @@ int lua_ax_base_Texture2D_drawAtPoint(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Action",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Action*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_drawAtPoint'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Action_clone'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 2) + if (argc == 0) { - ax::Vec2 arg0; - double arg1; - - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.Texture2D:drawAtPoint"); - - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Texture2D:drawAtPoint"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_drawAtPoint'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Action_clone'", nullptr); return 0; } - cobj->drawAtPoint(arg0, arg1); - lua_settop(tolua_S, 1); + auto&& ret = cobj->clone(); + object_to_luaval(tolua_S, "ax.Action",(ax::Action*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:drawAtPoint",argc, 2); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Action:clone",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_drawAtPoint'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Action_clone'.",&tolua_err); #endif return 0; } -int lua_ax_base_Texture2D_drawInRect(lua_State* tolua_S) +int lua_ax_base_Action_reverse(lua_State* tolua_S) { int argc = 0; - ax::Texture2D* cobj = nullptr; + ax::Action* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -1095,337 +1318,142 @@ int lua_ax_base_Texture2D_drawInRect(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Action",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Action*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_drawInRect'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Action_reverse'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 2) + if (argc == 0) { - ax::Rect arg0; - double arg1; - - ok &= luaval_to_rect(tolua_S, 2, &arg0, "ax.Texture2D:drawInRect"); - - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Texture2D:drawInRect"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_drawInRect'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Action_reverse'", nullptr); return 0; } - cobj->drawInRect(arg0, arg1); - lua_settop(tolua_S, 1); + auto&& ret = cobj->reverse(); + object_to_luaval(tolua_S, "ax.Action",(ax::Action*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:drawInRect",argc, 2); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Action:reverse",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_drawInRect'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Action_reverse'.",&tolua_err); #endif return 0; } -int lua_ax_base_Texture2D_initWithImage(lua_State* tolua_S) +int lua_ax_base_Action_isDone(lua_State* tolua_S) { int argc = 0; - ax::Texture2D* cobj = nullptr; + ax::Action* cobj = nullptr; bool ok = true; + #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Action",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); + + cobj = (ax::Action*)tolua_tousertype(tolua_S,1,0); + #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_initWithImage'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Action_isDone'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - do{ - if (argc == 2) { - ax::Image* arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.Image",&arg0, "ax.Texture2D:initWithImage"); - - if (!ok) { break; } - ax::backend::PixelFormat arg1; - ok &= luaval_to_int32(tolua_S, 3,(int *)&arg1, "ax.Texture2D:initWithImage"); - if (!ok) { break; } - bool ret = cobj->initWithImage(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 1) { - ax::Image* arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.Image",&arg0, "ax.Texture2D:initWithImage"); - - if (!ok) { break; } - bool ret = cobj->initWithImage(arg0); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; + argc = lua_gettop(tolua_S)-1; + if (argc == 0) + { + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Action_isDone'", nullptr); + return 0; } - }while(0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:initWithImage",argc, 1); + auto&& ret = cobj->isDone(); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Action:isDone",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_initWithImage'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Action_isDone'.",&tolua_err); #endif return 0; } -int lua_ax_base_Texture2D_initWithString(lua_State* tolua_S) +int lua_ax_base_Action_startWithTarget(lua_State* tolua_S) { int argc = 0; - ax::Texture2D* cobj = nullptr; + ax::Action* cobj = nullptr; bool ok = true; + #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Action",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); + + cobj = (ax::Action*)tolua_tousertype(tolua_S,1,0); + #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_initWithString'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Action_startWithTarget'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - do{ - if (argc == 2) { - std::string_view arg0; - ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Texture2D:initWithString"); - if (!ok) { break; } - ax::FontDefinition arg1; - ok &= luaval_to_fontdefinition(tolua_S, 3, &arg1, "ax.Texture2D:initWithString"); + argc = lua_gettop(tolua_S)-1; + if (argc == 1) + { + ax::Node* arg0; - if (!ok) { break; } - bool ret = cobj->initWithString(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; + ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Action:startWithTarget"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Action_startWithTarget'", nullptr); + return 0; } - }while(0); - ok = true; - do{ - if (argc == 3) { - std::string_view arg0; - ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Texture2D:initWithString"); - - if (!ok) { break; } - std::string_view arg1; - ok &= luaval_to_std_string_view(tolua_S, 3,&arg1, "ax.Texture2D:initWithString"); - - if (!ok) { break; } - double arg2; - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.Texture2D:initWithString"); - - if (!ok) { break; } - bool ret = cobj->initWithString(arg0, arg1, arg2); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 4) { - std::string_view arg0; - ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Texture2D:initWithString"); - - if (!ok) { break; } - std::string_view arg1; - ok &= luaval_to_std_string_view(tolua_S, 3,&arg1, "ax.Texture2D:initWithString"); - - if (!ok) { break; } - double arg2; - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.Texture2D:initWithString"); - - if (!ok) { break; } - ax::Vec2 arg3; - ok &= luaval_to_vec2(tolua_S, 5, &arg3, "ax.Texture2D:initWithString"); - - if (!ok) { break; } - bool ret = cobj->initWithString(arg0, arg1, arg2, arg3); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 5) { - std::string_view arg0; - ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Texture2D:initWithString"); - - if (!ok) { break; } - std::string_view arg1; - ok &= luaval_to_std_string_view(tolua_S, 3,&arg1, "ax.Texture2D:initWithString"); - - if (!ok) { break; } - double arg2; - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.Texture2D:initWithString"); - - if (!ok) { break; } - ax::Vec2 arg3; - ok &= luaval_to_vec2(tolua_S, 5, &arg3, "ax.Texture2D:initWithString"); - - if (!ok) { break; } - ax::TextHAlignment arg4; - ok &= luaval_to_int32(tolua_S, 6,(int *)&arg4, "ax.Texture2D:initWithString"); - - if (!ok) { break; } - bool ret = cobj->initWithString(arg0, arg1, arg2, arg3, arg4); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 6) { - std::string_view arg0; - ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Texture2D:initWithString"); - - if (!ok) { break; } - std::string_view arg1; - ok &= luaval_to_std_string_view(tolua_S, 3,&arg1, "ax.Texture2D:initWithString"); - - if (!ok) { break; } - double arg2; - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.Texture2D:initWithString"); - - if (!ok) { break; } - ax::Vec2 arg3; - ok &= luaval_to_vec2(tolua_S, 5, &arg3, "ax.Texture2D:initWithString"); - - if (!ok) { break; } - ax::TextHAlignment arg4; - ok &= luaval_to_int32(tolua_S, 6,(int *)&arg4, "ax.Texture2D:initWithString"); - - if (!ok) { break; } - ax::TextVAlignment arg5; - ok &= luaval_to_int32(tolua_S, 7,(int *)&arg5, "ax.Texture2D:initWithString"); - - if (!ok) { break; } - bool ret = cobj->initWithString(arg0, arg1, arg2, arg3, arg4, arg5); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 7) { - std::string_view arg0; - ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Texture2D:initWithString"); - - if (!ok) { break; } - std::string_view arg1; - ok &= luaval_to_std_string_view(tolua_S, 3,&arg1, "ax.Texture2D:initWithString"); - - if (!ok) { break; } - double arg2; - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.Texture2D:initWithString"); - - if (!ok) { break; } - ax::Vec2 arg3; - ok &= luaval_to_vec2(tolua_S, 5, &arg3, "ax.Texture2D:initWithString"); - - if (!ok) { break; } - ax::TextHAlignment arg4; - ok &= luaval_to_int32(tolua_S, 6,(int *)&arg4, "ax.Texture2D:initWithString"); - - if (!ok) { break; } - ax::TextVAlignment arg5; - ok &= luaval_to_int32(tolua_S, 7,(int *)&arg5, "ax.Texture2D:initWithString"); - - if (!ok) { break; } - bool arg6; - ok &= luaval_to_boolean(tolua_S, 8,&arg6, "ax.Texture2D:initWithString"); - - if (!ok) { break; } - bool ret = cobj->initWithString(arg0, arg1, arg2, arg3, arg4, arg5, arg6); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 8) { - std::string_view arg0; - ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Texture2D:initWithString"); - - if (!ok) { break; } - std::string_view arg1; - ok &= luaval_to_std_string_view(tolua_S, 3,&arg1, "ax.Texture2D:initWithString"); - - if (!ok) { break; } - double arg2; - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.Texture2D:initWithString"); - - if (!ok) { break; } - ax::Vec2 arg3; - ok &= luaval_to_vec2(tolua_S, 5, &arg3, "ax.Texture2D:initWithString"); - - if (!ok) { break; } - ax::TextHAlignment arg4; - ok &= luaval_to_int32(tolua_S, 6,(int *)&arg4, "ax.Texture2D:initWithString"); - - if (!ok) { break; } - ax::TextVAlignment arg5; - ok &= luaval_to_int32(tolua_S, 7,(int *)&arg5, "ax.Texture2D:initWithString"); - - if (!ok) { break; } - bool arg6; - ok &= luaval_to_boolean(tolua_S, 8,&arg6, "ax.Texture2D:initWithString"); - - if (!ok) { break; } - int arg7; - ok &= luaval_to_int32(tolua_S, 9,(int *)&arg7, "ax.Texture2D:initWithString"); - - if (!ok) { break; } - bool ret = cobj->initWithString(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - }while(0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:initWithString",argc, 3); + cobj->startWithTarget(arg0); + lua_settop(tolua_S, 1); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Action:startWithTarget",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_initWithString'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Action_startWithTarget'.",&tolua_err); #endif return 0; } -int lua_ax_base_Texture2D_updateTextureDescriptor(lua_State* tolua_S) +int lua_ax_base_Action_stop(lua_State* tolua_S) { int argc = 0; - ax::Texture2D* cobj = nullptr; + ax::Action* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -1434,67 +1462,45 @@ int lua_ax_base_Texture2D_updateTextureDescriptor(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Action",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Action*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_updateTextureDescriptor'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Action_stop'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - ax::backend::TextureDescriptor arg0; - - #pragma warning NO CONVERSION TO NATIVE FOR TextureDescriptor - ok = false; - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_updateTextureDescriptor'", nullptr); - return 0; - } - auto&& ret = cobj->updateTextureDescriptor(arg0); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - if (argc == 2) + if (argc == 0) { - ax::backend::TextureDescriptor arg0; - bool arg1; - - #pragma warning NO CONVERSION TO NATIVE FOR TextureDescriptor - ok = false; - - ok &= luaval_to_boolean(tolua_S, 3,&arg1, "ax.Texture2D:updateTextureDescriptor"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_updateTextureDescriptor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Action_stop'", nullptr); return 0; } - auto&& ret = cobj->updateTextureDescriptor(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); + cobj->stop(); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:updateTextureDescriptor",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Action:stop",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_updateTextureDescriptor'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Action_stop'.",&tolua_err); #endif return 0; } -int lua_ax_base_Texture2D_setRenderTarget(lua_State* tolua_S) +int lua_ax_base_Action_step(lua_State* tolua_S) { int argc = 0; - ax::Texture2D* cobj = nullptr; + ax::Action* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -1503,15 +1509,15 @@ int lua_ax_base_Texture2D_setRenderTarget(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Action",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Action*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_setRenderTarget'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Action_step'", nullptr); return 0; } #endif @@ -1519,32 +1525,32 @@ int lua_ax_base_Texture2D_setRenderTarget(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - bool arg0; + double arg0; - ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.Texture2D:setRenderTarget"); + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Action:step"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_setRenderTarget'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Action_step'", nullptr); return 0; } - cobj->setRenderTarget(arg0); + cobj->step(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:setRenderTarget",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Action:step",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_setRenderTarget'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Action_step'.",&tolua_err); #endif return 0; } -int lua_ax_base_Texture2D_isRenderTarget(lua_State* tolua_S) +int lua_ax_base_Action_update(lua_State* tolua_S) { int argc = 0; - ax::Texture2D* cobj = nullptr; + ax::Action* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -1553,45 +1559,48 @@ int lua_ax_base_Texture2D_isRenderTarget(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Action",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Action*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_isRenderTarget'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Action_update'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + double arg0; + + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Action:update"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_isRenderTarget'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Action_update'", nullptr); return 0; } - auto&& ret = cobj->isRenderTarget(); - tolua_pushboolean(tolua_S,(bool)ret); + cobj->update(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:isRenderTarget",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Action:update",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_isRenderTarget'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Action_update'.",&tolua_err); #endif return 0; } -int lua_ax_base_Texture2D_generateMipmap(lua_State* tolua_S) +int lua_ax_base_Action_getTarget(lua_State* tolua_S) { int argc = 0; - ax::Texture2D* cobj = nullptr; + ax::Action* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -1600,15 +1609,15 @@ int lua_ax_base_Texture2D_generateMipmap(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Action",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Action*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_generateMipmap'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Action_getTarget'", nullptr); return 0; } #endif @@ -1618,27 +1627,27 @@ int lua_ax_base_Texture2D_generateMipmap(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_generateMipmap'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Action_getTarget'", nullptr); return 0; } - cobj->generateMipmap(); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getTarget(); + object_to_luaval(tolua_S, "ax.Node",(ax::Node*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:generateMipmap",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Action:getTarget",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_generateMipmap'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Action_getTarget'.",&tolua_err); #endif return 0; } -int lua_ax_base_Texture2D_setAntiAliasTexParameters(lua_State* tolua_S) +int lua_ax_base_Action_setTarget(lua_State* tolua_S) { int argc = 0; - ax::Texture2D* cobj = nullptr; + ax::Action* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -1647,45 +1656,48 @@ int lua_ax_base_Texture2D_setAntiAliasTexParameters(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Action",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Action*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_setAntiAliasTexParameters'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Action_setTarget'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + ax::Node* arg0; + + ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Action:setTarget"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_setAntiAliasTexParameters'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Action_setTarget'", nullptr); return 0; } - cobj->setAntiAliasTexParameters(); + cobj->setTarget(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:setAntiAliasTexParameters",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Action:setTarget",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_setAntiAliasTexParameters'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Action_setTarget'.",&tolua_err); #endif return 0; } -int lua_ax_base_Texture2D_setAliasTexParameters(lua_State* tolua_S) +int lua_ax_base_Action_getOriginalTarget(lua_State* tolua_S) { int argc = 0; - ax::Texture2D* cobj = nullptr; + ax::Action* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -1694,15 +1706,15 @@ int lua_ax_base_Texture2D_setAliasTexParameters(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Action",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Action*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_setAliasTexParameters'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Action_getOriginalTarget'", nullptr); return 0; } #endif @@ -1712,27 +1724,27 @@ int lua_ax_base_Texture2D_setAliasTexParameters(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_setAliasTexParameters'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Action_getOriginalTarget'", nullptr); return 0; } - cobj->setAliasTexParameters(); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getOriginalTarget(); + object_to_luaval(tolua_S, "ax.Node",(ax::Node*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:setAliasTexParameters",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Action:getOriginalTarget",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_setAliasTexParameters'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Action_getOriginalTarget'.",&tolua_err); #endif return 0; } -int lua_ax_base_Texture2D_getStringForFormat(lua_State* tolua_S) +int lua_ax_base_Action_setOriginalTarget(lua_State* tolua_S) { int argc = 0; - ax::Texture2D* cobj = nullptr; + ax::Action* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -1741,96 +1753,48 @@ int lua_ax_base_Texture2D_getStringForFormat(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Action",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Action*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_getStringForFormat'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Action_setOriginalTarget'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + ax::Node* arg0; + + ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Action:setOriginalTarget"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_getStringForFormat'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Action_setOriginalTarget'", nullptr); return 0; } - auto&& ret = cobj->getStringForFormat(); - tolua_pushstring(tolua_S,(const char*)ret); + cobj->setOriginalTarget(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:getStringForFormat",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_getStringForFormat'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_base_Texture2D_getBitsPerPixelForFormat(lua_State* tolua_S) -{ - int argc = 0; - ax::Texture2D* cobj = nullptr; - bool ok = true; -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; -#endif - cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_getBitsPerPixelForFormat'", nullptr); - return 0; - } -#endif - argc = lua_gettop(tolua_S)-1; - do{ - if (argc == 1) { - ax::backend::PixelFormat arg0; - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Texture2D:getBitsPerPixelForFormat"); - - if (!ok) { break; } - unsigned int ret = cobj->getBitsPerPixelForFormat(arg0); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 0) { - unsigned int ret = cobj->getBitsPerPixelForFormat(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - }while(0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:getBitsPerPixelForFormat",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Action:setOriginalTarget",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_getBitsPerPixelForFormat'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Action_setOriginalTarget'.",&tolua_err); #endif return 0; } -int lua_ax_base_Texture2D_getContentSizeInPixels(lua_State* tolua_S) +int lua_ax_base_Action_getTag(lua_State* tolua_S) { int argc = 0; - ax::Texture2D* cobj = nullptr; + ax::Action* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -1839,15 +1803,15 @@ int lua_ax_base_Texture2D_getContentSizeInPixels(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Action",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Action*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_getContentSizeInPixels'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Action_getTag'", nullptr); return 0; } #endif @@ -1857,27 +1821,27 @@ int lua_ax_base_Texture2D_getContentSizeInPixels(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_getContentSizeInPixels'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Action_getTag'", nullptr); return 0; } - auto&& ret = cobj->getContentSizeInPixels(); - vec2_to_luaval(tolua_S, ret); + auto&& ret = cobj->getTag(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:getContentSizeInPixels",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Action:getTag",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_getContentSizeInPixels'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Action_getTag'.",&tolua_err); #endif return 0; } -int lua_ax_base_Texture2D_hasPremultipliedAlpha(lua_State* tolua_S) +int lua_ax_base_Action_setTag(lua_State* tolua_S) { int argc = 0; - ax::Texture2D* cobj = nullptr; + ax::Action* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -1886,45 +1850,48 @@ int lua_ax_base_Texture2D_hasPremultipliedAlpha(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Action",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Action*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_hasPremultipliedAlpha'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Action_setTag'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + int arg0; + + ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Action:setTag"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_hasPremultipliedAlpha'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Action_setTag'", nullptr); return 0; } - auto&& ret = cobj->hasPremultipliedAlpha(); - tolua_pushboolean(tolua_S,(bool)ret); + cobj->setTag(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:hasPremultipliedAlpha",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Action:setTag",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_hasPremultipliedAlpha'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Action_setTag'.",&tolua_err); #endif return 0; } -int lua_ax_base_Texture2D_setPremultipliedAlpha(lua_State* tolua_S) +int lua_ax_base_Action_getFlags(lua_State* tolua_S) { int argc = 0; - ax::Texture2D* cobj = nullptr; + ax::Action* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -1933,48 +1900,45 @@ int lua_ax_base_Texture2D_setPremultipliedAlpha(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Action",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Action*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_setPremultipliedAlpha'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Action_getFlags'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - bool arg0; - - ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.Texture2D:setPremultipliedAlpha"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_setPremultipliedAlpha'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Action_getFlags'", nullptr); return 0; } - cobj->setPremultipliedAlpha(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getFlags(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:setPremultipliedAlpha",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Action:getFlags",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_setPremultipliedAlpha'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Action_getFlags'.",&tolua_err); #endif return 0; } -int lua_ax_base_Texture2D_hasMipmaps(lua_State* tolua_S) +int lua_ax_base_Action_setFlags(lua_State* tolua_S) { int argc = 0; - ax::Texture2D* cobj = nullptr; + ax::Action* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -1983,62 +1947,99 @@ int lua_ax_base_Texture2D_hasMipmaps(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Action",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Action*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_hasMipmaps'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Action_setFlags'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + unsigned int arg0; + + ok &= luaval_to_uint32(tolua_S, 2,&arg0, "ax.Action:setFlags"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_hasMipmaps'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Action_setFlags'", nullptr); return 0; } - auto&& ret = cobj->hasMipmaps(); - tolua_pushboolean(tolua_S,(bool)ret); + cobj->setFlags(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:hasMipmaps",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Action:setFlags",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_hasMipmaps'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Action_setFlags'.",&tolua_err); #endif return 0; } -int lua_ax_base_Texture2D_getPixelFormat(lua_State* tolua_S) +static int lua_ax_base_Action_finalize(lua_State* tolua_S) { - int argc = 0; - ax::Texture2D* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 + AXLOGV("luabindings: finalizing LUA object (Action)"); + return 0; +} + +int lua_register_ax_base_Action(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.Action"); + tolua_cclass(tolua_S,"Action","ax.Action","ax.Object",nullptr); + + tolua_beginmodule(tolua_S,"Action"); + tolua_function(tolua_S,"clone",lua_ax_base_Action_clone); + tolua_function(tolua_S,"reverse",lua_ax_base_Action_reverse); + tolua_function(tolua_S,"isDone",lua_ax_base_Action_isDone); + tolua_function(tolua_S,"startWithTarget",lua_ax_base_Action_startWithTarget); + tolua_function(tolua_S,"stop",lua_ax_base_Action_stop); + tolua_function(tolua_S,"step",lua_ax_base_Action_step); + tolua_function(tolua_S,"update",lua_ax_base_Action_update); + tolua_function(tolua_S,"getTarget",lua_ax_base_Action_getTarget); + tolua_function(tolua_S,"setTarget",lua_ax_base_Action_setTarget); + tolua_function(tolua_S,"getOriginalTarget",lua_ax_base_Action_getOriginalTarget); + tolua_function(tolua_S,"setOriginalTarget",lua_ax_base_Action_setOriginalTarget); + tolua_function(tolua_S,"getTag",lua_ax_base_Action_getTag); + tolua_function(tolua_S,"setTag",lua_ax_base_Action_setTag); + tolua_function(tolua_S,"getFlags",lua_ax_base_Action_getFlags); + tolua_function(tolua_S,"setFlags",lua_ax_base_Action_setFlags); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::Action).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.Action"; + g_typeCast[typeName] = "ax.Action"; + return 1; +} + +int lua_ax_base_FiniteTimeAction_getDuration(lua_State* tolua_S) +{ + int argc = 0; + ax::FiniteTimeAction* cobj = nullptr; + bool ok = true; + +#if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.FiniteTimeAction",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::FiniteTimeAction*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_getPixelFormat'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_FiniteTimeAction_getDuration'", nullptr); return 0; } #endif @@ -2048,27 +2049,27 @@ int lua_ax_base_Texture2D_getPixelFormat(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_getPixelFormat'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_FiniteTimeAction_getDuration'", nullptr); return 0; } - int ret = (int)cobj->getPixelFormat(); + auto&& ret = cobj->getDuration(); tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:getPixelFormat",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.FiniteTimeAction:getDuration",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_getPixelFormat'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_FiniteTimeAction_getDuration'.",&tolua_err); #endif return 0; } -int lua_ax_base_Texture2D_getSamplerFlags(lua_State* tolua_S) +int lua_ax_base_FiniteTimeAction_setDuration(lua_State* tolua_S) { int argc = 0; - ax::Texture2D* cobj = nullptr; + ax::FiniteTimeAction* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -2077,45 +2078,69 @@ int lua_ax_base_Texture2D_getSamplerFlags(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.FiniteTimeAction",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::FiniteTimeAction*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_getSamplerFlags'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_FiniteTimeAction_setDuration'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + double arg0; + + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.FiniteTimeAction:setDuration"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_getSamplerFlags'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_FiniteTimeAction_setDuration'", nullptr); return 0; } - auto&& ret = cobj->getSamplerFlags(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + cobj->setDuration(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:getSamplerFlags",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.FiniteTimeAction:setDuration",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_getSamplerFlags'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_FiniteTimeAction_setDuration'.",&tolua_err); #endif return 0; } -int lua_ax_base_Texture2D_getPixelsWide(lua_State* tolua_S) +static int lua_ax_base_FiniteTimeAction_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (FiniteTimeAction)"); + return 0; +} + +int lua_register_ax_base_FiniteTimeAction(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.FiniteTimeAction"); + tolua_cclass(tolua_S,"FiniteTimeAction","ax.FiniteTimeAction","ax.Action",nullptr); + + tolua_beginmodule(tolua_S,"FiniteTimeAction"); + tolua_function(tolua_S,"getDuration",lua_ax_base_FiniteTimeAction_getDuration); + tolua_function(tolua_S,"setDuration",lua_ax_base_FiniteTimeAction_setDuration); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::FiniteTimeAction).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.FiniteTimeAction"; + g_typeCast[typeName] = "ax.FiniteTimeAction"; + return 1; +} + +int lua_ax_base_Speed_getSpeed(lua_State* tolua_S) { int argc = 0; - ax::Texture2D* cobj = nullptr; + ax::Speed* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -2124,15 +2149,15 @@ int lua_ax_base_Texture2D_getPixelsWide(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Speed",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Speed*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_getPixelsWide'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Speed_getSpeed'", nullptr); return 0; } #endif @@ -2142,27 +2167,27 @@ int lua_ax_base_Texture2D_getPixelsWide(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_getPixelsWide'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Speed_getSpeed'", nullptr); return 0; } - auto&& ret = cobj->getPixelsWide(); + auto&& ret = cobj->getSpeed(); tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:getPixelsWide",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Speed:getSpeed",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_getPixelsWide'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Speed_getSpeed'.",&tolua_err); #endif return 0; } -int lua_ax_base_Texture2D_getPixelsHigh(lua_State* tolua_S) +int lua_ax_base_Speed_setSpeed(lua_State* tolua_S) { int argc = 0; - ax::Texture2D* cobj = nullptr; + ax::Speed* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -2171,45 +2196,48 @@ int lua_ax_base_Texture2D_getPixelsHigh(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Speed",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Speed*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_getPixelsHigh'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Speed_setSpeed'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + double arg0; + + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Speed:setSpeed"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_getPixelsHigh'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Speed_setSpeed'", nullptr); return 0; } - auto&& ret = cobj->getPixelsHigh(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + cobj->setSpeed(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:getPixelsHigh",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Speed:setSpeed",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_getPixelsHigh'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Speed_setSpeed'.",&tolua_err); #endif return 0; } -int lua_ax_base_Texture2D_getBackendTexture(lua_State* tolua_S) +int lua_ax_base_Speed_setInnerAction(lua_State* tolua_S) { int argc = 0; - ax::Texture2D* cobj = nullptr; + ax::Speed* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -2218,45 +2246,48 @@ int lua_ax_base_Texture2D_getBackendTexture(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Speed",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Speed*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_getBackendTexture'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Speed_setInnerAction'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + ax::ActionInterval* arg0; + + ok &= luaval_to_object(tolua_S, 2, "ax.ActionInterval",&arg0, "ax.Speed:setInnerAction"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_getBackendTexture'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Speed_setInnerAction'", nullptr); return 0; } - auto&& ret = cobj->getBackendTexture(); - object_to_luaval(tolua_S, "axb.TextureBackend",(ax::backend::TextureBackend*)ret); + cobj->setInnerAction(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:getBackendTexture",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Speed:setInnerAction",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_getBackendTexture'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Speed_setInnerAction'.",&tolua_err); #endif return 0; } -int lua_ax_base_Texture2D_getMaxS(lua_State* tolua_S) +int lua_ax_base_Speed_getInnerAction(lua_State* tolua_S) { int argc = 0; - ax::Texture2D* cobj = nullptr; + ax::Speed* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -2265,15 +2296,15 @@ int lua_ax_base_Texture2D_getMaxS(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Speed",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Speed*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_getMaxS'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Speed_getInnerAction'", nullptr); return 0; } #endif @@ -2283,27 +2314,27 @@ int lua_ax_base_Texture2D_getMaxS(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_getMaxS'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Speed_getInnerAction'", nullptr); return 0; } - auto&& ret = cobj->getMaxS(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + auto&& ret = cobj->getInnerAction(); + object_to_luaval(tolua_S, "ax.ActionInterval",(ax::ActionInterval*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:getMaxS",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Speed:getInnerAction",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_getMaxS'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Speed_getInnerAction'.",&tolua_err); #endif return 0; } -int lua_ax_base_Texture2D_setMaxS(lua_State* tolua_S) +int lua_ax_base_Speed_initWithAction(lua_State* tolua_S) { int argc = 0; - ax::Texture2D* cobj = nullptr; + ax::Speed* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -2312,95 +2343,152 @@ int lua_ax_base_Texture2D_setMaxS(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Speed",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Speed*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_setMaxS'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Speed_initWithAction'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 2) { - double arg0; + ax::ActionInterval* arg0; + double arg1; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Texture2D:setMaxS"); + ok &= luaval_to_object(tolua_S, 2, "ax.ActionInterval",&arg0, "ax.Speed:initWithAction"); + + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Speed:initWithAction"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_setMaxS'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Speed_initWithAction'", nullptr); return 0; } - cobj->setMaxS(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->initWithAction(arg0, arg1); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:setMaxS",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Speed:initWithAction",argc, 2); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_setMaxS'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Speed_initWithAction'.",&tolua_err); #endif return 0; } -int lua_ax_base_Texture2D_getMaxT(lua_State* tolua_S) +int lua_ax_base_Speed_create(lua_State* tolua_S) { int argc = 0; - ax::Texture2D* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.Speed",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); + argc = lua_gettop(tolua_S) - 1; -#if _AX_DEBUG >= 1 - if (!cobj) + if (argc == 2) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_getMaxT'", nullptr); - return 0; + ax::ActionInterval* arg0; + double arg1; + ok &= luaval_to_object(tolua_S, 2, "ax.ActionInterval",&arg0, "ax.Speed:create"); + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Speed:create"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Speed_create'", nullptr); + return 0; + } + auto&& ret = ax::Speed::create(arg0, arg1); + object_to_luaval(tolua_S, "ax.Speed",(ax::Speed*)ret); + return 1; } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Speed:create",argc, 2); + return 0; +#if _AX_DEBUG >= 1 + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Speed_create'.",&tolua_err); +#endif + return 0; +} +int lua_ax_base_Speed_constructor(lua_State* tolua_S) +{ + int argc = 0; + ax::Speed* cobj = nullptr; + bool ok = true; + +#if _AX_DEBUG >= 1 + tolua_Error tolua_err; #endif + + argc = lua_gettop(tolua_S)-1; if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_getMaxT'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Speed_constructor'", nullptr); return 0; } - auto&& ret = cobj->getMaxT(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + cobj = new ax::Speed(); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.Speed"); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:getMaxT",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Speed:Speed",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_getMaxT'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Speed_constructor'.",&tolua_err); #endif return 0; } -int lua_ax_base_Texture2D_setMaxT(lua_State* tolua_S) + +static int lua_ax_base_Speed_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (Speed)"); + return 0; +} + +int lua_register_ax_base_Speed(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.Speed"); + tolua_cclass(tolua_S,"Speed","ax.Speed","ax.Action",nullptr); + + tolua_beginmodule(tolua_S,"Speed"); + tolua_function(tolua_S,"new",lua_ax_base_Speed_constructor); + tolua_function(tolua_S,"getSpeed",lua_ax_base_Speed_getSpeed); + tolua_function(tolua_S,"setSpeed",lua_ax_base_Speed_setSpeed); + tolua_function(tolua_S,"setInnerAction",lua_ax_base_Speed_setInnerAction); + tolua_function(tolua_S,"getInnerAction",lua_ax_base_Speed_getInnerAction); + tolua_function(tolua_S,"initWithAction",lua_ax_base_Speed_initWithAction); + tolua_function(tolua_S,"create", lua_ax_base_Speed_create); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::Speed).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.Speed"; + g_typeCast[typeName] = "ax.Speed"; + return 1; +} + +int lua_ax_base_Follow_isBoundarySet(lua_State* tolua_S) { int argc = 0; - ax::Texture2D* cobj = nullptr; + ax::Follow* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -2409,48 +2497,45 @@ int lua_ax_base_Texture2D_setMaxT(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Follow",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Follow*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_setMaxT'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Follow_isBoundarySet'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Texture2D:setMaxT"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_setMaxT'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Follow_isBoundarySet'", nullptr); return 0; } - cobj->setMaxT(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->isBoundarySet(); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:setMaxT",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Follow:isBoundarySet",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_setMaxT'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Follow_isBoundarySet'.",&tolua_err); #endif return 0; } -int lua_ax_base_Texture2D_getContentSize(lua_State* tolua_S) +int lua_ax_base_Follow_setBoundarySet(lua_State* tolua_S) { int argc = 0; - ax::Texture2D* cobj = nullptr; + ax::Follow* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -2459,45 +2544,48 @@ int lua_ax_base_Texture2D_getContentSize(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Follow",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Follow*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_getContentSize'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Follow_setBoundarySet'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + bool arg0; + + ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.Follow:setBoundarySet"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_getContentSize'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Follow_setBoundarySet'", nullptr); return 0; } - auto&& ret = cobj->getContentSize(); - vec2_to_luaval(tolua_S, ret); + cobj->setBoundarySet(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:getContentSize",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Follow:setBoundarySet",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_getContentSize'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Follow_setBoundarySet'.",&tolua_err); #endif return 0; } -int lua_ax_base_Texture2D_getPath(lua_State* tolua_S) +int lua_ax_base_Follow_initWithTarget(lua_State* tolua_S) { int argc = 0; - ax::Texture2D* cobj = nullptr; + ax::Follow* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -2506,78 +2594,141 @@ int lua_ax_base_Texture2D_getPath(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Follow",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Follow*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_getPath'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Follow_initWithTarget'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + ax::Node* arg0; + + ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Follow:initWithTarget"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_getPath'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Follow_initWithTarget'", nullptr); return 0; } - auto&& ret = cobj->getPath(); - lua_pushlstring(tolua_S,ret.c_str(),ret.length()); + auto&& ret = cobj->initWithTarget(arg0); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:getPath",argc, 0); + if (argc == 2) + { + ax::Node* arg0; + ax::Rect arg1; + + ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Follow:initWithTarget"); + + ok &= luaval_to_rect(tolua_S, 3, &arg1, "ax.Follow:initWithTarget"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Follow_initWithTarget'", nullptr); + return 0; + } + auto&& ret = cobj->initWithTarget(arg0, arg1); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Follow:initWithTarget",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_getPath'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Follow_initWithTarget'.",&tolua_err); #endif return 0; } -int lua_ax_base_Texture2D_setDefaultAlphaPixelFormat(lua_State* tolua_S) +int lua_ax_base_Follow_initWithTargetAndOffset(lua_State* tolua_S) { int argc = 0; + ax::Follow* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Follow",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S) - 1; + cobj = (ax::Follow*)tolua_tousertype(tolua_S,1,0); - if (argc == 1) +#if _AX_DEBUG >= 1 + if (!cobj) { - ax::backend::PixelFormat arg0; - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Texture2D:setDefaultAlphaPixelFormat"); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Follow_initWithTargetAndOffset'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 3) + { + ax::Node* arg0; + double arg1; + double arg2; + + ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Follow:initWithTargetAndOffset"); + + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Follow:initWithTargetAndOffset"); + + ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.Follow:initWithTargetAndOffset"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_setDefaultAlphaPixelFormat'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Follow_initWithTargetAndOffset'", nullptr); return 0; } - ax::Texture2D::setDefaultAlphaPixelFormat(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->initWithTargetAndOffset(arg0, arg1, arg2); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Texture2D:setDefaultAlphaPixelFormat",argc, 1); + if (argc == 4) + { + ax::Node* arg0; + double arg1; + double arg2; + ax::Rect arg3; + + ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Follow:initWithTargetAndOffset"); + + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Follow:initWithTargetAndOffset"); + + ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.Follow:initWithTargetAndOffset"); + + ok &= luaval_to_rect(tolua_S, 5, &arg3, "ax.Follow:initWithTargetAndOffset"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Follow_initWithTargetAndOffset'", nullptr); + return 0; + } + auto&& ret = cobj->initWithTargetAndOffset(arg0, arg1, arg2, arg3); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Follow:initWithTargetAndOffset",argc, 3); return 0; + #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_setDefaultAlphaPixelFormat'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Follow_initWithTargetAndOffset'.",&tolua_err); #endif + return 0; } -int lua_ax_base_Texture2D_getDefaultAlphaPixelFormat(lua_State* tolua_S) +int lua_ax_base_Follow_create(lua_State* tolua_S) { int argc = 0; bool ok = true; @@ -2587,123 +2738,110 @@ int lua_ax_base_Texture2D_getDefaultAlphaPixelFormat(lua_State* tolua_S) #endif #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.Follow",0,&tolua_err)) goto tolua_lerror; #endif argc = lua_gettop(tolua_S) - 1; - if (argc == 0) + if (argc == 1) { + ax::Node* arg0; + ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Follow:create"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_getDefaultAlphaPixelFormat'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Follow_create'", nullptr); return 0; } - int ret = (int)ax::Texture2D::getDefaultAlphaPixelFormat(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + auto&& ret = ax::Follow::create(arg0); + object_to_luaval(tolua_S, "ax.Follow",(ax::Follow*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Texture2D:getDefaultAlphaPixelFormat",argc, 0); + if (argc == 2) + { + ax::Node* arg0; + ax::Rect arg1; + ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Follow:create"); + ok &= luaval_to_rect(tolua_S, 3, &arg1, "ax.Follow:create"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Follow_create'", nullptr); + return 0; + } + auto&& ret = ax::Follow::create(arg0, arg1); + object_to_luaval(tolua_S, "ax.Follow",(ax::Follow*)ret); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Follow:create",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_getDefaultAlphaPixelFormat'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Follow_create'.",&tolua_err); #endif return 0; } -int lua_ax_base_Texture2D_constructor(lua_State* tolua_S) +int lua_ax_base_Follow_createWithOffset(lua_State* tolua_S) { int argc = 0; - ax::Texture2D* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif +#if _AX_DEBUG >= 1 + if (!tolua_isusertable(tolua_S,1,"ax.Follow",0,&tolua_err)) goto tolua_lerror; +#endif + argc = lua_gettop(tolua_S) - 1; - argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 3) { + ax::Node* arg0; + double arg1; + double arg2; + ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Follow:createWithOffset"); + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Follow:createWithOffset"); + ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.Follow:createWithOffset"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_constructor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Follow_createWithOffset'", nullptr); return 0; } - cobj = new ax::Texture2D(); - cobj->autorelease(); - int ID = (int)cobj->_ID ; - int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.Texture2D"); + auto&& ret = ax::Follow::createWithOffset(arg0, arg1, arg2); + object_to_luaval(tolua_S, "ax.Follow",(ax::Follow*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:Texture2D",argc, 0); + if (argc == 4) + { + ax::Node* arg0; + double arg1; + double arg2; + ax::Rect arg3; + ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Follow:createWithOffset"); + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Follow:createWithOffset"); + ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.Follow:createWithOffset"); + ok &= luaval_to_rect(tolua_S, 5, &arg3, "ax.Follow:createWithOffset"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Follow_createWithOffset'", nullptr); + return 0; + } + auto&& ret = ax::Follow::createWithOffset(arg0, arg1, arg2, arg3); + object_to_luaval(tolua_S, "ax.Follow",(ax::Follow*)ret); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Follow:createWithOffset",argc, 3); return 0; - #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_constructor'.",&tolua_err); + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Follow_createWithOffset'.",&tolua_err); #endif - - return 0; -} - -static int lua_ax_base_Texture2D_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (Texture2D)"); return 0; } - -int lua_register_ax_base_Texture2D(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.Texture2D"); - tolua_cclass(tolua_S,"Texture2D","ax.Texture2D","ax.Object",nullptr); - - tolua_beginmodule(tolua_S,"Texture2D"); - tolua_function(tolua_S,"new",lua_ax_base_Texture2D_constructor); - tolua_function(tolua_S,"updateWithImage",lua_ax_base_Texture2D_updateWithImage); - tolua_function(tolua_S,"updateWithMipmaps",lua_ax_base_Texture2D_updateWithMipmaps); - tolua_function(tolua_S,"updateWithSubData",lua_ax_base_Texture2D_updateWithSubData); - tolua_function(tolua_S,"drawAtPoint",lua_ax_base_Texture2D_drawAtPoint); - tolua_function(tolua_S,"drawInRect",lua_ax_base_Texture2D_drawInRect); - tolua_function(tolua_S,"initWithImage",lua_ax_base_Texture2D_initWithImage); - tolua_function(tolua_S,"initWithString",lua_ax_base_Texture2D_initWithString); - tolua_function(tolua_S,"updateTextureDescriptor",lua_ax_base_Texture2D_updateTextureDescriptor); - tolua_function(tolua_S,"setRenderTarget",lua_ax_base_Texture2D_setRenderTarget); - tolua_function(tolua_S,"isRenderTarget",lua_ax_base_Texture2D_isRenderTarget); - tolua_function(tolua_S,"generateMipmap",lua_ax_base_Texture2D_generateMipmap); - tolua_function(tolua_S,"setAntiAliasTexParameters",lua_ax_base_Texture2D_setAntiAliasTexParameters); - tolua_function(tolua_S,"setAliasTexParameters",lua_ax_base_Texture2D_setAliasTexParameters); - tolua_function(tolua_S,"getStringForFormat",lua_ax_base_Texture2D_getStringForFormat); - tolua_function(tolua_S,"getBitsPerPixelForFormat",lua_ax_base_Texture2D_getBitsPerPixelForFormat); - tolua_function(tolua_S,"getContentSizeInPixels",lua_ax_base_Texture2D_getContentSizeInPixels); - tolua_function(tolua_S,"hasPremultipliedAlpha",lua_ax_base_Texture2D_hasPremultipliedAlpha); - tolua_function(tolua_S,"setPremultipliedAlpha",lua_ax_base_Texture2D_setPremultipliedAlpha); - tolua_function(tolua_S,"hasMipmaps",lua_ax_base_Texture2D_hasMipmaps); - tolua_function(tolua_S,"getPixelFormat",lua_ax_base_Texture2D_getPixelFormat); - tolua_function(tolua_S,"getSamplerFlags",lua_ax_base_Texture2D_getSamplerFlags); - tolua_function(tolua_S,"getPixelsWide",lua_ax_base_Texture2D_getPixelsWide); - tolua_function(tolua_S,"getPixelsHigh",lua_ax_base_Texture2D_getPixelsHigh); - tolua_function(tolua_S,"getBackendTexture",lua_ax_base_Texture2D_getBackendTexture); - tolua_function(tolua_S,"getMaxS",lua_ax_base_Texture2D_getMaxS); - tolua_function(tolua_S,"setMaxS",lua_ax_base_Texture2D_setMaxS); - tolua_function(tolua_S,"getMaxT",lua_ax_base_Texture2D_getMaxT); - tolua_function(tolua_S,"setMaxT",lua_ax_base_Texture2D_setMaxT); - tolua_function(tolua_S,"getContentSize",lua_ax_base_Texture2D_getContentSize); - tolua_function(tolua_S,"getPath",lua_ax_base_Texture2D_getPath); - tolua_function(tolua_S,"setDefaultAlphaPixelFormat", lua_ax_base_Texture2D_setDefaultAlphaPixelFormat); - tolua_function(tolua_S,"getDefaultAlphaPixelFormat", lua_ax_base_Texture2D_getDefaultAlphaPixelFormat); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::Texture2D).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.Texture2D"; - g_typeCast[typeName] = "ax.Texture2D"; - return 1; -} - -int lua_ax_base_Touch_getLocation(lua_State* tolua_S) +int lua_ax_base_Follow_constructor(lua_State* tolua_S) { int argc = 0; - ax::Touch* cobj = nullptr; + ax::Follow* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -2711,46 +2849,62 @@ int lua_ax_base_Touch_getLocation(lua_State* tolua_S) #endif -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Touch",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::Touch*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Touch_getLocation'", nullptr); - return 0; - } -#endif argc = lua_gettop(tolua_S)-1; if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Touch_getLocation'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Follow_constructor'", nullptr); return 0; } - auto&& ret = cobj->getLocation(); - vec2_to_luaval(tolua_S, ret); + cobj = new ax::Follow(); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.Follow"); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Touch:getLocation",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Follow:Follow",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Touch_getLocation'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Follow_constructor'.",&tolua_err); #endif return 0; } -int lua_ax_base_Touch_getPreviousLocation(lua_State* tolua_S) + +static int lua_ax_base_Follow_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (Follow)"); + return 0; +} + +int lua_register_ax_base_Follow(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.Follow"); + tolua_cclass(tolua_S,"Follow","ax.Follow","ax.Action",nullptr); + + tolua_beginmodule(tolua_S,"Follow"); + tolua_function(tolua_S,"new",lua_ax_base_Follow_constructor); + tolua_function(tolua_S,"isBoundarySet",lua_ax_base_Follow_isBoundarySet); + tolua_function(tolua_S,"setBoundarySet",lua_ax_base_Follow_setBoundarySet); + tolua_function(tolua_S,"initWithTarget",lua_ax_base_Follow_initWithTarget); + tolua_function(tolua_S,"initWithTargetAndOffset",lua_ax_base_Follow_initWithTargetAndOffset); + tolua_function(tolua_S,"create", lua_ax_base_Follow_create); + tolua_function(tolua_S,"createWithOffset", lua_ax_base_Follow_createWithOffset); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::Follow).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.Follow"; + g_typeCast[typeName] = "ax.Follow"; + return 1; +} + +int lua_ax_base_EventListener_checkAvailable(lua_State* tolua_S) { int argc = 0; - ax::Touch* cobj = nullptr; + ax::EventListener* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -2759,15 +2913,15 @@ int lua_ax_base_Touch_getPreviousLocation(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Touch",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.EventListener",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Touch*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::EventListener*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Touch_getPreviousLocation'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_EventListener_checkAvailable'", nullptr); return 0; } #endif @@ -2777,27 +2931,27 @@ int lua_ax_base_Touch_getPreviousLocation(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Touch_getPreviousLocation'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_EventListener_checkAvailable'", nullptr); return 0; } - auto&& ret = cobj->getPreviousLocation(); - vec2_to_luaval(tolua_S, ret); + auto&& ret = cobj->checkAvailable(); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Touch:getPreviousLocation",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.EventListener:checkAvailable",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Touch_getPreviousLocation'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_EventListener_checkAvailable'.",&tolua_err); #endif return 0; } -int lua_ax_base_Touch_getStartLocation(lua_State* tolua_S) +int lua_ax_base_EventListener_clone(lua_State* tolua_S) { int argc = 0; - ax::Touch* cobj = nullptr; + ax::EventListener* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -2806,15 +2960,15 @@ int lua_ax_base_Touch_getStartLocation(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Touch",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.EventListener",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Touch*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::EventListener*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Touch_getStartLocation'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_EventListener_clone'", nullptr); return 0; } #endif @@ -2824,27 +2978,27 @@ int lua_ax_base_Touch_getStartLocation(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Touch_getStartLocation'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_EventListener_clone'", nullptr); return 0; } - auto&& ret = cobj->getStartLocation(); - vec2_to_luaval(tolua_S, ret); + auto&& ret = cobj->clone(); + object_to_luaval(tolua_S, "ax.EventListener",(ax::EventListener*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Touch:getStartLocation",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.EventListener:clone",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Touch_getStartLocation'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_EventListener_clone'.",&tolua_err); #endif return 0; } -int lua_ax_base_Touch_getDelta(lua_State* tolua_S) +int lua_ax_base_EventListener_setEnabled(lua_State* tolua_S) { int argc = 0; - ax::Touch* cobj = nullptr; + ax::EventListener* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -2853,45 +3007,48 @@ int lua_ax_base_Touch_getDelta(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Touch",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.EventListener",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Touch*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::EventListener*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Touch_getDelta'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_EventListener_setEnabled'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + bool arg0; + + ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.EventListener:setEnabled"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Touch_getDelta'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_EventListener_setEnabled'", nullptr); return 0; } - auto&& ret = cobj->getDelta(); - vec2_to_luaval(tolua_S, ret); + cobj->setEnabled(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Touch:getDelta",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.EventListener:setEnabled",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Touch_getDelta'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_EventListener_setEnabled'.",&tolua_err); #endif return 0; } -int lua_ax_base_Touch_getLocationInView(lua_State* tolua_S) +int lua_ax_base_EventListener_isEnabled(lua_State* tolua_S) { int argc = 0; - ax::Touch* cobj = nullptr; + ax::EventListener* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -2900,15 +3057,15 @@ int lua_ax_base_Touch_getLocationInView(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Touch",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.EventListener",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Touch*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::EventListener*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Touch_getLocationInView'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_EventListener_isEnabled'", nullptr); return 0; } #endif @@ -2918,27 +3075,50 @@ int lua_ax_base_Touch_getLocationInView(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Touch_getLocationInView'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_EventListener_isEnabled'", nullptr); return 0; } - auto&& ret = cobj->getLocationInView(); - vec2_to_luaval(tolua_S, ret); + auto&& ret = cobj->isEnabled(); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Touch:getLocationInView",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.EventListener:isEnabled",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Touch_getLocationInView'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_EventListener_isEnabled'.",&tolua_err); #endif return 0; } -int lua_ax_base_Touch_getPreviousLocationInView(lua_State* tolua_S) +static int lua_ax_base_EventListener_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (EventListener)"); + return 0; +} + +int lua_register_ax_base_EventListener(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.EventListener"); + tolua_cclass(tolua_S,"EventListener","ax.EventListener","ax.Object",nullptr); + + tolua_beginmodule(tolua_S,"EventListener"); + tolua_function(tolua_S,"checkAvailable",lua_ax_base_EventListener_checkAvailable); + tolua_function(tolua_S,"clone",lua_ax_base_EventListener_clone); + tolua_function(tolua_S,"setEnabled",lua_ax_base_EventListener_setEnabled); + tolua_function(tolua_S,"isEnabled",lua_ax_base_EventListener_isEnabled); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::EventListener).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.EventListener"; + g_typeCast[typeName] = "ax.EventListener"; + return 1; +} + +int lua_ax_base_EventListenerCustom_constructor(lua_State* tolua_S) { int argc = 0; - ax::Touch* cobj = nullptr; + ax::EventListenerCustom* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -2946,46 +3126,56 @@ int lua_ax_base_Touch_getPreviousLocationInView(lua_State* tolua_S) #endif -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Touch",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::Touch*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Touch_getPreviousLocationInView'", nullptr); - return 0; - } -#endif argc = lua_gettop(tolua_S)-1; if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Touch_getPreviousLocationInView'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_EventListenerCustom_constructor'", nullptr); return 0; } - auto&& ret = cobj->getPreviousLocationInView(); - vec2_to_luaval(tolua_S, ret); + cobj = new ax::EventListenerCustom(); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.EventListenerCustom"); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Touch:getPreviousLocationInView",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.EventListenerCustom:EventListenerCustom",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Touch_getPreviousLocationInView'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_EventListenerCustom_constructor'.",&tolua_err); #endif return 0; } -int lua_ax_base_Touch_getStartLocationInView(lua_State* tolua_S) + +static int lua_ax_base_EventListenerCustom_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (EventListenerCustom)"); + return 0; +} + +int lua_register_ax_base_EventListenerCustom(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.EventListenerCustom"); + tolua_cclass(tolua_S,"EventListenerCustom","ax.EventListenerCustom","ax.EventListener",nullptr); + + tolua_beginmodule(tolua_S,"EventListenerCustom"); + tolua_function(tolua_S,"new",lua_ax_base_EventListenerCustom_constructor); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::EventListenerCustom).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.EventListenerCustom"; + g_typeCast[typeName] = "ax.EventListenerCustom"; + return 1; +} + +int lua_ax_base_ShaderCache_purge(lua_State* tolua_S) { int argc = 0; - ax::Touch* cobj = nullptr; + ax::backend::ShaderCache* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -2994,15 +3184,15 @@ int lua_ax_base_Touch_getStartLocationInView(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Touch",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"axb.ShaderCache",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Touch*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::backend::ShaderCache*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Touch_getStartLocationInView'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_ShaderCache_purge'", nullptr); return 0; } #endif @@ -3012,106 +3202,77 @@ int lua_ax_base_Touch_getStartLocationInView(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Touch_getStartLocationInView'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_ShaderCache_purge'", nullptr); return 0; } - auto&& ret = cobj->getStartLocationInView(); - vec2_to_luaval(tolua_S, ret); + cobj->purge(); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Touch:getStartLocationInView",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "axb.ShaderCache:purge",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Touch_getStartLocationInView'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_ShaderCache_purge'.",&tolua_err); #endif return 0; } -int lua_ax_base_Touch_setTouchInfo(lua_State* tolua_S) +int lua_ax_base_ShaderCache_newVertexShaderModule(lua_State* tolua_S) { int argc = 0; - ax::Touch* cobj = nullptr; + ax::backend::ShaderCache* cobj = nullptr; bool ok = true; + #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Touch",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"axb.ShaderCache",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Touch*)tolua_tousertype(tolua_S,1,0); + + cobj = (ax::backend::ShaderCache*)tolua_tousertype(tolua_S,1,0); + #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Touch_setTouchInfo'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_ShaderCache_newVertexShaderModule'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - do{ - if (argc == 5) { - int arg0; - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Touch:setTouchInfo"); - - if (!ok) { break; } - double arg1; - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Touch:setTouchInfo"); - - if (!ok) { break; } - double arg2; - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.Touch:setTouchInfo"); - - if (!ok) { break; } - double arg3; - ok &= luaval_to_number(tolua_S, 5,&arg3, "ax.Touch:setTouchInfo"); - - if (!ok) { break; } - double arg4; - ok &= luaval_to_number(tolua_S, 6,&arg4, "ax.Touch:setTouchInfo"); - - if (!ok) { break; } - cobj->setTouchInfo(arg0, arg1, arg2, arg3, arg4); - lua_settop(tolua_S, 1); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 3) { - int arg0; - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Touch:setTouchInfo"); - - if (!ok) { break; } - double arg1; - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Touch:setTouchInfo"); - if (!ok) { break; } - double arg2; - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.Touch:setTouchInfo"); + argc = lua_gettop(tolua_S)-1; + if (argc == 1) + { + std::string_view arg0; - if (!ok) { break; } - cobj->setTouchInfo(arg0, arg1, arg2); - lua_settop(tolua_S, 1); - return 1; + ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "axb.ShaderCache:newVertexShaderModule"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_ShaderCache_newVertexShaderModule'", nullptr); + return 0; } - }while(0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Touch:setTouchInfo",argc, 3); + auto&& ret = cobj->newVertexShaderModule(arg0); + object_to_luaval(tolua_S, "axb.ShaderModule",(ax::backend::ShaderModule*)ret); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "axb.ShaderCache:newVertexShaderModule",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Touch_setTouchInfo'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_ShaderCache_newVertexShaderModule'.",&tolua_err); #endif return 0; } -int lua_ax_base_Touch_getID(lua_State* tolua_S) +int lua_ax_base_ShaderCache_newFragmentShaderModule(lua_State* tolua_S) { int argc = 0; - ax::Touch* cobj = nullptr; + ax::backend::ShaderCache* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -3120,45 +3281,48 @@ int lua_ax_base_Touch_getID(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Touch",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"axb.ShaderCache",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Touch*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::backend::ShaderCache*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Touch_getID'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_ShaderCache_newFragmentShaderModule'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + std::string_view arg0; + + ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "axb.ShaderCache:newFragmentShaderModule"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Touch_getID'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_ShaderCache_newFragmentShaderModule'", nullptr); return 0; } - auto&& ret = cobj->getID(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + auto&& ret = cobj->newFragmentShaderModule(arg0); + object_to_luaval(tolua_S, "axb.ShaderModule",(ax::backend::ShaderModule*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Touch:getID",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "axb.ShaderCache:newFragmentShaderModule",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Touch_getID'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_ShaderCache_newFragmentShaderModule'.",&tolua_err); #endif return 0; } -int lua_ax_base_Touch_getCurrentForce(lua_State* tolua_S) +int lua_ax_base_ShaderCache_removeUnusedShader(lua_State* tolua_S) { int argc = 0; - ax::Touch* cobj = nullptr; + ax::backend::ShaderCache* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -3167,15 +3331,15 @@ int lua_ax_base_Touch_getCurrentForce(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Touch",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"axb.ShaderCache",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Touch*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::backend::ShaderCache*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Touch_getCurrentForce'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_ShaderCache_removeUnusedShader'", nullptr); return 0; } #endif @@ -3185,142 +3349,120 @@ int lua_ax_base_Touch_getCurrentForce(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Touch_getCurrentForce'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_ShaderCache_removeUnusedShader'", nullptr); return 0; } - auto&& ret = cobj->getCurrentForce(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + cobj->removeUnusedShader(); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Touch:getCurrentForce",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "axb.ShaderCache:removeUnusedShader",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Touch_getCurrentForce'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_ShaderCache_removeUnusedShader'.",&tolua_err); #endif return 0; } -int lua_ax_base_Touch_getMaxForce(lua_State* tolua_S) +int lua_ax_base_ShaderCache_getInstance(lua_State* tolua_S) { int argc = 0; - ax::Touch* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Touch",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"axb.ShaderCache",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Touch*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Touch_getMaxForce'", nullptr); - return 0; - } -#endif + argc = lua_gettop(tolua_S) - 1; - argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Touch_getMaxForce'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_ShaderCache_getInstance'", nullptr); return 0; } - auto&& ret = cobj->getMaxForce(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + auto&& ret = ax::backend::ShaderCache::getInstance(); + object_to_luaval(tolua_S, "axb.ShaderCache",(ax::backend::ShaderCache*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Touch:getMaxForce",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "axb.ShaderCache:getInstance",argc, 0); return 0; - #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Touch_getMaxForce'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_ShaderCache_getInstance'.",&tolua_err); #endif - return 0; } -int lua_ax_base_Touch_constructor(lua_State* tolua_S) +int lua_ax_base_ShaderCache_destroyInstance(lua_State* tolua_S) { int argc = 0; - ax::Touch* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif +#if _AX_DEBUG >= 1 + if (!tolua_isusertable(tolua_S,1,"axb.ShaderCache",0,&tolua_err)) goto tolua_lerror; +#endif + argc = lua_gettop(tolua_S) - 1; - argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Touch_constructor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_ShaderCache_destroyInstance'", nullptr); return 0; } - cobj = new ax::Touch(); - cobj->autorelease(); - int ID = (int)cobj->_ID ; - int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.Touch"); + ax::backend::ShaderCache::destroyInstance(); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Touch:Touch",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "axb.ShaderCache:destroyInstance",argc, 0); return 0; - #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Touch_constructor'.",&tolua_err); + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_ShaderCache_destroyInstance'.",&tolua_err); #endif - return 0; } - -static int lua_ax_base_Touch_finalize(lua_State* tolua_S) +static int lua_ax_base_ShaderCache_finalize(lua_State* tolua_S) { - AXLOGV("luabindings: finalizing LUA object (Touch)"); + AXLOGV("luabindings: finalizing LUA object (ShaderCache)"); return 0; } -int lua_register_ax_base_Touch(lua_State* tolua_S) +int lua_register_ax_base_ShaderCache(lua_State* tolua_S) { - tolua_usertype(tolua_S,"ax.Touch"); - tolua_cclass(tolua_S,"Touch","ax.Touch","ax.Object",nullptr); + tolua_usertype(tolua_S,"axb.ShaderCache"); + tolua_cclass(tolua_S,"ShaderCache","axb.ShaderCache","",nullptr); - tolua_beginmodule(tolua_S,"Touch"); - tolua_function(tolua_S,"new",lua_ax_base_Touch_constructor); - tolua_function(tolua_S,"getLocation",lua_ax_base_Touch_getLocation); - tolua_function(tolua_S,"getPreviousLocation",lua_ax_base_Touch_getPreviousLocation); - tolua_function(tolua_S,"getStartLocation",lua_ax_base_Touch_getStartLocation); - tolua_function(tolua_S,"getDelta",lua_ax_base_Touch_getDelta); - tolua_function(tolua_S,"getLocationInView",lua_ax_base_Touch_getLocationInView); - tolua_function(tolua_S,"getPreviousLocationInView",lua_ax_base_Touch_getPreviousLocationInView); - tolua_function(tolua_S,"getStartLocationInView",lua_ax_base_Touch_getStartLocationInView); - tolua_function(tolua_S,"setTouchInfo",lua_ax_base_Touch_setTouchInfo); - tolua_function(tolua_S,"getId",lua_ax_base_Touch_getID); - tolua_function(tolua_S,"getCurrentForce",lua_ax_base_Touch_getCurrentForce); - tolua_function(tolua_S,"getMaxForce",lua_ax_base_Touch_getMaxForce); + tolua_beginmodule(tolua_S,"ShaderCache"); + tolua_function(tolua_S,"purge",lua_ax_base_ShaderCache_purge); + tolua_function(tolua_S,"newVertexShaderModule",lua_ax_base_ShaderCache_newVertexShaderModule); + tolua_function(tolua_S,"newFragmentShaderModule",lua_ax_base_ShaderCache_newFragmentShaderModule); + tolua_function(tolua_S,"removeUnusedShader",lua_ax_base_ShaderCache_removeUnusedShader); + tolua_function(tolua_S,"getInstance", lua_ax_base_ShaderCache_getInstance); + tolua_function(tolua_S,"destroyInstance", lua_ax_base_ShaderCache_destroyInstance); tolua_endmodule(tolua_S); - auto typeName = typeid(ax::Touch).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.Touch"; - g_typeCast[typeName] = "ax.Touch"; + auto typeName = typeid(ax::backend::ShaderCache).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "axb.ShaderCache"; + g_typeCast[typeName] = "axb.ShaderCache"; return 1; } -int lua_ax_base_Event_getType(lua_State* tolua_S) +int lua_ax_base_Texture2D_updateWithImage(lua_State* tolua_S) { int argc = 0; - ax::Event* cobj = nullptr; + ax::Texture2D* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -3329,45 +3471,71 @@ int lua_ax_base_Event_getType(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Event",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Event*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Event_getType'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_updateWithImage'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 2) { + ax::Image* arg0; + ax::backend::PixelFormat arg1; + + ok &= luaval_to_object(tolua_S, 2, "ax.Image",&arg0, "ax.Texture2D:updateWithImage"); + + ok &= luaval_to_int32(tolua_S, 3,(int *)&arg1, "ax.Texture2D:updateWithImage"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Event_getType'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_updateWithImage'", nullptr); return 0; } - int ret = (int)cobj->getType(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + auto&& ret = cobj->updateWithImage(arg0, arg1); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Event:getType",argc, 0); + if (argc == 3) + { + ax::Image* arg0; + ax::backend::PixelFormat arg1; + int arg2; + + ok &= luaval_to_object(tolua_S, 2, "ax.Image",&arg0, "ax.Texture2D:updateWithImage"); + + ok &= luaval_to_int32(tolua_S, 3,(int *)&arg1, "ax.Texture2D:updateWithImage"); + + ok &= luaval_to_int32(tolua_S, 4,(int *)&arg2, "ax.Texture2D:updateWithImage"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_updateWithImage'", nullptr); + return 0; + } + auto&& ret = cobj->updateWithImage(arg0, arg1, arg2); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:updateWithImage",argc, 2); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Event_getType'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_updateWithImage'.",&tolua_err); #endif return 0; } -int lua_ax_base_Event_stopPropagation(lua_State* tolua_S) +int lua_ax_base_Texture2D_updateWithMipmaps(lua_State* tolua_S) { int argc = 0; - ax::Event* cobj = nullptr; + ax::Texture2D* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -3376,92 +3544,133 @@ int lua_ax_base_Event_stopPropagation(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Event",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Event*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Event_stopPropagation'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_updateWithMipmaps'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 6) { + ax::_MipmapInfo* arg0; + int arg1; + ax::backend::PixelFormat arg2; + ax::backend::PixelFormat arg3; + int arg4; + int arg5; + + #pragma warning NO CONVERSION TO NATIVE FOR _MipmapInfo* + ok = false; + + ok &= luaval_to_int32(tolua_S, 3,(int *)&arg1, "ax.Texture2D:updateWithMipmaps"); + + ok &= luaval_to_int32(tolua_S, 4,(int *)&arg2, "ax.Texture2D:updateWithMipmaps"); + + ok &= luaval_to_int32(tolua_S, 5,(int *)&arg3, "ax.Texture2D:updateWithMipmaps"); + + ok &= luaval_to_int32(tolua_S, 6,(int *)&arg4, "ax.Texture2D:updateWithMipmaps"); + + ok &= luaval_to_int32(tolua_S, 7,(int *)&arg5, "ax.Texture2D:updateWithMipmaps"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Event_stopPropagation'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_updateWithMipmaps'", nullptr); return 0; } - cobj->stopPropagation(); - lua_settop(tolua_S, 1); + auto&& ret = cobj->updateWithMipmaps(arg0, arg1, arg2, arg3, arg4, arg5); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Event:stopPropagation",argc, 0); - return 0; + if (argc == 7) + { + ax::_MipmapInfo* arg0; + int arg1; + ax::backend::PixelFormat arg2; + ax::backend::PixelFormat arg3; + int arg4; + int arg5; + bool arg6; -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Event_stopPropagation'.",&tolua_err); -#endif + #pragma warning NO CONVERSION TO NATIVE FOR _MipmapInfo* + ok = false; - return 0; -} -int lua_ax_base_Event_isStopped(lua_State* tolua_S) -{ - int argc = 0; - ax::Event* cobj = nullptr; - bool ok = true; + ok &= luaval_to_int32(tolua_S, 3,(int *)&arg1, "ax.Texture2D:updateWithMipmaps"); -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif + ok &= luaval_to_int32(tolua_S, 4,(int *)&arg2, "ax.Texture2D:updateWithMipmaps"); + ok &= luaval_to_int32(tolua_S, 5,(int *)&arg3, "ax.Texture2D:updateWithMipmaps"); -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Event",0,&tolua_err)) goto tolua_lerror; -#endif + ok &= luaval_to_int32(tolua_S, 6,(int *)&arg4, "ax.Texture2D:updateWithMipmaps"); - cobj = (ax::Event*)tolua_tousertype(tolua_S,1,0); + ok &= luaval_to_int32(tolua_S, 7,(int *)&arg5, "ax.Texture2D:updateWithMipmaps"); -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Event_isStopped'", nullptr); - return 0; + ok &= luaval_to_boolean(tolua_S, 8,&arg6, "ax.Texture2D:updateWithMipmaps"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_updateWithMipmaps'", nullptr); + return 0; + } + auto&& ret = cobj->updateWithMipmaps(arg0, arg1, arg2, arg3, arg4, arg5, arg6); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 8) { + ax::_MipmapInfo* arg0; + int arg1; + ax::backend::PixelFormat arg2; + ax::backend::PixelFormat arg3; + int arg4; + int arg5; + bool arg6; + int arg7; + + #pragma warning NO CONVERSION TO NATIVE FOR _MipmapInfo* + ok = false; + + ok &= luaval_to_int32(tolua_S, 3,(int *)&arg1, "ax.Texture2D:updateWithMipmaps"); + + ok &= luaval_to_int32(tolua_S, 4,(int *)&arg2, "ax.Texture2D:updateWithMipmaps"); + + ok &= luaval_to_int32(tolua_S, 5,(int *)&arg3, "ax.Texture2D:updateWithMipmaps"); + + ok &= luaval_to_int32(tolua_S, 6,(int *)&arg4, "ax.Texture2D:updateWithMipmaps"); + + ok &= luaval_to_int32(tolua_S, 7,(int *)&arg5, "ax.Texture2D:updateWithMipmaps"); + + ok &= luaval_to_boolean(tolua_S, 8,&arg6, "ax.Texture2D:updateWithMipmaps"); + + ok &= luaval_to_int32(tolua_S, 9,(int *)&arg7, "ax.Texture2D:updateWithMipmaps"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Event_isStopped'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_updateWithMipmaps'", nullptr); return 0; } - auto&& ret = cobj->isStopped(); + auto&& ret = cobj->updateWithMipmaps(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7); tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Event:isStopped",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:updateWithMipmaps",argc, 6); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Event_isStopped'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_updateWithMipmaps'.",&tolua_err); #endif return 0; } -int lua_ax_base_Event_getCurrentTarget(lua_State* tolua_S) +int lua_ax_base_Texture2D_updateWithSubData(lua_State* tolua_S) { int argc = 0; - ax::Event* cobj = nullptr; + ax::Texture2D* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -3470,109 +3679,91 @@ int lua_ax_base_Event_getCurrentTarget(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Event",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Event*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Event_getCurrentTarget'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_updateWithSubData'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 5) { + void* arg0; + int arg1; + int arg2; + int arg3; + int arg4; + + #pragma warning NO CONVERSION TO NATIVE FOR void* + ok = false; + + ok &= luaval_to_int32(tolua_S, 3,(int *)&arg1, "ax.Texture2D:updateWithSubData"); + + ok &= luaval_to_int32(tolua_S, 4,(int *)&arg2, "ax.Texture2D:updateWithSubData"); + + ok &= luaval_to_int32(tolua_S, 5,(int *)&arg3, "ax.Texture2D:updateWithSubData"); + + ok &= luaval_to_int32(tolua_S, 6,(int *)&arg4, "ax.Texture2D:updateWithSubData"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Event_getCurrentTarget'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_updateWithSubData'", nullptr); return 0; } - auto&& ret = cobj->getCurrentTarget(); - object_to_luaval(tolua_S, "ax.Node",(ax::Node*)ret); + auto&& ret = cobj->updateWithSubData(arg0, arg1, arg2, arg3, arg4); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Event:getCurrentTarget",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Event_getCurrentTarget'.",&tolua_err); -#endif + if (argc == 6) + { + void* arg0; + int arg1; + int arg2; + int arg3; + int arg4; + int arg5; - return 0; -} -int lua_ax_base_Event_constructor(lua_State* tolua_S) -{ - int argc = 0; - ax::Event* cobj = nullptr; - bool ok = true; + #pragma warning NO CONVERSION TO NATIVE FOR void* + ok = false; -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif + ok &= luaval_to_int32(tolua_S, 3,(int *)&arg1, "ax.Texture2D:updateWithSubData"); + ok &= luaval_to_int32(tolua_S, 4,(int *)&arg2, "ax.Texture2D:updateWithSubData"); + ok &= luaval_to_int32(tolua_S, 5,(int *)&arg3, "ax.Texture2D:updateWithSubData"); - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - ax::Event::Type arg0; + ok &= luaval_to_int32(tolua_S, 6,(int *)&arg4, "ax.Texture2D:updateWithSubData"); - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Event:Event"); + ok &= luaval_to_int32(tolua_S, 7,(int *)&arg5, "ax.Texture2D:updateWithSubData"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Event_constructor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_updateWithSubData'", nullptr); return 0; } - cobj = new ax::Event(arg0); - cobj->autorelease(); - int ID = (int)cobj->_ID ; - int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.Event"); + auto&& ret = cobj->updateWithSubData(arg0, arg1, arg2, arg3, arg4, arg5); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Event:Event",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:updateWithSubData",argc, 5); return 0; #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Event_constructor'.",&tolua_err); + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_updateWithSubData'.",&tolua_err); #endif return 0; } - -static int lua_ax_base_Event_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (Event)"); - return 0; -} - -int lua_register_ax_base_Event(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.Event"); - tolua_cclass(tolua_S,"Event","ax.Event","ax.Object",nullptr); - - tolua_beginmodule(tolua_S,"Event"); - tolua_function(tolua_S,"new",lua_ax_base_Event_constructor); - tolua_function(tolua_S,"getType",lua_ax_base_Event_getType); - tolua_function(tolua_S,"stopPropagation",lua_ax_base_Event_stopPropagation); - tolua_function(tolua_S,"isStopped",lua_ax_base_Event_isStopped); - tolua_function(tolua_S,"getCurrentTarget",lua_ax_base_Event_getCurrentTarget); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::Event).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.Event"; - g_typeCast[typeName] = "ax.Event"; - return 1; -} - -int lua_ax_base_EventTouch_getEventCode(lua_State* tolua_S) +int lua_ax_base_Texture2D_drawAtPoint(lua_State* tolua_S) { int argc = 0; - ax::EventTouch* cobj = nullptr; + ax::Texture2D* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -3581,45 +3772,51 @@ int lua_ax_base_EventTouch_getEventCode(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.EventTouch",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::EventTouch*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_EventTouch_getEventCode'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_drawAtPoint'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 2) { + ax::Vec2 arg0; + double arg1; + + ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.Texture2D:drawAtPoint"); + + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Texture2D:drawAtPoint"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_EventTouch_getEventCode'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_drawAtPoint'", nullptr); return 0; } - int ret = (int)cobj->getEventCode(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + cobj->drawAtPoint(arg0, arg1); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.EventTouch:getEventCode",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:drawAtPoint",argc, 2); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_EventTouch_getEventCode'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_drawAtPoint'.",&tolua_err); #endif return 0; } -int lua_ax_base_EventTouch_setEventCode(lua_State* tolua_S) +int lua_ax_base_Texture2D_drawInRect(lua_State* tolua_S) { int argc = 0; - ax::EventTouch* cobj = nullptr; + ax::Texture2D* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -3628,217 +3825,337 @@ int lua_ax_base_EventTouch_setEventCode(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.EventTouch",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::EventTouch*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_EventTouch_setEventCode'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_drawInRect'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 2) { - ax::EventTouch::EventCode arg0; + ax::Rect arg0; + double arg1; - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.EventTouch:setEventCode"); + ok &= luaval_to_rect(tolua_S, 2, &arg0, "ax.Texture2D:drawInRect"); + + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Texture2D:drawInRect"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_EventTouch_setEventCode'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_drawInRect'", nullptr); return 0; } - cobj->setEventCode(arg0); + cobj->drawInRect(arg0, arg1); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.EventTouch:setEventCode",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:drawInRect",argc, 2); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_EventTouch_setEventCode'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_drawInRect'.",&tolua_err); #endif return 0; } -int lua_ax_base_EventTouch_constructor(lua_State* tolua_S) +int lua_ax_base_Texture2D_initWithImage(lua_State* tolua_S) { int argc = 0; - ax::EventTouch* cobj = nullptr; + ax::Texture2D* cobj = nullptr; bool ok = true; - #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_EventTouch_constructor'", nullptr); - return 0; - } - cobj = new ax::EventTouch(); - cobj->autorelease(); - int ID = (int)cobj->_ID ; - int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.EventTouch"); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.EventTouch:EventTouch",argc, 0); - return 0; - #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_EventTouch_constructor'.",&tolua_err); + if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; #endif - - return 0; -} - -static int lua_ax_base_EventTouch_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (EventTouch)"); - return 0; -} - -int lua_register_ax_base_EventTouch(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.EventTouch"); - tolua_cclass(tolua_S,"EventTouch","ax.EventTouch","ax.Event",nullptr); - - tolua_beginmodule(tolua_S,"EventTouch"); - tolua_function(tolua_S,"new",lua_ax_base_EventTouch_constructor); - tolua_function(tolua_S,"getEventCode",lua_ax_base_EventTouch_getEventCode); - tolua_function(tolua_S,"setEventCode",lua_ax_base_EventTouch_setEventCode); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::EventTouch).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.EventTouch"; - g_typeCast[typeName] = "ax.EventTouch"; - return 1; -} - -int lua_ax_base_EventKeyboard_constructor(lua_State* tolua_S) -{ - int argc = 0; - ax::EventKeyboard* cobj = nullptr; - bool ok = true; - + cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 - tolua_Error tolua_err; + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_initWithImage'", nullptr); + return 0; + } #endif - - - argc = lua_gettop(tolua_S)-1; - if (argc == 2) - { - ax::EventKeyboard::KeyCode arg0; - bool arg1; + do{ + if (argc == 2) { + ax::Image* arg0; + ok &= luaval_to_object(tolua_S, 2, "ax.Image",&arg0, "ax.Texture2D:initWithImage"); - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.EventKeyboard:EventKeyboard"); + if (!ok) { break; } + ax::backend::PixelFormat arg1; + ok &= luaval_to_int32(tolua_S, 3,(int *)&arg1, "ax.Texture2D:initWithImage"); - ok &= luaval_to_boolean(tolua_S, 3,&arg1, "ax.EventKeyboard:EventKeyboard"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_EventKeyboard_constructor'", nullptr); - return 0; + if (!ok) { break; } + bool ret = cobj->initWithImage(arg0, arg1); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; } - cobj = new ax::EventKeyboard(arg0, arg1); - cobj->autorelease(); - int ID = (int)cobj->_ID ; - int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.EventKeyboard"); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.EventKeyboard:EventKeyboard",argc, 2); + }while(0); + ok = true; + do{ + if (argc == 1) { + ax::Image* arg0; + ok &= luaval_to_object(tolua_S, 2, "ax.Image",&arg0, "ax.Texture2D:initWithImage"); + + if (!ok) { break; } + bool ret = cobj->initWithImage(arg0); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; + } + }while(0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:initWithImage",argc, 1); return 0; #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_EventKeyboard_constructor'.",&tolua_err); + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_initWithImage'.",&tolua_err); #endif return 0; } - -static int lua_ax_base_EventKeyboard_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (EventKeyboard)"); - return 0; -} - -int lua_register_ax_base_EventKeyboard(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.EventKeyboard"); - tolua_cclass(tolua_S,"EventKeyboard","ax.EventKeyboard","ax.Event",nullptr); - - tolua_beginmodule(tolua_S,"EventKeyboard"); - tolua_function(tolua_S,"new",lua_ax_base_EventKeyboard_constructor); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::EventKeyboard).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.EventKeyboard"; - g_typeCast[typeName] = "ax.EventKeyboard"; - return 1; -} - -int lua_ax_base_Component_init(lua_State* tolua_S) +int lua_ax_base_Texture2D_initWithString(lua_State* tolua_S) { int argc = 0; - ax::Component* cobj = nullptr; + ax::Texture2D* cobj = nullptr; bool ok = true; - #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Component",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; #endif - - cobj = (ax::Component*)tolua_tousertype(tolua_S,1,0); - + cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Component_init'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_initWithString'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Component_init'", nullptr); - return 0; + do{ + if (argc == 2) { + std::string_view arg0; + ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Texture2D:initWithString"); + + if (!ok) { break; } + ax::FontDefinition arg1; + ok &= luaval_to_fontdefinition(tolua_S, 3, &arg1, "ax.Texture2D:initWithString"); + + if (!ok) { break; } + bool ret = cobj->initWithString(arg0, arg1); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; } - auto&& ret = cobj->init(); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Component:init",argc, 0); + }while(0); + ok = true; + do{ + if (argc == 3) { + std::string_view arg0; + ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Texture2D:initWithString"); + + if (!ok) { break; } + std::string_view arg1; + ok &= luaval_to_std_string_view(tolua_S, 3,&arg1, "ax.Texture2D:initWithString"); + + if (!ok) { break; } + double arg2; + ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.Texture2D:initWithString"); + + if (!ok) { break; } + bool ret = cobj->initWithString(arg0, arg1, arg2); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; + } + }while(0); + ok = true; + do{ + if (argc == 4) { + std::string_view arg0; + ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Texture2D:initWithString"); + + if (!ok) { break; } + std::string_view arg1; + ok &= luaval_to_std_string_view(tolua_S, 3,&arg1, "ax.Texture2D:initWithString"); + + if (!ok) { break; } + double arg2; + ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.Texture2D:initWithString"); + + if (!ok) { break; } + ax::Vec2 arg3; + ok &= luaval_to_vec2(tolua_S, 5, &arg3, "ax.Texture2D:initWithString"); + + if (!ok) { break; } + bool ret = cobj->initWithString(arg0, arg1, arg2, arg3); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; + } + }while(0); + ok = true; + do{ + if (argc == 5) { + std::string_view arg0; + ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Texture2D:initWithString"); + + if (!ok) { break; } + std::string_view arg1; + ok &= luaval_to_std_string_view(tolua_S, 3,&arg1, "ax.Texture2D:initWithString"); + + if (!ok) { break; } + double arg2; + ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.Texture2D:initWithString"); + + if (!ok) { break; } + ax::Vec2 arg3; + ok &= luaval_to_vec2(tolua_S, 5, &arg3, "ax.Texture2D:initWithString"); + + if (!ok) { break; } + ax::TextHAlignment arg4; + ok &= luaval_to_int32(tolua_S, 6,(int *)&arg4, "ax.Texture2D:initWithString"); + + if (!ok) { break; } + bool ret = cobj->initWithString(arg0, arg1, arg2, arg3, arg4); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; + } + }while(0); + ok = true; + do{ + if (argc == 6) { + std::string_view arg0; + ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Texture2D:initWithString"); + + if (!ok) { break; } + std::string_view arg1; + ok &= luaval_to_std_string_view(tolua_S, 3,&arg1, "ax.Texture2D:initWithString"); + + if (!ok) { break; } + double arg2; + ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.Texture2D:initWithString"); + + if (!ok) { break; } + ax::Vec2 arg3; + ok &= luaval_to_vec2(tolua_S, 5, &arg3, "ax.Texture2D:initWithString"); + + if (!ok) { break; } + ax::TextHAlignment arg4; + ok &= luaval_to_int32(tolua_S, 6,(int *)&arg4, "ax.Texture2D:initWithString"); + + if (!ok) { break; } + ax::TextVAlignment arg5; + ok &= luaval_to_int32(tolua_S, 7,(int *)&arg5, "ax.Texture2D:initWithString"); + + if (!ok) { break; } + bool ret = cobj->initWithString(arg0, arg1, arg2, arg3, arg4, arg5); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; + } + }while(0); + ok = true; + do{ + if (argc == 7) { + std::string_view arg0; + ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Texture2D:initWithString"); + + if (!ok) { break; } + std::string_view arg1; + ok &= luaval_to_std_string_view(tolua_S, 3,&arg1, "ax.Texture2D:initWithString"); + + if (!ok) { break; } + double arg2; + ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.Texture2D:initWithString"); + + if (!ok) { break; } + ax::Vec2 arg3; + ok &= luaval_to_vec2(tolua_S, 5, &arg3, "ax.Texture2D:initWithString"); + + if (!ok) { break; } + ax::TextHAlignment arg4; + ok &= luaval_to_int32(tolua_S, 6,(int *)&arg4, "ax.Texture2D:initWithString"); + + if (!ok) { break; } + ax::TextVAlignment arg5; + ok &= luaval_to_int32(tolua_S, 7,(int *)&arg5, "ax.Texture2D:initWithString"); + + if (!ok) { break; } + bool arg6; + ok &= luaval_to_boolean(tolua_S, 8,&arg6, "ax.Texture2D:initWithString"); + + if (!ok) { break; } + bool ret = cobj->initWithString(arg0, arg1, arg2, arg3, arg4, arg5, arg6); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; + } + }while(0); + ok = true; + do{ + if (argc == 8) { + std::string_view arg0; + ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Texture2D:initWithString"); + + if (!ok) { break; } + std::string_view arg1; + ok &= luaval_to_std_string_view(tolua_S, 3,&arg1, "ax.Texture2D:initWithString"); + + if (!ok) { break; } + double arg2; + ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.Texture2D:initWithString"); + + if (!ok) { break; } + ax::Vec2 arg3; + ok &= luaval_to_vec2(tolua_S, 5, &arg3, "ax.Texture2D:initWithString"); + + if (!ok) { break; } + ax::TextHAlignment arg4; + ok &= luaval_to_int32(tolua_S, 6,(int *)&arg4, "ax.Texture2D:initWithString"); + + if (!ok) { break; } + ax::TextVAlignment arg5; + ok &= luaval_to_int32(tolua_S, 7,(int *)&arg5, "ax.Texture2D:initWithString"); + + if (!ok) { break; } + bool arg6; + ok &= luaval_to_boolean(tolua_S, 8,&arg6, "ax.Texture2D:initWithString"); + + if (!ok) { break; } + int arg7; + ok &= luaval_to_int32(tolua_S, 9,(int *)&arg7, "ax.Texture2D:initWithString"); + + if (!ok) { break; } + bool ret = cobj->initWithString(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; + } + }while(0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:initWithString",argc, 3); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Component_init'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_initWithString'.",&tolua_err); #endif return 0; } -int lua_ax_base_Component_isEnabled(lua_State* tolua_S) +int lua_ax_base_Texture2D_updateTextureDescriptor(lua_State* tolua_S) { int argc = 0; - ax::Component* cobj = nullptr; + ax::Texture2D* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -3847,45 +4164,67 @@ int lua_ax_base_Component_isEnabled(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Component",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Component*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Component_isEnabled'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_updateTextureDescriptor'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + ax::backend::TextureDescriptor arg0; + + #pragma warning NO CONVERSION TO NATIVE FOR TextureDescriptor + ok = false; if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Component_isEnabled'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_updateTextureDescriptor'", nullptr); return 0; } - auto&& ret = cobj->isEnabled(); + auto&& ret = cobj->updateTextureDescriptor(arg0); tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Component:isEnabled",argc, 0); + if (argc == 2) + { + ax::backend::TextureDescriptor arg0; + bool arg1; + + #pragma warning NO CONVERSION TO NATIVE FOR TextureDescriptor + ok = false; + + ok &= luaval_to_boolean(tolua_S, 3,&arg1, "ax.Texture2D:updateTextureDescriptor"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_updateTextureDescriptor'", nullptr); + return 0; + } + auto&& ret = cobj->updateTextureDescriptor(arg0, arg1); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:updateTextureDescriptor",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Component_isEnabled'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_updateTextureDescriptor'.",&tolua_err); #endif return 0; } -int lua_ax_base_Component_setEnabled(lua_State* tolua_S) +int lua_ax_base_Texture2D_setRenderTarget(lua_State* tolua_S) { int argc = 0; - ax::Component* cobj = nullptr; + ax::Texture2D* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -3894,15 +4233,15 @@ int lua_ax_base_Component_setEnabled(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Component",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Component*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Component_setEnabled'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_setRenderTarget'", nullptr); return 0; } #endif @@ -3912,30 +4251,30 @@ int lua_ax_base_Component_setEnabled(lua_State* tolua_S) { bool arg0; - ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.Component:setEnabled"); + ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.Texture2D:setRenderTarget"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Component_setEnabled'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_setRenderTarget'", nullptr); return 0; } - cobj->setEnabled(arg0); + cobj->setRenderTarget(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Component:setEnabled",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:setRenderTarget",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Component_setEnabled'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_setRenderTarget'.",&tolua_err); #endif return 0; } -int lua_ax_base_Component_getName(lua_State* tolua_S) +int lua_ax_base_Texture2D_isRenderTarget(lua_State* tolua_S) { int argc = 0; - ax::Component* cobj = nullptr; + ax::Texture2D* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -3944,15 +4283,15 @@ int lua_ax_base_Component_getName(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Component",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Component*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Component_getName'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_isRenderTarget'", nullptr); return 0; } #endif @@ -3962,27 +4301,27 @@ int lua_ax_base_Component_getName(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Component_getName'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_isRenderTarget'", nullptr); return 0; } - auto&& ret = cobj->getName(); - lua_pushlstring(tolua_S,ret.data(),ret.length()); + auto&& ret = cobj->isRenderTarget(); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Component:getName",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:isRenderTarget",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Component_getName'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_isRenderTarget'.",&tolua_err); #endif return 0; } -int lua_ax_base_Component_setName(lua_State* tolua_S) +int lua_ax_base_Texture2D_generateMipmap(lua_State* tolua_S) { int argc = 0; - ax::Component* cobj = nullptr; + ax::Texture2D* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -3991,48 +4330,45 @@ int lua_ax_base_Component_setName(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Component",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Component*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Component_setName'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_generateMipmap'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - std::string_view arg0; - - ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Component:setName"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Component_setName'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_generateMipmap'", nullptr); return 0; } - cobj->setName(arg0); + cobj->generateMipmap(); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Component:setName",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:generateMipmap",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Component_setName'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_generateMipmap'.",&tolua_err); #endif return 0; } -int lua_ax_base_Component_getOwner(lua_State* tolua_S) +int lua_ax_base_Texture2D_setAntiAliasTexParameters(lua_State* tolua_S) { int argc = 0; - ax::Component* cobj = nullptr; + ax::Texture2D* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -4041,15 +4377,15 @@ int lua_ax_base_Component_getOwner(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Component",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Component*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Component_getOwner'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_setAntiAliasTexParameters'", nullptr); return 0; } #endif @@ -4059,27 +4395,27 @@ int lua_ax_base_Component_getOwner(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Component_getOwner'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_setAntiAliasTexParameters'", nullptr); return 0; } - auto&& ret = cobj->getOwner(); - object_to_luaval(tolua_S, "ax.Node",(ax::Node*)ret); + cobj->setAntiAliasTexParameters(); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Component:getOwner",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:setAntiAliasTexParameters",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Component_getOwner'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_setAntiAliasTexParameters'.",&tolua_err); #endif return 0; } -int lua_ax_base_Component_setOwner(lua_State* tolua_S) +int lua_ax_base_Texture2D_setAliasTexParameters(lua_State* tolua_S) { int argc = 0; - ax::Component* cobj = nullptr; + ax::Texture2D* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -4088,48 +4424,45 @@ int lua_ax_base_Component_setOwner(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Component",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Component*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Component_setOwner'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_setAliasTexParameters'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - ax::Node* arg0; - - ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Component:setOwner"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Component_setOwner'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_setAliasTexParameters'", nullptr); return 0; } - cobj->setOwner(arg0); + cobj->setAliasTexParameters(); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Component:setOwner",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:setAliasTexParameters",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Component_setOwner'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_setAliasTexParameters'.",&tolua_err); #endif return 0; } -int lua_ax_base_Component_update(lua_State* tolua_S) +int lua_ax_base_Texture2D_getStringForFormat(lua_State* tolua_S) { int argc = 0; - ax::Component* cobj = nullptr; + ax::Texture2D* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -4138,48 +4471,96 @@ int lua_ax_base_Component_update(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Component",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Component*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Component_update'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_getStringForFormat'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Component:update"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Component_update'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_getStringForFormat'", nullptr); return 0; } - cobj->update(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getStringForFormat(); + tolua_pushstring(tolua_S,(const char*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Component:update",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:getStringForFormat",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Component_update'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_getStringForFormat'.",&tolua_err); #endif return 0; } -int lua_ax_base_Component_onAdd(lua_State* tolua_S) +int lua_ax_base_Texture2D_getBitsPerPixelForFormat(lua_State* tolua_S) { int argc = 0; - ax::Component* cobj = nullptr; + ax::Texture2D* cobj = nullptr; + bool ok = true; +#if _AX_DEBUG >= 1 + tolua_Error tolua_err; +#endif + +#if _AX_DEBUG >= 1 + if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; +#endif + cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_getBitsPerPixelForFormat'", nullptr); + return 0; + } +#endif + argc = lua_gettop(tolua_S)-1; + do{ + if (argc == 1) { + ax::backend::PixelFormat arg0; + ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Texture2D:getBitsPerPixelForFormat"); + + if (!ok) { break; } + unsigned int ret = cobj->getBitsPerPixelForFormat(arg0); + tolua_pushnumber(tolua_S,(lua_Number)ret); + return 1; + } + }while(0); + ok = true; + do{ + if (argc == 0) { + unsigned int ret = cobj->getBitsPerPixelForFormat(); + tolua_pushnumber(tolua_S,(lua_Number)ret); + return 1; + } + }while(0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:getBitsPerPixelForFormat",argc, 0); + return 0; + +#if _AX_DEBUG >= 1 + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_getBitsPerPixelForFormat'.",&tolua_err); +#endif + + return 0; +} +int lua_ax_base_Texture2D_getContentSizeInPixels(lua_State* tolua_S) +{ + int argc = 0; + ax::Texture2D* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -4188,15 +4569,15 @@ int lua_ax_base_Component_onAdd(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Component",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Component*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Component_onAdd'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_getContentSizeInPixels'", nullptr); return 0; } #endif @@ -4206,27 +4587,27 @@ int lua_ax_base_Component_onAdd(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Component_onAdd'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_getContentSizeInPixels'", nullptr); return 0; } - cobj->onAdd(); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getContentSizeInPixels(); + vec2_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Component:onAdd",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:getContentSizeInPixels",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Component_onAdd'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_getContentSizeInPixels'.",&tolua_err); #endif return 0; } -int lua_ax_base_Component_onRemove(lua_State* tolua_S) +int lua_ax_base_Texture2D_hasPremultipliedAlpha(lua_State* tolua_S) { int argc = 0; - ax::Component* cobj = nullptr; + ax::Texture2D* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -4235,15 +4616,15 @@ int lua_ax_base_Component_onRemove(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Component",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Component*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Component_onRemove'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_hasPremultipliedAlpha'", nullptr); return 0; } #endif @@ -4253,138 +4634,77 @@ int lua_ax_base_Component_onRemove(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Component_onRemove'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_hasPremultipliedAlpha'", nullptr); return 0; } - cobj->onRemove(); - lua_settop(tolua_S, 1); + auto&& ret = cobj->hasPremultipliedAlpha(); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Component:onRemove",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:hasPremultipliedAlpha",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Component_onRemove'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_hasPremultipliedAlpha'.",&tolua_err); #endif return 0; } -int lua_ax_base_Component_create(lua_State* tolua_S) +int lua_ax_base_Texture2D_setPremultipliedAlpha(lua_State* tolua_S) { int argc = 0; + ax::Texture2D* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.Component",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S) - 1; + cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); - if (argc == 0) +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_setPremultipliedAlpha'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 1) { + bool arg0; + + ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.Texture2D:setPremultipliedAlpha"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Component_create'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_setPremultipliedAlpha'", nullptr); return 0; } - auto&& ret = ax::Component::create(); - object_to_luaval(tolua_S, "ax.Component",(ax::Component*)ret); + cobj->setPremultipliedAlpha(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Component:create",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:setPremultipliedAlpha",argc, 1); return 0; + #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Component_create'.",&tolua_err); -#endif - return 0; -} -static int lua_ax_base_Component_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (Component)"); - return 0; -} - -int lua_register_ax_base_Component(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.Component"); - tolua_cclass(tolua_S,"Component","ax.Component","ax.Object",nullptr); - - tolua_beginmodule(tolua_S,"Component"); - tolua_function(tolua_S,"init",lua_ax_base_Component_init); - tolua_function(tolua_S,"isEnabled",lua_ax_base_Component_isEnabled); - tolua_function(tolua_S,"setEnabled",lua_ax_base_Component_setEnabled); - tolua_function(tolua_S,"getName",lua_ax_base_Component_getName); - tolua_function(tolua_S,"setName",lua_ax_base_Component_setName); - tolua_function(tolua_S,"getOwner",lua_ax_base_Component_getOwner); - tolua_function(tolua_S,"setOwner",lua_ax_base_Component_setOwner); - tolua_function(tolua_S,"update",lua_ax_base_Component_update); - tolua_function(tolua_S,"onAdd",lua_ax_base_Component_onAdd); - tolua_function(tolua_S,"onRemove",lua_ax_base_Component_onRemove); - tolua_function(tolua_S,"create", lua_ax_base_Component_create); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::Component).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.Component"; - g_typeCast[typeName] = "ax.Component"; - return 1; -} - -int lua_ax_base_Node_getDescription(lua_State* tolua_S) -{ - int argc = 0; - ax::Node* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getDescription'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getDescription'", nullptr); - return 0; - } - auto&& ret = cobj->getDescription(); - lua_pushlstring(tolua_S,ret.c_str(),ret.length()); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getDescription",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getDescription'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_setPremultipliedAlpha'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setLocalZOrder(lua_State* tolua_S) +int lua_ax_base_Texture2D_hasMipmaps(lua_State* tolua_S) { int argc = 0; - ax::Node* cobj = nullptr; + ax::Texture2D* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -4393,48 +4713,45 @@ int lua_ax_base_Node_setLocalZOrder(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setLocalZOrder'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_hasMipmaps'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - int arg0; - - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Node:setLocalZOrder"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setLocalZOrder'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_hasMipmaps'", nullptr); return 0; } - cobj->setLocalZOrder(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->hasMipmaps(); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setLocalZOrder",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:hasMipmaps",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setLocalZOrder'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_hasMipmaps'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_updateOrderOfArrival(lua_State* tolua_S) +int lua_ax_base_Texture2D_getPixelFormat(lua_State* tolua_S) { int argc = 0; - ax::Node* cobj = nullptr; + ax::Texture2D* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -4443,15 +4760,15 @@ int lua_ax_base_Node_updateOrderOfArrival(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_updateOrderOfArrival'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_getPixelFormat'", nullptr); return 0; } #endif @@ -4461,27 +4778,27 @@ int lua_ax_base_Node_updateOrderOfArrival(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_updateOrderOfArrival'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_getPixelFormat'", nullptr); return 0; } - cobj->updateOrderOfArrival(); - lua_settop(tolua_S, 1); + int ret = (int)cobj->getPixelFormat(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:updateOrderOfArrival",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:getPixelFormat",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_updateOrderOfArrival'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_getPixelFormat'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getLocalZOrder(lua_State* tolua_S) +int lua_ax_base_Texture2D_getSamplerFlags(lua_State* tolua_S) { int argc = 0; - ax::Node* cobj = nullptr; + ax::Texture2D* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -4490,15 +4807,15 @@ int lua_ax_base_Node_getLocalZOrder(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getLocalZOrder'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_getSamplerFlags'", nullptr); return 0; } #endif @@ -4508,27 +4825,27 @@ int lua_ax_base_Node_getLocalZOrder(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getLocalZOrder'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_getSamplerFlags'", nullptr); return 0; } - auto&& ret = cobj->getLocalZOrder(); + auto&& ret = cobj->getSamplerFlags(); tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getLocalZOrder",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:getSamplerFlags",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getLocalZOrder'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_getSamplerFlags'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setGlobalZOrder(lua_State* tolua_S) +int lua_ax_base_Texture2D_getPixelsWide(lua_State* tolua_S) { int argc = 0; - ax::Node* cobj = nullptr; + ax::Texture2D* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -4537,48 +4854,45 @@ int lua_ax_base_Node_setGlobalZOrder(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setGlobalZOrder'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_getPixelsWide'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Node:setGlobalZOrder"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setGlobalZOrder'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_getPixelsWide'", nullptr); return 0; } - cobj->setGlobalZOrder(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getPixelsWide(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setGlobalZOrder",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:getPixelsWide",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setGlobalZOrder'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_getPixelsWide'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getGlobalZOrder(lua_State* tolua_S) +int lua_ax_base_Texture2D_getPixelsHigh(lua_State* tolua_S) { int argc = 0; - ax::Node* cobj = nullptr; + ax::Texture2D* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -4587,15 +4901,15 @@ int lua_ax_base_Node_getGlobalZOrder(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getGlobalZOrder'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_getPixelsHigh'", nullptr); return 0; } #endif @@ -4605,27 +4919,27 @@ int lua_ax_base_Node_getGlobalZOrder(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getGlobalZOrder'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_getPixelsHigh'", nullptr); return 0; } - auto&& ret = cobj->getGlobalZOrder(); + auto&& ret = cobj->getPixelsHigh(); tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getGlobalZOrder",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:getPixelsHigh",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getGlobalZOrder'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_getPixelsHigh'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setScaleX(lua_State* tolua_S) +int lua_ax_base_Texture2D_getBackendTexture(lua_State* tolua_S) { int argc = 0; - ax::Node* cobj = nullptr; + ax::Texture2D* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -4634,48 +4948,45 @@ int lua_ax_base_Node_setScaleX(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setScaleX'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_getBackendTexture'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Node:setScaleX"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setScaleX'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_getBackendTexture'", nullptr); return 0; } - cobj->setScaleX(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getBackendTexture(); + object_to_luaval(tolua_S, "axb.TextureBackend",(ax::backend::TextureBackend*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setScaleX",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:getBackendTexture",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setScaleX'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_getBackendTexture'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getScaleX(lua_State* tolua_S) +int lua_ax_base_Texture2D_getMaxS(lua_State* tolua_S) { int argc = 0; - ax::Node* cobj = nullptr; + ax::Texture2D* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -4684,15 +4995,15 @@ int lua_ax_base_Node_getScaleX(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getScaleX'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_getMaxS'", nullptr); return 0; } #endif @@ -4702,27 +5013,27 @@ int lua_ax_base_Node_getScaleX(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getScaleX'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_getMaxS'", nullptr); return 0; } - auto&& ret = cobj->getScaleX(); + auto&& ret = cobj->getMaxS(); tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getScaleX",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:getMaxS",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getScaleX'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_getMaxS'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setScaleY(lua_State* tolua_S) +int lua_ax_base_Texture2D_setMaxS(lua_State* tolua_S) { int argc = 0; - ax::Node* cobj = nullptr; + ax::Texture2D* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -4731,15 +5042,15 @@ int lua_ax_base_Node_setScaleY(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setScaleY'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_setMaxS'", nullptr); return 0; } #endif @@ -4749,30 +5060,30 @@ int lua_ax_base_Node_setScaleY(lua_State* tolua_S) { double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Node:setScaleY"); + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Texture2D:setMaxS"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setScaleY'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_setMaxS'", nullptr); return 0; } - cobj->setScaleY(arg0); + cobj->setMaxS(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setScaleY",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:setMaxS",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setScaleY'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_setMaxS'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getScaleY(lua_State* tolua_S) +int lua_ax_base_Texture2D_getMaxT(lua_State* tolua_S) { int argc = 0; - ax::Node* cobj = nullptr; + ax::Texture2D* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -4781,15 +5092,15 @@ int lua_ax_base_Node_getScaleY(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getScaleY'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_getMaxT'", nullptr); return 0; } #endif @@ -4799,27 +5110,27 @@ int lua_ax_base_Node_getScaleY(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getScaleY'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_getMaxT'", nullptr); return 0; } - auto&& ret = cobj->getScaleY(); + auto&& ret = cobj->getMaxT(); tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getScaleY",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:getMaxT",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getScaleY'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_getMaxT'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setScaleZ(lua_State* tolua_S) +int lua_ax_base_Texture2D_setMaxT(lua_State* tolua_S) { int argc = 0; - ax::Node* cobj = nullptr; + ax::Texture2D* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -4828,15 +5139,15 @@ int lua_ax_base_Node_setScaleZ(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setScaleZ'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_setMaxT'", nullptr); return 0; } #endif @@ -4846,30 +5157,30 @@ int lua_ax_base_Node_setScaleZ(lua_State* tolua_S) { double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Node:setScaleZ"); + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Texture2D:setMaxT"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setScaleZ'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_setMaxT'", nullptr); return 0; } - cobj->setScaleZ(arg0); + cobj->setMaxT(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setScaleZ",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:setMaxT",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setScaleZ'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_setMaxT'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getScaleZ(lua_State* tolua_S) +int lua_ax_base_Texture2D_getContentSize(lua_State* tolua_S) { int argc = 0; - ax::Node* cobj = nullptr; + ax::Texture2D* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -4878,15 +5189,15 @@ int lua_ax_base_Node_getScaleZ(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getScaleZ'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_getContentSize'", nullptr); return 0; } #endif @@ -4896,86 +5207,27 @@ int lua_ax_base_Node_getScaleZ(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getScaleZ'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_getContentSize'", nullptr); return 0; } - auto&& ret = cobj->getScaleZ(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + auto&& ret = cobj->getContentSize(); + vec2_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getScaleZ",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getScaleZ'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_base_Node_setScale(lua_State* tolua_S) -{ - int argc = 0; - ax::Node* cobj = nullptr; - bool ok = true; -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; -#endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setScale'", nullptr); - return 0; - } -#endif - argc = lua_gettop(tolua_S)-1; - do{ - if (argc == 2) { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Node:setScale"); - - if (!ok) { break; } - double arg1; - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Node:setScale"); - - if (!ok) { break; } - cobj->setScale(arg0, arg1); - lua_settop(tolua_S, 1); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 1) { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Node:setScale"); - - if (!ok) { break; } - cobj->setScale(arg0); - lua_settop(tolua_S, 1); - return 1; - } - }while(0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setScale",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:getContentSize",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setScale'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_getContentSize'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getScale(lua_State* tolua_S) +int lua_ax_base_Texture2D_getPath(lua_State* tolua_S) { int argc = 0; - ax::Node* cobj = nullptr; + ax::Texture2D* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -4984,15 +5236,15 @@ int lua_ax_base_Node_getScale(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Texture2D*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getScale'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Texture2D_getPath'", nullptr); return 0; } #endif @@ -5002,136 +5254,97 @@ int lua_ax_base_Node_getScale(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getScale'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_getPath'", nullptr); return 0; } - auto&& ret = cobj->getScale(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + auto&& ret = cobj->getPath(); + lua_pushlstring(tolua_S,ret.c_str(),ret.length()); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getScale",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:getPath",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getScale'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_getPath'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setPosition(lua_State* tolua_S) +int lua_ax_base_Texture2D_setDefaultAlphaPixelFormat(lua_State* tolua_S) { int argc = 0; - ax::Node* cobj = nullptr; bool ok = true; + #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; -#endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setPosition'", nullptr); - return 0; - } + if (!tolua_isusertable(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S)-1; - do{ - if (argc == 2) { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Node:setPosition"); - - if (!ok) { break; } - double arg1; - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Node:setPosition"); - if (!ok) { break; } - cobj->setPosition(arg0, arg1); - lua_settop(tolua_S, 1); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 1) { - ax::Vec2 arg0; - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.Node:setPosition"); + argc = lua_gettop(tolua_S) - 1; - if (!ok) { break; } - cobj->setPosition(arg0); - lua_settop(tolua_S, 1); - return 1; + if (argc == 1) + { + ax::backend::PixelFormat arg0; + ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Texture2D:setDefaultAlphaPixelFormat"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_setDefaultAlphaPixelFormat'", nullptr); + return 0; } - }while(0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setPosition",argc, 1); + ax::Texture2D::setDefaultAlphaPixelFormat(arg0); + lua_settop(tolua_S, 1); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Texture2D:setDefaultAlphaPixelFormat",argc, 1); return 0; - #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setPosition'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_setDefaultAlphaPixelFormat'.",&tolua_err); #endif - return 0; } -int lua_ax_base_Node_setPositionNormalized(lua_State* tolua_S) +int lua_ax_base_Texture2D_getDefaultAlphaPixelFormat(lua_State* tolua_S) { int argc = 0; - ax::Node* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.Texture2D",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setPositionNormalized'", nullptr); - return 0; - } -#endif + argc = lua_gettop(tolua_S) - 1; - argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - ax::Vec2 arg0; - - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.Node:setPositionNormalized"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setPositionNormalized'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_getDefaultAlphaPixelFormat'", nullptr); return 0; } - cobj->setPositionNormalized(arg0); - lua_settop(tolua_S, 1); + int ret = (int)ax::Texture2D::getDefaultAlphaPixelFormat(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setPositionNormalized",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Texture2D:getDefaultAlphaPixelFormat",argc, 0); return 0; - #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setPositionNormalized'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_getDefaultAlphaPixelFormat'.",&tolua_err); #endif - return 0; } -int lua_ax_base_Node_setNormalizedPosition(lua_State* tolua_S) +int lua_ax_base_Texture2D_constructor(lua_State* tolua_S) { int argc = 0; - ax::Node* cobj = nullptr; + ax::Texture2D* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -5139,49 +5352,88 @@ int lua_ax_base_Node_setNormalizedPosition(lua_State* tolua_S) #endif -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setNormalizedPosition'", nullptr); - return 0; - } -#endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - ax::Vec2 arg0; - - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.Node:setNormalizedPosition"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setNormalizedPosition'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Texture2D_constructor'", nullptr); return 0; } - cobj->setNormalizedPosition(arg0); - lua_settop(tolua_S, 1); + cobj = new ax::Texture2D(); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.Texture2D"); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setNormalizedPosition",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Texture2D:Texture2D",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setNormalizedPosition'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Texture2D_constructor'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getPositionNormalized(lua_State* tolua_S) + +static int lua_ax_base_Texture2D_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (Texture2D)"); + return 0; +} + +int lua_register_ax_base_Texture2D(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.Texture2D"); + tolua_cclass(tolua_S,"Texture2D","ax.Texture2D","ax.Object",nullptr); + + tolua_beginmodule(tolua_S,"Texture2D"); + tolua_function(tolua_S,"new",lua_ax_base_Texture2D_constructor); + tolua_function(tolua_S,"updateWithImage",lua_ax_base_Texture2D_updateWithImage); + tolua_function(tolua_S,"updateWithMipmaps",lua_ax_base_Texture2D_updateWithMipmaps); + tolua_function(tolua_S,"updateWithSubData",lua_ax_base_Texture2D_updateWithSubData); + tolua_function(tolua_S,"drawAtPoint",lua_ax_base_Texture2D_drawAtPoint); + tolua_function(tolua_S,"drawInRect",lua_ax_base_Texture2D_drawInRect); + tolua_function(tolua_S,"initWithImage",lua_ax_base_Texture2D_initWithImage); + tolua_function(tolua_S,"initWithString",lua_ax_base_Texture2D_initWithString); + tolua_function(tolua_S,"updateTextureDescriptor",lua_ax_base_Texture2D_updateTextureDescriptor); + tolua_function(tolua_S,"setRenderTarget",lua_ax_base_Texture2D_setRenderTarget); + tolua_function(tolua_S,"isRenderTarget",lua_ax_base_Texture2D_isRenderTarget); + tolua_function(tolua_S,"generateMipmap",lua_ax_base_Texture2D_generateMipmap); + tolua_function(tolua_S,"setAntiAliasTexParameters",lua_ax_base_Texture2D_setAntiAliasTexParameters); + tolua_function(tolua_S,"setAliasTexParameters",lua_ax_base_Texture2D_setAliasTexParameters); + tolua_function(tolua_S,"getStringForFormat",lua_ax_base_Texture2D_getStringForFormat); + tolua_function(tolua_S,"getBitsPerPixelForFormat",lua_ax_base_Texture2D_getBitsPerPixelForFormat); + tolua_function(tolua_S,"getContentSizeInPixels",lua_ax_base_Texture2D_getContentSizeInPixels); + tolua_function(tolua_S,"hasPremultipliedAlpha",lua_ax_base_Texture2D_hasPremultipliedAlpha); + tolua_function(tolua_S,"setPremultipliedAlpha",lua_ax_base_Texture2D_setPremultipliedAlpha); + tolua_function(tolua_S,"hasMipmaps",lua_ax_base_Texture2D_hasMipmaps); + tolua_function(tolua_S,"getPixelFormat",lua_ax_base_Texture2D_getPixelFormat); + tolua_function(tolua_S,"getSamplerFlags",lua_ax_base_Texture2D_getSamplerFlags); + tolua_function(tolua_S,"getPixelsWide",lua_ax_base_Texture2D_getPixelsWide); + tolua_function(tolua_S,"getPixelsHigh",lua_ax_base_Texture2D_getPixelsHigh); + tolua_function(tolua_S,"getBackendTexture",lua_ax_base_Texture2D_getBackendTexture); + tolua_function(tolua_S,"getMaxS",lua_ax_base_Texture2D_getMaxS); + tolua_function(tolua_S,"setMaxS",lua_ax_base_Texture2D_setMaxS); + tolua_function(tolua_S,"getMaxT",lua_ax_base_Texture2D_getMaxT); + tolua_function(tolua_S,"setMaxT",lua_ax_base_Texture2D_setMaxT); + tolua_function(tolua_S,"getContentSize",lua_ax_base_Texture2D_getContentSize); + tolua_function(tolua_S,"getPath",lua_ax_base_Texture2D_getPath); + tolua_function(tolua_S,"setDefaultAlphaPixelFormat", lua_ax_base_Texture2D_setDefaultAlphaPixelFormat); + tolua_function(tolua_S,"getDefaultAlphaPixelFormat", lua_ax_base_Texture2D_getDefaultAlphaPixelFormat); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::Texture2D).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.Texture2D"; + g_typeCast[typeName] = "ax.Texture2D"; + return 1; +} + +int lua_ax_base_Component_init(lua_State* tolua_S) { int argc = 0; - ax::Node* cobj = nullptr; + ax::Component* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -5190,15 +5442,15 @@ int lua_ax_base_Node_getPositionNormalized(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Component",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Component*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getPositionNormalized'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Component_init'", nullptr); return 0; } #endif @@ -5208,27 +5460,27 @@ int lua_ax_base_Node_getPositionNormalized(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getPositionNormalized'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Component_init'", nullptr); return 0; } - auto&& ret = cobj->getPositionNormalized(); - vec2_to_luaval(tolua_S, ret); + auto&& ret = cobj->init(); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getPositionNormalized",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Component:init",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getPositionNormalized'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Component_init'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getNormalizedPosition(lua_State* tolua_S) +int lua_ax_base_Component_isEnabled(lua_State* tolua_S) { int argc = 0; - ax::Node* cobj = nullptr; + ax::Component* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -5237,15 +5489,15 @@ int lua_ax_base_Node_getNormalizedPosition(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Component",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Component*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getNormalizedPosition'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Component_isEnabled'", nullptr); return 0; } #endif @@ -5255,27 +5507,27 @@ int lua_ax_base_Node_getNormalizedPosition(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getNormalizedPosition'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Component_isEnabled'", nullptr); return 0; } - auto&& ret = cobj->getNormalizedPosition(); - vec2_to_luaval(tolua_S, ret); + auto&& ret = cobj->isEnabled(); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getNormalizedPosition",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Component:isEnabled",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getNormalizedPosition'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Component_isEnabled'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setPositionX(lua_State* tolua_S) +int lua_ax_base_Component_setEnabled(lua_State* tolua_S) { int argc = 0; - ax::Node* cobj = nullptr; + ax::Component* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -5284,15 +5536,15 @@ int lua_ax_base_Node_setPositionX(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Component",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Component*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setPositionX'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Component_setEnabled'", nullptr); return 0; } #endif @@ -5300,32 +5552,32 @@ int lua_ax_base_Node_setPositionX(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - double arg0; + bool arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Node:setPositionX"); + ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.Component:setEnabled"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setPositionX'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Component_setEnabled'", nullptr); return 0; } - cobj->setPositionX(arg0); + cobj->setEnabled(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setPositionX",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Component:setEnabled",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setPositionX'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Component_setEnabled'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getPositionX(lua_State* tolua_S) +int lua_ax_base_Component_getName(lua_State* tolua_S) { int argc = 0; - ax::Node* cobj = nullptr; + ax::Component* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -5334,15 +5586,15 @@ int lua_ax_base_Node_getPositionX(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Component",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Component*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getPositionX'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Component_getName'", nullptr); return 0; } #endif @@ -5352,27 +5604,27 @@ int lua_ax_base_Node_getPositionX(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getPositionX'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Component_getName'", nullptr); return 0; } - auto&& ret = cobj->getPositionX(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + auto&& ret = cobj->getName(); + lua_pushlstring(tolua_S,ret.data(),ret.length()); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getPositionX",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Component:getName",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getPositionX'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Component_getName'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setPositionY(lua_State* tolua_S) +int lua_ax_base_Component_setName(lua_State* tolua_S) { int argc = 0; - ax::Node* cobj = nullptr; + ax::Component* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -5381,15 +5633,15 @@ int lua_ax_base_Node_setPositionY(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Component",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Component*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setPositionY'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Component_setName'", nullptr); return 0; } #endif @@ -5397,32 +5649,32 @@ int lua_ax_base_Node_setPositionY(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - double arg0; + std::string_view arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Node:setPositionY"); + ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Component:setName"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setPositionY'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Component_setName'", nullptr); return 0; } - cobj->setPositionY(arg0); + cobj->setName(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setPositionY",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Component:setName",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setPositionY'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Component_setName'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getPositionY(lua_State* tolua_S) +int lua_ax_base_Component_getOwner(lua_State* tolua_S) { int argc = 0; - ax::Node* cobj = nullptr; + ax::Component* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -5431,15 +5683,15 @@ int lua_ax_base_Node_getPositionY(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Component",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Component*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getPositionY'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Component_getOwner'", nullptr); return 0; } #endif @@ -5449,27 +5701,27 @@ int lua_ax_base_Node_getPositionY(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getPositionY'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Component_getOwner'", nullptr); return 0; } - auto&& ret = cobj->getPositionY(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + auto&& ret = cobj->getOwner(); + object_to_luaval(tolua_S, "ax.Node",(ax::Node*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getPositionY",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Component:getOwner",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getPositionY'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Component_getOwner'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setPosition3D(lua_State* tolua_S) +int lua_ax_base_Component_setOwner(lua_State* tolua_S) { int argc = 0; - ax::Node* cobj = nullptr; + ax::Component* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -5478,15 +5730,15 @@ int lua_ax_base_Node_setPosition3D(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Component",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Component*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setPosition3D'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Component_setOwner'", nullptr); return 0; } #endif @@ -5494,32 +5746,32 @@ int lua_ax_base_Node_setPosition3D(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Vec3 arg0; + ax::Node* arg0; - ok &= luaval_to_vec3(tolua_S, 2, &arg0, "ax.Node:setPosition3D"); + ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Component:setOwner"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setPosition3D'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Component_setOwner'", nullptr); return 0; } - cobj->setPosition3D(arg0); + cobj->setOwner(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setPosition3D",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Component:setOwner",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setPosition3D'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Component_setOwner'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getPosition3D(lua_State* tolua_S) +int lua_ax_base_Component_update(lua_State* tolua_S) { int argc = 0; - ax::Node* cobj = nullptr; + ax::Component* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -5528,45 +5780,48 @@ int lua_ax_base_Node_getPosition3D(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Component",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Component*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getPosition3D'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Component_update'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + double arg0; + + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Component:update"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getPosition3D'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Component_update'", nullptr); return 0; } - auto&& ret = cobj->getPosition3D(); - vec3_to_luaval(tolua_S, ret); + cobj->update(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getPosition3D",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Component:update",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getPosition3D'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Component_update'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setPositionZ(lua_State* tolua_S) +int lua_ax_base_Component_onAdd(lua_State* tolua_S) { int argc = 0; - ax::Node* cobj = nullptr; + ax::Component* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -5575,48 +5830,45 @@ int lua_ax_base_Node_setPositionZ(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Component",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Component*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setPositionZ'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Component_onAdd'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Node:setPositionZ"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setPositionZ'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Component_onAdd'", nullptr); return 0; } - cobj->setPositionZ(arg0); + cobj->onAdd(); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setPositionZ",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Component:onAdd",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setPositionZ'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Component_onAdd'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getPositionZ(lua_State* tolua_S) +int lua_ax_base_Component_onRemove(lua_State* tolua_S) { int argc = 0; - ax::Node* cobj = nullptr; + ax::Component* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -5625,15 +5877,15 @@ int lua_ax_base_Node_getPositionZ(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Component",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Component*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getPositionZ'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Component_onRemove'", nullptr); return 0; } #endif @@ -5643,74 +5895,88 @@ int lua_ax_base_Node_getPositionZ(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getPositionZ'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Component_onRemove'", nullptr); return 0; } - auto&& ret = cobj->getPositionZ(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + cobj->onRemove(); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getPositionZ",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Component:onRemove",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getPositionZ'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Component_onRemove'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setSkewX(lua_State* tolua_S) +int lua_ax_base_Component_create(lua_State* tolua_S) { int argc = 0; - ax::Node* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.Component",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setSkewX'", nullptr); - return 0; - } -#endif + argc = lua_gettop(tolua_S) - 1; - argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Node:setSkewX"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setSkewX'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Component_create'", nullptr); return 0; } - cobj->setSkewX(arg0); - lua_settop(tolua_S, 1); + auto&& ret = ax::Component::create(); + object_to_luaval(tolua_S, "ax.Component",(ax::Component*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setSkewX",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Component:create",argc, 0); return 0; - #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setSkewX'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Component_create'.",&tolua_err); #endif - return 0; } -int lua_ax_base_Node_getSkewX(lua_State* tolua_S) +static int lua_ax_base_Component_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (Component)"); + return 0; +} + +int lua_register_ax_base_Component(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.Component"); + tolua_cclass(tolua_S,"Component","ax.Component","ax.Object",nullptr); + + tolua_beginmodule(tolua_S,"Component"); + tolua_function(tolua_S,"init",lua_ax_base_Component_init); + tolua_function(tolua_S,"isEnabled",lua_ax_base_Component_isEnabled); + tolua_function(tolua_S,"setEnabled",lua_ax_base_Component_setEnabled); + tolua_function(tolua_S,"getName",lua_ax_base_Component_getName); + tolua_function(tolua_S,"setName",lua_ax_base_Component_setName); + tolua_function(tolua_S,"getOwner",lua_ax_base_Component_getOwner); + tolua_function(tolua_S,"setOwner",lua_ax_base_Component_setOwner); + tolua_function(tolua_S,"update",lua_ax_base_Component_update); + tolua_function(tolua_S,"onAdd",lua_ax_base_Component_onAdd); + tolua_function(tolua_S,"onRemove",lua_ax_base_Component_onRemove); + tolua_function(tolua_S,"create", lua_ax_base_Component_create); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::Component).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.Component"; + g_typeCast[typeName] = "ax.Component"; + return 1; +} + +int lua_ax_base_Node_getDescription(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -5730,7 +5996,7 @@ int lua_ax_base_Node_getSkewX(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getSkewX'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getDescription'", nullptr); return 0; } #endif @@ -5740,24 +6006,24 @@ int lua_ax_base_Node_getSkewX(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getSkewX'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getDescription'", nullptr); return 0; } - auto&& ret = cobj->getSkewX(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + auto&& ret = cobj->getDescription(); + lua_pushlstring(tolua_S,ret.c_str(),ret.length()); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getSkewX",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getDescription",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getSkewX'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getDescription'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setSkewY(lua_State* tolua_S) +int lua_ax_base_Node_setLocalZOrder(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -5777,7 +6043,7 @@ int lua_ax_base_Node_setSkewY(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setSkewY'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setLocalZOrder'", nullptr); return 0; } #endif @@ -5785,29 +6051,29 @@ int lua_ax_base_Node_setSkewY(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - double arg0; + int arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Node:setSkewY"); + ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Node:setLocalZOrder"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setSkewY'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setLocalZOrder'", nullptr); return 0; } - cobj->setSkewY(arg0); + cobj->setLocalZOrder(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setSkewY",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setLocalZOrder",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setSkewY'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setLocalZOrder'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getSkewY(lua_State* tolua_S) +int lua_ax_base_Node_updateOrderOfArrival(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -5827,7 +6093,7 @@ int lua_ax_base_Node_getSkewY(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getSkewY'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_updateOrderOfArrival'", nullptr); return 0; } #endif @@ -5837,24 +6103,24 @@ int lua_ax_base_Node_getSkewY(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getSkewY'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_updateOrderOfArrival'", nullptr); return 0; } - auto&& ret = cobj->getSkewY(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + cobj->updateOrderOfArrival(); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getSkewY",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:updateOrderOfArrival",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getSkewY'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_updateOrderOfArrival'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getAnchorPoint(lua_State* tolua_S) +int lua_ax_base_Node_getLocalZOrder(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -5874,7 +6140,7 @@ int lua_ax_base_Node_getAnchorPoint(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getAnchorPoint'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getLocalZOrder'", nullptr); return 0; } #endif @@ -5884,24 +6150,24 @@ int lua_ax_base_Node_getAnchorPoint(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getAnchorPoint'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getLocalZOrder'", nullptr); return 0; } - auto&& ret = cobj->getAnchorPoint(); - vec2_to_luaval(tolua_S, ret); + auto&& ret = cobj->getLocalZOrder(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getAnchorPoint",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getLocalZOrder",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getAnchorPoint'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getLocalZOrder'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getAnchorPointInPoints(lua_State* tolua_S) +int lua_ax_base_Node_setGlobalZOrder(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -5921,34 +6187,37 @@ int lua_ax_base_Node_getAnchorPointInPoints(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getAnchorPointInPoints'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setGlobalZOrder'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + double arg0; + + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Node:setGlobalZOrder"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getAnchorPointInPoints'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setGlobalZOrder'", nullptr); return 0; } - auto&& ret = cobj->getAnchorPointInPoints(); - vec2_to_luaval(tolua_S, ret); + cobj->setGlobalZOrder(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getAnchorPointInPoints",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setGlobalZOrder",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getAnchorPointInPoints'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setGlobalZOrder'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getContentSize(lua_State* tolua_S) +int lua_ax_base_Node_getGlobalZOrder(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -5968,7 +6237,7 @@ int lua_ax_base_Node_getContentSize(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getContentSize'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getGlobalZOrder'", nullptr); return 0; } #endif @@ -5978,24 +6247,24 @@ int lua_ax_base_Node_getContentSize(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getContentSize'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getGlobalZOrder'", nullptr); return 0; } - auto&& ret = cobj->getContentSize(); - vec2_to_luaval(tolua_S, ret); + auto&& ret = cobj->getGlobalZOrder(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getContentSize",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getGlobalZOrder",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getContentSize'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getGlobalZOrder'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_hitTest(lua_State* tolua_S) +int lua_ax_base_Node_setScaleX(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -6015,7 +6284,7 @@ int lua_ax_base_Node_hitTest(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_hitTest'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setScaleX'", nullptr); return 0; } #endif @@ -6023,29 +6292,29 @@ int lua_ax_base_Node_hitTest(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Vec2 arg0; + double arg0; - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.Node:hitTest"); + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Node:setScaleX"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_hitTest'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setScaleX'", nullptr); return 0; } - auto&& ret = cobj->hitTest(arg0); - tolua_pushboolean(tolua_S,(bool)ret); + cobj->setScaleX(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:hitTest",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setScaleX",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_hitTest'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setScaleX'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setVisible(lua_State* tolua_S) +int lua_ax_base_Node_getScaleX(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -6065,37 +6334,34 @@ int lua_ax_base_Node_setVisible(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setVisible'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getScaleX'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - bool arg0; - - ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.Node:setVisible"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setVisible'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getScaleX'", nullptr); return 0; } - cobj->setVisible(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getScaleX(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setVisible",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getScaleX",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setVisible'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getScaleX'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_isVisible(lua_State* tolua_S) +int lua_ax_base_Node_setScaleY(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -6115,34 +6381,37 @@ int lua_ax_base_Node_isVisible(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_isVisible'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setScaleY'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + double arg0; + + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Node:setScaleY"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_isVisible'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setScaleY'", nullptr); return 0; } - auto&& ret = cobj->isVisible(); - tolua_pushboolean(tolua_S,(bool)ret); + cobj->setScaleY(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:isVisible",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setScaleY",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_isVisible'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setScaleY'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setRotation(lua_State* tolua_S) +int lua_ax_base_Node_getScaleY(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -6162,37 +6431,34 @@ int lua_ax_base_Node_setRotation(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setRotation'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getScaleY'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Node:setRotation"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setRotation'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getScaleY'", nullptr); return 0; } - cobj->setRotation(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getScaleY(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setRotation",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getScaleY",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setRotation'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getScaleY'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getRotation(lua_State* tolua_S) +int lua_ax_base_Node_setScaleZ(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -6212,34 +6478,37 @@ int lua_ax_base_Node_getRotation(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getRotation'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setScaleZ'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + double arg0; + + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Node:setScaleZ"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getRotation'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setScaleZ'", nullptr); return 0; } - auto&& ret = cobj->getRotation(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + cobj->setScaleZ(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getRotation",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setScaleZ",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getRotation'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setScaleZ'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setRotation3D(lua_State* tolua_S) +int lua_ax_base_Node_getScaleZ(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -6259,84 +6528,93 @@ int lua_ax_base_Node_setRotation3D(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setRotation3D'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getScaleZ'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - ax::Vec3 arg0; - - ok &= luaval_to_vec3(tolua_S, 2, &arg0, "ax.Node:setRotation3D"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setRotation3D'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getScaleZ'", nullptr); return 0; } - cobj->setRotation3D(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getScaleZ(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setRotation3D",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getScaleZ",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setRotation3D'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getScaleZ'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getRotation3D(lua_State* tolua_S) +int lua_ax_base_Node_setScale(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; bool ok = true; - #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); - #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getRotation3D'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setScale'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getRotation3D'", nullptr); - return 0; + do{ + if (argc == 2) { + double arg0; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Node:setScale"); + + if (!ok) { break; } + double arg1; + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Node:setScale"); + + if (!ok) { break; } + cobj->setScale(arg0, arg1); + lua_settop(tolua_S, 1); + return 1; } - auto&& ret = cobj->getRotation3D(); - vec3_to_luaval(tolua_S, ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getRotation3D",argc, 0); + }while(0); + ok = true; + do{ + if (argc == 1) { + double arg0; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Node:setScale"); + + if (!ok) { break; } + cobj->setScale(arg0); + lua_settop(tolua_S, 1); + return 1; + } + }while(0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setScale",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getRotation3D'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setScale'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setRotationSkewX(lua_State* tolua_S) +int lua_ax_base_Node_getScale(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -6356,84 +6634,93 @@ int lua_ax_base_Node_setRotationSkewX(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setRotationSkewX'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getScale'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Node:setRotationSkewX"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setRotationSkewX'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getScale'", nullptr); return 0; } - cobj->setRotationSkewX(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getScale(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setRotationSkewX",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getScale",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setRotationSkewX'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getScale'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getRotationSkewX(lua_State* tolua_S) +int lua_ax_base_Node_setPosition(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; bool ok = true; - #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); - #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getRotationSkewX'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setPosition'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getRotationSkewX'", nullptr); - return 0; + do{ + if (argc == 2) { + double arg0; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Node:setPosition"); + + if (!ok) { break; } + double arg1; + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Node:setPosition"); + + if (!ok) { break; } + cobj->setPosition(arg0, arg1); + lua_settop(tolua_S, 1); + return 1; } - auto&& ret = cobj->getRotationSkewX(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getRotationSkewX",argc, 0); + }while(0); + ok = true; + do{ + if (argc == 1) { + ax::Vec2 arg0; + ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.Node:setPosition"); + + if (!ok) { break; } + cobj->setPosition(arg0); + lua_settop(tolua_S, 1); + return 1; + } + }while(0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setPosition",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getRotationSkewX'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setPosition'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setRotationSkewY(lua_State* tolua_S) +int lua_ax_base_Node_setPositionNormalized(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -6453,7 +6740,7 @@ int lua_ax_base_Node_setRotationSkewY(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setRotationSkewY'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setPositionNormalized'", nullptr); return 0; } #endif @@ -6461,29 +6748,29 @@ int lua_ax_base_Node_setRotationSkewY(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - double arg0; + ax::Vec2 arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Node:setRotationSkewY"); + ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.Node:setPositionNormalized"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setRotationSkewY'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setPositionNormalized'", nullptr); return 0; } - cobj->setRotationSkewY(arg0); + cobj->setPositionNormalized(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setRotationSkewY",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setPositionNormalized",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setRotationSkewY'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setPositionNormalized'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getRotationSkewY(lua_State* tolua_S) +int lua_ax_base_Node_setNormalizedPosition(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -6503,34 +6790,37 @@ int lua_ax_base_Node_getRotationSkewY(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getRotationSkewY'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setNormalizedPosition'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + ax::Vec2 arg0; + + ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.Node:setNormalizedPosition"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getRotationSkewY'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setNormalizedPosition'", nullptr); return 0; } - auto&& ret = cobj->getRotationSkewY(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + cobj->setNormalizedPosition(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getRotationSkewY",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setNormalizedPosition",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getRotationSkewY'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setNormalizedPosition'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setIgnoreAnchorPointForPosition(lua_State* tolua_S) +int lua_ax_base_Node_getPositionNormalized(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -6550,37 +6840,34 @@ int lua_ax_base_Node_setIgnoreAnchorPointForPosition(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setIgnoreAnchorPointForPosition'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getPositionNormalized'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - bool arg0; - - ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.Node:setIgnoreAnchorPointForPosition"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setIgnoreAnchorPointForPosition'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getPositionNormalized'", nullptr); return 0; } - cobj->setIgnoreAnchorPointForPosition(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getPositionNormalized(); + vec2_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setIgnoreAnchorPointForPosition",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getPositionNormalized",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setIgnoreAnchorPointForPosition'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getPositionNormalized'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_isIgnoreAnchorPointForPosition(lua_State* tolua_S) +int lua_ax_base_Node_getNormalizedPosition(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -6600,7 +6887,7 @@ int lua_ax_base_Node_isIgnoreAnchorPointForPosition(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_isIgnoreAnchorPointForPosition'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getNormalizedPosition'", nullptr); return 0; } #endif @@ -6610,123 +6897,74 @@ int lua_ax_base_Node_isIgnoreAnchorPointForPosition(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_isIgnoreAnchorPointForPosition'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getNormalizedPosition'", nullptr); return 0; } - auto&& ret = cobj->isIgnoreAnchorPointForPosition(); - tolua_pushboolean(tolua_S,(bool)ret); + auto&& ret = cobj->getNormalizedPosition(); + vec2_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:isIgnoreAnchorPointForPosition",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getNormalizedPosition",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_isIgnoreAnchorPointForPosition'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getNormalizedPosition'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_addChild(lua_State* tolua_S) +int lua_ax_base_Node_setPositionX(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; bool ok = true; + #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif + cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); + #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_addChild'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setPositionX'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - do{ - if (argc == 2) { - ax::Node* arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Node:addChild"); - - if (!ok) { break; } - int arg1; - ok &= luaval_to_int32(tolua_S, 3,(int *)&arg1, "ax.Node:addChild"); - - if (!ok) { break; } - cobj->addChild(arg0, arg1); - lua_settop(tolua_S, 1); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 1) { - ax::Node* arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Node:addChild"); - - if (!ok) { break; } - cobj->addChild(arg0); - lua_settop(tolua_S, 1); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 3) { - ax::Node* arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Node:addChild"); - if (!ok) { break; } - int arg1; - ok &= luaval_to_int32(tolua_S, 3,(int *)&arg1, "ax.Node:addChild"); - - if (!ok) { break; } - int arg2; - ok &= luaval_to_int32(tolua_S, 4,(int *)&arg2, "ax.Node:addChild"); - - if (!ok) { break; } - cobj->addChild(arg0, arg1, arg2); - lua_settop(tolua_S, 1); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 3) { - ax::Node* arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Node:addChild"); - - if (!ok) { break; } - int arg1; - ok &= luaval_to_int32(tolua_S, 3,(int *)&arg1, "ax.Node:addChild"); - - if (!ok) { break; } - std::string_view arg2; - ok &= luaval_to_std_string_view(tolua_S, 4,&arg2, "ax.Node:addChild"); + argc = lua_gettop(tolua_S)-1; + if (argc == 1) + { + double arg0; - if (!ok) { break; } - cobj->addChild(arg0, arg1, arg2); - lua_settop(tolua_S, 1); - return 1; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Node:setPositionX"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setPositionX'", nullptr); + return 0; } - }while(0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:addChild",argc, 3); + cobj->setPositionX(arg0); + lua_settop(tolua_S, 1); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setPositionX",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_addChild'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setPositionX'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getChildByTag(lua_State* tolua_S) +int lua_ax_base_Node_getPositionX(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -6746,37 +6984,34 @@ int lua_ax_base_Node_getChildByTag(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getChildByTag'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getPositionX'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - int arg0; - - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Node:getChildByTag"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getChildByTag'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getPositionX'", nullptr); return 0; } - auto&& ret = cobj->getChildByTag(arg0); - object_to_luaval(tolua_S, "ax.Node",(ax::Node*)ret); + auto&& ret = cobj->getPositionX(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getChildByTag",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getPositionX",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getChildByTag'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getPositionX'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getChildByName(lua_State* tolua_S) +int lua_ax_base_Node_setPositionY(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -6796,7 +7031,7 @@ int lua_ax_base_Node_getChildByName(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getChildByName'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setPositionY'", nullptr); return 0; } #endif @@ -6804,76 +7039,76 @@ int lua_ax_base_Node_getChildByName(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - std::string_view arg0; + double arg0; - ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Node:getChildByName"); + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Node:setPositionY"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getChildByName'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setPositionY'", nullptr); return 0; } - auto&& ret = cobj->getChildByName(arg0); - object_to_luaval(tolua_S, "ax.Node",(ax::Node*)ret); + cobj->setPositionY(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getChildByName",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setPositionY",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getChildByName'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setPositionY'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getChildren(lua_State* tolua_S) +int lua_ax_base_Node_getPositionY(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; bool ok = true; + #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif + cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); + #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getChildren'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getPositionY'", nullptr); return 0; } #endif + argc = lua_gettop(tolua_S)-1; - do{ - if (argc == 0) { - const ax::Vector& ret = cobj->getChildren(); - ccvector_to_luaval(tolua_S, ret); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 0) { - ax::Vector& ret = cobj->getChildren(); - ccvector_to_luaval(tolua_S, ret); - return 1; + if (argc == 0) + { + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getPositionY'", nullptr); + return 0; } - }while(0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getChildren",argc, 0); + auto&& ret = cobj->getPositionY(); + tolua_pushnumber(tolua_S,(lua_Number)ret); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getPositionY",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getChildren'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getPositionY'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getChildrenCount(lua_State* tolua_S) +int lua_ax_base_Node_setPosition3D(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -6893,34 +7128,37 @@ int lua_ax_base_Node_getChildrenCount(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getChildrenCount'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setPosition3D'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + ax::Vec3 arg0; + + ok &= luaval_to_vec3(tolua_S, 2, &arg0, "ax.Node:setPosition3D"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getChildrenCount'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setPosition3D'", nullptr); return 0; } - auto&& ret = cobj->getChildrenCount(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + cobj->setPosition3D(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getChildrenCount",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setPosition3D",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getChildrenCount'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setPosition3D'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setParent(lua_State* tolua_S) +int lua_ax_base_Node_getPosition3D(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -6940,135 +7178,131 @@ int lua_ax_base_Node_setParent(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setParent'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getPosition3D'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - ax::Node* arg0; - - ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Node:setParent"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setParent'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getPosition3D'", nullptr); return 0; } - cobj->setParent(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getPosition3D(); + vec3_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setParent",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getPosition3D",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setParent'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getPosition3D'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getParent(lua_State* tolua_S) +int lua_ax_base_Node_setPositionZ(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; bool ok = true; + #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif + cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); + #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getParent'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setPositionZ'", nullptr); return 0; } #endif + argc = lua_gettop(tolua_S)-1; - do{ - if (argc == 0) { - const ax::Node* ret = cobj->getParent(); - object_to_luaval(tolua_S, "ax.Node",(ax::Node*)ret); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 0) { - ax::Node* ret = cobj->getParent(); - object_to_luaval(tolua_S, "ax.Node",(ax::Node*)ret); - return 1; + if (argc == 1) + { + double arg0; + + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Node:setPositionZ"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setPositionZ'", nullptr); + return 0; } - }while(0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getParent",argc, 0); + cobj->setPositionZ(arg0); + lua_settop(tolua_S, 1); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setPositionZ",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getParent'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setPositionZ'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_removeFromParentAndCleanup(lua_State* tolua_S) +int lua_ax_base_Node_getPositionZ(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; bool ok = true; + #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif + cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); + #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_removeFromParentAndCleanup'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getPositionZ'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - do{ - if (argc == 1) { - bool arg0; - ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.Node:removeFromParentAndCleanup"); - if (!ok) { break; } - cobj->removeFromParentAndCleanup(arg0); - lua_settop(tolua_S, 1); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 0) { - cobj->removeFromParent(); - lua_settop(tolua_S, 1); - return 1; + argc = lua_gettop(tolua_S)-1; + if (argc == 0) + { + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getPositionZ'", nullptr); + return 0; } - }while(0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:removeFromParent",argc, 0); + auto&& ret = cobj->getPositionZ(); + tolua_pushnumber(tolua_S,(lua_Number)ret); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getPositionZ",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_removeFromParentAndCleanup'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getPositionZ'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_removeChild(lua_State* tolua_S) +int lua_ax_base_Node_setSkewX(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -7088,7 +7322,7 @@ int lua_ax_base_Node_removeChild(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_removeChild'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setSkewX'", nullptr); return 0; } #endif @@ -7096,46 +7330,29 @@ int lua_ax_base_Node_removeChild(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Node* arg0; - - ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Node:removeChild"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_removeChild'", nullptr); - return 0; - } - cobj->removeChild(arg0); - lua_settop(tolua_S, 1); - return 1; - } - if (argc == 2) - { - ax::Node* arg0; - bool arg1; - - ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Node:removeChild"); + double arg0; - ok &= luaval_to_boolean(tolua_S, 3,&arg1, "ax.Node:removeChild"); + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Node:setSkewX"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_removeChild'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setSkewX'", nullptr); return 0; } - cobj->removeChild(arg0, arg1); + cobj->setSkewX(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:removeChild",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setSkewX",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_removeChild'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setSkewX'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_removeChildByTag(lua_State* tolua_S) +int lua_ax_base_Node_getSkewX(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -7155,54 +7372,34 @@ int lua_ax_base_Node_removeChildByTag(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_removeChildByTag'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getSkewX'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - int arg0; - - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Node:removeChildByTag"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_removeChildByTag'", nullptr); - return 0; - } - cobj->removeChildByTag(arg0); - lua_settop(tolua_S, 1); - return 1; - } - if (argc == 2) + if (argc == 0) { - int arg0; - bool arg1; - - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Node:removeChildByTag"); - - ok &= luaval_to_boolean(tolua_S, 3,&arg1, "ax.Node:removeChildByTag"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_removeChildByTag'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getSkewX'", nullptr); return 0; } - cobj->removeChildByTag(arg0, arg1); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getSkewX(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:removeChildByTag",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getSkewX",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_removeChildByTag'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getSkewX'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_removeChildByName(lua_State* tolua_S) +int lua_ax_base_Node_setSkewY(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -7222,7 +7419,7 @@ int lua_ax_base_Node_removeChildByName(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_removeChildByName'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setSkewY'", nullptr); return 0; } #endif @@ -7230,97 +7427,76 @@ int lua_ax_base_Node_removeChildByName(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - std::string_view arg0; - - ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Node:removeChildByName"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_removeChildByName'", nullptr); - return 0; - } - cobj->removeChildByName(arg0); - lua_settop(tolua_S, 1); - return 1; - } - if (argc == 2) - { - std::string_view arg0; - bool arg1; - - ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Node:removeChildByName"); + double arg0; - ok &= luaval_to_boolean(tolua_S, 3,&arg1, "ax.Node:removeChildByName"); + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Node:setSkewY"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_removeChildByName'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setSkewY'", nullptr); return 0; } - cobj->removeChildByName(arg0, arg1); + cobj->setSkewY(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:removeChildByName",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setSkewY",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_removeChildByName'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setSkewY'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_removeAllChildrenWithCleanup(lua_State* tolua_S) +int lua_ax_base_Node_getSkewY(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; bool ok = true; + #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif + cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); + #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_removeAllChildrenWithCleanup'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getSkewY'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - do{ - if (argc == 1) { - bool arg0; - ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.Node:removeAllChildrenWithCleanup"); - if (!ok) { break; } - cobj->removeAllChildrenWithCleanup(arg0); - lua_settop(tolua_S, 1); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 0) { - cobj->removeAllChildren(); - lua_settop(tolua_S, 1); - return 1; + argc = lua_gettop(tolua_S)-1; + if (argc == 0) + { + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getSkewY'", nullptr); + return 0; } - }while(0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:removeAllChildren",argc, 0); + auto&& ret = cobj->getSkewY(); + tolua_pushnumber(tolua_S,(lua_Number)ret); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getSkewY",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_removeAllChildrenWithCleanup'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getSkewY'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_reorderChild(lua_State* tolua_S) +int lua_ax_base_Node_getAnchorPoint(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -7340,40 +7516,34 @@ int lua_ax_base_Node_reorderChild(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_reorderChild'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getAnchorPoint'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 2) + if (argc == 0) { - ax::Node* arg0; - int arg1; - - ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Node:reorderChild"); - - ok &= luaval_to_int32(tolua_S, 3,(int *)&arg1, "ax.Node:reorderChild"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_reorderChild'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getAnchorPoint'", nullptr); return 0; } - cobj->reorderChild(arg0, arg1); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getAnchorPoint(); + vec2_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:reorderChild",argc, 2); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getAnchorPoint",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_reorderChild'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getAnchorPoint'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_sortAllChildren(lua_State* tolua_S) +int lua_ax_base_Node_getAnchorPointInPoints(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -7393,7 +7563,7 @@ int lua_ax_base_Node_sortAllChildren(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_sortAllChildren'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getAnchorPointInPoints'", nullptr); return 0; } #endif @@ -7403,24 +7573,24 @@ int lua_ax_base_Node_sortAllChildren(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_sortAllChildren'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getAnchorPointInPoints'", nullptr); return 0; } - cobj->sortAllChildren(); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getAnchorPointInPoints(); + vec2_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:sortAllChildren",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getAnchorPointInPoints",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_sortAllChildren'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getAnchorPointInPoints'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getTag(lua_State* tolua_S) +int lua_ax_base_Node_getContentSize(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -7440,7 +7610,7 @@ int lua_ax_base_Node_getTag(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getTag'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getContentSize'", nullptr); return 0; } #endif @@ -7450,24 +7620,24 @@ int lua_ax_base_Node_getTag(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getTag'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getContentSize'", nullptr); return 0; } - auto&& ret = cobj->getTag(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + auto&& ret = cobj->getContentSize(); + vec2_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getTag",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getContentSize",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getTag'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getContentSize'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setTag(lua_State* tolua_S) +int lua_ax_base_Node_hitTest(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -7487,7 +7657,7 @@ int lua_ax_base_Node_setTag(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setTag'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_hitTest'", nullptr); return 0; } #endif @@ -7495,29 +7665,29 @@ int lua_ax_base_Node_setTag(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - int arg0; + ax::Vec2 arg0; - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Node:setTag"); + ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.Node:hitTest"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setTag'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_hitTest'", nullptr); return 0; } - cobj->setTag(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->hitTest(arg0); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setTag",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:hitTest",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setTag'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_hitTest'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getName(lua_State* tolua_S) +int lua_ax_base_Node_setVisible(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -7537,34 +7707,37 @@ int lua_ax_base_Node_getName(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getName'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setVisible'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + bool arg0; + + ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.Node:setVisible"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getName'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setVisible'", nullptr); return 0; } - auto&& ret = cobj->getName(); - lua_pushlstring(tolua_S,ret.data(),ret.length()); + cobj->setVisible(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getName",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setVisible",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getName'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setVisible'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setName(lua_State* tolua_S) +int lua_ax_base_Node_isVisible(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -7584,37 +7757,34 @@ int lua_ax_base_Node_setName(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setName'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_isVisible'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - std::string_view arg0; - - ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Node:setName"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setName'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_isVisible'", nullptr); return 0; } - cobj->setName(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->isVisible(); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setName",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:isVisible",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setName'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_isVisible'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setUserObject(lua_State* tolua_S) +int lua_ax_base_Node_setRotation(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -7634,7 +7804,7 @@ int lua_ax_base_Node_setUserObject(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setUserObject'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setRotation'", nullptr); return 0; } #endif @@ -7642,29 +7812,29 @@ int lua_ax_base_Node_setUserObject(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Object* arg0; + double arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.Object",&arg0, "ax.Node:setUserObject"); + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Node:setRotation"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setUserObject'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setRotation'", nullptr); return 0; } - cobj->setUserObject(arg0); + cobj->setRotation(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setUserObject",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setRotation",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setUserObject'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setRotation'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_isRunning(lua_State* tolua_S) +int lua_ax_base_Node_getRotation(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -7684,7 +7854,7 @@ int lua_ax_base_Node_isRunning(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_isRunning'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getRotation'", nullptr); return 0; } #endif @@ -7694,24 +7864,24 @@ int lua_ax_base_Node_isRunning(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_isRunning'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getRotation'", nullptr); return 0; } - auto&& ret = cobj->isRunning(); - tolua_pushboolean(tolua_S,(bool)ret); + auto&& ret = cobj->getRotation(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:isRunning",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getRotation",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_isRunning'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getRotation'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_cleanup(lua_State* tolua_S) +int lua_ax_base_Node_setRotation3D(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -7731,152 +7901,134 @@ int lua_ax_base_Node_cleanup(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_cleanup'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setRotation3D'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + ax::Vec3 arg0; + + ok &= luaval_to_vec3(tolua_S, 2, &arg0, "ax.Node:setRotation3D"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_cleanup'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setRotation3D'", nullptr); return 0; } - cobj->cleanup(); + cobj->setRotation3D(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:cleanup",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setRotation3D",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_cleanup'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setRotation3D'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_draw(lua_State* tolua_S) +int lua_ax_base_Node_getRotation3D(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; bool ok = true; + #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif + cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); + #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_draw'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getRotation3D'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - do{ - if (argc == 0) { - cobj->draw(); - lua_settop(tolua_S, 1); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 3) { - ax::Renderer* arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.Renderer",&arg0, "ax.Node:draw"); - - if (!ok) { break; } - ax::Mat4 arg1; - ok &= luaval_to_mat4(tolua_S, 3, &arg1, "ax.Node:draw"); - - if (!ok) { break; } - unsigned int arg2; - ok &= luaval_to_uint32(tolua_S, 4,&arg2, "ax.Node:draw"); - if (!ok) { break; } - cobj->draw(arg0, arg1, arg2); - lua_settop(tolua_S, 1); - return 1; + argc = lua_gettop(tolua_S)-1; + if (argc == 0) + { + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getRotation3D'", nullptr); + return 0; } - }while(0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:draw",argc, 3); + auto&& ret = cobj->getRotation3D(); + vec3_to_luaval(tolua_S, ret); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getRotation3D",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_draw'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getRotation3D'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_visit(lua_State* tolua_S) +int lua_ax_base_Node_setRotationSkewX(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; bool ok = true; + #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif + cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); + #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_visit'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setRotationSkewX'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - do{ - if (argc == 0) { - cobj->visit(); - lua_settop(tolua_S, 1); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 3) { - ax::Renderer* arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.Renderer",&arg0, "ax.Node:visit"); - - if (!ok) { break; } - ax::Mat4 arg1; - ok &= luaval_to_mat4(tolua_S, 3, &arg1, "ax.Node:visit"); - if (!ok) { break; } - unsigned int arg2; - ok &= luaval_to_uint32(tolua_S, 4,&arg2, "ax.Node:visit"); + argc = lua_gettop(tolua_S)-1; + if (argc == 1) + { + double arg0; - if (!ok) { break; } - cobj->visit(arg0, arg1, arg2); - lua_settop(tolua_S, 1); - return 1; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Node:setRotationSkewX"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setRotationSkewX'", nullptr); + return 0; } - }while(0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:visit",argc, 3); + cobj->setRotationSkewX(arg0); + lua_settop(tolua_S, 1); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setRotationSkewX",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_visit'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setRotationSkewX'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getScene(lua_State* tolua_S) +int lua_ax_base_Node_getRotationSkewX(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -7896,7 +8048,7 @@ int lua_ax_base_Node_getScene(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getScene'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getRotationSkewX'", nullptr); return 0; } #endif @@ -7906,24 +8058,24 @@ int lua_ax_base_Node_getScene(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getScene'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getRotationSkewX'", nullptr); return 0; } - auto&& ret = cobj->getScene(); - object_to_luaval(tolua_S, "ax.Scene",(ax::Scene*)ret); + auto&& ret = cobj->getRotationSkewX(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getScene",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getRotationSkewX",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getScene'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getRotationSkewX'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getBoundingBox(lua_State* tolua_S) +int lua_ax_base_Node_setRotationSkewY(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -7943,34 +8095,37 @@ int lua_ax_base_Node_getBoundingBox(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getBoundingBox'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setRotationSkewY'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + double arg0; + + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Node:setRotationSkewY"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getBoundingBox'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setRotationSkewY'", nullptr); return 0; } - auto&& ret = cobj->getBoundingBox(); - rect_to_luaval(tolua_S, ret); + cobj->setRotationSkewY(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getBoundingBox",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setRotationSkewY",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getBoundingBox'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setRotationSkewY'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setEventDispatcher(lua_State* tolua_S) +int lua_ax_base_Node_getRotationSkewY(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -7990,37 +8145,34 @@ int lua_ax_base_Node_setEventDispatcher(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setEventDispatcher'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getRotationSkewY'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - ax::EventDispatcher* arg0; - - ok &= luaval_to_object(tolua_S, 2, "ax.EventDispatcher",&arg0, "ax.Node:setEventDispatcher"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setEventDispatcher'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getRotationSkewY'", nullptr); return 0; } - cobj->setEventDispatcher(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getRotationSkewY(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setEventDispatcher",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getRotationSkewY",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setEventDispatcher'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getRotationSkewY'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getEventDispatcher(lua_State* tolua_S) +int lua_ax_base_Node_setIgnoreAnchorPointForPosition(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -8040,34 +8192,37 @@ int lua_ax_base_Node_getEventDispatcher(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getEventDispatcher'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setIgnoreAnchorPointForPosition'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + bool arg0; + + ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.Node:setIgnoreAnchorPointForPosition"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getEventDispatcher'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setIgnoreAnchorPointForPosition'", nullptr); return 0; } - auto&& ret = cobj->getEventDispatcher(); - object_to_luaval(tolua_S, "ax.EventDispatcher",(ax::EventDispatcher*)ret); + cobj->setIgnoreAnchorPointForPosition(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getEventDispatcher",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setIgnoreAnchorPointForPosition",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getEventDispatcher'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setIgnoreAnchorPointForPosition'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setActionManager(lua_State* tolua_S) +int lua_ax_base_Node_isIgnoreAnchorPointForPosition(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -8087,37 +8242,34 @@ int lua_ax_base_Node_setActionManager(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setActionManager'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_isIgnoreAnchorPointForPosition'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - ax::ActionManager* arg0; - - ok &= luaval_to_object(tolua_S, 2, "ax.ActionManager",&arg0, "ax.Node:setActionManager"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setActionManager'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_isIgnoreAnchorPointForPosition'", nullptr); return 0; } - cobj->setActionManager(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->isIgnoreAnchorPointForPosition(); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setActionManager",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:isIgnoreAnchorPointForPosition",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setActionManager'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_isIgnoreAnchorPointForPosition'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getActionManager(lua_State* tolua_S) +int lua_ax_base_Node_addChild(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -8133,38 +8285,90 @@ int lua_ax_base_Node_getActionManager(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getActionManager'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_addChild'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; do{ - if (argc == 0) { - const ax::ActionManager* ret = cobj->getActionManager(); - object_to_luaval(tolua_S, "ax.ActionManager",(ax::ActionManager*)ret); + if (argc == 2) { + ax::Node* arg0; + ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Node:addChild"); + + if (!ok) { break; } + int arg1; + ok &= luaval_to_int32(tolua_S, 3,(int *)&arg1, "ax.Node:addChild"); + + if (!ok) { break; } + cobj->addChild(arg0, arg1); + lua_settop(tolua_S, 1); return 1; } }while(0); ok = true; do{ - if (argc == 0) { - ax::ActionManager* ret = cobj->getActionManager(); - object_to_luaval(tolua_S, "ax.ActionManager",(ax::ActionManager*)ret); - return 1; + if (argc == 1) { + ax::Node* arg0; + ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Node:addChild"); + + if (!ok) { break; } + cobj->addChild(arg0); + lua_settop(tolua_S, 1); + return 1; } }while(0); ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getActionManager",argc, 0); + do{ + if (argc == 3) { + ax::Node* arg0; + ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Node:addChild"); + + if (!ok) { break; } + int arg1; + ok &= luaval_to_int32(tolua_S, 3,(int *)&arg1, "ax.Node:addChild"); + + if (!ok) { break; } + int arg2; + ok &= luaval_to_int32(tolua_S, 4,(int *)&arg2, "ax.Node:addChild"); + + if (!ok) { break; } + cobj->addChild(arg0, arg1, arg2); + lua_settop(tolua_S, 1); + return 1; + } + }while(0); + ok = true; + do{ + if (argc == 3) { + ax::Node* arg0; + ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Node:addChild"); + + if (!ok) { break; } + int arg1; + ok &= luaval_to_int32(tolua_S, 3,(int *)&arg1, "ax.Node:addChild"); + + if (!ok) { break; } + std::string_view arg2; + ok &= luaval_to_std_string_view(tolua_S, 4,&arg2, "ax.Node:addChild"); + + if (!ok) { break; } + cobj->addChild(arg0, arg1, arg2); + lua_settop(tolua_S, 1); + return 1; + } + }while(0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:addChild",argc, 3); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getActionManager'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_addChild'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_runAction(lua_State* tolua_S) +int lua_ax_base_Node_getChildByTag(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -8184,7 +8388,7 @@ int lua_ax_base_Node_runAction(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_runAction'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getChildByTag'", nullptr); return 0; } #endif @@ -8192,29 +8396,29 @@ int lua_ax_base_Node_runAction(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Action* arg0; + int arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.Action",&arg0, "ax.Node:runAction"); + ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Node:getChildByTag"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_runAction'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getChildByTag'", nullptr); return 0; } - auto&& ret = cobj->runAction(arg0); - object_to_luaval(tolua_S, "ax.Action",(ax::Action*)ret); + auto&& ret = cobj->getChildByTag(arg0); + object_to_luaval(tolua_S, "ax.Node",(ax::Node*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:runAction",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getChildByTag",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_runAction'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getChildByTag'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_stopAllActions(lua_State* tolua_S) +int lua_ax_base_Node_getChildByName(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -8234,84 +8438,84 @@ int lua_ax_base_Node_stopAllActions(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_stopAllActions'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getChildByName'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + std::string_view arg0; + + ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Node:getChildByName"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_stopAllActions'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getChildByName'", nullptr); return 0; } - cobj->stopAllActions(); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getChildByName(arg0); + object_to_luaval(tolua_S, "ax.Node",(ax::Node*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:stopAllActions",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getChildByName",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_stopAllActions'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getChildByName'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_stopAction(lua_State* tolua_S) +int lua_ax_base_Node_getChildren(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; bool ok = true; - #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); - #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_stopAction'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getChildren'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - ax::Action* arg0; - - ok &= luaval_to_object(tolua_S, 2, "ax.Action",&arg0, "ax.Node:stopAction"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_stopAction'", nullptr); - return 0; + do{ + if (argc == 0) { + const ax::Vector& ret = cobj->getChildren(); + ccvector_to_luaval(tolua_S, ret); + return 1; } - cobj->stopAction(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:stopAction",argc, 1); + }while(0); + ok = true; + do{ + if (argc == 0) { + ax::Vector& ret = cobj->getChildren(); + ccvector_to_luaval(tolua_S, ret); + return 1; + } + }while(0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getChildren",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_stopAction'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getChildren'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_stopActionByTag(lua_State* tolua_S) +int lua_ax_base_Node_getChildrenCount(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -8331,37 +8535,34 @@ int lua_ax_base_Node_stopActionByTag(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_stopActionByTag'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getChildrenCount'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - int arg0; - - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Node:stopActionByTag"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_stopActionByTag'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getChildrenCount'", nullptr); return 0; } - cobj->stopActionByTag(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getChildrenCount(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:stopActionByTag",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getChildrenCount",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_stopActionByTag'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getChildrenCount'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_stopAllActionsByTag(lua_State* tolua_S) +int lua_ax_base_Node_setParent(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -8381,7 +8582,7 @@ int lua_ax_base_Node_stopAllActionsByTag(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_stopAllActionsByTag'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setParent'", nullptr); return 0; } #endif @@ -8389,129 +8590,127 @@ int lua_ax_base_Node_stopAllActionsByTag(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - int arg0; + ax::Node* arg0; - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Node:stopAllActionsByTag"); + ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Node:setParent"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_stopAllActionsByTag'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setParent'", nullptr); return 0; } - cobj->stopAllActionsByTag(arg0); + cobj->setParent(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:stopAllActionsByTag",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setParent",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_stopAllActionsByTag'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setParent'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_stopActionsByFlags(lua_State* tolua_S) +int lua_ax_base_Node_getParent(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; bool ok = true; - #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); - #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_stopActionsByFlags'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getParent'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - unsigned int arg0; - - ok &= luaval_to_uint32(tolua_S, 2,&arg0, "ax.Node:stopActionsByFlags"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_stopActionsByFlags'", nullptr); - return 0; + do{ + if (argc == 0) { + const ax::Node* ret = cobj->getParent(); + object_to_luaval(tolua_S, "ax.Node",(ax::Node*)ret); + return 1; } - cobj->stopActionsByFlags(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:stopActionsByFlags",argc, 1); + }while(0); + ok = true; + do{ + if (argc == 0) { + ax::Node* ret = cobj->getParent(); + object_to_luaval(tolua_S, "ax.Node",(ax::Node*)ret); + return 1; + } + }while(0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getParent",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_stopActionsByFlags'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getParent'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getActionByTag(lua_State* tolua_S) +int lua_ax_base_Node_removeFromParentAndCleanup(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; bool ok = true; - #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); - #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getActionByTag'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_removeFromParentAndCleanup'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - int arg0; + do{ + if (argc == 1) { + bool arg0; + ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.Node:removeFromParentAndCleanup"); - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Node:getActionByTag"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getActionByTag'", nullptr); - return 0; + if (!ok) { break; } + cobj->removeFromParentAndCleanup(arg0); + lua_settop(tolua_S, 1); + return 1; } - auto&& ret = cobj->getActionByTag(arg0); - object_to_luaval(tolua_S, "ax.Action",(ax::Action*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getActionByTag",argc, 1); + }while(0); + ok = true; + do{ + if (argc == 0) { + cobj->removeFromParent(); + lua_settop(tolua_S, 1); + return 1; + } + }while(0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:removeFromParent",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getActionByTag'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_removeFromParentAndCleanup'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getNumberOfRunningActions(lua_State* tolua_S) +int lua_ax_base_Node_removeChild(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -8531,34 +8730,54 @@ int lua_ax_base_Node_getNumberOfRunningActions(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getNumberOfRunningActions'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_removeChild'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + ax::Node* arg0; + + ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Node:removeChild"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getNumberOfRunningActions'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_removeChild'", nullptr); return 0; } - auto&& ret = cobj->getNumberOfRunningActions(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + cobj->removeChild(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getNumberOfRunningActions",argc, 0); + if (argc == 2) + { + ax::Node* arg0; + bool arg1; + + ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Node:removeChild"); + + ok &= luaval_to_boolean(tolua_S, 3,&arg1, "ax.Node:removeChild"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_removeChild'", nullptr); + return 0; + } + cobj->removeChild(arg0, arg1); + lua_settop(tolua_S, 1); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:removeChild",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getNumberOfRunningActions'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_removeChild'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getNumberOfRunningActionsByTag(lua_State* tolua_S) +int lua_ax_base_Node_removeChildByTag(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -8578,7 +8797,7 @@ int lua_ax_base_Node_getNumberOfRunningActionsByTag(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getNumberOfRunningActionsByTag'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_removeChildByTag'", nullptr); return 0; } #endif @@ -8588,27 +8807,44 @@ int lua_ax_base_Node_getNumberOfRunningActionsByTag(lua_State* tolua_S) { int arg0; - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Node:getNumberOfRunningActionsByTag"); + ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Node:removeChildByTag"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getNumberOfRunningActionsByTag'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_removeChildByTag'", nullptr); return 0; } - auto&& ret = cobj->getNumberOfRunningActionsByTag(arg0); - tolua_pushnumber(tolua_S,(lua_Number)ret); + cobj->removeChildByTag(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getNumberOfRunningActionsByTag",argc, 1); + if (argc == 2) + { + int arg0; + bool arg1; + + ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Node:removeChildByTag"); + + ok &= luaval_to_boolean(tolua_S, 3,&arg1, "ax.Node:removeChildByTag"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_removeChildByTag'", nullptr); + return 0; + } + cobj->removeChildByTag(arg0, arg1); + lua_settop(tolua_S, 1); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:removeChildByTag",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getNumberOfRunningActionsByTag'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_removeChildByTag'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setScheduler(lua_State* tolua_S) +int lua_ax_base_Node_removeChildByName(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -8628,7 +8864,7 @@ int lua_ax_base_Node_setScheduler(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setScheduler'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_removeChildByName'", nullptr); return 0; } #endif @@ -8636,29 +8872,46 @@ int lua_ax_base_Node_setScheduler(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Scheduler* arg0; + std::string_view arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.Scheduler",&arg0, "ax.Node:setScheduler"); + ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Node:removeChildByName"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setScheduler'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_removeChildByName'", nullptr); return 0; } - cobj->setScheduler(arg0); + cobj->removeChildByName(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setScheduler",argc, 1); + if (argc == 2) + { + std::string_view arg0; + bool arg1; + + ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Node:removeChildByName"); + + ok &= luaval_to_boolean(tolua_S, 3,&arg1, "ax.Node:removeChildByName"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_removeChildByName'", nullptr); + return 0; + } + cobj->removeChildByName(arg0, arg1); + lua_settop(tolua_S, 1); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:removeChildByName",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setScheduler'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_removeChildByName'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getScheduler(lua_State* tolua_S) +int lua_ax_base_Node_removeAllChildrenWithCleanup(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -8674,38 +8927,42 @@ int lua_ax_base_Node_getScheduler(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getScheduler'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_removeAllChildrenWithCleanup'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; do{ - if (argc == 0) { - const ax::Scheduler* ret = cobj->getScheduler(); - object_to_luaval(tolua_S, "ax.Scheduler",(ax::Scheduler*)ret); + if (argc == 1) { + bool arg0; + ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.Node:removeAllChildrenWithCleanup"); + + if (!ok) { break; } + cobj->removeAllChildrenWithCleanup(arg0); + lua_settop(tolua_S, 1); return 1; } }while(0); ok = true; do{ if (argc == 0) { - ax::Scheduler* ret = cobj->getScheduler(); - object_to_luaval(tolua_S, "ax.Scheduler",(ax::Scheduler*)ret); + cobj->removeAllChildren(); + lua_settop(tolua_S, 1); return 1; } }while(0); ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getScheduler",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:removeAllChildren",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getScheduler'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_removeAllChildrenWithCleanup'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_isScheduled(lua_State* tolua_S) +int lua_ax_base_Node_reorderChild(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -8725,37 +8982,40 @@ int lua_ax_base_Node_isScheduled(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_isScheduled'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_reorderChild'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 2) { - std::string_view arg0; + ax::Node* arg0; + int arg1; - ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Node:isScheduled"); + ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Node:reorderChild"); + + ok &= luaval_to_int32(tolua_S, 3,(int *)&arg1, "ax.Node:reorderChild"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_isScheduled'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_reorderChild'", nullptr); return 0; } - auto&& ret = cobj->isScheduled(arg0); - tolua_pushboolean(tolua_S,(bool)ret); + cobj->reorderChild(arg0, arg1); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:isScheduled",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:reorderChild",argc, 2); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_isScheduled'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_reorderChild'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_resume(lua_State* tolua_S) +int lua_ax_base_Node_sortAllChildren(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -8775,7 +9035,7 @@ int lua_ax_base_Node_resume(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_resume'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_sortAllChildren'", nullptr); return 0; } #endif @@ -8785,24 +9045,24 @@ int lua_ax_base_Node_resume(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_resume'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_sortAllChildren'", nullptr); return 0; } - cobj->resume(); + cobj->sortAllChildren(); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:resume",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:sortAllChildren",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_resume'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_sortAllChildren'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_pause(lua_State* tolua_S) +int lua_ax_base_Node_getTag(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -8822,7 +9082,7 @@ int lua_ax_base_Node_pause(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_pause'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getTag'", nullptr); return 0; } #endif @@ -8832,24 +9092,24 @@ int lua_ax_base_Node_pause(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_pause'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getTag'", nullptr); return 0; } - cobj->pause(); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getTag(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:pause",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getTag",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_pause'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getTag'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_update(lua_State* tolua_S) +int lua_ax_base_Node_setTag(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -8869,7 +9129,7 @@ int lua_ax_base_Node_update(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_update'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setTag'", nullptr); return 0; } #endif @@ -8877,29 +9137,29 @@ int lua_ax_base_Node_update(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - double arg0; + int arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Node:update"); + ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Node:setTag"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_update'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setTag'", nullptr); return 0; } - cobj->update(arg0); + cobj->setTag(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:update",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setTag",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_update'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setTag'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_updateTransform(lua_State* tolua_S) +int lua_ax_base_Node_getName(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -8919,7 +9179,7 @@ int lua_ax_base_Node_updateTransform(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_updateTransform'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getName'", nullptr); return 0; } #endif @@ -8929,126 +9189,124 @@ int lua_ax_base_Node_updateTransform(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_updateTransform'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getName'", nullptr); return 0; } - cobj->updateTransform(); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getName(); + lua_pushlstring(tolua_S,ret.data(),ret.length()); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:updateTransform",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getName",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_updateTransform'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getName'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getNodeToParentTransform(lua_State* tolua_S) +int lua_ax_base_Node_setName(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; bool ok = true; + #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif + cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); + #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getNodeToParentTransform'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setName'", nullptr); return 0; } #endif + argc = lua_gettop(tolua_S)-1; - do{ - if (argc == 1) { - ax::Node* arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Node:getNodeToParentTransform"); + if (argc == 1) + { + std::string_view arg0; - if (!ok) { break; } - ax::Mat4 ret = cobj->getNodeToParentTransform(arg0); - mat4_to_luaval(tolua_S, ret); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 0) { - const ax::Mat4& ret = cobj->getNodeToParentTransform(); - mat4_to_luaval(tolua_S, ret); - return 1; + ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Node:setName"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setName'", nullptr); + return 0; } - }while(0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getNodeToParentTransform",argc, 0); + cobj->setName(arg0); + lua_settop(tolua_S, 1); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setName",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getNodeToParentTransform'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setName'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getNodeToParentAffineTransform(lua_State* tolua_S) +int lua_ax_base_Node_setUserObject(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; bool ok = true; + #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif + cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); + #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getNodeToParentAffineTransform'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setUserObject'", nullptr); return 0; } #endif + argc = lua_gettop(tolua_S)-1; - do{ - if (argc == 1) { - ax::Node* arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Node:getNodeToParentAffineTransform"); + if (argc == 1) + { + ax::Object* arg0; - if (!ok) { break; } - ax::AffineTransform ret = cobj->getNodeToParentAffineTransform(arg0); - affinetransform_to_luaval(tolua_S, ret); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 0) { - ax::AffineTransform ret = cobj->getNodeToParentAffineTransform(); - affinetransform_to_luaval(tolua_S, ret); - return 1; + ok &= luaval_to_object(tolua_S, 2, "ax.Object",&arg0, "ax.Node:setUserObject"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setUserObject'", nullptr); + return 0; } - }while(0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getNodeToParentAffineTransform",argc, 0); + cobj->setUserObject(arg0); + lua_settop(tolua_S, 1); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setUserObject",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getNodeToParentAffineTransform'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setUserObject'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setNodeToParentTransform(lua_State* tolua_S) +int lua_ax_base_Node_isRunning(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -9068,37 +9326,34 @@ int lua_ax_base_Node_setNodeToParentTransform(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setNodeToParentTransform'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_isRunning'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - ax::Mat4 arg0; - - ok &= luaval_to_mat4(tolua_S, 2, &arg0, "ax.Node:setNodeToParentTransform"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setNodeToParentTransform'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_isRunning'", nullptr); return 0; } - cobj->setNodeToParentTransform(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->isRunning(); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setNodeToParentTransform",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:isRunning",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setNodeToParentTransform'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_isRunning'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getParentToNodeTransform(lua_State* tolua_S) +int lua_ax_base_Node_cleanup(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -9118,7 +9373,7 @@ int lua_ax_base_Node_getParentToNodeTransform(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getParentToNodeTransform'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_cleanup'", nullptr); return 0; } #endif @@ -9128,118 +9383,142 @@ int lua_ax_base_Node_getParentToNodeTransform(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getParentToNodeTransform'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_cleanup'", nullptr); return 0; } - auto&& ret = cobj->getParentToNodeTransform(); - mat4_to_luaval(tolua_S, ret); + cobj->cleanup(); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getParentToNodeTransform",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:cleanup",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getParentToNodeTransform'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_cleanup'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getParentToNodeAffineTransform(lua_State* tolua_S) +int lua_ax_base_Node_draw(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; bool ok = true; - #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); - #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getParentToNodeAffineTransform'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_draw'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getParentToNodeAffineTransform'", nullptr); - return 0; + do{ + if (argc == 0) { + cobj->draw(); + lua_settop(tolua_S, 1); + return 1; } - auto&& ret = cobj->getParentToNodeAffineTransform(); - affinetransform_to_luaval(tolua_S, ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getParentToNodeAffineTransform",argc, 0); + }while(0); + ok = true; + do{ + if (argc == 3) { + ax::Renderer* arg0; + ok &= luaval_to_object(tolua_S, 2, "ax.Renderer",&arg0, "ax.Node:draw"); + + if (!ok) { break; } + ax::Mat4 arg1; + ok &= luaval_to_mat4(tolua_S, 3, &arg1, "ax.Node:draw"); + + if (!ok) { break; } + unsigned int arg2; + ok &= luaval_to_uint32(tolua_S, 4,&arg2, "ax.Node:draw"); + + if (!ok) { break; } + cobj->draw(arg0, arg1, arg2); + lua_settop(tolua_S, 1); + return 1; + } + }while(0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:draw",argc, 3); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getParentToNodeAffineTransform'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_draw'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getNodeToWorldTransform(lua_State* tolua_S) +int lua_ax_base_Node_visit(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; bool ok = true; - #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); - #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getNodeToWorldTransform'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_visit'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getNodeToWorldTransform'", nullptr); - return 0; + do{ + if (argc == 0) { + cobj->visit(); + lua_settop(tolua_S, 1); + return 1; } - auto&& ret = cobj->getNodeToWorldTransform(); - mat4_to_luaval(tolua_S, ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getNodeToWorldTransform",argc, 0); + }while(0); + ok = true; + do{ + if (argc == 3) { + ax::Renderer* arg0; + ok &= luaval_to_object(tolua_S, 2, "ax.Renderer",&arg0, "ax.Node:visit"); + + if (!ok) { break; } + ax::Mat4 arg1; + ok &= luaval_to_mat4(tolua_S, 3, &arg1, "ax.Node:visit"); + + if (!ok) { break; } + unsigned int arg2; + ok &= luaval_to_uint32(tolua_S, 4,&arg2, "ax.Node:visit"); + + if (!ok) { break; } + cobj->visit(arg0, arg1, arg2); + lua_settop(tolua_S, 1); + return 1; + } + }while(0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:visit",argc, 3); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getNodeToWorldTransform'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_visit'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getNodeToWorldAffineTransform(lua_State* tolua_S) +int lua_ax_base_Node_getScene(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -9259,7 +9538,7 @@ int lua_ax_base_Node_getNodeToWorldAffineTransform(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getNodeToWorldAffineTransform'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getScene'", nullptr); return 0; } #endif @@ -9269,24 +9548,24 @@ int lua_ax_base_Node_getNodeToWorldAffineTransform(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getNodeToWorldAffineTransform'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getScene'", nullptr); return 0; } - auto&& ret = cobj->getNodeToWorldAffineTransform(); - affinetransform_to_luaval(tolua_S, ret); + auto&& ret = cobj->getScene(); + object_to_luaval(tolua_S, "ax.Scene",(ax::Scene*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getNodeToWorldAffineTransform",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getScene",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getNodeToWorldAffineTransform'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getScene'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getWorldToNodeTransform(lua_State* tolua_S) +int lua_ax_base_Node_getBoundingBox(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -9306,7 +9585,7 @@ int lua_ax_base_Node_getWorldToNodeTransform(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getWorldToNodeTransform'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getBoundingBox'", nullptr); return 0; } #endif @@ -9316,24 +9595,24 @@ int lua_ax_base_Node_getWorldToNodeTransform(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getWorldToNodeTransform'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getBoundingBox'", nullptr); return 0; } - auto&& ret = cobj->getWorldToNodeTransform(); - mat4_to_luaval(tolua_S, ret); + auto&& ret = cobj->getBoundingBox(); + rect_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getWorldToNodeTransform",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getBoundingBox",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getWorldToNodeTransform'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getBoundingBox'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getWorldToNodeAffineTransform(lua_State* tolua_S) +int lua_ax_base_Node_setEventDispatcher(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -9353,34 +9632,37 @@ int lua_ax_base_Node_getWorldToNodeAffineTransform(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getWorldToNodeAffineTransform'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setEventDispatcher'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + ax::EventDispatcher* arg0; + + ok &= luaval_to_object(tolua_S, 2, "ax.EventDispatcher",&arg0, "ax.Node:setEventDispatcher"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getWorldToNodeAffineTransform'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setEventDispatcher'", nullptr); return 0; } - auto&& ret = cobj->getWorldToNodeAffineTransform(); - affinetransform_to_luaval(tolua_S, ret); + cobj->setEventDispatcher(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getWorldToNodeAffineTransform",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setEventDispatcher",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getWorldToNodeAffineTransform'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setEventDispatcher'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_convertToNodeSpace(lua_State* tolua_S) +int lua_ax_base_Node_getEventDispatcher(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -9400,37 +9682,34 @@ int lua_ax_base_Node_convertToNodeSpace(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_convertToNodeSpace'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getEventDispatcher'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - ax::Vec2 arg0; - - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.Node:convertToNodeSpace"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_convertToNodeSpace'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getEventDispatcher'", nullptr); return 0; } - auto&& ret = cobj->convertToNodeSpace(arg0); - vec2_to_luaval(tolua_S, ret); + auto&& ret = cobj->getEventDispatcher(); + object_to_luaval(tolua_S, "ax.EventDispatcher",(ax::EventDispatcher*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:convertToNodeSpace",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getEventDispatcher",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_convertToNodeSpace'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getEventDispatcher'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_convertToWorldSpace(lua_State* tolua_S) +int lua_ax_base_Node_setActionManager(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -9450,7 +9729,7 @@ int lua_ax_base_Node_convertToWorldSpace(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_convertToWorldSpace'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setActionManager'", nullptr); return 0; } #endif @@ -9458,79 +9737,76 @@ int lua_ax_base_Node_convertToWorldSpace(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Vec2 arg0; + ax::ActionManager* arg0; - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.Node:convertToWorldSpace"); + ok &= luaval_to_object(tolua_S, 2, "ax.ActionManager",&arg0, "ax.Node:setActionManager"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_convertToWorldSpace'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setActionManager'", nullptr); return 0; } - auto&& ret = cobj->convertToWorldSpace(arg0); - vec2_to_luaval(tolua_S, ret); + cobj->setActionManager(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:convertToWorldSpace",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setActionManager",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_convertToWorldSpace'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setActionManager'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_convertToNodeSpaceAR(lua_State* tolua_S) +int lua_ax_base_Node_getActionManager(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; bool ok = true; - #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); - #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_convertToNodeSpaceAR'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getActionManager'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - ax::Vec2 arg0; - - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.Node:convertToNodeSpaceAR"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_convertToNodeSpaceAR'", nullptr); - return 0; + do{ + if (argc == 0) { + const ax::ActionManager* ret = cobj->getActionManager(); + object_to_luaval(tolua_S, "ax.ActionManager",(ax::ActionManager*)ret); + return 1; } - auto&& ret = cobj->convertToNodeSpaceAR(arg0); - vec2_to_luaval(tolua_S, ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:convertToNodeSpaceAR",argc, 1); + }while(0); + ok = true; + do{ + if (argc == 0) { + ax::ActionManager* ret = cobj->getActionManager(); + object_to_luaval(tolua_S, "ax.ActionManager",(ax::ActionManager*)ret); + return 1; + } + }while(0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getActionManager",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_convertToNodeSpaceAR'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getActionManager'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_convertToWorldSpaceAR(lua_State* tolua_S) +int lua_ax_base_Node_runAction(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -9550,7 +9826,7 @@ int lua_ax_base_Node_convertToWorldSpaceAR(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_convertToWorldSpaceAR'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_runAction'", nullptr); return 0; } #endif @@ -9558,29 +9834,29 @@ int lua_ax_base_Node_convertToWorldSpaceAR(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Vec2 arg0; + ax::Action* arg0; - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.Node:convertToWorldSpaceAR"); + ok &= luaval_to_object(tolua_S, 2, "ax.Action",&arg0, "ax.Node:runAction"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_convertToWorldSpaceAR'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_runAction'", nullptr); return 0; } - auto&& ret = cobj->convertToWorldSpaceAR(arg0); - vec2_to_luaval(tolua_S, ret); + auto&& ret = cobj->runAction(arg0); + object_to_luaval(tolua_S, "ax.Action",(ax::Action*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:convertToWorldSpaceAR",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:runAction",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_convertToWorldSpaceAR'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_runAction'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_convertTouchToNodeSpace(lua_State* tolua_S) +int lua_ax_base_Node_stopAllActions(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -9600,37 +9876,34 @@ int lua_ax_base_Node_convertTouchToNodeSpace(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_convertTouchToNodeSpace'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_stopAllActions'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - ax::Touch* arg0; - - ok &= luaval_to_object(tolua_S, 2, "ax.Touch",&arg0, "ax.Node:convertTouchToNodeSpace"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_convertTouchToNodeSpace'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_stopAllActions'", nullptr); return 0; } - auto&& ret = cobj->convertTouchToNodeSpace(arg0); - vec2_to_luaval(tolua_S, ret); + cobj->stopAllActions(); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:convertTouchToNodeSpace",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:stopAllActions",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_convertTouchToNodeSpace'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_stopAllActions'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_convertTouchToNodeSpaceAR(lua_State* tolua_S) +int lua_ax_base_Node_stopAction(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -9650,7 +9923,7 @@ int lua_ax_base_Node_convertTouchToNodeSpaceAR(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_convertTouchToNodeSpaceAR'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_stopAction'", nullptr); return 0; } #endif @@ -9658,29 +9931,29 @@ int lua_ax_base_Node_convertTouchToNodeSpaceAR(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Touch* arg0; + ax::Action* arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.Touch",&arg0, "ax.Node:convertTouchToNodeSpaceAR"); + ok &= luaval_to_object(tolua_S, 2, "ax.Action",&arg0, "ax.Node:stopAction"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_convertTouchToNodeSpaceAR'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_stopAction'", nullptr); return 0; } - auto&& ret = cobj->convertTouchToNodeSpaceAR(arg0); - vec2_to_luaval(tolua_S, ret); + cobj->stopAction(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:convertTouchToNodeSpaceAR",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:stopAction",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_convertTouchToNodeSpaceAR'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_stopAction'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getWorldPosition(lua_State* tolua_S) +int lua_ax_base_Node_stopActionByTag(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -9700,34 +9973,37 @@ int lua_ax_base_Node_getWorldPosition(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getWorldPosition'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_stopActionByTag'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + int arg0; + + ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Node:stopActionByTag"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getWorldPosition'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_stopActionByTag'", nullptr); return 0; } - auto&& ret = cobj->getWorldPosition(); - vec2_to_luaval(tolua_S, ret); + cobj->stopActionByTag(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getWorldPosition",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:stopActionByTag",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getWorldPosition'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_stopActionByTag'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setWorldPosition(lua_State* tolua_S) +int lua_ax_base_Node_stopAllActionsByTag(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -9747,7 +10023,7 @@ int lua_ax_base_Node_setWorldPosition(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setWorldPosition'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_stopAllActionsByTag'", nullptr); return 0; } #endif @@ -9755,29 +10031,29 @@ int lua_ax_base_Node_setWorldPosition(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Vec2 arg0; + int arg0; - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.Node:setWorldPosition"); + ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Node:stopAllActionsByTag"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setWorldPosition'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_stopAllActionsByTag'", nullptr); return 0; } - cobj->setWorldPosition(arg0); + cobj->stopAllActionsByTag(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setWorldPosition",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:stopAllActionsByTag",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setWorldPosition'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_stopAllActionsByTag'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getComponent(lua_State* tolua_S) +int lua_ax_base_Node_stopActionsByFlags(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -9797,7 +10073,7 @@ int lua_ax_base_Node_getComponent(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getComponent'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_stopActionsByFlags'", nullptr); return 0; } #endif @@ -9805,29 +10081,29 @@ int lua_ax_base_Node_getComponent(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - std::string_view arg0; + unsigned int arg0; - ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Node:getComponent"); + ok &= luaval_to_uint32(tolua_S, 2,&arg0, "ax.Node:stopActionsByFlags"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getComponent'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_stopActionsByFlags'", nullptr); return 0; } - auto&& ret = cobj->getComponent(arg0); - object_to_luaval(tolua_S, "ax.Component",(ax::Component*)ret); + cobj->stopActionsByFlags(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getComponent",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:stopActionsByFlags",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getComponent'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_stopActionsByFlags'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_addComponent(lua_State* tolua_S) +int lua_ax_base_Node_getActionByTag(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -9847,7 +10123,7 @@ int lua_ax_base_Node_addComponent(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_addComponent'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getActionByTag'", nullptr); return 0; } #endif @@ -9855,84 +10131,76 @@ int lua_ax_base_Node_addComponent(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Component* arg0; + int arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.Component",&arg0, "ax.Node:addComponent"); + ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Node:getActionByTag"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_addComponent'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getActionByTag'", nullptr); return 0; } - auto&& ret = cobj->addComponent(arg0); - tolua_pushboolean(tolua_S,(bool)ret); + auto&& ret = cobj->getActionByTag(arg0); + object_to_luaval(tolua_S, "ax.Action",(ax::Action*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:addComponent",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getActionByTag",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_addComponent'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getActionByTag'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_removeComponent(lua_State* tolua_S) +int lua_ax_base_Node_getNumberOfRunningActions(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; bool ok = true; + #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif + cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); + #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_removeComponent'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getNumberOfRunningActions'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - do{ - if (argc == 1) { - ax::Component* arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.Component",&arg0, "ax.Node:removeComponent"); - - if (!ok) { break; } - bool ret = cobj->removeComponent(arg0); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 1) { - std::string_view arg0; - ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Node:removeComponent"); - if (!ok) { break; } - bool ret = cobj->removeComponent(arg0); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; + argc = lua_gettop(tolua_S)-1; + if (argc == 0) + { + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getNumberOfRunningActions'", nullptr); + return 0; } - }while(0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:removeComponent",argc, 1); + auto&& ret = cobj->getNumberOfRunningActions(); + tolua_pushnumber(tolua_S,(lua_Number)ret); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getNumberOfRunningActions",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_removeComponent'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getNumberOfRunningActions'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_removeAllComponents(lua_State* tolua_S) +int lua_ax_base_Node_getNumberOfRunningActionsByTag(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -9952,34 +10220,37 @@ int lua_ax_base_Node_removeAllComponents(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_removeAllComponents'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getNumberOfRunningActionsByTag'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + int arg0; + + ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Node:getNumberOfRunningActionsByTag"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_removeAllComponents'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getNumberOfRunningActionsByTag'", nullptr); return 0; } - cobj->removeAllComponents(); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getNumberOfRunningActionsByTag(arg0); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:removeAllComponents",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getNumberOfRunningActionsByTag",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_removeAllComponents'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getNumberOfRunningActionsByTag'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getOpacity(lua_State* tolua_S) +int lua_ax_base_Node_setScheduler(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -9999,81 +10270,84 @@ int lua_ax_base_Node_getOpacity(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getOpacity'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setScheduler'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + ax::Scheduler* arg0; + + ok &= luaval_to_object(tolua_S, 2, "ax.Scheduler",&arg0, "ax.Node:setScheduler"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getOpacity'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setScheduler'", nullptr); return 0; } - auto&& ret = cobj->getOpacity(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + cobj->setScheduler(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getOpacity",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setScheduler",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getOpacity'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setScheduler'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getDisplayedOpacity(lua_State* tolua_S) +int lua_ax_base_Node_getScheduler(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; bool ok = true; - #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); - #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getDisplayedOpacity'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getScheduler'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getDisplayedOpacity'", nullptr); - return 0; + do{ + if (argc == 0) { + const ax::Scheduler* ret = cobj->getScheduler(); + object_to_luaval(tolua_S, "ax.Scheduler",(ax::Scheduler*)ret); + return 1; } - auto&& ret = cobj->getDisplayedOpacity(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getDisplayedOpacity",argc, 0); + }while(0); + ok = true; + do{ + if (argc == 0) { + ax::Scheduler* ret = cobj->getScheduler(); + object_to_luaval(tolua_S, "ax.Scheduler",(ax::Scheduler*)ret); + return 1; + } + }while(0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getScheduler",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getDisplayedOpacity'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getScheduler'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setOpacity(lua_State* tolua_S) +int lua_ax_base_Node_isScheduled(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -10093,7 +10367,7 @@ int lua_ax_base_Node_setOpacity(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setOpacity'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_isScheduled'", nullptr); return 0; } #endif @@ -10101,29 +10375,29 @@ int lua_ax_base_Node_setOpacity(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - uint16_t arg0; + std::string_view arg0; - ok &= luaval_to_uint16(tolua_S, 2,&arg0, "ax.Node:setOpacity"); + ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Node:isScheduled"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setOpacity'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_isScheduled'", nullptr); return 0; } - cobj->setOpacity(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->isScheduled(arg0); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setOpacity",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:isScheduled",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setOpacity'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_isScheduled'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_updateDisplayedOpacity(lua_State* tolua_S) +int lua_ax_base_Node_resume(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -10143,37 +10417,34 @@ int lua_ax_base_Node_updateDisplayedOpacity(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_updateDisplayedOpacity'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_resume'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - uint16_t arg0; - - ok &= luaval_to_uint16(tolua_S, 2,&arg0, "ax.Node:updateDisplayedOpacity"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_updateDisplayedOpacity'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_resume'", nullptr); return 0; } - cobj->updateDisplayedOpacity(arg0); + cobj->resume(); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:updateDisplayedOpacity",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:resume",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_updateDisplayedOpacity'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_resume'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_isCascadeOpacityEnabled(lua_State* tolua_S) +int lua_ax_base_Node_pause(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -10193,7 +10464,7 @@ int lua_ax_base_Node_isCascadeOpacityEnabled(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_isCascadeOpacityEnabled'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_pause'", nullptr); return 0; } #endif @@ -10203,24 +10474,24 @@ int lua_ax_base_Node_isCascadeOpacityEnabled(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_isCascadeOpacityEnabled'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_pause'", nullptr); return 0; } - auto&& ret = cobj->isCascadeOpacityEnabled(); - tolua_pushboolean(tolua_S,(bool)ret); + cobj->pause(); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:isCascadeOpacityEnabled",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:pause",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_isCascadeOpacityEnabled'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_pause'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setCascadeOpacityEnabled(lua_State* tolua_S) +int lua_ax_base_Node_update(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -10240,7 +10511,7 @@ int lua_ax_base_Node_setCascadeOpacityEnabled(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setCascadeOpacityEnabled'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_update'", nullptr); return 0; } #endif @@ -10248,29 +10519,29 @@ int lua_ax_base_Node_setCascadeOpacityEnabled(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - bool arg0; + double arg0; - ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.Node:setCascadeOpacityEnabled"); + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Node:update"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setCascadeOpacityEnabled'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_update'", nullptr); return 0; } - cobj->setCascadeOpacityEnabled(arg0); + cobj->update(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setCascadeOpacityEnabled",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:update",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setCascadeOpacityEnabled'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_update'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getColor(lua_State* tolua_S) +int lua_ax_base_Node_updateTransform(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -10290,7 +10561,7 @@ int lua_ax_base_Node_getColor(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getColor'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_updateTransform'", nullptr); return 0; } #endif @@ -10300,121 +10571,126 @@ int lua_ax_base_Node_getColor(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getColor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_updateTransform'", nullptr); return 0; } - auto&& ret = cobj->getColor(); - color3b_to_luaval(tolua_S, ret); + cobj->updateTransform(); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getColor",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:updateTransform",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getColor'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_updateTransform'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getDisplayedColor(lua_State* tolua_S) +int lua_ax_base_Node_getNodeToParentTransform(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; bool ok = true; - #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); - #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getDisplayedColor'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getNodeToParentTransform'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getDisplayedColor'", nullptr); - return 0; + do{ + if (argc == 1) { + ax::Node* arg0; + ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Node:getNodeToParentTransform"); + + if (!ok) { break; } + ax::Mat4 ret = cobj->getNodeToParentTransform(arg0); + mat4_to_luaval(tolua_S, ret); + return 1; } - auto&& ret = cobj->getDisplayedColor(); - color3b_to_luaval(tolua_S, ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getDisplayedColor",argc, 0); + }while(0); + ok = true; + do{ + if (argc == 0) { + const ax::Mat4& ret = cobj->getNodeToParentTransform(); + mat4_to_luaval(tolua_S, ret); + return 1; + } + }while(0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getNodeToParentTransform",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getDisplayedColor'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getNodeToParentTransform'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setColor(lua_State* tolua_S) +int lua_ax_base_Node_getNodeToParentAffineTransform(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; bool ok = true; - #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); - #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setColor'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getNodeToParentAffineTransform'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - ax::Color3B arg0; + do{ + if (argc == 1) { + ax::Node* arg0; + ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Node:getNodeToParentAffineTransform"); - ok &= luaval_to_color3b(tolua_S, 2, &arg0, "ax.Node:setColor"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setColor'", nullptr); - return 0; + if (!ok) { break; } + ax::AffineTransform ret = cobj->getNodeToParentAffineTransform(arg0); + affinetransform_to_luaval(tolua_S, ret); + return 1; } - cobj->setColor(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setColor",argc, 1); + }while(0); + ok = true; + do{ + if (argc == 0) { + ax::AffineTransform ret = cobj->getNodeToParentAffineTransform(); + affinetransform_to_luaval(tolua_S, ret); + return 1; + } + }while(0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getNodeToParentAffineTransform",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setColor'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getNodeToParentAffineTransform'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_updateDisplayedColor(lua_State* tolua_S) +int lua_ax_base_Node_setNodeToParentTransform(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -10434,7 +10710,7 @@ int lua_ax_base_Node_updateDisplayedColor(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_updateDisplayedColor'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setNodeToParentTransform'", nullptr); return 0; } #endif @@ -10442,29 +10718,29 @@ int lua_ax_base_Node_updateDisplayedColor(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Color3B arg0; + ax::Mat4 arg0; - ok &= luaval_to_color3b(tolua_S, 2, &arg0, "ax.Node:updateDisplayedColor"); + ok &= luaval_to_mat4(tolua_S, 2, &arg0, "ax.Node:setNodeToParentTransform"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_updateDisplayedColor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setNodeToParentTransform'", nullptr); return 0; } - cobj->updateDisplayedColor(arg0); + cobj->setNodeToParentTransform(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:updateDisplayedColor",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setNodeToParentTransform",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_updateDisplayedColor'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setNodeToParentTransform'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_isCascadeColorEnabled(lua_State* tolua_S) +int lua_ax_base_Node_getParentToNodeTransform(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -10484,7 +10760,7 @@ int lua_ax_base_Node_isCascadeColorEnabled(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_isCascadeColorEnabled'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getParentToNodeTransform'", nullptr); return 0; } #endif @@ -10494,24 +10770,24 @@ int lua_ax_base_Node_isCascadeColorEnabled(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_isCascadeColorEnabled'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getParentToNodeTransform'", nullptr); return 0; } - auto&& ret = cobj->isCascadeColorEnabled(); - tolua_pushboolean(tolua_S,(bool)ret); + auto&& ret = cobj->getParentToNodeTransform(); + mat4_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:isCascadeColorEnabled",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getParentToNodeTransform",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_isCascadeColorEnabled'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getParentToNodeTransform'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setCascadeColorEnabled(lua_State* tolua_S) +int lua_ax_base_Node_getParentToNodeAffineTransform(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -10531,37 +10807,34 @@ int lua_ax_base_Node_setCascadeColorEnabled(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setCascadeColorEnabled'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getParentToNodeAffineTransform'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - bool arg0; - - ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.Node:setCascadeColorEnabled"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setCascadeColorEnabled'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getParentToNodeAffineTransform'", nullptr); return 0; } - cobj->setCascadeColorEnabled(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getParentToNodeAffineTransform(); + affinetransform_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setCascadeColorEnabled",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getParentToNodeAffineTransform",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setCascadeColorEnabled'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getParentToNodeAffineTransform'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setOpacityModifyRGB(lua_State* tolua_S) +int lua_ax_base_Node_getNodeToWorldTransform(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -10581,37 +10854,34 @@ int lua_ax_base_Node_setOpacityModifyRGB(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setOpacityModifyRGB'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getNodeToWorldTransform'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - bool arg0; - - ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.Node:setOpacityModifyRGB"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setOpacityModifyRGB'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getNodeToWorldTransform'", nullptr); return 0; } - cobj->setOpacityModifyRGB(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getNodeToWorldTransform(); + mat4_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setOpacityModifyRGB",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getNodeToWorldTransform",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setOpacityModifyRGB'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getNodeToWorldTransform'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_isOpacityModifyRGB(lua_State* tolua_S) +int lua_ax_base_Node_getNodeToWorldAffineTransform(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -10631,7 +10901,7 @@ int lua_ax_base_Node_isOpacityModifyRGB(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_isOpacityModifyRGB'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getNodeToWorldAffineTransform'", nullptr); return 0; } #endif @@ -10641,24 +10911,24 @@ int lua_ax_base_Node_isOpacityModifyRGB(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_isOpacityModifyRGB'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getNodeToWorldAffineTransform'", nullptr); return 0; } - auto&& ret = cobj->isOpacityModifyRGB(); - tolua_pushboolean(tolua_S,(bool)ret); + auto&& ret = cobj->getNodeToWorldAffineTransform(); + affinetransform_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:isOpacityModifyRGB",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getNodeToWorldAffineTransform",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_isOpacityModifyRGB'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getNodeToWorldAffineTransform'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setOnEnterCallback(lua_State* tolua_S) +int lua_ax_base_Node_getWorldToNodeTransform(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -10678,41 +10948,34 @@ int lua_ax_base_Node_setOnEnterCallback(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setOnEnterCallback'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getWorldToNodeTransform'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - std::function arg0; - - do { - // Lambda binding for lua is not supported. - assert(false); - } while(0) - ; if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setOnEnterCallback'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getWorldToNodeTransform'", nullptr); return 0; } - cobj->setOnEnterCallback(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getWorldToNodeTransform(); + mat4_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setOnEnterCallback",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getWorldToNodeTransform",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setOnEnterCallback'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getWorldToNodeTransform'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setOnExitCallback(lua_State* tolua_S) +int lua_ax_base_Node_getWorldToNodeAffineTransform(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -10732,41 +10995,34 @@ int lua_ax_base_Node_setOnExitCallback(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setOnExitCallback'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getWorldToNodeAffineTransform'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - std::function arg0; - - do { - // Lambda binding for lua is not supported. - assert(false); - } while(0) - ; if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setOnExitCallback'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getWorldToNodeAffineTransform'", nullptr); return 0; } - cobj->setOnExitCallback(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getWorldToNodeAffineTransform(); + affinetransform_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setOnExitCallback",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getWorldToNodeAffineTransform",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setOnExitCallback'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getWorldToNodeAffineTransform'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setOnEnterTransitionDidFinishCallback(lua_State* tolua_S) +int lua_ax_base_Node_convertToNodeSpace(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -10786,7 +11042,7 @@ int lua_ax_base_Node_setOnEnterTransitionDidFinishCallback(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setOnEnterTransitionDidFinishCallback'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_convertToNodeSpace'", nullptr); return 0; } #endif @@ -10794,33 +11050,29 @@ int lua_ax_base_Node_setOnEnterTransitionDidFinishCallback(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - std::function arg0; + ax::Vec2 arg0; - do { - // Lambda binding for lua is not supported. - assert(false); - } while(0) - ; + ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.Node:convertToNodeSpace"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setOnEnterTransitionDidFinishCallback'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_convertToNodeSpace'", nullptr); return 0; } - cobj->setOnEnterTransitionDidFinishCallback(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->convertToNodeSpace(arg0); + vec2_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setOnEnterTransitionDidFinishCallback",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:convertToNodeSpace",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setOnEnterTransitionDidFinishCallback'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_convertToNodeSpace'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getOnEnterTransitionDidFinishCallback(lua_State* tolua_S) +int lua_ax_base_Node_convertToWorldSpace(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -10840,34 +11092,37 @@ int lua_ax_base_Node_getOnEnterTransitionDidFinishCallback(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getOnEnterTransitionDidFinishCallback'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_convertToWorldSpace'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + ax::Vec2 arg0; + + ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.Node:convertToWorldSpace"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getOnEnterTransitionDidFinishCallback'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_convertToWorldSpace'", nullptr); return 0; } - auto&& ret = cobj->getOnEnterTransitionDidFinishCallback(); - #pragma warning NO CONVERSION FROM NATIVE FOR std::function; + auto&& ret = cobj->convertToWorldSpace(arg0); + vec2_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getOnEnterTransitionDidFinishCallback",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:convertToWorldSpace",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getOnEnterTransitionDidFinishCallback'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_convertToWorldSpace'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setOnExitTransitionDidStartCallback(lua_State* tolua_S) +int lua_ax_base_Node_convertToNodeSpaceAR(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -10887,7 +11142,7 @@ int lua_ax_base_Node_setOnExitTransitionDidStartCallback(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setOnExitTransitionDidStartCallback'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_convertToNodeSpaceAR'", nullptr); return 0; } #endif @@ -10895,33 +11150,29 @@ int lua_ax_base_Node_setOnExitTransitionDidStartCallback(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - std::function arg0; + ax::Vec2 arg0; - do { - // Lambda binding for lua is not supported. - assert(false); - } while(0) - ; + ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.Node:convertToNodeSpaceAR"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setOnExitTransitionDidStartCallback'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_convertToNodeSpaceAR'", nullptr); return 0; } - cobj->setOnExitTransitionDidStartCallback(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->convertToNodeSpaceAR(arg0); + vec2_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setOnExitTransitionDidStartCallback",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:convertToNodeSpaceAR",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setOnExitTransitionDidStartCallback'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_convertToNodeSpaceAR'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getOnExitTransitionDidStartCallback(lua_State* tolua_S) +int lua_ax_base_Node_convertToWorldSpaceAR(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -10941,34 +11192,37 @@ int lua_ax_base_Node_getOnExitTransitionDidStartCallback(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getOnExitTransitionDidStartCallback'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_convertToWorldSpaceAR'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + ax::Vec2 arg0; + + ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.Node:convertToWorldSpaceAR"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getOnExitTransitionDidStartCallback'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_convertToWorldSpaceAR'", nullptr); return 0; } - auto&& ret = cobj->getOnExitTransitionDidStartCallback(); - #pragma warning NO CONVERSION FROM NATIVE FOR std::function; + auto&& ret = cobj->convertToWorldSpaceAR(arg0); + vec2_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getOnExitTransitionDidStartCallback",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:convertToWorldSpaceAR",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getOnExitTransitionDidStartCallback'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_convertToWorldSpaceAR'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getCameraMask(lua_State* tolua_S) +int lua_ax_base_Node_convertTouchToNodeSpace(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -10988,34 +11242,37 @@ int lua_ax_base_Node_getCameraMask(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getCameraMask'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_convertTouchToNodeSpace'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + ax::Touch* arg0; + + ok &= luaval_to_object(tolua_S, 2, "ax.Touch",&arg0, "ax.Node:convertTouchToNodeSpace"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getCameraMask'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_convertTouchToNodeSpace'", nullptr); return 0; } - auto&& ret = cobj->getCameraMask(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + auto&& ret = cobj->convertTouchToNodeSpace(arg0); + vec2_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getCameraMask",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:convertTouchToNodeSpace",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getCameraMask'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_convertTouchToNodeSpace'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setCameraMask(lua_State* tolua_S) +int lua_ax_base_Node_convertTouchToNodeSpaceAR(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -11035,7 +11292,7 @@ int lua_ax_base_Node_setCameraMask(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setCameraMask'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_convertTouchToNodeSpaceAR'", nullptr); return 0; } #endif @@ -11043,46 +11300,29 @@ int lua_ax_base_Node_setCameraMask(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - unsigned short arg0; - - ok &= luaval_to_ushort(tolua_S, 2, &arg0, "ax.Node:setCameraMask"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setCameraMask'", nullptr); - return 0; - } - cobj->setCameraMask(arg0); - lua_settop(tolua_S, 1); - return 1; - } - if (argc == 2) - { - unsigned short arg0; - bool arg1; - - ok &= luaval_to_ushort(tolua_S, 2, &arg0, "ax.Node:setCameraMask"); + ax::Touch* arg0; - ok &= luaval_to_boolean(tolua_S, 3,&arg1, "ax.Node:setCameraMask"); + ok &= luaval_to_object(tolua_S, 2, "ax.Touch",&arg0, "ax.Node:convertTouchToNodeSpaceAR"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setCameraMask'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_convertTouchToNodeSpaceAR'", nullptr); return 0; } - cobj->setCameraMask(arg0, arg1); - lua_settop(tolua_S, 1); + auto&& ret = cobj->convertTouchToNodeSpaceAR(arg0); + vec2_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setCameraMask",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:convertTouchToNodeSpaceAR",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setCameraMask'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_convertTouchToNodeSpaceAR'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_applyMaskOnEnter(lua_State* tolua_S) +int lua_ax_base_Node_getWorldPosition(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -11102,108 +11342,84 @@ int lua_ax_base_Node_applyMaskOnEnter(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_applyMaskOnEnter'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getWorldPosition'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - bool arg0; - - ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.Node:applyMaskOnEnter"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_applyMaskOnEnter'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getWorldPosition'", nullptr); return 0; } - cobj->applyMaskOnEnter(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getWorldPosition(); + vec2_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:applyMaskOnEnter",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getWorldPosition",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_applyMaskOnEnter'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getWorldPosition'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setProgramState(lua_State* tolua_S) +int lua_ax_base_Node_setWorldPosition(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; bool ok = true; + #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif + cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); + #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setProgramState'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setWorldPosition'", nullptr); return 0; } #endif + argc = lua_gettop(tolua_S)-1; - do{ - if (argc == 1) { - ax::backend::ProgramState* arg0; - ok &= luaval_to_object(tolua_S, 2, "axb.ProgramState",&arg0, "ax.Node:setProgramState"); + if (argc == 1) + { + ax::Vec2 arg0; - if (!ok) { break; } - bool ret = cobj->setProgramState(arg0); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 2) { - ax::backend::ProgramState* arg0; - ok &= luaval_to_object(tolua_S, 2, "axb.ProgramState",&arg0, "ax.Node:setProgramState"); - - if (!ok) { break; } - bool arg1; - ok &= luaval_to_boolean(tolua_S, 3,&arg1, "ax.Node:setProgramState"); - - if (!ok) { break; } - bool ret = cobj->setProgramState(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 1) { - unsigned int arg0; - ok &= luaval_to_uint32(tolua_S, 2,&arg0, "ax.Node:setProgramState"); - - if (!ok) { break; } - cobj->setProgramState(arg0); - lua_settop(tolua_S, 1); - return 1; + ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.Node:setWorldPosition"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setWorldPosition'", nullptr); + return 0; } - }while(0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setProgramState",argc, 1); + cobj->setWorldPosition(arg0); + lua_settop(tolua_S, 1); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setWorldPosition",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setProgramState'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setWorldPosition'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setProgramStateWithRegistry(lua_State* tolua_S) +int lua_ax_base_Node_getComponent(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -11223,40 +11439,37 @@ int lua_ax_base_Node_setProgramStateWithRegistry(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setProgramStateWithRegistry'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getComponent'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 2) + if (argc == 1) { - unsigned int arg0; - ax::Texture2D* arg1; - - ok &= luaval_to_uint32(tolua_S, 2,&arg0, "ax.Node:setProgramStateWithRegistry"); + std::string_view arg0; - ok &= luaval_to_object(tolua_S, 3, "ax.Texture2D",&arg1, "ax.Node:setProgramStateWithRegistry"); + ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Node:getComponent"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setProgramStateWithRegistry'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getComponent'", nullptr); return 0; } - cobj->setProgramStateWithRegistry(arg0, arg1); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getComponent(arg0); + object_to_luaval(tolua_S, "ax.Component",(ax::Component*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setProgramStateWithRegistry",argc, 2); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getComponent",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setProgramStateWithRegistry'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getComponent'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setProgramStateByProgramId(lua_State* tolua_S) +int lua_ax_base_Node_addComponent(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -11276,7 +11489,7 @@ int lua_ax_base_Node_setProgramStateByProgramId(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setProgramStateByProgramId'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_addComponent'", nullptr); return 0; } #endif @@ -11284,76 +11497,84 @@ int lua_ax_base_Node_setProgramStateByProgramId(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - unsigned long long arg0; + ax::Component* arg0; - ok &= luaval_to_long_long(tolua_S, 2,(long long*)&arg0, "ax.Node:setProgramStateByProgramId"); + ok &= luaval_to_object(tolua_S, 2, "ax.Component",&arg0, "ax.Node:addComponent"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setProgramStateByProgramId'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_addComponent'", nullptr); return 0; } - auto&& ret = cobj->setProgramStateByProgramId(arg0); - object_to_luaval(tolua_S, "axb.ProgramState",(ax::backend::ProgramState*)ret); + auto&& ret = cobj->addComponent(arg0); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setProgramStateByProgramId",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:addComponent",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setProgramStateByProgramId'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_addComponent'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getProgramState(lua_State* tolua_S) +int lua_ax_base_Node_removeComponent(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; bool ok = true; - #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); - #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getProgramState'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_removeComponent'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getProgramState'", nullptr); - return 0; + do{ + if (argc == 1) { + ax::Component* arg0; + ok &= luaval_to_object(tolua_S, 2, "ax.Component",&arg0, "ax.Node:removeComponent"); + + if (!ok) { break; } + bool ret = cobj->removeComponent(arg0); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; } - auto&& ret = cobj->getProgramState(); - object_to_luaval(tolua_S, "axb.ProgramState",(ax::backend::ProgramState*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getProgramState",argc, 0); + }while(0); + ok = true; + do{ + if (argc == 1) { + std::string_view arg0; + ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Node:removeComponent"); + + if (!ok) { break; } + bool ret = cobj->removeComponent(arg0); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; + } + }while(0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:removeComponent",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getProgramState'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_removeComponent'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_updateProgramStateTexture(lua_State* tolua_S) +int lua_ax_base_Node_removeAllComponents(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -11373,37 +11594,34 @@ int lua_ax_base_Node_updateProgramStateTexture(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_updateProgramStateTexture'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_removeAllComponents'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - ax::Texture2D* arg0; - - ok &= luaval_to_object(tolua_S, 2, "ax.Texture2D",&arg0, "ax.Node:updateProgramStateTexture"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_updateProgramStateTexture'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_removeAllComponents'", nullptr); return 0; } - cobj->updateProgramStateTexture(arg0); + cobj->removeAllComponents(); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:updateProgramStateTexture",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:removeAllComponents",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_updateProgramStateTexture'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_removeAllComponents'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_resetChild(lua_State* tolua_S) +int lua_ax_base_Node_getOpacity(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -11423,40 +11641,34 @@ int lua_ax_base_Node_resetChild(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_resetChild'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getOpacity'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 2) + if (argc == 0) { - ax::Node* arg0; - bool arg1; - - ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Node:resetChild"); - - ok &= luaval_to_boolean(tolua_S, 3,&arg1, "ax.Node:resetChild"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_resetChild'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getOpacity'", nullptr); return 0; } - cobj->resetChild(arg0, arg1); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getOpacity(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:resetChild",argc, 2); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getOpacity",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_resetChild'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getOpacity'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_init(lua_State* tolua_S) +int lua_ax_base_Node_getDisplayedOpacity(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -11476,7 +11688,7 @@ int lua_ax_base_Node_init(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_init'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getDisplayedOpacity'", nullptr); return 0; } #endif @@ -11486,24 +11698,24 @@ int lua_ax_base_Node_init(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_init'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getDisplayedOpacity'", nullptr); return 0; } - auto&& ret = cobj->init(); - tolua_pushboolean(tolua_S,(bool)ret); + auto&& ret = cobj->getDisplayedOpacity(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:init",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getDisplayedOpacity",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_init'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getDisplayedOpacity'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_initLayer(lua_State* tolua_S) +int lua_ax_base_Node_setOpacity(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -11523,34 +11735,37 @@ int lua_ax_base_Node_initLayer(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_initLayer'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setOpacity'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + uint16_t arg0; + + ok &= luaval_to_uint16(tolua_S, 2,&arg0, "ax.Node:setOpacity"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_initLayer'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setOpacity'", nullptr); return 0; } - auto&& ret = cobj->initLayer(); - tolua_pushboolean(tolua_S,(bool)ret); + cobj->setOpacity(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:initLayer",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setOpacity",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_initLayer'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setOpacity'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_setPhysicsBody(lua_State* tolua_S) +int lua_ax_base_Node_updateDisplayedOpacity(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -11570,7 +11785,7 @@ int lua_ax_base_Node_setPhysicsBody(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setPhysicsBody'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_updateDisplayedOpacity'", nullptr); return 0; } #endif @@ -11578,29 +11793,29 @@ int lua_ax_base_Node_setPhysicsBody(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::PhysicsBody* arg0; + uint16_t arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.PhysicsBody",&arg0, "ax.Node:setPhysicsBody"); + ok &= luaval_to_uint16(tolua_S, 2,&arg0, "ax.Node:updateDisplayedOpacity"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setPhysicsBody'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_updateDisplayedOpacity'", nullptr); return 0; } - cobj->setPhysicsBody(arg0); + cobj->updateDisplayedOpacity(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setPhysicsBody",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:updateDisplayedOpacity",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setPhysicsBody'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_updateDisplayedOpacity'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_getPhysicsBody(lua_State* tolua_S) +int lua_ax_base_Node_isCascadeOpacityEnabled(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -11620,7 +11835,7 @@ int lua_ax_base_Node_getPhysicsBody(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getPhysicsBody'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_isCascadeOpacityEnabled'", nullptr); return 0; } #endif @@ -11630,92 +11845,121 @@ int lua_ax_base_Node_getPhysicsBody(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getPhysicsBody'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_isCascadeOpacityEnabled'", nullptr); return 0; } - auto&& ret = cobj->getPhysicsBody(); - object_to_luaval(tolua_S, "ax.PhysicsBody",(ax::PhysicsBody*)ret); + auto&& ret = cobj->isCascadeOpacityEnabled(); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getPhysicsBody",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:isCascadeOpacityEnabled",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getPhysicsBody'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_isCascadeOpacityEnabled'.",&tolua_err); #endif return 0; } -int lua_ax_base_Node_create(lua_State* tolua_S) +int lua_ax_base_Node_setCascadeOpacityEnabled(lua_State* tolua_S) { int argc = 0; + ax::Node* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S) - 1; + cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); - if (argc == 0) +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setCascadeOpacityEnabled'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 1) { + bool arg0; + + ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.Node:setCascadeOpacityEnabled"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_create'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setCascadeOpacityEnabled'", nullptr); return 0; } - auto&& ret = ax::Node::create(); - object_to_luaval(tolua_S, "ax.Node",(ax::Node*)ret); + cobj->setCascadeOpacityEnabled(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Node:create",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setCascadeOpacityEnabled",argc, 1); return 0; + #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_create'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setCascadeOpacityEnabled'.",&tolua_err); #endif + return 0; } -int lua_ax_base_Node_getAttachedNodeCount(lua_State* tolua_S) +int lua_ax_base_Node_getColor(lua_State* tolua_S) { int argc = 0; + ax::Node* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S) - 1; + cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); - if (argc == 0) +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getColor'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getAttachedNodeCount'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getColor'", nullptr); return 0; } - auto&& ret = ax::Node::getAttachedNodeCount(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + auto&& ret = cobj->getColor(); + color3b_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Node:getAttachedNodeCount",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getColor",argc, 0); return 0; + #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getAttachedNodeCount'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getColor'.",&tolua_err); #endif + return 0; } -int lua_ax_base_Node_constructor(lua_State* tolua_S) +int lua_ax_base_Node_getDisplayedColor(lua_State* tolua_S) { int argc = 0; ax::Node* cobj = nullptr; @@ -11726,204 +11970,46 @@ int lua_ax_base_Node_constructor(lua_State* tolua_S) #endif +#if _AX_DEBUG >= 1 + if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; +#endif + + cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); + +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getDisplayedColor'", nullptr); + return 0; + } +#endif argc = lua_gettop(tolua_S)-1; if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_constructor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getDisplayedColor'", nullptr); return 0; } - cobj = new ax::Node(); - cobj->autorelease(); - int ID = (int)cobj->_ID ; - int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.Node"); + auto&& ret = cobj->getDisplayedColor(); + color3b_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:Node",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getDisplayedColor",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_constructor'.",&tolua_err); + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getDisplayedColor'.",&tolua_err); #endif return 0; } - -static int lua_ax_base_Node_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (Node)"); - return 0; -} - -int lua_register_ax_base_Node(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.Node"); - tolua_cclass(tolua_S,"Node","ax.Node","ax.Object",nullptr); - - tolua_beginmodule(tolua_S,"Node"); - tolua_function(tolua_S,"new",lua_ax_base_Node_constructor); - tolua_function(tolua_S,"getDescription",lua_ax_base_Node_getDescription); - tolua_function(tolua_S,"setLocalZOrder",lua_ax_base_Node_setLocalZOrder); - tolua_function(tolua_S,"updateOrderOfArrival",lua_ax_base_Node_updateOrderOfArrival); - tolua_function(tolua_S,"getLocalZOrder",lua_ax_base_Node_getLocalZOrder); - tolua_function(tolua_S,"setGlobalZOrder",lua_ax_base_Node_setGlobalZOrder); - tolua_function(tolua_S,"getGlobalZOrder",lua_ax_base_Node_getGlobalZOrder); - tolua_function(tolua_S,"setScaleX",lua_ax_base_Node_setScaleX); - tolua_function(tolua_S,"getScaleX",lua_ax_base_Node_getScaleX); - tolua_function(tolua_S,"setScaleY",lua_ax_base_Node_setScaleY); - tolua_function(tolua_S,"getScaleY",lua_ax_base_Node_getScaleY); - tolua_function(tolua_S,"setScaleZ",lua_ax_base_Node_setScaleZ); - tolua_function(tolua_S,"getScaleZ",lua_ax_base_Node_getScaleZ); - tolua_function(tolua_S,"setScale",lua_ax_base_Node_setScale); - tolua_function(tolua_S,"getScale",lua_ax_base_Node_getScale); - tolua_function(tolua_S,"setPosition",lua_ax_base_Node_setPosition); - tolua_function(tolua_S,"setPositionNormalized",lua_ax_base_Node_setPositionNormalized); - tolua_function(tolua_S,"setNormalizedPosition",lua_ax_base_Node_setNormalizedPosition); - tolua_function(tolua_S,"getPositionNormalized",lua_ax_base_Node_getPositionNormalized); - tolua_function(tolua_S,"getNormalizedPosition",lua_ax_base_Node_getNormalizedPosition); - tolua_function(tolua_S,"setPositionX",lua_ax_base_Node_setPositionX); - tolua_function(tolua_S,"getPositionX",lua_ax_base_Node_getPositionX); - tolua_function(tolua_S,"setPositionY",lua_ax_base_Node_setPositionY); - tolua_function(tolua_S,"getPositionY",lua_ax_base_Node_getPositionY); - tolua_function(tolua_S,"setPosition3D",lua_ax_base_Node_setPosition3D); - tolua_function(tolua_S,"getPosition3D",lua_ax_base_Node_getPosition3D); - tolua_function(tolua_S,"setPositionZ",lua_ax_base_Node_setPositionZ); - tolua_function(tolua_S,"getPositionZ",lua_ax_base_Node_getPositionZ); - tolua_function(tolua_S,"setSkewX",lua_ax_base_Node_setSkewX); - tolua_function(tolua_S,"getSkewX",lua_ax_base_Node_getSkewX); - tolua_function(tolua_S,"setSkewY",lua_ax_base_Node_setSkewY); - tolua_function(tolua_S,"getSkewY",lua_ax_base_Node_getSkewY); - tolua_function(tolua_S,"getAnchorPoint",lua_ax_base_Node_getAnchorPoint); - tolua_function(tolua_S,"getAnchorPointInPoints",lua_ax_base_Node_getAnchorPointInPoints); - tolua_function(tolua_S,"getContentSize",lua_ax_base_Node_getContentSize); - tolua_function(tolua_S,"hitTest",lua_ax_base_Node_hitTest); - tolua_function(tolua_S,"setVisible",lua_ax_base_Node_setVisible); - tolua_function(tolua_S,"isVisible",lua_ax_base_Node_isVisible); - tolua_function(tolua_S,"setRotation",lua_ax_base_Node_setRotation); - tolua_function(tolua_S,"getRotation",lua_ax_base_Node_getRotation); - tolua_function(tolua_S,"setRotation3D",lua_ax_base_Node_setRotation3D); - tolua_function(tolua_S,"getRotation3D",lua_ax_base_Node_getRotation3D); - tolua_function(tolua_S,"setRotationSkewX",lua_ax_base_Node_setRotationSkewX); - tolua_function(tolua_S,"getRotationSkewX",lua_ax_base_Node_getRotationSkewX); - tolua_function(tolua_S,"setRotationSkewY",lua_ax_base_Node_setRotationSkewY); - tolua_function(tolua_S,"getRotationSkewY",lua_ax_base_Node_getRotationSkewY); - tolua_function(tolua_S,"setIgnoreAnchorPointForPosition",lua_ax_base_Node_setIgnoreAnchorPointForPosition); - tolua_function(tolua_S,"isIgnoreAnchorPointForPosition",lua_ax_base_Node_isIgnoreAnchorPointForPosition); - tolua_function(tolua_S,"addChild",lua_ax_base_Node_addChild); - tolua_function(tolua_S,"getChildByTag",lua_ax_base_Node_getChildByTag); - tolua_function(tolua_S,"getChildByName",lua_ax_base_Node_getChildByName); - tolua_function(tolua_S,"getChildren",lua_ax_base_Node_getChildren); - tolua_function(tolua_S,"getChildrenCount",lua_ax_base_Node_getChildrenCount); - tolua_function(tolua_S,"setParent",lua_ax_base_Node_setParent); - tolua_function(tolua_S,"getParent",lua_ax_base_Node_getParent); - tolua_function(tolua_S,"removeFromParent",lua_ax_base_Node_removeFromParentAndCleanup); - tolua_function(tolua_S,"removeChild",lua_ax_base_Node_removeChild); - tolua_function(tolua_S,"removeChildByTag",lua_ax_base_Node_removeChildByTag); - tolua_function(tolua_S,"removeChildByName",lua_ax_base_Node_removeChildByName); - tolua_function(tolua_S,"removeAllChildren",lua_ax_base_Node_removeAllChildrenWithCleanup); - tolua_function(tolua_S,"reorderChild",lua_ax_base_Node_reorderChild); - tolua_function(tolua_S,"sortAllChildren",lua_ax_base_Node_sortAllChildren); - tolua_function(tolua_S,"getTag",lua_ax_base_Node_getTag); - tolua_function(tolua_S,"setTag",lua_ax_base_Node_setTag); - tolua_function(tolua_S,"getName",lua_ax_base_Node_getName); - tolua_function(tolua_S,"setName",lua_ax_base_Node_setName); - tolua_function(tolua_S,"setUserObject",lua_ax_base_Node_setUserObject); - tolua_function(tolua_S,"isRunning",lua_ax_base_Node_isRunning); - tolua_function(tolua_S,"cleanup",lua_ax_base_Node_cleanup); - tolua_function(tolua_S,"draw",lua_ax_base_Node_draw); - tolua_function(tolua_S,"visit",lua_ax_base_Node_visit); - tolua_function(tolua_S,"getScene",lua_ax_base_Node_getScene); - tolua_function(tolua_S,"getBoundingBox",lua_ax_base_Node_getBoundingBox); - tolua_function(tolua_S,"setEventDispatcher",lua_ax_base_Node_setEventDispatcher); - tolua_function(tolua_S,"getEventDispatcher",lua_ax_base_Node_getEventDispatcher); - tolua_function(tolua_S,"setActionManager",lua_ax_base_Node_setActionManager); - tolua_function(tolua_S,"getActionManager",lua_ax_base_Node_getActionManager); - tolua_function(tolua_S,"runAction",lua_ax_base_Node_runAction); - tolua_function(tolua_S,"stopAllActions",lua_ax_base_Node_stopAllActions); - tolua_function(tolua_S,"stopAction",lua_ax_base_Node_stopAction); - tolua_function(tolua_S,"stopActionByTag",lua_ax_base_Node_stopActionByTag); - tolua_function(tolua_S,"stopAllActionsByTag",lua_ax_base_Node_stopAllActionsByTag); - tolua_function(tolua_S,"stopActionsByFlags",lua_ax_base_Node_stopActionsByFlags); - tolua_function(tolua_S,"getActionByTag",lua_ax_base_Node_getActionByTag); - tolua_function(tolua_S,"getNumberOfRunningActions",lua_ax_base_Node_getNumberOfRunningActions); - tolua_function(tolua_S,"getNumberOfRunningActionsByTag",lua_ax_base_Node_getNumberOfRunningActionsByTag); - tolua_function(tolua_S,"setScheduler",lua_ax_base_Node_setScheduler); - tolua_function(tolua_S,"getScheduler",lua_ax_base_Node_getScheduler); - tolua_function(tolua_S,"isScheduled",lua_ax_base_Node_isScheduled); - tolua_function(tolua_S,"resume",lua_ax_base_Node_resume); - tolua_function(tolua_S,"pause",lua_ax_base_Node_pause); - tolua_function(tolua_S,"update",lua_ax_base_Node_update); - tolua_function(tolua_S,"updateTransform",lua_ax_base_Node_updateTransform); - tolua_function(tolua_S,"getNodeToParentTransform",lua_ax_base_Node_getNodeToParentTransform); - tolua_function(tolua_S,"getNodeToParentAffineTransform",lua_ax_base_Node_getNodeToParentAffineTransform); - tolua_function(tolua_S,"setNodeToParentTransform",lua_ax_base_Node_setNodeToParentTransform); - tolua_function(tolua_S,"getParentToNodeTransform",lua_ax_base_Node_getParentToNodeTransform); - tolua_function(tolua_S,"getParentToNodeAffineTransform",lua_ax_base_Node_getParentToNodeAffineTransform); - tolua_function(tolua_S,"getNodeToWorldTransform",lua_ax_base_Node_getNodeToWorldTransform); - tolua_function(tolua_S,"getNodeToWorldAffineTransform",lua_ax_base_Node_getNodeToWorldAffineTransform); - tolua_function(tolua_S,"getWorldToNodeTransform",lua_ax_base_Node_getWorldToNodeTransform); - tolua_function(tolua_S,"getWorldToNodeAffineTransform",lua_ax_base_Node_getWorldToNodeAffineTransform); - tolua_function(tolua_S,"convertToNodeSpace",lua_ax_base_Node_convertToNodeSpace); - tolua_function(tolua_S,"convertToWorldSpace",lua_ax_base_Node_convertToWorldSpace); - tolua_function(tolua_S,"convertToNodeSpaceAR",lua_ax_base_Node_convertToNodeSpaceAR); - tolua_function(tolua_S,"convertToWorldSpaceAR",lua_ax_base_Node_convertToWorldSpaceAR); - tolua_function(tolua_S,"convertTouchToNodeSpace",lua_ax_base_Node_convertTouchToNodeSpace); - tolua_function(tolua_S,"convertTouchToNodeSpaceAR",lua_ax_base_Node_convertTouchToNodeSpaceAR); - tolua_function(tolua_S,"getWorldPosition",lua_ax_base_Node_getWorldPosition); - tolua_function(tolua_S,"setWorldPosition",lua_ax_base_Node_setWorldPosition); - tolua_function(tolua_S,"getComponent",lua_ax_base_Node_getComponent); - tolua_function(tolua_S,"addComponent",lua_ax_base_Node_addComponent); - tolua_function(tolua_S,"removeComponent",lua_ax_base_Node_removeComponent); - tolua_function(tolua_S,"removeAllComponents",lua_ax_base_Node_removeAllComponents); - tolua_function(tolua_S,"getOpacity",lua_ax_base_Node_getOpacity); - tolua_function(tolua_S,"getDisplayedOpacity",lua_ax_base_Node_getDisplayedOpacity); - tolua_function(tolua_S,"setOpacity",lua_ax_base_Node_setOpacity); - tolua_function(tolua_S,"updateDisplayedOpacity",lua_ax_base_Node_updateDisplayedOpacity); - tolua_function(tolua_S,"isCascadeOpacityEnabled",lua_ax_base_Node_isCascadeOpacityEnabled); - tolua_function(tolua_S,"setCascadeOpacityEnabled",lua_ax_base_Node_setCascadeOpacityEnabled); - tolua_function(tolua_S,"getColor",lua_ax_base_Node_getColor); - tolua_function(tolua_S,"getDisplayedColor",lua_ax_base_Node_getDisplayedColor); - tolua_function(tolua_S,"setColor",lua_ax_base_Node_setColor); - tolua_function(tolua_S,"updateDisplayedColor",lua_ax_base_Node_updateDisplayedColor); - tolua_function(tolua_S,"isCascadeColorEnabled",lua_ax_base_Node_isCascadeColorEnabled); - tolua_function(tolua_S,"setCascadeColorEnabled",lua_ax_base_Node_setCascadeColorEnabled); - tolua_function(tolua_S,"setOpacityModifyRGB",lua_ax_base_Node_setOpacityModifyRGB); - tolua_function(tolua_S,"isOpacityModifyRGB",lua_ax_base_Node_isOpacityModifyRGB); - tolua_function(tolua_S,"setOnEnterCallback",lua_ax_base_Node_setOnEnterCallback); - tolua_function(tolua_S,"setOnExitCallback",lua_ax_base_Node_setOnExitCallback); - tolua_function(tolua_S,"setOnEnterTransitionDidFinishCallback",lua_ax_base_Node_setOnEnterTransitionDidFinishCallback); - tolua_function(tolua_S,"getOnEnterTransitionDidFinishCallback",lua_ax_base_Node_getOnEnterTransitionDidFinishCallback); - tolua_function(tolua_S,"setOnExitTransitionDidStartCallback",lua_ax_base_Node_setOnExitTransitionDidStartCallback); - tolua_function(tolua_S,"getOnExitTransitionDidStartCallback",lua_ax_base_Node_getOnExitTransitionDidStartCallback); - tolua_function(tolua_S,"getCameraMask",lua_ax_base_Node_getCameraMask); - tolua_function(tolua_S,"setCameraMask",lua_ax_base_Node_setCameraMask); - tolua_function(tolua_S,"applyMaskOnEnter",lua_ax_base_Node_applyMaskOnEnter); - tolua_function(tolua_S,"setProgramState",lua_ax_base_Node_setProgramState); - tolua_function(tolua_S,"setProgramStateWithRegistry",lua_ax_base_Node_setProgramStateWithRegistry); - tolua_function(tolua_S,"setProgramStateByProgramId",lua_ax_base_Node_setProgramStateByProgramId); - tolua_function(tolua_S,"getProgramState",lua_ax_base_Node_getProgramState); - tolua_function(tolua_S,"updateProgramStateTexture",lua_ax_base_Node_updateProgramStateTexture); - tolua_function(tolua_S,"resetChild",lua_ax_base_Node_resetChild); - tolua_function(tolua_S,"init",lua_ax_base_Node_init); - tolua_function(tolua_S,"initLayer",lua_ax_base_Node_initLayer); - tolua_function(tolua_S,"setPhysicsBody",lua_ax_base_Node_setPhysicsBody); - tolua_function(tolua_S,"getPhysicsBody",lua_ax_base_Node_getPhysicsBody); - tolua_function(tolua_S,"create", lua_ax_base_Node_create); - tolua_function(tolua_S,"getAttachedNodeCount", lua_ax_base_Node_getAttachedNodeCount); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::Node).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.Node"; - g_typeCast[typeName] = "ax.Node"; - return 1; -} - -int lua_ax_base_Scene_getDefaultCamera(lua_State* tolua_S) +int lua_ax_base_Node_setColor(lua_State* tolua_S) { int argc = 0; - ax::Scene* cobj = nullptr; + ax::Node* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -11932,45 +12018,48 @@ int lua_ax_base_Scene_getDefaultCamera(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Scene",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Scene*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Scene_getDefaultCamera'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setColor'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + ax::Color3B arg0; + + ok &= luaval_to_color3b(tolua_S, 2, &arg0, "ax.Node:setColor"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scene_getDefaultCamera'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setColor'", nullptr); return 0; } - auto&& ret = cobj->getDefaultCamera(); - object_to_luaval(tolua_S, "ax.Camera",(ax::Camera*)ret); + cobj->setColor(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Scene:getDefaultCamera",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setColor",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Scene_getDefaultCamera'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setColor'.",&tolua_err); #endif return 0; } -int lua_ax_base_Scene_render(lua_State* tolua_S) +int lua_ax_base_Node_updateDisplayedColor(lua_State* tolua_S) { int argc = 0; - ax::Scene* cobj = nullptr; + ax::Node* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -11979,71 +12068,48 @@ int lua_ax_base_Scene_render(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Scene",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Scene*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Scene_render'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_updateDisplayedColor'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 2) - { - ax::Renderer* arg0; - ax::Mat4 arg1; - - ok &= luaval_to_object(tolua_S, 2, "ax.Renderer",&arg0, "ax.Scene:render"); - - ok &= luaval_to_mat4(tolua_S, 3, &arg1, "ax.Scene:render"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scene_render'", nullptr); - return 0; - } - cobj->render(arg0, arg1); - lua_settop(tolua_S, 1); - return 1; - } - if (argc == 3) + if (argc == 1) { - ax::Renderer* arg0; - ax::Mat4 arg1; - const ax::Mat4* arg2; - - ok &= luaval_to_object(tolua_S, 2, "ax.Renderer",&arg0, "ax.Scene:render"); - - ok &= luaval_to_mat4(tolua_S, 3, &arg1, "ax.Scene:render"); + ax::Color3B arg0; - ok &= luaval_to_object(tolua_S, 4, "ax.Mat4",&arg2, "ax.Scene:render"); + ok &= luaval_to_color3b(tolua_S, 2, &arg0, "ax.Node:updateDisplayedColor"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scene_render'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_updateDisplayedColor'", nullptr); return 0; } - cobj->render(arg0, arg1, arg2); + cobj->updateDisplayedColor(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Scene:render",argc, 2); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:updateDisplayedColor",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Scene_render'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_updateDisplayedColor'.",&tolua_err); #endif return 0; } -int lua_ax_base_Scene_initWithSize(lua_State* tolua_S) +int lua_ax_base_Node_isCascadeColorEnabled(lua_State* tolua_S) { int argc = 0; - ax::Scene* cobj = nullptr; + ax::Node* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -12052,48 +12118,45 @@ int lua_ax_base_Scene_initWithSize(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Scene",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Scene*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Scene_initWithSize'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_isCascadeColorEnabled'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - ax::Vec2 arg0; - - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.Scene:initWithSize"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scene_initWithSize'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_isCascadeColorEnabled'", nullptr); return 0; } - auto&& ret = cobj->initWithSize(arg0); + auto&& ret = cobj->isCascadeColorEnabled(); tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Scene:initWithSize",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:isCascadeColorEnabled",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Scene_initWithSize'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_isCascadeColorEnabled'.",&tolua_err); #endif return 0; } -int lua_ax_base_Scene_setCameraOrderDirty(lua_State* tolua_S) +int lua_ax_base_Node_setCascadeColorEnabled(lua_State* tolua_S) { int argc = 0; - ax::Scene* cobj = nullptr; + ax::Node* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -12102,45 +12165,48 @@ int lua_ax_base_Scene_setCameraOrderDirty(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Scene",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Scene*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Scene_setCameraOrderDirty'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setCascadeColorEnabled'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + bool arg0; + + ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.Node:setCascadeColorEnabled"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scene_setCameraOrderDirty'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setCascadeColorEnabled'", nullptr); return 0; } - cobj->setCameraOrderDirty(); + cobj->setCascadeColorEnabled(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Scene:setCameraOrderDirty",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setCascadeColorEnabled",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Scene_setCameraOrderDirty'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setCascadeColorEnabled'.",&tolua_err); #endif return 0; } -int lua_ax_base_Scene_onProjectionChanged(lua_State* tolua_S) +int lua_ax_base_Node_setOpacityModifyRGB(lua_State* tolua_S) { int argc = 0; - ax::Scene* cobj = nullptr; + ax::Node* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -12149,15 +12215,15 @@ int lua_ax_base_Scene_onProjectionChanged(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Scene",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Scene*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Scene_onProjectionChanged'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setOpacityModifyRGB'", nullptr); return 0; } #endif @@ -12165,32 +12231,32 @@ int lua_ax_base_Scene_onProjectionChanged(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::EventCustom* arg0; + bool arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.EventCustom",&arg0, "ax.Scene:onProjectionChanged"); + ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.Node:setOpacityModifyRGB"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scene_onProjectionChanged'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setOpacityModifyRGB'", nullptr); return 0; } - cobj->onProjectionChanged(arg0); + cobj->setOpacityModifyRGB(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Scene:onProjectionChanged",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setOpacityModifyRGB",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Scene_onProjectionChanged'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setOpacityModifyRGB'.",&tolua_err); #endif return 0; } -int lua_ax_base_Scene_getPhysicsWorld(lua_State* tolua_S) +int lua_ax_base_Node_isOpacityModifyRGB(lua_State* tolua_S) { int argc = 0; - ax::Scene* cobj = nullptr; + ax::Node* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -12199,15 +12265,15 @@ int lua_ax_base_Scene_getPhysicsWorld(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Scene",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Scene*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Scene_getPhysicsWorld'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_isOpacityModifyRGB'", nullptr); return 0; } #endif @@ -12217,27 +12283,27 @@ int lua_ax_base_Scene_getPhysicsWorld(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scene_getPhysicsWorld'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_isOpacityModifyRGB'", nullptr); return 0; } - auto&& ret = cobj->getPhysicsWorld(); - object_to_luaval(tolua_S, "ax.PhysicsWorld",(ax::PhysicsWorld*)ret); + auto&& ret = cobj->isOpacityModifyRGB(); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Scene:getPhysicsWorld",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:isOpacityModifyRGB",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Scene_getPhysicsWorld'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_isOpacityModifyRGB'.",&tolua_err); #endif return 0; } -int lua_ax_base_Scene_initWithPhysics(lua_State* tolua_S) +int lua_ax_base_Node_setOnEnterCallback(lua_State* tolua_S) { int argc = 0; - ax::Scene* cobj = nullptr; + ax::Node* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -12246,45 +12312,52 @@ int lua_ax_base_Scene_initWithPhysics(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Scene",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Scene*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Scene_initWithPhysics'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setOnEnterCallback'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + std::function arg0; + + do { + // Lambda binding for lua is not supported. + assert(false); + } while(0) + ; if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scene_initWithPhysics'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setOnEnterCallback'", nullptr); return 0; } - auto&& ret = cobj->initWithPhysics(); - tolua_pushboolean(tolua_S,(bool)ret); + cobj->setOnEnterCallback(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Scene:initWithPhysics",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setOnEnterCallback",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Scene_initWithPhysics'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setOnEnterCallback'.",&tolua_err); #endif return 0; } -int lua_ax_base_Scene_initPhysicsWorld(lua_State* tolua_S) +int lua_ax_base_Node_setOnExitCallback(lua_State* tolua_S) { int argc = 0; - ax::Scene* cobj = nullptr; + ax::Node* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -12293,45 +12366,52 @@ int lua_ax_base_Scene_initPhysicsWorld(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Scene",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Scene*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Scene_initPhysicsWorld'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setOnExitCallback'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + std::function arg0; + + do { + // Lambda binding for lua is not supported. + assert(false); + } while(0) + ; if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scene_initPhysicsWorld'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setOnExitCallback'", nullptr); return 0; } - auto&& ret = cobj->initPhysicsWorld(); - tolua_pushboolean(tolua_S,(bool)ret); + cobj->setOnExitCallback(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Scene:initPhysicsWorld",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setOnExitCallback",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Scene_initPhysicsWorld'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setOnExitCallback'.",&tolua_err); #endif return 0; } -int lua_ax_base_Scene_fixedUpdate(lua_State* tolua_S) +int lua_ax_base_Node_setOnEnterTransitionDidFinishCallback(lua_State* tolua_S) { int argc = 0; - ax::Scene* cobj = nullptr; + ax::Node* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -12340,15 +12420,15 @@ int lua_ax_base_Scene_fixedUpdate(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Scene",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Scene*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Scene_fixedUpdate'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setOnEnterTransitionDidFinishCallback'", nullptr); return 0; } #endif @@ -12356,32 +12436,36 @@ int lua_ax_base_Scene_fixedUpdate(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - double arg0; + std::function arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Scene:fixedUpdate"); + do { + // Lambda binding for lua is not supported. + assert(false); + } while(0) + ; if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scene_fixedUpdate'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setOnEnterTransitionDidFinishCallback'", nullptr); return 0; } - cobj->fixedUpdate(arg0); + cobj->setOnEnterTransitionDidFinishCallback(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Scene:fixedUpdate",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setOnEnterTransitionDidFinishCallback",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Scene_fixedUpdate'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setOnEnterTransitionDidFinishCallback'.",&tolua_err); #endif return 0; } -int lua_ax_base_Scene_stepPhysicsAndNavigation(lua_State* tolua_S) +int lua_ax_base_Node_getOnEnterTransitionDidFinishCallback(lua_State* tolua_S) { int argc = 0; - ax::Scene* cobj = nullptr; + ax::Node* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -12390,222 +12474,99 @@ int lua_ax_base_Scene_stepPhysicsAndNavigation(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Scene",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Scene*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Scene_stepPhysicsAndNavigation'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getOnEnterTransitionDidFinishCallback'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Scene:stepPhysicsAndNavigation"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scene_stepPhysicsAndNavigation'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getOnEnterTransitionDidFinishCallback'", nullptr); return 0; } - cobj->stepPhysicsAndNavigation(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getOnEnterTransitionDidFinishCallback(); + #pragma warning NO CONVERSION FROM NATIVE FOR std::function; return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Scene:stepPhysicsAndNavigation",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getOnEnterTransitionDidFinishCallback",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Scene_stepPhysicsAndNavigation'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_base_Scene_create(lua_State* tolua_S) -{ - int argc = 0; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.Scene",0,&tolua_err)) goto tolua_lerror; + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getOnEnterTransitionDidFinishCallback'.",&tolua_err); #endif - argc = lua_gettop(tolua_S) - 1; - - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scene_create'", nullptr); - return 0; - } - auto&& ret = ax::Scene::create(); - object_to_luaval(tolua_S, "ax.Scene",(ax::Scene*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Scene:create",argc, 0); - return 0; -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Scene_create'.",&tolua_err); -#endif return 0; } -int lua_ax_base_Scene_createWithSize(lua_State* tolua_S) +int lua_ax_base_Node_setOnExitTransitionDidStartCallback(lua_State* tolua_S) { int argc = 0; + ax::Node* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif -#if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.Scene",0,&tolua_err)) goto tolua_lerror; -#endif - - argc = lua_gettop(tolua_S) - 1; - if (argc == 1) - { - ax::Vec2 arg0; - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.Scene:createWithSize"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scene_createWithSize'", nullptr); - return 0; - } - auto&& ret = ax::Scene::createWithSize(arg0); - object_to_luaval(tolua_S, "ax.Scene",(ax::Scene*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Scene:createWithSize",argc, 1); - return 0; #if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Scene_createWithSize'.",&tolua_err); + if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - return 0; -} -int lua_ax_base_Scene_createWithPhysics(lua_State* tolua_S) -{ - int argc = 0; - bool ok = true; -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif + cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.Scene",0,&tolua_err)) goto tolua_lerror; -#endif - - argc = lua_gettop(tolua_S) - 1; - - if (argc == 0) + if (!cobj) { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scene_createWithPhysics'", nullptr); - return 0; - } - auto&& ret = ax::Scene::createWithPhysics(); - object_to_luaval(tolua_S, "ax.Scene",(ax::Scene*)ret); - return 1; + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setOnExitTransitionDidStartCallback'", nullptr); + return 0; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Scene:createWithPhysics",argc, 0); - return 0; -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Scene_createWithPhysics'.",&tolua_err); -#endif - return 0; -} -int lua_ax_base_Scene_constructor(lua_State* tolua_S) -{ - int argc = 0; - ax::Scene* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; #endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + std::function arg0; + + do { + // Lambda binding for lua is not supported. + assert(false); + } while(0) + ; if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scene_constructor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setOnExitTransitionDidStartCallback'", nullptr); return 0; } - cobj = new ax::Scene(); - cobj->autorelease(); - int ID = (int)cobj->_ID ; - int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.Scene"); + cobj->setOnExitTransitionDidStartCallback(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Scene:Scene",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setOnExitTransitionDidStartCallback",argc, 1); return 0; #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Scene_constructor'.",&tolua_err); + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setOnExitTransitionDidStartCallback'.",&tolua_err); #endif return 0; } - -static int lua_ax_base_Scene_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (Scene)"); - return 0; -} - -int lua_register_ax_base_Scene(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.Scene"); - tolua_cclass(tolua_S,"Scene","ax.Scene","ax.Node",nullptr); - - tolua_beginmodule(tolua_S,"Scene"); - tolua_function(tolua_S,"new",lua_ax_base_Scene_constructor); - tolua_function(tolua_S,"getDefaultCamera",lua_ax_base_Scene_getDefaultCamera); - tolua_function(tolua_S,"render",lua_ax_base_Scene_render); - tolua_function(tolua_S,"initWithSize",lua_ax_base_Scene_initWithSize); - tolua_function(tolua_S,"setCameraOrderDirty",lua_ax_base_Scene_setCameraOrderDirty); - tolua_function(tolua_S,"onProjectionChanged",lua_ax_base_Scene_onProjectionChanged); - tolua_function(tolua_S,"getPhysicsWorld",lua_ax_base_Scene_getPhysicsWorld); - tolua_function(tolua_S,"initWithPhysics",lua_ax_base_Scene_initWithPhysics); - tolua_function(tolua_S,"initPhysicsWorld",lua_ax_base_Scene_initPhysicsWorld); - tolua_function(tolua_S,"fixedUpdate",lua_ax_base_Scene_fixedUpdate); - tolua_function(tolua_S,"stepPhysicsAndNavigation",lua_ax_base_Scene_stepPhysicsAndNavigation); - tolua_function(tolua_S,"create", lua_ax_base_Scene_create); - tolua_function(tolua_S,"createWithSize", lua_ax_base_Scene_createWithSize); - tolua_function(tolua_S,"createWithPhysics", lua_ax_base_Scene_createWithPhysics); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::Scene).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.Scene"; - g_typeCast[typeName] = "ax.Scene"; - return 1; -} - -int lua_ax_base_GLView_end(lua_State* tolua_S) +int lua_ax_base_Node_getOnExitTransitionDidStartCallback(lua_State* tolua_S) { int argc = 0; - ax::GLView* cobj = nullptr; + ax::Node* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -12614,15 +12575,15 @@ int lua_ax_base_GLView_end(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_end'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getOnExitTransitionDidStartCallback'", nullptr); return 0; } #endif @@ -12632,27 +12593,27 @@ int lua_ax_base_GLView_end(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_end'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getOnExitTransitionDidStartCallback'", nullptr); return 0; } - cobj->end(); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getOnExitTransitionDidStartCallback(); + #pragma warning NO CONVERSION FROM NATIVE FOR std::function; return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:end",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getOnExitTransitionDidStartCallback",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_end'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getOnExitTransitionDidStartCallback'.",&tolua_err); #endif return 0; } -int lua_ax_base_GLView_isOpenGLReady(lua_State* tolua_S) +int lua_ax_base_Node_getCameraMask(lua_State* tolua_S) { int argc = 0; - ax::GLView* cobj = nullptr; + ax::Node* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -12661,15 +12622,15 @@ int lua_ax_base_GLView_isOpenGLReady(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_isOpenGLReady'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getCameraMask'", nullptr); return 0; } #endif @@ -12679,27 +12640,27 @@ int lua_ax_base_GLView_isOpenGLReady(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_isOpenGLReady'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getCameraMask'", nullptr); return 0; } - auto&& ret = cobj->isOpenGLReady(); - tolua_pushboolean(tolua_S,(bool)ret); + auto&& ret = cobj->getCameraMask(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:isOpenGLReady",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getCameraMask",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_isOpenGLReady'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getCameraMask'.",&tolua_err); #endif return 0; } -int lua_ax_base_GLView_swapBuffers(lua_State* tolua_S) +int lua_ax_base_Node_setCameraMask(lua_State* tolua_S) { int argc = 0; - ax::GLView* cobj = nullptr; + ax::Node* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -12708,45 +12669,65 @@ int lua_ax_base_GLView_swapBuffers(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_swapBuffers'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setCameraMask'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + unsigned short arg0; + + ok &= luaval_to_ushort(tolua_S, 2, &arg0, "ax.Node:setCameraMask"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_swapBuffers'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setCameraMask'", nullptr); return 0; } - cobj->swapBuffers(); + cobj->setCameraMask(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:swapBuffers",argc, 0); + if (argc == 2) + { + unsigned short arg0; + bool arg1; + + ok &= luaval_to_ushort(tolua_S, 2, &arg0, "ax.Node:setCameraMask"); + + ok &= luaval_to_boolean(tolua_S, 3,&arg1, "ax.Node:setCameraMask"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setCameraMask'", nullptr); + return 0; + } + cobj->setCameraMask(arg0, arg1); + lua_settop(tolua_S, 1); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setCameraMask",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_swapBuffers'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setCameraMask'.",&tolua_err); #endif return 0; } -int lua_ax_base_GLView_setIMEKeyboardState(lua_State* tolua_S) +int lua_ax_base_Node_applyMaskOnEnter(lua_State* tolua_S) { int argc = 0; - ax::GLView* cobj = nullptr; + ax::Node* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -12755,15 +12736,15 @@ int lua_ax_base_GLView_setIMEKeyboardState(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_setIMEKeyboardState'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_applyMaskOnEnter'", nullptr); return 0; } #endif @@ -12773,77 +12754,101 @@ int lua_ax_base_GLView_setIMEKeyboardState(lua_State* tolua_S) { bool arg0; - ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.GLView:setIMEKeyboardState"); + ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.Node:applyMaskOnEnter"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_setIMEKeyboardState'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_applyMaskOnEnter'", nullptr); return 0; } - cobj->setIMEKeyboardState(arg0); + cobj->applyMaskOnEnter(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:setIMEKeyboardState",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:applyMaskOnEnter",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_setIMEKeyboardState'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_applyMaskOnEnter'.",&tolua_err); #endif return 0; } -int lua_ax_base_GLView_windowShouldClose(lua_State* tolua_S) +int lua_ax_base_Node_setProgramState(lua_State* tolua_S) { int argc = 0; - ax::GLView* cobj = nullptr; + ax::Node* cobj = nullptr; bool ok = true; - #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - - cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); - + cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_windowShouldClose'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setProgramState'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_windowShouldClose'", nullptr); - return 0; + do{ + if (argc == 1) { + ax::backend::ProgramState* arg0; + ok &= luaval_to_object(tolua_S, 2, "axb.ProgramState",&arg0, "ax.Node:setProgramState"); + + if (!ok) { break; } + bool ret = cobj->setProgramState(arg0); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; } - auto&& ret = cobj->windowShouldClose(); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:windowShouldClose",argc, 0); + }while(0); + ok = true; + do{ + if (argc == 2) { + ax::backend::ProgramState* arg0; + ok &= luaval_to_object(tolua_S, 2, "axb.ProgramState",&arg0, "ax.Node:setProgramState"); + + if (!ok) { break; } + bool arg1; + ok &= luaval_to_boolean(tolua_S, 3,&arg1, "ax.Node:setProgramState"); + + if (!ok) { break; } + bool ret = cobj->setProgramState(arg0, arg1); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; + } + }while(0); + ok = true; + do{ + if (argc == 1) { + unsigned int arg0; + ok &= luaval_to_uint32(tolua_S, 2,&arg0, "ax.Node:setProgramState"); + + if (!ok) { break; } + cobj->setProgramState(arg0); + lua_settop(tolua_S, 1); + return 1; + } + }while(0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setProgramState",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_windowShouldClose'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setProgramState'.",&tolua_err); #endif return 0; } -int lua_ax_base_GLView_pollEvents(lua_State* tolua_S) +int lua_ax_base_Node_setProgramStateWithRegistry(lua_State* tolua_S) { int argc = 0; - ax::GLView* cobj = nullptr; + ax::Node* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -12852,45 +12857,51 @@ int lua_ax_base_GLView_pollEvents(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_pollEvents'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setProgramStateWithRegistry'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 2) { + unsigned int arg0; + ax::Texture2D* arg1; + + ok &= luaval_to_uint32(tolua_S, 2,&arg0, "ax.Node:setProgramStateWithRegistry"); + + ok &= luaval_to_object(tolua_S, 3, "ax.Texture2D",&arg1, "ax.Node:setProgramStateWithRegistry"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_pollEvents'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setProgramStateWithRegistry'", nullptr); return 0; } - cobj->pollEvents(); + cobj->setProgramStateWithRegistry(arg0, arg1); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:pollEvents",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setProgramStateWithRegistry",argc, 2); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_pollEvents'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setProgramStateWithRegistry'.",&tolua_err); #endif return 0; } -int lua_ax_base_GLView_getFrameSize(lua_State* tolua_S) +int lua_ax_base_Node_setProgramStateByProgramId(lua_State* tolua_S) { int argc = 0; - ax::GLView* cobj = nullptr; + ax::Node* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -12899,45 +12910,48 @@ int lua_ax_base_GLView_getFrameSize(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_getFrameSize'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_setProgramStateByProgramId'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + unsigned long long arg0; + + ok &= luaval_to_long_long(tolua_S, 2,(long long*)&arg0, "ax.Node:setProgramStateByProgramId"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_getFrameSize'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_setProgramStateByProgramId'", nullptr); return 0; } - auto&& ret = cobj->getFrameSize(); - vec2_to_luaval(tolua_S, ret); + auto&& ret = cobj->setProgramStateByProgramId(arg0); + object_to_luaval(tolua_S, "axb.ProgramState",(ax::backend::ProgramState*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:getFrameSize",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:setProgramStateByProgramId",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_getFrameSize'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_setProgramStateByProgramId'.",&tolua_err); #endif return 0; } -int lua_ax_base_GLView_setFrameSize(lua_State* tolua_S) +int lua_ax_base_Node_getProgramState(lua_State* tolua_S) { int argc = 0; - ax::GLView* cobj = nullptr; + ax::Node* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -12946,51 +12960,45 @@ int lua_ax_base_GLView_setFrameSize(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_setFrameSize'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_getProgramState'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 2) + if (argc == 0) { - double arg0; - double arg1; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.GLView:setFrameSize"); - - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.GLView:setFrameSize"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_setFrameSize'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getProgramState'", nullptr); return 0; } - cobj->setFrameSize(arg0, arg1); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getProgramState(); + object_to_luaval(tolua_S, "axb.ProgramState",(ax::backend::ProgramState*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:setFrameSize",argc, 2); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:getProgramState",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_setFrameSize'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getProgramState'.",&tolua_err); #endif return 0; } -int lua_ax_base_GLView_setFrameZoomFactor(lua_State* tolua_S) +int lua_ax_base_Node_updateProgramStateTexture(lua_State* tolua_S) { int argc = 0; - ax::GLView* cobj = nullptr; + ax::Node* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -12999,15 +13007,15 @@ int lua_ax_base_GLView_setFrameZoomFactor(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_setFrameZoomFactor'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_updateProgramStateTexture'", nullptr); return 0; } #endif @@ -13015,32 +13023,32 @@ int lua_ax_base_GLView_setFrameZoomFactor(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - double arg0; + ax::Texture2D* arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.GLView:setFrameZoomFactor"); + ok &= luaval_to_object(tolua_S, 2, "ax.Texture2D",&arg0, "ax.Node:updateProgramStateTexture"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_setFrameZoomFactor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_updateProgramStateTexture'", nullptr); return 0; } - cobj->setFrameZoomFactor(arg0); + cobj->updateProgramStateTexture(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:setFrameZoomFactor",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:updateProgramStateTexture",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_setFrameZoomFactor'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_updateProgramStateTexture'.",&tolua_err); #endif return 0; } -int lua_ax_base_GLView_getFrameZoomFactor(lua_State* tolua_S) +int lua_ax_base_Node_resetChild(lua_State* tolua_S) { int argc = 0; - ax::GLView* cobj = nullptr; + ax::Node* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -13049,45 +13057,51 @@ int lua_ax_base_GLView_getFrameZoomFactor(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_getFrameZoomFactor'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_resetChild'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 2) { + ax::Node* arg0; + bool arg1; + + ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Node:resetChild"); + + ok &= luaval_to_boolean(tolua_S, 3,&arg1, "ax.Node:resetChild"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_getFrameZoomFactor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_resetChild'", nullptr); return 0; } - auto&& ret = cobj->getFrameZoomFactor(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + cobj->resetChild(arg0, arg1); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:getFrameZoomFactor",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:resetChild",argc, 2); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_getFrameZoomFactor'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_resetChild'.",&tolua_err); #endif return 0; } -int lua_ax_base_GLView_setCursorVisible(lua_State* tolua_S) +int lua_ax_base_Node_init(lua_State* tolua_S) { int argc = 0; - ax::GLView* cobj = nullptr; + ax::Node* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -13096,48 +13110,45 @@ int lua_ax_base_GLView_setCursorVisible(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_setCursorVisible'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_init'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - bool arg0; - - ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.GLView:setCursorVisible"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_setCursorVisible'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_init'", nullptr); return 0; } - cobj->setCursorVisible(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->init(); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:setCursorVisible",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:init",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_setCursorVisible'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_init'.",&tolua_err); #endif return 0; } -int lua_ax_base_GLView_getRetinaFactor(lua_State* tolua_S) +int lua_ax_base_Node_initLayer(lua_State* tolua_S) { int argc = 0; - ax::GLView* cobj = nullptr; + ax::Node* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -13146,15 +13157,15 @@ int lua_ax_base_GLView_getRetinaFactor(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Node*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_getRetinaFactor'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Node_initLayer'", nullptr); return 0; } #endif @@ -13164,124 +13175,298 @@ int lua_ax_base_GLView_getRetinaFactor(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_getRetinaFactor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_initLayer'", nullptr); return 0; } - auto&& ret = cobj->getRetinaFactor(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + auto&& ret = cobj->initLayer(); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:getRetinaFactor",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:initLayer",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_getRetinaFactor'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_initLayer'.",&tolua_err); #endif return 0; } -int lua_ax_base_GLView_setContentScaleFactor(lua_State* tolua_S) +int lua_ax_base_Node_create(lua_State* tolua_S) { int argc = 0; - ax::GLView* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_setContentScaleFactor'", nullptr); - return 0; - } -#endif + argc = lua_gettop(tolua_S) - 1; - argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.GLView:setContentScaleFactor"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_setContentScaleFactor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_create'", nullptr); return 0; } - auto&& ret = cobj->setContentScaleFactor(arg0); - tolua_pushboolean(tolua_S,(bool)ret); + auto&& ret = ax::Node::create(); + object_to_luaval(tolua_S, "ax.Node",(ax::Node*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:setContentScaleFactor",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Node:create",argc, 0); return 0; - #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_setContentScaleFactor'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_create'.",&tolua_err); #endif - return 0; } -int lua_ax_base_GLView_getContentScaleFactor(lua_State* tolua_S) +int lua_ax_base_Node_getAttachedNodeCount(lua_State* tolua_S) { int argc = 0; - ax::GLView* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.Node",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); + argc = lua_gettop(tolua_S) - 1; -#if _AX_DEBUG >= 1 - if (!cobj) + if (argc == 0) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_getContentScaleFactor'", nullptr); - return 0; + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_getAttachedNodeCount'", nullptr); + return 0; + } + auto&& ret = ax::Node::getAttachedNodeCount(); + tolua_pushnumber(tolua_S,(lua_Number)ret); + return 1; } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Node:getAttachedNodeCount",argc, 0); + return 0; +#if _AX_DEBUG >= 1 + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_getAttachedNodeCount'.",&tolua_err); +#endif + return 0; +} +int lua_ax_base_Node_constructor(lua_State* tolua_S) +{ + int argc = 0; + ax::Node* cobj = nullptr; + bool ok = true; + +#if _AX_DEBUG >= 1 + tolua_Error tolua_err; #endif + + argc = lua_gettop(tolua_S)-1; if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_getContentScaleFactor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Node_constructor'", nullptr); return 0; } - auto&& ret = cobj->getContentScaleFactor(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + cobj = new ax::Node(); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.Node"); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:getContentScaleFactor",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Node:Node",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_getContentScaleFactor'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Node_constructor'.",&tolua_err); #endif return 0; } -int lua_ax_base_GLView_isRetinaDisplay(lua_State* tolua_S) + +static int lua_ax_base_Node_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (Node)"); + return 0; +} + +int lua_register_ax_base_Node(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.Node"); + tolua_cclass(tolua_S,"Node","ax.Node","ax.Object",nullptr); + + tolua_beginmodule(tolua_S,"Node"); + tolua_function(tolua_S,"new",lua_ax_base_Node_constructor); + tolua_function(tolua_S,"getDescription",lua_ax_base_Node_getDescription); + tolua_function(tolua_S,"setLocalZOrder",lua_ax_base_Node_setLocalZOrder); + tolua_function(tolua_S,"updateOrderOfArrival",lua_ax_base_Node_updateOrderOfArrival); + tolua_function(tolua_S,"getLocalZOrder",lua_ax_base_Node_getLocalZOrder); + tolua_function(tolua_S,"setGlobalZOrder",lua_ax_base_Node_setGlobalZOrder); + tolua_function(tolua_S,"getGlobalZOrder",lua_ax_base_Node_getGlobalZOrder); + tolua_function(tolua_S,"setScaleX",lua_ax_base_Node_setScaleX); + tolua_function(tolua_S,"getScaleX",lua_ax_base_Node_getScaleX); + tolua_function(tolua_S,"setScaleY",lua_ax_base_Node_setScaleY); + tolua_function(tolua_S,"getScaleY",lua_ax_base_Node_getScaleY); + tolua_function(tolua_S,"setScaleZ",lua_ax_base_Node_setScaleZ); + tolua_function(tolua_S,"getScaleZ",lua_ax_base_Node_getScaleZ); + tolua_function(tolua_S,"setScale",lua_ax_base_Node_setScale); + tolua_function(tolua_S,"getScale",lua_ax_base_Node_getScale); + tolua_function(tolua_S,"setPosition",lua_ax_base_Node_setPosition); + tolua_function(tolua_S,"setPositionNormalized",lua_ax_base_Node_setPositionNormalized); + tolua_function(tolua_S,"setNormalizedPosition",lua_ax_base_Node_setNormalizedPosition); + tolua_function(tolua_S,"getPositionNormalized",lua_ax_base_Node_getPositionNormalized); + tolua_function(tolua_S,"getNormalizedPosition",lua_ax_base_Node_getNormalizedPosition); + tolua_function(tolua_S,"setPositionX",lua_ax_base_Node_setPositionX); + tolua_function(tolua_S,"getPositionX",lua_ax_base_Node_getPositionX); + tolua_function(tolua_S,"setPositionY",lua_ax_base_Node_setPositionY); + tolua_function(tolua_S,"getPositionY",lua_ax_base_Node_getPositionY); + tolua_function(tolua_S,"setPosition3D",lua_ax_base_Node_setPosition3D); + tolua_function(tolua_S,"getPosition3D",lua_ax_base_Node_getPosition3D); + tolua_function(tolua_S,"setPositionZ",lua_ax_base_Node_setPositionZ); + tolua_function(tolua_S,"getPositionZ",lua_ax_base_Node_getPositionZ); + tolua_function(tolua_S,"setSkewX",lua_ax_base_Node_setSkewX); + tolua_function(tolua_S,"getSkewX",lua_ax_base_Node_getSkewX); + tolua_function(tolua_S,"setSkewY",lua_ax_base_Node_setSkewY); + tolua_function(tolua_S,"getSkewY",lua_ax_base_Node_getSkewY); + tolua_function(tolua_S,"getAnchorPoint",lua_ax_base_Node_getAnchorPoint); + tolua_function(tolua_S,"getAnchorPointInPoints",lua_ax_base_Node_getAnchorPointInPoints); + tolua_function(tolua_S,"getContentSize",lua_ax_base_Node_getContentSize); + tolua_function(tolua_S,"hitTest",lua_ax_base_Node_hitTest); + tolua_function(tolua_S,"setVisible",lua_ax_base_Node_setVisible); + tolua_function(tolua_S,"isVisible",lua_ax_base_Node_isVisible); + tolua_function(tolua_S,"setRotation",lua_ax_base_Node_setRotation); + tolua_function(tolua_S,"getRotation",lua_ax_base_Node_getRotation); + tolua_function(tolua_S,"setRotation3D",lua_ax_base_Node_setRotation3D); + tolua_function(tolua_S,"getRotation3D",lua_ax_base_Node_getRotation3D); + tolua_function(tolua_S,"setRotationSkewX",lua_ax_base_Node_setRotationSkewX); + tolua_function(tolua_S,"getRotationSkewX",lua_ax_base_Node_getRotationSkewX); + tolua_function(tolua_S,"setRotationSkewY",lua_ax_base_Node_setRotationSkewY); + tolua_function(tolua_S,"getRotationSkewY",lua_ax_base_Node_getRotationSkewY); + tolua_function(tolua_S,"setIgnoreAnchorPointForPosition",lua_ax_base_Node_setIgnoreAnchorPointForPosition); + tolua_function(tolua_S,"isIgnoreAnchorPointForPosition",lua_ax_base_Node_isIgnoreAnchorPointForPosition); + tolua_function(tolua_S,"addChild",lua_ax_base_Node_addChild); + tolua_function(tolua_S,"getChildByTag",lua_ax_base_Node_getChildByTag); + tolua_function(tolua_S,"getChildByName",lua_ax_base_Node_getChildByName); + tolua_function(tolua_S,"getChildren",lua_ax_base_Node_getChildren); + tolua_function(tolua_S,"getChildrenCount",lua_ax_base_Node_getChildrenCount); + tolua_function(tolua_S,"setParent",lua_ax_base_Node_setParent); + tolua_function(tolua_S,"getParent",lua_ax_base_Node_getParent); + tolua_function(tolua_S,"removeFromParent",lua_ax_base_Node_removeFromParentAndCleanup); + tolua_function(tolua_S,"removeChild",lua_ax_base_Node_removeChild); + tolua_function(tolua_S,"removeChildByTag",lua_ax_base_Node_removeChildByTag); + tolua_function(tolua_S,"removeChildByName",lua_ax_base_Node_removeChildByName); + tolua_function(tolua_S,"removeAllChildren",lua_ax_base_Node_removeAllChildrenWithCleanup); + tolua_function(tolua_S,"reorderChild",lua_ax_base_Node_reorderChild); + tolua_function(tolua_S,"sortAllChildren",lua_ax_base_Node_sortAllChildren); + tolua_function(tolua_S,"getTag",lua_ax_base_Node_getTag); + tolua_function(tolua_S,"setTag",lua_ax_base_Node_setTag); + tolua_function(tolua_S,"getName",lua_ax_base_Node_getName); + tolua_function(tolua_S,"setName",lua_ax_base_Node_setName); + tolua_function(tolua_S,"setUserObject",lua_ax_base_Node_setUserObject); + tolua_function(tolua_S,"isRunning",lua_ax_base_Node_isRunning); + tolua_function(tolua_S,"cleanup",lua_ax_base_Node_cleanup); + tolua_function(tolua_S,"draw",lua_ax_base_Node_draw); + tolua_function(tolua_S,"visit",lua_ax_base_Node_visit); + tolua_function(tolua_S,"getScene",lua_ax_base_Node_getScene); + tolua_function(tolua_S,"getBoundingBox",lua_ax_base_Node_getBoundingBox); + tolua_function(tolua_S,"setEventDispatcher",lua_ax_base_Node_setEventDispatcher); + tolua_function(tolua_S,"getEventDispatcher",lua_ax_base_Node_getEventDispatcher); + tolua_function(tolua_S,"setActionManager",lua_ax_base_Node_setActionManager); + tolua_function(tolua_S,"getActionManager",lua_ax_base_Node_getActionManager); + tolua_function(tolua_S,"runAction",lua_ax_base_Node_runAction); + tolua_function(tolua_S,"stopAllActions",lua_ax_base_Node_stopAllActions); + tolua_function(tolua_S,"stopAction",lua_ax_base_Node_stopAction); + tolua_function(tolua_S,"stopActionByTag",lua_ax_base_Node_stopActionByTag); + tolua_function(tolua_S,"stopAllActionsByTag",lua_ax_base_Node_stopAllActionsByTag); + tolua_function(tolua_S,"stopActionsByFlags",lua_ax_base_Node_stopActionsByFlags); + tolua_function(tolua_S,"getActionByTag",lua_ax_base_Node_getActionByTag); + tolua_function(tolua_S,"getNumberOfRunningActions",lua_ax_base_Node_getNumberOfRunningActions); + tolua_function(tolua_S,"getNumberOfRunningActionsByTag",lua_ax_base_Node_getNumberOfRunningActionsByTag); + tolua_function(tolua_S,"setScheduler",lua_ax_base_Node_setScheduler); + tolua_function(tolua_S,"getScheduler",lua_ax_base_Node_getScheduler); + tolua_function(tolua_S,"isScheduled",lua_ax_base_Node_isScheduled); + tolua_function(tolua_S,"resume",lua_ax_base_Node_resume); + tolua_function(tolua_S,"pause",lua_ax_base_Node_pause); + tolua_function(tolua_S,"update",lua_ax_base_Node_update); + tolua_function(tolua_S,"updateTransform",lua_ax_base_Node_updateTransform); + tolua_function(tolua_S,"getNodeToParentTransform",lua_ax_base_Node_getNodeToParentTransform); + tolua_function(tolua_S,"getNodeToParentAffineTransform",lua_ax_base_Node_getNodeToParentAffineTransform); + tolua_function(tolua_S,"setNodeToParentTransform",lua_ax_base_Node_setNodeToParentTransform); + tolua_function(tolua_S,"getParentToNodeTransform",lua_ax_base_Node_getParentToNodeTransform); + tolua_function(tolua_S,"getParentToNodeAffineTransform",lua_ax_base_Node_getParentToNodeAffineTransform); + tolua_function(tolua_S,"getNodeToWorldTransform",lua_ax_base_Node_getNodeToWorldTransform); + tolua_function(tolua_S,"getNodeToWorldAffineTransform",lua_ax_base_Node_getNodeToWorldAffineTransform); + tolua_function(tolua_S,"getWorldToNodeTransform",lua_ax_base_Node_getWorldToNodeTransform); + tolua_function(tolua_S,"getWorldToNodeAffineTransform",lua_ax_base_Node_getWorldToNodeAffineTransform); + tolua_function(tolua_S,"convertToNodeSpace",lua_ax_base_Node_convertToNodeSpace); + tolua_function(tolua_S,"convertToWorldSpace",lua_ax_base_Node_convertToWorldSpace); + tolua_function(tolua_S,"convertToNodeSpaceAR",lua_ax_base_Node_convertToNodeSpaceAR); + tolua_function(tolua_S,"convertToWorldSpaceAR",lua_ax_base_Node_convertToWorldSpaceAR); + tolua_function(tolua_S,"convertTouchToNodeSpace",lua_ax_base_Node_convertTouchToNodeSpace); + tolua_function(tolua_S,"convertTouchToNodeSpaceAR",lua_ax_base_Node_convertTouchToNodeSpaceAR); + tolua_function(tolua_S,"getWorldPosition",lua_ax_base_Node_getWorldPosition); + tolua_function(tolua_S,"setWorldPosition",lua_ax_base_Node_setWorldPosition); + tolua_function(tolua_S,"getComponent",lua_ax_base_Node_getComponent); + tolua_function(tolua_S,"addComponent",lua_ax_base_Node_addComponent); + tolua_function(tolua_S,"removeComponent",lua_ax_base_Node_removeComponent); + tolua_function(tolua_S,"removeAllComponents",lua_ax_base_Node_removeAllComponents); + tolua_function(tolua_S,"getOpacity",lua_ax_base_Node_getOpacity); + tolua_function(tolua_S,"getDisplayedOpacity",lua_ax_base_Node_getDisplayedOpacity); + tolua_function(tolua_S,"setOpacity",lua_ax_base_Node_setOpacity); + tolua_function(tolua_S,"updateDisplayedOpacity",lua_ax_base_Node_updateDisplayedOpacity); + tolua_function(tolua_S,"isCascadeOpacityEnabled",lua_ax_base_Node_isCascadeOpacityEnabled); + tolua_function(tolua_S,"setCascadeOpacityEnabled",lua_ax_base_Node_setCascadeOpacityEnabled); + tolua_function(tolua_S,"getColor",lua_ax_base_Node_getColor); + tolua_function(tolua_S,"getDisplayedColor",lua_ax_base_Node_getDisplayedColor); + tolua_function(tolua_S,"setColor",lua_ax_base_Node_setColor); + tolua_function(tolua_S,"updateDisplayedColor",lua_ax_base_Node_updateDisplayedColor); + tolua_function(tolua_S,"isCascadeColorEnabled",lua_ax_base_Node_isCascadeColorEnabled); + tolua_function(tolua_S,"setCascadeColorEnabled",lua_ax_base_Node_setCascadeColorEnabled); + tolua_function(tolua_S,"setOpacityModifyRGB",lua_ax_base_Node_setOpacityModifyRGB); + tolua_function(tolua_S,"isOpacityModifyRGB",lua_ax_base_Node_isOpacityModifyRGB); + tolua_function(tolua_S,"setOnEnterCallback",lua_ax_base_Node_setOnEnterCallback); + tolua_function(tolua_S,"setOnExitCallback",lua_ax_base_Node_setOnExitCallback); + tolua_function(tolua_S,"setOnEnterTransitionDidFinishCallback",lua_ax_base_Node_setOnEnterTransitionDidFinishCallback); + tolua_function(tolua_S,"getOnEnterTransitionDidFinishCallback",lua_ax_base_Node_getOnEnterTransitionDidFinishCallback); + tolua_function(tolua_S,"setOnExitTransitionDidStartCallback",lua_ax_base_Node_setOnExitTransitionDidStartCallback); + tolua_function(tolua_S,"getOnExitTransitionDidStartCallback",lua_ax_base_Node_getOnExitTransitionDidStartCallback); + tolua_function(tolua_S,"getCameraMask",lua_ax_base_Node_getCameraMask); + tolua_function(tolua_S,"setCameraMask",lua_ax_base_Node_setCameraMask); + tolua_function(tolua_S,"applyMaskOnEnter",lua_ax_base_Node_applyMaskOnEnter); + tolua_function(tolua_S,"setProgramState",lua_ax_base_Node_setProgramState); + tolua_function(tolua_S,"setProgramStateWithRegistry",lua_ax_base_Node_setProgramStateWithRegistry); + tolua_function(tolua_S,"setProgramStateByProgramId",lua_ax_base_Node_setProgramStateByProgramId); + tolua_function(tolua_S,"getProgramState",lua_ax_base_Node_getProgramState); + tolua_function(tolua_S,"updateProgramStateTexture",lua_ax_base_Node_updateProgramStateTexture); + tolua_function(tolua_S,"resetChild",lua_ax_base_Node_resetChild); + tolua_function(tolua_S,"init",lua_ax_base_Node_init); + tolua_function(tolua_S,"initLayer",lua_ax_base_Node_initLayer); + tolua_function(tolua_S,"create", lua_ax_base_Node_create); + tolua_function(tolua_S,"getAttachedNodeCount", lua_ax_base_Node_getAttachedNodeCount); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::Node).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.Node"; + g_typeCast[typeName] = "ax.Node"; + return 1; +} + +int lua_ax_base_Image_initWithImageFile(lua_State* tolua_S) { int argc = 0; - ax::GLView* cobj = nullptr; + ax::Image* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -13290,45 +13475,48 @@ int lua_ax_base_GLView_isRetinaDisplay(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Image",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Image*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_isRetinaDisplay'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Image_initWithImageFile'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + std::string_view arg0; + + ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Image:initWithImageFile"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_isRetinaDisplay'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_initWithImageFile'", nullptr); return 0; } - auto&& ret = cobj->isRetinaDisplay(); + auto&& ret = cobj->initWithImageFile(arg0); tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:isRetinaDisplay",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Image:initWithImageFile",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_isRetinaDisplay'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_initWithImageFile'.",&tolua_err); #endif return 0; } -int lua_ax_base_GLView_getVisibleSize(lua_State* tolua_S) +int lua_ax_base_Image_flipRawData(lua_State* tolua_S) { int argc = 0; - ax::GLView* cobj = nullptr; + ax::Image* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -13337,15 +13525,15 @@ int lua_ax_base_GLView_getVisibleSize(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Image",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Image*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_getVisibleSize'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Image_flipRawData'", nullptr); return 0; } #endif @@ -13355,27 +13543,27 @@ int lua_ax_base_GLView_getVisibleSize(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_getVisibleSize'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_flipRawData'", nullptr); return 0; } - auto&& ret = cobj->getVisibleSize(); - vec2_to_luaval(tolua_S, ret); + cobj->flipRawData(); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:getVisibleSize",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Image:flipRawData",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_getVisibleSize'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_flipRawData'.",&tolua_err); #endif return 0; } -int lua_ax_base_GLView_getVisibleOrigin(lua_State* tolua_S) +int lua_ax_base_Image_getFileType(lua_State* tolua_S) { int argc = 0; - ax::GLView* cobj = nullptr; + ax::Image* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -13384,15 +13572,15 @@ int lua_ax_base_GLView_getVisibleOrigin(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Image",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Image*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_getVisibleOrigin'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Image_getFileType'", nullptr); return 0; } #endif @@ -13402,27 +13590,27 @@ int lua_ax_base_GLView_getVisibleOrigin(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_getVisibleOrigin'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_getFileType'", nullptr); return 0; } - auto&& ret = cobj->getVisibleOrigin(); - vec2_to_luaval(tolua_S, ret); + int ret = (int)cobj->getFileType(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:getVisibleOrigin",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Image:getFileType",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_getVisibleOrigin'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_getFileType'.",&tolua_err); #endif return 0; } -int lua_ax_base_GLView_getVisibleRect(lua_State* tolua_S) +int lua_ax_base_Image_getPixelFormat(lua_State* tolua_S) { int argc = 0; - ax::GLView* cobj = nullptr; + ax::Image* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -13431,15 +13619,15 @@ int lua_ax_base_GLView_getVisibleRect(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Image",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Image*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_getVisibleRect'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Image_getPixelFormat'", nullptr); return 0; } #endif @@ -13449,27 +13637,27 @@ int lua_ax_base_GLView_getVisibleRect(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_getVisibleRect'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_getPixelFormat'", nullptr); return 0; } - auto&& ret = cobj->getVisibleRect(); - rect_to_luaval(tolua_S, ret); + int ret = (int)cobj->getPixelFormat(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:getVisibleRect",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Image:getPixelFormat",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_getVisibleRect'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_getPixelFormat'.",&tolua_err); #endif return 0; } -int lua_ax_base_GLView_getSafeAreaRect(lua_State* tolua_S) +int lua_ax_base_Image_getWidth(lua_State* tolua_S) { int argc = 0; - ax::GLView* cobj = nullptr; + ax::Image* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -13478,15 +13666,15 @@ int lua_ax_base_GLView_getSafeAreaRect(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Image",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Image*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_getSafeAreaRect'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Image_getWidth'", nullptr); return 0; } #endif @@ -13496,27 +13684,27 @@ int lua_ax_base_GLView_getSafeAreaRect(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_getSafeAreaRect'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_getWidth'", nullptr); return 0; } - auto&& ret = cobj->getSafeAreaRect(); - rect_to_luaval(tolua_S, ret); + auto&& ret = cobj->getWidth(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:getSafeAreaRect",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Image:getWidth",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_getSafeAreaRect'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_getWidth'.",&tolua_err); #endif return 0; } -int lua_ax_base_GLView_setDesignResolutionSize(lua_State* tolua_S) +int lua_ax_base_Image_getHeight(lua_State* tolua_S) { int argc = 0; - ax::GLView* cobj = nullptr; + ax::Image* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -13525,54 +13713,45 @@ int lua_ax_base_GLView_setDesignResolutionSize(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Image",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Image*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_setDesignResolutionSize'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Image_getHeight'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 3) + if (argc == 0) { - double arg0; - double arg1; - ResolutionPolicy arg2; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.GLView:setDesignResolutionSize"); - - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.GLView:setDesignResolutionSize"); - - ok &= luaval_to_int32(tolua_S, 4,(int *)&arg2, "ax.GLView:setDesignResolutionSize"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_setDesignResolutionSize'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_getHeight'", nullptr); return 0; } - cobj->setDesignResolutionSize(arg0, arg1, arg2); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getHeight(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:setDesignResolutionSize",argc, 3); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Image:getHeight",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_setDesignResolutionSize'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_getHeight'.",&tolua_err); #endif return 0; } -int lua_ax_base_GLView_getDesignResolutionSize(lua_State* tolua_S) +int lua_ax_base_Image_getNumberOfMipmaps(lua_State* tolua_S) { int argc = 0; - ax::GLView* cobj = nullptr; + ax::Image* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -13581,15 +13760,15 @@ int lua_ax_base_GLView_getDesignResolutionSize(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Image",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Image*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_getDesignResolutionSize'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Image_getNumberOfMipmaps'", nullptr); return 0; } #endif @@ -13599,27 +13778,27 @@ int lua_ax_base_GLView_getDesignResolutionSize(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_getDesignResolutionSize'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_getNumberOfMipmaps'", nullptr); return 0; } - auto&& ret = cobj->getDesignResolutionSize(); - vec2_to_luaval(tolua_S, ret); + auto&& ret = cobj->getNumberOfMipmaps(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:getDesignResolutionSize",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Image:getNumberOfMipmaps",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_getDesignResolutionSize'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_getNumberOfMipmaps'.",&tolua_err); #endif return 0; } -int lua_ax_base_GLView_setViewPortInPoints(lua_State* tolua_S) +int lua_ax_base_Image_hasPremultipliedAlpha(lua_State* tolua_S) { int argc = 0; - ax::GLView* cobj = nullptr; + ax::Image* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -13628,57 +13807,45 @@ int lua_ax_base_GLView_setViewPortInPoints(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Image",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Image*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_setViewPortInPoints'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Image_hasPremultipliedAlpha'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 4) + if (argc == 0) { - double arg0; - double arg1; - double arg2; - double arg3; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.GLView:setViewPortInPoints"); - - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.GLView:setViewPortInPoints"); - - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.GLView:setViewPortInPoints"); - - ok &= luaval_to_number(tolua_S, 5,&arg3, "ax.GLView:setViewPortInPoints"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_setViewPortInPoints'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_hasPremultipliedAlpha'", nullptr); return 0; } - cobj->setViewPortInPoints(arg0, arg1, arg2, arg3); - lua_settop(tolua_S, 1); + auto&& ret = cobj->hasPremultipliedAlpha(); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:setViewPortInPoints",argc, 4); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Image:hasPremultipliedAlpha",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_setViewPortInPoints'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_hasPremultipliedAlpha'.",&tolua_err); #endif return 0; } -int lua_ax_base_GLView_setScissorInPoints(lua_State* tolua_S) +int lua_ax_base_Image_getFilePath(lua_State* tolua_S) { int argc = 0; - ax::GLView* cobj = nullptr; + ax::Image* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -13687,57 +13854,45 @@ int lua_ax_base_GLView_setScissorInPoints(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Image",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Image*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_setScissorInPoints'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Image_getFilePath'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 4) + if (argc == 0) { - double arg0; - double arg1; - double arg2; - double arg3; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.GLView:setScissorInPoints"); - - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.GLView:setScissorInPoints"); - - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.GLView:setScissorInPoints"); - - ok &= luaval_to_number(tolua_S, 5,&arg3, "ax.GLView:setScissorInPoints"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_setScissorInPoints'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_getFilePath'", nullptr); return 0; } - cobj->setScissorInPoints(arg0, arg1, arg2, arg3); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getFilePath(); + lua_pushlstring(tolua_S,ret.c_str(),ret.length()); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:setScissorInPoints",argc, 4); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Image:getFilePath",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_setScissorInPoints'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_getFilePath'.",&tolua_err); #endif return 0; } -int lua_ax_base_GLView_isScissorEnabled(lua_State* tolua_S) +int lua_ax_base_Image_getBitPerPixel(lua_State* tolua_S) { int argc = 0; - ax::GLView* cobj = nullptr; + ax::Image* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -13746,15 +13901,15 @@ int lua_ax_base_GLView_isScissorEnabled(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Image",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Image*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_isScissorEnabled'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Image_getBitPerPixel'", nullptr); return 0; } #endif @@ -13764,27 +13919,27 @@ int lua_ax_base_GLView_isScissorEnabled(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_isScissorEnabled'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_getBitPerPixel'", nullptr); return 0; } - auto&& ret = cobj->isScissorEnabled(); - tolua_pushboolean(tolua_S,(bool)ret); + auto&& ret = cobj->getBitPerPixel(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:isScissorEnabled",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Image:getBitPerPixel",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_isScissorEnabled'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_getBitPerPixel'.",&tolua_err); #endif return 0; } -int lua_ax_base_GLView_getScissorRect(lua_State* tolua_S) +int lua_ax_base_Image_hasAlpha(lua_State* tolua_S) { int argc = 0; - ax::GLView* cobj = nullptr; + ax::Image* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -13793,15 +13948,15 @@ int lua_ax_base_GLView_getScissorRect(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Image",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Image*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_getScissorRect'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Image_hasAlpha'", nullptr); return 0; } #endif @@ -13811,27 +13966,27 @@ int lua_ax_base_GLView_getScissorRect(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_getScissorRect'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_hasAlpha'", nullptr); return 0; } - auto&& ret = cobj->getScissorRect(); - rect_to_luaval(tolua_S, ret); + auto&& ret = cobj->hasAlpha(); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:getScissorRect",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Image:hasAlpha",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_getScissorRect'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_hasAlpha'.",&tolua_err); #endif return 0; } -int lua_ax_base_GLView_setViewName(lua_State* tolua_S) +int lua_ax_base_Image_isCompressed(lua_State* tolua_S) { int argc = 0; - ax::GLView* cobj = nullptr; + ax::Image* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -13840,48 +13995,45 @@ int lua_ax_base_GLView_setViewName(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Image",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Image*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_setViewName'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Image_isCompressed'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - std::string_view arg0; - - ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.GLView:setViewName"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_setViewName'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_isCompressed'", nullptr); return 0; } - cobj->setViewName(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->isCompressed(); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:setViewName",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Image:isCompressed",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_setViewName'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_isCompressed'.",&tolua_err); #endif return 0; } -int lua_ax_base_GLView_getViewName(lua_State* tolua_S) +int lua_ax_base_Image_saveToFile(lua_State* tolua_S) { int argc = 0; - ax::GLView* cobj = nullptr; + ax::Image* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -13890,100 +14042,65 @@ int lua_ax_base_GLView_getViewName(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Image",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Image*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_getViewName'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Image_saveToFile'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + std::string_view arg0; + + ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Image:saveToFile"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_getViewName'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_saveToFile'", nullptr); return 0; } - auto&& ret = cobj->getViewName(); - lua_pushlstring(tolua_S,ret.data(),ret.length()); + auto&& ret = cobj->saveToFile(arg0); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:getViewName",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_getViewName'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_base_GLView_setIcon(lua_State* tolua_S) -{ - int argc = 0; - ax::GLView* cobj = nullptr; - bool ok = true; -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; -#endif - cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); -#if _AX_DEBUG >= 1 - if (!cobj) + if (argc == 2) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_setIcon'", nullptr); - return 0; - } -#endif - argc = lua_gettop(tolua_S)-1; - do{ - if (argc == 1) { - std::vector arg0; - ok &= luaval_to_std_vector_string_view(tolua_S, 2, &arg0, "ax.GLView:setIcon"); + std::string_view arg0; + bool arg1; - if (!ok) { break; } - cobj->setIcon(arg0); - lua_settop(tolua_S, 1); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 1) { - std::string_view arg0; - ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.GLView:setIcon"); + ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Image:saveToFile"); - if (!ok) { break; } - cobj->setIcon(arg0); - lua_settop(tolua_S, 1); - return 1; + ok &= luaval_to_boolean(tolua_S, 3,&arg1, "ax.Image:saveToFile"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_saveToFile'", nullptr); + return 0; } - }while(0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:setIcon",argc, 1); + auto&& ret = cobj->saveToFile(arg0, arg1); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Image:saveToFile",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_setIcon'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_saveToFile'.",&tolua_err); #endif return 0; } -int lua_ax_base_GLView_setDefaultIcon(lua_State* tolua_S) +int lua_ax_base_Image_premultiplyAlpha(lua_State* tolua_S) { int argc = 0; - ax::GLView* cobj = nullptr; + ax::Image* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -13992,15 +14109,15 @@ int lua_ax_base_GLView_setDefaultIcon(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Image",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Image*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_setDefaultIcon'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Image_premultiplyAlpha'", nullptr); return 0; } #endif @@ -14010,27 +14127,27 @@ int lua_ax_base_GLView_setDefaultIcon(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_setDefaultIcon'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_premultiplyAlpha'", nullptr); return 0; } - cobj->setDefaultIcon(); + cobj->premultiplyAlpha(); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:setDefaultIcon",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Image:premultiplyAlpha",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_setDefaultIcon'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_premultiplyAlpha'.",&tolua_err); #endif return 0; } -int lua_ax_base_GLView_getViewPortRect(lua_State* tolua_S) +int lua_ax_base_Image_reversePremultipliedAlpha(lua_State* tolua_S) { int argc = 0; - ax::GLView* cobj = nullptr; + ax::Image* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -14039,15 +14156,15 @@ int lua_ax_base_GLView_getViewPortRect(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Image",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Image*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_getViewPortRect'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Image_reversePremultipliedAlpha'", nullptr); return 0; } #endif @@ -14057,168 +14174,137 @@ int lua_ax_base_GLView_getViewPortRect(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_getViewPortRect'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_reversePremultipliedAlpha'", nullptr); return 0; } - auto&& ret = cobj->getViewPortRect(); - rect_to_luaval(tolua_S, ret); + cobj->reversePremultipliedAlpha(); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:getViewPortRect",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Image:reversePremultipliedAlpha",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_getViewPortRect'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_reversePremultipliedAlpha'.",&tolua_err); #endif return 0; } -int lua_ax_base_GLView_getScaleX(lua_State* tolua_S) +int lua_ax_base_Image_setPNGPremultipliedAlphaEnabled(lua_State* tolua_S) { int argc = 0; - ax::GLView* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.Image",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_getScaleX'", nullptr); - return 0; - } -#endif + argc = lua_gettop(tolua_S) - 1; - argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + bool arg0; + ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.Image:setPNGPremultipliedAlphaEnabled"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_getScaleX'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_setPNGPremultipliedAlphaEnabled'", nullptr); return 0; } - auto&& ret = cobj->getScaleX(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + ax::Image::setPNGPremultipliedAlphaEnabled(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:getScaleX",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Image:setPNGPremultipliedAlphaEnabled",argc, 1); return 0; - #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_getScaleX'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_setPNGPremultipliedAlphaEnabled'.",&tolua_err); #endif - return 0; } -int lua_ax_base_GLView_getScaleY(lua_State* tolua_S) +int lua_ax_base_Image_setCompressedImagesHavePMA(lua_State* tolua_S) { int argc = 0; - ax::GLView* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.Image",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_getScaleY'", nullptr); - return 0; - } -#endif + argc = lua_gettop(tolua_S) - 1; - argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 2) { + unsigned int arg0; + bool arg1; + ok &= luaval_to_uint32(tolua_S, 2,&arg0, "ax.Image:setCompressedImagesHavePMA"); + ok &= luaval_to_boolean(tolua_S, 3,&arg1, "ax.Image:setCompressedImagesHavePMA"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_getScaleY'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_setCompressedImagesHavePMA'", nullptr); return 0; } - auto&& ret = cobj->getScaleY(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + ax::Image::setCompressedImagesHavePMA(arg0, arg1); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:getScaleY",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Image:setCompressedImagesHavePMA",argc, 2); return 0; - #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_getScaleY'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_setCompressedImagesHavePMA'.",&tolua_err); #endif - return 0; } -int lua_ax_base_GLView_getResolutionPolicy(lua_State* tolua_S) +int lua_ax_base_Image_isCompressedImageHavePMA(lua_State* tolua_S) { int argc = 0; - ax::GLView* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.Image",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_getResolutionPolicy'", nullptr); - return 0; - } -#endif + argc = lua_gettop(tolua_S) - 1; - argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + unsigned int arg0; + ok &= luaval_to_uint32(tolua_S, 2,&arg0, "ax.Image:isCompressedImageHavePMA"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_getResolutionPolicy'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_isCompressedImageHavePMA'", nullptr); return 0; } - int ret = (int)cobj->getResolutionPolicy(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + auto&& ret = ax::Image::isCompressedImageHavePMA(arg0); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:getResolutionPolicy",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Image:isCompressedImageHavePMA",argc, 1); return 0; - #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_getResolutionPolicy'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_isCompressedImageHavePMA'.",&tolua_err); #endif - return 0; } -int lua_ax_base_GLView_renderScene(lua_State* tolua_S) +int lua_ax_base_Image_constructor(lua_State* tolua_S) { int argc = 0; - ax::GLView* cobj = nullptr; + ax::Image* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -14226,178 +14312,179 @@ int lua_ax_base_GLView_renderScene(lua_State* tolua_S) #endif -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_renderScene'", nullptr); - return 0; - } -#endif argc = lua_gettop(tolua_S)-1; - if (argc == 2) + if (argc == 0) { - ax::Scene* arg0; - ax::Renderer* arg1; - - ok &= luaval_to_object(tolua_S, 2, "ax.Scene",&arg0, "ax.GLView:renderScene"); - - ok &= luaval_to_object(tolua_S, 3, "ax.Renderer",&arg1, "ax.GLView:renderScene"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_renderScene'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_constructor'", nullptr); return 0; } - cobj->renderScene(arg0, arg1); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:renderScene",argc, 2); + cobj = new ax::Image(); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.Image"); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Image:Image",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_renderScene'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_constructor'.",&tolua_err); #endif return 0; } -int lua_ax_base_GLView_setGLContextAttrs(lua_State* tolua_S) + +static int lua_ax_base_Image_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (Image)"); + return 0; +} + +int lua_register_ax_base_Image(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.Image"); + tolua_cclass(tolua_S,"Image","ax.Image","ax.Object",nullptr); + + tolua_beginmodule(tolua_S,"Image"); + tolua_function(tolua_S,"new",lua_ax_base_Image_constructor); + tolua_function(tolua_S,"initWithImageFile",lua_ax_base_Image_initWithImageFile); + tolua_function(tolua_S,"flipRawData",lua_ax_base_Image_flipRawData); + tolua_function(tolua_S,"getFileType",lua_ax_base_Image_getFileType); + tolua_function(tolua_S,"getPixelFormat",lua_ax_base_Image_getPixelFormat); + tolua_function(tolua_S,"getWidth",lua_ax_base_Image_getWidth); + tolua_function(tolua_S,"getHeight",lua_ax_base_Image_getHeight); + tolua_function(tolua_S,"getNumberOfMipmaps",lua_ax_base_Image_getNumberOfMipmaps); + tolua_function(tolua_S,"hasPremultipliedAlpha",lua_ax_base_Image_hasPremultipliedAlpha); + tolua_function(tolua_S,"getFilePath",lua_ax_base_Image_getFilePath); + tolua_function(tolua_S,"getBitPerPixel",lua_ax_base_Image_getBitPerPixel); + tolua_function(tolua_S,"hasAlpha",lua_ax_base_Image_hasAlpha); + tolua_function(tolua_S,"isCompressed",lua_ax_base_Image_isCompressed); + tolua_function(tolua_S,"saveToFile",lua_ax_base_Image_saveToFile); + tolua_function(tolua_S,"premultiplyAlpha",lua_ax_base_Image_premultiplyAlpha); + tolua_function(tolua_S,"reversePremultipliedAlpha",lua_ax_base_Image_reversePremultipliedAlpha); + tolua_function(tolua_S,"setPNGPremultipliedAlphaEnabled", lua_ax_base_Image_setPNGPremultipliedAlphaEnabled); + tolua_function(tolua_S,"setCompressedImagesHavePMA", lua_ax_base_Image_setCompressedImagesHavePMA); + tolua_function(tolua_S,"isCompressedImageHavePMA", lua_ax_base_Image_isCompressedImageHavePMA); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::Image).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.Image"; + g_typeCast[typeName] = "ax.Image"; + return 1; +} + +int lua_ax_base_PolygonInfo_setQuad(lua_State* tolua_S) { int argc = 0; + ax::PolygonInfo* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.PolygonInfo",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S) - 1; + cobj = (ax::PolygonInfo*)tolua_tousertype(tolua_S,1,0); - if (argc == 1) +#if _AX_DEBUG >= 1 + if (!cobj) { - GLContextAttrs arg0; - #pragma warning NO CONVERSION TO NATIVE FOR GLContextAttrs + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_PolygonInfo_setQuad'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 1) + { + ax::V3F_T2F_C4B_Quad* arg0; + + #pragma warning NO CONVERSION TO NATIVE FOR V3F_T2F_C4B_Quad* ok = false; if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_setGLContextAttrs'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_PolygonInfo_setQuad'", nullptr); return 0; } - ax::GLView::setGLContextAttrs(arg0); + cobj->setQuad(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.GLView:setGLContextAttrs",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PolygonInfo:setQuad",argc, 1); return 0; + #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_setGLContextAttrs'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_PolygonInfo_setQuad'.",&tolua_err); #endif + return 0; } -int lua_ax_base_GLView_getGLContextAttrs(lua_State* tolua_S) +int lua_ax_base_PolygonInfo_setQuads(lua_State* tolua_S) { int argc = 0; + ax::PolygonInfo* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.PolygonInfo",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S) - 1; + cobj = (ax::PolygonInfo*)tolua_tousertype(tolua_S,1,0); - if (argc == 0) +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_PolygonInfo_setQuads'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 2) { + ax::V3F_T2F_C4B_Quad* arg0; + int arg1; + + #pragma warning NO CONVERSION TO NATIVE FOR V3F_T2F_C4B_Quad* + ok = false; + + ok &= luaval_to_int32(tolua_S, 3,(int *)&arg1, "ax.PolygonInfo:setQuads"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_getGLContextAttrs'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_PolygonInfo_setQuads'", nullptr); return 0; } - auto&& ret = ax::GLView::getGLContextAttrs(); - #pragma warning NO CONVERSION FROM NATIVE FOR GLContextAttrs; + cobj->setQuads(arg0, arg1); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.GLView:getGLContextAttrs",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PolygonInfo:setQuads",argc, 2); return 0; + #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_getGLContextAttrs'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_PolygonInfo_setQuads'.",&tolua_err); #endif - return 0; -} -static int lua_ax_base_GLView_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (GLView)"); - return 0; -} - -int lua_register_ax_base_GLView(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.GLView"); - tolua_cclass(tolua_S,"GLView","ax.GLView","ax.Object",nullptr); - tolua_beginmodule(tolua_S,"GLView"); - tolua_function(tolua_S,"endToLua",lua_ax_base_GLView_end); - tolua_function(tolua_S,"isOpenGLReady",lua_ax_base_GLView_isOpenGLReady); - tolua_function(tolua_S,"swapBuffers",lua_ax_base_GLView_swapBuffers); - tolua_function(tolua_S,"setIMEKeyboardState",lua_ax_base_GLView_setIMEKeyboardState); - tolua_function(tolua_S,"windowShouldClose",lua_ax_base_GLView_windowShouldClose); - tolua_function(tolua_S,"pollEvents",lua_ax_base_GLView_pollEvents); - tolua_function(tolua_S,"getFrameSize",lua_ax_base_GLView_getFrameSize); - tolua_function(tolua_S,"setFrameSize",lua_ax_base_GLView_setFrameSize); - tolua_function(tolua_S,"setFrameZoomFactor",lua_ax_base_GLView_setFrameZoomFactor); - tolua_function(tolua_S,"getFrameZoomFactor",lua_ax_base_GLView_getFrameZoomFactor); - tolua_function(tolua_S,"setCursorVisible",lua_ax_base_GLView_setCursorVisible); - tolua_function(tolua_S,"getRetinaFactor",lua_ax_base_GLView_getRetinaFactor); - tolua_function(tolua_S,"setContentScaleFactor",lua_ax_base_GLView_setContentScaleFactor); - tolua_function(tolua_S,"getContentScaleFactor",lua_ax_base_GLView_getContentScaleFactor); - tolua_function(tolua_S,"isRetinaDisplay",lua_ax_base_GLView_isRetinaDisplay); - tolua_function(tolua_S,"getVisibleSize",lua_ax_base_GLView_getVisibleSize); - tolua_function(tolua_S,"getVisibleOrigin",lua_ax_base_GLView_getVisibleOrigin); - tolua_function(tolua_S,"getVisibleRect",lua_ax_base_GLView_getVisibleRect); - tolua_function(tolua_S,"getSafeAreaRect",lua_ax_base_GLView_getSafeAreaRect); - tolua_function(tolua_S,"setDesignResolutionSize",lua_ax_base_GLView_setDesignResolutionSize); - tolua_function(tolua_S,"getDesignResolutionSize",lua_ax_base_GLView_getDesignResolutionSize); - tolua_function(tolua_S,"setViewPortInPoints",lua_ax_base_GLView_setViewPortInPoints); - tolua_function(tolua_S,"setScissorInPoints",lua_ax_base_GLView_setScissorInPoints); - tolua_function(tolua_S,"isScissorEnabled",lua_ax_base_GLView_isScissorEnabled); - tolua_function(tolua_S,"getScissorRect",lua_ax_base_GLView_getScissorRect); - tolua_function(tolua_S,"setViewName",lua_ax_base_GLView_setViewName); - tolua_function(tolua_S,"getViewName",lua_ax_base_GLView_getViewName); - tolua_function(tolua_S,"setIcon",lua_ax_base_GLView_setIcon); - tolua_function(tolua_S,"setDefaultIcon",lua_ax_base_GLView_setDefaultIcon); - tolua_function(tolua_S,"getViewPortRect",lua_ax_base_GLView_getViewPortRect); - tolua_function(tolua_S,"getScaleX",lua_ax_base_GLView_getScaleX); - tolua_function(tolua_S,"getScaleY",lua_ax_base_GLView_getScaleY); - tolua_function(tolua_S,"getResolutionPolicy",lua_ax_base_GLView_getResolutionPolicy); - tolua_function(tolua_S,"renderScene",lua_ax_base_GLView_renderScene); - tolua_function(tolua_S,"setGLContextAttrs", lua_ax_base_GLView_setGLContextAttrs); - tolua_function(tolua_S,"getGLContextAttrs", lua_ax_base_GLView_getGLContextAttrs); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::GLView).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.GLView"; - g_typeCast[typeName] = "ax.GLView"; - return 1; + return 0; } - -int lua_ax_base_Director_init(lua_State* tolua_S) +int lua_ax_base_PolygonInfo_setTriangles(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::PolygonInfo* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -14406,45 +14493,49 @@ int lua_ax_base_Director_init(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.PolygonInfo",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::PolygonInfo*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_init'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_PolygonInfo_setTriangles'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + ax::TrianglesCommand::Triangles arg0; + + #pragma warning NO CONVERSION TO NATIVE FOR Triangles + ok = false; if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_init'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_PolygonInfo_setTriangles'", nullptr); return 0; } - auto&& ret = cobj->init(); - tolua_pushboolean(tolua_S,(bool)ret); + cobj->setTriangles(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:init",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PolygonInfo:setTriangles",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_init'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_PolygonInfo_setTriangles'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_getRunningScene(lua_State* tolua_S) +int lua_ax_base_PolygonInfo_getVertCount(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::PolygonInfo* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -14453,15 +14544,15 @@ int lua_ax_base_Director_getRunningScene(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.PolygonInfo",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::PolygonInfo*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getRunningScene'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_PolygonInfo_getVertCount'", nullptr); return 0; } #endif @@ -14471,27 +14562,27 @@ int lua_ax_base_Director_getRunningScene(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getRunningScene'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_PolygonInfo_getVertCount'", nullptr); return 0; } - auto&& ret = cobj->getRunningScene(); - object_to_luaval(tolua_S, "ax.Scene",(ax::Scene*)ret); + auto&& ret = cobj->getVertCount(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getRunningScene",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PolygonInfo:getVertCount",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getRunningScene'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_PolygonInfo_getVertCount'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_getNextScene(lua_State* tolua_S) +int lua_ax_base_PolygonInfo_getTrianglesCount(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::PolygonInfo* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -14500,15 +14591,15 @@ int lua_ax_base_Director_getNextScene(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.PolygonInfo",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::PolygonInfo*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getNextScene'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_PolygonInfo_getTrianglesCount'", nullptr); return 0; } #endif @@ -14518,27 +14609,27 @@ int lua_ax_base_Director_getNextScene(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getNextScene'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_PolygonInfo_getTrianglesCount'", nullptr); return 0; } - auto&& ret = cobj->getNextScene(); - object_to_luaval(tolua_S, "ax.Scene",(ax::Scene*)ret); + auto&& ret = cobj->getTrianglesCount(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getNextScene",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PolygonInfo:getTrianglesCount",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getNextScene'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_PolygonInfo_getTrianglesCount'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_getAnimationInterval(lua_State* tolua_S) +int lua_ax_base_PolygonInfo_getArea(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::PolygonInfo* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -14547,15 +14638,15 @@ int lua_ax_base_Director_getAnimationInterval(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.PolygonInfo",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::PolygonInfo*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getAnimationInterval'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_PolygonInfo_getArea'", nullptr); return 0; } #endif @@ -14565,27 +14656,27 @@ int lua_ax_base_Director_getAnimationInterval(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getAnimationInterval'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_PolygonInfo_getArea'", nullptr); return 0; } - auto&& ret = cobj->getAnimationInterval(); + auto&& ret = cobj->getArea(); tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getAnimationInterval",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PolygonInfo:getArea",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getAnimationInterval'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_PolygonInfo_getArea'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_setAnimationInterval(lua_State* tolua_S) +int lua_ax_base_PolygonInfo_getRect(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::PolygonInfo* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -14594,48 +14685,45 @@ int lua_ax_base_Director_setAnimationInterval(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.PolygonInfo",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::PolygonInfo*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_setAnimationInterval'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_PolygonInfo_getRect'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Director:setAnimationInterval"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_setAnimationInterval'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_PolygonInfo_getRect'", nullptr); return 0; } - cobj->setAnimationInterval(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getRect(); + rect_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:setAnimationInterval",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PolygonInfo:getRect",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_setAnimationInterval'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_PolygonInfo_getRect'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_isStatsDisplay(lua_State* tolua_S) +int lua_ax_base_PolygonInfo_setRect(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::PolygonInfo* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -14644,45 +14732,48 @@ int lua_ax_base_Director_isStatsDisplay(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.PolygonInfo",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::PolygonInfo*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_isStatsDisplay'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_PolygonInfo_setRect'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + ax::Rect arg0; + + ok &= luaval_to_rect(tolua_S, 2, &arg0, "ax.PolygonInfo:setRect"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_isStatsDisplay'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_PolygonInfo_setRect'", nullptr); return 0; } - auto&& ret = cobj->isStatsDisplay(); - tolua_pushboolean(tolua_S,(bool)ret); + cobj->setRect(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:isStatsDisplay",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PolygonInfo:setRect",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_isStatsDisplay'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_PolygonInfo_setRect'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_setStatsDisplay(lua_State* tolua_S) +int lua_ax_base_PolygonInfo_getFilename(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::PolygonInfo* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -14691,48 +14782,45 @@ int lua_ax_base_Director_setStatsDisplay(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.PolygonInfo",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::PolygonInfo*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_setStatsDisplay'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_PolygonInfo_getFilename'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - bool arg0; - - ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.Director:setStatsDisplay"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_setStatsDisplay'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_PolygonInfo_getFilename'", nullptr); return 0; } - cobj->setStatsDisplay(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getFilename(); + lua_pushlstring(tolua_S,ret.data(),ret.length()); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:setStatsDisplay",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PolygonInfo:getFilename",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_setStatsDisplay'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_PolygonInfo_getFilename'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_getSecondsPerFrame(lua_State* tolua_S) +int lua_ax_base_PolygonInfo_setFilename(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::PolygonInfo* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -14741,45 +14829,48 @@ int lua_ax_base_Director_getSecondsPerFrame(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.PolygonInfo",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::PolygonInfo*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getSecondsPerFrame'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_PolygonInfo_setFilename'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + std::string_view arg0; + + ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.PolygonInfo:setFilename"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getSecondsPerFrame'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_PolygonInfo_setFilename'", nullptr); return 0; } - auto&& ret = cobj->getSecondsPerFrame(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + cobj->setFilename(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getSecondsPerFrame",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PolygonInfo:setFilename",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getSecondsPerFrame'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_PolygonInfo_setFilename'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_setStatsAnchor(lua_State* tolua_S) +int lua_ax_base_PolygonInfo_constructor(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::PolygonInfo* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -14787,60 +14878,122 @@ int lua_ax_base_Director_setStatsAnchor(lua_State* tolua_S) #endif -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_setStatsAnchor'", nullptr); - return 0; - } -#endif argc = lua_gettop(tolua_S)-1; if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_setStatsAnchor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_PolygonInfo_constructor'", nullptr); return 0; } - cobj->setStatsAnchor(); - lua_settop(tolua_S, 1); + cobj = new ax::PolygonInfo(); + tolua_pushusertype(tolua_S,(void*)cobj,"ax.PolygonInfo"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); return 1; } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PolygonInfo:PolygonInfo",argc, 0); + return 0; + +#if _AX_DEBUG >= 1 + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_PolygonInfo_constructor'.",&tolua_err); +#endif + + return 0; +} + +static int lua_ax_base_PolygonInfo_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (PolygonInfo)"); + return 0; +} + +int lua_register_ax_base_PolygonInfo(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.PolygonInfo"); + tolua_cclass(tolua_S,"PolygonInfo","ax.PolygonInfo","",nullptr); + + tolua_beginmodule(tolua_S,"PolygonInfo"); + tolua_function(tolua_S,"new",lua_ax_base_PolygonInfo_constructor); + tolua_function(tolua_S,"setQuad",lua_ax_base_PolygonInfo_setQuad); + tolua_function(tolua_S,"setQuads",lua_ax_base_PolygonInfo_setQuads); + tolua_function(tolua_S,"setTriangles",lua_ax_base_PolygonInfo_setTriangles); + tolua_function(tolua_S,"getVertCount",lua_ax_base_PolygonInfo_getVertCount); + tolua_function(tolua_S,"getTrianglesCount",lua_ax_base_PolygonInfo_getTrianglesCount); + tolua_function(tolua_S,"getArea",lua_ax_base_PolygonInfo_getArea); + tolua_function(tolua_S,"getRect",lua_ax_base_PolygonInfo_getRect); + tolua_function(tolua_S,"setRect",lua_ax_base_PolygonInfo_setRect); + tolua_function(tolua_S,"getFilename",lua_ax_base_PolygonInfo_getFilename); + tolua_function(tolua_S,"setFilename",lua_ax_base_PolygonInfo_setFilename); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::PolygonInfo).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.PolygonInfo"; + g_typeCast[typeName] = "ax.PolygonInfo"; + return 1; +} + +int lua_ax_base_AutoPolygon_constructor(lua_State* tolua_S) +{ + int argc = 0; + ax::AutoPolygon* cobj = nullptr; + bool ok = true; + +#if _AX_DEBUG >= 1 + tolua_Error tolua_err; +#endif + + + + argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::AnchorPreset arg0; + std::string_view arg0; - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Director:setStatsAnchor"); + ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.AutoPolygon:AutoPolygon"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_setStatsAnchor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_AutoPolygon_constructor'", nullptr); return 0; } - cobj->setStatsAnchor(arg0); - lua_settop(tolua_S, 1); + cobj = new ax::AutoPolygon(arg0); + tolua_pushusertype(tolua_S,(void*)cobj,"ax.AutoPolygon"); + tolua_register_gc(tolua_S,lua_gettop(tolua_S)); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:setStatsAnchor",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.AutoPolygon:AutoPolygon",argc, 1); return 0; #if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_setStatsAnchor'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_AutoPolygon_constructor'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_getGLView(lua_State* tolua_S) + +static int lua_ax_base_AutoPolygon_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (AutoPolygon)"); + return 0; +} + +int lua_register_ax_base_AutoPolygon(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.AutoPolygon"); + tolua_cclass(tolua_S,"AutoPolygon","ax.AutoPolygon","",nullptr); + + tolua_beginmodule(tolua_S,"AutoPolygon"); + tolua_function(tolua_S,"new",lua_ax_base_AutoPolygon_constructor); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::AutoPolygon).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.AutoPolygon"; + g_typeCast[typeName] = "ax.AutoPolygon"; + return 1; +} + +int lua_ax_base_SpriteFrame_getRectInPixels(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::SpriteFrame* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -14849,15 +15002,15 @@ int lua_ax_base_Director_getGLView(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getGLView'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_getRectInPixels'", nullptr); return 0; } #endif @@ -14867,27 +15020,27 @@ int lua_ax_base_Director_getGLView(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getGLView'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_getRectInPixels'", nullptr); return 0; } - auto&& ret = cobj->getGLView(); - object_to_luaval(tolua_S, "ax.GLView",(ax::GLView*)ret); + auto&& ret = cobj->getRectInPixels(); + rect_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getGLView",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:getRectInPixels",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getGLView'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_getRectInPixels'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_setGLView(lua_State* tolua_S) +int lua_ax_base_SpriteFrame_setRectInPixels(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::SpriteFrame* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -14896,15 +15049,15 @@ int lua_ax_base_Director_setGLView(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_setGLView'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_setRectInPixels'", nullptr); return 0; } #endif @@ -14912,79 +15065,32 @@ int lua_ax_base_Director_setGLView(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::GLView* arg0; + ax::Rect arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.GLView",&arg0, "ax.Director:setGLView"); + ok &= luaval_to_rect(tolua_S, 2, &arg0, "ax.SpriteFrame:setRectInPixels"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_setGLView'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_setRectInPixels'", nullptr); return 0; } - cobj->setGLView(arg0); + cobj->setRectInPixels(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:setGLView",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:setRectInPixels",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_setGLView'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_base_Director_getTextureCache(lua_State* tolua_S) -{ - int argc = 0; - ax::Director* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getTextureCache'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getTextureCache'", nullptr); - return 0; - } - auto&& ret = cobj->getTextureCache(); - object_to_luaval(tolua_S, "ax.TextureCache",(ax::TextureCache*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getTextureCache",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getTextureCache'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_setRectInPixels'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_isNextDeltaTimeZero(lua_State* tolua_S) +int lua_ax_base_SpriteFrame_isRotated(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::SpriteFrame* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -14993,15 +15099,15 @@ int lua_ax_base_Director_isNextDeltaTimeZero(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_isNextDeltaTimeZero'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_isRotated'", nullptr); return 0; } #endif @@ -15011,27 +15117,27 @@ int lua_ax_base_Director_isNextDeltaTimeZero(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_isNextDeltaTimeZero'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_isRotated'", nullptr); return 0; } - auto&& ret = cobj->isNextDeltaTimeZero(); + auto&& ret = cobj->isRotated(); tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:isNextDeltaTimeZero",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:isRotated",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_isNextDeltaTimeZero'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_isRotated'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_setNextDeltaTimeZero(lua_State* tolua_S) +int lua_ax_base_SpriteFrame_setRotated(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::SpriteFrame* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -15040,15 +15146,15 @@ int lua_ax_base_Director_setNextDeltaTimeZero(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_setNextDeltaTimeZero'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_setRotated'", nullptr); return 0; } #endif @@ -15058,30 +15164,30 @@ int lua_ax_base_Director_setNextDeltaTimeZero(lua_State* tolua_S) { bool arg0; - ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.Director:setNextDeltaTimeZero"); + ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.SpriteFrame:setRotated"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_setNextDeltaTimeZero'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_setRotated'", nullptr); return 0; } - cobj->setNextDeltaTimeZero(arg0); + cobj->setRotated(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:setNextDeltaTimeZero",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:setRotated",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_setNextDeltaTimeZero'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_setRotated'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_isPaused(lua_State* tolua_S) +int lua_ax_base_SpriteFrame_getRect(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::SpriteFrame* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -15090,15 +15196,15 @@ int lua_ax_base_Director_isPaused(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_isPaused'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_getRect'", nullptr); return 0; } #endif @@ -15108,27 +15214,27 @@ int lua_ax_base_Director_isPaused(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_isPaused'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_getRect'", nullptr); return 0; } - auto&& ret = cobj->isPaused(); - tolua_pushboolean(tolua_S,(bool)ret); + auto&& ret = cobj->getRect(); + rect_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:isPaused",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:getRect",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_isPaused'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_getRect'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_getTotalFrames(lua_State* tolua_S) +int lua_ax_base_SpriteFrame_setRect(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::SpriteFrame* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -15137,45 +15243,48 @@ int lua_ax_base_Director_getTotalFrames(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getTotalFrames'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_setRect'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + ax::Rect arg0; + + ok &= luaval_to_rect(tolua_S, 2, &arg0, "ax.SpriteFrame:setRect"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getTotalFrames'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_setRect'", nullptr); return 0; } - auto&& ret = cobj->getTotalFrames(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + cobj->setRect(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getTotalFrames",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:setRect",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getTotalFrames'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_setRect'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_setProjection(lua_State* tolua_S) +int lua_ax_base_SpriteFrame_getCenterRect(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::SpriteFrame* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -15184,48 +15293,45 @@ int lua_ax_base_Director_setProjection(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_setProjection'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_getCenterRect'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - ax::Director::Projection arg0; - - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Director:setProjection"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_setProjection'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_getCenterRect'", nullptr); return 0; } - cobj->setProjection(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getCenterRect(); + rect_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:setProjection",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:getCenterRect",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_setProjection'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_getCenterRect'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_setViewport(lua_State* tolua_S) +int lua_ax_base_SpriteFrame_setCenterRectInPixels(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::SpriteFrame* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -15234,45 +15340,48 @@ int lua_ax_base_Director_setViewport(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_setViewport'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_setCenterRectInPixels'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + ax::Rect arg0; + + ok &= luaval_to_rect(tolua_S, 2, &arg0, "ax.SpriteFrame:setCenterRectInPixels"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_setViewport'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_setCenterRectInPixels'", nullptr); return 0; } - cobj->setViewport(); + cobj->setCenterRectInPixels(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:setViewport",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:setCenterRectInPixels",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_setViewport'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_setCenterRectInPixels'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_isSendCleanupToScene(lua_State* tolua_S) +int lua_ax_base_SpriteFrame_hasCenterRect(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::SpriteFrame* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -15281,15 +15390,15 @@ int lua_ax_base_Director_isSendCleanupToScene(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_isSendCleanupToScene'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_hasCenterRect'", nullptr); return 0; } #endif @@ -15299,27 +15408,27 @@ int lua_ax_base_Director_isSendCleanupToScene(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_isSendCleanupToScene'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_hasCenterRect'", nullptr); return 0; } - auto&& ret = cobj->isSendCleanupToScene(); + auto&& ret = cobj->hasCenterRect(); tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:isSendCleanupToScene",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:hasCenterRect",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_isSendCleanupToScene'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_hasCenterRect'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_getNotificationNode(lua_State* tolua_S) +int lua_ax_base_SpriteFrame_getOffsetInPixels(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::SpriteFrame* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -15328,15 +15437,15 @@ int lua_ax_base_Director_getNotificationNode(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getNotificationNode'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_getOffsetInPixels'", nullptr); return 0; } #endif @@ -15346,27 +15455,27 @@ int lua_ax_base_Director_getNotificationNode(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getNotificationNode'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_getOffsetInPixels'", nullptr); return 0; } - auto&& ret = cobj->getNotificationNode(); - object_to_luaval(tolua_S, "ax.Node",(ax::Node*)ret); + auto&& ret = cobj->getOffsetInPixels(); + vec2_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getNotificationNode",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:getOffsetInPixels",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getNotificationNode'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_getOffsetInPixels'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_setNotificationNode(lua_State* tolua_S) +int lua_ax_base_SpriteFrame_setOffsetInPixels(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::SpriteFrame* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -15375,15 +15484,15 @@ int lua_ax_base_Director_setNotificationNode(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_setNotificationNode'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_setOffsetInPixels'", nullptr); return 0; } #endif @@ -15391,32 +15500,32 @@ int lua_ax_base_Director_setNotificationNode(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Node* arg0; + ax::Vec2 arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Director:setNotificationNode"); + ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.SpriteFrame:setOffsetInPixels"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_setNotificationNode'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_setOffsetInPixels'", nullptr); return 0; } - cobj->setNotificationNode(arg0); + cobj->setOffsetInPixels(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:setNotificationNode",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:setOffsetInPixels",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_setNotificationNode'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_setOffsetInPixels'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_getWinSize(lua_State* tolua_S) +int lua_ax_base_SpriteFrame_getOriginalSizeInPixels(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::SpriteFrame* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -15425,15 +15534,15 @@ int lua_ax_base_Director_getWinSize(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getWinSize'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_getOriginalSizeInPixels'", nullptr); return 0; } #endif @@ -15443,27 +15552,27 @@ int lua_ax_base_Director_getWinSize(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getWinSize'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_getOriginalSizeInPixels'", nullptr); return 0; } - auto&& ret = cobj->getWinSize(); + auto&& ret = cobj->getOriginalSizeInPixels(); vec2_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getWinSize",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:getOriginalSizeInPixels",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getWinSize'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_getOriginalSizeInPixels'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_getWinSizeInPixels(lua_State* tolua_S) +int lua_ax_base_SpriteFrame_setOriginalSizeInPixels(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::SpriteFrame* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -15472,45 +15581,48 @@ int lua_ax_base_Director_getWinSizeInPixels(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getWinSizeInPixels'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_setOriginalSizeInPixels'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + ax::Vec2 arg0; + + ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.SpriteFrame:setOriginalSizeInPixels"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getWinSizeInPixels'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_setOriginalSizeInPixels'", nullptr); return 0; } - auto&& ret = cobj->getWinSizeInPixels(); - vec2_to_luaval(tolua_S, ret); + cobj->setOriginalSizeInPixels(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getWinSizeInPixels",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:setOriginalSizeInPixels",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getWinSizeInPixels'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_setOriginalSizeInPixels'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_getVisibleSize(lua_State* tolua_S) +int lua_ax_base_SpriteFrame_getOriginalSize(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::SpriteFrame* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -15519,15 +15631,15 @@ int lua_ax_base_Director_getVisibleSize(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getVisibleSize'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_getOriginalSize'", nullptr); return 0; } #endif @@ -15537,27 +15649,27 @@ int lua_ax_base_Director_getVisibleSize(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getVisibleSize'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_getOriginalSize'", nullptr); return 0; } - auto&& ret = cobj->getVisibleSize(); + auto&& ret = cobj->getOriginalSize(); vec2_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getVisibleSize",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:getOriginalSize",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getVisibleSize'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_getOriginalSize'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_getVisibleOrigin(lua_State* tolua_S) +int lua_ax_base_SpriteFrame_setOriginalSize(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::SpriteFrame* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -15566,45 +15678,48 @@ int lua_ax_base_Director_getVisibleOrigin(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getVisibleOrigin'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_setOriginalSize'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + ax::Vec2 arg0; + + ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.SpriteFrame:setOriginalSize"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getVisibleOrigin'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_setOriginalSize'", nullptr); return 0; } - auto&& ret = cobj->getVisibleOrigin(); - vec2_to_luaval(tolua_S, ret); + cobj->setOriginalSize(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getVisibleOrigin",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:setOriginalSize",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getVisibleOrigin'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_setOriginalSize'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_getSafeAreaRect(lua_State* tolua_S) +int lua_ax_base_SpriteFrame_getTexture(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::SpriteFrame* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -15613,15 +15728,15 @@ int lua_ax_base_Director_getSafeAreaRect(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getSafeAreaRect'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_getTexture'", nullptr); return 0; } #endif @@ -15631,27 +15746,27 @@ int lua_ax_base_Director_getSafeAreaRect(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getSafeAreaRect'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_getTexture'", nullptr); return 0; } - auto&& ret = cobj->getSafeAreaRect(); - rect_to_luaval(tolua_S, ret); + auto&& ret = cobj->getTexture(); + object_to_luaval(tolua_S, "ax.Texture2D",(ax::Texture2D*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getSafeAreaRect",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:getTexture",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getSafeAreaRect'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_getTexture'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_convertToGL(lua_State* tolua_S) +int lua_ax_base_SpriteFrame_setTexture(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::SpriteFrame* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -15660,15 +15775,15 @@ int lua_ax_base_Director_convertToGL(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_convertToGL'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_setTexture'", nullptr); return 0; } #endif @@ -15676,32 +15791,32 @@ int lua_ax_base_Director_convertToGL(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Vec2 arg0; + ax::Texture2D* arg0; - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.Director:convertToGL"); + ok &= luaval_to_object(tolua_S, 2, "ax.Texture2D",&arg0, "ax.SpriteFrame:setTexture"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_convertToGL'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_setTexture'", nullptr); return 0; } - auto&& ret = cobj->convertToGL(arg0); - vec2_to_luaval(tolua_S, ret); + cobj->setTexture(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:convertToGL",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:setTexture",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_convertToGL'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_setTexture'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_convertToUI(lua_State* tolua_S) +int lua_ax_base_SpriteFrame_getOffset(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::SpriteFrame* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -15710,48 +15825,45 @@ int lua_ax_base_Director_convertToUI(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_convertToUI'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_getOffset'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - ax::Vec2 arg0; - - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.Director:convertToUI"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_convertToUI'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_getOffset'", nullptr); return 0; } - auto&& ret = cobj->convertToUI(arg0); + auto&& ret = cobj->getOffset(); vec2_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:convertToUI",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:getOffset",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_convertToUI'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_getOffset'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_getZEye(lua_State* tolua_S) +int lua_ax_base_SpriteFrame_setOffset(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::SpriteFrame* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -15760,45 +15872,48 @@ int lua_ax_base_Director_getZEye(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getZEye'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_setOffset'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + ax::Vec2 arg0; + + ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.SpriteFrame:setOffset"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getZEye'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_setOffset'", nullptr); return 0; } - auto&& ret = cobj->getZEye(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + cobj->setOffset(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getZEye",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:setOffset",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getZEye'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_setOffset'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_runWithScene(lua_State* tolua_S) +int lua_ax_base_SpriteFrame_getAnchorPoint(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::SpriteFrame* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -15807,48 +15922,45 @@ int lua_ax_base_Director_runWithScene(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_runWithScene'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_getAnchorPoint'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - ax::Scene* arg0; - - ok &= luaval_to_object(tolua_S, 2, "ax.Scene",&arg0, "ax.Director:runWithScene"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_runWithScene'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_getAnchorPoint'", nullptr); return 0; } - cobj->runWithScene(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getAnchorPoint(); + vec2_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:runWithScene",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:getAnchorPoint",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_runWithScene'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_getAnchorPoint'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_pushScene(lua_State* tolua_S) +int lua_ax_base_SpriteFrame_setAnchorPoint(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::SpriteFrame* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -15857,15 +15969,15 @@ int lua_ax_base_Director_pushScene(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_pushScene'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_setAnchorPoint'", nullptr); return 0; } #endif @@ -15873,32 +15985,32 @@ int lua_ax_base_Director_pushScene(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Scene* arg0; + ax::Vec2 arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.Scene",&arg0, "ax.Director:pushScene"); + ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.SpriteFrame:setAnchorPoint"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_pushScene'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_setAnchorPoint'", nullptr); return 0; } - cobj->pushScene(arg0); + cobj->setAnchorPoint(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:pushScene",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:setAnchorPoint",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_pushScene'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_setAnchorPoint'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_popScene(lua_State* tolua_S) +int lua_ax_base_SpriteFrame_hasAnchorPoint(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::SpriteFrame* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -15907,15 +16019,15 @@ int lua_ax_base_Director_popScene(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_popScene'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_hasAnchorPoint'", nullptr); return 0; } #endif @@ -15925,27 +16037,27 @@ int lua_ax_base_Director_popScene(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_popScene'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_hasAnchorPoint'", nullptr); return 0; } - cobj->popScene(); - lua_settop(tolua_S, 1); + auto&& ret = cobj->hasAnchorPoint(); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:popScene",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:hasAnchorPoint",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_popScene'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_hasAnchorPoint'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_popToRootScene(lua_State* tolua_S) +int lua_ax_base_SpriteFrame_clone(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::SpriteFrame* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -15954,15 +16066,15 @@ int lua_ax_base_Director_popToRootScene(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_popToRootScene'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_clone'", nullptr); return 0; } #endif @@ -15972,221 +16084,387 @@ int lua_ax_base_Director_popToRootScene(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_popToRootScene'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_clone'", nullptr); return 0; } - cobj->popToRootScene(); - lua_settop(tolua_S, 1); + auto&& ret = cobj->clone(); + object_to_luaval(tolua_S, "ax.SpriteFrame",(ax::SpriteFrame*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:popToRootScene",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:clone",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_popToRootScene'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_clone'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_popToSceneStackLevel(lua_State* tolua_S) +int lua_ax_base_SpriteFrame_initWithTexture(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::SpriteFrame* cobj = nullptr; bool ok = true; - #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; #endif - - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); - + cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_popToSceneStackLevel'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_initWithTexture'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - int arg0; + do{ + if (argc == 5) { + ax::Texture2D* arg0; + ok &= luaval_to_object(tolua_S, 2, "ax.Texture2D",&arg0, "ax.SpriteFrame:initWithTexture"); - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Director:popToSceneStackLevel"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_popToSceneStackLevel'", nullptr); - return 0; + if (!ok) { break; } + ax::Rect arg1; + ok &= luaval_to_rect(tolua_S, 3, &arg1, "ax.SpriteFrame:initWithTexture"); + + if (!ok) { break; } + bool arg2; + ok &= luaval_to_boolean(tolua_S, 4,&arg2, "ax.SpriteFrame:initWithTexture"); + + if (!ok) { break; } + ax::Vec2 arg3; + ok &= luaval_to_vec2(tolua_S, 5, &arg3, "ax.SpriteFrame:initWithTexture"); + + if (!ok) { break; } + ax::Vec2 arg4; + ok &= luaval_to_vec2(tolua_S, 6, &arg4, "ax.SpriteFrame:initWithTexture"); + + if (!ok) { break; } + bool ret = cobj->initWithTexture(arg0, arg1, arg2, arg3, arg4); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; } - cobj->popToSceneStackLevel(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:popToSceneStackLevel",argc, 1); + }while(0); + ok = true; + do{ + if (argc == 2) { + ax::Texture2D* arg0; + ok &= luaval_to_object(tolua_S, 2, "ax.Texture2D",&arg0, "ax.SpriteFrame:initWithTexture"); + + if (!ok) { break; } + ax::Rect arg1; + ok &= luaval_to_rect(tolua_S, 3, &arg1, "ax.SpriteFrame:initWithTexture"); + + if (!ok) { break; } + bool ret = cobj->initWithTexture(arg0, arg1); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; + } + }while(0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:initWithTexture",argc, 2); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_popToSceneStackLevel'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_initWithTexture'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_replaceScene(lua_State* tolua_S) +int lua_ax_base_SpriteFrame_initWithTextureFilename(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::SpriteFrame* cobj = nullptr; bool ok = true; - #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; #endif - - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); - + cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_replaceScene'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_initWithTextureFilename'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - ax::Scene* arg0; + do{ + if (argc == 5) { + std::string_view arg0; + ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.SpriteFrame:initWithTextureFilename"); - ok &= luaval_to_object(tolua_S, 2, "ax.Scene",&arg0, "ax.Director:replaceScene"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_replaceScene'", nullptr); - return 0; + if (!ok) { break; } + ax::Rect arg1; + ok &= luaval_to_rect(tolua_S, 3, &arg1, "ax.SpriteFrame:initWithTextureFilename"); + + if (!ok) { break; } + bool arg2; + ok &= luaval_to_boolean(tolua_S, 4,&arg2, "ax.SpriteFrame:initWithTextureFilename"); + + if (!ok) { break; } + ax::Vec2 arg3; + ok &= luaval_to_vec2(tolua_S, 5, &arg3, "ax.SpriteFrame:initWithTextureFilename"); + + if (!ok) { break; } + ax::Vec2 arg4; + ok &= luaval_to_vec2(tolua_S, 6, &arg4, "ax.SpriteFrame:initWithTextureFilename"); + + if (!ok) { break; } + bool ret = cobj->initWithTextureFilename(arg0, arg1, arg2, arg3, arg4); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; } - cobj->replaceScene(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:replaceScene",argc, 1); + }while(0); + ok = true; + do{ + if (argc == 2) { + std::string_view arg0; + ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.SpriteFrame:initWithTextureFilename"); + + if (!ok) { break; } + ax::Rect arg1; + ok &= luaval_to_rect(tolua_S, 3, &arg1, "ax.SpriteFrame:initWithTextureFilename"); + + if (!ok) { break; } + bool ret = cobj->initWithTextureFilename(arg0, arg1); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; + } + }while(0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:initWithTextureFilename",argc, 2); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_replaceScene'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_initWithTextureFilename'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_end(lua_State* tolua_S) +int lua_ax_base_SpriteFrame_create(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; bool ok = true; - #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + argc = lua_gettop(tolua_S)-1; -#if _AX_DEBUG >= 1 - if (!cobj) + do { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_end'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 5) + { + std::string_view arg0; + ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.SpriteFrame:create"); + if (!ok) { break; } + ax::Rect arg1; + ok &= luaval_to_rect(tolua_S, 3, &arg1, "ax.SpriteFrame:create"); + if (!ok) { break; } + bool arg2; + ok &= luaval_to_boolean(tolua_S, 4,&arg2, "ax.SpriteFrame:create"); + if (!ok) { break; } + ax::Vec2 arg3; + ok &= luaval_to_vec2(tolua_S, 5, &arg3, "ax.SpriteFrame:create"); + if (!ok) { break; } + ax::Vec2 arg4; + ok &= luaval_to_vec2(tolua_S, 6, &arg4, "ax.SpriteFrame:create"); + if (!ok) { break; } + ax::SpriteFrame* ret = ax::SpriteFrame::create(arg0, arg1, arg2, arg3, arg4); + object_to_luaval(tolua_S, "ax.SpriteFrame",(ax::SpriteFrame*)ret); + return 1; + } + } while (0); + ok = true; + do { - if(!ok) + if (argc == 2) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_end'", nullptr); - return 0; + std::string_view arg0; + ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.SpriteFrame:create"); + if (!ok) { break; } + ax::Rect arg1; + ok &= luaval_to_rect(tolua_S, 3, &arg1, "ax.SpriteFrame:create"); + if (!ok) { break; } + ax::SpriteFrame* ret = ax::SpriteFrame::create(arg0, arg1); + object_to_luaval(tolua_S, "ax.SpriteFrame",(ax::SpriteFrame*)ret); + return 1; } - cobj->end(); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:end",argc, 0); + } while (0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d", "ax.SpriteFrame:create",argc, 2); return 0; - #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_end'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_create'.",&tolua_err); #endif - return 0; } -int lua_ax_base_Director_pause(lua_State* tolua_S) +int lua_ax_base_SpriteFrame_createWithTexture(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; bool ok = true; - #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + argc = lua_gettop(tolua_S)-1; -#if _AX_DEBUG >= 1 - if (!cobj) + do { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_pause'", nullptr); - return 0; - } + if (argc == 5) + { + ax::Texture2D* arg0; + ok &= luaval_to_object(tolua_S, 2, "ax.Texture2D",&arg0, "ax.SpriteFrame:createWithTexture"); + if (!ok) { break; } + ax::Rect arg1; + ok &= luaval_to_rect(tolua_S, 3, &arg1, "ax.SpriteFrame:createWithTexture"); + if (!ok) { break; } + bool arg2; + ok &= luaval_to_boolean(tolua_S, 4,&arg2, "ax.SpriteFrame:createWithTexture"); + if (!ok) { break; } + ax::Vec2 arg3; + ok &= luaval_to_vec2(tolua_S, 5, &arg3, "ax.SpriteFrame:createWithTexture"); + if (!ok) { break; } + ax::Vec2 arg4; + ok &= luaval_to_vec2(tolua_S, 6, &arg4, "ax.SpriteFrame:createWithTexture"); + if (!ok) { break; } + ax::SpriteFrame* ret = ax::SpriteFrame::createWithTexture(arg0, arg1, arg2, arg3, arg4); + object_to_luaval(tolua_S, "ax.SpriteFrame",(ax::SpriteFrame*)ret); + return 1; + } + } while (0); + ok = true; + do + { + if (argc == 2) + { + ax::Texture2D* arg0; + ok &= luaval_to_object(tolua_S, 2, "ax.Texture2D",&arg0, "ax.SpriteFrame:createWithTexture"); + if (!ok) { break; } + ax::Rect arg1; + ok &= luaval_to_rect(tolua_S, 3, &arg1, "ax.SpriteFrame:createWithTexture"); + if (!ok) { break; } + ax::SpriteFrame* ret = ax::SpriteFrame::createWithTexture(arg0, arg1); + object_to_luaval(tolua_S, "ax.SpriteFrame",(ax::SpriteFrame*)ret); + return 1; + } + } while (0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d", "ax.SpriteFrame:createWithTexture",argc, 2); + return 0; +#if _AX_DEBUG >= 1 + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_createWithTexture'.",&tolua_err); +#endif + return 0; +} +int lua_ax_base_SpriteFrame_constructor(lua_State* tolua_S) +{ + int argc = 0; + ax::SpriteFrame* cobj = nullptr; + bool ok = true; + +#if _AX_DEBUG >= 1 + tolua_Error tolua_err; #endif + + argc = lua_gettop(tolua_S)-1; if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_pause'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_constructor'", nullptr); return 0; } - cobj->pause(); - lua_settop(tolua_S, 1); + cobj = new ax::SpriteFrame(); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.SpriteFrame"); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:pause",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:SpriteFrame",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_pause'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_constructor'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_resume(lua_State* tolua_S) + +static int lua_ax_base_SpriteFrame_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (SpriteFrame)"); + return 0; +} + +int lua_register_ax_base_SpriteFrame(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.SpriteFrame"); + tolua_cclass(tolua_S,"SpriteFrame","ax.SpriteFrame","ax.Object",nullptr); + + tolua_beginmodule(tolua_S,"SpriteFrame"); + tolua_function(tolua_S,"new",lua_ax_base_SpriteFrame_constructor); + tolua_function(tolua_S,"getRectInPixels",lua_ax_base_SpriteFrame_getRectInPixels); + tolua_function(tolua_S,"setRectInPixels",lua_ax_base_SpriteFrame_setRectInPixels); + tolua_function(tolua_S,"isRotated",lua_ax_base_SpriteFrame_isRotated); + tolua_function(tolua_S,"setRotated",lua_ax_base_SpriteFrame_setRotated); + tolua_function(tolua_S,"getRect",lua_ax_base_SpriteFrame_getRect); + tolua_function(tolua_S,"setRect",lua_ax_base_SpriteFrame_setRect); + tolua_function(tolua_S,"getCenterRect",lua_ax_base_SpriteFrame_getCenterRect); + tolua_function(tolua_S,"setCenterRectInPixels",lua_ax_base_SpriteFrame_setCenterRectInPixels); + tolua_function(tolua_S,"hasCenterRect",lua_ax_base_SpriteFrame_hasCenterRect); + tolua_function(tolua_S,"getOffsetInPixels",lua_ax_base_SpriteFrame_getOffsetInPixels); + tolua_function(tolua_S,"setOffsetInPixels",lua_ax_base_SpriteFrame_setOffsetInPixels); + tolua_function(tolua_S,"getOriginalSizeInPixels",lua_ax_base_SpriteFrame_getOriginalSizeInPixels); + tolua_function(tolua_S,"setOriginalSizeInPixels",lua_ax_base_SpriteFrame_setOriginalSizeInPixels); + tolua_function(tolua_S,"getOriginalSize",lua_ax_base_SpriteFrame_getOriginalSize); + tolua_function(tolua_S,"setOriginalSize",lua_ax_base_SpriteFrame_setOriginalSize); + tolua_function(tolua_S,"getTexture",lua_ax_base_SpriteFrame_getTexture); + tolua_function(tolua_S,"setTexture",lua_ax_base_SpriteFrame_setTexture); + tolua_function(tolua_S,"getOffset",lua_ax_base_SpriteFrame_getOffset); + tolua_function(tolua_S,"setOffset",lua_ax_base_SpriteFrame_setOffset); + tolua_function(tolua_S,"getAnchorPoint",lua_ax_base_SpriteFrame_getAnchorPoint); + tolua_function(tolua_S,"setAnchorPoint",lua_ax_base_SpriteFrame_setAnchorPoint); + tolua_function(tolua_S,"hasAnchorPoint",lua_ax_base_SpriteFrame_hasAnchorPoint); + tolua_function(tolua_S,"clone",lua_ax_base_SpriteFrame_clone); + tolua_function(tolua_S,"initWithTexture",lua_ax_base_SpriteFrame_initWithTexture); + tolua_function(tolua_S,"initWithTextureFilename",lua_ax_base_SpriteFrame_initWithTextureFilename); + tolua_function(tolua_S,"create", lua_ax_base_SpriteFrame_create); + tolua_function(tolua_S,"createWithTexture", lua_ax_base_SpriteFrame_createWithTexture); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::SpriteFrame).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.SpriteFrame"; + g_typeCast[typeName] = "ax.SpriteFrame"; + return 1; +} + +int lua_ax_base_AnimationFrame_getSpriteFrame(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::AnimationFrame* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -16195,15 +16473,15 @@ int lua_ax_base_Director_resume(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.AnimationFrame",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::AnimationFrame*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_resume'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_AnimationFrame_getSpriteFrame'", nullptr); return 0; } #endif @@ -16213,27 +16491,27 @@ int lua_ax_base_Director_resume(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_resume'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_AnimationFrame_getSpriteFrame'", nullptr); return 0; } - cobj->resume(); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getSpriteFrame(); + object_to_luaval(tolua_S, "ax.SpriteFrame",(ax::SpriteFrame*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:resume",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.AnimationFrame:getSpriteFrame",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_resume'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_AnimationFrame_getSpriteFrame'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_restart(lua_State* tolua_S) +int lua_ax_base_AnimationFrame_setSpriteFrame(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::AnimationFrame* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -16242,45 +16520,48 @@ int lua_ax_base_Director_restart(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.AnimationFrame",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::AnimationFrame*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_restart'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_AnimationFrame_setSpriteFrame'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + ax::SpriteFrame* arg0; + + ok &= luaval_to_object(tolua_S, 2, "ax.SpriteFrame",&arg0, "ax.AnimationFrame:setSpriteFrame"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_restart'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_AnimationFrame_setSpriteFrame'", nullptr); return 0; } - cobj->restart(); + cobj->setSpriteFrame(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:restart",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.AnimationFrame:setSpriteFrame",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_restart'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_AnimationFrame_setSpriteFrame'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_stopAnimation(lua_State* tolua_S) +int lua_ax_base_AnimationFrame_getDelayUnits(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::AnimationFrame* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -16289,15 +16570,15 @@ int lua_ax_base_Director_stopAnimation(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.AnimationFrame",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::AnimationFrame*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_stopAnimation'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_AnimationFrame_getDelayUnits'", nullptr); return 0; } #endif @@ -16307,27 +16588,27 @@ int lua_ax_base_Director_stopAnimation(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_stopAnimation'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_AnimationFrame_getDelayUnits'", nullptr); return 0; } - cobj->stopAnimation(); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getDelayUnits(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:stopAnimation",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.AnimationFrame:getDelayUnits",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_stopAnimation'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_AnimationFrame_getDelayUnits'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_startAnimation(lua_State* tolua_S) +int lua_ax_base_AnimationFrame_setDelayUnits(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::AnimationFrame* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -16336,92 +16617,95 @@ int lua_ax_base_Director_startAnimation(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.AnimationFrame",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::AnimationFrame*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_startAnimation'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_AnimationFrame_setDelayUnits'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + double arg0; + + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.AnimationFrame:setDelayUnits"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_startAnimation'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_AnimationFrame_setDelayUnits'", nullptr); return 0; } - cobj->startAnimation(); + cobj->setDelayUnits(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:startAnimation",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.AnimationFrame:setDelayUnits",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_startAnimation'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_AnimationFrame_setDelayUnits'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_drawScene(lua_State* tolua_S) +int lua_ax_base_AnimationFrame_getUserInfo(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::AnimationFrame* cobj = nullptr; bool ok = true; - #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.AnimationFrame",0,&tolua_err)) goto tolua_lerror; #endif - - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); - + cobj = (ax::AnimationFrame*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_drawScene'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_AnimationFrame_getUserInfo'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_drawScene'", nullptr); - return 0; + do{ + if (argc == 0) { + ax::ValueMap& ret = cobj->getUserInfo(); + ccvaluemap_to_luaval(tolua_S, ret); + return 1; } - cobj->drawScene(); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:drawScene",argc, 0); + }while(0); + ok = true; + do{ + if (argc == 0) { + const ax::ValueMap& ret = cobj->getUserInfo(); + ccvaluemap_to_luaval(tolua_S, ret); + return 1; + } + }while(0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.AnimationFrame:getUserInfo",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_drawScene'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_AnimationFrame_getUserInfo'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_purgeCachedData(lua_State* tolua_S) +int lua_ax_base_AnimationFrame_setUserInfo(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::AnimationFrame* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -16430,45 +16714,48 @@ int lua_ax_base_Director_purgeCachedData(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.AnimationFrame",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::AnimationFrame*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_purgeCachedData'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_AnimationFrame_setUserInfo'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + ax::ValueMap arg0; + + ok &= luaval_to_ccvaluemap(tolua_S, 2, &arg0, "ax.AnimationFrame:setUserInfo"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_purgeCachedData'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_AnimationFrame_setUserInfo'", nullptr); return 0; } - cobj->purgeCachedData(); + cobj->setUserInfo(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:purgeCachedData",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.AnimationFrame:setUserInfo",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_purgeCachedData'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_AnimationFrame_setUserInfo'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_setDefaultValues(lua_State* tolua_S) +int lua_ax_base_AnimationFrame_clone(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::AnimationFrame* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -16477,15 +16764,15 @@ int lua_ax_base_Director_setDefaultValues(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.AnimationFrame",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::AnimationFrame*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_setDefaultValues'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_AnimationFrame_clone'", nullptr); return 0; } #endif @@ -16495,27 +16782,27 @@ int lua_ax_base_Director_setDefaultValues(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_setDefaultValues'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_AnimationFrame_clone'", nullptr); return 0; } - cobj->setDefaultValues(); - lua_settop(tolua_S, 1); + auto&& ret = cobj->clone(); + object_to_luaval(tolua_S, "ax.AnimationFrame",(ax::AnimationFrame*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:setDefaultValues",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.AnimationFrame:clone",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_setDefaultValues'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_AnimationFrame_clone'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_setGLDefaultValues(lua_State* tolua_S) +int lua_ax_base_AnimationFrame_initWithSpriteFrame(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::AnimationFrame* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -16524,146 +16811,210 @@ int lua_ax_base_Director_setGLDefaultValues(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.AnimationFrame",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::AnimationFrame*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_setGLDefaultValues'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_AnimationFrame_initWithSpriteFrame'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 3) { + ax::SpriteFrame* arg0; + double arg1; + ax::ValueMap arg2; + + ok &= luaval_to_object(tolua_S, 2, "ax.SpriteFrame",&arg0, "ax.AnimationFrame:initWithSpriteFrame"); + + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.AnimationFrame:initWithSpriteFrame"); + + ok &= luaval_to_ccvaluemap(tolua_S, 4, &arg2, "ax.AnimationFrame:initWithSpriteFrame"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_setGLDefaultValues'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_AnimationFrame_initWithSpriteFrame'", nullptr); return 0; } - cobj->setGLDefaultValues(); - lua_settop(tolua_S, 1); + auto&& ret = cobj->initWithSpriteFrame(arg0, arg1, arg2); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:setGLDefaultValues",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.AnimationFrame:initWithSpriteFrame",argc, 3); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_setGLDefaultValues'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_AnimationFrame_initWithSpriteFrame'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_setClearColor(lua_State* tolua_S) +int lua_ax_base_AnimationFrame_create(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.AnimationFrame",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + argc = lua_gettop(tolua_S) - 1; -#if _AX_DEBUG >= 1 - if (!cobj) + if (argc == 3) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_setClearColor'", nullptr); - return 0; + ax::SpriteFrame* arg0; + double arg1; + ax::ValueMap arg2; + ok &= luaval_to_object(tolua_S, 2, "ax.SpriteFrame",&arg0, "ax.AnimationFrame:create"); + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.AnimationFrame:create"); + ok &= luaval_to_ccvaluemap(tolua_S, 4, &arg2, "ax.AnimationFrame:create"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_AnimationFrame_create'", nullptr); + return 0; + } + auto&& ret = ax::AnimationFrame::create(arg0, arg1, arg2); + object_to_luaval(tolua_S, "ax.AnimationFrame",(ax::AnimationFrame*)ret); + return 1; } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.AnimationFrame:create",argc, 3); + return 0; +#if _AX_DEBUG >= 1 + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_AnimationFrame_create'.",&tolua_err); +#endif + return 0; +} +int lua_ax_base_AnimationFrame_constructor(lua_State* tolua_S) +{ + int argc = 0; + ax::AnimationFrame* cobj = nullptr; + bool ok = true; + +#if _AX_DEBUG >= 1 + tolua_Error tolua_err; #endif + + argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - ax::Color4F arg0; - - ok &=luaval_to_color4f(tolua_S, 2, &arg0, "ax.Director:setClearColor"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_setClearColor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_AnimationFrame_constructor'", nullptr); return 0; } - cobj->setClearColor(arg0); - lua_settop(tolua_S, 1); + cobj = new ax::AnimationFrame(); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.AnimationFrame"); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:setClearColor",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.AnimationFrame:AnimationFrame",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_setClearColor'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_AnimationFrame_constructor'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_mainLoop(lua_State* tolua_S) + +static int lua_ax_base_AnimationFrame_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (AnimationFrame)"); + return 0; +} + +int lua_register_ax_base_AnimationFrame(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.AnimationFrame"); + tolua_cclass(tolua_S,"AnimationFrame","ax.AnimationFrame","ax.Object",nullptr); + + tolua_beginmodule(tolua_S,"AnimationFrame"); + tolua_function(tolua_S,"new",lua_ax_base_AnimationFrame_constructor); + tolua_function(tolua_S,"getSpriteFrame",lua_ax_base_AnimationFrame_getSpriteFrame); + tolua_function(tolua_S,"setSpriteFrame",lua_ax_base_AnimationFrame_setSpriteFrame); + tolua_function(tolua_S,"getDelayUnits",lua_ax_base_AnimationFrame_getDelayUnits); + tolua_function(tolua_S,"setDelayUnits",lua_ax_base_AnimationFrame_setDelayUnits); + tolua_function(tolua_S,"getUserInfo",lua_ax_base_AnimationFrame_getUserInfo); + tolua_function(tolua_S,"setUserInfo",lua_ax_base_AnimationFrame_setUserInfo); + tolua_function(tolua_S,"clone",lua_ax_base_AnimationFrame_clone); + tolua_function(tolua_S,"initWithSpriteFrame",lua_ax_base_AnimationFrame_initWithSpriteFrame); + tolua_function(tolua_S,"create", lua_ax_base_AnimationFrame_create); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::AnimationFrame).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.AnimationFrame"; + g_typeCast[typeName] = "ax.AnimationFrame"; + return 1; +} + +int lua_ax_base_Animation_addSpriteFrame(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::Animation* cobj = nullptr; bool ok = true; + #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + + cobj = (ax::Animation*)tolua_tousertype(tolua_S,1,0); + #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_mainLoop'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animation_addSpriteFrame'", nullptr); return 0; } #endif + argc = lua_gettop(tolua_S)-1; - do{ - if (argc == 1) { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Director:mainLoop"); + if (argc == 1) + { + ax::SpriteFrame* arg0; - if (!ok) { break; } - cobj->mainLoop(arg0); - lua_settop(tolua_S, 1); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 0) { - cobj->mainLoop(); - lua_settop(tolua_S, 1); - return 1; + ok &= luaval_to_object(tolua_S, 2, "ax.SpriteFrame",&arg0, "ax.Animation:addSpriteFrame"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_addSpriteFrame'", nullptr); + return 0; } - }while(0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:mainLoop",argc, 0); + cobj->addSpriteFrame(arg0); + lua_settop(tolua_S, 1); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animation:addSpriteFrame",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_mainLoop'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_addSpriteFrame'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_setContentScaleFactor(lua_State* tolua_S) +int lua_ax_base_Animation_addSpriteFrameWithFile(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::Animation* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -16672,15 +17023,15 @@ int lua_ax_base_Director_setContentScaleFactor(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Animation*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_setContentScaleFactor'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animation_addSpriteFrameWithFile'", nullptr); return 0; } #endif @@ -16688,32 +17039,32 @@ int lua_ax_base_Director_setContentScaleFactor(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - double arg0; + std::string_view arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Director:setContentScaleFactor"); + ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Animation:addSpriteFrameWithFile"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_setContentScaleFactor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_addSpriteFrameWithFile'", nullptr); return 0; } - cobj->setContentScaleFactor(arg0); + cobj->addSpriteFrameWithFile(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:setContentScaleFactor",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animation:addSpriteFrameWithFile",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_setContentScaleFactor'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_addSpriteFrameWithFile'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_getContentScaleFactor(lua_State* tolua_S) +int lua_ax_base_Animation_addSpriteFrameWithTexture(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::Animation* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -16722,45 +17073,51 @@ int lua_ax_base_Director_getContentScaleFactor(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Animation*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getContentScaleFactor'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animation_addSpriteFrameWithTexture'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 2) { + ax::Texture2D* arg0; + ax::Rect arg1; + + ok &= luaval_to_object(tolua_S, 2, "ax.Texture2D",&arg0, "ax.Animation:addSpriteFrameWithTexture"); + + ok &= luaval_to_rect(tolua_S, 3, &arg1, "ax.Animation:addSpriteFrameWithTexture"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getContentScaleFactor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_addSpriteFrameWithTexture'", nullptr); return 0; } - auto&& ret = cobj->getContentScaleFactor(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + cobj->addSpriteFrameWithTexture(arg0, arg1); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getContentScaleFactor",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animation:addSpriteFrameWithTexture",argc, 2); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getContentScaleFactor'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_addSpriteFrameWithTexture'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_getScheduler(lua_State* tolua_S) +int lua_ax_base_Animation_getTotalDelayUnits(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::Animation* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -16769,15 +17126,15 @@ int lua_ax_base_Director_getScheduler(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Animation*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getScheduler'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animation_getTotalDelayUnits'", nullptr); return 0; } #endif @@ -16787,27 +17144,27 @@ int lua_ax_base_Director_getScheduler(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getScheduler'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_getTotalDelayUnits'", nullptr); return 0; } - auto&& ret = cobj->getScheduler(); - object_to_luaval(tolua_S, "ax.Scheduler",(ax::Scheduler*)ret); + auto&& ret = cobj->getTotalDelayUnits(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getScheduler",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animation:getTotalDelayUnits",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getScheduler'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_getTotalDelayUnits'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_setScheduler(lua_State* tolua_S) +int lua_ax_base_Animation_setDelayPerUnit(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::Animation* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -16816,15 +17173,15 @@ int lua_ax_base_Director_setScheduler(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Animation*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_setScheduler'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animation_setDelayPerUnit'", nullptr); return 0; } #endif @@ -16832,32 +17189,32 @@ int lua_ax_base_Director_setScheduler(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Scheduler* arg0; + double arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.Scheduler",&arg0, "ax.Director:setScheduler"); + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Animation:setDelayPerUnit"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_setScheduler'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_setDelayPerUnit'", nullptr); return 0; } - cobj->setScheduler(arg0); + cobj->setDelayPerUnit(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:setScheduler",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animation:setDelayPerUnit",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_setScheduler'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_setDelayPerUnit'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_getActionManager(lua_State* tolua_S) +int lua_ax_base_Animation_getDelayPerUnit(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::Animation* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -16866,15 +17223,15 @@ int lua_ax_base_Director_getActionManager(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Animation*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getActionManager'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animation_getDelayPerUnit'", nullptr); return 0; } #endif @@ -16884,27 +17241,27 @@ int lua_ax_base_Director_getActionManager(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getActionManager'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_getDelayPerUnit'", nullptr); return 0; } - auto&& ret = cobj->getActionManager(); - object_to_luaval(tolua_S, "ax.ActionManager",(ax::ActionManager*)ret); + auto&& ret = cobj->getDelayPerUnit(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getActionManager",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animation:getDelayPerUnit",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getActionManager'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_getDelayPerUnit'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_setActionManager(lua_State* tolua_S) +int lua_ax_base_Animation_getDuration(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::Animation* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -16913,48 +17270,45 @@ int lua_ax_base_Director_setActionManager(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Animation*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_setActionManager'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animation_getDuration'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - ax::ActionManager* arg0; - - ok &= luaval_to_object(tolua_S, 2, "ax.ActionManager",&arg0, "ax.Director:setActionManager"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_setActionManager'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_getDuration'", nullptr); return 0; } - cobj->setActionManager(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getDuration(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:setActionManager",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animation:getDuration",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_setActionManager'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_getDuration'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_getEventDispatcher(lua_State* tolua_S) +int lua_ax_base_Animation_getFrames(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::Animation* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -16963,15 +17317,15 @@ int lua_ax_base_Director_getEventDispatcher(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Animation*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getEventDispatcher'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animation_getFrames'", nullptr); return 0; } #endif @@ -16981,27 +17335,27 @@ int lua_ax_base_Director_getEventDispatcher(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getEventDispatcher'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_getFrames'", nullptr); return 0; } - auto&& ret = cobj->getEventDispatcher(); - object_to_luaval(tolua_S, "ax.EventDispatcher",(ax::EventDispatcher*)ret); + auto&& ret = cobj->getFrames(); + ccvector_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getEventDispatcher",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animation:getFrames",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getEventDispatcher'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_getFrames'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_setEventDispatcher(lua_State* tolua_S) +int lua_ax_base_Animation_setFrames(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::Animation* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -17010,15 +17364,15 @@ int lua_ax_base_Director_setEventDispatcher(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Animation*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_setEventDispatcher'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animation_setFrames'", nullptr); return 0; } #endif @@ -17026,32 +17380,32 @@ int lua_ax_base_Director_setEventDispatcher(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::EventDispatcher* arg0; + ax::Vector arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.EventDispatcher",&arg0, "ax.Director:setEventDispatcher"); + ok &= luaval_to_ccvector(tolua_S, 2, &arg0, "ax.Animation:setFrames"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_setEventDispatcher'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_setFrames'", nullptr); return 0; } - cobj->setEventDispatcher(arg0); + cobj->setFrames(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:setEventDispatcher",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animation:setFrames",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_setEventDispatcher'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_setFrames'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_getRenderer(lua_State* tolua_S) +int lua_ax_base_Animation_getRestoreOriginalFrame(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::Animation* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -17060,15 +17414,15 @@ int lua_ax_base_Director_getRenderer(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Animation*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getRenderer'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animation_getRestoreOriginalFrame'", nullptr); return 0; } #endif @@ -17078,27 +17432,27 @@ int lua_ax_base_Director_getRenderer(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getRenderer'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_getRestoreOriginalFrame'", nullptr); return 0; } - auto&& ret = cobj->getRenderer(); - object_to_luaval(tolua_S, "ax.Renderer",(ax::Renderer*)ret); + auto&& ret = cobj->getRestoreOriginalFrame(); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getRenderer",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animation:getRestoreOriginalFrame",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getRenderer'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_getRestoreOriginalFrame'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_getDeltaTime(lua_State* tolua_S) +int lua_ax_base_Animation_setRestoreOriginalFrame(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::Animation* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -17107,45 +17461,48 @@ int lua_ax_base_Director_getDeltaTime(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Animation*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getDeltaTime'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animation_setRestoreOriginalFrame'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + bool arg0; + + ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.Animation:setRestoreOriginalFrame"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getDeltaTime'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_setRestoreOriginalFrame'", nullptr); return 0; } - auto&& ret = cobj->getDeltaTime(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + cobj->setRestoreOriginalFrame(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getDeltaTime",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animation:setRestoreOriginalFrame",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getDeltaTime'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_setRestoreOriginalFrame'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_getFrameRate(lua_State* tolua_S) +int lua_ax_base_Animation_getLoops(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::Animation* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -17154,15 +17511,15 @@ int lua_ax_base_Director_getFrameRate(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Animation*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getFrameRate'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animation_getLoops'", nullptr); return 0; } #endif @@ -17172,27 +17529,27 @@ int lua_ax_base_Director_getFrameRate(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getFrameRate'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_getLoops'", nullptr); return 0; } - auto&& ret = cobj->getFrameRate(); + auto&& ret = cobj->getLoops(); tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getFrameRate",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animation:getLoops",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getFrameRate'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_getLoops'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_pushMatrix(lua_State* tolua_S) +int lua_ax_base_Animation_setLoops(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::Animation* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -17201,15 +17558,15 @@ int lua_ax_base_Director_pushMatrix(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Animation*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_pushMatrix'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animation_setLoops'", nullptr); return 0; } #endif @@ -17217,32 +17574,32 @@ int lua_ax_base_Director_pushMatrix(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::MATRIX_STACK_TYPE arg0; + unsigned int arg0; - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Director:pushMatrix"); + ok &= luaval_to_uint32(tolua_S, 2,&arg0, "ax.Animation:setLoops"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_pushMatrix'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_setLoops'", nullptr); return 0; } - cobj->pushMatrix(arg0); + cobj->setLoops(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:pushMatrix",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animation:setLoops",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_pushMatrix'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_setLoops'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_popMatrix(lua_State* tolua_S) +int lua_ax_base_Animation_clone(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::Animation* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -17251,48 +17608,45 @@ int lua_ax_base_Director_popMatrix(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Animation*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_popMatrix'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animation_clone'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - ax::MATRIX_STACK_TYPE arg0; - - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Director:popMatrix"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_popMatrix'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_clone'", nullptr); return 0; } - cobj->popMatrix(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->clone(); + object_to_luaval(tolua_S, "ax.Animation",(ax::Animation*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:popMatrix",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animation:clone",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_popMatrix'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_clone'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_loadIdentityMatrix(lua_State* tolua_S) +int lua_ax_base_Animation_init(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::Animation* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -17301,48 +17655,45 @@ int lua_ax_base_Director_loadIdentityMatrix(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Animation*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_loadIdentityMatrix'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animation_init'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - ax::MATRIX_STACK_TYPE arg0; - - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Director:loadIdentityMatrix"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_loadIdentityMatrix'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_init'", nullptr); return 0; } - cobj->loadIdentityMatrix(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->init(); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:loadIdentityMatrix",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animation:init",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_loadIdentityMatrix'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_init'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_loadMatrix(lua_State* tolua_S) +int lua_ax_base_Animation_initWithSpriteFrames(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::Animation* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -17351,51 +17702,85 @@ int lua_ax_base_Director_loadMatrix(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Animation*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_loadMatrix'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animation_initWithSpriteFrames'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 2) + if (argc == 1) { - ax::MATRIX_STACK_TYPE arg0; - ax::Mat4 arg1; + ax::Vector arg0; - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Director:loadMatrix"); + ok &= luaval_to_ccvector(tolua_S, 2, &arg0, "ax.Animation:initWithSpriteFrames"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_initWithSpriteFrames'", nullptr); + return 0; + } + auto&& ret = cobj->initWithSpriteFrames(arg0); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; + } + if (argc == 2) + { + ax::Vector arg0; + double arg1; - ok &= luaval_to_mat4(tolua_S, 3, &arg1, "ax.Director:loadMatrix"); + ok &= luaval_to_ccvector(tolua_S, 2, &arg0, "ax.Animation:initWithSpriteFrames"); + + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Animation:initWithSpriteFrames"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_loadMatrix'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_initWithSpriteFrames'", nullptr); return 0; } - cobj->loadMatrix(arg0, arg1); - lua_settop(tolua_S, 1); + auto&& ret = cobj->initWithSpriteFrames(arg0, arg1); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:loadMatrix",argc, 2); + if (argc == 3) + { + ax::Vector arg0; + double arg1; + unsigned int arg2; + + ok &= luaval_to_ccvector(tolua_S, 2, &arg0, "ax.Animation:initWithSpriteFrames"); + + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Animation:initWithSpriteFrames"); + + ok &= luaval_to_uint32(tolua_S, 4,&arg2, "ax.Animation:initWithSpriteFrames"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_initWithSpriteFrames'", nullptr); + return 0; + } + auto&& ret = cobj->initWithSpriteFrames(arg0, arg1, arg2); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animation:initWithSpriteFrames",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_loadMatrix'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_initWithSpriteFrames'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_multiplyMatrix(lua_State* tolua_S) +int lua_ax_base_Animation_initWithAnimationFrames(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::Animation* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -17404,101 +17789,265 @@ int lua_ax_base_Director_multiplyMatrix(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Animation*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_multiplyMatrix'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animation_initWithAnimationFrames'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 2) + if (argc == 3) { - ax::MATRIX_STACK_TYPE arg0; - ax::Mat4 arg1; + ax::Vector arg0; + double arg1; + unsigned int arg2; - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Director:multiplyMatrix"); + ok &= luaval_to_ccvector(tolua_S, 2, &arg0, "ax.Animation:initWithAnimationFrames"); - ok &= luaval_to_mat4(tolua_S, 3, &arg1, "ax.Director:multiplyMatrix"); + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Animation:initWithAnimationFrames"); + + ok &= luaval_to_uint32(tolua_S, 4,&arg2, "ax.Animation:initWithAnimationFrames"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_multiplyMatrix'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_initWithAnimationFrames'", nullptr); return 0; } - cobj->multiplyMatrix(arg0, arg1); - lua_settop(tolua_S, 1); + auto&& ret = cobj->initWithAnimationFrames(arg0, arg1, arg2); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:multiplyMatrix",argc, 2); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animation:initWithAnimationFrames",argc, 3); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_multiplyMatrix'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_initWithAnimationFrames'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_getMatrix(lua_State* tolua_S) +int lua_ax_base_Animation_create(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; bool ok = true; - #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif +#if _AX_DEBUG >= 1 + if (!tolua_isusertable(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; +#endif + + argc = lua_gettop(tolua_S)-1; + do + { + if (argc == 2) + { + ax::Vector arg0; + ok &= luaval_to_ccvector(tolua_S, 2, &arg0, "ax.Animation:create"); + if (!ok) { break; } + double arg1; + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Animation:create"); + if (!ok) { break; } + ax::Animation* ret = ax::Animation::create(arg0, arg1); + object_to_luaval(tolua_S, "ax.Animation",(ax::Animation*)ret); + return 1; + } + } while (0); + ok = true; + do + { + if (argc == 3) + { + ax::Vector arg0; + ok &= luaval_to_ccvector(tolua_S, 2, &arg0, "ax.Animation:create"); + if (!ok) { break; } + double arg1; + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Animation:create"); + if (!ok) { break; } + unsigned int arg2; + ok &= luaval_to_uint32(tolua_S, 4,&arg2, "ax.Animation:create"); + if (!ok) { break; } + ax::Animation* ret = ax::Animation::create(arg0, arg1, arg2); + object_to_luaval(tolua_S, "ax.Animation",(ax::Animation*)ret); + return 1; + } + } while (0); + ok = true; + do + { + if (argc == 0) + { + ax::Animation* ret = ax::Animation::create(); + object_to_luaval(tolua_S, "ax.Animation",(ax::Animation*)ret); + return 1; + } + } while (0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d", "ax.Animation:create",argc, 0); + return 0; #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_create'.",&tolua_err); #endif + return 0; +} +int lua_ax_base_Animation_createWithSpriteFrames(lua_State* tolua_S) +{ + int argc = 0; + bool ok = true; - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); +#if _AX_DEBUG >= 1 + tolua_Error tolua_err; +#endif #if _AX_DEBUG >= 1 - if (!cobj) + if (!tolua_isusertable(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; +#endif + + argc = lua_gettop(tolua_S) - 1; + + if (argc == 1) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getMatrix'", nullptr); - return 0; + ax::Vector arg0; + ok &= luaval_to_ccvector(tolua_S, 2, &arg0, "ax.Animation:createWithSpriteFrames"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_createWithSpriteFrames'", nullptr); + return 0; + } + auto&& ret = ax::Animation::createWithSpriteFrames(arg0); + object_to_luaval(tolua_S, "ax.Animation",(ax::Animation*)ret); + return 1; + } + if (argc == 2) + { + ax::Vector arg0; + double arg1; + ok &= luaval_to_ccvector(tolua_S, 2, &arg0, "ax.Animation:createWithSpriteFrames"); + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Animation:createWithSpriteFrames"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_createWithSpriteFrames'", nullptr); + return 0; + } + auto&& ret = ax::Animation::createWithSpriteFrames(arg0, arg1); + object_to_luaval(tolua_S, "ax.Animation",(ax::Animation*)ret); + return 1; + } + if (argc == 3) + { + ax::Vector arg0; + double arg1; + unsigned int arg2; + ok &= luaval_to_ccvector(tolua_S, 2, &arg0, "ax.Animation:createWithSpriteFrames"); + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Animation:createWithSpriteFrames"); + ok &= luaval_to_uint32(tolua_S, 4,&arg2, "ax.Animation:createWithSpriteFrames"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_createWithSpriteFrames'", nullptr); + return 0; + } + auto&& ret = ax::Animation::createWithSpriteFrames(arg0, arg1, arg2); + object_to_luaval(tolua_S, "ax.Animation",(ax::Animation*)ret); + return 1; } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Animation:createWithSpriteFrames",argc, 1); + return 0; +#if _AX_DEBUG >= 1 + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_createWithSpriteFrames'.",&tolua_err); +#endif + return 0; +} +int lua_ax_base_Animation_constructor(lua_State* tolua_S) +{ + int argc = 0; + ax::Animation* cobj = nullptr; + bool ok = true; + +#if _AX_DEBUG >= 1 + tolua_Error tolua_err; #endif + + argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - ax::MATRIX_STACK_TYPE arg0; - - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Director:getMatrix"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getMatrix'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_constructor'", nullptr); return 0; } - auto&& ret = cobj->getMatrix(arg0); - mat4_to_luaval(tolua_S, ret); + cobj = new ax::Animation(); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.Animation"); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getMatrix",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animation:Animation",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getMatrix'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_constructor'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_resetMatrixStack(lua_State* tolua_S) + +static int lua_ax_base_Animation_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (Animation)"); + return 0; +} + +int lua_register_ax_base_Animation(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.Animation"); + tolua_cclass(tolua_S,"Animation","ax.Animation","ax.Object",nullptr); + + tolua_beginmodule(tolua_S,"Animation"); + tolua_function(tolua_S,"new",lua_ax_base_Animation_constructor); + tolua_function(tolua_S,"addSpriteFrame",lua_ax_base_Animation_addSpriteFrame); + tolua_function(tolua_S,"addSpriteFrameWithFile",lua_ax_base_Animation_addSpriteFrameWithFile); + tolua_function(tolua_S,"addSpriteFrameWithTexture",lua_ax_base_Animation_addSpriteFrameWithTexture); + tolua_function(tolua_S,"getTotalDelayUnits",lua_ax_base_Animation_getTotalDelayUnits); + tolua_function(tolua_S,"setDelayPerUnit",lua_ax_base_Animation_setDelayPerUnit); + tolua_function(tolua_S,"getDelayPerUnit",lua_ax_base_Animation_getDelayPerUnit); + tolua_function(tolua_S,"getDuration",lua_ax_base_Animation_getDuration); + tolua_function(tolua_S,"getFrames",lua_ax_base_Animation_getFrames); + tolua_function(tolua_S,"setFrames",lua_ax_base_Animation_setFrames); + tolua_function(tolua_S,"getRestoreOriginalFrame",lua_ax_base_Animation_getRestoreOriginalFrame); + tolua_function(tolua_S,"setRestoreOriginalFrame",lua_ax_base_Animation_setRestoreOriginalFrame); + tolua_function(tolua_S,"getLoops",lua_ax_base_Animation_getLoops); + tolua_function(tolua_S,"setLoops",lua_ax_base_Animation_setLoops); + tolua_function(tolua_S,"clone",lua_ax_base_Animation_clone); + tolua_function(tolua_S,"init",lua_ax_base_Animation_init); + tolua_function(tolua_S,"initWithSpriteFrames",lua_ax_base_Animation_initWithSpriteFrames); + tolua_function(tolua_S,"initWithAnimationFrames",lua_ax_base_Animation_initWithAnimationFrames); + tolua_function(tolua_S,"create", lua_ax_base_Animation_create); + tolua_function(tolua_S,"createWithSpriteFrames", lua_ax_base_Animation_createWithSpriteFrames); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::Animation).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.Animation"; + g_typeCast[typeName] = "ax.Animation"; + return 1; +} + +int lua_ax_base_ActionInterval_getElapsed(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::ActionInterval* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -17507,15 +18056,15 @@ int lua_ax_base_Director_resetMatrixStack(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.ActionInterval",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::ActionInterval*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_resetMatrixStack'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_ActionInterval_getElapsed'", nullptr); return 0; } #endif @@ -17525,27 +18074,27 @@ int lua_ax_base_Director_resetMatrixStack(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_resetMatrixStack'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_ActionInterval_getElapsed'", nullptr); return 0; } - cobj->resetMatrixStack(); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getElapsed(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:resetMatrixStack",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.ActionInterval:getElapsed",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_resetMatrixStack'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_ActionInterval_getElapsed'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_getAxmolThreadId(lua_State* tolua_S) +int lua_ax_base_ActionInterval_setAmplitudeRate(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::ActionInterval* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -17554,45 +18103,48 @@ int lua_ax_base_Director_getAxmolThreadId(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.ActionInterval",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::ActionInterval*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getAxmolThreadId'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_ActionInterval_setAmplitudeRate'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + double arg0; + + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.ActionInterval:setAmplitudeRate"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getAxmolThreadId'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_ActionInterval_setAmplitudeRate'", nullptr); return 0; } - auto&& ret = cobj->getAxmolThreadId(); - std_thread_id_to_luaval(tolua_S, ret); + cobj->setAmplitudeRate(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getAxmolThreadId",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.ActionInterval:setAmplitudeRate",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getAxmolThreadId'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_ActionInterval_setAmplitudeRate'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_setChildrenIndexerEnabled(lua_State* tolua_S) +int lua_ax_base_ActionInterval_getAmplitudeRate(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::ActionInterval* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -17601,48 +18153,45 @@ int lua_ax_base_Director_setChildrenIndexerEnabled(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.ActionInterval",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::ActionInterval*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_setChildrenIndexerEnabled'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_ActionInterval_getAmplitudeRate'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - bool arg0; - - ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.Director:setChildrenIndexerEnabled"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_setChildrenIndexerEnabled'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_ActionInterval_getAmplitudeRate'", nullptr); return 0; } - cobj->setChildrenIndexerEnabled(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getAmplitudeRate(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:setChildrenIndexerEnabled",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.ActionInterval:getAmplitudeRate",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_setChildrenIndexerEnabled'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_ActionInterval_getAmplitudeRate'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_isChildrenIndexerEnabled(lua_State* tolua_S) +int lua_ax_base_ActionInterval_initWithDuration(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::ActionInterval* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -17651,45 +18200,71 @@ int lua_ax_base_Director_isChildrenIndexerEnabled(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.ActionInterval",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::ActionInterval*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_isChildrenIndexerEnabled'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_ActionInterval_initWithDuration'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + double arg0; + + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.ActionInterval:initWithDuration"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_isChildrenIndexerEnabled'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_ActionInterval_initWithDuration'", nullptr); return 0; } - auto&& ret = cobj->isChildrenIndexerEnabled(); + auto&& ret = cobj->initWithDuration(arg0); tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:isChildrenIndexerEnabled",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.ActionInterval:initWithDuration",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_isChildrenIndexerEnabled'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_ActionInterval_initWithDuration'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_isValid(lua_State* tolua_S) +static int lua_ax_base_ActionInterval_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (ActionInterval)"); + return 0; +} + +int lua_register_ax_base_ActionInterval(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.ActionInterval"); + tolua_cclass(tolua_S,"ActionInterval","ax.ActionInterval","ax.FiniteTimeAction",nullptr); + + tolua_beginmodule(tolua_S,"ActionInterval"); + tolua_function(tolua_S,"getElapsed",lua_ax_base_ActionInterval_getElapsed); + tolua_function(tolua_S,"setAmplitudeRate",lua_ax_base_ActionInterval_setAmplitudeRate); + tolua_function(tolua_S,"getAmplitudeRate",lua_ax_base_ActionInterval_getAmplitudeRate); + tolua_function(tolua_S,"initWithDuration",lua_ax_base_ActionInterval_initWithDuration); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::ActionInterval).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.ActionInterval"; + g_typeCast[typeName] = "ax.ActionInterval"; + return 1; +} + +int lua_ax_base_Sequence_initWithTwoActions(lua_State* tolua_S) { int argc = 0; - ax::Director* cobj = nullptr; + ax::Sequence* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -17698,203 +18273,160 @@ int lua_ax_base_Director_isValid(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Sequence",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Sequence*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_isValid'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Sequence_initWithTwoActions'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 2) { + ax::FiniteTimeAction* arg0; + ax::FiniteTimeAction* arg1; + + ok &= luaval_to_object(tolua_S, 2, "ax.FiniteTimeAction",&arg0, "ax.Sequence:initWithTwoActions"); + + ok &= luaval_to_object(tolua_S, 3, "ax.FiniteTimeAction",&arg1, "ax.Sequence:initWithTwoActions"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_isValid'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Sequence_initWithTwoActions'", nullptr); return 0; } - auto&& ret = cobj->isValid(); + auto&& ret = cobj->initWithTwoActions(arg0, arg1); tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:isValid",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Sequence:initWithTwoActions",argc, 2); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_isValid'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Sequence_initWithTwoActions'.",&tolua_err); #endif return 0; } -int lua_ax_base_Director_getInstance(lua_State* tolua_S) +int lua_ax_base_Sequence_init(lua_State* tolua_S) { int argc = 0; + ax::Sequence* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Sequence",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S) - 1; + cobj = (ax::Sequence*)tolua_tousertype(tolua_S,1,0); - if (argc == 0) +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Sequence_init'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 1) { + ax::Vector arg0; + + ok &= luaval_to_ccvector(tolua_S, 2, &arg0, "ax.Sequence:init"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getInstance'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Sequence_init'", nullptr); return 0; } - auto&& ret = ax::Director::getInstance(); - object_to_luaval(tolua_S, "ax.Director",(ax::Director*)ret); + auto&& ret = cobj->init(arg0); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Director:getInstance",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Sequence:init",argc, 1); return 0; + #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getInstance'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Sequence_init'.",&tolua_err); #endif + return 0; } -int lua_ax_base_Director_destroyInstance(lua_State* tolua_S) +int lua_ax_base_Sequence_constructor(lua_State* tolua_S) { int argc = 0; + ax::Sequence* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif -#if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; -#endif - argc = lua_gettop(tolua_S) - 1; - if (argc == 0) + argc = lua_gettop(tolua_S)-1; + if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_destroyInstance'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Sequence_constructor'", nullptr); return 0; } - ax::Director::destroyInstance(); - lua_settop(tolua_S, 1); + cobj = new ax::Sequence(); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.Sequence"); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Director:destroyInstance",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Sequence:Sequence",argc, 0); return 0; + #if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_destroyInstance'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Sequence_constructor'.",&tolua_err); #endif + return 0; } -static int lua_ax_base_Director_finalize(lua_State* tolua_S) + +static int lua_ax_base_Sequence_finalize(lua_State* tolua_S) { - AXLOGV("luabindings: finalizing LUA object (Director)"); + AXLOGV("luabindings: finalizing LUA object (Sequence)"); return 0; } -int lua_register_ax_base_Director(lua_State* tolua_S) +int lua_register_ax_base_Sequence(lua_State* tolua_S) { - tolua_usertype(tolua_S,"ax.Director"); - tolua_cclass(tolua_S,"Director","ax.Director","",nullptr); + tolua_usertype(tolua_S,"ax.Sequence"); + tolua_cclass(tolua_S,"Sequence","ax.Sequence","ax.ActionInterval",nullptr); - tolua_beginmodule(tolua_S,"Director"); - tolua_function(tolua_S,"init",lua_ax_base_Director_init); - tolua_function(tolua_S,"getRunningScene",lua_ax_base_Director_getRunningScene); - tolua_function(tolua_S,"getNextScene",lua_ax_base_Director_getNextScene); - tolua_function(tolua_S,"getAnimationInterval",lua_ax_base_Director_getAnimationInterval); - tolua_function(tolua_S,"setAnimationInterval",lua_ax_base_Director_setAnimationInterval); - tolua_function(tolua_S,"isStatsDisplay",lua_ax_base_Director_isStatsDisplay); - tolua_function(tolua_S,"setStatsDisplay",lua_ax_base_Director_setStatsDisplay); - tolua_function(tolua_S,"getSecondsPerFrame",lua_ax_base_Director_getSecondsPerFrame); - tolua_function(tolua_S,"setStatsAnchor",lua_ax_base_Director_setStatsAnchor); - tolua_function(tolua_S,"getGLView",lua_ax_base_Director_getGLView); - tolua_function(tolua_S,"setGLView",lua_ax_base_Director_setGLView); - tolua_function(tolua_S,"getTextureCache",lua_ax_base_Director_getTextureCache); - tolua_function(tolua_S,"isNextDeltaTimeZero",lua_ax_base_Director_isNextDeltaTimeZero); - tolua_function(tolua_S,"setNextDeltaTimeZero",lua_ax_base_Director_setNextDeltaTimeZero); - tolua_function(tolua_S,"isPaused",lua_ax_base_Director_isPaused); - tolua_function(tolua_S,"getTotalFrames",lua_ax_base_Director_getTotalFrames); - tolua_function(tolua_S,"setProjection",lua_ax_base_Director_setProjection); - tolua_function(tolua_S,"setViewport",lua_ax_base_Director_setViewport); - tolua_function(tolua_S,"isSendCleanupToScene",lua_ax_base_Director_isSendCleanupToScene); - tolua_function(tolua_S,"getNotificationNode",lua_ax_base_Director_getNotificationNode); - tolua_function(tolua_S,"setNotificationNode",lua_ax_base_Director_setNotificationNode); - tolua_function(tolua_S,"getWinSize",lua_ax_base_Director_getWinSize); - tolua_function(tolua_S,"getWinSizeInPixels",lua_ax_base_Director_getWinSizeInPixels); - tolua_function(tolua_S,"getVisibleSize",lua_ax_base_Director_getVisibleSize); - tolua_function(tolua_S,"getVisibleOrigin",lua_ax_base_Director_getVisibleOrigin); - tolua_function(tolua_S,"getSafeAreaRect",lua_ax_base_Director_getSafeAreaRect); - tolua_function(tolua_S,"convertToGL",lua_ax_base_Director_convertToGL); - tolua_function(tolua_S,"convertToUI",lua_ax_base_Director_convertToUI); - tolua_function(tolua_S,"getZEye",lua_ax_base_Director_getZEye); - tolua_function(tolua_S,"runWithScene",lua_ax_base_Director_runWithScene); - tolua_function(tolua_S,"pushScene",lua_ax_base_Director_pushScene); - tolua_function(tolua_S,"popScene",lua_ax_base_Director_popScene); - tolua_function(tolua_S,"popToRootScene",lua_ax_base_Director_popToRootScene); - tolua_function(tolua_S,"popToSceneStackLevel",lua_ax_base_Director_popToSceneStackLevel); - tolua_function(tolua_S,"replaceScene",lua_ax_base_Director_replaceScene); - tolua_function(tolua_S,"endToLua",lua_ax_base_Director_end); - tolua_function(tolua_S,"pause",lua_ax_base_Director_pause); - tolua_function(tolua_S,"resume",lua_ax_base_Director_resume); - tolua_function(tolua_S,"restart",lua_ax_base_Director_restart); - tolua_function(tolua_S,"stopAnimation",lua_ax_base_Director_stopAnimation); - tolua_function(tolua_S,"startAnimation",lua_ax_base_Director_startAnimation); - tolua_function(tolua_S,"drawScene",lua_ax_base_Director_drawScene); - tolua_function(tolua_S,"purgeCachedData",lua_ax_base_Director_purgeCachedData); - tolua_function(tolua_S,"setDefaultValues",lua_ax_base_Director_setDefaultValues); - tolua_function(tolua_S,"setGLDefaultValues",lua_ax_base_Director_setGLDefaultValues); - tolua_function(tolua_S,"setClearColor",lua_ax_base_Director_setClearColor); - tolua_function(tolua_S,"mainLoop",lua_ax_base_Director_mainLoop); - tolua_function(tolua_S,"setContentScaleFactor",lua_ax_base_Director_setContentScaleFactor); - tolua_function(tolua_S,"getContentScaleFactor",lua_ax_base_Director_getContentScaleFactor); - tolua_function(tolua_S,"getScheduler",lua_ax_base_Director_getScheduler); - tolua_function(tolua_S,"setScheduler",lua_ax_base_Director_setScheduler); - tolua_function(tolua_S,"getActionManager",lua_ax_base_Director_getActionManager); - tolua_function(tolua_S,"setActionManager",lua_ax_base_Director_setActionManager); - tolua_function(tolua_S,"getEventDispatcher",lua_ax_base_Director_getEventDispatcher); - tolua_function(tolua_S,"setEventDispatcher",lua_ax_base_Director_setEventDispatcher); - tolua_function(tolua_S,"getRenderer",lua_ax_base_Director_getRenderer); - tolua_function(tolua_S,"getDeltaTime",lua_ax_base_Director_getDeltaTime); - tolua_function(tolua_S,"getFrameRate",lua_ax_base_Director_getFrameRate); - tolua_function(tolua_S,"pushMatrix",lua_ax_base_Director_pushMatrix); - tolua_function(tolua_S,"popMatrix",lua_ax_base_Director_popMatrix); - tolua_function(tolua_S,"loadIdentityMatrix",lua_ax_base_Director_loadIdentityMatrix); - tolua_function(tolua_S,"loadMatrix",lua_ax_base_Director_loadMatrix); - tolua_function(tolua_S,"multiplyMatrix",lua_ax_base_Director_multiplyMatrix); - tolua_function(tolua_S,"getMatrix",lua_ax_base_Director_getMatrix); - tolua_function(tolua_S,"resetMatrixStack",lua_ax_base_Director_resetMatrixStack); - tolua_function(tolua_S,"getAxmolThreadId",lua_ax_base_Director_getAxmolThreadId); - tolua_function(tolua_S,"setChildrenIndexerEnabled",lua_ax_base_Director_setChildrenIndexerEnabled); - tolua_function(tolua_S,"isChildrenIndexerEnabled",lua_ax_base_Director_isChildrenIndexerEnabled); - tolua_function(tolua_S,"isValid",lua_ax_base_Director_isValid); - tolua_function(tolua_S,"getInstance", lua_ax_base_Director_getInstance); - tolua_function(tolua_S,"destroyInstance", lua_ax_base_Director_destroyInstance); + tolua_beginmodule(tolua_S,"Sequence"); + tolua_function(tolua_S,"new",lua_ax_base_Sequence_constructor); + tolua_function(tolua_S,"initWithTwoActions",lua_ax_base_Sequence_initWithTwoActions); + tolua_function(tolua_S,"init",lua_ax_base_Sequence_init); tolua_endmodule(tolua_S); - auto typeName = typeid(ax::Director).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.Director"; - g_typeCast[typeName] = "ax.Director"; + auto typeName = typeid(ax::Sequence).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.Sequence"; + g_typeCast[typeName] = "ax.Sequence"; return 1; } -int lua_ax_base_Timer_setupTimerWithInterval(lua_State* tolua_S) +int lua_ax_base_Repeat_setInnerAction(lua_State* tolua_S) { int argc = 0; - ax::Timer* cobj = nullptr; + ax::Repeat* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -17903,54 +18435,48 @@ int lua_ax_base_Timer_setupTimerWithInterval(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Timer",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Repeat",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Timer*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Repeat*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Timer_setupTimerWithInterval'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Repeat_setInnerAction'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 3) + if (argc == 1) { - double arg0; - unsigned int arg1; - double arg2; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Timer:setupTimerWithInterval"); - - ok &= luaval_to_uint32(tolua_S, 3,&arg1, "ax.Timer:setupTimerWithInterval"); + ax::FiniteTimeAction* arg0; - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.Timer:setupTimerWithInterval"); + ok &= luaval_to_object(tolua_S, 2, "ax.FiniteTimeAction",&arg0, "ax.Repeat:setInnerAction"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Timer_setupTimerWithInterval'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Repeat_setInnerAction'", nullptr); return 0; } - cobj->setupTimerWithInterval(arg0, arg1, arg2); + cobj->setInnerAction(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Timer:setupTimerWithInterval",argc, 3); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Repeat:setInnerAction",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Timer_setupTimerWithInterval'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Repeat_setInnerAction'.",&tolua_err); #endif return 0; } -int lua_ax_base_Timer_setAborted(lua_State* tolua_S) +int lua_ax_base_Repeat_getInnerAction(lua_State* tolua_S) { int argc = 0; - ax::Timer* cobj = nullptr; + ax::Repeat* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -17959,15 +18485,15 @@ int lua_ax_base_Timer_setAborted(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Timer",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Repeat",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Timer*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Repeat*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Timer_setAborted'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Repeat_getInnerAction'", nullptr); return 0; } #endif @@ -17977,27 +18503,27 @@ int lua_ax_base_Timer_setAborted(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Timer_setAborted'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Repeat_getInnerAction'", nullptr); return 0; } - cobj->setAborted(); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getInnerAction(); + object_to_luaval(tolua_S, "ax.FiniteTimeAction",(ax::FiniteTimeAction*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Timer:setAborted",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Repeat:getInnerAction",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Timer_setAborted'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Repeat_getInnerAction'.",&tolua_err); #endif return 0; } -int lua_ax_base_Timer_isAborted(lua_State* tolua_S) +int lua_ax_base_Repeat_initWithAction(lua_State* tolua_S) { int argc = 0; - ax::Timer* cobj = nullptr; + ax::Repeat* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -18006,92 +18532,150 @@ int lua_ax_base_Timer_isAborted(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Timer",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Repeat",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Timer*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Repeat*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Timer_isAborted'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Repeat_initWithAction'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 2) { + ax::FiniteTimeAction* arg0; + unsigned int arg1; + + ok &= luaval_to_object(tolua_S, 2, "ax.FiniteTimeAction",&arg0, "ax.Repeat:initWithAction"); + + ok &= luaval_to_uint32(tolua_S, 3,&arg1, "ax.Repeat:initWithAction"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Timer_isAborted'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Repeat_initWithAction'", nullptr); return 0; } - auto&& ret = cobj->isAborted(); + auto&& ret = cobj->initWithAction(arg0, arg1); tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Timer:isAborted",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Repeat:initWithAction",argc, 2); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Timer_isAborted'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Repeat_initWithAction'.",&tolua_err); #endif return 0; } -int lua_ax_base_Timer_isExhausted(lua_State* tolua_S) +int lua_ax_base_Repeat_create(lua_State* tolua_S) { int argc = 0; - ax::Timer* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Timer",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.Repeat",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Timer*)tolua_tousertype(tolua_S,1,0); + argc = lua_gettop(tolua_S) - 1; -#if _AX_DEBUG >= 1 - if (!cobj) + if (argc == 2) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Timer_isExhausted'", nullptr); - return 0; + ax::FiniteTimeAction* arg0; + unsigned int arg1; + ok &= luaval_to_object(tolua_S, 2, "ax.FiniteTimeAction",&arg0, "ax.Repeat:create"); + ok &= luaval_to_uint32(tolua_S, 3,&arg1, "ax.Repeat:create"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Repeat_create'", nullptr); + return 0; + } + auto&& ret = ax::Repeat::create(arg0, arg1); + object_to_luaval(tolua_S, "ax.Repeat",(ax::Repeat*)ret); + return 1; } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Repeat:create",argc, 2); + return 0; +#if _AX_DEBUG >= 1 + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Repeat_create'.",&tolua_err); +#endif + return 0; +} +int lua_ax_base_Repeat_constructor(lua_State* tolua_S) +{ + int argc = 0; + ax::Repeat* cobj = nullptr; + bool ok = true; + +#if _AX_DEBUG >= 1 + tolua_Error tolua_err; #endif + + argc = lua_gettop(tolua_S)-1; if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Timer_isExhausted'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Repeat_constructor'", nullptr); return 0; } - auto&& ret = cobj->isExhausted(); - tolua_pushboolean(tolua_S,(bool)ret); + cobj = new ax::Repeat(); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.Repeat"); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Timer:isExhausted",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Repeat:Repeat",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Timer_isExhausted'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Repeat_constructor'.",&tolua_err); #endif return 0; } -int lua_ax_base_Timer_trigger(lua_State* tolua_S) + +static int lua_ax_base_Repeat_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (Repeat)"); + return 0; +} + +int lua_register_ax_base_Repeat(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.Repeat"); + tolua_cclass(tolua_S,"Repeat","ax.Repeat","ax.ActionInterval",nullptr); + + tolua_beginmodule(tolua_S,"Repeat"); + tolua_function(tolua_S,"new",lua_ax_base_Repeat_constructor); + tolua_function(tolua_S,"setInnerAction",lua_ax_base_Repeat_setInnerAction); + tolua_function(tolua_S,"getInnerAction",lua_ax_base_Repeat_getInnerAction); + tolua_function(tolua_S,"initWithAction",lua_ax_base_Repeat_initWithAction); + tolua_function(tolua_S,"create", lua_ax_base_Repeat_create); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::Repeat).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.Repeat"; + g_typeCast[typeName] = "ax.Repeat"; + return 1; +} + +int lua_ax_base_RepeatForever_setInnerAction(lua_State* tolua_S) { int argc = 0; - ax::Timer* cobj = nullptr; + ax::RepeatForever* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -18100,15 +18684,15 @@ int lua_ax_base_Timer_trigger(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Timer",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.RepeatForever",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Timer*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::RepeatForever*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Timer_trigger'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_RepeatForever_setInnerAction'", nullptr); return 0; } #endif @@ -18116,32 +18700,32 @@ int lua_ax_base_Timer_trigger(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - double arg0; + ax::ActionInterval* arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Timer:trigger"); + ok &= luaval_to_object(tolua_S, 2, "ax.ActionInterval",&arg0, "ax.RepeatForever:setInnerAction"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Timer_trigger'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_RepeatForever_setInnerAction'", nullptr); return 0; } - cobj->trigger(arg0); + cobj->setInnerAction(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Timer:trigger",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.RepeatForever:setInnerAction",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Timer_trigger'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_RepeatForever_setInnerAction'.",&tolua_err); #endif return 0; } -int lua_ax_base_Timer_cancel(lua_State* tolua_S) +int lua_ax_base_RepeatForever_getInnerAction(lua_State* tolua_S) { int argc = 0; - ax::Timer* cobj = nullptr; + ax::RepeatForever* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -18150,15 +18734,15 @@ int lua_ax_base_Timer_cancel(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Timer",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.RepeatForever",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Timer*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::RepeatForever*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Timer_cancel'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_RepeatForever_getInnerAction'", nullptr); return 0; } #endif @@ -18168,27 +18752,27 @@ int lua_ax_base_Timer_cancel(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Timer_cancel'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_RepeatForever_getInnerAction'", nullptr); return 0; } - cobj->cancel(); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getInnerAction(); + object_to_luaval(tolua_S, "ax.ActionInterval",(ax::ActionInterval*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Timer:cancel",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.RepeatForever:getInnerAction",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Timer_cancel'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_RepeatForever_getInnerAction'.",&tolua_err); #endif return 0; } -int lua_ax_base_Timer_update(lua_State* tolua_S) +int lua_ax_base_RepeatForever_initWithAction(lua_State* tolua_S) { int argc = 0; - ax::Timer* cobj = nullptr; + ax::RepeatForever* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -18197,15 +18781,15 @@ int lua_ax_base_Timer_update(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Timer",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.RepeatForever",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Timer*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::RepeatForever*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Timer_update'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_RepeatForever_initWithAction'", nullptr); return 0; } #endif @@ -18213,105 +18797,68 @@ int lua_ax_base_Timer_update(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - double arg0; + ax::ActionInterval* arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Timer:update"); + ok &= luaval_to_object(tolua_S, 2, "ax.ActionInterval",&arg0, "ax.RepeatForever:initWithAction"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Timer_update'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_RepeatForever_initWithAction'", nullptr); return 0; } - cobj->update(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->initWithAction(arg0); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Timer:update",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.RepeatForever:initWithAction",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Timer_update'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_RepeatForever_initWithAction'.",&tolua_err); #endif return 0; } -static int lua_ax_base_Timer_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (Timer)"); - return 0; -} - -int lua_register_ax_base_Timer(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.Timer"); - tolua_cclass(tolua_S,"Timer","ax.Timer","ax.Object",nullptr); - - tolua_beginmodule(tolua_S,"Timer"); - tolua_function(tolua_S,"setupTimerWithInterval",lua_ax_base_Timer_setupTimerWithInterval); - tolua_function(tolua_S,"setAborted",lua_ax_base_Timer_setAborted); - tolua_function(tolua_S,"isAborted",lua_ax_base_Timer_isAborted); - tolua_function(tolua_S,"isExhausted",lua_ax_base_Timer_isExhausted); - tolua_function(tolua_S,"trigger",lua_ax_base_Timer_trigger); - tolua_function(tolua_S,"cancel",lua_ax_base_Timer_cancel); - tolua_function(tolua_S,"update",lua_ax_base_Timer_update); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::Timer).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.Timer"; - g_typeCast[typeName] = "ax.Timer"; - return 1; -} - -int lua_ax_base_Scheduler_getTimeScale(lua_State* tolua_S) +int lua_ax_base_RepeatForever_create(lua_State* tolua_S) { int argc = 0; - ax::Scheduler* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Scheduler",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.RepeatForever",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Scheduler*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Scheduler_getTimeScale'", nullptr); - return 0; - } -#endif + argc = lua_gettop(tolua_S) - 1; - argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + ax::ActionInterval* arg0; + ok &= luaval_to_object(tolua_S, 2, "ax.ActionInterval",&arg0, "ax.RepeatForever:create"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scheduler_getTimeScale'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_RepeatForever_create'", nullptr); return 0; } - auto&& ret = cobj->getTimeScale(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + auto&& ret = ax::RepeatForever::create(arg0); + object_to_luaval(tolua_S, "ax.RepeatForever",(ax::RepeatForever*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Scheduler:getTimeScale",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.RepeatForever:create",argc, 1); return 0; - #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Scheduler_getTimeScale'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_RepeatForever_create'.",&tolua_err); #endif - return 0; } -int lua_ax_base_Scheduler_setTimeScale(lua_State* tolua_S) +int lua_ax_base_RepeatForever_constructor(lua_State* tolua_S) { int argc = 0; - ax::Scheduler* cobj = nullptr; + ax::RepeatForever* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -18319,49 +18866,60 @@ int lua_ax_base_Scheduler_setTimeScale(lua_State* tolua_S) #endif -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Scheduler",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::Scheduler*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Scheduler_setTimeScale'", nullptr); - return 0; - } -#endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Scheduler:setTimeScale"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scheduler_setTimeScale'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_RepeatForever_constructor'", nullptr); return 0; } - cobj->setTimeScale(arg0); - lua_settop(tolua_S, 1); + cobj = new ax::RepeatForever(); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.RepeatForever"); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Scheduler:setTimeScale",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.RepeatForever:RepeatForever",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Scheduler_setTimeScale'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_RepeatForever_constructor'.",&tolua_err); #endif return 0; } -int lua_ax_base_Scheduler_runOnAxmolThread(lua_State* tolua_S) + +static int lua_ax_base_RepeatForever_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (RepeatForever)"); + return 0; +} + +int lua_register_ax_base_RepeatForever(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.RepeatForever"); + tolua_cclass(tolua_S,"RepeatForever","ax.RepeatForever","ax.ActionInterval",nullptr); + + tolua_beginmodule(tolua_S,"RepeatForever"); + tolua_function(tolua_S,"new",lua_ax_base_RepeatForever_constructor); + tolua_function(tolua_S,"setInnerAction",lua_ax_base_RepeatForever_setInnerAction); + tolua_function(tolua_S,"getInnerAction",lua_ax_base_RepeatForever_getInnerAction); + tolua_function(tolua_S,"initWithAction",lua_ax_base_RepeatForever_initWithAction); + tolua_function(tolua_S,"create", lua_ax_base_RepeatForever_create); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::RepeatForever).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.RepeatForever"; + g_typeCast[typeName] = "ax.RepeatForever"; + return 1; +} + +int lua_ax_base_Spawn_initWithTwoActions(lua_State* tolua_S) { int argc = 0; - ax::Scheduler* cobj = nullptr; + ax::Spawn* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -18370,52 +18928,51 @@ int lua_ax_base_Scheduler_runOnAxmolThread(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Scheduler",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Spawn",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Scheduler*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Spawn*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Scheduler_runOnAxmolThread'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Spawn_initWithTwoActions'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 2) { - std::function arg0; + ax::FiniteTimeAction* arg0; + ax::FiniteTimeAction* arg1; - do { - // Lambda binding for lua is not supported. - assert(false); - } while(0) - ; + ok &= luaval_to_object(tolua_S, 2, "ax.FiniteTimeAction",&arg0, "ax.Spawn:initWithTwoActions"); + + ok &= luaval_to_object(tolua_S, 3, "ax.FiniteTimeAction",&arg1, "ax.Spawn:initWithTwoActions"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scheduler_runOnAxmolThread'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Spawn_initWithTwoActions'", nullptr); return 0; } - cobj->runOnAxmolThread(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->initWithTwoActions(arg0, arg1); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Scheduler:runOnAxmolThread",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Spawn:initWithTwoActions",argc, 2); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Scheduler_runOnAxmolThread'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Spawn_initWithTwoActions'.",&tolua_err); #endif return 0; } -int lua_ax_base_Scheduler_removeAllPendingActions(lua_State* tolua_S) +int lua_ax_base_Spawn_init(lua_State* tolua_S) { int argc = 0; - ax::Scheduler* cobj = nullptr; + ax::Spawn* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -18424,45 +18981,48 @@ int lua_ax_base_Scheduler_removeAllPendingActions(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Scheduler",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Spawn",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Scheduler*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Spawn*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Scheduler_removeAllPendingActions'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Spawn_init'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + ax::Vector arg0; + + ok &= luaval_to_ccvector(tolua_S, 2, &arg0, "ax.Spawn:init"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scheduler_removeAllPendingActions'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Spawn_init'", nullptr); return 0; } - cobj->removeAllPendingActions(); - lua_settop(tolua_S, 1); + auto&& ret = cobj->init(arg0); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Scheduler:removeAllPendingActions",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Spawn:init",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Scheduler_removeAllPendingActions'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Spawn_init'.",&tolua_err); #endif return 0; } -int lua_ax_base_Scheduler_constructor(lua_State* tolua_S) +int lua_ax_base_Spawn_constructor(lua_State* tolua_S) { int argc = 0; - ax::Scheduler* cobj = nullptr; + ax::Spawn* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -18476,148 +19036,192 @@ int lua_ax_base_Scheduler_constructor(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scheduler_constructor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Spawn_constructor'", nullptr); return 0; } - cobj = new ax::Scheduler(); + cobj = new ax::Spawn(); cobj->autorelease(); int ID = (int)cobj->_ID ; int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.Scheduler"); + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.Spawn"); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Scheduler:Scheduler",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Spawn:Spawn",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Scheduler_constructor'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Spawn_constructor'.",&tolua_err); #endif return 0; } -static int lua_ax_base_Scheduler_finalize(lua_State* tolua_S) +static int lua_ax_base_Spawn_finalize(lua_State* tolua_S) { - AXLOGV("luabindings: finalizing LUA object (Scheduler)"); + AXLOGV("luabindings: finalizing LUA object (Spawn)"); return 0; } -int lua_register_ax_base_Scheduler(lua_State* tolua_S) +int lua_register_ax_base_Spawn(lua_State* tolua_S) { - tolua_usertype(tolua_S,"ax.Scheduler"); - tolua_cclass(tolua_S,"Scheduler","ax.Scheduler","ax.Object",nullptr); + tolua_usertype(tolua_S,"ax.Spawn"); + tolua_cclass(tolua_S,"Spawn","ax.Spawn","ax.ActionInterval",nullptr); - tolua_beginmodule(tolua_S,"Scheduler"); - tolua_function(tolua_S,"new",lua_ax_base_Scheduler_constructor); - tolua_function(tolua_S,"getTimeScale",lua_ax_base_Scheduler_getTimeScale); - tolua_function(tolua_S,"setTimeScale",lua_ax_base_Scheduler_setTimeScale); - tolua_function(tolua_S,"runOnAxmolThread",lua_ax_base_Scheduler_runOnAxmolThread); - tolua_function(tolua_S,"removeAllPendingActions",lua_ax_base_Scheduler_removeAllPendingActions); + tolua_beginmodule(tolua_S,"Spawn"); + tolua_function(tolua_S,"new",lua_ax_base_Spawn_constructor); + tolua_function(tolua_S,"initWithTwoActions",lua_ax_base_Spawn_initWithTwoActions); + tolua_function(tolua_S,"init",lua_ax_base_Spawn_init); tolua_endmodule(tolua_S); - auto typeName = typeid(ax::Scheduler).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.Scheduler"; - g_typeCast[typeName] = "ax.Scheduler"; + auto typeName = typeid(ax::Spawn).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.Spawn"; + g_typeCast[typeName] = "ax.Spawn"; return 1; } -int lua_ax_base_Action_clone(lua_State* tolua_S) +int lua_ax_base_RotateTo_initWithDuration(lua_State* tolua_S) { int argc = 0; - ax::Action* cobj = nullptr; + ax::RotateTo* cobj = nullptr; bool ok = true; - #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Action",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.RotateTo",0,&tolua_err)) goto tolua_lerror; #endif - - cobj = (ax::Action*)tolua_tousertype(tolua_S,1,0); - + cobj = (ax::RotateTo*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Action_clone'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_RotateTo_initWithDuration'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Action_clone'", nullptr); - return 0; + do{ + if (argc == 2) { + double arg0; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.RotateTo:initWithDuration"); + + if (!ok) { break; } + ax::Vec3 arg1; + ok &= luaval_to_vec3(tolua_S, 3, &arg1, "ax.RotateTo:initWithDuration"); + + if (!ok) { break; } + bool ret = cobj->initWithDuration(arg0, arg1); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; } - auto&& ret = cobj->clone(); - object_to_luaval(tolua_S, "ax.Action",(ax::Action*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Action:clone",argc, 0); + }while(0); + ok = true; + do{ + if (argc == 3) { + double arg0; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.RotateTo:initWithDuration"); + + if (!ok) { break; } + double arg1; + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.RotateTo:initWithDuration"); + + if (!ok) { break; } + double arg2; + ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.RotateTo:initWithDuration"); + + if (!ok) { break; } + bool ret = cobj->initWithDuration(arg0, arg1, arg2); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; + } + }while(0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.RotateTo:initWithDuration",argc, 3); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Action_clone'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_RotateTo_initWithDuration'.",&tolua_err); #endif return 0; } -int lua_ax_base_Action_reverse(lua_State* tolua_S) +int lua_ax_base_RotateTo_create(lua_State* tolua_S) { int argc = 0; - ax::Action* cobj = nullptr; bool ok = true; - #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Action",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.RotateTo",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Action*)tolua_tousertype(tolua_S,1,0); + argc = lua_gettop(tolua_S)-1; -#if _AX_DEBUG >= 1 - if (!cobj) + do { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Action_reverse'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 2) + { + double arg0; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.RotateTo:create"); + if (!ok) { break; } + double arg1; + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.RotateTo:create"); + if (!ok) { break; } + ax::RotateTo* ret = ax::RotateTo::create(arg0, arg1); + object_to_luaval(tolua_S, "ax.RotateTo",(ax::RotateTo*)ret); + return 1; + } + } while (0); + ok = true; + do { - if(!ok) + if (argc == 3) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Action_reverse'", nullptr); - return 0; + double arg0; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.RotateTo:create"); + if (!ok) { break; } + double arg1; + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.RotateTo:create"); + if (!ok) { break; } + double arg2; + ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.RotateTo:create"); + if (!ok) { break; } + ax::RotateTo* ret = ax::RotateTo::create(arg0, arg1, arg2); + object_to_luaval(tolua_S, "ax.RotateTo",(ax::RotateTo*)ret); + return 1; } - auto&& ret = cobj->reverse(); - object_to_luaval(tolua_S, "ax.Action",(ax::Action*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Action:reverse",argc, 0); + } while (0); + ok = true; + do + { + if (argc == 2) + { + double arg0; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.RotateTo:create"); + if (!ok) { break; } + ax::Vec3 arg1; + ok &= luaval_to_vec3(tolua_S, 3, &arg1, "ax.RotateTo:create"); + if (!ok) { break; } + ax::RotateTo* ret = ax::RotateTo::create(arg0, arg1); + object_to_luaval(tolua_S, "ax.RotateTo",(ax::RotateTo*)ret); + return 1; + } + } while (0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d", "ax.RotateTo:create",argc, 2); return 0; - #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Action_reverse'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_RotateTo_create'.",&tolua_err); #endif - return 0; } -int lua_ax_base_Action_isDone(lua_State* tolua_S) +int lua_ax_base_RotateTo_constructor(lua_State* tolua_S) { int argc = 0; - ax::Action* cobj = nullptr; + ax::RotateTo* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -18625,143 +19229,214 @@ int lua_ax_base_Action_isDone(lua_State* tolua_S) #endif -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Action",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::Action*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Action_isDone'", nullptr); - return 0; - } -#endif argc = lua_gettop(tolua_S)-1; if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Action_isDone'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_RotateTo_constructor'", nullptr); return 0; } - auto&& ret = cobj->isDone(); - tolua_pushboolean(tolua_S,(bool)ret); + cobj = new ax::RotateTo(); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.RotateTo"); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Action:isDone",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.RotateTo:RotateTo",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Action_isDone'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_RotateTo_constructor'.",&tolua_err); #endif return 0; } -int lua_ax_base_Action_startWithTarget(lua_State* tolua_S) + +static int lua_ax_base_RotateTo_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (RotateTo)"); + return 0; +} + +int lua_register_ax_base_RotateTo(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.RotateTo"); + tolua_cclass(tolua_S,"RotateTo","ax.RotateTo","ax.ActionInterval",nullptr); + + tolua_beginmodule(tolua_S,"RotateTo"); + tolua_function(tolua_S,"new",lua_ax_base_RotateTo_constructor); + tolua_function(tolua_S,"initWithDuration",lua_ax_base_RotateTo_initWithDuration); + tolua_function(tolua_S,"create", lua_ax_base_RotateTo_create); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::RotateTo).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.RotateTo"; + g_typeCast[typeName] = "ax.RotateTo"; + return 1; +} + +int lua_ax_base_RotateBy_initWithDuration(lua_State* tolua_S) { int argc = 0; - ax::Action* cobj = nullptr; + ax::RotateBy* cobj = nullptr; bool ok = true; - #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Action",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.RotateBy",0,&tolua_err)) goto tolua_lerror; #endif - - cobj = (ax::Action*)tolua_tousertype(tolua_S,1,0); - + cobj = (ax::RotateBy*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Action_startWithTarget'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_RotateBy_initWithDuration'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - ax::Node* arg0; + do{ + if (argc == 3) { + double arg0; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.RotateBy:initWithDuration"); - ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Action:startWithTarget"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Action_startWithTarget'", nullptr); - return 0; + if (!ok) { break; } + double arg1; + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.RotateBy:initWithDuration"); + + if (!ok) { break; } + double arg2; + ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.RotateBy:initWithDuration"); + + if (!ok) { break; } + bool ret = cobj->initWithDuration(arg0, arg1, arg2); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; } - cobj->startWithTarget(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Action:startWithTarget",argc, 1); + }while(0); + ok = true; + do{ + if (argc == 2) { + double arg0; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.RotateBy:initWithDuration"); + + if (!ok) { break; } + double arg1; + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.RotateBy:initWithDuration"); + + if (!ok) { break; } + bool ret = cobj->initWithDuration(arg0, arg1); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; + } + }while(0); + ok = true; + do{ + if (argc == 2) { + double arg0; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.RotateBy:initWithDuration"); + + if (!ok) { break; } + ax::Vec3 arg1; + ok &= luaval_to_vec3(tolua_S, 3, &arg1, "ax.RotateBy:initWithDuration"); + + if (!ok) { break; } + bool ret = cobj->initWithDuration(arg0, arg1); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; + } + }while(0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.RotateBy:initWithDuration",argc, 2); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Action_startWithTarget'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_RotateBy_initWithDuration'.",&tolua_err); #endif return 0; } -int lua_ax_base_Action_stop(lua_State* tolua_S) +int lua_ax_base_RotateBy_create(lua_State* tolua_S) { int argc = 0; - ax::Action* cobj = nullptr; bool ok = true; - #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Action",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.RotateBy",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Action*)tolua_tousertype(tolua_S,1,0); + argc = lua_gettop(tolua_S)-1; -#if _AX_DEBUG >= 1 - if (!cobj) + do { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Action_stop'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 3) + { + double arg0; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.RotateBy:create"); + if (!ok) { break; } + double arg1; + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.RotateBy:create"); + if (!ok) { break; } + double arg2; + ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.RotateBy:create"); + if (!ok) { break; } + ax::RotateBy* ret = ax::RotateBy::create(arg0, arg1, arg2); + object_to_luaval(tolua_S, "ax.RotateBy",(ax::RotateBy*)ret); + return 1; + } + } while (0); + ok = true; + do { - if(!ok) + if (argc == 2) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Action_stop'", nullptr); - return 0; + double arg0; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.RotateBy:create"); + if (!ok) { break; } + double arg1; + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.RotateBy:create"); + if (!ok) { break; } + ax::RotateBy* ret = ax::RotateBy::create(arg0, arg1); + object_to_luaval(tolua_S, "ax.RotateBy",(ax::RotateBy*)ret); + return 1; } - cobj->stop(); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Action:stop",argc, 0); + } while (0); + ok = true; + do + { + if (argc == 2) + { + double arg0; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.RotateBy:create"); + if (!ok) { break; } + ax::Vec3 arg1; + ok &= luaval_to_vec3(tolua_S, 3, &arg1, "ax.RotateBy:create"); + if (!ok) { break; } + ax::RotateBy* ret = ax::RotateBy::create(arg0, arg1); + object_to_luaval(tolua_S, "ax.RotateBy",(ax::RotateBy*)ret); + return 1; + } + } while (0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d", "ax.RotateBy:create",argc, 2); return 0; - #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Action_stop'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_RotateBy_create'.",&tolua_err); #endif - return 0; } -int lua_ax_base_Action_step(lua_State* tolua_S) +int lua_ax_base_RotateBy_constructor(lua_State* tolua_S) { int argc = 0; - ax::Action* cobj = nullptr; + ax::RotateBy* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -18769,146 +19444,175 @@ int lua_ax_base_Action_step(lua_State* tolua_S) #endif -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Action",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::Action*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Action_step'", nullptr); - return 0; - } -#endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Action:step"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Action_step'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_RotateBy_constructor'", nullptr); return 0; } - cobj->step(arg0); - lua_settop(tolua_S, 1); + cobj = new ax::RotateBy(); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.RotateBy"); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Action:step",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.RotateBy:RotateBy",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Action_step'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_RotateBy_constructor'.",&tolua_err); #endif return 0; } -int lua_ax_base_Action_update(lua_State* tolua_S) + +static int lua_ax_base_RotateBy_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (RotateBy)"); + return 0; +} + +int lua_register_ax_base_RotateBy(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.RotateBy"); + tolua_cclass(tolua_S,"RotateBy","ax.RotateBy","ax.ActionInterval",nullptr); + + tolua_beginmodule(tolua_S,"RotateBy"); + tolua_function(tolua_S,"new",lua_ax_base_RotateBy_constructor); + tolua_function(tolua_S,"initWithDuration",lua_ax_base_RotateBy_initWithDuration); + tolua_function(tolua_S,"create", lua_ax_base_RotateBy_create); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::RotateBy).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.RotateBy"; + g_typeCast[typeName] = "ax.RotateBy"; + return 1; +} + +int lua_ax_base_MoveBy_initWithDuration(lua_State* tolua_S) { int argc = 0; - ax::Action* cobj = nullptr; + ax::MoveBy* cobj = nullptr; bool ok = true; - #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Action",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.MoveBy",0,&tolua_err)) goto tolua_lerror; #endif - - cobj = (ax::Action*)tolua_tousertype(tolua_S,1,0); - + cobj = (ax::MoveBy*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Action_update'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_MoveBy_initWithDuration'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; + do{ + if (argc == 2) { + double arg0; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.MoveBy:initWithDuration"); - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Action:update"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Action_update'", nullptr); - return 0; + if (!ok) { break; } + ax::Vec3 arg1; + ok &= luaval_to_vec3(tolua_S, 3, &arg1, "ax.MoveBy:initWithDuration"); + + if (!ok) { break; } + bool ret = cobj->initWithDuration(arg0, arg1); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; } - cobj->update(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Action:update",argc, 1); + }while(0); + ok = true; + do{ + if (argc == 2) { + double arg0; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.MoveBy:initWithDuration"); + + if (!ok) { break; } + ax::Vec2 arg1; + ok &= luaval_to_vec2(tolua_S, 3, &arg1, "ax.MoveBy:initWithDuration"); + + if (!ok) { break; } + bool ret = cobj->initWithDuration(arg0, arg1); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; + } + }while(0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.MoveBy:initWithDuration",argc, 2); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Action_update'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_MoveBy_initWithDuration'.",&tolua_err); #endif return 0; } -int lua_ax_base_Action_getTarget(lua_State* tolua_S) +int lua_ax_base_MoveBy_create(lua_State* tolua_S) { int argc = 0; - ax::Action* cobj = nullptr; bool ok = true; - #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Action",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::Action*)tolua_tousertype(tolua_S,1,0); - #if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Action_getTarget'", nullptr); - return 0; - } + if (!tolua_isusertable(tolua_S,1,"ax.MoveBy",0,&tolua_err)) goto tolua_lerror; #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + + do { - if(!ok) + if (argc == 2) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Action_getTarget'", nullptr); - return 0; + double arg0; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.MoveBy:create"); + if (!ok) { break; } + ax::Vec3 arg1; + ok &= luaval_to_vec3(tolua_S, 3, &arg1, "ax.MoveBy:create"); + if (!ok) { break; } + ax::MoveBy* ret = ax::MoveBy::create(arg0, arg1); + object_to_luaval(tolua_S, "ax.MoveBy",(ax::MoveBy*)ret); + return 1; } - auto&& ret = cobj->getTarget(); - object_to_luaval(tolua_S, "ax.Node",(ax::Node*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Action:getTarget",argc, 0); - return 0; - + } while (0); + ok = true; + do + { + if (argc == 2) + { + double arg0; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.MoveBy:create"); + if (!ok) { break; } + ax::Vec2 arg1; + ok &= luaval_to_vec2(tolua_S, 3, &arg1, "ax.MoveBy:create"); + if (!ok) { break; } + ax::MoveBy* ret = ax::MoveBy::create(arg0, arg1); + object_to_luaval(tolua_S, "ax.MoveBy",(ax::MoveBy*)ret); + return 1; + } + } while (0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d", "ax.MoveBy:create",argc, 2); + return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Action_getTarget'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_MoveBy_create'.",&tolua_err); #endif - return 0; } -int lua_ax_base_Action_setTarget(lua_State* tolua_S) +int lua_ax_base_MoveBy_constructor(lua_State* tolua_S) { int argc = 0; - ax::Action* cobj = nullptr; + ax::MoveBy* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -18916,146 +19620,175 @@ int lua_ax_base_Action_setTarget(lua_State* tolua_S) #endif -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Action",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::Action*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Action_setTarget'", nullptr); - return 0; - } -#endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - ax::Node* arg0; - - ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Action:setTarget"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Action_setTarget'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_MoveBy_constructor'", nullptr); return 0; } - cobj->setTarget(arg0); - lua_settop(tolua_S, 1); + cobj = new ax::MoveBy(); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.MoveBy"); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Action:setTarget",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.MoveBy:MoveBy",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Action_setTarget'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_MoveBy_constructor'.",&tolua_err); #endif return 0; } -int lua_ax_base_Action_getOriginalTarget(lua_State* tolua_S) + +static int lua_ax_base_MoveBy_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (MoveBy)"); + return 0; +} + +int lua_register_ax_base_MoveBy(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.MoveBy"); + tolua_cclass(tolua_S,"MoveBy","ax.MoveBy","ax.ActionInterval",nullptr); + + tolua_beginmodule(tolua_S,"MoveBy"); + tolua_function(tolua_S,"new",lua_ax_base_MoveBy_constructor); + tolua_function(tolua_S,"initWithDuration",lua_ax_base_MoveBy_initWithDuration); + tolua_function(tolua_S,"create", lua_ax_base_MoveBy_create); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::MoveBy).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.MoveBy"; + g_typeCast[typeName] = "ax.MoveBy"; + return 1; +} + +int lua_ax_base_MoveTo_initWithDuration(lua_State* tolua_S) { int argc = 0; - ax::Action* cobj = nullptr; + ax::MoveTo* cobj = nullptr; bool ok = true; - #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Action",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.MoveTo",0,&tolua_err)) goto tolua_lerror; #endif - - cobj = (ax::Action*)tolua_tousertype(tolua_S,1,0); - + cobj = (ax::MoveTo*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Action_getOriginalTarget'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_MoveTo_initWithDuration'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Action_getOriginalTarget'", nullptr); - return 0; + do{ + if (argc == 2) { + double arg0; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.MoveTo:initWithDuration"); + + if (!ok) { break; } + ax::Vec3 arg1; + ok &= luaval_to_vec3(tolua_S, 3, &arg1, "ax.MoveTo:initWithDuration"); + + if (!ok) { break; } + bool ret = cobj->initWithDuration(arg0, arg1); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; } - auto&& ret = cobj->getOriginalTarget(); - object_to_luaval(tolua_S, "ax.Node",(ax::Node*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Action:getOriginalTarget",argc, 0); + }while(0); + ok = true; + do{ + if (argc == 2) { + double arg0; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.MoveTo:initWithDuration"); + + if (!ok) { break; } + ax::Vec2 arg1; + ok &= luaval_to_vec2(tolua_S, 3, &arg1, "ax.MoveTo:initWithDuration"); + + if (!ok) { break; } + bool ret = cobj->initWithDuration(arg0, arg1); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; + } + }while(0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.MoveTo:initWithDuration",argc, 2); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Action_getOriginalTarget'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_MoveTo_initWithDuration'.",&tolua_err); #endif return 0; } -int lua_ax_base_Action_setOriginalTarget(lua_State* tolua_S) +int lua_ax_base_MoveTo_create(lua_State* tolua_S) { int argc = 0; - ax::Action* cobj = nullptr; bool ok = true; - #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Action",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.MoveTo",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Action*)tolua_tousertype(tolua_S,1,0); + argc = lua_gettop(tolua_S)-1; -#if _AX_DEBUG >= 1 - if (!cobj) + do { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Action_setOriginalTarget'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 2) + { + double arg0; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.MoveTo:create"); + if (!ok) { break; } + ax::Vec3 arg1; + ok &= luaval_to_vec3(tolua_S, 3, &arg1, "ax.MoveTo:create"); + if (!ok) { break; } + ax::MoveTo* ret = ax::MoveTo::create(arg0, arg1); + object_to_luaval(tolua_S, "ax.MoveTo",(ax::MoveTo*)ret); + return 1; + } + } while (0); + ok = true; + do { - ax::Node* arg0; - - ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Action:setOriginalTarget"); - if(!ok) + if (argc == 2) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Action_setOriginalTarget'", nullptr); - return 0; + double arg0; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.MoveTo:create"); + if (!ok) { break; } + ax::Vec2 arg1; + ok &= luaval_to_vec2(tolua_S, 3, &arg1, "ax.MoveTo:create"); + if (!ok) { break; } + ax::MoveTo* ret = ax::MoveTo::create(arg0, arg1); + object_to_luaval(tolua_S, "ax.MoveTo",(ax::MoveTo*)ret); + return 1; } - cobj->setOriginalTarget(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Action:setOriginalTarget",argc, 1); + } while (0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d", "ax.MoveTo:create",argc, 2); return 0; - #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Action_setOriginalTarget'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_MoveTo_create'.",&tolua_err); #endif - return 0; } -int lua_ax_base_Action_getTag(lua_State* tolua_S) +int lua_ax_base_MoveTo_constructor(lua_State* tolua_S) { int argc = 0; - ax::Action* cobj = nullptr; + ax::MoveTo* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -19063,46 +19796,58 @@ int lua_ax_base_Action_getTag(lua_State* tolua_S) #endif -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Action",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::Action*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Action_getTag'", nullptr); - return 0; - } -#endif argc = lua_gettop(tolua_S)-1; if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Action_getTag'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_MoveTo_constructor'", nullptr); return 0; } - auto&& ret = cobj->getTag(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + cobj = new ax::MoveTo(); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.MoveTo"); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Action:getTag",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.MoveTo:MoveTo",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Action_getTag'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_MoveTo_constructor'.",&tolua_err); #endif return 0; } -int lua_ax_base_Action_setTag(lua_State* tolua_S) + +static int lua_ax_base_MoveTo_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (MoveTo)"); + return 0; +} + +int lua_register_ax_base_MoveTo(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.MoveTo"); + tolua_cclass(tolua_S,"MoveTo","ax.MoveTo","ax.MoveBy",nullptr); + + tolua_beginmodule(tolua_S,"MoveTo"); + tolua_function(tolua_S,"new",lua_ax_base_MoveTo_constructor); + tolua_function(tolua_S,"initWithDuration",lua_ax_base_MoveTo_initWithDuration); + tolua_function(tolua_S,"create", lua_ax_base_MoveTo_create); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::MoveTo).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.MoveTo"; + g_typeCast[typeName] = "ax.MoveTo"; + return 1; +} + +int lua_ax_base_SkewTo_initWithDuration(lua_State* tolua_S) { int argc = 0; - ax::Action* cobj = nullptr; + ax::SkewTo* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -19111,95 +19856,94 @@ int lua_ax_base_Action_setTag(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Action",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.SkewTo",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Action*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::SkewTo*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Action_setTag'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SkewTo_initWithDuration'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 3) { - int arg0; + double arg0; + double arg1; + double arg2; - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Action:setTag"); + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.SkewTo:initWithDuration"); + + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.SkewTo:initWithDuration"); + + ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.SkewTo:initWithDuration"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Action_setTag'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SkewTo_initWithDuration'", nullptr); return 0; } - cobj->setTag(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->initWithDuration(arg0, arg1, arg2); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Action:setTag",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SkewTo:initWithDuration",argc, 3); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Action_setTag'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SkewTo_initWithDuration'.",&tolua_err); #endif return 0; } -int lua_ax_base_Action_getFlags(lua_State* tolua_S) +int lua_ax_base_SkewTo_create(lua_State* tolua_S) { int argc = 0; - ax::Action* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Action",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.SkewTo",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Action*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Action_getFlags'", nullptr); - return 0; - } -#endif + argc = lua_gettop(tolua_S) - 1; - argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 3) { + double arg0; + double arg1; + double arg2; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.SkewTo:create"); + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.SkewTo:create"); + ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.SkewTo:create"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Action_getFlags'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SkewTo_create'", nullptr); return 0; } - auto&& ret = cobj->getFlags(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + auto&& ret = ax::SkewTo::create(arg0, arg1, arg2); + object_to_luaval(tolua_S, "ax.SkewTo",(ax::SkewTo*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Action:getFlags",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.SkewTo:create",argc, 3); return 0; - #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Action_getFlags'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SkewTo_create'.",&tolua_err); #endif - return 0; } -int lua_ax_base_Action_setFlags(lua_State* tolua_S) +int lua_ax_base_SkewTo_constructor(lua_State* tolua_S) { int argc = 0; - ax::Action* cobj = nullptr; + ax::SkewTo* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -19207,83 +19951,58 @@ int lua_ax_base_Action_setFlags(lua_State* tolua_S) #endif -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Action",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::Action*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Action_setFlags'", nullptr); - return 0; - } -#endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - unsigned int arg0; - - ok &= luaval_to_uint32(tolua_S, 2,&arg0, "ax.Action:setFlags"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Action_setFlags'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SkewTo_constructor'", nullptr); return 0; } - cobj->setFlags(arg0); - lua_settop(tolua_S, 1); + cobj = new ax::SkewTo(); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.SkewTo"); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Action:setFlags",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SkewTo:SkewTo",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Action_setFlags'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SkewTo_constructor'.",&tolua_err); #endif return 0; } -static int lua_ax_base_Action_finalize(lua_State* tolua_S) + +static int lua_ax_base_SkewTo_finalize(lua_State* tolua_S) { - AXLOGV("luabindings: finalizing LUA object (Action)"); + AXLOGV("luabindings: finalizing LUA object (SkewTo)"); return 0; } -int lua_register_ax_base_Action(lua_State* tolua_S) +int lua_register_ax_base_SkewTo(lua_State* tolua_S) { - tolua_usertype(tolua_S,"ax.Action"); - tolua_cclass(tolua_S,"Action","ax.Action","ax.Object",nullptr); + tolua_usertype(tolua_S,"ax.SkewTo"); + tolua_cclass(tolua_S,"SkewTo","ax.SkewTo","ax.ActionInterval",nullptr); - tolua_beginmodule(tolua_S,"Action"); - tolua_function(tolua_S,"clone",lua_ax_base_Action_clone); - tolua_function(tolua_S,"reverse",lua_ax_base_Action_reverse); - tolua_function(tolua_S,"isDone",lua_ax_base_Action_isDone); - tolua_function(tolua_S,"startWithTarget",lua_ax_base_Action_startWithTarget); - tolua_function(tolua_S,"stop",lua_ax_base_Action_stop); - tolua_function(tolua_S,"step",lua_ax_base_Action_step); - tolua_function(tolua_S,"update",lua_ax_base_Action_update); - tolua_function(tolua_S,"getTarget",lua_ax_base_Action_getTarget); - tolua_function(tolua_S,"setTarget",lua_ax_base_Action_setTarget); - tolua_function(tolua_S,"getOriginalTarget",lua_ax_base_Action_getOriginalTarget); - tolua_function(tolua_S,"setOriginalTarget",lua_ax_base_Action_setOriginalTarget); - tolua_function(tolua_S,"getTag",lua_ax_base_Action_getTag); - tolua_function(tolua_S,"setTag",lua_ax_base_Action_setTag); - tolua_function(tolua_S,"getFlags",lua_ax_base_Action_getFlags); - tolua_function(tolua_S,"setFlags",lua_ax_base_Action_setFlags); + tolua_beginmodule(tolua_S,"SkewTo"); + tolua_function(tolua_S,"new",lua_ax_base_SkewTo_constructor); + tolua_function(tolua_S,"initWithDuration",lua_ax_base_SkewTo_initWithDuration); + tolua_function(tolua_S,"create", lua_ax_base_SkewTo_create); tolua_endmodule(tolua_S); - auto typeName = typeid(ax::Action).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.Action"; - g_typeCast[typeName] = "ax.Action"; + auto typeName = typeid(ax::SkewTo).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.SkewTo"; + g_typeCast[typeName] = "ax.SkewTo"; return 1; } -int lua_ax_base_FiniteTimeAction_getDuration(lua_State* tolua_S) +int lua_ax_base_SkewBy_initWithDuration(lua_State* tolua_S) { int argc = 0; - ax::FiniteTimeAction* cobj = nullptr; + ax::SkewBy* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -19292,116 +20011,94 @@ int lua_ax_base_FiniteTimeAction_getDuration(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.FiniteTimeAction",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.SkewBy",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::FiniteTimeAction*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::SkewBy*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_FiniteTimeAction_getDuration'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SkewBy_initWithDuration'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 3) { + double arg0; + double arg1; + double arg2; + + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.SkewBy:initWithDuration"); + + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.SkewBy:initWithDuration"); + + ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.SkewBy:initWithDuration"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_FiniteTimeAction_getDuration'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SkewBy_initWithDuration'", nullptr); return 0; } - auto&& ret = cobj->getDuration(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + auto&& ret = cobj->initWithDuration(arg0, arg1, arg2); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.FiniteTimeAction:getDuration",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SkewBy:initWithDuration",argc, 3); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_FiniteTimeAction_getDuration'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SkewBy_initWithDuration'.",&tolua_err); #endif return 0; } -int lua_ax_base_FiniteTimeAction_setDuration(lua_State* tolua_S) +int lua_ax_base_SkewBy_create(lua_State* tolua_S) { int argc = 0; - ax::FiniteTimeAction* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.FiniteTimeAction",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.SkewBy",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::FiniteTimeAction*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_FiniteTimeAction_setDuration'", nullptr); - return 0; - } -#endif + argc = lua_gettop(tolua_S) - 1; - argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 3) { double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.FiniteTimeAction:setDuration"); + double arg1; + double arg2; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.SkewBy:create"); + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.SkewBy:create"); + ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.SkewBy:create"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_FiniteTimeAction_setDuration'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SkewBy_create'", nullptr); return 0; } - cobj->setDuration(arg0); - lua_settop(tolua_S, 1); + auto&& ret = ax::SkewBy::create(arg0, arg1, arg2); + object_to_luaval(tolua_S, "ax.SkewBy",(ax::SkewBy*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.FiniteTimeAction:setDuration",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.SkewBy:create",argc, 3); return 0; - #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_FiniteTimeAction_setDuration'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SkewBy_create'.",&tolua_err); #endif - - return 0; -} -static int lua_ax_base_FiniteTimeAction_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (FiniteTimeAction)"); return 0; } - -int lua_register_ax_base_FiniteTimeAction(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.FiniteTimeAction"); - tolua_cclass(tolua_S,"FiniteTimeAction","ax.FiniteTimeAction","ax.Action",nullptr); - - tolua_beginmodule(tolua_S,"FiniteTimeAction"); - tolua_function(tolua_S,"getDuration",lua_ax_base_FiniteTimeAction_getDuration); - tolua_function(tolua_S,"setDuration",lua_ax_base_FiniteTimeAction_setDuration); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::FiniteTimeAction).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.FiniteTimeAction"; - g_typeCast[typeName] = "ax.FiniteTimeAction"; - return 1; -} - -int lua_ax_base_Speed_getSpeed(lua_State* tolua_S) +int lua_ax_base_SkewBy_constructor(lua_State* tolua_S) { int argc = 0; - ax::Speed* cobj = nullptr; + ax::SkewBy* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -19409,46 +20106,58 @@ int lua_ax_base_Speed_getSpeed(lua_State* tolua_S) #endif -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Speed",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::Speed*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Speed_getSpeed'", nullptr); - return 0; - } -#endif argc = lua_gettop(tolua_S)-1; if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Speed_getSpeed'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SkewBy_constructor'", nullptr); return 0; } - auto&& ret = cobj->getSpeed(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + cobj = new ax::SkewBy(); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.SkewBy"); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Speed:getSpeed",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SkewBy:SkewBy",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Speed_getSpeed'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SkewBy_constructor'.",&tolua_err); #endif return 0; } -int lua_ax_base_Speed_setSpeed(lua_State* tolua_S) + +static int lua_ax_base_SkewBy_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (SkewBy)"); + return 0; +} + +int lua_register_ax_base_SkewBy(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.SkewBy"); + tolua_cclass(tolua_S,"SkewBy","ax.SkewBy","ax.SkewTo",nullptr); + + tolua_beginmodule(tolua_S,"SkewBy"); + tolua_function(tolua_S,"new",lua_ax_base_SkewBy_constructor); + tolua_function(tolua_S,"initWithDuration",lua_ax_base_SkewBy_initWithDuration); + tolua_function(tolua_S,"create", lua_ax_base_SkewBy_create); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::SkewBy).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.SkewBy"; + g_typeCast[typeName] = "ax.SkewBy"; + return 1; +} + +int lua_ax_base_JumpBy_initWithDuration(lua_State* tolua_S) { int argc = 0; - ax::Speed* cobj = nullptr; + ax::JumpBy* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -19457,98 +20166,99 @@ int lua_ax_base_Speed_setSpeed(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Speed",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.JumpBy",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Speed*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::JumpBy*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Speed_setSpeed'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_JumpBy_initWithDuration'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 4) { double arg0; + ax::Vec2 arg1; + double arg2; + int arg3; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Speed:setSpeed"); + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.JumpBy:initWithDuration"); + + ok &= luaval_to_vec2(tolua_S, 3, &arg1, "ax.JumpBy:initWithDuration"); + + ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.JumpBy:initWithDuration"); + + ok &= luaval_to_int32(tolua_S, 5,(int *)&arg3, "ax.JumpBy:initWithDuration"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Speed_setSpeed'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_JumpBy_initWithDuration'", nullptr); return 0; } - cobj->setSpeed(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->initWithDuration(arg0, arg1, arg2, arg3); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Speed:setSpeed",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.JumpBy:initWithDuration",argc, 4); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Speed_setSpeed'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_JumpBy_initWithDuration'.",&tolua_err); #endif return 0; } -int lua_ax_base_Speed_setInnerAction(lua_State* tolua_S) +int lua_ax_base_JumpBy_create(lua_State* tolua_S) { int argc = 0; - ax::Speed* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Speed",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.JumpBy",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Speed*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Speed_setInnerAction'", nullptr); - return 0; - } -#endif + argc = lua_gettop(tolua_S) - 1; - argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 4) { - ax::ActionInterval* arg0; - - ok &= luaval_to_object(tolua_S, 2, "ax.ActionInterval",&arg0, "ax.Speed:setInnerAction"); + double arg0; + ax::Vec2 arg1; + double arg2; + int arg3; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.JumpBy:create"); + ok &= luaval_to_vec2(tolua_S, 3, &arg1, "ax.JumpBy:create"); + ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.JumpBy:create"); + ok &= luaval_to_int32(tolua_S, 5,(int *)&arg3, "ax.JumpBy:create"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Speed_setInnerAction'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_JumpBy_create'", nullptr); return 0; } - cobj->setInnerAction(arg0); - lua_settop(tolua_S, 1); + auto&& ret = ax::JumpBy::create(arg0, arg1, arg2, arg3); + object_to_luaval(tolua_S, "ax.JumpBy",(ax::JumpBy*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Speed:setInnerAction",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.JumpBy:create",argc, 4); return 0; - #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Speed_setInnerAction'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_JumpBy_create'.",&tolua_err); #endif - return 0; } -int lua_ax_base_Speed_getInnerAction(lua_State* tolua_S) +int lua_ax_base_JumpBy_constructor(lua_State* tolua_S) { int argc = 0; - ax::Speed* cobj = nullptr; + ax::JumpBy* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -19556,46 +20266,58 @@ int lua_ax_base_Speed_getInnerAction(lua_State* tolua_S) #endif -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Speed",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::Speed*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Speed_getInnerAction'", nullptr); - return 0; - } -#endif argc = lua_gettop(tolua_S)-1; if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Speed_getInnerAction'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_JumpBy_constructor'", nullptr); return 0; } - auto&& ret = cobj->getInnerAction(); - object_to_luaval(tolua_S, "ax.ActionInterval",(ax::ActionInterval*)ret); + cobj = new ax::JumpBy(); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.JumpBy"); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Speed:getInnerAction",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.JumpBy:JumpBy",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Speed_getInnerAction'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_JumpBy_constructor'.",&tolua_err); #endif return 0; } -int lua_ax_base_Speed_initWithAction(lua_State* tolua_S) + +static int lua_ax_base_JumpBy_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (JumpBy)"); + return 0; +} + +int lua_register_ax_base_JumpBy(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.JumpBy"); + tolua_cclass(tolua_S,"JumpBy","ax.JumpBy","ax.ActionInterval",nullptr); + + tolua_beginmodule(tolua_S,"JumpBy"); + tolua_function(tolua_S,"new",lua_ax_base_JumpBy_constructor); + tolua_function(tolua_S,"initWithDuration",lua_ax_base_JumpBy_initWithDuration); + tolua_function(tolua_S,"create", lua_ax_base_JumpBy_create); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::JumpBy).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.JumpBy"; + g_typeCast[typeName] = "ax.JumpBy"; + return 1; +} + +int lua_ax_base_JumpTo_initWithDuration(lua_State* tolua_S) { int argc = 0; - ax::Speed* cobj = nullptr; + ax::JumpTo* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -19604,48 +20326,54 @@ int lua_ax_base_Speed_initWithAction(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Speed",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.JumpTo",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Speed*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::JumpTo*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Speed_initWithAction'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_JumpTo_initWithDuration'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 2) + if (argc == 4) { - ax::ActionInterval* arg0; - double arg1; + double arg0; + ax::Vec2 arg1; + double arg2; + int arg3; - ok &= luaval_to_object(tolua_S, 2, "ax.ActionInterval",&arg0, "ax.Speed:initWithAction"); + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.JumpTo:initWithDuration"); - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Speed:initWithAction"); + ok &= luaval_to_vec2(tolua_S, 3, &arg1, "ax.JumpTo:initWithDuration"); + + ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.JumpTo:initWithDuration"); + + ok &= luaval_to_int32(tolua_S, 5,(int *)&arg3, "ax.JumpTo:initWithDuration"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Speed_initWithAction'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_JumpTo_initWithDuration'", nullptr); return 0; } - auto&& ret = cobj->initWithAction(arg0, arg1); + auto&& ret = cobj->initWithDuration(arg0, arg1, arg2, arg3); tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Speed:initWithAction",argc, 2); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.JumpTo:initWithDuration",argc, 4); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Speed_initWithAction'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_JumpTo_initWithDuration'.",&tolua_err); #endif return 0; } -int lua_ax_base_Speed_create(lua_State* tolua_S) +int lua_ax_base_JumpTo_create(lua_State* tolua_S) { int argc = 0; bool ok = true; @@ -19655,38 +20383,42 @@ int lua_ax_base_Speed_create(lua_State* tolua_S) #endif #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.Speed",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.JumpTo",0,&tolua_err)) goto tolua_lerror; #endif argc = lua_gettop(tolua_S) - 1; - if (argc == 2) + if (argc == 4) { - ax::ActionInterval* arg0; - double arg1; - ok &= luaval_to_object(tolua_S, 2, "ax.ActionInterval",&arg0, "ax.Speed:create"); - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Speed:create"); + double arg0; + ax::Vec2 arg1; + double arg2; + int arg3; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.JumpTo:create"); + ok &= luaval_to_vec2(tolua_S, 3, &arg1, "ax.JumpTo:create"); + ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.JumpTo:create"); + ok &= luaval_to_int32(tolua_S, 5,(int *)&arg3, "ax.JumpTo:create"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Speed_create'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_JumpTo_create'", nullptr); return 0; } - auto&& ret = ax::Speed::create(arg0, arg1); - object_to_luaval(tolua_S, "ax.Speed",(ax::Speed*)ret); + auto&& ret = ax::JumpTo::create(arg0, arg1, arg2, arg3); + object_to_luaval(tolua_S, "ax.JumpTo",(ax::JumpTo*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Speed:create",argc, 2); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.JumpTo:create",argc, 4); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Speed_create'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_JumpTo_create'.",&tolua_err); #endif return 0; } -int lua_ax_base_Speed_constructor(lua_State* tolua_S) +int lua_ax_base_JumpTo_constructor(lua_State* tolua_S) { int argc = 0; - ax::Speed* cobj = nullptr; + ax::JumpTo* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -19700,56 +20432,52 @@ int lua_ax_base_Speed_constructor(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Speed_constructor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_JumpTo_constructor'", nullptr); return 0; } - cobj = new ax::Speed(); + cobj = new ax::JumpTo(); cobj->autorelease(); int ID = (int)cobj->_ID ; int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.Speed"); + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.JumpTo"); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Speed:Speed",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.JumpTo:JumpTo",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Speed_constructor'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_JumpTo_constructor'.",&tolua_err); #endif return 0; } -static int lua_ax_base_Speed_finalize(lua_State* tolua_S) +static int lua_ax_base_JumpTo_finalize(lua_State* tolua_S) { - AXLOGV("luabindings: finalizing LUA object (Speed)"); + AXLOGV("luabindings: finalizing LUA object (JumpTo)"); return 0; } -int lua_register_ax_base_Speed(lua_State* tolua_S) +int lua_register_ax_base_JumpTo(lua_State* tolua_S) { - tolua_usertype(tolua_S,"ax.Speed"); - tolua_cclass(tolua_S,"Speed","ax.Speed","ax.Action",nullptr); + tolua_usertype(tolua_S,"ax.JumpTo"); + tolua_cclass(tolua_S,"JumpTo","ax.JumpTo","ax.JumpBy",nullptr); - tolua_beginmodule(tolua_S,"Speed"); - tolua_function(tolua_S,"new",lua_ax_base_Speed_constructor); - tolua_function(tolua_S,"getSpeed",lua_ax_base_Speed_getSpeed); - tolua_function(tolua_S,"setSpeed",lua_ax_base_Speed_setSpeed); - tolua_function(tolua_S,"setInnerAction",lua_ax_base_Speed_setInnerAction); - tolua_function(tolua_S,"getInnerAction",lua_ax_base_Speed_getInnerAction); - tolua_function(tolua_S,"initWithAction",lua_ax_base_Speed_initWithAction); - tolua_function(tolua_S,"create", lua_ax_base_Speed_create); + tolua_beginmodule(tolua_S,"JumpTo"); + tolua_function(tolua_S,"new",lua_ax_base_JumpTo_constructor); + tolua_function(tolua_S,"initWithDuration",lua_ax_base_JumpTo_initWithDuration); + tolua_function(tolua_S,"create", lua_ax_base_JumpTo_create); tolua_endmodule(tolua_S); - auto typeName = typeid(ax::Speed).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.Speed"; - g_typeCast[typeName] = "ax.Speed"; + auto typeName = typeid(ax::JumpTo).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.JumpTo"; + g_typeCast[typeName] = "ax.JumpTo"; return 1; } -int lua_ax_base_Follow_isBoundarySet(lua_State* tolua_S) +int lua_ax_base_BezierBy_initWithDuration(lua_State* tolua_S) { int argc = 0; - ax::Follow* cobj = nullptr; + ax::BezierBy* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -19758,45 +20486,52 @@ int lua_ax_base_Follow_isBoundarySet(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Follow",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.BezierBy",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Follow*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::BezierBy*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Follow_isBoundarySet'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_BezierBy_initWithDuration'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 2) { + double arg0; + ax::_ccBezierConfig arg1; + + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.BezierBy:initWithDuration"); + + #pragma warning NO CONVERSION TO NATIVE FOR _ccBezierConfig + ok = false; if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Follow_isBoundarySet'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_BezierBy_initWithDuration'", nullptr); return 0; } - auto&& ret = cobj->isBoundarySet(); + auto&& ret = cobj->initWithDuration(arg0, arg1); tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Follow:isBoundarySet",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.BezierBy:initWithDuration",argc, 2); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Follow_isBoundarySet'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_BezierBy_initWithDuration'.",&tolua_err); #endif return 0; } -int lua_ax_base_Follow_setBoundarySet(lua_State* tolua_S) +int lua_ax_base_BezierBy_constructor(lua_State* tolua_S) { int argc = 0; - ax::Follow* cobj = nullptr; + ax::BezierBy* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -19804,49 +20539,57 @@ int lua_ax_base_Follow_setBoundarySet(lua_State* tolua_S) #endif -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Follow",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::Follow*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Follow_setBoundarySet'", nullptr); - return 0; - } -#endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - bool arg0; - - ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.Follow:setBoundarySet"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Follow_setBoundarySet'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_BezierBy_constructor'", nullptr); return 0; } - cobj->setBoundarySet(arg0); - lua_settop(tolua_S, 1); + cobj = new ax::BezierBy(); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.BezierBy"); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Follow:setBoundarySet",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.BezierBy:BezierBy",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Follow_setBoundarySet'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_BezierBy_constructor'.",&tolua_err); #endif return 0; } -int lua_ax_base_Follow_initWithTarget(lua_State* tolua_S) + +static int lua_ax_base_BezierBy_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (BezierBy)"); + return 0; +} + +int lua_register_ax_base_BezierBy(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.BezierBy"); + tolua_cclass(tolua_S,"BezierBy","ax.BezierBy","ax.ActionInterval",nullptr); + + tolua_beginmodule(tolua_S,"BezierBy"); + tolua_function(tolua_S,"new",lua_ax_base_BezierBy_constructor); + tolua_function(tolua_S,"initWithDuration",lua_ax_base_BezierBy_initWithDuration); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::BezierBy).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.BezierBy"; + g_typeCast[typeName] = "ax.BezierBy"; + return 1; +} + +int lua_ax_base_BezierTo_initWithDuration(lua_State* tolua_S) { int argc = 0; - ax::Follow* cobj = nullptr; + ax::BezierTo* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -19855,65 +20598,52 @@ int lua_ax_base_Follow_initWithTarget(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Follow",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.BezierTo",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Follow*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::BezierTo*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Follow_initWithTarget'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_BezierTo_initWithDuration'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - ax::Node* arg0; - - ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Follow:initWithTarget"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Follow_initWithTarget'", nullptr); - return 0; - } - auto&& ret = cobj->initWithTarget(arg0); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } if (argc == 2) { - ax::Node* arg0; - ax::Rect arg1; + double arg0; + ax::_ccBezierConfig arg1; - ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Follow:initWithTarget"); + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.BezierTo:initWithDuration"); - ok &= luaval_to_rect(tolua_S, 3, &arg1, "ax.Follow:initWithTarget"); + #pragma warning NO CONVERSION TO NATIVE FOR _ccBezierConfig + ok = false; if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Follow_initWithTarget'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_BezierTo_initWithDuration'", nullptr); return 0; } - auto&& ret = cobj->initWithTarget(arg0, arg1); + auto&& ret = cobj->initWithDuration(arg0, arg1); tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Follow:initWithTarget",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.BezierTo:initWithDuration",argc, 2); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Follow_initWithTarget'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_BezierTo_initWithDuration'.",&tolua_err); #endif return 0; } -int lua_ax_base_Follow_initWithTargetAndOffset(lua_State* tolua_S) +int lua_ax_base_BezierTo_constructor(lua_State* tolua_S) { int argc = 0; - ax::Follow* cobj = nullptr; + ax::BezierTo* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -19921,188 +20651,227 @@ int lua_ax_base_Follow_initWithTargetAndOffset(lua_State* tolua_S) #endif -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Follow",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::Follow*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Follow_initWithTargetAndOffset'", nullptr); - return 0; - } -#endif argc = lua_gettop(tolua_S)-1; - if (argc == 3) - { - ax::Node* arg0; - double arg1; - double arg2; - - ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Follow:initWithTargetAndOffset"); - - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Follow:initWithTargetAndOffset"); - - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.Follow:initWithTargetAndOffset"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Follow_initWithTargetAndOffset'", nullptr); - return 0; - } - auto&& ret = cobj->initWithTargetAndOffset(arg0, arg1, arg2); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - if (argc == 4) + if (argc == 0) { - ax::Node* arg0; - double arg1; - double arg2; - ax::Rect arg3; - - ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Follow:initWithTargetAndOffset"); - - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Follow:initWithTargetAndOffset"); - - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.Follow:initWithTargetAndOffset"); - - ok &= luaval_to_rect(tolua_S, 5, &arg3, "ax.Follow:initWithTargetAndOffset"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Follow_initWithTargetAndOffset'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_BezierTo_constructor'", nullptr); return 0; } - auto&& ret = cobj->initWithTargetAndOffset(arg0, arg1, arg2, arg3); - tolua_pushboolean(tolua_S,(bool)ret); + cobj = new ax::BezierTo(); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.BezierTo"); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Follow:initWithTargetAndOffset",argc, 3); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.BezierTo:BezierTo",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Follow_initWithTargetAndOffset'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_BezierTo_constructor'.",&tolua_err); #endif return 0; } -int lua_ax_base_Follow_create(lua_State* tolua_S) + +static int lua_ax_base_BezierTo_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (BezierTo)"); + return 0; +} + +int lua_register_ax_base_BezierTo(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.BezierTo"); + tolua_cclass(tolua_S,"BezierTo","ax.BezierTo","ax.BezierBy",nullptr); + + tolua_beginmodule(tolua_S,"BezierTo"); + tolua_function(tolua_S,"new",lua_ax_base_BezierTo_constructor); + tolua_function(tolua_S,"initWithDuration",lua_ax_base_BezierTo_initWithDuration); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::BezierTo).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.BezierTo"; + g_typeCast[typeName] = "ax.BezierTo"; + return 1; +} + +int lua_ax_base_ScaleTo_initWithDuration(lua_State* tolua_S) { int argc = 0; + ax::ScaleTo* cobj = nullptr; bool ok = true; - #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.Follow",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.ScaleTo",0,&tolua_err)) goto tolua_lerror; #endif - - argc = lua_gettop(tolua_S) - 1; - - if (argc == 1) + cobj = (ax::ScaleTo*)tolua_tousertype(tolua_S,1,0); +#if _AX_DEBUG >= 1 + if (!cobj) { - ax::Node* arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Follow:create"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Follow_create'", nullptr); - return 0; - } - auto&& ret = ax::Follow::create(arg0); - object_to_luaval(tolua_S, "ax.Follow",(ax::Follow*)ret); - return 1; + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_ScaleTo_initWithDuration'", nullptr); + return 0; } - if (argc == 2) - { - ax::Node* arg0; - ax::Rect arg1; - ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Follow:create"); - ok &= luaval_to_rect(tolua_S, 3, &arg1, "ax.Follow:create"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Follow_create'", nullptr); - return 0; +#endif + argc = lua_gettop(tolua_S)-1; + do{ + if (argc == 3) { + double arg0; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.ScaleTo:initWithDuration"); + + if (!ok) { break; } + double arg1; + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.ScaleTo:initWithDuration"); + + if (!ok) { break; } + double arg2; + ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.ScaleTo:initWithDuration"); + + if (!ok) { break; } + bool ret = cobj->initWithDuration(arg0, arg1, arg2); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; } - auto&& ret = ax::Follow::create(arg0, arg1); - object_to_luaval(tolua_S, "ax.Follow",(ax::Follow*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Follow:create",argc, 1); + }while(0); + ok = true; + do{ + if (argc == 2) { + double arg0; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.ScaleTo:initWithDuration"); + + if (!ok) { break; } + double arg1; + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.ScaleTo:initWithDuration"); + + if (!ok) { break; } + bool ret = cobj->initWithDuration(arg0, arg1); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; + } + }while(0); + ok = true; + do{ + if (argc == 4) { + double arg0; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.ScaleTo:initWithDuration"); + + if (!ok) { break; } + double arg1; + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.ScaleTo:initWithDuration"); + + if (!ok) { break; } + double arg2; + ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.ScaleTo:initWithDuration"); + + if (!ok) { break; } + double arg3; + ok &= luaval_to_number(tolua_S, 5,&arg3, "ax.ScaleTo:initWithDuration"); + + if (!ok) { break; } + bool ret = cobj->initWithDuration(arg0, arg1, arg2, arg3); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; + } + }while(0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.ScaleTo:initWithDuration",argc, 4); return 0; + #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Follow_create'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_ScaleTo_initWithDuration'.",&tolua_err); #endif + return 0; } -int lua_ax_base_Follow_createWithOffset(lua_State* tolua_S) +int lua_ax_base_ScaleTo_create(lua_State* tolua_S) { int argc = 0; bool ok = true; - #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.Follow",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.ScaleTo",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S) - 1; + argc = lua_gettop(tolua_S)-1; - if (argc == 3) + do { - ax::Node* arg0; - double arg1; - double arg2; - ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Follow:createWithOffset"); - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Follow:createWithOffset"); - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.Follow:createWithOffset"); - if(!ok) + if (argc == 3) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Follow_createWithOffset'", nullptr); - return 0; + double arg0; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.ScaleTo:create"); + if (!ok) { break; } + double arg1; + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.ScaleTo:create"); + if (!ok) { break; } + double arg2; + ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.ScaleTo:create"); + if (!ok) { break; } + ax::ScaleTo* ret = ax::ScaleTo::create(arg0, arg1, arg2); + object_to_luaval(tolua_S, "ax.ScaleTo",(ax::ScaleTo*)ret); + return 1; } - auto&& ret = ax::Follow::createWithOffset(arg0, arg1, arg2); - object_to_luaval(tolua_S, "ax.Follow",(ax::Follow*)ret); - return 1; - } - if (argc == 4) + } while (0); + ok = true; + do { - ax::Node* arg0; - double arg1; - double arg2; - ax::Rect arg3; - ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Follow:createWithOffset"); - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Follow:createWithOffset"); - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.Follow:createWithOffset"); - ok &= luaval_to_rect(tolua_S, 5, &arg3, "ax.Follow:createWithOffset"); - if(!ok) + if (argc == 2) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Follow_createWithOffset'", nullptr); - return 0; + double arg0; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.ScaleTo:create"); + if (!ok) { break; } + double arg1; + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.ScaleTo:create"); + if (!ok) { break; } + ax::ScaleTo* ret = ax::ScaleTo::create(arg0, arg1); + object_to_luaval(tolua_S, "ax.ScaleTo",(ax::ScaleTo*)ret); + return 1; } - auto&& ret = ax::Follow::createWithOffset(arg0, arg1, arg2, arg3); - object_to_luaval(tolua_S, "ax.Follow",(ax::Follow*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Follow:createWithOffset",argc, 3); + } while (0); + ok = true; + do + { + if (argc == 4) + { + double arg0; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.ScaleTo:create"); + if (!ok) { break; } + double arg1; + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.ScaleTo:create"); + if (!ok) { break; } + double arg2; + ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.ScaleTo:create"); + if (!ok) { break; } + double arg3; + ok &= luaval_to_number(tolua_S, 5,&arg3, "ax.ScaleTo:create"); + if (!ok) { break; } + ax::ScaleTo* ret = ax::ScaleTo::create(arg0, arg1, arg2, arg3); + object_to_luaval(tolua_S, "ax.ScaleTo",(ax::ScaleTo*)ret); + return 1; + } + } while (0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d", "ax.ScaleTo:create",argc, 4); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Follow_createWithOffset'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_ScaleTo_create'.",&tolua_err); #endif return 0; } -int lua_ax_base_Follow_constructor(lua_State* tolua_S) +int lua_ax_base_ScaleTo_constructor(lua_State* tolua_S) { int argc = 0; - ax::Follow* cobj = nullptr; + ax::ScaleTo* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -20116,106 +20885,131 @@ int lua_ax_base_Follow_constructor(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Follow_constructor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_ScaleTo_constructor'", nullptr); return 0; } - cobj = new ax::Follow(); + cobj = new ax::ScaleTo(); cobj->autorelease(); int ID = (int)cobj->_ID ; int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.Follow"); + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.ScaleTo"); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Follow:Follow",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.ScaleTo:ScaleTo",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Follow_constructor'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_ScaleTo_constructor'.",&tolua_err); #endif return 0; } -static int lua_ax_base_Follow_finalize(lua_State* tolua_S) +static int lua_ax_base_ScaleTo_finalize(lua_State* tolua_S) { - AXLOGV("luabindings: finalizing LUA object (Follow)"); + AXLOGV("luabindings: finalizing LUA object (ScaleTo)"); return 0; } -int lua_register_ax_base_Follow(lua_State* tolua_S) +int lua_register_ax_base_ScaleTo(lua_State* tolua_S) { - tolua_usertype(tolua_S,"ax.Follow"); - tolua_cclass(tolua_S,"Follow","ax.Follow","ax.Action",nullptr); + tolua_usertype(tolua_S,"ax.ScaleTo"); + tolua_cclass(tolua_S,"ScaleTo","ax.ScaleTo","ax.ActionInterval",nullptr); - tolua_beginmodule(tolua_S,"Follow"); - tolua_function(tolua_S,"new",lua_ax_base_Follow_constructor); - tolua_function(tolua_S,"isBoundarySet",lua_ax_base_Follow_isBoundarySet); - tolua_function(tolua_S,"setBoundarySet",lua_ax_base_Follow_setBoundarySet); - tolua_function(tolua_S,"initWithTarget",lua_ax_base_Follow_initWithTarget); - tolua_function(tolua_S,"initWithTargetAndOffset",lua_ax_base_Follow_initWithTargetAndOffset); - tolua_function(tolua_S,"create", lua_ax_base_Follow_create); - tolua_function(tolua_S,"createWithOffset", lua_ax_base_Follow_createWithOffset); + tolua_beginmodule(tolua_S,"ScaleTo"); + tolua_function(tolua_S,"new",lua_ax_base_ScaleTo_constructor); + tolua_function(tolua_S,"initWithDuration",lua_ax_base_ScaleTo_initWithDuration); + tolua_function(tolua_S,"create", lua_ax_base_ScaleTo_create); tolua_endmodule(tolua_S); - auto typeName = typeid(ax::Follow).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.Follow"; - g_typeCast[typeName] = "ax.Follow"; + auto typeName = typeid(ax::ScaleTo).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.ScaleTo"; + g_typeCast[typeName] = "ax.ScaleTo"; return 1; } -int lua_ax_base_Image_initWithImageFile(lua_State* tolua_S) +int lua_ax_base_ScaleBy_create(lua_State* tolua_S) { int argc = 0; - ax::Image* cobj = nullptr; bool ok = true; - #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Image",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.ScaleBy",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Image*)tolua_tousertype(tolua_S,1,0); + argc = lua_gettop(tolua_S)-1; -#if _AX_DEBUG >= 1 - if (!cobj) + do { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Image_initWithImageFile'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 3) + { + double arg0; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.ScaleBy:create"); + if (!ok) { break; } + double arg1; + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.ScaleBy:create"); + if (!ok) { break; } + double arg2; + ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.ScaleBy:create"); + if (!ok) { break; } + ax::ScaleBy* ret = ax::ScaleBy::create(arg0, arg1, arg2); + object_to_luaval(tolua_S, "ax.ScaleBy",(ax::ScaleBy*)ret); + return 1; + } + } while (0); + ok = true; + do { - std::string_view arg0; - - ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Image:initWithImageFile"); - if(!ok) + if (argc == 2) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_initWithImageFile'", nullptr); - return 0; + double arg0; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.ScaleBy:create"); + if (!ok) { break; } + double arg1; + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.ScaleBy:create"); + if (!ok) { break; } + ax::ScaleBy* ret = ax::ScaleBy::create(arg0, arg1); + object_to_luaval(tolua_S, "ax.ScaleBy",(ax::ScaleBy*)ret); + return 1; } - auto&& ret = cobj->initWithImageFile(arg0); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Image:initWithImageFile",argc, 1); + } while (0); + ok = true; + do + { + if (argc == 4) + { + double arg0; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.ScaleBy:create"); + if (!ok) { break; } + double arg1; + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.ScaleBy:create"); + if (!ok) { break; } + double arg2; + ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.ScaleBy:create"); + if (!ok) { break; } + double arg3; + ok &= luaval_to_number(tolua_S, 5,&arg3, "ax.ScaleBy:create"); + if (!ok) { break; } + ax::ScaleBy* ret = ax::ScaleBy::create(arg0, arg1, arg2, arg3); + object_to_luaval(tolua_S, "ax.ScaleBy",(ax::ScaleBy*)ret); + return 1; + } + } while (0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d", "ax.ScaleBy:create",argc, 4); return 0; - #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_initWithImageFile'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_ScaleBy_create'.",&tolua_err); #endif - return 0; } -int lua_ax_base_Image_flipRawData(lua_State* tolua_S) +int lua_ax_base_ScaleBy_constructor(lua_State* tolua_S) { int argc = 0; - ax::Image* cobj = nullptr; + ax::ScaleBy* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -20223,46 +21017,57 @@ int lua_ax_base_Image_flipRawData(lua_State* tolua_S) #endif -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Image",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::Image*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Image_flipRawData'", nullptr); - return 0; - } -#endif argc = lua_gettop(tolua_S)-1; if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_flipRawData'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_ScaleBy_constructor'", nullptr); return 0; } - cobj->flipRawData(); - lua_settop(tolua_S, 1); + cobj = new ax::ScaleBy(); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.ScaleBy"); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Image:flipRawData",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.ScaleBy:ScaleBy",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_flipRawData'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_ScaleBy_constructor'.",&tolua_err); #endif return 0; } -int lua_ax_base_Image_getFileType(lua_State* tolua_S) + +static int lua_ax_base_ScaleBy_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (ScaleBy)"); + return 0; +} + +int lua_register_ax_base_ScaleBy(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.ScaleBy"); + tolua_cclass(tolua_S,"ScaleBy","ax.ScaleBy","ax.ScaleTo",nullptr); + + tolua_beginmodule(tolua_S,"ScaleBy"); + tolua_function(tolua_S,"new",lua_ax_base_ScaleBy_constructor); + tolua_function(tolua_S,"create", lua_ax_base_ScaleBy_create); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::ScaleBy).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.ScaleBy"; + g_typeCast[typeName] = "ax.ScaleBy"; + return 1; +} + +int lua_ax_base_Blink_initWithDuration(lua_State* tolua_S) { int argc = 0; - ax::Image* cobj = nullptr; + ax::Blink* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -20271,92 +21076,89 @@ int lua_ax_base_Image_getFileType(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Image",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Blink",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Image*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Blink*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Image_getFileType'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Blink_initWithDuration'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 2) { + double arg0; + int arg1; + + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Blink:initWithDuration"); + + ok &= luaval_to_int32(tolua_S, 3,(int *)&arg1, "ax.Blink:initWithDuration"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_getFileType'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Blink_initWithDuration'", nullptr); return 0; } - int ret = (int)cobj->getFileType(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + auto&& ret = cobj->initWithDuration(arg0, arg1); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Image:getFileType",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Blink:initWithDuration",argc, 2); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_getFileType'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Blink_initWithDuration'.",&tolua_err); #endif return 0; } -int lua_ax_base_Image_getPixelFormat(lua_State* tolua_S) +int lua_ax_base_Blink_create(lua_State* tolua_S) { int argc = 0; - ax::Image* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Image",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.Blink",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Image*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Image_getPixelFormat'", nullptr); - return 0; - } -#endif + argc = lua_gettop(tolua_S) - 1; - argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 2) { + double arg0; + int arg1; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Blink:create"); + ok &= luaval_to_int32(tolua_S, 3,(int *)&arg1, "ax.Blink:create"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_getPixelFormat'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Blink_create'", nullptr); return 0; } - int ret = (int)cobj->getPixelFormat(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + auto&& ret = ax::Blink::create(arg0, arg1); + object_to_luaval(tolua_S, "ax.Blink",(ax::Blink*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Image:getPixelFormat",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Blink:create",argc, 2); return 0; - #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_getPixelFormat'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Blink_create'.",&tolua_err); #endif - return 0; } -int lua_ax_base_Image_getWidth(lua_State* tolua_S) +int lua_ax_base_Blink_constructor(lua_State* tolua_S) { int argc = 0; - ax::Image* cobj = nullptr; + ax::Blink* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -20364,46 +21166,58 @@ int lua_ax_base_Image_getWidth(lua_State* tolua_S) #endif -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Image",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::Image*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Image_getWidth'", nullptr); - return 0; - } -#endif argc = lua_gettop(tolua_S)-1; if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_getWidth'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Blink_constructor'", nullptr); return 0; } - auto&& ret = cobj->getWidth(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + cobj = new ax::Blink(); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.Blink"); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Image:getWidth",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Blink:Blink",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_getWidth'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Blink_constructor'.",&tolua_err); #endif return 0; } -int lua_ax_base_Image_getHeight(lua_State* tolua_S) + +static int lua_ax_base_Blink_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (Blink)"); + return 0; +} + +int lua_register_ax_base_Blink(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.Blink"); + tolua_cclass(tolua_S,"Blink","ax.Blink","ax.ActionInterval",nullptr); + + tolua_beginmodule(tolua_S,"Blink"); + tolua_function(tolua_S,"new",lua_ax_base_Blink_constructor); + tolua_function(tolua_S,"initWithDuration",lua_ax_base_Blink_initWithDuration); + tolua_function(tolua_S,"create", lua_ax_base_Blink_create); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::Blink).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.Blink"; + g_typeCast[typeName] = "ax.Blink"; + return 1; +} + +int lua_ax_base_FadeTo_initWithDuration(lua_State* tolua_S) { int argc = 0; - ax::Image* cobj = nullptr; + ax::FadeTo* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -20412,92 +21226,89 @@ int lua_ax_base_Image_getHeight(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Image",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.FadeTo",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Image*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::FadeTo*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Image_getHeight'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_FadeTo_initWithDuration'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 2) { + double arg0; + uint16_t arg1; + + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.FadeTo:initWithDuration"); + + ok &= luaval_to_uint16(tolua_S, 3,&arg1, "ax.FadeTo:initWithDuration"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_getHeight'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_FadeTo_initWithDuration'", nullptr); return 0; } - auto&& ret = cobj->getHeight(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + auto&& ret = cobj->initWithDuration(arg0, arg1); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Image:getHeight",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.FadeTo:initWithDuration",argc, 2); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_getHeight'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_FadeTo_initWithDuration'.",&tolua_err); #endif return 0; } -int lua_ax_base_Image_getNumberOfMipmaps(lua_State* tolua_S) +int lua_ax_base_FadeTo_create(lua_State* tolua_S) { int argc = 0; - ax::Image* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Image",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.FadeTo",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Image*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Image_getNumberOfMipmaps'", nullptr); - return 0; - } -#endif + argc = lua_gettop(tolua_S) - 1; - argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 2) { + double arg0; + uint16_t arg1; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.FadeTo:create"); + ok &= luaval_to_uint16(tolua_S, 3,&arg1, "ax.FadeTo:create"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_getNumberOfMipmaps'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_FadeTo_create'", nullptr); return 0; } - auto&& ret = cobj->getNumberOfMipmaps(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + auto&& ret = ax::FadeTo::create(arg0, arg1); + object_to_luaval(tolua_S, "ax.FadeTo",(ax::FadeTo*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Image:getNumberOfMipmaps",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.FadeTo:create",argc, 2); return 0; - #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_getNumberOfMipmaps'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_FadeTo_create'.",&tolua_err); #endif - return 0; } -int lua_ax_base_Image_hasPremultipliedAlpha(lua_State* tolua_S) +int lua_ax_base_FadeTo_constructor(lua_State* tolua_S) { int argc = 0; - ax::Image* cobj = nullptr; + ax::FadeTo* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -20505,46 +21316,58 @@ int lua_ax_base_Image_hasPremultipliedAlpha(lua_State* tolua_S) #endif -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Image",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::Image*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Image_hasPremultipliedAlpha'", nullptr); - return 0; - } -#endif argc = lua_gettop(tolua_S)-1; if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_hasPremultipliedAlpha'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_FadeTo_constructor'", nullptr); return 0; } - auto&& ret = cobj->hasPremultipliedAlpha(); - tolua_pushboolean(tolua_S,(bool)ret); + cobj = new ax::FadeTo(); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.FadeTo"); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Image:hasPremultipliedAlpha",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.FadeTo:FadeTo",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_hasPremultipliedAlpha'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_FadeTo_constructor'.",&tolua_err); #endif return 0; } -int lua_ax_base_Image_getFilePath(lua_State* tolua_S) + +static int lua_ax_base_FadeTo_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (FadeTo)"); + return 0; +} + +int lua_register_ax_base_FadeTo(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.FadeTo"); + tolua_cclass(tolua_S,"FadeTo","ax.FadeTo","ax.ActionInterval",nullptr); + + tolua_beginmodule(tolua_S,"FadeTo"); + tolua_function(tolua_S,"new",lua_ax_base_FadeTo_constructor); + tolua_function(tolua_S,"initWithDuration",lua_ax_base_FadeTo_initWithDuration); + tolua_function(tolua_S,"create", lua_ax_base_FadeTo_create); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::FadeTo).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.FadeTo"; + g_typeCast[typeName] = "ax.FadeTo"; + return 1; +} + +int lua_ax_base_FadeIn_setReverseAction(lua_State* tolua_S) { int argc = 0; - ax::Image* cobj = nullptr; + ax::FadeIn* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -20553,92 +21376,84 @@ int lua_ax_base_Image_getFilePath(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Image",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.FadeIn",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Image*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::FadeIn*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Image_getFilePath'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_FadeIn_setReverseAction'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + ax::FadeTo* arg0; + + ok &= luaval_to_object(tolua_S, 2, "ax.FadeTo",&arg0, "ax.FadeIn:setReverseAction"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_getFilePath'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_FadeIn_setReverseAction'", nullptr); return 0; } - auto&& ret = cobj->getFilePath(); - lua_pushlstring(tolua_S,ret.c_str(),ret.length()); + cobj->setReverseAction(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Image:getFilePath",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.FadeIn:setReverseAction",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_getFilePath'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_FadeIn_setReverseAction'.",&tolua_err); #endif return 0; } -int lua_ax_base_Image_getBitPerPixel(lua_State* tolua_S) +int lua_ax_base_FadeIn_create(lua_State* tolua_S) { int argc = 0; - ax::Image* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Image",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.FadeIn",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Image*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Image_getBitPerPixel'", nullptr); - return 0; - } -#endif + argc = lua_gettop(tolua_S) - 1; - argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + double arg0; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.FadeIn:create"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_getBitPerPixel'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_FadeIn_create'", nullptr); return 0; } - auto&& ret = cobj->getBitPerPixel(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + auto&& ret = ax::FadeIn::create(arg0); + object_to_luaval(tolua_S, "ax.FadeIn",(ax::FadeIn*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Image:getBitPerPixel",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.FadeIn:create",argc, 1); return 0; - #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_getBitPerPixel'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_FadeIn_create'.",&tolua_err); #endif - return 0; } -int lua_ax_base_Image_hasAlpha(lua_State* tolua_S) +int lua_ax_base_FadeIn_constructor(lua_State* tolua_S) { int argc = 0; - ax::Image* cobj = nullptr; + ax::FadeIn* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -20646,46 +21461,58 @@ int lua_ax_base_Image_hasAlpha(lua_State* tolua_S) #endif -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Image",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::Image*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Image_hasAlpha'", nullptr); - return 0; - } -#endif argc = lua_gettop(tolua_S)-1; if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_hasAlpha'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_FadeIn_constructor'", nullptr); return 0; } - auto&& ret = cobj->hasAlpha(); - tolua_pushboolean(tolua_S,(bool)ret); + cobj = new ax::FadeIn(); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.FadeIn"); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Image:hasAlpha",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.FadeIn:FadeIn",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_hasAlpha'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_FadeIn_constructor'.",&tolua_err); #endif return 0; } -int lua_ax_base_Image_isCompressed(lua_State* tolua_S) + +static int lua_ax_base_FadeIn_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (FadeIn)"); + return 0; +} + +int lua_register_ax_base_FadeIn(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.FadeIn"); + tolua_cclass(tolua_S,"FadeIn","ax.FadeIn","ax.FadeTo",nullptr); + + tolua_beginmodule(tolua_S,"FadeIn"); + tolua_function(tolua_S,"new",lua_ax_base_FadeIn_constructor); + tolua_function(tolua_S,"setReverseAction",lua_ax_base_FadeIn_setReverseAction); + tolua_function(tolua_S,"create", lua_ax_base_FadeIn_create); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::FadeIn).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.FadeIn"; + g_typeCast[typeName] = "ax.FadeIn"; + return 1; +} + +int lua_ax_base_FadeOut_setReverseAction(lua_State* tolua_S) { int argc = 0; - ax::Image* cobj = nullptr; + ax::FadeOut* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -20694,112 +21521,84 @@ int lua_ax_base_Image_isCompressed(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Image",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.FadeOut",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Image*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::FadeOut*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Image_isCompressed'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_FadeOut_setReverseAction'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + ax::FadeTo* arg0; + + ok &= luaval_to_object(tolua_S, 2, "ax.FadeTo",&arg0, "ax.FadeOut:setReverseAction"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_isCompressed'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_FadeOut_setReverseAction'", nullptr); return 0; } - auto&& ret = cobj->isCompressed(); - tolua_pushboolean(tolua_S,(bool)ret); + cobj->setReverseAction(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Image:isCompressed",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.FadeOut:setReverseAction",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_isCompressed'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_FadeOut_setReverseAction'.",&tolua_err); #endif return 0; } -int lua_ax_base_Image_saveToFile(lua_State* tolua_S) +int lua_ax_base_FadeOut_create(lua_State* tolua_S) { int argc = 0; - ax::Image* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Image",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::Image*)tolua_tousertype(tolua_S,1,0); - #if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Image_saveToFile'", nullptr); - return 0; - } + if (!tolua_isusertable(tolua_S,1,"ax.FadeOut",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - std::string_view arg0; + argc = lua_gettop(tolua_S) - 1; - ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Image:saveToFile"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_saveToFile'", nullptr); - return 0; - } - auto&& ret = cobj->saveToFile(arg0); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - if (argc == 2) + if (argc == 1) { - std::string_view arg0; - bool arg1; - - ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Image:saveToFile"); - - ok &= luaval_to_boolean(tolua_S, 3,&arg1, "ax.Image:saveToFile"); + double arg0; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.FadeOut:create"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_saveToFile'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_FadeOut_create'", nullptr); return 0; } - auto&& ret = cobj->saveToFile(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); + auto&& ret = ax::FadeOut::create(arg0); + object_to_luaval(tolua_S, "ax.FadeOut",(ax::FadeOut*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Image:saveToFile",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.FadeOut:create",argc, 1); return 0; - #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_saveToFile'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_FadeOut_create'.",&tolua_err); #endif - return 0; } -int lua_ax_base_Image_premultiplyAlpha(lua_State* tolua_S) +int lua_ax_base_FadeOut_constructor(lua_State* tolua_S) { int argc = 0; - ax::Image* cobj = nullptr; + ax::FadeOut* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -20807,46 +21606,58 @@ int lua_ax_base_Image_premultiplyAlpha(lua_State* tolua_S) #endif -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Image",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::Image*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Image_premultiplyAlpha'", nullptr); - return 0; - } -#endif argc = lua_gettop(tolua_S)-1; if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_premultiplyAlpha'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_FadeOut_constructor'", nullptr); return 0; } - cobj->premultiplyAlpha(); - lua_settop(tolua_S, 1); + cobj = new ax::FadeOut(); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.FadeOut"); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Image:premultiplyAlpha",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.FadeOut:FadeOut",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_premultiplyAlpha'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_FadeOut_constructor'.",&tolua_err); #endif return 0; } -int lua_ax_base_Image_reversePremultipliedAlpha(lua_State* tolua_S) + +static int lua_ax_base_FadeOut_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (FadeOut)"); + return 0; +} + +int lua_register_ax_base_FadeOut(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.FadeOut"); + tolua_cclass(tolua_S,"FadeOut","ax.FadeOut","ax.FadeTo",nullptr); + + tolua_beginmodule(tolua_S,"FadeOut"); + tolua_function(tolua_S,"new",lua_ax_base_FadeOut_constructor); + tolua_function(tolua_S,"setReverseAction",lua_ax_base_FadeOut_setReverseAction); + tolua_function(tolua_S,"create", lua_ax_base_FadeOut_create); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::FadeOut).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.FadeOut"; + g_typeCast[typeName] = "ax.FadeOut"; + return 1; +} + +int lua_ax_base_TintTo_initWithDuration(lua_State* tolua_S) { int argc = 0; - ax::Image* cobj = nullptr; + ax::TintTo* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -20855,281 +21666,372 @@ int lua_ax_base_Image_reversePremultipliedAlpha(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Image",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.TintTo",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Image*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::TintTo*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Image_reversePremultipliedAlpha'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_TintTo_initWithDuration'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 4) { + double arg0; + uint16_t arg1; + uint16_t arg2; + uint16_t arg3; + + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.TintTo:initWithDuration"); + + ok &= luaval_to_uint16(tolua_S, 3,&arg1, "ax.TintTo:initWithDuration"); + + ok &= luaval_to_uint16(tolua_S, 4,&arg2, "ax.TintTo:initWithDuration"); + + ok &= luaval_to_uint16(tolua_S, 5,&arg3, "ax.TintTo:initWithDuration"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_reversePremultipliedAlpha'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_TintTo_initWithDuration'", nullptr); return 0; } - cobj->reversePremultipliedAlpha(); - lua_settop(tolua_S, 1); + auto&& ret = cobj->initWithDuration(arg0, arg1, arg2, arg3); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Image:reversePremultipliedAlpha",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.TintTo:initWithDuration",argc, 4); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_reversePremultipliedAlpha'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_TintTo_initWithDuration'.",&tolua_err); #endif return 0; } -int lua_ax_base_Image_setPNGPremultipliedAlphaEnabled(lua_State* tolua_S) +int lua_ax_base_TintTo_create(lua_State* tolua_S) { int argc = 0; bool ok = true; - #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.Image",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.TintTo",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S) - 1; + argc = lua_gettop(tolua_S)-1; - if (argc == 1) + do { - bool arg0; - ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.Image:setPNGPremultipliedAlphaEnabled"); - if(!ok) + if (argc == 2) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_setPNGPremultipliedAlphaEnabled'", nullptr); - return 0; + double arg0; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.TintTo:create"); + if (!ok) { break; } + ax::Color3B arg1; + ok &= luaval_to_color3b(tolua_S, 3, &arg1, "ax.TintTo:create"); + if (!ok) { break; } + ax::TintTo* ret = ax::TintTo::create(arg0, arg1); + object_to_luaval(tolua_S, "ax.TintTo",(ax::TintTo*)ret); + return 1; } - ax::Image::setPNGPremultipliedAlphaEnabled(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Image:setPNGPremultipliedAlphaEnabled",argc, 1); + } while (0); + ok = true; + do + { + if (argc == 4) + { + double arg0; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.TintTo:create"); + if (!ok) { break; } + uint16_t arg1; + ok &= luaval_to_uint16(tolua_S, 3,&arg1, "ax.TintTo:create"); + if (!ok) { break; } + uint16_t arg2; + ok &= luaval_to_uint16(tolua_S, 4,&arg2, "ax.TintTo:create"); + if (!ok) { break; } + uint16_t arg3; + ok &= luaval_to_uint16(tolua_S, 5,&arg3, "ax.TintTo:create"); + if (!ok) { break; } + ax::TintTo* ret = ax::TintTo::create(arg0, arg1, arg2, arg3); + object_to_luaval(tolua_S, "ax.TintTo",(ax::TintTo*)ret); + return 1; + } + } while (0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d", "ax.TintTo:create",argc, 4); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_setPNGPremultipliedAlphaEnabled'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_TintTo_create'.",&tolua_err); #endif return 0; } -int lua_ax_base_Image_setCompressedImagesHavePMA(lua_State* tolua_S) +int lua_ax_base_TintTo_constructor(lua_State* tolua_S) { int argc = 0; + ax::TintTo* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif -#if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.Image",0,&tolua_err)) goto tolua_lerror; -#endif - argc = lua_gettop(tolua_S) - 1; - if (argc == 2) + argc = lua_gettop(tolua_S)-1; + if (argc == 0) { - unsigned int arg0; - bool arg1; - ok &= luaval_to_uint32(tolua_S, 2,&arg0, "ax.Image:setCompressedImagesHavePMA"); - ok &= luaval_to_boolean(tolua_S, 3,&arg1, "ax.Image:setCompressedImagesHavePMA"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_setCompressedImagesHavePMA'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_TintTo_constructor'", nullptr); return 0; } - ax::Image::setCompressedImagesHavePMA(arg0, arg1); - lua_settop(tolua_S, 1); + cobj = new ax::TintTo(); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.TintTo"); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Image:setCompressedImagesHavePMA",argc, 2); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.TintTo:TintTo",argc, 0); return 0; + #if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_setCompressedImagesHavePMA'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_TintTo_constructor'.",&tolua_err); #endif + return 0; } -int lua_ax_base_Image_isCompressedImageHavePMA(lua_State* tolua_S) + +static int lua_ax_base_TintTo_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (TintTo)"); + return 0; +} + +int lua_register_ax_base_TintTo(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.TintTo"); + tolua_cclass(tolua_S,"TintTo","ax.TintTo","ax.ActionInterval",nullptr); + + tolua_beginmodule(tolua_S,"TintTo"); + tolua_function(tolua_S,"new",lua_ax_base_TintTo_constructor); + tolua_function(tolua_S,"initWithDuration",lua_ax_base_TintTo_initWithDuration); + tolua_function(tolua_S,"create", lua_ax_base_TintTo_create); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::TintTo).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.TintTo"; + g_typeCast[typeName] = "ax.TintTo"; + return 1; +} + +int lua_ax_base_TintBy_initWithDuration(lua_State* tolua_S) { int argc = 0; + ax::TintBy* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.Image",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.TintBy",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S) - 1; + cobj = (ax::TintBy*)tolua_tousertype(tolua_S,1,0); - if (argc == 1) +#if _AX_DEBUG >= 1 + if (!cobj) { - unsigned int arg0; - ok &= luaval_to_uint32(tolua_S, 2,&arg0, "ax.Image:isCompressedImageHavePMA"); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_TintBy_initWithDuration'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 4) + { + double arg0; + int32_t arg1; + int32_t arg2; + int32_t arg3; + + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.TintBy:initWithDuration"); + + ok &= luaval_to_int32(tolua_S, 3,&arg1, "ax.TintBy:initWithDuration"); + + ok &= luaval_to_int32(tolua_S, 4,&arg2, "ax.TintBy:initWithDuration"); + + ok &= luaval_to_int32(tolua_S, 5,&arg3, "ax.TintBy:initWithDuration"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_isCompressedImageHavePMA'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_TintBy_initWithDuration'", nullptr); return 0; } - auto&& ret = ax::Image::isCompressedImageHavePMA(arg0); + auto&& ret = cobj->initWithDuration(arg0, arg1, arg2, arg3); tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Image:isCompressedImageHavePMA",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.TintBy:initWithDuration",argc, 4); return 0; + #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_isCompressedImageHavePMA'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_TintBy_initWithDuration'.",&tolua_err); #endif + return 0; } -int lua_ax_base_Image_constructor(lua_State* tolua_S) +int lua_ax_base_TintBy_create(lua_State* tolua_S) { int argc = 0; - ax::Image* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif +#if _AX_DEBUG >= 1 + if (!tolua_isusertable(tolua_S,1,"ax.TintBy",0,&tolua_err)) goto tolua_lerror; +#endif + argc = lua_gettop(tolua_S) - 1; - argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 4) { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Image_constructor'", nullptr); + double arg0; + int32_t arg1; + int32_t arg2; + int32_t arg3; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.TintBy:create"); + ok &= luaval_to_int32(tolua_S, 3,&arg1, "ax.TintBy:create"); + ok &= luaval_to_int32(tolua_S, 4,&arg2, "ax.TintBy:create"); + ok &= luaval_to_int32(tolua_S, 5,&arg3, "ax.TintBy:create"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_TintBy_create'", nullptr); return 0; } - cobj = new ax::Image(); + auto&& ret = ax::TintBy::create(arg0, arg1, arg2, arg3); + object_to_luaval(tolua_S, "ax.TintBy",(ax::TintBy*)ret); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.TintBy:create",argc, 4); + return 0; +#if _AX_DEBUG >= 1 + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_TintBy_create'.",&tolua_err); +#endif + return 0; +} +int lua_ax_base_TintBy_constructor(lua_State* tolua_S) +{ + int argc = 0; + ax::TintBy* cobj = nullptr; + bool ok = true; + +#if _AX_DEBUG >= 1 + tolua_Error tolua_err; +#endif + + + + argc = lua_gettop(tolua_S)-1; + if (argc == 0) + { + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_TintBy_constructor'", nullptr); + return 0; + } + cobj = new ax::TintBy(); cobj->autorelease(); int ID = (int)cobj->_ID ; int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.Image"); + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.TintBy"); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Image:Image",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.TintBy:TintBy",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Image_constructor'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_TintBy_constructor'.",&tolua_err); #endif return 0; } -static int lua_ax_base_Image_finalize(lua_State* tolua_S) +static int lua_ax_base_TintBy_finalize(lua_State* tolua_S) { - AXLOGV("luabindings: finalizing LUA object (Image)"); + AXLOGV("luabindings: finalizing LUA object (TintBy)"); return 0; } -int lua_register_ax_base_Image(lua_State* tolua_S) +int lua_register_ax_base_TintBy(lua_State* tolua_S) { - tolua_usertype(tolua_S,"ax.Image"); - tolua_cclass(tolua_S,"Image","ax.Image","ax.Object",nullptr); + tolua_usertype(tolua_S,"ax.TintBy"); + tolua_cclass(tolua_S,"TintBy","ax.TintBy","ax.ActionInterval",nullptr); - tolua_beginmodule(tolua_S,"Image"); - tolua_function(tolua_S,"new",lua_ax_base_Image_constructor); - tolua_function(tolua_S,"initWithImageFile",lua_ax_base_Image_initWithImageFile); - tolua_function(tolua_S,"flipRawData",lua_ax_base_Image_flipRawData); - tolua_function(tolua_S,"getFileType",lua_ax_base_Image_getFileType); - tolua_function(tolua_S,"getPixelFormat",lua_ax_base_Image_getPixelFormat); - tolua_function(tolua_S,"getWidth",lua_ax_base_Image_getWidth); - tolua_function(tolua_S,"getHeight",lua_ax_base_Image_getHeight); - tolua_function(tolua_S,"getNumberOfMipmaps",lua_ax_base_Image_getNumberOfMipmaps); - tolua_function(tolua_S,"hasPremultipliedAlpha",lua_ax_base_Image_hasPremultipliedAlpha); - tolua_function(tolua_S,"getFilePath",lua_ax_base_Image_getFilePath); - tolua_function(tolua_S,"getBitPerPixel",lua_ax_base_Image_getBitPerPixel); - tolua_function(tolua_S,"hasAlpha",lua_ax_base_Image_hasAlpha); - tolua_function(tolua_S,"isCompressed",lua_ax_base_Image_isCompressed); - tolua_function(tolua_S,"saveToFile",lua_ax_base_Image_saveToFile); - tolua_function(tolua_S,"premultiplyAlpha",lua_ax_base_Image_premultiplyAlpha); - tolua_function(tolua_S,"reversePremultipliedAlpha",lua_ax_base_Image_reversePremultipliedAlpha); - tolua_function(tolua_S,"setPNGPremultipliedAlphaEnabled", lua_ax_base_Image_setPNGPremultipliedAlphaEnabled); - tolua_function(tolua_S,"setCompressedImagesHavePMA", lua_ax_base_Image_setCompressedImagesHavePMA); - tolua_function(tolua_S,"isCompressedImageHavePMA", lua_ax_base_Image_isCompressedImageHavePMA); + tolua_beginmodule(tolua_S,"TintBy"); + tolua_function(tolua_S,"new",lua_ax_base_TintBy_constructor); + tolua_function(tolua_S,"initWithDuration",lua_ax_base_TintBy_initWithDuration); + tolua_function(tolua_S,"create", lua_ax_base_TintBy_create); tolua_endmodule(tolua_S); - auto typeName = typeid(ax::Image).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.Image"; - g_typeCast[typeName] = "ax.Image"; + auto typeName = typeid(ax::TintBy).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.TintBy"; + g_typeCast[typeName] = "ax.TintBy"; return 1; } -int lua_ax_base_PolygonInfo_setQuad(lua_State* tolua_S) +int lua_ax_base_DelayTime_create(lua_State* tolua_S) { int argc = 0; - ax::PolygonInfo* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PolygonInfo",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.DelayTime",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::PolygonInfo*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_PolygonInfo_setQuad'", nullptr); - return 0; - } -#endif + argc = lua_gettop(tolua_S) - 1; - argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 1) { - ax::V3F_C4B_T2F_Quad* arg0; - - #pragma warning NO CONVERSION TO NATIVE FOR V3F_C4B_T2F_Quad* - ok = false; + double arg0; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.DelayTime:create"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_PolygonInfo_setQuad'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_DelayTime_create'", nullptr); return 0; } - cobj->setQuad(arg0); - lua_settop(tolua_S, 1); + auto&& ret = ax::DelayTime::create(arg0); + object_to_luaval(tolua_S, "ax.DelayTime",(ax::DelayTime*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PolygonInfo:setQuad",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.DelayTime:create",argc, 1); return 0; - #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_PolygonInfo_setQuad'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_DelayTime_create'.",&tolua_err); #endif - return 0; } -int lua_ax_base_PolygonInfo_setQuads(lua_State* tolua_S) +int lua_ax_base_DelayTime_constructor(lua_State* tolua_S) { int argc = 0; - ax::PolygonInfo* cobj = nullptr; + ax::DelayTime* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -21137,53 +22039,57 @@ int lua_ax_base_PolygonInfo_setQuads(lua_State* tolua_S) #endif -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PolygonInfo",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PolygonInfo*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_PolygonInfo_setQuads'", nullptr); - return 0; - } -#endif argc = lua_gettop(tolua_S)-1; - if (argc == 2) + if (argc == 0) { - ax::V3F_C4B_T2F_Quad* arg0; - int arg1; - - #pragma warning NO CONVERSION TO NATIVE FOR V3F_C4B_T2F_Quad* - ok = false; - - ok &= luaval_to_int32(tolua_S, 3,(int *)&arg1, "ax.PolygonInfo:setQuads"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_PolygonInfo_setQuads'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_DelayTime_constructor'", nullptr); return 0; } - cobj->setQuads(arg0, arg1); - lua_settop(tolua_S, 1); + cobj = new ax::DelayTime(); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.DelayTime"); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PolygonInfo:setQuads",argc, 2); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.DelayTime:DelayTime",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_PolygonInfo_setQuads'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_DelayTime_constructor'.",&tolua_err); #endif return 0; } -int lua_ax_base_PolygonInfo_setTriangles(lua_State* tolua_S) + +static int lua_ax_base_DelayTime_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (DelayTime)"); + return 0; +} + +int lua_register_ax_base_DelayTime(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.DelayTime"); + tolua_cclass(tolua_S,"DelayTime","ax.DelayTime","ax.ActionInterval",nullptr); + + tolua_beginmodule(tolua_S,"DelayTime"); + tolua_function(tolua_S,"new",lua_ax_base_DelayTime_constructor); + tolua_function(tolua_S,"create", lua_ax_base_DelayTime_create); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::DelayTime).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.DelayTime"; + g_typeCast[typeName] = "ax.DelayTime"; + return 1; +} + +int lua_ax_base_Animate_setAnimation(lua_State* tolua_S) { int argc = 0; - ax::PolygonInfo* cobj = nullptr; + ax::Animate* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -21192,15 +22098,15 @@ int lua_ax_base_PolygonInfo_setTriangles(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PolygonInfo",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Animate",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::PolygonInfo*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Animate*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_PolygonInfo_setTriangles'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animate_setAnimation'", nullptr); return 0; } #endif @@ -21208,80 +22114,79 @@ int lua_ax_base_PolygonInfo_setTriangles(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::TrianglesCommand::Triangles arg0; + ax::Animation* arg0; - #pragma warning NO CONVERSION TO NATIVE FOR Triangles - ok = false; + ok &= luaval_to_object(tolua_S, 2, "ax.Animation",&arg0, "ax.Animate:setAnimation"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_PolygonInfo_setTriangles'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animate_setAnimation'", nullptr); return 0; } - cobj->setTriangles(arg0); + cobj->setAnimation(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PolygonInfo:setTriangles",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animate:setAnimation",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_PolygonInfo_setTriangles'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animate_setAnimation'.",&tolua_err); #endif return 0; } -int lua_ax_base_PolygonInfo_getVertCount(lua_State* tolua_S) +int lua_ax_base_Animate_getAnimation(lua_State* tolua_S) { int argc = 0; - ax::PolygonInfo* cobj = nullptr; + ax::Animate* cobj = nullptr; bool ok = true; - #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PolygonInfo",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Animate",0,&tolua_err)) goto tolua_lerror; #endif - - cobj = (ax::PolygonInfo*)tolua_tousertype(tolua_S,1,0); - + cobj = (ax::Animate*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_PolygonInfo_getVertCount'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animate_getAnimation'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_PolygonInfo_getVertCount'", nullptr); - return 0; + do{ + if (argc == 0) { + const ax::Animation* ret = cobj->getAnimation(); + object_to_luaval(tolua_S, "ax.Animation",(ax::Animation*)ret); + return 1; } - auto&& ret = cobj->getVertCount(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PolygonInfo:getVertCount",argc, 0); + }while(0); + ok = true; + do{ + if (argc == 0) { + ax::Animation* ret = cobj->getAnimation(); + object_to_luaval(tolua_S, "ax.Animation",(ax::Animation*)ret); + return 1; + } + }while(0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animate:getAnimation",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_PolygonInfo_getVertCount'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animate_getAnimation'.",&tolua_err); #endif return 0; } -int lua_ax_base_PolygonInfo_getTrianglesCount(lua_State* tolua_S) +int lua_ax_base_Animate_getCurrentFrameIndex(lua_State* tolua_S) { int argc = 0; - ax::PolygonInfo* cobj = nullptr; + ax::Animate* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -21290,15 +22195,15 @@ int lua_ax_base_PolygonInfo_getTrianglesCount(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PolygonInfo",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Animate",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::PolygonInfo*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Animate*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_PolygonInfo_getTrianglesCount'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animate_getCurrentFrameIndex'", nullptr); return 0; } #endif @@ -21308,27 +22213,27 @@ int lua_ax_base_PolygonInfo_getTrianglesCount(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_PolygonInfo_getTrianglesCount'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animate_getCurrentFrameIndex'", nullptr); return 0; } - auto&& ret = cobj->getTrianglesCount(); + auto&& ret = cobj->getCurrentFrameIndex(); tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PolygonInfo:getTrianglesCount",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animate:getCurrentFrameIndex",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_PolygonInfo_getTrianglesCount'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animate_getCurrentFrameIndex'.",&tolua_err); #endif return 0; } -int lua_ax_base_PolygonInfo_getArea(lua_State* tolua_S) +int lua_ax_base_Animate_initWithAnimation(lua_State* tolua_S) { int argc = 0; - ax::PolygonInfo* cobj = nullptr; + ax::Animate* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -21337,92 +22242,146 @@ int lua_ax_base_PolygonInfo_getArea(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PolygonInfo",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Animate",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::PolygonInfo*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Animate*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_PolygonInfo_getArea'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animate_initWithAnimation'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + ax::Animation* arg0; + + ok &= luaval_to_object(tolua_S, 2, "ax.Animation",&arg0, "ax.Animate:initWithAnimation"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_PolygonInfo_getArea'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animate_initWithAnimation'", nullptr); return 0; } - auto&& ret = cobj->getArea(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + auto&& ret = cobj->initWithAnimation(arg0); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PolygonInfo:getArea",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animate:initWithAnimation",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_PolygonInfo_getArea'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animate_initWithAnimation'.",&tolua_err); #endif return 0; } -int lua_ax_base_PolygonInfo_getRect(lua_State* tolua_S) +int lua_ax_base_Animate_create(lua_State* tolua_S) { int argc = 0; - ax::PolygonInfo* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PolygonInfo",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.Animate",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::PolygonInfo*)tolua_tousertype(tolua_S,1,0); + argc = lua_gettop(tolua_S) - 1; -#if _AX_DEBUG >= 1 - if (!cobj) + if (argc == 1) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_PolygonInfo_getRect'", nullptr); - return 0; + ax::Animation* arg0; + ok &= luaval_to_object(tolua_S, 2, "ax.Animation",&arg0, "ax.Animate:create"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animate_create'", nullptr); + return 0; + } + auto&& ret = ax::Animate::create(arg0); + object_to_luaval(tolua_S, "ax.Animate",(ax::Animate*)ret); + return 1; } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Animate:create",argc, 1); + return 0; +#if _AX_DEBUG >= 1 + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animate_create'.",&tolua_err); +#endif + return 0; +} +int lua_ax_base_Animate_constructor(lua_State* tolua_S) +{ + int argc = 0; + ax::Animate* cobj = nullptr; + bool ok = true; + +#if _AX_DEBUG >= 1 + tolua_Error tolua_err; #endif + + argc = lua_gettop(tolua_S)-1; if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_PolygonInfo_getRect'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animate_constructor'", nullptr); return 0; } - auto&& ret = cobj->getRect(); - rect_to_luaval(tolua_S, ret); + cobj = new ax::Animate(); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.Animate"); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PolygonInfo:getRect",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animate:Animate",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_PolygonInfo_getRect'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animate_constructor'.",&tolua_err); #endif return 0; } -int lua_ax_base_PolygonInfo_setRect(lua_State* tolua_S) + +static int lua_ax_base_Animate_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (Animate)"); + return 0; +} + +int lua_register_ax_base_Animate(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.Animate"); + tolua_cclass(tolua_S,"Animate","ax.Animate","ax.ActionInterval",nullptr); + + tolua_beginmodule(tolua_S,"Animate"); + tolua_function(tolua_S,"new",lua_ax_base_Animate_constructor); + tolua_function(tolua_S,"setAnimation",lua_ax_base_Animate_setAnimation); + tolua_function(tolua_S,"getAnimation",lua_ax_base_Animate_getAnimation); + tolua_function(tolua_S,"getCurrentFrameIndex",lua_ax_base_Animate_getCurrentFrameIndex); + tolua_function(tolua_S,"initWithAnimation",lua_ax_base_Animate_initWithAnimation); + tolua_function(tolua_S,"create", lua_ax_base_Animate_create); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::Animate).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.Animate"; + g_typeCast[typeName] = "ax.Animate"; + return 1; +} + +int lua_ax_base_TargetedAction_setForcedTarget(lua_State* tolua_S) { int argc = 0; - ax::PolygonInfo* cobj = nullptr; + ax::TargetedAction* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -21431,15 +22390,15 @@ int lua_ax_base_PolygonInfo_setRect(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PolygonInfo",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.TargetedAction",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::PolygonInfo*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::TargetedAction*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_PolygonInfo_setRect'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_TargetedAction_setForcedTarget'", nullptr); return 0; } #endif @@ -21447,79 +22406,79 @@ int lua_ax_base_PolygonInfo_setRect(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Rect arg0; + ax::Node* arg0; - ok &= luaval_to_rect(tolua_S, 2, &arg0, "ax.PolygonInfo:setRect"); + ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.TargetedAction:setForcedTarget"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_PolygonInfo_setRect'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_TargetedAction_setForcedTarget'", nullptr); return 0; } - cobj->setRect(arg0); + cobj->setForcedTarget(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PolygonInfo:setRect",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.TargetedAction:setForcedTarget",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_PolygonInfo_setRect'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_TargetedAction_setForcedTarget'.",&tolua_err); #endif return 0; } -int lua_ax_base_PolygonInfo_getFilename(lua_State* tolua_S) +int lua_ax_base_TargetedAction_getForcedTarget(lua_State* tolua_S) { int argc = 0; - ax::PolygonInfo* cobj = nullptr; + ax::TargetedAction* cobj = nullptr; bool ok = true; - #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PolygonInfo",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.TargetedAction",0,&tolua_err)) goto tolua_lerror; #endif - - cobj = (ax::PolygonInfo*)tolua_tousertype(tolua_S,1,0); - + cobj = (ax::TargetedAction*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_PolygonInfo_getFilename'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_TargetedAction_getForcedTarget'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_PolygonInfo_getFilename'", nullptr); - return 0; + do{ + if (argc == 0) { + const ax::Node* ret = cobj->getForcedTarget(); + object_to_luaval(tolua_S, "ax.Node",(ax::Node*)ret); + return 1; } - auto&& ret = cobj->getFilename(); - lua_pushlstring(tolua_S,ret.data(),ret.length()); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PolygonInfo:getFilename",argc, 0); + }while(0); + ok = true; + do{ + if (argc == 0) { + ax::Node* ret = cobj->getForcedTarget(); + object_to_luaval(tolua_S, "ax.Node",(ax::Node*)ret); + return 1; + } + }while(0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.TargetedAction:getForcedTarget",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_PolygonInfo_getFilename'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_TargetedAction_getForcedTarget'.",&tolua_err); #endif return 0; } -int lua_ax_base_PolygonInfo_setFilename(lua_State* tolua_S) +int lua_ax_base_TargetedAction_initWithTarget(lua_State* tolua_S) { int argc = 0; - ax::PolygonInfo* cobj = nullptr; + ax::TargetedAction* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -21528,113 +22487,89 @@ int lua_ax_base_PolygonInfo_setFilename(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PolygonInfo",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.TargetedAction",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::PolygonInfo*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::TargetedAction*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_PolygonInfo_setFilename'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_TargetedAction_initWithTarget'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 2) { - std::string_view arg0; + ax::Node* arg0; + ax::FiniteTimeAction* arg1; - ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.PolygonInfo:setFilename"); + ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.TargetedAction:initWithTarget"); + + ok &= luaval_to_object(tolua_S, 3, "ax.FiniteTimeAction",&arg1, "ax.TargetedAction:initWithTarget"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_PolygonInfo_setFilename'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_TargetedAction_initWithTarget'", nullptr); return 0; } - cobj->setFilename(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->initWithTarget(arg0, arg1); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PolygonInfo:setFilename",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.TargetedAction:initWithTarget",argc, 2); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_PolygonInfo_setFilename'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_TargetedAction_initWithTarget'.",&tolua_err); #endif return 0; } -int lua_ax_base_PolygonInfo_constructor(lua_State* tolua_S) +int lua_ax_base_TargetedAction_create(lua_State* tolua_S) { int argc = 0; - ax::PolygonInfo* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif +#if _AX_DEBUG >= 1 + if (!tolua_isusertable(tolua_S,1,"ax.TargetedAction",0,&tolua_err)) goto tolua_lerror; +#endif + argc = lua_gettop(tolua_S) - 1; - argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 2) { + ax::Node* arg0; + ax::FiniteTimeAction* arg1; + ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.TargetedAction:create"); + ok &= luaval_to_object(tolua_S, 3, "ax.FiniteTimeAction",&arg1, "ax.TargetedAction:create"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_PolygonInfo_constructor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_TargetedAction_create'", nullptr); return 0; } - cobj = new ax::PolygonInfo(); - tolua_pushusertype(tolua_S,(void*)cobj,"ax.PolygonInfo"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + auto&& ret = ax::TargetedAction::create(arg0, arg1); + object_to_luaval(tolua_S, "ax.TargetedAction",(ax::TargetedAction*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PolygonInfo:PolygonInfo",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.TargetedAction:create",argc, 2); return 0; - #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_PolygonInfo_constructor'.",&tolua_err); + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_TargetedAction_create'.",&tolua_err); #endif - return 0; } - -static int lua_ax_base_PolygonInfo_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (PolygonInfo)"); - return 0; -} - -int lua_register_ax_base_PolygonInfo(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.PolygonInfo"); - tolua_cclass(tolua_S,"PolygonInfo","ax.PolygonInfo","",nullptr); - - tolua_beginmodule(tolua_S,"PolygonInfo"); - tolua_function(tolua_S,"new",lua_ax_base_PolygonInfo_constructor); - tolua_function(tolua_S,"setQuad",lua_ax_base_PolygonInfo_setQuad); - tolua_function(tolua_S,"setQuads",lua_ax_base_PolygonInfo_setQuads); - tolua_function(tolua_S,"setTriangles",lua_ax_base_PolygonInfo_setTriangles); - tolua_function(tolua_S,"getVertCount",lua_ax_base_PolygonInfo_getVertCount); - tolua_function(tolua_S,"getTrianglesCount",lua_ax_base_PolygonInfo_getTrianglesCount); - tolua_function(tolua_S,"getArea",lua_ax_base_PolygonInfo_getArea); - tolua_function(tolua_S,"getRect",lua_ax_base_PolygonInfo_getRect); - tolua_function(tolua_S,"setRect",lua_ax_base_PolygonInfo_setRect); - tolua_function(tolua_S,"getFilename",lua_ax_base_PolygonInfo_getFilename); - tolua_function(tolua_S,"setFilename",lua_ax_base_PolygonInfo_setFilename); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::PolygonInfo).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.PolygonInfo"; - g_typeCast[typeName] = "ax.PolygonInfo"; - return 1; -} - -int lua_ax_base_AutoPolygon_constructor(lua_State* tolua_S) +int lua_ax_base_TargetedAction_constructor(lua_State* tolua_S) { int argc = 0; - ax::AutoPolygon* cobj = nullptr; + ax::TargetedAction* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -21644,55 +22579,58 @@ int lua_ax_base_AutoPolygon_constructor(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - std::string_view arg0; - - ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.AutoPolygon:AutoPolygon"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_AutoPolygon_constructor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_TargetedAction_constructor'", nullptr); return 0; } - cobj = new ax::AutoPolygon(arg0); - tolua_pushusertype(tolua_S,(void*)cobj,"ax.AutoPolygon"); - tolua_register_gc(tolua_S,lua_gettop(tolua_S)); + cobj = new ax::TargetedAction(); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.TargetedAction"); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.AutoPolygon:AutoPolygon",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.TargetedAction:TargetedAction",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_AutoPolygon_constructor'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_TargetedAction_constructor'.",&tolua_err); #endif return 0; } -static int lua_ax_base_AutoPolygon_finalize(lua_State* tolua_S) +static int lua_ax_base_TargetedAction_finalize(lua_State* tolua_S) { - AXLOGV("luabindings: finalizing LUA object (AutoPolygon)"); + AXLOGV("luabindings: finalizing LUA object (TargetedAction)"); return 0; } -int lua_register_ax_base_AutoPolygon(lua_State* tolua_S) +int lua_register_ax_base_TargetedAction(lua_State* tolua_S) { - tolua_usertype(tolua_S,"ax.AutoPolygon"); - tolua_cclass(tolua_S,"AutoPolygon","ax.AutoPolygon","",nullptr); + tolua_usertype(tolua_S,"ax.TargetedAction"); + tolua_cclass(tolua_S,"TargetedAction","ax.TargetedAction","ax.ActionInterval",nullptr); - tolua_beginmodule(tolua_S,"AutoPolygon"); - tolua_function(tolua_S,"new",lua_ax_base_AutoPolygon_constructor); + tolua_beginmodule(tolua_S,"TargetedAction"); + tolua_function(tolua_S,"new",lua_ax_base_TargetedAction_constructor); + tolua_function(tolua_S,"setForcedTarget",lua_ax_base_TargetedAction_setForcedTarget); + tolua_function(tolua_S,"getForcedTarget",lua_ax_base_TargetedAction_getForcedTarget); + tolua_function(tolua_S,"initWithTarget",lua_ax_base_TargetedAction_initWithTarget); + tolua_function(tolua_S,"create", lua_ax_base_TargetedAction_create); tolua_endmodule(tolua_S); - auto typeName = typeid(ax::AutoPolygon).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.AutoPolygon"; - g_typeCast[typeName] = "ax.AutoPolygon"; + auto typeName = typeid(ax::TargetedAction).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.TargetedAction"; + g_typeCast[typeName] = "ax.TargetedAction"; return 1; } -int lua_ax_base_SpriteFrame_getRectInPixels(lua_State* tolua_S) +int lua_ax_base_ActionFloat_initWithDuration(lua_State* tolua_S) { int argc = 0; - ax::SpriteFrame* cobj = nullptr; + ax::ActionFloat* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -21701,95 +22639,107 @@ int lua_ax_base_SpriteFrame_getRectInPixels(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.ActionFloat",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::ActionFloat*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_getRectInPixels'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_ActionFloat_initWithDuration'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 4) { + double arg0; + double arg1; + double arg2; + std::function arg3; + + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.ActionFloat:initWithDuration"); + + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.ActionFloat:initWithDuration"); + + ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.ActionFloat:initWithDuration"); + + do { + // Lambda binding for lua is not supported. + assert(false); + } while(0) + ; if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_getRectInPixels'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_ActionFloat_initWithDuration'", nullptr); return 0; } - auto&& ret = cobj->getRectInPixels(); - rect_to_luaval(tolua_S, ret); + auto&& ret = cobj->initWithDuration(arg0, arg1, arg2, arg3); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:getRectInPixels",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.ActionFloat:initWithDuration",argc, 4); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_getRectInPixels'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_ActionFloat_initWithDuration'.",&tolua_err); #endif return 0; } -int lua_ax_base_SpriteFrame_setRectInPixels(lua_State* tolua_S) +int lua_ax_base_ActionFloat_create(lua_State* tolua_S) { int argc = 0; - ax::SpriteFrame* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.ActionFloat",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_setRectInPixels'", nullptr); - return 0; - } -#endif + argc = lua_gettop(tolua_S) - 1; - argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 4) { - ax::Rect arg0; - - ok &= luaval_to_rect(tolua_S, 2, &arg0, "ax.SpriteFrame:setRectInPixels"); + double arg0; + double arg1; + double arg2; + std::function arg3; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.ActionFloat:create"); + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.ActionFloat:create"); + ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.ActionFloat:create"); + do { + // Lambda binding for lua is not supported. + assert(false); + } while(0) + ; if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_setRectInPixels'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_ActionFloat_create'", nullptr); return 0; } - cobj->setRectInPixels(arg0); - lua_settop(tolua_S, 1); + auto&& ret = ax::ActionFloat::create(arg0, arg1, arg2, arg3); + object_to_luaval(tolua_S, "ax.ActionFloat",(ax::ActionFloat*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:setRectInPixels",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.ActionFloat:create",argc, 4); return 0; - #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_setRectInPixels'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_ActionFloat_create'.",&tolua_err); #endif - return 0; } -int lua_ax_base_SpriteFrame_isRotated(lua_State* tolua_S) +int lua_ax_base_ActionFloat_constructor(lua_State* tolua_S) { int argc = 0; - ax::SpriteFrame* cobj = nullptr; + ax::ActionFloat* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -21797,46 +22747,58 @@ int lua_ax_base_SpriteFrame_isRotated(lua_State* tolua_S) #endif -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_isRotated'", nullptr); - return 0; - } -#endif argc = lua_gettop(tolua_S)-1; if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_isRotated'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_ActionFloat_constructor'", nullptr); return 0; } - auto&& ret = cobj->isRotated(); - tolua_pushboolean(tolua_S,(bool)ret); + cobj = new ax::ActionFloat(); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.ActionFloat"); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:isRotated",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.ActionFloat:ActionFloat",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_isRotated'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_ActionFloat_constructor'.",&tolua_err); #endif return 0; } -int lua_ax_base_SpriteFrame_setRotated(lua_State* tolua_S) + +static int lua_ax_base_ActionFloat_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (ActionFloat)"); + return 0; +} + +int lua_register_ax_base_ActionFloat(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.ActionFloat"); + tolua_cclass(tolua_S,"ActionFloat","ax.ActionFloat","ax.ActionInterval",nullptr); + + tolua_beginmodule(tolua_S,"ActionFloat"); + tolua_function(tolua_S,"new",lua_ax_base_ActionFloat_constructor); + tolua_function(tolua_S,"initWithDuration",lua_ax_base_ActionFloat_initWithDuration); + tolua_function(tolua_S,"create", lua_ax_base_ActionFloat_create); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::ActionFloat).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.ActionFloat"; + g_typeCast[typeName] = "ax.ActionFloat"; + return 1; +} + +int lua_ax_base_Scene_getDefaultCamera(lua_State* tolua_S) { int argc = 0; - ax::SpriteFrame* cobj = nullptr; + ax::Scene* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -21845,48 +22807,45 @@ int lua_ax_base_SpriteFrame_setRotated(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Scene",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Scene*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_setRotated'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Scene_getDefaultCamera'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - bool arg0; - - ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.SpriteFrame:setRotated"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_setRotated'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scene_getDefaultCamera'", nullptr); return 0; } - cobj->setRotated(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getDefaultCamera(); + object_to_luaval(tolua_S, "ax.Camera",(ax::Camera*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:setRotated",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Scene:getDefaultCamera",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_setRotated'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Scene_getDefaultCamera'.",&tolua_err); #endif return 0; } -int lua_ax_base_SpriteFrame_getRect(lua_State* tolua_S) +int lua_ax_base_Scene_render(lua_State* tolua_S) { int argc = 0; - ax::SpriteFrame* cobj = nullptr; + ax::Scene* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -21895,45 +22854,71 @@ int lua_ax_base_SpriteFrame_getRect(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Scene",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Scene*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_getRect'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Scene_render'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 2) { + ax::Renderer* arg0; + ax::Mat4 arg1; + + ok &= luaval_to_object(tolua_S, 2, "ax.Renderer",&arg0, "ax.Scene:render"); + + ok &= luaval_to_mat4(tolua_S, 3, &arg1, "ax.Scene:render"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_getRect'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scene_render'", nullptr); return 0; } - auto&& ret = cobj->getRect(); - rect_to_luaval(tolua_S, ret); + cobj->render(arg0, arg1); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:getRect",argc, 0); + if (argc == 3) + { + ax::Renderer* arg0; + ax::Mat4 arg1; + const ax::Mat4* arg2; + + ok &= luaval_to_object(tolua_S, 2, "ax.Renderer",&arg0, "ax.Scene:render"); + + ok &= luaval_to_mat4(tolua_S, 3, &arg1, "ax.Scene:render"); + + ok &= luaval_to_object(tolua_S, 4, "ax.Mat4",&arg2, "ax.Scene:render"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scene_render'", nullptr); + return 0; + } + cobj->render(arg0, arg1, arg2); + lua_settop(tolua_S, 1); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Scene:render",argc, 2); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_getRect'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Scene_render'.",&tolua_err); #endif return 0; } -int lua_ax_base_SpriteFrame_setRect(lua_State* tolua_S) +int lua_ax_base_Scene_initWithSize(lua_State* tolua_S) { int argc = 0; - ax::SpriteFrame* cobj = nullptr; + ax::Scene* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -21942,15 +22927,15 @@ int lua_ax_base_SpriteFrame_setRect(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Scene",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Scene*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_setRect'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Scene_initWithSize'", nullptr); return 0; } #endif @@ -21958,32 +22943,32 @@ int lua_ax_base_SpriteFrame_setRect(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Rect arg0; + ax::Vec2 arg0; - ok &= luaval_to_rect(tolua_S, 2, &arg0, "ax.SpriteFrame:setRect"); + ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.Scene:initWithSize"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_setRect'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scene_initWithSize'", nullptr); return 0; } - cobj->setRect(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->initWithSize(arg0); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:setRect",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Scene:initWithSize",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_setRect'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Scene_initWithSize'.",&tolua_err); #endif return 0; } -int lua_ax_base_SpriteFrame_getCenterRect(lua_State* tolua_S) +int lua_ax_base_Scene_setCameraOrderDirty(lua_State* tolua_S) { int argc = 0; - ax::SpriteFrame* cobj = nullptr; + ax::Scene* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -21992,15 +22977,15 @@ int lua_ax_base_SpriteFrame_getCenterRect(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Scene",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Scene*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_getCenterRect'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Scene_setCameraOrderDirty'", nullptr); return 0; } #endif @@ -22010,27 +22995,27 @@ int lua_ax_base_SpriteFrame_getCenterRect(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_getCenterRect'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scene_setCameraOrderDirty'", nullptr); return 0; } - auto&& ret = cobj->getCenterRect(); - rect_to_luaval(tolua_S, ret); + cobj->setCameraOrderDirty(); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:getCenterRect",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Scene:setCameraOrderDirty",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_getCenterRect'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Scene_setCameraOrderDirty'.",&tolua_err); #endif return 0; } -int lua_ax_base_SpriteFrame_setCenterRectInPixels(lua_State* tolua_S) +int lua_ax_base_Scene_onProjectionChanged(lua_State* tolua_S) { int argc = 0; - ax::SpriteFrame* cobj = nullptr; + ax::Scene* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -22039,15 +23024,15 @@ int lua_ax_base_SpriteFrame_setCenterRectInPixels(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Scene",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Scene*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_setCenterRectInPixels'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Scene_onProjectionChanged'", nullptr); return 0; } #endif @@ -22055,32 +23040,32 @@ int lua_ax_base_SpriteFrame_setCenterRectInPixels(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Rect arg0; + ax::EventCustom* arg0; - ok &= luaval_to_rect(tolua_S, 2, &arg0, "ax.SpriteFrame:setCenterRectInPixels"); + ok &= luaval_to_object(tolua_S, 2, "ax.EventCustom",&arg0, "ax.Scene:onProjectionChanged"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_setCenterRectInPixels'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scene_onProjectionChanged'", nullptr); return 0; } - cobj->setCenterRectInPixels(arg0); + cobj->onProjectionChanged(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:setCenterRectInPixels",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Scene:onProjectionChanged",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_setCenterRectInPixels'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Scene_onProjectionChanged'.",&tolua_err); #endif return 0; } -int lua_ax_base_SpriteFrame_hasCenterRect(lua_State* tolua_S) +int lua_ax_base_Scene_initWithPhysics(lua_State* tolua_S) { int argc = 0; - ax::SpriteFrame* cobj = nullptr; + ax::Scene* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -22089,15 +23074,15 @@ int lua_ax_base_SpriteFrame_hasCenterRect(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Scene",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Scene*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_hasCenterRect'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Scene_initWithPhysics'", nullptr); return 0; } #endif @@ -22107,27 +23092,27 @@ int lua_ax_base_SpriteFrame_hasCenterRect(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_hasCenterRect'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scene_initWithPhysics'", nullptr); return 0; } - auto&& ret = cobj->hasCenterRect(); + auto&& ret = cobj->initWithPhysics(); tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:hasCenterRect",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Scene:initWithPhysics",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_hasCenterRect'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Scene_initWithPhysics'.",&tolua_err); #endif return 0; } -int lua_ax_base_SpriteFrame_getOffsetInPixels(lua_State* tolua_S) +int lua_ax_base_Scene_initPhysicsWorld(lua_State* tolua_S) { int argc = 0; - ax::SpriteFrame* cobj = nullptr; + ax::Scene* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -22136,15 +23121,15 @@ int lua_ax_base_SpriteFrame_getOffsetInPixels(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Scene",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Scene*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_getOffsetInPixels'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Scene_initPhysicsWorld'", nullptr); return 0; } #endif @@ -22154,27 +23139,27 @@ int lua_ax_base_SpriteFrame_getOffsetInPixels(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_getOffsetInPixels'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scene_initPhysicsWorld'", nullptr); return 0; } - auto&& ret = cobj->getOffsetInPixels(); - vec2_to_luaval(tolua_S, ret); + auto&& ret = cobj->initPhysicsWorld(); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:getOffsetInPixels",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Scene:initPhysicsWorld",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_getOffsetInPixels'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Scene_initPhysicsWorld'.",&tolua_err); #endif return 0; } -int lua_ax_base_SpriteFrame_setOffsetInPixels(lua_State* tolua_S) +int lua_ax_base_Scene_fixedUpdate(lua_State* tolua_S) { int argc = 0; - ax::SpriteFrame* cobj = nullptr; + ax::Scene* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -22183,15 +23168,15 @@ int lua_ax_base_SpriteFrame_setOffsetInPixels(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Scene",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Scene*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_setOffsetInPixels'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Scene_fixedUpdate'", nullptr); return 0; } #endif @@ -22199,32 +23184,32 @@ int lua_ax_base_SpriteFrame_setOffsetInPixels(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Vec2 arg0; + double arg0; - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.SpriteFrame:setOffsetInPixels"); + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Scene:fixedUpdate"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_setOffsetInPixels'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scene_fixedUpdate'", nullptr); return 0; } - cobj->setOffsetInPixels(arg0); + cobj->fixedUpdate(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:setOffsetInPixels",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Scene:fixedUpdate",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_setOffsetInPixels'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Scene_fixedUpdate'.",&tolua_err); #endif return 0; } -int lua_ax_base_SpriteFrame_getOriginalSizeInPixels(lua_State* tolua_S) +int lua_ax_base_Scene_stepPhysicsAndNavigation(lua_State* tolua_S) { int argc = 0; - ax::SpriteFrame* cobj = nullptr; + ax::Scene* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -22233,192 +23218,221 @@ int lua_ax_base_SpriteFrame_getOriginalSizeInPixels(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Scene",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Scene*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_getOriginalSizeInPixels'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Scene_stepPhysicsAndNavigation'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + double arg0; + + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Scene:stepPhysicsAndNavigation"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_getOriginalSizeInPixels'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scene_stepPhysicsAndNavigation'", nullptr); return 0; } - auto&& ret = cobj->getOriginalSizeInPixels(); - vec2_to_luaval(tolua_S, ret); + cobj->stepPhysicsAndNavigation(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:getOriginalSizeInPixels",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Scene:stepPhysicsAndNavigation",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_getOriginalSizeInPixels'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Scene_stepPhysicsAndNavigation'.",&tolua_err); #endif return 0; } -int lua_ax_base_SpriteFrame_setOriginalSizeInPixels(lua_State* tolua_S) +int lua_ax_base_Scene_create(lua_State* tolua_S) { int argc = 0; - ax::SpriteFrame* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.Scene",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_setOriginalSizeInPixels'", nullptr); - return 0; - } -#endif + argc = lua_gettop(tolua_S) - 1; - argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - ax::Vec2 arg0; - - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.SpriteFrame:setOriginalSizeInPixels"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_setOriginalSizeInPixels'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scene_create'", nullptr); return 0; } - cobj->setOriginalSizeInPixels(arg0); - lua_settop(tolua_S, 1); + auto&& ret = ax::Scene::create(); + object_to_luaval(tolua_S, "ax.Scene",(ax::Scene*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:setOriginalSizeInPixels",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Scene:create",argc, 0); return 0; - #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_setOriginalSizeInPixels'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Scene_create'.",&tolua_err); #endif - return 0; } -int lua_ax_base_SpriteFrame_getOriginalSize(lua_State* tolua_S) +int lua_ax_base_Scene_createWithSize(lua_State* tolua_S) { int argc = 0; - ax::SpriteFrame* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.Scene",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_getOriginalSize'", nullptr); - return 0; - } -#endif + argc = lua_gettop(tolua_S) - 1; - argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + ax::Vec2 arg0; + ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.Scene:createWithSize"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_getOriginalSize'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scene_createWithSize'", nullptr); return 0; } - auto&& ret = cobj->getOriginalSize(); - vec2_to_luaval(tolua_S, ret); + auto&& ret = ax::Scene::createWithSize(arg0); + object_to_luaval(tolua_S, "ax.Scene",(ax::Scene*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:getOriginalSize",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Scene:createWithSize",argc, 1); return 0; - #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_getOriginalSize'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Scene_createWithSize'.",&tolua_err); #endif - return 0; } -int lua_ax_base_SpriteFrame_setOriginalSize(lua_State* tolua_S) +int lua_ax_base_Scene_createWithPhysics(lua_State* tolua_S) { int argc = 0; - ax::SpriteFrame* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.Scene",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); + argc = lua_gettop(tolua_S) - 1; -#if _AX_DEBUG >= 1 - if (!cobj) + if (argc == 0) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_setOriginalSize'", nullptr); - return 0; + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scene_createWithPhysics'", nullptr); + return 0; + } + auto&& ret = ax::Scene::createWithPhysics(); + object_to_luaval(tolua_S, "ax.Scene",(ax::Scene*)ret); + return 1; } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Scene:createWithPhysics",argc, 0); + return 0; +#if _AX_DEBUG >= 1 + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Scene_createWithPhysics'.",&tolua_err); +#endif + return 0; +} +int lua_ax_base_Scene_constructor(lua_State* tolua_S) +{ + int argc = 0; + ax::Scene* cobj = nullptr; + bool ok = true; + +#if _AX_DEBUG >= 1 + tolua_Error tolua_err; #endif + + argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - ax::Vec2 arg0; - - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.SpriteFrame:setOriginalSize"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_setOriginalSize'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scene_constructor'", nullptr); return 0; } - cobj->setOriginalSize(arg0); - lua_settop(tolua_S, 1); + cobj = new ax::Scene(); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.Scene"); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:setOriginalSize",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Scene:Scene",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_setOriginalSize'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Scene_constructor'.",&tolua_err); #endif return 0; } -int lua_ax_base_SpriteFrame_getTexture(lua_State* tolua_S) + +static int lua_ax_base_Scene_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (Scene)"); + return 0; +} + +int lua_register_ax_base_Scene(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.Scene"); + tolua_cclass(tolua_S,"Scene","ax.Scene","ax.Node",nullptr); + + tolua_beginmodule(tolua_S,"Scene"); + tolua_function(tolua_S,"new",lua_ax_base_Scene_constructor); + tolua_function(tolua_S,"getDefaultCamera",lua_ax_base_Scene_getDefaultCamera); + tolua_function(tolua_S,"render",lua_ax_base_Scene_render); + tolua_function(tolua_S,"initWithSize",lua_ax_base_Scene_initWithSize); + tolua_function(tolua_S,"setCameraOrderDirty",lua_ax_base_Scene_setCameraOrderDirty); + tolua_function(tolua_S,"onProjectionChanged",lua_ax_base_Scene_onProjectionChanged); + tolua_function(tolua_S,"initWithPhysics",lua_ax_base_Scene_initWithPhysics); + tolua_function(tolua_S,"initPhysicsWorld",lua_ax_base_Scene_initPhysicsWorld); + tolua_function(tolua_S,"fixedUpdate",lua_ax_base_Scene_fixedUpdate); + tolua_function(tolua_S,"stepPhysicsAndNavigation",lua_ax_base_Scene_stepPhysicsAndNavigation); + tolua_function(tolua_S,"create", lua_ax_base_Scene_create); + tolua_function(tolua_S,"createWithSize", lua_ax_base_Scene_createWithSize); + tolua_function(tolua_S,"createWithPhysics", lua_ax_base_Scene_createWithPhysics); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::Scene).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.Scene"; + g_typeCast[typeName] = "ax.Scene"; + return 1; +} + +int lua_ax_base_GLView_end(lua_State* tolua_S) { int argc = 0; - ax::SpriteFrame* cobj = nullptr; + ax::GLView* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -22427,15 +23441,15 @@ int lua_ax_base_SpriteFrame_getTexture(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_getTexture'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_end'", nullptr); return 0; } #endif @@ -22445,27 +23459,27 @@ int lua_ax_base_SpriteFrame_getTexture(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_getTexture'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_end'", nullptr); return 0; } - auto&& ret = cobj->getTexture(); - object_to_luaval(tolua_S, "ax.Texture2D",(ax::Texture2D*)ret); + cobj->end(); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:getTexture",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:end",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_getTexture'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_end'.",&tolua_err); #endif return 0; } -int lua_ax_base_SpriteFrame_setTexture(lua_State* tolua_S) +int lua_ax_base_GLView_isOpenGLReady(lua_State* tolua_S) { int argc = 0; - ax::SpriteFrame* cobj = nullptr; + ax::GLView* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -22474,48 +23488,45 @@ int lua_ax_base_SpriteFrame_setTexture(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_setTexture'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_isOpenGLReady'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - ax::Texture2D* arg0; - - ok &= luaval_to_object(tolua_S, 2, "ax.Texture2D",&arg0, "ax.SpriteFrame:setTexture"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_setTexture'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_isOpenGLReady'", nullptr); return 0; } - cobj->setTexture(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->isOpenGLReady(); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:setTexture",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:isOpenGLReady",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_setTexture'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_isOpenGLReady'.",&tolua_err); #endif return 0; } -int lua_ax_base_SpriteFrame_getOffset(lua_State* tolua_S) +int lua_ax_base_GLView_swapBuffers(lua_State* tolua_S) { int argc = 0; - ax::SpriteFrame* cobj = nullptr; + ax::GLView* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -22524,15 +23535,15 @@ int lua_ax_base_SpriteFrame_getOffset(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_getOffset'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_swapBuffers'", nullptr); return 0; } #endif @@ -22542,27 +23553,27 @@ int lua_ax_base_SpriteFrame_getOffset(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_getOffset'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_swapBuffers'", nullptr); return 0; } - auto&& ret = cobj->getOffset(); - vec2_to_luaval(tolua_S, ret); + cobj->swapBuffers(); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:getOffset",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:swapBuffers",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_getOffset'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_swapBuffers'.",&tolua_err); #endif return 0; } -int lua_ax_base_SpriteFrame_setOffset(lua_State* tolua_S) +int lua_ax_base_GLView_setIMEKeyboardState(lua_State* tolua_S) { int argc = 0; - ax::SpriteFrame* cobj = nullptr; + ax::GLView* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -22571,15 +23582,15 @@ int lua_ax_base_SpriteFrame_setOffset(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_setOffset'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_setIMEKeyboardState'", nullptr); return 0; } #endif @@ -22587,32 +23598,32 @@ int lua_ax_base_SpriteFrame_setOffset(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Vec2 arg0; + bool arg0; - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.SpriteFrame:setOffset"); + ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.GLView:setIMEKeyboardState"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_setOffset'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_setIMEKeyboardState'", nullptr); return 0; } - cobj->setOffset(arg0); + cobj->setIMEKeyboardState(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:setOffset",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:setIMEKeyboardState",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_setOffset'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_setIMEKeyboardState'.",&tolua_err); #endif return 0; } -int lua_ax_base_SpriteFrame_getAnchorPoint(lua_State* tolua_S) +int lua_ax_base_GLView_windowShouldClose(lua_State* tolua_S) { int argc = 0; - ax::SpriteFrame* cobj = nullptr; + ax::GLView* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -22621,15 +23632,15 @@ int lua_ax_base_SpriteFrame_getAnchorPoint(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_getAnchorPoint'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_windowShouldClose'", nullptr); return 0; } #endif @@ -22639,27 +23650,27 @@ int lua_ax_base_SpriteFrame_getAnchorPoint(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_getAnchorPoint'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_windowShouldClose'", nullptr); return 0; } - auto&& ret = cobj->getAnchorPoint(); - vec2_to_luaval(tolua_S, ret); + auto&& ret = cobj->windowShouldClose(); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:getAnchorPoint",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:windowShouldClose",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_getAnchorPoint'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_windowShouldClose'.",&tolua_err); #endif return 0; } -int lua_ax_base_SpriteFrame_setAnchorPoint(lua_State* tolua_S) +int lua_ax_base_GLView_pollEvents(lua_State* tolua_S) { int argc = 0; - ax::SpriteFrame* cobj = nullptr; + ax::GLView* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -22668,48 +23679,45 @@ int lua_ax_base_SpriteFrame_setAnchorPoint(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_setAnchorPoint'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_pollEvents'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - ax::Vec2 arg0; - - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.SpriteFrame:setAnchorPoint"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_setAnchorPoint'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_pollEvents'", nullptr); return 0; } - cobj->setAnchorPoint(arg0); + cobj->pollEvents(); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:setAnchorPoint",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:pollEvents",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_setAnchorPoint'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_pollEvents'.",&tolua_err); #endif return 0; } -int lua_ax_base_SpriteFrame_hasAnchorPoint(lua_State* tolua_S) +int lua_ax_base_GLView_getFrameSize(lua_State* tolua_S) { int argc = 0; - ax::SpriteFrame* cobj = nullptr; + ax::GLView* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -22718,15 +23726,15 @@ int lua_ax_base_SpriteFrame_hasAnchorPoint(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_hasAnchorPoint'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_getFrameSize'", nullptr); return 0; } #endif @@ -22736,27 +23744,27 @@ int lua_ax_base_SpriteFrame_hasAnchorPoint(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_hasAnchorPoint'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_getFrameSize'", nullptr); return 0; } - auto&& ret = cobj->hasAnchorPoint(); - tolua_pushboolean(tolua_S,(bool)ret); + auto&& ret = cobj->getFrameSize(); + vec2_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:hasAnchorPoint",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:getFrameSize",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_hasAnchorPoint'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_getFrameSize'.",&tolua_err); #endif return 0; } -int lua_ax_base_SpriteFrame_clone(lua_State* tolua_S) +int lua_ax_base_GLView_setFrameSize(lua_State* tolua_S) { int argc = 0; - ax::SpriteFrame* cobj = nullptr; + ax::GLView* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -22765,405 +23773,245 @@ int lua_ax_base_SpriteFrame_clone(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_clone'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_setFrameSize'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 2) { + double arg0; + double arg1; + + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.GLView:setFrameSize"); + + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.GLView:setFrameSize"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_clone'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_setFrameSize'", nullptr); return 0; } - auto&& ret = cobj->clone(); - object_to_luaval(tolua_S, "ax.SpriteFrame",(ax::SpriteFrame*)ret); + cobj->setFrameSize(arg0, arg1); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:clone",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:setFrameSize",argc, 2); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_clone'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_setFrameSize'.",&tolua_err); #endif return 0; } -int lua_ax_base_SpriteFrame_initWithTexture(lua_State* tolua_S) +int lua_ax_base_GLView_setFrameZoomFactor(lua_State* tolua_S) { int argc = 0; - ax::SpriteFrame* cobj = nullptr; + ax::GLView* cobj = nullptr; bool ok = true; + #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); + + cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); + #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_initWithTexture'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_setFrameZoomFactor'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - do{ - if (argc == 5) { - ax::Texture2D* arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.Texture2D",&arg0, "ax.SpriteFrame:initWithTexture"); - - if (!ok) { break; } - ax::Rect arg1; - ok &= luaval_to_rect(tolua_S, 3, &arg1, "ax.SpriteFrame:initWithTexture"); - - if (!ok) { break; } - bool arg2; - ok &= luaval_to_boolean(tolua_S, 4,&arg2, "ax.SpriteFrame:initWithTexture"); - - if (!ok) { break; } - ax::Vec2 arg3; - ok &= luaval_to_vec2(tolua_S, 5, &arg3, "ax.SpriteFrame:initWithTexture"); - - if (!ok) { break; } - ax::Vec2 arg4; - ok &= luaval_to_vec2(tolua_S, 6, &arg4, "ax.SpriteFrame:initWithTexture"); - - if (!ok) { break; } - bool ret = cobj->initWithTexture(arg0, arg1, arg2, arg3, arg4); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 2) { - ax::Texture2D* arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.Texture2D",&arg0, "ax.SpriteFrame:initWithTexture"); - if (!ok) { break; } - ax::Rect arg1; - ok &= luaval_to_rect(tolua_S, 3, &arg1, "ax.SpriteFrame:initWithTexture"); + argc = lua_gettop(tolua_S)-1; + if (argc == 1) + { + double arg0; - if (!ok) { break; } - bool ret = cobj->initWithTexture(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.GLView:setFrameZoomFactor"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_setFrameZoomFactor'", nullptr); + return 0; } - }while(0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:initWithTexture",argc, 2); + cobj->setFrameZoomFactor(arg0); + lua_settop(tolua_S, 1); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:setFrameZoomFactor",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_initWithTexture'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_setFrameZoomFactor'.",&tolua_err); #endif return 0; } -int lua_ax_base_SpriteFrame_initWithTextureFilename(lua_State* tolua_S) +int lua_ax_base_GLView_getFrameZoomFactor(lua_State* tolua_S) { int argc = 0; - ax::SpriteFrame* cobj = nullptr; + ax::GLView* cobj = nullptr; bool ok = true; + #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::SpriteFrame*)tolua_tousertype(tolua_S,1,0); + + cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); + #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SpriteFrame_initWithTextureFilename'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_getFrameZoomFactor'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - do{ - if (argc == 5) { - std::string_view arg0; - ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.SpriteFrame:initWithTextureFilename"); - - if (!ok) { break; } - ax::Rect arg1; - ok &= luaval_to_rect(tolua_S, 3, &arg1, "ax.SpriteFrame:initWithTextureFilename"); - - if (!ok) { break; } - bool arg2; - ok &= luaval_to_boolean(tolua_S, 4,&arg2, "ax.SpriteFrame:initWithTextureFilename"); - - if (!ok) { break; } - ax::Vec2 arg3; - ok &= luaval_to_vec2(tolua_S, 5, &arg3, "ax.SpriteFrame:initWithTextureFilename"); - - if (!ok) { break; } - ax::Vec2 arg4; - ok &= luaval_to_vec2(tolua_S, 6, &arg4, "ax.SpriteFrame:initWithTextureFilename"); - - if (!ok) { break; } - bool ret = cobj->initWithTextureFilename(arg0, arg1, arg2, arg3, arg4); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 2) { - std::string_view arg0; - ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.SpriteFrame:initWithTextureFilename"); - - if (!ok) { break; } - ax::Rect arg1; - ok &= luaval_to_rect(tolua_S, 3, &arg1, "ax.SpriteFrame:initWithTextureFilename"); - if (!ok) { break; } - bool ret = cobj->initWithTextureFilename(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; + argc = lua_gettop(tolua_S)-1; + if (argc == 0) + { + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_getFrameZoomFactor'", nullptr); + return 0; } - }while(0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:initWithTextureFilename",argc, 2); + auto&& ret = cobj->getFrameZoomFactor(); + tolua_pushnumber(tolua_S,(lua_Number)ret); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:getFrameZoomFactor",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_initWithTextureFilename'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_getFrameZoomFactor'.",&tolua_err); #endif return 0; } -int lua_ax_base_SpriteFrame_create(lua_State* tolua_S) +int lua_ax_base_GLView_setCursorVisible(lua_State* tolua_S) { int argc = 0; + ax::GLView* cobj = nullptr; bool ok = true; + #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S)-1; + cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); - do +#if _AX_DEBUG >= 1 + if (!cobj) { - if (argc == 5) - { - std::string_view arg0; - ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.SpriteFrame:create"); - if (!ok) { break; } - ax::Rect arg1; - ok &= luaval_to_rect(tolua_S, 3, &arg1, "ax.SpriteFrame:create"); - if (!ok) { break; } - bool arg2; - ok &= luaval_to_boolean(tolua_S, 4,&arg2, "ax.SpriteFrame:create"); - if (!ok) { break; } - ax::Vec2 arg3; - ok &= luaval_to_vec2(tolua_S, 5, &arg3, "ax.SpriteFrame:create"); - if (!ok) { break; } - ax::Vec2 arg4; - ok &= luaval_to_vec2(tolua_S, 6, &arg4, "ax.SpriteFrame:create"); - if (!ok) { break; } - ax::SpriteFrame* ret = ax::SpriteFrame::create(arg0, arg1, arg2, arg3, arg4); - object_to_luaval(tolua_S, "ax.SpriteFrame",(ax::SpriteFrame*)ret); - return 1; - } - } while (0); - ok = true; - do + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_setCursorVisible'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 1) { - if (argc == 2) + bool arg0; + + ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.GLView:setCursorVisible"); + if(!ok) { - std::string_view arg0; - ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.SpriteFrame:create"); - if (!ok) { break; } - ax::Rect arg1; - ok &= luaval_to_rect(tolua_S, 3, &arg1, "ax.SpriteFrame:create"); - if (!ok) { break; } - ax::SpriteFrame* ret = ax::SpriteFrame::create(arg0, arg1); - object_to_luaval(tolua_S, "ax.SpriteFrame",(ax::SpriteFrame*)ret); - return 1; + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_setCursorVisible'", nullptr); + return 0; } - } while (0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d", "ax.SpriteFrame:create",argc, 2); + cobj->setCursorVisible(arg0); + lua_settop(tolua_S, 1); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:setCursorVisible",argc, 1); return 0; + #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_create'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_setCursorVisible'.",&tolua_err); #endif + return 0; } -int lua_ax_base_SpriteFrame_createWithTexture(lua_State* tolua_S) +int lua_ax_base_GLView_getRetinaFactor(lua_State* tolua_S) { int argc = 0; + ax::GLView* cobj = nullptr; bool ok = true; + #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.SpriteFrame",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S)-1; + cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); - do +#if _AX_DEBUG >= 1 + if (!cobj) { - if (argc == 5) + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_getRetinaFactor'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 0) + { + if(!ok) { - ax::Texture2D* arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.Texture2D",&arg0, "ax.SpriteFrame:createWithTexture"); - if (!ok) { break; } - ax::Rect arg1; - ok &= luaval_to_rect(tolua_S, 3, &arg1, "ax.SpriteFrame:createWithTexture"); - if (!ok) { break; } - bool arg2; - ok &= luaval_to_boolean(tolua_S, 4,&arg2, "ax.SpriteFrame:createWithTexture"); - if (!ok) { break; } - ax::Vec2 arg3; - ok &= luaval_to_vec2(tolua_S, 5, &arg3, "ax.SpriteFrame:createWithTexture"); - if (!ok) { break; } - ax::Vec2 arg4; - ok &= luaval_to_vec2(tolua_S, 6, &arg4, "ax.SpriteFrame:createWithTexture"); - if (!ok) { break; } - ax::SpriteFrame* ret = ax::SpriteFrame::createWithTexture(arg0, arg1, arg2, arg3, arg4); - object_to_luaval(tolua_S, "ax.SpriteFrame",(ax::SpriteFrame*)ret); - return 1; - } - } while (0); - ok = true; - do - { - if (argc == 2) - { - ax::Texture2D* arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.Texture2D",&arg0, "ax.SpriteFrame:createWithTexture"); - if (!ok) { break; } - ax::Rect arg1; - ok &= luaval_to_rect(tolua_S, 3, &arg1, "ax.SpriteFrame:createWithTexture"); - if (!ok) { break; } - ax::SpriteFrame* ret = ax::SpriteFrame::createWithTexture(arg0, arg1); - object_to_luaval(tolua_S, "ax.SpriteFrame",(ax::SpriteFrame*)ret); - return 1; - } - } while (0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d", "ax.SpriteFrame:createWithTexture",argc, 2); - return 0; -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_createWithTexture'.",&tolua_err); -#endif - return 0; -} -int lua_ax_base_SpriteFrame_constructor(lua_State* tolua_S) -{ - int argc = 0; - ax::SpriteFrame* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SpriteFrame_constructor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_getRetinaFactor'", nullptr); return 0; } - cobj = new ax::SpriteFrame(); - cobj->autorelease(); - int ID = (int)cobj->_ID ; - int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.SpriteFrame"); + auto&& ret = cobj->getRetinaFactor(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SpriteFrame:SpriteFrame",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:getRetinaFactor",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SpriteFrame_constructor'.",&tolua_err); + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_getRetinaFactor'.",&tolua_err); #endif return 0; } - -static int lua_ax_base_SpriteFrame_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (SpriteFrame)"); - return 0; -} - -int lua_register_ax_base_SpriteFrame(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.SpriteFrame"); - tolua_cclass(tolua_S,"SpriteFrame","ax.SpriteFrame","ax.Object",nullptr); - - tolua_beginmodule(tolua_S,"SpriteFrame"); - tolua_function(tolua_S,"new",lua_ax_base_SpriteFrame_constructor); - tolua_function(tolua_S,"getRectInPixels",lua_ax_base_SpriteFrame_getRectInPixels); - tolua_function(tolua_S,"setRectInPixels",lua_ax_base_SpriteFrame_setRectInPixels); - tolua_function(tolua_S,"isRotated",lua_ax_base_SpriteFrame_isRotated); - tolua_function(tolua_S,"setRotated",lua_ax_base_SpriteFrame_setRotated); - tolua_function(tolua_S,"getRect",lua_ax_base_SpriteFrame_getRect); - tolua_function(tolua_S,"setRect",lua_ax_base_SpriteFrame_setRect); - tolua_function(tolua_S,"getCenterRect",lua_ax_base_SpriteFrame_getCenterRect); - tolua_function(tolua_S,"setCenterRectInPixels",lua_ax_base_SpriteFrame_setCenterRectInPixels); - tolua_function(tolua_S,"hasCenterRect",lua_ax_base_SpriteFrame_hasCenterRect); - tolua_function(tolua_S,"getOffsetInPixels",lua_ax_base_SpriteFrame_getOffsetInPixels); - tolua_function(tolua_S,"setOffsetInPixels",lua_ax_base_SpriteFrame_setOffsetInPixels); - tolua_function(tolua_S,"getOriginalSizeInPixels",lua_ax_base_SpriteFrame_getOriginalSizeInPixels); - tolua_function(tolua_S,"setOriginalSizeInPixels",lua_ax_base_SpriteFrame_setOriginalSizeInPixels); - tolua_function(tolua_S,"getOriginalSize",lua_ax_base_SpriteFrame_getOriginalSize); - tolua_function(tolua_S,"setOriginalSize",lua_ax_base_SpriteFrame_setOriginalSize); - tolua_function(tolua_S,"getTexture",lua_ax_base_SpriteFrame_getTexture); - tolua_function(tolua_S,"setTexture",lua_ax_base_SpriteFrame_setTexture); - tolua_function(tolua_S,"getOffset",lua_ax_base_SpriteFrame_getOffset); - tolua_function(tolua_S,"setOffset",lua_ax_base_SpriteFrame_setOffset); - tolua_function(tolua_S,"getAnchorPoint",lua_ax_base_SpriteFrame_getAnchorPoint); - tolua_function(tolua_S,"setAnchorPoint",lua_ax_base_SpriteFrame_setAnchorPoint); - tolua_function(tolua_S,"hasAnchorPoint",lua_ax_base_SpriteFrame_hasAnchorPoint); - tolua_function(tolua_S,"clone",lua_ax_base_SpriteFrame_clone); - tolua_function(tolua_S,"initWithTexture",lua_ax_base_SpriteFrame_initWithTexture); - tolua_function(tolua_S,"initWithTextureFilename",lua_ax_base_SpriteFrame_initWithTextureFilename); - tolua_function(tolua_S,"create", lua_ax_base_SpriteFrame_create); - tolua_function(tolua_S,"createWithTexture", lua_ax_base_SpriteFrame_createWithTexture); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::SpriteFrame).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.SpriteFrame"; - g_typeCast[typeName] = "ax.SpriteFrame"; - return 1; -} - -int lua_ax_base_AnimationFrame_getSpriteFrame(lua_State* tolua_S) +int lua_ax_base_GLView_setContentScaleFactor(lua_State* tolua_S) { int argc = 0; - ax::AnimationFrame* cobj = nullptr; + ax::GLView* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -23172,45 +24020,48 @@ int lua_ax_base_AnimationFrame_getSpriteFrame(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.AnimationFrame",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::AnimationFrame*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_AnimationFrame_getSpriteFrame'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_setContentScaleFactor'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + double arg0; + + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.GLView:setContentScaleFactor"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_AnimationFrame_getSpriteFrame'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_setContentScaleFactor'", nullptr); return 0; } - auto&& ret = cobj->getSpriteFrame(); - object_to_luaval(tolua_S, "ax.SpriteFrame",(ax::SpriteFrame*)ret); + auto&& ret = cobj->setContentScaleFactor(arg0); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.AnimationFrame:getSpriteFrame",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:setContentScaleFactor",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_AnimationFrame_getSpriteFrame'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_setContentScaleFactor'.",&tolua_err); #endif return 0; } -int lua_ax_base_AnimationFrame_setSpriteFrame(lua_State* tolua_S) +int lua_ax_base_GLView_getContentScaleFactor(lua_State* tolua_S) { int argc = 0; - ax::AnimationFrame* cobj = nullptr; + ax::GLView* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -23219,48 +24070,45 @@ int lua_ax_base_AnimationFrame_setSpriteFrame(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.AnimationFrame",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::AnimationFrame*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_AnimationFrame_setSpriteFrame'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_getContentScaleFactor'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - ax::SpriteFrame* arg0; - - ok &= luaval_to_object(tolua_S, 2, "ax.SpriteFrame",&arg0, "ax.AnimationFrame:setSpriteFrame"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_AnimationFrame_setSpriteFrame'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_getContentScaleFactor'", nullptr); return 0; } - cobj->setSpriteFrame(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getContentScaleFactor(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.AnimationFrame:setSpriteFrame",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:getContentScaleFactor",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_AnimationFrame_setSpriteFrame'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_getContentScaleFactor'.",&tolua_err); #endif return 0; } -int lua_ax_base_AnimationFrame_getDelayUnits(lua_State* tolua_S) +int lua_ax_base_GLView_isRetinaDisplay(lua_State* tolua_S) { int argc = 0; - ax::AnimationFrame* cobj = nullptr; + ax::GLView* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -23269,15 +24117,15 @@ int lua_ax_base_AnimationFrame_getDelayUnits(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.AnimationFrame",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::AnimationFrame*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_AnimationFrame_getDelayUnits'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_isRetinaDisplay'", nullptr); return 0; } #endif @@ -23287,27 +24135,27 @@ int lua_ax_base_AnimationFrame_getDelayUnits(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_AnimationFrame_getDelayUnits'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_isRetinaDisplay'", nullptr); return 0; } - auto&& ret = cobj->getDelayUnits(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + auto&& ret = cobj->isRetinaDisplay(); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.AnimationFrame:getDelayUnits",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:isRetinaDisplay",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_AnimationFrame_getDelayUnits'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_isRetinaDisplay'.",&tolua_err); #endif return 0; } -int lua_ax_base_AnimationFrame_setDelayUnits(lua_State* tolua_S) +int lua_ax_base_GLView_getVisibleSize(lua_State* tolua_S) { int argc = 0; - ax::AnimationFrame* cobj = nullptr; + ax::GLView* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -23316,95 +24164,92 @@ int lua_ax_base_AnimationFrame_setDelayUnits(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.AnimationFrame",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::AnimationFrame*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_AnimationFrame_setDelayUnits'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_getVisibleSize'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.AnimationFrame:setDelayUnits"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_AnimationFrame_setDelayUnits'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_getVisibleSize'", nullptr); return 0; } - cobj->setDelayUnits(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getVisibleSize(); + vec2_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.AnimationFrame:setDelayUnits",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:getVisibleSize",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_AnimationFrame_setDelayUnits'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_getVisibleSize'.",&tolua_err); #endif return 0; } -int lua_ax_base_AnimationFrame_getUserInfo(lua_State* tolua_S) +int lua_ax_base_GLView_getVisibleOrigin(lua_State* tolua_S) { int argc = 0; - ax::AnimationFrame* cobj = nullptr; + ax::GLView* cobj = nullptr; bool ok = true; + #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.AnimationFrame",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::AnimationFrame*)tolua_tousertype(tolua_S,1,0); + + cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); + #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_AnimationFrame_getUserInfo'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_getVisibleOrigin'", nullptr); return 0; } #endif + argc = lua_gettop(tolua_S)-1; - do{ - if (argc == 0) { - ax::ValueMap& ret = cobj->getUserInfo(); - ccvaluemap_to_luaval(tolua_S, ret); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 0) { - const ax::ValueMap& ret = cobj->getUserInfo(); - ccvaluemap_to_luaval(tolua_S, ret); - return 1; + if (argc == 0) + { + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_getVisibleOrigin'", nullptr); + return 0; } - }while(0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.AnimationFrame:getUserInfo",argc, 0); + auto&& ret = cobj->getVisibleOrigin(); + vec2_to_luaval(tolua_S, ret); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:getVisibleOrigin",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_AnimationFrame_getUserInfo'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_getVisibleOrigin'.",&tolua_err); #endif return 0; } -int lua_ax_base_AnimationFrame_setUserInfo(lua_State* tolua_S) +int lua_ax_base_GLView_getVisibleRect(lua_State* tolua_S) { int argc = 0; - ax::AnimationFrame* cobj = nullptr; + ax::GLView* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -23413,48 +24258,45 @@ int lua_ax_base_AnimationFrame_setUserInfo(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.AnimationFrame",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::AnimationFrame*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_AnimationFrame_setUserInfo'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_getVisibleRect'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - ax::ValueMap arg0; - - ok &= luaval_to_ccvaluemap(tolua_S, 2, &arg0, "ax.AnimationFrame:setUserInfo"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_AnimationFrame_setUserInfo'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_getVisibleRect'", nullptr); return 0; } - cobj->setUserInfo(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getVisibleRect(); + rect_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.AnimationFrame:setUserInfo",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:getVisibleRect",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_AnimationFrame_setUserInfo'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_getVisibleRect'.",&tolua_err); #endif return 0; } -int lua_ax_base_AnimationFrame_clone(lua_State* tolua_S) +int lua_ax_base_GLView_getSafeAreaRect(lua_State* tolua_S) { int argc = 0; - ax::AnimationFrame* cobj = nullptr; + ax::GLView* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -23463,15 +24305,15 @@ int lua_ax_base_AnimationFrame_clone(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.AnimationFrame",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::AnimationFrame*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_AnimationFrame_clone'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_getSafeAreaRect'", nullptr); return 0; } #endif @@ -23481,27 +24323,27 @@ int lua_ax_base_AnimationFrame_clone(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_AnimationFrame_clone'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_getSafeAreaRect'", nullptr); return 0; } - auto&& ret = cobj->clone(); - object_to_luaval(tolua_S, "ax.AnimationFrame",(ax::AnimationFrame*)ret); + auto&& ret = cobj->getSafeAreaRect(); + rect_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.AnimationFrame:clone",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:getSafeAreaRect",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_AnimationFrame_clone'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_getSafeAreaRect'.",&tolua_err); #endif return 0; } -int lua_ax_base_AnimationFrame_initWithSpriteFrame(lua_State* tolua_S) +int lua_ax_base_GLView_setDesignResolutionSize(lua_State* tolua_S) { int argc = 0; - ax::AnimationFrame* cobj = nullptr; + ax::GLView* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -23510,15 +24352,15 @@ int lua_ax_base_AnimationFrame_initWithSpriteFrame(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.AnimationFrame",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::AnimationFrame*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_AnimationFrame_initWithSpriteFrame'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_setDesignResolutionSize'", nullptr); return 0; } #endif @@ -23526,144 +24368,85 @@ int lua_ax_base_AnimationFrame_initWithSpriteFrame(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 3) { - ax::SpriteFrame* arg0; + double arg0; double arg1; - ax::ValueMap arg2; + ResolutionPolicy arg2; - ok &= luaval_to_object(tolua_S, 2, "ax.SpriteFrame",&arg0, "ax.AnimationFrame:initWithSpriteFrame"); + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.GLView:setDesignResolutionSize"); - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.AnimationFrame:initWithSpriteFrame"); + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.GLView:setDesignResolutionSize"); - ok &= luaval_to_ccvaluemap(tolua_S, 4, &arg2, "ax.AnimationFrame:initWithSpriteFrame"); + ok &= luaval_to_int32(tolua_S, 4,(int *)&arg2, "ax.GLView:setDesignResolutionSize"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_AnimationFrame_initWithSpriteFrame'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_setDesignResolutionSize'", nullptr); return 0; } - auto&& ret = cobj->initWithSpriteFrame(arg0, arg1, arg2); - tolua_pushboolean(tolua_S,(bool)ret); + cobj->setDesignResolutionSize(arg0, arg1, arg2); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.AnimationFrame:initWithSpriteFrame",argc, 3); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:setDesignResolutionSize",argc, 3); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_AnimationFrame_initWithSpriteFrame'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_setDesignResolutionSize'.",&tolua_err); #endif return 0; } -int lua_ax_base_AnimationFrame_create(lua_State* tolua_S) +int lua_ax_base_GLView_getDesignResolutionSize(lua_State* tolua_S) { int argc = 0; + ax::GLView* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.AnimationFrame",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S) - 1; + cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); - if (argc == 3) +#if _AX_DEBUG >= 1 + if (!cobj) { - ax::SpriteFrame* arg0; - double arg1; - ax::ValueMap arg2; - ok &= luaval_to_object(tolua_S, 2, "ax.SpriteFrame",&arg0, "ax.AnimationFrame:create"); - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.AnimationFrame:create"); - ok &= luaval_to_ccvaluemap(tolua_S, 4, &arg2, "ax.AnimationFrame:create"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_AnimationFrame_create'", nullptr); - return 0; - } - auto&& ret = ax::AnimationFrame::create(arg0, arg1, arg2); - object_to_luaval(tolua_S, "ax.AnimationFrame",(ax::AnimationFrame*)ret); - return 1; + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_getDesignResolutionSize'", nullptr); + return 0; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.AnimationFrame:create",argc, 3); - return 0; -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_AnimationFrame_create'.",&tolua_err); -#endif - return 0; -} -int lua_ax_base_AnimationFrame_constructor(lua_State* tolua_S) -{ - int argc = 0; - ax::AnimationFrame* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; #endif - - argc = lua_gettop(tolua_S)-1; if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_AnimationFrame_constructor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_getDesignResolutionSize'", nullptr); return 0; } - cobj = new ax::AnimationFrame(); - cobj->autorelease(); - int ID = (int)cobj->_ID ; - int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.AnimationFrame"); + auto&& ret = cobj->getDesignResolutionSize(); + vec2_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.AnimationFrame:AnimationFrame",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:getDesignResolutionSize",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_AnimationFrame_constructor'.",&tolua_err); + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_getDesignResolutionSize'.",&tolua_err); #endif return 0; } - -static int lua_ax_base_AnimationFrame_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (AnimationFrame)"); - return 0; -} - -int lua_register_ax_base_AnimationFrame(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.AnimationFrame"); - tolua_cclass(tolua_S,"AnimationFrame","ax.AnimationFrame","ax.Object",nullptr); - - tolua_beginmodule(tolua_S,"AnimationFrame"); - tolua_function(tolua_S,"new",lua_ax_base_AnimationFrame_constructor); - tolua_function(tolua_S,"getSpriteFrame",lua_ax_base_AnimationFrame_getSpriteFrame); - tolua_function(tolua_S,"setSpriteFrame",lua_ax_base_AnimationFrame_setSpriteFrame); - tolua_function(tolua_S,"getDelayUnits",lua_ax_base_AnimationFrame_getDelayUnits); - tolua_function(tolua_S,"setDelayUnits",lua_ax_base_AnimationFrame_setDelayUnits); - tolua_function(tolua_S,"getUserInfo",lua_ax_base_AnimationFrame_getUserInfo); - tolua_function(tolua_S,"setUserInfo",lua_ax_base_AnimationFrame_setUserInfo); - tolua_function(tolua_S,"clone",lua_ax_base_AnimationFrame_clone); - tolua_function(tolua_S,"initWithSpriteFrame",lua_ax_base_AnimationFrame_initWithSpriteFrame); - tolua_function(tolua_S,"create", lua_ax_base_AnimationFrame_create); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::AnimationFrame).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.AnimationFrame"; - g_typeCast[typeName] = "ax.AnimationFrame"; - return 1; -} - -int lua_ax_base_Animation_addSpriteFrame(lua_State* tolua_S) +int lua_ax_base_GLView_setViewPortInPoints(lua_State* tolua_S) { int argc = 0; - ax::Animation* cobj = nullptr; + ax::GLView* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -23672,48 +24455,57 @@ int lua_ax_base_Animation_addSpriteFrame(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Animation*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animation_addSpriteFrame'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_setViewPortInPoints'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 4) { - ax::SpriteFrame* arg0; + double arg0; + double arg1; + double arg2; + double arg3; - ok &= luaval_to_object(tolua_S, 2, "ax.SpriteFrame",&arg0, "ax.Animation:addSpriteFrame"); + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.GLView:setViewPortInPoints"); + + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.GLView:setViewPortInPoints"); + + ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.GLView:setViewPortInPoints"); + + ok &= luaval_to_number(tolua_S, 5,&arg3, "ax.GLView:setViewPortInPoints"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_addSpriteFrame'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_setViewPortInPoints'", nullptr); return 0; } - cobj->addSpriteFrame(arg0); + cobj->setViewPortInPoints(arg0, arg1, arg2, arg3); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animation:addSpriteFrame",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:setViewPortInPoints",argc, 4); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_addSpriteFrame'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_setViewPortInPoints'.",&tolua_err); #endif return 0; } -int lua_ax_base_Animation_addSpriteFrameWithFile(lua_State* tolua_S) +int lua_ax_base_GLView_setScissorInPoints(lua_State* tolua_S) { int argc = 0; - ax::Animation* cobj = nullptr; + ax::GLView* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -23722,48 +24514,57 @@ int lua_ax_base_Animation_addSpriteFrameWithFile(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Animation*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animation_addSpriteFrameWithFile'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_setScissorInPoints'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 4) { - std::string_view arg0; + double arg0; + double arg1; + double arg2; + double arg3; - ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Animation:addSpriteFrameWithFile"); + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.GLView:setScissorInPoints"); + + ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.GLView:setScissorInPoints"); + + ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.GLView:setScissorInPoints"); + + ok &= luaval_to_number(tolua_S, 5,&arg3, "ax.GLView:setScissorInPoints"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_addSpriteFrameWithFile'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_setScissorInPoints'", nullptr); return 0; } - cobj->addSpriteFrameWithFile(arg0); + cobj->setScissorInPoints(arg0, arg1, arg2, arg3); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animation:addSpriteFrameWithFile",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:setScissorInPoints",argc, 4); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_addSpriteFrameWithFile'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_setScissorInPoints'.",&tolua_err); #endif return 0; } -int lua_ax_base_Animation_addSpriteFrameWithTexture(lua_State* tolua_S) +int lua_ax_base_GLView_isScissorEnabled(lua_State* tolua_S) { int argc = 0; - ax::Animation* cobj = nullptr; + ax::GLView* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -23772,51 +24573,45 @@ int lua_ax_base_Animation_addSpriteFrameWithTexture(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Animation*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animation_addSpriteFrameWithTexture'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_isScissorEnabled'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 2) + if (argc == 0) { - ax::Texture2D* arg0; - ax::Rect arg1; - - ok &= luaval_to_object(tolua_S, 2, "ax.Texture2D",&arg0, "ax.Animation:addSpriteFrameWithTexture"); - - ok &= luaval_to_rect(tolua_S, 3, &arg1, "ax.Animation:addSpriteFrameWithTexture"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_addSpriteFrameWithTexture'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_isScissorEnabled'", nullptr); return 0; } - cobj->addSpriteFrameWithTexture(arg0, arg1); - lua_settop(tolua_S, 1); + auto&& ret = cobj->isScissorEnabled(); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animation:addSpriteFrameWithTexture",argc, 2); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:isScissorEnabled",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_addSpriteFrameWithTexture'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_isScissorEnabled'.",&tolua_err); #endif return 0; } -int lua_ax_base_Animation_getTotalDelayUnits(lua_State* tolua_S) +int lua_ax_base_GLView_getScissorRect(lua_State* tolua_S) { int argc = 0; - ax::Animation* cobj = nullptr; + ax::GLView* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -23825,15 +24620,15 @@ int lua_ax_base_Animation_getTotalDelayUnits(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Animation*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animation_getTotalDelayUnits'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_getScissorRect'", nullptr); return 0; } #endif @@ -23843,27 +24638,27 @@ int lua_ax_base_Animation_getTotalDelayUnits(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_getTotalDelayUnits'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_getScissorRect'", nullptr); return 0; } - auto&& ret = cobj->getTotalDelayUnits(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + auto&& ret = cobj->getScissorRect(); + rect_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animation:getTotalDelayUnits",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:getScissorRect",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_getTotalDelayUnits'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_getScissorRect'.",&tolua_err); #endif return 0; } -int lua_ax_base_Animation_setDelayPerUnit(lua_State* tolua_S) +int lua_ax_base_GLView_setViewName(lua_State* tolua_S) { int argc = 0; - ax::Animation* cobj = nullptr; + ax::GLView* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -23872,15 +24667,15 @@ int lua_ax_base_Animation_setDelayPerUnit(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Animation*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animation_setDelayPerUnit'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_setViewName'", nullptr); return 0; } #endif @@ -23888,32 +24683,32 @@ int lua_ax_base_Animation_setDelayPerUnit(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - double arg0; + std::string_view arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Animation:setDelayPerUnit"); + ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.GLView:setViewName"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_setDelayPerUnit'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_setViewName'", nullptr); return 0; } - cobj->setDelayPerUnit(arg0); + cobj->setViewName(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animation:setDelayPerUnit",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:setViewName",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_setDelayPerUnit'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_setViewName'.",&tolua_err); #endif return 0; } -int lua_ax_base_Animation_getDelayPerUnit(lua_State* tolua_S) +int lua_ax_base_GLView_getViewName(lua_State* tolua_S) { int argc = 0; - ax::Animation* cobj = nullptr; + ax::GLView* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -23922,15 +24717,15 @@ int lua_ax_base_Animation_getDelayPerUnit(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Animation*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animation_getDelayPerUnit'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_getViewName'", nullptr); return 0; } #endif @@ -23940,74 +24735,82 @@ int lua_ax_base_Animation_getDelayPerUnit(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_getDelayPerUnit'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_getViewName'", nullptr); return 0; } - auto&& ret = cobj->getDelayPerUnit(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + auto&& ret = cobj->getViewName(); + lua_pushlstring(tolua_S,ret.data(),ret.length()); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animation:getDelayPerUnit",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:getViewName",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_getDelayPerUnit'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_getViewName'.",&tolua_err); #endif return 0; } -int lua_ax_base_Animation_getDuration(lua_State* tolua_S) +int lua_ax_base_GLView_setIcon(lua_State* tolua_S) { int argc = 0; - ax::Animation* cobj = nullptr; + ax::GLView* cobj = nullptr; bool ok = true; - #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; #endif - - cobj = (ax::Animation*)tolua_tousertype(tolua_S,1,0); - + cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animation_getDuration'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_setIcon'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_getDuration'", nullptr); - return 0; + do{ + if (argc == 1) { + std::vector arg0; + ok &= luaval_to_std_vector_string_view(tolua_S, 2, &arg0, "ax.GLView:setIcon"); + + if (!ok) { break; } + cobj->setIcon(arg0); + lua_settop(tolua_S, 1); + return 1; } - auto&& ret = cobj->getDuration(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animation:getDuration",argc, 0); + }while(0); + ok = true; + do{ + if (argc == 1) { + std::string_view arg0; + ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.GLView:setIcon"); + + if (!ok) { break; } + cobj->setIcon(arg0); + lua_settop(tolua_S, 1); + return 1; + } + }while(0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:setIcon",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_getDuration'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_setIcon'.",&tolua_err); #endif return 0; } -int lua_ax_base_Animation_getFrames(lua_State* tolua_S) +int lua_ax_base_GLView_setDefaultIcon(lua_State* tolua_S) { int argc = 0; - ax::Animation* cobj = nullptr; + ax::GLView* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -24016,15 +24819,15 @@ int lua_ax_base_Animation_getFrames(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Animation*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animation_getFrames'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_setDefaultIcon'", nullptr); return 0; } #endif @@ -24034,27 +24837,27 @@ int lua_ax_base_Animation_getFrames(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_getFrames'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_setDefaultIcon'", nullptr); return 0; } - auto&& ret = cobj->getFrames(); - ccvector_to_luaval(tolua_S, ret); + cobj->setDefaultIcon(); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animation:getFrames",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:setDefaultIcon",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_getFrames'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_setDefaultIcon'.",&tolua_err); #endif return 0; } -int lua_ax_base_Animation_setFrames(lua_State* tolua_S) +int lua_ax_base_GLView_getViewPortRect(lua_State* tolua_S) { int argc = 0; - ax::Animation* cobj = nullptr; + ax::GLView* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -24063,48 +24866,45 @@ int lua_ax_base_Animation_setFrames(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Animation*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animation_setFrames'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_getViewPortRect'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - ax::Vector arg0; - - ok &= luaval_to_ccvector(tolua_S, 2, &arg0, "ax.Animation:setFrames"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_setFrames'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_getViewPortRect'", nullptr); return 0; } - cobj->setFrames(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getViewPortRect(); + rect_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animation:setFrames",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:getViewPortRect",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_setFrames'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_getViewPortRect'.",&tolua_err); #endif return 0; } -int lua_ax_base_Animation_getRestoreOriginalFrame(lua_State* tolua_S) +int lua_ax_base_GLView_getScaleX(lua_State* tolua_S) { int argc = 0; - ax::Animation* cobj = nullptr; + ax::GLView* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -24113,15 +24913,15 @@ int lua_ax_base_Animation_getRestoreOriginalFrame(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Animation*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animation_getRestoreOriginalFrame'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_getScaleX'", nullptr); return 0; } #endif @@ -24131,27 +24931,27 @@ int lua_ax_base_Animation_getRestoreOriginalFrame(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_getRestoreOriginalFrame'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_getScaleX'", nullptr); return 0; } - auto&& ret = cobj->getRestoreOriginalFrame(); - tolua_pushboolean(tolua_S,(bool)ret); + auto&& ret = cobj->getScaleX(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animation:getRestoreOriginalFrame",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:getScaleX",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_getRestoreOriginalFrame'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_getScaleX'.",&tolua_err); #endif return 0; } -int lua_ax_base_Animation_setRestoreOriginalFrame(lua_State* tolua_S) +int lua_ax_base_GLView_getScaleY(lua_State* tolua_S) { int argc = 0; - ax::Animation* cobj = nullptr; + ax::GLView* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -24160,48 +24960,45 @@ int lua_ax_base_Animation_setRestoreOriginalFrame(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Animation*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animation_setRestoreOriginalFrame'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_getScaleY'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - bool arg0; - - ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.Animation:setRestoreOriginalFrame"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_setRestoreOriginalFrame'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_getScaleY'", nullptr); return 0; } - cobj->setRestoreOriginalFrame(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getScaleY(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animation:setRestoreOriginalFrame",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:getScaleY",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_setRestoreOriginalFrame'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_getScaleY'.",&tolua_err); #endif return 0; } -int lua_ax_base_Animation_getLoops(lua_State* tolua_S) +int lua_ax_base_GLView_getResolutionPolicy(lua_State* tolua_S) { int argc = 0; - ax::Animation* cobj = nullptr; + ax::GLView* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -24210,15 +25007,15 @@ int lua_ax_base_Animation_getLoops(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Animation*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animation_getLoops'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_getResolutionPolicy'", nullptr); return 0; } #endif @@ -24228,27 +25025,27 @@ int lua_ax_base_Animation_getLoops(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_getLoops'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_getResolutionPolicy'", nullptr); return 0; } - auto&& ret = cobj->getLoops(); + int ret = (int)cobj->getResolutionPolicy(); tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animation:getLoops",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:getResolutionPolicy",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_getLoops'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_getResolutionPolicy'.",&tolua_err); #endif return 0; } -int lua_ax_base_Animation_setLoops(lua_State* tolua_S) +int lua_ax_base_GLView_renderScene(lua_State* tolua_S) { int argc = 0; - ax::Animation* cobj = nullptr; + ax::GLView* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -24257,95 +25054,177 @@ int lua_ax_base_Animation_setLoops(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Animation*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::GLView*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animation_setLoops'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_GLView_renderScene'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 2) { - unsigned int arg0; + ax::Scene* arg0; + ax::Renderer* arg1; - ok &= luaval_to_uint32(tolua_S, 2,&arg0, "ax.Animation:setLoops"); + ok &= luaval_to_object(tolua_S, 2, "ax.Scene",&arg0, "ax.GLView:renderScene"); + + ok &= luaval_to_object(tolua_S, 3, "ax.Renderer",&arg1, "ax.GLView:renderScene"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_setLoops'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_renderScene'", nullptr); return 0; } - cobj->setLoops(arg0); + cobj->renderScene(arg0, arg1); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animation:setLoops",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.GLView:renderScene",argc, 2); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_setLoops'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_renderScene'.",&tolua_err); #endif return 0; } -int lua_ax_base_Animation_clone(lua_State* tolua_S) +int lua_ax_base_GLView_setGLContextAttrs(lua_State* tolua_S) { int argc = 0; - ax::Animation* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Animation*)tolua_tousertype(tolua_S,1,0); + argc = lua_gettop(tolua_S) - 1; -#if _AX_DEBUG >= 1 - if (!cobj) + if (argc == 1) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animation_clone'", nullptr); - return 0; + GLContextAttrs arg0; + #pragma warning NO CONVERSION TO NATIVE FOR GLContextAttrs + ok = false; + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_setGLContextAttrs'", nullptr); + return 0; + } + ax::GLView::setGLContextAttrs(arg0); + lua_settop(tolua_S, 1); + return 1; } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.GLView:setGLContextAttrs",argc, 1); + return 0; +#if _AX_DEBUG >= 1 + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_setGLContextAttrs'.",&tolua_err); #endif + return 0; +} +int lua_ax_base_GLView_getGLContextAttrs(lua_State* tolua_S) +{ + int argc = 0; + bool ok = true; - argc = lua_gettop(tolua_S)-1; - if (argc == 0) +#if _AX_DEBUG >= 1 + tolua_Error tolua_err; +#endif + +#if _AX_DEBUG >= 1 + if (!tolua_isusertable(tolua_S,1,"ax.GLView",0,&tolua_err)) goto tolua_lerror; +#endif + + argc = lua_gettop(tolua_S) - 1; + + if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_clone'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_GLView_getGLContextAttrs'", nullptr); return 0; } - auto&& ret = cobj->clone(); - object_to_luaval(tolua_S, "ax.Animation",(ax::Animation*)ret); + auto&& ret = ax::GLView::getGLContextAttrs(); + #pragma warning NO CONVERSION FROM NATIVE FOR GLContextAttrs; return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animation:clone",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.GLView:getGLContextAttrs",argc, 0); return 0; - #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_clone'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_GLView_getGLContextAttrs'.",&tolua_err); #endif - return 0; } -int lua_ax_base_Animation_init(lua_State* tolua_S) +static int lua_ax_base_GLView_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (GLView)"); + return 0; +} + +int lua_register_ax_base_GLView(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.GLView"); + tolua_cclass(tolua_S,"GLView","ax.GLView","ax.Object",nullptr); + + tolua_beginmodule(tolua_S,"GLView"); + tolua_function(tolua_S,"endToLua",lua_ax_base_GLView_end); + tolua_function(tolua_S,"isOpenGLReady",lua_ax_base_GLView_isOpenGLReady); + tolua_function(tolua_S,"swapBuffers",lua_ax_base_GLView_swapBuffers); + tolua_function(tolua_S,"setIMEKeyboardState",lua_ax_base_GLView_setIMEKeyboardState); + tolua_function(tolua_S,"windowShouldClose",lua_ax_base_GLView_windowShouldClose); + tolua_function(tolua_S,"pollEvents",lua_ax_base_GLView_pollEvents); + tolua_function(tolua_S,"getFrameSize",lua_ax_base_GLView_getFrameSize); + tolua_function(tolua_S,"setFrameSize",lua_ax_base_GLView_setFrameSize); + tolua_function(tolua_S,"setFrameZoomFactor",lua_ax_base_GLView_setFrameZoomFactor); + tolua_function(tolua_S,"getFrameZoomFactor",lua_ax_base_GLView_getFrameZoomFactor); + tolua_function(tolua_S,"setCursorVisible",lua_ax_base_GLView_setCursorVisible); + tolua_function(tolua_S,"getRetinaFactor",lua_ax_base_GLView_getRetinaFactor); + tolua_function(tolua_S,"setContentScaleFactor",lua_ax_base_GLView_setContentScaleFactor); + tolua_function(tolua_S,"getContentScaleFactor",lua_ax_base_GLView_getContentScaleFactor); + tolua_function(tolua_S,"isRetinaDisplay",lua_ax_base_GLView_isRetinaDisplay); + tolua_function(tolua_S,"getVisibleSize",lua_ax_base_GLView_getVisibleSize); + tolua_function(tolua_S,"getVisibleOrigin",lua_ax_base_GLView_getVisibleOrigin); + tolua_function(tolua_S,"getVisibleRect",lua_ax_base_GLView_getVisibleRect); + tolua_function(tolua_S,"getSafeAreaRect",lua_ax_base_GLView_getSafeAreaRect); + tolua_function(tolua_S,"setDesignResolutionSize",lua_ax_base_GLView_setDesignResolutionSize); + tolua_function(tolua_S,"getDesignResolutionSize",lua_ax_base_GLView_getDesignResolutionSize); + tolua_function(tolua_S,"setViewPortInPoints",lua_ax_base_GLView_setViewPortInPoints); + tolua_function(tolua_S,"setScissorInPoints",lua_ax_base_GLView_setScissorInPoints); + tolua_function(tolua_S,"isScissorEnabled",lua_ax_base_GLView_isScissorEnabled); + tolua_function(tolua_S,"getScissorRect",lua_ax_base_GLView_getScissorRect); + tolua_function(tolua_S,"setViewName",lua_ax_base_GLView_setViewName); + tolua_function(tolua_S,"getViewName",lua_ax_base_GLView_getViewName); + tolua_function(tolua_S,"setIcon",lua_ax_base_GLView_setIcon); + tolua_function(tolua_S,"setDefaultIcon",lua_ax_base_GLView_setDefaultIcon); + tolua_function(tolua_S,"getViewPortRect",lua_ax_base_GLView_getViewPortRect); + tolua_function(tolua_S,"getScaleX",lua_ax_base_GLView_getScaleX); + tolua_function(tolua_S,"getScaleY",lua_ax_base_GLView_getScaleY); + tolua_function(tolua_S,"getResolutionPolicy",lua_ax_base_GLView_getResolutionPolicy); + tolua_function(tolua_S,"renderScene",lua_ax_base_GLView_renderScene); + tolua_function(tolua_S,"setGLContextAttrs", lua_ax_base_GLView_setGLContextAttrs); + tolua_function(tolua_S,"getGLContextAttrs", lua_ax_base_GLView_getGLContextAttrs); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::GLView).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.GLView"; + g_typeCast[typeName] = "ax.GLView"; + return 1; +} + +int lua_ax_base_Director_init(lua_State* tolua_S) { int argc = 0; - ax::Animation* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -24354,15 +25233,15 @@ int lua_ax_base_Animation_init(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Animation*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animation_init'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_init'", nullptr); return 0; } #endif @@ -24372,27 +25251,27 @@ int lua_ax_base_Animation_init(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_init'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_init'", nullptr); return 0; } auto&& ret = cobj->init(); tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animation:init",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:init",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_init'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_init'.",&tolua_err); #endif return 0; } -int lua_ax_base_Animation_initWithSpriteFrames(lua_State* tolua_S) +int lua_ax_base_Director_getRunningScene(lua_State* tolua_S) { int argc = 0; - ax::Animation* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -24401,85 +25280,92 @@ int lua_ax_base_Animation_initWithSpriteFrames(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Animation*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animation_initWithSpriteFrames'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getRunningScene'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - ax::Vector arg0; - - ok &= luaval_to_ccvector(tolua_S, 2, &arg0, "ax.Animation:initWithSpriteFrames"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_initWithSpriteFrames'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getRunningScene'", nullptr); return 0; } - auto&& ret = cobj->initWithSpriteFrames(arg0); - tolua_pushboolean(tolua_S,(bool)ret); + auto&& ret = cobj->getRunningScene(); + object_to_luaval(tolua_S, "ax.Scene",(ax::Scene*)ret); return 1; } - if (argc == 2) - { - ax::Vector arg0; - double arg1; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getRunningScene",argc, 0); + return 0; - ok &= luaval_to_ccvector(tolua_S, 2, &arg0, "ax.Animation:initWithSpriteFrames"); +#if _AX_DEBUG >= 1 + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getRunningScene'.",&tolua_err); +#endif - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Animation:initWithSpriteFrames"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_initWithSpriteFrames'", nullptr); - return 0; - } - auto&& ret = cobj->initWithSpriteFrames(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - if (argc == 3) - { - ax::Vector arg0; - double arg1; - unsigned int arg2; + return 0; +} +int lua_ax_base_Director_getNextScene(lua_State* tolua_S) +{ + int argc = 0; + ax::Director* cobj = nullptr; + bool ok = true; - ok &= luaval_to_ccvector(tolua_S, 2, &arg0, "ax.Animation:initWithSpriteFrames"); +#if _AX_DEBUG >= 1 + tolua_Error tolua_err; +#endif - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Animation:initWithSpriteFrames"); - ok &= luaval_to_uint32(tolua_S, 4,&arg2, "ax.Animation:initWithSpriteFrames"); +#if _AX_DEBUG >= 1 + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; +#endif + + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getNextScene'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 0) + { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_initWithSpriteFrames'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getNextScene'", nullptr); return 0; } - auto&& ret = cobj->initWithSpriteFrames(arg0, arg1, arg2); - tolua_pushboolean(tolua_S,(bool)ret); + auto&& ret = cobj->getNextScene(); + object_to_luaval(tolua_S, "ax.Scene",(ax::Scene*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animation:initWithSpriteFrames",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getNextScene",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_initWithSpriteFrames'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getNextScene'.",&tolua_err); #endif return 0; } -int lua_ax_base_Animation_initWithAnimationFrames(lua_State* tolua_S) +int lua_ax_base_Director_getAnimationInterval(lua_State* tolua_S) { int argc = 0; - ax::Animation* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -24488,189 +25374,142 @@ int lua_ax_base_Animation_initWithAnimationFrames(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Animation*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animation_initWithAnimationFrames'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getAnimationInterval'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 3) + if (argc == 0) { - ax::Vector arg0; - double arg1; - unsigned int arg2; - - ok &= luaval_to_ccvector(tolua_S, 2, &arg0, "ax.Animation:initWithAnimationFrames"); - - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Animation:initWithAnimationFrames"); - - ok &= luaval_to_uint32(tolua_S, 4,&arg2, "ax.Animation:initWithAnimationFrames"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_initWithAnimationFrames'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getAnimationInterval'", nullptr); return 0; } - auto&& ret = cobj->initWithAnimationFrames(arg0, arg1, arg2); - tolua_pushboolean(tolua_S,(bool)ret); + auto&& ret = cobj->getAnimationInterval(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animation:initWithAnimationFrames",argc, 3); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getAnimationInterval",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_initWithAnimationFrames'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getAnimationInterval'.",&tolua_err); #endif return 0; } -int lua_ax_base_Animation_create(lua_State* tolua_S) +int lua_ax_base_Director_setAnimationInterval(lua_State* tolua_S) { int argc = 0; + ax::Director* cobj = nullptr; bool ok = true; + #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S)-1; + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); - do +#if _AX_DEBUG >= 1 + if (!cobj) { - if (argc == 2) - { - ax::Vector arg0; - ok &= luaval_to_ccvector(tolua_S, 2, &arg0, "ax.Animation:create"); - if (!ok) { break; } - double arg1; - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Animation:create"); - if (!ok) { break; } - ax::Animation* ret = ax::Animation::create(arg0, arg1); - object_to_luaval(tolua_S, "ax.Animation",(ax::Animation*)ret); - return 1; - } - } while (0); - ok = true; - do - { - if (argc == 3) - { - ax::Vector arg0; - ok &= luaval_to_ccvector(tolua_S, 2, &arg0, "ax.Animation:create"); - if (!ok) { break; } - double arg1; - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Animation:create"); - if (!ok) { break; } - unsigned int arg2; - ok &= luaval_to_uint32(tolua_S, 4,&arg2, "ax.Animation:create"); - if (!ok) { break; } - ax::Animation* ret = ax::Animation::create(arg0, arg1, arg2); - object_to_luaval(tolua_S, "ax.Animation",(ax::Animation*)ret); - return 1; - } - } while (0); - ok = true; - do + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_setAnimationInterval'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 1) { - if (argc == 0) + double arg0; + + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Director:setAnimationInterval"); + if(!ok) { - ax::Animation* ret = ax::Animation::create(); - object_to_luaval(tolua_S, "ax.Animation",(ax::Animation*)ret); - return 1; + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_setAnimationInterval'", nullptr); + return 0; } - } while (0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d", "ax.Animation:create",argc, 0); + cobj->setAnimationInterval(arg0); + lua_settop(tolua_S, 1); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:setAnimationInterval",argc, 1); return 0; + #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_create'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_setAnimationInterval'.",&tolua_err); #endif + return 0; } -int lua_ax_base_Animation_createWithSpriteFrames(lua_State* tolua_S) +int lua_ax_base_Director_isStatsDisplay(lua_State* tolua_S) { int argc = 0; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.Animation",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S) - 1; + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); - if (argc == 1) - { - ax::Vector arg0; - ok &= luaval_to_ccvector(tolua_S, 2, &arg0, "ax.Animation:createWithSpriteFrames"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_createWithSpriteFrames'", nullptr); - return 0; - } - auto&& ret = ax::Animation::createWithSpriteFrames(arg0); - object_to_luaval(tolua_S, "ax.Animation",(ax::Animation*)ret); - return 1; - } - if (argc == 2) +#if _AX_DEBUG >= 1 + if (!cobj) { - ax::Vector arg0; - double arg1; - ok &= luaval_to_ccvector(tolua_S, 2, &arg0, "ax.Animation:createWithSpriteFrames"); - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Animation:createWithSpriteFrames"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_createWithSpriteFrames'", nullptr); - return 0; - } - auto&& ret = ax::Animation::createWithSpriteFrames(arg0, arg1); - object_to_luaval(tolua_S, "ax.Animation",(ax::Animation*)ret); - return 1; + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_isStatsDisplay'", nullptr); + return 0; } - if (argc == 3) +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 0) { - ax::Vector arg0; - double arg1; - unsigned int arg2; - ok &= luaval_to_ccvector(tolua_S, 2, &arg0, "ax.Animation:createWithSpriteFrames"); - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.Animation:createWithSpriteFrames"); - ok &= luaval_to_uint32(tolua_S, 4,&arg2, "ax.Animation:createWithSpriteFrames"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_createWithSpriteFrames'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_isStatsDisplay'", nullptr); return 0; } - auto&& ret = ax::Animation::createWithSpriteFrames(arg0, arg1, arg2); - object_to_luaval(tolua_S, "ax.Animation",(ax::Animation*)ret); + auto&& ret = cobj->isStatsDisplay(); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Animation:createWithSpriteFrames",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:isStatsDisplay",argc, 0); return 0; + #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_createWithSpriteFrames'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_isStatsDisplay'.",&tolua_err); #endif + return 0; } -int lua_ax_base_Animation_constructor(lua_State* tolua_S) +int lua_ax_base_Director_setStatsDisplay(lua_State* tolua_S) { int argc = 0; - ax::Animation* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -24678,75 +25517,49 @@ int lua_ax_base_Animation_constructor(lua_State* tolua_S) #endif +#if _AX_DEBUG >= 1 + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; +#endif + + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_setStatsDisplay'", nullptr); + return 0; + } +#endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + bool arg0; + + ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.Director:setStatsDisplay"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animation_constructor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_setStatsDisplay'", nullptr); return 0; } - cobj = new ax::Animation(); - cobj->autorelease(); - int ID = (int)cobj->_ID ; - int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.Animation"); + cobj->setStatsDisplay(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animation:Animation",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:setStatsDisplay",argc, 1); return 0; #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animation_constructor'.",&tolua_err); + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_setStatsDisplay'.",&tolua_err); #endif return 0; } - -static int lua_ax_base_Animation_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (Animation)"); - return 0; -} - -int lua_register_ax_base_Animation(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.Animation"); - tolua_cclass(tolua_S,"Animation","ax.Animation","ax.Object",nullptr); - - tolua_beginmodule(tolua_S,"Animation"); - tolua_function(tolua_S,"new",lua_ax_base_Animation_constructor); - tolua_function(tolua_S,"addSpriteFrame",lua_ax_base_Animation_addSpriteFrame); - tolua_function(tolua_S,"addSpriteFrameWithFile",lua_ax_base_Animation_addSpriteFrameWithFile); - tolua_function(tolua_S,"addSpriteFrameWithTexture",lua_ax_base_Animation_addSpriteFrameWithTexture); - tolua_function(tolua_S,"getTotalDelayUnits",lua_ax_base_Animation_getTotalDelayUnits); - tolua_function(tolua_S,"setDelayPerUnit",lua_ax_base_Animation_setDelayPerUnit); - tolua_function(tolua_S,"getDelayPerUnit",lua_ax_base_Animation_getDelayPerUnit); - tolua_function(tolua_S,"getDuration",lua_ax_base_Animation_getDuration); - tolua_function(tolua_S,"getFrames",lua_ax_base_Animation_getFrames); - tolua_function(tolua_S,"setFrames",lua_ax_base_Animation_setFrames); - tolua_function(tolua_S,"getRestoreOriginalFrame",lua_ax_base_Animation_getRestoreOriginalFrame); - tolua_function(tolua_S,"setRestoreOriginalFrame",lua_ax_base_Animation_setRestoreOriginalFrame); - tolua_function(tolua_S,"getLoops",lua_ax_base_Animation_getLoops); - tolua_function(tolua_S,"setLoops",lua_ax_base_Animation_setLoops); - tolua_function(tolua_S,"clone",lua_ax_base_Animation_clone); - tolua_function(tolua_S,"init",lua_ax_base_Animation_init); - tolua_function(tolua_S,"initWithSpriteFrames",lua_ax_base_Animation_initWithSpriteFrames); - tolua_function(tolua_S,"initWithAnimationFrames",lua_ax_base_Animation_initWithAnimationFrames); - tolua_function(tolua_S,"create", lua_ax_base_Animation_create); - tolua_function(tolua_S,"createWithSpriteFrames", lua_ax_base_Animation_createWithSpriteFrames); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::Animation).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.Animation"; - g_typeCast[typeName] = "ax.Animation"; - return 1; -} - -int lua_ax_base_ActionInterval_getElapsed(lua_State* tolua_S) +int lua_ax_base_Director_getSecondsPerFrame(lua_State* tolua_S) { int argc = 0; - ax::ActionInterval* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -24755,15 +25568,15 @@ int lua_ax_base_ActionInterval_getElapsed(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.ActionInterval",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::ActionInterval*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_ActionInterval_getElapsed'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getSecondsPerFrame'", nullptr); return 0; } #endif @@ -24773,27 +25586,27 @@ int lua_ax_base_ActionInterval_getElapsed(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_ActionInterval_getElapsed'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getSecondsPerFrame'", nullptr); return 0; } - auto&& ret = cobj->getElapsed(); + auto&& ret = cobj->getSecondsPerFrame(); tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.ActionInterval:getElapsed",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getSecondsPerFrame",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_ActionInterval_getElapsed'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getSecondsPerFrame'.",&tolua_err); #endif return 0; } -int lua_ax_base_ActionInterval_setAmplitudeRate(lua_State* tolua_S) +int lua_ax_base_Director_setStatsAnchor(lua_State* tolua_S) { int argc = 0; - ax::ActionInterval* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -24802,48 +25615,59 @@ int lua_ax_base_ActionInterval_setAmplitudeRate(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.ActionInterval",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::ActionInterval*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_ActionInterval_setAmplitudeRate'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_setStatsAnchor'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; + if (argc == 0) + { + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_setStatsAnchor'", nullptr); + return 0; + } + cobj->setStatsAnchor(); + lua_settop(tolua_S, 1); + return 1; + } if (argc == 1) { - double arg0; + ax::AnchorPreset arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.ActionInterval:setAmplitudeRate"); + ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Director:setStatsAnchor"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_ActionInterval_setAmplitudeRate'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_setStatsAnchor'", nullptr); return 0; } - cobj->setAmplitudeRate(arg0); + cobj->setStatsAnchor(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.ActionInterval:setAmplitudeRate",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:setStatsAnchor",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_ActionInterval_setAmplitudeRate'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_setStatsAnchor'.",&tolua_err); #endif return 0; } -int lua_ax_base_ActionInterval_getAmplitudeRate(lua_State* tolua_S) +int lua_ax_base_Director_getGLView(lua_State* tolua_S) { int argc = 0; - ax::ActionInterval* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -24852,15 +25676,15 @@ int lua_ax_base_ActionInterval_getAmplitudeRate(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.ActionInterval",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::ActionInterval*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_ActionInterval_getAmplitudeRate'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getGLView'", nullptr); return 0; } #endif @@ -24870,27 +25694,27 @@ int lua_ax_base_ActionInterval_getAmplitudeRate(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_ActionInterval_getAmplitudeRate'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getGLView'", nullptr); return 0; } - auto&& ret = cobj->getAmplitudeRate(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + auto&& ret = cobj->getGLView(); + object_to_luaval(tolua_S, "ax.GLView",(ax::GLView*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.ActionInterval:getAmplitudeRate",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getGLView",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_ActionInterval_getAmplitudeRate'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getGLView'.",&tolua_err); #endif return 0; } -int lua_ax_base_ActionInterval_initWithDuration(lua_State* tolua_S) +int lua_ax_base_Director_setGLView(lua_State* tolua_S) { int argc = 0; - ax::ActionInterval* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -24899,15 +25723,15 @@ int lua_ax_base_ActionInterval_initWithDuration(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.ActionInterval",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::ActionInterval*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_ActionInterval_initWithDuration'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_setGLView'", nullptr); return 0; } #endif @@ -24915,55 +25739,32 @@ int lua_ax_base_ActionInterval_initWithDuration(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - double arg0; + ax::GLView* arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.ActionInterval:initWithDuration"); + ok &= luaval_to_object(tolua_S, 2, "ax.GLView",&arg0, "ax.Director:setGLView"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_ActionInterval_initWithDuration'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_setGLView'", nullptr); return 0; } - auto&& ret = cobj->initWithDuration(arg0); - tolua_pushboolean(tolua_S,(bool)ret); + cobj->setGLView(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.ActionInterval:initWithDuration",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:setGLView",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_ActionInterval_initWithDuration'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_setGLView'.",&tolua_err); #endif return 0; } -static int lua_ax_base_ActionInterval_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (ActionInterval)"); - return 0; -} - -int lua_register_ax_base_ActionInterval(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.ActionInterval"); - tolua_cclass(tolua_S,"ActionInterval","ax.ActionInterval","ax.FiniteTimeAction",nullptr); - - tolua_beginmodule(tolua_S,"ActionInterval"); - tolua_function(tolua_S,"getElapsed",lua_ax_base_ActionInterval_getElapsed); - tolua_function(tolua_S,"setAmplitudeRate",lua_ax_base_ActionInterval_setAmplitudeRate); - tolua_function(tolua_S,"getAmplitudeRate",lua_ax_base_ActionInterval_getAmplitudeRate); - tolua_function(tolua_S,"initWithDuration",lua_ax_base_ActionInterval_initWithDuration); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::ActionInterval).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.ActionInterval"; - g_typeCast[typeName] = "ax.ActionInterval"; - return 1; -} - -int lua_ax_base_Sequence_initWithTwoActions(lua_State* tolua_S) +int lua_ax_base_Director_getTextureCache(lua_State* tolua_S) { int argc = 0; - ax::Sequence* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -24972,51 +25773,45 @@ int lua_ax_base_Sequence_initWithTwoActions(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Sequence",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Sequence*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Sequence_initWithTwoActions'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getTextureCache'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 2) + if (argc == 0) { - ax::FiniteTimeAction* arg0; - ax::FiniteTimeAction* arg1; - - ok &= luaval_to_object(tolua_S, 2, "ax.FiniteTimeAction",&arg0, "ax.Sequence:initWithTwoActions"); - - ok &= luaval_to_object(tolua_S, 3, "ax.FiniteTimeAction",&arg1, "ax.Sequence:initWithTwoActions"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Sequence_initWithTwoActions'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getTextureCache'", nullptr); return 0; } - auto&& ret = cobj->initWithTwoActions(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); + auto&& ret = cobj->getTextureCache(); + object_to_luaval(tolua_S, "ax.TextureCache",(ax::TextureCache*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Sequence:initWithTwoActions",argc, 2); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getTextureCache",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Sequence_initWithTwoActions'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getTextureCache'.",&tolua_err); #endif return 0; } -int lua_ax_base_Sequence_init(lua_State* tolua_S) +int lua_ax_base_Director_isNextDeltaTimeZero(lua_State* tolua_S) { int argc = 0; - ax::Sequence* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -25025,48 +25820,45 @@ int lua_ax_base_Sequence_init(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Sequence",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Sequence*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Sequence_init'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_isNextDeltaTimeZero'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - ax::Vector arg0; - - ok &= luaval_to_ccvector(tolua_S, 2, &arg0, "ax.Sequence:init"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Sequence_init'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_isNextDeltaTimeZero'", nullptr); return 0; } - auto&& ret = cobj->init(arg0); + auto&& ret = cobj->isNextDeltaTimeZero(); tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Sequence:init",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:isNextDeltaTimeZero",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Sequence_init'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_isNextDeltaTimeZero'.",&tolua_err); #endif return 0; } -int lua_ax_base_Sequence_constructor(lua_State* tolua_S) +int lua_ax_base_Director_setNextDeltaTimeZero(lua_State* tolua_S) { int argc = 0; - ax::Sequence* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -25074,58 +25866,49 @@ int lua_ax_base_Sequence_constructor(lua_State* tolua_S) #endif +#if _AX_DEBUG >= 1 + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; +#endif + + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_setNextDeltaTimeZero'", nullptr); + return 0; + } +#endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + bool arg0; + + ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.Director:setNextDeltaTimeZero"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Sequence_constructor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_setNextDeltaTimeZero'", nullptr); return 0; } - cobj = new ax::Sequence(); - cobj->autorelease(); - int ID = (int)cobj->_ID ; - int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.Sequence"); + cobj->setNextDeltaTimeZero(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Sequence:Sequence",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:setNextDeltaTimeZero",argc, 1); return 0; #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Sequence_constructor'.",&tolua_err); + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_setNextDeltaTimeZero'.",&tolua_err); #endif return 0; } - -static int lua_ax_base_Sequence_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (Sequence)"); - return 0; -} - -int lua_register_ax_base_Sequence(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.Sequence"); - tolua_cclass(tolua_S,"Sequence","ax.Sequence","ax.ActionInterval",nullptr); - - tolua_beginmodule(tolua_S,"Sequence"); - tolua_function(tolua_S,"new",lua_ax_base_Sequence_constructor); - tolua_function(tolua_S,"initWithTwoActions",lua_ax_base_Sequence_initWithTwoActions); - tolua_function(tolua_S,"init",lua_ax_base_Sequence_init); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::Sequence).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.Sequence"; - g_typeCast[typeName] = "ax.Sequence"; - return 1; -} - -int lua_ax_base_Repeat_setInnerAction(lua_State* tolua_S) +int lua_ax_base_Director_isPaused(lua_State* tolua_S) { int argc = 0; - ax::Repeat* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -25134,48 +25917,45 @@ int lua_ax_base_Repeat_setInnerAction(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Repeat",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Repeat*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Repeat_setInnerAction'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_isPaused'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - ax::FiniteTimeAction* arg0; - - ok &= luaval_to_object(tolua_S, 2, "ax.FiniteTimeAction",&arg0, "ax.Repeat:setInnerAction"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Repeat_setInnerAction'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_isPaused'", nullptr); return 0; } - cobj->setInnerAction(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->isPaused(); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Repeat:setInnerAction",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:isPaused",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Repeat_setInnerAction'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_isPaused'.",&tolua_err); #endif return 0; } -int lua_ax_base_Repeat_getInnerAction(lua_State* tolua_S) +int lua_ax_base_Director_getTotalFrames(lua_State* tolua_S) { int argc = 0; - ax::Repeat* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -25184,15 +25964,15 @@ int lua_ax_base_Repeat_getInnerAction(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Repeat",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Repeat*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Repeat_getInnerAction'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getTotalFrames'", nullptr); return 0; } #endif @@ -25202,27 +25982,27 @@ int lua_ax_base_Repeat_getInnerAction(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Repeat_getInnerAction'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getTotalFrames'", nullptr); return 0; } - auto&& ret = cobj->getInnerAction(); - object_to_luaval(tolua_S, "ax.FiniteTimeAction",(ax::FiniteTimeAction*)ret); + auto&& ret = cobj->getTotalFrames(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Repeat:getInnerAction",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getTotalFrames",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Repeat_getInnerAction'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getTotalFrames'.",&tolua_err); #endif return 0; } -int lua_ax_base_Repeat_initWithAction(lua_State* tolua_S) +int lua_ax_base_Director_setProjection(lua_State* tolua_S) { int argc = 0; - ax::Repeat* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -25231,200 +26011,95 @@ int lua_ax_base_Repeat_initWithAction(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Repeat",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Repeat*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Repeat_initWithAction'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_setProjection'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 2) + if (argc == 1) { - ax::FiniteTimeAction* arg0; - unsigned int arg1; - - ok &= luaval_to_object(tolua_S, 2, "ax.FiniteTimeAction",&arg0, "ax.Repeat:initWithAction"); + ax::Director::Projection arg0; - ok &= luaval_to_uint32(tolua_S, 3,&arg1, "ax.Repeat:initWithAction"); + ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Director:setProjection"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Repeat_initWithAction'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_setProjection'", nullptr); return 0; } - auto&& ret = cobj->initWithAction(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); + cobj->setProjection(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Repeat:initWithAction",argc, 2); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:setProjection",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Repeat_initWithAction'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_setProjection'.",&tolua_err); #endif return 0; } -int lua_ax_base_Repeat_create(lua_State* tolua_S) +int lua_ax_base_Director_setViewport(lua_State* tolua_S) { int argc = 0; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.Repeat",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S) - 1; + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); - if (argc == 2) +#if _AX_DEBUG >= 1 + if (!cobj) { - ax::FiniteTimeAction* arg0; - unsigned int arg1; - ok &= luaval_to_object(tolua_S, 2, "ax.FiniteTimeAction",&arg0, "ax.Repeat:create"); - ok &= luaval_to_uint32(tolua_S, 3,&arg1, "ax.Repeat:create"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Repeat_create'", nullptr); - return 0; - } - auto&& ret = ax::Repeat::create(arg0, arg1); - object_to_luaval(tolua_S, "ax.Repeat",(ax::Repeat*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Repeat:create",argc, 2); - return 0; -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Repeat_create'.",&tolua_err); -#endif - return 0; -} -int lua_ax_base_Repeat_constructor(lua_State* tolua_S) -{ - int argc = 0; - ax::Repeat* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Repeat_constructor'", nullptr); - return 0; - } - cobj = new ax::Repeat(); - cobj->autorelease(); - int ID = (int)cobj->_ID ; - int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.Repeat"); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Repeat:Repeat",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Repeat_constructor'.",&tolua_err); -#endif - - return 0; -} - -static int lua_ax_base_Repeat_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (Repeat)"); - return 0; -} - -int lua_register_ax_base_Repeat(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.Repeat"); - tolua_cclass(tolua_S,"Repeat","ax.Repeat","ax.ActionInterval",nullptr); - - tolua_beginmodule(tolua_S,"Repeat"); - tolua_function(tolua_S,"new",lua_ax_base_Repeat_constructor); - tolua_function(tolua_S,"setInnerAction",lua_ax_base_Repeat_setInnerAction); - tolua_function(tolua_S,"getInnerAction",lua_ax_base_Repeat_getInnerAction); - tolua_function(tolua_S,"initWithAction",lua_ax_base_Repeat_initWithAction); - tolua_function(tolua_S,"create", lua_ax_base_Repeat_create); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::Repeat).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.Repeat"; - g_typeCast[typeName] = "ax.Repeat"; - return 1; -} - -int lua_ax_base_RepeatForever_setInnerAction(lua_State* tolua_S) -{ - int argc = 0; - ax::RepeatForever* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.RepeatForever",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::RepeatForever*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_RepeatForever_setInnerAction'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_setViewport'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - ax::ActionInterval* arg0; - - ok &= luaval_to_object(tolua_S, 2, "ax.ActionInterval",&arg0, "ax.RepeatForever:setInnerAction"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_RepeatForever_setInnerAction'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_setViewport'", nullptr); return 0; } - cobj->setInnerAction(arg0); + cobj->setViewport(); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.RepeatForever:setInnerAction",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:setViewport",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_RepeatForever_setInnerAction'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_setViewport'.",&tolua_err); #endif return 0; } -int lua_ax_base_RepeatForever_getInnerAction(lua_State* tolua_S) +int lua_ax_base_Director_isSendCleanupToScene(lua_State* tolua_S) { int argc = 0; - ax::RepeatForever* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -25433,15 +26108,15 @@ int lua_ax_base_RepeatForever_getInnerAction(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.RepeatForever",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::RepeatForever*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_RepeatForever_getInnerAction'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_isSendCleanupToScene'", nullptr); return 0; } #endif @@ -25451,174 +26126,27 @@ int lua_ax_base_RepeatForever_getInnerAction(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_RepeatForever_getInnerAction'", nullptr); - return 0; - } - auto&& ret = cobj->getInnerAction(); - object_to_luaval(tolua_S, "ax.ActionInterval",(ax::ActionInterval*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.RepeatForever:getInnerAction",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_RepeatForever_getInnerAction'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_base_RepeatForever_initWithAction(lua_State* tolua_S) -{ - int argc = 0; - ax::RepeatForever* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.RepeatForever",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::RepeatForever*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_RepeatForever_initWithAction'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - ax::ActionInterval* arg0; - - ok &= luaval_to_object(tolua_S, 2, "ax.ActionInterval",&arg0, "ax.RepeatForever:initWithAction"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_RepeatForever_initWithAction'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_isSendCleanupToScene'", nullptr); return 0; } - auto&& ret = cobj->initWithAction(arg0); + auto&& ret = cobj->isSendCleanupToScene(); tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.RepeatForever:initWithAction",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:isSendCleanupToScene",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_RepeatForever_initWithAction'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_base_RepeatForever_create(lua_State* tolua_S) -{ - int argc = 0; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.RepeatForever",0,&tolua_err)) goto tolua_lerror; -#endif - - argc = lua_gettop(tolua_S) - 1; - - if (argc == 1) - { - ax::ActionInterval* arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.ActionInterval",&arg0, "ax.RepeatForever:create"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_RepeatForever_create'", nullptr); - return 0; - } - auto&& ret = ax::RepeatForever::create(arg0); - object_to_luaval(tolua_S, "ax.RepeatForever",(ax::RepeatForever*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.RepeatForever:create",argc, 1); - return 0; -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_RepeatForever_create'.",&tolua_err); -#endif - return 0; -} -int lua_ax_base_RepeatForever_constructor(lua_State* tolua_S) -{ - int argc = 0; - ax::RepeatForever* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_RepeatForever_constructor'", nullptr); - return 0; - } - cobj = new ax::RepeatForever(); - cobj->autorelease(); - int ID = (int)cobj->_ID ; - int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.RepeatForever"); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.RepeatForever:RepeatForever",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_RepeatForever_constructor'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_isSendCleanupToScene'.",&tolua_err); #endif return 0; } - -static int lua_ax_base_RepeatForever_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (RepeatForever)"); - return 0; -} - -int lua_register_ax_base_RepeatForever(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.RepeatForever"); - tolua_cclass(tolua_S,"RepeatForever","ax.RepeatForever","ax.ActionInterval",nullptr); - - tolua_beginmodule(tolua_S,"RepeatForever"); - tolua_function(tolua_S,"new",lua_ax_base_RepeatForever_constructor); - tolua_function(tolua_S,"setInnerAction",lua_ax_base_RepeatForever_setInnerAction); - tolua_function(tolua_S,"getInnerAction",lua_ax_base_RepeatForever_getInnerAction); - tolua_function(tolua_S,"initWithAction",lua_ax_base_RepeatForever_initWithAction); - tolua_function(tolua_S,"create", lua_ax_base_RepeatForever_create); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::RepeatForever).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.RepeatForever"; - g_typeCast[typeName] = "ax.RepeatForever"; - return 1; -} - -int lua_ax_base_Spawn_initWithTwoActions(lua_State* tolua_S) +int lua_ax_base_Director_getNotificationNode(lua_State* tolua_S) { int argc = 0; - ax::Spawn* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -25627,51 +26155,45 @@ int lua_ax_base_Spawn_initWithTwoActions(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Spawn",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Spawn*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Spawn_initWithTwoActions'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getNotificationNode'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 2) + if (argc == 0) { - ax::FiniteTimeAction* arg0; - ax::FiniteTimeAction* arg1; - - ok &= luaval_to_object(tolua_S, 2, "ax.FiniteTimeAction",&arg0, "ax.Spawn:initWithTwoActions"); - - ok &= luaval_to_object(tolua_S, 3, "ax.FiniteTimeAction",&arg1, "ax.Spawn:initWithTwoActions"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Spawn_initWithTwoActions'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getNotificationNode'", nullptr); return 0; } - auto&& ret = cobj->initWithTwoActions(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); + auto&& ret = cobj->getNotificationNode(); + object_to_luaval(tolua_S, "ax.Node",(ax::Node*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Spawn:initWithTwoActions",argc, 2); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getNotificationNode",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Spawn_initWithTwoActions'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getNotificationNode'.",&tolua_err); #endif return 0; } -int lua_ax_base_Spawn_init(lua_State* tolua_S) +int lua_ax_base_Director_setNotificationNode(lua_State* tolua_S) { int argc = 0; - ax::Spawn* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -25680,638 +26202,48 @@ int lua_ax_base_Spawn_init(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Spawn",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Spawn*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Spawn_init'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_setNotificationNode'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; if (argc == 1) - { - ax::Vector arg0; - - ok &= luaval_to_ccvector(tolua_S, 2, &arg0, "ax.Spawn:init"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Spawn_init'", nullptr); - return 0; - } - auto&& ret = cobj->init(arg0); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Spawn:init",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Spawn_init'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_base_Spawn_constructor(lua_State* tolua_S) -{ - int argc = 0; - ax::Spawn* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Spawn_constructor'", nullptr); - return 0; - } - cobj = new ax::Spawn(); - cobj->autorelease(); - int ID = (int)cobj->_ID ; - int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.Spawn"); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Spawn:Spawn",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Spawn_constructor'.",&tolua_err); -#endif - - return 0; -} - -static int lua_ax_base_Spawn_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (Spawn)"); - return 0; -} - -int lua_register_ax_base_Spawn(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.Spawn"); - tolua_cclass(tolua_S,"Spawn","ax.Spawn","ax.ActionInterval",nullptr); - - tolua_beginmodule(tolua_S,"Spawn"); - tolua_function(tolua_S,"new",lua_ax_base_Spawn_constructor); - tolua_function(tolua_S,"initWithTwoActions",lua_ax_base_Spawn_initWithTwoActions); - tolua_function(tolua_S,"init",lua_ax_base_Spawn_init); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::Spawn).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.Spawn"; - g_typeCast[typeName] = "ax.Spawn"; - return 1; -} - -int lua_ax_base_RotateTo_initWithDuration(lua_State* tolua_S) -{ - int argc = 0; - ax::RotateTo* cobj = nullptr; - bool ok = true; -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.RotateTo",0,&tolua_err)) goto tolua_lerror; -#endif - cobj = (ax::RotateTo*)tolua_tousertype(tolua_S,1,0); -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_RotateTo_initWithDuration'", nullptr); - return 0; - } -#endif - argc = lua_gettop(tolua_S)-1; - do{ - if (argc == 2) { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.RotateTo:initWithDuration"); - - if (!ok) { break; } - ax::Vec3 arg1; - ok &= luaval_to_vec3(tolua_S, 3, &arg1, "ax.RotateTo:initWithDuration"); - - if (!ok) { break; } - bool ret = cobj->initWithDuration(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 3) { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.RotateTo:initWithDuration"); - - if (!ok) { break; } - double arg1; - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.RotateTo:initWithDuration"); - - if (!ok) { break; } - double arg2; - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.RotateTo:initWithDuration"); - - if (!ok) { break; } - bool ret = cobj->initWithDuration(arg0, arg1, arg2); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - }while(0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.RotateTo:initWithDuration",argc, 3); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_RotateTo_initWithDuration'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_base_RotateTo_create(lua_State* tolua_S) -{ - int argc = 0; - bool ok = true; -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.RotateTo",0,&tolua_err)) goto tolua_lerror; -#endif - - argc = lua_gettop(tolua_S)-1; - - do - { - if (argc == 2) - { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.RotateTo:create"); - if (!ok) { break; } - double arg1; - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.RotateTo:create"); - if (!ok) { break; } - ax::RotateTo* ret = ax::RotateTo::create(arg0, arg1); - object_to_luaval(tolua_S, "ax.RotateTo",(ax::RotateTo*)ret); - return 1; - } - } while (0); - ok = true; - do - { - if (argc == 3) - { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.RotateTo:create"); - if (!ok) { break; } - double arg1; - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.RotateTo:create"); - if (!ok) { break; } - double arg2; - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.RotateTo:create"); - if (!ok) { break; } - ax::RotateTo* ret = ax::RotateTo::create(arg0, arg1, arg2); - object_to_luaval(tolua_S, "ax.RotateTo",(ax::RotateTo*)ret); - return 1; - } - } while (0); - ok = true; - do - { - if (argc == 2) - { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.RotateTo:create"); - if (!ok) { break; } - ax::Vec3 arg1; - ok &= luaval_to_vec3(tolua_S, 3, &arg1, "ax.RotateTo:create"); - if (!ok) { break; } - ax::RotateTo* ret = ax::RotateTo::create(arg0, arg1); - object_to_luaval(tolua_S, "ax.RotateTo",(ax::RotateTo*)ret); - return 1; - } - } while (0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d", "ax.RotateTo:create",argc, 2); - return 0; -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_RotateTo_create'.",&tolua_err); -#endif - return 0; -} -int lua_ax_base_RotateTo_constructor(lua_State* tolua_S) -{ - int argc = 0; - ax::RotateTo* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_RotateTo_constructor'", nullptr); - return 0; - } - cobj = new ax::RotateTo(); - cobj->autorelease(); - int ID = (int)cobj->_ID ; - int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.RotateTo"); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.RotateTo:RotateTo",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_RotateTo_constructor'.",&tolua_err); -#endif - - return 0; -} - -static int lua_ax_base_RotateTo_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (RotateTo)"); - return 0; -} - -int lua_register_ax_base_RotateTo(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.RotateTo"); - tolua_cclass(tolua_S,"RotateTo","ax.RotateTo","ax.ActionInterval",nullptr); - - tolua_beginmodule(tolua_S,"RotateTo"); - tolua_function(tolua_S,"new",lua_ax_base_RotateTo_constructor); - tolua_function(tolua_S,"initWithDuration",lua_ax_base_RotateTo_initWithDuration); - tolua_function(tolua_S,"create", lua_ax_base_RotateTo_create); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::RotateTo).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.RotateTo"; - g_typeCast[typeName] = "ax.RotateTo"; - return 1; -} - -int lua_ax_base_RotateBy_initWithDuration(lua_State* tolua_S) -{ - int argc = 0; - ax::RotateBy* cobj = nullptr; - bool ok = true; -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.RotateBy",0,&tolua_err)) goto tolua_lerror; -#endif - cobj = (ax::RotateBy*)tolua_tousertype(tolua_S,1,0); -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_RotateBy_initWithDuration'", nullptr); - return 0; - } -#endif - argc = lua_gettop(tolua_S)-1; - do{ - if (argc == 3) { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.RotateBy:initWithDuration"); - - if (!ok) { break; } - double arg1; - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.RotateBy:initWithDuration"); - - if (!ok) { break; } - double arg2; - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.RotateBy:initWithDuration"); - - if (!ok) { break; } - bool ret = cobj->initWithDuration(arg0, arg1, arg2); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 2) { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.RotateBy:initWithDuration"); - - if (!ok) { break; } - double arg1; - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.RotateBy:initWithDuration"); - - if (!ok) { break; } - bool ret = cobj->initWithDuration(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 2) { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.RotateBy:initWithDuration"); - - if (!ok) { break; } - ax::Vec3 arg1; - ok &= luaval_to_vec3(tolua_S, 3, &arg1, "ax.RotateBy:initWithDuration"); - - if (!ok) { break; } - bool ret = cobj->initWithDuration(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - }while(0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.RotateBy:initWithDuration",argc, 2); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_RotateBy_initWithDuration'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_base_RotateBy_create(lua_State* tolua_S) -{ - int argc = 0; - bool ok = true; -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.RotateBy",0,&tolua_err)) goto tolua_lerror; -#endif - - argc = lua_gettop(tolua_S)-1; - - do - { - if (argc == 3) - { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.RotateBy:create"); - if (!ok) { break; } - double arg1; - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.RotateBy:create"); - if (!ok) { break; } - double arg2; - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.RotateBy:create"); - if (!ok) { break; } - ax::RotateBy* ret = ax::RotateBy::create(arg0, arg1, arg2); - object_to_luaval(tolua_S, "ax.RotateBy",(ax::RotateBy*)ret); - return 1; - } - } while (0); - ok = true; - do - { - if (argc == 2) - { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.RotateBy:create"); - if (!ok) { break; } - double arg1; - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.RotateBy:create"); - if (!ok) { break; } - ax::RotateBy* ret = ax::RotateBy::create(arg0, arg1); - object_to_luaval(tolua_S, "ax.RotateBy",(ax::RotateBy*)ret); - return 1; - } - } while (0); - ok = true; - do - { - if (argc == 2) - { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.RotateBy:create"); - if (!ok) { break; } - ax::Vec3 arg1; - ok &= luaval_to_vec3(tolua_S, 3, &arg1, "ax.RotateBy:create"); - if (!ok) { break; } - ax::RotateBy* ret = ax::RotateBy::create(arg0, arg1); - object_to_luaval(tolua_S, "ax.RotateBy",(ax::RotateBy*)ret); - return 1; - } - } while (0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d", "ax.RotateBy:create",argc, 2); - return 0; -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_RotateBy_create'.",&tolua_err); -#endif - return 0; -} -int lua_ax_base_RotateBy_constructor(lua_State* tolua_S) -{ - int argc = 0; - ax::RotateBy* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_RotateBy_constructor'", nullptr); - return 0; - } - cobj = new ax::RotateBy(); - cobj->autorelease(); - int ID = (int)cobj->_ID ; - int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.RotateBy"); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.RotateBy:RotateBy",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_RotateBy_constructor'.",&tolua_err); -#endif - - return 0; -} - -static int lua_ax_base_RotateBy_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (RotateBy)"); - return 0; -} - -int lua_register_ax_base_RotateBy(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.RotateBy"); - tolua_cclass(tolua_S,"RotateBy","ax.RotateBy","ax.ActionInterval",nullptr); - - tolua_beginmodule(tolua_S,"RotateBy"); - tolua_function(tolua_S,"new",lua_ax_base_RotateBy_constructor); - tolua_function(tolua_S,"initWithDuration",lua_ax_base_RotateBy_initWithDuration); - tolua_function(tolua_S,"create", lua_ax_base_RotateBy_create); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::RotateBy).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.RotateBy"; - g_typeCast[typeName] = "ax.RotateBy"; - return 1; -} - -int lua_ax_base_MoveBy_initWithDuration(lua_State* tolua_S) -{ - int argc = 0; - ax::MoveBy* cobj = nullptr; - bool ok = true; -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.MoveBy",0,&tolua_err)) goto tolua_lerror; -#endif - cobj = (ax::MoveBy*)tolua_tousertype(tolua_S,1,0); -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_MoveBy_initWithDuration'", nullptr); - return 0; - } -#endif - argc = lua_gettop(tolua_S)-1; - do{ - if (argc == 2) { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.MoveBy:initWithDuration"); - - if (!ok) { break; } - ax::Vec3 arg1; - ok &= luaval_to_vec3(tolua_S, 3, &arg1, "ax.MoveBy:initWithDuration"); - - if (!ok) { break; } - bool ret = cobj->initWithDuration(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 2) { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.MoveBy:initWithDuration"); - - if (!ok) { break; } - ax::Vec2 arg1; - ok &= luaval_to_vec2(tolua_S, 3, &arg1, "ax.MoveBy:initWithDuration"); - - if (!ok) { break; } - bool ret = cobj->initWithDuration(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - }while(0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.MoveBy:initWithDuration",argc, 2); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_MoveBy_initWithDuration'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_base_MoveBy_create(lua_State* tolua_S) -{ - int argc = 0; - bool ok = true; -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.MoveBy",0,&tolua_err)) goto tolua_lerror; -#endif - - argc = lua_gettop(tolua_S)-1; - - do - { - if (argc == 2) - { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.MoveBy:create"); - if (!ok) { break; } - ax::Vec3 arg1; - ok &= luaval_to_vec3(tolua_S, 3, &arg1, "ax.MoveBy:create"); - if (!ok) { break; } - ax::MoveBy* ret = ax::MoveBy::create(arg0, arg1); - object_to_luaval(tolua_S, "ax.MoveBy",(ax::MoveBy*)ret); - return 1; - } - } while (0); - ok = true; - do - { - if (argc == 2) - { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.MoveBy:create"); - if (!ok) { break; } - ax::Vec2 arg1; - ok &= luaval_to_vec2(tolua_S, 3, &arg1, "ax.MoveBy:create"); - if (!ok) { break; } - ax::MoveBy* ret = ax::MoveBy::create(arg0, arg1); - object_to_luaval(tolua_S, "ax.MoveBy",(ax::MoveBy*)ret); - return 1; + { + ax::Node* arg0; + + ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.Director:setNotificationNode"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_setNotificationNode'", nullptr); + return 0; } - } while (0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d", "ax.MoveBy:create",argc, 2); + cobj->setNotificationNode(arg0); + lua_settop(tolua_S, 1); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:setNotificationNode",argc, 1); return 0; + #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_MoveBy_create'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_setNotificationNode'.",&tolua_err); #endif + return 0; } -int lua_ax_base_MoveBy_constructor(lua_State* tolua_S) +int lua_ax_base_Director_getWinSize(lua_State* tolua_S) { int argc = 0; - ax::MoveBy* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -26319,175 +26251,140 @@ int lua_ax_base_MoveBy_constructor(lua_State* tolua_S) #endif +#if _AX_DEBUG >= 1 + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; +#endif + + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getWinSize'", nullptr); + return 0; + } +#endif argc = lua_gettop(tolua_S)-1; if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_MoveBy_constructor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getWinSize'", nullptr); return 0; } - cobj = new ax::MoveBy(); - cobj->autorelease(); - int ID = (int)cobj->_ID ; - int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.MoveBy"); + auto&& ret = cobj->getWinSize(); + vec2_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.MoveBy:MoveBy",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getWinSize",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_MoveBy_constructor'.",&tolua_err); + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getWinSize'.",&tolua_err); #endif return 0; } - -static int lua_ax_base_MoveBy_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (MoveBy)"); - return 0; -} - -int lua_register_ax_base_MoveBy(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.MoveBy"); - tolua_cclass(tolua_S,"MoveBy","ax.MoveBy","ax.ActionInterval",nullptr); - - tolua_beginmodule(tolua_S,"MoveBy"); - tolua_function(tolua_S,"new",lua_ax_base_MoveBy_constructor); - tolua_function(tolua_S,"initWithDuration",lua_ax_base_MoveBy_initWithDuration); - tolua_function(tolua_S,"create", lua_ax_base_MoveBy_create); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::MoveBy).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.MoveBy"; - g_typeCast[typeName] = "ax.MoveBy"; - return 1; -} - -int lua_ax_base_MoveTo_initWithDuration(lua_State* tolua_S) +int lua_ax_base_Director_getWinSizeInPixels(lua_State* tolua_S) { int argc = 0; - ax::MoveTo* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; + #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.MoveTo",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::MoveTo*)tolua_tousertype(tolua_S,1,0); + + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_MoveTo_initWithDuration'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getWinSizeInPixels'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - do{ - if (argc == 2) { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.MoveTo:initWithDuration"); - - if (!ok) { break; } - ax::Vec3 arg1; - ok &= luaval_to_vec3(tolua_S, 3, &arg1, "ax.MoveTo:initWithDuration"); - - if (!ok) { break; } - bool ret = cobj->initWithDuration(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 2) { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.MoveTo:initWithDuration"); - - if (!ok) { break; } - ax::Vec2 arg1; - ok &= luaval_to_vec2(tolua_S, 3, &arg1, "ax.MoveTo:initWithDuration"); - if (!ok) { break; } - bool ret = cobj->initWithDuration(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; + argc = lua_gettop(tolua_S)-1; + if (argc == 0) + { + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getWinSizeInPixels'", nullptr); + return 0; } - }while(0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.MoveTo:initWithDuration",argc, 2); + auto&& ret = cobj->getWinSizeInPixels(); + vec2_to_luaval(tolua_S, ret); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getWinSizeInPixels",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_MoveTo_initWithDuration'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getWinSizeInPixels'.",&tolua_err); #endif return 0; } -int lua_ax_base_MoveTo_create(lua_State* tolua_S) +int lua_ax_base_Director_getVisibleSize(lua_State* tolua_S) { int argc = 0; + ax::Director* cobj = nullptr; bool ok = true; + #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.MoveTo",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S)-1; + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); - do +#if _AX_DEBUG >= 1 + if (!cobj) { - if (argc == 2) - { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.MoveTo:create"); - if (!ok) { break; } - ax::Vec3 arg1; - ok &= luaval_to_vec3(tolua_S, 3, &arg1, "ax.MoveTo:create"); - if (!ok) { break; } - ax::MoveTo* ret = ax::MoveTo::create(arg0, arg1); - object_to_luaval(tolua_S, "ax.MoveTo",(ax::MoveTo*)ret); - return 1; - } - } while (0); - ok = true; - do + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getVisibleSize'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 0) { - if (argc == 2) + if(!ok) { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.MoveTo:create"); - if (!ok) { break; } - ax::Vec2 arg1; - ok &= luaval_to_vec2(tolua_S, 3, &arg1, "ax.MoveTo:create"); - if (!ok) { break; } - ax::MoveTo* ret = ax::MoveTo::create(arg0, arg1); - object_to_luaval(tolua_S, "ax.MoveTo",(ax::MoveTo*)ret); - return 1; + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getVisibleSize'", nullptr); + return 0; } - } while (0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d", "ax.MoveTo:create",argc, 2); + auto&& ret = cobj->getVisibleSize(); + vec2_to_luaval(tolua_S, ret); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getVisibleSize",argc, 0); return 0; + #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_MoveTo_create'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getVisibleSize'.",&tolua_err); #endif + return 0; } -int lua_ax_base_MoveTo_constructor(lua_State* tolua_S) +int lua_ax_base_Director_getVisibleOrigin(lua_State* tolua_S) { int argc = 0; - ax::MoveTo* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -26495,58 +26392,46 @@ int lua_ax_base_MoveTo_constructor(lua_State* tolua_S) #endif +#if _AX_DEBUG >= 1 + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; +#endif + + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getVisibleOrigin'", nullptr); + return 0; + } +#endif argc = lua_gettop(tolua_S)-1; if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_MoveTo_constructor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getVisibleOrigin'", nullptr); return 0; } - cobj = new ax::MoveTo(); - cobj->autorelease(); - int ID = (int)cobj->_ID ; - int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.MoveTo"); + auto&& ret = cobj->getVisibleOrigin(); + vec2_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.MoveTo:MoveTo",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getVisibleOrigin",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_MoveTo_constructor'.",&tolua_err); + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getVisibleOrigin'.",&tolua_err); #endif return 0; } - -static int lua_ax_base_MoveTo_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (MoveTo)"); - return 0; -} - -int lua_register_ax_base_MoveTo(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.MoveTo"); - tolua_cclass(tolua_S,"MoveTo","ax.MoveTo","ax.MoveBy",nullptr); - - tolua_beginmodule(tolua_S,"MoveTo"); - tolua_function(tolua_S,"new",lua_ax_base_MoveTo_constructor); - tolua_function(tolua_S,"initWithDuration",lua_ax_base_MoveTo_initWithDuration); - tolua_function(tolua_S,"create", lua_ax_base_MoveTo_create); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::MoveTo).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.MoveTo"; - g_typeCast[typeName] = "ax.MoveTo"; - return 1; -} - -int lua_ax_base_SkewTo_initWithDuration(lua_State* tolua_S) +int lua_ax_base_Director_getSafeAreaRect(lua_State* tolua_S) { int argc = 0; - ax::SkewTo* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -26555,94 +26440,95 @@ int lua_ax_base_SkewTo_initWithDuration(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.SkewTo",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::SkewTo*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SkewTo_initWithDuration'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getSafeAreaRect'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 3) + if (argc == 0) { - double arg0; - double arg1; - double arg2; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.SkewTo:initWithDuration"); - - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.SkewTo:initWithDuration"); - - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.SkewTo:initWithDuration"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SkewTo_initWithDuration'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getSafeAreaRect'", nullptr); return 0; } - auto&& ret = cobj->initWithDuration(arg0, arg1, arg2); - tolua_pushboolean(tolua_S,(bool)ret); + auto&& ret = cobj->getSafeAreaRect(); + rect_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SkewTo:initWithDuration",argc, 3); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getSafeAreaRect",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SkewTo_initWithDuration'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getSafeAreaRect'.",&tolua_err); #endif return 0; } -int lua_ax_base_SkewTo_create(lua_State* tolua_S) +int lua_ax_base_Director_convertToGL(lua_State* tolua_S) { int argc = 0; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.SkewTo",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S) - 1; + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); - if (argc == 3) +#if _AX_DEBUG >= 1 + if (!cobj) { - double arg0; - double arg1; - double arg2; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.SkewTo:create"); - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.SkewTo:create"); - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.SkewTo:create"); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_convertToGL'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 1) + { + ax::Vec2 arg0; + + ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.Director:convertToGL"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SkewTo_create'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_convertToGL'", nullptr); return 0; } - auto&& ret = ax::SkewTo::create(arg0, arg1, arg2); - object_to_luaval(tolua_S, "ax.SkewTo",(ax::SkewTo*)ret); + auto&& ret = cobj->convertToGL(arg0); + vec2_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.SkewTo:create",argc, 3); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:convertToGL",argc, 1); return 0; + #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SkewTo_create'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_convertToGL'.",&tolua_err); #endif + return 0; } -int lua_ax_base_SkewTo_constructor(lua_State* tolua_S) +int lua_ax_base_Director_convertToUI(lua_State* tolua_S) { int argc = 0; - ax::SkewTo* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -26650,58 +26536,49 @@ int lua_ax_base_SkewTo_constructor(lua_State* tolua_S) #endif +#if _AX_DEBUG >= 1 + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; +#endif + + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_convertToUI'", nullptr); + return 0; + } +#endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + ax::Vec2 arg0; + + ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.Director:convertToUI"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SkewTo_constructor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_convertToUI'", nullptr); return 0; } - cobj = new ax::SkewTo(); - cobj->autorelease(); - int ID = (int)cobj->_ID ; - int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.SkewTo"); + auto&& ret = cobj->convertToUI(arg0); + vec2_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SkewTo:SkewTo",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:convertToUI",argc, 1); return 0; #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SkewTo_constructor'.",&tolua_err); + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_convertToUI'.",&tolua_err); #endif return 0; } - -static int lua_ax_base_SkewTo_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (SkewTo)"); - return 0; -} - -int lua_register_ax_base_SkewTo(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.SkewTo"); - tolua_cclass(tolua_S,"SkewTo","ax.SkewTo","ax.ActionInterval",nullptr); - - tolua_beginmodule(tolua_S,"SkewTo"); - tolua_function(tolua_S,"new",lua_ax_base_SkewTo_constructor); - tolua_function(tolua_S,"initWithDuration",lua_ax_base_SkewTo_initWithDuration); - tolua_function(tolua_S,"create", lua_ax_base_SkewTo_create); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::SkewTo).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.SkewTo"; - g_typeCast[typeName] = "ax.SkewTo"; - return 1; -} - -int lua_ax_base_SkewBy_initWithDuration(lua_State* tolua_S) +int lua_ax_base_Director_getZEye(lua_State* tolua_S) { int argc = 0; - ax::SkewBy* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -26710,94 +26587,95 @@ int lua_ax_base_SkewBy_initWithDuration(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.SkewBy",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::SkewBy*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_SkewBy_initWithDuration'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getZEye'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 3) + if (argc == 0) { - double arg0; - double arg1; - double arg2; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.SkewBy:initWithDuration"); - - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.SkewBy:initWithDuration"); - - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.SkewBy:initWithDuration"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SkewBy_initWithDuration'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getZEye'", nullptr); return 0; } - auto&& ret = cobj->initWithDuration(arg0, arg1, arg2); - tolua_pushboolean(tolua_S,(bool)ret); + auto&& ret = cobj->getZEye(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SkewBy:initWithDuration",argc, 3); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getZEye",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SkewBy_initWithDuration'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getZEye'.",&tolua_err); #endif return 0; } -int lua_ax_base_SkewBy_create(lua_State* tolua_S) +int lua_ax_base_Director_runWithScene(lua_State* tolua_S) { int argc = 0; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.SkewBy",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S) - 1; + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); - if (argc == 3) +#if _AX_DEBUG >= 1 + if (!cobj) { - double arg0; - double arg1; - double arg2; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.SkewBy:create"); - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.SkewBy:create"); - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.SkewBy:create"); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_runWithScene'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 1) + { + ax::Scene* arg0; + + ok &= luaval_to_object(tolua_S, 2, "ax.Scene",&arg0, "ax.Director:runWithScene"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SkewBy_create'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_runWithScene'", nullptr); return 0; } - auto&& ret = ax::SkewBy::create(arg0, arg1, arg2); - object_to_luaval(tolua_S, "ax.SkewBy",(ax::SkewBy*)ret); + cobj->runWithScene(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.SkewBy:create",argc, 3); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:runWithScene",argc, 1); return 0; + #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SkewBy_create'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_runWithScene'.",&tolua_err); #endif + return 0; } -int lua_ax_base_SkewBy_constructor(lua_State* tolua_S) +int lua_ax_base_Director_pushScene(lua_State* tolua_S) { int argc = 0; - ax::SkewBy* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -26805,58 +26683,49 @@ int lua_ax_base_SkewBy_constructor(lua_State* tolua_S) #endif +#if _AX_DEBUG >= 1 + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; +#endif + + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_pushScene'", nullptr); + return 0; + } +#endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + ax::Scene* arg0; + + ok &= luaval_to_object(tolua_S, 2, "ax.Scene",&arg0, "ax.Director:pushScene"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_SkewBy_constructor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_pushScene'", nullptr); return 0; } - cobj = new ax::SkewBy(); - cobj->autorelease(); - int ID = (int)cobj->_ID ; - int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.SkewBy"); + cobj->pushScene(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.SkewBy:SkewBy",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:pushScene",argc, 1); return 0; #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_SkewBy_constructor'.",&tolua_err); -#endif - - return 0; -} - -static int lua_ax_base_SkewBy_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (SkewBy)"); - return 0; -} - -int lua_register_ax_base_SkewBy(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.SkewBy"); - tolua_cclass(tolua_S,"SkewBy","ax.SkewBy","ax.SkewTo",nullptr); - - tolua_beginmodule(tolua_S,"SkewBy"); - tolua_function(tolua_S,"new",lua_ax_base_SkewBy_constructor); - tolua_function(tolua_S,"initWithDuration",lua_ax_base_SkewBy_initWithDuration); - tolua_function(tolua_S,"create", lua_ax_base_SkewBy_create); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::SkewBy).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.SkewBy"; - g_typeCast[typeName] = "ax.SkewBy"; - return 1; -} + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_pushScene'.",&tolua_err); +#endif -int lua_ax_base_JumpBy_initWithDuration(lua_State* tolua_S) + return 0; +} +int lua_ax_base_Director_popScene(lua_State* tolua_S) { int argc = 0; - ax::JumpBy* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -26865,99 +26734,92 @@ int lua_ax_base_JumpBy_initWithDuration(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.JumpBy",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::JumpBy*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_JumpBy_initWithDuration'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_popScene'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 4) + if (argc == 0) { - double arg0; - ax::Vec2 arg1; - double arg2; - int arg3; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.JumpBy:initWithDuration"); - - ok &= luaval_to_vec2(tolua_S, 3, &arg1, "ax.JumpBy:initWithDuration"); - - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.JumpBy:initWithDuration"); - - ok &= luaval_to_int32(tolua_S, 5,(int *)&arg3, "ax.JumpBy:initWithDuration"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_JumpBy_initWithDuration'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_popScene'", nullptr); return 0; } - auto&& ret = cobj->initWithDuration(arg0, arg1, arg2, arg3); - tolua_pushboolean(tolua_S,(bool)ret); + cobj->popScene(); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.JumpBy:initWithDuration",argc, 4); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:popScene",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_JumpBy_initWithDuration'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_popScene'.",&tolua_err); #endif return 0; } -int lua_ax_base_JumpBy_create(lua_State* tolua_S) +int lua_ax_base_Director_popToRootScene(lua_State* tolua_S) { int argc = 0; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.JumpBy",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S) - 1; + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); - if (argc == 4) +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_popToRootScene'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 0) { - double arg0; - ax::Vec2 arg1; - double arg2; - int arg3; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.JumpBy:create"); - ok &= luaval_to_vec2(tolua_S, 3, &arg1, "ax.JumpBy:create"); - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.JumpBy:create"); - ok &= luaval_to_int32(tolua_S, 5,(int *)&arg3, "ax.JumpBy:create"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_JumpBy_create'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_popToRootScene'", nullptr); return 0; } - auto&& ret = ax::JumpBy::create(arg0, arg1, arg2, arg3); - object_to_luaval(tolua_S, "ax.JumpBy",(ax::JumpBy*)ret); + cobj->popToRootScene(); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.JumpBy:create",argc, 4); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:popToRootScene",argc, 0); return 0; + #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_JumpBy_create'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_popToRootScene'.",&tolua_err); #endif + return 0; } -int lua_ax_base_JumpBy_constructor(lua_State* tolua_S) +int lua_ax_base_Director_popToSceneStackLevel(lua_State* tolua_S) { int argc = 0; - ax::JumpBy* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -26965,58 +26827,49 @@ int lua_ax_base_JumpBy_constructor(lua_State* tolua_S) #endif +#if _AX_DEBUG >= 1 + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; +#endif + + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_popToSceneStackLevel'", nullptr); + return 0; + } +#endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + int arg0; + + ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Director:popToSceneStackLevel"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_JumpBy_constructor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_popToSceneStackLevel'", nullptr); return 0; } - cobj = new ax::JumpBy(); - cobj->autorelease(); - int ID = (int)cobj->_ID ; - int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.JumpBy"); + cobj->popToSceneStackLevel(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.JumpBy:JumpBy",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:popToSceneStackLevel",argc, 1); return 0; #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_JumpBy_constructor'.",&tolua_err); + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_popToSceneStackLevel'.",&tolua_err); #endif return 0; } - -static int lua_ax_base_JumpBy_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (JumpBy)"); - return 0; -} - -int lua_register_ax_base_JumpBy(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.JumpBy"); - tolua_cclass(tolua_S,"JumpBy","ax.JumpBy","ax.ActionInterval",nullptr); - - tolua_beginmodule(tolua_S,"JumpBy"); - tolua_function(tolua_S,"new",lua_ax_base_JumpBy_constructor); - tolua_function(tolua_S,"initWithDuration",lua_ax_base_JumpBy_initWithDuration); - tolua_function(tolua_S,"create", lua_ax_base_JumpBy_create); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::JumpBy).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.JumpBy"; - g_typeCast[typeName] = "ax.JumpBy"; - return 1; -} - -int lua_ax_base_JumpTo_initWithDuration(lua_State* tolua_S) +int lua_ax_base_Director_replaceScene(lua_State* tolua_S) { int argc = 0; - ax::JumpTo* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -27025,99 +26878,95 @@ int lua_ax_base_JumpTo_initWithDuration(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.JumpTo",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::JumpTo*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_JumpTo_initWithDuration'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_replaceScene'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 4) + if (argc == 1) { - double arg0; - ax::Vec2 arg1; - double arg2; - int arg3; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.JumpTo:initWithDuration"); - - ok &= luaval_to_vec2(tolua_S, 3, &arg1, "ax.JumpTo:initWithDuration"); - - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.JumpTo:initWithDuration"); + ax::Scene* arg0; - ok &= luaval_to_int32(tolua_S, 5,(int *)&arg3, "ax.JumpTo:initWithDuration"); + ok &= luaval_to_object(tolua_S, 2, "ax.Scene",&arg0, "ax.Director:replaceScene"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_JumpTo_initWithDuration'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_replaceScene'", nullptr); return 0; } - auto&& ret = cobj->initWithDuration(arg0, arg1, arg2, arg3); - tolua_pushboolean(tolua_S,(bool)ret); + cobj->replaceScene(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.JumpTo:initWithDuration",argc, 4); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:replaceScene",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_JumpTo_initWithDuration'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_replaceScene'.",&tolua_err); #endif return 0; } -int lua_ax_base_JumpTo_create(lua_State* tolua_S) +int lua_ax_base_Director_end(lua_State* tolua_S) { int argc = 0; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.JumpTo",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S) - 1; + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); - if (argc == 4) +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_end'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 0) { - double arg0; - ax::Vec2 arg1; - double arg2; - int arg3; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.JumpTo:create"); - ok &= luaval_to_vec2(tolua_S, 3, &arg1, "ax.JumpTo:create"); - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.JumpTo:create"); - ok &= luaval_to_int32(tolua_S, 5,(int *)&arg3, "ax.JumpTo:create"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_JumpTo_create'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_end'", nullptr); return 0; } - auto&& ret = ax::JumpTo::create(arg0, arg1, arg2, arg3); - object_to_luaval(tolua_S, "ax.JumpTo",(ax::JumpTo*)ret); + cobj->end(); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.JumpTo:create",argc, 4); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:end",argc, 0); return 0; + #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_JumpTo_create'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_end'.",&tolua_err); #endif + return 0; } -int lua_ax_base_JumpTo_constructor(lua_State* tolua_S) +int lua_ax_base_Director_pause(lua_State* tolua_S) { int argc = 0; - ax::JumpTo* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -27125,58 +26974,46 @@ int lua_ax_base_JumpTo_constructor(lua_State* tolua_S) #endif +#if _AX_DEBUG >= 1 + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; +#endif + + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_pause'", nullptr); + return 0; + } +#endif argc = lua_gettop(tolua_S)-1; if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_JumpTo_constructor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_pause'", nullptr); return 0; } - cobj = new ax::JumpTo(); - cobj->autorelease(); - int ID = (int)cobj->_ID ; - int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.JumpTo"); + cobj->pause(); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.JumpTo:JumpTo",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:pause",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_JumpTo_constructor'.",&tolua_err); + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_pause'.",&tolua_err); #endif return 0; } - -static int lua_ax_base_JumpTo_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (JumpTo)"); - return 0; -} - -int lua_register_ax_base_JumpTo(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.JumpTo"); - tolua_cclass(tolua_S,"JumpTo","ax.JumpTo","ax.JumpBy",nullptr); - - tolua_beginmodule(tolua_S,"JumpTo"); - tolua_function(tolua_S,"new",lua_ax_base_JumpTo_constructor); - tolua_function(tolua_S,"initWithDuration",lua_ax_base_JumpTo_initWithDuration); - tolua_function(tolua_S,"create", lua_ax_base_JumpTo_create); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::JumpTo).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.JumpTo"; - g_typeCast[typeName] = "ax.JumpTo"; - return 1; -} - -int lua_ax_base_BezierBy_initWithDuration(lua_State* tolua_S) +int lua_ax_base_Director_resume(lua_State* tolua_S) { int argc = 0; - ax::BezierBy* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -27185,52 +27022,45 @@ int lua_ax_base_BezierBy_initWithDuration(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.BezierBy",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::BezierBy*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_BezierBy_initWithDuration'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_resume'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 2) + if (argc == 0) { - double arg0; - ax::_ccBezierConfig arg1; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.BezierBy:initWithDuration"); - - #pragma warning NO CONVERSION TO NATIVE FOR _ccBezierConfig - ok = false; if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_BezierBy_initWithDuration'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_resume'", nullptr); return 0; } - auto&& ret = cobj->initWithDuration(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); + cobj->resume(); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.BezierBy:initWithDuration",argc, 2); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:resume",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_BezierBy_initWithDuration'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_resume'.",&tolua_err); #endif return 0; } -int lua_ax_base_BezierBy_constructor(lua_State* tolua_S) +int lua_ax_base_Director_restart(lua_State* tolua_S) { int argc = 0; - ax::BezierBy* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -27238,57 +27068,46 @@ int lua_ax_base_BezierBy_constructor(lua_State* tolua_S) #endif +#if _AX_DEBUG >= 1 + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; +#endif + + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_restart'", nullptr); + return 0; + } +#endif argc = lua_gettop(tolua_S)-1; if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_BezierBy_constructor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_restart'", nullptr); return 0; } - cobj = new ax::BezierBy(); - cobj->autorelease(); - int ID = (int)cobj->_ID ; - int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.BezierBy"); + cobj->restart(); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.BezierBy:BezierBy",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:restart",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_BezierBy_constructor'.",&tolua_err); + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_restart'.",&tolua_err); #endif return 0; } - -static int lua_ax_base_BezierBy_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (BezierBy)"); - return 0; -} - -int lua_register_ax_base_BezierBy(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.BezierBy"); - tolua_cclass(tolua_S,"BezierBy","ax.BezierBy","ax.ActionInterval",nullptr); - - tolua_beginmodule(tolua_S,"BezierBy"); - tolua_function(tolua_S,"new",lua_ax_base_BezierBy_constructor); - tolua_function(tolua_S,"initWithDuration",lua_ax_base_BezierBy_initWithDuration); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::BezierBy).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.BezierBy"; - g_typeCast[typeName] = "ax.BezierBy"; - return 1; -} - -int lua_ax_base_BezierTo_initWithDuration(lua_State* tolua_S) +int lua_ax_base_Director_stopAnimation(lua_State* tolua_S) { int argc = 0; - ax::BezierTo* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -27297,52 +27116,45 @@ int lua_ax_base_BezierTo_initWithDuration(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.BezierTo",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::BezierTo*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_BezierTo_initWithDuration'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_stopAnimation'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 2) + if (argc == 0) { - double arg0; - ax::_ccBezierConfig arg1; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.BezierTo:initWithDuration"); - - #pragma warning NO CONVERSION TO NATIVE FOR _ccBezierConfig - ok = false; if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_BezierTo_initWithDuration'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_stopAnimation'", nullptr); return 0; } - auto&& ret = cobj->initWithDuration(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); + cobj->stopAnimation(); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.BezierTo:initWithDuration",argc, 2); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:stopAnimation",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_BezierTo_initWithDuration'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_stopAnimation'.",&tolua_err); #endif return 0; } -int lua_ax_base_BezierTo_constructor(lua_State* tolua_S) +int lua_ax_base_Director_startAnimation(lua_State* tolua_S) { int argc = 0; - ax::BezierTo* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -27350,227 +27162,140 @@ int lua_ax_base_BezierTo_constructor(lua_State* tolua_S) #endif +#if _AX_DEBUG >= 1 + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; +#endif + + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_startAnimation'", nullptr); + return 0; + } +#endif argc = lua_gettop(tolua_S)-1; if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_BezierTo_constructor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_startAnimation'", nullptr); return 0; } - cobj = new ax::BezierTo(); - cobj->autorelease(); - int ID = (int)cobj->_ID ; - int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.BezierTo"); + cobj->startAnimation(); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.BezierTo:BezierTo",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:startAnimation",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_BezierTo_constructor'.",&tolua_err); + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_startAnimation'.",&tolua_err); #endif return 0; } - -static int lua_ax_base_BezierTo_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (BezierTo)"); - return 0; -} - -int lua_register_ax_base_BezierTo(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.BezierTo"); - tolua_cclass(tolua_S,"BezierTo","ax.BezierTo","ax.BezierBy",nullptr); - - tolua_beginmodule(tolua_S,"BezierTo"); - tolua_function(tolua_S,"new",lua_ax_base_BezierTo_constructor); - tolua_function(tolua_S,"initWithDuration",lua_ax_base_BezierTo_initWithDuration); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::BezierTo).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.BezierTo"; - g_typeCast[typeName] = "ax.BezierTo"; - return 1; -} - -int lua_ax_base_ScaleTo_initWithDuration(lua_State* tolua_S) +int lua_ax_base_Director_drawScene(lua_State* tolua_S) { int argc = 0; - ax::ScaleTo* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; + #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.ScaleTo",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::ScaleTo*)tolua_tousertype(tolua_S,1,0); + + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_ScaleTo_initWithDuration'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_drawScene'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - do{ - if (argc == 3) { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.ScaleTo:initWithDuration"); - if (!ok) { break; } - double arg1; - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.ScaleTo:initWithDuration"); - - if (!ok) { break; } - double arg2; - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.ScaleTo:initWithDuration"); - - if (!ok) { break; } - bool ret = cobj->initWithDuration(arg0, arg1, arg2); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 2) { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.ScaleTo:initWithDuration"); - - if (!ok) { break; } - double arg1; - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.ScaleTo:initWithDuration"); - - if (!ok) { break; } - bool ret = cobj->initWithDuration(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 4) { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.ScaleTo:initWithDuration"); - - if (!ok) { break; } - double arg1; - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.ScaleTo:initWithDuration"); - - if (!ok) { break; } - double arg2; - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.ScaleTo:initWithDuration"); - - if (!ok) { break; } - double arg3; - ok &= luaval_to_number(tolua_S, 5,&arg3, "ax.ScaleTo:initWithDuration"); - - if (!ok) { break; } - bool ret = cobj->initWithDuration(arg0, arg1, arg2, arg3); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; + argc = lua_gettop(tolua_S)-1; + if (argc == 0) + { + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_drawScene'", nullptr); + return 0; } - }while(0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.ScaleTo:initWithDuration",argc, 4); + cobj->drawScene(); + lua_settop(tolua_S, 1); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:drawScene",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_ScaleTo_initWithDuration'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_drawScene'.",&tolua_err); #endif return 0; } -int lua_ax_base_ScaleTo_create(lua_State* tolua_S) +int lua_ax_base_Director_purgeCachedData(lua_State* tolua_S) { int argc = 0; + ax::Director* cobj = nullptr; bool ok = true; + #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.ScaleTo",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S)-1; + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); - do - { - if (argc == 3) - { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.ScaleTo:create"); - if (!ok) { break; } - double arg1; - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.ScaleTo:create"); - if (!ok) { break; } - double arg2; - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.ScaleTo:create"); - if (!ok) { break; } - ax::ScaleTo* ret = ax::ScaleTo::create(arg0, arg1, arg2); - object_to_luaval(tolua_S, "ax.ScaleTo",(ax::ScaleTo*)ret); - return 1; - } - } while (0); - ok = true; - do +#if _AX_DEBUG >= 1 + if (!cobj) { - if (argc == 2) - { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.ScaleTo:create"); - if (!ok) { break; } - double arg1; - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.ScaleTo:create"); - if (!ok) { break; } - ax::ScaleTo* ret = ax::ScaleTo::create(arg0, arg1); - object_to_luaval(tolua_S, "ax.ScaleTo",(ax::ScaleTo*)ret); - return 1; - } - } while (0); - ok = true; - do + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_purgeCachedData'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 0) { - if (argc == 4) + if(!ok) { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.ScaleTo:create"); - if (!ok) { break; } - double arg1; - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.ScaleTo:create"); - if (!ok) { break; } - double arg2; - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.ScaleTo:create"); - if (!ok) { break; } - double arg3; - ok &= luaval_to_number(tolua_S, 5,&arg3, "ax.ScaleTo:create"); - if (!ok) { break; } - ax::ScaleTo* ret = ax::ScaleTo::create(arg0, arg1, arg2, arg3); - object_to_luaval(tolua_S, "ax.ScaleTo",(ax::ScaleTo*)ret); - return 1; + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_purgeCachedData'", nullptr); + return 0; } - } while (0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d", "ax.ScaleTo:create",argc, 4); + cobj->purgeCachedData(); + lua_settop(tolua_S, 1); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:purgeCachedData",argc, 0); return 0; + #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_ScaleTo_create'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_purgeCachedData'.",&tolua_err); #endif + return 0; } -int lua_ax_base_ScaleTo_constructor(lua_State* tolua_S) +int lua_ax_base_Director_setDefaultValues(lua_State* tolua_S) { int argc = 0; - ax::ScaleTo* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -27578,137 +27303,93 @@ int lua_ax_base_ScaleTo_constructor(lua_State* tolua_S) #endif +#if _AX_DEBUG >= 1 + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; +#endif + + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_setDefaultValues'", nullptr); + return 0; + } +#endif argc = lua_gettop(tolua_S)-1; if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_ScaleTo_constructor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_setDefaultValues'", nullptr); return 0; } - cobj = new ax::ScaleTo(); - cobj->autorelease(); - int ID = (int)cobj->_ID ; - int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.ScaleTo"); + cobj->setDefaultValues(); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.ScaleTo:ScaleTo",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:setDefaultValues",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_ScaleTo_constructor'.",&tolua_err); + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_setDefaultValues'.",&tolua_err); #endif return 0; } - -static int lua_ax_base_ScaleTo_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (ScaleTo)"); - return 0; -} - -int lua_register_ax_base_ScaleTo(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.ScaleTo"); - tolua_cclass(tolua_S,"ScaleTo","ax.ScaleTo","ax.ActionInterval",nullptr); - - tolua_beginmodule(tolua_S,"ScaleTo"); - tolua_function(tolua_S,"new",lua_ax_base_ScaleTo_constructor); - tolua_function(tolua_S,"initWithDuration",lua_ax_base_ScaleTo_initWithDuration); - tolua_function(tolua_S,"create", lua_ax_base_ScaleTo_create); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::ScaleTo).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.ScaleTo"; - g_typeCast[typeName] = "ax.ScaleTo"; - return 1; -} - -int lua_ax_base_ScaleBy_create(lua_State* tolua_S) +int lua_ax_base_Director_setGLDefaultValues(lua_State* tolua_S) { int argc = 0; + ax::Director* cobj = nullptr; bool ok = true; + #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.ScaleBy",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S)-1; + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); - do - { - if (argc == 3) - { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.ScaleBy:create"); - if (!ok) { break; } - double arg1; - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.ScaleBy:create"); - if (!ok) { break; } - double arg2; - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.ScaleBy:create"); - if (!ok) { break; } - ax::ScaleBy* ret = ax::ScaleBy::create(arg0, arg1, arg2); - object_to_luaval(tolua_S, "ax.ScaleBy",(ax::ScaleBy*)ret); - return 1; - } - } while (0); - ok = true; - do +#if _AX_DEBUG >= 1 + if (!cobj) { - if (argc == 2) - { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.ScaleBy:create"); - if (!ok) { break; } - double arg1; - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.ScaleBy:create"); - if (!ok) { break; } - ax::ScaleBy* ret = ax::ScaleBy::create(arg0, arg1); - object_to_luaval(tolua_S, "ax.ScaleBy",(ax::ScaleBy*)ret); - return 1; - } - } while (0); - ok = true; - do + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_setGLDefaultValues'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 0) { - if (argc == 4) + if(!ok) { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.ScaleBy:create"); - if (!ok) { break; } - double arg1; - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.ScaleBy:create"); - if (!ok) { break; } - double arg2; - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.ScaleBy:create"); - if (!ok) { break; } - double arg3; - ok &= luaval_to_number(tolua_S, 5,&arg3, "ax.ScaleBy:create"); - if (!ok) { break; } - ax::ScaleBy* ret = ax::ScaleBy::create(arg0, arg1, arg2, arg3); - object_to_luaval(tolua_S, "ax.ScaleBy",(ax::ScaleBy*)ret); - return 1; + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_setGLDefaultValues'", nullptr); + return 0; } - } while (0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d", "ax.ScaleBy:create",argc, 4); + cobj->setGLDefaultValues(); + lua_settop(tolua_S, 1); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:setGLDefaultValues",argc, 0); return 0; + #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_ScaleBy_create'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_setGLDefaultValues'.",&tolua_err); #endif + return 0; } -int lua_ax_base_ScaleBy_constructor(lua_State* tolua_S) +int lua_ax_base_Director_setClearColor(lua_State* tolua_S) { int argc = 0; - ax::ScaleBy* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -27716,148 +27397,150 @@ int lua_ax_base_ScaleBy_constructor(lua_State* tolua_S) #endif +#if _AX_DEBUG >= 1 + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; +#endif + + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_setClearColor'", nullptr); + return 0; + } +#endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + ax::Color arg0; + + ok &=luaval_to_color(tolua_S, 2, &arg0, "ax.Director:setClearColor"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_ScaleBy_constructor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_setClearColor'", nullptr); return 0; } - cobj = new ax::ScaleBy(); - cobj->autorelease(); - int ID = (int)cobj->_ID ; - int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.ScaleBy"); + cobj->setClearColor(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.ScaleBy:ScaleBy",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:setClearColor",argc, 1); return 0; #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_ScaleBy_constructor'.",&tolua_err); + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_setClearColor'.",&tolua_err); #endif return 0; } - -static int lua_ax_base_ScaleBy_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (ScaleBy)"); - return 0; -} - -int lua_register_ax_base_ScaleBy(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.ScaleBy"); - tolua_cclass(tolua_S,"ScaleBy","ax.ScaleBy","ax.ScaleTo",nullptr); - - tolua_beginmodule(tolua_S,"ScaleBy"); - tolua_function(tolua_S,"new",lua_ax_base_ScaleBy_constructor); - tolua_function(tolua_S,"create", lua_ax_base_ScaleBy_create); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::ScaleBy).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.ScaleBy"; - g_typeCast[typeName] = "ax.ScaleBy"; - return 1; -} - -int lua_ax_base_Blink_initWithDuration(lua_State* tolua_S) +int lua_ax_base_Director_mainLoop(lua_State* tolua_S) { int argc = 0; - ax::Blink* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; - #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Blink",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - - cobj = (ax::Blink*)tolua_tousertype(tolua_S,1,0); - + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Blink_initWithDuration'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_mainLoop'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - if (argc == 2) - { - double arg0; - int arg1; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Blink:initWithDuration"); + do{ + if (argc == 1) { + double arg0; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Director:mainLoop"); - ok &= luaval_to_int32(tolua_S, 3,(int *)&arg1, "ax.Blink:initWithDuration"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Blink_initWithDuration'", nullptr); - return 0; + if (!ok) { break; } + cobj->mainLoop(arg0); + lua_settop(tolua_S, 1); + return 1; } - auto&& ret = cobj->initWithDuration(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Blink:initWithDuration",argc, 2); + }while(0); + ok = true; + do{ + if (argc == 0) { + cobj->mainLoop(); + lua_settop(tolua_S, 1); + return 1; + } + }while(0); + ok = true; + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:mainLoop",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Blink_initWithDuration'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_mainLoop'.",&tolua_err); #endif return 0; } -int lua_ax_base_Blink_create(lua_State* tolua_S) +int lua_ax_base_Director_setContentScaleFactor(lua_State* tolua_S) { int argc = 0; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.Blink",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S) - 1; + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); - if (argc == 2) +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_setContentScaleFactor'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 1) { double arg0; - int arg1; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Blink:create"); - ok &= luaval_to_int32(tolua_S, 3,(int *)&arg1, "ax.Blink:create"); + + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Director:setContentScaleFactor"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Blink_create'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_setContentScaleFactor'", nullptr); return 0; } - auto&& ret = ax::Blink::create(arg0, arg1); - object_to_luaval(tolua_S, "ax.Blink",(ax::Blink*)ret); + cobj->setContentScaleFactor(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Blink:create",argc, 2); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:setContentScaleFactor",argc, 1); return 0; + #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Blink_create'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_setContentScaleFactor'.",&tolua_err); #endif + return 0; } -int lua_ax_base_Blink_constructor(lua_State* tolua_S) +int lua_ax_base_Director_getContentScaleFactor(lua_State* tolua_S) { int argc = 0; - ax::Blink* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -27865,58 +27548,46 @@ int lua_ax_base_Blink_constructor(lua_State* tolua_S) #endif +#if _AX_DEBUG >= 1 + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; +#endif + + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getContentScaleFactor'", nullptr); + return 0; + } +#endif argc = lua_gettop(tolua_S)-1; if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Blink_constructor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getContentScaleFactor'", nullptr); return 0; } - cobj = new ax::Blink(); - cobj->autorelease(); - int ID = (int)cobj->_ID ; - int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.Blink"); + auto&& ret = cobj->getContentScaleFactor(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Blink:Blink",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getContentScaleFactor",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Blink_constructor'.",&tolua_err); + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getContentScaleFactor'.",&tolua_err); #endif return 0; } - -static int lua_ax_base_Blink_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (Blink)"); - return 0; -} - -int lua_register_ax_base_Blink(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.Blink"); - tolua_cclass(tolua_S,"Blink","ax.Blink","ax.ActionInterval",nullptr); - - tolua_beginmodule(tolua_S,"Blink"); - tolua_function(tolua_S,"new",lua_ax_base_Blink_constructor); - tolua_function(tolua_S,"initWithDuration",lua_ax_base_Blink_initWithDuration); - tolua_function(tolua_S,"create", lua_ax_base_Blink_create); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::Blink).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.Blink"; - g_typeCast[typeName] = "ax.Blink"; - return 1; -} - -int lua_ax_base_FadeTo_initWithDuration(lua_State* tolua_S) +int lua_ax_base_Director_getScheduler(lua_State* tolua_S) { int argc = 0; - ax::FadeTo* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -27925,89 +27596,95 @@ int lua_ax_base_FadeTo_initWithDuration(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.FadeTo",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::FadeTo*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_FadeTo_initWithDuration'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getScheduler'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 2) + if (argc == 0) { - double arg0; - uint16_t arg1; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.FadeTo:initWithDuration"); - - ok &= luaval_to_uint16(tolua_S, 3,&arg1, "ax.FadeTo:initWithDuration"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_FadeTo_initWithDuration'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getScheduler'", nullptr); return 0; } - auto&& ret = cobj->initWithDuration(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); + auto&& ret = cobj->getScheduler(); + object_to_luaval(tolua_S, "ax.Scheduler",(ax::Scheduler*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.FadeTo:initWithDuration",argc, 2); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getScheduler",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_FadeTo_initWithDuration'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getScheduler'.",&tolua_err); #endif return 0; } -int lua_ax_base_FadeTo_create(lua_State* tolua_S) +int lua_ax_base_Director_setScheduler(lua_State* tolua_S) { int argc = 0; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.FadeTo",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S) - 1; + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); - if (argc == 2) +#if _AX_DEBUG >= 1 + if (!cobj) { - double arg0; - uint16_t arg1; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.FadeTo:create"); - ok &= luaval_to_uint16(tolua_S, 3,&arg1, "ax.FadeTo:create"); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_setScheduler'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 1) + { + ax::Scheduler* arg0; + + ok &= luaval_to_object(tolua_S, 2, "ax.Scheduler",&arg0, "ax.Director:setScheduler"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_FadeTo_create'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_setScheduler'", nullptr); return 0; } - auto&& ret = ax::FadeTo::create(arg0, arg1); - object_to_luaval(tolua_S, "ax.FadeTo",(ax::FadeTo*)ret); + cobj->setScheduler(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.FadeTo:create",argc, 2); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:setScheduler",argc, 1); return 0; + #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_FadeTo_create'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_setScheduler'.",&tolua_err); #endif + return 0; } -int lua_ax_base_FadeTo_constructor(lua_State* tolua_S) +int lua_ax_base_Director_getActionManager(lua_State* tolua_S) { int argc = 0; - ax::FadeTo* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -28015,58 +27692,46 @@ int lua_ax_base_FadeTo_constructor(lua_State* tolua_S) #endif +#if _AX_DEBUG >= 1 + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; +#endif + + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getActionManager'", nullptr); + return 0; + } +#endif argc = lua_gettop(tolua_S)-1; if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_FadeTo_constructor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getActionManager'", nullptr); return 0; } - cobj = new ax::FadeTo(); - cobj->autorelease(); - int ID = (int)cobj->_ID ; - int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.FadeTo"); + auto&& ret = cobj->getActionManager(); + object_to_luaval(tolua_S, "ax.ActionManager",(ax::ActionManager*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.FadeTo:FadeTo",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getActionManager",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_FadeTo_constructor'.",&tolua_err); + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getActionManager'.",&tolua_err); #endif return 0; } - -static int lua_ax_base_FadeTo_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (FadeTo)"); - return 0; -} - -int lua_register_ax_base_FadeTo(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.FadeTo"); - tolua_cclass(tolua_S,"FadeTo","ax.FadeTo","ax.ActionInterval",nullptr); - - tolua_beginmodule(tolua_S,"FadeTo"); - tolua_function(tolua_S,"new",lua_ax_base_FadeTo_constructor); - tolua_function(tolua_S,"initWithDuration",lua_ax_base_FadeTo_initWithDuration); - tolua_function(tolua_S,"create", lua_ax_base_FadeTo_create); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::FadeTo).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.FadeTo"; - g_typeCast[typeName] = "ax.FadeTo"; - return 1; -} - -int lua_ax_base_FadeIn_setReverseAction(lua_State* tolua_S) +int lua_ax_base_Director_setActionManager(lua_State* tolua_S) { int argc = 0; - ax::FadeIn* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -28075,15 +27740,15 @@ int lua_ax_base_FadeIn_setReverseAction(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.FadeIn",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::FadeIn*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_FadeIn_setReverseAction'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_setActionManager'", nullptr); return 0; } #endif @@ -28091,68 +27756,79 @@ int lua_ax_base_FadeIn_setReverseAction(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::FadeTo* arg0; + ax::ActionManager* arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.FadeTo",&arg0, "ax.FadeIn:setReverseAction"); + ok &= luaval_to_object(tolua_S, 2, "ax.ActionManager",&arg0, "ax.Director:setActionManager"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_FadeIn_setReverseAction'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_setActionManager'", nullptr); return 0; } - cobj->setReverseAction(arg0); + cobj->setActionManager(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.FadeIn:setReverseAction",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:setActionManager",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_FadeIn_setReverseAction'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_setActionManager'.",&tolua_err); #endif return 0; } -int lua_ax_base_FadeIn_create(lua_State* tolua_S) +int lua_ax_base_Director_getEventDispatcher(lua_State* tolua_S) { int argc = 0; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.FadeIn",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S) - 1; + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); - if (argc == 1) +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getEventDispatcher'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 0) { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.FadeIn:create"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_FadeIn_create'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getEventDispatcher'", nullptr); return 0; } - auto&& ret = ax::FadeIn::create(arg0); - object_to_luaval(tolua_S, "ax.FadeIn",(ax::FadeIn*)ret); + auto&& ret = cobj->getEventDispatcher(); + object_to_luaval(tolua_S, "ax.EventDispatcher",(ax::EventDispatcher*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.FadeIn:create",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getEventDispatcher",argc, 0); return 0; + #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_FadeIn_create'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getEventDispatcher'.",&tolua_err); #endif + return 0; } -int lua_ax_base_FadeIn_constructor(lua_State* tolua_S) +int lua_ax_base_Director_setEventDispatcher(lua_State* tolua_S) { int argc = 0; - ax::FadeIn* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -28160,58 +27836,49 @@ int lua_ax_base_FadeIn_constructor(lua_State* tolua_S) #endif +#if _AX_DEBUG >= 1 + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; +#endif + + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_setEventDispatcher'", nullptr); + return 0; + } +#endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + ax::EventDispatcher* arg0; + + ok &= luaval_to_object(tolua_S, 2, "ax.EventDispatcher",&arg0, "ax.Director:setEventDispatcher"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_FadeIn_constructor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_setEventDispatcher'", nullptr); return 0; } - cobj = new ax::FadeIn(); - cobj->autorelease(); - int ID = (int)cobj->_ID ; - int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.FadeIn"); + cobj->setEventDispatcher(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.FadeIn:FadeIn",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:setEventDispatcher",argc, 1); return 0; #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_FadeIn_constructor'.",&tolua_err); + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_setEventDispatcher'.",&tolua_err); #endif return 0; } - -static int lua_ax_base_FadeIn_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (FadeIn)"); - return 0; -} - -int lua_register_ax_base_FadeIn(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.FadeIn"); - tolua_cclass(tolua_S,"FadeIn","ax.FadeIn","ax.FadeTo",nullptr); - - tolua_beginmodule(tolua_S,"FadeIn"); - tolua_function(tolua_S,"new",lua_ax_base_FadeIn_constructor); - tolua_function(tolua_S,"setReverseAction",lua_ax_base_FadeIn_setReverseAction); - tolua_function(tolua_S,"create", lua_ax_base_FadeIn_create); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::FadeIn).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.FadeIn"; - g_typeCast[typeName] = "ax.FadeIn"; - return 1; -} - -int lua_ax_base_FadeOut_setReverseAction(lua_State* tolua_S) +int lua_ax_base_Director_getRenderer(lua_State* tolua_S) { int argc = 0; - ax::FadeOut* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -28220,84 +27887,92 @@ int lua_ax_base_FadeOut_setReverseAction(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.FadeOut",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::FadeOut*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_FadeOut_setReverseAction'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getRenderer'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - ax::FadeTo* arg0; - - ok &= luaval_to_object(tolua_S, 2, "ax.FadeTo",&arg0, "ax.FadeOut:setReverseAction"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_FadeOut_setReverseAction'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getRenderer'", nullptr); return 0; } - cobj->setReverseAction(arg0); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getRenderer(); + object_to_luaval(tolua_S, "ax.Renderer",(ax::Renderer*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.FadeOut:setReverseAction",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getRenderer",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_FadeOut_setReverseAction'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getRenderer'.",&tolua_err); #endif return 0; } -int lua_ax_base_FadeOut_create(lua_State* tolua_S) +int lua_ax_base_Director_getDeltaTime(lua_State* tolua_S) { int argc = 0; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.FadeOut",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S) - 1; + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); - if (argc == 1) +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getDeltaTime'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 0) { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.FadeOut:create"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_FadeOut_create'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getDeltaTime'", nullptr); return 0; } - auto&& ret = ax::FadeOut::create(arg0); - object_to_luaval(tolua_S, "ax.FadeOut",(ax::FadeOut*)ret); + auto&& ret = cobj->getDeltaTime(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.FadeOut:create",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getDeltaTime",argc, 0); return 0; + #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_FadeOut_create'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getDeltaTime'.",&tolua_err); #endif + return 0; } -int lua_ax_base_FadeOut_constructor(lua_State* tolua_S) +int lua_ax_base_Director_getFrameRate(lua_State* tolua_S) { int argc = 0; - ax::FadeOut* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -28305,58 +27980,46 @@ int lua_ax_base_FadeOut_constructor(lua_State* tolua_S) #endif +#if _AX_DEBUG >= 1 + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; +#endif + + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getFrameRate'", nullptr); + return 0; + } +#endif argc = lua_gettop(tolua_S)-1; if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_FadeOut_constructor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getFrameRate'", nullptr); return 0; } - cobj = new ax::FadeOut(); - cobj->autorelease(); - int ID = (int)cobj->_ID ; - int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.FadeOut"); + auto&& ret = cobj->getFrameRate(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.FadeOut:FadeOut",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getFrameRate",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_FadeOut_constructor'.",&tolua_err); + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getFrameRate'.",&tolua_err); #endif return 0; } - -static int lua_ax_base_FadeOut_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (FadeOut)"); - return 0; -} - -int lua_register_ax_base_FadeOut(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.FadeOut"); - tolua_cclass(tolua_S,"FadeOut","ax.FadeOut","ax.FadeTo",nullptr); - - tolua_beginmodule(tolua_S,"FadeOut"); - tolua_function(tolua_S,"new",lua_ax_base_FadeOut_constructor); - tolua_function(tolua_S,"setReverseAction",lua_ax_base_FadeOut_setReverseAction); - tolua_function(tolua_S,"create", lua_ax_base_FadeOut_create); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::FadeOut).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.FadeOut"; - g_typeCast[typeName] = "ax.FadeOut"; - return 1; -} - -int lua_ax_base_TintTo_initWithDuration(lua_State* tolua_S) +int lua_ax_base_Director_pushMatrix(lua_State* tolua_S) { int argc = 0; - ax::TintTo* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -28365,117 +28028,98 @@ int lua_ax_base_TintTo_initWithDuration(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.TintTo",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::TintTo*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_TintTo_initWithDuration'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_pushMatrix'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 4) + if (argc == 1) { - double arg0; - uint16_t arg1; - uint16_t arg2; - uint16_t arg3; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.TintTo:initWithDuration"); - - ok &= luaval_to_uint16(tolua_S, 3,&arg1, "ax.TintTo:initWithDuration"); - - ok &= luaval_to_uint16(tolua_S, 4,&arg2, "ax.TintTo:initWithDuration"); + ax::MATRIX_STACK_TYPE arg0; - ok &= luaval_to_uint16(tolua_S, 5,&arg3, "ax.TintTo:initWithDuration"); + ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Director:pushMatrix"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_TintTo_initWithDuration'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_pushMatrix'", nullptr); return 0; } - auto&& ret = cobj->initWithDuration(arg0, arg1, arg2, arg3); - tolua_pushboolean(tolua_S,(bool)ret); + cobj->pushMatrix(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.TintTo:initWithDuration",argc, 4); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:pushMatrix",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_TintTo_initWithDuration'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_pushMatrix'.",&tolua_err); #endif return 0; } -int lua_ax_base_TintTo_create(lua_State* tolua_S) +int lua_ax_base_Director_popMatrix(lua_State* tolua_S) { int argc = 0; + ax::Director* cobj = nullptr; bool ok = true; + #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.TintTo",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S)-1; + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); - do +#if _AX_DEBUG >= 1 + if (!cobj) { - if (argc == 2) - { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.TintTo:create"); - if (!ok) { break; } - ax::Color3B arg1; - ok &= luaval_to_color3b(tolua_S, 3, &arg1, "ax.TintTo:create"); - if (!ok) { break; } - ax::TintTo* ret = ax::TintTo::create(arg0, arg1); - object_to_luaval(tolua_S, "ax.TintTo",(ax::TintTo*)ret); - return 1; - } - } while (0); - ok = true; - do + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_popMatrix'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 1) { - if (argc == 4) + ax::MATRIX_STACK_TYPE arg0; + + ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Director:popMatrix"); + if(!ok) { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.TintTo:create"); - if (!ok) { break; } - uint16_t arg1; - ok &= luaval_to_uint16(tolua_S, 3,&arg1, "ax.TintTo:create"); - if (!ok) { break; } - uint16_t arg2; - ok &= luaval_to_uint16(tolua_S, 4,&arg2, "ax.TintTo:create"); - if (!ok) { break; } - uint16_t arg3; - ok &= luaval_to_uint16(tolua_S, 5,&arg3, "ax.TintTo:create"); - if (!ok) { break; } - ax::TintTo* ret = ax::TintTo::create(arg0, arg1, arg2, arg3); - object_to_luaval(tolua_S, "ax.TintTo",(ax::TintTo*)ret); - return 1; + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_popMatrix'", nullptr); + return 0; } - } while (0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d", "ax.TintTo:create",argc, 4); + cobj->popMatrix(arg0); + lua_settop(tolua_S, 1); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:popMatrix",argc, 1); return 0; + #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_TintTo_create'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_popMatrix'.",&tolua_err); #endif + return 0; } -int lua_ax_base_TintTo_constructor(lua_State* tolua_S) +int lua_ax_base_Director_loadIdentityMatrix(lua_State* tolua_S) { int argc = 0; - ax::TintTo* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -28483,58 +28127,49 @@ int lua_ax_base_TintTo_constructor(lua_State* tolua_S) #endif +#if _AX_DEBUG >= 1 + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; +#endif + + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_loadIdentityMatrix'", nullptr); + return 0; + } +#endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + ax::MATRIX_STACK_TYPE arg0; + + ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Director:loadIdentityMatrix"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_TintTo_constructor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_loadIdentityMatrix'", nullptr); return 0; } - cobj = new ax::TintTo(); - cobj->autorelease(); - int ID = (int)cobj->_ID ; - int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.TintTo"); + cobj->loadIdentityMatrix(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.TintTo:TintTo",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:loadIdentityMatrix",argc, 1); return 0; #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_TintTo_constructor'.",&tolua_err); + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_loadIdentityMatrix'.",&tolua_err); #endif return 0; } - -static int lua_ax_base_TintTo_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (TintTo)"); - return 0; -} - -int lua_register_ax_base_TintTo(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.TintTo"); - tolua_cclass(tolua_S,"TintTo","ax.TintTo","ax.ActionInterval",nullptr); - - tolua_beginmodule(tolua_S,"TintTo"); - tolua_function(tolua_S,"new",lua_ax_base_TintTo_constructor); - tolua_function(tolua_S,"initWithDuration",lua_ax_base_TintTo_initWithDuration); - tolua_function(tolua_S,"create", lua_ax_base_TintTo_create); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::TintTo).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.TintTo"; - g_typeCast[typeName] = "ax.TintTo"; - return 1; -} - -int lua_ax_base_TintBy_initWithDuration(lua_State* tolua_S) +int lua_ax_base_Director_loadMatrix(lua_State* tolua_S) { int argc = 0; - ax::TintBy* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -28543,99 +28178,104 @@ int lua_ax_base_TintBy_initWithDuration(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.TintBy",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::TintBy*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_TintBy_initWithDuration'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_loadMatrix'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 4) + if (argc == 2) { - double arg0; - int32_t arg1; - int32_t arg2; - int32_t arg3; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.TintBy:initWithDuration"); - - ok &= luaval_to_int32(tolua_S, 3,&arg1, "ax.TintBy:initWithDuration"); + ax::MATRIX_STACK_TYPE arg0; + ax::Mat4 arg1; - ok &= luaval_to_int32(tolua_S, 4,&arg2, "ax.TintBy:initWithDuration"); + ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Director:loadMatrix"); - ok &= luaval_to_int32(tolua_S, 5,&arg3, "ax.TintBy:initWithDuration"); + ok &= luaval_to_mat4(tolua_S, 3, &arg1, "ax.Director:loadMatrix"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_TintBy_initWithDuration'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_loadMatrix'", nullptr); return 0; } - auto&& ret = cobj->initWithDuration(arg0, arg1, arg2, arg3); - tolua_pushboolean(tolua_S,(bool)ret); + cobj->loadMatrix(arg0, arg1); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.TintBy:initWithDuration",argc, 4); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:loadMatrix",argc, 2); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_TintBy_initWithDuration'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_loadMatrix'.",&tolua_err); #endif return 0; } -int lua_ax_base_TintBy_create(lua_State* tolua_S) +int lua_ax_base_Director_multiplyMatrix(lua_State* tolua_S) { int argc = 0; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.TintBy",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S) - 1; + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); - if (argc == 4) +#if _AX_DEBUG >= 1 + if (!cobj) { - double arg0; - int32_t arg1; - int32_t arg2; - int32_t arg3; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.TintBy:create"); - ok &= luaval_to_int32(tolua_S, 3,&arg1, "ax.TintBy:create"); - ok &= luaval_to_int32(tolua_S, 4,&arg2, "ax.TintBy:create"); - ok &= luaval_to_int32(tolua_S, 5,&arg3, "ax.TintBy:create"); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_multiplyMatrix'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 2) + { + ax::MATRIX_STACK_TYPE arg0; + ax::Mat4 arg1; + + ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Director:multiplyMatrix"); + + ok &= luaval_to_mat4(tolua_S, 3, &arg1, "ax.Director:multiplyMatrix"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_TintBy_create'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_multiplyMatrix'", nullptr); return 0; } - auto&& ret = ax::TintBy::create(arg0, arg1, arg2, arg3); - object_to_luaval(tolua_S, "ax.TintBy",(ax::TintBy*)ret); + cobj->multiplyMatrix(arg0, arg1); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.TintBy:create",argc, 4); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:multiplyMatrix",argc, 2); return 0; + #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_TintBy_create'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_multiplyMatrix'.",&tolua_err); #endif + return 0; } -int lua_ax_base_TintBy_constructor(lua_State* tolua_S) +int lua_ax_base_Director_getMatrix(lua_State* tolua_S) { int argc = 0; - ax::TintBy* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -28643,94 +28283,96 @@ int lua_ax_base_TintBy_constructor(lua_State* tolua_S) #endif +#if _AX_DEBUG >= 1 + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; +#endif + + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getMatrix'", nullptr); + return 0; + } +#endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + ax::MATRIX_STACK_TYPE arg0; + + ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Director:getMatrix"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_TintBy_constructor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getMatrix'", nullptr); return 0; } - cobj = new ax::TintBy(); - cobj->autorelease(); - int ID = (int)cobj->_ID ; - int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.TintBy"); + auto&& ret = cobj->getMatrix(arg0); + mat4_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.TintBy:TintBy",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getMatrix",argc, 1); return 0; #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_TintBy_constructor'.",&tolua_err); + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getMatrix'.",&tolua_err); #endif return 0; } - -static int lua_ax_base_TintBy_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (TintBy)"); - return 0; -} - -int lua_register_ax_base_TintBy(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.TintBy"); - tolua_cclass(tolua_S,"TintBy","ax.TintBy","ax.ActionInterval",nullptr); - - tolua_beginmodule(tolua_S,"TintBy"); - tolua_function(tolua_S,"new",lua_ax_base_TintBy_constructor); - tolua_function(tolua_S,"initWithDuration",lua_ax_base_TintBy_initWithDuration); - tolua_function(tolua_S,"create", lua_ax_base_TintBy_create); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::TintBy).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.TintBy"; - g_typeCast[typeName] = "ax.TintBy"; - return 1; -} - -int lua_ax_base_DelayTime_create(lua_State* tolua_S) +int lua_ax_base_Director_resetMatrixStack(lua_State* tolua_S) { int argc = 0; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.DelayTime",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S) - 1; + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); - if (argc == 1) +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_resetMatrixStack'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 0) { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.DelayTime:create"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_DelayTime_create'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_resetMatrixStack'", nullptr); return 0; } - auto&& ret = ax::DelayTime::create(arg0); - object_to_luaval(tolua_S, "ax.DelayTime",(ax::DelayTime*)ret); + cobj->resetMatrixStack(); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.DelayTime:create",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:resetMatrixStack",argc, 0); return 0; + #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_DelayTime_create'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_resetMatrixStack'.",&tolua_err); #endif + return 0; } -int lua_ax_base_DelayTime_constructor(lua_State* tolua_S) +int lua_ax_base_Director_getAxmolThreadId(lua_State* tolua_S) { int argc = 0; - ax::DelayTime* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -28738,57 +28380,46 @@ int lua_ax_base_DelayTime_constructor(lua_State* tolua_S) #endif +#if _AX_DEBUG >= 1 + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; +#endif + + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_getAxmolThreadId'", nullptr); + return 0; + } +#endif argc = lua_gettop(tolua_S)-1; if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_DelayTime_constructor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getAxmolThreadId'", nullptr); return 0; } - cobj = new ax::DelayTime(); - cobj->autorelease(); - int ID = (int)cobj->_ID ; - int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.DelayTime"); + auto&& ret = cobj->getAxmolThreadId(); + std_thread_id_to_luaval(tolua_S, ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.DelayTime:DelayTime",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:getAxmolThreadId",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_DelayTime_constructor'.",&tolua_err); + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getAxmolThreadId'.",&tolua_err); #endif return 0; } - -static int lua_ax_base_DelayTime_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (DelayTime)"); - return 0; -} - -int lua_register_ax_base_DelayTime(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.DelayTime"); - tolua_cclass(tolua_S,"DelayTime","ax.DelayTime","ax.ActionInterval",nullptr); - - tolua_beginmodule(tolua_S,"DelayTime"); - tolua_function(tolua_S,"new",lua_ax_base_DelayTime_constructor); - tolua_function(tolua_S,"create", lua_ax_base_DelayTime_create); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::DelayTime).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.DelayTime"; - g_typeCast[typeName] = "ax.DelayTime"; - return 1; -} - -int lua_ax_base_Animate_setAnimation(lua_State* tolua_S) +int lua_ax_base_Director_setChildrenIndexerEnabled(lua_State* tolua_S) { int argc = 0; - ax::Animate* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -28797,15 +28428,15 @@ int lua_ax_base_Animate_setAnimation(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Animate",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Animate*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animate_setAnimation'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_setChildrenIndexerEnabled'", nullptr); return 0; } #endif @@ -28813,79 +28444,79 @@ int lua_ax_base_Animate_setAnimation(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Animation* arg0; + bool arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.Animation",&arg0, "ax.Animate:setAnimation"); + ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.Director:setChildrenIndexerEnabled"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animate_setAnimation'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_setChildrenIndexerEnabled'", nullptr); return 0; } - cobj->setAnimation(arg0); + cobj->setChildrenIndexerEnabled(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animate:setAnimation",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:setChildrenIndexerEnabled",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animate_setAnimation'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_setChildrenIndexerEnabled'.",&tolua_err); #endif return 0; } -int lua_ax_base_Animate_getAnimation(lua_State* tolua_S) +int lua_ax_base_Director_isChildrenIndexerEnabled(lua_State* tolua_S) { int argc = 0; - ax::Animate* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; + #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Animate",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Animate*)tolua_tousertype(tolua_S,1,0); + + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); + #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animate_getAnimation'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_isChildrenIndexerEnabled'", nullptr); return 0; } #endif + argc = lua_gettop(tolua_S)-1; - do{ - if (argc == 0) { - const ax::Animation* ret = cobj->getAnimation(); - object_to_luaval(tolua_S, "ax.Animation",(ax::Animation*)ret); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 0) { - ax::Animation* ret = cobj->getAnimation(); - object_to_luaval(tolua_S, "ax.Animation",(ax::Animation*)ret); - return 1; + if (argc == 0) + { + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_isChildrenIndexerEnabled'", nullptr); + return 0; } - }while(0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animate:getAnimation",argc, 0); + auto&& ret = cobj->isChildrenIndexerEnabled(); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:isChildrenIndexerEnabled",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animate_getAnimation'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_isChildrenIndexerEnabled'.",&tolua_err); #endif return 0; } -int lua_ax_base_Animate_getCurrentFrameIndex(lua_State* tolua_S) +int lua_ax_base_Director_isValid(lua_State* tolua_S) { int argc = 0; - ax::Animate* cobj = nullptr; + ax::Director* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -28894,15 +28525,15 @@ int lua_ax_base_Animate_getCurrentFrameIndex(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Animate",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Animate*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Director*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animate_getCurrentFrameIndex'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Director_isValid'", nullptr); return 0; } #endif @@ -28912,74 +28543,58 @@ int lua_ax_base_Animate_getCurrentFrameIndex(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animate_getCurrentFrameIndex'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_isValid'", nullptr); return 0; } - auto&& ret = cobj->getCurrentFrameIndex(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + auto&& ret = cobj->isValid(); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animate:getCurrentFrameIndex",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Director:isValid",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animate_getCurrentFrameIndex'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_isValid'.",&tolua_err); #endif return 0; } -int lua_ax_base_Animate_initWithAnimation(lua_State* tolua_S) +int lua_ax_base_Director_getInstance(lua_State* tolua_S) { int argc = 0; - ax::Animate* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Animate",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Animate*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Animate_initWithAnimation'", nullptr); - return 0; - } -#endif + argc = lua_gettop(tolua_S) - 1; - argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - ax::Animation* arg0; - - ok &= luaval_to_object(tolua_S, 2, "ax.Animation",&arg0, "ax.Animate:initWithAnimation"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animate_initWithAnimation'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_getInstance'", nullptr); return 0; } - auto&& ret = cobj->initWithAnimation(arg0); - tolua_pushboolean(tolua_S,(bool)ret); + auto&& ret = ax::Director::getInstance(); + object_to_luaval(tolua_S, "ax.Director",(ax::Director*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animate:initWithAnimation",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Director:getInstance",argc, 0); return 0; - #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animate_initWithAnimation'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_getInstance'.",&tolua_err); #endif - return 0; } -int lua_ax_base_Animate_create(lua_State* tolua_S) +int lua_ax_base_Director_destroyInstance(lua_State* tolua_S) { int argc = 0; bool ok = true; @@ -28989,36 +28604,124 @@ int lua_ax_base_Animate_create(lua_State* tolua_S) #endif #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.Animate",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.Director",0,&tolua_err)) goto tolua_lerror; #endif argc = lua_gettop(tolua_S) - 1; - if (argc == 1) + if (argc == 0) { - ax::Animation* arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.Animation",&arg0, "ax.Animate:create"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animate_create'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Director_destroyInstance'", nullptr); return 0; } - auto&& ret = ax::Animate::create(arg0); - object_to_luaval(tolua_S, "ax.Animate",(ax::Animate*)ret); + ax::Director::destroyInstance(); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Animate:create",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Director:destroyInstance",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animate_create'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Director_destroyInstance'.",&tolua_err); #endif return 0; } -int lua_ax_base_Animate_constructor(lua_State* tolua_S) +static int lua_ax_base_Director_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (Director)"); + return 0; +} + +int lua_register_ax_base_Director(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.Director"); + tolua_cclass(tolua_S,"Director","ax.Director","",nullptr); + + tolua_beginmodule(tolua_S,"Director"); + tolua_function(tolua_S,"init",lua_ax_base_Director_init); + tolua_function(tolua_S,"getRunningScene",lua_ax_base_Director_getRunningScene); + tolua_function(tolua_S,"getNextScene",lua_ax_base_Director_getNextScene); + tolua_function(tolua_S,"getAnimationInterval",lua_ax_base_Director_getAnimationInterval); + tolua_function(tolua_S,"setAnimationInterval",lua_ax_base_Director_setAnimationInterval); + tolua_function(tolua_S,"isStatsDisplay",lua_ax_base_Director_isStatsDisplay); + tolua_function(tolua_S,"setStatsDisplay",lua_ax_base_Director_setStatsDisplay); + tolua_function(tolua_S,"getSecondsPerFrame",lua_ax_base_Director_getSecondsPerFrame); + tolua_function(tolua_S,"setStatsAnchor",lua_ax_base_Director_setStatsAnchor); + tolua_function(tolua_S,"getGLView",lua_ax_base_Director_getGLView); + tolua_function(tolua_S,"setGLView",lua_ax_base_Director_setGLView); + tolua_function(tolua_S,"getTextureCache",lua_ax_base_Director_getTextureCache); + tolua_function(tolua_S,"isNextDeltaTimeZero",lua_ax_base_Director_isNextDeltaTimeZero); + tolua_function(tolua_S,"setNextDeltaTimeZero",lua_ax_base_Director_setNextDeltaTimeZero); + tolua_function(tolua_S,"isPaused",lua_ax_base_Director_isPaused); + tolua_function(tolua_S,"getTotalFrames",lua_ax_base_Director_getTotalFrames); + tolua_function(tolua_S,"setProjection",lua_ax_base_Director_setProjection); + tolua_function(tolua_S,"setViewport",lua_ax_base_Director_setViewport); + tolua_function(tolua_S,"isSendCleanupToScene",lua_ax_base_Director_isSendCleanupToScene); + tolua_function(tolua_S,"getNotificationNode",lua_ax_base_Director_getNotificationNode); + tolua_function(tolua_S,"setNotificationNode",lua_ax_base_Director_setNotificationNode); + tolua_function(tolua_S,"getWinSize",lua_ax_base_Director_getWinSize); + tolua_function(tolua_S,"getWinSizeInPixels",lua_ax_base_Director_getWinSizeInPixels); + tolua_function(tolua_S,"getVisibleSize",lua_ax_base_Director_getVisibleSize); + tolua_function(tolua_S,"getVisibleOrigin",lua_ax_base_Director_getVisibleOrigin); + tolua_function(tolua_S,"getSafeAreaRect",lua_ax_base_Director_getSafeAreaRect); + tolua_function(tolua_S,"convertToGL",lua_ax_base_Director_convertToGL); + tolua_function(tolua_S,"convertToUI",lua_ax_base_Director_convertToUI); + tolua_function(tolua_S,"getZEye",lua_ax_base_Director_getZEye); + tolua_function(tolua_S,"runWithScene",lua_ax_base_Director_runWithScene); + tolua_function(tolua_S,"pushScene",lua_ax_base_Director_pushScene); + tolua_function(tolua_S,"popScene",lua_ax_base_Director_popScene); + tolua_function(tolua_S,"popToRootScene",lua_ax_base_Director_popToRootScene); + tolua_function(tolua_S,"popToSceneStackLevel",lua_ax_base_Director_popToSceneStackLevel); + tolua_function(tolua_S,"replaceScene",lua_ax_base_Director_replaceScene); + tolua_function(tolua_S,"endToLua",lua_ax_base_Director_end); + tolua_function(tolua_S,"pause",lua_ax_base_Director_pause); + tolua_function(tolua_S,"resume",lua_ax_base_Director_resume); + tolua_function(tolua_S,"restart",lua_ax_base_Director_restart); + tolua_function(tolua_S,"stopAnimation",lua_ax_base_Director_stopAnimation); + tolua_function(tolua_S,"startAnimation",lua_ax_base_Director_startAnimation); + tolua_function(tolua_S,"drawScene",lua_ax_base_Director_drawScene); + tolua_function(tolua_S,"purgeCachedData",lua_ax_base_Director_purgeCachedData); + tolua_function(tolua_S,"setDefaultValues",lua_ax_base_Director_setDefaultValues); + tolua_function(tolua_S,"setGLDefaultValues",lua_ax_base_Director_setGLDefaultValues); + tolua_function(tolua_S,"setClearColor",lua_ax_base_Director_setClearColor); + tolua_function(tolua_S,"mainLoop",lua_ax_base_Director_mainLoop); + tolua_function(tolua_S,"setContentScaleFactor",lua_ax_base_Director_setContentScaleFactor); + tolua_function(tolua_S,"getContentScaleFactor",lua_ax_base_Director_getContentScaleFactor); + tolua_function(tolua_S,"getScheduler",lua_ax_base_Director_getScheduler); + tolua_function(tolua_S,"setScheduler",lua_ax_base_Director_setScheduler); + tolua_function(tolua_S,"getActionManager",lua_ax_base_Director_getActionManager); + tolua_function(tolua_S,"setActionManager",lua_ax_base_Director_setActionManager); + tolua_function(tolua_S,"getEventDispatcher",lua_ax_base_Director_getEventDispatcher); + tolua_function(tolua_S,"setEventDispatcher",lua_ax_base_Director_setEventDispatcher); + tolua_function(tolua_S,"getRenderer",lua_ax_base_Director_getRenderer); + tolua_function(tolua_S,"getDeltaTime",lua_ax_base_Director_getDeltaTime); + tolua_function(tolua_S,"getFrameRate",lua_ax_base_Director_getFrameRate); + tolua_function(tolua_S,"pushMatrix",lua_ax_base_Director_pushMatrix); + tolua_function(tolua_S,"popMatrix",lua_ax_base_Director_popMatrix); + tolua_function(tolua_S,"loadIdentityMatrix",lua_ax_base_Director_loadIdentityMatrix); + tolua_function(tolua_S,"loadMatrix",lua_ax_base_Director_loadMatrix); + tolua_function(tolua_S,"multiplyMatrix",lua_ax_base_Director_multiplyMatrix); + tolua_function(tolua_S,"getMatrix",lua_ax_base_Director_getMatrix); + tolua_function(tolua_S,"resetMatrixStack",lua_ax_base_Director_resetMatrixStack); + tolua_function(tolua_S,"getAxmolThreadId",lua_ax_base_Director_getAxmolThreadId); + tolua_function(tolua_S,"setChildrenIndexerEnabled",lua_ax_base_Director_setChildrenIndexerEnabled); + tolua_function(tolua_S,"isChildrenIndexerEnabled",lua_ax_base_Director_isChildrenIndexerEnabled); + tolua_function(tolua_S,"isValid",lua_ax_base_Director_isValid); + tolua_function(tolua_S,"getInstance", lua_ax_base_Director_getInstance); + tolua_function(tolua_S,"destroyInstance", lua_ax_base_Director_destroyInstance); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::Director).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.Director"; + g_typeCast[typeName] = "ax.Director"; + return 1; +} + +int lua_ax_base_Properties_getNextProperty(lua_State* tolua_S) { int argc = 0; - ax::Animate* cobj = nullptr; + ax::Properties* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -29026,61 +28729,93 @@ int lua_ax_base_Animate_constructor(lua_State* tolua_S) #endif +#if _AX_DEBUG >= 1 + if (!tolua_isusertype(tolua_S,1,"ax.Properties",0,&tolua_err)) goto tolua_lerror; +#endif + + cobj = (ax::Properties*)tolua_tousertype(tolua_S,1,0); + +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_getNextProperty'", nullptr); + return 0; + } +#endif argc = lua_gettop(tolua_S)-1; if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Animate_constructor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getNextProperty'", nullptr); return 0; } - cobj = new ax::Animate(); - cobj->autorelease(); - int ID = (int)cobj->_ID ; - int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.Animate"); + auto&& ret = cobj->getNextProperty(); + tolua_pushstring(tolua_S,(const char*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Animate:Animate",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:getNextProperty",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Animate_constructor'.",&tolua_err); + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_getNextProperty'.",&tolua_err); #endif return 0; } - -static int lua_ax_base_Animate_finalize(lua_State* tolua_S) +int lua_ax_base_Properties_getNextNamespace(lua_State* tolua_S) { - AXLOGV("luabindings: finalizing LUA object (Animate)"); + int argc = 0; + ax::Properties* cobj = nullptr; + bool ok = true; + +#if _AX_DEBUG >= 1 + tolua_Error tolua_err; +#endif + + +#if _AX_DEBUG >= 1 + if (!tolua_isusertype(tolua_S,1,"ax.Properties",0,&tolua_err)) goto tolua_lerror; +#endif + + cobj = (ax::Properties*)tolua_tousertype(tolua_S,1,0); + +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_getNextNamespace'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 0) + { + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getNextNamespace'", nullptr); + return 0; + } + auto&& ret = cobj->getNextNamespace(); + object_to_luaval(tolua_S, "ax.Properties",(ax::Properties*)ret); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:getNextNamespace",argc, 0); return 0; -} -int lua_register_ax_base_Animate(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.Animate"); - tolua_cclass(tolua_S,"Animate","ax.Animate","ax.ActionInterval",nullptr); +#if _AX_DEBUG >= 1 + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_getNextNamespace'.",&tolua_err); +#endif - tolua_beginmodule(tolua_S,"Animate"); - tolua_function(tolua_S,"new",lua_ax_base_Animate_constructor); - tolua_function(tolua_S,"setAnimation",lua_ax_base_Animate_setAnimation); - tolua_function(tolua_S,"getAnimation",lua_ax_base_Animate_getAnimation); - tolua_function(tolua_S,"getCurrentFrameIndex",lua_ax_base_Animate_getCurrentFrameIndex); - tolua_function(tolua_S,"initWithAnimation",lua_ax_base_Animate_initWithAnimation); - tolua_function(tolua_S,"create", lua_ax_base_Animate_create); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::Animate).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.Animate"; - g_typeCast[typeName] = "ax.Animate"; - return 1; + return 0; } - -int lua_ax_base_TargetedAction_setForcedTarget(lua_State* tolua_S) +int lua_ax_base_Properties_rewind(lua_State* tolua_S) { int argc = 0; - ax::TargetedAction* cobj = nullptr; + ax::Properties* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -29089,95 +28824,132 @@ int lua_ax_base_TargetedAction_setForcedTarget(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.TargetedAction",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Properties",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::TargetedAction*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Properties*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_TargetedAction_setForcedTarget'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_rewind'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 0) { - ax::Node* arg0; - - ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.TargetedAction:setForcedTarget"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_TargetedAction_setForcedTarget'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_rewind'", nullptr); return 0; } - cobj->setForcedTarget(arg0); + cobj->rewind(); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.TargetedAction:setForcedTarget",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:rewind",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_TargetedAction_setForcedTarget'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_rewind'.",&tolua_err); #endif return 0; } -int lua_ax_base_TargetedAction_getForcedTarget(lua_State* tolua_S) +int lua_ax_base_Properties_getNamespace(lua_State* tolua_S) { int argc = 0; - ax::TargetedAction* cobj = nullptr; + ax::Properties* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.TargetedAction",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Properties",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::TargetedAction*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Properties*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_TargetedAction_getForcedTarget'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_getNamespace'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; do{ - if (argc == 0) { - const ax::Node* ret = cobj->getForcedTarget(); - object_to_luaval(tolua_S, "ax.Node",(ax::Node*)ret); + if (argc == 0) { + const char* ret = cobj->getNamespace(); + tolua_pushstring(tolua_S,(const char*)ret); + return 1; + } + }while(0); + ok = true; + do{ + if (argc == 1) { + std::string_view arg0; + ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Properties:getNamespace"); + + if (!ok) { break; } + ax::Properties* ret = cobj->getNamespace(arg0); + object_to_luaval(tolua_S, "ax.Properties",(ax::Properties*)ret); + return 1; + } + }while(0); + ok = true; + do{ + if (argc == 2) { + std::string_view arg0; + ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Properties:getNamespace"); + + if (!ok) { break; } + bool arg1; + ok &= luaval_to_boolean(tolua_S, 3,&arg1, "ax.Properties:getNamespace"); + + if (!ok) { break; } + ax::Properties* ret = cobj->getNamespace(arg0, arg1); + object_to_luaval(tolua_S, "ax.Properties",(ax::Properties*)ret); return 1; } }while(0); ok = true; do{ - if (argc == 0) { - ax::Node* ret = cobj->getForcedTarget(); - object_to_luaval(tolua_S, "ax.Node",(ax::Node*)ret); + if (argc == 3) { + std::string_view arg0; + ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Properties:getNamespace"); + + if (!ok) { break; } + bool arg1; + ok &= luaval_to_boolean(tolua_S, 3,&arg1, "ax.Properties:getNamespace"); + + if (!ok) { break; } + bool arg2; + ok &= luaval_to_boolean(tolua_S, 4,&arg2, "ax.Properties:getNamespace"); + + if (!ok) { break; } + ax::Properties* ret = cobj->getNamespace(arg0, arg1, arg2); + object_to_luaval(tolua_S, "ax.Properties",(ax::Properties*)ret); return 1; } }while(0); ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.TargetedAction:getForcedTarget",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:getNamespace",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_TargetedAction_getForcedTarget'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_getNamespace'.",&tolua_err); #endif return 0; } -int lua_ax_base_TargetedAction_initWithTarget(lua_State* tolua_S) +int lua_ax_base_Properties_getId(lua_State* tolua_S) { int argc = 0; - ax::TargetedAction* cobj = nullptr; + ax::Properties* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -29186,89 +28958,95 @@ int lua_ax_base_TargetedAction_initWithTarget(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.TargetedAction",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Properties",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::TargetedAction*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Properties*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_TargetedAction_initWithTarget'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_getId'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 2) + if (argc == 0) { - ax::Node* arg0; - ax::FiniteTimeAction* arg1; - - ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.TargetedAction:initWithTarget"); - - ok &= luaval_to_object(tolua_S, 3, "ax.FiniteTimeAction",&arg1, "ax.TargetedAction:initWithTarget"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_TargetedAction_initWithTarget'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getId'", nullptr); return 0; } - auto&& ret = cobj->initWithTarget(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); + auto&& ret = cobj->getId(); + lua_pushlstring(tolua_S,ret.data(),ret.length()); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.TargetedAction:initWithTarget",argc, 2); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:getId",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_TargetedAction_initWithTarget'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_getId'.",&tolua_err); #endif return 0; } -int lua_ax_base_TargetedAction_create(lua_State* tolua_S) +int lua_ax_base_Properties_exists(lua_State* tolua_S) { int argc = 0; + ax::Properties* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.TargetedAction",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Properties",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S) - 1; + cobj = (ax::Properties*)tolua_tousertype(tolua_S,1,0); - if (argc == 2) +#if _AX_DEBUG >= 1 + if (!cobj) { - ax::Node* arg0; - ax::FiniteTimeAction* arg1; - ok &= luaval_to_object(tolua_S, 2, "ax.Node",&arg0, "ax.TargetedAction:create"); - ok &= luaval_to_object(tolua_S, 3, "ax.FiniteTimeAction",&arg1, "ax.TargetedAction:create"); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_exists'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 1) + { + const char* arg0; + + std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:exists"); arg0 = arg0_tmp.c_str(); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_TargetedAction_create'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_exists'", nullptr); return 0; } - auto&& ret = ax::TargetedAction::create(arg0, arg1); - object_to_luaval(tolua_S, "ax.TargetedAction",(ax::TargetedAction*)ret); + auto&& ret = cobj->exists(arg0); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.TargetedAction:create",argc, 2); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:exists",argc, 1); return 0; + #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_TargetedAction_create'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_exists'.",&tolua_err); #endif + return 0; } -int lua_ax_base_TargetedAction_constructor(lua_State* tolua_S) +int lua_ax_base_Properties_getType(lua_State* tolua_S) { int argc = 0; - ax::TargetedAction* cobj = nullptr; + ax::Properties* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -29276,60 +29054,60 @@ int lua_ax_base_TargetedAction_constructor(lua_State* tolua_S) #endif +#if _AX_DEBUG >= 1 + if (!tolua_isusertype(tolua_S,1,"ax.Properties",0,&tolua_err)) goto tolua_lerror; +#endif + + cobj = (ax::Properties*)tolua_tousertype(tolua_S,1,0); + +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_getType'", nullptr); + return 0; + } +#endif argc = lua_gettop(tolua_S)-1; if (argc == 0) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_TargetedAction_constructor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getType'", nullptr); return 0; } - cobj = new ax::TargetedAction(); - cobj->autorelease(); - int ID = (int)cobj->_ID ; - int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.TargetedAction"); + int ret = (int)cobj->getType(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.TargetedAction:TargetedAction",argc, 0); + if (argc == 1) + { + const char* arg0; + + std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:getType"); arg0 = arg0_tmp.c_str(); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getType'", nullptr); + return 0; + } + int ret = (int)cobj->getType(arg0); + tolua_pushnumber(tolua_S,(lua_Number)ret); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:getType",argc, 0); return 0; #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_TargetedAction_constructor'.",&tolua_err); + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_getType'.",&tolua_err); #endif return 0; } - -static int lua_ax_base_TargetedAction_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (TargetedAction)"); - return 0; -} - -int lua_register_ax_base_TargetedAction(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.TargetedAction"); - tolua_cclass(tolua_S,"TargetedAction","ax.TargetedAction","ax.ActionInterval",nullptr); - - tolua_beginmodule(tolua_S,"TargetedAction"); - tolua_function(tolua_S,"new",lua_ax_base_TargetedAction_constructor); - tolua_function(tolua_S,"setForcedTarget",lua_ax_base_TargetedAction_setForcedTarget); - tolua_function(tolua_S,"getForcedTarget",lua_ax_base_TargetedAction_getForcedTarget); - tolua_function(tolua_S,"initWithTarget",lua_ax_base_TargetedAction_initWithTarget); - tolua_function(tolua_S,"create", lua_ax_base_TargetedAction_create); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::TargetedAction).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.TargetedAction"; - g_typeCast[typeName] = "ax.TargetedAction"; - return 1; -} - -int lua_ax_base_ActionFloat_initWithDuration(lua_State* tolua_S) +int lua_ax_base_Properties_getString(lua_State* tolua_S) { int argc = 0; - ax::ActionFloat* cobj = nullptr; + ax::Properties* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -29338,163 +29116,126 @@ int lua_ax_base_ActionFloat_initWithDuration(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.ActionFloat",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Properties",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::ActionFloat*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Properties*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_ActionFloat_initWithDuration'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_getString'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 4) + if (argc == 0) { - double arg0; - double arg1; - double arg2; - std::function arg3; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.ActionFloat:initWithDuration"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getString'", nullptr); + return 0; + } + auto&& ret = cobj->getString(); + tolua_pushstring(tolua_S,(const char*)ret); + return 1; + } + if (argc == 1) + { + const char* arg0; - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.ActionFloat:initWithDuration"); + std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:getString"); arg0 = arg0_tmp.c_str(); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getString'", nullptr); + return 0; + } + auto&& ret = cobj->getString(arg0); + tolua_pushstring(tolua_S,(const char*)ret); + return 1; + } + if (argc == 2) + { + const char* arg0; + const char* arg1; - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.ActionFloat:initWithDuration"); + std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:getString"); arg0 = arg0_tmp.c_str(); - do { - // Lambda binding for lua is not supported. - assert(false); - } while(0) - ; + std::string arg1_tmp; ok &= luaval_to_std_string(tolua_S, 3, &arg1_tmp, "ax.Properties:getString"); arg1 = arg1_tmp.c_str(); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_ActionFloat_initWithDuration'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getString'", nullptr); return 0; } - auto&& ret = cobj->initWithDuration(arg0, arg1, arg2, arg3); - tolua_pushboolean(tolua_S,(bool)ret); + auto&& ret = cobj->getString(arg0, arg1); + tolua_pushstring(tolua_S,(const char*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.ActionFloat:initWithDuration",argc, 4); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:getString",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_ActionFloat_initWithDuration'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_getString'.",&tolua_err); #endif return 0; } -int lua_ax_base_ActionFloat_create(lua_State* tolua_S) +int lua_ax_base_Properties_setString(lua_State* tolua_S) { int argc = 0; + ax::Properties* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.ActionFloat",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Properties",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S) - 1; + cobj = (ax::Properties*)tolua_tousertype(tolua_S,1,0); - if (argc == 4) +#if _AX_DEBUG >= 1 + if (!cobj) { - double arg0; - double arg1; - double arg2; - std::function arg3; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.ActionFloat:create"); - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.ActionFloat:create"); - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.ActionFloat:create"); - do { - // Lambda binding for lua is not supported. - assert(false); - } while(0) - ; - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_ActionFloat_create'", nullptr); - return 0; - } - auto&& ret = ax::ActionFloat::create(arg0, arg1, arg2, arg3); - object_to_luaval(tolua_S, "ax.ActionFloat",(ax::ActionFloat*)ret); - return 1; + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_setString'", nullptr); + return 0; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.ActionFloat:create",argc, 4); - return 0; -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_ActionFloat_create'.",&tolua_err); -#endif - return 0; -} -int lua_ax_base_ActionFloat_constructor(lua_State* tolua_S) -{ - int argc = 0; - ax::ActionFloat* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; #endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 2) { + const char* arg0; + const char* arg1; + + std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:setString"); arg0 = arg0_tmp.c_str(); + + std::string arg1_tmp; ok &= luaval_to_std_string(tolua_S, 3, &arg1_tmp, "ax.Properties:setString"); arg1 = arg1_tmp.c_str(); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_ActionFloat_constructor'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_setString'", nullptr); return 0; } - cobj = new ax::ActionFloat(); - cobj->autorelease(); - int ID = (int)cobj->_ID ; - int* luaID = &cobj->_luaID ; - toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.ActionFloat"); + auto&& ret = cobj->setString(arg0, arg1); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.ActionFloat:ActionFloat",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:setString",argc, 2); return 0; #if _AX_DEBUG >= 1 - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_ActionFloat_constructor'.",&tolua_err); + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_setString'.",&tolua_err); #endif return 0; } - -static int lua_ax_base_ActionFloat_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (ActionFloat)"); - return 0; -} - -int lua_register_ax_base_ActionFloat(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.ActionFloat"); - tolua_cclass(tolua_S,"ActionFloat","ax.ActionFloat","ax.ActionInterval",nullptr); - - tolua_beginmodule(tolua_S,"ActionFloat"); - tolua_function(tolua_S,"new",lua_ax_base_ActionFloat_constructor); - tolua_function(tolua_S,"initWithDuration",lua_ax_base_ActionFloat_initWithDuration); - tolua_function(tolua_S,"create", lua_ax_base_ActionFloat_create); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::ActionFloat).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.ActionFloat"; - g_typeCast[typeName] = "ax.ActionFloat"; - return 1; -} - -int lua_ax_base_Properties_getNextProperty(lua_State* tolua_S) +int lua_ax_base_Properties_getBool(lua_State* tolua_S) { int argc = 0; ax::Properties* cobj = nullptr; @@ -29514,7 +29255,7 @@ int lua_ax_base_Properties_getNextProperty(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_getNextProperty'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_getBool'", nullptr); return 0; } #endif @@ -29524,24 +29265,55 @@ int lua_ax_base_Properties_getNextProperty(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getNextProperty'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getBool'", nullptr); return 0; } - auto&& ret = cobj->getNextProperty(); - tolua_pushstring(tolua_S,(const char*)ret); + auto&& ret = cobj->getBool(); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:getNextProperty",argc, 0); + if (argc == 1) + { + const char* arg0; + + std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:getBool"); arg0 = arg0_tmp.c_str(); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getBool'", nullptr); + return 0; + } + auto&& ret = cobj->getBool(arg0); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; + } + if (argc == 2) + { + const char* arg0; + bool arg1; + + std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:getBool"); arg0 = arg0_tmp.c_str(); + + ok &= luaval_to_boolean(tolua_S, 3,&arg1, "ax.Properties:getBool"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getBool'", nullptr); + return 0; + } + auto&& ret = cobj->getBool(arg0, arg1); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:getBool",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_getNextProperty'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_getBool'.",&tolua_err); #endif return 0; } -int lua_ax_base_Properties_getNextNamespace(lua_State* tolua_S) +int lua_ax_base_Properties_getInt(lua_State* tolua_S) { int argc = 0; ax::Properties* cobj = nullptr; @@ -29561,7 +29333,7 @@ int lua_ax_base_Properties_getNextNamespace(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_getNextNamespace'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_getInt'", nullptr); return 0; } #endif @@ -29571,24 +29343,38 @@ int lua_ax_base_Properties_getNextNamespace(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getNextNamespace'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getInt'", nullptr); return 0; } - auto&& ret = cobj->getNextNamespace(); - object_to_luaval(tolua_S, "ax.Properties",(ax::Properties*)ret); + auto&& ret = cobj->getInt(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:getNextNamespace",argc, 0); + if (argc == 1) + { + const char* arg0; + + std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:getInt"); arg0 = arg0_tmp.c_str(); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getInt'", nullptr); + return 0; + } + auto&& ret = cobj->getInt(arg0); + tolua_pushnumber(tolua_S,(lua_Number)ret); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:getInt",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_getNextNamespace'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_getInt'.",&tolua_err); #endif return 0; } -int lua_ax_base_Properties_rewind(lua_State* tolua_S) +int lua_ax_base_Properties_getFloat(lua_State* tolua_S) { int argc = 0; ax::Properties* cobj = nullptr; @@ -29608,7 +29394,7 @@ int lua_ax_base_Properties_rewind(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_rewind'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_getFloat'", nullptr); return 0; } #endif @@ -29618,111 +29404,91 @@ int lua_ax_base_Properties_rewind(lua_State* tolua_S) { if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_rewind'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getFloat'", nullptr); return 0; } - cobj->rewind(); - lua_settop(tolua_S, 1); + auto&& ret = cobj->getFloat(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:rewind",argc, 0); + if (argc == 1) + { + const char* arg0; + + std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:getFloat"); arg0 = arg0_tmp.c_str(); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getFloat'", nullptr); + return 0; + } + auto&& ret = cobj->getFloat(arg0); + tolua_pushnumber(tolua_S,(lua_Number)ret); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:getFloat",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_rewind'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_getFloat'.",&tolua_err); #endif return 0; } -int lua_ax_base_Properties_getNamespace(lua_State* tolua_S) +int lua_ax_base_Properties_getMat4(lua_State* tolua_S) { int argc = 0; ax::Properties* cobj = nullptr; bool ok = true; + #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 if (!tolua_isusertype(tolua_S,1,"ax.Properties",0,&tolua_err)) goto tolua_lerror; #endif + cobj = (ax::Properties*)tolua_tousertype(tolua_S,1,0); + #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_getNamespace'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_getMat4'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - do{ - if (argc == 0) { - const char* ret = cobj->getNamespace(); - tolua_pushstring(tolua_S,(const char*)ret); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 1) { - std::string_view arg0; - ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Properties:getNamespace"); - - if (!ok) { break; } - ax::Properties* ret = cobj->getNamespace(arg0); - object_to_luaval(tolua_S, "ax.Properties",(ax::Properties*)ret); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 2) { - std::string_view arg0; - ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Properties:getNamespace"); - - if (!ok) { break; } - bool arg1; - ok &= luaval_to_boolean(tolua_S, 3,&arg1, "ax.Properties:getNamespace"); - - if (!ok) { break; } - ax::Properties* ret = cobj->getNamespace(arg0, arg1); - object_to_luaval(tolua_S, "ax.Properties",(ax::Properties*)ret); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 3) { - std::string_view arg0; - ok &= luaval_to_std_string_view(tolua_S, 2,&arg0, "ax.Properties:getNamespace"); - if (!ok) { break; } - bool arg1; - ok &= luaval_to_boolean(tolua_S, 3,&arg1, "ax.Properties:getNamespace"); + argc = lua_gettop(tolua_S)-1; + if (argc == 2) + { + const char* arg0; + ax::Mat4* arg1; - if (!ok) { break; } - bool arg2; - ok &= luaval_to_boolean(tolua_S, 4,&arg2, "ax.Properties:getNamespace"); + std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:getMat4"); arg0 = arg0_tmp.c_str(); - if (!ok) { break; } - ax::Properties* ret = cobj->getNamespace(arg0, arg1, arg2); - object_to_luaval(tolua_S, "ax.Properties",(ax::Properties*)ret); - return 1; + ok &= luaval_to_object(tolua_S, 3, "ax.Mat4",&arg1, "ax.Properties:getMat4"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getMat4'", nullptr); + return 0; } - }while(0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:getNamespace",argc, 1); + auto&& ret = cobj->getMat4(arg0, arg1); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:getMat4",argc, 2); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_getNamespace'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_getMat4'.",&tolua_err); #endif return 0; } -int lua_ax_base_Properties_getId(lua_State* tolua_S) +int lua_ax_base_Properties_getVec2(lua_State* tolua_S) { int argc = 0; ax::Properties* cobj = nullptr; @@ -29742,34 +29508,40 @@ int lua_ax_base_Properties_getId(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_getId'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_getVec2'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 2) { + const char* arg0; + ax::Vec2* arg1; + + std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:getVec2"); arg0 = arg0_tmp.c_str(); + + ok &= luaval_to_object(tolua_S, 3, "ax.Vec2",&arg1, "ax.Properties:getVec2"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getId'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getVec2'", nullptr); return 0; } - auto&& ret = cobj->getId(); - lua_pushlstring(tolua_S,ret.data(),ret.length()); + auto&& ret = cobj->getVec2(arg0, arg1); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:getId",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:getVec2",argc, 2); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_getId'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_getVec2'.",&tolua_err); #endif return 0; } -int lua_ax_base_Properties_exists(lua_State* tolua_S) +int lua_ax_base_Properties_getVec3(lua_State* tolua_S) { int argc = 0; ax::Properties* cobj = nullptr; @@ -29789,37 +29561,40 @@ int lua_ax_base_Properties_exists(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_exists'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_getVec3'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) + if (argc == 2) { const char* arg0; + ax::Vec3* arg1; - std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:exists"); arg0 = arg0_tmp.c_str(); + std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:getVec3"); arg0 = arg0_tmp.c_str(); + + ok &= luaval_to_object(tolua_S, 3, "ax.Vec3",&arg1, "ax.Properties:getVec3"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_exists'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getVec3'", nullptr); return 0; } - auto&& ret = cobj->exists(arg0); + auto&& ret = cobj->getVec3(arg0, arg1); tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:exists",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:getVec3",argc, 2); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_exists'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_getVec3'.",&tolua_err); #endif return 0; } -int lua_ax_base_Properties_getType(lua_State* tolua_S) +int lua_ax_base_Properties_getVec4(lua_State* tolua_S) { int argc = 0; ax::Properties* cobj = nullptr; @@ -29839,48 +29614,40 @@ int lua_ax_base_Properties_getType(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_getType'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_getVec4'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getType'", nullptr); - return 0; - } - int ret = (int)cobj->getType(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - if (argc == 1) + if (argc == 2) { const char* arg0; + ax::Vec4* arg1; - std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:getType"); arg0 = arg0_tmp.c_str(); + std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:getVec4"); arg0 = arg0_tmp.c_str(); + + ok &= luaval_to_object(tolua_S, 3, "ax.Vec4",&arg1, "ax.Properties:getVec4"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getType'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getVec4'", nullptr); return 0; } - int ret = (int)cobj->getType(arg0); - tolua_pushnumber(tolua_S,(lua_Number)ret); + auto&& ret = cobj->getVec4(arg0, arg1); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:getType",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:getVec4",argc, 2); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_getType'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_getVec4'.",&tolua_err); #endif return 0; } -int lua_ax_base_Properties_getString(lua_State* tolua_S) +int lua_ax_base_Properties_getQuaternionFromAxisAngle(lua_State* tolua_S) { int argc = 0; ax::Properties* cobj = nullptr; @@ -29900,65 +29667,40 @@ int lua_ax_base_Properties_getString(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_getString'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_getQuaternionFromAxisAngle'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getString'", nullptr); - return 0; - } - auto&& ret = cobj->getString(); - tolua_pushstring(tolua_S,(const char*)ret); - return 1; - } - if (argc == 1) - { - const char* arg0; - - std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:getString"); arg0 = arg0_tmp.c_str(); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getString'", nullptr); - return 0; - } - auto&& ret = cobj->getString(arg0); - tolua_pushstring(tolua_S,(const char*)ret); - return 1; - } if (argc == 2) { const char* arg0; - const char* arg1; + ax::Quaternion* arg1; - std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:getString"); arg0 = arg0_tmp.c_str(); + std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:getQuaternionFromAxisAngle"); arg0 = arg0_tmp.c_str(); - std::string arg1_tmp; ok &= luaval_to_std_string(tolua_S, 3, &arg1_tmp, "ax.Properties:getString"); arg1 = arg1_tmp.c_str(); + ok &= luaval_to_object(tolua_S, 3, "ax.Quaternion",&arg1, "ax.Properties:getQuaternionFromAxisAngle"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getString'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getQuaternionFromAxisAngle'", nullptr); return 0; } - auto&& ret = cobj->getString(arg0, arg1); - tolua_pushstring(tolua_S,(const char*)ret); + auto&& ret = cobj->getQuaternionFromAxisAngle(arg0, arg1); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:getString",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:getQuaternionFromAxisAngle",argc, 2); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_getString'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_getQuaternionFromAxisAngle'.",&tolua_err); #endif return 0; } -int lua_ax_base_Properties_setString(lua_State* tolua_S) +int lua_ax_base_Properties_getColor(lua_State* tolua_S) { int argc = 0; ax::Properties* cobj = nullptr; @@ -29978,7 +29720,7 @@ int lua_ax_base_Properties_setString(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_setString'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_getColor'", nullptr); return 0; } #endif @@ -29987,31 +29729,32 @@ int lua_ax_base_Properties_setString(lua_State* tolua_S) if (argc == 2) { const char* arg0; - const char* arg1; + ax::Color* arg1; - std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:setString"); arg0 = arg0_tmp.c_str(); + std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:getColor"); arg0 = arg0_tmp.c_str(); - std::string arg1_tmp; ok &= luaval_to_std_string(tolua_S, 3, &arg1_tmp, "ax.Properties:setString"); arg1 = arg1_tmp.c_str(); + #pragma warning NO CONVERSION TO NATIVE FOR Color* + ok = false; if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_setString'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getColor'", nullptr); return 0; } - auto&& ret = cobj->setString(arg0, arg1); + auto&& ret = cobj->getColor(arg0, arg1); tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:setString",argc, 2); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:getColor",argc, 2); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_setString'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_getColor'.",&tolua_err); #endif return 0; } -int lua_ax_base_Properties_getBool(lua_State* tolua_S) +int lua_ax_base_Properties_getPath(lua_State* tolua_S) { int argc = 0; ax::Properties* cobj = nullptr; @@ -30031,65 +29774,41 @@ int lua_ax_base_Properties_getBool(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_getBool'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_getPath'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getBool'", nullptr); - return 0; - } - auto&& ret = cobj->getBool(); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - if (argc == 1) - { - const char* arg0; - - std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:getBool"); arg0 = arg0_tmp.c_str(); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getBool'", nullptr); - return 0; - } - auto&& ret = cobj->getBool(arg0); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } if (argc == 2) { const char* arg0; - bool arg1; + std::string* arg1; - std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:getBool"); arg0 = arg0_tmp.c_str(); + std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:getPath"); arg0 = arg0_tmp.c_str(); - ok &= luaval_to_boolean(tolua_S, 3,&arg1, "ax.Properties:getBool"); + #pragma warning NO CONVERSION TO NATIVE FOR basic_string* + ok = false; if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getBool'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getPath'", nullptr); return 0; } - auto&& ret = cobj->getBool(arg0, arg1); + auto&& ret = cobj->getPath(arg0, arg1); tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:getBool",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:getPath",argc, 2); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_getBool'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_getPath'.",&tolua_err); #endif return 0; } -int lua_ax_base_Properties_getInt(lua_State* tolua_S) +int lua_ax_base_Properties_getVariable(lua_State* tolua_S) { int argc = 0; ax::Properties* cobj = nullptr; @@ -30109,48 +29828,54 @@ int lua_ax_base_Properties_getInt(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_getInt'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_getVariable'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 1) { + const char* arg0; + + std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:getVariable"); arg0 = arg0_tmp.c_str(); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getInt'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getVariable'", nullptr); return 0; } - auto&& ret = cobj->getInt(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + auto&& ret = cobj->getVariable(arg0); + tolua_pushstring(tolua_S,(const char*)ret); return 1; } - if (argc == 1) + if (argc == 2) { const char* arg0; + const char* arg1; - std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:getInt"); arg0 = arg0_tmp.c_str(); + std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:getVariable"); arg0 = arg0_tmp.c_str(); + + std::string arg1_tmp; ok &= luaval_to_std_string(tolua_S, 3, &arg1_tmp, "ax.Properties:getVariable"); arg1 = arg1_tmp.c_str(); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getInt'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getVariable'", nullptr); return 0; } - auto&& ret = cobj->getInt(arg0); - tolua_pushnumber(tolua_S,(lua_Number)ret); + auto&& ret = cobj->getVariable(arg0, arg1); + tolua_pushstring(tolua_S,(const char*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:getInt",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:getVariable",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_getInt'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_getVariable'.",&tolua_err); #endif return 0; } -int lua_ax_base_Properties_getFloat(lua_State* tolua_S) +int lua_ax_base_Properties_setVariable(lua_State* tolua_S) { int argc = 0; ax::Properties* cobj = nullptr; @@ -30170,157 +29895,279 @@ int lua_ax_base_Properties_getFloat(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_getFloat'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_setVariable'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 0) + if (argc == 2) { + const char* arg0; + const char* arg1; + + std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:setVariable"); arg0 = arg0_tmp.c_str(); + + std::string arg1_tmp; ok &= luaval_to_std_string(tolua_S, 3, &arg1_tmp, "ax.Properties:setVariable"); arg1 = arg1_tmp.c_str(); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getFloat'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_setVariable'", nullptr); return 0; } - auto&& ret = cobj->getFloat(); - tolua_pushnumber(tolua_S,(lua_Number)ret); + cobj->setVariable(arg0, arg1); + lua_settop(tolua_S, 1); return 1; } - if (argc == 1) + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:setVariable",argc, 2); + return 0; + +#if _AX_DEBUG >= 1 + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_setVariable'.",&tolua_err); +#endif + + return 0; +} +int lua_ax_base_Properties_parseVec2(lua_State* tolua_S) +{ + int argc = 0; + bool ok = true; + +#if _AX_DEBUG >= 1 + tolua_Error tolua_err; +#endif + +#if _AX_DEBUG >= 1 + if (!tolua_isusertable(tolua_S,1,"ax.Properties",0,&tolua_err)) goto tolua_lerror; +#endif + + argc = lua_gettop(tolua_S) - 1; + + if (argc == 2) { const char* arg0; - - std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:getFloat"); arg0 = arg0_tmp.c_str(); + ax::Vec2* arg1; + std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:parseVec2"); arg0 = arg0_tmp.c_str(); + ok &= luaval_to_object(tolua_S, 3, "ax.Vec2",&arg1, "ax.Properties:parseVec2"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getFloat'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_parseVec2'", nullptr); return 0; } - auto&& ret = cobj->getFloat(arg0); - tolua_pushnumber(tolua_S,(lua_Number)ret); + auto&& ret = ax::Properties::parseVec2(arg0, arg1); + tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:getFloat",argc, 0); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Properties:parseVec2",argc, 2); return 0; - #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_getFloat'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_parseVec2'.",&tolua_err); #endif - return 0; } -int lua_ax_base_Properties_getMat4(lua_State* tolua_S) +int lua_ax_base_Properties_parseVec3(lua_State* tolua_S) { int argc = 0; - ax::Properties* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Properties",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.Properties",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Properties*)tolua_tousertype(tolua_S,1,0); + argc = lua_gettop(tolua_S) - 1; -#if _AX_DEBUG >= 1 - if (!cobj) + if (argc == 2) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_getMat4'", nullptr); - return 0; + const char* arg0; + ax::Vec3* arg1; + std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:parseVec3"); arg0 = arg0_tmp.c_str(); + ok &= luaval_to_object(tolua_S, 3, "ax.Vec3",&arg1, "ax.Properties:parseVec3"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_parseVec3'", nullptr); + return 0; + } + auto&& ret = ax::Properties::parseVec3(arg0, arg1); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Properties:parseVec3",argc, 2); + return 0; +#if _AX_DEBUG >= 1 + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_parseVec3'.",&tolua_err); #endif + return 0; +} +int lua_ax_base_Properties_parseVec4(lua_State* tolua_S) +{ + int argc = 0; + bool ok = true; - argc = lua_gettop(tolua_S)-1; - if (argc == 2) - { - const char* arg0; - ax::Mat4* arg1; +#if _AX_DEBUG >= 1 + tolua_Error tolua_err; +#endif - std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:getMat4"); arg0 = arg0_tmp.c_str(); +#if _AX_DEBUG >= 1 + if (!tolua_isusertable(tolua_S,1,"ax.Properties",0,&tolua_err)) goto tolua_lerror; +#endif - ok &= luaval_to_object(tolua_S, 3, "ax.Mat4",&arg1, "ax.Properties:getMat4"); + argc = lua_gettop(tolua_S) - 1; + + if (argc == 2) + { + const char* arg0; + ax::Vec4* arg1; + std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:parseVec4"); arg0 = arg0_tmp.c_str(); + ok &= luaval_to_object(tolua_S, 3, "ax.Vec4",&arg1, "ax.Properties:parseVec4"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getMat4'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_parseVec4'", nullptr); return 0; } - auto&& ret = cobj->getMat4(arg0, arg1); + auto&& ret = ax::Properties::parseVec4(arg0, arg1); tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:getMat4",argc, 2); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Properties:parseVec4",argc, 2); return 0; - #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_getMat4'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_parseVec4'.",&tolua_err); #endif - return 0; } -int lua_ax_base_Properties_getVec2(lua_State* tolua_S) +int lua_ax_base_Properties_parseAxisAngle(lua_State* tolua_S) { int argc = 0; - ax::Properties* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif - #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Properties",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertable(tolua_S,1,"ax.Properties",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Properties*)tolua_tousertype(tolua_S,1,0); + argc = lua_gettop(tolua_S) - 1; -#if _AX_DEBUG >= 1 - if (!cobj) + if (argc == 2) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_getVec2'", nullptr); - return 0; + const char* arg0; + ax::Quaternion* arg1; + std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:parseAxisAngle"); arg0 = arg0_tmp.c_str(); + ok &= luaval_to_object(tolua_S, 3, "ax.Quaternion",&arg1, "ax.Properties:parseAxisAngle"); + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_parseAxisAngle'", nullptr); + return 0; + } + auto&& ret = ax::Properties::parseAxisAngle(arg0, arg1); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Properties:parseAxisAngle",argc, 2); + return 0; +#if _AX_DEBUG >= 1 + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_parseAxisAngle'.",&tolua_err); #endif + return 0; +} +int lua_ax_base_Properties_parseColor(lua_State* tolua_S) +{ + int argc = 0; + bool ok = true; - argc = lua_gettop(tolua_S)-1; - if (argc == 2) - { - const char* arg0; - ax::Vec2* arg1; +#if _AX_DEBUG >= 1 + tolua_Error tolua_err; +#endif - std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:getVec2"); arg0 = arg0_tmp.c_str(); +#if _AX_DEBUG >= 1 + if (!tolua_isusertable(tolua_S,1,"ax.Properties",0,&tolua_err)) goto tolua_lerror; +#endif - ok &= luaval_to_object(tolua_S, 3, "ax.Vec2",&arg1, "ax.Properties:getVec2"); + argc = lua_gettop(tolua_S) - 1; + + if (argc == 2) + { + const char* arg0; + ax::Color* arg1; + std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:parseColor"); arg0 = arg0_tmp.c_str(); + #pragma warning NO CONVERSION TO NATIVE FOR Color* + ok = false; if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getVec2'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_parseColor'", nullptr); return 0; } - auto&& ret = cobj->getVec2(arg0, arg1); + auto&& ret = ax::Properties::parseColor(arg0, arg1); tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:getVec2",argc, 2); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Properties:parseColor",argc, 2); return 0; - #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_getVec2'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_parseColor'.",&tolua_err); #endif - return 0; } -int lua_ax_base_Properties_getVec3(lua_State* tolua_S) +static int lua_ax_base_Properties_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (Properties)"); + return 0; +} + +int lua_register_ax_base_Properties(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.Properties"); + tolua_cclass(tolua_S,"Properties","ax.Properties","",nullptr); + + tolua_beginmodule(tolua_S,"Properties"); + tolua_function(tolua_S,"getNextProperty",lua_ax_base_Properties_getNextProperty); + tolua_function(tolua_S,"getNextNamespace",lua_ax_base_Properties_getNextNamespace); + tolua_function(tolua_S,"rewind",lua_ax_base_Properties_rewind); + tolua_function(tolua_S,"getNamespace",lua_ax_base_Properties_getNamespace); + tolua_function(tolua_S,"getId",lua_ax_base_Properties_getId); + tolua_function(tolua_S,"exists",lua_ax_base_Properties_exists); + tolua_function(tolua_S,"getType",lua_ax_base_Properties_getType); + tolua_function(tolua_S,"getString",lua_ax_base_Properties_getString); + tolua_function(tolua_S,"setString",lua_ax_base_Properties_setString); + tolua_function(tolua_S,"getBool",lua_ax_base_Properties_getBool); + tolua_function(tolua_S,"getInt",lua_ax_base_Properties_getInt); + tolua_function(tolua_S,"getFloat",lua_ax_base_Properties_getFloat); + tolua_function(tolua_S,"getMat4",lua_ax_base_Properties_getMat4); + tolua_function(tolua_S,"getVec2",lua_ax_base_Properties_getVec2); + tolua_function(tolua_S,"getVec3",lua_ax_base_Properties_getVec3); + tolua_function(tolua_S,"getVec4",lua_ax_base_Properties_getVec4); + tolua_function(tolua_S,"getQuaternionFromAxisAngle",lua_ax_base_Properties_getQuaternionFromAxisAngle); + tolua_function(tolua_S,"getColor",lua_ax_base_Properties_getColor); + tolua_function(tolua_S,"getPath",lua_ax_base_Properties_getPath); + tolua_function(tolua_S,"getVariable",lua_ax_base_Properties_getVariable); + tolua_function(tolua_S,"setVariable",lua_ax_base_Properties_setVariable); + tolua_function(tolua_S,"parseVec2", lua_ax_base_Properties_parseVec2); + tolua_function(tolua_S,"parseVec3", lua_ax_base_Properties_parseVec3); + tolua_function(tolua_S,"parseVec4", lua_ax_base_Properties_parseVec4); + tolua_function(tolua_S,"parseAxisAngle", lua_ax_base_Properties_parseAxisAngle); + tolua_function(tolua_S,"parseColor", lua_ax_base_Properties_parseColor); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::Properties).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.Properties"; + g_typeCast[typeName] = "ax.Properties"; + return 1; +} + +int lua_ax_base_Timer_setupTimerWithInterval(lua_State* tolua_S) { int argc = 0; - ax::Properties* cobj = nullptr; + ax::Timer* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -30329,51 +30176,54 @@ int lua_ax_base_Properties_getVec3(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Properties",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Timer",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Properties*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Timer*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_getVec3'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Timer_setupTimerWithInterval'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 2) + if (argc == 3) { - const char* arg0; - ax::Vec3* arg1; + double arg0; + unsigned int arg1; + double arg2; - std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:getVec3"); arg0 = arg0_tmp.c_str(); + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Timer:setupTimerWithInterval"); - ok &= luaval_to_object(tolua_S, 3, "ax.Vec3",&arg1, "ax.Properties:getVec3"); + ok &= luaval_to_uint32(tolua_S, 3,&arg1, "ax.Timer:setupTimerWithInterval"); + + ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.Timer:setupTimerWithInterval"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getVec3'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Timer_setupTimerWithInterval'", nullptr); return 0; } - auto&& ret = cobj->getVec3(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); + cobj->setupTimerWithInterval(arg0, arg1, arg2); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:getVec3",argc, 2); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Timer:setupTimerWithInterval",argc, 3); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_getVec3'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Timer_setupTimerWithInterval'.",&tolua_err); #endif return 0; } -int lua_ax_base_Properties_getVec4(lua_State* tolua_S) +int lua_ax_base_Timer_setAborted(lua_State* tolua_S) { int argc = 0; - ax::Properties* cobj = nullptr; + ax::Timer* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -30382,51 +30232,45 @@ int lua_ax_base_Properties_getVec4(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Properties",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Timer",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Properties*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Timer*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_getVec4'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Timer_setAborted'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 2) + if (argc == 0) { - const char* arg0; - ax::Vec4* arg1; - - std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:getVec4"); arg0 = arg0_tmp.c_str(); - - ok &= luaval_to_object(tolua_S, 3, "ax.Vec4",&arg1, "ax.Properties:getVec4"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getVec4'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Timer_setAborted'", nullptr); return 0; } - auto&& ret = cobj->getVec4(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); + cobj->setAborted(); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:getVec4",argc, 2); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Timer:setAborted",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_getVec4'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Timer_setAborted'.",&tolua_err); #endif return 0; } -int lua_ax_base_Properties_getQuaternionFromAxisAngle(lua_State* tolua_S) +int lua_ax_base_Timer_isAborted(lua_State* tolua_S) { int argc = 0; - ax::Properties* cobj = nullptr; + ax::Timer* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -30435,114 +30279,92 @@ int lua_ax_base_Properties_getQuaternionFromAxisAngle(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Properties",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Timer",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Properties*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Timer*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_getQuaternionFromAxisAngle'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Timer_isAborted'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 2) + if (argc == 0) { - const char* arg0; - ax::Quaternion* arg1; - - std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:getQuaternionFromAxisAngle"); arg0 = arg0_tmp.c_str(); - - ok &= luaval_to_object(tolua_S, 3, "ax.Quaternion",&arg1, "ax.Properties:getQuaternionFromAxisAngle"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getQuaternionFromAxisAngle'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Timer_isAborted'", nullptr); return 0; } - auto&& ret = cobj->getQuaternionFromAxisAngle(arg0, arg1); + auto&& ret = cobj->isAborted(); tolua_pushboolean(tolua_S,(bool)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:getQuaternionFromAxisAngle",argc, 2); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Timer:isAborted",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_getQuaternionFromAxisAngle'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Timer_isAborted'.",&tolua_err); #endif return 0; } -int lua_ax_base_Properties_getColor(lua_State* tolua_S) +int lua_ax_base_Timer_isExhausted(lua_State* tolua_S) { int argc = 0; - ax::Properties* cobj = nullptr; + ax::Timer* cobj = nullptr; bool ok = true; + #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Properties",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Timer",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Properties*)tolua_tousertype(tolua_S,1,0); + + cobj = (ax::Timer*)tolua_tousertype(tolua_S,1,0); + #if _AX_DEBUG >= 1 - if (!cobj) + if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_getColor'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Timer_isExhausted'", nullptr); return 0; } #endif - argc = lua_gettop(tolua_S)-1; - do{ - if (argc == 2) { - const char* arg0; - std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:getColor"); arg0 = arg0_tmp.c_str(); - - if (!ok) { break; } - ax::Vec4* arg1; - ok &= luaval_to_object(tolua_S, 3, "ax.Vec4",&arg1, "ax.Properties:getColor"); - - if (!ok) { break; } - bool ret = cobj->getColor(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 2) { - const char* arg0; - std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:getColor"); arg0 = arg0_tmp.c_str(); - - if (!ok) { break; } - ax::Vec3* arg1; - ok &= luaval_to_object(tolua_S, 3, "ax.Vec3",&arg1, "ax.Properties:getColor"); - if (!ok) { break; } - bool ret = cobj->getColor(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; + argc = lua_gettop(tolua_S)-1; + if (argc == 0) + { + if(!ok) + { + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Timer_isExhausted'", nullptr); + return 0; } - }while(0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:getColor",argc, 2); + auto&& ret = cobj->isExhausted(); + tolua_pushboolean(tolua_S,(bool)ret); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Timer:isExhausted",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_getColor'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Timer_isExhausted'.",&tolua_err); #endif return 0; } -int lua_ax_base_Properties_getPath(lua_State* tolua_S) +int lua_ax_base_Timer_trigger(lua_State* tolua_S) { int argc = 0; - ax::Properties* cobj = nullptr; + ax::Timer* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -30551,52 +30373,48 @@ int lua_ax_base_Properties_getPath(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Properties",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Timer",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Properties*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Timer*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_getPath'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Timer_trigger'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 2) + if (argc == 1) { - const char* arg0; - std::string* arg1; - - std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:getPath"); arg0 = arg0_tmp.c_str(); + double arg0; - #pragma warning NO CONVERSION TO NATIVE FOR basic_string* - ok = false; + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Timer:trigger"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getPath'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Timer_trigger'", nullptr); return 0; } - auto&& ret = cobj->getPath(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); + cobj->trigger(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:getPath",argc, 2); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Timer:trigger",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_getPath'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Timer_trigger'.",&tolua_err); #endif return 0; } -int lua_ax_base_Properties_getVariable(lua_State* tolua_S) +int lua_ax_base_Timer_cancel(lua_State* tolua_S) { int argc = 0; - ax::Properties* cobj = nullptr; + ax::Timer* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -30605,65 +30423,45 @@ int lua_ax_base_Properties_getVariable(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Properties",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Timer",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Properties*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Timer*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_getVariable'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Timer_cancel'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - const char* arg0; - - std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:getVariable"); arg0 = arg0_tmp.c_str(); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getVariable'", nullptr); - return 0; - } - auto&& ret = cobj->getVariable(arg0); - tolua_pushstring(tolua_S,(const char*)ret); - return 1; - } - if (argc == 2) + if (argc == 0) { - const char* arg0; - const char* arg1; - - std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:getVariable"); arg0 = arg0_tmp.c_str(); - - std::string arg1_tmp; ok &= luaval_to_std_string(tolua_S, 3, &arg1_tmp, "ax.Properties:getVariable"); arg1 = arg1_tmp.c_str(); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_getVariable'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Timer_cancel'", nullptr); return 0; } - auto&& ret = cobj->getVariable(arg0, arg1); - tolua_pushstring(tolua_S,(const char*)ret); + cobj->cancel(); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:getVariable",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Timer:cancel",argc, 0); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_getVariable'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Timer_cancel'.",&tolua_err); #endif return 0; } -int lua_ax_base_Properties_setVariable(lua_State* tolua_S) +int lua_ax_base_Timer_update(lua_State* tolua_S) { int argc = 0; - ax::Properties* cobj = nullptr; + ax::Timer* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 @@ -30672,295 +30470,326 @@ int lua_ax_base_Properties_setVariable(lua_State* tolua_S) #if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.Properties",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Timer",0,&tolua_err)) goto tolua_lerror; #endif - cobj = (ax::Properties*)tolua_tousertype(tolua_S,1,0); + cobj = (ax::Timer*)tolua_tousertype(tolua_S,1,0); #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Properties_setVariable'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Timer_update'", nullptr); return 0; } #endif argc = lua_gettop(tolua_S)-1; - if (argc == 2) + if (argc == 1) { - const char* arg0; - const char* arg1; - - std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:setVariable"); arg0 = arg0_tmp.c_str(); + double arg0; - std::string arg1_tmp; ok &= luaval_to_std_string(tolua_S, 3, &arg1_tmp, "ax.Properties:setVariable"); arg1 = arg1_tmp.c_str(); + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Timer:update"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_setVariable'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Timer_update'", nullptr); return 0; } - cobj->setVariable(arg0, arg1); + cobj->update(arg0); lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Properties:setVariable",argc, 2); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Timer:update",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_setVariable'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Timer_update'.",&tolua_err); #endif return 0; } -int lua_ax_base_Properties_parseVec2(lua_State* tolua_S) +static int lua_ax_base_Timer_finalize(lua_State* tolua_S) +{ + AXLOGV("luabindings: finalizing LUA object (Timer)"); + return 0; +} + +int lua_register_ax_base_Timer(lua_State* tolua_S) +{ + tolua_usertype(tolua_S,"ax.Timer"); + tolua_cclass(tolua_S,"Timer","ax.Timer","ax.Object",nullptr); + + tolua_beginmodule(tolua_S,"Timer"); + tolua_function(tolua_S,"setupTimerWithInterval",lua_ax_base_Timer_setupTimerWithInterval); + tolua_function(tolua_S,"setAborted",lua_ax_base_Timer_setAborted); + tolua_function(tolua_S,"isAborted",lua_ax_base_Timer_isAborted); + tolua_function(tolua_S,"isExhausted",lua_ax_base_Timer_isExhausted); + tolua_function(tolua_S,"trigger",lua_ax_base_Timer_trigger); + tolua_function(tolua_S,"cancel",lua_ax_base_Timer_cancel); + tolua_function(tolua_S,"update",lua_ax_base_Timer_update); + tolua_endmodule(tolua_S); + auto typeName = typeid(ax::Timer).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.Timer"; + g_typeCast[typeName] = "ax.Timer"; + return 1; +} + +int lua_ax_base_Scheduler_getTimeScale(lua_State* tolua_S) { int argc = 0; + ax::Scheduler* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.Properties",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Scheduler",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S) - 1; + cobj = (ax::Scheduler*)tolua_tousertype(tolua_S,1,0); - if (argc == 2) +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Scheduler_getTimeScale'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 0) { - const char* arg0; - ax::Vec2* arg1; - std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:parseVec2"); arg0 = arg0_tmp.c_str(); - ok &= luaval_to_object(tolua_S, 3, "ax.Vec2",&arg1, "ax.Properties:parseVec2"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_parseVec2'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scheduler_getTimeScale'", nullptr); return 0; } - auto&& ret = ax::Properties::parseVec2(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); + auto&& ret = cobj->getTimeScale(); + tolua_pushnumber(tolua_S,(lua_Number)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Properties:parseVec2",argc, 2); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Scheduler:getTimeScale",argc, 0); return 0; + #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_parseVec2'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Scheduler_getTimeScale'.",&tolua_err); #endif + return 0; } -int lua_ax_base_Properties_parseVec3(lua_State* tolua_S) +int lua_ax_base_Scheduler_setTimeScale(lua_State* tolua_S) { int argc = 0; + ax::Scheduler* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.Properties",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Scheduler",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S) - 1; + cobj = (ax::Scheduler*)tolua_tousertype(tolua_S,1,0); - if (argc == 2) +#if _AX_DEBUG >= 1 + if (!cobj) { - const char* arg0; - ax::Vec3* arg1; - std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:parseVec3"); arg0 = arg0_tmp.c_str(); - ok &= luaval_to_object(tolua_S, 3, "ax.Vec3",&arg1, "ax.Properties:parseVec3"); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Scheduler_setTimeScale'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 1) + { + double arg0; + + ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.Scheduler:setTimeScale"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_parseVec3'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scheduler_setTimeScale'", nullptr); return 0; } - auto&& ret = ax::Properties::parseVec3(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); + cobj->setTimeScale(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Properties:parseVec3",argc, 2); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Scheduler:setTimeScale",argc, 1); return 0; + #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_parseVec3'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Scheduler_setTimeScale'.",&tolua_err); #endif + return 0; } -int lua_ax_base_Properties_parseVec4(lua_State* tolua_S) +int lua_ax_base_Scheduler_runOnAxmolThread(lua_State* tolua_S) { int argc = 0; + ax::Scheduler* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.Properties",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Scheduler",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S) - 1; + cobj = (ax::Scheduler*)tolua_tousertype(tolua_S,1,0); - if (argc == 2) +#if _AX_DEBUG >= 1 + if (!cobj) { - const char* arg0; - ax::Vec4* arg1; - std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:parseVec4"); arg0 = arg0_tmp.c_str(); - ok &= luaval_to_object(tolua_S, 3, "ax.Vec4",&arg1, "ax.Properties:parseVec4"); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Scheduler_runOnAxmolThread'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 1) + { + std::function arg0; + + do { + // Lambda binding for lua is not supported. + assert(false); + } while(0) + ; if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_parseVec4'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scheduler_runOnAxmolThread'", nullptr); return 0; } - auto&& ret = ax::Properties::parseVec4(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); + cobj->runOnAxmolThread(arg0); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Properties:parseVec4",argc, 2); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Scheduler:runOnAxmolThread",argc, 1); return 0; + #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_parseVec4'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Scheduler_runOnAxmolThread'.",&tolua_err); #endif + return 0; } -int lua_ax_base_Properties_parseAxisAngle(lua_State* tolua_S) +int lua_ax_base_Scheduler_removeAllPendingActions(lua_State* tolua_S) { int argc = 0; + ax::Scheduler* cobj = nullptr; bool ok = true; #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif + #if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.Properties",0,&tolua_err)) goto tolua_lerror; + if (!tolua_isusertype(tolua_S,1,"ax.Scheduler",0,&tolua_err)) goto tolua_lerror; #endif - argc = lua_gettop(tolua_S) - 1; + cobj = (ax::Scheduler*)tolua_tousertype(tolua_S,1,0); - if (argc == 2) +#if _AX_DEBUG >= 1 + if (!cobj) + { + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_Scheduler_removeAllPendingActions'", nullptr); + return 0; + } +#endif + + argc = lua_gettop(tolua_S)-1; + if (argc == 0) { - const char* arg0; - ax::Quaternion* arg1; - std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:parseAxisAngle"); arg0 = arg0_tmp.c_str(); - ok &= luaval_to_object(tolua_S, 3, "ax.Quaternion",&arg1, "ax.Properties:parseAxisAngle"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Properties_parseAxisAngle'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scheduler_removeAllPendingActions'", nullptr); return 0; } - auto&& ret = ax::Properties::parseAxisAngle(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); + cobj->removeAllPendingActions(); + lua_settop(tolua_S, 1); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.Properties:parseAxisAngle",argc, 2); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Scheduler:removeAllPendingActions",argc, 0); return 0; + #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_parseAxisAngle'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Scheduler_removeAllPendingActions'.",&tolua_err); #endif + return 0; } -int lua_ax_base_Properties_parseColor(lua_State* tolua_S) +int lua_ax_base_Scheduler_constructor(lua_State* tolua_S) { int argc = 0; + ax::Scheduler* cobj = nullptr; bool ok = true; + #if _AX_DEBUG >= 1 tolua_Error tolua_err; #endif -#if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.Properties",0,&tolua_err)) goto tolua_lerror; -#endif - argc = lua_gettop(tolua_S)-1; - do - { - if (argc == 2) - { - const char* arg0; - std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:parseColor"); arg0 = arg0_tmp.c_str(); - if (!ok) { break; } - ax::Vec4* arg1; - ok &= luaval_to_object(tolua_S, 3, "ax.Vec4",&arg1, "ax.Properties:parseColor"); - if (!ok) { break; } - bool ret = ax::Properties::parseColor(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - } while (0); - ok = true; - do + argc = lua_gettop(tolua_S)-1; + if (argc == 0) { - if (argc == 2) + if(!ok) { - const char* arg0; - std::string arg0_tmp; ok &= luaval_to_std_string(tolua_S, 2, &arg0_tmp, "ax.Properties:parseColor"); arg0 = arg0_tmp.c_str(); - if (!ok) { break; } - ax::Vec3* arg1; - ok &= luaval_to_object(tolua_S, 3, "ax.Vec3",&arg1, "ax.Properties:parseColor"); - if (!ok) { break; } - bool ret = ax::Properties::parseColor(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Scheduler_constructor'", nullptr); + return 0; } - } while (0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d", "ax.Properties:parseColor",argc, 2); + cobj = new ax::Scheduler(); + cobj->autorelease(); + int ID = (int)cobj->_ID ; + int* luaID = &cobj->_luaID ; + toluafix_pushusertype_object(tolua_S, ID, luaID, (void*)cobj,"ax.Scheduler"); + return 1; + } + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Scheduler:Scheduler",argc, 0); return 0; + #if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Properties_parseColor'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_base_Scheduler_constructor'.",&tolua_err); #endif + return 0; } -static int lua_ax_base_Properties_finalize(lua_State* tolua_S) + +static int lua_ax_base_Scheduler_finalize(lua_State* tolua_S) { - AXLOGV("luabindings: finalizing LUA object (Properties)"); + AXLOGV("luabindings: finalizing LUA object (Scheduler)"); return 0; } -int lua_register_ax_base_Properties(lua_State* tolua_S) +int lua_register_ax_base_Scheduler(lua_State* tolua_S) { - tolua_usertype(tolua_S,"ax.Properties"); - tolua_cclass(tolua_S,"Properties","ax.Properties","",nullptr); + tolua_usertype(tolua_S,"ax.Scheduler"); + tolua_cclass(tolua_S,"Scheduler","ax.Scheduler","ax.Object",nullptr); - tolua_beginmodule(tolua_S,"Properties"); - tolua_function(tolua_S,"getNextProperty",lua_ax_base_Properties_getNextProperty); - tolua_function(tolua_S,"getNextNamespace",lua_ax_base_Properties_getNextNamespace); - tolua_function(tolua_S,"rewind",lua_ax_base_Properties_rewind); - tolua_function(tolua_S,"getNamespace",lua_ax_base_Properties_getNamespace); - tolua_function(tolua_S,"getId",lua_ax_base_Properties_getId); - tolua_function(tolua_S,"exists",lua_ax_base_Properties_exists); - tolua_function(tolua_S,"getType",lua_ax_base_Properties_getType); - tolua_function(tolua_S,"getString",lua_ax_base_Properties_getString); - tolua_function(tolua_S,"setString",lua_ax_base_Properties_setString); - tolua_function(tolua_S,"getBool",lua_ax_base_Properties_getBool); - tolua_function(tolua_S,"getInt",lua_ax_base_Properties_getInt); - tolua_function(tolua_S,"getFloat",lua_ax_base_Properties_getFloat); - tolua_function(tolua_S,"getMat4",lua_ax_base_Properties_getMat4); - tolua_function(tolua_S,"getVec2",lua_ax_base_Properties_getVec2); - tolua_function(tolua_S,"getVec3",lua_ax_base_Properties_getVec3); - tolua_function(tolua_S,"getVec4",lua_ax_base_Properties_getVec4); - tolua_function(tolua_S,"getQuaternionFromAxisAngle",lua_ax_base_Properties_getQuaternionFromAxisAngle); - tolua_function(tolua_S,"getColor",lua_ax_base_Properties_getColor); - tolua_function(tolua_S,"getPath",lua_ax_base_Properties_getPath); - tolua_function(tolua_S,"getVariable",lua_ax_base_Properties_getVariable); - tolua_function(tolua_S,"setVariable",lua_ax_base_Properties_setVariable); - tolua_function(tolua_S,"parseVec2", lua_ax_base_Properties_parseVec2); - tolua_function(tolua_S,"parseVec3", lua_ax_base_Properties_parseVec3); - tolua_function(tolua_S,"parseVec4", lua_ax_base_Properties_parseVec4); - tolua_function(tolua_S,"parseAxisAngle", lua_ax_base_Properties_parseAxisAngle); - tolua_function(tolua_S,"parseColor", lua_ax_base_Properties_parseColor); + tolua_beginmodule(tolua_S,"Scheduler"); + tolua_function(tolua_S,"new",lua_ax_base_Scheduler_constructor); + tolua_function(tolua_S,"getTimeScale",lua_ax_base_Scheduler_getTimeScale); + tolua_function(tolua_S,"setTimeScale",lua_ax_base_Scheduler_setTimeScale); + tolua_function(tolua_S,"runOnAxmolThread",lua_ax_base_Scheduler_runOnAxmolThread); + tolua_function(tolua_S,"removeAllPendingActions",lua_ax_base_Scheduler_removeAllPendingActions); tolua_endmodule(tolua_S); - auto typeName = typeid(ax::Properties).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.Properties"; - g_typeCast[typeName] = "ax.Properties"; + auto typeName = typeid(ax::Scheduler).name(); // rtti is literal storage + g_luaType[reinterpret_cast(typeName)] = "ax.Scheduler"; + g_typeCast[typeName] = "ax.Scheduler"; return 1; } @@ -51535,13 +51364,13 @@ int lua_ax_base_DrawNode_drawPoint(lua_State* tolua_S) { ax::Vec2 arg0; double arg1; - ax::Color4B arg2; + ax::Color arg2; ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.DrawNode:drawPoint"); ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.DrawNode:drawPoint"); - ok &=luaval_to_color4b(tolua_S, 4, &arg2, "ax.DrawNode:drawPoint"); + ok &=luaval_to_color(tolua_S, 4, &arg2, "ax.DrawNode:drawPoint"); if(!ok) { tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_DrawNode_drawPoint'", nullptr); @@ -51555,14 +51384,14 @@ int lua_ax_base_DrawNode_drawPoint(lua_State* tolua_S) { ax::Vec2 arg0; double arg1; - ax::Color4B arg2; + ax::Color arg2; ax::DrawNode::PointType arg3; ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.DrawNode:drawPoint"); ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.DrawNode:drawPoint"); - ok &=luaval_to_color4b(tolua_S, 4, &arg2, "ax.DrawNode:drawPoint"); + ok &=luaval_to_color(tolua_S, 4, &arg2, "ax.DrawNode:drawPoint"); ok &= luaval_to_int32(tolua_S, 5,(int *)&arg3, "ax.DrawNode:drawPoint"); if(!ok) @@ -51614,13 +51443,13 @@ int lua_ax_base_DrawNode_drawLine(lua_State* tolua_S) { ax::Vec2 arg0; ax::Vec2 arg1; - ax::Color4B arg2; + ax::Color arg2; ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.DrawNode:drawLine"); ok &= luaval_to_vec2(tolua_S, 3, &arg1, "ax.DrawNode:drawLine"); - ok &=luaval_to_color4b(tolua_S, 4, &arg2, "ax.DrawNode:drawLine"); + ok &=luaval_to_color(tolua_S, 4, &arg2, "ax.DrawNode:drawLine"); if(!ok) { tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_DrawNode_drawLine'", nullptr); @@ -51634,14 +51463,14 @@ int lua_ax_base_DrawNode_drawLine(lua_State* tolua_S) { ax::Vec2 arg0; ax::Vec2 arg1; - ax::Color4B arg2; + ax::Color arg2; double arg3; ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.DrawNode:drawLine"); ok &= luaval_to_vec2(tolua_S, 3, &arg1, "ax.DrawNode:drawLine"); - ok &=luaval_to_color4b(tolua_S, 4, &arg2, "ax.DrawNode:drawLine"); + ok &=luaval_to_color(tolua_S, 4, &arg2, "ax.DrawNode:drawLine"); ok &= luaval_to_number(tolua_S, 5,&arg3, "ax.DrawNode:drawLine"); if(!ok) @@ -51657,7 +51486,7 @@ int lua_ax_base_DrawNode_drawLine(lua_State* tolua_S) { ax::Vec2 arg0; ax::Vec2 arg1; - ax::Color4B arg2; + ax::Color arg2; double arg3; ax::DrawNode::EndType arg4; @@ -51665,7 +51494,7 @@ int lua_ax_base_DrawNode_drawLine(lua_State* tolua_S) ok &= luaval_to_vec2(tolua_S, 3, &arg1, "ax.DrawNode:drawLine"); - ok &=luaval_to_color4b(tolua_S, 4, &arg2, "ax.DrawNode:drawLine"); + ok &=luaval_to_color(tolua_S, 4, &arg2, "ax.DrawNode:drawLine"); ok &= luaval_to_number(tolua_S, 5,&arg3, "ax.DrawNode:drawLine"); @@ -51683,7 +51512,7 @@ int lua_ax_base_DrawNode_drawLine(lua_State* tolua_S) { ax::Vec2 arg0; ax::Vec2 arg1; - ax::Color4B arg2; + ax::Color arg2; double arg3; ax::DrawNode::EndType arg4; ax::DrawNode::EndType arg5; @@ -51692,7 +51521,7 @@ int lua_ax_base_DrawNode_drawLine(lua_State* tolua_S) ok &= luaval_to_vec2(tolua_S, 3, &arg1, "ax.DrawNode:drawLine"); - ok &=luaval_to_color4b(tolua_S, 4, &arg2, "ax.DrawNode:drawLine"); + ok &=luaval_to_color(tolua_S, 4, &arg2, "ax.DrawNode:drawLine"); ok &= luaval_to_number(tolua_S, 5,&arg3, "ax.DrawNode:drawLine"); @@ -51757,8 +51586,8 @@ int lua_ax_base_DrawNode_drawRect(lua_State* tolua_S) ok &= luaval_to_vec2(tolua_S, 5, &arg3, "ax.DrawNode:drawRect"); if (!ok) { break; } - ax::Color4B arg4; - ok &=luaval_to_color4b(tolua_S, 6, &arg4, "ax.DrawNode:drawRect"); + ax::Color arg4; + ok &=luaval_to_color(tolua_S, 6, &arg4, "ax.DrawNode:drawRect"); if (!ok) { break; } cobj->drawRect(arg0, arg1, arg2, arg3, arg4); @@ -51785,8 +51614,8 @@ int lua_ax_base_DrawNode_drawRect(lua_State* tolua_S) ok &= luaval_to_vec2(tolua_S, 5, &arg3, "ax.DrawNode:drawRect"); if (!ok) { break; } - ax::Color4B arg4; - ok &=luaval_to_color4b(tolua_S, 6, &arg4, "ax.DrawNode:drawRect"); + ax::Color arg4; + ok &=luaval_to_color(tolua_S, 6, &arg4, "ax.DrawNode:drawRect"); if (!ok) { break; } double arg5; @@ -51809,8 +51638,8 @@ int lua_ax_base_DrawNode_drawRect(lua_State* tolua_S) ok &= luaval_to_vec2(tolua_S, 3, &arg1, "ax.DrawNode:drawRect"); if (!ok) { break; } - ax::Color4B arg2; - ok &=luaval_to_color4b(tolua_S, 4, &arg2, "ax.DrawNode:drawRect"); + ax::Color arg2; + ok &=luaval_to_color(tolua_S, 4, &arg2, "ax.DrawNode:drawRect"); if (!ok) { break; } cobj->drawRect(arg0, arg1, arg2); @@ -51829,8 +51658,8 @@ int lua_ax_base_DrawNode_drawRect(lua_State* tolua_S) ok &= luaval_to_vec2(tolua_S, 3, &arg1, "ax.DrawNode:drawRect"); if (!ok) { break; } - ax::Color4B arg2; - ok &=luaval_to_color4b(tolua_S, 4, &arg2, "ax.DrawNode:drawRect"); + ax::Color arg2; + ok &=luaval_to_color(tolua_S, 4, &arg2, "ax.DrawNode:drawRect"); if (!ok) { break; } double arg3; @@ -51896,8 +51725,8 @@ int lua_ax_base_DrawNode_drawCircle(lua_State* tolua_S) ok &= luaval_to_boolean(tolua_S, 6,&arg4, "ax.DrawNode:drawCircle"); if (!ok) { break; } - ax::Color4B arg5; - ok &=luaval_to_color4b(tolua_S, 7, &arg5, "ax.DrawNode:drawCircle"); + ax::Color arg5; + ok &=luaval_to_color(tolua_S, 7, &arg5, "ax.DrawNode:drawCircle"); if (!ok) { break; } cobj->drawCircle(arg0, arg1, arg2, arg3, arg4, arg5); @@ -51928,8 +51757,8 @@ int lua_ax_base_DrawNode_drawCircle(lua_State* tolua_S) ok &= luaval_to_boolean(tolua_S, 6,&arg4, "ax.DrawNode:drawCircle"); if (!ok) { break; } - ax::Color4B arg5; - ok &=luaval_to_color4b(tolua_S, 7, &arg5, "ax.DrawNode:drawCircle"); + ax::Color arg5; + ok &=luaval_to_color(tolua_S, 7, &arg5, "ax.DrawNode:drawCircle"); if (!ok) { break; } double arg6; @@ -51972,8 +51801,8 @@ int lua_ax_base_DrawNode_drawCircle(lua_State* tolua_S) ok &= luaval_to_number(tolua_S, 8,&arg6, "ax.DrawNode:drawCircle"); if (!ok) { break; } - ax::Color4B arg7; - ok &=luaval_to_color4b(tolua_S, 9, &arg7, "ax.DrawNode:drawCircle"); + ax::Color arg7; + ok &=luaval_to_color(tolua_S, 9, &arg7, "ax.DrawNode:drawCircle"); if (!ok) { break; } cobj->drawCircle(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7); @@ -52012,8 +51841,8 @@ int lua_ax_base_DrawNode_drawCircle(lua_State* tolua_S) ok &= luaval_to_number(tolua_S, 8,&arg6, "ax.DrawNode:drawCircle"); if (!ok) { break; } - ax::Color4B arg7; - ok &=luaval_to_color4b(tolua_S, 9, &arg7, "ax.DrawNode:drawCircle"); + ax::Color arg7; + ok &=luaval_to_color(tolua_S, 9, &arg7, "ax.DrawNode:drawCircle"); if (!ok) { break; } double arg8; @@ -52068,7 +51897,7 @@ int lua_ax_base_DrawNode_drawStar(lua_State* tolua_S) double arg1; double arg2; unsigned int arg3; - ax::Color4B arg4; + ax::Color arg4; ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.DrawNode:drawStar"); @@ -52078,7 +51907,7 @@ int lua_ax_base_DrawNode_drawStar(lua_State* tolua_S) ok &= luaval_to_uint32(tolua_S, 5,&arg3, "ax.DrawNode:drawStar"); - ok &=luaval_to_color4b(tolua_S, 6, &arg4, "ax.DrawNode:drawStar"); + ok &=luaval_to_color(tolua_S, 6, &arg4, "ax.DrawNode:drawStar"); if(!ok) { tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_DrawNode_drawStar'", nullptr); @@ -52094,7 +51923,7 @@ int lua_ax_base_DrawNode_drawStar(lua_State* tolua_S) double arg1; double arg2; unsigned int arg3; - ax::Color4B arg4; + ax::Color arg4; double arg5; ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.DrawNode:drawStar"); @@ -52105,7 +51934,7 @@ int lua_ax_base_DrawNode_drawStar(lua_State* tolua_S) ok &= luaval_to_uint32(tolua_S, 5,&arg3, "ax.DrawNode:drawStar"); - ok &=luaval_to_color4b(tolua_S, 6, &arg4, "ax.DrawNode:drawStar"); + ok &=luaval_to_color(tolua_S, 6, &arg4, "ax.DrawNode:drawStar"); ok &= luaval_to_number(tolua_S, 7,&arg5, "ax.DrawNode:drawStar"); if(!ok) @@ -52159,8 +51988,8 @@ int lua_ax_base_DrawNode_drawSolidStar(lua_State* tolua_S) double arg1; double arg2; unsigned int arg3; - ax::Color4B arg4; - ax::Color4B arg5; + ax::Color arg4; + ax::Color arg5; ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.DrawNode:drawSolidStar"); @@ -52170,9 +51999,9 @@ int lua_ax_base_DrawNode_drawSolidStar(lua_State* tolua_S) ok &= luaval_to_uint32(tolua_S, 5,&arg3, "ax.DrawNode:drawSolidStar"); - ok &=luaval_to_color4b(tolua_S, 6, &arg4, "ax.DrawNode:drawSolidStar"); + ok &=luaval_to_color(tolua_S, 6, &arg4, "ax.DrawNode:drawSolidStar"); - ok &=luaval_to_color4b(tolua_S, 7, &arg5, "ax.DrawNode:drawSolidStar"); + ok &=luaval_to_color(tolua_S, 7, &arg5, "ax.DrawNode:drawSolidStar"); if(!ok) { tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_DrawNode_drawSolidStar'", nullptr); @@ -52188,8 +52017,8 @@ int lua_ax_base_DrawNode_drawSolidStar(lua_State* tolua_S) double arg1; double arg2; unsigned int arg3; - ax::Color4B arg4; - ax::Color4B arg5; + ax::Color arg4; + ax::Color arg5; double arg6; ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.DrawNode:drawSolidStar"); @@ -52200,9 +52029,9 @@ int lua_ax_base_DrawNode_drawSolidStar(lua_State* tolua_S) ok &= luaval_to_uint32(tolua_S, 5,&arg3, "ax.DrawNode:drawSolidStar"); - ok &=luaval_to_color4b(tolua_S, 6, &arg4, "ax.DrawNode:drawSolidStar"); + ok &=luaval_to_color(tolua_S, 6, &arg4, "ax.DrawNode:drawSolidStar"); - ok &=luaval_to_color4b(tolua_S, 7, &arg5, "ax.DrawNode:drawSolidStar"); + ok &=luaval_to_color(tolua_S, 7, &arg5, "ax.DrawNode:drawSolidStar"); ok &= luaval_to_number(tolua_S, 8,&arg6, "ax.DrawNode:drawSolidStar"); if(!ok) @@ -52256,7 +52085,7 @@ int lua_ax_base_DrawNode_drawQuadBezier(lua_State* tolua_S) ax::Vec2 arg1; ax::Vec2 arg2; unsigned int arg3; - ax::Color4B arg4; + ax::Color arg4; ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.DrawNode:drawQuadBezier"); @@ -52266,7 +52095,7 @@ int lua_ax_base_DrawNode_drawQuadBezier(lua_State* tolua_S) ok &= luaval_to_uint32(tolua_S, 5,&arg3, "ax.DrawNode:drawQuadBezier"); - ok &=luaval_to_color4b(tolua_S, 6, &arg4, "ax.DrawNode:drawQuadBezier"); + ok &=luaval_to_color(tolua_S, 6, &arg4, "ax.DrawNode:drawQuadBezier"); if(!ok) { tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_DrawNode_drawQuadBezier'", nullptr); @@ -52282,7 +52111,7 @@ int lua_ax_base_DrawNode_drawQuadBezier(lua_State* tolua_S) ax::Vec2 arg1; ax::Vec2 arg2; unsigned int arg3; - ax::Color4B arg4; + ax::Color arg4; double arg5; ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.DrawNode:drawQuadBezier"); @@ -52293,7 +52122,7 @@ int lua_ax_base_DrawNode_drawQuadBezier(lua_State* tolua_S) ok &= luaval_to_uint32(tolua_S, 5,&arg3, "ax.DrawNode:drawQuadBezier"); - ok &=luaval_to_color4b(tolua_S, 6, &arg4, "ax.DrawNode:drawQuadBezier"); + ok &=luaval_to_color(tolua_S, 6, &arg4, "ax.DrawNode:drawQuadBezier"); ok &= luaval_to_number(tolua_S, 7,&arg5, "ax.DrawNode:drawQuadBezier"); if(!ok) @@ -52348,7 +52177,7 @@ int lua_ax_base_DrawNode_drawCubicBezier(lua_State* tolua_S) ax::Vec2 arg2; ax::Vec2 arg3; unsigned int arg4; - ax::Color4B arg5; + ax::Color arg5; ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.DrawNode:drawCubicBezier"); @@ -52360,7 +52189,7 @@ int lua_ax_base_DrawNode_drawCubicBezier(lua_State* tolua_S) ok &= luaval_to_uint32(tolua_S, 6,&arg4, "ax.DrawNode:drawCubicBezier"); - ok &=luaval_to_color4b(tolua_S, 7, &arg5, "ax.DrawNode:drawCubicBezier"); + ok &=luaval_to_color(tolua_S, 7, &arg5, "ax.DrawNode:drawCubicBezier"); if(!ok) { tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_DrawNode_drawCubicBezier'", nullptr); @@ -52377,7 +52206,7 @@ int lua_ax_base_DrawNode_drawCubicBezier(lua_State* tolua_S) ax::Vec2 arg2; ax::Vec2 arg3; unsigned int arg4; - ax::Color4B arg5; + ax::Color arg5; double arg6; ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.DrawNode:drawCubicBezier"); @@ -52390,7 +52219,7 @@ int lua_ax_base_DrawNode_drawCubicBezier(lua_State* tolua_S) ok &= luaval_to_uint32(tolua_S, 6,&arg4, "ax.DrawNode:drawCubicBezier"); - ok &=luaval_to_color4b(tolua_S, 7, &arg5, "ax.DrawNode:drawCubicBezier"); + ok &=luaval_to_color(tolua_S, 7, &arg5, "ax.DrawNode:drawCubicBezier"); ok &= luaval_to_number(tolua_S, 8,&arg6, "ax.DrawNode:drawCubicBezier"); if(!ok) @@ -52442,13 +52271,13 @@ int lua_ax_base_DrawNode_drawDot(lua_State* tolua_S) { ax::Vec2 arg0; double arg1; - ax::Color4B arg2; + ax::Color arg2; ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.DrawNode:drawDot"); ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.DrawNode:drawDot"); - ok &=luaval_to_color4b(tolua_S, 4, &arg2, "ax.DrawNode:drawDot"); + ok &=luaval_to_color(tolua_S, 4, &arg2, "ax.DrawNode:drawDot"); if(!ok) { tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_DrawNode_drawDot'", nullptr); @@ -52498,13 +52327,13 @@ int lua_ax_base_DrawNode_drawSolidRect(lua_State* tolua_S) { ax::Vec2 arg0; ax::Vec2 arg1; - ax::Color4B arg2; + ax::Color arg2; ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.DrawNode:drawSolidRect"); ok &= luaval_to_vec2(tolua_S, 3, &arg1, "ax.DrawNode:drawSolidRect"); - ok &=luaval_to_color4b(tolua_S, 4, &arg2, "ax.DrawNode:drawSolidRect"); + ok &=luaval_to_color(tolua_S, 4, &arg2, "ax.DrawNode:drawSolidRect"); if(!ok) { tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_DrawNode_drawSolidRect'", nullptr); @@ -52518,14 +52347,14 @@ int lua_ax_base_DrawNode_drawSolidRect(lua_State* tolua_S) { ax::Vec2 arg0; ax::Vec2 arg1; - ax::Color4B arg2; + ax::Color arg2; double arg3; ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.DrawNode:drawSolidRect"); ok &= luaval_to_vec2(tolua_S, 3, &arg1, "ax.DrawNode:drawSolidRect"); - ok &=luaval_to_color4b(tolua_S, 4, &arg2, "ax.DrawNode:drawSolidRect"); + ok &=luaval_to_color(tolua_S, 4, &arg2, "ax.DrawNode:drawSolidRect"); ok &= luaval_to_number(tolua_S, 5,&arg3, "ax.DrawNode:drawSolidRect"); if(!ok) @@ -52541,19 +52370,19 @@ int lua_ax_base_DrawNode_drawSolidRect(lua_State* tolua_S) { ax::Vec2 arg0; ax::Vec2 arg1; - ax::Color4B arg2; + ax::Color arg2; double arg3; - ax::Color4B arg4; + ax::Color arg4; ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.DrawNode:drawSolidRect"); ok &= luaval_to_vec2(tolua_S, 3, &arg1, "ax.DrawNode:drawSolidRect"); - ok &=luaval_to_color4b(tolua_S, 4, &arg2, "ax.DrawNode:drawSolidRect"); + ok &=luaval_to_color(tolua_S, 4, &arg2, "ax.DrawNode:drawSolidRect"); ok &= luaval_to_number(tolua_S, 5,&arg3, "ax.DrawNode:drawSolidRect"); - ok &=luaval_to_color4b(tolua_S, 6, &arg4, "ax.DrawNode:drawSolidRect"); + ok &=luaval_to_color(tolua_S, 6, &arg4, "ax.DrawNode:drawSolidRect"); if(!ok) { tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_DrawNode_drawSolidRect'", nullptr); @@ -52620,8 +52449,8 @@ int lua_ax_base_DrawNode_drawSolidCircle(lua_State* tolua_S) ok &= luaval_to_number(tolua_S, 7,&arg5, "ax.DrawNode:drawSolidCircle"); if (!ok) { break; } - ax::Color4B arg6; - ok &=luaval_to_color4b(tolua_S, 8, &arg6, "ax.DrawNode:drawSolidCircle"); + ax::Color arg6; + ok &=luaval_to_color(tolua_S, 8, &arg6, "ax.DrawNode:drawSolidCircle"); if (!ok) { break; } cobj->drawSolidCircle(arg0, arg1, arg2, arg3, arg4, arg5, arg6); @@ -52656,16 +52485,16 @@ int lua_ax_base_DrawNode_drawSolidCircle(lua_State* tolua_S) ok &= luaval_to_number(tolua_S, 7,&arg5, "ax.DrawNode:drawSolidCircle"); if (!ok) { break; } - ax::Color4B arg6; - ok &=luaval_to_color4b(tolua_S, 8, &arg6, "ax.DrawNode:drawSolidCircle"); + ax::Color arg6; + ok &=luaval_to_color(tolua_S, 8, &arg6, "ax.DrawNode:drawSolidCircle"); if (!ok) { break; } double arg7; ok &= luaval_to_number(tolua_S, 9,&arg7, "ax.DrawNode:drawSolidCircle"); if (!ok) { break; } - ax::Color4B arg8; - ok &=luaval_to_color4b(tolua_S, 10, &arg8, "ax.DrawNode:drawSolidCircle"); + ax::Color arg8; + ok &=luaval_to_color(tolua_S, 10, &arg8, "ax.DrawNode:drawSolidCircle"); if (!ok) { break; } cobj->drawSolidCircle(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); @@ -52700,16 +52529,16 @@ int lua_ax_base_DrawNode_drawSolidCircle(lua_State* tolua_S) ok &= luaval_to_number(tolua_S, 7,&arg5, "ax.DrawNode:drawSolidCircle"); if (!ok) { break; } - ax::Color4B arg6; - ok &=luaval_to_color4b(tolua_S, 8, &arg6, "ax.DrawNode:drawSolidCircle"); + ax::Color arg6; + ok &=luaval_to_color(tolua_S, 8, &arg6, "ax.DrawNode:drawSolidCircle"); if (!ok) { break; } double arg7; ok &= luaval_to_number(tolua_S, 9,&arg7, "ax.DrawNode:drawSolidCircle"); if (!ok) { break; } - ax::Color4B arg8; - ok &=luaval_to_color4b(tolua_S, 10, &arg8, "ax.DrawNode:drawSolidCircle"); + ax::Color arg8; + ok &=luaval_to_color(tolua_S, 10, &arg8, "ax.DrawNode:drawSolidCircle"); if (!ok) { break; } bool arg9; @@ -52740,8 +52569,8 @@ int lua_ax_base_DrawNode_drawSolidCircle(lua_State* tolua_S) ok &= luaval_to_uint32(tolua_S, 5,&arg3, "ax.DrawNode:drawSolidCircle"); if (!ok) { break; } - ax::Color4B arg4; - ok &=luaval_to_color4b(tolua_S, 6, &arg4, "ax.DrawNode:drawSolidCircle"); + ax::Color arg4; + ok &=luaval_to_color(tolua_S, 6, &arg4, "ax.DrawNode:drawSolidCircle"); if (!ok) { break; } cobj->drawSolidCircle(arg0, arg1, arg2, arg3, arg4); @@ -52811,8 +52640,8 @@ int lua_ax_base_DrawNode_drawPie(lua_State* tolua_S) ok &= luaval_to_number(tolua_S, 8,&arg6, "ax.DrawNode:drawPie"); if (!ok) { break; } - ax::Color4B arg7; - ok &=luaval_to_color4b(tolua_S, 9, &arg7, "ax.DrawNode:drawPie"); + ax::Color arg7; + ok &=luaval_to_color(tolua_S, 9, &arg7, "ax.DrawNode:drawPie"); if (!ok) { break; } cobj->drawPie(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7); @@ -52851,8 +52680,8 @@ int lua_ax_base_DrawNode_drawPie(lua_State* tolua_S) ok &= luaval_to_number(tolua_S, 8,&arg6, "ax.DrawNode:drawPie"); if (!ok) { break; } - ax::Color4B arg7; - ok &=luaval_to_color4b(tolua_S, 9, &arg7, "ax.DrawNode:drawPie"); + ax::Color arg7; + ok &=luaval_to_color(tolua_S, 9, &arg7, "ax.DrawNode:drawPie"); if (!ok) { break; } ax::DrawNode::DrawMode arg8; @@ -52895,12 +52724,12 @@ int lua_ax_base_DrawNode_drawPie(lua_State* tolua_S) ok &= luaval_to_number(tolua_S, 8,&arg6, "ax.DrawNode:drawPie"); if (!ok) { break; } - ax::Color4B arg7; - ok &=luaval_to_color4b(tolua_S, 9, &arg7, "ax.DrawNode:drawPie"); + ax::Color arg7; + ok &=luaval_to_color(tolua_S, 9, &arg7, "ax.DrawNode:drawPie"); if (!ok) { break; } - ax::Color4B arg8; - ok &=luaval_to_color4b(tolua_S, 10, &arg8, "ax.DrawNode:drawPie"); + ax::Color arg8; + ok &=luaval_to_color(tolua_S, 10, &arg8, "ax.DrawNode:drawPie"); if (!ok) { break; } cobj->drawPie(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); @@ -52939,12 +52768,12 @@ int lua_ax_base_DrawNode_drawPie(lua_State* tolua_S) ok &= luaval_to_number(tolua_S, 8,&arg6, "ax.DrawNode:drawPie"); if (!ok) { break; } - ax::Color4B arg7; - ok &=luaval_to_color4b(tolua_S, 9, &arg7, "ax.DrawNode:drawPie"); + ax::Color arg7; + ok &=luaval_to_color(tolua_S, 9, &arg7, "ax.DrawNode:drawPie"); if (!ok) { break; } - ax::Color4B arg8; - ok &=luaval_to_color4b(tolua_S, 10, &arg8, "ax.DrawNode:drawPie"); + ax::Color arg8; + ok &=luaval_to_color(tolua_S, 10, &arg8, "ax.DrawNode:drawPie"); if (!ok) { break; } ax::DrawNode::DrawMode arg9; @@ -52987,12 +52816,12 @@ int lua_ax_base_DrawNode_drawPie(lua_State* tolua_S) ok &= luaval_to_number(tolua_S, 8,&arg6, "ax.DrawNode:drawPie"); if (!ok) { break; } - ax::Color4B arg7; - ok &=luaval_to_color4b(tolua_S, 9, &arg7, "ax.DrawNode:drawPie"); + ax::Color arg7; + ok &=luaval_to_color(tolua_S, 9, &arg7, "ax.DrawNode:drawPie"); if (!ok) { break; } - ax::Color4B arg8; - ok &=luaval_to_color4b(tolua_S, 10, &arg8, "ax.DrawNode:drawPie"); + ax::Color arg8; + ok &=luaval_to_color(tolua_S, 10, &arg8, "ax.DrawNode:drawPie"); if (!ok) { break; } ax::DrawNode::DrawMode arg9; @@ -53019,56 +52848,6 @@ int lua_ax_base_DrawNode_drawPie(lua_State* tolua_S) return 0; } -int lua_ax_base_DrawNode_setIsConvex(lua_State* tolua_S) -{ - int argc = 0; - ax::DrawNode* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.DrawNode",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::DrawNode*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_base_DrawNode_setIsConvex'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - bool arg0; - - ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.DrawNode:setIsConvex"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_DrawNode_setIsConvex'", nullptr); - return 0; - } - cobj->setIsConvex(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.DrawNode:setIsConvex",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_base_DrawNode_setIsConvex'.",&tolua_err); -#endif - - return 0; -} int lua_ax_base_DrawNode_drawSegment(lua_State* tolua_S) { int argc = 0; @@ -53100,7 +52879,7 @@ int lua_ax_base_DrawNode_drawSegment(lua_State* tolua_S) ax::Vec2 arg0; ax::Vec2 arg1; double arg2; - ax::Color4B arg3; + ax::Color arg3; ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.DrawNode:drawSegment"); @@ -53108,7 +52887,7 @@ int lua_ax_base_DrawNode_drawSegment(lua_State* tolua_S) ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.DrawNode:drawSegment"); - ok &=luaval_to_color4b(tolua_S, 5, &arg3, "ax.DrawNode:drawSegment"); + ok &=luaval_to_color(tolua_S, 5, &arg3, "ax.DrawNode:drawSegment"); if(!ok) { tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_DrawNode_drawSegment'", nullptr); @@ -53123,7 +52902,7 @@ int lua_ax_base_DrawNode_drawSegment(lua_State* tolua_S) ax::Vec2 arg0; ax::Vec2 arg1; double arg2; - ax::Color4B arg3; + ax::Color arg3; ax::DrawNode::EndType arg4; ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.DrawNode:drawSegment"); @@ -53132,7 +52911,7 @@ int lua_ax_base_DrawNode_drawSegment(lua_State* tolua_S) ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.DrawNode:drawSegment"); - ok &=luaval_to_color4b(tolua_S, 5, &arg3, "ax.DrawNode:drawSegment"); + ok &=luaval_to_color(tolua_S, 5, &arg3, "ax.DrawNode:drawSegment"); ok &= luaval_to_int32(tolua_S, 6,(int *)&arg4, "ax.DrawNode:drawSegment"); if(!ok) @@ -53149,7 +52928,7 @@ int lua_ax_base_DrawNode_drawSegment(lua_State* tolua_S) ax::Vec2 arg0; ax::Vec2 arg1; double arg2; - ax::Color4B arg3; + ax::Color arg3; ax::DrawNode::EndType arg4; ax::DrawNode::EndType arg5; @@ -53159,7 +52938,7 @@ int lua_ax_base_DrawNode_drawSegment(lua_State* tolua_S) ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.DrawNode:drawSegment"); - ok &=luaval_to_color4b(tolua_S, 5, &arg3, "ax.DrawNode:drawSegment"); + ok &=luaval_to_color(tolua_S, 5, &arg3, "ax.DrawNode:drawSegment"); ok &= luaval_to_int32(tolua_S, 6,(int *)&arg4, "ax.DrawNode:drawSegment"); @@ -53212,11 +52991,11 @@ int lua_ax_base_DrawNode_drawColoredTriangle(lua_State* tolua_S) if (argc == 2) { const ax::Vec2* arg0; - const ax::Color4B* arg1; + const ax::Color* arg1; ok &= luaval_to_object(tolua_S, 2, "ax.Vec2",&arg0, "ax.DrawNode:drawColoredTriangle"); - #pragma warning NO CONVERSION TO NATIVE FOR Color4B* + #pragma warning NO CONVERSION TO NATIVE FOR Color* ok = false; if(!ok) { @@ -53272,8 +53051,8 @@ int lua_ax_base_DrawNode_drawTriangle(lua_State* tolua_S) ok &= luaval_to_vec2(tolua_S, 4, &arg2, "ax.DrawNode:drawTriangle"); if (!ok) { break; } - ax::Color4B arg3; - ok &=luaval_to_color4b(tolua_S, 5, &arg3, "ax.DrawNode:drawTriangle"); + ax::Color arg3; + ok &=luaval_to_color(tolua_S, 5, &arg3, "ax.DrawNode:drawTriangle"); if (!ok) { break; } cobj->drawTriangle(arg0, arg1, arg2, arg3); @@ -53288,8 +53067,8 @@ int lua_ax_base_DrawNode_drawTriangle(lua_State* tolua_S) ok &= luaval_to_object(tolua_S, 2, "ax.Vec2",&arg0, "ax.DrawNode:drawTriangle"); if (!ok) { break; } - ax::Color4B arg1; - ok &=luaval_to_color4b(tolua_S, 3, &arg1, "ax.DrawNode:drawTriangle"); + ax::Color arg1; + ok &=luaval_to_color(tolua_S, 3, &arg1, "ax.DrawNode:drawTriangle"); if (!ok) { break; } cobj->drawTriangle(arg0, arg1); @@ -53343,12 +53122,12 @@ int lua_ax_base_DrawNode_drawSolidTriangle(lua_State* tolua_S) ok &= luaval_to_vec2(tolua_S, 4, &arg2, "ax.DrawNode:drawSolidTriangle"); if (!ok) { break; } - ax::Color4B arg3; - ok &=luaval_to_color4b(tolua_S, 5, &arg3, "ax.DrawNode:drawSolidTriangle"); + ax::Color arg3; + ok &=luaval_to_color(tolua_S, 5, &arg3, "ax.DrawNode:drawSolidTriangle"); if (!ok) { break; } - ax::Color4B arg4; - ok &=luaval_to_color4b(tolua_S, 6, &arg4, "ax.DrawNode:drawSolidTriangle"); + ax::Color arg4; + ok &=luaval_to_color(tolua_S, 6, &arg4, "ax.DrawNode:drawSolidTriangle"); if (!ok) { break; } cobj->drawSolidTriangle(arg0, arg1, arg2, arg3, arg4); @@ -53371,12 +53150,12 @@ int lua_ax_base_DrawNode_drawSolidTriangle(lua_State* tolua_S) ok &= luaval_to_vec2(tolua_S, 4, &arg2, "ax.DrawNode:drawSolidTriangle"); if (!ok) { break; } - ax::Color4B arg3; - ok &=luaval_to_color4b(tolua_S, 5, &arg3, "ax.DrawNode:drawSolidTriangle"); + ax::Color arg3; + ok &=luaval_to_color(tolua_S, 5, &arg3, "ax.DrawNode:drawSolidTriangle"); if (!ok) { break; } - ax::Color4B arg4; - ok &=luaval_to_color4b(tolua_S, 6, &arg4, "ax.DrawNode:drawSolidTriangle"); + ax::Color arg4; + ok &=luaval_to_color(tolua_S, 6, &arg4, "ax.DrawNode:drawSolidTriangle"); if (!ok) { break; } double arg5; @@ -53395,12 +53174,12 @@ int lua_ax_base_DrawNode_drawSolidTriangle(lua_State* tolua_S) ok &= luaval_to_object(tolua_S, 2, "ax.Vec2",&arg0, "ax.DrawNode:drawSolidTriangle"); if (!ok) { break; } - ax::Color4B arg1; - ok &=luaval_to_color4b(tolua_S, 3, &arg1, "ax.DrawNode:drawSolidTriangle"); + ax::Color arg1; + ok &=luaval_to_color(tolua_S, 3, &arg1, "ax.DrawNode:drawSolidTriangle"); if (!ok) { break; } - ax::Color4B arg2; - ok &=luaval_to_color4b(tolua_S, 4, &arg2, "ax.DrawNode:drawSolidTriangle"); + ax::Color arg2; + ok &=luaval_to_color(tolua_S, 4, &arg2, "ax.DrawNode:drawSolidTriangle"); if (!ok) { break; } cobj->drawSolidTriangle(arg0, arg1, arg2); @@ -53415,12 +53194,12 @@ int lua_ax_base_DrawNode_drawSolidTriangle(lua_State* tolua_S) ok &= luaval_to_object(tolua_S, 2, "ax.Vec2",&arg0, "ax.DrawNode:drawSolidTriangle"); if (!ok) { break; } - ax::Color4B arg1; - ok &=luaval_to_color4b(tolua_S, 3, &arg1, "ax.DrawNode:drawSolidTriangle"); + ax::Color arg1; + ok &=luaval_to_color(tolua_S, 3, &arg1, "ax.DrawNode:drawSolidTriangle"); if (!ok) { break; } - ax::Color4B arg2; - ok &=luaval_to_color4b(tolua_S, 4, &arg2, "ax.DrawNode:drawSolidTriangle"); + ax::Color arg2; + ok &=luaval_to_color(tolua_S, 4, &arg2, "ax.DrawNode:drawSolidTriangle"); if (!ok) { break; } double arg3; @@ -53780,7 +53559,6 @@ int lua_register_ax_base_DrawNode(lua_State* tolua_S) tolua_function(tolua_S,"drawSolidRect",lua_ax_base_DrawNode_drawSolidRect); tolua_function(tolua_S,"drawSolidCircle",lua_ax_base_DrawNode_drawSolidCircle); tolua_function(tolua_S,"drawPie",lua_ax_base_DrawNode_drawPie); - tolua_function(tolua_S,"setIsConvex",lua_ax_base_DrawNode_setIsConvex); tolua_function(tolua_S,"drawSegment",lua_ax_base_DrawNode_drawSegment); tolua_function(tolua_S,"drawColoredTriangle",lua_ax_base_DrawNode_drawColoredTriangle); tolua_function(tolua_S,"drawTriangle",lua_ax_base_DrawNode_drawTriangle); @@ -54632,9 +54410,9 @@ int lua_ax_base_Label_setTextColor(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Color4B arg0; + ax::Color32 arg0; - ok &=luaval_to_color4b(tolua_S, 2, &arg0, "ax.Label:setTextColor"); + ok &=luaval_to_color32(tolua_S, 2, &arg0, "ax.Label:setTextColor"); if(!ok) { tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Label_setTextColor'", nullptr); @@ -54688,7 +54466,7 @@ int lua_ax_base_Label_getTextColor(lua_State* tolua_S) return 0; } auto&& ret = cobj->getTextColor(); - color4b_to_luaval(tolua_S, ret); + color32_to_luaval(tolua_S, ret); return 1; } luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Label:getTextColor",argc, 0); @@ -54740,9 +54518,9 @@ int lua_ax_base_Label_enableShadow(lua_State* tolua_S) } if (argc == 1) { - ax::Color4B arg0; + ax::Color32 arg0; - ok &=luaval_to_color4b(tolua_S, 2, &arg0, "ax.Label:enableShadow"); + ok &=luaval_to_color32(tolua_S, 2, &arg0, "ax.Label:enableShadow"); if(!ok) { tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Label_enableShadow'", nullptr); @@ -54754,10 +54532,10 @@ int lua_ax_base_Label_enableShadow(lua_State* tolua_S) } if (argc == 2) { - ax::Color4B arg0; + ax::Color32 arg0; ax::Vec2 arg1; - ok &=luaval_to_color4b(tolua_S, 2, &arg0, "ax.Label:enableShadow"); + ok &=luaval_to_color32(tolua_S, 2, &arg0, "ax.Label:enableShadow"); ok &= luaval_to_vec2(tolua_S, 3, &arg1, "ax.Label:enableShadow"); if(!ok) @@ -54771,11 +54549,11 @@ int lua_ax_base_Label_enableShadow(lua_State* tolua_S) } if (argc == 3) { - ax::Color4B arg0; + ax::Color32 arg0; ax::Vec2 arg1; int arg2; - ok &=luaval_to_color4b(tolua_S, 2, &arg0, "ax.Label:enableShadow"); + ok &=luaval_to_color32(tolua_S, 2, &arg0, "ax.Label:enableShadow"); ok &= luaval_to_vec2(tolua_S, 3, &arg1, "ax.Label:enableShadow"); @@ -54827,9 +54605,9 @@ int lua_ax_base_Label_enableOutline(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Color4B arg0; + ax::Color32 arg0; - ok &=luaval_to_color4b(tolua_S, 2, &arg0, "ax.Label:enableOutline"); + ok &=luaval_to_color32(tolua_S, 2, &arg0, "ax.Label:enableOutline"); if(!ok) { tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Label_enableOutline'", nullptr); @@ -54841,10 +54619,10 @@ int lua_ax_base_Label_enableOutline(lua_State* tolua_S) } if (argc == 2) { - ax::Color4B arg0; + ax::Color32 arg0; int arg1; - ok &=luaval_to_color4b(tolua_S, 2, &arg0, "ax.Label:enableOutline"); + ok &=luaval_to_color32(tolua_S, 2, &arg0, "ax.Label:enableOutline"); ok &= luaval_to_int32(tolua_S, 3,(int *)&arg1, "ax.Label:enableOutline"); if(!ok) @@ -54894,9 +54672,9 @@ int lua_ax_base_Label_enableGlow(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Color4B arg0; + ax::Color32 arg0; - ok &=luaval_to_color4b(tolua_S, 2, &arg0, "ax.Label:enableGlow"); + ok &=luaval_to_color32(tolua_S, 2, &arg0, "ax.Label:enableGlow"); if(!ok) { tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_Label_enableGlow'", nullptr); @@ -55330,7 +55108,7 @@ int lua_ax_base_Label_getShadowColor(lua_State* tolua_S) return 0; } auto&& ret = cobj->getShadowColor(); - color4f_to_luaval(tolua_S, ret); + color_to_luaval(tolua_S, ret); return 1; } luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Label:getShadowColor",argc, 0); @@ -55471,7 +55249,7 @@ int lua_ax_base_Label_getEffectColor(lua_State* tolua_S) return 0; } auto&& ret = cobj->getEffectColor(); - color4f_to_luaval(tolua_S, ret); + color_to_luaval(tolua_S, ret); return 1; } luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Label:getEffectColor",argc, 0); @@ -60975,8 +60753,8 @@ int lua_ax_base_LayerColor_initWithColor(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; do{ if (argc == 1) { - ax::Color4B arg0; - ok &=luaval_to_color4b(tolua_S, 2, &arg0, "ax.LayerColor:initWithColor"); + ax::Color32 arg0; + ok &=luaval_to_color32(tolua_S, 2, &arg0, "ax.LayerColor:initWithColor"); if (!ok) { break; } bool ret = cobj->initWithColor(arg0); @@ -60987,8 +60765,8 @@ int lua_ax_base_LayerColor_initWithColor(lua_State* tolua_S) ok = true; do{ if (argc == 3) { - ax::Color4B arg0; - ok &=luaval_to_color4b(tolua_S, 2, &arg0, "ax.LayerColor:initWithColor"); + ax::Color32 arg0; + ok &=luaval_to_color32(tolua_S, 2, &arg0, "ax.LayerColor:initWithColor"); if (!ok) { break; } double arg1; @@ -61033,8 +60811,8 @@ int lua_ax_base_LayerColor_create(lua_State* tolua_S) { if (argc == 3) { - ax::Color4B arg0; - ok &=luaval_to_color4b(tolua_S, 2, &arg0, "ax.LayerColor:create"); + ax::Color32 arg0; + ok &=luaval_to_color32(tolua_S, 2, &arg0, "ax.LayerColor:create"); if (!ok) { break; } double arg1; ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.LayerColor:create"); @@ -61062,8 +60840,8 @@ int lua_ax_base_LayerColor_create(lua_State* tolua_S) { if (argc == 1) { - ax::Color4B arg0; - ok &=luaval_to_color4b(tolua_S, 2, &arg0, "ax.LayerColor:create"); + ax::Color32 arg0; + ok &=luaval_to_color32(tolua_S, 2, &arg0, "ax.LayerColor:create"); if (!ok) { break; } ax::LayerColor* ret = ax::LayerColor::create(arg0); object_to_luaval(tolua_S, "ax.LayerColor",(ax::LayerColor*)ret); @@ -61746,12 +61524,12 @@ int lua_ax_base_LayerGradient_initWithColor(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; do{ if (argc == 3) { - ax::Color4B arg0; - ok &=luaval_to_color4b(tolua_S, 2, &arg0, "ax.LayerGradient:initWithColor"); + ax::Color32 arg0; + ok &=luaval_to_color32(tolua_S, 2, &arg0, "ax.LayerGradient:initWithColor"); if (!ok) { break; } - ax::Color4B arg1; - ok &=luaval_to_color4b(tolua_S, 3, &arg1, "ax.LayerGradient:initWithColor"); + ax::Color32 arg1; + ok &=luaval_to_color32(tolua_S, 3, &arg1, "ax.LayerGradient:initWithColor"); if (!ok) { break; } ax::Vec2 arg2; @@ -61766,12 +61544,12 @@ int lua_ax_base_LayerGradient_initWithColor(lua_State* tolua_S) ok = true; do{ if (argc == 2) { - ax::Color4B arg0; - ok &=luaval_to_color4b(tolua_S, 2, &arg0, "ax.LayerGradient:initWithColor"); + ax::Color32 arg0; + ok &=luaval_to_color32(tolua_S, 2, &arg0, "ax.LayerGradient:initWithColor"); if (!ok) { break; } - ax::Color4B arg1; - ok &=luaval_to_color4b(tolua_S, 3, &arg1, "ax.LayerGradient:initWithColor"); + ax::Color32 arg1; + ok &=luaval_to_color32(tolua_S, 3, &arg1, "ax.LayerGradient:initWithColor"); if (!ok) { break; } bool ret = cobj->initWithColor(arg0, arg1); @@ -61808,11 +61586,11 @@ int lua_ax_base_LayerGradient_create(lua_State* tolua_S) { if (argc == 2) { - ax::Color4B arg0; - ok &=luaval_to_color4b(tolua_S, 2, &arg0, "ax.LayerGradient:create"); + ax::Color32 arg0; + ok &=luaval_to_color32(tolua_S, 2, &arg0, "ax.LayerGradient:create"); if (!ok) { break; } - ax::Color4B arg1; - ok &=luaval_to_color4b(tolua_S, 3, &arg1, "ax.LayerGradient:create"); + ax::Color32 arg1; + ok &=luaval_to_color32(tolua_S, 3, &arg1, "ax.LayerGradient:create"); if (!ok) { break; } ax::LayerGradient* ret = ax::LayerGradient::create(arg0, arg1); object_to_luaval(tolua_S, "ax.LayerGradient",(ax::LayerGradient*)ret); @@ -61834,11 +61612,11 @@ int lua_ax_base_LayerGradient_create(lua_State* tolua_S) { if (argc == 3) { - ax::Color4B arg0; - ok &=luaval_to_color4b(tolua_S, 2, &arg0, "ax.LayerGradient:create"); + ax::Color32 arg0; + ok &=luaval_to_color32(tolua_S, 2, &arg0, "ax.LayerGradient:create"); if (!ok) { break; } - ax::Color4B arg1; - ok &=luaval_to_color4b(tolua_S, 3, &arg1, "ax.LayerGradient:create"); + ax::Color32 arg1; + ok &=luaval_to_color32(tolua_S, 3, &arg1, "ax.LayerGradient:create"); if (!ok) { break; } ax::Vec2 arg2; ok &= luaval_to_vec2(tolua_S, 4, &arg2, "ax.LayerGradient:create"); @@ -62436,8 +62214,8 @@ int lua_ax_base_LayerRadialGradient_setStartColor(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; do{ if (argc == 1) { - ax::Color4B arg0; - ok &=luaval_to_color4b(tolua_S, 2, &arg0, "ax.LayerRadialGradient:setStartColor"); + ax::Color32 arg0; + ok &=luaval_to_color32(tolua_S, 2, &arg0, "ax.LayerRadialGradient:setStartColor"); if (!ok) { break; } cobj->setStartColor(arg0); @@ -62502,7 +62280,7 @@ int lua_ax_base_LayerRadialGradient_getStartColor(lua_State* tolua_S) return 0; } auto&& ret = cobj->getStartColor(); - color4b_to_luaval(tolua_S, ret); + color32_to_luaval(tolua_S, ret); return 1; } luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.LayerRadialGradient:getStartColor",argc, 0); @@ -62585,8 +62363,8 @@ int lua_ax_base_LayerRadialGradient_setEndColor(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; do{ if (argc == 1) { - ax::Color4B arg0; - ok &=luaval_to_color4b(tolua_S, 2, &arg0, "ax.LayerRadialGradient:setEndColor"); + ax::Color32 arg0; + ok &=luaval_to_color32(tolua_S, 2, &arg0, "ax.LayerRadialGradient:setEndColor"); if (!ok) { break; } cobj->setEndColor(arg0); @@ -62651,7 +62429,7 @@ int lua_ax_base_LayerRadialGradient_getEndColor(lua_State* tolua_S) return 0; } auto&& ret = cobj->getEndColor(); - color4b_to_luaval(tolua_S, ret); + color32_to_luaval(tolua_S, ret); return 1; } luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.LayerRadialGradient:getEndColor",argc, 0); @@ -62836,15 +62614,15 @@ int lua_ax_base_LayerRadialGradient_initWithColor(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 5) { - ax::Color4B arg0; - ax::Color4B arg1; + ax::Color32 arg0; + ax::Color32 arg1; double arg2; ax::Vec2 arg3; double arg4; - ok &=luaval_to_color4b(tolua_S, 2, &arg0, "ax.LayerRadialGradient:initWithColor"); + ok &=luaval_to_color32(tolua_S, 2, &arg0, "ax.LayerRadialGradient:initWithColor"); - ok &=luaval_to_color4b(tolua_S, 3, &arg1, "ax.LayerRadialGradient:initWithColor"); + ok &=luaval_to_color32(tolua_S, 3, &arg1, "ax.LayerRadialGradient:initWithColor"); ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.LayerRadialGradient:initWithColor"); @@ -62898,11 +62676,11 @@ int lua_ax_base_LayerRadialGradient_create(lua_State* tolua_S) { if (argc == 5) { - ax::Color4B arg0; - ok &=luaval_to_color4b(tolua_S, 2, &arg0, "ax.LayerRadialGradient:create"); + ax::Color32 arg0; + ok &=luaval_to_color32(tolua_S, 2, &arg0, "ax.LayerRadialGradient:create"); if (!ok) { break; } - ax::Color4B arg1; - ok &=luaval_to_color4b(tolua_S, 3, &arg1, "ax.LayerRadialGradient:create"); + ax::Color32 arg1; + ok &=luaval_to_color32(tolua_S, 3, &arg1, "ax.LayerRadialGradient:create"); if (!ok) { break; } double arg2; ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.LayerRadialGradient:create"); @@ -73490,7 +73268,7 @@ int lua_ax_base_ParticleSystem_getStartColor(lua_State* tolua_S) return 0; } auto&& ret = cobj->getStartColor(); - color4f_to_luaval(tolua_S, ret); + color_to_luaval(tolua_S, ret); return 1; } luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.ParticleSystem:getStartColor",argc, 0); @@ -73531,9 +73309,9 @@ int lua_ax_base_ParticleSystem_setStartColor(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Color4F arg0; + ax::Color arg0; - ok &=luaval_to_color4f(tolua_S, 2, &arg0, "ax.ParticleSystem:setStartColor"); + ok &=luaval_to_color(tolua_S, 2, &arg0, "ax.ParticleSystem:setStartColor"); if(!ok) { tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_ParticleSystem_setStartColor'", nullptr); @@ -73587,7 +73365,7 @@ int lua_ax_base_ParticleSystem_getStartColorVar(lua_State* tolua_S) return 0; } auto&& ret = cobj->getStartColorVar(); - color4f_to_luaval(tolua_S, ret); + color_to_luaval(tolua_S, ret); return 1; } luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.ParticleSystem:getStartColorVar",argc, 0); @@ -73628,9 +73406,9 @@ int lua_ax_base_ParticleSystem_setStartColorVar(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Color4F arg0; + ax::Color arg0; - ok &=luaval_to_color4f(tolua_S, 2, &arg0, "ax.ParticleSystem:setStartColorVar"); + ok &=luaval_to_color(tolua_S, 2, &arg0, "ax.ParticleSystem:setStartColorVar"); if(!ok) { tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_ParticleSystem_setStartColorVar'", nullptr); @@ -73684,7 +73462,7 @@ int lua_ax_base_ParticleSystem_getEndColor(lua_State* tolua_S) return 0; } auto&& ret = cobj->getEndColor(); - color4f_to_luaval(tolua_S, ret); + color_to_luaval(tolua_S, ret); return 1; } luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.ParticleSystem:getEndColor",argc, 0); @@ -73725,9 +73503,9 @@ int lua_ax_base_ParticleSystem_setEndColor(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Color4F arg0; + ax::Color arg0; - ok &=luaval_to_color4f(tolua_S, 2, &arg0, "ax.ParticleSystem:setEndColor"); + ok &=luaval_to_color(tolua_S, 2, &arg0, "ax.ParticleSystem:setEndColor"); if(!ok) { tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_ParticleSystem_setEndColor'", nullptr); @@ -73781,7 +73559,7 @@ int lua_ax_base_ParticleSystem_getEndColorVar(lua_State* tolua_S) return 0; } auto&& ret = cobj->getEndColorVar(); - color4f_to_luaval(tolua_S, ret); + color_to_luaval(tolua_S, ret); return 1; } luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.ParticleSystem:getEndColorVar",argc, 0); @@ -73822,9 +73600,9 @@ int lua_ax_base_ParticleSystem_setEndColorVar(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Color4F arg0; + ax::Color arg0; - ok &=luaval_to_color4f(tolua_S, 2, &arg0, "ax.ParticleSystem:setEndColorVar"); + ok &=luaval_to_color(tolua_S, 2, &arg0, "ax.ParticleSystem:setEndColorVar"); if(!ok) { tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_ParticleSystem_setEndColorVar'", nullptr); @@ -83786,7 +83564,7 @@ int lua_ax_base_RenderTexture_getClearColor(lua_State* tolua_S) return 0; } auto&& ret = cobj->getClearColor(); - color4f_to_luaval(tolua_S, ret); + color_to_luaval(tolua_S, ret); return 1; } luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.RenderTexture:getClearColor",argc, 0); @@ -83827,9 +83605,9 @@ int lua_ax_base_RenderTexture_setClearColor(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Color4F arg0; + ax::Color arg0; - ok &=luaval_to_color4f(tolua_S, 2, &arg0, "ax.RenderTexture:setClearColor"); + ok &=luaval_to_color(tolua_S, 2, &arg0, "ax.RenderTexture:setClearColor"); if(!ok) { tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_RenderTexture_setClearColor'", nullptr); @@ -91709,9 +91487,9 @@ int lua_ax_base_CameraBackgroundBrush_createColorBrush(lua_State* tolua_S) if (argc == 2) { - ax::Color4F arg0; + ax::Color arg0; double arg1; - ok &=luaval_to_color4f(tolua_S, 2, &arg0, "ax.CameraBackgroundBrush:createColorBrush"); + ok &=luaval_to_color(tolua_S, 2, &arg0, "ax.CameraBackgroundBrush:createColorBrush"); ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.CameraBackgroundBrush:createColorBrush"); if(!ok) { @@ -92014,9 +91792,9 @@ int lua_ax_base_CameraBackgroundColorBrush_setColor(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Color4F arg0; + ax::Color arg0; - ok &=luaval_to_color4f(tolua_S, 2, &arg0, "ax.CameraBackgroundColorBrush:setColor"); + ok &=luaval_to_color(tolua_S, 2, &arg0, "ax.CameraBackgroundColorBrush:setColor"); if(!ok) { tolua_error(tolua_S,"invalid arguments in function 'lua_ax_base_CameraBackgroundColorBrush_setColor'", nullptr); @@ -92053,9 +91831,9 @@ int lua_ax_base_CameraBackgroundColorBrush_create(lua_State* tolua_S) if (argc == 2) { - ax::Color4F arg0; + ax::Color arg0; double arg1; - ok &=luaval_to_color4f(tolua_S, 2, &arg0, "ax.CameraBackgroundColorBrush:create"); + ok &=luaval_to_color(tolua_S, 2, &arg0, "ax.CameraBackgroundColorBrush:create"); ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.CameraBackgroundColorBrush:create"); if(!ok) { @@ -99478,14 +99256,14 @@ int lua_ax_base_Renderer_clear(lua_State* tolua_S) if (argc == 5) { ax::backend::TargetBufferFlags arg0; - ax::Color4F arg1; + ax::Color arg1; double arg2; unsigned int arg3; double arg4; ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.Renderer:clear"); - ok &=luaval_to_color4f(tolua_S, 3, &arg1, "ax.Renderer:clear"); + ok &=luaval_to_color(tolua_S, 3, &arg1, "ax.Renderer:clear"); ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.Renderer:clear"); @@ -99545,7 +99323,7 @@ int lua_ax_base_Renderer_getClearColor(lua_State* tolua_S) return 0; } auto&& ret = cobj->getClearColor(); - color4f_to_luaval(tolua_S, ret); + color_to_luaval(tolua_S, ret); return 1; } luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.Renderer:getClearColor",argc, 0); @@ -112743,25 +112521,20 @@ TOLUA_API int register_all_ax_base(lua_State* tolua_S) tolua_beginmodule(tolua_S,"ax"); lua_register_ax_base_Object(tolua_S); - lua_register_ax_base_EventListener(tolua_S); - lua_register_ax_base_EventListenerCustom(tolua_S); - lua_register_ax_base_ShaderCache(tolua_S); - lua_register_ax_base_Texture2D(tolua_S); lua_register_ax_base_Touch(tolua_S); lua_register_ax_base_Event(tolua_S); lua_register_ax_base_EventTouch(tolua_S); lua_register_ax_base_EventKeyboard(tolua_S); - lua_register_ax_base_Component(tolua_S); - lua_register_ax_base_Node(tolua_S); - lua_register_ax_base_Scene(tolua_S); - lua_register_ax_base_GLView(tolua_S); - lua_register_ax_base_Director(tolua_S); - lua_register_ax_base_Timer(tolua_S); - lua_register_ax_base_Scheduler(tolua_S); lua_register_ax_base_Action(tolua_S); lua_register_ax_base_FiniteTimeAction(tolua_S); lua_register_ax_base_Speed(tolua_S); lua_register_ax_base_Follow(tolua_S); + lua_register_ax_base_EventListener(tolua_S); + lua_register_ax_base_EventListenerCustom(tolua_S); + lua_register_ax_base_ShaderCache(tolua_S); + lua_register_ax_base_Texture2D(tolua_S); + lua_register_ax_base_Component(tolua_S); + lua_register_ax_base_Node(tolua_S); lua_register_ax_base_Image(tolua_S); lua_register_ax_base_PolygonInfo(tolua_S); lua_register_ax_base_AutoPolygon(tolua_S); @@ -112795,7 +112568,12 @@ TOLUA_API int register_all_ax_base(lua_State* tolua_S) lua_register_ax_base_Animate(tolua_S); lua_register_ax_base_TargetedAction(tolua_S); lua_register_ax_base_ActionFloat(tolua_S); + lua_register_ax_base_Scene(tolua_S); + lua_register_ax_base_GLView(tolua_S); + lua_register_ax_base_Director(tolua_S); lua_register_ax_base_Properties(tolua_S); + lua_register_ax_base_Timer(tolua_S); + lua_register_ax_base_Scheduler(tolua_S); lua_register_ax_base_UserDefault(tolua_S); lua_register_ax_base_FileUtils(tolua_S); lua_register_ax_base_EventAcceleration(tolua_S); diff --git a/extensions/scripting/lua-bindings/auto/axlua_fairygui_auto.cpp b/extensions/scripting/lua-bindings/auto/axlua_fairygui_auto.cpp index a6600ccfa24e..c5fc054bcab6 100644 --- a/extensions/scripting/lua-bindings/auto/axlua_fairygui_auto.cpp +++ b/extensions/scripting/lua-bindings/auto/axlua_fairygui_auto.cpp @@ -10668,8 +10668,8 @@ int lua_ax_fairygui_GGraph_drawRect(lua_State* tolua_S) double arg0; double arg1; int arg2; - ax::Color4B arg3; - ax::Color4B arg4; + ax::Color arg3; + ax::Color arg4; ok &= luaval_to_number(tolua_S, 2,&arg0, "fgui.GGraph:drawRect"); @@ -10677,9 +10677,9 @@ int lua_ax_fairygui_GGraph_drawRect(lua_State* tolua_S) ok &= luaval_to_int32(tolua_S, 4,(int *)&arg2, "fgui.GGraph:drawRect"); - ok &=luaval_to_color4b(tolua_S, 5, &arg3, "fgui.GGraph:drawRect"); + ok &=luaval_to_color(tolua_S, 5, &arg3, "fgui.GGraph:drawRect"); - ok &=luaval_to_color4b(tolua_S, 6, &arg4, "fgui.GGraph:drawRect"); + ok &=luaval_to_color(tolua_S, 6, &arg4, "fgui.GGraph:drawRect"); if(!ok) { tolua_error(tolua_S,"invalid arguments in function 'lua_ax_fairygui_GGraph_drawRect'", nullptr); @@ -10730,8 +10730,8 @@ int lua_ax_fairygui_GGraph_drawEllipse(lua_State* tolua_S) double arg0; double arg1; int arg2; - ax::Color4B arg3; - ax::Color4B arg4; + ax::Color arg3; + ax::Color arg4; ok &= luaval_to_number(tolua_S, 2,&arg0, "fgui.GGraph:drawEllipse"); @@ -10739,9 +10739,9 @@ int lua_ax_fairygui_GGraph_drawEllipse(lua_State* tolua_S) ok &= luaval_to_int32(tolua_S, 4,(int *)&arg2, "fgui.GGraph:drawEllipse"); - ok &=luaval_to_color4b(tolua_S, 5, &arg3, "fgui.GGraph:drawEllipse"); + ok &=luaval_to_color(tolua_S, 5, &arg3, "fgui.GGraph:drawEllipse"); - ok &=luaval_to_color4b(tolua_S, 6, &arg4, "fgui.GGraph:drawEllipse"); + ok &=luaval_to_color(tolua_S, 6, &arg4, "fgui.GGraph:drawEllipse"); if(!ok) { tolua_error(tolua_S,"invalid arguments in function 'lua_ax_fairygui_GGraph_drawEllipse'", nullptr); @@ -10790,16 +10790,16 @@ int lua_ax_fairygui_GGraph_drawPolygon(lua_State* tolua_S) if (argc == 5) { int arg0; - ax::Color4B arg1; - ax::Color4B arg2; + ax::Color arg1; + ax::Color arg2; const ax::Vec2* arg3; int arg4; ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "fgui.GGraph:drawPolygon"); - ok &=luaval_to_color4b(tolua_S, 3, &arg1, "fgui.GGraph:drawPolygon"); + ok &=luaval_to_color(tolua_S, 3, &arg1, "fgui.GGraph:drawPolygon"); - ok &=luaval_to_color4b(tolua_S, 4, &arg2, "fgui.GGraph:drawPolygon"); + ok &=luaval_to_color(tolua_S, 4, &arg2, "fgui.GGraph:drawPolygon"); ok &= luaval_to_object(tolua_S, 5, "ax.Vec2",&arg3, "fgui.GGraph:drawPolygon"); @@ -10852,15 +10852,15 @@ int lua_ax_fairygui_GGraph_drawRegularPolygon(lua_State* tolua_S) if (argc == 4) { int arg0; - ax::Color4B arg1; - ax::Color4B arg2; + ax::Color arg1; + ax::Color arg2; int arg3; ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "fgui.GGraph:drawRegularPolygon"); - ok &=luaval_to_color4b(tolua_S, 3, &arg1, "fgui.GGraph:drawRegularPolygon"); + ok &=luaval_to_color(tolua_S, 3, &arg1, "fgui.GGraph:drawRegularPolygon"); - ok &=luaval_to_color4b(tolua_S, 4, &arg2, "fgui.GGraph:drawRegularPolygon"); + ok &=luaval_to_color(tolua_S, 4, &arg2, "fgui.GGraph:drawRegularPolygon"); ok &= luaval_to_int32(tolua_S, 5,(int *)&arg3, "fgui.GGraph:drawRegularPolygon"); if(!ok) @@ -10875,16 +10875,16 @@ int lua_ax_fairygui_GGraph_drawRegularPolygon(lua_State* tolua_S) if (argc == 5) { int arg0; - ax::Color4B arg1; - ax::Color4B arg2; + ax::Color arg1; + ax::Color arg2; int arg3; double arg4; ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "fgui.GGraph:drawRegularPolygon"); - ok &=luaval_to_color4b(tolua_S, 3, &arg1, "fgui.GGraph:drawRegularPolygon"); + ok &=luaval_to_color(tolua_S, 3, &arg1, "fgui.GGraph:drawRegularPolygon"); - ok &=luaval_to_color4b(tolua_S, 4, &arg2, "fgui.GGraph:drawRegularPolygon"); + ok &=luaval_to_color(tolua_S, 4, &arg2, "fgui.GGraph:drawRegularPolygon"); ok &= luaval_to_int32(tolua_S, 5,(int *)&arg3, "fgui.GGraph:drawRegularPolygon"); @@ -10901,17 +10901,17 @@ int lua_ax_fairygui_GGraph_drawRegularPolygon(lua_State* tolua_S) if (argc == 6) { int arg0; - ax::Color4B arg1; - ax::Color4B arg2; + ax::Color arg1; + ax::Color arg2; int arg3; double arg4; const float* arg5; ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "fgui.GGraph:drawRegularPolygon"); - ok &=luaval_to_color4b(tolua_S, 3, &arg1, "fgui.GGraph:drawRegularPolygon"); + ok &=luaval_to_color(tolua_S, 3, &arg1, "fgui.GGraph:drawRegularPolygon"); - ok &=luaval_to_color4b(tolua_S, 4, &arg2, "fgui.GGraph:drawRegularPolygon"); + ok &=luaval_to_color(tolua_S, 4, &arg2, "fgui.GGraph:drawRegularPolygon"); ok &= luaval_to_int32(tolua_S, 5,(int *)&arg3, "fgui.GGraph:drawRegularPolygon"); @@ -10931,8 +10931,8 @@ int lua_ax_fairygui_GGraph_drawRegularPolygon(lua_State* tolua_S) if (argc == 7) { int arg0; - ax::Color4B arg1; - ax::Color4B arg2; + ax::Color arg1; + ax::Color arg2; int arg3; double arg4; const float* arg5; @@ -10940,9 +10940,9 @@ int lua_ax_fairygui_GGraph_drawRegularPolygon(lua_State* tolua_S) ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "fgui.GGraph:drawRegularPolygon"); - ok &=luaval_to_color4b(tolua_S, 3, &arg1, "fgui.GGraph:drawRegularPolygon"); + ok &=luaval_to_color(tolua_S, 3, &arg1, "fgui.GGraph:drawRegularPolygon"); - ok &=luaval_to_color4b(tolua_S, 4, &arg2, "fgui.GGraph:drawRegularPolygon"); + ok &=luaval_to_color(tolua_S, 4, &arg2, "fgui.GGraph:drawRegularPolygon"); ok &= luaval_to_int32(tolua_S, 5,(int *)&arg3, "fgui.GGraph:drawRegularPolygon"); @@ -32463,7 +32463,7 @@ int lua_ax_fairygui_GTween_toVec4(lua_State* tolua_S) #endif return 0; } -int lua_ax_fairygui_GTween_toColor4B(lua_State* tolua_S) +int lua_ax_fairygui_GTween_toColor32(lua_State* tolua_S) { int argc = 0; bool ok = true; @@ -32480,26 +32480,26 @@ int lua_ax_fairygui_GTween_toColor4B(lua_State* tolua_S) if (argc == 3) { - ax::Color4B arg0; - ax::Color4B arg1; + ax::Color32 arg0; + ax::Color32 arg1; double arg2; - ok &=luaval_to_color4b(tolua_S, 2, &arg0, "fgui.GTween:toColor4B"); - ok &=luaval_to_color4b(tolua_S, 3, &arg1, "fgui.GTween:toColor4B"); - ok &= luaval_to_number(tolua_S, 4,&arg2, "fgui.GTween:toColor4B"); + ok &=luaval_to_color32(tolua_S, 2, &arg0, "fgui.GTween:toColor32"); + ok &=luaval_to_color32(tolua_S, 3, &arg1, "fgui.GTween:toColor32"); + ok &= luaval_to_number(tolua_S, 4,&arg2, "fgui.GTween:toColor32"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_fairygui_GTween_toColor4B'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_fairygui_GTween_toColor32'", nullptr); return 0; } - auto&& ret = fairygui::GTween::toColor4B(arg0, arg1, arg2); + auto&& ret = fairygui::GTween::toColor32(arg0, arg1, arg2); object_to_luaval(tolua_S, "fgui.GTweener",(fairygui::GTweener*)ret); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "fgui.GTween:toColor4B",argc, 3); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "fgui.GTween:toColor32",argc, 3); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_fairygui_GTween_toColor4B'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_fairygui_GTween_toColor32'.",&tolua_err); #endif return 0; } @@ -32839,7 +32839,7 @@ int lua_register_ax_fairygui_GTween(lua_State* tolua_S) tolua_function(tolua_S,"toVec2", lua_ax_fairygui_GTween_toVec2); tolua_function(tolua_S,"toVec3", lua_ax_fairygui_GTween_toVec3); tolua_function(tolua_S,"toVec4", lua_ax_fairygui_GTween_toVec4); - tolua_function(tolua_S,"toColor4B", lua_ax_fairygui_GTween_toColor4B); + tolua_function(tolua_S,"toColor32", lua_ax_fairygui_GTween_toColor32); tolua_function(tolua_S,"toDouble", lua_ax_fairygui_GTween_toDouble); tolua_function(tolua_S,"delayedCall", lua_ax_fairygui_GTween_delayedCall); tolua_function(tolua_S,"shake", lua_ax_fairygui_GTween_shake); diff --git a/extensions/scripting/lua-bindings/auto/axlua_physics3d_auto.cpp b/extensions/scripting/lua-bindings/auto/axlua_physics3d_auto.cpp index 16e671e4059f..3d57dde04cfa 100644 --- a/extensions/scripting/lua-bindings/auto/axlua_physics3d_auto.cpp +++ b/extensions/scripting/lua-bindings/auto/axlua_physics3d_auto.cpp @@ -1,5 +1,5 @@ #include "lua-bindings/auto/axlua_physics3d_auto.hpp" -#if defined(AX_ENABLE_3D_PHYSICS) && AX_ENABLE_BULLET_INTEGRATION +#if defined(AX_ENABLE_3D_PHYSICS) #include "physics3d/Physics3D.h" #include "lua-bindings/manual/tolua_fix.h" #include "lua-bindings/manual/LuaBasicConversions.h" diff --git a/extensions/scripting/lua-bindings/auto/axlua_physics3d_auto.hpp b/extensions/scripting/lua-bindings/auto/axlua_physics3d_auto.hpp index 972467ea855a..39abbc8b3c54 100644 --- a/extensions/scripting/lua-bindings/auto/axlua_physics3d_auto.hpp +++ b/extensions/scripting/lua-bindings/auto/axlua_physics3d_auto.hpp @@ -1,8 +1,8 @@ #include "base/Config.h" -#if defined(AX_ENABLE_3D_PHYSICS) && AX_ENABLE_BULLET_INTEGRATION +#if defined(AX_ENABLE_3D_PHYSICS) #ifndef __ax_physics3d_h__ #define __ax_physics3d_h__ #include "tolua++.h" int register_all_ax_physics3d(lua_State* tolua_S); #endif // __ax_physics3d_h__ -#endif //#if defined(AX_ENABLE_3D_PHYSICS) && AX_ENABLE_BULLET_INTEGRATION +#endif //#if defined(AX_ENABLE_3D_PHYSICS) diff --git a/extensions/scripting/lua-bindings/auto/axlua_physics_auto.cpp b/extensions/scripting/lua-bindings/auto/axlua_physics_auto.cpp index 0b16a8891731..8d48327059b3 100644 --- a/extensions/scripting/lua-bindings/auto/axlua_physics_auto.cpp +++ b/extensions/scripting/lua-bindings/auto/axlua_physics_auto.cpp @@ -3,12892 +3,6 @@ #include "axmol.h" #include "lua-bindings/manual/tolua_fix.h" #include "lua-bindings/manual/LuaBasicConversions.h" - -int lua_ax_physics_PhysicsShape_getBody(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShape* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShape",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShape*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShape_getBody'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShape_getBody'", nullptr); - return 0; - } - auto&& ret = cobj->getBody(); - object_to_luaval(tolua_S, "ax.PhysicsBody",(ax::PhysicsBody*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShape:getBody",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShape_getBody'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsShape_getType(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShape* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShape",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShape*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShape_getType'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShape_getType'", nullptr); - return 0; - } - int ret = (int)cobj->getType(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShape:getType",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShape_getType'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsShape_getArea(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShape* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShape",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShape*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShape_getArea'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShape_getArea'", nullptr); - return 0; - } - auto&& ret = cobj->getArea(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShape:getArea",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShape_getArea'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsShape_getMoment(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShape* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShape",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShape*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShape_getMoment'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShape_getMoment'", nullptr); - return 0; - } - auto&& ret = cobj->getMoment(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShape:getMoment",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShape_getMoment'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsShape_setMoment(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShape* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShape",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShape*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShape_setMoment'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsShape:setMoment"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShape_setMoment'", nullptr); - return 0; - } - cobj->setMoment(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShape:setMoment",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShape_setMoment'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsShape_setTag(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShape* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShape",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShape*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShape_setTag'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - int arg0; - - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.PhysicsShape:setTag"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShape_setTag'", nullptr); - return 0; - } - cobj->setTag(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShape:setTag",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShape_setTag'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsShape_getTag(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShape* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShape",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShape*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShape_getTag'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShape_getTag'", nullptr); - return 0; - } - auto&& ret = cobj->getTag(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShape:getTag",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShape_getTag'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsShape_getMass(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShape* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShape",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShape*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShape_getMass'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShape_getMass'", nullptr); - return 0; - } - auto&& ret = cobj->getMass(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShape:getMass",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShape_getMass'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsShape_setMass(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShape* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShape",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShape*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShape_setMass'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsShape:setMass"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShape_setMass'", nullptr); - return 0; - } - cobj->setMass(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShape:setMass",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShape_setMass'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsShape_getDensity(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShape* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShape",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShape*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShape_getDensity'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShape_getDensity'", nullptr); - return 0; - } - auto&& ret = cobj->getDensity(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShape:getDensity",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShape_getDensity'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsShape_setDensity(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShape* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShape",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShape*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShape_setDensity'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsShape:setDensity"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShape_setDensity'", nullptr); - return 0; - } - cobj->setDensity(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShape:setDensity",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShape_setDensity'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsShape_getRestitution(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShape* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShape",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShape*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShape_getRestitution'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShape_getRestitution'", nullptr); - return 0; - } - auto&& ret = cobj->getRestitution(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShape:getRestitution",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShape_getRestitution'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsShape_setRestitution(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShape* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShape",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShape*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShape_setRestitution'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsShape:setRestitution"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShape_setRestitution'", nullptr); - return 0; - } - cobj->setRestitution(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShape:setRestitution",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShape_setRestitution'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsShape_getFriction(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShape* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShape",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShape*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShape_getFriction'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShape_getFriction'", nullptr); - return 0; - } - auto&& ret = cobj->getFriction(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShape:getFriction",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShape_getFriction'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsShape_setFriction(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShape* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShape",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShape*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShape_setFriction'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsShape:setFriction"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShape_setFriction'", nullptr); - return 0; - } - cobj->setFriction(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShape:setFriction",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShape_setFriction'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsShape_getMaterial(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShape* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShape",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShape*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShape_getMaterial'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShape_getMaterial'", nullptr); - return 0; - } - auto&& ret = cobj->getMaterial(); - physics_material_to_luaval(tolua_S, ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShape:getMaterial",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShape_getMaterial'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsShape_setMaterial(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShape* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShape",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShape*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShape_setMaterial'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - ax::PhysicsMaterial arg0; - - ok &= luaval_to_physics_material(tolua_S, 2, &arg0, "ax.PhysicsShape:setMaterial"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShape_setMaterial'", nullptr); - return 0; - } - cobj->setMaterial(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShape:setMaterial",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShape_setMaterial'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsShape_isSensor(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShape* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShape",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShape*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShape_isSensor'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShape_isSensor'", nullptr); - return 0; - } - auto&& ret = cobj->isSensor(); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShape:isSensor",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShape_isSensor'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsShape_setSensor(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShape* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShape",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShape*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShape_setSensor'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - bool arg0; - - ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.PhysicsShape:setSensor"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShape_setSensor'", nullptr); - return 0; - } - cobj->setSensor(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShape:setSensor",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShape_setSensor'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsShape_calculateDefaultMoment(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShape* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShape",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShape*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShape_calculateDefaultMoment'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShape_calculateDefaultMoment'", nullptr); - return 0; - } - auto&& ret = cobj->calculateDefaultMoment(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShape:calculateDefaultMoment",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShape_calculateDefaultMoment'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsShape_getOffset(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShape* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShape",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShape*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShape_getOffset'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShape_getOffset'", nullptr); - return 0; - } - auto&& ret = cobj->getOffset(); - vec2_to_luaval(tolua_S, ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShape:getOffset",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShape_getOffset'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsShape_getCenter(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShape* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShape",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShape*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShape_getCenter'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShape_getCenter'", nullptr); - return 0; - } - auto&& ret = cobj->getCenter(); - vec2_to_luaval(tolua_S, ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShape:getCenter",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShape_getCenter'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsShape_containsPoint(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShape* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShape",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShape*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShape_containsPoint'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - ax::Vec2 arg0; - - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsShape:containsPoint"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShape_containsPoint'", nullptr); - return 0; - } - auto&& ret = cobj->containsPoint(arg0); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShape:containsPoint",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShape_containsPoint'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsShape_setCategoryBitmask(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShape* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShape",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShape*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShape_setCategoryBitmask'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - int arg0; - - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.PhysicsShape:setCategoryBitmask"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShape_setCategoryBitmask'", nullptr); - return 0; - } - cobj->setCategoryBitmask(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShape:setCategoryBitmask",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShape_setCategoryBitmask'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsShape_getCategoryBitmask(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShape* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShape",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShape*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShape_getCategoryBitmask'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShape_getCategoryBitmask'", nullptr); - return 0; - } - auto&& ret = cobj->getCategoryBitmask(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShape:getCategoryBitmask",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShape_getCategoryBitmask'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsShape_setContactTestBitmask(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShape* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShape",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShape*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShape_setContactTestBitmask'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - int arg0; - - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.PhysicsShape:setContactTestBitmask"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShape_setContactTestBitmask'", nullptr); - return 0; - } - cobj->setContactTestBitmask(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShape:setContactTestBitmask",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShape_setContactTestBitmask'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsShape_getContactTestBitmask(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShape* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShape",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShape*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShape_getContactTestBitmask'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShape_getContactTestBitmask'", nullptr); - return 0; - } - auto&& ret = cobj->getContactTestBitmask(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShape:getContactTestBitmask",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShape_getContactTestBitmask'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsShape_setCollisionBitmask(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShape* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShape",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShape*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShape_setCollisionBitmask'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - int arg0; - - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.PhysicsShape:setCollisionBitmask"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShape_setCollisionBitmask'", nullptr); - return 0; - } - cobj->setCollisionBitmask(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShape:setCollisionBitmask",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShape_setCollisionBitmask'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsShape_getCollisionBitmask(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShape* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShape",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShape*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShape_getCollisionBitmask'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShape_getCollisionBitmask'", nullptr); - return 0; - } - auto&& ret = cobj->getCollisionBitmask(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShape:getCollisionBitmask",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShape_getCollisionBitmask'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsShape_setGroup(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShape* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShape",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShape*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShape_setGroup'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - int arg0; - - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.PhysicsShape:setGroup"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShape_setGroup'", nullptr); - return 0; - } - cobj->setGroup(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShape:setGroup",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShape_setGroup'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsShape_getGroup(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShape* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShape",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShape*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShape_getGroup'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShape_getGroup'", nullptr); - return 0; - } - auto&& ret = cobj->getGroup(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShape:getGroup",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShape_getGroup'.",&tolua_err); -#endif - - return 0; -} -static int lua_ax_physics_PhysicsShape_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (PhysicsShape)"); - return 0; -} - -int lua_register_ax_physics_PhysicsShape(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.PhysicsShape"); - tolua_cclass(tolua_S,"PhysicsShape","ax.PhysicsShape","ax.Object",nullptr); - - tolua_beginmodule(tolua_S,"PhysicsShape"); - tolua_function(tolua_S,"getBody",lua_ax_physics_PhysicsShape_getBody); - tolua_function(tolua_S,"getType",lua_ax_physics_PhysicsShape_getType); - tolua_function(tolua_S,"getArea",lua_ax_physics_PhysicsShape_getArea); - tolua_function(tolua_S,"getMoment",lua_ax_physics_PhysicsShape_getMoment); - tolua_function(tolua_S,"setMoment",lua_ax_physics_PhysicsShape_setMoment); - tolua_function(tolua_S,"setTag",lua_ax_physics_PhysicsShape_setTag); - tolua_function(tolua_S,"getTag",lua_ax_physics_PhysicsShape_getTag); - tolua_function(tolua_S,"getMass",lua_ax_physics_PhysicsShape_getMass); - tolua_function(tolua_S,"setMass",lua_ax_physics_PhysicsShape_setMass); - tolua_function(tolua_S,"getDensity",lua_ax_physics_PhysicsShape_getDensity); - tolua_function(tolua_S,"setDensity",lua_ax_physics_PhysicsShape_setDensity); - tolua_function(tolua_S,"getRestitution",lua_ax_physics_PhysicsShape_getRestitution); - tolua_function(tolua_S,"setRestitution",lua_ax_physics_PhysicsShape_setRestitution); - tolua_function(tolua_S,"getFriction",lua_ax_physics_PhysicsShape_getFriction); - tolua_function(tolua_S,"setFriction",lua_ax_physics_PhysicsShape_setFriction); - tolua_function(tolua_S,"getMaterial",lua_ax_physics_PhysicsShape_getMaterial); - tolua_function(tolua_S,"setMaterial",lua_ax_physics_PhysicsShape_setMaterial); - tolua_function(tolua_S,"isSensor",lua_ax_physics_PhysicsShape_isSensor); - tolua_function(tolua_S,"setSensor",lua_ax_physics_PhysicsShape_setSensor); - tolua_function(tolua_S,"calculateDefaultMoment",lua_ax_physics_PhysicsShape_calculateDefaultMoment); - tolua_function(tolua_S,"getOffset",lua_ax_physics_PhysicsShape_getOffset); - tolua_function(tolua_S,"getCenter",lua_ax_physics_PhysicsShape_getCenter); - tolua_function(tolua_S,"containsPoint",lua_ax_physics_PhysicsShape_containsPoint); - tolua_function(tolua_S,"setCategoryBitmask",lua_ax_physics_PhysicsShape_setCategoryBitmask); - tolua_function(tolua_S,"getCategoryBitmask",lua_ax_physics_PhysicsShape_getCategoryBitmask); - tolua_function(tolua_S,"setContactTestBitmask",lua_ax_physics_PhysicsShape_setContactTestBitmask); - tolua_function(tolua_S,"getContactTestBitmask",lua_ax_physics_PhysicsShape_getContactTestBitmask); - tolua_function(tolua_S,"setCollisionBitmask",lua_ax_physics_PhysicsShape_setCollisionBitmask); - tolua_function(tolua_S,"getCollisionBitmask",lua_ax_physics_PhysicsShape_getCollisionBitmask); - tolua_function(tolua_S,"setGroup",lua_ax_physics_PhysicsShape_setGroup); - tolua_function(tolua_S,"getGroup",lua_ax_physics_PhysicsShape_getGroup); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::PhysicsShape).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.PhysicsShape"; - g_typeCast[typeName] = "ax.PhysicsShape"; - return 1; -} - -int lua_ax_physics_PhysicsShapeCircle_getRadius(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShapeCircle* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShapeCircle",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShapeCircle*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShapeCircle_getRadius'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShapeCircle_getRadius'", nullptr); - return 0; - } - auto&& ret = cobj->getRadius(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShapeCircle:getRadius",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShapeCircle_getRadius'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsShapeCircle_create(lua_State* tolua_S) -{ - int argc = 0; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.PhysicsShapeCircle",0,&tolua_err)) goto tolua_lerror; -#endif - - argc = lua_gettop(tolua_S) - 1; - - if (argc == 1) - { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsShapeCircle:create"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShapeCircle_create'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsShapeCircle::create(arg0); - object_to_luaval(tolua_S, "ax.PhysicsShapeCircle",(ax::PhysicsShapeCircle*)ret); - return 1; - } - if (argc == 2) - { - double arg0; - ax::PhysicsMaterial arg1; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsShapeCircle:create"); - ok &= luaval_to_physics_material(tolua_S, 3, &arg1, "ax.PhysicsShapeCircle:create"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShapeCircle_create'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsShapeCircle::create(arg0, arg1); - object_to_luaval(tolua_S, "ax.PhysicsShapeCircle",(ax::PhysicsShapeCircle*)ret); - return 1; - } - if (argc == 3) - { - double arg0; - ax::PhysicsMaterial arg1; - ax::Vec2 arg2; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsShapeCircle:create"); - ok &= luaval_to_physics_material(tolua_S, 3, &arg1, "ax.PhysicsShapeCircle:create"); - ok &= luaval_to_vec2(tolua_S, 4, &arg2, "ax.PhysicsShapeCircle:create"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShapeCircle_create'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsShapeCircle::create(arg0, arg1, arg2); - object_to_luaval(tolua_S, "ax.PhysicsShapeCircle",(ax::PhysicsShapeCircle*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.PhysicsShapeCircle:create",argc, 1); - return 0; -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShapeCircle_create'.",&tolua_err); -#endif - return 0; -} -int lua_ax_physics_PhysicsShapeCircle_calculateArea(lua_State* tolua_S) -{ - int argc = 0; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.PhysicsShapeCircle",0,&tolua_err)) goto tolua_lerror; -#endif - - argc = lua_gettop(tolua_S) - 1; - - if (argc == 1) - { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsShapeCircle:calculateArea"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShapeCircle_calculateArea'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsShapeCircle::calculateArea(arg0); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.PhysicsShapeCircle:calculateArea",argc, 1); - return 0; -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShapeCircle_calculateArea'.",&tolua_err); -#endif - return 0; -} -int lua_ax_physics_PhysicsShapeCircle_calculateMoment(lua_State* tolua_S) -{ - int argc = 0; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.PhysicsShapeCircle",0,&tolua_err)) goto tolua_lerror; -#endif - - argc = lua_gettop(tolua_S) - 1; - - if (argc == 2) - { - double arg0; - double arg1; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsShapeCircle:calculateMoment"); - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.PhysicsShapeCircle:calculateMoment"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShapeCircle_calculateMoment'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsShapeCircle::calculateMoment(arg0, arg1); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - if (argc == 3) - { - double arg0; - double arg1; - ax::Vec2 arg2; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsShapeCircle:calculateMoment"); - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.PhysicsShapeCircle:calculateMoment"); - ok &= luaval_to_vec2(tolua_S, 4, &arg2, "ax.PhysicsShapeCircle:calculateMoment"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShapeCircle_calculateMoment'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsShapeCircle::calculateMoment(arg0, arg1, arg2); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.PhysicsShapeCircle:calculateMoment",argc, 2); - return 0; -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShapeCircle_calculateMoment'.",&tolua_err); -#endif - return 0; -} -static int lua_ax_physics_PhysicsShapeCircle_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (PhysicsShapeCircle)"); - return 0; -} - -int lua_register_ax_physics_PhysicsShapeCircle(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.PhysicsShapeCircle"); - tolua_cclass(tolua_S,"PhysicsShapeCircle","ax.PhysicsShapeCircle","ax.PhysicsShape",nullptr); - - tolua_beginmodule(tolua_S,"PhysicsShapeCircle"); - tolua_function(tolua_S,"getRadius",lua_ax_physics_PhysicsShapeCircle_getRadius); - tolua_function(tolua_S,"create", lua_ax_physics_PhysicsShapeCircle_create); - tolua_function(tolua_S,"calculateArea", lua_ax_physics_PhysicsShapeCircle_calculateArea); - tolua_function(tolua_S,"calculateMoment", lua_ax_physics_PhysicsShapeCircle_calculateMoment); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::PhysicsShapeCircle).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.PhysicsShapeCircle"; - g_typeCast[typeName] = "ax.PhysicsShapeCircle"; - return 1; -} - -int lua_ax_physics_PhysicsShapePolygon_getPoint(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShapePolygon* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShapePolygon",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShapePolygon*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShapePolygon_getPoint'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - int arg0; - - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.PhysicsShapePolygon:getPoint"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShapePolygon_getPoint'", nullptr); - return 0; - } - auto&& ret = cobj->getPoint(arg0); - vec2_to_luaval(tolua_S, ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShapePolygon:getPoint",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShapePolygon_getPoint'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsShapePolygon_getPointsCount(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShapePolygon* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShapePolygon",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShapePolygon*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShapePolygon_getPointsCount'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShapePolygon_getPointsCount'", nullptr); - return 0; - } - auto&& ret = cobj->getPointsCount(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShapePolygon:getPointsCount",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShapePolygon_getPointsCount'.",&tolua_err); -#endif - - return 0; -} -static int lua_ax_physics_PhysicsShapePolygon_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (PhysicsShapePolygon)"); - return 0; -} - -int lua_register_ax_physics_PhysicsShapePolygon(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.PhysicsShapePolygon"); - tolua_cclass(tolua_S,"PhysicsShapePolygon","ax.PhysicsShapePolygon","ax.PhysicsShape",nullptr); - - tolua_beginmodule(tolua_S,"PhysicsShapePolygon"); - tolua_function(tolua_S,"getPoint",lua_ax_physics_PhysicsShapePolygon_getPoint); - tolua_function(tolua_S,"getPointsCount",lua_ax_physics_PhysicsShapePolygon_getPointsCount); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::PhysicsShapePolygon).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.PhysicsShapePolygon"; - g_typeCast[typeName] = "ax.PhysicsShapePolygon"; - return 1; -} - -int lua_ax_physics_PhysicsShapeBox_getSize(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShapeBox* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShapeBox",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShapeBox*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShapeBox_getSize'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShapeBox_getSize'", nullptr); - return 0; - } - auto&& ret = cobj->getSize(); - vec2_to_luaval(tolua_S, ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShapeBox:getSize",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShapeBox_getSize'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsShapeBox_create(lua_State* tolua_S) -{ - int argc = 0; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.PhysicsShapeBox",0,&tolua_err)) goto tolua_lerror; -#endif - - argc = lua_gettop(tolua_S) - 1; - - if (argc == 1) - { - ax::Vec2 arg0; - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsShapeBox:create"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShapeBox_create'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsShapeBox::create(arg0); - object_to_luaval(tolua_S, "ax.PhysicsShapeBox",(ax::PhysicsShapeBox*)ret); - return 1; - } - if (argc == 2) - { - ax::Vec2 arg0; - ax::PhysicsMaterial arg1; - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsShapeBox:create"); - ok &= luaval_to_physics_material(tolua_S, 3, &arg1, "ax.PhysicsShapeBox:create"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShapeBox_create'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsShapeBox::create(arg0, arg1); - object_to_luaval(tolua_S, "ax.PhysicsShapeBox",(ax::PhysicsShapeBox*)ret); - return 1; - } - if (argc == 3) - { - ax::Vec2 arg0; - ax::PhysicsMaterial arg1; - ax::Vec2 arg2; - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsShapeBox:create"); - ok &= luaval_to_physics_material(tolua_S, 3, &arg1, "ax.PhysicsShapeBox:create"); - ok &= luaval_to_vec2(tolua_S, 4, &arg2, "ax.PhysicsShapeBox:create"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShapeBox_create'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsShapeBox::create(arg0, arg1, arg2); - object_to_luaval(tolua_S, "ax.PhysicsShapeBox",(ax::PhysicsShapeBox*)ret); - return 1; - } - if (argc == 4) - { - ax::Vec2 arg0; - ax::PhysicsMaterial arg1; - ax::Vec2 arg2; - double arg3; - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsShapeBox:create"); - ok &= luaval_to_physics_material(tolua_S, 3, &arg1, "ax.PhysicsShapeBox:create"); - ok &= luaval_to_vec2(tolua_S, 4, &arg2, "ax.PhysicsShapeBox:create"); - ok &= luaval_to_number(tolua_S, 5,&arg3, "ax.PhysicsShapeBox:create"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShapeBox_create'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsShapeBox::create(arg0, arg1, arg2, arg3); - object_to_luaval(tolua_S, "ax.PhysicsShapeBox",(ax::PhysicsShapeBox*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.PhysicsShapeBox:create",argc, 1); - return 0; -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShapeBox_create'.",&tolua_err); -#endif - return 0; -} -static int lua_ax_physics_PhysicsShapeBox_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (PhysicsShapeBox)"); - return 0; -} - -int lua_register_ax_physics_PhysicsShapeBox(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.PhysicsShapeBox"); - tolua_cclass(tolua_S,"PhysicsShapeBox","ax.PhysicsShapeBox","ax.PhysicsShapePolygon",nullptr); - - tolua_beginmodule(tolua_S,"PhysicsShapeBox"); - tolua_function(tolua_S,"getSize",lua_ax_physics_PhysicsShapeBox_getSize); - tolua_function(tolua_S,"create", lua_ax_physics_PhysicsShapeBox_create); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::PhysicsShapeBox).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.PhysicsShapeBox"; - g_typeCast[typeName] = "ax.PhysicsShapeBox"; - return 1; -} - -int lua_ax_physics_PhysicsShapeEdgeSegment_getPointA(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShapeEdgeSegment* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShapeEdgeSegment",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShapeEdgeSegment*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShapeEdgeSegment_getPointA'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShapeEdgeSegment_getPointA'", nullptr); - return 0; - } - auto&& ret = cobj->getPointA(); - vec2_to_luaval(tolua_S, ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShapeEdgeSegment:getPointA",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShapeEdgeSegment_getPointA'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsShapeEdgeSegment_getPointB(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShapeEdgeSegment* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShapeEdgeSegment",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShapeEdgeSegment*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShapeEdgeSegment_getPointB'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShapeEdgeSegment_getPointB'", nullptr); - return 0; - } - auto&& ret = cobj->getPointB(); - vec2_to_luaval(tolua_S, ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShapeEdgeSegment:getPointB",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShapeEdgeSegment_getPointB'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsShapeEdgeSegment_create(lua_State* tolua_S) -{ - int argc = 0; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.PhysicsShapeEdgeSegment",0,&tolua_err)) goto tolua_lerror; -#endif - - argc = lua_gettop(tolua_S) - 1; - - if (argc == 2) - { - ax::Vec2 arg0; - ax::Vec2 arg1; - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsShapeEdgeSegment:create"); - ok &= luaval_to_vec2(tolua_S, 3, &arg1, "ax.PhysicsShapeEdgeSegment:create"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShapeEdgeSegment_create'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsShapeEdgeSegment::create(arg0, arg1); - object_to_luaval(tolua_S, "ax.PhysicsShapeEdgeSegment",(ax::PhysicsShapeEdgeSegment*)ret); - return 1; - } - if (argc == 3) - { - ax::Vec2 arg0; - ax::Vec2 arg1; - ax::PhysicsMaterial arg2; - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsShapeEdgeSegment:create"); - ok &= luaval_to_vec2(tolua_S, 3, &arg1, "ax.PhysicsShapeEdgeSegment:create"); - ok &= luaval_to_physics_material(tolua_S, 4, &arg2, "ax.PhysicsShapeEdgeSegment:create"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShapeEdgeSegment_create'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsShapeEdgeSegment::create(arg0, arg1, arg2); - object_to_luaval(tolua_S, "ax.PhysicsShapeEdgeSegment",(ax::PhysicsShapeEdgeSegment*)ret); - return 1; - } - if (argc == 4) - { - ax::Vec2 arg0; - ax::Vec2 arg1; - ax::PhysicsMaterial arg2; - double arg3; - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsShapeEdgeSegment:create"); - ok &= luaval_to_vec2(tolua_S, 3, &arg1, "ax.PhysicsShapeEdgeSegment:create"); - ok &= luaval_to_physics_material(tolua_S, 4, &arg2, "ax.PhysicsShapeEdgeSegment:create"); - ok &= luaval_to_number(tolua_S, 5,&arg3, "ax.PhysicsShapeEdgeSegment:create"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShapeEdgeSegment_create'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsShapeEdgeSegment::create(arg0, arg1, arg2, arg3); - object_to_luaval(tolua_S, "ax.PhysicsShapeEdgeSegment",(ax::PhysicsShapeEdgeSegment*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.PhysicsShapeEdgeSegment:create",argc, 2); - return 0; -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShapeEdgeSegment_create'.",&tolua_err); -#endif - return 0; -} -static int lua_ax_physics_PhysicsShapeEdgeSegment_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (PhysicsShapeEdgeSegment)"); - return 0; -} - -int lua_register_ax_physics_PhysicsShapeEdgeSegment(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.PhysicsShapeEdgeSegment"); - tolua_cclass(tolua_S,"PhysicsShapeEdgeSegment","ax.PhysicsShapeEdgeSegment","ax.PhysicsShape",nullptr); - - tolua_beginmodule(tolua_S,"PhysicsShapeEdgeSegment"); - tolua_function(tolua_S,"getPointA",lua_ax_physics_PhysicsShapeEdgeSegment_getPointA); - tolua_function(tolua_S,"getPointB",lua_ax_physics_PhysicsShapeEdgeSegment_getPointB); - tolua_function(tolua_S,"create", lua_ax_physics_PhysicsShapeEdgeSegment_create); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::PhysicsShapeEdgeSegment).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.PhysicsShapeEdgeSegment"; - g_typeCast[typeName] = "ax.PhysicsShapeEdgeSegment"; - return 1; -} - -int lua_ax_physics_PhysicsShapeEdgePolygon_getPointsCount(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShapeEdgePolygon* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShapeEdgePolygon",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShapeEdgePolygon*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShapeEdgePolygon_getPointsCount'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShapeEdgePolygon_getPointsCount'", nullptr); - return 0; - } - auto&& ret = cobj->getPointsCount(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShapeEdgePolygon:getPointsCount",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShapeEdgePolygon_getPointsCount'.",&tolua_err); -#endif - - return 0; -} -static int lua_ax_physics_PhysicsShapeEdgePolygon_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (PhysicsShapeEdgePolygon)"); - return 0; -} - -int lua_register_ax_physics_PhysicsShapeEdgePolygon(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.PhysicsShapeEdgePolygon"); - tolua_cclass(tolua_S,"PhysicsShapeEdgePolygon","ax.PhysicsShapeEdgePolygon","ax.PhysicsShape",nullptr); - - tolua_beginmodule(tolua_S,"PhysicsShapeEdgePolygon"); - tolua_function(tolua_S,"getPointsCount",lua_ax_physics_PhysicsShapeEdgePolygon_getPointsCount); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::PhysicsShapeEdgePolygon).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.PhysicsShapeEdgePolygon"; - g_typeCast[typeName] = "ax.PhysicsShapeEdgePolygon"; - return 1; -} - -int lua_ax_physics_PhysicsShapeEdgeBox_create(lua_State* tolua_S) -{ - int argc = 0; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.PhysicsShapeEdgeBox",0,&tolua_err)) goto tolua_lerror; -#endif - - argc = lua_gettop(tolua_S) - 1; - - if (argc == 1) - { - ax::Vec2 arg0; - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsShapeEdgeBox:create"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShapeEdgeBox_create'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsShapeEdgeBox::create(arg0); - object_to_luaval(tolua_S, "ax.PhysicsShapeEdgeBox",(ax::PhysicsShapeEdgeBox*)ret); - return 1; - } - if (argc == 2) - { - ax::Vec2 arg0; - ax::PhysicsMaterial arg1; - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsShapeEdgeBox:create"); - ok &= luaval_to_physics_material(tolua_S, 3, &arg1, "ax.PhysicsShapeEdgeBox:create"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShapeEdgeBox_create'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsShapeEdgeBox::create(arg0, arg1); - object_to_luaval(tolua_S, "ax.PhysicsShapeEdgeBox",(ax::PhysicsShapeEdgeBox*)ret); - return 1; - } - if (argc == 3) - { - ax::Vec2 arg0; - ax::PhysicsMaterial arg1; - double arg2; - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsShapeEdgeBox:create"); - ok &= luaval_to_physics_material(tolua_S, 3, &arg1, "ax.PhysicsShapeEdgeBox:create"); - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.PhysicsShapeEdgeBox:create"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShapeEdgeBox_create'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsShapeEdgeBox::create(arg0, arg1, arg2); - object_to_luaval(tolua_S, "ax.PhysicsShapeEdgeBox",(ax::PhysicsShapeEdgeBox*)ret); - return 1; - } - if (argc == 4) - { - ax::Vec2 arg0; - ax::PhysicsMaterial arg1; - double arg2; - ax::Vec2 arg3; - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsShapeEdgeBox:create"); - ok &= luaval_to_physics_material(tolua_S, 3, &arg1, "ax.PhysicsShapeEdgeBox:create"); - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.PhysicsShapeEdgeBox:create"); - ok &= luaval_to_vec2(tolua_S, 5, &arg3, "ax.PhysicsShapeEdgeBox:create"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShapeEdgeBox_create'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsShapeEdgeBox::create(arg0, arg1, arg2, arg3); - object_to_luaval(tolua_S, "ax.PhysicsShapeEdgeBox",(ax::PhysicsShapeEdgeBox*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.PhysicsShapeEdgeBox:create",argc, 1); - return 0; -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShapeEdgeBox_create'.",&tolua_err); -#endif - return 0; -} -static int lua_ax_physics_PhysicsShapeEdgeBox_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (PhysicsShapeEdgeBox)"); - return 0; -} - -int lua_register_ax_physics_PhysicsShapeEdgeBox(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.PhysicsShapeEdgeBox"); - tolua_cclass(tolua_S,"PhysicsShapeEdgeBox","ax.PhysicsShapeEdgeBox","ax.PhysicsShapeEdgePolygon",nullptr); - - tolua_beginmodule(tolua_S,"PhysicsShapeEdgeBox"); - tolua_function(tolua_S,"create", lua_ax_physics_PhysicsShapeEdgeBox_create); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::PhysicsShapeEdgeBox).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.PhysicsShapeEdgeBox"; - g_typeCast[typeName] = "ax.PhysicsShapeEdgeBox"; - return 1; -} - -int lua_ax_physics_PhysicsShapeEdgeChain_getPointsCount(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsShapeEdgeChain* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsShapeEdgeChain",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsShapeEdgeChain*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsShapeEdgeChain_getPointsCount'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsShapeEdgeChain_getPointsCount'", nullptr); - return 0; - } - auto&& ret = cobj->getPointsCount(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsShapeEdgeChain:getPointsCount",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsShapeEdgeChain_getPointsCount'.",&tolua_err); -#endif - - return 0; -} -static int lua_ax_physics_PhysicsShapeEdgeChain_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (PhysicsShapeEdgeChain)"); - return 0; -} - -int lua_register_ax_physics_PhysicsShapeEdgeChain(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.PhysicsShapeEdgeChain"); - tolua_cclass(tolua_S,"PhysicsShapeEdgeChain","ax.PhysicsShapeEdgeChain","ax.PhysicsShape",nullptr); - - tolua_beginmodule(tolua_S,"PhysicsShapeEdgeChain"); - tolua_function(tolua_S,"getPointsCount",lua_ax_physics_PhysicsShapeEdgeChain_getPointsCount); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::PhysicsShapeEdgeChain).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.PhysicsShapeEdgeChain"; - g_typeCast[typeName] = "ax.PhysicsShapeEdgeChain"; - return 1; -} - -int lua_ax_physics_PhysicsBody_addShape(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_addShape'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - ax::PhysicsShape* arg0; - - ok &= luaval_to_object(tolua_S, 2, "ax.PhysicsShape",&arg0, "ax.PhysicsBody:addShape"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_addShape'", nullptr); - return 0; - } - auto&& ret = cobj->addShape(arg0); - object_to_luaval(tolua_S, "ax.PhysicsShape",(ax::PhysicsShape*)ret); - return 1; - } - if (argc == 2) - { - ax::PhysicsShape* arg0; - bool arg1; - - ok &= luaval_to_object(tolua_S, 2, "ax.PhysicsShape",&arg0, "ax.PhysicsBody:addShape"); - - ok &= luaval_to_boolean(tolua_S, 3,&arg1, "ax.PhysicsBody:addShape"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_addShape'", nullptr); - return 0; - } - auto&& ret = cobj->addShape(arg0, arg1); - object_to_luaval(tolua_S, "ax.PhysicsShape",(ax::PhysicsShape*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:addShape",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_addShape'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_removeShape(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_removeShape'", nullptr); - return 0; - } -#endif - argc = lua_gettop(tolua_S)-1; - do{ - if (argc == 1) { - int arg0; - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.PhysicsBody:removeShape"); - - if (!ok) { break; } - cobj->removeShape(arg0); - lua_settop(tolua_S, 1); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 2) { - int arg0; - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.PhysicsBody:removeShape"); - - if (!ok) { break; } - bool arg1; - ok &= luaval_to_boolean(tolua_S, 3,&arg1, "ax.PhysicsBody:removeShape"); - - if (!ok) { break; } - cobj->removeShape(arg0, arg1); - lua_settop(tolua_S, 1); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 1) { - ax::PhysicsShape* arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.PhysicsShape",&arg0, "ax.PhysicsBody:removeShape"); - - if (!ok) { break; } - cobj->removeShape(arg0); - lua_settop(tolua_S, 1); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 2) { - ax::PhysicsShape* arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.PhysicsShape",&arg0, "ax.PhysicsBody:removeShape"); - - if (!ok) { break; } - bool arg1; - ok &= luaval_to_boolean(tolua_S, 3,&arg1, "ax.PhysicsBody:removeShape"); - - if (!ok) { break; } - cobj->removeShape(arg0, arg1); - lua_settop(tolua_S, 1); - return 1; - } - }while(0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:removeShape",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_removeShape'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_removeAllShapes(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_removeAllShapes'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_removeAllShapes'", nullptr); - return 0; - } - cobj->removeAllShapes(); - lua_settop(tolua_S, 1); - return 1; - } - if (argc == 1) - { - bool arg0; - - ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.PhysicsBody:removeAllShapes"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_removeAllShapes'", nullptr); - return 0; - } - cobj->removeAllShapes(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:removeAllShapes",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_removeAllShapes'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_getShapes(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_getShapes'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_getShapes'", nullptr); - return 0; - } - auto&& ret = cobj->getShapes(); - ccvector_to_luaval(tolua_S, ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:getShapes",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_getShapes'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_getFirstShape(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_getFirstShape'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_getFirstShape'", nullptr); - return 0; - } - auto&& ret = cobj->getFirstShape(); - object_to_luaval(tolua_S, "ax.PhysicsShape",(ax::PhysicsShape*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:getFirstShape",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_getFirstShape'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_getShape(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_getShape'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - int arg0; - - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.PhysicsBody:getShape"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_getShape'", nullptr); - return 0; - } - auto&& ret = cobj->getShape(arg0); - object_to_luaval(tolua_S, "ax.PhysicsShape",(ax::PhysicsShape*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:getShape",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_getShape'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_applyForce(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_applyForce'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - ax::Vec2 arg0; - - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsBody:applyForce"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_applyForce'", nullptr); - return 0; - } - cobj->applyForce(arg0); - lua_settop(tolua_S, 1); - return 1; - } - if (argc == 2) - { - ax::Vec2 arg0; - ax::Vec2 arg1; - - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsBody:applyForce"); - - ok &= luaval_to_vec2(tolua_S, 3, &arg1, "ax.PhysicsBody:applyForce"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_applyForce'", nullptr); - return 0; - } - cobj->applyForce(arg0, arg1); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:applyForce",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_applyForce'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_resetForces(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_resetForces'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_resetForces'", nullptr); - return 0; - } - cobj->resetForces(); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:resetForces",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_resetForces'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_applyImpulse(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_applyImpulse'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - ax::Vec2 arg0; - - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsBody:applyImpulse"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_applyImpulse'", nullptr); - return 0; - } - cobj->applyImpulse(arg0); - lua_settop(tolua_S, 1); - return 1; - } - if (argc == 2) - { - ax::Vec2 arg0; - ax::Vec2 arg1; - - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsBody:applyImpulse"); - - ok &= luaval_to_vec2(tolua_S, 3, &arg1, "ax.PhysicsBody:applyImpulse"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_applyImpulse'", nullptr); - return 0; - } - cobj->applyImpulse(arg0, arg1); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:applyImpulse",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_applyImpulse'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_applyTorque(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_applyTorque'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsBody:applyTorque"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_applyTorque'", nullptr); - return 0; - } - cobj->applyTorque(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:applyTorque",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_applyTorque'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_setVelocity(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_setVelocity'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - ax::Vec2 arg0; - - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsBody:setVelocity"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_setVelocity'", nullptr); - return 0; - } - cobj->setVelocity(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:setVelocity",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_setVelocity'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_getVelocity(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_getVelocity'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_getVelocity'", nullptr); - return 0; - } - auto&& ret = cobj->getVelocity(); - vec2_to_luaval(tolua_S, ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:getVelocity",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_getVelocity'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_setAngularVelocity(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_setAngularVelocity'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsBody:setAngularVelocity"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_setAngularVelocity'", nullptr); - return 0; - } - cobj->setAngularVelocity(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:setAngularVelocity",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_setAngularVelocity'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_getVelocityAtLocalPoint(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_getVelocityAtLocalPoint'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - ax::Vec2 arg0; - - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsBody:getVelocityAtLocalPoint"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_getVelocityAtLocalPoint'", nullptr); - return 0; - } - auto&& ret = cobj->getVelocityAtLocalPoint(arg0); - vec2_to_luaval(tolua_S, ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:getVelocityAtLocalPoint",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_getVelocityAtLocalPoint'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_getVelocityAtWorldPoint(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_getVelocityAtWorldPoint'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - ax::Vec2 arg0; - - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsBody:getVelocityAtWorldPoint"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_getVelocityAtWorldPoint'", nullptr); - return 0; - } - auto&& ret = cobj->getVelocityAtWorldPoint(arg0); - vec2_to_luaval(tolua_S, ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:getVelocityAtWorldPoint",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_getVelocityAtWorldPoint'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_getAngularVelocity(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_getAngularVelocity'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_getAngularVelocity'", nullptr); - return 0; - } - auto&& ret = cobj->getAngularVelocity(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:getAngularVelocity",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_getAngularVelocity'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_setVelocityLimit(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_setVelocityLimit'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsBody:setVelocityLimit"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_setVelocityLimit'", nullptr); - return 0; - } - cobj->setVelocityLimit(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:setVelocityLimit",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_setVelocityLimit'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_getVelocityLimit(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_getVelocityLimit'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_getVelocityLimit'", nullptr); - return 0; - } - auto&& ret = cobj->getVelocityLimit(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:getVelocityLimit",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_getVelocityLimit'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_setAngularVelocityLimit(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_setAngularVelocityLimit'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsBody:setAngularVelocityLimit"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_setAngularVelocityLimit'", nullptr); - return 0; - } - cobj->setAngularVelocityLimit(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:setAngularVelocityLimit",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_setAngularVelocityLimit'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_getAngularVelocityLimit(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_getAngularVelocityLimit'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_getAngularVelocityLimit'", nullptr); - return 0; - } - auto&& ret = cobj->getAngularVelocityLimit(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:getAngularVelocityLimit",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_getAngularVelocityLimit'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_removeFromWorld(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_removeFromWorld'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_removeFromWorld'", nullptr); - return 0; - } - cobj->removeFromWorld(); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:removeFromWorld",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_removeFromWorld'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_getWorld(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_getWorld'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_getWorld'", nullptr); - return 0; - } - auto&& ret = cobj->getWorld(); - object_to_luaval(tolua_S, "ax.PhysicsWorld",(ax::PhysicsWorld*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:getWorld",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_getWorld'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_getNode(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_getNode'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_getNode'", nullptr); - return 0; - } - auto&& ret = cobj->getNode(); - object_to_luaval(tolua_S, "ax.Node",(ax::Node*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:getNode",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_getNode'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_setCategoryBitmask(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_setCategoryBitmask'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - int arg0; - - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.PhysicsBody:setCategoryBitmask"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_setCategoryBitmask'", nullptr); - return 0; - } - cobj->setCategoryBitmask(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:setCategoryBitmask",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_setCategoryBitmask'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_setContactTestBitmask(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_setContactTestBitmask'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - int arg0; - - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.PhysicsBody:setContactTestBitmask"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_setContactTestBitmask'", nullptr); - return 0; - } - cobj->setContactTestBitmask(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:setContactTestBitmask",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_setContactTestBitmask'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_setCollisionBitmask(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_setCollisionBitmask'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - int arg0; - - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.PhysicsBody:setCollisionBitmask"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_setCollisionBitmask'", nullptr); - return 0; - } - cobj->setCollisionBitmask(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:setCollisionBitmask",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_setCollisionBitmask'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_getCategoryBitmask(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_getCategoryBitmask'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_getCategoryBitmask'", nullptr); - return 0; - } - auto&& ret = cobj->getCategoryBitmask(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:getCategoryBitmask",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_getCategoryBitmask'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_getContactTestBitmask(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_getContactTestBitmask'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_getContactTestBitmask'", nullptr); - return 0; - } - auto&& ret = cobj->getContactTestBitmask(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:getContactTestBitmask",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_getContactTestBitmask'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_getCollisionBitmask(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_getCollisionBitmask'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_getCollisionBitmask'", nullptr); - return 0; - } - auto&& ret = cobj->getCollisionBitmask(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:getCollisionBitmask",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_getCollisionBitmask'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_setGroup(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_setGroup'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - int arg0; - - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.PhysicsBody:setGroup"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_setGroup'", nullptr); - return 0; - } - cobj->setGroup(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:setGroup",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_setGroup'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_getGroup(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_getGroup'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_getGroup'", nullptr); - return 0; - } - auto&& ret = cobj->getGroup(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:getGroup",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_getGroup'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_getPosition(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_getPosition'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_getPosition'", nullptr); - return 0; - } - auto&& ret = cobj->getPosition(); - vec2_to_luaval(tolua_S, ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:getPosition",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_getPosition'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_getRotation(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_getRotation'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_getRotation'", nullptr); - return 0; - } - auto&& ret = cobj->getRotation(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:getRotation",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_getRotation'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_setPositionOffset(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_setPositionOffset'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - ax::Vec2 arg0; - - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsBody:setPositionOffset"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_setPositionOffset'", nullptr); - return 0; - } - cobj->setPositionOffset(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:setPositionOffset",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_setPositionOffset'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_getPositionOffset(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_getPositionOffset'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_getPositionOffset'", nullptr); - return 0; - } - auto&& ret = cobj->getPositionOffset(); - vec2_to_luaval(tolua_S, ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:getPositionOffset",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_getPositionOffset'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_setRotationOffset(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_setRotationOffset'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsBody:setRotationOffset"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_setRotationOffset'", nullptr); - return 0; - } - cobj->setRotationOffset(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:setRotationOffset",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_setRotationOffset'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_getRotationOffset(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_getRotationOffset'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_getRotationOffset'", nullptr); - return 0; - } - auto&& ret = cobj->getRotationOffset(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:getRotationOffset",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_getRotationOffset'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_isDynamic(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_isDynamic'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_isDynamic'", nullptr); - return 0; - } - auto&& ret = cobj->isDynamic(); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:isDynamic",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_isDynamic'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_setDynamic(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_setDynamic'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - bool arg0; - - ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.PhysicsBody:setDynamic"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_setDynamic'", nullptr); - return 0; - } - cobj->setDynamic(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:setDynamic",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_setDynamic'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_setMass(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_setMass'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsBody:setMass"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_setMass'", nullptr); - return 0; - } - cobj->setMass(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:setMass",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_setMass'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_getMass(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_getMass'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_getMass'", nullptr); - return 0; - } - auto&& ret = cobj->getMass(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:getMass",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_getMass'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_addMass(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_addMass'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsBody:addMass"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_addMass'", nullptr); - return 0; - } - cobj->addMass(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:addMass",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_addMass'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_setMoment(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_setMoment'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsBody:setMoment"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_setMoment'", nullptr); - return 0; - } - cobj->setMoment(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:setMoment",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_setMoment'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_getMoment(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_getMoment'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_getMoment'", nullptr); - return 0; - } - auto&& ret = cobj->getMoment(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:getMoment",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_getMoment'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_addMoment(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_addMoment'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsBody:addMoment"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_addMoment'", nullptr); - return 0; - } - cobj->addMoment(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:addMoment",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_addMoment'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_getLinearDamping(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_getLinearDamping'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_getLinearDamping'", nullptr); - return 0; - } - auto&& ret = cobj->getLinearDamping(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:getLinearDamping",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_getLinearDamping'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_setLinearDamping(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_setLinearDamping'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsBody:setLinearDamping"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_setLinearDamping'", nullptr); - return 0; - } - cobj->setLinearDamping(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:setLinearDamping",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_setLinearDamping'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_getAngularDamping(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_getAngularDamping'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_getAngularDamping'", nullptr); - return 0; - } - auto&& ret = cobj->getAngularDamping(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:getAngularDamping",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_getAngularDamping'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_setAngularDamping(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_setAngularDamping'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsBody:setAngularDamping"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_setAngularDamping'", nullptr); - return 0; - } - cobj->setAngularDamping(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:setAngularDamping",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_setAngularDamping'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_isResting(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_isResting'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_isResting'", nullptr); - return 0; - } - auto&& ret = cobj->isResting(); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:isResting",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_isResting'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_setResting(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_setResting'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - bool arg0; - - ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.PhysicsBody:setResting"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_setResting'", nullptr); - return 0; - } - cobj->setResting(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:setResting",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_setResting'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_isRotationEnabled(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_isRotationEnabled'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_isRotationEnabled'", nullptr); - return 0; - } - auto&& ret = cobj->isRotationEnabled(); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:isRotationEnabled",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_isRotationEnabled'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_setRotationEnable(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_setRotationEnable'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - bool arg0; - - ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.PhysicsBody:setRotationEnable"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_setRotationEnable'", nullptr); - return 0; - } - cobj->setRotationEnable(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:setRotationEnable",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_setRotationEnable'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_isGravityEnabled(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_isGravityEnabled'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_isGravityEnabled'", nullptr); - return 0; - } - auto&& ret = cobj->isGravityEnabled(); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:isGravityEnabled",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_isGravityEnabled'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_setGravityEnable(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_setGravityEnable'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - bool arg0; - - ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.PhysicsBody:setGravityEnable"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_setGravityEnable'", nullptr); - return 0; - } - cobj->setGravityEnable(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:setGravityEnable",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_setGravityEnable'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_getTag(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_getTag'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_getTag'", nullptr); - return 0; - } - auto&& ret = cobj->getTag(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:getTag",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_getTag'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_setTag(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_setTag'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - int arg0; - - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.PhysicsBody:setTag"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_setTag'", nullptr); - return 0; - } - cobj->setTag(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:setTag",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_setTag'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_world2Local(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_world2Local'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - ax::Vec2 arg0; - - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsBody:world2Local"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_world2Local'", nullptr); - return 0; - } - auto&& ret = cobj->world2Local(arg0); - vec2_to_luaval(tolua_S, ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:world2Local",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_world2Local'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_local2World(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_local2World'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - ax::Vec2 arg0; - - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsBody:local2World"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_local2World'", nullptr); - return 0; - } - auto&& ret = cobj->local2World(arg0); - vec2_to_luaval(tolua_S, ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:local2World",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_local2World'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_getCPBody(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_getCPBody'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_getCPBody'", nullptr); - return 0; - } - auto&& ret = cobj->getCPBody(); - #pragma warning NO CONVERSION FROM NATIVE FOR cpBody*; - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:getCPBody",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_getCPBody'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_setFixedUpdate(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsBody* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsBody*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsBody_setFixedUpdate'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - bool arg0; - - ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.PhysicsBody:setFixedUpdate"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_setFixedUpdate'", nullptr); - return 0; - } - cobj->setFixedUpdate(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsBody:setFixedUpdate",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_setFixedUpdate'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsBody_create(lua_State* tolua_S) -{ - int argc = 0; - bool ok = true; -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - argc = lua_gettop(tolua_S)-1; - - do - { - if (argc == 1) - { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsBody:create"); - if (!ok) { break; } - ax::PhysicsBody* ret = ax::PhysicsBody::create(arg0); - object_to_luaval(tolua_S, "ax.PhysicsBody",(ax::PhysicsBody*)ret); - return 1; - } - } while (0); - ok = true; - do - { - if (argc == 0) - { - ax::PhysicsBody* ret = ax::PhysicsBody::create(); - object_to_luaval(tolua_S, "ax.PhysicsBody",(ax::PhysicsBody*)ret); - return 1; - } - } while (0); - ok = true; - do - { - if (argc == 2) - { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsBody:create"); - if (!ok) { break; } - double arg1; - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.PhysicsBody:create"); - if (!ok) { break; } - ax::PhysicsBody* ret = ax::PhysicsBody::create(arg0, arg1); - object_to_luaval(tolua_S, "ax.PhysicsBody",(ax::PhysicsBody*)ret); - return 1; - } - } while (0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d", "ax.PhysicsBody:create",argc, 2); - return 0; -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_create'.",&tolua_err); -#endif - return 0; -} -int lua_ax_physics_PhysicsBody_createCircle(lua_State* tolua_S) -{ - int argc = 0; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - argc = lua_gettop(tolua_S) - 1; - - if (argc == 1) - { - double arg0; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsBody:createCircle"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_createCircle'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsBody::createCircle(arg0); - object_to_luaval(tolua_S, "ax.PhysicsBody",(ax::PhysicsBody*)ret); - return 1; - } - if (argc == 2) - { - double arg0; - ax::PhysicsMaterial arg1; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsBody:createCircle"); - ok &= luaval_to_physics_material(tolua_S, 3, &arg1, "ax.PhysicsBody:createCircle"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_createCircle'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsBody::createCircle(arg0, arg1); - object_to_luaval(tolua_S, "ax.PhysicsBody",(ax::PhysicsBody*)ret); - return 1; - } - if (argc == 3) - { - double arg0; - ax::PhysicsMaterial arg1; - ax::Vec2 arg2; - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsBody:createCircle"); - ok &= luaval_to_physics_material(tolua_S, 3, &arg1, "ax.PhysicsBody:createCircle"); - ok &= luaval_to_vec2(tolua_S, 4, &arg2, "ax.PhysicsBody:createCircle"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_createCircle'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsBody::createCircle(arg0, arg1, arg2); - object_to_luaval(tolua_S, "ax.PhysicsBody",(ax::PhysicsBody*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.PhysicsBody:createCircle",argc, 1); - return 0; -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_createCircle'.",&tolua_err); -#endif - return 0; -} -int lua_ax_physics_PhysicsBody_createBox(lua_State* tolua_S) -{ - int argc = 0; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - argc = lua_gettop(tolua_S) - 1; - - if (argc == 1) - { - ax::Vec2 arg0; - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsBody:createBox"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_createBox'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsBody::createBox(arg0); - object_to_luaval(tolua_S, "ax.PhysicsBody",(ax::PhysicsBody*)ret); - return 1; - } - if (argc == 2) - { - ax::Vec2 arg0; - ax::PhysicsMaterial arg1; - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsBody:createBox"); - ok &= luaval_to_physics_material(tolua_S, 3, &arg1, "ax.PhysicsBody:createBox"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_createBox'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsBody::createBox(arg0, arg1); - object_to_luaval(tolua_S, "ax.PhysicsBody",(ax::PhysicsBody*)ret); - return 1; - } - if (argc == 3) - { - ax::Vec2 arg0; - ax::PhysicsMaterial arg1; - ax::Vec2 arg2; - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsBody:createBox"); - ok &= luaval_to_physics_material(tolua_S, 3, &arg1, "ax.PhysicsBody:createBox"); - ok &= luaval_to_vec2(tolua_S, 4, &arg2, "ax.PhysicsBody:createBox"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_createBox'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsBody::createBox(arg0, arg1, arg2); - object_to_luaval(tolua_S, "ax.PhysicsBody",(ax::PhysicsBody*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.PhysicsBody:createBox",argc, 1); - return 0; -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_createBox'.",&tolua_err); -#endif - return 0; -} -int lua_ax_physics_PhysicsBody_createEdgeSegment(lua_State* tolua_S) -{ - int argc = 0; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - argc = lua_gettop(tolua_S) - 1; - - if (argc == 2) - { - ax::Vec2 arg0; - ax::Vec2 arg1; - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsBody:createEdgeSegment"); - ok &= luaval_to_vec2(tolua_S, 3, &arg1, "ax.PhysicsBody:createEdgeSegment"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_createEdgeSegment'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsBody::createEdgeSegment(arg0, arg1); - object_to_luaval(tolua_S, "ax.PhysicsBody",(ax::PhysicsBody*)ret); - return 1; - } - if (argc == 3) - { - ax::Vec2 arg0; - ax::Vec2 arg1; - ax::PhysicsMaterial arg2; - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsBody:createEdgeSegment"); - ok &= luaval_to_vec2(tolua_S, 3, &arg1, "ax.PhysicsBody:createEdgeSegment"); - ok &= luaval_to_physics_material(tolua_S, 4, &arg2, "ax.PhysicsBody:createEdgeSegment"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_createEdgeSegment'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsBody::createEdgeSegment(arg0, arg1, arg2); - object_to_luaval(tolua_S, "ax.PhysicsBody",(ax::PhysicsBody*)ret); - return 1; - } - if (argc == 4) - { - ax::Vec2 arg0; - ax::Vec2 arg1; - ax::PhysicsMaterial arg2; - double arg3; - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsBody:createEdgeSegment"); - ok &= luaval_to_vec2(tolua_S, 3, &arg1, "ax.PhysicsBody:createEdgeSegment"); - ok &= luaval_to_physics_material(tolua_S, 4, &arg2, "ax.PhysicsBody:createEdgeSegment"); - ok &= luaval_to_number(tolua_S, 5,&arg3, "ax.PhysicsBody:createEdgeSegment"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_createEdgeSegment'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsBody::createEdgeSegment(arg0, arg1, arg2, arg3); - object_to_luaval(tolua_S, "ax.PhysicsBody",(ax::PhysicsBody*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.PhysicsBody:createEdgeSegment",argc, 2); - return 0; -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_createEdgeSegment'.",&tolua_err); -#endif - return 0; -} -int lua_ax_physics_PhysicsBody_createEdgeBox(lua_State* tolua_S) -{ - int argc = 0; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.PhysicsBody",0,&tolua_err)) goto tolua_lerror; -#endif - - argc = lua_gettop(tolua_S) - 1; - - if (argc == 1) - { - ax::Vec2 arg0; - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsBody:createEdgeBox"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_createEdgeBox'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsBody::createEdgeBox(arg0); - object_to_luaval(tolua_S, "ax.PhysicsBody",(ax::PhysicsBody*)ret); - return 1; - } - if (argc == 2) - { - ax::Vec2 arg0; - ax::PhysicsMaterial arg1; - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsBody:createEdgeBox"); - ok &= luaval_to_physics_material(tolua_S, 3, &arg1, "ax.PhysicsBody:createEdgeBox"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_createEdgeBox'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsBody::createEdgeBox(arg0, arg1); - object_to_luaval(tolua_S, "ax.PhysicsBody",(ax::PhysicsBody*)ret); - return 1; - } - if (argc == 3) - { - ax::Vec2 arg0; - ax::PhysicsMaterial arg1; - double arg2; - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsBody:createEdgeBox"); - ok &= luaval_to_physics_material(tolua_S, 3, &arg1, "ax.PhysicsBody:createEdgeBox"); - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.PhysicsBody:createEdgeBox"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_createEdgeBox'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsBody::createEdgeBox(arg0, arg1, arg2); - object_to_luaval(tolua_S, "ax.PhysicsBody",(ax::PhysicsBody*)ret); - return 1; - } - if (argc == 4) - { - ax::Vec2 arg0; - ax::PhysicsMaterial arg1; - double arg2; - ax::Vec2 arg3; - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsBody:createEdgeBox"); - ok &= luaval_to_physics_material(tolua_S, 3, &arg1, "ax.PhysicsBody:createEdgeBox"); - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.PhysicsBody:createEdgeBox"); - ok &= luaval_to_vec2(tolua_S, 5, &arg3, "ax.PhysicsBody:createEdgeBox"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsBody_createEdgeBox'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsBody::createEdgeBox(arg0, arg1, arg2, arg3); - object_to_luaval(tolua_S, "ax.PhysicsBody",(ax::PhysicsBody*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.PhysicsBody:createEdgeBox",argc, 1); - return 0; -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsBody_createEdgeBox'.",&tolua_err); -#endif - return 0; -} -static int lua_ax_physics_PhysicsBody_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (PhysicsBody)"); - return 0; -} - -int lua_register_ax_physics_PhysicsBody(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.PhysicsBody"); - tolua_cclass(tolua_S,"PhysicsBody","ax.PhysicsBody","ax.Component",nullptr); - - tolua_beginmodule(tolua_S,"PhysicsBody"); - tolua_function(tolua_S,"addShape",lua_ax_physics_PhysicsBody_addShape); - tolua_function(tolua_S,"removeShape",lua_ax_physics_PhysicsBody_removeShape); - tolua_function(tolua_S,"removeAllShapes",lua_ax_physics_PhysicsBody_removeAllShapes); - tolua_function(tolua_S,"getShapes",lua_ax_physics_PhysicsBody_getShapes); - tolua_function(tolua_S,"getFirstShape",lua_ax_physics_PhysicsBody_getFirstShape); - tolua_function(tolua_S,"getShape",lua_ax_physics_PhysicsBody_getShape); - tolua_function(tolua_S,"applyForce",lua_ax_physics_PhysicsBody_applyForce); - tolua_function(tolua_S,"resetForces",lua_ax_physics_PhysicsBody_resetForces); - tolua_function(tolua_S,"applyImpulse",lua_ax_physics_PhysicsBody_applyImpulse); - tolua_function(tolua_S,"applyTorque",lua_ax_physics_PhysicsBody_applyTorque); - tolua_function(tolua_S,"setVelocity",lua_ax_physics_PhysicsBody_setVelocity); - tolua_function(tolua_S,"getVelocity",lua_ax_physics_PhysicsBody_getVelocity); - tolua_function(tolua_S,"setAngularVelocity",lua_ax_physics_PhysicsBody_setAngularVelocity); - tolua_function(tolua_S,"getVelocityAtLocalPoint",lua_ax_physics_PhysicsBody_getVelocityAtLocalPoint); - tolua_function(tolua_S,"getVelocityAtWorldPoint",lua_ax_physics_PhysicsBody_getVelocityAtWorldPoint); - tolua_function(tolua_S,"getAngularVelocity",lua_ax_physics_PhysicsBody_getAngularVelocity); - tolua_function(tolua_S,"setVelocityLimit",lua_ax_physics_PhysicsBody_setVelocityLimit); - tolua_function(tolua_S,"getVelocityLimit",lua_ax_physics_PhysicsBody_getVelocityLimit); - tolua_function(tolua_S,"setAngularVelocityLimit",lua_ax_physics_PhysicsBody_setAngularVelocityLimit); - tolua_function(tolua_S,"getAngularVelocityLimit",lua_ax_physics_PhysicsBody_getAngularVelocityLimit); - tolua_function(tolua_S,"removeFromWorld",lua_ax_physics_PhysicsBody_removeFromWorld); - tolua_function(tolua_S,"getWorld",lua_ax_physics_PhysicsBody_getWorld); - tolua_function(tolua_S,"getNode",lua_ax_physics_PhysicsBody_getNode); - tolua_function(tolua_S,"setCategoryBitmask",lua_ax_physics_PhysicsBody_setCategoryBitmask); - tolua_function(tolua_S,"setContactTestBitmask",lua_ax_physics_PhysicsBody_setContactTestBitmask); - tolua_function(tolua_S,"setCollisionBitmask",lua_ax_physics_PhysicsBody_setCollisionBitmask); - tolua_function(tolua_S,"getCategoryBitmask",lua_ax_physics_PhysicsBody_getCategoryBitmask); - tolua_function(tolua_S,"getContactTestBitmask",lua_ax_physics_PhysicsBody_getContactTestBitmask); - tolua_function(tolua_S,"getCollisionBitmask",lua_ax_physics_PhysicsBody_getCollisionBitmask); - tolua_function(tolua_S,"setGroup",lua_ax_physics_PhysicsBody_setGroup); - tolua_function(tolua_S,"getGroup",lua_ax_physics_PhysicsBody_getGroup); - tolua_function(tolua_S,"getPosition",lua_ax_physics_PhysicsBody_getPosition); - tolua_function(tolua_S,"getRotation",lua_ax_physics_PhysicsBody_getRotation); - tolua_function(tolua_S,"setPositionOffset",lua_ax_physics_PhysicsBody_setPositionOffset); - tolua_function(tolua_S,"getPositionOffset",lua_ax_physics_PhysicsBody_getPositionOffset); - tolua_function(tolua_S,"setRotationOffset",lua_ax_physics_PhysicsBody_setRotationOffset); - tolua_function(tolua_S,"getRotationOffset",lua_ax_physics_PhysicsBody_getRotationOffset); - tolua_function(tolua_S,"isDynamic",lua_ax_physics_PhysicsBody_isDynamic); - tolua_function(tolua_S,"setDynamic",lua_ax_physics_PhysicsBody_setDynamic); - tolua_function(tolua_S,"setMass",lua_ax_physics_PhysicsBody_setMass); - tolua_function(tolua_S,"getMass",lua_ax_physics_PhysicsBody_getMass); - tolua_function(tolua_S,"addMass",lua_ax_physics_PhysicsBody_addMass); - tolua_function(tolua_S,"setMoment",lua_ax_physics_PhysicsBody_setMoment); - tolua_function(tolua_S,"getMoment",lua_ax_physics_PhysicsBody_getMoment); - tolua_function(tolua_S,"addMoment",lua_ax_physics_PhysicsBody_addMoment); - tolua_function(tolua_S,"getLinearDamping",lua_ax_physics_PhysicsBody_getLinearDamping); - tolua_function(tolua_S,"setLinearDamping",lua_ax_physics_PhysicsBody_setLinearDamping); - tolua_function(tolua_S,"getAngularDamping",lua_ax_physics_PhysicsBody_getAngularDamping); - tolua_function(tolua_S,"setAngularDamping",lua_ax_physics_PhysicsBody_setAngularDamping); - tolua_function(tolua_S,"isResting",lua_ax_physics_PhysicsBody_isResting); - tolua_function(tolua_S,"setResting",lua_ax_physics_PhysicsBody_setResting); - tolua_function(tolua_S,"isRotationEnabled",lua_ax_physics_PhysicsBody_isRotationEnabled); - tolua_function(tolua_S,"setRotationEnable",lua_ax_physics_PhysicsBody_setRotationEnable); - tolua_function(tolua_S,"isGravityEnabled",lua_ax_physics_PhysicsBody_isGravityEnabled); - tolua_function(tolua_S,"setGravityEnable",lua_ax_physics_PhysicsBody_setGravityEnable); - tolua_function(tolua_S,"getTag",lua_ax_physics_PhysicsBody_getTag); - tolua_function(tolua_S,"setTag",lua_ax_physics_PhysicsBody_setTag); - tolua_function(tolua_S,"world2Local",lua_ax_physics_PhysicsBody_world2Local); - tolua_function(tolua_S,"local2World",lua_ax_physics_PhysicsBody_local2World); - tolua_function(tolua_S,"getCPBody",lua_ax_physics_PhysicsBody_getCPBody); - tolua_function(tolua_S,"setFixedUpdate",lua_ax_physics_PhysicsBody_setFixedUpdate); - tolua_function(tolua_S,"create", lua_ax_physics_PhysicsBody_create); - tolua_function(tolua_S,"createCircle", lua_ax_physics_PhysicsBody_createCircle); - tolua_function(tolua_S,"createBox", lua_ax_physics_PhysicsBody_createBox); - tolua_function(tolua_S,"createEdgeSegment", lua_ax_physics_PhysicsBody_createEdgeSegment); - tolua_function(tolua_S,"createEdgeBox", lua_ax_physics_PhysicsBody_createEdgeBox); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::PhysicsBody).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.PhysicsBody"; - g_typeCast[typeName] = "ax.PhysicsBody"; - return 1; -} - -int lua_ax_physics_PhysicsContact_getShapeA(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsContact* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsContact",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsContact*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsContact_getShapeA'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsContact_getShapeA'", nullptr); - return 0; - } - auto&& ret = cobj->getShapeA(); - object_to_luaval(tolua_S, "ax.PhysicsShape",(ax::PhysicsShape*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsContact:getShapeA",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsContact_getShapeA'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsContact_getShapeB(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsContact* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsContact",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsContact*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsContact_getShapeB'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsContact_getShapeB'", nullptr); - return 0; - } - auto&& ret = cobj->getShapeB(); - object_to_luaval(tolua_S, "ax.PhysicsShape",(ax::PhysicsShape*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsContact:getShapeB",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsContact_getShapeB'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsContact_getContactData(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsContact* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsContact",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsContact*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsContact_getContactData'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsContact_getContactData'", nullptr); - return 0; - } - auto&& ret = cobj->getContactData(); - physics_contactdata_to_luaval(tolua_S, ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsContact:getContactData",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsContact_getContactData'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsContact_getPreContactData(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsContact* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsContact",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsContact*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsContact_getPreContactData'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsContact_getPreContactData'", nullptr); - return 0; - } - auto&& ret = cobj->getPreContactData(); - physics_contactdata_to_luaval(tolua_S, ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsContact:getPreContactData",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsContact_getPreContactData'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsContact_getEventCode(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsContact* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsContact",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsContact*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsContact_getEventCode'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsContact_getEventCode'", nullptr); - return 0; - } - int ret = (int)cobj->getEventCode(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsContact:getEventCode",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsContact_getEventCode'.",&tolua_err); -#endif - - return 0; -} -static int lua_ax_physics_PhysicsContact_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (PhysicsContact)"); - return 0; -} - -int lua_register_ax_physics_PhysicsContact(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.PhysicsContact"); - tolua_cclass(tolua_S,"PhysicsContact","ax.PhysicsContact","ax.EventCustom",nullptr); - - tolua_beginmodule(tolua_S,"PhysicsContact"); - tolua_function(tolua_S,"getShapeA",lua_ax_physics_PhysicsContact_getShapeA); - tolua_function(tolua_S,"getShapeB",lua_ax_physics_PhysicsContact_getShapeB); - tolua_function(tolua_S,"getContactData",lua_ax_physics_PhysicsContact_getContactData); - tolua_function(tolua_S,"getPreContactData",lua_ax_physics_PhysicsContact_getPreContactData); - tolua_function(tolua_S,"getEventCode",lua_ax_physics_PhysicsContact_getEventCode); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::PhysicsContact).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.PhysicsContact"; - g_typeCast[typeName] = "ax.PhysicsContact"; - return 1; -} - -int lua_ax_physics_PhysicsContactPreSolve_getRestitution(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsContactPreSolve* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsContactPreSolve",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsContactPreSolve*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsContactPreSolve_getRestitution'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsContactPreSolve_getRestitution'", nullptr); - return 0; - } - auto&& ret = cobj->getRestitution(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsContactPreSolve:getRestitution",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsContactPreSolve_getRestitution'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsContactPreSolve_getFriction(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsContactPreSolve* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsContactPreSolve",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsContactPreSolve*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsContactPreSolve_getFriction'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsContactPreSolve_getFriction'", nullptr); - return 0; - } - auto&& ret = cobj->getFriction(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsContactPreSolve:getFriction",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsContactPreSolve_getFriction'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsContactPreSolve_getSurfaceVelocity(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsContactPreSolve* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsContactPreSolve",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsContactPreSolve*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsContactPreSolve_getSurfaceVelocity'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsContactPreSolve_getSurfaceVelocity'", nullptr); - return 0; - } - auto&& ret = cobj->getSurfaceVelocity(); - vec2_to_luaval(tolua_S, ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsContactPreSolve:getSurfaceVelocity",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsContactPreSolve_getSurfaceVelocity'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsContactPreSolve_setRestitution(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsContactPreSolve* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsContactPreSolve",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsContactPreSolve*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsContactPreSolve_setRestitution'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsContactPreSolve:setRestitution"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsContactPreSolve_setRestitution'", nullptr); - return 0; - } - cobj->setRestitution(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsContactPreSolve:setRestitution",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsContactPreSolve_setRestitution'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsContactPreSolve_setFriction(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsContactPreSolve* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsContactPreSolve",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsContactPreSolve*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsContactPreSolve_setFriction'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsContactPreSolve:setFriction"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsContactPreSolve_setFriction'", nullptr); - return 0; - } - cobj->setFriction(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsContactPreSolve:setFriction",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsContactPreSolve_setFriction'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsContactPreSolve_setSurfaceVelocity(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsContactPreSolve* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsContactPreSolve",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsContactPreSolve*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsContactPreSolve_setSurfaceVelocity'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - ax::Vec2 arg0; - - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsContactPreSolve:setSurfaceVelocity"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsContactPreSolve_setSurfaceVelocity'", nullptr); - return 0; - } - cobj->setSurfaceVelocity(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsContactPreSolve:setSurfaceVelocity",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsContactPreSolve_setSurfaceVelocity'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsContactPreSolve_ignore(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsContactPreSolve* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsContactPreSolve",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsContactPreSolve*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsContactPreSolve_ignore'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsContactPreSolve_ignore'", nullptr); - return 0; - } - cobj->ignore(); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsContactPreSolve:ignore",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsContactPreSolve_ignore'.",&tolua_err); -#endif - - return 0; -} -static int lua_ax_physics_PhysicsContactPreSolve_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (PhysicsContactPreSolve)"); - return 0; -} - -int lua_register_ax_physics_PhysicsContactPreSolve(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.PhysicsContactPreSolve"); - tolua_cclass(tolua_S,"PhysicsContactPreSolve","ax.PhysicsContactPreSolve","",nullptr); - - tolua_beginmodule(tolua_S,"PhysicsContactPreSolve"); - tolua_function(tolua_S,"getRestitution",lua_ax_physics_PhysicsContactPreSolve_getRestitution); - tolua_function(tolua_S,"getFriction",lua_ax_physics_PhysicsContactPreSolve_getFriction); - tolua_function(tolua_S,"getSurfaceVelocity",lua_ax_physics_PhysicsContactPreSolve_getSurfaceVelocity); - tolua_function(tolua_S,"setRestitution",lua_ax_physics_PhysicsContactPreSolve_setRestitution); - tolua_function(tolua_S,"setFriction",lua_ax_physics_PhysicsContactPreSolve_setFriction); - tolua_function(tolua_S,"setSurfaceVelocity",lua_ax_physics_PhysicsContactPreSolve_setSurfaceVelocity); - tolua_function(tolua_S,"ignore",lua_ax_physics_PhysicsContactPreSolve_ignore); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::PhysicsContactPreSolve).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.PhysicsContactPreSolve"; - g_typeCast[typeName] = "ax.PhysicsContactPreSolve"; - return 1; -} - -int lua_ax_physics_PhysicsContactPostSolve_getRestitution(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsContactPostSolve* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsContactPostSolve",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsContactPostSolve*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsContactPostSolve_getRestitution'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsContactPostSolve_getRestitution'", nullptr); - return 0; - } - auto&& ret = cobj->getRestitution(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsContactPostSolve:getRestitution",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsContactPostSolve_getRestitution'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsContactPostSolve_getFriction(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsContactPostSolve* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsContactPostSolve",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsContactPostSolve*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsContactPostSolve_getFriction'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsContactPostSolve_getFriction'", nullptr); - return 0; - } - auto&& ret = cobj->getFriction(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsContactPostSolve:getFriction",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsContactPostSolve_getFriction'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsContactPostSolve_getSurfaceVelocity(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsContactPostSolve* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsContactPostSolve",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsContactPostSolve*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsContactPostSolve_getSurfaceVelocity'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsContactPostSolve_getSurfaceVelocity'", nullptr); - return 0; - } - auto&& ret = cobj->getSurfaceVelocity(); - vec2_to_luaval(tolua_S, ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsContactPostSolve:getSurfaceVelocity",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsContactPostSolve_getSurfaceVelocity'.",&tolua_err); -#endif - - return 0; -} -static int lua_ax_physics_PhysicsContactPostSolve_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (PhysicsContactPostSolve)"); - return 0; -} - -int lua_register_ax_physics_PhysicsContactPostSolve(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.PhysicsContactPostSolve"); - tolua_cclass(tolua_S,"PhysicsContactPostSolve","ax.PhysicsContactPostSolve","",nullptr); - - tolua_beginmodule(tolua_S,"PhysicsContactPostSolve"); - tolua_function(tolua_S,"getRestitution",lua_ax_physics_PhysicsContactPostSolve_getRestitution); - tolua_function(tolua_S,"getFriction",lua_ax_physics_PhysicsContactPostSolve_getFriction); - tolua_function(tolua_S,"getSurfaceVelocity",lua_ax_physics_PhysicsContactPostSolve_getSurfaceVelocity); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::PhysicsContactPostSolve).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.PhysicsContactPostSolve"; - g_typeCast[typeName] = "ax.PhysicsContactPostSolve"; - return 1; -} - -int lua_ax_physics_EventListenerPhysicsContact_create(lua_State* tolua_S) -{ - int argc = 0; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.EventListenerPhysicsContact",0,&tolua_err)) goto tolua_lerror; -#endif - - argc = lua_gettop(tolua_S) - 1; - - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_EventListenerPhysicsContact_create'", nullptr); - return 0; - } - auto&& ret = ax::EventListenerPhysicsContact::create(); - object_to_luaval(tolua_S, "ax.EventListenerPhysicsContact",(ax::EventListenerPhysicsContact*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.EventListenerPhysicsContact:create",argc, 0); - return 0; -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_EventListenerPhysicsContact_create'.",&tolua_err); -#endif - return 0; -} -static int lua_ax_physics_EventListenerPhysicsContact_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (EventListenerPhysicsContact)"); - return 0; -} - -int lua_register_ax_physics_EventListenerPhysicsContact(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.EventListenerPhysicsContact"); - tolua_cclass(tolua_S,"EventListenerPhysicsContact","ax.EventListenerPhysicsContact","ax.EventListenerCustom",nullptr); - - tolua_beginmodule(tolua_S,"EventListenerPhysicsContact"); - tolua_function(tolua_S,"create", lua_ax_physics_EventListenerPhysicsContact_create); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::EventListenerPhysicsContact).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.EventListenerPhysicsContact"; - g_typeCast[typeName] = "ax.EventListenerPhysicsContact"; - return 1; -} - -int lua_ax_physics_EventListenerPhysicsContactWithBodies_hitTest(lua_State* tolua_S) -{ - int argc = 0; - ax::EventListenerPhysicsContactWithBodies* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.EventListenerPhysicsContactWithBodies",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::EventListenerPhysicsContactWithBodies*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_EventListenerPhysicsContactWithBodies_hitTest'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 2) - { - ax::PhysicsShape* arg0; - ax::PhysicsShape* arg1; - - ok &= luaval_to_object(tolua_S, 2, "ax.PhysicsShape",&arg0, "ax.EventListenerPhysicsContactWithBodies:hitTest"); - - ok &= luaval_to_object(tolua_S, 3, "ax.PhysicsShape",&arg1, "ax.EventListenerPhysicsContactWithBodies:hitTest"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_EventListenerPhysicsContactWithBodies_hitTest'", nullptr); - return 0; - } - auto&& ret = cobj->hitTest(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.EventListenerPhysicsContactWithBodies:hitTest",argc, 2); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_EventListenerPhysicsContactWithBodies_hitTest'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_EventListenerPhysicsContactWithBodies_create(lua_State* tolua_S) -{ - int argc = 0; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.EventListenerPhysicsContactWithBodies",0,&tolua_err)) goto tolua_lerror; -#endif - - argc = lua_gettop(tolua_S) - 1; - - if (argc == 2) - { - ax::PhysicsBody* arg0; - ax::PhysicsBody* arg1; - ok &= luaval_to_object(tolua_S, 2, "ax.PhysicsBody",&arg0, "ax.EventListenerPhysicsContactWithBodies:create"); - ok &= luaval_to_object(tolua_S, 3, "ax.PhysicsBody",&arg1, "ax.EventListenerPhysicsContactWithBodies:create"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_EventListenerPhysicsContactWithBodies_create'", nullptr); - return 0; - } - auto&& ret = ax::EventListenerPhysicsContactWithBodies::create(arg0, arg1); - object_to_luaval(tolua_S, "ax.EventListenerPhysicsContactWithBodies",(ax::EventListenerPhysicsContactWithBodies*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.EventListenerPhysicsContactWithBodies:create",argc, 2); - return 0; -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_EventListenerPhysicsContactWithBodies_create'.",&tolua_err); -#endif - return 0; -} -static int lua_ax_physics_EventListenerPhysicsContactWithBodies_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (EventListenerPhysicsContactWithBodies)"); - return 0; -} - -int lua_register_ax_physics_EventListenerPhysicsContactWithBodies(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.EventListenerPhysicsContactWithBodies"); - tolua_cclass(tolua_S,"EventListenerPhysicsContactWithBodies","ax.EventListenerPhysicsContactWithBodies","ax.EventListenerPhysicsContact",nullptr); - - tolua_beginmodule(tolua_S,"EventListenerPhysicsContactWithBodies"); - tolua_function(tolua_S,"hitTest",lua_ax_physics_EventListenerPhysicsContactWithBodies_hitTest); - tolua_function(tolua_S,"create", lua_ax_physics_EventListenerPhysicsContactWithBodies_create); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::EventListenerPhysicsContactWithBodies).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.EventListenerPhysicsContactWithBodies"; - g_typeCast[typeName] = "ax.EventListenerPhysicsContactWithBodies"; - return 1; -} - -int lua_ax_physics_EventListenerPhysicsContactWithShapes_hitTest(lua_State* tolua_S) -{ - int argc = 0; - ax::EventListenerPhysicsContactWithShapes* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.EventListenerPhysicsContactWithShapes",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::EventListenerPhysicsContactWithShapes*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_EventListenerPhysicsContactWithShapes_hitTest'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 2) - { - ax::PhysicsShape* arg0; - ax::PhysicsShape* arg1; - - ok &= luaval_to_object(tolua_S, 2, "ax.PhysicsShape",&arg0, "ax.EventListenerPhysicsContactWithShapes:hitTest"); - - ok &= luaval_to_object(tolua_S, 3, "ax.PhysicsShape",&arg1, "ax.EventListenerPhysicsContactWithShapes:hitTest"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_EventListenerPhysicsContactWithShapes_hitTest'", nullptr); - return 0; - } - auto&& ret = cobj->hitTest(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.EventListenerPhysicsContactWithShapes:hitTest",argc, 2); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_EventListenerPhysicsContactWithShapes_hitTest'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_EventListenerPhysicsContactWithShapes_create(lua_State* tolua_S) -{ - int argc = 0; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.EventListenerPhysicsContactWithShapes",0,&tolua_err)) goto tolua_lerror; -#endif - - argc = lua_gettop(tolua_S) - 1; - - if (argc == 2) - { - ax::PhysicsShape* arg0; - ax::PhysicsShape* arg1; - ok &= luaval_to_object(tolua_S, 2, "ax.PhysicsShape",&arg0, "ax.EventListenerPhysicsContactWithShapes:create"); - ok &= luaval_to_object(tolua_S, 3, "ax.PhysicsShape",&arg1, "ax.EventListenerPhysicsContactWithShapes:create"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_EventListenerPhysicsContactWithShapes_create'", nullptr); - return 0; - } - auto&& ret = ax::EventListenerPhysicsContactWithShapes::create(arg0, arg1); - object_to_luaval(tolua_S, "ax.EventListenerPhysicsContactWithShapes",(ax::EventListenerPhysicsContactWithShapes*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.EventListenerPhysicsContactWithShapes:create",argc, 2); - return 0; -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_EventListenerPhysicsContactWithShapes_create'.",&tolua_err); -#endif - return 0; -} -static int lua_ax_physics_EventListenerPhysicsContactWithShapes_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (EventListenerPhysicsContactWithShapes)"); - return 0; -} - -int lua_register_ax_physics_EventListenerPhysicsContactWithShapes(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.EventListenerPhysicsContactWithShapes"); - tolua_cclass(tolua_S,"EventListenerPhysicsContactWithShapes","ax.EventListenerPhysicsContactWithShapes","ax.EventListenerPhysicsContact",nullptr); - - tolua_beginmodule(tolua_S,"EventListenerPhysicsContactWithShapes"); - tolua_function(tolua_S,"hitTest",lua_ax_physics_EventListenerPhysicsContactWithShapes_hitTest); - tolua_function(tolua_S,"create", lua_ax_physics_EventListenerPhysicsContactWithShapes_create); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::EventListenerPhysicsContactWithShapes).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.EventListenerPhysicsContactWithShapes"; - g_typeCast[typeName] = "ax.EventListenerPhysicsContactWithShapes"; - return 1; -} - -int lua_ax_physics_EventListenerPhysicsContactWithGroup_hitTest(lua_State* tolua_S) -{ - int argc = 0; - ax::EventListenerPhysicsContactWithGroup* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.EventListenerPhysicsContactWithGroup",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::EventListenerPhysicsContactWithGroup*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_EventListenerPhysicsContactWithGroup_hitTest'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 2) - { - ax::PhysicsShape* arg0; - ax::PhysicsShape* arg1; - - ok &= luaval_to_object(tolua_S, 2, "ax.PhysicsShape",&arg0, "ax.EventListenerPhysicsContactWithGroup:hitTest"); - - ok &= luaval_to_object(tolua_S, 3, "ax.PhysicsShape",&arg1, "ax.EventListenerPhysicsContactWithGroup:hitTest"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_EventListenerPhysicsContactWithGroup_hitTest'", nullptr); - return 0; - } - auto&& ret = cobj->hitTest(arg0, arg1); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.EventListenerPhysicsContactWithGroup:hitTest",argc, 2); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_EventListenerPhysicsContactWithGroup_hitTest'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_EventListenerPhysicsContactWithGroup_create(lua_State* tolua_S) -{ - int argc = 0; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.EventListenerPhysicsContactWithGroup",0,&tolua_err)) goto tolua_lerror; -#endif - - argc = lua_gettop(tolua_S) - 1; - - if (argc == 1) - { - int arg0; - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.EventListenerPhysicsContactWithGroup:create"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_EventListenerPhysicsContactWithGroup_create'", nullptr); - return 0; - } - auto&& ret = ax::EventListenerPhysicsContactWithGroup::create(arg0); - object_to_luaval(tolua_S, "ax.EventListenerPhysicsContactWithGroup",(ax::EventListenerPhysicsContactWithGroup*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.EventListenerPhysicsContactWithGroup:create",argc, 1); - return 0; -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_EventListenerPhysicsContactWithGroup_create'.",&tolua_err); -#endif - return 0; -} -static int lua_ax_physics_EventListenerPhysicsContactWithGroup_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (EventListenerPhysicsContactWithGroup)"); - return 0; -} - -int lua_register_ax_physics_EventListenerPhysicsContactWithGroup(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.EventListenerPhysicsContactWithGroup"); - tolua_cclass(tolua_S,"EventListenerPhysicsContactWithGroup","ax.EventListenerPhysicsContactWithGroup","ax.EventListenerPhysicsContact",nullptr); - - tolua_beginmodule(tolua_S,"EventListenerPhysicsContactWithGroup"); - tolua_function(tolua_S,"hitTest",lua_ax_physics_EventListenerPhysicsContactWithGroup_hitTest); - tolua_function(tolua_S,"create", lua_ax_physics_EventListenerPhysicsContactWithGroup_create); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::EventListenerPhysicsContactWithGroup).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.EventListenerPhysicsContactWithGroup"; - g_typeCast[typeName] = "ax.EventListenerPhysicsContactWithGroup"; - return 1; -} - -int lua_ax_physics_PhysicsJoint_getBodyA(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJoint* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJoint",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJoint*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJoint_getBodyA'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJoint_getBodyA'", nullptr); - return 0; - } - auto&& ret = cobj->getBodyA(); - object_to_luaval(tolua_S, "ax.PhysicsBody",(ax::PhysicsBody*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJoint:getBodyA",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJoint_getBodyA'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJoint_getBodyB(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJoint* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJoint",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJoint*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJoint_getBodyB'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJoint_getBodyB'", nullptr); - return 0; - } - auto&& ret = cobj->getBodyB(); - object_to_luaval(tolua_S, "ax.PhysicsBody",(ax::PhysicsBody*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJoint:getBodyB",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJoint_getBodyB'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJoint_getWorld(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJoint* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJoint",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJoint*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJoint_getWorld'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJoint_getWorld'", nullptr); - return 0; - } - auto&& ret = cobj->getWorld(); - object_to_luaval(tolua_S, "ax.PhysicsWorld",(ax::PhysicsWorld*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJoint:getWorld",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJoint_getWorld'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJoint_getTag(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJoint* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJoint",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJoint*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJoint_getTag'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJoint_getTag'", nullptr); - return 0; - } - auto&& ret = cobj->getTag(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJoint:getTag",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJoint_getTag'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJoint_setTag(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJoint* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJoint",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJoint*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJoint_setTag'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - int arg0; - - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.PhysicsJoint:setTag"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJoint_setTag'", nullptr); - return 0; - } - cobj->setTag(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJoint:setTag",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJoint_setTag'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJoint_isEnabled(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJoint* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJoint",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJoint*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJoint_isEnabled'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJoint_isEnabled'", nullptr); - return 0; - } - auto&& ret = cobj->isEnabled(); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJoint:isEnabled",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJoint_isEnabled'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJoint_setEnable(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJoint* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJoint",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJoint*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJoint_setEnable'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - bool arg0; - - ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.PhysicsJoint:setEnable"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJoint_setEnable'", nullptr); - return 0; - } - cobj->setEnable(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJoint:setEnable",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJoint_setEnable'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJoint_isCollisionEnabled(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJoint* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJoint",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJoint*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJoint_isCollisionEnabled'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJoint_isCollisionEnabled'", nullptr); - return 0; - } - auto&& ret = cobj->isCollisionEnabled(); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJoint:isCollisionEnabled",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJoint_isCollisionEnabled'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJoint_setCollisionEnable(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJoint* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJoint",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJoint*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJoint_setCollisionEnable'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - bool arg0; - - ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.PhysicsJoint:setCollisionEnable"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJoint_setCollisionEnable'", nullptr); - return 0; - } - cobj->setCollisionEnable(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJoint:setCollisionEnable",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJoint_setCollisionEnable'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJoint_removeFormWorld(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJoint* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJoint",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJoint*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJoint_removeFormWorld'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJoint_removeFormWorld'", nullptr); - return 0; - } - cobj->removeFormWorld(); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJoint:removeFormWorld",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJoint_removeFormWorld'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJoint_setMaxForce(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJoint* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJoint",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJoint*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJoint_setMaxForce'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsJoint:setMaxForce"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJoint_setMaxForce'", nullptr); - return 0; - } - cobj->setMaxForce(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJoint:setMaxForce",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJoint_setMaxForce'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJoint_getMaxForce(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJoint* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJoint",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJoint*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJoint_getMaxForce'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJoint_getMaxForce'", nullptr); - return 0; - } - auto&& ret = cobj->getMaxForce(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJoint:getMaxForce",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJoint_getMaxForce'.",&tolua_err); -#endif - - return 0; -} -static int lua_ax_physics_PhysicsJoint_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (PhysicsJoint)"); - return 0; -} - -int lua_register_ax_physics_PhysicsJoint(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.PhysicsJoint"); - tolua_cclass(tolua_S,"PhysicsJoint","ax.PhysicsJoint","",nullptr); - - tolua_beginmodule(tolua_S,"PhysicsJoint"); - tolua_function(tolua_S,"getBodyA",lua_ax_physics_PhysicsJoint_getBodyA); - tolua_function(tolua_S,"getBodyB",lua_ax_physics_PhysicsJoint_getBodyB); - tolua_function(tolua_S,"getWorld",lua_ax_physics_PhysicsJoint_getWorld); - tolua_function(tolua_S,"getTag",lua_ax_physics_PhysicsJoint_getTag); - tolua_function(tolua_S,"setTag",lua_ax_physics_PhysicsJoint_setTag); - tolua_function(tolua_S,"isEnabled",lua_ax_physics_PhysicsJoint_isEnabled); - tolua_function(tolua_S,"setEnable",lua_ax_physics_PhysicsJoint_setEnable); - tolua_function(tolua_S,"isCollisionEnabled",lua_ax_physics_PhysicsJoint_isCollisionEnabled); - tolua_function(tolua_S,"setCollisionEnable",lua_ax_physics_PhysicsJoint_setCollisionEnable); - tolua_function(tolua_S,"removeFormWorld",lua_ax_physics_PhysicsJoint_removeFormWorld); - tolua_function(tolua_S,"setMaxForce",lua_ax_physics_PhysicsJoint_setMaxForce); - tolua_function(tolua_S,"getMaxForce",lua_ax_physics_PhysicsJoint_getMaxForce); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::PhysicsJoint).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.PhysicsJoint"; - g_typeCast[typeName] = "ax.PhysicsJoint"; - return 1; -} - -int lua_ax_physics_PhysicsJointFixed_createConstraints(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointFixed* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointFixed",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointFixed*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointFixed_createConstraints'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointFixed_createConstraints'", nullptr); - return 0; - } - auto&& ret = cobj->createConstraints(); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointFixed:createConstraints",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointFixed_createConstraints'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointFixed_construct(lua_State* tolua_S) -{ - int argc = 0; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.PhysicsJointFixed",0,&tolua_err)) goto tolua_lerror; -#endif - - argc = lua_gettop(tolua_S) - 1; - - if (argc == 3) - { - ax::PhysicsBody* arg0; - ax::PhysicsBody* arg1; - ax::Vec2 arg2; - ok &= luaval_to_object(tolua_S, 2, "ax.PhysicsBody",&arg0, "ax.PhysicsJointFixed:construct"); - ok &= luaval_to_object(tolua_S, 3, "ax.PhysicsBody",&arg1, "ax.PhysicsJointFixed:construct"); - ok &= luaval_to_vec2(tolua_S, 4, &arg2, "ax.PhysicsJointFixed:construct"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointFixed_construct'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsJointFixed::construct(arg0, arg1, arg2); - object_to_luaval(tolua_S, "ax.PhysicsJointFixed",(ax::PhysicsJointFixed*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.PhysicsJointFixed:construct",argc, 3); - return 0; -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointFixed_construct'.",&tolua_err); -#endif - return 0; -} -static int lua_ax_physics_PhysicsJointFixed_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (PhysicsJointFixed)"); - return 0; -} - -int lua_register_ax_physics_PhysicsJointFixed(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.PhysicsJointFixed"); - tolua_cclass(tolua_S,"PhysicsJointFixed","ax.PhysicsJointFixed","ax.PhysicsJoint",nullptr); - - tolua_beginmodule(tolua_S,"PhysicsJointFixed"); - tolua_function(tolua_S,"createConstraints",lua_ax_physics_PhysicsJointFixed_createConstraints); - tolua_function(tolua_S,"construct", lua_ax_physics_PhysicsJointFixed_construct); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::PhysicsJointFixed).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.PhysicsJointFixed"; - g_typeCast[typeName] = "ax.PhysicsJointFixed"; - return 1; -} - -int lua_ax_physics_PhysicsJointLimit_getAnchr1(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointLimit* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointLimit",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointLimit*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointLimit_getAnchr1'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointLimit_getAnchr1'", nullptr); - return 0; - } - auto&& ret = cobj->getAnchr1(); - vec2_to_luaval(tolua_S, ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointLimit:getAnchr1",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointLimit_getAnchr1'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointLimit_setAnchr1(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointLimit* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointLimit",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointLimit*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointLimit_setAnchr1'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - ax::Vec2 arg0; - - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsJointLimit:setAnchr1"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointLimit_setAnchr1'", nullptr); - return 0; - } - cobj->setAnchr1(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointLimit:setAnchr1",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointLimit_setAnchr1'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointLimit_getAnchr2(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointLimit* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointLimit",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointLimit*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointLimit_getAnchr2'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointLimit_getAnchr2'", nullptr); - return 0; - } - auto&& ret = cobj->getAnchr2(); - vec2_to_luaval(tolua_S, ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointLimit:getAnchr2",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointLimit_getAnchr2'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointLimit_setAnchr2(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointLimit* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointLimit",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointLimit*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointLimit_setAnchr2'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - ax::Vec2 arg0; - - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsJointLimit:setAnchr2"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointLimit_setAnchr2'", nullptr); - return 0; - } - cobj->setAnchr2(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointLimit:setAnchr2",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointLimit_setAnchr2'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointLimit_getMin(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointLimit* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointLimit",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointLimit*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointLimit_getMin'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointLimit_getMin'", nullptr); - return 0; - } - auto&& ret = cobj->getMin(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointLimit:getMin",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointLimit_getMin'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointLimit_setMin(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointLimit* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointLimit",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointLimit*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointLimit_setMin'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsJointLimit:setMin"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointLimit_setMin'", nullptr); - return 0; - } - cobj->setMin(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointLimit:setMin",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointLimit_setMin'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointLimit_getMax(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointLimit* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointLimit",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointLimit*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointLimit_getMax'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointLimit_getMax'", nullptr); - return 0; - } - auto&& ret = cobj->getMax(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointLimit:getMax",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointLimit_getMax'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointLimit_setMax(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointLimit* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointLimit",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointLimit*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointLimit_setMax'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsJointLimit:setMax"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointLimit_setMax'", nullptr); - return 0; - } - cobj->setMax(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointLimit:setMax",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointLimit_setMax'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointLimit_createConstraints(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointLimit* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointLimit",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointLimit*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointLimit_createConstraints'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointLimit_createConstraints'", nullptr); - return 0; - } - auto&& ret = cobj->createConstraints(); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointLimit:createConstraints",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointLimit_createConstraints'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointLimit_construct(lua_State* tolua_S) -{ - int argc = 0; - bool ok = true; -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.PhysicsJointLimit",0,&tolua_err)) goto tolua_lerror; -#endif - - argc = lua_gettop(tolua_S)-1; - - do - { - if (argc == 6) - { - ax::PhysicsBody* arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.PhysicsBody",&arg0, "ax.PhysicsJointLimit:construct"); - if (!ok) { break; } - ax::PhysicsBody* arg1; - ok &= luaval_to_object(tolua_S, 3, "ax.PhysicsBody",&arg1, "ax.PhysicsJointLimit:construct"); - if (!ok) { break; } - ax::Vec2 arg2; - ok &= luaval_to_vec2(tolua_S, 4, &arg2, "ax.PhysicsJointLimit:construct"); - if (!ok) { break; } - ax::Vec2 arg3; - ok &= luaval_to_vec2(tolua_S, 5, &arg3, "ax.PhysicsJointLimit:construct"); - if (!ok) { break; } - double arg4; - ok &= luaval_to_number(tolua_S, 6,&arg4, "ax.PhysicsJointLimit:construct"); - if (!ok) { break; } - double arg5; - ok &= luaval_to_number(tolua_S, 7,&arg5, "ax.PhysicsJointLimit:construct"); - if (!ok) { break; } - ax::PhysicsJointLimit* ret = ax::PhysicsJointLimit::construct(arg0, arg1, arg2, arg3, arg4, arg5); - object_to_luaval(tolua_S, "ax.PhysicsJointLimit",(ax::PhysicsJointLimit*)ret); - return 1; - } - } while (0); - ok = true; - do - { - if (argc == 4) - { - ax::PhysicsBody* arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.PhysicsBody",&arg0, "ax.PhysicsJointLimit:construct"); - if (!ok) { break; } - ax::PhysicsBody* arg1; - ok &= luaval_to_object(tolua_S, 3, "ax.PhysicsBody",&arg1, "ax.PhysicsJointLimit:construct"); - if (!ok) { break; } - ax::Vec2 arg2; - ok &= luaval_to_vec2(tolua_S, 4, &arg2, "ax.PhysicsJointLimit:construct"); - if (!ok) { break; } - ax::Vec2 arg3; - ok &= luaval_to_vec2(tolua_S, 5, &arg3, "ax.PhysicsJointLimit:construct"); - if (!ok) { break; } - ax::PhysicsJointLimit* ret = ax::PhysicsJointLimit::construct(arg0, arg1, arg2, arg3); - object_to_luaval(tolua_S, "ax.PhysicsJointLimit",(ax::PhysicsJointLimit*)ret); - return 1; - } - } while (0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d", "ax.PhysicsJointLimit:construct",argc, 4); - return 0; -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointLimit_construct'.",&tolua_err); -#endif - return 0; -} -static int lua_ax_physics_PhysicsJointLimit_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (PhysicsJointLimit)"); - return 0; -} - -int lua_register_ax_physics_PhysicsJointLimit(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.PhysicsJointLimit"); - tolua_cclass(tolua_S,"PhysicsJointLimit","ax.PhysicsJointLimit","ax.PhysicsJoint",nullptr); - - tolua_beginmodule(tolua_S,"PhysicsJointLimit"); - tolua_function(tolua_S,"getAnchr1",lua_ax_physics_PhysicsJointLimit_getAnchr1); - tolua_function(tolua_S,"setAnchr1",lua_ax_physics_PhysicsJointLimit_setAnchr1); - tolua_function(tolua_S,"getAnchr2",lua_ax_physics_PhysicsJointLimit_getAnchr2); - tolua_function(tolua_S,"setAnchr2",lua_ax_physics_PhysicsJointLimit_setAnchr2); - tolua_function(tolua_S,"getMin",lua_ax_physics_PhysicsJointLimit_getMin); - tolua_function(tolua_S,"setMin",lua_ax_physics_PhysicsJointLimit_setMin); - tolua_function(tolua_S,"getMax",lua_ax_physics_PhysicsJointLimit_getMax); - tolua_function(tolua_S,"setMax",lua_ax_physics_PhysicsJointLimit_setMax); - tolua_function(tolua_S,"createConstraints",lua_ax_physics_PhysicsJointLimit_createConstraints); - tolua_function(tolua_S,"construct", lua_ax_physics_PhysicsJointLimit_construct); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::PhysicsJointLimit).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.PhysicsJointLimit"; - g_typeCast[typeName] = "ax.PhysicsJointLimit"; - return 1; -} - -int lua_ax_physics_PhysicsJointPin_createConstraints(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointPin* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointPin",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointPin*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointPin_createConstraints'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointPin_createConstraints'", nullptr); - return 0; - } - auto&& ret = cobj->createConstraints(); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointPin:createConstraints",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointPin_createConstraints'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointPin_construct(lua_State* tolua_S) -{ - int argc = 0; - bool ok = true; -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.PhysicsJointPin",0,&tolua_err)) goto tolua_lerror; -#endif - - argc = lua_gettop(tolua_S)-1; - - do - { - if (argc == 4) - { - ax::PhysicsBody* arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.PhysicsBody",&arg0, "ax.PhysicsJointPin:construct"); - if (!ok) { break; } - ax::PhysicsBody* arg1; - ok &= luaval_to_object(tolua_S, 3, "ax.PhysicsBody",&arg1, "ax.PhysicsJointPin:construct"); - if (!ok) { break; } - ax::Vec2 arg2; - ok &= luaval_to_vec2(tolua_S, 4, &arg2, "ax.PhysicsJointPin:construct"); - if (!ok) { break; } - ax::Vec2 arg3; - ok &= luaval_to_vec2(tolua_S, 5, &arg3, "ax.PhysicsJointPin:construct"); - if (!ok) { break; } - ax::PhysicsJointPin* ret = ax::PhysicsJointPin::construct(arg0, arg1, arg2, arg3); - object_to_luaval(tolua_S, "ax.PhysicsJointPin",(ax::PhysicsJointPin*)ret); - return 1; - } - } while (0); - ok = true; - do - { - if (argc == 3) - { - ax::PhysicsBody* arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.PhysicsBody",&arg0, "ax.PhysicsJointPin:construct"); - if (!ok) { break; } - ax::PhysicsBody* arg1; - ok &= luaval_to_object(tolua_S, 3, "ax.PhysicsBody",&arg1, "ax.PhysicsJointPin:construct"); - if (!ok) { break; } - ax::Vec2 arg2; - ok &= luaval_to_vec2(tolua_S, 4, &arg2, "ax.PhysicsJointPin:construct"); - if (!ok) { break; } - ax::PhysicsJointPin* ret = ax::PhysicsJointPin::construct(arg0, arg1, arg2); - object_to_luaval(tolua_S, "ax.PhysicsJointPin",(ax::PhysicsJointPin*)ret); - return 1; - } - } while (0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d", "ax.PhysicsJointPin:construct",argc, 3); - return 0; -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointPin_construct'.",&tolua_err); -#endif - return 0; -} -static int lua_ax_physics_PhysicsJointPin_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (PhysicsJointPin)"); - return 0; -} - -int lua_register_ax_physics_PhysicsJointPin(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.PhysicsJointPin"); - tolua_cclass(tolua_S,"PhysicsJointPin","ax.PhysicsJointPin","ax.PhysicsJoint",nullptr); - - tolua_beginmodule(tolua_S,"PhysicsJointPin"); - tolua_function(tolua_S,"createConstraints",lua_ax_physics_PhysicsJointPin_createConstraints); - tolua_function(tolua_S,"construct", lua_ax_physics_PhysicsJointPin_construct); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::PhysicsJointPin).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.PhysicsJointPin"; - g_typeCast[typeName] = "ax.PhysicsJointPin"; - return 1; -} - -int lua_ax_physics_PhysicsJointDistance_getDistance(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointDistance* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointDistance",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointDistance*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointDistance_getDistance'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointDistance_getDistance'", nullptr); - return 0; - } - auto&& ret = cobj->getDistance(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointDistance:getDistance",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointDistance_getDistance'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointDistance_setDistance(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointDistance* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointDistance",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointDistance*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointDistance_setDistance'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsJointDistance:setDistance"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointDistance_setDistance'", nullptr); - return 0; - } - cobj->setDistance(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointDistance:setDistance",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointDistance_setDistance'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointDistance_createConstraints(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointDistance* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointDistance",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointDistance*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointDistance_createConstraints'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointDistance_createConstraints'", nullptr); - return 0; - } - auto&& ret = cobj->createConstraints(); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointDistance:createConstraints",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointDistance_createConstraints'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointDistance_construct(lua_State* tolua_S) -{ - int argc = 0; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.PhysicsJointDistance",0,&tolua_err)) goto tolua_lerror; -#endif - - argc = lua_gettop(tolua_S) - 1; - - if (argc == 4) - { - ax::PhysicsBody* arg0; - ax::PhysicsBody* arg1; - ax::Vec2 arg2; - ax::Vec2 arg3; - ok &= luaval_to_object(tolua_S, 2, "ax.PhysicsBody",&arg0, "ax.PhysicsJointDistance:construct"); - ok &= luaval_to_object(tolua_S, 3, "ax.PhysicsBody",&arg1, "ax.PhysicsJointDistance:construct"); - ok &= luaval_to_vec2(tolua_S, 4, &arg2, "ax.PhysicsJointDistance:construct"); - ok &= luaval_to_vec2(tolua_S, 5, &arg3, "ax.PhysicsJointDistance:construct"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointDistance_construct'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsJointDistance::construct(arg0, arg1, arg2, arg3); - object_to_luaval(tolua_S, "ax.PhysicsJointDistance",(ax::PhysicsJointDistance*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.PhysicsJointDistance:construct",argc, 4); - return 0; -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointDistance_construct'.",&tolua_err); -#endif - return 0; -} -static int lua_ax_physics_PhysicsJointDistance_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (PhysicsJointDistance)"); - return 0; -} - -int lua_register_ax_physics_PhysicsJointDistance(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.PhysicsJointDistance"); - tolua_cclass(tolua_S,"PhysicsJointDistance","ax.PhysicsJointDistance","ax.PhysicsJoint",nullptr); - - tolua_beginmodule(tolua_S,"PhysicsJointDistance"); - tolua_function(tolua_S,"getDistance",lua_ax_physics_PhysicsJointDistance_getDistance); - tolua_function(tolua_S,"setDistance",lua_ax_physics_PhysicsJointDistance_setDistance); - tolua_function(tolua_S,"createConstraints",lua_ax_physics_PhysicsJointDistance_createConstraints); - tolua_function(tolua_S,"construct", lua_ax_physics_PhysicsJointDistance_construct); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::PhysicsJointDistance).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.PhysicsJointDistance"; - g_typeCast[typeName] = "ax.PhysicsJointDistance"; - return 1; -} - -int lua_ax_physics_PhysicsJointSpring_getAnchr1(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointSpring* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointSpring",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointSpring*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointSpring_getAnchr1'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointSpring_getAnchr1'", nullptr); - return 0; - } - auto&& ret = cobj->getAnchr1(); - vec2_to_luaval(tolua_S, ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointSpring:getAnchr1",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointSpring_getAnchr1'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointSpring_setAnchr1(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointSpring* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointSpring",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointSpring*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointSpring_setAnchr1'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - ax::Vec2 arg0; - - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsJointSpring:setAnchr1"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointSpring_setAnchr1'", nullptr); - return 0; - } - cobj->setAnchr1(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointSpring:setAnchr1",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointSpring_setAnchr1'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointSpring_getAnchr2(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointSpring* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointSpring",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointSpring*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointSpring_getAnchr2'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointSpring_getAnchr2'", nullptr); - return 0; - } - auto&& ret = cobj->getAnchr2(); - vec2_to_luaval(tolua_S, ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointSpring:getAnchr2",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointSpring_getAnchr2'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointSpring_setAnchr2(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointSpring* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointSpring",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointSpring*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointSpring_setAnchr2'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - ax::Vec2 arg0; - - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsJointSpring:setAnchr2"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointSpring_setAnchr2'", nullptr); - return 0; - } - cobj->setAnchr2(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointSpring:setAnchr2",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointSpring_setAnchr2'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointSpring_getRestLength(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointSpring* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointSpring",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointSpring*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointSpring_getRestLength'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointSpring_getRestLength'", nullptr); - return 0; - } - auto&& ret = cobj->getRestLength(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointSpring:getRestLength",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointSpring_getRestLength'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointSpring_setRestLength(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointSpring* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointSpring",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointSpring*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointSpring_setRestLength'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsJointSpring:setRestLength"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointSpring_setRestLength'", nullptr); - return 0; - } - cobj->setRestLength(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointSpring:setRestLength",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointSpring_setRestLength'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointSpring_getStiffness(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointSpring* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointSpring",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointSpring*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointSpring_getStiffness'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointSpring_getStiffness'", nullptr); - return 0; - } - auto&& ret = cobj->getStiffness(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointSpring:getStiffness",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointSpring_getStiffness'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointSpring_setStiffness(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointSpring* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointSpring",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointSpring*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointSpring_setStiffness'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsJointSpring:setStiffness"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointSpring_setStiffness'", nullptr); - return 0; - } - cobj->setStiffness(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointSpring:setStiffness",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointSpring_setStiffness'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointSpring_getDamping(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointSpring* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointSpring",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointSpring*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointSpring_getDamping'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointSpring_getDamping'", nullptr); - return 0; - } - auto&& ret = cobj->getDamping(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointSpring:getDamping",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointSpring_getDamping'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointSpring_setDamping(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointSpring* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointSpring",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointSpring*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointSpring_setDamping'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsJointSpring:setDamping"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointSpring_setDamping'", nullptr); - return 0; - } - cobj->setDamping(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointSpring:setDamping",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointSpring_setDamping'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointSpring_createConstraints(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointSpring* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointSpring",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointSpring*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointSpring_createConstraints'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointSpring_createConstraints'", nullptr); - return 0; - } - auto&& ret = cobj->createConstraints(); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointSpring:createConstraints",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointSpring_createConstraints'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointSpring_construct(lua_State* tolua_S) -{ - int argc = 0; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.PhysicsJointSpring",0,&tolua_err)) goto tolua_lerror; -#endif - - argc = lua_gettop(tolua_S) - 1; - - if (argc == 6) - { - ax::PhysicsBody* arg0; - ax::PhysicsBody* arg1; - ax::Vec2 arg2; - ax::Vec2 arg3; - double arg4; - double arg5; - ok &= luaval_to_object(tolua_S, 2, "ax.PhysicsBody",&arg0, "ax.PhysicsJointSpring:construct"); - ok &= luaval_to_object(tolua_S, 3, "ax.PhysicsBody",&arg1, "ax.PhysicsJointSpring:construct"); - ok &= luaval_to_vec2(tolua_S, 4, &arg2, "ax.PhysicsJointSpring:construct"); - ok &= luaval_to_vec2(tolua_S, 5, &arg3, "ax.PhysicsJointSpring:construct"); - ok &= luaval_to_number(tolua_S, 6,&arg4, "ax.PhysicsJointSpring:construct"); - ok &= luaval_to_number(tolua_S, 7,&arg5, "ax.PhysicsJointSpring:construct"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointSpring_construct'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsJointSpring::construct(arg0, arg1, arg2, arg3, arg4, arg5); - object_to_luaval(tolua_S, "ax.PhysicsJointSpring",(ax::PhysicsJointSpring*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.PhysicsJointSpring:construct",argc, 6); - return 0; -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointSpring_construct'.",&tolua_err); -#endif - return 0; -} -static int lua_ax_physics_PhysicsJointSpring_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (PhysicsJointSpring)"); - return 0; -} - -int lua_register_ax_physics_PhysicsJointSpring(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.PhysicsJointSpring"); - tolua_cclass(tolua_S,"PhysicsJointSpring","ax.PhysicsJointSpring","ax.PhysicsJoint",nullptr); - - tolua_beginmodule(tolua_S,"PhysicsJointSpring"); - tolua_function(tolua_S,"getAnchr1",lua_ax_physics_PhysicsJointSpring_getAnchr1); - tolua_function(tolua_S,"setAnchr1",lua_ax_physics_PhysicsJointSpring_setAnchr1); - tolua_function(tolua_S,"getAnchr2",lua_ax_physics_PhysicsJointSpring_getAnchr2); - tolua_function(tolua_S,"setAnchr2",lua_ax_physics_PhysicsJointSpring_setAnchr2); - tolua_function(tolua_S,"getRestLength",lua_ax_physics_PhysicsJointSpring_getRestLength); - tolua_function(tolua_S,"setRestLength",lua_ax_physics_PhysicsJointSpring_setRestLength); - tolua_function(tolua_S,"getStiffness",lua_ax_physics_PhysicsJointSpring_getStiffness); - tolua_function(tolua_S,"setStiffness",lua_ax_physics_PhysicsJointSpring_setStiffness); - tolua_function(tolua_S,"getDamping",lua_ax_physics_PhysicsJointSpring_getDamping); - tolua_function(tolua_S,"setDamping",lua_ax_physics_PhysicsJointSpring_setDamping); - tolua_function(tolua_S,"createConstraints",lua_ax_physics_PhysicsJointSpring_createConstraints); - tolua_function(tolua_S,"construct", lua_ax_physics_PhysicsJointSpring_construct); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::PhysicsJointSpring).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.PhysicsJointSpring"; - g_typeCast[typeName] = "ax.PhysicsJointSpring"; - return 1; -} - -int lua_ax_physics_PhysicsJointGroove_getGrooveA(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointGroove* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointGroove",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointGroove*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointGroove_getGrooveA'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointGroove_getGrooveA'", nullptr); - return 0; - } - auto&& ret = cobj->getGrooveA(); - vec2_to_luaval(tolua_S, ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointGroove:getGrooveA",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointGroove_getGrooveA'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointGroove_setGrooveA(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointGroove* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointGroove",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointGroove*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointGroove_setGrooveA'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - ax::Vec2 arg0; - - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsJointGroove:setGrooveA"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointGroove_setGrooveA'", nullptr); - return 0; - } - cobj->setGrooveA(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointGroove:setGrooveA",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointGroove_setGrooveA'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointGroove_getGrooveB(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointGroove* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointGroove",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointGroove*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointGroove_getGrooveB'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointGroove_getGrooveB'", nullptr); - return 0; - } - auto&& ret = cobj->getGrooveB(); - vec2_to_luaval(tolua_S, ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointGroove:getGrooveB",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointGroove_getGrooveB'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointGroove_setGrooveB(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointGroove* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointGroove",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointGroove*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointGroove_setGrooveB'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - ax::Vec2 arg0; - - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsJointGroove:setGrooveB"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointGroove_setGrooveB'", nullptr); - return 0; - } - cobj->setGrooveB(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointGroove:setGrooveB",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointGroove_setGrooveB'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointGroove_getAnchr2(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointGroove* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointGroove",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointGroove*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointGroove_getAnchr2'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointGroove_getAnchr2'", nullptr); - return 0; - } - auto&& ret = cobj->getAnchr2(); - vec2_to_luaval(tolua_S, ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointGroove:getAnchr2",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointGroove_getAnchr2'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointGroove_setAnchr2(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointGroove* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointGroove",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointGroove*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointGroove_setAnchr2'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - ax::Vec2 arg0; - - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsJointGroove:setAnchr2"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointGroove_setAnchr2'", nullptr); - return 0; - } - cobj->setAnchr2(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointGroove:setAnchr2",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointGroove_setAnchr2'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointGroove_createConstraints(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointGroove* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointGroove",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointGroove*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointGroove_createConstraints'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointGroove_createConstraints'", nullptr); - return 0; - } - auto&& ret = cobj->createConstraints(); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointGroove:createConstraints",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointGroove_createConstraints'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointGroove_construct(lua_State* tolua_S) -{ - int argc = 0; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.PhysicsJointGroove",0,&tolua_err)) goto tolua_lerror; -#endif - - argc = lua_gettop(tolua_S) - 1; - - if (argc == 5) - { - ax::PhysicsBody* arg0; - ax::PhysicsBody* arg1; - ax::Vec2 arg2; - ax::Vec2 arg3; - ax::Vec2 arg4; - ok &= luaval_to_object(tolua_S, 2, "ax.PhysicsBody",&arg0, "ax.PhysicsJointGroove:construct"); - ok &= luaval_to_object(tolua_S, 3, "ax.PhysicsBody",&arg1, "ax.PhysicsJointGroove:construct"); - ok &= luaval_to_vec2(tolua_S, 4, &arg2, "ax.PhysicsJointGroove:construct"); - ok &= luaval_to_vec2(tolua_S, 5, &arg3, "ax.PhysicsJointGroove:construct"); - ok &= luaval_to_vec2(tolua_S, 6, &arg4, "ax.PhysicsJointGroove:construct"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointGroove_construct'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsJointGroove::construct(arg0, arg1, arg2, arg3, arg4); - object_to_luaval(tolua_S, "ax.PhysicsJointGroove",(ax::PhysicsJointGroove*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.PhysicsJointGroove:construct",argc, 5); - return 0; -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointGroove_construct'.",&tolua_err); -#endif - return 0; -} -static int lua_ax_physics_PhysicsJointGroove_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (PhysicsJointGroove)"); - return 0; -} - -int lua_register_ax_physics_PhysicsJointGroove(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.PhysicsJointGroove"); - tolua_cclass(tolua_S,"PhysicsJointGroove","ax.PhysicsJointGroove","ax.PhysicsJoint",nullptr); - - tolua_beginmodule(tolua_S,"PhysicsJointGroove"); - tolua_function(tolua_S,"getGrooveA",lua_ax_physics_PhysicsJointGroove_getGrooveA); - tolua_function(tolua_S,"setGrooveA",lua_ax_physics_PhysicsJointGroove_setGrooveA); - tolua_function(tolua_S,"getGrooveB",lua_ax_physics_PhysicsJointGroove_getGrooveB); - tolua_function(tolua_S,"setGrooveB",lua_ax_physics_PhysicsJointGroove_setGrooveB); - tolua_function(tolua_S,"getAnchr2",lua_ax_physics_PhysicsJointGroove_getAnchr2); - tolua_function(tolua_S,"setAnchr2",lua_ax_physics_PhysicsJointGroove_setAnchr2); - tolua_function(tolua_S,"createConstraints",lua_ax_physics_PhysicsJointGroove_createConstraints); - tolua_function(tolua_S,"construct", lua_ax_physics_PhysicsJointGroove_construct); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::PhysicsJointGroove).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.PhysicsJointGroove"; - g_typeCast[typeName] = "ax.PhysicsJointGroove"; - return 1; -} - -int lua_ax_physics_PhysicsJointRotarySpring_getRestAngle(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointRotarySpring* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointRotarySpring",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointRotarySpring*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointRotarySpring_getRestAngle'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointRotarySpring_getRestAngle'", nullptr); - return 0; - } - auto&& ret = cobj->getRestAngle(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointRotarySpring:getRestAngle",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointRotarySpring_getRestAngle'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointRotarySpring_setRestAngle(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointRotarySpring* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointRotarySpring",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointRotarySpring*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointRotarySpring_setRestAngle'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsJointRotarySpring:setRestAngle"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointRotarySpring_setRestAngle'", nullptr); - return 0; - } - cobj->setRestAngle(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointRotarySpring:setRestAngle",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointRotarySpring_setRestAngle'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointRotarySpring_getStiffness(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointRotarySpring* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointRotarySpring",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointRotarySpring*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointRotarySpring_getStiffness'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointRotarySpring_getStiffness'", nullptr); - return 0; - } - auto&& ret = cobj->getStiffness(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointRotarySpring:getStiffness",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointRotarySpring_getStiffness'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointRotarySpring_setStiffness(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointRotarySpring* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointRotarySpring",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointRotarySpring*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointRotarySpring_setStiffness'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsJointRotarySpring:setStiffness"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointRotarySpring_setStiffness'", nullptr); - return 0; - } - cobj->setStiffness(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointRotarySpring:setStiffness",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointRotarySpring_setStiffness'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointRotarySpring_getDamping(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointRotarySpring* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointRotarySpring",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointRotarySpring*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointRotarySpring_getDamping'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointRotarySpring_getDamping'", nullptr); - return 0; - } - auto&& ret = cobj->getDamping(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointRotarySpring:getDamping",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointRotarySpring_getDamping'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointRotarySpring_setDamping(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointRotarySpring* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointRotarySpring",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointRotarySpring*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointRotarySpring_setDamping'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsJointRotarySpring:setDamping"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointRotarySpring_setDamping'", nullptr); - return 0; - } - cobj->setDamping(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointRotarySpring:setDamping",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointRotarySpring_setDamping'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointRotarySpring_createConstraints(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointRotarySpring* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointRotarySpring",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointRotarySpring*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointRotarySpring_createConstraints'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointRotarySpring_createConstraints'", nullptr); - return 0; - } - auto&& ret = cobj->createConstraints(); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointRotarySpring:createConstraints",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointRotarySpring_createConstraints'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointRotarySpring_construct(lua_State* tolua_S) -{ - int argc = 0; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.PhysicsJointRotarySpring",0,&tolua_err)) goto tolua_lerror; -#endif - - argc = lua_gettop(tolua_S) - 1; - - if (argc == 4) - { - ax::PhysicsBody* arg0; - ax::PhysicsBody* arg1; - double arg2; - double arg3; - ok &= luaval_to_object(tolua_S, 2, "ax.PhysicsBody",&arg0, "ax.PhysicsJointRotarySpring:construct"); - ok &= luaval_to_object(tolua_S, 3, "ax.PhysicsBody",&arg1, "ax.PhysicsJointRotarySpring:construct"); - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.PhysicsJointRotarySpring:construct"); - ok &= luaval_to_number(tolua_S, 5,&arg3, "ax.PhysicsJointRotarySpring:construct"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointRotarySpring_construct'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsJointRotarySpring::construct(arg0, arg1, arg2, arg3); - object_to_luaval(tolua_S, "ax.PhysicsJointRotarySpring",(ax::PhysicsJointRotarySpring*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.PhysicsJointRotarySpring:construct",argc, 4); - return 0; -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointRotarySpring_construct'.",&tolua_err); -#endif - return 0; -} -static int lua_ax_physics_PhysicsJointRotarySpring_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (PhysicsJointRotarySpring)"); - return 0; -} - -int lua_register_ax_physics_PhysicsJointRotarySpring(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.PhysicsJointRotarySpring"); - tolua_cclass(tolua_S,"PhysicsJointRotarySpring","ax.PhysicsJointRotarySpring","ax.PhysicsJoint",nullptr); - - tolua_beginmodule(tolua_S,"PhysicsJointRotarySpring"); - tolua_function(tolua_S,"getRestAngle",lua_ax_physics_PhysicsJointRotarySpring_getRestAngle); - tolua_function(tolua_S,"setRestAngle",lua_ax_physics_PhysicsJointRotarySpring_setRestAngle); - tolua_function(tolua_S,"getStiffness",lua_ax_physics_PhysicsJointRotarySpring_getStiffness); - tolua_function(tolua_S,"setStiffness",lua_ax_physics_PhysicsJointRotarySpring_setStiffness); - tolua_function(tolua_S,"getDamping",lua_ax_physics_PhysicsJointRotarySpring_getDamping); - tolua_function(tolua_S,"setDamping",lua_ax_physics_PhysicsJointRotarySpring_setDamping); - tolua_function(tolua_S,"createConstraints",lua_ax_physics_PhysicsJointRotarySpring_createConstraints); - tolua_function(tolua_S,"construct", lua_ax_physics_PhysicsJointRotarySpring_construct); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::PhysicsJointRotarySpring).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.PhysicsJointRotarySpring"; - g_typeCast[typeName] = "ax.PhysicsJointRotarySpring"; - return 1; -} - -int lua_ax_physics_PhysicsJointRotaryLimit_getMin(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointRotaryLimit* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointRotaryLimit",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointRotaryLimit*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointRotaryLimit_getMin'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointRotaryLimit_getMin'", nullptr); - return 0; - } - auto&& ret = cobj->getMin(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointRotaryLimit:getMin",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointRotaryLimit_getMin'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointRotaryLimit_setMin(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointRotaryLimit* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointRotaryLimit",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointRotaryLimit*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointRotaryLimit_setMin'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsJointRotaryLimit:setMin"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointRotaryLimit_setMin'", nullptr); - return 0; - } - cobj->setMin(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointRotaryLimit:setMin",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointRotaryLimit_setMin'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointRotaryLimit_getMax(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointRotaryLimit* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointRotaryLimit",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointRotaryLimit*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointRotaryLimit_getMax'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointRotaryLimit_getMax'", nullptr); - return 0; - } - auto&& ret = cobj->getMax(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointRotaryLimit:getMax",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointRotaryLimit_getMax'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointRotaryLimit_setMax(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointRotaryLimit* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointRotaryLimit",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointRotaryLimit*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointRotaryLimit_setMax'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsJointRotaryLimit:setMax"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointRotaryLimit_setMax'", nullptr); - return 0; - } - cobj->setMax(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointRotaryLimit:setMax",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointRotaryLimit_setMax'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointRotaryLimit_createConstraints(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointRotaryLimit* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointRotaryLimit",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointRotaryLimit*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointRotaryLimit_createConstraints'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointRotaryLimit_createConstraints'", nullptr); - return 0; - } - auto&& ret = cobj->createConstraints(); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointRotaryLimit:createConstraints",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointRotaryLimit_createConstraints'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointRotaryLimit_construct(lua_State* tolua_S) -{ - int argc = 0; - bool ok = true; -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.PhysicsJointRotaryLimit",0,&tolua_err)) goto tolua_lerror; -#endif - - argc = lua_gettop(tolua_S)-1; - - do - { - if (argc == 2) - { - ax::PhysicsBody* arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.PhysicsBody",&arg0, "ax.PhysicsJointRotaryLimit:construct"); - if (!ok) { break; } - ax::PhysicsBody* arg1; - ok &= luaval_to_object(tolua_S, 3, "ax.PhysicsBody",&arg1, "ax.PhysicsJointRotaryLimit:construct"); - if (!ok) { break; } - ax::PhysicsJointRotaryLimit* ret = ax::PhysicsJointRotaryLimit::construct(arg0, arg1); - object_to_luaval(tolua_S, "ax.PhysicsJointRotaryLimit",(ax::PhysicsJointRotaryLimit*)ret); - return 1; - } - } while (0); - ok = true; - do - { - if (argc == 4) - { - ax::PhysicsBody* arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.PhysicsBody",&arg0, "ax.PhysicsJointRotaryLimit:construct"); - if (!ok) { break; } - ax::PhysicsBody* arg1; - ok &= luaval_to_object(tolua_S, 3, "ax.PhysicsBody",&arg1, "ax.PhysicsJointRotaryLimit:construct"); - if (!ok) { break; } - double arg2; - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.PhysicsJointRotaryLimit:construct"); - if (!ok) { break; } - double arg3; - ok &= luaval_to_number(tolua_S, 5,&arg3, "ax.PhysicsJointRotaryLimit:construct"); - if (!ok) { break; } - ax::PhysicsJointRotaryLimit* ret = ax::PhysicsJointRotaryLimit::construct(arg0, arg1, arg2, arg3); - object_to_luaval(tolua_S, "ax.PhysicsJointRotaryLimit",(ax::PhysicsJointRotaryLimit*)ret); - return 1; - } - } while (0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d", "ax.PhysicsJointRotaryLimit:construct",argc, 4); - return 0; -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointRotaryLimit_construct'.",&tolua_err); -#endif - return 0; -} -static int lua_ax_physics_PhysicsJointRotaryLimit_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (PhysicsJointRotaryLimit)"); - return 0; -} - -int lua_register_ax_physics_PhysicsJointRotaryLimit(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.PhysicsJointRotaryLimit"); - tolua_cclass(tolua_S,"PhysicsJointRotaryLimit","ax.PhysicsJointRotaryLimit","ax.PhysicsJoint",nullptr); - - tolua_beginmodule(tolua_S,"PhysicsJointRotaryLimit"); - tolua_function(tolua_S,"getMin",lua_ax_physics_PhysicsJointRotaryLimit_getMin); - tolua_function(tolua_S,"setMin",lua_ax_physics_PhysicsJointRotaryLimit_setMin); - tolua_function(tolua_S,"getMax",lua_ax_physics_PhysicsJointRotaryLimit_getMax); - tolua_function(tolua_S,"setMax",lua_ax_physics_PhysicsJointRotaryLimit_setMax); - tolua_function(tolua_S,"createConstraints",lua_ax_physics_PhysicsJointRotaryLimit_createConstraints); - tolua_function(tolua_S,"construct", lua_ax_physics_PhysicsJointRotaryLimit_construct); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::PhysicsJointRotaryLimit).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.PhysicsJointRotaryLimit"; - g_typeCast[typeName] = "ax.PhysicsJointRotaryLimit"; - return 1; -} - -int lua_ax_physics_PhysicsJointRatchet_getAngle(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointRatchet* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointRatchet",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointRatchet*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointRatchet_getAngle'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointRatchet_getAngle'", nullptr); - return 0; - } - auto&& ret = cobj->getAngle(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointRatchet:getAngle",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointRatchet_getAngle'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointRatchet_setAngle(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointRatchet* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointRatchet",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointRatchet*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointRatchet_setAngle'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsJointRatchet:setAngle"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointRatchet_setAngle'", nullptr); - return 0; - } - cobj->setAngle(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointRatchet:setAngle",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointRatchet_setAngle'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointRatchet_getPhase(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointRatchet* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointRatchet",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointRatchet*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointRatchet_getPhase'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointRatchet_getPhase'", nullptr); - return 0; - } - auto&& ret = cobj->getPhase(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointRatchet:getPhase",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointRatchet_getPhase'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointRatchet_setPhase(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointRatchet* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointRatchet",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointRatchet*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointRatchet_setPhase'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsJointRatchet:setPhase"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointRatchet_setPhase'", nullptr); - return 0; - } - cobj->setPhase(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointRatchet:setPhase",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointRatchet_setPhase'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointRatchet_getRatchet(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointRatchet* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointRatchet",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointRatchet*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointRatchet_getRatchet'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointRatchet_getRatchet'", nullptr); - return 0; - } - auto&& ret = cobj->getRatchet(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointRatchet:getRatchet",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointRatchet_getRatchet'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointRatchet_setRatchet(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointRatchet* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointRatchet",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointRatchet*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointRatchet_setRatchet'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsJointRatchet:setRatchet"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointRatchet_setRatchet'", nullptr); - return 0; - } - cobj->setRatchet(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointRatchet:setRatchet",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointRatchet_setRatchet'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointRatchet_createConstraints(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointRatchet* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointRatchet",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointRatchet*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointRatchet_createConstraints'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointRatchet_createConstraints'", nullptr); - return 0; - } - auto&& ret = cobj->createConstraints(); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointRatchet:createConstraints",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointRatchet_createConstraints'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointRatchet_construct(lua_State* tolua_S) -{ - int argc = 0; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.PhysicsJointRatchet",0,&tolua_err)) goto tolua_lerror; -#endif - - argc = lua_gettop(tolua_S) - 1; - - if (argc == 4) - { - ax::PhysicsBody* arg0; - ax::PhysicsBody* arg1; - double arg2; - double arg3; - ok &= luaval_to_object(tolua_S, 2, "ax.PhysicsBody",&arg0, "ax.PhysicsJointRatchet:construct"); - ok &= luaval_to_object(tolua_S, 3, "ax.PhysicsBody",&arg1, "ax.PhysicsJointRatchet:construct"); - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.PhysicsJointRatchet:construct"); - ok &= luaval_to_number(tolua_S, 5,&arg3, "ax.PhysicsJointRatchet:construct"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointRatchet_construct'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsJointRatchet::construct(arg0, arg1, arg2, arg3); - object_to_luaval(tolua_S, "ax.PhysicsJointRatchet",(ax::PhysicsJointRatchet*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.PhysicsJointRatchet:construct",argc, 4); - return 0; -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointRatchet_construct'.",&tolua_err); -#endif - return 0; -} -static int lua_ax_physics_PhysicsJointRatchet_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (PhysicsJointRatchet)"); - return 0; -} - -int lua_register_ax_physics_PhysicsJointRatchet(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.PhysicsJointRatchet"); - tolua_cclass(tolua_S,"PhysicsJointRatchet","ax.PhysicsJointRatchet","ax.PhysicsJoint",nullptr); - - tolua_beginmodule(tolua_S,"PhysicsJointRatchet"); - tolua_function(tolua_S,"getAngle",lua_ax_physics_PhysicsJointRatchet_getAngle); - tolua_function(tolua_S,"setAngle",lua_ax_physics_PhysicsJointRatchet_setAngle); - tolua_function(tolua_S,"getPhase",lua_ax_physics_PhysicsJointRatchet_getPhase); - tolua_function(tolua_S,"setPhase",lua_ax_physics_PhysicsJointRatchet_setPhase); - tolua_function(tolua_S,"getRatchet",lua_ax_physics_PhysicsJointRatchet_getRatchet); - tolua_function(tolua_S,"setRatchet",lua_ax_physics_PhysicsJointRatchet_setRatchet); - tolua_function(tolua_S,"createConstraints",lua_ax_physics_PhysicsJointRatchet_createConstraints); - tolua_function(tolua_S,"construct", lua_ax_physics_PhysicsJointRatchet_construct); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::PhysicsJointRatchet).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.PhysicsJointRatchet"; - g_typeCast[typeName] = "ax.PhysicsJointRatchet"; - return 1; -} - -int lua_ax_physics_PhysicsJointGear_getPhase(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointGear* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointGear",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointGear*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointGear_getPhase'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointGear_getPhase'", nullptr); - return 0; - } - auto&& ret = cobj->getPhase(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointGear:getPhase",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointGear_getPhase'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointGear_setPhase(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointGear* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointGear",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointGear*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointGear_setPhase'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsJointGear:setPhase"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointGear_setPhase'", nullptr); - return 0; - } - cobj->setPhase(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointGear:setPhase",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointGear_setPhase'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointGear_getRatio(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointGear* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointGear",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointGear*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointGear_getRatio'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointGear_getRatio'", nullptr); - return 0; - } - auto&& ret = cobj->getRatio(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointGear:getRatio",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointGear_getRatio'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointGear_setRatio(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointGear* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointGear",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointGear*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointGear_setRatio'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsJointGear:setRatio"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointGear_setRatio'", nullptr); - return 0; - } - cobj->setRatio(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointGear:setRatio",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointGear_setRatio'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointGear_createConstraints(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointGear* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointGear",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointGear*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointGear_createConstraints'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointGear_createConstraints'", nullptr); - return 0; - } - auto&& ret = cobj->createConstraints(); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointGear:createConstraints",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointGear_createConstraints'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointGear_construct(lua_State* tolua_S) -{ - int argc = 0; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.PhysicsJointGear",0,&tolua_err)) goto tolua_lerror; -#endif - - argc = lua_gettop(tolua_S) - 1; - - if (argc == 4) - { - ax::PhysicsBody* arg0; - ax::PhysicsBody* arg1; - double arg2; - double arg3; - ok &= luaval_to_object(tolua_S, 2, "ax.PhysicsBody",&arg0, "ax.PhysicsJointGear:construct"); - ok &= luaval_to_object(tolua_S, 3, "ax.PhysicsBody",&arg1, "ax.PhysicsJointGear:construct"); - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.PhysicsJointGear:construct"); - ok &= luaval_to_number(tolua_S, 5,&arg3, "ax.PhysicsJointGear:construct"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointGear_construct'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsJointGear::construct(arg0, arg1, arg2, arg3); - object_to_luaval(tolua_S, "ax.PhysicsJointGear",(ax::PhysicsJointGear*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.PhysicsJointGear:construct",argc, 4); - return 0; -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointGear_construct'.",&tolua_err); -#endif - return 0; -} -static int lua_ax_physics_PhysicsJointGear_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (PhysicsJointGear)"); - return 0; -} - -int lua_register_ax_physics_PhysicsJointGear(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.PhysicsJointGear"); - tolua_cclass(tolua_S,"PhysicsJointGear","ax.PhysicsJointGear","ax.PhysicsJoint",nullptr); - - tolua_beginmodule(tolua_S,"PhysicsJointGear"); - tolua_function(tolua_S,"getPhase",lua_ax_physics_PhysicsJointGear_getPhase); - tolua_function(tolua_S,"setPhase",lua_ax_physics_PhysicsJointGear_setPhase); - tolua_function(tolua_S,"getRatio",lua_ax_physics_PhysicsJointGear_getRatio); - tolua_function(tolua_S,"setRatio",lua_ax_physics_PhysicsJointGear_setRatio); - tolua_function(tolua_S,"createConstraints",lua_ax_physics_PhysicsJointGear_createConstraints); - tolua_function(tolua_S,"construct", lua_ax_physics_PhysicsJointGear_construct); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::PhysicsJointGear).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.PhysicsJointGear"; - g_typeCast[typeName] = "ax.PhysicsJointGear"; - return 1; -} - -int lua_ax_physics_PhysicsJointMotor_getRate(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointMotor* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointMotor",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointMotor*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointMotor_getRate'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointMotor_getRate'", nullptr); - return 0; - } - auto&& ret = cobj->getRate(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointMotor:getRate",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointMotor_getRate'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointMotor_setRate(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointMotor* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointMotor",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointMotor*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointMotor_setRate'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsJointMotor:setRate"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointMotor_setRate'", nullptr); - return 0; - } - cobj->setRate(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointMotor:setRate",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointMotor_setRate'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointMotor_createConstraints(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsJointMotor* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsJointMotor",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsJointMotor*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsJointMotor_createConstraints'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointMotor_createConstraints'", nullptr); - return 0; - } - auto&& ret = cobj->createConstraints(); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsJointMotor:createConstraints",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointMotor_createConstraints'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsJointMotor_construct(lua_State* tolua_S) -{ - int argc = 0; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertable(tolua_S,1,"ax.PhysicsJointMotor",0,&tolua_err)) goto tolua_lerror; -#endif - - argc = lua_gettop(tolua_S) - 1; - - if (argc == 3) - { - ax::PhysicsBody* arg0; - ax::PhysicsBody* arg1; - double arg2; - ok &= luaval_to_object(tolua_S, 2, "ax.PhysicsBody",&arg0, "ax.PhysicsJointMotor:construct"); - ok &= luaval_to_object(tolua_S, 3, "ax.PhysicsBody",&arg1, "ax.PhysicsJointMotor:construct"); - ok &= luaval_to_number(tolua_S, 4,&arg2, "ax.PhysicsJointMotor:construct"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsJointMotor_construct'", nullptr); - return 0; - } - auto&& ret = ax::PhysicsJointMotor::construct(arg0, arg1, arg2); - object_to_luaval(tolua_S, "ax.PhysicsJointMotor",(ax::PhysicsJointMotor*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d\n ", "ax.PhysicsJointMotor:construct",argc, 3); - return 0; -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsJointMotor_construct'.",&tolua_err); -#endif - return 0; -} -static int lua_ax_physics_PhysicsJointMotor_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (PhysicsJointMotor)"); - return 0; -} - -int lua_register_ax_physics_PhysicsJointMotor(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.PhysicsJointMotor"); - tolua_cclass(tolua_S,"PhysicsJointMotor","ax.PhysicsJointMotor","ax.PhysicsJoint",nullptr); - - tolua_beginmodule(tolua_S,"PhysicsJointMotor"); - tolua_function(tolua_S,"getRate",lua_ax_physics_PhysicsJointMotor_getRate); - tolua_function(tolua_S,"setRate",lua_ax_physics_PhysicsJointMotor_setRate); - tolua_function(tolua_S,"createConstraints",lua_ax_physics_PhysicsJointMotor_createConstraints); - tolua_function(tolua_S,"construct", lua_ax_physics_PhysicsJointMotor_construct); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::PhysicsJointMotor).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.PhysicsJointMotor"; - g_typeCast[typeName] = "ax.PhysicsJointMotor"; - return 1; -} - -int lua_ax_physics_PhysicsWorld_addJoint(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsWorld* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsWorld",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsWorld*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsWorld_addJoint'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - ax::PhysicsJoint* arg0; - - ok &= luaval_to_object(tolua_S, 2, "ax.PhysicsJoint",&arg0, "ax.PhysicsWorld:addJoint"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsWorld_addJoint'", nullptr); - return 0; - } - cobj->addJoint(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsWorld:addJoint",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsWorld_addJoint'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsWorld_removeJoint(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsWorld* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsWorld",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsWorld*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsWorld_removeJoint'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - ax::PhysicsJoint* arg0; - - ok &= luaval_to_object(tolua_S, 2, "ax.PhysicsJoint",&arg0, "ax.PhysicsWorld:removeJoint"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsWorld_removeJoint'", nullptr); - return 0; - } - cobj->removeJoint(arg0); - lua_settop(tolua_S, 1); - return 1; - } - if (argc == 2) - { - ax::PhysicsJoint* arg0; - bool arg1; - - ok &= luaval_to_object(tolua_S, 2, "ax.PhysicsJoint",&arg0, "ax.PhysicsWorld:removeJoint"); - - ok &= luaval_to_boolean(tolua_S, 3,&arg1, "ax.PhysicsWorld:removeJoint"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsWorld_removeJoint'", nullptr); - return 0; - } - cobj->removeJoint(arg0, arg1); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsWorld:removeJoint",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsWorld_removeJoint'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsWorld_removeAllJoints(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsWorld* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsWorld",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsWorld*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsWorld_removeAllJoints'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsWorld_removeAllJoints'", nullptr); - return 0; - } - cobj->removeAllJoints(); - lua_settop(tolua_S, 1); - return 1; - } - if (argc == 1) - { - bool arg0; - - ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.PhysicsWorld:removeAllJoints"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsWorld_removeAllJoints'", nullptr); - return 0; - } - cobj->removeAllJoints(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsWorld:removeAllJoints",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsWorld_removeAllJoints'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsWorld_removeBody(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsWorld* cobj = nullptr; - bool ok = true; -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsWorld",0,&tolua_err)) goto tolua_lerror; -#endif - cobj = (ax::PhysicsWorld*)tolua_tousertype(tolua_S,1,0); -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsWorld_removeBody'", nullptr); - return 0; - } -#endif - argc = lua_gettop(tolua_S)-1; - do{ - if (argc == 1) { - int arg0; - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.PhysicsWorld:removeBody"); - - if (!ok) { break; } - cobj->removeBody(arg0); - lua_settop(tolua_S, 1); - return 1; - } - }while(0); - ok = true; - do{ - if (argc == 1) { - ax::PhysicsBody* arg0; - ok &= luaval_to_object(tolua_S, 2, "ax.PhysicsBody",&arg0, "ax.PhysicsWorld:removeBody"); - - if (!ok) { break; } - cobj->removeBody(arg0); - lua_settop(tolua_S, 1); - return 1; - } - }while(0); - ok = true; - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsWorld:removeBody",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsWorld_removeBody'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsWorld_removeAllBodies(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsWorld* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsWorld",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsWorld*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsWorld_removeAllBodies'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsWorld_removeAllBodies'", nullptr); - return 0; - } - cobj->removeAllBodies(); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsWorld:removeAllBodies",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsWorld_removeAllBodies'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsWorld_getShapes(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsWorld* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsWorld",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsWorld*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsWorld_getShapes'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - ax::Vec2 arg0; - - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsWorld:getShapes"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsWorld_getShapes'", nullptr); - return 0; - } - auto&& ret = cobj->getShapes(arg0); - ccvector_to_luaval(tolua_S, ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsWorld:getShapes",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsWorld_getShapes'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsWorld_getShape(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsWorld* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsWorld",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsWorld*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsWorld_getShape'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - ax::Vec2 arg0; - - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsWorld:getShape"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsWorld_getShape'", nullptr); - return 0; - } - auto&& ret = cobj->getShape(arg0); - object_to_luaval(tolua_S, "ax.PhysicsShape",(ax::PhysicsShape*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsWorld:getShape",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsWorld_getShape'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsWorld_getAllBodies(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsWorld* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsWorld",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsWorld*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsWorld_getAllBodies'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsWorld_getAllBodies'", nullptr); - return 0; - } - auto&& ret = cobj->getAllBodies(); - ccvector_to_luaval(tolua_S, ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsWorld:getAllBodies",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsWorld_getAllBodies'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsWorld_getBody(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsWorld* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsWorld",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsWorld*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsWorld_getBody'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - int arg0; - - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.PhysicsWorld:getBody"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsWorld_getBody'", nullptr); - return 0; - } - auto&& ret = cobj->getBody(arg0); - object_to_luaval(tolua_S, "ax.PhysicsBody",(ax::PhysicsBody*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsWorld:getBody",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsWorld_getBody'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsWorld_getGravity(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsWorld* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsWorld",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsWorld*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsWorld_getGravity'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsWorld_getGravity'", nullptr); - return 0; - } - auto&& ret = cobj->getGravity(); - vec2_to_luaval(tolua_S, ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsWorld:getGravity",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsWorld_getGravity'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsWorld_setGravity(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsWorld* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsWorld",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsWorld*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsWorld_setGravity'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - ax::Vec2 arg0; - - ok &= luaval_to_vec2(tolua_S, 2, &arg0, "ax.PhysicsWorld:setGravity"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsWorld_setGravity'", nullptr); - return 0; - } - cobj->setGravity(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsWorld:setGravity",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsWorld_setGravity'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsWorld_setSlopBias(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsWorld* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsWorld",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsWorld*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsWorld_setSlopBias'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 2) - { - double arg0; - double arg1; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsWorld:setSlopBias"); - - ok &= luaval_to_number(tolua_S, 3,&arg1, "ax.PhysicsWorld:setSlopBias"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsWorld_setSlopBias'", nullptr); - return 0; - } - cobj->setSlopBias(arg0, arg1); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsWorld:setSlopBias",argc, 2); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsWorld_setSlopBias'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsWorld_setSpeed(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsWorld* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsWorld",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsWorld*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsWorld_setSpeed'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsWorld:setSpeed"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsWorld_setSpeed'", nullptr); - return 0; - } - cobj->setSpeed(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsWorld:setSpeed",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsWorld_setSpeed'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsWorld_getSpeed(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsWorld* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsWorld",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsWorld*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsWorld_getSpeed'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsWorld_getSpeed'", nullptr); - return 0; - } - auto&& ret = cobj->getSpeed(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsWorld:getSpeed",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsWorld_getSpeed'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsWorld_setUpdateRate(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsWorld* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsWorld",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsWorld*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsWorld_setUpdateRate'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - int arg0; - - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.PhysicsWorld:setUpdateRate"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsWorld_setUpdateRate'", nullptr); - return 0; - } - cobj->setUpdateRate(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsWorld:setUpdateRate",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsWorld_setUpdateRate'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsWorld_getUpdateRate(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsWorld* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsWorld",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsWorld*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsWorld_getUpdateRate'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsWorld_getUpdateRate'", nullptr); - return 0; - } - auto&& ret = cobj->getUpdateRate(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsWorld:getUpdateRate",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsWorld_getUpdateRate'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsWorld_setSubsteps(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsWorld* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsWorld",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsWorld*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsWorld_setSubsteps'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - int arg0; - - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.PhysicsWorld:setSubsteps"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsWorld_setSubsteps'", nullptr); - return 0; - } - cobj->setSubsteps(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsWorld:setSubsteps",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsWorld_setSubsteps'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsWorld_getSubsteps(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsWorld* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsWorld",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsWorld*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsWorld_getSubsteps'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsWorld_getSubsteps'", nullptr); - return 0; - } - auto&& ret = cobj->getSubsteps(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsWorld:getSubsteps",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsWorld_getSubsteps'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsWorld_setFixedUpdateRate(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsWorld* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsWorld",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsWorld*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsWorld_setFixedUpdateRate'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - int arg0; - - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.PhysicsWorld:setFixedUpdateRate"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsWorld_setFixedUpdateRate'", nullptr); - return 0; - } - cobj->setFixedUpdateRate(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsWorld:setFixedUpdateRate",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsWorld_setFixedUpdateRate'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsWorld_getFixedUpdateRate(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsWorld* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsWorld",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsWorld*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsWorld_getFixedUpdateRate'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsWorld_getFixedUpdateRate'", nullptr); - return 0; - } - auto&& ret = cobj->getFixedUpdateRate(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsWorld:getFixedUpdateRate",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsWorld_getFixedUpdateRate'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsWorld_setDebugDrawMask(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsWorld* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsWorld",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsWorld*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsWorld_setDebugDrawMask'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - int arg0; - - ok &= luaval_to_int32(tolua_S, 2,(int *)&arg0, "ax.PhysicsWorld:setDebugDrawMask"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsWorld_setDebugDrawMask'", nullptr); - return 0; - } - cobj->setDebugDrawMask(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsWorld:setDebugDrawMask",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsWorld_setDebugDrawMask'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsWorld_setPreUpdateCallback(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsWorld* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsWorld",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsWorld*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsWorld_setPreUpdateCallback'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - std::function arg0; - - do { - // Lambda binding for lua is not supported. - assert(false); - } while(0) - ; - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsWorld_setPreUpdateCallback'", nullptr); - return 0; - } - cobj->setPreUpdateCallback(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsWorld:setPreUpdateCallback",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsWorld_setPreUpdateCallback'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsWorld_setPostUpdateCallback(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsWorld* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsWorld",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsWorld*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsWorld_setPostUpdateCallback'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - std::function arg0; - - do { - // Lambda binding for lua is not supported. - assert(false); - } while(0) - ; - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsWorld_setPostUpdateCallback'", nullptr); - return 0; - } - cobj->setPostUpdateCallback(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsWorld:setPostUpdateCallback",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsWorld_setPostUpdateCallback'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsWorld_getDebugDrawMask(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsWorld* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsWorld",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsWorld*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsWorld_getDebugDrawMask'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsWorld_getDebugDrawMask'", nullptr); - return 0; - } - auto&& ret = cobj->getDebugDrawMask(); - tolua_pushnumber(tolua_S,(lua_Number)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsWorld:getDebugDrawMask",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsWorld_getDebugDrawMask'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsWorld_getDebugDraw(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsWorld* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsWorld",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsWorld*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsWorld_getDebugDraw'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsWorld_getDebugDraw'", nullptr); - return 0; - } - auto&& ret = cobj->getDebugDraw(); - object_to_luaval(tolua_S, "ax.DrawNode",(ax::DrawNode*)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsWorld:getDebugDraw",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsWorld_getDebugDraw'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsWorld_setAutoStep(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsWorld* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsWorld",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsWorld*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsWorld_setAutoStep'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - bool arg0; - - ok &= luaval_to_boolean(tolua_S, 2,&arg0, "ax.PhysicsWorld:setAutoStep"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsWorld_setAutoStep'", nullptr); - return 0; - } - cobj->setAutoStep(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsWorld:setAutoStep",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsWorld_setAutoStep'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsWorld_isAutoStep(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsWorld* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsWorld",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsWorld*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsWorld_isAutoStep'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 0) - { - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsWorld_isAutoStep'", nullptr); - return 0; - } - auto&& ret = cobj->isAutoStep(); - tolua_pushboolean(tolua_S,(bool)ret); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsWorld:isAutoStep",argc, 0); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsWorld_isAutoStep'.",&tolua_err); -#endif - - return 0; -} -int lua_ax_physics_PhysicsWorld_step(lua_State* tolua_S) -{ - int argc = 0; - ax::PhysicsWorld* cobj = nullptr; - bool ok = true; - -#if _AX_DEBUG >= 1 - tolua_Error tolua_err; -#endif - - -#if _AX_DEBUG >= 1 - if (!tolua_isusertype(tolua_S,1,"ax.PhysicsWorld",0,&tolua_err)) goto tolua_lerror; -#endif - - cobj = (ax::PhysicsWorld*)tolua_tousertype(tolua_S,1,0); - -#if _AX_DEBUG >= 1 - if (!cobj) - { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_physics_PhysicsWorld_step'", nullptr); - return 0; - } -#endif - - argc = lua_gettop(tolua_S)-1; - if (argc == 1) - { - double arg0; - - ok &= luaval_to_number(tolua_S, 2,&arg0, "ax.PhysicsWorld:step"); - if(!ok) - { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_physics_PhysicsWorld_step'", nullptr); - return 0; - } - cobj->step(arg0); - lua_settop(tolua_S, 1); - return 1; - } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ax.PhysicsWorld:step",argc, 1); - return 0; - -#if _AX_DEBUG >= 1 - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_physics_PhysicsWorld_step'.",&tolua_err); -#endif - - return 0; -} -static int lua_ax_physics_PhysicsWorld_finalize(lua_State* tolua_S) -{ - AXLOGV("luabindings: finalizing LUA object (PhysicsWorld)"); - return 0; -} - -int lua_register_ax_physics_PhysicsWorld(lua_State* tolua_S) -{ - tolua_usertype(tolua_S,"ax.PhysicsWorld"); - tolua_cclass(tolua_S,"PhysicsWorld","ax.PhysicsWorld","",nullptr); - - tolua_beginmodule(tolua_S,"PhysicsWorld"); - tolua_function(tolua_S,"addJoint",lua_ax_physics_PhysicsWorld_addJoint); - tolua_function(tolua_S,"removeJoint",lua_ax_physics_PhysicsWorld_removeJoint); - tolua_function(tolua_S,"removeAllJoints",lua_ax_physics_PhysicsWorld_removeAllJoints); - tolua_function(tolua_S,"removeBody",lua_ax_physics_PhysicsWorld_removeBody); - tolua_function(tolua_S,"removeAllBodies",lua_ax_physics_PhysicsWorld_removeAllBodies); - tolua_function(tolua_S,"getShapes",lua_ax_physics_PhysicsWorld_getShapes); - tolua_function(tolua_S,"getShape",lua_ax_physics_PhysicsWorld_getShape); - tolua_function(tolua_S,"getAllBodies",lua_ax_physics_PhysicsWorld_getAllBodies); - tolua_function(tolua_S,"getBody",lua_ax_physics_PhysicsWorld_getBody); - tolua_function(tolua_S,"getGravity",lua_ax_physics_PhysicsWorld_getGravity); - tolua_function(tolua_S,"setGravity",lua_ax_physics_PhysicsWorld_setGravity); - tolua_function(tolua_S,"setSlopBias",lua_ax_physics_PhysicsWorld_setSlopBias); - tolua_function(tolua_S,"setSpeed",lua_ax_physics_PhysicsWorld_setSpeed); - tolua_function(tolua_S,"getSpeed",lua_ax_physics_PhysicsWorld_getSpeed); - tolua_function(tolua_S,"setUpdateRate",lua_ax_physics_PhysicsWorld_setUpdateRate); - tolua_function(tolua_S,"getUpdateRate",lua_ax_physics_PhysicsWorld_getUpdateRate); - tolua_function(tolua_S,"setSubsteps",lua_ax_physics_PhysicsWorld_setSubsteps); - tolua_function(tolua_S,"getSubsteps",lua_ax_physics_PhysicsWorld_getSubsteps); - tolua_function(tolua_S,"setFixedUpdateRate",lua_ax_physics_PhysicsWorld_setFixedUpdateRate); - tolua_function(tolua_S,"getFixedUpdateRate",lua_ax_physics_PhysicsWorld_getFixedUpdateRate); - tolua_function(tolua_S,"setDebugDrawMask",lua_ax_physics_PhysicsWorld_setDebugDrawMask); - tolua_function(tolua_S,"setPreUpdateCallback",lua_ax_physics_PhysicsWorld_setPreUpdateCallback); - tolua_function(tolua_S,"setPostUpdateCallback",lua_ax_physics_PhysicsWorld_setPostUpdateCallback); - tolua_function(tolua_S,"getDebugDrawMask",lua_ax_physics_PhysicsWorld_getDebugDrawMask); - tolua_function(tolua_S,"getDebugDraw",lua_ax_physics_PhysicsWorld_getDebugDraw); - tolua_function(tolua_S,"setAutoStep",lua_ax_physics_PhysicsWorld_setAutoStep); - tolua_function(tolua_S,"isAutoStep",lua_ax_physics_PhysicsWorld_isAutoStep); - tolua_function(tolua_S,"step",lua_ax_physics_PhysicsWorld_step); - tolua_endmodule(tolua_S); - auto typeName = typeid(ax::PhysicsWorld).name(); // rtti is literal storage - g_luaType[reinterpret_cast(typeName)] = "ax.PhysicsWorld"; - g_typeCast[typeName] = "ax.PhysicsWorld"; - return 1; -} TOLUA_API int register_all_ax_physics(lua_State* tolua_S) { tolua_open(tolua_S); @@ -12896,35 +10,6 @@ TOLUA_API int register_all_ax_physics(lua_State* tolua_S) tolua_module(tolua_S,"ax",0); tolua_beginmodule(tolua_S,"ax"); - lua_register_ax_physics_PhysicsShape(tolua_S); - lua_register_ax_physics_PhysicsShapeCircle(tolua_S); - lua_register_ax_physics_PhysicsShapePolygon(tolua_S); - lua_register_ax_physics_PhysicsShapeBox(tolua_S); - lua_register_ax_physics_PhysicsShapeEdgeSegment(tolua_S); - lua_register_ax_physics_PhysicsShapeEdgePolygon(tolua_S); - lua_register_ax_physics_PhysicsShapeEdgeBox(tolua_S); - lua_register_ax_physics_PhysicsShapeEdgeChain(tolua_S); - lua_register_ax_physics_PhysicsBody(tolua_S); - lua_register_ax_physics_PhysicsContact(tolua_S); - lua_register_ax_physics_PhysicsContactPreSolve(tolua_S); - lua_register_ax_physics_PhysicsContactPostSolve(tolua_S); - lua_register_ax_physics_EventListenerPhysicsContact(tolua_S); - lua_register_ax_physics_EventListenerPhysicsContactWithBodies(tolua_S); - lua_register_ax_physics_EventListenerPhysicsContactWithShapes(tolua_S); - lua_register_ax_physics_EventListenerPhysicsContactWithGroup(tolua_S); - lua_register_ax_physics_PhysicsJoint(tolua_S); - lua_register_ax_physics_PhysicsJointFixed(tolua_S); - lua_register_ax_physics_PhysicsJointLimit(tolua_S); - lua_register_ax_physics_PhysicsJointPin(tolua_S); - lua_register_ax_physics_PhysicsJointDistance(tolua_S); - lua_register_ax_physics_PhysicsJointSpring(tolua_S); - lua_register_ax_physics_PhysicsJointGroove(tolua_S); - lua_register_ax_physics_PhysicsJointRotarySpring(tolua_S); - lua_register_ax_physics_PhysicsJointRotaryLimit(tolua_S); - lua_register_ax_physics_PhysicsJointRatchet(tolua_S); - lua_register_ax_physics_PhysicsJointGear(tolua_S); - lua_register_ax_physics_PhysicsJointMotor(tolua_S); - lua_register_ax_physics_PhysicsWorld(tolua_S); tolua_endmodule(tolua_S); return 1; diff --git a/extensions/scripting/lua-bindings/auto/axlua_studio_auto.cpp b/extensions/scripting/lua-bindings/auto/axlua_studio_auto.cpp index a4b031d2a8bf..4bc96ae6ea48 100644 --- a/extensions/scripting/lua-bindings/auto/axlua_studio_auto.cpp +++ b/extensions/scripting/lua-bindings/auto/axlua_studio_auto.cpp @@ -3002,9 +3002,9 @@ int lua_ax_studio_BaseData_setColor(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Color4B arg0; + ax::Color32 arg0; - ok &=luaval_to_color4b(tolua_S, 2, &arg0, "ccs.BaseData:setColor"); + ok &=luaval_to_color32(tolua_S, 2, &arg0, "ccs.BaseData:setColor"); if(!ok) { tolua_error(tolua_S,"invalid arguments in function 'lua_ax_studio_BaseData_setColor'", nullptr); @@ -3058,7 +3058,7 @@ int lua_ax_studio_BaseData_getColor(lua_State* tolua_S) return 0; } auto&& ret = cobj->getColor(); - color4b_to_luaval(tolua_S, ret); + color32_to_luaval(tolua_S, ret); return 1; } luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ccs.BaseData:getColor",argc, 0); @@ -24342,9 +24342,9 @@ int lua_ax_studio_BoneNode_setDebugDrawColor(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Color4F arg0; + ax::Color arg0; - ok &=luaval_to_color4f(tolua_S, 2, &arg0, "ccs.BoneNode:setDebugDrawColor"); + ok &=luaval_to_color(tolua_S, 2, &arg0, "ccs.BoneNode:setDebugDrawColor"); if(!ok) { tolua_error(tolua_S,"invalid arguments in function 'lua_ax_studio_BoneNode_setDebugDrawColor'", nullptr); @@ -24398,7 +24398,7 @@ int lua_ax_studio_BoneNode_getDebugDrawColor(lua_State* tolua_S) return 0; } auto&& ret = cobj->getDebugDrawColor(); - color4f_to_luaval(tolua_S, ret); + color_to_luaval(tolua_S, ret); return 1; } luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "ccs.BoneNode:getDebugDrawColor",argc, 0); diff --git a/extensions/scripting/lua-bindings/auto/axlua_ui_auto.cpp b/extensions/scripting/lua-bindings/auto/axlua_ui_auto.cpp index ddd590ea684a..f9749eaf29c1 100644 --- a/extensions/scripting/lua-bindings/auto/axlua_ui_auto.cpp +++ b/extensions/scripting/lua-bindings/auto/axlua_ui_auto.cpp @@ -12284,9 +12284,9 @@ int lua_ax_ui_Text_setTextColor(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Color4B arg0; + ax::Color32 arg0; - ok &=luaval_to_color4b(tolua_S, 2, &arg0, "axui.Text:setTextColor"); + ok &=luaval_to_color32(tolua_S, 2, &arg0, "axui.Text:setTextColor"); if(!ok) { tolua_error(tolua_S,"invalid arguments in function 'lua_ax_ui_Text_setTextColor'", nullptr); @@ -12340,7 +12340,7 @@ int lua_ax_ui_Text_getTextColor(lua_State* tolua_S) return 0; } auto&& ret = cobj->getTextColor(); - color4b_to_luaval(tolua_S, ret); + color32_to_luaval(tolua_S, ret); return 1; } luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "axui.Text:getTextColor",argc, 0); @@ -12392,9 +12392,9 @@ int lua_ax_ui_Text_enableShadow(lua_State* tolua_S) } if (argc == 1) { - ax::Color4B arg0; + ax::Color32 arg0; - ok &=luaval_to_color4b(tolua_S, 2, &arg0, "axui.Text:enableShadow"); + ok &=luaval_to_color32(tolua_S, 2, &arg0, "axui.Text:enableShadow"); if(!ok) { tolua_error(tolua_S,"invalid arguments in function 'lua_ax_ui_Text_enableShadow'", nullptr); @@ -12406,10 +12406,10 @@ int lua_ax_ui_Text_enableShadow(lua_State* tolua_S) } if (argc == 2) { - ax::Color4B arg0; + ax::Color32 arg0; ax::Vec2 arg1; - ok &=luaval_to_color4b(tolua_S, 2, &arg0, "axui.Text:enableShadow"); + ok &=luaval_to_color32(tolua_S, 2, &arg0, "axui.Text:enableShadow"); ok &= luaval_to_vec2(tolua_S, 3, &arg1, "axui.Text:enableShadow"); if(!ok) @@ -12423,11 +12423,11 @@ int lua_ax_ui_Text_enableShadow(lua_State* tolua_S) } if (argc == 3) { - ax::Color4B arg0; + ax::Color32 arg0; ax::Vec2 arg1; int arg2; - ok &=luaval_to_color4b(tolua_S, 2, &arg0, "axui.Text:enableShadow"); + ok &=luaval_to_color32(tolua_S, 2, &arg0, "axui.Text:enableShadow"); ok &= luaval_to_vec2(tolua_S, 3, &arg1, "axui.Text:enableShadow"); @@ -12479,9 +12479,9 @@ int lua_ax_ui_Text_enableOutline(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Color4B arg0; + ax::Color32 arg0; - ok &=luaval_to_color4b(tolua_S, 2, &arg0, "axui.Text:enableOutline"); + ok &=luaval_to_color32(tolua_S, 2, &arg0, "axui.Text:enableOutline"); if(!ok) { tolua_error(tolua_S,"invalid arguments in function 'lua_ax_ui_Text_enableOutline'", nullptr); @@ -12493,10 +12493,10 @@ int lua_ax_ui_Text_enableOutline(lua_State* tolua_S) } if (argc == 2) { - ax::Color4B arg0; + ax::Color32 arg0; int arg1; - ok &=luaval_to_color4b(tolua_S, 2, &arg0, "axui.Text:enableOutline"); + ok &=luaval_to_color32(tolua_S, 2, &arg0, "axui.Text:enableOutline"); ok &= luaval_to_int32(tolua_S, 3,(int *)&arg1, "axui.Text:enableOutline"); if(!ok) @@ -12546,9 +12546,9 @@ int lua_ax_ui_Text_enableGlow(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Color4B arg0; + ax::Color32 arg0; - ok &=luaval_to_color4b(tolua_S, 2, &arg0, "axui.Text:enableGlow"); + ok &=luaval_to_color32(tolua_S, 2, &arg0, "axui.Text:enableGlow"); if(!ok) { tolua_error(tolua_S,"invalid arguments in function 'lua_ax_ui_Text_enableGlow'", nullptr); @@ -12794,7 +12794,7 @@ int lua_ax_ui_Text_getShadowColor(lua_State* tolua_S) return 0; } auto&& ret = cobj->getShadowColor(); - color4b_to_luaval(tolua_S, ret); + color32_to_luaval(tolua_S, ret); return 1; } luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "axui.Text:getShadowColor",argc, 0); @@ -12935,7 +12935,7 @@ int lua_ax_ui_Text_getEffectColor(lua_State* tolua_S) return 0; } auto&& ret = cobj->getEffectColor(); - color4b_to_luaval(tolua_S, ret); + color32_to_luaval(tolua_S, ret); return 1; } luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "axui.Text:getEffectColor",argc, 0); @@ -22126,7 +22126,7 @@ int lua_ax_ui_TextField_getPlaceHolderColor(lua_State* tolua_S) return 0; } auto&& ret = cobj->getPlaceHolderColor(); - color4b_to_luaval(tolua_S, ret); + color32_to_luaval(tolua_S, ret); return 1; } luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "axui.TextField:getPlaceHolderColor",argc, 0); @@ -22162,8 +22162,8 @@ int lua_ax_ui_TextField_setPlaceHolderColor(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; do{ if (argc == 1) { - ax::Color4B arg0; - ok &=luaval_to_color4b(tolua_S, 2, &arg0, "axui.TextField:setPlaceHolderColor"); + ax::Color32 arg0; + ok &=luaval_to_color32(tolua_S, 2, &arg0, "axui.TextField:setPlaceHolderColor"); if (!ok) { break; } cobj->setPlaceHolderColor(arg0); @@ -22228,7 +22228,7 @@ int lua_ax_ui_TextField_getTextColor(lua_State* tolua_S) return 0; } auto&& ret = cobj->getTextColor(); - color4b_to_luaval(tolua_S, ret); + color32_to_luaval(tolua_S, ret); return 1; } luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "axui.TextField:getTextColor",argc, 0); @@ -22269,9 +22269,9 @@ int lua_ax_ui_TextField_setTextColor(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Color4B arg0; + ax::Color32 arg0; - ok &=luaval_to_color4b(tolua_S, 2, &arg0, "axui.TextField:setTextColor"); + ok &=luaval_to_color32(tolua_S, 2, &arg0, "axui.TextField:setTextColor"); if(!ok) { tolua_error(tolua_S,"invalid arguments in function 'lua_ax_ui_TextField_setTextColor'", nullptr); @@ -30973,7 +30973,7 @@ int lua_ax_ui_RichText_stringWithColor3B(lua_State* tolua_S) return 0; } -int lua_ax_ui_RichText_stringWithColor4B(lua_State* tolua_S) +int lua_ax_ui_RichText_stringWithColor32(lua_State* tolua_S) { int argc = 0; ax::ui::RichText* cobj = nullptr; @@ -30993,7 +30993,7 @@ int lua_ax_ui_RichText_stringWithColor4B(lua_State* tolua_S) #if _AX_DEBUG >= 1 if (!cobj) { - tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_ui_RichText_stringWithColor4B'", nullptr); + tolua_error(tolua_S,"invalid 'cobj' in function 'lua_ax_ui_RichText_stringWithColor32'", nullptr); return 0; } #endif @@ -31001,24 +31001,24 @@ int lua_ax_ui_RichText_stringWithColor4B(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Color4B arg0; + ax::Color32 arg0; - ok &=luaval_to_color4b(tolua_S, 2, &arg0, "axui.RichText:stringWithColor4B"); + ok &=luaval_to_color32(tolua_S, 2, &arg0, "axui.RichText:stringWithColor32"); if(!ok) { - tolua_error(tolua_S,"invalid arguments in function 'lua_ax_ui_RichText_stringWithColor4B'", nullptr); + tolua_error(tolua_S,"invalid arguments in function 'lua_ax_ui_RichText_stringWithColor32'", nullptr); return 0; } - auto&& ret = cobj->stringWithColor4B(arg0); + auto&& ret = cobj->stringWithColor32(arg0); lua_pushlstring(tolua_S,ret.c_str(),ret.length()); return 1; } - luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "axui.RichText:stringWithColor4B",argc, 1); + luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "axui.RichText:stringWithColor32",argc, 1); return 0; #if _AX_DEBUG >= 1 tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'lua_ax_ui_RichText_stringWithColor4B'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'lua_ax_ui_RichText_stringWithColor32'.",&tolua_err); #endif return 0; @@ -31388,7 +31388,7 @@ int lua_register_ax_ui_RichText(lua_State* tolua_S) tolua_function(tolua_S,"getDefaults",lua_ax_ui_RichText_getDefaults); tolua_function(tolua_S,"color3BWithString",lua_ax_ui_RichText_color3BWithString); tolua_function(tolua_S,"stringWithColor3B",lua_ax_ui_RichText_stringWithColor3B); - tolua_function(tolua_S,"stringWithColor4B",lua_ax_ui_RichText_stringWithColor4B); + tolua_function(tolua_S,"stringWithColor32",lua_ax_ui_RichText_stringWithColor32); tolua_function(tolua_S,"openUrl",lua_ax_ui_RichText_openUrl); tolua_function(tolua_S,"initWithXML",lua_ax_ui_RichText_initWithXML); tolua_function(tolua_S,"setString",lua_ax_ui_RichText_setString); @@ -35062,8 +35062,8 @@ int lua_ax_ui_EditBox_setFontColor(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; do{ if (argc == 1) { - ax::Color4B arg0; - ok &=luaval_to_color4b(tolua_S, 2, &arg0, "axui.EditBox:setFontColor"); + ax::Color32 arg0; + ok &=luaval_to_color32(tolua_S, 2, &arg0, "axui.EditBox:setFontColor"); if (!ok) { break; } cobj->setFontColor(arg0); @@ -35128,7 +35128,7 @@ int lua_ax_ui_EditBox_getFontColor(lua_State* tolua_S) return 0; } auto&& ret = cobj->getFontColor(); - color4b_to_luaval(tolua_S, ret); + color32_to_luaval(tolua_S, ret); return 1; } luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "axui.EditBox:getFontColor",argc, 0); @@ -35411,8 +35411,8 @@ int lua_ax_ui_EditBox_setPlaceholderFontColor(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; do{ if (argc == 1) { - ax::Color4B arg0; - ok &=luaval_to_color4b(tolua_S, 2, &arg0, "axui.EditBox:setPlaceholderFontColor"); + ax::Color32 arg0; + ok &=luaval_to_color32(tolua_S, 2, &arg0, "axui.EditBox:setPlaceholderFontColor"); if (!ok) { break; } cobj->setPlaceholderFontColor(arg0); @@ -35477,7 +35477,7 @@ int lua_ax_ui_EditBox_getPlaceholderFontColor(lua_State* tolua_S) return 0; } auto&& ret = cobj->getPlaceholderFontColor(); - color4b_to_luaval(tolua_S, ret); + color32_to_luaval(tolua_S, ret); return 1; } luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "axui.EditBox:getPlaceholderFontColor",argc, 0); @@ -39386,9 +39386,9 @@ int lua_ax_ui_TabHeader_setTitleColor(lua_State* tolua_S) argc = lua_gettop(tolua_S)-1; if (argc == 1) { - ax::Color4B arg0; + ax::Color32 arg0; - ok &=luaval_to_color4b(tolua_S, 2, &arg0, "axui.TabHeader:setTitleColor"); + ok &=luaval_to_color32(tolua_S, 2, &arg0, "axui.TabHeader:setTitleColor"); if(!ok) { tolua_error(tolua_S,"invalid arguments in function 'lua_ax_ui_TabHeader_setTitleColor'", nullptr); @@ -39442,7 +39442,7 @@ int lua_ax_ui_TabHeader_getTitleColor(lua_State* tolua_S) return 0; } auto&& ret = cobj->getTitleColor(); - color4b_to_luaval(tolua_S, ret); + color32_to_luaval(tolua_S, ret); return 1; } luaL_error(tolua_S, "%s has wrong number of arguments: %d, was expecting %d \n", "axui.TabHeader:getTitleColor",argc, 0); diff --git a/extensions/scripting/lua-bindings/manual/LuaBasicConversions.cpp b/extensions/scripting/lua-bindings/manual/LuaBasicConversions.cpp index da8e20c40bb0..ed7066895092 100644 --- a/extensions/scripting/lua-bindings/manual/LuaBasicConversions.cpp +++ b/extensions/scripting/lua-bindings/manual/LuaBasicConversions.cpp @@ -506,7 +506,7 @@ bool luaval_to_blendfunc(lua_State* L, int lo, ax::BlendFunc* outValue, const ch return ok; } -#if defined(AX_ENABLE_PHYSICS) +#if defined(AX_ENABLE_PHYSICS) && 0 bool luaval_to_physics_material(lua_State* L, int lo, PhysicsMaterial* outValue, const char* funcName) { if (NULL == L || NULL == outValue) @@ -666,7 +666,7 @@ bool luaval_to_rect(lua_State* L, int lo, Rect* outValue, const char* funcName) return ok; } -bool luaval_to_color4b(lua_State* L, int lo, Color4B* outValue, const char* funcName) +bool luaval_to_color32(lua_State* L, int lo, Color32* outValue, const char* funcName) { if (NULL == L || NULL == outValue) return false; @@ -708,7 +708,7 @@ bool luaval_to_color4b(lua_State* L, int lo, Color4B* outValue, const char* func return ok; } -bool luaval_to_color4f(lua_State* L, int lo, Color4F* outValue, const char* funcName) +bool luaval_to_color(lua_State* L, int lo, ax::Color* outValue, const char* funcName) { if (NULL == L || NULL == outValue) return false; @@ -1885,7 +1885,7 @@ bool luaval_to_tex2f(lua_State* L, int lo, ax::Tex2F* outValue, const char* func return ok; } -bool luaval_to_v3f_c4b_t2f(lua_State* L, int lo, ax::V3F_C4B_T2F* outValue, const char* funcName) +bool luaval_to_v3f_c4f_t2f(lua_State* L, int lo, ax::V3F_T2F_C4F* outValue, const char* funcName) { if (nullptr == L || nullptr == outValue) return false; @@ -1903,7 +1903,7 @@ bool luaval_to_v3f_c4b_t2f(lua_State* L, int lo, ax::V3F_C4B_T2F* outValue, cons if (ok) { - lua_pushstring(L, "vertices"); + lua_pushstring(L, "position"); lua_gettable(L, lo); if (!tolua_istable(L, lua_gettop(L), 0, &tolua_err)) { @@ -1911,7 +1911,7 @@ bool luaval_to_v3f_c4b_t2f(lua_State* L, int lo, ax::V3F_C4B_T2F* outValue, cons return false; } - ok &= luaval_to_vec3(L, lua_gettop(L), &outValue->vertices); + ok &= luaval_to_vec3(L, lua_gettop(L), &outValue->position); if (!ok) { lua_pop(L, 1); @@ -1919,14 +1919,14 @@ bool luaval_to_v3f_c4b_t2f(lua_State* L, int lo, ax::V3F_C4B_T2F* outValue, cons } lua_pop(L, 1); - lua_pushstring(L, "colors"); + lua_pushstring(L, "color"); lua_gettable(L, lo); if (!tolua_istable(L, lua_gettop(L), 0, &tolua_err)) { lua_pop(L, 1); return false; } - ok &= luaval_to_color4b(L, lua_gettop(L), &outValue->colors); + ok &= luaval_to_color(L, lua_gettop(L), &outValue->color); if (!ok) { lua_pop(L, 1); @@ -1934,14 +1934,14 @@ bool luaval_to_v3f_c4b_t2f(lua_State* L, int lo, ax::V3F_C4B_T2F* outValue, cons } lua_pop(L, 1); - lua_pushstring(L, "texCoords"); + lua_pushstring(L, "texCoord"); lua_gettable(L, lo); if (!tolua_istable(L, lua_gettop(L), 0, &tolua_err)) { lua_pop(L, 1); return false; } - ok &= luaval_to_tex2f(L, lua_gettop(L), &outValue->texCoords); + ok &= luaval_to_tex2f(L, lua_gettop(L), &outValue->texCoord); if (!ok) { lua_pop(L, 1); @@ -2040,7 +2040,7 @@ bool luaval_to_std_vector_vec3(lua_State* L, int lo, std::vector* ret, bool luaval_to_std_vector_v3f_c4b_t2f(lua_State* L, int lo, - std::vector* ret, + std::vector* ret, const char* funcName) { if (nullptr == L || nullptr == ret || lua_gettop(L) < lo) @@ -2060,14 +2060,14 @@ bool luaval_to_std_vector_v3f_c4b_t2f(lua_State* L, if (ok) { size_t len = lua_objlen(L, lo); - ax::V3F_C4B_T2F value; + ax::V3F_T2F_C4F value; for (size_t i = 0; i < len; i++) { lua_pushnumber(L, i + 1); lua_gettable(L, lo); if (lua_istable(L, lua_gettop(L))) { - ok &= luaval_to_v3f_c4b_t2f(L, lua_gettop(L), &value); + ok &= luaval_to_v3f_c4f_t2f(L, lua_gettop(L), &value); if (ok) { ret->emplace_back(value); @@ -2075,7 +2075,7 @@ bool luaval_to_std_vector_v3f_c4b_t2f(lua_State* L, } else { - AXASSERT(false, "V3F_C4B_T2F type is needed"); + AXASSERT(false, "V3F_T2F_C4F type is needed"); } lua_pop(L, 1); } @@ -2255,7 +2255,7 @@ int vec4_to_luaval(lua_State* L, const ax::Vec4& vec4) return 1; } -#if defined(AX_ENABLE_PHYSICS) +#if defined(AX_ENABLE_PHYSICS) && 0 void physics_material_to_luaval(lua_State* L, const PhysicsMaterial& pm) { if (nullptr == L) @@ -2367,7 +2367,7 @@ void rect_to_luaval(lua_State* L, const Rect& rt) lua_rawset(L, -3); /* table[key] = value, L: table */ } -void color4b_to_luaval(lua_State* L, const Color4B& color) +void color32_to_luaval(lua_State* L, const Color32& color) { if (NULL == L) return; @@ -2386,7 +2386,7 @@ void color4b_to_luaval(lua_State* L, const Color4B& color) lua_rawset(L, -3); /* table[key] = value, L: table */ } -void color4f_to_luaval(lua_State* L, const Color4F& color) +void color_to_luaval(lua_State* L, const ax::Color& color) { if (NULL == L) return; diff --git a/extensions/scripting/lua-bindings/manual/LuaBasicConversions.h b/extensions/scripting/lua-bindings/manual/LuaBasicConversions.h index c0210d65fb37..47f4d1fd43db 100644 --- a/extensions/scripting/lua-bindings/manual/LuaBasicConversions.h +++ b/extensions/scripting/lua-bindings/manual/LuaBasicConversions.h @@ -23,8 +23,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ****************************************************************************/ -#ifndef __COCOS2DX_SCRIPTING_LUA_COCOS2DXSUPPORT_LUABAISCCONVERSIONS_H__ -#define __COCOS2DX_SCRIPTING_LUA_COCOS2DXSUPPORT_LUABAISCCONVERSIONS_H__ +#ifndef __AXMOL_SCRIPTING_LUA_COCOS2DXSUPPORT_LUABAISCCONVERSIONS_H__ +#define __AXMOL_SCRIPTING_LUA_COCOS2DXSUPPORT_LUABAISCCONVERSIONS_H__ #include #include @@ -39,10 +39,11 @@ #include "3d/Bundle3D.h" #include "base/Value.h" #include "base/Types.h" +# if defined(AX_ENABLE_PHYSICS) && 0 #include "physics/PhysicsContact.h" #include "physics/PhysicsJoint.h" -#include "physics/PhysicsShape.h" #include "physics/PhysicsWorld.h" +# endif #include "renderer/backend/Types.h" #include "renderer/backend/VertexLayout.h" #include "ui/GUIDefine.h" @@ -283,33 +284,33 @@ extern bool luaval_to_rect(lua_State* L, int lo, Rect* outValue, const char* fun extern AX_LUA_DLL bool luaval_to_color3b(lua_State* L, int lo, Color3B* outValue, const char* funcName = ""); /** - * Get a Color4B object value from the given acceptable index of stack. + * Get a Color32 object value from the given acceptable index of stack. * If the value at the given acceptable index of stack is a table it returns true, otherwise returns false. * If the table has the `r`,`g`, `b` and 'a' keys and the corresponding values are not nil, this function would assign * the values to the corresponding members of outValue. Otherwise, the value of members of outValue would be 0. * * @param L the current lua_State. * @param lo the given acceptable index of stack. - * @param outValue the pointer to a Color4B object which stores the values from the Lua table. + * @param outValue the pointer to a Color32 object which stores the values from the Lua table. * @param funcName the name of calling function, it is used for error output in the debug model. * @return Return true if the value at the given acceptable index of stack is a table, otherwise return false. */ -extern bool luaval_to_color4b(lua_State* L, int lo, Color4B* outValue, const char* funcName = ""); +extern bool luaval_to_color32(lua_State* L, int lo, Color32* outValue, const char* funcName = ""); /** - * Get a Color4F object value from the given acceptable index of stack. + * Get a ax::Color object value from the given acceptable index of stack. * If the value at the given acceptable index of stack is a table it returns true, otherwise returns false. * If the table has the `r`,`g`, `b` and 'a' keys and the corresponding values are not nil, this function would assign * the values to the corresponding members of outValue. Otherwise, the value of members of outValue would be 0. * * @param L the current lua_State. * @param lo the given acceptable index of stack. - * @param outValue the pointer to a Color4F object which stores the values from the Lua table. + * @param outValue the pointer to a ax::Color object which stores the values from the Lua table. * @param funcName the name of calling function, it is used for error output in the debug model. * @return Return true if the value at the given acceptable index of stack is a table, otherwise return false. */ -extern bool luaval_to_color4f(lua_State* L, int lo, Color4F* outValue, const char* funcName = ""); -#if defined(AX_ENABLE_PHYSICS) +extern bool luaval_to_color(lua_State* L, int lo, ax::Color* outValue, const char* funcName = ""); +#if defined(AX_ENABLE_PHYSICS) && 0 /** * Get a PhysicsMaterial object value from the given acceptable index of stack. @@ -798,17 +799,17 @@ extern bool luaval_to_quaternion(lua_State* L, int lo, ax::Quaternion* outValue, extern bool luaval_to_texparams(lua_State* L, int lo, ax::Texture2D::TexParams* outValue, const char* funcName = ""); /** - * Get a ax::V3F_C4B_T2F object value from the given acceptable index of stack. + * Get a ax::V3F_T2F_C4F object value from the given acceptable index of stack. * If the value at the given acceptable index of stack is a table it returns true, otherwise returns false. * If the table has the `vertices`, `colors`, and `texCoords` keys and the corresponding values are not nil, this * function would assign the values to the corresponding members of outValue. * @param L the current lua_State. * @param lo the given acceptable index of stack. - * @param outValue the pointer to a ax::V3F_C4B_T2F object which stores the values from the Lua table. + * @param outValue the pointer to a ax::V3F_T2F_C4F object which stores the values from the Lua table. * @param funcName the name of calling function, it is used for error output in the debug model. * @return true if the value at the given acceptable index of stack is a table, otherwise return false. */ -extern bool luaval_to_v3f_c4b_t2f(lua_State* L, int lo, ax::V3F_C4B_T2F* outValue, const char* funcName = ""); +extern bool luaval_to_v3f_c4f_t2f(lua_State* L, int lo, ax::V3F_T2F_C4F* outValue, const char* funcName = ""); /** * Get a ax::Tex2F object value from the given acceptable index of stack. @@ -824,17 +825,17 @@ extern bool luaval_to_v3f_c4b_t2f(lua_State* L, int lo, ax::V3F_C4B_T2F* outValu extern bool luaval_to_tex2f(lua_State* L, int lo, ax::Tex2F* outValue, const char* funcName = ""); /** - * Get a pointer points to a std::vector from a Lua array table in the stack. + * Get a pointer points to a std::vector from a Lua array table in the stack. * * @param L the current lua_State. * @param lo the given acceptable index of stack. - * @param ret a pointer points to a std::vector. + * @param ret a pointer points to a std::vector. * @param funcName the name of calling function, it is used for error output in the debug model. * @return Return true if the value at the given acceptable index of stack is a table, otherwise return false. */ extern bool luaval_to_std_vector_v3f_c4b_t2f(lua_State* L, int lo, - std::vector* ret, + std::vector* ret, const char* funcName = ""); /** @@ -942,26 +943,26 @@ extern void rect_to_luaval(lua_State* L, const Rect& rt); extern AX_LUA_DLL void color3b_to_luaval(lua_State* L, const Color3B& cc); /** - * Push a table converted from a ax::Color4B object into the Lua stack. + * Push a table converted from a ax::Color32 object into the Lua stack. * The format of table as follows: {r=numberValue1, g=numberValue2, b=numberValue3, a=numberValue4} * * @param L the current lua_State. - * @param cc a ax::Color4B object. + * @param cc a ax::Color32 object. */ -extern void color4b_to_luaval(lua_State* L, const Color4B& cc); +extern void color32_to_luaval(lua_State* L, const Color32& cc); /** - * Push a table converted from a ax::Color4F object into the Lua stack. + * Push a table converted from a ax::Color object into the Lua stack. * The format of table as follows: {r=numberValue1, g=numberValue2, b=numberValue3, a=numberValue4} * * @param L the current lua_State. - * @param cc a ax::Color4F object. + * @param cc a ax::Color object. */ -extern void color4f_to_luaval(lua_State* L, const Color4F& cc); +extern void color_to_luaval(lua_State* L, const Color& cc); void std_thread_id_to_luaval(lua_State* L, const std::thread::id& value); -#if defined(AX_ENABLE_PHYSICS) +#if defined(AX_ENABLE_PHYSICS) && 0 /** * Push a table converted from a ax::PhysicsMaterial object into the Lua stack. diff --git a/extensions/scripting/lua-bindings/manual/LuaStack.cpp b/extensions/scripting/lua-bindings/manual/LuaStack.cpp index 0db6b798b38c..cb6fe7a5d7c5 100644 --- a/extensions/scripting/lua-bindings/manual/LuaStack.cpp +++ b/extensions/scripting/lua-bindings/manual/LuaStack.cpp @@ -223,7 +223,7 @@ bool LuaStack::init() tolua_luanode_open(_state); register_luanode_manual(_state); -#if defined(AX_ENABLE_PHYSICS) +#if defined(AX_ENABLE_PHYSICS) && 0 register_all_ax_physics(_state); register_all_ax_physics_manual(_state); #endif diff --git a/extensions/scripting/lua-bindings/manual/base/axlua_base_manual.cpp b/extensions/scripting/lua-bindings/manual/base/axlua_base_manual.cpp index b447a8920c34..75face6e60a2 100644 --- a/extensions/scripting/lua-bindings/manual/base/axlua_base_manual.cpp +++ b/extensions/scripting/lua-bindings/manual/base/axlua_base_manual.cpp @@ -3041,8 +3041,8 @@ static int toaxlua_DrawNode_drawPolygon(lua_State* tolua_S) lua_pop(tolua_S, 1); } - Color4B fillColor; - if (!luaval_to_color4b(tolua_S, 4, &fillColor, "ax.DrawNode:drawPolygon")) + ax::Color fillColor; + if (!luaval_to_color(tolua_S, 4, &fillColor, "ax.DrawNode:drawPolygon")) { AX_SAFE_DELETE_ARRAY(points); return 0; @@ -3050,8 +3050,8 @@ static int toaxlua_DrawNode_drawPolygon(lua_State* tolua_S) float borderWidth = (float)tolua_tonumber(tolua_S, 5, 0); - Color4B borderColor; - if (!luaval_to_color4b(tolua_S, 6, &borderColor, "ax.DrawNode:drawPolygon")) + ax::Color borderColor; + if (!luaval_to_color(tolua_S, 6, &borderColor, "ax.DrawNode:drawPolygon")) { AX_SAFE_DELETE_ARRAY(points); return 0; @@ -3125,9 +3125,9 @@ int toaxlua_DrawNode_drawSolidPoly(lua_State* tolua_S) lua_pop(tolua_S, 1); } - ax::Color4B arg2; + ax::Color arg2; - ok &= luaval_to_color4b(tolua_S, 4, &arg2, "ax.DrawNode:drawSolidPoly"); + ok &= luaval_to_color(tolua_S, 4, &arg2, "ax.DrawNode:drawSolidPoly"); if (!ok) return 0; self->drawSolidPoly(points, size, arg2); @@ -3204,11 +3204,11 @@ int toaxlua_DrawNode_drawPoly(lua_State* tolua_S) } bool arg2; - ax::Color4B arg3; + ax::Color arg3; ok &= luaval_to_boolean(tolua_S, 4, &arg2, "ax.DrawNode:drawPoly"); - ok &= luaval_to_color4b(tolua_S, 5, &arg3, "ax.DrawNode:drawPoly"); + ok &= luaval_to_color(tolua_S, 5, &arg3, "ax.DrawNode:drawPoly"); if (!ok) return 0; @@ -3273,13 +3273,13 @@ int toaxlua_DrawNode_drawCardinalSpline(lua_State* tolua_S) double arg1; unsigned int arg2; - ax::Color4B arg3; + ax::Color arg3; ok &= luaval_to_number(tolua_S, 3, &arg1, "ax.DrawNode:drawCardinalSpline"); ok &= luaval_to_uint32(tolua_S, 4, &arg2, "ax.DrawNode:drawCardinalSpline"); - ok &= luaval_to_color4b(tolua_S, 5, &arg3, "ax.DrawNode:drawCardinalSpline"); + ok &= luaval_to_color(tolua_S, 5, &arg3, "ax.DrawNode:drawCardinalSpline"); if (!ok) return 0; self->drawCardinalSpline(config, (float)arg1, arg2, arg3); @@ -3340,11 +3340,11 @@ int toaxlua_DrawNode_drawCatmullRom(lua_State* tolua_S) AX_SAFE_DELETE_ARRAY(arr); unsigned int arg1; - ax::Color4B arg2; + ax::Color arg2; ok &= luaval_to_uint32(tolua_S, 3, &arg1, "ax.DrawNode:drawCatmullRom"); - ok &= luaval_to_color4b(tolua_S, 4, &arg2, "ax.DrawNode:drawCatmullRom"); + ok &= luaval_to_color(tolua_S, 4, &arg2, "ax.DrawNode:drawCatmullRom"); if (!ok) return 0; self->drawCatmullRom(config, arg1, arg2); @@ -3415,9 +3415,9 @@ int toaxlua_DrawNode_drawPoints(lua_State* tolua_S) lua_pop(tolua_S, 1); } - ax::Color4B arg2; + ax::Color arg2; - ok &= luaval_to_color4b(tolua_S, 4, &arg2, "ax.DrawNode:drawPoints"); + ok &= luaval_to_color(tolua_S, 4, &arg2, "ax.DrawNode:drawPoints"); if (!ok) return 0; self->drawPoints(points, size, arg2); @@ -3453,8 +3453,8 @@ int toaxlua_DrawNode_drawPoints(lua_State* tolua_S) } float pointSize = (float)tolua_tonumber(tolua_S, 4, 0); - ax::Color4B color; - ok &= luaval_to_color4b(tolua_S, 5, &color, "ax.DrawNode:drawPoints"); + ax::Color color; + ok &= luaval_to_color(tolua_S, 5, &color, "ax.DrawNode:drawPoints"); if (!ok) return 0; self->drawPoints(points, size, pointSize, color); diff --git a/extensions/scripting/lua-bindings/manual/physics/axlua_physics_manual.cpp b/extensions/scripting/lua-bindings/manual/physics/axlua_physics_manual.cpp index 63bffba609c7..ecbd884861e4 100644 --- a/extensions/scripting/lua-bindings/manual/physics/axlua_physics_manual.cpp +++ b/extensions/scripting/lua-bindings/manual/physics/axlua_physics_manual.cpp @@ -25,7 +25,7 @@ #include "lua-bindings/manual/base/axlua_base_manual.hpp" -#if defined(AX_ENABLE_PHYSICS) +#if defined(AX_ENABLE_PHYSICS) && 0 # include "lua-bindings/manual/tolua_fix.h" # include "lua-bindings/manual/LuaBasicConversions.h" # include "lua-bindings/manual/LuaValue.h" diff --git a/extensions/scripting/lua-bindings/manual/physics3d/axlua_physics3d_manual.cpp b/extensions/scripting/lua-bindings/manual/physics3d/axlua_physics3d_manual.cpp index 2178f6e801aa..292f3addd6a6 100644 --- a/extensions/scripting/lua-bindings/manual/physics3d/axlua_physics3d_manual.cpp +++ b/extensions/scripting/lua-bindings/manual/physics3d/axlua_physics3d_manual.cpp @@ -25,7 +25,7 @@ ****************************************************************************/ #include "platform/PlatformConfig.h" #include "base/Config.h" -#if defined(AX_ENABLE_3D_PHYSICS) && AX_ENABLE_BULLET_INTEGRATION +#if defined(AX_ENABLE_3D_PHYSICS) && 0 # include "lua-bindings/manual/physics3d/axlua_physics3d_manual.h" # include "lua-bindings/auto/axlua_physics3d_auto.hpp" # include "lua-bindings/manual/tolua_fix.h" diff --git a/extensions/scripting/lua-bindings/manual/physics3d/axlua_physics3d_manual.h b/extensions/scripting/lua-bindings/manual/physics3d/axlua_physics3d_manual.h index e35e37c3e494..5393eb91b50f 100644 --- a/extensions/scripting/lua-bindings/manual/physics3d/axlua_physics3d_manual.h +++ b/extensions/scripting/lua-bindings/manual/physics3d/axlua_physics3d_manual.h @@ -26,7 +26,7 @@ #ifndef SCRIPTING_AXLUA_PHYSICS3D_MANUAL_H__ #define SCRIPTING_AXLUA_PHYSICS3D_MANUAL_H__ -#if defined(AX_ENABLE_3D_PHYSICS) && AX_ENABLE_BULLET_INTEGRATION +#if defined(AX_ENABLE_3D_PHYSICS) && 0 #include "tolua++.h" diff --git a/extensions/scripting/lua-bindings/script/core/Axmol.lua b/extensions/scripting/lua-bindings/script/core/Axmol.lua index 08d1be92b392..cfc62cff9cca 100644 --- a/extensions/scripting/lua-bindings/script/core/Axmol.lua +++ b/extensions/scripting/lua-bindings/script/core/Axmol.lua @@ -290,12 +290,12 @@ function cc.c3b( _r,_g,_b ) return { r = _r, g = _g, b = _b } end ---Color4B +--Color32 function cc.c4b( _r,_g,_b,_a ) return { r = _r, g = _g, b = _b, a = _a } end ---Color4F +--ax::Color function cc.c4f( _r,_g,_b,_a ) return { r = _r, g = _g, b = _b, a = _a } end @@ -379,36 +379,21 @@ function cc.Quad3(_tl, _tr, _bl, _br) return { tl = _tl, tr = _tr, bl = _bl, br = _br } end ---V2F_C4B_T2F -function cc.V2F_C4B_T2F(_vertices, _colors, _texCoords) +--V2F_T2F_C4F +function cc.V2F_T2F_C4F(_vertices, _colors, _texCoords) return { vertices = _vertices, colors = _colors, texCoords = _texCoords } end ---V2F_C4F_T2F -function cc.V2F_C4F_T2F(_vertices, _colors, _texCoords) +--V3F_T2F_C4F +function cc.V3F_T2F_C4F(_vertices, _colors, _texCoords) return { vertices = _vertices, colors = _colors, texCoords = _texCoords } end ---V3F_C4B_T2F -function cc.V3F_C4B_T2F(_vertices, _colors, _texCoords) - return { vertices = _vertices, colors = _colors, texCoords = _texCoords } -end - ---V2F_C4B_T2F_Quad -function cc.V2F_C4B_T2F_Quad(_bl, _br, _tl, _tr) - return { bl = _bl, br = _br, tl = _tl, tr = _tr } -end - ---V3F_C4B_T2F_Quad -function cc.V3F_C4B_T2F_Quad(_tl, _bl, _tr, _br) +--V3F_T2F_C4F_Quad +function cc.V3F_T2F_C4F_Quad(_tl, _bl, _tr, _br) return { tl = _tl, bl = _bl, tr = _tr, br = _br } end ---V2F_C4F_T2F_Quad -function cc.V2F_C4F_T2F_Quad(_bl, _br, _tl, _tr) - return { bl = _bl, br = _br, tl = _tl, tr = _tr } -end - --T2F_Quad function cc.T2F_Quad(_bl, _br, _tl, _tr) return { bl = _bl, br = _br, tl = _tl, tr = _tr } diff --git a/extensions/scripting/lua-bindings/script/core/DrawPrimitives.lua b/extensions/scripting/lua-bindings/script/core/DrawPrimitives.lua index e4f50a93ff57..0c4ba3fff248 100644 --- a/extensions/scripting/lua-bindings/script/core/DrawPrimitives.lua +++ b/extensions/scripting/lua-bindings/script/core/DrawPrimitives.lua @@ -55,7 +55,7 @@ function ccPointSize(pointSize) dp_pointSize = pointSize * cc.Director:getInstance():getContentScaleFactor() end -function ccDrawColor4B(r,g,b,a) +function ccDrawColor32(r,g,b,a) dp_color[1] = r / 255.0 dp_color[2] = g / 255.0 dp_color[3] = b / 255.0 diff --git a/extensions/scripting/lua-bindings/script/framework/display.lua b/extensions/scripting/lua-bindings/script/framework/display.lua index 3a9cdb718105..d24bc555e35d 100644 --- a/extensions/scripting/lua-bindings/script/framework/display.lua +++ b/extensions/scripting/lua-bindings/script/framework/display.lua @@ -295,14 +295,14 @@ function display.newLayer(...) layer = cc.Layer:create() elseif c == 1 then -- /** creates a Layer with color. Width and height are the window size. */ - -- static LayerColor * create(const Color4B& color); + -- static LayerColor * create(const Color32& color); layer = cc.LayerColor:create(cc.convertColor(params[1], "4b")) elseif c == 2 then -- /** creates a Layer with color, width and height in Points */ - -- static LayerColor * create(const Color4B& color, const Size& size); + -- static LayerColor * create(const Color32& color, const Size& size); -- -- /** Creates a full-screen Layer with a gradient between start and end. */ - -- static LayerGradient* create(const Color4B& start, const Color4B& end); + -- static LayerGradient* create(const Color32& start, const Color32& end); local color1 = cc.convertColor(params[1], "4b") local p2 = params[2] assert(type(p2) == "table" and (p2.width or p2.r), "display.newLayer() - invalid paramerter 2") @@ -313,10 +313,10 @@ function display.newLayer(...) end elseif c == 3 then -- /** creates a Layer with color, width and height in Points */ - -- static LayerColor * create(const Color4B& color, GLfloat width, GLfloat height); + -- static LayerColor * create(const Color32& color, GLfloat width, GLfloat height); -- -- /** Creates a full-screen Layer with a gradient between start and end in the direction of v. */ - -- static LayerGradient* create(const Color4B& start, const Color4B& end, const Vec2& v); + -- static LayerGradient* create(const Color32& start, const Color32& end, const Vec2& v); local color1 = cc.convertColor(params[1], "4b") local p2 = params[2] local p2type = type(p2) diff --git a/extensions/spine/src/spine/SkeletonBatch.cpp b/extensions/spine/src/spine/SkeletonBatch.cpp index f6be3fdd12b6..0b99d041f12a 100644 --- a/extensions/spine/src/spine/SkeletonBatch.cpp +++ b/extensions/spine/src/spine/SkeletonBatch.cpp @@ -107,11 +107,11 @@ namespace spine { reset(); } - axmol::V3F_C4B_T2F *SkeletonBatch::allocateVertices(uint32_t numVertices) { + axmol::V3F_T2F_C4B *SkeletonBatch::allocateVertices(uint32_t numVertices) { if (_vertices.size() - _numVertices < numVertices) { - axmol::V3F_C4B_T2F *oldData = _vertices.data(); + auto oldData = _vertices.data(); _vertices.resize((_vertices.size() + numVertices) * 2 + 1); - axmol::V3F_C4B_T2F *newData = _vertices.data(); + auto newData = _vertices.data(); for (uint32_t i = 0; i < this->_nextFreeCommand; i++) { SkeletonCommand *command = _commandsPool[i]; SkeletonCommand::Triangles &triangles = (SkeletonCommand::Triangles &) command->getTriangles(); @@ -119,7 +119,7 @@ namespace spine { } } - axmol::V3F_C4B_T2F *vertices = _vertices.data() + _numVertices; + axmol::V3F_T2F_C4B *vertices = _vertices.data() + _numVertices; _numVertices += numVertices; return vertices; } diff --git a/extensions/spine/src/spine/SkeletonBatch.h b/extensions/spine/src/spine/SkeletonBatch.h index 4768bece33cf..8387c0cf98b2 100644 --- a/extensions/spine/src/spine/SkeletonBatch.h +++ b/extensions/spine/src/spine/SkeletonBatch.h @@ -49,7 +49,7 @@ namespace spine { void update(float delta); - axmol::V3F_C4B_T2F *allocateVertices(uint32_t numVertices); + axmol::V3F_T2F_C4B *allocateVertices(uint32_t numVertices); void deallocateVertices(uint32_t numVertices); unsigned short *allocateIndices(uint32_t numIndices); void deallocateIndices(uint32_t numVertices); @@ -74,7 +74,7 @@ namespace spine { uint32_t _nextFreeCommand; // pool of vertices - std::vector _vertices; + std::vector _vertices; uint32_t _numVertices; // pool of indices diff --git a/extensions/spine/src/spine/SkeletonRenderer.cpp b/extensions/spine/src/spine/SkeletonRenderer.cpp index e4fd0b16b5d6..7bc2a1c93935 100644 --- a/extensions/spine/src/spine/SkeletonRenderer.cpp +++ b/extensions/spine/src/spine/SkeletonRenderer.cpp @@ -44,7 +44,6 @@ namespace spine { BlendFunc makeBlendFunc(BlendMode blendMode, bool premultipliedAlpha); void transformWorldVertices(float *dstCoord, int coordCount, Skeleton &skeleton, int startSlotIndex, int endSlotIndex); bool cullRectangle(Renderer *renderer, const Mat4 &transform, const axmol::Rect &rect); - Color4B ColorToColor4B(const Color &color); bool slotIsOutRange(Slot &slot, int startSlotIndex, int endSlotIndex); bool nothingToDraw(Slot &slot, int startSlotIndex, int endSlotIndex); }// namespace @@ -287,11 +286,11 @@ namespace spine { triangles.vertCount = 4; assert(triangles.vertCount == 4); for (int v = 0, i = 0; v < triangles.vertCount; v++, i += 2) { - auto &texCoords = triangles.verts[v].texCoords; + auto &texCoords = triangles.verts[v].texCoord; texCoords.u = attachment->getUVs()[i]; texCoords.v = attachment->getUVs()[i + 1]; } - dstStride = sizeof(V3F_C4B_T2F) / sizeof(float); + dstStride = sizeof(V3F_T2F_C4B) / sizeof(float); dstTriangleVertices = reinterpret_cast(triangles.verts); } else { trianglesTwoColor.indices = quadIndices; @@ -300,7 +299,7 @@ namespace spine { trianglesTwoColor.vertCount = 4; assert(trianglesTwoColor.vertCount == 4); for (int v = 0, i = 0; v < trianglesTwoColor.vertCount; v++, i += 2) { - auto &texCoords = trianglesTwoColor.verts[v].texCoords; + auto &texCoords = trianglesTwoColor.verts[v].texCoord; texCoords.u = attachment->getUVs()[i]; texCoords.v = attachment->getUVs()[i + 1]; } @@ -325,12 +324,12 @@ namespace spine { triangles.verts = batch->allocateVertices((int)attachment->getWorldVerticesLength() / 2); triangles.vertCount = (int)attachment->getWorldVerticesLength() / 2; for (int v = 0, i = 0; v < triangles.vertCount; v++, i += 2) { - auto &texCoords = triangles.verts[v].texCoords; + auto &texCoords = triangles.verts[v].texCoord; texCoords.u = attachment->getUVs()[i]; texCoords.v = attachment->getUVs()[i + 1]; } dstTriangleVertices = (float *) triangles.verts; - dstStride = sizeof(V3F_C4B_T2F) / sizeof(float); + dstStride = sizeof(V3F_T2F_C4B) / sizeof(float); dstVertexCount = triangles.vertCount; } else { trianglesTwoColor.indices = attachment->getTriangles().buffer(); @@ -338,7 +337,7 @@ namespace spine { trianglesTwoColor.verts = twoColorBatch->allocateVertices((int)attachment->getWorldVerticesLength() / 2); trianglesTwoColor.vertCount = (int)attachment->getWorldVerticesLength() / 2; for (int v = 0, i = 0; v < trianglesTwoColor.vertCount; v++, i += 2) { - auto &texCoords = trianglesTwoColor.verts[v].texCoords; + auto &texCoords = trianglesTwoColor.verts[v].texCoord; texCoords.u = attachment->getUVs()[i]; texCoords.v = attachment->getUVs()[i + 1]; } @@ -385,14 +384,14 @@ namespace spine { color.b *= color.a; } - const axmol::Color4B color4B = ColorToColor4B(color); - const axmol::Color4B darkColor4B = ColorToColor4B(darkColor); + const axmol::Color color_r{color}; + const axmol::Color darkColor_r{darkColor}; const BlendFunc blendFunc = makeBlendFunc(slot->getData().getBlendMode(), texture->hasPremultipliedAlpha()); _blendFunc = blendFunc; if (hasSingleTint) { if (_clipper->isClipping()) { - _clipper->clipTriangles((float *) &triangles.verts[0].vertices, triangles.indices, triangles.indexCount, (float *) &triangles.verts[0].texCoords, sizeof(axmol::V3F_C4B_T2F) / 4); + _clipper->clipTriangles((float *) &triangles.verts[0].position, triangles.indices, triangles.indexCount, (float *) &triangles.verts[0].texCoord, sizeof(axmol::V3F_T2F_C4B) / 4); batch->deallocateVertices(triangles.vertCount); if (_clipper->getClippedTriangles().size() == 0) { @@ -408,24 +407,24 @@ namespace spine { const float* verts = _clipper->getClippedVertices().buffer(); const float* uvs = _clipper->getClippedUVs().buffer(); - V3F_C4B_T2F* vertex = triangles.verts; + auto vertex = triangles.verts; for (int v = 0, vn = triangles.vertCount, vv = 0; v < vn; ++v, vv += 2, ++vertex) { - vertex->vertices.x = verts[vv]; - vertex->vertices.y = verts[vv + 1]; - vertex->texCoords.u = uvs[vv]; - vertex->texCoords.v = uvs[vv + 1]; - vertex->colors = color4B; + vertex->position.x = verts[vv]; + vertex->position.y = verts[vv + 1]; + vertex->texCoord.u = uvs[vv]; + vertex->texCoord.v = uvs[vv + 1]; + vertex->color = color_r; } batch->addCommand(renderer, _globalZOrder, texture, _programState, blendFunc, triangles, transform, transformFlags); } else { // Not clipping. - V3F_C4B_T2F* vertex = triangles.verts; + auto vertex = triangles.verts; for (int v = 0, vn = triangles.vertCount; v < vn; ++v, ++vertex) { - vertex->colors = color4B; + vertex->color = color_r; } batch->addCommand(renderer, _globalZOrder, texture, _programState, blendFunc, triangles, transform, transformFlags); } @@ -433,7 +432,7 @@ namespace spine { // Two color tinting. if (_clipper->isClipping()) { - _clipper->clipTriangles((float *) &trianglesTwoColor.verts[0].position, trianglesTwoColor.indices, trianglesTwoColor.indexCount, (float *) &trianglesTwoColor.verts[0].texCoords, sizeof(V3F_C4B_C4B_T2F) / 4); + _clipper->clipTriangles((float *) &trianglesTwoColor.verts[0].position, trianglesTwoColor.indices, trianglesTwoColor.indexCount, (float *) &trianglesTwoColor.verts[0].texCoord, sizeof(V3F_C4B_C4B_T2F) / 4); twoColorBatch->deallocateVertices(trianglesTwoColor.vertCount); if (_clipper->getClippedTriangles().size() == 0) { @@ -456,10 +455,10 @@ namespace spine { { vertex->position.x = verts[vv]; vertex->position.y = verts[vv + 1]; - vertex->texCoords.u = uvs[vv]; - vertex->texCoords.v = uvs[vv + 1]; - vertex->color = color4B; - vertex->color2 = darkColor4B; + vertex->texCoord.u = uvs[vv]; + vertex->texCoord.v = uvs[vv + 1]; + vertex->color = color_r; + vertex->color2 = darkColor_r; } lastTwoColorTrianglesCommand = twoColorBatch->addCommand(renderer, _globalZOrder, texture, _programState, blendFunc, trianglesTwoColor, transform, transformFlags); } else { @@ -467,8 +466,8 @@ namespace spine { for (int v = 0, vn = trianglesTwoColor.vertCount; v < vn; ++v, ++vertex) { - vertex->color = color4B; - vertex->color2 = darkColor4B; + vertex->color = color_r; + vertex->color2 = darkColor_r; } lastTwoColorTrianglesCommand = twoColorBatch->addCommand(renderer, _globalZOrder, texture, _programState, blendFunc, trianglesTwoColor, transform, transformFlags); } @@ -539,13 +538,12 @@ namespace spine { {brect.origin.x + brect.size.width, brect.origin.y}, {brect.origin.x + brect.size.width, brect.origin.y + brect.size.height}, {brect.origin.x, brect.origin.y + brect.size.height}}; - drawNode->drawPoly(points, 4, true, Color4B::GREEN, 2.0f); + drawNode->drawPoly(points, 4, true, ax::Color::GREEN, 2.0f); } if (_debugSlots) { // Slots. - // DrawPrimitives::setDrawColor4B(0, 0, 255, 255); - V3F_C4B_T2F_Quad quad; + // DrawPrimitives::setDrawColor32(0, 0, 255, 255); for (int i = 0, n = (int)_skeleton->getSlots().size(); i < n; i++) { Slot *slot = _skeleton->getDrawOrder()[i]; @@ -565,7 +563,7 @@ namespace spine { {worldVertices[2], worldVertices[3]}, {worldVertices[4], worldVertices[5]}, {worldVertices[6], worldVertices[7]}}; - drawNode->drawPoly(points, 4, true, Color4B::BLUE, 2.0f); + drawNode->drawPoly(points, 4, true, ax::Color::BLUE, 2.0f); } } @@ -576,15 +574,15 @@ namespace spine { if (!bone->isActive()) continue; float x = bone->getData().getLength() * bone->getA() + bone->getWorldX(); float y = bone->getData().getLength() * bone->getC() + bone->getWorldY(); - drawNode->drawLine(Vec2(bone->getWorldX(), bone->getWorldY()), Vec2(x, y), Color4B::RED, 2.0f); + drawNode->drawLine(Vec2(bone->getWorldX(), bone->getWorldY()), Vec2(x, y), ax::Color::RED, 2.0f); } // Bone origins. - auto color = Color4B::BLUE;// Root bone is blue. + auto color = ax::Color::BLUE; // Root bone is blue. for (int i = 0, n = (int)_skeleton->getBones().size(); i < n; i++) { Bone *bone = _skeleton->getBones()[i]; if (!bone->isActive()) continue; drawNode->drawPoint(Vec2(bone->getWorldX(), bone->getWorldY()), 4, color); - if (i == 0) color = Color4B::GREEN; + if (i == 0) color = ax::Color::GREEN; } } @@ -607,7 +605,7 @@ namespace spine { worldCoord + (idx0 * 2), worldCoord + (idx1 * 2), worldCoord + (idx2 * 2)}; - drawNode->drawPoly(v, 3, true, Color4B::YELLOW, 2.0f); + drawNode->drawPoly(v, 3, true, ax::Color::YELLOW, 2.0f); } VLA_FREE(worldCoord); } @@ -916,10 +914,6 @@ namespace spine { return !visibleRect.containsPoint(v2p); } - - Color4B ColorToColor4B(const Color &color) { - return {(uint8_t) (color.r * 255.f), (uint8_t) (color.g * 255.f), (uint8_t) (color.b * 255.f), (uint8_t) (color.a * 255.f)}; - } }// namespace }// namespace spine diff --git a/extensions/spine/src/spine/SkeletonTwoColorBatch.cpp b/extensions/spine/src/spine/SkeletonTwoColorBatch.cpp index 71cc7e359a72..0a91fcf92e17 100644 --- a/extensions/spine/src/spine/SkeletonTwoColorBatch.cpp +++ b/extensions/spine/src/spine/SkeletonTwoColorBatch.cpp @@ -69,7 +69,7 @@ namespace { vertexLayout->setAttrib("a_color2", locColor2, backend::VertexFormat::UBYTE4, offsetof(spine::V3F_C4B_C4B_T2F, color2), true); vertexLayout->setAttrib("a_texCoord", locTexcoord, backend::VertexFormat::FLOAT2, - offsetof(spine::V3F_C4B_C4B_T2F, texCoords), false); + offsetof(spine::V3F_C4B_C4B_T2F, texCoord), false); vertexLayout->setStride(sizeof(spine::V3F_C4B_C4B_T2F)); } diff --git a/extensions/spine/src/spine/SkeletonTwoColorBatch.h b/extensions/spine/src/spine/SkeletonTwoColorBatch.h index b928c60bb481..83a12d3f183e 100644 --- a/extensions/spine/src/spine/SkeletonTwoColorBatch.h +++ b/extensions/spine/src/spine/SkeletonTwoColorBatch.h @@ -38,9 +38,9 @@ namespace spine { struct V3F_C4B_C4B_T2F { axmol::Vec3 position; - axmol::Color4B color; - axmol::Color4B color2; - axmol::Tex2F texCoords; + axmol::Color32 color; + axmol::Color32 color2; + axmol::Tex2F texCoord; }; struct TwoColorTriangles { diff --git a/templates/common/proj.android/app/AndroidManifest.xml b/templates/common/proj.android/app/AndroidManifest.xml index 07dbf865abed..2aa68c192353 100644 --- a/templates/common/proj.android/app/AndroidManifest.xml +++ b/templates/common/proj.android/app/AndroidManifest.xml @@ -18,7 +18,7 @@ android:screenOrientation="sensorLandscape" android:configChanges="orientation|keyboardHidden|screenSize" android:label="@string/app_name" - android:theme="@android:style/Theme.NoTitleBar.Fullscreen" + android:theme="@style/Theme.AppCompat.Light.NoActionBar" android:launchMode="singleTask" android:taskAffinity="" android:exported="true" > diff --git a/templates/common/proj.android/app/build.gradle b/templates/common/proj.android/app/build.gradle index 0dcf6eac5669..bef3ebc40d4e 100644 --- a/templates/common/proj.android/app/build.gradle +++ b/templates/common/proj.android/app/build.gradle @@ -117,6 +117,9 @@ android.applicationVariants.configureEach { variant -> dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation project(':libaxmol') + + def appcompat_ver = buildProfiles['appcompat'] + implementation "androidx.appcompat:appcompat:$appcompat_ver" } project.afterEvaluate { diff --git a/templates/cpp/Source/MainScene.cpp b/templates/cpp/Source/MainScene.cpp index 8190a5abb272..f6d3902fa09a 100644 --- a/templates/cpp/Source/MainScene.cpp +++ b/templates/cpp/Source/MainScene.cpp @@ -133,7 +133,7 @@ bool MainScene::init() drawNode->setPosition(Vec2(0, 0)); addChild(drawNode); - drawNode->drawRect(safeArea.origin + Vec2(1, 1), safeArea.origin + safeArea.size, Color4F::BLUE); + drawNode->drawRect(safeArea.origin + Vec2(1, 1), safeArea.origin + safeArea.size, Color::BLUE); } // scheduleUpdate() is required to ensure update(float) is called on every loop diff --git a/tests/cpp-tests/CMakeLists.txt b/tests/cpp-tests/CMakeLists.txt index 185aab4830b2..7bc7c9a124e5 100644 --- a/tests/cpp-tests/CMakeLists.txt +++ b/tests/cpp-tests/CMakeLists.txt @@ -404,74 +404,8 @@ list(APPEND GAME_SOURCE ) if((WINDOWS OR MACOSX OR LINUX OR WASM) AND (NOT WINRT)) - list(APPEND GAME_HEADER - Source/Box2DTestBed/tests/test.h - Source/Box2DTestBed/tests/settings.h - Source/Box2DTestBed/Box2DTestBed.h - ) - - list(APPEND GAME_SOURCE - Source/Box2DTestBed/Box2DTestBed.cpp - Source/Box2DTestBed/test.cpp - Source/Box2DTestBed/tests/add_pair.cpp - Source/Box2DTestBed/tests/apply_force.cpp - Source/Box2DTestBed/tests/body_types.cpp - Source/Box2DTestBed/tests/box_stack.cpp - Source/Box2DTestBed/tests/breakable.cpp - Source/Box2DTestBed/tests/bridge.cpp - Source/Box2DTestBed/tests/bullet_test.cpp - Source/Box2DTestBed/tests/cantilever.cpp - Source/Box2DTestBed/tests/car.cpp - Source/Box2DTestBed/tests/chain.cpp - Source/Box2DTestBed/tests/chain_problem.cpp - Source/Box2DTestBed/tests/character_collision.cpp - Source/Box2DTestBed/tests/circle_stack.cpp - Source/Box2DTestBed/tests/collision_filtering.cpp - Source/Box2DTestBed/tests/collision_processing.cpp - Source/Box2DTestBed/tests/compound_shapes.cpp - Source/Box2DTestBed/tests/confined.cpp - Source/Box2DTestBed/tests/continuous_test.cpp - Source/Box2DTestBed/tests/convex_hull.cpp - Source/Box2DTestBed/tests/conveyor_belt.cpp - Source/Box2DTestBed/tests/distance_joint.cpp - Source/Box2DTestBed/tests/distance_test.cpp - Source/Box2DTestBed/tests/dominos.cpp - Source/Box2DTestBed/tests/dump_loader.cpp - Source/Box2DTestBed/tests/dynamic_tree.cpp - Source/Box2DTestBed/tests/edge_shapes.cpp - Source/Box2DTestBed/tests/edge_test.cpp - Source/Box2DTestBed/tests/friction.cpp - Source/Box2DTestBed/tests/gear_joint.cpp - Source/Box2DTestBed/tests/heavy1.cpp - Source/Box2DTestBed/tests/heavy2.cpp - Source/Box2DTestBed/tests/mobile_balanced.cpp - Source/Box2DTestBed/tests/mobile_unbalanced.cpp - Source/Box2DTestBed/tests/motor_joint.cpp - Source/Box2DTestBed/tests/pinball.cpp - Source/Box2DTestBed/tests/platformer.cpp - Source/Box2DTestBed/tests/polygon_collision.cpp - Source/Box2DTestBed/tests/polygon_shapes.cpp - Source/Box2DTestBed/tests/prismatic_joint.cpp - Source/Box2DTestBed/tests/pulley_joint.cpp - Source/Box2DTestBed/tests/pyramid.cpp - Source/Box2DTestBed/tests/ray_cast.cpp - Source/Box2DTestBed/tests/restitution.cpp - Source/Box2DTestBed/tests/revolute_joint.cpp - Source/Box2DTestBed/tests/rope.cpp - Source/Box2DTestBed/tests/sensor.cpp - Source/Box2DTestBed/tests/shape_cast.cpp - Source/Box2DTestBed/tests/shape_editing.cpp - Source/Box2DTestBed/tests/skier.cpp - Source/Box2DTestBed/tests/slider_crank_1.cpp - Source/Box2DTestBed/tests/slider_crank_2.cpp - Source/Box2DTestBed/tests/theo_jansen.cpp - Source/Box2DTestBed/tests/tiles.cpp - Source/Box2DTestBed/tests/time_of_impact.cpp - Source/Box2DTestBed/tests/tumbler.cpp - Source/Box2DTestBed/tests/web.cpp - Source/Box2DTestBed/tests/wheel_joint.cpp - Source/Box2DTestBed/tests/wrecking_ball.cpp - ) + file(GLOB_RECURSE BOX2D_TESTBED_SOURCES Source/Box2DTestBed/*.h;Source/Box2DTestBed/*.cpp) + list(APPEND GAME_SOURCE ${BOX2D_TESTBED_SOURCES}) endif() list(APPEND GAME_HEADER @@ -484,51 +418,12 @@ list(APPEND GAME_SOURCE ) list(APPEND GAME_HEADER Source/PhysicsTest/PhysicsTest.h - Source/ChipmunkTest/ChipmunkTest.h ) list(APPEND GAME_SOURCE Source/PhysicsTest/PhysicsTest.cpp - Source/ChipmunkTest/ChipmunkTest.cpp ) -if(WINDOWS OR MACOSX OR LINUX OR WASM) - list(APPEND GAME_HEADER - Source/ChipmunkTestBed/demo/ChipmunkDemo.h - Source/ChipmunkTestBed/ChipmunkTestBed.h - ) - set (TESTBED_C_SORUCES - Source/ChipmunkTestBed/demo/Bench.cpp - Source/ChipmunkTestBed/demo/Chains.cpp - Source/ChipmunkTestBed/demo/Convex.cpp - Source/ChipmunkTestBed/demo/Crane.cpp - Source/ChipmunkTestBed/demo/Joints.cpp - Source/ChipmunkTestBed/demo/LogoSmash.cpp - Source/ChipmunkTestBed/demo/OneWay.cpp - Source/ChipmunkTestBed/demo/Planet.cpp - Source/ChipmunkTestBed/demo/Player.cpp - Source/ChipmunkTestBed/demo/Plink.cpp - Source/ChipmunkTestBed/demo/Pump.cpp - Source/ChipmunkTestBed/demo/PyramidStack.cpp - Source/ChipmunkTestBed/demo/PyramidTopple.cpp - Source/ChipmunkTestBed/demo/Shatter.cpp - Source/ChipmunkTestBed/demo/Springies.cpp - Source/ChipmunkTestBed/demo/Sticky.cpp - Source/ChipmunkTestBed/demo/Tank.cpp - Source/ChipmunkTestBed/demo/TheoJansen.cpp - Source/ChipmunkTestBed/demo/Buoyancy.cpp - Source/ChipmunkTestBed/demo/ContactGraph.cpp - Source/ChipmunkTestBed/demo/Example.cpp - Source/ChipmunkTestBed/demo/Query.cpp - Source/ChipmunkTestBed/demo/Slice.cpp - Source/ChipmunkTestBed/demo/Unicycle.cpp - Source/ChipmunkTestBed/demo/Tumble.cpp) - list(APPEND GAME_SOURCE - ${TESTBED_C_SORUCES} - Source/ChipmunkTestBed/ChipmunkTestBed.cpp - ) -endif() - if (AX_ENABLE_EXT_EFFEKSEER) list(APPEND GAME_HEADER Source/EffekseerTest/EffekseerTest.h) list(APPEND GAME_SOURCE Source/EffekseerTest/EffekseerTest.cpp) diff --git a/tests/cpp-tests/Source/ActionsProgressTest/ActionsProgressTest.cpp b/tests/cpp-tests/Source/ActionsProgressTest/ActionsProgressTest.cpp index 81ea34121351..10138fd3a3f1 100644 --- a/tests/cpp-tests/Source/ActionsProgressTest/ActionsProgressTest.cpp +++ b/tests/cpp-tests/Source/ActionsProgressTest/ActionsProgressTest.cpp @@ -58,7 +58,7 @@ void SpriteDemo::onEnter() { TestCase::onEnter(); - auto background = LayerColor::create(Color4B(255, 0, 0, 255)); + auto background = LayerColor::create(Color32(255, 0, 0, 255)); addChild(background, -10); } diff --git a/tests/cpp-tests/Source/ActionsTest/ActionsTest.cpp b/tests/cpp-tests/Source/ActionsTest/ActionsTest.cpp index c900bb35270e..b4b81a55200a 100644 --- a/tests/cpp-tests/Source/ActionsTest/ActionsTest.cpp +++ b/tests/cpp-tests/Source/ActionsTest/ActionsTest.cpp @@ -340,7 +340,7 @@ void ActionRotationalSkewVSStandardSkew::onEnter() Size boxSize(100.0f, 100.0f); - auto box = LayerColor::create(Color4B(255, 255, 0, 255)); + auto box = LayerColor::create(Color32(255, 255, 0, 255)); box->setAnchorPoint(Vec2(0.5f, 0.5f)); box->setContentSize(boxSize); box->setIgnoreAnchorPointForPosition(false); @@ -356,7 +356,7 @@ void ActionRotationalSkewVSStandardSkew::onEnter() box->runAction(Sequence::create(actionTo, actionToBack, nullptr)); - box = LayerColor::create(Color4B(255, 255, 0, 255)); + box = LayerColor::create(Color32(255, 255, 0, 255)); box->setAnchorPoint(Vec2(0.5f, 0.5f)); box->setContentSize(boxSize); box->setIgnoreAnchorPointForPosition(false); @@ -386,19 +386,19 @@ void ActionSkewRotateScale::onEnter() Size boxSize(100.0f, 100.0f); - auto box = LayerColor::create(Color4B(255, 255, 0, 255)); + auto box = LayerColor::create(Color32(255, 255, 0, 255)); box->setAnchorPoint(Vec2(0.0f, 0.0f)); box->setPosition(190, 110); box->setContentSize(boxSize); static float markrside = 10.0f; - auto uL = LayerColor::create(Color4B(255, 0, 0, 255)); + auto uL = LayerColor::create(Color32(255, 0, 0, 255)); box->addChild(uL); uL->setContentSize(Size(markrside, markrside)); uL->setPosition(0.f, boxSize.height - markrside); uL->setAnchorPoint(Vec2(0.0f, 0.0f)); - auto uR = LayerColor::create(Color4B(0, 0, 255, 255)); + auto uR = LayerColor::create(Color32(0, 0, 255, 255)); box->addChild(uR); uR->setContentSize(Size(markrside, markrside)); uR->setPosition(boxSize.width - markrside, boxSize.height - markrside); @@ -1244,7 +1244,7 @@ void ActionFollow::onEnter() float y = s.height; Vec2 vertices[] = {Vec2(5.0f, 5.0f), Vec2(x - 5, 5.0f), Vec2(x - 5, y - 5), Vec2(5.0f, y - 5)}; - drawNode->drawPoly(vertices, 4, true, Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); + drawNode->drawPoly(vertices, 4, true, Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); this->addChild(drawNode); @@ -1281,7 +1281,7 @@ void ActionFollowWithOffset::onEnter() float y = s.height; Vec2 vertices[] = {Vec2(5.0f, 5.0f), Vec2(x - 5, 5.0f), Vec2(x - 5, y - 5), Vec2(5.0f, y - 5)}; - drawNode->drawPoly(vertices, 4, true, Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); + drawNode->drawPoly(vertices, 4, true, Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); this->addChild(drawNode); @@ -1523,7 +1523,7 @@ void ActionCatmullRomStacked::onEnter() auto drawNode1 = DrawNode::create(); drawNode1->setPosition(Vec2(50.0f, 50.0f)); - drawNode1->drawCatmullRom(array, 50, Color4F(1.0f, 1.0f, 0.0f, 0.5f)); + drawNode1->drawCatmullRom(array, 50, Color(1.0f, 1.0f, 0.0f, 0.5f)); this->addChild(drawNode1); // @@ -1552,7 +1552,7 @@ void ActionCatmullRomStacked::onEnter() MoveBy::create(0.05f, Vec2(-10.0f, 0.0f)), nullptr))); auto drawNode2 = DrawNode::create(); - drawNode2->drawCatmullRom(array2, 50, Color4F(1.0, 0.0, 0.0, 0.5)); + drawNode2->drawCatmullRom(array2, 50, Color(1.0, 0.0, 0.0, 0.5)); this->addChild(drawNode2); } @@ -1605,7 +1605,7 @@ void ActionCardinalSplineStacked::onEnter() auto drawNode1 = DrawNode::create(); drawNode1->setPosition(Vec2(50.0f, 50.0f)); - drawNode1->drawCardinalSpline(array, 0, 100, Color4F(1.0f, 0.0f, 1.0f, 1.0f)); + drawNode1->drawCardinalSpline(array, 0, 100, Color(1.0f, 0.0f, 1.0f, 1.0f)); this->addChild(drawNode1); // @@ -1628,7 +1628,7 @@ void ActionCardinalSplineStacked::onEnter() auto drawNode2 = DrawNode::create(); drawNode2->setPosition(Vec2(s.width / 2, 50.0f)); - drawNode2->drawCardinalSpline(array, 1, 100, Color4F(0.0f, 0.0f, 1.0f, 1.0f)); + drawNode2->drawCardinalSpline(array, 1, 100, Color(0.0f, 0.0f, 1.0f, 1.0f)); this->addChild(drawNode2); } @@ -1950,7 +1950,7 @@ void ActionCatmullRom::onEnter() auto drawNode1 = DrawNode::create(); drawNode1->setPosition(Vec2(50.0f, 50.0f)); - drawNode1->drawCatmullRom(array, 50, Color4F(1.0f, 0.0f, 1.0f, 1.0f)); + drawNode1->drawCatmullRom(array, 50, Color(1.0f, 0.0f, 1.0f, 1.0f)); this->addChild(drawNode1); // @@ -1976,7 +1976,7 @@ void ActionCatmullRom::onEnter() _kathia->runAction(seq2); auto drawNode2 = DrawNode::create(); - drawNode2->drawCatmullRom(array2, 50, Color4F(0.0f, 1.0f, 1.0f, 1.0f)); + drawNode2->drawCatmullRom(array2, 50, Color(0.0f, 1.0f, 1.0f, 1.0f)); this->addChild(drawNode2); } @@ -2026,7 +2026,7 @@ void ActionCardinalSpline::onEnter() auto drawNode1 = DrawNode::create(); drawNode1->setPosition(Vec2(50.0f, 50.0f)); - drawNode1->drawCardinalSpline(array, 0, 100, Color4F(1.0f, 0.0f, 1.0f, 1.0f)); + drawNode1->drawCardinalSpline(array, 0, 100, Color(1.0f, 0.0f, 1.0f, 1.0f)); this->addChild(drawNode1); // @@ -2045,7 +2045,7 @@ void ActionCardinalSpline::onEnter() auto drawNode2 = DrawNode::create(); drawNode2->setPosition(Vec2(s.width / 2, 50.0f)); - drawNode2->drawCardinalSpline(array, 1, 100, Color4F(1.0f, 0.0f, 1.0f, 1.0f)); + drawNode2->drawCardinalSpline(array, 1, 100, Color(1.0f, 0.0f, 1.0f, 1.0f)); this->addChild(drawNode2); } diff --git a/tests/cpp-tests/Source/BaseTest.cpp b/tests/cpp-tests/Source/BaseTest.cpp index 84fbef1d5c1e..6d5cfd5787a6 100644 --- a/tests/cpp-tests/Source/BaseTest.cpp +++ b/tests/cpp-tests/Source/BaseTest.cpp @@ -343,6 +343,17 @@ void TestSuite::enterNextTest() Director::getInstance()->replaceScene(scene); } +void TestSuite::enterTest(int index) { + _currTestIndex = index % _childTestNames.size(); + + auto scene = _testCallbacks[_currTestIndex](); + auto testCase = getTestCase(scene); + testCase->setTestSuite(this); + testCase->setTestCaseName(_childTestNames[_currTestIndex]); + + Director::getInstance()->replaceScene(scene); +} + void TestSuite::enterPreviousTest() { if (_currTestIndex > 0) diff --git a/tests/cpp-tests/Source/BaseTest.h b/tests/cpp-tests/Source/BaseTest.h index 8951d1123f6c..210c5f9fb77d 100644 --- a/tests/cpp-tests/Source/BaseTest.h +++ b/tests/cpp-tests/Source/BaseTest.h @@ -172,6 +172,8 @@ class TestSuite : public TestBase virtual void enterNextTest(); virtual void enterPreviousTest(); + void enterTest(int index); + int getCurrTestIndex() { return _currTestIndex; } virtual void runThisTest() override; diff --git a/tests/cpp-tests/Source/Box2DTest/Box2dTest.cpp b/tests/cpp-tests/Source/Box2DTest/Box2dTest.cpp index 4e07c56d12cb..b98ca7b42f9b 100644 --- a/tests/cpp-tests/Source/Box2DTest/Box2dTest.cpp +++ b/tests/cpp-tests/Source/Box2DTest/Box2dTest.cpp @@ -94,154 +94,139 @@ bool Box2DTest::init() this->addChild(menu); menu->setPosition(VisibleRect::right().x - 100, VisibleRect::top().y - 60); - drawBox2D = g_debugDraw.GetDrawNode(); - addChild(drawBox2D, 100); - drawBox2D->setOpacity(150); - scheduleUpdate(); return true; } -Box2DTest::Box2DTest() : _spriteTexture(nullptr), world(nullptr) {} +Box2DTest::Box2DTest() : _spriteTexture(nullptr) {} Box2DTest::~Box2DTest() { - AX_SAFE_DELETE(world); + b2DestroyWorld(world); } void Box2DTest::toggleDebugCallback(Object* sender) { showDebugDraw = !showDebugDraw; - drawBox2D->clear(); + _debugDrawNode->setVisible(showDebugDraw); } -void Box2DTest::initPhysics() +b2BodyId Box2DTest::createRigibody(b2BodyDef* def) { - b2Vec2 gravity; - gravity.Set(0.0f, -10.0f); - world = new b2World(gravity); + auto bodyId = b2CreateBody(world, def); + return bodyId; +} - // Do we want to let bodies sleep? - world->SetAllowSleeping(true); +void Box2DTest::initPhysics() +{ + b2Vec2 gravity = {0.0f, -10.0f}; + b2WorldDef worldDef = b2DefaultWorldDef(); + worldDef.gravity = gravity; + world = b2CreateWorld(&worldDef); - world->SetContinuousPhysics(true); + // debug draw node for world + _debugDrawNode = utils::createInstance(&PhysicsDebugNode::initWithWorld, world); + addChild(_debugDrawNode, 100); + _debugDrawNode->setOpacity(150); - // Define the ground body. - b2BodyDef groundBodyDef; - groundBodyDef.position.Set(0, 0); // bottom-left corner + // Do we want to let bodies sleep? + b2World_EnableSleeping(world, true); + b2World_EnableContinuous(world, true); - // Call the body factory which allocates memory for the ground body - // from a pool and creates the ground box shape (also from a pool). - // The body is also added to the world. - b2Body* groundBody = world->CreateBody(&groundBodyDef); + // The segment ground + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = createRigibody(&bodyDef); - // Define the ground box shape. - b2EdgeShape groundBox; + b2ShapeDef shapeDef = b2DefaultShapeDef(); - // bottom - groundBox.SetTwoSided(b2Vec2(VisibleRect::leftBottom().x / PTM_RATIO, VisibleRect::leftBottom().y / PTM_RATIO), - b2Vec2(VisibleRect::rightBottom().x / PTM_RATIO, VisibleRect::rightBottom().y / PTM_RATIO)); - groundBody->CreateFixture(&groundBox, 0); + auto conerOffset = 1.0f / PTM_RATIO; + auto viewRect = VisibleRect::getVisibleRect(); + b2Segment segment = {{conerOffset, conerOffset}, {viewRect.size.width / PTM_RATIO - conerOffset, conerOffset}}; + b2CreateSegmentShape(groundId, &shapeDef, &segment); // bottom - // top - groundBox.SetTwoSided(b2Vec2(VisibleRect::leftTop().x / PTM_RATIO, VisibleRect::leftTop().y / PTM_RATIO), - b2Vec2(VisibleRect::rightTop().x / PTM_RATIO, VisibleRect::rightTop().y / PTM_RATIO)); - groundBody->CreateFixture(&groundBox, 0); + segment = {{conerOffset, viewRect.size.height / PTM_RATIO - conerOffset}, + {viewRect.size.width / PTM_RATIO - conerOffset, viewRect.size.height / PTM_RATIO - conerOffset}}; + b2CreateSegmentShape(groundId, &shapeDef, &segment); // top - // left - groundBox.SetTwoSided(b2Vec2(VisibleRect::leftTop().x / PTM_RATIO, VisibleRect::leftTop().y / PTM_RATIO), - b2Vec2(VisibleRect::leftBottom().x / PTM_RATIO, VisibleRect::leftBottom().y / PTM_RATIO)); - groundBody->CreateFixture(&groundBox, 0); + segment = {{conerOffset, conerOffset}, {conerOffset, viewRect.size.height / PTM_RATIO - conerOffset}}; + b2CreateSegmentShape(groundId, &shapeDef, &segment); // left - // right - groundBox.SetTwoSided(b2Vec2(VisibleRect::rightBottom().x / PTM_RATIO, VisibleRect::rightBottom().y / PTM_RATIO), - b2Vec2(VisibleRect::rightTop().x / PTM_RATIO, VisibleRect::rightTop().y / PTM_RATIO)); - groundBody->CreateFixture(&groundBox, 0); + segment = {{viewRect.size.width / PTM_RATIO - conerOffset, conerOffset}, + {viewRect.size.width / PTM_RATIO - conerOffset, viewRect.size.height / PTM_RATIO - conerOffset}}; + b2CreateSegmentShape(groundId, &shapeDef, &segment); // right // Small triangle b2Vec2 vertices[3]; - vertices[0].Set(-1.0f, 0.0f); - vertices[1].Set(1.0f, 0.0f); - vertices[2].Set(0.0f, 2.0f); - - b2PolygonShape polygon; - polygon.Set(vertices, 3); - - b2FixtureDef triangleShapeDef; - triangleShapeDef.shape = &polygon; - triangleShapeDef.density = 1.0f; - - b2BodyDef triangleBodyDef; - triangleBodyDef.type = b2_dynamicBody; - triangleBodyDef.position.Set(rand() % 13 + 3, 4); - - b2Body* body1 = world->CreateBody(&triangleBodyDef); - body1->CreateFixture(&triangleShapeDef); + vertices[0] = b2Vec2{-1.0f, 0.0f}; + vertices[1] = b2Vec2{1.0f, 0.0f}; + vertices[2] = b2Vec2{0.0f, 2.0f}; + b2Hull hull = b2ComputeHull(vertices, 3); + b2Polygon polygon = b2MakePolygon(&hull, 0.0f); + + b2BodyDef triangleBodyDef = b2DefaultBodyDef(); + triangleBodyDef.type = b2_dynamicBody; + triangleBodyDef.position = b2Vec2{static_cast(rand() % 13 + 3), 4.0f}; // bottom-left corner + auto body1 = createRigibody(&triangleBodyDef); + b2ShapeDef triangleShapeDef = b2DefaultShapeDef(); + triangleShapeDef.density = 1.0f; + b2CreatePolygonShape(body1, &triangleShapeDef, &polygon); // Large triangle (recycle definitions) vertices[0] *= 2.0f; vertices[1] *= 2.0f; vertices[2] *= 2.0f; - polygon.Set(vertices, 3); - - triangleBodyDef.position.Set(rand() % 13 + 3, 4); - b2Body* body2 = world->CreateBody(&triangleBodyDef); - body2->CreateFixture(&triangleShapeDef); + triangleBodyDef.position = b2Vec2{static_cast(rand() % 13 + 3), 4.0f}; + auto body2 = createRigibody(&triangleBodyDef); + hull = b2ComputeHull(vertices, 3); + polygon = b2MakePolygon(&hull, 0.0f); + b2CreatePolygonShape(body2, &triangleShapeDef, &polygon); // Small box - polygon.SetAsBox(1.0f, 0.5f); + polygon = b2MakeBox(1.0f, 0.5f); // .SetAsBox(1.0f, 0.5f); - b2FixtureDef boxShapeDef; - boxShapeDef.shape = &polygon; + auto boxShapeDef = b2DefaultShapeDef(); boxShapeDef.density = 1.0f; - b2BodyDef boxBodyDef; - boxBodyDef.type = b2_dynamicBody; - boxBodyDef.position.Set(rand() % 13 + 3, 4); + b2BodyDef boxBodyDef = b2DefaultBodyDef(); + boxBodyDef.type = b2_dynamicBody; + boxBodyDef.position = b2Vec2{static_cast(rand() % 13 + 3), 4.0f}; - b2Body* body3 = world->CreateBody(&boxBodyDef); - body3->CreateFixture(&boxShapeDef); + auto body3 = createRigibody(&boxBodyDef); + b2CreatePolygonShape(body3, &boxShapeDef, &polygon); // Large box (recycle definitions) - polygon.SetAsBox(2.0f, 1.0f); - boxBodyDef.position.Set(rand() % 13 + 3, 4); + polygon = b2MakeBox(2.0f, 1.0f); // .SetAsBox(2.0f, 1.0f); + boxBodyDef.position = b2Vec2{static_cast(rand() % 13 + 3), 4.0f}; - b2Body* body4 = world->CreateBody(&boxBodyDef); - body4->CreateFixture(&boxShapeDef); + auto body4 = createRigibody(&boxBodyDef); + b2CreatePolygonShape(body4, &boxShapeDef, &polygon); // Small circle - b2CircleShape circle; - circle.m_radius = 1.0f; - - b2FixtureDef circleShapeDef; - circleShapeDef.shape = &circle; + auto circleShapeDef = b2DefaultShapeDef(); circleShapeDef.density = 1.0f; - b2BodyDef circleBodyDef; - circleBodyDef.type = b2_dynamicBody; - circleBodyDef.position.Set(rand() % 13 + 3, 4); + b2Circle circle{{0.0f, 0.0f}, 1.0f}; - b2Body* body5 = world->CreateBody(&circleBodyDef); - body5->CreateFixture(&circleShapeDef); + b2BodyDef circleBodyDef = b2DefaultBodyDef(); + circleBodyDef.type = b2_dynamicBody; + circleBodyDef.position = b2Vec2{static_cast(rand() % 13 + 3), 4.0f}; + + auto body5 = createRigibody(&circleBodyDef); + b2CreateCircleShape(body5, &circleShapeDef, &circle); // Large circle - circle.m_radius *= 2.0f; - circleBodyDef.position.Set(rand() % 13 + 3, 4); - - b2Body* body6 = world->CreateBody(&circleBodyDef); - body6->CreateFixture(&circleShapeDef); - - uint32 flags = 0; - flags += 1 * b2Draw::e_shapeBit; - flags += 1 * b2Draw::e_jointBit; - flags += 0 * b2Draw::e_aabbBit; - flags += 0 * b2Draw::e_centerOfMassBit; - g_debugDraw.SetFlags(flags); - g_debugDraw.mRatio = PTM_RATIO; - g_debugDraw.debugNodeOffset = {0, 0}; - world->SetDebugDraw(&g_debugDraw); + circle.radius *= 2.0f; + circleBodyDef.position = b2Vec2{static_cast(rand() % 13 + 3), 4.0f}; + + auto body6 = createRigibody(&circleBodyDef); + b2CreateCircleShape(body6, &circleShapeDef, &circle); + + _debugDrawNode->setPTMRatio(PTM_RATIO); + auto& settings = _debugDrawNode->getB2DebugDraw(); + settings.drawShapes = true; + settings.drawJoints = true; } void Box2DTest::createResetButton() @@ -261,24 +246,22 @@ void Box2DTest::addNewSpriteAtPosition(Vec2 p) // Define the dynamic body. // Set up a 1m squared box in the physics world - b2BodyDef bodyDef; - bodyDef.type = b2_dynamicBody; - bodyDef.position.Set(p.x / PTM_RATIO, p.y / PTM_RATIO); + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = b2Vec2{p.x / PTM_RATIO, p.y / PTM_RATIO}; AXLOGD("Add PTM_RATIO sprite x: {:.2} y: {:.2}", p.x / PTM_RATIO, p.y / PTM_RATIO); - b2Body* body = world->CreateBody(&bodyDef); + auto body = createRigibody(&bodyDef); // Define another box shape for our dynamic body. - b2PolygonShape dynamicBox; - dynamicBox.SetAsBox(.5f, .5f); // These are mid points for our 1m box + auto dynamicBox = b2MakeBox(.5f, .5f); // These are mid points for our 1m box // Define the dynamic body fixture. - b2FixtureDef fixtureDef; - fixtureDef.shape = &dynamicBox; - fixtureDef.density = 1.0f; - fixtureDef.friction = 0.3f; - body->CreateFixture(&fixtureDef); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 1.0f; + shapeDef.friction = 0.3f; + b2CreatePolygonShape(body, &shapeDef, &dynamicBox); auto parent = this->getChildByTag(kTagParentNode); @@ -286,7 +269,7 @@ void Box2DTest::addNewSpriteAtPosition(Vec2 p) // just randomly picking one of the images int idx = (AXRANDOM_0_1() > .5 ? 0 : 1); int idy = (AXRANDOM_0_1() > .5 ? 0 : 1); - auto sprite = PhysicsSpriteBox2D::createWithTexture(_spriteTexture, Rect(32 * idx, 32 * idy, 32, 32)); + auto sprite = PhysicsSprite::createWithTexture(_spriteTexture, Rect(32 * idx, 32 * idy, 32, 32)); parent->addChild(sprite); sprite->setB2Body(body); sprite->setPTMRatio(PTM_RATIO); @@ -300,19 +283,9 @@ void Box2DTest::update(float dt) // You need to make an informed choice, the following URL is useful // http://gafferongames.com/game-physics/fix-your-timestep/ - int velocityIterations = 8; - int positionIterations = 1; - - // Instruct the world to perform a single step of simulation. It is - // generally best to keep the time step and iterations fixed. - world->Step(dt, velocityIterations, positionIterations); - - // Debug draw - if (showDebugDraw) - { - drawBox2D->clear(); - world->DebugDraw(); - } + // The number of sub-steps, increasing the sub-step count can increase accuracy. Typically 4. + constexpr int subStepCount = 4; + b2World_Step(world, _director->getAnimationInterval(), subStepCount); } void Box2DTest::onTouchesEnded(const std::vector& touches, Event* event) diff --git a/tests/cpp-tests/Source/Box2DTest/Box2dTest.h b/tests/cpp-tests/Source/Box2DTest/Box2dTest.h index 3defee8d622d..dbf75366d8a5 100644 --- a/tests/cpp-tests/Source/Box2DTest/Box2dTest.h +++ b/tests/cpp-tests/Source/Box2DTest/Box2dTest.h @@ -53,13 +53,14 @@ class Box2DTest : public TestCase void toggleDebugCallback(ax::Object* sender); + b2BodyId createRigibody(b2BodyDef*); + private: - b2World* world; + b2WorldId world{}; - ax::Texture2D* _spriteTexture; - ax::DrawNode* drawBox2D; - ax::extension::PhysicsDebugNodeBox2D g_debugDraw; - bool showDebugDraw = true; + ax::Texture2D* _spriteTexture{nullptr}; + ax::extension::PhysicsDebugNode* _debugDrawNode{nullptr}; + bool showDebugDraw{true}; }; #endif diff --git a/tests/cpp-tests/Source/Box2DTestBed/Box2DTestBed.cpp b/tests/cpp-tests/Source/Box2DTestBed/Box2DTestBed.cpp index 7166c3f6caaa..5e071913a8ef 100644 --- a/tests/cpp-tests/Source/Box2DTestBed/Box2DTestBed.cpp +++ b/tests/cpp-tests/Source/Box2DTestBed/Box2DTestBed.cpp @@ -28,8 +28,9 @@ #include "axmol.h" #include "Box2DTestBed.h" -#include "tests/test.h" -#include "tests/settings.h" +#include "samples/sample.h" +#include "samples/settings.h" +#include "GLView.h" using namespace ax; USING_NS_AX_EXT; @@ -39,35 +40,52 @@ enum kTagParentNode = 1, }; -Settings settings; -ax::Label* labelDebugDraw; +static Settings s_settings; enum { kTagBox2DNode, }; -TestEntry g_testEntries[MAX_TESTS] = {{nullptr}}; -int g_testCount = 0; +static b2Vec2 tob2Vec2(const Vec2& val) +{ + return b2Vec2{val.x, val.y}; +} -int RegisterTest(const char* category, const char* name, TestCreateFcn* fcn) +static inline int CompareSamples(const void* a, const void* b) { - int index = g_testCount; - if (index < MAX_TESTS) + SampleEntry* sa = (SampleEntry*)a; + SampleEntry* sb = (SampleEntry*)b; + + int result = strcmp(sa->category, sb->category); + if (result == 0) { - g_testEntries[index] = {category, name, fcn}; - ++g_testCount; - return index; + result = strcmp(sa->name, sb->name); } - return -1; + return result; +} + +static void SortTests() +{ + qsort(g_sampleEntries, g_sampleCount, sizeof(SampleEntry), CompareSamples); } Box2DTestBedTests::Box2DTestBedTests() { - for (int entryId = 0; entryId < g_testCount; ++entryId) + // TODO: determine properly view size + g_camera.m_width = 1920; + g_camera.m_height = 1080; + g_camera.m_zoom = 150; + g_camera.m_center = b2Vec2_zero; + + ImGuiPresenter::getInstance()->setViewResolution(g_camera.m_width, g_camera.m_height); + + SortTests(); + + for (int idx = 0; idx < g_sampleCount; ++idx) { - addTestCase(g_testEntries[entryId].name, [entryId]() { return Box2DTestBed::createWithEntryID(entryId); }); + addTestCase(g_sampleEntries[idx].name, [idx]() { return Box2DTestBed::create(idx); }); } } @@ -81,19 +99,20 @@ Box2DTestBed::Box2DTestBed() {} Box2DTestBed::~Box2DTestBed() { - Layer::_eventDispatcher->removeEventListener(_touchListener); + //_eventDispatcher->removeEventListener(_touchListener); + _eventDispatcher->removeEventListener(_keyboardListener); + _eventDispatcher->removeEventListener(_mouseListener); } -Box2DTestBed* Box2DTestBed::createWithEntryID(int entryId) +Box2DTestBed* Box2DTestBed::create(int index) { auto layer = new Box2DTestBed(); - layer->initWithEntryID(entryId); - // layer->autorelease(); - + layer->initWithEntryIndex(index); + layer->autorelease(); return layer; } -bool Box2DTestBed::initWithEntryID(int entryId) +bool Box2DTestBed::initWithEntryIndex(int index) { if (!TestCase::init()) { @@ -103,177 +122,312 @@ bool Box2DTestBed::initWithEntryID(int entryId) Vec2 visibleOrigin = director->getVisibleOrigin(); Size visibleSize = director->getVisibleSize(); - m_entryID = entryId; + m_entryIndex = index; - m_entry = g_testEntries + entryId; - m_test = m_entry->createFcn(); - - debugDrawNode = g_debugDraw.GetDrawNode(); - m_test->debugDrawNode = debugDrawNode; - m_test->g_debugDraw = g_debugDraw; - - TestCase::addChild(debugDrawNode, 100); + m_entry = g_sampleEntries + index; + m_sample = m_entry->createFcn(s_settings); // init physics this->initPhysics(); auto label = Label::createWithTTF(m_entry->name, "fonts/arial.ttf", 28); - TestCase::addChild(label, 1); + addChild(label, 1); label->setPosition(visibleOrigin.x + visibleSize.width / 2, visibleOrigin.y + visibleSize.height - 50); // Adds touch event listener - _touchListener = EventListenerTouchOneByOne::create(); - _touchListener->setSwallowTouches(true); - _touchListener->onTouchBegan = AX_CALLBACK_2(Box2DTestBed::onTouchBegan, this); - _touchListener->onTouchMoved = AX_CALLBACK_2(Box2DTestBed::onTouchMoved, this); - _touchListener->onTouchEnded = AX_CALLBACK_2(Box2DTestBed::onTouchEnded, this); - TestCase::_eventDispatcher->addEventListenerWithFixedPriority(_touchListener, 10); + // _touchListener = EventListenerTouchOneByOne::create(); + // _touchListener->setSwallowTouches(true); + // _touchListener->onTouchBegan = AX_CALLBACK_2(Box2DTestBed::onTouchBegan, this); + // _touchListener->onTouchMoved = AX_CALLBACK_2(Box2DTestBed::onTouchMoved, this); + // _touchListener->onTouchEnded = AX_CALLBACK_2(Box2DTestBed::onTouchEnded, this); + // _eventDispatcher->addEventListenerWithFixedPriority(_touchListener, 10); // Adds Keyboard event listener _keyboardListener = EventListenerKeyboard::create(); _keyboardListener->onKeyPressed = AX_CALLBACK_2(Box2DTestBed::onKeyPressed, this); _keyboardListener->onKeyReleased = AX_CALLBACK_2(Box2DTestBed::onKeyReleased, this); - TestCase::_eventDispatcher->addEventListenerWithFixedPriority(_keyboardListener, 11); + _eventDispatcher->addEventListenerWithFixedPriority(_keyboardListener, 11); - auto _mouseListener = EventListenerMouse::create(); + _mouseListener = EventListenerMouse::create(); _mouseListener->onMouseMove = AX_CALLBACK_1(Box2DTestBed::onMouseMove, this); _mouseListener->onMouseUp = AX_CALLBACK_1(Box2DTestBed::onMouseUp, this); _mouseListener->onMouseDown = AX_CALLBACK_1(Box2DTestBed::onMouseDown, this); _mouseListener->onMouseScroll = AX_CALLBACK_1(Box2DTestBed::onMouseScroll, this); - TestCase::_eventDispatcher->addEventListenerWithFixedPriority(_mouseListener, 12); - - // Demo messageString - labelDebugDraw = Label::createWithTTF("TEST", "fonts/arial.ttf", 8.0f); - labelDebugDraw->setAnchorPoint(Vec2(0, 1)); - labelDebugDraw->setPosition(VisibleRect::left().x, VisibleRect::top().y - 10); - labelDebugDraw->setColor(Color3B::WHITE); - TestCase::addChild(labelDebugDraw, 100); + _eventDispatcher->addEventListenerWithFixedPriority(_mouseListener, 12); - TestCase::scheduleUpdate(); + scheduleUpdate(); return true; } -bool Box2DTestBed::onTouchBegan(Touch* touch, Event* event) -{ - auto location = touch->getLocation() - g_debugDraw.debugNodeOffset; - b2Vec2 pos = {location.x / g_debugDraw.mRatio, location.y / g_debugDraw.mRatio}; - return m_test->MouseDown(pos); -} - -void Box2DTestBed::onTouchMoved(Touch* touch, Event* event) -{ - auto location = touch->getLocation() - g_debugDraw.debugNodeOffset; - b2Vec2 pos = {location.x / g_debugDraw.mRatio, location.y / g_debugDraw.mRatio}; - m_test->MouseMove(pos); -} - -void Box2DTestBed::onTouchEnded(Touch* touch, Event* event) -{ - auto location = touch->getLocation() - g_debugDraw.debugNodeOffset; - b2Vec2 pos = {location.x / g_debugDraw.mRatio, location.y / g_debugDraw.mRatio}; - m_test->MouseUp(pos); -} - void Box2DTestBed::onKeyPressed(EventKeyboard::KeyCode code, Event* event) { - AXLOGD("onKeyPressed, keycode: {}", static_cast(code)); - m_test->Keyboard((static_cast(code) - 59)); // its a bad hack! + // AXLOGD("onKeyPressed, keycode: {}", static_cast(code)); + // m_sample->Keyboard((static_cast(code) - 59)); // its a bad hack! } void Box2DTestBed::onKeyReleased(EventKeyboard::KeyCode code, Event* event) { AXLOGD("onKeyPressed, keycode: {}", static_cast(code)); - m_test->KeyboardUp((static_cast(code) - 59)); // its a bad hack! + // m_sample->KeyboardUp((static_cast(code) - 59)); // its a bad hack! + m_sample->Keyboard((static_cast(code) - 59)); } void Box2DTestBed::onMouseDown(Event* event) { - EventMouse* e = (EventMouse*)event; - button[(int)EventMouse::MouseButton::BUTTON_LEFT] = false; - button[(int)EventMouse::MouseButton::BUTTON_RIGHT] = false; - button[(int)EventMouse::MouseButton::BUTTON_MIDDLE] = false; - switch (e->getMouseButton()) - { - case EventMouse::MouseButton::BUTTON_LEFT: - button[(int)EventMouse::MouseButton::BUTTON_LEFT] = true; - break; - case EventMouse::MouseButton::BUTTON_RIGHT: - button[(int)EventMouse::MouseButton::BUTTON_RIGHT] = true; - break; - case EventMouse::MouseButton::BUTTON_MIDDLE: - button[(int)EventMouse::MouseButton::BUTTON_MIDDLE] = true; - break; - } + EventMouse* e = static_cast(event); + + auto location = e->getLocation() - _debugDraw->getWorldOffset(); + b2Vec2 pos = {location.x / _debugDraw->getPTMRatio(), location.y / _debugDraw->getPTMRatio()}; + + int mods = 0; +#if defined(_WIN32) + if (GetAsyncKeyState(VK_SHIFT) & 0x80) + mods |= GLFW_MOD_SHIFT; + if (GetAsyncKeyState(VK_CONTROL) & 0x80) + mods |= GLFW_MOD_CONTROL; + if (GetAsyncKeyState(VK_MENU) & 0x80) + mods |= GLFW_MOD_ALT; +#endif + _draging = true; + _mouseDownPos = pos; + _dragingStartPos = _debugDraw->getPosition(); + + m_sample->MouseDown(pos, static_cast(e->getMouseButton()), mods); } void Box2DTestBed::onMouseUp(Event* event) { - button[(int)EventMouse::MouseButton::BUTTON_LEFT] = false; - button[(int)EventMouse::MouseButton::BUTTON_RIGHT] = false; - button[(int)EventMouse::MouseButton::BUTTON_MIDDLE] = false; + const auto ratio = _debugDraw->getPTMRatio(); + _draging = false; + EventMouse* e = static_cast(event); + auto location = e->getLocation() - _debugDraw->getWorldOffset(); + b2Vec2 pos = {location.x / ratio, location.y / ratio}; + m_sample->MouseUp(pos, static_cast(e->getMouseButton())); } void Box2DTestBed::onMouseMove(Event* event) { - EventMouse* e = (EventMouse*)event; - auto pt = e->getLocation(); - pos = {pt.x / g_debugDraw.mRatio, pt.y / g_debugDraw.mRatio}; + const auto ratio = _debugDraw->getPTMRatio(); + EventMouse* e = static_cast(event); + + auto location = e->getLocation() - _debugDraw->getWorldOffset(); + b2Vec2 pos{location.x / ratio, location.y / ratio}; + m_sample->MouseMove(pos); - if (button[(int)EventMouse::MouseButton::BUTTON_RIGHT]) + if (e->getMouseButton() == EventMouse::MouseButton::BUTTON_RIGHT) { - (pos.x > oldPos.x) ? g_debugDraw.debugNodeOffset.x += 4 : g_debugDraw.debugNodeOffset.x -= 4; - (pos.y < oldPos.y) ? g_debugDraw.debugNodeOffset.y -= 2 : g_debugDraw.debugNodeOffset.y += 2; + auto diff = b2Sub(pos, _mouseDownPos); + _debugDraw->setPosition(_dragingStartPos.x + diff.x, _dragingStartPos.y + diff.y); } - oldPos = pos; } void Box2DTestBed::onMouseScroll(Event* event) { EventMouse* e = (EventMouse*)event; - g_debugDraw.mRatio += e->getScrollY(); + _debugDraw->setPTMRatio(_debugDraw->getPTMRatio() - e->getScrollY()); } void Box2DTestBed::onEnter() { Scene::onEnter(); ImGuiPresenter::getInstance()->addFont(FileUtils::getInstance()->fullPathForFilename("fonts/arial.ttf")); - ImGuiPresenter::getInstance()->addRenderLoop("#im01", AX_CALLBACK_0(Box2DTestBed::onDrawImGui, this), this); + ImGuiPresenter::getInstance()->addRenderLoop("#bv3t", AX_CALLBACK_0(Box2DTestBed::onDrawImGui, this), this); } void Box2DTestBed::onExit() { + ImGuiPresenter::getInstance()->removeRenderLoop("#bv3t"); Scene::onExit(); - ImGuiPresenter::getInstance()->removeRenderLoop("#im01"); } void Box2DTestBed::update(float dt) { // Debug draw - m_test->debugString = ""; - labelDebugDraw->setString(""); - debugDrawNode->clear(); - m_test->Step(settings); - m_test->m_world->DebugDraw(); + _debugDraw->clear(); + m_sample->Step(s_settings); } void Box2DTestBed::initPhysics() { - uint32 flags = 0; - flags += 1 * b2Draw::e_shapeBit; - flags += 1 * b2Draw::e_jointBit; - flags += 0 * b2Draw::e_aabbBit; - flags += 0 * b2Draw::e_centerOfMassBit; - g_debugDraw.SetFlags(flags); - g_debugDraw.mRatio = 8; - m_test->m_world->SetDebugDraw(&g_debugDraw); - m_test->g_debugDraw = g_debugDraw; - g_debugDraw.debugNodeOffset = {250, 70}; - m_test->g_debugDraw.debugNodeOffset = g_debugDraw.debugNodeOffset; - - settings.m_hertz = 60; + _debugDraw = + utils::createInstance(&Box2DTestDebugDrawNode::initWithWorld, m_sample->m_worldId); + _debugDraw->setAutoDraw(false); + addChild(_debugDraw); + + auto& b2dw = _debugDraw->getB2DebugDraw(); + b2dw.drawShapes = true; + b2dw.drawJoints = true; + b2dw.drawAABBs = false; + + _debugDraw->setWorldOffset({250, 70}); + _debugDraw->setPTMRatio(3.0f); + + s_settings.hertz = 60; +} + +void Box2DTestBed::RestartSample() +{ + getTestSuite()->restartCurrTest(); } void Box2DTestBed::onDrawImGui() { - m_test->UpdateUI(); + int maxWorkers = enki::GetNumHardwareThreads(); + + float menuWidth = 180.0f; + auto cursorPos = ImGui::GetCursorScreenPos(); + ImVec2 toolWindowPos = {cursorPos.x + g_camera.m_width - menuWidth - 80, cursorPos.y - 80}; + ImVec2 toolWindowSize = {menuWidth, g_camera.m_height - 200.0f}; + if (g_draw.m_showUI) + { + ImGui::SetNextWindowPos(toolWindowPos, ImGuiCond_FirstUseEver); + ImGui::SetNextWindowSize(toolWindowSize, ImGuiCond_FirstUseEver); + ImGui::Begin("Tools", &g_draw.m_showUI, + /*ImGuiWindowFlags_NoMove | */ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + + if (ImGui::BeginTabBar("ControlTabs", ImGuiTabBarFlags_None)) + { + if (ImGui::BeginTabItem("Controls")) + { + ImGui::PushItemWidth(100.0f); + ImGui::SliderInt("Sub-steps", &s_settings.subStepCount, 1, 50); + ImGui::SliderFloat("Hertz", &s_settings.hertz, 5.0f, 120.0f, "%.0f hz"); + + if (ImGui::SliderInt("Workers", &s_settings.workerCount, 1, maxWorkers)) + { + s_settings.workerCount = b2ClampInt(s_settings.workerCount, 1, maxWorkers); + RestartSample(); + } + ImGui::PopItemWidth(); + + ImGui::Separator(); + + ImGui::Checkbox("Sleep", &s_settings.enableSleep); + ImGui::Checkbox("Warm Starting", &s_settings.enableWarmStarting); + ImGui::Checkbox("Continuous", &s_settings.enableContinuous); + + ImGui::Separator(); + + ImGui::Checkbox("Shapes", &s_settings.drawShapes); + ImGui::Checkbox("Joints", &s_settings.drawJoints); + ImGui::Checkbox("Joint Extras", &s_settings.drawJointExtras); + ImGui::Checkbox("AABBs", &s_settings.drawAABBs); + ImGui::Checkbox("Contact Points", &s_settings.drawContactPoints); + ImGui::Checkbox("Contact Normals", &s_settings.drawContactNormals); + ImGui::Checkbox("Contact Impulses", &s_settings.drawContactImpulses); + ImGui::Checkbox("Friction Impulses", &s_settings.drawFrictionImpulses); + ImGui::Checkbox("Center of Masses", &s_settings.drawMass); + ImGui::Checkbox("Graph Colors", &s_settings.drawGraphColors); + ImGui::Checkbox("Counters", &s_settings.drawCounters); + ImGui::Checkbox("Profile", &s_settings.drawProfile); + + ImVec2 button_sz = ImVec2(-1, 0); + if (ImGui::Button("Pause (P)", button_sz)) + { + s_settings.pause = !s_settings.pause; + } + + if (ImGui::Button("Single Step (O)", button_sz)) + { + s_settings.singleStep = !s_settings.singleStep; + } + + if (ImGui::Button("Dump Mem Stats", button_sz)) + { + b2World_DumpMemoryStats(m_sample->m_worldId); + } + + if (ImGui::Button("Reset Profile", button_sz)) + { + m_sample->ResetProfile(); + } + + if (ImGui::Button("Restart (R)", button_sz)) + { + RestartSample(); + } + + if (ImGui::Button("Quit", button_sz)) + { + glfwSetWindowShouldClose(g_mainWindow, GL_TRUE); + } + + ImGui::EndTabItem(); + } + + ImGuiTreeNodeFlags leafNodeFlags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; + leafNodeFlags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; + + ImGuiTreeNodeFlags nodeFlags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; + + if (ImGui::BeginTabItem("Tests")) + { + int categoryIndex = 0; + const char* category = g_sampleEntries[categoryIndex].category; + int i = 0; + while (i < g_sampleCount) + { + bool categorySelected = strcmp(category, g_sampleEntries[s_settings.sampleIndex].category) == 0; + ImGuiTreeNodeFlags nodeSelectionFlags = categorySelected ? ImGuiTreeNodeFlags_Selected : 0; + bool nodeOpen = ImGui::TreeNodeEx(category, nodeFlags | nodeSelectionFlags); + + if (nodeOpen) + { + while (i < g_sampleCount && strcmp(category, g_sampleEntries[i].category) == 0) + { + ImGuiTreeNodeFlags selectionFlags = 0; + if (s_settings.sampleIndex == i) + { + selectionFlags = ImGuiTreeNodeFlags_Selected; + } + ImGui::TreeNodeEx((void*)(intptr_t)i, leafNodeFlags | selectionFlags, "%s", + g_sampleEntries[i].name); + if (ImGui::IsItemClicked()) + { + getTestSuite()->enterTest(i); + } + ++i; + } + ImGui::TreePop(); + } + else + { + while (i < g_sampleCount && strcmp(category, g_sampleEntries[i].category) == 0) + { + ++i; + } + } + + if (i < g_sampleCount) + { + category = g_sampleEntries[i].category; + categoryIndex = i; + } + } + ImGui::EndTabItem(); + } + ImGui::EndTabBar(); + } + + ImGui::End(); + + m_sample->UpdateUI(); + } + + // if (g_draw.m_showUI) + { + char buffer[128]; + snprintf(buffer, 128, "%.1f ms - step %d - camera (%g, %g, %g)", 1000.0f * _director->getDeltaTime(), + m_sample->m_stepCount, g_camera.m_center.x, g_camera.m_center.y, g_camera.m_zoom); + // snprintf( buffer, 128, "%.1f ms", 1000.0f * frameTime ); + + ImGui::SetNextWindowPos(ImVec2{cursorPos.x + 92, cursorPos.y + g_camera.m_height - 235}, + ImGuiCond_FirstUseEver); + ImGui::Begin("Overlay", nullptr, + ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | + ImGuiWindowFlags_NoScrollbar); + // ImGui::SetCursorPos(ImVec2(5.0f, g_camera.m_height - 20.0f)); + ImGui::TextColored(ImColor(153, 230, 153, 255), "%s", buffer); + ImGui::End(); + } } diff --git a/tests/cpp-tests/Source/Box2DTestBed/Box2DTestBed.h b/tests/cpp-tests/Source/Box2DTestBed/Box2DTestBed.h index 027a1e887252..2e3f5171fc7a 100644 --- a/tests/cpp-tests/Source/Box2DTestBed/Box2DTestBed.h +++ b/tests/cpp-tests/Source/Box2DTestBed/Box2DTestBed.h @@ -28,28 +28,17 @@ #include "axmol.h" #include "box2d/box2d.h" #include "../BaseTest.h" +#include "Box2DTestDebugDrawNode.h" -DEFINE_TEST_SUITE(Box2DTestBedTests); - -class Test; -typedef Test* TestCreateFcn(); - -struct TestEntry -{ - const char* category; - const char* name; - TestCreateFcn* createFcn; -}; +class SampleEntry; +class Sample; -#define MAX_TESTS 256 -extern TestEntry g_testEntries[MAX_TESTS]; - -int RegisterTest(const char* category, const char* name, TestCreateFcn* fcn); +DEFINE_TEST_SUITE(Box2DTestBedTests); -class Box2DTestBed : public TestCase, ax::Layer +class Box2DTestBed : public TestCase { public: - static Box2DTestBed* createWithEntryID(int entryId); + static Box2DTestBed* create(int entryIndex); Box2DTestBed(); virtual ~Box2DTestBed(); @@ -62,13 +51,7 @@ class Box2DTestBed : public TestCase, ax::Layer void initPhysics(); void update(float dt) override; - void createResetButton(); - - bool initWithEntryID(int entryId); - - bool onTouchBegan(ax::Touch* touch, ax::Event* event); - void onTouchMoved(ax::Touch* touch, ax::Event* event); - void onTouchEnded(ax::Touch* touch, ax::Event* event); + bool initWithEntryIndex(int entryIndex); void onKeyPressed(ax::EventKeyboard::KeyCode code, ax::Event* event); void onKeyReleased(ax::EventKeyboard::KeyCode code, ax::Event* event); @@ -78,24 +61,23 @@ class Box2DTestBed : public TestCase, ax::Layer void onMouseMove(ax::Event* event); void onMouseScroll(ax::Event* event); - ax::EventListenerTouchOneByOne* _touchListener; - ax::EventListenerKeyboard* _keyboardListener; + void RestartSample(); - TestEntry* m_entry; - Test* m_test; - int m_entryID; + SampleEntry* m_entry{}; + Sample* m_sample{}; + int m_entryIndex{}; private: - b2World* world; - ax::Texture2D* _spriteTexture; + b2Vec2 _mouseDownPos{}; + ax::Vec2 _dragingStartPos; + bool _draging{false}; - b2Vec2 pos; - b2Vec2 oldPos; - bool button[2]; + /*ax::EventListenerTouchOneByOne* _touchListener{};*/ + ax::EventListenerKeyboard* _keyboardListener{}; + ax::EventListenerMouse* _mouseListener{}; // Debug stuff - ax::DrawNode* debugDrawNode; - ax::extension::PhysicsDebugNodeBox2D g_debugDraw; + Box2DTestDebugDrawNode* _debugDraw{}; }; #endif diff --git a/tests/cpp-tests/Source/Box2DTestBed/Box2DTestDebugDrawNode.cpp b/tests/cpp-tests/Source/Box2DTestBed/Box2DTestDebugDrawNode.cpp new file mode 100644 index 000000000000..41e648bc5418 --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/Box2DTestDebugDrawNode.cpp @@ -0,0 +1,411 @@ +#include "Box2DTestDebugDrawNode.h" +#include "VisibleRect.h" +#include "physics/PhysicsHelper.h" + +using namespace ax; + +Box2DTestDebugDrawNode* g_pDebugDrawNode; +GLFWwindow* g_mainWindow; +b2SampleCamera g_camera; + +b2AABB b2SampleCamera::GetViewBounds() +{ + b2AABB bounds; + bounds.lowerBound = ConvertScreenToWorld({0.0f, (float)m_height}); + bounds.upperBound = ConvertScreenToWorld({(float)m_width, 0.0f}); + return bounds; +} + +b2Vec2 b2SampleCamera::ConvertScreenToWorld(b2Vec2 ps) +{ + float w = float(m_width); + float h = float(m_height); + float u = ps.x / w; + float v = (h - ps.y) / h; + + float ratio = w / h; + b2Vec2 extents = {m_zoom * ratio, m_zoom}; + + b2Vec2 lower = b2Sub(m_center, extents); + b2Vec2 upper = b2Add(m_center, extents); + + b2Vec2 pw = {(1.0f - u) * lower.x + u * upper.x, (1.0f - v) * lower.y + v * upper.y}; + return pw; +} + +static void b2DrawCircle(b2Vec2 center, float radius, b2HexColor color, Box2DTestDebugDrawNode* context) +{ + auto ratio = context->getPTMRatio(); + auto offset = context->getWorldOffset(); + context->AddCircle(CircleData{b2Vec2{center.x * ratio + offset.x, center.y * ratio + offset.y}, radius * ratio, + PhysicsHelper::toColor(color)}); +} + +static void b2DrawSolidCircle(b2Transform t, float radius, b2HexColor color, Box2DTestDebugDrawNode* context) +{ + // RGBA8 rgba = MakeRGBA8(color, 1.0f); + // m_circles.push_back({{transform.p.x, transform.p.y, transform.q.c, transform.q.s}, radius, rgba}); + auto ratio = context->getPTMRatio(); + auto offset = context->getWorldOffset(); + context->AddCircle({{t.p.x * ratio + offset.x, t.p.y * ratio + offset.y, t.q.c, t.q.s}, + radius * ratio, + PhysicsHelper::toColor(color)}); +} + +static void b2DrawSolidCapsule(b2Vec2 pt1, b2Vec2 pt2, float radius, b2HexColor c, Box2DTestDebugDrawNode* context) +{ + auto ratio = context->getPTMRatio(); + auto offset = context->getWorldOffset(); + Vec2 p1{pt1.x * ratio, pt1.y * ratio}, p2{pt2.x * ratio, pt2.y * ratio}; + + Vec2 d = (p2 - p1); + float length = d.length(); + if (length < 0.001f) + { + printf("WARNING: sample app: capsule too short!\n"); + return; + } + + b2Vec2 axis = {d.x / length, d.y / length}; + b2Transform transform; + transform.p = PhysicsHelper::tob2Vec2(0.5f * (p1 + p2)); + transform.q.c = axis.x; + transform.q.s = axis.y; + + auto rgba = PhysicsHelper::toColor(c); + + context->AddCapsule({{transform.p.x + offset.x, transform.p.y + offset.y, transform.q.c, transform.q.s}, + radius * ratio, + length, + rgba}); +} + +bool Box2DTestDebugDrawNode::initWithWorld(b2WorldId worldId) +{ + g_pDebugDrawNode = this; + g_mainWindow = static_cast(_director->getGLView())->getWindow(); + + bool ret = ax::extension::PhysicsDebugNode::initWithWorld(worldId); + +#define __b2_setfun(f) _debugDraw.f = reinterpret_cast(b2##f); + __b2_setfun(DrawCircle); + __b2_setfun(DrawSolidCircle); + __b2_setfun(DrawSolidCapsule); +#undef __b2_setfun + + // Demo messageString + _textRender = Label::createWithTTF("TEST", "fonts/arial.ttf", 8.0f); + _textRender->setAnchorPoint(Vec2(0, 1)); + _textRender->setPosition(VisibleRect::left().x, VisibleRect::top().y - 10); + _textRender->setColor(Color3B::WHITE); + this->addChild(_textRender, 99); + + /// circle shader + { + auto& cmd = _customCommandCircle; + auto& pipelinePS = cmd.getPipelineDescriptor().programState; + AX_SAFE_RELEASE(pipelinePS); + + // vertex attributes + auto program = backend::ProgramManager::getInstance()->loadProgram("custom/circle_vs", "custom/circle_fs"); + pipelinePS = new backend::ProgramState(program); + auto vfmt = pipelinePS->getMutableVertexLayout(); + vfmt->setAttrib("a_localPosition", program->getAttributeLocation("a_localPosition"), + backend::VertexFormat::FLOAT2, 0, false); + vfmt->setStride(sizeof(Vec2)); + cmd.createVertexBuffer(sizeof(Vec2), 6, CustomCommand::BufferUsage::STATIC); + float a = 1.1f; + b2Vec2 vertices[] = {{-a, -a}, {a, -a}, {-a, a}, {a, -a}, {a, a}, {-a, a}}; + cmd.updateVertexBuffer(vertices, sizeof(vertices)); + cmd.setVertexDrawInfo(0, 6); + + // instanced attributes + auto vfmtInstanced = pipelinePS->getMutableVertexLayout(true); + vfmtInstanced->setAttrib("a_instanceTransform", program->getAttributeLocation("a_instancePosition"), + backend::VertexFormat::FLOAT2, offsetof(CircleData, position), false); + vfmtInstanced->setAttrib("a_instanceRadius", program->getAttributeLocation("a_instanceRadius"), + backend::VertexFormat::FLOAT, offsetof(CircleData, radius), false); + vfmtInstanced->setAttrib("a_instanceColor", program->getAttributeLocation("a_instanceColor"), + backend::VertexFormat::FLOAT4, offsetof(CircleData, rgba), false); + vfmtInstanced->setStride(sizeof(CircleData)); + + cmd.setPrimitiveType(CustomCommand::PrimitiveType::TRIANGLE); + cmd.setDrawType(CustomCommand::DrawType::ARRAY_INSTANCED); + } + + /// solid circle shader + { + auto& cmd = _customCommandSolidCircle; + auto& pipelinePS = cmd.getPipelineDescriptor().programState; + AX_SAFE_RELEASE(pipelinePS); + + // vertex attributes + auto program = + backend::ProgramManager::getInstance()->loadProgram("custom/solid_circle_vs", "custom/solid_circle_fs"); + pipelinePS = new backend::ProgramState(program); + auto vfmt = pipelinePS->getMutableVertexLayout(); + vfmt->setAttrib("a_localPosition", program->getAttributeLocation("a_localPosition"), + backend::VertexFormat::FLOAT2, 0, false); + vfmt->setStride(sizeof(Vec2)); + cmd.createVertexBuffer(sizeof(Vec2), 6, CustomCommand::BufferUsage::STATIC); + float a = 1.1f; + b2Vec2 vertices[] = {{-a, -a}, {a, -a}, {-a, a}, {a, -a}, {a, a}, {-a, a}}; + cmd.updateVertexBuffer(vertices, sizeof(vertices)); + cmd.setVertexDrawInfo(0, 6); + + // instanced attributes + auto vfmtInstanced = pipelinePS->getMutableVertexLayout(true); + vfmtInstanced->setAttrib("a_instanceTransform", program->getAttributeLocation("a_instanceTransform"), + backend::VertexFormat::FLOAT4, offsetof(SolidCircleData, transform), false); + vfmtInstanced->setAttrib("a_instanceRadius", program->getAttributeLocation("a_instanceRadius"), + backend::VertexFormat::FLOAT, offsetof(SolidCircleData, radius), false); + vfmtInstanced->setAttrib("a_instanceColor", program->getAttributeLocation("a_instanceColor"), + backend::VertexFormat::FLOAT4, offsetof(SolidCircleData, rgba), false); + vfmtInstanced->setStride(sizeof(SolidCircleData)); + + cmd.setPrimitiveType(CustomCommand::PrimitiveType::TRIANGLE); + cmd.setDrawType(CustomCommand::DrawType::ARRAY_INSTANCED); + } + + /// solid capsule shader + { + auto& cmd = _customCommandCapsule; + auto& pipelinePS = cmd.getPipelineDescriptor().programState; + AX_SAFE_RELEASE(pipelinePS); + + // vertex attributes + auto program = + backend::ProgramManager::getInstance()->loadProgram("custom/solid_capsule_vs", "custom/solid_capsule_fs"); + pipelinePS = new backend::ProgramState(program); + auto vfmt = pipelinePS->getMutableVertexLayout(); + vfmt->setAttrib("a_localPosition", program->getAttributeLocation("a_localPosition"), + backend::VertexFormat::FLOAT2, 0, false); + vfmt->setStride(sizeof(Vec2)); + cmd.createVertexBuffer(sizeof(Vec2), 6, CustomCommand::BufferUsage::STATIC); + float a = 1.1f; + b2Vec2 vertices[] = {{-a, -a}, {a, -a}, {-a, a}, {a, -a}, {a, a}, {-a, a}}; + cmd.updateVertexBuffer(vertices, sizeof(vertices)); + cmd.setVertexDrawInfo(0, 6); + + // instanced attributes + auto vfmtInstanced = pipelinePS->getMutableVertexLayout(true); + vfmtInstanced->setAttrib("a_instanceTransform", program->getAttributeLocation("a_instanceTransform"), + backend::VertexFormat::FLOAT4, offsetof(CapsuleData, transform), false); + vfmtInstanced->setAttrib("a_instanceRadius", program->getAttributeLocation("a_instanceRadius"), + backend::VertexFormat::FLOAT, offsetof(CapsuleData, radius), false); + vfmtInstanced->setAttrib("a_instanceLength", program->getAttributeLocation("a_instanceLength"), + backend::VertexFormat::FLOAT, offsetof(CapsuleData, length), false); + vfmtInstanced->setAttrib("a_instanceColor", program->getAttributeLocation("a_instanceColor"), + backend::VertexFormat::FLOAT4, offsetof(CapsuleData, rgba), false); + vfmtInstanced->setStride(sizeof(CapsuleData)); + + cmd.setPrimitiveType(CustomCommand::PrimitiveType::TRIANGLE); + cmd.setDrawType(CustomCommand::DrawType::ARRAY_INSTANCED); + } + + return ret; +} + +void Box2DTestDebugDrawNode::DrawPolygon(const b2Vec2* vertices, int32_t vertexCount, b2HexColor color) +{ + _debugDraw.DrawPolygon(vertices, vertexCount, color, this); +} + +void Box2DTestDebugDrawNode::DrawSolidPolygon(b2Transform transform, + const b2Vec2* vertices, + int32_t vertexCount, + float radius, + b2HexColor color) +{ + _debugDraw.DrawSolidPolygon(transform, vertices, vertexCount, radius, color, this); +} + +void Box2DTestDebugDrawNode::DrawCircle(b2Vec2 center, float radius, b2HexColor color) +{ + _debugDraw.DrawCircle(center, radius, color, this); +} +void Box2DTestDebugDrawNode::DrawSolidCircle(b2Transform transform, b2Vec2 center, float radius, b2HexColor color) +{ + _debugDraw.DrawSolidCircle(transform, radius, color, this); +} + +void Box2DTestDebugDrawNode::DrawSolidCapsule(b2Vec2 p1, b2Vec2 p2, float radius, b2HexColor color) +{ + _debugDraw.DrawSolidCapsule(p1, p2, radius, color, this); +} + +void Box2DTestDebugDrawNode::DrawSegment(b2Vec2 p1, b2Vec2 p2, b2HexColor color) +{ + _debugDraw.DrawSegment(p1, p2, color, this); +} + +void Box2DTestDebugDrawNode::DrawTransform(b2Transform transform) +{ + _debugDraw.DrawTransform(transform, this); +} + +void Box2DTestDebugDrawNode::DrawPoint(b2Vec2 p, float size, b2HexColor color) +{ + _debugDraw.DrawPoint(p, size, color, this); +} + +void Box2DTestDebugDrawNode::DrawString(int x, int y, const char* pszFormat, ...) +{ + va_list args; + va_start(args, pszFormat); + auto ret = StringUtils::vformat(pszFormat, args); + va_end(args); + + _debugString.append(ret); + _debugString.push_back('\n'); + _textRender->setString(_debugString); +} + +void Box2DTestDebugDrawNode::DrawString(b2Vec2 p, const char* pszFormat, ...) +{ + va_list args; + va_start(args, pszFormat); + auto ret = StringUtils::vformat(pszFormat, args); + va_end(args); + + _debugString.append(ret); + _debugString.push_back('\n'); + _textRender->setString(_debugString); +} + +void Box2DTestDebugDrawNode::AddCapsule(const CapsuleData& capsule) +{ + _capsules.push_back(capsule); + _capsulesDirty = true; +} + +void Box2DTestDebugDrawNode::AddCircle(const CircleData& circle) +{ + _circles.push_back(circle); + _circlesDirty = true; +} + +void Box2DTestDebugDrawNode::AddCircle(const SolidCircleData& circle) +{ + _solidCircles.push_back(circle); + _solidCirclesDirty = true; +} + +void Box2DTestDebugDrawNode::draw(Renderer* renderer, const Mat4& transform, uint32_t flags) +{ + PhysicsDebugNode::draw(renderer, transform, flags); + + /// circle + + // update buffer + if (_circlesDirty) + { + _circlesDirty = false; + if (_circles.empty()) + _customCommandCircle.setInstanceBuffer(nullptr, 0); + else + { + if (_customCommandCircle.getInstanceCapacity() < static_cast(_circles.size())) + _customCommandCircle.createInstanceBuffer(sizeof(CircleData), _circles.size(), + CustomCommand::BufferUsage::DYNAMIC); + _customCommandCircle.updateInstanceBuffer(_circles.data(), _circles.size() * sizeof(CircleData)); + _customCommandCircle.setInstanceDrawInfo(static_cast(_circles.size())); + } + } + + // submit draw command + if (_customCommandCircle.getInstanceCount() > 0) + submitDrawCommand(renderer, _customCommandCircle, transform); + + /// solid circle + + // update buffer + if (_solidCirclesDirty) + { + _solidCirclesDirty = false; + if (_solidCircles.empty()) + _customCommandSolidCircle.setInstanceBuffer(nullptr, 0); + else + { + if (_customCommandSolidCircle.getInstanceCapacity() < static_cast(_solidCircles.size())) + _customCommandSolidCircle.createInstanceBuffer(sizeof(SolidCircleData), _solidCircles.size(), + CustomCommand::BufferUsage::DYNAMIC); + _customCommandSolidCircle.updateInstanceBuffer(_solidCircles.data(), + _solidCircles.size() * sizeof(SolidCircleData)); + _customCommandSolidCircle.setInstanceDrawInfo(static_cast(_solidCircles.size())); + } + } + + // submit draw command + if (_customCommandSolidCircle.getInstanceCount() > 0) + submitDrawCommand(renderer, _customCommandSolidCircle, transform); + + /// capsule + + // update buffer + if (_capsulesDirty) + { + _capsulesDirty = false; + if (_capsules.empty()) + _customCommandCapsule.setInstanceBuffer(nullptr, 0); + else + { + if (_customCommandCapsule.getInstanceCapacity() < static_cast(_capsules.size())) + _customCommandCapsule.createInstanceBuffer(sizeof(CapsuleData), _capsules.size(), + CustomCommand::BufferUsage::DYNAMIC); + _customCommandCapsule.updateInstanceBuffer(_capsules.data(), _capsules.size() * sizeof(CapsuleData)); + _customCommandCapsule.setInstanceDrawInfo(static_cast(_capsules.size())); + } + } + + // submit draw command + if (_customCommandCapsule.getInstanceCount() > 0) + submitDrawCommand(renderer, _customCommandCapsule, transform); +} + +void Box2DTestDebugDrawNode::submitDrawCommand(Renderer* renderer, CustomCommand& cmd, const Mat4& transform) +{ + backend::BlendDescriptor& blendDescriptor = cmd.getPipelineDescriptor().blendDescriptor; + blendDescriptor.blendEnabled = true; + blendDescriptor.sourceRGBBlendFactor = backend::BlendFactor::SRC_ALPHA; + blendDescriptor.destinationRGBBlendFactor = backend::BlendFactor::ONE_MINUS_SRC_ALPHA; + blendDescriptor.sourceAlphaBlendFactor = backend::BlendFactor::SRC_ALPHA; + blendDescriptor.destinationAlphaBlendFactor = backend::BlendFactor::ONE_MINUS_SRC_ALPHA; + + auto& pipelineDescriptor = cmd.getPipelineDescriptor(); + const auto& matrixP = _director->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION); + Mat4 matrixMVP = matrixP * transform; + auto mvpLocation = pipelineDescriptor.programState->getUniformLocation("u_MVPMatrix"); + pipelineDescriptor.programState->setUniform(mvpLocation, matrixMVP.m, sizeof(matrixMVP.m)); + + auto viewHeight = _director->getGLView()->getDesignResolutionSize().height; + constexpr float zoom = 25.0 * 2.35f; + float pixelScale = viewHeight / zoom; + auto uniformLocation = pipelineDescriptor.programState->getUniformLocation("u_pixelScale"); + pipelineDescriptor.programState->setUniform(uniformLocation, &pixelScale, sizeof(pixelScale)); + + cmd.init(_globalZOrder); + renderer->addCommand(&cmd); +} + +void Box2DTestDebugDrawNode::clear() +{ + ax::extension::PhysicsDebugNode::clear(); + + _circles.clear(); + _circlesDirty = true; + + _solidCircles.clear(); + _solidCirclesDirty = true; + + _capsules.clear(); + _capsulesDirty = true; + + _debugString.clear(); +} + +void Box2DTestDebugDrawNode::DrawAABB(b2AABB aabb, b2HexColor color) +{ + this->drawRect(Vec2{aabb.lowerBound.x, aabb.lowerBound.y}, Vec2{aabb.upperBound.x, aabb.upperBound.y}, + PhysicsHelper::toColor(color)); +} diff --git a/tests/cpp-tests/Source/Box2DTestBed/Box2DTestDebugDrawNode.h b/tests/cpp-tests/Source/Box2DTestBed/Box2DTestDebugDrawNode.h new file mode 100644 index 000000000000..350cbd0b7955 --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/Box2DTestDebugDrawNode.h @@ -0,0 +1,106 @@ +#pragma once + +#include "platform/GLViewImpl.h" +#include "physics-nodes/PhysicsDebugNode.h" + +using namespace ax; + +struct CircleData +{ + b2Vec2 position; + float radius; + ax::Color rgba; +}; + +struct SolidCircleData +{ + b2Transform transform; + float radius; + ax::Color rgba; +}; + +struct CapsuleData +{ + b2Transform transform; + float radius; + float length; + ax::Color rgba; +}; + +class Box2DTestDebugDrawNode : public ax::extension::PhysicsDebugNode +{ +public: + Box2DTestDebugDrawNode() : m_debugDraw(_debugDraw) {} + ~Box2DTestDebugDrawNode() + { + + } + bool initWithWorld(b2WorldId worldId) override; + + void DrawPolygon(const b2Vec2* vertices, int32_t vertexCount, b2HexColor color); + void DrawSolidPolygon(b2Transform transform, + const b2Vec2* vertices, + int32_t vertexCount, + float radius, + b2HexColor color); + + void DrawCircle(b2Vec2 center, float radius, b2HexColor color); + void DrawSolidCircle(b2Transform transform, b2Vec2 center, float radius, b2HexColor color); + + void DrawSolidCapsule(b2Vec2 p1, b2Vec2 p2, float radius, b2HexColor color); + + void DrawSegment(b2Vec2 p1, b2Vec2 p2, b2HexColor color); + + void DrawTransform(b2Transform transform); + + void DrawPoint(b2Vec2 p, float size, b2HexColor color); + + void DrawString(int x, int y, const char* string, ...); + + void DrawString(b2Vec2 p, const char* string, ...); + + void DrawAABB(b2AABB aabb, b2HexColor color); + + void AddCircle(const CircleData& circle); + void AddCircle(const SolidCircleData& circle); + void AddCapsule(const CapsuleData& capsule); + + void submitDrawCommand(Renderer* renderer, CustomCommand& cmd, const Mat4& transform); + + void draw(Renderer* renderer, const Mat4& transform, uint32_t flags); + void clear() override; + + b2DebugDraw& m_debugDraw; + bool m_showUI{true}; + + ax::Label* _textRender{nullptr}; + std::string _debugString; + + CustomCommand _customCommandCircle; + CustomCommand _customCommandSolidCircle; + CustomCommand _customCommandCapsule; + axstd::pod_vector _circles; + axstd::pod_vector _solidCircles; + axstd::pod_vector _capsules; + bool _circlesDirty{true}; + bool _solidCirclesDirty{true}; + bool _capsulesDirty{true}; +}; + +extern Box2DTestDebugDrawNode* g_pDebugDrawNode; +#define g_draw (*g_pDebugDrawNode) + +extern GLFWwindow* g_mainWindow; + +struct b2SampleCamera +{ + b2Vec2 ConvertScreenToWorld(b2Vec2 ps); + b2AABB GetViewBounds(); + b2Vec2 m_center{0.0f, 0.0f}; + float m_zoom{1.0f}; + float m_width{480}; + float m_height{320}; +}; + +extern b2SampleCamera g_camera; + diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/CMakeLists.txt b/tests/cpp-tests/Source/Box2DTestBed/samples/CMakeLists.txt new file mode 100644 index 000000000000..0114c790edb1 --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/CMakeLists.txt @@ -0,0 +1,111 @@ +# Box2D samples app + +# glad for OpenGL API +set(GLAD_DIR ${CMAKE_SOURCE_DIR}/extern/glad) + +add_library( + glad STATIC + ${GLAD_DIR}/src/glad.c + ${GLAD_DIR}/include/glad/glad.h + ${GLAD_DIR}/include/KHR/khrplatform.h +) +target_include_directories(glad PUBLIC ${GLAD_DIR}/include) + +# glfw for windowing and input +set(GLFW_BUILD_DOCS OFF CACHE BOOL "GLFW Docs") +set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "GLFW Examples") +set(GLFW_BUILD_TESTS OFF CACHE BOOL "GLFW Tests") +set(GLFW_INSTALL OFF CACHE BOOL "GLFW Install") + +FetchContent_Declare( + glfw + GIT_REPOSITORY https://github.com/glfw/glfw.git + GIT_TAG master + GIT_SHALLOW TRUE + GIT_PROGRESS TRUE +) +FetchContent_MakeAvailable(glfw) + +# imgui and glfw backend for GUI +# https://gist.github.com/jeffamstutz/992723dfabac4e3ffff265eb71a24cd9 +FetchContent_Populate(imgui + URL https://github.com/ocornut/imgui/archive/docking.zip + SOURCE_DIR ${CMAKE_SOURCE_DIR}/build/imgui +) + +set(IMGUI_DIR ${CMAKE_SOURCE_DIR}/build/imgui) + +add_library(imgui STATIC + ${IMGUI_DIR}/imgui.cpp + ${IMGUI_DIR}/imgui_draw.cpp + ${IMGUI_DIR}/imgui_demo.cpp + ${IMGUI_DIR}/imgui_tables.cpp + ${IMGUI_DIR}/imgui_widgets.cpp + + ${IMGUI_DIR}/backends/imgui_impl_glfw.cpp + ${IMGUI_DIR}/backends/imgui_impl_opengl3.cpp +) + +target_link_libraries(imgui PUBLIC glfw glad) +target_include_directories(imgui PUBLIC ${IMGUI_DIR} ${IMGUI_DIR}/backends) +target_compile_definitions(imgui PUBLIC IMGUI_DISABLE_OBSOLETE_FUNCTIONS) + +# jsmn for json +set(JSMN_DIR ${CMAKE_SOURCE_DIR}/extern/jsmn) + +add_library(jsmn INTERFACE ${JSMN_DIR}/jsmn.h) +target_include_directories(jsmn INTERFACE ${JSMN_DIR}) + +add_executable(samples + car.cpp + car.h + donut.cpp + donut.h + doohickey.cpp + doohickey.h + draw.cpp + draw.h + human.cpp + human.h + main.cpp + sample.cpp + sample.h + sample_benchmark.cpp + sample_bodies.cpp + sample_collision.cpp + sample_continuous.cpp + sample_determinism.cpp + sample_events.cpp + sample_geometry.cpp + sample_joints.cpp + sample_robustness.cpp + sample_shapes.cpp + sample_stacking.cpp + sample_world.cpp + settings.cpp + settings.h + shader.cpp + shader.h +) + +set_target_properties(samples PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED YES + CXX_EXTENSIONS NO +) + +target_include_directories(samples PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${JSMN_DIR}) +target_link_libraries(samples PUBLIC box2d imgui glfw glad enkiTS) + +# target_compile_definitions(samples PRIVATE "$<$:SAMPLES_DEBUG>") +# message(STATUS "runtime = ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") +# message(STATUS "binary = ${CMAKE_CURRENT_BINARY_DIR}") + +# Copy font files, etc +add_custom_command( + TARGET samples POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_SOURCE_DIR}/data/ + ${CMAKE_CURRENT_BINARY_DIR}/data/) + +# source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${BOX2D_SAMPLES}) diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/LockLessMultiReadPipe.h b/tests/cpp-tests/Source/Box2DTestBed/samples/LockLessMultiReadPipe.h new file mode 100644 index 000000000000..339c8bdd486c --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/LockLessMultiReadPipe.h @@ -0,0 +1,283 @@ +// Copyright (c) 2013 Doug Binks +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgement in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. + +#pragma once + +#include +#include +#include + +#ifndef ENKI_ASSERT +#include +#define ENKI_ASSERT(x) assert(x) +#endif + +namespace enki +{ + // LockLessMultiReadPipe - Single writer, multiple reader thread safe pipe using (semi) lockless programming + // Readers can only read from the back of the pipe + // The single writer can write to the front of the pipe, and read from both ends (a writer can be a reader) + // for many of the principles used here, see http://msdn.microsoft.com/en-us/library/windows/desktop/ee418650(v=vs.85).aspx + // Note: using log2 sizes so we do not need to clamp (multi-operation) + // T is the contained type + // Note this is not true lockless as the use of flags as a form of lock state. + template class LockLessMultiReadPipe + { + public: + LockLessMultiReadPipe(); + ~LockLessMultiReadPipe() {} + + // ReaderTryReadBack returns false if we were unable to read + // This is thread safe for both multiple readers and the writer + bool ReaderTryReadBack( T* pOut ); + + // WriterTryReadFront returns false if we were unable to read + // This is thread safe for the single writer, but should not be called by readers + bool WriterTryReadFront( T* pOut ); + + // WriterTryWriteFront returns false if we were unable to write + // This is thread safe for the single writer, but should not be called by readers + bool WriterTryWriteFront( const T& in ); + + // IsPipeEmpty() is a utility function, not intended for general use + // Should only be used very prudently. + bool IsPipeEmpty() const + { + return 0 == m_WriteIndex.load( std::memory_order_relaxed ) - m_ReadCount.load( std::memory_order_relaxed ); + } + + void Clear() + { + m_WriteIndex = 0; + m_ReadIndex = 0; + m_ReadCount = 0; + memset( (void*)m_Flags, 0, sizeof( m_Flags ) ); + } + + private: + const static uint32_t ms_cSize = ( 1 << cSizeLog2 ); + const static uint32_t ms_cIndexMask = ms_cSize - 1; + const static uint32_t FLAG_INVALID = 0xFFFFFFFF; // 32bit for CAS + const static uint32_t FLAG_CAN_WRITE = 0x00000000; // 32bit for CAS + const static uint32_t FLAG_CAN_READ = 0x11111111; // 32bit for CAS + + T m_Buffer[ ms_cSize ]; + + // read and write indexes allow fast access to the pipe, but actual access + // controlled by the access flags. + std::atomic m_WriteIndex; + std::atomic m_ReadCount; + std::atomic m_Flags[ ms_cSize ]; + std::atomic m_ReadIndex; + }; + + template inline + LockLessMultiReadPipe::LockLessMultiReadPipe() + : m_WriteIndex(0) + , m_ReadCount(0) + , m_ReadIndex(0) + { + ENKI_ASSERT( cSizeLog2 < 32 ); + memset( (void*)m_Flags, 0, sizeof( m_Flags ) ); + } + + template inline + bool LockLessMultiReadPipe::ReaderTryReadBack( T* pOut ) + { + + uint32_t actualReadIndex; + uint32_t readCount = m_ReadCount.load( std::memory_order_relaxed ); + + // We get hold of read index for consistency + // and do first pass starting at read count + uint32_t readIndexToUse = readCount; + while(true) + { + + uint32_t writeIndex = m_WriteIndex.load( std::memory_order_relaxed ); + // power of two sizes ensures we can use a simple calc without modulus + uint32_t numInPipe = writeIndex - readCount; + if( 0 == numInPipe ) + { + return false; + } + if( readIndexToUse >= writeIndex ) + { + readIndexToUse = m_ReadIndex.load( std::memory_order_relaxed ); + } + + // power of two sizes ensures we can perform AND for a modulus + actualReadIndex = readIndexToUse & ms_cIndexMask; + + // Multiple potential readers mean we should check if the data is valid, + // using an atomic compare exchange + uint32_t previous = FLAG_CAN_READ; + bool bSuccess = m_Flags[ actualReadIndex ].compare_exchange_strong( previous, FLAG_INVALID, std::memory_order_acq_rel, std::memory_order_relaxed ); + if( bSuccess ) + { + break; + } + ++readIndexToUse; + + // Update read count + readCount = m_ReadCount.load( std::memory_order_relaxed ); + } + + // we update the read index using an atomic add, as we've only read one piece of data. + // this ensure consistency of the read index, and the above loop ensures readers + // only read from unread data + m_ReadCount.fetch_add(1, std::memory_order_relaxed ); + + // now read data, ensuring we do so after above reads & CAS + *pOut = m_Buffer[ actualReadIndex ]; + + m_Flags[ actualReadIndex ].store( FLAG_CAN_WRITE, std::memory_order_release ); + + return true; + } + + template inline + bool LockLessMultiReadPipe::WriterTryReadFront( T* pOut ) + { + uint32_t writeIndex = m_WriteIndex.load( std::memory_order_relaxed ); + uint32_t frontReadIndex = writeIndex; + + // Multiple potential readers mean we should check if the data is valid, + // using an atomic compare exchange - which acts as a form of lock (so not quite lockless really). + uint32_t actualReadIndex = 0; + while(true) + { + uint32_t readCount = m_ReadCount.load( std::memory_order_relaxed ); + // power of two sizes ensures we can use a simple calc without modulus + uint32_t numInPipe = writeIndex - readCount; + if( 0 == numInPipe ) + { + m_ReadIndex.store( readCount, std::memory_order_release ); + return false; + } + --frontReadIndex; + actualReadIndex = frontReadIndex & ms_cIndexMask; + uint32_t previous = FLAG_CAN_READ; + bool success = m_Flags[ actualReadIndex ].compare_exchange_strong( previous, FLAG_INVALID, std::memory_order_acq_rel, std::memory_order_relaxed ); + if( success ) + { + break; + } + else if( m_ReadIndex.load( std::memory_order_acquire ) >= frontReadIndex ) + { + return false; + } + } + + // now read data, ensuring we do so after above reads & CAS + *pOut = m_Buffer[ actualReadIndex ]; + + m_Flags[ actualReadIndex ].store( FLAG_CAN_WRITE, std::memory_order_relaxed ); + + m_WriteIndex.store(writeIndex-1, std::memory_order_relaxed); + return true; + } + + + template inline + bool LockLessMultiReadPipe::WriterTryWriteFront( const T& in ) + { + // The writer 'owns' the write index, and readers can only reduce + // the amount of data in the pipe. + // We get hold of both values for consistency and to reduce false sharing + // impacting more than one access + uint32_t writeIndex = m_WriteIndex; + + // power of two sizes ensures we can perform AND for a modulus + uint32_t actualWriteIndex = writeIndex & ms_cIndexMask; + + // a reader may still be reading this item, as there are multiple readers + if( m_Flags[ actualWriteIndex ].load(std::memory_order_acquire) != FLAG_CAN_WRITE ) + { + return false; // still being read, so have caught up with tail. + } + + // as we are the only writer we can update the data without atomics + // whilst the write index has not been updated + m_Buffer[ actualWriteIndex ] = in; + m_Flags[ actualWriteIndex ].store( FLAG_CAN_READ, std::memory_order_release ); + + m_WriteIndex.fetch_add(1, std::memory_order_relaxed); + return true; + } + + + // Lockless multiwriter intrusive list + // Type T must implement T* volatile pNext; + template class LocklessMultiWriteIntrusiveList + { + + std::atomic pHead; + T tail; + public: + LocklessMultiWriteIntrusiveList() : pHead( &tail ) + { + tail.pNext = NULL; + } + + bool IsListEmpty() const + { + return pHead == &tail; + } + + // Add - safe to perform from any thread + void WriterWriteFront( T* pNode_ ) + { + ENKI_ASSERT( pNode_ ); + pNode_->pNext = NULL; + T* pPrev = pHead.exchange( pNode_ ); + pPrev->pNext = pNode_; + } + + // Remove - only thread safe for owner + T* ReaderReadBack() + { + T* pTailPlus1 = tail.pNext; + if( pTailPlus1 ) + { + T* pTailPlus2 = pTailPlus1->pNext; + if( pTailPlus2 ) + { + //not head + tail.pNext = pTailPlus2; + } + else + { + tail.pNext = NULL; + T* pCompare = pTailPlus1; // we need preserve pTailPlus1 as compare will alter it on failure + // pTailPlus1 is the head, attempt swap with tail + if( !pHead.compare_exchange_strong( pCompare, &tail ) ) + { + // pCompare receives the revised pHead on failure. + // pTailPlus1 is no longer the head, so pTailPlus1->pNext should be non NULL + while( (T*)NULL == pTailPlus1->pNext ) {;} // wait for pNext to be updated as head may have just changed. + tail.pNext = pTailPlus1->pNext.load(); + pTailPlus1->pNext = NULL; + } + } + } + return pTailPlus1; + } + }; + +} diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/TaskScheduler.cpp b/tests/cpp-tests/Source/Box2DTestBed/samples/TaskScheduler.cpp new file mode 100644 index 000000000000..a0edaa951bc1 --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/TaskScheduler.cpp @@ -0,0 +1,1547 @@ +// Copyright (c) 2013 Doug Binks +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgement in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. + +#include "TaskScheduler.h" +#include "LockLessMultiReadPipe.h" + +#include + +#if defined __i386__ || defined __x86_64__ +#include "x86intrin.h" +#elif defined _WIN32 +#include +#endif + +using namespace enki; + +#if defined(ENKI_CUSTOM_ALLOC_FILE_AND_LINE) +#define ENKI_FILE_AND_LINE __FILE__, __LINE__ +#else +namespace +{ + const char* gc_File = ""; + const uint32_t gc_Line = 0; +} +#define ENKI_FILE_AND_LINE gc_File, gc_Line +#endif + +// UWP and MinGW don't have GetActiveProcessorCount +#if defined(_WIN64) \ + && !defined(__MINGW32__) \ + && !(defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PC_APP || WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)) +#define ENKI_USE_WINDOWS_PROCESSOR_API +#endif + +#ifdef ENKI_USE_WINDOWS_PROCESSOR_API +#ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN +#endif + +#ifndef NOMINMAX + #define NOMINMAX +#endif +#include +#endif + +uint32_t enki::GetNumHardwareThreads() +{ +#ifdef ENKI_USE_WINDOWS_PROCESSOR_API + return GetActiveProcessorCount(ALL_PROCESSOR_GROUPS); +#else + return std::thread::hardware_concurrency(); +#endif +} + +namespace enki +{ + static constexpr int32_t gc_TaskStartCount = 2; + static constexpr int32_t gc_TaskAlmostCompleteCount = 1; // GetIsComplete() will return false, but execution is done and about to complete + static constexpr uint32_t gc_PipeSizeLog2 = 8; + static constexpr uint32_t gc_SpinCount = 10; + static constexpr uint32_t gc_SpinBackOffMultiplier = 100; + static constexpr uint32_t gc_MaxNumInitialPartitions = 8; + static constexpr uint32_t gc_MaxStolenPartitions = 1 << gc_PipeSizeLog2; + static constexpr uint32_t gc_CacheLineSize = 64; + // awaiting std::hardware_constructive_interference_size +} + +// thread_local not well supported yet by some older C++11 compilers. +// For XCode before version 8 thread_local is not defined, so add to your compile defines: ENKI_THREAD_LOCAL __thread +#ifndef ENKI_THREAD_LOCAL +#if defined(_MSC_VER) && _MSC_VER <= 1800 + #define ENKI_THREAD_LOCAL __declspec(thread) +// Removed below as XCode supports thread_local since version 8 +// #elif __APPLE__ +// // Apple thread_local currently not implemented in XCode before version 8 despite it being in Clang. +// #define ENKI_THREAD_LOCAL __thread +#else + #define ENKI_THREAD_LOCAL thread_local +#endif +#endif + + +// each software thread gets its own copy of gtl_threadNum, so this is safe to use as a static variable +static ENKI_THREAD_LOCAL uint32_t gtl_threadNum = enki::NO_THREAD_NUM; + +namespace enki +{ + struct SubTaskSet + { + ITaskSet* pTask; + TaskSetPartition partition; + }; + + // we derive class TaskPipe rather than typedef to get forward declaration working easily + class TaskPipe : public LockLessMultiReadPipe {}; + + enum ThreadState : int32_t + { + ENKI_THREAD_STATE_NONE, // shouldn't get this value + ENKI_THREAD_STATE_NOT_LAUNCHED, // for debug purposes - indicates enki task thread not yet launched + ENKI_THREAD_STATE_RUNNING, + ENKI_THREAD_STATE_PRIMARY_REGISTERED, // primary thread is the one enkiTS was initialized on + ENKI_THREAD_STATE_EXTERNAL_REGISTERED, + ENKI_THREAD_STATE_EXTERNAL_UNREGISTERED, + ENKI_THREAD_STATE_WAIT_TASK_COMPLETION, + ENKI_THREAD_STATE_WAIT_NEW_TASKS, + ENKI_THREAD_STATE_WAIT_NEW_PINNED_TASKS, + ENKI_THREAD_STATE_STOPPED, + }; + + struct ThreadArgs + { + uint32_t threadNum; + TaskScheduler* pTaskScheduler; + }; + + struct alignas(enki::gc_CacheLineSize) ThreadDataStore + { + semaphoreid_t* pWaitNewPinnedTaskSemaphore = nullptr; + std::atomic threadState = { ENKI_THREAD_STATE_NONE }; + uint32_t rndSeed = 0; + char prevent_false_Share[ enki::gc_CacheLineSize - sizeof(std::atomic) - sizeof(semaphoreid_t*) - sizeof( uint32_t ) ]; // required to prevent alignment padding warning + }; + constexpr size_t SIZEOFTHREADDATASTORE = sizeof( ThreadDataStore ); // for easier inspection + static_assert( SIZEOFTHREADDATASTORE == enki::gc_CacheLineSize, "ThreadDataStore may exhibit false sharing" ); + + class PinnedTaskList : public LocklessMultiWriteIntrusiveList {}; + + semaphoreid_t* SemaphoreCreate(); + void SemaphoreDelete( semaphoreid_t* pSemaphore_ ); + void SemaphoreWait( semaphoreid_t& semaphoreid ); + void SemaphoreSignal( semaphoreid_t& semaphoreid, int32_t countWaiting ); +} + +namespace +{ + SubTaskSet SplitTask( SubTaskSet& subTask_, uint32_t rangeToSplit_ ) + { + SubTaskSet splitTask = subTask_; + uint32_t rangeLeft = subTask_.partition.end - subTask_.partition.start; + rangeToSplit_ = std::min( rangeToSplit_, rangeLeft ); + splitTask.partition.end = subTask_.partition.start + rangeToSplit_; + subTask_.partition.start = splitTask.partition.end; + return splitTask; + } + + #if ( defined _WIN32 && ( defined _M_IX86 || defined _M_X64 ) ) || ( defined __i386__ || defined __x86_64__ ) + // Note: see https://software.intel.com/en-us/articles/a-common-construct-to-avoid-the-contention-of-threads-architecture-agnostic-spin-wait-loops + void SpinWait( uint32_t spinCount_ ) + { + uint64_t end = __rdtsc() + spinCount_; + while( __rdtsc() < end ) + { + _mm_pause(); + } + } + #else + void SpinWait( uint32_t spinCount_ ) + { + while( spinCount_ ) + { + // TODO: may have NOP or yield equiv + --spinCount_; + } + } + #endif + + void SafeCallback( ProfilerCallbackFunc func_, uint32_t threadnum_ ) + { + if( func_ != nullptr ) + { + func_( threadnum_ ); + } + } +} + + +ENKITS_API void* enki::DefaultAllocFunc( size_t align_, size_t size_, void* userData_, const char* file_, int line_ ) +{ + (void)userData_; (void)file_; (void)line_; + void* pRet; +#ifdef _WIN32 + pRet = (void*)_aligned_malloc( size_, align_ ); +#else + pRet = nullptr; + if( align_ <= size_ && align_ <= alignof(int64_t) ) + { + // no need for alignment, use malloc + pRet = malloc( size_ ); + } + else + { + int retval = posix_memalign( &pRet, align_, size_ ); + (void)retval; // unused + } +#endif + return pRet; +} + +ENKITS_API void enki::DefaultFreeFunc( void* ptr_, size_t size_, void* userData_, const char* file_, int line_ ) +{ + (void)size_; (void)userData_; (void)file_; (void)line_; +#ifdef _WIN32 + _aligned_free( ptr_ ); +#else + free( ptr_ ); +#endif +} + +bool TaskScheduler::RegisterExternalTaskThread() +{ + bool bRegistered = false; + while( !bRegistered && m_NumExternalTaskThreadsRegistered < (int32_t)m_Config.numExternalTaskThreads ) + { + for(uint32_t thread = GetNumFirstExternalTaskThread(); thread < GetNumFirstExternalTaskThread() + m_Config.numExternalTaskThreads; ++thread ) + { + ThreadState threadStateExpected = ENKI_THREAD_STATE_EXTERNAL_UNREGISTERED; + if( m_pThreadDataStore[thread].threadState.compare_exchange_strong( + threadStateExpected, ENKI_THREAD_STATE_EXTERNAL_REGISTERED ) ) + { + ++m_NumExternalTaskThreadsRegistered; + gtl_threadNum = thread; + bRegistered = true; + break; + } + } + } + return bRegistered; +} + +bool TaskScheduler::RegisterExternalTaskThread( uint32_t threadNumToRegister_ ) +{ + ENKI_ASSERT( threadNumToRegister_ >= GetNumFirstExternalTaskThread() ); + ENKI_ASSERT( threadNumToRegister_ < ( GetNumFirstExternalTaskThread() + m_Config.numExternalTaskThreads ) ); + ThreadState threadStateExpected = ENKI_THREAD_STATE_EXTERNAL_UNREGISTERED; + if( m_pThreadDataStore[threadNumToRegister_].threadState.compare_exchange_strong( + threadStateExpected, ENKI_THREAD_STATE_EXTERNAL_REGISTERED ) ) + { + ++m_NumExternalTaskThreadsRegistered; + gtl_threadNum = threadNumToRegister_; + return true; + } + return false; +} + + +void TaskScheduler::DeRegisterExternalTaskThread() +{ + ENKI_ASSERT( gtl_threadNum != enki::NO_THREAD_NUM ); + ENKI_ASSERT( gtl_threadNum >= GetNumFirstExternalTaskThread() ); + ThreadState threadState = m_pThreadDataStore[gtl_threadNum].threadState.load( std::memory_order_acquire ); + ENKI_ASSERT( threadState == ENKI_THREAD_STATE_EXTERNAL_REGISTERED ); + if( threadState == ENKI_THREAD_STATE_EXTERNAL_REGISTERED ) + { + --m_NumExternalTaskThreadsRegistered; + m_pThreadDataStore[gtl_threadNum].threadState.store( ENKI_THREAD_STATE_EXTERNAL_UNREGISTERED, std::memory_order_release ); + gtl_threadNum = enki::NO_THREAD_NUM; + } +} + +uint32_t TaskScheduler::GetNumRegisteredExternalTaskThreads() +{ + return m_NumExternalTaskThreadsRegistered; +} + +void TaskScheduler::TaskingThreadFunction( const ThreadArgs& args_ ) +{ + uint32_t threadNum = args_.threadNum; + TaskScheduler* pTS = args_.pTaskScheduler; + gtl_threadNum = threadNum; + + pTS->m_pThreadDataStore[threadNum].threadState.store( ENKI_THREAD_STATE_RUNNING, std::memory_order_release ); + SafeCallback( pTS->m_Config.profilerCallbacks.threadStart, threadNum ); + + uint32_t spinCount = 0; + uint32_t hintPipeToCheck_io = threadNum + 1; // does not need to be clamped. + while( pTS->GetIsRunningInt() ) + { + if( !pTS->TryRunTask( threadNum, hintPipeToCheck_io ) ) + { + // no tasks, will spin then wait + ++spinCount; + if( spinCount > gc_SpinCount ) + { + pTS->WaitForNewTasks( threadNum ); + } + else + { + uint32_t spinBackoffCount = spinCount * gc_SpinBackOffMultiplier; + SpinWait( spinBackoffCount ); + } + } + else + { + spinCount = 0; // have run a task so reset spin count. + } + } + + pTS->m_NumInternalTaskThreadsRunning.fetch_sub( 1, std::memory_order_release ); + pTS->m_pThreadDataStore[threadNum].threadState.store( ENKI_THREAD_STATE_STOPPED, std::memory_order_release ); + SafeCallback( pTS->m_Config.profilerCallbacks.threadStop, threadNum ); +} + + +void TaskScheduler::StartThreads() +{ + if( m_bHaveThreads ) + { + return; + } + + m_NumThreads = m_Config.numTaskThreadsToCreate + m_Config.numExternalTaskThreads + 1; + + for( int priority = 0; priority < TASK_PRIORITY_NUM; ++priority ) + { + m_pPipesPerThread[ priority ] = NewArray( m_NumThreads, ENKI_FILE_AND_LINE ); + m_pPinnedTaskListPerThread[ priority ] = NewArray( m_NumThreads, ENKI_FILE_AND_LINE ); + } + + m_pNewTaskSemaphore = SemaphoreNew(); + m_pTaskCompleteSemaphore = SemaphoreNew(); + + // we create one less thread than m_NumThreads as the main thread counts as one + m_pThreadDataStore = NewArray( m_NumThreads, ENKI_FILE_AND_LINE ); + m_pThreads = NewArray( m_NumThreads, ENKI_FILE_AND_LINE ); + m_bRunning = true; + m_bWaitforAllCalled = false; + m_bShutdownRequested = false; + + // current thread is primary enkiTS thread + m_pThreadDataStore[0].threadState = ENKI_THREAD_STATE_PRIMARY_REGISTERED; + gtl_threadNum = 0; + + for( uint32_t thread = GetNumFirstExternalTaskThread(); thread < m_Config.numExternalTaskThreads + GetNumFirstExternalTaskThread(); ++thread ) + { + m_pThreadDataStore[thread].threadState = ENKI_THREAD_STATE_EXTERNAL_UNREGISTERED; + } + for( uint32_t thread = m_Config.numExternalTaskThreads + GetNumFirstExternalTaskThread(); thread < m_NumThreads; ++thread ) + { + m_pThreadDataStore[thread].threadState = ENKI_THREAD_STATE_NOT_LAUNCHED; + } + + + // Create Wait New Pinned Task Semaphores and init rndSeed + for( uint32_t threadNum = 0; threadNum < m_NumThreads; ++threadNum ) + { + m_pThreadDataStore[threadNum].pWaitNewPinnedTaskSemaphore = SemaphoreNew(); + m_pThreadDataStore[threadNum].rndSeed = threadNum; + } + + // only launch threads once all thread states are set + for( uint32_t thread = m_Config.numExternalTaskThreads + GetNumFirstExternalTaskThread(); thread < m_NumThreads; ++thread ) + { + m_pThreads[thread] = std::thread( TaskingThreadFunction, ThreadArgs{ thread, this } ); + ++m_NumInternalTaskThreadsRunning; + } + + // ensure we have sufficient tasks to equally fill either all threads including main + // or just the threads we've launched, this is outside the first init as we want to be able + // to runtime change it + if( 1 == m_NumThreads ) + { + m_NumPartitions = 1; + m_NumInitialPartitions = 1; + } + else + { + // There could be more threads than hardware threads if external threads are + // being intended for blocking functionality such as io etc. + // We only need to partition for a maximum of the available processor parallelism. + uint32_t numThreadsToPartitionFor = std::min( m_NumThreads, GetNumHardwareThreads() ); + m_NumPartitions = numThreadsToPartitionFor * (numThreadsToPartitionFor - 1); + // ensure m_NumPartitions, m_NumInitialPartitions non zero, can happen if m_NumThreads > 1 && GetNumHardwareThreads() == 1 + m_NumPartitions = std::max( m_NumPartitions, (uint32_t)1 ); + m_NumInitialPartitions = std::max( numThreadsToPartitionFor - 1, (uint32_t)1 ); + m_NumInitialPartitions = std::min( m_NumInitialPartitions, gc_MaxNumInitialPartitions ); + } + +#ifdef ENKI_USE_WINDOWS_PROCESSOR_API + // x64 bit Windows may support >64 logical processors using processor groups, and only allocate threads to a default group. + // We need to detect this and distribute threads accordingly + if( GetNumHardwareThreads() > 64 && // only have processor groups if > 64 hardware threads + std::thread::hardware_concurrency() < GetNumHardwareThreads() && // if std::thread sees > 64 hardware threads no need to distribute + std::thread::hardware_concurrency() < m_NumThreads ) // no need to distribute if number of threads requested lower than std::thread sees + { + uint32_t numProcessorGroups = GetActiveProcessorGroupCount(); + GROUP_AFFINITY mainThreadAffinity; + BOOL success = GetThreadGroupAffinity( GetCurrentThread(), &mainThreadAffinity ); + ENKI_ASSERT( success ); + if( success ) + { + uint32_t mainProcessorGroup = mainThreadAffinity.Group; + uint32_t currLogicalProcess = GetActiveProcessorCount( (WORD)mainProcessorGroup ); // we start iteration at end of current process group's threads + + // If more threads are created than there are logical processors then we still want to distribute them evenly amongst groups + // so we iterate continuously around the groups until we reach m_NumThreads + uint32_t group = 0; + while( currLogicalProcess < m_NumThreads ) + { + ++group; // start at group 1 since we set currLogicalProcess to start of next group + uint32_t currGroup = ( group + mainProcessorGroup ) % numProcessorGroups; // we start at mainProcessorGroup, go round in circles + uint32_t groupNumLogicalProcessors = GetActiveProcessorCount( (WORD)currGroup ); + ENKI_ASSERT( groupNumLogicalProcessors <= 64 ); + uint64_t GROUPMASK = 0xFFFFFFFFFFFFFFFFULL >> (64-groupNumLogicalProcessors); // group mask should not have 1's where there are no processors + for( uint32_t groupLogicalProcess = 0; ( groupLogicalProcess < groupNumLogicalProcessors ) && ( currLogicalProcess < m_NumThreads ); ++groupLogicalProcess, ++currLogicalProcess ) + { + if( currLogicalProcess > m_Config.numExternalTaskThreads + GetNumFirstExternalTaskThread() ) + { + auto thread_handle = m_pThreads[currLogicalProcess].native_handle(); + + // From https://learn.microsoft.com/en-us/windows/win32/procthread/processor-groups + // If a thread is assigned to a different group than the process, the process's affinity is updated to include the thread's affinity + // and the process becomes a multi-group process. + GROUP_AFFINITY threadAffinity; + success = GetThreadGroupAffinity( thread_handle, &threadAffinity ); + ENKI_ASSERT(success); (void)success; + if( threadAffinity.Group != currGroup ) + { + threadAffinity.Group = (WORD)currGroup; + threadAffinity.Mask = GROUPMASK; + success = SetThreadGroupAffinity( thread_handle, &threadAffinity, nullptr ); + ENKI_ASSERT( success ); (void)success; + } + } + } + } + } + } +#endif + + m_bHaveThreads = true; +} + +void TaskScheduler::StopThreads( bool bWait_ ) +{ + // we set m_bWaitforAllCalled to true to ensure any task which loop using this status exit + m_bWaitforAllCalled.store( true, std::memory_order_release ); + + // set status + m_bShutdownRequested.store( true, std::memory_order_release ); + m_bRunning.store( false, std::memory_order_release ); + + if( m_bHaveThreads ) + { + + // wait for threads to quit before deleting data + while( bWait_ && m_NumInternalTaskThreadsRunning ) + { + // keep firing event to ensure all threads pick up state of m_bRunning + WakeThreadsForNewTasks(); + + for( uint32_t threadId = 0; threadId < m_NumThreads; ++threadId ) + { + // send wait for new pinned tasks signal to ensure any waiting are awoken + SemaphoreSignal( *m_pThreadDataStore[ threadId ].pWaitNewPinnedTaskSemaphore, 1 ); + } + } + + // detach threads starting with thread GetNumFirstExternalTaskThread() (as 0 is initialization thread). + for( uint32_t thread = m_Config.numExternalTaskThreads + GetNumFirstExternalTaskThread(); thread < m_NumThreads; ++thread ) + { + ENKI_ASSERT( m_pThreads[thread].joinable() ); + m_pThreads[thread].join(); + } + + // delete any Wait New Pinned Task Semaphores + for( uint32_t threadNum = 0; threadNum < m_NumThreads; ++threadNum ) + { + SemaphoreDelete( m_pThreadDataStore[threadNum].pWaitNewPinnedTaskSemaphore ); + } + + DeleteArray( m_pThreadDataStore, m_NumThreads, ENKI_FILE_AND_LINE ); + DeleteArray( m_pThreads, m_NumThreads, ENKI_FILE_AND_LINE ); + m_pThreadDataStore = 0; + m_pThreads = 0; + + SemaphoreDelete( m_pNewTaskSemaphore ); + m_pNewTaskSemaphore = 0; + SemaphoreDelete( m_pTaskCompleteSemaphore ); + m_pTaskCompleteSemaphore = 0; + + m_bHaveThreads = false; + m_NumThreadsWaitingForNewTasks = 0; + m_NumThreadsWaitingForTaskCompletion = 0; + m_NumInternalTaskThreadsRunning = 0; + m_NumExternalTaskThreadsRegistered = 0; + + for( int priority = 0; priority < TASK_PRIORITY_NUM; ++priority ) + { + DeleteArray( m_pPipesPerThread[ priority ], m_NumThreads, ENKI_FILE_AND_LINE ); + m_pPipesPerThread[ priority ] = NULL; + DeleteArray( m_pPinnedTaskListPerThread[ priority ], m_NumThreads, ENKI_FILE_AND_LINE ); + m_pPinnedTaskListPerThread[ priority ] = NULL; + } + m_NumThreads = 0; + } +} + +bool TaskScheduler::TryRunTask( uint32_t threadNum_, uint32_t& hintPipeToCheck_io_ ) +{ + for( int priority = 0; priority < TASK_PRIORITY_NUM; ++priority ) + { + if( TryRunTask( threadNum_, priority, hintPipeToCheck_io_ ) ) + { + return true; + } + } + return false; +} + +static inline uint32_t RotateLeft( uint32_t value, int32_t count ) +{ + return ( value << count ) | ( value >> ( 32 - count )); +} +/* xxHash variant based on documentation on + https://github.com/Cyan4973/xxHash/blob/eec5700f4d62113b47ee548edbc4746f61ffb098/doc/xxhash_spec.md + + Copyright (c) Yann Collet + + Permission is granted to copy and distribute this document for any purpose and without charge, including translations into other languages and incorporation into compilations, provided that the copyright notice and this notice are preserved, and that any substantive changes or deletions from the original are clearly marked. Distribution of this document is unlimited. +*/ +static inline uint32_t Hash32( uint32_t in_ ) +{ + static const uint32_t PRIME32_1 = 2654435761U; // 0b10011110001101110111100110110001 + static const uint32_t PRIME32_2 = 2246822519U; // 0b10000101111010111100101001110111 + static const uint32_t PRIME32_3 = 3266489917U; // 0b11000010101100101010111000111101 + static const uint32_t PRIME32_4 = 668265263U; // 0b00100111110101001110101100101111 + static const uint32_t PRIME32_5 = 374761393U; // 0b00010110010101100110011110110001 + static const uint32_t SEED = 0; // can configure seed if needed + + // simple hash of nodes, does not check if nodePool is compressed or not. + uint32_t acc = SEED + PRIME32_5; + + // add node types to map, and also ensure that fully empty nodes are well distributed by hashing the pointer. + acc += in_; + acc = acc ^ (acc >> 15); + acc = acc * PRIME32_2; + acc = acc ^ (acc >> 13); + acc = acc * PRIME32_3; + acc = acc ^ (acc >> 16); + return acc; +} + +bool TaskScheduler::TryRunTask( uint32_t threadNum_, uint32_t priority_, uint32_t& hintPipeToCheck_io_ ) +{ + // Run any tasks for this thread + RunPinnedTasks( threadNum_, priority_ ); + + // check for tasks + SubTaskSet subTask; + bool bHaveTask = m_pPipesPerThread[ priority_ ][ threadNum_ ].WriterTryReadFront( &subTask ); + + uint32_t threadToCheckStart = hintPipeToCheck_io_ % m_NumThreads; + uint32_t threadToCheck = threadToCheckStart; + uint32_t checkCount = 0; + if( !bHaveTask ) + { + bHaveTask = m_pPipesPerThread[ priority_ ][ threadToCheck ].ReaderTryReadBack( &subTask ); + if( !bHaveTask ) + { + // To prevent many threads checking the same task pipe for work we pseudorandomly distribute + // the starting thread which we start checking for tasks to run + uint32_t& rndSeed = m_pThreadDataStore[threadNum_].rndSeed; + ++rndSeed; + uint32_t threadToCheckOffset = Hash32( rndSeed * threadNum_ ); + while( !bHaveTask && checkCount < m_NumThreads ) + { + threadToCheck = ( threadToCheckOffset + checkCount ) % m_NumThreads; + if( threadToCheck != threadNum_ && threadToCheckOffset != threadToCheckStart ) + { + bHaveTask = m_pPipesPerThread[ priority_ ][ threadToCheck ].ReaderTryReadBack( &subTask ); + } + ++checkCount; + } + } + } + + if( bHaveTask ) + { + // update hint, will preserve value unless actually got task from another thread. + hintPipeToCheck_io_ = threadToCheck; + + uint32_t partitionSize = subTask.partition.end - subTask.partition.start; + if( subTask.pTask->m_RangeToRun < partitionSize ) + { + SubTaskSet taskToRun = SplitTask( subTask, subTask.pTask->m_RangeToRun ); + uint32_t rangeToSplit = subTask.pTask->m_RangeToRun; + if( threadNum_ != threadToCheck ) + { + // task was stolen from another thread + // in order to ensure other threads can get enough work we need to split into larger ranges + // these larger splits are then stolen and split themselves + // otherwise other threads must keep stealing from this thread, which may stall when pipe is full + rangeToSplit = std::max( rangeToSplit, (subTask.partition.end - subTask.partition.start) / gc_MaxStolenPartitions ); + } + SplitAndAddTask( threadNum_, subTask, rangeToSplit ); + taskToRun.pTask->ExecuteRange( taskToRun.partition, threadNum_ ); + int prevCount = taskToRun.pTask->m_RunningCount.fetch_sub(1,std::memory_order_acq_rel ); + if( gc_TaskStartCount == prevCount ) + { + TaskComplete( taskToRun.pTask, true, threadNum_ ); + } + } + else + { + // the task has already been divided up by AddTaskSetToPipe, so just run it + subTask.pTask->ExecuteRange( subTask.partition, threadNum_ ); + int prevCount = subTask.pTask->m_RunningCount.fetch_sub(1,std::memory_order_acq_rel ); + if( gc_TaskStartCount == prevCount ) + { + TaskComplete( subTask.pTask, true, threadNum_ ); + } + } + } + + return bHaveTask; + +} + +void TaskScheduler::TaskComplete( ICompletable* pTask_, bool bWakeThreads_, uint32_t threadNum_ ) +{ + // It must be impossible for a thread to enter the sleeping wait prior to the load of m_WaitingForTaskCount + // in this function, so we introduce a gc_TaskAlmostCompleteCount to prevent this. + ENKI_ASSERT( gc_TaskAlmostCompleteCount == pTask_->m_RunningCount.load( std::memory_order_acquire ) ); + bool bCallWakeThreads = bWakeThreads_ && pTask_->m_WaitingForTaskCount.load( std::memory_order_acquire ); + + Dependency* pDependent = pTask_->m_pDependents; + + // Do not access pTask_ below this line unless we have dependencies. + pTask_->m_RunningCount.store( 0, std::memory_order_release ); + + if( bCallWakeThreads ) + { + WakeThreadsForTaskCompletion(); + } + + while( pDependent ) + { + // access pTaskToRunOnCompletion member data before incrementing m_DependenciesCompletedCount so + // they do not get deleted when another thread completes the pTaskToRunOnCompletion + int32_t dependenciesCount = pDependent->pTaskToRunOnCompletion->m_DependenciesCount; + // get temp copy of pDependent so OnDependenciesComplete can delete task if needed. + Dependency* pDependentCurr = pDependent; + pDependent = pDependent->pNext; + int32_t prevDeps = pDependentCurr->pTaskToRunOnCompletion->m_DependenciesCompletedCount.fetch_add( 1, std::memory_order_release ); + ENKI_ASSERT( prevDeps < dependenciesCount ); + if( dependenciesCount == ( prevDeps + 1 ) ) + { + // reset dependencies + // only safe to access pDependentCurr here after above fetch_add because this is the thread + // which calls OnDependenciesComplete after store with memory_order_release + pDependentCurr->pTaskToRunOnCompletion->m_DependenciesCompletedCount.store( + 0, + std::memory_order_release ); + pDependentCurr->pTaskToRunOnCompletion->OnDependenciesComplete( this, threadNum_ ); + } + } +} + +bool TaskScheduler::HaveTasks( uint32_t threadNum_ ) +{ + for( int priority = 0; priority < TASK_PRIORITY_NUM; ++priority ) + { + for( uint32_t thread = 0; thread < m_NumThreads; ++thread ) + { + if( !m_pPipesPerThread[ priority ][ thread ].IsPipeEmpty() ) + { + return true; + } + } + if( !m_pPinnedTaskListPerThread[ priority ][ threadNum_ ].IsListEmpty() ) + { + return true; + } + } + return false; +} + +void TaskScheduler::WaitForNewTasks( uint32_t threadNum_ ) +{ + // We don't want to suspend this thread if there are task threads + // with pinned tasks suspended, as it could result in this thread + // being unsuspended and not the thread with pinned tasks + if( WakeSuspendedThreadsWithPinnedTasks( threadNum_ ) ) + { + return; + } + + // We increment the number of threads waiting here in order + // to ensure that the check for tasks occurs after the increment + // to prevent a task being added after a check, then the thread waiting. + // This will occasionally result in threads being mistakenly awoken, + // but they will then go back to sleep. + m_NumThreadsWaitingForNewTasks.fetch_add( 1, std::memory_order_acquire ); + ThreadState prevThreadState = m_pThreadDataStore[threadNum_].threadState.load( std::memory_order_relaxed ); + m_pThreadDataStore[threadNum_].threadState.store( ENKI_THREAD_STATE_WAIT_NEW_TASKS, std::memory_order_seq_cst ); + + if( HaveTasks( threadNum_ ) ) + { + m_NumThreadsWaitingForNewTasks.fetch_sub( 1, std::memory_order_release ); + } + else + { + SafeCallback( m_Config.profilerCallbacks.waitForNewTaskSuspendStart, threadNum_ ); + SemaphoreWait( *m_pNewTaskSemaphore ); + SafeCallback( m_Config.profilerCallbacks.waitForNewTaskSuspendStop, threadNum_ ); + } + + m_pThreadDataStore[threadNum_].threadState.store( prevThreadState, std::memory_order_release ); +} + +void TaskScheduler::WaitForTaskCompletion( const ICompletable* pCompletable_, uint32_t threadNum_ ) +{ + // We don't want to suspend this thread if there are task threads + // with pinned tasks suspended, as the completable could be a pinned task + // or it could be waiting on one. + if( WakeSuspendedThreadsWithPinnedTasks( threadNum_ ) ) + { + return; + } + + m_NumThreadsWaitingForTaskCompletion.fetch_add( 1, std::memory_order_acq_rel ); + pCompletable_->m_WaitingForTaskCount.fetch_add( 1, std::memory_order_acq_rel ); + ThreadState prevThreadState = m_pThreadDataStore[threadNum_].threadState.load( std::memory_order_relaxed ); + m_pThreadDataStore[threadNum_].threadState.store( ENKI_THREAD_STATE_WAIT_TASK_COMPLETION, std::memory_order_seq_cst ); + + // do not wait on semaphore if task in gc_TaskAlmostCompleteCount state. + if( gc_TaskAlmostCompleteCount >= pCompletable_->m_RunningCount.load( std::memory_order_acquire ) || HaveTasks( threadNum_ ) ) + { + m_NumThreadsWaitingForTaskCompletion.fetch_sub( 1, std::memory_order_acq_rel ); + } + else + { + SafeCallback( m_Config.profilerCallbacks.waitForTaskCompleteSuspendStart, threadNum_ ); + std::atomic_thread_fence(std::memory_order_acquire); + + SemaphoreWait( *m_pTaskCompleteSemaphore ); + if( !pCompletable_->GetIsComplete() ) + { + // This thread which may not the one which was supposed to be awoken + WakeThreadsForTaskCompletion(); + } + SafeCallback( m_Config.profilerCallbacks.waitForTaskCompleteSuspendStop, threadNum_ ); + } + + m_pThreadDataStore[threadNum_].threadState.store( prevThreadState, std::memory_order_release ); + pCompletable_->m_WaitingForTaskCount.fetch_sub( 1, std::memory_order_acq_rel ); +} + +void TaskScheduler::WakeThreadsForNewTasks() +{ + int32_t waiting = m_NumThreadsWaitingForNewTasks.load( std::memory_order_relaxed ); + while( waiting > 0 && !m_NumThreadsWaitingForNewTasks.compare_exchange_weak(waiting, 0, std::memory_order_release, std::memory_order_relaxed ) ) {} + + if( waiting > 0 ) + { + SemaphoreSignal( *m_pNewTaskSemaphore, waiting ); + } + + // We also wake tasks waiting for completion as they can run tasks + WakeThreadsForTaskCompletion(); +} + +void TaskScheduler::WakeThreadsForTaskCompletion() +{ + // m_NumThreadsWaitingForTaskCompletion can go negative as this indicates that + // we signalled more threads than the number which ended up waiting + int32_t waiting = m_NumThreadsWaitingForTaskCompletion.load( std::memory_order_relaxed ); + while( waiting > 0 && !m_NumThreadsWaitingForTaskCompletion.compare_exchange_weak(waiting, 0, std::memory_order_release, std::memory_order_relaxed ) ) {} + + if( waiting > 0 ) + { + SemaphoreSignal( *m_pTaskCompleteSemaphore, waiting ); + } +} + +bool TaskScheduler::WakeSuspendedThreadsWithPinnedTasks( uint32_t threadNum_ ) +{ + for( uint32_t t = 1; t < m_NumThreads; ++t ) + { + // distribute thread checks more evenly by starting at our thread number rather than 0. + uint32_t thread = ( threadNum_ + t ) % m_NumThreads; + + ThreadState state = m_pThreadDataStore[ thread ].threadState.load( std::memory_order_acquire ); + + ENKI_ASSERT( state != ENKI_THREAD_STATE_NONE ); + + if( state == ENKI_THREAD_STATE_WAIT_NEW_TASKS || state == ENKI_THREAD_STATE_WAIT_TASK_COMPLETION ) + { + // thread is suspended, check if it has pinned tasks + for( int priority = 0; priority < TASK_PRIORITY_NUM; ++priority ) + { + if( !m_pPinnedTaskListPerThread[ priority ][ thread ].IsListEmpty() ) + { + WakeThreadsForNewTasks(); + return true; + } + } + } + } + return false; +} + +void TaskScheduler::SplitAndAddTask( uint32_t threadNum_, SubTaskSet subTask_, uint32_t rangeToSplit_ ) +{ + int32_t numAdded = 0; + int32_t numNewTasksSinceNotification = 0; + int32_t numRun = 0; + + int32_t upperBoundNumToAdd = 2 + (int32_t)( ( subTask_.partition.end - subTask_.partition.start ) / rangeToSplit_ ); + + // ensure that an artificial completion is not registered whilst adding tasks by incrementing count + subTask_.pTask->m_RunningCount.fetch_add( upperBoundNumToAdd, std::memory_order_acquire ); + while( subTask_.partition.start != subTask_.partition.end ) + { + SubTaskSet taskToAdd = SplitTask( subTask_, rangeToSplit_ ); + + // add the partition to the pipe + ++numAdded; ++numNewTasksSinceNotification; + if( !m_pPipesPerThread[ subTask_.pTask->m_Priority ][ threadNum_ ].WriterTryWriteFront( taskToAdd ) ) + { + --numAdded; // we were unable to add the task + if( numNewTasksSinceNotification > 1 ) + { + WakeThreadsForNewTasks(); + } + numNewTasksSinceNotification = 0; + // alter range to run the appropriate fraction + if( taskToAdd.pTask->m_RangeToRun < taskToAdd.partition.end - taskToAdd.partition.start ) + { + taskToAdd.partition.end = taskToAdd.partition.start + taskToAdd.pTask->m_RangeToRun; + ENKI_ASSERT( taskToAdd.partition.end <= taskToAdd.pTask->m_SetSize ); + subTask_.partition.start = taskToAdd.partition.end; + } + taskToAdd.pTask->ExecuteRange( taskToAdd.partition, threadNum_ ); + ++numRun; + } + } + int32_t countToRemove = upperBoundNumToAdd - numAdded; + ENKI_ASSERT( countToRemove > 0 ); + int prevCount = subTask_.pTask->m_RunningCount.fetch_sub( countToRemove, std::memory_order_acq_rel ); + if( countToRemove-1 + gc_TaskStartCount == prevCount ) + { + TaskComplete( subTask_.pTask, false, threadNum_ ); + } + + // WakeThreadsForNewTasks also calls WakeThreadsForTaskCompletion() so do not need to do so above + WakeThreadsForNewTasks(); +} + +TaskSchedulerConfig TaskScheduler::GetConfig() const +{ + return m_Config; +} + +void TaskScheduler::AddTaskSetToPipeInt( ITaskSet* pTaskSet_, uint32_t threadNum_ ) +{ + ENKI_ASSERT( pTaskSet_->m_RunningCount == gc_TaskStartCount ); + ThreadState prevThreadState = m_pThreadDataStore[threadNum_].threadState.load( std::memory_order_relaxed ); + m_pThreadDataStore[threadNum_].threadState.store( ENKI_THREAD_STATE_RUNNING, std::memory_order_relaxed ); + std::atomic_thread_fence(std::memory_order_acquire); + + + // divide task up and add to pipe + pTaskSet_->m_RangeToRun = pTaskSet_->m_SetSize / m_NumPartitions; + pTaskSet_->m_RangeToRun = std::max( pTaskSet_->m_RangeToRun, pTaskSet_->m_MinRange ); + // Note: if m_SetSize is < m_RangeToRun this will be handled by SplitTask and so does not need to be handled here + + uint32_t rangeToSplit = pTaskSet_->m_SetSize / m_NumInitialPartitions; + rangeToSplit = std::max( rangeToSplit, pTaskSet_->m_MinRange ); + + SubTaskSet subTask; + subTask.pTask = pTaskSet_; + subTask.partition.start = 0; + subTask.partition.end = pTaskSet_->m_SetSize; + SplitAndAddTask( threadNum_, subTask, rangeToSplit ); + int prevCount = pTaskSet_->m_RunningCount.fetch_sub(1, std::memory_order_acq_rel ); + if( gc_TaskStartCount == prevCount ) + { + TaskComplete( pTaskSet_, true, threadNum_ ); + } + + m_pThreadDataStore[threadNum_].threadState.store( prevThreadState, std::memory_order_release ); +} + +void TaskScheduler::AddTaskSetToPipe( ITaskSet* pTaskSet_ ) +{ + ENKI_ASSERT( pTaskSet_->m_RunningCount == 0 ); + InitDependencies( pTaskSet_ ); + pTaskSet_->m_RunningCount.store( gc_TaskStartCount, std::memory_order_relaxed ); + AddTaskSetToPipeInt( pTaskSet_, gtl_threadNum ); +} + +void TaskScheduler::AddPinnedTaskInt( IPinnedTask* pTask_ ) +{ + ENKI_ASSERT( pTask_->m_RunningCount == gc_TaskStartCount ); + m_pPinnedTaskListPerThread[ pTask_->m_Priority ][ pTask_->threadNum ].WriterWriteFront( pTask_ ); + + ThreadState statePinnedTaskThread = m_pThreadDataStore[ pTask_->threadNum ].threadState.load( std::memory_order_acquire ); + if( statePinnedTaskThread == ENKI_THREAD_STATE_WAIT_NEW_PINNED_TASKS ) + { + SemaphoreSignal( *m_pThreadDataStore[ pTask_->threadNum ].pWaitNewPinnedTaskSemaphore, 1 ); + } + else + { + WakeThreadsForNewTasks(); + } +} + +void TaskScheduler::AddPinnedTask( IPinnedTask* pTask_ ) +{ + ENKI_ASSERT( pTask_->m_RunningCount == 0 ); + InitDependencies( pTask_ ); + pTask_->m_RunningCount = gc_TaskStartCount; + AddPinnedTaskInt( pTask_ ); +} + +void TaskScheduler::InitDependencies( ICompletable* pCompletable_ ) +{ + // go through any dependencies and set their running count so they show as not complete + // and increment dependency count + if( pCompletable_->m_RunningCount.load( std::memory_order_relaxed ) ) + { + // already initialized + return; + } + Dependency* pDependent = pCompletable_->m_pDependents; + while( pDependent ) + { + InitDependencies( pDependent->pTaskToRunOnCompletion ); + pDependent->pTaskToRunOnCompletion->m_RunningCount.store( gc_TaskStartCount, std::memory_order_relaxed ); + pDependent = pDependent->pNext; + } +} + + +void TaskScheduler::RunPinnedTasks() +{ + ENKI_ASSERT( gtl_threadNum != enki::NO_THREAD_NUM ); + uint32_t threadNum = gtl_threadNum; + ThreadState prevThreadState = m_pThreadDataStore[threadNum].threadState.load( std::memory_order_relaxed ); + m_pThreadDataStore[threadNum].threadState.store( ENKI_THREAD_STATE_RUNNING, std::memory_order_relaxed ); + std::atomic_thread_fence(std::memory_order_acquire); + for( int priority = 0; priority < TASK_PRIORITY_NUM; ++priority ) + { + RunPinnedTasks( threadNum, priority ); + } + m_pThreadDataStore[threadNum].threadState.store( prevThreadState, std::memory_order_release ); +} + +void TaskScheduler::RunPinnedTasks( uint32_t threadNum_, uint32_t priority_ ) +{ + IPinnedTask* pPinnedTaskSet = NULL; + do + { + pPinnedTaskSet = m_pPinnedTaskListPerThread[ priority_ ][ threadNum_ ].ReaderReadBack(); + if( pPinnedTaskSet ) + { + pPinnedTaskSet->Execute(); + pPinnedTaskSet->m_RunningCount.fetch_sub(1,std::memory_order_acq_rel); + TaskComplete( pPinnedTaskSet, true, threadNum_ ); + } + } while( pPinnedTaskSet ); +} + +void TaskScheduler::WaitforTask( const ICompletable* pCompletable_, enki::TaskPriority priorityOfLowestToRun_ ) +{ + ENKI_ASSERT( gtl_threadNum != enki::NO_THREAD_NUM ); + uint32_t threadNum = gtl_threadNum; + uint32_t hintPipeToCheck_io = threadNum + 1; // does not need to be clamped. + + // waiting for a task is equivalent to 'running' for thread state purpose as we may run tasks whilst waiting + ThreadState prevThreadState = m_pThreadDataStore[threadNum].threadState.load( std::memory_order_relaxed ); + m_pThreadDataStore[threadNum].threadState.store( ENKI_THREAD_STATE_RUNNING, std::memory_order_relaxed ); + std::atomic_thread_fence(std::memory_order_acquire); + + + if( pCompletable_ && !pCompletable_->GetIsComplete() ) + { + SafeCallback( m_Config.profilerCallbacks.waitForTaskCompleteStart, threadNum ); + // We need to ensure that the task we're waiting on can complete even if we're the only thread, + // so we clamp the priorityOfLowestToRun_ to no smaller than the task we're waiting for + priorityOfLowestToRun_ = std::max( priorityOfLowestToRun_, pCompletable_->m_Priority ); + uint32_t spinCount = 0; + while( !pCompletable_->GetIsComplete() && GetIsRunningInt() ) + { + ++spinCount; + for( int priority = 0; priority <= priorityOfLowestToRun_; ++priority ) + { + if( TryRunTask( threadNum, priority, hintPipeToCheck_io ) ) + { + spinCount = 0; // reset spin as ran a task + break; + } + } + if( spinCount > gc_SpinCount ) + { + WaitForTaskCompletion( pCompletable_, threadNum ); + spinCount = 0; + } + else + { + uint32_t spinBackoffCount = spinCount * gc_SpinBackOffMultiplier; + SpinWait( spinBackoffCount ); + } + } + SafeCallback( m_Config.profilerCallbacks.waitForTaskCompleteStop, threadNum ); + } + else if( nullptr == pCompletable_ ) + { + for( int priority = 0; priority <= priorityOfLowestToRun_; ++priority ) + { + if( TryRunTask( threadNum, priority, hintPipeToCheck_io ) ) + { + break; + } + } + } + + m_pThreadDataStore[threadNum].threadState.store( prevThreadState, std::memory_order_release ); + +} + +class TaskSchedulerWaitTask : public IPinnedTask +{ + void Execute() override + { + // do nothing + } +}; + +void TaskScheduler::WaitforAll() +{ + ENKI_ASSERT( gtl_threadNum != enki::NO_THREAD_NUM ); + m_bWaitforAllCalled.store( true, std::memory_order_release ); + + bool bHaveTasks = true; + uint32_t ourThreadNum = gtl_threadNum; + uint32_t hintPipeToCheck_io = ourThreadNum + 1; // does not need to be clamped. + bool otherThreadsRunning = false; // account for this thread + uint32_t spinCount = 0; + TaskSchedulerWaitTask dummyWaitTask; + dummyWaitTask.threadNum = 0; + while( GetIsRunningInt() && ( bHaveTasks || otherThreadsRunning ) ) + { + bHaveTasks = TryRunTask( ourThreadNum, hintPipeToCheck_io ); + ++spinCount; + if( bHaveTasks ) + { + spinCount = 0; // reset spin as ran a task + } + if( spinCount > gc_SpinCount ) + { + // find a running thread and add a dummy wait task + int32_t countThreadsToCheck = m_NumThreads - 1; + bool bHaveThreadToWaitOn = false; + do + { + --countThreadsToCheck; + dummyWaitTask.threadNum = ( dummyWaitTask.threadNum + 1 ) % m_NumThreads; + + // We can only add a pinned task to wait on if we find an enki Task Thread which isn't this thread. + // Otherwise, we have to busy wait. + if( dummyWaitTask.threadNum != ourThreadNum && dummyWaitTask.threadNum > m_Config.numExternalTaskThreads ) + { + ThreadState state = m_pThreadDataStore[ dummyWaitTask.threadNum ].threadState.load( std::memory_order_acquire ); + if( state == ENKI_THREAD_STATE_RUNNING || state == ENKI_THREAD_STATE_WAIT_TASK_COMPLETION ) + { + bHaveThreadToWaitOn = true; + break; + } + } + } while( countThreadsToCheck ); + + if( bHaveThreadToWaitOn ) + { + ENKI_ASSERT( dummyWaitTask.threadNum != ourThreadNum ); + AddPinnedTask( &dummyWaitTask ); + WaitforTask( &dummyWaitTask ); + } + spinCount = 0; + } + else + { + uint32_t spinBackoffCount = spinCount * gc_SpinBackOffMultiplier; + SpinWait( spinBackoffCount ); + } + + // count threads running + otherThreadsRunning = false; + for(uint32_t thread = 0; thread < m_NumThreads && !otherThreadsRunning; ++thread ) + { + // ignore our thread + if( thread != ourThreadNum ) + { + switch( m_pThreadDataStore[thread].threadState.load( std::memory_order_acquire ) ) + { + case ENKI_THREAD_STATE_NONE: + ENKI_ASSERT(false); + break; + case ENKI_THREAD_STATE_NOT_LAUNCHED: + case ENKI_THREAD_STATE_RUNNING: + case ENKI_THREAD_STATE_WAIT_TASK_COMPLETION: + otherThreadsRunning = true; + break; + case ENKI_THREAD_STATE_WAIT_NEW_PINNED_TASKS: + otherThreadsRunning = true; + SemaphoreSignal( *m_pThreadDataStore[thread].pWaitNewPinnedTaskSemaphore, 1 ); + break; + case ENKI_THREAD_STATE_PRIMARY_REGISTERED: + case ENKI_THREAD_STATE_EXTERNAL_REGISTERED: + case ENKI_THREAD_STATE_EXTERNAL_UNREGISTERED: + case ENKI_THREAD_STATE_WAIT_NEW_TASKS: + case ENKI_THREAD_STATE_STOPPED: + break; + } + } + } + if( !otherThreadsRunning ) + { + // check there are no tasks + for(uint32_t thread = 0; thread < m_NumThreads && !otherThreadsRunning; ++thread ) + { + // ignore our thread + if( thread != ourThreadNum ) + { + otherThreadsRunning = HaveTasks( thread ); + } + } + } + } + + m_bWaitforAllCalled.store( false, std::memory_order_release ); +} + +void TaskScheduler::WaitforAllAndShutdown() +{ + m_bWaitforAllCalled.store( true, std::memory_order_release ); + m_bShutdownRequested.store( true, std::memory_order_release ); + if( m_bHaveThreads ) + { + WaitforAll(); + StopThreads(true); + } +} + +void TaskScheduler::ShutdownNow() +{ + m_bWaitforAllCalled.store( true, std::memory_order_release ); + m_bShutdownRequested.store( true, std::memory_order_release ); + if( m_bHaveThreads ) + { + StopThreads(true); + } +} + +void TaskScheduler::WaitForNewPinnedTasks() +{ + ENKI_ASSERT( gtl_threadNum != enki::NO_THREAD_NUM ); + uint32_t threadNum = gtl_threadNum; + ThreadState prevThreadState = m_pThreadDataStore[threadNum].threadState.load( std::memory_order_relaxed ); + m_pThreadDataStore[threadNum].threadState.store( ENKI_THREAD_STATE_WAIT_NEW_PINNED_TASKS, std::memory_order_seq_cst ); + + // check if have tasks inside threadState change but before waiting + bool bHavePinnedTasks = false; + for( int priority = 0; priority < TASK_PRIORITY_NUM; ++priority ) + { + if( !m_pPinnedTaskListPerThread[ priority ][ threadNum ].IsListEmpty() ) + { + bHavePinnedTasks = true; + break; + } + } + + if( !bHavePinnedTasks ) + { + SafeCallback( m_Config.profilerCallbacks.waitForNewTaskSuspendStart, threadNum ); + SemaphoreWait( *m_pThreadDataStore[threadNum].pWaitNewPinnedTaskSemaphore ); + SafeCallback( m_Config.profilerCallbacks.waitForNewTaskSuspendStop, threadNum ); + } + + m_pThreadDataStore[threadNum].threadState.store( prevThreadState, std::memory_order_release ); +} + + +uint32_t TaskScheduler::GetNumTaskThreads() const +{ + return m_NumThreads; +} + + +uint32_t TaskScheduler::GetThreadNum() const +{ + return gtl_threadNum; +} + +template +T* TaskScheduler::NewArray( size_t num_, const char* file_, int line_ ) +{ + T* pRet = (T*)m_Config.customAllocator.alloc( alignof(T), num_*sizeof(T), m_Config.customAllocator.userData, file_, line_ ); + if( !std::is_trivial::value ) + { + T* pCurr = pRet; + for( size_t i = 0; i < num_; ++i ) + { + void* pBuffer = pCurr; + pCurr = new(pBuffer) T; + ++pCurr; + } + } + return pRet; +} + +template +void TaskScheduler::DeleteArray( T* p_, size_t num_, const char* file_, int line_ ) +{ + if( !std::is_trivially_destructible::value ) + { + size_t i = num_; + while(i) + { + p_[--i].~T(); + } + } + m_Config.customAllocator.free( p_, sizeof(T)*num_, m_Config.customAllocator.userData, file_, line_ ); +} + +template +T* TaskScheduler::New( const char* file_, int line_, Args&&... args_ ) +{ + T* pRet = this->Alloc( file_, line_ ); + return new(pRet) T( std::forward(args_)... ); +} + +template< typename T > +void TaskScheduler::Delete( T* p_, const char* file_, int line_ ) +{ + p_->~T(); + this->Free(p_, file_, line_ ); +} + +template< typename T > +T* TaskScheduler::Alloc( const char* file_, int line_ ) +{ + T* pRet = (T*)m_Config.customAllocator.alloc( alignof(T), sizeof(T), m_Config.customAllocator.userData, file_, line_ ); + return pRet; +} + +template< typename T > +void TaskScheduler::Free( T* p_, const char* file_, int line_ ) +{ + m_Config.customAllocator.free( p_, sizeof(T), m_Config.customAllocator.userData, file_, line_ ); +} + +TaskScheduler::TaskScheduler() + : m_pPipesPerThread() + , m_pPinnedTaskListPerThread() + , m_NumThreads(0) + , m_pThreadDataStore(NULL) + , m_pThreads(NULL) + , m_bRunning(false) + , m_NumInternalTaskThreadsRunning(0) + , m_NumThreadsWaitingForNewTasks(0) + , m_NumThreadsWaitingForTaskCompletion(0) + , m_NumPartitions(0) + , m_pNewTaskSemaphore(NULL) + , m_pTaskCompleteSemaphore(NULL) + , m_NumInitialPartitions(0) + , m_bHaveThreads(false) + , m_NumExternalTaskThreadsRegistered(0) +{ +} + +TaskScheduler::~TaskScheduler() +{ + StopThreads( true ); // Stops threads, waiting for them. +} + +void TaskScheduler::Initialize( uint32_t numThreadsTotal_ ) +{ + ENKI_ASSERT( numThreadsTotal_ >= 1 ); + StopThreads( true ); // Stops threads, waiting for them. + m_Config.numTaskThreadsToCreate = numThreadsTotal_ - 1; + m_Config.numExternalTaskThreads = 0; + StartThreads();} + +void TaskScheduler::Initialize( TaskSchedulerConfig config_ ) +{ + StopThreads( true ); // Stops threads, waiting for them. + m_Config = config_; + StartThreads(); +} + +void TaskScheduler::Initialize() +{ + Initialize( std::thread::hardware_concurrency() ); +} + +// Semaphore implementation +#ifdef _WIN32 + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include + +namespace enki +{ + struct semaphoreid_t + { + HANDLE sem; + }; + + inline void SemaphoreCreate( semaphoreid_t& semaphoreid ) + { +#ifdef _XBOX_ONE + semaphoreid.sem = CreateSemaphoreExW( NULL, 0, MAXLONG, NULL, 0, SEMAPHORE_ALL_ACCESS ); +#else + semaphoreid.sem = CreateSemaphore( NULL, 0, MAXLONG, NULL ); +#endif + } + + inline void SemaphoreClose( semaphoreid_t& semaphoreid ) + { + CloseHandle( semaphoreid.sem ); + } + + inline void SemaphoreWait( semaphoreid_t& semaphoreid ) + { + DWORD retval = WaitForSingleObject( semaphoreid.sem, INFINITE ); + ENKI_ASSERT( retval != WAIT_FAILED ); + (void)retval; // only needed for ENKI_ASSERT + } + + inline void SemaphoreSignal( semaphoreid_t& semaphoreid, int32_t countWaiting ) + { + if( countWaiting ) + { + ReleaseSemaphore( semaphoreid.sem, countWaiting, NULL ); + } + } +} +#elif defined(__MACH__) + + +// OS X does not have POSIX semaphores +// Mach semaphores can now only be created by the kernel +// Named semaphores work, but would require unique name construction to ensure +// they are isolated to this process. +// Dispatch semaphores appear to be the way other developers use OSX Semaphores, e.g. Boost +// However the API could change +// OSX below 10.6 does not support dispatch, but I do not have an earlier OSX version +// to test alternatives +#include + +namespace enki +{ + + struct semaphoreid_t + { + dispatch_semaphore_t sem; + }; + + inline void SemaphoreCreate( semaphoreid_t& semaphoreid ) + { + semaphoreid.sem = dispatch_semaphore_create(0); + } + + inline void SemaphoreClose( semaphoreid_t& semaphoreid ) + { + dispatch_release( semaphoreid.sem ); + } + + inline void SemaphoreWait( semaphoreid_t& semaphoreid ) + { + dispatch_semaphore_wait( semaphoreid.sem, DISPATCH_TIME_FOREVER ); + } + + inline void SemaphoreSignal( semaphoreid_t& semaphoreid, int32_t countWaiting ) + { + while( countWaiting-- > 0 ) + { + dispatch_semaphore_signal( semaphoreid.sem ); + } + } +} + +#else // POSIX + +#include +#include + +namespace enki +{ + + struct semaphoreid_t + { + sem_t sem; + }; + + inline void SemaphoreCreate( semaphoreid_t& semaphoreid ) + { + int err = sem_init( &semaphoreid.sem, 0, 0 ); + ENKI_ASSERT( err == 0 ); + (void)err; + } + + inline void SemaphoreClose( semaphoreid_t& semaphoreid ) + { + sem_destroy( &semaphoreid.sem ); + } + + inline void SemaphoreWait( semaphoreid_t& semaphoreid ) + { + while( sem_wait( &semaphoreid.sem ) == -1 && errno == EINTR ) {} + } + + inline void SemaphoreSignal( semaphoreid_t& semaphoreid, int32_t countWaiting ) + { + while( countWaiting-- > 0 ) + { + sem_post( &semaphoreid.sem ); + } + } +} +#endif + +semaphoreid_t* TaskScheduler::SemaphoreNew() +{ + semaphoreid_t* pSemaphore = this->Alloc( ENKI_FILE_AND_LINE ); + SemaphoreCreate( *pSemaphore ); + return pSemaphore; +} + +void TaskScheduler::SemaphoreDelete( semaphoreid_t* pSemaphore_ ) +{ + SemaphoreClose( *pSemaphore_ ); + this->Free( pSemaphore_, ENKI_FILE_AND_LINE ); +} + +void TaskScheduler::SetCustomAllocator( CustomAllocator customAllocator_ ) +{ + m_Config.customAllocator = customAllocator_; +} + +Dependency::Dependency( const ICompletable* pDependencyTask_, ICompletable* pTaskToRunOnCompletion_ ) + : pTaskToRunOnCompletion( pTaskToRunOnCompletion_ ) + , pDependencyTask( pDependencyTask_ ) + , pNext( pDependencyTask->m_pDependents ) +{ + ENKI_ASSERT( pDependencyTask->GetIsComplete() ); + ENKI_ASSERT( pTaskToRunOnCompletion->GetIsComplete() ); + pDependencyTask->m_pDependents = this; + ++pTaskToRunOnCompletion->m_DependenciesCount; +} + +Dependency::Dependency( Dependency&& rhs_ ) noexcept +{ + pDependencyTask = rhs_.pDependencyTask; + pTaskToRunOnCompletion = rhs_.pTaskToRunOnCompletion; + pNext = rhs_.pNext; + if( rhs_.pDependencyTask ) + { + ENKI_ASSERT( rhs_.pTaskToRunOnCompletion ); + ENKI_ASSERT( rhs_.pDependencyTask->GetIsComplete() ); + ENKI_ASSERT( rhs_.pTaskToRunOnCompletion->GetIsComplete() ); + Dependency** ppDependent = &(pDependencyTask->m_pDependents); + while( *ppDependent ) + { + if( &rhs_ == *ppDependent ) + { + *ppDependent = this; + break; + } + ppDependent = &((*ppDependent)->pNext); + } + } +} + + +Dependency::~Dependency() +{ + ClearDependency(); +} + +void Dependency::SetDependency( const ICompletable* pDependencyTask_, ICompletable* pTaskToRunOnCompletion_ ) +{ + ClearDependency(); + ENKI_ASSERT( pDependencyTask_->GetIsComplete() ); + ENKI_ASSERT( pTaskToRunOnCompletion_->GetIsComplete() ); + pDependencyTask = pDependencyTask_; + pTaskToRunOnCompletion = pTaskToRunOnCompletion_; + pNext = pDependencyTask->m_pDependents; + pDependencyTask->m_pDependents = this; + ++pTaskToRunOnCompletion->m_DependenciesCount; +} + +void Dependency::ClearDependency() +{ + if( pDependencyTask ) + { + ENKI_ASSERT( pTaskToRunOnCompletion ); + ENKI_ASSERT( pDependencyTask->GetIsComplete() ); + ENKI_ASSERT( pTaskToRunOnCompletion->GetIsComplete() ); + ENKI_ASSERT( pTaskToRunOnCompletion->m_DependenciesCount > 0 ); + Dependency* pDependent = pDependencyTask->m_pDependents; + --pTaskToRunOnCompletion->m_DependenciesCount; + if( this == pDependent ) + { + pDependencyTask->m_pDependents = pDependent->pNext; + } + else + { + while( pDependent ) + { + Dependency* pPrev = pDependent; + pDependent = pDependent->pNext; + if( this == pDependent ) + { + pPrev->pNext = pDependent->pNext; + break; + } + } + } + } + pDependencyTask = NULL; + pDependencyTask = NULL; + pNext = NULL; +} diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/TaskScheduler.h b/tests/cpp-tests/Source/Box2DTestBed/samples/TaskScheduler.h new file mode 100644 index 000000000000..9d1d962ff7f5 --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/TaskScheduler.h @@ -0,0 +1,583 @@ +// Copyright (c) 2013 Doug Binks +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgement in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. + +#pragma once + +#include +#include +#include +#include +#include + +// ENKITS_TASK_PRIORITIES_NUM can be set from 1 to 5. +// 1 corresponds to effectively no priorities. +#ifndef ENKITS_TASK_PRIORITIES_NUM + #define ENKITS_TASK_PRIORITIES_NUM 3 +#endif + +#ifndef ENKITS_API +#if defined(_WIN32) && defined(ENKITS_BUILD_DLL) + // Building enkiTS as a DLL + #define ENKITS_API __declspec(dllexport) +#elif defined(_WIN32) && defined(ENKITS_DLL) + // Using enkiTS as a DLL + #define ENKITS_API __declspec(dllimport) +#elif defined(__GNUC__) && defined(ENKITS_BUILD_DLL) + // Building enkiTS as a shared library + #define ENKITS_API __attribute__((visibility("default"))) +#else + #define ENKITS_API +#endif +#endif + +// Define ENKI_CUSTOM_ALLOC_FILE_AND_LINE (at project level) to get file and line report in custom allocators, +// this is default in Debug - to turn off define ENKI_CUSTOM_ALLOC_NO_FILE_AND_LINE +#ifndef ENKI_CUSTOM_ALLOC_FILE_AND_LINE +#if defined(_DEBUG ) && !defined(ENKI_CUSTOM_ALLOC_NO_FILE_AND_LINE) +#define ENKI_CUSTOM_ALLOC_FILE_AND_LINE +#endif +#endif + +#ifndef ENKI_ASSERT +#include +#define ENKI_ASSERT(x) assert(x) +#endif + +#if (!defined(_MSVC_LANG) && __cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) +#define ENKI_DEPRECATED [[deprecated]] +#else +#define ENKI_DEPRECATED +#endif + +namespace enki +{ + struct TaskSetPartition + { + uint32_t start; + uint32_t end; + }; + + class TaskScheduler; + class TaskPipe; + class PinnedTaskList; + class Dependency; + struct ThreadArgs; + struct ThreadDataStore; + struct SubTaskSet; + struct semaphoreid_t; + + static constexpr uint32_t NO_THREAD_NUM = 0xFFFFFFFF; + + ENKITS_API uint32_t GetNumHardwareThreads(); + + enum TaskPriority + { + TASK_PRIORITY_HIGH = 0, +#if ( ENKITS_TASK_PRIORITIES_NUM > 3 ) + TASK_PRIORITY_MED_HI, +#endif +#if ( ENKITS_TASK_PRIORITIES_NUM > 2 ) + TASK_PRIORITY_MED, +#endif +#if ( ENKITS_TASK_PRIORITIES_NUM > 4 ) + TASK_PRIORITY_MED_LO, +#endif +#if ( ENKITS_TASK_PRIORITIES_NUM > 1 ) + TASK_PRIORITY_LOW, +#endif + TASK_PRIORITY_NUM + }; + + // ICompletable is a base class used to check for completion. + // Can be used with dependencies to wait for their completion. + // Derive from ITaskSet or IPinnedTask for running parallel tasks. + class ICompletable + { + public: + bool GetIsComplete() const { + return 0 == m_RunningCount.load( std::memory_order_acquire ); + } + + virtual ~ICompletable(); + + // Dependency helpers, see Dependencies.cpp + void SetDependency( Dependency& dependency_, const ICompletable* pDependencyTask_ ); + template void SetDependenciesArr( D& dependencyArray_ , const T(&taskArray_)[SIZE] ); + template void SetDependenciesArr( D& dependencyArray_, std::initializer_list taskpList_ ); + template void SetDependenciesArr( D(&dependencyArray_)[SIZE], const T(&taskArray_)[SIZE] ); + template void SetDependenciesArr( D(&dependencyArray_)[SIZE], std::initializer_list taskpList_ ); + template void SetDependenciesVec( D& dependencyVec_, const T(&taskArray_)[SIZE] ); + template void SetDependenciesVec( D& dependencyVec_, std::initializer_list taskpList_ ); + + TaskPriority m_Priority = TASK_PRIORITY_HIGH; + protected: + // Deriving from an ICompletable and overriding OnDependenciesComplete is advanced use. + // If you do override OnDependenciesComplete() call: + // ICompletable::OnDependenciesComplete( pTaskScheduler_, threadNum_ ); + // in your implementation. + virtual void OnDependenciesComplete( TaskScheduler* pTaskScheduler_, uint32_t threadNum_ ); + private: + friend class TaskScheduler; + friend class Dependency; + std::atomic m_RunningCount = {0}; + std::atomic m_DependenciesCompletedCount = {0}; + int32_t m_DependenciesCount = 0; + mutable std::atomic m_WaitingForTaskCount = {0}; + mutable Dependency* m_pDependents = NULL; + }; + + // Subclass ITaskSet to create tasks. + // TaskSets can be re-used, but check completion first. + class ITaskSet : public ICompletable + { + public: + ITaskSet() = default; + ITaskSet( uint32_t setSize_ ) + : m_SetSize( setSize_ ) + {} + + ITaskSet( uint32_t setSize_, uint32_t minRange_ ) + : m_SetSize( setSize_ ) + , m_MinRange( minRange_ ) + , m_RangeToRun(minRange_) + {} + + // Execute range should be overloaded to process tasks. It will be called with a + // range_ where range.start >= 0; range.start < range.end; and range.end < m_SetSize; + // The range values should be mapped so that linearly processing them in order is cache friendly + // i.e. neighbouring values should be close together. + // threadnum_ should not be used for changing processing of data, its intended purpose + // is to allow per-thread data buckets for output. + virtual void ExecuteRange( TaskSetPartition range_, uint32_t threadnum_ ) = 0; + + // Set Size - usually the number of data items to be processed, see ExecuteRange. Defaults to 1 + uint32_t m_SetSize = 1; + + // Min Range - Minimum size of TaskSetPartition range when splitting a task set into partitions. + // Designed for reducing scheduling overhead by preventing set being + // divided up too small. Ranges passed to ExecuteRange will *not* be a multiple of this, + // only attempts to deliver range sizes larger than this most of the time. + // This should be set to a value which results in computation effort of at least 10k + // clock cycles to minimize task scheduler overhead. + // NOTE: The last partition will be smaller than m_MinRange if m_SetSize is not a multiple + // of m_MinRange. + // Also known as grain size in literature. + uint32_t m_MinRange = 1; + + private: + friend class TaskScheduler; + void OnDependenciesComplete( TaskScheduler* pTaskScheduler_, uint32_t threadNum_ ) final; + uint32_t m_RangeToRun = 1; + }; + + // Subclass IPinnedTask to create tasks which can be run on a given thread only. + class IPinnedTask : public ICompletable + { + public: + IPinnedTask() = default; + IPinnedTask( uint32_t threadNum_ ) : threadNum(threadNum_) {} // default is to run a task on main thread + + // IPinnedTask needs to be non-abstract for intrusive list functionality. + // Should never be called as is, should be overridden. + virtual void Execute() { ENKI_ASSERT(false); } + + uint32_t threadNum = 0; // thread to run this pinned task on + std::atomic pNext = {NULL}; + private: + void OnDependenciesComplete( TaskScheduler* pTaskScheduler_, uint32_t threadNum_ ) final; + }; + + // TaskSet - a utility task set for creating tasks based on std::function. + typedef std::function TaskSetFunction; + class TaskSet : public ITaskSet + { + public: + TaskSet() = default; + TaskSet( TaskSetFunction func_ ) : m_Function( std::move(func_) ) {} + TaskSet( uint32_t setSize_, TaskSetFunction func_ ) : ITaskSet( setSize_ ), m_Function( std::move(func_) ) {} + + void ExecuteRange( TaskSetPartition range_, uint32_t threadnum_ ) override { m_Function( range_, threadnum_ ); } + TaskSetFunction m_Function; + }; + + // LambdaPinnedTask - a utility pinned task for creating tasks based on std::func. + typedef std::function PinnedTaskFunction; + class LambdaPinnedTask : public IPinnedTask + { + public: + LambdaPinnedTask() = default; + LambdaPinnedTask( PinnedTaskFunction func_ ) : m_Function( std::move(func_) ) {} + LambdaPinnedTask( uint32_t threadNum_, PinnedTaskFunction func_ ) : IPinnedTask( threadNum_ ), m_Function( std::move(func_) ) {} + + void Execute() override { m_Function(); } + PinnedTaskFunction m_Function; + }; + + class Dependency + { + public: + Dependency() = default; + Dependency( const Dependency& ) = delete; + ENKITS_API Dependency( Dependency&& ) noexcept; + ENKITS_API Dependency( const ICompletable* pDependencyTask_, ICompletable* pTaskToRunOnCompletion_ ); + ENKITS_API ~Dependency(); + + ENKITS_API void SetDependency( const ICompletable* pDependencyTask_, ICompletable* pTaskToRunOnCompletion_ ); + ENKITS_API void ClearDependency(); + ICompletable* GetTaskToRunOnCompletion() { return pTaskToRunOnCompletion; } + const ICompletable* GetDependencyTask() { return pDependencyTask; } + private: + friend class TaskScheduler; friend class ICompletable; + ICompletable* pTaskToRunOnCompletion = NULL; + const ICompletable* pDependencyTask = NULL; + Dependency* pNext = NULL; + }; + + // TaskScheduler implements several callbacks intended for profilers + typedef void (*ProfilerCallbackFunc)( uint32_t threadnum_ ); + struct ProfilerCallbacks + { + ProfilerCallbackFunc threadStart; + ProfilerCallbackFunc threadStop; + ProfilerCallbackFunc waitForNewTaskSuspendStart; // thread suspended waiting for new tasks + ProfilerCallbackFunc waitForNewTaskSuspendStop; // thread unsuspended + ProfilerCallbackFunc waitForTaskCompleteStart; // thread waiting for task completion + ProfilerCallbackFunc waitForTaskCompleteStop; // thread stopped waiting + ProfilerCallbackFunc waitForTaskCompleteSuspendStart; // thread suspended waiting task completion + ProfilerCallbackFunc waitForTaskCompleteSuspendStop; // thread unsuspended + }; + + // Custom allocator, set in TaskSchedulerConfig. Also see ENKI_CUSTOM_ALLOC_FILE_AND_LINE for file_ and line_ + typedef void* (*AllocFunc)( size_t align_, size_t size_, void* userData_, const char* file_, int line_ ); + typedef void (*FreeFunc)( void* ptr_, size_t size_, void* userData_, const char* file_, int line_ ); + ENKITS_API void* DefaultAllocFunc( size_t align_, size_t size_, void* userData_, const char* file_, int line_ ); + ENKITS_API void DefaultFreeFunc( void* ptr_, size_t size_, void* userData_, const char* file_, int line_ ); + struct CustomAllocator + { + AllocFunc alloc = DefaultAllocFunc; + FreeFunc free = DefaultFreeFunc; + void* userData = nullptr; + }; + + // TaskSchedulerConfig - configuration struct for advanced Initialize + struct TaskSchedulerConfig + { + // numTaskThreadsToCreate - Number of tasking threads the task scheduler will create. Must be > 0. + // Defaults to GetNumHardwareThreads()-1 threads as thread which calls initialize is thread 0. + uint32_t numTaskThreadsToCreate = GetNumHardwareThreads()-1; + + // numExternalTaskThreads - Advanced use. Number of external threads which need to use TaskScheduler API. + // See TaskScheduler::RegisterExternalTaskThread() for usage. + // Defaults to 0. The thread used to initialize the TaskScheduler can also use the TaskScheduler API. + // Thus there are (numTaskThreadsToCreate + numExternalTaskThreads + 1) able to use the API, with this + // defaulting to the number of hardware threads available to the system. + uint32_t numExternalTaskThreads = 0; + + ProfilerCallbacks profilerCallbacks = {}; + + CustomAllocator customAllocator; + }; + + class TaskScheduler + { + public: + ENKITS_API TaskScheduler(); + ENKITS_API ~TaskScheduler(); + + // Call an Initialize function before adding tasks. + + // Initialize() will create GetNumHardwareThreads()-1 tasking threads, which is + // sufficient to fill the system when including the main thread. + // Initialize can be called multiple times - it will wait for completion + // before re-initializing. + ENKITS_API void Initialize(); + + // Initialize( numThreadsTotal_ ) + // will create numThreadsTotal_-1 threads, as thread 0 is + // the thread on which the initialize was called. + // numThreadsTotal_ must be > 0 + ENKITS_API void Initialize( uint32_t numThreadsTotal_ ); + + // Initialize with advanced TaskSchedulerConfig settings. See TaskSchedulerConfig. + ENKITS_API void Initialize( TaskSchedulerConfig config_ ); + + // Get config. Can be called before Initialize to get the defaults. + ENKITS_API TaskSchedulerConfig GetConfig() const; + + // while( !GetIsShutdownRequested() ) {} can be used in tasks which loop, to check if enkiTS has been requested to shutdown. + // If GetIsShutdownRequested() returns true should then exit. Not required for finite tasks + // Safe to use with WaitforAllAndShutdown() and ShutdownNow() where this will be set + // Not safe to use with WaitforAll(), use GetIsWaitforAllCalled() instead. + inline bool GetIsShutdownRequested() const { return m_bShutdownRequested.load( std::memory_order_acquire ); } + + // while( !GetIsWaitforAllCalled() ) {} can be used in tasks which loop, to check if WaitforAll() has been called. + // If GetIsWaitforAllCalled() returns false should then exit. Not required for finite tasks + // This is intended to be used with code which calls WaitforAll(). + // This is also set when the task manager is shutting down, so no need to have an additional check for GetIsShutdownRequested() + inline bool GetIsWaitforAllCalled() const { return m_bWaitforAllCalled.load( std::memory_order_acquire ); } + + // Adds the TaskSet to pipe and returns if the pipe is not full. + // If the pipe is full, pTaskSet is run. + // should only be called from main thread, or within a task + ENKITS_API void AddTaskSetToPipe( ITaskSet* pTaskSet_ ); + + // Thread 0 is main thread, otherwise use threadNum + // Pinned tasks can be added from any thread + ENKITS_API void AddPinnedTask( IPinnedTask* pTask_ ); + + // This function will run any IPinnedTask* for current thread, but not run other + // Main thread should call this or use a wait to ensure its tasks are run. + ENKITS_API void RunPinnedTasks(); + + // Runs the TaskSets in pipe until true == pTaskSet->GetIsComplete(); + // Should only be called from thread which created the task scheduler, or within a task. + // If called with 0 it will try to run tasks, and return if none are available. + // To run only a subset of tasks, set priorityOfLowestToRun_ to a high priority. + // Default is lowest priority available. + // Only wait for child tasks of the current task otherwise a deadlock could occur. + // WaitforTask will exit if ShutdownNow() is called even if pCompletable_ is not complete. + ENKITS_API void WaitforTask( const ICompletable* pCompletable_, enki::TaskPriority priorityOfLowestToRun_ = TaskPriority(TASK_PRIORITY_NUM - 1) ); + + // Waits for all task sets to complete - not guaranteed to work unless we know we + // are in a situation where tasks aren't being continuously added. + // If you are running tasks which loop, make sure to check GetIsWaitforAllCalled() and exit + // WaitforAll will exit if ShutdownNow() is called even if there are still tasks to run or currently running + ENKITS_API void WaitforAll(); + + // Waits for all task sets to complete and shutdown threads - not guaranteed to work unless we know we + // are in a situation where tasks aren't being continuously added. + // This function can be safely called even if TaskScheduler::Initialize() has not been called. + ENKITS_API void WaitforAllAndShutdown(); + + // Shutdown threads without waiting for all tasks to complete. + // Intended to be used to exit an application quickly. + // This function can be safely called even if TaskScheduler::Initialize() has not been called. + // This function will still wait for any running tasks to exit before the task threads exit. + // ShutdownNow will cause tasks which have been added to the scheduler but not completed + // to be in an undefined state in which should not be re-launched. + ENKITS_API void ShutdownNow(); + + // Waits for the current thread to receive a PinnedTask. + // Will not run any tasks - use with RunPinnedTasks(). + // Can be used with both ExternalTaskThreads or with an enkiTS tasking thread to create + // a thread which only runs pinned tasks. If enkiTS threads are used can create + // extra enkiTS task threads to handle non-blocking computation via normal tasks. + ENKITS_API void WaitForNewPinnedTasks(); + + // Returns the number of threads created for running tasks + number of external threads + // plus 1 to account for the thread used to initialize the task scheduler. + // Equivalent to config values: numTaskThreadsToCreate + numExternalTaskThreads + 1. + // It is guaranteed that GetThreadNum() < GetNumTaskThreads() + ENKITS_API uint32_t GetNumTaskThreads() const; + + // Returns the current task threadNum. + // Will return 0 for thread which initialized the task scheduler, + // and NO_THREAD_NUM for all other non-enkiTS threads which have not been registered ( see RegisterExternalTaskThread() ), + // and < GetNumTaskThreads() for all registered and internal enkiTS threads. + // It is guaranteed that GetThreadNum() < GetNumTaskThreads() unless it is NO_THREAD_NUM + ENKITS_API uint32_t GetThreadNum() const; + + // Call on a thread to register the thread to use the TaskScheduling API. + // This is implicitly done for the thread which initializes the TaskScheduler + // Intended for developers who have threads who need to call the TaskScheduler API + // Returns true if successful, false if not. + // Can only have numExternalTaskThreads registered at any one time, which must be set + // at initialization time. + ENKITS_API bool RegisterExternalTaskThread(); + + // As RegisterExternalTaskThread() but explicitly requests a given thread number. + // threadNumToRegister_ must be >= GetNumFirstExternalTaskThread() + // and < ( GetNumFirstExternalTaskThread() + numExternalTaskThreads ). + ENKITS_API bool RegisterExternalTaskThread( uint32_t threadNumToRegister_ ); + + // Call on a thread on which RegisterExternalTaskThread has been called to deregister that thread. + ENKITS_API void DeRegisterExternalTaskThread(); + + // Get the number of registered external task threads. + ENKITS_API uint32_t GetNumRegisteredExternalTaskThreads(); + + // Get the thread number of the first external task thread. This thread + // is not guaranteed to be registered, but threads are registered in order + // from GetNumFirstExternalTaskThread() up to ( GetNumFirstExternalTaskThread() + numExternalTaskThreads ) + // Note that if numExternalTaskThreads == 0 a for loop using this will be valid: + // for( uint32_t externalThreadNum = GetNumFirstExternalTaskThread(); + // externalThreadNum < ( GetNumFirstExternalTaskThread() + numExternalTaskThreads + // ++externalThreadNum ) { // do something with externalThreadNum } + inline static constexpr uint32_t GetNumFirstExternalTaskThread() { return 1; } + + // ------------- Start DEPRECATED Functions ------------- + // DEPRECATED: use GetIsShutdownRequested() instead of GetIsRunning() in external code + // while( GetIsRunning() ) {} can be used in tasks which loop, to check if enkiTS has been shutdown. + // If GetIsRunning() returns false should then exit. Not required for finite tasks. + ENKI_DEPRECATED inline bool GetIsRunning() const { return !GetIsShutdownRequested(); } + + // DEPRECATED - WaitforTaskSet, deprecated interface use WaitforTask. + ENKI_DEPRECATED inline void WaitforTaskSet( const ICompletable* pCompletable_ ) { WaitforTask( pCompletable_ ); } + + // DEPRECATED - GetProfilerCallbacks. Use TaskSchedulerConfig instead. + // Returns the ProfilerCallbacks structure so that it can be modified to + // set the callbacks. Should be set prior to initialization. + ENKI_DEPRECATED inline ProfilerCallbacks* GetProfilerCallbacks() { return &m_Config.profilerCallbacks; } + // ------------- End DEPRECATED Functions ------------- + + private: + friend class ICompletable; friend class ITaskSet; friend class IPinnedTask; + static void TaskingThreadFunction( const ThreadArgs& args_ ); + bool HaveTasks( uint32_t threadNum_ ); + void WaitForNewTasks( uint32_t threadNum_ ); + void WaitForTaskCompletion( const ICompletable* pCompletable_, uint32_t threadNum_ ); + void RunPinnedTasks( uint32_t threadNum_, uint32_t priority_ ); + bool TryRunTask( uint32_t threadNum_, uint32_t& hintPipeToCheck_io_ ); + bool TryRunTask( uint32_t threadNum_, uint32_t priority_, uint32_t& hintPipeToCheck_io_ ); + void StartThreads(); + void StopThreads( bool bWait_ ); + void SplitAndAddTask( uint32_t threadNum_, SubTaskSet subTask_, uint32_t rangeToSplit_ ); + void WakeThreadsForNewTasks(); + void WakeThreadsForTaskCompletion(); + bool WakeSuspendedThreadsWithPinnedTasks( uint32_t threadNum_ ); + void InitDependencies( ICompletable* pCompletable_ ); + inline bool GetIsRunningInt() const { return m_bRunning.load( std::memory_order_acquire ); } + + ENKITS_API void TaskComplete( ICompletable* pTask_, bool bWakeThreads_, uint32_t threadNum_ ); + ENKITS_API void AddTaskSetToPipeInt( ITaskSet* pTaskSet_, uint32_t threadNum_ ); + ENKITS_API void AddPinnedTaskInt( IPinnedTask* pTask_ ); + + template< typename T > T* NewArray( size_t num_, const char* file_, int line_ ); + template< typename T > void DeleteArray( T* p_, size_t num_, const char* file_, int line_ ); + template T* New( const char* file_, int line_, Args&&... args_ ); + template< typename T > void Delete( T* p_, const char* file_, int line_ ); + template< typename T > T* Alloc( const char* file_, int line_ ); + template< typename T > void Free( T* p_, const char* file_, int line_ ); + semaphoreid_t* SemaphoreNew(); + void SemaphoreDelete( semaphoreid_t* pSemaphore_ ); + + TaskPipe* m_pPipesPerThread[ TASK_PRIORITY_NUM ]; + PinnedTaskList* m_pPinnedTaskListPerThread[ TASK_PRIORITY_NUM ]; + + uint32_t m_NumThreads; + ThreadDataStore* m_pThreadDataStore; + std::thread* m_pThreads; + std::atomic m_bRunning; + std::atomic m_bShutdownRequested; + std::atomic m_bWaitforAllCalled; + std::atomic m_NumInternalTaskThreadsRunning; + std::atomic m_NumThreadsWaitingForNewTasks; + std::atomic m_NumThreadsWaitingForTaskCompletion; + uint32_t m_NumPartitions; + semaphoreid_t* m_pNewTaskSemaphore; + semaphoreid_t* m_pTaskCompleteSemaphore; + uint32_t m_NumInitialPartitions; + bool m_bHaveThreads; + TaskSchedulerConfig m_Config; + std::atomic m_NumExternalTaskThreadsRegistered; + + TaskScheduler( const TaskScheduler& nocopy_ ); + TaskScheduler& operator=( const TaskScheduler& nocopy_ ); + + protected: + void SetCustomAllocator( CustomAllocator customAllocator_ ); // for C interface + }; + + inline void ICompletable::OnDependenciesComplete( TaskScheduler* pTaskScheduler_, uint32_t threadNum_ ) + { + m_RunningCount.fetch_sub( 1, std::memory_order_acq_rel ); + pTaskScheduler_->TaskComplete( this, true, threadNum_ ); + } + + inline void ITaskSet::OnDependenciesComplete( TaskScheduler* pTaskScheduler_, uint32_t threadNum_ ) + { + pTaskScheduler_->AddTaskSetToPipeInt( this, threadNum_ ); + } + + inline void IPinnedTask::OnDependenciesComplete( TaskScheduler* pTaskScheduler_, uint32_t threadNum_ ) + { + (void)threadNum_; + pTaskScheduler_->AddPinnedTaskInt( this ); + } + + inline ICompletable::~ICompletable() + { + ENKI_ASSERT( GetIsComplete() ); // this task is still waiting to run + Dependency* pDependency = m_pDependents; + while( pDependency ) + { + Dependency* pNext = pDependency->pNext; + pDependency->pDependencyTask = NULL; + pDependency->pNext = NULL; + pDependency = pNext; + } + } + + inline void ICompletable::SetDependency( Dependency& dependency_, const ICompletable* pDependencyTask_ ) + { + ENKI_ASSERT( pDependencyTask_ != this ); + dependency_.SetDependency( pDependencyTask_, this ); + } + + template + void ICompletable::SetDependenciesArr( D& dependencyArray_ , const T(&taskArray_)[SIZE] ) { + static_assert( std::tuple_size::value >= SIZE, "Size of dependency array too small" ); + for( int i = 0; i < SIZE; ++i ) + { + dependencyArray_[i].SetDependency( &taskArray_[i], this ); + } + } + template + void ICompletable::SetDependenciesArr( D& dependencyArray_, std::initializer_list taskpList_ ) { + ENKI_ASSERT( std::tuple_size::value >= taskpList_.size() ); + int i = 0; + for( auto pTask : taskpList_ ) + { + dependencyArray_[i++].SetDependency( pTask, this ); + } + } + template + void ICompletable::SetDependenciesArr( D(&dependencyArray_)[SIZE], const T(&taskArray_)[SIZE] ) { + for( int i = 0; i < SIZE; ++i ) + { + dependencyArray_[i].SetDependency( &taskArray_[i], this ); + } + } + template + void ICompletable::SetDependenciesArr( D(&dependencyArray_)[SIZE], std::initializer_list taskpList_ ) { + ENKI_ASSERT( SIZE >= taskpList_.size() ); + int i = 0; + for( auto pTask : taskpList_ ) + { + dependencyArray_[i++].SetDependency( pTask, this ); + } + } + template + void ICompletable::SetDependenciesVec( D& dependencyVec_, const T(&taskArray_)[SIZE] ) { + dependencyVec_.resize( SIZE ); + for( int i = 0; i < SIZE; ++i ) + { + dependencyVec_[i].SetDependency( &taskArray_[i], this ); + } + } + + template + void ICompletable::SetDependenciesVec( D& dependencyVec_, std::initializer_list taskpList_ ) { + dependencyVec_.resize( taskpList_.size() ); + int i = 0; + for( auto pTask : taskpList_ ) + { + dependencyVec_[i++].SetDependency( pTask, this ); + } + } +} diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/car.cpp b/tests/cpp-tests/Source/Box2DTestBed/samples/car.cpp new file mode 100644 index 000000000000..e54679e0cf32 --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/car.cpp @@ -0,0 +1,295 @@ +// SPDX-FileCopyrightText: 2022 Erin Catto +// SPDX-License-Identifier: MIT + +#include "car.h" + +#include "box2d/box2d.h" +#include "box2d/math_functions.h" + +#include + +Car::Car() +{ + m_chassisId = {}; + m_rearWheelId = {}; + m_frontWheelId = {}; + m_rearAxleId = {}; + m_frontAxleId = {}; + m_isSpawned = false; +} + +void Car::Spawn( b2WorldId worldId, b2Vec2 position, float scale, float hertz, float dampingRatio, float torque, void* userData ) +{ + assert( m_isSpawned == false ); + + assert( B2_IS_NULL( m_chassisId ) ); + assert( B2_IS_NULL( m_frontWheelId ) ); + assert( B2_IS_NULL( m_rearWheelId ) ); + + b2Vec2 vertices[6] = { + { -1.5f, -0.5f }, { 1.5f, -0.5f }, { 1.5f, 0.0f }, { 0.0f, 0.9f }, { -1.15f, 0.9f }, { -1.5f, 0.2f }, + }; + + for ( int i = 0; i < 6; ++i ) + { + vertices[i].x *= 0.85f * scale; + vertices[i].y *= 0.85f * scale; + } + + b2Hull hull = b2ComputeHull( vertices, 6 ); + b2Polygon chassis = b2MakePolygon( &hull, 0.15f * scale ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 1.0f / scale; + shapeDef.friction = 0.2f; + + b2Circle circle = { { 0.0f, 0.0f }, 0.4f * scale }; + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = b2Add( { 0.0f, 1.0f * scale }, position ); + m_chassisId = b2CreateBody( worldId, &bodyDef ); + b2CreatePolygonShape( m_chassisId, &shapeDef, &chassis ); + + shapeDef.density = 2.0f / scale; + shapeDef.friction = 1.5f; + + bodyDef.position = b2Add( { -1.0f * scale, 0.35f * scale }, position ); + bodyDef.allowFastRotation = true; + m_rearWheelId = b2CreateBody( worldId, &bodyDef ); + b2CreateCircleShape( m_rearWheelId, &shapeDef, &circle ); + + bodyDef.position = b2Add( { 1.0f * scale, 0.4f * scale }, position ); + bodyDef.allowFastRotation = true; + m_frontWheelId = b2CreateBody( worldId, &bodyDef ); + b2CreateCircleShape( m_frontWheelId, &shapeDef, &circle ); + + b2Vec2 axis = { 0.0f, 1.0f }; + b2Vec2 pivot = b2Body_GetPosition( m_rearWheelId ); + + // float throttle = 0.0f; + // float speed = 35.0f; + // float torque = 2.5f * scale; + // float hertz = 5.0f; + // float dampingRatio = 0.7f; + + b2WheelJointDef jointDef = b2DefaultWheelJointDef(); + + jointDef.bodyIdA = m_chassisId; + jointDef.bodyIdB = m_rearWheelId; + jointDef.localAxisA = b2Body_GetLocalVector( jointDef.bodyIdA, axis ); + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); + jointDef.motorSpeed = 0.0f; + jointDef.maxMotorTorque = torque; + jointDef.enableMotor = true; + jointDef.hertz = hertz; + jointDef.dampingRatio = dampingRatio; + jointDef.lowerTranslation = -0.25f * scale; + jointDef.upperTranslation = 0.25f * scale; + jointDef.enableLimit = true; + m_rearAxleId = b2CreateWheelJoint( worldId, &jointDef ); + + pivot = b2Body_GetPosition( m_frontWheelId ); + jointDef.bodyIdA = m_chassisId; + jointDef.bodyIdB = m_frontWheelId; + jointDef.localAxisA = b2Body_GetLocalVector( jointDef.bodyIdA, axis ); + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); + jointDef.motorSpeed = 0.0f; + jointDef.maxMotorTorque = torque; + jointDef.enableMotor = true; + jointDef.hertz = hertz; + jointDef.dampingRatio = dampingRatio; + jointDef.lowerTranslation = -0.25f * scale; + jointDef.upperTranslation = 0.25f * scale; + jointDef.enableLimit = true; + m_frontAxleId = b2CreateWheelJoint( worldId, &jointDef ); +} + +void Car::Despawn() +{ + assert( m_isSpawned == true ); + + b2DestroyJoint( m_rearAxleId ); + b2DestroyJoint( m_frontAxleId ); + b2DestroyBody( m_rearWheelId ); + b2DestroyBody( m_frontWheelId ); + b2DestroyBody( m_chassisId ); + + m_isSpawned = false; +} + +void Car::SetSpeed( float speed ) +{ + b2WheelJoint_SetMotorSpeed( m_rearAxleId, speed ); + b2WheelJoint_SetMotorSpeed( m_frontAxleId, speed ); + b2Joint_WakeBodies( m_rearAxleId ); +} + +void Car::SetTorque( float torque ) +{ + b2WheelJoint_SetMaxMotorTorque( m_rearAxleId, torque ); + b2WheelJoint_SetMaxMotorTorque( m_frontAxleId, torque ); +} + +void Car::SetHertz( float hertz ) +{ + b2WheelJoint_SetSpringHertz( m_rearAxleId, hertz ); + b2WheelJoint_SetSpringHertz( m_frontAxleId, hertz ); +} + +void Car::SetDampingRadio( float dampingRatio ) +{ + b2WheelJoint_SetSpringDampingRatio( m_rearAxleId, dampingRatio ); + b2WheelJoint_SetSpringDampingRatio( m_frontAxleId, dampingRatio ); +} + +Truck::Truck() +{ + m_chassisId = {}; + m_rearWheelId = {}; + m_frontWheelId = {}; + m_rearAxleId = {}; + m_frontAxleId = {}; + m_isSpawned = false; +} + +void Truck::Spawn( b2WorldId worldId, b2Vec2 position, float scale, float hertz, float dampingRatio, float torque, float density, + void* userData ) +{ + assert( m_isSpawned == false ); + + assert( B2_IS_NULL( m_chassisId ) ); + assert( B2_IS_NULL( m_frontWheelId ) ); + assert( B2_IS_NULL( m_rearWheelId ) ); + + // b2Vec2 vertices[6] = { + // { -1.5f, -0.5f }, { 1.5f, -0.5f }, { 1.5f, 0.0f }, { 0.0f, 0.9f }, { -1.15f, 0.9f }, { -1.5f, 0.2f }, + // }; + + b2Vec2 vertices[5] = { + { -0.65f, -0.4f }, { 1.5f, -0.4f }, { 1.5f, 0.0f }, { 0.0f, 0.9f }, { -0.65f, 0.9f }, + }; + + for ( int i = 0; i < 5; ++i ) + { + vertices[i].x *= 0.85f * scale; + vertices[i].y *= 0.85f * scale; + } + + b2Hull hull = b2ComputeHull( vertices, 5 ); + b2Polygon chassis = b2MakePolygon( &hull, 0.15f * scale ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = density; + shapeDef.friction = 0.2f; + shapeDef.customColor = b2_colorHotPink; + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = b2Add( { 0.0f, 1.0f * scale }, position ); + m_chassisId = b2CreateBody( worldId, &bodyDef ); + b2CreatePolygonShape( m_chassisId, &shapeDef, &chassis ); + + b2Polygon box = b2MakeOffsetBox( 1.25f * scale, 0.1f * scale, { -2.05f * scale, -0.275f * scale }, b2Rot_identity ); + box.radius = 0.1f * scale; + b2CreatePolygonShape( m_chassisId, &shapeDef, &box ); + + box = b2MakeOffsetBox( 0.05f * scale, 0.35f * scale, { -3.25f * scale, 0.375f * scale }, b2Rot_identity ); + box.radius = 0.1f * scale; + b2CreatePolygonShape( m_chassisId, &shapeDef, &box ); + + shapeDef.density = 2.0f * density; + shapeDef.friction = 2.5f; + shapeDef.customColor = b2_colorSilver; + + b2Circle circle = { { 0.0f, 0.0f }, 0.4f * scale }; + bodyDef.position = b2Add( { -2.75f * scale, 0.3f * scale }, position ); + m_rearWheelId = b2CreateBody( worldId, &bodyDef ); + b2CreateCircleShape( m_rearWheelId, &shapeDef, &circle ); + + bodyDef.position = b2Add( { 0.8f * scale, 0.3f * scale }, position ); + m_frontWheelId = b2CreateBody( worldId, &bodyDef ); + b2CreateCircleShape( m_frontWheelId, &shapeDef, &circle ); + + b2Vec2 axis = { 0.0f, 1.0f }; + b2Vec2 pivot = b2Body_GetPosition( m_rearWheelId ); + + // float throttle = 0.0f; + // float speed = 35.0f; + // float torque = 2.5f * scale; + // float hertz = 5.0f; + // float dampingRatio = 0.7f; + + b2WheelJointDef jointDef = b2DefaultWheelJointDef(); + + jointDef.bodyIdA = m_chassisId; + jointDef.bodyIdB = m_rearWheelId; + jointDef.localAxisA = b2Body_GetLocalVector( jointDef.bodyIdA, axis ); + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); + jointDef.motorSpeed = 0.0f; + jointDef.maxMotorTorque = torque; + jointDef.enableMotor = true; + jointDef.hertz = hertz; + jointDef.dampingRatio = dampingRatio; + jointDef.lowerTranslation = -0.25f * scale; + jointDef.upperTranslation = 0.25f * scale; + jointDef.enableLimit = true; + m_rearAxleId = b2CreateWheelJoint( worldId, &jointDef ); + + pivot = b2Body_GetPosition( m_frontWheelId ); + jointDef.bodyIdA = m_chassisId; + jointDef.bodyIdB = m_frontWheelId; + jointDef.localAxisA = b2Body_GetLocalVector( jointDef.bodyIdA, axis ); + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); + jointDef.motorSpeed = 0.0f; + jointDef.maxMotorTorque = torque; + jointDef.enableMotor = true; + jointDef.hertz = hertz; + jointDef.dampingRatio = dampingRatio; + jointDef.lowerTranslation = -0.25f * scale; + jointDef.upperTranslation = 0.25f * scale; + jointDef.enableLimit = true; + m_frontAxleId = b2CreateWheelJoint( worldId, &jointDef ); +} + +void Truck::Despawn() +{ + assert( m_isSpawned == true ); + + b2DestroyJoint( m_rearAxleId ); + b2DestroyJoint( m_frontAxleId ); + b2DestroyBody( m_rearWheelId ); + b2DestroyBody( m_frontWheelId ); + b2DestroyBody( m_chassisId ); + + m_isSpawned = false; +} + +void Truck::SetSpeed( float speed ) +{ + b2WheelJoint_SetMotorSpeed( m_rearAxleId, speed ); + b2WheelJoint_SetMotorSpeed( m_frontAxleId, speed ); + b2Joint_WakeBodies( m_rearAxleId ); +} + +void Truck::SetTorque( float torque ) +{ + b2WheelJoint_SetMaxMotorTorque( m_rearAxleId, torque ); + b2WheelJoint_SetMaxMotorTorque( m_frontAxleId, torque ); +} + +void Truck::SetHertz( float hertz ) +{ + b2WheelJoint_SetSpringHertz( m_rearAxleId, hertz ); + b2WheelJoint_SetSpringHertz( m_frontAxleId, hertz ); +} + +void Truck::SetDampingRadio( float dampingRatio ) +{ + b2WheelJoint_SetSpringDampingRatio( m_rearAxleId, dampingRatio ); + b2WheelJoint_SetSpringDampingRatio( m_frontAxleId, dampingRatio ); +} diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/car.h b/tests/cpp-tests/Source/Box2DTestBed/samples/car.h new file mode 100644 index 000000000000..8013d1c051dc --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/car.h @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include "box2d/types.h" + +class Car +{ +public: + Car(); + + void Spawn( b2WorldId worldId, b2Vec2 position, float scale, float hertz, float dampingRatio, float torque, void* userData ); + void Despawn(); + + void SetSpeed( float speed ); + void SetTorque( float torque ); + void SetHertz( float hertz ); + void SetDampingRadio( float dampingRatio ); + + b2BodyId m_chassisId; + b2BodyId m_rearWheelId; + b2BodyId m_frontWheelId; + b2JointId m_rearAxleId; + b2JointId m_frontAxleId; + bool m_isSpawned; +}; + +class Truck +{ +public: + Truck(); + + void Spawn( b2WorldId worldId, b2Vec2 position, float scale, float hertz, float dampingRatio, float torque, float density, + void* userData ); + void Despawn(); + + void SetSpeed( float speed ); + void SetTorque( float torque ); + void SetHertz( float hertz ); + void SetDampingRadio( float dampingRatio ); + + b2BodyId m_chassisId; + b2BodyId m_rearWheelId; + b2BodyId m_frontWheelId; + b2JointId m_rearAxleId; + b2JointId m_frontAxleId; + bool m_isSpawned; +}; diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/data/background.fs b/tests/cpp-tests/Source/Box2DTestBed/samples/data/background.fs new file mode 100644 index 000000000000..bd0a8e4141a2 --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/data/background.fs @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: 2024 Erin Catto +// SPDX-License-Identifier: MIT + +#version 330 + +out vec4 FragColor; + +uniform float time; +uniform vec2 resolution; +uniform vec3 baseColor; + +// A simple pseudo-random function +float random(vec2 st) +{ + return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123); +} + +void main() +{ + vec2 uv = gl_FragCoord.xy / resolution.xy; + + // Create some noise + float noise = random(uv + time * 0.1); + + // Adjust these values to control the intensity and color of the grain + float grainIntensity = 0.03; + + // Mix the base color with the noise + vec3 color = baseColor + vec3(noise * grainIntensity); + + FragColor = vec4(color, 1.0); +} + diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/data/background.vs b/tests/cpp-tests/Source/Box2DTestBed/samples/data/background.vs new file mode 100644 index 000000000000..1ea9a85021e4 --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/data/background.vs @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2024 Erin Catto +// SPDX-License-Identifier: MIT +#version 330 + +layout(location = 0) in vec2 v_position; + +void main(void) +{ + gl_Position = vec4(v_position, 0.0f, 1.0f); +} diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/data/circle.fs b/tests/cpp-tests/Source/Box2DTestBed/samples/data/circle.fs new file mode 100644 index 000000000000..7586a2a8bc85 --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/data/circle.fs @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2024 Erin Catto +// SPDX-License-Identifier: MIT + +#version 330 + +in vec2 f_position; +in vec4 f_color; +in float f_thickness; + +out vec4 fragColor; + +void main() +{ + // radius in unit quad + float radius = 1.0; + + // distance to circle + vec2 w = f_position; + float dw = length(w); + float d = abs(dw - radius); + + fragColor = vec4(f_color.rgb, smoothstep(f_thickness, 0.0, d)); +} diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/data/circle.vs b/tests/cpp-tests/Source/Box2DTestBed/samples/data/circle.vs new file mode 100644 index 000000000000..df10674bce0a --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/data/circle.vs @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2024 Erin Catto +// SPDX-License-Identifier: MIT + +#version 330 + +uniform mat4 projectionMatrix; +uniform float pixelScale; + +layout(location = 0) in vec2 v_localPosition; +layout(location = 1) in vec2 v_instancePosition; +layout(location = 2) in float v_instanceRadius; +layout(location = 3) in vec4 v_instanceColor; + +out vec2 f_position; +out vec4 f_color; +out float f_thickness; + +void main() +{ + f_position = v_localPosition; + f_color = v_instanceColor; + float radius = v_instanceRadius; + + // resolution.y = pixelScale * radius + f_thickness = 3.0f / (pixelScale * radius); + + vec2 p = vec2(radius * v_localPosition.x, radius * v_localPosition.y) + v_instancePosition; + gl_Position = projectionMatrix * vec4(p, 0.0f, 1.0f); +} diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/data/droid_sans.ttf b/tests/cpp-tests/Source/Box2DTestBed/samples/data/droid_sans.ttf new file mode 100644 index 000000000000..767c63ad000e Binary files /dev/null and b/tests/cpp-tests/Source/Box2DTestBed/samples/data/droid_sans.ttf differ diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/data/solid_capsule.fs b/tests/cpp-tests/Source/Box2DTestBed/samples/data/solid_capsule.fs new file mode 100644 index 000000000000..ba5abe2b47de --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/data/solid_capsule.fs @@ -0,0 +1,61 @@ +// SPDX-FileCopyrightText: 2024 Erin Catto +// SPDX-License-Identifier: MIT + +#version 330 + +in vec2 f_position; +in vec4 f_color; +in float f_length; +in float f_thickness; + +out vec4 color; + +// Thanks to baz and kolyan3040 for help on this shader +// todo this can be optimized a bit, keeping some terms for clarity + +// https://en.wikipedia.org/wiki/Alpha_compositing +vec4 blend_colors(vec4 front,vec4 back) +{ + vec3 cSrc = front.rgb; + float alphaSrc = front.a; + vec3 cDst = back.rgb; + float alphaDst = back.a; + + vec3 cOut = cSrc * alphaSrc + cDst * alphaDst * (1.0 - alphaSrc); + float alphaOut = alphaSrc + alphaDst * (1.0 - alphaSrc); + + // remove alpha from rgb + cOut = cOut / alphaOut; + + return vec4(cOut, alphaOut); +} + +void main() +{ + // radius in unit quad + float radius = 0.5 * (2.0 - f_length); + + vec4 borderColor = f_color; + vec4 fillColor = 0.6f * borderColor; + + vec2 v1 = vec2(-0.5 * f_length, 0); + vec2 v2 = vec2(0.5 * f_length, 0); + + // distance to line segment + vec2 e = v2 - v1; + vec2 w = f_position - v1; + float we = dot(w, e); + vec2 b = w - e * clamp(we / dot(e, e), 0.0, 1.0); + float dw = length(b); + + // SDF union of capsule and line segment + float d = min(dw, abs(dw - radius)); + + // roll the fill alpha down at the border + vec4 back = vec4(fillColor.rgb, fillColor.a * smoothstep(radius + f_thickness, radius, dw)); + + // roll the border alpha down from 1 to 0 across the border thickness + vec4 front = vec4(borderColor.rgb, smoothstep(f_thickness, 0.0f, d)); + + color = blend_colors(front, back); +} diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/data/solid_capsule.vs b/tests/cpp-tests/Source/Box2DTestBed/samples/data/solid_capsule.vs new file mode 100644 index 000000000000..c592fcce30f7 --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/data/solid_capsule.vs @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: 2024 Erin Catto +// SPDX-License-Identifier: MIT + +#version 330 + +uniform mat4 projectionMatrix; +uniform float pixelScale; + +layout(location=0) in vec2 v_localPosition; +layout(location=1) in vec4 v_instanceTransform; +layout(location=2) in float v_instanceRadius; +layout(location=3) in float v_instanceLength; +layout(location=4) in vec4 v_instanceColor; + +out vec2 f_position; +out vec4 f_color; +out float f_length; +out float f_thickness; + +void main() +{ + f_position = v_localPosition; + f_color = v_instanceColor; + + float radius = v_instanceRadius; + float length = v_instanceLength; + + // scale quad large enough to hold capsule + float scale = radius + 0.5 * length; + + // quad range of [-1, 1] implies normalize radius and length + f_length = length / scale; + + // resolution.y = pixelScale * scale + f_thickness = 3.0f / (pixelScale * scale); + + float x = v_instanceTransform.x; + float y = v_instanceTransform.y; + float c = v_instanceTransform.z; + float s = v_instanceTransform.w; + vec2 p = vec2(scale * v_localPosition.x, scale * v_localPosition.y); + p = vec2((c * p.x - s * p.y) + x, (s * p.x + c * p.y) + y); + gl_Position = projectionMatrix * vec4(p, 0.0, 1.0); +} diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/data/solid_circle.fs b/tests/cpp-tests/Source/Box2DTestBed/samples/data/solid_circle.fs new file mode 100644 index 000000000000..695c884df804 --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/data/solid_circle.fs @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: 2024 Erin Catto +// SPDX-License-Identifier: MIT + +#version 330 + +in vec2 f_position; +in vec4 f_color; +in float f_thickness; + +out vec4 fragColor; + +// https://en.wikipedia.org/wiki/Alpha_compositing +vec4 blend_colors(vec4 front, vec4 back) +{ + vec3 cSrc = front.rgb; + float alphaSrc = front.a; + vec3 cDst = back.rgb; + float alphaDst = back.a; + + vec3 cOut = cSrc * alphaSrc + cDst * alphaDst * (1.0 - alphaSrc); + float alphaOut = alphaSrc + alphaDst * (1.0 - alphaSrc); + cOut = cOut / alphaOut; + + return vec4(cOut, alphaOut); +} + +void main() +{ + // radius in unit quad + float radius = 1.0; + + // distance to axis line segment + vec2 e = vec2(radius, 0); + vec2 w = f_position; + float we = dot(w, e); + vec2 b = w - e * clamp(we / dot(e, e), 0.0, 1.0); + float da = length(b); + + // distance to circle + float dw = length(w); + float dc = abs(dw - radius); + + // union of circle and axis + float d = min(da, dc); + + vec4 borderColor = f_color; + vec4 fillColor = 0.6f * borderColor; + + // roll the fill alpha down at the border + vec4 back = vec4(fillColor.rgb, fillColor.a * smoothstep(radius + f_thickness, radius, dw)); + + // roll the border alpha down from 1 to 0 across the border thickness + vec4 front = vec4(borderColor.rgb, smoothstep(f_thickness, 0.0f, d)); + + fragColor = blend_colors(front, back); +} diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/data/solid_circle.vs b/tests/cpp-tests/Source/Box2DTestBed/samples/data/solid_circle.vs new file mode 100644 index 000000000000..69d14e1cc6d5 --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/data/solid_circle.vs @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: 2024 Erin Catto +// SPDX-License-Identifier: MIT + +#version 330 + +uniform mat4 projectionMatrix; +uniform float pixelScale; + +layout(location = 0) in vec2 v_localPosition; +layout(location = 1) in vec4 v_instanceTransform; +layout(location = 2) in float v_instanceRadius; +layout(location = 3) in vec4 v_instanceColor; + +out vec2 f_position; +out vec4 f_color; +out float f_thickness; + +void main() +{ + f_position = v_localPosition; + f_color = v_instanceColor; + float radius = v_instanceRadius; + + // resolution.y = pixelScale * radius + f_thickness = 3.0f / (pixelScale * radius); + + float x = v_instanceTransform.x; + float y = v_instanceTransform.y; + float c = v_instanceTransform.z; + float s = v_instanceTransform.w; + vec2 p = vec2(radius * v_localPosition.x, radius * v_localPosition.y); + p = vec2((c * p.x - s * p.y) + x, (s * p.x + c * p.y) + y); + gl_Position = projectionMatrix * vec4(p, 0.0f, 1.0f); +} diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/data/solid_polygon.fs b/tests/cpp-tests/Source/Box2DTestBed/samples/data/solid_polygon.fs new file mode 100644 index 000000000000..021c9f3185c5 --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/data/solid_polygon.fs @@ -0,0 +1,106 @@ +// SPDX-FileCopyrightText: 2024 Erin Catto +// SPDX-License-Identifier: MIT + +#version 330 + +in vec2 f_position; +in vec2 f_points[8]; +flat in int f_count; +in float f_radius; +in vec4 f_color; +in float f_thickness; + +out vec4 fragColor; + +// https://en.wikipedia.org/wiki/Alpha_compositing +vec4 blend_colors(vec4 front, vec4 back) +{ + vec3 cSrc = front.rgb; + float alphaSrc = front.a; + vec3 cDst = back.rgb; + float alphaDst = back.a; + + vec3 cOut = cSrc * alphaSrc + cDst * alphaDst * (1.0 - alphaSrc); + float alphaOut = alphaSrc + alphaDst * (1.0 - alphaSrc); + + // remove alpha from rgb + cOut = cOut / alphaOut; + + return vec4(cOut, alphaOut); +} + +float cross2d(in vec2 v1, in vec2 v2) +{ + return v1.x * v2.y - v1.y * v2.x; +} + +// Signed distance function for convex polygon +float sdConvexPolygon(in vec2 p, in vec2[8] v, in int count) +{ + // Initial squared distance + float d = dot(p - v[0], p - v[0]); + + // Consider query point inside to start + float side = -1.0; + int j = count - 1; + for (int i = 0; i < count; ++i) + { + // Distance to a polygon edge + vec2 e = v[i] - v[j]; + vec2 w = p - v[j]; + float we = dot(w, e); + vec2 b = w - e * clamp(we / dot(e, e), 0.0, 1.0); + float bb = dot(b, b); + + // Track smallest distance + if (bb < d) + { + d = bb; + } + + // If the query point is outside any edge then it is outside the entire polygon. + // This depends on the CCW winding order of points. + float s = cross2d(w, e); + if (s >= 0.0) + { + side = 1.0; + } + + j = i; + } + + return side * sqrt(d); +} + +void main() +{ + vec4 borderColor = f_color; + vec4 fillColor = 0.6f * borderColor; + + float dw = sdConvexPolygon(f_position, f_points, f_count); + float d = abs(dw - f_radius); + + // roll the fill alpha down at the border + vec4 back = vec4(fillColor.rgb, fillColor.a * smoothstep(f_radius + f_thickness, f_radius, dw)); + + // roll the border alpha down from 1 to 0 across the border thickness + vec4 front = vec4(borderColor.rgb, smoothstep(f_thickness, 0.0f, d)); + + fragColor = blend_colors(front, back); + + // todo debugging + // float resy = 3.0f / f_thickness; + + // if (resy < 539.9f) + // { + // fragColor = vec4(1, 0, 0, 1); + // } + // else if (resy > 540.1f) + // { + // fragColor = vec4(0, 1, 0, 1); + // } + // else + // { + // fragColor = vec4(0, 0, 1, 1); + // } +} diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/data/solid_polygon.vs b/tests/cpp-tests/Source/Box2DTestBed/samples/data/solid_polygon.vs new file mode 100644 index 000000000000..62dce4431361 --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/data/solid_polygon.vs @@ -0,0 +1,79 @@ +// SPDX-FileCopyrightText: 2024 Erin Catto +// SPDX-License-Identifier: MIT + +#version 330 + +uniform mat4 projectionMatrix; +uniform float pixelScale; + +layout(location = 0) in vec2 v_localPosition; +layout(location = 1) in vec4 v_instanceTransform; +layout(location = 2) in vec4 v_instancePoints12; +layout(location = 3) in vec4 v_instancePoints34; +layout(location = 4) in vec4 v_instancePoints56; +layout(location = 5) in vec4 v_instancePoints78; +layout(location = 6) in int v_instanceCount; +layout(location = 7) in float v_instanceRadius; +layout(location = 8) in vec4 v_instanceColor; + +out vec2 f_position; +out vec4 f_color; +out vec2 f_points[8]; +flat out int f_count; +out float f_radius; +out float f_thickness; + +void main() +{ + f_position = v_localPosition; + f_color = v_instanceColor; + + f_radius = v_instanceRadius; + f_count = v_instanceCount; + + f_points[0] = v_instancePoints12.xy; + f_points[1] = v_instancePoints12.zw; + f_points[2] = v_instancePoints34.xy; + f_points[3] = v_instancePoints34.zw; + f_points[4] = v_instancePoints56.xy; + f_points[5] = v_instancePoints56.zw; + f_points[6] = v_instancePoints78.xy; + f_points[7] = v_instancePoints78.zw; + + // Compute polygon AABB + vec2 lower = f_points[0]; + vec2 upper = f_points[0]; + for (int i = 1; i < v_instanceCount; ++i) + { + lower = min(lower, f_points[i]); + upper = max(upper, f_points[i]); + } + + vec2 center = 0.5 * (lower + upper); + vec2 width = upper - lower; + float maxWidth = max(width.x, width.y); + + float scale = f_radius + 0.5 * maxWidth; + float invScale = 1.0 / scale; + + // Shift and scale polygon points so they fit in 2x2 quad + for (int i = 0; i < f_count; ++i) + { + f_points[i] = invScale * (f_points[i] - center); + } + + // Scale radius as well + f_radius = invScale * f_radius; + + // resolution.y = pixelScale * scale + f_thickness = 3.0f / (pixelScale * scale); + + // scale up and transform quad to fit polygon + float x = v_instanceTransform.x; + float y = v_instanceTransform.y; + float c = v_instanceTransform.z; + float s = v_instanceTransform.w; + vec2 p = vec2(scale * v_localPosition.x, scale * v_localPosition.y) + center; + p = vec2((c * p.x - s * p.y) + x, (s * p.x + c * p.y) + y); + gl_Position = projectionMatrix * vec4(p, 0.0f, 1.0f); +} diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/donut.cpp b/tests/cpp-tests/Source/Box2DTestBed/samples/donut.cpp new file mode 100644 index 000000000000..48c3f65af86b --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/donut.cpp @@ -0,0 +1,96 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#include "donut.h" + +#include "box2d/box2d.h" +#include "box2d/math_functions.h" + +#include + +Donut::Donut() +{ + for ( int i = 0; i < e_sides; ++i ) + { + m_bodyIds[i] = b2_nullBodyId; + m_jointIds[i] = b2_nullJointId; + } + + m_isSpawned = false; +} + +void Donut::Spawn( b2WorldId worldId, b2Vec2 position, float scale, int groupIndex, void* userData ) +{ + assert( m_isSpawned == false ); + + for ( int i = 0; i < e_sides; ++i ) + { + assert( B2_IS_NULL( m_bodyIds[i] ) ); + assert( B2_IS_NULL( m_jointIds[i] ) ); + } + + float radius = 1.0f * scale; + float deltaAngle = 2.0f * b2_pi / e_sides; + float length = 2.0f * b2_pi * radius / e_sides; + + b2Capsule capsule = { { 0.0f, -0.5f * length }, { 0.0f, 0.5f * length }, 0.25f * scale }; + + b2Vec2 center = position; + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.userData = userData; + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 1.0f; + shapeDef.filter.groupIndex = -groupIndex; + shapeDef.friction = 0.3f; + + // Create bodies + float angle = 0.0f; + for ( int i = 0; i < e_sides; ++i ) + { + bodyDef.position = { radius * cosf( angle ) + center.x, radius * sinf( angle ) + center.y }; + bodyDef.rotation = b2MakeRot( angle ); + + m_bodyIds[i] = b2CreateBody( worldId, &bodyDef ); + b2CreateCapsuleShape( m_bodyIds[i], &shapeDef, &capsule ); + + angle += deltaAngle; + } + + // Create joints + b2WeldJointDef weldDef = b2DefaultWeldJointDef(); + weldDef.angularHertz = 5.0f; + weldDef.angularDampingRatio = 0.0f; + weldDef.localAnchorA = { 0.0f, 0.5f * length }; + weldDef.localAnchorB = { 0.0f, -0.5f * length }; + + b2BodyId prevBodyId = m_bodyIds[e_sides - 1]; + for ( int i = 0; i < e_sides; ++i ) + { + weldDef.bodyIdA = prevBodyId; + weldDef.bodyIdB = m_bodyIds[i]; + b2Rot rotA = b2Body_GetRotation( prevBodyId ); + b2Rot rotB = b2Body_GetRotation( m_bodyIds[i] ); + weldDef.referenceAngle = b2RelativeAngle( rotB, rotA ); + m_jointIds[i] = b2CreateWeldJoint( worldId, &weldDef ); + prevBodyId = weldDef.bodyIdB; + } + + m_isSpawned = true; +} + +void Donut::Despawn() +{ + assert( m_isSpawned == true ); + + for ( int i = 0; i < e_sides; ++i ) + { + b2DestroyBody( m_bodyIds[i] ); + m_bodyIds[i] = b2_nullBodyId; + m_jointIds[i] = b2_nullJointId; + } + + m_isSpawned = false; +} diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/donut.h b/tests/cpp-tests/Source/Box2DTestBed/samples/donut.h new file mode 100644 index 000000000000..aecc016c5143 --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/donut.h @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include "box2d/types.h" + +class Donut +{ + enum + { + e_sides = 7 + }; + +public: + Donut(); + + void Spawn( b2WorldId worldId, b2Vec2 position, float scale, int groupIndex, void* userData ); + void Despawn(); + + b2BodyId m_bodyIds[e_sides]; + b2JointId m_jointIds[e_sides]; + bool m_isSpawned; +}; diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/doohickey.cpp b/tests/cpp-tests/Source/Box2DTestBed/samples/doohickey.cpp new file mode 100644 index 000000000000..89ac2dafd2a1 --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/doohickey.cpp @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: 2022 Erin Catto +// SPDX-License-Identifier: MIT + +#include "doohickey.h" + +#include "box2d/box2d.h" +#include "box2d/math_functions.h" + +#include + +Doohickey::Doohickey() +{ + m_wheelId1 = {}; + m_wheelId2 = {}; + m_barId1 = {}; + m_barId2 = {}; + + m_axleId1 = {}; + m_axleId2 = {}; + m_sliderId = {}; + + m_isSpawned = false; +} + +void Doohickey::Spawn( b2WorldId worldId, b2Vec2 position, float scale ) +{ + assert( m_isSpawned == false ); + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Circle circle = { { 0.0f, 0.0f }, 1.0f * scale }; + b2Capsule capsule = { { -3.5f * scale, 0.0f }, { 3.5f * scale, 0.0f }, 0.15f * scale }; + + bodyDef.position = b2MulAdd( position, scale, { -5.0f, 3.0f } ); + m_wheelId1 = b2CreateBody( worldId, &bodyDef ); + b2CreateCircleShape( m_wheelId1, &shapeDef, &circle ); + + bodyDef.position = b2MulAdd( position, scale, { 5.0f, 3.0f } ); + m_wheelId2 = b2CreateBody( worldId, &bodyDef ); + b2CreateCircleShape( m_wheelId2, &shapeDef, &circle ); + + bodyDef.position = b2MulAdd( position, scale, { -1.5f, 3.0f } ); + m_barId1 = b2CreateBody( worldId, &bodyDef ); + b2CreateCapsuleShape( m_barId1, &shapeDef, &capsule ); + + bodyDef.position = b2MulAdd( position, scale, { 1.5f, 3.0f } ); + m_barId2 = b2CreateBody( worldId, &bodyDef ); + b2CreateCapsuleShape( m_barId2, &shapeDef, &capsule ); + + b2RevoluteJointDef revoluteDef = b2DefaultRevoluteJointDef(); + + revoluteDef.bodyIdA = m_wheelId1; + revoluteDef.bodyIdB = m_barId1; + revoluteDef.localAnchorA = { 0.0f, 0.0f }; + revoluteDef.localAnchorB = { -3.5f * scale, 0.0f }; + revoluteDef.enableMotor = true; + revoluteDef.maxMotorTorque = 2.0f * scale; + b2CreateRevoluteJoint( worldId, &revoluteDef ); + + revoluteDef.bodyIdA = m_wheelId2; + revoluteDef.bodyIdB = m_barId2; + revoluteDef.localAnchorA = { 0.0f, 0.0f }; + revoluteDef.localAnchorB = { 3.5f * scale, 0.0f }; + revoluteDef.enableMotor = true; + revoluteDef.maxMotorTorque = 2.0f * scale; + b2CreateRevoluteJoint( worldId, &revoluteDef ); + + b2PrismaticJointDef prismaticDef = b2DefaultPrismaticJointDef(); + prismaticDef.bodyIdA = m_barId1; + prismaticDef.bodyIdB = m_barId2; + prismaticDef.localAxisA = { 1.0f, 0.0f }; + prismaticDef.localAnchorA = { 2.0f * scale, 0.0f }; + prismaticDef.localAnchorB = { -2.0f * scale, 0.0f }; + prismaticDef.lowerTranslation = -2.0f * scale; + prismaticDef.upperTranslation = 2.0f * scale; + prismaticDef.enableLimit = true; + prismaticDef.enableMotor = true; + prismaticDef.maxMotorForce = 2.0f * scale; + prismaticDef.enableSpring = true; + prismaticDef.hertz = 1.0f; + prismaticDef.dampingRatio = 0.5; + b2CreatePrismaticJoint( worldId, &prismaticDef ); +} + +void Doohickey::Despawn() +{ + assert( m_isSpawned == true ); + + b2DestroyJoint( m_axleId1 ); + b2DestroyJoint( m_axleId2 ); + b2DestroyJoint( m_sliderId ); + + b2DestroyBody( m_wheelId1 ); + b2DestroyBody( m_wheelId2 ); + b2DestroyBody( m_barId1 ); + b2DestroyBody( m_barId2 ); + + m_isSpawned = false; +} diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/doohickey.h b/tests/cpp-tests/Source/Box2DTestBed/samples/doohickey.h new file mode 100644 index 000000000000..0db7345ffd7d --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/doohickey.h @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include "box2d/types.h" + +class Doohickey +{ +public: + Doohickey(); + + void Spawn( b2WorldId worldId, b2Vec2 position, float scale ); + void Despawn(); + + b2BodyId m_wheelId1; + b2BodyId m_wheelId2; + b2BodyId m_barId1; + b2BodyId m_barId2; + + b2JointId m_axleId1; + b2JointId m_axleId2; + b2JointId m_sliderId; + + bool m_isSpawned; +}; diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/draw.h b/tests/cpp-tests/Source/Box2DTestBed/samples/draw.h new file mode 100644 index 000000000000..1d73d6a5684e --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/draw.h @@ -0,0 +1,3 @@ +#pragma once + +#include "../Box2DTestDebugDrawNode.h" diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/human.cpp b/tests/cpp-tests/Source/Box2DTestBed/samples/human.cpp new file mode 100644 index 000000000000..70912a929436 --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/human.cpp @@ -0,0 +1,573 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#include "human.h" + +#include "sample.h" + +#include "box2d/box2d.h" +#include "box2d/math_functions.h" + +#include + +Human::Human() +{ + for ( int i = 0; i < Bone::e_count; ++i ) + { + m_bones[i].bodyId = b2_nullBodyId; + m_bones[i].jointId = b2_nullJointId; + m_bones[i].frictionScale = 1.0f; + m_bones[i].parentIndex = -1; + } + + m_scale = 1.0f; + m_isSpawned = false; +} + +void Human::Spawn( b2WorldId worldId, b2Vec2 position, float scale, float frictionTorque, float hertz, float dampingRatio, + int groupIndex, void* userData, bool colorize ) +{ + assert( m_isSpawned == false ); + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.sleepThreshold = 0.1f; + bodyDef.userData = userData; + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.friction = 0.2f; + shapeDef.filter.groupIndex = -groupIndex; + shapeDef.filter.maskBits = 1; + + b2ShapeDef footShapeDef = shapeDef; + footShapeDef.friction = 0.05f; + + if ( colorize ) + { + footShapeDef.customColor = b2_colorSaddleBrown; + } + + m_scale = scale; + float s = scale; + float maxTorque = frictionTorque * s; + bool enableMotor = true; + bool enableLimit = true; + float drawSize = 0.05f; + + b2HexColor shirtColor = b2_colorMediumTurquoise; + b2HexColor pantColor = b2_colorDodgerBlue; + + b2HexColor skinColors[4] = { b2_colorNavajoWhite, b2_colorLightYellow, b2_colorPeru, b2_colorTan }; + b2HexColor skinColor = skinColors[groupIndex % 4]; + + // hip + { + Bone* bone = m_bones + Bone::e_hip; + bone->parentIndex = -1; + + bodyDef.position = b2Add( { 0.0f, 0.95f * s }, position ); + bodyDef.linearDamping = 0.0f; + bone->bodyId = b2CreateBody( worldId, &bodyDef ); + + if ( colorize ) + { + shapeDef.customColor = pantColor; + } + + b2Capsule capsule = { { 0.0f, -0.02f * s }, { 0.0f, 0.02f * s }, 0.095f * s }; + b2CreateCapsuleShape( bone->bodyId, &shapeDef, &capsule ); + } + + // torso + { + Bone* bone = m_bones + Bone::e_torso; + bone->parentIndex = Bone::e_hip; + + bodyDef.position = b2Add( { 0.0f, 1.2f * s }, position ); + bodyDef.linearDamping = 0.0f; + // bodyDef.type = b2_staticBody; + bone->bodyId = b2CreateBody( worldId, &bodyDef ); + bone->frictionScale = 0.5f; + bodyDef.type = b2_dynamicBody; + + if ( colorize ) + { + shapeDef.customColor = shirtColor; + } + + b2Capsule capsule = { { 0.0f, -0.135f * s }, { 0.0f, 0.135f * s }, 0.09f * s }; + b2CreateCapsuleShape( bone->bodyId, &shapeDef, &capsule ); + + b2Vec2 pivot = b2Add( { 0.0f, 1.0f * s }, position ); + b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef(); + jointDef.bodyIdA = m_bones[bone->parentIndex].bodyId; + jointDef.bodyIdB = bone->bodyId; + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); + jointDef.enableLimit = enableLimit; + jointDef.lowerAngle = -0.25f * b2_pi; + jointDef.upperAngle = 0.0f; + jointDef.enableMotor = enableMotor; + jointDef.maxMotorTorque = bone->frictionScale * maxTorque; + jointDef.enableSpring = hertz > 0.0f; + jointDef.hertz = hertz; + jointDef.dampingRatio = dampingRatio; + jointDef.drawSize = drawSize; + + bone->jointId = b2CreateRevoluteJoint( worldId, &jointDef ); + } + + // head + { + Bone* bone = m_bones + Bone::e_head; + bone->parentIndex = Bone::e_torso; + + bodyDef.position = b2Add( { 0.0f * s, 1.5f * s }, position ); + bodyDef.linearDamping = 0.1f; + + bone->bodyId = b2CreateBody( worldId, &bodyDef ); + bone->frictionScale = 0.25f; + + if ( colorize ) + { + shapeDef.customColor = skinColor; + } + + b2Capsule capsule = { { 0.0f, -0.0325f * s }, { 0.0f, 0.0325f * s }, 0.08f * s }; + b2CreateCapsuleShape( bone->bodyId, &shapeDef, &capsule ); + + // neck + capsule = { { 0.0f, -0.12f * s }, { 0.0f, -0.08f * s }, 0.05f * s }; + b2CreateCapsuleShape( bone->bodyId, &shapeDef, &capsule ); + + b2Vec2 pivot = b2Add( { 0.0f, 1.4f * s }, position ); + b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef(); + jointDef.bodyIdA = m_bones[bone->parentIndex].bodyId; + jointDef.bodyIdB = bone->bodyId; + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); + jointDef.enableLimit = enableLimit; + jointDef.lowerAngle = -0.3f * b2_pi; + jointDef.upperAngle = 0.1f * b2_pi; + jointDef.enableMotor = enableMotor; + jointDef.maxMotorTorque = bone->frictionScale * maxTorque; + jointDef.enableSpring = hertz > 0.0f; + jointDef.hertz = hertz; + jointDef.dampingRatio = dampingRatio; + jointDef.drawSize = drawSize; + + bone->jointId = b2CreateRevoluteJoint( worldId, &jointDef ); + } + + // upper left leg + { + Bone* bone = m_bones + Bone::e_upperLeftLeg; + bone->parentIndex = Bone::e_hip; + + bodyDef.position = b2Add( { 0.0f, 0.775f * s }, position ); + bodyDef.linearDamping = 0.0f; + bone->bodyId = b2CreateBody( worldId, &bodyDef ); + bone->frictionScale = 1.0f; + + if ( colorize ) + { + shapeDef.customColor = pantColor; + } + + b2Capsule capsule = { { 0.0f, -0.125f * s }, { 0.0f, 0.125f * s }, 0.06f * s }; + b2CreateCapsuleShape( bone->bodyId, &shapeDef, &capsule ); + + b2Vec2 pivot = b2Add( { 0.0f, 0.9f * s }, position ); + b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef(); + jointDef.bodyIdA = m_bones[bone->parentIndex].bodyId; + jointDef.bodyIdB = bone->bodyId; + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); + jointDef.enableLimit = enableLimit; + jointDef.lowerAngle = -0.05f * b2_pi; + jointDef.upperAngle = 0.4f * b2_pi; + jointDef.enableMotor = enableMotor; + jointDef.maxMotorTorque = bone->frictionScale * maxTorque; + jointDef.enableSpring = hertz > 0.0f; + jointDef.hertz = hertz; + jointDef.dampingRatio = dampingRatio; + jointDef.drawSize = drawSize; + + bone->jointId = b2CreateRevoluteJoint( worldId, &jointDef ); + } + + // lower left leg + { + Bone* bone = m_bones + Bone::e_lowerLeftLeg; + bone->parentIndex = Bone::e_upperLeftLeg; + + bodyDef.position = b2Add( { 0.0f, 0.475f * s }, position ); + bodyDef.linearDamping = 0.0f; + bone->bodyId = b2CreateBody( worldId, &bodyDef ); + bone->frictionScale = 0.5f; + + if ( colorize ) + { + shapeDef.customColor = pantColor; + } + + b2Capsule capsule = { { 0.0f, -0.14f * s }, { 0.0f, 0.125f * s }, 0.05f * s }; + b2CreateCapsuleShape( bone->bodyId, &shapeDef, &capsule ); + + // b2Polygon box = b2MakeOffsetBox(0.1f * s, 0.03f * s, {0.05f * s, -0.175f * s}, 0.0f); + // b2CreatePolygonShape(bone->bodyId, &shapeDef, &box); + + capsule = { { -0.02f * s, -0.175f * s }, { 0.13f * s, -0.175f * s }, 0.03f * s }; + b2CreateCapsuleShape( bone->bodyId, &footShapeDef, &capsule ); + + b2Vec2 pivot = b2Add( { 0.0f, 0.625f * s }, position ); + b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef(); + jointDef.bodyIdA = m_bones[bone->parentIndex].bodyId; + jointDef.bodyIdB = bone->bodyId; + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); + jointDef.enableLimit = enableLimit; + jointDef.lowerAngle = -0.5f * b2_pi; + jointDef.upperAngle = -0.02f * b2_pi; + jointDef.enableMotor = enableMotor; + jointDef.maxMotorTorque = bone->frictionScale * maxTorque; + jointDef.enableSpring = hertz > 0.0f; + jointDef.hertz = hertz; + jointDef.dampingRatio = dampingRatio; + jointDef.drawSize = drawSize; + + bone->jointId = b2CreateRevoluteJoint( worldId, &jointDef ); + } + + // upper right leg + { + Bone* bone = m_bones + Bone::e_upperRightLeg; + bone->parentIndex = Bone::e_hip; + + bodyDef.position = b2Add( { 0.0f, 0.775f * s }, position ); + bodyDef.linearDamping = 0.0f; + bone->bodyId = b2CreateBody( worldId, &bodyDef ); + bone->frictionScale = 1.0f; + + if ( colorize ) + { + shapeDef.customColor = pantColor; + } + + b2Capsule capsule = { { 0.0f, -0.125f * s }, { 0.0f, 0.125f * s }, 0.06f * s }; + b2CreateCapsuleShape( bone->bodyId, &shapeDef, &capsule ); + + b2Vec2 pivot = b2Add( { 0.0f, 0.9f * s }, position ); + b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef(); + jointDef.bodyIdA = m_bones[bone->parentIndex].bodyId; + jointDef.bodyIdB = bone->bodyId; + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); + jointDef.enableLimit = enableLimit; + jointDef.lowerAngle = -0.05f * b2_pi; + jointDef.upperAngle = 0.4f * b2_pi; + jointDef.enableMotor = enableMotor; + jointDef.maxMotorTorque = bone->frictionScale * maxTorque; + jointDef.enableSpring = hertz > 0.0f; + jointDef.hertz = hertz; + jointDef.dampingRatio = dampingRatio; + jointDef.drawSize = drawSize; + + bone->jointId = b2CreateRevoluteJoint( worldId, &jointDef ); + } + + // lower right leg + { + Bone* bone = m_bones + Bone::e_lowerRightLeg; + bone->parentIndex = Bone::e_upperRightLeg; + + bodyDef.position = b2Add( { 0.0f, 0.475f * s }, position ); + bodyDef.linearDamping = 0.0f; + bone->bodyId = b2CreateBody( worldId, &bodyDef ); + bone->frictionScale = 0.5f; + + if ( colorize ) + { + shapeDef.customColor = pantColor; + } + + b2Capsule capsule = { { 0.0f, -0.14f * s }, { 0.0f, 0.125f * s }, 0.05f * s }; + b2CreateCapsuleShape( bone->bodyId, &shapeDef, &capsule ); + + // b2Polygon box = b2MakeOffsetBox(0.1f * s, 0.03f * s, {0.05f * s, -0.175f * s}, 0.0f); + // b2CreatePolygonShape(bone->bodyId, &shapeDef, &box); + + capsule = { { -0.02f * s, -0.175f * s }, { 0.13f * s, -0.175f * s }, 0.03f * s }; + b2CreateCapsuleShape( bone->bodyId, &footShapeDef, &capsule ); + + b2Vec2 pivot = b2Add( { 0.0f, 0.625f * s }, position ); + b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef(); + jointDef.bodyIdA = m_bones[bone->parentIndex].bodyId; + jointDef.bodyIdB = bone->bodyId; + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); + jointDef.enableLimit = enableLimit; + jointDef.lowerAngle = -0.5f * b2_pi; + jointDef.upperAngle = -0.02f * b2_pi; + jointDef.enableMotor = enableMotor; + jointDef.maxMotorTorque = bone->frictionScale * maxTorque; + jointDef.enableSpring = hertz > 0.0f; + jointDef.hertz = hertz; + jointDef.dampingRatio = dampingRatio; + jointDef.drawSize = drawSize; + + bone->jointId = b2CreateRevoluteJoint( worldId, &jointDef ); + } + + // upper left arm + { + Bone* bone = m_bones + Bone::e_upperLeftArm; + bone->parentIndex = Bone::e_torso; + bone->frictionScale = 0.5f; + + bodyDef.position = b2Add( { 0.0f, 1.225f * s }, position ); + bodyDef.linearDamping = 0.0f; + bone->bodyId = b2CreateBody( worldId, &bodyDef ); + + if ( colorize ) + { + shapeDef.customColor = shirtColor; + } + + b2Capsule capsule = { { 0.0f, -0.125f * s }, { 0.0f, 0.125f * s }, 0.035f * s }; + b2CreateCapsuleShape( bone->bodyId, &shapeDef, &capsule ); + + b2Vec2 pivot = b2Add( { 0.0f, 1.35f * s }, position ); + b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef(); + jointDef.bodyIdA = m_bones[bone->parentIndex].bodyId; + jointDef.bodyIdB = bone->bodyId; + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); + jointDef.enableLimit = enableLimit; + jointDef.lowerAngle = -0.1f * b2_pi; + jointDef.upperAngle = 0.8f * b2_pi; + jointDef.enableMotor = enableMotor; + jointDef.maxMotorTorque = bone->frictionScale * maxTorque; + jointDef.enableSpring = hertz > 0.0f; + jointDef.hertz = hertz; + jointDef.dampingRatio = dampingRatio; + jointDef.drawSize = drawSize; + + bone->jointId = b2CreateRevoluteJoint( worldId, &jointDef ); + } + + // lower left arm + { + Bone* bone = m_bones + Bone::e_lowerLeftArm; + bone->parentIndex = Bone::e_upperLeftArm; + + bodyDef.position = b2Add( { 0.0f, 0.975f * s }, position ); + bodyDef.linearDamping = 0.1f; + bone->bodyId = b2CreateBody( worldId, &bodyDef ); + bone->frictionScale = 0.1f; + + if ( colorize ) + { + shapeDef.customColor = skinColor; + } + + b2Capsule capsule = { { 0.0f, -0.125f * s }, { 0.0f, 0.125f * s }, 0.03f * s }; + b2CreateCapsuleShape( bone->bodyId, &shapeDef, &capsule ); + + b2Vec2 pivot = b2Add( { 0.0f, 1.1f * s }, position ); + b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef(); + jointDef.bodyIdA = m_bones[bone->parentIndex].bodyId; + jointDef.bodyIdB = bone->bodyId; + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); + jointDef.enableLimit = enableLimit; + jointDef.lowerAngle = 0.01f * b2_pi; + jointDef.upperAngle = 0.5f * b2_pi; + jointDef.enableMotor = enableMotor; + jointDef.maxMotorTorque = bone->frictionScale * maxTorque; + jointDef.enableSpring = hertz > 0.0f; + jointDef.hertz = hertz; + jointDef.dampingRatio = dampingRatio; + jointDef.drawSize = drawSize; + + bone->jointId = b2CreateRevoluteJoint( worldId, &jointDef ); + } + + // upper right arm + { + Bone* bone = m_bones + Bone::e_upperRightArm; + bone->parentIndex = Bone::e_torso; + + bodyDef.position = b2Add( { 0.0f, 1.225f * s }, position ); + bodyDef.linearDamping = 0.0f; + bone->bodyId = b2CreateBody( worldId, &bodyDef ); + bone->frictionScale = 0.5f; + + if ( colorize ) + { + shapeDef.customColor = shirtColor; + } + + b2Capsule capsule = { { 0.0f, -0.125f * s }, { 0.0f, 0.125f * s }, 0.035f * s }; + b2CreateCapsuleShape( bone->bodyId, &shapeDef, &capsule ); + + b2Vec2 pivot = b2Add( { 0.0f, 1.35f * s }, position ); + b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef(); + jointDef.bodyIdA = m_bones[bone->parentIndex].bodyId; + jointDef.bodyIdB = bone->bodyId; + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); + jointDef.enableLimit = enableLimit; + jointDef.lowerAngle = -0.1f * b2_pi; + jointDef.upperAngle = 0.8f * b2_pi; + jointDef.enableMotor = enableMotor; + jointDef.maxMotorTorque = bone->frictionScale * maxTorque; + jointDef.enableSpring = hertz > 0.0f; + jointDef.hertz = hertz; + jointDef.dampingRatio = dampingRatio; + jointDef.drawSize = drawSize; + + bone->jointId = b2CreateRevoluteJoint( worldId, &jointDef ); + } + + // lower right arm + { + Bone* bone = m_bones + Bone::e_lowerRightArm; + bone->parentIndex = Bone::e_upperRightArm; + + bodyDef.position = b2Add( { 0.0f, 0.975f * s }, position ); + bodyDef.linearDamping = 0.1f; + bone->bodyId = b2CreateBody( worldId, &bodyDef ); + bone->frictionScale = 0.1f; + + if ( colorize ) + { + shapeDef.customColor = skinColor; + } + + b2Capsule capsule = { { 0.0f, -0.125f * s }, { 0.0f, 0.125f * s }, 0.03f * s }; + b2CreateCapsuleShape( bone->bodyId, &shapeDef, &capsule ); + + b2Vec2 pivot = b2Add( { 0.0f, 1.1f * s }, position ); + b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef(); + jointDef.bodyIdA = m_bones[bone->parentIndex].bodyId; + jointDef.bodyIdB = bone->bodyId; + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); + jointDef.enableLimit = enableLimit; + jointDef.lowerAngle = 0.01f * b2_pi; + jointDef.upperAngle = 0.5f * b2_pi; + jointDef.enableMotor = enableMotor; + jointDef.maxMotorTorque = bone->frictionScale * maxTorque; + jointDef.enableSpring = hertz > 0.0f; + jointDef.hertz = hertz; + jointDef.dampingRatio = dampingRatio; + jointDef.drawSize = drawSize; + + bone->jointId = b2CreateRevoluteJoint( worldId, &jointDef ); + } + + m_isSpawned = true; +} + +void Human::Despawn() +{ + assert( m_isSpawned == true ); + + for ( int i = 0; i < Bone::e_count; ++i ) + { + if ( B2_IS_NULL( m_bones[i].jointId ) ) + { + continue; + } + + b2DestroyJoint( m_bones[i].jointId ); + m_bones[i].jointId = b2_nullJointId; + } + + for ( int i = 0; i < Bone::e_count; ++i ) + { + if ( B2_IS_NULL( m_bones[i].bodyId ) ) + { + continue; + } + + b2DestroyBody( m_bones[i].bodyId ); + m_bones[i].bodyId = b2_nullBodyId; + } + + m_isSpawned = false; +} + +void Human::ApplyRandomAngularImpulse( float magnitude ) +{ + if ( m_isSpawned == false ) + { + return; + } + + float impulse = RandomFloat( -magnitude, magnitude ); + b2Body_ApplyAngularImpulse( m_bones[Bone::e_torso].bodyId, impulse, true ); +} + +void Human::SetJointFrictionTorque( float torque ) +{ + if ( m_isSpawned == false ) + { + return; + } + + if ( torque == 0.0f ) + { + for ( int i = 1; i < Bone::e_count; ++i ) + { + b2RevoluteJoint_EnableMotor( m_bones[i].jointId, false ); + } + } + else + { + for ( int i = 1; i < Bone::e_count; ++i ) + { + b2RevoluteJoint_EnableMotor( m_bones[i].jointId, true ); + float scale = m_scale * m_bones[i].frictionScale; + b2RevoluteJoint_SetMaxMotorTorque( m_bones[i].jointId, scale * torque ); + } + } +} + +void Human::SetJointSpringHertz( float hertz ) +{ + if ( m_isSpawned == false ) + { + return; + } + + if ( hertz == 0.0f ) + { + for ( int i = 1; i < Bone::e_count; ++i ) + { + b2RevoluteJoint_EnableSpring( m_bones[i].jointId, false ); + } + } + else + { + for ( int i = 1; i < Bone::e_count; ++i ) + { + b2RevoluteJoint_EnableSpring( m_bones[i].jointId, true ); + b2RevoluteJoint_SetSpringHertz( m_bones[i].jointId, hertz ); + } + } +} + +void Human::SetJointDampingRatio( float dampingRatio ) +{ + if ( m_isSpawned == false ) + { + return; + } + + for ( int i = 1; i < Bone::e_count; ++i ) + { + b2RevoluteJoint_SetSpringDampingRatio( m_bones[i].jointId, dampingRatio ); + } +} diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/human.h b/tests/cpp-tests/Source/Box2DTestBed/samples/human.h new file mode 100644 index 000000000000..306a867da7b2 --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/human.h @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include "box2d/types.h" + +struct Bone +{ + enum + { + e_hip = 0, + e_torso = 1, + e_head = 2, + e_upperLeftLeg = 3, + e_lowerLeftLeg = 4, + e_upperRightLeg = 5, + e_lowerRightLeg = 6, + e_upperLeftArm = 7, + e_lowerLeftArm = 8, + e_upperRightArm = 9, + e_lowerRightArm = 10, + e_count = 11, + }; + + b2BodyId bodyId; + b2JointId jointId; + float frictionScale; + int parentIndex; +}; + +class Human +{ +public: + Human(); + + void Spawn( b2WorldId worldId, b2Vec2 position, float scale, float frictionTorque, float hertz, float dampingRatio, + int groupIndex, void* userData, bool colorize ); + void Despawn(); + + void ApplyRandomAngularImpulse( float magnitude ); + void SetJointFrictionTorque( float torque ); + void SetJointSpringHertz( float hertz ); + void SetJointDampingRatio( float dampingRatio ); + + Bone m_bones[Bone::e_count]; + float m_scale; + bool m_isSpawned; +}; diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/jsmn/jsmn.h b/tests/cpp-tests/Source/Box2DTestBed/samples/jsmn/jsmn.h new file mode 100644 index 000000000000..8ac14c1bdec9 --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/jsmn/jsmn.h @@ -0,0 +1,471 @@ +/* + * MIT License + * + * Copyright (c) 2010 Serge Zaitsev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef JSMN_H +#define JSMN_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef JSMN_STATIC +#define JSMN_API static +#else +#define JSMN_API extern +#endif + +/** + * JSON type identifier. Basic types are: + * o Object + * o Array + * o String + * o Other primitive: number, boolean (true/false) or null + */ +typedef enum { + JSMN_UNDEFINED = 0, + JSMN_OBJECT = 1 << 0, + JSMN_ARRAY = 1 << 1, + JSMN_STRING = 1 << 2, + JSMN_PRIMITIVE = 1 << 3 +} jsmntype_t; + +enum jsmnerr { + /* Not enough tokens were provided */ + JSMN_ERROR_NOMEM = -1, + /* Invalid character inside JSON string */ + JSMN_ERROR_INVAL = -2, + /* The string is not a full JSON packet, more bytes expected */ + JSMN_ERROR_PART = -3 +}; + +/** + * JSON token description. + * type type (object, array, string etc.) + * start start position in JSON data string + * end end position in JSON data string + */ +typedef struct jsmntok { + jsmntype_t type; + int start; + int end; + int size; +#ifdef JSMN_PARENT_LINKS + int parent; +#endif +} jsmntok_t; + +/** + * JSON parser. Contains an array of token blocks available. Also stores + * the string being parsed now and current position in that string. + */ +typedef struct jsmn_parser { + unsigned int pos; /* offset in the JSON string */ + unsigned int toknext; /* next token to allocate */ + int toksuper; /* superior token node, e.g. parent object or array */ +} jsmn_parser; + +/** + * Create JSON parser over an array of tokens + */ +JSMN_API void jsmn_init(jsmn_parser *parser); + +/** + * Run JSON parser. It parses a JSON data string into and array of tokens, each + * describing + * a single JSON object. + */ +JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, + jsmntok_t *tokens, const unsigned int num_tokens); + +#ifndef JSMN_HEADER +/** + * Allocates a fresh unused token from the token pool. + */ +static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *tok; + if (parser->toknext >= num_tokens) { + return NULL; + } + tok = &tokens[parser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; +#ifdef JSMN_PARENT_LINKS + tok->parent = -1; +#endif + return tok; +} + +/** + * Fills token type and boundaries. + */ +static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type, + const int start, const int end) { + token->type = type; + token->start = start; + token->end = end; + token->size = 0; +} + +/** + * Fills next available token with JSON primitive. + */ +static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, + const size_t len, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *token; + int start; + + start = parser->pos; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + switch (js[parser->pos]) { +#ifndef JSMN_STRICT + /* In strict mode primitive must be followed by "," or "}" or "]" */ + case ':': +#endif + case '\t': + case '\r': + case '\n': + case ' ': + case ',': + case ']': + case '}': + goto found; + default: + /* to quiet a warning from gcc*/ + break; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } +#ifdef JSMN_STRICT + /* In strict mode primitive must be followed by a comma/object/array */ + parser->pos = start; + return JSMN_ERROR_PART; +#endif + +found: + if (tokens == NULL) { + parser->pos--; + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + parser->pos--; + return 0; +} + +/** + * Fills next token with JSON string. + */ +static int jsmn_parse_string(jsmn_parser *parser, const char *js, + const size_t len, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *token; + + int start = parser->pos; + + /* Skip starting quote */ + parser->pos++; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c = js[parser->pos]; + + /* Quote: end of string */ + if (c == '\"') { + if (tokens == NULL) { + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + return 0; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\' && parser->pos + 1 < len) { + int i; + parser->pos++; + switch (js[parser->pos]) { + /* Allowed escaped symbols */ + case '\"': + case '/': + case '\\': + case 'b': + case 'f': + case 'r': + case 'n': + case 't': + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + parser->pos++; + for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; + i++) { + /* If it isn't a hex character we have an error */ + if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ + (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ + (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ + parser->pos = start; + return JSMN_ERROR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; + return JSMN_ERROR_PART; +} + +/** + * Parse JSON string and fill tokens. + */ +JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, + jsmntok_t *tokens, const unsigned int num_tokens) { + int r; + int i; + jsmntok_t *token; + int count = parser->toknext; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c; + jsmntype_t type; + + c = js[parser->pos]; + switch (c) { + case '{': + case '[': + count++; + if (tokens == NULL) { + break; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + return JSMN_ERROR_NOMEM; + } + if (parser->toksuper != -1) { + jsmntok_t *t = &tokens[parser->toksuper]; +#ifdef JSMN_STRICT + /* In strict mode an object or array can't become a key */ + if (t->type == JSMN_OBJECT) { + return JSMN_ERROR_INVAL; + } +#endif + t->size++; +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + break; + case '}': + case ']': + if (tokens == NULL) { + break; + } + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); +#ifdef JSMN_PARENT_LINKS + if (parser->toknext < 1) { + return JSMN_ERROR_INVAL; + } + token = &tokens[parser->toknext - 1]; + for (;;) { + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + token->end = parser->pos + 1; + parser->toksuper = token->parent; + break; + } + if (token->parent == -1) { + if (token->type != type || parser->toksuper == -1) { + return JSMN_ERROR_INVAL; + } + break; + } + token = &tokens[token->parent]; + } +#else + for (i = parser->toknext - 1; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + parser->toksuper = -1; + token->end = parser->pos + 1; + break; + } + } + /* Error if unmatched closing bracket */ + if (i == -1) { + return JSMN_ERROR_INVAL; + } + for (; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + parser->toksuper = i; + break; + } + } +#endif + break; + case '\"': + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r < 0) { + return r; + } + count++; + if (parser->toksuper != -1 && tokens != NULL) { + tokens[parser->toksuper].size++; + } + break; + case '\t': + case '\r': + case '\n': + case ' ': + break; + case ':': + parser->toksuper = parser->toknext - 1; + break; + case ',': + if (tokens != NULL && parser->toksuper != -1 && + tokens[parser->toksuper].type != JSMN_ARRAY && + tokens[parser->toksuper].type != JSMN_OBJECT) { +#ifdef JSMN_PARENT_LINKS + parser->toksuper = tokens[parser->toksuper].parent; +#else + for (i = parser->toknext - 1; i >= 0; i--) { + if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { + if (tokens[i].start != -1 && tokens[i].end == -1) { + parser->toksuper = i; + break; + } + } + } +#endif + } + break; +#ifdef JSMN_STRICT + /* In strict mode primitives are: numbers and booleans */ + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 't': + case 'f': + case 'n': + /* And they must not be keys of the object */ + if (tokens != NULL && parser->toksuper != -1) { + const jsmntok_t *t = &tokens[parser->toksuper]; + if (t->type == JSMN_OBJECT || + (t->type == JSMN_STRING && t->size != 0)) { + return JSMN_ERROR_INVAL; + } + } +#else + /* In non-strict mode every unquoted value is a primitive */ + default: +#endif + r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); + if (r < 0) { + return r; + } + count++; + if (parser->toksuper != -1 && tokens != NULL) { + tokens[parser->toksuper].size++; + } + break; + +#ifdef JSMN_STRICT + /* Unexpected char in strict mode */ + default: + return JSMN_ERROR_INVAL; +#endif + } + } + + if (tokens != NULL) { + for (i = parser->toknext - 1; i >= 0; i--) { + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) { + return JSMN_ERROR_PART; + } + } + } + + return count; +} + +/** + * Creates a new parser based over a given buffer with an array of tokens + * available. + */ +JSMN_API void jsmn_init(jsmn_parser *parser) { + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; +} + +#endif /* JSMN_HEADER */ + +#ifdef __cplusplus +} +#endif + +#endif /* JSMN_H */ diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/sample.cpp b/tests/cpp-tests/Source/Box2DTestBed/samples/sample.cpp new file mode 100644 index 000000000000..dada65b29fd1 --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/sample.cpp @@ -0,0 +1,511 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#include "sample.h" + +#include "draw.h" +#include "settings.h" + +#include "box2d/box2d.h" +#include "box2d/collision.h" +#include "box2d/math_functions.h" + +#include +#include +#include + +static void* EnqueueTask( b2TaskCallback* task, int32_t itemCount, int32_t minRange, void* taskContext, void* userContext ) +{ + Sample* sample = static_cast( userContext ); + if ( sample->m_taskCount < maxTasks ) + { + SampleTask& sampleTask = sample->m_tasks[sample->m_taskCount]; + sampleTask.m_SetSize = itemCount; + sampleTask.m_MinRange = minRange; + sampleTask.m_task = task; + sampleTask.m_taskContext = taskContext; + sample->m_scheduler.AddTaskSetToPipe( &sampleTask ); + ++sample->m_taskCount; + return &sampleTask; + } + else + { + // This is not fatal but the maxTasks should be increased + assert( false ); + task( 0, itemCount, 0, taskContext ); + return nullptr; + } +} + +static void FinishTask( void* taskPtr, void* userContext ) +{ + if ( taskPtr != nullptr ) + { + SampleTask* sampleTask = static_cast( taskPtr ); + Sample* sample = static_cast( userContext ); + sample->m_scheduler.WaitforTask( sampleTask ); + } +} + +static void TestMathCpp() +{ + b2Vec2 a = { 1.0f, 2.0f }; + b2Vec2 b = { 3.0f, 4.0f }; + + b2Vec2 c = a; + c += b; + c -= b; + c *= 2.0f; + c = -a; + c = c + b; + c = c - a; + c = 2.0f * a; + c = a * 2.0f; + + if ( b == a ) + { + c = a; + } + + if ( b != a ) + { + c = b; + } + + c += c; +} + +Sample::Sample( Settings& settings ) +{ + m_scheduler.Initialize( settings.workerCount ); + m_taskCount = 0; + + m_threadCount = 1 + settings.workerCount; + + b2WorldDef worldDef = b2DefaultWorldDef(); + worldDef.workerCount = settings.workerCount; + worldDef.enqueueTask = EnqueueTask; + worldDef.finishTask = FinishTask; + worldDef.userTaskContext = this; + worldDef.enableSleep = settings.enableSleep; + + m_worldId = b2CreateWorld( &worldDef ); + m_textLine = 30; + m_textIncrement = 18; + m_mouseJointId = b2_nullJointId; + + m_stepCount = 0; + + m_groundBodyId = b2_nullBodyId; + + m_maxProfile = {}; + m_totalProfile = {}; + + g_seed = RAND_SEED; + + TestMathCpp(); +} + +Sample::~Sample() +{ + // By deleting the world, we delete the bomb, mouse joint, etc. + b2DestroyWorld( m_worldId ); +} + +void Sample::DrawTitle( const char* string ) +{ + g_draw.DrawString( 5, 5, string ); + m_textLine = int( 26.0f ); +} + +struct QueryContext +{ + b2Vec2 point; + b2BodyId bodyId = b2_nullBodyId; +}; + +bool QueryCallback( b2ShapeId shapeId, void* context ) +{ + QueryContext* queryContext = static_cast( context ); + + b2BodyId bodyId = b2Shape_GetBody( shapeId ); + b2BodyType bodyType = b2Body_GetType( bodyId ); + if ( bodyType != b2_dynamicBody ) + { + // continue query + return true; + } + + bool overlap = b2Shape_TestPoint( shapeId, queryContext->point ); + if ( overlap ) + { + // found shape + queryContext->bodyId = bodyId; + return false; + } + + return true; +} + +void Sample::MouseDown( b2Vec2 p, int button, int mod ) +{ + if ( B2_IS_NON_NULL( m_mouseJointId ) ) + { + return; + } + + if ( button == GLFW_MOUSE_BUTTON_1 ) + { + // Make a small box. + b2AABB box; + b2Vec2 d = { 0.001f, 0.001f }; + box.lowerBound = b2Sub( p, d ); + box.upperBound = b2Add( p, d ); + + // Query the world for overlapping shapes. + QueryContext queryContext = { p, b2_nullBodyId }; + b2World_OverlapAABB( m_worldId, box, b2DefaultQueryFilter(), QueryCallback, &queryContext ); + + if ( B2_IS_NON_NULL( queryContext.bodyId ) ) + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + m_groundBodyId = b2CreateBody( m_worldId, &bodyDef ); + + b2MouseJointDef mouseDef = b2DefaultMouseJointDef(); + mouseDef.bodyIdA = m_groundBodyId; + mouseDef.bodyIdB = queryContext.bodyId; + mouseDef.target = p; + mouseDef.hertz = 5.0f; + mouseDef.dampingRatio = 0.7f; + mouseDef.maxForce = 1000.0f * b2Body_GetMass( queryContext.bodyId ); + m_mouseJointId = b2CreateMouseJoint( m_worldId, &mouseDef ); + + b2Body_SetAwake( queryContext.bodyId, true ); + } + } +} + +void Sample::MouseUp( b2Vec2 p, int button ) +{ + if ( b2Joint_IsValid( m_mouseJointId ) == false ) + { + // The world or attached body was destroyed. + m_mouseJointId = b2_nullJointId; + } + + if ( B2_IS_NON_NULL( m_mouseJointId ) && button == GLFW_MOUSE_BUTTON_1 ) + { + b2DestroyJoint( m_mouseJointId ); + m_mouseJointId = b2_nullJointId; + + b2DestroyBody( m_groundBodyId ); + m_groundBodyId = b2_nullBodyId; + } +} + +void Sample::MouseMove( b2Vec2 p ) +{ + if ( b2Joint_IsValid( m_mouseJointId ) == false ) + { + // The world or attached body was destroyed. + m_mouseJointId = b2_nullJointId; + } + + if ( B2_IS_NON_NULL( m_mouseJointId ) ) + { + b2MouseJoint_SetTarget( m_mouseJointId, p ); + b2BodyId bodyIdB = b2Joint_GetBodyB( m_mouseJointId ); + b2Body_SetAwake( bodyIdB, true ); + } +} + +void Sample::ResetProfile() +{ + m_totalProfile = {}; + m_maxProfile = {}; + m_stepCount = 0; +} + +void Sample::Step( Settings& settings ) +{ + float timeStep = settings.hertz > 0.0f ? 1.0f / settings.hertz : 0.0f; + + if ( settings.pause ) + { + if ( settings.singleStep ) + { + settings.singleStep = false; + } + else + { + timeStep = 0.0f; + } + + g_draw.DrawString( 5, m_textLine, "****PAUSED****" ); + m_textLine += m_textIncrement; + } + + g_draw.m_debugDraw.drawingBounds = g_camera.GetViewBounds(); + g_draw.m_debugDraw.useDrawingBounds = settings.useCameraBounds; + + // todo testing + // b2Transform t1 = {g_draw.m_debugDraw.drawingBounds.lowerBound, b2Rot_identity}; + // b2Transform t2 = {g_draw.m_debugDraw.drawingBounds.upperBound, b2Rot_identity}; + // g_draw.DrawSolidCircle(t1, b2Vec2_zero, 1.0f, {1.0f, 0.0f, 0.0f, 1.0f}); + // g_draw.DrawSolidCircle(t2, b2Vec2_zero, 1.0f, {1.0f, 0.0f, 0.0f, 1.0f}); + + g_draw.m_debugDraw.drawShapes = settings.drawShapes; + g_draw.m_debugDraw.drawJoints = settings.drawJoints; + g_draw.m_debugDraw.drawJointExtras = settings.drawJointExtras; + g_draw.m_debugDraw.drawAABBs = settings.drawAABBs; + g_draw.m_debugDraw.drawMass = settings.drawMass; + g_draw.m_debugDraw.drawContacts = settings.drawContactPoints; + g_draw.m_debugDraw.drawGraphColors = settings.drawGraphColors; + g_draw.m_debugDraw.drawContactNormals = settings.drawContactNormals; + g_draw.m_debugDraw.drawContactImpulses = settings.drawContactImpulses; + g_draw.m_debugDraw.drawFrictionImpulses = settings.drawFrictionImpulses; + + b2World_EnableSleeping( m_worldId, settings.enableSleep ); + b2World_EnableWarmStarting( m_worldId, settings.enableWarmStarting ); + b2World_EnableContinuous( m_worldId, settings.enableContinuous ); + + for ( int i = 0; i < 1; ++i ) + { + b2World_Step( m_worldId, timeStep, settings.subStepCount ); + m_taskCount = 0; + } + + b2World_Draw( m_worldId, &g_draw.m_debugDraw ); + + if ( timeStep > 0.0f ) + { + ++m_stepCount; + } + + if ( settings.drawCounters ) + { + b2Counters s = b2World_GetCounters( m_worldId ); + + g_draw.DrawString( 5, m_textLine, "bodies/shapes/contacts/joints = %d/%d/%d/%d", s.bodyCount, s.shapeCount, + s.contactCount, s.jointCount ); + m_textLine += m_textIncrement; + + g_draw.DrawString( 5, m_textLine, "islands/tasks = %d/%d", s.islandCount, s.taskCount ); + m_textLine += m_textIncrement; + + g_draw.DrawString( 5, m_textLine, "tree height static/movable = %d/%d", s.staticTreeHeight, s.treeHeight ); + m_textLine += m_textIncrement; + + int totalCount = 0; + char buffer[256] = { 0 }; + static_assert( std::size( s.colorCounts ) == 12 ); + + int offset = snprintf( buffer, 256, "colors: " ); + for ( int i = 0; i < 12; ++i ) + { + offset += snprintf( buffer + offset, 256 - offset, "%d/", s.colorCounts[i] ); + totalCount += s.colorCounts[i]; + } + snprintf( buffer + offset, 256 - offset, "[%d]", totalCount ); + g_draw.DrawString( 5, m_textLine, buffer ); + m_textLine += m_textIncrement; + + g_draw.DrawString( 5, m_textLine, "stack allocator size = %d K", s.stackUsed / 1024 ); + m_textLine += m_textIncrement; + + g_draw.DrawString( 5, m_textLine, "total allocation = %d K", s.byteCount / 1024 ); + m_textLine += m_textIncrement; + } + + // Track maximum profile times + { + b2Profile p = b2World_GetProfile( m_worldId ); + m_maxProfile.step = b2MaxFloat( m_maxProfile.step, p.step ); + m_maxProfile.pairs = b2MaxFloat( m_maxProfile.pairs, p.pairs ); + m_maxProfile.collide = b2MaxFloat( m_maxProfile.collide, p.collide ); + m_maxProfile.solve = b2MaxFloat( m_maxProfile.solve, p.solve ); + m_maxProfile.buildIslands = b2MaxFloat( m_maxProfile.buildIslands, p.buildIslands ); + m_maxProfile.solveConstraints = b2MaxFloat( m_maxProfile.solveConstraints, p.solveConstraints ); + m_maxProfile.prepareTasks = b2MaxFloat( m_maxProfile.prepareTasks, p.prepareTasks ); + m_maxProfile.solverTasks = b2MaxFloat( m_maxProfile.solverTasks, p.solverTasks ); + m_maxProfile.prepareConstraints = b2MaxFloat( m_maxProfile.prepareConstraints, p.prepareConstraints ); + m_maxProfile.integrateVelocities = b2MaxFloat( m_maxProfile.integrateVelocities, p.integrateVelocities ); + m_maxProfile.warmStart = b2MaxFloat( m_maxProfile.warmStart, p.warmStart ); + m_maxProfile.solveVelocities = b2MaxFloat( m_maxProfile.solveVelocities, p.solveVelocities ); + m_maxProfile.integratePositions = b2MaxFloat( m_maxProfile.integratePositions, p.integratePositions ); + m_maxProfile.relaxVelocities = b2MaxFloat( m_maxProfile.relaxVelocities, p.relaxVelocities ); + m_maxProfile.applyRestitution = b2MaxFloat( m_maxProfile.applyRestitution, p.applyRestitution ); + m_maxProfile.storeImpulses = b2MaxFloat( m_maxProfile.storeImpulses, p.storeImpulses ); + m_maxProfile.finalizeBodies = b2MaxFloat( m_maxProfile.finalizeBodies, p.finalizeBodies ); + m_maxProfile.sleepIslands = b2MaxFloat( m_maxProfile.sleepIslands, p.sleepIslands ); + m_maxProfile.splitIslands = b2MaxFloat( m_maxProfile.splitIslands, p.splitIslands ); + m_maxProfile.hitEvents = b2MaxFloat( m_maxProfile.hitEvents, p.hitEvents ); + m_maxProfile.broadphase = b2MaxFloat( m_maxProfile.broadphase, p.broadphase ); + m_maxProfile.continuous = b2MaxFloat( m_maxProfile.continuous, p.continuous ); + + m_totalProfile.step += p.step; + m_totalProfile.pairs += p.pairs; + m_totalProfile.collide += p.collide; + m_totalProfile.solve += p.solve; + m_totalProfile.buildIslands += p.buildIslands; + m_totalProfile.solveConstraints += p.solveConstraints; + m_totalProfile.prepareTasks += p.prepareTasks; + m_totalProfile.solverTasks += p.solverTasks; + m_totalProfile.prepareConstraints += p.prepareConstraints; + m_totalProfile.integrateVelocities += p.integrateVelocities; + m_totalProfile.warmStart += p.warmStart; + m_totalProfile.solveVelocities += p.solveVelocities; + m_totalProfile.integratePositions += p.integratePositions; + m_totalProfile.relaxVelocities += p.relaxVelocities; + m_totalProfile.applyRestitution += p.applyRestitution; + m_totalProfile.storeImpulses += p.storeImpulses; + m_totalProfile.finalizeBodies += p.finalizeBodies; + m_totalProfile.sleepIslands += p.sleepIslands; + m_totalProfile.splitIslands += p.splitIslands; + m_totalProfile.hitEvents += p.hitEvents; + m_totalProfile.broadphase += p.broadphase; + m_totalProfile.continuous += p.continuous; + } + + if ( settings.drawProfile ) + { + b2Profile p = b2World_GetProfile( m_worldId ); + + b2Profile aveProfile; + memset( &aveProfile, 0, sizeof( b2Profile ) ); + if ( m_stepCount > 0 ) + { + float scale = 1.0f / m_stepCount; + aveProfile.step = scale * m_totalProfile.step; + aveProfile.pairs = scale * m_totalProfile.pairs; + aveProfile.collide = scale * m_totalProfile.collide; + aveProfile.solve = scale * m_totalProfile.solve; + aveProfile.buildIslands = scale * m_totalProfile.buildIslands; + aveProfile.solveConstraints = scale * m_totalProfile.solveConstraints; + aveProfile.prepareTasks = scale * m_totalProfile.prepareTasks; + aveProfile.solverTasks = scale * m_totalProfile.solverTasks; + aveProfile.prepareConstraints = scale * m_totalProfile.prepareConstraints; + aveProfile.integrateVelocities = scale * m_totalProfile.integrateVelocities; + aveProfile.warmStart = scale * m_totalProfile.warmStart; + aveProfile.solveVelocities = scale * m_totalProfile.solveVelocities; + aveProfile.integratePositions = scale * m_totalProfile.integratePositions; + aveProfile.relaxVelocities = scale * m_totalProfile.relaxVelocities; + aveProfile.applyRestitution = scale * m_totalProfile.applyRestitution; + aveProfile.storeImpulses = scale * m_totalProfile.storeImpulses; + aveProfile.finalizeBodies = scale * m_totalProfile.finalizeBodies; + aveProfile.sleepIslands = scale * m_totalProfile.sleepIslands; + aveProfile.splitIslands = scale * m_totalProfile.splitIslands; + aveProfile.hitEvents = scale * m_totalProfile.hitEvents; + aveProfile.broadphase = scale * m_totalProfile.broadphase; + aveProfile.continuous = scale * m_totalProfile.continuous; + } + + g_draw.DrawString( 5, m_textLine, "step [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.step, aveProfile.step, + m_maxProfile.step ); + m_textLine += m_textIncrement; + g_draw.DrawString( 5, m_textLine, "pairs [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.pairs, aveProfile.pairs, + m_maxProfile.pairs ); + m_textLine += m_textIncrement; + g_draw.DrawString( 5, m_textLine, "collide [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.collide, aveProfile.collide, + m_maxProfile.collide ); + m_textLine += m_textIncrement; + g_draw.DrawString( 5, m_textLine, "solve [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.solve, aveProfile.solve, + m_maxProfile.solve ); + m_textLine += m_textIncrement; + g_draw.DrawString( 5, m_textLine, "builds island [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.buildIslands, + aveProfile.buildIslands, m_maxProfile.buildIslands ); + m_textLine += m_textIncrement; + g_draw.DrawString( 5, m_textLine, "solve constraints [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.solveConstraints, + aveProfile.solveConstraints, m_maxProfile.solveConstraints ); + m_textLine += m_textIncrement; + g_draw.DrawString( 5, m_textLine, "prepare tasks [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.prepareTasks, + aveProfile.prepareTasks, m_maxProfile.prepareTasks ); + m_textLine += m_textIncrement; + g_draw.DrawString( 5, m_textLine, "solver tasks [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.solverTasks, + aveProfile.solverTasks, m_maxProfile.solverTasks ); + m_textLine += m_textIncrement; + g_draw.DrawString( 5, m_textLine, "prepare constraints [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.prepareConstraints, + aveProfile.prepareConstraints, m_maxProfile.prepareConstraints ); + m_textLine += m_textIncrement; + g_draw.DrawString( 5, m_textLine, "integrate velocities [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.integrateVelocities, + aveProfile.integrateVelocities, m_maxProfile.integrateVelocities ); + m_textLine += m_textIncrement; + g_draw.DrawString( 5, m_textLine, "warm start [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.warmStart, aveProfile.warmStart, + m_maxProfile.warmStart ); + m_textLine += m_textIncrement; + g_draw.DrawString( 5, m_textLine, "solve velocities [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.solveVelocities, + aveProfile.solveVelocities, m_maxProfile.solveVelocities ); + m_textLine += m_textIncrement; + g_draw.DrawString( 5, m_textLine, "integrate positions [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.integratePositions, + aveProfile.integratePositions, m_maxProfile.integratePositions ); + m_textLine += m_textIncrement; + g_draw.DrawString( 5, m_textLine, "relax velocities [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.relaxVelocities, + aveProfile.relaxVelocities, m_maxProfile.relaxVelocities ); + m_textLine += m_textIncrement; + g_draw.DrawString( 5, m_textLine, "apply restitution [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.applyRestitution, + aveProfile.applyRestitution, m_maxProfile.applyRestitution ); + m_textLine += m_textIncrement; + g_draw.DrawString( 5, m_textLine, "store impulses [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.storeImpulses, + aveProfile.storeImpulses, m_maxProfile.storeImpulses ); + m_textLine += m_textIncrement; + g_draw.DrawString( 5, m_textLine, "finalize bodies [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.finalizeBodies, + aveProfile.finalizeBodies, m_maxProfile.finalizeBodies ); + m_textLine += m_textIncrement; + g_draw.DrawString( 5, m_textLine, "sleep islands [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.sleepIslands, + aveProfile.sleepIslands, m_maxProfile.sleepIslands ); + m_textLine += m_textIncrement; + g_draw.DrawString( 5, m_textLine, "split islands [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.splitIslands, + aveProfile.splitIslands, m_maxProfile.splitIslands ); + m_textLine += m_textIncrement; + g_draw.DrawString( 5, m_textLine, "hit events [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.hitEvents, aveProfile.hitEvents, + m_maxProfile.hitEvents ); + m_textLine += m_textIncrement; + g_draw.DrawString( 5, m_textLine, "broad-phase [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.broadphase, aveProfile.broadphase, + m_maxProfile.broadphase ); + m_textLine += m_textIncrement; + g_draw.DrawString( 5, m_textLine, "continuous collision [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.continuous, + aveProfile.continuous, m_maxProfile.continuous ); + m_textLine += m_textIncrement; + } +} + +void Sample::ShiftOrigin( b2Vec2 newOrigin ) +{ + // m_world->ShiftOrigin(newOrigin); +} + +SampleEntry g_sampleEntries[MAX_SAMPLES] = {}; +int g_sampleCount = 0; + +int RegisterSample( const char* category, const char* name, SampleCreateFcn* fcn ) +{ + int index = g_sampleCount; + if ( index < MAX_SAMPLES ) + { + g_sampleEntries[index] = { category, name, fcn }; + ++g_sampleCount; + return index; + } + + return -1; +} + +uint32_t g_seed = RAND_SEED; + +b2Polygon RandomPolygon( float extent ) +{ + b2Vec2 points[b2_maxPolygonVertices]; + int count = 3 + RandomInt() % 6; + for ( int i = 0; i < count; ++i ) + { + points[i] = RandomVec2( -extent, extent ); + } + + b2Hull hull = b2ComputeHull( points, count ); + if ( hull.count > 0 ) + { + return b2MakePolygon( &hull, 0.0f ); + } + + return b2MakeSquare( extent ); +} diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/sample.h b/tests/cpp-tests/Source/Box2DTestBed/samples/sample.h new file mode 100644 index 000000000000..f697213ee879 --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/sample.h @@ -0,0 +1,169 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include "box2d/collision.h" +#include "box2d/id.h" +#include "box2d/types.h" +#include "../Box2DTestDebugDrawNode.h" + +// todo this include is slow +#include "TaskScheduler.h" + +#define ARRAY_COUNT( A ) (int)( sizeof( A ) / sizeof( A[0] ) ) + +struct Settings; + +#ifdef NDEBUG +constexpr bool g_sampleDebug = false; +#else +constexpr bool g_sampleDebug = true; +#endif + +constexpr int32_t k_maxContactPoints = 12 * 2048; + +struct ContactPoint +{ + b2ShapeId shapeIdA; + b2ShapeId shapeIdB; + b2Vec2 normal; + b2Vec2 position; + bool persisted; + float normalImpulse; + float tangentImpulse; + float separation; + int32_t constraintIndex; + int32_t color; +}; + +class SampleTask : public enki::ITaskSet +{ +public: + SampleTask() = default; + + void ExecuteRange( enki::TaskSetPartition range, uint32_t threadIndex ) override + { + m_task( range.start, range.end, threadIndex, m_taskContext ); + } + + b2TaskCallback* m_task = nullptr; + void* m_taskContext = nullptr; +}; + +constexpr int32_t maxTasks = 64; +constexpr int32_t maxThreads = 64; + +class Sample +{ +public: + explicit Sample( Settings& settings ); + virtual ~Sample(); + + void DrawTitle( const char* string ); + virtual void Step( Settings& settings ); + virtual void UpdateUI() + { + } + virtual void Keyboard( int ) + { + } + virtual void MouseDown( b2Vec2 p, int button, int mod ); + virtual void MouseUp( b2Vec2 p, int button ); + virtual void MouseMove( b2Vec2 p ); + + void ResetProfile(); + void ShiftOrigin( b2Vec2 newOrigin ); + + friend class DestructionListener; + friend class BoundaryListener; + friend class ContactListener; + + enki::TaskScheduler m_scheduler; + SampleTask m_tasks[maxTasks]; + int32_t m_taskCount; + int m_threadCount; + + b2BodyId m_groundBodyId; + + // DestructionListener m_destructionListener; + int32_t m_textLine; + b2WorldId m_worldId; + b2JointId m_mouseJointId; + int32_t m_stepCount; + int32_t m_textIncrement; + b2Profile m_maxProfile; + b2Profile m_totalProfile; +}; + +typedef Sample* SampleCreateFcn( Settings& settings ); + +int RegisterSample( const char* category, const char* name, SampleCreateFcn* fcn ); + +struct SampleEntry +{ + const char* category; + const char* name; + SampleCreateFcn* createFcn; +}; + +#define MAX_SAMPLES 256 +extern SampleEntry g_sampleEntries[MAX_SAMPLES]; +extern int g_sampleCount; + +#define RAND_LIMIT 32767 +#define RAND_SEED 12345 + +// Global seed for simple random number generator. This is reset +// for each sample. +extern uint32_t g_seed; + +// Simple random number generator. Using this instead of rand() +// for cross platform determinism. +inline int RandomInt() +{ + // XorShift32 algorithm + uint32_t x = g_seed; + x ^= x << 13; + x ^= x >> 17; + x ^= x << 5; + g_seed = x; + + // Map the 32-bit value to the range 0 to RAND_LIMIT + return (int)( x % ( RAND_LIMIT + 1 ) ); +} + +// Random integer in range [lo, hi] +inline float RandomInt( int lo, int hi ) +{ + return lo + RandomInt() % ( hi - lo + 1 ); +} + +// Random number in range [-1,1] +inline float RandomFloat() +{ + float r = (float)( RandomInt() & ( RAND_LIMIT ) ); + r /= RAND_LIMIT; + r = 2.0f * r - 1.0f; + return r; +} + +// Random floating point number in range [lo, hi] +inline float RandomFloat( float lo, float hi ) +{ + float r = (float)( RandomInt() & ( RAND_LIMIT ) ); + r /= RAND_LIMIT; + r = ( hi - lo ) * r + lo; + return r; +} + +// Random vector with coordinates in range [lo, hi] +inline b2Vec2 RandomVec2( float lo, float hi ) +{ + b2Vec2 v; + v.x = RandomFloat( lo, hi ); + v.y = RandomFloat( lo, hi ); + return v; +} + +b2Polygon RandomPolygon( float extent ); diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/sample_benchmark.cpp b/tests/cpp-tests/Source/Box2DTestBed/samples/sample_benchmark.cpp new file mode 100644 index 000000000000..d761803b3937 --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/sample_benchmark.cpp @@ -0,0 +1,1531 @@ +// SPDX-FileCopyrightText: 2022 Erin Catto +// SPDX-License-Identifier: MIT + +#include "draw.h" +#include "human.h" +#include "sample.h" +#include "settings.h" + +#include "box2d/box2d.h" +#include "box2d/math_functions.h" + +#include +#include + +// Note: resetting the scene is non-deterministic because the world uses freelists +class BenchmarkBarrel : public Sample +{ +public: + enum ShapeType + { + e_circleShape = 0, + e_caspuleShape, + e_mixShape, + e_compoundShape, + e_humanShape, + }; + + enum + { + e_maxColumns = 26, + e_maxRows = 130, + }; + + explicit BenchmarkBarrel( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 8.0f, 53.0f }; + g_camera.m_zoom = 25.0f * 2.35f; + } + + settings.drawJoints = false; + + float groundSize = 25.0f; + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2Polygon box = b2MakeBox( groundSize, 1.2f ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + + box = b2MakeOffsetBox( 1.2f, 2.0f * groundSize, { -groundSize, 2.0f * groundSize }, b2Rot_identity ); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + + box = b2MakeOffsetBox( 1.2f, 2.0f * groundSize, { groundSize, 2.0f * groundSize }, b2Rot_identity ); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + + box = b2MakeOffsetBox( 800.0f, 10.0f, { 0.0f, -80.0f }, b2Rot_identity ); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + } + + for ( int i = 0; i < e_maxRows * e_maxColumns; ++i ) + { + m_bodies[i] = b2_nullBodyId; + } + + m_shapeType = e_circleShape; + + CreateScene(); + } + + void CreateScene() + { + g_seed = 42; + + for ( int i = 0; i < e_maxRows * e_maxColumns; ++i ) + { + if ( B2_IS_NON_NULL( m_bodies[i] ) ) + { + b2DestroyBody( m_bodies[i] ); + m_bodies[i] = b2_nullBodyId; + } + + if ( m_humans[i].m_isSpawned ) + { + m_humans[i].Despawn(); + } + } + + m_columnCount = g_sampleDebug ? 10 : e_maxColumns; + m_rowCount = g_sampleDebug ? 40 : e_maxRows; + + if ( m_shapeType == e_compoundShape ) + { + if constexpr ( g_sampleDebug == false ) + { + m_columnCount = 20; + } + } + else if ( m_shapeType == e_humanShape ) + { + if constexpr ( g_sampleDebug ) + { + m_rowCount = 5; + m_columnCount = 10; + } + else + { + m_columnCount = 15; + m_rowCount = 50; + } + } + + float rad = 0.5f; + + float shift = 1.15f; + float centerx = shift * m_columnCount / 2.0f; + float centery = shift / 2.0f; + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 1.0f; + shapeDef.friction = 0.5f; + + b2Capsule capsule = { { 0.0f, -0.25f }, { 0.0f, 0.25f }, rad }; + b2Circle circle = { { 0.0f, 0.0f }, rad }; + + b2Vec2 points[3] = { { -0.1f, -0.5f }, { 0.1f, -0.5f }, { 0.0f, 0.5f } }; + b2Hull wedgeHull = b2ComputeHull( points, 3 ); + b2Polygon wedge = b2MakePolygon( &wedgeHull, 0.0f ); + + b2Vec2 vertices[3]; + vertices[0] = { -1.0f, 0.0f }; + vertices[1] = { 0.5f, 1.0f }; + vertices[2] = { 0.0f, 2.0f }; + b2Hull hull = b2ComputeHull( vertices, 3 ); + b2Polygon left = b2MakePolygon( &hull, 0.0f ); + + vertices[0] = { 1.0f, 0.0f }; + vertices[1] = { -0.5f, 1.0f }; + vertices[2] = { 0.0f, 2.0f }; + hull = b2ComputeHull( vertices, 3 ); + b2Polygon right = b2MakePolygon( &hull, 0.0f ); + + // b2Polygon top = b2MakeOffsetBox(0.8f, 0.2f, {0.0f, 0.8f}, 0.0f); + // b2Polygon leftLeg = b2MakeOffsetBox(0.2f, 0.5f, {-0.6f, 0.5f}, 0.0f); + // b2Polygon rightLeg = b2MakeOffsetBox(0.2f, 0.5f, {0.6f, 0.5f}, 0.0f); + + float side = -0.1f; + float extray = 0.5f; + + if ( m_shapeType == e_compoundShape ) + { + extray = 0.25f; + side = 0.25f; + shift = 2.0f; + centerx = shift * m_columnCount / 2.0f - 1.0f; + } + else if ( m_shapeType == e_humanShape ) + { + extray = 0.5f; + side = 0.55f; + shift = 2.5f; + centerx = shift * m_columnCount / 2.0f; + } + + int index = 0; + + for ( int i = 0; i < m_columnCount; ++i ) + { + float x = i * shift - centerx; + + for ( int j = 0; j < m_rowCount; ++j ) + { + float y = j * ( shift + extray ) + centery + 2.0f; + + bodyDef.position = { x + side, y }; + side = -side; + + if ( m_shapeType == e_circleShape ) + { + m_bodies[index] = b2CreateBody( m_worldId, &bodyDef ); + circle.radius = RandomFloat( 0.25f, 0.75f ); + b2CreateCircleShape( m_bodies[index], &shapeDef, &circle ); + } + else if ( m_shapeType == e_caspuleShape ) + { + m_bodies[index] = b2CreateBody( m_worldId, &bodyDef ); + capsule.radius = RandomFloat( 0.25f, 0.5f ); + float length = RandomFloat( 0.25f, 1.0f ); + capsule.center1 = { 0.0f, -0.5f * length }; + capsule.center2 = { 0.0f, 0.5f * length }; + b2CreateCapsuleShape( m_bodies[index], &shapeDef, &capsule ); + } + else if ( m_shapeType == e_mixShape ) + { + m_bodies[index] = b2CreateBody( m_worldId, &bodyDef ); + + int mod = index % 3; + if ( mod == 0 ) + { + circle.radius = RandomFloat( 0.25f, 0.75f ); + b2CreateCircleShape( m_bodies[index], &shapeDef, &circle ); + } + else if ( mod == 1 ) + { + capsule.radius = RandomFloat( 0.25f, 0.5f ); + float length = RandomFloat( 0.25f, 1.0f ); + capsule.center1 = { 0.0f, -0.5f * length }; + capsule.center2 = { 0.0f, 0.5f * length }; + b2CreateCapsuleShape( m_bodies[index], &shapeDef, &capsule ); + } + else if ( mod == 2 ) + { + float width = RandomFloat( 0.1f, 0.5f ); + float height = RandomFloat( 0.5f, 0.75f ); + b2Polygon box = b2MakeBox( width, height ); + + // Don't put a function call into a macro. + float value = RandomFloat( -1.0f, 1.0f ); + box.radius = 0.25f * b2MaxFloat( 0.0f, value ); + b2CreatePolygonShape( m_bodies[index], &shapeDef, &box ); + } + else + { + wedge.radius = RandomFloat( 0.1f, 0.25f ); + b2CreatePolygonShape( m_bodies[index], &shapeDef, &wedge ); + } + } + else if ( m_shapeType == e_compoundShape ) + { + m_bodies[index] = b2CreateBody( m_worldId, &bodyDef ); + + b2CreatePolygonShape( m_bodies[index], &shapeDef, &left ); + b2CreatePolygonShape( m_bodies[index], &shapeDef, &right ); + // b2CreatePolygonShape(m_bodies[index], &shapeDef, &top); + // b2CreatePolygonShape(m_bodies[index], &shapeDef, &leftLeg); + // b2CreatePolygonShape(m_bodies[index], &shapeDef, &rightLeg); + } + else if ( m_shapeType == e_humanShape ) + { + m_humans[index].Spawn( m_worldId, bodyDef.position, 3.5f, 0.05f, 0.0f, 0.0f, index + 1, nullptr, false ); + } + + index += 1; + } + } + } + + void UpdateUI() override + { + float height = 80.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 220.0f, height ) ); + ImGui::Begin( "Benchmark: Barrel", nullptr, ImGuiWindowFlags_NoResize ); + + bool changed = false; + const char* shapeTypes[] = { "Circle", "Capsule", "Mix", "Compound", "Human" }; + + int shapeType = int( m_shapeType ); + changed = changed || ImGui::Combo( "Shape", &shapeType, shapeTypes, IM_ARRAYSIZE( shapeTypes ) ); + m_shapeType = ShapeType( shapeType ); + + changed = changed || ImGui::Button( "Reset Scene" ); + + if ( changed ) + { + CreateScene(); + } + + ImGui::End(); + } + + static Sample* Create( Settings& settings ) + { + return new BenchmarkBarrel( settings ); + } + + b2BodyId m_bodies[e_maxRows * e_maxColumns]; + Human m_humans[e_maxRows * e_maxColumns]; + int m_columnCount; + int m_rowCount; + + ShapeType m_shapeType; +}; + +static int benchmarkBarrel = RegisterSample( "Benchmark", "Barrel", BenchmarkBarrel::Create ); + +class BenchmarkTumbler : public Sample +{ +public: + explicit BenchmarkTumbler( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 1.5f, 10.0f }; + g_camera.m_zoom = 25.0f * 0.6f; + } + + b2BodyId groundId; + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + groundId = b2CreateBody( m_worldId, &bodyDef ); + } + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.enableSleep = true; + bodyDef.position = { 0.0f, 10.0f }; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 50.0f; + + b2Polygon polygon; + polygon = b2MakeOffsetBox( 0.5f, 10.0f, { 10.0f, 0.0f }, b2Rot_identity ); + b2CreatePolygonShape( bodyId, &shapeDef, &polygon ); + polygon = b2MakeOffsetBox( 0.5f, 10.0f, { -10.0f, 0.0f }, b2Rot_identity ); + b2CreatePolygonShape( bodyId, &shapeDef, &polygon ); + polygon = b2MakeOffsetBox( 10.0f, 0.5f, { 0.0f, 10.0f }, b2Rot_identity ); + b2CreatePolygonShape( bodyId, &shapeDef, &polygon ); + polygon = b2MakeOffsetBox( 10.0f, 0.5f, { 0.0f, -10.0f }, b2Rot_identity ); + b2CreatePolygonShape( bodyId, &shapeDef, &polygon ); + + // m_motorSpeed = 9.0f; + m_motorSpeed = 25.0f; + + b2RevoluteJointDef jd = b2DefaultRevoluteJointDef(); + jd.bodyIdA = groundId; + jd.bodyIdB = bodyId; + jd.localAnchorA = { 0.0f, 10.0f }; + jd.localAnchorB = { 0.0f, 0.0f }; + jd.referenceAngle = 0.0f; + jd.motorSpeed = ( b2_pi / 180.0f ) * m_motorSpeed; + jd.maxMotorTorque = 1e8f; + jd.enableMotor = true; + + m_jointId = b2CreateRevoluteJoint( m_worldId, &jd ); + } + + int gridCount = g_sampleDebug ? 20 : 45; + b2Polygon polygon = b2MakeBox( 0.125f, 0.125f ); + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + b2ShapeDef shapeDef = b2DefaultShapeDef(); + + float y = -0.2f * gridCount + 10.0f; + for ( int i = 0; i < gridCount; ++i ) + { + float x = -0.2f * gridCount; + + for ( int j = 0; j < gridCount; ++j ) + { + bodyDef.position = { x, y }; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + + b2CreatePolygonShape( bodyId, &shapeDef, &polygon ); + + x += 0.4f; + } + + y += 0.4f; + } + } + + void UpdateUI() override + { + float height = 60.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 200.0f, height ) ); + ImGui::Begin( "Benchmark: Tumbler", nullptr, ImGuiWindowFlags_NoResize ); + ImGui::PushItemWidth( 120.0f ); + + if ( ImGui::SliderFloat( "Speed", &m_motorSpeed, 0.0f, 100.0f, "%.f" ) ) + { + b2RevoluteJoint_SetMotorSpeed( m_jointId, ( b2_pi / 180.0f ) * m_motorSpeed ); + + if ( m_motorSpeed > 0.0f ) + { + b2Joint_WakeBodies( m_jointId ); + } + } + + ImGui::PopItemWidth(); + ImGui::End(); + } + + static Sample* Create( Settings& settings ) + { + return new BenchmarkTumbler( settings ); + } + + b2JointId m_jointId; + float m_motorSpeed; +}; + +static int benchmarkTumbler = RegisterSample( "Benchmark", "Tumbler", BenchmarkTumbler::Create ); + +// todo try removing kinematics from graph coloring +class BenchmarkManyTumblers : public Sample +{ +public: + explicit BenchmarkManyTumblers( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 1.0f, -5.5 }; + g_camera.m_zoom = 25.0f * 3.4f; + settings.drawJoints = false; + } + + b2BodyDef bodyDef = b2DefaultBodyDef(); + m_groundId = b2CreateBody( m_worldId, &bodyDef ); + + m_rowCount = g_sampleDebug ? 2 : 19; + m_columnCount = g_sampleDebug ? 2 : 19; + + m_tumblerIds = nullptr; + m_positions = nullptr; + m_tumblerCount = 0; + + m_bodyIds = nullptr; + m_bodyCount = 0; + m_bodyIndex = 0; + + m_angularSpeed = 25.0f; + + CreateScene(); + } + + ~BenchmarkManyTumblers() override + { + free( m_tumblerIds ); + free( m_positions ); + free( m_bodyIds ); + } + + void CreateTumbler( b2Vec2 position, int index ) + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_kinematicBody; + bodyDef.position = { position.x, position.y }; + bodyDef.angularVelocity = ( b2_pi / 180.0f ) * m_angularSpeed; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + m_tumblerIds[index] = bodyId; + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 50.0f; + + b2Polygon polygon; + polygon = b2MakeOffsetBox( 0.25f, 2.0f, { 2.0f, 0.0f }, b2Rot_identity ); + b2CreatePolygonShape( bodyId, &shapeDef, &polygon ); + polygon = b2MakeOffsetBox( 0.25f, 2.0f, { -2.0f, 0.0f }, b2Rot_identity ); + b2CreatePolygonShape( bodyId, &shapeDef, &polygon ); + polygon = b2MakeOffsetBox( 2.0f, 0.25f, { 0.0f, 2.0f }, b2Rot_identity ); + b2CreatePolygonShape( bodyId, &shapeDef, &polygon ); + polygon = b2MakeOffsetBox( 2.0f, 0.25f, { 0.0f, -2.0f }, b2Rot_identity ); + b2CreatePolygonShape( bodyId, &shapeDef, &polygon ); + } + + void CreateScene() + { + for ( int i = 0; i < m_bodyCount; ++i ) + { + if ( B2_IS_NON_NULL( m_bodyIds[i] ) ) + { + b2DestroyBody( m_bodyIds[i] ); + } + } + + for ( int i = 0; i < m_tumblerCount; ++i ) + { + b2DestroyBody( m_tumblerIds[i] ); + } + + free( m_tumblerIds ); + free( m_positions ); + + m_tumblerCount = m_rowCount * m_columnCount; + m_tumblerIds = static_cast( malloc( m_tumblerCount * sizeof( b2BodyId ) ) ); + m_positions = static_cast( malloc( m_tumblerCount * sizeof( b2Vec2 ) ) ); + + int index = 0; + float x = -4.0f * m_rowCount; + for ( int i = 0; i < m_rowCount; ++i ) + { + float y = -4.0f * m_columnCount; + for ( int j = 0; j < m_columnCount; ++j ) + { + m_positions[index] = { x, y }; + CreateTumbler( m_positions[index], index ); + ++index; + y += 8.0f; + } + + x += 8.0f; + } + + free( m_bodyIds ); + + int bodiesPerTumbler = g_sampleDebug ? 8 : 50; + m_bodyCount = bodiesPerTumbler * m_tumblerCount; + + m_bodyIds = static_cast( malloc( m_bodyCount * sizeof( b2BodyId ) ) ); + + memset( m_bodyIds, 0, m_bodyCount * sizeof( b2BodyId ) ); + m_bodyIndex = 0; + } + + void UpdateUI() override + { + float height = 110.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 200.0f, height ) ); + ImGui::Begin( "Benchmark: Many Tumblers", nullptr, ImGuiWindowFlags_NoResize ); + ImGui::PushItemWidth( 100.0f ); + + bool changed = false; + changed = changed || ImGui::SliderInt( "Row Count", &m_rowCount, 1, 32 ); + changed = changed || ImGui::SliderInt( "Column Count", &m_columnCount, 1, 32 ); + + if ( changed ) + { + CreateScene(); + } + + if ( ImGui::SliderFloat( "Speed", &m_angularSpeed, 0.0f, 100.0f, "%.f" ) ) + { + for ( int i = 0; i < m_tumblerCount; ++i ) + { + b2Body_SetAngularVelocity( m_tumblerIds[i], ( b2_pi / 180.0f ) * m_angularSpeed ); + b2Body_SetAwake( m_tumblerIds[i], true ); + } + } + + ImGui::PopItemWidth(); + ImGui::End(); + } + + void Step( Settings& settings ) override + { + Sample::Step( settings ); + + if ( m_bodyIndex < m_bodyCount && ( m_stepCount & 0x7 ) == 0 ) + { + b2ShapeDef shapeDef = b2DefaultShapeDef(); + + b2Capsule capsule = { { -0.1f, 0.0f }, { 0.1f, 0.0f }, 0.075f }; + + for ( int i = 0; i < m_tumblerCount; ++i ) + { + assert( m_bodyIndex < m_bodyCount ); + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = m_positions[i]; + m_bodyIds[m_bodyIndex] = b2CreateBody( m_worldId, &bodyDef ); + b2CreateCapsuleShape( m_bodyIds[m_bodyIndex], &shapeDef, &capsule ); + + m_bodyIndex += 1; + } + } + } + + static Sample* Create( Settings& settings ) + { + return new BenchmarkManyTumblers( settings ); + } + + b2BodyId m_groundId; + + int m_rowCount; + int m_columnCount; + + b2BodyId* m_tumblerIds; + b2Vec2* m_positions; + int m_tumblerCount; + + b2BodyId* m_bodyIds; + int m_bodyCount; + int m_bodyIndex; + + float m_angularSpeed; +}; + +static int benchmarkManyTumblers = RegisterSample( "Benchmark", "Many Tumblers", BenchmarkManyTumblers::Create ); + +class BenchmarkLargePyramid : public Sample +{ +public: + explicit BenchmarkLargePyramid( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, 50.0f }; + g_camera.m_zoom = 25.0f * 2.2f; + } + +#ifdef NDEBUG + int baseCount = 100; +#else + int baseCount = 40; +#endif + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.position = { 0.0f, -1.0f }; + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2Polygon box = b2MakeBox( 100.0f, 1.0f ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + } + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 1.0f; + + float h = 0.5f; + b2Polygon box = b2MakeRoundedBox( h - 0.05f, h - 0.05f, 0.05f ); + + float shift = 1.0f * h; + + for ( int i = 0; i < baseCount; ++i ) + { + float y = ( 2.0f * i + 1.0f ) * shift; + + for ( int j = i; j < baseCount; ++j ) + { + float x = ( i + 1.0f ) * shift + 2.0f * ( j - i ) * shift - h * baseCount; + + bodyDef.position = { x, y }; + + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + } + } + } + + static Sample* Create( Settings& settings ) + { + return new BenchmarkLargePyramid( settings ); + } +}; + +static int benchmarkLargePyramid = RegisterSample( "Benchmark", "Large Pyramid", BenchmarkLargePyramid::Create ); + +class BenchmarkManyPyramids : public Sample +{ +public: + explicit BenchmarkManyPyramids( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 16.0f, 110.0f }; + g_camera.m_zoom = 25.0f * 5.0f; + } + + m_extent = 0.5f; + m_round = 0.0f; + m_baseCount = 10; + m_rowCount = g_sampleDebug ? 4 : 20; + m_columnCount = g_sampleDebug ? 4 : 20; + m_groundId = b2_nullBodyId; + m_bodyIds = nullptr; + m_bodyCount = 0; + m_bodyIndex = 0; + + CreateScene(); + } + + ~BenchmarkManyPyramids() override + { + free( m_bodyIds ); + } + + void CreatePyramid( float centerX, float baseY ) + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 1.0f; + + float h = m_extent - m_round; + b2Polygon box = b2MakeRoundedBox( h, h, m_round ); + + float shift = 1.0f * h; + + for ( int i = 0; i < m_baseCount; ++i ) + { + float y = ( 2.0f * i + 1.0f ) * shift + baseY; + + for ( int j = i; j < m_baseCount; ++j ) + { + float x = ( i + 1.0f ) * shift + 2.0f * ( j - i ) * shift + centerX - 0.5f; + + bodyDef.position = { x, y }; + + assert( m_bodyIndex < m_bodyCount ); + m_bodyIds[m_bodyIndex] = b2CreateBody( m_worldId, &bodyDef ); + b2CreatePolygonShape( m_bodyIds[m_bodyIndex], &shapeDef, &box ); + + m_bodyIndex += 1; + } + } + } + + void CreateScene() + { + if ( B2_IS_NON_NULL( m_groundId ) ) + { + b2DestroyBody( m_groundId ); + } + + for ( int i = 0; i < m_bodyCount; ++i ) + { + b2DestroyBody( m_bodyIds[i] ); + } + + free( m_bodyIds ); + + m_bodyCount = m_rowCount * m_columnCount * m_baseCount * ( m_baseCount + 1 ) / 2; + m_bodyIds = (b2BodyId*)malloc( m_bodyCount * sizeof( b2BodyId ) ); + m_bodyIndex = 0; + + b2BodyDef bodyDef = b2DefaultBodyDef(); + m_groundId = b2CreateBody( m_worldId, &bodyDef ); + + float groundDeltaY = 2.0f * m_extent * ( m_baseCount + 1.0f ); + float groundWidth = 2.0f * m_extent * m_columnCount * ( m_baseCount + 1.0f ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + + float groundY = 0.0f; + + for ( int i = 0; i < m_rowCount; ++i ) + { + // b2Segment segment = {{-0.5f * groundWidth, groundY}, {0.5f * groundWidth, groundY}}; + b2Segment segment = { { -0.5f * 2.0f * groundWidth, groundY }, { 0.5f * 2.0f * groundWidth, groundY } }; + b2CreateSegmentShape( m_groundId, &shapeDef, &segment ); + groundY += groundDeltaY; + } + + float baseWidth = 2.0f * m_extent * m_baseCount; + float baseY = 0.0f; + + for ( int i = 0; i < m_rowCount; ++i ) + { + for ( int j = 0; j < m_columnCount; ++j ) + { + float centerX = -0.5f * groundWidth + j * ( baseWidth + 2.0f * m_extent ) + m_extent; + CreatePyramid( centerX, baseY ); + } + + baseY += groundDeltaY; + } + + assert( m_bodyIndex == m_bodyCount ); + } + + void UpdateUI() override + { + float height = 160.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 200.0f, height ) ); + ImGui::Begin( "Benchmark: Many Pyramids", nullptr, ImGuiWindowFlags_NoResize ); + ImGui::PushItemWidth( 100.0f ); + + bool changed = false; + changed = changed || ImGui::SliderInt( "Row Count", &m_rowCount, 1, 32 ); + changed = changed || ImGui::SliderInt( "Column Count", &m_columnCount, 1, 32 ); + changed = changed || ImGui::SliderInt( "Base Count", &m_baseCount, 1, 30 ); + + changed = changed || ImGui::SliderFloat( "Round", &m_round, 0.0f, 0.4f, "%.1f" ); + changed = changed || ImGui::Button( "Reset Scene" ); + + if ( changed ) + { + CreateScene(); + } + + ImGui::PopItemWidth(); + ImGui::End(); + } + + static Sample* Create( Settings& settings ) + { + return new BenchmarkManyPyramids( settings ); + } + + b2BodyId m_groundId; + b2BodyId* m_bodyIds; + int m_bodyCount; + int m_bodyIndex; + int m_baseCount; + int m_rowCount; + int m_columnCount; + float m_round; + float m_extent; +}; + +static int benchmarkManyPyramids = RegisterSample( "Benchmark", "Many Pyramids", BenchmarkManyPyramids::Create ); + +class BenchmarkCreateDestroy : public Sample +{ +public: + enum + { + e_maxBaseCount = 100, + e_maxBodyCount = e_maxBaseCount * ( e_maxBaseCount + 1 ) / 2 + }; + + explicit BenchmarkCreateDestroy( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, 50.0f }; + g_camera.m_zoom = 25.0f * 2.2f; + } + + float groundSize = 100.0f; + + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2Polygon box = b2MakeBox( groundSize, 1.0f ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + + for ( int i = 0; i < e_maxBodyCount; ++i ) + { + m_bodies[i] = b2_nullBodyId; + } + + m_baseCount = g_sampleDebug ? 40 : 100; + m_iterations = g_sampleDebug ? 1 : 10; + m_bodyCount = 0; + } + + void CreateScene() + { + for ( int i = 0; i < e_maxBodyCount; ++i ) + { + if ( B2_IS_NON_NULL( m_bodies[i] ) ) + { + b2DestroyBody( m_bodies[i] ); + m_bodies[i] = b2_nullBodyId; + } + } + + int count = m_baseCount; + float rad = 0.5f; + float shift = rad * 2.0f; + float centerx = shift * count / 2.0f; + float centery = shift / 2.0f + 1.0f; + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 1.0f; + shapeDef.friction = 0.5f; + + float h = 0.5f; + b2Polygon box = b2MakeRoundedBox( h, h, 0.0f ); + + int index = 0; + + for ( int i = 0; i < count; ++i ) + { + float y = i * shift + centery; + + for ( int j = i; j < count; ++j ) + { + float x = 0.5f * i * shift + ( j - i ) * shift - centerx; + bodyDef.position = { x, y }; + + assert( index < e_maxBodyCount ); + m_bodies[index] = b2CreateBody( m_worldId, &bodyDef ); + b2CreatePolygonShape( m_bodies[index], &shapeDef, &box ); + + index += 1; + } + } + + m_bodyCount = index; + } + + void Step( Settings& settings ) override + { + b2Timer timer = b2CreateTimer(); + + for ( int i = 0; i < m_iterations; ++i ) + { + CreateScene(); + } + + float ms = b2GetMilliseconds( &timer ); + + g_draw.DrawString( 5, m_textLine, "milliseconds = %g", ms ); + m_textLine += m_textIncrement; + + Sample::Step( settings ); + } + + static Sample* Create( Settings& settings ) + { + return new BenchmarkCreateDestroy( settings ); + } + + b2BodyId m_bodies[e_maxBodyCount]; + int m_bodyCount; + int m_baseCount; + int m_iterations; +}; + +static int benchmarkCreateDestroy = RegisterSample( "Benchmark", "CreateDestroy", BenchmarkCreateDestroy::Create ); + +class BenchmarkSleep : public Sample +{ +public: + enum + { + e_maxBaseCount = 100, + e_maxBodyCount = e_maxBaseCount * ( e_maxBaseCount + 1 ) / 2 + }; + + explicit BenchmarkSleep( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, 50.0f }; + g_camera.m_zoom = 25.0f * 2.2f; + } + + float groundSize = 100.0f; + + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2Polygon box = b2MakeBox( groundSize, 1.0f ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + + for ( int i = 0; i < e_maxBodyCount; ++i ) + { + m_bodies[i] = b2_nullBodyId; + } + + m_baseCount = g_sampleDebug ? 40 : 100; + m_iterations = g_sampleDebug ? 1 : 41; + m_bodyCount = 0; + m_awake = false; + + m_wakeTotal = 0.0f; + m_wakeCount = 0; + + m_sleepTotal = 0.0f; + m_sleepCount = 0; + + CreateScene(); + } + + void CreateScene() + { + for ( int i = 0; i < e_maxBodyCount; ++i ) + { + if ( B2_IS_NON_NULL( m_bodies[i] ) ) + { + b2DestroyBody( m_bodies[i] ); + m_bodies[i] = b2_nullBodyId; + } + } + + int count = m_baseCount; + float rad = 0.5f; + float shift = rad * 2.0f; + float centerx = shift * count / 2.0f; + float centery = shift / 2.0f + 1.0f; + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 1.0f; + shapeDef.friction = 0.5f; + + float h = 0.5f; + b2Polygon box = b2MakeRoundedBox( h, h, 0.0f ); + + int index = 0; + + for ( int i = 0; i < count; ++i ) + { + float y = i * shift + centery; + + for ( int j = i; j < count; ++j ) + { + float x = 0.5f * i * shift + ( j - i ) * shift - centerx; + bodyDef.position = { x, y }; + + assert( index < e_maxBodyCount ); + m_bodies[index] = b2CreateBody( m_worldId, &bodyDef ); + b2CreatePolygonShape( m_bodies[index], &shapeDef, &box ); + + index += 1; + } + } + + m_bodyCount = index; + } + + void Step( Settings& settings ) override + { + float timeStep = settings.hertz > 0.0f ? 1.0f / settings.hertz : float( 0.0f ); + + b2Timer timer = b2CreateTimer(); + + for ( int i = 0; i < m_iterations; ++i ) + { + b2Body_SetAwake( m_bodies[0], m_awake ); + if ( m_awake ) + { + m_wakeTotal += b2GetMillisecondsAndReset( &timer ); + m_wakeCount += 1; + } + else + { + m_sleepTotal += b2GetMillisecondsAndReset( &timer ); + m_sleepCount += 1; + } + m_awake = !m_awake; + } + + if ( m_wakeCount > 0 ) + { + g_draw.DrawString( 5, m_textLine, "wake ave = %g ms", m_wakeTotal / m_wakeCount ); + m_textLine += m_textIncrement; + } + + if ( m_sleepCount > 0 ) + { + g_draw.DrawString( 5, m_textLine, "sleep ave = %g ms", m_sleepTotal / m_sleepCount ); + m_textLine += m_textIncrement; + } + + Sample::Step( settings ); + } + + static Sample* Create( Settings& settings ) + { + return new BenchmarkSleep( settings ); + } + + b2BodyId m_bodies[e_maxBodyCount]; + int m_bodyCount; + int m_baseCount; + int m_iterations; + float m_wakeTotal; + float m_sleepTotal; + int m_wakeCount; + int m_sleepCount; + bool m_awake; +}; + +static int benchmarkSleep = RegisterSample( "Benchmark", "Sleep", BenchmarkSleep::Create ); + +class BenchmarkJointGrid : public Sample +{ +public: + explicit BenchmarkJointGrid( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 60.0f, -57.0f }; + g_camera.m_zoom = 25.0f * 2.5f; + } + + constexpr int N = g_sampleDebug ? 10 : 100; + + // Allocate to avoid huge stack usage + b2BodyId* bodies = static_cast( malloc( N * N * sizeof( b2BodyId ) ) ); + int index = 0; + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 1.0f; + shapeDef.filter.categoryBits = 2; + shapeDef.filter.maskBits = ~2u; + + b2Circle circle = { { 0.0f, 0.0f }, 0.4f }; + + b2RevoluteJointDef jd = b2DefaultRevoluteJointDef(); + b2BodyDef bodyDef = b2DefaultBodyDef(); + + for ( int k = 0; k < N; ++k ) + { + for ( int i = 0; i < N; ++i ) + { + float fk = (float)k; + float fi = (float)i; + + if ( k >= N / 2 - 3 && k <= N / 2 + 3 && i == 0 ) + { + bodyDef.type = b2_staticBody; + } + else + { + bodyDef.type = b2_dynamicBody; + } + + bodyDef.position = { fk, -fi }; + + b2BodyId body = b2CreateBody( m_worldId, &bodyDef ); + + b2CreateCircleShape( body, &shapeDef, &circle ); + + if ( i > 0 ) + { + jd.bodyIdA = bodies[index - 1]; + jd.bodyIdB = body; + jd.localAnchorA = { 0.0f, -0.5f }; + jd.localAnchorB = { 0.0f, 0.5f }; + b2CreateRevoluteJoint( m_worldId, &jd ); + } + + if ( k > 0 ) + { + jd.bodyIdA = bodies[index - N]; + jd.bodyIdB = body; + jd.localAnchorA = { 0.5f, 0.0f }; + jd.localAnchorB = { -0.5f, 0.0f }; + b2CreateRevoluteJoint( m_worldId, &jd ); + } + + bodies[index++] = body; + } + } + + free( bodies ); + + m_gravity = 10.0f; + } + + void UpdateUI() override + { + float height = 60.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 240.0f, height ) ); + ImGui::Begin( "Benchmark: Joint Grid", nullptr, ImGuiWindowFlags_NoResize ); + + if ( ImGui::SliderFloat( "gravity", &m_gravity, 0.0f, 20.0f, "%.1f" ) ) + { + b2World_SetGravity( m_worldId, { 0.0f, -m_gravity } ); + } + + ImGui::End(); + } + + static Sample* Create( Settings& settings ) + { + return new BenchmarkJointGrid( settings ); + } + + float m_gravity; +}; + +static int benchmarkJointGridIndex = RegisterSample( "Benchmark", "Joint Grid", BenchmarkJointGrid::Create ); + +class BenchmarkSmash : public Sample +{ +public: + explicit BenchmarkSmash( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 60.0f, 6.0f }; + g_camera.m_zoom = 25.0f * 1.6f; + } + + b2World_SetGravity( m_worldId, b2Vec2_zero ); + + { + b2Polygon box = b2MakeBox( 4.0f, 4.0f ); + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = { -20.0f, 0.0f }; + bodyDef.linearVelocity = { 40.0f, 0.0f }; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 8.0f; + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + } + + m_created = false; + } + + void CreateScene1() + { + ImGuiIO& io = ImGui::GetIO(); + if ( io.Fonts->Fonts.size() == 0 ) + { + return; + } + + const ImFont* font = io.Fonts->Fonts[0]; + const unsigned char* pixels = font->ContainerAtlas->TexPixelsAlpha8; + int width = font->ContainerAtlas->TexWidth; + int height = font->ContainerAtlas->TexHeight; + + float scale = 0.1f; + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.isAwake = false; + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + + for ( int i = 0; i < height; ++i ) + { + for ( int j = 0; j < width; ++j ) + { + unsigned char value = pixels[i * width + j]; + if ( value != 0 && value != 0xFF ) + { + value += 0; + } + + if ( value > 50 ) + { + b2Polygon square = b2MakeSquare( 0.95f * scale * ( value / 255.0f ) ); + bodyDef.position = { 2.0f * j * scale, 2.0f * ( height - i ) * scale - 10.0f }; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2CreatePolygonShape( bodyId, &shapeDef, &square ); + } + } + } + + m_created = true; + } + + void CreateScene2() + { + ImGuiIO& io = ImGui::GetIO(); + if ( io.Fonts->Fonts.size() == 0 ) + { + return; + } + + const ImFont* font = io.Fonts->Fonts.back(); + const unsigned char* pixels = font->ContainerAtlas->TexPixelsAlpha8; + int width = font->ContainerAtlas->TexWidth; + int height = font->ContainerAtlas->TexHeight; + int fontSize = font->Ascent; + + float scale = 0.1f; + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.isAwake = false; + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + + const char* text = "I"; + int n = (int)strlen( text ); + float zoom = 1.0f; + + float x = 0.0f; + for ( int k = 0; k < n; ++k ) + { + const ImFontGlyph* glyph = font->FindGlyph( text[k] ); + float x1 = glyph->X0; + float x2 = glyph->X1; + float y1 = glyph->Y0; + float y2 = glyph->Y1; + float u1 = glyph->U0; + float v1 = glyph->V0; + float u2 = glyph->U1; + float v2 = glyph->V1; + + float w = zoom * ( x2 - x1 ); + float h = zoom * ( y2 - y1 ); + + int gridx = int( w ); + int gridy = int( h ); + for ( int i = 0; i < gridy; ++i ) + { + float v = v1 + i / h * ( v2 - v1 ); + int iy = int( v * height ); + + for ( int j = 0; j < gridx; ++j ) + { + float u = u1 + j / w * ( u2 - u1 ); + int ix = int( u * width ); + + unsigned char value = pixels[iy * width + ix]; + if ( value > 50 ) + { + b2Polygon square = b2MakeSquare( 0.9f * scale * value / 255.0f ); + bodyDef.position = { x + 2.0f * ( zoom * x1 + j ) * scale, -2.0f * ( zoom * y1 + i ) * scale + 13.0f }; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2CreatePolygonShape( bodyId, &shapeDef, &square ); + } + } + } + + x += 2.0f * zoom * scale * glyph->AdvanceX; + } + + m_created = true; + } + + void CreateScene3() + { + float d = 0.4f; + b2Polygon box = b2MakeSquare( 0.5f * d ); + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.isAwake = false; + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + + int columns = g_sampleDebug ? 20 : 120; + int rows = g_sampleDebug ? 10 : 80; + + for ( int i = 0; i < columns; ++i ) + { + for ( int j = 0; j < rows; ++j ) + { + bodyDef.position.x = i * d + 30.0f; + bodyDef.position.y = ( j - rows / 2.0f ) * d; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + } + } + + m_created = true; + } + + void Step( Settings& settings ) override + { + Sample::Step( settings ); + + if ( m_created == false ) + { + CreateScene3(); + } + } + + static Sample* Create( Settings& settings ) + { + return new BenchmarkSmash( settings ); + } + + bool m_created; +}; + +static int sampleSmash = RegisterSample( "Benchmark", "Smash", BenchmarkSmash::Create ); + +class BenchmarkCompound : public Sample +{ +public: + explicit BenchmarkCompound( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 18.0f, 115.0f }; + g_camera.m_zoom = 25.0f * 5.5f; + } + + float grid = 1.0f; +#ifdef NDEBUG + int height = 200; + int width = 200; +#else + int height = 100; + int width = 100; +#endif + { + + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + + for ( int i = 0; i < height; ++i ) + { + float y = grid * i; + for ( int j = i; j < width; ++j ) + { + float x = grid * j; + b2Polygon square = b2MakeOffsetBox( 0.5f * grid, 0.5f * grid, { x, y }, b2Rot_identity ); + b2CreatePolygonShape( groundId, &shapeDef, &square ); + } + } + + for ( int i = 0; i < height; ++i ) + { + float y = grid * i; + for ( int j = i; j < width; ++j ) + { + float x = -grid * j; + b2Polygon square = b2MakeOffsetBox( 0.5f * grid, 0.5f * grid, { x, y }, b2Rot_identity ); + b2CreatePolygonShape( groundId, &shapeDef, &square ); + } + } + } + + { +#ifdef NDEBUG + int span = 20; + int count = 5; +#else + int span = 5; + int count = 5; +#endif + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + // defer mass properties to avoid n-squared mass computations + bodyDef.automaticMass = false; + b2ShapeDef shapeDef = b2DefaultShapeDef(); + + for ( int m = 0; m < count; ++m ) + { + float ybody = ( 100.0f + m * span ) * grid; + + for ( int n = 0; n < count; ++n ) + { + float xbody = -0.5f * grid * count * span + n * span * grid; + bodyDef.position = { xbody, ybody }; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + + for ( int i = 0; i < span; ++i ) + { + float y = i * grid; + for ( int j = 0; j < span; ++j ) + { + float x = j * grid; + b2Polygon square = b2MakeOffsetBox( 0.5f * grid, 0.5f * grid, { x, y }, b2Rot_identity ); + b2CreatePolygonShape( bodyId, &shapeDef, &square ); + } + } + + // All shapes have been added so I can efficiently compute the mass properties. + b2Body_ApplyMassFromShapes( bodyId ); + } + } + } + } + + static Sample* Create( Settings& settings ) + { + return new BenchmarkCompound( settings ); + } +}; + +static int sampleCompound = RegisterSample( "Benchmark", "Compound", BenchmarkCompound::Create ); + +class BenchmarkKinematic : public Sample +{ +public: + explicit BenchmarkKinematic( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, 0.0f }; + g_camera.m_zoom = 150.0f; + } + + float grid = 1.0f; + +#ifdef NDEBUG + int span = 100; +#else + int span = 20; +#endif + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_kinematicBody; + bodyDef.angularVelocity = 1.0f; + // defer mass properties to avoid n-squared mass computations + bodyDef.automaticMass = false; + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.filter.categoryBits = 1; + shapeDef.filter.maskBits = 2; + + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + + for ( int i = -span; i < span; ++i ) + { + float y = i * grid; + for ( int j = -span; j < span; ++j ) + { + float x = j * grid; + b2Polygon square = b2MakeOffsetBox( 0.5f * grid, 0.5f * grid, { x, y }, b2Rot_identity ); + b2CreatePolygonShape( bodyId, &shapeDef, &square ); + } + } + + // All shapes have been added so I can efficiently compute the mass properties. + b2Body_ApplyMassFromShapes( bodyId ); + } + + static Sample* Create( Settings& settings ) + { + return new BenchmarkKinematic( settings ); + } +}; + +static int sampleKinematic = RegisterSample( "Benchmark", "Kinematic", BenchmarkKinematic::Create ); diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/sample_bodies.cpp b/tests/cpp-tests/Source/Box2DTestBed/samples/sample_bodies.cpp new file mode 100644 index 000000000000..cc441a50510e --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/sample_bodies.cpp @@ -0,0 +1,839 @@ +// SPDX-FileCopyrightText: 2022 Erin Catto +// SPDX-License-Identifier: MIT + +#include "draw.h" +#include "sample.h" +#include "settings.h" + +#include "box2d/box2d.h" + +#include + +class BodyType : public Sample +{ +public: + explicit BodyType( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.8f, 6.4f }; + g_camera.m_zoom = 25.0f * 0.4f; + } + + m_type = b2_dynamicBody; + m_isEnabled = true; + + b2BodyId groundId = b2_nullBodyId; + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2Segment segment = { { -20.0f, 0.0f }, { 20.0f, 0.0f } }; + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2CreateSegmentShape( groundId, &shapeDef, &segment ); + } + + // Define attachment + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = { -2.0f, 3.0f }; + m_attachmentId = b2CreateBody( m_worldId, &bodyDef ); + + b2Polygon box = b2MakeBox( 0.5f, 2.0f ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 1.0f; + b2CreatePolygonShape( m_attachmentId, &shapeDef, &box ); + } + + // Define second attachment + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = m_type; + bodyDef.isEnabled = m_isEnabled; + bodyDef.position = { 3.0f, 3.0f }; + m_secondAttachmentId = b2CreateBody( m_worldId, &bodyDef ); + + b2Polygon box = b2MakeBox( 0.5f, 2.0f ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 1.0f; + b2CreatePolygonShape( m_secondAttachmentId, &shapeDef, &box ); + } + + // Define platform + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = m_type; + bodyDef.isEnabled = m_isEnabled; + bodyDef.position = { -4.0f, 5.0f }; + m_platformId = b2CreateBody( m_worldId, &bodyDef ); + + b2Polygon box = b2MakeOffsetBox( 0.5f, 4.0f, { 4.0f, 0.0f }, b2MakeRot(0.5f * b2_pi) ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.friction = 0.6f; + shapeDef.density = 2.0f; + b2CreatePolygonShape( m_platformId, &shapeDef, &box ); + + b2RevoluteJointDef revoluteDef = b2DefaultRevoluteJointDef(); + b2Vec2 pivot = { -2.0f, 5.0f }; + revoluteDef.bodyIdA = m_attachmentId; + revoluteDef.bodyIdB = m_platformId; + revoluteDef.localAnchorA = b2Body_GetLocalPoint( m_attachmentId, pivot ); + revoluteDef.localAnchorB = b2Body_GetLocalPoint( m_platformId, pivot ); + revoluteDef.maxMotorTorque = 50.0f; + revoluteDef.enableMotor = true; + b2CreateRevoluteJoint( m_worldId, &revoluteDef ); + + pivot = { 3.0f, 5.0f }; + revoluteDef.bodyIdA = m_secondAttachmentId; + revoluteDef.bodyIdB = m_platformId; + revoluteDef.localAnchorA = b2Body_GetLocalPoint( m_secondAttachmentId, pivot ); + revoluteDef.localAnchorB = b2Body_GetLocalPoint( m_platformId, pivot ); + revoluteDef.maxMotorTorque = 50.0f; + revoluteDef.enableMotor = true; + b2CreateRevoluteJoint( m_worldId, &revoluteDef ); + + b2PrismaticJointDef prismaticDef = b2DefaultPrismaticJointDef(); + b2Vec2 anchor = { 0.0f, 5.0f }; + prismaticDef.bodyIdA = groundId; + prismaticDef.bodyIdB = m_platformId; + prismaticDef.localAnchorA = b2Body_GetLocalPoint( groundId, anchor ); + prismaticDef.localAnchorB = b2Body_GetLocalPoint( m_platformId, anchor ); + prismaticDef.localAxisA = { 1.0f, 0.0f }; + prismaticDef.maxMotorForce = 1000.0f; + prismaticDef.motorSpeed = 0.0f; + prismaticDef.enableMotor = true; + prismaticDef.lowerTranslation = -10.0f; + prismaticDef.upperTranslation = 10.0f; + prismaticDef.enableLimit = true; + + b2CreatePrismaticJoint( m_worldId, &prismaticDef ); + + m_speed = 3.0f; + } + + // Create a payload + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = { -3.0f, 8.0f }; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + + b2Polygon box = b2MakeBox( 0.75f, 0.75f ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.friction = 0.6f; + shapeDef.density = 2.0f; + + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + } + + // Create a second payload + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = m_type; + bodyDef.isEnabled = m_isEnabled; + bodyDef.position = { 2.0f, 8.0f }; + m_secondPayloadId = b2CreateBody( m_worldId, &bodyDef ); + + b2Polygon box = b2MakeBox( 0.75f, 0.75f ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.friction = 0.6f; + shapeDef.density = 2.0f; + + b2CreatePolygonShape( m_secondPayloadId, &shapeDef, &box ); + } + + // Create a separate body on the ground + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = m_type; + bodyDef.isEnabled = m_isEnabled; + bodyDef.position = { 8.0f, 0.2f }; + m_touchingBodyId = b2CreateBody( m_worldId, &bodyDef ); + + b2Capsule capsule = { { 0.0f, 0.0f }, { 1.0f, 0.0f }, 0.25f }; + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.friction = 0.6f; + shapeDef.density = 2.0f; + + b2CreateCapsuleShape( m_touchingBodyId, &shapeDef, &capsule ); + } + + // Create a separate floating body + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = m_type; + bodyDef.isEnabled = m_isEnabled; + bodyDef.position = { -8.0f, 12.0f }; + bodyDef.gravityScale = 0.0f; + m_floatingBodyId = b2CreateBody( m_worldId, &bodyDef ); + + b2Circle circle = { { 0.0f, 0.5f }, 0.25f }; + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.friction = 0.6f; + shapeDef.density = 2.0f; + + b2CreateCircleShape( m_floatingBodyId, &shapeDef, &circle ); + } + } + + void UpdateUI() override + { + float height = 140.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 180.0f, height ) ); + ImGui::Begin( "Body Type", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize ); + + if ( ImGui::RadioButton( "Static", m_type == b2_staticBody ) ) + { + m_type = b2_staticBody; + b2Body_SetType( m_platformId, b2_staticBody ); + b2Body_SetType( m_secondAttachmentId, b2_staticBody ); + b2Body_SetType( m_secondPayloadId, b2_staticBody ); + b2Body_SetType( m_touchingBodyId, b2_staticBody ); + b2Body_SetType( m_floatingBodyId, b2_staticBody ); + } + + if ( ImGui::RadioButton( "Kinematic", m_type == b2_kinematicBody ) ) + { + m_type = b2_kinematicBody; + b2Body_SetType( m_platformId, b2_kinematicBody ); + b2Body_SetLinearVelocity( m_platformId, { -m_speed, 0.0f } ); + b2Body_SetAngularVelocity( m_platformId, 0.0f ); + b2Body_SetType( m_secondAttachmentId, b2_kinematicBody ); + b2Body_SetType( m_secondPayloadId, b2_kinematicBody ); + b2Body_SetType( m_touchingBodyId, b2_kinematicBody ); + b2Body_SetType( m_floatingBodyId, b2_kinematicBody ); + } + + if ( ImGui::RadioButton( "Dynamic", m_type == b2_dynamicBody ) ) + { + m_type = b2_dynamicBody; + b2Body_SetType( m_platformId, b2_dynamicBody ); + b2Body_SetType( m_secondAttachmentId, b2_dynamicBody ); + b2Body_SetType( m_secondPayloadId, b2_dynamicBody ); + b2Body_SetType( m_touchingBodyId, b2_dynamicBody ); + b2Body_SetType( m_floatingBodyId, b2_dynamicBody ); + } + + if ( ImGui::Checkbox( "Enable", &m_isEnabled ) ) + { + if ( m_isEnabled ) + { + b2Body_Enable( m_platformId ); + b2Body_Enable( m_secondAttachmentId ); + b2Body_Enable( m_secondPayloadId ); + b2Body_Enable( m_touchingBodyId ); + b2Body_Enable( m_floatingBodyId ); + + if ( m_type == b2_kinematicBody ) + { + b2Body_SetLinearVelocity( m_platformId, { -m_speed, 0.0f } ); + b2Body_SetAngularVelocity( m_platformId, 0.0f ); + } + } + else + { + b2Body_Disable( m_platformId ); + b2Body_Disable( m_secondAttachmentId ); + b2Body_Disable( m_secondPayloadId ); + b2Body_Disable( m_touchingBodyId ); + b2Body_Disable( m_floatingBodyId ); + } + } + + ImGui::End(); + } + + void Step( Settings& settings ) override + { + // Drive the kinematic body. + if ( m_type == b2_kinematicBody ) + { + b2Vec2 p = b2Body_GetPosition( m_platformId ); + b2Vec2 v = b2Body_GetLinearVelocity( m_platformId ); + + if ( ( p.x < -14.0f && v.x < 0.0f ) || ( p.x > 6.0f && v.x > 0.0f ) ) + { + v.x = -v.x; + b2Body_SetLinearVelocity( m_platformId, v ); + } + } + + Sample::Step( settings ); + } + + static Sample* Create( Settings& settings ) + { + return new BodyType( settings ); + } + + b2BodyId m_attachmentId; + b2BodyId m_secondAttachmentId; + b2BodyId m_platformId; + b2BodyId m_secondPayloadId; + b2BodyId m_touchingBodyId; + b2BodyId m_floatingBodyId; + b2BodyType m_type; + float m_speed; + bool m_isEnabled; +}; + +static int sampleBodyType = RegisterSample( "Bodies", "Body Type", BodyType::Create ); + +/// This is a test of typical character collision scenarios. This does not +/// show how you should implement a character in your application. +/// Instead this is used to test smooth collision on chain shapes. +class Character : public Sample +{ +public: + explicit Character( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { -2.0f, 7.0f }; + g_camera.m_zoom = 25.0f * 0.4f; + } + + // Ground body + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Segment segment = { { -20.0f, 0.0f }, { 20.0f, 0.0f } }; + b2CreateSegmentShape( groundId, &shapeDef, &segment ); + } + + // Collinear edges with no adjacency information. + // This shows the problematic case where a box shape can hit + // an internal vertex. + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Segment segment1 = { { -8.0f, 1.0f }, { -6.0f, 1.0f } }; + b2CreateSegmentShape( groundId, &shapeDef, &segment1 ); + + b2Segment segment2 = { { -6.0f, 1.0f }, { -4.0f, 1.0f } }; + b2CreateSegmentShape( groundId, &shapeDef, &segment2 ); + + b2Segment segment3 = { { -4.0f, 1.0f }, { -2.0f, 1.0f } }; + b2CreateSegmentShape( groundId, &shapeDef, &segment3 ); + } + + // Chain shape + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.rotation = b2MakeRot( 0.25f * b2_pi ); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2Vec2 points[4] = { { 8.0f, 7.0f }, { 7.0f, 8.0f }, { 6.0f, 8.0f }, { 5.0f, 7.0f } }; + b2ChainDef chainDef = b2DefaultChainDef(); + chainDef.points = points; + chainDef.count = 4; + chainDef.isLoop = true; + + b2CreateChain( groundId, &chainDef ); + } + + // Square tiles. This shows that adjacency shapes may have non-smooth collision. Box2D has no solution + // to this problem. + // todo_erin try this: https://briansemrau.github.io/dealing-with-ghost-collisions/ + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Polygon box = b2MakeOffsetBox( 1.0f, 1.0f, { 4.0f, 3.0f }, b2Rot_identity ); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + + box = b2MakeOffsetBox( 1.0f, 1.0f, { 6.0f, 3.0f }, b2Rot_identity ); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + + box = b2MakeOffsetBox( 1.0f, 1.0f, { 8.0f, 3.0f }, b2Rot_identity ); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + } + + // Square made from a chain loop. Collision should be smooth. + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2Vec2 points[4] = { { -1.0f, 3.0 }, { 1.0f, 3.0f }, { 1.0f, 5.0f }, { -1.0f, 5.0 } }; + b2ChainDef chainDef = b2DefaultChainDef(); + chainDef.points = points; + chainDef.count = 4; + chainDef.isLoop = true; + b2CreateChain( groundId, &chainDef ); + } + + // Chain loop. Collision should be smooth. + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.position = { -10.0f, 4.0f }; + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2Vec2 points[10] = { { 0.0f, 0.0f }, { 6.0f, 0.0f }, { 6.0f, 2.0f }, { 4.0f, 1.0f }, { 2.0f, 2.0f }, + { 0.0f, 2.0f }, { -2.0f, 2.0f }, { -4.0f, 3.0f }, { -6.0f, 2.0f }, { -6.0f, 0.0f } }; + b2ChainDef chainDef = b2DefaultChainDef(); + chainDef.points = points; + chainDef.count = 10; + chainDef.isLoop = true; + b2CreateChain( groundId, &chainDef ); + } + + // Circle character + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.position = { -7.0f, 6.0f }; + bodyDef.type = b2_dynamicBody; + bodyDef.fixedRotation = true; + bodyDef.enableSleep = false; + + m_circleCharacterId = b2CreateBody( m_worldId, &bodyDef ); + + b2Circle circle = { { 0.0f, 0.0f }, 0.25f }; + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 20.0f; + shapeDef.friction = 0.2f; + b2CreateCircleShape( m_circleCharacterId, &shapeDef, &circle ); + } + + // Capsule character + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.position = { 3.0f, 5.0f }; + bodyDef.type = b2_dynamicBody; + bodyDef.fixedRotation = true; + bodyDef.enableSleep = false; + + m_capsuleCharacterId = b2CreateBody( m_worldId, &bodyDef ); + + b2Capsule capsule = { { 0.0f, 0.25f }, { 0.0f, 0.75f }, 0.25f }; + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 20.0f; + shapeDef.friction = 0.2f; + b2CreateCapsuleShape( m_capsuleCharacterId, &shapeDef, &capsule ); + } + + // Square character + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.position = { -3.0f, 8.0f }; + bodyDef.type = b2_dynamicBody; + bodyDef.fixedRotation = true; + bodyDef.enableSleep = false; + + m_boxCharacterId = b2CreateBody( m_worldId, &bodyDef ); + + b2Polygon box = b2MakeBox( 0.4f, 0.4f ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 20.0f; + shapeDef.friction = 0.2f; + b2CreatePolygonShape( m_boxCharacterId, &shapeDef, &box ); + } + } + + void Step( Settings& settings ) override + { + Sample::Step( settings ); + + g_draw.DrawString( 5, m_textLine, "This tests various character collision shapes." ); + m_textLine += m_textIncrement; + g_draw.DrawString( 5, m_textLine, "Limitation: square and hexagon can snag on aligned boxes." ); + m_textLine += m_textIncrement; + } + + static Sample* Create( Settings& settings ) + { + return new Character( settings ); + } + + b2BodyId m_circleCharacterId; + b2BodyId m_capsuleCharacterId; + b2BodyId m_boxCharacterId; +}; + +static int sampleCharacter = RegisterSample( "Bodies", "Character", Character::Create ); + +class Weeble : public Sample +{ +public: + explicit Weeble( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 2.3f, 10.0f }; + g_camera.m_zoom = 25.0f * 0.5f; + } + + b2BodyId groundId = b2_nullBodyId; + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2Segment segment = { { -20.0f, 0.0f }, { 20.0f, 0.0f } }; + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2CreateSegmentShape( groundId, &shapeDef, &segment ); + } + + // Build weeble + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = { 0.0f, 3.0f }; + bodyDef.rotation = b2MakeRot( 0.25f * b2_pi ); + m_weebleId = b2CreateBody( m_worldId, &bodyDef ); + + b2Capsule capsule = { { 0.0f, -1.0f }, { 0.0f, 1.0f }, 1.0f }; + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 1.0f; + b2CreateCapsuleShape( m_weebleId, &shapeDef, &capsule ); + + float mass = b2Body_GetMass( m_weebleId ); + float inertiaTensor = b2Body_GetRotationalInertia( m_weebleId ); + + float offset = 1.5f; + + // See: https://en.wikipedia.org/wiki/Parallel_axis_theorem + inertiaTensor += mass * offset * offset; + + b2MassData massData = { mass, { 0.0f, -offset }, inertiaTensor }; + b2Body_SetMassData( m_weebleId, massData ); + } + + m_explosionPosition = { 0.0f, 0.0f }; + m_explosionRadius = 2.0f; + m_explosionMagnitude = 8.0f; + } + + void UpdateUI() override + { + float height = 120.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 200.0f, height ) ); + ImGui::Begin( "Weeble", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize ); + if ( ImGui::Button( "Teleport" ) ) + { + b2Body_SetTransform( m_weebleId, { 0.0f, 5.0f }, b2MakeRot( 0.95 * b2_pi ) ); + } + + if ( ImGui::Button( "Explode" ) ) + { + b2World_Explode( m_worldId, m_explosionPosition, m_explosionRadius, m_explosionMagnitude ); + } + ImGui::PushItemWidth( 100.0f ); + + ImGui::SliderFloat( "Magnitude", &m_explosionMagnitude, -100.0f, 100.0f, "%.1f" ); + + ImGui::PopItemWidth(); + ImGui::End(); + } + + void Step( Settings& settings ) override + { + Sample::Step( settings ); + + g_draw.DrawCircle( m_explosionPosition, m_explosionRadius, b2_colorAzure ); + } + + static Sample* Create( Settings& settings ) + { + return new Weeble( settings ); + } + + b2BodyId m_weebleId; + b2Vec2 m_explosionPosition; + float m_explosionRadius; + float m_explosionMagnitude; +}; + +static int sampleWeeble = RegisterSample( "Bodies", "Weeble", Weeble::Create ); + +class Sleep : public Sample +{ +public: + explicit Sleep( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 3.0f, 50.0f }; + g_camera.m_zoom = 25.0f * 2.2f; + } + + b2BodyId groundId = b2_nullBodyId; + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2Segment segment = { { -20.0f, 0.0f }, { 20.0f, 0.0f } }; + b2ShapeDef shapeDef = b2DefaultShapeDef(); + m_groundShapeId = b2CreateSegmentShape( groundId, &shapeDef, &segment ); + } + + // Sleeping body with sensors + for ( int i = 0; i < 2; ++i ) + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = { -4.0f, 3.0f + 2.0f * i }; + bodyDef.isAwake = false; + bodyDef.enableSleep = true; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + + b2Capsule capsule = { { 0.0f, 1.0f }, { 1.0f, 1.0f }, 0.75f }; + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2CreateCapsuleShape( bodyId, &shapeDef, &capsule ); + + shapeDef.isSensor = true; + capsule.radius = 1.0f; + m_sensorIds[i] = b2CreateCapsuleShape( bodyId, &shapeDef, &capsule ); + m_sensorTouching[i] = false; + } + + // Sleeping body but sleep is disabled + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = { 0.0f, 3.0f }; + bodyDef.isAwake = false; + bodyDef.enableSleep = false; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + + b2Circle circle = { { 1.0f, 1.0f }, 1.0f }; + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2CreateCircleShape( bodyId, &shapeDef, &circle ); + } + + // Awake body and sleep is disabled + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = { 5.0f, 3.0f }; + bodyDef.isAwake = true; + bodyDef.enableSleep = false; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + + b2Polygon box = b2MakeOffsetBox( 1.0f, 1.0f, { 0.0f, 1.0f }, b2MakeRot(0.25f * b2_pi) ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + } + + // A sleeping body to test waking on collision + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = { 5.0f, 1.0f }; + bodyDef.isAwake = false; + bodyDef.enableSleep = true; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + + b2Polygon box = b2MakeSquare( 1.0f ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + } + + // A long pendulum + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = { 0.0f, 100.0f }; + bodyDef.angularDamping = 0.5f; + bodyDef.sleepThreshold = 0.05f; + m_pendulumId = b2CreateBody( m_worldId, &bodyDef ); + + b2Capsule capsule = { { 0.0f, 0.0f }, { 90.0f, 0.0f }, 0.25f }; + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2CreateCapsuleShape( m_pendulumId, &shapeDef, &capsule ); + + b2Vec2 pivot = bodyDef.position; + b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef(); + jointDef.bodyIdA = groundId; + jointDef.bodyIdB = m_pendulumId; + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); + b2CreateRevoluteJoint( m_worldId, &jointDef ); + } + } + + void UpdateUI() override + { + float height = 100.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 240.0f, height ) ); + ImGui::Begin( "Sleep", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize ); + + ImGui::PushItemWidth( 120.0f ); + + ImGui::Text( "Pendulum Tuning" ); + + float sleepVelocity = b2Body_GetSleepThreshold( m_pendulumId ); + if ( ImGui::SliderFloat( "sleep velocity", &sleepVelocity, 0.0f, 1.0f, "%.2f" ) ) + { + b2Body_SetSleepThreshold( m_pendulumId, sleepVelocity ); + b2Body_SetAwake( m_pendulumId, true ); + } + + float angularDamping = b2Body_GetAngularDamping( m_pendulumId ); + if ( ImGui::SliderFloat( "angular damping", &angularDamping, 0.0f, 2.0f, "%.2f" ) ) + { + b2Body_SetAngularDamping( m_pendulumId, angularDamping ); + } + + ImGui::PopItemWidth(); + + ImGui::End(); + } + + void Step( Settings& settings ) override + { + Sample::Step( settings ); + + // Detect sensors touching the ground + b2SensorEvents sensorEvents = b2World_GetSensorEvents( m_worldId ); + + for ( int i = 0; i < sensorEvents.beginCount; ++i ) + { + b2SensorBeginTouchEvent* event = sensorEvents.beginEvents + i; + if ( B2_ID_EQUALS( event->visitorShapeId, m_groundShapeId ) ) + { + if ( B2_ID_EQUALS( event->sensorShapeId, m_sensorIds[0] ) ) + { + m_sensorTouching[0] = true; + } + else if ( B2_ID_EQUALS( event->sensorShapeId, m_sensorIds[1] ) ) + { + m_sensorTouching[1] = true; + } + } + } + + for ( int i = 0; i < sensorEvents.endCount; ++i ) + { + b2SensorEndTouchEvent* event = sensorEvents.endEvents + i; + if ( B2_ID_EQUALS( event->visitorShapeId, m_groundShapeId ) ) + { + if ( B2_ID_EQUALS( event->sensorShapeId, m_sensorIds[0] ) ) + { + m_sensorTouching[0] = false; + } + else if ( B2_ID_EQUALS( event->sensorShapeId, m_sensorIds[1] ) ) + { + m_sensorTouching[1] = false; + } + } + } + + for ( int i = 0; i < 2; ++i ) + { + g_draw.DrawString( 5, m_textLine, "sensor touch %d = %s", i, m_sensorTouching[i] ? "true" : "false" ); + m_textLine += m_textIncrement; + } + } + + static Sample* Create( Settings& settings ) + { + return new Sleep( settings ); + } + + b2BodyId m_pendulumId; + b2ShapeId m_groundShapeId; + b2ShapeId m_sensorIds[2]; + bool m_sensorTouching[2]; +}; + +static int sampleSleep = RegisterSample( "Bodies", "Sleep", Sleep::Create ); + +class BadBody : public Sample +{ +public: + explicit BadBody( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 2.3f, 10.0f }; + g_camera.m_zoom = 25.0f * 0.5f; + } + + b2BodyId groundId = b2_nullBodyId; + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2Segment segment = { { -20.0f, 0.0f }, { 20.0f, 0.0f } }; + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2CreateSegmentShape( groundId, &shapeDef, &segment ); + } + + // Build a bad body + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = { 0.0f, 3.0f }; + bodyDef.angularVelocity = 0.5f; + bodyDef.rotation = b2MakeRot( 0.25f * b2_pi ); + + m_badBodyId = b2CreateBody( m_worldId, &bodyDef ); + + b2Capsule capsule = { { 0.0f, -1.0f }, { 0.0f, 1.0f }, 1.0f }; + b2ShapeDef shapeDef = b2DefaultShapeDef(); + + // density set to zero intentionally to create a bad body + shapeDef.density = 0.0f; + b2CreateCapsuleShape( m_badBodyId, &shapeDef, &capsule ); + } + + // Build a normal body + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = { 2.0f, 3.0f }; + bodyDef.rotation = b2MakeRot( 0.25f * b2_pi ); + + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + + b2Capsule capsule = { { 0.0f, -1.0f }, { 0.0f, 1.0f }, 1.0f }; + b2ShapeDef shapeDef = b2DefaultShapeDef(); + + b2CreateCapsuleShape( bodyId, &shapeDef, &capsule ); + } + } + + void Step( Settings& settings ) override + { + Sample::Step( settings ); + + g_draw.DrawString( 5, m_textLine, "A bad body is a dynamic body with no mass and behaves like a kinematic body." ); + m_textLine += m_textIncrement; + + g_draw.DrawString( 5, m_textLine, "Bad bodies are considered invalid and a user bug. Behavior is not guaranteed." ); + m_textLine += m_textIncrement; + + // For science + b2Body_ApplyForceToCenter( m_badBodyId, { 0.0f, 10.0f }, true ); + } + + static Sample* Create( Settings& settings ) + { + return new BadBody( settings ); + } + + b2BodyId m_badBodyId; +}; + +static int sampleBadBody = RegisterSample( "Bodies", "Bad", BadBody::Create ); diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/sample_collision.cpp b/tests/cpp-tests/Source/Box2DTestBed/samples/sample_collision.cpp new file mode 100644 index 000000000000..9241152e40f4 --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/sample_collision.cpp @@ -0,0 +1,3478 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#include "draw.h" +#include "sample.h" +#include "settings.h" + +#include "box2d/box2d.h" +#include "box2d/collision.h" +#include "box2d/math_functions.h" + +#include +#include + +constexpr int SIMPLEX_CAPACITY = 20; + +class ShapeDistance : public Sample +{ +public: + enum ShapeType + { + e_point, + e_segment, + e_triangle, + e_box + }; + + explicit ShapeDistance( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, 0.0f }; + g_camera.m_zoom = 3.0f; + } + + m_point = b2Vec2_zero; + m_segment = { { -0.5f, 0.0f }, { 0.5f, 0.0f } }; + + { + b2Vec2 points[3] = { { -0.5f, 0.0f }, { 0.5f, 0.0f }, { 0.0f, 1.0f } }; + b2Hull hull = b2ComputeHull( points, 3 ); + m_triangle = b2MakePolygon( &hull, 0.0f ); + } + + m_box = b2MakeBox( 0.5f, 0.5f ); + + m_transform = { { 1.5f, -1.5f }, b2Rot_identity }; + m_angle = 0.0f; + + m_cache = b2_emptyDistanceCache; + m_simplexCount = 0; + m_startPoint = { 0.0f, 0.0f }; + m_basePosition = { 0.0f, 0.0f }; + m_baseAngle = 0.0f; + + m_dragging = false; + m_rotating = false; + m_showIndices = false; + m_useCache = false; + m_drawSimplex = false; + + m_typeA = e_box; + m_typeB = e_box; + m_radiusA = 0.0f; + m_radiusB = 0.0f; + + m_proxyA = MakeProxy( m_typeA, m_radiusA ); + m_proxyB = MakeProxy( m_typeB, m_radiusB ); + } + + b2DistanceProxy MakeProxy( ShapeType type, float radius ) + { + b2DistanceProxy proxy = {}; + proxy.radius = radius; + + switch ( type ) + { + case e_point: + proxy.points[0] = b2Vec2_zero; + proxy.count = 1; + break; + + case e_segment: + proxy.points[0] = m_segment.point1; + proxy.points[1] = m_segment.point2; + proxy.count = 2; + break; + + case e_triangle: + proxy.points[0] = m_triangle.vertices[0]; + proxy.points[1] = m_triangle.vertices[1]; + proxy.points[2] = m_triangle.vertices[2]; + proxy.count = 3; + break; + + case e_box: + proxy.points[0] = m_box.vertices[0]; + proxy.points[1] = m_box.vertices[1]; + proxy.points[2] = m_box.vertices[2]; + proxy.points[3] = m_box.vertices[3]; + proxy.count = 4; + break; + + default: + assert( false ); + } + + return proxy; + } + + void DrawShape( ShapeType type, b2Transform transform, float radius, b2HexColor color ) + { + switch ( type ) + { + case e_point: + { + b2Vec2 p = b2TransformPoint( transform, m_point ); + if ( radius > 0.0f ) + { + g_draw.DrawSolidCircle( transform, m_point, radius, color ); + } + else + { + g_draw.DrawPoint( p, 5.0f, color ); + } + } + break; + + case e_segment: + { + b2Vec2 p1 = b2TransformPoint( transform, m_segment.point1 ); + b2Vec2 p2 = b2TransformPoint( transform, m_segment.point2 ); + + if ( radius > 0.0f ) + { + g_draw.DrawSolidCapsule( p1, p2, radius, color ); + } + else + { + g_draw.DrawSegment( p1, p2, color ); + } + } + break; + + case e_triangle: + g_draw.DrawSolidPolygon( transform, m_triangle.vertices, 3, radius, color ); + break; + + case e_box: + g_draw.DrawSolidPolygon( transform, m_box.vertices, 4, radius, color ); + break; + + default: + assert( false ); + } + } + + void UpdateUI() override + { + float height = 310.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 240.0f, height ) ); + + ImGui::Begin( "Shape Distance", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize ); + + const char* shapeTypes[] = { "point", "segment", "triangle", "box" }; + int shapeType = int( m_typeA ); + if ( ImGui::Combo( "shape A", &shapeType, shapeTypes, IM_ARRAYSIZE( shapeTypes ) ) ) + { + m_typeA = ShapeType( shapeType ); + m_proxyA = MakeProxy( m_typeA, m_radiusA ); + } + + if ( ImGui::SliderFloat( "radius A", &m_radiusA, 0.0f, 0.5f, "%.2f" ) ) + { + m_proxyA.radius = m_radiusA; + } + + shapeType = int( m_typeB ); + if ( ImGui::Combo( "shape B", &shapeType, shapeTypes, IM_ARRAYSIZE( shapeTypes ) ) ) + { + m_typeB = ShapeType( shapeType ); + m_proxyB = MakeProxy( m_typeB, m_radiusB ); + } + + if ( ImGui::SliderFloat( "radius B", &m_radiusB, 0.0f, 0.5f, "%.2f" ) ) + { + m_proxyB.radius = m_radiusB; + } + + ImGui::Separator(); + + ImGui::SliderFloat( "x offset", &m_transform.p.x, -2.0f, 2.0f, "%.2f" ); + ImGui::SliderFloat( "y offset", &m_transform.p.y, -2.0f, 2.0f, "%.2f" ); + + if ( ImGui::SliderFloat( "angle", &m_angle, -b2_pi, b2_pi, "%.2f" ) ) + { + m_transform.q = b2MakeRot( m_angle ); + } + + ImGui::Separator(); + + ImGui::Checkbox( "show indices", &m_showIndices ); + ImGui::Checkbox( "use cache", &m_useCache ); + + ImGui::Separator(); + + if ( ImGui::Checkbox( "draw simplex", &m_drawSimplex ) ) + { + m_simplexIndex = 0; + } + + if ( m_drawSimplex ) + { + ImGui::SliderInt( "index", &m_simplexIndex, 0, m_simplexCount - 1 ); + m_simplexIndex = b2ClampInt( m_simplexIndex, 0, m_simplexCount - 1 ); + } + + ImGui::End(); + } + + void MouseDown( b2Vec2 p, int button, int mods ) override + { + if ( button == GLFW_MOUSE_BUTTON_1 ) + { + if ( mods == 0 && m_rotating == false ) + { + m_dragging = true; + m_startPoint = p; + m_basePosition = m_transform.p; + } + else if ( mods == GLFW_MOD_SHIFT && m_dragging == false ) + { + m_rotating = true; + m_startPoint = p; + m_baseAngle = m_angle; + } + } + } + + void MouseUp( b2Vec2, int button ) override + { + if ( button == GLFW_MOUSE_BUTTON_1 ) + { + m_dragging = false; + m_rotating = false; + } + } + + void MouseMove( b2Vec2 p ) override + { + if ( m_dragging ) + { + m_transform.p.x = m_basePosition.x + 0.5f * ( p.x - m_startPoint.x ); + m_transform.p.y = m_basePosition.y + 0.5f * ( p.y - m_startPoint.y ); + } + else if ( m_rotating ) + { + float dx = p.x - m_startPoint.x; + m_angle = b2ClampFloat( m_baseAngle + 1.0f * dx, -b2_pi, b2_pi ); + m_transform.q = b2MakeRot( m_angle ); + } + } + + static b2Vec2 Weight2( float a1, b2Vec2 w1, float a2, b2Vec2 w2 ) + { + return { a1 * w1.x + a2 * w2.x, a1 * w1.y + a2 * w2.y }; + } + + static b2Vec2 Weight3( float a1, b2Vec2 w1, float a2, b2Vec2 w2, float a3, b2Vec2 w3 ) + { + return { a1 * w1.x + a2 * w2.x + a3 * w3.x, a1 * w1.y + a2 * w2.y + a3 * w3.y }; + } + + void ComputeSimplexWitnessPoints( b2Vec2* a, b2Vec2* b, const b2Simplex* s ) + { + switch ( s->count ) + { + case 0: + assert( false ); + break; + + case 1: + *a = s->v1.wA; + *b = s->v1.wB; + break; + + case 2: + *a = Weight2( s->v1.a, s->v1.wA, s->v2.a, s->v2.wA ); + *b = Weight2( s->v1.a, s->v1.wB, s->v2.a, s->v2.wB ); + break; + + case 3: + *a = Weight3( s->v1.a, s->v1.wA, s->v2.a, s->v2.wA, s->v3.a, s->v3.wA ); + *b = *a; + break; + + default: + assert( false ); + break; + } + } + + void Step( Settings& ) override + { + b2DistanceInput input; + input.proxyA = m_proxyA; + input.proxyB = m_proxyB; + input.transformA = b2Transform_identity; + input.transformB = m_transform; + input.useRadii = m_radiusA > 0.0f || m_radiusB > 0.0f; + + if ( m_useCache == false ) + { + m_cache.count = 0; + } + + b2DistanceOutput output = b2ShapeDistance( &m_cache, &input, m_simplexes, SIMPLEX_CAPACITY ); + + m_simplexCount = output.simplexCount; + + DrawShape( m_typeA, b2Transform_identity, m_radiusA, b2_colorCyan ); + DrawShape( m_typeB, m_transform, m_radiusB, b2_colorBisque ); + + if ( m_drawSimplex ) + { + b2Simplex* simplex = m_simplexes + m_simplexIndex; + b2SimplexVertex* vertices[3] = { &simplex->v1, &simplex->v2, &simplex->v3 }; + + if ( m_simplexIndex > 0 ) + { + // The first recorded simplex does not have valid barycentric coordinates + b2Vec2 pointA, pointB; + ComputeSimplexWitnessPoints( &pointA, &pointB, simplex ); + + g_draw.DrawSegment( pointA, pointB, b2_colorWhite ); + g_draw.DrawPoint( pointA, 5.0f, b2_colorWhite ); + g_draw.DrawPoint( pointB, 5.0f, b2_colorWhite ); + } + + b2HexColor colors[3] = { b2_colorRed, b2_colorGreen, b2_colorBlue }; + + for ( int i = 0; i < simplex->count; ++i ) + { + b2SimplexVertex* vertex = vertices[i]; + g_draw.DrawPoint( vertex->wA, 5.0f, colors[i] ); + g_draw.DrawPoint( vertex->wB, 5.0f, colors[i] ); + } + } + else + { + g_draw.DrawSegment( output.pointA, output.pointB, b2_colorWhite ); + g_draw.DrawPoint( output.pointA, 5.0f, b2_colorWhite ); + g_draw.DrawPoint( output.pointB, 5.0f, b2_colorWhite ); + } + + if ( m_showIndices ) + { + for ( int i = 0; i < m_proxyA.count; ++i ) + { + b2Vec2 p = m_proxyA.points[i]; + g_draw.DrawString( p, " %d", i ); + } + + for ( int i = 0; i < m_proxyB.count; ++i ) + { + b2Vec2 p = b2TransformPoint( m_transform, m_proxyB.points[i] ); + g_draw.DrawString( p, " %d", i ); + } + } + + g_draw.DrawString( 5, m_textLine, "mouse button 1: drag" ); + m_textLine += m_textIncrement; + g_draw.DrawString( 5, m_textLine, "mouse button 1 + shift: rotate" ); + m_textLine += m_textIncrement; + g_draw.DrawString( 5, m_textLine, "distance = %.2f, iterations = %d", output.distance, output.iterations ); + m_textLine += m_textIncrement; + + if ( m_cache.count == 1 ) + { + g_draw.DrawString( 5, m_textLine, "cache = {%d}, {%d}", m_cache.indexA[0], m_cache.indexB[0] ); + } + else if ( m_cache.count == 2 ) + { + g_draw.DrawString( 5, m_textLine, "cache = {%d, %d}, {%d, %d}", m_cache.indexA[0], m_cache.indexA[1], + m_cache.indexB[0], m_cache.indexB[1] ); + } + else if ( m_cache.count == 3 ) + { + g_draw.DrawString( 5, m_textLine, "cache = {%d, %d, %d}, {%d, %d, %d}", m_cache.indexA[0], m_cache.indexA[1], + m_cache.indexA[2], m_cache.indexB[0], m_cache.indexB[1], m_cache.indexB[2] ); + } + m_textLine += m_textIncrement; + } + + static Sample* Create( Settings& settings ) + { + return new ShapeDistance( settings ); + } + + b2Polygon m_box; + b2Polygon m_triangle; + b2Vec2 m_point; + b2Segment m_segment; + + ShapeType m_typeA; + ShapeType m_typeB; + float m_radiusA; + float m_radiusB; + b2DistanceProxy m_proxyA; + b2DistanceProxy m_proxyB; + + b2DistanceCache m_cache; + b2Simplex m_simplexes[SIMPLEX_CAPACITY]; + int m_simplexCount; + int m_simplexIndex; + + b2Transform m_transform; + float m_angle; + + b2Vec2 m_basePosition; + b2Vec2 m_startPoint; + float m_baseAngle; + + bool m_dragging; + bool m_rotating; + bool m_showIndices; + bool m_useCache; + bool m_drawSimplex; +}; + +static int sampleShapeDistance = RegisterSample( "Collision", "Shape Distance", ShapeDistance::Create ); + +enum UpdateType +{ + Update_Incremental = 0, + Update_FullRebuild = 1, + Update_PartialRebuild = 2, +}; + +struct Proxy +{ + b2AABB box; + b2AABB fatBox; + b2Vec2 position; + b2Vec2 width; + int proxyId; + int rayStamp; + int queryStamp; + bool moved; +}; + +static bool QueryCallback( int32_t proxyId, int32_t userData, void* context ); +static float RayCallback( const b2RayCastInput* input, int32_t proxyId, int32_t userData, void* context ); + +// Tests the Box2D bounding volume hierarchy (BVH). The dynamic tree +// can be used independently as a spatial data structure. +class DynamicTree : public Sample +{ +public: + explicit DynamicTree( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 500.0f, 500.0f }; + g_camera.m_zoom = 25.0f * 21.0f; + } + + m_fill = 0.25f; + m_moveFraction = 0.05f; + m_moveDelta = 0.1f; + m_proxies = nullptr; + m_proxyCount = 0; + m_proxyCapacity = 0; + m_ratio = 5.0f; + m_grid = 1.0f; + + m_moveBuffer = nullptr; + m_moveCount = 0; + + m_rowCount = g_sampleDebug ? 100 : 1000; + m_columnCount = g_sampleDebug ? 100 : 1000; + memset( &m_tree, 0, sizeof( m_tree ) ); + BuildTree(); + m_timeStamp = 0; + m_updateType = Update_Incremental; + + m_startPoint = { 0.0f, 0.0f }; + m_endPoint = { 0.0f, 0.0f }; + m_queryDrag = false; + m_rayDrag = false; + m_validate = true; + } + + ~DynamicTree() override + { + free( m_proxies ); + free( m_moveBuffer ); + b2DynamicTree_Destroy( &m_tree ); + } + + void BuildTree() + { + b2DynamicTree_Destroy( &m_tree ); + free( m_proxies ); + free( m_moveBuffer ); + + m_proxyCapacity = m_rowCount * m_columnCount; + m_proxies = static_cast( malloc( m_proxyCapacity * sizeof( Proxy ) ) ); + m_proxyCount = 0; + + m_moveBuffer = static_cast( malloc( m_proxyCapacity * sizeof( int ) ) ); + m_moveCount = 0; + + float y = -4.0f; + + bool isStatic = false; + m_tree = b2DynamicTree_Create(); + + const b2Vec2 aabbMargin = { 0.1f, 0.1f }; + + for ( int i = 0; i < m_rowCount; ++i ) + { + float x = -40.0f; + + for ( int j = 0; j < m_columnCount; ++j ) + { + float fillTest = RandomFloat( 0.0f, 1.0f ); + if ( fillTest <= m_fill ) + { + assert( m_proxyCount <= m_proxyCapacity ); + Proxy* p = m_proxies + m_proxyCount; + p->position = { x, y }; + + float ratio = RandomFloat( 1.0f, m_ratio ); + float width = RandomFloat( 0.1f, 0.5f ); + if ( RandomFloat() > 0.0f ) + { + p->width.x = ratio * width; + p->width.y = width; + } + else + { + p->width.x = width; + p->width.y = ratio * width; + } + + p->box.lowerBound = { x, y }; + p->box.upperBound = { x + p->width.x, y + p->width.y }; + p->fatBox.lowerBound = b2Sub( p->box.lowerBound, aabbMargin ); + p->fatBox.upperBound = b2Add( p->box.upperBound, aabbMargin ); + + p->proxyId = b2DynamicTree_CreateProxy( &m_tree, p->fatBox, b2_defaultCategoryBits, m_proxyCount ); + p->rayStamp = -1; + p->queryStamp = -1; + p->moved = false; + ++m_proxyCount; + } + + x += m_grid; + } + + y += m_grid; + } + } + + void UpdateUI() override + { + float height = 320.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 200.0f, height ) ); + + ImGui::Begin( "Dynamic Tree", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize ); + + ImGui::PushItemWidth( 100.0f ); + + bool changed = false; + if ( ImGui::SliderInt( "rows", &m_rowCount, 0, 1000, "%d" ) ) + { + changed = true; + } + + if ( ImGui::SliderInt( "columns", &m_columnCount, 0, 1000, "%d" ) ) + { + changed = true; + } + + if ( ImGui::SliderFloat( "fill", &m_fill, 0.0f, 1.0f, "%.2f" ) ) + { + changed = true; + } + + if ( ImGui::SliderFloat( "grid", &m_grid, 0.5f, 2.0f, "%.2f" ) ) + { + changed = true; + } + + if ( ImGui::SliderFloat( "ratio", &m_ratio, 1.0f, 10.0f, "%.2f" ) ) + { + changed = true; + } + + if ( ImGui::SliderFloat( "move", &m_moveFraction, 0.0f, 1.0f, "%.2f" ) ) + { + } + + if ( ImGui::SliderFloat( "delta", &m_moveDelta, 0.0f, 1.0f, "%.2f" ) ) + { + } + + if ( ImGui::RadioButton( "Incremental", m_updateType == Update_Incremental ) ) + { + m_updateType = Update_Incremental; + changed = true; + } + + if ( ImGui::RadioButton( "Full Rebuild", m_updateType == Update_FullRebuild ) ) + { + m_updateType = Update_FullRebuild; + changed = true; + } + + if ( ImGui::RadioButton( "Partial Rebuild", m_updateType == Update_PartialRebuild ) ) + { + m_updateType = Update_PartialRebuild; + changed = true; + } + + ImGui::Separator(); + + ImGui::Text( "mouse button 1: ray cast" ); + ImGui::Text( "mouse button 1 + shift: query" ); + + ImGui::PopItemWidth(); + ImGui::End(); + + if ( changed ) + { + BuildTree(); + } + } + + void MouseDown( b2Vec2 p, int button, int mods ) override + { + if ( button == GLFW_MOUSE_BUTTON_1 ) + { + if ( mods == 0 && m_queryDrag == false ) + { + m_rayDrag = true; + m_startPoint = p; + m_endPoint = p; + } + else if ( mods == GLFW_MOD_SHIFT && m_rayDrag == false ) + { + m_queryDrag = true; + m_startPoint = p; + m_endPoint = p; + } + } + } + + void MouseUp( b2Vec2, int button ) override + { + if ( button == GLFW_MOUSE_BUTTON_1 ) + { + m_queryDrag = false; + m_rayDrag = false; + } + } + + void MouseMove( b2Vec2 p ) override + { + m_endPoint = p; + } + + void Step( Settings& ) override + { + if ( m_queryDrag ) + { + b2AABB box = { b2Min( m_startPoint, m_endPoint ), b2Max( m_startPoint, m_endPoint ) }; + b2DynamicTree_Query( &m_tree, box, b2_defaultMaskBits, QueryCallback, this ); + + g_draw.DrawAABB( box, b2_colorWhite ); + } + + // m_startPoint = {-1.0f, 0.5f}; + // m_endPoint = {7.0f, 0.5f}; + + if ( m_rayDrag ) + { + b2RayCastInput input = { m_startPoint, b2Sub( m_endPoint, m_startPoint ), 1.0f }; + b2DynamicTree_RayCast( &m_tree, &input, b2_defaultMaskBits, RayCallback, this ); + + g_draw.DrawSegment( m_startPoint, m_endPoint, b2_colorWhite ); + g_draw.DrawPoint( m_startPoint, 5.0f, b2_colorGreen ); + g_draw.DrawPoint( m_endPoint, 5.0f, b2_colorRed ); + } + + b2HexColor c = b2_colorBlue; + b2HexColor qc = b2_colorGreen; + + const b2Vec2 aabbMargin = { 0.1f, 0.1f }; + + for ( int i = 0; i < m_proxyCount; ++i ) + { + Proxy* p = m_proxies + i; + + if ( p->queryStamp == m_timeStamp || p->rayStamp == m_timeStamp ) + { + g_draw.DrawAABB( p->box, qc ); + } + else + { + g_draw.DrawAABB( p->box, c ); + } + + float moveTest = RandomFloat( 0.0f, 1.0f ); + if ( m_moveFraction > moveTest ) + { + float dx = m_moveDelta * RandomFloat(); + float dy = m_moveDelta * RandomFloat(); + + p->position.x += dx; + p->position.y += dy; + + p->box.lowerBound.x = p->position.x + dx; + p->box.lowerBound.y = p->position.y + dy; + p->box.upperBound.x = p->position.x + dx + p->width.x; + p->box.upperBound.y = p->position.y + dy + p->width.y; + + if ( b2AABB_Contains( p->fatBox, p->box ) == false ) + { + p->fatBox.lowerBound = b2Sub( p->box.lowerBound, aabbMargin ); + p->fatBox.upperBound = b2Add( p->box.upperBound, aabbMargin ); + p->moved = true; + } + else + { + p->moved = false; + } + } + else + { + p->moved = false; + } + } + + switch ( m_updateType ) + { + case Update_Incremental: + { + b2Timer timer = b2CreateTimer(); + for ( int i = 0; i < m_proxyCount; ++i ) + { + Proxy* p = m_proxies + i; + if ( p->moved ) + { + b2DynamicTree_MoveProxy( &m_tree, p->proxyId, p->fatBox ); + } + } + float ms = b2GetMilliseconds( &timer ); + g_draw.DrawString( 5, m_textLine, "incremental : %.3f ms", ms ); + m_textLine += m_textIncrement; + } + break; + + case Update_FullRebuild: + { + for ( int i = 0; i < m_proxyCount; ++i ) + { + Proxy* p = m_proxies + i; + if ( p->moved ) + { + b2DynamicTree_EnlargeProxy( &m_tree, p->proxyId, p->fatBox ); + } + } + + b2Timer timer = b2CreateTimer(); + int boxCount = b2DynamicTree_Rebuild( &m_tree, true ); + float ms = b2GetMilliseconds( &timer ); + g_draw.DrawString( 5, m_textLine, "full build %d : %.3f ms", boxCount, ms ); + m_textLine += m_textIncrement; + } + break; + + case Update_PartialRebuild: + { + for ( int i = 0; i < m_proxyCount; ++i ) + { + Proxy* p = m_proxies + i; + if ( p->moved ) + { + b2DynamicTree_EnlargeProxy( &m_tree, p->proxyId, p->fatBox ); + } + } + + b2Timer timer = b2CreateTimer(); + int boxCount = b2DynamicTree_Rebuild( &m_tree, false ); + float ms = b2GetMilliseconds( &timer ); + g_draw.DrawString( 5, m_textLine, "partial rebuild %d : %.3f ms", boxCount, ms ); + m_textLine += m_textIncrement; + } + break; + + default: + break; + } + + int height = b2DynamicTree_GetHeight( &m_tree ); + float areaRatio = b2DynamicTree_GetAreaRatio( &m_tree ); + + int hmin = (int)( ceilf( logf( (float)m_proxyCount ) / logf( 2.0f ) - 1.0f ) ); + g_draw.DrawString( 5, m_textLine, "proxies = %d, height = %d, hmin = %d, area ratio = %.1f", m_proxyCount, height, hmin, + areaRatio ); + m_textLine += m_textIncrement; + + b2DynamicTree_Validate( &m_tree ); + + m_timeStamp += 1; + } + + static Sample* Create( Settings& settings ) + { + return new DynamicTree( settings ); + } + + b2DynamicTree m_tree; + int m_rowCount, m_columnCount; + Proxy* m_proxies; + int* m_moveBuffer; + int m_moveCount; + int m_proxyCapacity; + int m_proxyCount; + int m_timeStamp; + int m_updateType; + float m_fill; + float m_moveFraction; + float m_moveDelta; + float m_ratio; + float m_grid; + + b2Vec2 m_startPoint; + b2Vec2 m_endPoint; + + bool m_rayDrag; + bool m_queryDrag; + bool m_validate; +}; + +static bool QueryCallback( int proxyId, int userData, void* context ) +{ + DynamicTree* sample = static_cast( context ); + Proxy* proxy = sample->m_proxies + userData; + assert( proxy->proxyId == proxyId ); + proxy->queryStamp = sample->m_timeStamp; + return true; +} + +static float RayCallback( const b2RayCastInput* input, int proxyId, int userData, void* context ) +{ + DynamicTree* sample = static_cast( context ); + Proxy* proxy = sample->m_proxies + userData; + assert( proxy->proxyId == proxyId ); + proxy->rayStamp = sample->m_timeStamp; + return input->maxFraction; +} + +static int sampleDynamicTree = RegisterSample( "Collision", "Dynamic Tree", DynamicTree::Create ); + +class RayCast : public Sample +{ +public: + explicit RayCast( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, 20.0f }; + g_camera.m_zoom = 17.5f; + } + + m_circle = { { 0.0f, 0.0f }, 2.0f }; + m_capsule = { { -1.0f, 1.0f }, { 1.0f, -1.0f }, 1.5f }; + m_box = b2MakeBox( 2.0f, 2.0f ); + + b2Vec2 vertices[3] = { { -2.0f, 0.0f }, { 2.0f, 0.0f }, { 2.0f, 3.0f } }; + b2Hull hull = b2ComputeHull( vertices, 3 ); + m_triangle = b2MakePolygon( &hull, 0.0f ); + + m_segment = { { -3.0f, 0.0f }, { 3.0f, 0.0 } }; + + m_transform = b2Transform_identity; + m_angle = 0.0f; + + m_basePosition = { 0.0f, 0.0f }; + m_baseAngle = 0.0f; + m_startPosition = { 0.0f, 0.0f }; + + m_rayStart = { 0.0f, 30.0f }; + m_rayEnd = { 0.0f, 0.0f }; + + m_rayDrag = false; + m_translating = false; + m_rotating = false; + + m_showFraction = false; + } + + void UpdateUI() override + { + float height = 230.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 200.0f, height ) ); + + ImGui::Begin( "Ray-cast", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize ); + + ImGui::PushItemWidth( 100.0f ); + + ImGui::SliderFloat( "x offset", &m_transform.p.x, -2.0f, 2.0f, "%.2f" ); + ImGui::SliderFloat( "y offset", &m_transform.p.y, -2.0f, 2.0f, "%.2f" ); + + if ( ImGui::SliderFloat( "angle", &m_angle, -b2_pi, b2_pi, "%.2f" ) ) + { + m_transform.q = b2MakeRot( m_angle ); + } + + // if (ImGui::SliderFloat("ray radius", &m_rayRadius, 0.0f, 1.0f, "%.1f")) + //{ + // } + + ImGui::Checkbox( "show fraction", &m_showFraction ); + + if ( ImGui::Button( "Reset" ) ) + { + m_transform = b2Transform_identity; + m_angle = 0.0f; + } + + ImGui::Separator(); + + ImGui::Text( "mouse btn 1: ray cast" ); + ImGui::Text( "mouse btn 1 + shft: translate" ); + ImGui::Text( "mouse btn 1 + ctrl: rotate" ); + + ImGui::PopItemWidth(); + + ImGui::End(); + } + + void MouseDown( b2Vec2 p, int button, int mods ) override + { + if ( button == GLFW_MOUSE_BUTTON_1 ) + { + m_startPosition = p; + + if ( mods == 0 ) + { + m_rayStart = p; + m_rayDrag = true; + } + else if ( mods == GLFW_MOD_SHIFT ) + { + m_translating = true; + m_basePosition = m_transform.p; + } + else if ( mods == GLFW_MOD_CONTROL ) + { + m_rotating = true; + m_baseAngle = m_angle; + } + } + } + + void MouseUp( b2Vec2, int button ) override + { + if ( button == GLFW_MOUSE_BUTTON_1 ) + { + m_rayDrag = false; + m_rotating = false; + m_translating = false; + } + } + + void MouseMove( b2Vec2 p ) override + { + if ( m_rayDrag ) + { + m_rayEnd = p; + } + else if ( m_translating ) + { + m_transform.p.x = m_basePosition.x + 0.5f * ( p.x - m_startPosition.x ); + m_transform.p.y = m_basePosition.y + 0.5f * ( p.y - m_startPosition.y ); + } + else if ( m_rotating ) + { + float dx = p.x - m_startPosition.x; + m_angle = b2ClampFloat( m_baseAngle + 0.5f * dx, -b2_pi, b2_pi ); + m_transform.q = b2MakeRot( m_angle ); + } + } + + void DrawRay( const b2CastOutput* output ) + { + b2Vec2 p1 = m_rayStart; + b2Vec2 p2 = m_rayEnd; + b2Vec2 d = b2Sub( p2, p1 ); + + if ( output->hit ) + { + b2Vec2 p = b2MulAdd( p1, output->fraction, d ); + g_draw.DrawSegment( p1, p, b2_colorWhite ); + g_draw.DrawPoint( p1, 5.0f, b2_colorGreen ); + g_draw.DrawPoint( output->point, 5.0f, b2_colorWhite ); + + b2Vec2 n = b2MulAdd( p, 1.0f, output->normal ); + g_draw.DrawSegment( p, n, b2_colorViolet ); + + // if (m_rayRadius > 0.0f) + //{ + // g_draw.DrawCircle(p1, m_rayRadius, b2_colorGreen); + // g_draw.DrawCircle(p, m_rayRadius, b2_colorRed); + // } + + if ( m_showFraction ) + { + b2Vec2 ps = { p.x + 0.05f, p.y - 0.02f }; + g_draw.DrawString( ps, "%.2f", output->fraction ); + } + } + else + { + g_draw.DrawSegment( p1, p2, b2_colorWhite ); + g_draw.DrawPoint( p1, 5.0f, b2_colorGreen ); + g_draw.DrawPoint( p2, 5.0f, b2_colorRed ); + + // if (m_rayRadius > 0.0f) + //{ + // g_draw.DrawCircle(p1, m_rayRadius, b2_colorGreen); + // g_draw.DrawCircle(p2, m_rayRadius, b2_colorRed); + // } + } + } + + void Step( Settings& ) override + { + b2Vec2 offset = { -20.0f, 20.0f }; + b2Vec2 increment = { 10.0f, 0.0f }; + + b2HexColor color1 = b2_colorYellow; + + b2CastOutput output = { 0 }; + float maxFraction = 1.0f; + + // circle + { + b2Transform transform = { b2Add( m_transform.p, offset ), m_transform.q }; + g_draw.DrawSolidCircle( transform, m_circle.center, m_circle.radius, color1 ); + + b2Vec2 start = b2InvTransformPoint( transform, m_rayStart ); + b2Vec2 translation = b2InvRotateVector( transform.q, b2Sub( m_rayEnd, m_rayStart ) ); + b2RayCastInput input = { start, translation, maxFraction }; + + b2CastOutput localOutput = b2RayCastCircle( &input, &m_circle ); + if ( localOutput.hit ) + { + output = localOutput; + output.point = b2TransformPoint( transform, localOutput.point ); + output.normal = b2RotateVector( transform.q, localOutput.normal ); + maxFraction = localOutput.fraction; + } + + offset = b2Add( offset, increment ); + } + + // capsule + { + b2Transform transform = { b2Add( m_transform.p, offset ), m_transform.q }; + b2Vec2 v1 = b2TransformPoint( transform, m_capsule.center1 ); + b2Vec2 v2 = b2TransformPoint( transform, m_capsule.center2 ); + g_draw.DrawSolidCapsule( v1, v2, m_capsule.radius, color1 ); + + b2Vec2 start = b2InvTransformPoint( transform, m_rayStart ); + b2Vec2 translation = b2InvRotateVector( transform.q, b2Sub( m_rayEnd, m_rayStart ) ); + b2RayCastInput input = { start, translation, maxFraction }; + + b2CastOutput localOutput = b2RayCastCapsule( &input, &m_capsule ); + if ( localOutput.hit ) + { + output = localOutput; + output.point = b2TransformPoint( transform, localOutput.point ); + output.normal = b2RotateVector( transform.q, localOutput.normal ); + maxFraction = localOutput.fraction; + } + + offset = b2Add( offset, increment ); + } + + // box + { + b2Transform transform = { b2Add( m_transform.p, offset ), m_transform.q }; + g_draw.DrawSolidPolygon( transform, m_box.vertices, m_box.count, 0.0f, color1 ); + + b2Vec2 start = b2InvTransformPoint( transform, m_rayStart ); + b2Vec2 translation = b2InvRotateVector( transform.q, b2Sub( m_rayEnd, m_rayStart ) ); + b2RayCastInput input = { start, translation, maxFraction }; + + b2CastOutput localOutput = b2RayCastPolygon( &input, &m_box ); + if ( localOutput.hit ) + { + output = localOutput; + output.point = b2TransformPoint( transform, localOutput.point ); + output.normal = b2RotateVector( transform.q, localOutput.normal ); + maxFraction = localOutput.fraction; + } + + offset = b2Add( offset, increment ); + } + + // triangle + { + b2Transform transform = { b2Add( m_transform.p, offset ), m_transform.q }; + g_draw.DrawSolidPolygon( transform, m_triangle.vertices, m_triangle.count, 0.0f, color1 ); + + b2Vec2 start = b2InvTransformPoint( transform, m_rayStart ); + b2Vec2 translation = b2InvRotateVector( transform.q, b2Sub( m_rayEnd, m_rayStart ) ); + b2RayCastInput input = { start, translation, maxFraction }; + + b2CastOutput localOutput = b2RayCastPolygon( &input, &m_triangle ); + if ( localOutput.hit ) + { + output = localOutput; + output.point = b2TransformPoint( transform, localOutput.point ); + output.normal = b2RotateVector( transform.q, localOutput.normal ); + maxFraction = localOutput.fraction; + } + + offset = b2Add( offset, increment ); + } + + // segment + { + b2Transform transform = { b2Add( m_transform.p, offset ), m_transform.q }; + + b2Vec2 p1 = b2TransformPoint( transform, m_segment.point1 ); + b2Vec2 p2 = b2TransformPoint( transform, m_segment.point2 ); + g_draw.DrawSegment( p1, p2, color1 ); + + b2Vec2 start = b2InvTransformPoint( transform, m_rayStart ); + b2Vec2 translation = b2InvRotateVector( transform.q, b2Sub( m_rayEnd, m_rayStart ) ); + b2RayCastInput input = { start, translation, maxFraction }; + + b2CastOutput localOutput = b2RayCastSegment( &input, &m_segment, false ); + if ( localOutput.hit ) + { + output = localOutput; + output.point = b2TransformPoint( transform, localOutput.point ); + output.normal = b2RotateVector( transform.q, localOutput.normal ); + maxFraction = localOutput.fraction; + } + + offset = b2Add( offset, increment ); + } + + DrawRay( &output ); + } + + static Sample* Create( Settings& settings ) + { + return new RayCast( settings ); + } + + b2Polygon m_box; + b2Polygon m_triangle; + b2Circle m_circle; + b2Capsule m_capsule; + b2Segment m_segment; + + b2Transform m_transform; + float m_angle; + + b2Vec2 m_rayStart; + b2Vec2 m_rayEnd; + + b2Vec2 m_basePosition; + float m_baseAngle; + + b2Vec2 m_startPosition; + + bool m_rayDrag; + bool m_translating; + bool m_rotating; + bool m_showFraction; +}; + +static int sampleIndex = RegisterSample( "Collision", "Ray Cast", RayCast::Create ); + +// This shows how to filter a specific shape using using data. +struct ShapeUserData +{ + int index; + bool ignore; +}; + +// Context for ray cast callbacks. Do what you want with this. +struct RayCastContext +{ + b2Vec2 points[3]; + b2Vec2 normals[3]; + float fractions[3]; + int count; +}; + +// This callback finds the closest hit. This is the most common callback used in games. +static float RayCastClosestCallback( b2ShapeId shapeId, b2Vec2 point, b2Vec2 normal, float fraction, void* context ) +{ + RayCastContext* rayContext = (RayCastContext*)context; + + ShapeUserData* userData = (ShapeUserData*)b2Shape_GetUserData( shapeId ); + if ( userData != nullptr && userData->ignore ) + { + // By returning -1, we instruct the calling code to ignore this shape and + // continue the ray-cast to the next shape. + return -1.0f; + } + + rayContext->points[0] = point; + rayContext->normals[0] = normal; + rayContext->fractions[0] = fraction; + rayContext->count = 1; + + // By returning the current fraction, we instruct the calling code to clip the ray and + // continue the ray-cast to the next shape. WARNING: do not assume that shapes + // are reported in order. However, by clipping, we can always get the closest shape. + return fraction; +} + +// This callback finds any hit. For this type of query we are usually just checking for obstruction, +// so the hit data is not relevant. +// NOTE: shape hits are not ordered, so this may not return the closest hit +static float RayCastAnyCallback( b2ShapeId shapeId, b2Vec2 point, b2Vec2 normal, float fraction, void* context ) +{ + RayCastContext* rayContext = (RayCastContext*)context; + + ShapeUserData* userData = (ShapeUserData*)b2Shape_GetUserData( shapeId ); + if ( userData != nullptr && userData->ignore ) + { + // By returning -1, we instruct the calling code to ignore this shape and + // continue the ray-cast to the next shape. + return -1.0f; + } + + rayContext->points[0] = point; + rayContext->normals[0] = normal; + rayContext->fractions[0] = fraction; + rayContext->count = 1; + + // At this point we have a hit, so we know the ray is obstructed. + // By returning 0, we instruct the calling code to terminate the ray-cast. + return 0.0f; +} + +// This ray cast collects multiple hits along the ray. +// The shapes are not necessary reported in order, so we might not capture +// the closest shape. +// NOTE: shape hits are not ordered, so this may return hits in any order. This means that +// if you limit the number of results, you may discard the closest hit. You can see this +// behavior in the sample. +static float RayCastMultipleCallback( b2ShapeId shapeId, b2Vec2 point, b2Vec2 normal, float fraction, void* context ) +{ + RayCastContext* rayContext = (RayCastContext*)context; + + ShapeUserData* userData = (ShapeUserData*)b2Shape_GetUserData( shapeId ); + if ( userData != nullptr && userData->ignore ) + { + // By returning -1, we instruct the calling code to ignore this shape and + // continue the ray-cast to the next shape. + return -1.0f; + } + + int count = rayContext->count; + assert( count < 3 ); + + rayContext->points[count] = point; + rayContext->normals[count] = normal; + rayContext->fractions[count] = fraction; + rayContext->count = count + 1; + + if ( rayContext->count == 3 ) + { + // At this point the buffer is full. + // By returning 0, we instruct the calling code to terminate the ray-cast. + return 0.0f; + } + + // By returning 1, we instruct the caller to continue without clipping the ray. + return 1.0f; +} + +// This ray cast collects multiple hits along the ray and sorts them. +static float RayCastSortedCallback( b2ShapeId shapeId, b2Vec2 point, b2Vec2 normal, float fraction, void* context ) +{ + RayCastContext* rayContext = (RayCastContext*)context; + + ShapeUserData* userData = (ShapeUserData*)b2Shape_GetUserData( shapeId ); + if ( userData != nullptr && userData->ignore ) + { + // By returning -1, we instruct the calling code to ignore this shape and + // continue the ray-cast to the next shape. + return -1.0f; + } + + int count = rayContext->count; + assert( count <= 3 ); + + int index = 3; + while ( fraction < rayContext->fractions[index - 1] ) + { + index -= 1; + + if ( index == 0 ) + { + break; + } + } + + if ( index == 3 ) + { + // not closer, continue but tell the caller not to consider fractions further than the largest fraction acquired + // this only happens once the buffer is full + assert( rayContext->count == 3 ); + assert( rayContext->fractions[2] <= 1.0f ); + return rayContext->fractions[2]; + } + + for ( int j = 2; j > index; --j ) + { + rayContext->points[j] = rayContext->points[j - 1]; + rayContext->normals[j] = rayContext->normals[j - 1]; + rayContext->fractions[j] = rayContext->fractions[j - 1]; + } + + rayContext->points[index] = point; + rayContext->normals[index] = normal; + rayContext->fractions[index] = fraction; + rayContext->count = count < 3 ? count + 1 : 3; + + if ( rayContext->count == 3 ) + { + return rayContext->fractions[2]; + } + + // By returning 1, we instruct the caller to continue without clipping the ray. + return 1.0f; +} + +class RayCastWorld : public Sample +{ +public: + enum Mode + { + e_any = 0, + e_closest = 1, + e_multiple = 2, + e_sorted = 3 + }; + + enum CastType + { + e_rayCast = 0, + e_circleCast = 1, + e_capsuleCast = 2, + e_polygonCast = 3 + }; + + enum + { + e_maxCount = 64 + }; + + explicit RayCastWorld( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 2.0f, 14.0f }; + g_camera.m_zoom = 25.0f * 0.75f; + } + + // Ground body + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Segment segment = { { -40.0f, 0.0f }, { 40.0f, 0.0f } }; + b2CreateSegmentShape( groundId, &shapeDef, &segment ); + } + + { + b2Vec2 vertices[3] = { { -0.5f, 0.0f }, { 0.5f, 0.0f }, { 0.0f, 1.5f } }; + b2Hull hull = b2ComputeHull( vertices, 3 ); + m_polygons[0] = b2MakePolygon( &hull, 0.0f ); + } + + { + b2Vec2 vertices[3] = { { -0.1f, 0.0f }, { 0.1f, 0.0f }, { 0.0f, 1.5f } }; + b2Hull hull = b2ComputeHull( vertices, 3 ); + m_polygons[1] = b2MakePolygon( &hull, 0.0f ); + m_polygons[1].radius = 0.5f; + } + + { + float w = 1.0f; + float b = w / ( 2.0f + sqrtf( 2.0f ) ); + float s = sqrtf( 2.0f ) * b; + + b2Vec2 vertices[8] = { { 0.5f * s, 0.0f }, { 0.5f * w, b }, { 0.5f * w, b + s }, { 0.5f * s, w }, + { -0.5f * s, w }, { -0.5f * w, b + s }, { -0.5f * w, b }, { -0.5f * s, 0.0f } }; + + b2Hull hull = b2ComputeHull( vertices, 8 ); + m_polygons[2] = b2MakePolygon( &hull, 0.0f ); + } + + m_polygons[3] = b2MakeBox( 0.5f, 0.5f ); + m_capsule = { { -0.5f, 0.0f }, { 0.5f, 0.0f }, 0.25f }; + m_circle = { { 0.0f, 0.0f }, 0.5f }; + m_segment = { { -1.0f, 0.0f }, { 1.0f, 0.0f } }; + + m_bodyIndex = 0; + + for ( int i = 0; i < e_maxCount; ++i ) + { + m_bodyIds[i] = b2_nullBodyId; + } + + m_mode = e_closest; + m_ignoreIndex = 7; + + m_castType = e_rayCast; + m_castRadius = 0.5f; + + m_rayStart = { -20.0f, 10.0f }; + m_rayEnd = { 20.0f, 10.0f }; + m_dragging = false; + + m_angle = 0.0f; + m_baseAngle = 0.0f; + m_angleAnchor = { 0.0f, 0.0f }; + m_rotating = false; + + m_simple = false; + } + + void Create( int index ) + { + if ( B2_IS_NON_NULL( m_bodyIds[m_bodyIndex] ) ) + { + b2DestroyBody( m_bodyIds[m_bodyIndex] ); + m_bodyIds[m_bodyIndex] = b2_nullBodyId; + } + + float x = RandomFloat( -20.0f, 20.0f ); + float y = RandomFloat( 0.0f, 20.0f ); + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.position = { x, y }; + bodyDef.rotation = b2MakeRot( RandomFloat( -b2_pi, b2_pi ) ); + + m_bodyIds[m_bodyIndex] = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.userData = m_userData + m_bodyIndex; + m_userData[m_bodyIndex].ignore = false; + if ( m_bodyIndex == m_ignoreIndex ) + { + m_userData[m_bodyIndex].ignore = true; + } + + if ( index < 4 ) + { + b2CreatePolygonShape( m_bodyIds[m_bodyIndex], &shapeDef, m_polygons + index ); + } + else if ( index == 4 ) + { + b2CreateCircleShape( m_bodyIds[m_bodyIndex], &shapeDef, &m_circle ); + } + else if ( index == 5 ) + { + b2CreateCapsuleShape( m_bodyIds[m_bodyIndex], &shapeDef, &m_capsule ); + } + else + { + b2CreateSegmentShape( m_bodyIds[m_bodyIndex], &shapeDef, &m_segment ); + } + + m_bodyIndex = ( m_bodyIndex + 1 ) % e_maxCount; + } + + void CreateN( int index, int count ) + { + for ( int i = 0; i < count; ++i ) + { + Create( index ); + } + } + + void DestroyBody() + { + for ( int i = 0; i < e_maxCount; ++i ) + { + if ( B2_IS_NON_NULL( m_bodyIds[i] ) ) + { + b2DestroyBody( m_bodyIds[i] ); + m_bodyIds[i] = b2_nullBodyId; + return; + } + } + } + + void MouseDown( b2Vec2 p, int button, int mods ) override + { + if ( button == GLFW_MOUSE_BUTTON_1 ) + { + if ( mods == 0 && m_rotating == false ) + { + m_rayStart = p; + m_rayEnd = p; + m_dragging = true; + } + else if ( mods == GLFW_MOD_SHIFT && m_dragging == false ) + { + m_rotating = true; + m_angleAnchor = p; + m_baseAngle = m_angle; + } + } + } + + void MouseUp( b2Vec2, int button ) override + { + if ( button == GLFW_MOUSE_BUTTON_1 ) + { + m_dragging = false; + m_rotating = false; + } + } + + void MouseMove( b2Vec2 p ) override + { + if ( m_dragging ) + { + m_rayEnd = p; + } + else if ( m_rotating ) + { + float dx = p.x - m_angleAnchor.x; + m_angle = m_baseAngle + 1.0f * dx; + } + } + + void UpdateUI() override + { + float height = 300.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 200.0f, height ) ); + + ImGui::Begin( "Ray-cast World", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize ); + + ImGui::Checkbox( "Simple", &m_simple ); + + if ( m_simple == false ) + { + const char* castTypes[] = { "Ray", "Circle", "Capsule", "Polygon" }; + int castType = int( m_castType ); + if ( ImGui::Combo( "Type", &castType, castTypes, IM_ARRAYSIZE( castTypes ) ) ) + { + m_castType = CastType( castType ); + } + + if ( m_castType != e_rayCast ) + { + ImGui::SliderFloat( "Radius", &m_castRadius, 0.0f, 2.0f, "%.1f" ); + } + + const char* modes[] = { "Any", "Closest", "Multiple", "Sorted" }; + int mode = int( m_mode ); + if ( ImGui::Combo( "Mode", &mode, modes, IM_ARRAYSIZE( modes ) ) ) + { + m_mode = Mode( mode ); + } + } + + if ( ImGui::Button( "Polygon 1" ) ) + Create( 0 ); + ImGui::SameLine(); + if ( ImGui::Button( "10x##Poly1" ) ) + CreateN( 0, 10 ); + + if ( ImGui::Button( "Polygon 2" ) ) + Create( 1 ); + ImGui::SameLine(); + if ( ImGui::Button( "10x##Poly2" ) ) + CreateN( 1, 10 ); + + if ( ImGui::Button( "Polygon 3" ) ) + Create( 2 ); + ImGui::SameLine(); + if ( ImGui::Button( "10x##Poly3" ) ) + CreateN( 2, 10 ); + + if ( ImGui::Button( "Box" ) ) + Create( 3 ); + ImGui::SameLine(); + if ( ImGui::Button( "10x##Box" ) ) + CreateN( 3, 10 ); + + if ( ImGui::Button( "Circle" ) ) + Create( 4 ); + ImGui::SameLine(); + if ( ImGui::Button( "10x##Circle" ) ) + CreateN( 4, 10 ); + + if ( ImGui::Button( "Capsule" ) ) + Create( 5 ); + ImGui::SameLine(); + if ( ImGui::Button( "10x##Capsule" ) ) + CreateN( 5, 10 ); + + if ( ImGui::Button( "Segment" ) ) + Create( 6 ); + ImGui::SameLine(); + if ( ImGui::Button( "10x##Segment" ) ) + CreateN( 6, 10 ); + + if ( ImGui::Button( "Destroy Shape" ) ) + { + DestroyBody(); + } + + ImGui::End(); + } + + void Step( Settings& settings ) override + { + Sample::Step( settings ); + + g_draw.DrawString( 5, m_textLine, "Click left mouse button and drag to modify ray cast" ); + m_textLine += m_textIncrement; + g_draw.DrawString( 5, m_textLine, "Shape 7 is intentionally ignored by the ray" ); + m_textLine += m_textIncrement; + + m_textLine += m_textIncrement; + + b2HexColor color1 = b2_colorGreen; + b2HexColor color2 = b2_colorGray8; + b2HexColor color3 = b2_colorMagenta; + + b2Vec2 rayTranslation = b2Sub( m_rayEnd, m_rayStart ); + + if ( m_simple ) + { + g_draw.DrawString( 5, m_textLine, "Simple closest point ray cast" ); + m_textLine += m_textIncrement; + + // This version doesn't have a callback, but it doesn't skip the ignored shape + b2RayResult result = b2World_CastRayClosest( m_worldId, m_rayStart, rayTranslation, b2DefaultQueryFilter() ); + + if ( result.hit == true ) + { + b2Vec2 c = b2MulAdd( m_rayStart, result.fraction, rayTranslation ); + g_draw.DrawPoint( result.point, 5.0f, color1 ); + g_draw.DrawSegment( m_rayStart, c, color2 ); + b2Vec2 head = b2MulAdd( result.point, 0.5f, result.normal ); + g_draw.DrawSegment( result.point, head, color3 ); + } + else + { + g_draw.DrawSegment( m_rayStart, m_rayEnd, color2 ); + } + } + else + { + switch ( m_mode ) + { + case e_any: + g_draw.DrawString( 5, m_textLine, "Cast mode: any - check for obstruction - unsorted" ); + break; + + case e_closest: + g_draw.DrawString( 5, m_textLine, "Cast mode: closest - find closest shape along the cast" ); + break; + + case e_multiple: + g_draw.DrawString( 5, m_textLine, "Cast mode: multiple - gather up to 3 shapes - unsorted" ); + break; + + case e_sorted: + g_draw.DrawString( 5, m_textLine, "Cast mode: sorted - gather up to 3 shapes sorted by closeness" ); + break; + } + + m_textLine += m_textIncrement; + + b2CastResultFcn* fcns[] = { RayCastAnyCallback, RayCastClosestCallback, RayCastMultipleCallback, + RayCastSortedCallback }; + b2CastResultFcn* modeFcn = fcns[m_mode]; + + RayCastContext context = { 0 }; + + // Must initialize fractions for sorting + context.fractions[0] = FLT_MAX; + context.fractions[1] = FLT_MAX; + context.fractions[2] = FLT_MAX; + + b2Circle circle = { { 0.0f, 0.0f }, m_castRadius }; + b2Capsule capsule = { { -0.25f, 0.0f }, { 0.25f, 0.0f }, m_castRadius }; + b2Polygon box = b2MakeRoundedBox( 0.25f, 0.5f, m_castRadius ); + b2Transform transform = { m_rayStart, b2MakeRot( m_angle ) }; + + switch ( m_castType ) + { + case e_rayCast: + b2World_CastRay( m_worldId, m_rayStart, rayTranslation, b2DefaultQueryFilter(), modeFcn, &context ); + break; + + case e_circleCast: + b2World_CastCircle( m_worldId, &circle, transform, rayTranslation, b2DefaultQueryFilter(), modeFcn, + &context ); + break; + + case e_capsuleCast: + b2World_CastCapsule( m_worldId, &capsule, transform, rayTranslation, b2DefaultQueryFilter(), modeFcn, + &context ); + break; + + case e_polygonCast: + b2World_CastPolygon( m_worldId, &box, transform, rayTranslation, b2DefaultQueryFilter(), modeFcn, &context ); + break; + } + + if ( context.count > 0 ) + { + assert( context.count <= 3 ); + b2HexColor colors[3] = { b2_colorRed, b2_colorGreen, b2_colorBlue }; + for ( int i = 0; i < context.count; ++i ) + { + b2Vec2 c = b2MulAdd( m_rayStart, context.fractions[i], rayTranslation ); + b2Vec2 p = context.points[i]; + b2Vec2 n = context.normals[i]; + g_draw.DrawPoint( p, 5.0f, colors[i] ); + g_draw.DrawSegment( m_rayStart, c, color2 ); + b2Vec2 head = b2MulAdd( p, 0.5f, n ); + g_draw.DrawSegment( p, head, color3 ); + + b2Vec2 t = b2MulSV( context.fractions[i], rayTranslation ); + b2Transform shiftedTransform = { b2Add( transform.p, t ), transform.q }; + + if ( m_castType == e_circleCast ) + { + g_draw.DrawSolidCircle( shiftedTransform, b2Vec2_zero, m_castRadius, b2_colorYellow ); + } + else if ( m_castType == e_capsuleCast ) + { + b2Vec2 p1 = b2Add( b2TransformPoint( transform, capsule.center1 ), t ); + b2Vec2 p2 = b2Add( b2TransformPoint( transform, capsule.center2 ), t ); + g_draw.DrawSolidCapsule( p1, p2, m_castRadius, b2_colorYellow ); + } + else if ( m_castType == e_polygonCast ) + { + g_draw.DrawSolidPolygon( shiftedTransform, box.vertices, box.count, box.radius, b2_colorYellow ); + } + } + } + else + { + b2Transform shiftedTransform = { b2Add( transform.p, rayTranslation ), transform.q }; + g_draw.DrawSegment( m_rayStart, m_rayEnd, color2 ); + + if ( m_castType == e_circleCast ) + { + g_draw.DrawSolidCircle( shiftedTransform, b2Vec2_zero, m_castRadius, b2_colorGray ); + } + else if ( m_castType == e_capsuleCast ) + { + b2Vec2 p1 = b2Add( b2TransformPoint( transform, capsule.center1 ), rayTranslation ); + b2Vec2 p2 = b2Add( b2TransformPoint( transform, capsule.center2 ), rayTranslation ); + g_draw.DrawSolidCapsule( p1, p2, m_castRadius, b2_colorYellow ); + } + else if ( m_castType == e_polygonCast ) + { + g_draw.DrawSolidPolygon( shiftedTransform, box.vertices, box.count, box.radius, b2_colorYellow ); + } + } + } + + g_draw.DrawPoint( m_rayStart, 5.0f, b2_colorGreen ); + + if ( B2_IS_NON_NULL( m_bodyIds[m_ignoreIndex] ) ) + { + b2Vec2 p = b2Body_GetPosition( m_bodyIds[m_ignoreIndex] ); + p.x -= 0.2f; + g_draw.DrawString( p, "ign" ); + } + } + + static Sample* Create( Settings& settings ) + { + return new RayCastWorld( settings ); + } + + int m_bodyIndex; + b2BodyId m_bodyIds[e_maxCount]; + ShapeUserData m_userData[e_maxCount]; + b2Polygon m_polygons[4]; + b2Capsule m_capsule; + b2Circle m_circle; + b2Segment m_segment; + + bool m_simple; + + int m_mode; + int m_ignoreIndex; + + CastType m_castType; + float m_castRadius; + + b2Vec2 m_angleAnchor; + float m_baseAngle; + float m_angle; + bool m_rotating; + + b2Vec2 m_rayStart; + b2Vec2 m_rayEnd; + bool m_dragging; +}; + +static int sampleRayCastWorld = RegisterSample( "Collision", "Ray Cast World", RayCastWorld::Create ); + +class OverlapWorld : public Sample +{ +public: + enum + { + e_circleShape = 0, + e_capsuleShape = 1, + e_boxShape = 2 + }; + + enum + { + e_maxCount = 64, + e_maxDoomed = 16, + }; + + static bool OverlapResultFcn( b2ShapeId shapeId, void* context ) + { + ShapeUserData* userData = (ShapeUserData*)b2Shape_GetUserData( shapeId ); + if ( userData != nullptr && userData->ignore ) + { + // continue the query + return true; + } + + OverlapWorld* sample = (OverlapWorld*)context; + + if ( sample->m_doomCount < e_maxDoomed ) + { + int index = sample->m_doomCount; + sample->m_doomIds[index] = shapeId; + sample->m_doomCount += 1; + } + + // continue the query + return true; + } + + explicit OverlapWorld( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, 10.0f }; + g_camera.m_zoom = 25.0f * 0.7f; + } + + { + b2Vec2 vertices[3] = { { -0.5f, 0.0f }, { 0.5f, 0.0f }, { 0.0f, 1.5f } }; + b2Hull hull = b2ComputeHull( vertices, 3 ); + m_polygons[0] = b2MakePolygon( &hull, 0.0f ); + } + + { + b2Vec2 vertices[3] = { { -0.1f, 0.0f }, { 0.1f, 0.0f }, { 0.0f, 1.5f } }; + b2Hull hull = b2ComputeHull( vertices, 3 ); + m_polygons[1] = b2MakePolygon( &hull, 0.0f ); + } + + { + float w = 1.0f; + float b = w / ( 2.0f + sqrtf( 2.0f ) ); + float s = sqrtf( 2.0f ) * b; + + b2Vec2 vertices[8] = { { 0.5f * s, 0.0f }, { 0.5f * w, b }, { 0.5f * w, b + s }, { 0.5f * s, w }, + { -0.5f * s, w }, { -0.5f * w, b + s }, { -0.5f * w, b }, { -0.5f * s, 0.0f } }; + + b2Hull hull = b2ComputeHull( vertices, 8 ); + m_polygons[2] = b2MakePolygon( &hull, 0.0f ); + } + + m_polygons[3] = b2MakeBox( 0.5f, 0.5f ); + m_capsule = { { -0.5f, 0.0f }, { 0.5f, 0.0f }, 0.25f }; + m_circle = { { 0.0f, 0.0f }, 0.5f }; + m_segment = { { -1.0f, 0.0f }, { 1.0f, 0.0f } }; + + m_bodyIndex = 0; + + for ( int i = 0; i < e_maxCount; ++i ) + { + m_bodyIds[i] = b2_nullBodyId; + } + + m_ignoreIndex = 7; + + m_shapeType = e_circleShape; + + m_queryCircle = { { 0.0f, 0.0f }, 1.0f }; + m_queryCapsule = { { -1.0f, 0.0f }, { 1.0f, 0.0f }, 0.5f }; + m_queryBox = b2MakeBox( 2.0f, 0.5f ); + + m_position = { 0.0f, 10.0f }; + m_angle = 0.0f; + m_dragging = false; + m_rotating = false; + + m_doomCount = 0; + + CreateN( 0, 10 ); + } + + void Create( int index ) + { + if ( B2_IS_NON_NULL( m_bodyIds[m_bodyIndex] ) ) + { + b2DestroyBody( m_bodyIds[m_bodyIndex] ); + m_bodyIds[m_bodyIndex] = b2_nullBodyId; + } + + float x = RandomFloat( -20.0f, 20.0f ); + float y = RandomFloat( 0.0f, 20.0f ); + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.position = { x, y }; + bodyDef.rotation = b2MakeRot( RandomFloat( -b2_pi, b2_pi ) ); + + m_bodyIds[m_bodyIndex] = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.userData = m_userData + m_bodyIndex; + m_userData[m_bodyIndex].index = m_bodyIndex; + m_userData[m_bodyIndex].ignore = false; + if ( m_bodyIndex == m_ignoreIndex ) + { + m_userData[m_bodyIndex].ignore = true; + } + + if ( index < 4 ) + { + b2CreatePolygonShape( m_bodyIds[m_bodyIndex], &shapeDef, m_polygons + index ); + } + else if ( index == 4 ) + { + b2CreateCircleShape( m_bodyIds[m_bodyIndex], &shapeDef, &m_circle ); + } + else if ( index == 5 ) + { + b2CreateCapsuleShape( m_bodyIds[m_bodyIndex], &shapeDef, &m_capsule ); + } + else + { + b2CreateSegmentShape( m_bodyIds[m_bodyIndex], &shapeDef, &m_segment ); + } + + m_bodyIndex = ( m_bodyIndex + 1 ) % e_maxCount; + } + + void CreateN( int index, int count ) + { + for ( int i = 0; i < count; ++i ) + { + Create( index ); + } + } + + void DestroyBody() + { + for ( int i = 0; i < e_maxCount; ++i ) + { + if ( B2_IS_NON_NULL( m_bodyIds[i] ) ) + { + b2DestroyBody( m_bodyIds[i] ); + m_bodyIds[i] = b2_nullBodyId; + return; + } + } + } + + void MouseDown( b2Vec2 p, int button, int mods ) override + { + if ( button == GLFW_MOUSE_BUTTON_1 ) + { + if ( mods == 0 && m_rotating == false ) + { + m_dragging = true; + m_position = p; + } + else if ( mods == GLFW_MOD_SHIFT && m_dragging == false ) + { + m_rotating = true; + m_startPosition = p; + m_baseAngle = m_angle; + } + } + } + + void MouseUp( b2Vec2, int button ) override + { + if ( button == GLFW_MOUSE_BUTTON_1 ) + { + m_dragging = false; + m_rotating = false; + } + } + + void MouseMove( b2Vec2 p ) override + { + if ( m_dragging ) + { + m_position = p; + } + else if ( m_rotating ) + { + float dx = p.x - m_startPosition.x; + m_angle = m_baseAngle + 1.0f * dx; + } + } + + void UpdateUI() override + { + float height = 330.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 140.0f, height ) ); + + ImGui::Begin( "Overlap World", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize ); + + if ( ImGui::Button( "Polygon 1" ) ) + Create( 0 ); + ImGui::SameLine(); + if ( ImGui::Button( "10x##Poly1" ) ) + CreateN( 0, 10 ); + + if ( ImGui::Button( "Polygon 2" ) ) + Create( 1 ); + ImGui::SameLine(); + if ( ImGui::Button( "10x##Poly2" ) ) + CreateN( 1, 10 ); + + if ( ImGui::Button( "Polygon 3" ) ) + Create( 2 ); + ImGui::SameLine(); + if ( ImGui::Button( "10x##Poly3" ) ) + CreateN( 2, 10 ); + + if ( ImGui::Button( "Box" ) ) + Create( 3 ); + ImGui::SameLine(); + if ( ImGui::Button( "10x##Box" ) ) + CreateN( 3, 10 ); + + if ( ImGui::Button( "Circle" ) ) + Create( 4 ); + ImGui::SameLine(); + if ( ImGui::Button( "10x##Circle" ) ) + CreateN( 4, 10 ); + + if ( ImGui::Button( "Capsule" ) ) + Create( 5 ); + ImGui::SameLine(); + if ( ImGui::Button( "10x##Capsule" ) ) + CreateN( 5, 10 ); + + if ( ImGui::Button( "Segment" ) ) + Create( 6 ); + ImGui::SameLine(); + if ( ImGui::Button( "10x##Segment" ) ) + CreateN( 6, 10 ); + + if ( ImGui::Button( "Destroy Shape" ) ) + { + DestroyBody(); + } + + ImGui::Separator(); + ImGui::Text( "Overlap Shape" ); + ImGui::RadioButton( "Circle##Overlap", &m_shapeType, e_circleShape ); + ImGui::RadioButton( "Capsule##Overlap", &m_shapeType, e_capsuleShape ); + ImGui::RadioButton( "Box##Overlap", &m_shapeType, e_boxShape ); + + ImGui::End(); + } + + void Step( Settings& settings ) override + { + Sample::Step( settings ); + + g_draw.DrawString( 5, m_textLine, "left mouse button: drag query shape" ); + m_textLine += m_textIncrement; + g_draw.DrawString( 5, m_textLine, "left mouse button + shift: rotate query shape" ); + m_textLine += m_textIncrement; + + m_doomCount = 0; + + b2Transform transform = { m_position, b2MakeRot( m_angle ) }; + + if ( m_shapeType == e_circleShape ) + { + b2World_OverlapCircle( m_worldId, &m_queryCircle, transform, b2DefaultQueryFilter(), OverlapWorld::OverlapResultFcn, + this ); + g_draw.DrawSolidCircle( transform, b2Vec2_zero, m_queryCircle.radius, b2_colorWhite ); + } + else if ( m_shapeType == e_capsuleShape ) + { + b2World_OverlapCapsule( m_worldId, &m_queryCapsule, transform, b2DefaultQueryFilter(), OverlapWorld::OverlapResultFcn, + this ); + b2Vec2 p1 = b2TransformPoint( transform, m_queryCapsule.center1 ); + b2Vec2 p2 = b2TransformPoint( transform, m_queryCapsule.center2 ); + g_draw.DrawSolidCapsule( p1, p2, m_queryCapsule.radius, b2_colorWhite ); + } + else if ( m_shapeType == e_boxShape ) + { + b2World_OverlapPolygon( m_worldId, &m_queryBox, transform, b2DefaultQueryFilter(), OverlapWorld::OverlapResultFcn, + this ); + b2Vec2 points[b2_maxPolygonVertices] = { 0 }; + for ( int i = 0; i < m_queryBox.count; ++i ) + { + points[i] = b2TransformPoint( transform, m_queryBox.vertices[i] ); + } + g_draw.DrawPolygon( points, m_queryBox.count, b2_colorWhite ); + } + + if ( B2_IS_NON_NULL( m_bodyIds[m_ignoreIndex] ) ) + { + b2Vec2 p = b2Body_GetPosition( m_bodyIds[m_ignoreIndex] ); + p.x -= 0.2f; + g_draw.DrawString( p, "skip" ); + } + + for ( int i = 0; i < m_doomCount; ++i ) + { + b2ShapeId shapeId = m_doomIds[i]; + ShapeUserData* userData = (ShapeUserData*)b2Shape_GetUserData( shapeId ); + if ( userData == nullptr ) + { + continue; + } + + int index = userData->index; + assert( 0 <= index && index < e_maxCount ); + assert( B2_IS_NON_NULL( m_bodyIds[index] ) ); + + b2DestroyBody( m_bodyIds[index] ); + m_bodyIds[index] = b2_nullBodyId; + } + } + + static Sample* Create( Settings& settings ) + { + return new OverlapWorld( settings ); + } + + int m_bodyIndex; + b2BodyId m_bodyIds[e_maxCount]; + ShapeUserData m_userData[e_maxCount]; + b2Polygon m_polygons[4]; + b2Capsule m_capsule; + b2Circle m_circle; + b2Segment m_segment; + int m_ignoreIndex; + + b2ShapeId m_doomIds[e_maxDoomed]; + int m_doomCount; + + b2Circle m_queryCircle; + b2Capsule m_queryCapsule; + b2Polygon m_queryBox; + + int m_shapeType; + b2Transform m_transform; + + b2Vec2 m_startPosition; + + b2Vec2 m_position; + b2Vec2 m_basePosition; + float m_angle; + float m_baseAngle; + + bool m_dragging; + bool m_rotating; +}; + +static int sampleOverlapWorld = RegisterSample( "Collision", "Overlap World", OverlapWorld::Create ); + +// Tests manifolds and contact points +class Manifold : public Sample +{ +public: + explicit Manifold( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + // g_camera.m_center = {1.8f, 15.0f}; + g_camera.m_center = { 1.8f, 0.0f }; + g_camera.m_zoom = 25.0f * 0.45f; + } + + m_smgroxCache1 = b2_emptyDistanceCache; + m_smgroxCache2 = b2_emptyDistanceCache; + m_smgcapCache1 = b2_emptyDistanceCache; + m_smgcapCache2 = b2_emptyDistanceCache; + + m_transform = b2Transform_identity; + m_transform.p.x = 1.0f; + m_transform.p.y = 0.0f; + //m_transform.q = b2MakeRot( 0.5f * b2_pi ); + m_angle = 0.0f; + m_round = 0.1f; + + m_startPoint = { 0.0f, 0.0f }; + m_basePosition = { 0.0f, 0.0f }; + m_baseAngle = 0.0f; + + m_dragging = false; + m_rotating = false; + m_showIds = false; + m_showSeparation = false; + m_showAnchors = false; + m_enableCaching = true; + + b2Vec2 points[3] = { { -0.1f, -0.5f }, { 0.1f, -0.5f }, { 0.0f, 0.5f } }; + m_wedge = b2ComputeHull( points, 3 ); + } + + void UpdateUI() override + { + float height = 300.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 240.0f, height ) ); + + ImGui::Begin( "Manifold", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize ); + + ImGui::SliderFloat( "x offset", &m_transform.p.x, -2.0f, 2.0f, "%.2f" ); + ImGui::SliderFloat( "y offset", &m_transform.p.y, -2.0f, 2.0f, "%.2f" ); + + if ( ImGui::SliderFloat( "angle", &m_angle, -b2_pi, b2_pi, "%.2f" ) ) + { + m_transform.q = b2MakeRot( m_angle ); + } + + ImGui::SliderFloat( "round", &m_round, 0.0f, 0.4f, "%.1f" ); + ImGui::Checkbox( "show ids", &m_showIds ); + ImGui::Checkbox( "show separation", &m_showSeparation ); + ImGui::Checkbox( "show anchors", &m_showAnchors ); + ImGui::Checkbox( "enable caching", &m_enableCaching ); + + if ( ImGui::Button( "Reset" ) ) + { + m_transform = b2Transform_identity; + m_angle = 0.0f; + } + + ImGui::Separator(); + + ImGui::Text( "mouse button 1: drag" ); + ImGui::Text( "mouse button 1 + shift: rotate" ); + + ImGui::End(); + } + + void MouseDown( b2Vec2 p, int button, int mods ) override + { + if ( button == GLFW_MOUSE_BUTTON_1 ) + { + if ( mods == 0 && m_rotating == false ) + { + m_dragging = true; + m_startPoint = p; + m_basePosition = m_transform.p; + } + else if ( mods == GLFW_MOD_SHIFT && m_dragging == false ) + { + m_rotating = true; + m_startPoint = p; + m_baseAngle = m_angle; + } + } + } + + void MouseUp( b2Vec2, int button ) override + { + if ( button == GLFW_MOUSE_BUTTON_1 ) + { + m_dragging = false; + m_rotating = false; + } + } + + void MouseMove( b2Vec2 p ) override + { + if ( m_dragging ) + { + m_transform.p.x = m_basePosition.x + 0.5f * ( p.x - m_startPoint.x ); + m_transform.p.y = m_basePosition.y + 0.5f * ( p.y - m_startPoint.y ); + } + else if ( m_rotating ) + { + float dx = p.x - m_startPoint.x; + m_angle = b2ClampFloat( m_baseAngle + 1.0f * dx, -b2_pi, b2_pi ); + m_transform.q = b2MakeRot( m_angle ); + } + } + + void DrawManifold( const b2Manifold* manifold, b2Vec2 origin1, b2Vec2 origin2 ) + { + for ( int i = 0; i < manifold->pointCount; ++i ) + { + const b2ManifoldPoint* mp = manifold->points + i; + + b2Vec2 p1 = mp->point; + b2Vec2 p2 = b2MulAdd( p1, 0.5f, manifold->normal ); + g_draw.DrawSegment( p1, p2, b2_colorWhite ); + + if ( m_showAnchors ) + { + g_draw.DrawPoint( b2Add( origin1, mp->anchorA ), 5.0f, b2_colorRed ); + g_draw.DrawPoint( b2Add( origin2, mp->anchorB ), 5.0f, b2_colorGreen ); + } + else + { + g_draw.DrawPoint( p1, 5.0f, b2_colorBlue ); + } + + if ( m_showIds ) + { + // uint32_t indexA = mp->id >> 8; + // uint32_t indexB = 0xFF & mp->id; + b2Vec2 p = { p1.x + 0.05f, p1.y - 0.02f }; + g_draw.DrawString( p, "0x%04x", mp->id ); + } + + if ( m_showSeparation ) + { + b2Vec2 p = { p1.x + 0.05f, p1.y + 0.03f }; + g_draw.DrawString( p, "%.3f", mp->separation ); + } + } + } + + void Step( Settings& ) override + { + b2Vec2 offset = { -10.0f, -5.0f }; + b2Vec2 increment = { 4.0f, 0.0f }; + + b2HexColor color1 = b2_colorAquamarine; + b2HexColor color2 = b2_colorPaleGoldenrod; + + if ( m_enableCaching == false ) + { + m_smgroxCache1 = b2_emptyDistanceCache; + m_smgroxCache2 = b2_emptyDistanceCache; + m_smgcapCache1 = b2_emptyDistanceCache; + m_smgcapCache2 = b2_emptyDistanceCache; + } + + // circle-circle + { + b2Circle circle1 = { { 0.0f, 0.0f }, 0.5f }; + b2Circle circle2 = { { 0.0f, 0.0f }, 1.0f }; + + b2Transform transform1 = { offset, b2Rot_identity }; + b2Transform transform2 = { b2Add( m_transform.p, offset ), m_transform.q }; + + b2Manifold m = b2CollideCircles( &circle1, transform1, &circle2, transform2 ); + + g_draw.DrawSolidCircle( transform1, circle1.center, circle1.radius, color1 ); + g_draw.DrawSolidCircle( transform2, circle2.center, circle2.radius, color2 ); + + DrawManifold( &m, transform1.p, transform2.p ); + + offset = b2Add( offset, increment ); + } + + // capsule-circle + { + b2Capsule capsule = { { -0.5f, 0.0f }, { 0.5f, 0.0 }, 0.25f }; + b2Circle circle = { { 0.0f, 0.0f }, 0.5f }; + + b2Transform transform1 = { offset, b2Rot_identity }; + b2Transform transform2 = { b2Add( m_transform.p, offset ), m_transform.q }; + + b2Manifold m = b2CollideCapsuleAndCircle( &capsule, transform1, &circle, transform2 ); + + b2Vec2 v1 = b2TransformPoint( transform1, capsule.center1 ); + b2Vec2 v2 = b2TransformPoint( transform1, capsule.center2 ); + g_draw.DrawSolidCapsule( v1, v2, capsule.radius, color1 ); + + g_draw.DrawSolidCircle( transform2, circle.center, circle.radius, color2 ); + + DrawManifold( &m, transform1.p, transform2.p ); + + offset = b2Add( offset, increment ); + } + + // segment-circle + { + b2Segment segment = { { -1.0f, 0.0f }, { 1.0f, 0.0 } }; + b2Circle circle = { { 0.0f, 0.0f }, 0.5f }; + + b2Transform transform1 = { offset, b2Rot_identity }; + b2Transform transform2 = { b2Add( m_transform.p, offset ), m_transform.q }; + + b2Manifold m = b2CollideSegmentAndCircle( &segment, transform1, &circle, transform2 ); + + b2Vec2 p1 = b2TransformPoint( transform1, segment.point1 ); + b2Vec2 p2 = b2TransformPoint( transform1, segment.point2 ); + g_draw.DrawSegment( p1, p2, color1 ); + + g_draw.DrawSolidCircle( transform2, circle.center, circle.radius, color2 ); + + DrawManifold( &m, transform1.p, transform2.p ); + + offset = b2Add( offset, increment ); + } + + // box-circle + { + b2Circle circle = { { 0.0f, 0.0f }, 0.5f }; + b2Polygon box = b2MakeSquare( 0.5f ); + box.radius = m_round; + + b2Transform transform1 = { offset, b2Rot_identity }; + b2Transform transform2 = { b2Add( m_transform.p, offset ), m_transform.q }; + + b2Manifold m = b2CollidePolygonAndCircle( &box, transform1, &circle, transform2 ); + + g_draw.DrawSolidPolygon( transform1, box.vertices, box.count, m_round, color1 ); + g_draw.DrawSolidCircle( transform2, circle.center, circle.radius, color2 ); + + DrawManifold( &m, transform1.p, transform2.p ); + + offset = b2Add( offset, increment ); + } + + // capsule-capsule + { + b2Capsule capsule1 = { { -0.5f, 0.0f }, { 0.5f, 0.0 }, 0.25f }; + b2Capsule capsule2 = { { 0.25f, 0.0f }, { 1.0f, 0.0 }, 0.1f }; + + b2Transform transform1 = { offset, b2Rot_identity }; + b2Transform transform2 = { b2Add( m_transform.p, offset ), m_transform.q }; + + b2Manifold m = b2CollideCapsules( &capsule1, transform1, &capsule2, transform2 ); + + b2Vec2 v1 = b2TransformPoint( transform1, capsule1.center1 ); + b2Vec2 v2 = b2TransformPoint( transform1, capsule1.center2 ); + g_draw.DrawSolidCapsule( v1, v2, capsule1.radius, color1 ); + + v1 = b2TransformPoint( transform2, capsule2.center1 ); + v2 = b2TransformPoint( transform2, capsule2.center2 ); + g_draw.DrawSolidCapsule( v1, v2, capsule2.radius, color2 ); + + DrawManifold( &m, transform1.p, transform2.p ); + + offset = b2Add( offset, increment ); + } + + // box-capsule + { + b2Capsule capsule = { { -0.4f, 0.0f }, { -0.1f, 0.0f }, 0.1f }; + b2Polygon box = b2MakeOffsetBox( 0.25f, 1.0f, { 1.0f, -1.0f }, b2MakeRot(0.25f * b2_pi) ); + + b2Transform transform1 = { offset, b2Rot_identity }; + b2Transform transform2 = { b2Add( m_transform.p, offset ), m_transform.q }; + + b2Manifold m = b2CollidePolygonAndCapsule( &box, transform1, &capsule, transform2 ); + + g_draw.DrawSolidPolygon( transform1, box.vertices, box.count, box.radius, color1 ); + + b2Vec2 v1 = b2TransformPoint( transform2, capsule.center1 ); + b2Vec2 v2 = b2TransformPoint( transform2, capsule.center2 ); + g_draw.DrawSolidCapsule( v1, v2, capsule.radius, color2 ); + + DrawManifold( &m, transform1.p, transform2.p ); + + offset = b2Add( offset, increment ); + } + + // segment-capsule + { + b2Segment segment = { { -1.0f, 0.0f }, { 1.0f, 0.0 } }; + b2Capsule capsule = { { -0.5f, 0.0f }, { 0.5f, 0.0 }, 0.25f }; + + b2Transform transform1 = { offset, b2Rot_identity }; + b2Transform transform2 = { b2Add( m_transform.p, offset ), m_transform.q }; + + b2Manifold m = b2CollideSegmentAndCapsule( &segment, transform1, &capsule, transform2 ); + + b2Vec2 p1 = b2TransformPoint( transform1, segment.point1 ); + b2Vec2 p2 = b2TransformPoint( transform1, segment.point2 ); + g_draw.DrawSegment( p1, p2, color1 ); + + p1 = b2TransformPoint( transform2, capsule.center1 ); + p2 = b2TransformPoint( transform2, capsule.center2 ); + g_draw.DrawSolidCapsule( p1, p2, capsule.radius, color2 ); + + DrawManifold( &m, transform1.p, transform2.p ); + + offset = b2Add( offset, increment ); + } + + offset = { -10.0f, 0.0f }; + + // box-box + { + b2Polygon box1 = b2MakeBox( 2.0f, 0.1f ); + b2Polygon box = b2MakeSquare( 0.25f ); + + b2Transform transform1 = { offset, b2Rot_identity }; + b2Transform transform2 = { b2Add( m_transform.p, offset ), m_transform.q }; + // b2Transform transform2 = {b2Add({0.0f, -0.1f}, offset), {0.0f, 1.0f}}; + + b2Manifold m = b2CollidePolygons( &box1, transform1, &box, transform2 ); + + g_draw.DrawSolidPolygon( transform1, box1.vertices, box1.count, box1.radius, color1 ); + g_draw.DrawSolidPolygon( transform2, box.vertices, box.count, box.radius, color2 ); + + DrawManifold( &m, transform1.p, transform2.p ); + + offset = b2Add( offset, increment ); + } + + // box-rox + { + b2Polygon box = b2MakeSquare( 0.5f ); + float h = 0.5f - m_round; + b2Polygon rox = b2MakeRoundedBox( h, h, m_round ); + + b2Transform transform1 = { offset, b2Rot_identity }; + b2Transform transform2 = { b2Add( m_transform.p, offset ), m_transform.q }; + // b2Transform transform2 = {b2Add({0.0f, -0.1f}, offset), {0.0f, 1.0f}}; + + b2Manifold m = b2CollidePolygons( &box, transform1, &rox, transform2 ); + + g_draw.DrawSolidPolygon( transform1, box.vertices, box.count, box.radius, color1 ); + g_draw.DrawSolidPolygon( transform2, rox.vertices, rox.count, rox.radius, color2 ); + + DrawManifold( &m, transform1.p, transform2.p ); + + offset = b2Add( offset, increment ); + } + + // rox-rox + { + float h = 0.5f - m_round; + b2Polygon rox = b2MakeRoundedBox( h, h, m_round ); + + b2Transform transform1 = { offset, b2Rot_identity }; + b2Transform transform2 = { b2Add( m_transform.p, offset ), m_transform.q }; + // b2Transform transform1 = {{6.48024225f, 2.07872653f}, {-0.938356698f, 0.345668465f}}; + // b2Transform transform2 = {{5.52862263f, 2.51146317f}, {-0.859374702f, -0.511346340f}}; + + b2Manifold m = b2CollidePolygons( &rox, transform1, &rox, transform2 ); + + g_draw.DrawSolidPolygon( transform1, rox.vertices, rox.count, rox.radius, color1 ); + g_draw.DrawSolidPolygon( transform2, rox.vertices, rox.count, rox.radius, color2 ); + + DrawManifold( &m, transform1.p, transform2.p ); + + offset = b2Add( offset, increment ); + } + + // segment-rox + { + b2Segment segment = { { -1.0f, 0.0f }, { 1.0f, 0.0 } }; + float h = 0.5f - m_round; + b2Polygon rox = b2MakeRoundedBox( h, h, m_round ); + + b2Transform transform1 = { offset, b2Rot_identity }; + b2Transform transform2 = { b2Add( m_transform.p, offset ), m_transform.q }; + // b2Transform transform2 = {b2Add({-1.44583416f, 0.397352695f}, offset), m_transform.q}; + + b2Manifold m = b2CollideSegmentAndPolygon( &segment, transform1, &rox, transform2 ); + + b2Vec2 p1 = b2TransformPoint( transform1, segment.point1 ); + b2Vec2 p2 = b2TransformPoint( transform1, segment.point2 ); + g_draw.DrawSegment( p1, p2, color1 ); + g_draw.DrawSolidPolygon( transform2, rox.vertices, rox.count, rox.radius, color2 ); + + DrawManifold( &m, transform1.p, transform2.p ); + + offset = b2Add( offset, increment ); + } + + // wox-wox + { + b2Polygon wox = b2MakePolygon( &m_wedge, m_round ); + + b2Transform transform1 = { offset, b2Rot_identity }; + b2Transform transform2 = { b2Add( m_transform.p, offset ), m_transform.q }; + // b2Transform transform2 = {b2Add({0.0f, -0.1f}, offset), {0.0f, 1.0f}}; + + b2Manifold m = b2CollidePolygons( &wox, transform1, &wox, transform2 ); + + g_draw.DrawSolidPolygon( transform1, wox.vertices, wox.count, wox.radius, color1 ); + g_draw.DrawSolidPolygon( transform1, wox.vertices, wox.count, 0.0f, color1 ); + g_draw.DrawSolidPolygon( transform2, wox.vertices, wox.count, wox.radius, color2 ); + g_draw.DrawSolidPolygon( transform2, wox.vertices, wox.count, 0.0f, color2 ); + + DrawManifold( &m, transform1.p, transform2.p ); + + offset = b2Add( offset, increment ); + } + + // wox-wox + { + b2Vec2 p1s[3] = { { 0.175740838, 0.224936664 }, { -0.301293969, 0.194021404 }, { -0.105151534, -0.432157338 } }; + b2Vec2 p2s[3] = { { -0.427884758, -0.225028217 }, { 0.0566576123, -0.128772855 }, { 0.176625848, 0.338923335 } }; + + b2Hull h1 = b2ComputeHull( p1s, 3 ); + b2Hull h2 = b2ComputeHull( p2s, 3 ); + b2Polygon w1 = b2MakePolygon( &h1, 0.158798501 ); + b2Polygon w2 = b2MakePolygon( &h2, 0.205900759 ); + + b2Transform transform1 = { offset, b2Rot_identity }; + b2Transform transform2 = { b2Add( m_transform.p, offset ), m_transform.q }; + // b2Transform transform2 = {b2Add({0.0f, -0.1f}, offset), {0.0f, 1.0f}}; + + b2Manifold m = b2CollidePolygons( &w1, transform1, &w2, transform2 ); + + g_draw.DrawSolidPolygon( transform1, w1.vertices, w1.count, w1.radius, color1 ); + g_draw.DrawSolidPolygon( transform1, w1.vertices, w1.count, 0.0f, color1 ); + g_draw.DrawSolidPolygon( transform2, w2.vertices, w2.count, w2.radius, color2 ); + g_draw.DrawSolidPolygon( transform2, w2.vertices, w2.count, 0.0f, color2 ); + + DrawManifold( &m, transform1.p, transform2.p ); + + offset = b2Add( offset, increment ); + } + + offset = { -10.0f, 5.0f }; + + // chain-segment vs circle + { + b2ChainSegment segment = { { 2.0f, 1.0f }, { { 1.0f, 1.0f }, { -1.0f, 0.0f } }, { -2.0f, 0.0f }, -1 }; + b2Circle circle = { { 0.0f, 0.0f }, 0.5f }; + + b2Transform transform1 = { offset, b2Rot_identity }; + b2Transform transform2 = { b2Add( m_transform.p, offset ), m_transform.q }; + + b2Manifold m = b2CollideChainSegmentAndCircle( &segment, transform1, &circle, transform2 ); + + b2Vec2 g1 = b2TransformPoint( transform1, segment.ghost1 ); + b2Vec2 g2 = b2TransformPoint( transform1, segment.ghost2 ); + b2Vec2 p1 = b2TransformPoint( transform1, segment.segment.point1 ); + b2Vec2 p2 = b2TransformPoint( transform1, segment.segment.point2 ); + g_draw.DrawSegment( g1, p1, b2_colorLightGray ); + g_draw.DrawSegment( p1, p2, color1 ); + g_draw.DrawSegment( p2, g2, b2_colorLightGray ); + g_draw.DrawSolidCircle( transform2, circle.center, circle.radius, color2 ); + + DrawManifold( &m, transform1.p, transform2.p ); + + offset.x += 2.0f * increment.x; + } + + // chain-segment vs rounded polygon + { + b2ChainSegment segment1 = { { 2.0f, 1.0f }, { { 1.0f, 1.0f }, { -1.0f, 0.0f } }, { -2.0f, 0.0f }, -1 }; + b2ChainSegment segment2 = { { 3.0f, 1.0f }, { { 2.0f, 1.0f }, { 1.0f, 1.0f } }, { -1.0f, 0.0f }, -1 }; + // b2ChainSegment segment1 = {{2.0f, 0.0f}, {{1.0f, 0.0f}, {-1.0f, 0.0f}}, {-2.0f, 0.0f}, -1}; + // b2ChainSegment segment2 = {{3.0f, 0.0f}, {{2.0f, 0.0f}, {1.0f, 0.0f}}, {-1.0f, 0.0f}, -1}; + // b2ChainSegment segment1 = {{0.5f, 1.0f}, {{0.0f, 2.0f}, {-0.5f, 1.0f}}, {-1.0f, 0.0f}, -1}; + // b2ChainSegment segment2 = {{1.0f, 0.0f}, {{0.5f, 1.0f}, {0.0f, 2.0f}}, {-0.5f, 1.0f}, -1}; + float h = 0.5f - m_round; + b2Polygon rox = b2MakeRoundedBox( h, h, m_round ); + + b2Transform transform1 = { offset, b2Rot_identity }; + b2Transform transform2 = { b2Add( m_transform.p, offset ), m_transform.q }; + + b2Manifold m1 = b2CollideChainSegmentAndPolygon( &segment1, transform1, &rox, transform2, &m_smgroxCache1 ); + b2Manifold m2 = b2CollideChainSegmentAndPolygon( &segment2, transform1, &rox, transform2, &m_smgroxCache2 ); + + { + b2Vec2 g1 = b2TransformPoint( transform1, segment1.ghost1 ); + b2Vec2 g2 = b2TransformPoint( transform1, segment1.ghost2 ); + b2Vec2 p1 = b2TransformPoint( transform1, segment1.segment.point1 ); + b2Vec2 p2 = b2TransformPoint( transform1, segment1.segment.point2 ); + g_draw.DrawSegment( p1, p2, color1 ); + g_draw.DrawPoint( p1, 4.0f, color1 ); + g_draw.DrawPoint( p2, 4.0f, color1 ); + g_draw.DrawSegment( p2, g2, b2_colorLightGray ); + } + + { + b2Vec2 g1 = b2TransformPoint( transform1, segment2.ghost1 ); + b2Vec2 g2 = b2TransformPoint( transform1, segment2.ghost2 ); + b2Vec2 p1 = b2TransformPoint( transform1, segment2.segment.point1 ); + b2Vec2 p2 = b2TransformPoint( transform1, segment2.segment.point2 ); + g_draw.DrawSegment( g1, p1, b2_colorLightGray ); + g_draw.DrawSegment( p1, p2, color1 ); + g_draw.DrawPoint( p1, 4.0f, color1 ); + g_draw.DrawPoint( p2, 4.0f, color1 ); + } + + g_draw.DrawSolidPolygon( transform2, rox.vertices, rox.count, rox.radius, color2 ); + g_draw.DrawPoint( b2TransformPoint( transform2, rox.centroid ), 5.0f, b2_colorGainsboro ); + + DrawManifold( &m1, transform1.p, transform2.p ); + DrawManifold( &m2, transform1.p, transform2.p ); + + offset.x += 2.0f * increment.x; + } + + // chain-segment vs capsule + { + b2ChainSegment segment1 = { { 2.0f, 1.0f }, { { 1.0f, 1.0f }, { -1.0f, 0.0f } }, { -2.0f, 0.0f }, -1 }; + b2ChainSegment segment2 = { { 3.0f, 1.0f }, { { 2.0f, 1.0f }, { 1.0f, 1.0f } }, { -1.0f, 0.0f }, -1 }; + b2Capsule capsule = { { -0.5f, 0.0f }, { 0.5f, 0.0 }, 0.25f }; + + b2Transform transform1 = { offset, b2Rot_identity }; + b2Transform transform2 = { b2Add( m_transform.p, offset ), m_transform.q }; + + b2Manifold m1 = b2CollideChainSegmentAndCapsule( &segment1, transform1, &capsule, transform2, &m_smgcapCache1 ); + b2Manifold m2 = b2CollideChainSegmentAndCapsule( &segment2, transform1, &capsule, transform2, &m_smgcapCache2 ); + + { + b2Vec2 g1 = b2TransformPoint( transform1, segment1.ghost1 ); + b2Vec2 g2 = b2TransformPoint( transform1, segment1.ghost2 ); + b2Vec2 p1 = b2TransformPoint( transform1, segment1.segment.point1 ); + b2Vec2 p2 = b2TransformPoint( transform1, segment1.segment.point2 ); + // g_draw.DrawSegment(g1, p1, b2_colorLightGray); + g_draw.DrawSegment( p1, p2, color1 ); + g_draw.DrawPoint( p1, 4.0f, color1 ); + g_draw.DrawPoint( p2, 4.0f, color1 ); + g_draw.DrawSegment( p2, g2, b2_colorLightGray ); + } + + { + b2Vec2 g1 = b2TransformPoint( transform1, segment2.ghost1 ); + b2Vec2 g2 = b2TransformPoint( transform1, segment2.ghost2 ); + b2Vec2 p1 = b2TransformPoint( transform1, segment2.segment.point1 ); + b2Vec2 p2 = b2TransformPoint( transform1, segment2.segment.point2 ); + g_draw.DrawSegment( g1, p1, b2_colorLightGray ); + g_draw.DrawSegment( p1, p2, color1 ); + g_draw.DrawPoint( p1, 4.0f, color1 ); + g_draw.DrawPoint( p2, 4.0f, color1 ); + // g_draw.DrawSegment(p2, g2, b2_colorLightGray); + } + + b2Vec2 p1 = b2TransformPoint( transform2, capsule.center1 ); + b2Vec2 p2 = b2TransformPoint( transform2, capsule.center2 ); + g_draw.DrawSolidCapsule( p1, p2, capsule.radius, color2 ); + + g_draw.DrawPoint( b2Lerp( p1, p2, 0.5f ), 5.0f, b2_colorGainsboro ); + + DrawManifold( &m1, transform1.p, transform2.p ); + DrawManifold( &m2, transform1.p, transform2.p ); + + offset.x += 2.0f * increment.x; + } + } + + static Sample* Create( Settings& settings ) + { + return new Manifold( settings ); + } + + b2DistanceCache m_smgroxCache1; + b2DistanceCache m_smgroxCache2; + b2DistanceCache m_smgcapCache1; + b2DistanceCache m_smgcapCache2; + + b2Hull m_wedge; + + b2Transform m_transform; + float m_angle; + float m_round; + + b2Vec2 m_basePosition; + b2Vec2 m_startPoint; + float m_baseAngle; + + bool m_dragging; + bool m_rotating; + bool m_showIds; + bool m_showAnchors; + bool m_showSeparation; + bool m_enableCaching; +}; + +static int sampleManifoldIndex = RegisterSample( "Collision", "Manifold", Manifold::Create ); + +class SmoothManifold : public Sample +{ +public: + enum ShapeType + { + e_circleShape = 0, + e_boxShape + }; + + explicit SmoothManifold( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 2.0f, 20.0f }; + g_camera.m_zoom = 21.0f; + } + + m_shapeType = e_boxShape; + m_transform = { { 0.0f, 20.0f }, b2Rot_identity }; + m_angle = 0.0f; + m_round = 0.0f; + + m_startPoint = { 0.0f, 00.0f }; + m_basePosition = { 0.0f, 0.0f }; + m_baseAngle = 0.0f; + + m_dragging = false; + m_rotating = false; + m_showIds = false; + m_showAnchors = false; + m_showSeparation = false; + + // https://betravis.github.io/shape-tools/path-to-polygon/ + m_count = 36; + + b2Vec2 points[36]; + points[0] = { -20.58325, 14.54175 }; + points[1] = { -21.90625, 15.8645 }; + points[2] = { -24.552, 17.1875 }; + points[3] = { -27.198, 11.89575 }; + points[4] = { -29.84375, 15.8645 }; + points[5] = { -29.84375, 21.15625 }; + points[6] = { -25.875, 23.802 }; + points[7] = { -20.58325, 25.125 }; + points[8] = { -25.875, 29.09375 }; + points[9] = { -20.58325, 31.7395 }; + points[10] = { -11.0089998, 23.2290001 }; + points[11] = { -8.67700005, 21.15625 }; + points[12] = { -6.03125, 21.15625 }; + points[13] = { -7.35424995, 29.09375 }; + points[14] = { -3.38549995, 29.09375 }; + points[15] = { 1.90625, 30.41675 }; + points[16] = { 5.875, 17.1875 }; + points[17] = { 11.16675, 25.125 }; + points[18] = { 9.84375, 29.09375 }; + points[19] = { 13.8125, 31.7395 }; + points[20] = { 21.75, 30.41675 }; + points[21] = { 28.3644981, 26.448 }; + points[22] = { 25.71875, 18.5105 }; + points[23] = { 24.3957481, 13.21875 }; + points[24] = { 17.78125, 11.89575 }; + points[25] = { 15.1355, 7.92700005 }; + points[26] = { 5.875, 9.25 }; + points[27] = { 1.90625, 11.89575 }; + points[28] = { -3.25, 11.89575 }; + points[29] = { -3.25, 9.9375 }; + points[30] = { -4.70825005, 9.25 }; + points[31] = { -8.67700005, 9.25 }; + points[32] = { -11.323, 11.89575 }; + points[33] = { -13.96875, 11.89575 }; + points[34] = { -15.29175, 14.54175 }; + points[35] = { -19.2605, 14.54175 }; + + m_segments = (b2ChainSegment*)malloc( m_count * sizeof( b2ChainSegment ) ); + + for ( int i = 0; i < m_count; ++i ) + { + int i0 = i > 0 ? i - 1 : m_count - 1; + int i1 = i; + int i2 = i1 < m_count - 1 ? i1 + 1 : 0; + int i3 = i2 < m_count - 1 ? i2 + 1 : 0; + + b2Vec2 g1 = points[i0]; + b2Vec2 p1 = points[i1]; + b2Vec2 p2 = points[i2]; + b2Vec2 g2 = points[i3]; + + m_segments[i] = { g1, { p1, p2 }, g2, -1 }; + } + } + + virtual ~SmoothManifold() override + { + free( m_segments ); + } + + void UpdateUI() override + { + float height = 290.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 180.0f, height ) ); + + ImGui::Begin( "Smooth Manifold", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize ); + ImGui::PushItemWidth( 100.0f ); + + { + const char* shapeTypes[] = { "Circle", "Box" }; + int shapeType = int( m_shapeType ); + ImGui::Combo( "Shape", &shapeType, shapeTypes, IM_ARRAYSIZE( shapeTypes ) ); + m_shapeType = ShapeType( shapeType ); + } + + ImGui::SliderFloat( "x Offset", &m_transform.p.x, -2.0f, 2.0f, "%.2f" ); + ImGui::SliderFloat( "y Offset", &m_transform.p.y, -2.0f, 2.0f, "%.2f" ); + + if ( ImGui::SliderFloat( "Angle", &m_angle, -b2_pi, b2_pi, "%.2f" ) ) + { + m_transform.q = b2MakeRot( m_angle ); + } + + ImGui::SliderFloat( "Round", &m_round, 0.0f, 0.4f, "%.1f" ); + ImGui::Checkbox( "Show Ids", &m_showIds ); + ImGui::Checkbox( "Show Separation", &m_showSeparation ); + ImGui::Checkbox( "Show Anchors", &m_showAnchors ); + + if ( ImGui::Button( "Reset" ) ) + { + m_transform = b2Transform_identity; + m_angle = 0.0f; + } + + ImGui::Separator(); + + ImGui::Text( "mouse button 1: drag" ); + ImGui::Text( "mouse button 1 + shift: rotate" ); + + ImGui::PopItemWidth(); + ImGui::End(); + } + + void MouseDown( b2Vec2 p, int button, int mods ) override + { + if ( button == GLFW_MOUSE_BUTTON_1 ) + { + if ( mods == 0 && m_rotating == false ) + { + m_dragging = true; + m_startPoint = p; + m_basePosition = m_transform.p; + } + else if ( mods == GLFW_MOD_SHIFT && m_dragging == false ) + { + m_rotating = true; + m_startPoint = p; + m_baseAngle = m_angle; + } + } + } + + void MouseUp( b2Vec2, int button ) override + { + if ( button == GLFW_MOUSE_BUTTON_1 ) + { + m_dragging = false; + m_rotating = false; + } + } + + void MouseMove( b2Vec2 p ) override + { + if ( m_dragging ) + { + m_transform.p.x = m_basePosition.x + ( p.x - m_startPoint.x ); + m_transform.p.y = m_basePosition.y + ( p.y - m_startPoint.y ); + } + else if ( m_rotating ) + { + float dx = p.x - m_startPoint.x; + m_angle = b2ClampFloat( m_baseAngle + 1.0f * dx, -b2_pi, b2_pi ); + m_transform.q = b2MakeRot( m_angle ); + } + } + + void DrawManifold( const b2Manifold* manifold ) + { + for ( int i = 0; i < manifold->pointCount; ++i ) + { + const b2ManifoldPoint* mp = manifold->points + i; + + b2Vec2 p1 = mp->point; + b2Vec2 p2 = b2MulAdd( p1, 0.5f, manifold->normal ); + g_draw.DrawSegment( p1, p2, b2_colorWhite ); + + if ( m_showAnchors ) + { + g_draw.DrawPoint( p1, 5.0f, b2_colorGreen ); + } + else + { + g_draw.DrawPoint( p1, 5.0f, b2_colorGreen ); + } + + if ( m_showIds ) + { + // uint32_t indexA = mp->id >> 8; + // uint32_t indexB = 0xFF & mp->id; + b2Vec2 p = { p1.x + 0.05f, p1.y - 0.02f }; + g_draw.DrawString( p, "0x%04x", mp->id ); + } + + if ( m_showSeparation ) + { + b2Vec2 p = { p1.x + 0.05f, p1.y + 0.03f }; + g_draw.DrawString( p, "%.3f", mp->separation ); + } + } + } + + void Step( Settings& ) override + { + b2HexColor color1 = b2_colorYellow; + b2HexColor color2 = b2_colorMagenta; + + b2Transform transform1 = b2Transform_identity; + b2Transform transform2 = m_transform; + + for ( int i = 0; i < m_count; ++i ) + { + const b2ChainSegment* segment = m_segments + i; + b2Vec2 p1 = b2TransformPoint( transform1, segment->segment.point1 ); + b2Vec2 p2 = b2TransformPoint( transform1, segment->segment.point2 ); + g_draw.DrawSegment( p1, p2, color1 ); + g_draw.DrawPoint( p1, 4.0f, color1 ); + } + + // chain-segment vs circle + if ( m_shapeType == e_circleShape ) + { + b2Circle circle = { { 0.0f, 0.0f }, 0.5f }; + g_draw.DrawSolidCircle( transform2, circle.center, circle.radius, color2 ); + + for ( int i = 0; i < m_count; ++i ) + { + const b2ChainSegment* segment = m_segments + i; + b2Manifold m = b2CollideChainSegmentAndCircle( segment, transform1, &circle, transform2 ); + DrawManifold( &m ); + } + } + else if ( m_shapeType == e_boxShape ) + { + float h = 0.5f - m_round; + b2Polygon rox = b2MakeRoundedBox( h, h, m_round ); + g_draw.DrawSolidPolygon( transform2, rox.vertices, rox.count, rox.radius, color2 ); + + for ( int i = 0; i < m_count; ++i ) + { + const b2ChainSegment* segment = m_segments + i; + b2DistanceCache cache = {}; + b2Manifold m = b2CollideChainSegmentAndPolygon( segment, transform1, &rox, transform2, &cache ); + DrawManifold( &m ); + } + } + } + + static Sample* Create( Settings& settings ) + { + return new SmoothManifold( settings ); + } + + ShapeType m_shapeType; + + b2ChainSegment* m_segments; + int m_count; + + b2Transform m_transform; + float m_angle; + float m_round; + + b2Vec2 m_basePosition; + b2Vec2 m_startPoint; + float m_baseAngle; + + bool m_dragging; + bool m_rotating; + bool m_showIds; + bool m_showAnchors; + bool m_showSeparation; +}; + +static int sampleSmoothManifoldIndex = RegisterSample( "Collision", "Smooth Manifold", SmoothManifold::Create ); + +class ShapeCast : public Sample +{ +public: + enum + { + e_vertexCount = 8 + }; + + explicit ShapeCast( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { -1.5f, 1.0f }; + g_camera.m_zoom = 25.0f * 0.2f; + } + +#if 0 + // box swept against a triangle + m_vAs[0] = {-0.5f, 1.0f}; + m_vAs[1] = {0.5f, 1.0f}; + m_vAs[2] = {0.0f, 0.0f}; + m_countA = 3; + m_radiusA = 0.0f; + + m_vBs[0] = {-0.5f, -0.5f}; + m_vBs[1] = {0.5f, -0.5f}; + m_vBs[2] = {0.5f, 0.5f}; + m_vBs[3] = {-0.5f, 0.5f}; + m_countB = 4; + m_radiusB = 0.0f; + + m_transformA.p = {0.0f, 0.25f}; + m_transformA.q = b2Rot_identity; + m_transformB.p = {-4.0f, 0.0f}; + m_transformB.q = b2Rot_identity; + m_translationB = {8.0f, 0.0f}; +#elif 1 + // box swept against a segment + m_vAs[0] = { -2.0f, 0.0f }; + m_vAs[1] = { 2.0f, 0.0f }; + m_countA = 2; + m_radiusA = 0.0f; + + m_vBs[0] = { -0.25f, -0.25f }; + m_vBs[1] = { 0.25f, -0.25f }; + m_vBs[2] = { 0.25f, 0.25f }; + m_vBs[3] = { -0.25f, 0.25f }; + m_countB = 4; + m_radiusB = 0.25f; + + m_transformA.p = { 0.0f, 0.0 }; + m_transformA.q = b2MakeRot( 0.25f * b2_pi ); + m_transformB.p = { -8.0f, 0.0f }; + m_transformB.q = b2Rot_identity; + m_translationB = { 8.0f, 0.0f }; +#elif 0 + // A point swept against a box + m_vAs[0] = { -0.5f, -0.5f }; + m_vAs[1] = { 0.5f, -0.5f }; + m_vAs[2] = { 0.5f, 0.5f }; + m_vAs[3] = { -0.5f, 0.5f }; + m_countA = 4; + m_radiusA = 0.0f; + + m_vBs[0] = { 0.0f, 0.0f }; + m_countB = 1; + m_radiusB = 0.0f; + + m_transformA.p = { 0.0f, 0.0f }; + m_transformA.q = b2Rot_identity; + m_transformB.p = { -1.0f, 0.0f }; + m_transformB.q = b2Rot_identity; + m_translationB = { 1.0f, 0.0f }; +#elif 0 + m_vAs[0] = { 0.0f, 0.0f }; + m_countA = 1; + m_radiusA = 0.5f; + + m_vBs[0] = { 0.0f, 0.0f }; + m_countB = 1; + m_radiusB = 0.5f; + + m_transformA.p = { 0.0f, 0.25f }; + m_transformA.q = b2Rot_identity; + m_transformB.p = { -4.0f, 0.0f }; + m_transformB.q = b2Rot_identity; + m_translationB = { 8.0f, 0.0f }; +#else + m_vAs[0] = { 0.0f, 0.0f }; + m_vAs[1] = { 2.0f, 0.0f }; + m_countA = 2; + m_radiusA = 0.0f; + + m_vBs[0] = { 0.0f, 0.0f }; + m_countB = 1; + m_radiusB = 0.25f; + + // Initial overlap + m_transformA.p = b2Vec2_zero; + m_transformA.q = b2Rot_identity; + m_transformB.p = { -0.244360745f, 0.05999358f }; + m_transformB.q = b2Rot_identity; + m_translationB = { 0.0f, 0.0399999991f }; +#endif + + m_rayDrag = false; + } + + static Sample* Create( Settings& settings ) + { + return new ShapeCast( settings ); + } + + void MouseDown( b2Vec2 p, int button, int mods ) override + { + if ( button == GLFW_MOUSE_BUTTON_1 ) + { + m_transformB.p = p; + m_rayDrag = true; + } + } + + void MouseUp( b2Vec2, int button ) override + { + if ( button == GLFW_MOUSE_BUTTON_1 ) + { + m_rayDrag = false; + } + } + + void MouseMove( b2Vec2 p ) override + { + if ( m_rayDrag ) + { + m_translationB = b2Sub( p, m_transformB.p ); + } + } + + void Step( Settings& settings ) override + { + Sample::Step( settings ); + + b2ShapeCastPairInput input = { 0 }; + input.proxyA = b2MakeProxy( m_vAs, m_countA, m_radiusA ); + input.proxyB = b2MakeProxy( m_vBs, m_countB, m_radiusB ); + input.transformA = m_transformA; + input.transformB = m_transformB; + input.translationB = m_translationB; + input.maxFraction = 1.0f; + + b2CastOutput output = b2ShapeCast( &input ); + + b2Transform transformB2; + transformB2.q = m_transformB.q; + transformB2.p = b2MulAdd( m_transformB.p, output.fraction, input.translationB ); + + b2DistanceInput distanceInput; + distanceInput.proxyA = b2MakeProxy( m_vAs, m_countA, m_radiusA ); + distanceInput.proxyB = b2MakeProxy( m_vBs, m_countB, m_radiusB ); + distanceInput.transformA = m_transformA; + distanceInput.transformB = transformB2; + distanceInput.useRadii = false; + b2DistanceCache distanceCache; + distanceCache.count = 0; + b2DistanceOutput distanceOutput = b2ShapeDistance( &distanceCache, &distanceInput, nullptr, 0 ); + + g_draw.DrawString( 5, m_textLine, "hit = %s, iters = %d, lambda = %g, distance = %g", output.hit ? "true" : "false", + output.iterations, output.fraction, distanceOutput.distance ); + m_textLine += m_textIncrement; + + b2Vec2 vertices[b2_maxPolygonVertices]; + + for ( int i = 0; i < m_countA; ++i ) + { + vertices[i] = b2TransformPoint( m_transformA, m_vAs[i] ); + } + + if ( m_countA == 1 ) + { + if ( m_radiusA > 0.0f ) + { + g_draw.DrawSolidCircle( b2Transform_identity, vertices[0], m_radiusA, b2_colorGray9 ); + } + else + { + g_draw.DrawPoint( vertices[0], 5.0f, b2_colorGray9 ); + } + } + else + { + g_draw.DrawSolidPolygon( b2Transform_identity, vertices, m_countA, m_radiusA, b2_colorGray9 ); + } + + for ( int i = 0; i < m_countB; ++i ) + { + vertices[i] = b2TransformPoint( m_transformB, m_vBs[i] ); + } + + if ( m_countB == 1 ) + { + if ( m_radiusB > 0.0f ) + { + g_draw.DrawSolidCircle( b2Transform_identity, vertices[0], m_radiusB, b2_colorGreen ); + } + else + { + g_draw.DrawPoint( vertices[0], 5.0f, b2_colorGreen ); + } + } + else + { + g_draw.DrawSolidPolygon( b2Transform_identity, vertices, m_countB, m_radiusB, b2_colorGreen ); + } + + for ( int i = 0; i < m_countB; ++i ) + { + vertices[i] = b2TransformPoint( transformB2, m_vBs[i] ); + } + + if ( m_countB == 1 ) + { + if ( m_radiusB > 0.0f ) + { + g_draw.DrawSolidCircle( b2Transform_identity, vertices[0], m_radiusB, b2_colorOrange ); + } + else + { + g_draw.DrawPoint( vertices[0], 5.0f, b2_colorOrange ); + } + } + else + { + g_draw.DrawSolidPolygon( b2Transform_identity, vertices, m_countB, m_radiusB, b2_colorOrange ); + } + + if ( output.hit ) + { + b2Vec2 p1 = output.point; + g_draw.DrawPoint( p1, 10.0f, b2_colorRed ); + b2Vec2 p2 = b2MulAdd( p1, 1.0f, output.normal ); + g_draw.DrawSegment( p1, p2, b2_colorRed ); + } + + g_draw.DrawSegment( m_transformB.p, b2Add( m_transformB.p, m_translationB ), b2_colorGray ); + } + + b2Vec2 m_vAs[b2_maxPolygonVertices]; + int m_countA; + float m_radiusA; + + b2Vec2 m_vBs[b2_maxPolygonVertices]; + int m_countB; + float m_radiusB; + + b2Transform m_transformA; + b2Transform m_transformB; + b2Vec2 m_translationB; + bool m_rayDrag; +}; + +static int sampleShapeCast = RegisterSample( "Collision", "Shape Cast", ShapeCast::Create ); + +class TimeOfImpact : public Sample +{ +public: + explicit TimeOfImpact( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.6f, 2.0f }; + g_camera.m_zoom = 25.0f * 0.18f; + } + } + + static Sample* Create( Settings& settings ) + { + return new TimeOfImpact( settings ); + } + + void Step( Settings& settings ) override + { + Sample::Step( settings ); + + b2Sweep sweepA = { b2Vec2_zero, { 0.0f, 0.0f }, { 0.0f, 0.0f }, b2Rot_identity, b2Rot_identity }; + b2Sweep sweepB = { b2Vec2_zero, { 2.0f, 4.0f }, { 2.0f, 4.0f }, b2Rot_identity, b2MakeRot( -0.25f * b2_pi ) }; + + b2TOIInput input; + input.proxyA = b2MakeProxy( m_verticesA, m_countA, 0.0f ); + input.proxyB = b2MakeProxy( m_verticesB, m_countB, 0.0f ); + input.sweepA = sweepA; + input.sweepB = sweepB; + input.tMax = 1.0f; + + b2TOIOutput output = b2TimeOfImpact( &input ); + + g_draw.DrawString( 5, m_textLine, "toi = %g", output.t ); + m_textLine += m_textIncrement; + + // g_draw.DrawString(5, m_textLine, "max toi iters = %d, max root iters = %d", b2_toiMaxIters, + // b2_toiMaxRootIters); + m_textLine += m_textIncrement; + + b2Vec2 vertices[b2_maxPolygonVertices]; + + // Draw A + b2Transform transformA = b2GetSweepTransform( &sweepA, 0.0f ); + for ( int i = 0; i < m_countA; ++i ) + { + vertices[i] = b2TransformPoint( transformA, m_verticesA[i] ); + } + g_draw.DrawPolygon( vertices, m_countA, b2_colorGray ); + + // Draw B at t = 0 + b2Transform transformB = b2GetSweepTransform( &sweepB, 0.0f ); + for ( int i = 0; i < m_countB; ++i ) + { + vertices[i] = b2TransformPoint( transformB, m_verticesB[i] ); + } + g_draw.DrawPolygon( vertices, m_countB, b2_colorGreen ); + + // Draw B at t = hit_time + transformB = b2GetSweepTransform( &sweepB, output.t ); + for ( int i = 0; i < m_countB; ++i ) + { + vertices[i] = b2TransformPoint( transformB, m_verticesB[i] ); + } + g_draw.DrawPolygon( vertices, m_countB, b2_colorOrange ); + + // Draw B at t = 1 + transformB = b2GetSweepTransform( &sweepB, 1.0f ); + for ( int i = 0; i < m_countB; ++i ) + { + vertices[i] = b2TransformPoint( transformB, m_verticesB[i] ); + } + g_draw.DrawPolygon( vertices, m_countB, b2_colorRed ); + + if ( output.state == b2_toiStateHit ) + { + b2DistanceInput dinput; + dinput.proxyA = input.proxyA; + dinput.proxyB = input.proxyB; + dinput.transformA = b2GetSweepTransform( &sweepA, output.t ); + dinput.transformB = b2GetSweepTransform( &sweepB, output.t ); + dinput.useRadii = false; + b2DistanceCache cache = { 0 }; + b2DistanceOutput doutput = b2ShapeDistance( &cache, &dinput, nullptr, 0 ); + g_draw.DrawString( 5, m_textLine, "distance = %g", doutput.distance ); + m_textLine += m_textIncrement; + } + +#if 0 + for (float t = 0.0f; t < 1.0f; t += 0.1f) + { + transformB = b2GetSweepTransform(&sweepB, t); + for (int i = 0; i < m_countB; ++i) + { + vertices[i] = b2TransformPoint(transformB, m_verticesB[i]); + } + g_draw.DrawPolygon(vertices, m_countB, {0.3f, 0.3f, 0.3f}); + } +#endif + } + + b2Vec2 m_verticesA[4] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { 1.0f, 5.0f }, { -1.0f, 5.0f } }; + b2Vec2 m_verticesB[4] = { { -0.5f, -4.0f }, { 0.0f, -4.0f }, { 0.0f, 0.0f }, { -0.5f, 0.0f } }; + int m_countA = ARRAY_COUNT( m_verticesA ); + int m_countB = ARRAY_COUNT( m_verticesB ); +}; + +static int sampleTimeOfImpact = RegisterSample( "Collision", "Time of Impact", TimeOfImpact::Create ); diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/sample_continuous.cpp b/tests/cpp-tests/Source/Box2DTestBed/samples/sample_continuous.cpp new file mode 100644 index 000000000000..ac35e07c8974 --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/sample_continuous.cpp @@ -0,0 +1,955 @@ +// SPDX-FileCopyrightText: 2022 Erin Catto +// SPDX-License-Identifier: MIT + +#include "draw.h" +#include "sample.h" +#include "settings.h" + +#include "box2d/box2d.h" +#include "box2d/math_functions.h" + +#include +#include + +// This tests continuous collision robustness and also demonstrates the speed limits imposed +// by b2_maxTranslation and b2_maxRotation. +struct HitEvent +{ + b2Vec2 point; + float speed; + int stepIndex; +}; + +class BounceHouse : public Sample +{ +public: + enum ShapeType + { + e_circleShape = 0, + e_capsuleShape, + e_boxShape + }; + + explicit BounceHouse( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, 0.0f }; + g_camera.m_zoom = 25.0f * 0.45f; + } + + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + { + b2Segment segment = { { -10.0f, -10.0f }, { 10.0f, -10.0f } }; + b2CreateSegmentShape( groundId, &shapeDef, &segment ); + } + + { + b2Segment segment = { { 10.0f, -10.0f }, { 10.0f, 10.0f } }; + b2CreateSegmentShape( groundId, &shapeDef, &segment ); + } + + { + b2Segment segment = { { 10.0f, 10.0f }, { -10.0f, 10.0f } }; + b2CreateSegmentShape( groundId, &shapeDef, &segment ); + } + + { + b2Segment segment = { { -10.0f, 10.0f }, { -10.0f, -10.0f } }; + b2CreateSegmentShape( groundId, &shapeDef, &segment ); + } + + m_shapeType = e_circleShape; + m_bodyId = b2_nullBodyId; + m_enableHitEvents = true; + + memset( m_hitEvents, 0, sizeof( m_hitEvents ) ); + + Launch(); + } + + void Launch() + { + if ( B2_IS_NON_NULL( m_bodyId ) ) + { + b2DestroyBody( m_bodyId ); + } + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.linearVelocity = { 10.0f, 20.0f }; + bodyDef.position = { 0.0f, 0.0f }; + bodyDef.gravityScale = 0.0f; + + // Circle shapes centered on the body can spin fast without risk of tunnelling. + bodyDef.allowFastRotation = m_shapeType == e_circleShape; + + m_bodyId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 1.0f; + shapeDef.restitution = 1.2f; + shapeDef.friction = 0.3f; + shapeDef.enableHitEvents = m_enableHitEvents; + + if ( m_shapeType == e_circleShape ) + { + b2Circle circle = { { 0.0f, 0.0f }, 0.5f }; + b2CreateCircleShape( m_bodyId, &shapeDef, &circle ); + } + else if ( m_shapeType == e_capsuleShape ) + { + b2Capsule capsule = { { -0.5f, 0.0f }, { 0.5f, 0.0 }, 0.25f }; + b2CreateCapsuleShape( m_bodyId, &shapeDef, &capsule ); + } + else + { + float h = 0.1f; + b2Polygon box = b2MakeBox( 20.0f * h, h ); + b2CreatePolygonShape( m_bodyId, &shapeDef, &box ); + } + } + + void UpdateUI() override + { + float height = 100.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 240.0f, height ) ); + + ImGui::Begin( "Bounce House", nullptr, ImGuiWindowFlags_NoResize ); + + const char* shapeTypes[] = { "Circle", "Capsule", "Box" }; + int shapeType = int( m_shapeType ); + if ( ImGui::Combo( "Shape", &shapeType, shapeTypes, IM_ARRAYSIZE( shapeTypes ) ) ) + { + m_shapeType = ShapeType( shapeType ); + Launch(); + } + + if ( ImGui::Checkbox( "hit events", &m_enableHitEvents ) ) + { + b2Body_EnableHitEvents( m_bodyId, m_enableHitEvents ); + } + + ImGui::End(); + } + + void Step( Settings& settings ) override + { + Sample::Step( settings ); + + b2ContactEvents events = b2World_GetContactEvents( m_worldId ); + for ( int i = 0; i < events.hitCount; ++i ) + { + b2ContactHitEvent* event = events.hitEvents + i; + + HitEvent* e = m_hitEvents + 0; + for ( int j = 1; j < 4; ++j ) + { + if ( m_hitEvents[j].stepIndex < e->stepIndex ) + { + e = m_hitEvents + j; + } + } + + e->point = event->point; + e->speed = event->approachSpeed; + e->stepIndex = m_stepCount; + } + + for ( int i = 0; i < 4; ++i ) + { + HitEvent* e = m_hitEvents + i; + if ( e->stepIndex > 0 && m_stepCount <= e->stepIndex + 30 ) + { + g_draw.DrawCircle( e->point, 0.1f, b2_colorOrangeRed ); + g_draw.DrawString( e->point, "%.1f", e->speed ); + } + } + } + + static Sample* Create( Settings& settings ) + { + return new BounceHouse( settings ); + } + + HitEvent m_hitEvents[4]; + b2BodyId m_bodyId; + ShapeType m_shapeType; + bool m_enableHitEvents; +}; + +static int sampleBounceHouse = RegisterSample( "Continuous", "Bounce House", BounceHouse::Create ); + +class FastChain : public Sample +{ +public: + explicit FastChain( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, 0.0f }; + g_camera.m_zoom = 25.0f * 0.35f; + } + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.position = { 0.0f, -6.0f }; + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2Vec2 points[4] = { { -10.0f, -2.0f }, { 10.0f, -2.0f }, { 10.0f, 1.0f }, { -10.0f, 1.0f } }; + + b2ChainDef chainDef = b2DefaultChainDef(); + chainDef.points = points; + chainDef.count = 4; + chainDef.isLoop = true; + + b2CreateChain( groundId, &chainDef ); + + m_bodyId = b2_nullBodyId; + + Launch(); + } + + void Launch() + { + if ( B2_IS_NON_NULL( m_bodyId ) ) + { + b2DestroyBody( m_bodyId ); + } + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.linearVelocity = { 0.0f, -200.0f }; + bodyDef.position = { 0.0f, 10.0f }; + bodyDef.gravityScale = 1.0f; + m_bodyId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Circle circle = { { 0.0f, 0.0f }, 0.5f }; + b2CreateCircleShape( m_bodyId, &shapeDef, &circle ); + } + + void UpdateUI() override + { + float height = 70.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 240.0f, height ) ); + + ImGui::Begin( "Fast Chain", nullptr, ImGuiWindowFlags_NoResize ); + + if ( ImGui::Button( "Launch" ) ) + { + Launch(); + } + + ImGui::End(); + } + + static Sample* Create( Settings& settings ) + { + return new FastChain( settings ); + } + + b2BodyId m_bodyId; +}; + +static int sampleFastChainHouse = RegisterSample( "Continuous", "Fast Chain", FastChain::Create ); + +class SkinnyBox : public Sample +{ +public: + explicit SkinnyBox( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 1.0f, 5.0f }; + g_camera.m_zoom = 25.0f * 0.25f; + } + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2Segment segment = { { -10.0f, 0.0f }, { 10.0f, 0.0f } }; + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.friction = 0.9f; + b2CreateSegmentShape( groundId, &shapeDef, &segment ); + + b2Polygon box = b2MakeOffsetBox( 0.1f, 1.0f, { 0.0f, 1.0f }, b2Rot_identity ); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + } + + m_autoTest = false; + m_bullet = false; + m_capsule = false; + + m_bodyId = b2_nullBodyId; + m_bulletId = b2_nullBodyId; + + Launch(); + } + + void Launch() + { + if ( B2_IS_NON_NULL( m_bodyId ) ) + { + b2DestroyBody( m_bodyId ); + } + + if ( B2_IS_NON_NULL( m_bulletId ) ) + { + b2DestroyBody( m_bulletId ); + } + + m_angularVelocity = RandomFloat( -50.0f, 50.0f ); + // m_angularVelocity = -30.6695766f; + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = { 0.0f, 8.0f }; + bodyDef.angularVelocity = m_angularVelocity; + bodyDef.linearVelocity = { 0.0f, -100.0f }; + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 1.0f; + shapeDef.friction = 0.9f; + + m_bodyId = b2CreateBody( m_worldId, &bodyDef ); + + if ( m_capsule ) + { + b2Capsule capsule = { { 0.0f, -1.0f }, { 0.0f, 1.0f }, 0.1f }; + b2CreateCapsuleShape( m_bodyId, &shapeDef, &capsule ); + } + else + { + b2Polygon polygon = b2MakeBox( 2.0f, 0.05f ); + b2CreatePolygonShape( m_bodyId, &shapeDef, &polygon ); + } + + if ( m_bullet ) + { + b2Polygon polygon = b2MakeBox( 0.25f, 0.25f ); + m_x = RandomFloat( -1.0f, 1.0f ); + bodyDef.position = { m_x, 10.0f }; + bodyDef.linearVelocity = { 0.0f, -50.0f }; + m_bulletId = b2CreateBody( m_worldId, &bodyDef ); + b2CreatePolygonShape( m_bulletId, &shapeDef, &polygon ); + } + } + + void UpdateUI() override + { + float height = 110.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 140.0f, height ) ); + + ImGui::Begin( "Skinny Box", nullptr, ImGuiWindowFlags_NoResize ); + + ImGui::Checkbox( "Capsule", &m_capsule ); + + if ( ImGui::Button( "Launch" ) ) + { + Launch(); + } + + ImGui::Checkbox( "Auto Test", &m_autoTest ); + + ImGui::End(); + } + + void Step( Settings& settings ) override + { + Sample::Step( settings ); + + if ( m_autoTest && m_stepCount % 60 == 0 ) + { + Launch(); + } + } + + static Sample* Create( Settings& settings ) + { + return new SkinnyBox( settings ); + } + + b2BodyId m_bodyId, m_bulletId; + float m_angularVelocity; + float m_x; + bool m_capsule; + bool m_autoTest; + bool m_bullet; +}; + +static int sampleSkinnyBox = RegisterSample( "Continuous", "Skinny Box", SkinnyBox::Create ); + +class SpeculativeBug : public Sample +{ +public: + explicit SpeculativeBug( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 1.0f, 5.0f }; + g_camera.m_zoom = 25.0f * 0.25f; + } + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2Segment segment = { { -10.0f, 0.0f }, { 10.0f, 0.0f } }; + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2CreateSegmentShape( groundId, &shapeDef, &segment ); + + shapeDef.friction = 0.0f; + b2Polygon box = b2MakeOffsetBox( 0.05f, 1.0f, { 0.0f, 1.0f }, b2Rot_identity ); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + } + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + for (int i = 0; i < 2; ++i) + { + if (i == 0) + { + bodyDef.position = { -0.8f, 0.25f }; + bodyDef.isAwake = false; + } + else + { + bodyDef.position = { 0.8f, 2.0f }; + bodyDef.isAwake = true; + } + + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Capsule capsule = { { -0.5f, 0.0f }, { 0.5f, 0.0f }, 0.25f }; + b2CreateCapsuleShape( bodyId, &shapeDef, &capsule ); + } + } + + static Sample* Create( Settings& settings ) + { + return new SpeculativeBug( settings ); + } +}; + +static int sampleSpeculativeBug = RegisterSample( "Continuous", "Speculative Bug", SpeculativeBug::Create ); + +// This sample shows ghost collisions +class GhostCollision : public Sample +{ +public: + enum ShapeType + { + e_circleShape = 0, + e_capsuleShape, + e_boxShape + }; + + explicit GhostCollision( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 1.5f, 16.0f }; + g_camera.m_zoom = 25.0f * 0.8f; + } + + m_groundId = b2_nullBodyId; + m_bodyId = b2_nullBodyId; + m_shapeId = b2_nullShapeId; + m_shapeType = e_circleShape; + m_round = 0.0f; + m_friction = 0.2f; + m_bevel = 0.0f; + m_useChain = true; + + CreateScene(); + Launch(); + } + + void CreateScene() + { + if ( B2_IS_NON_NULL( m_groundId ) ) + { + b2DestroyBody( m_groundId ); + } + + m_shapeId = b2_nullShapeId; + + b2BodyDef bodyDef = b2DefaultBodyDef(); + m_groundId = b2CreateBody( m_worldId, &bodyDef ); + + float m = 1.0f / sqrt( 2.0f ); + float mm = 2.0f * ( sqrt( 2.0f ) - 1.0f ); + float hx = 4.0f, hy = 0.25f; + + if ( m_useChain ) + { + b2Vec2 points[20]; + points[0] = { -3.0f * hx, hy }; + points[1] = b2Add( points[0], { -2.0f * hx * m, 2.0f * hx * m } ); + points[2] = b2Add( points[1], { -2.0f * hx * m, 2.0f * hx * m } ); + points[3] = b2Add( points[2], { -2.0f * hx * m, 2.0f * hx * m } ); + points[4] = b2Add( points[3], { -2.0f * hy * m, -2.0f * hy * m } ); + points[5] = b2Add( points[4], { 2.0f * hx * m, -2.0f * hx * m } ); + points[6] = b2Add( points[5], { 2.0f * hx * m, -2.0f * hx * m } ); + points[7] = + b2Add( points[6], { 2.0f * hx * m + 2.0f * hy * ( 1.0f - m ), -2.0f * hx * m - 2.0f * hy * ( 1.0f - m ) } ); + points[8] = b2Add( points[7], { 2.0f * hx + hy * mm, 0.0f } ); + points[9] = b2Add( points[8], { 2.0f * hx, 0.0f } ); + points[10] = b2Add( points[9], { 2.0f * hx + hy * mm, 0.0f } ); + points[11] = + b2Add( points[10], { 2.0f * hx * m + 2.0f * hy * ( 1.0f - m ), 2.0f * hx * m + 2.0f * hy * ( 1.0f - m ) } ); + points[12] = b2Add( points[11], { 2.0f * hx * m, 2.0f * hx * m } ); + points[13] = b2Add( points[12], { 2.0f * hx * m, 2.0f * hx * m } ); + points[14] = b2Add( points[13], { -2.0f * hy * m, 2.0f * hy * m } ); + points[15] = b2Add( points[14], { -2.0f * hx * m, -2.0f * hx * m } ); + points[16] = b2Add( points[15], { -2.0f * hx * m, -2.0f * hx * m } ); + points[17] = b2Add( points[16], { -2.0f * hx * m, -2.0f * hx * m } ); + points[18] = b2Add( points[17], { -2.0f * hx, 0.0f } ); + points[19] = b2Add( points[18], { -2.0f * hx, 0.0f } ); + + b2ChainDef chainDef = b2DefaultChainDef(); + chainDef.points = points; + chainDef.count = 20; + chainDef.isLoop = true; + chainDef.friction = m_friction; + + b2CreateChain( m_groundId, &chainDef ); + } + else + { + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.friction = m_friction; + + b2Hull hull = { 0 }; + + if ( m_bevel > 0.0f ) + { + float hb = m_bevel; + b2Vec2 vs[8] = { { hx + hb, hy - 0.05f }, { hx, hy }, { -hx, hy }, { -hx - hb, hy - 0.05f }, + { -hx - hb, -hy + 0.05f }, { -hx, -hy }, { hx, -hy }, { hx + hb, -hy + 0.05f } }; + hull = b2ComputeHull( vs, 8 ); + } + else + { + b2Vec2 vs[4] = { { hx, hy }, { -hx, hy }, { -hx, -hy }, { hx, -hy } }; + hull = b2ComputeHull( vs, 4 ); + } + + b2Transform transform; + float x, y; + + // Left slope + x = -3.0f * hx - m * hx - m * hy; + y = hy + m * hx - m * hy; + transform.q = b2MakeRot( -0.25f * b2_pi ); + + { + transform.p = { x, y }; + b2Polygon polygon = b2MakeOffsetPolygon( &hull, 0.0f, transform ); + b2CreatePolygonShape( m_groundId, &shapeDef, &polygon ); + x -= 2.0f * m * hx; + y += 2.0f * m * hx; + } + { + transform.p = { x, y }; + b2Polygon polygon = b2MakeOffsetPolygon( &hull, 0.0f, transform ); + b2CreatePolygonShape( m_groundId, &shapeDef, &polygon ); + x -= 2.0f * m * hx; + y += 2.0f * m * hx; + } + { + transform.p = { x, y }; + b2Polygon polygon = b2MakeOffsetPolygon( &hull, 0.0f, transform ); + b2CreatePolygonShape( m_groundId, &shapeDef, &polygon ); + x -= 2.0f * m * hx; + y += 2.0f * m * hx; + } + + x = -2.0f * hx; + y = 0.0f; + transform.q = b2MakeRot( 0.0f ); + + { + transform.p = { x, y }; + b2Polygon polygon = b2MakeOffsetPolygon( &hull, 0.0f, transform ); + b2CreatePolygonShape( m_groundId, &shapeDef, &polygon ); + x += 2.0f * hx; + } + { + transform.p = { x, y }; + b2Polygon polygon = b2MakeOffsetPolygon( &hull, 0.0f, transform ); + b2CreatePolygonShape( m_groundId, &shapeDef, &polygon ); + x += 2.0f * hx; + } + { + transform.p = { x, y }; + b2Polygon polygon = b2MakeOffsetPolygon( &hull, 0.0f, transform ); + b2CreatePolygonShape( m_groundId, &shapeDef, &polygon ); + x += 2.0f * hx; + } + + x = 3.0f * hx + m * hx + m * hy; + y = hy + m * hx - m * hy; + transform.q = b2MakeRot( 0.25f * b2_pi ); + + { + transform.p = { x, y }; + b2Polygon polygon = b2MakeOffsetPolygon( &hull, 0.0f, transform ); + b2CreatePolygonShape( m_groundId, &shapeDef, &polygon ); + x += 2.0f * m * hx; + y += 2.0f * m * hx; + } + { + transform.p = { x, y }; + b2Polygon polygon = b2MakeOffsetPolygon( &hull, 0.0f, transform ); + b2CreatePolygonShape( m_groundId, &shapeDef, &polygon ); + x += 2.0f * m * hx; + y += 2.0f * m * hx; + } + { + transform.p = { x, y }; + b2Polygon polygon = b2MakeOffsetPolygon( &hull, 0.0f, transform ); + b2CreatePolygonShape( m_groundId, &shapeDef, &polygon ); + x += 2.0f * m * hx; + y += 2.0f * m * hx; + } + } + } + + void Launch() + { + if ( B2_IS_NON_NULL( m_bodyId ) ) + { + b2DestroyBody( m_bodyId ); + m_shapeId = b2_nullShapeId; + } + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = { -28.0f, 18.0f }; + bodyDef.linearVelocity = { 0.0f, 0.0f }; + m_bodyId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 1.0f; + shapeDef.friction = m_friction; + + if ( m_shapeType == e_circleShape ) + { + b2Circle circle = { { 0.0f, 0.0f }, 0.5f }; + m_shapeId = b2CreateCircleShape( m_bodyId, &shapeDef, &circle ); + } + else if ( m_shapeType == e_capsuleShape ) + { + b2Capsule capsule = { { -0.5f, 0.0f }, { 0.5f, 0.0 }, 0.25f }; + m_shapeId = b2CreateCapsuleShape( m_bodyId, &shapeDef, &capsule ); + } + else + { + float h = 0.5f - m_round; + b2Polygon box = b2MakeRoundedBox( h, 2.0f * h, m_round ); + m_shapeId = b2CreatePolygonShape( m_bodyId, &shapeDef, &box ); + } + } + + void UpdateUI() override + { + float height = 140.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 180.0f, height ) ); + + ImGui::Begin( "Ghost Collision", nullptr, ImGuiWindowFlags_NoResize ); + ImGui::PushItemWidth( 100.0f ); + + if ( ImGui::Checkbox( "Chain", &m_useChain ) ) + { + CreateScene(); + } + + if ( m_useChain == false ) + { + if ( ImGui::SliderFloat( "Bevel", &m_bevel, 0.0f, 1.0f, "%.2f" ) ) + { + CreateScene(); + } + } + + { + const char* shapeTypes[] = { "Circle", "Capsule", "Box" }; + int shapeType = int( m_shapeType ); + ImGui::Combo( "Shape", &shapeType, shapeTypes, IM_ARRAYSIZE( shapeTypes ) ); + m_shapeType = ShapeType( shapeType ); + } + + if ( m_shapeType == e_boxShape ) + { + ImGui::SliderFloat( "Round", &m_round, 0.0f, 0.4f, "%.1f" ); + } + + if ( ImGui::SliderFloat( "Friction", &m_friction, 0.0f, 1.0f, "%.1f" ) ) + { + if ( B2_IS_NON_NULL( m_shapeId ) ) + { + b2Shape_SetFriction( m_shapeId, m_friction ); + } + + CreateScene(); + } + + if ( ImGui::Button( "Launch" ) ) + { + Launch(); + } + + ImGui::PopItemWidth(); + ImGui::End(); + } + + static Sample* Create( Settings& settings ) + { + return new GhostCollision( settings ); + } + + b2BodyId m_groundId; + b2BodyId m_bodyId; + b2ShapeId m_shapeId; + ShapeType m_shapeType; + float m_round; + float m_friction; + float m_bevel; + bool m_useChain; +}; + +static int sampleGhostCollision = RegisterSample( "Continuous", "Ghost Collision", GhostCollision::Create ); + +// Speculative collision failure case suggested by Dirk Gregorius. This uses +// a simple fallback scheme to prevent tunneling. +class SpeculativeFallback : public Sample +{ +public: + explicit SpeculativeFallback( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 1.0f, 5.0f }; + g_camera.m_zoom = 25.0f * 0.25f; + } + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Segment segment = { { -10.0f, 0.0f }, { 10.0f, 0.0f } }; + b2CreateSegmentShape( groundId, &shapeDef, &segment ); + + b2Vec2 points[5] = { { -2.0f, 4.0f }, { 2.0f, 4.0f }, { 2.0f, 4.1f }, { -0.5f, 4.2f }, { -2.0f, 4.2f } }; + b2Hull hull = b2ComputeHull( points, 5 ); + b2Polygon poly = b2MakePolygon( &hull, 0.0f ); + b2CreatePolygonShape( groundId, &shapeDef, &poly ); + } + + // Fast moving skinny box. Also testing a large shape offset. + { + float offset = 8.0f; + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = { offset, 12.0f }; + bodyDef.linearVelocity = { 0.0f, -100.0f }; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Polygon box = b2MakeOffsetBox( 2.0f, 0.05f, { -offset, 0.0f }, b2MakeRot(b2_pi) ); + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + } + } + + static Sample* Create( Settings& settings ) + { + return new SpeculativeFallback( settings ); + } +}; + +static int sampleSpeculativeFallback = RegisterSample( "Continuous", "Speculative Fallback", SpeculativeFallback::Create ); + +// This shows a fast moving body that uses continuous collision versus static and dynamic bodies. +// This is achieved by setting the ball body as a *bullet*. +class Pinball : public Sample +{ +public: + explicit Pinball( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, 9.0f }; + g_camera.m_zoom = 25.0f * 0.5f; + } + + settings.drawJoints = false; + + // Ground body + b2BodyId groundId = {}; + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2Vec2 vs[5] = { { -8.0f, 6.0f }, { -8.0f, 20.0f }, { 8.0f, 20.0f }, { 8.0f, 6.0f }, { 0.0f, -2.0f } }; + + b2ChainDef chainDef = b2DefaultChainDef(); + chainDef.points = vs; + chainDef.count = 5; + chainDef.isLoop = true; + b2CreateChain( groundId, &chainDef ); + } + + // Flippers + { + b2Vec2 p1 = { -2.0f, 0.0f }, p2 = { 2.0f, 0.0f }; + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.enableSleep = false; + + bodyDef.position = p1; + b2BodyId leftFlipperId = b2CreateBody( m_worldId, &bodyDef ); + + bodyDef.position = p2; + b2BodyId rightFlipperId = b2CreateBody( m_worldId, &bodyDef ); + + b2Polygon box = b2MakeBox( 1.75f, 0.2f ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + + b2CreatePolygonShape( leftFlipperId, &shapeDef, &box ); + b2CreatePolygonShape( rightFlipperId, &shapeDef, &box ); + + b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef(); + jointDef.bodyIdA = groundId; + jointDef.localAnchorB = b2Vec2_zero; + jointDef.enableMotor = true; + jointDef.maxMotorTorque = 1000.0f; + jointDef.enableLimit = true; + + jointDef.motorSpeed = 0.0f; + jointDef.localAnchorA = p1; + jointDef.bodyIdB = leftFlipperId; + jointDef.lowerAngle = -30.0f * b2_pi / 180.0f; + jointDef.upperAngle = 5.0f * b2_pi / 180.0f; + m_leftJointId = b2CreateRevoluteJoint( m_worldId, &jointDef ); + + jointDef.motorSpeed = 0.0f; + jointDef.localAnchorA = p2; + jointDef.bodyIdB = rightFlipperId; + jointDef.lowerAngle = -5.0f * b2_pi / 180.0f; + jointDef.upperAngle = 30.0f * b2_pi / 180.0f; + m_rightJointId = b2CreateRevoluteJoint( m_worldId, &jointDef ); + } + + // Spinners + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = { -4.0f, 17.0f }; + + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Polygon box1 = b2MakeBox( 1.5f, 0.125f ); + b2Polygon box2 = b2MakeBox( 0.125f, 1.5f ); + + b2CreatePolygonShape( bodyId, &shapeDef, &box1 ); + b2CreatePolygonShape( bodyId, &shapeDef, &box2 ); + + b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef(); + jointDef.bodyIdA = groundId; + jointDef.bodyIdB = bodyId; + jointDef.localAnchorA = bodyDef.position; + jointDef.localAnchorB = b2Vec2_zero; + jointDef.enableMotor = true; + jointDef.maxMotorTorque = 0.1f; + b2CreateRevoluteJoint( m_worldId, &jointDef ); + + bodyDef.position = { 4.0f, 8.0f }; + bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2CreatePolygonShape( bodyId, &shapeDef, &box1 ); + b2CreatePolygonShape( bodyId, &shapeDef, &box2 ); + jointDef.localAnchorA = bodyDef.position; + jointDef.bodyIdB = bodyId; + b2CreateRevoluteJoint( m_worldId, &jointDef ); + } + + // Bumpers + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.position = { -4.0f, 8.0f }; + + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.restitution = 1.5f; + + b2Circle circle = { { 0.0f, 0.0f }, 1.0f }; + b2CreateCircleShape( bodyId, &shapeDef, &circle ); + + bodyDef.position = { 4.0f, 17.0f }; + bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2CreateCircleShape( bodyId, &shapeDef, &circle ); + } + + // Ball + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.position = { 1.0f, 15.0f }; + bodyDef.type = b2_dynamicBody; + bodyDef.isBullet = true; + + m_ballId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Circle circle = { { 0.0f, 0.0f }, 0.2f }; + b2CreateCircleShape( m_ballId, &shapeDef, &circle ); + } + } + + void Step( Settings& settings ) override + { + Sample::Step( settings ); + + if ( glfwGetKey( g_mainWindow, GLFW_KEY_SPACE ) == GLFW_PRESS ) + { + b2RevoluteJoint_SetMotorSpeed( m_leftJointId, 20.0f ); + b2RevoluteJoint_SetMotorSpeed( m_rightJointId, -20.0f ); + } + else + { + b2RevoluteJoint_SetMotorSpeed( m_leftJointId, -10.0f ); + b2RevoluteJoint_SetMotorSpeed( m_rightJointId, 10.0f ); + } + } + + static Sample* Create( Settings& settings ) + { + return new Pinball( settings ); + } + + b2JointId m_leftJointId; + b2JointId m_rightJointId; + b2BodyId m_ballId; +}; + +static int samplePinball = RegisterSample( "Continuous", "Pinball", Pinball::Create ); diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/sample_determinism.cpp b/tests/cpp-tests/Source/Box2DTestBed/samples/sample_determinism.cpp new file mode 100644 index 000000000000..29abdd43c5cf --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/sample_determinism.cpp @@ -0,0 +1,188 @@ +// SPDX-FileCopyrightText: 2022 Erin Catto +// SPDX-License-Identifier: MIT + +#include "draw.h" +#include "sample.h" +#include "settings.h" + +#include "box2d/box2d.h" +#include "box2d/math_functions.h" + +#include +#include +#include + +// This sample provides a visual representation of the cross platform determinism unit test. +// The scenario is designed to produce a chaotic result engaging: +// - continuous collision +// - joint limits (approximate atan2) +// - b2MakeRot (approximate sin/cos) +// Once all the bodies go to sleep the step counter and transform hash is emitted which +// can then be transferred to the unit test and tested in GitHub build actions. +// See CrossPlatformTest in the unit tests. +class FallingHinges : public Sample +{ +public: + enum + { + e_columns = 4, + e_rows = 30, + }; + + explicit FallingHinges( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, 7.5f }; + g_camera.m_zoom = 10.0f; + } + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.position = { 0.0f, -1.0f }; + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2Polygon box = b2MakeBox( 20.0f, 1.0f ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + } + + for ( int i = 0; i < e_rows * e_columns; ++i ) + { + m_bodies[i] = b2_nullBodyId; + } + + float h = 0.25f; + float r = 0.1f * h; + b2Polygon box = b2MakeRoundedBox( h - r, h - r, r ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.friction = 0.3f; + + float offset = 0.4f * h; + float dx = 10.0f * h; + float xroot = -0.5f * dx * ( e_columns - 1.0f ); + + b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef(); + jointDef.enableLimit = true; + jointDef.lowerAngle = -0.1f * b2_pi; + jointDef.upperAngle = 0.2f * b2_pi; + jointDef.enableSpring = true; + jointDef.hertz = 0.5f; + jointDef.dampingRatio = 0.5f; + jointDef.localAnchorA = { h, h }; + jointDef.localAnchorB = { offset, -h }; + jointDef.drawSize = 0.1f; + + int bodyIndex = 0; + int bodyCount = e_rows * e_columns; + + for ( int j = 0; j < e_columns; ++j ) + { + float x = xroot + j * dx; + + b2BodyId prevBodyId = b2_nullBodyId; + + for ( int i = 0; i < e_rows; ++i ) + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + + bodyDef.position.x = x + offset * i; + bodyDef.position.y = h + 2.0f * h * i; + + // this tests the deterministic cosine and sine functions + bodyDef.rotation = b2MakeRot( 0.1f * i - 1.0f ); + + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + + if ((i & 1) == 0) + { + prevBodyId = bodyId; + } + else + { + jointDef.bodyIdA = prevBodyId; + jointDef.bodyIdB = bodyId; + b2CreateRevoluteJoint( m_worldId, &jointDef ); + prevBodyId = b2_nullBodyId; + } + + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + + assert( bodyIndex < bodyCount ); + m_bodies[bodyIndex] = bodyId; + + bodyIndex += 1; + } + } + + m_hash = 0; + m_sleepStep = -1; + + //PrintTransforms(); + } + + void PrintTransforms() + { + uint32_t hash = B2_HASH_INIT; + int bodyCount = e_rows * e_columns; + for ( int i = 0; i < bodyCount; ++i ) + { + b2Transform xf = b2Body_GetTransform( m_bodies[i] ); + printf( "%d %.9f %.9f %.9f %.9f\n", i, xf.p.x, xf.p.y, xf.q.c, xf.q.s ); + hash = b2Hash( hash, reinterpret_cast( &xf ), sizeof( b2Transform ) ); + } + + printf( "hash = 0x%08x\n", hash ); + } + + void Step(Settings& settings) override + { + Sample::Step( settings ); + + if (m_hash == 0) + { + bool sleeping = true; + int bodyCount = e_rows * e_columns; + for (int i = 0; i < bodyCount; ++i) + { + if ( b2Body_IsAwake( m_bodies[i] ) == true ) + { + sleeping = false; + break; + } + } + + if (sleeping == true) + { + uint32_t hash = B2_HASH_INIT; + for ( int i = 0; i < bodyCount; ++i ) + { + b2Transform xf = b2Body_GetTransform( m_bodies[i] ); + //printf( "%d %.9f %.9f %.9f %.9f\n", i, xf.p.x, xf.p.y, xf.q.c, xf.q.s ); + hash = b2Hash( hash, reinterpret_cast( &xf ), sizeof( b2Transform ) ); + } + + m_sleepStep = m_stepCount - 1; + m_hash = hash; + printf( "sleep step = %d, hash = 0x%08x\n", m_sleepStep, m_hash ); + } + } + + g_draw.DrawString( 5, m_textLine, "sleep step = %d, hash = 0x%08x", m_sleepStep, m_hash ); + m_textLine += m_textIncrement; + } + + static Sample* Create( Settings& settings ) + { + return new FallingHinges( settings ); + } + + b2BodyId m_bodies[e_rows * e_columns]; + uint32_t m_hash; + int m_sleepStep; +}; + +static int sampleFallingHinges = RegisterSample( "Determinism", "Falling Hinges", FallingHinges::Create ); diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/sample_events.cpp b/tests/cpp-tests/Source/Box2DTestBed/samples/sample_events.cpp new file mode 100644 index 000000000000..e538ff2fdf7e --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/sample_events.cpp @@ -0,0 +1,1114 @@ +// SPDX-FileCopyrightText: 2022 Erin Catto +// SPDX-License-Identifier: MIT + +#include "donut.h" +#include "draw.h" +#include "human.h" +#include "sample.h" +#include "settings.h" + +#include "box2d/box2d.h" +#include "box2d/math_functions.h" + +#include +#include +#include + +class SensorEvent : public Sample +{ +public: + enum + { + e_donut = 1, + e_human = 2, + e_count = 32 + }; + + explicit SensorEvent( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, 0.0f }; + g_camera.m_zoom = 25.0f * 1.333f; + } + + settings.drawJoints = false; + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + // b2Vec2 points[] = { + //{42.333, 44.979}, {177.271, 44.979}, {177.271, 100.542}, {142.875, 121.708}, {177.271, 121.708}, + //{177.271, 171.979}, {142.875, 193.146}, {177.271, 193.146}, {177.271, 222.250}, {124.354, 261.938}, + //{124.354, 293.688}, {95.250, 293.688}, {95.250, 261.938}, {42.333, 222.250}, {42.333, 193.146}, + //{76.729, 193.146}, {42.333, 171.979}, {42.333, 121.708}, {76.729, 121.708}, {42.333, 100.542}, + //}; + + b2Vec2 points[] = { + { -16.8672504, 31.088623 }, { 16.8672485, 31.088623 }, { 16.8672485, 17.1978741 }, + { 8.26824951, 11.906374 }, { 16.8672485, 11.906374 }, { 16.8672485, -0.661376953 }, + { 8.26824951, -5.953125 }, { 16.8672485, -5.953125 }, { 16.8672485, -13.229126 }, + { 3.63799858, -23.151123 }, { 3.63799858, -31.088623 }, { -3.63800049, -31.088623 }, + { -3.63800049, -23.151123 }, { -16.8672504, -13.229126 }, { -16.8672504, -5.953125 }, + { -8.26825142, -5.953125 }, { -16.8672504, -0.661376953 }, { -16.8672504, 11.906374 }, + { -8.26825142, 11.906374 }, { -16.8672504, 17.1978741 }, + }; + + int count = sizeof( points ) / sizeof( points[0] ); + + // float scale = 0.25f; + // b2Vec2 lower = {FLT_MAX, FLT_MAX}; + // b2Vec2 upper = {-FLT_MAX, -FLT_MAX}; + // for (int i = 0; i < count; ++i) + //{ + // points[i].x = scale * points[i].x; + // points[i].y = -scale * points[i].y; + + // lower = b2Min(lower, points[i]); + // upper = b2Max(upper, points[i]); + //} + + // b2Vec2 center = b2MulSV(0.5f, b2Add(lower, upper)); + // for (int i = 0; i < count; ++i) + //{ + // points[i] = b2Sub(points[i], center); + // } + + // for (int i = 0; i < count / 2; ++i) + //{ + // b2Vec2 temp = points[i]; + // points[i] = points[count - 1 - i]; + // points[count - 1 - i] = temp; + // } + + // printf("{"); + // for (int i = 0; i < count; ++i) + //{ + // printf("{%.9g, %.9g},", points[i].x, points[i].y); + // } + // printf("};\n"); + + b2ChainDef chainDef = b2DefaultChainDef(); + chainDef.points = points; + chainDef.count = count; + chainDef.isLoop = true; + chainDef.friction = 0.2f; + b2CreateChain( groundId, &chainDef ); + + float sign = 1.0f; + float y = 14.0f; + for ( int i = 0; i < 3; ++i ) + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.position = { 0.0f, y }; + bodyDef.type = b2_dynamicBody; + + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + + b2Polygon box = b2MakeBox( 6.0f, 0.5f ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.friction = 0.1f; + shapeDef.restitution = 1.0f; + shapeDef.density = 1.0f; + + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + + b2RevoluteJointDef revoluteDef = b2DefaultRevoluteJointDef(); + revoluteDef.bodyIdA = groundId; + revoluteDef.bodyIdB = bodyId; + revoluteDef.localAnchorA = bodyDef.position; + revoluteDef.localAnchorB = b2Vec2_zero; + revoluteDef.maxMotorTorque = 200.0f; + revoluteDef.motorSpeed = 2.0f * sign; + revoluteDef.enableMotor = true; + + b2CreateRevoluteJoint( m_worldId, &revoluteDef ); + + y -= 14.0f; + sign = -sign; + } + + { + b2Polygon box = b2MakeOffsetBox( 4.0f, 1.0f, { 0.0f, -30.5f }, b2Rot_identity ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.isSensor = true; + b2CreatePolygonShape( groundId, &shapeDef, &box ); + } + } + + m_wait = 0.5f; + m_side = -15.0f; + m_type = e_human; + + for ( int i = 0; i < e_count; ++i ) + { + m_isSpawned[i] = false; + } + + CreateElement(); + } + + void CreateElement() + { + int index = -1; + for ( int i = 0; i < e_count; ++i ) + { + if ( m_isSpawned[i] == false ) + { + index = i; + break; + } + } + + if ( index == -1 ) + { + return; + } + + b2Vec2 center = { m_side, 29.5f }; + + if ( m_type == e_donut ) + { + Donut* donut = m_donuts + index; + // donut->Spawn(m_worldId, center, index + 1, donut); + donut->Spawn( m_worldId, center, 1.0f, 0, donut ); + } + else + { + Human* human = m_humans + index; + human->Spawn( m_worldId, center, 2.0f, 0.05f, 0.0f, 0.0f, index + 1, human, false ); + } + + m_isSpawned[index] = true; + m_side = -m_side; + } + + void DestroyElement( int index ) + { + if ( m_type == e_donut ) + { + Donut* donut = m_donuts + index; + donut->Despawn(); + } + else + { + Human* human = m_humans + index; + human->Despawn(); + } + + m_isSpawned[index] = false; + } + + void Clear() + { + for ( int i = 0; i < e_count; ++i ) + { + if ( m_isSpawned[i] == true ) + { + if ( m_type == e_donut ) + { + m_donuts[i].Despawn(); + } + else + { + m_humans[i].Despawn(); + } + + m_isSpawned[i] = false; + } + } + } + + void UpdateUI() override + { + float height = 90.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 140.0f, height ) ); + + ImGui::Begin( "Sensor Event", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize ); + + if ( ImGui::RadioButton( "donut", m_type == e_donut ) ) + { + Clear(); + m_type = e_donut; + } + + if ( ImGui::RadioButton( "human", m_type == e_human ) ) + { + Clear(); + m_type = e_human; + } + + ImGui::End(); + } + + void Step( Settings& settings ) override + { + if ( m_stepCount == 832 ) + { + m_stepCount += 0; + } + + Sample::Step( settings ); + + // Discover rings that touch the bottom sensor + bool deferredDestructions[e_count] = {}; + b2SensorEvents sensorEvents = b2World_GetSensorEvents( m_worldId ); + for ( int i = 0; i < sensorEvents.beginCount; ++i ) + { + b2SensorBeginTouchEvent event = sensorEvents.beginEvents[i]; + b2ShapeId visitorId = event.visitorShapeId; + b2BodyId bodyId = b2Shape_GetBody( visitorId ); + + if ( m_type == e_donut ) + { + Donut* donut = (Donut*)b2Body_GetUserData( bodyId ); + if ( donut != nullptr ) + { + int index = (int)( donut - m_donuts ); + assert( 0 <= index && index < e_count ); + + // Defer destruction to avoid double destruction and event invalidation (orphaned shape ids) + deferredDestructions[index] = true; + } + } + else + { + Human* human = (Human*)b2Body_GetUserData( bodyId ); + if ( human != nullptr ) + { + int index = (int)( human - m_humans ); + assert( 0 <= index && index < e_count ); + + // Defer destruction to avoid double destruction and event invalidation (orphaned shape ids) + deferredDestructions[index] = true; + } + } + } + + // todo destroy mouse joint if necessary + + // Safely destroy rings that hit the bottom sensor + for ( int i = 0; i < e_count; ++i ) + { + if ( deferredDestructions[i] ) + { + DestroyElement( i ); + } + } + + if ( settings.hertz > 0.0f && settings.pause == false ) + { + m_wait -= 1.0f / settings.hertz; + if ( m_wait < 0.0f ) + { + CreateElement(); + m_wait += 0.5f; + } + } + } + + static Sample* Create( Settings& settings ) + { + return new SensorEvent( settings ); + } + + Human m_humans[e_count]; + Donut m_donuts[e_count]; + bool m_isSpawned[e_count]; + int m_type; + float m_wait; + float m_side; +}; + +static int sampleSensorEvent = RegisterSample( "Events", "Sensor", SensorEvent::Create ); + +struct BodyUserData +{ + int index; +}; + +class ContactEvent : public Sample +{ +public: + enum + { + e_count = 20 + }; + + explicit ContactEvent( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, 0.0f }; + g_camera.m_zoom = 25.0f * 1.75f; + } + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2Vec2 points[] = { { 40.0f, -40.0f }, { -40.0f, -40.0f }, { -40.0f, 40.0f }, { 40.0f, 40.0f } }; + + b2ChainDef chainDef = b2DefaultChainDef(); + chainDef.count = 4; + chainDef.points = points; + chainDef.isLoop = true; + + b2CreateChain( groundId, &chainDef ); + } + + // Player + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.gravityScale = 0.0f; + bodyDef.linearDamping = 0.5f; + bodyDef.angularDamping = 0.5f; + bodyDef.isBullet = true; + m_playerId = b2CreateBody( m_worldId, &bodyDef ); + + b2Circle circle = { { 0.0f, 0.0f }, 1.0f }; + b2ShapeDef shapeDef = b2DefaultShapeDef(); + + // Enable contact events for the player shape + shapeDef.enableContactEvents = true; + + m_coreShapeId = b2CreateCircleShape( m_playerId, &shapeDef, &circle ); + } + + for ( int i = 0; i < e_count; ++i ) + { + m_debrisIds[i] = b2_nullBodyId; + m_bodyUserData[i].index = i; + } + + m_wait = 0.5f; + m_force = 200.0f; + } + + void SpawnDebris() + { + int index = -1; + for ( int i = 0; i < e_count; ++i ) + { + if ( B2_IS_NULL( m_debrisIds[i] ) ) + { + index = i; + break; + } + } + + if ( index == -1 ) + { + return; + } + + // Debris + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = { RandomFloat( -38.0f, 38.0f ), RandomFloat( -38.0f, 38.0f ) }; + bodyDef.rotation = b2MakeRot( RandomFloat( -b2_pi, b2_pi ) ); + bodyDef.linearVelocity = { RandomFloat( -5.0f, 5.0f ), RandomFloat( -5.0f, 5.0f ) }; + bodyDef.angularVelocity = RandomFloat( -1.0f, 1.0f ); + bodyDef.gravityScale = 0.0f; + bodyDef.userData = m_bodyUserData + index; + m_debrisIds[index] = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.restitution = 0.8f; + + // No events when debris hits debris + shapeDef.enableContactEvents = false; + + if ( ( index + 1 ) % 3 == 0 ) + { + b2Circle circle = { { 0.0f, 0.0f }, 0.5f }; + b2CreateCircleShape( m_debrisIds[index], &shapeDef, &circle ); + } + else if ( ( index + 1 ) % 2 == 0 ) + { + b2Capsule capsule = { { 0.0f, -0.25f }, { 0.0f, 0.25f }, 0.25f }; + b2CreateCapsuleShape( m_debrisIds[index], &shapeDef, &capsule ); + } + else + { + b2Polygon box = b2MakeBox( 0.4f, 0.6f ); + b2CreatePolygonShape( m_debrisIds[index], &shapeDef, &box ); + } + } + + void UpdateUI() override + { + float height = 60.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 240.0f, height ) ); + + ImGui::Begin( "Contact Event", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize ); + + ImGui::SliderFloat( "force", &m_force, 100.0f, 500.0f, "%.1f" ); + + ImGui::End(); + } + + void Step( Settings& settings ) override + { + g_draw.DrawString( 5, m_textLine, "move using WASD" ); + m_textLine += m_textIncrement; + + b2Vec2 position = b2Body_GetPosition( m_playerId ); + + if ( glfwGetKey( g_mainWindow, GLFW_KEY_A ) == GLFW_PRESS ) + { + b2Body_ApplyForce( m_playerId, { -m_force, 0.0f }, position, true ); + } + + if ( glfwGetKey( g_mainWindow, GLFW_KEY_D ) == GLFW_PRESS ) + { + b2Body_ApplyForce( m_playerId, { m_force, 0.0f }, position, true ); + } + + if ( glfwGetKey( g_mainWindow, GLFW_KEY_W ) == GLFW_PRESS ) + { + b2Body_ApplyForce( m_playerId, { 0.0f, m_force }, position, true ); + } + + if ( glfwGetKey( g_mainWindow, GLFW_KEY_S ) == GLFW_PRESS ) + { + b2Body_ApplyForce( m_playerId, { 0.0f, -m_force }, position, true ); + } + + Sample::Step( settings ); + + // Discover rings that touch the bottom sensor + int debrisToAttach[e_count] = {}; + b2ShapeId shapesToDestroy[e_count] = { b2_nullShapeId }; + int attachCount = 0; + int destroyCount = 0; + + std::vector contactData; + + b2ContactEvents contactEvents = b2World_GetContactEvents( m_worldId ); + for ( int i = 0; i < contactEvents.beginCount; ++i ) + { + b2ContactBeginTouchEvent event = contactEvents.beginEvents[i]; + b2BodyId bodyIdA = b2Shape_GetBody( event.shapeIdA ); + b2BodyId bodyIdB = b2Shape_GetBody( event.shapeIdB ); + + int capacityA = b2Shape_GetContactCapacity( event.shapeIdA ); + contactData.resize( capacityA ); + int countA = b2Shape_GetContactData( event.shapeIdA, contactData.data(), capacityA ); + assert( countA >= 1 ); + + for (int j = 0; j < countA; ++j) + { + b2Manifold manifold = contactData[j].manifold; + b2Vec2 normal = manifold.normal; + assert( b2AbsFloat( b2Length( normal ) - 1.0f ) < 4.0f * FLT_EPSILON ); + + for (int k = 0; k < manifold.pointCount; ++k) + { + b2ManifoldPoint point = manifold.points[k]; + g_draw.DrawSegment( point.point, point.point + 4.0f * normal, b2_colorBlueViolet ); + g_draw.DrawPoint( point.point, 10.0f, b2_colorWhite ); + } + } + + int capacityB = b2Shape_GetContactCapacity( event.shapeIdB ); + contactData.resize( capacityB ); + int countB = b2Shape_GetContactData( event.shapeIdB, contactData.data(), capacityB ); + assert( countB >= 1 ); + + for (int j = 0; j < countB; ++j) + { + b2Manifold manifold = contactData[j].manifold; + b2Vec2 normal = manifold.normal; + assert( b2AbsFloat( b2Length( normal ) - 1.0f ) < 4.0f * FLT_EPSILON ); + + for (int k = 0; k < manifold.pointCount; ++k) + { + b2ManifoldPoint point = manifold.points[k]; + g_draw.DrawSegment( point.point, point.point + 4.0f * normal, b2_colorYellowGreen ); + g_draw.DrawPoint( point.point, 10.0f, b2_colorWhite ); + } + } + + if ( B2_ID_EQUALS( bodyIdA, m_playerId ) ) + { + BodyUserData* userDataB = static_cast( b2Body_GetUserData( bodyIdB ) ); + if ( userDataB == nullptr ) + { + if ( B2_ID_EQUALS( event.shapeIdA, m_coreShapeId ) == false && destroyCount < e_count ) + { + // player non-core shape hit the wall + + bool found = false; + for ( int j = 0; j < destroyCount; ++j ) + { + if ( B2_ID_EQUALS( event.shapeIdA, shapesToDestroy[j] ) ) + { + found = true; + break; + } + } + + // avoid double deletion + if ( found == false ) + { + shapesToDestroy[destroyCount] = event.shapeIdA; + destroyCount += 1; + } + } + } + else if ( attachCount < e_count ) + { + debrisToAttach[attachCount] = userDataB->index; + attachCount += 1; + } + } + else + { + // Only expect events for the player + assert( B2_ID_EQUALS( bodyIdB, m_playerId ) ); + BodyUserData* userDataA = static_cast( b2Body_GetUserData( bodyIdA ) ); + if ( userDataA == nullptr ) + { + if ( B2_ID_EQUALS( event.shapeIdB, m_coreShapeId ) == false && destroyCount < e_count ) + { + // player non-core shape hit the wall + + bool found = false; + for ( int j = 0; j < destroyCount; ++j ) + { + if ( B2_ID_EQUALS( event.shapeIdB, shapesToDestroy[j] ) ) + { + found = true; + break; + } + } + + // avoid double deletion + if ( found == false ) + { + shapesToDestroy[destroyCount] = event.shapeIdB; + destroyCount += 1; + } + } + } + else if ( attachCount < e_count ) + { + debrisToAttach[attachCount] = userDataA->index; + attachCount += 1; + } + } + } + + // Attach debris to player body + for ( int i = 0; i < attachCount; ++i ) + { + int index = debrisToAttach[i]; + b2BodyId debrisId = m_debrisIds[index]; + if ( B2_IS_NULL( debrisId ) ) + { + continue; + } + + b2Transform playerTransform = b2Body_GetTransform( m_playerId ); + b2Transform debrisTransform = b2Body_GetTransform( debrisId ); + b2Transform relativeTransform = b2InvMulTransforms( playerTransform, debrisTransform ); + + int shapeCount = b2Body_GetShapeCount( debrisId ); + if ( shapeCount == 0 ) + { + continue; + } + + b2ShapeId shapeId; + b2Body_GetShapes( debrisId, &shapeId, 1 ); + + b2ShapeType type = b2Shape_GetType( shapeId ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.enableContactEvents = true; + + switch ( type ) + { + case b2_circleShape: + { + b2Circle circle = b2Shape_GetCircle( shapeId ); + circle.center = b2TransformPoint( relativeTransform, circle.center ); + + b2CreateCircleShape( m_playerId, &shapeDef, &circle ); + } + break; + + case b2_capsuleShape: + { + b2Capsule capsule = b2Shape_GetCapsule( shapeId ); + capsule.center1 = b2TransformPoint( relativeTransform, capsule.center1 ); + capsule.center2 = b2TransformPoint( relativeTransform, capsule.center2 ); + + b2CreateCapsuleShape( m_playerId, &shapeDef, &capsule ); + } + break; + + case b2_polygonShape: + { + b2Polygon originalPolygon = b2Shape_GetPolygon( shapeId ); + b2Polygon polygon = b2TransformPolygon( relativeTransform, &originalPolygon ); + + b2CreatePolygonShape( m_playerId, &shapeDef, &polygon ); + } + break; + + default: + assert( false ); + } + + b2DestroyBody( debrisId ); + m_debrisIds[index] = b2_nullBodyId; + } + + for ( int i = 0; i < destroyCount; ++i ) + { + b2DestroyShape( shapesToDestroy[i] ); + } + + if ( settings.hertz > 0.0f && settings.pause == false ) + { + m_wait -= 1.0f / settings.hertz; + if ( m_wait < 0.0f ) + { + SpawnDebris(); + m_wait += 0.5f; + } + } + } + + static Sample* Create( Settings& settings ) + { + return new ContactEvent( settings ); + } + + b2BodyId m_playerId; + b2ShapeId m_coreShapeId; + b2BodyId m_debrisIds[e_count]; + BodyUserData m_bodyUserData[e_count]; + float m_force; + float m_wait; +}; + +static int sampleWeeble = RegisterSample( "Events", "Contact", ContactEvent::Create ); + +// Shows how to make a rigid body character mover and use the pre-solve callback. +class Platformer : public Sample +{ +public: + explicit Platformer( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.5f, 7.5f }; + g_camera.m_zoom = 25.0f * 0.4f; + } + + b2World_SetPreSolveCallback( m_worldId, PreSolveStatic, this ); + + // Ground + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Segment segment = { { -20.0f, 0.0f }, { 20.0f, 0.0f } }; + b2CreateSegmentShape( groundId, &shapeDef, &segment ); + } + + // Platform + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_kinematicBody; + bodyDef.position = { 0.0f, 6.0f }; + bodyDef.linearVelocity = { 2.0f, 0.0f }; + m_platformId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Polygon box = b2MakeBox( 3.0f, 0.5f ); + m_platformShapeId = b2CreatePolygonShape( m_platformId, &shapeDef, &box ); + } + + // Actor + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.fixedRotation = true; + bodyDef.linearDamping = 0.5f; + bodyDef.position = { 0.0f, 1.0f }; + m_characterId = b2CreateBody( m_worldId, &bodyDef ); + + m_radius = 0.5f; + b2Capsule capsule = { { 0.0f, 0.0f }, { 0.0f, 1.0f }, m_radius }; + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.friction = 0.1f; + + // Need to turn this on to get the callback + shapeDef.enablePreSolveEvents = true; + + b2CreateCapsuleShape( m_characterId, &shapeDef, &capsule ); + } + + m_force = 25.0f; + m_impulse = 25.0f; + m_jumpDelay = 0.25f; + m_jumping = false; + } + + static bool PreSolveStatic( b2ShapeId shapeIdA, b2ShapeId shapeIdB, b2Manifold* manifold, void* context ) + { + Platformer* platformer = static_cast( context ); + return platformer->PreSolve( shapeIdA, shapeIdB, manifold ); + } + + // This callback must be thread-safe. It may be called multiple times simultaneously. + // Notice how this method is constant and doesn't change any data. It also + // does not try to access any values in the world that may be changing, such as contact data. + bool PreSolve( b2ShapeId shapeIdA, b2ShapeId shapeIdB, b2Manifold* manifold ) const + { + assert( b2Shape_IsValid( shapeIdA ) ); + assert( b2Shape_IsValid( shapeIdB ) ); + + b2ShapeId actorShapeId = b2_nullShapeId; + float sign = 0.0f; + if ( B2_ID_EQUALS( shapeIdA, m_platformShapeId ) ) + { + sign = 1.0f; + actorShapeId = shapeIdB; + } + else if ( B2_ID_EQUALS( shapeIdB, m_platformShapeId ) ) + { + sign = -1.0f; + actorShapeId = shapeIdA; + } + else + { + // not the platform, enable contact + return true; + } + + b2BodyId bodyId = b2Shape_GetBody( actorShapeId ); + if ( B2_ID_EQUALS( bodyId, m_characterId ) == false ) + { + // not the character, enable contact + return true; + } + + b2Vec2 normal = manifold->normal; + if ( sign * normal.y > 0.95f ) + { + return true; + } + + float separation = 0.0f; + for ( int i = 0; i < manifold->pointCount; ++i ) + { + float s = manifold->points[i].separation; + separation = separation < s ? separation : s; + } + + if ( separation > 0.1f * m_radius ) + { + // shallow overlap + return true; + } + + // normal points down, disable contact + return false; + } + + void UpdateUI() override + { + float height = 100.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 240.0f, height ) ); + + ImGui::Begin( "Platformer", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize ); + + ImGui::SliderFloat( "force", &m_force, 0.0f, 50.0f, "%.1f" ); + ImGui::SliderFloat( "impulse", &m_impulse, 0.0f, 50.0f, "%.1f" ); + + ImGui::End(); + } + + void Step( Settings& settings ) override + { + bool canJump = false; + b2Vec2 velocity = b2Body_GetLinearVelocity( m_characterId ); + if ( m_jumpDelay == 0.0f && m_jumping == false && velocity.y < 0.01f ) + { + int capacity = b2Body_GetContactCapacity( m_characterId ); + capacity = b2MinInt( capacity, 4 ); + b2ContactData contactData[4]; + int count = b2Body_GetContactData( m_characterId, contactData, capacity ); + for ( int i = 0; i < count; ++i ) + { + b2BodyId bodyIdA = b2Shape_GetBody( contactData[i].shapeIdA ); + float sign = 0.0f; + if ( B2_ID_EQUALS( bodyIdA, m_characterId ) ) + { + // normal points from A to B + sign = -1.0f; + } + else + { + sign = 1.0f; + } + + if ( sign * contactData[i].manifold.normal.y > 0.9f ) + { + canJump = true; + break; + } + } + } + + // A kinematic body is moved by setting its velocity. This + // ensure friction works correctly. + b2Vec2 platformPosition = b2Body_GetPosition( m_platformId ); + if ( platformPosition.x < -15.0f ) + { + b2Body_SetLinearVelocity( m_platformId, { 2.0f, 0.0f } ); + } + else if ( platformPosition.x > 15.0f ) + { + b2Body_SetLinearVelocity( m_platformId, { -2.0f, 0.0f } ); + } + + if ( glfwGetKey( g_mainWindow, GLFW_KEY_A ) == GLFW_PRESS ) + { + b2Body_ApplyForceToCenter( m_characterId, { -m_force, 0.0f }, true ); + } + + if ( glfwGetKey( g_mainWindow, GLFW_KEY_D ) == GLFW_PRESS ) + { + b2Body_ApplyForceToCenter( m_characterId, { m_force, 0.0f }, true ); + } + + int keyState = glfwGetKey( g_mainWindow, GLFW_KEY_SPACE ); + if ( keyState == GLFW_PRESS ) + { + if ( canJump ) + { + b2Body_ApplyLinearImpulseToCenter( m_characterId, { 0.0f, m_impulse }, true ); + m_jumpDelay = 0.5f; + m_jumping = true; + } + } + else + { + m_jumping = false; + } + + Sample::Step( settings ); + + g_draw.DrawString( 5, m_textLine, "Movement: A/D/Space" ); + m_textLine += m_textIncrement; + + g_draw.DrawString( 5, m_textLine, "Can jump = %s", canJump ? "true" : "false" ); + m_textLine += m_textIncrement; + + if ( settings.hertz > 0.0f ) + { + m_jumpDelay = b2MaxFloat( 0.0f, m_jumpDelay - 1.0f / settings.hertz ); + } + } + + static Sample* Create( Settings& settings ) + { + return new Platformer( settings ); + } + + bool m_jumping; + float m_radius; + float m_force; + float m_impulse; + float m_jumpDelay; + b2BodyId m_characterId; + b2BodyId m_platformId; + b2ShapeId m_platformShapeId; +}; + +static int samplePlatformer = RegisterSample( "Events", "Platformer", Platformer::Create ); + +// This shows how to process body events. +class BodyMove : public Sample +{ +public: + enum + { + e_count = 50 + }; + + explicit BodyMove( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 2.0f, 8.0f }; + g_camera.m_zoom = 25.0f * 0.55f; + } + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.friction = 0.1f; + + b2Polygon box = b2MakeOffsetBox( 12.0f, 0.1f, { -10.0f, -0.1f }, b2MakeRot(-0.15f * b2_pi) ); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + + box = b2MakeOffsetBox( 12.0f, 0.1f, { 10.0f, -0.1f }, b2MakeRot(0.15f * b2_pi) ); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + + shapeDef.restitution = 0.8f; + + box = b2MakeOffsetBox( 0.1f, 10.0f, { 19.9f, 10.0f }, b2Rot_identity ); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + + box = b2MakeOffsetBox( 0.1f, 10.0f, { -19.9f, 10.0f }, b2Rot_identity ); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + + box = b2MakeOffsetBox( 20.0f, 0.1f, { 0.0f, 20.1f }, b2Rot_identity ); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + } + + m_sleepCount = 0; + m_count = 0; + + m_explosionPosition = { 0.0f, -5.0f }; + m_explosionRadius = 10.0f; + m_explosionMagnitude = 6.0f; + } + + void CreateBodies() + { + b2Capsule capsule = { { -0.25f, 0.0f }, { 0.25f, 0.0f }, 0.25f }; + b2Circle circle = { { 0.0f, 0.0f }, 0.35f }; + b2Polygon square = b2MakeSquare( 0.35f ); + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + b2ShapeDef shapeDef = b2DefaultShapeDef(); + + float x = -5.0f, y = 10.0f; + for ( int32_t i = 0; i < 10 && m_count < e_count; ++i ) + { + bodyDef.position = { x, y }; + bodyDef.userData = m_bodyIds + m_count; + m_bodyIds[m_count] = b2CreateBody( m_worldId, &bodyDef ); + m_sleeping[m_count] = false; + + int remainder = m_count % 4; + if ( remainder == 0 ) + { + b2CreateCapsuleShape( m_bodyIds[m_count], &shapeDef, &capsule ); + } + else if ( remainder == 1 ) + { + b2CreateCircleShape( m_bodyIds[m_count], &shapeDef, &circle ); + } + else if ( remainder == 2 ) + { + b2CreatePolygonShape( m_bodyIds[m_count], &shapeDef, &square ); + } + else + { + b2Polygon poly = RandomPolygon( 0.75f ); + poly.radius = 0.1f; + b2CreatePolygonShape( m_bodyIds[m_count], &shapeDef, &poly ); + } + + m_count += 1; + x += 1.0f; + } + } + + void UpdateUI() override + { + float height = 100.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 240.0f, height ) ); + + ImGui::Begin( "Body Move", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize ); + + if ( ImGui::Button( "Explode" ) ) + { + b2World_Explode( m_worldId, m_explosionPosition, m_explosionRadius, m_explosionMagnitude ); + } + + ImGui::SliderFloat( "Magnitude", &m_explosionMagnitude, -8.0f, 8.0f, "%.1f" ); + + ImGui::End(); + } + + void Step( Settings& settings ) override + { + if ( settings.pause == false && ( m_stepCount & 15 ) == 15 && m_count < e_count ) + { + CreateBodies(); + } + + Sample::Step( settings ); + + // Process body events + b2BodyEvents events = b2World_GetBodyEvents( m_worldId ); + for ( int i = 0; i < events.moveCount; ++i ) + { + // draw the transform of every body that moved (not sleeping) + const b2BodyMoveEvent* event = events.moveEvents + i; + g_draw.DrawTransform( event->transform ); + + // this shows a somewhat contrived way to track body sleeping + b2BodyId* bodyId = static_cast( event->userData ); + ptrdiff_t diff = bodyId - m_bodyIds; + bool* sleeping = m_sleeping + diff; + + if ( event->fellAsleep ) + { + *sleeping = true; + m_sleepCount += 1; + } + else + { + if ( *sleeping ) + { + *sleeping = false; + m_sleepCount -= 1; + } + } + } + + g_draw.DrawCircle( m_explosionPosition, m_explosionRadius, b2_colorAzure ); + + g_draw.DrawString( 5, m_textLine, "sleep count: %d", m_sleepCount ); + m_textLine += m_textIncrement; + } + + static Sample* Create( Settings& settings ) + { + return new BodyMove( settings ); + } + + b2BodyId m_bodyIds[e_count]; + bool m_sleeping[e_count]; + int m_count; + int m_sleepCount; + b2Vec2 m_explosionPosition; + float m_explosionRadius; + float m_explosionMagnitude; +}; + +static int sampleBodyMove = RegisterSample( "Events", "Body Move", BodyMove::Create ); diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/sample_geometry.cpp b/tests/cpp-tests/Source/Box2DTestBed/samples/sample_geometry.cpp new file mode 100644 index 000000000000..7ed1a8dc77c2 --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/sample_geometry.cpp @@ -0,0 +1,220 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#include "draw.h" +#include "sample.h" +#include "settings.h" + +#include "box2d/math_functions.h" + +#include + +class ConvexHull : public Sample +{ +public: + enum + { + e_count = b2_maxPolygonVertices + }; + + explicit ConvexHull( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.5f, 0.0f }; + g_camera.m_zoom = 25.0f * 0.3f; + } + + m_generation = 0; + m_auto = false; + m_bulk = false; + Generate(); + } + + void Generate() + { +#if 0 + m_points[0] = { 5.65314484f, 0.204832315f }; + m_points[1] = {-5.65314484f, -0.204832315f }; + m_points[2] = {2.34463644f, 1.15731204f }; + m_points[3] = {0.0508846045f, 3.23230696f }; + m_points[4] = {-5.65314484f, -0.204832315f }; + m_points[5] = {-5.65314484f, -0.204832315f }; + m_points[6] = {3.73758054f, -1.11098099f }; + m_points[7] = {1.33504069f, -4.43795443f }; + + m_count = e_count; +#elif 0 + m_points[0] = { -0.328125, 0.179688 }; + m_points[1] = { -0.203125, 0.304688 }; + m_points[2] = { 0.171875, 0.304688 }; + m_points[3] = { 0.359375, 0.117188 }; + m_points[4] = { 0.359375, -0.195313 }; + m_points[5] = { 0.234375, -0.320313 }; + m_points[6] = { -0.265625, -0.257813 }; + m_points[7] = { -0.328125, -0.132813 }; + + b2Hull hull = b2ComputeHull( m_points, 8 ); + bool valid = b2ValidateHull( &hull ); + if ( valid == false ) + { + assert( valid ); + } + + m_count = e_count; +#else + + float angle = b2_pi * RandomFloat(); + b2Rot r = b2MakeRot( angle ); + + b2Vec2 lowerBound = { -4.0f, -4.0f }; + b2Vec2 upperBound = { 4.0f, 4.0f }; + + for ( int i = 0; i < e_count; ++i ) + { + float x = 10.0f * RandomFloat(); + float y = 10.0f * RandomFloat(); + + // Clamp onto a square to help create collinearities. + // This will stress the convex hull algorithm. + b2Vec2 v = b2Clamp( { x, y }, lowerBound, upperBound ); + m_points[i] = b2RotateVector( r, v ); + } + + m_count = e_count; +#endif + + m_generation += 1; + } + + void Keyboard( int key ) override + { + switch ( key ) + { + case GLFW_KEY_A: + m_auto = !m_auto; + break; + + case GLFW_KEY_B: + m_bulk = !m_bulk; + break; + + case GLFW_KEY_G: + Generate(); + break; + + default: + break; + } + } + + void Step( Settings& settings ) override + { + Sample::Step( settings ); + + g_draw.DrawString( 5, m_textLine, "Options: generate(g), auto(a), bulk(b)" ); + m_textLine += m_textIncrement; + + b2Hull hull; + bool valid = false; + float milliseconds = 0.0f; + + if ( m_bulk ) + { +#if 1 + // defect hunting + for ( int i = 0; i < 10000; ++i ) + { + Generate(); + hull = b2ComputeHull( m_points, m_count ); + if ( hull.count == 0 ) + { + // m_bulk = false; + // break; + continue; + } + + valid = b2ValidateHull( &hull ); + if ( valid == false || m_bulk == false ) + { + m_bulk = false; + break; + } + } +#else + // performance + Generate(); + b2Timer timer; + for ( int i = 0; i < 1000000; ++i ) + { + hull = b2ComputeHull( m_points, m_count ); + } + valid = hull.count > 0; + milliseconds = timer.GetMilliseconds(); +#endif + } + else + { + if ( m_auto ) + { + Generate(); + } + + hull = b2ComputeHull( m_points, m_count ); + if ( hull.count > 0 ) + { + valid = b2ValidateHull( &hull ); + if ( valid == false ) + { + m_auto = false; + } + } + } + + if ( valid == false ) + { + g_draw.DrawString( 5, m_textLine, "generation = %d, FAILED", m_generation ); + m_textLine += m_textIncrement; + } + else + { + g_draw.DrawString( 5, m_textLine, "generation = %d, count = %d", m_generation, hull.count ); + m_textLine += m_textIncrement; + } + + if ( milliseconds > 0.0f ) + { + g_draw.DrawString( 5, m_textLine, "milliseconds = %g", milliseconds ); + m_textLine += m_textIncrement; + } + + m_textLine += m_textIncrement; + + g_draw.DrawPolygon( hull.points, hull.count, b2_colorGray ); + + for ( int32_t i = 0; i < m_count; ++i ) + { + g_draw.DrawPoint( m_points[i], 5.0f, b2_colorBlue ); + g_draw.DrawString( b2Add( m_points[i], { 0.1f, 0.1f } ), "%d", i ); + } + + for ( int32_t i = 0; i < hull.count; ++i ) + { + g_draw.DrawPoint( hull.points[i], 6.0f, b2_colorGreen ); + } + } + + static Sample* Create( Settings& settings ) + { + return new ConvexHull( settings ); + } + + b2Vec2 m_points[b2_maxPolygonVertices]; + int32_t m_count; + int32_t m_generation; + bool m_auto; + bool m_bulk; +}; + +static int sampleIndex = RegisterSample( "Geometry", "Convex Hull", ConvexHull::Create ); diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/sample_joints.cpp b/tests/cpp-tests/Source/Box2DTestBed/samples/sample_joints.cpp new file mode 100644 index 000000000000..ace8c28089a7 --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/sample_joints.cpp @@ -0,0 +1,2467 @@ +// SPDX-FileCopyrightText: 2022 Erin Catto +// SPDX-License-Identifier: MIT + +#include "car.h" +#include "donut.h" +#include "doohickey.h" +#include "draw.h" +#include "human.h" +#include "sample.h" +#include "settings.h" + +#include "box2d/box2d.h" +#include "box2d/math_functions.h" + +#include +#include + +// Test the distance joint and all options +class DistanceJoint : public Sample +{ +public: + enum + { + e_maxCount = 10 + }; + + explicit DistanceJoint( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, 12.0f }; + g_camera.m_zoom = 25.0f * 0.35f; + } + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + m_groundId = b2CreateBody( m_worldId, &bodyDef ); + } + + m_count = 0; + m_hertz = 2.0f; + m_dampingRatio = 0.5f; + m_length = 1.0f; + m_minLength = m_length; + m_maxLength = m_length; + m_enableSpring = false; + m_enableLimit = false; + + for ( int i = 0; i < e_maxCount; ++i ) + { + m_bodyIds[i] = b2_nullBodyId; + m_jointIds[i] = b2_nullJointId; + } + + CreateScene( 1 ); + } + + void CreateScene( int newCount ) + { + // Must destroy joints before bodies + for ( int i = 0; i < m_count; ++i ) + { + b2DestroyJoint( m_jointIds[i] ); + m_jointIds[i] = b2_nullJointId; + } + + for ( int i = 0; i < m_count; ++i ) + { + b2DestroyBody( m_bodyIds[i] ); + m_bodyIds[i] = b2_nullBodyId; + } + + m_count = newCount; + + float radius = 0.25f; + b2Circle circle = { { 0.0f, 0.0f }, radius }; + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 20.0f; + + float yOffset = 20.0f; + + b2DistanceJointDef jointDef = b2DefaultDistanceJointDef(); + jointDef.hertz = m_hertz; + jointDef.dampingRatio = m_dampingRatio; + jointDef.length = m_length; + jointDef.minLength = m_minLength; + jointDef.maxLength = m_maxLength; + jointDef.enableSpring = m_enableSpring; + jointDef.enableLimit = m_enableLimit; + + b2BodyId prevBodyId = m_groundId; + for ( int i = 0; i < m_count; ++i ) + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.angularDamping = 0.1f; + bodyDef.position = { m_length * ( i + 1.0f ), yOffset }; + m_bodyIds[i] = b2CreateBody( m_worldId, &bodyDef ); + b2CreateCircleShape( m_bodyIds[i], &shapeDef, &circle ); + + b2Vec2 pivotA = { m_length * i, yOffset }; + b2Vec2 pivotB = { m_length * ( i + 1.0f ), yOffset }; + jointDef.bodyIdA = prevBodyId; + jointDef.bodyIdB = m_bodyIds[i]; + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivotA ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivotB ); + m_jointIds[i] = b2CreateDistanceJoint( m_worldId, &jointDef ); + + prevBodyId = m_bodyIds[i]; + } + } + + void UpdateUI() override + { + float height = 240.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 180.0f, height ) ); + + ImGui::Begin( "Distance Joint", nullptr, ImGuiWindowFlags_NoResize ); + ImGui::PushItemWidth( 100.0f ); + + if ( ImGui::SliderFloat( "Length", &m_length, 0.1f, 4.0f, "%3.1f" ) ) + { + for ( int i = 0; i < m_count; ++i ) + { + b2DistanceJoint_SetLength( m_jointIds[i], m_length ); + b2Joint_WakeBodies( m_jointIds[i] ); + } + } + + if ( ImGui::Checkbox( "Spring", &m_enableSpring ) ) + { + for ( int i = 0; i < m_count; ++i ) + { + b2DistanceJoint_EnableSpring( m_jointIds[i], m_enableSpring ); + b2Joint_WakeBodies( m_jointIds[i] ); + } + } + + if ( m_enableSpring ) + { + if ( ImGui::SliderFloat( "Hertz", &m_hertz, 0.0f, 15.0f, "%3.1f" ) ) + { + for ( int i = 0; i < m_count; ++i ) + { + b2DistanceJoint_SetSpringHertz( m_jointIds[i], m_hertz ); + b2Joint_WakeBodies( m_jointIds[i] ); + } + } + + if ( ImGui::SliderFloat( "Damping", &m_dampingRatio, 0.0f, 4.0f, "%3.1f" ) ) + { + for ( int i = 0; i < m_count; ++i ) + { + b2DistanceJoint_SetSpringDampingRatio( m_jointIds[i], m_dampingRatio ); + b2Joint_WakeBodies( m_jointIds[i] ); + } + } + } + + if ( ImGui::Checkbox( "Limit", &m_enableLimit ) ) + { + for ( int i = 0; i < m_count; ++i ) + { + b2DistanceJoint_EnableLimit( m_jointIds[i], m_enableLimit ); + b2Joint_WakeBodies( m_jointIds[i] ); + } + } + + if ( m_enableLimit ) + { + if ( ImGui::SliderFloat( "Min Length", &m_minLength, 0.1f, 4.0f, "%3.1f" ) ) + { + for ( int i = 0; i < m_count; ++i ) + { + b2DistanceJoint_SetLengthRange( m_jointIds[i], m_minLength, m_maxLength ); + b2Joint_WakeBodies( m_jointIds[i] ); + } + } + + if ( ImGui::SliderFloat( "Max Length", &m_maxLength, 0.1f, 4.0f, "%3.1f" ) ) + { + for ( int i = 0; i < m_count; ++i ) + { + b2DistanceJoint_SetLengthRange( m_jointIds[i], m_minLength, m_maxLength ); + b2Joint_WakeBodies( m_jointIds[i] ); + } + } + } + + int count = m_count; + if ( ImGui::SliderInt( "Count", &count, 1, e_maxCount ) ) + { + CreateScene( count ); + } + + ImGui::PopItemWidth(); + ImGui::End(); + } + + static Sample* Create( Settings& settings ) + { + return new DistanceJoint( settings ); + } + + b2BodyId m_groundId; + b2BodyId m_bodyIds[e_maxCount]; + b2JointId m_jointIds[e_maxCount]; + int m_count; + float m_hertz; + float m_dampingRatio; + float m_length; + float m_minLength; + float m_maxLength; + bool m_enableSpring; + bool m_enableLimit; +}; + +static int sampleDistanceJoint = RegisterSample( "Joints", "Distance Joint", DistanceJoint::Create ); + +/// This test shows how to use a motor joint. A motor joint +/// can be used to animate a dynamic body. With finite motor forces +/// the body can be blocked by collision with other bodies. +/// By setting the correction factor to zero, the motor joint acts +/// like top-down dry friction. +class MotorJoint : public Sample +{ +public: + explicit MotorJoint( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, 7.0f }; + g_camera.m_zoom = 25.0f * 0.4f; + } + + b2BodyId groundId; + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + groundId = b2CreateBody( m_worldId, &bodyDef ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Segment segment = { { -20.0f, 0.0f }, { 20.0f, 0.0f } }; + b2CreateSegmentShape( groundId, &shapeDef, &segment ); + } + + // Define motorized body + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = { 0.0f, 8.0f }; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + + b2Polygon box = b2MakeBox( 2.0f, 0.5f ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 1.0f; + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + + m_maxForce = 500.0f; + m_maxTorque = 500.0f; + m_correctionFactor = 0.3f; + + b2MotorJointDef jointDef = b2DefaultMotorJointDef(); + jointDef.bodyIdA = groundId; + jointDef.bodyIdB = bodyId; + jointDef.maxForce = m_maxForce; + jointDef.maxTorque = m_maxTorque; + jointDef.correctionFactor = m_correctionFactor; + + m_jointId = b2CreateMotorJoint( m_worldId, &jointDef ); + } + + m_go = true; + m_time = 0.0f; + } + + void UpdateUI() override + { + float height = 140.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 240.0f, height ) ); + + ImGui::Begin( "Motor Joint", nullptr, ImGuiWindowFlags_NoResize ); + + if ( ImGui::Checkbox( "Go", &m_go ) ) + { + } + + if ( ImGui::SliderFloat( "Max Force", &m_maxForce, 0.0f, 1000.0f, "%.0f" ) ) + { + b2MotorJoint_SetMaxForce( m_jointId, m_maxForce ); + } + + if ( ImGui::SliderFloat( "Max Torque", &m_maxTorque, 0.0f, 1000.0f, "%.0f" ) ) + { + b2MotorJoint_SetMaxTorque( m_jointId, m_maxTorque ); + } + + if ( ImGui::SliderFloat( "Correction", &m_correctionFactor, 0.0f, 1.0f, "%.1f" ) ) + { + b2MotorJoint_SetCorrectionFactor( m_jointId, m_correctionFactor ); + } + + ImGui::End(); + } + + void Step( Settings& settings ) override + { + if ( m_go && settings.hertz > 0.0f ) + { + m_time += 1.0f / settings.hertz; + } + + b2Vec2 linearOffset; + linearOffset.x = 6.0f * sinf( 2.0f * m_time ); + linearOffset.y = 8.0f + 4.0f * sinf( 1.0f * m_time ); + + float angularOffset = b2_pi * sinf( -0.5f * m_time ); + + b2MotorJoint_SetLinearOffset( m_jointId, linearOffset ); + b2MotorJoint_SetAngularOffset( m_jointId, angularOffset ); + + b2Transform transform = { linearOffset, b2MakeRot( angularOffset ) }; + g_draw.DrawTransform( transform ); + + Sample::Step( settings ); + + b2Vec2 force = b2Joint_GetConstraintForce( m_jointId ); + float torque = b2Joint_GetConstraintTorque( m_jointId ); + + g_draw.DrawString( 5, m_textLine, "force = {%3.f, %3.f}, torque = %3.f", force.x, force.y, torque ); + m_textLine += 15; + } + + static Sample* Create( Settings& settings ) + { + return new MotorJoint( settings ); + } + + b2JointId m_jointId; + float m_time; + float m_maxForce; + float m_maxTorque; + float m_correctionFactor; + bool m_go; +}; + +static int sampleMotorJoint = RegisterSample( "Joints", "Motor Joint", MotorJoint::Create ); + +class RevoluteJoint : public Sample +{ +public: + explicit RevoluteJoint( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, 15.5f }; + g_camera.m_zoom = 25.0f * 0.7f; + } + + b2BodyId groundId = b2_nullBodyId; + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.position = { 0.0f, -1.0f }; + groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2Polygon box = b2MakeBox( 40.0f, 1.0f ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + } + + m_enableSpring = false; + m_enableLimit = true; + m_enableMotor = false; + m_hertz = 1.0f; + m_dampingRatio = 0.5f; + m_motorSpeed = 1.0f; + m_motorTorque = 1000.0f; + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = { -10.0f, 20.0f }; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 1.0f; + + b2Capsule capsule = { { 0.0f, -1.0f }, { 0.0f, 6.0f }, 0.5f }; + b2CreateCapsuleShape( bodyId, &shapeDef, &capsule ); + + b2Vec2 pivot = { -10.0f, 20.5f }; + b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef(); + jointDef.bodyIdA = groundId; + jointDef.bodyIdB = bodyId; + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); + jointDef.enableSpring = m_enableSpring; + jointDef.hertz = m_hertz; + jointDef.dampingRatio = m_dampingRatio; + jointDef.motorSpeed = m_motorSpeed; + jointDef.maxMotorTorque = m_motorTorque; + jointDef.enableMotor = m_enableMotor; + jointDef.referenceAngle = 0.5f * b2_pi; + jointDef.lowerAngle = -0.5f * b2_pi; + jointDef.upperAngle = 0.75f * b2_pi; + jointDef.enableLimit = m_enableLimit; + + m_jointId1 = b2CreateRevoluteJoint( m_worldId, &jointDef ); + } + + { + b2Circle circle = { 0 }; + circle.radius = 2.0f; + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = { 5.0f, 30.0f }; + m_ball = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 1.0f; + + b2CreateCircleShape( m_ball, &shapeDef, &circle ); + } + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.position = { 20.0f, 10.0f }; + bodyDef.type = b2_dynamicBody; + b2BodyId body = b2CreateBody( m_worldId, &bodyDef ); + + b2Polygon box = b2MakeOffsetBox( 10.0f, 0.5f, { -10.0f, 0.0f }, b2Rot_identity ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 1.0f; + b2CreatePolygonShape( body, &shapeDef, &box ); + + b2Vec2 pivot = { 19.0f, 10.0f }; + b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef(); + jointDef.bodyIdA = groundId; + jointDef.bodyIdB = body; + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); + jointDef.lowerAngle = -0.25f * b2_pi; + jointDef.upperAngle = 0.0f * b2_pi; + jointDef.enableLimit = true; + jointDef.enableMotor = true; + jointDef.motorSpeed = 0.0f; + jointDef.maxMotorTorque = m_motorTorque; + + m_jointId2 = b2CreateRevoluteJoint( m_worldId, &jointDef ); + } + } + + void UpdateUI() override + { + float height = 220.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 240.0f, height ) ); + + ImGui::Begin( "Revolute Joint", nullptr, ImGuiWindowFlags_NoResize ); + + if ( ImGui::Checkbox( "Limit", &m_enableLimit ) ) + { + b2RevoluteJoint_EnableLimit( m_jointId1, m_enableLimit ); + b2Joint_WakeBodies( m_jointId1 ); + } + + if ( ImGui::Checkbox( "Motor", &m_enableMotor ) ) + { + b2RevoluteJoint_EnableMotor( m_jointId1, m_enableMotor ); + b2Joint_WakeBodies( m_jointId1 ); + } + + if ( m_enableMotor ) + { + if ( ImGui::SliderFloat( "Max Torque", &m_motorTorque, 0.0f, 5000.0f, "%.0f" ) ) + { + b2RevoluteJoint_SetMaxMotorTorque( m_jointId1, m_motorTorque ); + b2Joint_WakeBodies( m_jointId1 ); + } + + if ( ImGui::SliderFloat( "Speed", &m_motorSpeed, -20.0f, 20.0f, "%.0f" ) ) + { + b2RevoluteJoint_SetMotorSpeed( m_jointId1, m_motorSpeed ); + b2Joint_WakeBodies( m_jointId1 ); + } + } + + if ( ImGui::Checkbox( "Spring", &m_enableSpring ) ) + { + b2RevoluteJoint_EnableSpring( m_jointId1, m_enableSpring ); + b2Joint_WakeBodies( m_jointId1 ); + } + + if ( m_enableSpring ) + { + if ( ImGui::SliderFloat( "Hertz", &m_hertz, 0.0f, 10.0f, "%.1f" ) ) + { + b2RevoluteJoint_SetSpringHertz( m_jointId1, m_hertz ); + b2Joint_WakeBodies( m_jointId1 ); + } + + if ( ImGui::SliderFloat( "Damping", &m_dampingRatio, 0.0f, 2.0f, "%.1f" ) ) + { + b2RevoluteJoint_SetSpringDampingRatio( m_jointId1, m_dampingRatio ); + b2Joint_WakeBodies( m_jointId1 ); + } + } + + ImGui::End(); + } + + void Step( Settings& settings ) override + { + Sample::Step( settings ); + + float angle1 = b2RevoluteJoint_GetAngle( m_jointId1 ); + g_draw.DrawString( 5, m_textLine, "Angle (Deg) 1 = %2.1f", angle1 ); + m_textLine += m_textIncrement; + + float torque1 = b2RevoluteJoint_GetMotorTorque( m_jointId1 ); + g_draw.DrawString( 5, m_textLine, "Motor Torque 1 = %4.1f", torque1 ); + m_textLine += m_textIncrement; + + float torque2 = b2RevoluteJoint_GetMotorTorque( m_jointId2 ); + g_draw.DrawString( 5, m_textLine, "Motor Torque 2 = %4.1f", torque2 ); + m_textLine += m_textIncrement; + } + + static Sample* Create( Settings& settings ) + { + return new RevoluteJoint( settings ); + } + + b2BodyId m_ball; + b2JointId m_jointId1; + b2JointId m_jointId2; + float m_motorSpeed; + float m_motorTorque; + float m_hertz; + float m_dampingRatio; + bool m_enableSpring; + bool m_enableMotor; + bool m_enableLimit; +}; + +static int sampleRevolute = RegisterSample( "Joints", "Revolute", RevoluteJoint::Create ); + +class PrismaticJoint : public Sample +{ +public: + explicit PrismaticJoint( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, 8.0f }; + g_camera.m_zoom = 25.0f * 0.5f; + } + + b2BodyId groundId; + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + groundId = b2CreateBody( m_worldId, &bodyDef ); + } + + m_enableSpring = false; + m_enableLimit = true; + m_enableMotor = false; + m_motorSpeed = 2.0f; + m_motorForce = 25.0f; + m_hertz = 1.0f; + m_dampingRatio = 0.5f; + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.position = { 0.0f, 10.0f }; + bodyDef.type = b2_dynamicBody; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Polygon box = b2MakeBox( 0.5f, 2.0f ); + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + + b2Vec2 pivot = { 0.0f, 9.0f }; + // b2Vec2 axis = b2Normalize({1.0f, 0.0f}); + b2Vec2 axis = b2Normalize( { 1.0f, 1.0f } ); + b2PrismaticJointDef jointDef = b2DefaultPrismaticJointDef(); + jointDef.bodyIdA = groundId; + jointDef.bodyIdB = bodyId; + jointDef.localAxisA = b2Body_GetLocalVector( jointDef.bodyIdA, axis ); + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); + jointDef.motorSpeed = m_motorSpeed; + jointDef.maxMotorForce = m_motorForce; + jointDef.enableMotor = m_enableMotor; + jointDef.lowerTranslation = -10.0f; + jointDef.upperTranslation = 10.0f; + jointDef.enableLimit = m_enableLimit; + jointDef.enableSpring = m_enableSpring; + jointDef.hertz = m_hertz; + jointDef.dampingRatio = m_dampingRatio; + + m_jointId = b2CreatePrismaticJoint( m_worldId, &jointDef ); + } + } + + void UpdateUI() override + { + float height = 220.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 240.0f, height ) ); + + ImGui::Begin( "Prismatic Joint", nullptr, ImGuiWindowFlags_NoResize ); + + if ( ImGui::Checkbox( "Limit", &m_enableLimit ) ) + { + b2PrismaticJoint_EnableLimit( m_jointId, m_enableLimit ); + b2Joint_WakeBodies( m_jointId ); + } + + if ( ImGui::Checkbox( "Motor", &m_enableMotor ) ) + { + b2PrismaticJoint_EnableMotor( m_jointId, m_enableMotor ); + b2Joint_WakeBodies( m_jointId ); + } + + if ( m_enableMotor ) + { + if ( ImGui::SliderFloat( "Max Force", &m_motorForce, 0.0f, 200.0f, "%.0f" ) ) + { + b2PrismaticJoint_SetMaxMotorForce( m_jointId, m_motorForce ); + b2Joint_WakeBodies( m_jointId ); + } + + if ( ImGui::SliderFloat( "Speed", &m_motorSpeed, -40.0f, 40.0f, "%.0f" ) ) + { + b2PrismaticJoint_SetMotorSpeed( m_jointId, m_motorSpeed ); + b2Joint_WakeBodies( m_jointId ); + } + } + + if ( ImGui::Checkbox( "Spring", &m_enableSpring ) ) + { + b2PrismaticJoint_EnableSpring( m_jointId, m_enableSpring ); + b2Joint_WakeBodies( m_jointId ); + } + + if ( m_enableSpring ) + { + if ( ImGui::SliderFloat( "Hertz", &m_hertz, 0.0f, 10.0f, "%.1f" ) ) + { + b2PrismaticJoint_SetSpringHertz( m_jointId, m_hertz ); + b2Joint_WakeBodies( m_jointId ); + } + + if ( ImGui::SliderFloat( "Damping", &m_dampingRatio, 0.0f, 2.0f, "%.1f" ) ) + { + b2PrismaticJoint_SetSpringDampingRatio( m_jointId, m_dampingRatio ); + b2Joint_WakeBodies( m_jointId ); + } + } + + ImGui::End(); + } + + void Step( Settings& settings ) override + { + Sample::Step( settings ); + + float force = b2PrismaticJoint_GetMotorForce( m_jointId ); + g_draw.DrawString( 5, m_textLine, "Motor Force = %4.1f", force ); + m_textLine += m_textIncrement; + } + + static Sample* Create( Settings& settings ) + { + return new PrismaticJoint( settings ); + } + + b2JointId m_jointId; + float m_motorSpeed; + float m_motorForce; + float m_hertz; + float m_dampingRatio; + bool m_enableSpring; + bool m_enableMotor; + bool m_enableLimit; +}; + +static int samplePrismatic = RegisterSample( "Joints", "Prismatic", PrismaticJoint::Create ); + +class WheelJoint : public Sample +{ +public: + explicit WheelJoint( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, 10.0f }; + g_camera.m_zoom = 25.0f * 0.15f; + } + + b2BodyId groundId; + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + groundId = b2CreateBody( m_worldId, &bodyDef ); + } + + m_enableSpring = true; + m_enableLimit = true; + m_enableMotor = true; + m_motorSpeed = 2.0f; + m_motorTorque = 5.0f; + m_hertz = 1.0f; + m_dampingRatio = 0.7f; + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.position = { 0.0f, 10.25f }; + bodyDef.type = b2_dynamicBody; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Capsule capsule = { { 0.0f, -0.5f }, { 0.0f, 0.5f }, 0.5f }; + b2CreateCapsuleShape( bodyId, &shapeDef, &capsule ); + + b2Vec2 pivot = { 0.0f, 10.0f }; + b2Vec2 axis = b2Normalize( { 1.0f, 1.0f } ); + b2WheelJointDef jointDef = b2DefaultWheelJointDef(); + jointDef.bodyIdA = groundId; + jointDef.bodyIdB = bodyId; + jointDef.localAxisA = b2Body_GetLocalVector( jointDef.bodyIdA, axis ); + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); + jointDef.motorSpeed = m_motorSpeed; + jointDef.maxMotorTorque = m_motorTorque; + jointDef.enableMotor = m_enableMotor; + jointDef.lowerTranslation = -3.0f; + jointDef.upperTranslation = 3.0f; + jointDef.enableLimit = m_enableLimit; + jointDef.hertz = m_hertz; + jointDef.dampingRatio = m_dampingRatio; + + m_jointId = b2CreateWheelJoint( m_worldId, &jointDef ); + } + } + + void UpdateUI() override + { + float height = 220.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 240.0f, height ) ); + + ImGui::Begin( "Wheel Joint", nullptr, ImGuiWindowFlags_NoResize ); + + if ( ImGui::Checkbox( "Limit", &m_enableLimit ) ) + { + b2WheelJoint_EnableLimit( m_jointId, m_enableLimit ); + } + + if ( ImGui::Checkbox( "Motor", &m_enableMotor ) ) + { + b2WheelJoint_EnableMotor( m_jointId, m_enableMotor ); + } + + if ( m_enableMotor ) + { + if ( ImGui::SliderFloat( "Torque", &m_motorTorque, 0.0f, 20.0f, "%.0f" ) ) + { + b2WheelJoint_SetMaxMotorTorque( m_jointId, m_motorTorque ); + } + + if ( ImGui::SliderFloat( "Speed", &m_motorSpeed, -20.0f, 20.0f, "%.0f" ) ) + { + b2WheelJoint_SetMotorSpeed( m_jointId, m_motorSpeed ); + } + } + + if ( ImGui::Checkbox( "Spring", &m_enableSpring ) ) + { + b2WheelJoint_EnableSpring( m_jointId, m_enableSpring ); + } + + if ( m_enableSpring ) + { + if ( ImGui::SliderFloat( "Hertz", &m_hertz, 0.0f, 10.0f, "%.1f" ) ) + { + b2WheelJoint_SetSpringHertz( m_jointId, m_hertz ); + } + + if ( ImGui::SliderFloat( "Damping", &m_dampingRatio, 0.0f, 2.0f, "%.1f" ) ) + { + b2WheelJoint_SetSpringDampingRatio( m_jointId, m_dampingRatio ); + } + } + + ImGui::End(); + } + + void Step( Settings& settings ) override + { + Sample::Step( settings ); + + float torque = b2WheelJoint_GetMotorTorque( m_jointId ); + g_draw.DrawString( 5, m_textLine, "Motor Torque = %4.1f", torque ); + m_textLine += m_textIncrement; + } + + static Sample* Create( Settings& settings ) + { + return new WheelJoint( settings ); + } + + b2JointId m_jointId; + float m_hertz; + float m_dampingRatio; + float m_motorSpeed; + float m_motorTorque; + bool m_enableSpring; + bool m_enableMotor; + bool m_enableLimit; +}; + +static int sampleWheel = RegisterSample( "Joints", "Wheel", WheelJoint::Create ); + +// A suspension bridge +class Bridge : public Sample +{ +public: + enum + { + e_count = 160 + }; + + explicit Bridge( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_zoom = 25.0f * 2.5f; + } + + b2BodyId groundId = b2_nullBodyId; + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + groundId = b2CreateBody( m_worldId, &bodyDef ); + } + + { + b2Polygon box = b2MakeBox( 0.5f, 0.125f ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 20.0f; + + b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef(); + int jointIndex = 0; + m_frictionTorque = 200.0f; + m_gravityScale = 1.0f; + + float xbase = -80.0f; + + b2BodyId prevBodyId = groundId; + for ( int i = 0; i < e_count; ++i ) + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = { xbase + 0.5f + 1.0f * i, 20.0f }; + bodyDef.linearDamping = 0.1f; + bodyDef.angularDamping = 0.1f; + m_bodyIds[i] = b2CreateBody( m_worldId, &bodyDef ); + b2CreatePolygonShape( m_bodyIds[i], &shapeDef, &box ); + + b2Vec2 pivot = { xbase + 1.0f * i, 20.0f }; + jointDef.bodyIdA = prevBodyId; + jointDef.bodyIdB = m_bodyIds[i]; + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); + jointDef.enableMotor = true; + jointDef.maxMotorTorque = m_frictionTorque; + m_jointIds[jointIndex++] = b2CreateRevoluteJoint( m_worldId, &jointDef ); + + prevBodyId = m_bodyIds[i]; + } + + b2Vec2 pivot = { xbase + 1.0f * e_count, 20.0f }; + jointDef.bodyIdA = prevBodyId; + jointDef.bodyIdB = groundId; + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); + jointDef.enableMotor = true; + jointDef.maxMotorTorque = m_frictionTorque; + m_jointIds[jointIndex++] = b2CreateRevoluteJoint( m_worldId, &jointDef ); + + assert( jointIndex == e_count + 1 ); + } + + for ( int i = 0; i < 2; ++i ) + { + b2Vec2 vertices[3] = { { -0.5f, 0.0f }, { 0.5f, 0.0f }, { 0.0f, 1.5f } }; + + b2Hull hull = b2ComputeHull( vertices, 3 ); + b2Polygon triangle = b2MakePolygon( &hull, 0.0f ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 20.0f; + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = { -8.0f + 8.0f * i, 22.0f }; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2CreatePolygonShape( bodyId, &shapeDef, &triangle ); + } + + for ( int i = 0; i < 3; ++i ) + { + b2Circle circle = { { 0.0f, 0.0f }, 0.5f }; + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 20.0f; + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = { -6.0f + 6.0f * i, 25.0f }; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2CreateCircleShape( bodyId, &shapeDef, &circle ); + } + } + + void UpdateUI() override + { + float height = 80.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 240.0f, height ) ); + + ImGui::Begin( "Bridge", nullptr, ImGuiWindowFlags_NoResize ); + + // Slider takes half the window + ImGui::PushItemWidth( ImGui::GetWindowWidth() * 0.5f ); + bool updateFriction = ImGui::SliderFloat( "Joint Friction", &m_frictionTorque, 0.0f, 1000.0f, "%2.f" ); + if ( updateFriction ) + { + for ( int i = 0; i <= e_count; ++i ) + { + b2RevoluteJoint_SetMaxMotorTorque( m_jointIds[i], m_frictionTorque ); + } + } + + if ( ImGui::SliderFloat( "Gravity scale", &m_gravityScale, -1.0f, 1.0f, "%.1f" ) ) + { + for ( int i = 0; i < e_count; ++i ) + { + b2Body_SetGravityScale( m_bodyIds[i], m_gravityScale ); + } + } + + ImGui::End(); + } + + static Sample* Create( Settings& settings ) + { + return new Bridge( settings ); + } + + b2BodyId m_bodyIds[e_count]; + b2JointId m_jointIds[e_count + 1]; + float m_frictionTorque; + float m_gravityScale; +}; + +static int sampleBridgeIndex = RegisterSample( "Joints", "Bridge", Bridge::Create ); + +class BallAndChain : public Sample +{ +public: + enum + { + e_count = 30 + }; + + explicit BallAndChain( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, -8.0f }; + g_camera.m_zoom = 27.5f; + } + + b2BodyId groundId = b2_nullBodyId; + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + groundId = b2CreateBody( m_worldId, &bodyDef ); + } + + m_frictionTorque = 100.0f; + + { + float hx = 0.5f; + b2Capsule capsule = { { -hx, 0.0f }, { hx, 0.0f }, 0.125f }; + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 20.0f; + + b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef(); + + int jointIndex = 0; + + b2BodyId prevBodyId = groundId; + for ( int i = 0; i < e_count; ++i ) + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = { ( 1.0f + 2.0f * i ) * hx, e_count * hx }; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2CreateCapsuleShape( bodyId, &shapeDef, &capsule ); + + b2Vec2 pivot = { ( 2.0f * i ) * hx, e_count * hx }; + jointDef.bodyIdA = prevBodyId; + jointDef.bodyIdB = bodyId; + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); + // jointDef.enableMotor = true; + jointDef.maxMotorTorque = m_frictionTorque; + m_jointIds[jointIndex++] = b2CreateRevoluteJoint( m_worldId, &jointDef ); + + prevBodyId = bodyId; + } + + b2Circle circle = { { 0.0f, 0.0f }, 4.0f }; + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = { ( 1.0f + 2.0f * e_count ) * hx + circle.radius - hx, e_count * hx }; + + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2CreateCircleShape( bodyId, &shapeDef, &circle ); + + b2Vec2 pivot = { ( 2.0f * e_count ) * hx, e_count * hx }; + jointDef.bodyIdA = prevBodyId; + jointDef.bodyIdB = bodyId; + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); + jointDef.enableMotor = true; + jointDef.maxMotorTorque = m_frictionTorque; + m_jointIds[jointIndex++] = b2CreateRevoluteJoint( m_worldId, &jointDef ); + assert( jointIndex == e_count + 1 ); + } + } + + void UpdateUI() override + { + float height = 60.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 240.0f, height ) ); + + ImGui::Begin( "Ball and Chain", nullptr, ImGuiWindowFlags_NoResize ); + + bool updateFriction = ImGui::SliderFloat( "Joint Friction", &m_frictionTorque, 0.0f, 1000.0f, "%2.f" ); + if ( updateFriction ) + { + for ( int i = 0; i <= e_count; ++i ) + { + b2RevoluteJoint_SetMaxMotorTorque( m_jointIds[i], m_frictionTorque ); + } + } + + ImGui::End(); + } + + static Sample* Create( Settings& settings ) + { + return new BallAndChain( settings ); + } + + b2JointId m_jointIds[e_count + 1]; + float m_frictionTorque; +}; + +static int sampleBallAndChainIndex = RegisterSample( "Joints", "Ball & Chain", BallAndChain::Create ); + +// This sample shows the limitations of an iterative solver. The cantilever sags even though the weld +// joint is stiff as possible. +class Cantilever : public Sample +{ +public: + enum + { + e_count = 8 + }; + + explicit Cantilever( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, 0.0f }; + g_camera.m_zoom = 25.0f * 0.35f; + } + + b2BodyId groundId = b2_nullBodyId; + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + groundId = b2CreateBody( m_worldId, &bodyDef ); + } + + { + m_linearHertz = 15.0f; + m_linearDampingRatio = 0.5f; + m_angularHertz = 5.0f; + m_angularDampingRatio = 0.5f; + m_gravityScale = 1.0f; + m_collideConnected = false; + + float hx = 0.5f; + b2Capsule capsule = { { -hx, 0.0f }, { hx, 0.0f }, 0.125f }; + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 20.0f; + + b2WeldJointDef jointDef = b2DefaultWeldJointDef(); + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.isAwake = false; + + b2BodyId prevBodyId = groundId; + for ( int i = 0; i < e_count; ++i ) + { + bodyDef.position = { ( 1.0f + 2.0f * i ) * hx, 0.0f }; + m_bodyIds[i] = b2CreateBody( m_worldId, &bodyDef ); + b2CreateCapsuleShape( m_bodyIds[i], &shapeDef, &capsule ); + + b2Vec2 pivot = { ( 2.0f * i ) * hx, 0.0f }; + jointDef.bodyIdA = prevBodyId; + jointDef.bodyIdB = m_bodyIds[i]; + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); + jointDef.linearHertz = m_linearHertz; + jointDef.linearDampingRatio = m_linearDampingRatio; + jointDef.angularHertz = m_angularHertz; + jointDef.angularDampingRatio = m_angularDampingRatio; + jointDef.collideConnected = m_collideConnected; + m_jointIds[i] = b2CreateWeldJoint( m_worldId, &jointDef ); + + prevBodyId = m_bodyIds[i]; + } + + m_tipId = prevBodyId; + } + } + + void UpdateUI() override + { + float height = 180.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 240.0f, height ) ); + + ImGui::Begin( "Cantilever", nullptr, ImGuiWindowFlags_NoResize ); + ImGui::PushItemWidth( 100.0f ); + + if ( ImGui::SliderFloat( "Linear Hertz", &m_linearHertz, 0.0f, 20.0f, "%.0f" ) ) + { + for ( int i = 0; i < e_count; ++i ) + { + b2WeldJoint_SetLinearHertz( m_jointIds[i], m_linearHertz ); + } + } + + if ( ImGui::SliderFloat( "Linear Damping Ratio", &m_linearDampingRatio, 0.0f, 10.0f, "%.1f" ) ) + { + for ( int i = 0; i < e_count; ++i ) + { + b2WeldJoint_SetLinearDampingRatio( m_jointIds[i], m_linearDampingRatio ); + } + } + + if ( ImGui::SliderFloat( "Angular Hertz", &m_angularHertz, 0.0f, 20.0f, "%.0f" ) ) + { + for ( int i = 0; i < e_count; ++i ) + { + b2WeldJoint_SetAngularHertz( m_jointIds[i], m_angularHertz ); + } + } + + if ( ImGui::SliderFloat( "Angular Damping Ratio", &m_angularDampingRatio, 0.0f, 10.0f, "%.1f" ) ) + { + for ( int i = 0; i < e_count; ++i ) + { + b2WeldJoint_SetAngularDampingRatio( m_jointIds[i], m_angularDampingRatio ); + } + } + + if ( ImGui::Checkbox( "Collide Connected", &m_collideConnected ) ) + { + for ( int i = 0; i < e_count; ++i ) + { + b2Joint_SetCollideConnected( m_jointIds[i], m_collideConnected ); + } + } + + if ( ImGui::SliderFloat( "Gravity Scale", &m_gravityScale, -1.0f, 1.0f, "%.1f" ) ) + { + for ( int i = 0; i < e_count; ++i ) + { + b2Body_SetGravityScale( m_bodyIds[i], m_gravityScale ); + } + } + + ImGui::PopItemWidth(); + ImGui::End(); + } + + void Step( Settings& settings ) override + { + Sample::Step( settings ); + + b2Vec2 tipPosition = b2Body_GetPosition( m_tipId ); + g_draw.DrawString( 5, m_textLine, "tip-y = %.2f", tipPosition.y ); + m_textLine += m_textIncrement; + } + + static Sample* Create( Settings& settings ) + { + return new Cantilever( settings ); + } + + float m_linearHertz; + float m_linearDampingRatio; + float m_angularHertz; + float m_angularDampingRatio; + float m_gravityScale; + b2BodyId m_tipId; + b2BodyId m_bodyIds[e_count]; + b2JointId m_jointIds[e_count]; + bool m_collideConnected; +}; + +static int sampleCantileverIndex = RegisterSample( "Joints", "Cantilever", Cantilever::Create ); + +// This test ensures joints work correctly with bodies that have fixed rotation +class FixedRotation : public Sample +{ +public: + enum + { + e_count = 6 + }; + + explicit FixedRotation( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, 8.0f }; + g_camera.m_zoom = 25.0f * 0.7f; + } + + b2BodyDef bodyDef = b2DefaultBodyDef(); + m_groundId = b2CreateBody( m_worldId, &bodyDef ); + m_fixedRotation = true; + + for ( int i = 0; i < e_count; ++i ) + { + m_bodyIds[i] = b2_nullBodyId; + m_jointIds[i] = b2_nullJointId; + } + + CreateScene(); + } + + void CreateScene() + { + for ( int i = 0; i < e_count; ++i ) + { + if ( B2_IS_NON_NULL( m_jointIds[i] ) ) + { + b2DestroyJoint( m_jointIds[i] ); + m_jointIds[i] = b2_nullJointId; + } + + if ( B2_IS_NON_NULL( m_bodyIds[i] ) ) + { + b2DestroyBody( m_bodyIds[i] ); + m_bodyIds[i] = b2_nullBodyId; + } + } + + b2Vec2 position = { -12.5f, 10.0f }; + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.fixedRotation = m_fixedRotation; + + b2Polygon box = b2MakeBox( 1.0f, 1.0f ); + + int index = 0; + + // distance joint + { + assert( index < e_count ); + + bodyDef.position = position; + m_bodyIds[index] = b2CreateBody( m_worldId, &bodyDef ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2CreatePolygonShape( m_bodyIds[index], &shapeDef, &box ); + + float length = 2.0f; + b2Vec2 pivot1 = { position.x, position.y + 1.0f + length }; + b2Vec2 pivot2 = { position.x, position.y + 1.0f }; + b2DistanceJointDef jointDef = b2DefaultDistanceJointDef(); + jointDef.bodyIdA = m_groundId; + jointDef.bodyIdB = m_bodyIds[index]; + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot1 ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot2 ); + jointDef.length = length; + m_jointIds[index] = b2CreateDistanceJoint( m_worldId, &jointDef ); + } + + position.x += 5.0f; + ++index; + + // motor joint + { + assert( index < e_count ); + + bodyDef.position = position; + m_bodyIds[index] = b2CreateBody( m_worldId, &bodyDef ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2CreatePolygonShape( m_bodyIds[index], &shapeDef, &box ); + + b2MotorJointDef jointDef = b2DefaultMotorJointDef(); + jointDef.bodyIdA = m_groundId; + jointDef.bodyIdB = m_bodyIds[index]; + jointDef.linearOffset = position; + jointDef.maxForce = 200.0f; + jointDef.maxTorque = 20.0f; + m_jointIds[index] = b2CreateMotorJoint( m_worldId, &jointDef ); + } + + position.x += 5.0f; + ++index; + + // prismatic joint + { + assert( index < e_count ); + + bodyDef.position = position; + m_bodyIds[index] = b2CreateBody( m_worldId, &bodyDef ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2CreatePolygonShape( m_bodyIds[index], &shapeDef, &box ); + + b2Vec2 pivot = { position.x - 1.0f, position.y }; + b2PrismaticJointDef jointDef = b2DefaultPrismaticJointDef(); + jointDef.bodyIdA = m_groundId; + jointDef.bodyIdB = m_bodyIds[index]; + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); + jointDef.localAxisA = b2Body_GetLocalVector( jointDef.bodyIdA, { 1.0f, 0.0f } ); + m_jointIds[index] = b2CreatePrismaticJoint( m_worldId, &jointDef ); + } + + position.x += 5.0f; + ++index; + + // revolute joint + { + assert( index < e_count ); + + bodyDef.position = position; + m_bodyIds[index] = b2CreateBody( m_worldId, &bodyDef ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2CreatePolygonShape( m_bodyIds[index], &shapeDef, &box ); + + b2Vec2 pivot = { position.x - 1.0f, position.y }; + b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef(); + jointDef.bodyIdA = m_groundId; + jointDef.bodyIdB = m_bodyIds[index]; + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); + m_jointIds[index] = b2CreateRevoluteJoint( m_worldId, &jointDef ); + } + + position.x += 5.0f; + ++index; + + // weld joint + { + assert( index < e_count ); + + bodyDef.position = position; + m_bodyIds[index] = b2CreateBody( m_worldId, &bodyDef ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2CreatePolygonShape( m_bodyIds[index], &shapeDef, &box ); + + b2Vec2 pivot = { position.x - 1.0f, position.y }; + b2WeldJointDef jointDef = b2DefaultWeldJointDef(); + jointDef.bodyIdA = m_groundId; + jointDef.bodyIdB = m_bodyIds[index]; + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); + jointDef.angularHertz = 1.0f; + jointDef.angularDampingRatio = 0.5f; + jointDef.linearHertz = 1.0f; + jointDef.linearDampingRatio = 0.5f; + m_jointIds[index] = b2CreateWeldJoint( m_worldId, &jointDef ); + } + + position.x += 5.0f; + ++index; + + // wheel joint + { + assert( index < e_count ); + + bodyDef.position = position; + m_bodyIds[index] = b2CreateBody( m_worldId, &bodyDef ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2CreatePolygonShape( m_bodyIds[index], &shapeDef, &box ); + + b2Vec2 pivot = { position.x - 1.0f, position.y }; + b2WheelJointDef jointDef = b2DefaultWheelJointDef(); + jointDef.bodyIdA = m_groundId; + jointDef.bodyIdB = m_bodyIds[index]; + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); + jointDef.localAxisA = b2Body_GetLocalVector( jointDef.bodyIdA, { 1.0f, 0.0f } ); + jointDef.hertz = 1.0f; + jointDef.dampingRatio = 0.7f; + jointDef.lowerTranslation = -1.0f; + jointDef.upperTranslation = 1.0f; + jointDef.enableLimit = true; + jointDef.enableMotor = true; + jointDef.maxMotorTorque = 10.0f; + jointDef.motorSpeed = 1.0f; + m_jointIds[index] = b2CreateWheelJoint( m_worldId, &jointDef ); + } + + position.x += 5.0f; + ++index; + } + + void UpdateUI() override + { + float height = 60.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 180.0f, height ) ); + + ImGui::Begin( "Fixed Rotation", nullptr, ImGuiWindowFlags_NoResize ); + + if ( ImGui::Checkbox( "Fixed Rotation", &m_fixedRotation ) ) + { + for ( int i = 0; i < e_count; ++i ) + { + b2Body_SetFixedRotation( m_bodyIds[i], m_fixedRotation ); + } + } + + ImGui::End(); + } + + static Sample* Create( Settings& settings ) + { + return new FixedRotation( settings ); + } + + b2BodyId m_groundId; + b2BodyId m_bodyIds[e_count]; + b2JointId m_jointIds[e_count]; + bool m_fixedRotation; +}; + +static int sampleFixedRotation = RegisterSample( "Joints", "Fixed Rotation", FixedRotation::Create ); + +// This sample shows how to break joints when the internal reaction force becomes large. +class BreakableJoint : public Sample +{ +public: + enum + { + e_count = 6 + }; + + explicit BreakableJoint( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, 8.0f }; + g_camera.m_zoom = 25.0f * 0.7f; + } + + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Segment segment = { { -40.0f, 0.0f }, { 40.0f, 0.0f } }; + b2CreateSegmentShape( groundId, &shapeDef, &segment ); + + for ( int i = 0; i < e_count; ++i ) + { + m_jointIds[i] = b2_nullJointId; + } + + b2Vec2 position = { -12.5f, 10.0f }; + bodyDef.type = b2_dynamicBody; + bodyDef.enableSleep = false; + + b2Polygon box = b2MakeBox( 1.0f, 1.0f ); + + int index = 0; + + // distance joint + { + assert( index < e_count ); + + bodyDef.position = position; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + + float length = 2.0f; + b2Vec2 pivot1 = { position.x, position.y + 1.0f + length }; + b2Vec2 pivot2 = { position.x, position.y + 1.0f }; + b2DistanceJointDef jointDef = b2DefaultDistanceJointDef(); + jointDef.bodyIdA = groundId; + jointDef.bodyIdB = bodyId; + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot1 ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot2 ); + jointDef.length = length; + jointDef.collideConnected = true; + m_jointIds[index] = b2CreateDistanceJoint( m_worldId, &jointDef ); + } + + position.x += 5.0f; + ++index; + + // motor joint + { + assert( index < e_count ); + + bodyDef.position = position; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + + b2MotorJointDef jointDef = b2DefaultMotorJointDef(); + jointDef.bodyIdA = groundId; + jointDef.bodyIdB = bodyId; + jointDef.linearOffset = position; + jointDef.maxForce = 1000.0f; + jointDef.maxTorque = 20.0f; + jointDef.collideConnected = true; + m_jointIds[index] = b2CreateMotorJoint( m_worldId, &jointDef ); + } + + position.x += 5.0f; + ++index; + + // prismatic joint + { + assert( index < e_count ); + + bodyDef.position = position; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + + b2Vec2 pivot = { position.x - 1.0f, position.y }; + b2PrismaticJointDef jointDef = b2DefaultPrismaticJointDef(); + jointDef.bodyIdA = groundId; + jointDef.bodyIdB = bodyId; + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); + jointDef.localAxisA = b2Body_GetLocalVector( jointDef.bodyIdA, { 1.0f, 0.0f } ); + jointDef.collideConnected = true; + m_jointIds[index] = b2CreatePrismaticJoint( m_worldId, &jointDef ); + } + + position.x += 5.0f; + ++index; + + // revolute joint + { + assert( index < e_count ); + + bodyDef.position = position; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + + b2Vec2 pivot = { position.x - 1.0f, position.y }; + b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef(); + jointDef.bodyIdA = groundId; + jointDef.bodyIdB = bodyId; + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); + jointDef.collideConnected = true; + m_jointIds[index] = b2CreateRevoluteJoint( m_worldId, &jointDef ); + } + + position.x += 5.0f; + ++index; + + // weld joint + { + assert( index < e_count ); + + bodyDef.position = position; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + + b2Vec2 pivot = { position.x - 1.0f, position.y }; + b2WeldJointDef jointDef = b2DefaultWeldJointDef(); + jointDef.bodyIdA = groundId; + jointDef.bodyIdB = bodyId; + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); + jointDef.angularHertz = 2.0f; + jointDef.angularDampingRatio = 0.5f; + jointDef.linearHertz = 2.0f; + jointDef.linearDampingRatio = 0.5f; + jointDef.collideConnected = true; + m_jointIds[index] = b2CreateWeldJoint( m_worldId, &jointDef ); + } + + position.x += 5.0f; + ++index; + + // wheel joint + { + assert( index < e_count ); + + bodyDef.position = position; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + + b2Vec2 pivot = { position.x - 1.0f, position.y }; + b2WheelJointDef jointDef = b2DefaultWheelJointDef(); + jointDef.bodyIdA = groundId; + jointDef.bodyIdB = bodyId; + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); + jointDef.localAxisA = b2Body_GetLocalVector( jointDef.bodyIdA, { 1.0f, 0.0f } ); + jointDef.hertz = 1.0f; + jointDef.dampingRatio = 0.7f; + jointDef.lowerTranslation = -1.0f; + jointDef.upperTranslation = 1.0f; + jointDef.enableLimit = true; + jointDef.enableMotor = true; + jointDef.maxMotorTorque = 10.0f; + jointDef.motorSpeed = 1.0f; + jointDef.collideConnected = true; + m_jointIds[index] = b2CreateWheelJoint( m_worldId, &jointDef ); + } + + position.x += 5.0f; + ++index; + + m_breakForce = 1000.0f; + } + + void UpdateUI() override + { + float height = 100.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 240.0f, height ) ); + + ImGui::Begin( "Breakable Joint", nullptr, ImGuiWindowFlags_NoResize ); + + ImGui::SliderFloat( "break force", &m_breakForce, 0.0f, 10000.0f, "%.1f" ); + + b2Vec2 gravity = b2World_GetGravity( m_worldId ); + if ( ImGui::SliderFloat( "gravity", &gravity.y, -50.0f, 50.0f, "%.1f" ) ) + { + b2World_SetGravity( m_worldId, gravity ); + } + + ImGui::End(); + } + + void Step( Settings& settings ) override + { + for ( int i = 0; i < e_count; ++i ) + { + if ( B2_IS_NULL( m_jointIds[i] ) ) + { + continue; + } + + b2Vec2 force = b2Joint_GetConstraintForce( m_jointIds[i] ); + if ( b2LengthSquared( force ) > m_breakForce * m_breakForce ) + { + b2DestroyJoint( m_jointIds[i] ); + m_jointIds[i] = b2_nullJointId; + } + else + { + b2Vec2 point = b2Joint_GetLocalAnchorA( m_jointIds[i] ); + g_draw.DrawString( point, "(%.1f, %.1f)", force.x, force.y ); + } + } + + Sample::Step( settings ); + } + + static Sample* Create( Settings& settings ) + { + return new BreakableJoint( settings ); + } + + b2JointId m_jointIds[e_count]; + float m_breakForce; +}; + +static int sampleBreakableJoint = RegisterSample( "Joints", "Breakable", BreakableJoint::Create ); + +// This shows how you can implement a constraint outside of Box2D +class UserConstraint : public Sample +{ +public: + explicit UserConstraint( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 3.0f, -1.0f }; + g_camera.m_zoom = 25.0f * 0.15f; + } + + b2Polygon box = b2MakeBox( 1.0f, 0.5f ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 20.0f; + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.gravityScale = 1.0f; + bodyDef.angularDamping = 0.5f; + bodyDef.linearDamping = 0.2f; + m_bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2CreatePolygonShape( m_bodyId, &shapeDef, &box ); + + m_impulses[0] = 0.0f; + m_impulses[1] = 0.0f; + } + + void Step( Settings& settings ) override + { + Sample::Step( settings ); + + b2Transform axes = b2Transform_identity; + g_draw.DrawTransform( axes ); + + if ( settings.pause ) + { + return; + } + + float timeStep = settings.hertz > 0.0f ? 1.0f / settings.hertz : 0.0f; + if ( timeStep == 0.0f ) + { + return; + } + + float invTimeStep = settings.hertz; + + static float hertz = 3.0f; + static float zeta = 0.7f; + static float maxForce = 1000.0f; + float omega = 2.0f * b2_pi * hertz; + float sigma = 2.0f * zeta + timeStep * omega; + float s = timeStep * omega * sigma; + float impulseCoefficient = 1.0f / ( 1.0f + s ); + float massCoefficient = s * impulseCoefficient; + float biasCoefficient = omega / sigma; + + b2Vec2 localAnchors[2] = { { 1.0f, -0.5f }, { 1.0f, 0.5f } }; + float mass = b2Body_GetMass( m_bodyId ); + float invMass = mass < 0.0001f ? 0.0f : 1.0f / mass; + float inertiaTensor = b2Body_GetRotationalInertia( m_bodyId ); + float invI = inertiaTensor < 0.0001f ? 0.0f : 1.0f / inertiaTensor; + + b2Vec2 vB = b2Body_GetLinearVelocity( m_bodyId ); + float omegaB = b2Body_GetAngularVelocity( m_bodyId ); + b2Vec2 pB = b2Body_GetWorldCenterOfMass( m_bodyId ); + + for ( int i = 0; i < 2; ++i ) + { + b2Vec2 anchorA = { 3.0f, 0.0f }; + b2Vec2 anchorB = b2Body_GetWorldPoint( m_bodyId, localAnchors[i] ); + + b2Vec2 deltaAnchor = b2Sub( anchorB, anchorA ); + + float slackLength = 1.0f; + float length = b2Length( deltaAnchor ); + float C = length - slackLength; + if ( C < 0.0f || length < 0.001f ) + { + g_draw.DrawSegment( anchorA, anchorB, b2_colorLightCyan ); + m_impulses[i] = 0.0f; + continue; + } + + g_draw.DrawSegment( anchorA, anchorB, b2_colorViolet ); + b2Vec2 axis = b2Normalize( deltaAnchor ); + + b2Vec2 rB = b2Sub( anchorB, pB ); + float Jb = b2Cross( rB, axis ); + float K = invMass + Jb * invI * Jb; + float invK = K < 0.0001f ? 0.0f : 1.0f / K; + + float Cdot = b2Dot( vB, axis ) + Jb * omegaB; + float impulse = -massCoefficient * invK * ( Cdot + biasCoefficient * C ); + float appliedImpulse = b2ClampFloat( impulse, -maxForce * timeStep, 0.0f ); + + vB = b2MulAdd( vB, invMass * appliedImpulse, axis ); + omegaB += appliedImpulse * invI * Jb; + + m_impulses[i] = appliedImpulse; + } + + b2Body_SetLinearVelocity( m_bodyId, vB ); + b2Body_SetAngularVelocity( m_bodyId, omegaB ); + + g_draw.DrawString( 5, m_textLine, "forces = %g, %g", m_impulses[0] * invTimeStep, m_impulses[1] * invTimeStep ); + m_textLine += m_textIncrement; + } + + static Sample* Create( Settings& settings ) + { + return new UserConstraint( settings ); + } + + b2BodyId m_bodyId; + float m_impulses[2]; +}; + +static int sampleUserConstraintIndex = RegisterSample( "Joints", "User Constraint", UserConstraint::Create ); + +// This is a fun demo that shows off the wheel joint +class Driving : public Sample +{ +public: + explicit Driving( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center.y = 5.0f; + g_camera.m_zoom = 25.0f * 0.4f; + settings.drawJoints = false; + } + + b2BodyId groundId; + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2Vec2 points[25]; + int count = 24; + + // fill in reverse to match line list convention + points[count--] = { -20.0f, -20.0f }; + points[count--] = { -20.0f, 0.0f }; + points[count--] = { 20.0f, 0.0f }; + + float hs[10] = { 0.25f, 1.0f, 4.0f, 0.0f, 0.0f, -1.0f, -2.0f, -2.0f, -1.25f, 0.0f }; + float x = 20.0f, y1 = 0.0f, dx = 5.0f; + + for ( int j = 0; j < 2; ++j ) + { + for ( int i = 0; i < 10; ++i ) + { + float y2 = hs[i]; + points[count--] = { x + dx, y2 }; + y1 = y2; + x += dx; + } + } + + // flat before bridge + points[count--] = { x + 40.0f, 0.0f }; + points[count--] = { x + 40.0f, -20.0f }; + + assert( count == -1 ); + + b2ChainDef chainDef = b2DefaultChainDef(); + chainDef.points = points; + chainDef.count = 25; + chainDef.isLoop = true; + b2CreateChain( groundId, &chainDef ); + + // flat after bridge + x += 80.0f; + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Segment segment = { { x, 0.0f }, { x + 40.0f, 0.0f } }; + b2CreateSegmentShape( groundId, &shapeDef, &segment ); + + // jump ramp + x += 40.0f; + segment = { { x, 0.0f }, { x + 10.0f, 5.0f } }; + b2CreateSegmentShape( groundId, &shapeDef, &segment ); + + // final corner + x += 20.0f; + segment = { { x, 0.0f }, { x + 40.0f, 0.0f } }; + b2CreateSegmentShape( groundId, &shapeDef, &segment ); + + x += 40.0f; + segment = { { x, 0.0f }, { x, 20.0f } }; + b2CreateSegmentShape( groundId, &shapeDef, &segment ); + } + + // Teeter + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.position = { 140.0f, 1.0f }; + bodyDef.angularVelocity = 1.0f; + bodyDef.type = b2_dynamicBody; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Polygon box = b2MakeBox( 10.0f, 0.25f ); + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + + b2Vec2 pivot = bodyDef.position; + b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef(); + jointDef.bodyIdA = groundId; + jointDef.bodyIdB = bodyId; + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); + jointDef.lowerAngle = -8.0f * b2_pi / 180.0f; + jointDef.upperAngle = 8.0f * b2_pi / 180.0f; + jointDef.enableLimit = true; + b2CreateRevoluteJoint( m_worldId, &jointDef ); + } + + // Bridge + { + int N = 20; + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Capsule capsule = { { -1.0f, 0.0f }, { 1.0f, 0.0f }, 0.125f }; + + b2RevoluteJointDef jointDef = b2DefaultRevoluteJointDef(); + + b2BodyId prevBodyId = groundId; + for ( int i = 0; i < N; ++i ) + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = { 161.0f + 2.0f * i, -0.125f }; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2CreateCapsuleShape( bodyId, &shapeDef, &capsule ); + + b2Vec2 pivot = { 160.0f + 2.0f * i, -0.125f }; + jointDef.bodyIdA = prevBodyId; + jointDef.bodyIdB = bodyId; + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); + b2CreateRevoluteJoint( m_worldId, &jointDef ); + + prevBodyId = bodyId; + } + + b2Vec2 pivot = { 160.0f + 2.0f * N, -0.125f }; + jointDef.bodyIdA = prevBodyId; + jointDef.bodyIdB = groundId; + jointDef.localAnchorA = b2Body_GetLocalPoint( jointDef.bodyIdA, pivot ); + jointDef.localAnchorB = b2Body_GetLocalPoint( jointDef.bodyIdB, pivot ); + jointDef.enableMotor = true; + jointDef.maxMotorTorque = 50.0f; + b2CreateRevoluteJoint( m_worldId, &jointDef ); + } + + // Boxes + { + b2Polygon box = b2MakeBox( 0.5f, 0.5f ); + + b2BodyId bodyId; + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.friction = 0.25f; + shapeDef.restitution = 0.25f; + shapeDef.density = 0.25f; + + bodyDef.position = { 230.0f, 0.5f }; + bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + + bodyDef.position = { 230.0f, 1.5f }; + bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + + bodyDef.position = { 230.0f, 2.5f }; + bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + + bodyDef.position = { 230.0f, 3.5f }; + bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + + bodyDef.position = { 230.0f, 4.5f }; + bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + } + + // Car + + m_throttle = 0.0f; + m_speed = 35.0f; + m_torque = 2.5f; + m_hertz = 5.0f; + m_dampingRatio = 0.7f; + + m_car.Spawn( m_worldId, { 0.0f, 0.0f }, 1.0f, m_hertz, m_dampingRatio, m_torque, NULL ); + } + + void UpdateUI() override + { + float height = 140.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 200.0f, height ) ); + + ImGui::Begin( "Driving", nullptr, ImGuiWindowFlags_NoResize ); + + ImGui::PushItemWidth( 100.0f ); + if ( ImGui::SliderFloat( "Spring Hertz", &m_hertz, 0.0f, 20.0f, "%.0f" ) ) + { + m_car.SetHertz( m_hertz ); + } + + if ( ImGui::SliderFloat( "Damping Ratio", &m_dampingRatio, 0.0f, 10.0f, "%.1f" ) ) + { + m_car.SetDampingRadio( m_dampingRatio ); + } + + if ( ImGui::SliderFloat( "Speed", &m_speed, 0.0f, 50.0f, "%.0f" ) ) + { + m_car.SetSpeed( m_throttle * m_speed ); + } + + if ( ImGui::SliderFloat( "Torque", &m_torque, 0.0f, 5.0f, "%.1f" ) ) + { + m_car.SetTorque( m_torque ); + } + ImGui::PopItemWidth(); + + ImGui::End(); + } + + void Step( Settings& settings ) override + { + if ( glfwGetKey( g_mainWindow, GLFW_KEY_A ) == GLFW_PRESS ) + { + m_throttle = 1.0f; + m_car.SetSpeed( m_speed ); + } + + if ( glfwGetKey( g_mainWindow, GLFW_KEY_S ) == GLFW_PRESS ) + { + m_throttle = 0.0f; + m_car.SetSpeed( 0.0f ); + } + + if ( glfwGetKey( g_mainWindow, GLFW_KEY_D ) == GLFW_PRESS ) + { + m_throttle = -1.0f; + m_car.SetSpeed( -m_speed ); + } + + g_draw.DrawString( 5, m_textLine, "Keys: left = a, brake = s, right = d" ); + m_textLine += m_textIncrement; + + b2Vec2 linearVelocity = b2Body_GetLinearVelocity( m_car.m_chassisId ); + float kph = linearVelocity.x * 3.6f; + g_draw.DrawString( 5, m_textLine, "speed in kph: %.2g", kph ); + m_textLine += m_textIncrement; + + b2Vec2 carPosition = b2Body_GetPosition( m_car.m_chassisId ); + g_camera.m_center.x = carPosition.x; + + Sample::Step( settings ); + } + + static Sample* Create( Settings& settings ) + { + return new Driving( settings ); + } + + Car m_car; + + float m_throttle; + float m_hertz; + float m_dampingRatio; + float m_torque; + float m_speed; +}; + +static int sampleDriving = RegisterSample( "Joints", "Driving", Driving::Create ); + +class Ragdoll : public Sample +{ +public: + explicit Ragdoll( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, 3.0f }; + g_camera.m_zoom = 25.0f * 0.15f; + } + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Segment segment = { { -20.0f, 0.0f }, { 20.0f, 0.0f } }; + b2CreateSegmentShape( groundId, &shapeDef, &segment ); + } + + m_jointFrictionTorque = 0.05f; + m_jointHertz = 0.0f; + m_jointDampingRatio = 0.5f; + + m_human.Spawn( m_worldId, { 0.0f, 5.0f }, 1.0f, m_jointFrictionTorque, m_jointHertz, m_jointDampingRatio, 1, nullptr, + true ); + m_human.ApplyRandomAngularImpulse( 10.0f ); + } + + void UpdateUI() override + { + float height = 140.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 180.0f, height ) ); + + ImGui::Begin( "Ragdoll", nullptr, ImGuiWindowFlags_NoResize ); + ImGui::PushItemWidth( 100.0f ); + + if ( ImGui::SliderFloat( "Friction", &m_jointFrictionTorque, 0.0f, 1.0f, "%3.2f" ) ) + { + m_human.SetJointFrictionTorque( m_jointFrictionTorque ); + } + + if ( ImGui::SliderFloat( "Hertz", &m_jointHertz, 0.0f, 10.0f, "%3.1f" ) ) + { + m_human.SetJointSpringHertz( m_jointHertz ); + } + + if ( ImGui::SliderFloat( "Damping", &m_jointDampingRatio, 0.0f, 4.0f, "%3.1f" ) ) + { + m_human.SetJointDampingRatio( m_jointDampingRatio ); + } + + ImGui::PopItemWidth(); + ImGui::End(); + } + + static Sample* Create( Settings& settings ) + { + return new Ragdoll( settings ); + } + + Human m_human; + float m_jointFrictionTorque; + float m_jointHertz; + float m_jointDampingRatio; +}; + +static int sampleRagdoll = RegisterSample( "Joints", "Ragdoll", Ragdoll::Create ); + +class SoftBody : public Sample +{ +public: + explicit SoftBody( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, 5.0f }; + g_camera.m_zoom = 25.0f * 0.25f; + } + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Segment segment = { { -20.0f, 0.0f }, { 20.0f, 0.0f } }; + b2CreateSegmentShape( groundId, &shapeDef, &segment ); + } + + m_donut.Spawn( m_worldId, { 0.0f, 10.0f }, 2.0f, 0, nullptr ); + } + + static Sample* Create( Settings& settings ) + { + return new SoftBody( settings ); + } + + Donut m_donut; +}; + +static int sampleDonut = RegisterSample( "Joints", "Soft Body", SoftBody::Create ); + +class DoohickeyFarm : public Sample +{ +public: + explicit DoohickeyFarm( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, 5.0f }; + g_camera.m_zoom = 25.0f * 0.35f; + } + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Segment segment = { { -20.0f, 0.0f }, { 20.0f, 0.0f } }; + b2CreateSegmentShape( groundId, &shapeDef, &segment ); + + b2Polygon box = b2MakeOffsetBox( 1.0f, 1.0f, { 0.0f, 1.0f }, b2Rot_identity ); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + } + + float y = 4.0f; + for ( int i = 0; i < 4; ++i ) + { + Doohickey doohickey; + doohickey.Spawn( m_worldId, { 0.0f, y }, 0.5f ); + y += 2.0f; + } + } + + void Step( Settings& settings ) override + { + Sample::Step( settings ); + } + + static Sample* Create( Settings& settings ) + { + return new DoohickeyFarm( settings ); + } +}; + +static int sampleDoohickey = RegisterSample( "Joints", "Doohickey", DoohickeyFarm::Create ); + +class ScissorLift : public Sample +{ +public: + explicit ScissorLift( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, 9.0f }; + g_camera.m_zoom = 25.0f * 0.4f; + } + + // Need 8 sub-steps for smoother operation + settings.subStepCount = 8; + + b2BodyId groundId; + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Segment segment = { { -20.0f, 0.0f }, { 20.0f, 0.0f } }; + b2CreateSegmentShape( groundId, &shapeDef, &segment ); + } + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.sleepThreshold = 0.01f; + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Capsule capsule = { { -2.5f, 0.0f }, { 2.5f, 0.0f }, 0.15f }; + + b2BodyId baseId1 = groundId; + b2BodyId baseId2 = groundId; + b2Vec2 baseAnchor1 = { -2.5f, 0.2f }; + b2Vec2 baseAnchor2 = { 2.5f, 0.2f }; + float y = 0.5f; + + b2BodyId linkId1; + int N = 3; + + for ( int i = 0; i < N; ++i ) + { + bodyDef.position = { 0.0f, y }; + bodyDef.rotation = b2MakeRot( 0.15f ); + b2BodyId bodyId1 = b2CreateBody( m_worldId, &bodyDef ); + b2CreateCapsuleShape( bodyId1, &shapeDef, &capsule ); + + bodyDef.position = { 0.0f, y }; + bodyDef.rotation = b2MakeRot( -0.15f ); + + b2BodyId bodyId2 = b2CreateBody( m_worldId, &bodyDef ); + b2CreateCapsuleShape( bodyId2, &shapeDef, &capsule ); + + if ( i == 1 ) + { + linkId1 = bodyId2; + } + + b2RevoluteJointDef revoluteDef = b2DefaultRevoluteJointDef(); + + // left pin + revoluteDef.bodyIdA = baseId1; + revoluteDef.bodyIdB = bodyId1; + revoluteDef.localAnchorA = baseAnchor1; + revoluteDef.localAnchorB = { -2.5f, 0.0f }; + revoluteDef.enableMotor = false; + revoluteDef.maxMotorTorque = 1.0f; + revoluteDef.collideConnected = ( i == 0 ) ? true : false; + + b2CreateRevoluteJoint( m_worldId, &revoluteDef ); + + // right pin + if ( i == 0 ) + { + b2WheelJointDef wheelDef = b2DefaultWheelJointDef(); + wheelDef.bodyIdA = baseId2; + wheelDef.bodyIdB = bodyId2; + wheelDef.localAxisA = { 1.0f, 0.0f }; + wheelDef.localAnchorA = baseAnchor2; + wheelDef.localAnchorB = { 2.5f, 0.0f }; + wheelDef.enableSpring = false; + wheelDef.collideConnected = true; + + b2CreateWheelJoint( m_worldId, &wheelDef ); + } + else + { + revoluteDef.bodyIdA = baseId2; + revoluteDef.bodyIdB = bodyId2; + revoluteDef.localAnchorA = baseAnchor2; + revoluteDef.localAnchorB = { 2.5f, 0.0f }; + revoluteDef.enableMotor = false; + revoluteDef.maxMotorTorque = 1.0f; + revoluteDef.collideConnected = false; + + b2CreateRevoluteJoint( m_worldId, &revoluteDef ); + } + + // middle pin + revoluteDef.bodyIdA = bodyId1; + revoluteDef.bodyIdB = bodyId2; + revoluteDef.localAnchorA = { 0.0f, 0.0f }; + revoluteDef.localAnchorB = { 0.0f, 0.0f }; + revoluteDef.enableMotor = false; + revoluteDef.maxMotorTorque = 1.0f; + revoluteDef.collideConnected = false; + + b2CreateRevoluteJoint( m_worldId, &revoluteDef ); + + baseId1 = bodyId2; + baseId2 = bodyId1; + baseAnchor1 = { -2.5f, 0.0f }; + baseAnchor2 = { 2.5f, 0.0f }; + y += 1.0f; + } + + bodyDef.position = { 0.0f, y }; + bodyDef.rotation = b2Rot_identity; + b2BodyId platformId = b2CreateBody( m_worldId, &bodyDef ); + + b2Polygon box = b2MakeBox( 3.0f, 0.2f ); + b2CreatePolygonShape( platformId, &shapeDef, &box ); + + // left pin + b2RevoluteJointDef revoluteDef = b2DefaultRevoluteJointDef(); + revoluteDef.bodyIdA = platformId; + revoluteDef.bodyIdB = baseId1; + revoluteDef.localAnchorA = { -2.5f, -0.4f }; + revoluteDef.localAnchorB = baseAnchor1; + revoluteDef.enableMotor = false; + revoluteDef.maxMotorTorque = 1.0f; + revoluteDef.collideConnected = true; + b2CreateRevoluteJoint( m_worldId, &revoluteDef ); + + // right pin + b2WheelJointDef wheelDef = b2DefaultWheelJointDef(); + wheelDef.bodyIdA = platformId; + wheelDef.bodyIdB = baseId2; + wheelDef.localAxisA = { 1.0f, 0.0f }; + wheelDef.localAnchorA = { 2.5f, -0.4f }; + wheelDef.localAnchorB = baseAnchor2; + wheelDef.enableSpring = false; + wheelDef.collideConnected = true; + b2CreateWheelJoint( m_worldId, &wheelDef ); + + m_enableMotor = false; + m_motorSpeed = 0.25f; + m_motorForce = 2000.0f; + + b2DistanceJointDef distanceDef = b2DefaultDistanceJointDef(); + distanceDef.bodyIdA = groundId; + distanceDef.bodyIdB = linkId1; + distanceDef.localAnchorA = { -2.5f, 0.2f }; + distanceDef.localAnchorB = { 0.5f, 0.0f }; + distanceDef.enableSpring = true; + distanceDef.minLength = 0.2f; + distanceDef.maxLength = 5.5f; + distanceDef.enableLimit = true; + distanceDef.enableMotor = m_enableMotor; + distanceDef.motorSpeed = m_motorSpeed; + distanceDef.maxMotorForce = m_motorForce; + m_liftJointId = b2CreateDistanceJoint( m_worldId, &distanceDef ); + + Car car; + car.Spawn( m_worldId, { 0.0f, y + 2.0f }, 1.0f, 3.0f, 0.7f, 0.0f, NULL ); + } + + void UpdateUI() override + { + float height = 140.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 240.0f, height ) ); + + ImGui::Begin( "Scissor Lift", nullptr, ImGuiWindowFlags_NoResize ); + + if ( ImGui::Checkbox( "Motor", &m_enableMotor ) ) + { + b2DistanceJoint_EnableMotor( m_liftJointId, m_enableMotor ); + b2Joint_WakeBodies( m_liftJointId ); + } + + if ( ImGui::SliderFloat( "Max Force", &m_motorForce, 0.0f, 3000.0f, "%.0f" ) ) + { + b2DistanceJoint_SetMaxMotorForce( m_liftJointId, m_motorForce ); + b2Joint_WakeBodies( m_liftJointId ); + } + + if ( ImGui::SliderFloat( "Speed", &m_motorSpeed, -0.3f, 0.3f, "%.2f" ) ) + { + b2DistanceJoint_SetMotorSpeed( m_liftJointId, m_motorSpeed ); + b2Joint_WakeBodies( m_liftJointId ); + } + + ImGui::End(); + } + + void Step( Settings& settings ) override + { + Sample::Step( settings ); + } + + static Sample* Create( Settings& settings ) + { + return new ScissorLift( settings ); + } + + b2JointId m_liftJointId; + float m_motorForce; + float m_motorSpeed; + bool m_enableMotor; +}; + +static int sampleScissorLift = RegisterSample( "Joints", "Scissor Lift", ScissorLift::Create ); diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/sample_robustness.cpp b/tests/cpp-tests/Source/Box2DTestBed/samples/sample_robustness.cpp new file mode 100644 index 000000000000..5d997f2195b3 --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/sample_robustness.cpp @@ -0,0 +1,261 @@ +// SPDX-FileCopyrightText: 2022 Erin Catto +// SPDX-License-Identifier: MIT + +#include "draw.h" +#include "sample.h" +#include "settings.h" + +#include "box2d/box2d.h" + +#include +#include + +// Pyramid with heavy box on top +class HighMassRatio1 : public Sample +{ +public: + explicit HighMassRatio1( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 3.0f, 14.0f }; + g_camera.m_zoom = 25.0f; + } + + float extent = 1.0f; + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Polygon box = b2MakeOffsetBox( 50.0f, 1.0f, { 0.0f, -1.0f }, b2Rot_identity ); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + } + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + b2Polygon box = b2MakeBox( extent, extent ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + + for ( int j = 0; j < 3; ++j ) + { + int count = 10; + float offset = -20.0f * extent + 2.0f * ( count + 1.0f ) * extent * j; + float y = extent; + while ( count > 0 ) + { + for ( int i = 0; i < count; ++i ) + { + float coeff = i - 0.5f * count; + + float yy = count == 1 ? y + 2.0f : y; + bodyDef.position = { 2.0f * coeff * extent + offset, yy }; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + + shapeDef.density = count == 1 ? ( j + 1.0f ) * 100.0f : 1.0f; + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + } + + --count; + y += 2.0f * extent; + } + } + } + } + + static Sample* Create( Settings& settings ) + { + return new HighMassRatio1( settings ); + } +}; + +static int sampleIndex1 = RegisterSample( "Robustness", "HighMassRatio1", HighMassRatio1::Create ); + +// Big box on small boxes +class HighMassRatio2 : public Sample +{ +public: + explicit HighMassRatio2( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, 16.5f }; + g_camera.m_zoom = 25.0f; + } + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Polygon box = b2MakeOffsetBox( 50.0f, 1.0f, { 0.0f, -1.0f }, b2Rot_identity ); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + } + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + b2ShapeDef shapeDef = b2DefaultShapeDef(); + + float extent = 1.0f; + b2Vec2 points[3] = { { -0.5f * extent, 0.0f }, { 0.5f * extent, 0.0f }, { 0.0f, 1.0f * extent } }; + b2Hull hull = b2ComputeHull( points, 3 ); + b2Polygon smallTriangle = b2MakePolygon( &hull, 0.0f ); + b2Polygon smallBox = b2MakeBox( 0.5f * extent, 0.5f * extent ); + b2Polygon bigBox = b2MakeBox( 10.0f * extent, 10.0f * extent ); + + { + bodyDef.position = { -9.0f * extent, 0.5f * extent }; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2CreatePolygonShape( bodyId, &shapeDef, &smallBox ); + } + + { + bodyDef.position = { 9.0f * extent, 0.5f * extent }; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2CreatePolygonShape( bodyId, &shapeDef, &smallBox ); + } + + { + bodyDef.position = { 0.0f, ( 10.0f + 16.0f ) * extent }; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2CreatePolygonShape( bodyId, &shapeDef, &bigBox ); + } + } + } + + static Sample* Create( Settings& settings ) + { + return new HighMassRatio2( settings ); + } +}; + +static int sampleIndex2 = RegisterSample( "Robustness", "HighMassRatio2", HighMassRatio2::Create ); + +class OverlapRecovery : public Sample +{ +public: + explicit OverlapRecovery( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, 2.5f }; + g_camera.m_zoom = 25.0f * 0.15f; + } + + m_bodyIds = nullptr; + m_bodyCount = 0; + m_baseCount = 4; + m_overlap = 0.25f; + m_extent = 0.5f; + m_pushout = 3.0f; + m_hertz = 30.0f; + m_dampingRatio = 10.0f; + + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + float groundWidth = 40.0f; + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 1.0f; + + b2Segment segment = { { -groundWidth, 0.0f }, { groundWidth, 0.0f } }; + b2CreateSegmentShape( groundId, &shapeDef, &segment ); + + CreateScene(); + } + + ~OverlapRecovery() override + { + free( m_bodyIds ); + } + + void CreateScene() + { + for ( int32_t i = 0; i < m_bodyCount; ++i ) + { + b2DestroyBody( m_bodyIds[i] ); + } + + b2World_SetContactTuning( m_worldId, m_hertz, m_dampingRatio, m_pushout ); + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + + b2Polygon box = b2MakeBox( m_extent, m_extent ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 1.0f; + + m_bodyCount = m_baseCount * ( m_baseCount + 1 ) / 2; + m_bodyIds = (b2BodyId*)realloc( m_bodyIds, m_bodyCount * sizeof( b2BodyId ) ); + + int32_t bodyIndex = 0; + float fraction = 1.0f - m_overlap; + float y = m_extent; + for ( int32_t i = 0; i < m_baseCount; ++i ) + { + float x = fraction * m_extent * ( i - m_baseCount ); + for ( int32_t j = i; j < m_baseCount; ++j ) + { + bodyDef.position = { x, y }; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + + m_bodyIds[bodyIndex++] = bodyId; + + x += 2.0f * fraction * m_extent; + } + + y += 2.0f * fraction * m_extent; + } + + assert( bodyIndex == m_bodyCount ); + } + + void UpdateUI() override + { + float height = 210.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 220.0f, height ) ); + + ImGui::Begin( "Overlap Recovery", nullptr, ImGuiWindowFlags_NoResize ); + ImGui::PushItemWidth( 100.0f ); + + bool changed = false; + changed = changed || ImGui::SliderFloat( "Extent", &m_extent, 0.1f, 1.0f, "%.1f" ); + changed = changed || ImGui::SliderInt( "Base Count", &m_baseCount, 1, 10 ); + changed = changed || ImGui::SliderFloat( "Overlap", &m_overlap, 0.0f, 1.0f, "%.2f" ); + changed = changed || ImGui::SliderFloat( "Pushout", &m_pushout, 0.0f, 10.0f, "%.1f" ); + changed = changed || ImGui::SliderFloat( "Hertz", &m_hertz, 0.0f, 120.0f, "%.f" ); + changed = changed || ImGui::SliderFloat( "Damping Ratio", &m_dampingRatio, 0.0f, 20.0f, "%.1f" ); + changed = changed || ImGui::Button( "Reset Scene" ); + + if ( changed ) + { + CreateScene(); + } + + ImGui::PopItemWidth(); + ImGui::End(); + } + + static Sample* Create( Settings& settings ) + { + return new OverlapRecovery( settings ); + } + + b2BodyId* m_bodyIds; + int32_t m_bodyCount; + int32_t m_baseCount; + float m_overlap; + float m_extent; + float m_pushout; + float m_hertz; + float m_dampingRatio; +}; + +static int sampleIndex4 = RegisterSample( "Robustness", "Overlap Recovery", OverlapRecovery::Create ); diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/sample_shapes.cpp b/tests/cpp-tests/Source/Box2DTestBed/samples/sample_shapes.cpp new file mode 100644 index 000000000000..91df17c43f88 --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/sample_shapes.cpp @@ -0,0 +1,1304 @@ +// SPDX-FileCopyrightText: 2022 Erin Catto +// SPDX-License-Identifier: MIT + +#include "draw.h" +#include "sample.h" +#include "settings.h" + +#include "box2d/box2d.h" +#include "box2d/math_functions.h" + +#include +#include + +class ChainShape : public Sample +{ +public: + enum ShapeType + { + e_circleShape = 0, + e_capsuleShape, + e_boxShape + }; + + explicit ChainShape( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, 0.0f }; + g_camera.m_zoom = 25.0f * 1.75f; + } + + m_groundId = b2_nullBodyId; + m_bodyId = b2_nullBodyId; + m_chainId = b2_nullChainId; + m_shapeId = b2_nullShapeId; + m_shapeType = e_circleShape; + m_restitution = 0.0f; + m_friction = 0.2f; + CreateScene(); + Launch(); + } + + void CreateScene() + { + if ( B2_IS_NON_NULL( m_groundId ) ) + { + b2DestroyBody( m_groundId ); + } + + // https://betravis.github.io/shape-tools/path-to-polygon/ + // b2Vec2 points[] = {{-20.58325, 14.54175}, {-21.90625, 15.8645}, {-24.552, 17.1875}, + // {-27.198, 11.89575}, {-29.84375, 15.8645}, {-29.84375, 21.15625}, + // {-25.875, 23.802}, {-20.58325, 25.125}, {-25.875, 29.09375}, + // {-20.58325, 31.7395}, {-11.0089998, 23.2290001}, {-8.67700005, 21.15625}, + // {-6.03125, 21.15625}, {-7.35424995, 29.09375}, {-3.38549995, 29.09375}, + // {1.90625, 30.41675}, {5.875, 17.1875}, {11.16675, 25.125}, + // {9.84375, 29.09375}, {13.8125, 31.7395}, {21.75, 30.41675}, + // {28.3644981, 26.448}, {25.71875, 18.5105}, {24.3957481, 13.21875}, + // {17.78125, 11.89575}, {15.1355, 7.92700005}, {5.875, 9.25}, + // {1.90625, 11.89575}, {-3.25, 11.89575}, {-3.25, 9.9375}, + // {-4.70825005, 9.25}, {-8.67700005, 9.25}, {-11.323, 11.89575}, + // {-13.96875, 11.89575}, {-15.29175, 14.54175}, {-19.2605, 14.54175}}; + + b2Vec2 points[] = { + { -56.885498, 12.8985004 }, { -56.885498, 16.2057495 }, { 56.885498, 16.2057495 }, { 56.885498, -16.2057514 }, + { 51.5935059, -16.2057514 }, { 43.6559982, -10.9139996 }, { 35.7184982, -10.9139996 }, { 27.7809982, -10.9139996 }, + { 21.1664963, -14.2212505 }, { 11.9059982, -16.2057514 }, { 0, -16.2057514 }, { -10.5835037, -14.8827496 }, + { -17.1980019, -13.5597477 }, { -21.1665001, -12.2370014 }, { -25.1355019, -9.5909977 }, { -31.75, -3.63799858 }, + { -38.3644981, 6.2840004 }, { -42.3334999, 9.59125137 }, { -47.625, 11.5755005 }, { -56.885498, 12.8985004 }, + }; + + int count = sizeof( points ) / sizeof( points[0] ); + + // float scale = 0.25f; + // b2Vec2 lower = {FLT_MAX, FLT_MAX}; + // b2Vec2 upper = {-FLT_MAX, -FLT_MAX}; + // for (int i = 0; i < count; ++i) + //{ + // points[i].x = 2.0f * scale * points[i].x; + // points[i].y = -scale * points[i].y; + + // lower = b2Min(lower, points[i]); + // upper = b2Max(upper, points[i]); + //} + + // b2Vec2 center = b2MulSV(0.5f, b2Add(lower, upper)); + // for (int i = 0; i < count; ++i) + //{ + // points[i] = b2Sub(points[i], center); + // } + + // for (int i = 0; i < count / 2; ++i) + //{ + // b2Vec2 temp = points[i]; + // points[i] = points[count - 1 - i]; + // points[count - 1 - i] = temp; + // } + + // printf("{"); + // for (int i = 0; i < count; ++i) + //{ + // printf("{%.9g, %.9g},", points[i].x, points[i].y); + // } + // printf("};\n"); + + b2ChainDef chainDef = b2DefaultChainDef(); + chainDef.points = points; + chainDef.count = count; + chainDef.isLoop = true; + chainDef.friction = 0.2f; + + b2BodyDef bodyDef = b2DefaultBodyDef(); + m_groundId = b2CreateBody( m_worldId, &bodyDef ); + + m_chainId = b2CreateChain( m_groundId, &chainDef ); + } + + void Launch() + { + if ( B2_IS_NON_NULL( m_bodyId ) ) + { + b2DestroyBody( m_bodyId ); + } + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = { -55.0f, 13.5f }; + m_bodyId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 1.0f; + shapeDef.friction = m_friction; + shapeDef.restitution = m_restitution; + + if ( m_shapeType == e_circleShape ) + { + b2Circle circle = { { 0.0f, 0.0f }, 0.5f }; + m_shapeId = b2CreateCircleShape( m_bodyId, &shapeDef, &circle ); + } + else if ( m_shapeType == e_capsuleShape ) + { + b2Capsule capsule = { { -0.5f, 0.0f }, { 0.5f, 0.0 }, 0.25f }; + m_shapeId = b2CreateCapsuleShape( m_bodyId, &shapeDef, &capsule ); + } + else + { + float h = 0.5f; + b2Polygon box = b2MakeBox( h, h ); + m_shapeId = b2CreatePolygonShape( m_bodyId, &shapeDef, &box ); + } + } + + void UpdateUI() override + { + float height = 135.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 240.0f, height ) ); + + ImGui::Begin( "Chain Shape", nullptr, ImGuiWindowFlags_NoResize ); + + const char* shapeTypes[] = { "Circle", "Capsule", "Box" }; + int shapeType = int( m_shapeType ); + if ( ImGui::Combo( "Shape", &shapeType, shapeTypes, IM_ARRAYSIZE( shapeTypes ) ) ) + { + m_shapeType = ShapeType( shapeType ); + Launch(); + } + + if ( ImGui::SliderFloat( "Friction", &m_friction, 0.0f, 1.0f, "%.2f" ) ) + { + b2Shape_SetFriction( m_shapeId, m_friction ); + b2Chain_SetFriction( m_chainId, m_friction ); + } + + if ( ImGui::SliderFloat( "Restitution", &m_restitution, 0.0f, 2.0f, "%.1f" ) ) + { + b2Shape_SetRestitution( m_shapeId, m_restitution ); + } + + if ( ImGui::Button( "Launch" ) ) + { + Launch(); + } + + ImGui::End(); + } + + void Step( Settings& settings ) override + { + Sample::Step( settings ); + + g_draw.DrawSegment( b2Vec2_zero, { 0.5f, 0.0f }, b2_colorRed ); + g_draw.DrawSegment( b2Vec2_zero, { 0.0f, 0.5f }, b2_colorGreen ); + } + + static Sample* Create( Settings& settings ) + { + return new ChainShape( settings ); + } + + b2BodyId m_groundId; + b2BodyId m_bodyId; + b2ChainId m_chainId; + ShapeType m_shapeType; + b2ShapeId m_shapeId; + float m_restitution; + float m_friction; +}; + +static int sampleChainShape = RegisterSample( "Shapes", "Chain Shape", ChainShape::Create ); + +// This sample shows how careful creation of compound shapes leads to better simulation and avoids +// objects getting stuck. +// This also shows how to get the combined AABB for the body. +class CompoundShapes : public Sample +{ +public: + explicit CompoundShapes( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, 6.0f }; + g_camera.m_zoom = 25.0f * 0.5f; + } + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Segment segment = { { 50.0f, 0.0f }, { -50.0f, 0.0f } }; + b2CreateSegmentShape( groundId, &shapeDef, &segment ); + } + + // Table 1 + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = { -15.0f, 1.0f }; + m_table1Id = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Polygon top = b2MakeOffsetBox( 3.0f, 0.5f, { 0.0f, 3.5f }, b2Rot_identity ); + b2Polygon leftLeg = b2MakeOffsetBox( 0.5f, 1.5f, { -2.5f, 1.5f }, b2Rot_identity ); + b2Polygon rightLeg = b2MakeOffsetBox( 0.5f, 1.5f, { 2.5f, 1.5f }, b2Rot_identity ); + + b2CreatePolygonShape( m_table1Id, &shapeDef, &top ); + b2CreatePolygonShape( m_table1Id, &shapeDef, &leftLeg ); + b2CreatePolygonShape( m_table1Id, &shapeDef, &rightLeg ); + } + + // Table 2 + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = { -5.0f, 1.0f }; + m_table2Id = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Polygon top = b2MakeOffsetBox( 3.0f, 0.5f, { 0.0f, 3.5f }, b2Rot_identity ); + b2Polygon leftLeg = b2MakeOffsetBox( 0.5f, 2.0f, { -2.5f, 2.0f }, b2Rot_identity ); + b2Polygon rightLeg = b2MakeOffsetBox( 0.5f, 2.0f, { 2.5f, 2.0f }, b2Rot_identity ); + + b2CreatePolygonShape( m_table2Id, &shapeDef, &top ); + b2CreatePolygonShape( m_table2Id, &shapeDef, &leftLeg ); + b2CreatePolygonShape( m_table2Id, &shapeDef, &rightLeg ); + } + + // Spaceship 1 + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = { 5.0f, 1.0f }; + m_ship1Id = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Vec2 vertices[3]; + + vertices[0] = { -2.0f, 0.0f }; + vertices[1] = { 0.0f, 4.0f / 3.0f }; + vertices[2] = { 0.0f, 4.0f }; + b2Hull hull = b2ComputeHull( vertices, 3 ); + b2Polygon left = b2MakePolygon( &hull, 0.0f ); + + vertices[0] = { 2.0f, 0.0f }; + vertices[1] = { 0.0f, 4.0f / 3.0f }; + vertices[2] = { 0.0f, 4.0f }; + hull = b2ComputeHull( vertices, 3 ); + b2Polygon right = b2MakePolygon( &hull, 0.0f ); + + b2CreatePolygonShape( m_ship1Id, &shapeDef, &left ); + b2CreatePolygonShape( m_ship1Id, &shapeDef, &right ); + } + + // Spaceship 2 + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = { 15.0f, 1.0f }; + m_ship2Id = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Vec2 vertices[3]; + + vertices[0] = { -2.0f, 0.0f }; + vertices[1] = { 1.0f, 2.0f }; + vertices[2] = { 0.0f, 4.0f }; + b2Hull hull = b2ComputeHull( vertices, 3 ); + b2Polygon left = b2MakePolygon( &hull, 0.0f ); + + vertices[0] = { 2.0f, 0.0f }; + vertices[1] = { -1.0f, 2.0f }; + vertices[2] = { 0.0f, 4.0f }; + hull = b2ComputeHull( vertices, 3 ); + b2Polygon right = b2MakePolygon( &hull, 0.0f ); + + b2CreatePolygonShape( m_ship2Id, &shapeDef, &left ); + b2CreatePolygonShape( m_ship2Id, &shapeDef, &right ); + } + + m_drawBodyAABBs = false; + } + + void Spawn() + { + // Table 1 obstruction + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = b2Body_GetPosition( m_table1Id ); + bodyDef.rotation = b2Body_GetRotation( m_table1Id ); + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Polygon box = b2MakeOffsetBox( 4.0f, 0.1f, { 0.0f, 3.0f }, b2Rot_identity ); + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + } + + // Table 2 obstruction + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = b2Body_GetPosition( m_table2Id ); + bodyDef.rotation = b2Body_GetRotation( m_table2Id ); + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Polygon box = b2MakeOffsetBox( 4.0f, 0.1f, { 0.0f, 3.0f }, b2Rot_identity ); + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + } + + // Ship 1 obstruction + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = b2Body_GetPosition( m_ship1Id ); + bodyDef.rotation = b2Body_GetRotation( m_ship1Id ); + // bodyDef.gravityScale = 0.0f; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Circle circle = { { 0.0f, 2.0f }, 0.5f }; + b2CreateCircleShape( bodyId, &shapeDef, &circle ); + } + + // Ship 2 obstruction + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = b2Body_GetPosition( m_ship2Id ); + bodyDef.rotation = b2Body_GetRotation( m_ship2Id ); + // bodyDef.gravityScale = 0.0f; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Circle circle = { { 0.0f, 2.0f }, 0.5f }; + b2CreateCircleShape( bodyId, &shapeDef, &circle ); + } + } + + void UpdateUI() override + { + float height = 100.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 180.0f, height ) ); + + ImGui::Begin( "Compound Shapes", nullptr, ImGuiWindowFlags_NoResize ); + + if ( ImGui::Button( "Intrude" ) ) + { + Spawn(); + } + + ImGui::Checkbox( "Body AABBs", &m_drawBodyAABBs ); + + ImGui::End(); + } + + void Step( Settings& settings ) override + { + Sample::Step( settings ); + + if ( m_drawBodyAABBs ) + { + b2AABB aabb = b2Body_ComputeAABB( m_table1Id ); + g_draw.DrawAABB( aabb, b2_colorYellow ); + + aabb = b2Body_ComputeAABB( m_table2Id ); + g_draw.DrawAABB( aabb, b2_colorYellow ); + + aabb = b2Body_ComputeAABB( m_ship1Id ); + g_draw.DrawAABB( aabb, b2_colorYellow ); + + aabb = b2Body_ComputeAABB( m_ship2Id ); + g_draw.DrawAABB( aabb, b2_colorYellow ); + } + } + + static Sample* Create( Settings& settings ) + { + return new CompoundShapes( settings ); + } + + b2BodyId m_table1Id; + b2BodyId m_table2Id; + b2BodyId m_ship1Id; + b2BodyId m_ship2Id; + bool m_drawBodyAABBs; +}; + +static int sampleCompoundShape = RegisterSample( "Shapes", "Compound Shapes", CompoundShapes::Create ); + +class ShapeFilter : public Sample +{ +public: + enum CollisionBits + { + GROUND = 0x00000001, + TEAM1 = 0x00000002, + TEAM2 = 0x00000004, + TEAM3 = 0x00000008, + + ALL_BITS = ( ~0u ) + }; + + explicit ShapeFilter( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_zoom = 25.0f * 0.5f; + g_camera.m_center = { 0.0f, 5.0f }; + } + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + b2Segment segment = { { -20.0f, 0.0f }, { 20.0f, 0.0f } }; + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.filter.categoryBits = GROUND; + shapeDef.filter.maskBits = ALL_BITS; + + b2CreateSegmentShape( groundId, &shapeDef, &segment ); + } + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + + bodyDef.position = { 0.0f, 2.0f }; + m_player1Id = b2CreateBody( m_worldId, &bodyDef ); + + bodyDef.position = { 0.0f, 5.0f }; + m_player2Id = b2CreateBody( m_worldId, &bodyDef ); + + bodyDef.position = { 0.0f, 8.0f }; + m_player3Id = b2CreateBody( m_worldId, &bodyDef ); + + b2Polygon box = b2MakeBox( 2.0f, 1.0f ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + + shapeDef.filter.categoryBits = TEAM1; + shapeDef.filter.maskBits = GROUND | TEAM2 | TEAM3; + m_shape1Id = b2CreatePolygonShape( m_player1Id, &shapeDef, &box ); + + shapeDef.filter.categoryBits = TEAM2; + shapeDef.filter.maskBits = GROUND | TEAM1 | TEAM3; + m_shape2Id = b2CreatePolygonShape( m_player2Id, &shapeDef, &box ); + + shapeDef.filter.categoryBits = TEAM3; + shapeDef.filter.maskBits = GROUND | TEAM1 | TEAM2; + m_shape3Id = b2CreatePolygonShape( m_player3Id, &shapeDef, &box ); + } + } + + void UpdateUI() override + { + float height = 240.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 240.0f, height ) ); + + ImGui::Begin( "Shape Filter", nullptr, ImGuiWindowFlags_NoResize ); + + ImGui::Text( "Player 1 Collides With" ); + { + b2Filter filter1 = b2Shape_GetFilter( m_shape1Id ); + bool team2 = ( filter1.maskBits & TEAM2 ) == TEAM2; + if ( ImGui::Checkbox( "Team 2##1", &team2 ) ) + { + if ( team2 ) + { + filter1.maskBits |= TEAM2; + } + else + { + filter1.maskBits &= ~TEAM2; + } + + b2Shape_SetFilter( m_shape1Id, filter1 ); + } + + bool team3 = ( filter1.maskBits & TEAM3 ) == TEAM3; + if ( ImGui::Checkbox( "Team 3##1", &team3 ) ) + { + if ( team3 ) + { + filter1.maskBits |= TEAM3; + } + else + { + filter1.maskBits &= ~TEAM3; + } + + b2Shape_SetFilter( m_shape1Id, filter1 ); + } + } + + ImGui::Separator(); + + ImGui::Text( "Player 2 Collides With" ); + { + b2Filter filter2 = b2Shape_GetFilter( m_shape2Id ); + bool team1 = ( filter2.maskBits & TEAM1 ) == TEAM1; + if ( ImGui::Checkbox( "Team 1##2", &team1 ) ) + { + if ( team1 ) + { + filter2.maskBits |= TEAM1; + } + else + { + filter2.maskBits &= ~TEAM1; + } + + b2Shape_SetFilter( m_shape2Id, filter2 ); + } + + bool team3 = ( filter2.maskBits & TEAM3 ) == TEAM3; + if ( ImGui::Checkbox( "Team 3##2", &team3 ) ) + { + if ( team3 ) + { + filter2.maskBits |= TEAM3; + } + else + { + filter2.maskBits &= ~TEAM3; + } + + b2Shape_SetFilter( m_shape2Id, filter2 ); + } + } + + ImGui::Separator(); + + ImGui::Text( "Player 3 Collides With" ); + { + b2Filter filter3 = b2Shape_GetFilter( m_shape3Id ); + bool team1 = ( filter3.maskBits & TEAM1 ) == TEAM1; + if ( ImGui::Checkbox( "Team 1##3", &team1 ) ) + { + if ( team1 ) + { + filter3.maskBits |= TEAM1; + } + else + { + filter3.maskBits &= ~TEAM1; + } + + b2Shape_SetFilter( m_shape3Id, filter3 ); + } + + bool team2 = ( filter3.maskBits & TEAM2 ) == TEAM2; + if ( ImGui::Checkbox( "Team 2##3", &team2 ) ) + { + if ( team2 ) + { + filter3.maskBits |= TEAM2; + } + else + { + filter3.maskBits &= ~TEAM2; + } + + b2Shape_SetFilter( m_shape3Id, filter3 ); + } + } + + ImGui::End(); + } + + void Step( Settings& settings ) override + { + Sample::Step( settings ); + + b2Vec2 p1 = b2Body_GetPosition( m_player1Id ); + g_draw.DrawString( { p1.x - 0.5f, p1.y }, "player 1" ); + + b2Vec2 p2 = b2Body_GetPosition( m_player2Id ); + g_draw.DrawString( { p2.x - 0.5f, p2.y }, "player 2" ); + + b2Vec2 p3 = b2Body_GetPosition( m_player3Id ); + g_draw.DrawString( { p3.x - 0.5f, p3.y }, "player 3" ); + } + + static Sample* Create( Settings& settings ) + { + return new ShapeFilter( settings ); + } + + b2BodyId m_player1Id; + b2BodyId m_player2Id; + b2BodyId m_player3Id; + + b2ShapeId m_shape1Id; + b2ShapeId m_shape2Id; + b2ShapeId m_shape3Id; +}; + +static int sampleShapeFilter = RegisterSample( "Shapes", "Filter", ShapeFilter::Create ); + +// This shows how to use custom filtering +class CustomFilter : public Sample +{ +public: + enum + { + e_count = 10 + }; + + explicit CustomFilter( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, 5.0f }; + g_camera.m_zoom = 10.0f; + } + + // Register custom filter + b2World_SetCustomFilterCallback( m_worldId, CustomFilterStatic, this ); + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + b2Segment segment = { { -40.0f, 0.0f }, { 40.0f, 0.0f } }; + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + + b2CreateSegmentShape( groundId, &shapeDef, &segment ); + } + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Polygon box = b2MakeSquare( 1.0f ); + float x = -e_count; + + for ( int i = 0; i < e_count; ++i ) + { + bodyDef.position = { x, 5.0f }; + m_bodyIds[i] = b2CreateBody( m_worldId, &bodyDef ); + + shapeDef.userData = reinterpret_cast( intptr_t( i + 1 ) ); + m_shapeIds[i] = b2CreatePolygonShape( m_bodyIds[i], &shapeDef, &box ); + x += 2.0f; + } + } + + void Step( Settings& settings ) override + { + g_draw.DrawString( 5, m_textLine, "Custom filter disables collision between odd and even shapes" ); + m_textLine += m_textIncrement; + + Sample::Step( settings ); + + for ( int i = 0; i < e_count; ++i ) + { + b2Vec2 p = b2Body_GetPosition( m_bodyIds[i] ); + g_draw.DrawString( { p.x, p.y }, "%d", i ); + } + } + + bool ShouldCollide( b2ShapeId shapeIdA, b2ShapeId shapeIdB ) + { + void* userDataA = b2Shape_GetUserData( shapeIdA ); + void* userDataB = b2Shape_GetUserData( shapeIdB ); + + if ( userDataA == NULL || userDataB == NULL ) + { + return true; + } + + int indexA = static_cast( reinterpret_cast( userDataA ) ); + int indexB = static_cast( reinterpret_cast( userDataB ) ); + + return ( ( indexA & 1 ) + ( indexB & 1 ) ) != 1; + } + + static bool CustomFilterStatic( b2ShapeId shapeIdA, b2ShapeId shapeIdB, void* context ) + { + CustomFilter* customFilter = static_cast( context ); + return customFilter->ShouldCollide( shapeIdA, shapeIdB ); + } + + static Sample* Create( Settings& settings ) + { + return new CustomFilter( settings ); + } + + b2BodyId m_bodyIds[e_count]; + b2ShapeId m_shapeIds[e_count]; +}; + +static int sampleCustomFilter = RegisterSample( "Shapes", "Custom Filter", CustomFilter::Create ); + +// Restitution is approximate since Box2D uses speculative collision +class Restitution : public Sample +{ +public: + enum + { + e_count = 40 + }; + + enum ShapeType + { + e_circleShape = 0, + e_boxShape + }; + + explicit Restitution( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 4.0f, 17.0f }; + g_camera.m_zoom = 27.5f; + } + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + float h = 1.0f * e_count; + b2Segment segment = { { -h, 0.0f }, { h, 0.0f } }; + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2CreateSegmentShape( groundId, &shapeDef, &segment ); + } + + for ( int i = 0; i < e_count; ++i ) + { + m_bodyIds[i] = b2_nullBodyId; + } + + m_shapeType = e_circleShape; + + CreateBodies(); + } + + void CreateBodies() + { + for ( int i = 0; i < e_count; ++i ) + { + if ( B2_IS_NON_NULL( m_bodyIds[i] ) ) + { + b2DestroyBody( m_bodyIds[i] ); + m_bodyIds[i] = b2_nullBodyId; + } + } + + b2Circle circle = { 0 }; + circle.radius = 0.5f; + + b2Polygon box = b2MakeBox( 0.5f, 0.5f ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 1.0f; + shapeDef.restitution = 0.0f; + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + + float dr = 1.0f / ( e_count > 1 ? e_count - 1 : 1 ); + float x = -1.0f * ( e_count - 1 ); + float dx = 2.0f; + + for ( int i = 0; i < e_count; ++i ) + { + bodyDef.position = { x, 40.0f }; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + + m_bodyIds[i] = bodyId; + + if ( m_shapeType == e_circleShape ) + { + b2CreateCircleShape( bodyId, &shapeDef, &circle ); + } + else + { + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + } + + shapeDef.restitution += dr; + x += dx; + } + } + + void UpdateUI() override + { + float height = 100.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 240.0f, height ) ); + + ImGui::Begin( "Restitution", nullptr, ImGuiWindowFlags_NoResize ); + + bool changed = false; + const char* shapeTypes[] = { "Circle", "Box" }; + + int shapeType = int( m_shapeType ); + changed = changed || ImGui::Combo( "Shape", &shapeType, shapeTypes, IM_ARRAYSIZE( shapeTypes ) ); + m_shapeType = ShapeType( shapeType ); + + changed = changed || ImGui::Button( "Reset" ); + + if ( changed ) + { + CreateBodies(); + } + + ImGui::End(); + } + + static Sample* Create( Settings& settings ) + { + return new Restitution( settings ); + } + + b2BodyId m_bodyIds[e_count]; + ShapeType m_shapeType; +}; + +static int sampleIndex = RegisterSample( "Shapes", "Restitution", Restitution::Create ); + +class Friction : public Sample +{ +public: + explicit Friction( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, 14.0f }; + g_camera.m_zoom = 25.0f * 0.6f; + } + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.friction = 0.2f; + + b2Segment segment = { { -40.0f, 0.0f }, { 40.0f, 0.0f } }; + b2CreateSegmentShape( groundId, &shapeDef, &segment ); + + b2Polygon box = b2MakeOffsetBox( 13.0f, 0.25f, { -4.0f, 22.0f }, b2MakeRot(-0.25f) ); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + + box = b2MakeOffsetBox( 0.25f, 1.0f, { 10.5f, 19.0f }, b2Rot_identity ); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + + box = b2MakeOffsetBox( 13.0f, 0.25f, { 4.0f, 14.0f }, b2MakeRot(0.25f) ); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + + box = b2MakeOffsetBox( 0.25f, 1.0f, { -10.5f, 11.0f }, b2Rot_identity ); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + + box = b2MakeOffsetBox( 13.0f, 0.25f, { -4.0f, 6.0f }, b2MakeRot(-0.25f) ); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + } + + { + b2Polygon box = b2MakeBox( 0.5f, 0.5f ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 25.0f; + + float friction[5] = { 0.75f, 0.5f, 0.35f, 0.1f, 0.0f }; + + for ( int i = 0; i < 5; ++i ) + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = { -15.0f + 4.0f * i, 28.0f }; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + + shapeDef.friction = friction[i]; + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + } + } + } + + static Sample* Create( Settings& settings ) + { + return new Friction( settings ); + } +}; + +static int sampleIndex3 = RegisterSample( "Shapes", "Friction", Friction::Create ); + +// This sample shows how to modify the geometry on an existing shape. This is only supported on +// dynamic and kinematic shapes because static shapes don't look for new collisions. +class ModifyGeometry : public Sample +{ +public: + explicit ModifyGeometry( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_zoom = 25.0f * 0.25f; + g_camera.m_center = { 0.0f, 5.0f }; + } + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Polygon box = b2MakeOffsetBox( 10.0f, 1.0f, { 0.0f, -1.0f }, b2Rot_identity ); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + } + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = { 0.0f, 4.0f }; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Polygon box = b2MakeBox( 1.0f, 1.0f ); + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + } + + { + m_shapeType = b2_circleShape; + m_scale = 1.0f; + m_circle = { { 0.0f, 0.0f }, 0.5f }; + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_kinematicBody; + bodyDef.position = { 0.0f, 1.0f }; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + m_shapeId = b2CreateCircleShape( bodyId, &shapeDef, &m_circle ); + } + } + + void UpdateShape() + { + switch ( m_shapeType ) + { + case b2_circleShape: + m_circle = { { 0.0f, 0.0f }, 0.5f * m_scale }; + b2Shape_SetCircle( m_shapeId, &m_circle ); + break; + + case b2_capsuleShape: + m_capsule = { { -0.5f * m_scale, 0.0f }, { 0.0f, 0.5f * m_scale }, 0.5f * m_scale }; + b2Shape_SetCapsule( m_shapeId, &m_capsule ); + break; + + case b2_segmentShape: + m_segment = { { -0.5f * m_scale, 0.0f }, { 0.75f * m_scale, 0.0f } }; + b2Shape_SetSegment( m_shapeId, &m_segment ); + break; + + case b2_polygonShape: + m_polygon = b2MakeBox( 0.5f * m_scale, 0.75f * m_scale ); + b2Shape_SetPolygon( m_shapeId, &m_polygon ); + break; + + default: + assert( false ); + break; + } + + b2BodyId bodyId = b2Shape_GetBody( m_shapeId ); + b2Body_ApplyMassFromShapes( bodyId ); + } + + void UpdateUI() override + { + float height = 230.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 200.0f, height ) ); + + ImGui::Begin( "Modify Geometry", nullptr, ImGuiWindowFlags_NoResize ); + + if ( ImGui::RadioButton( "Circle", m_shapeType == b2_circleShape ) ) + { + m_shapeType = b2_circleShape; + UpdateShape(); + } + + if ( ImGui::RadioButton( "Capsule", m_shapeType == b2_capsuleShape ) ) + { + m_shapeType = b2_capsuleShape; + UpdateShape(); + } + + if ( ImGui::RadioButton( "Segment", m_shapeType == b2_segmentShape ) ) + { + m_shapeType = b2_segmentShape; + UpdateShape(); + } + + if ( ImGui::RadioButton( "Polygon", m_shapeType == b2_polygonShape ) ) + { + m_shapeType = b2_polygonShape; + UpdateShape(); + } + + if ( ImGui::SliderFloat( "Scale", &m_scale, 0.1f, 10.0f, "%.2f" ) ) + { + UpdateShape(); + } + + b2BodyId bodyId = b2Shape_GetBody( m_shapeId ); + b2BodyType bodyType = b2Body_GetType( bodyId ); + + if ( ImGui::RadioButton( "Static", bodyType == b2_staticBody ) ) + { + b2Body_SetType( bodyId, b2_staticBody ); + } + + if ( ImGui::RadioButton( "Kinematic", bodyType == b2_kinematicBody ) ) + { + b2Body_SetType( bodyId, b2_kinematicBody ); + } + + if ( ImGui::RadioButton( "Dynamic", bodyType == b2_dynamicBody ) ) + { + b2Body_SetType( bodyId, b2_dynamicBody ); + } + + ImGui::End(); + } + + void Step( Settings& settings ) override + { + Sample::Step( settings ); + } + + static Sample* Create( Settings& settings ) + { + return new ModifyGeometry( settings ); + } + + b2ShapeId m_shapeId; + b2ShapeType m_shapeType; + float m_scale; + + union + { + b2Circle m_circle; + b2Capsule m_capsule; + b2Segment m_segment; + b2Polygon m_polygon; + }; +}; + +static int sampleModifyGeometry = RegisterSample( "Shapes", "Modify Geometry", ModifyGeometry::Create ); + +// Shows how to link to chain shapes together. This is a useful technique for building large game levels with smooth collision. +class ChainLink : public Sample +{ +public: + explicit ChainLink( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, 5.0f }; + g_camera.m_zoom = 25.0f * 0.5f; + } + + b2Vec2 points1[] = { { 40.0f, 1.0f }, { 0.0f, 0.0f }, { -40.0f, 0.0f }, + { -40.0f, -1.0f }, { 0.0f, -1.0f }, { 40.0f, -1.0f } }; + b2Vec2 points2[] = { { -40.0f, -1.0f }, { 0.0f, -1.0f }, { 40.0f, -1.0f }, + { 40.0f, 0.0f }, { 0.0f, 0.0f }, { -40.0f, 0.0f } }; + + int count1 = std::size( points1 ); + int count2 = std::size( points2 ); + + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + { + b2ChainDef chainDef = b2DefaultChainDef(); + chainDef.points = points1; + chainDef.count = count1; + chainDef.isLoop = false; + b2CreateChain( groundId, &chainDef ); + } + + { + b2ChainDef chainDef = b2DefaultChainDef(); + chainDef.points = points2; + chainDef.count = count2; + chainDef.isLoop = false; + b2CreateChain( groundId, &chainDef ); + } + + bodyDef.type = b2_dynamicBody; + b2ShapeDef shapeDef = b2DefaultShapeDef(); + + { + bodyDef.position = { -5.0f, 2.0f }; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2Circle circle = { { 0.0f, 0.0f }, 0.5f }; + b2CreateCircleShape( bodyId, &shapeDef, &circle ); + } + + { + bodyDef.position = { 0.0f, 2.0f }; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2Capsule capsule = { { -0.5f, 0.0f }, { 0.5f, 0.0 }, 0.25f }; + b2CreateCapsuleShape( bodyId, &shapeDef, &capsule ); + } + + { + bodyDef.position = { 5.0f, 2.0f }; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + float h = 0.5f; + b2Polygon box = b2MakeBox( h, h ); + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + } + } + + void Step( Settings& settings ) override + { + Sample::Step( settings ); + + g_draw.DrawString( 5, m_textLine, "This shows how to link together two chain shapes" ); + m_textLine += m_textIncrement; + } + + static Sample* Create( Settings& settings ) + { + return new ChainLink( settings ); + } +}; + +static int sampleChainLink = RegisterSample( "Shapes", "Chain Link", ChainLink::Create ); + +class RoundedShapes : public Sample +{ +public: + explicit RoundedShapes( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_zoom = 25.0f * 0.55f; + g_camera.m_center = { 2.0f, 8.0f }; + } + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Polygon box = b2MakeOffsetBox( 20.0f, 1.0f, { 0.0f, -1.0f }, b2Rot_identity ); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + + box = b2MakeOffsetBox( 1.0f, 5.0f, { 19.0f, 5.0f }, b2Rot_identity ); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + + box = b2MakeOffsetBox( 1.0f, 5.0f, { -19.0f, 5.0f }, b2Rot_identity ); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + } + + // b2Capsule capsule = {{-0.25f, 0.0f}, {0.25f, 0.0f}, 0.25f}; + // b2Circle circle = {{0.0f, 0.0f}, 0.35f}; + // b2Polygon square = b2MakeSquare(0.35f); + + // b2Vec2 points[3] = {{-0.1f, -0.5f}, {0.1f, -0.5f}, {0.0f, 0.5f}}; + // b2Hull wedgeHull = b2ComputeHull(points, 3); + // b2Polygon wedge = b2MakePolygon(&wedgeHull, 0.0f); + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + b2ShapeDef shapeDef = b2DefaultShapeDef(); + + float y = 2.0f; + int xcount = 10, ycount = 10; + + for ( int i = 0; i < ycount; ++i ) + { + float x = -5.0f; + for ( int j = 0; j < xcount; ++j ) + { + bodyDef.position = { x, y }; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + + b2Polygon poly = RandomPolygon( 0.5f ); + poly.radius = RandomFloat( 0.05f, 0.25f ); + b2CreatePolygonShape( bodyId, &shapeDef, &poly ); + + x += 1.0f; + } + + y += 1.0f; + } + } + + static Sample* Create( Settings& settings ) + { + return new RoundedShapes( settings ); + } +}; + +static int sampleRoundedShapes = RegisterSample( "Shapes", "Rounded", RoundedShapes::Create ); + +class OffsetShapes : public Sample +{ +public: + explicit OffsetShapes( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_zoom = 25.0f * 0.55f; + g_camera.m_center = { 2.0f, 8.0f }; + } + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.position = { -1.0f, 1.0f }; + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Polygon box = b2MakeOffsetBox( 1.0f, 1.0f, { 10.0f, -2.0f }, b2MakeRot(0.5f * b2_pi) ); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + } + + { + b2Capsule capsule = { { -5.0f, 1.0f }, { -4.0f, 1.0f }, 0.25f }; + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.position = { 13.5f, -0.75f }; + bodyDef.type = b2_dynamicBody; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2CreateCapsuleShape( bodyId, &shapeDef, &capsule ); + } + + { + b2Polygon box = b2MakeOffsetBox( 0.75f, 0.5f, { 9.0f, 2.0f }, b2MakeRot(0.5f * b2_pi) ); + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.position = { 0.0f, 0.0f }; + bodyDef.type = b2_dynamicBody; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + } + } + + void Step( Settings& settings ) override + { + Sample::Step( settings ); + + g_draw.DrawTransform( b2Transform_identity ); + } + + static Sample* Create( Settings& settings ) + { + return new OffsetShapes( settings ); + } +}; + +static int sampleOffsetShapes = RegisterSample( "Shapes", "Offset", OffsetShapes::Create ); diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/sample_stacking.cpp b/tests/cpp-tests/Source/Box2DTestBed/samples/sample_stacking.cpp new file mode 100644 index 000000000000..b0e39edda39a --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/sample_stacking.cpp @@ -0,0 +1,889 @@ +// SPDX-FileCopyrightText: 2022 Erin Catto +// SPDX-License-Identifier: MIT + +#include "draw.h" +#include "sample.h" +#include "settings.h" + +#include "box2d/box2d.h" +#include "box2d/math_functions.h" + +#include +#include + +class SingleBox : public Sample +{ +public: + explicit SingleBox( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, 2.5f }; + g_camera.m_zoom = 3.5f; + } + + float extent = 1.0f; + + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + float groundWidth = 66.0f * extent; + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.friction = 0.5f; + + b2Segment segment = { { -0.5f * 2.0f * groundWidth, 0.0f }, { 0.5f * 2.0f * groundWidth, 0.0f } }; + b2CreateSegmentShape( groundId, &shapeDef, &segment ); + bodyDef.type = b2_dynamicBody; + + b2Polygon box = b2MakeBox( extent, extent ); + bodyDef.position = { 0.0f, 4.0f }; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + } + + void Step( Settings& settings ) override + { + Sample::Step( settings ); + + // g_draw.DrawCircle({0.0f, 2.0f}, 1.0f, b2_colorWhite); + } + + static Sample* Create( Settings& settings ) + { + return new SingleBox( settings ); + } +}; + +static int sampleSingleBox = RegisterSample( "Stacking", "Single Box", SingleBox::Create ); + +class TiltedStack : public Sample +{ +public: + enum + { + e_columns = 10, + e_rows = 10, + }; + + explicit TiltedStack( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 7.5f, 7.5f }; + g_camera.m_zoom = 20.0f; + } + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.position = { 0.0f, -1.0f }; + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2Polygon box = b2MakeBox( 1000.0f, 1.0f ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + } + + for ( int i = 0; i < e_rows * e_columns; ++i ) + { + m_bodies[i] = b2_nullBodyId; + } + + b2Polygon box = b2MakeRoundedBox( 0.45f, 0.45f, 0.05f ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 1.0f; + shapeDef.friction = 0.3f; + + float offset = 0.2f; + float dx = 5.0f; + float xroot = -0.5f * dx * ( e_columns - 1.0f ); + + for ( int j = 0; j < e_columns; ++j ) + { + float x = xroot + j * dx; + + for ( int i = 0; i < e_rows; ++i ) + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + + int n = j * e_rows + i; + + bodyDef.position = { x + offset * i, 0.5f + 1.0f * i }; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + + m_bodies[n] = bodyId; + + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + } + } + } + + static Sample* Create( Settings& settings ) + { + return new TiltedStack( settings ); + } + + b2BodyId m_bodies[e_rows * e_columns]; +}; + +static int sampleTiltedStack = RegisterSample( "Stacking", "Tilted Stack", TiltedStack::Create ); + +class VerticalStack : public Sample +{ +public: + enum + { + e_maxColumns = 10, + e_maxRows = 12, + e_maxBullets = 8 + }; + + enum ShapeType + { + e_circleShape = 0, + e_boxShape + }; + + explicit VerticalStack( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { -7.0f, 9.0f }; + g_camera.m_zoom = 14.0f; + } + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.position = { 0.0f, -1.0f }; + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2Polygon box = b2MakeBox( 100.0f, 1.0f ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + + b2Segment segment = { { 10.0f, 1.0f }, { 10.0f, 21.0f } }; + b2CreateSegmentShape( groundId, &shapeDef, &segment ); + } + + for ( int i = 0; i < e_maxRows * e_maxColumns; ++i ) + { + m_bodies[i] = b2_nullBodyId; + } + + for ( int i = 0; i < e_maxBullets; ++i ) + { + m_bullets[i] = b2_nullBodyId; + } + + m_shapeType = e_boxShape; + m_rowCount = e_maxRows; + m_columnCount = 5; + m_bulletCount = 1; + m_bulletType = e_circleShape; + + CreateStacks(); + } + + void CreateStacks() + { + for ( int i = 0; i < e_maxRows * e_maxColumns; ++i ) + { + if ( B2_IS_NON_NULL( m_bodies[i] ) ) + { + b2DestroyBody( m_bodies[i] ); + m_bodies[i] = b2_nullBodyId; + } + } + + b2Circle circle = { 0 }; + circle.radius = 0.5f; + + b2Polygon box = b2MakeBox( 0.5f, 0.5f ); + // b2Polygon box = b2MakeRoundedBox(0.45f, 0.45f, 0.05f); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 1.0f; + shapeDef.friction = 0.3f; + + float offset; + + if ( m_shapeType == e_circleShape ) + { + offset = 0.0f; + } + else + { + offset = 0.01f; + } + + float dx = -3.0f; + float xroot = 8.0f; + + for ( int j = 0; j < m_columnCount; ++j ) + { + float x = xroot + j * dx; + + for ( int i = 0; i < m_rowCount; ++i ) + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + + int n = j * m_rowCount + i; + + float shift = ( i % 2 == 0 ? -offset : offset ); + bodyDef.position = { x + shift, 0.5f + 1.0f * i }; + // bodyDef.position = {x + shift, 1.0f + 1.51f * i}; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + + m_bodies[n] = bodyId; + + if ( m_shapeType == e_circleShape ) + { + b2CreateCircleShape( bodyId, &shapeDef, &circle ); + } + else + { + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + } + } + } + } + + void DestroyBody() + { + for ( int j = 0; j < m_columnCount; ++j ) + { + for ( int i = 0; i < m_rowCount; ++i ) + { + int n = j * m_rowCount + i; + + if ( B2_IS_NON_NULL( m_bodies[n] ) ) + { + b2DestroyBody( m_bodies[n] ); + m_bodies[n] = b2_nullBodyId; + break; + } + } + } + } + + void DestroyBullets() + { + for ( int i = 0; i < e_maxBullets; ++i ) + { + b2BodyId bullet = m_bullets[i]; + + if ( B2_IS_NON_NULL( bullet ) ) + { + b2DestroyBody( bullet ); + m_bullets[i] = b2_nullBodyId; + } + } + } + + void FireBullets() + { + b2Circle circle = { { 0.0f, 0.0f }, 0.25f }; + b2Polygon box = b2MakeBox( 0.25f, 0.25f ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.density = 4.0f; + + for ( int i = 0; i < m_bulletCount; ++i ) + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = { -25.0f - i, 6.0f }; + float speed = RandomFloat( 200.0f, 300.0f ); + bodyDef.linearVelocity = { speed, 0.0f }; + bodyDef.isBullet = true; + + b2BodyId bullet = b2CreateBody( m_worldId, &bodyDef ); + + if ( m_bulletType == e_boxShape ) + { + b2CreatePolygonShape( bullet, &shapeDef, &box ); + } + else + { + b2CreateCircleShape( bullet, &shapeDef, &circle ); + } + assert( B2_IS_NULL( m_bullets[i] ) ); + m_bullets[i] = bullet; + } + } + + void UpdateUI() override + { + float height = 230.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 240.0f, height ) ); + + ImGui::Begin( "Vertical Stack", nullptr, ImGuiWindowFlags_NoResize ); + + ImGui::PushItemWidth( 120.0f ); + + bool changed = false; + const char* shapeTypes[] = { "Circle", "Box" }; + + int shapeType = int( m_shapeType ); + changed = changed || ImGui::Combo( "Shape", &shapeType, shapeTypes, IM_ARRAYSIZE( shapeTypes ) ); + m_shapeType = ShapeType( shapeType ); + + changed = changed || ImGui::SliderInt( "Rows", &m_rowCount, 1, e_maxRows ); + changed = changed || ImGui::SliderInt( "Columns", &m_columnCount, 1, e_maxColumns ); + + ImGui::SliderInt( "Bullets", &m_bulletCount, 1, e_maxBullets ); + + int bulletType = int( m_bulletType ); + ImGui::Combo( "Bullet Shape", &bulletType, shapeTypes, IM_ARRAYSIZE( shapeTypes ) ); + m_bulletType = ShapeType( bulletType ); + + ImGui::PopItemWidth(); + + if ( ImGui::Button( "Fire Bullets" ) || glfwGetKey( g_mainWindow, GLFW_KEY_B ) == GLFW_PRESS ) + { + DestroyBullets(); + FireBullets(); + } + + if ( ImGui::Button( "Destroy Body" ) ) + { + DestroyBody(); + } + + changed = changed || ImGui::Button( "Reset Stack" ); + + if ( changed ) + { + DestroyBullets(); + CreateStacks(); + } + + ImGui::End(); + } + + static Sample* Create( Settings& settings ) + { + return new VerticalStack( settings ); + } + + b2BodyId m_bullets[e_maxBullets]; + b2BodyId m_bodies[e_maxRows * e_maxColumns]; + int m_columnCount; + int m_rowCount; + int m_bulletCount; + ShapeType m_shapeType; + ShapeType m_bulletType; +}; + +static int sampleVerticalStack = RegisterSample( "Stacking", "Vertical Stack", VerticalStack::Create ); + +// This shows how to handle high gravity and small shapes using a small time step +class CircleStack : public Sample +{ +public: + explicit CircleStack( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, 5.0f }; + g_camera.m_zoom = 6.0f; + } + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Segment segment = { { -10.0f, 0.0f }, { 10.0f, 0.0f } }; + b2CreateSegmentShape( groundId, &shapeDef, &segment ); + } + + b2World_SetGravity( m_worldId, { 0.0f, -20.0f } ); + b2World_SetContactTuning( m_worldId, 0.25f * 360.0f, 10.0f, 3.0f ); + + b2Circle circle = {}; + circle.radius = 0.5f; + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + + float y = 0.5f; + + for ( int i = 0; i < 8; ++i ) + { + bodyDef.position.y = y; + + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2CreateCircleShape( bodyId, &shapeDef, &circle ); + + y += 1.0f; + } + } + + static Sample* Create( Settings& settings ) + { + return new CircleStack( settings ); + } +}; + +static int sampleCircleStack = RegisterSample( "Stacking", "Circle Stack", CircleStack::Create ); + +class Cliff : public Sample +{ +public: + explicit Cliff( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_zoom = 25.0f * 0.5f; + g_camera.m_center = { 0.0f, 5.0f }; + } + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.position = { 0.0f, 0.0f }; + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Polygon box = b2MakeOffsetBox( 100.0f, 1.0f, { 0.0f, -1.0f }, b2Rot_identity ); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + + b2Segment segment = { { -14.0f, 4.0f }, { -8.0f, 4.0f } }; + b2CreateSegmentShape( groundId, &shapeDef, &segment ); + + box = b2MakeOffsetBox( 3.0f, 0.5f, { 0.0f, 4.0f }, b2Rot_identity ); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + + b2Capsule capsule = { { 8.5f, 4.0f }, { 13.5f, 4.0f }, 0.5f }; + b2CreateCapsuleShape( groundId, &shapeDef, &capsule ); + } + + m_flip = false; + + for ( int i = 0; i < 9; ++i ) + { + m_bodyIds[i] = b2_nullBodyId; + } + + CreateBodies(); + } + + void CreateBodies() + { + for ( int i = 0; i < 9; ++i ) + { + if ( B2_IS_NON_NULL( m_bodyIds[i] ) ) + { + b2DestroyBody( m_bodyIds[i] ); + m_bodyIds[i] = b2_nullBodyId; + } + } + + float sign = m_flip ? -1.0f : 1.0f; + + b2Capsule capsule = { { -0.25f, 0.0f }, { 0.25f, 0.0f }, 0.25f }; + b2Circle circle = { { 0.0f, 0.0f }, 0.5f }; + b2Polygon square = b2MakeSquare( 0.5f ); + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + + { + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.friction = 0.01f; + bodyDef.linearVelocity = { 2.0f * sign, 0.0f }; + + float offset = m_flip ? -4.0f : 0.0f; + + bodyDef.position = { -9.0f + offset, 4.25f }; + m_bodyIds[0] = b2CreateBody( m_worldId, &bodyDef ); + b2CreateCapsuleShape( m_bodyIds[0], &shapeDef, &capsule ); + + bodyDef.position = { 2.0f + offset, 4.75f }; + m_bodyIds[1] = b2CreateBody( m_worldId, &bodyDef ); + b2CreateCapsuleShape( m_bodyIds[1], &shapeDef, &capsule ); + + bodyDef.position = { 13.0f + offset, 4.75f }; + m_bodyIds[2] = b2CreateBody( m_worldId, &bodyDef ); + b2CreateCapsuleShape( m_bodyIds[2], &shapeDef, &capsule ); + } + + { + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.friction = 0.01f; + bodyDef.linearVelocity = { 2.5f * sign, 0.0f }; + + bodyDef.position = { -11.0f, 4.5f }; + m_bodyIds[3] = b2CreateBody( m_worldId, &bodyDef ); + b2CreatePolygonShape( m_bodyIds[3], &shapeDef, &square ); + + bodyDef.position = { 0.0f, 5.0f }; + m_bodyIds[4] = b2CreateBody( m_worldId, &bodyDef ); + b2CreatePolygonShape( m_bodyIds[4], &shapeDef, &square ); + + bodyDef.position = { 11.0f, 5.0f }; + m_bodyIds[5] = b2CreateBody( m_worldId, &bodyDef ); + b2CreatePolygonShape( m_bodyIds[5], &shapeDef, &square ); + } + + { + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.friction = 0.2f; + bodyDef.linearVelocity = { 1.5f * sign, 0.0f }; + + float offset = m_flip ? 4.0f : 0.0f; + + bodyDef.position = { -13.0f + offset, 4.5f }; + m_bodyIds[6] = b2CreateBody( m_worldId, &bodyDef ); + b2CreateCircleShape( m_bodyIds[6], &shapeDef, &circle ); + + bodyDef.position = { -2.0f + offset, 5.0f }; + m_bodyIds[7] = b2CreateBody( m_worldId, &bodyDef ); + b2CreateCircleShape( m_bodyIds[7], &shapeDef, &circle ); + + bodyDef.position = { 9.0f + offset, 5.0f }; + m_bodyIds[8] = b2CreateBody( m_worldId, &bodyDef ); + b2CreateCircleShape( m_bodyIds[8], &shapeDef, &circle ); + } + } + + void UpdateUI() override + { + float height = 60.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 160.0f, height ) ); + + ImGui::Begin( "Cliff", nullptr, ImGuiWindowFlags_NoResize ); + + if ( ImGui::Button( "Flip" ) ) + { + m_flip = !m_flip; + CreateBodies(); + } + + ImGui::End(); + } + + static Sample* Create( Settings& settings ) + { + return new Cliff( settings ); + } + + b2BodyId m_bodyIds[9]; + bool m_flip; +}; + +static int sampleCliff = RegisterSample( "Stacking", "Cliff", Cliff::Create ); + +class Arch : public Sample +{ +public: + explicit Arch( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, 8.0f }; + g_camera.m_zoom = 25.0f * 0.35f; + } + + b2Vec2 ps1[9] = { { 16.0f, 0.0f }, + { 14.93803712795643f, 5.133601056842984f }, + { 13.79871746027416f, 10.24928069555078f }, + { 12.56252963284711f, 15.34107019122473f }, + { 11.20040987372525f, 20.39856541571217f }, + { 9.66521217819836f, 25.40369899225096f }, + { 7.87179930638133f, 30.3179337000085f }, + { 5.635199558196225f, 35.03820717801641f }, + { 2.405937953536585f, 39.09554102558315f } }; + + b2Vec2 ps2[9] = { { 24.0f, 0.0f }, + { 22.33619528222415f, 6.02299846205841f }, + { 20.54936888969905f, 12.00964361211476f }, + { 18.60854610798073f, 17.9470321677465f }, + { 16.46769273811807f, 23.81367936585418f }, + { 14.05325025774858f, 29.57079353071012f }, + { 11.23551045834022f, 35.13775818285372f }, + { 7.752568160730571f, 40.30450679009583f }, + { 3.016931552701656f, 44.28891593799322f } }; + + float scale = 0.25f; + for ( int i = 0; i < 9; ++i ) + { + ps1[i] = b2MulSV( scale, ps1[i] ); + ps2[i] = b2MulSV( scale, ps2[i] ); + } + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.friction = 0.6f; + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + b2Segment segment = { { -100.0f, 0.0f }, { 100.0f, 0.0f } }; + b2CreateSegmentShape( groundId, &shapeDef, &segment ); + } + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + + for ( int i = 0; i < 8; ++i ) + { + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2Vec2 ps[4] = { ps1[i], ps2[i], ps2[i + 1], ps1[i + 1] }; + b2Hull hull = b2ComputeHull( ps, 4 ); + b2Polygon polygon = b2MakePolygon( &hull, 0.0f ); + b2CreatePolygonShape( bodyId, &shapeDef, &polygon ); + } + + for ( int i = 0; i < 8; ++i ) + { + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2Vec2 ps[4] = { { -ps2[i].x, ps2[i].y }, + { -ps1[i].x, ps1[i].y }, + { -ps1[i + 1].x, ps1[i + 1].y }, + { -ps2[i + 1].x, ps2[i + 1].y } }; + b2Hull hull = b2ComputeHull( ps, 4 ); + b2Polygon polygon = b2MakePolygon( &hull, 0.0f ); + b2CreatePolygonShape( bodyId, &shapeDef, &polygon ); + } + + { + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2Vec2 ps[4] = { ps1[8], ps2[8], { -ps2[8].x, ps2[8].y }, { -ps1[8].x, ps1[8].y } }; + b2Hull hull = b2ComputeHull( ps, 4 ); + b2Polygon polygon = b2MakePolygon( &hull, 0.0f ); + b2CreatePolygonShape( bodyId, &shapeDef, &polygon ); + } + + for ( int i = 0; i < 4; ++i ) + { + b2Polygon box = b2MakeBox( 2.0f, 0.5f ); + bodyDef.position = { 0.0f, 0.5f + ps2[8].y + 1.0f * i }; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + } + } + + static Sample* Create( Settings& settings ) + { + return new Arch( settings ); + } +}; + +static int sampleArch = RegisterSample( "Stacking", "Arch", Arch::Create ); + +class DoubleDomino : public Sample +{ +public: + explicit DoubleDomino( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, 4.0f }; + g_camera.m_zoom = 25.0f * 0.25f; + } + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.position = { 0.0f, -1.0f }; + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2Polygon box = b2MakeBox( 100.0f, 1.0f ); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2CreatePolygonShape( groundId, &shapeDef, &box ); + } + + b2Polygon box = b2MakeBox( 0.125f, 0.5f ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.friction = 0.6f; + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + + int count = 15; + float x = -0.5f * count; + for ( int i = 0; i < count; ++i ) + { + bodyDef.position = { x, 0.5f }; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + if ( i == 0 ) + { + b2Body_ApplyLinearImpulse( bodyId, b2Vec2{ 0.2f, 0.0f }, b2Vec2{ x, 1.0f }, true ); + } + + x += 1.0f; + } + } + + static Sample* Create( Settings& settings ) + { + return new DoubleDomino( settings ); + } +}; + +static int sampleDoubleDomino = RegisterSample( "Stacking", "Double Domino", DoubleDomino::Create ); + +class Confined : public Sample +{ +public: + enum + { + e_gridCount = 25, + e_maxCount = e_gridCount * e_gridCount + }; + + explicit Confined( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.0f, 10.0f }; + g_camera.m_zoom = 25.0f * 0.5f; + } + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Capsule capsule; + capsule = { { -10.5f, 0.0f }, { 10.5f, 0.0f }, 0.5f }; + b2CreateCapsuleShape( groundId, &shapeDef, &capsule ); + capsule = { { -10.5f, 0.0f }, { -10.5f, 20.5f }, 0.5f }; + b2CreateCapsuleShape( groundId, &shapeDef, &capsule ); + capsule = { { 10.5f, 0.0f }, { 10.5f, 20.5f }, 0.5f }; + b2CreateCapsuleShape( groundId, &shapeDef, &capsule ); + capsule = { { -10.5f, 20.5f }, { 10.5f, 20.5f }, 0.5f }; + b2CreateCapsuleShape( groundId, &shapeDef, &capsule ); + } + + m_row = 0; + m_column = 0; + m_count = 0; + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.gravityScale = 0.0f; + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Circle circle = { { 0.0f, 0.0f }, 0.5f }; + + while ( m_count < e_maxCount ) + { + m_row = 0; + for ( int i = 0; i < e_gridCount; ++i ) + { + float x = -8.75f + m_column * 18.0f / e_gridCount; + float y = 1.5f + m_row * 18.0f / e_gridCount; + + bodyDef.position = { x, y }; + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2CreateCircleShape( bodyId, &shapeDef, &circle ); + + m_count += 1; + m_row += 1; + } + m_column += 1; + } + } + + static Sample* Create( Settings& settings ) + { + return new Confined( settings ); + } + + int m_row; + int m_column; + int m_count; +}; + +static int sampleConfined = RegisterSample( "Stacking", "Confined", Confined::Create ); + +// From PEEL +class CardHouse : public Sample +{ +public: + explicit CardHouse( Settings& settings ) + : Sample( settings ) + { + if ( settings.restart == false ) + { + g_camera.m_center = { 0.75f, 0.9f }; + g_camera.m_zoom = 25.0f * 0.05f; + } + + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.position = { 0.0f, -2.0f }; + b2BodyId groundId = b2CreateBody( m_worldId, &bodyDef ); + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + shapeDef.friction = 0.7f; + + b2Polygon groundBox = b2MakeBox( 40.0f, 2.0f ); + b2CreatePolygonShape( groundId, &shapeDef, &groundBox ); + + float cardHeight = 0.2f; + float cardThickness = 0.001f; + + float angle0 = 25.0f * b2_pi / 180.0f; + float angle1 = -25.0f * b2_pi / 180.0f; + float angle2 = 0.5f * b2_pi; + + b2Polygon cardBox = b2MakeBox( cardThickness, cardHeight ); + bodyDef.type = b2_dynamicBody; + + int Nb = 5; + float z0 = 0.0f; + float y = cardHeight - 0.02f; + while ( Nb ) + { + float z = z0; + for ( int i = 0; i < Nb; i++ ) + { + if ( i != Nb - 1 ) + { + bodyDef.position = { z + 0.25f, y + cardHeight - 0.015f }; + bodyDef.rotation = b2MakeRot( angle2 ); + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2CreatePolygonShape( bodyId, &shapeDef, &cardBox ); + } + + bodyDef.position = { z, y }; + bodyDef.rotation = b2MakeRot( angle1 ); + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2CreatePolygonShape( bodyId, &shapeDef, &cardBox ); + + z += 0.175f; + + bodyDef.position = { z, y }; + bodyDef.rotation = b2MakeRot( angle0 ); + bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2CreatePolygonShape( bodyId, &shapeDef, &cardBox ); + + z += 0.175f; + } + y += cardHeight * 2.0f - 0.03f; + z0 += 0.175f; + Nb--; + } + } + + static Sample* Create( Settings& settings ) + { + return new CardHouse( settings ); + } +}; + +static int sampleCardHouse = RegisterSample( "Stacking", "Card House", CardHouse::Create ); diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/sample_world.cpp b/tests/cpp-tests/Source/Box2DTestBed/samples/sample_world.cpp new file mode 100644 index 000000000000..1e5ad87be943 --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/sample_world.cpp @@ -0,0 +1,242 @@ +// SPDX-FileCopyrightText: 2022 Erin Catto +// SPDX-License-Identifier: MIT + +#include "car.h" +#include "donut.h" +#include "draw.h" +#include "human.h" +#include "sample.h" +#include "settings.h" + +#include "box2d/box2d.h" +#include "box2d/math_functions.h" + +#include +#include + +class LargeWorld : public Sample +{ +public: + explicit LargeWorld( Settings& settings ) + : Sample( settings ) + { + m_period = 40.0f; + float omega = 2.0 * b2_pi / m_period; + m_cycleCount = g_sampleDebug ? 10 : 600; + m_gridSize = 1.0f; + m_gridCount = (int)( m_cycleCount * m_period / m_gridSize ); + + float xStart = -0.5f * ( m_cycleCount * m_period ); + + m_viewPosition = { xStart, 15.0f }; + + if ( settings.restart == false ) + { + g_camera.m_center = m_viewPosition; + g_camera.m_zoom = 25.0f * 1.0f; + settings.drawJoints = false; + settings.useCameraBounds = true; + } + + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + b2ShapeDef shapeDef = b2DefaultShapeDef(); + + // Setting this to false significantly reduces the cost of creating + // static bodies and shapes. + shapeDef.forceContactCreation = false; + + float height = 4.0f; + float xBody = xStart; + float xShape = xStart; + + b2BodyId groundId; + + for ( int i = 0; i < m_gridCount; ++i ) + { + // Create a new body regularly so that shapes are not too far from the body origin. + // Most algorithms in Box2D work in local coordinates, but contact points are computed + // relative to the body origin. + // This makes a noticeable improvement in stability far from the origin. + if ( i % 10 == 0 ) + { + bodyDef.position.x = xBody; + groundId = b2CreateBody( m_worldId, &bodyDef ); + xShape = 0.0f; + } + + float y = 0.0f; + + int ycount = (int)roundf( height * cosf( omega * xBody ) ) + 12; + + for ( int j = 0; j < ycount; ++j ) + { + b2Polygon square = b2MakeOffsetBox( 0.4f * m_gridSize, 0.4f * m_gridSize, { xShape, y }, b2Rot_identity ); + square.radius = 0.1f; + b2CreatePolygonShape( groundId, &shapeDef, &square ); + + y += m_gridSize; + } + + xBody += m_gridSize; + xShape += m_gridSize; + } + } + + int humanIndex = 0; + int donutIndex = 0; + for ( int cycleIndex = 0; cycleIndex < m_cycleCount; ++cycleIndex ) + { + float xbase = ( 0.5f + cycleIndex ) * m_period + xStart; + + int remainder = cycleIndex % 3; + if ( remainder == 0 ) + { + b2BodyDef bodyDef = b2DefaultBodyDef(); + bodyDef.type = b2_dynamicBody; + bodyDef.position = { xbase - 3.0f, 10.0f }; + + b2ShapeDef shapeDef = b2DefaultShapeDef(); + b2Polygon box = b2MakeBox( 0.3f, 0.2f ); + + for ( int i = 0; i < 10; ++i ) + { + bodyDef.position.y = 10.0f; + for ( int j = 0; j < 5; ++j ) + { + b2BodyId bodyId = b2CreateBody( m_worldId, &bodyDef ); + b2CreatePolygonShape( bodyId, &shapeDef, &box ); + bodyDef.position.y += 0.5f; + } + bodyDef.position.x += 0.6f; + } + } + else if ( remainder == 1 ) + { + b2Vec2 position = { xbase - 2.0f, 10.0f }; + for ( int i = 0; i < 5; ++i ) + { + Human human; + human.Spawn( m_worldId, position, 1.5f, 0.05f, 0.0f, 0.0f, humanIndex + 1, NULL, false ); + humanIndex += 1; + position.x += 1.0f; + } + } + else + { + b2Vec2 position = { xbase - 4.0f, 12.0f }; + + for ( int i = 0; i < 5; ++i ) + { + Donut donut; + donut.Spawn( m_worldId, position, 0.75f, 0, NULL ); + donutIndex += 1; + position.x += 2.0f; + } + } + } + + m_car.Spawn( m_worldId, { xStart + 20.0f, 40.0f }, 10.0f, 2.0f, 0.7f, 2000.0f, nullptr ); + + m_cycleIndex = 0; + m_speed = 0.0f; + m_explosionPosition = { ( 0.5f + m_cycleIndex ) * m_period + xStart, 7.0f }; + m_explode = true; + m_followCar = false; + } + + void UpdateUI() override + { + float height = 160.0f; + ImGui::SetNextWindowPos( ImVec2( 10.0f, g_camera.m_height - height - 50.0f ), ImGuiCond_Once ); + ImGui::SetNextWindowSize( ImVec2( 240.0f, height ) ); + + ImGui::Begin( "Large World", nullptr, ImGuiWindowFlags_NoResize ); + + ImGui::SliderFloat( "speed", &m_speed, -400.0f, 400.0f, "%.0f" ); + if ( ImGui::Button( "stop" ) ) + { + m_speed = 0.0f; + } + + ImGui::Checkbox( "explode", &m_explode ); + ImGui::Checkbox( "follow car", &m_followCar ); + + ImGui::Text( "world size = %g kilometers", m_gridSize * m_gridCount / 1000.0f ); + ImGui::End(); + } + + void Step( Settings& settings ) override + { + float span = 0.5f * ( m_period * m_cycleCount ); + float timeStep = settings.hertz > 0.0f ? 1.0f / settings.hertz : 0.0f; + + if ( settings.pause ) + { + timeStep = 0.0f; + } + + m_viewPosition.x += timeStep * m_speed; + m_viewPosition.x = b2ClampFloat( m_viewPosition.x, -span, span ); + + if ( m_speed != 0.0f ) + { + g_camera.m_center = m_viewPosition; + } + + if ( m_followCar ) + { + g_camera.m_center.x = b2Body_GetPosition( m_car.m_chassisId ).x; + } + + float radius = 2.0f; + if ( ( m_stepCount & 0x1 ) == 0x1 && m_explode ) + { + m_explosionPosition.x = ( 0.5f + m_cycleIndex ) * m_period - span; + b2World_Explode( m_worldId, m_explosionPosition, radius, 1.0f ); + m_cycleIndex = ( m_cycleIndex + 1 ) % m_cycleCount; + } + + if ( m_explode ) + { + g_draw.DrawCircle( m_explosionPosition, radius, b2_colorAzure ); + } + + if ( glfwGetKey( g_mainWindow, GLFW_KEY_A ) == GLFW_PRESS ) + { + m_car.SetSpeed( 20.0f ); + } + + if ( glfwGetKey( g_mainWindow, GLFW_KEY_S ) == GLFW_PRESS ) + { + m_car.SetSpeed( 0.0f ); + } + + if ( glfwGetKey( g_mainWindow, GLFW_KEY_D ) == GLFW_PRESS ) + { + m_car.SetSpeed( -5.0f ); + } + + Sample::Step( settings ); + } + + static Sample* Create( Settings& settings ) + { + return new LargeWorld( settings ); + } + + Car m_car; + b2Vec2 m_viewPosition; + float m_period; + int m_cycleCount; + int m_cycleIndex; + float m_gridCount; + float m_gridSize; + float m_speed; + + b2Vec2 m_explosionPosition; + bool m_explode; + bool m_followCar; +}; + +static int sampleLargeWorld = RegisterSample( "World", "Large World", LargeWorld::Create ); diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/settings.cpp b/tests/cpp-tests/Source/Box2DTestBed/samples/settings.cpp new file mode 100644 index 000000000000..76edc758c168 --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/settings.cpp @@ -0,0 +1,136 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#define _CRT_SECURE_NO_WARNINGS +#include "settings.h" + +// todo consider using https://github.com/skeeto/pdjson +#include +#include "jsmn/jsmn.h" +#include +#include +#include + +static const char* fileName = "settings.ini"; + +// Load a file. You must free the character array. +static bool ReadFile( char*& data, int& size, const char* filename ) +{ + FILE* file = fopen( filename, "rb" ); + if ( file == nullptr ) + { + return false; + } + + fseek( file, 0, SEEK_END ); + size = (int)ftell( file ); + fseek( file, 0, SEEK_SET ); + + if ( size == 0 ) + { + return false; + } + + data = (char*)malloc( size + 1 ); + size_t count = fread( data, size, 1, file ); + fclose( file ); + data[size] = 0; + + return true; +} + +void Settings::Save() +{ + FILE* file = fopen( fileName, "w" ); + fprintf( file, "{\n" ); + fprintf( file, " \"sampleIndex\": %d,\n", sampleIndex ); + fprintf( file, " \"drawShapes\": %s,\n", drawShapes ? "true" : "false" ); + fprintf( file, " \"drawJoints\": %s,\n", drawJoints ? "true" : "false" ); + fprintf( file, " \"drawAABBs\": %s,\n", drawAABBs ? "true" : "false" ); + fprintf( file, " \"drawContactPoints\": %s,\n", drawContactPoints ? "true" : "false" ); + fprintf( file, " \"drawContactNormals\": %s,\n", drawContactNormals ? "true" : "false" ); + fprintf( file, " \"drawContactImpulses\": %s,\n", drawContactImpulses ? "true" : "false" ); + fprintf( file, " \"drawFrictionImpulse\": %s,\n", drawFrictionImpulses ? "true" : "false" ); + fprintf( file, " \"drawMass\": %s,\n", drawMass ? "true" : "false" ); + fprintf( file, " \"drawCounters\": %s,\n", drawCounters ? "true" : "false" ); + fprintf( file, " \"drawProfile\": %s,\n", drawProfile ? "true" : "false" ); + fprintf( file, " \"enableWarmStarting\": %s,\n", enableWarmStarting ? "true" : "false" ); + fprintf( file, " \"enableContinuous\": %s,\n", enableContinuous ? "true" : "false" ); + fprintf( file, " \"enableSleep\": %s\n", enableSleep ? "true" : "false" ); + fprintf( file, "}\n" ); + fclose( file ); +} + +static int jsoneq( const char* json, jsmntok_t* tok, const char* s ) +{ + if ( tok->type == JSMN_STRING && (int)strlen( s ) == tok->end - tok->start && + strncmp( json + tok->start, s, tok->end - tok->start ) == 0 ) + { + return 0; + } + return -1; +} + +#define MAX_TOKENS 32 + +void Settings::Load() +{ + char* data = nullptr; + int size = 0; + bool found = ReadFile( data, size, fileName ); + if ( found == false ) + { + return; + } + + jsmn_parser parser; + jsmntok_t tokens[MAX_TOKENS]; + + jsmn_init( &parser ); + + // js - pointer to JSON string + // tokens - an array of tokens available + // 10 - number of tokens available + int tokenCount = jsmn_parse( &parser, data, size, tokens, MAX_TOKENS ); + char buffer[32]; + + for ( int i = 0; i < tokenCount; ++i ) + { + if ( jsoneq( data, &tokens[i], "sampleIndex" ) == 0 ) + { + int count = tokens[i + 1].end - tokens[i + 1].start; + assert( count < 32 ); + const char* s = data + tokens[i + 1].start; + strncpy( buffer, s, count ); + buffer[count] = 0; + char* dummy; + sampleIndex = (int)strtol( buffer, &dummy, 10 ); + } + else if ( jsoneq( data, &tokens[i], "drawShapes" ) == 0 ) + { + const char* s = data + tokens[i + 1].start; + if ( strncmp( s, "true", 4 ) == 0 ) + { + drawShapes = true; + } + else if ( strncmp( s, "false", 5 ) == 0 ) + { + drawShapes = false; + } + } + else if ( jsoneq( data, &tokens[i], "drawJoints" ) == 0 ) + { + const char* s = data + tokens[i + 1].start; + if ( strncmp( s, "true", 4 ) == 0 ) + { + drawJoints = true; + } + else if ( strncmp( s, "false", 5 ) == 0 ) + { + drawJoints = false; + } + } + } + + free( data ); +} diff --git a/tests/cpp-tests/Source/Box2DTestBed/samples/settings.h b/tests/cpp-tests/Source/Box2DTestBed/samples/settings.h new file mode 100644 index 000000000000..04a16a47b4af --- /dev/null +++ b/tests/cpp-tests/Source/Box2DTestBed/samples/settings.h @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +// todo add camera and draw and remove globals +struct Settings +{ + void Save(); + void Load(); + + int sampleIndex = 0; + // int windowWidth = 3840; + // int windowHeight = 2160; + int windowWidth = 1920; + int windowHeight = 1080; + // int windowWidth = 1280; + // int windowHeight = 720; + // int windowWidth = 800; + // int windowHeight = 600; + float hertz = 60.0f; + int subStepCount = 4; + int workerCount = 1; + bool useCameraBounds = false; + bool drawShapes = true; + bool drawJoints = true; + bool drawJointExtras = false; + bool drawAABBs = false; + bool drawContactPoints = false; + bool drawContactNormals = false; + bool drawContactImpulses = false; + bool drawFrictionImpulses = false; + bool drawMass = false; + bool drawGraphColors = false; + bool drawCounters = false; + bool drawProfile = false; + bool enableWarmStarting = true; + bool enableContinuous = true; + bool enableSleep = true; + bool pause = false; + bool singleStep = false; + bool restart = false; +}; diff --git a/tests/cpp-tests/Source/Box2DTestBed/test.cpp b/tests/cpp-tests/Source/Box2DTestBed/test.cpp deleted file mode 100644 index d5c26853a097..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/test.cpp +++ /dev/null @@ -1,514 +0,0 @@ -/* - * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -#include "tests/test.h" -#include "tests/settings.h" - -#include "extensions/axmol-ext.h" -#include "axmol.h" - -#include - -using namespace ax; -USING_NS_AX_EXT; - -#if defined(AX_PLATFORM_PC) -extern ax::Label* labelDebugDraw; -#endif - -void DestructionListener::SayGoodbye(b2Joint* joint) -{ - if (test->m_mouseJoint == joint) - { - test->m_mouseJoint = NULL; - } - else - { - test->JointDestroyed(joint); - } -} - -Test::Test() -{ - b2Vec2 gravity; - gravity.Set(0.0f, -10.0f); - m_world = new b2World(gravity); - m_bomb = NULL; - m_textLine = 30; - m_textIncrement = 13; - m_mouseJoint = NULL; - m_pointCount = 0; - - m_destructionListener.test = this; - m_world->SetDestructionListener(&m_destructionListener); - m_world->SetContactListener(this); - m_world->SetDebugDraw(&g_debugDraw); - - m_bombSpawning = false; - - m_stepCount = 0; - - b2BodyDef bodyDef; - m_groundBody = m_world->CreateBody(&bodyDef); - - memset(&m_maxProfile, 0, sizeof(b2Profile)); - memset(&m_totalProfile, 0, sizeof(b2Profile)); -} - -Test::~Test() -{ - // By deleting the world, we delete the bomb, mouse joint, etc. - delete m_world; - m_world = NULL; -} - -void Test::PreSolve(b2Contact* contact, const b2Manifold* oldManifold) -{ - const b2Manifold* manifold = contact->GetManifold(); - - if (manifold->pointCount == 0) - { - return; - } - - b2Fixture* fixtureA = contact->GetFixtureA(); - b2Fixture* fixtureB = contact->GetFixtureB(); - - b2PointState state1[b2_maxManifoldPoints], state2[b2_maxManifoldPoints]; - b2GetPointStates(state1, state2, oldManifold, manifold); - - b2WorldManifold worldManifold; - contact->GetWorldManifold(&worldManifold); - - for (int32 i = 0; i < manifold->pointCount && m_pointCount < k_maxContactPoints; ++i) - { - ContactPoint* cp = m_points + m_pointCount; - cp->fixtureA = fixtureA; - cp->fixtureB = fixtureB; - cp->position = worldManifold.points[i]; - cp->normal = worldManifold.normal; - cp->state = state2[i]; - cp->normalImpulse = manifold->points[i].normalImpulse; - cp->tangentImpulse = manifold->points[i].tangentImpulse; - cp->separation = worldManifold.separations[i]; - ++m_pointCount; - } -} - -void Test::DrawTitle(const char* string) -{ - DrawString(5, 5, string); - m_textLine = int32(26.0f); -} - -class QueryCallback : public b2QueryCallback -{ -public: - QueryCallback(const b2Vec2& point) - { - m_point = point; - m_fixture = NULL; - } - - bool ReportFixture(b2Fixture* fixture) override - { - b2Body* body = fixture->GetBody(); - if (body->GetType() == b2_dynamicBody) - { - bool inside = fixture->TestPoint(m_point); - if (inside) - { - m_fixture = fixture; - - // We are done, terminate the query. - return false; - } - } - - // Continue the query. - return true; - } - - b2Vec2 m_point; - b2Fixture* m_fixture; -}; - -bool Test::MouseDown(const b2Vec2& p) -{ - m_mouseWorld = p; - - if (m_mouseJoint != NULL) - { - return false; - } - - // Make a small box. - b2AABB aabb; - b2Vec2 d; - d.Set(0.001f, 0.001f); - aabb.lowerBound = p - d; - aabb.upperBound = p + d; - - // Query the world for overlapping shapes. - QueryCallback callback(p); - m_world->QueryAABB(&callback, aabb); - - if (callback.m_fixture) - { - float frequencyHz = 5.0f; - float dampingRatio = 0.7f; - - b2Body* body = callback.m_fixture->GetBody(); - b2MouseJointDef jd; - jd.bodyA = m_groundBody; - jd.bodyB = body; - jd.target = p; - jd.maxForce = 1000.0f * body->GetMass(); - b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB); - - m_mouseJoint = (b2MouseJoint*)m_world->CreateJoint(&jd); - body->SetAwake(true); - return true; - } - return false; -} - -void Test::SpawnBomb(const b2Vec2& worldPt) -{ - m_bombSpawnPoint = worldPt; - m_bombSpawning = true; -} - -void Test::CompleteBombSpawn(const b2Vec2& p) -{ - if (m_bombSpawning == false) - { - return; - } - - const float multiplier = 30.0f; - b2Vec2 vel = m_bombSpawnPoint - p; - vel *= multiplier; - LaunchBomb(m_bombSpawnPoint, vel); - m_bombSpawning = false; -} - -void Test::ShiftMouseDown(const b2Vec2& p) -{ - m_mouseWorld = p; - - if (m_mouseJoint != NULL) - { - return; - } - - SpawnBomb(p); -} - -void Test::MouseUp(const b2Vec2& p) -{ - if (m_mouseJoint) - { - m_world->DestroyJoint(m_mouseJoint); - m_mouseJoint = NULL; - } - - if (m_bombSpawning) - { - CompleteBombSpawn(p); - } -} - -void Test::MouseMove(const b2Vec2& p) -{ - m_mouseWorld = p; - - if (m_mouseJoint) - { - m_mouseJoint->SetTarget(p); - } -} - -void Test::LaunchBomb() -{ - b2Vec2 p(RandomFloat(-15.0f, 15.0f), 30.0f); - b2Vec2 v = -5.0f * p; - LaunchBomb(p, v); -} - -void Test::LaunchBomb(const b2Vec2& position, const b2Vec2& velocity) -{ - if (m_bomb) - { - m_world->DestroyBody(m_bomb); - m_bomb = NULL; - } - - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position = position; - bd.bullet = true; - m_bomb = m_world->CreateBody(&bd); - m_bomb->SetLinearVelocity(velocity); - - b2CircleShape circle; - circle.m_radius = 0.3f; - - b2FixtureDef fd; - fd.shape = &circle; - fd.density = 20.0f; - fd.restitution = 0.0f; - - b2Vec2 minV = position - b2Vec2(0.3f, 0.3f); - b2Vec2 maxV = position + b2Vec2(0.3f, 0.3f); - - b2AABB aabb; - aabb.lowerBound = minV; - aabb.upperBound = maxV; - - m_bomb->CreateFixture(&fd); -} - -void Test::Step(Settings& settings) -{ - float timeStep = settings.m_hertz > 0.0f ? 1.0f / settings.m_hertz : float(0.0f); - - if (settings.m_pause) - { - if (settings.m_singleStep) - { - settings.m_singleStep = 0; - } - else - { - timeStep = 0.0f; - } - - DrawString(5, m_textLine, "****PAUSED****"); - } - - uint32 flags = 0; - flags += settings.m_drawShapes * b2Draw::e_shapeBit; - flags += settings.m_drawJoints * b2Draw::e_jointBit; - flags += settings.m_drawAABBs * b2Draw::e_aabbBit; - flags += settings.m_drawCOMs * b2Draw::e_centerOfMassBit; - g_debugDraw.SetFlags(flags); - - m_world->SetAllowSleeping(settings.m_enableSleep); - m_world->SetWarmStarting(settings.m_enableWarmStarting); - m_world->SetContinuousPhysics(settings.m_enableContinuous); - m_world->SetSubStepping(settings.m_enableSubStepping); - - m_pointCount = 0; - - m_world->Step(timeStep, settings.m_velocityIterations, settings.m_positionIterations); - - m_world->DebugDraw(); - - if (timeStep > 0.0f) - { - ++m_stepCount; - } - - if (settings.m_drawStats) - { - int32 bodyCount = m_world->GetBodyCount(); - int32 contactCount = m_world->GetContactCount(); - int32 jointCount = m_world->GetJointCount(); - DrawString(5, m_textLine, "bodies/contacts/joints = %d/%d/%d", bodyCount, contactCount, jointCount); - - int32 proxyCount = m_world->GetProxyCount(); - int32 height = m_world->GetTreeHeight(); - int32 balance = m_world->GetTreeBalance(); - float quality = m_world->GetTreeQuality(); - DrawString(5, m_textLine, "proxies/height/balance/quality = %d/%d/%d/%g", proxyCount, height, balance, quality); - } - - // Track maximum profile times - { - const b2Profile& p = m_world->GetProfile(); - m_maxProfile.step = b2Max(m_maxProfile.step, p.step); - m_maxProfile.collide = b2Max(m_maxProfile.collide, p.collide); - m_maxProfile.solve = b2Max(m_maxProfile.solve, p.solve); - m_maxProfile.solveInit = b2Max(m_maxProfile.solveInit, p.solveInit); - m_maxProfile.solveVelocity = b2Max(m_maxProfile.solveVelocity, p.solveVelocity); - m_maxProfile.solvePosition = b2Max(m_maxProfile.solvePosition, p.solvePosition); - m_maxProfile.solveTOI = b2Max(m_maxProfile.solveTOI, p.solveTOI); - m_maxProfile.broadphase = b2Max(m_maxProfile.broadphase, p.broadphase); - - m_totalProfile.step += p.step; - m_totalProfile.collide += p.collide; - m_totalProfile.solve += p.solve; - m_totalProfile.solveInit += p.solveInit; - m_totalProfile.solveVelocity += p.solveVelocity; - m_totalProfile.solvePosition += p.solvePosition; - m_totalProfile.solveTOI += p.solveTOI; - m_totalProfile.broadphase += p.broadphase; - } - - if (settings.m_drawProfile) - { - const b2Profile& p = m_world->GetProfile(); - - b2Profile aveProfile; - memset(&aveProfile, 0, sizeof(b2Profile)); - if (m_stepCount > 0) - { - float scale = 1.0f / m_stepCount; - aveProfile.step = scale * m_totalProfile.step; - aveProfile.collide = scale * m_totalProfile.collide; - aveProfile.solve = scale * m_totalProfile.solve; - aveProfile.solveInit = scale * m_totalProfile.solveInit; - aveProfile.solveVelocity = scale * m_totalProfile.solveVelocity; - aveProfile.solvePosition = scale * m_totalProfile.solvePosition; - aveProfile.solveTOI = scale * m_totalProfile.solveTOI; - aveProfile.broadphase = scale * m_totalProfile.broadphase; - } - - DrawString(5, m_textLine, "step [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.step, aveProfile.step, - m_maxProfile.step); - DrawString(5, m_textLine, "collide [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.collide, aveProfile.collide, - m_maxProfile.collide); - DrawString(5, m_textLine, "solve [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.solve, aveProfile.solve, - m_maxProfile.solve); - DrawString(5, m_textLine, "solve init [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.solveInit, aveProfile.solveInit, - m_maxProfile.solveInit); - DrawString(5, m_textLine, "solve velocity [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.solveVelocity, - aveProfile.solveVelocity, m_maxProfile.solveVelocity); - DrawString(5, m_textLine, "solve position [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.solvePosition, - aveProfile.solvePosition, m_maxProfile.solvePosition); - DrawString(5, m_textLine, "solveTOI [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.solveTOI, aveProfile.solveTOI, - m_maxProfile.solveTOI); - DrawString(5, m_textLine, "broad-phase [ave] (max) = %5.2f [%6.2f] (%6.2f)", p.broadphase, - aveProfile.broadphase, m_maxProfile.broadphase); - } - - if (m_bombSpawning) - { - b2Color c; - c.Set(0.0f, 0.0f, 1.0f); - g_debugDraw.DrawPoint(m_bombSpawnPoint, 4.0f, c); - - c.Set(0.8f, 0.8f, 0.8f); - g_debugDraw.DrawSegment(m_mouseWorld, m_bombSpawnPoint, c); - } - - if (settings.m_drawContactPoints) - { - const float k_impulseScale = 0.1f; - const float k_axisScale = 0.3f; - - for (int32 i = 0; i < m_pointCount; ++i) - { - ContactPoint* point = m_points + i; - - if (point->state == b2_addState) - { - // Add - g_debugDraw.DrawPoint(point->position, 10.0f, b2Color(0.3f, 0.95f, 0.3f)); - } - else if (point->state == b2_persistState) - { - // Persist - g_debugDraw.DrawPoint(point->position, 5.0f, b2Color(0.3f, 0.3f, 0.95f)); - } - - if (settings.m_drawContactNormals == 1) - { - b2Vec2 p1 = point->position; - b2Vec2 p2 = p1 + k_axisScale * point->normal; - g_debugDraw.DrawSegment(p1, p2, b2Color(0.9f, 0.9f, 0.9f)); - } - else if (settings.m_drawContactImpulse == 1) - { - b2Vec2 p1 = point->position; - b2Vec2 p2 = p1 + k_impulseScale * point->normalImpulse * point->normal; - g_debugDraw.DrawSegment(p1, p2, b2Color(0.9f, 0.9f, 0.3f)); - } - - if (settings.m_drawFrictionImpulse == 1) - { - b2Vec2 tangent = b2Cross(point->normal, 1.0f); - b2Vec2 p1 = point->position; - b2Vec2 p2 = p1 + k_impulseScale * point->tangentImpulse * tangent; - g_debugDraw.DrawSegment(p1, p2, b2Color(0.9f, 0.9f, 0.3f)); - } - } - } -} - -void Test::ShiftOrigin(const b2Vec2& newOrigin) -{ - m_world->ShiftOrigin(newOrigin); -} - -void Test::initShader(void) -{ - // initShader is unsupported -} - -void Test::DrawString(int x, int y, const char* fmt, ...) -{ -#if defined(AX_PLATFORM_PC) - va_list args; - va_start(args, fmt); - auto str = StringUtils::vformat(fmt, args); - va_end(args); - debugString.append(str); - debugString.append("\n"); - labelDebugDraw->setString(debugString); - // labelDebugDraw->setPosition(x, y); -#endif -} - -void Test::DrawString(const b2Vec2& pw, const char* fmt, ...) -{ -#if defined(AX_PLATFORM_PC) - va_list args; - va_start(args, fmt); - auto str = StringUtils::vformat(fmt, args); - va_end(args); - debugString.append(str); - debugString.append("\n"); - labelDebugDraw->setString(debugString); - // labelDebugDraw->setPosition(pw.x, pw.y); -#endif -} - -void Test::DrawAABB(b2AABB* aabb, const b2Color& color) -{ - b2Vec2 p1 = aabb->lowerBound; - b2Vec2 p2 = b2Vec2(aabb->upperBound.x, aabb->lowerBound.y); - b2Vec2 p3 = aabb->upperBound; - b2Vec2 p4 = b2Vec2(aabb->lowerBound.x, aabb->upperBound.y); - - Vec2 verts[] = { - Vec2(p1.x * g_debugDraw.mRatio, p1.y * g_debugDraw.mRatio) + g_debugDraw.debugNodeOffset, - Vec2(p2.x * g_debugDraw.mRatio, p2.y * g_debugDraw.mRatio) + g_debugDraw.debugNodeOffset, - Vec2(p3.x * g_debugDraw.mRatio, p3.y * g_debugDraw.mRatio) + g_debugDraw.debugNodeOffset, - Vec2(p4.x * g_debugDraw.mRatio, p4.y * g_debugDraw.mRatio) + g_debugDraw.debugNodeOffset, - }; - debugDrawNode->drawPolygon(verts, sizeof(verts) / sizeof(verts[0]), - Color4F(color.r / 2, color.g / 2, color.b / 2, 0), 0.4f, - Color4F(color.r, color.g, color.b, color.a)); -} - -void Test::Flush() -{ - // Flush is unsupported -} diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/add_pair.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/add_pair.cpp deleted file mode 100644 index 4465d35f3e01..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/add_pair.cpp +++ /dev/null @@ -1,67 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -class AddPair : public Test -{ -public: - AddPair() - { - m_world->SetGravity(b2Vec2(0.0f, 0.0f)); - { - b2CircleShape shape; - shape.m_p.SetZero(); - shape.m_radius = 0.1f; - - float minX = -6.0f; - float maxX = 0.0f; - float minY = 4.0f; - float maxY = 6.0f; - - for (int32 i = 0; i < 400; ++i) - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position = b2Vec2(RandomFloat(minX, maxX), RandomFloat(minY, maxY)); - b2Body* body = m_world->CreateBody(&bd); - body->CreateFixture(&shape, 0.01f); - } - } - - { - b2PolygonShape shape; - shape.SetAsBox(1.5f, 1.5f); - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(-40.0f, 5.0f); - bd.bullet = true; - b2Body* body = m_world->CreateBody(&bd); - body->CreateFixture(&shape, 1.0f); - body->SetLinearVelocity(b2Vec2(10.0f, 0.0f)); - } - } - - static Test* Create() { return new AddPair; } -}; - -static int testIndex = RegisterTest("Benchmark", "Add Pair", AddPair::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/apply_force.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/apply_force.cpp deleted file mode 100644 index 51239f60e2f3..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/apply_force.cpp +++ /dev/null @@ -1,199 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -// This test shows how to apply forces and torques to a body. -// It also shows how to use the friction joint that can be useful -// for overhead games. -class ApplyForce : public Test -{ -public: - ApplyForce() - { - m_world->SetGravity(b2Vec2(0.0f, 0.0f)); - - const float k_restitution = 0.4f; - - b2Body* ground; - { - b2BodyDef bd; - bd.position.Set(0.0f, 20.0f); - ground = m_world->CreateBody(&bd); - - b2EdgeShape shape; - - b2FixtureDef sd; - sd.shape = &shape; - sd.density = 0.0f; - sd.restitution = k_restitution; - - // Left vertical - shape.SetTwoSided(b2Vec2(-20.0f, -20.0f), b2Vec2(-20.0f, 20.0f)); - ground->CreateFixture(&sd); - - // Right vertical - shape.SetTwoSided(b2Vec2(20.0f, -20.0f), b2Vec2(20.0f, 20.0f)); - ground->CreateFixture(&sd); - - // Top horizontal - shape.SetTwoSided(b2Vec2(-20.0f, 20.0f), b2Vec2(20.0f, 20.0f)); - ground->CreateFixture(&sd); - - // Bottom horizontal - shape.SetTwoSided(b2Vec2(-20.0f, -20.0f), b2Vec2(20.0f, -20.0f)); - ground->CreateFixture(&sd); - } - - { - b2Transform xf1; - xf1.q.Set(0.3524f * b2_pi); - xf1.p = xf1.q.GetXAxis(); - - b2Vec2 vertices[3]; - vertices[0] = b2Mul(xf1, b2Vec2(-1.0f, 0.0f)); - vertices[1] = b2Mul(xf1, b2Vec2(1.0f, 0.0f)); - vertices[2] = b2Mul(xf1, b2Vec2(0.0f, 0.5f)); - - b2PolygonShape poly1; - poly1.Set(vertices, 3); - - b2FixtureDef sd1; - sd1.shape = &poly1; - sd1.density = 2.0f; - - b2Transform xf2; - xf2.q.Set(-0.3524f * b2_pi); - xf2.p = -xf2.q.GetXAxis(); - - vertices[0] = b2Mul(xf2, b2Vec2(-1.0f, 0.0f)); - vertices[1] = b2Mul(xf2, b2Vec2(1.0f, 0.0f)); - vertices[2] = b2Mul(xf2, b2Vec2(0.0f, 0.5f)); - - b2PolygonShape poly2; - poly2.Set(vertices, 3); - - b2FixtureDef sd2; - sd2.shape = &poly2; - sd2.density = 2.0f; - - b2BodyDef bd; - bd.type = b2_dynamicBody; - - bd.position.Set(0.0f, 3.0); - bd.angle = b2_pi; - bd.allowSleep = false; - m_body = m_world->CreateBody(&bd); - m_body->CreateFixture(&sd1); - m_body->CreateFixture(&sd2); - - float gravity = 10.0f; - float I = m_body->GetInertia(); - float mass = m_body->GetMass(); - - // Compute an effective radius that can be used to - // set the max torque for a friction joint - // For a circle: I = 0.5 * m * r * r ==> r = sqrt(2 * I / m) - float radius = b2Sqrt(2.0f * I / mass); - - b2FrictionJointDef jd; - jd.bodyA = ground; - jd.bodyB = m_body; - jd.localAnchorA.SetZero(); - jd.localAnchorB = m_body->GetLocalCenter(); - jd.collideConnected = true; - jd.maxForce = 0.5f * mass * gravity; - jd.maxTorque = 0.2f * mass * radius * gravity; - - m_world->CreateJoint(&jd); - } - - { - b2PolygonShape shape; - shape.SetAsBox(0.5f, 0.5f); - - b2FixtureDef fd; - fd.shape = &shape; - fd.density = 1.0f; - fd.friction = 0.3f; - - for (int i = 0; i < 10; ++i) - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - - bd.position.Set(0.0f, 7.0f + 1.54f * i); - b2Body* body = m_world->CreateBody(&bd); - - body->CreateFixture(&fd); - - float gravity = 10.0f; - float I = body->GetInertia(); - float mass = body->GetMass(); - - // For a circle: I = 0.5 * m * r * r ==> r = sqrt(2 * I / m) - float radius = b2Sqrt(2.0f * I / mass); - - b2FrictionJointDef jd; - jd.localAnchorA.SetZero(); - jd.localAnchorB.SetZero(); - jd.bodyA = ground; - jd.bodyB = body; - jd.collideConnected = true; - jd.maxForce = mass * gravity; - jd.maxTorque = 0.1f * mass * radius * gravity; - - m_world->CreateJoint(&jd); - } - } - } - - void Step(Settings& settings) override - { - DrawString(5, m_textLine, "Forward (W), Turn (A) and (D)"); - - // if (glfwGetKey(g_mainWindow, GLFW_KEY_W) == GLFW_PRESS) - //{ - // b2Vec2 f = m_body->GetWorldVector(b2Vec2(0.0f, -50.0f)); - // b2Vec2 p = m_body->GetWorldPoint(b2Vec2(0.0f, 3.0f)); - // m_body->ApplyForce(f, p, true); - // } - - // if (glfwGetKey(g_mainWindow, GLFW_KEY_A) == GLFW_PRESS) - //{ - // m_body->ApplyTorque(10.0f, true); - // } - - // if (glfwGetKey(g_mainWindow, GLFW_KEY_D) == GLFW_PRESS) - //{ - // m_body->ApplyTorque(-10.0f, true); - // } - - Test::Step(settings); - } - - static Test* Create() { return new ApplyForce; } - - b2Body* m_body; -}; - -static int testIndex = RegisterTest("Forces", "Apply Force", ApplyForce::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/body_types.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/body_types.cpp deleted file mode 100644 index 0de1079826f0..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/body_types.cpp +++ /dev/null @@ -1,158 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -class BodyTypes : public Test -{ -public: - BodyTypes() - { - b2Body* ground = NULL; - { - b2BodyDef bd; - ground = m_world->CreateBody(&bd); - - b2EdgeShape shape; - shape.SetTwoSided(b2Vec2(-20.0f, 0.0f), b2Vec2(20.0f, 0.0f)); - - b2FixtureDef fd; - fd.shape = &shape; - - ground->CreateFixture(&fd); - } - - // Define attachment - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(0.0f, 3.0f); - m_attachment = m_world->CreateBody(&bd); - - b2PolygonShape shape; - shape.SetAsBox(0.5f, 2.0f); - m_attachment->CreateFixture(&shape, 2.0f); - } - - // Define platform - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(-4.0f, 5.0f); - m_platform = m_world->CreateBody(&bd); - - b2PolygonShape shape; - shape.SetAsBox(0.5f, 4.0f, b2Vec2(4.0f, 0.0f), 0.5f * b2_pi); - - b2FixtureDef fd; - fd.shape = &shape; - fd.friction = 0.6f; - fd.density = 2.0f; - m_platform->CreateFixture(&fd); - - b2RevoluteJointDef rjd; - rjd.Initialize(m_attachment, m_platform, b2Vec2(0.0f, 5.0f)); - rjd.maxMotorTorque = 50.0f; - rjd.enableMotor = true; - m_world->CreateJoint(&rjd); - - b2PrismaticJointDef pjd; - pjd.Initialize(ground, m_platform, b2Vec2(0.0f, 5.0f), b2Vec2(1.0f, 0.0f)); - - pjd.maxMotorForce = 1000.0f; - pjd.enableMotor = true; - pjd.lowerTranslation = -10.0f; - pjd.upperTranslation = 10.0f; - pjd.enableLimit = true; - - m_world->CreateJoint(&pjd); - - m_speed = 3.0f; - } - - // Create a payload - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(0.0f, 8.0f); - b2Body* body = m_world->CreateBody(&bd); - - b2PolygonShape shape; - shape.SetAsBox(0.75f, 0.75f); - - b2FixtureDef fd; - fd.shape = &shape; - fd.friction = 0.6f; - fd.density = 2.0f; - - body->CreateFixture(&fd); - } - } - - void Keyboard(int key) override - { - switch (key) - { - case GLFW_KEY_D: - m_platform->SetType(b2_dynamicBody); - break; - - case GLFW_KEY_S: - m_platform->SetType(b2_staticBody); - break; - - case GLFW_KEY_K: - m_platform->SetType(b2_kinematicBody); - m_platform->SetLinearVelocity(b2Vec2(-m_speed, 0.0f)); - m_platform->SetAngularVelocity(0.0f); - break; - } - } - - void Step(Settings& settings) override - { - // Drive the kinematic body. - if (m_platform->GetType() == b2_kinematicBody) - { - b2Vec2 p = m_platform->GetTransform().p; - b2Vec2 v = m_platform->GetLinearVelocity(); - - if ((p.x < -10.0f && v.x < 0.0f) || (p.x > 10.0f && v.x > 0.0f)) - { - v.x = -v.x; - m_platform->SetLinearVelocity(v); - } - } - - Test::Step(settings); - - DrawString(5, m_textLine, "Keys: (d) dynamic, (s) static, (k) kinematic"); - } - - static Test* Create() { return new BodyTypes; } - - b2Body* m_attachment; - b2Body* m_platform; - float m_speed; -}; - -static int testIndex = RegisterTest("Examples", "Body Types", BodyTypes::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/box_stack.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/box_stack.cpp deleted file mode 100644 index 463d399b7f8e..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/box_stack.cpp +++ /dev/null @@ -1,170 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -extern B2_API bool g_blockSolve; - -class BoxStack : public Test -{ -public: - enum - { - e_columnCount = 1, - e_rowCount = 15 - // e_columnCount = 1, - // e_rowCount = 1 - }; - - BoxStack() - { - { - b2BodyDef bd; - b2Body* ground = m_world->CreateBody(&bd); - - b2EdgeShape shape; - shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f)); - ground->CreateFixture(&shape, 0.0f); - - shape.SetTwoSided(b2Vec2(20.0f, 0.0f), b2Vec2(20.0f, 20.0f)); - ground->CreateFixture(&shape, 0.0f); - } - - float xs[5] = {0.0f, -10.0f, -5.0f, 5.0f, 10.0f}; - - for (int32 j = 0; j < e_columnCount; ++j) - { - b2PolygonShape shape; - shape.SetAsBox(0.5f, 0.5f); - - b2FixtureDef fd; - fd.shape = &shape; - fd.density = 1.0f; - fd.friction = 0.3f; - - for (int i = 0; i < e_rowCount; ++i) - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - - int32 n = j * e_rowCount + i; - b2Assert(n < e_rowCount * e_columnCount); - m_indices[n] = n; - bd.userData.pointer = n; - - float x = 0.0f; - // float x = RandomFloat(-0.02f, 0.02f); - // float x = i % 2 == 0 ? -0.01f : 0.01f; - bd.position.Set(xs[j] + x, 0.55f + 1.1f * i); - b2Body* body = m_world->CreateBody(&bd); - - m_bodies[n] = body; - - body->CreateFixture(&fd); - } - } - - m_bullet = NULL; - } - - void Keyboard(int key) override - { - switch (key) - { - case GLFW_KEY_COMMA: - if (m_bullet != NULL) - { - m_world->DestroyBody(m_bullet); - m_bullet = NULL; - } - - { - b2CircleShape shape; - shape.m_radius = 0.25f; - - b2FixtureDef fd; - fd.shape = &shape; - fd.density = 20.0f; - fd.restitution = 0.05f; - - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.bullet = true; - bd.position.Set(-31.0f, 5.0f); - - m_bullet = m_world->CreateBody(&bd); - m_bullet->CreateFixture(&fd); - - m_bullet->SetLinearVelocity(b2Vec2(400.0f, 0.0f)); - } - break; - - case GLFW_KEY_B: - g_blockSolve = !g_blockSolve; - break; - } - } - - void Step(Settings& settings) override - { - Test::Step(settings); - DrawString(5, m_textLine, "Press: (,) to launch a bullet."); - - DrawString(5, m_textLine, "Blocksolve = %d", g_blockSolve); - if (m_stepCount == 300) - { - if (m_bullet != NULL) - { - m_world->DestroyBody(m_bullet); - m_bullet = NULL; - } - - { - b2CircleShape shape; - shape.m_radius = 0.25f; - - b2FixtureDef fd; - fd.shape = &shape; - fd.density = 20.0f; - fd.restitution = 0.05f; - - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.bullet = true; - bd.position.Set(-31.0f, 5.0f); - - m_bullet = m_world->CreateBody(&bd); - m_bullet->CreateFixture(&fd); - - m_bullet->SetLinearVelocity(b2Vec2(400.0f, 0.0f)); - } - } - } - - static Test* Create() { return new BoxStack; } - - b2Body* m_bullet; - b2Body* m_bodies[e_rowCount * e_columnCount]; - int32 m_indices[e_rowCount * e_columnCount]; -}; - -static int testIndex = RegisterTest("Stacking", "Boxes", BoxStack::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/breakable.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/breakable.cpp deleted file mode 100644 index 1128a56ef498..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/breakable.cpp +++ /dev/null @@ -1,154 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -// This is used to test sensor shapes. -class Breakable : public Test -{ -public: - enum - { - e_count = 7 - }; - - Breakable() - { - // Ground body - { - b2BodyDef bd; - b2Body* ground = m_world->CreateBody(&bd); - - b2EdgeShape shape; - shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f)); - ground->CreateFixture(&shape, 0.0f); - } - - // Breakable dynamic body - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(0.0f, 40.0f); - bd.angle = 0.25f * b2_pi; - m_body1 = m_world->CreateBody(&bd); - - m_shape1.SetAsBox(0.5f, 0.5f, b2Vec2(-0.5f, 0.0f), 0.0f); - m_piece1 = m_body1->CreateFixture(&m_shape1, 1.0f); - - m_shape2.SetAsBox(0.5f, 0.5f, b2Vec2(0.5f, 0.0f), 0.0f); - m_piece2 = m_body1->CreateFixture(&m_shape2, 1.0f); - } - - m_break = false; - m_broke = false; - } - - void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse) override - { - if (m_broke) - { - // The body already broke. - return; - } - - // Should the body break? - int32 count = contact->GetManifold()->pointCount; - - float maxImpulse = 0.0f; - for (int32 i = 0; i < count; ++i) - { - maxImpulse = b2Max(maxImpulse, impulse->normalImpulses[i]); - } - - if (maxImpulse > 40.0f) - { - // Flag the body for breaking. - m_break = true; - } - } - - void Break() - { - // Create two bodies from one. - b2Body* body1 = m_piece1->GetBody(); - b2Vec2 center = body1->GetWorldCenter(); - - body1->DestroyFixture(m_piece2); - m_piece2 = NULL; - - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position = body1->GetPosition(); - bd.angle = body1->GetAngle(); - - b2Body* body2 = m_world->CreateBody(&bd); - m_piece2 = body2->CreateFixture(&m_shape2, 1.0f); - - // Compute consistent velocities for new bodies based on - // cached velocity. - b2Vec2 center1 = body1->GetWorldCenter(); - b2Vec2 center2 = body2->GetWorldCenter(); - - b2Vec2 velocity1 = m_velocity + b2Cross(m_angularVelocity, center1 - center); - b2Vec2 velocity2 = m_velocity + b2Cross(m_angularVelocity, center2 - center); - - body1->SetAngularVelocity(m_angularVelocity); - body1->SetLinearVelocity(velocity1); - - body2->SetAngularVelocity(m_angularVelocity); - body2->SetLinearVelocity(velocity2); - } - - void Step(Settings& settings) override - { - if (m_break) - { - Break(); - m_broke = true; - m_break = false; - } - - // Cache velocities to improve movement on breakage. - if (m_broke == false) - { - m_velocity = m_body1->GetLinearVelocity(); - m_angularVelocity = m_body1->GetAngularVelocity(); - } - - Test::Step(settings); - } - - static Test* Create() { return new Breakable; } - - b2Body* m_body1; - b2Vec2 m_velocity; - float m_angularVelocity; - b2PolygonShape m_shape1; - b2PolygonShape m_shape2; - b2Fixture* m_piece1; - b2Fixture* m_piece2; - - bool m_broke; - bool m_break; -}; - -static int testIndex = RegisterTest("Examples", "Breakable", Breakable::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/bridge.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/bridge.cpp deleted file mode 100644 index 8447bd43dd30..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/bridge.cpp +++ /dev/null @@ -1,124 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -class Bridge : public Test -{ -public: - enum - { - e_count = 30 - }; - - Bridge() - { - b2Body* ground = NULL; - { - b2BodyDef bd; - ground = m_world->CreateBody(&bd); - - b2EdgeShape shape; - shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f)); - ground->CreateFixture(&shape, 0.0f); - } - - { - b2PolygonShape shape; - shape.SetAsBox(0.5f, 0.125f); - - b2FixtureDef fd; - fd.shape = &shape; - fd.density = 20.0f; - fd.friction = 0.2f; - - b2RevoluteJointDef jd; - - b2Body* prevBody = ground; - for (int32 i = 0; i < e_count; ++i) - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(-14.5f + 1.0f * i, 5.0f); - b2Body* body = m_world->CreateBody(&bd); - body->CreateFixture(&fd); - - b2Vec2 anchor(-15.0f + 1.0f * i, 5.0f); - jd.Initialize(prevBody, body, anchor); - m_world->CreateJoint(&jd); - - if (i == (e_count >> 1)) - { - m_middle = body; - } - prevBody = body; - } - - b2Vec2 anchor(-15.0f + 1.0f * static_cast(e_count), 5.0f); - jd.Initialize(prevBody, ground, anchor); - m_world->CreateJoint(&jd); - } - - for (int32 i = 0; i < 2; ++i) - { - b2Vec2 vertices[3]; - vertices[0].Set(-0.5f, 0.0f); - vertices[1].Set(0.5f, 0.0f); - vertices[2].Set(0.0f, 1.5f); - - b2PolygonShape shape; - shape.Set(vertices, 3); - - b2FixtureDef fd; - fd.shape = &shape; - fd.density = 1.0f; - - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(-8.0f + 8.0f * i, 12.0f); - b2Body* body = m_world->CreateBody(&bd); - body->CreateFixture(&fd); - } - - for (int32 i = 0; i < 3; ++i) - { - b2CircleShape shape; - shape.m_radius = 0.5f; - - b2FixtureDef fd; - fd.shape = &shape; - fd.density = 1.0f; - - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(-6.0f + 6.0f * i, 10.0f); - b2Body* body = m_world->CreateBody(&bd); - body->CreateFixture(&fd); - } - } - - static Test* Create() { return new Bridge; } - - b2Body* m_middle; -}; - -static int testIndex = RegisterTest("Joints", "Bridge", Bridge::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/bullet_test.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/bullet_test.cpp deleted file mode 100644 index 945887da6f9c..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/bullet_test.cpp +++ /dev/null @@ -1,132 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -class BulletTest : public Test -{ -public: - BulletTest() - { - { - b2BodyDef bd; - bd.position.Set(0.0f, 0.0f); - b2Body* body = m_world->CreateBody(&bd); - - b2EdgeShape edge; - - edge.SetTwoSided(b2Vec2(-10.0f, 0.0f), b2Vec2(10.0f, 0.0f)); - body->CreateFixture(&edge, 0.0f); - - b2PolygonShape shape; - shape.SetAsBox(0.2f, 1.0f, b2Vec2(0.5f, 1.0f), 0.0f); - body->CreateFixture(&shape, 0.0f); - } - - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(0.0f, 4.0f); - - b2PolygonShape box; - box.SetAsBox(2.0f, 0.1f); - - m_body = m_world->CreateBody(&bd); - m_body->CreateFixture(&box, 1.0f); - - box.SetAsBox(0.25f, 0.25f); - - // m_x = RandomFloat(-1.0f, 1.0f); - m_x = 0.20352793f; - bd.position.Set(m_x, 10.0f); - bd.bullet = true; - - m_bullet = m_world->CreateBody(&bd); - m_bullet->CreateFixture(&box, 100.0f); - - m_bullet->SetLinearVelocity(b2Vec2(0.0f, -50.0f)); - } - } - - void Launch() - { - m_body->SetTransform(b2Vec2(0.0f, 4.0f), 0.0f); - m_body->SetLinearVelocity(b2Vec2_zero); - m_body->SetAngularVelocity(0.0f); - - m_x = RandomFloat(-1.0f, 1.0f); - m_bullet->SetTransform(b2Vec2(m_x, 10.0f), 0.0f); - m_bullet->SetLinearVelocity(b2Vec2(0.0f, -50.0f)); - m_bullet->SetAngularVelocity(0.0f); - - extern B2_API int32 b2_gjkCalls, b2_gjkIters, b2_gjkMaxIters; - extern B2_API int32 b2_toiCalls, b2_toiIters, b2_toiMaxIters; - extern B2_API int32 b2_toiRootIters, b2_toiMaxRootIters; - - b2_gjkCalls = 0; - b2_gjkIters = 0; - b2_gjkMaxIters = 0; - - b2_toiCalls = 0; - b2_toiIters = 0; - b2_toiMaxIters = 0; - b2_toiRootIters = 0; - b2_toiMaxRootIters = 0; - } - - void Step(Settings& settings) override - { - Test::Step(settings); - - extern B2_API int32 b2_gjkCalls, b2_gjkIters, b2_gjkMaxIters; - extern B2_API int32 b2_toiCalls, b2_toiIters; - extern B2_API int32 b2_toiRootIters, b2_toiMaxRootIters; - - if (b2_gjkCalls > 0) - { - DrawString(5, m_textLine, "gjk calls = %d, ave gjk iters = %3.1f, max gjk iters = %d", b2_gjkCalls, - b2_gjkIters / float(b2_gjkCalls), b2_gjkMaxIters); - } - - if (b2_toiCalls > 0) - { - DrawString(5, m_textLine, "toi calls = %d, ave toi iters = %3.1f, max toi iters = %d", b2_toiCalls, - b2_toiIters / float(b2_toiCalls), b2_toiMaxRootIters); - - DrawString(5, m_textLine, "ave toi root iters = %3.1f, max toi root iters = %d", - b2_toiRootIters / float(b2_toiCalls), b2_toiMaxRootIters); - } - - if (m_stepCount % 60 == 0) - { - Launch(); - } - } - - static Test* Create() { return new BulletTest; } - - b2Body* m_body; - b2Body* m_bullet; - float m_x; -}; - -static int testIndex = RegisterTest("Continuous", "Bullet Test", BulletTest::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/cantilever.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/cantilever.cpp deleted file mode 100644 index 8a9ffce0b7cd..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/cantilever.cpp +++ /dev/null @@ -1,214 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -// It is difficult to make a cantilever made of links completely rigid with weld joints. -// You will have to use a high number of iterations to make them stiff. -// So why not go ahead and use soft weld joints? They behave like a revolute -// joint with a rotational spring. -class Cantilever : public Test -{ -public: - enum - { - e_count = 8 - }; - - Cantilever() - { - b2Body* ground = NULL; - { - b2BodyDef bd; - ground = m_world->CreateBody(&bd); - - b2EdgeShape shape; - shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f)); - ground->CreateFixture(&shape, 0.0f); - } - - { - b2PolygonShape shape; - shape.SetAsBox(0.5f, 0.125f); - - b2FixtureDef fd; - fd.shape = &shape; - fd.density = 20.0f; - - b2WeldJointDef jd; - - b2Body* prevBody = ground; - for (int32 i = 0; i < e_count; ++i) - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(-14.5f + 1.0f * i, 5.0f); - b2Body* body = m_world->CreateBody(&bd); - body->CreateFixture(&fd); - - b2Vec2 anchor(-15.0f + 1.0f * i, 5.0f); - jd.Initialize(prevBody, body, anchor); - m_world->CreateJoint(&jd); - - prevBody = body; - } - } - - { - b2PolygonShape shape; - shape.SetAsBox(1.0f, 0.125f); - - b2FixtureDef fd; - fd.shape = &shape; - fd.density = 20.0f; - - b2WeldJointDef jd; - float frequencyHz = 5.0f; - float dampingRatio = 0.7f; - - b2Body* prevBody = ground; - for (int32 i = 0; i < 3; ++i) - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(-14.0f + 2.0f * i, 15.0f); - b2Body* body = m_world->CreateBody(&bd); - body->CreateFixture(&fd); - - b2Vec2 anchor(-15.0f + 2.0f * i, 15.0f); - jd.Initialize(prevBody, body, anchor); - b2AngularStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB); - m_world->CreateJoint(&jd); - - prevBody = body; - } - } - - { - b2PolygonShape shape; - shape.SetAsBox(0.5f, 0.125f); - - b2FixtureDef fd; - fd.shape = &shape; - fd.density = 20.0f; - - b2WeldJointDef jd; - - b2Body* prevBody = ground; - for (int32 i = 0; i < e_count; ++i) - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(-4.5f + 1.0f * i, 5.0f); - b2Body* body = m_world->CreateBody(&bd); - body->CreateFixture(&fd); - - if (i > 0) - { - b2Vec2 anchor(-5.0f + 1.0f * i, 5.0f); - jd.Initialize(prevBody, body, anchor); - m_world->CreateJoint(&jd); - } - - prevBody = body; - } - } - - { - b2PolygonShape shape; - shape.SetAsBox(0.5f, 0.125f); - - b2FixtureDef fd; - fd.shape = &shape; - fd.density = 20.0f; - - b2WeldJointDef jd; - float frequencyHz = 8.0f; - float dampingRatio = 0.7f; - - b2Body* prevBody = ground; - for (int32 i = 0; i < e_count; ++i) - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(5.5f + 1.0f * i, 10.0f); - b2Body* body = m_world->CreateBody(&bd); - body->CreateFixture(&fd); - - if (i > 0) - { - b2Vec2 anchor(5.0f + 1.0f * i, 10.0f); - jd.Initialize(prevBody, body, anchor); - - b2AngularStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, prevBody, body); - - m_world->CreateJoint(&jd); - } - - prevBody = body; - } - } - - for (int32 i = 0; i < 2; ++i) - { - b2Vec2 vertices[3]; - vertices[0].Set(-0.5f, 0.0f); - vertices[1].Set(0.5f, 0.0f); - vertices[2].Set(0.0f, 1.5f); - - b2PolygonShape shape; - shape.Set(vertices, 3); - - b2FixtureDef fd; - fd.shape = &shape; - fd.density = 1.0f; - - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(-8.0f + 8.0f * i, 12.0f); - b2Body* body = m_world->CreateBody(&bd); - body->CreateFixture(&fd); - } - - for (int32 i = 0; i < 2; ++i) - { - b2CircleShape shape; - shape.m_radius = 0.5f; - - b2FixtureDef fd; - fd.shape = &shape; - fd.density = 1.0f; - - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(-6.0f + 6.0f * i, 10.0f); - b2Body* body = m_world->CreateBody(&bd); - body->CreateFixture(&fd); - } - } - - static Test* Create() { return new Cantilever; } - - b2Body* m_middle; -}; - -static int testIndex = RegisterTest("Joints", "Cantilever", Cantilever::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/car.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/car.cpp deleted file mode 100644 index 2051641bde4f..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/car.cpp +++ /dev/null @@ -1,286 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto -// Copyright(c) 2021 @aismann; Peter Eismann, Germany; dreifrankensoft - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" -#include "extensions/axmol-ext.h" - -USING_NS_AX_EXT; - -// This is a fun demo that shows off the wheel joint -class Car : public Test -{ -public: - Car() - { - m_speed = 50.0f; - - b2Body* ground = NULL; - { - b2BodyDef bd; - ground = m_world->CreateBody(&bd); - - b2EdgeShape shape; - - b2FixtureDef fd; - fd.shape = &shape; - fd.density = 0.0f; - fd.friction = 0.6f; - - shape.SetTwoSided(b2Vec2(-20.0f, 0.0f), b2Vec2(20.0f, 0.0f)); - ground->CreateFixture(&fd); - - float hs[10] = {0.25f, 1.0f, 4.0f, 0.0f, 0.0f, -1.0f, -2.0f, -2.0f, -1.25f, 0.0f}; - - float x = 20.0f, y1 = 0.0f, dx = 5.0f; - - for (int32 i = 0; i < 10; ++i) - { - float y2 = hs[i]; - shape.SetTwoSided(b2Vec2(x, y1), b2Vec2(x + dx, y2)); - ground->CreateFixture(&fd); - y1 = y2; - x += dx; - } - - for (int32 i = 0; i < 10; ++i) - { - float y2 = hs[i]; - shape.SetTwoSided(b2Vec2(x, y1), b2Vec2(x + dx, y2)); - ground->CreateFixture(&fd); - y1 = y2; - x += dx; - } - - shape.SetTwoSided(b2Vec2(x, 0.0f), b2Vec2(x + 40.0f, 0.0f)); - ground->CreateFixture(&fd); - - x += 80.0f; - shape.SetTwoSided(b2Vec2(x, 0.0f), b2Vec2(x + 40.0f, 0.0f)); - ground->CreateFixture(&fd); - - x += 40.0f; - shape.SetTwoSided(b2Vec2(x, 0.0f), b2Vec2(x + 10.0f, 5.0f)); - ground->CreateFixture(&fd); - - x += 20.0f; - shape.SetTwoSided(b2Vec2(x, 0.0f), b2Vec2(x + 40.0f, 0.0f)); - ground->CreateFixture(&fd); - - x += 40.0f; - shape.SetTwoSided(b2Vec2(x, 0.0f), b2Vec2(x, 20.0f)); - ground->CreateFixture(&fd); - } - - // Teeter - { - b2BodyDef bd; - bd.position.Set(140.0f, 1.0f); - bd.type = b2_dynamicBody; - b2Body* body = m_world->CreateBody(&bd); - - b2PolygonShape box; - box.SetAsBox(10.0f, 0.25f); - body->CreateFixture(&box, 1.0f); - - b2RevoluteJointDef jd; - jd.Initialize(ground, body, body->GetPosition()); - jd.lowerAngle = -8.0f * b2_pi / 180.0f; - jd.upperAngle = 8.0f * b2_pi / 180.0f; - jd.enableLimit = true; - m_world->CreateJoint(&jd); - - body->ApplyAngularImpulse(100.0f, true); - } - - // Bridge - { - int32 N = 20; - b2PolygonShape shape; - shape.SetAsBox(1.0f, 0.125f); - - b2FixtureDef fd; - fd.shape = &shape; - fd.density = 1.0f; - fd.friction = 0.6f; - - b2RevoluteJointDef jd; - - b2Body* prevBody = ground; - for (int32 i = 0; i < N; ++i) - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(161.0f + 2.0f * i, -0.125f); - b2Body* body = m_world->CreateBody(&bd); - body->CreateFixture(&fd); - - b2Vec2 anchor(160.0f + 2.0f * i, -0.125f); - jd.Initialize(prevBody, body, anchor); - m_world->CreateJoint(&jd); - - prevBody = body; - } - - b2Vec2 anchor(160.0f + 2.0f * N, -0.125f); - jd.Initialize(prevBody, ground, anchor); - m_world->CreateJoint(&jd); - } - - // Boxes - { - b2PolygonShape box; - box.SetAsBox(0.5f, 0.5f); - - b2Body* body = NULL; - b2BodyDef bd; - bd.type = b2_dynamicBody; - - bd.position.Set(230.0f, 0.5f); - body = m_world->CreateBody(&bd); - body->CreateFixture(&box, 0.5f); - - bd.position.Set(230.0f, 1.5f); - body = m_world->CreateBody(&bd); - body->CreateFixture(&box, 0.5f); - - bd.position.Set(230.0f, 2.5f); - body = m_world->CreateBody(&bd); - body->CreateFixture(&box, 0.5f); - - bd.position.Set(230.0f, 3.5f); - body = m_world->CreateBody(&bd); - body->CreateFixture(&box, 0.5f); - - bd.position.Set(230.0f, 4.5f); - body = m_world->CreateBody(&bd); - body->CreateFixture(&box, 0.5f); - } - - // Car - { - b2PolygonShape chassis; - b2Vec2 vertices[8]; - vertices[0].Set(-1.5f, -0.5f); - vertices[1].Set(1.5f, -0.5f); - vertices[2].Set(1.5f, 0.0f); - vertices[3].Set(0.0f, 0.9f); - vertices[4].Set(-1.15f, 0.9f); - vertices[5].Set(-1.5f, 0.2f); - chassis.Set(vertices, 6); - - b2CircleShape circle; - circle.m_radius = 0.4f; - - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(0.0f, 1.0f); - m_car = m_world->CreateBody(&bd); - m_car->CreateFixture(&chassis, 1.0f); - - b2FixtureDef fd; - fd.shape = &circle; - fd.density = 1.0f; - fd.friction = 0.9f; - - bd.position.Set(-1.0f, 0.35f); - m_wheel1 = m_world->CreateBody(&bd); - m_wheel1->CreateFixture(&fd); - - bd.position.Set(1.0f, 0.4f); - m_wheel2 = m_world->CreateBody(&bd); - m_wheel2->CreateFixture(&fd); - - b2WheelJointDef jd; - b2Vec2 axis(0.0f, 1.0f); - - float mass1 = m_wheel1->GetMass(); - float mass2 = m_wheel2->GetMass(); - - float hertz = 4.0f; - float dampingRatio = 0.7f; - float omega = 2.0f * b2_pi * hertz; - - jd.Initialize(m_car, m_wheel1, m_wheel1->GetPosition(), axis); - jd.motorSpeed = 0.0f; - jd.maxMotorTorque = 20.0f; - jd.enableMotor = true; - jd.stiffness = mass1 * omega * omega; - jd.damping = 2.0f * mass1 * dampingRatio * omega; - jd.lowerTranslation = -0.25f; - jd.upperTranslation = 0.25f; - jd.enableLimit = true; - m_spring1 = (b2WheelJoint*)m_world->CreateJoint(&jd); - - jd.Initialize(m_car, m_wheel2, m_wheel2->GetPosition(), axis); - jd.motorSpeed = 0.0f; - jd.maxMotorTorque = 10.0f; - jd.enableMotor = false; - jd.stiffness = mass2 * omega * omega; - jd.damping = 2.0f * mass2 * dampingRatio * omega; - jd.lowerTranslation = -0.25f; - jd.upperTranslation = 0.25f; - jd.enableLimit = true; - m_spring2 = (b2WheelJoint*)m_world->CreateJoint(&jd); - } - } - - void Keyboard(int key) override - { - switch (key) - { - case GLFW_KEY_A: - m_spring1->SetMotorSpeed(m_speed); - break; - - case GLFW_KEY_S: - m_spring1->SetMotorSpeed(0.0f); - break; - - case GLFW_KEY_D: - m_spring1->SetMotorSpeed(-m_speed); - break; - } - } - - void Step(Settings& settings) override - { - DrawString(5, m_textLine, "Keys: left = a, brake = s, right = d, hz down = q, hz up = e"); - - // g_camera.m_center.x = m_car->GetPosition().x; - g_debugDraw.debugNodeOffset.x += m_car->GetPosition().x; - - Test::Step(settings); - } - - static Test* Create() { return new Car; } - - b2Body* m_car; - b2Body* m_wheel1; - b2Body* m_wheel2; - - float m_speed; - b2WheelJoint* m_spring1; - b2WheelJoint* m_spring2; -}; - -static int testIndex = RegisterTest("Examples", "Car", Car::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/chain.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/chain.cpp deleted file mode 100644 index 3520b89330ca..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/chain.cpp +++ /dev/null @@ -1,89 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -#define TEST_BAD_BODY 0 - -class Chain : public Test -{ -public: - Chain() - { - b2Body* ground = NULL; - { - b2BodyDef bd; - ground = m_world->CreateBody(&bd); - - b2EdgeShape shape; - shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f)); - ground->CreateFixture(&shape, 0.0f); - } - - { - b2PolygonShape shape; - shape.SetAsBox(0.6f, 0.125f); - - b2FixtureDef fd; - fd.shape = &shape; - fd.density = 20.0f; - fd.friction = 0.2f; - - b2RevoluteJointDef jd; - jd.collideConnected = false; - - const float y = 25.0f; - b2Body* prevBody = ground; - for (int32 i = 0; i < 30; ++i) - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(0.5f + i, y); - b2Body* body = m_world->CreateBody(&bd); - -#if TEST_BAD_BODY == 1 - if (i == 10) - { - // Test zero density dynamic body - fd.density = 0.0f; - } - else - { - fd.density = 20.0f; - } -#endif - - body->CreateFixture(&fd); - - b2Vec2 anchor(float(i), y); - jd.Initialize(prevBody, body, anchor); - m_world->CreateJoint(&jd); - - prevBody = body; - } - } - } - - static Test* Create() { return new Chain; } -}; - -static int testIndex = RegisterTest("Joints", "Chain", Chain::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/chain_problem.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/chain_problem.cpp deleted file mode 100644 index a9a48a87301d..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/chain_problem.cpp +++ /dev/null @@ -1,89 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -class ChainProblem : public Test -{ -public: - ChainProblem() - { - { - b2Vec2 g(0.0f, -10.0f); - m_world->SetGravity(g); - b2Body** bodies = (b2Body**)b2Alloc(2 * sizeof(b2Body*)); - b2Joint** joints = (b2Joint**)b2Alloc(0 * sizeof(b2Joint*)); - { - b2BodyDef bd; - bd.type = b2BodyType(0); - bodies[0] = m_world->CreateBody(&bd); - - { - b2FixtureDef fd; - - b2Vec2 v1(0.0f, 1.0f); - b2Vec2 v2(0.0f, 0.0f); - b2Vec2 v3(4.0f, 0.0f); - - b2EdgeShape shape; - shape.SetTwoSided(v1, v2); - bodies[0]->CreateFixture(&shape, 0.0f); - - shape.SetTwoSided(v2, v3); - bodies[0]->CreateFixture(&shape, 0.0f); - } - } - { - b2BodyDef bd; - bd.type = b2BodyType(2); - // bd.position.Set(6.033980250358582e-01f, 3.028350114822388e+00f); - bd.position.Set(1.0f, 3.0f); - bodies[1] = m_world->CreateBody(&bd); - - { - b2FixtureDef fd; - fd.friction = 0.2f; - fd.density = 10.0f; - b2PolygonShape shape; - b2Vec2 vs[8]; - vs[0].Set(0.5f, -3.0f); - vs[1].Set(0.5f, 3.0f); - vs[2].Set(-0.5f, 3.0f); - vs[3].Set(-0.5f, -3.0f); - shape.Set(vs, 4); - - fd.shape = &shape; - - bodies[1]->CreateFixture(&fd); - } - } - b2Free(joints); - b2Free(bodies); - joints = NULL; - bodies = NULL; - } - } - - static Test* Create() { return new ChainProblem; } -}; - -static int testIndex = RegisterTest("Bugs", "Chain Problem", ChainProblem::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/character_collision.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/character_collision.cpp deleted file mode 100644 index 42f9f81d6f98..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/character_collision.cpp +++ /dev/null @@ -1,252 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -/// This is a test of typical character collision scenarios. This does not -/// show how you should implement a character in your application. -/// Instead this is used to test smooth collision on edge chains. -class CharacterCollision : public Test -{ -public: - CharacterCollision() - { - // Ground body - { - b2BodyDef bd; - b2Body* ground = m_world->CreateBody(&bd); - - b2EdgeShape shape; - shape.SetTwoSided(b2Vec2(-20.0f, 0.0f), b2Vec2(20.0f, 0.0f)); - ground->CreateFixture(&shape, 0.0f); - } - - // Collinear edges with no adjacency information. - // This shows the problematic case where a box shape can hit - // an internal vertex. - { - b2BodyDef bd; - b2Body* ground = m_world->CreateBody(&bd); - - b2EdgeShape shape; - shape.SetTwoSided(b2Vec2(-8.0f, 1.0f), b2Vec2(-6.0f, 1.0f)); - ground->CreateFixture(&shape, 0.0f); - shape.SetTwoSided(b2Vec2(-6.0f, 1.0f), b2Vec2(-4.0f, 1.0f)); - ground->CreateFixture(&shape, 0.0f); - shape.SetTwoSided(b2Vec2(-4.0f, 1.0f), b2Vec2(-2.0f, 1.0f)); - ground->CreateFixture(&shape, 0.0f); - } - - // Chain shape - { - b2BodyDef bd; - bd.angle = 0.25f * b2_pi; - b2Body* ground = m_world->CreateBody(&bd); - - b2Vec2 vs[4]; - vs[0].Set(5.0f, 7.0f); - vs[1].Set(6.0f, 8.0f); - vs[2].Set(7.0f, 8.0f); - vs[3].Set(8.0f, 7.0f); - b2ChainShape shape; - shape.CreateLoop(vs, 4); - ground->CreateFixture(&shape, 0.0f); - } - - // Square tiles. This shows that adjacency shapes may - // have non-smooth collision. There is no solution - // to this problem. - { - b2BodyDef bd; - b2Body* ground = m_world->CreateBody(&bd); - - b2PolygonShape shape; - shape.SetAsBox(1.0f, 1.0f, b2Vec2(4.0f, 3.0f), 0.0f); - ground->CreateFixture(&shape, 0.0f); - shape.SetAsBox(1.0f, 1.0f, b2Vec2(6.0f, 3.0f), 0.0f); - ground->CreateFixture(&shape, 0.0f); - shape.SetAsBox(1.0f, 1.0f, b2Vec2(8.0f, 3.0f), 0.0f); - ground->CreateFixture(&shape, 0.0f); - } - - // Square made from an edge loop. Collision should be smooth. - { - b2BodyDef bd; - b2Body* ground = m_world->CreateBody(&bd); - - b2Vec2 vs[4]; - vs[0].Set(-1.0f, 3.0f); - vs[1].Set(1.0f, 3.0f); - vs[2].Set(1.0f, 5.0f); - vs[3].Set(-1.0f, 5.0f); - b2ChainShape shape; - shape.CreateLoop(vs, 4); - ground->CreateFixture(&shape, 0.0f); - } - - // Edge loop. Collision should be smooth. - { - b2BodyDef bd; - bd.position.Set(-10.0f, 4.0f); - b2Body* ground = m_world->CreateBody(&bd); - - b2Vec2 vs[10]; - vs[0].Set(0.0f, 0.0f); - vs[1].Set(6.0f, 0.0f); - vs[2].Set(6.0f, 2.0f); - vs[3].Set(4.0f, 1.0f); - vs[4].Set(2.0f, 2.0f); - vs[5].Set(0.0f, 2.0f); - vs[6].Set(-2.0f, 2.0f); - vs[7].Set(-4.0f, 3.0f); - vs[8].Set(-6.0f, 2.0f); - vs[9].Set(-6.0f, 0.0f); - b2ChainShape shape; - shape.CreateLoop(vs, 10); - ground->CreateFixture(&shape, 0.0f); - } - - // Square character 1 - { - b2BodyDef bd; - bd.position.Set(-3.0f, 8.0f); - bd.type = b2_dynamicBody; - bd.fixedRotation = true; - bd.allowSleep = false; - - b2Body* body = m_world->CreateBody(&bd); - - b2PolygonShape shape; - shape.SetAsBox(0.5f, 0.5f); - - b2FixtureDef fd; - fd.shape = &shape; - fd.density = 20.0f; - body->CreateFixture(&fd); - } - - // Square character 2 - { - b2BodyDef bd; - bd.position.Set(-5.0f, 5.0f); - bd.type = b2_dynamicBody; - bd.fixedRotation = true; - bd.allowSleep = false; - - b2Body* body = m_world->CreateBody(&bd); - - b2PolygonShape shape; - shape.SetAsBox(0.25f, 0.25f); - - b2FixtureDef fd; - fd.shape = &shape; - fd.density = 20.0f; - body->CreateFixture(&fd); - } - - // Hexagon character - { - b2BodyDef bd; - bd.position.Set(-5.0f, 8.0f); - bd.type = b2_dynamicBody; - bd.fixedRotation = true; - bd.allowSleep = false; - - b2Body* body = m_world->CreateBody(&bd); - - float angle = 0.0f; - float delta = b2_pi / 3.0f; - b2Vec2 vertices[6]; - for (int32 i = 0; i < 6; ++i) - { - vertices[i].Set(0.5f * cosf(angle), 0.5f * sinf(angle)); - angle += delta; - } - - b2PolygonShape shape; - shape.Set(vertices, 6); - - b2FixtureDef fd; - fd.shape = &shape; - fd.density = 20.0f; - body->CreateFixture(&fd); - } - - // Circle character - { - b2BodyDef bd; - bd.position.Set(3.0f, 5.0f); - bd.type = b2_dynamicBody; - bd.fixedRotation = true; - bd.allowSleep = false; - - b2Body* body = m_world->CreateBody(&bd); - - b2CircleShape shape; - shape.m_radius = 0.5f; - - b2FixtureDef fd; - fd.shape = &shape; - fd.density = 20.0f; - body->CreateFixture(&fd); - } - - // Circle character - { - b2BodyDef bd; - bd.position.Set(-7.0f, 6.0f); - bd.type = b2_dynamicBody; - bd.allowSleep = false; - - m_character = m_world->CreateBody(&bd); - - b2CircleShape shape; - shape.m_radius = 0.25f; - - b2FixtureDef fd; - fd.shape = &shape; - fd.density = 20.0f; - fd.friction = 1.0f; - m_character->CreateFixture(&fd); - } - } - - void Step(Settings& settings) override - { - b2Vec2 v = m_character->GetLinearVelocity(); - v.x = -5.0f; - m_character->SetLinearVelocity(v); - - Test::Step(settings); - DrawString(5, m_textLine, "This tests various character collision shapes."); - - DrawString(5, m_textLine, "Limitation: square and hexagon can snag on aligned boxes."); - - DrawString(5, m_textLine, "Feature: edge chains have smooth collision inside and out."); - } - - static Test* Create() { return new CharacterCollision; } - - b2Body* m_character; -}; - -static int testIndex = RegisterTest("Examples", "Character Collision", CharacterCollision::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/circle_stack.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/circle_stack.cpp deleted file mode 100644 index d642a12d33e2..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/circle_stack.cpp +++ /dev/null @@ -1,85 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -class CircleStack : public Test -{ -public: - enum - { - e_count = 10 - }; - - CircleStack() - { - { - b2BodyDef bd; - b2Body* ground = m_world->CreateBody(&bd); - - b2EdgeShape shape; - shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f)); - ground->CreateFixture(&shape, 0.0f); - } - - { - b2CircleShape shape; - shape.m_radius = 1.0f; - - for (int32 i = 0; i < e_count; ++i) - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(0.0, 4.0f + 3.0f * i); - - m_bodies[i] = m_world->CreateBody(&bd); - - m_bodies[i]->CreateFixture(&shape, 1.0f); - - m_bodies[i]->SetLinearVelocity(b2Vec2(0.0f, -50.0f)); - } - } - } - - void Step(Settings& settings) override - { - Test::Step(settings); - - // for (int32 i = 0; i < e_count; ++i) - //{ - // printf("%g ", m_bodies[i]->GetWorldCenter().y); - // } - - // for (int32 i = 0; i < e_count; ++i) - //{ - // printf("%g ", m_bodies[i]->GetLinearVelocity().y); - // } - - // printf("\n"); - } - - static Test* Create() { return new CircleStack; } - - b2Body* m_bodies[e_count]; -}; - -static int testIndex = RegisterTest("Stacking", "Circles", CircleStack::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/collision_filtering.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/collision_filtering.cpp deleted file mode 100644 index 65acb3866862..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/collision_filtering.cpp +++ /dev/null @@ -1,176 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -// This is a test of collision filtering. -// There is a triangle, a box, and a circle. -// There are 6 shapes. 3 large and 3 small. -// The 3 small ones always collide. -// The 3 large ones never collide. -// The boxes don't collide with triangles (except if both are small). -const int16 k_smallGroup = 1; -const int16 k_largeGroup = -1; - -const uint16 k_triangleCategory = 0x0002; -const uint16 k_boxCategory = 0x0004; -const uint16 k_circleCategory = 0x0008; - -const uint16 k_triangleMask = 0xFFFF; -const uint16 k_boxMask = 0xFFFF ^ k_triangleCategory; -const uint16 k_circleMask = 0xFFFF; - -class CollisionFiltering : public Test -{ -public: - CollisionFiltering() - { - // Ground body - { - b2EdgeShape shape; - shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f)); - - b2FixtureDef sd; - sd.shape = &shape; - sd.friction = 0.3f; - - b2BodyDef bd; - b2Body* ground = m_world->CreateBody(&bd); - ground->CreateFixture(&sd); - } - - // Small triangle - b2Vec2 vertices[3]; - vertices[0].Set(-1.0f, 0.0f); - vertices[1].Set(1.0f, 0.0f); - vertices[2].Set(0.0f, 2.0f); - b2PolygonShape polygon; - polygon.Set(vertices, 3); - - b2FixtureDef triangleShapeDef; - triangleShapeDef.shape = &polygon; - triangleShapeDef.density = 1.0f; - - triangleShapeDef.filter.groupIndex = k_smallGroup; - triangleShapeDef.filter.categoryBits = k_triangleCategory; - triangleShapeDef.filter.maskBits = k_triangleMask; - - b2BodyDef triangleBodyDef; - triangleBodyDef.type = b2_dynamicBody; - triangleBodyDef.position.Set(-5.0f, 2.0f); - - b2Body* body1 = m_world->CreateBody(&triangleBodyDef); - body1->CreateFixture(&triangleShapeDef); - - // Large triangle (recycle definitions) - vertices[0] *= 2.0f; - vertices[1] *= 2.0f; - vertices[2] *= 2.0f; - polygon.Set(vertices, 3); - triangleShapeDef.filter.groupIndex = k_largeGroup; - triangleBodyDef.position.Set(-5.0f, 6.0f); - triangleBodyDef.fixedRotation = true; // look at me! - - b2Body* body2 = m_world->CreateBody(&triangleBodyDef); - body2->CreateFixture(&triangleShapeDef); - - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(-5.0f, 10.0f); - b2Body* body = m_world->CreateBody(&bd); - - b2PolygonShape p; - p.SetAsBox(0.5f, 1.0f); - body->CreateFixture(&p, 1.0f); - - b2PrismaticJointDef jd; - jd.bodyA = body2; - jd.bodyB = body; - jd.enableLimit = true; - jd.localAnchorA.Set(0.0f, 4.0f); - jd.localAnchorB.SetZero(); - jd.localAxisA.Set(0.0f, 1.0f); - jd.lowerTranslation = -1.0f; - jd.upperTranslation = 1.0f; - - m_world->CreateJoint(&jd); - } - - // Small box - polygon.SetAsBox(1.0f, 0.5f); - b2FixtureDef boxShapeDef; - boxShapeDef.shape = &polygon; - boxShapeDef.density = 1.0f; - boxShapeDef.restitution = 0.1f; - - boxShapeDef.filter.groupIndex = k_smallGroup; - boxShapeDef.filter.categoryBits = k_boxCategory; - boxShapeDef.filter.maskBits = k_boxMask; - - b2BodyDef boxBodyDef; - boxBodyDef.type = b2_dynamicBody; - boxBodyDef.position.Set(0.0f, 2.0f); - - b2Body* body3 = m_world->CreateBody(&boxBodyDef); - body3->CreateFixture(&boxShapeDef); - - // Large box (recycle definitions) - polygon.SetAsBox(2.0f, 1.0f); - boxShapeDef.filter.groupIndex = k_largeGroup; - boxBodyDef.position.Set(0.0f, 6.0f); - - b2Body* body4 = m_world->CreateBody(&boxBodyDef); - body4->CreateFixture(&boxShapeDef); - - // Small circle - b2CircleShape circle; - circle.m_radius = 1.0f; - - b2FixtureDef circleShapeDef; - circleShapeDef.shape = &circle; - circleShapeDef.density = 1.0f; - - circleShapeDef.filter.groupIndex = k_smallGroup; - circleShapeDef.filter.categoryBits = k_circleCategory; - circleShapeDef.filter.maskBits = k_circleMask; - - b2BodyDef circleBodyDef; - circleBodyDef.type = b2_dynamicBody; - circleBodyDef.position.Set(5.0f, 2.0f); - - b2Body* body5 = m_world->CreateBody(&circleBodyDef); - body5->CreateFixture(&circleShapeDef); - - // Large circle - circle.m_radius *= 2.0f; - circleShapeDef.filter.groupIndex = k_largeGroup; - circleBodyDef.position.Set(5.0f, 6.0f); - - b2Body* body6 = m_world->CreateBody(&circleBodyDef); - body6->CreateFixture(&circleShapeDef); - } - - static Test* Create() { return new CollisionFiltering; } -}; - -static int testIndex = RegisterTest("Examples", "Collision Filtering", CollisionFiltering::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/collision_processing.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/collision_processing.cpp deleted file mode 100644 index f31db91a3b7d..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/collision_processing.cpp +++ /dev/null @@ -1,189 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -#include - -// This test shows collision processing and tests -// deferred body destruction. -class CollisionProcessing : public Test -{ -public: - CollisionProcessing() - { - // Ground body - { - b2EdgeShape shape; - shape.SetTwoSided(b2Vec2(-50.0f, 0.0f), b2Vec2(50.0f, 0.0f)); - - b2FixtureDef sd; - sd.shape = &shape; - ; - - b2BodyDef bd; - b2Body* ground = m_world->CreateBody(&bd); - ground->CreateFixture(&sd); - } - - float xLo = -5.0f, xHi = 5.0f; - float yLo = 2.0f, yHi = 35.0f; - - // Small triangle - b2Vec2 vertices[3]; - vertices[0].Set(-1.0f, 0.0f); - vertices[1].Set(1.0f, 0.0f); - vertices[2].Set(0.0f, 2.0f); - - b2PolygonShape polygon; - polygon.Set(vertices, 3); - - b2FixtureDef triangleShapeDef; - triangleShapeDef.shape = &polygon; - triangleShapeDef.density = 1.0f; - - b2BodyDef triangleBodyDef; - triangleBodyDef.type = b2_dynamicBody; - triangleBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi)); - - b2Body* body1 = m_world->CreateBody(&triangleBodyDef); - body1->CreateFixture(&triangleShapeDef); - - // Large triangle (recycle definitions) - vertices[0] *= 2.0f; - vertices[1] *= 2.0f; - vertices[2] *= 2.0f; - polygon.Set(vertices, 3); - - triangleBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi)); - - b2Body* body2 = m_world->CreateBody(&triangleBodyDef); - body2->CreateFixture(&triangleShapeDef); - - // Small box - polygon.SetAsBox(1.0f, 0.5f); - - b2FixtureDef boxShapeDef; - boxShapeDef.shape = &polygon; - boxShapeDef.density = 1.0f; - - b2BodyDef boxBodyDef; - boxBodyDef.type = b2_dynamicBody; - boxBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi)); - - b2Body* body3 = m_world->CreateBody(&boxBodyDef); - body3->CreateFixture(&boxShapeDef); - - // Large box (recycle definitions) - polygon.SetAsBox(2.0f, 1.0f); - boxBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi)); - - b2Body* body4 = m_world->CreateBody(&boxBodyDef); - body4->CreateFixture(&boxShapeDef); - - // Small circle - b2CircleShape circle; - circle.m_radius = 1.0f; - - b2FixtureDef circleShapeDef; - circleShapeDef.shape = &circle; - circleShapeDef.density = 1.0f; - - b2BodyDef circleBodyDef; - circleBodyDef.type = b2_dynamicBody; - circleBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi)); - - b2Body* body5 = m_world->CreateBody(&circleBodyDef); - body5->CreateFixture(&circleShapeDef); - - // Large circle - circle.m_radius *= 2.0f; - circleBodyDef.position.Set(RandomFloat(xLo, xHi), RandomFloat(yLo, yHi)); - - b2Body* body6 = m_world->CreateBody(&circleBodyDef); - body6->CreateFixture(&circleShapeDef); - } - - void Step(Settings& settings) override - { - Test::Step(settings); - - // We are going to destroy some bodies according to contact - // points. We must buffer the bodies that should be destroyed - // because they may belong to multiple contact points. - const int32 k_maxNuke = 6; - b2Body* nuke[k_maxNuke]; - int32 nukeCount = 0; - - // Traverse the contact results. Destroy bodies that - // are touching heavier bodies. - for (int32 i = 0; i < m_pointCount; ++i) - { - ContactPoint* point = m_points + i; - - b2Body* body1 = point->fixtureA->GetBody(); - b2Body* body2 = point->fixtureB->GetBody(); - float mass1 = body1->GetMass(); - float mass2 = body2->GetMass(); - - if (mass1 > 0.0f && mass2 > 0.0f) - { - if (mass2 > mass1) - { - nuke[nukeCount++] = body1; - } - else - { - nuke[nukeCount++] = body2; - } - - if (nukeCount == k_maxNuke) - { - break; - } - } - } - - // Sort the nuke array to group duplicates. - std::sort(nuke, nuke + nukeCount); - - // Destroy the bodies, skipping duplicates. - int32 i = 0; - while (i < nukeCount) - { - b2Body* b = nuke[i++]; - while (i < nukeCount && nuke[i] == b) - { - ++i; - } - - if (b != m_bomb) - { - m_world->DestroyBody(b); - } - } - } - - static Test* Create() { return new CollisionProcessing; } -}; - -static int testIndex = RegisterTest("Examples", "Collision Processing", CollisionProcessing::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/compound_shapes.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/compound_shapes.cpp deleted file mode 100644 index bb3d7579fbcc..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/compound_shapes.cpp +++ /dev/null @@ -1,224 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" -#include "ImGui/ImGuiPresenter.h" - -class CompoundShapes : public Test -{ -public: - CompoundShapes() - { - { - b2BodyDef bd; - bd.position.Set(0.0f, 0.0f); - b2Body* body = m_world->CreateBody(&bd); - - b2EdgeShape shape; - shape.SetTwoSided(b2Vec2(50.0f, 0.0f), b2Vec2(-50.0f, 0.0f)); - - body->CreateFixture(&shape, 0.0f); - } - - // Table 1 - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(-15.0f, 1.0f); - m_table1 = m_world->CreateBody(&bd); - - b2PolygonShape top; - top.SetAsBox(3.0f, 0.5f, b2Vec2(0.0f, 3.5f), 0.0f); - - b2PolygonShape leftLeg; - leftLeg.SetAsBox(0.5f, 1.5f, b2Vec2(-2.5f, 1.5f), 0.0f); - - b2PolygonShape rightLeg; - rightLeg.SetAsBox(0.5f, 1.5f, b2Vec2(2.5f, 1.5f), 0.0f); - - m_table1->CreateFixture(&top, 2.0f); - m_table1->CreateFixture(&leftLeg, 2.0f); - m_table1->CreateFixture(&rightLeg, 2.0f); - } - - // Table 2 - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(-5.0f, 1.0f); - m_table2 = m_world->CreateBody(&bd); - - b2PolygonShape top; - top.SetAsBox(3.0f, 0.5f, b2Vec2(0.0f, 3.5f), 0.0f); - - b2PolygonShape leftLeg; - leftLeg.SetAsBox(0.5f, 2.0f, b2Vec2(-2.5f, 2.0f), 0.0f); - - b2PolygonShape rightLeg; - rightLeg.SetAsBox(0.5f, 2.0f, b2Vec2(2.5f, 2.0f), 0.0f); - - m_table2->CreateFixture(&top, 2.0f); - m_table2->CreateFixture(&leftLeg, 2.0f); - m_table2->CreateFixture(&rightLeg, 2.0f); - } - - // Spaceship 1 - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(5.0f, 1.0f); - m_ship1 = m_world->CreateBody(&bd); - - b2Vec2 vertices[3]; - - b2PolygonShape left; - vertices[0].Set(-2.0f, 0.0f); - vertices[1].Set(0.0f, 4.0f / 3.0f); - vertices[2].Set(0.0f, 4.0f); - left.Set(vertices, 3); - - b2PolygonShape right; - vertices[0].Set(2.0f, 0.0f); - vertices[1].Set(0.0f, 4.0f / 3.0f); - vertices[2].Set(0.0f, 4.0f); - right.Set(vertices, 3); - - m_ship1->CreateFixture(&left, 2.0f); - m_ship1->CreateFixture(&right, 2.0f); - } - - // Spaceship 2 - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(15.0f, 1.0f); - m_ship2 = m_world->CreateBody(&bd); - - b2Vec2 vertices[3]; - - b2PolygonShape left; - vertices[0].Set(-2.0f, 0.0f); - vertices[1].Set(1.0f, 2.0f); - vertices[2].Set(0.0f, 4.0f); - left.Set(vertices, 3); - - b2PolygonShape right; - vertices[0].Set(2.0f, 0.0f); - vertices[1].Set(-1.0f, 2.0f); - vertices[2].Set(0.0f, 4.0f); - right.Set(vertices, 3); - - m_ship2->CreateFixture(&left, 2.0f); - m_ship2->CreateFixture(&right, 2.0f); - } - } - - void Spawn() - { - // Table 1 obstruction - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position = m_table1->GetPosition(); - bd.angle = m_table1->GetAngle(); - - b2Body* body = m_world->CreateBody(&bd); - - b2PolygonShape box; - box.SetAsBox(4.0f, 0.1f, b2Vec2(0.0f, 3.0f), 0.0f); - - body->CreateFixture(&box, 2.0f); - } - - // Table 2 obstruction - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position = m_table2->GetPosition(); - bd.angle = m_table2->GetAngle(); - - b2Body* body = m_world->CreateBody(&bd); - - b2PolygonShape box; - box.SetAsBox(4.0f, 0.1f, b2Vec2(0.0f, 3.0f), 0.0f); - - body->CreateFixture(&box, 2.0f); - } - - // Ship 1 obstruction - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position = m_ship1->GetPosition(); - bd.angle = m_ship1->GetAngle(); - bd.gravityScale = 0.0f; - - b2Body* body = m_world->CreateBody(&bd); - - b2CircleShape circle; - circle.m_radius = 0.5f; - circle.m_p.Set(0.0f, 2.0f); - - body->CreateFixture(&circle, 2.0f); - } - - // Ship 2 obstruction - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position = m_ship2->GetPosition(); - bd.angle = m_ship2->GetAngle(); - bd.gravityScale = 0.0f; - - b2Body* body = m_world->CreateBody(&bd); - - b2CircleShape circle; - circle.m_radius = 0.5f; - circle.m_p.Set(0.0f, 2.0f); - - body->CreateFixture(&circle, 2.0f); - } - } - - void UpdateUI() override - { - // ImGui::SetNextWindowPos(ImVec2(10.0f, 100.0f)); - ImGui::SetNextWindowSize(ImVec2(200.0f, 100.0f)); - ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_NoResize); - - if (ImGui::Button("Spawn")) - { - Spawn(); - } - - ImGui::End(); - } - - static Test* Create() { return new CompoundShapes; } - - b2Body* m_table1; - b2Body* m_table2; - b2Body* m_ship1; - b2Body* m_ship2; -}; - -static int testIndex = RegisterTest("Examples", "Compound Shapes", CompoundShapes::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/confined.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/confined.cpp deleted file mode 100644 index 44e2f6201036..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/confined.cpp +++ /dev/null @@ -1,165 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -class Confined : public Test -{ -public: - enum - { - e_columnCount = 0, - e_rowCount = 0 - }; - - Confined() - { - { - b2BodyDef bd; - b2Body* ground = m_world->CreateBody(&bd); - - b2EdgeShape shape; - - // Floor - shape.SetTwoSided(b2Vec2(-10.0f, 0.0f), b2Vec2(10.0f, 0.0f)); - ground->CreateFixture(&shape, 0.0f); - - // Left wall - shape.SetTwoSided(b2Vec2(-10.0f, 0.0f), b2Vec2(-10.0f, 20.0f)); - ground->CreateFixture(&shape, 0.0f); - - // Right wall - shape.SetTwoSided(b2Vec2(10.0f, 0.0f), b2Vec2(10.0f, 20.0f)); - ground->CreateFixture(&shape, 0.0f); - - // Roof - shape.SetTwoSided(b2Vec2(-10.0f, 20.0f), b2Vec2(10.0f, 20.0f)); - ground->CreateFixture(&shape, 0.0f); - } - - float radius = 0.5f; - b2CircleShape shape; - shape.m_p.SetZero(); - shape.m_radius = radius; - - b2FixtureDef fd; - fd.shape = &shape; - fd.density = 1.0f; - fd.friction = 0.1f; - - for (int32 j = 0; j < e_columnCount; ++j) - { - for (int i = 0; i < e_rowCount; ++i) - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(-10.0f + (2.1f * j + 1.0f + 0.01f * i) * radius, (2.0f * i + 1.0f) * radius); - b2Body* body = m_world->CreateBody(&bd); - - body->CreateFixture(&fd); - } - } - - m_world->SetGravity(b2Vec2(0.0f, 0.0f)); - } - - void CreateCircle() - { - float radius = 2.0f; - b2CircleShape shape; - shape.m_p.SetZero(); - shape.m_radius = radius; - - b2FixtureDef fd; - fd.shape = &shape; - fd.density = 1.0f; - fd.friction = 0.0f; - - b2Vec2 p(RandomFloat(), 3.0f + RandomFloat()); - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position = p; - // bd.allowSleep = false; - b2Body* body = m_world->CreateBody(&bd); - - body->CreateFixture(&fd); - } - - void Keyboard(int key) override - { - switch (key) - { - case GLFW_KEY_C: - CreateCircle(); - break; - } - } - - void Step(Settings& settings) override - { - bool sleeping = true; - for (b2Body* b = m_world->GetBodyList(); b; b = b->GetNext()) - { - if (b->GetType() != b2_dynamicBody) - { - continue; - } - - if (b->IsAwake()) - { - sleeping = false; - } - } - - if (m_stepCount == 180) - { - m_stepCount += 0; - } - - // if (sleeping) - //{ - // CreateCircle(); - // } - - Test::Step(settings); - - for (b2Body* b = m_world->GetBodyList(); b; b = b->GetNext()) - { - if (b->GetType() != b2_dynamicBody) - { - continue; - } - - b2Vec2 p = b->GetPosition(); - if (p.x <= -10.0f || 10.0f <= p.x || p.y <= 0.0f || 20.0f <= p.y) - { - p.x += 0.0f; - } - } - - DrawString(5, m_textLine, "Press 'c' to create a circle."); - } - - static Test* Create() { return new Confined; } -}; - -static int testIndex = RegisterTest("Solver", "Confined", Confined::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/continuous_test.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/continuous_test.cpp deleted file mode 100644 index 80187d9daaad..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/continuous_test.cpp +++ /dev/null @@ -1,162 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -class ContinuousTest : public Test -{ -public: - ContinuousTest() - { - { - b2BodyDef bd; - bd.position.Set(0.0f, 0.0f); - b2Body* body = m_world->CreateBody(&bd); - - b2EdgeShape edge; - - edge.SetTwoSided(b2Vec2(-10.0f, 0.0f), b2Vec2(10.0f, 0.0f)); - body->CreateFixture(&edge, 0.0f); - - b2PolygonShape shape; - shape.SetAsBox(0.2f, 1.0f, b2Vec2(0.5f, 1.0f), 0.0f); - body->CreateFixture(&shape, 0.0f); - } - -#if 1 - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(0.0f, 20.0f); - // bd.angle = 0.1f; - - b2PolygonShape shape; - shape.SetAsBox(2.0f, 0.1f); - - m_body = m_world->CreateBody(&bd); - m_body->CreateFixture(&shape, 1.0f); - - m_angularVelocity = RandomFloat(-50.0f, 50.0f); - // m_angularVelocity = 46.661274f; - m_body->SetLinearVelocity(b2Vec2(0.0f, -100.0f)); - m_body->SetAngularVelocity(m_angularVelocity); - } -#else - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(0.0f, 2.0f); - b2Body* body = m_world->CreateBody(&bd); - - b2CircleShape shape; - shape.m_p.SetZero(); - shape.m_radius = 0.5f; - body->CreateFixture(&shape, 1.0f); - - bd.bullet = true; - bd.position.Set(0.0f, 10.0f); - body = m_world->CreateBody(&bd); - body->CreateFixture(&shape, 1.0f); - body->SetLinearVelocity(b2Vec2(0.0f, -100.0f)); - } -#endif - - extern B2_API int32 b2_gjkCalls, b2_gjkIters, b2_gjkMaxIters; - extern B2_API int32 b2_toiCalls, b2_toiIters; - extern B2_API int32 b2_toiRootIters, b2_toiMaxRootIters; - extern B2_API float b2_toiTime, b2_toiMaxTime; - - b2_gjkCalls = 0; - b2_gjkIters = 0; - b2_gjkMaxIters = 0; - b2_toiCalls = 0; - b2_toiIters = 0; - b2_toiRootIters = 0; - b2_toiMaxRootIters = 0; - b2_toiTime = 0.0f; - b2_toiMaxTime = 0.0f; - } - - void Launch() - { - extern B2_API int32 b2_gjkCalls, b2_gjkIters, b2_gjkMaxIters; - extern B2_API int32 b2_toiCalls, b2_toiIters; - extern B2_API int32 b2_toiRootIters, b2_toiMaxRootIters; - extern B2_API float b2_toiTime, b2_toiMaxTime; - - b2_gjkCalls = 0; - b2_gjkIters = 0; - b2_gjkMaxIters = 0; - b2_toiCalls = 0; - b2_toiIters = 0; - b2_toiRootIters = 0; - b2_toiMaxRootIters = 0; - b2_toiTime = 0.0f; - b2_toiMaxTime = 0.0f; - - m_body->SetTransform(b2Vec2(0.0f, 20.0f), 0.0f); - m_angularVelocity = RandomFloat(-50.0f, 50.0f); - m_body->SetLinearVelocity(b2Vec2(0.0f, -100.0f)); - m_body->SetAngularVelocity(m_angularVelocity); - } - - void Step(Settings& settings) override - { - Test::Step(settings); - - extern B2_API int32 b2_gjkCalls, b2_gjkIters, b2_gjkMaxIters; - - if (b2_gjkCalls > 0) - { - DrawString(5, m_textLine, "gjk calls = %d, ave gjk iters = %3.1f, max gjk iters = %d", b2_gjkCalls, - b2_gjkIters / float(b2_gjkCalls), b2_gjkMaxIters); - } - - extern B2_API int32 b2_toiCalls, b2_toiIters; - extern B2_API int32 b2_toiRootIters, b2_toiMaxRootIters; - extern B2_API float b2_toiTime, b2_toiMaxTime; - - if (b2_toiCalls > 0) - { - DrawString(5, m_textLine, "toi calls = %d, ave [max] toi iters = %3.1f [%d]", b2_toiCalls, - b2_toiIters / float(b2_toiCalls), b2_toiMaxRootIters); - - DrawString(5, m_textLine, "ave [max] toi root iters = %3.1f [%d]", b2_toiRootIters / float(b2_toiCalls), - b2_toiMaxRootIters); - - DrawString(5, m_textLine, "ave [max] toi time = %.1f [%.1f] (microseconds)", - 1000.0f * b2_toiTime / float(b2_toiCalls), 1000.0f * b2_toiMaxTime); - } - - if (m_stepCount % 60 == 0) - { - Launch(); - } - } - - static Test* Create() { return new ContinuousTest; } - - b2Body* m_body; - float m_angularVelocity; -}; - -static int testIndex = RegisterTest("Continuous", "Continuous Test", ContinuousTest::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/convex_hull.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/convex_hull.cpp deleted file mode 100644 index 44f4da8906e5..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/convex_hull.cpp +++ /dev/null @@ -1,108 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -class ConvexHull : public Test -{ -public: - enum - { - e_count = b2_maxPolygonVertices - }; - - ConvexHull() - { - Generate(); - m_auto = false; - } - - void Generate() - { - b2Vec2 lowerBound(-8.0f, -8.0f); - b2Vec2 upperBound(8.0f, 8.0f); - - for (int32 i = 0; i < e_count; ++i) - { - float x = 10.0f * RandomFloat(); - float y = 10.0f * RandomFloat(); - - // Clamp onto a square to help create collinearities. - // This will stress the convex hull algorithm. - b2Vec2 v(x, y); - v = b2Clamp(v, lowerBound, upperBound); - m_points[i] = v; - } - - m_count = e_count; - } - - void Keyboard(int key) override - { - switch (key) - { - case GLFW_KEY_A: - m_auto = !m_auto; - break; - - case GLFW_KEY_G: - Generate(); - break; - } - } - - void Step(Settings& settings) override - { - Test::Step(settings); - - b2PolygonShape shape; - shape.Set(m_points, m_count); - - DrawString(5, m_textLine, "Press g to generate a new random convex hull"); - - g_debugDraw.DrawPolygon(shape.m_vertices, shape.m_count, b2Color(0.9f, 0.9f, 0.9f)); - - for (int32 i = 0; i < m_count; ++i) - { - g_debugDraw.DrawPoint(m_points[i], 3.0f, b2Color(0.3f, 0.9f, 0.3f)); - DrawString(m_points[i] + b2Vec2(0.05f, 0.05f), "%d", i); - } - - if (shape.Validate() == false) - { - m_textLine += 0; - } - - if (m_auto) - { - Generate(); - } - } - - static Test* Create() { return new ConvexHull; } - - b2Vec2 m_points[b2_maxPolygonVertices]; - int32 m_count; - bool m_auto; -}; - -static int testIndex = RegisterTest("Geometry", "Convex Hull", ConvexHull::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/conveyor_belt.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/conveyor_belt.cpp deleted file mode 100644 index 967ff5826369..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/conveyor_belt.cpp +++ /dev/null @@ -1,94 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -class ConveyorBelt : public Test -{ -public: - ConveyorBelt() - { - // Ground - { - b2BodyDef bd; - b2Body* ground = m_world->CreateBody(&bd); - - b2EdgeShape shape; - shape.SetTwoSided(b2Vec2(-20.0f, 0.0f), b2Vec2(20.0f, 0.0f)); - ground->CreateFixture(&shape, 0.0f); - } - - // Platform - { - b2BodyDef bd; - bd.position.Set(-5.0f, 5.0f); - b2Body* body = m_world->CreateBody(&bd); - - b2PolygonShape shape; - shape.SetAsBox(10.0f, 0.5f); - - b2FixtureDef fd; - fd.shape = &shape; - fd.friction = 0.8f; - m_platform = body->CreateFixture(&fd); - } - - // Boxes - for (int32 i = 0; i < 5; ++i) - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(-10.0f + 2.0f * i, 7.0f); - b2Body* body = m_world->CreateBody(&bd); - - b2PolygonShape shape; - shape.SetAsBox(0.5f, 0.5f); - body->CreateFixture(&shape, 20.0f); - } - } - - void PreSolve(b2Contact* contact, const b2Manifold* oldManifold) override - { - Test::PreSolve(contact, oldManifold); - - b2Fixture* fixtureA = contact->GetFixtureA(); - b2Fixture* fixtureB = contact->GetFixtureB(); - - if (fixtureA == m_platform) - { - contact->SetTangentSpeed(5.0f); - } - - if (fixtureB == m_platform) - { - contact->SetTangentSpeed(-5.0f); - } - } - - void Step(Settings& settings) override { Test::Step(settings); } - - static Test* Create() { return new ConveyorBelt; } - - b2Fixture* m_platform; -}; - -static int testIndex = RegisterTest("Examples", "Conveyor Belt", ConveyorBelt::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/distance_joint.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/distance_joint.cpp deleted file mode 100644 index 96eded3cf53c..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/distance_joint.cpp +++ /dev/null @@ -1,120 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" -#include "ImGui/ImGuiPresenter.h" - -// This tests distance joints, body destruction, and joint destruction. -class DistanceJoint : public Test -{ -public: - DistanceJoint() - { - b2Body* ground = NULL; - { - b2BodyDef bd; - ground = m_world->CreateBody(&bd); - - b2EdgeShape shape; - shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f)); - ground->CreateFixture(&shape, 0.0f); - } - - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.angularDamping = 0.1f; - - bd.position.Set(0.0f, 5.0f); - b2Body* body = m_world->CreateBody(&bd); - - b2PolygonShape shape; - shape.SetAsBox(0.5f, 0.5f); - body->CreateFixture(&shape, 5.0f); - - m_hertz = 1.0f; - m_dampingRatio = 0.7f; - - b2DistanceJointDef jd; - jd.Initialize(ground, body, b2Vec2(0.0f, 15.0f), bd.position); - jd.collideConnected = true; - m_length = jd.length; - m_minLength = m_length; - m_maxLength = m_length; - b2LinearStiffness(jd.stiffness, jd.damping, m_hertz, m_dampingRatio, jd.bodyA, jd.bodyB); - m_joint = (b2DistanceJoint*)m_world->CreateJoint(&jd); - } - } - - void UpdateUI() override - { - // ImGui::SetNextWindowPos(ImVec2(10.0f, 100.0f)); - ImGui::SetNextWindowSize(ImVec2(260.0f, 150.0f)); - ImGui::Begin("Joint Controls", nullptr, ImGuiWindowFlags_NoResize); - - if (ImGui::SliderFloat("Length", &m_length, 0.0f, 20.0f, "%.0f")) - { - m_length = m_joint->SetLength(m_length); - } - - if (ImGui::SliderFloat("Min Length", &m_minLength, 0.0f, 20.0f, "%.0f")) - { - m_minLength = m_joint->SetMinLength(m_minLength); - } - - if (ImGui::SliderFloat("Max Length", &m_maxLength, 0.0f, 20.0f, "%.0f")) - { - m_maxLength = m_joint->SetMaxLength(m_maxLength); - } - - if (ImGui::SliderFloat("Hertz", &m_hertz, 0.0f, 10.0f, "%.1f")) - { - float stiffness; - float damping; - b2LinearStiffness(stiffness, damping, m_hertz, m_dampingRatio, m_joint->GetBodyA(), m_joint->GetBodyB()); - m_joint->SetStiffness(stiffness); - m_joint->SetDamping(damping); - } - - if (ImGui::SliderFloat("Damping Ratio", &m_dampingRatio, 0.0f, 2.0f, "%.1f")) - { - float stiffness; - float damping; - b2LinearStiffness(stiffness, damping, m_hertz, m_dampingRatio, m_joint->GetBodyA(), m_joint->GetBodyB()); - m_joint->SetStiffness(stiffness); - m_joint->SetDamping(damping); - } - - ImGui::End(); - } - - static Test* Create() { return new DistanceJoint; } - - b2DistanceJoint* m_joint; - float m_length; - float m_minLength; - float m_maxLength; - float m_hertz; - float m_dampingRatio; -}; - -static int testIndex = RegisterTest("Joints", "Distance Joint", DistanceJoint::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/distance_test.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/distance_test.cpp deleted file mode 100644 index 6372330426b0..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/distance_test.cpp +++ /dev/null @@ -1,134 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" -#include "box2d/b2_distance.h" - -class DistanceTest : public Test -{ -public: - DistanceTest() - { - { - m_transformA.SetIdentity(); - m_transformA.p.Set(0.0f, -0.2f); - m_polygonA.SetAsBox(10.0f, 0.2f); - } - - { - m_positionB.Set(12.017401f, 0.13678508f); - m_angleB = -0.0109265f; - m_transformB.Set(m_positionB, m_angleB); - - m_polygonB.SetAsBox(2.0f, 0.1f); - } - } - - static Test* Create() { return new DistanceTest; } - - void Step(Settings& settings) override - { - Test::Step(settings); - - b2DistanceInput input; - input.proxyA.Set(&m_polygonA, 0); - input.proxyB.Set(&m_polygonB, 0); - input.transformA = m_transformA; - input.transformB = m_transformB; - input.useRadii = true; - b2SimplexCache cache; - cache.count = 0; - b2DistanceOutput output; - b2Distance(&output, &cache, &input); - - DrawString(5, m_textLine, "distance = %g", output.distance); - - DrawString(5, m_textLine, "iterations = %d", output.iterations); - - { - b2Color color(0.9f, 0.9f, 0.9f); - b2Vec2 v[b2_maxPolygonVertices]; - for (int32 i = 0; i < m_polygonA.m_count; ++i) - { - v[i] = b2Mul(m_transformA, m_polygonA.m_vertices[i]); - } - g_debugDraw.DrawPolygon(v, m_polygonA.m_count, color); - - for (int32 i = 0; i < m_polygonB.m_count; ++i) - { - v[i] = b2Mul(m_transformB, m_polygonB.m_vertices[i]); - } - g_debugDraw.DrawPolygon(v, m_polygonB.m_count, color); - } - - b2Vec2 x1 = output.pointA; - b2Vec2 x2 = output.pointB; - - b2Color c1(1.0f, 0.0f, 0.0f); - g_debugDraw.DrawPoint(x1, 4.0f, c1); - - b2Color c2(1.0f, 1.0f, 0.0f); - g_debugDraw.DrawPoint(x2, 4.0f, c2); - } - - void Keyboard(int key) override - { - switch (key) - { - case GLFW_KEY_A: - m_positionB.x -= 0.1f; - break; - - case GLFW_KEY_D: - m_positionB.x += 0.1f; - break; - - case GLFW_KEY_S: - m_positionB.y -= 0.1f; - break; - - case GLFW_KEY_W: - m_positionB.y += 0.1f; - break; - - case GLFW_KEY_Q: - m_angleB += 0.1f * b2_pi; - break; - - case GLFW_KEY_E: - m_angleB -= 0.1f * b2_pi; - break; - } - - m_transformB.Set(m_positionB, m_angleB); - } - - b2Vec2 m_positionB; - float m_angleB; - - b2Transform m_transformA; - b2Transform m_transformB; - b2PolygonShape m_polygonA; - b2PolygonShape m_polygonB; -}; - -static int testIndex = RegisterTest("Geometry", "Distance Test", DistanceTest::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/dominos.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/dominos.cpp deleted file mode 100644 index f64a141bd407..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/dominos.cpp +++ /dev/null @@ -1,216 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -class Dominos : public Test -{ -public: - Dominos() - { - b2Body* b1; - { - b2EdgeShape shape; - shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f)); - - b2BodyDef bd; - b1 = m_world->CreateBody(&bd); - b1->CreateFixture(&shape, 0.0f); - } - - { - b2PolygonShape shape; - shape.SetAsBox(6.0f, 0.25f); - - b2BodyDef bd; - bd.position.Set(-1.5f, 10.0f); - b2Body* ground = m_world->CreateBody(&bd); - ground->CreateFixture(&shape, 0.0f); - } - - { - b2PolygonShape shape; - shape.SetAsBox(0.1f, 1.0f); - - b2FixtureDef fd; - fd.shape = &shape; - fd.density = 20.0f; - fd.friction = 0.1f; - - for (int i = 0; i < 10; ++i) - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(-6.0f + 1.0f * i, 11.25f); - b2Body* body = m_world->CreateBody(&bd); - body->CreateFixture(&fd); - } - } - - { - b2PolygonShape shape; - shape.SetAsBox(7.0f, 0.25f, b2Vec2_zero, 0.3f); - - b2BodyDef bd; - bd.position.Set(1.0f, 6.0f); - b2Body* ground = m_world->CreateBody(&bd); - ground->CreateFixture(&shape, 0.0f); - } - - b2Body* b2; - { - b2PolygonShape shape; - shape.SetAsBox(0.25f, 1.5f); - - b2BodyDef bd; - bd.position.Set(-7.0f, 4.0f); - b2 = m_world->CreateBody(&bd); - b2->CreateFixture(&shape, 0.0f); - } - - b2Body* b3; - { - b2PolygonShape shape; - shape.SetAsBox(6.0f, 0.125f); - - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(-0.9f, 1.0f); - bd.angle = -0.15f; - - b3 = m_world->CreateBody(&bd); - b3->CreateFixture(&shape, 10.0f); - } - - b2RevoluteJointDef jd; - b2Vec2 anchor; - - anchor.Set(-2.0f, 1.0f); - jd.Initialize(b1, b3, anchor); - jd.collideConnected = true; - m_world->CreateJoint(&jd); - - b2Body* b4; - { - b2PolygonShape shape; - shape.SetAsBox(0.25f, 0.25f); - - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(-10.0f, 15.0f); - b4 = m_world->CreateBody(&bd); - b4->CreateFixture(&shape, 10.0f); - } - - anchor.Set(-7.0f, 15.0f); - jd.Initialize(b2, b4, anchor); - m_world->CreateJoint(&jd); - - b2Body* b5; - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(6.5f, 3.0f); - b5 = m_world->CreateBody(&bd); - - b2PolygonShape shape; - b2FixtureDef fd; - - fd.shape = &shape; - fd.density = 10.0f; - fd.friction = 0.1f; - - shape.SetAsBox(1.0f, 0.1f, b2Vec2(0.0f, -0.9f), 0.0f); - b5->CreateFixture(&fd); - - shape.SetAsBox(0.1f, 1.0f, b2Vec2(-0.9f, 0.0f), 0.0f); - b5->CreateFixture(&fd); - - shape.SetAsBox(0.1f, 1.0f, b2Vec2(0.9f, 0.0f), 0.0f); - b5->CreateFixture(&fd); - } - - anchor.Set(6.0f, 2.0f); - jd.Initialize(b1, b5, anchor); - m_world->CreateJoint(&jd); - - b2Body* b6; - { - b2PolygonShape shape; - shape.SetAsBox(1.0f, 0.1f); - - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(6.5f, 4.1f); - b6 = m_world->CreateBody(&bd); - b6->CreateFixture(&shape, 30.0f); - } - - anchor.Set(7.5f, 4.0f); - jd.Initialize(b5, b6, anchor); - m_world->CreateJoint(&jd); - - b2Body* b7; - { - b2PolygonShape shape; - shape.SetAsBox(0.1f, 1.0f); - - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(7.4f, 1.0f); - - b7 = m_world->CreateBody(&bd); - b7->CreateFixture(&shape, 10.0f); - } - - b2DistanceJointDef djd; - djd.bodyA = b3; - djd.bodyB = b7; - djd.localAnchorA.Set(6.0f, 0.0f); - djd.localAnchorB.Set(0.0f, -1.0f); - b2Vec2 d = djd.bodyB->GetWorldPoint(djd.localAnchorB) - djd.bodyA->GetWorldPoint(djd.localAnchorA); - djd.length = d.Length(); - - b2LinearStiffness(djd.stiffness, djd.damping, 1.0f, 1.0f, djd.bodyA, djd.bodyB); - m_world->CreateJoint(&djd); - - { - float radius = 0.2f; - - b2CircleShape shape; - shape.m_radius = radius; - - for (int32 i = 0; i < 4; ++i) - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(5.9f + 2.0f * radius * i, 2.4f); - b2Body* body = m_world->CreateBody(&bd); - body->CreateFixture(&shape, 10.0f); - } - } - } - - static Test* Create() { return new Dominos; } -}; - -static int testIndex = RegisterTest("Examples", "Dominos", Dominos::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/dump_loader.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/dump_loader.cpp deleted file mode 100644 index b874a2a174ef..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/dump_loader.cpp +++ /dev/null @@ -1,82 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -// This test holds worlds dumped using b2World::Dump. -class DumpLoader : public Test -{ -public: - DumpLoader() - { - b2ChainShape chainShape; - b2Vec2 vertices[] = {b2Vec2(-5, 0), b2Vec2(5, 0), b2Vec2(5, 5), b2Vec2(4, 1), b2Vec2(-4, 1), b2Vec2(-5, 5)}; - chainShape.CreateLoop(vertices, 6); - - b2FixtureDef groundFixtureDef; - groundFixtureDef.density = 0; - groundFixtureDef.shape = &chainShape; - - b2BodyDef groundBodyDef; - groundBodyDef.type = b2_staticBody; - - b2Body* groundBody = m_world->CreateBody(&groundBodyDef); - b2Fixture* groundBodyFixture = groundBody->CreateFixture(&groundFixtureDef); - - b2CircleShape ballShape; - ballShape.m_radius = 1; - - b2FixtureDef ballFixtureDef; - ballFixtureDef.restitution = 0.75f; - ballFixtureDef.density = 1; - ballFixtureDef.shape = &ballShape; - - b2BodyDef ballBodyDef; - ballBodyDef.type = b2BodyType::b2_dynamicBody; - ballBodyDef.position = b2Vec2(0, 10); - // ballBodyDef.angularDamping = 0.2f; - - m_ball = m_world->CreateBody(&ballBodyDef); - b2Fixture* ballFixture = m_ball->CreateFixture(&ballFixtureDef); - m_ball->ApplyForceToCenter(b2Vec2(-1000, -400), true); - } - - void Step(Settings& settings) override - { - b2Vec2 v = m_ball->GetLinearVelocity(); - float omega = m_ball->GetAngularVelocity(); - - b2MassData massData = m_ball->GetMassData(); - - float ke = 0.5f * massData.mass * b2Dot(v, v) + 0.5f * massData.I * omega * omega; - - DrawString(5, m_textLine, "kinetic energy = %.6f", ke); - - Test::Step(settings); - } - - static Test* Create() { return new DumpLoader; } - - b2Body* m_ball; -}; - -static int testIndex = RegisterTest("Bugs", "Dump Loader", DumpLoader::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/dynamic_tree.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/dynamic_tree.cpp deleted file mode 100644 index bd2de4d80a0f..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/dynamic_tree.cpp +++ /dev/null @@ -1,357 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -class DynamicTree : public Test -{ -public: - enum - { - e_actorCount = 128 - }; - - DynamicTree() - { - m_worldExtent = 15.0f; - m_proxyExtent = 0.5f; - - srand(888); - - for (int32 i = 0; i < e_actorCount; ++i) - { - Actor* actor = m_actors + i; - GetRandomAABB(&actor->aabb); - actor->proxyId = m_tree.CreateProxy(actor->aabb, actor); - } - - m_stepCount = 0; - - float h = m_worldExtent; - m_queryAABB.lowerBound.Set(-3.0f, -4.0f + h); - m_queryAABB.upperBound.Set(5.0f, 6.0f + h); - - m_rayCastInput.p1.Set(-5.0, 5.0f + h); - m_rayCastInput.p2.Set(7.0f, -4.0f + h); - // m_rayCastInput.p1.Set(0.0f, 2.0f + h); - // m_rayCastInput.p2.Set(0.0f, -2.0f + h); - m_rayCastInput.maxFraction = 1.0f; - - m_automated = false; - } - - static Test* Create() { return new DynamicTree; } - - void Step(Settings& settings) override - { - B2_NOT_USED(settings); - - m_rayActor = NULL; - for (int32 i = 0; i < e_actorCount; ++i) - { - m_actors[i].fraction = 1.0f; - m_actors[i].overlap = false; - } - - if (m_automated == true) - { - int32 actionCount = b2Max(1, e_actorCount >> 2); - - for (int32 i = 0; i < actionCount; ++i) - { - Action(); - } - } - - Query(); - RayCast(); - - for (int32 i = 0; i < e_actorCount; ++i) - { - Actor* actor = m_actors + i; - if (actor->proxyId == b2_nullNode) - continue; - - b2Color c(0.9f, 0.9f, 0.9f); - if (actor == m_rayActor && actor->overlap) - { - c.Set(0.9f, 0.6f, 0.6f); - } - else if (actor == m_rayActor) - { - c.Set(0.6f, 0.9f, 0.6f); - } - else if (actor->overlap) - { - c.Set(0.6f, 0.6f, 0.9f); - } - - DrawAABB(&actor->aabb, c); - } - - b2Color c(0.7f, 0.7f, 0.7f); - DrawAABB(&m_queryAABB, c); - - g_debugDraw.DrawSegment(m_rayCastInput.p1, m_rayCastInput.p2, c); - - b2Color c1(0.2f, 0.9f, 0.2f); - b2Color c2(0.9f, 0.2f, 0.2f); - g_debugDraw.DrawPoint(m_rayCastInput.p1, 6.0f, c1); - g_debugDraw.DrawPoint(m_rayCastInput.p2, 6.0f, c2); - - if (m_rayActor) - { - b2Color cr(0.2f, 0.2f, 0.9f); - b2Vec2 p = m_rayCastInput.p1 + m_rayActor->fraction * (m_rayCastInput.p2 - m_rayCastInput.p1); - g_debugDraw.DrawPoint(p, 6.0f, cr); - } - - { - int32 height = m_tree.GetHeight(); - DrawString(5, m_textLine, "dynamic tree height = %d", height); - } - - ++m_stepCount; - } - - void Keyboard(int key) override - { - switch (key) - { - case GLFW_KEY_A: - m_automated = !m_automated; - break; - - case GLFW_KEY_C: - CreateProxy(); - break; - - case GLFW_KEY_D: - DestroyProxy(); - break; - - case GLFW_KEY_M: - MoveProxy(); - break; - } - } - - bool QueryCallback(int32 proxyId) - { - Actor* actor = (Actor*)m_tree.GetUserData(proxyId); - actor->overlap = b2TestOverlap(m_queryAABB, actor->aabb); - return true; - } - - float RayCastCallback(const b2RayCastInput& input, int32 proxyId) - { - Actor* actor = (Actor*)m_tree.GetUserData(proxyId); - - b2RayCastOutput output; - bool hit = actor->aabb.RayCast(&output, input); - - if (hit) - { - m_rayCastOutput = output; - m_rayActor = actor; - m_rayActor->fraction = output.fraction; - return output.fraction; - } - - return input.maxFraction; - } - -private: - struct Actor - { - b2AABB aabb; - float fraction; - bool overlap; - int32 proxyId; - }; - - void GetRandomAABB(b2AABB* aabb) - { - b2Vec2 w; - w.Set(2.0f * m_proxyExtent, 2.0f * m_proxyExtent); - // aabb->lowerBound.x = -m_proxyExtent; - // aabb->lowerBound.y = -m_proxyExtent + m_worldExtent; - aabb->lowerBound.x = RandomFloat(-m_worldExtent, m_worldExtent); - aabb->lowerBound.y = RandomFloat(0.0f, 2.0f * m_worldExtent); - aabb->upperBound = aabb->lowerBound + w; - } - - void MoveAABB(b2AABB* aabb) - { - b2Vec2 d; - d.x = RandomFloat(-0.5f, 0.5f); - d.y = RandomFloat(-0.5f, 0.5f); - // d.x = 2.0f; - // d.y = 0.0f; - aabb->lowerBound += d; - aabb->upperBound += d; - - b2Vec2 c0 = 0.5f * (aabb->lowerBound + aabb->upperBound); - b2Vec2 min; - min.Set(-m_worldExtent, 0.0f); - b2Vec2 max; - max.Set(m_worldExtent, 2.0f * m_worldExtent); - b2Vec2 c = b2Clamp(c0, min, max); - - aabb->lowerBound += c - c0; - aabb->upperBound += c - c0; - } - - void CreateProxy() - { - for (int32 i = 0; i < e_actorCount; ++i) - { - int32 j = rand() % e_actorCount; - Actor* actor = m_actors + j; - if (actor->proxyId == b2_nullNode) - { - GetRandomAABB(&actor->aabb); - actor->proxyId = m_tree.CreateProxy(actor->aabb, actor); - return; - } - } - } - - void DestroyProxy() - { - for (int32 i = 0; i < e_actorCount; ++i) - { - int32 j = rand() % e_actorCount; - Actor* actor = m_actors + j; - if (actor->proxyId != b2_nullNode) - { - m_tree.DestroyProxy(actor->proxyId); - actor->proxyId = b2_nullNode; - return; - } - } - } - - void MoveProxy() - { - for (int32 i = 0; i < e_actorCount; ++i) - { - int32 j = rand() % e_actorCount; - Actor* actor = m_actors + j; - if (actor->proxyId == b2_nullNode) - { - continue; - } - - b2AABB aabb0 = actor->aabb; - MoveAABB(&actor->aabb); - b2Vec2 displacement = actor->aabb.GetCenter() - aabb0.GetCenter(); - m_tree.MoveProxy(actor->proxyId, actor->aabb, displacement); - return; - } - } - - void Action() - { - int32 choice = rand() % 20; - - switch (choice) - { - case 0: - CreateProxy(); - break; - - case 1: - DestroyProxy(); - break; - - default: - MoveProxy(); - } - } - - void Query() - { - m_tree.Query(this, m_queryAABB); - - for (int32 i = 0; i < e_actorCount; ++i) - { - if (m_actors[i].proxyId == b2_nullNode) - { - continue; - } - - bool overlap = b2TestOverlap(m_queryAABB, m_actors[i].aabb); - B2_NOT_USED(overlap); - b2Assert(overlap == m_actors[i].overlap); - } - } - - void RayCast() - { - m_rayActor = NULL; - - b2RayCastInput input = m_rayCastInput; - - // Ray cast against the dynamic tree. - m_tree.RayCast(this, input); - - // Brute force ray cast. - Actor* bruteActor = NULL; - b2RayCastOutput bruteOutput; - for (int32 i = 0; i < e_actorCount; ++i) - { - if (m_actors[i].proxyId == b2_nullNode) - { - continue; - } - - b2RayCastOutput output; - bool hit = m_actors[i].aabb.RayCast(&output, input); - if (hit) - { - bruteActor = m_actors + i; - bruteOutput = output; - input.maxFraction = output.fraction; - } - } - - if (bruteActor != NULL) - { - b2Assert(bruteOutput.fraction == m_rayCastOutput.fraction); - } - } - - float m_worldExtent; - float m_proxyExtent; - - b2DynamicTree m_tree; - b2AABB m_queryAABB; - b2RayCastInput m_rayCastInput; - b2RayCastOutput m_rayCastOutput; - Actor* m_rayActor; - Actor m_actors[e_actorCount]; - int32 m_stepCount; - bool m_automated; -}; - -static int testIndex = RegisterTest("Collision", "Dynamic Tree", DynamicTree::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/edge_shapes.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/edge_shapes.cpp deleted file mode 100644 index f7416dc4373d..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/edge_shapes.cpp +++ /dev/null @@ -1,244 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "settings.h" -#include "test.h" - -class EdgeShapesCallback : public b2RayCastCallback -{ -public: - EdgeShapesCallback() { m_fixture = NULL; } - - float ReportFixture(b2Fixture* fixture, const b2Vec2& point, const b2Vec2& normal, float fraction) override - { - m_fixture = fixture; - m_point = point; - m_normal = normal; - - return fraction; - } - - b2Fixture* m_fixture; - b2Vec2 m_point; - b2Vec2 m_normal; -}; - -class EdgeShapes : public Test -{ -public: - enum - { - e_maxBodies = 256 - }; - - EdgeShapes() - { - // Ground body - { - b2BodyDef bd; - b2Body* ground = m_world->CreateBody(&bd); - - float x1 = -20.0f; - float y1 = 2.0f * cosf(x1 / 10.0f * b2_pi); - for (int32 i = 0; i < 80; ++i) - { - float x2 = x1 + 0.5f; - float y2 = 2.0f * cosf(x2 / 10.0f * b2_pi); - - b2EdgeShape shape; - shape.SetTwoSided(b2Vec2(x1, y1), b2Vec2(x2, y2)); - ground->CreateFixture(&shape, 0.0f); - - x1 = x2; - y1 = y2; - } - } - - { - b2Vec2 vertices[3]; - vertices[0].Set(-0.5f, 0.0f); - vertices[1].Set(0.5f, 0.0f); - vertices[2].Set(0.0f, 1.5f); - m_polygons[0].Set(vertices, 3); - } - - { - b2Vec2 vertices[3]; - vertices[0].Set(-0.1f, 0.0f); - vertices[1].Set(0.1f, 0.0f); - vertices[2].Set(0.0f, 1.5f); - m_polygons[1].Set(vertices, 3); - } - - { - float w = 1.0f; - float b = w / (2.0f + b2Sqrt(2.0f)); - float s = b2Sqrt(2.0f) * b; - - b2Vec2 vertices[8]; - vertices[0].Set(0.5f * s, 0.0f); - vertices[1].Set(0.5f * w, b); - vertices[2].Set(0.5f * w, b + s); - vertices[3].Set(0.5f * s, w); - vertices[4].Set(-0.5f * s, w); - vertices[5].Set(-0.5f * w, b + s); - vertices[6].Set(-0.5f * w, b); - vertices[7].Set(-0.5f * s, 0.0f); - - m_polygons[2].Set(vertices, 8); - } - - { - m_polygons[3].SetAsBox(0.5f, 0.5f); - } - - { - m_circle.m_radius = 0.5f; - } - - m_bodyIndex = 0; - memset(m_bodies, 0, sizeof(m_bodies)); - - m_angle = 0.0f; - } - - void Create(int32 index) - { - if (m_bodies[m_bodyIndex] != NULL) - { - m_world->DestroyBody(m_bodies[m_bodyIndex]); - m_bodies[m_bodyIndex] = NULL; - } - - b2BodyDef bd; - - float x = RandomFloat(-10.0f, 10.0f); - float y = RandomFloat(10.0f, 20.0f); - bd.position.Set(x, y); - bd.angle = RandomFloat(-b2_pi, b2_pi); - bd.type = b2_dynamicBody; - - if (index == 4) - { - bd.angularDamping = 0.02f; - } - - m_bodies[m_bodyIndex] = m_world->CreateBody(&bd); - - if (index < 4) - { - b2FixtureDef fd; - fd.shape = m_polygons + index; - fd.friction = 0.3f; - fd.density = 20.0f; - m_bodies[m_bodyIndex]->CreateFixture(&fd); - } - else - { - b2FixtureDef fd; - fd.shape = &m_circle; - fd.friction = 0.3f; - fd.density = 20.0f; - m_bodies[m_bodyIndex]->CreateFixture(&fd); - } - - m_bodyIndex = (m_bodyIndex + 1) % e_maxBodies; - } - - void DestroyBody() - { - for (int32 i = 0; i < e_maxBodies; ++i) - { - if (m_bodies[i] != NULL) - { - m_world->DestroyBody(m_bodies[i]); - m_bodies[i] = NULL; - return; - } - } - } - - void Keyboard(int key) override - { - switch (key) - { - case GLFW_KEY_1: - case GLFW_KEY_2: - case GLFW_KEY_3: - case GLFW_KEY_4: - case GLFW_KEY_5: - Create(key - GLFW_KEY_1); - break; - - case GLFW_KEY_D: - DestroyBody(); - break; - } - } - - void Step(Settings& settings) override - { - bool advanceRay = settings.m_pause == 0 || settings.m_singleStep; - - Test::Step(settings); - DrawString(5, m_textLine, "Press 1-5 to drop stuff"); - - float L = 25.0f; - b2Vec2 point1(0.0f, 10.0f); - b2Vec2 d(L * cosf(m_angle), -L * b2Abs(sinf(m_angle))); - b2Vec2 point2 = point1 + d; - - EdgeShapesCallback callback; - - m_world->RayCast(&callback, point1, point2); - - if (callback.m_fixture) - { - g_debugDraw.DrawPoint(callback.m_point, 5.0f, b2Color(0.4f, 0.9f, 0.4f)); - - g_debugDraw.DrawSegment(point1, callback.m_point, b2Color(0.8f, 0.8f, 0.8f)); - - b2Vec2 head = callback.m_point + 0.5f * callback.m_normal; - g_debugDraw.DrawSegment(callback.m_point, head, b2Color(0.9f, 0.9f, 0.4f)); - } - else - { - g_debugDraw.DrawSegment(point1, point2, b2Color(0.8f, 0.8f, 0.8f)); - } - - if (advanceRay) - { - m_angle += 0.25f * b2_pi / 180.0f; - } - } - - static Test* Create() { return new EdgeShapes; } - - int32 m_bodyIndex; - b2Body* m_bodies[e_maxBodies]; - b2PolygonShape m_polygons[4]; - b2CircleShape m_circle; - - float m_angle; -}; - -static int testIndex = RegisterTest("Geometry", "Edge Shapes", EdgeShapes::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/edge_test.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/edge_test.cpp deleted file mode 100644 index 6cd086c3972c..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/edge_test.cpp +++ /dev/null @@ -1,267 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" -#include "ImGui/ImGuiPresenter.h" - -class EdgeTest : public Test -{ -public: - EdgeTest() - { - b2Vec2 vertices[10] = {{10.0f, -4.0f}, {10.0f, 0.0f}, {6.0f, 0.0f}, {4.0f, 2.0f}, {2.0f, 0.0f}, - {-2.0f, 0.0f}, {-6.0f, 0.0f}, {-8.0f, -3.0f}, {-10.0f, 0.0f}, {-10.0f, -4.0f}}; - - m_offset1.Set(0.0f, 8.0f); - m_offset2.Set(0.0f, 16.0f); - - { - b2Vec2 v1 = vertices[0] + m_offset1; - b2Vec2 v2 = vertices[1] + m_offset1; - b2Vec2 v3 = vertices[2] + m_offset1; - b2Vec2 v4 = vertices[3] + m_offset1; - b2Vec2 v5 = vertices[4] + m_offset1; - b2Vec2 v6 = vertices[5] + m_offset1; - b2Vec2 v7 = vertices[6] + m_offset1; - b2Vec2 v8 = vertices[7] + m_offset1; - b2Vec2 v9 = vertices[8] + m_offset1; - b2Vec2 v10 = vertices[9] + m_offset1; - - b2BodyDef bd; - b2Body* ground = m_world->CreateBody(&bd); - - b2EdgeShape shape; - - shape.SetOneSided(v10, v1, v2, v3); - ground->CreateFixture(&shape, 0.0f); - - shape.SetOneSided(v1, v2, v3, v4); - ground->CreateFixture(&shape, 0.0f); - - shape.SetOneSided(v2, v3, v4, v5); - ground->CreateFixture(&shape, 0.0f); - - shape.SetOneSided(v3, v4, v5, v6); - ground->CreateFixture(&shape, 0.0f); - - shape.SetOneSided(v4, v5, v6, v7); - ground->CreateFixture(&shape, 0.0f); - - shape.SetOneSided(v5, v6, v7, v8); - ground->CreateFixture(&shape, 0.0f); - - shape.SetOneSided(v6, v7, v8, v9); - ground->CreateFixture(&shape, 0.0f); - - shape.SetOneSided(v7, v8, v9, v10); - ground->CreateFixture(&shape, 0.0f); - - shape.SetOneSided(v8, v9, v10, v1); - ground->CreateFixture(&shape, 0.0f); - - shape.SetOneSided(v9, v10, v1, v2); - ground->CreateFixture(&shape, 0.0f); - } - - { - b2Vec2 v1 = vertices[0] + m_offset2; - b2Vec2 v2 = vertices[1] + m_offset2; - b2Vec2 v3 = vertices[2] + m_offset2; - b2Vec2 v4 = vertices[3] + m_offset2; - b2Vec2 v5 = vertices[4] + m_offset2; - b2Vec2 v6 = vertices[5] + m_offset2; - b2Vec2 v7 = vertices[6] + m_offset2; - b2Vec2 v8 = vertices[7] + m_offset2; - b2Vec2 v9 = vertices[8] + m_offset2; - b2Vec2 v10 = vertices[9] + m_offset2; - - b2BodyDef bd; - b2Body* ground = m_world->CreateBody(&bd); - - b2EdgeShape shape; - - shape.SetTwoSided(v1, v2); - ground->CreateFixture(&shape, 0.0f); - - shape.SetTwoSided(v2, v3); - ground->CreateFixture(&shape, 0.0f); - - shape.SetTwoSided(v3, v4); - ground->CreateFixture(&shape, 0.0f); - - shape.SetTwoSided(v4, v5); - ground->CreateFixture(&shape, 0.0f); - - shape.SetTwoSided(v5, v6); - ground->CreateFixture(&shape, 0.0f); - - shape.SetTwoSided(v6, v7); - ground->CreateFixture(&shape, 0.0f); - - shape.SetTwoSided(v7, v8); - ground->CreateFixture(&shape, 0.0f); - - shape.SetTwoSided(v8, v9); - ground->CreateFixture(&shape, 0.0f); - - shape.SetTwoSided(v9, v10); - ground->CreateFixture(&shape, 0.0f); - - shape.SetTwoSided(v10, v1); - ground->CreateFixture(&shape, 0.0f); - } - - m_body1 = nullptr; - m_body2 = nullptr; - CreateBoxes(); - m_boxes = true; - } - - void CreateBoxes() - { - if (m_body1) - { - m_world->DestroyBody(m_body1); - m_body1 = nullptr; - } - - if (m_body2) - { - m_world->DestroyBody(m_body2); - m_body2 = nullptr; - } - - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position = b2Vec2(8.0f, 2.6f) + m_offset1; - bd.allowSleep = false; - m_body1 = m_world->CreateBody(&bd); - - b2PolygonShape shape; - shape.SetAsBox(0.5f, 1.0f); - - m_body1->CreateFixture(&shape, 1.0f); - } - - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position = b2Vec2(8.0f, 2.6f) + m_offset2; - bd.allowSleep = false; - m_body2 = m_world->CreateBody(&bd); - - b2PolygonShape shape; - shape.SetAsBox(0.5f, 1.0f); - - m_body2->CreateFixture(&shape, 1.0f); - } - } - - void CreateCircles() - { - if (m_body1) - { - m_world->DestroyBody(m_body1); - m_body1 = nullptr; - } - - if (m_body2) - { - m_world->DestroyBody(m_body2); - m_body2 = nullptr; - } - - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position = b2Vec2(-0.5f, 0.6f) + m_offset1; - bd.allowSleep = false; - m_body1 = m_world->CreateBody(&bd); - - b2CircleShape shape; - shape.m_radius = 0.5f; - - m_body1->CreateFixture(&shape, 1.0f); - } - - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position = b2Vec2(-0.5f, 0.6f) + m_offset2; - bd.allowSleep = false; - m_body2 = m_world->CreateBody(&bd); - - b2CircleShape shape; - shape.m_radius = 0.5f; - - m_body2->CreateFixture(&shape, 1.0f); - } - } - - void UpdateUI() override - { - // ImGui::SetNextWindowPos(ImVec2(10.0f, 100.0f)); - ImGui::SetNextWindowSize(ImVec2(200.0f, 100.0f)); - ImGui::Begin("Custom Controls", nullptr, ImGuiWindowFlags_NoResize); - - if (ImGui::RadioButton("Boxes", m_boxes == true)) - { - CreateBoxes(); - m_boxes = true; - } - - if (ImGui::RadioButton("Circles", m_boxes == false)) - { - CreateCircles(); - m_boxes = false; - } - - ImGui::End(); - } - - void Step(Settings& settings) override - { - // if (glfwGetKey(g_mainWindow, GLFW_KEY_A) == GLFW_PRESS) - //{ - // m_body1->ApplyForceToCenter(b2Vec2(-10.0f, 0.0f), true); - // m_body2->ApplyForceToCenter(b2Vec2(-10.0f, 0.0f), true); - // } - - // if (glfwGetKey(g_mainWindow, GLFW_KEY_D) == GLFW_PRESS) - //{ - // m_body1->ApplyForceToCenter(b2Vec2(10.0f, 0.0f), true); - // m_body2->ApplyForceToCenter(b2Vec2(10.0f, 0.0f), true); - // } - - Test::Step(settings); - } - - static Test* Create() { return new EdgeTest; } - - b2Vec2 m_offset1, m_offset2; - b2Body* m_body1; - b2Body* m_body2; - bool m_boxes; -}; - -static int testIndex = RegisterTest("Geometry", "Edge Test", EdgeTest::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/friction.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/friction.cpp deleted file mode 100644 index b935e0459c64..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/friction.cpp +++ /dev/null @@ -1,123 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -class Friction : public Test -{ -public: - Friction() - { - { - b2BodyDef bd; - b2Body* ground = m_world->CreateBody(&bd); - - b2EdgeShape shape; - shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f)); - ground->CreateFixture(&shape, 0.0f); - } - - { - b2PolygonShape shape; - shape.SetAsBox(13.0f, 0.25f); - - b2BodyDef bd; - bd.position.Set(-4.0f, 22.0f); - bd.angle = -0.25f; - - b2Body* ground = m_world->CreateBody(&bd); - ground->CreateFixture(&shape, 0.0f); - } - - { - b2PolygonShape shape; - shape.SetAsBox(0.25f, 1.0f); - - b2BodyDef bd; - bd.position.Set(10.5f, 19.0f); - - b2Body* ground = m_world->CreateBody(&bd); - ground->CreateFixture(&shape, 0.0f); - } - - { - b2PolygonShape shape; - shape.SetAsBox(13.0f, 0.25f); - - b2BodyDef bd; - bd.position.Set(4.0f, 14.0f); - bd.angle = 0.25f; - - b2Body* ground = m_world->CreateBody(&bd); - ground->CreateFixture(&shape, 0.0f); - } - - { - b2PolygonShape shape; - shape.SetAsBox(0.25f, 1.0f); - - b2BodyDef bd; - bd.position.Set(-10.5f, 11.0f); - - b2Body* ground = m_world->CreateBody(&bd); - ground->CreateFixture(&shape, 0.0f); - } - - { - b2PolygonShape shape; - shape.SetAsBox(13.0f, 0.25f); - - b2BodyDef bd; - bd.position.Set(-4.0f, 6.0f); - bd.angle = -0.25f; - - b2Body* ground = m_world->CreateBody(&bd); - ground->CreateFixture(&shape, 0.0f); - } - - { - b2PolygonShape shape; - shape.SetAsBox(0.5f, 0.5f); - - b2FixtureDef fd; - fd.shape = &shape; - fd.density = 25.0f; - - float friction[5] = {0.75f, 0.5f, 0.35f, 0.1f, 0.0f}; - - for (int i = 0; i < 5; ++i) - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(-15.0f + 4.0f * i, 28.0f); - b2Body* body = m_world->CreateBody(&bd); - - fd.friction = friction[i]; - body->CreateFixture(&fd); - } - } - } - - static Test* Create() { return new Friction; } -}; - -static int testIndex = RegisterTest("Forces", "Friction", Friction::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/gear_joint.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/gear_joint.cpp deleted file mode 100644 index d7e1b15771e2..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/gear_joint.cpp +++ /dev/null @@ -1,175 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -class GearJoint : public Test -{ -public: - GearJoint() - { - b2Body* ground = NULL; - { - b2BodyDef bd; - ground = m_world->CreateBody(&bd); - - b2EdgeShape shape; - shape.SetTwoSided(b2Vec2(50.0f, 0.0f), b2Vec2(-50.0f, 0.0f)); - ground->CreateFixture(&shape, 0.0f); - } - - { - b2CircleShape circle1; - circle1.m_radius = 1.0f; - - b2PolygonShape box; - box.SetAsBox(0.5f, 5.0f); - - b2CircleShape circle2; - circle2.m_radius = 2.0f; - - b2BodyDef bd1; - bd1.type = b2_staticBody; - bd1.position.Set(10.0f, 9.0f); - b2Body* body1 = m_world->CreateBody(&bd1); - body1->CreateFixture(&circle1, 5.0f); - - b2BodyDef bd2; - bd2.type = b2_dynamicBody; - bd2.position.Set(10.0f, 8.0f); - b2Body* body2 = m_world->CreateBody(&bd2); - body2->CreateFixture(&box, 5.0f); - - b2BodyDef bd3; - bd3.type = b2_dynamicBody; - bd3.position.Set(10.0f, 6.0f); - b2Body* body3 = m_world->CreateBody(&bd3); - body3->CreateFixture(&circle2, 5.0f); - - b2RevoluteJointDef jd1; - jd1.Initialize(body1, body2, bd1.position); - b2Joint* joint1 = m_world->CreateJoint(&jd1); - - b2RevoluteJointDef jd2; - jd2.Initialize(body2, body3, bd3.position); - b2Joint* joint2 = m_world->CreateJoint(&jd2); - - b2GearJointDef jd4; - jd4.bodyA = body1; - jd4.bodyB = body3; - jd4.joint1 = joint1; - jd4.joint2 = joint2; - jd4.ratio = circle2.m_radius / circle1.m_radius; - m_world->CreateJoint(&jd4); - } - - { - b2CircleShape circle1; - circle1.m_radius = 1.0f; - - b2CircleShape circle2; - circle2.m_radius = 2.0f; - - b2PolygonShape box; - box.SetAsBox(0.5f, 5.0f); - - b2BodyDef bd1; - bd1.type = b2_dynamicBody; - bd1.position.Set(-3.0f, 12.0f); - b2Body* body1 = m_world->CreateBody(&bd1); - body1->CreateFixture(&circle1, 5.0f); - - b2RevoluteJointDef jd1; - jd1.bodyA = ground; - jd1.bodyB = body1; - jd1.localAnchorA = ground->GetLocalPoint(bd1.position); - jd1.localAnchorB = body1->GetLocalPoint(bd1.position); - jd1.referenceAngle = body1->GetAngle() - ground->GetAngle(); - m_joint1 = (b2RevoluteJoint*)m_world->CreateJoint(&jd1); - - b2BodyDef bd2; - bd2.type = b2_dynamicBody; - bd2.position.Set(0.0f, 12.0f); - b2Body* body2 = m_world->CreateBody(&bd2); - body2->CreateFixture(&circle2, 5.0f); - - b2RevoluteJointDef jd2; - jd2.Initialize(ground, body2, bd2.position); - m_joint2 = (b2RevoluteJoint*)m_world->CreateJoint(&jd2); - - b2BodyDef bd3; - bd3.type = b2_dynamicBody; - bd3.position.Set(2.5f, 12.0f); - b2Body* body3 = m_world->CreateBody(&bd3); - body3->CreateFixture(&box, 5.0f); - - b2PrismaticJointDef jd3; - jd3.Initialize(ground, body3, bd3.position, b2Vec2(0.0f, 1.0f)); - jd3.lowerTranslation = -5.0f; - jd3.upperTranslation = 5.0f; - jd3.enableLimit = true; - - m_joint3 = (b2PrismaticJoint*)m_world->CreateJoint(&jd3); - - b2GearJointDef jd4; - jd4.bodyA = body1; - jd4.bodyB = body2; - jd4.joint1 = m_joint1; - jd4.joint2 = m_joint2; - jd4.ratio = circle2.m_radius / circle1.m_radius; - m_joint4 = (b2GearJoint*)m_world->CreateJoint(&jd4); - - b2GearJointDef jd5; - jd5.bodyA = body2; - jd5.bodyB = body3; - jd5.joint1 = m_joint2; - jd5.joint2 = m_joint3; - jd5.ratio = -1.0f / circle2.m_radius; - m_joint5 = (b2GearJoint*)m_world->CreateJoint(&jd5); - } - } - - void Step(Settings& settings) override - { - Test::Step(settings); - - float ratio, value; - - ratio = m_joint4->GetRatio(); - value = m_joint1->GetJointAngle() + ratio * m_joint2->GetJointAngle(); - DrawString(5, m_textLine, "theta1 + %4.2f * theta2 = %4.2f", (float)ratio, (float)value); - - ratio = m_joint5->GetRatio(); - value = m_joint2->GetJointAngle() + ratio * m_joint3->GetJointTranslation(); - DrawString(5, m_textLine, "theta2 + %4.2f * delta = %4.2f", (float)ratio, (float)value); - } - - static Test* Create() { return new GearJoint; } - - b2RevoluteJoint* m_joint1; - b2RevoluteJoint* m_joint2; - b2PrismaticJoint* m_joint3; - b2GearJoint* m_joint4; - b2GearJoint* m_joint5; -}; - -static int testIndex = RegisterTest("Joints", "Gear", GearJoint::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/heavy1.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/heavy1.cpp deleted file mode 100644 index 17ba6d183f23..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/heavy1.cpp +++ /dev/null @@ -1,57 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -class Heavy1 : public Test -{ -public: - Heavy1() - { - { - b2BodyDef bd; - b2Body* ground = m_world->CreateBody(&bd); - - b2EdgeShape shape; - shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f)); - ground->CreateFixture(&shape, 0.0f); - } - - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(0.0f, 0.5f); - b2Body* body = m_world->CreateBody(&bd); - - b2CircleShape shape; - shape.m_radius = 0.5f; - body->CreateFixture(&shape, 10.0f); - - bd.position.Set(0.0f, 6.0f); - body = m_world->CreateBody(&bd); - shape.m_radius = 5.0f; - body->CreateFixture(&shape, 10.0f); - } - - static Test* Create() { return new Heavy1; } -}; - -static int testIndex = RegisterTest("Solver", "Heavy 1", Heavy1::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/heavy2.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/heavy2.cpp deleted file mode 100644 index c34a36995eb8..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/heavy2.cpp +++ /dev/null @@ -1,90 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -class Heavy2 : public Test -{ -public: - Heavy2() - { - { - b2BodyDef bd; - b2Body* ground = m_world->CreateBody(&bd); - - b2EdgeShape shape; - shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f)); - ground->CreateFixture(&shape, 0.0f); - } - - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(0.0f, 2.5f); - b2Body* body = m_world->CreateBody(&bd); - - b2CircleShape shape; - shape.m_radius = 0.5f; - body->CreateFixture(&shape, 10.0f); - - bd.position.Set(0.0f, 3.5f); - body = m_world->CreateBody(&bd); - body->CreateFixture(&shape, 10.0f); - - m_heavy = NULL; - } - - void ToggleHeavy() - { - if (m_heavy) - { - m_world->DestroyBody(m_heavy); - m_heavy = NULL; - } - else - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(0.0f, 9.0f); - m_heavy = m_world->CreateBody(&bd); - - b2CircleShape shape; - shape.m_radius = 5.0f; - m_heavy->CreateFixture(&shape, 10.0f); - } - } - - void Keyboard(int key) override - { - switch (key) - { - case GLFW_KEY_H: - ToggleHeavy(); - break; - } - } - - static Test* Create() { return new Heavy2; } - - b2Body* m_heavy; -}; - -static int testIndex = RegisterTest("Solver", "Heavy 2", Heavy2::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/mobile_balanced.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/mobile_balanced.cpp deleted file mode 100644 index 8b68edd0b7c7..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/mobile_balanced.cpp +++ /dev/null @@ -1,104 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -class MobileBalanced : public Test -{ -public: - enum - { - e_depth = 4 - }; - - MobileBalanced() - { - b2Body* ground; - - // Create ground body. - { - b2BodyDef bodyDef; - bodyDef.position.Set(0.0f, 20.0f); - ground = m_world->CreateBody(&bodyDef); - } - - float a = 0.5f; - b2Vec2 h(0.0f, a); - - b2Body* root = AddNode(ground, b2Vec2_zero, 0, 3.0f, a); - - b2RevoluteJointDef jointDef; - jointDef.bodyA = ground; - jointDef.bodyB = root; - jointDef.localAnchorA.SetZero(); - jointDef.localAnchorB = h; - m_world->CreateJoint(&jointDef); - } - - b2Body* AddNode(b2Body* parent, const b2Vec2& localAnchor, int32 depth, float offset, float a) - { - float density = 20.0f; - b2Vec2 h(0.0f, a); - - b2Vec2 p = parent->GetPosition() + localAnchor - h; - - b2BodyDef bodyDef; - bodyDef.type = b2_dynamicBody; - bodyDef.position = p; - b2Body* body = m_world->CreateBody(&bodyDef); - - b2PolygonShape shape; - shape.SetAsBox(0.25f * a, a); - body->CreateFixture(&shape, density); - - if (depth == e_depth) - { - return body; - } - - shape.SetAsBox(offset, 0.25f * a, b2Vec2(0, -a), 0.0f); - body->CreateFixture(&shape, density); - - b2Vec2 a1 = b2Vec2(offset, -a); - b2Vec2 a2 = b2Vec2(-offset, -a); - b2Body* body1 = AddNode(body, a1, depth + 1, 0.5f * offset, a); - b2Body* body2 = AddNode(body, a2, depth + 1, 0.5f * offset, a); - - b2RevoluteJointDef jointDef; - jointDef.bodyA = body; - jointDef.localAnchorB = h; - - jointDef.localAnchorA = a1; - jointDef.bodyB = body1; - m_world->CreateJoint(&jointDef); - - jointDef.localAnchorA = a2; - jointDef.bodyB = body2; - m_world->CreateJoint(&jointDef); - - return body; - } - - static Test* Create() { return new MobileBalanced; } -}; - -static int testIndex = RegisterTest("Solver", "Mobile Balanced", MobileBalanced::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/mobile_unbalanced.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/mobile_unbalanced.cpp deleted file mode 100644 index 6d95738e5443..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/mobile_unbalanced.cpp +++ /dev/null @@ -1,101 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -class MobileUnbalanced : public Test -{ -public: - enum - { - e_depth = 4 - }; - - MobileUnbalanced() - { - b2Body* ground; - - // Create ground body. - { - b2BodyDef bodyDef; - bodyDef.position.Set(0.0f, 20.0f); - ground = m_world->CreateBody(&bodyDef); - } - - float a = 0.5f; - b2Vec2 h(0.0f, a); - - b2Body* root = AddNode(ground, b2Vec2_zero, 0, 3.0f, a); - - b2RevoluteJointDef jointDef; - jointDef.bodyA = ground; - jointDef.bodyB = root; - jointDef.localAnchorA.SetZero(); - jointDef.localAnchorB = h; - m_world->CreateJoint(&jointDef); - } - - b2Body* AddNode(b2Body* parent, const b2Vec2& localAnchor, int32 depth, float offset, float a) - { - float density = 20.0f; - b2Vec2 h(0.0f, a); - - b2Vec2 p = parent->GetPosition() + localAnchor - h; - - b2BodyDef bodyDef; - bodyDef.type = b2_dynamicBody; - bodyDef.position = p; - b2Body* body = m_world->CreateBody(&bodyDef); - - b2PolygonShape shape; - shape.SetAsBox(0.25f * a, a); - body->CreateFixture(&shape, density); - - if (depth == e_depth) - { - return body; - } - - b2Vec2 a1 = b2Vec2(offset, -a); - b2Vec2 a2 = b2Vec2(-offset, -a); - b2Body* body1 = AddNode(body, a1, depth + 1, 0.5f * offset, a); - b2Body* body2 = AddNode(body, a2, depth + 1, 0.5f * offset, a); - - b2RevoluteJointDef jointDef; - jointDef.bodyA = body; - jointDef.localAnchorB = h; - - jointDef.localAnchorA = a1; - jointDef.bodyB = body1; - m_world->CreateJoint(&jointDef); - - jointDef.localAnchorA = a2; - jointDef.bodyB = body2; - m_world->CreateJoint(&jointDef); - - return body; - } - - static Test* Create() { return new MobileUnbalanced; } -}; - -static int testIndex = RegisterTest("Solver", "Mobile Unbalanced", MobileUnbalanced::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/motor_joint.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/motor_joint.cpp deleted file mode 100644 index 1b4873ad0eda..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/motor_joint.cpp +++ /dev/null @@ -1,115 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "settings.h" -#include "test.h" - -/// This test shows how to use a motor joint. A motor joint -/// can be used to animate a dynamic body. With finite motor forces -/// the body can be blocked by collision with other bodies. -class MotorJoint : public Test -{ -public: - MotorJoint() - { - b2Body* ground = NULL; - { - b2BodyDef bd; - ground = m_world->CreateBody(&bd); - - b2EdgeShape shape; - shape.SetTwoSided(b2Vec2(-20.0f, 0.0f), b2Vec2(20.0f, 0.0f)); - - b2FixtureDef fd; - fd.shape = &shape; - - ground->CreateFixture(&fd); - } - - // Define motorized body - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(0.0f, 8.0f); - b2Body* body = m_world->CreateBody(&bd); - - b2PolygonShape shape; - shape.SetAsBox(2.0f, 0.5f); - - b2FixtureDef fd; - fd.shape = &shape; - fd.friction = 0.6f; - fd.density = 2.0f; - body->CreateFixture(&fd); - - b2MotorJointDef mjd; - mjd.Initialize(ground, body); - mjd.maxForce = 1000.0f; - mjd.maxTorque = 1000.0f; - m_joint = (b2MotorJoint*)m_world->CreateJoint(&mjd); - } - - m_go = false; - m_time = 0.0f; - } - - void Keyboard(int key) override - { - switch (key) - { - case GLFW_KEY_S: - m_go = !m_go; - break; - } - } - - void Step(Settings& settings) override - { - if (m_go && settings.m_hertz > 0.0f) - { - m_time += 1.0f / settings.m_hertz; - } - - b2Vec2 linearOffset; - linearOffset.x = 6.0f * sinf(2.0f * m_time); - linearOffset.y = 8.0f + 4.0f * sinf(1.0f * m_time); - - float angularOffset = 4.0f * m_time; - - m_joint->SetLinearOffset(linearOffset); - m_joint->SetAngularOffset(angularOffset); - - g_debugDraw.DrawPoint(linearOffset, 4.0f, b2Color(0.9f, 0.9f, 0.9f)); - - Test::Step(settings); - DrawString(5, m_textLine, "Keys: (s) pause"); - m_textLine += 15; - } - - static Test* Create() { return new MotorJoint; } - - b2MotorJoint* m_joint; - float m_time; - bool m_go; -}; - -static int testIndex = RegisterTest("Joints", "Motor Joint", MotorJoint::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/pinball.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/pinball.cpp deleted file mode 100644 index 1c117de53c0e..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/pinball.cpp +++ /dev/null @@ -1,165 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -/// This tests bullet collision and provides an example of a gameplay scenario. -/// This also uses a loop shape. -class Pinball : public Test -{ -public: - Pinball() - { - // Ground body - b2Body* ground = NULL; - { - b2BodyDef bd; - ground = m_world->CreateBody(&bd); - - b2Vec2 vs[5]; - vs[0].Set(-8.0f, 6.0f); - vs[1].Set(-8.0f, 20.0f); - vs[2].Set(8.0f, 20.0f); - vs[3].Set(8.0f, 6.0f); - vs[4].Set(0.0f, -2.0f); - - b2ChainShape loop; - loop.CreateLoop(vs, 5); - b2FixtureDef fd; - fd.shape = &loop; - fd.density = 0.0f; - ground->CreateFixture(&fd); - } - - // Flippers - { - b2Vec2 p1(-2.0f, 0.0f), p2(2.0f, 0.0f); - - b2BodyDef bd; - bd.type = b2_dynamicBody; - - bd.position = p1; - b2Body* leftFlipper = m_world->CreateBody(&bd); - - bd.position = p2; - b2Body* rightFlipper = m_world->CreateBody(&bd); - - b2PolygonShape box; - box.SetAsBox(1.75f, 0.1f); - - b2FixtureDef fd; - fd.shape = &box; - fd.density = 1.0f; - - leftFlipper->CreateFixture(&fd); - rightFlipper->CreateFixture(&fd); - - b2RevoluteJointDef jd; - jd.bodyA = ground; - jd.localAnchorB.SetZero(); - jd.enableMotor = true; - jd.maxMotorTorque = 1000.0f; - jd.enableLimit = true; - - jd.motorSpeed = 0.0f; - jd.localAnchorA = p1; - jd.bodyB = leftFlipper; - jd.lowerAngle = -30.0f * b2_pi / 180.0f; - jd.upperAngle = 5.0f * b2_pi / 180.0f; - m_leftJoint = (b2RevoluteJoint*)m_world->CreateJoint(&jd); - - jd.motorSpeed = 0.0f; - jd.localAnchorA = p2; - jd.bodyB = rightFlipper; - jd.lowerAngle = -5.0f * b2_pi / 180.0f; - jd.upperAngle = 30.0f * b2_pi / 180.0f; - m_rightJoint = (b2RevoluteJoint*)m_world->CreateJoint(&jd); - } - - // Circle character - { - b2BodyDef bd; - bd.position.Set(1.0f, 15.0f); - bd.type = b2_dynamicBody; - bd.bullet = true; - - m_ball = m_world->CreateBody(&bd); - - b2CircleShape shape; - shape.m_radius = 0.2f; - - b2FixtureDef fd; - fd.shape = &shape; - fd.density = 1.0f; - m_ball->CreateFixture(&fd); - } - - m_button = false; - } - - void Step(Settings& settings) override - { - if (m_button) - { - m_leftJoint->SetMotorSpeed(20.0f); - m_rightJoint->SetMotorSpeed(-20.0f); - } - else - { - m_leftJoint->SetMotorSpeed(-10.0f); - m_rightJoint->SetMotorSpeed(10.0f); - } - - Test::Step(settings); - - DrawString(5, m_textLine, "Press 'a' to control the flippers"); - } - - void Keyboard(int key) override - { - switch (key) - { - case GLFW_KEY_A: - m_button = true; - break; - } - } - - void KeyboardUp(int key) override - { - switch (key) - { - case GLFW_KEY_A: - m_button = false; - break; - } - } - - static Test* Create() { return new Pinball; } - - b2RevoluteJoint* m_leftJoint; - b2RevoluteJoint* m_rightJoint; - b2Body* m_ball; - bool m_button; -}; - -static int testIndex = RegisterTest("Examples", "Pinball", Pinball::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/platformer.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/platformer.cpp deleted file mode 100644 index c308f4922bac..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/platformer.cpp +++ /dev/null @@ -1,128 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -class Platformer : public Test -{ -public: - enum State - { - e_unknown, - e_above, - e_below - }; - - Platformer() - { - // Ground - { - b2BodyDef bd; - b2Body* ground = m_world->CreateBody(&bd); - - b2EdgeShape shape; - shape.SetTwoSided(b2Vec2(-20.0f, 0.0f), b2Vec2(20.0f, 0.0f)); - ground->CreateFixture(&shape, 0.0f); - } - - // Platform - { - b2BodyDef bd; - bd.position.Set(0.0f, 10.0f); - b2Body* body = m_world->CreateBody(&bd); - - b2PolygonShape shape; - shape.SetAsBox(3.0f, 0.5f); - m_platform = body->CreateFixture(&shape, 0.0f); - - m_bottom = 10.0f - 0.5f; - m_top = 10.0f + 0.5f; - } - - // Actor - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(0.0f, 12.0f); - b2Body* body = m_world->CreateBody(&bd); - - m_radius = 0.5f; - b2CircleShape shape; - shape.m_radius = m_radius; - m_character = body->CreateFixture(&shape, 20.0f); - - body->SetLinearVelocity(b2Vec2(0.0f, -50.0f)); - - m_state = e_unknown; - } - } - - void PreSolve(b2Contact* contact, const b2Manifold* oldManifold) override - { - Test::PreSolve(contact, oldManifold); - - b2Fixture* fixtureA = contact->GetFixtureA(); - b2Fixture* fixtureB = contact->GetFixtureB(); - - if (fixtureA != m_platform && fixtureA != m_character) - { - return; - } - - if (fixtureB != m_platform && fixtureB != m_character) - { - return; - } - -#if 1 - b2Vec2 position = m_character->GetBody()->GetPosition(); - - if (position.y < m_top + m_radius - 3.0f * b2_linearSlop) - { - contact->SetEnabled(false); - } -#else - b2Vec2 v = m_character->GetBody()->GetLinearVelocity(); - if (v.y > 0.0f) - { - contact->SetEnabled(false); - } -#endif - } - - void Step(Settings& settings) override - { - Test::Step(settings); - - b2Vec2 v = m_character->GetBody()->GetLinearVelocity(); - DrawString(5, m_textLine, "Character Linear Velocity: %f", v.y); - } - - static Test* Create() { return new Platformer; } - - float m_radius, m_top, m_bottom; - State m_state; - b2Fixture* m_platform; - b2Fixture* m_character; -}; - -static int testIndex = RegisterTest("Examples", "Platformer", Platformer::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/polygon_collision.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/polygon_collision.cpp deleted file mode 100644 index dbc924694c2d..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/polygon_collision.cpp +++ /dev/null @@ -1,123 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -class PolygonCollision : public Test -{ -public: - PolygonCollision() - { - { - m_polygonA.SetAsBox(0.2f, 0.4f); - m_transformA.Set(b2Vec2(0.0f, 0.0f), 0.0f); - } - - { - m_polygonB.SetAsBox(0.5f, 0.5f); - m_positionB.Set(19.345284f, 1.5632932f); - m_angleB = 1.9160721f; - m_transformB.Set(m_positionB, m_angleB); - } - } - - static Test* Create() { return new PolygonCollision; } - - void Step(Settings& settings) override - { - B2_NOT_USED(settings); - - b2Manifold manifold; - b2CollidePolygons(&manifold, &m_polygonA, m_transformA, &m_polygonB, m_transformB); - - b2WorldManifold worldManifold; - worldManifold.Initialize(&manifold, m_transformA, m_polygonA.m_radius, m_transformB, m_polygonB.m_radius); - - DrawString(5, m_textLine, "point count = %d", manifold.pointCount); - - { - b2Color color(0.9f, 0.9f, 0.9f); - b2Vec2 v[b2_maxPolygonVertices]; - for (int32 i = 0; i < m_polygonA.m_count; ++i) - { - v[i] = b2Mul(m_transformA, m_polygonA.m_vertices[i]); - } - g_debugDraw.DrawPolygon(v, m_polygonA.m_count, color); - - for (int32 i = 0; i < m_polygonB.m_count; ++i) - { - v[i] = b2Mul(m_transformB, m_polygonB.m_vertices[i]); - } - g_debugDraw.DrawPolygon(v, m_polygonB.m_count, color); - } - - for (int32 i = 0; i < manifold.pointCount; ++i) - { - g_debugDraw.DrawPoint(worldManifold.points[i], 4.0f, b2Color(0.9f, 0.3f, 0.3f)); - } - - Test::Step(settings); - } - - void Keyboard(int key) override - { - switch (key) - { - case GLFW_KEY_A: - m_positionB.x -= 0.1f; - break; - - case GLFW_KEY_D: - m_positionB.x += 0.1f; - break; - - case GLFW_KEY_S: - m_positionB.y -= 0.1f; - break; - - case GLFW_KEY_W: - m_positionB.y += 0.1f; - break; - - case GLFW_KEY_Q: - m_angleB += 0.1f * b2_pi; - break; - - case GLFW_KEY_E: - m_angleB -= 0.1f * b2_pi; - break; - } - - m_transformB.Set(m_positionB, m_angleB); - } - - b2PolygonShape m_polygonA; - b2PolygonShape m_polygonB; - - b2Transform m_transformA; - b2Transform m_transformB; - - b2Vec2 m_positionB; - float m_angleB; -}; - -static int testIndex = RegisterTest("Geometry", "Polygon Collision", PolygonCollision::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/polygon_shapes.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/polygon_shapes.cpp deleted file mode 100644 index 369f777ee85c..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/polygon_shapes.cpp +++ /dev/null @@ -1,257 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -/// This tests stacking. It also shows how to use b2World::Query -/// and b2TestOverlap. - -/// This callback is called by b2World::QueryAABB. We find all the fixtures -/// that overlap an AABB. Of those, we use b2TestOverlap to determine which fixtures -/// overlap a circle. Up to 4 overlapped fixtures will be highlighted with a yellow border. -class PolygonShapesCallback : public b2QueryCallback -{ -public: - enum - { - e_maxCount = 4 - }; - - PolygonShapesCallback() { m_count = 0; } - - /// Called for each fixture found in the query AABB. - /// @return false to terminate the query. - bool ReportFixture(b2Fixture* fixture) override - { - if (m_count == e_maxCount) - { - return false; - } - - b2Body* body = fixture->GetBody(); - b2Shape* shape = fixture->GetShape(); - - bool overlap = b2TestOverlap(shape, 0, &m_circle, 0, body->GetTransform(), m_transform); - - if (overlap) - { - b2Color color(0.95f, 0.95f, 0.6f); - b2Vec2 center = body->GetWorldCenter(); - g_debugDraw->DrawPoint(center, 5.0f, color); - ++m_count; - } - - return true; - } - - b2CircleShape m_circle; - b2Transform m_transform; - b2Draw* g_debugDraw; - int32 m_count; -}; - -class PolygonShapes : public Test -{ -public: - enum - { - e_maxBodies = 256 - }; - - PolygonShapes() - { - // Ground body - { - b2BodyDef bd; - b2Body* ground = m_world->CreateBody(&bd); - - b2EdgeShape shape; - shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f)); - ground->CreateFixture(&shape, 0.0f); - } - - { - b2Vec2 vertices[3]; - vertices[0].Set(-0.5f, 0.0f); - vertices[1].Set(0.5f, 0.0f); - vertices[2].Set(0.0f, 1.5f); - m_polygons[0].Set(vertices, 3); - } - - { - b2Vec2 vertices[3]; - vertices[0].Set(-0.1f, 0.0f); - vertices[1].Set(0.1f, 0.0f); - vertices[2].Set(0.0f, 1.5f); - m_polygons[1].Set(vertices, 3); - } - - { - float w = 1.0f; - float b = w / (2.0f + b2Sqrt(2.0f)); - float s = b2Sqrt(2.0f) * b; - - b2Vec2 vertices[8]; - vertices[0].Set(0.5f * s, 0.0f); - vertices[1].Set(0.5f * w, b); - vertices[2].Set(0.5f * w, b + s); - vertices[3].Set(0.5f * s, w); - vertices[4].Set(-0.5f * s, w); - vertices[5].Set(-0.5f * w, b + s); - vertices[6].Set(-0.5f * w, b); - vertices[7].Set(-0.5f * s, 0.0f); - - m_polygons[2].Set(vertices, 8); - } - - { - m_polygons[3].SetAsBox(0.5f, 0.5f); - } - - { - m_circle.m_radius = 0.5f; - } - - m_bodyIndex = 0; - memset(m_bodies, 0, sizeof(m_bodies)); - } - - void Create(int32 index) - { - if (m_bodies[m_bodyIndex] != NULL) - { - m_world->DestroyBody(m_bodies[m_bodyIndex]); - m_bodies[m_bodyIndex] = NULL; - } - - b2BodyDef bd; - bd.type = b2_dynamicBody; - - float x = RandomFloat(-2.0f, 2.0f); - bd.position.Set(x, 10.0f); - bd.angle = RandomFloat(-b2_pi, b2_pi); - - if (index == 4) - { - bd.angularDamping = 0.02f; - } - - m_bodies[m_bodyIndex] = m_world->CreateBody(&bd); - - if (index < 4) - { - b2FixtureDef fd; - fd.shape = m_polygons + index; - fd.density = 1.0f; - fd.friction = 0.3f; - m_bodies[m_bodyIndex]->CreateFixture(&fd); - } - else - { - b2FixtureDef fd; - fd.shape = &m_circle; - fd.density = 1.0f; - fd.friction = 0.3f; - - m_bodies[m_bodyIndex]->CreateFixture(&fd); - } - - m_bodyIndex = (m_bodyIndex + 1) % e_maxBodies; - } - - void DestroyBody() - { - for (int32 i = 0; i < e_maxBodies; ++i) - { - if (m_bodies[i] != NULL) - { - m_world->DestroyBody(m_bodies[i]); - m_bodies[i] = NULL; - return; - } - } - } - - void Keyboard(int key) override - { - switch (key) - { - case GLFW_KEY_1: - case GLFW_KEY_2: - case GLFW_KEY_3: - case GLFW_KEY_4: - case GLFW_KEY_5: - Create(key - GLFW_KEY_1); - break; - - case GLFW_KEY_A: - for (int32 i = 0; i < e_maxBodies; i += 2) - { - if (m_bodies[i]) - { - bool enabled = m_bodies[i]->IsEnabled(); - m_bodies[i]->SetEnabled(!enabled); - } - } - break; - - case GLFW_KEY_D: - DestroyBody(); - break; - } - } - - void Step(Settings& settings) override - { - Test::Step(settings); - - PolygonShapesCallback callback; - callback.m_circle.m_radius = 2.0f; - callback.m_circle.m_p.Set(0.0f, 1.1f); - callback.m_transform.SetIdentity(); - callback.g_debugDraw = &g_debugDraw; - - b2AABB aabb; - callback.m_circle.ComputeAABB(&aabb, callback.m_transform, 0); - - m_world->QueryAABB(&callback, aabb); - - b2Color color(0.4f, 0.7f, 0.8f); - g_debugDraw.DrawCircle(callback.m_circle.m_p, callback.m_circle.m_radius, color); - - DrawString(5, m_textLine, "Press 1-5 to drop stuff, maximum of %d overlaps detected", - PolygonShapesCallback::e_maxCount); - - DrawString(5, m_textLine, "Press 'a' to enable/disable some bodies"); - - DrawString(5, m_textLine, "Press 'd' to destroy a body"); - } - - static Test* Create() { return new PolygonShapes; } - - int32 m_bodyIndex; - b2Body* m_bodies[e_maxBodies]; - b2PolygonShape m_polygons[4]; - b2CircleShape m_circle; -}; - -static int testIndex = RegisterTest("Geometry", "Polygon Shapes", PolygonShapes::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/prismatic_joint.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/prismatic_joint.cpp deleted file mode 100644 index c9d05bed0c04..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/prismatic_joint.cpp +++ /dev/null @@ -1,114 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "settings.h" -#include "test.h" -#include "ImGui/ImGuiPresenter.h" - -// Test the prismatic joint with limits and motor options. -class PrismaticJoint : public Test -{ -public: - PrismaticJoint() - { - b2Body* ground = NULL; - { - b2BodyDef bd; - ground = m_world->CreateBody(&bd); - - b2EdgeShape shape; - shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f)); - ground->CreateFixture(&shape, 0.0f); - } - - m_enableLimit = true; - m_enableMotor = false; - m_motorSpeed = 10.0f; - - { - b2PolygonShape shape; - shape.SetAsBox(1.0f, 1.0f); - - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(0.0f, 10.0f); - bd.angle = 0.5f * b2_pi; - bd.allowSleep = false; - b2Body* body = m_world->CreateBody(&bd); - body->CreateFixture(&shape, 5.0f); - - b2PrismaticJointDef pjd; - - // Horizontal - pjd.Initialize(ground, body, bd.position, b2Vec2(1.0f, 0.0f)); - - pjd.motorSpeed = m_motorSpeed; - pjd.maxMotorForce = 10000.0f; - pjd.enableMotor = m_enableMotor; - pjd.lowerTranslation = -10.0f; - pjd.upperTranslation = 10.0f; - pjd.enableLimit = m_enableLimit; - - m_joint = (b2PrismaticJoint*)m_world->CreateJoint(&pjd); - } - } - - void UpdateUI() override - { - // ImGui::SetNextWindowPos(ImVec2(10.0f, 100.0f)); - ImGui::SetNextWindowSize(ImVec2(200.0f, 100.0f)); - ImGui::Begin("Joint Controls", nullptr, ImGuiWindowFlags_NoResize); - - if (ImGui::Checkbox("Limit", &m_enableLimit)) - { - m_joint->EnableLimit(m_enableLimit); - } - - if (ImGui::Checkbox("Motor", &m_enableMotor)) - { - m_joint->EnableMotor(m_enableMotor); - } - - if (ImGui::SliderFloat("Speed", &m_motorSpeed, -100.0f, 100.0f, "%.0f")) - { - m_joint->SetMotorSpeed(m_motorSpeed); - } - - ImGui::End(); - } - - void Step(Settings& settings) override - { - Test::Step(settings); - float force = m_joint->GetMotorForce(settings.m_hertz); - DrawString(5, m_textLine, "Motor Force = %4.0f", force); - } - - static Test* Create() { return new PrismaticJoint; } - - b2PrismaticJoint* m_joint; - float m_motorSpeed; - bool m_enableMotor; - bool m_enableLimit; -}; - -static int testIndex = RegisterTest("Joints", "Prismatic", PrismaticJoint::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/pulley_joint.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/pulley_joint.cpp deleted file mode 100644 index b724673d148a..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/pulley_joint.cpp +++ /dev/null @@ -1,92 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -class PulleyJoint : public Test -{ -public: - PulleyJoint() - { - float y = 16.0f; - float L = 12.0f; - float a = 1.0f; - float b = 2.0f; - - b2Body* ground = NULL; - { - b2BodyDef bd; - ground = m_world->CreateBody(&bd); - - b2CircleShape circle; - circle.m_radius = 2.0f; - - circle.m_p.Set(-10.0f, y + b + L); - ground->CreateFixture(&circle, 0.0f); - - circle.m_p.Set(10.0f, y + b + L); - ground->CreateFixture(&circle, 0.0f); - } - - { - - b2PolygonShape shape; - shape.SetAsBox(a, b); - - b2BodyDef bd; - bd.type = b2_dynamicBody; - - // bd.fixedRotation = true; - bd.position.Set(-10.0f, y); - b2Body* body1 = m_world->CreateBody(&bd); - body1->CreateFixture(&shape, 5.0f); - - bd.position.Set(10.0f, y); - b2Body* body2 = m_world->CreateBody(&bd); - body2->CreateFixture(&shape, 5.0f); - - b2PulleyJointDef pulleyDef; - b2Vec2 anchor1(-10.0f, y + b); - b2Vec2 anchor2(10.0f, y + b); - b2Vec2 groundAnchor1(-10.0f, y + b + L); - b2Vec2 groundAnchor2(10.0f, y + b + L); - pulleyDef.Initialize(body1, body2, groundAnchor1, groundAnchor2, anchor1, anchor2, 1.5f); - - m_joint1 = (b2PulleyJoint*)m_world->CreateJoint(&pulleyDef); - } - } - - void Step(Settings& settings) override - { - Test::Step(settings); - - float ratio = m_joint1->GetRatio(); - float L = m_joint1->GetCurrentLengthA() + ratio * m_joint1->GetCurrentLengthB(); - DrawString(5, m_textLine, "L1 + %4.2f * L2 = %4.2f", (float)ratio, (float)L); - } - - static Test* Create() { return new PulleyJoint; } - - b2PulleyJoint* m_joint1; -}; - -static int testIndex = RegisterTest("Joints", "Pulley", PulleyJoint::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/pyramid.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/pyramid.cpp deleted file mode 100644 index 5f527451601a..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/pyramid.cpp +++ /dev/null @@ -1,89 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -class Pyramid : public Test -{ -public: - enum - { - e_count = 20 - }; - - Pyramid() - { - { - b2BodyDef bd; - b2Body* ground = m_world->CreateBody(&bd); - - b2EdgeShape shape; - shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f)); - ground->CreateFixture(&shape, 0.0f); - } - - { - float a = 0.5f; - b2PolygonShape shape; - shape.SetAsBox(a, a); - - b2Vec2 x(-7.0f, 0.75f); - b2Vec2 y; - b2Vec2 deltaX(0.5625f, 1.25f); - b2Vec2 deltaY(1.125f, 0.0f); - - for (int32 i = 0; i < e_count; ++i) - { - y = x; - - for (int32 j = i; j < e_count; ++j) - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position = y; - b2Body* body = m_world->CreateBody(&bd); - body->CreateFixture(&shape, 5.0f); - - y += deltaY; - } - - x += deltaX; - } - } - } - - void Step(Settings& settings) override - { - Test::Step(settings); - - // b2DynamicTree* tree = &m_world->m_contactManager.m_broadPhase.m_tree; - - // if (m_stepCount == 400) - //{ - // tree->RebuildBottomUp(); - // } - } - - static Test* Create() { return new Pyramid; } -}; - -static int testIndex = RegisterTest("Stacking", "Pyramid", Pyramid::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/ray_cast.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/ray_cast.cpp deleted file mode 100644 index 81697a8542af..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/ray_cast.cpp +++ /dev/null @@ -1,463 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "settings.h" -#include "test.h" -#include "ImGui/ImGuiPresenter.h" - -enum -{ - e_maxBodies = 256 -}; - -// This test demonstrates how to use the world ray-cast feature. -// NOTE: we are intentionally filtering one of the polygons, therefore -// the ray will always miss one type of polygon. - -// This callback finds the closest hit. Polygon 0 is filtered. -class RayCastClosestCallback : public b2RayCastCallback -{ -public: - RayCastClosestCallback() { m_hit = false; } - - float ReportFixture(b2Fixture* fixture, const b2Vec2& point, const b2Vec2& normal, float fraction) override - { - uintptr_t index = fixture->GetUserData().pointer; - if (index == 1) - { - // By returning -1, we instruct the calling code to ignore this fixture and - // continue the ray-cast to the next fixture. - return -1.0f; - } - - m_hit = true; - m_point = point; - m_normal = normal; - - // By returning the current fraction, we instruct the calling code to clip the ray and - // continue the ray-cast to the next fixture. WARNING: do not assume that fixtures - // are reported in order. However, by clipping, we can always get the closest fixture. - return fraction; - } - - bool m_hit; - b2Vec2 m_point; - b2Vec2 m_normal; -}; - -// This callback finds any hit. Polygon 0 is filtered. For this type of query we are usually -// just checking for obstruction, so the actual fixture and hit point are irrelevant. -class RayCastAnyCallback : public b2RayCastCallback -{ -public: - RayCastAnyCallback() { m_hit = false; } - - float ReportFixture(b2Fixture* fixture, const b2Vec2& point, const b2Vec2& normal, float) override - { - uintptr_t index = fixture->GetUserData().pointer; - if (index == 1) - { - // By returning -1, we instruct the calling code to ignore this fixture and - // continue the ray-cast to the next fixture. - return -1.0f; - } - - m_hit = true; - m_point = point; - m_normal = normal; - - // At this point we have a hit, so we know the ray is obstructed. - // By returning 0, we instruct the calling code to terminate the ray-cast. - return 0.0f; - } - - bool m_hit; - b2Vec2 m_point; - b2Vec2 m_normal; -}; - -// This ray cast collects multiple hits along the ray. Polygon 0 is filtered. -// The fixtures are not necessary reported in order, so we might not capture -// the closest fixture. -class RayCastMultipleCallback : public b2RayCastCallback -{ -public: - enum - { - e_maxCount = 3 - }; - - RayCastMultipleCallback() { m_count = 0; } - - float ReportFixture(b2Fixture* fixture, const b2Vec2& point, const b2Vec2& normal, float) override - { - uintptr_t index = fixture->GetUserData().pointer; - if (index == 1) - { - // By returning -1, we instruct the calling code to ignore this fixture and - // continue the ray-cast to the next fixture. - return -1.0f; - } - - b2Assert(m_count < e_maxCount); - - m_points[m_count] = point; - m_normals[m_count] = normal; - ++m_count; - - if (m_count == e_maxCount) - { - // At this point the buffer is full. - // By returning 0, we instruct the calling code to terminate the ray-cast. - return 0.0f; - } - - // By returning 1, we instruct the caller to continue without clipping the ray. - return 1.0f; - } - - b2Vec2 m_points[e_maxCount]; - b2Vec2 m_normals[e_maxCount]; - int32 m_count; -}; - -class RayCast : public Test -{ -public: - enum Mode - { - e_any = 0, - e_closest = 1, - e_multiple = 2 - }; - - RayCast() - { - // Ground body - { - b2BodyDef bd; - b2Body* ground = m_world->CreateBody(&bd); - - b2EdgeShape shape; - shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f)); - ground->CreateFixture(&shape, 0.0f); - } - - { - b2Vec2 vertices[3]; - vertices[0].Set(-0.5f, 0.0f); - vertices[1].Set(0.5f, 0.0f); - vertices[2].Set(0.0f, 1.5f); - m_polygons[0].Set(vertices, 3); - } - - { - b2Vec2 vertices[3]; - vertices[0].Set(-0.1f, 0.0f); - vertices[1].Set(0.1f, 0.0f); - vertices[2].Set(0.0f, 1.5f); - m_polygons[1].Set(vertices, 3); - } - - { - float w = 1.0f; - float b = w / (2.0f + b2Sqrt(2.0f)); - float s = b2Sqrt(2.0f) * b; - - b2Vec2 vertices[8]; - vertices[0].Set(0.5f * s, 0.0f); - vertices[1].Set(0.5f * w, b); - vertices[2].Set(0.5f * w, b + s); - vertices[3].Set(0.5f * s, w); - vertices[4].Set(-0.5f * s, w); - vertices[5].Set(-0.5f * w, b + s); - vertices[6].Set(-0.5f * w, b); - vertices[7].Set(-0.5f * s, 0.0f); - - m_polygons[2].Set(vertices, 8); - } - - { - m_polygons[3].SetAsBox(0.5f, 0.5f); - } - - { - m_circle.m_radius = 0.5f; - } - - { - m_edge.SetTwoSided(b2Vec2(-1.0f, 0.0f), b2Vec2(1.0f, 0.0f)); - } - - m_bodyIndex = 0; - memset(m_bodies, 0, sizeof(m_bodies)); - - m_degrees = 0.0f; - - m_mode = e_closest; - } - - void Create(int32 index) - { - if (m_bodies[m_bodyIndex] != NULL) - { - m_world->DestroyBody(m_bodies[m_bodyIndex]); - m_bodies[m_bodyIndex] = NULL; - } - - b2BodyDef bd; - - float x = RandomFloat(-10.0f, 10.0f); - float y = RandomFloat(0.0f, 20.0f); - bd.position.Set(x, y); - bd.angle = RandomFloat(-b2_pi, b2_pi); - - if (index == 4) - { - bd.angularDamping = 0.02f; - } - - m_bodies[m_bodyIndex] = m_world->CreateBody(&bd); - - if (index < 4) - { - b2FixtureDef fd; - fd.shape = m_polygons + index; - fd.friction = 0.3f; - fd.userData.pointer = index + 1; - m_bodies[m_bodyIndex]->CreateFixture(&fd); - } - else if (index < 5) - { - b2FixtureDef fd; - fd.shape = &m_circle; - fd.friction = 0.3f; - fd.userData.pointer = index + 1; - m_bodies[m_bodyIndex]->CreateFixture(&fd); - } - else - { - b2FixtureDef fd; - fd.shape = &m_edge; - fd.friction = 0.3f; - fd.userData.pointer = index + 1; - - m_bodies[m_bodyIndex]->CreateFixture(&fd); - } - - m_bodyIndex = (m_bodyIndex + 1) % e_maxBodies; - } - - void DestroyBody() - { - for (int32 i = 0; i < e_maxBodies; ++i) - { - if (m_bodies[i] != NULL) - { - m_world->DestroyBody(m_bodies[i]); - m_bodies[i] = NULL; - return; - } - } - } - - void UpdateUI() override - { - // ImGui::SetNextWindowPos(ImVec2(g_debugDrawTestBed.debugNodeOffset.x, g_debugDrawTestBed.debugNodeOffset.y)); - ImGui::SetNextWindowSize(ImVec2(210.0f, 285.0f)); - ImGui::Begin("Ray-cast Controls", nullptr, ImGuiWindowFlags_NoResize); - - if (ImGui::Button("Shape 1")) - { - Create(0); - } - - if (ImGui::Button("Shape 2")) - { - Create(1); - } - - if (ImGui::Button("Shape 3")) - { - Create(2); - } - - if (ImGui::Button("Shape 4")) - { - Create(3); - } - - if (ImGui::Button("Shape 5")) - { - Create(4); - } - - if (ImGui::Button("Shape 6")) - { - Create(5); - } - - if (ImGui::Button("Destroy Shape")) - { - DestroyBody(); - } - - ImGui::RadioButton("Any", &m_mode, e_any); - ImGui::RadioButton("Closest", &m_mode, e_closest); - ImGui::RadioButton("Multiple", &m_mode, e_multiple); - - ImGui::SliderFloat("Angle", &m_degrees, 0.0f, 360.0f, "%.0f"); - - ImGui::End(); - } - - void Step(Settings& settings) override - { - Test::Step(settings); - - DrawString(5, m_textLine, "Shape 1 is intentionally ignored by the ray"); - - switch (m_mode) - { - case e_closest: - DrawString(5, m_textLine, "Ray-cast mode: closest - find closest fixture along the ray"); - break; - - case e_any: - DrawString(5, m_textLine, "Ray-cast mode: any - check for obstruction"); - break; - - case e_multiple: - DrawString(5, m_textLine, "Ray-cast mode: multiple - gather multiple fixtures"); - break; - } - - float angle = b2_pi * m_degrees / 180.0f; - float L = 11.0f; - b2Vec2 point1(0.0f, 10.0f); - b2Vec2 d(L * cosf(angle), L * sinf(angle)); - b2Vec2 point2 = point1 + d; - - if (m_mode == e_closest) - { - RayCastClosestCallback callback; - m_world->RayCast(&callback, point1, point2); - - if (callback.m_hit) - { - g_debugDraw.DrawPoint(callback.m_point, 5.0f, b2Color(0.4f, 0.9f, 0.4f)); - g_debugDraw.DrawSegment(point1, callback.m_point, b2Color(0.8f, 0.8f, 0.8f)); - b2Vec2 head = callback.m_point + 0.5f * callback.m_normal; - g_debugDraw.DrawSegment(callback.m_point, head, b2Color(0.9f, 0.9f, 0.4f)); - } - else - { - g_debugDraw.DrawSegment(point1, point2, b2Color(0.8f, 0.8f, 0.8f)); - } - } - else if (m_mode == e_any) - { - RayCastAnyCallback callback; - m_world->RayCast(&callback, point1, point2); - - if (callback.m_hit) - { - g_debugDraw.DrawPoint(callback.m_point, 5.0f, b2Color(0.4f, 0.9f, 0.4f)); - g_debugDraw.DrawSegment(point1, callback.m_point, b2Color(0.8f, 0.8f, 0.8f)); - b2Vec2 head = callback.m_point + 0.5f * callback.m_normal; - g_debugDraw.DrawSegment(callback.m_point, head, b2Color(0.9f, 0.9f, 0.4f)); - } - else - { - g_debugDraw.DrawSegment(point1, point2, b2Color(0.8f, 0.8f, 0.8f)); - } - } - else if (m_mode == e_multiple) - { - RayCastMultipleCallback callback; - m_world->RayCast(&callback, point1, point2); - g_debugDraw.DrawSegment(point1, point2, b2Color(0.8f, 0.8f, 0.8f)); - - for (int32 i = 0; i < callback.m_count; ++i) - { - b2Vec2 p = callback.m_points[i]; - b2Vec2 n = callback.m_normals[i]; - g_debugDraw.DrawPoint(p, 5.0f, b2Color(0.4f, 0.9f, 0.4f)); - g_debugDraw.DrawSegment(point1, p, b2Color(0.8f, 0.8f, 0.8f)); - b2Vec2 head = p + 0.5f * n; - g_debugDraw.DrawSegment(p, head, b2Color(0.9f, 0.9f, 0.4f)); - } - } - -#if 0 - // This case was failing. - { - b2Vec2 vertices[4]; - //vertices[0].Set(-22.875f, -3.0f); - //vertices[1].Set(22.875f, -3.0f); - //vertices[2].Set(22.875f, 3.0f); - //vertices[3].Set(-22.875f, 3.0f); - - b2PolygonShape shape; - //shape.Set(vertices, 4); - shape.SetAsBox(22.875f, 3.0f); - - b2RayCastInput input; - input.p1.Set(10.2725f,1.71372f); - input.p2.Set(10.2353f,2.21807f); - //input.maxFraction = 0.567623f; - input.maxFraction = 0.56762173f; - - b2Transform xf; - xf.SetIdentity(); - xf.position.Set(23.0f, 5.0f); - - b2RayCastOutput output; - bool hit; - hit = shape.RayCast(&output, input, xf); - hit = false; - - b2Color color(1.0f, 1.0f, 1.0f); - b2Vec2 vs[4]; - for (int32 i = 0; i < 4; ++i) - { - vs[i] = b2Mul(xf, shape.m_vertices[i]); - } - - g_debugDraw.DrawPolygon(vs, 4, color); - g_debugDraw.DrawSegment(input.p1, input.p2, color); - } -#endif - } - - static Test* Create() { return new RayCast; } - - int32 m_bodyIndex; - b2Body* m_bodies[e_maxBodies]; - b2PolygonShape m_polygons[4]; - b2CircleShape m_circle; - b2EdgeShape m_edge; - float m_degrees; - int32 m_mode; -}; - -static int testIndex = RegisterTest("Collision", "Ray Cast", RayCast::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/restitution.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/restitution.cpp deleted file mode 100644 index 10dd861e57f5..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/restitution.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -// Note: even with a restitution of 1.0, there is some energy change -// due to position correction. -class Restitution : public Test -{ -public: - Restitution() - { - const float threshold = 10.0f; - - { - b2BodyDef bd; - b2Body* ground = m_world->CreateBody(&bd); - - b2EdgeShape shape; - shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f)); - - b2FixtureDef fd; - fd.shape = &shape; - fd.restitutionThreshold = threshold; - ground->CreateFixture(&fd); - } - - { - b2CircleShape shape; - shape.m_radius = 1.0f; - - b2FixtureDef fd; - fd.shape = &shape; - fd.density = 1.0f; - - float restitution[7] = {0.0f, 0.1f, 0.3f, 0.5f, 0.75f, 0.9f, 1.0f}; - - for (int32 i = 0; i < 7; ++i) - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(-10.0f + 3.0f * i, 20.0f); - - b2Body* body = m_world->CreateBody(&bd); - - fd.restitution = restitution[i]; - fd.restitutionThreshold = threshold; - body->CreateFixture(&fd); - } - } - } - - static Test* Create() { return new Restitution; } -}; - -static int testIndex = RegisterTest("Forces", "Restitution", Restitution::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/revolute_joint.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/revolute_joint.cpp deleted file mode 100644 index 5c29859d833e..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/revolute_joint.cpp +++ /dev/null @@ -1,157 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "settings.h" -#include "test.h" -#include "ImGui/ImGuiPresenter.h" - -class RevoluteJoint : public Test -{ -public: - RevoluteJoint() - { - b2Body* ground = NULL; - { - b2BodyDef bd; - ground = m_world->CreateBody(&bd); - - b2EdgeShape shape; - shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f)); - - b2FixtureDef fd; - fd.shape = &shape; - // fd.filter.categoryBits = 2; - - ground->CreateFixture(&fd); - } - - m_enableLimit = true; - m_enableMotor = false; - m_motorSpeed = 1.0f; - - { - b2PolygonShape shape; - shape.SetAsBox(0.25f, 3.0f, b2Vec2(0.0f, 3.0f), 0.0f); - - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(-10.0f, 20.0f); - b2Body* body = m_world->CreateBody(&bd); - body->CreateFixture(&shape, 5.0f); - - b2RevoluteJointDef jd; - jd.Initialize(ground, body, b2Vec2(-10.0f, 20.5f)); - jd.motorSpeed = m_motorSpeed; - jd.maxMotorTorque = 10000.0f; - jd.enableMotor = m_enableMotor; - jd.lowerAngle = -0.25f * b2_pi; - jd.upperAngle = 0.5f * b2_pi; - jd.enableLimit = m_enableLimit; - - m_joint1 = (b2RevoluteJoint*)m_world->CreateJoint(&jd); - } - - { - b2CircleShape circle_shape; - circle_shape.m_radius = 2.0f; - - b2BodyDef circle_bd; - circle_bd.type = b2_dynamicBody; - circle_bd.position.Set(5.0f, 30.0f); - - b2FixtureDef fd; - fd.density = 5.0f; - fd.filter.maskBits = 1; - fd.shape = &circle_shape; - - m_ball = m_world->CreateBody(&circle_bd); - m_ball->CreateFixture(&fd); - - b2PolygonShape polygon_shape; - polygon_shape.SetAsBox(10.0f, 0.5f, b2Vec2(-10.0f, 0.0f), 0.0f); - - b2BodyDef polygon_bd; - polygon_bd.position.Set(20.0f, 10.0f); - polygon_bd.type = b2_dynamicBody; - polygon_bd.bullet = true; - b2Body* polygon_body = m_world->CreateBody(&polygon_bd); - polygon_body->CreateFixture(&polygon_shape, 2.0f); - - b2RevoluteJointDef jd; - jd.Initialize(ground, polygon_body, b2Vec2(19.0f, 10.0f)); - jd.lowerAngle = -0.25f * b2_pi; - jd.upperAngle = 0.0f * b2_pi; - jd.enableLimit = true; - jd.enableMotor = true; - jd.motorSpeed = 0.0f; - jd.maxMotorTorque = 10000.0f; - - m_joint2 = (b2RevoluteJoint*)m_world->CreateJoint(&jd); - } - } - - void UpdateUI() override - { - // ImGui::SetNextWindowPos(ImVec2(10.0f, 100.0f)); - ImGui::SetNextWindowSize(ImVec2(200.0f, 100.0f)); - ImGui::Begin("Joint Controls", nullptr, ImGuiWindowFlags_NoResize); - - if (ImGui::Checkbox("Limit", &m_enableLimit)) - { - m_joint1->EnableLimit(m_enableLimit); - } - - if (ImGui::Checkbox("Motor", &m_enableMotor)) - { - m_joint1->EnableMotor(m_enableMotor); - } - - if (ImGui::SliderFloat("Speed", &m_motorSpeed, -20.0f, 20.0f, "%.0f")) - { - m_joint1->SetMotorSpeed(m_motorSpeed); - } - - ImGui::End(); - } - - void Step(Settings& settings) override - { - Test::Step(settings); - - float torque1 = m_joint1->GetMotorTorque(settings.m_hertz); - DrawString(5, m_textLine, "Motor Torque 1= %4.0f", torque1); - - float torque2 = m_joint2->GetMotorTorque(settings.m_hertz); - DrawString(5, m_textLine, "Motor Torque 2= %4.0f", torque2); - } - - static Test* Create() { return new RevoluteJoint; } - - b2Body* m_ball; - b2RevoluteJoint* m_joint1; - b2RevoluteJoint* m_joint2; - float m_motorSpeed; - bool m_enableMotor; - bool m_enableLimit; -}; - -static int testIndex = RegisterTest("Joints", "Revolute", RevoluteJoint::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/rope.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/rope.cpp deleted file mode 100644 index a36a60105ec7..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/rope.cpp +++ /dev/null @@ -1,282 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "settings.h" -#include "test.h" -#include "box2d/b2_rope.h" -#include "ImGui/ImGuiPresenter.h" - -/// -class Rope : public Test -{ -public: - Rope() - { - const int32 N = 20; - const float L = 0.5f; - b2Vec2 vertices[N]; - float masses[N]; - - for (int32 i = 0; i < N; ++i) - { - vertices[i].Set(0.0f, L * (N - i)); - masses[i] = 1.0f; - } - masses[0] = 0.0f; - masses[1] = 0.0f; - - m_tuning1.bendHertz = 30.0f; - m_tuning1.bendDamping = 4.0f; - m_tuning1.bendStiffness = 1.0f; - m_tuning1.bendingModel = b2_pbdTriangleBendingModel; - m_tuning1.isometric = true; - - m_tuning1.stretchHertz = 30.0f; - m_tuning1.stretchDamping = 4.0f; - m_tuning1.stretchStiffness = 1.0f; - m_tuning1.stretchingModel = b2_pbdStretchingModel; - - m_tuning2.bendHertz = 30.0f; - m_tuning2.bendDamping = 0.7f; - m_tuning2.bendStiffness = 1.0f; - m_tuning2.bendingModel = b2_pbdHeightBendingModel; - m_tuning2.isometric = true; - - m_tuning2.stretchHertz = 30.0f; - m_tuning2.stretchDamping = 1.0f; - m_tuning2.stretchStiffness = 1.0f; - m_tuning2.stretchingModel = b2_pbdStretchingModel; - - m_position1.Set(-5.0f, 15.0f); - m_position2.Set(5.0f, 15.0f); - - b2RopeDef def; - def.vertices = vertices; - def.count = N; - def.gravity.Set(0.0f, -10.0f); - def.masses = masses; - - def.position = m_position1; - def.tuning = m_tuning1; - m_rope1.Create(def); - - def.position = m_position2; - def.tuning = m_tuning2; - m_rope2.Create(def); - - m_iterations1 = 8; - m_iterations2 = 8; - - m_speed = 10.0f; - } - - void UpdateUI() override - { - // ImGui::SetNextWindowPos(ImVec2(10.0f, 100.0f)); - ImGui::SetNextWindowSize(ImVec2(200.0f, 700.0f)); - ImGui::Begin("Tuning", nullptr, ImGuiWindowFlags_NoResize); - - ImGui::Separator(); - - ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.5f); - - const ImGuiComboFlags comboFlags = 0; - const char* bendModels[] = {"Spring", "PBD Ang", "XPBD Ang", "PBD Dist", "PBD Height", "PBD Triangle"}; - const char* stretchModels[] = {"PBD", "XPBD"}; - - ImGui::Text("Rope 1"); - static int bendModel1 = m_tuning1.bendingModel; - if (ImGui::BeginCombo("Bend Model##1", bendModels[bendModel1], comboFlags)) - { - for (int i = 0; i < IM_ARRAYSIZE(bendModels); ++i) - { - bool isSelected = (bendModel1 == i); - if (ImGui::Selectable(bendModels[i], isSelected)) - { - bendModel1 = i; - m_tuning1.bendingModel = b2BendingModel(i); - } - - if (isSelected) - { - ImGui::SetItemDefaultFocus(); - } - } - ImGui::EndCombo(); - } - - ImGui::SliderFloat("Damping##B1", &m_tuning1.bendDamping, 0.0f, 4.0f, "%.1f"); - ImGui::SliderFloat("Hertz##B1", &m_tuning1.bendHertz, 0.0f, 60.0f, "%.0f"); - ImGui::SliderFloat("Stiffness##B1", &m_tuning1.bendStiffness, 0.0f, 1.0f, "%.1f"); - - ImGui::Checkbox("Isometric##1", &m_tuning1.isometric); - ImGui::Checkbox("Fixed Mass##1", &m_tuning1.fixedEffectiveMass); - ImGui::Checkbox("Warm Start##1", &m_tuning1.warmStart); - - static int stretchModel1 = m_tuning1.stretchingModel; - if (ImGui::BeginCombo("Stretch Model##1", stretchModels[stretchModel1], comboFlags)) - { - for (int i = 0; i < IM_ARRAYSIZE(stretchModels); ++i) - { - bool isSelected = (stretchModel1 == i); - if (ImGui::Selectable(stretchModels[i], isSelected)) - { - stretchModel1 = i; - m_tuning1.stretchingModel = b2StretchingModel(i); - } - - if (isSelected) - { - ImGui::SetItemDefaultFocus(); - } - } - ImGui::EndCombo(); - } - - ImGui::SliderFloat("Damping##S1", &m_tuning1.stretchDamping, 0.0f, 4.0f, "%.1f"); - ImGui::SliderFloat("Hertz##S1", &m_tuning1.stretchHertz, 0.0f, 60.0f, "%.0f"); - ImGui::SliderFloat("Stiffness##S1", &m_tuning1.stretchStiffness, 0.0f, 1.0f, "%.1f"); - - ImGui::SliderInt("Iterations##1", &m_iterations1, 1, 100, "%d"); - - ImGui::Separator(); - - ImGui::Text("Rope 2"); - static int bendModel2 = m_tuning2.bendingModel; - if (ImGui::BeginCombo("Bend Model##2", bendModels[bendModel2], comboFlags)) - { - for (int i = 0; i < IM_ARRAYSIZE(bendModels); ++i) - { - bool isSelected = (bendModel2 == i); - if (ImGui::Selectable(bendModels[i], isSelected)) - { - bendModel2 = i; - m_tuning2.bendingModel = b2BendingModel(i); - } - - if (isSelected) - { - ImGui::SetItemDefaultFocus(); - } - } - ImGui::EndCombo(); - } - - ImGui::SliderFloat("Damping##B2", &m_tuning2.bendDamping, 0.0f, 4.0f, "%.1f"); - ImGui::SliderFloat("Hertz##B2", &m_tuning2.bendHertz, 0.0f, 60.0f, "%.0f"); - ImGui::SliderFloat("Stiffness##B2", &m_tuning2.bendStiffness, 0.0f, 1.0f, "%.1f"); - - ImGui::Checkbox("Isometric##2", &m_tuning2.isometric); - ImGui::Checkbox("Fixed Mass##2", &m_tuning2.fixedEffectiveMass); - ImGui::Checkbox("Warm Start##2", &m_tuning2.warmStart); - - static int stretchModel2 = m_tuning2.stretchingModel; - if (ImGui::BeginCombo("Stretch Model##2", stretchModels[stretchModel2], comboFlags)) - { - for (int i = 0; i < IM_ARRAYSIZE(stretchModels); ++i) - { - bool isSelected = (stretchModel2 == i); - if (ImGui::Selectable(stretchModels[i], isSelected)) - { - stretchModel2 = i; - m_tuning2.stretchingModel = b2StretchingModel(i); - } - - if (isSelected) - { - ImGui::SetItemDefaultFocus(); - } - } - ImGui::EndCombo(); - } - - ImGui::SliderFloat("Damping##S2", &m_tuning2.stretchDamping, 0.0f, 4.0f, "%.1f"); - ImGui::SliderFloat("Hertz##S2", &m_tuning2.stretchHertz, 0.0f, 60.0f, "%.0f"); - ImGui::SliderFloat("Stiffness##S2", &m_tuning2.stretchStiffness, 0.0f, 1.0f, "%.1f"); - - ImGui::SliderInt("Iterations##2", &m_iterations2, 1, 100, "%d"); - - ImGui::Separator(); - - ImGui::SliderFloat("Speed", &m_speed, 10.0f, 100.0f, "%.0f"); - - if (ImGui::Button("Reset")) - { - m_position1.Set(-5.0f, 15.0f); - m_position2.Set(5.0f, 15.0f); - m_rope1.Reset(m_position1); - m_rope2.Reset(m_position2); - } - - ImGui::PopItemWidth(); - - ImGui::End(); - } - - void Step(Settings& settings) override - { - float dt = settings.m_hertz > 0.0f ? 1.0f / settings.m_hertz : 0.0f; - - if (settings.m_pause == 1 && settings.m_singleStep == 0) - { - dt = 0.0f; - } - - // if (glfwGetKey(g_mainWindow, GLFW_KEY_COMMA) == GLFW_PRESS) - //{ - // m_position1.x -= m_speed * dt; - // m_position2.x -= m_speed * dt; - // } - - // if (glfwGetKey(g_mainWindow, GLFW_KEY_PERIOD) == GLFW_PRESS) - //{ - // m_position1.x += m_speed * dt; - // m_position2.x += m_speed * dt; - // } - - m_rope1.SetTuning(m_tuning1); - m_rope2.SetTuning(m_tuning2); - m_rope1.Step(dt, m_iterations1, m_position1); - m_rope2.Step(dt, m_iterations2, m_position2); - - Test::Step(settings); - - m_rope1.Draw(&g_debugDraw); - m_rope2.Draw(&g_debugDraw); - - DrawString(5, m_textLine, "Press comma and period to move left and right"); - } - - static Test* Create() { return new Rope; } - - b2Rope m_rope1; - b2Rope m_rope2; - b2RopeTuning m_tuning1; - b2RopeTuning m_tuning2; - int32 m_iterations1; - int32 m_iterations2; - b2Vec2 m_position1; - b2Vec2 m_position2; - float m_speed; -}; - -static int testIndex = RegisterTest("Rope", "Bending", Rope::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/sensor.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/sensor.cpp deleted file mode 100644 index 9c3985412566..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/sensor.cpp +++ /dev/null @@ -1,191 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" -#include "ImGui/ImGuiPresenter.h" - -// This shows how to use sensor shapes. Sensors don't have collision, but report overlap events. -class Sensors : public Test -{ -public: - enum - { - e_count = 7 - }; - - Sensors() - { - { - b2BodyDef bd; - b2Body* ground = m_world->CreateBody(&bd); - - { - b2EdgeShape shape; - shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f)); - ground->CreateFixture(&shape, 0.0f); - } - -#if 0 - { - b2FixtureDef sd; - sd.SetAsBox(10.0f, 2.0f, b2Vec2(0.0f, 20.0f), 0.0f); - sd.isSensor = true; - m_sensor = ground->CreateFixture(&sd); - } -#else - { - b2CircleShape shape; - shape.m_radius = 5.0f; - shape.m_p.Set(0.0f, 10.0f); - - b2FixtureDef fd; - fd.shape = &shape; - fd.isSensor = true; - m_sensor = ground->CreateFixture(&fd); - } -#endif - } - - { - b2CircleShape shape; - shape.m_radius = 1.0f; - - for (int32 i = 0; i < e_count; ++i) - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(-10.0f + 3.0f * i, 20.0f); - bd.userData.pointer = i; - - m_touching[i] = false; - m_bodies[i] = m_world->CreateBody(&bd); - - m_bodies[i]->CreateFixture(&shape, 1.0f); - } - } - - m_force = 100.0f; - } - - // Implement contact listener. - void BeginContact(b2Contact* contact) override - { - b2Fixture* fixtureA = contact->GetFixtureA(); - b2Fixture* fixtureB = contact->GetFixtureB(); - - if (fixtureA == m_sensor) - { - uintptr_t index = fixtureB->GetBody()->GetUserData().pointer; - if (index < e_count) - { - m_touching[index] = true; - } - } - - if (fixtureB == m_sensor) - { - uintptr_t index = fixtureA->GetBody()->GetUserData().pointer; - if (index < e_count) - { - m_touching[index] = true; - } - } - } - - // Implement contact listener. - void EndContact(b2Contact* contact) override - { - b2Fixture* fixtureA = contact->GetFixtureA(); - b2Fixture* fixtureB = contact->GetFixtureB(); - - if (fixtureA == m_sensor) - { - uintptr_t index = fixtureB->GetBody()->GetUserData().pointer; - if (index < e_count) - { - m_touching[index] = false; - } - } - - if (fixtureB == m_sensor) - { - uintptr_t index = fixtureA->GetBody()->GetUserData().pointer; - if (index < e_count) - { - m_touching[index] = false; - } - } - } - - void UpdateUI() override - { - // ImGui::SetNextWindowPos(ImVec2(10.0f, 100.0f)); - ImGui::SetNextWindowSize(ImVec2(200.0f, 60.0f)); - ImGui::Begin("Sensor Controls", nullptr, ImGuiWindowFlags_NoResize); - - ImGui::SliderFloat("Force", &m_force, 0.0f, 2000.0f, "%.0f"); - - ImGui::End(); - } - - void Step(Settings& settings) override - { - Test::Step(settings); - - // Traverse the contact results. Apply a force on shapes - // that overlap the sensor. - for (int32 i = 0; i < e_count; ++i) - { - if (m_touching[i] == false) - { - continue; - } - - b2Body* body = m_bodies[i]; - b2Body* ground = m_sensor->GetBody(); - - b2CircleShape* circle = (b2CircleShape*)m_sensor->GetShape(); - b2Vec2 center = ground->GetWorldPoint(circle->m_p); - - b2Vec2 position = body->GetPosition(); - - b2Vec2 d = center - position; - if (d.LengthSquared() < FLT_EPSILON * FLT_EPSILON) - { - continue; - } - - d.Normalize(); - b2Vec2 F = m_force * d; - body->ApplyForce(F, position, false); - } - } - - static Test* Create() { return new Sensors; } - - b2Fixture* m_sensor; - b2Body* m_bodies[e_count]; - float m_force; - bool m_touching[e_count]; -}; - -static int testIndex = RegisterTest("Collision", "Sensors", Sensors::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/settings.h b/tests/cpp-tests/Source/Box2DTestBed/tests/settings.h deleted file mode 100644 index 7ba71423e898..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/settings.h +++ /dev/null @@ -1,83 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef SETTINGS_H -#define SETTINGS_H - -struct Settings -{ - Settings() { Reset(); } - - void Reset() - { - m_testIndex = 0; - m_windowWidth = 1600; - m_windowHeight = 900; - m_hertz = 60.0f; - m_velocityIterations = 8; - m_positionIterations = 3; - m_drawShapes = true; - m_drawJoints = true; - m_drawAABBs = false; - m_drawContactPoints = false; - m_drawContactNormals = false; - m_drawContactImpulse = false; - m_drawFrictionImpulse = false; - m_drawCOMs = false; - m_drawStats = false; - m_drawProfile = false; - m_enableWarmStarting = true; - m_enableContinuous = true; - m_enableSubStepping = false; - m_enableSleep = true; - m_pause = false; - m_singleStep = false; - } - - void Save(); - void Load(); - - int m_testIndex; - int m_windowWidth; - int m_windowHeight; - float m_hertz; - int m_velocityIterations; - int m_positionIterations; - bool m_drawShapes; - bool m_drawJoints; - bool m_drawAABBs; - bool m_drawContactPoints; - bool m_drawContactNormals; - bool m_drawContactImpulse; - bool m_drawFrictionImpulse; - bool m_drawCOMs; - bool m_drawStats; - bool m_drawProfile; - bool m_enableWarmStarting; - bool m_enableContinuous; - bool m_enableSubStepping; - bool m_enableSleep; - bool m_pause; - bool m_singleStep; -}; - -#endif \ No newline at end of file diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/shape_cast.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/shape_cast.cpp deleted file mode 100644 index 60771b589681..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/shape_cast.cpp +++ /dev/null @@ -1,189 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" -#include "box2d/b2_distance.h" - -class ShapeCast : public Test -{ -public: - enum - { - e_vertexCount = 8 - }; - - ShapeCast() - { -#if 1 - m_vAs[0].Set(-0.5f, 1.0f); - m_vAs[1].Set(0.5f, 1.0f); - m_vAs[2].Set(0.0f, 0.0f); - m_countA = 3; - m_radiusA = b2_polygonRadius; - - m_vBs[0].Set(-0.5f, -0.5f); - m_vBs[1].Set(0.5f, -0.5f); - m_vBs[2].Set(0.5f, 0.5f); - m_vBs[3].Set(-0.5f, 0.5f); - m_countB = 4; - m_radiusB = b2_polygonRadius; - - m_transformA.p.Set(0.0f, 0.25f); - m_transformA.q.SetIdentity(); - m_transformB.p.Set(-4.0f, 0.0f); - m_transformB.q.SetIdentity(); - m_translationB.Set(8.0f, 0.0f); -#elif 0 - m_vAs[0].Set(0.0f, 0.0f); - m_countA = 1; - m_radiusA = 0.5f; - - m_vBs[0].Set(0.0f, 0.0f); - m_countB = 1; - m_radiusB = 0.5f; - - m_transformA.p.Set(0.0f, 0.25f); - m_transformA.q.SetIdentity(); - m_transformB.p.Set(-4.0f, 0.0f); - m_transformB.q.SetIdentity(); - m_translationB.Set(8.0f, 0.0f); -#else - m_vAs[0].Set(0.0f, 0.0f); - m_vAs[1].Set(2.0f, 0.0f); - m_countA = 2; - m_radiusA = b2_polygonRadius; - - m_vBs[0].Set(0.0f, 0.0f); - m_countB = 1; - m_radiusB = 0.25f; - - // Initial overlap - m_transformA.p.Set(0.0f, 0.0f); - m_transformA.q.SetIdentity(); - m_transformB.p.Set(-0.244360745f, 0.05999358f); - m_transformB.q.SetIdentity(); - m_translationB.Set(0.0f, 0.0399999991f); -#endif - } - - static Test* Create() { return new ShapeCast; } - - void Step(Settings& settings) override - { - Test::Step(settings); - - b2ShapeCastInput input; - input.proxyA.Set(m_vAs, m_countA, m_radiusA); - input.proxyB.Set(m_vBs, m_countB, m_radiusB); - input.transformA = m_transformA; - input.transformB = m_transformB; - input.translationB = m_translationB; - - b2ShapeCastOutput output; - bool hit = b2ShapeCast(&output, &input); - - b2Transform transformB2; - transformB2.q = m_transformB.q; - transformB2.p = m_transformB.p + output.lambda * input.translationB; - - b2DistanceInput distanceInput; - distanceInput.proxyA.Set(m_vAs, m_countA, m_radiusA); - distanceInput.proxyB.Set(m_vBs, m_countB, m_radiusB); - distanceInput.transformA = m_transformA; - distanceInput.transformB = transformB2; - distanceInput.useRadii = false; - b2SimplexCache simplexCache; - simplexCache.count = 0; - b2DistanceOutput distanceOutput; - - b2Distance(&distanceOutput, &simplexCache, &distanceInput); - - DrawString(5, m_textLine, "hit = %s, iters = %d, lambda = %g, distance = %g", hit ? "true" : "false", - output.iterations, output.lambda, distanceOutput.distance); - - b2Vec2 vertices[b2_maxPolygonVertices]; - - for (int32 i = 0; i < m_countA; ++i) - { - vertices[i] = b2Mul(m_transformA, m_vAs[i]); - } - - if (m_countA == 1) - { - g_debugDraw.DrawCircle(vertices[0], m_radiusA, b2Color(0.9f, 0.9f, 0.9f)); - } - else - { - g_debugDraw.DrawPolygon(vertices, m_countA, b2Color(0.9f, 0.9f, 0.9f)); - } - - for (int32 i = 0; i < m_countB; ++i) - { - vertices[i] = b2Mul(m_transformB, m_vBs[i]); - } - - if (m_countB == 1) - { - g_debugDraw.DrawCircle(vertices[0], m_radiusB, b2Color(0.5f, 0.9f, 0.5f)); - } - else - { - g_debugDraw.DrawPolygon(vertices, m_countB, b2Color(0.5f, 0.9f, 0.5f)); - } - - for (int32 i = 0; i < m_countB; ++i) - { - vertices[i] = b2Mul(transformB2, m_vBs[i]); - } - - if (m_countB == 1) - { - g_debugDraw.DrawCircle(vertices[0], m_radiusB, b2Color(0.5f, 0.7f, 0.9f)); - } - else - { - g_debugDraw.DrawPolygon(vertices, m_countB, b2Color(0.5f, 0.7f, 0.9f)); - } - - if (hit) - { - b2Vec2 p1 = output.point; - g_debugDraw.DrawPoint(p1, 10.0f, b2Color(0.9f, 0.3f, 0.3f)); - b2Vec2 p2 = p1 + output.normal; - g_debugDraw.DrawSegment(p1, p2, b2Color(0.9f, 0.3f, 0.3f)); - } - } - - b2Vec2 m_vAs[b2_maxPolygonVertices]; - int32 m_countA; - float m_radiusA; - - b2Vec2 m_vBs[b2_maxPolygonVertices]; - int32 m_countB; - float m_radiusB; - - b2Transform m_transformA; - b2Transform m_transformB; - b2Vec2 m_translationB; -}; - -static int testIndex = RegisterTest("Collision", "Shape Cast", ShapeCast::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/shape_editing.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/shape_editing.cpp deleted file mode 100644 index c7b753b52400..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/shape_editing.cpp +++ /dev/null @@ -1,103 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -class ShapeEditing : public Test -{ -public: - ShapeEditing() - { - { - b2BodyDef bd; - b2Body* ground = m_world->CreateBody(&bd); - - b2EdgeShape shape; - shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f)); - ground->CreateFixture(&shape, 0.0f); - } - - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(0.0f, 10.0f); - m_body = m_world->CreateBody(&bd); - - b2PolygonShape shape; - shape.SetAsBox(4.0f, 4.0f, b2Vec2(0.0f, 0.0f), 0.0f); - m_fixture1 = m_body->CreateFixture(&shape, 10.0f); - - m_fixture2 = NULL; - - m_sensor = false; - } - - void Keyboard(int key) override - { - switch (key) - { - case GLFW_KEY_C: - if (m_fixture2 == NULL) - { - b2CircleShape shape; - shape.m_radius = 3.0f; - shape.m_p.Set(0.5f, -4.0f); - m_fixture2 = m_body->CreateFixture(&shape, 10.0f); - m_body->SetAwake(true); - } - break; - - case GLFW_KEY_D: - if (m_fixture2 != NULL) - { - m_body->DestroyFixture(m_fixture2); - m_fixture2 = NULL; - m_body->SetAwake(true); - } - break; - - case GLFW_KEY_S: - if (m_fixture2 != NULL) - { - m_sensor = !m_sensor; - m_fixture2->SetSensor(m_sensor); - } - break; - } - } - - void Step(Settings& settings) override - { - Test::Step(settings); - DrawString(5, m_textLine, "Press: (c) create a shape, (d) destroy a shape."); - - DrawString(5, m_textLine, "sensor = %d", m_sensor); - } - - static Test* Create() { return new ShapeEditing; } - - b2Body* m_body; - b2Fixture* m_fixture1; - b2Fixture* m_fixture2; - bool m_sensor; -}; - -static int testIndex = RegisterTest("Examples", "Shape Editing", ShapeEditing::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/skier.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/skier.cpp deleted file mode 100644 index 05529640a25c..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/skier.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/* -Test case for collision/jerking issue. -*/ - -#include "test.h" - -#include -#include - -class Skier : public Test -{ -public: - Skier() - { - b2Body* ground = NULL; - { - b2BodyDef bd; - ground = m_world->CreateBody(&bd); - - float const PlatformWidth = 8.0f; - - /* - First angle is from the horizontal and should be negative for a downward slope. - Second angle is relative to the preceding slope, and should be positive, creating a kind of - loose 'Z'-shape from the 3 edges. - If A1 = -10, then A2 <= ~1.5 will result in the collision glitch. - If A1 = -30, then A2 <= ~10.0 will result in the glitch. - */ - float const Angle1Degrees = -30.0f; - float const Angle2Degrees = 10.0f; - - /* - The larger the value of SlopeLength, the less likely the glitch will show up. - */ - float const SlopeLength = 2.0f; - - float const SurfaceFriction = 0.2f; - - // Convert to radians - float const Slope1Incline = -Angle1Degrees * b2_pi / 180.0f; - float const Slope2Incline = Slope1Incline - Angle2Degrees * b2_pi / 180.0f; - // - - m_platform_width = PlatformWidth; - - // Horizontal platform - b2Vec2 v1(-PlatformWidth, 0.0f); - b2Vec2 v2(0.0f, 0.0f); - b2Vec2 v3(SlopeLength * cosf(Slope1Incline), -SlopeLength * sinf(Slope1Incline)); - b2Vec2 v4(v3.x + SlopeLength * cosf(Slope2Incline), v3.y - SlopeLength * sinf(Slope2Incline)); - b2Vec2 v5(v4.x, v4.y - 1.0f); - - b2Vec2 vertices[5] = {v5, v4, v3, v2, v1}; - - b2ChainShape shape; - shape.CreateLoop(vertices, 5); - b2FixtureDef fd; - fd.shape = &shape; - fd.density = 0.0f; - fd.friction = SurfaceFriction; - - ground->CreateFixture(&fd); - } - - { - float const BodyWidth = 1.0f; - float const BodyHeight = 2.5f; - float const SkiLength = 3.0f; - - /* - Larger values for this seem to alleviate the issue to some extent. - */ - float const SkiThickness = 0.3f; - - float const SkiFriction = 0.0f; - float const SkiRestitution = 0.15f; - - b2BodyDef bd; - bd.type = b2_dynamicBody; - - float initial_y = BodyHeight / 2 + SkiThickness; - bd.position.Set(-m_platform_width / 2, initial_y); - - b2Body* skier = m_world->CreateBody(&bd); - - b2PolygonShape ski; - b2Vec2 verts[4]; - verts[0].Set(-SkiLength / 2 - SkiThickness, -BodyHeight / 2); - verts[1].Set(-SkiLength / 2, -BodyHeight / 2 - SkiThickness); - verts[2].Set(SkiLength / 2, -BodyHeight / 2 - SkiThickness); - verts[3].Set(SkiLength / 2 + SkiThickness, -BodyHeight / 2); - ski.Set(verts, 4); - - b2FixtureDef fd; - fd.density = 1.0f; - - fd.friction = SkiFriction; - fd.restitution = SkiRestitution; - - fd.shape = &ski; - skier->CreateFixture(&fd); - - skier->SetLinearVelocity(b2Vec2(0.5f, 0.0f)); - - m_skier = skier; - } - - // g_camera.m_center = b2Vec2(m_platform_width / 2.0f, 0.0f); - // g_camera.m_zoom = 0.4f; - m_fixed_camera = true; - } - - void Keyboard(int key) override - { - switch (key) - { - case GLFW_KEY_C: - m_fixed_camera = !m_fixed_camera; - if (m_fixed_camera) - { - // g_camera.m_center = b2Vec2(m_platform_width / 2.0f, 0.0f); - } - break; - } - } - - void Step(Settings& settings) override - { - DrawString(5, m_textLine, "Keys: c = Camera fixed/tracking"); - - if (!m_fixed_camera) - { - // g_camera.m_center = m_skier->GetPosition(); - } - - Test::Step(settings); - } - - static Test* Create() { return new Skier; } - - b2Body* m_skier; - float m_platform_width; - bool m_fixed_camera; -}; - -static int testIndex = RegisterTest("Bugs", "Skier", Skier::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/slider_crank_1.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/slider_crank_1.cpp deleted file mode 100644 index e09e8ade1992..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/slider_crank_1.cpp +++ /dev/null @@ -1,103 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -// A basic slider crank created for GDC tutorial: Understanding Constraints -class SliderCrank1 : public Test -{ -public: - SliderCrank1() - { - b2Body* ground = NULL; - { - b2BodyDef bd; - bd.position.Set(0.0f, 17.0f); - ground = m_world->CreateBody(&bd); - } - - { - b2Body* prevBody = ground; - - // Define crank. - { - b2PolygonShape shape; - shape.SetAsBox(4.0f, 1.0f); - - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(-8.0f, 20.0f); - b2Body* body = m_world->CreateBody(&bd); - body->CreateFixture(&shape, 2.0f); - - b2RevoluteJointDef rjd; - rjd.Initialize(prevBody, body, b2Vec2(-12.0f, 20.0f)); - m_world->CreateJoint(&rjd); - - prevBody = body; - } - - // Define connecting rod - { - b2PolygonShape shape; - shape.SetAsBox(8.0f, 1.0f); - - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(4.0f, 20.0f); - b2Body* body = m_world->CreateBody(&bd); - body->CreateFixture(&shape, 2.0f); - - b2RevoluteJointDef rjd; - rjd.Initialize(prevBody, body, b2Vec2(-4.0f, 20.0f)); - m_world->CreateJoint(&rjd); - - prevBody = body; - } - - // Define piston - { - b2PolygonShape shape; - shape.SetAsBox(3.0f, 3.0f); - - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.fixedRotation = true; - bd.position.Set(12.0f, 20.0f); - b2Body* body = m_world->CreateBody(&bd); - body->CreateFixture(&shape, 2.0f); - - b2RevoluteJointDef rjd; - rjd.Initialize(prevBody, body, b2Vec2(12.0f, 20.0f)); - m_world->CreateJoint(&rjd); - - b2PrismaticJointDef pjd; - pjd.Initialize(ground, body, b2Vec2(12.0f, 17.0f), b2Vec2(1.0f, 0.0f)); - m_world->CreateJoint(&pjd); - } - } - } - - static Test* Create() { return new SliderCrank1; } -}; - -static int testIndex = RegisterTest("Examples", "Slider Crank 1", SliderCrank1::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/slider_crank_2.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/slider_crank_2.cpp deleted file mode 100644 index f55a05859cf6..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/slider_crank_2.cpp +++ /dev/null @@ -1,156 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "settings.h" -#include "test.h" - -// A motor driven slider crank with joint friction. - -class SliderCrank2 : public Test -{ -public: - SliderCrank2() - { - b2Body* ground = NULL; - { - b2BodyDef bd; - ground = m_world->CreateBody(&bd); - - b2EdgeShape shape; - shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f)); - ground->CreateFixture(&shape, 0.0f); - } - - { - b2Body* prevBody = ground; - - // Define crank. - { - b2PolygonShape shape; - shape.SetAsBox(0.5f, 2.0f); - - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(0.0f, 7.0f); - b2Body* body = m_world->CreateBody(&bd); - body->CreateFixture(&shape, 2.0f); - - b2RevoluteJointDef rjd; - rjd.Initialize(prevBody, body, b2Vec2(0.0f, 5.0f)); - rjd.motorSpeed = 1.0f * b2_pi; - rjd.maxMotorTorque = 10000.0f; - rjd.enableMotor = true; - m_joint1 = (b2RevoluteJoint*)m_world->CreateJoint(&rjd); - - prevBody = body; - } - - // Define follower. - { - b2PolygonShape shape; - shape.SetAsBox(0.5f, 4.0f); - - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(0.0f, 13.0f); - b2Body* body = m_world->CreateBody(&bd); - body->CreateFixture(&shape, 2.0f); - - b2RevoluteJointDef rjd; - rjd.Initialize(prevBody, body, b2Vec2(0.0f, 9.0f)); - rjd.enableMotor = false; - m_world->CreateJoint(&rjd); - - prevBody = body; - } - - // Define piston - { - b2PolygonShape shape; - shape.SetAsBox(1.5f, 1.5f); - - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.fixedRotation = true; - bd.position.Set(0.0f, 17.0f); - b2Body* body = m_world->CreateBody(&bd); - body->CreateFixture(&shape, 2.0f); - - b2RevoluteJointDef rjd; - rjd.Initialize(prevBody, body, b2Vec2(0.0f, 17.0f)); - m_world->CreateJoint(&rjd); - - b2PrismaticJointDef pjd; - pjd.Initialize(ground, body, b2Vec2(0.0f, 17.0f), b2Vec2(0.0f, 1.0f)); - - pjd.maxMotorForce = 1000.0f; - pjd.enableMotor = true; - - m_joint2 = (b2PrismaticJoint*)m_world->CreateJoint(&pjd); - } - - // Create a payload - { - b2PolygonShape shape; - shape.SetAsBox(1.5f, 1.5f); - - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(0.0f, 23.0f); - b2Body* body = m_world->CreateBody(&bd); - body->CreateFixture(&shape, 2.0f); - } - } - } - - void Keyboard(int key) override - { - switch (key) - { - case GLFW_KEY_F: - m_joint2->EnableMotor(!m_joint2->IsMotorEnabled()); - m_joint2->GetBodyB()->SetAwake(true); - break; - - case GLFW_KEY_M: - m_joint1->EnableMotor(!m_joint1->IsMotorEnabled()); - m_joint1->GetBodyB()->SetAwake(true); - break; - } - } - - void Step(Settings& settings) override - { - Test::Step(settings); - DrawString(5, m_textLine, "Keys: (f) toggle friction, (m) toggle motor"); - - float torque = m_joint1->GetMotorTorque(settings.m_hertz); - DrawString(5, m_textLine, "Motor Torque = %5.0f", (float)torque); - } - - static Test* Create() { return new SliderCrank2; } - - b2RevoluteJoint* m_joint1; - b2PrismaticJoint* m_joint2; -}; - -static int testIndex = RegisterTest("Examples", "Slider Crank 2", SliderCrank2::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/test.h b/tests/cpp-tests/Source/Box2DTestBed/tests/test.h deleted file mode 100644 index 680a6226c4cd..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/test.h +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (c) 2006-2009 Erin Catto http://www.box2d.org - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - */ - -#ifndef TEST_H -#define TEST_H - -#include "box2d/box2d.h" -#include "extensions/axmol-ext.h" - -#include - -class Test; -struct Settings; - -#define RAND_LIMIT 32767 -#define DRAW_STRING_NEW_LINE 25 - -/// Random number in range [-1,1] -inline float RandomFloat() -{ - float r = (float)(std::rand() & (RAND_LIMIT)); - r /= RAND_LIMIT; - r = 2.0f * r - 1.0f; - return r; -} - -/// Random floating point number in range [lo, hi] -inline float RandomFloat(float lo, float hi) -{ - float r = (float)(std::rand() & (RAND_LIMIT)); - r /= RAND_LIMIT; - r = (hi - lo) * r + lo; - return r; -} - -// This is called when a joint in the world is implicitly destroyed -// because an attached body is destroyed. This gives us a chance to -// nullify the mouse joint. -class DestructionListener : public b2DestructionListener -{ -public: - void SayGoodbye(b2Fixture* fixture) { B2_NOT_USED(fixture); } - void SayGoodbye(b2Joint* joint); - - Test* test; -}; - -const int32 k_maxContactPoints = 2048; - -struct ContactPoint -{ - b2Fixture* fixtureA; - b2Fixture* fixtureB; - b2Vec2 normal; - b2Vec2 position; - b2PointState state; - float normalImpulse; - float tangentImpulse; - float separation; -}; -class Test : public b2ContactListener -{ -public: - Test(); - virtual ~Test(); - - void DrawTitle(const char* string); - virtual void Step(Settings& settings); - virtual void UpdateUI() {} - virtual void Keyboard(int key) { B2_NOT_USED(key); } - virtual void KeyboardUp(int key) { B2_NOT_USED(key); } - void ShiftMouseDown(const b2Vec2& p); - virtual bool MouseDown(const b2Vec2& p); - virtual void MouseUp(const b2Vec2& p); - virtual void MouseMove(const b2Vec2& p); - void LaunchBomb(); - void LaunchBomb(const b2Vec2& position, const b2Vec2& velocity); - - void SpawnBomb(const b2Vec2& worldPt); - void CompleteBombSpawn(const b2Vec2& p); - - // Let derived tests know that a joint was destroyed. - virtual void JointDestroyed(b2Joint* joint) { B2_NOT_USED(joint); } - - // Callbacks for derived classes. - virtual void BeginContact(b2Contact* contact) override { B2_NOT_USED(contact); } - virtual void EndContact(b2Contact* contact) override { B2_NOT_USED(contact); } - virtual void PreSolve(b2Contact* contact, const b2Manifold* oldManifold) override; - virtual void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse) override - { - B2_NOT_USED(contact); - B2_NOT_USED(impulse); - } - - void ShiftOrigin(const b2Vec2& newOrigin); - - void initShader(void); - void DrawString(int x, int y, const char* fmt, ...); - void DrawString(const b2Vec2& p, const char* fmt, ...); - void DrawAABB(b2AABB* aabb, const b2Color& color); - void Flush(); - - ax::extension::PhysicsDebugNodeBox2D g_debugDraw; - ax::DrawNode* debugDrawNode; - std::string debugString = ""; - - b2World* m_world; - -protected: - friend class DestructionListener; - friend class BoundaryListener; - friend class ContactListener; - - b2Body* m_groundBody; - b2AABB m_worldAABB; - ContactPoint m_points[k_maxContactPoints]; - int32 m_pointCount; - DestructionListener m_destructionListener; - int32 m_textLine; - - b2Body* m_bomb; - b2MouseJoint* m_mouseJoint; - b2Vec2 m_bombSpawnPoint; - bool m_bombSpawning; - b2Vec2 m_mouseWorld; - int32 m_stepCount; - int32 m_textIncrement; - b2Profile m_maxProfile; - b2Profile m_totalProfile; -}; - -typedef Test* TestCreateFcn(); -int RegisterTest(const char* category, const char* name, TestCreateFcn* fcn); - -#endif diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/theo_jansen.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/theo_jansen.cpp deleted file mode 100644 index cab814037c1c..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/theo_jansen.cpp +++ /dev/null @@ -1,261 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -// Inspired by a contribution from roman_m -// Dimensions scooped from APE (http://www.cove.org/ape/index.htm) - -#include "test.h" - -class TheoJansen : public Test -{ -public: - void CreateLeg(float s, const b2Vec2& wheelAnchor) - { - b2Vec2 p1(5.4f * s, -6.1f); - b2Vec2 p2(7.2f * s, -1.2f); - b2Vec2 p3(4.3f * s, -1.9f); - b2Vec2 p4(3.1f * s, 0.8f); - b2Vec2 p5(6.0f * s, 1.5f); - b2Vec2 p6(2.5f * s, 3.7f); - - b2FixtureDef fd1, fd2; - fd1.filter.groupIndex = -1; - fd2.filter.groupIndex = -1; - fd1.density = 1.0f; - fd2.density = 1.0f; - - b2PolygonShape poly1, poly2; - - if (s > 0.0f) - { - b2Vec2 vertices[3]; - - vertices[0] = p1; - vertices[1] = p2; - vertices[2] = p3; - poly1.Set(vertices, 3); - - vertices[0] = b2Vec2_zero; - vertices[1] = p5 - p4; - vertices[2] = p6 - p4; - poly2.Set(vertices, 3); - } - else - { - b2Vec2 vertices[3]; - - vertices[0] = p1; - vertices[1] = p3; - vertices[2] = p2; - poly1.Set(vertices, 3); - - vertices[0] = b2Vec2_zero; - vertices[1] = p6 - p4; - vertices[2] = p5 - p4; - poly2.Set(vertices, 3); - } - - fd1.shape = &poly1; - fd2.shape = &poly2; - - b2BodyDef bd1, bd2; - bd1.type = b2_dynamicBody; - bd2.type = b2_dynamicBody; - bd1.position = m_offset; - bd2.position = p4 + m_offset; - - bd1.angularDamping = 10.0f; - bd2.angularDamping = 10.0f; - - b2Body* body1 = m_world->CreateBody(&bd1); - b2Body* body2 = m_world->CreateBody(&bd2); - - body1->CreateFixture(&fd1); - body2->CreateFixture(&fd2); - - { - b2DistanceJointDef jd; - - // Using a soft distance constraint can reduce some jitter. - // It also makes the structure seem a bit more fluid by - // acting like a suspension system. - float dampingRatio = 0.5f; - float frequencyHz = 10.0f; - - jd.Initialize(body1, body2, p2 + m_offset, p5 + m_offset); - b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB); - m_world->CreateJoint(&jd); - - jd.Initialize(body1, body2, p3 + m_offset, p4 + m_offset); - b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB); - m_world->CreateJoint(&jd); - - jd.Initialize(body1, m_wheel, p3 + m_offset, wheelAnchor + m_offset); - b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB); - m_world->CreateJoint(&jd); - - jd.Initialize(body2, m_wheel, p6 + m_offset, wheelAnchor + m_offset); - b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB); - m_world->CreateJoint(&jd); - } - - { - b2RevoluteJointDef jd; - jd.Initialize(body2, m_chassis, p4 + m_offset); - m_world->CreateJoint(&jd); - } - } - - TheoJansen() - { - m_offset.Set(0.0f, 8.0f); - m_motorSpeed = 2.0f; - m_motorOn = true; - b2Vec2 pivot(0.0f, 0.8f); - - // Ground - { - b2BodyDef bd; - b2Body* ground = m_world->CreateBody(&bd); - - b2EdgeShape shape; - shape.SetTwoSided(b2Vec2(-50.0f, 0.0f), b2Vec2(50.0f, 0.0f)); - ground->CreateFixture(&shape, 0.0f); - - shape.SetTwoSided(b2Vec2(-50.0f, 0.0f), b2Vec2(-50.0f, 10.0f)); - ground->CreateFixture(&shape, 0.0f); - - shape.SetTwoSided(b2Vec2(50.0f, 0.0f), b2Vec2(50.0f, 10.0f)); - ground->CreateFixture(&shape, 0.0f); - } - - // Balls - for (int32 i = 0; i < 40; ++i) - { - b2CircleShape shape; - shape.m_radius = 0.25f; - - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(-40.0f + 2.0f * i, 0.5f); - - b2Body* body = m_world->CreateBody(&bd); - body->CreateFixture(&shape, 1.0f); - } - - // Chassis - { - b2PolygonShape shape; - shape.SetAsBox(2.5f, 1.0f); - - b2FixtureDef sd; - sd.density = 1.0f; - sd.shape = &shape; - sd.filter.groupIndex = -1; - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position = pivot + m_offset; - m_chassis = m_world->CreateBody(&bd); - m_chassis->CreateFixture(&sd); - } - - { - b2CircleShape shape; - shape.m_radius = 1.6f; - - b2FixtureDef sd; - sd.density = 1.0f; - sd.shape = &shape; - sd.filter.groupIndex = -1; - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position = pivot + m_offset; - m_wheel = m_world->CreateBody(&bd); - m_wheel->CreateFixture(&sd); - } - - { - b2RevoluteJointDef jd; - jd.Initialize(m_wheel, m_chassis, pivot + m_offset); - jd.collideConnected = false; - jd.motorSpeed = m_motorSpeed; - jd.maxMotorTorque = 400.0f; - jd.enableMotor = m_motorOn; - m_motorJoint = (b2RevoluteJoint*)m_world->CreateJoint(&jd); - } - - b2Vec2 wheelAnchor; - - wheelAnchor = pivot + b2Vec2(0.0f, -0.8f); - - CreateLeg(-1.0f, wheelAnchor); - CreateLeg(1.0f, wheelAnchor); - - m_wheel->SetTransform(m_wheel->GetPosition(), 120.0f * b2_pi / 180.0f); - CreateLeg(-1.0f, wheelAnchor); - CreateLeg(1.0f, wheelAnchor); - - m_wheel->SetTransform(m_wheel->GetPosition(), -120.0f * b2_pi / 180.0f); - CreateLeg(-1.0f, wheelAnchor); - CreateLeg(1.0f, wheelAnchor); - } - - void Step(Settings& settings) override - { - DrawString(5, m_textLine, "Keys: left = a, brake = s, right = d, toggle motor = m"); - - Test::Step(settings); - } - - void Keyboard(int key) override - { - switch (key) - { - case GLFW_KEY_A: - m_motorJoint->SetMotorSpeed(-m_motorSpeed); - break; - - case GLFW_KEY_S: - m_motorJoint->SetMotorSpeed(0.0f); - break; - - case GLFW_KEY_D: - m_motorJoint->SetMotorSpeed(m_motorSpeed); - break; - - case GLFW_KEY_M: - m_motorJoint->EnableMotor(!m_motorJoint->IsMotorEnabled()); - break; - } - } - - static Test* Create() { return new TheoJansen; } - - b2Vec2 m_offset; - b2Body* m_chassis; - b2Body* m_wheel; - b2RevoluteJoint* m_motorJoint; - bool m_motorOn; - float m_motorSpeed; -}; - -static int testIndex = RegisterTest("Examples", "Theo Jansen", TheoJansen::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/tiles.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/tiles.cpp deleted file mode 100644 index e5b9b5dd5f40..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/tiles.cpp +++ /dev/null @@ -1,153 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -/// This stress tests the dynamic tree broad-phase. This also shows that tile -/// based collision is _not_ smooth due to Box2D not knowing about adjacency. -class Tiles : public Test -{ -public: - enum - { - e_count = 20 - }; - - Tiles() - { - m_fixtureCount = 0; - b2Timer timer; - - { - float a = 0.5f; - b2BodyDef bd; - bd.position.y = -a; - b2Body* ground = m_world->CreateBody(&bd); - -#if 1 - int32 N = 200; - int32 M = 10; - b2Vec2 position; - position.y = 0.0f; - for (int32 j = 0; j < M; ++j) - { - position.x = -N * a; - for (int32 i = 0; i < N; ++i) - { - b2PolygonShape shape; - shape.SetAsBox(a, a, position, 0.0f); - ground->CreateFixture(&shape, 0.0f); - ++m_fixtureCount; - position.x += 2.0f * a; - } - position.y -= 2.0f * a; - } -#else - int32 N = 200; - int32 M = 10; - b2Vec2 position; - position.x = -N * a; - for (int32 i = 0; i < N; ++i) - { - position.y = 0.0f; - for (int32 j = 0; j < M; ++j) - { - b2PolygonShape shape; - shape.SetAsBox(a, a, position, 0.0f); - ground->CreateFixture(&shape, 0.0f); - position.y -= 2.0f * a; - } - position.x += 2.0f * a; - } -#endif - } - - { - float a = 0.5f; - b2PolygonShape shape; - shape.SetAsBox(a, a); - - b2Vec2 x(-7.0f, 0.75f); - b2Vec2 y; - b2Vec2 deltaX(0.5625f, 1.25f); - b2Vec2 deltaY(1.125f, 0.0f); - - for (int32 i = 0; i < e_count; ++i) - { - y = x; - - for (int32 j = i; j < e_count; ++j) - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position = y; - - // if (i == 0 && j == 0) - //{ - // bd.allowSleep = false; - // } - // else - //{ - // bd.allowSleep = true; - // } - - b2Body* body = m_world->CreateBody(&bd); - body->CreateFixture(&shape, 5.0f); - ++m_fixtureCount; - y += deltaY; - } - - x += deltaX; - } - } - - m_createTime = timer.GetMilliseconds(); - } - - void Step(Settings& settings) override - { - const b2ContactManager& cm = m_world->GetContactManager(); - int32 height = cm.m_broadPhase.GetTreeHeight(); - int32 leafCount = cm.m_broadPhase.GetProxyCount(); - int32 minimumNodeCount = 2 * leafCount - 1; - float minimumHeight = ceilf(logf(float(minimumNodeCount)) / logf(2.0f)); - DrawString(5, m_textLine, "dynamic tree height = %d, min = %d", height, int32(minimumHeight)); - - Test::Step(settings); - - DrawString(5, m_textLine, "create time = %6.2f ms, fixture count = %d", m_createTime, m_fixtureCount); - - // b2DynamicTree* tree = &m_world->m_contactManager.m_broadPhase.m_tree; - - // if (m_stepCount == 400) - //{ - // tree->RebuildBottomUp(); - // } - } - - static Test* Create() { return new Tiles; } - - int32 m_fixtureCount; - float m_createTime; -}; - -static int testIndex = RegisterTest("Benchmark", "Tiles", Tiles::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/time_of_impact.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/time_of_impact.cpp deleted file mode 100644 index f33b53476ddd..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/time_of_impact.cpp +++ /dev/null @@ -1,126 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" -#include "box2d/b2_time_of_impact.h" - -class TimeOfImpact : public Test -{ -public: - TimeOfImpact() - { - m_shapeA.SetAsBox(25.0f, 5.0f); - m_shapeB.SetAsBox(2.5f, 2.5f); - } - - static Test* Create() { return new TimeOfImpact; } - - void Step(Settings& settings) override - { - Test::Step(settings); - - b2Sweep sweepA; - sweepA.c0.Set(24.0f, -60.0f); - sweepA.a0 = 2.95f; - sweepA.c = sweepA.c0; - sweepA.a = sweepA.a0; - sweepA.localCenter.SetZero(); - - b2Sweep sweepB; - sweepB.c0.Set(53.474274f, -50.252514f); - sweepB.a0 = 513.36676f; // - 162.0f * b2_pi; - sweepB.c.Set(54.595478f, -51.083473f); - sweepB.a = 513.62781f; // - 162.0f * b2_pi; - sweepB.localCenter.SetZero(); - - // sweepB.a0 -= 300.0f * b2_pi; - // sweepB.a -= 300.0f * b2_pi; - - b2TOIInput input; - input.proxyA.Set(&m_shapeA, 0); - input.proxyB.Set(&m_shapeB, 0); - input.sweepA = sweepA; - input.sweepB = sweepB; - input.tMax = 1.0f; - - b2TOIOutput output; - - b2TimeOfImpact(&output, &input); - - DrawString(5, m_textLine, "toi = %g", output.t); - - extern B2_API int32 b2_toiMaxIters, b2_toiMaxRootIters; - DrawString(5, m_textLine, "max toi iters = %d, max root iters = %d", b2_toiMaxIters, b2_toiMaxRootIters); - - b2Vec2 vertices[b2_maxPolygonVertices]; - - b2Transform transformA; - sweepA.GetTransform(&transformA, 0.0f); - for (int32 i = 0; i < m_shapeA.m_count; ++i) - { - vertices[i] = b2Mul(transformA, m_shapeA.m_vertices[i]); - } - g_debugDraw.DrawPolygon(vertices, m_shapeA.m_count, b2Color(0.9f, 0.9f, 0.9f)); - - b2Transform transformB; - sweepB.GetTransform(&transformB, 0.0f); - - // b2Vec2 localPoint(2.0f, -0.1f); - - for (int32 i = 0; i < m_shapeB.m_count; ++i) - { - vertices[i] = b2Mul(transformB, m_shapeB.m_vertices[i]); - } - g_debugDraw.DrawPolygon(vertices, m_shapeB.m_count, b2Color(0.5f, 0.9f, 0.5f)); - - sweepB.GetTransform(&transformB, output.t); - for (int32 i = 0; i < m_shapeB.m_count; ++i) - { - vertices[i] = b2Mul(transformB, m_shapeB.m_vertices[i]); - } - g_debugDraw.DrawPolygon(vertices, m_shapeB.m_count, b2Color(0.5f, 0.7f, 0.9f)); - - sweepB.GetTransform(&transformB, 1.0f); - for (int32 i = 0; i < m_shapeB.m_count; ++i) - { - vertices[i] = b2Mul(transformB, m_shapeB.m_vertices[i]); - } - g_debugDraw.DrawPolygon(vertices, m_shapeB.m_count, b2Color(0.9f, 0.5f, 0.5f)); - -#if 0 - for (float t = 0.0f; t < 1.0f; t += 0.1f) - { - sweepB.GetTransform(&transformB, t); - for (int32 i = 0; i < m_shapeB.m_count; ++i) - { - vertices[i] = b2Mul(transformB, m_shapeB.m_vertices[i]); - } - g_debugDraw.DrawPolygon(vertices, m_shapeB.m_count, b2Color(0.9f, 0.5f, 0.5f)); - } -#endif - } - - b2PolygonShape m_shapeA; - b2PolygonShape m_shapeB; -}; - -static int testIndex = RegisterTest("Collision", "Time of Impact", TimeOfImpact::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/tumbler.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/tumbler.cpp deleted file mode 100644 index e19ffae13f29..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/tumbler.cpp +++ /dev/null @@ -1,98 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -class Tumbler : public Test -{ -public: - enum - { - e_count = 800 - }; - - Tumbler() - { - b2Body* ground = NULL; - { - b2BodyDef bd; - ground = m_world->CreateBody(&bd); - } - - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.allowSleep = false; - bd.position.Set(0.0f, 10.0f); - b2Body* body = m_world->CreateBody(&bd); - - b2PolygonShape shape; - shape.SetAsBox(0.5f, 10.0f, b2Vec2(10.0f, 0.0f), 0.0); - body->CreateFixture(&shape, 5.0f); - shape.SetAsBox(0.5f, 10.0f, b2Vec2(-10.0f, 0.0f), 0.0); - body->CreateFixture(&shape, 5.0f); - shape.SetAsBox(10.0f, 0.5f, b2Vec2(0.0f, 10.0f), 0.0); - body->CreateFixture(&shape, 5.0f); - shape.SetAsBox(10.0f, 0.5f, b2Vec2(0.0f, -10.0f), 0.0); - body->CreateFixture(&shape, 5.0f); - - b2RevoluteJointDef jd; - jd.bodyA = ground; - jd.bodyB = body; - jd.localAnchorA.Set(0.0f, 10.0f); - jd.localAnchorB.Set(0.0f, 0.0f); - jd.referenceAngle = 0.0f; - jd.motorSpeed = 0.05f * b2_pi; - jd.maxMotorTorque = 1e8f; - jd.enableMotor = true; - m_joint = (b2RevoluteJoint*)m_world->CreateJoint(&jd); - } - - m_count = 0; - } - - void Step(Settings& settings) override - { - Test::Step(settings); - - if (m_count < e_count) - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(0.0f, 10.0f); - b2Body* body = m_world->CreateBody(&bd); - - b2PolygonShape shape; - shape.SetAsBox(0.125f, 0.125f); - body->CreateFixture(&shape, 1.0f); - - ++m_count; - } - } - - static Test* Create() { return new Tumbler; } - - b2RevoluteJoint* m_joint; - int32 m_count; -}; - -static int testIndex = RegisterTest("Benchmark", "Tumbler", Tumbler::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/web.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/web.cpp deleted file mode 100644 index 658bc9f87bc0..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/web.cpp +++ /dev/null @@ -1,215 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" - -// Test distance joints, body destruction, and joint destruction. -class Web : public Test -{ -public: - Web() - { - b2Body* ground = NULL; - { - b2BodyDef bd; - ground = m_world->CreateBody(&bd); - - b2EdgeShape shape; - shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f)); - ground->CreateFixture(&shape, 0.0f); - } - - { - b2PolygonShape shape; - shape.SetAsBox(0.5f, 0.5f); - - b2BodyDef bd; - bd.type = b2_dynamicBody; - - bd.position.Set(-5.0f, 5.0f); - m_bodies[0] = m_world->CreateBody(&bd); - m_bodies[0]->CreateFixture(&shape, 5.0f); - - bd.position.Set(5.0f, 5.0f); - m_bodies[1] = m_world->CreateBody(&bd); - m_bodies[1]->CreateFixture(&shape, 5.0f); - - bd.position.Set(5.0f, 15.0f); - m_bodies[2] = m_world->CreateBody(&bd); - m_bodies[2]->CreateFixture(&shape, 5.0f); - - bd.position.Set(-5.0f, 15.0f); - m_bodies[3] = m_world->CreateBody(&bd); - m_bodies[3]->CreateFixture(&shape, 5.0f); - - b2DistanceJointDef jd; - b2Vec2 p1, p2, d; - - float frequencyHz = 2.0f; - float dampingRatio = 0.0f; - - jd.bodyA = ground; - jd.bodyB = m_bodies[0]; - jd.localAnchorA.Set(-10.0f, 0.0f); - jd.localAnchorB.Set(-0.5f, -0.5f); - p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA); - p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB); - d = p2 - p1; - jd.length = d.Length(); - b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB); - m_joints[0] = m_world->CreateJoint(&jd); - - jd.bodyA = ground; - jd.bodyB = m_bodies[1]; - jd.localAnchorA.Set(10.0f, 0.0f); - jd.localAnchorB.Set(0.5f, -0.5f); - p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA); - p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB); - d = p2 - p1; - jd.length = d.Length(); - b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB); - m_joints[1] = m_world->CreateJoint(&jd); - - jd.bodyA = ground; - jd.bodyB = m_bodies[2]; - jd.localAnchorA.Set(10.0f, 20.0f); - jd.localAnchorB.Set(0.5f, 0.5f); - p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA); - p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB); - d = p2 - p1; - jd.length = d.Length(); - b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB); - m_joints[2] = m_world->CreateJoint(&jd); - - jd.bodyA = ground; - jd.bodyB = m_bodies[3]; - jd.localAnchorA.Set(-10.0f, 20.0f); - jd.localAnchorB.Set(-0.5f, 0.5f); - p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA); - p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB); - d = p2 - p1; - jd.length = d.Length(); - b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB); - m_joints[3] = m_world->CreateJoint(&jd); - - jd.bodyA = m_bodies[0]; - jd.bodyB = m_bodies[1]; - jd.localAnchorA.Set(0.5f, 0.0f); - jd.localAnchorB.Set(-0.5f, 0.0f); - ; - p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA); - p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB); - d = p2 - p1; - jd.length = d.Length(); - b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB); - m_joints[4] = m_world->CreateJoint(&jd); - - jd.bodyA = m_bodies[1]; - jd.bodyB = m_bodies[2]; - jd.localAnchorA.Set(0.0f, 0.5f); - jd.localAnchorB.Set(0.0f, -0.5f); - p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA); - p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB); - d = p2 - p1; - jd.length = d.Length(); - b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB); - m_joints[5] = m_world->CreateJoint(&jd); - - jd.bodyA = m_bodies[2]; - jd.bodyB = m_bodies[3]; - jd.localAnchorA.Set(-0.5f, 0.0f); - jd.localAnchorB.Set(0.5f, 0.0f); - p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA); - p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB); - d = p2 - p1; - jd.length = d.Length(); - b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB); - m_joints[6] = m_world->CreateJoint(&jd); - - jd.bodyA = m_bodies[3]; - jd.bodyB = m_bodies[0]; - jd.localAnchorA.Set(0.0f, -0.5f); - jd.localAnchorB.Set(0.0f, 0.5f); - p1 = jd.bodyA->GetWorldPoint(jd.localAnchorA); - p2 = jd.bodyB->GetWorldPoint(jd.localAnchorB); - d = p2 - p1; - jd.length = d.Length(); - b2LinearStiffness(jd.stiffness, jd.damping, frequencyHz, dampingRatio, jd.bodyA, jd.bodyB); - m_joints[7] = m_world->CreateJoint(&jd); - } - } - - void Keyboard(int key) override - { - switch (key) - { - case GLFW_KEY_B: - for (int32 i = 0; i < 4; ++i) - { - if (m_bodies[i]) - { - m_world->DestroyBody(m_bodies[i]); - m_bodies[i] = NULL; - break; - } - } - break; - - case GLFW_KEY_J: - for (int32 i = 0; i < 8; ++i) - { - if (m_joints[i]) - { - m_world->DestroyJoint(m_joints[i]); - m_joints[i] = NULL; - break; - } - } - break; - } - } - - void Step(Settings& settings) override - { - Test::Step(settings); - DrawString(5, m_textLine, "Press: (b) to delete a body, (j) to delete a joint"); - } - - void JointDestroyed(b2Joint* joint) override - { - for (int32 i = 0; i < 8; ++i) - { - if (m_joints[i] == joint) - { - m_joints[i] = NULL; - break; - } - } - } - - static Test* Create() { return new Web; } - - b2Body* m_bodies[4]; - b2Joint* m_joints[8]; -}; - -static int testIndex = RegisterTest("Examples", "Web", Web::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/wheel_joint.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/wheel_joint.cpp deleted file mode 100644 index 08f62427232f..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/wheel_joint.cpp +++ /dev/null @@ -1,121 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "settings.h" -#include "test.h" -#include "ImGui/ImGuiPresenter.h" - -// Test the wheel joint with motor, spring, and limit options. -class WheelJoint : public Test -{ -public: - WheelJoint() - { - b2Body* ground = NULL; - { - b2BodyDef bd; - ground = m_world->CreateBody(&bd); - - b2EdgeShape shape; - shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f)); - ground->CreateFixture(&shape, 0.0f); - } - - m_enableLimit = true; - m_enableMotor = false; - m_motorSpeed = 10.0f; - - { - b2CircleShape shape; - shape.m_radius = 2.0f; - - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(0.0f, 10.0f); - bd.allowSleep = false; - b2Body* body = m_world->CreateBody(&bd); - body->CreateFixture(&shape, 5.0f); - - b2WheelJointDef jd; - - // Horizontal - jd.Initialize(ground, body, bd.position, b2Vec2(0.0f, 1.0f)); - - jd.motorSpeed = m_motorSpeed; - jd.maxMotorTorque = 10000.0f; - jd.enableMotor = m_enableMotor; - jd.lowerTranslation = -3.0f; - jd.upperTranslation = 3.0f; - jd.enableLimit = m_enableLimit; - - float hertz = 1.0f; - float dampingRatio = 0.7f; - b2LinearStiffness(jd.stiffness, jd.damping, hertz, dampingRatio, ground, body); - - m_joint = (b2WheelJoint*)m_world->CreateJoint(&jd); - } - } - - void Step(Settings& settings) override - { - Test::Step(settings); - - float torque = m_joint->GetMotorTorque(settings.m_hertz); - DrawString(5, m_textLine, "Motor Torque = %4.0f", torque); - - b2Vec2 F = m_joint->GetReactionForce(settings.m_hertz); - DrawString(5, m_textLine, "Reaction Force = (%4.1f, %4.1f)", F.x, F.y); - } - - void UpdateUI() override - { - // ImGui::SetNextWindowPos(ImVec2(10.0f, 100.0f)); - ImGui::SetNextWindowSize(ImVec2(200.0f, 100.0f)); - ImGui::Begin("Joint Controls", nullptr, ImGuiWindowFlags_NoResize); - - if (ImGui::Checkbox("Limit", &m_enableLimit)) - { - m_joint->EnableLimit(m_enableLimit); - } - - if (ImGui::Checkbox("Motor", &m_enableMotor)) - { - m_joint->EnableMotor(m_enableMotor); - } - - if (ImGui::SliderFloat("Speed", &m_motorSpeed, -100.0f, 100.0f, "%.0f")) - { - m_joint->SetMotorSpeed(m_motorSpeed); - } - - ImGui::End(); - } - - static Test* Create() { return new WheelJoint; } - - b2WheelJoint* m_joint; - float m_motorSpeed; - bool m_enableMotor; - bool m_enableLimit; -}; - -static int testIndex = RegisterTest("Joints", "Wheel", WheelJoint::Create); diff --git a/tests/cpp-tests/Source/Box2DTestBed/tests/wrecking_ball.cpp b/tests/cpp-tests/Source/Box2DTestBed/tests/wrecking_ball.cpp deleted file mode 100644 index 69a7745e4c89..000000000000 --- a/tests/cpp-tests/Source/Box2DTestBed/tests/wrecking_ball.cpp +++ /dev/null @@ -1,161 +0,0 @@ -// MIT License - -// Copyright (c) 2019 Erin Catto - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "test.h" -#include "ImGui/ImGuiPresenter.h" - -/// This test shows how a distance joint can be used to stabilize a chain of -/// bodies with a heavy payload. Notice that the distance joint just prevents -/// excessive stretching and has no other effect. -/// By disabling the distance joint you can see that the Box2D solver has trouble -/// supporting heavy bodies with light bodies. Try playing around with the -/// densities, time step, and iterations to see how they affect stability. -/// This test also shows how to use contact filtering. Filtering is configured -/// so that the payload does not collide with the chain. -class WreckingBall : public Test -{ -public: - WreckingBall() - { - b2Body* ground = NULL; - { - b2BodyDef bd; - ground = m_world->CreateBody(&bd); - - b2EdgeShape shape; - shape.SetTwoSided(b2Vec2(-40.0f, 0.0f), b2Vec2(40.0f, 0.0f)); - ground->CreateFixture(&shape, 0.0f); - } - - { - b2PolygonShape shape; - shape.SetAsBox(0.5f, 0.125f); - - b2FixtureDef fd; - fd.shape = &shape; - fd.density = 20.0f; - fd.friction = 0.2f; - fd.filter.categoryBits = 0x0001; - fd.filter.maskBits = 0xFFFF & ~0x0002; - - b2RevoluteJointDef jd; - jd.collideConnected = false; - - const int32 N = 10; - const float y = 15.0f; - m_distanceJointDef.localAnchorA.Set(0.0f, y); - - b2Body* prevBody = ground; - for (int32 i = 0; i < N; ++i) - { - b2BodyDef bd; - bd.type = b2_dynamicBody; - bd.position.Set(0.5f + 1.0f * i, y); - if (i == N - 1) - { - bd.position.Set(1.0f * i, y); - bd.angularDamping = 0.4f; - } - - b2Body* body = m_world->CreateBody(&bd); - - if (i == N - 1) - { - b2CircleShape circleShape; - circleShape.m_radius = 1.5f; - b2FixtureDef sfd; - sfd.shape = &circleShape; - sfd.density = 100.0f; - sfd.filter.categoryBits = 0x0002; - body->CreateFixture(&sfd); - } - else - { - body->CreateFixture(&fd); - } - - b2Vec2 anchor(float(i), y); - jd.Initialize(prevBody, body, anchor); - m_world->CreateJoint(&jd); - - prevBody = body; - } - - m_distanceJointDef.localAnchorB.SetZero(); - - float extraLength = 0.01f; - m_distanceJointDef.minLength = 0.0f; - m_distanceJointDef.maxLength = N - 1.0f + extraLength; - m_distanceJointDef.bodyB = prevBody; - } - - { - m_distanceJointDef.bodyA = ground; - m_distanceJoint = m_world->CreateJoint(&m_distanceJointDef); - m_stabilize = true; - } - } - - void UpdateUI() override - { - // ImGui::SetNextWindowPos(ImVec2(Test::g_debugDraw.debugNodeOffset.x, 100.0f)); - ImGui::SetNextWindowSize(ImVec2(200.0f, 100.0f)); - ImGui::Begin("Wrecking Ball Controls", nullptr, ImGuiWindowFlags_NoResize); - - if (ImGui::Checkbox("Stabilize", &m_stabilize)) - { - if (m_stabilize == true && m_distanceJoint == nullptr) - { - m_distanceJoint = m_world->CreateJoint(&m_distanceJointDef); - } - else if (m_stabilize == false && m_distanceJoint != nullptr) - { - m_world->DestroyJoint(m_distanceJoint); - m_distanceJoint = nullptr; - } - } - - ImGui::End(); - } - - void Step(Settings& settings) override - { - Test::Step(settings); - - if (m_distanceJoint) - { - DrawString(5, m_textLine, "Distance Joint ON"); - } - else - { - DrawString(5, m_textLine, "Distance Joint OFF"); - } - } - - static Test* Create() { return new WreckingBall; } - - b2DistanceJointDef m_distanceJointDef; - b2Joint* m_distanceJoint; - bool m_stabilize; -}; - -static int testIndex = RegisterTest("Examples", "Wrecking Ball", WreckingBall::Create); diff --git a/tests/cpp-tests/Source/BugsTest/Bug-1159.cpp b/tests/cpp-tests/Source/BugsTest/Bug-1159.cpp index b0c8cd6cfe93..1596aebd1fad 100644 --- a/tests/cpp-tests/Source/BugsTest/Bug-1159.cpp +++ b/tests/cpp-tests/Source/BugsTest/Bug-1159.cpp @@ -32,10 +32,10 @@ bool Bug1159Layer::init() { auto s = Director::getInstance()->getWinSize(); - auto background = LayerColor::create(Color4B(255, 0, 255, 255)); + auto background = LayerColor::create(Color32(255, 0, 255, 255)); addChild(background); - auto sprite_a = LayerColor::create(Color4B(255, 0, 0, 255), 700, 700); + auto sprite_a = LayerColor::create(Color32(255, 0, 0, 255), 700, 700); sprite_a->setAnchorPoint(Vec2(0.5f, 0.5f)); sprite_a->setIgnoreAnchorPointForPosition(false); sprite_a->setPosition(0.0f, s.height / 2); @@ -44,7 +44,7 @@ bool Bug1159Layer::init() sprite_a->runAction(RepeatForever::create(Sequence::create(MoveTo::create(1.0f, Vec2(1024.0f, 384.0f)), MoveTo::create(1.0f, Vec2(0.0f, 384.0f)), nullptr))); - auto sprite_b = LayerColor::create(Color4B(0, 0, 255, 255), 400, 400); + auto sprite_b = LayerColor::create(Color32(0, 0, 255, 255), 400, 400); sprite_b->setAnchorPoint(Vec2(0.5f, 0.5f)); sprite_b->setIgnoreAnchorPointForPosition(false); sprite_b->setPosition(s.width / 2, s.height / 2); diff --git a/tests/cpp-tests/Source/BugsTest/Bug-12847.cpp b/tests/cpp-tests/Source/BugsTest/Bug-12847.cpp index 9e5f1893287b..6feb3730eab4 100644 --- a/tests/cpp-tests/Source/BugsTest/Bug-12847.cpp +++ b/tests/cpp-tests/Source/BugsTest/Bug-12847.cpp @@ -97,11 +97,11 @@ void Bug12847Layer::update(float dt) void Bug12847Layer::onEnter() { BugsTestBase::onEnter(); - Director::getInstance()->setClearColor(Color4F::RED); + Director::getInstance()->setClearColor(Color::RED); } void Bug12847Layer::onExit() { - Director::getInstance()->setClearColor(Color4F::BLACK); + Director::getInstance()->setClearColor(Color::BLACK); BugsTestBase::onExit(); } \ No newline at end of file diff --git a/tests/cpp-tests/Source/BugsTest/Bug-458/Bug-458.cpp b/tests/cpp-tests/Source/BugsTest/Bug-458/Bug-458.cpp index ce6d44c1384f..001388ade048 100644 --- a/tests/cpp-tests/Source/BugsTest/Bug-458/Bug-458.cpp +++ b/tests/cpp-tests/Source/BugsTest/Bug-458/Bug-458.cpp @@ -48,11 +48,11 @@ bool Bug458Layer::init() // [question2 setContentSize:CGSizeMake(50,50)]; auto sprite = MenuItemSprite::create(question2, question, AX_CALLBACK_1(Bug458Layer::selectAnswer, this)); - auto layer = LayerColor::create(Color4B(0, 0, 255, 255), 100, 100); + auto layer = LayerColor::create(Color32(0, 0, 255, 255), 100, 100); question->release(); question2->release(); - auto layer2 = LayerColor::create(Color4B(255, 0, 0, 255), 100, 100); + auto layer2 = LayerColor::create(Color32(255, 0, 0, 255), 100, 100); auto sprite2 = MenuItemSprite::create(layer, layer2, AX_CALLBACK_1(Bug458Layer::selectAnswer, this)); auto menu = Menu::create(sprite, sprite2, nullptr); menu->alignItemsVerticallyWithPadding(100); diff --git a/tests/cpp-tests/Source/BugsTest/Bug-458/QuestionContainerSprite.cpp b/tests/cpp-tests/Source/BugsTest/Bug-458/QuestionContainerSprite.cpp index 152dbbf77c21..e8c39bd6aa06 100644 --- a/tests/cpp-tests/Source/BugsTest/Bug-458/QuestionContainerSprite.cpp +++ b/tests/cpp-tests/Source/BugsTest/Bug-458/QuestionContainerSprite.cpp @@ -42,7 +42,7 @@ bool QuestionContainerSprite::init() int width = size.width * 0.9f - (corner->getContentSize().width * 2); int height = size.height * 0.15f - (corner->getContentSize().height * 2); - auto layer = LayerColor::create(Color4B(255, 255, 255, 255 * .75), width, height); + auto layer = LayerColor::create(Color32(255, 255, 255, 255 * .75), width, height); layer->setPosition(Vec2(-width / 2, -height / 2)); // First button is blue, diff --git a/tests/cpp-tests/Source/BugsTest/Bug-914.cpp b/tests/cpp-tests/Source/BugsTest/Bug-914.cpp index a323f51d0b91..dd732526bfc5 100644 --- a/tests/cpp-tests/Source/BugsTest/Bug-914.cpp +++ b/tests/cpp-tests/Source/BugsTest/Bug-914.cpp @@ -44,7 +44,7 @@ bool Bug914Layer::init() LayerColor* layer; for (int i = 0; i < 5; i++) { - layer = LayerColor::create(Color4B(i * 20, i * 20, i * 20, 255)); + layer = LayerColor::create(Color32(i * 20, i * 20, i * 20, 255)); layer->setContentSize(Size(i * 100.0f, i * 100.0f)); layer->setPosition(size.width / 2, size.height / 2); layer->setAnchorPoint(Vec2(0.5f, 0.5f)); diff --git a/tests/cpp-tests/Source/BugsTest/Bug-DrawNode.cpp b/tests/cpp-tests/Source/BugsTest/Bug-DrawNode.cpp index 1eacfbe68a43..dbbe77455881 100644 --- a/tests/cpp-tests/Source/BugsTest/Bug-DrawNode.cpp +++ b/tests/cpp-tests/Source/BugsTest/Bug-DrawNode.cpp @@ -49,15 +49,15 @@ bool BugDrawNodeLayer::init() addChild(testSprite); auto drawNode = DrawNode::create(); - drawNode->drawLine(Vec2(0, 0), Vec2(size.width, size.height), Color4F(1, 0, 0, 0.5f)); + drawNode->drawLine(Vec2(0, 0), Vec2(size.width, size.height), Color(1, 0, 0, 0.5f)); Vec2 point = Vec2(size.width / 2, size.height / 2); - drawNode->drawPoint(point, 8, Color4F(1, 0, 0, 0.5f)); + drawNode->drawPoint(point, 8, Color(1, 0, 0, 0.5f)); addChild(drawNode); auto label = Label::create(); label->setString(std::string("If you see a red line with a block at center, the bug is fixed!")); label->setPosition(size.width / 2, size.height / 4); - label->setTextColor(Color4B::ORANGE); + label->setTextColor(Color32::ORANGE); addChild(label); return true; diff --git a/tests/cpp-tests/Source/Camera3DTest/Camera3DTest.cpp b/tests/cpp-tests/Source/Camera3DTest/Camera3DTest.cpp index 1504d9e719f2..a2b471cd6487 100644 --- a/tests/cpp-tests/Source/Camera3DTest/Camera3DTest.cpp +++ b/tests/cpp-tests/Source/Camera3DTest/Camera3DTest.cpp @@ -360,16 +360,16 @@ void Camera3DTestDemo::onEnter() // draw x for (int j = -20; j <= 20; j++) { - line->drawLine(Vec3(-100.0f, 0.0f, 5.0f * j), Vec3(100.0f, 0.0f, 5.0f * j), Color4F(1, 0, 0, 1)); + line->drawLine(Vec3(-100.0f, 0.0f, 5.0f * j), Vec3(100.0f, 0.0f, 5.0f * j), Color(1, 0, 0, 1)); } // draw z for (int j = -20; j <= 20; j++) { - line->drawLine(Vec3(5.0f * j, 0.0f, -100.0f), Vec3(5.0f * j, 0.0f, 100.0f), Color4F(0, 0, 1, 1)); + line->drawLine(Vec3(5.0f * j, 0.0f, -100.0f), Vec3(5.0f * j, 0.0f, 100.0f), Color(0, 0, 1, 1)); } // draw y - line->drawLine(Vec3(0.0f, -50.0f, 0.0f), Vec3(0, 0, 0), Color4F(0, 0.5, 0, 1)); - line->drawLine(Vec3(0, 0, 0), Vec3(0, 50.0f, 0), Color4F(0, 1, 0, 1)); + line->drawLine(Vec3(0.0f, -50.0f, 0.0f), Vec3(0, 0, 0), Color(0, 0.5, 0, 1)); + line->drawLine(Vec3(0, 0, 0), Vec3(0, 50.0f, 0), Color(0, 1, 0, 1)); _layer3D->addChild(line); _layer3D->setCameraMask(2); @@ -809,7 +809,7 @@ void CameraCullingDemo::update(float dt) if (_cameraFirst->isVisibleInFrustum(&aabb)) { aabb.getCorners(corners); - _drawAABB->drawCube(corners, Color4F(0, 1, 0, 1)); + _drawAABB->drawCube(corners, Color(0, 1, 0, 1)); } } } @@ -934,7 +934,7 @@ void CameraCullingDemo::drawCameraFrustum() _drawFrustum->clear(); auto size = Director::getInstance()->getWinSize(); - Color4F color(1.f, 1.f, 0.f, 1); + Color color(1.f, 1.f, 0.f, 1); // top-left Vec3 tl_0, tl_1; @@ -1060,15 +1060,15 @@ void CameraArcBallDemo::onEnter() // draw x for (int j = -20; j <= 20; j++) { - _drawGrid->drawLine(Vec3(-100.0f, 0, 5.0f * j), Vec3(100.0f, 0, 5.0f * j), Color4F(1, 0, 0, 1)); + _drawGrid->drawLine(Vec3(-100.0f, 0, 5.0f * j), Vec3(100.0f, 0, 5.0f * j), Color(1, 0, 0, 1)); } // draw z for (int j = -20; j <= 20; j++) { - _drawGrid->drawLine(Vec3(5.0f * j, 0, -100.0f), Vec3(5.0f * j, 0, 100.0f), Color4F(0, 0, 1, 1)); + _drawGrid->drawLine(Vec3(5.0f * j, 0, -100.0f), Vec3(5.0f * j, 0, 100.0f), Color(0, 0, 1, 1)); } // draw y - _drawGrid->drawLine(Vec3(0, 0, 0), Vec3(0, 50.0f, 0), Color4F(0, 1, 0, 1)); + _drawGrid->drawLine(Vec3(0, 0, 0), Vec3(0, 50.0f, 0), Color(0, 1, 0, 1)); _layer3D->addChild(_drawGrid); _layer3D->setCameraMask(2); @@ -1221,7 +1221,7 @@ void FogTestDemo::onEnter() { CameraBaseTest::onEnter(); schedule(AX_SCHEDULE_SELECTOR(FogTestDemo::update), 0.0f); - Director::getInstance()->setClearColor(Color4F(0.5, 0.5, 0.5, 1)); + Director::getInstance()->setClearColor(Color(0.5, 0.5, 0.5, 1)); auto s = Director::getInstance()->getWinSize(); auto listener = EventListenerTouchAllAtOnce::create(); @@ -1296,7 +1296,7 @@ void FogTestDemo::onEnter() #if (AX_TARGET_PLATFORM == AX_PLATFORM_ANDROID) _backToForegroundListener = EventListenerCustom::create(EVENT_RENDERER_RECREATED, [this](EventCustom*) { - Director::getInstance()->setClearColor(Color4F(0.5, 0.5, 0.5, 1)); + Director::getInstance()->setClearColor(Color(0.5, 0.5, 0.5, 1)); AX_SAFE_RELEASE_NULL(_programState1); AX_SAFE_RELEASE_NULL(_programState2); @@ -1360,7 +1360,7 @@ void FogTestDemo::switchTypeCallback(Object* sender, int type) void FogTestDemo::onExit() { CameraBaseTest::onExit(); - Director::getInstance()->setClearColor(Color4F(0, 0, 0, 1)); + Director::getInstance()->setClearColor(Color(0, 0, 0, 1)); if (_camera) { _camera = nullptr; @@ -1460,7 +1460,7 @@ void FogTestDemo::onTouchesMoved(const std::vector& touches, ax::Event* // camera->setCameraFlag(CameraFlag::USER1); // camera->setDepth(-1); // camera->setFrameBufferObject(fbo); -// fbo->setClearColor(Color4F(1,1,1,1)); +// fbo->setClearColor(Color(1,1,1,1)); // addChild(camera); // } @@ -1511,7 +1511,7 @@ void BackgroundColorBrushTest::onEnter() // 2nd Camera auto camera = Camera::createPerspective(60, (float)s.width / s.height, 1, 1000); - auto colorBrush = CameraBackgroundBrush::createColorBrush(Color4F(.1f, .1f, 1.f, .5f), 1.f); + auto colorBrush = CameraBackgroundBrush::createColorBrush(Color(.1f, .1f, 1.f, .5f), 1.f); camera->setBackgroundBrush(colorBrush); camera->setPosition3D(Vec3(0.0f, 0.0f, 200.0f)); camera->lookAt(Vec3::ZERO); @@ -1527,7 +1527,7 @@ void BackgroundColorBrushTest::onEnter() slider->setPosition(Vec2(s.width / 2, s.height / 4)); slider->setPercent(50); slider->addEventListener([slider, colorBrush](Object*, ui::Slider::EventType) { - colorBrush->setColor(Color4F(.1f, .1f, 1.f, (float)slider->getPercent() / 100.f)); + colorBrush->setColor(Color(.1f, .1f, 1.f, (float)slider->getPercent() / 100.f)); }); addChild(slider); diff --git a/tests/cpp-tests/Source/ChipmunkTest/ChipmunkTest.cpp b/tests/cpp-tests/Source/ChipmunkTest/ChipmunkTest.cpp deleted file mode 100644 index 3c2ea6873a40..000000000000 --- a/tests/cpp-tests/Source/ChipmunkTest/ChipmunkTest.cpp +++ /dev/null @@ -1,279 +0,0 @@ -/**************************************************************************** - Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - Copyright (c) 2021 @aismann; Peter Eismann, Germany; dreifrankensoft - - https://axmol.dev/ - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - ****************************************************************************/ - -// -// Accelerometer + Chipmunk physics + multi touches example -// a cocos2d example -// https://axmol.dev/ -// - -#include "chipmunk/chipmunk.h" - -#include "ChipmunkTest.h" - -using namespace ax; -USING_NS_AX_EXT; - -enum -{ - kTagParentNode = 1, -}; - -enum -{ - Z_PHYSICS_DEBUG = 100, -}; - -// callback to remove Shapes from the Space - -ChipmunkTest::ChipmunkTest() -{ - // enable events - - auto touchListener = EventListenerTouchAllAtOnce::create(); - touchListener->onTouchesEnded = AX_CALLBACK_2(ChipmunkTest::onTouchesEnded, this); - _eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener, this); - - Device::setAccelerometerEnabled(true); - auto accListener = EventListenerAcceleration::create(AX_CALLBACK_2(ChipmunkTest::onAcceleration, this)); - _eventDispatcher->addEventListenerWithSceneGraphPriority(accListener, this); - - // title - auto label = Label::createWithTTF("Multi touch the screen", "fonts/Marker Felt.ttf", 36.0f); - label->setPosition(VisibleRect::center().x, VisibleRect::top().y - 30); - this->addChild(label, -1); - - // reset button - createResetButton(); - - // init physics - - physicsDebugNodeOffset = Vec2(0, 0); - initPhysics(); - -#if 1 - // Use batch node. Faster - auto parent = SpriteBatchNode::create("Images/grossini_dance_atlas.png", 100); - _spriteTexture = parent->getTexture(); -#else - // doesn't use batch node. Slower - _spriteTexture = Director::getInstance()->getTextureCache()->addImage("Images/grossini_dance_atlas.png"); - auto parent = Node::create(); -#endif - addChild(parent, 0, kTagParentNode); - - addNewSpriteAtPosition(ax::Vec2(200.0f, 200.0f)); - - // menu for debug layer - MenuItemFont::setFontSize(18); - auto item = MenuItemFont::create("Toggle debug", AX_CALLBACK_1(ChipmunkTest::toggleDebugCallback, this)); - - auto menu = Menu::create(item, nullptr); - this->addChild(menu); - menu->setPosition(VisibleRect::right().x - 100, VisibleRect::top().y - 60); - - scheduleUpdate(); -} - -void ChipmunkTest::toggleDebugCallback(Object* sender) -{ - _debugLayer->setVisible(!_debugLayer->isVisible()); -} - -ChipmunkTest::~ChipmunkTest() -{ - // manually Free rogue shapes - for (int i = 0; i < 4; i++) - { - cpShapeFree(_walls[i]); - } - -#if AX_TARGET_PLATFORM == AX_PLATFORM_WIN32 - cpSpaceFree(_space); -#else - cpHastySpaceFree(_space); -#endif - - Device::setAccelerometerEnabled(false); -} - -void ChipmunkTest::initPhysics() -{ - - // init chipmunk - // cpInitChipmunk(); - -#if AX_TARGET_PLATFORM == AX_PLATFORM_WIN32 - _space = cpSpaceNew(); -#else - _space = cpHastySpaceNew(); - cpHastySpaceSetThreads(_space, 0); -#endif - - cpSpaceSetGravity(_space, cpv(0.0f, -100.0f)); - - // - // rogue shapes - // We have to free them manually - // - // bottom - _walls[0] = - cpSegmentShapeNew(cpSpaceGetStaticBody(_space), cpv(VisibleRect::leftBottom().x, VisibleRect::leftBottom().y), - cpv(VisibleRect::rightBottom().x, VisibleRect::rightBottom().y), 0.0f); - - // top - _walls[1] = cpSegmentShapeNew(cpSpaceGetStaticBody(_space), cpv(VisibleRect::leftTop().x, VisibleRect::leftTop().y), - cpv(VisibleRect::rightTop().x, VisibleRect::rightTop().y), 0.0f); - - // left - _walls[2] = - cpSegmentShapeNew(cpSpaceGetStaticBody(_space), cpv(VisibleRect::leftBottom().x, VisibleRect::leftBottom().y), - cpv(VisibleRect::leftTop().x, VisibleRect::leftTop().y), 0.0f); - - // right - _walls[3] = - cpSegmentShapeNew(cpSpaceGetStaticBody(_space), cpv(VisibleRect::rightBottom().x, VisibleRect::rightBottom().y), - cpv(VisibleRect::rightTop().x, VisibleRect::rightTop().y), 0.0f); - - for (int i = 0; i < 4; i++) - { - - cpShapeSetElasticity(_walls[i], 1.0f); - cpShapeSetFriction(_walls[i], 1.0f); - cpSpaceAddShape(_space, _walls[i]); - } - - // Physics debug layer - _debugLayer = PhysicsDebugNodeChipmunk2D::create(_space); - this->addChild(_debugLayer, Z_PHYSICS_DEBUG); -} - -void ChipmunkTest::update(float delta) -{ - // Should use a fixed size step based on the animation interval. - int steps = 2; - float dt = Director::getInstance()->getAnimationInterval() / (float)steps; - - for (int i = 0; i < steps; i++) - { - -#if AX_TARGET_PLATFORM == AX_PLATFORM_WIN32 - cpSpaceStep(_space, dt); -#else - cpHastySpaceStep(_space, dt); -#endif - } -} - -void ChipmunkTest::createResetButton() -{ - auto reset = MenuItemImage::create("Images/r1.png", "Images/r2.png", AX_CALLBACK_1(ChipmunkTest::reset, this)); - - auto menu = Menu::create(reset, nullptr); - - menu->setPosition(VisibleRect::center().x, VisibleRect::bottom().y + 30); - this->addChild(menu, -1); -} - -void ChipmunkTest::reset(Object* sender) -{ - getTestSuite()->restartCurrTest(); -} - -void ChipmunkTest::addNewSpriteAtPosition(ax::Vec2 pos) -{ - int posx, posy; - - auto parent = getChildByTag(kTagParentNode); - - posx = AXRANDOM_0_1() * 200.0f; - posy = AXRANDOM_0_1() * 200.0f; - - posx = (posx % 4) * 85; - posy = (posy % 3) * 121; - - int num = 4; - cpVect verts[] = { - cpv(-24, -54), - cpv(-24, 54), - cpv(24, 54), - cpv(24, -54), - }; - - cpBody* body = cpBodyNew(1.0f, cpMomentForPoly(1.0f, num, verts, cpvzero, 0.0f)); - - cpBodySetPosition(body, cpv(pos.x, pos.y)); - cpSpaceAddBody(_space, body); - - cpShape* shape = cpPolyShapeNew(body, num, verts, cpTransformIdentity, 0.0f); - cpShapeSetElasticity(shape, 0.5f); - cpShapeSetFriction(shape, 0.5f); - cpSpaceAddShape(_space, shape); - - auto sprite = PhysicsSpriteChipmunk2D::createWithTexture(_spriteTexture, ax::Rect(posx, posy, 85, 121)); - parent->addChild(sprite); - - sprite->setCPBody(body); - sprite->setPosition(pos); -} - -void ChipmunkTest::onEnter() -{ - TestCase::onEnter(); -} - -void ChipmunkTest::onTouchesEnded(const std::vector& touches, Event* event) -{ - // Add a new body/atlas sprite at the touched location - - for (auto& touch : touches) - { - auto location = touch->getLocation(); - - addNewSpriteAtPosition(location); - } -} - -void ChipmunkTest::onAcceleration(Acceleration* acc, Event* event) -{ - static float prevX = 0, prevY = 0; - -#define kFilterFactor 0.05f - - float accelX = (float)acc->x * kFilterFactor + (1 - kFilterFactor) * prevX; - float accelY = (float)acc->y * kFilterFactor + (1 - kFilterFactor) * prevY; - - prevX = accelX; - prevY = accelY; - - auto v = ax::Vec2(accelX, accelY); - v = v * 200; - cpSpaceSetGravity(_space, cpv(v.x, v.y)); -} - -ChipmunkTests::ChipmunkTests() -{ - ADD_TEST_CASE(ChipmunkTest); -} diff --git a/tests/cpp-tests/Source/ChipmunkTest/ChipmunkTest.h b/tests/cpp-tests/Source/ChipmunkTest/ChipmunkTest.h deleted file mode 100644 index 4da9edf3e65c..000000000000 --- a/tests/cpp-tests/Source/ChipmunkTest/ChipmunkTest.h +++ /dev/null @@ -1,66 +0,0 @@ -/**************************************************************************** - Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. - Copyright (c) 2021 @aismann; Peter Eismann, Germany; dreifrankensoft - - https://axmol.dev/ - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - ****************************************************************************/ - -// -// cocos2d -// -#ifndef __CHIPMUNKTEST_H__ -#define __CHIPMUNKTEST_H__ - -#include "axmol.h" -#include "chipmunk/chipmunk.h" -#include "../BaseTest.h" -#include "extensions/axmol-ext.h" - -class ChipmunkTest : public TestCase -{ -public: - CREATE_FUNC(ChipmunkTest); - - ChipmunkTest(); - ~ChipmunkTest(); - void onEnter() override; - void initPhysics(); - void createResetButton(); - void reset(ax::Object* sender); - - void addNewSpriteAtPosition(ax::Vec2 p); - void update(float dt) override; - void toggleDebugCallback(ax::Object* sender); - void onTouchesEnded(const std::vector& touches, ax::Event* event); - virtual void onAcceleration(ax::Acceleration* acc, ax::Event* event); - -private: - ax::Texture2D* _spriteTexture; // weak ref - ax::extension::PhysicsDebugNodeChipmunk2D* _debugLayer; // weak ref - - cpSpace* _space; // strong ref - cpShape* _walls[4]; - ax::DrawNode* drawNode; -}; - -DEFINE_TEST_SUITE(ChipmunkTests); - -#endif /* __CHIPMUNKACCELTOUCHTEST_H__ */ diff --git a/tests/cpp-tests/Source/ChipmunkTestBed/ChipmunkTestBed.cpp b/tests/cpp-tests/Source/ChipmunkTestBed/ChipmunkTestBed.cpp deleted file mode 100644 index 4166ffdd8dd9..000000000000 --- a/tests/cpp-tests/Source/ChipmunkTestBed/ChipmunkTestBed.cpp +++ /dev/null @@ -1,1263 +0,0 @@ -/**************************************************************************** - * Copyright (c) 2021 @aismann; Peter Eismann, Germany; dreifrankensoft - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - ****************************************************************************/ - -#include -#include -#include -#include -#include - -#include "chipmunk/chipmunk_private.h" -#include "chipmunk/chipmunk.h" - -#include "ChipmunkTestBed.h" - -using namespace ax; -USING_NS_AX_EXT; - -enum -{ - kTagParentNode = 1, -}; - -enum -{ - Z_PHYSICS_DEBUG = 100, -}; - -extern ChipmunkDemo Example; // Use as template for new Chipmunk2D demos -extern ChipmunkDemo LogoSmash; -extern ChipmunkDemo PyramidStack; -extern ChipmunkDemo Plink; -extern ChipmunkDemo Tumble; -extern ChipmunkDemo PyramidTopple; -extern ChipmunkDemo Planet; -extern ChipmunkDemo Springies; -extern ChipmunkDemo Pump; -extern ChipmunkDemo TheoJansen; -extern ChipmunkDemo Query; -extern ChipmunkDemo OneWay; -extern ChipmunkDemo PlatformerPlayer; -extern ChipmunkDemo Joints; -extern ChipmunkDemo Tank; -extern ChipmunkDemo Chains; -extern ChipmunkDemo Crane; -extern ChipmunkDemo Buoyancy; -extern ChipmunkDemo ContactGraph; -extern ChipmunkDemo Slice; -extern ChipmunkDemo Convex; -extern ChipmunkDemo Unicycle; -extern ChipmunkDemo Sticky; -extern ChipmunkDemo Shatter; -extern ChipmunkDemo bench_list[]; -extern int bench_count; -int bench = 16; - -static Vec2 cpVert2Point(const cpVect& vert) -{ - return Vec2(vert.x, vert.y) + physicsDebugNodeOffset; -} - -ChipmunkDemo demos[] = {LogoSmash, PyramidStack, Plink, Tumble, PyramidTopple, Planet, - Springies, Pump, TheoJansen, Query, OneWay, Joints, - Tank, Chains, Crane, ContactGraph, Buoyancy, PlatformerPlayer, - Slice, Convex, Unicycle, Sticky, Shatter}; - -int demo_count = sizeof(demos); - -cpVect ChipmunkDemoMouse; -cpVect ChipmunkDemoKeyboard; -cpBool ChipmunkDemoRightClick = cpFalse; -cpBool ChipmunkDemoRightDown = cpFalse; -cpBool ChipmunkDemoLeftDown = cpFalse; -double ChipmunkDemoTime; - -cpBody* mouse_body = cpBodyNewKinematic(); -static cpConstraint* mouse_joint = nullptr; - -char const* ChipmunkDemoMessageString = nullptr; - -#define GRABBABLE_MASK_BIT (1 << 31) -cpShapeFilter GRAB_FILTER = {CP_NO_GROUP, (unsigned int)GRABBABLE_MASK_BIT, (unsigned int)GRABBABLE_MASK_BIT}; -cpShapeFilter NOT_GRABBABLE_FILTER = {CP_NO_GROUP, ~GRABBABLE_MASK_BIT, ~GRABBABLE_MASK_BIT}; - -ax::DrawNode* drawCP = nullptr; - -void ChipmunkDemoDefaultDrawImpl(cpSpace* space){}; - -void ChipmunkDebugDrawDot(cpFloat size, cpVect pos, cpSpaceDebugColor fillColor) -{ - - drawCP->drawPoint(Vec2(pos.x, pos.y) + physicsDebugNodeOffset, size, - Color4F(fillColor.r, fillColor.g, fillColor.b, fillColor.a)); -} - -void ChipmunkDebugDrawCircle(cpVect pos, - cpFloat angle, - cpFloat radius, - cpSpaceDebugColor outlineColor, - cpSpaceDebugColor fillColor) -{ - - drawCP->drawCircle(Vec2(pos.x, pos.y) + physicsDebugNodeOffset, 100, AX_DEGREES_TO_RADIANS(90), 50, true, 1.0f, - 2.0f, Color4F(fillColor.r, fillColor.g, fillColor.b, fillColor.a)); -} - -void ChipmunkDebugDrawSegment(cpVect a, cpVect b, cpSpaceDebugColor color) -{ - drawCP->drawLine(Vec2(a.x, a.y) + physicsDebugNodeOffset, Vec2(b.x, b.y) + physicsDebugNodeOffset, - Color4F(color.r, color.g, color.b, color.a)); -} - -void ChipmunkDebugDrawFatSegment(cpVect a, - cpVect b, - cpFloat radius, - cpSpaceDebugColor outlineColor, - cpSpaceDebugColor fillColor) -{ - - drawCP->drawSegment(Vec2(a.x, a.y) + physicsDebugNodeOffset, Vec2(b.x, b.y) + physicsDebugNodeOffset, radius, - Color4F(outlineColor.r, outlineColor.g, outlineColor.b, outlineColor.a)); -} - -void ChipmunkDebugDrawPolygon(int count, - const cpVect* verts, - cpFloat radius, - cpSpaceDebugColor outlineColor, - cpSpaceDebugColor fillColor) -{ - - Vec2* vec = new Vec2[count]; - for (size_t i = 0; i < count; i++) - { - vec[i] = cpVert2Point(verts[i]); - } - drawCP->drawPolygon(vec, count, Color4F(1.0f, 0.0f, 0.0f, 0.5f), radius, Color4F(0.0f, 0.0f, 1.0f, 1.0f)); - - delete[] vec; -} - -void ChipmunkDebugDrawBB(cpBB bb, cpSpaceDebugColor color) -{ - Vec2 verts[] = { - Vec2(bb.r, bb.b) + physicsDebugNodeOffset, - Vec2(bb.r, bb.t) + physicsDebugNodeOffset, - Vec2(bb.l, bb.t) + physicsDebugNodeOffset, - Vec2(bb.l, bb.b) + physicsDebugNodeOffset, - }; - drawCP->drawPolygon(verts, sizeof(verts) / sizeof(verts[0]), Color4F(1.0f, 0.0f, 0.0f, 0.0f), 1, - Color4F(0.0f, 0.0f, 1.0f, 1.0f)); -} - -ax::Label* label; - -static int max_arbiters = 0; -static int max_points = 0; -static int max_constraints = 0; - -void ChipmunkTestBed::DrawInfo() -{ - int arbiters = _space->arbiters->num; - int points = 0; - - for (int i = 0; i < arbiters; i++) - points += ((cpArbiter*)(_space->arbiters->arr[i]))->count; - - int constraints = (_space->constraints->num + points) * _space->iterations; - - max_arbiters = arbiters > max_arbiters ? arbiters : max_arbiters; - max_points = points > max_points ? points : max_points; - max_constraints = constraints > max_constraints ? constraints : max_constraints; - - char buffer[1024]; - const char* format = - "Arbiters: %d (%d) - " - "Contact Points: %d (%d)\n" - "Other Constraints: %d, Iterations: %d\n" - "Constraints x Iterations: %d (%d)\n" - "Time:% 5.2fs, KE:% 5.2e"; - - cpArray* bodies = _space->dynamicBodies; - cpFloat ke = 0.0f; - for (int i = 0; i < bodies->num; i++) - { - cpBody* body = (cpBody*)bodies->arr[i]; - if (body->m == INFINITY || body->i == INFINITY) - continue; - - ke += body->m * cpvdot(body->v, body->v) + body->i * body->w * body->w; - } - - sprintf(buffer, format, arbiters, max_arbiters, points, max_points, _space->constraints->num, _space->iterations, - constraints, max_constraints, ChipmunkDemoTime, (ke < 1e-10f ? 0.0f : ke)); - - drawInfo->setString(buffer); -} - -static char PrintStringBuffer[1024 * 8]; -static char* PrintStringCursor; -void ChipmunkDemoPrintString(char const* fmt, ...) -{ - if (PrintStringCursor == nullptr) - { - return; - } - - ChipmunkDemoMessageString = PrintStringBuffer; - - va_list args; - va_start(args, fmt); - int remaining = sizeof(PrintStringBuffer) - (PrintStringCursor - PrintStringBuffer); - int would_write = vsnprintf(PrintStringCursor, remaining, fmt, args); - if (would_write > 0 && would_write < remaining) - { - PrintStringCursor += would_write; - } - else - { - // encoding error or overflow, prevent further use until reinitialized - PrintStringCursor = nullptr; - } - va_end(args); - - label->setString(ChipmunkDemoMessageString); -} - -cpSpaceDebugColor RGBAColor(float r, float g, float b, float a) -{ - cpSpaceDebugColor color = {r, g, b, a}; - return color; -}; - -cpSpaceDebugColor LAColor(float l, float a) -{ - cpSpaceDebugColor color = {l, l, l, a}; - return color; -} - -static void ShapeFreeWrap(cpSpace* space, cpShape* shape, void* unused) -{ - cpSpaceRemoveShape(space, shape); - cpShapeFree(shape); -} - -static void PostShapeFree(cpShape* shape, cpSpace* space) -{ - cpSpaceAddPostStepCallback(space, (cpPostStepFunc)ShapeFreeWrap, shape, nullptr); -} - -static void ConstraintFreeWrap(cpSpace* space, cpConstraint* constraint, void* unused) -{ - mouse_joint = nullptr; - cpSpaceRemoveConstraint(space, constraint); - cpConstraintFree(constraint); -} - -static void PostConstraintFree(cpConstraint* constraint, cpSpace* space) -{ - cpSpaceAddPostStepCallback(space, (cpPostStepFunc)ConstraintFreeWrap, constraint, nullptr); -} - -static void BodyFreeWrap(cpSpace* space, cpBody* body, void* unused) -{ - cpSpaceRemoveBody(space, body); - cpBodyFree(body); -} - -static void PostBodyFree(cpBody* body, cpSpace* space) -{ - cpSpaceAddPostStepCallback(space, (cpPostStepFunc)BodyFreeWrap, body, nullptr); -} - -// Safe and future proof way to remove and free all objects that have been added to the space. -void ChipmunkDemoFreeSpaceChildren(cpSpace* space) -{ - // Must remove these BEFORE freeing the body or you will access dangling pointers. - cpSpaceEachShape(space, (cpSpaceShapeIteratorFunc)PostShapeFree, space); - cpSpaceEachConstraint(space, (cpSpaceConstraintIteratorFunc)PostConstraintFree, space); - cpSpaceEachBody(space, (cpSpaceBodyIteratorFunc)PostBodyFree, space); -} - -void updateMouseBody(void) -{ - cpVect new_point = cpvlerp(mouse_body->p, ChipmunkDemoMouse, 0.25f); - mouse_body->v = cpvmult(cpvsub(new_point, mouse_body->p), 60.0f); - mouse_body->p = new_point; -} - -ChipmunkTestBed::ChipmunkTestBed() -{ - // halx99: since axmol init scene default camera at 'initWithXXX' function, only change design size at scene - // construct is ok see also: https://github.com/axmolengine/axmol/commit/581a7921554c09746616759d5a5ca6ce9d3eaa22 - auto director = Director::getInstance(); - auto glView = director->getGLView(); - Size designSize(g_designSize.width * 0.85, g_designSize.height * 0.85); - glView->setDesignResolutionSize(designSize.width, designSize.height, ResolutionPolicy::SHOW_ALL); - - // creating a keyboard event listener - auto listener = EventListenerKeyboard::create(); - listener->onKeyPressed = [](EventKeyboard::KeyCode keyCode, Event* event) { - if ((int)keyCode == 26) // Left - { - ChipmunkDemoKeyboard.x--; - } - if ((int)keyCode == 27) // Right - { - ChipmunkDemoKeyboard.x++; - } - if ((int)keyCode == 28) // Up - { - ChipmunkDemoKeyboard.y++; - } - if ((int)keyCode == 29) // Down - { - ChipmunkDemoKeyboard.y--; - } - }; - - listener->onKeyReleased = [](EventKeyboard::KeyCode keyCode, Event* event) { ChipmunkDemoKeyboard = {0, 0}; }; - - _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this); - - // creating a mouse event listener - _mouseListener = EventListenerMouse::create(); - _mouseListener->onMouseMove = AX_CALLBACK_1(ChipmunkTestBed::onMouseMove, this); - _mouseListener->onMouseUp = AX_CALLBACK_1(ChipmunkTestBed::onMouseUp, this); - _mouseListener->onMouseDown = AX_CALLBACK_1(ChipmunkTestBed::onMouseDown, this); - _eventDispatcher->addEventListenerWithSceneGraphPriority(_mouseListener, this); - - // Some info text - auto label1 = Label::createWithTTF("Use the mouse to grab objects.", "fonts/Marker Felt.ttf", 12.0f); - label1->setPosition(VisibleRect::center().x, VisibleRect::top().y - 100); - label1->setColor(Color3B::WHITE); - this->addChild(label1, 1000); - - // ChipmunkDemoMessageString - label = Label::createWithTTF("", "fonts/Marker Felt.ttf", 12.0f); - label->setPosition(VisibleRect::center().x, VisibleRect::top().y - 130); - label->setColor(Color3B::MAGENTA); - this->addChild(label, 1000); - - drawInfo = Label::createWithTTF("Use the mouse to grab objects.", "fonts/Marker Felt.ttf", 12.0f); - drawInfo->setAnchorPoint(Vec2(0, 0)); - drawInfo->setPosition(VisibleRect::left().x + 10, VisibleRect::top().y - 60); - drawInfo->setColor(Color3B::WHITE); - this->addChild(drawInfo, 1000); - - draw = DrawNode::create(); - addChild(draw, 100); - - drawCP = draw; - - scheduleUpdate(); -} - -ChipmunkTestBed::~ChipmunkTestBed() -{ - ChipmunkDemoFreeSpaceChildren(_space); - _eventDispatcher->removeEventListener(_mouseListener); -} - -void ChipmunkTestBed::initPhysics() -{ - if (ChipmunkDemoMessageString) - { - label->setString(ChipmunkDemoMessageString); - } - else - { - label->setString(""); - } - drawCP->clear(); - // Physics debug layer - _debugLayer = PhysicsDebugNodeChipmunk2D::create(_space); - this->addChild(_debugLayer, Z_PHYSICS_DEBUG); -} - -void ChipmunkTestBed::update(float delta) -{ - //#if AX_TARGET_PLATFORM == AX_PLATFORM_WIN32 - // cpSpaceStep(_space, delta); - //#else - // cpHastySpaceStep(_space, delta); - //#endif -} - -void ChipmunkTestBed::createResetButton() -{ - auto reset = MenuItemImage::create("Images/r1.png", "Images/r2.png", AX_CALLBACK_1(ChipmunkTestBed::reset, this)); - auto menu = Menu::create(reset, nullptr); - menu->setPosition(VisibleRect::center().x, VisibleRect::bottom().y); - this->addChild(menu, -1); -} - -void ChipmunkTestBed::reset(Object* sender) -{ - ChipmunkDemoFreeSpaceChildren(_space); - getTestSuite()->restartCurrTest(); -} - -void ChipmunkTestBed::onEnter() -{ - TestCase::onEnter(); - physicsDebugNodeOffset = VisibleRect::center(); - physicsDebugNodeOffset.y += 20; - ChipmunkDemoMessageString = ""; - label->setString(""); -} - -void ChipmunkTestBed::onMouseDown(Event* event) -{ - EventMouse* e = (EventMouse*)event; - - if ((int)e->getMouseButton() == 0) - { - ChipmunkDemoLeftDown = cpTrue; - // give the mouse click a little radius to make it easier to click small shapes. - cpFloat radius = 5.0; - - cpPointQueryInfo info = {0}; - cpShape* shape = cpSpacePointQueryNearest(_space, ChipmunkDemoMouse, radius, GRAB_FILTER, &info); - - if (shape && cpBodyGetMass(cpShapeGetBody(shape)) < INFINITY) - { - // Use the closest point on the surface if the click is outside of the shape. - cpVect nearest = (info.distance > 0.0f ? info.point : ChipmunkDemoMouse); - - cpBody* body = cpShapeGetBody(shape); - mouse_joint = cpPivotJointNew2(mouse_body, body, cpvzero, cpBodyWorldToLocal(body, nearest)); - mouse_joint->maxForce = 50000.0f; - mouse_joint->errorBias = cpfpow(1.0f - 0.15f, 60.0f); - cpSpaceAddConstraint(_space, mouse_joint); - } - } - else if ((int)e->getMouseButton() == 1) - { - if (mouse_joint) - { - cpSpaceRemoveConstraint(_space, mouse_joint); - cpConstraintFree(mouse_joint); - mouse_joint = nullptr; - } - ChipmunkDemoRightDown = cpTrue; - ChipmunkDemoRightClick = cpTrue; - } -} - -void ChipmunkTestBed::onMouseUp(Event* event) -{ - EventMouse* e = (EventMouse*)event; - mousePresses = false; - if (mouse_joint) - { - cpSpaceRemoveConstraint(_space, mouse_joint); - cpConstraintFree(mouse_joint); - mouse_joint = nullptr; - } - ChipmunkDemoLeftDown = cpFalse; - ChipmunkDemoRightDown = cpFalse; - ChipmunkDemoRightClick = cpFalse; -} - -void ChipmunkTestBed::onMouseMove(Event* event) -{ - EventMouse* e = (EventMouse*)event; - auto pt = e->getLocation(); - ChipmunkDemoMouse.x = pt.x - physicsDebugNodeOffset.x; - ChipmunkDemoMouse.y = pt.y - physicsDebugNodeOffset.y; - - cpBodySetPosition(mouse_body, ChipmunkDemoMouse); -} - -void ChipmunkTestBed::updateInit(ChipmunkDemo tt) -{ - PrintStringBuffer[0] = 0; - PrintStringCursor = PrintStringBuffer; - - drawCP->clear(); - updateMouseBody(); - ChipmunkDemoTime += tt.timestep; - ChipmunkTestBed::DrawInfo(); - tt.updateFunc(_space, tt.timestep); -} - -//------------------------------------------------------------------ -// -// LogoSmashDemo -// -//------------------------------------------------------------------ -void LogoSmashDemo::onEnter() -{ - ChipmunkTestBed::onEnter(); - initPhysics(); -} - -std::string LogoSmashDemo::title() const -{ - return LogoSmash.name; -} - -void LogoSmashDemo::initPhysics() -{ - _space = LogoSmash.initFunc(); - ChipmunkTestBed::initPhysics(); -} - -void LogoSmashDemo::update(float delta) -{ - ChipmunkTestBed::updateInit(LogoSmash); -} - -//------------------------------------------------------------------ -// -// PlinkDemo -// -//------------------------------------------------------------------ -void PlinkDemo::onEnter() -{ - ChipmunkTestBed::onEnter(); - - initPhysics(); -} - -std::string PlinkDemo::title() const -{ - return Plink.name; -} - -void PlinkDemo::initPhysics() -{ - _space = Plink.initFunc(); - ChipmunkTestBed::initPhysics(); -} - -void PlinkDemo::update(float delta) -{ - ChipmunkTestBed::updateInit(Plink); -} - -//------------------------------------------------------------------ -// -// TumbleDemo -// -//------------------------------------------------------------------ -void TumbleDemo::onEnter() -{ - ChipmunkTestBed::onEnter(); - initPhysics(); -} - -std::string TumbleDemo::title() const -{ - return Tumble.name; -} - -void TumbleDemo::initPhysics() -{ - _space = Tumble.initFunc(); - ChipmunkTestBed::initPhysics(); -} - -void TumbleDemo::update(float delta) -{ - ChipmunkTestBed::updateInit(Tumble); -} - -//------------------------------------------------------------------ -// -// PyramidStackDemo -// -//------------------------------------------------------------------ -void PyramidStackDemo::onEnter() -{ - ChipmunkTestBed::onEnter(); - initPhysics(); -} - -std::string PyramidStackDemo::title() const -{ - return PyramidStack.name; -} - -void PyramidStackDemo::initPhysics() -{ - _space = PyramidStack.initFunc(); - ChipmunkTestBed::initPhysics(); -} - -void PyramidStackDemo::update(float delta) -{ - ChipmunkTestBed::updateInit(PyramidStack); -} - -//------------------------------------------------------------------ -// -// PyramidToppleDemo -// -//------------------------------------------------------------------ -void PyramidToppleDemo::onEnter() -{ - ChipmunkTestBed::onEnter(); - initPhysics(); -} - -std::string PyramidToppleDemo::title() const -{ - return PyramidTopple.name; -} - -void PyramidToppleDemo::initPhysics() -{ - _space = PyramidTopple.initFunc(); - ChipmunkTestBed::initPhysics(); -} - -void PyramidToppleDemo::update(float delta) -{ - ChipmunkTestBed::updateInit(PyramidTopple); -} - -//------------------------------------------------------------------ -// -// ChainDemo -// -//------------------------------------------------------------------ -void ChainsDemo::onEnter() -{ - ChipmunkTestBed::onEnter(); - - initPhysics(); -} - -std::string ChainsDemo::title() const -{ - return Chains.name; -} - -void ChainsDemo::initPhysics() -{ - _space = Chains.initFunc(); - ChipmunkTestBed::initPhysics(); -} - -void ChainsDemo::update(float delta) -{ - ChipmunkTestBed::updateInit(Chains); -} - -//------------------------------------------------------------------ -// -// OneWayDemo -// -//------------------------------------------------------------------ -void OneWayDemo::onEnter() -{ - ChipmunkTestBed::onEnter(); - - initPhysics(); -} - -std::string OneWayDemo::title() const -{ - return OneWay.name; -} - -void OneWayDemo::initPhysics() -{ - _space = OneWay.initFunc(); - ChipmunkTestBed::initPhysics(); -} - -void OneWayDemo::update(float delta) -{ - ChipmunkTestBed::updateInit(OneWay); -} - -//------------------------------------------------------------------ -// -// PlanetDemo -// -//------------------------------------------------------------------ -void PlanetDemo::onEnter() -{ - ChipmunkTestBed::onEnter(); - - initPhysics(); -} - -std::string PlanetDemo::title() const -{ - return Planet.name; -} - -void PlanetDemo::initPhysics() -{ - _space = Planet.initFunc(); - ChipmunkTestBed::initPhysics(); -} - -void PlanetDemo::update(float delta) -{ - ChipmunkTestBed::updateInit(Planet); -} - -//------------------------------------------------------------------ -// -// TheoJansenDemo -// -//------------------------------------------------------------------ -void TheoJansenDemo::onEnter() -{ - ChipmunkTestBed::onEnter(); - - initPhysics(); -} - -std::string TheoJansenDemo::title() const -{ - return TheoJansen.name; -} - -void TheoJansenDemo::initPhysics() -{ - _space = TheoJansen.initFunc(); - ChipmunkTestBed::initPhysics(); -} - -void TheoJansenDemo::update(float delta) -{ - ChipmunkTestBed::updateInit(TheoJansen); -} - -//------------------------------------------------------------------ -// -// TankDemo -// -//------------------------------------------------------------------ -void TankDemo::onEnter() -{ - ChipmunkTestBed::onEnter(); - - initPhysics(); -} - -std::string TankDemo::title() const -{ - return Tank.name; -} - -void TankDemo::initPhysics() -{ - _space = Tank.initFunc(); - ChipmunkTestBed::initPhysics(); -} - -void TankDemo::update(float delta) -{ - ChipmunkTestBed::updateInit(Tank); -} - -//------------------------------------------------------------------ -// -// BenchDemo -// -//------------------------------------------------------------------ -void BenchDemo::onEnter() -{ - ChipmunkTestBed::onEnter(); - - auto itemPrev = MenuItemImage::create("Images/b1.png", "Images/b2.png", [&](Object* sender) { - bench = (bench > 0) ? bench - 1 : (bench_count - 1); - reset(sender); - }); - - auto itemNext = MenuItemImage::create("Images/f1.png", "Images/f2.png", [&](Object* sender) { - bench = (bench < (bench_count - 1)) ? bench + 1 : 0; - reset(sender); - }); - - auto s = Director::getInstance()->getWinSize(); - - auto menuPrev = Menu::create(itemPrev, nullptr); - menuPrev->alignItemsHorizontally(); - menuPrev->setScale(0.4); - menuPrev->setAnchorPoint(Vec2(0.0f, 0.0f)); - menuPrev->setPosition(Vec2(s.width / 2 - 45, 23.0f)); - addChild(menuPrev); - - auto menuNext = Menu::create(itemNext, nullptr); - menuNext->alignItemsHorizontally(); - menuNext->setScale(0.4); - menuNext->setAnchorPoint(Vec2(0.0f, 0.0f)); - menuNext->setPosition(Vec2(s.width / 2 + 45, 23.0f)); - addChild(menuNext); - - initPhysics(); -} - -std::string BenchDemo::title() const -{ - return "Bench"; -} - -std::string BenchDemo::subtitle() const -{ - std::string s = std::to_string(bench + 1) + ". " + bench_list[bench].name; - return s.c_str(); -} - -void BenchDemo::initPhysics() -{ - _space = bench_list[bench].initFunc(); - ChipmunkTestBed::initPhysics(); -} - -void BenchDemo::update(float delta) -{ - ChipmunkTestBed::updateInit(bench_list[bench]); -} - -//------------------------------------------------------------------ -// -// SpringiesDemo -// -//------------------------------------------------------------------ -void SpringiesDemo::onEnter() -{ - ChipmunkTestBed::onEnter(); - - initPhysics(); -} - -std::string SpringiesDemo::title() const -{ - return Springies.name; -} - -void SpringiesDemo::initPhysics() -{ - _space = Springies.initFunc(); - ChipmunkTestBed::initPhysics(); -} - -void SpringiesDemo::update(float delta) -{ - ChipmunkTestBed::updateInit(Springies); -} - -//------------------------------------------------------------------ -// -// ShatterDemo -// -//------------------------------------------------------------------ -void ShatterDemo::onEnter() -{ - ChipmunkTestBed::onEnter(); - initPhysics(); -} - -std::string ShatterDemo::title() const -{ - return Shatter.name; -} - -void ShatterDemo::initPhysics() -{ - _space = Shatter.initFunc(); - ChipmunkTestBed::initPhysics(); -} - -void ShatterDemo::update(float delta) -{ - ChipmunkTestBed::updateInit(Shatter); -} - -//------------------------------------------------------------------ -// -// StickyDemo -// -//------------------------------------------------------------------ -void StickyDemo::onEnter() -{ - ChipmunkTestBed::onEnter(); - - initPhysics(); -} - -std::string StickyDemo::title() const -{ - return Sticky.name; -} - -void StickyDemo::initPhysics() -{ - _space = Sticky.initFunc(); - ChipmunkTestBed::initPhysics(); -} - -void StickyDemo::update(float delta) -{ - ChipmunkTestBed::updateInit(Sticky); -} - -//------------------------------------------------------------------ -// -// CraneDemo -// -//------------------------------------------------------------------ -void CraneDemo::onEnter() -{ - ChipmunkTestBed::onEnter(); - - initPhysics(); -} - -std::string CraneDemo::title() const -{ - return Crane.name; -} - -void CraneDemo::initPhysics() -{ - _space = Crane.initFunc(); - ChipmunkTestBed::initPhysics(); -} - -void CraneDemo::update(float delta) -{ - ChipmunkTestBed::updateInit(Crane); -} - -//------------------------------------------------------------------ -// -// JointsDemo -// -//------------------------------------------------------------------ -void JointsDemo::onEnter() -{ - ChipmunkTestBed::onEnter(); - - initPhysics(); -} - -std::string JointsDemo::title() const -{ - return Joints.name; -} - -void JointsDemo::initPhysics() -{ - _space = Joints.initFunc(); - ChipmunkTestBed::initPhysics(); -} - -void JointsDemo::update(float delta) -{ - ChipmunkTestBed::updateInit(Joints); -} - -//------------------------------------------------------------------ -// -// ConvexDemo -// -//------------------------------------------------------------------ -void ConvexDemo::onEnter() -{ - ChipmunkTestBed::onEnter(); - - initPhysics(); -} - -std::string ConvexDemo::title() const -{ - return Convex.name; -} - -void ConvexDemo::initPhysics() -{ - _space = Convex.initFunc(); - ChipmunkTestBed::initPhysics(); -} - -void ConvexDemo::update(float delta) -{ - ChipmunkTestBed::updateInit(Convex); -} - -//------------------------------------------------------------------ -// -// PumpDemo -// -//------------------------------------------------------------------ -void PumpDemo::onEnter() -{ - ChipmunkTestBed::onEnter(); - - initPhysics(); -} - -std::string PumpDemo::title() const -{ - return Pump.name; -} - -void PumpDemo::initPhysics() -{ - _space = Pump.initFunc(); - ChipmunkTestBed::initPhysics(); -} - -void PumpDemo::update(float delta) -{ - ChipmunkTestBed::updateInit(Pump); -} - -//------------------------------------------------------------------ -// -// PlatformerPlayerDemo -// -//------------------------------------------------------------------ -void PlatformerPlayerDemo::onEnter() -{ - ChipmunkTestBed::onEnter(); - - initPhysics(); -} - -std::string PlatformerPlayerDemo::title() const -{ - return PlatformerPlayer.name; -} - -void PlatformerPlayerDemo::initPhysics() -{ - _space = PlatformerPlayer.initFunc(); - ChipmunkTestBed::initPhysics(); -} - -void PlatformerPlayerDemo::update(float delta) -{ - ChipmunkTestBed::updateInit(PlatformerPlayer); -} - -//------------------------------------------------------------------ -// -// QueryDemo -// -//------------------------------------------------------------------ -void QueryDemo::onEnter() -{ - ChipmunkTestBed::onEnter(); - - initPhysics(); -} - -std::string QueryDemo::title() const -{ - return Query.name; -} - -void QueryDemo::initPhysics() -{ - _space = Query.initFunc(); - ChipmunkTestBed::initPhysics(); -} - -void QueryDemo::update(float delta) -{ - ChipmunkTestBed::updateInit(Query); -} - -//------------------------------------------------------------------ -// -// ContactGraphDemo -// -//------------------------------------------------------------------ -void ContactGraphDemo::onEnter() -{ - ChipmunkTestBed::onEnter(); - - initPhysics(); -} - -std::string ContactGraphDemo::title() const -{ - return ContactGraph.name; -} - -void ContactGraphDemo::initPhysics() -{ - _space = ContactGraph.initFunc(); - ChipmunkTestBed::initPhysics(); -} - -void ContactGraphDemo::update(float delta) -{ - ChipmunkTestBed::updateInit(ContactGraph); -} - -//------------------------------------------------------------------ -// -// BuoyancyDemo -// -//------------------------------------------------------------------ -void BuoyancyDemo::onEnter() -{ - ChipmunkTestBed::onEnter(); - - initPhysics(); -} - -std::string BuoyancyDemo::title() const -{ - return Buoyancy.name; -} - -void BuoyancyDemo::initPhysics() -{ - _space = Buoyancy.initFunc(); - ChipmunkTestBed::initPhysics(); -} - -void BuoyancyDemo::update(float delta) -{ - ChipmunkTestBed::updateInit(Buoyancy); -} - -//------------------------------------------------------------------ -// -// SliceDemo -// -//------------------------------------------------------------------ -void SliceDemo::onEnter() -{ - ChipmunkTestBed::onEnter(); - - initPhysics(); -} - -std::string SliceDemo::title() const -{ - return Slice.name; -} - -void SliceDemo::initPhysics() -{ - _space = Slice.initFunc(); - ChipmunkTestBed::initPhysics(); -} - -void SliceDemo::update(float delta) -{ - ChipmunkTestBed::updateInit(Slice); -} - -//------------------------------------------------------------------ -// -// UnicycleDemo -// -//------------------------------------------------------------------ -void UnicycleDemo::onEnter() -{ - ChipmunkTestBed::onEnter(); - - initPhysics(); -} - -std::string UnicycleDemo::title() const -{ - return Unicycle.name; -} - -void UnicycleDemo::initPhysics() -{ - _space = Unicycle.initFunc(); - ChipmunkTestBed::initPhysics(); -} - -void UnicycleDemo::update(float delta) -{ - ChipmunkTestBed::updateInit(Unicycle); -} - -//------------------------------------------------------------------ -// -// ExampleDemo -// -//------------------------------------------------------------------ -void ExampleDemo::onEnter() -{ - ChipmunkTestBed::onEnter(); - - initPhysics(); -} - -std::string ExampleDemo::title() const -{ - return Example.name; -} - -void ExampleDemo::initPhysics() -{ - _space = Example.initFunc(); - ChipmunkTestBed::initPhysics(); -} - -void ExampleDemo::update(float delta) -{ - ChipmunkTestBed::updateInit(Example); -} - -ChipmunkTestBedTests::ChipmunkTestBedTests() -{ - ADD_TEST_CASE(BenchDemo); - ADD_TEST_CASE(LogoSmashDemo); - ADD_TEST_CASE(PlinkDemo); - ADD_TEST_CASE(TumbleDemo); - ADD_TEST_CASE(PyramidToppleDemo); - ADD_TEST_CASE(PyramidStackDemo); - ADD_TEST_CASE(ChainsDemo); - ADD_TEST_CASE(OneWayDemo); - ADD_TEST_CASE(PlanetDemo); - ADD_TEST_CASE(TheoJansenDemo); - ADD_TEST_CASE(TankDemo); - - ADD_TEST_CASE(SpringiesDemo); - - ADD_TEST_CASE(ShatterDemo); - ADD_TEST_CASE(StickyDemo); - ADD_TEST_CASE(CraneDemo); - ADD_TEST_CASE(JointsDemo); - - ADD_TEST_CASE(ConvexDemo); - - ADD_TEST_CASE(PumpDemo); - ADD_TEST_CASE(PlatformerPlayerDemo); - - ADD_TEST_CASE(QueryDemo); - ADD_TEST_CASE(ContactGraphDemo); - ADD_TEST_CASE(BuoyancyDemo); - ADD_TEST_CASE(SliceDemo); - ADD_TEST_CASE(UnicycleDemo); - - ADD_TEST_CASE(ExampleDemo); // the template for your one examples/Demos -} diff --git a/tests/cpp-tests/Source/ChipmunkTestBed/ChipmunkTestBed.h b/tests/cpp-tests/Source/ChipmunkTestBed/ChipmunkTestBed.h deleted file mode 100644 index 11f40b310e5a..000000000000 --- a/tests/cpp-tests/Source/ChipmunkTestBed/ChipmunkTestBed.h +++ /dev/null @@ -1,345 +0,0 @@ -/**************************************************************************** - * Copyright (c) 2021 @aismann; Peter Eismann, Germany; dreifrankensoft - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - ****************************************************************************/ - -// -// cocos2d -// -#ifndef __CHIPMUNKTESTBED_H__ -#define __CHIPMUNKTESTBED_H__ - -#include "axmol.h" -#include "chipmunk/chipmunk.h" -#include "../BaseTest.h" -#include "extensions/axmol-ext.h" - -#include "demo/ChipmunkDemo.h" - -class ChipmunkTestBed : public TestCase -{ -public: - CREATE_FUNC(ChipmunkTestBed); - - ChipmunkTestBed(); - ~ChipmunkTestBed(); - - void onEnter() override; - void createResetButton(); - void reset(ax::Object* sender); - - void update(float dt) override; - virtual void initPhysics(); - - void onMouseDown(ax::Event* event); - void onMouseUp(ax::Event* event); - void onMouseMove(ax::Event* event); - void DrawInfo(); - void updateInit(ChipmunkDemo tt); - - cpSpace* _space; // strong ref - ax::extension::PhysicsDebugNodeChipmunk2D* _debugLayer; // weak ref - int keyPresses; - bool mousePresses = false; - -private: - ax::EventListenerMouse* _mouseListener; - ax::Node* _trackNode; - ax::DrawNode* draw; - ax::Label* drawInfo; -}; - -class LogoSmashDemo : public ChipmunkTestBed -{ -public: - CREATE_FUNC(LogoSmashDemo); - std::string title() const override; - void onEnter() override; - - void initPhysics() override; - virtual void update(float dt) override; -}; - -class PlinkDemo : public ChipmunkTestBed -{ -public: - CREATE_FUNC(PlinkDemo); - virtual std::string title() const override; - virtual void onEnter() override; - void initPhysics() override; - virtual void update(float dt) override; -}; - -class TumbleDemo : public ChipmunkTestBed -{ -public: - CREATE_FUNC(TumbleDemo); - virtual std::string title() const override; - virtual void onEnter() override; - - void initPhysics() override; - virtual void update(float dt) override; -}; - -class PyramidStackDemo : public ChipmunkTestBed -{ -public: - CREATE_FUNC(PyramidStackDemo); - virtual std::string title() const override; - virtual void onEnter() override; - - void initPhysics() override; - virtual void update(float dt) override; -}; - -class PyramidToppleDemo : public ChipmunkTestBed -{ -public: - CREATE_FUNC(PyramidToppleDemo); - virtual std::string title() const override; - virtual void onEnter() override; - - void initPhysics() override; - virtual void update(float dt) override; -}; - -class ChainsDemo : public ChipmunkTestBed -{ -public: - CREATE_FUNC(ChainsDemo); - virtual std::string title() const override; - virtual void onEnter() override; - - void initPhysics() override; - virtual void update(float dt) override; -}; - -class OneWayDemo : public ChipmunkTestBed -{ -public: - CREATE_FUNC(OneWayDemo); - virtual std::string title() const override; - virtual void onEnter() override; - - void initPhysics() override; - virtual void update(float dt) override; -}; - -class PlanetDemo : public ChipmunkTestBed -{ -public: - CREATE_FUNC(PlanetDemo); - virtual std::string title() const override; - virtual void onEnter() override; - - void initPhysics() override; - virtual void update(float dt) override; -}; - -class TheoJansenDemo : public ChipmunkTestBed -{ -public: - CREATE_FUNC(TheoJansenDemo); - virtual std::string title() const override; - virtual void onEnter() override; - - void initPhysics() override; - virtual void update(float dt) override; -}; - -class TankDemo : public ChipmunkTestBed -{ -public: - CREATE_FUNC(TankDemo); - virtual std::string title() const override; - virtual void onEnter() override; - - void initPhysics() override; - virtual void update(float dt) override; -}; - -class BenchDemo : public ChipmunkTestBed -{ -public: - CREATE_FUNC(BenchDemo); - virtual std::string title() const override; - virtual std::string subtitle() const override; - virtual void onEnter() override; - - void initPhysics() override; - virtual void update(float dt) override; -}; - -class SpringiesDemo : public ChipmunkTestBed -{ -public: - CREATE_FUNC(SpringiesDemo); - virtual std::string title() const override; - virtual void onEnter() override; - - void initPhysics() override; - virtual void update(float dt) override; -}; - -class StickyDemo : public ChipmunkTestBed -{ -public: - CREATE_FUNC(StickyDemo); - virtual std::string title() const override; - virtual void onEnter() override; - - void initPhysics() override; - virtual void update(float dt) override; -}; - -class ShatterDemo : public ChipmunkTestBed -{ -public: - CREATE_FUNC(ShatterDemo); - virtual std::string title() const override; - virtual void onEnter() override; - - void initPhysics() override; - virtual void update(float dt) override; -}; - -class CraneDemo : public ChipmunkTestBed -{ -public: - CREATE_FUNC(CraneDemo); - virtual std::string title() const override; - virtual void onEnter() override; - - void initPhysics() override; - virtual void update(float dt) override; -}; - -class JointsDemo : public ChipmunkTestBed -{ -public: - CREATE_FUNC(JointsDemo); - virtual std::string title() const override; - virtual void onEnter() override; - - void initPhysics() override; - virtual void update(float dt) override; -}; - -class ConvexDemo : public ChipmunkTestBed -{ -public: - CREATE_FUNC(ConvexDemo); - virtual std::string title() const override; - virtual void onEnter() override; - - void initPhysics() override; - virtual void update(float dt) override; -}; - -class PumpDemo : public ChipmunkTestBed -{ -public: - CREATE_FUNC(PumpDemo); - virtual std::string title() const override; - virtual void onEnter() override; - - void initPhysics() override; - virtual void update(float dt) override; -}; - -class PlatformerPlayerDemo : public ChipmunkTestBed -{ -public: - CREATE_FUNC(PlatformerPlayerDemo); - virtual std::string title() const override; - virtual void onEnter() override; - - void initPhysics() override; - virtual void update(float dt) override; -}; - -class QueryDemo : public ChipmunkTestBed -{ -public: - CREATE_FUNC(QueryDemo); - virtual std::string title() const override; - virtual void onEnter() override; - - void initPhysics() override; - virtual void update(float dt) override; -}; - -class ContactGraphDemo : public ChipmunkTestBed -{ -public: - CREATE_FUNC(ContactGraphDemo); - virtual std::string title() const override; - virtual void onEnter() override; - - void initPhysics() override; - virtual void update(float dt) override; -}; - -class BuoyancyDemo : public ChipmunkTestBed -{ -public: - CREATE_FUNC(BuoyancyDemo); - virtual std::string title() const override; - virtual void onEnter() override; - - void initPhysics() override; - virtual void update(float dt) override; -}; - -class SliceDemo : public ChipmunkTestBed -{ -public: - CREATE_FUNC(SliceDemo); - virtual std::string title() const override; - virtual void onEnter() override; - - void initPhysics() override; - virtual void update(float dt) override; -}; - -class UnicycleDemo : public ChipmunkTestBed -{ -public: - CREATE_FUNC(UnicycleDemo); - virtual std::string title() const override; - virtual void onEnter() override; - - void initPhysics() override; - virtual void update(float dt) override; -}; - -class ExampleDemo : public ChipmunkTestBed -{ -public: - CREATE_FUNC(ExampleDemo); - virtual std::string title() const override; - virtual void onEnter() override; - - void initPhysics() override; - virtual void update(float dt) override; -}; -DEFINE_TEST_SUITE(ChipmunkTestBedTests); - -#endif /* __CHIPMUNKTESTBED_H__ */ diff --git a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Bench.cpp b/tests/cpp-tests/Source/ChipmunkTestBed/demo/Bench.cpp deleted file mode 100644 index 76dfe5755f47..000000000000 --- a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Bench.cpp +++ /dev/null @@ -1,614 +0,0 @@ -#include "chipmunk/chipmunk.h" -#include "chipmunk/chipmunk_unsafe.h" -#include "ChipmunkDemo.h" - -#define ENABLE_HASTY 0 -#if ENABLE_HASTY -# include "chipmunk/cpHastySpace.h" - -static cpSpace* MakeHastySpace() -{ - cpSpace* space = cpHastySpaceNew(); - cpHastySpaceSetThreads(space, 0); - return space; -} - -# define BENCH_SPACE_NEW MakeHastySpace -# define BENCH_SPACE_FREE cpHastySpaceFree -# define BENCH_SPACE_STEP cpHastySpaceStep -#else -# define BENCH_SPACE_NEW cpSpaceNew -# define BENCH_SPACE_FREE cpSpaceFree -# define BENCH_SPACE_STEP cpSpaceStep -#endif - -const cpFloat bevel = 1.0; - -static cpVect simple_terrain_verts[] = { - {350.00, 425.07}, {336.00, 436.55}, {272.00, 435.39}, {258.00, 427.63}, {225.28, 420.00}, {202.82, 396.00}, - {191.81, 388.00}, {189.00, 381.89}, {173.00, 380.39}, {162.59, 368.00}, {150.47, 319.00}, {128.00, 311.55}, - {119.14, 286.00}, {126.84, 263.00}, {120.56, 227.00}, {141.14, 178.00}, {137.52, 162.00}, {146.51, 142.00}, - {156.23, 136.00}, {158.00, 118.27}, {170.00, 100.77}, {208.43, 84.00}, {224.00, 69.65}, {249.30, 68.00}, - {257.00, 54.77}, {363.00, 45.94}, {374.15, 54.00}, {386.00, 69.60}, {413.00, 70.73}, {456.00, 84.89}, - {468.09, 99.00}, {467.09, 123.00}, {464.92, 135.00}, {469.00, 141.03}, {497.00, 148.67}, {513.85, 180.00}, - {509.56, 223.00}, {523.51, 247.00}, {523.00, 277.00}, {497.79, 311.00}, {478.67, 348.00}, {467.90, 360.00}, - {456.76, 382.00}, {432.95, 389.00}, {417.00, 411.32}, {373.00, 433.19}, {361.00, 430.02}, {350.00, 425.07}, -}; -static int simple_terrain_count = sizeof(simple_terrain_verts) / sizeof(cpVect); - -// cpBody bodies[1000] = {}; -// cpCircleShape circles[1000] = {}; - -static void add_circle(cpSpace* space, int index, cpFloat radius) -{ - cpFloat mass = radius * radius / 25.0f; - cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForCircle(mass, 0.0f, radius, cpvzero))); - // cpBody *body = cpSpaceAddBody(space, cpBodyInit(&bodies[i], mass, cpMomentForCircle(mass, 0.0f, radius, - // cpvzero))); - cpBodySetPosition(body, cpvmult(frand_unit_circle(), 180.0f)); - - cpShape* shape = cpSpaceAddShape(space, cpCircleShapeNew(body, radius, cpvzero)); - // cpShape *shape = cpSpaceAddShape(space, cpCircleShapeInit(&circles[i], body, radius, cpvzero)); - cpShapeSetElasticity(shape, 0.0); - cpShapeSetFriction(shape, 0.9); -} - -static void add_box(cpSpace* space, int index, cpFloat size) -{ - cpFloat mass = size * size / 100.0f; - cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForBox(mass, size, size))); - // cpBody *body = cpSpaceAddBody(space, cpBodyInit(&bodies[i], mass, cpMomentForBox(mass, size, size))); - cpBodySetPosition(body, cpvmult(frand_unit_circle(), 180.0f)); - - cpShape* shape = cpSpaceAddShape(space, cpBoxShapeNew(body, size - bevel * 2, size - bevel * 2, 0.0)); - cpPolyShapeSetRadius(shape, bevel); - cpShapeSetElasticity(shape, 0.0); - cpShapeSetFriction(shape, 0.9); -} - -static void add_hexagon(cpSpace* space, int index, cpFloat radius) -{ - cpVect hexagon[6]; - for (int i = 0; i < 6; i++) - { - cpFloat angle = -CP_PI * 2.0f * i / 6.0f; - hexagon[i] = cpvmult(cpv(cos(angle), sin(angle)), radius - bevel); - } - - cpFloat mass = radius * radius; - cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForPoly(mass, 6, hexagon, cpvzero, 0.0f))); - cpBodySetPosition(body, cpvmult(frand_unit_circle(), 180.0f)); - - cpShape* shape = cpSpaceAddShape(space, cpPolyShapeNew(body, 6, hexagon, cpTransformIdentity, bevel)); - cpShapeSetElasticity(shape, 0.0); - cpShapeSetFriction(shape, 0.9); -} - -static cpSpace* SetupSpace_simpleTerrain() -{ - cpSpace* space = BENCH_SPACE_NEW(); - cpSpaceSetIterations(space, 10); - cpSpaceSetGravity(space, cpv(0, -100)); - cpSpaceSetCollisionSlop(space, 0.5f); - - cpVect offset = cpv(-320, -240); - for (int i = 0; i < (simple_terrain_count - 1); i++) - { - cpVect a = simple_terrain_verts[i], b = simple_terrain_verts[i + 1]; - cpSpaceAddShape(space, - cpSegmentShapeNew(cpSpaceGetStaticBody(space), cpvadd(a, offset), cpvadd(b, offset), 0.0f)); - } - - return space; -} - -// SimpleTerrain constant sized objects -static cpSpace* init_SimpleTerrainCircles_1000(void) -{ - cpSpace* space = SetupSpace_simpleTerrain(); - for (int i = 0; i < 1000; i++) - add_circle(space, i, 5.0f); - - return space; -} - -static cpSpace* init_SimpleTerrainCircles_500(void) -{ - cpSpace* space = SetupSpace_simpleTerrain(); - for (int i = 0; i < 500; i++) - add_circle(space, i, 5.0f); - - return space; -} - -static cpSpace* init_SimpleTerrainCircles_100(void) -{ - cpSpace* space = SetupSpace_simpleTerrain(); - for (int i = 0; i < 100; i++) - add_circle(space, i, 5.0f); - - return space; -} - -static cpSpace* init_SimpleTerrainBoxes_1000(void) -{ - cpSpace* space = SetupSpace_simpleTerrain(); - for (int i = 0; i < 1000; i++) - add_box(space, i, 10.0f); - - return space; -} - -static cpSpace* init_SimpleTerrainBoxes_500(void) -{ - cpSpace* space = SetupSpace_simpleTerrain(); - for (int i = 0; i < 500; i++) - add_box(space, i, 10.0f); - - return space; -} - -static cpSpace* init_SimpleTerrainBoxes_100(void) -{ - cpSpace* space = SetupSpace_simpleTerrain(); - for (int i = 0; i < 100; i++) - add_box(space, i, 10.0f); - - return space; -} - -static cpSpace* init_SimpleTerrainHexagons_1000(void) -{ - cpSpace* space = SetupSpace_simpleTerrain(); - for (int i = 0; i < 1000; i++) - add_hexagon(space, i, 5.0f); - - return space; -} - -static cpSpace* init_SimpleTerrainHexagons_500(void) -{ - cpSpace* space = SetupSpace_simpleTerrain(); - for (int i = 0; i < 500; i++) - add_hexagon(space, i, 5.0f); - - return space; -} - -static cpSpace* init_SimpleTerrainHexagons_100(void) -{ - cpSpace* space = SetupSpace_simpleTerrain(); - for (int i = 0; i < 100; i++) - add_hexagon(space, i, 5.0f); - - return space; -} - -// SimpleTerrain variable sized objects -static cpFloat rand_size() -{ - return cpfpow(1.5, cpflerp(-1.5, 3.5, frand())); -} - -static cpSpace* init_SimpleTerrainVCircles_200(void) -{ - cpSpace* space = SetupSpace_simpleTerrain(); - for (int i = 0; i < 200; i++) - add_circle(space, i, 5.0f * rand_size()); - - return space; -} - -static cpSpace* init_SimpleTerrainVBoxes_200(void) -{ - cpSpace* space = SetupSpace_simpleTerrain(); - for (int i = 0; i < 200; i++) - add_box(space, i, 8.0f * rand_size()); - - return space; -} - -static cpSpace* init_SimpleTerrainVHexagons_200(void) -{ - cpSpace* space = SetupSpace_simpleTerrain(); - for (int i = 0; i < 200; i++) - add_hexagon(space, i, 5.0f * rand_size()); - - return space; -} - -// ComplexTerrain -static cpVect complex_terrain_verts[] = { - {46.78, 479.00}, {35.00, 475.63}, {27.52, 469.00}, {23.52, 455.00}, {23.78, 441.00}, {28.41, 428.00}, - {49.61, 394.00}, {59.00, 381.56}, {80.00, 366.03}, {81.46, 358.00}, {86.31, 350.00}, {77.74, 320.00}, - {70.26, 278.00}, {67.51, 270.00}, {58.86, 260.00}, {57.19, 247.00}, {38.00, 235.60}, {25.76, 221.00}, - {24.58, 209.00}, {27.63, 202.00}, {31.28, 198.00}, {40.00, 193.72}, {48.00, 193.73}, {55.00, 196.70}, - {62.10, 204.00}, {71.00, 209.04}, {79.00, 206.55}, {88.00, 206.81}, {95.88, 211.00}, {103.00, 220.49}, - {131.00, 220.51}, {137.00, 222.66}, {143.08, 228.00}, {146.22, 234.00}, {147.08, 241.00}, {145.45, 248.00}, - {142.31, 253.00}, {132.00, 259.30}, {115.00, 259.70}, {109.28, 270.00}, {112.91, 296.00}, {119.69, 324.00}, - {129.00, 336.26}, {141.00, 337.59}, {153.00, 331.57}, {175.00, 325.74}, {188.00, 325.19}, {235.00, 317.46}, - {250.00, 317.19}, {255.00, 309.12}, {262.62, 302.00}, {262.21, 295.00}, {248.00, 273.59}, {229.00, 257.93}, - {221.00, 255.48}, {215.00, 251.59}, {210.79, 246.00}, {207.47, 234.00}, {203.25, 227.00}, {179.00, 205.90}, - {148.00, 189.54}, {136.00, 181.45}, {120.00, 180.31}, {110.00, 181.65}, {95.00, 179.31}, {63.00, 166.96}, - {50.00, 164.23}, {31.00, 154.49}, {19.76, 145.00}, {15.96, 136.00}, {16.65, 127.00}, {20.57, 120.00}, - {28.00, 114.63}, {40.00, 113.67}, {65.00, 127.22}, {73.00, 128.69}, {81.96, 120.00}, {77.58, 103.00}, - {78.18, 92.00}, {59.11, 77.00}, {52.00, 67.29}, {31.29, 55.00}, {25.67, 47.00}, {24.65, 37.00}, - {27.82, 29.00}, {35.00, 22.55}, {44.00, 20.35}, {49.00, 20.81}, {61.00, 25.69}, {79.00, 37.81}, - {88.00, 49.64}, {97.00, 56.65}, {109.00, 49.61}, {143.00, 38.96}, {197.00, 37.27}, {215.00, 35.30}, - {222.00, 36.65}, {228.42, 41.00}, {233.30, 49.00}, {234.14, 57.00}, {231.00, 65.80}, {224.00, 72.38}, - {218.00, 74.50}, {197.00, 76.62}, {145.00, 78.81}, {123.00, 87.41}, {117.59, 98.00}, {117.79, 104.00}, - {119.00, 106.23}, {138.73, 120.00}, {148.00, 129.50}, {158.50, 149.00}, {203.93, 175.00}, {229.00, 196.60}, - {238.16, 208.00}, {245.20, 221.00}, {275.45, 245.00}, {289.00, 263.24}, {303.60, 287.00}, {312.00, 291.57}, - {339.25, 266.00}, {366.33, 226.00}, {363.43, 216.00}, {364.13, 206.00}, {353.00, 196.72}, {324.00, 181.05}, - {307.00, 169.63}, {274.93, 156.00}, {256.00, 152.48}, {228.00, 145.13}, {221.09, 142.00}, {214.87, 135.00}, - {212.67, 127.00}, {213.81, 119.00}, {219.32, 111.00}, {228.00, 106.52}, {236.00, 106.39}, {290.00, 119.40}, - {299.33, 114.00}, {300.52, 109.00}, {300.30, 53.00}, {301.46, 47.00}, {305.00, 41.12}, {311.00, 36.37}, - {317.00, 34.43}, {325.00, 34.81}, {334.90, 41.00}, {339.45, 50.00}, {339.82, 132.00}, {346.09, 139.00}, - {350.00, 150.26}, {380.00, 167.38}, {393.00, 166.48}, {407.00, 155.54}, {430.00, 147.30}, {437.78, 135.00}, - {433.13, 122.00}, {410.23, 78.00}, {401.59, 69.00}, {393.48, 56.00}, {392.80, 44.00}, {395.50, 38.00}, - {401.00, 32.49}, {409.00, 29.41}, {420.00, 30.84}, {426.92, 36.00}, {432.32, 44.00}, {439.49, 51.00}, - {470.13, 108.00}, {475.71, 124.00}, {483.00, 130.11}, {488.00, 139.43}, {529.00, 139.40}, {536.00, 132.52}, - {543.73, 129.00}, {540.47, 115.00}, {541.11, 100.00}, {552.18, 68.00}, {553.78, 47.00}, {559.00, 39.76}, - {567.00, 35.52}, {577.00, 35.45}, {585.00, 39.58}, {591.38, 50.00}, {591.67, 66.00}, {590.31, 79.00}, - {579.76, 109.00}, {582.25, 119.00}, {583.66, 136.00}, {586.45, 143.00}, {586.44, 151.00}, {580.42, 168.00}, - {577.15, 173.00}, {572.00, 177.13}, {564.00, 179.49}, {478.00, 178.81}, {443.00, 184.76}, {427.10, 190.00}, - {424.00, 192.11}, {415.94, 209.00}, {408.82, 228.00}, {405.82, 241.00}, {411.00, 250.82}, {415.00, 251.50}, - {428.00, 248.89}, {469.00, 246.29}, {505.00, 246.49}, {533.00, 243.60}, {541.87, 248.00}, {547.55, 256.00}, - {548.48, 267.00}, {544.00, 276.00}, {534.00, 282.24}, {513.00, 285.46}, {468.00, 285.76}, {402.00, 291.70}, - {392.00, 290.29}, {377.00, 294.46}, {367.00, 294.43}, {356.44, 304.00}, {354.22, 311.00}, {362.00, 321.36}, - {390.00, 322.44}, {433.00, 330.16}, {467.00, 332.76}, {508.00, 347.64}, {522.00, 357.67}, {528.00, 354.46}, - {536.00, 352.96}, {546.06, 336.00}, {553.47, 306.00}, {564.19, 282.00}, {567.84, 268.00}, {578.72, 246.00}, - {585.00, 240.97}, {592.00, 238.91}, {600.00, 239.72}, {606.00, 242.82}, {612.36, 251.00}, {613.35, 263.00}, - {588.75, 324.00}, {583.25, 350.00}, {572.12, 370.00}, {575.45, 378.00}, {575.20, 388.00}, {589.00, 393.81}, - {599.20, 404.00}, {607.14, 416.00}, {609.96, 430.00}, {615.45, 441.00}, {613.44, 462.00}, {610.48, 469.00}, - {603.00, 475.63}, {590.96, 479.00}, -}; -static int complex_terrain_count = sizeof(complex_terrain_verts) / sizeof(cpVect); - -static cpSpace* init_ComplexTerrainCircles_1000(void) -{ - cpSpace* space = BENCH_SPACE_NEW(); - cpSpaceSetIterations(space, 10); - cpSpaceSetGravity(space, cpv(0, -100)); - cpSpaceSetCollisionSlop(space, 0.5f); - - cpVect offset = cpv(-320, -240); - for (int i = 0; i < (complex_terrain_count - 1); i++) - { - cpVect a = complex_terrain_verts[i], b = complex_terrain_verts[i + 1]; - cpSpaceAddShape(space, - cpSegmentShapeNew(cpSpaceGetStaticBody(space), cpvadd(a, offset), cpvadd(b, offset), 0.0f)); - } - - for (int i = 0; i < 1000; i++) - { - cpFloat radius = 5.0f; - cpFloat mass = radius * radius; - cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForCircle(mass, 0.0f, radius, cpvzero))); - cpBodySetPosition(body, cpvadd(cpvmult(frand_unit_circle(), 180.0f), cpv(0.0f, 300.0f))); - - cpShape* shape = cpSpaceAddShape(space, cpCircleShapeNew(body, radius, cpvzero)); - cpShapeSetElasticity(shape, 0.0); - cpShapeSetFriction(shape, 0.0); - } - - return space; -} - -static cpSpace* init_ComplexTerrainHexagons_1000(void) -{ - cpSpace* space = BENCH_SPACE_NEW(); - cpSpaceSetIterations(space, 10); - cpSpaceSetGravity(space, cpv(0, -100)); - cpSpaceSetCollisionSlop(space, 0.5f); - - cpVect offset = cpv(-320, -240); - for (int i = 0; i < (complex_terrain_count - 1); i++) - { - cpVect a = complex_terrain_verts[i], b = complex_terrain_verts[i + 1]; - cpSpaceAddShape(space, - cpSegmentShapeNew(cpSpaceGetStaticBody(space), cpvadd(a, offset), cpvadd(b, offset), 0.0f)); - } - - cpFloat radius = 5.0f; - cpVect hexagon[6]; - for (int i = 0; i < 6; i++) - { - cpFloat angle = -CP_PI * 2.0f * i / 6.0f; - hexagon[i] = cpvmult(cpv(cos(angle), sin(angle)), radius - bevel); - } - - for (int i = 0; i < 1000; i++) - { - cpFloat mass = radius * radius; - cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForPoly(mass, 6, hexagon, cpvzero, 0.0f))); - cpBodySetPosition(body, cpvadd(cpvmult(frand_unit_circle(), 180.0f), cpv(0.0f, 300.0f))); - - cpShape* shape = cpSpaceAddShape(space, cpPolyShapeNew(body, 6, hexagon, cpTransformIdentity, bevel)); - cpShapeSetElasticity(shape, 0.0); - cpShapeSetFriction(shape, 0.0); - } - - return space; -} - -// BouncyTerrain -static cpVect bouncy_terrain_verts[] = { - {537.18, 23.00}, {520.50, 36.00}, {501.53, 63.00}, {496.14, 76.00}, {498.86, 86.00}, {504.00, 90.51}, - {508.00, 91.36}, {508.77, 84.00}, {513.00, 77.73}, {519.00, 74.48}, {530.00, 74.67}, {545.00, 54.65}, - {554.00, 48.77}, {562.00, 46.39}, {568.00, 45.94}, {568.61, 47.00}, {567.94, 55.00}, {571.27, 64.00}, - {572.92, 80.00}, {572.00, 81.39}, {563.00, 79.93}, {556.00, 82.69}, {551.49, 88.00}, {549.00, 95.76}, - {538.00, 93.40}, {530.00, 102.38}, {523.00, 104.00}, {517.00, 103.02}, {516.22, 109.00}, {518.96, 116.00}, - {526.00, 121.15}, {534.00, 116.48}, {543.00, 116.77}, {549.28, 121.00}, {554.00, 130.17}, {564.00, 125.67}, - {575.60, 129.00}, {573.31, 121.00}, {567.77, 111.00}, {575.00, 106.47}, {578.51, 102.00}, {580.25, 95.00}, - {577.98, 87.00}, {582.00, 85.71}, {597.00, 89.46}, {604.80, 95.00}, {609.28, 104.00}, {610.55, 116.00}, - {609.30, 125.00}, {600.80, 142.00}, {597.31, 155.00}, {584.00, 167.23}, {577.86, 175.00}, {583.52, 184.00}, - {582.64, 195.00}, {591.00, 196.56}, {597.81, 201.00}, {607.45, 219.00}, {607.51, 246.00}, {600.00, 275.46}, - {588.00, 267.81}, {579.00, 264.91}, {557.00, 264.41}, {552.98, 259.00}, {548.00, 246.18}, {558.00, 247.12}, - {565.98, 244.00}, {571.10, 237.00}, {571.61, 229.00}, {568.25, 222.00}, {562.00, 217.67}, {544.00, 213.93}, - {536.73, 214.00}, {535.60, 204.00}, {539.69, 181.00}, {542.84, 171.00}, {550.43, 161.00}, {540.00, 156.27}, - {536.62, 152.00}, {534.70, 146.00}, {527.00, 141.88}, {518.59, 152.00}, {514.51, 160.00}, {510.33, 175.00}, - {519.38, 183.00}, {520.52, 194.00}, {516.00, 201.27}, {505.25, 206.00}, {507.57, 223.00}, {519.90, 260.00}, - {529.00, 260.48}, {534.00, 262.94}, {538.38, 268.00}, {540.00, 275.00}, {537.06, 284.00}, {530.00, 289.23}, - {520.00, 289.23}, {513.00, 284.18}, {509.71, 286.00}, {501.69, 298.00}, {501.56, 305.00}, {504.30, 311.00}, - {512.00, 316.43}, {521.00, 316.42}, {525.67, 314.00}, {535.00, 304.98}, {562.00, 294.80}, {573.00, 294.81}, - {587.52, 304.00}, {600.89, 310.00}, {596.96, 322.00}, {603.28, 327.00}, {606.52, 333.00}, {605.38, 344.00}, - {597.65, 352.00}, {606.36, 375.00}, {607.16, 384.00}, {603.40, 393.00}, {597.00, 398.14}, {577.00, 386.15}, - {564.35, 373.00}, {565.21, 364.00}, {562.81, 350.00}, {553.00, 346.06}, {547.48, 338.00}, {547.48, 330.00}, - {550.00, 323.30}, {544.00, 321.53}, {537.00, 322.70}, {532.00, 326.23}, {528.89, 331.00}, {527.83, 338.00}, - {533.02, 356.00}, {542.00, 360.73}, {546.68, 369.00}, {545.38, 379.00}, {537.58, 386.00}, {537.63, 388.00}, - {555.00, 407.47}, {563.00, 413.52}, {572.57, 418.00}, {582.72, 426.00}, {578.00, 431.12}, {563.21, 440.00}, - {558.00, 449.27}, {549.00, 452.94}, {541.00, 451.38}, {536.73, 448.00}, {533.00, 441.87}, {520.00, 437.96}, - {514.00, 429.69}, {490.00, 415.15}, {472.89, 399.00}, {472.03, 398.00}, {474.00, 396.71}, {486.00, 393.61}, - {492.00, 385.85}, {492.00, 376.15}, {489.04, 371.00}, {485.00, 368.11}, {480.00, 376.27}, {472.00, 379.82}, - {463.00, 378.38}, {455.08, 372.00}, {446.00, 377.69}, {439.00, 385.24}, {436.61, 391.00}, {437.52, 404.00}, - {440.00, 409.53}, {463.53, 433.00}, {473.80, 441.00}, {455.00, 440.30}, {443.00, 436.18}, {436.00, 431.98}, - {412.00, 440.92}, {397.00, 442.46}, {393.59, 431.00}, {393.71, 412.00}, {400.00, 395.10}, {407.32, 387.00}, - {408.54, 380.00}, {407.42, 375.00}, {403.97, 370.00}, {399.00, 366.74}, {393.00, 365.68}, {391.23, 374.00}, - {387.00, 380.27}, {381.00, 383.52}, {371.56, 384.00}, {364.98, 401.00}, {362.96, 412.00}, {363.63, 435.00}, - {345.00, 433.55}, {344.52, 442.00}, {342.06, 447.00}, {337.00, 451.38}, {330.00, 453.00}, {325.00, 452.23}, - {318.00, 448.17}, {298.00, 453.70}, {284.00, 451.49}, {278.62, 449.00}, {291.47, 408.00}, {291.77, 398.00}, - {301.00, 393.83}, {305.00, 393.84}, {305.60, 403.00}, {310.00, 409.47}, {318.00, 413.07}, {325.00, 412.40}, - {332.31, 407.00}, {335.07, 400.00}, {334.40, 393.00}, {329.00, 385.69}, {319.00, 382.79}, {301.00, 389.23}, - {289.00, 389.97}, {265.00, 389.82}, {251.00, 385.85}, {245.00, 389.23}, {239.00, 389.94}, {233.00, 388.38}, - {226.00, 382.04}, {206.00, 374.75}, {206.00, 394.00}, {204.27, 402.00}, {197.00, 401.79}, {191.00, 403.49}, - {186.53, 407.00}, {183.60, 412.00}, {183.60, 422.00}, {189.00, 429.31}, {196.00, 432.07}, {203.00, 431.40}, - {209.47, 427.00}, {213.00, 419.72}, {220.00, 420.21}, {227.00, 418.32}, {242.00, 408.41}, {258.98, 409.00}, - {250.00, 435.43}, {239.00, 438.78}, {223.00, 448.19}, {209.00, 449.70}, {205.28, 456.00}, {199.00, 460.23}, - {190.00, 460.52}, {182.73, 456.00}, {178.00, 446.27}, {160.00, 441.42}, {148.35, 435.00}, {149.79, 418.00}, - {157.72, 401.00}, {161.00, 396.53}, {177.00, 385.00}, {180.14, 380.00}, {181.11, 374.00}, {180.00, 370.52}, - {170.00, 371.68}, {162.72, 368.00}, {158.48, 361.00}, {159.56, 349.00}, {154.00, 342.53}, {146.00, 339.85}, - {136.09, 343.00}, {130.64, 351.00}, {131.74, 362.00}, {140.61, 374.00}, {130.68, 387.00}, {120.75, 409.00}, - {118.09, 421.00}, {117.92, 434.00}, {100.00, 432.40}, {87.00, 427.48}, {81.59, 423.00}, {73.64, 409.00}, - {72.57, 398.00}, {74.62, 386.00}, {78.80, 378.00}, {88.00, 373.43}, {92.49, 367.00}, {93.32, 360.00}, - {91.30, 353.00}, {103.00, 342.67}, {109.00, 343.10}, {116.00, 340.44}, {127.33, 330.00}, {143.00, 327.24}, - {154.30, 322.00}, {145.00, 318.06}, {139.77, 311.00}, {139.48, 302.00}, {144.95, 293.00}, {143.00, 291.56}, - {134.00, 298.21}, {118.00, 300.75}, {109.40, 305.00}, {94.67, 319.00}, {88.00, 318.93}, {81.00, 321.69}, - {67.24, 333.00}, {56.68, 345.00}, {53.00, 351.40}, {47.34, 333.00}, {50.71, 314.00}, {56.57, 302.00}, - {68.00, 287.96}, {91.00, 287.24}, {110.00, 282.36}, {133.80, 271.00}, {147.34, 256.00}, {156.47, 251.00}, - {157.26, 250.00}, {154.18, 242.00}, {154.48, 236.00}, {158.72, 229.00}, {166.71, 224.00}, {170.15, 206.00}, - {170.19, 196.00}, {167.24, 188.00}, {160.00, 182.67}, {150.00, 182.66}, {143.60, 187.00}, {139.96, 195.00}, - {139.50, 207.00}, {136.45, 221.00}, {136.52, 232.00}, {133.28, 238.00}, {129.00, 241.38}, {119.00, 243.07}, - {115.00, 246.55}, {101.00, 253.16}, {86.00, 257.32}, {63.00, 259.24}, {57.00, 257.31}, {50.54, 252.00}, - {47.59, 247.00}, {46.30, 240.00}, {47.58, 226.00}, {50.00, 220.57}, {58.00, 226.41}, {69.00, 229.17}, - {79.00, 229.08}, {94.50, 225.00}, {100.21, 231.00}, {107.00, 233.47}, {107.48, 224.00}, {109.94, 219.00}, - {115.00, 214.62}, {122.57, 212.00}, {116.00, 201.49}, {104.00, 194.57}, {90.00, 194.04}, {79.00, 198.21}, - {73.00, 198.87}, {62.68, 191.00}, {62.58, 184.00}, {64.42, 179.00}, {75.00, 167.70}, {80.39, 157.00}, - {68.79, 140.00}, {61.67, 126.00}, {61.47, 117.00}, {64.43, 109.00}, {63.10, 96.00}, {56.48, 82.00}, - {48.00, 73.88}, {43.81, 66.00}, {43.81, 56.00}, {50.11, 46.00}, {59.00, 41.55}, {71.00, 42.64}, - {78.00, 36.77}, {83.00, 34.75}, {99.00, 34.32}, {117.00, 38.92}, {133.00, 55.15}, {142.00, 50.70}, - {149.74, 51.00}, {143.55, 68.00}, {153.28, 74.00}, {156.23, 79.00}, {157.00, 84.00}, {156.23, 89.00}, - {153.28, 94.00}, {144.58, 99.00}, {151.52, 112.00}, {151.51, 124.00}, {150.00, 126.36}, {133.00, 130.25}, - {126.71, 125.00}, {122.00, 117.25}, {114.00, 116.23}, {107.73, 112.00}, {104.48, 106.00}, {104.32, 99.00}, - {106.94, 93.00}, {111.24, 89.00}, {111.60, 85.00}, {107.24, 73.00}, {102.00, 67.57}, {99.79, 67.00}, - {99.23, 76.00}, {95.00, 82.27}, {89.00, 85.52}, {79.84, 86.00}, {86.73, 114.00}, {98.00, 136.73}, - {99.00, 137.61}, {109.00, 135.06}, {117.00, 137.94}, {122.52, 146.00}, {122.94, 151.00}, {121.00, 158.58}, - {134.00, 160.97}, {153.00, 157.45}, {171.30, 150.00}, {169.06, 142.00}, {169.77, 136.00}, {174.00, 129.73}, - {181.46, 126.00}, {182.22, 120.00}, {182.20, 111.00}, {180.06, 101.00}, {171.28, 85.00}, {171.75, 80.00}, - {182.30, 53.00}, {189.47, 50.00}, {190.62, 38.00}, {194.00, 33.73}, {199.00, 30.77}, {208.00, 30.48}, - {216.00, 34.94}, {224.00, 31.47}, {240.00, 30.37}, {247.00, 32.51}, {249.77, 35.00}, {234.75, 53.00}, - {213.81, 93.00}, {212.08, 99.00}, {213.00, 101.77}, {220.00, 96.77}, {229.00, 96.48}, {236.28, 101.00}, - {240.00, 107.96}, {245.08, 101.00}, {263.00, 65.32}, {277.47, 48.00}, {284.00, 47.03}, {286.94, 41.00}, - {292.00, 36.62}, {298.00, 35.06}, {304.00, 35.77}, {314.00, 43.81}, {342.00, 32.56}, {359.00, 31.32}, - {365.00, 32.57}, {371.00, 36.38}, {379.53, 48.00}, {379.70, 51.00}, {356.00, 52.19}, {347.00, 54.74}, - {344.38, 66.00}, {341.00, 70.27}, {335.00, 73.52}, {324.00, 72.38}, {317.00, 65.75}, {313.00, 67.79}, - {307.57, 76.00}, {315.00, 78.62}, {319.28, 82.00}, {322.23, 87.00}, {323.00, 94.41}, {334.00, 92.49}, - {347.00, 87.47}, {349.62, 80.00}, {353.00, 75.73}, {359.00, 72.48}, {366.00, 72.32}, {372.00, 74.94}, - {377.00, 81.34}, {382.00, 83.41}, {392.00, 83.40}, {399.00, 79.15}, {404.00, 85.74}, {411.00, 85.06}, - {417.00, 86.62}, {423.38, 93.00}, {425.05, 104.00}, {438.00, 110.35}, {450.00, 112.17}, {452.62, 103.00}, - {456.00, 98.73}, {462.00, 95.48}, {472.00, 95.79}, {471.28, 92.00}, {464.00, 84.62}, {445.00, 80.39}, - {436.00, 75.33}, {428.00, 68.46}, {419.00, 68.52}, {413.00, 65.27}, {408.48, 58.00}, {409.87, 46.00}, - {404.42, 39.00}, {408.00, 33.88}, {415.00, 29.31}, {429.00, 26.45}, {455.00, 28.77}, {470.00, 33.81}, - {482.00, 42.16}, {494.00, 46.85}, {499.65, 36.00}, {513.00, 25.95}, {529.00, 22.42}, {537.18, 23.00}, -}; -static int bouncy_terrain_count = sizeof(bouncy_terrain_verts) / sizeof(cpVect); - -static cpSpace* init_BouncyTerrainCircles_500(void) -{ - cpSpace* space = BENCH_SPACE_NEW(); - cpSpaceSetIterations(space, 10); - - cpVect offset = cpv(-320, -240); - for (int i = 0; i < (bouncy_terrain_count - 1); i++) - { - cpVect a = bouncy_terrain_verts[i], b = bouncy_terrain_verts[i + 1]; - cpShape* shape = cpSpaceAddShape( - space, cpSegmentShapeNew(cpSpaceGetStaticBody(space), cpvadd(a, offset), cpvadd(b, offset), 0.0f)); - cpShapeSetElasticity(shape, 1.0); - } - - for (int i = 0; i < 500; i++) - { - cpFloat radius = 5.0f; - cpFloat mass = radius * radius; - cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForCircle(mass, 0.0f, radius, cpvzero))); - cpBodySetPosition(body, cpvadd(cpvmult(frand_unit_circle(), 130.0f), cpvzero)); - cpBodySetVelocity(body, cpvmult(frand_unit_circle(), 50.0f)); - - cpShape* shape = cpSpaceAddShape(space, cpCircleShapeNew(body, radius, cpvzero)); - cpShapeSetElasticity(shape, 1.0); - } - - return space; -} - -static cpSpace* init_BouncyTerrainHexagons_500(void) -{ - cpSpace* space = BENCH_SPACE_NEW(); - cpSpaceSetIterations(space, 10); - - cpVect offset = cpv(-320, -240); - for (int i = 0; i < (bouncy_terrain_count - 1); i++) - { - cpVect a = bouncy_terrain_verts[i], b = bouncy_terrain_verts[i + 1]; - cpShape* shape = cpSpaceAddShape( - space, cpSegmentShapeNew(cpSpaceGetStaticBody(space), cpvadd(a, offset), cpvadd(b, offset), 0.0f)); - cpShapeSetElasticity(shape, 1.0); - } - - cpFloat radius = 5.0f; - cpVect hexagon[6]; - for (int i = 0; i < 6; i++) - { - cpFloat angle = -CP_PI * 2.0f * i / 6.0f; - hexagon[i] = cpvmult(cpv(cos(angle), sin(angle)), radius - bevel); - } - - for (int i = 0; i < 500; i++) - { - cpFloat mass = radius * radius; - cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForPoly(mass, 6, hexagon, cpvzero, 0.0f))); - cpBodySetPosition(body, cpvadd(cpvmult(frand_unit_circle(), 130.0f), cpvzero)); - cpBodySetVelocity(body, cpvmult(frand_unit_circle(), 50.0f)); - - cpShape* shape = cpSpaceAddShape(space, cpPolyShapeNew(body, 6, hexagon, cpTransformIdentity, bevel)); - cpShapeSetElasticity(shape, 1.0); - } - - return space; -} - -// No collisions - -static cpFloat pentagon_mass = 0.0f; -static cpFloat pentagon_moment = 0.0f; - -static cpBool NoCollide_begin(cpArbiter* arb, cpSpace* space, void* data) -{ - - // AXLOGD("NoCollide_begin"); - - return cpTrue; -} - -static cpSpace* init_NoCollide(void) -{ - cpSpace* space = BENCH_SPACE_NEW(); - cpSpaceSetIterations(space, 10); - - cpCollisionHandler* handler = cpSpaceAddCollisionHandler(space, 2, 2); - handler->beginFunc = NoCollide_begin; - - float radius = 4.5f; - - cpShapeSetElasticity( - cpSpaceAddShape(space, cpSegmentShapeNew(cpSpaceGetStaticBody(space), cpv(-330 - radius, -250 - radius), - cpv(330 + radius, -250 - radius), 0.0f)), - 1.0f); - cpShapeSetElasticity( - cpSpaceAddShape(space, cpSegmentShapeNew(cpSpaceGetStaticBody(space), cpv(330 + radius, 250 + radius), - cpv(330 + radius, -250 - radius), 0.0f)), - 1.0f); - cpShapeSetElasticity( - cpSpaceAddShape(space, cpSegmentShapeNew(cpSpaceGetStaticBody(space), cpv(330 + radius, 250 + radius), - cpv(-330 - radius, 250 + radius), 0.0f)), - 1.0f); - cpShapeSetElasticity( - cpSpaceAddShape(space, cpSegmentShapeNew(cpSpaceGetStaticBody(space), cpv(-330 - radius, -250 - radius), - cpv(-330 - radius, 250 + radius), 0.0f)), - 1.0f); - - for (int x = -320; x <= 320; x += 20) - { - for (int y = -240; y <= 240; y += 20) - { - cpShape* shape = cpSpaceAddShape(space, cpCircleShapeNew(cpSpaceGetStaticBody(space), radius, cpv(x, y))); - cpShapeSetElasticity(shape, 1.0); - cpShapeSetCollisionType(shape, 2); - } - } - - for (int y = 10 - 240; y <= 240; y += 40) - { - cpFloat mass = 7.0f; - cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForCircle(mass, 0.0f, radius, cpvzero))); - cpBodySetPosition(body, cpv(-320.0f, y)); - cpBodySetVelocity(body, cpv(100.0f, 0.0f)); - - cpShape* shape = cpSpaceAddShape(space, cpCircleShapeNew(body, radius, cpvzero)); - cpShapeSetElasticity(shape, 1.0); - cpShapeSetCollisionType(shape, 2); - } - - for (int x = 30 - 320; x <= 320; x += 40) - { - cpFloat mass = 7.0f; - cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForCircle(mass, 0.0f, radius, cpvzero))); - cpBodySetPosition(body, cpv(x, -240.0f)); - cpBodySetVelocity(body, cpv(0.0f, 100.0f)); - - cpShape* shape = cpSpaceAddShape(space, cpCircleShapeNew(body, radius, cpvzero)); - cpShapeSetElasticity(shape, 1.0); - cpShapeSetCollisionType(shape, 2); - } - - return space; -} - -// TODO ideas: -// addition/removal -// Memory usage? (too small to matter?) -// http://forums.tigsource.com/index.php?topic=18077.msg518578#msg518578 - -// Build benchmark list -static void update(cpSpace* space, double dt) -{ - BENCH_SPACE_STEP(space, dt); -} - -static void destroy(cpSpace* space) -{ - ChipmunkDemoFreeSpaceChildren(space); - BENCH_SPACE_FREE(space); -} - -// Make a second demo declaration for this demo to use in the regular demo set. -ChipmunkDemo BouncyHexagons = { - "Bouncy Hexagons", 1.0 / 60.0, init_BouncyTerrainHexagons_500, update, ChipmunkDemoDefaultDrawImpl, destroy, -}; - -#define BENCH(n) \ - { \ -# n, 1.0 / 60.0, init_##n, update, ChipmunkDemoDefaultDrawImpl, destroy \ - } -ChipmunkDemo bench_list[] = { - BENCH(SimpleTerrainCircles_1000), - BENCH(SimpleTerrainCircles_500), - BENCH(SimpleTerrainCircles_100), - BENCH(SimpleTerrainBoxes_1000), - BENCH(SimpleTerrainBoxes_500), - BENCH(SimpleTerrainBoxes_100), - BENCH(SimpleTerrainHexagons_1000), - BENCH(SimpleTerrainHexagons_500), - BENCH(SimpleTerrainHexagons_100), - BENCH(SimpleTerrainVCircles_200), - BENCH(SimpleTerrainVBoxes_200), - BENCH(SimpleTerrainVHexagons_200), - BENCH(ComplexTerrainCircles_1000), - BENCH(ComplexTerrainHexagons_1000), - BENCH(BouncyTerrainCircles_500), - BENCH(BouncyTerrainHexagons_500), - BENCH(NoCollide), -}; - -int bench_count = sizeof(bench_list) / sizeof(ChipmunkDemo); diff --git a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Buoyancy.cpp b/tests/cpp-tests/Source/ChipmunkTestBed/demo/Buoyancy.cpp deleted file mode 100644 index 05224adbf800..000000000000 --- a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Buoyancy.cpp +++ /dev/null @@ -1,220 +0,0 @@ -/* Copyright (c) 2007 Scott Lembcke - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk.h" - -#include "ChipmunkDemo.h" - -static void update(cpSpace* space, double dt) -{ - cpSpaceStep(space, dt); -} - -#define FLUID_DENSITY 0.00014 -#define FLUID_DRAG 2.0 - -char messageBuffer[1024]; - -// Modified from chipmunk_private.h -static inline cpFloat k_scalar_body(cpBody* body, cpVect point, cpVect n) -{ - cpFloat rcn = cpvcross(cpvsub(point, cpBodyGetPosition(body)), n); - return 1.0f / cpBodyGetMass(body) + rcn * rcn / cpBodyGetMoment(body); -} - -static cpBool waterPreSolve(cpArbiter* arb, cpSpace* space, void* ptr) -{ - CP_ARBITER_GET_SHAPES(arb, water, poly); - cpBody* body = cpShapeGetBody(poly); - - // Get the top of the water sensor bounding box to use as the water level. - cpFloat level = cpShapeGetBB(water).t; - - // Clip the polygon against the water level - int count = cpPolyShapeGetCount(poly); - int clippedCount = 0; -#ifdef _MSC_VER - // MSVC is pretty much the only compiler in existence that doesn't support variable sized arrays. - cpVect clipped[10]; -#else - cpVect clipped[count + 1]; -#endif - - for (int i = 0, j = count - 1; i < count; j = i, i++) - { - cpVect a = cpBodyLocalToWorld(body, cpPolyShapeGetVert(poly, j)); - cpVect b = cpBodyLocalToWorld(body, cpPolyShapeGetVert(poly, i)); - - if (a.y < level) - { - clipped[clippedCount] = a; - clippedCount++; - } - - cpFloat a_level = a.y - level; - cpFloat b_level = b.y - level; - - if (a_level * b_level < 0.0f) - { - cpFloat t = cpfabs(a_level) / (cpfabs(a_level) + cpfabs(b_level)); - - clipped[clippedCount] = cpvlerp(a, b, t); - clippedCount++; - } - } - - // Calculate buoyancy from the clipped polygon area - cpFloat clippedArea = cpAreaForPoly(clippedCount, clipped, 0.0f); - cpFloat displacedMass = clippedArea * FLUID_DENSITY; - cpVect centroid = cpCentroidForPoly(clippedCount, clipped); - - ChipmunkDebugDrawPolygon(clippedCount, clipped, 5.0f, RGBAColor(0, 0, 1, 1), RGBAColor(0, 0, 1, 0.1f)); - ChipmunkDebugDrawDot(5, centroid, RGBAColor(0, 0, 1, 1)); - - cpFloat dt = cpSpaceGetCurrentTimeStep(space); - cpVect g = cpSpaceGetGravity(space); - - // Apply the buoyancy force as an impulse. - cpBodyApplyImpulseAtWorldPoint(body, cpvmult(g, -displacedMass * dt), centroid); - - // Apply linear damping for the fluid drag. - cpVect v_centroid = cpBodyGetVelocityAtWorldPoint(body, centroid); - cpFloat k = k_scalar_body(body, centroid, cpvnormalize(v_centroid)); - cpFloat damping = clippedArea * FLUID_DRAG * FLUID_DENSITY; - cpFloat v_coef = cpfexp(-damping * dt * k); // linear drag - // cpFloat v_coef = 1.0/(1.0 + damping*dt*cpvlength(v_centroid)*k); // quadratic drag - cpBodyApplyImpulseAtWorldPoint(body, cpvmult(cpvsub(cpvmult(v_centroid, v_coef), v_centroid), 1.0 / k), centroid); - - // Apply angular damping for the fluid drag. - cpVect cog = cpBodyLocalToWorld(body, cpBodyGetCenterOfGravity(body)); - cpFloat w_damping = - cpMomentForPoly(FLUID_DRAG * FLUID_DENSITY * clippedArea, clippedCount, clipped, cpvneg(cog), 0.0f); - cpBodySetAngularVelocity(body, cpBodyGetAngularVelocity(body) * cpfexp(-w_damping * dt / cpBodyGetMoment(body))); - - return cpTrue; -} - -static cpSpace* init(void) -{ - ChipmunkDemoMessageString = messageBuffer; - - cpSpace* space = cpSpaceNew(); - cpSpaceSetIterations(space, 30); - cpSpaceSetGravity(space, cpv(0, -500)); - // cpSpaceSetDamping(space, 0.5); - cpSpaceSetSleepTimeThreshold(space, 0.5f); - cpSpaceSetCollisionSlop(space, 0.5f); - - cpBody *body, *staticBody = cpSpaceGetStaticBody(space); - cpShape* shape; - - // Create segments around the edge of the screen. - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, -240), cpv(-320, 240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(320, -240), cpv(320, 240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, -240), cpv(320, -240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, 240), cpv(320, 240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - { - // Add the edges of the bucket - cpBB bb = cpBBNew(-300, -200, 100, 0); - cpFloat radius = 5.0f; - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(bb.l, bb.b), cpv(bb.l, bb.t), radius)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(bb.r, bb.b), cpv(bb.r, bb.t), radius)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(bb.l, bb.b), cpv(bb.r, bb.b), radius)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - // Add the sensor for the water. - shape = cpSpaceAddShape(space, cpBoxShapeNew2(staticBody, bb, 0.0)); - cpShapeSetSensor(shape, cpTrue); - cpShapeSetCollisionType(shape, 1); - } - - { - cpFloat width = 200.0f; - cpFloat height = 50.0f; - cpFloat mass = 0.3 * FLUID_DENSITY * width * height; - cpFloat moment = cpMomentForBox(mass, width, height); - - body = cpSpaceAddBody(space, cpBodyNew(mass, moment)); - cpBodySetPosition(body, cpv(-50, -100)); - cpBodySetVelocity(body, cpv(0, -100)); - cpBodySetAngularVelocity(body, 1); - - shape = cpSpaceAddShape(space, cpBoxShapeNew(body, width, height, 0.0)); - cpShapeSetFriction(shape, 0.8f); - } - - { - cpFloat width = 40.0f; - cpFloat height = width * 2; - cpFloat mass = 0.3 * FLUID_DENSITY * width * height; - cpFloat moment = cpMomentForBox(mass, width, height); - - body = cpSpaceAddBody(space, cpBodyNew(mass, moment)); - cpBodySetPosition(body, cpv(-200, -50)); - cpBodySetVelocity(body, cpv(0, -100)); - cpBodySetAngularVelocity(body, 1); - - shape = cpSpaceAddShape(space, cpBoxShapeNew(body, width, height, 0.0)); - cpShapeSetFriction(shape, 0.8f); - } - - cpCollisionHandler* handler = cpSpaceAddCollisionHandler(space, 1, 0); - handler->preSolveFunc = (cpCollisionPreSolveFunc)waterPreSolve; - - return space; -} - -static void destroy(cpSpace* space) -{ - ChipmunkDemoFreeSpaceChildren(space); - cpSpaceFree(space); -} - -ChipmunkDemo Buoyancy = { - "Simple Sensor based fluids.", 1.0 / 180.0, init, update, ChipmunkDemoDefaultDrawImpl, destroy, -}; diff --git a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Chains.cpp b/tests/cpp-tests/Source/ChipmunkTestBed/demo/Chains.cpp deleted file mode 100644 index 41cbf8cd2a11..000000000000 --- a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Chains.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/* Copyright (c) 2007 Scott Lembcke - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk.h" -#include "ChipmunkDemo.h" - -#define CHAIN_COUNT 8 -#define LINK_COUNT 10 - -static void BreakablejointPostStepRemove(cpSpace* space, cpConstraint* joint, void* unused) -{ - cpSpaceRemoveConstraint(space, joint); - cpConstraintFree(joint); -} - -static void BreakableJointPostSolve(cpConstraint* joint, cpSpace* space) -{ - cpFloat dt = cpSpaceGetCurrentTimeStep(space); - - // Convert the impulse to a force by dividing it by the timestep. - cpFloat force = cpConstraintGetImpulse(joint) / dt; - cpFloat maxForce = cpConstraintGetMaxForce(joint); - - // If the force is almost as big as the joint's max force, break it. - if (force > 0.9 * maxForce) - { - cpSpaceAddPostStepCallback(space, (cpPostStepFunc)BreakablejointPostStepRemove, joint, NULL); - } -} - -static void update(cpSpace* space, double dt) -{ - cpSpaceStep(space, dt); -} - -static cpSpace* init(void) -{ - cpSpace* space = cpSpaceNew(); - cpSpaceSetIterations(space, 30); - cpSpaceSetGravity(space, cpv(0, -100)); - cpSpaceSetSleepTimeThreshold(space, 0.5f); - - cpBody *body, *staticBody = cpSpaceGetStaticBody(space); - cpShape* shape; - - // Create segments around the edge of the screen. - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, -240), cpv(-320, 240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(320, -240), cpv(320, 240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, -240), cpv(320, -240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, 240), cpv(320, 240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - cpFloat mass = 1; - cpFloat width = 20; - cpFloat height = 30; - - cpFloat spacing = width * 0.3; - - // Add lots of boxes. - for (int i = 0; i < CHAIN_COUNT; i++) - { - cpBody* prev = NULL; - - for (int j = 0; j < LINK_COUNT; j++) - { - cpVect pos = cpv(40 * (i - (CHAIN_COUNT - 1) / 2.0), 240 - (j + 0.5) * height - (j + 1) * spacing); - - body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForBox(mass, width, height))); - cpBodySetPosition(body, pos); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(body, cpv(0, (height - width) / 2.0), - cpv(0, (width - height) / 2.0), width / 2.0)); - cpShapeSetFriction(shape, 0.8f); - - cpFloat breakingForce = 80000; - - cpConstraint* constraint = NULL; - if (prev == NULL) - { - constraint = cpSpaceAddConstraint( - space, cpSlideJointNew(body, staticBody, cpv(0, height / 2), cpv(pos.x, 240), 0, spacing)); - } - else - { - constraint = cpSpaceAddConstraint( - space, cpSlideJointNew(body, prev, cpv(0, height / 2), cpv(0, -height / 2), 0, spacing)); - } - - cpConstraintSetMaxForce(constraint, breakingForce); - cpConstraintSetPostSolveFunc(constraint, BreakableJointPostSolve); - cpConstraintSetCollideBodies(constraint, cpFalse); - - prev = body; - } - } - - cpFloat radius = 15.0f; - body = cpSpaceAddBody(space, cpBodyNew(10.0f, cpMomentForCircle(10.0f, 0.0f, radius, cpvzero))); - cpBodySetPosition(body, cpv(0, -240 + radius + 5)); - cpBodySetVelocity(body, cpv(0, 300)); - - shape = cpSpaceAddShape(space, cpCircleShapeNew(body, radius, cpvzero)); - cpShapeSetElasticity(shape, 0.0f); - cpShapeSetFriction(shape, 0.9f); - - return space; -} - -static void destroy(cpSpace* space) -{ - ChipmunkDemoFreeSpaceChildren(space); - cpSpaceFree(space); -} - -ChipmunkDemo Chains = { - "Breakable Chains", 1.0 / 180.0, init, update, ChipmunkDemoDefaultDrawImpl, destroy, -}; diff --git a/tests/cpp-tests/Source/ChipmunkTestBed/demo/ChipmunkDemo.h b/tests/cpp-tests/Source/ChipmunkTestBed/demo/ChipmunkDemo.h deleted file mode 100644 index 65c2cbaaac8c..000000000000 --- a/tests/cpp-tests/Source/ChipmunkTestBed/demo/ChipmunkDemo.h +++ /dev/null @@ -1,104 +0,0 @@ -/* Copyright (c) 2007 Scott Lembcke - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -typedef struct ChipmunkDemo ChipmunkDemo; - -typedef cpSpace* (*ChipmunkDemoInitFunc)(void); -typedef void (*ChipmunkDemoUpdateFunc)(cpSpace* space, double dt); -typedef void (*ChipmunkDemoDrawFunc)(cpSpace* space); -typedef void (*ChipmunkDemoDestroyFunc)(cpSpace* space); - -struct ChipmunkDemo -{ - const char* name; - double timestep; - - ChipmunkDemoInitFunc initFunc; - ChipmunkDemoUpdateFunc updateFunc; - ChipmunkDemoDrawFunc drawFunc; - - ChipmunkDemoDestroyFunc destroyFunc; -}; - -static inline cpFloat frand(void) -{ - return (cpFloat)rand() / (cpFloat)RAND_MAX; -} - -static inline cpVect frand_unit_circle() -{ - cpVect v = cpv(frand() * 2.0f - 1.0f, frand() * 2.0f - 1.0f); - return (cpvlengthsq(v) < 1.0f ? v : frand_unit_circle()); -} - -typedef struct -{ - float x, y; -} float2; -typedef struct -{ - uint8_t r, g, b, a; -} RGBA8; -typedef struct -{ - float2 pos; - float2 uv; - float r; - RGBA8 fill, outline; -} Vertex; -typedef uint16_t Index; - -extern int ChipmunkDemoTicks; -extern double ChipmunkDemoTime; -extern cpVect ChipmunkDemoKeyboard; -extern cpVect ChipmunkDemoMouse; -extern cpBool ChipmunkDemoRightClick; -extern cpBool ChipmunkDemoRightDown; -extern float ChipmunkDebugDrawPointLineScale; - -extern char const* ChipmunkDemoMessageString; -extern void ChipmunkDemoPrintString(char const* fmt, ...); - -extern cpShapeFilter GRAB_FILTER; -extern cpShapeFilter NOT_GRABBABLE_FILTER; - -void ChipmunkDemoDefaultDrawImpl(cpSpace* space); -void ChipmunkDemoFreeSpaceChildren(cpSpace* space); -void ChipmunkDebugDrawCircle(cpVect pos, - cpFloat angle, - cpFloat radius, - cpSpaceDebugColor outlineColor, - cpSpaceDebugColor fillColor); -void ChipmunkDebugDrawSegment(cpVect a, cpVect b, cpSpaceDebugColor color); -void ChipmunkDebugDrawFatSegment(cpVect a, - cpVect b, - cpFloat radius, - cpSpaceDebugColor outlineColor, - cpSpaceDebugColor fillColor); -void ChipmunkDebugDrawPolygon(int count, - const cpVect* verts, - cpFloat radius, - cpSpaceDebugColor outlineColor, - cpSpaceDebugColor fillColor); -void ChipmunkDebugDrawDot(cpFloat size, cpVect pos, cpSpaceDebugColor fillColor); -void ChipmunkDebugDrawBB(cpBB bb, cpSpaceDebugColor outlineColor); -cpSpaceDebugColor LAColor(float l, float a); -cpSpaceDebugColor RGBAColor(float r, float g, float b, float a); \ No newline at end of file diff --git a/tests/cpp-tests/Source/ChipmunkTestBed/demo/ChipmunkDemo.h_org b/tests/cpp-tests/Source/ChipmunkTestBed/demo/ChipmunkDemo.h_org deleted file mode 100644 index 7e3d3b33c148..000000000000 --- a/tests/cpp-tests/Source/ChipmunkTestBed/demo/ChipmunkDemo.h_org +++ /dev/null @@ -1,68 +0,0 @@ -/* Copyright (c) 2007 Scott Lembcke - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "ChipmunkDebugDraw.h" - -typedef struct ChipmunkDemo ChipmunkDemo; - -typedef cpSpace *(*ChipmunkDemoInitFunc)(void); -typedef void (*ChipmunkDemoUpdateFunc)(cpSpace *space, double dt); -typedef void (*ChipmunkDemoDrawFunc)(cpSpace *space); -typedef void (*ChipmunkDemoDestroyFunc)(cpSpace *space); - -struct ChipmunkDemo { - const char *name; - double timestep; - - ChipmunkDemoInitFunc initFunc; - ChipmunkDemoUpdateFunc updateFunc; - ChipmunkDemoDrawFunc drawFunc; - - ChipmunkDemoDestroyFunc destroyFunc; -}; - -static inline cpFloat -frand(void) -{ - return (cpFloat)rand()/(cpFloat)RAND_MAX; -} - -static inline cpVect -frand_unit_circle(){ - cpVect v = cpv(frand()*2.0f - 1.0f, frand()*2.0f - 1.0f); - return (cpvlengthsq(v) < 1.0f ? v : frand_unit_circle()); -} - -extern int ChipmunkDemoTicks; -extern double ChipmunkDemoTime; -extern cpVect ChipmunkDemoKeyboard; -extern cpVect ChipmunkDemoMouse; -extern cpBool ChipmunkDemoRightClick; -extern cpBool ChipmunkDemoRightDown; - -extern char const *ChipmunkDemoMessageString; -void ChipmunkDemoPrintString(char const *fmt, ...); - -extern cpShapeFilter GRAB_FILTER; -extern cpShapeFilter NOT_GRABBABLE_FILTER; - -void ChipmunkDemoDefaultDrawImpl(cpSpace *space); -void ChipmunkDemoFreeSpaceChildren(cpSpace *space); diff --git a/tests/cpp-tests/Source/ChipmunkTestBed/demo/ContactGraph.cpp b/tests/cpp-tests/Source/ChipmunkTestBed/demo/ContactGraph.cpp deleted file mode 100644 index ce6d1a29ed96..000000000000 --- a/tests/cpp-tests/Source/ChipmunkTestBed/demo/ContactGraph.cpp +++ /dev/null @@ -1,211 +0,0 @@ -/* Copyright (c) 2007 Scott Lembcke - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk.h" -#include "ChipmunkDemo.h" - -// static body that we will be making into a scale -static cpBody* scaleStaticBody; -static cpBody* ballBody; - -// If your compiler supports blocks (Clang or some GCC versions), -// You can use the block based iterators instead of the function ones to make your life easier. -#if defined(__has_extension) -# if __has_extension(blocks) - -# define USE_BLOCKS 1 - -# endif -#endif - -#if !USE_BLOCKS - -static void ScaleIterator(cpBody* body, cpArbiter* arb, cpVect* sum) -{ - (*sum) = cpvadd(*sum, cpArbiterTotalImpulse(arb)); -} - -static void BallIterator(cpBody* body, cpArbiter* arb, int* count) -{ - // body is the body we are iterating the arbiters for. - // CP_ARBITER_GET_*() in an arbiter iterator always returns the body/shape for the iterated body first. - CP_ARBITER_GET_SHAPES(arb, ball, other); - ChipmunkDebugDrawBB(cpShapeGetBB(other), RGBAColor(1, 0, 0, 1)); - - (*count)++; -} - -struct CrushingContext -{ - cpFloat magnitudeSum; - cpVect vectorSum; -}; - -static void EstimateCrushing(cpBody* body, cpArbiter* arb, struct CrushingContext* context) -{ - cpVect j = cpArbiterTotalImpulse(arb); - context->magnitudeSum += cpvlength(j); - context->vectorSum = cpvadd(context->vectorSum, j); -} - -#endif - -static void update(cpSpace* space, double dt) -{ - cpSpaceStep(space, dt); - - ChipmunkDemoPrintString("Place objects on the scale to weigh them. The ball marks the shapes it's sitting on.\n"); - -// Sum the total impulse applied to the scale from all collision pairs in the contact graph. -// If your compiler supports blocks, your life is a little easier. -// You can use the "Block" versions of the functions without needing the callbacks above. -#if USE_BLOCKS - __block cpVect impulseSum = cpvzero; - cpBodyEachArbiter_b(scaleStaticBody, ^(cpArbiter* arb) { - impulseSum = cpvadd(impulseSum, cpArbiterTotalImpulse(arb)); - }); -#else - cpVect impulseSum = cpvzero; - cpBodyEachArbiter(scaleStaticBody, (cpBodyArbiterIteratorFunc)ScaleIterator, &impulseSum); -#endif - - // Force is the impulse divided by the timestep. - cpFloat force = cpvlength(impulseSum) / dt; - - // Weight can be found similarly from the gravity vector. - cpVect g = cpSpaceGetGravity(space); - cpFloat weight = cpvdot(g, impulseSum) / (cpvlengthsq(g) * dt); - - ChipmunkDemoPrintString("Total force: %5.2f, Total weight: %5.2f. ", force, weight); - -// Highlight and count the number of shapes the ball is touching. -#if USE_BLOCKS - __block int count = 0; - cpBodyEachArbiter_b(ballBody, ^(cpArbiter* arb) { - // body is the body we are iterating the arbiters for. - // CP_ARBITER_GET_*() in an arbiter iterator always returns the body/shape for the iterated body first. - CP_ARBITER_GET_SHAPES(arb, ball, other); - ChipmunkDebugDrawBB(cpShapeGetBB(other), RGBAColor(1, 0, 0, 1)); - - count++; - }); -#else - int count = 0; - cpBodyEachArbiter(ballBody, (cpBodyArbiterIteratorFunc)BallIterator, &count); -#endif - - ChipmunkDemoPrintString("The ball is touching %d shapes.\n", count); - -#if USE_BLOCKS - __block cpFloat magnitudeSum = 0.0f; - __block cpVect vectorSum = cpvzero; - cpBodyEachArbiter_b(ballBody, ^(cpArbiter* arb) { - cpVect j = cpArbiterTotalImpulse(arb); - magnitudeSum += cpvlength(j); - vectorSum = cpvadd(vectorSum, j); - }); - - cpFloat crushForce = (magnitudeSum - cpvlength(vectorSum)) * dt; -#else - struct CrushingContext crush = {0.0f, cpvzero}; - cpBodyEachArbiter(ballBody, (cpBodyArbiterIteratorFunc)EstimateCrushing, &crush); - - cpFloat crushForce = (crush.magnitudeSum - cpvlength(crush.vectorSum)) * dt; -#endif - - if (crushForce > 10.0f) - { - ChipmunkDemoPrintString("The ball is being crushed. (f: %.2f)", crushForce); - } - else - { - ChipmunkDemoPrintString("The ball is not being crushed. (f: %.2f)", crushForce); - } -} - -#define WIDTH 4.0f -#define HEIGHT 30.0f - -static cpSpace* init(void) -{ - cpSpace* space = cpSpaceNew(); - cpSpaceSetIterations(space, 30); - cpSpaceSetGravity(space, cpv(0, -300)); - cpSpaceSetCollisionSlop(space, 0.5); - cpSpaceSetSleepTimeThreshold(space, 1.0f); - - cpBody *body, *staticBody = cpSpaceGetStaticBody(space); - cpShape* shape; - - // Create segments around the edge of the screen. - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, -240), cpv(-320, 240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(320, -240), cpv(320, 240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, -240), cpv(320, -240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - scaleStaticBody = cpSpaceAddBody(space, cpBodyNewStatic()); - shape = cpSpaceAddShape(space, cpSegmentShapeNew(scaleStaticBody, cpv(-240, -180), cpv(-140, -180), 4.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - // add some boxes to stack on the scale - for (int i = 0; i < 5; i++) - { - body = cpSpaceAddBody(space, cpBodyNew(1.0f, cpMomentForBox(1.0f, 30.0f, 30.0f))); - cpBodySetPosition(body, cpv(0, i * 32 - 120)); - - shape = cpSpaceAddShape(space, cpBoxShapeNew(body, 30.0f, 30.0f, 0.0)); - cpShapeSetElasticity(shape, 0.0f); - cpShapeSetFriction(shape, 0.8f); - } - - // Add a ball that we'll track which objects are beneath it. - cpFloat radius = 15.0f; - ballBody = cpSpaceAddBody(space, cpBodyNew(10.0f, cpMomentForCircle(10.0f, 0.0f, radius, cpvzero))); - cpBodySetPosition(ballBody, cpv(120, -140 + radius + 5)); - - shape = cpSpaceAddShape(space, cpCircleShapeNew(ballBody, radius, cpvzero)); - cpShapeSetElasticity(shape, 0.0f); - cpShapeSetFriction(shape, 0.9f); - - return space; -} - -static void destroy(cpSpace* space) -{ - ChipmunkDemoFreeSpaceChildren(space); - cpSpaceFree(space); -} - -ChipmunkDemo ContactGraph = { - "Contact Graph", 1.0 / 60.0, init, update, ChipmunkDemoDefaultDrawImpl, destroy, -}; diff --git a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Convex.cpp b/tests/cpp-tests/Source/ChipmunkTestBed/demo/Convex.cpp deleted file mode 100644 index b4e457f26be3..000000000000 --- a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Convex.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* Copyright (c) 2007 Scott Lembcke - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk.h" -#include "chipmunk/chipmunk_unsafe.h" - -#include "ChipmunkDemo.h" - -#define DENSITY (1.0 / 10000.0) - -static cpShape* shape; - -static void update(cpSpace* space, double dt) -{ - cpFloat tolerance = 2.0; - - if (ChipmunkDemoRightClick && cpShapePointQuery(shape, ChipmunkDemoMouse, NULL) > tolerance) - { - cpBody* body = cpShapeGetBody(shape); - int count = cpPolyShapeGetCount(shape); - - // Allocate the space for the new vertexes on the stack. - cpVect* verts = (cpVect*)alloca((count + 1) * sizeof(cpVect)); - - for (int i = 0; i < count; i++) - { - verts[i] = cpPolyShapeGetVert(shape, i); - } - - verts[count] = cpBodyWorldToLocal(body, ChipmunkDemoMouse); - - // This function builds a convex hull for the vertexes. - // Because the result array is the same as verts, it will reduce it in place. - int hullCount = cpConvexHull(count + 1, verts, verts, NULL, tolerance); - - // Figure out how much to shift the body by. - cpVect centroid = cpCentroidForPoly(hullCount, verts); - - // Recalculate the body properties to match the updated shape. - cpFloat mass = cpAreaForPoly(hullCount, verts, 0.0f) * DENSITY; - cpBodySetMass(body, mass); - cpBodySetMoment(body, cpMomentForPoly(mass, hullCount, verts, cpvneg(centroid), 0.0f)); - cpBodySetPosition(body, cpBodyLocalToWorld(body, centroid)); - - // Use the setter function from chipmunk_unsafe.h. - // You could also remove and recreate the shape if you wanted. - cpPolyShapeSetVerts(shape, hullCount, verts, cpTransformTranslate(cpvneg(centroid))); - } - - cpSpaceStep(space, dt); -} - -static cpSpace* init(void) -{ - ChipmunkDemoMessageString = "Right click and drag to change the blocks's shape."; - - cpSpace* space = cpSpaceNew(); - cpSpaceSetIterations(space, 30); - cpSpaceSetGravity(space, cpv(0, -500)); - cpSpaceSetSleepTimeThreshold(space, 0.5f); - cpSpaceSetCollisionSlop(space, 0.5f); - - cpBody *body, *staticBody = cpSpaceGetStaticBody(space); - - // Create segments around the edge of the screen. - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, -240), cpv(320, -240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - cpFloat width = 50.0f; - cpFloat height = 70.0f; - cpFloat mass = width * height * DENSITY; - cpFloat moment = cpMomentForBox(mass, width, height); - - body = cpSpaceAddBody(space, cpBodyNew(mass, moment)); - - shape = cpSpaceAddShape(space, cpBoxShapeNew(body, width, height, 0.0)); - cpShapeSetFriction(shape, 0.6f); - - return space; -} - -static void destroy(cpSpace* space) -{ - ChipmunkDemoFreeSpaceChildren(space); - cpSpaceFree(space); -} - -ChipmunkDemo Convex = { - "Convex.", 1.0 / 60.0, init, update, ChipmunkDemoDefaultDrawImpl, destroy, -}; diff --git a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Crane.cpp b/tests/cpp-tests/Source/ChipmunkTestBed/demo/Crane.cpp deleted file mode 100644 index 7d0cfe9e021d..000000000000 --- a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Crane.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/* Copyright (c) 2007 Scott Lembcke - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk.h" -#include "ChipmunkDemo.h" - -static cpBody* dollyBody = NULL; -// Constraint used as a servo motor to move the dolly back and forth. -static cpConstraint* dollyServo = NULL; - -// Constraint used as a winch motor to lift the load. -static cpConstraint* winchServo = NULL; - -// Temporary joint used to hold the hook to the load. -static cpConstraint* hookJoint = NULL; - -static void update(cpSpace* space, double dt) -{ - // Set the first anchor point (the one attached to the static body) of the dolly servo to the mouse's x position. - cpPivotJointSetAnchorA(dollyServo, cpv(ChipmunkDemoMouse.x, 100)); - - // Set the max length of the winch servo to match the mouse's height. - cpSlideJointSetMax(winchServo, cpfmax(100 - ChipmunkDemoMouse.y, 50)); - - if (hookJoint && ChipmunkDemoRightClick) - { - cpSpaceRemoveConstraint(space, hookJoint); - cpConstraintFree(hookJoint); - hookJoint = NULL; - } - - cpSpaceStep(space, dt); -} - -enum COLLISION_TYPES -{ - HOOK_SENSOR = 1, - CRATE, -}; - -static void AttachHook(cpSpace* space, cpBody* hook, cpBody* crate) -{ - hookJoint = cpSpaceAddConstraint(space, cpPivotJointNew(hook, crate, cpBodyGetPosition(hook))); -} - -static cpBool HookCrate(cpArbiter* arb, cpSpace* space, void* data) -{ - if (hookJoint == NULL) - { - // Get pointers to the two bodies in the collision pair and define local variables for them. - // Their order matches the order of the collision types passed - // to the collision handler this function was defined for - CP_ARBITER_GET_BODIES(arb, hook, crate); - - // additions and removals can't be done in a normal callback. - // Schedule a post step callback to do it. - // Use the hook as the key and pass along the arbiter. - cpSpaceAddPostStepCallback(space, (cpPostStepFunc)AttachHook, hook, crate); - } - - return cpTrue; // return value is ignored for sensor callbacks anyway -} - -static cpSpace* init(void) -{ - ChipmunkDemoMessageString = "Control the crane by moving the mouse. Right click to release."; - - cpSpace* space = cpSpaceNew(); - cpSpaceSetIterations(space, 30); - cpSpaceSetGravity(space, cpv(0, -100)); - cpSpaceSetDamping(space, 0.8); - - cpBody* staticBody = cpSpaceGetStaticBody(space); - cpShape* shape; - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, -240), cpv(320, -240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - // Add a body for the dolly. - dollyBody = cpSpaceAddBody(space, cpBodyNew(10, INFINITY)); - cpBodySetPosition(dollyBody, cpv(0, 100)); - - // Add a block so you can see it. - cpSpaceAddShape(space, cpBoxShapeNew(dollyBody, 30, 30, 0.0)); - - // Add a groove joint for it to move back and forth on. - cpSpaceAddConstraint(space, cpGrooveJointNew(staticBody, dollyBody, cpv(-250, 100), cpv(250, 100), cpvzero)); - - // Add a pivot joint to act as a servo motor controlling it's position - // By updating the anchor points of the pivot joint, you can move the dolly. - dollyServo = cpSpaceAddConstraint(space, cpPivotJointNew(staticBody, dollyBody, cpBodyGetPosition(dollyBody))); - // Max force the dolly servo can generate. - cpConstraintSetMaxForce(dollyServo, 10000); - // Max speed of the dolly servo - cpConstraintSetMaxBias(dollyServo, 100); - // You can also change the error bias to control how it slows down. - // cpConstraintSetErrorBias(dollyServo, 0.2); - - // Add the crane hook. - cpBody* hookBody = cpSpaceAddBody(space, cpBodyNew(1, INFINITY)); - cpBodySetPosition(hookBody, cpv(0, 50)); - - // Add a sensor shape for it. This will be used to figure out when the hook touches a box. - shape = cpSpaceAddShape(space, cpCircleShapeNew(hookBody, 10, cpvzero)); - cpShapeSetSensor(shape, cpTrue); - cpShapeSetCollisionType(shape, HOOK_SENSOR); - - // Add a slide joint to act as a winch motor - // By updating the max length of the joint you can make it pull up the load. - winchServo = cpSpaceAddConstraint(space, cpSlideJointNew(dollyBody, hookBody, cpvzero, cpvzero, 0, INFINITY)); - // Max force the dolly servo can generate. - cpConstraintSetMaxForce(winchServo, 30000); - // Max speed of the dolly servo - cpConstraintSetMaxBias(winchServo, 60); - - // TODO: cleanup - // Finally a box to play with - cpBody* boxBody = cpSpaceAddBody(space, cpBodyNew(30, cpMomentForBox(30, 50, 50))); - cpBodySetPosition(boxBody, cpv(200, -200)); - - // Add a block so you can see it. - shape = cpSpaceAddShape(space, cpBoxShapeNew(boxBody, 50, 50, 0.0)); - cpShapeSetFriction(shape, 0.7); - cpShapeSetCollisionType(shape, CRATE); - - cpCollisionHandler* handler = cpSpaceAddCollisionHandler(space, HOOK_SENSOR, CRATE); - handler->beginFunc = (cpCollisionBeginFunc)HookCrate; - - return space; -} - -static void destroy(cpSpace* space) -{ - ChipmunkDemoFreeSpaceChildren(space); - cpSpaceFree(space); -} - -ChipmunkDemo Crane = { - "Crane", 1.0 / 60.0, init, update, ChipmunkDemoDefaultDrawImpl, destroy, -}; diff --git a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Example.cpp b/tests/cpp-tests/Source/ChipmunkTestBed/demo/Example.cpp deleted file mode 100644 index 43eb6ac0f988..000000000000 --- a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Example.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/* Copyright (c) 2007 Scott Lembcke - * Copyright (c) 2021 @aismann; Peter Eismann, Germany; dreifrankensoft - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include -#include -#if !defined(_USE_MATH_DEFINES) -# define _USE_MATH_DEFINES -#endif -#include -#include - -#include "chipmunk/chipmunk_private.h" -#include "chipmunk/chipmunk_unsafe.h" -#include "ChipmunkDemo.h" - -static void update(cpSpace* space, double dt) -{ - cpSpaceStep(space, dt); -} - -static cpSpace* init(void) -{ - cpSpace* space = cpSpaceNew(); - cpSpaceSetIterations(space, 5); - space->damping = 0.1; - - cpShape *shape1, *shape2; - - cpFloat mass = 1.0f; - - { - cpFloat size = 100.0; - - cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForBox(mass, size, size))); - cpBodySetPosition(body, cpv(100.0, 50.0f)); - - shape1 = cpSpaceAddShape(space, cpBoxShapeNew(body, size, size, 0.0)); - } - { - cpFloat size = 100.0; - - cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForBox(mass, size, size))); - cpBodySetPosition(body, cpv(120.0, -40.0f)); - cpBodySetAngle(body, 1e-2); - - shape2 = cpSpaceAddShape(space, cpBoxShapeNew(body, size, size, 0.0)); - } - - { - cpFloat size = 100.0; - const int NUM_VERTS = 5; - - cpVect verts[NUM_VERTS]; - for (int i = 0; i < NUM_VERTS; i++) - { - cpFloat angle = -2 * M_PI * i / ((cpFloat)NUM_VERTS); - verts[i] = cpv(size / 2.0 * cos(angle), size / 2.0 * sin(angle)); - } - - cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForPoly(mass, NUM_VERTS, verts, cpvzero, 0.0))); - cpBodySetPosition(body, cpv(100.0, 50.0f)); - - shape1 = cpSpaceAddShape(space, cpPolyShapeNew(body, NUM_VERTS, verts, cpTransformIdentity, 0.0)); - } - { - cpFloat size = 100.0; - const int NUM_VERTS = 4; - - cpVect verts[NUM_VERTS]; - for (int i = 0; i < NUM_VERTS; i++) - { - cpFloat angle = -2 * M_PI * i / ((cpFloat)NUM_VERTS); - verts[i] = cpv(size / 2.0 * cos(angle), size / 2.0 * sin(angle)); - } - - cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForPoly(mass, NUM_VERTS, verts, cpvzero, 0.0))); - cpBodySetPosition(body, cpv(100.0, -50.0f)); - shape2 = cpSpaceAddShape(space, cpPolyShapeNew(body, NUM_VERTS, verts, cpTransformIdentity, 5.0)); - } - - { - cpFloat size = 150.0; - cpFloat radius = 25.0; - - cpVect a = cpv(size / 2.0, 0.0); - cpVect b = cpv(-size / 2.0, 0.0); - cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForSegment(mass, a, b, 0.0))); - cpBodySetPosition(body, cpv(0, 25)); - - shape1 = cpSpaceAddShape(space, cpSegmentShapeNew(body, a, b, radius)); - } - { - cpFloat radius = 50.0; - - cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForCircle(mass, 0.0f, radius, cpvzero))); - cpBodySetPosition(body, cpv(0, -25)); - - shape2 = cpSpaceAddShape(space, cpCircleShapeNew(body, radius, cpvzero)); - } - - return space; -} - -static void destroy(cpSpace* space) -{ - ChipmunkDemoFreeSpaceChildren(space); - cpSpaceFree(space); -} - -ChipmunkDemo Example = { - "Template to start (GJK)", 1.0f / 60.0f, init, update, ChipmunkDemoDefaultDrawImpl, destroy, -}; diff --git a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Joints.cpp b/tests/cpp-tests/Source/ChipmunkTestBed/demo/Joints.cpp deleted file mode 100644 index db27fe805351..000000000000 --- a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Joints.cpp +++ /dev/null @@ -1,284 +0,0 @@ -/* Copyright (c) 2007 Scott Lembcke - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk.h" -#include "ChipmunkDemo.h" - -static cpBody* addBall(cpSpace* space, cpVect pos, cpVect boxOffset) -{ - cpFloat radius = 15.0f; - cpFloat mass = 1.0f; - cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForCircle(mass, 0.0f, radius, cpvzero))); - cpBodySetPosition(body, cpvadd(pos, boxOffset)); - - cpShape* shape = cpSpaceAddShape(space, cpCircleShapeNew(body, radius, cpvzero)); - cpShapeSetElasticity(shape, 0.0f); - cpShapeSetFriction(shape, 0.7f); - - return body; -} - -static cpBody* addLever(cpSpace* space, cpVect pos, cpVect boxOffset) -{ - cpFloat mass = 1.0f; - cpVect a = cpv(0, 15); - cpVect b = cpv(0, -15); - - cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForSegment(mass, a, b, 0.0f))); - cpBodySetPosition(body, cpvadd(pos, cpvadd(boxOffset, cpv(0, -15)))); - - cpShape* shape = cpSpaceAddShape(space, cpSegmentShapeNew(body, a, b, 5.0f)); - cpShapeSetElasticity(shape, 0.0f); - cpShapeSetFriction(shape, 0.7f); - - return body; -} - -static cpBody* addBar(cpSpace* space, cpVect pos, cpVect boxOffset) -{ - cpFloat mass = 2.0f; - cpVect a = cpv(0, 30); - cpVect b = cpv(0, -30); - - cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForSegment(mass, a, b, 0.0f))); - cpBodySetPosition(body, cpvadd(pos, boxOffset)); - - cpShape* shape = cpSpaceAddShape(space, cpSegmentShapeNew(body, a, b, 5.0f)); - cpShapeSetElasticity(shape, 0.0f); - cpShapeSetFriction(shape, 0.7f); - cpShapeSetFilter(shape, cpShapeFilterNew(1, CP_ALL_CATEGORIES, CP_ALL_CATEGORIES)); - - return body; -} - -static cpBody* addWheel(cpSpace* space, cpVect pos, cpVect boxOffset) -{ - cpFloat radius = 15.0f; - cpFloat mass = 1.0f; - cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForCircle(mass, 0.0f, radius, cpvzero))); - cpBodySetPosition(body, cpvadd(pos, boxOffset)); - - cpShape* shape = cpSpaceAddShape(space, cpCircleShapeNew(body, radius, cpvzero)); - cpShapeSetElasticity(shape, 0.0f); - cpShapeSetFriction(shape, 0.7f); - cpShapeSetFilter(shape, cpShapeFilterNew(1, CP_ALL_CATEGORIES, CP_ALL_CATEGORIES)); - - return body; -} - -static cpBody* addChassis(cpSpace* space, cpVect pos, cpVect boxOffset) -{ - cpFloat mass = 5.0f; - cpFloat width = 80; - cpFloat height = 30; - - cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForBox(mass, width, height))); - cpBodySetPosition(body, cpvadd(pos, boxOffset)); - - cpShape* shape = cpSpaceAddShape(space, cpBoxShapeNew(body, width, height, 0.0)); - cpShapeSetElasticity(shape, 0.0f); - cpShapeSetFriction(shape, 0.7f); - cpShapeSetFilter(shape, cpShapeFilterNew(1, CP_ALL_CATEGORIES, CP_ALL_CATEGORIES)); - - return body; -} - -static cpSpace* init(void) -{ - cpSpace* space = cpSpaceNew(); - cpSpaceSetIterations(space, 10); - cpSpaceSetGravity(space, cpv(0, -100)); - cpSpaceSetSleepTimeThreshold(space, 0.5f); - - cpBody* staticBody = cpSpaceGetStaticBody(space); - cpShape* shape; - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, 240), cpv(320, 240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, 120), cpv(320, 120), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, 0), cpv(320, 0), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, -120), cpv(320, -120), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, -240), cpv(320, -240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, -240), cpv(-320, 240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-160, -240), cpv(-160, 240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(0, -240), cpv(0, 240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(160, -240), cpv(160, 240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(320, -240), cpv(320, 240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - cpVect boxOffset; - cpBody *body1, *body2; - - cpVect posA = cpv(50, 60); - cpVect posB = cpv(110, 60); - -#define POS_A cpvadd(boxOffset, posA) -#define POS_B cpvadd(boxOffset, posB) - - // Pin Joints - Link shapes with a solid bar or pin. - // Keeps the anchor points the same distance apart from when the joint was created. - boxOffset = cpv(-320, -240); - body1 = addBall(space, posA, boxOffset); - body2 = addBall(space, posB, boxOffset); - cpSpaceAddConstraint(space, cpPinJointNew(body1, body2, cpv(15, 0), cpv(-15, 0))); - - // Slide Joints - Like pin joints but with a min/max distance. - // Can be used for a cheap approximation of a rope. - boxOffset = cpv(-160, -240); - body1 = addBall(space, posA, boxOffset); - body2 = addBall(space, posB, boxOffset); - cpSpaceAddConstraint(space, cpSlideJointNew(body1, body2, cpv(15, 0), cpv(-15, 0), 20.0f, 40.0f)); - - // Pivot Joints - Holds the two anchor points together. Like a swivel. - boxOffset = cpv(0, -240); - body1 = addBall(space, posA, boxOffset); - body2 = addBall(space, posB, boxOffset); - cpSpaceAddConstraint(space, cpPivotJointNew(body1, body2, cpvadd(boxOffset, cpv(80, 60)))); - // cpPivotJointNew() takes it's anchor parameter in world coordinates. The anchors are calculated from that - // cpPivotJointNew2() lets you specify the two anchor points explicitly - - // Groove Joints - Like a pivot joint, but one of the anchors is a line segment that the pivot can slide in - boxOffset = cpv(160, -240); - body1 = addBall(space, posA, boxOffset); - body2 = addBall(space, posB, boxOffset); - cpSpaceAddConstraint(space, cpGrooveJointNew(body1, body2, cpv(30, 30), cpv(30, -30), cpv(-30, 0))); - - // Damped Springs - boxOffset = cpv(-320, -120); - body1 = addBall(space, posA, boxOffset); - body2 = addBall(space, posB, boxOffset); - cpSpaceAddConstraint(space, cpDampedSpringNew(body1, body2, cpv(15, 0), cpv(-15, 0), 20.0f, 5.0f, 0.3f)); - - // Damped Rotary Springs - boxOffset = cpv(-160, -120); - body1 = addBar(space, posA, boxOffset); - body2 = addBar(space, posB, boxOffset); - // Add some pin joints to hold the circles in place. - cpSpaceAddConstraint(space, cpPivotJointNew(body1, staticBody, POS_A)); - cpSpaceAddConstraint(space, cpPivotJointNew(body2, staticBody, POS_B)); - cpSpaceAddConstraint(space, cpDampedRotarySpringNew(body1, body2, 0.0f, 3000.0f, 60.0f)); - - // Rotary Limit Joint - boxOffset = cpv(0, -120); - body1 = addLever(space, posA, boxOffset); - body2 = addLever(space, posB, boxOffset); - // Add some pin joints to hold the circles in place. - cpSpaceAddConstraint(space, cpPivotJointNew(body1, staticBody, POS_A)); - cpSpaceAddConstraint(space, cpPivotJointNew(body2, staticBody, POS_B)); - // Hold their rotation within 90 degrees of each other. - cpSpaceAddConstraint(space, cpRotaryLimitJointNew(body1, body2, -CP_PI / 2.0f, CP_PI / 2.0f)); - - // Ratchet Joint - A rotary ratchet, like a socket wrench - boxOffset = cpv(160, -120); - body1 = addLever(space, posA, boxOffset); - body2 = addLever(space, posB, boxOffset); - // Add some pin joints to hold the circles in place. - cpSpaceAddConstraint(space, cpPivotJointNew(body1, staticBody, POS_A)); - cpSpaceAddConstraint(space, cpPivotJointNew(body2, staticBody, POS_B)); - // Ratchet every 90 degrees - cpSpaceAddConstraint(space, cpRatchetJointNew(body1, body2, 0.0f, CP_PI / 2.0f)); - - // Gear Joint - Maintain a specific angular velocity ratio - boxOffset = cpv(-320, 0); - body1 = addBar(space, posA, boxOffset); - body2 = addBar(space, posB, boxOffset); - // Add some pin joints to hold the circles in place. - cpSpaceAddConstraint(space, cpPivotJointNew(body1, staticBody, POS_A)); - cpSpaceAddConstraint(space, cpPivotJointNew(body2, staticBody, POS_B)); - // Force one to sping 2x as fast as the other - cpSpaceAddConstraint(space, cpGearJointNew(body1, body2, 0.0f, 2.0f)); - - // Simple Motor - Maintain a specific angular relative velocity - boxOffset = cpv(-160, 0); - body1 = addBar(space, posA, boxOffset); - body2 = addBar(space, posB, boxOffset); - // Add some pin joints to hold the circles in place. - cpSpaceAddConstraint(space, cpPivotJointNew(body1, staticBody, POS_A)); - cpSpaceAddConstraint(space, cpPivotJointNew(body2, staticBody, POS_B)); - // Make them spin at 1/2 revolution per second in relation to each other. - cpSpaceAddConstraint(space, cpSimpleMotorNew(body1, body2, CP_PI)); - - // Make a car with some nice soft suspension - boxOffset = cpv(0, 0); - cpBody* wheel1 = addWheel(space, posA, boxOffset); - cpBody* wheel2 = addWheel(space, posB, boxOffset); - cpBody* chassis = addChassis(space, cpv(80, 100), boxOffset); - - cpSpaceAddConstraint(space, cpGrooveJointNew(chassis, wheel1, cpv(-30, -10), cpv(-30, -40), cpvzero)); - cpSpaceAddConstraint(space, cpGrooveJointNew(chassis, wheel2, cpv(30, -10), cpv(30, -40), cpvzero)); - - cpSpaceAddConstraint(space, cpDampedSpringNew(chassis, wheel1, cpv(-30, 0), cpvzero, 50.0f, 20.0f, 10.0f)); - cpSpaceAddConstraint(space, cpDampedSpringNew(chassis, wheel2, cpv(30, 0), cpvzero, 50.0f, 20.0f, 10.0f)); - - return space; -} - -static void update(cpSpace* space, double dt) -{ - cpSpaceStep(space, dt); -} - -static void destroy(cpSpace* space) -{ - ChipmunkDemoFreeSpaceChildren(space); - cpSpaceFree(space); -} - -ChipmunkDemo Joints = { - "Joints and Constraints", 1.0 / 60.0, init, update, ChipmunkDemoDefaultDrawImpl, destroy, -}; diff --git a/tests/cpp-tests/Source/ChipmunkTestBed/demo/LogoSmash.cpp b/tests/cpp-tests/Source/ChipmunkTestBed/demo/LogoSmash.cpp deleted file mode 100644 index 4971f05a5e62..000000000000 --- a/tests/cpp-tests/Source/ChipmunkTestBed/demo/LogoSmash.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/* Copyright (c) 2007 Scott Lembcke - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk.h" -#include "ChipmunkDemo.h" - -static const int image_width = 188; -static const int image_height = 35; -static const int image_row_length = 24; -float ChipmunkDebugDrawPointLineScale = 1.0f; - -static const char image_bitmap[] = { - 15, -16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 7, -64, 15, 63, -32, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 31, -64, 15, 127, -125, -1, -128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 127, -64, 15, 127, 15, -1, -64, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, -64, 15, -2, 31, -1, -64, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, -64, 0, -4, 63, -1, -32, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, -64, 15, -8, 127, -1, - -32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, -64, 0, - -8, -15, -1, -32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -31, - -1, -64, 15, -8, -32, -1, -32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 7, -15, -1, -64, 9, -15, -32, -1, -32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 31, -15, -1, -64, 0, -15, -32, -1, -32, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 63, -7, -1, -64, 9, -29, -32, 127, -61, -16, 63, 15, -61, -1, - -8, 31, -16, 15, -8, 126, 7, -31, -8, 31, -65, -7, -1, -64, 9, -29, -32, 0, 7, -8, 127, - -97, -25, -1, -2, 63, -8, 31, -4, -1, 15, -13, -4, 63, -1, -3, -1, -64, 9, -29, -32, 0, - 7, -8, 127, -97, -25, -1, -2, 63, -8, 31, -4, -1, 15, -13, -2, 63, -1, -3, -1, -64, 9, - -29, -32, 0, 7, -8, 127, -97, -25, -1, -1, 63, -4, 63, -4, -1, 15, -13, -2, 63, -33, -1, - -1, -32, 9, -25, -32, 0, 7, -8, 127, -97, -25, -1, -1, 63, -4, 63, -4, -1, 15, -13, -1, - 63, -33, -1, -1, -16, 9, -25, -32, 0, 7, -8, 127, -97, -25, -1, -1, 63, -4, 63, -4, -1, - 15, -13, -1, 63, -49, -1, -1, -8, 9, -57, -32, 0, 7, -8, 127, -97, -25, -8, -1, 63, -2, - 127, -4, -1, 15, -13, -1, -65, -49, -1, -1, -4, 9, -57, -32, 0, 7, -8, 127, -97, -25, -8, - -1, 63, -2, 127, -4, -1, 15, -13, -1, -65, -57, -1, -1, -2, 9, -57, -32, 0, 7, -8, 127, - -97, -25, -8, -1, 63, -2, 127, -4, -1, 15, -13, -1, -1, -57, -1, -1, -1, 9, -57, -32, 0, - 7, -1, -1, -97, -25, -8, -1, 63, -1, -1, -4, -1, 15, -13, -1, -1, -61, -1, -1, -1, -119, - -57, -32, 0, 7, -1, -1, -97, -25, -8, -1, 63, -1, -1, -4, -1, 15, -13, -1, -1, -61, -1, - -1, -1, -55, -49, -32, 0, 7, -1, -1, -97, -25, -8, -1, 63, -1, -1, -4, -1, 15, -13, -1, - -1, -63, -1, -1, -1, -23, -49, -32, 127, -57, -1, -1, -97, -25, -1, -1, 63, -1, -1, -4, -1, - 15, -13, -1, -1, -63, -1, -1, -1, -16, -49, -32, -1, -25, -1, -1, -97, -25, -1, -1, 63, -33, - -5, -4, -1, 15, -13, -1, -1, -64, -1, -9, -1, -7, -49, -32, -1, -25, -8, 127, -97, -25, -1, - -1, 63, -33, -5, -4, -1, 15, -13, -1, -1, -64, -1, -13, -1, -32, -49, -32, -1, -25, -8, 127, - -97, -25, -1, -2, 63, -49, -13, -4, -1, 15, -13, -1, -1, -64, 127, -7, -1, -119, -17, -15, -1, - -25, -8, 127, -97, -25, -1, -2, 63, -49, -13, -4, -1, 15, -13, -3, -1, -64, 127, -8, -2, 15, - -17, -1, -1, -25, -8, 127, -97, -25, -1, -8, 63, -49, -13, -4, -1, 15, -13, -3, -1, -64, 63, - -4, 120, 0, -17, -1, -1, -25, -8, 127, -97, -25, -8, 0, 63, -57, -29, -4, -1, 15, -13, -4, - -1, -64, 63, -4, 0, 15, -17, -1, -1, -25, -8, 127, -97, -25, -8, 0, 63, -57, -29, -4, -1, - -1, -13, -4, -1, -64, 31, -2, 0, 0, 103, -1, -1, -57, -8, 127, -97, -25, -8, 0, 63, -57, - -29, -4, -1, -1, -13, -4, 127, -64, 31, -2, 0, 15, 103, -1, -1, -57, -8, 127, -97, -25, -8, - 0, 63, -61, -61, -4, 127, -1, -29, -4, 127, -64, 15, -8, 0, 0, 55, -1, -1, -121, -8, 127, - -97, -25, -8, 0, 63, -61, -61, -4, 127, -1, -29, -4, 63, -64, 15, -32, 0, 0, 23, -1, -2, - 3, -16, 63, 15, -61, -16, 0, 31, -127, -127, -8, 31, -1, -127, -8, 31, -128, 7, -128, 0, 0}; - -static inline int get_pixel(int x, int y) -{ - return (image_bitmap[(x >> 3) + y * image_row_length] >> (~x & 0x7)) & 1; -} - -static int bodyCount = 0; - -static void update(cpSpace* space, double dt) -{ - cpSpaceStep(space, dt); -} - -static void DrawDot(cpBody* body, void* unused) -{ - ChipmunkDebugDrawDot(3.5 / ChipmunkDebugDrawPointLineScale, cpBodyGetPosition(body), - RGBAColor(0xee / 255.0f, 0xe8 / 255.0f, 0xd5 / 255.0f, 1.0f)); -} - -static void draw(cpSpace* space) -{ - cpSpaceEachBody(space, DrawDot, NULL); - - // ChipmunkDebugDrawCollisionPoints(space); -} - -static cpShape* make_ball(cpFloat x, cpFloat y) -{ - cpBody* body = cpBodyNew(1.0, INFINITY); - cpBodySetPosition(body, cpv(x, y)); - - cpShape* shape = cpCircleShapeNew(body, 0.95, cpvzero); - cpShapeSetElasticity(shape, 0.0); - cpShapeSetFriction(shape, 0.0); - - return shape; -} - -static cpSpace* init(void) -{ - cpSpace* space = cpSpaceNew(); - cpSpaceSetIterations(space, 1); - - // The space will contain a very large number of similary sized objects. - // This is the perfect candidate for using the spatial hash. - // Generally you will never need to do this. - cpSpaceUseSpatialHash(space, 2.0, 10000); - - bodyCount = 0; - - cpBody* body; - cpShape* shape; - - for (int y = 0; y < image_height; y++) - { - for (int x = 0; x < image_width; x++) - { - if (!get_pixel(x, y)) - continue; - - cpFloat x_jitter = 0.05 * frand(); - cpFloat y_jitter = 0.05 * frand(); - - shape = make_ball(2 * (x - image_width / 2 + x_jitter), 2 * (image_height / 2 - y + y_jitter)); - cpSpaceAddBody(space, cpShapeGetBody(shape)); - cpSpaceAddShape(space, shape); - - bodyCount++; - } - } - - body = cpSpaceAddBody(space, cpBodyNew(1e9, INFINITY)); - cpBodySetPosition(body, cpv(-1000, -10)); - cpBodySetVelocity(body, cpv(400, 0)); - - shape = cpSpaceAddShape(space, cpCircleShapeNew(body, 8.0f, cpvzero)); - cpShapeSetElasticity(shape, 0.0); - cpShapeSetFriction(shape, 0.0); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - bodyCount++; - - return space; -} - -static void destroy(cpSpace* space) -{ - ChipmunkDemoFreeSpaceChildren(space); - cpSpaceFree(space); -} - -ChipmunkDemo LogoSmash = { - "Logo Smash", 1.0 / 60.0, init, update, draw, destroy, -}; diff --git a/tests/cpp-tests/Source/ChipmunkTestBed/demo/OneWay.cpp b/tests/cpp-tests/Source/ChipmunkTestBed/demo/OneWay.cpp deleted file mode 100644 index 63d103c92e00..000000000000 --- a/tests/cpp-tests/Source/ChipmunkTestBed/demo/OneWay.cpp +++ /dev/null @@ -1,118 +0,0 @@ -/* Copyright (c) 2007 Scott Lembcke - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk.h" -#include "ChipmunkDemo.h" - -enum CollisionTypes -{ - COLLISION_TYPE_ONE_WAY = 1, -}; - -typedef struct OneWayPlatform -{ - cpVect n; // direction objects may pass through -} OneWayPlatform; - -static OneWayPlatform platformInstance; - -static cpBool PreSolve(cpArbiter* arb, cpSpace* space, void* ignore) -{ - CP_ARBITER_GET_SHAPES(arb, a, b); - OneWayPlatform* platform = (OneWayPlatform*)cpShapeGetUserData(a); - - if (cpvdot(cpArbiterGetNormal(arb), platform->n) < 0) - { - return cpArbiterIgnore(arb); - } - - return cpTrue; -} - -static void update(cpSpace* space, double dt) -{ - cpSpaceStep(space, dt); -} - -static cpSpace* init(void) -{ - ChipmunkDemoMessageString = "One way platforms are trivial in Chipmunk using a very simple collision callback."; - - cpSpace* space = cpSpaceNew(); - cpSpaceSetIterations(space, 10); - cpSpaceSetGravity(space, cpv(0, -100)); - - cpBody *body, *staticBody = cpSpaceGetStaticBody(space); - cpShape* shape; - - // Create segments around the edge of the screen. - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, -240), cpv(-320, 240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(320, -240), cpv(320, 240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, -240), cpv(320, -240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - // Add our one way segment - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-160, -100), cpv(160, -100), 10.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetCollisionType(shape, COLLISION_TYPE_ONE_WAY); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - // We'll use the data pointer for the OneWayPlatform struct - platformInstance.n = cpv(0, 1); // let objects pass upwards - cpShapeSetUserData(shape, &platformInstance); - - // Add a ball to test it out - cpFloat radius = 15.0f; - body = cpSpaceAddBody(space, cpBodyNew(10.0f, cpMomentForCircle(10.0f, 0.0f, radius, cpvzero))); - cpBodySetPosition(body, cpv(0, -200)); - cpBodySetVelocity(body, cpv(0, 170)); - - shape = cpSpaceAddShape(space, cpCircleShapeNew(body, radius, cpvzero)); - cpShapeSetElasticity(shape, 0.0f); - cpShapeSetFriction(shape, 0.9f); - cpShapeSetCollisionType(shape, 2); - - cpCollisionHandler* handler = cpSpaceAddWildcardHandler(space, COLLISION_TYPE_ONE_WAY); - handler->preSolveFunc = PreSolve; - - return space; -} - -static void destroy(cpSpace* space) -{ - ChipmunkDemoFreeSpaceChildren(space); - cpSpaceFree(space); -} - -ChipmunkDemo OneWay = { - "One Way Platforms", 1.0 / 60.0, init, update, ChipmunkDemoDefaultDrawImpl, destroy, -}; diff --git a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Planet.cpp b/tests/cpp-tests/Source/ChipmunkTestBed/demo/Planet.cpp deleted file mode 100644 index cc7d4f8c6985..000000000000 --- a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Planet.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/* Copyright (c) 2007 Scott Lembcke - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk.h" -#include "ChipmunkDemo.h" - -static cpBody* planetBody; - -static cpFloat gravityStrength = 5.0e6f; - -static void update(cpSpace* space, double dt) -{ - cpSpaceStep(space, dt); -} - -static void planetGravityVelocityFunc(cpBody* body, cpVect gravity, cpFloat damping, cpFloat dt) -{ - // Gravitational acceleration is proportional to the inverse square of - // distance, and directed toward the origin. The central planet is assumed - // to be massive enough that it affects the satellites but not vice versa. - cpVect p = cpBodyGetPosition(body); - cpFloat sqdist = cpvlengthsq(p); - cpVect g = cpvmult(p, -gravityStrength / (sqdist * cpfsqrt(sqdist))); - - cpBodyUpdateVelocity(body, g, damping, dt); -} - -static cpVect rand_pos(cpFloat radius) -{ - cpVect v; - do - { - v = cpv(frand() * (640 - 2 * radius) - (320 - radius), frand() * (480 - 2 * radius) - (240 - radius)); - } while (cpvlength(v) < 85.0f); - - return v; -} - -static void add_box(cpSpace* space) -{ - const cpFloat size = 10.0f; - const cpFloat mass = 1.0f; - - cpVect verts[] = { - cpv(-size, -size), - cpv(-size, size), - cpv(size, size), - cpv(size, -size), - }; - - cpFloat radius = cpvlength(cpv(size, size)); - cpVect pos = rand_pos(radius); - - cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForPoly(mass, 4, verts, cpvzero, 0.0f))); - cpBodySetVelocityUpdateFunc(body, planetGravityVelocityFunc); - cpBodySetPosition(body, pos); - - // Set the box's velocity to put it into a circular orbit from its - // starting position. - cpFloat r = cpvlength(pos); - cpFloat v = cpfsqrt(gravityStrength / r) / r; - cpBodySetVelocity(body, cpvmult(cpvperp(pos), v)); - - // Set the box's angular velocity to match its orbital period and - // align its initial angle with its position. - cpBodySetAngularVelocity(body, v); - cpBodySetAngle(body, cpfatan2(pos.y, pos.x)); - - cpShape* shape = cpSpaceAddShape(space, cpPolyShapeNew(body, 4, verts, cpTransformIdentity, 0.0)); - cpShapeSetElasticity(shape, 0.0f); - cpShapeSetFriction(shape, 0.7f); -} - -static cpSpace* init(void) -{ - // Create a rouge body to control the planet manually. - cpSpace* space = cpSpaceNew(); - cpSpaceSetIterations(space, 20); - - planetBody = cpSpaceAddBody(space, cpBodyNewKinematic()); - cpBodySetAngularVelocity(planetBody, 0.2f); - - for (int i = 0; i < 30; i++) - { - add_box(space); - } - - cpShape* shape = cpSpaceAddShape(space, cpCircleShapeNew(planetBody, 70.0f, cpvzero)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - return space; -} - -static void destroy(cpSpace* space) -{ - ChipmunkDemoFreeSpaceChildren(space); - cpSpaceFree(space); -} - -ChipmunkDemo Planet = { - "Planet", 1.0 / 60.0, init, update, ChipmunkDemoDefaultDrawImpl, destroy, -}; diff --git a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Player.cpp b/tests/cpp-tests/Source/ChipmunkTestBed/demo/Player.cpp deleted file mode 100644 index 0ed950dc8077..000000000000 --- a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Player.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/* Copyright (c) 2007 Scott Lembcke - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk_private.h" -#include "ChipmunkDemo.h" - -#define PLAYER_VELOCITY 500.0 - -#define PLAYER_GROUND_ACCEL_TIME 0.1 -#define PLAYER_GROUND_ACCEL (PLAYER_VELOCITY / PLAYER_GROUND_ACCEL_TIME) - -#define PLAYER_AIR_ACCEL_TIME 0.25 -#define PLAYER_AIR_ACCEL (PLAYER_VELOCITY / PLAYER_AIR_ACCEL_TIME) - -#define JUMP_HEIGHT 50.0 -#define JUMP_BOOST_HEIGHT 55.0 -#define FALL_VELOCITY 900.0 -#define GRAVITY 2000.0 - -static cpBody* playerBody = NULL; -static cpShape* playerShape = NULL; - -static cpFloat remainingBoost = 0; -static cpBool grounded = cpFalse; -static cpBool lastJumpState = cpFalse; - -static void SelectPlayerGroundNormal(cpBody* body, cpArbiter* arb, cpVect* groundNormal) -{ - cpVect n = cpvneg(cpArbiterGetNormal(arb)); - - if (n.y > groundNormal->y) - { - (*groundNormal) = n; - } -} - -static void playerUpdateVelocity(cpBody* body, cpVect gravity, cpFloat damping, cpFloat dt) -{ - int jumpState = (ChipmunkDemoKeyboard.y > 0.0f); - - // Grab the grounding normal from last frame - cpVect groundNormal = cpvzero; - cpBodyEachArbiter(playerBody, (cpBodyArbiterIteratorFunc)SelectPlayerGroundNormal, &groundNormal); - - grounded = (groundNormal.y > 0.0); - if (groundNormal.y < 0.0f) - remainingBoost = 0.0f; - - // Do a normal-ish update - cpBool boost = (jumpState && remainingBoost > 0.0f); - cpVect g = (boost ? cpvzero : gravity); - cpBodyUpdateVelocity(body, g, damping, dt); - - // Target horizontal speed for air/ground control - cpFloat target_vx = PLAYER_VELOCITY * ChipmunkDemoKeyboard.x; - - // Update the surface velocity and friction - // Note that the "feet" move in the opposite direction of the player. - cpVect surface_v = cpv(-target_vx, 0.0); - playerShape->surfaceV = surface_v; - playerShape->u = (grounded ? PLAYER_GROUND_ACCEL / GRAVITY : 0.0); - - // Apply air control if not grounded - if (!grounded) - { - // Smoothly accelerate the velocity - playerBody->v.x = cpflerpconst(playerBody->v.x, target_vx, PLAYER_AIR_ACCEL * dt); - } - - body->v.y = cpfclamp(body->v.y, -FALL_VELOCITY, INFINITY); -} - -static void update(cpSpace* space, double dt) -{ - int jumpState = (ChipmunkDemoKeyboard.y > 0.0f); - - // If the jump key was just pressed this frame, jump! - if (jumpState && !lastJumpState && grounded) - { - cpFloat jump_v = cpfsqrt(2.0 * JUMP_HEIGHT * GRAVITY); - playerBody->v = cpvadd(playerBody->v, cpv(0.0, jump_v)); - - remainingBoost = JUMP_BOOST_HEIGHT / jump_v; - } - - // Step the space - cpSpaceStep(space, dt); - - remainingBoost -= dt; - lastJumpState = jumpState; -} - -static cpSpace* init(void) -{ - cpSpace* space = cpSpaceNew(); - space->iterations = 10; - space->gravity = cpv(0, -GRAVITY); - // space->sleepTimeThreshold = 1000; - - cpBody *body, *staticBody = cpSpaceGetStaticBody(space); - cpShape* shape; - - // Create segments around the edge of the screen. - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, -240), cpv(-320, 240), 0.0f)); - shape->e = 1.0f; - shape->u = 1.0f; - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(320, -240), cpv(320, 240), 0.0f)); - shape->e = 1.0f; - shape->u = 1.0f; - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, -240), cpv(320, -240), 0.0f)); - shape->e = 1.0f; - shape->u = 1.0f; - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, 240), cpv(320, 240), 0.0f)); - shape->e = 1.0f; - shape->u = 1.0f; - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - // Set up the player - body = cpSpaceAddBody(space, cpBodyNew(1.0f, INFINITY)); - body->p = cpv(0, -200); - body->velocity_func = playerUpdateVelocity; - playerBody = body; - - shape = cpSpaceAddShape(space, cpBoxShapeNew2(body, cpBBNew(-15.0, -27.5, 15.0, 27.5), 1.0)); - // shape = cpSpaceAddShape(space, cpSegmentShapeNew(playerBody, cpvzero, cpv(0, radius), radius)); - shape->e = 0.0f; - shape->u = 0.0f; - shape->type = 1; - playerShape = shape; - - // Add some boxes to jump on - for (int i = 0; i < 6; i++) - { - for (int j = 0; j < 3; j++) - { - body = cpSpaceAddBody(space, cpBodyNew(4.0f, INFINITY)); - body->p = cpv(100 + j * 60, -200 + i * 60); - - shape = cpSpaceAddShape(space, cpBoxShapeNew(body, 50, 50, 0.0)); - shape->e = 0.0f; - shape->u = 0.7f; - } - } - - return space; -} - -static void destroy(cpSpace* space) -{ - ChipmunkDemoFreeSpaceChildren(space); - cpSpaceFree(space); -} - -ChipmunkDemo PlatformerPlayer = { - "Platformer Player Controls", 1.0 / 180.0, init, update, ChipmunkDemoDefaultDrawImpl, destroy, -}; diff --git a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Plink.cpp b/tests/cpp-tests/Source/ChipmunkTestBed/demo/Plink.cpp deleted file mode 100644 index dec53e8bc4e4..000000000000 --- a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Plink.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/* Copyright (c) 2007 Scott Lembcke - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk.h" -#include "ChipmunkDemo.h" - -static cpFloat pentagon_mass = 0.0f; -static cpFloat pentagon_moment = 0.0f; - -// Iterate over all of the bodies and reset the ones that have fallen offscreen. -static void eachBody(cpBody* body, void* unused) -{ - cpVect pos = cpBodyGetPosition(body); - if (pos.y < -260) - { - cpFloat x = rand() / (cpFloat)RAND_MAX * 640 - 320; - cpBodySetPosition(body, cpv(x, 260)); - } -} - -static void update(cpSpace* space, double dt) -{ - if (ChipmunkDemoRightDown) - { - ChipmunkDemoRightDown = cpFalse; - cpShape* nearest = cpSpacePointQueryNearest(space, ChipmunkDemoMouse, 0.0, GRAB_FILTER, NULL); - if (nearest) - { - cpBody* body = cpShapeGetBody(nearest); - if (cpBodyGetType(body) == CP_BODY_TYPE_STATIC) - { - cpBodySetType(body, CP_BODY_TYPE_DYNAMIC); - cpBodySetMass(body, pentagon_mass); - cpBodySetMoment(body, pentagon_moment); - } - else if (cpBodyGetType(body) == CP_BODY_TYPE_DYNAMIC) - { - cpBodySetType(body, CP_BODY_TYPE_STATIC); - } - } - } - - cpSpaceEachBody(space, &eachBody, NULL); - cpSpaceStep(space, dt); -} - -#define NUM_VERTS 5 - -static cpSpace* init(void) -{ - ChipmunkDemoMessageString = "Right click to make pentagons static/dynamic."; - - cpSpace* space = cpSpaceNew(); - cpSpaceSetIterations(space, 5); - cpSpaceSetGravity(space, cpv(0, -100)); - - cpBody *body, *staticBody = cpSpaceGetStaticBody(space); - cpShape* shape; - - // Vertexes for a triangle shape. - cpVect tris[] = { - cpv(-15, -15), - cpv(0, 10), - cpv(15, -15), - }; - - // Create the static triangles. - for (int i = 0; i < 10; i++) - { - for (int j = 0; j < 6; j++) - { - cpFloat stagger = (j % 2) * 40; - cpVect offset = cpv(i * 80 - 360 + stagger, j * 70 - 200); - shape = cpSpaceAddShape(space, cpPolyShapeNew(staticBody, 3, tris, cpTransformTranslate(offset), 0.0)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - } - } - - // Create vertexes for a pentagon shape. - cpVect verts[NUM_VERTS]; - for (int i = 0; i < NUM_VERTS; i++) - { - cpFloat angle = -2.0f * CP_PI * i / ((cpFloat)NUM_VERTS); - verts[i] = cpv(10 * cos(angle), 10 * sin(angle)); - } - - pentagon_mass = 1.0; - pentagon_moment = cpMomentForPoly(1.0f, NUM_VERTS, verts, cpvzero, 0.0f); - - // Add lots of pentagons. - for (int i = 0; i < 300; i++) - { - body = cpSpaceAddBody(space, cpBodyNew(pentagon_mass, pentagon_moment)); - cpFloat x = rand() / (cpFloat)RAND_MAX * 640 - 320; - cpBodySetPosition(body, cpv(x, 350)); - - shape = cpSpaceAddShape(space, cpPolyShapeNew(body, NUM_VERTS, verts, cpTransformIdentity, 0.0)); - cpShapeSetElasticity(shape, 0.0f); - cpShapeSetFriction(shape, 0.4f); - } - - return space; -} - -static void destroy(cpSpace* space) -{ - ChipmunkDemoFreeSpaceChildren(space); - cpSpaceFree(space); -} - -ChipmunkDemo Plink = { - "Plink", 1.0 / 60.0, init, update, ChipmunkDemoDefaultDrawImpl, destroy, -}; diff --git a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Pump.cpp b/tests/cpp-tests/Source/ChipmunkTestBed/demo/Pump.cpp deleted file mode 100644 index b059b52df0a7..000000000000 --- a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Pump.cpp +++ /dev/null @@ -1,184 +0,0 @@ -/* Copyright (c) 2007 Scott Lembcke - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk.h" -#include "ChipmunkDemo.h" - -static cpConstraint* motor; - -#define numBalls 5 -static cpBody* balls[numBalls]; - -static void update(cpSpace* space, double dt) -{ - cpFloat coef = (2.0f + ChipmunkDemoKeyboard.y) / 3.0f; - cpFloat rate = ChipmunkDemoKeyboard.x * 30.0f * coef; - - cpSimpleMotorSetRate(motor, rate); - cpConstraintSetMaxForce(motor, rate ? 1000000.0f : 0.0f); - - cpSpaceStep(space, dt); - - for (int i = 0; i < numBalls; i++) - { - cpBody* ball = balls[i]; - cpVect pos = cpBodyGetPosition(ball); - - if (pos.x > 320.0f) - { - cpBodySetVelocity(ball, cpvzero); - cpBodySetPosition(ball, cpv(-224.0f, 200.0f)); - } - } -} - -static cpBody* add_ball(cpSpace* space, cpVect pos) -{ - cpBody* body = cpSpaceAddBody(space, cpBodyNew(1.0f, cpMomentForCircle(1.0f, 30, 0, cpvzero))); - cpBodySetPosition(body, pos); - - cpShape* shape = cpSpaceAddShape(space, cpCircleShapeNew(body, 30, cpvzero)); - cpShapeSetElasticity(shape, 0.0f); - cpShapeSetFriction(shape, 0.5f); - - return body; -} - -static cpSpace* init(void) -{ - ChipmunkDemoMessageString = "Use the arrow keys to control the machine."; - - cpSpace* space = cpSpaceNew(); - cpSpaceSetGravity(space, cpv(0, -600)); - - cpBody* staticBody = cpSpaceGetStaticBody(space); - cpShape* shape; - - // beveling all of the line segments slightly helps prevent things from getting stuck on cracks - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-256, 16), cpv(-256, 300), 2.0f)); - cpShapeSetElasticity(shape, 0.0f); - cpShapeSetFriction(shape, 0.5f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-256, 16), cpv(-192, 0), 2.0f)); - cpShapeSetElasticity(shape, 0.0f); - cpShapeSetFriction(shape, 0.5f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-192, 0), cpv(-192, -64), 2.0f)); - cpShapeSetElasticity(shape, 0.0f); - cpShapeSetFriction(shape, 0.5f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-128, -64), cpv(-128, 144), 2.0f)); - cpShapeSetElasticity(shape, 0.0f); - cpShapeSetFriction(shape, 0.5f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-192, 80), cpv(-192, 176), 2.0f)); - cpShapeSetElasticity(shape, 0.0f); - cpShapeSetFriction(shape, 0.5f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-192, 176), cpv(-128, 240), 2.0f)); - cpShapeSetElasticity(shape, 0.0f); - cpShapeSetFriction(shape, 0.5f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-128, 144), cpv(192, 64), 2.0f)); - cpShapeSetElasticity(shape, 0.0f); - cpShapeSetFriction(shape, 0.5f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - cpVect verts[] = { - cpv(-30, -80), - cpv(-30, 80), - cpv(30, 64), - cpv(30, -80), - }; - - cpBody* plunger = cpSpaceAddBody(space, cpBodyNew(1.0f, INFINITY)); - cpBodySetPosition(plunger, cpv(-160, -80)); - - shape = cpSpaceAddShape(space, cpPolyShapeNew(plunger, 4, verts, cpTransformIdentity, 0.0)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 0.5f); - cpShapeSetFilter(shape, cpShapeFilterNew(CP_NO_GROUP, 1, 1)); - - // add balls to hopper - for (int i = 0; i < numBalls; i++) - balls[i] = add_ball(space, cpv(-224 + i, 80 + 64 * i)); - - // add small gear - cpBody* smallGear = cpSpaceAddBody(space, cpBodyNew(10.0f, cpMomentForCircle(10.0f, 80, 0, cpvzero))); - cpBodySetPosition(smallGear, cpv(-160, -160)); - cpBodySetAngle(smallGear, -CP_PI / 2.0f); - - shape = cpSpaceAddShape(space, cpCircleShapeNew(smallGear, 80.0f, cpvzero)); - cpShapeSetFilter(shape, CP_SHAPE_FILTER_NONE); - - cpSpaceAddConstraint(space, cpPivotJointNew2(staticBody, smallGear, cpv(-160, -160), cpvzero)); - - // add big gear - cpBody* bigGear = cpSpaceAddBody(space, cpBodyNew(40.0f, cpMomentForCircle(40.0f, 160, 0, cpvzero))); - cpBodySetPosition(bigGear, cpv(80, -160)); - cpBodySetAngle(bigGear, CP_PI / 2.0f); - - shape = cpSpaceAddShape(space, cpCircleShapeNew(bigGear, 160.0f, cpvzero)); - cpShapeSetFilter(shape, CP_SHAPE_FILTER_NONE); - - cpSpaceAddConstraint(space, cpPivotJointNew2(staticBody, bigGear, cpv(80, -160), cpvzero)); - - // connect the plunger to the small gear. - cpSpaceAddConstraint(space, cpPinJointNew(smallGear, plunger, cpv(80, 0), cpv(0, 0))); - // connect the gears. - cpSpaceAddConstraint(space, cpGearJointNew(smallGear, bigGear, -CP_PI / 2.0f, -2.0f)); - - // feeder mechanism - cpFloat bottom = -300.0f; - cpFloat top = 32.0f; - cpBody* feeder = - cpSpaceAddBody(space, cpBodyNew(1.0f, cpMomentForSegment(1.0f, cpv(-224.0f, bottom), cpv(-224.0f, top), 0.0f))); - cpBodySetPosition(feeder, cpv(-224, (bottom + top) / 2.0f)); - - cpFloat len = top - bottom; - shape = cpSpaceAddShape(space, cpSegmentShapeNew(feeder, cpv(0.0f, len / 2.0f), cpv(0.0f, -len / 2.0f), 20.0f)); - cpShapeSetFilter(shape, GRAB_FILTER); - - cpSpaceAddConstraint(space, cpPivotJointNew2(staticBody, feeder, cpv(-224.0f, bottom), cpv(0.0f, -len / 2.0f))); - cpVect anchr = cpBodyWorldToLocal(feeder, cpv(-224.0f, -160.0f)); - cpSpaceAddConstraint(space, cpPinJointNew(feeder, smallGear, anchr, cpv(0.0f, 80.0f))); - - // motorize the second gear - motor = cpSpaceAddConstraint(space, cpSimpleMotorNew(staticBody, bigGear, 3.0f)); - - return space; -} - -static void destroy(cpSpace* space) -{ - ChipmunkDemoFreeSpaceChildren(space); - cpSpaceFree(space); -} - -ChipmunkDemo Pump = { - "Pump", 1.0 / 120.0, init, update, ChipmunkDemoDefaultDrawImpl, destroy, -}; diff --git a/tests/cpp-tests/Source/ChipmunkTestBed/demo/PyramidStack.cpp b/tests/cpp-tests/Source/ChipmunkTestBed/demo/PyramidStack.cpp deleted file mode 100644 index 520affdab40e..000000000000 --- a/tests/cpp-tests/Source/ChipmunkTestBed/demo/PyramidStack.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* Copyright (c) 2007 Scott Lembcke - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk.h" -#include "chipmunk/chipmunk_unsafe.h" -#include "ChipmunkDemo.h" - -static void update(cpSpace* space, double dt) -{ - cpSpaceStep(space, dt); -} - -static cpSpace* init(void) -{ - cpSpace* space = cpSpaceNew(); - cpSpaceSetIterations(space, 30); - cpSpaceSetGravity(space, cpv(0, -100)); - cpSpaceSetSleepTimeThreshold(space, 0.5f); - cpSpaceSetCollisionSlop(space, 0.5f); - - cpBody *body, *staticBody = cpSpaceGetStaticBody(space); - cpShape* shape; - - // Create segments around the edge of the screen. - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, -240), cpv(-320, 240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(320, -240), cpv(320, 240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, -240), cpv(320, -240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - // Add lots of boxes. - for (int i = 0; i < 14; i++) - { - for (int j = 0; j <= i; j++) - { - body = cpSpaceAddBody(space, cpBodyNew(1.0f, cpMomentForBox(1.0f, 30.0f, 30.0f))); - cpBodySetPosition(body, cpv(j * 32 - i * 16, 300 - i * 32)); - - shape = cpSpaceAddShape(space, cpBoxShapeNew(body, 30.0f, 30.0f, 0.5f)); - cpShapeSetElasticity(shape, 0.0f); - cpShapeSetFriction(shape, 0.8f); - } - } - - // Add a ball to make things more interesting - cpFloat radius = 15.0f; - body = cpSpaceAddBody(space, cpBodyNew(10.0f, cpMomentForCircle(10.0f, 0.0f, radius, cpvzero))); - cpBodySetPosition(body, cpv(0, -200 + radius + 5)); - - shape = cpSpaceAddShape(space, cpCircleShapeNew(body, radius, cpvzero)); - cpShapeSetElasticity(shape, 0.0f); - cpShapeSetFriction(shape, 0.9f); - - return space; -} - -static void destroy(cpSpace* space) -{ - ChipmunkDemoFreeSpaceChildren(space); - cpSpaceFree(space); -} - -ChipmunkDemo PyramidStack = { - "Pyramid Stack", 1.0 / 180.0, init, update, ChipmunkDemoDefaultDrawImpl, destroy, -}; diff --git a/tests/cpp-tests/Source/ChipmunkTestBed/demo/PyramidTopple.cpp b/tests/cpp-tests/Source/ChipmunkTestBed/demo/PyramidTopple.cpp deleted file mode 100644 index a45649901644..000000000000 --- a/tests/cpp-tests/Source/ChipmunkTestBed/demo/PyramidTopple.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* Copyright (c) 2007 Scott Lembcke - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk.h" -#include "ChipmunkDemo.h" - -static void update(cpSpace* space, double dt) -{ - cpSpaceStep(space, dt); -} - -#define WIDTH 4.0f -#define HEIGHT 30.0f - -static void add_domino(cpSpace* space, cpVect pos, cpBool flipped) -{ - cpFloat mass = 1.0f; - cpFloat radius = 0.5f; - cpFloat moment = cpMomentForBox(mass, WIDTH, HEIGHT); - - cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, moment)); - cpBodySetPosition(body, pos); - - cpShape* shape = (flipped ? cpBoxShapeNew(body, HEIGHT, WIDTH, 0.0) - : cpBoxShapeNew(body, WIDTH - radius * 2.0f, HEIGHT, radius)); - cpSpaceAddShape(space, shape); - cpShapeSetElasticity(shape, 0.0f); - cpShapeSetFriction(shape, 0.6f); -} - -static cpSpace* init(void) -{ - cpSpace* space = cpSpaceNew(); - cpSpaceSetIterations(space, 30); - cpSpaceSetGravity(space, cpv(0, -300)); - cpSpaceSetSleepTimeThreshold(space, 0.5f); - cpSpaceSetCollisionSlop(space, 0.5f); - - // Add a floor. - cpShape* shape = - cpSpaceAddShape(space, cpSegmentShapeNew(cpSpaceGetStaticBody(space), cpv(-600, -240), cpv(600, -240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - // Add the dominoes. - int n = 12; - for (int i = 0; i < n; i++) - { - for (int j = 0; j < (n - i); j++) - { - cpVect offset = - cpv((j - (n - 1 - i) * 0.5f) * 1.5f * HEIGHT, (i + 0.5f) * (HEIGHT + 2 * WIDTH) - WIDTH - 240); - add_domino(space, offset, cpFalse); - add_domino(space, cpvadd(offset, cpv(0, (HEIGHT + WIDTH) / 2.0f)), cpTrue); - - if (j == 0) - { - add_domino(space, cpvadd(offset, cpv(0.5f * (WIDTH - HEIGHT), HEIGHT + WIDTH)), cpFalse); - } - - if (j != n - i - 1) - { - add_domino(space, cpvadd(offset, cpv(HEIGHT * 0.75f, (HEIGHT + 3 * WIDTH) / 2.0f)), cpTrue); - } - else - { - add_domino(space, cpvadd(offset, cpv(0.5f * (HEIGHT - WIDTH), HEIGHT + WIDTH)), cpFalse); - } - } - } - - return space; -} - -static void destroy(cpSpace* space) -{ - ChipmunkDemoFreeSpaceChildren(space); - cpSpaceFree(space); -} - -ChipmunkDemo PyramidTopple = { - "Pyramid Topple", 1.0 / 180.0, init, update, ChipmunkDemoDefaultDrawImpl, destroy, -}; diff --git a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Query.cpp b/tests/cpp-tests/Source/ChipmunkTestBed/demo/Query.cpp deleted file mode 100644 index c030b1308d5f..000000000000 --- a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Query.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/* Copyright (c) 2007 Scott Lembcke - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk.h" -#include "ChipmunkDemo.h" - -static cpVect QUERY_START = {0, 0}; - -static void update(cpSpace* space, double dt) -{ - cpSpaceStep(space, dt); - - if (ChipmunkDemoRightClick) - { - QUERY_START = ChipmunkDemoMouse; - } - - cpVect start = QUERY_START; - cpVect end = ChipmunkDemoMouse; - cpFloat radius = 10.0; - ChipmunkDebugDrawSegment(start, end, RGBAColor(0, 1, 0, 1)); - - ChipmunkDemoPrintString("Query: Dist(%f) Point(%5.2f, %5.2f),\n", cpvdist(start, end), end.x, end.y); - - cpSegmentQueryInfo segInfo = {0}; - if (cpSpaceSegmentQueryFirst(space, start, end, radius, CP_SHAPE_FILTER_ALL, &segInfo)) - { - cpVect point = segInfo.point; - cpVect n = segInfo.normal; - - // Draw blue over the occluded part of the query - ChipmunkDebugDrawSegment(cpvlerp(start, end, segInfo.alpha), end, RGBAColor(0, 0, 1, 1)); - - // Draw a little red surface normal - ChipmunkDebugDrawSegment(point, cpvadd(point, cpvmult(n, 16)), RGBAColor(1, 0, 0, 1)); - - // Draw a little red dot on the hit point. - ChipmunkDebugDrawDot(3, point, RGBAColor(1, 0, 0, 1)); - - ChipmunkDemoPrintString("Segment Query: Dist(%f) Normal(%5.2f, %5.2f)\n", segInfo.alpha * cpvdist(start, end), - n.x, n.y); - } - else - { - ChipmunkDemoPrintString("Segment Query (None)\n"); - } - - // Draw a fat green line over the unoccluded part of the query - ChipmunkDebugDrawFatSegment(start, cpvlerp(start, end, segInfo.alpha), radius, RGBAColor(0, 1, 0, 1), - LAColor(0, 0)); - - cpPointQueryInfo nearestInfo = {0}; - cpSpacePointQueryNearest(space, ChipmunkDemoMouse, 100.0, CP_SHAPE_FILTER_ALL, &nearestInfo); - if (nearestInfo.shape) - { - // Draw a grey line to the closest shape. - ChipmunkDebugDrawDot(3, ChipmunkDemoMouse, RGBAColor(0.5, 0.5, 0.5, 1.0)); - ChipmunkDebugDrawSegment(ChipmunkDemoMouse, nearestInfo.point, RGBAColor(0.5, 0.5, 0.5, 1.0)); - - // Draw a red bounding box around the shape under the mouse. - if (nearestInfo.distance < 0) - ChipmunkDebugDrawBB(cpShapeGetBB(nearestInfo.shape), RGBAColor(1, 0, 0, 1)); - } -} - -static cpSpace* init(void) -{ - QUERY_START = cpvzero; - - cpSpace* space = cpSpaceNew(); - cpSpaceSetIterations(space, 5); - - { // add a fat segment - cpFloat mass = 1.0f; - cpFloat length = 100.0f; - cpVect a = cpv(-length / 2.0f, 0.0f), b = cpv(length / 2.0f, 0.0f); - - cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForSegment(mass, a, b, 0.0f))); - cpBodySetPosition(body, cpv(0.0f, 100.0f)); - - cpSpaceAddShape(space, cpSegmentShapeNew(body, a, b, 20.0f)); - } - - { // add a static segment - cpSpaceAddShape(space, cpSegmentShapeNew(cpSpaceGetStaticBody(space), cpv(0, 300), cpv(300, 0), 0.0f)); - } - - { // add a pentagon - cpFloat mass = 1.0f; - - cpVect verts[5]; - for (int i = 0; i < 5; i++) - { - cpFloat angle = -2.0f * CP_PI * i / ((cpFloat)5); - verts[i] = cpv(30 * cos(angle), 30 * sin(angle)); - } - - cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForPoly(mass, 5, verts, cpvzero, 0.0f))); - cpBodySetPosition(body, cpv(50.0f, 30.0f)); - - cpSpaceAddShape(space, cpPolyShapeNew(body, 5, verts, cpTransformIdentity, 10.0f)); - } - - { // add a circle - cpFloat mass = 1.0f; - cpFloat r = 20.0f; - - cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForCircle(mass, 0.0f, r, cpvzero))); - cpBodySetPosition(body, cpv(100.0f, 100.0f)); - - cpSpaceAddShape(space, cpCircleShapeNew(body, r, cpvzero)); - } - - return space; -} - -static void destroy(cpSpace* space) -{ - ChipmunkDemoFreeSpaceChildren(space); - cpSpaceFree(space); -} - -ChipmunkDemo Query = { - "Segment Query", 1.0 / 60.0, init, update, ChipmunkDemoDefaultDrawImpl, destroy, -}; diff --git a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Shatter.cpp b/tests/cpp-tests/Source/ChipmunkTestBed/demo/Shatter.cpp deleted file mode 100644 index d013e7e6335d..000000000000 --- a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Shatter.cpp +++ /dev/null @@ -1,257 +0,0 @@ -/* Copyright (c) 2007 Scott Lembcke - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include - -#include "chipmunk/chipmunk.h" - -#include "ChipmunkDemo.h" - -#define DENSITY (1.0 / 10000.0) - -#define MAX_VERTEXES_PER_VORONOI 16 - -struct WorleyContex -{ - uint32_t seed; - cpFloat cellSize; - int width, height; - cpBB bb; - cpVect focus; -}; - -static inline cpVect HashVect(uint32_t x, uint32_t y, uint32_t seed) -{ - // cpFloat border = 0.21f; - cpFloat border = 0.05f; - uint32_t h = (x * 1640531513 ^ y * 2654435789) + seed; - - return cpv(cpflerp(border, 1.0f - border, (cpFloat)(h & 0xFFFF) / (cpFloat)0xFFFF), - cpflerp(border, 1.0f - border, (cpFloat)((h >> 16) & 0xFFFF) / (cpFloat)0xFFFF)); -} - -static cpVect WorleyPoint(int i, int j, struct WorleyContex* context) -{ - cpFloat size = context->cellSize; - int width = context->width; - int height = context->height; - cpBB bb = context->bb; - - // cpVect fv = cpv(0.5, 0.5); - cpVect fv = HashVect(i, j, context->seed); - - return cpv(cpflerp(bb.l, bb.r, 0.5f) + size * (i + fv.x - width * 0.5f), - cpflerp(bb.b, bb.t, 0.5f) + size * (j + fv.y - height * 0.5f)); -} - -static int ClipCell(cpShape* shape, - cpVect center, - int i, - int j, - struct WorleyContex* context, - cpVect* verts, - cpVect* clipped, - int count) -{ - cpVect other = WorleyPoint(i, j, context); - // printf(" other %dx%d: (% 5.2f, % 5.2f) ", i, j, other.x, other.y); - if (cpShapePointQuery(shape, other, NULL) > 0.0f) - { - // printf("excluded\n"); - memcpy(clipped, verts, count * sizeof(cpVect)); - return count; - } - else - { - // printf("clipped\n"); - } - - cpVect n = cpvsub(other, center); - cpFloat dist = cpvdot(n, cpvlerp(center, other, 0.5f)); - - int clipped_count = 0; - for (int j = 0, i = count - 1; j < count; i = j, j++) - { - cpVect a = verts[i]; - cpFloat a_dist = cpvdot(a, n) - dist; - - if (a_dist <= 0.0) - { - clipped[clipped_count] = a; - clipped_count++; - } - - cpVect b = verts[j]; - cpFloat b_dist = cpvdot(b, n) - dist; - - if (a_dist * b_dist < 0.0f) - { - cpFloat t = cpfabs(a_dist) / (cpfabs(a_dist) + cpfabs(b_dist)); - - clipped[clipped_count] = cpvlerp(a, b, t); - clipped_count++; - } - } - - return clipped_count; -} - -static void ShatterCell(cpSpace* space, - cpShape* shape, - cpVect cell, - int cell_i, - int cell_j, - struct WorleyContex* context) -{ - // printf("cell %dx%d: (% 5.2f, % 5.2f)\n", cell_i, cell_j, cell.x, cell.y); - - cpBody* body = cpShapeGetBody(shape); - - cpVect* ping = (cpVect*)alloca(MAX_VERTEXES_PER_VORONOI * sizeof(cpVect)); - cpVect* pong = (cpVect*)alloca(MAX_VERTEXES_PER_VORONOI * sizeof(cpVect)); - - int count = cpPolyShapeGetCount(shape); - count = (count > MAX_VERTEXES_PER_VORONOI ? MAX_VERTEXES_PER_VORONOI : count); - - for (int i = 0; i < count; i++) - { - ping[i] = cpBodyLocalToWorld(body, cpPolyShapeGetVert(shape, i)); - } - - for (int i = 0; i < context->width; i++) - { - for (int j = 0; j < context->height; j++) - { - if (!(i == cell_i && j == cell_j) && cpShapePointQuery(shape, cell, NULL) < 0.0f) - { - count = ClipCell(shape, cell, i, j, context, ping, pong, count); - memcpy(ping, pong, count * sizeof(cpVect)); - } - } - } - - cpVect centroid = cpCentroidForPoly(count, ping); - cpFloat mass = cpAreaForPoly(count, ping, 0.0f) * DENSITY; - cpFloat moment = cpMomentForPoly(mass, count, ping, cpvneg(centroid), 0.0f); - - cpBody* new_body = cpSpaceAddBody(space, cpBodyNew(mass, moment)); - cpBodySetPosition(new_body, centroid); - cpBodySetVelocity(new_body, cpBodyGetVelocityAtWorldPoint(body, centroid)); - cpBodySetAngularVelocity(new_body, cpBodyGetAngularVelocity(body)); - - cpTransform transform = cpTransformTranslate(cpvneg(centroid)); - cpShape* new_shape = cpSpaceAddShape(space, cpPolyShapeNew(new_body, count, ping, transform, 0.0)); - // Copy whatever properties you have set on the original shape that are important - cpShapeSetFriction(new_shape, cpShapeGetFriction(shape)); -} - -static void ShatterShape(cpSpace* space, cpShape* shape, cpFloat cellSize, cpVect focus) -{ - cpSpaceRemoveShape(space, shape); - cpSpaceRemoveBody(space, cpShapeGetBody(shape)); - - cpBB bb = cpShapeGetBB(shape); - int width = (int)((bb.r - bb.l) / cellSize) + 1; - int height = (int)((bb.t - bb.b) / cellSize) + 1; - // printf("Splitting as %dx%d\n", width, height); - struct WorleyContex context = {(uint32_t)rand(), cellSize, width, height, bb, focus}; - - for (int i = 0; i < context.width; i++) - { - for (int j = 0; j < context.height; j++) - { - cpVect cell = WorleyPoint(i, j, &context); - if (cpShapePointQuery(shape, cell, NULL) < 0.0f) - { - ShatterCell(space, shape, cell, i, j, &context); - } - } - } - - cpBodyFree(cpShapeGetBody(shape)); - cpShapeFree(shape); -} - -static void update(cpSpace* space, double dt) -{ - cpSpaceStep(space, dt); - - if (ChipmunkDemoRightDown) - { - cpPointQueryInfo info; - if (cpSpacePointQueryNearest(space, ChipmunkDemoMouse, 0, GRAB_FILTER, &info)) - { - cpBB bb = cpShapeGetBB(info.shape); - cpFloat cell_size = cpfmax(bb.r - bb.l, bb.t - bb.b) / 5.0f; - if (cell_size > 5.0f) - { - ShatterShape(space, (cpShape*)info.shape, cell_size, ChipmunkDemoMouse); - } - else - { - // printf("Too small to splinter %f\n", cell_size); - } - } - ChipmunkDemoRightDown = false; - } -} - -static cpSpace* init(void) -{ - ChipmunkDemoMessageString = "Right click something to shatter it."; - - cpSpace* space = cpSpaceNew(); - cpSpaceSetIterations(space, 30); - cpSpaceSetGravity(space, cpv(0, -500)); - cpSpaceSetSleepTimeThreshold(space, 0.5f); - cpSpaceSetCollisionSlop(space, 0.5f); - - cpBody *body, *staticBody = cpSpaceGetStaticBody(space); - cpShape* shape; - - // Create segments around the edge of the screen. - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-1000, -240), cpv(1000, -240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - cpFloat width = 200.0f; - cpFloat height = 200.0f; - cpFloat mass = width * height * DENSITY; - cpFloat moment = cpMomentForBox(mass, width, height); - - body = cpSpaceAddBody(space, cpBodyNew(mass, moment)); - - shape = cpSpaceAddShape(space, cpBoxShapeNew(body, width, height, 0.0)); - cpShapeSetFriction(shape, 0.6f); - - return space; -} - -static void destroy(cpSpace* space) -{ - ChipmunkDemoFreeSpaceChildren(space); - cpSpaceFree(space); -} - -ChipmunkDemo Shatter = { - "Shatter.", 1.0f / 60.0f, init, update, ChipmunkDemoDefaultDrawImpl, destroy, -}; diff --git a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Slice.cpp b/tests/cpp-tests/Source/ChipmunkTestBed/demo/Slice.cpp deleted file mode 100644 index 445452e17a6a..000000000000 --- a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Slice.cpp +++ /dev/null @@ -1,188 +0,0 @@ -/* Copyright (c) 2007 Scott Lembcke - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk.h" - -#include "ChipmunkDemo.h" - -#define DENSITY (1.0 / 10000.0) - -static void ClipPoly(cpSpace* space, cpShape* shape, cpVect n, cpFloat dist) -{ - cpBody* body = cpShapeGetBody(shape); - - int count = cpPolyShapeGetCount(shape); - int clippedCount = 0; - - cpVect* clipped = (cpVect*)alloca((count + 1) * sizeof(cpVect)); - - for (int i = 0, j = count - 1; i < count; j = i, i++) - { - cpVect a = cpBodyLocalToWorld(body, cpPolyShapeGetVert(shape, j)); - cpFloat a_dist = cpvdot(a, n) - dist; - - if (a_dist < 0.0) - { - clipped[clippedCount] = a; - clippedCount++; - } - - cpVect b = cpBodyLocalToWorld(body, cpPolyShapeGetVert(shape, i)); - cpFloat b_dist = cpvdot(b, n) - dist; - - if (a_dist * b_dist < 0.0f) - { - cpFloat t = cpfabs(a_dist) / (cpfabs(a_dist) + cpfabs(b_dist)); - - clipped[clippedCount] = cpvlerp(a, b, t); - clippedCount++; - } - } - - cpVect centroid = cpCentroidForPoly(clippedCount, clipped); - cpFloat mass = cpAreaForPoly(clippedCount, clipped, 0.0f) * DENSITY; - cpFloat moment = cpMomentForPoly(mass, clippedCount, clipped, cpvneg(centroid), 0.0f); - - cpBody* new_body = cpSpaceAddBody(space, cpBodyNew(mass, moment)); - cpBodySetPosition(new_body, centroid); - cpBodySetVelocity(new_body, cpBodyGetVelocityAtWorldPoint(body, centroid)); - cpBodySetAngularVelocity(new_body, cpBodyGetAngularVelocity(body)); - - cpTransform transform = cpTransformTranslate(cpvneg(centroid)); - cpShape* new_shape = cpSpaceAddShape(space, cpPolyShapeNew(new_body, clippedCount, clipped, transform, 0.0)); - // Copy whatever properties you have set on the original shape that are important - cpShapeSetFriction(new_shape, cpShapeGetFriction(shape)); -} - -// Context structs are annoying, use blocks or closures instead if your compiler supports them. -struct SliceContext -{ - cpVect a, b; - cpSpace* space; -}; - -static void SliceShapePostStep(cpSpace* space, cpShape* shape, struct SliceContext* context) -{ - cpVect a = context->a; - cpVect b = context->b; - - // Clipping plane normal and distance. - cpVect n = cpvnormalize(cpvperp(cpvsub(b, a))); - cpFloat dist = cpvdot(a, n); - - ClipPoly(space, shape, n, dist); - ClipPoly(space, shape, cpvneg(n), -dist); - - cpBody* body = cpShapeGetBody(shape); - cpSpaceRemoveShape(space, shape); - cpSpaceRemoveBody(space, body); - cpShapeFree(shape); - cpBodyFree(body); -} - -static void SliceQuery(cpShape* shape, cpVect point, cpVect normal, cpFloat alpha, struct SliceContext* context) -{ - cpVect a = context->a; - cpVect b = context->b; - - // Check that the slice was complete by checking that the endpoints aren't in the sliced shape. - if (cpShapePointQuery(shape, a, NULL) > 0.0f && cpShapePointQuery(shape, b, NULL) > 0.0f) - { - // Can't modify the space during a query. - // Must make a post-step callback to do the actual slicing. - cpSpaceAddPostStepCallback(context->space, (cpPostStepFunc)SliceShapePostStep, shape, context); - } -} - -static void update(cpSpace* space, double dt) -{ - cpSpaceStep(space, dt); - - static cpBool lastClickState = cpFalse; - static cpVect sliceStart = {0.0, 0.0}; - - // Annoying state tracking code that you wouldn't need - // in a real event driven system. - if (ChipmunkDemoRightClick != lastClickState) - { - if (ChipmunkDemoRightClick) - { - // MouseDown - sliceStart = ChipmunkDemoMouse; - } - else - { - // MouseUp - struct SliceContext context = {sliceStart, ChipmunkDemoMouse, space}; - cpSpaceSegmentQuery(space, sliceStart, ChipmunkDemoMouse, 0.0, GRAB_FILTER, - (cpSpaceSegmentQueryFunc)SliceQuery, &context); - } - - lastClickState = ChipmunkDemoRightClick; - } - - if (ChipmunkDemoRightClick) - { - ChipmunkDebugDrawSegment(sliceStart, ChipmunkDemoMouse, RGBAColor(1, 0, 0, 1)); - } -} - -static cpSpace* init(void) -{ - ChipmunkDemoMessageString = "Right click and drag to slice up the block."; - - cpSpace* space = cpSpaceNew(); - cpSpaceSetIterations(space, 30); - cpSpaceSetGravity(space, cpv(0, -500)); - cpSpaceSetSleepTimeThreshold(space, 0.5f); - cpSpaceSetCollisionSlop(space, 0.5f); - - cpBody *body, *staticBody = cpSpaceGetStaticBody(space); - cpShape* shape; - - // Create segments around the edge of the screen. - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-1000, -240), cpv(1000, -240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - cpFloat width = 200.0f; - cpFloat height = 300.0f; - cpFloat mass = width * height * DENSITY; - cpFloat moment = cpMomentForBox(mass, width, height); - - body = cpSpaceAddBody(space, cpBodyNew(mass, moment)); - - shape = cpSpaceAddShape(space, cpBoxShapeNew(body, width, height, 0.0)); - cpShapeSetFriction(shape, 0.6f); - - return space; -} - -static void destroy(cpSpace* space) -{ - ChipmunkDemoFreeSpaceChildren(space); - cpSpaceFree(space); -} - -ChipmunkDemo Slice = { - "Slice.", 1.0 / 60.0, init, update, ChipmunkDemoDefaultDrawImpl, destroy, -}; diff --git a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Springies.cpp b/tests/cpp-tests/Source/ChipmunkTestBed/demo/Springies.cpp deleted file mode 100644 index 89fdfab17025..000000000000 --- a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Springies.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* Copyright (c) 2007 Scott Lembcke - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include - -#include "chipmunk/chipmunk.h" -#include "ChipmunkDemo.h" - -static cpFloat springForce(cpConstraint* spring, cpFloat dist) -{ - cpFloat clamp = 20.0f; - return cpfclamp(cpDampedSpringGetRestLength(spring) - dist, -clamp, clamp) * cpDampedSpringGetStiffness(spring); -} - -static cpConstraint* -new_spring(cpBody* a, cpBody* b, cpVect anchorA, cpVect anchorB, cpFloat restLength, cpFloat stiff, cpFloat damp) -{ - cpConstraint* spring = cpDampedSpringNew(a, b, anchorA, anchorB, restLength, stiff, damp); - cpDampedSpringSetSpringForceFunc(spring, springForce); - - return spring; -} - -static void update(cpSpace* space, double dt) -{ - cpSpaceStep(space, dt); -} - -static cpBody* add_bar(cpSpace* space, cpVect a, cpVect b, int group) -{ - cpVect center = cpvmult(cpvadd(a, b), 1.0f / 2.0f); - cpFloat length = cpvlength(cpvsub(b, a)); - cpFloat mass = length / 160.0f; - - cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, mass * length * length / 12.0f)); - cpBodySetPosition(body, center); - - cpShape* shape = cpSpaceAddShape(space, cpSegmentShapeNew(body, cpvsub(a, center), cpvsub(b, center), 10.0f)); - cpShapeSetFilter(shape, cpShapeFilterNew(group, CP_ALL_CATEGORIES, CP_ALL_CATEGORIES)); - - return body; -} - -static cpSpace* init(void) -{ - cpSpace* space = cpSpaceNew(); - cpBody* staticBody = cpSpaceGetStaticBody(space); - - cpBody* body1 = add_bar(space, cpv(-240, 160), cpv(-160, 80), 1); - cpBody* body2 = add_bar(space, cpv(-160, 80), cpv(-80, 160), 1); - cpBody* body3 = add_bar(space, cpv(0, 160), cpv(80, 0), 0); - cpBody* body4 = add_bar(space, cpv(160, 160), cpv(240, 160), 0); - cpBody* body5 = add_bar(space, cpv(-240, 0), cpv(-160, -80), 2); - cpBody* body6 = add_bar(space, cpv(-160, -80), cpv(-80, 0), 2); - cpBody* body7 = add_bar(space, cpv(-80, 0), cpv(0, 0), 2); - cpBody* body8 = add_bar(space, cpv(0, -80), cpv(80, -80), 0); - cpBody* body9 = add_bar(space, cpv(240, 80), cpv(160, 0), 3); - cpBody* body10 = add_bar(space, cpv(160, 0), cpv(240, -80), 3); - cpBody* body11 = add_bar(space, cpv(-240, -80), cpv(-160, -160), 4); - cpBody* body12 = add_bar(space, cpv(-160, -160), cpv(-80, -160), 4); - cpBody* body13 = add_bar(space, cpv(0, -160), cpv(80, -160), 0); - cpBody* body14 = add_bar(space, cpv(160, -160), cpv(240, -160), 0); - - cpSpaceAddConstraint(space, cpPivotJointNew2(body1, body2, cpv(40, -40), cpv(-40, -40))); - cpSpaceAddConstraint(space, cpPivotJointNew2(body5, body6, cpv(40, -40), cpv(-40, -40))); - cpSpaceAddConstraint(space, cpPivotJointNew2(body6, body7, cpv(40, 40), cpv(-40, 0))); - cpSpaceAddConstraint(space, cpPivotJointNew2(body9, body10, cpv(-40, -40), cpv(-40, 40))); - cpSpaceAddConstraint(space, cpPivotJointNew2(body11, body12, cpv(40, -40), cpv(-40, 0))); - - cpFloat stiff = 100.0f; - cpFloat damp = 0.5f; - cpSpaceAddConstraint(space, new_spring(staticBody, body1, cpv(-320, 240), cpv(-40, 40), 0.0f, stiff, damp)); - cpSpaceAddConstraint(space, new_spring(staticBody, body1, cpv(-320, 80), cpv(-40, 40), 0.0f, stiff, damp)); - cpSpaceAddConstraint(space, new_spring(staticBody, body1, cpv(-160, 240), cpv(-40, 40), 0.0f, stiff, damp)); - - cpSpaceAddConstraint(space, new_spring(staticBody, body2, cpv(-160, 240), cpv(40, 40), 0.0f, stiff, damp)); - cpSpaceAddConstraint(space, new_spring(staticBody, body2, cpv(0, 240), cpv(40, 40), 0.0f, stiff, damp)); - - cpSpaceAddConstraint(space, new_spring(staticBody, body3, cpv(80, 240), cpv(-40, 80), 0.0f, stiff, damp)); - - cpSpaceAddConstraint(space, new_spring(staticBody, body4, cpv(80, 240), cpv(-40, 0), 0.0f, stiff, damp)); - cpSpaceAddConstraint(space, new_spring(staticBody, body4, cpv(320, 240), cpv(40, 0), 0.0f, stiff, damp)); - - cpSpaceAddConstraint(space, new_spring(staticBody, body5, cpv(-320, 80), cpv(-40, 40), 0.0f, stiff, damp)); - - cpSpaceAddConstraint(space, new_spring(staticBody, body9, cpv(320, 80), cpv(40, 40), 0.0f, stiff, damp)); - - cpSpaceAddConstraint(space, new_spring(staticBody, body10, cpv(320, 0), cpv(40, -40), 0.0f, stiff, damp)); - cpSpaceAddConstraint(space, new_spring(staticBody, body10, cpv(320, -160), cpv(40, -40), 0.0f, stiff, damp)); - - cpSpaceAddConstraint(space, new_spring(staticBody, body11, cpv(-320, -160), cpv(-40, 40), 0.0f, stiff, damp)); - - cpSpaceAddConstraint(space, new_spring(staticBody, body12, cpv(-240, -240), cpv(-40, 0), 0.0f, stiff, damp)); - cpSpaceAddConstraint(space, new_spring(staticBody, body12, cpv(0, -240), cpv(40, 0), 0.0f, stiff, damp)); - - cpSpaceAddConstraint(space, new_spring(staticBody, body13, cpv(0, -240), cpv(-40, 0), 0.0f, stiff, damp)); - cpSpaceAddConstraint(space, new_spring(staticBody, body13, cpv(80, -240), cpv(40, 0), 0.0f, stiff, damp)); - - cpSpaceAddConstraint(space, new_spring(staticBody, body14, cpv(80, -240), cpv(-40, 0), 0.0f, stiff, damp)); - cpSpaceAddConstraint(space, new_spring(staticBody, body14, cpv(240, -240), cpv(40, 0), 0.0f, stiff, damp)); - cpSpaceAddConstraint(space, new_spring(staticBody, body14, cpv(320, -160), cpv(40, 0), 0.0f, stiff, damp)); - - cpSpaceAddConstraint(space, new_spring(body1, body5, cpv(40, -40), cpv(-40, 40), 0.0f, stiff, damp)); - cpSpaceAddConstraint(space, new_spring(body1, body6, cpv(40, -40), cpv(40, 40), 0.0f, stiff, damp)); - cpSpaceAddConstraint(space, new_spring(body2, body3, cpv(40, 40), cpv(-40, 80), 0.0f, stiff, damp)); - cpSpaceAddConstraint(space, new_spring(body3, body4, cpv(-40, 80), cpv(-40, 0), 0.0f, stiff, damp)); - cpSpaceAddConstraint(space, new_spring(body3, body4, cpv(40, -80), cpv(-40, 0), 0.0f, stiff, damp)); - cpSpaceAddConstraint(space, new_spring(body3, body7, cpv(40, -80), cpv(40, 0), 0.0f, stiff, damp)); - cpSpaceAddConstraint(space, new_spring(body3, body7, cpv(-40, 80), cpv(-40, 0), 0.0f, stiff, damp)); - cpSpaceAddConstraint(space, new_spring(body3, body8, cpv(40, -80), cpv(40, 0), 0.0f, stiff, damp)); - cpSpaceAddConstraint(space, new_spring(body3, body9, cpv(40, -80), cpv(-40, -40), 0.0f, stiff, damp)); - cpSpaceAddConstraint(space, new_spring(body4, body9, cpv(40, 0), cpv(40, 40), 0.0f, stiff, damp)); - cpSpaceAddConstraint(space, new_spring(body5, body11, cpv(-40, 40), cpv(-40, 40), 0.0f, stiff, damp)); - cpSpaceAddConstraint(space, new_spring(body5, body11, cpv(40, -40), cpv(40, -40), 0.0f, stiff, damp)); - cpSpaceAddConstraint(space, new_spring(body7, body8, cpv(40, 0), cpv(-40, 0), 0.0f, stiff, damp)); - cpSpaceAddConstraint(space, new_spring(body8, body12, cpv(-40, 0), cpv(40, 0), 0.0f, stiff, damp)); - cpSpaceAddConstraint(space, new_spring(body8, body13, cpv(-40, 0), cpv(-40, 0), 0.0f, stiff, damp)); - cpSpaceAddConstraint(space, new_spring(body8, body13, cpv(40, 0), cpv(40, 0), 0.0f, stiff, damp)); - cpSpaceAddConstraint(space, new_spring(body8, body14, cpv(40, 0), cpv(-40, 0), 0.0f, stiff, damp)); - cpSpaceAddConstraint(space, new_spring(body10, body14, cpv(40, -40), cpv(-40, 0), 0.0f, stiff, damp)); - cpSpaceAddConstraint(space, new_spring(body10, body14, cpv(40, -40), cpv(-40, 0), 0.0f, stiff, damp)); - - return space; -} - -static void destroy(cpSpace* space) -{ - ChipmunkDemoFreeSpaceChildren(space); - cpSpaceFree(space); -} - -ChipmunkDemo Springies = { - "Springies", 1.0 / 60.0, init, update, ChipmunkDemoDefaultDrawImpl, destroy, -}; diff --git a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Sticky.cpp b/tests/cpp-tests/Source/ChipmunkTestBed/demo/Sticky.cpp deleted file mode 100644 index f7545afdbf17..000000000000 --- a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Sticky.cpp +++ /dev/null @@ -1,190 +0,0 @@ -/* Copyright (c) 2007 Scott Lembcke - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk.h" -#include "ChipmunkDemo.h" - -enum -{ - COLLISION_TYPE_STICKY = 1, -}; - -#define STICK_SENSOR_THICKNESS 2.5f - -static void PostStepAddJoint(cpSpace* space, void* key, void* data) -{ - // printf("Adding joint for %p\n", data); - - cpConstraint* joint = (cpConstraint*)key; - cpSpaceAddConstraint(space, joint); -} - -static cpBool StickyPreSolve(cpArbiter* arb, cpSpace* space, void* data) -{ - // We want to fudge the collisions a bit to allow shapes to overlap more. - // This simulates their squishy sticky surface, and more importantly - // keeps them from separating and destroying the joint. - - // Track the deepest collision point and use that to determine if a rigid collision should occur. - cpFloat deepest = INFINITY; - - // Grab the contact set and iterate over them. - cpContactPointSet contacts = cpArbiterGetContactPointSet(arb); - for (int i = 0; i < contacts.count; i++) - { - // Sink the contact points into the surface of each shape. - contacts.points[i].pointA = cpvsub(contacts.points[i].pointA, cpvmult(contacts.normal, STICK_SENSOR_THICKNESS)); - contacts.points[i].pointB = cpvadd(contacts.points[i].pointB, cpvmult(contacts.normal, STICK_SENSOR_THICKNESS)); - deepest = cpfmin(deepest, contacts.points[i].distance); // + 2.0f*STICK_SENSOR_THICKNESS); - } - - // Set the new contact point data. - cpArbiterSetContactPointSet(arb, &contacts); - - // If the shapes are overlapping enough, then create a - // joint that sticks them together at the first contact point. - if (!cpArbiterGetUserData(arb) && deepest <= 0.0f) - { - CP_ARBITER_GET_BODIES(arb, bodyA, bodyB); - - // Create a joint at the contact point to hold the body in place. - cpVect anchorA = cpBodyWorldToLocal(bodyA, contacts.points[0].pointA); - cpVect anchorB = cpBodyWorldToLocal(bodyB, contacts.points[0].pointB); - cpConstraint* joint = cpPivotJointNew2(bodyA, bodyB, anchorA, anchorB); - - // Give it a finite force for the stickyness. - cpConstraintSetMaxForce(joint, 3e3); - - // Schedule a post-step() callback to add the joint. - cpSpaceAddPostStepCallback(space, PostStepAddJoint, joint, NULL); - - // Store the joint on the arbiter so we can remove it later. - cpArbiterSetUserData(arb, joint); - } - - // Position correction and velocity are handled separately so changing - // the overlap distance alone won't prevent the collision from occuring. - // Explicitly the collision for this frame if the shapes don't overlap using the new distance. - return (deepest <= 0.0f); - - // Lots more that you could improve upon here as well: - // * Modify the joint over time to make it plastic. - // * Modify the joint in the post-step to make it conditionally plastic (like clay). - // * Track a joint for the deepest contact point instead of the first. - // * Track a joint for each contact point. (more complicated since you only get one data pointer). -} - -static void PostStepRemoveJoint(cpSpace* space, void* key, void* data) -{ - // printf("Removing joint for %p\n", data); - - cpConstraint* joint = (cpConstraint*)key; - cpSpaceRemoveConstraint(space, joint); - cpConstraintFree(joint); -} - -static void StickySeparate(cpArbiter* arb, cpSpace* space, void* data) -{ - cpConstraint* joint = (cpConstraint*)cpArbiterGetUserData(arb); - - if (joint) - { - // The joint won't be removed until the step is done. - // Need to disable it so that it won't apply itself. - // Setting the force to 0 will do just that - cpConstraintSetMaxForce(joint, 0.0f); - - // Perform the removal in a post-step() callback. - cpSpaceAddPostStepCallback(space, PostStepRemoveJoint, joint, NULL); - - // NULL out the reference to the joint. - // Not required, but it's a good practice. - cpArbiterSetUserData(arb, NULL); - } -} - -static void update(cpSpace* space, double dt) -{ - cpSpaceStep(space, dt); -} - -static cpSpace* init(void) -{ - ChipmunkDemoMessageString = "Sticky collisions using the cpArbiter data pointer."; - - cpSpace* space = cpSpaceNew(); - cpSpaceSetIterations(space, 10); - cpSpaceSetGravity(space, cpv(0, -1000)); - cpSpaceSetCollisionSlop(space, 2.0); - - cpBody* staticBody = cpSpaceGetStaticBody(space); - cpShape* shape; - - // Create segments around the edge of the screen. - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-340, -260), cpv(-340, 260), 20.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(340, -260), cpv(340, 260), 20.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-340, -260), cpv(340, -260), 20.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-340, 260), cpv(340, 260), 20.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - for (int i = 0; i < 200; i++) - { - cpFloat mass = 0.15f; - cpFloat radius = 10.0f; - - cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForCircle(mass, 0.0f, radius, cpvzero))); - cpBodySetPosition(body, cpv(cpflerp(-150.0f, 150.0f, frand()), cpflerp(-150.0f, 150.0f, frand()))); - - cpShape* shape = cpSpaceAddShape(space, cpCircleShapeNew(body, radius + STICK_SENSOR_THICKNESS, cpvzero)); - cpShapeSetFriction(shape, 0.9f); - cpShapeSetCollisionType(shape, COLLISION_TYPE_STICKY); - } - - cpCollisionHandler* handler = cpSpaceAddWildcardHandler(space, COLLISION_TYPE_STICKY); - handler->preSolveFunc = StickyPreSolve; - handler->separateFunc = StickySeparate; - - return space; -} - -static void destroy(cpSpace* space) -{ - ChipmunkDemoFreeSpaceChildren(space); - cpSpaceFree(space); -} - -ChipmunkDemo Sticky = { - "Sticky Surfaces", 1.0 / 60.0, init, update, ChipmunkDemoDefaultDrawImpl, destroy, -}; diff --git a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Tank.cpp b/tests/cpp-tests/Source/ChipmunkTestBed/demo/Tank.cpp deleted file mode 100644 index 3b5d1d41b417..000000000000 --- a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Tank.cpp +++ /dev/null @@ -1,132 +0,0 @@ -/* Copyright (c) 2007 Scott Lembcke - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk.h" -#include "ChipmunkDemo.h" - -static cpBody *tankBody, *tankControlBody; - -static void update(cpSpace* space, double dt) -{ - // turn the control body based on the angle relative to the actual body - cpVect mouseDelta = cpvsub(ChipmunkDemoMouse, cpBodyGetPosition(tankBody)); - cpFloat turn = cpvtoangle(cpvunrotate(cpBodyGetRotation(tankBody), mouseDelta)); - cpBodySetAngle(tankControlBody, cpBodyGetAngle(tankBody) - turn); - - // drive the tank towards the mouse - if (cpvnear(ChipmunkDemoMouse, cpBodyGetPosition(tankBody), 30.0)) - { - cpBodySetVelocity(tankControlBody, cpvzero); // stop - } - else - { - cpFloat direction = (cpvdot(mouseDelta, cpBodyGetRotation(tankBody)) > 0.0 ? 1.0 : -1.0); - cpBodySetVelocity(tankControlBody, cpvrotate(cpBodyGetRotation(tankBody), cpv(30.0f * direction, 0.0f))); - } - - cpSpaceStep(space, dt); -} - -static cpBody* add_box(cpSpace* space, cpFloat size, cpFloat mass) -{ - cpFloat radius = cpvlength(cpv(size, size)); - - cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForBox(mass, size, size))); - cpBodySetPosition( - body, cpv(frand() * (640 - 2 * radius) - (320 - radius), frand() * (480 - 2 * radius) - (240 - radius))); - - cpShape* shape = cpSpaceAddShape(space, cpBoxShapeNew(body, size, size, 0.0)); - cpShapeSetElasticity(shape, 0.0f); - cpShapeSetFriction(shape, 0.7f); - - return body; -} - -static cpSpace* init(void) -{ - ChipmunkDemoMessageString = "Use the mouse to drive the tank, it will follow the cursor."; - - cpSpace* space = cpSpaceNew(); - cpSpaceSetIterations(space, 10); - cpSpaceSetSleepTimeThreshold(space, 0.5f); - - cpBody* staticBody = cpSpaceGetStaticBody(space); - cpShape* shape; - - // Create segments around the edge of the screen. - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, -240), cpv(-320, 240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(320, -240), cpv(320, 240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, -240), cpv(320, -240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, 240), cpv(320, 240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - for (int i = 0; i < 50; i++) - { - cpBody* body = add_box(space, 20, 1); - - cpConstraint* pivot = cpSpaceAddConstraint(space, cpPivotJointNew2(staticBody, body, cpvzero, cpvzero)); - cpConstraintSetMaxBias(pivot, 0); // disable joint correction - cpConstraintSetMaxForce(pivot, 1000.0f); // emulate linear friction - - cpConstraint* gear = cpSpaceAddConstraint(space, cpGearJointNew(staticBody, body, 0.0f, 1.0f)); - cpConstraintSetMaxBias(gear, 0); // disable joint correction - cpConstraintSetMaxForce(gear, 5000.0f); // emulate angular friction - } - - // We joint the tank to the control body and control the tank indirectly by modifying the control body. - tankControlBody = cpSpaceAddBody(space, cpBodyNewKinematic()); - tankBody = add_box(space, 30, 10); - - cpConstraint* pivot = cpSpaceAddConstraint(space, cpPivotJointNew2(tankControlBody, tankBody, cpvzero, cpvzero)); - cpConstraintSetMaxBias(pivot, 0); // disable joint correction - cpConstraintSetMaxForce(pivot, 10000.0f); // emulate linear friction - - cpConstraint* gear = cpSpaceAddConstraint(space, cpGearJointNew(tankControlBody, tankBody, 0.0f, 1.0f)); - cpConstraintSetErrorBias(gear, 0); // attempt to fully correct the joint each step - cpConstraintSetMaxBias(gear, 1.2f); // but limit it's angular correction rate - cpConstraintSetMaxForce(gear, 50000.0f); // emulate angular friction - - return space; -} - -static void destroy(cpSpace* space) -{ - ChipmunkDemoFreeSpaceChildren(space); - cpSpaceFree(space); -} - -ChipmunkDemo Tank = { - "Tank", 1.0 / 60.0, init, update, ChipmunkDemoDefaultDrawImpl, destroy, -}; diff --git a/tests/cpp-tests/Source/ChipmunkTestBed/demo/TheoJansen.cpp b/tests/cpp-tests/Source/ChipmunkTestBed/demo/TheoJansen.cpp deleted file mode 100644 index 7888cf861cab..000000000000 --- a/tests/cpp-tests/Source/ChipmunkTestBed/demo/TheoJansen.cpp +++ /dev/null @@ -1,163 +0,0 @@ -/* Copyright (c) 2007 Scott Lembcke - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* - * The previous WalkBot demo I designed was fairly disappointing, so I implemented - * the mechanism that Theo Jansen uses in his kinetic sculptures. Brilliant. - * Read more here: http://en.wikipedia.org/wiki/Theo_Jansen - */ - -#include "chipmunk/chipmunk.h" -#include "ChipmunkDemo.h" - -static cpConstraint* motor; - -static void update(cpSpace* space, double dt) -{ - cpFloat coef = (2.0f + ChipmunkDemoKeyboard.y) / 3.0f; - cpFloat rate = ChipmunkDemoKeyboard.x * 10.0f * coef; - - cpSimpleMotorSetRate(motor, rate); - cpConstraintSetMaxForce(motor, (rate) ? 100000.0f : 0.0f); - - cpSpaceStep(space, dt); -} - -static cpFloat seg_radius = 3.0f; - -static void make_leg(cpSpace* space, cpFloat side, cpFloat offset, cpBody* chassis, cpBody* crank, cpVect anchor) -{ - cpVect a, b; - cpShape* shape; - - cpFloat leg_mass = 1.0f; - - // make leg - a = cpvzero, b = cpv(0.0f, side); - cpBody* upper_leg = cpSpaceAddBody(space, cpBodyNew(leg_mass, cpMomentForSegment(leg_mass, a, b, 0.0f))); - cpBodySetPosition(upper_leg, cpv(offset, 0.0f)); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(upper_leg, a, b, seg_radius)); - cpShapeSetFilter(shape, cpShapeFilterNew(1, CP_ALL_CATEGORIES, CP_ALL_CATEGORIES)); - - cpSpaceAddConstraint(space, cpPivotJointNew2(chassis, upper_leg, cpv(offset, 0.0f), cpvzero)); - - // lower leg - a = cpvzero, b = cpv(0.0f, -1.0f * side); - cpBody* lower_leg = cpSpaceAddBody(space, cpBodyNew(leg_mass, cpMomentForSegment(leg_mass, a, b, 0.0f))); - cpBodySetPosition(lower_leg, cpv(offset, -side)); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(lower_leg, a, b, seg_radius)); - cpShapeSetFilter(shape, cpShapeFilterNew(1, CP_ALL_CATEGORIES, CP_ALL_CATEGORIES)); - - shape = cpSpaceAddShape(space, cpCircleShapeNew(lower_leg, seg_radius * 2.0f, b)); - cpShapeSetFilter(shape, cpShapeFilterNew(1, CP_ALL_CATEGORIES, CP_ALL_CATEGORIES)); - cpShapeSetElasticity(shape, 0.0f); - cpShapeSetFriction(shape, 1.0f); - - cpSpaceAddConstraint(space, cpPinJointNew(chassis, lower_leg, cpv(offset, 0.0f), cpvzero)); - - cpSpaceAddConstraint(space, cpGearJointNew(upper_leg, lower_leg, 0.0f, 1.0f)); - - cpConstraint* constraint; - cpFloat diag = cpfsqrt(side * side + offset * offset); - - constraint = cpSpaceAddConstraint(space, cpPinJointNew(crank, upper_leg, anchor, cpv(0.0f, side))); - cpPinJointSetDist(constraint, diag); - - constraint = cpSpaceAddConstraint(space, cpPinJointNew(crank, lower_leg, anchor, cpvzero)); - cpPinJointSetDist(constraint, diag); -} - -static cpSpace* init(void) -{ - ChipmunkDemoMessageString = "Use the arrow keys to control the machine."; - - cpSpace* space = cpSpaceNew(); - cpSpaceSetIterations(space, 20); - cpSpaceSetGravity(space, cpv(0, -500)); - - cpBody* staticBody = cpSpaceGetStaticBody(space); - cpShape* shape; - cpVect a, b; - - // Create segments around the edge of the screen. - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, -240), cpv(-320, 240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(320, -240), cpv(320, 240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, -240), cpv(320, -240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - cpFloat offset = 30.0f; - - // make chassis - cpFloat chassis_mass = 2.0f; - a = cpv(-offset, 0.0f), b = cpv(offset, 0.0f); - cpBody* chassis = cpSpaceAddBody(space, cpBodyNew(chassis_mass, cpMomentForSegment(chassis_mass, a, b, 0.0f))); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(chassis, a, b, seg_radius)); - cpShapeSetFilter(shape, cpShapeFilterNew(1, CP_ALL_CATEGORIES, CP_ALL_CATEGORIES)); - - // make crank - cpFloat crank_mass = 1.0f; - cpFloat crank_radius = 13.0f; - cpBody* crank = - cpSpaceAddBody(space, cpBodyNew(crank_mass, cpMomentForCircle(crank_mass, crank_radius, 0.0f, cpvzero))); - - shape = cpSpaceAddShape(space, cpCircleShapeNew(crank, crank_radius, cpvzero)); - cpShapeSetFilter(shape, cpShapeFilterNew(1, CP_ALL_CATEGORIES, CP_ALL_CATEGORIES)); - - cpSpaceAddConstraint(space, cpPivotJointNew2(chassis, crank, cpvzero, cpvzero)); - - cpFloat side = 30.0f; - - int num_legs = 2; - for (int i = 0; i < num_legs; i++) - { - make_leg(space, side, offset, chassis, crank, - cpvmult(cpvforangle((cpFloat)(2 * i + 0) / (cpFloat)num_legs * CP_PI), crank_radius)); - make_leg(space, side, -offset, chassis, crank, - cpvmult(cpvforangle((cpFloat)(2 * i + 1) / (cpFloat)num_legs * CP_PI), crank_radius)); - } - - motor = cpSpaceAddConstraint(space, cpSimpleMotorNew(chassis, crank, 6.0f)); - - return space; -} - -static void destroy(cpSpace* space) -{ - ChipmunkDemoFreeSpaceChildren(space); - cpSpaceFree(space); -} - -ChipmunkDemo TheoJansen = { - "Theo Jansen Machine", 1.0 / 180.0, init, update, ChipmunkDemoDefaultDrawImpl, destroy, -}; diff --git a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Tumble.cpp b/tests/cpp-tests/Source/ChipmunkTestBed/demo/Tumble.cpp deleted file mode 100644 index 21f89814cd47..000000000000 --- a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Tumble.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/* Copyright (c) 2007 Scott Lembcke - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk.h" -#include "ChipmunkDemo.h" - -static cpBody* KinematicBoxBody; - -static void update(cpSpace* space, double dt) -{ - cpSpaceStep(space, dt); -} - -static void AddBox(cpSpace* space, cpVect pos, cpFloat mass, cpFloat width, cpFloat height) -{ - cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForBox(mass, width, height))); - cpBodySetPosition(body, pos); - - cpShape* shape = cpSpaceAddShape(space, cpBoxShapeNew(body, width, height, 0.0)); - cpShapeSetElasticity(shape, 0.0f); - cpShapeSetFriction(shape, 0.7f); -} - -static void AddSegment(cpSpace* space, cpVect pos, cpFloat mass, cpFloat width, cpFloat height) -{ - cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForBox(mass, width, height))); - cpBodySetPosition(body, pos); - - cpShape* shape = cpSpaceAddShape(space, cpSegmentShapeNew(body, cpv(0.0, (height - width) / 2.0), - cpv(0.0, (width - height) / 2.0), width / 2.0)); - cpShapeSetElasticity(shape, 0.0f); - cpShapeSetFriction(shape, 0.7f); -} - -static void AddCircle(cpSpace* space, cpVect pos, cpFloat mass, cpFloat radius) -{ - cpBody* body = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForCircle(mass, 0.0, radius, cpvzero))); - cpBodySetPosition(body, pos); - - cpShape* shape = cpSpaceAddShape(space, cpCircleShapeNew(body, radius, cpvzero)); - cpShapeSetElasticity(shape, 0.0f); - cpShapeSetFriction(shape, 0.7f); -} - -static cpSpace* init(void) -{ - cpSpace* space = cpSpaceNew(); - cpSpaceSetGravity(space, cpv(0, -600)); - - cpShape* shape; - - // We create an infinite mass rogue body to attach the line segments too - // This way we can control the rotation however we want. - KinematicBoxBody = cpSpaceAddBody(space, cpBodyNewKinematic()); - cpBodySetAngularVelocity(KinematicBoxBody, 0.4f); - - // Set up the static box. - cpVect a = cpv(-200, -200); - cpVect b = cpv(-200, 200); - cpVect c = cpv(200, 200); - cpVect d = cpv(200, -200); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(KinematicBoxBody, a, b, 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(KinematicBoxBody, b, c, 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(KinematicBoxBody, c, d, 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(KinematicBoxBody, d, a, 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - cpFloat mass = 1; - cpFloat width = 30; - cpFloat height = width * 2; - - // Add the bricks. - for (int i = 0; i < 7; i++) - { - for (int j = 0; j < 3; j++) - { - cpVect pos = cpv(i * width - 150, j * height - 150); - - int type = (rand() % 3000) / 1000; - if (type == 0) - { - AddBox(space, pos, mass, width, height); - } - else if (type == 1) - { - AddSegment(space, pos, mass, width, height); - } - else - { - AddCircle(space, cpvadd(pos, cpv(0.0, (height - width) / 2.0)), mass, width / 2.0); - AddCircle(space, cpvadd(pos, cpv(0.0, (width - height) / 2.0)), mass, width / 2.0); - } - } - } - - return space; -} - -static void destroy(cpSpace* space) -{ - ChipmunkDemoFreeSpaceChildren(space); - cpSpaceFree(space); -} - -ChipmunkDemo Tumble = { - "Tumble", - 1.0 / 60.0, // faster as the original (more action) - init, update, ChipmunkDemoDefaultDrawImpl, destroy, -}; diff --git a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Unicycle.cpp b/tests/cpp-tests/Source/ChipmunkTestBed/demo/Unicycle.cpp deleted file mode 100644 index f40481c13a56..000000000000 --- a/tests/cpp-tests/Source/ChipmunkTestBed/demo/Unicycle.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/* Copyright (c) 2007 Scott Lembcke - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "chipmunk/chipmunk.h" -#include "ChipmunkDemo.h" - -static cpBody* balance_body; -static cpFloat balance_sin = 0.0; -// static cpFloat last_v = 0.0; - -static cpBody* wheel_body; -static cpConstraint* motor; - -/* - TODO: - - Clamp max angle dynamically based on output torque. -*/ - -static inline cpFloat bias_coef(cpFloat errorBias, cpFloat dt) -{ - return 1.0f - cpfpow(errorBias, dt); -} - -static void motor_preSolve(cpConstraint* motor, cpSpace* space) -{ - cpFloat dt = cpSpaceGetCurrentTimeStep(space); - - cpFloat target_x = ChipmunkDemoMouse.x; - ChipmunkDebugDrawSegment(cpv(target_x, -1000.0), cpv(target_x, 1000.0), RGBAColor(1.0, 0.0, 0.0, 1.0)); - - cpFloat max_v = 500.0; - cpFloat target_v = - cpfclamp(bias_coef(0.5, dt / 1.2) * (target_x - cpBodyGetPosition(balance_body).x) / dt, -max_v, max_v); - cpFloat error_v = (target_v - cpBodyGetVelocity(balance_body).x); - cpFloat target_sin = 3.0e-3 * bias_coef(0.1, dt) * error_v / dt; - - cpFloat max_sin = cpfsin(0.6); - balance_sin = cpfclamp(balance_sin - 6.0e-5 * bias_coef(0.2, dt) * error_v / dt, -max_sin, max_sin); - cpFloat target_a = asin(cpfclamp(-target_sin + balance_sin, -max_sin, max_sin)); - cpFloat angular_diff = asin(cpvcross(cpBodyGetRotation(balance_body), cpvforangle(target_a))); - cpFloat target_w = bias_coef(0.1, dt / 0.4) * (angular_diff) / dt; - - cpFloat max_rate = 50.0; - cpFloat rate = cpfclamp(cpBodyGetAngularVelocity(wheel_body) + cpBodyGetAngularVelocity(balance_body) - target_w, - -max_rate, max_rate); - cpSimpleMotorSetRate(motor, cpfclamp(rate, -max_rate, max_rate)); - cpConstraintSetMaxForce(motor, 8.0e4); -} - -static void update(cpSpace* space, double dt) -{ - cpSpaceStep(space, dt); -} - -static cpSpace* init(void) -{ - ChipmunkDemoMessageString = - "This unicycle is completely driven and balanced by a single cpSimpleMotor.\nMove the mouse to make the " - "unicycle follow it."; - - cpSpace* space = cpSpaceNew(); - cpSpaceSetIterations(space, 30); - cpSpaceSetGravity(space, cpv(0, -500)); - - { - cpShape* shape = NULL; - cpBody* staticBody = cpSpaceGetStaticBody(space); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-3200, -240), cpv(3200, -240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(0, -200), cpv(240, -240), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - - shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-240, -240), cpv(0, -200), 0.0f)); - cpShapeSetElasticity(shape, 1.0f); - cpShapeSetFriction(shape, 1.0f); - cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER); - } - - { - cpFloat radius = 20.0; - cpFloat mass = 1.0; - - cpFloat moment = cpMomentForCircle(mass, 0.0, radius, cpvzero); - wheel_body = cpSpaceAddBody(space, cpBodyNew(mass, moment)); - cpBodySetPosition(wheel_body, cpv(0.0, -160.0 + radius)); - - cpShape* shape = cpSpaceAddShape(space, cpCircleShapeNew(wheel_body, radius, cpvzero)); - cpShapeSetFriction(shape, 0.7); - cpShapeSetFilter(shape, cpShapeFilterNew(1, CP_ALL_CATEGORIES, CP_ALL_CATEGORIES)); - } - - { - cpFloat cog_offset = 30.0; - - cpBB bb1 = cpBBNew(-5.0, 0.0 - cog_offset, 5.0, cog_offset * 1.2 - cog_offset); - cpBB bb2 = cpBBNew(-25.0, bb1.t, 25.0, bb1.t + 10.0); - - cpFloat mass = 3.0; - cpFloat moment = cpMomentForBox2(mass, bb1) + cpMomentForBox2(mass, bb2); - - balance_body = cpSpaceAddBody(space, cpBodyNew(mass, moment)); - cpBodySetPosition(balance_body, cpv(0.0, cpBodyGetPosition(wheel_body).y + cog_offset)); - - cpShape* shape = NULL; - - shape = cpSpaceAddShape(space, cpBoxShapeNew2(balance_body, bb1, 0.0)); - cpShapeSetFriction(shape, 1.0); - cpShapeSetFilter(shape, cpShapeFilterNew(1, CP_ALL_CATEGORIES, CP_ALL_CATEGORIES)); - - shape = cpSpaceAddShape(space, cpBoxShapeNew2(balance_body, bb2, 0.0)); - cpShapeSetFriction(shape, 1.0); - cpShapeSetFilter(shape, cpShapeFilterNew(1, CP_ALL_CATEGORIES, CP_ALL_CATEGORIES)); - } - - cpVect anchorA = cpBodyWorldToLocal(balance_body, cpBodyGetPosition(wheel_body)); - cpVect groove_a = cpvadd(anchorA, cpv(0.0, 30.0)); - cpVect groove_b = cpvadd(anchorA, cpv(0.0, -10.0)); - cpSpaceAddConstraint(space, cpGrooveJointNew(balance_body, wheel_body, groove_a, groove_b, cpvzero)); - cpSpaceAddConstraint(space, cpDampedSpringNew(balance_body, wheel_body, anchorA, cpvzero, 0.0, 6.0e2, 30.0)); - - motor = cpSpaceAddConstraint(space, cpSimpleMotorNew(wheel_body, balance_body, 0.0)); - cpConstraintSetPreSolveFunc(motor, motor_preSolve); - - { - cpFloat width = 100.0; - cpFloat height = 20.0; - cpFloat mass = 3.0; - - cpBody* boxBody = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForBox(mass, width, height))); - cpBodySetPosition(boxBody, cpv(200, -100)); - - cpShape* shape = cpSpaceAddShape(space, cpBoxShapeNew(boxBody, width, height, 0.0)); - cpShapeSetFriction(shape, 0.7); - } - - return space; -} - -static void destroy(cpSpace* space) -{ - ChipmunkDemoFreeSpaceChildren(space); - cpSpaceFree(space); -} - -ChipmunkDemo Unicycle = { - "Unicycle", 1.0 / 60.0, init, update, ChipmunkDemoDefaultDrawImpl, destroy, -}; diff --git a/tests/cpp-tests/Source/ClickAndMoveTest/ClickAndMoveTest.cpp b/tests/cpp-tests/Source/ClickAndMoveTest/ClickAndMoveTest.cpp index 7e5fc4282040..5f8571b3a5bb 100644 --- a/tests/cpp-tests/Source/ClickAndMoveTest/ClickAndMoveTest.cpp +++ b/tests/cpp-tests/Source/ClickAndMoveTest/ClickAndMoveTest.cpp @@ -46,7 +46,7 @@ ClickAndMoveTestCase::ClickAndMoveTestCase() auto sprite = Sprite::create(s_pathGrossini); - auto layer = LayerColor::create(Color4B(255, 255, 0, 255)); + auto layer = LayerColor::create(Color32(255, 255, 0, 255)); addChild(layer, -1); addChild(sprite, 0, kTagSprite); diff --git a/tests/cpp-tests/Source/ClippingNodeTest/ClippingNodeTest.cpp b/tests/cpp-tests/Source/ClippingNodeTest/ClippingNodeTest.cpp index 738f2ab7e9c5..e938fe2a96ed 100644 --- a/tests/cpp-tests/Source/ClippingNodeTest/ClippingNodeTest.cpp +++ b/tests/cpp-tests/Source/ClippingNodeTest/ClippingNodeTest.cpp @@ -152,7 +152,7 @@ DrawNode* BasicTest::shape() triangle[1] = Vec2(100, -100); triangle[2] = Vec2(0, 100); - static Color4F green(0, 1, 0, 1); + static Color green(0, 1, 0, 1); shape->drawPolygon(triangle, 3, green, 0, green); return shape; } @@ -464,7 +464,7 @@ void ScrollViewDemo::setup() rectangle[2] = Vec2(clipper->getContentSize().width, clipper->getContentSize().height); rectangle[3] = Vec2(0.0f, clipper->getContentSize().height); - Color4F white(1, 1, 1, 1); + Color white(1, 1, 1, 1); stencil->drawPolygon(rectangle, 4, white, 1, white); clipper->setStencil(stencil); @@ -623,7 +623,7 @@ void RawStencilBufferTest::draw(Renderer* renderer, const Mat4& transform, uint3 _sprites.at(i)->setPosition(spritePoint); _spritesStencil.at(i)->setPosition(spritePoint); - renderer->clear(ClearFlag::STENCIL, Color4F::BLACK, 0.f, 0x0, _globalZOrder); + renderer->clear(ClearFlag::STENCIL, Color::BLACK, 0.f, 0x0, _globalZOrder); renderer->addCommand(&_renderCmds[cmdIndex]); cmdIndex++; @@ -870,7 +870,7 @@ void ClippingToRenderTextureTest::expectedBehaviour() triangle[0] = Point(-50, -50); triangle[1] = Point(50, -50); triangle[2] = Point(0, 50); - Color4F green(0, 1, 0, 1); + Color green(0, 1, 0, 1); stencil->drawPolygon(triangle, 3, green, 0, green); auto clipper = ClippingNode::create(); @@ -884,7 +884,7 @@ void ClippingToRenderTextureTest::expectedBehaviour() triangle[0] = Point(-200, -200); triangle[1] = Point(200, -200); triangle[2] = Point(0, 200); - Color4F red(1, 0, 0, 1); + Color red(1, 0, 0, 1); img->drawPolygon(triangle, 3, red, 0, red); clipper->addChild(img); } @@ -913,7 +913,7 @@ void ClippingToRenderTextureTest::reproduceBug() triangle[0] = Point(-50, -50); triangle[1] = Point(50, -50); triangle[2] = Point(0, 50); - Color4F green(0, 1, 0, 1); + Color green(0, 1, 0, 1); stencil->drawPolygon(triangle, 3, green, 0, green); auto clipper = ClippingNode::create(); @@ -927,7 +927,7 @@ void ClippingToRenderTextureTest::reproduceBug() triangle[0] = Point(-200, -200); triangle[1] = Point(200, -200); triangle[2] = Point(0, 200); - Color4F red(1, 0, 0, 1); + Color red(1, 0, 0, 1); img->drawPolygon(triangle, 3, red, 0, red); clipper->addChild(img); @@ -987,7 +987,7 @@ void ClippingNodePerformanceTest::setup() auto s = Director::getInstance()->getWinSize(); auto countLabel = Label::createWithTTF("0", "fonts/arial.ttf", 30); - countLabel->enableOutline(Color4B(0, 0, 0, 255), 2); + countLabel->enableOutline(Color32(0, 0, 0, 255), 2); countLabel->setPosition(Vec2(s.width / 2, s.height - 120)); addChild(countLabel, 1); @@ -1084,14 +1084,14 @@ void UniqueChildStencilTest::addChildStencils() // Child stencil 1 constexpr auto radius = 30.f; auto* drawNode = DrawNode::create(); - drawNode->drawSolidCircle(Vec2(50, 50), radius, 360, 180, 1, 1, Color4B::MAGENTA); + drawNode->drawSolidCircle(Vec2(50, 50), radius, 360, 180, 1, 1, Color::MAGENTA); _parentStencil->addChild(drawNode); // Child stencil 2 drawNode = DrawNode::create(); drawNode->drawSolidRect(Vec2(contentSize.width - 75, contentSize.height - 75), - Vec2(contentSize.width - 25, contentSize.height - 25), Color4B::MAGENTA); + Vec2(contentSize.width - 25, contentSize.height - 25), Color::MAGENTA); _parentStencil->addChild(drawNode); // Child stencil 3 diff --git a/tests/cpp-tests/Source/DrawNodeTest/DrawNodeTest.cpp b/tests/cpp-tests/Source/DrawNodeTest/DrawNodeTest.cpp index 5118f50987ab..1a42ecef0bd7 100644 --- a/tests/cpp-tests/Source/DrawNodeTest/DrawNodeTest.cpp +++ b/tests/cpp-tests/Source/DrawNodeTest/DrawNodeTest.cpp @@ -1355,7 +1355,7 @@ DrawNodeTests::DrawNodeTests() DrawNodeBaseTest::DrawNodeBaseTest() { auto director = Director::getInstance(); - director->setClearColor(Color4F(0, 0, 0, 0)); + director->setClearColor(Color(0, 0, 0, 0)); origin = director->getVisibleOrigin(); size = director->getVisibleSize(); @@ -1587,7 +1587,7 @@ DrawNodeMorphTest_SolidPolygon::DrawNodeMorphTest_SolidPolygon() addChild(drawNodeArray[n]); drawNodeArray[n]->setPosition( Vec2(AXRANDOM_MINUS1_1() * size.width / 4, AXRANDOM_MINUS1_1() * size.height / 4) + Vec2(100, 100)); - color[n] = Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f); + color[n] = Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f); rad[n] = 90 + AXRANDOM_0_1() * 10; state[n] = (AXRANDOM_0_1() > 0.5f) ? false : true; @@ -1674,7 +1674,7 @@ void DrawNodeMorphTest_SolidPolygon::update(float dt) drawNodeArray[n]->properties.setScale(Vec2(0.5f, 0.5f)); drawNodeArray[n]->drawSolidPolygon(verticesObjMorph[n], segments, color[n], sliderValue[sliderType::Thickness], - Color4B::YELLOW); + Color::YELLOW); } } @@ -1717,7 +1717,7 @@ DrawNodeMorphTest_Polygon::DrawNodeMorphTest_Polygon() addChild(drawNodeArray[n]); drawNodeArray[n]->setPosition( Vec2(AXRANDOM_MINUS1_1() * size.width / 4, AXRANDOM_MINUS1_1() * size.height / 4) + Vec2(100, 100)); - color[n] = Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f); + color[n] = Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f); rad[n] = 90 + AXRANDOM_0_1() * 10; state[n] = (AXRANDOM_0_1() > 0.5f) ? false : true; @@ -1874,7 +1874,7 @@ void DrawNodePictureTest::update(float dt) int sph_la = 0; do { - Color4F color = Color4F(sph_xx[sph_la + 1], sph_yy[sph_la + 1], sph_xx[sph_la + 2], sph_yy[sph_la + 2] * 255); + Color color = Color(sph_xx[sph_la + 1], sph_yy[sph_la + 1], sph_xx[sph_la + 2], sph_yy[sph_la + 2] * 255); Vec2* vertices = new Vec2[(int)(sph_cmb - 3)]; for (int n = 3; n < sph_cmb; n++) { @@ -1887,7 +1887,7 @@ void DrawNodePictureTest::update(float dt) drawNode->properties.setCenter(vertices[0]); drawNode->properties.setRotation(rot); drawNode->drawPolygon(vertices, sph_cmb - 3, color, /*rot*/ 0.f, - Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1), true); + Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1), true); sph_la += sph_cmb; sph_cmb = sph_yy[sph_la]; @@ -1929,9 +1929,9 @@ void DrawNodeLineDrawTest::update(float dt) float x = radius * cosf(rads) + center.x; float y = radius * sinf(rads) + center.y; - drawNode->drawLine(center - Vec2(20, 40), Vec2(x, y)- Vec2(20, 40), Color4F::RED, sliderValue[sliderType::Thickness]); - drawNode->drawLine(center + Vec2(120, 20), Vec2(x, y) + Vec2(120,20), Color4F::BLUE, sliderValue[sliderType::Thickness]); - drawNode->drawLine(center - Vec2(130, 110),Vec2(x,y) - Vec2(130,110), Color4F::GREEN, sliderValue[sliderType::Thickness]); + drawNode->drawLine(center - Vec2(20, 40), Vec2(x, y)- Vec2(20, 40), Color::RED, sliderValue[sliderType::Thickness]); + drawNode->drawLine(center + Vec2(120, 20), Vec2(x, y) + Vec2(120,20), Color::BLUE, sliderValue[sliderType::Thickness]); + drawNode->drawLine(center - Vec2(130, 110),Vec2(x,y) - Vec2(130,110), Color::GREEN, sliderValue[sliderType::Thickness]); } } @@ -1972,22 +1972,22 @@ void DrawNodeThicknessTest::update(float dt) drawNode->clear(); - drawNode->drawCircle(VisibleRect::center(), 60, AX_DEGREES_TO_RADIANS(77), 30, false, Color4F::GREEN, + drawNode->drawCircle(VisibleRect::center(), 60, AX_DEGREES_TO_RADIANS(77), 30, false, Color::GREEN, sliderValue[sliderType::Thickness]); - drawNode->drawLine(Vec2(0.0f, size.height), Vec2(size.width, size.height - 20), Color4F::YELLOW, + drawNode->drawLine(Vec2(0.0f, size.height), Vec2(size.width, size.height - 20), Color::YELLOW, sliderValue[sliderType::Thickness]); - drawNode->drawLine(Vec2(0.0f, 0.0f), Vec2(size.width, size.height - 20), Color4F::YELLOW, + drawNode->drawLine(Vec2(0.0f, 0.0f), Vec2(size.width, size.height - 20), Color::YELLOW, sliderValue[sliderType::Thickness]); // drawNode a rectangles - drawNode->drawRect(Vec2(123, 123), Vec2(227, 227), Color4F(1, 1, 0, 1), sliderValue[sliderType::Thickness]); - drawNode->drawRect(Vec2(115, 130), Vec2(130, 115), Vec2(115, 100), Vec2(100, 115), Color4F::MAGENTA, + drawNode->drawRect(Vec2(123, 123), Vec2(227, 227), Color(1, 1, 0, 1), sliderValue[sliderType::Thickness]); + drawNode->drawRect(Vec2(115, 130), Vec2(130, 115), Vec2(115, 100), Vec2(100, 115), Color::MAGENTA, sliderValue[sliderType::Thickness]); - drawNode->drawLine(Vec2(200.0f, size.height - 20), Vec2(size.width - 100, size.height - 20), Color4F::YELLOW, + drawNode->drawLine(Vec2(200.0f, size.height - 20), Vec2(size.width - 100, size.height - 20), Color::YELLOW, sliderValue[sliderType::Thickness]); - drawNode->drawLine(Vec2(300.0f, 100.0f), Vec2(size.width - 200, size.height - 120), Color4F::GREEN, + drawNode->drawLine(Vec2(300.0f, 100.0f), Vec2(size.width - 200, size.height - 120), Color::GREEN, sliderValue[sliderType::Thickness]); Vec2 vertices24[] = { @@ -2005,35 +2005,35 @@ void DrawNodeThicknessTest::update(float dt) {135.250000f, 108.625000f}, {151.000000f, 124.125000f}, {90.500000f, 131.875000f}, {113.250000f, 120.875000f}, {88.000000f, 116.875000f}, {106.000000f, 103.875000f}, {88.000000f, 97.875000f}, }; - drawNode->drawPolygon(vertices24, sizeof(vertices24) / sizeof(vertices24[0]), Color4B::TRANSPARENT, - sliderValue[sliderType::Thickness] / 2, Color4F::RED); + drawNode->drawPolygon(vertices24, sizeof(vertices24) / sizeof(vertices24[0]), Color::TRANSPARENT, + sliderValue[sliderType::Thickness] / 2, Color::RED); // open random color poly Vec2 vertices[] = {Vec2(0.0f, 0.0f), Vec2(50.0f, 50.0f), Vec2(100.0f, 50.0f), Vec2(100.0f, 100.0f), Vec2(50.0f, 100.0f)}; - drawNode->drawPoly(vertices, 5, false, Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f), + drawNode->drawPoly(vertices, 5, false, Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f), sliderValue[sliderType::Thickness]); // closed random color poly Vec2 vertices2[] = {Vec2(30.0f, 130.0f), Vec2(30.0f, 230.0f), Vec2(50.0f, 200.0f)}; - drawNode->drawPoly(vertices2, 3, true, Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f), + drawNode->drawPoly(vertices2, 3, true, Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f), sliderValue[sliderType::Thickness]); // drawNode some beziers drawNode->drawQuadBezier(Vec2(size.width - 150, size.height - 150), Vec2(size.width - 70, size.height - 10), - Vec2(size.width - 10, size.height - 10), 10, Color4F::BLUE, + Vec2(size.width - 10, size.height - 10), 10, Color::BLUE, sliderValue[sliderType::Thickness]); drawNode->drawQuadBezier(Vec2(0.0f + 100, size.height - 100), Vec2(size.width / 2, size.height / 2), - Vec2(size.width - 100, size.height - 100), 50, Color4F::RED, + Vec2(size.width - 100, size.height - 100), 50, Color::RED, sliderValue[sliderType::Thickness]); drawNode->drawCubicBezier(VisibleRect::center(), Vec2(VisibleRect::center().x + 30, VisibleRect::center().y + 50), Vec2(VisibleRect::center().x + 60, VisibleRect::center().y - 50), VisibleRect::right(), - 100, Color4F::WHITE, sliderValue[sliderType::Thickness]); + 100, Color::WHITE, sliderValue[sliderType::Thickness]); drawNode->drawCubicBezier(Vec2(size.width - 250, 40.0f), Vec2(size.width - 70, 100.0f), - Vec2(size.width - 30, 250.0f), Vec2(size.width - 10, size.height - 50), 10, Color4F::GRAY, + Vec2(size.width - 30, 250.0f), Vec2(size.width - 10, size.height - 50), 10, Color::GRAY, sliderValue[sliderType::Thickness]); auto array = ax::PointArray::create(20); @@ -2044,7 +2044,7 @@ void DrawNodeThicknessTest::update(float dt) array->addControlPoint(Vec2(80.0f, size.height - 80)); array->addControlPoint(Vec2(80.0f, 80.0f)); array->addControlPoint(Vec2(size.width / 2, size.height / 2)); - drawNode->drawCardinalSpline(array, 0.5f, 50, Color4F::MAGENTA, sliderValue[sliderType::Thickness]); + drawNode->drawCardinalSpline(array, 0.5f, 50, Color::MAGENTA, sliderValue[sliderType::Thickness]); auto array2 = ax::PointArray::create(20); array2->addControlPoint(Vec2(size.width / 2, 30.0f)); @@ -2052,51 +2052,51 @@ void DrawNodeThicknessTest::update(float dt) array2->addControlPoint(Vec2(size.width - 80, size.height - 80)); array2->addControlPoint(Vec2(size.width / 2, size.height - 80)); array2->addControlPoint(Vec2(size.width / 2, 30.0f)); - drawNode->drawCatmullRom(array2, 50, Color4F::ORANGE, sliderValue[sliderType::Thickness]); + drawNode->drawCatmullRom(array2, 50, Color::ORANGE, sliderValue[sliderType::Thickness]); auto s = Director::getInstance()->getWinSize(); drawNode->drawPoint(Vec2(s.width / 2 - 120, s.height / 2 - 120), 10, - Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1)); + Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1)); drawNode->drawPoint(Vec2(s.width / 2 + 120, s.height / 2 + 120), 10, - Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1)); + Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1)); // drawNode 4 small points Vec2 position[] = {Vec2(60, 60), Vec2(70, 70), Vec2(60, 70), Vec2(70, 60)}; - drawNode->drawPoints(position, 4, 5, Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1)); + drawNode->drawPoints(position, 4, 5, Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1)); // drawNode a line - drawNode->drawLine(Vec2(0, 0), Vec2(s.width, s.height), Color4F(1.0, 0.0, 0.0, 0.5)); + drawNode->drawLine(Vec2(0, 0), Vec2(s.width, s.height), Color(1.0, 0.0, 0.0, 0.5)); // drawNode a rectangle - drawNode->drawRect(Vec2(23, 23), Vec2(7, 7), Color4F(1, 1, 0, 1)); + drawNode->drawRect(Vec2(23, 23), Vec2(7, 7), Color(1, 1, 0, 1)); drawNode->drawRect(Vec2(15, 30), Vec2(30, 15), Vec2(15, 0), Vec2(0, 15), - Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1)); + Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1)); // drawNode a circle drawNode->drawCircle(VisibleRect::center() + Vec2(140, 0), 100, AX_DEGREES_TO_RADIANS(90), 50, true, 1.0f, 2.0f, - Color4F(1.0f, 0.0f, 0.0f, 0.5f)); + Color(1.0f, 0.0f, 0.0f, 0.5f)); drawNode->drawCircle(VisibleRect::center() - Vec2(140, 0), 50, AX_DEGREES_TO_RADIANS(90), 30, false, - Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); + Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); // drawNode some beziers drawNode->drawQuadBezier(Vec2(s.width - 150, s.height - 150), Vec2(s.width - 70, s.height - 10), Vec2(s.width - 10, s.height - 10), 10, - Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 0.5f)); + Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 0.5f)); drawNode->drawQuadBezier(Vec2(0.0f, s.height), Vec2(s.width / 2, s.height / 2), Vec2(s.width, s.height), 50, - Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 0.5f)); + Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 0.5f)); drawNode->drawCubicBezier(VisibleRect::center(), Vec2(VisibleRect::center().x + 30, VisibleRect::center().y + 50), Vec2(VisibleRect::center().x + 60, VisibleRect::center().y - 50), VisibleRect::right(), - 100, Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 0.5f)); + 100, Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 0.5f)); drawNode->drawCubicBezier(Vec2(s.width - 250, 40.0f), Vec2(s.width - 70, 100.0f), Vec2(s.width - 30, 250.0f), Vec2(s.width - 10, s.height - 50), 10, - Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 0.5f)); + Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 0.5f)); auto array3 = PointArray::create(20); array3->addControlPoint(Vec2(0.0f, 0.0f)); @@ -2106,7 +2106,7 @@ void DrawNodeThicknessTest::update(float dt) array3->addControlPoint(Vec2(80.0f, s.height - 80)); array3->addControlPoint(Vec2(80.0f, 80.0f)); array3->addControlPoint(Vec2(s.width / 2, s.height / 2)); - drawNode->drawCardinalSpline(array3, 0.5f, 50, Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 0.5f)); + drawNode->drawCardinalSpline(array3, 0.5f, 50, Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 0.5f)); auto array4 = PointArray::create(20); array4->addControlPoint(Vec2(s.width / 2, 30.0f)); @@ -2114,28 +2114,28 @@ void DrawNodeThicknessTest::update(float dt) array4->addControlPoint(Vec2(s.width - 80, s.height - 80)); array4->addControlPoint(Vec2(s.width / 2, s.height - 80)); array4->addControlPoint(Vec2(s.width / 2, 30.0f)); - drawNode->drawCatmullRom(array4, 50, Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 0.5f)); + drawNode->drawCatmullRom(array4, 50, Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 0.5f)); // open random color poly Vec2 verticesA[] = {Vec2(0.0f, 0.0f), Vec2(50.0f, 50.0f), Vec2(100.0f, 50.0f), Vec2(100.0f, 100.0f), Vec2(50.0f, 100.0f)}; - drawNode->drawPoly(verticesA, 5, false, Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); + drawNode->drawPoly(verticesA, 5, false, Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); // closed random color poly Vec2 verticesB[] = {Vec2(30.0f, 130.0f), Vec2(30.0f, 230.0f), Vec2(50.0f, 200.0f)}; - drawNode->drawPoly(verticesB, 3, true, Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); + drawNode->drawPoly(verticesB, 3, true, Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); // drawNode 10 circles for (int i = 0; i < 10; i++) { drawNode->drawDot(Vec2(s.width / 2, s.height / 2), 10.f * (10 - i), - Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); + Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); } // drawNode polygons Vec2 points[] = {Vec2(s.height / 4, 0.0f), Vec2(s.width, s.height / 5), Vec2(s.width / 3 * 2, s.height)}; - drawNode->drawPolygon(points, sizeof(points) / sizeof(points[0]), Color4F(1.0f, 0.0f, 0.0f, 0.5f), 4, - Color4F(0.0f, 0.0f, 1.0f, 0.5f)); + drawNode->drawPolygon(points, sizeof(points) / sizeof(points[0]), Color(1.0f, 0.0f, 0.0f, 0.5f), 4, + Color(0.0f, 0.0f, 1.0f, 0.5f)); // star poly (triggers buggs) { @@ -2149,8 +2149,8 @@ void DrawNodeThicknessTest::update(float dt) // {o -h, o+w}, {o,o}, // left spike }; - drawNode->drawPolygon(star, sizeof(star) / sizeof(star[0]), Color4F(1.0f, 0.0f, 0.0f, 0.5f), 1, - Color4F(0.0f, 0.0f, 1.0f, 1.0f)); + drawNode->drawPolygon(star, sizeof(star) / sizeof(star[0]), Color(1.0f, 0.0f, 0.0f, 0.5f), 1, + Color(0.0f, 0.0f, 1.0f, 1.0f)); } // star poly (doesn't trigger bug... order is important un tesselation is supported. @@ -2169,39 +2169,40 @@ void DrawNodeThicknessTest::update(float dt) Vec2(o - h, o + w), // left spike }; - drawNode->drawPolygon(star, sizeof(star) / sizeof(star[0]), Color4F(1.0f, 0.0f, 0.0f, 0.5f), 1, - Color4F(0.0f, 0.0f, 1.0f, 1.0f)); + drawNode->drawPolygon(star, sizeof(star) / sizeof(star[0]), Color(1.0f, 0.0f, 0.0f, 0.5f), 1, + Color(0.0f, 0.0f, 1.0f, 1.0f)); } // drawNode a solid polygon Vec2 vertices3[] = {Vec2(60.0f, 160.0f), Vec2(70.0f, 190.0f), Vec2(100.0f, 190.0f), Vec2(90.0f, 160.0f)}; - drawNode->drawSolidPoly(vertices3, 4, Color4F(1.0f, 1.0f, 0.0f, 1.0f)); + drawNode->drawSolidPoly(vertices3, 4, Color(1.0f, 1.0f, 0.0f, 1.0f)); // drawNode a solid rectangle - drawNode->drawSolidRect(Vec2(10.0f, 10.0f), Vec2(20.0f, 20.0f), Color4F(1.0f, 1.0f, 0.0f, 1.0f)); + drawNode->drawSolidRect(Vec2(10.0f, 10.0f), Vec2(20.0f, 20.0f), Color(1.0f, 1.0f, 0.0f, 1.0f)); // drawNode a solid circle drawNode->drawSolidCircle(VisibleRect::center() + Vec2(140.0f, 0.0f), 40, AX_DEGREES_TO_RADIANS(90), 50, 2.0f, 2.0f, - Color4F(0.0f, 1.0f, 0.0f, 1.0f)); + Color(0.0f, 1.0f, 0.0f, 1.0f)); // drawNode segment - drawNode->drawSegment(Vec2(20.0f, s.height), Vec2(20.0f, s.height / 2), 10, Color4F(0.0f, 1.0f, 0.0f, 1.0f)); + drawNode->drawSegment(Vec2(20.0f, s.height), Vec2(20.0f, s.height / 2), 10, Color(0.0f, 1.0f, 0.0f, 1.0f)); drawNode->drawSegment(Vec2(10.0f, s.height / 2), Vec2(s.width / 2, s.height / 2), 40, - Color4F(1.0f, 0.0f, 1.0f, 0.5f)); + Color(1.0f, 0.0f, 1.0f, 0.5f)); // drawNode triangle drawNode->drawTriangle(Vec2(10.0f, 10.0f), Vec2(70.0f, 30.0f), Vec2(100.0f, 140.0f), - Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 0.5f)); + Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 0.5f)); for (int i = 0; i < 100; i++) { drawNode->drawPoint(Vec2(i * 7.0f, 5.0f), (float)i / 5 + 1, - Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); + Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); } - drawNode->drawLine(Vec2(0.0f, s.height), Vec2(s.width, s.height - 20), Color4F::YELLOW); - drawNode->drawLine(Vec2(0.0f, 0.0f), Vec2(s.width, s.height - 20), Color4F::YELLOW); + + drawNode->drawLine(Vec2(0.0f, s.height), Vec2(s.width, s.height - 20), Color::YELLOW); + drawNode->drawLine(Vec2(0.0f, 0.0f), Vec2(s.width, s.height - 20), Color::YELLOW); drawNode->runAction(RepeatForever::create(Sequence::create(FadeIn::create(1.2f), FadeOut::create(1.2f), NULL))); } @@ -2246,13 +2247,13 @@ void DrawNodeThicknessStressTest::update(float dt) drawNode->clear(); - drawNode->drawCircle(VisibleRect::center(), 60, AX_DEGREES_TO_RADIANS(77), 30, false, Color4F::GREEN, + drawNode->drawCircle(VisibleRect::center(), 60, AX_DEGREES_TO_RADIANS(77), 30, false, Color::GREEN, negativThickness); - drawNode->drawLine(Vec2(0.0f, size.height), Vec2(size.width, size.height - 20), Color4F::YELLOW, negativThickness); + drawNode->drawLine(Vec2(0.0f, size.height), Vec2(size.width, size.height - 20), Color::YELLOW, negativThickness); // drawNode a rectangles - drawNode->drawRect(Vec2(123, 123), Vec2(227, 227), Color4F(1, 1, 0, 1), negativThickness); + drawNode->drawRect(Vec2(123, 123), Vec2(227, 227), Color(1, 1, 0, 1), negativThickness); Vec2 vertices24[] = { {45.750000f, 144.375000f}, {75.500000f, 136.875000f}, {75.500000f, 159.125000f}, {100.250000f, 161.375000f}, @@ -2269,27 +2270,27 @@ void DrawNodeThicknessStressTest::update(float dt) {135.250000f, 108.625000f}, {151.000000f, 124.125000f}, {90.500000f, 131.875000f}, {113.250000f, 120.875000f}, {88.000000f, 116.875000f}, {106.000000f, 103.875000f}, {88.000000f, 97.875000f}, }; - drawNode->drawPolygon(vertices24, sizeof(vertices24) / sizeof(vertices24[0]), Color4B::TRANSPARENT, - negativThickness, Color4F::RED); + drawNode->drawPolygon(vertices24, sizeof(vertices24) / sizeof(vertices24[0]), Color::TRANSPARENT, + negativThickness, Color::RED); // open random color poly Vec2 vertices[] = {Vec2(0.0f, 0.0f), Vec2(50.0f, 50.0f), Vec2(100.0f, 50.0f), Vec2(100.0f, 100.0f), Vec2(50.0f, 100.0f)}; - drawNode->drawPoly(vertices, 5, false, Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f), + drawNode->drawPoly(vertices, 5, false, Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f), negativThickness); // closed random color poly Vec2 vertices2[] = {Vec2(30.0f, 130.0f), Vec2(30.0f, 230.0f), Vec2(50.0f, 200.0f)}; - drawNode->drawPoly(vertices2, 3, true, Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f), + drawNode->drawPoly(vertices2, 3, true, Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f), negativThickness); // drawNode some beziers drawNode->drawQuadBezier(Vec2(size.width - 150, size.height - 150), Vec2(size.width - 70, size.height - 10), - Vec2(size.width - 10, size.height - 10), 10, Color4F::BLUE, negativThickness); + Vec2(size.width - 10, size.height - 10), 10, Color::BLUE, negativThickness); drawNode->drawCubicBezier(VisibleRect::center(), Vec2(VisibleRect::center().x + 30, VisibleRect::center().y + 50), Vec2(VisibleRect::center().x + 60, VisibleRect::center().y - 50), VisibleRect::right(), - 100, Color4F::WHITE, negativThickness); + 100, Color::WHITE, negativThickness); auto array = ax::PointArray::create(20); array->addControlPoint(Vec2(0.0f, 0.0f)); @@ -2299,7 +2300,7 @@ void DrawNodeThicknessStressTest::update(float dt) array->addControlPoint(Vec2(80.0f, size.height - 80)); array->addControlPoint(Vec2(80.0f, 80.0f)); array->addControlPoint(Vec2(size.width / 2, size.height / 2)); - drawNode->drawCardinalSpline(array, 0.5f, 50, Color4F::MAGENTA, negativThickness); + drawNode->drawCardinalSpline(array, 0.5f, 50, Color::MAGENTA, negativThickness); auto array2 = ax::PointArray::create(20); array2->addControlPoint(Vec2(size.width / 2, 30.0f)); @@ -2307,28 +2308,28 @@ void DrawNodeThicknessStressTest::update(float dt) array2->addControlPoint(Vec2(size.width - 80, size.height - 80)); array2->addControlPoint(Vec2(size.width / 2, size.height - 80)); array2->addControlPoint(Vec2(size.width / 2, 30.0f)); - drawNode->drawCatmullRom(array2, 50, Color4F::ORANGE, negativThickness); + drawNode->drawCatmullRom(array2, 50, Color::ORANGE, negativThickness); auto s = Director::getInstance()->getWinSize(); drawNode->drawPoint(Vec2(s.width / 2 - 120, s.height / 2 - 120), negativThickness, - Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1)); + Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1)); // drawNode 4 small points Vec2 position[] = {Vec2(60, 60), Vec2(70, 70), Vec2(60, 70), Vec2(70, 60)}; - drawNode->drawPoints(position, 4, 5, Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1), DrawNode::Rect); + drawNode->drawPoints(position, 4, 5, Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1), DrawNode::Rect); Vec2 position1[] = {Vec2(100, 100), Vec2(170, 170), Vec2(260, 170), Vec2(170, 260)}; - drawNode->drawPoints(position1, 4, 25, Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1), DrawNode::Rect); + drawNode->drawPoints(position1, 4, 25, Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1), DrawNode::Rect); // drawNode a rectangle - drawNode->drawRect(Vec2(23, 23), Vec2(7, 7), Color4F(1, 1, 0, 1), negativThickness); + drawNode->drawRect(Vec2(23, 23), Vec2(7, 7), Color(1, 1, 0, 1), negativThickness); // drawNode 10 circles for (int i = 0; i < 10; i++) { drawNode->drawDot(Vec2(s.width / 2, s.height / 2), negativThickness, - Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); + Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); } // star poly (doesn't trigger bug... order is important un tesselation is supported. @@ -2347,27 +2348,27 @@ void DrawNodeThicknessStressTest::update(float dt) Vec2(o - h, o + w), // left spike }; - drawNode->drawPolygon(star, sizeof(star) / sizeof(star[0]), Color4F(1.0f, 0.0f, 0.0f, 0.5f), 1, - Color4F(0.0f, 0.0f, 1.0f, 1.0f)); + drawNode->drawPolygon(star, sizeof(star) / sizeof(star[0]), Color(1.0f, 0.0f, 0.0f, 0.5f), 1, + Color(0.0f, 0.0f, 1.0f, 1.0f)); } // drawNode a solid polygon Vec2 vertices3[] = {Vec2(60.0f, 160.0f), Vec2(70.0f, 190.0f), Vec2(100.0f, 190.0f), Vec2(90.0f, 160.0f)}; - drawNode->drawSolidPoly(vertices3, 4, Color4F(1.0f, 1.0f, 0.0f, 1.0f)); + drawNode->drawSolidPoly(vertices3, 4, Color(1.0f, 1.0f, 0.0f, 1.0f)); // drawNode a solid rectangle - drawNode->drawSolidRect(Vec2(10.0f, 10.0f), Vec2(20.0f, 20.0f), Color4F(1.0f, 1.0f, 0.0f, 1.0f)); + drawNode->drawSolidRect(Vec2(10.0f, 10.0f), Vec2(20.0f, 20.0f), Color(1.0f, 1.0f, 0.0f, 1.0f)); // drawNode a solid circle drawNode->drawSolidCircle(VisibleRect::center() + Vec2(140.0f, 0.0f), 40, AX_DEGREES_TO_RADIANS(90), 50, 2.0f, 2.0f, - Color4F(0.0f, 1.0f, 0.0f, 1.0f)); + Color(0.0f, 1.0f, 0.0f, 1.0f)); // drawNode segment - drawNode->drawSegment(Vec2(20.0f, s.height), Vec2(20.0f, s.height / 2), 10, Color4F(0.0f, 1.0f, 0.0f, 1.0f)); + drawNode->drawSegment(Vec2(20.0f, s.height), Vec2(20.0f, s.height / 2), 10, Color(0.0f, 1.0f, 0.0f, 1.0f)); // drawNode triangle drawNode->drawTriangle(Vec2(10.0f, 10.0f), Vec2(70.0f, 30.0f), Vec2(100.0f, 140.0f), - Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 0.5f)); + Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 0.5f)); } void DrawNodeThicknessStressTest::onEnter() @@ -2413,31 +2414,31 @@ void DrawNodePieTest::update(float dt) // Filled drawNode->drawPie(VisibleRect::center() - Vec2(190.0f, -35.0f), 40, sliderValue[sliderType::Rotation], - sliderValue[sliderType::AngleStart], sliderValue[sliderType::AngleEnd], 1.0f, 1.0f, Color4F::RED, - Color4F::BLUE, drawNode->DrawMode::Fill, sliderValue[sliderType::Thickness]); + sliderValue[sliderType::AngleStart], sliderValue[sliderType::AngleEnd], 1.0f, 1.0f, Color::RED, + Color::BLUE, drawNode->DrawMode::Fill, sliderValue[sliderType::Thickness]); // Outlined drawNode->drawPie(VisibleRect::center() - Vec2(95.0f, -35.0f), 40, sliderValue[sliderType::Rotation], sliderValue[sliderType::AngleStart], sliderValue[sliderType::AngleEnd], 1.0f, 1.0f, - Color4F::TRANSPARENT, Color4F::BLUE, drawNode->DrawMode::Outline, + Color::TRANSPARENT, Color::BLUE, drawNode->DrawMode::Outline, sliderValue[sliderType::Thickness]); // Line drawNode->drawPie(VisibleRect::center() + Vec2(0.0f, 35.0f), 40, sliderValue[sliderType::Rotation], sliderValue[sliderType::AngleStart], sliderValue[sliderType::AngleEnd], 1.0f, 1.0f, - Color4F::TRANSPARENT, Color4F::BLUE, drawNode->DrawMode::Line, + Color::TRANSPARENT, Color::BLUE, drawNode->DrawMode::Line, sliderValue[sliderType::Thickness]); // Semi drawNode->drawPie(VisibleRect::center() + Vec2(95.0f, 35.0f), 40, sliderValue[sliderType::Rotation], sliderValue[sliderType::AngleStart], sliderValue[sliderType::AngleEnd], 1.0f, 1.0f, - Color4F::TRANSPARENT, Color4F::BLUE, drawNode->DrawMode::Semi, + Color::TRANSPARENT, Color::BLUE, drawNode->DrawMode::Semi, sliderValue[sliderType::Thickness]); // Semi (Filled) drawNode->drawPie(VisibleRect::center() + Vec2(190.0f, 35.0f), 40, sliderValue[sliderType::Rotation], - sliderValue[sliderType::AngleStart], sliderValue[sliderType::AngleEnd], 1.0f, 1.0f, Color4F::RED, - Color4F::BLUE, drawNode->DrawMode::Semi, sliderValue[sliderType::Thickness]); + sliderValue[sliderType::AngleStart], sliderValue[sliderType::AngleEnd], 1.0f, 1.0f, Color::RED, + Color::BLUE, drawNode->DrawMode::Semi, sliderValue[sliderType::Thickness]); } void DrawNodePieTest::onEnter() @@ -2581,7 +2582,7 @@ void DrawNodeMethodsTest::drawAll() for (int i = 0; i < 100; i++) { drawNode->drawLine(Vec2(-size.x / 2, -size.y / 2 + i * 4), Vec2(size.x - 50, -size.y / 2 + i * 4), - Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f), + Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f), sliderValue[sliderType::Thickness]); } @@ -2594,7 +2595,7 @@ void DrawNodeMethodsTest::drawAll() { rec = Vec2(i * 3, i * 3); drawNode->drawRect(center / 2 - rec, center / 2 + rec, - Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f), + Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f), sliderValue[sliderType::Thickness]); } @@ -2605,7 +2606,7 @@ void DrawNodeMethodsTest::drawAll() for (int i = 0; i < 100; i++) { drawNode->drawCircle(VisibleRect::center(), 3 * i, AX_DEGREES_TO_RADIANS(90), i, false, 1.0f, 1.0f, - Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f), + Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f), sliderValue[sliderType::Thickness]); } @@ -2614,10 +2615,10 @@ void DrawNodeMethodsTest::drawAll() case drawMethodes::QuadBezier: { drawNode->drawQuadBezier(Vec2(size.width - 150, size.height - 150), Vec2(size.width - 70, size.height - 10), - Vec2(size.width - 10, size.height - 10), 10, Color4F::BLUE, + Vec2(size.width - 10, size.height - 10), 10, Color::BLUE, sliderValue[sliderType::Thickness]); drawNode->drawQuadBezier(Vec2(0.0f + 100, size.height - 100), Vec2(size.width / 2, size.height / 2), - Vec2(size.width - 100, size.height - 100), 50, Color4F::RED, + Vec2(size.width - 100, size.height - 100), 50, Color::RED, sliderValue[sliderType::Thickness]); for (int i = 0; i < 360;) @@ -2627,7 +2628,8 @@ void DrawNodeMethodsTest::drawAll() Vec2 p3 = pts->getControlPointAtIndex(i); drawNode->properties.setPosition(Vec2(-100, -100)); - drawNode->drawQuadBezier(p1, p2, p3, 30, Color4B::RED, sliderValue[sliderType::Thickness]); + drawNode->drawQuadBezier(p1, p2, p3, 30, Color::RED, + sliderValue[sliderType::Thickness]); } for (int i = 0; i < 360;) @@ -2637,7 +2639,8 @@ void DrawNodeMethodsTest::drawAll() Vec2 p3 = pts2->getControlPointAtIndex(i); drawNode->properties.setPosition(Vec2(-100, -100)); - drawNode->drawQuadBezier(p1, p2, p3, 30, Color4B::GREEN, sliderValue[sliderType::Thickness]); + drawNode->drawQuadBezier(p1, p2, p3, 30, Color::GREEN, + sliderValue[sliderType::Thickness]); } break; @@ -2647,10 +2650,10 @@ void DrawNodeMethodsTest::drawAll() drawNode->drawCubicBezier(VisibleRect::center(), Vec2(VisibleRect::center().x + 30, VisibleRect::center().y + 50), Vec2(VisibleRect::center().x + 60, VisibleRect::center().y - 50), - VisibleRect::right(), 20, Color4F::WHITE, sliderValue[sliderType::Thickness]); + VisibleRect::right(), 20, Color::WHITE, sliderValue[sliderType::Thickness]); drawNode->drawCubicBezier(Vec2(size.width - 250, 40.0f), Vec2(size.width - 70, 100.0f), Vec2(size.width - 30, 250.0f), Vec2(size.width - 10, size.height - 50), 20, - Color4F::GRAY, sliderValue[sliderType::Thickness]); + Color::GRAY, sliderValue[sliderType::Thickness]); for (int i = 0; i < 360;) { @@ -2659,7 +2662,7 @@ void DrawNodeMethodsTest::drawAll() Vec2 p3 = pts->getControlPointAtIndex(i++); Vec2 p4 = pts->getControlPointAtIndex(i); drawNode->properties.setPosition(Vec2(-100, -100)); - drawNode->drawCubicBezier(p1, p2, p3, p4, 120, Color4F::RED, sliderValue[sliderType::Thickness]); + drawNode->drawCubicBezier(p1, p2, p3, p4, 120, Color::RED, sliderValue[sliderType::Thickness]); } for (int i = 0; i < 360;) @@ -2669,7 +2672,7 @@ void DrawNodeMethodsTest::drawAll() Vec2 p3 = pts2->getControlPointAtIndex(i++); Vec2 p4 = pts2->getControlPointAtIndex(i); drawNode->properties.setPosition(Vec2(-100, -100)); - drawNode->drawCubicBezier(p1, p2, p3, p4, 120, Color4F::GREEN, sliderValue[sliderType::Thickness]); + drawNode->drawCubicBezier(p1, p2, p3, p4, 120, Color::GREEN, sliderValue[sliderType::Thickness]); } break; @@ -2685,7 +2688,7 @@ void DrawNodeMethodsTest::drawAll() array->addControlPoint(Vec2(80.0f, size.height - 80)); array->addControlPoint(Vec2(80.0f, 80.0f)); array->addControlPoint(Vec2(size.width / 2, size.height / 2)); - drawNode->drawCardinalSpline(array, 0.5f, 120, Color4F::MAGENTA, sliderValue[sliderType::Thickness]); + drawNode->drawCardinalSpline(array, 0.5f, 120, Color::MAGENTA, sliderValue[sliderType::Thickness]); auto array2 = ax::PointArray::create(5); array2->addControlPoint(Vec2(size.width / 2, 80.0f)); @@ -2693,11 +2696,11 @@ void DrawNodeMethodsTest::drawAll() array2->addControlPoint(Vec2(size.width - 80, size.height - 80)); array2->addControlPoint(Vec2(size.width / 2, size.height - 80)); array2->addControlPoint(Vec2(size.width / 2, 80.0f)); - drawNode->drawCardinalSpline(array2, 5.0f, 120, Color4F::ORANGE, sliderValue[sliderType::Thickness]); + drawNode->drawCardinalSpline(array2, 5.0f, 120, Color::ORANGE, sliderValue[sliderType::Thickness]); drawNode->properties.setPosition(Vec2(-100, -100)); - drawNode->drawCardinalSpline(pts, 0.001f, 360, Color4F::RED, sliderValue[sliderType::Thickness]); - drawNode->drawCardinalSpline(pts2, 0.001f, 360, Color4F::GREEN, sliderValue[sliderType::Thickness]); + drawNode->drawCardinalSpline(pts, 0.001f, 360, Color::RED, sliderValue[sliderType::Thickness]); + drawNode->drawCardinalSpline(pts2, 0.001f, 360, Color::GREEN, sliderValue[sliderType::Thickness]); break; } @@ -2709,7 +2712,7 @@ void DrawNodeMethodsTest::drawAll() array2->addControlPoint(Vec2(size.width - 80, size.height - 80)); array2->addControlPoint(Vec2(size.width / 2, size.height - 80)); array2->addControlPoint(Vec2(size.width / 2, 80.0f)); - drawNode->drawCatmullRom(array2, 20, Color4F::ORANGE, sliderValue[sliderType::Thickness]); + drawNode->drawCatmullRom(array2, 20, Color::ORANGE, sliderValue[sliderType::Thickness]); auto array = ax::PointArray::create(7); array->addControlPoint(Vec2(0.0f, 0.0f)); @@ -2719,11 +2722,11 @@ void DrawNodeMethodsTest::drawAll() array->addControlPoint(Vec2(80.0f, size.height - 80)); array->addControlPoint(Vec2(80.0f, 80.0f)); array->addControlPoint(Vec2(size.width / 2, size.height / 2)); - drawNode->drawCatmullRom(array, 20, Color4F::MAGENTA, sliderValue[sliderType::Thickness]); + drawNode->drawCatmullRom(array, 20, Color::MAGENTA, sliderValue[sliderType::Thickness]); drawNode->properties.setPosition(Vec2(-100, -100)); - drawNode->drawCatmullRom(pts, 360, Color4F::RED, sliderValue[sliderType::Thickness]); - drawNode->drawCatmullRom(pts2, 360, Color4F::GREEN, sliderValue[sliderType::Thickness]); + drawNode->drawCatmullRom(pts, 360, Color::RED, sliderValue[sliderType::Thickness]); + drawNode->drawCatmullRom(pts2, 360, Color::GREEN, sliderValue[sliderType::Thickness]); break; } @@ -2731,42 +2734,42 @@ void DrawNodeMethodsTest::drawAll() { Vec2 vertices[5] = {{0.0f, 0.0f}, {50.0f, 50.0f}, {100.0f, 50.0f}, {100.0f, 100.0f}, {50.0f, 100.0f}}; drawNode->properties.setPosition(Vec2(-200, -300)); - drawNode->drawPoly(vertices, 5, false, Color4B::BLUE, sliderValue[sliderType::Thickness]); + drawNode->drawPoly(vertices, 5, false, Color::BLUE, sliderValue[sliderType::Thickness]); Vec2 vertices2[3] = {{30.0f, 130.0f}, {30.0f, 230.0f}, {50.0f, 200.0f}}; - drawNode->drawPoly(vertices2, 3, true, Color4B::GREEN, sliderValue[sliderType::Thickness]); + drawNode->drawPoly(vertices2, 3, true, Color::GREEN, sliderValue[sliderType::Thickness]); drawNode->properties.setDefaultValues(); - drawNode->drawPoly(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), true, Color4B::RED, + drawNode->drawPoly(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), true, Color::RED, sliderValue[sliderType::Thickness]); drawNode->properties.setPosition(Vec2(0, -300)); drawNode->properties.setRotation(sliderValue[sliderType::Rotation]); - drawNode->drawPoly(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), true, Color4F::GREEN, + drawNode->drawPoly(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), true, Color::GREEN, sliderValue[sliderType::Thickness]); drawNode->properties.setPosition(Vec2(-100, -300)); drawNode->properties.setRotation(sliderValue[sliderType::Rotation]); drawNode->properties.setCenter(vertices1[0]); - drawNode->drawPoly(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), true, Color4F::MAGENTA, + drawNode->drawPoly(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), true, Color::MAGENTA, sliderValue[sliderType::Thickness]); drawNode->properties.setPosition(Vec2(200, 0)); - drawNode->drawPoly(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), true, Color4F::RED, + drawNode->drawPoly(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), true, Color::RED, sliderValue[sliderType::Thickness]); drawNode->properties.setPosition(Vec2(0.0f, -300.0f)); drawNode->properties.setRotation(rotation / 10.0f); drawNode->properties.setScale(Vec2(2.0f, 2.0f)); drawNode->properties.setCenter(vertices1[4]); - drawNode->drawPoly(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), true, Color4F::BLUE, + drawNode->drawPoly(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), true, Color::BLUE, sliderValue[sliderType::Thickness]); drawNode->properties.setRotation(rotation); - drawNode->drawPoly(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), true, Color4F::YELLOW, + drawNode->drawPoly(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), true, Color::YELLOW, sliderValue[sliderType::Thickness]); drawNode->properties.setRotation(-rotation / 5); - drawNode->drawPoly(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), true, Color4F::WHITE, + drawNode->drawPoly(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), true, Color::WHITE, sliderValue[sliderType::Thickness]); drawNode->properties.setDefaultValues(); - drawNode->drawPoly(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), true, Color4F::GREEN, + drawNode->drawPoly(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), true, Color::GREEN, sliderValue[sliderType::Thickness]); break; } @@ -2774,34 +2777,34 @@ void DrawNodeMethodsTest::drawAll() { drawNode->properties.setPosition(Vec2(0, -300)); drawNode->properties.setRotation(sliderValue[sliderType::Rotation]); - drawNode->drawPolygon(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), Color4F::GREEN, - sliderValue[sliderType::Thickness], Color4F::YELLOW); + drawNode->drawPolygon(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), Color::GREEN, + sliderValue[sliderType::Thickness], Color::YELLOW); drawNode->properties.setPosition(Vec2(-100, -300)); drawNode->properties.setRotation(sliderValue[sliderType::Rotation]); drawNode->properties.setCenter(vertices1[0]); - drawNode->drawPolygon(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), Color4F::MAGENTA, - sliderValue[sliderType::Thickness], Color4F::GRAY); + drawNode->drawPolygon(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), Color::MAGENTA, + sliderValue[sliderType::Thickness], Color::GRAY); drawNode->properties.setPosition(Vec2(200, 0)); - drawNode->drawPolygon(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), Color4F::RED, - sliderValue[sliderType::Thickness], Color4F::YELLOW); + drawNode->drawPolygon(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), Color::RED, + sliderValue[sliderType::Thickness], Color::YELLOW); drawNode->properties.setPosition(Vec2(0.0f, -300.0f)); drawNode->properties.setRotation(rotation / 10.0f); drawNode->properties.setScale(Vec2(2.0f, 2.0f)); drawNode->properties.setCenter(vertices1[4]); - drawNode->drawPolygon(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), Color4F::BLUE, - sliderValue[sliderType::Thickness], Color4F::WHITE); + drawNode->drawPolygon(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), Color::BLUE, + sliderValue[sliderType::Thickness], Color::WHITE); drawNode->properties.setRotation(rotation); - drawNode->drawPolygon(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), Color4F::YELLOW, - sliderValue[sliderType::Thickness], Color4F::GREEN); + drawNode->drawPolygon(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), Color::YELLOW, + sliderValue[sliderType::Thickness], Color::GREEN); ; drawNode->properties.setRotation(-rotation / 5); - drawNode->drawPolygon(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), Color4F::WHITE, - sliderValue[sliderType::Thickness], Color4F::YELLOW); + drawNode->drawPolygon(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), Color::WHITE, + sliderValue[sliderType::Thickness], Color::YELLOW); drawNode->properties.setDefaultValues(); - drawNode->drawPolygon(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), Color4F::GREEN, - sliderValue[sliderType::Thickness], Color4F::BLUE); + drawNode->drawPolygon(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), Color::GREEN, + sliderValue[sliderType::Thickness], Color::BLUE); break; } @@ -2811,7 +2814,7 @@ void DrawNodeMethodsTest::drawAll() { drawNode->drawDot(Vec2(AXRANDOM_MINUS1_1() * 400 + 200, AXRANDOM_MINUS1_1() * 400), 20 + sliderValue[sliderType::Thickness], - Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); + Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); } break; @@ -2822,7 +2825,7 @@ void DrawNodeMethodsTest::drawAll() { drawNode->drawPoint(Vec2(AXRANDOM_MINUS1_1() * 400 + 200, AXRANDOM_MINUS1_1() * 400), 30 + sliderValue[sliderType::Thickness], - Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); + Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); } break; } @@ -2841,20 +2844,20 @@ void DrawNodeMethodsTest::drawAll() {70 + AXRANDOM_MINUS1_1() * VisibleRect::rightTop().x, 70 + AXRANDOM_MINUS1_1() * VisibleRect::rightTop().y / 2}}; drawNode->drawPoints(position, 4, 10 + 2 * sliderValue[sliderType::Thickness], - Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1)); + Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1)); } break; } case drawMethodes::Triangle: { - static Color4B color3[] = {Color4B::GREEN, Color4B::BLUE, Color4B::RED}; + static Color color3[] = {Color::GREEN, Color::BLUE, Color::RED}; drawNode->properties.setPosition(center); drawNode->properties.setScale(Vec2(10, 10)); { drawNode->drawTriangle(Vec2(AXRANDOM_MINUS1_1(), AXRANDOM_MINUS1_1()) * 20, Vec2(AXRANDOM_MINUS1_1(), AXRANDOM_MINUS1_1()) * 20, - Vec2(AXRANDOM_MINUS1_1(), AXRANDOM_MINUS1_1()) * 20, Color4B::RED); + Vec2(AXRANDOM_MINUS1_1(), AXRANDOM_MINUS1_1()) * 20, Color::RED); } { Vec2 triangle[] = {Vec2(AXRANDOM_MINUS1_1(), AXRANDOM_MINUS1_1()) * 20, @@ -2877,11 +2880,11 @@ void DrawNodeMethodsTest::drawAll() drawNode->drawSolidTriangle(Vec2(AXRANDOM_MINUS1_1(), AXRANDOM_MINUS1_1()) * 20, Vec2(AXRANDOM_MINUS1_1(), AXRANDOM_MINUS1_1()) * 20, - Vec2(AXRANDOM_MINUS1_1(), AXRANDOM_MINUS1_1()) * 20, Color4B::RED, - Color4B::BLUE, sliderValue[sliderType::Thickness]); + Vec2(AXRANDOM_MINUS1_1(), AXRANDOM_MINUS1_1()) * 20, Color::RED, + Color::BLUE, sliderValue[sliderType::Thickness]); drawNode->properties.setPosition(center - Vec2(200, 200)); - drawNode->drawSolidTriangle(triangle, Color4B::GREEN, Color4B::BLUE, sliderValue[sliderType::Thickness]); + drawNode->drawSolidTriangle(triangle, Color::GREEN, Color::BLUE, sliderValue[sliderType::Thickness]); } break; } @@ -2894,17 +2897,17 @@ void DrawNodeMethodsTest::drawAll() int yy1 = 150; int yy = 0; drawNode->drawSegment(Vec2(-150.0f, yy - yy1), Vec2(200, yy - yy1), 20 + 5 * sliderValue[sliderType::Thickness], - Color4F::GREEN, DrawNode::Round, DrawNode::Round); + Color::GREEN, DrawNode::Round, DrawNode::Round); labelRound->setPosition(Vec2(250.0f, 85)); yy += 170; drawNode->drawSegment(Vec2(-150.0f, yy - yy1), Vec2(200, yy - yy1), 20 + 5 * sliderValue[sliderType::Thickness], - Color4F::BLUE, DrawNode::Square, DrawNode::Square); + Color::BLUE, DrawNode::Square, DrawNode::Square); labelSquare->setPosition(Vec2(250.0f, 170)); yy += 170; drawNode->drawSegment(Vec2(-150.0f, yy - yy1), Vec2(200, yy - yy1), 20 + 5 * sliderValue[sliderType::Thickness], - Color4F::RED, DrawNode::Butt, DrawNode::Butt); + Color::RED, DrawNode::Butt, DrawNode::Butt); labelButt->setPosition(Vec2(250.0f, 255)); break; @@ -2918,30 +2921,30 @@ void DrawNodeMethodsTest::drawAll() drawNode->drawSolidCircle( VisibleRect::center(), AXRANDOM_0_1() * 200, AX_DEGREES_TO_RADIANS(AXRANDOM_MINUS1_1() * 90), 10, 1.0f, - 1.0f, Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f), sliderValue[sliderType::Thickness], - Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); + 1.0f, Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f), sliderValue[sliderType::Thickness], + Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); drawNode->drawSolidCircle( VisibleRect::center() + pos, AXRANDOM_0_1() * 200, AX_DEGREES_TO_RADIANS(AXRANDOM_MINUS1_1() * 90), 10, - 1.0f, 1.0f, Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f), - sliderValue[sliderType::Thickness], Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); + 1.0f, 1.0f, Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f), + sliderValue[sliderType::Thickness], Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); drawNode->drawSolidCircle( VisibleRect::center() - pos, AXRANDOM_0_1() * 200, AX_DEGREES_TO_RADIANS(AXRANDOM_MINUS1_1() * 90), 10, - 1.0f, 1.0f, Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f), - sliderValue[sliderType::Thickness], Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); + 1.0f, 1.0f, Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f), + sliderValue[sliderType::Thickness], Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); // for (int i = 5; i > 1; i--) //{ // drawNode->drawSolidCircle( // VisibleRect::center(), 3 * i, AX_DEGREES_TO_RADIANS(90), AXRANDOM_0_1() * 20.f + 20.f, 1.0f, 1.0f, - // Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f), sliderValue[sliderType::Thickness], - // Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 0.5f)); + // Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f), sliderValue[sliderType::Thickness], + // Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 0.5f)); // drawNode->drawSolidCircle( // VisibleRect::center() + pos, AXRANDOM_0_1() * 200, AX_DEGREES_TO_RADIANS(AXRANDOM_MINUS1_1() * 90), - // 10, 1.0f, 1.0f, Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f), - // sliderValue[sliderType::Thickness], Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); + // 10, 1.0f, 1.0f, Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f), + // sliderValue[sliderType::Thickness], Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); //} break; @@ -2955,14 +2958,14 @@ void DrawNodeMethodsTest::drawAll() drawNode->properties.setRotation(rotation / 10.0f); drawNode->properties.setScale(Vec2(2.0f, 2.0f)); drawNode->properties.setCenter(vertices1[4]); - drawNode->drawSolidPoly(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), Color4F::BLUE); + drawNode->drawSolidPoly(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), Color::BLUE); drawNode->properties.setRotation(rotation); - drawNode->drawSolidPoly(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), Color4F::YELLOW); + drawNode->drawSolidPoly(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), Color::YELLOW); drawNode->properties.setRotation(-rotation / 5); - drawNode->drawSolidPoly(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), Color4F::WHITE); + drawNode->drawSolidPoly(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), Color::WHITE); drawNode->properties.setDefaultValues(); - drawNode->drawSolidPoly(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), Color4F::GREEN); + drawNode->drawSolidPoly(vertices1, sizeof(vertices1) / sizeof(vertices1[0]), Color::GREEN); break; } @@ -2974,7 +2977,7 @@ void DrawNodeMethodsTest::drawAll() AXRANDOM_MINUS1_1() * VisibleRect::rightTop().y); drawNode->drawSolidRect( pos, pos + Vec2(20.0f * sliderValue[sliderType::Thickness], 20.0f * sliderValue[sliderType::Thickness]), - Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 0.5f), sliderValue[sliderType::Thickness]); + Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 0.5f), sliderValue[sliderType::Thickness]); } break; @@ -2989,29 +2992,29 @@ void DrawNodeMethodsTest::drawAll() drawNode->properties.setRotation(rotation + 45); drawNode->properties.setCenter(gear1); - drawNode->drawStar(Vec2(gear1), 30, 60, 8, Color4F::BLUE, 4.0); + drawNode->drawStar(Vec2(gear1), 30, 60, 8, Color::BLUE, 4.0); drawNode->properties.setRotation(-rotation); drawNode->properties.setCenter(gear2); - drawNode->drawStar(gear2, 30, 60, 8, Color4F::GREEN, 4.0); + drawNode->drawStar(gear2, 30, 60, 8, Color::GREEN, 4.0); drawNode->properties.setDefaultValues(); - drawNode->drawLine(gear2, gear1, Color4F::RED, sliderValue[sliderType::Thickness]); // line + drawNode->drawLine(gear2, gear1, Color::RED, sliderValue[sliderType::Thickness]); // line drawNode->properties.setCenter(gear4); drawNode->properties.setRotation(rotation + 45); - drawNode->drawStar(gear3, 30, 60, 18, Color4F::RED, 1.0); - drawNode->drawLine(gear3, gear4, Color4F::YELLOW, sliderValue[sliderType::Thickness]); // line + drawNode->drawStar(gear3, 30, 60, 18, Color::RED, 1.0); + drawNode->drawLine(gear3, gear4, Color::YELLOW, sliderValue[sliderType::Thickness]); // line // drawNode->properties.setDefaultValues(); drawNode->properties.setRotation(rotation - 45); drawNode->properties.setCenter(gear4); - drawNode->drawStar(gear4, 40, 60, 60, Color4F::GREEN, 1.0); + drawNode->drawStar(gear4, 40, 60, 60, Color::GREEN, 1.0); drawNode->properties.setRotation(rotation); drawNode->properties.setCenter(Vec2(-110, 250)); - drawNode->drawStar(Vec2(-110, 250), 30, 70, 5, Color4F::GREEN, 1.0); + drawNode->drawStar(Vec2(-110, 250), 30, 70, 5, Color::GREEN, 1.0); drawNode->properties.setCenter(Vec2(-150, 100)); - drawNode->drawStar(Vec2(-150, 100), 80, 100, 40, Color4F::GREEN, 1.0); + drawNode->drawStar(Vec2(-150, 100), 80, 100, 40, Color::GREEN, 1.0); drawNode->properties.setCenter(Vec2(-150, -100)); - drawNode->drawStar(Vec2(-150, -100), 5, 70, 3, Color4F::GREEN, 1.0); + drawNode->drawStar(Vec2(-150, -100), 5, 70, 3, Color::GREEN, 1.0); drawNode->properties.setRotation(0); for (int i = 0; i < 10; i++) @@ -3020,7 +3023,7 @@ void DrawNodeMethodsTest::drawAll() drawNode->properties.setPosition(Vec2(ppp)); drawNode->drawStar( Vec2::ZERO, 40, 60, AXRANDOM_0_1() * 60 + 3, - Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), sliderValue[sliderType::Thickness])); + Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), sliderValue[sliderType::Thickness])); } break; } @@ -3033,29 +3036,29 @@ void DrawNodeMethodsTest::drawAll() drawNode->properties.setRotation(rotation + 45); drawNode->properties.setCenter(gear1); - drawNode->drawSolidStar(Vec2(gear1), 30, 60, 8, Color4F::BLUE, Color4F::YELLOW, 4.0); + drawNode->drawSolidStar(Vec2(gear1), 30, 60, 8, Color::BLUE, Color::YELLOW, 4.0); drawNode->properties.setRotation(-rotation); drawNode->properties.setCenter(gear2); - drawNode->drawSolidStar(gear2, 30, 60, 8, Color4F::GREEN, Color4F::YELLOW, 4.0); + drawNode->drawSolidStar(gear2, 30, 60, 8, Color::GREEN, Color::YELLOW, 4.0); drawNode->properties.setDefaultValues(); - drawNode->drawLine(gear2, gear1, Color4F::RED, sliderValue[sliderType::Thickness]); // line + drawNode->drawLine(gear2, gear1, Color::RED, sliderValue[sliderType::Thickness]); // line drawNode->properties.setCenter(gear4); drawNode->properties.setRotation(rotation + 45); - drawNode->drawSolidStar(gear3, 30, 60, 18, Color4F::RED, Color4F::YELLOW, 1.0); - drawNode->drawLine(gear3, gear4, Color4F::YELLOW, sliderValue[sliderType::Thickness]); // line + drawNode->drawSolidStar(gear3, 30, 60, 18, Color::RED, Color::YELLOW, 1.0); + drawNode->drawLine(gear3, gear4, Color::YELLOW, sliderValue[sliderType::Thickness]); // line drawNode->properties.setDefaultValues(); drawNode->properties.setRotation(rotation - 45); drawNode->properties.setCenter(gear4); - drawNode->drawSolidStar(gear4, 40, 60, 60, Color4F::GREEN, Color4F::YELLOW, 1.0); + drawNode->drawSolidStar(gear4, 40, 60, 60, Color::GREEN, Color::YELLOW, 1.0); drawNode->properties.setRotation(rotation); drawNode->properties.setCenter(Vec2(-110, 250)); - drawNode->drawSolidStar(Vec2(-110, 250), 30, 70, 5, Color4F::GREEN, Color4F::YELLOW, 1.0); + drawNode->drawSolidStar(Vec2(-110, 250), 30, 70, 5, Color::GREEN, Color::YELLOW, 1.0); drawNode->properties.setCenter(Vec2(-150, 100)); - drawNode->drawSolidStar(Vec2(-150, 100), 80, 100, 40, Color4F::GREEN, Color4F::YELLOW, 1.0); + drawNode->drawSolidStar(Vec2(-150, 100), 80, 100, 40, Color::GREEN, Color::YELLOW, 1.0); drawNode->properties.setCenter(Vec2(-150, -100)); - drawNode->drawSolidStar(Vec2(-150, -100), 5, 70, 3, Color4F::GREEN, Color4F::YELLOW, 1.0); + drawNode->drawSolidStar(Vec2(-150, -100), 5, 70, 3, Color::GREEN, Color::YELLOW, 1.0); drawNode->properties.setRotation(0); for (int i = 0; i < 10; i++) @@ -3063,8 +3066,8 @@ void DrawNodeMethodsTest::drawAll() Vec2 ppp = Vec2(AXRANDOM_MINUS1_1() * size.x / 2, AXRANDOM_MINUS1_1() * size.y / 2); drawNode->properties.setPosition(Vec2(ppp)); drawNode->drawSolidStar(Vec2::ZERO, 40, 60, AXRANDOM_0_1() * 60 + 3, - Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f), - Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); + Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f), + Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); } break; @@ -3097,7 +3100,7 @@ void DrawNodeDrawInWrongOrder_Issue1888::update(float dt) drawNode->clear(); - drawNode->drawLine(Vec2(20, 140), Vec2(450, 110), Color4B::RED, 20.0f); + drawNode->drawLine(Vec2(20, 140), Vec2(450, 110), Color::RED, 20.0f); for (int i = 0; i < 200; i++) { @@ -3106,20 +3109,20 @@ void DrawNodeDrawInWrongOrder_Issue1888::update(float dt) {70 + AXRANDOM_0_1() * VisibleRect::rightTop().x, 70 + AXRANDOM_0_1() * VisibleRect::rightTop().y}, {60 + AXRANDOM_0_1() * VisibleRect::rightTop().x, 60 + AXRANDOM_0_1() * VisibleRect::rightTop().y}, {70 + AXRANDOM_0_1() * VisibleRect::rightTop().x, 70 + AXRANDOM_0_1() * VisibleRect::rightTop().y}}; - drawNode->drawPoints(position1, 4, 10, Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1)); + drawNode->drawPoints(position1, 4, 10, Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1)); } - drawNode->drawSolidRect(Vec2(150, 80), Vec2(400, 220), Color4B::YELLOW); + drawNode->drawSolidRect(Vec2(150, 80), Vec2(400, 220), Color::YELLOW); for (int i = 0; i < 50; i++) { drawNode->drawPoint(Vec2(i * 7.0f, 120.0f), (float)i / 5 + 1, - Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); + Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); } - drawNode->drawLine(Vec2(20, 100), Vec2(450, 220), Color4B::GREEN, 8.0f); + drawNode->drawLine(Vec2(20, 100), Vec2(450, 220), Color::GREEN, 8.0f); - drawNode->drawLine(Vec2(200, 100), Vec2(450, 250), Color4B::BLUE, 6.0f); + drawNode->drawLine(Vec2(200, 100), Vec2(450, 250), Color::BLUE, 6.0f); } DrawNodeAxmolTest2::DrawNodeAxmolTest2() @@ -3209,47 +3212,47 @@ void DrawNodeAxmolTest2::drawAllv2(DrawNode* drawNode, bool drawOrder) drawNode->properties.setDrawOrder(drawOrder); drawNode->drawPoint(Vec2(size.width / 2 - 120, size.height / 2 - 120), 10, - Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1)); + Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1)); drawNode->drawPoint(Vec2(size.width / 2 + 120, size.height / 2 + 120), 10, - Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1)); + Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1)); // drawNode 4 small points Vec2 position[] = {Vec2(60, 60), Vec2(70, 70), Vec2(60, 70), Vec2(70, 60)}; - drawNode->drawPoints(position, 4, 5, Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1)); + drawNode->drawPoints(position, 4, 5, Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1)); // drawNode a line - drawNode->drawLine(Vec2(0, 0), Vec2(size.width, size.height), Color4F(1.0, 0.0, 0.0, 0.5)); + drawNode->drawLine(Vec2(0, 0), Vec2(size.width, size.height), Color(1.0, 0.0, 0.0, 0.5)); // drawNode a rectangle - drawNode->drawRect(Vec2(23, 23), Vec2(7, 7), Color4F::RED); + drawNode->drawRect(Vec2(23, 23), Vec2(7, 7), Color::RED); drawNode->drawRect(Vec2(15, 30), Vec2(30, 15), Vec2(15, 0), Vec2(0, 15), - Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1)); + Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1)); // drawNode a circle drawNode->drawCircle(VisibleRect::center() + Vec2(140, 0), 100, AX_DEGREES_TO_RADIANS(90), 30, true, 1.0f, 2.0f, - Color4F(1.0f, 0.0f, 0.0f, 0.5f)); + Color(1.0f, 0.0f, 0.0f, 0.5f)); drawNode->drawCircle(VisibleRect::center() - Vec2(140, 0), 50, AX_DEGREES_TO_RADIANS(90), 30, false, - Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); + Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); // drawNode some beziers drawNode->drawQuadBezier(Vec2(size.width - 150, size.height - 150), Vec2(size.width - 70, size.height - 10), Vec2(size.width - 10, size.height - 10), 10, - Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 0.5f)); + Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 0.5f)); drawNode->drawQuadBezier(Vec2(0.0f, size.height), Vec2(size.width / 2, size.height / 2), Vec2(size.width, size.height), 50, - Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 0.5f)); + Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 0.5f)); drawNode->drawCubicBezier(VisibleRect::center(), Vec2(VisibleRect::center().x + 30, VisibleRect::center().y + 50), Vec2(VisibleRect::center().x + 60, VisibleRect::center().y - 50), VisibleRect::right(), - 100, Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 0.5f)); + 100, Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 0.5f)); drawNode->drawCubicBezier(Vec2(size.width - 250, 40.0f), Vec2(size.width - 70, 100.0f), Vec2(size.width - 30, 250.0f), Vec2(size.width - 10, size.height - 50), 10, - Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 0.5f)); + Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 0.5f)); auto array = PointArray::create(20); array->addControlPoint(Vec2(0.0f, 0.0f)); @@ -3259,7 +3262,7 @@ void DrawNodeAxmolTest2::drawAllv2(DrawNode* drawNode, bool drawOrder) array->addControlPoint(Vec2(80.0f, size.height - 80)); array->addControlPoint(Vec2(80.0f, 80.0f)); array->addControlPoint(Vec2(size.width / 2, size.height / 2)); - drawNode->drawCardinalSpline(array, 0.5f, 50, Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 0.5f)); + drawNode->drawCardinalSpline(array, 0.5f, 50, Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 0.5f)); auto array2 = PointArray::create(20); array2->addControlPoint(Vec2(size.width / 2, 30.0f)); @@ -3267,29 +3270,29 @@ void DrawNodeAxmolTest2::drawAllv2(DrawNode* drawNode, bool drawOrder) array2->addControlPoint(Vec2(size.width - 80, size.height - 80)); array2->addControlPoint(Vec2(size.width / 2, size.height - 80)); array2->addControlPoint(Vec2(size.width / 2, 30.0f)); - drawNode->drawCatmullRom(array2, 50, Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 0.5f)); + drawNode->drawCatmullRom(array2, 50, Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 0.5f)); // open random color poly Vec2 vertices[] = {Vec2(0.0f, 0.0f), Vec2(50.0f, 50.0f), Vec2(100.0f, 50.0f), Vec2(100.0f, 100.0f), Vec2(50.0f, 100.0f)}; - drawNode->drawPoly(vertices, 5, false, Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); + drawNode->drawPoly(vertices, 5, false, Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); // closed random color poly Vec2 vertices2[] = {Vec2(30.0f, 130.0f), Vec2(30.0f, 230.0f), Vec2(50.0f, 200.0f)}; - drawNode->drawPoly(vertices2, 3, true, Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); + drawNode->drawPoly(vertices2, 3, true, Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); // drawNode 10 circles for (int i = 0; i < 10; i++) { drawNode->drawDot(Vec2(size.width / 2, size.height / 2), 10.f * (10 - i), - Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); + Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); } // drawNode polygons Vec2 points[] = {Vec2(size.height / 4, 0.0f), Vec2(size.width, size.height / 5), Vec2(size.width / 3 * 2, size.height)}; - drawNode->drawPolygon(points, sizeof(points) / sizeof(points[0]), Color4F(1.0f, 0.0f, 0.0f, 0.5f), 4, - Color4F(0.0f, 0.0f, 1.0f, 0.5f)); + drawNode->drawPolygon(points, sizeof(points) / sizeof(points[0]), Color(1.0f, 0.0f, 0.0f, 0.5f), 4, + Color(0.0f, 0.0f, 1.0f, 0.5f)); // star poly (triggers buggs) { @@ -3303,8 +3306,8 @@ void DrawNodeAxmolTest2::drawAllv2(DrawNode* drawNode, bool drawOrder) //{o -h, o+w}, {o,o}, // left spike }; - drawNode->drawPolygon(star, sizeof(star) / sizeof(star[0]), Color4F(1.0f, 0.0f, 0.0f, 0.5f), 1, - Color4F(0.0f, 0.0f, 1.0f, 1.0f)); + drawNode->drawPolygon(star, sizeof(star) / sizeof(star[0]), Color(1.0f, 0.0f, 0.0f, 0.5f), 1, + Color(0.0f, 0.0f, 1.0f, 1.0f)); } // star poly (doesn't trigger bug... order is important un tesselation is supported. @@ -3324,39 +3327,40 @@ void DrawNodeAxmolTest2::drawAllv2(DrawNode* drawNode, bool drawOrder) Vec2(o, o), // left spike }; - drawNode->drawPolygon(star, sizeof(star) / sizeof(star[0]), Color4F(1.0f, 0.0f, 0.0f, 0.5f), 1, - Color4F(0.0f, 0.0f, 1.0f, 1.0f)); + drawNode->drawPolygon(star, sizeof(star) / sizeof(star[0]), Color(1.0f, 0.0f, 0.0f, 0.5f), 1, + Color(0.0f, 0.0f, 1.0f, 1.0f)); } // drawNode a solid polygon Vec2 vertices3[] = {Vec2(60.0f, 160.0f), Vec2(70.0f, 190.0f), Vec2(100.0f, 190.0f), Vec2(90.0f, 160.0f)}; - drawNode->drawSolidPoly(vertices3, 4, Color4F(1.0f, 1.0f, 0.0f, 1.0f)); + drawNode->drawSolidPoly(vertices3, 4, Color(1.0f, 1.0f, 0.0f, 1.0f)); // drawNode a solid rectangle - drawNode->drawSolidRect(Vec2(10.0f, 10.0f), Vec2(20.0f, 20.0f), Color4F(1.0f, 1.0f, 0.0f, 1.0f)); + drawNode->drawSolidRect(Vec2(10.0f, 10.0f), Vec2(20.0f, 20.0f), Color(1.0f, 1.0f, 0.0f, 1.0f)); // drawNode a solid circle drawNode->drawSolidCircle(VisibleRect::center() + Vec2(140.0f, 0.0f), 40, AX_DEGREES_TO_RADIANS(90), 50, 2.0f, 2.0f, - Color4F(0.0f, 1.0f, 0.0f, 1.0f)); + Color(0.0f, 1.0f, 0.0f, 1.0f)); // drawNode segment - drawNode->drawSegment(Vec2(20.0f, size.height), Vec2(20.0f, size.height / 2), 10, Color4F(0.0f, 1.0f, 0.0f, 1.0f)); + drawNode->drawSegment(Vec2(20.0f, size.height), Vec2(20.0f, size.height / 2), 10, Color(0.0f, 1.0f, 0.0f, 1.0f)); drawNode->drawSegment(Vec2(10.0f, size.height / 2), Vec2(size.width / 2, size.height / 2), 40, - Color4F(1.0f, 0.0f, 1.0f, 0.5f)); + Color(1.0f, 0.0f, 1.0f, 0.5f)); // drawNode triangle drawNode->drawTriangle(Vec2(10.0f, 10.0f), Vec2(70.0f, 30.0f), Vec2(100.0f, 140.0f), - Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 0.5f)); + Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 0.5f)); for (int i = 0; i < 100; i++) { drawNode->drawPoint(Vec2(i * 7.0f, 5.0f), (float)i / 5 + 1, - Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); + Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f)); } - drawNode->drawLine(Vec2(0.0f, size.height), Vec2(size.width, size.height - 20), Color4F::YELLOW); - drawNode->drawLine(Vec2(0.0f, 0.0f), Vec2(size.width, size.height - 20), Color4F::YELLOW); + + drawNode->drawLine(Vec2(0.0f, size.height), Vec2(size.width, size.height - 20), Color::YELLOW); + drawNode->drawLine(Vec2(0.0f, 0.0f), Vec2(size.width, size.height - 20), Color::YELLOW); } string DrawNodeAxmolTest2::title() const @@ -3376,26 +3380,26 @@ DrawNodeIssueTester::DrawNodeIssueTester() int verticesCount = 5; drawNode->properties.setPosition(Vec2(5, 150)); - drawNode->drawPoly(vertices, verticesCount, false, Color4F::GREEN); + drawNode->drawPoly(vertices, verticesCount, false, Color::GREEN); auto draw = DrawNode::create(); addChild(draw, 10); draw->setPosition(70, 150); - draw->drawPoly(vertices, verticesCount, false, Color4F::BLUE); + draw->drawPoly(vertices, verticesCount, false, Color::BLUE); drawNode->properties.setPosition(Vec2(140, 150)); - drawNode->drawPoly(vertices, verticesCount, false, Color4F::RED); + drawNode->drawPoly(vertices, verticesCount, false, Color::RED); drawNode->properties.setPosition(Vec2(200, 150)); - drawNode->drawPoly(vertices, verticesCount, false, Color4F::RED, 3); - drawNode->drawPoly(vertices, verticesCount, false, Color4F::WHITE); + drawNode->drawPoly(vertices, verticesCount, false, Color::RED, 3); + drawNode->drawPoly(vertices, verticesCount, false, Color::WHITE); drawNode->properties.setPosition(Vec2(270, 150)); - drawNode->drawPoly(vertices, verticesCount, false, Color4F(0.0f, 0.5f, 0.5f, 0.5f), 10); - drawNode->drawPoly(vertices, verticesCount, false, Color4F::BLACK); + drawNode->drawPoly(vertices, verticesCount, false, Color(0.0f, 0.5f, 0.5f, 0.5f), 10); + drawNode->drawPoly(vertices, verticesCount, false, Color::BLACK); float thick = 0.0f; float y = -90.0f; @@ -3404,17 +3408,17 @@ DrawNodeIssueTester::DrawNodeIssueTester() { thick += 0.5f; y += thick + 1; - drawNode->drawLine(Vec2(140, y), Vec2(180, y), Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f), + drawNode->drawLine(Vec2(140, y), Vec2(180, y), Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f), thick); } - drawNode->drawPie(Vec2(-220, 150), 20, 0, 100, 300, 1, 1, Color4B::TRANSPARENT, Color4B::BLUE, + drawNode->drawPie(Vec2(-220, 150), 20, 0, 100, 300, 1, 1, Color::TRANSPARENT, Color::BLUE, DrawNode::DrawMode::Line, 10); drawNode->properties.setPosition(Vec2(50, -100)); for (int i = 2; i < 30; i++) { drawNode->drawCircle(center, 5 * i, AX_DEGREES_TO_RADIANS(90), i, false, 1.0f, 1.0f, - Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f), 0.5f); + Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1.0f), 0.5f); } Vec2* fbHorse = new Vec2[856 / 2]; @@ -3425,18 +3429,18 @@ DrawNodeIssueTester::DrawNodeIssueTester() for (size_t i = 0; i < sizeof(verticesFB) / sizeof(verticesFB[0]); i += 4) { drawNode->drawLine(Vec2(verticesFB[i] * scale, verticesFB[i + 1] * scale) + pos, - Vec2(verticesFB[i + 2] * scale, verticesFB[i + 3] * scale) + pos, Color4F::RED, 0.5f); + Vec2(verticesFB[i + 2] * scale, verticesFB[i + 3] * scale) + pos, Color::RED, 0.5f); } drawNode->properties.setPosition(VisibleRect::center() - Vec2(100, 50)); - drawNode->drawSolidCircle(Vec2::ZERO, 40, AX_DEGREES_TO_RADIANS(-90), 30, 1.0f, 1.0f, Color4F::GREEN, 6, - Color4F::BLUE, false); - drawNode->drawSolidCircle(Vec2(100, 0), 40, AX_DEGREES_TO_RADIANS(-90), 30, 1.0f, 1.0f, Color4F::RED, 6, - Color4F::BLUE, true); + drawNode->drawSolidCircle(Vec2::ZERO, 40, AX_DEGREES_TO_RADIANS(-90), 30, 1.0f, 1.0f, Color::GREEN, 6, + Color::BLUE, false); + drawNode->drawSolidCircle(Vec2(100, 0), 40, AX_DEGREES_TO_RADIANS(-90), 30, 1.0f, 1.0f, Color::RED, 6, + Color::BLUE, true); - drawNode->drawCircle(Vec2(100, 100), 40, AX_DEGREES_TO_RADIANS(-90), 30, true, 1.0f, 1.0f, Color4F::GREEN, 6); + drawNode->drawCircle(Vec2(100, 100), 40, AX_DEGREES_TO_RADIANS(-90), 30, true, 1.0f, 1.0f, Color::GREEN, 6); - drawNode->drawCircle(Vec2(0, 100), 40, AX_DEGREES_TO_RADIANS(-90), 30, false, 1.0f, 1.0f, Color4F::RED, 6); + drawNode->drawCircle(Vec2(0, 100), 40, AX_DEGREES_TO_RADIANS(-90), 30, false, 1.0f, 1.0f, Color::RED, 6); // scheduleUpdate(); } @@ -3541,41 +3545,41 @@ void DrawNodeSpLinesTest::update(float dt) int boxSize = 3; for (auto&& p : points) { - drawNodeCP->drawRect(Vec2(p.x - boxSize, p.y - boxSize), Vec2(p.x + boxSize, p.y + boxSize), Color4F::BLUE); + drawNodeCP->drawRect(Vec2(p.x - boxSize, p.y - boxSize), Vec2(p.x + boxSize, p.y + boxSize), Color::BLUE); array->addControlPoint(Vec2(p.x, p.y)); } - drawNode->drawCardinalSpline(array, 0.2f, points.size() * 8, Color4F::GREEN, 20.0f); - drawNode->drawCardinalSpline(array, 0.2f, points.size() * 8, Color4F::BLUE); - drawNode->drawCardinalSpline(array, 0.2f, points.size() * 16, Color4F(1.0f, 1.0f, 0.5f, 1.0f), 10.0f); + drawNode->drawCardinalSpline(array, 0.2f, points.size() * 8, Color::GREEN, 20.0f); + drawNode->drawCardinalSpline(array, 0.2f, points.size() * 8, Color::BLUE); + drawNode->drawCardinalSpline(array, 0.2f, points.size() * 16, Color(1.0f, 1.0f, 0.5f, 1.0f), 10.0f); // Issue #2302 auto array3 = PointArray::create(20); for (int i = 0; i < 10; i++) { array3->addControlPoint(Vec2((i % 2) ? 20 : screen.width - 20, 50 + i * 20)); - drawNode->drawPoint(array3->getControlPointAtIndex(i), 10, Color4F::BLUE); + drawNode->drawPoint(array3->getControlPointAtIndex(i), 10, Color::BLUE); } - drawNode->drawCardinalSpline(array3, 0.1, 20, Color4F::ORANGE); + drawNode->drawCardinalSpline(array3, 0.1, 20, Color::ORANGE); - // drawNode->drawCatmullRom(array, points.size() * 8, Color4F::YELLOW,5); + // drawNode->drawCatmullRom(array, points.size() * 8, Color::YELLOW,5); // if (points.size()>3) //{ // int step = points.size()/4; // drawNode->drawCubicBezier(points.at(0), points.at(step),points.at(step*2),points.at(points.size()-1), - // points.size(), Color4F::BLUE); + // points.size(), Color::BLUE); //} - drawNode->drawCardinalSpline(pts, 0.5f, 360, Color4F::RED, 5.0f); - drawNode->drawCardinalSpline(pts2, 0.5f, 360, Color4F::GREEN, 2.0f); + drawNode->drawCardinalSpline(pts, 0.5f, 360, Color::RED, 5.0f); + drawNode->drawCardinalSpline(pts2, 0.5f, 360, Color::GREEN, 2.0f); int i1 = RandomHelper::random_int(0, n - 1); int i2 = RandomHelper::random_int(0, n - 1); - drawNode->drawDot(pts->getControlPointAtIndex(i1), 7, Color4F(0, 1, 0, 0.3)); - drawNode->drawDot(pts->getControlPointAtIndex(i1), 4, Color4F::GREEN); + drawNode->drawDot(pts->getControlPointAtIndex(i1), 7, Color(0, 1, 0, 0.3)); + drawNode->drawDot(pts->getControlPointAtIndex(i1), 4, Color::GREEN); - drawNode->drawDot(pts2->getControlPointAtIndex(i2), 7, Color4F(0, 1, 0, 0.3)); - drawNode->drawDot(pts2->getControlPointAtIndex(i2), 4, Color4F::GREEN); + drawNode->drawDot(pts2->getControlPointAtIndex(i2), 7, Color(0, 1, 0, 0.3)); + drawNode->drawDot(pts2->getControlPointAtIndex(i2), 4, Color::GREEN); } #if defined(AX_PLATFORM_PC) @@ -3597,7 +3601,7 @@ std::string CandyMixEeffect::subtitle() const return ""; } -void CandyMixEeffect::renderLine(float x1, float x2, float y, ax::Color4F color, float angle) +void CandyMixEeffect::renderLine(float x1, float x2, float y, ax::Color color, float angle) { static float WID = 400; @@ -3620,7 +3624,7 @@ void CandyMixEeffect::renderLine(float x1, float x2, float y, ax::Color4F color, float rr = MIN(mm, r * sf + sp); float gg = MIN(mm, g * sf + sp); float bb = MIN(mm, b * sf + sp); - drawNode->drawPoint(Vec2(y, x - 50), 2.0f, Color4F(rr, gg, bb, 1.0f)); + drawNode->drawPoint(Vec2(y, x - 50), 2.0f, Color(rr, gg, bb, 1.0f)); } } @@ -3628,12 +3632,11 @@ void CandyMixEeffect::update(float dt) { DrawNodeBaseTest::update(dt); drawNode->clear(); + static b2Timer timer = b2CreateTimer(); static float WID = 400; static float HIG = 600; - static b2Timer timer; - - float t = timer.GetMilliseconds() / 1000.0f; + float t = b2GetMilliseconds(&timer) / 1000.0f; float ta = sin(t * cos(t) * 0.02f) + t; float tb = (1.0f + sin(t) * 1.0f) * 0.02f + 0.01f; float xa = WID * 0.5f; @@ -3647,13 +3650,13 @@ void CandyMixEeffect::update(float dt) float x3 = xa + sin(rot + (float)M_PI) * rad; float x4 = xa + sin(rot + (float)M_PI * 1.5f) * rad; if (x1 < x2) - renderLine(x1, x2, y, Color4F::RED, rot); + renderLine(x1, x2, y, Color::RED, rot); if (x2 < x3) - renderLine(x2, x3, y, Color4F::GREEN, rot + (float)M_PI * 0.5f); + renderLine(x2, x3, y, Color::GREEN, rot + (float)M_PI * 0.5f); if (x3 < x4) - renderLine(x3, x4, y, Color4F::BLUE, rot + (float)M_PI); + renderLine(x3, x4, y, Color::BLUE, rot + (float)M_PI); if (x4 < x1) - renderLine(x4, x1, y, Color4F::YELLOW, rot + (float)M_PI * 1.5f); + renderLine(x4, x1, y, Color::YELLOW, rot + (float)M_PI * 1.5f); t += sin(ta + ya) * tb; xa += sin(t + ta) * 0.1f; } diff --git a/tests/cpp-tests/Source/DrawNodeTest/DrawNodeTest.h b/tests/cpp-tests/Source/DrawNodeTest/DrawNodeTest.h index 232d7507eea2..1c16be79aff8 100644 --- a/tests/cpp-tests/Source/DrawNodeTest/DrawNodeTest.h +++ b/tests/cpp-tests/Source/DrawNodeTest/DrawNodeTest.h @@ -180,7 +180,7 @@ class DrawNodeMorphTest_SolidPolygon : public DrawNodeBaseTest ax::Vec2* verticesObj1[10]; ax::Vec2* verticesObj2[10]; ax::Vec2* verticesObjMorph[10]; - ax::Color4F color[10]; + ax::Color color[10]; float rad[10]; bool state[10]; @@ -203,7 +203,7 @@ class DrawNodeMorphTest_Polygon : public DrawNodeBaseTest ax::Vec2* verticesObj1[10]; ax::Vec2* verticesObj2[10]; ax::Vec2* verticesObjMorph[10]; - ax::Color4F color[10]; + ax::Color color[10]; float rad[10]; bool state[10]; @@ -442,7 +442,7 @@ class CandyMixEeffect : public DrawNodeBaseTest virtual std::string subtitle() const override; void update(float dt); - void renderLine(float x1, float x2, float y, ax::Color4F color, float angle); + void renderLine(float x1, float x2, float y, ax::Color color, float angle); private: std::vector points; diff --git a/tests/cpp-tests/Source/EffectsAdvancedTest/EffectsAdvancedTest.cpp b/tests/cpp-tests/Source/EffectsAdvancedTest/EffectsAdvancedTest.cpp index f1e6dc6bfe63..063524565631 100644 --- a/tests/cpp-tests/Source/EffectsAdvancedTest/EffectsAdvancedTest.cpp +++ b/tests/cpp-tests/Source/EffectsAdvancedTest/EffectsAdvancedTest.cpp @@ -258,7 +258,7 @@ void Issue631::onEnter() removeChild(_bgNode, true); // background - auto layer = LayerColor::create(Color4B(255, 0, 0, 255)); + auto layer = LayerColor::create(Color32(255, 0, 0, 255)); addChild(layer, -10); auto sprite = Sprite::create("Images/grossini.png"); sprite->setPosition(50, 80); @@ -266,7 +266,7 @@ void Issue631::onEnter() // foreground auto layer2BaseGrid = NodeGrid::create(); - auto layer2 = LayerColor::create(Color4B(0, 255, 0, 255)); + auto layer2 = LayerColor::create(Color32(0, 255, 0, 255)); auto fog = Sprite::create("Images/Fog.png"); BlendFunc bf = {backend::BlendFactor::SRC_ALPHA, backend::BlendFactor::ONE_MINUS_SRC_ALPHA}; diff --git a/tests/cpp-tests/Source/EffectsTest/EffectsTest.cpp b/tests/cpp-tests/Source/EffectsTest/EffectsTest.cpp index 51148abf5a53..1e013722367d 100644 --- a/tests/cpp-tests/Source/EffectsTest/EffectsTest.cpp +++ b/tests/cpp-tests/Source/EffectsTest/EffectsTest.cpp @@ -349,7 +349,7 @@ bool EffectBaseTest::init() { if (TestCase::init()) { - LayerColor* background = LayerColor::create(Color4B(32, 128, 32, 255)); + LayerColor* background = LayerColor::create(Color32(32, 128, 32, 255)); this->addChild(background, -20); if (isRectEffect()) { diff --git a/tests/cpp-tests/Source/FontTest/FontTest.cpp b/tests/cpp-tests/Source/FontTest/FontTest.cpp index b93089b3a43b..09c9e9326045 100644 --- a/tests/cpp-tests/Source/FontTest/FontTest.cpp +++ b/tests/cpp-tests/Source/FontTest/FontTest.cpp @@ -105,9 +105,9 @@ void FontTest::showFont(std::string_view fontFile) auto right = Label::createWithSystemFont("alignment right", fontFile, fontSize, blockSize, TextHAlignment::RIGHT, verticalAlignment[vAlignIdx]); - auto leftColor = LayerColor::create(Color4B(100, 100, 100, 255), blockSize.width, blockSize.height); - auto centerColor = LayerColor::create(Color4B(200, 100, 100, 255), blockSize.width, blockSize.height); - auto rightColor = LayerColor::create(Color4B(100, 100, 200, 255), blockSize.width, blockSize.height); + auto leftColor = LayerColor::create(Color32(100, 100, 100, 255), blockSize.width, blockSize.height); + auto centerColor = LayerColor::create(Color32(200, 100, 100, 255), blockSize.width, blockSize.height); + auto rightColor = LayerColor::create(Color32(100, 100, 200, 255), blockSize.width, blockSize.height); leftColor->setIgnoreAnchorPointForPosition(false); centerColor->setIgnoreAnchorPointForPosition(false); @@ -202,9 +202,9 @@ void FontNoReplacementTest::onEnter() auto right = Label::createWithTTF("fonts/Schwarzwald.ttf" + suffix, "fonts/Schwarzwald.ttf", fontSize, blockSize, TextHAlignment::RIGHT, verticalAlignment[0]); - auto leftColor = LayerColor::create(Color4B(100, 100, 100, 255), blockSize.width, blockSize.height); - auto centerColor = LayerColor::create(Color4B(200, 100, 100, 255), blockSize.width, blockSize.height); - auto rightColor = LayerColor::create(Color4B(100, 100, 200, 255), blockSize.width, blockSize.height); + auto leftColor = LayerColor::create(Color32(100, 100, 100, 255), blockSize.width, blockSize.height); + auto centerColor = LayerColor::create(Color32(200, 100, 100, 255), blockSize.width, blockSize.height); + auto rightColor = LayerColor::create(Color32(100, 100, 200, 255), blockSize.width, blockSize.height); leftColor->setIgnoreAnchorPointForPosition(false); centerColor->setIgnoreAnchorPointForPosition(false); diff --git a/tests/cpp-tests/Source/LabelTest/LabelTestNew.cpp b/tests/cpp-tests/Source/LabelTest/LabelTestNew.cpp index 5c82b5e91bd7..cef9132b78e4 100644 --- a/tests/cpp-tests/Source/LabelTest/LabelTestNew.cpp +++ b/tests/cpp-tests/Source/LabelTest/LabelTestNew.cpp @@ -176,7 +176,7 @@ LabelFNTColorAndOpacity::LabelFNTColorAndOpacity() { _time = 0; - auto col = LayerColor::create(Color4B(128, 128, 128, 255)); + auto col = LayerColor::create(Color32(128, 128, 128, 255)); addChild(col, -10); auto label1 = Label::createWithBMFont("fonts/bitmapFontTest2.fnt", "Test"); @@ -240,8 +240,8 @@ LabelFNTSpriteActions::LabelFNTSpriteActions() auto s = Director::getInstance()->getWinSize(); auto drawNode = DrawNode::create(); - drawNode->drawLine(Vec2(0.0f, s.height / 2), Vec2(s.width, s.height / 2), Color4F(1.0f, 1.0f, 1.0f, 1.0f)); - drawNode->drawLine(Vec2(s.width / 2, 0.0f), Vec2(s.width / 2, s.height), Color4F(1.0f, 1.0f, 1.0f, 1.0f)); + drawNode->drawLine(Vec2(0.0f, s.height / 2), Vec2(s.width, s.height / 2), Color(1.0f, 1.0f, 1.0f, 1.0f)); + drawNode->drawLine(Vec2(s.width / 2, 0.0f), Vec2(s.width / 2, s.height), Color(1.0f, 1.0f, 1.0f, 1.0f)); addChild(drawNode, -1); // Upper Label @@ -560,7 +560,7 @@ LabelFNTGlyphDesigner::LabelFNTGlyphDesigner() { auto winSize = Director::getInstance()->getWinSize(); - auto layer = LayerColor::create(Color4B(128, 128, 128, 255)); + auto layer = LayerColor::create(Color32(128, 128, 128, 255)); addChild(layer, -10); auto label1 = Label::createWithBMFont("fonts/futura-48.fnt", "Testing Glyph Designer"); @@ -926,7 +926,7 @@ LabelFNTBounds::LabelFNTBounds() { auto s = Director::getInstance()->getWinSize(); - auto layer = LayerColor::create(Color4B(128, 128, 128, 255)); + auto layer = LayerColor::create(Color32(128, 128, 128, 255)); addChild(layer, -10); // LabelBMFont @@ -945,7 +945,7 @@ LabelFNTBounds::LabelFNTBounds() Vec2 vertices[4] = {Vec2(origin.width, origin.height), Vec2(labelSize.width + origin.width, origin.height), Vec2(labelSize.width + origin.width, labelSize.height + origin.height), Vec2(origin.width, labelSize.height + origin.height)}; - drawNode->drawPoly(vertices, 4, true, Color4F(1.0f, 1.0f, 1.0f, 1.0f)); + drawNode->drawPoly(vertices, 4, true, Color(1.0f, 1.0f, 1.0f, 1.0f)); addChild(drawNode); } @@ -1043,19 +1043,19 @@ LabelTTFColor::LabelTTFColor() // Green auto label1 = Label::createWithTTF(ttfConfig, "Green", TextHAlignment::CENTER, size.width); label1->setPosition(size.width / 2, size.height * 0.5f); - label1->setTextColor(Color4B::GREEN); + label1->setTextColor(Color32::GREEN); addChild(label1); // Red auto label2 = Label::createWithTTF(ttfConfig, "Red", TextHAlignment::CENTER, size.width); label2->setPosition(size.width / 2, size.height * 0.65f); - label2->setTextColor(Color4B::RED); + label2->setTextColor(Color32::RED); addChild(label2); // Blue auto label3 = Label::createWithTTF(ttfConfig, "Blue", TextHAlignment::CENTER, size.width); label3->setPosition(size.width / 2, size.height * 0.35f); - label3->setTextColor(Color4B::BLUE); + label3->setTextColor(Color32::BLUE); addChild(label3); } @@ -1135,15 +1135,15 @@ LabelTTFCJKWrappingTest::LabelTTFCJKWrappingTest() drawNode->setAnchorPoint(Vec2(0, 0)); this->addChild(drawNode); drawNode->drawSegment(Vec2(size.width * 0.1f, size.height * 0.8f), Vec2(size.width * 0.1, 0.0f), 1, - Color4F(1.0f, 0.0f, 0.0f, 1.0f)); + Color(1.0f, 0.0f, 0.0f, 1.0f)); drawNode->drawSegment(Vec2(size.width * 0.85f, size.height * 0.8f), Vec2(size.width * 0.85f, 0.0f), 1, - Color4F(1.0f, 0.0f, 0.0f, 1.0f)); + Color(1.0f, 0.0f, 0.0f, 1.0f)); TTFConfig ttfConfig("fonts/HKYuanMini.ttf", 25, GlyphCollection::DYNAMIC); auto label1 = Label::createWithTTF(ttfConfig, "你好,Axmol Label.", TextHAlignment::LEFT, size.width * 0.75f); if (label1) { - label1->setTextColor(Color4B(128, 255, 255, 255)); + label1->setTextColor(Color32(128, 255, 255, 255)); label1->setPosition(Vec2(size.width * 0.1f, size.height * 0.6f)); label1->setAnchorPoint(Vec2(0.0f, 0.5f)); this->addChild(label1); @@ -1156,7 +1156,7 @@ LabelTTFCJKWrappingTest::LabelTTFCJKWrappingTest() auto label2 = Label::createWithTTF(ttfConfig, "早上好,Axmol Label.", TextHAlignment::LEFT, size.width * 0.75f); if (label2) { - label2->setTextColor(Color4B(255, 128, 255, 255)); + label2->setTextColor(Color32(255, 128, 255, 255)); label2->setPosition(Vec2(size.width * 0.1f, size.height * 0.4f)); label2->setAnchorPoint(Vec2(0.0f, 0.5f)); this->addChild(label2); @@ -1166,7 +1166,7 @@ LabelTTFCJKWrappingTest::LabelTTFCJKWrappingTest() size.width * 0.75f); if (label3) { - label3->setTextColor(Color4B(255, 255, 128, 255)); + label3->setTextColor(Color32(255, 255, 128, 255)); label3->setPosition(Vec2(size.width * 0.1f, size.height * 0.2f)); label3->setAnchorPoint(Vec2(0.0f, 0.5f)); this->addChild(label3); @@ -1289,7 +1289,7 @@ LabelTTFDistanceField::LabelTTFDistanceField() auto label1 = Label::createWithTTF(ttfConfig, "Distance Field", TextHAlignment::CENTER, size.width); label1->setPosition(Vec2(size.width / 2, size.height * 0.6f)); - label1->setTextColor(Color4B::GREEN); + label1->setTextColor(Color32::GREEN); addChild(label1); auto action = Sequence::create(DelayTime::create(1.0f), ScaleTo::create(6.0f, 5.0f, 5.0f), @@ -1303,11 +1303,11 @@ LabelTTFDistanceField::LabelTTFDistanceField() borderDraw->clear(); Vec2 vertices[4] = {Vec2::ZERO, Vec2(labelContentSize.width, 0.0f), Vec2(labelContentSize.width, labelContentSize.height), Vec2(0.0f, labelContentSize.height)}; - borderDraw->drawPoly(vertices, 4, true, Color4F::RED); + borderDraw->drawPoly(vertices, 4, true, Color::RED); auto label2 = Label::createWithTTF(ttfConfig, "Distance Field", TextHAlignment::CENTER, size.width); label2->setPosition(Vec2(size.width / 2, size.height * 0.3f)); - label2->setTextColor(Color4B::RED); + label2->setTextColor(Color32::RED); addChild(label2); // Draw the label border @@ -1317,7 +1317,7 @@ LabelTTFDistanceField::LabelTTFDistanceField() borderDraw2->clear(); Vec2 vertices2[4] = {Vec2::ZERO, Vec2(labelContentSize2.width, 0.0f), Vec2(labelContentSize2.width, labelContentSize2.height), Vec2(0.0f, labelContentSize2.height)}; - borderDraw2->drawPoly(vertices2, 4, true, Color4F::GREEN); + borderDraw2->drawPoly(vertices2, 4, true, Color::GREEN); } std::string LabelTTFDistanceField::title() const @@ -1334,7 +1334,7 @@ LabelOutlineAndGlowTest::LabelOutlineAndGlowTest() { auto size = Director::getInstance()->getWinSize(); - auto bg = LayerColor::create(Color4B(200, 191, 231, 255)); + auto bg = LayerColor::create(Color32(200, 191, 231, 255)); this->addChild(bg); TTFConfig ttfConfig("fonts/arial.ttf", 40, GlyphCollection::DYNAMIC, nullptr, true); @@ -1342,16 +1342,16 @@ LabelOutlineAndGlowTest::LabelOutlineAndGlowTest() // Glow SDF (GPU) auto label1 = Label::createWithTTF(ttfConfig, "Glow1", TextHAlignment::CENTER, size.width); label1->setPosition(Vec2(size.width / 2, size.height * 0.7)); - label1->setTextColor(Color4B::GREEN); - label1->enableGlow(Color4B::YELLOW); + label1->setTextColor(Color32::GREEN); + label1->enableGlow(Color32::YELLOW); addChild(label1); // Glow normal(CPU) ttfConfig.distanceFieldEnabled = false; auto label2 = Label::createWithTTF(ttfConfig, "Glow2", TextHAlignment::CENTER, size.width); label2->setPosition(Vec2(size.width / 2, size.height * 0.6)); - label2->setTextColor(Color4B::GREEN); - label2->enableGlow(Color4B::YELLOW); + label2->setTextColor(Color32::GREEN); + label2->enableGlow(Color32::YELLOW); addChild(label2); // Outline SDF(GPU) @@ -1359,8 +1359,8 @@ LabelOutlineAndGlowTest::LabelOutlineAndGlowTest() ttfConfig.outlineSize = 2; auto label3 = Label::createWithTTF(ttfConfig, "Outline1", TextHAlignment::CENTER, size.width); label3->setPosition(Vec2(size.width / 2, size.height * 0.48)); - label3->setTextColor(Color4B::RED); - label3->enableOutline(Color4B::BLUE); + label3->setTextColor(Color32::RED); + label3->enableOutline(Color32::BLUE); addChild(label3); // Outline normal(CPU by freetype2) @@ -1368,8 +1368,8 @@ LabelOutlineAndGlowTest::LabelOutlineAndGlowTest() ttfConfig.outlineSize = 2; auto label4 = Label::createWithTTF(ttfConfig, "Outline2", TextHAlignment::CENTER, size.width); label4->setPosition(Vec2(size.width / 2, size.height * 0.36)); - label4->setTextColor(Color4B::RED); - label4->enableOutline(Color4B::BLUE, 2); + label4->setTextColor(Color32::RED); + label4->enableOutline(Color32::BLUE, 2); addChild(label4); } @@ -1391,7 +1391,7 @@ void LabelShadowTest::onEnter() auto size = Director::getInstance()->getWinSize(); - auto bg = LayerColor::create(Color4B(200, 191, 231, 255)); + auto bg = LayerColor::create(Color32(200, 191, 231, 255)); this->addChild(bg); auto slider = ui::Slider::create(); @@ -1425,28 +1425,28 @@ void LabelShadowTest::onEnter() shadowLabelTTF = Label::createWithTTF(ttfConfig, "TTF:Shadow"); shadowLabelTTF->setPosition(Vec2(size.width / 2, horizontalSliderY + step * (0.5f + 3))); - shadowLabelTTF->setTextColor(Color4B::RED); - shadowLabelTTF->enableShadow(Color4B::BLACK); + shadowLabelTTF->setTextColor(Color32::RED); + shadowLabelTTF->enableShadow(Color32::BLACK); addChild(shadowLabelTTF); shadowLabelOutline = Label::createWithTTF(ttfConfig, "TTF:Shadow"); shadowLabelOutline->setPosition(Vec2(size.width / 2, horizontalSliderY + step * (0.5f + 2))); - shadowLabelOutline->setTextColor(Color4B::RED); - shadowLabelOutline->enableOutline(Color4B::YELLOW, 1); - shadowLabelOutline->enableShadow(Color4B::GREEN); + shadowLabelOutline->setTextColor(Color32::RED); + shadowLabelOutline->enableOutline(Color32::YELLOW, 1); + shadowLabelOutline->enableShadow(Color32::GREEN); addChild(shadowLabelOutline); shadowLabelGrow = Label::createWithTTF(ttfConfig, "TTF:Shadow"); shadowLabelGrow->setPosition(Vec2(size.width / 2, horizontalSliderY + step * (0.5f + 1))); - shadowLabelGrow->setTextColor(Color4B::RED); - shadowLabelGrow->enableGlow(Color4B::YELLOW); - shadowLabelGrow->enableShadow(Color4B::BLUE); + shadowLabelGrow->setTextColor(Color32::RED); + shadowLabelGrow->enableGlow(Color32::YELLOW); + shadowLabelGrow->enableShadow(Color32::BLUE); addChild(shadowLabelGrow); shadowLabelBMFont = Label::createWithBMFont("fonts/bitmapFontTest.fnt", "BMFont:Shadow"); shadowLabelBMFont->setPosition(Vec2(size.width / 2, horizontalSliderY + step * 0.5f)); shadowLabelBMFont->setColor(Color3B::RED); - shadowLabelBMFont->enableShadow(Color4B::GREEN); + shadowLabelBMFont->enableShadow(Color32::GREEN); addChild(shadowLabelBMFont); } @@ -1458,10 +1458,10 @@ void LabelShadowTest::sliderEvent(Object* pSender, ui::Slider::EventType type) Slider* slider2 = (Slider*)this->getChildByTag(2); auto offset = Size(slider->getPercent() - 50, 50 - slider2->getPercent()); - shadowLabelTTF->enableShadow(Color4B::BLACK, offset); - shadowLabelBMFont->enableShadow(Color4B::GREEN, offset); - shadowLabelOutline->enableShadow(Color4B::GREEN, offset); - shadowLabelGrow->enableShadow(Color4B::BLUE, offset); + shadowLabelTTF->enableShadow(Color32::BLACK, offset); + shadowLabelBMFont->enableShadow(Color32::GREEN, offset); + shadowLabelOutline->enableShadow(Color32::GREEN, offset); + shadowLabelGrow->enableShadow(Color32::BLUE, offset); } } @@ -1623,7 +1623,7 @@ LabelTTFOldNew::LabelTTFOldNew() Vec2 vertices[4] = {Vec2(origin.width, origin.height), Vec2(labelSize.width + origin.width, origin.height), Vec2(labelSize.width + origin.width, labelSize.height + origin.height), Vec2(origin.width, labelSize.height + origin.height)}; - drawNode->drawPoly(vertices, 4, true, Color4F(1.0f, 0.0f, 0.0f, 1.0f)); + drawNode->drawPoly(vertices, 4, true, Color(1.0f, 0.0f, 0.0f, 1.0f)); labelSize = label2->getContentSize(); origin = Director::getInstance()->getWinSize(); @@ -1634,7 +1634,7 @@ LabelTTFOldNew::LabelTTFOldNew() Vec2 vertices2[4] = {Vec2(origin.width, origin.height), Vec2(labelSize.width + origin.width, origin.height), Vec2(labelSize.width + origin.width, labelSize.height + origin.height), Vec2(origin.width, labelSize.height + origin.height)}; - drawNode->drawPoly(vertices2, 4, true, Color4F(1.0f, 1.0f, 1.0f, 1.0f)); + drawNode->drawPoly(vertices2, 4, true, Color(1.0f, 1.0f, 1.0f, 1.0f)); addChild(drawNode); } @@ -1679,7 +1679,7 @@ LabelAlignmentTest::LabelAlignmentTest() auto s = Director::getInstance()->getWinSize(); auto pos = Vec2((s.width - blockSize.width) / 2, (s.height - blockSize.height) / 2); - auto colorLayer = LayerColor::create(Color4B(100, 100, 100, 255), blockSize.width, blockSize.height); + auto colorLayer = LayerColor::create(Color32(100, 100, 100, 255), blockSize.width, blockSize.height); colorLayer->setAnchorPoint(Vec2::ANCHOR_BOTTOM_LEFT); colorLayer->setPosition(pos); @@ -1802,14 +1802,14 @@ LabelLineHeightTest::LabelLineHeightTest() { auto size = Director::getInstance()->getWinSize(); - auto bg = LayerColor::create(Color4B(200, 191, 231, 255)); + auto bg = LayerColor::create(Color32(200, 191, 231, 255)); this->addChild(bg); TTFConfig ttfConfig("fonts/arial.ttf", 25, GlyphCollection::DYNAMIC, nullptr, false); label = Label::createWithTTF(ttfConfig, "Test\nLine\nHeight"); label->setPosition(Vec2(size.width / 2, size.height * 0.5f)); - label->setTextColor(Color4B::RED); + label->setTextColor(Color32::RED); addChild(label); auto slider = ui::Slider::create(); @@ -1846,14 +1846,14 @@ LabelAdditionalKerningTest::LabelAdditionalKerningTest() { auto size = Director::getInstance()->getWinSize(); - auto bg = LayerColor::create(Color4B(200, 191, 231, 255)); + auto bg = LayerColor::create(Color32(200, 191, 231, 255)); this->addChild(bg); TTFConfig ttfConfig("fonts/arial.ttf", 40, GlyphCollection::DYNAMIC, nullptr, false); label = Label::createWithTTF(ttfConfig, "Test additional kerning"); label->setPosition(size.width / 2, size.height * 0.5f); - label->setTextColor(Color4B::RED); + label->setTextColor(Color32::RED); addChild(label); auto slider = ui::Slider::create(); @@ -1907,7 +1907,7 @@ std::string LabelIssue8492Test::subtitle() const LabelMultilineWithOutline::LabelMultilineWithOutline() { auto label = Label::createWithTTF("Multi-line text\nwith\noutline feature", "fonts/arial.ttf", 24); - label->enableOutline(Color4B::ORANGE, 1); + label->enableOutline(Color32::ORANGE, 1); label->setPosition(VisibleRect::center()); addChild(label); } @@ -1993,24 +1993,24 @@ LabelSystemFontColor::LabelSystemFontColor() { auto size = Director::getInstance()->getWinSize(); - auto label1 = Label::createWithSystemFont("Color4B::Red", "fonts/arial.ttf", 20); + auto label1 = Label::createWithSystemFont("Color32::Red", "fonts/arial.ttf", 20); label1->setPosition(Vec2(size.width / 2, size.height * 0.3f)); - label1->setTextColor(Color4B::RED); + label1->setTextColor(Color32::RED); addChild(label1); - auto label2 = Label::createWithSystemFont("Color4B::Green", "fonts/arial.ttf", 20); + auto label2 = Label::createWithSystemFont("Color32::Green", "fonts/arial.ttf", 20); label2->setPosition(Vec2(size.width / 2, size.height * 0.4f)); - label2->setTextColor(Color4B::GREEN); + label2->setTextColor(Color32::GREEN); addChild(label2); - auto label3 = Label::createWithSystemFont("Color4B::Blue", "fonts/arial.ttf", 20); + auto label3 = Label::createWithSystemFont("Color32::Blue", "fonts/arial.ttf", 20); label3->setPosition(Vec2(size.width / 2, size.height * 0.5f)); - label3->setTextColor(Color4B::BLUE); + label3->setTextColor(Color32::BLUE); addChild(label3); - auto label4 = Label::createWithSystemFont("Color4B(0, 0, 255, 100)", "fonts/arial.ttf", 20); + auto label4 = Label::createWithSystemFont("Color32(0, 0, 255, 100)", "fonts/arial.ttf", 20); label4->setPosition(Vec2(size.width / 2, size.height * 0.6f)); - label4->setTextColor(Color4B(0, 0, 255, 100)); + label4->setTextColor(Color32(0, 0, 255, 100)); addChild(label4); } @@ -2077,7 +2077,7 @@ LabelIssue11699Test::LabelIssue11699Test() auto center = VisibleRect::center(); auto label = Label::createWithTTF("中国", "fonts/HKYuanMini.ttf", 150); - label->enableOutline(Color4B::RED, 2); + label->enableOutline(Color32::RED, 2); label->setPosition(center.x, center.y); addChild(label); } @@ -2112,7 +2112,7 @@ LabelIssue12409Test::LabelIssue12409Test() Vec2(labelOrigin.x, labelOrigin.y + labelSize.height)}; auto drawNode = DrawNode::create(); - drawNode->drawPoly(vertices, 4, true, Color4F::WHITE); + drawNode->drawPoly(vertices, 4, true, Color::WHITE); addChild(drawNode); } @@ -2198,8 +2198,8 @@ LabelIssue10688Test::LabelIssue10688Test() auto center = VisibleRect::center(); auto label = Label::createWithTTF("Glow MenuItemLabel", "fonts/arial.ttf", 30); - label->setTextColor(Color4B::RED); - label->enableGlow(Color4B::YELLOW); + label->setTextColor(Color32::RED); + label->enableGlow(Color32::YELLOW); auto menuItem1 = MenuItemLabel::create(label, [](Object*) {}); menuItem1->setAnchorPoint(Vec2::ANCHOR_MIDDLE_LEFT); menuItem1->setPosition(center.x - label->getContentSize().width / 2, center.y); @@ -2576,10 +2576,10 @@ void LabelLayoutBaseTest::updateDrawNodeSize(const ax::Size& drawNodeSize) Vec2(drawNodeSize.width + origin.width, drawNodeSize.height + origin.height), Vec2(origin.width, drawNodeSize.height + origin.height)}; _drawNode->clear(); - _drawNode->drawLine(vertices[0], vertices[1], Color4F(1.0f, 1.0f, 1.0f, 1.0f)); - _drawNode->drawLine(vertices[0], vertices[3], Color4F(1.0f, 1.0f, 1.0f, 1.0f)); - _drawNode->drawLine(vertices[2], vertices[3], Color4F(1.0f, 1.0f, 1.0f, 1.0f)); - _drawNode->drawLine(vertices[1], vertices[2], Color4F(1.0f, 1.0f, 1.0f, 1.0f)); + _drawNode->drawLine(vertices[0], vertices[1], Color(1.0f, 1.0f, 1.0f, 1.0f)); + _drawNode->drawLine(vertices[0], vertices[3], Color(1.0f, 1.0f, 1.0f, 1.0f)); + _drawNode->drawLine(vertices[2], vertices[3], Color(1.0f, 1.0f, 1.0f, 1.0f)); + _drawNode->drawLine(vertices[1], vertices[2], Color(1.0f, 1.0f, 1.0f, 1.0f)); } LabelWrapByWordTest::LabelWrapByWordTest() @@ -2896,7 +2896,7 @@ LabelSystemFontTest::LabelSystemFontTest() _label->setOverflow(Label::Overflow::NONE); _label->setSystemFontName("Hiragino Sans GB"); _label->setSystemFontSize(20); - _label->enableOutline(Color4B::RED, 1.0); + _label->enableOutline(Color32::RED, 1.0); _label->setString("This is a very\n 我爱你中国\n long sentence"); _labelType = 2; @@ -3316,8 +3316,8 @@ LabelUnderlineStrikethroughMultiline::LabelUnderlineStrikethroughMultiline() // Glow SDF (GPU) auto label1 = Label::createWithTTF(ttfConf, "Glow1", TextHAlignment::CENTER, s.width); label1->setPosition(Vec2(s.width / 2, s.height * 0.7)); - label1->setTextColor(Color4B::GREEN); - label1->enableGlow(Color4B::YELLOW); + label1->setTextColor(Color32::GREEN); + label1->enableGlow(Color32::YELLOW); label1->enableUnderline(); label1->enableStrikethrough(); addChild(label1); @@ -3326,8 +3326,8 @@ LabelUnderlineStrikethroughMultiline::LabelUnderlineStrikethroughMultiline() ttfConf.distanceFieldEnabled = false; auto label2 = Label::createWithTTF(ttfConf, "Glow2", TextHAlignment::CENTER, s.width); label2->setPosition(Vec2(s.width / 2, s.height * 0.6)); - label2->setTextColor(Color4B::GREEN); - label2->enableGlow(Color4B::YELLOW); + label2->setTextColor(Color32::GREEN); + label2->enableGlow(Color32::YELLOW); label2->enableUnderline(); label2->enableStrikethrough(); addChild(label2); @@ -3337,8 +3337,8 @@ LabelUnderlineStrikethroughMultiline::LabelUnderlineStrikethroughMultiline() ttfConf.outlineSize = 2; auto label3 = Label::createWithTTF(ttfConf, "Outline1", TextHAlignment::CENTER, s.width); label3->setPosition(Vec2(s.width / 2, s.height * 0.48)); - label3->setTextColor(Color4B::RED); - label3->enableOutline(Color4B::BLUE); + label3->setTextColor(Color32::RED); + label3->enableOutline(Color32::BLUE); label3->enableUnderline(); label3->enableStrikethrough(); addChild(label3); @@ -3348,8 +3348,8 @@ LabelUnderlineStrikethroughMultiline::LabelUnderlineStrikethroughMultiline() ttfConf.outlineSize = 2; auto label4 = Label::createWithTTF(ttfConf, "Outline2", TextHAlignment::CENTER, s.width); label4->setPosition(Vec2(s.width / 2, s.height * 0.36)); - label4->setTextColor(Color4B::RED); - label4->enableOutline(Color4B::BLUE, 2); + label4->setTextColor(Color32::RED); + label4->enableOutline(Color32::BLUE, 2); label4->enableUnderline(); label4->enableStrikethrough(); addChild(label4); @@ -3365,21 +3365,21 @@ LabelUnderlineStrikethroughMultiline::LabelUnderlineStrikethroughMultiline() Label* labels[count]; labels[0] = Label::createWithSystemFont("SystemFont TextVAlignment::TOP\nusing setTextColor(255, 0, 255, 100)", font, 14, Vec2::ZERO, TextHAlignment::LEFT, TextVAlignment::TOP); - labels[0]->setTextColor(Color4B(255, 0, 255, 100)); - labels[0]->enableGlow(Color4B::BLUE); + labels[0]->setTextColor(Color32(255, 0, 255, 100)); + labels[0]->enableGlow(Color32::BLUE); labels[1] = Label::createWithSystemFont("SystemFont TextVAlignment::CENTER\nusing setColor(*RED*)", font, 14, Vec2::ZERO, TextHAlignment::RIGHT, TextVAlignment::CENTER); labels[1]->setColor(Color3B::RED); labels[2] = Label::createWithSystemFont("SystemFont TextVAlignment::BOTTOM\nusingsetTextColor(*YELLOW)", font, 14, Vec2::ZERO, TextHAlignment::CENTER, TextVAlignment::BOTTOM); - labels[2]->setTextColor(Color4B::YELLOW); + labels[2]->setTextColor(Color32::YELLOW); labels[3] = Label::createWithBMFont("fonts/bitmapFontTest5.fnt", "BMFont\nwith default color", TextHAlignment::CENTER, s.width); labels[4] = Label::createWithBMFont("fonts/bitmapFontTest5.fnt", "BMFont\nusing setTextColor(0, 255, 0, 100)", TextHAlignment::CENTER, s.width); - labels[4]->setTextColor(Color4B(0, 255, 0, 100)); + labels[4]->setTextColor(Color32(0, 255, 0, 100)); labels[5] = Label::createWithTTF(ttfConfig, "TTF setColor(*BLUE*)\nwith multiline 1\nand a much more longer multiline 2", TextHAlignment::LEFT, s.width); @@ -3387,7 +3387,7 @@ LabelUnderlineStrikethroughMultiline::LabelUnderlineStrikethroughMultiline() labels[6] = Label::createWithTTF("TTF setTextColor(*RED*)\nwith multiline 1\nand a much more longer multiline 2", font, 14); - labels[6]->setTextColor(Color4B::RED); + labels[6]->setTextColor(Color32::RED); for (int i = 0; i < count; i++) { @@ -3632,14 +3632,14 @@ LabelIssue15214::LabelIssue15214() // 3 Label* label3 = Label::createWithTTF("TTF with setTextColor()", "fonts/arial.ttf", 24.0f); label3->enableUnderline(); - label3->setTextColor(Color4B::BLUE); + label3->setTextColor(Color32::BLUE); label3->setPosition(size.width / 2, size.height / 5 * 2); this->addChild(label3); // 4 Label* label4 = Label::createWithSystemFont("System with setTextColor()", "Verdana", 24.0f); label4->enableUnderline(); - label4->setTextColor(Color4B::BLUE); + label4->setTextColor(Color32::BLUE); label4->setPosition(size.width / 2, size.height / 5 * 1); this->addChild(label4); } @@ -3697,7 +3697,7 @@ LabelIssue16471::LabelIssue16471() // if set false then testLabel:setTextColor is useful node->setCascadeColorEnabled(true); Label* label = Label::createWithTTF("Should be Yellow", "fonts/arial.ttf", 12); - label->setTextColor(Color4B::YELLOW); + label->setTextColor(Color32::YELLOW); node->addChild(label); } @@ -3723,16 +3723,16 @@ LabelIssue16717::LabelIssue16717() { auto label = Label::createWithTTF("Hello World", "fonts/arial.ttf", 70); label->setPosition(VisibleRect::center() + Vec2(0.0f, 40.0f)); - label->enableOutline(Color4B(0, 255, 0, 100), 10); // Set 100 alpha for outline - label->setTextColor(Color4B(0, 0, 255, 100)); // Also set 100 alpha for text + label->enableOutline(Color32(0, 255, 0, 100), 10); // Set 100 alpha for outline + label->setTextColor(Color32(0, 0, 255, 100)); // Also set 100 alpha for text addChild(label); } { auto label = Label::createWithTTF("Hello World", "fonts/arial.ttf", 70); label->setPosition(VisibleRect::center() + Vec2(0.0f, -40.0f)); - label->enableOutline(Color4B(0, 255, 0, 100), 10); // Set 100 alpha for outline - label->setTextColor(Color4B(0, 255, 0, 100)); // Also set 100 alpha for text + label->enableOutline(Color32(0, 255, 0, 100), 10); // Set 100 alpha for outline + label->setTextColor(Color32(0, 255, 0, 100)); // Also set 100 alpha for text addChild(label); } } diff --git a/tests/cpp-tests/Source/LayerTest/LayerTest.cpp b/tests/cpp-tests/Source/LayerTest/LayerTest.cpp index d89e55161d4a..db2b010bd449 100644 --- a/tests/cpp-tests/Source/LayerTest/LayerTest.cpp +++ b/tests/cpp-tests/Source/LayerTest/LayerTest.cpp @@ -118,7 +118,7 @@ void LayerTestCascadingOpacityB::onEnter() LayerTest::onEnter(); auto s = Director::getInstance()->getWinSize(); - auto layer1 = LayerColor::create(Color4B(192, 0, 0, 255), s.width, s.height / 2); + auto layer1 = LayerColor::create(Color32(192, 0, 0, 255), s.width, s.height / 2); layer1->setCascadeColorEnabled(false); layer1->setPosition(Vec2(0.0f, s.height / 2)); @@ -158,7 +158,7 @@ void LayerTestCascadingOpacityC::onEnter() LayerTest::onEnter(); auto s = Director::getInstance()->getWinSize(); - auto layer1 = LayerColor::create(Color4B(192, 0, 0, 255), s.width, s.height / 2); + auto layer1 = LayerColor::create(Color32(192, 0, 0, 255), s.width, s.height / 2); layer1->setCascadeColorEnabled(false); layer1->setCascadeOpacityEnabled(false); @@ -235,7 +235,7 @@ void LayerTestCascadingColorB::onEnter() { LayerTest::onEnter(); auto s = Director::getInstance()->getWinSize(); - auto layer1 = LayerColor::create(Color4B(255, 255, 255, 255), s.width, s.height / 2); + auto layer1 = LayerColor::create(Color32(255, 255, 255, 255), s.width, s.height / 2); layer1->setPosition(Vec2(0.0f, s.height / 2)); @@ -274,7 +274,7 @@ void LayerTestCascadingColorC::onEnter() { LayerTest::onEnter(); auto s = Director::getInstance()->getWinSize(); - auto layer1 = LayerColor::create(Color4B(255, 255, 255, 255), s.width, s.height / 2); + auto layer1 = LayerColor::create(Color32(255, 255, 255, 255), s.width, s.height / 2); layer1->setCascadeColorEnabled(false); layer1->setPosition(Vec2(0.0f, s.height / 2)); @@ -322,7 +322,7 @@ void LayerTest1::onEnter() _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this); auto s = Director::getInstance()->getWinSize(); - auto layer = LayerColor::create(Color4B(0xFF, 0x00, 0x00, 0x80), 200, 200); + auto layer = LayerColor::create(Color32(0xFF, 0x00, 0x00, 0x80), 200, 200); layer->setIgnoreAnchorPointForPosition(false); layer->setPosition(Vec2(s.width / 2, s.height / 2)); @@ -372,12 +372,12 @@ void LayerTest2::onEnter() LayerTest::onEnter(); auto s = Director::getInstance()->getWinSize(); - auto layer1 = LayerColor::create(Color4B(255, 255, 0, 80), 100, 300); + auto layer1 = LayerColor::create(Color32(255, 255, 0, 80), 100, 300); layer1->setPosition(Vec2(s.width / 3, s.height / 2)); layer1->setIgnoreAnchorPointForPosition(false); addChild(layer1, 1); - auto layer2 = LayerColor::create(Color4B(0, 0, 255, 255), 100, 300); + auto layer2 = LayerColor::create(Color32(0, 0, 255, 255), 100, 300); layer2->setPosition(Vec2((s.width / 3) * 2, s.height / 2)); layer2->setIgnoreAnchorPointForPosition(false); addChild(layer2, 1); @@ -407,7 +407,7 @@ std::string LayerTest2::subtitle() const LayerTestBlend::LayerTestBlend() { auto s = Director::getInstance()->getWinSize(); - auto layer1 = LayerColor::create(Color4B(255, 255, 255, 80)); + auto layer1 = LayerColor::create(Color32(255, 255, 255, 80)); auto sister1 = Sprite::create(s_pathSister1); auto sister2 = Sprite::create(s_pathSister2); @@ -456,7 +456,7 @@ std::string LayerTestBlend::subtitle() const //------------------------------------------------------------------ LayerGradientTest::LayerGradientTest() { - auto layer1 = LayerGradient::create(Color4B(255, 0, 0, 255), Color4B(0, 255, 0, 255), Vec2(0.9f, 0.9f)); + auto layer1 = LayerGradient::create(Color32(255, 0, 0, 255), Color32(0, 255, 0, 255), Vec2(0.9f, 0.9f)); addChild(layer1, 0, kTagLayer); auto listener = EventListenerTouchAllAtOnce::create(); @@ -513,7 +513,7 @@ std::string LayerGradientTest::subtitle() const //------------------------------------------------------------------ LayerGradientTest2::LayerGradientTest2() { - auto layer = LayerGradient::create(Color4B(255, 0, 0, 255), Color4B(255, 255, 0, 255)); + auto layer = LayerGradient::create(Color32(255, 0, 0, 255), Color32(255, 255, 0, 255)); addChild(layer); } @@ -537,7 +537,7 @@ void LayerIgnoreAnchorPointPos::onEnter() auto s = Director::getInstance()->getWinSize(); - auto l = LayerColor::create(Color4B(255, 0, 0, 255), 150, 150); + auto l = LayerColor::create(Color32(255, 0, 0, 255), 150, 150); l->setAnchorPoint(Vec2(0.5f, 0.5f)); l->setPosition(Vec2(s.width / 2, s.height / 2)); @@ -586,7 +586,7 @@ void LayerIgnoreAnchorPointRot::onEnter() LayerTest::onEnter(); auto s = Director::getInstance()->getWinSize(); - auto l = LayerColor::create(Color4B(255, 0, 0, 255), 200, 200); + auto l = LayerColor::create(Color32(255, 0, 0, 255), 200, 200); l->setAnchorPoint(Vec2(0.5f, 0.5f)); l->setPosition(Vec2(s.width / 2, s.height / 2)); @@ -634,7 +634,7 @@ void LayerIgnoreAnchorPointScale::onEnter() auto s = Director::getInstance()->getWinSize(); - auto l = LayerColor::create(Color4B(255, 0, 0, 255), 200, 200); + auto l = LayerColor::create(Color32(255, 0, 0, 255), 200, 200); l->setAnchorPoint(Vec2(0.5f, 1.0f)); l->setPosition(Vec2(s.width / 2, s.height / 2)); @@ -680,12 +680,12 @@ std::string LayerIgnoreAnchorPointScale::subtitle() const LayerExtendedBlendOpacityTest::LayerExtendedBlendOpacityTest() { - auto layer1 = LayerGradient::create(Color4B(255, 0, 0, 255), Color4B(255, 0, 255, 255)); + auto layer1 = LayerGradient::create(Color32(255, 0, 0, 255), Color32(255, 0, 255, 255)); layer1->setContentSize(Size(80.0f, 80.0f)); layer1->setPosition(Vec2(50.0f, 50.0f)); addChild(layer1); - auto layer2 = LayerGradient::create(Color4B(0, 0, 0, 127), Color4B(255, 255, 255, 127)); + auto layer2 = LayerGradient::create(Color32(0, 0, 0, 127), Color32(255, 255, 255, 127)); layer2->setContentSize(Size(80.0f, 80.0f)); layer2->setPosition(Vec2(100.0f, 90.0f)); addChild(layer2); @@ -719,7 +719,7 @@ void LayerBug3162A::onEnter() Size size = VisibleRect::getVisibleRect().size; size.width = size.width / 2; size.height = size.height / 3; - Color4B color[3] = {Color4B(255, 0, 0, 255), Color4B(0, 255, 0, 255), Color4B(0, 0, 255, 255)}; + Color32 color[3] = {Color32(255, 0, 0, 255), Color32(0, 255, 0, 255), Color32(0, 0, 255, 255)}; for (int i = 0; i < 3; ++i) { @@ -762,7 +762,7 @@ void LayerBug3162B::onEnter() Size size = VisibleRect::getVisibleRect().size; size.width = size.width / 2; size.height = size.height / 3; - Color4B color[3] = {Color4B(200, 0, 0, 255), Color4B(150, 0, 0, 255), Color4B(100, 0, 0, 255)}; + Color32 color[3] = {Color32(200, 0, 0, 255), Color32(150, 0, 0, 255), Color32(100, 0, 0, 255)}; for (int i = 0; i < 3; ++i) { @@ -814,7 +814,7 @@ void LayerColorOccludeBug::onEnter() { LayerTest::onEnter(); Director::getInstance()->getRenderer()->setDepthTest(true); - _layer = LayerColor::create(Color4B(0, 80, 95, 255)); + _layer = LayerColor::create(Color32(0, 80, 95, 255)); addChild(_layer); } @@ -838,12 +838,12 @@ void LayerRadialGradientTest::onEnter() _currentSeletedItemIndex = 0; auto director = Director::getInstance(); - director->setClearColor(Color4F(0, 0, 0, 0)); + director->setClearColor(Color(0, 0, 0, 0)); auto origin = director->getVisibleOrigin(); auto size = director->getVisibleSize(); Vec2 center(origin.x + size.width / 2 + 50, origin.y + size.height / 2); float radius = (size.height - 50) / 2; - _layer = LayerRadialGradient::create(Color4B(145, 106, 209, 140), Color4B(0, 0, 0, 0), radius, center, 1.0f); + _layer = LayerRadialGradient::create(Color32(145, 106, 209, 140), Color32(0, 0, 0, 0), radius, center, 1.0f); addChild(_layer); auto scaleSlider = LayerRadialGradientTest::createSlider(); diff --git a/tests/cpp-tests/Source/MeshRendererTest/DrawNode3D.cpp b/tests/cpp-tests/Source/MeshRendererTest/DrawNode3D.cpp index d70826688c06..e620a18fe629 100644 --- a/tests/cpp-tests/Source/MeshRendererTest/DrawNode3D.cpp +++ b/tests/cpp-tests/Source/MeshRendererTest/DrawNode3D.cpp @@ -65,7 +65,7 @@ void DrawNode3D::ensureCapacity(int count) if (!_customCommand.getVertexBuffer() || _customCommand.getVertexBuffer()->getSize() < (EXTENDED_SIZE * sizeof(_bufferLines[0]))) { - _customCommand.createVertexBuffer(sizeof(V3F_C4B), EXTENDED_SIZE + (EXTENDED_SIZE >> 1), + _customCommand.createVertexBuffer(sizeof(V3F_C4F), EXTENDED_SIZE + (EXTENDED_SIZE >> 1), CustomCommand::BufferUsage::DYNAMIC); } } @@ -90,7 +90,7 @@ bool DrawNode3D::init() _customCommand.setDrawType(CustomCommand::DrawType::ARRAY); _customCommand.setPrimitiveType(CustomCommand::PrimitiveType::LINE); - _customCommand.createVertexBuffer(sizeof(V3F_C4B), INITIAL_VERTEX_BUFFER_LENGTH, + _customCommand.createVertexBuffer(sizeof(V3F_C4F), INITIAL_VERTEX_BUFFER_LENGTH, CustomCommand::BufferUsage::DYNAMIC); _isDirty = true; @@ -142,25 +142,18 @@ void DrawNode3D::updateCommand(ax::Renderer* renderer, const Mat4& transform, ui AX_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1, _bufferLines.size()); } -void DrawNode3D::drawLine(const Vec3& from, const Vec3& to, const Color4F& color) +void DrawNode3D::drawLine(const Vec3& from, const Vec3& to, const Color& color) { unsigned int vertex_count = 2; ensureCapacity(vertex_count); - Color4B col = Color4B(color); - V3F_C4B a = {Vec3(from.x, from.y, from.z), col}; - V3F_C4B b = { - Vec3(to.x, to.y, to.z), - col, - }; - - _bufferLines.emplace_back(a); - _bufferLines.emplace_back(b); + _bufferLines.emplace_back(from, color); + _bufferLines.emplace_back(to, color); _isDirty = true; } -void DrawNode3D::drawCube(Vec3* vertices, const Color4F& color) +void DrawNode3D::drawCube(Vec3* vertices, const Color& color) { // front face drawLine(vertices[0], vertices[1], color); diff --git a/tests/cpp-tests/Source/MeshRendererTest/DrawNode3D.h b/tests/cpp-tests/Source/MeshRendererTest/DrawNode3D.h index 83682058fdf7..092b8353d52e 100644 --- a/tests/cpp-tests/Source/MeshRendererTest/DrawNode3D.h +++ b/tests/cpp-tests/Source/MeshRendererTest/DrawNode3D.h @@ -43,7 +43,7 @@ class DrawNode3D : public ax::Node /** * Draw 3D Line */ - void drawLine(const ax::Vec3& from, const ax::Vec3& to, const Color4F& color); + void drawLine(const ax::Vec3& from, const ax::Vec3& to, const Color& color); /** * Draw 3D cube @@ -58,7 +58,7 @@ class DrawNode3D : public ax::Node * vertices[7]:Left-top-back. * @param color */ - void drawCube(ax::Vec3* vertices, const Color4F& color); + void drawCube(ax::Vec3* vertices, const Color& color); /** Clear the geometry in the node's buffer. */ void clear(); @@ -98,7 +98,7 @@ class DrawNode3D : public ax::Node backend::ProgramState* _programStateLine = nullptr; backend::DepthStencilDescriptor* _depthstencilDescriptor = nullptr; backend::UniformLocation _locMVPMatrix; - std::vector _bufferLines; + axstd::pod_vector _bufferLines; private: AX_DISALLOW_COPY_AND_ASSIGN(DrawNode3D); diff --git a/tests/cpp-tests/Source/MeshRendererTest/MeshRendererTest.cpp b/tests/cpp-tests/Source/MeshRendererTest/MeshRendererTest.cpp index 67582224e2fd..55eea18145cd 100644 --- a/tests/cpp-tests/Source/MeshRendererTest/MeshRendererTest.cpp +++ b/tests/cpp-tests/Source/MeshRendererTest/MeshRendererTest.cpp @@ -1657,7 +1657,7 @@ void MeshRendererWithOBBPerformanceTest::update(float dt) Vec3 corners[8] = {}; _obbt.getCorners(corners); - _drawDebug->drawCube(corners, Color4F(0, 0, 1, 1)); + _drawDebug->drawCube(corners, Color(0, 0, 1, 1)); } if (_obb.size() > 0) { @@ -1667,7 +1667,7 @@ void MeshRendererWithOBBPerformanceTest::update(float dt) { Vec3 corners[8] = {}; _obb[i].getCorners(corners); - _drawOBB->drawCube(corners, _obbt.intersects(_obb[i]) ? Color4F(1, 0, 0, 1) : Color4F(0, 1, 0, 1)); + _drawOBB->drawCube(corners, _obbt.intersects(_obb[i]) ? Color(1, 0, 0, 1) : Color(0, 1, 0, 1)); } } } @@ -2010,7 +2010,7 @@ void UseCaseMeshRenderer::switchCase() } else if (_caseIdx == 1) // use case 2, ui - 3d - ui, last ui should on the top { - auto layer = LayerColor::create(Color4B(0, 0, 100, 255), s.width / 2.f, s.height / 2.f); + auto layer = LayerColor::create(Color32(0, 0, 100, 255), s.width / 2.f, s.height / 2.f); layer->setPosition(s.width * 0.25f, s.height * 0.25f); layer->setGlobalZOrder(-1); addChild(layer); @@ -2056,7 +2056,7 @@ void UseCaseMeshRenderer::menuCallback_Message(Object* sender) { // create a new message layer on the top auto s = layer->getContentSize(); - auto messagelayer = LayerColor::create(Color4B(100, 100, 0, 255)); + auto messagelayer = LayerColor::create(Color32(100, 100, 0, 255)); messagelayer->setContentSize(Size(s.width * 0.5f, s.height * 0.5f)); messagelayer->setPosition(Vec2(s.width * 0.25f, s.height * 0.25f)); auto label = Label::create(); @@ -2558,7 +2558,7 @@ void CameraBackgroundClearTest::switch_CameraClearMode(ax::Object* sender) } else if (type == CameraBackgroundBrush::BrushType::DEPTH) { - _camera->setBackgroundBrush(CameraBackgroundBrush::createColorBrush(Color4F(1.f, 0.f, 0.f, 1.f), 1.f)); + _camera->setBackgroundBrush(CameraBackgroundBrush::createColorBrush(Color(1.f, 0.f, 0.f, 1.f), 1.f)); _label->setString("Color Clear Brush"); } else if (type == CameraBackgroundBrush::BrushType::COLOR) diff --git a/tests/cpp-tests/Source/MultiTouchTest/MultiTouchTest.cpp b/tests/cpp-tests/Source/MultiTouchTest/MultiTouchTest.cpp index 4c79536d9563..342bdcead23b 100644 --- a/tests/cpp-tests/Source/MultiTouchTest/MultiTouchTest.cpp +++ b/tests/cpp-tests/Source/MultiTouchTest/MultiTouchTest.cpp @@ -41,7 +41,7 @@ class TouchPoint : public Node { DrawNode* drawNode = DrawNode::create(); auto s = Director::getInstance()->getWinSize(); - Color4F color(touchColor.r / 255.0f, touchColor.g / 255.0f, touchColor.b / 255.0f, 1.0f); + Color color(touchColor.r / 255.0f, touchColor.g / 255.0f, touchColor.b / 255.0f, 1.0f); drawNode->drawLine(Vec2(0.0f, touchPoint.y), Vec2(s.width, touchPoint.y), color); drawNode->drawLine(Vec2(touchPoint.x, 0.0f), Vec2(touchPoint.x, s.height), color); drawNode->drawDot(touchPoint, 3, color); diff --git a/tests/cpp-tests/Source/NewEventDispatcherTest/NewEventDispatcherTest.cpp b/tests/cpp-tests/Source/NewEventDispatcherTest/NewEventDispatcherTest.cpp index 04f0a2a1911e..8e4932bb8a41 100644 --- a/tests/cpp-tests/Source/NewEventDispatcherTest/NewEventDispatcherTest.cpp +++ b/tests/cpp-tests/Source/NewEventDispatcherTest/NewEventDispatcherTest.cpp @@ -1222,7 +1222,7 @@ PauseResumeTargetTest::PauseResumeTargetTest() sprite3->getListener()->setEnabled(false); _eventDispatcher->pauseEventListenersForTarget(this, true); - auto colorLayer = LayerColor::create(Color4B(0, 0, 255, 100)); + auto colorLayer = LayerColor::create(Color32(0, 0, 255, 100)); this->addChild(colorLayer, 99999); auto closeItem = MenuItemFont::create("close", [this, colorLayer, sprite3](Object* /*sender*/) { diff --git a/tests/cpp-tests/Source/NewRendererTest/NewRendererTest.cpp b/tests/cpp-tests/Source/NewRendererTest/NewRendererTest.cpp index 8a1e4cb20823..a82c8185d5ec 100644 --- a/tests/cpp-tests/Source/NewRendererTest/NewRendererTest.cpp +++ b/tests/cpp-tests/Source/NewRendererTest/NewRendererTest.cpp @@ -258,7 +258,7 @@ NewClippingNodeTest::NewClippingNodeTest() // rectangle[2] = Vec2(clipper->getContentSize().width, clipper->getContentSize().height); // rectangle[3] = Vec2(0, clipper->getContentSize().height); // - // Color4F white(1, 1, 1, 1); + // Color white(1, 1, 1, 1); // stencil->drawPolygon(rectangle, 4, white, 1, white); // clipper->setStencil(stencil); @@ -343,7 +343,7 @@ NewDrawNodeTest::NewDrawNodeTest() rectangle[2] = Vec2(50, 50); rectangle[3] = Vec2(-50, 50); - Color4F white(1, 1, 1, 1); + Color white(1, 1, 1, 1); rectNode->drawPolygon(rectangle, 4, white, 1, white); parent->addChild(rectNode); } @@ -971,7 +971,7 @@ NonBatchSprites::NonBatchSprites() _totalSprites = Label::createWithTTF(TTFConfig("fonts/arial.ttf"), "sprites"); _totalSprites->setColor(Color3B::YELLOW); - _totalSprites->enableOutline(Color4B::RED, 2); + _totalSprites->enableOutline(Color32::RED, 2); _totalSprites->setPosition(s.width / 2, s.height / 2); addChild(_totalSprites); diff --git a/tests/cpp-tests/Source/NodeTest/NodeTest.cpp b/tests/cpp-tests/Source/NodeTest/NodeTest.cpp index bf7960cbc88a..e146c20af402 100644 --- a/tests/cpp-tests/Source/NodeTest/NodeTest.cpp +++ b/tests/cpp-tests/Source/NodeTest/NodeTest.cpp @@ -963,8 +963,8 @@ bool MySprite::setProgramState(backend::ProgramState* programState, bool ownPS/* _customCommand.setDrawType(CustomCommand::DrawType::ARRAY); _customCommand.setPrimitiveType(CustomCommand::PrimitiveType::TRIANGLE_STRIP); - _customCommand.createVertexBuffer(sizeof(V3F_C4B_T2F), 4, CustomCommand::BufferUsage::STATIC); - _customCommand.updateVertexBuffer(&_quad, 4 * sizeof(V3F_C4B_T2F)); + _customCommand.createVertexBuffer(sizeof(V3F_T2F_C4B), 4, CustomCommand::BufferUsage::STATIC); + _customCommand.updateVertexBuffer(&_quad, 4 * sizeof(V3F_T2F_C4B)); return true; } return false; @@ -1500,9 +1500,9 @@ void Issue16735Test::onEnter() auto d = DrawNode::create(); d->drawLine(Vec2(origin.x, origin.y + visibleSize.height / 2), - Vec2(origin.x + visibleSize.width, origin.y + visibleSize.height / 2), Color4F::RED); + Vec2(origin.x + visibleSize.width, origin.y + visibleSize.height / 2), Color::RED); d->drawLine(Vec2(origin.x + visibleSize.width / 2, origin.y), - Vec2(origin.x + visibleSize.width / 2, origin.y + visibleSize.height), Color4F::RED); + Vec2(origin.x + visibleSize.width / 2, origin.y + visibleSize.height), Color::RED); addChild(d); } diff --git a/tests/cpp-tests/Source/ParticleTest/ParticleTest.cpp b/tests/cpp-tests/Source/ParticleTest/ParticleTest.cpp index b30d6f75cdff..57115f00bae8 100644 --- a/tests/cpp-tests/Source/ParticleTest/ParticleTest.cpp +++ b/tests/cpp-tests/Source/ParticleTest/ParticleTest.cpp @@ -236,16 +236,16 @@ void DemoBigFlower::onEnter() _emitter->setEndSpinVar(0); // color of particles - Color4F startColor(0.5f, 0.5f, 0.5f, 1.0f); + Color startColor(0.5f, 0.5f, 0.5f, 1.0f); _emitter->setStartColor(startColor); - Color4F startColorVar(0.5f, 0.5f, 0.5f, 1.0f); + Color startColorVar(0.5f, 0.5f, 0.5f, 1.0f); _emitter->setStartColorVar(startColorVar); - Color4F endColor(0.1f, 0.1f, 0.1f, 0.2f); + Color endColor(0.1f, 0.1f, 0.1f, 0.2f); _emitter->setEndColor(endColor); - Color4F endColorVar(0.1f, 0.1f, 0.1f, 0.2f); + Color endColorVar(0.1f, 0.1f, 0.1f, 0.2f); _emitter->setEndColorVar(endColorVar); // size, in pixels @@ -320,16 +320,16 @@ void DemoRotFlower::onEnter() _emitter->setEndSpinVar(2000); // color of particles - Color4F startColor(0.5f, 0.5f, 0.5f, 1.0f); + Color startColor(0.5f, 0.5f, 0.5f, 1.0f); _emitter->setStartColor(startColor); - Color4F startColorVar(0.5f, 0.5f, 0.5f, 1.0f); + Color startColorVar(0.5f, 0.5f, 0.5f, 1.0f); _emitter->setStartColorVar(startColorVar); - Color4F endColor(0.1f, 0.1f, 0.1f, 0.2f); + Color endColor(0.1f, 0.1f, 0.1f, 0.2f); _emitter->setEndColor(endColor); - Color4F endColorVar(0.1f, 0.1f, 0.1f, 0.2f); + Color endColorVar(0.1f, 0.1f, 0.1f, 0.2f); _emitter->setEndColorVar(endColorVar); // size, in pixels @@ -472,13 +472,13 @@ void DemoSnow::onEnter() _emitter->setSpeed(130); _emitter->setSpeedVar(30); - Color4F startColor = _emitter->getStartColor(); + Color startColor = _emitter->getStartColor(); startColor.r = 0.9f; startColor.g = 0.9f; startColor.b = 0.9f; _emitter->setStartColor(startColor); - Color4F startColorVar = _emitter->getStartColorVar(); + Color startColorVar = _emitter->getStartColorVar(); startColorVar.b = 0.1f; _emitter->setStartColorVar(startColorVar); @@ -574,16 +574,16 @@ void DemoModernArt::onEnter() _emitter->setEmissionRate(_emitter->getTotalParticles() / _emitter->getLife()); // color of particles - Color4F startColor(0.5f, 0.5f, 0.5f, 1.0f); + Color startColor(0.5f, 0.5f, 0.5f, 1.0f); _emitter->setStartColor(startColor); - Color4F startColorVar(0.5f, 0.5f, 0.5f, 1.0f); + Color startColorVar(0.5f, 0.5f, 0.5f, 1.0f); _emitter->setStartColorVar(startColorVar); - Color4F endColor(0.1f, 0.1f, 0.1f, 0.2f); + Color endColor(0.1f, 0.1f, 0.1f, 0.2f); _emitter->setEndColor(endColor); - Color4F endColorVar(0.1f, 0.1f, 0.1f, 0.2f); + Color endColorVar(0.1f, 0.1f, 0.1f, 0.2f); _emitter->setEndColorVar(endColorVar); // size, in pixels @@ -1776,8 +1776,8 @@ void DemoEmissionShapeAlphaMask::onEnter() _emitter->setSpeed(0); _emitter->setSpeedVar(0); - _emitter->setStartColor(Color4F::WHITE); - _emitter->setEndColor(Color4F::WHITE); + _emitter->setStartColor(Color::WHITE); + _emitter->setEndColor(Color::WHITE); _emitter->setStartSize(6); _emitter->setEndSize(3); @@ -1853,16 +1853,16 @@ void RadiusMode1::onEnter() _emitter->setEndSpinVar(0); // color of particles - Color4F startColor(0.5f, 0.5f, 0.5f, 1.0f); + Color startColor(0.5f, 0.5f, 0.5f, 1.0f); _emitter->setStartColor(startColor); - Color4F startColorVar(0.5f, 0.5f, 0.5f, 1.0f); + Color startColorVar(0.5f, 0.5f, 0.5f, 1.0f); _emitter->setStartColorVar(startColorVar); - Color4F endColor(0.1f, 0.1f, 0.1f, 0.2f); + Color endColor(0.1f, 0.1f, 0.1f, 0.2f); _emitter->setEndColor(endColor); - Color4F endColorVar(0.1f, 0.1f, 0.1f, 0.2f); + Color endColorVar(0.1f, 0.1f, 0.1f, 0.2f); _emitter->setEndColorVar(endColorVar); // size, in pixels @@ -1936,16 +1936,16 @@ void RadiusMode2::onEnter() _emitter->setEndSpinVar(0); // color of particles - Color4F startColor(0.5f, 0.5f, 0.5f, 1.0f); + Color startColor(0.5f, 0.5f, 0.5f, 1.0f); _emitter->setStartColor(startColor); - Color4F startColorVar(0.5f, 0.5f, 0.5f, 1.0f); + Color startColorVar(0.5f, 0.5f, 0.5f, 1.0f); _emitter->setStartColorVar(startColorVar); - Color4F endColor(0.1f, 0.1f, 0.1f, 0.2f); + Color endColor(0.1f, 0.1f, 0.1f, 0.2f); _emitter->setEndColor(endColor); - Color4F endColorVar(0.1f, 0.1f, 0.1f, 0.2f); + Color endColorVar(0.1f, 0.1f, 0.1f, 0.2f); _emitter->setEndColorVar(endColorVar); // size, in pixels @@ -2019,16 +2019,16 @@ void Issue704::onEnter() _emitter->setEndSpinVar(0); // color of particles - Color4F startColor(0.5f, 0.5f, 0.5f, 1.0f); + Color startColor(0.5f, 0.5f, 0.5f, 1.0f); _emitter->setStartColor(startColor); - Color4F startColorVar(0.5f, 0.5f, 0.5f, 1.0f); + Color startColorVar(0.5f, 0.5f, 0.5f, 1.0f); _emitter->setStartColorVar(startColorVar); - Color4F endColor(0.1f, 0.1f, 0.1f, 0.2f); + Color endColor(0.1f, 0.1f, 0.1f, 0.2f); _emitter->setEndColor(endColor); - Color4F endColorVar(0.1f, 0.1f, 0.1f, 0.2f); + Color endColorVar(0.1f, 0.1f, 0.1f, 0.2f); _emitter->setEndColorVar(endColorVar); // size, in pixels @@ -2233,7 +2233,7 @@ void ParticleDemo::onEnter() MenuItemFont::setFontSize(32); - _color = LayerColor::create(Color4B(127, 127, 127, 255)); + _color = LayerColor::create(Color32(127, 127, 127, 255)); this->addChild(_color); _emitter = nullptr; @@ -2405,11 +2405,11 @@ void ParticleBatchMultipleEmitters::onEnter() _background = nullptr; auto emitter1 = ParticleSystemQuad::create("Particles/LavaFlow.plist"); - emitter1->setStartColor(Color4F(1, 0, 0, 1)); + emitter1->setStartColor(Color(1, 0, 0, 1)); auto emitter2 = ParticleSystemQuad::create("Particles/LavaFlow.plist"); - emitter2->setStartColor(Color4F(0, 1, 0, 1)); + emitter2->setStartColor(Color(0, 1, 0, 1)); auto emitter3 = ParticleSystemQuad::create("Particles/LavaFlow.plist"); - emitter3->setStartColor(Color4F(0, 0, 1, 1)); + emitter3->setStartColor(Color(0, 0, 1, 1)); auto s = Director::getInstance()->getWinSize(); @@ -2457,13 +2457,13 @@ void ParticleReorder::onEnter() auto parent = (i == 0 ? parent1 : parent2); auto emitter1 = ParticleSystemQuad::create("Particles/SmallSun.plist"); - emitter1->setStartColor(Color4F(1, 0, 0, 1)); + emitter1->setStartColor(Color(1, 0, 0, 1)); emitter1->setBlendAdditive(false); auto emitter2 = ParticleSystemQuad::create("Particles/SmallSun.plist"); - emitter2->setStartColor(Color4F(0, 1, 0, 1)); + emitter2->setStartColor(Color(0, 1, 0, 1)); emitter2->setBlendAdditive(false); auto emitter3 = ParticleSystemQuad::create("Particles/SmallSun.plist"); - emitter3->setStartColor(Color4F(0, 0, 1, 1)); + emitter3->setStartColor(Color(0, 0, 1, 1)); emitter3->setBlendAdditive(false); auto s = Director::getInstance()->getWinSize(); @@ -2586,8 +2586,8 @@ bool RainbowEffect::initWithTotalParticles(int numberOfParticles) setEmissionRate(getTotalParticles() / getLife()); // color of particles - setStartColor(Color4F(Color4B(50, 50, 50, 50))); - setEndColor(Color4F(Color4B(0, 0, 0, 0))); + setStartColor(Color(Color32(50, 50, 50, 50))); + setEndColor(Color(Color32(0, 0, 0, 0))); _startColorVar.r = 0.0f; _startColorVar.g = 0.0f; @@ -2900,16 +2900,16 @@ void ReorderParticleSystems::onEnter() // color of particles float color[3] = {0, 0, 0}; color[i] = 1; - Color4F startColor(color[0], color[1], color[2], 1.0f); + Color startColor(color[0], color[1], color[2], 1.0f); particleSystem->setStartColor(startColor); - Color4F startColorVar(0, 0, 0, 0); + Color startColorVar(0, 0, 0, 0); particleSystem->setStartColorVar(startColorVar); - Color4F endColor = startColor; + Color endColor = startColor; particleSystem->setEndColor(endColor); - Color4F endColorVar = startColorVar; + Color endColorVar = startColorVar; particleSystem->setEndColorVar(endColorVar); // size, in pixels @@ -3019,10 +3019,10 @@ void PremultipliedAlphaTest::onEnter() // Toggle next line to see old behavior // this->emitter.opacityModifyRGB = NO; - _emitter->setStartColor(Color4F(1.0f, 1.0f, 1.0f, 1.0f)); - _emitter->setEndColor(Color4F(1.0f, 1.0f, 1.0f, 0.0f)); - _emitter->setStartColorVar(Color4F(0.0f, 0.0f, 0.0f, 0.0f)); - _emitter->setEndColorVar(Color4F(0.0f, 0.0f, 0.0f, 0.0f)); + _emitter->setStartColor(Color(1.0f, 1.0f, 1.0f, 1.0f)); + _emitter->setEndColor(Color(1.0f, 1.0f, 1.0f, 0.0f)); + _emitter->setStartColorVar(Color(0.0f, 0.0f, 0.0f, 0.0f)); + _emitter->setEndColorVar(Color(0.0f, 0.0f, 0.0f, 0.0f)); this->addChild(_emitter, 10); _hasEmitter = true; diff --git a/tests/cpp-tests/Source/PhysicsTest/PhysicsTest.cpp b/tests/cpp-tests/Source/PhysicsTest/PhysicsTest.cpp index b24ea2c322e7..24bdc0cafa4b 100644 --- a/tests/cpp-tests/Source/PhysicsTest/PhysicsTest.cpp +++ b/tests/cpp-tests/Source/PhysicsTest/PhysicsTest.cpp @@ -25,7 +25,7 @@ #include "PhysicsTest.h" -#if defined(AX_ENABLE_PHYSICS) +#if defined(AX_ENABLE_PHYSICS) && 0 # include # include "ui/CocosGUI.h" @@ -58,7 +58,7 @@ PhysicsTests::PhysicsTests() namespace { -Color4F STATIC_COLOR(1.0f, 0.0f, 0.0f, 1.0f); +Color STATIC_COLOR(1.0f, 0.0f, 0.0f, 1.0f); const int DRAG_BODYS_TAG = 0x80; } // namespace @@ -548,7 +548,7 @@ void PhysicsDemoRayCast::update(float /*delta*/) if (point2 != point3) { - _node->drawDot(point3, 2, Color4F(1.0f, 1.0f, 1.0f, 1.0f)); + _node->drawDot(point3, 2, Color(1.0f, 1.0f, 1.0f, 1.0f)); } addChild(_node); @@ -574,7 +574,7 @@ void PhysicsDemoRayCast::update(float /*delta*/) if (point2 != point3) { - _node->drawDot(point3, 2, Color4F(1.0f, 1.0f, 1.0f, 1.0f)); + _node->drawDot(point3, 2, Color(1.0f, 1.0f, 1.0f, 1.0f)); } addChild(_node); @@ -602,7 +602,7 @@ void PhysicsDemoRayCast::update(float /*delta*/) for (int i = 0; i < num; ++i) { - _node->drawDot(points[i], 2, Color4F(1.0f, 1.0f, 1.0f, 1.0f)); + _node->drawDot(points[i], 2, Color(1.0f, 1.0f, 1.0f, 1.0f)); } addChild(_node); diff --git a/tests/cpp-tests/Source/PhysicsTest/PhysicsTest.h b/tests/cpp-tests/Source/PhysicsTest/PhysicsTest.h index 8575d6a9a958..0f74e70e4fb9 100644 --- a/tests/cpp-tests/Source/PhysicsTest/PhysicsTest.h +++ b/tests/cpp-tests/Source/PhysicsTest/PhysicsTest.h @@ -29,7 +29,7 @@ #include "../BaseTest.h" -#if defined(AX_ENABLE_PHYSICS) +#if defined(AX_ENABLE_PHYSICS) && 0 DEFINE_TEST_SUITE(PhysicsTests); diff --git a/tests/cpp-tests/Source/RenderTextureTest/RenderTextureTest.cpp b/tests/cpp-tests/Source/RenderTextureTest/RenderTextureTest.cpp index 7af6828fe18f..690f90d43c87 100644 --- a/tests/cpp-tests/Source/RenderTextureTest/RenderTextureTest.cpp +++ b/tests/cpp-tests/Source/RenderTextureTest/RenderTextureTest.cpp @@ -233,7 +233,7 @@ RenderTextureIssue937::RenderTextureIssue937() * B1: non-premulti sprite * B2: non-premulti render */ - auto background = LayerColor::create(Color4B(200, 200, 200, 255)); + auto background = LayerColor::create(Color32(200, 200, 200, 255)); addChild(background); auto s = Director::getInstance()->getWinSize(); @@ -604,7 +604,7 @@ RenderTextureTargetNode::RenderTextureTargetNode() * B1: non-premulti sprite * B2: non-premulti render */ - auto background = LayerColor::create(Color4B(40, 40, 40, 255)); + auto background = LayerColor::create(Color32(40, 40, 40, 255)); addChild(background); // sprite 1 @@ -632,8 +632,7 @@ RenderTextureTargetNode::RenderTextureTargetNode() sprite2->setAnchorPoint(Vec2::ANCHOR_MIDDLE); sprite2->setPosition(_spriteCenterPosition); renderTexture->addChild(sprite2); - - renderTexture->setClearColor(Color4F(0, 0, 0, 0)); + renderTexture->setClearColor(Color(0, 0, 0, 0)); renderTexture->setClearFlags(ClearFlag::COLOR); /* add the render texture to the scene */ @@ -661,7 +660,7 @@ void RenderTextureTargetNode::touched(Object* sender) else { renderTexture->setClearFlags(ClearFlag::NONE); - renderTexture->setClearColor(Color4F(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1)); + renderTexture->setClearColor(Color(AXRANDOM_0_1(), AXRANDOM_0_1(), AXRANDOM_0_1(), 1)); } } @@ -798,7 +797,7 @@ Issue16113Test::Issue16113Test() auto item1 = MenuItemFont::create("Save Image", [&](Object* ref) { auto winSize = Director::getInstance()->getVisibleSize(); auto text = Label::createWithTTF("hello world", "fonts/Marker Felt.ttf", 40); - text->setTextColor(Color4B::RED); + text->setTextColor(Color32::RED); auto target = RenderTexture::create(winSize.width, winSize.height, backend::PixelFormat::RGBA8); target->beginWithClear(0, 0, 0, 0); text->setPosition(winSize.width / 2, winSize.height / 2); diff --git a/tests/cpp-tests/Source/RotateWorldTest/RotateWorldTest.cpp b/tests/cpp-tests/Source/RotateWorldTest/RotateWorldTest.cpp index de2556dbebd1..b3ef70a32eef 100644 --- a/tests/cpp-tests/Source/RotateWorldTest/RotateWorldTest.cpp +++ b/tests/cpp-tests/Source/RotateWorldTest/RotateWorldTest.cpp @@ -124,10 +124,10 @@ void RotateWorldMainLayer::onEnter() x = size.width; y = size.height; - auto blue = LayerColor::create(Color4B(0, 0, 255, 255)); - auto red = LayerColor::create(Color4B(255, 0, 0, 255)); - auto dark = LayerColor::create(Color4B(2, 2, 2, 255)); - auto white = LayerColor::create(Color4B(255, 255, 255, 255)); + auto blue = LayerColor::create(Color32(0, 0, 255, 255)); + auto red = LayerColor::create(Color32(255, 0, 0, 255)); + auto dark = LayerColor::create(Color32(2, 2, 2, 255)); + auto white = LayerColor::create(Color32(255, 255, 255, 255)); blue->setScale(0.5f); blue->setPosition(Vec2(-x / 4, -y / 4)); diff --git a/tests/cpp-tests/Source/SceneTest/SceneTest.cpp b/tests/cpp-tests/Source/SceneTest/SceneTest.cpp index f01b28880bc2..925f4cac4953 100644 --- a/tests/cpp-tests/Source/SceneTest/SceneTest.cpp +++ b/tests/cpp-tests/Source/SceneTest/SceneTest.cpp @@ -180,7 +180,7 @@ SceneTestLayer3::SceneTestLayer3() {} bool SceneTestLayer3::init() { - if (LayerColor::initWithColor(Color4B(0, 0, 255, 255))) + if (LayerColor::initWithColor(Color32(0, 0, 255, 255))) { auto s = Director::getInstance()->getWinSize(); diff --git a/tests/cpp-tests/Source/ShaderTest/ShaderTest2.cpp b/tests/cpp-tests/Source/ShaderTest/ShaderTest2.cpp index 1697d47f6a9d..0e6b4f0120b3 100644 --- a/tests/cpp-tests/Source/ShaderTest/ShaderTest2.cpp +++ b/tests/cpp-tests/Source/ShaderTest/ShaderTest2.cpp @@ -413,7 +413,7 @@ class EffectNormalMapped : public Effect } void setKBump(float value); void setLightPos(const Vec3& pos); - void setLightColor(const Color4F& color); + void setLightColor(const Color& color); float getKBump() const { return _kBump; } protected: @@ -422,7 +422,7 @@ class EffectNormalMapped : public Effect virtual void setTarget(EffectSprite* sprite) override; EffectSprite* _sprite; Vec3 _lightPos; - Color4F _lightColor; + Color _lightColor; float _kBump; }; @@ -457,7 +457,7 @@ void EffectNormalMapped::setLightPos(const Vec3& pos) SET_UNIFORM(_programState, "u_lightPosInLocalSpace", Vec4(_lightPos.x, _lightPos.y, _lightPos.z, 1)); } -void EffectNormalMapped::setLightColor(const Color4F& color) +void EffectNormalMapped::setLightColor(const Color& color) { _lightColor = color; SET_UNIFORM(_programState, "u_diffuseL", Vec3(_lightColor.r, _lightColor.g, _lightColor.b)); @@ -470,7 +470,7 @@ bool EffectSpriteTest::init() if (ShaderTestDemo2::init()) { - auto layer = LayerColor::create(Color4B::BLUE); + auto layer = LayerColor::create(Color32::BLUE); this->addChild(layer); auto s = Director::getInstance()->getWinSize(); @@ -552,7 +552,7 @@ bool EffectSpriteLamp::init() Mat4 mat = _sprite->getNodeToWorldTransform(); Point lightPosInLocalSpace = PointApplyAffineTransform(Vec2(pos.x, pos.y), _sprite->getWorldToNodeAffineTransform()); - lampEffect->setLightColor(Color4F(1, 1, 1, 1)); + lampEffect->setLightColor(Color(1, 1, 1, 1)); lampEffect->setLightPos(Vec3(lightPosInLocalSpace.x, lightPosInLocalSpace.y, 50.0f)); lampEffect->setKBump(2); _sprite->setEffect(lampEffect); diff --git a/tests/cpp-tests/Source/SpineTest/SpineTest.cpp b/tests/cpp-tests/Source/SpineTest/SpineTest.cpp index 914c9ed70f00..a54c9dfe4f12 100644 --- a/tests/cpp-tests/Source/SpineTest/SpineTest.cpp +++ b/tests/cpp-tests/Source/SpineTest/SpineTest.cpp @@ -460,7 +460,7 @@ bool SkeletonRendererSeparatorExample::init() rect[1] = Vec2(40, 0); rect[2] = Vec2(40, 200); rect[3] = Vec2(0, 200); - betweenNode->drawPolygon(rect, 4, Color4F(1, 0, 0, 1), 1, Color4F(1, 0, 0, 1)); + betweenNode->drawPolygon(rect, 4, ax::Color(1, 0, 0, 1), 1, ax::Color(1, 0, 0, 1)); betweenNode->setPosition(Vec2(_contentSize.width / 2 + 30, 20)); // Spineboy's front, doesn't manage any skeleton, animation or GPU resources, but simply // renders the back slots of Spineboy. The skeleton, animatio state and GPU resources diff --git a/tests/cpp-tests/Source/SpritePolygonTest/SpritePolygonTest.cpp b/tests/cpp-tests/Source/SpritePolygonTest/SpritePolygonTest.cpp index 4b993a760f4c..740e3050bcc8 100644 --- a/tests/cpp-tests/Source/SpritePolygonTest/SpritePolygonTest.cpp +++ b/tests/cpp-tests/Source/SpritePolygonTest/SpritePolygonTest.cpp @@ -44,7 +44,7 @@ SpritePolygonTest::SpritePolygonTest() ADD_TEST_CASE(SpritePolygonTestAutoPolyIsland); ADD_TEST_CASE(SpritePolygonTestFrameAnim); ADD_TEST_CASE(Issue14017Test); - ADD_TEST_CASE(SpritePolygonTestPerformance); + ADD_TEST_CASE(SpritePolygonTestPerformance); } SpritePolygonTestCase::SpritePolygonTestCase() @@ -61,12 +61,12 @@ SpritePolygonTestCase::~SpritePolygonTestCase() void SpritePolygonTestCase::onEnter() { TestCase::onEnter(); - Director::getInstance()->setClearColor(Color4F(102.0f / 255.0f, 184.0f / 255.0f, 204.0f / 255.0f, 1.0f)); + Director::getInstance()->setClearColor(Color(102.0f / 255.0f, 184.0f / 255.0f, 204.0f / 255.0f, 1.0f)); } void SpritePolygonTestCase::onExit() { - Director::getInstance()->setClearColor(Color4F::BLACK); + Director::getInstance()->setClearColor(Color::BLACK); TestCase::onExit(); } @@ -129,17 +129,17 @@ void SpritePolygonTestCase::updateDrawNode() for (ssize_t i = 0; i < count; i++) { // draw 3 lines - Vec3 from = verts[indices[i * 3]].vertices; - Vec3 to = verts[indices[i * 3 + 1]].vertices; - drawnode->drawLine(Vec2(from.x, from.y), Vec2(to.x, to.y), Color4F::BLUE); + Vec3 from = verts[indices[i * 3]].position; + Vec3 to = verts[indices[i * 3 + 1]].position; + drawnode->drawLine(Vec2(from.x, from.y), Vec2(to.x, to.y), Color::BLUE); - from = verts[indices[i * 3 + 1]].vertices; - to = verts[indices[i * 3 + 2]].vertices; - drawnode->drawLine(Vec2(from.x, from.y), Vec2(to.x, to.y), Color4F::GREEN); + from = verts[indices[i * 3 + 1]].position; + to = verts[indices[i * 3 + 2]].position; + drawnode->drawLine(Vec2(from.x, from.y), Vec2(to.x, to.y), Color::GREEN); - from = verts[indices[i * 3 + 2]].vertices; - to = verts[indices[i * 3]].vertices; - drawnode->drawLine(Vec2(from.x, from.y), Vec2(to.x, to.y), Color4F::RED); + from = verts[indices[i * 3 + 2]].position; + to = verts[indices[i * 3]].position; + drawnode->drawLine(Vec2(from.x, from.y), Vec2(to.x, to.y), Color::RED); } } } @@ -241,10 +241,10 @@ void SpritePolygonTest2::initSprites() auto offset = Vec2(0.15 * s.width, 0); auto filename = s_pathGrossini; - // Fix for issue #231 Rect have to be adapt to the 2.0/"ContentScaleFactor()" + // Fix for issue #231 Rect have to be adapt to the 2.0/"ContentScaleFactor()" auto a = 2.0 / Director::getInstance()->getContentScaleFactor(); Rect head = Rect(30 * a, 25 * a, 25 * a, 25 * a); - + // Sprite auto pinfo = AutoPolygon::generatePolygon(filename, head); _polygonSprite = Sprite::create(pinfo); @@ -902,7 +902,7 @@ SpritePolygonTestPerformance::SpritePolygonTestPerformance() _totalSprites = Label::createWithTTF(TTFConfig("fonts/arial.ttf"), "sprites"); _totalSprites->setColor(Color3B::YELLOW); - _totalSprites->enableOutline(Color4B::RED, 2); + _totalSprites->enableOutline(Color32::RED, 2); _totalSprites->setPosition(s.width / 2, s.height / 2); addChild(_totalSprites); diff --git a/tests/cpp-tests/Source/SpriteTest/SpriteTest.cpp b/tests/cpp-tests/Source/SpriteTest/SpriteTest.cpp index 23e3cd361360..168a210ca1c1 100644 --- a/tests/cpp-tests/Source/SpriteTest/SpriteTest.cpp +++ b/tests/cpp-tests/Source/SpriteTest/SpriteTest.cpp @@ -5991,7 +5991,7 @@ void ZwoptexGenericTest::onEnter() SpriteFrameCache::getInstance()->addSpriteFramesWithFile("zwoptex/grossini.plist"); SpriteFrameCache::getInstance()->addSpriteFramesWithFile("zwoptex/grossini-generic.plist"); - auto layer1 = LayerColor::create(Color4B(255, 0, 0, 255), 85, 121); + auto layer1 = LayerColor::create(Color32(255, 0, 0, 255), 85, 121); layer1->setPosition(Vec2(s.width / 2 - 80 - (85.0f * 0.5f), s.height / 2 - (121.0f * 0.5f))); addChild(layer1); @@ -6003,7 +6003,7 @@ void ZwoptexGenericTest::onEnter() sprite1->setFlippedX(false); sprite1->setFlippedY(false); - auto layer2 = LayerColor::create(Color4B(255, 0, 0, 255), 85, 121); + auto layer2 = LayerColor::create(Color32(255, 0, 0, 255), 85, 121); layer2->setPosition(Vec2(s.width / 2 + 80 - (85.0f * 0.5f), s.height / 2 - (121.0f * 0.5f))); addChild(layer2); diff --git a/tests/cpp-tests/Source/Texture2dTest/Texture2dTest.cpp b/tests/cpp-tests/Source/Texture2dTest/Texture2dTest.cpp index f1a9f5044cb2..9f57595f8f06 100644 --- a/tests/cpp-tests/Source/Texture2dTest/Texture2dTest.cpp +++ b/tests/cpp-tests/Source/Texture2dTest/Texture2dTest.cpp @@ -123,7 +123,7 @@ void TextureDemo::onEnter() { TestCase::onEnter(); - auto col = LayerColor::create(Color4B(128, 128, 128, 255)); + auto col = LayerColor::create(Color32(128, 128, 128, 255)); addChild(col, -10); auto textureCache = Director::getInstance()->getTextureCache(); @@ -148,7 +148,7 @@ void TextureASTC::onEnter() TextureDemo::onEnter(); auto& s = getContentSize(); - _background = LayerColor::create(Color4B(15, 19, 42, 255), s.width, s.height); + _background = LayerColor::create(Color32(15, 19, 42, 255), s.width, s.height); _background->setIgnoreAnchorPointForPosition(false); _background->setAnchorPoint(Vec2::ANCHOR_MIDDLE); @@ -197,7 +197,7 @@ bool TextureETC1Alpha::init() return false; auto& canvasSize = getContentSize(); - _background = LayerColor::create(Color4B(15, 19, 42, 255), canvasSize.width, canvasSize.height); + _background = LayerColor::create(Color32(15, 19, 42, 255), canvasSize.width, canvasSize.height); _background->setIgnoreAnchorPointForPosition(false); _background->setAnchorPoint(Vec2::ANCHOR_MIDDLE); @@ -278,7 +278,7 @@ bool TextureETC2::init() return false; auto& canvasSize = getContentSize(); - _background = LayerColor::create(Color4B(15, 19, 42, 255), canvasSize.width, canvasSize.height); + _background = LayerColor::create(Color32(15, 19, 42, 255), canvasSize.width, canvasSize.height); _background->setIgnoreAnchorPointForPosition(false); _background->setAnchorPoint(Vec2::ANCHOR_MIDDLE); @@ -1590,7 +1590,7 @@ void TexturePixelFormat::onEnter() auto s = Director::getInstance()->getWinSize(); - auto background = LayerColor::create(Color4B(128, 128, 128, 255), s.width, s.height); + auto background = LayerColor::create(Color32(128, 128, 128, 255), s.width, s.height); addChild(background, -1); // RGBA 8888 image (32-bit) @@ -2190,7 +2190,7 @@ TexturePVRv3Premult::TexturePVRv3Premult() { auto size = Director::getInstance()->getWinSize(); - auto background = LayerColor::create(Color4B(128, 128, 128, 255), size.width, size.height); + auto background = LayerColor::create(Color32(128, 128, 128, 255), size.width, size.height); addChild(background, -1); // PVR premultiplied @@ -2375,7 +2375,7 @@ void TextureConvertRGB888::onEnter() auto s = Director::getInstance()->getWinSize(); - auto background = LayerColor::create(Color4B(255, 0, 0, 255), s.width, s.height); + auto background = LayerColor::create(Color32(255, 0, 0, 255), s.width, s.height); addChild(background, -1); const char* img = "Images/test_image_rgb888.png"; @@ -2406,7 +2406,7 @@ void TextureConvertRGBA8888::onEnter() auto s = Director::getInstance()->getWinSize(); - auto background = LayerColor::create(Color4B(255, 0, 0, 255), s.width, s.height); + auto background = LayerColor::create(Color32(255, 0, 0, 255), s.width, s.height); addChild(background, -1); const char* img = "Images/test_image_rgba8888.png"; @@ -2437,7 +2437,7 @@ void TextureConvertL8::onEnter() auto s = Director::getInstance()->getWinSize(); - auto background = LayerColor::create(Color4B(255, 0, 0, 255), s.width, s.height); + auto background = LayerColor::create(Color32(255, 0, 0, 255), s.width, s.height); addChild(background, -1); const char* img = "Images/test_image_i8.png"; @@ -2468,7 +2468,7 @@ void TextureConvertLA8::onEnter() auto s = Director::getInstance()->getWinSize(); - auto background = LayerColor::create(Color4B(255, 0, 0, 255), s.width, s.height); + auto background = LayerColor::create(Color32(255, 0, 0, 255), s.width, s.height); addChild(background, -1); const char* img = "Images/test_image_ai88.png"; diff --git a/tests/cpp-tests/Source/TileMapTest/TileMapTest2.cpp b/tests/cpp-tests/Source/TileMapTest/TileMapTest2.cpp index ad84f3221ed8..4ba3581a4d21 100644 --- a/tests/cpp-tests/Source/TileMapTest/TileMapTest2.cpp +++ b/tests/cpp-tests/Source/TileMapTest/TileMapTest2.cpp @@ -221,7 +221,7 @@ TMXOrthoTestNew::TMXOrthoTestNew() // // it should not flicker. No artifacts should appear // - // auto color = LayerColor::create( Color4B(64,64,64,255) ); + // auto color = LayerColor::create( Color32(64,64,64,255) ); // addChild(color, -1); auto map = ax::FastTMXTiledMap::create("TileMaps/orthogonal-test2.tmx"); @@ -493,7 +493,7 @@ std::string TMXReadWriteTestNew::title() const //------------------------------------------------------------------ TMXHexTestNew::TMXHexTestNew() { - auto color = LayerColor::create(Color4B(64, 64, 64, 255)); + auto color = LayerColor::create(Color32(64, 64, 64, 255)); addChild(color, -1); auto map = ax::FastTMXTiledMap::create("TileMaps/hexa-test.tmx"); @@ -515,7 +515,7 @@ std::string TMXHexTestNew::title() const //------------------------------------------------------------------ TMXIsoTestNew::TMXIsoTestNew() { - auto color = LayerColor::create(Color4B(64, 64, 64, 255)); + auto color = LayerColor::create(Color32(64, 64, 64, 255)); addChild(color, -1); auto map = ax::FastTMXTiledMap::create("TileMaps/iso-test.tmx"); @@ -539,7 +539,7 @@ std::string TMXIsoTestNew::title() const //------------------------------------------------------------------ TMXIsoTest1New::TMXIsoTest1New() { - auto color = LayerColor::create(Color4B(64, 64, 64, 255)); + auto color = LayerColor::create(Color32(64, 64, 64, 255)); addChild(color, -1); auto map = ax::FastTMXTiledMap::create("TileMaps/iso-test1.tmx"); @@ -563,7 +563,7 @@ std::string TMXIsoTest1New::title() const //------------------------------------------------------------------ TMXIsoTest2New::TMXIsoTest2New() { - auto color = LayerColor::create(Color4B(64, 64, 64, 255)); + auto color = LayerColor::create(Color32(64, 64, 64, 255)); addChild(color, -1); auto map = ax::FastTMXTiledMap::create("TileMaps/iso-test2.tmx"); @@ -590,7 +590,7 @@ std::string TMXIsoTest2New::title() const //------------------------------------------------------------------ TMXUncompressedTestNew::TMXUncompressedTestNew() { - auto color = LayerColor::create(Color4B(64, 64, 64, 255)); + auto color = LayerColor::create(Color32(64, 64, 64, 255)); addChild(color, -1); auto map = ax::FastTMXTiledMap::create("TileMaps/iso-test2-uncompressed.tmx"); @@ -659,7 +659,7 @@ TMXOrthoObjectsTestNew::TMXOrthoObjectsTestNew() AXLOGD("{}", objectsVal.getDescription()); auto drawNode = DrawNode::create(); - Color4F color(1.0, 1.0, 1.0, 1.0); + Color color(1.0, 1.0, 1.0, 1.0); for (auto&& obj : objects) { ValueMap& dict = obj.asValueMap(); @@ -709,7 +709,7 @@ TMXIsoObjectsTestNew::TMXIsoObjectsTestNew() AXLOGD("{}", objectsVal.getDescription()); auto drawNode = DrawNode::create(); - Color4F color(1.0, 1.0, 1.0, 1.0); + Color color(1.0, 1.0, 1.0, 1.0); for (auto&& obj : objects) { ValueMap& dict = obj.asValueMap(); @@ -1338,7 +1338,7 @@ TMXGIDObjectsTestNew::TMXGIDObjectsTestNew() AXLOGD("----> Iterating over all the group objects"); auto drawNode = DrawNode::create(); - Color4F color(1.0, 1.0, 1.0, 1.0); + Color color(1.0, 1.0, 1.0, 1.0); auto group = map->getObjectGroup("Object Layer 1"); auto objects = group->getObjects(); for (auto&& obj : objects) diff --git a/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UIButtonTest/UIButtonTest.cpp b/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UIButtonTest/UIButtonTest.cpp index cbddeca41276..4ee790a1cb6b 100644 --- a/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UIButtonTest/UIButtonTest.cpp +++ b/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UIButtonTest/UIButtonTest.cpp @@ -804,7 +804,7 @@ bool UIButtonTitleEffectTest::init() button->setPressedActionEnabled(true); Label* title = button->getTitleRenderer(); button->setTitleColor(Color3B::RED); - title->enableShadow(Color4B::BLACK, Size(2, -2)); + title->enableShadow(Color32::BLACK, Size(2, -2)); _uiLayer->addChild(button); @@ -813,7 +813,7 @@ bool UIButtonTitleEffectTest::init() button2->setPositionNormalized(Vec2(0.8f, 0.5f)); button2->setTitleText("PLAY GAME"); auto title2 = button2->getTitleRenderer(); - title2->enableOutline(Color4B::GREEN, 3); + title2->enableOutline(Color32::GREEN, 3); _uiLayer->addChild(button2); return true; diff --git a/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UIListViewTest/UIListViewTest.cpp b/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UIListViewTest/UIListViewTest.cpp index 727e2777f84b..19f28601fdd3 100644 --- a/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UIListViewTest/UIListViewTest.cpp +++ b/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UIListViewTest/UIListViewTest.cpp @@ -680,12 +680,12 @@ bool UIListViewTest_ScrollToItem::init() if (getListViewDirection() == ScrollView::Direction::HORIZONTAL) { float halfY = 110; - pNode->drawLine(Vec2(center.x, center.y - halfY), Vec2(center.x, center.y + halfY), Color4F(0, 0, 0, 1), 2.0f); + pNode->drawLine(Vec2(center.x, center.y - halfY), Vec2(center.x, center.y + halfY), Color(0, 0, 0, 1), 2.0f); } else { float halfX = 150; - pNode->drawLine(Vec2(center.x - halfX, center.y), Vec2(center.x + halfX, center.y), Color4F(0, 0, 0, 1), 2.0f); + pNode->drawLine(Vec2(center.x - halfX, center.y), Vec2(center.x + halfX, center.y), Color(0, 0, 0, 1), 2.0f); } _uiLayer->addChild(pNode); } @@ -751,12 +751,12 @@ bool UIListViewTest_Magnetic::init() if (getListViewDirection() == ScrollView::Direction::HORIZONTAL) { float halfY = 110; - pNode->drawLine(Vec2(center.x, center.y - halfY), Vec2(center.x, center.y + halfY), Color4F(0, 0, 0, 1), 2.0f); + pNode->drawLine(Vec2(center.x, center.y - halfY), Vec2(center.x, center.y + halfY), Color(0, 0, 0, 1), 2.0f); } else { float halfX = 150; - pNode->drawLine(Vec2(center.x - halfX, center.y), Vec2(center.x + halfX, center.y), Color4F(0, 0, 0, 1), 2.0f); + pNode->drawLine(Vec2(center.x - halfX, center.y), Vec2(center.x + halfX, center.y), Color(0, 0, 0, 1), 2.0f); } _uiLayer->addChild(pNode); } @@ -908,12 +908,12 @@ bool UIListViewTest_Padding::init() if (getListViewDirection() == ScrollView::Direction::HORIZONTAL) { float halfY = 110; - pNode->drawLine(Vec2(center.x, center.y - halfY), Vec2(center.x, center.y + halfY), Color4F(0, 0, 0, 1), 2.0f); + pNode->drawLine(Vec2(center.x, center.y - halfY), Vec2(center.x, center.y + halfY), Color(0, 0, 0, 1), 2.0f); } else { float halfX = 150; - pNode->drawLine(Vec2(center.x - halfX, center.y), Vec2(center.x + halfX, center.y), Color4F(0, 0, 0, 1), 2.0f); + pNode->drawLine(Vec2(center.x - halfX, center.y), Vec2(center.x + halfX, center.y), Color(0, 0, 0, 1), 2.0f); } _uiLayer->addChild(pNode); } diff --git a/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UIPageViewTest/UIPageViewTest.cpp b/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UIPageViewTest/UIPageViewTest.cpp index 325727f131a9..787a46c820d2 100644 --- a/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UIPageViewTest/UIPageViewTest.cpp +++ b/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UIPageViewTest/UIPageViewTest.cpp @@ -334,13 +334,13 @@ bool UIPageViewTouchPropagationTest::init() Text* propagationText = Text::create("Allow Propagation", "Arial", 10); propagationText->setAnchorPoint(Vec2(0.0f, 0.5f)); - propagationText->setTextColor(Color4B::RED); + propagationText->setTextColor(Color32::RED); propagationText->setPosition(Vec2(0.0f, pageView->getPosition().y + 50)); _uiLayer->addChild(propagationText); Text* swallowTouchText = Text::create("Swallow Touches", "Arial", 10); swallowTouchText->setAnchorPoint(Vec2(0.f, 0.5f)); - swallowTouchText->setTextColor(Color4B::RED); + swallowTouchText->setTextColor(Color32::RED); swallowTouchText->setPosition(Vec2(0.0f, pageView->getPosition().y)); _uiLayer->addChild(swallowTouchText); diff --git a/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UIRadioButtonTest/UIRadioButtonTest.cpp b/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UIRadioButtonTest/UIRadioButtonTest.cpp index 5d177354087c..68906e11bd76 100644 --- a/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UIRadioButtonTest/UIRadioButtonTest.cpp +++ b/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UIRadioButtonTest/UIRadioButtonTest.cpp @@ -321,7 +321,7 @@ bool UIRadioButtonTabTest::init() const float buttonWidth = 350 * BUTTON_SCALE / Director::getInstance()->getContentScaleFactor(); // Background for buttons - LayerColor* colorLayer = LayerColor::create(Color4B::WHITE); + LayerColor* colorLayer = LayerColor::create(Color32::WHITE); colorLayer->setIgnoreAnchorPointForPosition(false); colorLayer->setAnchorPoint(Vec2::ANCHOR_MIDDLE); colorLayer->setContentSize(Size(buttonWidth * 3, 170 / Director::getInstance()->getContentScaleFactor())); diff --git a/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UIScrollViewTest/UIScrollViewTest.cpp b/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UIScrollViewTest/UIScrollViewTest.cpp index 9bf1b2502cf0..8bb673106a58 100644 --- a/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UIScrollViewTest/UIScrollViewTest.cpp +++ b/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UIScrollViewTest/UIScrollViewTest.cpp @@ -740,7 +740,7 @@ bool UIScrollViewTestEvents::init() // Jump to right bottom scrollView->jumpToBottomRight(); - auto getRandomColor = [] { return Color4B(random(0, 255), random(0, 255), random(0, 255), 255); }; + auto getRandomColor = [] { return Color32(random(0, 255), random(0, 255), random(0, 255), 255); }; scrollView->addEventListener([&](Object*, ui::ScrollView::EventType e) { switch (e) { diff --git a/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UITextAtlasTest/UITextAtlasTest.cpp b/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UITextAtlasTest/UITextAtlasTest.cpp index dbace24358dc..237974873850 100644 --- a/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UITextAtlasTest/UITextAtlasTest.cpp +++ b/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UITextAtlasTest/UITextAtlasTest.cpp @@ -107,7 +107,7 @@ bool UITextAtlasETC1ShadowTest::init() textAtlas->setPosition(Vec2((widgetSize.width) / 2, widgetSize.height / 2.0f)); _uiLayer->addChild(textAtlas); auto labelAtlas = (Label*)textAtlas->getVirtualRenderer(); - labelAtlas->enableShadow(Color4B::GREEN); + labelAtlas->enableShadow(Color32::GREEN); _textAtlas = textAtlas; TTFConfig ttfConfig("fonts/arial.ttf", 15); diff --git a/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UITextFieldTest/UITextFieldTest.cpp b/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UITextFieldTest/UITextFieldTest.cpp index 684812555fb0..14d089d0705c 100644 --- a/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UITextFieldTest/UITextFieldTest.cpp +++ b/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UITextFieldTest/UITextFieldTest.cpp @@ -523,8 +523,8 @@ bool UITextFieldTest_PlaceHolderColor::init() // Create the textfield TextField* textField = TextField::create("input words here", "Arial", 30); textField->setPlaceHolder("input text here"); - textField->setPlaceHolderColor(Color4B::GREEN); - textField->setTextColor(Color4B::RED); + textField->setPlaceHolderColor(Color32::GREEN); + textField->setTextColor(Color32::RED); textField->setPosition(Vec2(widgetSize.width / 2.0f, widgetSize.height / 2.0f)); textField->addEventListener(AX_CALLBACK_2(UITextFieldTest_PlaceHolderColor::textFieldEvent, this)); _uiLayer->addChild(textField); diff --git a/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UITextTest/UITextTest.cpp b/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UITextTest/UITextTest.cpp index 93bf6637afc6..ea86dbcfdbb0 100644 --- a/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UITextTest/UITextTest.cpp +++ b/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UITextTest/UITextTest.cpp @@ -139,7 +139,7 @@ bool UILabelTest_Effect::init() Text* glow_label = Text::create(); glow_label->setFontName("fonts/Marker Felt.ttf"); glow_label->setString("Glow"); - glow_label->enableGlow(Color4B::RED); + glow_label->enableGlow(Color32::RED); glow_label->setPosition(Vec2(widgetSize.width / 2.0f, widgetSize.height / 2.0f - 20)); @@ -150,7 +150,7 @@ bool UILabelTest_Effect::init() outline_label->setString("Outline"); AXLOGD("content size without outline: {} {}", outline_label->getContentSize().width, outline_label->getContentSize().height); - outline_label->enableOutline(Color4B::GREEN, 4); + outline_label->enableOutline(Color32::GREEN, 4); outline_label->setPosition( Vec2(widgetSize.width / 2.0f, widgetSize.height / 2.0f - shadow_label->getContentSize().height - 50)); @@ -268,8 +268,8 @@ bool UITextTest_Clone::init() singleText->setString("CHUKONG"); singleText->setTouchScaleChangeEnabled(true); singleText->setTouchEnabled(true); - singleText->enableOutline(Color4B(255, 0, 0, 100), 10); - singleText->enableShadow(Color4B::YELLOW, Size(2, -2), 0); + singleText->enableOutline(Color32(255, 0, 0, 100), 10); + singleText->enableShadow(Color32::YELLOW, Size(2, -2), 0); _uiLayer->addChild(singleText); auto cloneText = singleText->clone(); diff --git a/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UIVideoPlayerTest/UIVideoPlayerTest.cpp b/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UIVideoPlayerTest/UIVideoPlayerTest.cpp index 8a4c4df8230b..5a331c7a5fdc 100644 --- a/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UIVideoPlayerTest/UIVideoPlayerTest.cpp +++ b/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UIVideoPlayerTest/UIVideoPlayerTest.cpp @@ -104,14 +104,14 @@ bool VideoPlayerTest::init() _videoStateLabel->setPosition( Vec2(_visibleRect.origin.x + _visibleRect.size.width - 10, _visibleRect.origin.y + 200)); _uiLayer->addChild(_videoStateLabel, 1); - _videoStateLabel->setTextColor(Color4B::YELLOW); + _videoStateLabel->setTextColor(Color32::YELLOW); _loopStatusLabel = Label::createWithSystemFont("(1)", "Arial", 10); _loopStatusLabel->setAnchorPoint(Vec2::ANCHOR_MIDDLE_RIGHT); _loopStatusLabel->setPosition( Vec2(_visibleRect.origin.x + _visibleRect.size.width - 10, _visibleRect.origin.y + 185)); _uiLayer->addChild(_loopStatusLabel, 1); - _loopStatusLabel->setTextColor(Color4B::YELLOW); + _loopStatusLabel->setTextColor(Color32::YELLOW); return true; } diff --git a/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UIWebViewTest/UIWebViewTest.cpp b/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UIWebViewTest/UIWebViewTest.cpp index f2f77009f859..9f929d950eeb 100644 --- a/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UIWebViewTest/UIWebViewTest.cpp +++ b/tests/cpp-tests/Source/UITest/CocoStudioGUITest/UIWebViewTest/UIWebViewTest.cpp @@ -62,7 +62,7 @@ bool WebViewTest::init() this->addChild(urlTextField); Text* httpLabel = Text::create("https:// ", "Arial", 20); - httpLabel->setTextColor(Color4B::GREEN); + httpLabel->setTextColor(Color32::GREEN); httpLabel->setAnchorPoint(Vec2(1.0, 0.5)); httpLabel->setPosition(urlTextField->getPosition() - Vec2(urlTextField->getContentSize().width / 2, 0)); this->addChild(httpLabel); diff --git a/tests/cpp-tests/Source/controller.cpp b/tests/cpp-tests/Source/controller.cpp index 3c944fe04cb0..cfbee33eca75 100644 --- a/tests/cpp-tests/Source/controller.cpp +++ b/tests/cpp-tests/Source/controller.cpp @@ -60,10 +60,6 @@ class RootTests : public TestList addTest("Box2D - Basic", []() { return new Box2DTests(); }); #if defined(AX_PLATFORM_PC) || defined(__EMSCRIPTEN__) addTest("Box2D - TestBed", []() { return new Box2DTestBedTests(); }); -#endif - addTest("Chipmunk2D - Basic", []() { return new ChipmunkTests(); }); -#if defined(AX_PLATFORM_PC) || defined(__EMSCRIPTEN__) - addTest("Chipmunk2D - TestBed", []() { return new ChipmunkTestBedTests(); }); #endif addTest("Bugs", []() { return new BugsTests(); }); addTest("Click and Move", []() { return new ClickAndMoveTest(); }); @@ -98,8 +94,8 @@ class RootTests : public TestList addTest("Node: Parallax", []() { return new ParallaxTests(); }); addTest("Node: Particles", []() { return new ParticleTests(); }); addTest("Node: Particle3D (PU)", []() { return new Particle3DTests(); }); -#if defined(AX_ENABLE_PHYSICS) - addTest("Node: Physics", []() { return new PhysicsTests(); }); +#if defined(AX_ENABLE_PHYSICS) && 0 + addTest("Node: Physics", []() { return new PhysicsTests(); }); #endif addTest("Node: Physics3D", []() { return new Physics3DTests(); }); addTest("Node: RenderTexture", []() { return new RenderTextureTests(); }); diff --git a/tests/cpp-tests/Source/shaders/circle.fs b/tests/cpp-tests/Source/shaders/circle.fs new file mode 100644 index 000000000000..a1e7e6091efb --- /dev/null +++ b/tests/cpp-tests/Source/shaders/circle.fs @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2024 Erin Catto +// SPDX-License-Identifier: MIT + +#version 420 + +layout(location = 0) in vec2 v_position; +layout(location = 1) in vec4 v_color; +layout(location = 2) in float v_thickness; + +layout(location = SV_Target0) out vec4 fragColor; + +void main() +{ + // radius in unit quad + float radius = 1.0; + + // distance to circle + vec2 w = v_position; + float dw = length(w); + float d = abs(dw - radius); + + fragColor = vec4(v_color.rgb, smoothstep(v_thickness, 0.0, d)); +} diff --git a/tests/cpp-tests/Source/shaders/circle.vs b/tests/cpp-tests/Source/shaders/circle.vs new file mode 100644 index 000000000000..361c07cdcddf --- /dev/null +++ b/tests/cpp-tests/Source/shaders/circle.vs @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: 2024 Erin Catto +// SPDX-License-Identifier: MIT + +#version 420 + +layout(location = 0) in vec2 a_localPosition; +layout(location = 1) in vec2 a_instancePosition; +layout(location = 2) in float a_instanceRadius; +layout(location = 3) in vec4 a_instanceColor; + +layout(location = 0) out vec2 v_position; +layout(location = 1) out vec4 v_color; +layout(location = 2) out float v_thickness; + +layout(std140) uniform vs_ub { + float u_pixelScale; + mat4 u_MVPMatrix; +}; + +void main() +{ + v_position = a_localPosition; + v_color = a_instanceColor; + float radius = a_instanceRadius; + + // resolution.y = pixelScale * radius + v_thickness = 3.0f / (u_pixelScale * radius); + + vec2 p = vec2(radius * a_localPosition.x, radius * a_localPosition.y) + a_instancePosition; + gl_Position = u_MVPMatrix * vec4(p, 0.0f, 1.0f); +} diff --git a/tests/cpp-tests/Source/shaders/solid_capsule.fs b/tests/cpp-tests/Source/shaders/solid_capsule.fs new file mode 100644 index 000000000000..8536db9f44c5 --- /dev/null +++ b/tests/cpp-tests/Source/shaders/solid_capsule.fs @@ -0,0 +1,61 @@ +// SPDX-FileCopyrightText: 2024 Erin Catto +// SPDX-License-Identifier: MIT + +#version 420 + +layout(location=0) in vec2 v_position; +layout(location=1) in vec4 v_color; +layout(location=2) in float v_length; +layout(location=3) in float v_thickness; + +layout(location = SV_Target0) out vec4 fragColor; + +// Thanks to baz and kolyan3040 for help on this shader +// todo this can be optimized a bit, keeping some terms for clarity + +// https://en.wikipedia.org/wiki/Alpha_compositing +vec4 blend_colors(vec4 front,vec4 back) +{ + vec3 cSrc = front.rgb; + float alphaSrc = front.a; + vec3 cDst = back.rgb; + float alphaDst = back.a; + + vec3 cOut = cSrc * alphaSrc + cDst * alphaDst * (1.0 - alphaSrc); + float alphaOut = alphaSrc + alphaDst * (1.0 - alphaSrc); + + // remove alpha from rgb + cOut = cOut / alphaOut; + + return vec4(cOut, alphaOut); +} + +void main() +{ + // radius in unit quad + float radius = 0.5 * (2.0 - v_length); + + vec4 borderColor = v_color; + vec4 fillColor = 0.6f * borderColor; + + vec2 v1 = vec2(-0.5 * v_length, 0); + vec2 v2 = vec2(0.5 * v_length, 0); + + // distance to line segment + vec2 e = v2 - v1; + vec2 w = v_position - v1; + float we = dot(w, e); + vec2 b = w - e * clamp(we / dot(e, e), 0.0, 1.0); + float dw = length(b); + + // SDF union of capsule and line segment + float d = min(dw, abs(dw - radius)); + + // roll the fill alpha down at the border + vec4 back = vec4(fillColor.rgb, fillColor.a * smoothstep(radius + v_thickness, radius, dw)); + + // roll the border alpha down from 1 to 0 across the border thickness + vec4 front = vec4(borderColor.rgb, smoothstep(v_thickness, 0.0f, d)); + + fragColor = blend_colors(front, back); +} diff --git a/tests/cpp-tests/Source/shaders/solid_capsule.vs b/tests/cpp-tests/Source/shaders/solid_capsule.vs new file mode 100644 index 000000000000..94b7a6e228f4 --- /dev/null +++ b/tests/cpp-tests/Source/shaders/solid_capsule.vs @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: 2024 Erin Catto +// SPDX-License-Identifier: MIT + +#version 420 + +layout(location=0) in vec2 a_localPosition; +layout(location=1) in vec4 a_instanceTransform; +layout(location=2) in float a_instanceRadius; +layout(location=3) in float a_instanceLength; +layout(location=4) in vec4 a_instanceColor; + +layout(location=0) out vec2 v_position; +layout(location=1) out vec4 v_color; +layout(location=2) out float v_length; +layout(location=3) out float v_thickness; + +layout(std140) uniform vs_ub { + float u_pixelScale; + mat4 u_MVPMatrix; +}; + +void main() +{ + v_position = a_localPosition; + v_color = a_instanceColor; + + float radius = a_instanceRadius; + float length = a_instanceLength; + + // scale quad large enough to hold capsule + float scale = radius + 0.5 * length; + + // quad range of [-1, 1] implies normalize radius and length + v_length = length / scale; + + // resolution.y = pixelScale * scale + v_thickness = 3.0f / (u_pixelScale * scale); + + float x = a_instanceTransform.x; + float y = a_instanceTransform.y; + float c = a_instanceTransform.z; + float s = a_instanceTransform.w; + vec2 p = vec2(scale * a_localPosition.x, scale * a_localPosition.y); + p = vec2((c * p.x - s * p.y) + x, (s * p.x + c * p.y) + y); + gl_Position = u_MVPMatrix * vec4(p, 0.0, 1.0); +} diff --git a/tests/cpp-tests/Source/shaders/solid_circle.fs b/tests/cpp-tests/Source/shaders/solid_circle.fs new file mode 100644 index 000000000000..e9f5d47e3a10 --- /dev/null +++ b/tests/cpp-tests/Source/shaders/solid_circle.fs @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: 2024 Erin Catto +// SPDX-License-Identifier: MIT + +#version 420 + +layout(location = 0) in vec2 v_position; +layout(location = 1) in vec4 v_color; +layout(location = 2) in float v_thickness; + +layout(location = SV_Target0) out vec4 fragColor; + +// https://en.wikipedia.org/wiki/Alpha_compositing +vec4 blend_colors(vec4 front, vec4 back) +{ + vec3 cSrc = front.rgb; + float alphaSrc = front.a; + vec3 cDst = back.rgb; + float alphaDst = back.a; + + vec3 cOut = cSrc * alphaSrc + cDst * alphaDst * (1.0 - alphaSrc); + float alphaOut = alphaSrc + alphaDst * (1.0 - alphaSrc); + cOut = cOut / alphaOut; + + return vec4(cOut, alphaOut); +} + +void main() +{ + // radius in unit quad + float radius = 1.0; + + // distance to axis line segment + vec2 e = vec2(radius, 0); + vec2 w = v_position; + float we = dot(w, e); + vec2 b = w - e * clamp(we / dot(e, e), 0.0, 1.0); + float da = length(b); + + // distance to circle + float dw = length(w); + float dc = abs(dw - radius); + + // union of circle and axis + float d = min(da, dc); + + vec4 borderColor = v_color; + vec4 fillColor = 0.6f * borderColor; + + // roll the fill alpha down at the border + vec4 back = vec4(fillColor.rgb, fillColor.a * smoothstep(radius + v_thickness, radius, dw)); + + // roll the border alpha down from 1 to 0 across the border thickness + vec4 front = vec4(borderColor.rgb, smoothstep(v_thickness, 0.0f, d)); + + fragColor = blend_colors(front, back); +} diff --git a/tests/cpp-tests/Source/shaders/solid_circle.vs b/tests/cpp-tests/Source/shaders/solid_circle.vs new file mode 100644 index 000000000000..c4061d56b48f --- /dev/null +++ b/tests/cpp-tests/Source/shaders/solid_circle.vs @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: 2024 Erin Catto +// SPDX-License-Identifier: MIT + +#version 420 + +layout(location = 0) in vec2 a_localPosition; +layout(location = 1) in vec4 a_instanceTransform; +layout(location = 2) in float a_instanceRadius; +layout(location = 3) in vec4 a_instanceColor; + +layout(location = 0) out vec2 v_position; +layout(location = 1) out vec4 v_color; +layout(location = 2) out float v_thickness; + +layout(std140) uniform vs_ub { + float u_pixelScale; + mat4 u_MVPMatrix; +}; + +void main() +{ + v_position = a_localPosition; + v_color = a_instanceColor; + float radius = a_instanceRadius; + + // resolution.y = pixelScale * radius + v_thickness = 3.0f / (u_pixelScale * radius); + + float x = a_instanceTransform.x; + float y = a_instanceTransform.y; + float c = a_instanceTransform.z; + float s = a_instanceTransform.w; + vec2 p = vec2(radius * a_localPosition.x, radius * a_localPosition.y); + p = vec2((c * p.x - s * p.y) + x, (s * p.x + c * p.y) + y); + gl_Position = u_MVPMatrix * vec4(p, 0.0f, 1.0f); +} diff --git a/tests/cpp-tests/Source/tests.h b/tests/cpp-tests/Source/tests.h index 38542c183b4d..b3cfc466656c 100644 --- a/tests/cpp-tests/Source/tests.h +++ b/tests/cpp-tests/Source/tests.h @@ -27,11 +27,8 @@ #define _TESTS_H_ #include "Box2DTest/Box2dTest.h" -#include "Box2DTestBed/Box2DTestBed.h" - -#include "ChipmunkTest/ChipmunkTest.h" #if defined(AX_PLATFORM_PC) || defined(__EMSCRIPTEN__) -# include "ChipmunkTestBed/ChipmunkTestBed.h" +# include "Box2DTestBed/Box2DTestBed.h" #endif #if (AX_TARGET_PLATFORM != AX_PLATFORM_MARMALADE) @@ -55,7 +52,7 @@ #endif #if defined(AX_ENABLE_EXT_EFFEKSEER) -#include "EffekseerTest/EffekseerTest.h" +# include "EffekseerTest/EffekseerTest.h" #endif // sort them alphabetically. thanks diff --git a/tests/cpp-tests/proj.android/app/AndroidManifest.xml b/tests/cpp-tests/proj.android/app/AndroidManifest.xml index 344e07e6aee1..c91813d171d7 100644 --- a/tests/cpp-tests/proj.android/app/AndroidManifest.xml +++ b/tests/cpp-tests/proj.android/app/AndroidManifest.xml @@ -21,7 +21,7 @@ android:screenOrientation="sensorLandscape" android:configChanges="orientation|keyboardHidden|screenSize" android:label="@string/app_name" - android:theme="@android:style/Theme.NoTitleBar.Fullscreen" + android:theme="@style/Theme.AppCompat.Light.NoActionBar" android:launchMode="singleTask" android:taskAffinity="" android:exported="true" > diff --git a/tests/cpp-tests/proj.android/app/build.gradle b/tests/cpp-tests/proj.android/app/build.gradle index 0a48325fe480..c8c3ebc72aea 100644 --- a/tests/cpp-tests/proj.android/app/build.gradle +++ b/tests/cpp-tests/proj.android/app/build.gradle @@ -1,9 +1,10 @@ apply plugin: 'com.android.application' -apply from: project(':libaxmol').projectDir.toString() + "/axutils.gradle" +apply from: project(':libaxmol').projectDir.toString() + "/axmol.gradle" -android { - // Resolve build profiles - def buildProfiles = AxmolUtils.resolveBuildProfiles(project) +// Resolve build profiles +def buildProfiles = AxmolUtils.resolveBuildProfiles(project) + +android { def packageName = buildProfiles['packageName'] def cmakeVer = buildProfiles['cmakeVer'] def cmakeOptions = Eval.me(buildProfiles['cmakeOptions']) @@ -37,6 +38,8 @@ android { ndk { abiFilters = __1K_ARCHS.split(':').collect{it as String} } + + multiDexEnabled true } sourceSets.main { @@ -112,6 +115,9 @@ android.applicationVariants.configureEach { variant -> dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation project(':libaxmol') + + def appcompat_ver = buildProfiles['appcompat'] + implementation "androidx.appcompat:appcompat:$appcompat_ver" } project.afterEvaluate { diff --git a/tests/cpp-tests/proj.android/gradle.properties b/tests/cpp-tests/proj.android/gradle.properties index 3121b8ced270..e4bc1182c7a2 100644 --- a/tests/cpp-tests/proj.android/gradle.properties +++ b/tests/cpp-tests/proj.android/gradle.properties @@ -30,4 +30,4 @@ __1K_ARCHS=arm64-v8a android.injected.testOnly=false android.useAndroidX=true - +android.enableJetifier=true diff --git a/tests/fairygui-tests/Source/AppDelegate.cpp b/tests/fairygui-tests/Source/AppDelegate.cpp index 1a1d4338487e..4fc59881e62c 100644 --- a/tests/fairygui-tests/Source/AppDelegate.cpp +++ b/tests/fairygui-tests/Source/AppDelegate.cpp @@ -74,7 +74,7 @@ bool AppDelegate::applicationDidFinishLaunching() { director->setContentScaleFactor(MIN(smallResolutionSize.height / designResolutionSize.height, smallResolutionSize.width / designResolutionSize.width)); }*/ - director->setClearColor(Color4F(Color4B(0x36, 0x3B, 0x44, 0xFF))); + director->setClearColor(Color(Color32(0x36, 0x3B, 0x44, 0xFF))); register_all_packages(); diff --git a/tests/fairygui-tests/Source/BasicsScene.cpp b/tests/fairygui-tests/Source/BasicsScene.cpp index 3c31c4d74584..21590b5a79d3 100644 --- a/tests/fairygui-tests/Source/BasicsScene.cpp +++ b/tests/fairygui-tests/Source/BasicsScene.cpp @@ -186,7 +186,7 @@ void BasicsScene::playDepth() startPos.x += 10; startPos.y += 10; graph->setPosition(startPos.x, startPos.y); - graph->drawRect(150, 150, 1, Color4F::BLACK, Color4F::RED); + graph->drawRect(150, 150, 1, Color::BLACK, Color::RED); obj->getChild("n22")->as()->addChild(graph); }, EventTag(this)); //avoid duplicate register @@ -196,7 +196,7 @@ void BasicsScene::playDepth() startPos.x += 10; startPos.y += 10; graph->setPosition(startPos.x, startPos.y); - graph->drawRect(150, 150, 1, Color4F::BLACK, Color4F::GREEN); + graph->drawRect(150, 150, 1, Color::BLACK, Color::GREEN); graph->setSortingOrder(200); obj->getChild("n22")->as()->addChild(graph); }, diff --git a/tests/fairygui-tests/proj.android/app/AndroidManifest.xml b/tests/fairygui-tests/proj.android/app/AndroidManifest.xml index 5ac9b73bd8c5..67ce4bfb54b6 100644 --- a/tests/fairygui-tests/proj.android/app/AndroidManifest.xml +++ b/tests/fairygui-tests/proj.android/app/AndroidManifest.xml @@ -3,23 +3,23 @@ android:installLocation="auto"> - + - + - + diff --git a/tests/fairygui-tests/proj.android/app/build.gradle b/tests/fairygui-tests/proj.android/app/build.gradle index 3d76ebafd634..461442ac7db7 100644 --- a/tests/fairygui-tests/proj.android/app/build.gradle +++ b/tests/fairygui-tests/proj.android/app/build.gradle @@ -1,9 +1,10 @@ apply plugin: 'com.android.application' -apply from: project(':libaxmol').projectDir.toString() + "/axutils.gradle" +apply from: project(':libaxmol').projectDir.toString() + "/axmol.gradle" -android { - // Resolve build profiles - def buildProfiles = AxmolUtils.resolveBuildProfiles(project) +// Resolve build profiles +def buildProfiles = AxmolUtils.resolveBuildProfiles(project) + +android { def packageName = buildProfiles['packageName'] def cmakeVer = buildProfiles['cmakeVer'] def cmakeOptions = Eval.me(buildProfiles['cmakeOptions']) @@ -112,6 +113,9 @@ android.applicationVariants.configureEach { variant -> dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation project(':libaxmol') + + def appcompat_ver = buildProfiles['appcompat'] + implementation "androidx.appcompat:appcompat:$appcompat_ver" } project.afterEvaluate { diff --git a/tests/live2d-tests/Source/LAppView.cpp b/tests/live2d-tests/Source/LAppView.cpp index d82aa0736b8f..c60f110f667c 100644 --- a/tests/live2d-tests/Source/LAppView.cpp +++ b/tests/live2d-tests/Source/LAppView.cpp @@ -218,8 +218,8 @@ void LAppView::setDebugRectsNode(DrawNode* debugRects) void LAppView::drawDebugRects(LAppLive2DManager* manager) const { - const Color4F hitAreaColor = Color4F(1.0f, 0, 0, 0.2f); - const Color4F userDataAreaColor = Color4F(0, 0, 1.0f, 0.2f); + const Color hitAreaColor = Color(1.0f, 0, 0, 0.2f); + const Color userDataAreaColor = Color(0, 0, 1.0f, 0.2f); CubismMatrix44 projection; const Size window = Director::getInstance()->getWinSize(); diff --git a/tests/live2d-tests/proj.android/app/AndroidManifest.xml b/tests/live2d-tests/proj.android/app/AndroidManifest.xml index 595e17e91d09..7c46ebb8bb2e 100644 --- a/tests/live2d-tests/proj.android/app/AndroidManifest.xml +++ b/tests/live2d-tests/proj.android/app/AndroidManifest.xml @@ -3,14 +3,14 @@ android:installLocation="auto"> - + - + - + @@ -20,7 +20,7 @@ android:screenOrientation="sensorLandscape" android:configChanges="orientation|keyboardHidden|screenSize" android:label="@string/app_name" - android:theme="@android:style/Theme.NoTitleBar.Fullscreen" + android:theme="@style/Theme.AppCompat.Light.NoActionBar" android:launchMode="singleTask" android:taskAffinity="" android:exported="true" > diff --git a/tests/live2d-tests/proj.android/app/build.gradle b/tests/live2d-tests/proj.android/app/build.gradle index 119576238702..3655290ae026 100644 --- a/tests/live2d-tests/proj.android/app/build.gradle +++ b/tests/live2d-tests/proj.android/app/build.gradle @@ -1,9 +1,10 @@ apply plugin: 'com.android.application' -apply from: project(':libaxmol').projectDir.toString() + "/axutils.gradle" +apply from: project(':libaxmol').projectDir.toString() + "/axmol.gradle" -android { - // Resolve build profiles - def buildProfiles = AxmolUtils.resolveBuildProfiles(project) +// Resolve build profiles +def buildProfiles = AxmolUtils.resolveBuildProfiles(project) + +android { def packageName = buildProfiles['packageName'] def cmakeVer = buildProfiles['cmakeVer'] def cmakeOptions = Eval.me(buildProfiles['cmakeOptions']) @@ -112,6 +113,9 @@ android.applicationVariants.configureEach { variant -> dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation project(':libaxmol') + + def appcompat_ver = buildProfiles['appcompat'] + implementation "androidx.appcompat:appcompat:$appcompat_ver" } project.afterEvaluate { diff --git a/tests/lua-tests/Source/lua_test_bindings.cpp b/tests/lua-tests/Source/lua_test_bindings.cpp index 6f3d1c022ee5..25404a61925d 100644 --- a/tests/lua-tests/Source/lua_test_bindings.cpp +++ b/tests/lua-tests/Source/lua_test_bindings.cpp @@ -41,7 +41,7 @@ class DrawNode3D : public Node /** * Draw 3D Line */ - void drawLine(const Vec3& from, const Vec3& to, const Color4F& color); + void drawLine(const Vec3& from, const Vec3& to, const Color& color); /** * Draw 3D cube @@ -56,7 +56,7 @@ class DrawNode3D : public Node * vertices[7]:Left-top-back. * @param color */ - void drawCube(Vec3* vertices, const Color4F& color); + void drawCube(Vec3* vertices, const Color& color); /** Clear the geometry in the node's buffer. */ void clear(); @@ -87,7 +87,7 @@ class DrawNode3D : public Node struct V3F_C4B { Vec3 vertices; - Color4B colors; + Color32 colors; }; void ensureCapacity(int count); @@ -223,12 +223,12 @@ void DrawNode3D::draw(Renderer* renderer, const Mat4& transform, uint32_t flags) } } -void DrawNode3D::drawLine(const Vec3& from, const Vec3& to, const Color4F& color) +void DrawNode3D::drawLine(const Vec3& from, const Vec3& to, const Color& color) { unsigned int vertex_count = 2; ensureCapacity(vertex_count); - Color4B col = Color4B(color); + Color32 col = Color32(color); V3F_C4B a = {Vec3(from.x, from.y, from.z), col}; V3F_C4B b = { Vec3(to.x, to.y, to.z), @@ -239,7 +239,7 @@ void DrawNode3D::drawLine(const Vec3& from, const Vec3& to, const Color4F& color _dirty = true; } -void DrawNode3D::drawCube(Vec3* vertices, const Color4F& color) +void DrawNode3D::drawCube(Vec3* vertices, const Color& color) { // front face drawLine(vertices[0], vertices[1], color); @@ -459,13 +459,13 @@ int lua_cocos2dx_DrawNode3D_drawLine(lua_State* L) { ax::Vec3 arg0; ax::Vec3 arg1; - ax::Color4F arg2; + ax::Color arg2; ok &= luaval_to_vec3(L, 2, &arg0, "ax.DrawNode3D:drawLine"); ok &= luaval_to_vec3(L, 3, &arg1, "ax.DrawNode3D:drawLine"); - ok &= luaval_to_color4f(L, 4, &arg2, "ax.DrawNode3D:drawLine"); + ok &= luaval_to_color(L, 4, &arg2, "ax.DrawNode3D:drawLine"); if (!ok) return 0; cobj->drawLine(arg0, arg1, arg2); @@ -555,7 +555,7 @@ int lua_cocos2dx_DrawNode3D_drawCube(lua_State* L) if (argc == 2) { std::vector arg0; - ax::Color4F arg1; + ax::Color arg1; Vec3 vec3; #if _AX_DEBUG >= 1 if (!tolua_istable(L, 2, 0, &tolua_err)) @@ -587,7 +587,7 @@ int lua_cocos2dx_DrawNode3D_drawCube(lua_State* L) lua_pop(L, 1); } - ok &= luaval_to_color4f(L, 3, &arg1, "ax.DrawNode3D:drawCube"); + ok &= luaval_to_color(L, 3, &arg1, "ax.DrawNode3D:drawCube"); if (!ok) return 0; cobj->drawCube(&arg0[0], arg1); diff --git a/tests/lua-tests/proj.android/app/AndroidManifest.xml b/tests/lua-tests/proj.android/app/AndroidManifest.xml index c5bdf9a6871a..db59987ecf51 100644 --- a/tests/lua-tests/proj.android/app/AndroidManifest.xml +++ b/tests/lua-tests/proj.android/app/AndroidManifest.xml @@ -5,23 +5,23 @@ - + - + - + diff --git a/tests/lua-tests/proj.android/app/build.gradle b/tests/lua-tests/proj.android/app/build.gradle index 36147e046a99..e4942c68c6b7 100644 --- a/tests/lua-tests/proj.android/app/build.gradle +++ b/tests/lua-tests/proj.android/app/build.gradle @@ -1,9 +1,10 @@ apply plugin: 'com.android.application' -apply from: project(':libaxmol').projectDir.toString() + "/axutils.gradle" +apply from: project(':libaxmol').projectDir.toString() + "/axmol.gradle" -android { - // Resolve build profiles - def buildProfiles = AxmolUtils.resolveBuildProfiles(project) +// Resolve build profiles +def buildProfiles = AxmolUtils.resolveBuildProfiles(project) + +android { def packageName = buildProfiles['packageName'] def cmakeVer = buildProfiles['cmakeVer'] def cmakeOptions = Eval.me(buildProfiles['cmakeOptions']) @@ -112,6 +113,9 @@ android.applicationVariants.configureEach { variant -> dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation project(':libaxmol') + + def appcompat_ver = buildProfiles['appcompat'] + implementation "androidx.appcompat:appcompat:$appcompat_ver" } project.afterEvaluate { diff --git a/tests/unit-tests/Source/TestUtils.cpp b/tests/unit-tests/Source/TestUtils.cpp index 5c85db682e2b..b50f5d85c6ee 100644 --- a/tests/unit-tests/Source/TestUtils.cpp +++ b/tests/unit-tests/Source/TestUtils.cpp @@ -28,7 +28,7 @@ doctest::String toString(const Vec3& value) { return s.c_str(); } -doctest::String toString(const Color4B& value) { +doctest::String toString(const Color32& value) { std::string s; s.append("("); s.append(std::to_string(value.r)); diff --git a/tests/unit-tests/Source/TestUtils.h b/tests/unit-tests/Source/TestUtils.h index a6bf0af45474..a416ddebd958 100644 --- a/tests/unit-tests/Source/TestUtils.h +++ b/tests/unit-tests/Source/TestUtils.h @@ -42,7 +42,7 @@ class AsyncRunner { namespace ax { - doctest::String toString(const Color4B& value); + doctest::String toString(const Color32& value); doctest::String toString(const Vec2& value); doctest::String toString(const Vec3& value); } diff --git a/tests/unit-tests/Source/core/math/MathUtilTests.cpp b/tests/unit-tests/Source/core/math/MathUtilTests.cpp index 1c220c8e3115..9bcb945183f5 100644 --- a/tests/unit-tests/Source/core/math/MathUtilTests.cpp +++ b/tests/unit-tests/Source/core/math/MathUtilTests.cpp @@ -67,33 +67,33 @@ TEST_SUITE("math/MathUtil") { using namespace UnitTest::ax; - static void checkVerticesAreEqual(const V3F_C4B_T2F* v1, const V3F_C4B_T2F* v2, size_t count) + static void checkVerticesAreEqual(const V3F_T2F_C4B* v1, const V3F_T2F_C4B* v2, size_t count) { for (size_t i = 0; i < count; ++i) { - CHECK_EQ(v1[i].vertices, v2[i].vertices); - CHECK_EQ(v1[i].colors, v2[i].colors); - CHECK_EQ(v1[i].texCoords, v2[i].texCoords); + CHECK_EQ(v1[i].position, v2[i].position); + CHECK_EQ(v1[i].color, v2[i].color); + CHECK_EQ(v1[i].texCoord, v2[i].texCoord); } } TEST_CASE("transformVertices") { auto count = 5; - std::vector src(count); - std::vector expected(count); - std::vector dst(count); + std::vector src(count); + std::vector expected(count); + std::vector dst(count); for (int i = 0; i < count; ++i) { - src[i].vertices.set(float(i), float(i + 1), float(i + 2)); - src[i].colors.set(uint8_t(i + 3), uint8_t(i + 4), uint8_t(i + 5), uint8_t(i + 6)); - src[i].texCoords.set(float(i + 7), float(i + 8)); + src[i].position.set(float(i), float(i + 1), float(i + 2)); + src[i].color.set(uint8_t(i + 3), uint8_t(i + 4), uint8_t(i + 5), uint8_t(i + 6)); + src[i].texCoord.set(float(i + 7), float(i + 8)); expected[i] = src[i]; - expected[i].vertices.x = src[i].vertices.y * 4; - expected[i].vertices.y = src[i].vertices.x * -5; - expected[i].vertices.z = src[i].vertices.z * 6; + expected[i].position.x = src[i].position.y * 4; + expected[i].position.y = src[i].position.x * -5; + expected[i].position.z = src[i].position.z * 6; } Mat4 transform(0, 4, 0, 0, -5, 0, 0, 0, 0, 0, 6, 0, 1, 2, 3, 1); diff --git a/tests/unit-tests/proj.android/app/AndroidManifest.xml b/tests/unit-tests/proj.android/app/AndroidManifest.xml index e783f2c93955..cfe6701e170a 100644 --- a/tests/unit-tests/proj.android/app/AndroidManifest.xml +++ b/tests/unit-tests/proj.android/app/AndroidManifest.xml @@ -21,7 +21,7 @@ android:screenOrientation="sensorLandscape" android:configChanges="orientation|keyboardHidden|screenSize" android:label="@string/app_name" - android:theme="@android:style/Theme.NoTitleBar.Fullscreen" + android:theme="@style/Theme.AppCompat.Light.NoActionBar" android:launchMode="singleTask" android:taskAffinity="" android:exported="true" > diff --git a/tests/unit-tests/proj.android/app/build.gradle b/tests/unit-tests/proj.android/app/build.gradle index 16b442ca9971..bc7351d2f907 100644 --- a/tests/unit-tests/proj.android/app/build.gradle +++ b/tests/unit-tests/proj.android/app/build.gradle @@ -1,9 +1,10 @@ apply plugin: 'com.android.application' -apply from: project(':libaxmol').projectDir.toString() + "/axutils.gradle" +apply from: project(':libaxmol').projectDir.toString() + "/axmol.gradle" -android { - // Resolve build profiles - def buildProfiles = AxmolUtils.resolveBuildProfiles(project) +// Resolve build profiles +def buildProfiles = AxmolUtils.resolveBuildProfiles(project) + +android { def packageName = buildProfiles['packageName'] def cmakeVer = buildProfiles['cmakeVer'] def cmakeOptions = Eval.me(buildProfiles['cmakeOptions']) @@ -114,6 +115,9 @@ android.applicationVariants.configureEach { variant -> dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation project(':libaxmol') + + def appcompat_ver = buildProfiles['appcompat'] + implementation "androidx.appcompat:appcompat:$appcompat_ver" } project.afterEvaluate { diff --git a/tools/bindings-generator/generator.py b/tools/bindings-generator/generator.py index 8df9a974b59a..1970f3e4dbb8 100644 --- a/tools/bindings-generator/generator.py +++ b/tools/bindings-generator/generator.py @@ -1381,6 +1381,8 @@ def __init__(self, opts): if sys.platform == 'win32' and self.win32_clang_flags != None: self.clang_args.extend(self.win32_clang_flags) + print(f'clang_args={self.clang_args}') + if opts['skip']: list_of_skips = re.split(",\n?", opts['skip']) for skip in list_of_skips: @@ -1741,10 +1743,10 @@ def js_typename_from_natve(self, namespace_class_name): return "rect_object" if namespace_class_name.find("ax::Color3B") == 0: return "color3b_object" - if namespace_class_name.find("ax::Color4B") == 0: - return "color4b_object" - if namespace_class_name.find("ax::Color4F") == 0: - return "color4f_object" + if namespace_class_name.find("ax::Color32") == 0: + return "color32_object" + if namespace_class_name.find("ax::Color") == 0: + return "color_object" else: return namespace_class_name.replace("*","").replace("const ", "").replace(k,v) return namespace_class_name.replace("*","").replace("const ", "") @@ -1787,8 +1789,8 @@ def lua_typename_from_natve(self, namespace_class_name, is_ret = False): return "rect_table" if namespace_class_name.find("ax::Color3B") == 0: return "color3b_table" - if namespace_class_name.find("ax::Color4B") == 0: - return "color4b_table" + if namespace_class_name.find("ax::Color32") == 0: + return "color32_table" if namespace_class_name.find("ax::Color4F") == 0: return "color4f_table" if is_ret == 1: diff --git a/tools/bindings-generator/targets/lua/conversions.yaml b/tools/bindings-generator/targets/lua/conversions.yaml index 0168090fceb8..453f93e8c862 100644 --- a/tools/bindings-generator/targets/lua/conversions.yaml +++ b/tools/bindings-generator/targets/lua/conversions.yaml @@ -52,8 +52,8 @@ conversions: "Vec4": "ok &= luaval_to_vec4(tolua_S, ${arg_idx}, &${out_value}, \"${lua_namespaced_class_name}:${func_name}\")" "Rect": "ok &= luaval_to_rect(tolua_S, ${arg_idx}, &${out_value}, \"${lua_namespaced_class_name}:${func_name}\")" "Size": "ok &= luaval_to_size(tolua_S, ${arg_idx}, &${out_value}, \"${lua_namespaced_class_name}:${func_name}\")" - "Color4B": "ok &=luaval_to_color4b(tolua_S, ${arg_idx}, &${out_value}, \"${lua_namespaced_class_name}:${func_name}\")" - "Color4F": "ok &=luaval_to_color4f(tolua_S, ${arg_idx}, &${out_value}, \"${lua_namespaced_class_name}:${func_name}\")" + "Color32": "ok &=luaval_to_color32(tolua_S, ${arg_idx}, &${out_value}, \"${lua_namespaced_class_name}:${func_name}\")" + "Color": "ok &=luaval_to_color(tolua_S, ${arg_idx}, &${out_value}, \"${lua_namespaced_class_name}:${func_name}\")" "Color3B": "ok &= luaval_to_color3b(tolua_S, ${arg_idx}, &${out_value}, \"${lua_namespaced_class_name}:${func_name}\")" "PhysicsMaterial": "ok &= luaval_to_physics_material(tolua_S, ${arg_idx}, &${out_value}, \"${lua_namespaced_class_name}:${func_name}\")" "Array*": "ok &= luaval_to_array(tolua_S, ${arg_idx}, &${out_value}, \"${lua_namespaced_class_name}:${func_name}\")" @@ -117,8 +117,9 @@ conversions: "Vec4": "vec4_to_luaval(tolua_S, ${in_value})" "Rect": "rect_to_luaval(tolua_S, ${in_value})" "Size": "size_to_luaval(tolua_S, ${in_value})" - "Color4B": "color4b_to_luaval(tolua_S, ${in_value})" + "Color32": "color32_to_luaval(tolua_S, ${in_value})" "Color4F": "color4f_to_luaval(tolua_S, ${in_value})" + "Color": "color_to_luaval(tolua_S, ${in_value})" "Color3B": "color3b_to_luaval(tolua_S, ${in_value})" "PhysicsMaterial": "physics_material_to_luaval(tolua_S, ${in_value})" "PhysicsContactData*": "physics_contactdata_to_luaval(tolua_S, ${in_value})" diff --git a/tools/cmdline/axmol b/tools/cmdline/axmol index 0a3999e002df..805b0f7f570b 100755 --- a/tools/cmdline/axmol +++ b/tools/cmdline/axmol @@ -1,13 +1,11 @@ #!/bin/bash -l -AXMOL_CONSOLE_BIN_DIRECTORY=$(dirname "$0") -AXMOL_CONSOLE_BIN_DIRECTORY=$(cd "$AXMOL_CONSOLE_BIN_DIRECTORY" && pwd -P) - +SCRIPT_ROOT=$(dirname "$0") +SCRIPT_ROOT=$(cd "$SCRIPT_ROOT" && pwd -P) if hash pwsh 2>/dev/null; then POWERSHELL=pwsh else echo "PowerShell 7+ required." exit 1 fi - -$POWERSHELL "$AXMOL_CONSOLE_BIN_DIRECTORY/axmol.ps1" "$@" +$POWERSHELL "$SCRIPT_ROOT/axmol.ps1" "$@" diff --git a/tools/tolua/ax_base.ini b/tools/tolua/ax_base.ini index c3c7823ebadf..4f0322af90c7 100644 --- a/tools/tolua/ax_base.ini +++ b/tools/tolua/ax_base.ini @@ -127,7 +127,7 @@ skip = Node::[setGLServerState description _setLocalZOrder getUserObject .*UserD TurnOffTiles::[shuffle], LabelTTF::[*], LabelBMFont::[*], - Scene::[getCameras getLights .*(Physics3D).* .*(NavMesh).*], + Scene::[getCameras getLights .*(Physics3D).* .*(NavMesh).* getPhysicsWorld], Animate3D::[*], MeshRenderer::[*], AttachNode::[*], diff --git a/tools/tolua/ax_physics3d.ini b/tools/tolua/ax_physics3d.ini index bb59d82bac9d..51f8bfbdd6e5 100644 --- a/tools/tolua/ax_physics3d.ini +++ b/tools/tolua/ax_physics3d.ini @@ -7,14 +7,14 @@ prefix = ax_physics3d # all classes will be embedded in that namespace target_namespace = ax -macro_judgement = #if defined(AX_ENABLE_3D_PHYSICS) && AX_ENABLE_BULLET_INTEGRATION +macro_judgement = #if defined(AX_ENABLE_3D_PHYSICS) android_headers = android_flags = -target armv7-none-linux-androideabi -D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS -DANDROID -D__ANDROID_API__=16 -idirafter %(androidndkdir)s/sources/android/support/include -idirafter %(clangllvmdir)s/sysroot/usr/include -idirafter %(clangllvmdir)s/sysroot/usr/include/arm-linux-androideabi -I%(androidndkdir)s/sources/cxx-stl/llvm-libc++/include clang_headers = -clang_flags = -nostdinc -x c++ -std=%(cxx_std)s -fsigned-char -U__SSE__ -DAX_ENABLE_3D_PHYSICS -DAX_ENABLE_BULLET_INTEGRATION=1 +clang_flags = -nostdinc -x c++ -std=%(cxx_std)s -fsigned-char -U__SSE__ -DAX_ENABLE_3D_PHYSICS win32_clang_flags = -U __SSE__ diff --git a/tools/tolua/genbindings.py b/tools/tolua/genbindings.py index c23ef7fe353d..f85ee15caa69 100644 --- a/tools/tolua/genbindings.py +++ b/tools/tolua/genbindings.py @@ -7,7 +7,6 @@ import argparse import sys import os, os.path -import shutil import subprocess import re from contextlib import contextmanager